3

My approach to this problem turned out to be correct only in several programs. Why it isn't universal?

Works fine on:

  • Firefox

  • Visual Studio Text Editor
  • Unfortunately, in some cases nothing happens(even if I click into a textbox area before execution of my program):

  • Google Chrome
  • Notepad
  • GetLastError returns always 0, even using SendMessage instead of PostMessage.Could you point out my mistake?

    #include <Windows.h>
    #include <iostream>
    
    int main()
    {
        HWND hCurrentWindow;
    
        Sleep(5000);
    
        hCurrentWindow = GetForegroundWindow();
    
        std::cout<<"GO!!!\n";
    
        for(int i=0; i<500; i++) //simulate 500 keystrokes of 'E'.
            {
                PostMessage(hCurrentWindow,WM_KEYDOWN,0x45,NULL);
                PostMessage(hCurrentWindow,WM_KEYUP,0x45,NULL);
            }
    
        std::cout<<GetLastError()<<std::endl;
    
        system("Pause");
        return 0;
    }
    

    UPDATE after Maximus sugestion

    #include <Windows.h>
    #include <iostream>
    
    int main()
    {
        HWND hCurrentWindow;
    
        Sleep(5000);
    
        hCurrentWindow = GetForegroundWindow();
    
        if(!hCurrentWindow)
            std::cout<<"Failed get set the window handle\n";
    
        std::cout<<"GO!!!\n";
    
        for(int i=0; i<500; i++)
            {
                PostMessage(hCurrentWindow,WM_KEYDOWN,0x45,0x45);
                PostMessage(hCurrentWindow,WM_KEYUP,0x45,0x45);
            }
    
        std::cout<<GetLastError()<<std::endl;
    
        system("Pause");
        return 0;
    }
    

    There is no difference in effect.

    UPDATE after Rob Kennedy's comment and Hans Passant's answer

    #include <Windows.h>
    #include <iostream>
    
    int main()
    {
        HWND hCurrentWindow;
        DWORD procID;
        GUITHREADINFO currentWindowGuiThreadInfo;
    
        Sleep(5000);
    
        hCurrentWindow = GetForegroundWindow();
    
        if(!hCurrentWindow)
            std::cout<<"Failed get main the window handle\n";
    
        GetWindowThreadProcessId(hCurrentWindow,&procID); 
        GetGUIThreadInfo(procID,&currentWindowGuiThreadInfo);               
        hCurrentWindow = currentWindowGuiThreadInfo.hwndFocus;
    
        if(!hCurrentWindow)
            std::cout<<"Failed get the child window handle\n";
    
        std::cout<<"GO!!!\n";
    
        for(int i=0; i<500; i++)
            {
                PostMessage(hCurrentWindow,WM_KEYDOWN,0x45, MapVirtualKey(0x45,MAPVK_VK_TO_VSC));
                PostMessage(hCurrentWindow,WM_KEYUP,0x45, MapVirtualKey(0x45,MAPVK_VK_TO_VSC));
            }
    
        std::cout<<GetLastError()<<std::endl;
    
        system("Pause");
        return 0;
    }
    

    Now, "transparent" messages are sent every time. GetLastError() says:

    ERROR_INVALID_WINDOW_HANDLE

    1400 (0x578)
    
    Invalid window handle.
    

    GetLastError() "fixed"

    int main()
    {
        HWND hCurrentWindow;
        DWORD procID;
        GUITHREADINFO currentWindowGuiThreadInfo;
    
        Sleep(5000);
    
        hCurrentWindow = GetForegroundWindow();
    
        if(!hCurrentWindow)
            std::cout<<"Failed get main the window handle\n";
    
        GetWindowThreadProcessId(hCurrentWindow,&procID); 
        GetGUIThreadInfo(procID,&currentWindowGuiThreadInfo);               
        hCurrentWindow = currentWindowGuiThreadInfo.hwndFocus;
    
        if(!hCurrentWindow)
            std::cout<<"Failed get the child window handle\n";
    
        std::cout<<"GO!!!\n";
    
        for(int i=0; i<500; i++)
            {
    
                if(!PostMessage(hCurrentWindow,WM_KEYDOWN,0x45, MapVirtualKey(0x45,MAPVK_VK_TO_VSC))) std::cout<<GetLastError()<<std::endl;
                if(!PostMessage(hCurrentWindow,WM_KEYUP,0x45, MapVirtualKey(0x45,MAPVK_VK_TO_VSC)))   std::cout<<GetLastError()<<std::endl;
            }
    
    
    
        system("Pause");
        return 0;
    }
    

    ...outputs 1400 thousand times. Except this, nothing has changed.

    0x6B6F77616C74
    • 2,559
    • 7
    • 38
    • 65
    • Maximus suggested you include the scan code in the `lParam` argument. You haven't done that, though. Your second block of code sets the *repeat count*. Please refer to MSDN for what all the bits of `lParam` mean. – Rob Kennedy Aug 09 '12 at 20:21
    • 7
      Have you considered just using `SendInput` instead? That's what it's for. – Rob Kennedy Aug 09 '12 at 20:22
    • 5
      PostMessage will never be able to simulate input perfectly because there is other state that PostMessage does not update (like shift key states). – Raymond Chen Aug 09 '12 at 20:24
    • @RobKennedy Thanks, I've never heard about this function before and I found it useful. However, my question is still open. – 0x6B6F77616C74 Aug 09 '12 at 20:26
    • 1
      Sounds to me like an answer, @Raymond. – Rob Kennedy Aug 09 '12 at 20:35
    • Better use SendInput for sending keyboard and mouse inputs. I personally have used this function successfully on all windows applications. http://msdn.microsoft.com/en-us/library/windows/desktop/ms646310(v=vs.85).aspx – jsist Aug 10 '12 at 11:26

    4 Answers4

    6

    This will of course happen when you post the message to the wrong window. Which is certainly the case for Notepad. It doesn't have just one window, something you can see with Spy++. GetForegroundWindow() returns you a top-level window, the frame window for Notepad. Inside of that frame window it has a child window, an EDIT control. That window needs to get the messages.

    You'll need to jump through a few hoops to get that window, the GetFocus() function returns the window with the focus but that only works if you call it from the process that owns the window. When you do this out-of-process then you must first call GetWindowThreadProcessId() to get the ID of the thread that owns the foreground window. Then you must call GetGUIThreadInfo(), the GUITHREADINFO.hwndFocus it returns is the window handle you need.

    This is still not without trouble, you cannot control the keyboard state of the process. In other words, the state of the Shift, Ctrl and Alt keys as well as any dead keys (like Alt+Gr on certain keyboard layouts). Favor sending WM_CHAR for typing keys.

    Hans Passant
    • 922,412
    • 146
    • 1,693
    • 2,536
    2

    Something else to bear in mind is that you can't always assume that the target application is handling user input in the same way. For example they could be using something GetAsyncKeyState() instead, causing your posted messages to have no effect at all.

    OJ.
    • 28,944
    • 5
    • 56
    • 71
    1

    You send lParam==NULL. It contains the scan code of the key which may be needed by the applications which aren't working currently.

    Also, your code does not check the return value of GetForegroundWindow(). It may be NULL.

    Lastly, you omit WM_CHAR, which is an important part of keyboard cycle.

    ApprenticeHacker
    • 21,351
    • 27
    • 103
    • 153
    Maximus
    • 10,751
    • 8
    • 47
    • 65
    1

    Between sending the KeyDown and KeyUp you need any delay in some cases. I solved my similar situation by adding a pause of 100ms after KeyDown

        {
            PostMessage(hCurrentWindow,WM_KEYDOWN,0x45, MapVirtualKey(0x45,MAPVK_VK_TO_VSC));
            Sleep(100);
            PostMessage(hCurrentWindow,WM_KEYUP,0x45, MapVirtualKey(0x45,MAPVK_VK_TO_VSC));
        }
    
    Nasenbaer
    • 4,810
    • 11
    • 53
    • 86