【问题标题】:Simulating key press with PostMessage only works in some applications?使用 PostMessage 模拟按键仅适用于某些应用程序?
【发布时间】:2012-08-07 02:59:25
【问题描述】:

我解决这个问题的方法被证明只在几个程序中是正确的。为什么它不是通用的?

适用于:

  • 火狐

  • Visual Studio 文本编辑器
  • 不幸的是,在某些情况下什么也没有发生(即使我在执行程序之前单击文本框区域):

  • 谷歌浏览器
  • 记事本
  • 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 建议后更新

    #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 千次。除此之外,没有任何改变。

    【问题讨论】:

    • Maximus 建议您在 lParam 参数中包含扫描码。不过,你还没有这样做。您的第二个代码块设置了重复计数。请参阅 MSDN,了解 lParam 的所有位的含义。
    • 您是否考虑过只使用SendInput 代替?这就是它的用途。
    • PostMessage 将永远无法完美模拟输入,因为 PostMessage 有其他状态不会更新(如 shift 键状态)。
    • @RobKennedy 谢谢,我以前从未听说过这个功能,我发现它很有用。但是,我的问题仍然悬而未决。
    • 听起来像是一个答案,@Raymond。

    标签: c++ winapi keypress


    【解决方案1】:

    在某些情况下,在发送 KeyDown 和 KeyUp 之间,您需要任何 delay。我通过在 KeyDown 后添加 100 毫秒的暂停解决了我的类似情况

        {
            PostMessage(hCurrentWindow,WM_KEYDOWN,0x45, MapVirtualKey(0x45,MAPVK_VK_TO_VSC));
            Sleep(100);
            PostMessage(hCurrentWindow,WM_KEYUP,0x45, MapVirtualKey(0x45,MAPVK_VK_TO_VSC));
        }
    

    【讨论】:

    • 不确定是否正确,扫描码必须是lParam的第16-23位。
    【解决方案2】:

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

    【讨论】:

      【解决方案3】:

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

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

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

      【讨论】:

      • 除非 PostMessage 返回 FALSE,否则不调用 GetLastError()。
      • 再次更新。我不知道我是否做到了你的意思。
      【解决方案4】:

      您发送lParam==NULL。它包含当前未运行的应用程序可能需要的密钥扫描码。

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

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

      【讨论】:

      • WM_CHAR 由 TranslateMessage 注入,不应由您的代码发送,除非它是您发送的唯一消息。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2014-09-16
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多