0

我有一个多线程应用程序,在某些线程上,我正在使用 ATL 的CWindowImpl<>. 我有一个用作线程过程的静态方法。我需要在线程上创建一个窗口,因为我需要与线程的一些通信是同步的,并且PostThreadMessage()明确是异步的。当我的窗口接收到WM_DESTROY消息(由MESSAGE_HANDLER宏定义的处理程序)时,它会调用PostQuitMessage(),如下方法所示:

LRESULT MyATLWindowClass::OnDestroy(UINT uMsg,
                                    WPARAM wParam,
                                    LPARAM lParam,
                                    BOOL& bHandled) {
  ::PostQuitMessage(0);
  return 0;
}

我正在向线程使用自定义消息,用于向线程PostThreadMessage()指示是时候自行终止了。处理该自定义消息时,我调用了该CWindowImpl::DestroyWindow()方法,该方法似乎正确地破坏了窗口,因为我的OnDestroy消息处理程序被调用。但是,拥有线程似乎从未收到WM_QUIT要处理的消息。下面是我的线程程序的简化版本。

unsigned int WINAPI MyATLWindowClass::ThreadProc(LPVOID lpParameter) {
  // Initialize COM on the thread
  ::CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);

  // Create the window using ATL
  MyATLWindowClass new_window;
  HWND session_window_handle = new_window.Create(
      /* HWND hWndParent */ HWND_MESSAGE,
      /* _U_RECT rect */ CWindow::rcDefault,
      /* LPCTSTR szWindowName */ NULL,
      /* DWORD dwStyle */ NULL,
      /* DWORD dwExStyle */ NULL,
      /* _U_MENUorID MenuOrID */ 0U,
      /* LPVOID lpCreateParam */ NULL);

  // Initialize the message pump on the thread.
  MSG msg;
  ::PeekMessage(&msg, NULL, WM_USER, WM_USER, PM_NOREMOVE);

  // Run the message loop
  BOOL get_message_return_value;
  while ((get_message_return_value = ::GetMessage(&msg, NULL, 0, 0)) != 0) {
    if (get_message_return_value == -1) {
      // GetMessage handling logic taken from MSDN documentation
      break;
    } else {
      if (msg.message == WD_SIGNAL_THREAD_SHUTDOWN) {
        // Requested thread shutdown, so destroy the window
        new_window.DestroyWindow();
      } else if (msg.message == WM_QUIT) {
        // Process the quit message and exit the message loop
        // to terminate the thread
        break;
      } else {
        ::TranslateMessage(&msg);
        ::DispatchMessage(&msg);
      }
    }
  }

  // Uninitialize COM on the thread before exiting
  ::CoUninitialize();
  return 0;
}

请注意,我是否打电话或向窗口DestroyWindow()发送消息似乎并不重要。WM_CLOSE在这两种情况下,线程的消息泵都没有接收到 WM_QUIT。拥有线程的消息泵应该接收这样的消息吗?我对线程的消息泵和窗口的消息泵如何交互的误解在哪里?或者关于 ATL 的窗口类如何创建和管理窗口,我缺少什么?

4

2 回答 2

1

GetMessage() 从不返回 WM_QUIT。该消息强制它返回 0,旨在终止您的消息循环。

当心使用 PostThreadMessage() 的相当大的危险。它永远不应该在也显示窗口的线程上使用,就像你正在使用的那样。问题是它不需要 HWND 参数。所以只有你的消息循环可以看到消息,它不会被发送到任何带有 DispatchMessage() 的窗口。当输入模式消息循环时,这会出错,这种循环不在您的控制范围内。就像使 MessageBox 工作的模态循环一样。或者 Windows 用来允许用户调整窗口大小的那个。或者 DialogBox() 使用的那个。等等。始终使用 PostMessage(),使用您自己的消息编号。

于 2013-05-27T18:30:57.720 回答
0

一些迟到的额外想法。一旦您发现,您可能可以安全地终止您的消息循环WD_SIGNAL_THREAD_SHUTDOWN

  if (msg.message == WD_SIGNAL_THREAD_SHUTDOWN) {
    // Requested thread shutdown, so destroy the window
    new_window.DestroyWindow();
    break; // exit the message loop
  }

DestroyWindow是同步调用,窗口在返回前会被完全销毁,可以退出循环。因此,发布 WM_QUIT 将是多余的。

此外,如果窗口不可见并且其唯一目的是处理消息,则可以使用仅消息窗口。

于 2013-09-29T10:53:21.273 回答