线程无法在应用程序退出时退出 - C ++

时间:2010-12-01 23:01:59

标签: c++ multithreading visual-studio-2008 messaging exit

我的应用程序创建了一个轮询Windows消息的线程。当需要关闭时,我的应用程序会发送WM_QUIT消息。

在应用程序线程中,这就是我试图关闭的方式:

if ( _hNotifyWindowThread != NULL )
{
    ASSERT(_pobjNotifyWindow != NULL);

    ::SendMessage( _pobjNotifyWindow->m_hWnd, WM_QUIT, 0, 0 );
    ::WaitForSingleObject( _hNotifyWindowThread, 50000L );
    ::CloseHandle( _hNotifyWindowThread ); // <-- PC never gets here.
    _hNotifyWindowThread = NULL;
}

这是在我的线程函数中运行的消息泵:

// Start the message pump...
while ( (bRetVal = ::GetMessage(
    &msg,                           // message structure
    _pobjNotifyWindow->m_hWnd,      // handle to window whose messages are to be retrieved
    WM_DEVICECHANGE,                // lowest message value to retrieve
    WM_DEVICECHANGE                 // highest message value to retrieve
    )) != 0 )
{
    switch ( bRetVal )
    {
    case -1:                        // Error generated in GetMessage.
        TRACE(_T("NotifyWindowThreadFn : Failed to get notify window message.\r\n\tError: %d\r\n\tFile: %s\r\n\tLine: %d\r\n"), ::GetLastError(), __WFILE__, __LINE__);
        return ::GetLastError();
        break;

    default:                        // Other message received.
        ::TranslateMessage( &msg );
        ::DispatchMessage( &msg );
        break;
    }
}

delete _pobjNotifyWindow;           // Delete the notify window.

return msg.wParam;                  // Return exit code.

Microsoft documentation for GetMessage州:

  

如果函数检索WM_QUIT消息,则返回值为零。

     

请注意,无论您为wMsgFilterMin和wMsgFilterMax指定了哪个值,GetMessage始终会检索WM_QUIT消息。

如果是这种情况,那么我希望调用GetMessage来检索WM_QUIT消息以返回0.但是,调试让我相信消息未被正确接收。奇怪的是,我可以在WndProc函数中放置一个断点,它似乎得到WM_QUIT消息。

我做错了什么?我应该使用不同的函数在线程之间发布消息吗?感谢。

7 个答案:

答案 0 :(得分:2)

这是完整的答案(我几乎可以肯定):

替换

::SendMessage( _pobjNotifyWindow->m_hWnd, WM_QUIT, 0, 0 ); 

::PostMessage( _pobjNotifyWindow->m_hWnd, WM_CLOSE, 0, 0 ); 

替换

 ( (bRetVal = ::GetMessage( &msg, _pobjNotifyWindow->m_hWnd, WM_DEVICECHANGE, WM_DEVICECHANGE )) != 0 ) 

( (bRetVal = ::GetMessage( &msg, NULL ,0 ,0 )) != 0 )

在WindowsProcedure中:

 case WM_CLOSE : DestroyWindow( hWnd ); break; //can be return 

 case WM_DESTROY : PostQuitMessage( 0 ); 

答案 1 :(得分:1)

虽然我对WinAPI的了解有限制,但WM_QUIT似乎很特别,并不像其他消息那样张贴。

根据Raymond Chen

  

与WM_PAINT,WM_MOUSEMOVE和WM_TIMER消息类似,WM_QUIT消息不是“真实”发布的消息。相反,它是系统生成的消息之一,就像它被发布一样,即使它不是。

  

当一个线程调用PostQuitMessage时,会设置一个队列状态的标志,表示“如果有人要求提供消息并且没有发布消息,则制作WM_QUIT消息”。这就像其他“虚拟发布”消息一样。

  

PostThreadMessage只是将消息放入线程队列中(对于真实的,而不是虚拟的),因此它不会获得真正的PostQuitMessage触发的任何特殊处理。

所以你应该使用PostQuitMessage。

当然,可能有办法解决当前的奇怪行为(根据其他答案)。但鉴于WM_QUIT的描述很特殊,你可能还是想使用PostQuitMessage。

答案 2 :(得分:0)

只是一个猜测。您的退出代码是从另一个窗口的消息循环调用中调用的,该窗口有自己的消息泵吗?根据MSDN for WaitForSingleObject,您可以无限期地阻止当前的UI线程,并阻止处理自己的消息。

http://msdn.microsoft.com/en-us/library/ms687032%28VS.85%29.aspx

  

呼叫等待时要小心   函数和代码直接或   间接创建窗口。如果一个   线程创建任何窗口,它必须   处理消息。消息广播   被发送到系统中的所有窗口。   使用等待函数的线程   没有超时间隔可能会导致   系统陷入僵局。二   间接代码的例子   创建窗口是DDE和   CoInitialize功能。因此,如果   你有一个创建的线程   windows,使用MsgWaitForMultipleObjects   或者是MsgWaitForMultipleObjectsEx   而不是WaitForSingleObject。

可能是WM_Quit消息被广播到您自己的窗口,由于您的WaitForSingleObject调用,它不会处理任何消息。请尝试使用MsgWaitForMultipleOjbects,它会不时尝试调用您的消息循环。

此致,   Alois Kraus

答案 3 :(得分:0)

WM_QUIT不是窗口消息,因此您不应将其发送到窗口。请尝试使用PostThreadMessage代替:

PostThreadMessage(GetThreadId(_hNotifyWindowThread), WM_QUIT, 0, 0);

如果这不起作用,请尝试在窗口中发布虚拟消息:

::PostMessage( _pobjNotifyWindow->m_hWnd, WM_APP, 0, 0 );

并将其用作窗口过程中退出的信号:

case WM_APP:
  PostQuitMessage(0);

答案 4 :(得分:0)

您的GetMessage(...)仅检索您提供的窗口的消息。但是,WM_QUIT没有与之关联的窗口。您需要在没有窗口句柄(即NULL)的情况下调用GetMessage,它会检索消息队列中的任何消息。

答案 5 :(得分:0)

详细说明TheUndeadFish所说的内容;调用PostQuitMessage时,在线程的消息队列的唤醒标志中设置了一个“秘密”未记录的QS_QUIT标志。 GetMessage以特定顺序查看其唤醒标志,以确定接下来要处理的消息。

如果发现QS_QUIT已设置,则会生成WM_QUIT消息并导致GetMessage返回FALSE。

您可以获取当前使用GetQueueStatus设置的线程的记录的唤醒标记。

详细信息可以在Programming Applications for Microsoft Windows,第4版中找到(不幸的是,最新版本删除了这些主题)。

答案 6 :(得分:0)

此代码存在2个问题。

  1. ::GetMessage()不会停止,因为您使用的hWnd参数不是NULL。您需要获取主题消息才能让::GetMessage()返回0
  2. 遵循(1)中的逻辑,您需要使用::PostThreadMessage()发布消息,将其放入线程的消息队列中。
  3. ::PostQuitMessage(status)

    的简写,这一点很好地说明了这一切
    ::PostThreadMessage(::GetCurrentThreadId(), WM_QUIT, status, 0);
    

    修改

    似乎人们已经开始认为::PostThreadMessage(...,WM_QUIT,...);不起作用,因为它没有得到设置由QS_QUIT设置的::PostQuitMessage()标志的特殊处理。如果是这种情况,则无法将WM_QUIT发送到另一个线程的消息队列。这证明它无论如何都有效。

    特别要注意常量Use_PostQuitMessageGetMessage_UseWindowHandle。随意更改值并使用代码。它的工作原理与我的回答一样,除了我在尝试之前错误地使用了::GetCurrentThread()而不是::GetCurrentThreadId()

    #include <Windows.h>
    #include <iomanip>
    #include <iostream>
    
    namespace {
    
            // Doesn't matter if this is 'true' or 'false'.
        const bool Use_PostQuitMessage        = false;
    
            // Setting this to 'true' prevents the application from closing.
        const bool GetMessage_UseWindowHandle = false;
    
        void post_quit_message ()
        {
            if ( Use_PostQuitMessage ) {
                ::PostQuitMessage(0);
            }
            else {
                ::PostThreadMessageW(::GetCurrentThreadId(), WM_QUIT, 0, 0);
            }
        }
    
        ::BOOL get_message ( ::HWND window, ::MSG& message )
        {
            if ( GetMessage_UseWindowHandle ) {
                return (::GetMessageW(&message, window, 0, 0));
            }
            else {
                return (::GetMessageW(&message, 0, 0, 0));
            }
        }
    
        ::ULONG __stdcall background ( void * )
        {
                // Allocate window in background thread that is to be interrupted.
            ::HWND window = ::CreateWindowW(L"STATIC", 0, WS_OVERLAPPEDWINDOW,
                0, 0, 512, 256, 0, 0, ::GetModuleHandleW(0), 0);
            if ( window == 0 ) {
                std::cerr << "Could not create window." << std::endl;
                return (EXIT_FAILURE);
            }
    
                // Process messages for this thread's windows.
            ::ShowWindow(window, SW_NORMAL);
            ::MSG message;
            ::BOOL result = FALSE;
            while ((result = get_message(window,message)) > 0)
            {
                    // Handle 'CloseWindow()'.
                if ( message.message == WM_CLOSE )
                {
                    post_quit_message(); continue;
                }
                    // Handling for 'ALT+F4'.
                if ((message.message == WM_SYSCOMMAND) &&
                    (message.wParam == SC_CLOSE))
                {
                    post_quit_message(); continue;
                }
                    // Dispatch message to window procedure.
                ::TranslateMessage(&message);
                ::DispatchMessageW(&message);
            }
                // Check for error in 'GetMessage()'.
            if ( result == -1 )
            {
                std::cout << "GetMessage() failed with error: "
                    << ::GetLastError() << "." << std::endl;
                return (EXIT_FAILURE);
            }
            return (EXIT_SUCCESS);
        }
    
    }
    
    int main ( int, char ** )
    {
            // Launch window & message pump in background thread.
        ::DWORD id = 0;
        ::HANDLE thread = ::CreateThread(0, 0, &::background, 0, 0, &id);
        if ( thread == INVALID_HANDLE_VALUE ) {
            std::cerr << "Could not launch thread." << std::endl;
            return (EXIT_FAILURE);
        }
    
            // "do something"...
        ::Sleep(1000);
    
            // Decide to close application.
        ::PostThreadMessageW(id, WM_QUIT, 0, 0);
    
            // Wait for everything to shut down.
        ::WaitForSingleObject(thread, INFINITE);
    
            // Return background thread's success code.
        ::DWORD status = EXIT_FAILURE;
        ::GetExitCodeThread(thread,&status);
        return (status);
    }
    

    <强> P.S

    实际测试main中::PostThreadMessage(::GetCurrentThreadId(),...);调用::background(0);的单线程使用,而不是启动线程。