A customer was trying to manipulate another program, so they tried posting messages to it. They found that input didn’t work reliably because the program had its own WH_
hook, and the posted messages weren’t triggering the hook, so the program saw input come in the message queue with no corresponding WH_
hook, and things got out of sync and didn’t work right. What’s going on?
What’s going on is that you can’t simulate keyboard input with PostMessage. Posting input messages is like prank calling the program. “Hello, this is (giggle) the input system? Yeah, and um (covers handset, whispers what should I say? back into handset), yeah the user hit the um, the Enter key? When? Um, like (other person whispers in their ear) 5 seconds ago? Yeah, okay, so like Enter key, got it? Cool, thanks.” How successful this is depends on how much the program allows itself to be fooled.
In this case, the program was correlating the input messages with information from another channel (the WH_
hook), and only keyboard input goes through the WH_
hook, because it is when a message is fetched from the input queue that the WH_
hook is called. Posted messages masquerading as keyboard input don’t come from the input queue; they come from the posted message queue. And the code that pulls messages from the posted message queue doesn’t call the WH_
hook.
As usual, the typical¹ answers are either to use UI Automation to drive the target program, or if the target program’s support for UI Automation is insufficient, you can use SendInput
to generate synthetic input. Synthetic input is treated like real input, and it goes through the input system like hardware input.¹
¹ An atypical answer is to use the target program’s object model if it offers one. For example, Microsoft Word lets you manipulate the application and its active document through its Application object.
² Synthetic input can still be detected, for example, by looking for the LLKHF_INJECTED
flag in the KBDLLHOOKSTRUCT
‘s flags.
Using UI Automation is the preferred method, obviously. However at least with .NET 8.0 Forms (and probably also 6.0) you can’t use it for everything. Clicking a button waits until the button handler returns. Which is unfortunate for automation, when the button click opens a modal dialog which you also want to control. This didn’t happen with an older version of Forms.
These are the cases when I didn’t find anything better than using PostMessage to place a mouse click on the button and hope for the best.
You can’t simulate keyboard input with PostMessage perfectly. But there are indeed many programs that can be fooled by this trick.
I once came across an application where I could simulate keyboard input via PostMessage only if it had keyboard focus.
In order to simulate input to the application while it is in the background, I sent it a fake WM_SETFOCUS message, which successfully fooled it.
SendInput takes an array so you can perform “atomic” up/down events etc. without it getting mixed with real user input that might be happening at the same time.
Keyboard hooks and SendInput takes me back in time, i wrote a little utility which took the selected text from any program and could list files and other things matching the text string.
It used keyboard hooks to register a global hotkey, and i think SendInput was used to do Ctrl-C to put the selected text into Clipboard from where it was later read, although it might have been too brittle so it just resorted to read from Clipboard directly and it was up to the user to put it there.
It also used Raymond's favourite AttachThreadInput function to be...
I know I can’t simulate keyboard input with PostMessage, and yet I still resort to trying to do so (with extra pains to simulate raw input and other fun bits like the mentioned here keyboard hooks). The reason? Windows does not provide a supported way to simulate input for programs that are not in the foreground. I can't run multiple programs in parallel and simulate input in each, unless they are cooperative. The only supported options that I can think of are prohibitively expensive: run each in its own VM (Windows license per program instance) or use Windows Server and...
I still have an application I wrote years ago that uses mouse_event. Now that I look at the documentation for mouse_event and keybd_event it says “This function has been superseded. Use SendInput instead.”. Should I update my application before a Windows update breaks it or is SendInput just a wrapper for those two functions or does it do something more? The documentation for SendInput mentions these two only in context of “These events are not interspersed with other (…)”.
I still use keybd_event without problems on several Win10 machines. Much easier API than SendInput IMHO.