C# - Capturing Windows Messages from a specific application - c#

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.

Related

WinAPI FindWindow not working for Unreal engine games

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;
}
}

WindowsAccessBridge for Java Automation using C#

I try to automate an java application using WindowsAccessBridge.dll.
I can get the window handle but calling the function isJavaWindow(System.IntPtr hWnd) always return false
Please find my code below:
static void Main()
{
System.Int32 vmID = 0;
System.Int64 _acParent = 0;
string WndName = "GLOBUS EDU";
string ClassName = "SunAwtFrame";
Windows_run();
System.IntPtr hWnd = System.IntPtr.Zero;
hWnd = (System.IntPtr)FindWindow(ClassName, WndName);
bool Found = isJavaWindow(hWnd);
if (!Found) { throw new System.Exception("ERROR: Unable to find window by classname " + ClassName + " and " + WndName + "!"); }
System.Console.WriteLine("Application is finished. Press ENTER to exit...");
System.Console.ReadKey();
}
Interop:
[return: System.Runtime.InteropServices.MarshalAs(System.Runtime.InteropServices.UnmanagedType.Bool)]
[System.Runtime.InteropServices.DllImport("WindowsAccessBridge-64.dll", CallingConvention = System.Runtime.InteropServices.CallingConvention.Cdecl)]
private extern static bool getAccessibleContextFromHWNDFct(System.IntPtr hwnd, out System.Int32 vmID, out System.Int32 _acParent);
private static bool getAccesibleContextFromHWND(System.IntPtr hWnd, out System.Int32 vmID, out System.Int64 acParent)
{
System.Int32 ac = -1;
bool retVal = false;
getAccessibleContextFromHWNDFct(hWnd, out vmID, out ac);
acParent = ac;
return retVal;
}
[System.Runtime.InteropServices.DllImport("WindowsAccessBridge-64.dll", CallingConvention = System.Runtime.InteropServices.CallingConvention.Cdecl)]
private extern static bool getAccessibleContextInfo(int vmID, System.IntPtr ac, out AccessibleContextInfo textInfo);
[System.Runtime.InteropServices.DllImport("WindowsAccessBridge-64.dll", CallingConvention = System.Runtime.InteropServices.CallingConvention.Cdecl, ThrowOnUnmappableChar = true, CharSet = System.Runtime.InteropServices.CharSet.Unicode)]
private extern static void Windows_run();
[System.Runtime.InteropServices.DllImport("user32.dll", SetLastError = true)]
private static extern System.IntPtr FindWindow(string lpClassName, string lpWindowName);
[System.Runtime.InteropServices.DllImport("user32.dll", EntryPoint = "FindWindow", SetLastError = true)]
private static extern System.IntPtr FindWindowByCaptionFct(System.IntPtr ZeroOnly, string lpWindowName);
private static System.IntPtr FindWindowByCaption(string WindowTitle) { return FindWindowByCaptionFct(System.IntPtr.Zero, WindowTitle); }
[System.Runtime.InteropServices.DllImport("WindowsAccessBridge-64.dll", CallingConvention = System.Runtime.InteropServices.CallingConvention.Cdecl, ThrowOnUnmappableChar = true, CharSet = System.Runtime.InteropServices.CharSet.Unicode)]
private extern static System.Boolean isJavaWindow(System.IntPtr hwnd);
The function FindWindowis working perfect and I'm getting the window handle also Spy++ shows me. The classname is SunAwtFrameas Spy++ says.
My Java applications runs in 64 bit but I tried all the Libraries (-32, -64) and also switched in the VS Configuration Manager from x86 to x64 and back.
The AccessBridge itself is working well - Java-Monkey-64.exe can spy my running java application.
Does anybody has an idea, why this is not working?
Regards,
Jan
I have been fighting with your problem in few days.
i created a program that enumerate window that is java application(of course write on console application), and catch same problem like yours.
then, i rewrite it on WPF application,enumerate all window, then recognize that: besides the normal window, i see a strange window named: "java access bridge", and the problem is clearly:
the Windows_run function need to have an active windows message pump.
another way, you must putting it on the constructor of a WPF application or something same that.
if (result != FALSE) {
while (GetMessage(&msg, NULL, 0, 0)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
shutdownAccessBridge();
}
code in Java Monkey application.
After create a hidden window, it performs a PostMessage with a registered message. The JVM side of the access bridge responds to this message, and posts back another message to the window that was created. As such, they communicate by that way.
and more, you only can call JAB functions after the message pump can process messages.
that is reason why the java monkey need to use call back for it's business.
Pass null for class name as in the below code:
IntPtr hWnd = FindWindow(null, "GLOBUS EDU"); //cast to IntPtr is redundant
bool Found = isJavaWindow(hWnd);
Reference is here on Pinvoke documentation, and it works for me!

Get the handle and write to the console that launched our process

How could I write to the standard output of some already open console?
I find the console I need with this piece of code:
IntPtr ptr = GetForegroundWindow();
int u;
GetWindowThreadProcessId(ptr, out u);
Process process = Process.GetProcessById(u);
The problem is how to get the standard output handle pointer (stdHandle) of this process.
I would then want something like:
SafeFileHandle safeFileHandle = new SafeFileHandle(stdHandle, true);
FileStream fileStream = new FileStream(safeFileHandle, FileAccess.Write);
Encoding encoding = Encoding.ASCII;
StreamWriter standardOutput = new StreamWriter(fileStream, encoding);
standardOutput.AutoFlush = true;
Console.SetOut(standardOutput);
Code in C++ using windows API is OK - I can use pInvoke.
Effectively what I would like is to write text to an already open console window not spawned by my process (and it is the one that was in foreground when launching my process through command line - but my process is a WinApp so the console does not attach the std).
Can the standard output be redirected after the process has been created?
PS: I read about some COM file that can be used to do this, so this means that there is a programmatic way ...
Thanks!
I finally figured out how to attach transparently to a console if it is the foreground window while launching the windows app.
Don't ask me why STD_ERROR_HANDLE must be passed instead of STD_OUTPUT_HANDLE, but it simply works, probably because the standard error can be shared.
N.B.: the console can accept user input while displaying you app messages inside, but it is a bit confusing to use it while the stderr is outputting from you app.
With this snippet of code if you launch you app from a console window with at least one parameter it will attach Console.Write to it, and if you launch the app with the parameter /debug then it will attach even the Debug.Write to the console.
Call Cleanup() before exiting you app to free the console and send an Enter keypress to release the last line so the console is usable as before starting the app.
PS. You cannto use output redirection with this method ie.: yourapp.exe > file.txt because
you will get an empty file. And dont even try myapp.exe > file.txt 2>&1 because you will crash the app (redirecting error to output means we are trying to attach to a nonshared buffer).
Here is the code:
[DllImport("user32.dll")]
static extern IntPtr GetForegroundWindow();
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool SetForegroundWindow(IntPtr hWnd);
[DllImport("user32.dll", SetLastError = true)]
static extern uint GetWindowThreadProcessId(IntPtr hWnd, out int lpdwProcessId);
[DllImport("kernel32.dll",
EntryPoint = "GetStdHandle",
SetLastError = true,
CharSet = CharSet.Auto,
CallingConvention = CallingConvention.StdCall)]
private static extern IntPtr GetStdHandle(int nStdHandle);
[DllImport("kernel32", SetLastError = true)]
static extern bool AttachConsole(uint dwProcessId);
[DllImport("kernel32.dll",
EntryPoint = "AllocConsole",
SetLastError = true,
CharSet = CharSet.Auto,
CallingConvention = CallingConvention.StdCall)]
private static extern int AllocConsole();
[DllImport("kernel32.dll", SetLastError = true, ExactSpelling = true)]
static extern bool FreeConsole();
private const int STD_OUTPUT_HANDLE = -11;
private const int STD_ERROR_HANDLE = -12;
private static bool _consoleAttached = false;
private static IntPtr consoleWindow;
[STAThread]
static void Main()
{
args = new List<string>(Environment.GetCommandLineArgs());
int prId;
consoleWindow = GetForegroundWindow();
GetWindowThreadProcessId(consoleWindow, out prId);
Process process = Process.GetProcessById(prId);
if (args.Count > 1 && process.ProcessName == "cmd")
{
if (AttachConsole((uint)prId)) {
_consoleAttached = true;
IntPtr stdHandle = GetStdHandle(STD_ERROR_HANDLE); // must be error dunno why
SafeFileHandle safeFileHandle = new SafeFileHandle(stdHandle, true);
FileStream fileStream = new FileStream(safeFileHandle, FileAccess.Write);
Encoding encoding = Encoding.ASCII;
StreamWriter standardOutput = new StreamWriter(fileStream, encoding);
standardOutput.AutoFlush = true;
Console.SetOut(standardOutput);
if (args.Contains("/debug")) Debug.Listeners.Add(new TextWriterTraceListener(Console.Out));
Console.WriteLine(Application.ProductName + " was launched from a console window and will redirect output to it.");
}
}
// ... do whatever, use console.writeline or debug.writeline
// if you started the app with /debug from a console
Cleanup();
}
private static void Cleanup() {
try
{
if (_consoleAttached)
{
SetForegroundWindow(consoleWindow);
SendKeys.SendWait("{ENTER}");
FreeConsole();
}
}
}
If the intention is to write to the parent console, if any, you can use the AttachConsole function with the ATTACH_PARENT_PROCESS argument. (see msdn attachconsole)
ATTACH_PARENT_PROCESS (DWORD)-1 : Use the console of the parent of the current process
And if you do need to check the parent process, you might use the CreateToolhelp32Snapshot and get the parent process thru the th32ParentProcessID member of the PROCESSENTRY32 structure.
If you just want to write to the console that's used by some other app, then you can use the following - you'll need to use P/Invoke to do the first step:
AttachConsole(pid) to attach to that console - if your process is already associated with a console, you'll have to FreeConsole first, since a process can be associated with only one console at a time.
Now that you're attached, get the console output handle using CreateFile("CONOUT$", GENERIC_WRITE, FILE_SHARE_WRITE, ... ) - might be able to do this part in managed code.
Now that you've got the HANDLE, wrap it up in managed code - this part you already know.
Having said that, even though you can do this, it's not necessarily a good idea to do so. There's nothing to stop the original process from writing to the console while you are doing likewise, and the output from both getting mixed-up, depending on how the processes are doing buffering. If you want to do something like notify the user of something regardless of which window is active, there may be a better way of doing that.
A system process is uniquely identified on the system by its process identifier. Like many Windows resources, a process is also identified by its handle, which might not be unique on the computer. A handle is the generic term for an identifier of a resource. The operating system persists the process handle, which is accessed through the Process.Handle property of the Process component, even when the process has exited. Thus, you can get the process's administrative information, such as the Process.ExitCode (usually either zero for success or a nonzero error code) and the Process.ExitTime. Handles are an extremely valuable resource, so leaking handles is more virulent than leaking memory.
This is not the exact answer to ur questions , but it helps u to understand the basic thing actually.

Bring a window to foreground without window handle

I have a Windows CE embedded 6.0 application that opens another app in the background, and I want to bring the other app to the front. I first tried SetParent with the third party app's MainWindowHandle and it didnt work. I then tried SetActiveWindow on the same MainWindowHandle again and it didnt work. This led me to believe that the MainWindowHandle was messed up, and when I print it on the console, its always 0. This brings me to my first question: Is it possible that the dev for the app forgot to mention what the MainWindow is? Or is it assigned automatically in .NET?
Secondly, now that that approach failed, I tried to EnumWindows, then get the ID for each window and match it to the process Id I knew for my required program. This gave me an exception 0x80131515 saying "EnumWindows" is not supported. I have imported EnumWindows from CoreDll just fine. Second question: what could be the cause of this error? What am I doing wrong?
Sorry! Here's some code (Assume VCProcess has already been started):
[DllImport("coredll.dll")]
static extern int EnumWindows(CallbackDef callback, int lParam);
[DllImport("coredll.dll")]
static extern int GetWindowThreadProcessId(IntPtr hWnd, int pid);
static void Main()
{
callBackPtr = new CallBackPtr(Report);
EnumWindows(callBackPtr, 0);
}
public static bool Report(int hwnd, int lParam)
{
int pid = 0;
GetWindowThreadProcessId(hWnd, pid);
if (pid == VCProcessId)
{
SetForegroundWindow(hWnd);
}
MessageBox.show("Window handle is "+hwnd);
return true;
}
Your OEM must not have included support for EnumWindows. You could try FindWindow instead.
I would probably P/Invoke SetForegroundWindow to do this. SetActiveWindow does not work if the application is in the background.
-PaulH
Edit
P/Invoking EnumWindows can't throw a System.NotSupportedException (unless you throw it in your code) and GetLastError() wouldn't return an HRESULT COR_E_NOTSUPPORTED. There's something fishy in your code.
I am answering this question after having the same issue and having it resolved.
While it is true that OEMs may not include certain portions of the OS as part of WindowsCE (being the nature of its modular architecture), it is also true that a call like EnumWindows, or most other low level calls for that matter, are intrinsically part of the OS and it would be crazy to remove them.
I actually received a message back from a Microsoft engineer (!) which pointed out that the issue is the way the callback is defined. While I tried different approaches (delegates, intPtr vs int, and others) he gave the following answer that actually works well in WindowsCE 5/6 for different devices:
"[The “EnumWindows call from .Net/C# Application results in NotSupportedException 0x80131515” error because it ONLY supports Integer return types: I2, I4 etc. This applies to all callback methods and may vary depending on the call being used]"
So INSTEAD OF defining your callback as you did (I tried delegates, WinProcs and others unsuccessfully as well), define it as:
[DllImport("coredll.dll")]
[return: MarshalAs(UnmanagedType.I4)]
private static extern int EnumWindows(IntPtr callPtr, int param);
which works perfectly!!
The following is my working code implementing this approach and works flawlessly in different devices running PocketPC/WindowsCE etc:
public delegate int CallBackPtr(int hwnd, int param);
[DllImport("coredll.dll")]
[return: MarshalAs(UnmanagedType.I4)]
private static extern int EnumWindows(IntPtr callPtr, int param);
private static List<IntPtr> windows = new List<IntPtr>();
private static int CallBackMethod(int hwnd, int param)
{
windows.Add(new IntPtr(hwnd));
return 1;
}
private static void GetAllWindowsHandles()
{
// using a delegate does NOT work.
//EnumWindows(delegate(IntPtr wnd, IntPtr param)
//{
// windows.Add(wnd);
// return true;
//}, IntPtr.Zero);
CallBackPtr callbackPtr = CallBackMethod;
IntPtr cb = Marshal.GetFunctionPointerForDelegate(callbackPtr);
EnumWindows(cb, 0);
}
CJ.

send message to a window handle

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.

Categories