应用程序退出时线程无法退出 - C++
我的应用程序创建一个轮询 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.
GetMessage
的 Microsoft 文档指出:
如果函数检索到 WM_QUIT 消息,则返回值为零。
请注意,无论您为 wMsgFilterMin 和 wMsgFilterMax 指定哪个值,GetMessage 始终检索 WM_QUIT 消息。
如果是这种情况,那么我希望调用 GetMessage
来检索 WM_QUIT
消息返回 0。但是,调试让我相信没有收到该消息适当地。奇怪的是,我可以在 WndProc 函数中放置一个断点,并且它似乎收到了 WM_QUIT 消息。
我做错了什么?我应该使用不同的函数在线程之间发布消息吗?谢谢。
My application creates a thread that polls for Windows messages. When it is time to close down, my application sends the WM_QUIT
message.
In the application thread, this is how I am attempting to shut things down:
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;
}
This is the message pump running in my thread function:
// 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.
The Microsoft documentation for GetMessage
states:
If the function retrieves the WM_QUIT message, the return value is zero.
Note that GetMessage always retrieves WM_QUIT messages, no matter which values you specify for wMsgFilterMin and wMsgFilterMax.
If this is the case, then I would expect a call to GetMessage
that retrieves the WM_QUIT
message to return 0. However, debugging leaves me to believe that the message is not received properly. What is odd is that I can place a breakpoint in the WndProc
function, and it seems to get the WM_QUIT
message.
What am I doing wrong? Should I be using a different function for posting messages between threads? Thanks.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(7)
这是完整的答案(我几乎可以肯定):
替换
为
替换
为
( (bRetVal = ::GetMessage( &msg, NULL ,0 ,0 )) != 0 )
在您的 WindowsProcedure 中:
This the complete answer (I'm almost sure):
replace
with
replace
with
( (bRetVal = ::GetMessage( &msg, NULL ,0 ,0 )) != 0 )
In your WindowsProcedure :
虽然我对 WinAPI 的了解有限,但 WM_QUIT 似乎很特殊,并不意味着像其他消息一样发布。
根据Raymond Chen:
。
。
所以您可能应该使用 PostQuitMessage。
当然,可能有一些方法可以解决当前的奇怪行为(根据其他答案)。但考虑到 WM_QUIT 的描述很特殊,您可能还是想使用 PostQuitMessage。
While my knowledge of the WinAPI has limits, it seems WM_QUIT is special and not meant to be posted like other messages.
According to Raymond Chen:
.
.
So you should probably be using PostQuitMessage.
Of course, there may be ways to work around the current odd behavior (as per other answers). But given the description of WM_QUIT being special, you may want to use PostQuitMessage anyway.
这段代码有两个问题。
::GetMessage()
不会停止,因为您将hWnd
参数与NULL
以外的其他参数一起使用。您需要获取线程消息以使::GetMessage()
返回0
。::PostThreadMessage()
发布消息,将其放入线程的消息队列中。的简写,这一事实很好地说明了所有这一切
::PostQuitMessage(status)
是EDIT:似乎人们已经被引导认为
::PostThreadMessage(...,WM_QUIT,...);
不起作用,因为它没有获得设置由QS_QUIT
标志设置的特殊处理>::PostQuitMessage()。如果是这种情况,则无法将WM_QUIT
发送到另一个线程的消息队列。这是它无论如何都有效的证据。特别要注意常量
Use_PostQuitMessage
和GetMessage_UseWindowHandle
。请随意更改值并使用代码。它的工作原理就像我的答案中所宣传的那样,只是我在尝试之前错误地使用了::GetCurrentThread()
而不是::GetCurrentThreadId()
。PS:
实际测试
::PostThreadMessage(::GetCurrentThreadId(),...);
的单线程使用调用::background(0) ;
在 main 中而不是启动线程。There are 2 problems with this code.
::GetMessage()
doesn't stop because you're using thehWnd
parameter with something else thanNULL
. You need to fetch the thread messages to get::GetMessage()
to return0
.::PostThreadMessage()
to put it in the thread's message queue.All of this is rather well illustrated by the fact the
::PostQuitMessage(status)
is a shorthand forEDIT:
It seems that people have been led into thinking that
::PostThreadMessage(...,WM_QUIT,...);
doesn't work because it doesn't get the special treatement of setting theQS_QUIT
flag that is set by::PostQuitMessage()
. If that was the case, then there would be no way to sendWM_QUIT
to another thread's message queue. Here is proof that it works anyways.In particular, pay attention to the constants
Use_PostQuitMessage
andGetMessage_UseWindowHandle
. Feel free to change the values and play around with the code. It works just as advertised in my answer, except that I mistakenly used::GetCurrentThread()
rather than::GetCurrentThreadId()
before trying it out.P.S.:
To actually test the single-threaded use of
::PostThreadMessage(::GetCurrentThreadId(),...);
invoke::background(0);
in main instead of launching the thread.只是一个猜测。您的退出代码是从另一个具有自己的消息泵的窗口的消息循环调用中调用的吗?根据 MSDN for WaitForSingleObject,您会无限期地阻止当前 UI 线程并阻止处理其自己的消息。
从
http://msdn.microsoft.com/en-us /library/ms687032%28VS.85%29.aspx
可能是 WM_Quit 消息被广播到您自己的窗口,由于您的 WaitForSingleObject 调用,该窗口不处理任何消息。请尝试使用 MsgWaitForMultipleOjbects,它会不时尝试调用您的消息循环。
你的,
阿洛伊斯·克劳斯
Just a guess. Your quit code is called from within a message loop call from another window which has its own message pump? According to MSDN for WaitForSingleObject you block the current UI thread indefinitely and prevent processing its own messages.
From
http://msdn.microsoft.com/en-us/library/ms687032%28VS.85%29.aspx
It could be that the WM_Quit message is broadcast to your own window which does not process any messages due to your WaitForSingleObject call. Try instead MsgWaitForMultipleOjbects which tries to call your message loop from time to time.
Yours,
Alois Kraus
WM_QUIT
不是窗口消息,因此您不应将其发送到窗口。尝试使用PostThreadMessage
< /a> 相反:如果这不起作用,请尝试向您的窗口发布一条虚拟消息:
并将其用作窗口过程中退出的信号:
WM_QUIT
is not a window message, so you shouldn't be sending it to a window. Try usingPostThreadMessage
instead:If that doesn't work, try posting a dummy message to your window:
and use it as a signal to quit in your window procedure:
您的 GetMessage(...) 仅检索您提供的窗口的消息。然而,WM_QUIT 没有与其关联的窗口。您需要在没有窗口句柄(即 NULL)的情况下调用 GetMessage,以检索消息队列中的任何消息。
Your GetMessage(...) retrieves only message of the window you supplied. However WM_QUIT does not have a window associated with it. You Need to call GetMessage without a window handle (i.e. NULL) which retrieve any message in the Message-Queue.
详细解释 TheUndeadFish 所说的话;当调用 PostQuitMessage 时,线程消息队列的唤醒标志中设置了一个“秘密”未记录的
QS_QUIT
标志。 GetMessage 以特定顺序查看其唤醒标志,以确定接下来要处理的消息。如果它发现设置了
QS_QUIT
,则会生成WM_QUIT
消息并导致GetMessage
返回FALSE。您可以获取当前使用 获取队列状态。
有关详细信息,请参阅 Microsoft Windows 编程应用程序,第 4 版(遗憾的是,最新版本删除了这些主题)。
To elaborate on what TheUndeadFish said; there's a "secret" undocumented
QS_QUIT
flag set in the wake flags of the thread's message queue when PostQuitMessage is called. GetMessage looks at its wake flags in a particular order to determine what message to process next.If it finds that
QS_QUIT
is set, it generates aWM_QUIT
message and causesGetMessage
to return FALSE.You can get a thread's documented wake flags that are currently set with GetQueueStatus.
The details can be found in Programming Applications for Microsoft Windows, 4th Edition (unfortunately, the newest edition removed these topics).