實(shí)戰(zhàn)必備!MFC 消息類(lèi)型
1、命令消息(WM_COMMAND
所有派生自?CCmdTarget?的類(lèi)都有資格接受WM_COMMAND。
2、Window消息(WM_xxx)
所有派生自?CWnd?的類(lèi)都有資格接受?WM_xxx。
3、控件消息(WM_NOTIFY)
控件向其父窗口通知消息。
消息處理
1、WM_xxx?消息處理
窗口類(lèi)(自身)處理→基類(lèi)處理→CWnd∷DefWindowProc()處理;
其所對(duì)應(yīng)的宏一般為在消息?WM_?前面加上?ON_。
2、命令消息處理
命令消息來(lái)自命令用戶(hù)接口對(duì)象(菜單、加速鍵或工具欄按鈕)發(fā)出的WM_COMMAND消息;
㈠、WM_COMMAND消息
其所包含的類(lèi)型和對(duì)應(yīng)的宏如下:
①、ON_COMMAND(ID,pfn)
標(biāo)準(zhǔn)的命令消息;
②、ON_COMMAND_EX(ID,pfn)
多個(gè)命令?ID?提供相同的處理,每個(gè)ID都要單獨(dú)使用一個(gè)ON_COMMAND_EX,ID可以不連續(xù)、多個(gè)對(duì)象對(duì)同一個(gè)命令?ID?的處理;
其函數(shù)的原型如下:
afx_msg?BOOL?pfn(UINT?nID)
說(shuō)明:
當(dāng)返回?TRUE?時(shí)表示已經(jīng)處理,不用在消息處理鏈中繼續(xù)處理該命令;為?FALSE?時(shí)表示繼續(xù)在消息處理鏈中處理該命令。
注意:
其一:在多對(duì)象處理中一定要使用該宏;
其二:pfn(UINT?nID)(消息處理函數(shù))返回值將其類(lèi)型void改成BOOL,而且必須為FALSE;
其三:多個(gè)對(duì)象的處理是由高層向低層的過(guò)程:即視圖類(lèi)→主框架窗口類(lèi)→應(yīng)用程序類(lèi);
③、ON_COMMAND_RANGE(nID,nLastID,pfn)
多個(gè)命令?ID?提供相同的處理、多個(gè)對(duì)象對(duì)同一個(gè)命令?ID?的處理;?
注意:
其一:確保nID、nLastID的值在?Resource.h?中是連續(xù)的。
其二:一般在函數(shù)?pfn(UINT?nID)?中加入?yún)?shù),用來(lái)確定那一個(gè)按鈕點(diǎn)擊。
㈡、CN_UPDATE_COMMAND_UI消息
當(dāng)菜單項(xiàng)、工具欄按鈕等[命令用戶(hù)接口對(duì)象]要更新其狀態(tài)時(shí)所對(duì)應(yīng)的消息,它所包含的類(lèi)型和對(duì)應(yīng)的宏如下:
①、ON_UPDATE_COMMAND_UI(ID,pfn)
其中函數(shù)的原型如下:
afx_msg?void?pfn(CCmdUI*?pCmdUI)
②、ON_UPDATE_COMMAND_UI_RANGE(nID,nLastID,pfn)
該函數(shù)可以處理一組[命令用戶(hù)接口對(duì)象]的外觀;
其中函數(shù)的原型如下:
afx_msg?void?pfn(CCmdUI*?pCmdUI)
重要:
CCmdUI?中的?m_nID?成員表示不同的?ID,因此可以利用它來(lái)進(jìn)行區(qū)別處理。
3、控件的通知消息
從控件和子窗口發(fā)送到父窗口的WM_COMMAND通知消息(即在發(fā)送命令消息中加入控件的通知碼)。
注意:在?Window9x?新控件中不再傳送WM_COMMAND通知消息,而是發(fā)送?WM_NOTIFY?消息,但為了兼容,舊有的控件還是傳送WM_COMMAND消息。
例如:
CEdit控件向父窗口發(fā)送?EN_CHANGE?通知代碼的WM_COMMAND消息。
注意:框架像傳送其它?WM_?消息一樣傳送通知消息,但有一個(gè)例外,即由?[按鈕]?控件發(fā)送的?BN_CLICKED?通知消息,被作為命令消息特別處理。
㈠、WM_COMMAND?其所對(duì)應(yīng)的宏如下:
①、ON_CONTROL(通知碼,?nID,fn)
②、ON_CONTROL_RANGE(通知碼,?nFirstID,nEndID,fn)
注意:
這兩個(gè)宏的應(yīng)用和?ON_COMMAND、ON_COMMAND_RANGE相同,所不同的是在宏前面加入[通知碼]。
注意:可以根據(jù)不同的控件的[通知碼]派生出特定的宏,其所派生的宏一般為在?[通知碼]?前面加上?ON_。
㈡、WM_NOTIFY?其所對(duì)應(yīng)的宏如下:
①、ON_NOTIFY(通知碼,?nID,fn)
其中函數(shù)的原型如下:
afx_msg?void?fn(NMHDR*?pNotifyStruct,LRESULT*?result)
其中結(jié)構(gòu):
typedef?struct?tagNMHDR?{?
HWND?hwndFrom;?//發(fā)送通知消息的控件的句柄;
UINT?idFrom;?//發(fā)送通知消息的控件的?ID;
UINT?code;?//通知碼;
}?NMHDR;?
②、ON_NOTIFY_EX(通知碼,?nID,fn)
表示一個(gè)消息在多個(gè)對(duì)象的成員函數(shù)中進(jìn)行處理。
其中函數(shù)的原型如下:
afx_msg?BOOL?fn(UINT?nID,NMHDR*?pNotifyStruct,LRESULT*?result)
說(shuō)明:
它必須返回?BOOL?類(lèi)型的數(shù)值,其意義和?ON_COMMAND_EX?相同。
③、ON_NOTIFY_RANGE(通知碼,?nFirstID,nEnd,fn)
表示多個(gè)控件的通知消息在同一個(gè)函數(shù)中進(jìn)行處理。
其中函數(shù)的原型如下:
afx_msg?void?fn(UINT?nID,NMHDR*?pNotifyStruct,LRESULT*?result)
說(shuō)明:
其意義和ON_COMMAND_RANGE相同。
4、反射消息處理
父窗口在處理控件窗口的通知消息WM_CTLCOLOR、WM_COMMAND、WM_NOTIFY時(shí),會(huì)把該消息轉(zhuǎn)化為反射消息,并轉(zhuǎn)交給控件子窗口處理,只有在控件子窗體不處理該消息時(shí),父窗口才有機(jī)會(huì)處理。
注意:在類(lèi)的屬性對(duì)話(huà)框中的消息頁(yè)面可查反射消息(前面有"="標(biāo)志)
①、WM_CTLCOLOR_REFLECT反射消息
其所對(duì)應(yīng)的宏如下:
ON_WM_CTLCOLOR_REFLECT()
反射消息函數(shù)的原型:
HBRUSH?class_name∷CtlColor(CDC*?pDC,UINT?nCtlColor)
{
return?NULL;
}
該函數(shù)用來(lái)重置控件的顔色;注意:必須?return?CBrush才有效。
5、自定義的窗口消息
自定義窗口消息的消息標(biāo)志都大于WM_USER(至少是WM_USER+100,因?yàn)樵S多控件都使用這一范圍的WM_USER消息)
使用自定義的消息分為二步:
①、在?Resource.h?中定義消息標(biāo)記
#define?WM_MYMSG?(WM_USER+1000)
②、在消息映射表中加入消息映射宏
BEGIN_MESSAGE_MAP()
ON_MESSAGE(WM_MYMSG,fn)
END_MESSAGE_MAP()
說(shuō)明:
其對(duì)應(yīng)的宏為?ON_MESSAGE(),其成員函數(shù)的原型為:
afx_msg?LRESULT?fn(WPARAM,LPARAM)
6、登記消息
①、在系統(tǒng)中注冊(cè)并獲取一個(gè)登記消息的消息標(biāo)記
UINT?RegisterWindowMessage(LPCTSTR)
說(shuō)明:
通過(guò)?API?函數(shù)來(lái)注冊(cè)消息標(biāo)記,其中?LPCTSTR?為用戶(hù)的任意字符串。例如:
UINT?WM_MYMSG=RegisterWindowMessage("MYMSG");
其中?WM_MYMSG?是自定義無(wú)符號(hào)整型的消息標(biāo)記。
②、在消息映射表中加入消息映射宏
BEGIN_MESSAGE_MAP()
ON_REGISTERED_MESSAGE(WM_MYMSG,fn)
END_MESSAGE_MAP()
說(shuō)明:
其對(duì)應(yīng)的宏為?ON_REGISTERED_MESSAGE(),其成員函數(shù)的原型為:
afx_msg?LRESULT?fn(WPARAM,LPARAM)
注意:登記消息可以實(shí)現(xiàn)跨進(jìn)程的數(shù)據(jù)通訊。
7、線程消息
只有繼承自CWinThread類(lèi)才能允許處理線程消息。
①、定義線程的消息標(biāo)記
有兩種方法:
(1)、使用自定義的消息標(biāo)記,即:WM_USER;
(2)、使用登記的消息標(biāo)記,即:RegisterWindowMessage;
②、在CWinThread繼承類(lèi)的消息映射表中添加宏
ON_THREAD_MESSAGE(消息標(biāo)記,fn)?//自定義的消息;
ON_REGISTERED_THREAD_MESSAGE(消息標(biāo)記,fn)?//登記的?//消息
③、其函數(shù)的原型如下:
afx_msg?void?fn(WPARAM?wPARAM,LPARAM?lParam)
④、引發(fā)線程消息
線程消息的引發(fā)必須調(diào)用?CWinThread?類(lèi)的PostThreadMessage將消息投遞到線程消息隊(duì)列中。
注意:可以通過(guò)?AfxGetApp()?函數(shù)獲取一個(gè)全局的應(yīng)用對(duì)象。
PostThreadMessage(UINT,WPARAM,LPARAM)
8、WM_COPYDATA
操作系統(tǒng)維護(hù)一塊內(nèi)存來(lái)管理?WM_COPYDATA?消息,該消息主要用于跨進(jìn)程傳遞數(shù)據(jù),傳遞的數(shù)據(jù)量達(dá)到?232。
①、定義一個(gè)?COPYDATASTRUCT?數(shù)據(jù)結(jié)構(gòu)
typedef?struct?tagCOPYDATASTRUCT?{?
DWORD?dwData;?//自定義的特殊數(shù)據(jù);
DWORD?cbData;?//以字節(jié)為單位的?lpData?的大小;
PVOID?lpData;?//傳送的數(shù)據(jù)內(nèi)存塊的指針;
}?COPYDATASTRUCT;?
②、其所對(duì)應(yīng)的宏
ON_WM_COPYDATA()
③、其所對(duì)應(yīng)的函數(shù)的原型
afx_msg?BOOL?OnCopyData(CWnd*,COPYDATASTRUCT*)
說(shuō)明:
CWnd*:發(fā)送該消息的窗口的指針;
9、投遞和發(fā)送消息
通過(guò)向一個(gè)窗體投遞或發(fā)送消息,可以間接地驅(qū)動(dòng)窗體的消息過(guò)程。
投遞(PostMessage):將消息放到線程的消息隊(duì)列中,然后不等線程處理該消息就直接返回到調(diào)用方。
發(fā)送(SendMessage):當(dāng)一個(gè)線程向目標(biāo)線程發(fā)送消息時(shí),該線程要一直等待,直到目標(biāo)線程處理了該消息為止。
①、投遞消息
BOOL?CWnd∷PostMessage(UINT,WPARAM=0,LPARAM=0)
說(shuō)明:
CWnd:目標(biāo)窗口;
該函數(shù)將一條消息放入到應(yīng)用程序的消息隊(duì)列,然后不等窗口處理就直接返回。
②、發(fā)送消息
LRESULT?CWnd∷SendMessage(UINT,WPARAM=0,LPARAM=0)
說(shuō)明:
CWnd:目標(biāo)窗口;
該函數(shù)將一條消息放入到應(yīng)用程序的消息隊(duì)列,等待窗口處理后才返回。
為了避免線程陷入永久等待狀態(tài),可以用SendMessageTimeout代替SendMessage:
LRESULT?SendMessageTimeout(HWND,UINT,WPARAM,LPARAM,UINT,UINT,PDWORD_PTR)
說(shuō)明:
HWND:窗口句柄;
UINT:消息發(fā)送的選項(xiàng),為SMTO_BLOCK時(shí),可以防止線程無(wú)?限等待,即根據(jù)一定的超時(shí)值返回。
UINT:超時(shí),以毫秒為單位;
PDWORD_PTR:返回值;
注意:CWnd沒(méi)有對(duì)該函數(shù)的包裝。
③、投遞和發(fā)送消息
BOOL?CWnd∷SendNotifyMessage(UINT,WPARAM,LPARAM)
說(shuō)明:
CWnd:目標(biāo)窗口;
該消息具有SendMessage和PostMessage兩種功能:
當(dāng)目標(biāo)窗口和發(fā)送窗口為同一個(gè)線程時(shí),則相當(dāng)于SendMessage的功能;否則當(dāng)不為同一個(gè)線程時(shí),則為PostMessage的功能。
6-1、投遞和發(fā)送?WM_XXX?消息
在發(fā)送標(biāo)準(zhǔn)的?WINDOW?消息時(shí),只要將該消息的?ID、wParam、lParam參數(shù)放在?SendMessage()和PostMessage()函數(shù)的相應(yīng)位置即可。
6-2、投遞和發(fā)送命令消息和控件的通知消息
在投遞和發(fā)送命令消息時(shí),消息的?ID為?WM_COMMADN,而對(duì)于不同的菜單項(xiàng)、加速鍵、控件則wParam、lParam的取值不同。
wParam分成低、高兩部分,
低部分為菜單項(xiàng)、加速鍵、控件的ID。
高部分則: 菜單項(xiàng):0;加速鍵:1;控件:通知碼
lParam:當(dāng)控件時(shí)是控件的句柄,否則為?NULL。
對(duì)于wParam參數(shù)可以采用自定義宏:
WPARAM?MAKEWPARAM(WORD?wLow,WORD?wHigh)
SendMessage (?WM_COMMAND, MAKEWPARAM (GetDlgCtrlID (), BN_CLICKED),?(LPARAM) m_hWnd);
6-3、投遞和發(fā)送自定義的窗口消息
在投遞和發(fā)送自定義的窗口消息時(shí),參數(shù)?wParam、lParam?沒(méi)有特別的涵義,只和普通函數(shù)的形參一樣進(jìn)行數(shù)據(jù)的傳遞。
注意:
PostMessage?和?SendMessage?是不同的,前者投遞后就返回,而后者必須等到消息處理后再返回;所以在參數(shù)是?[局部]?或?[臨時(shí)]時(shí),使用PostMessage函數(shù)會(huì)引發(fā)錯(cuò)誤(除非參數(shù)使用?指針,則可避免錯(cuò)誤),而必須使用SendMessage函數(shù)。
6-4、投遞和發(fā)送注冊(cè)的窗口消息
和?6-3?基本一樣,但它要特別注意的問(wèn)題是:在跨進(jìn)程的處理消息時(shí),如果將消息PostMessage、SendMessage到某個(gè)進(jìn)程?A,則必須在進(jìn)程?B?中獲取進(jìn)程?A?的窗口類(lèi)名,并通過(guò)窗口類(lèi)名獲取窗口的指針,最后再根據(jù)指針調(diào)用?PostMessage、SendMessage?函數(shù)。
注意:在獲取窗口的指針時(shí),可以根據(jù)窗口類(lèi)名或窗口的標(biāo)題。
6-5、投遞和發(fā)送WM_COPYDATA消息
SendMessage(消息標(biāo)記,WPARAM,LPARAM)
其中:
消息標(biāo)記:WM_COPYDATA;
WPARAM:發(fā)送該消息的窗口句柄;
LPARAM:COPYDATASTRUCT結(jié)構(gòu)的指針,先通過(guò)(LPVOID)進(jìn)行轉(zhuǎn)換,再通過(guò)(LPARAM)進(jìn)行轉(zhuǎn)換,如下形式:
(LPARAM)(LPVOID)&cds