轉自:http://www.cppblog.com/mzty/archive/2006/11/24/15619.html
一 系統消息隊列和應用程序消息隊列
Windows中有一個系統消息隊列,對於每一個正在執行的Windows應用程序,系統為其建立一個“消息隊列”,即應用程
序消息隊列,用來存放該程序可能創建的各種窗口的消息。應用程序中含有一段稱作“消息循環”的代碼,用來從消息隊列中
檢索這些消息並把它們分發到相應的窗口函數中。
二 消息循環
Windows為當前執行的每個Windows程序維護一個「消息隊列」。在發生輸入事件之后,Windows將事件轉換為一個「消
息」並將消息放入程序的消息隊列中。程序通過執行一塊稱之為「消息循環」的程序代碼從消息隊列中取出消息:
while(GetMessage (&msg, NULL, 0, 0))
{
TranslateMessage (&msg) ;
DispatchMessage (&msg) ;
}
msg變量是型態為MSG的結構,型態MSG在WINUSER.H中定義如下:
typedef struct tagMSG
{
HWND hwnd ;
UINT message ;
WPARAM wParam ;
LPARAM lParam ;
DWORD time ;
POINT pt ;
}
MSG, * PMSG ;
POINT數據型態也是一個結構,它在WINDEF.H中定義如下:
typedef struct tagPOINT
{
LONG x ;
LONG y ;
}
POINT, * PPOINT;
TranslateMessage(&msg); 將msg結構傳給Windows,進行一些鍵盤轉換。
DispatchMessage(&msg);又將msg結構回傳給Windows。然后,Windows將該消息發送給適當的窗口消息處理程序,讓它
進行處理。這也就是說,Windows將呼叫窗口消息處理程序。在HELLOWIN中,這個窗口消息處理程序就是WndProc函數。
處理完消息之后,WndProc傳回到Windows。此時,Windows還停留在DispatchMessage呼叫中。在結束
DispatchMessage呼叫的處理之后,Windows回到HELLOWIN程序中,並且接着從下一個GetMessage呼叫開始消息循環。
附:關於窗口過程和消息循環的一些注解。轉自孫鑫老師《Windows程序內部運行機制》
1.關於WNDPROC函數指針原型:typedef LRESULT (CALLBACK * WNDPROC)(HWND, UINT, WPARAM, LPARAM);
其中CALLBACK函數約定就是__stdcall約定.
2.TranslateMessage函數用於將虛擬鍵消息轉為字符消息。字符消息被投遞到調用線程的消息隊列中,當下一次調用GetMessage函數時取出。當我們敲擊鍵盤上的某個字符鍵時,系統將產生WM_KEYDOWN和WM_KEYUP消息。這兩個消息的附加參數(wParam和lParam)包含的是虛擬鍵代碼和掃描碼等消息,而我們在程序中往往需要得到某個字符的ASSII碼,TranslateMessage這個函數可以將WM_KEYDOWN和WM_KEYUP消息的組合轉換為一條WM_CHAR消息(該消息的wParam附加參數包含了字符的ASCII碼),並將轉換后的新消息投遞到調用線程的消息隊列中。注意,TranslateMessage函數並不會修改原有的消息,它只是產生新的消息並投遞到消息隊列中。
DispatchMessage函數分派一個消息到窗口過程,由窗口過程函數對消息進行處理。DispatchMessage實際上是將消息回傳給操作系統,由操作系統調用窗口過程函數對消息進行處理(響應)。
Windows應用程序的消息處理機制如下圖所示:
(1)操作系統收到應用程序的窗口消息,將消息投遞到該應用程序的消息隊列中。
(2)應用程序在消息循環中調用GetMessage函數從消息隊列中取出一條一條的消息。取出消息后,應用程序可以對消息進行一些預處理,例如,放棄對某些消息的響應,或者調用TranslateMessage產生新的消息。
(3)應用程序調用DispatchMessage,將消息回傳給操作系統。消息是由MSG結構體對象進行表示的,其中就包含了接收消息的窗口的句柄。因此,DispatchMessage函數總能進行正確的傳遞。
(4)系統利用WNDCLASS結構體的lpfnWndProc成員保存的窗口過程函數的指針調用窗口過程,對消息進行處理(即“系統給應用程序發送了消息”)。
以上就是Windows應用程序的消息處理過程。
三 隊列化消息與非隊列化消息
消息能夠被分為「隊列化的」和「非隊列化的」。隊列化的消息是由Windows放入程序消息隊列中的。在程序的消息循環中,
重新傳回並分配給窗口消息處理程序。非隊列化的消息在Windows呼叫窗口時直接送給窗口消息處理程序。也就是說,隊列化
的消息被「發送」給消息隊列,而非隊列化的消息則「發送」給窗口消息處理程序。任何情況下,窗口消息處理程序都將獲得
窗口所有的消息--包括隊列化的和非隊列化的。窗口消息處理程序是窗口的「消息中心」。
隊列化消息基本上是使用者輸入的結果,以擊鍵(如WM_KEYDOWN和WM_KEYUP消息)、擊鍵產生的字符
(WM_CHAR)、鼠標移動(WM_MOUSEMOVE)和鼠標按鈕(WM_LBUTTONDOWN)的形式給出。隊列化消息還包含時
鍾消息(WM_TIMER)、更新消息(WM_PAINT)和退出消息(WM_QUIT)。
非隊列化消息則是其它消息。在許多情況下,非隊列化消息來自呼叫特定的Windows函數。例如,當WinMain呼叫
CreateWindow時,Windows將建立窗口並在處理中給窗口消息處理程序發送一個WM_CREATE消息。當WinMain呼叫
ShowWindow時,Windows將給窗口消息處理程序發送WM_SIZE和WM_SHOWWINDOW消息。當WinMain呼叫
UpdateWindow時,Windows將給窗口消息處理程序發送WM_PAINT消息。鍵盤或鼠標輸入時發出的隊列化消息信號,也能
在非隊列化消息中出現。例如,用鍵盤或鼠標選擇了一個菜單項時,鍵盤或鼠標消息就是隊列化的,而說明菜單項已選中的
WM_COMMAND消息則可能就是非隊列化的。
四 SendMessage()與PostMessage()之間的區別是什么?
它們兩者是用於向應用程序發送消息的。PostMessage()將消息直接加入到應用程序的消息隊列中,不等程序返回就退出;而
SendMessage()則剛好相反,應用程序處理完此消息后,它才返回。我想下圖能夠比較好的體現這兩個函數的關系:
五 函數peekmessage和getmessage的區別?
兩個函數主要有以下兩個區別:
1.GetMessage將等到有合適的消息時才返回,而PeekMessage只是撇一下消息隊列。
2.GetMessage會將消息從隊列中刪除,而PeekMessage可以設置最后一個參數wRemoveMsg來決定是否將消息保留在隊列
中。