首先來了解幾個基本概念:
消息:在了解什么是消息先來了解什么是事件。事件可分為幾種,由輸入設備觸發的,比如鼠標鍵盤等等。由窗體控件觸發的,比如button控件,file菜單等。還有就是來自Windows內部的事件。這三種稱為事件。而消息,是由事件翻譯而來的。事件產生消息。
從數據結構角度來講,消息是一種結構體。結構如下:
typedef struct tagMSG { HWND hwnd; //窗口句柄。 UINT message;//消息類型。 WPARAM wParam;//32位附加信息。 LPARAM lParam;//32位附加信息。 DWORD time;//消息發送時間。 POINT pt;//消息發送時,鼠標所在位置。 }MSG;
消息隊列:消息隊列有兩種,分為系統消息隊列和應用程序消息隊列。產生的消息首先由Windows系統捕獲,放在系統消息隊列,再拷貝到對應的應用程序消息隊列。32/64位系統為每一個應用程序維護一個消息隊列。
消息循環:系統為每個應用程序維護一個消息循環,消息循環會不斷檢索自身的消息隊列。每有一個消息,就用GetMessage()取出消息。
while(GetMessage (&msg, NULL, 0, 0))//Windows消息循環。 { TranslateMessage (&msg) ;//翻譯消息,如按鍵消息,翻譯為WM_CHAR DispatchMessage (&msg) ;//分發消息到對應窗口 }
GetMessage具有阻塞機制。當消息隊列中沒有消息時,程序非忙等,而是讓權等待。當收到WM_QUIT時,GetMessage返回false,循環停止,同時應用程序終止。
消息處理:DispatchMessage()把取出來的消息分配給相應的窗口或線程,由窗口過程處理函數DefWindowProc()處理。
==============================================================================================================================================================================割割割割割
以上簡要地介紹了消息隊列,消息循環與消息處理的概念。
Windows的應用程序靠消息驅動來實現功能。而消息驅動靠消息機制來處理。消息機制就是由消息隊列,消息循環,消息處理構成的。
那么,消息機制是如何運作的呢?
當用戶運行一個應用程序,通過對鼠標的點擊或鍵盤按鍵,產生一些特定事件。由於Windows一直監控着I/O設備,該事件首先會被翻譯成消息,由系統捕獲,存放於系統消息隊列。經分析,Windows知道該消息應由那個應用程序處理,則拷貝到相應的應用程序消息隊列。由於消息循環不斷檢索自身的消息隊列,當發現應用程序消息隊列里有消息,就用GetMessage()取出消息,封裝成Msg()結構。如果該消息是由鍵盤按鍵產生的,用TranslateMessage()翻譯為WM_CHAR消息,否則,用DisPatchMessage()將取出的消息分發到相應的應用程序窗口,交由窗口處理程序處理。Windows為每個窗體預留了過程窗口函數,該函數是一個回掉函數,由系統調用,應用程序不能調用。程序員可以通過重載該函數處理我們”感興趣”的消息。對於不感興趣的消息,則由系統默認的窗口過程處理程序做出處理。
下面看這么一張圖:
這張圖很好地解釋了消息機制的運行原理。
當運行程序->事件操作引發消息->消息先存在系統消息隊列->再存入到應用程序消息隊列->用消息循環提取消息->處理消息->再返回消息隊列....
while(GetMessage(&Msg, NULL, 0, 0) > 0) { TranslateMessage(&Msg); DispatchMessage(&Msg); }
上面代碼的執行過程為:
1. 消息循環調用GetMessage()從消息隊列中查找消息進行處理,如果消息隊列為空,程序將停止執行並等待(程序阻塞)。
2. 事件發生時導致一個消息加入到消息隊列(例如系統注冊了一個鼠標點擊事件),GetMessage()將返回一個正值,這表明有消息需要被處理,並且消息已經填充到傳入的MSG參數中;當傳入WM_QUIT消息時返回0;如果返回值為負表明發生了錯誤。
3. 取出消息(在Msg變量中)並將其傳遞給TranslateMessage()函數,這個函數做一些額外的處理:將虛擬鍵值信息轉換為字符信息。這一步實際上是可選的,但有些地方需要用到這一步。
4. 上面的步驟執行完后,將消息傳遞給DispatchMessage()函數。DispatchMessage()函數將消息分發到消息的目標窗口,並且查找目標窗口過程函數,給窗口過程函數傳遞窗口句柄、消息、wParam、lParam等參數然后調用該函數。
5. 在窗口過程函數中,檢查消息和其他參數,你可以用它來實現你想要的操作。如果不想處理某些特殊的消息,你應該總是調用DefWindowProc()函數,系統將按按默認的方式處理這些消息(通常認為是不做任何操作)。
6. 一旦一個消息處理完成,窗口過程函數返回,DispatchMessage()函數返回,繼續循環處理下一個消息。