?? mfc教程_ 消息映射的實(shí)現(xiàn).htm
字號(hào):
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<!-- saved from url=(0041)http://www.vczx.com/tutorial/mfc/mfc4.php -->
<HTML><HEAD><TITLE>MFC教程_ 消息映射的實(shí)現(xiàn)</TITLE>
<META http-equiv=Content-Type content="text/html; charset=gb2312">
<META content="MSHTML 6.00.2900.3354" name=GENERATOR></HEAD>
<BODY bgColor=#ffffff>
<OL start=4>
<P align=justify>
<LI><A name=_Toc445888998></A><A name=_Toc445782401></A><A
name=_Toc452640897></A><A name=_Toc457298962></A><B>消息映射的實(shí)現(xiàn)</B>
<P></P>
<OL>
<P align=justify>
<LI><B><A name=_Toc445888999></A><A name=_Toc445782402></A><A
name=_Toc452640898></A><A name=_Toc457298963></A>Windows消息概述</B>
<P></P>
<P
align=justify>Windows應(yīng)用程序的輸入由Windows系統(tǒng)以消息的形式發(fā)送給應(yīng)用程序的窗口。這些窗口通過(guò)窗口過(guò)程來(lái)接收和處理消息,然后把控制返還給Windows。</P>
<OL>
<P align=justify>
<LI><A name=_Toc457298964></A><B>消息的分類(lèi)</B>
<P></P></LI></OL></LI></OL></LI></OL>
<OL>
<P align=justify>
<LI>隊(duì)列消息和非隊(duì)列消息
<P></P>
<P
align=justify>從消息的發(fā)送途徑上看,消息分兩種:隊(duì)列消息和非隊(duì)列消息。隊(duì)列消息送到系統(tǒng)消息隊(duì)列,然后到線程消息隊(duì)列;非隊(duì)列消息直接送給目的窗口過(guò)程。</P>
<P align=justify>這里,對(duì)消息隊(duì)列闡述如下:</P>
<P align=justify>Windows維護(hù)一個(gè)系統(tǒng)消息隊(duì)列(System message
queue),每個(gè)GUI線程有一個(gè)線程消息隊(duì)列(Thread message queue)。</P>
<P
align=justify>鼠標(biāo)、鍵盤(pán)事件由鼠標(biāo)或鍵盤(pán)驅(qū)動(dòng)程序轉(zhuǎn)換成輸入消息并把消息放進(jìn)系統(tǒng)消息隊(duì)列,例如WM_MOUSEMOVE、WM_LBUTTONUP、WM_KEYDOWN、WM_CHAR等等。Windows每次從系統(tǒng)消息隊(duì)列移走一個(gè)消息,確定它是送給哪個(gè)窗口的和這個(gè)窗口是由哪個(gè)線程創(chuàng)建的,然后,把它放進(jìn)窗口創(chuàng)建線程的線程消息隊(duì)列。線程消息隊(duì)列接收送給該線程所創(chuàng)建窗口的消息。線程從消息隊(duì)列取出消息,通過(guò)Windows把它送給適當(dāng)?shù)拇翱谶^(guò)程來(lái)處理。</P>
<P align=justify>除了鍵盤(pán)、鼠標(biāo)消息以外,隊(duì)列消息還有WM_PAINT、WM_TIMER和WM_QUIT。</P>
<P align=justify>這些隊(duì)列消息以外的絕大多數(shù)消息是非隊(duì)列消息。</P>
<P align=justify></P>
<LI>系統(tǒng)消息和應(yīng)用程序消息
<P></P></LI></OL>
<P align=justify>從消息的來(lái)源來(lái)看,可以分為:系統(tǒng)定義的消息和應(yīng)用程序定義的消息。</P>
<P
align=justify>系統(tǒng)消息ID的范圍是從0到WM_USER-1,或0X80000到0XBFFFF;應(yīng)用程序消息從WM_USER(0X0400)到0X7FFF,或0XC000到0XFFFF;WM_USER到0X7FFF范圍的消息由應(yīng)用程序自己使用;0XC000到0XFFFF范圍的消息用來(lái)和其他應(yīng)用程序通信,為了ID的唯一性,使用::RegisterWindowMessage來(lái)得到該范圍的消息ID。</P>
<OL>
<OL>
<OL>
<P align=justify>
<LI><A name=_Toc457298965></A><B>消息結(jié)構(gòu)和消息處理</B>
<P></P></LI></OL></OL></OL>
<OL>
<P align=justify>
<LI><A name=_Toc445889001></A><A name=_Toc445782404></A>消息的結(jié)構(gòu)
<P></P>
<P
align=justify>為了從消息隊(duì)列獲取消息信息,需要使用MSG結(jié)構(gòu)。例如,::GetMessage函數(shù)(從消息隊(duì)列得到消息并從隊(duì)列中移走)和::PeekMessage函數(shù)(從消息隊(duì)列得到消息但是可以不移走)都使用了該結(jié)構(gòu)來(lái)保存獲得的消息信息。</P>
<P align=justify>MSG結(jié)構(gòu)的定義如下:</P>
<P align=justify>typedef struct tagMSG { // msg </P>
<P align=justify>HWND hwnd; </P>
<P align=justify>UINT message; </P>
<P align=justify>WPARAM wParam; </P>
<P align=justify>LPARAM lParam; </P>
<P align=justify>DWORD time; </P>
<P align=justify>POINT pt; </P>
<P align=justify>} MSG; </P>
<P align=justify>該結(jié)構(gòu)包括了六個(gè)成員,用來(lái)描述消息的有關(guān)屬性:</P>
<P align=justify>接收消息的窗口句柄、消息標(biāo)識(shí)(ID)、第一個(gè)消息參數(shù)、第二個(gè)消息參數(shù)、消息產(chǎn)生的時(shí)間、消息產(chǎn)生時(shí)鼠標(biāo)的位置。</P>
<P align=justify></P>
<LI><A name=_Toc445889002><A name=_Toc445782405>應(yīng)用程序通過(guò)窗口過(guò)程來(lái)處理消息</A></A>
<P></P>
<P align=justify>如前所述,每個(gè)“窗口類(lèi)”都要登記一個(gè)如下形式的窗口過(guò)程:</P>
<P align=justify>LRESULT CALLBACK MainWndProc (</P>
<P align=justify>HWND hwnd,// 窗口句柄</P>
<P align=justify>UINT msg,// 消息標(biāo)識(shí)</P>
<P align=justify>WPARAM wParam,//消息參數(shù)1</P>
<P align=justify>LPARAM lParam//消息參數(shù)2</P>
<P align=justify>)</P>
<P
align=justify>應(yīng)用程序通過(guò)窗口過(guò)程來(lái)處理消息:非隊(duì)列消息由Windows直接送給目的窗口的窗口過(guò)程,隊(duì)列消息由::DispatchMessage等派發(fā)給目的窗口的窗口過(guò)程。窗口過(guò)程被調(diào)用時(shí),接受四個(gè)參數(shù):</P>
<P align=justify>a window handle(窗口句柄);</P>
<P align=justify>a message identifier(消息標(biāo)識(shí));</P>
<P align=justify>two 32-bit values called message parameters(兩個(gè)32位的消息參數(shù));</P>
<P
align=justify>需要的話,窗口過(guò)程用::GetMessageTime獲取消息產(chǎn)生的時(shí)間,用::GetMessagePos獲取消息產(chǎn)生時(shí)鼠標(biāo)光標(biāo)所在的位置。</P>
<P align=justify>在窗口過(guò)程里,用switch/case分支處理語(yǔ)句來(lái)識(shí)別和處理消息。</P>
<P align=justify></P>
<LI><A name=_Toc445889003><A name=_Toc445782406>應(yīng)用程序通過(guò)消息循環(huán)來(lái)獲得對(duì)消息的處理</A></A>
<P></P>
<P align=justify>每個(gè)GDI應(yīng)用程序在主窗口創(chuàng)建之后,都會(huì)進(jìn)入消息循環(huán),接受用戶(hù)輸入、解釋和處理消息。</P>
<P align=justify>消息循環(huán)的結(jié)構(gòu)如下:</P>
<P align=justify>while (GetMessage(&msg, (HWND) NULL, 0, 0))
{//從消息隊(duì)列得到消息</P>
<P align=justify>if (hwndDlgModeless == (HWND) NULL || </P>
<P align=justify>!IsDialogMessage(hwndDlgModeless, &msg) && </P>
<P align=justify>!TranslateAccelerator(hwndMain, haccel, &msg)) { </P>
<P align=justify>TranslateMessage(&msg); </P>
<P align=justify>DispatchMessage(&msg); //發(fā)送消息</P>
<P align=justify>} </P>
<P align=justify>}</P>
<P>消息循環(huán)從消息隊(duì)列中得到消息,如果不是快捷鍵消息或者對(duì)話框消息,就進(jìn)行消息轉(zhuǎn)換和派發(fā),讓目的窗口的窗口過(guò)程來(lái)處理。</P>
<P>當(dāng)?shù)玫较M_QUIT,或者::GetMessage出錯(cuò)時(shí),退出消息循環(huán)。</P>
<P align=justify></P>
<LI>MFC消息處理
<P></P></LI></OL>
<P
align=justify>使用MFC框架編程時(shí),消息發(fā)送和處理的本質(zhì)也如上所述。但是,有一點(diǎn)需要強(qiáng)調(diào)的是,所有的MFC窗口都使用同一窗口過(guò)程,程序員不必去設(shè)計(jì)和實(shí)現(xiàn)自己的窗口過(guò)程,而是通過(guò)MFC提供的一套消息映射機(jī)制來(lái)處理消息。因此,MFC簡(jiǎn)化了程序員編程時(shí)處理消息的復(fù)雜性。</P>
<P
align=justify>所謂消息映射,簡(jiǎn)單地講,就是讓程序員指定要某個(gè)MFC類(lèi)(有消息處理能力的類(lèi))處理某個(gè)消息。MFC提供了工具ClassWizard來(lái)幫助實(shí)現(xiàn)消息映射,在處理消息的類(lèi)中添加一些有關(guān)消息映射的內(nèi)容和處理消息的成員函數(shù)。程序員將完成消息處理函數(shù),實(shí)現(xiàn)所希望的消息處理能力。</P>
<P>如果派生類(lèi)要覆蓋基類(lèi)的消息處理函數(shù),就用ClassWizard在派生類(lèi)中添加一個(gè)消息映射條目,用同樣的原型定義一個(gè)函數(shù),然后實(shí)現(xiàn)該函數(shù)。這個(gè)函數(shù)覆蓋派生類(lèi)的任何基類(lèi)的同名處理函數(shù)。</P>
<P align=justify></P>
<P
align=justify>下面幾節(jié)將分析MFC的消息機(jī)制的實(shí)現(xiàn)原理和消息處理的過(guò)程。為此,首先要分析ClassWizard實(shí)現(xiàn)消息映射的內(nèi)幕,然后討論MFC的窗口過(guò)程,分析MFC窗口過(guò)程是如何實(shí)現(xiàn)消息處理的。</P>
<OL>
<OL>
<P align=justify>
<LI><A name=_Toc445889004></A><A name=_Toc445782407></A><A
name=_Toc452640900></A><A name=_Toc457298966></A><B>消息映射的定義和實(shí)現(xiàn)</B>
<P></P>
<OL>
<P align=justify>
<LI><B><A name=_Toc445889005></A><A name=_Toc445782408></A><A
name=_Toc452640901></A><A name=_Toc457298967></A>MFC處理的三類(lèi)消息</B>
<P></P></LI></OL></LI></OL></OL>
<P align=justify>根據(jù)處理函數(shù)和處理過(guò)程的不同,MFC主要處理三類(lèi)消息:</P>
<UL>
<P align=justify>
<LI>Windows消息,前綴以“WM_”打頭,WM_COMMAND例外。Windows消息直接送給MFC窗口過(guò)程處理,窗口過(guò)程調(diào)用對(duì)應(yīng)的消息處理函數(shù)。一般,由窗口對(duì)象來(lái)處理這類(lèi)消息,也就是說(shuō),這類(lèi)消息處理函數(shù)一般是MFC窗口類(lèi)的成員函數(shù)。
<P></P>
<P align=justify></P>
<LI>控制通知消息,是控制子窗口送給父窗口的WM_COMMAND通知消息。窗口過(guò)程調(diào)用對(duì)應(yīng)的消息處理函數(shù)。一般,由窗口對(duì)象來(lái)處理這類(lèi)消息,也就是說(shuō),這類(lèi)消息處理函數(shù)一般是MFC窗口類(lèi)的成員函數(shù)。
<P></P></LI></UL>
<P
align=justify>需要指出的是,Win32使用新的WM_NOFITY來(lái)處理復(fù)雜的通知消息。WM_COMMAND類(lèi)型的通知消息僅僅能傳遞一個(gè)控制窗口句柄(lparam)、控制窗ID和通知代碼(wparam)。WM_NOTIFY能傳遞任意復(fù)雜的信息。</P>
<UL>
<P align=justify>
<LI>命令消息,這是來(lái)自菜單、工具條按鈕、加速鍵等用戶(hù)接口對(duì)象的WM_COMMAND通知消息,屬于應(yīng)用程序自己定義的消息。通過(guò)消息映射機(jī)制,MFC框架把命令按一定的路徑分發(fā)給多種類(lèi)型的對(duì)象(具備消息處理能力)處理,如文檔、窗口、應(yīng)用程序、文檔模板等對(duì)象。能處理消息映射的類(lèi)必須從CCmdTarget類(lèi)派生。
<P></P></LI></UL>
<P align=justify>在討論了消息的分類(lèi)之后,應(yīng)該是討論各類(lèi)消息如何處理的時(shí)候了。但是,要知道怎么處理消息,首先要知道如何映射消息。</P>
<OL>
<OL>
<OL>
<P align=justify>
<LI><A name=_Toc445889006></A><A name=_Toc445782409></A><A
name=_Toc452640902></A><A name=_Toc457298968></A><B>MFC消息映射的實(shí)現(xiàn)方法</B>
<P></P>
<P
align=justify>MFC使用ClassWizard幫助實(shí)現(xiàn)消息映射,它在源碼中添加一些消息映射的內(nèi)容,并聲明和實(shí)現(xiàn)消息處理函數(shù)。現(xiàn)在來(lái)分析這些被添加的內(nèi)容。</P>
<P
align=justify>在類(lèi)的定義(頭文件)里,它增加了消息處理函數(shù)聲明,并添加一行聲明消息映射的宏DECLARE_MESSAGE_MAP。</P>
<P
align=justify>在類(lèi)的實(shí)現(xiàn)(實(shí)現(xiàn)文件)里,實(shí)現(xiàn)消息處理函數(shù),并使用IMPLEMENT_MESSAGE_MAP宏實(shí)現(xiàn)消息映射。一般情況下,這些聲明和實(shí)現(xiàn)是由MFC的ClassWizard自動(dòng)來(lái)維護(hù)的。看一個(gè)例子:</P>
<P align=justify>在AppWizard產(chǎn)生的應(yīng)用程序類(lèi)的源碼中,應(yīng)用程序類(lèi)的定義(頭文件)包含了類(lèi)似如下的代碼:</P>
<P align=justify>//{{AFX_MSG(CTttApp)</P>
<P align=justify>afx_msg void OnAppAbout();</P>
<P align=justify>//}}AFX_MSG</P>
<P align=justify>DECLARE_MESSAGE_MAP()</P>
<P align=justify></P>
<P align=justify>應(yīng)用程序類(lèi)的實(shí)現(xiàn)文件中包含了類(lèi)似如下的代碼:</P>
<P align=justify>BEGIN_MESSAGE_MAP(CTApp, CWinApp)</P>
<P align=justify>//{{AFX_MSG_MAP(CTttApp)</P>
<P align=justify>ON_COMMAND(ID_APP_ABOUT, OnAppAbout)</P>
<P align=justify>//}}AFX_MSG_MAP</P>
<P align=justify>END_MESSAGE_MAP()</P>
<P align=justify></P>
<P
align=justify>頭文件里是消息映射和消息處理函數(shù)的聲明,實(shí)現(xiàn)文件里是消息映射的實(shí)現(xiàn)和消息處理函數(shù)的實(shí)現(xiàn)。它表示讓?xiě)?yīng)用程序?qū)ο筇幚砻钕D_APP_ABOUT,消息處理函數(shù)是OnAppAbout。</P>
<P align=justify>為什么這樣做之后就完成了一個(gè)消息映射?這些聲明和實(shí)現(xiàn)到底作了些什么呢?接著,將討論這些問(wèn)題。</P>
<P align=justify></P>
<LI><A name=_Toc445889007></A><A name=_Toc445782410></A><A
name=_Toc452640903></A><A name=_Toc457298969></A><B>在聲明與實(shí)現(xiàn)的內(nèi)部</B>
<P></P></LI></OL></OL></OL>
<OL>
<P align=justify>
?? 快捷鍵說(shuō)明
復(fù)制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號(hào)
Ctrl + =
減小字號(hào)
Ctrl + -