-2

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.

  • How many return values do you get from _getch? I experienced mulitple values for some keys (ESC e.g.). Is that the problem? Or the MapVirtualKey? What does it return? Maybe you're sending a unicode character? – KungPhoo Feb 23 '23 at 05:39
  • @KungPhoo just one value. Each iteration of the while loop I am having the console read just one keypress. I believe `MapVirtualKey` returns a virtual scan code. I am only testing pressing the W, A, S, D and Spacebar keys as those are the only keys I am currently interested in. – Farhan Alvi Feb 23 '23 at 05:48
  • 1
    Have you tried sending the returned character directly? I think the mapping does not what you want it to do. – KungPhoo Feb 23 '23 at 05:53
  • @KungPhoo looked more into the `lParam` parameter. I've added an answer. – Farhan Alvi Feb 23 '23 at 07:43
  • [You can't simulate keyboard input with PostMessage](https://devblogs.microsoft.com/oldnewthing/20050530-11/?p=35513). – IInspectable Feb 23 '23 at 09:01
  • @IInspectable I've read this. Raymond is correct about shift-states and Unicode character complexity. However for my simple demo where my program is not shift-sensitive nor do I use any complex characters, I don't see why I cannot use PostMessage. – Farhan Alvi Feb 23 '23 at 10:15
  • You seem to have glossed over a crucially important detail: *"Posting keyboard input messages [...] is not reliable **for many reasons**."* The article goes on to spell out *some* of them, but not all. If you need to automate a UI, use [UI Automation](https://learn.microsoft.com/en-us/windows/win32/winauto/entry-uiauto-win32). – IInspectable Feb 23 '23 at 10:28

2 Answers2

1

I think the issue will become obvious if you properly format your code.

Compare:

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

with

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

Hint: you might need some curly braces.

Vlad Feinstein
  • 10,960
  • 1
  • 12
  • 27
0

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.