使用ATL正确关闭在单独线程上创建的窗口

时间:2013-05-27 15:41:37

标签: c++ multithreading winapi com atl

我有一个多线程应用程序,在某些线程上,我正在使用ATL的CWindowImpl<>创建窗口。我有一个静态方法,我用作线程程序。我需要在线程上创建一个窗口,因为我需要与线程be synchronous进行一些通信,而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的窗口类如何创建和管理窗口缺少什么?

2 个答案:

答案 0 :(得分:1)

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

请注意使用PostThreadMessage()的相当大的危险。它应该永远在一个也显示窗口的线程上使用,就像你正在使用的那样。问题是它没有采用HWND参数。因此,只有您的消息循环可以看到消息,它不会被传递到DispatchMessage()的任何窗口。输入模态消息循环时出现这种情况,这种循环不在您的控件之内。就像使MessageBox工作的模态循环一样。或者Windows用来允许用户调整窗口大小的那个。或者DialogBox()使用的那个。等等。始终使用PostMessage(),使用您自己的消息号。

答案 1 :(得分: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将是多余的。

此外,您可以使用message-only窗口,如果窗口不可见,其唯一目的是处理邮件。