本文對WM_CLOSE、WM_DESTROY、WM_QUIT及各種訊息投遞函數的功能及區別做出了分析比對,有助於讀者更好的對訊息投遞函數加以理解。詳情如下:
一、WM_CLOSE、WM_DESTROY、WM_QUIT區別
WM_CLOSE:關閉應用程式視窗
WM_DESTROY:關閉應用程式
WM_QUIT:關閉訊息迴圈
只有關閉了訊息迴圈,應用程式的進程才真正退出(在工作管理員裡消失)。
win32應用程式的完整退出過程:點擊視窗右上方的關閉按鈕,發送WM_CLOSE訊息。此訊息處理中調用DestroyWindow函數,發送WM_DESTROY訊息。此訊息處理中調用PostQuitMessage(0)函數,發送WM_QUIT訊息到訊息佇列中。GetMessage捕獲到WM_QUIT,返回0,退出迴圈(應用程式真正退出)。
注意:按照上述正常流程,WM_QUIT是不會到達視窗過程的。(因為在GetMessage截獲了WM_QUIT訊息之後,程式已經徹底退出了!)
MFC應用程式的完整退出過程:點擊視窗右上方的關閉按鈕,或選擇【File/Close】,發出 WM_CLOSE訊息。CMyFrameWnd 並沒有設定WM_CLOSE 處理常式,於是交給預設之處理常式。預設函數對於WM_CLOSE 的處理方式是呼叫 ::DestroyWindow, 並因而發出WM_DESTROY。預設之WM_DESTROY 處理方式是呼叫::PostQuitMessage,因此發出WM_QUIT。CWinApp::Run 收到WM_QUIT 後會結束其內部之訊息迴路, 然後呼叫ExitInstance,這是CWinApp 的一個虛擬函數。如果自己應用程式類CMyWinApp 改寫了ExitInstance , 那麼CWinApp::Run 所呼叫的就是CMyWinApp::ExitInstance,否則就是 CWinApp::ExitInstance。最後回到 AfxWinMain,執行 AfxWinTerm,結束程式。
附加:當調用DestroyWindow函數後,作業系統就會進行一系列的刪除動作,先發送WM_DESTROY訊息,接著發送WM_NCDESTROY訊息。如果這個視窗還有子視窗或者是其它視窗的所有者,就需要給所有子視窗發送刪除訊息。
WM_QUIT是唯一可以使GetMessage(&msg,NULL,0,0)返回假值的訊息.
相關程式碼分析:
//主函數中進入訊息迴圈的代碼片斷while(GetMessage(&msg,NULL,0,0)){TranslateMessage(&msg); //將訊息進行處理一下DispatchMessage(&msg); //再將訊息變數msg傳給windows,讓windows來調用訊息處理函數}
如果把GetMessage(&msg,NULL,0,0)改為GetMessage(&msg,hWnd,0,0),則發現關閉應用程式後,工作管理員中仍有該程式的進程,且佔用大量的記憶體,why?
msdn中的原因解釋是:對於GetMessage(&msg,hWnd,0,0),當第二個參數無效時,此函數傳回值為-1。對於上述迴圈來說,此while條件為真,因此進入死迴圈,進程無法退出。
二、各種訊息投遞函數
1、SendMessage:發送訊息給指定的視窗過程;直到視窗過程處理了訊息才返回。
2、PostMessage:將訊息放入訊息佇列(與指定視窗建立的線程相關)中;無需等待訊息處理,立即返回。
不能發送WM_QUIT訊息,此訊息只能由PostQuitMessage函數發送。
3、PostThreadMessage:發送訊息給指定線程的訊息佇列;無需等待線程處理訊息,立即返回。
此函數發送的訊息和視窗是無關的。我們只需指定線程ID就OK了,但要保證線程已建立,否則會失敗。
4、GetMessage:從調用線程的訊息佇列中取訊息。
當第二個參數為NULL時,它檢索以下兩種訊息:
a、屬於調用線程的任何視窗的訊息;
b、由PostThreadMessag投遞給該調用線程的訊息。
5、PeekMessage:功能同GetMessage。區別是:
GetMessage:直到一個匹配了過濾條件的訊息,被放到訊息佇列中才返回。
PeekMessage:不管訊息是否放入隊列,立即返回。
6、DispatchMessage:派遣訊息給相應的視窗過程。
7、TranslateMessage:轉換虛擬鍵資訊到字元訊息。