天天看點

Win32 SDK基礎(8)—— Windows消息機制一、 什麼是消息二、 消息擷取三、消息處理

一、 什麼是消息

        在解釋什麼是消息之前,我們先讨論一下程式的執行機制問題。大體上說,程式按照執行機制可以分為兩類:

第一類是過程驅動。比如我們最早接觸程式設計時寫的C程式,又或者單片機程式。這類程式往往預先已經設定好了執行流程,我們執行時隻是按部就班的執行;

第二類是事件驅動。事件,相信大家都能夠了解。每個事件的發生都是随機的,每個事件都會有發生的時刻,類似生活中的事件。程式中的事件也會有自己的觸發點,事件驅動程式就是事先編寫好了針對每個事件的處理流程。在Windows的作業系統中,消息就是Windows中的事件。Windows中的幾乎每個操作都會觸發消息,像我們之前講過的建立視窗會觸發WM_CREATE消息,繪制視窗會觸發WM_PAINT消息,我們點選滑鼠、鍵盤、都會觸發相應的消息。

        Windows的消息被封裝成了一個叫做MSG的結構體,其原型如下:

typedef struct tagMSG { // msg 
HWND hwnd;
UINT message;
WPARAM wParam;
LPARAM lParam;
DWORD time;
POINT pt;
} MSG;
           

Hwnd —— 觸發消息的視窗的句柄。

Message —— 消息ID。Windows作業系統為每個消息都配置設定了一個消息ID,這個ID是唯一的。我們在上文中提到過的WM_CREATE本質就是一個整數,就是消息ID。

wParam —— 消息可附帶的參數。

lParam —— 消息可附帶的參數。

Time —— 發生消息的時刻。

Pt —— 發生消息時滑鼠所在的位置。

以上參數對消息來說,缺一不可。

Windows中就是将一個個消息封裝成MSG對象,發送消息時,将這些對象放置到消息隊列中;擷取消息時,也是擷取的這些MSG對象。

二、 消息擷取

2.1 消息隊列

        我們說過,在Windows中幾乎每個操作都會觸發一條消息,這些消息都被發送到消息隊列中。何為消息隊列?我們可以将其了解為使用了一個存放Msg對象的先進先出的Deque—— Deque<Msg>。消息隊列分為兩種,一種是系統消息隊列,另外一種是程序消息隊列。我們在觸發消息後,消息先進入系統消息隊列。作業系統處理後會根據消息的的視窗句柄hwnd值将消息配置設定到我們程式自己的消息隊列,然後在我們程式内部進行消息的處理。

2.2 消息循環

        在前面的文章中,我們曾經寫過一個消息循環。所謂的消息循環,就是不斷的讀取我們的程序中的消息隊裡中的消息,然後在進行處理。

  1. void Message()  
  2. {  
  3.     MSG nMsg = { 0 };  
  4.     while (GetMessage(&nMsg, NULL, 0, 0))  
  5.     {  
  6.         TranslateMessage(&nMsg);  
  7.         DispatchMessage(&nMsg);  
  8.     }  
  9. }  

        這裡面,GetMessage()不斷的在消息隊列中抓取消息,其函數原型如下:

GetMessage(LPMSG lpMsg,HWND hWnd,UINT wMsgFilterMin,UINT wMsgFilterMax)

lpMsg —— 用來存放消息的MSG類型的指針。

hWnd —— 指定取得其消息的視窗的句柄。當其值取NULL時,GetMessage為任何屬于調用線程的視窗檢索消息。

wMsgFilterMin —— 指定被檢索的最小消息值的整數。

wMsgFilterMax —— 指定被檢索的最大消息值的整數。

        GetMessage()擷取到消息後,TranslateMessage會将消息進行翻譯,主要是把虛拟鍵消息轉換為字元消息。字元消息被寄送到調用線程的消息隊列裡,當下一次線程調用函數GetMessage或PeekMessage時被讀出。Windows中每一個鍵盤按鍵,都對應了一個宏,這個鍵盤按鍵發出的消息就是虛拟鍵消息。TranslateMessage的作用就是将虛拟鍵消息轉成字元消息WM_CHAR、WM_SYSCHAR等等。

三、消息處理

        DispatchMessage的作用就是将消息,分派到我們實作定義好的視窗處理函數中進行處理,下面是我們在之前的文章中定義的視窗處理函數:

LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)  
{  
	switch (uMsg)  
	{  
	case WM_DESTROY:  
		PostQuitMessage(0);//可¨¦以°?使º1GetMessage返¤¦Ì回?0  
		break;  
	default:  
		break;  
	}  
	return DefWindowProc(hWnd, uMsg, wParam, lParam);  
}  
           

        hWnd就是産生消息的視窗句柄,uMsg是傳遞的消息,wParam和lParam分别是消息攜帶的兩個參數。在上面的視窗處理函數中,我們定隻處理了一個消息WM_DESTROY,這是我們在點選視窗的關閉按鈕後産生的一個消息。我們說過,我們在建立視窗是,也會産生一個WM_CREATE消息。下面我們在視窗處理函數中處理這個消息:

LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)  
{  
	switch (uMsg)  
	{  
	case WM_DESTROY:  
		PostQuitMessage(0);//可¨¦以°?使º1GetMessage返¤¦Ì回?0  
		break; 
	case  WM_CREATE:
		MessageBox(NULL,"WM_CREATE消息被處理了","消息處理",MB_OK);
	default:  
		break;  
	}  
	return DefWindowProc(hWnd, uMsg, wParam, lParam);  
}  
           

        我們在接受到WM_CREATE後,會彈出一個對話框。預期的效果是點選這個對話框的确定按鈕後才會顯示視窗。如下面所示:

        運作程式,先彈出對話框:

Win32 SDK基礎(8)—— Windows消息機制一、 什麼是消息二、 消息擷取三、消息處理

        點選确定按鈕後,彈出視窗:

Win32 SDK基礎(8)—— Windows消息機制一、 什麼是消息二、 消息擷取三、消息處理

Github位置:

https://github.com/HymanLiuTS/Win32SDK

克隆本項目:

git clone [email protected]:HymanLiuTS/Win32SDK.git

擷取本文源代碼:

git checkout WL08

繼續閱讀