http://blogs.msdn.com/b/oldnewthing/archive/2005/11/04/489028.aspx
Why is there a special PostQuitMessage function?
Because it's not really a posted message.
Commenter A. Skrobov asked, "What's the difference
between PostQuitMessage
and PostThreadMessage (GetCurrentThreadId, WM_QUIT)
?"
They are not equivalent, though they may look that way at first glance.
The differences are subtle but significant.
Like the WM_PAINT
, WM_MOUSEMOVE
, and WM_TIMER
messages,
the WM_QUIT
message is not a "real" posted message.
Rather, it is one of those messages that the system generates as if it were posted, even though it wasn't.
And like the other messages, the WM_QUIT
message is a "low priority" message,
generated only when the message queue is otherwise empty.
When a thread calls PostQuitMessage
, a flag in the queue state is set that says,
"If somebody asks for a message and there are no posted messages, then manufacture a WM_QUIT
message."
This is just like the other "virtually posted" messages.
WM_PAINT
messages are generated on demand if there are any invalid regions,
WM_MOUSEMOVE
messages are generated on demand if the mouse has moved since the last time you checked, and
WM_TIMER
messages are generated on demand if there are any due timers.
And since the message is "virtually posted", multiple calls coalesce,
in the same way that multiple paint messages, multiple mouse motions, and multiple timer messages also coalesce.
Why is WM_QUIT
handled like a low-priority message?
Because the system tries not to inject a WM_QUIT
message at a "bad time";
instead it waits for things to "settle down" before generating the WM_QUIT
message,
thereby reducing the chances that the program might be in the middle of a multi-step procedure triggered by a sequence of posted messages.
If you PeekMessage(..., PM_NOREMOVE)
a WM_QUIT
message, this returns a WM_QUIT
message but does not clear the flag.
The WM_QUIT
message virtually "stays in the queue". You can peek it use PeekMessage(..., PM_NOREMOVE)
again ?
i := 0; while TRUE do begin if LongInt( PeekMessage( Msg, 0, 0, 0, PM_REMOVE ) ) > 0 then begin case Msg.message of WM_QUIT : begin Inc( i ); if i = 1 then // First WM_QUIT posted by other thread begin for j := 0 to 100 do PostQuitMessage( j ); // only 1 WM_QUIT in queue ! end else if i = 100 then // i = 2 break; // Never be executed, unless other thread posted WM_QUIT again end; end; end else begin Sleep( 10 ); end; end;
Only can peek WM_QUIT once even if more WM_QUIT posted !
i := 0; while TRUE do begin if LongInt( PeekMessage( Msg, 0, 0, 0, PM_REMOVE ) ) > 0 then begin case Msg.message of WM_QUIT : begin Inc( i ); if i = 1 then // First WM_QIUT from other thread begin for j := 0 to 100 do begin PostQuitMessage( j ); // Only set "quit message pending" flag PostThreadMessage( GetCurrentThreadId( ), WM_USER+j, 0, 0 ); // 1024, 1025, .... end; end else if i = 100 then // Next WM_QUIT from this thread, but no more ... break; end; else begin Sleep( 1 ); // 1024, 1025, ... end; end; end else begin Sleep( 1 ); // end; end;
i := 0; while TRUE do begin if LongInt( PeekMessage( Msg, 0, 0, 0, PM_REMOVE ) ) > 0 then begin case Msg.message of WM_QUIT : begin Inc( i ); if i = 1 then begin for j := 0 to 100 do begin PostThreadMessage( GetCurrentThreadId( ), WM_QUIT, 0, 0 ); PostThreadMessage( GetCurrentThreadId( ), WM_USER+j, 0, 0 ); // WM_QUIT, WM_USER+0, WM_QUIT, WM_USER+1, ... end; end else if i = 100 then break; end; else begin Sleep( 1 ); end; end; end else begin Sleep( 1 ); end; end;
// Exit from here
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.
By comparison, PostThreadMessage
just places the message in the thread queue (for real, not virtually),
and therefore it does not get any of the special treatment that a real PostQuitMessage
triggers.
PostQuitMessage : Set
"quit message pending" flag, Generate a WM_QUIT when the queue goes quit
PostThreadMessage
: Place WM_QUIT in queue
Thread0 : PostThreadMessage( ThreadId, WM_QUIT )
Thread1 : PostThreadMessage( ThreadId, WM_USER )
Thread2 : PostThreadMessage( ThreadId, WM_XXXX )
Thread3 : PostThreadMessage( ThreadId, WM_QUIT )
Thread4 : PostThreadMessage( ThreadId, WM_YYYY )
PeekMessage() : WM_USER, WM_XXXX, WM_YYYY, WM_QUIT
Because the system tries not to inject a WM_QUIT
message at a "bad time";
instead it waits for things to "settle down" before generating the WM_QUIT
message,
thereby reducing the chances that the program might be in the middle of a multi-step procedure triggered by a sequence of posted messages.
WINUSERAPI VOID WINAPI PostQuitMessage( __in int nExitCode);
nExitCode是退出标识码,它被放到WM_QUIT消息的参数wParam里。
PostQuitMessage寄送一个WM_oUT消息给线程的消息队列并立即返回;
此函数向系统表明有个线程请求在随后的某一时间终止。
当线程从消息队列里取得WM_QUIT消息时,应当退出消息循环并将控制返回给系统。
返回给系统的退出值必须是消息WM_QUIT的wParam参数。
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { int wmId, wmEvent; PAINTSTRUCT ps; HDC hdc; switch (message) { case WM_COMMAND: wmId = LOWORD(wParam); wmEvent = HIWORD(wParam); switch (wmId) { case IDM_ABOUT: DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About); break; case IDM_EXIT: DestroyWindow(hWnd); break; default: return DefWindowProc(hWnd, message, wParam, lParam); } break; case WM_PAINT: hdc = BeginPaint(hWnd, &ps); // EndPaint(hWnd, &ps); break; case WM_DESTROY: PostQuitMessage(0); break;
default: return DefWindowProc(hWnd, message, wParam, lParam); } return 0; }