未收到 WH_KEYBOARD_LL 处理程序 PostThreadMessage 消息的 SetWindowsHookEx?

SetWindowsHookEx for WH_KEYBOARD_LL handler PostThreadMessage message not received?

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

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

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 window 的消息循环看起来像:

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

我暂时将其更改为使用可变变量来关闭 window,但我真的很想向它发送消息。如果没有锁,使用全局 HWND 将不是线程安全的,所以不想那样做。

P.S。我确实检查了 PostThreadMessage 结果,它不是假的(当我添加它们时没有调试消息)所以它似乎 post 它在某处。

PostThreadMessage() post向队列发送 线程 消息,而不是 window 消息.您的消息循环忽略 thread 消息,仅发送 window 消息。 window 永远不会看到 thread 消息,因此您需要直接在消息循环中处理此类消息,例如:

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 在评论中所说:

Never use PostThreadMessage() when you also created a window, something as simple as resizing the window causes the message to get lost.

Raymond Chen post编辑了几篇关于这个主题的博客文章:

Thread messages are eaten by modal loops

Watching thread messages disappear

Rescuing thread messages from modal loops via message filters

Why do messages posted by PostThreadMessage disappear?

您可以尝试使用 WH_MSGFILTER 挂钩,就像 Raymond 演示的那样。但解决这个问题的最好方法是根本不 post 一个 thread 消息, post 一个 window 消息代替。让你的键盘挂钩使用 PostMessage()(甚至 SendMessage())到你的应用程序拥有的 HWND,然后你可以在那个 window 的消息过程中处理消息。