C++ 从按钮正确接收消息

C++ 从按钮正确接收消息,c++,winapi,C++,Winapi,我想正确地接收来自另一个窗口按钮的消息。可能我需要使用挂钩,但我不确定这件事(这就是为什么我要问的)。因此,我想做的这件事(但它不能正常工作): void CaptureTextWindow::Execute() { 布尔布雷特; 向量文本窗口(ExecuteCapturingWindow()); 而(!(bRet=GetMessage(NULL,hWndBut,0,0))) { SaveResultToFile(“Message.txt”); } } 所以这就是我想要的,如果按下另一个窗口(

我想正确地接收来自另一个窗口按钮的消息。可能我需要使用挂钩,但我不确定这件事(这就是为什么我要问的)。因此,我想做的这件事(但它不能正常工作):

void CaptureTextWindow::Execute()
{
布尔布雷特;
向量文本窗口(ExecuteCapturingWindow());
而(!(bRet=GetMessage(NULL,hWndBut,0,0)))
{
SaveResultToFile(“Message.txt”);
}
}

所以这就是我想要的,如果按下另一个窗口(进程)的按钮,这个程序应该将某物保存到一个文件中,否则它应该等待

要检测另一个进程中的按钮按下,必须挂接该进程。您有四种选择:

  • 用于将
    WH_CALLWNDPROC
    WH_GETMESSAGE
    挂钩安装到目标进程中,以捕获单击按钮时发送到按钮父窗口的消息。该消息标识所单击按钮的
    HWND
    和ID。钩子必须在DLL中实现,以便可以注入到另一个进程中

  • 用于在单击按钮时捕获调用的
    事件\u对象\u
    事件。钩子回调标识被单击按钮的
    HWND
    和ID。钩子应该在DLL中实现,这样就可以将它注入目标进程以获得更好的性能,但不是必需的。如果不使用DLL,挂接线程必须有一个消息循环来接收事件,因为它们必须跨进程边界传递

  • 使用或将代码注入目标进程,然后使用或将该代码作为按钮父窗口的子类,以便捕获发送到该父窗口的消息

  • 使用。使用
    CoCreateInstance()
    函数创建接口实例,然后使用该方法从按钮的
    HWND
    获取接口(或通过其他方式获取按钮的接口),然后使用该方法订阅该按钮的
    UIA\u Invoke\u InvokedEventId
    事件

  • 如果您有目标按钮的
    HWND
    ,您可以使用来检索按钮的进程ID和线程ID。您需要按钮的父窗口
    HWND
    (如果需要,可以使用该窗口)来使用或

    并允许您指定要挂接的特定线程ID(在的情况下,还可以指定特定的进程ID),以便最小化挂接中使用的开销。需要目标进程句柄,可以使用进程ID获取该句柄

    因此,正如您所看到的,您所要求的并不是简单的实现。在我看来(不是确定的),就编码而言,从最简单到最难的技术顺序是:

  • 没有DLL(但以影响性能为代价)

  • ,或使用DLL

  • 由于额外的注入代码和子类化

  • @雷米,你的建议可以用来检测标准的按钮点击。假设您需要检测系统菜单项单击?如果通过右键单击浏览器状态栏(屏幕底部)中的窗口打开系统菜单,则系统菜单的所有者是GetDesktopWindow(),或者在IUIAutomation术语中是GetRootElement。这里是你提到的“事件对象调用”的问题。WinEventProc将返回UIA_MenuItemControlTypeId的hwnd。启动检查,选中“选项”子菜单中的“观察光标”,然后选择您的系统菜单项。你会得到:

    Ancestors:  "Systemmenü" Menü
    "Desktop 1" Bereich
    [ No Parent ]
    
    呸呸!桌面窗口的系统菜单是唯一的,可用于所有窗口。您如何确定此系统菜单当前与哪个窗口关联?Microsoft显然知道,因为如果您单击该菜单上的某个项目,系统将在适当的窗口上执行分配给已单击菜单项的默认操作。可惜我们不知道微软是怎么做到的

    因此,可以使用“GetWindowThreadProcessId”。所以,在“SetWinEventHook(事件\u对象\u调用…”的WinEventProc中,我们使用

    我们得到了线程id。在Spy++中,我们看到实际拥有sysmenu的顶部窗口被列为该线程的后代:

    Thread 0000000000000D60 WIN32CALC
        Window: CalcFrame
        ...
    
    如何以编程方式获取窗口HWND?EnumThreadWindows?每个线程控制数十、数百个窗口!我们将如何比较EnumThreadWindows返回的HWND?让我们停在这里,因为无论采用何种解决方案,这都会使cpu紧张

    您建议在元素上使用AddAutomationEventHandler。但是,假设您在每个可见的顶部窗口的系统菜单中插入一个自定义系统菜单项。是否要为您添加的每个菜单项添加事件处理程序?这在cpu使用率方面会很昂贵。假设打开20个窗口,要监视20个元素。是吗甚至可以通过iUI自动监控系统菜单点击

    CoInitializeEx(NULL, COINIT_MULTITHREADED);
    
    IUIAutomation6 *pAutomation6 = NULL;
    
    CoCreateInstance(CLSID_CUIAutomation, NULL, CLSCTX_INPROC_SERVER, IID_IUIAutomation, reinterpret_cast<void**>(&pAutomation6));
    
    IUIAutomationEventHandlerGroup *AutomationGroup = NULL;
    
    pAutomation6 -> CreateEventHandlerGroup(&AutomationGroup);
    
    AutomationEventHandler* handler = new AutomationEventHandler();
    
    AutomationGroup -> AddAutomationEventHandler(???, TreeScope_Element, NULL, (IUIAutomationEventHandler*) handler); // first parameter to detect a sysmenu item click, please?
    
    因此,我们有一个堆栈溢出(可能?因为我没有得到),但我们肯定没有被单击的系统菜单项的id。您提到的UIA_Invoke_invoked不是sysmenu事件的正确事件标识符,它会触发按钮等(如LTSC win32calc.exe计算器中的数字),对于系统菜单,我们必须使用UIA_MenuModeEndEventId或UIA_MenuClosedEventId(区别?),但这将如何帮助我们呢?HandleAutomationEvent在系统菜单关闭时触发,以任何方式关闭,您不必单击菜单项来关闭它,如果您简单地关闭菜单,事件也会触发。HandleAutomationEvent不会告诉您单击了哪个菜单项(如果有!)

    还有什么?SetWindowsHookEx?昂贵。子类化?需要在所有受影响的窗口中注入dll

    我最后使用了“SetWi”
    DWORD i = GetWindowThreadProcessId(hwnd, NULL);
    
    Thread 0000000000000D60 WIN32CALC
        Window: CalcFrame
        ...
    
    CoInitializeEx(NULL, COINIT_MULTITHREADED);
    
    IUIAutomation6 *pAutomation6 = NULL;
    
    CoCreateInstance(CLSID_CUIAutomation, NULL, CLSCTX_INPROC_SERVER, IID_IUIAutomation, reinterpret_cast<void**>(&pAutomation6));
    
    IUIAutomationEventHandlerGroup *AutomationGroup = NULL;
    
    pAutomation6 -> CreateEventHandlerGroup(&AutomationGroup);
    
    AutomationEventHandler* handler = new AutomationEventHandler();
    
    AutomationGroup -> AddAutomationEventHandler(???, TreeScope_Element, NULL, (IUIAutomationEventHandler*) handler); // first parameter to detect a sysmenu item click, please?
    
    When searching for top-level windows on the desktop, be sure to specify TreeScope_Children in the scope parameter, NOT TreeScope_Descendants [TreeScope_Subtree is worse!]. A search through the entire subtree of the desktop could iterate through thousands of items and lead to a stack overflow.