1. 程式人生 > >Win32 SDK基礎(11)—— 訊息佇列和GetMessage/PeekMessage、SendMessage/Postmesage

Win32 SDK基礎(11)—— 訊息佇列和GetMessage/PeekMessage、SendMessage/Postmesage

一、訊息佇列

1.1 訊息佇列

        訊息佇列是用來存放訊息的一個佇列,訊息在佇列中先入先出,所有的視窗程式都具有訊息佇列,程式可以從佇列中獲取訊息。

1.2 訊息佇列的型別

        系統訊息佇列:由作業系統維護的訊息佇列,存放系統產生的訊息,如滑鼠、鍵盤訊息等等。

        程式訊息佇列:屬於每一個應用程式(執行緒)的訊息佇列,用應用程式維護。

        當產生滑鼠、鍵盤等訊息時,訊息先存放到系統訊息佇列,然後作業系統根據存放的訊息找到對應的視窗的訊息佇列,將訊息投遞到視窗的訊息佇列中。

1.3 佇列訊息和非佇列訊息

        佇列訊息:訊息發出後,首先放入佇列,然後通過訊息迴圈獲取。常見的佇列訊息:鍵盤、滑鼠、定時器訊息等等。         非佇列訊息:訊息發出後,直接找到視窗的訊息處理函式,呼叫訊息處理函式進行處理,無需經過訊息佇列。常見的非佇列訊息:WM_PAINT、WM_SIZE等等。

二、訊息迴圈和GetMessage/PeekMessage

2.1 訊息迴圈

        一般的訊息迴圈如下:
void Message(HWND hWnd)  
{  
	MSG nMsg = { 0 };
	while (GetMessage(&nMsg, hWnd, 0, 0))  
	{  
		TranslateMessage(&nMsg);  
		DispatchMessage(&nMsg);  

		if(nMsg.message == WM_PAINT)
		{
			char buff[30]={};
			sprintf(buff,"處理訊息%d\n",nMsg.message);
			WriteConsole(hOutput,buff,sizeof(buff),NULL,NULL);
		}
	}  
}  
GetMessage/PeekMessage:從程式的訊息隊列當中獲取訊息。 TranslateMessage:將鍵盤上的按鍵等訊息翻譯成字元訊息。  DispatchMessage:將翻譯後的訊息再次放入到程式的訊息佇列中。

2.2 GetMessage和PeekMessage

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

lpMsg:指向MSG結構的指標,該結構從執行緒的訊息佇列裡接收訊息資訊。
hWnd:取得其訊息的視窗的控制代碼。當其值取NULL時,GetMessage為任何屬於呼叫執行緒的視窗檢索訊息,執行緒訊息通過PostThreadMessage寄送給呼叫執行緒。
wMsgFilterMin:指定被檢索的最小訊息值的整數。
wMsgFilterMax:指定被檢索的最大訊息值的整數。
返回值:如果函式取得WM_QUIT之外的其他訊息,返回非零值。如果函式取得WM_QUIT訊息,返回值是零。如果出現了錯誤,返回值是-1。例如,當hWnd是無效的視窗控制代碼或lpMsg是無效的指標時。若想獲得更多的錯誤資訊,請呼叫GetLastError函式。
BOOL PeekMessage(
LPMSG IpMsg,
HWND hWnd,
UINT wMSGfilterMin,
UINT wMsgFilterMax,
UINT wRemoveMsg
);
lpMsg:接收訊息資訊的MSG結構指標。
hWnd:其訊息被檢查的視窗控制代碼。
wMsgFilterMin:指定被檢查的訊息範圍裡的第一個訊息。
wMsgFilterMax:指定被檢查的訊息範圍裡的最後一個訊息。
wRemoveMsg:確定訊息如何被處理。此引數可取下列值之一:
意義
PM_NOREMOVE PeekMessage處理後,訊息不從佇列裡除掉。
PM_REMOVE PeekMessage處理後,訊息從佇列裡除掉。
PM_NOYIELD 此標誌使系統不釋放等待呼叫程式空閒的執行緒。可將PM_NOYIELD隨意組合到PM_NOREMOVE或PM_REMOVE。
GetMessage和PeekMessage的主要區別在於:GetMessage是阻塞函式,它會在訊息迴圈中會一直阻塞直到訊息佇列中出現了訊息可以被獲取,而PeekMessage是非阻塞函式,不管有沒有獲取到訊息佇列中的訊息,它都會返回。PeekMessage更多用來檢測訊息隊裡中是否有訊息,它的最後一個引數可以用來指定獲取到訊息後要不要把訊息從訊息佇列中移除,通常情況下通過PeekMessage檢測到訊息佇列有訊息之後,再呼叫GetMessage區獲取。

2.3 GetMessage/PeekMessage獲取訊息的過程

1、先在程式的訊息佇列中查詢訊息,如果有佇列訊息,就取出訊息。 2、如果程式的訊息佇列中沒有訊息,向系統的訊息佇列獲取屬於本程式的訊息。如果系統的訊息佇列中有屬於本程式的訊息,系統的訊息佇列會將訊息分發到本程式的訊息佇列中。 3、如果系統的訊息佇列也沒有訊息,檢查視窗需要繪製的區域是否需要重繪,如果發現有需要重繪的區域,產生WM_PAINT訊息。 4、如果沒有重新繪製區域,檢查是否具有到時的定時器,如果有產生WM_TIMER定時器訊息。 5、如果沒有到時的定時器,整理程式的資源、記憶體等等。

三、SendMessage和PostMessage

LRESULT SendMessage(
HWND hWnd,
UINT Msg,
WPARAM wParam,
LPARAM IParam
)

hWnd:其視窗程式將接收訊息的視窗的控制代碼。如果此引數為HWND_BROADCAST,則訊息將被髮送到系統中所有頂層視窗,包括無效或不可見的非自身擁有的視窗、被覆蓋的視窗和彈出式視窗,但訊息不被髮送到子視窗。
Msg:指定被髮送的訊息。
wParam:指定附加的訊息特定資訊。
IParam:指定附加的訊息特定資訊。 返回值:返回值指定訊息處理的結果,依賴於所傳送的訊息。
BOOL WINAPI PostMessage(
HWND hWnd,
UINT Msg,
WPARAM wParam,
LPARAM lParam
);

hWnd:其視窗程式接收訊息的視窗的控制代碼。可取有特定含義的兩個值:
HWND_BROADCAST:訊息被寄送到系統的所有頂層視窗,包括無效或不可見的非自身擁有的視窗、 被覆蓋的視窗和彈出式視窗。訊息不被寄送到子視窗
NULL:此函式的操作和呼叫引數dwThread設定為當前執行緒的識別符號PostThreadMessage函式一樣
Msg:指定被寄送的訊息。
wParam:指定附加的訊息特定的資訊。
LParam:指定附加的訊息特定的資訊。
返回值:如果函式呼叫成功,返回非零,否則函式呼叫返回值為零
1、SendMessage        傳送訊息到指定的視窗,並等候對方將訊息處理,為阻塞函式,獲取訊息的執行結果後返回。主要需要傳送非佇列訊息,傳送的訊息不經過訊息佇列。 2、PostMessage         傳送訊息到程式的訊息佇列,不管訊息有沒有被處理都會立即返回,用於佇列訊息的傳送。 Github位置:克隆本專案:
git clone [email protected]:HymanLiuTS/Win32SDK.git

獲取本文原始碼:
git checkout WL11