I'm running an exe through which I get a handle of a control in another exe. Now what I want to do is send messages to the particular handle from my exe.
You need to import the function using:
[DllImport("user32.dll")]
public static extern int SendMessage(
int hWnd, // handle to destination window
uint Msg, // message
long wParam, // first message parameter
long lParam // second message parameter
);
and define the message that you want to send like:
public const int <WM_YOURMESSAGE> = <yourvalue>;
and then call SendMessage like any other function.
Related
I am making a Unity app that will run in Windows in a custom arcade cabinet and will load and stop games.
I have been abusing winAPI to do this.
Eg. I start the game process, then use findwindow to wait until a window of the correct name exists and also get an IntPtr for that window which I can send to the SetForegroundWindow winAPI function to make sure the game is in front and focused for inputs.
This all works fine.
Except for some unreal games I was using to test. Eg. this game 'Peekaboo', despite calling findwindow every frame in my Unity app a window called Peekaboo is never found, though when I look in Windows it is clearly there.
Its the same story for another Unreal engine game 'Mechwarrior 5 mercenaries'
I think it might have something to do with the fact that unreal games seems to launch several nested processes like in the image below.
(Eg. To stop non-unreal games I can just stop the process using the reference I got when I started it. But this did not work with Unreal games, the exe I started is gone and I needed to find that Win64-Shipping process and stop that.)
Here is the code I am using to call findwindow and also setforground
using UnityEngine;
using System.Collections;
using System;
using System.Runtime.InteropServices;
public static class WinAPIGetWindowIfExists
{
[DllImport("user32.dll", SetLastError = true)]
public static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
public static IntPtr DoesWindowExist(string windowName)
{
Debug.Log("looking for window: " + windowName);
return FindWindow(null, #windowName);
}
}
public static class WinAPISetForegroundWindow
{
[DllImport("user32.dll")]
public static extern bool SetForegroundWindow(IntPtr handle);
public static void SetForeground(IntPtr windowHandle)
{
SetForegroundWindow(windowHandle);
}
}
So how can I get a reference to an unreal game window so I can set it to the forground?
UPDATE:
I am super confused now, as I have called EnumWindows, and called GetWindowText for every window returned by it, and I have found a window called Peekaboo.. If I pass that window's handle to SetForeground the correct game window is set to foreground, also if I sendmessage with this message 0x000D; I get the text peekboo back. Yet, findwindow still finds no window named peekaboo...
So I can use this EnumWindows to solve my issue.. but this utterly sucks. Why does iterating through every window, calling get windowtext, then checking if the text contains the window title work, whereas findwindow doesnt work?
So here is the code for using enumwindows. This code does work for Unreal engine games. I found this somewhere on the internet. It's a good thing too cus figuring out how to make these functions work with interop from just MS documentation is not trivial imo. It was really not clear to me from the MS documentation that after calling the enumwindows function it would call the callback function over and over for every single window returned. It seems like a kind of ridiculous pattern to me, just to avoid having any unsafe code.
using System;
using System.Collections.Generic;
using System.IO;
using System.Runtime.InteropServices;
using System.Text;
public delegate bool WNDENUMPROC(
UInt32 hWnd,
IntPtr lParam
);
internal static class WinAPI
{
[DllImport("user32.dll")]
internal static extern bool EnumWindows(
WNDENUMPROC cb, // WNDENUMPROC lpEnumFiunc
IntPtr manageObject // LPARAM lParam (the managed object))
);
[DllImport("user32.dll", CharSet = CharSet.Auto)]
internal static extern bool GetWindowText(
UInt32 hWnd,
StringBuilder title,
int size
);
[DllImport("user32.dll", CharSet = CharSet.Auto)]
internal static extern IntPtr SendMessage(IntPtr hWnd, uint Msg, int wParam,
StringBuilder lParam);
}
public class WindowEnumerator
{
private const uint WM_GETTEXT = 0x000D;
public static IntPtr FindWindowWithThisTitle(string findthis)
{
List<UInt32> hWnds = new List<UInt32>();
//
// Prevent the managed object (hWnds) from being collected
// by the garbage collector:
//
GCHandle objHandle = GCHandle.Alloc(hWnds);
//
// Create an instance of a delegate in order to
// provide a «callback» for EnumWindows:
//
WNDENUMPROC enumProc = new WNDENUMPROC(callback);
//
// Get an internal representation for the gc handle
// so as to be able to pass it to the second parameter
// of EnumWindows:
//
IntPtr objHandlePtr = GCHandle.ToIntPtr(objHandle);
WinAPI.EnumWindows( // Let Windows iterate over each window and
enumProc, // call enumProc (which is «initialized» for the method callback)
objHandlePtr // and pass this pointer to the method
);
//
// Free the handle of the object so that
// the object can be collected and
// thus to prevent memory leaks:
//
objHandle.Free();
StringBuilder title = new StringBuilder(256);
foreach (UInt32 hWnd in hWnds)
{
WinAPI.GetWindowText(hWnd, title, 256);
if (title.ToString().Contains(findthis))
{
return (IntPtr)hWnd;
}
//UnityEngine.Debug.Log(" "+ hWnd+" "+ title);
}
return IntPtr.Zero;
}
private static bool callback(
//
// After calling WinAPI.EnumWindows, Windows calls
// this method for each Window and passes it
// the hWnd of the respective Window and
// the value that was given as second parameter
// to EnumWindows (objHandlePtr);
//
UInt32 hWnd,
IntPtr objHandlePtr
)
{
//
// Get the handle to the object from the pointer:
//
GCHandle objHandle = GCHandle.FromIntPtr(objHandlePtr);
//
// and cast the handle's target into the underlying
// managed object:
//
List<UInt32> obj = (List<UInt32>)objHandle.Target;
obj.Add(hWnd);
return true;
}
}
I am using System.Windows.Clipboard to copy some text, and I would like to know is there a chance to get the origin source,
e.g. a the file where I copyied it from, or the website, folder.... ?
Thanks
The Win32 GetClipboardOwner() function can be used to get the Handle of the Window that last placed data into the Clipboard.
You can then pass the returned handle to GetWindowThreadProcessId() to get the Process ID and Thread ID of that Window.
Back in .Net territory, you can use the Process ID as the parameter to pass to the System.Diagnostics.Process.GetProcessById() method, to retrieve the information needed.
Note that you have to build a 64bit application to fully inspect a 64bit
process. If your project has the Prefer 32-bit option set, some
information will not be available.
See also:
How to get the process ID or name of the application that has updated the clipboard?
Windows API declarations. The overloaded GetClipboardOwnerProcessID() wrapper method returns the ProcessID of the ClipBoard Owner and, optionally, its Thread ID.
public class WinApi
{
[DllImport("user32.dll")]
private static extern IntPtr GetClipboardOwner();
//The return value is the identifier of the thread that created the window.
[DllImport("user32.dll", SetLastError = true)]
private static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId);
//Wrapper used to return the Window processId
public static uint GetClipboardOwnerProcessID()
{
uint processId = 0;
GetWindowThreadProcessId(SafeNativeMethods.GetClipboardOwner(), out processId);
return processId;
}
//Overload that returns a reference to the Thread ID
public static uint GetClipboardOwnerProcessID(ref uint threadId)
{
uint processId = 0;
threadId = GetWindowThreadProcessId(SafeNativeMethods.GetClipboardOwner(), out processId);
return processId;
}
}
The wrapper can be called like this, if you just need the Process Id:
uint ClipBoadrOwnerProcessId = WinApi.GetClipboardOwnerProcessID();
Or this way, if you also need the Thread Id:
uint ClipBoadrOwnerThreadId = 0;
uint ClipBoadrOwnerProcessId = WinApi.GetClipboardOwnerProcessID(ref ClipBoadrOwnerThreadId);
Pass the returned value to the Process.GetProcessById() method:
Process ClipBoardOwnerProcess = Process.GetProcessById((int)WinApi.GetClipboardOwnerProcessID());
string ProcessName = ClipBoardOwnerProcess.ProcessName;
string ProcessWindowTitle = ClipBoardOwnerProcess.MainWindowTitle;
string ProcessFileName = ClipBoardOwnerProcess.MainModule.FileName;
//(...)
If you copy some text from your browser, the ProcessName will be the name of your browser and the ProcessFileName the path to its executable.
So I have this code to set all items (or even a single item i stated the code for item number 3) for a listview in another application using sendmessage, I already managed to successfully get the item window handle of the listview and got the item count right but when i use setitemstate the other application gives me error and closes (given that there is another application I have that do this job just fine but i don't have its source code)
this is my code :
if (windowName.Contains("Invite to Room")) {
IntPtr hwndChild0 = FindWindowEx(hWnd, IntPtr.Zero, "SysListView32", "");
int itemCount = SendMessage(hwndChild0, LVM_GETITEMCOUNT, IntPtr.Zero, IntPtr.Zero);
LV_ITEM lvItem = new LV_ITEM();
lvItem.Index = 3;
lvItem.SubIndex = 2;
lvItem.TextLength = 50;
lvItem.Mask = LVIF_STATE;
lvItem.State = LVIS_SELECTED;
lvItem.StateMask = LVIS_SELECTED;
SendMessage(hwndChild0, LVM_SETITEMSTATE, 3, IntPtr.Zero);
}
this is my LV_ITEM structure
public struct LV_ITEM
{
public uint Mask;
public int Index;
public int SubIndex;
public int State;
public IntPtr StateMask;
public string Text;
public int TextLength;
public int ImageIndex;
public IntPtr LParam;
}
and this is the declaration of the used (sendmessage and setitemstate)
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = false)]
public static extern int SendMessage(IntPtr hWnd, uint msg, IntPtr wParam, string lParam);
private const uint LVM_FIRST = 0x1000;
private const uint LVM_SETITEMSTATE = (LVM_FIRST + 43);
any help with this?
Edit :
the application error has a log file that said the following message :
"The thread tried to read from or write to a virtual address for which it doesn't have the appropriate access"
Some messages only use WPARAM, LPARAM and return LRESULT to pass data around, like LVM_GETITEMCOUNT that worked for you.
Other messages use pointers to some data structure to be used or filled, like LVM_SETITEMSTATE. It expects a pointer to pre-filled LV_ITEM structure in LPARAM, while you pass IntPtr.Zero - so you cause an access violation in that other application trying to dereference that zero pointer.
However, you can't just simply pass the pointer to your lvItem, as it wouldn't make any sense in another process. You need to allocate a memory for that structure in the second process, initialize it, send you message and read that memory back (if you expect any response).
To work around the limitations of GenerateConsoleCtrlEvent, I have to create an intermediate "middle-man" process to handle launching some console applications. The process's main purpose is to call GenerateConsoleCtrlEvent on itself, causing itself and all child process to close cleanly in response to a ctrl+break signal (rather than using Process.Kill). This need arises from the fact that GenerateConsoelCtrlEvent basically has no effect unless the process group id is zero, which means it is only ever effective on the calling process group itself. See: https://stackoverflow.com/a/2431295/88409
So anyway... I've created this intermediate process, which starts a thread that calls Application.Run on a form which processes specific user-defined messages.
My problem is... how do send messages to this process to control it?
I have the Process object and its process id, but that's all. Process.MainWindowHandle is zero.
So I need a way to send a message to a specific process or broadcast the message to all windows in a specific process.
FindWindow is not an option, because it tries to identify a window by name and class on any process, which is unreliable. I want to send a message to a specific process with no ambiguity.
There are 3 scenarios in which a message could be thought of as being sent or posted to a process:
I can "send" or "post" a [window] message to a specific process by sending it to the first enumerated window in that process
I can "post" a [thread] message to a specific process by posting it to the first enumerated thread in the process.
I can "post" a [thread] message to a specific process by posting it to the thread that owns the first enumerated window of that process.
Approach 1 may be too specific, since it targets a specific but arbitrary window.
Approach 2 may be not specific enough, since the first enumerated thread is arbitrary and may not have a message loop.
Approach 3 is a hybrid approach that first identifies a window, but then posts a thread message to that window's thread, so it is not targetted at a specific window (i.e. it's a "thread message") but it is targetted at a thread that is likely to have a message loop since the thread owns at least one window.
The following is an implementation that supports all three approaches and both methods "send" and "post". Approach 1 is encompassed by the methods SendMessage and PostMessage below. Approach 2 and 3 are encompassed by the method PostThreadMessage below, depending on whether you set the optional parameter ensureTargetThreadHasWindow (true = approach 3, false = approach 2).
public static class ProcessExtensions
{
private static class Win32
{
[DllImport("user32.dll", CharSet = CharSet.Auto)]
public static extern IntPtr SendMessage(IntPtr hWnd, UInt32 Msg, IntPtr wParam, IntPtr lParam);
[return: MarshalAs(UnmanagedType.Bool)]
[DllImport("user32.dll", SetLastError = true)]
public static extern bool PostMessage(IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam);
[return: MarshalAs(UnmanagedType.Bool)]
[DllImport("user32.dll", SetLastError = true)]
public static extern bool PostThreadMessage(uint threadId, uint msg, IntPtr wParam, IntPtr lParam);
public delegate bool EnumThreadDelegate (IntPtr hWnd, IntPtr lParam);
[DllImport("user32.dll")]
public static extern bool EnumThreadWindows(uint dwThreadId, EnumThreadDelegate lpfn, IntPtr lParam);
[DllImport("user32.dll", SetLastError=true)]
public static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId);
}
//Sends a message to the first enumerated window in the first enumerated thread with at least one window, and returns the handle of that window through the hwnd output parameter if such a window was enumerated. If a window was enumerated, the return value is the return value of the SendMessage call, otherwise the return value is zero.
public static IntPtr SendMessage( this Process p, out IntPtr hwnd, UInt32 msg, IntPtr wParam, IntPtr lParam )
{
hwnd = p.WindowHandles().FirstOrDefault();
if (hwnd != IntPtr.Zero)
return Win32.SendMessage( hwnd, msg, wParam, lParam );
else
return IntPtr.Zero;
}
//Posts a message to the first enumerated window in the first enumerated thread with at least one window, and returns the handle of that window through the hwnd output parameter if such a window was enumerated. If a window was enumerated, the return value is the return value of the PostMessage call, otherwise the return value is false.
public static bool PostMessage( this Process p, out IntPtr hwnd, UInt32 msg, IntPtr wParam, IntPtr lParam )
{
hwnd = p.WindowHandles().FirstOrDefault();
if (hwnd != IntPtr.Zero)
return Win32.PostMessage( hwnd, msg, wParam, lParam );
else
return false;
}
//Posts a thread message to the first enumerated thread (when ensureTargetThreadHasWindow is false), or posts a thread message to the first enumerated thread with a window, unless no windows are found in which case the call fails. If an appropriate thread was found, the return value is the return value of PostThreadMessage call, otherwise the return value is false.
public static bool PostThreadMessage( this Process p, UInt32 msg, IntPtr wParam, IntPtr lParam, bool ensureTargetThreadHasWindow = true )
{
uint targetThreadId = 0;
if (ensureTargetThreadHasWindow)
{
IntPtr hwnd = p.WindowHandles().FirstOrDefault();
uint processId = 0;
if (hwnd != IntPtr.Zero)
targetThreadId = Win32.GetWindowThreadProcessId( hwnd, out processId );
}
else
{
targetThreadId = (uint)p.Threads[0].Id;
}
if (targetThreadId != 0)
return Win32.PostThreadMessage( targetThreadId, msg, wParam, lParam );
else
return false;
}
public static IEnumerable<IntPtr> WindowHandles( this Process process )
{
var handles = new List<IntPtr>();
foreach (ProcessThread thread in process.Threads)
Win32.EnumThreadWindows( (uint)thread.Id, (hWnd, lParam) => { handles.Add(hWnd); return true; }, IntPtr.Zero );
return handles;
}
}
To use this extension method on a Process object:
Process process = Process.Start( exePath, args );
IntPtr hwndMessageWasSentTo = IntPtr.Zero; //this will receive a non-zero value if SendMessage was called successfully
uint msg = 0xC000; //The message you want to send
IntPtr wParam = IntPtr.Zero; //The wParam value to pass to SendMessage
IntPtr lParam = IntPtr.Zero; //The lParam value to pass to SendMessage
IntPtr returnValue = process.SendMessage( out hwndMessageWasSentTo, msg, wParam, lParam );
if (hwndMessageWasSentTo != IntPtr.Zero)
Console.WriteLine( "Message successfully sent to hwnd: " + hwndMessageWasSentTo.ToString() + " and return value was: " + returnValue.ToString() );
else
Console.WriteLine( "No windows found in process. SendMessage was not called." );
You can't send nor post a message to a process, but you can post a message to a thread. Of course, that thread must have started a message loop to process it.
Use EnumWindows to scan the top level windows, and use GetProcessHandleFromHWnd to determine whether a window is in your target process.
You can then send messages to that hwnd, and they should be received by your target process (if your target process has a message loop).
I just answered a very similar question (with sample code) here.
The quick answer is PostThreadMessage().
I'm writing a C# application which needs to intercept Window Messages that another applications is sending out. The company who wrote the application I'm monitoring sent me some example code, however it's in C++ which I don't really know.
In the C++ example code I've got they use the following code:
UINT uMsg = RegisterWindowMessage(SHOCK_MESSAGE_BROADCAST);
ON_REGISTERED_MESSAGE(WM_SHOCK_BROADCAST_MESSAGE, OnShockStatusMessage)
LRESULT OnShockStatusMessage(WPARAM wParam, LPARAM lParam);
As I understand it this retrieves an Id from Windows for the specific message we want to listen for. Then we're asking C++ to call OnShockStatusMessage whenever an message matching the Id is intercepted.
After a bit of research I've put together the following in C#
[DllImport("user32.dll", SetLastError = true)]
public static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
static extern uint RegisterWindowMessage(string lpString);
private IntPtr _hWnd; // APS-50 class reference
private List<IntPtr> _windowsMessages = new List<IntPtr>(); // APS-50 messages
private const string _className = "www.AuPix.com/SHOCK/MessageWindowClass";
// Windows Messages events
private const string _messageBroadcast = "www.AuPix.com/SHOCK/BROADCAST";
private const string _messageCallEvents = "www.AuPix.com/SHOCK/CallEvents";
private const string _messageRegistrationEvents = "www.AuPix.com/SHOCK/RegistrationEvents";
private const string _messageActions = "www.AuPix.com/SHOCK/Actions";
private void DemoProblem()
{
// Find hidden window handle
_hWnd = FindWindow(_className, null);
// Register for events
_windowsMessages.Add( new IntPtr( RegisterWindowMessage( _messageActions ) ) );
_windowsMessages.Add( new IntPtr( RegisterWindowMessage( _messageBroadcast ) ) );
_windowsMessages.Add( new IntPtr( RegisterWindowMessage( _messageCallEvents ) ) );
_windowsMessages.Add( new IntPtr( RegisterWindowMessage( _messageRegistrationEvents ) ) );
}
protected override void WndProc(ref Message m)
{
base.WndProc(ref m);
// Are they registered Windows Messages for the APS-50 application?
foreach (IntPtr message in _windowsMessages)
{
if ((IntPtr)m.Msg == message)
{
Debug.WriteLine("Message from specified application found!");
}
}
// Are they coming from the APS-50 application?
if ( m.HWnd == shock.WindowsHandle)
{
Debug.WriteLine("Message from specified application found!");
}
}
As I understand this should do the same basic thing, in that it:
Finds the application I wish to monitor
Registers the Window Messages I wish to intercept
Watches for all Window Messages - then strips out the ones I need
However in my override of the WndProc() method neither of my checks intercept any of the specific messages or any message from the application I'm monitoring.
If I Debug.WriteLine for all messages that come through it, I can see that it's monitoring them. However it never filters out the messages that I want.
By running the example monitoring application written in C++ I can see that Window Messages are being sent and picked up - it's just my C# implemention doesn't do the same.
Turns out I also needed to send the other application a PostMessage asking it to send my application the Window Messages.
PostMessage((int)_hWnd, _windowsMessages[0], SHOCK_REQUEST_ACTIVE_CALLINFO, (int)_thisHandle);
PostMessage((int)_hWnd, _windowsMessages[0], SHOCK_REQUEST_ALL_REGISTRATIONINFO, (int)_thisHandle);
PostMessage((int)_hWnd, _windowsMessages[0], SHOCK_REQUEST_CALL_EVENTS, (int)_thisHandle);
PostMessage((int)_hWnd, _windowsMessages[0], SHOCK_REQUEST_REGISTRATION_EVENTS, (int)_thisHandle);
Not pretty code, but good enough to prove it works which is all I need for now :)
I think the problem is with your P/Invoke definition for RegisterWindowMessage(). pinvoke.net suggests using the following:
[DllImport("user32.dll", SetLastError=true, CharSet=CharSet.Auto)]
static extern uint RegisterWindowMessage(string lpString);
Using uint as the return value instead of IntPtr should make the difference. Typically you want to use IntPtr when the return value is a handle (such as an HWND or HANDLE), but when the return value can be directly converted to a C# type it is better to use that type.