为啥必须将 SetWindowsHookEx 与 Windows 消息队列一起使用
Posted
技术标签:
【中文标题】为啥必须将 SetWindowsHookEx 与 Windows 消息队列一起使用【英文标题】:Why must SetWindowsHookEx be used with a windows message queue为什么必须将 SetWindowsHookEx 与 Windows 消息队列一起使用 【发布时间】:2011-11-19 12:05:24 【问题描述】:我一直在尝试用钩子做一些事情,但我不明白为什么钩子必须与消息队列一起使用
hook = SetWindowsHookEx(WH_KEYBOARD_LL, KeyboardProc, NULL, 0);
MSG msg;
while(GetMessage(&msg, NULL, 0, 0) > 0)
TranslateMessage(&msg);
DispatchMessage(&msg);
UnhookWindowsHookEx(hook);
为什么这样的东西不起作用?
hook = SetWindowsHookEx(WH_KEYBOARD, KeyboardProc, NULL, 0);
cin >> aKey;
UnhookWindowsHookEx(hook);
使用 boost 线程,屏障也不起作用。为什么hook和unhook之间的等待不能换一种方式呢?
编辑:
我在创建这个示例时犯了一个错误,我创建了一个 WH_KEYBOARD_LL 钩子,而不是 WH_KEYBOARD,(我认为这不会有很大的不同)
此外,循环永远不会执行,只会等待 GetMessage 函数。
只有在我发布退出消息PostThreadMessage(id, WM_QUIT, 2323, NULL);
时才会执行循环,所以我不明白它除了等待之外还有什么作用,是否有一些内部处理?
相关:
C++ SetWindowsHookEx WH_KEYBOARD_LL Correct Setup
How can I set up a CBT hook on a Win32 console window?
【问题讨论】:
【参考方案1】:Windows Hooks 钩住 Windows 消息循环:http://msdn.microsoft.com/en-us/library/ms644959#wh_keyboardhook
WH_KEYBOARD 挂钩使应用程序能够监控消息流量 用于将要返回的 WM_KEYDOWN 和 WM_KEYUP 消息 GetMessage 或 PeekMessage 函数。您可以使用 WH_KEYBOARD 挂钩 监视发布到消息队列的键盘输入。
控制台应用程序本身不会发送消息 - 控制台进程会。所以除非进程有消息循环,否则它不会工作。
见:
How can I set up a CBT hook on a Win32 console window?
C++ SetWindowsHookEx WH_KEYBOARD_LL Correct Setup
【讨论】:
感谢您提供的链接,我浏览了所有这些链接,但我仍然不明白 GetMessage 除了等待之外还能做什么。在我的情况下(控制台应用程序),当我将退出消息发布到线程时,它唯一一次执行它。 另外,由于 GetMessage 等待,这是否意味着控制台在我的退出消息之前不会发送任何消息? 查看 Han 的回答 - Windows 只会在知道线程空闲时回调您的线程。如果线程在 GetMesasge() 中被阻塞,Windows 就知道它没有做任何事情【参考方案2】:低级钩子 WH_KEYBOARD_LL 和 WH_MOUSE_LL 与所有其他钩子不同。它们不需要将 DLL 注入目标进程。相反,Windows 在您自己的进程中直接调用您的挂钩回调。为了使它工作,需要一个消息循环。 Windows 没有其他机制可以在您的主线程上进行回调,回调只能在您调用 Get/PeekMessage() 以使 Windows 处于控制状态时发生。
像 WH_KEYBOARD 这样的全局钩子是非常不同的。它需要一个 DLL,并且回调发生在处理键盘消息的进程中。您需要某种进程间通信来让您自己的程序意识到这一点。命名管道是通常的选择。否则当然需要这个注入的进程泵送一个消息循环。否则它不会收到键盘消息。
支持低级钩子,它们更更容易上手。但是做泵,否则它不会工作。并且要注意超时,如果你没有足够的响应,那么 Windows 会在没有通知的情况下杀死你的钩子。
Understanding the low-level mouse and keyboard hook (win32)
【讨论】:
所以,如果我理解正确,Windows 只能在 Get/PeekMessage() 调用中调用我的回调? 你理解正确。没有其他方法可以安全地将调用注入线程,它必须处于空闲状态。这就是存在消息循环的原因。 @HansPassant 澄清一下,您是否建议像 WH_KEYBOARD 命名管道这样的全局挂钩可用于将信息“传递”回我自己的程序,并且该程序不需要消息泵?以上是关于为啥必须将 SetWindowsHookEx 与 Windows 消息队列一起使用的主要内容,如果未能解决你的问题,请参考以下文章
SetWindowsHookEx 使用谷歌浏览器失败。错误代码 87 参数无效
无法在 Borland C++ Builder 中使用 SetWindowsHookEx 和 LowLevelKeyboardProc