在Win32 SDK中消息本身是作為一個結構體記錄傳遞給應用程序的,這個記錄中包含了消息的類型以及其他信息。這個記錄類型叫做MSG,它在window中是這樣聲明的:
typedef struct tagMSG { // msg
HWND hwnd; //窗口句柄
UINT message; //消息常量標識符
WPARAM wParam; //32位消息的特定附加信息,具體表示什么處決於message
LPARAM lParam; //32位消息的特定附加信息,具體表示什么處決於message
DWORD time; //消息創建時的時間
POINT pt; //消息創建時的鼠標位置
} MSG;
hwnd 接收消息的32位窗口句柄。窗口可以是任何類型的屏幕對象,
因為Win32能夠維護大多數可視對象的句柄(窗口、對話框、按鈕、編輯框等)。
message 用於區別其他消息的常量值,這些常量可以是Windows單元中預定義的常量,也可以是自定義的常量。
wParam 通常是一個與消息有關的常量值,也可能是窗口或控件的句柄。
lParam 通常是一個指向內存中數據的指針。
由於wParam,lParam和指針都是32位的,需要時可以強制類型轉換。具體表示什么,與message相關,他們是事先定義好的。
如果自定義消息:#define WM_MYMESSAGE WM_USER+100,需確定wParam,lParam的意義
(假設wParam=0時發送數據,wParam=1時接收數據,lParam為CMyClass* 指針,指向一個CMyClass對象,准備要發送的數據或接收數據
發送WM_MYMESSAGE時 SendMessage(hwnd,WM_MYMESSAGE,0,pMyClassObject)
接收消息的窗口,接收WM_MYMESSAGE中(CMyClass*)lParam參數即pMyClassObject傳過來的數據
我舉個例子吧,也不知確切不確切,大家多包涵阿
首先要說windows是消息驅動的系統,系統為每一個程序(應該說進程)建立一個消息隊列。
舉我們選擇菜單的例子,當選擇了一個菜單項的時候,Windows向菜單所屬的窗口發送WM_COMMAND消息;而用戶按下了一個加速鍵的時候,windows向TranslateAccelerate函數指定的目標窗口發送WM_COMMAND消息。一般這兩者對應的窗口都是主窗口,所以在主窗口中的窗口過程中集中處理WM_COMMAND消息,而不必考慮它究竟是菜單引發的還是加速鍵引發的。
WM_COMMAND消息的兩個參數是這樣定義的:
wParam的高位 =wNotifyCode ;通知碼
wParam的低位 =wID ;命令ID
lParam = hwdCtl ;發送WM_COMMAND 消息的子窗口句柄,即誰發的該消息
除了菜單和加速鍵,WM_COMMAND 消息也可以由其他子窗口引發,如主窗口中的按鈕或工具欄,還有你提到的系統托盤的鼠標事件等等,lParam參數指定了引發消息的子窗口句柄,對於菜單和加速鍵引發的WM_COMMAND消息,lParam的值為0。wParam參數的低16位是命令ID,也就是資源腳本文件中菜單項的命令ID或加速鍵的命令ID,高16位是通知碼,菜單消息的通知碼是0,加速鍵消息的通知碼為1。
這只是菜單和加速鍵的定義。其他的消息可能與此不同,具體查資料吧。
WPARAM
WPARAM,消息響應機制 wParam和lParam 這兩個是Win16系統遺留下來的產物,在Win16API中WndProc有兩個參數:
一個是WORD類型的16位整型變量;另一個是LONG類型的32位整型變量。因此根據匈牙利命名法,16位的變量就被命名為wParam, 32位的變量就被命名為lParam。 到了Win32API中,原來的16位變量也被擴展為32位,因此此時wParam和lParam的大小完全相同。 在Win32API的早期,為了保證和Win16API的代碼可移植性MS定義了WPARAM和LPARAM兩個宏。當時保留了w前綴的原因一方面是由於WPARAM宏也已W開頭,還有也因為要提醒程序員注意到可移植性,當然到了現在Win16早已退出歷史舞台,這個前綴也就約定俗成的沿用下來了。
例如:主程序MyDlg.cpp
1.自定義消息:#define WM_TRAY WM_USER 100
2.函數原形:afx_msg LRESULT OnTrayNotify(WPARAM wParam,LPARAM lParam);
3.消息映射:ON_MESSAGE(WM_TRAY,OnTrayNotify)
4.原函數: LRESULT CMyDlg::OnTrayNotify(WPARAM wParam,LPARAM lParam)
{
return m_tray.OnTrayNotify(wParam,lParam);
}
托盤類的實現程序Tray.cpp
成員函數: int OnTrayNotify(WPARAM wID,LPARAM lEvent)
{
if(wID == TRAYNOTIFYDATA.uID) return 0;
if(lEvent == WM_LBUTTONDOWN){ 處理代碼 }
else if(lEvent == WM_RBUTTONDOWN){ 處理代碼 } return 0;
}
WPARAM 和 LPARAM 本質上沒有什么區別:都是32位數,但是區別也還是有的:除了上面幾位若仁兄說的關於16位的的歷史問題外,MICROSOFT在使用時兩種參數分別代表不同的含義和內容,WPARAM常常代表一些控件的ID或者高位底位組合起來分別表示鼠標的位置,如果消息的發送者需要將某種結構的指針或者是某種類型的句柄時,習慣上用LPARAM來傳遞,可以參考各種控件的通知消息:可以查看:EN_CHANGE (EDIT控件的一個通知消息),CBEM_INSERTITEM(可擴展組合框的可接受消息)等等來加以領會。 理論上在使用自定義消息時,WPARAM LPARAM的含義可以程序員任意指定的,但是最好遵從MFC中的習慣。在調用SendMessage()函數時,第二個參數是WPARAM,第三個參數是這個消息的LPARAM,但是你在程序中某個類中寫下ON_MESSAGE()宏來處理這個消息時,處理函數SomeHandler(WPARAM,LPRAM(默認是0))中解釋這兩個參數時必須按照SendMessage調用中的意義來進行。
消息響應機制
1、消息的組成:一個消息由一個消息名稱(UINT),和兩個參數(WPARAM,LPARAM)。當用戶進行了輸入或是窗口的狀態發生改變時系統都會發送消息到某一個窗口。例如當菜單轉中之后會有WM_COMMAND消息發送,WPARAM的低字中(LOWORD(wParam))是命令的ID號,對菜單來講就是菜單ID。當然用戶也可以定義自己的消息名稱,也可以利用自定義消息來發送通知和傳送數據。
2、誰將收到消息:一個消息必須由一個窗口接收。在窗口的過程(WNDPROC)中可以對消息進行分析,對自己感興趣的消息進行處理。例如你希望對菜單選擇進行處理那么你可以定義對WM_COMMAND進行處理的代碼,如果希望在窗口中進行圖形輸出就必須對WM_PAINT進行處理。
3、未處理的消息到那里去了:M$為窗口編寫了默認的窗口過程,這個窗口過程將負責處理那些你不處理消息。正因為有了這個默認窗口過程我們才可以利用Windows的窗口進行開發而不必過多關注窗口各種消息的處理。例如窗口在被拖動時會有很多消息發送,而我們都可以不予理睬讓系統自己去處理。
4、窗口句柄:說到消息就不能不說窗口句柄,系統通過窗口句柄來在整個系統中唯一標識一個窗口,發送一個消息時必須指定一個窗口句柄表明該消息由那個窗口接收。而每個窗口都會有自己的窗口過程,所以用戶的輸入就會被正確的處理。例如有兩個窗口共用一個窗口過程代碼,你在窗口一上按下鼠標時消息就會通過窗口一的句柄被發送到窗口一而不是窗口二。
5、示例:下面有一段偽代碼演示如何在窗口過程中處理消息
LONG yourWndProc(HWND hWnd,UINT uMessageType,WPARAM wP,LPARAM) {
switch(uMessageType) {//使用SWITCH語句將各種消息分開
case(WM_PAINT): doYourWindow(...);//在窗口需要重新繪制時進行輸出 break;
case(WM_LBUTTONDOWN): doYourWork(...);//在鼠標左鍵被按下時進行處理 break;
default: callDefaultWndProc(...);//對於其它情況就讓系統自己處理 break;
}
}
接下來談談什么是消息機制:系統將會維護一個或多個消息隊列,所有產生的消息都回被放入或是插入隊列中。系統會在隊列中取出每一條消息,根據消息的接收句柄而將該消息發送給擁有該窗口的程序的消息循環。每一個運行的程序都有自己的消息循環,在循環中得到屬於自己的消息並根據接收窗口的句柄調用相應的窗口過程。而在沒有消息時消息循環就將控制權交給系統所以Windows可以同時進行多個任務。
下面的偽代碼演示了消息循環的用法:
while(1) {
id=getMessage(...); i
f(id == quit) break;
translateMessage(...);
}
當該程序沒有消息通知時getMessage就不會返回,也就不會占用系統的CPU時間。