March 19th, 2025
likeheart7 reactions

You can’t simulate keyboard input with PostMessage, revisited

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_KEYBOARD hook, and the posted messages weren’t triggering the hook, so the program saw input come in the message queue with no corresponding WH_KEYBOARD 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_KEYBOARD hook), and only keyboard input goes through the WH_KEYBOARD hook, because it is when a message is fetched from the input queue that the WH_KEYBOARD 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_KEYBOARD 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 Send­Input 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.

Topics

Author

Raymond has been involved in the evolution of Windows for more than 30 years. In 2003, he began a Web site known as The Old New Thing which has grown in popularity far beyond his wildest imagination, a development which still gives him the heebie-jeebies. The Web site spawned a book, coincidentally also titled The Old New Thing (Addison Wesley 2007). He occasionally appears on the Windows Dev Docs Twitter account to tell stories which convey no useful information.

9 comments

Discussion is closed. Login to edit/delete existing comments.

Sort by :
  • Zakaria TAHRI

    What a great coincidence that this blog came out just as I was trying to find a way to simulate keyboard input without changing the keyboard layout! I was using keybd_event, but after coming across this commit, it looks like using SendInput is the best way to do it. Thanks for sharing this!

  • Georg Rottensteiner

    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.

  • 許恩嘉 · Edited

    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.

  • skSdnW

    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.