我的应用程序创建了一个轮询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
消息。
我做错了什么?我应该使用不同的函数在线程之间发布消息吗?感谢。
答案 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个问题。
::GetMessage()
不会停止,因为您使用的hWnd
参数不是NULL
。您需要获取主题消息才能让::GetMessage()
返回0
。::PostThreadMessage()
发布消息,将其放入线程的消息队列中。 ::PostQuitMessage(status)
是
::PostThreadMessage(::GetCurrentThreadId(), WM_QUIT, status, 0);
修改强>:
似乎人们已经开始认为::PostThreadMessage(...,WM_QUIT,...);
不起作用,因为它没有得到设置由QS_QUIT
设置的::PostQuitMessage()
标志的特殊处理。如果是这种情况,则无法将WM_QUIT
发送到另一个线程的消息队列。这证明它无论如何都有效。
特别要注意常量Use_PostQuitMessage
和GetMessage_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);
的单线程使用,而不是启动线程。