WM_QUIT 仅针对线程而不是 window?
WM_QUIT only posts for thread and not the window?
在 Windows API 中,我正在研究 GetMessage
函数的实际工作原理。我已经看到了 Windows 消息循环的 3 个实现,并且想探索它们。
1)
截至撰写本文时post,this MSDN article 描述了我认为正确 实现消息循环的方法。
MSG msg;
BOOL bRet;
while( (bRet = GetMessage( &msg, NULL, 0, 0 )) != 0)
{
if (bRet == -1)
{
// handle the error and possibly exit
}
else
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
2)
在 GetMessage
function page 上,我看到了这个实现:
MSG msg;
BOOL bRet;
while( (bRet = GetMessage( &msg, hWnd, 0, 0 )) != 0)
{
if (bRet == -1)
{
// handle the error and possibly exit
}
else
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
3)
最后,Visual Studio documentation 将此实现作为其 Win32 应用程序演示的一部分。
MSG msg;
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
讨论
简而言之,实施 #3 忽略了 GetMessage
中的错误 return,但在其他方面与第一个实施相同。也就是说,它们都处理当前线程的所有消息。当 GetMessage
函数 returns 0
时,循环终止。
因为我在#1 之前找到了实现#2,所以我认为它已经完成了。但是,我注意到当 WM_QUIT
消息通过 PostQuitMessage
编辑时 GetMessage
不会 return 0
这导致了一些混乱,直到我找到实施 #1 并对其进行了测试。前两个实现之间的区别是 GetMessage
的第二个参数。在 #2 中,它指定 hWnd
,根据 GetMessage
文档,它是:
A handle to the window whose messages are to be retrieved. The window must belong to the current thread.
在#1 中,它是 NULL
,它属于这个摘录:
If hWnd is NULL, GetMessage retrieves messages for any window that belongs to the current thread, and any messages on the current thread's message queue whose hwnd value is NULL (see the MSG structure). Therefore if hWnd is NULL, both window messages and thread messages are processed.
当使用NULL
进行测试时,GetMessage
函数returns 0
当处理WM_QUIT
消息时,成功终止循环。
问题
即使 PostQuitMessage
是从特定 window 的回调函数调用的,WM_QUIT
实际上属于 window 还是当前线?根据对这三个实现的测试,它似乎与当前线程相关联。
如果与线程关联,什么时候使用有效的hWnd
作为GetMessage
的参数有用或合适?这样的消息循环将无法 return 0
作为对 WM_QUIT
的反应,那么是否有另一种方法可以终止消息循环?
参考资料
GetMessage
PostQuitMessage
- Message Loop #1 (Wikipedia Link)
- Message Loop #2
- Message Loop #3
WM_QUIT
Message
- Related Question
代码
#include <Windows.h>
#include <tchar.h>
#include <strsafe.h>
LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) {
switch(msg)
{
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, msg, wParam, lParam);
}
return 0;
}
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR cmdLine, int nCmdShow) {
LPCTSTR wndClassName =_T("Class_SHTEST");
LPCTSTR wndName = _T("SHTest");
WNDCLASSEX wcex;
wcex.cbSize = sizeof(WNDCLASSEX);
wcex.style = CS_HREDRAW|CS_VREDRAW;
wcex.lpfnWndProc = WndProc;
wcex.cbClsExtra = 0;
wcex.cbWndExtra = 0;
wcex.hInstance = hInstance;
wcex.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_APPLICATION));
wcex.hCursor = LoadCursor(NULL, MAKEINTRESOURCE(IDC_ARROW));
wcex.hbrBackground = (HBRUSH) COLOR_WINDOW+1;
wcex.lpszMenuName = NULL;
wcex.lpszClassName = wndClassName;
wcex.hIconSm = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_APPLICATION));
if (!RegisterClassEx(&wcex))
{
MessageBox(NULL, _T("Call to RegisterClassEx failed!"), wndName, MB_OK|MB_ICONERROR);
}
HWND window = CreateWindow(wndClassName, wndName,
WS_OVERLAPPEDWINDOW | WS_MAXIMIZE,
0, 0, CW_USEDEFAULT, CW_USEDEFAULT,
NULL, NULL, hInstance, NULL);
if (!window) {
MessageBox(NULL, _T("Call to CreateWindow failed!"), wndName, MB_OK|MB_ICONERROR);
}
ShowWindow(window, SW_SHOW);
UpdateWindow(window);
//Message loop (using implementation #1)
MSG msg;
BOOL bRet;
while((bRet = GetMessage(&msg, NULL, 0, 0)) != 0) {
if (bRet == -1) {
//Handle error and possibly exit.
}
else {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
//Return the exit code in the WM_QUIT message.
return (int) msg.wParam;
}
WM_QUIT
与线程相关,与个人无关 window。请注意 PostQuitMessage()
缺少 hwnd
参数。它不可能是 window 特定的,因为无法告诉它为哪个 window 生成消息。
WM_QUIT
实际上不是真正的消息。当您调用 PostQuitMessage()
时,消息队列状态中会设置一个内部标志,表明已请求 WM_QUIT
。这将由 GetMessage()
或 PeekMessage()
在将来的某个时候自动生成(通常是立即生成,但如果队列包含其他已发布的消息,这些消息将首先处理)。
这在 Raymond Chen's blog 上有更详细的解释,其中还包含以下引用:
As another special behavior, the generated WM_QUIT message bypasses
the message filters passed to the GetMessage and PeekMessage
functions. If the internal "quit message pending" flag is set, then
you will get a WM_QUIT message once the queue goes quiet, regardless
of what filter you pass.
这表明您的观察是错误的,并且即使提供了过滤器参数,在调用 PostQuitMessage()
之后上面示例 #2 中的 GetMessage()
也应该 return 0。
一般来说,只有在您有特定需要时才应使用邮件过滤器(例如,您特别想检索发布到特定 window 的邮件)。在大多数情况下,这些参数都应设置为 0,以便 UI.
正常运行
根据 WM_QUIT
的 MSDN documention:
The WM_QUIT message is not associated with a window and therefore will
never be received through a window's window procedure. It is retrieved
only by the GetMessage or PeekMessage functions.
由于 WM_QUIT
未与 window 关联,并且将 HWND
传递给 GetMessage()
只会检索与 window 关联的那些消息,因此后者将永远不会收到 WM_QUIT
设计。
至于您何时希望将 HWND
传递给 GetMessage()
,在应用程序的一般消息循环中您不会。但有时您想要在 UI 中发生某些事情时发送消息,并且只关心与特定 window.
关联的消息
在 Windows API 中,我正在研究 GetMessage
函数的实际工作原理。我已经看到了 Windows 消息循环的 3 个实现,并且想探索它们。
1)
截至撰写本文时post,this MSDN article 描述了我认为正确 实现消息循环的方法。
MSG msg;
BOOL bRet;
while( (bRet = GetMessage( &msg, NULL, 0, 0 )) != 0)
{
if (bRet == -1)
{
// handle the error and possibly exit
}
else
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
2)
在 GetMessage
function page 上,我看到了这个实现:
MSG msg;
BOOL bRet;
while( (bRet = GetMessage( &msg, hWnd, 0, 0 )) != 0)
{
if (bRet == -1)
{
// handle the error and possibly exit
}
else
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
3)
最后,Visual Studio documentation 将此实现作为其 Win32 应用程序演示的一部分。
MSG msg;
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
讨论
简而言之,实施 #3 忽略了 GetMessage
中的错误 return,但在其他方面与第一个实施相同。也就是说,它们都处理当前线程的所有消息。当 GetMessage
函数 returns 0
时,循环终止。
因为我在#1 之前找到了实现#2,所以我认为它已经完成了。但是,我注意到当 WM_QUIT
消息通过 PostQuitMessage
GetMessage
不会 return 0
这导致了一些混乱,直到我找到实施 #1 并对其进行了测试。前两个实现之间的区别是 GetMessage
的第二个参数。在 #2 中,它指定 hWnd
,根据 GetMessage
文档,它是:
A handle to the window whose messages are to be retrieved. The window must belong to the current thread.
在#1 中,它是 NULL
,它属于这个摘录:
If hWnd is NULL, GetMessage retrieves messages for any window that belongs to the current thread, and any messages on the current thread's message queue whose hwnd value is NULL (see the MSG structure). Therefore if hWnd is NULL, both window messages and thread messages are processed.
当使用NULL
进行测试时,GetMessage
函数returns 0
当处理WM_QUIT
消息时,成功终止循环。
问题
即使
PostQuitMessage
是从特定 window 的回调函数调用的,WM_QUIT
实际上属于 window 还是当前线?根据对这三个实现的测试,它似乎与当前线程相关联。如果与线程关联,什么时候使用有效的
hWnd
作为GetMessage
的参数有用或合适?这样的消息循环将无法 return0
作为对WM_QUIT
的反应,那么是否有另一种方法可以终止消息循环?
参考资料
GetMessage
PostQuitMessage
- Message Loop #1 (Wikipedia Link)
- Message Loop #2
- Message Loop #3
WM_QUIT
Message- Related Question
代码
#include <Windows.h>
#include <tchar.h>
#include <strsafe.h>
LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) {
switch(msg)
{
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, msg, wParam, lParam);
}
return 0;
}
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR cmdLine, int nCmdShow) {
LPCTSTR wndClassName =_T("Class_SHTEST");
LPCTSTR wndName = _T("SHTest");
WNDCLASSEX wcex;
wcex.cbSize = sizeof(WNDCLASSEX);
wcex.style = CS_HREDRAW|CS_VREDRAW;
wcex.lpfnWndProc = WndProc;
wcex.cbClsExtra = 0;
wcex.cbWndExtra = 0;
wcex.hInstance = hInstance;
wcex.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_APPLICATION));
wcex.hCursor = LoadCursor(NULL, MAKEINTRESOURCE(IDC_ARROW));
wcex.hbrBackground = (HBRUSH) COLOR_WINDOW+1;
wcex.lpszMenuName = NULL;
wcex.lpszClassName = wndClassName;
wcex.hIconSm = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_APPLICATION));
if (!RegisterClassEx(&wcex))
{
MessageBox(NULL, _T("Call to RegisterClassEx failed!"), wndName, MB_OK|MB_ICONERROR);
}
HWND window = CreateWindow(wndClassName, wndName,
WS_OVERLAPPEDWINDOW | WS_MAXIMIZE,
0, 0, CW_USEDEFAULT, CW_USEDEFAULT,
NULL, NULL, hInstance, NULL);
if (!window) {
MessageBox(NULL, _T("Call to CreateWindow failed!"), wndName, MB_OK|MB_ICONERROR);
}
ShowWindow(window, SW_SHOW);
UpdateWindow(window);
//Message loop (using implementation #1)
MSG msg;
BOOL bRet;
while((bRet = GetMessage(&msg, NULL, 0, 0)) != 0) {
if (bRet == -1) {
//Handle error and possibly exit.
}
else {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
//Return the exit code in the WM_QUIT message.
return (int) msg.wParam;
}
WM_QUIT
与线程相关,与个人无关 window。请注意 PostQuitMessage()
缺少 hwnd
参数。它不可能是 window 特定的,因为无法告诉它为哪个 window 生成消息。
WM_QUIT
实际上不是真正的消息。当您调用 PostQuitMessage()
时,消息队列状态中会设置一个内部标志,表明已请求 WM_QUIT
。这将由 GetMessage()
或 PeekMessage()
在将来的某个时候自动生成(通常是立即生成,但如果队列包含其他已发布的消息,这些消息将首先处理)。
这在 Raymond Chen's blog 上有更详细的解释,其中还包含以下引用:
As another special behavior, the generated WM_QUIT message bypasses the message filters passed to the GetMessage and PeekMessage functions. If the internal "quit message pending" flag is set, then you will get a WM_QUIT message once the queue goes quiet, regardless of what filter you pass.
这表明您的观察是错误的,并且即使提供了过滤器参数,在调用 PostQuitMessage()
之后上面示例 #2 中的 GetMessage()
也应该 return 0。
一般来说,只有在您有特定需要时才应使用邮件过滤器(例如,您特别想检索发布到特定 window 的邮件)。在大多数情况下,这些参数都应设置为 0,以便 UI.
正常运行根据 WM_QUIT
的 MSDN documention:
The WM_QUIT message is not associated with a window and therefore will never be received through a window's window procedure. It is retrieved only by the GetMessage or PeekMessage functions.
由于 WM_QUIT
未与 window 关联,并且将 HWND
传递给 GetMessage()
只会检索与 window 关联的那些消息,因此后者将永远不会收到 WM_QUIT
设计。
至于您何时希望将 HWND
传递给 GetMessage()
,在应用程序的一般消息循环中您不会。但有时您想要在 UI 中发生某些事情时发送消息,并且只关心与特定 window.