使用PostMessage模拟按键仅适用于某些应用程序?

时间:2012-08-09 19:54:04

标签: c++ winapi keypress

我对此问题的处理方法仅在几个程序中才是正确的。为什么它不普遍?

正常工作:

  • 火狐

  • Visual Studio文本编辑器
  • 不幸的是,在某些情况下没有任何反应(即使我在执行程序之前点击文本框区域):

  • Google Chrome
  • 记事本
  • GetLastError总是返回0,即使使用SendMessage而不是PostMessage。你能否指出我的错误?

    #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;
    }
    

    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;
    }
    

    效果没有区别。

    Rob Kennedy的评论和Hans Passant的回答后更新

    #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;
    }
    

    现在,每次都会发送“透明”消息。 GetLastError()说:

      

    ERROR_INVALID_WINDOW_HANDLE

    1400 (0x578)
    
    Invalid window handle.
    

    GetLastError()“已修复”

    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;
    }
    

    ...输出1400千次。除此之外,没有任何改变。

    4 个答案:

    答案 0 :(得分:5)

    当您将消息发布到错误的窗口时,这当然会发生。对于记事本来说肯定是这样的。它没有一个窗口,你可以用Spy ++看到它。 GetForegroundWindow()返回一个顶级窗口,即记事本的框架窗口。在该框架窗口内部,它有一个窗口,一个EDIT控件。该窗口需要获取消息。

    你需要跳过几个环来获得那个窗口,GetFocus()函数返回带焦点的窗口,但只有从你拥有窗口的进程中调用它才有效。当您在进程外执行此操作时,必须首先调用GetWindowThreadProcessId()以获取拥有前台窗口的线程的ID。然后你必须调用GetGUIThreadInfo(),它返回的GUITHREADINFO.hwndFocus是你需要的窗口句柄。

    这仍然不是没有问题,你无法控制进程的键盘状态。换句话说,Shift,Ctrl和Alt键的状态以及任何死键(如某些键盘布局上的Alt + Gr)。喜欢发送WM_CHAR来键入密钥。

    答案 1 :(得分:2)

    要记住的其他事情是,您不能总是假设目标应用程序以相同的方式处理用户输入。例如,他们可能正在使用GetAsyncKeyState()来代替,导致您发布的消息完全没有效果。

    答案 2 :(得分:1)

    您发送lParam==NULL。它包含当前不工作的应用程序可能需要的密钥的扫描代码。

    此外,您的代码不会检查 GetForegroundWindow()的返回值。它可能是 NULL

    最后,省略 WM_CHAR ,这是键盘循环的重要部分。

    答案 3 :(得分:1)

    在发送KeyDown和KeyUp之间,在某些情况下需要delay。我通过在KeyDown

    之后添加100ms的暂停来解决我的类似情况
        {
            PostMessage(hCurrentWindow,WM_KEYDOWN,0x45, MapVirtualKey(0x45,MAPVK_VK_TO_VSC));
            Sleep(100);
            PostMessage(hCurrentWindow,WM_KEYUP,0x45, MapVirtualKey(0x45,MAPVK_VK_TO_VSC));
        }