c++winapikeyboard-eventssendkeyssendmessage

Simulating/Synthesizing simple key presses within an out-of-focus application window using PostMessage


Trying to write a simple demo program where keyboard input into the console gets translated to key presses on a target window belonging to some other application process. So if I type the character "w" on my keyboard into the console, it translates to appropriate messages sent to the message queue of a target thread that has the application believe that the "w" key was pressed while it's window was in focus.

This is what I've tried:

int main()
{
    // setting up window handle etc...

    println("Enter W, A, S, D or SPACE:");
    while (true)
    {
        int posted_key = NULL;
        int key_char = _getch();
        
        if (key_char==27) break;

        switch(key_char)
        {
            case 119: posted_key = 0x57; break;
            case 97: posted_key = 0x41; break;
            case 115: posted_key = 0x53; break;
            case 100: posted_key = 0x44; break;
            case 32: posted_key = VK_SPACE; break;
        }

        if (posted_key == NULL) continue; // Key isn't W, A, S, D or SPACE

        if (!PostMessage(hndl, WM_KEYDOWN, posted_key, MapVirtualKey(posted_key, MAPVK_VK_TO_VSC))) print("Error while posting WM_KEYDOWN"); break;
        if (!PostMessage(hndl, WM_CHAR, posted_key, MapVirtualKey(posted_key, MAPVK_VK_TO_VSC))) print("Error while posting WM_CHAR"); break;
        Sleep(100);
        if (!PostMessage(hndl, WM_KEYUP, posted_key, MapVirtualKey(posted_key, MAPVK_VK_TO_VSC))) print("Error while posting WM_KEYUP"); break;
    }

    return 0;
}

However, only the WM_KEYDOWN and WM_CHAR messages (1 each) end up actually posting to the queue, and not the WM_KEYUP message, resulting in the application behaving as if the key is being pressed down indefinitely. From using Spy++ on various applications it seems that a quick keypress usually comes in 3s: WM_KEYDOWN, WM_CHAR and then WM_KEYUP a fraction of a second later. So this is what I tried to emulate in my code, but it clearly did not work.

What's more is that my program instantly exits with a return code 0 when I press W, A, S, D or SPACE, and my error messages did not print. So either my program crashed or one of my invocations of PostMessage returned a nonzero value. The latter is probably unlikely since my error messages did not print.

What am I doing wrong? I cannot use SendInput or SendKeys for what I'm trying to do as I need this to work even while the application window is in not in focus.

-- EDIT --

I've misformatted my if conditions in the original.

int main()
{
    // setting up window handle etc...

    println("Enter W, A, S, D or SPACE:");
    while (true)
    {
        int posted_key = NULL;
        int key_char = _getch();

        if (key_char==27) break;

        switch(key_char)
        {
            case 119: posted_key = 0x57; break;
            case 97: posted_key = 0x41; break;
            case 115: posted_key = 0x53; break;
            case 100: posted_key = 0x44; break;
            case 32: posted_key = VK_SPACE; break;
        }

        if (posted_key == NULL) continue; // Key isn't W, A, S, D or SPACE

        if (!PostMessage(hndl, WM_KEYDOWN, posted_key, MapVirtualKey(posted_key, MAPVK_VK_TO_VSC)))
        { 
            print("Error while posting WM_KEYDOWN"); 
            break; 
        }

        if (!PostMessage(hndl, WM_CHAR, posted_key, MapVirtualKey(posted_key, MAPVK_VK_TO_VSC))) 
        { 
            print("Error while posting WM_CHAR"); 
            break; 
        }

        Sleep(100);
        if (!PostMessage(hndl, WM_KEYUP, posted_key, MapVirtualKey(posted_key, MAPVK_VK_TO_VSC))) 
        { 
            print("Error while posting WM_KEYUP"); 
            break; 
        }
    }

    return 0;
}

Now the messages posted are of a different pattern as you can see here. There seems to be a WM_CHAR message of character code '83' posted in between the 3 expected messages for a character code of '119'. And then an extra '119' char code message after the WM_KEYUP message.

As for my program behaviour, it does not exit abruptly anymore indicating that PostMessage is returning a nonzero value.

The target application is however still behaving as if the key is being pressed down indefinitely.


Solution

  • It seems the 4th (lParam) parameter of the PostMessage function requires different kind of data that I was not aware of. Thanks @KungPhoo for bringing my attention to this parameter. This MSDN article explains what needs to go in there. I copied the lParam values from Spy++ after pressing "w" on the target window and hardcoded them into my program and then pressed "w" in the console. This resulted in the desired behaviour and no "indefinite keydown" behaviour. Just need to know how to craft my own lParam.