SendMessage disappears into the ether - c#

I'm attempting to pass messages between two applications - one of them is a plugin, and the other is a standalone configuration utility. When my plugin detects an event, I want to send a message to my utility and prompt the user to reconfigure.
The code I'm using is as follows:
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern IntPtr SendMessage(IntPtr hwnd, uint Msg, IntPtr wParam, IntPtr lParam);
private const int MESSAGE_UNAUTH = 0x401;
[... misc logic here, function def, etc]
Process[] processes = Process.GetProcessesByName("MyConfigurationApplication");
if (processes.Length > 0)
{
foreach (Process p in processes)
{
SendMessage(p.MainWindowHandle, MESSAGE_UNAUTH, IntPtr.Zero, IntPtr.Zero);
}
}
And then in my receiving process, I have the following code (I also defined MESSAGE_UNAUTH in this class):
protected override void WndProc(ref Message message)
{
if (message.Msg == MESSAGE_UNAUTH)
{
MessageBox.Show("Message received");
}
base.WndProc(ref message);
}
Things I have already verified with the debugger:
The message is getting sent. All the code in the Sender, including the SendMessage call, is executing.
The message is not getting received (obviously).
The WndProc() function is not getting called at all when the message is sent. It is, however, getting called a whole bunch of times when the configuration utility is launched (I'm assuming this is Windows' behavior).
I've gone through enough online tutorials to need eyedrops, and as far as I know, everything here is syntax-correct and "proper," but for some reason, between when I send the message and when the receiver's WndProc() should be called, black magic is happening.
Any ideas would be greatly appreciated.
Update: Using Marshal.GetLastWin32Error(), I am getting an error #1400, which appears to correspond to an invalid window handle. However, I don't see why that would be the case, since the process was found and we entered the for each loop successfully. The one caveat I can think of is that my configuration utility is presented as a taskbar icon, and doesn't necessarily have a visible window at all times - would this prevent p.MainWindowHandle from being valid? If so, how can I work around this to pass a message to the process instead of the window?
Update: Process.MainWindowHandle is 0, so it looks like that is indeed the problem - when the form in my configuration utility is not visible, no valid window handler is returned, even though my utility icon is visible in the notification bar. Is there any way I can send a message to the process, or even to the taskbar icon?

You can try to enumerate all windows associated with the process. See How to enumerate all windows belonging to a particular process using .NET?

Depending on the .NET framework you are using, this will help resolve your issues.
There was a bug in the old .NET frameworks (2.0 I think) where calling to Process.MainWindowHandle when the process starts up returns 0. Any subsequent call will also result in a 0. This is due to caching the main window handle, which should have been fixed in .NET 3.0 and later.
You might also try giving full trust to your WndProc which might help. Something like:
[System.Security.Permissions.PermissionSet( System.Security.Permissions.SecurityAction.Demand, Name="FullTrust")]
protected override void WndProc(ref Message m)
{
//...
}
On a side note, if you can change your implementation then I strongly suggest you go for better inter process communication means such as sockets, TCPChannel (which I think is replaced by WCF), named pipes...

The message might not be sent, it might be blocked. See here: When a message is blocked by UIPI the last error, retrieved with GetLastError, is set to 5 (access denied).

Use Windows Registermessage in bothe sender and receiver end will resolve the problem

Problem was that the process I was sending the message to only existed as a tooltip icon and not as an active, open window. Turns out the windows message functionality is designed for window-to-window messages, not process-to-process messages.
Solution was aforementioned kludgy system of file IO handlers.

Related

Is it possible to close a process without forcing, killing it? [duplicate]

I have an application that I would like to close from my current C# application. The problem is that the application I want to close has an exit confirmation that requires the user to confirm the closing of the application.
When I use this code:
foreach (Process process in runningProcesses)
{
if (process.ProcessName == "ProcessName")
process.CloseMainWindow();
}
the exit confirmation popup still appears on the other application.
When questions similar to this are asked elsewhere, all I can find are people suggesting process.Kill() to get past the exit confirmation. This is not an option for me as I need the other application to close down gently.
Is there a way to send a closing message to the other application that will force it to start its shutdown process without killing the process abruptly?
If you have to close the application gently and it displays a confirmation when trying to close it, then you'll have to handle it as well.
The actual way to do that depends on what exactly the popup is. If it's a standard dialog, something like the following could suffice:
SendMessage(hDlg, WM_COMMAND, IDOK, 0);
If it's a less standard dialog, but still using standard Windows components (like MFC or WinForms or something), you'll have to inspect its window structure (using Spy++ for example), get the handle of the button you need to press and use something like:
SendMessage(hBtn, BM_CLICK, 0, 0);
If however the dialog doesn't use standard windows (like Qt or WPF), you'll need a lot more specialized code. I'd suggest hooking into the parent dialog and pressing the button yourself to see what events are triggered (Spy++ can do that) and mimicking them with SendMessage.
You can send a WM_ENDSESSION message, which in most applications cause all forms to gracefully close.
PostMessage(process.MainWindowHandle, WM_ENDSESSION, IntPtr.Zero, new IntPtr(ENDSESSION_CLOSEAPP));
Definitions:
[DllImport("user32.dll")]
public static extern IntPtr PostMessage(IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam);
const int WM_ENDSESSION = 0x16;
const int ENDSESSION_CLOSEAPP = 0x1;

SendKeys with games: with some characters it works, but with some it doesn't

I want to simulate input in games with SendKeys, but I have a hard time.
If I use it with i.e. the letter T, while the cursor in Minecraft is in a textbox (in the main menu), it works, the letter T is written in the textbox.
But with {ESC} it doesn't work. Nothing happens. If I press it manually, it backs to the previous menu. (as it should)
With some applications ESC works:
It works with Discord, Sourcetree, Slack, Chrome, CS2D,
but for some reason it doesn't work with Minecraft, Spelunky, Half-Life.
All of the applications mentioned above were in windowed mode.
Another issue:
If I send 2 to Minecraft while in a text field, it works correctly, 2 is written.
But if I send it while I'm playing, there is no effect. (The character should switch to Item Slot #2)
Same with " " (whitespace). In text fields it works, but the character won't jump in the game.
Code:
[DllImport("USER32.DLL", CharSet = CharSet.Unicode)]
public static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
[DllImport("USER32.DLL")]
public static extern bool SetForegroundWindow(IntPtr hWnd);
public Form1()
{
InitializeComponent();
IntPtr minecraftHandle = FindWindow("GLFW30", "Minecraft* 1.15.2");
if (minecraftHandle == IntPtr.Zero)
{
MessageBox.Show("Minecraft is not running.");
return;
}
SetForegroundWindow(minecraftHandle);
SendKeys.SendWait("{ESC}");
}
I tried it without focus switching: by assigning the SendKey calls to a hotkey, so the target application can be in focus when the SendKeys are called.
The results are the same :\
Do not use SendKeys.Send to messaging between processes working on different runtimes
SendKeys.Send method is from System.Windows.Forms namespace.
This means it is not a Windows Input simulator, but just a little helper for Windows Forms applications. There is no guarantee this method work with another process on different (not .NET) runtime system.
Despite the fact that SendKeys.Send method uses native Windows API, it send key pressing message only of fixed period of time, so game frame handling may not have time to catch this message to manage it. So you may need for separate commands to send message about key down and key up events.
Do not use SendKeys API for messaging with another processes, especially with games.
Also, games can use protection system to rid of automatic bots that can blocks any messages from operation system programming input
So, what you can use?
First, you can try to use PostMessage of user32.dll system library:
const uint WM_KEYDOWN = 0x0100;
const uint WM_KEYUP = 0x0101;
[DllImport("user32.dll")]
static extern bool PostMessage(IntPtr hWnd, uint msg, int wParam, int lParam);
// hWnd - Window handler (can be getted by using GetForegroundWindow/FindWindow methods)
// msg - Key up/down message (WM_KEYUP / WM_KEYDOWN)
// wParam - Virual key code you need to pass to the window
// lParam - Additional parameter for set up key message behaviour.
All virtual key codes can be found on microsoft docs website:
https://learn.microsoft.com/en-us/windows/win32/inputdev/virtual-key-codes
Don't forget that you need to wait some time before key releasing. This is needed because games cache inputs between frames, and frame has fixed time to catch input. Just write some delay between key down and up messages.
Also you can set up key message behaviour by lParam. See WM_KEYDOWN and WM_KEYUP parameters. Special thing about WM_KEYDOWN message is if you pressing key on real keyboard long time, operation system repeating WM_KEYDOWN message accordingly. Repeating count can be setted up through lParam. Use it if window you messaging does not react on single keydown message.
PostMessage is low-level system command that can be used for messaging between processes. This command has a low probability to be blocked by protection system (but not zero) and high probability to be received by the game/process you working with. Also it provides opportunity to separate key up and key down messages.
What if PostMessage didn't work?
Try to use hardware scan code instead of virtual key code. Detailed explanation how you can do that described in this answer.
If protection system is really good and PostMessage is blocking even if you use hardware scan code, one thing you can try is to use another keyboard input driver or write it yourself. That driver must replace default system keyboard driver. And you can message it to interact with game. This is the 100% guarantee way to interact with other process through keyboard. But if you use public custom keyboard drivers, there is some probability that protection system blocks it. So you need to write your own driver to message between processes.

How to do "something" when a window that is on background sends a specific message

im trying to do something on an app, that I know its name etc...(so I'm alredy casting findwindow and stuff)
For ex. I want to notify user when that window tries to gain focus.
I have mess around with wndproc yet I seem to not get it at all.
for ex. here is a code I found on stackoverflow and failed even executing it
public IntPtr WndProc(int hwnd, int msg, IntPtr wParam, IntPtr lParam)
{
if (msg == WM_NCACTIVATE)
{
SystemSounds.Beep.Play();
SystemSounds.Beep.Play();
SystemSounds.Beep.Play();
SystemSounds.Beep.Play();
SystemSounds.Beep.Play();
}
return IntPtr.Zero;
}
now the thing I dont understand is, there is int msg that I think stands for message. arent wndproc is the one that supposed to get it ? what is the point of giving it to wndproc ?
Second, executing this function. What do I do, check for message every 100 ms or are there event-type thing for it ?
Im really confused here and I'd appreciate little help here.
I'm afraid I don't entirely understand what you're asking here.
That WndProc function definition that you've found is not what it would look like in C#. Instead, you would override the WndProc member function of the Control class. All window messages are routed through this function. You would only override it if you wanted to process a message in an unusual way, to do something that the .NET Framework isn't already doing for you.
For example, for a Form, you would override WndProc like this:
public class MyForm : Form
{
// other code
protected override void WndProc(ref Message m)
{
switch (m.Msg)
{
case WM_NCACTIVATE:
{
SystemSounds.Beep.Play();
break;
}
base.WndProc(ref m); // proceed with default processing
}
}
}
Notice that the .NET Framework wraps all of the message information up into a Message structure, rather than passing 4 raw parameters to the function like the Win32 API does.
there is int msg that I think stands for message. arent wndproc is the one that supposed to get it ? what is the point of giving it to wndproc ?
The Message.Msg member corresponds to the int msg parameter in your function definition. This is the identifier of the window message that is to be processed. These are all defined inside of the Windows header files, and they generally begin with WM_. You can find the documentation for these messages on MSDN, which will tell you what they mean and when they are received. For example, the WM_NCACTIVATE message is sent to a window when its non-client area is being activated or inactivated.
The WndProc function is going to be called each time any message is received. The way you determine which message was received, and therefore which one you should be processing, is by switching on the value of the Message.Msg member (or, in your original example, the msg parameter).
Second, executing this function. What do I do, check for message every 100 ms or are there event-type thing for it ?
You don't have to check for anything. WndProc is a function, just like any other function, including those that you write yourself, which means that it only gets called when it should execute.
It is not itself an event, although the default processing inside of the WndProc function is what is responsible for raising the events you're familiar with in response to certain messages that it receives.
im trying to do something on an app, that I know its name etc...(so I'm alredy casting findwindow and stuff) For ex. I want to notify user when that window tries to gain focus.
I'm not exactly sure what this means, but you should look into the WM_ACTIVATEAPP message. This message is sent to a window whenever it is being activated and whenever it is being deactivated. In response to that message, you could choose to do whatever you like, including playing a sound. As the linked documentation indicates, the wParam parameter (found in the Message.WParam member) tells you whether your window is being activated or deactivated.
This is pretty much as advanced as it gets. It's extremely rare that you need to override the WndProc method when you're programming in WinForms. By doing so, you can do nearly anything, but there's almost always a better, simpler way to do things.

C# SendKeys Wait for Program to Load before sending

So long story short, I am trying to automate some things when my computer boots up. I thought I'd write an C# console application to do this and then add it to a schedule task in windows to be executed on bootup. My problem is with one program, it requires a password and has no options to open via the command line. Thus it must be entered manually. My thought was to retrieve my password from a KeePass database and use SendKeys to enter the password and login to the program. The problem I'm having is the time it takes to load; I have no way of detecting when the GUI interface has loaded and is ready for my SendKeys. Is there any way to detect this? I'm assuming all I have to work with is the "Process" class since thats what I used to run the program. Also note that when I run the executable using Process.Start(), the program creates another process for logging in, but it has no associated window that I can see using Windows API calls.
Okay that was long, I can re-cap...
Problem:
From C# detecting when a third party program has loaded (i.e. the splash screen is gone and GUI is ready for user interaction - meaning I can't just rely on if the Process is running or not).
Also, no command line options for the third party program, or I would just run it with the password as an argument.
Goal:
To use SendKeys in order to automate entering a password, but my program must wait for the third party application to finish loading.
Notes:
Using C# .NET 3.5 Console Application
NOT detecting load for my own form but a third party otherwise this would be easy (i.e. form_loaded event...)
Thank you for looking at my question, if you want any more details or anything let me know.
UPDATE:
Problem solved!
The two answers I received combined to give me the solution I wanted. So if anyone comes across this later, here is what I did to make it work.
So this program automates a login for some client software that you must login to. My problem was that the software offered not option or documentation for command line prameters which many other programs offer so you can login with a keyfile or something. This program also disabled copy and paste so the password HAS to be typed in manually, which is a big pain if you use passwords like I do, long complicated ones with no patterns. So I wrote this program for my benefit as well others at work; I just schedule it to run at logon to my windows machine and it opens the client software and performs login automatically.
//
// IMPORTANT Windows API imports....
//
[DllImport("user32.dll", SetLastError = true)]
private static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint procId);
[DllImport("user32.dll", SetLastError = true)]
private static extern IntPtr FindWindowEx(IntPtr hwndParent, IntPtr hwndChildAfter, string lpszClass, string lpszWindow);
[DllImport("user32.dll", SetLastError = true)]
private static extern bool SetForegroundWindow(IntPtr hWnd);
// When I get to this point in my code, I already had the password and window title...
string password = "password";
string title = "window title";
// This gets a handle to the window I want where "title" is the text in the title
// bar of the window as a string.
// This is a Windows API function that must be imported by DLLImport
// I had to get the handle this way because all I knew about the third party
// window was the title, not the process name or anything...
IntPtr hWnd = FindWindowEx(IntPtr.Zero, IntPtr.Zero, null, title);
// Now that I have a handle to the login window I used another windows API
// call to get the process ID.
// This windows API call gives me the Process ID as an out parameter and returns
// the thread ID of the window. I don't use the thread it, but maybe you can...
uint loginWindowProcId;
uint loginWindowThreadId = GetWindowThreadProcessId(hWnd, out loginWindowProcId);
// now I can just use .NET to find the Process for me...
Process loginWindowProcess = null;
if (0 != loginWindowProcId)
{
// get the process object
loginWindowProcess = Process.GetProcessById((int)loginWindowProcId);
// This right here is why I wanted the Process structure. It takes a
// little while for the client software to load and be ready. So here
// you wait for the window to be idle so you know it has loaded and can
// receive user input, or in this case keys from "SendKeys".
loginWindowProcess.WaitForInputIdle();
// I use yet another windows API call to make sure that the login window
// is currently in the foreground. This ensures that the keys are sent
// to the right window. Use the handle that we started with.
SetForegroundWindow(hWnd);
// Now send the password to the window. In my case, the user name is
// always there from my windows credentials. So normally I would type in the
// password and press ENTER to login. But here I'll use SendKeys to mimic my
// behavior.
SendKeys.SendWait(password); // send password string
SendKeys.SendWait("{ENTER}"); // send ENTER key
// Now the client should be logging in for you! : )
// IMPORTANT NOTE
// If you are using a console application like I am, you must add a reference to
// System.Windows.Forms to your project and put "using System.Windows.Forms;" in
// your code. This is required to use the "SendKeys" function.
//
// Also this code is just for my testing (quick and dirty), you will want to write
// more checks and catch errors and such. You should probably give the
// WaitForInputIdle a timeout etc...
}
You can check with Process.WaitForInputIdle after you start a process, and wait until is fully started, here is the simple example :
http://msdn.microsoft.com/en-us/library/xb73d10t%28v=vs.71%29.aspx
Try looking at that: http://www.acoolsip.com/a-cool-blog/science-and-technology/151-c-sending-commands-to-independent-windows.html
you can check if the window is shown (using the link) and then sending messages (also on the link)

IntPtr To String conversion in Windows messages

I'm getting in trouble by hooking window messages. I need to detect window text (caption) changes, so I intercept the WM_SETTEXT message for the interesting windows (I do so because at window creation the window caption is not specified).
Reading the documentation of the WM_SETTEXT documentation, the lParam parameter specify a pointer to the string representing the window text.
The message is forwarded to a .NET application using SendMessage. What I do in the .NET application is:
private static bool ProcessMessage(ref Message msg) {
...
string s = Marshal.PtrToStringAuto(msg.LParam) *
}
where ProcessMessage is the routine handling messages of the .NET form.
What I always get is an empty string, which is not the expected result. I tried other Marshal.PtrToString* methods, but no one has worked.
What am I doing wrong in the conversion of a IntPtr to String?
(*)Note that I cannot debug this code, since it would block the entire system, since all windows messages are intercepted.
LParam is a string pointer, and your code is correct, assuming that it is executed in the same process where WM_SETTEXT message was sent. In another process, this pointer is invalid, and result of using this pointer is undefined.
It is probably a pointer to a pointer of chars.
So read the IntPtr, the read the value in IntPtr which is also IntPtr, then use that as you did.
Maybe it works, maybe it doesn't :p

Categories

Resources