首页 > 解决方案 > 未收到 WH_KEYBOARD_LL 处理程序 PostThreadMessage 消息的 SetWindowsHookEx?

问题描述

我设置了一个处理程序来在短时间内捕捉击键,同时为窗口使用私人消息循环。虽然它有效,但如果我尝试PostThreadMessage窗口永远不会收到消息?

回调看起来像(我尝试了其他消息,这个示例带有私人消息):

LRESULT CALLBACK LLKeyboardHookProc(int ncode, WPARAM wp, LPARAM lp) 
{
  if (ncode ==  HC_ACTION) {
    if (wp==WM_KEYDOWN) {
      KBDLLHOOKSTRUCT *kbs=reinterpret_cast<KBDLLHOOKSTRUCT*>(lp);
      if (kbs->vkCode==VK_ESCAPE) {
        PostThreadMessage (GetCurrentThreadId(), UWM_MYCLOSEMESSAGE, 0, 0);
      }
    }
  }
  return CallNextHookEx(0, ncode, wp, lp);
}

HHOOK kbhook = SetWindowsHookEx(WH_KEYBOARD_LL, LLKeyboardHookProc, GetModuleHandle(NULL), 0);

WS_EX_LAYERED窗口的消息循环如下所示:

  while (IsWindow(hwnd)) {
    MSG msg;
    while (::PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
      ::TranslateMessage(&msg);
      ::DispatchMessage(&msg);
    }
    // let MFC do its idle processing
    LONG lIdle=0;
    while (AfxGetApp()->OnIdle(lIdle++));

    WaitMessage();
  }

  // clean up
  UnhookWindowsHookEx(kbhook);

我现在将其更改为使用 volatile 变量来关闭窗口,但我真的很想给它发送一条消息。如果没有锁,使用全局 HWND 就不是线程安全的,所以不想那样做。

PS我确实检查了 PostThreadMessage 结果,它不是 FALSE(添加它们时没有调试消息),所以它似乎将它发布在某个地方。

标签: winapisetwindowshookex

解决方案


PostThreadMessage()线程消息发布到队列,而不是窗口消息。您的消息循环忽略线程消息,仅调度窗口消息。窗口永远不会看到线程消息,因此您需要直接在消息循环中处理此类消息,例如:

MSG msg;
while (::PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
    if (msg.hwnd == NULL && msg.message == UWM_MYCLOSEMESSAGE) { // <-- add this
        // do something...
    }
    else {
        ::TranslateMessage(&msg);
        ::DispatchMessage(&msg);
    }
}

然而,正如 Hans Passant 在评论中所说:

当您还创建了一个窗口时,永远不要使用 PostThreadMessage(),像调整窗口大小这样简单的事情会导致消息丢失

Raymond Chen 发表了几篇关于这个主题的博客文章:

线程消息被模态循环吃掉

看着线程消息消失

通过消息过滤器从模态循环中拯救线程消息

为什么 PostThreadMessage 发布的消息会消失?

您可以尝试使用WH_MSGFILTER钩子,就像 Raymond 演示的那样。但解决此问题的最佳方法是根本不发布线程消息,而是发布窗口消息。让您的键盘挂钩使用PostMessage()(甚至SendMessage())到HWND您的应用程序拥有的一个,然后您可以在该窗口的消息过程中处理该消息。


推荐阅读