日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程资源 > 编程问答 >内容正文

编程问答

MFC六大核心机制之五、六:消息映射和命令传递

發(fā)布時(shí)間:2024/4/11 编程问答 33 豆豆
生活随笔 收集整理的這篇文章主要介紹了 MFC六大核心机制之五、六:消息映射和命令传递 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

作為C++程序員,我們總是希望自己程序的所有代碼都是自己寫(xiě)出來(lái)的,如果使用了其他的一些庫(kù),也總是千方百計(jì)想弄清楚其中的類(lèi)和函數(shù)的原理,否則就會(huì)感覺(jué)不踏實(shí)。所以,我們對(duì)于在進(jìn)行MFC視窗程序設(shè)計(jì)時(shí)經(jīng)常要用到的消息機(jī)制也不滿(mǎn)足于會(huì)使用,而是希望能理解個(gè)中道理。本文就為大家剖析MFC消息映射和命令傳遞的原理。

???????理解MFC消息機(jī)制的必要性

?????? 說(shuō)到消息,在MFC中,“最熟悉的神秘”可以說(shuō)是消息映射了,那是我們剛開(kāi)始接觸MFC時(shí)就要面對(duì)的東西。有過(guò)SDK編程經(jīng)驗(yàn)的朋友轉(zhuǎn)到MFC編程的時(shí)候,一下子覺(jué)得什么都變了樣。特別是窗口消息及對(duì)消息的處理跟以前相比,更是風(fēng)馬牛不相及的。如文檔不是窗口,是怎樣響應(yīng)命令消息的呢?

?????? 初次用MFC編程,我們只會(huì)用MFC ClassWizard為我們做大量的東西,最主要的是添加消息響應(yīng)。記憶中,如果是自已添加消息響應(yīng),我們應(yīng)何等的小心翼翼,對(duì)BEGIN_MESSAGE_MAP()……END_MESSAGE_MAP()更要奉若神靈。它就是一個(gè)魔盒子,把我們的咒語(yǔ)放入恰當(dāng)?shù)牡胤?#xff0c;就會(huì)發(fā)生神奇的力量,放錯(cuò)了,自己的程序就連“命”都沒(méi)有。

?????? 據(jù)說(shuō),知道得太多未必是好事。我也曾經(jīng)打算不去理解這神秘的區(qū)域,覺(jué)得編程的時(shí)候知道自己想做什么就行了。MFC外表上給我們提供了東西,直觀地說(shuō),不但給了我個(gè)一個(gè)程序的外殼,更給我們?cè)S多方便。微軟的出發(fā)點(diǎn)可能是希望達(dá)到“傻瓜編程”的結(jié)果,試想,誰(shuí)不會(huì)用ClassWizard?大家知道,Windows是基于消息的,有了ClassWizard,你又會(huì)添加類(lèi),又會(huì)添加消息,那么你所學(xué)的東西似乎學(xué)到頭了。于是許多程序員認(rèn)為“我們沒(méi)有必要走SDK的老路,直接用MFC編程,新的東西通常是簡(jiǎn)單、直觀、易學(xué)……”。

?????? 到你真正想用MFC編程的時(shí)候,你會(huì)發(fā)覺(jué)光會(huì)ClassWizard的你是多么的愚蠢。MFC不是一個(gè)普通的類(lèi)庫(kù),普通的類(lèi)庫(kù)我們完全可以不理解里面的細(xì)節(jié),只要知道這些類(lèi)庫(kù)能干什么,接口參數(shù)如何就萬(wàn)事大吉。如string類(lèi),操作順序是定義一個(gè)string對(duì)象,然后修改屬性,調(diào)用方法。但對(duì)于MFC,并不是在你的程序中寫(xiě)上一句“#include MFC.h”,然后就使用MFC類(lèi)庫(kù)的。

?????? MFC是一塊包著糖衣的牛骨頭。你很輕松地寫(xiě)出一個(gè)單文檔窗口,在窗口中間打印一句“I love MFC!”,然后,惡夢(mèng)開(kāi)始了……想逃避,打算永遠(yuǎn)不去理解MFC內(nèi)幕?門(mén)都沒(méi)有!在MFC這個(gè)黑暗神秘的洞中,即使你打算摸著石頭前行,也注定找不到出口。對(duì)著MFC這塊牛骨頭,微軟溫和、民主地告訴你“你當(dāng)然可以選擇不啃掉它,咳咳……但你必然會(huì)因此而餓死!”

???????MFC消息機(jī)制與SDK的不同

?????? 消息映射與命令傳遞體現(xiàn)了MFC與SDK的不同。在SDK編程中,沒(méi)有消息映射的概念,它有明確的回調(diào)函數(shù),通過(guò)一個(gè)switch語(yǔ)句去判斷收到了何種消息,然后對(duì)這個(gè)消息進(jìn)行處理。所以,在SDK編程中,會(huì)發(fā)送消息和在回調(diào)函數(shù)中處理消息就差不多可以寫(xiě)SDK程序了。

?????? 在MFC中,看上去發(fā)送消息和處理消息比SDK更簡(jiǎn)單、直接,但可惜不直觀。舉個(gè)簡(jiǎn)單的例子,如果我們想自定義一個(gè)消息,SDK是非常簡(jiǎn)單直觀的,用一條語(yǔ)句:SendMessage(hwnd,message/*一個(gè)大于或等于WM_USER的數(shù)字*/,wparam,lparam),之后就可以在回調(diào)函數(shù)中處理了。但MFC就不同了,因?yàn)槟阃ǔ2恢苯尤ジ膶?xiě)窗口的回調(diào)函數(shù),所以只能亦步亦趨對(duì)照原來(lái)的MFC代碼,把消息放到恰當(dāng)?shù)牡胤健_@確實(shí)是一樣很痛苦的勞動(dòng)。

???????要了解MFC消息映射原理并不是一件輕松的事情。我們可以逆向思維,想象一下消息映射為我們做了什么工作。MFC在自動(dòng)化給我們提供了很大的方便,比如,所有的MFC窗口都使用同一窗口過(guò)程,即所有的MFC窗口都有一個(gè)默認(rèn)的窗口過(guò)程。不像在SDK編程中,要為每個(gè)窗口類(lèi)寫(xiě)一個(gè)窗口過(guò)程。

???????MFC消息映射原理

?????? 對(duì)于消息映射,最直截了當(dāng)?shù)夭孪胧?#xff1a;消息映射就是用一個(gè)數(shù)據(jù)結(jié)構(gòu)把“消息”與“響應(yīng)消息函數(shù)名”串聯(lián)起來(lái)。這樣,當(dāng)窗口感知消息發(fā)生時(shí),就對(duì)結(jié)構(gòu)查找,找到相應(yīng)的消息響應(yīng)函數(shù)執(zhí)行。其實(shí)這個(gè)想法也不能簡(jiǎn)單地實(shí)現(xiàn):我們每個(gè)不同的MFC窗口類(lèi),對(duì)同一種消息,有不同的響應(yīng)方式。即是說(shuō),對(duì)同一種消息,不同的MFC窗口會(huì)有不同的消息響應(yīng)函數(shù)。

???????這時(shí),大家又想了一個(gè)可行的方法。我們?cè)O(shè)計(jì)窗口基類(lèi)(CWnd)時(shí),我們讓它對(duì)每種不同的消息都來(lái)一個(gè)消息響應(yīng),并把這個(gè)消息響應(yīng)函數(shù)定義為虛函數(shù)。這樣,從CWnd派生的窗口類(lèi)對(duì)所有消息都有了一個(gè)空響應(yīng),我們要響應(yīng)一個(gè)特定的消息就重載這個(gè)消息響應(yīng)函數(shù)就可以了。但這樣做的結(jié)果,一個(gè)幾乎什么也不做的CWnd類(lèi)要有幾百個(gè)“多余”的函數(shù),哪怕這些消息響應(yīng)函數(shù)都為純虛函數(shù),每個(gè)CWnd對(duì)象也要背負(fù)著一個(gè)巨大的虛擬表,這也是得不償失的。

?????? 許多朋友在學(xué)習(xí)消息映射時(shí)苦無(wú)突破,其原因是一開(kāi)始就認(rèn)為MFC的消息映射的目的是為了替代SDK窗口過(guò)程的編寫(xiě)——這本來(lái)沒(méi)有理解錯(cuò)。但他們還有多一層的理解,認(rèn)為既然是替代“舊”的東西,那么MFC消息映身應(yīng)該是更高層次的抽象、更簡(jiǎn)單、更容易認(rèn)識(shí)。但結(jié)果是,如果我們不通過(guò)ClassWizard工具,手動(dòng)添加消息是相當(dāng)迷茫的一件事。

?????? 所以,我們?cè)趯W(xué)習(xí)MFC消息映射時(shí),首先要弄清楚:消息映射的目的,不是為是更加快捷地向窗口過(guò)程添加代碼,而是一種機(jī)制的改變。如果不想改變窗口過(guò)程函數(shù),那么應(yīng)該在哪里進(jìn)行消息響應(yīng)呢?許多朋友一知半解地認(rèn)為:我們可以用HOOK技術(shù),搶在消息隊(duì)列前把消息抓取,把消息響應(yīng)提到窗口過(guò)程的外面。再者,不同的窗口,會(huì)有不同的感興趣的消息,所以每個(gè)MFC窗口都應(yīng)該有一個(gè)表把感興趣的消息和相應(yīng)消息響應(yīng)函數(shù)連系起來(lái)。然后得出——消息映射機(jī)制執(zhí)行步驟是:當(dāng)消息發(fā)生,我們用HOOK技術(shù)把本來(lái)要發(fā)送到窗口過(guò)程的消息抓獲,然后對(duì)照一下MFC窗口的消息映射表,如果是表里面有的消息,就執(zhí)行其對(duì)應(yīng)的函數(shù)。

?????? 當(dāng)然,用HOOK技術(shù),我們理論上可以在不改變窗口過(guò)程函數(shù)的情況下,可以完成消息響應(yīng)。MFC確實(shí)是這樣做的,但實(shí)際操作起來(lái)可能跟你的想象差別很大。

?????? 現(xiàn)在我們來(lái)編寫(xiě)消息映射表,我們先定義一個(gè)結(jié)構(gòu),這個(gè)結(jié)構(gòu)至少有兩個(gè)項(xiàng):一是消息ID,二是響應(yīng)該消息的函數(shù)。如下:

C++代碼
  • struct?AFX_MSGMAP_ENTRY ??
  • { ??
  • UINT?nMessage;???????????//感興趣的消息???
  • AFX_PMSG?pfn;??????????//響應(yīng)以上消息的函數(shù)指針???
  • }??
  • ?????? 當(dāng)然,只有兩個(gè)成員的結(jié)構(gòu)連接起來(lái)的消息映射表是不成熟的。Windows消息分為標(biāo)準(zhǔn)消息、控件消息和命令消息,每類(lèi)型的消息都是包含數(shù)百不同ID、不同意義、不同參數(shù)的消息。我們要準(zhǔn)確地判別發(fā)生了何種消息,必須再增加幾個(gè)成員。還有,對(duì)于AFX_PMSG pfn,實(shí)際上等于作以下聲明:

    ?????? void (CCmdTarget::*pfn)();??? // 提示:AFX_PMSG為類(lèi)型標(biāo)識(shí),具體聲明是:typedef void (AFX_MSG_CALL CCmdTarget::*AFX_PMSG)(void);

    ?????? pfn是一個(gè)不帶參數(shù)和返回值的CCmdTarget類(lèi)型函數(shù)指針,只能指向CCmdTarget類(lèi)中不帶參數(shù)和返回值的成員函數(shù),這樣pfn更為通用,但我們響應(yīng)消息的函數(shù)許多需要傳入?yún)?shù)的。為了解決這個(gè)矛盾,我們還要增加一個(gè)表示參數(shù)類(lèi)型的成員。當(dāng)然,還有其它……

    ?????? 最后,MFC我們消息映射表成員結(jié)構(gòu)如下定義:

    C++代碼
  • struct?AFX_MSGMAP_ENTRY ??
  • { ??
  • UINT?nMessage;???????????//Windows?消息ID???
  • UINT?nCode;????????????????//?控制消息的通知碼???
  • UINT?nID;???????????????????//命令消息ID范圍的起始值???
  • UINT?nLastID;?????????????//命令消息ID范圍的終點(diǎn)???
  • UINT?nSig;???????????????????//?消息的動(dòng)作標(biāo)識(shí)???
  • AFX_PMSG?pfn; ??
  • };??
  • ?????? 有了以上消息映射表成員結(jié)構(gòu),我們就可以定義一個(gè)AFX_MSGMAP_ENTRY類(lèi)型的數(shù)組,用來(lái)容納消息映射項(xiàng)。定義如下:

    ?????? AFX_MSGMAP_ENTRY _messageEntries[];

    ?????? 但這樣還不夠,每個(gè)AFX_MSGMAP_ENTRY數(shù)組,只能保存著當(dāng)前類(lèi)感興趣的消息,而這僅僅是我們想處理的消息中的一部分。對(duì)于一個(gè)MFC程序,一般有多個(gè)窗口類(lèi),里面都應(yīng)該有一個(gè)AFX_MSGMAP_ENTRY數(shù)組。

    ?????? 我們知道,MFC還有一個(gè)消息傳遞機(jī)制,可以把自己不處理的消息傳送給別的類(lèi)進(jìn)行處理。為了能查找各下MFC對(duì)象的消息映射表,我們還要增加一個(gè)結(jié)構(gòu),把所有的AFX_MSGMAP_ENTRY數(shù)組串聯(lián)起來(lái)。于是,我們定義了一個(gè)新結(jié)構(gòu)體:

    C++代碼
  • struct?AFX_MSGMAP ??
  • { ??
  • const?AFX_MSGMAP*?pBaseMap;????????????????????//指向別的類(lèi)的AFX_MSGMAP對(duì)象???
  • const?AFX_MSGMAP_ENTRY*?lpEntries;??????????//指向自身的消息表???
  • };??
  • ?????? 之后,在每個(gè)打算響應(yīng)消息的類(lèi)中聲明這樣一個(gè)變量:AFX_MSGMAP messageMap,讓其中的pBaseMap指向基類(lèi)或另一個(gè)類(lèi)的messageMap,那么將得到一個(gè)AFX_MSGMAP元素的單向鏈表。這樣,所有的消息映射信息形成了一張消息網(wǎng)。

    ?????? 當(dāng)然,僅有消息映射表還不夠,它只能把各個(gè)MFC對(duì)象的消息、參數(shù)與相應(yīng)的消息響應(yīng)函數(shù)連成一張網(wǎng)。為了方便查找,MFC在上面的類(lèi)中插入了兩個(gè)函數(shù)(其中theClass代表當(dāng)前類(lèi)):

    ???????一個(gè)是_GetBaseMessageMap(),用來(lái)得到基類(lèi)消息映射的函數(shù)。函數(shù)原型如下:

    C++代碼
  • const?AFX_MSGMAP*?PASCAL?theClass::_GetBaseMessageMap()?/ ??
  • {?return?&baseClass::messageMap;?}?/??
  • ????? 另一個(gè)是GetMessageMap() ,用來(lái)得到自身消息映射的函數(shù)。函數(shù)原型如下:

    C++代碼
  • const?AFX_MSGMAP*?theClass::GetMessageMap()?const?/ ??
  • {?return?&theClass::messageMap;?}?/??
  • ?????? 有了消息映射表之后,我們得討論到問(wèn)題的關(guān)鍵,那就是消息發(fā)生以后,其對(duì)應(yīng)的響應(yīng)函數(shù)如何被調(diào)用。大家知道,所有的MFC窗口,都有一個(gè)同樣的窗口過(guò)程——AfxWndProc(…)。在這里順便要提一下的是,看過(guò)MFC源代碼的朋友都得,從AfxWndProc函數(shù)進(jìn)去,會(huì)遇到一大堆曲折與迷團(tuán),因?yàn)閷?duì)于這個(gè)龐大的消息映射機(jī)制,MFC要做的事情很多,如優(yōu)化消息,增強(qiáng)兼容性等,這一大量的工作,有些甚至用匯編語(yǔ)言來(lái)完成,對(duì)此,我們很難深究它。所以我們要省略大量代碼,理性地分析它。

    ?????? 對(duì)已定型的AfxWndProc來(lái)說(shuō),對(duì)所有消息,最多只能提供一種默認(rèn)的處理方式。這當(dāng)然不是我們想要的。我們想通過(guò)AfxWndProc最終執(zhí)行消息映射網(wǎng)中對(duì)應(yīng)的函數(shù)。那么,這個(gè)執(zhí)行路線是怎么樣的呢?

    ?????? 從AfxWndProc下去,最終會(huì)調(diào)用到一個(gè)函數(shù)OnWndMsg。請(qǐng)看代碼:

    C++代碼
  • LRESULT?CALLBACK?AfxWndProc(HWND?hWnd,UINT?nMsg,WPARAM?wParam,?LPARAM?lParam) ??
  • {????? ??
  • …… ??
  • ???????CWnd*?pWnd?=?CWnd::FromHandlePermanent(hWnd);?//把對(duì)句柄的操作轉(zhuǎn)換成對(duì)CWnd對(duì)象。???
  • ???????Return?AfxCallWndProc(pWnd,hWnd,nMsg,wParam,lParam); ??
  • }??
  • ?????? 把對(duì)句柄的操作轉(zhuǎn)換成對(duì)CWnd對(duì)象是很重要的一件事,因?yàn)锳fxWndProc只是一個(gè)全局函數(shù),當(dāng)然不知怎么樣去處理各種windows窗口消息,所以它聰明地把處理權(quán)交給windows窗口所關(guān)聯(lián)的MFC窗口對(duì)象。

    ?????? 現(xiàn)在,大家?guī)缀蹩梢韵胂蟮玫紸fxCallWndProc要做的事情,不錯(cuò),它當(dāng)中有一句:

    ?????? pWnd->WindowProc(nMsg,wParam,lParam);

    ?????? 到此,MFC窗口過(guò)程函數(shù)變成了自己的一個(gè)成員函數(shù)。WindowProc是一個(gè)虛函數(shù),我們甚至可以通過(guò)改寫(xiě)這個(gè)函數(shù)去響應(yīng)不同的消息,當(dāng)然,這是題外話。

    ?????? WindowProc會(huì)調(diào)用到CWnd對(duì)象的另一個(gè)成員函數(shù)OnWndMsg,下面看看大概的函數(shù)原型是怎么樣的:

    C++代碼
  • BOOL?CWnd::OnWndMsg(UINT?message,WPARAM?wParam,LPARAM?lParam,LRESULT*?pResult) ??
  • { ??
  • ??????if(message==WM_COMMAND) ??
  • ??????{ ??
  • ????????????OnCommand(wParam,lParam); ??
  • ??????????? …… ??
  • ??????} ??
  • ??????if(message==WM_NOTIFY) ??
  • ??????{ ??
  • ????????????OnCommand(wParam,lParam,&lResult); ??
  • ??????????? …… ??
  • ??????} ??
  • ????? const?AFX_MSGMAP*?pMessageMap;?pMessageMap=GetMessageMap(); ??
  • ????? const?AFX_MSGMAP_ENTRY*?lpEntry; ??
  • ????? /*以下代碼作用為:用AfxFindMessageEntry函數(shù)從消息入口pMessageMap處查找指定消息,如果找到,返回指定消息映射表成員的指針給lpEntry。然后執(zhí)行該結(jié)構(gòu)成員的pfn所指向的函數(shù)*/?????
  • ??????if((lpEntry=AfxFindMessageEntry(pMessageMap->lpEntries,message,0,0)!=NULL) ??
  • ????? { ??
  • ???????????lpEntry->pfn();/*注意:真正MFC代碼中沒(méi)有用這一條語(yǔ)句。上面提到,不同的消息參數(shù)代表不同的意義和不同的消息響應(yīng)函數(shù)有不同類(lèi)型的返回值。而pfn是一個(gè)不帶參數(shù)的函數(shù)指針,所以真正的MFC代碼中,要根據(jù)對(duì)象lpEntry的消息的動(dòng)作標(biāo)識(shí)nSig給消息處理函數(shù)傳遞參數(shù)類(lèi)型。這個(gè)過(guò)程包含很復(fù)雜的宏代換,大家在此知道:找到匹配消息,執(zhí)行相應(yīng)函數(shù)就行!*/??
  • ????? } ??
  • }??
  • ???????MFC命令傳遞

    ?????? 在上面的代碼中,大家看到了OnWndMsg能根據(jù)傳進(jìn)來(lái)的消息參數(shù),查找到匹配的消息和執(zhí)行相應(yīng)的消息響應(yīng)。但這還不夠,我們平常響應(yīng)菜單命令消息的時(shí)候,原本屬于框架窗口(CFrameWnd)的WM_COMMAND消息,卻可以放到視對(duì)象或文檔對(duì)象中去響應(yīng)。其原理如下:

    ?????? 我們看上面函數(shù)OnWndMsg原型中看到以下代碼:

    ?????? if(message==WM_COMMAND)
    ???????{
    ?????????????????OnCommand(wParam,lParam);
    ?????????????????……
    ?????? }

    ?????? 即對(duì)于命令消息,實(shí)際上是交給OnCommand函數(shù)處理。而OnCommand是一個(gè)虛函數(shù),即WM_COMMAND消息發(fā)生時(shí),最終是發(fā)生該消息所對(duì)應(yīng)的MFC對(duì)象去執(zhí)行OnCommand。比如點(diǎn)框架窗口菜單,即向CFrameWnd發(fā)送一個(gè)WM_COMMAND,將會(huì)導(dǎo)致CFrameWnd::OnCommand(wParam,lParam)的執(zhí)行。且看該函數(shù)原型:

    C++代碼
  • BOOL?CFrameWnd::OnCommand(WPARAM?wParam,LPARAM?lParam) ??
  • { ??
  • ???????…… ??
  • ???????return?CWnd::?OnCommand(wParam,lParam); ??
  • }??
  • ?????? 可以看出,它最后把該消息交給CWnd:: OnCommand處理。再看:

    C++代碼
  • BOOL?CWnd::OnCommand(WPARAM?wParam,LPARAM?lParam) ??
  • { ??
  • ???????…… ??
  • ???????return?OnCmdMsg(nID,nCode,NULL,NULL); ??
  • }??
  • ?????? 這里包含了一個(gè)C++多態(tài)性很經(jīng)典的問(wèn)題。在這里,雖然是執(zhí)行CWnd類(lèi)的函數(shù),但由于這個(gè)函數(shù)在CFrameWnd:: OnCmdMsg里執(zhí)行,即當(dāng)前指針是CFrameWnd類(lèi)指針,再有OnCmdMsg是一個(gè)虛函數(shù),所以如果CFrameWnd改寫(xiě)了OnCommand,程序會(huì)執(zhí)行CFrameWnd::OnCmdMsg(…)。

    ?????? 對(duì)CFrameWnd::OnCmdMsg(…)函數(shù)的原理扼要分析如下:

    C++代碼
  • BOOL?CFrameWnd::?OnCmdMsg(…) ??
  • { ??
  • ???????CView?pView?=?GetActiveView();//得到活動(dòng)視指針。???
  • ???????if(pView->?OnCmdMsg(…)) ??
  • ???????return?TRUE;?//如果CView類(lèi)對(duì)象或其派生類(lèi)對(duì)象已經(jīng)處理該消息,則返回。??
  • ???????……//否則,同理向下執(zhí)行,交給文檔、框架、及應(yīng)用程序執(zhí)行自身的OnCmdMsg。??
  • }??
  • ?????? 到此,CFrameWnd:: OnCmdMsg完成了把WM_COMMAND消息傳遞到視對(duì)象、文檔對(duì)象及應(yīng)用程序?qū)ο髮?shí)現(xiàn)消息響應(yīng)。

    ?????? 寫(xiě)了這么多,我們已經(jīng)清楚了MFC消息映射與命令傳遞的大致過(guò)程。

    ???????MFC消息映射宏

    ?????? 現(xiàn)在,我們來(lái)看MFC“神秘代碼”,會(huì)發(fā)覺(jué)好看多了。

    ?????? 先看DECLARE_MESSAGE_MAP()宏,它在MFC中定義如下:

    C++代碼
  • #define?DECLARE_MESSAGE_MAP()?/???
  • private:?/ ??
  • ???????static?const?AFX_MSGMAP_ENTRY?_messageEntries[];?/???????? ??
  • protected:?/ ??
  • ???????static?AFX_DATA?const?AFX_MSGMAP?messageMap;?/ ??
  • ???????virtual?const?AFX_MSGMAP*?GetMessageMap()?const;?/??
  • ?????? 可以看出DECLARE_MESSAGE_MAP()定義了我們熟悉的兩個(gè)結(jié)構(gòu)和一個(gè)函數(shù),顯而易見(jiàn),這個(gè)宏為每個(gè)需要實(shí)現(xiàn)消息映射的類(lèi)提供了相關(guān)變量和函數(shù)。

    ?????? 現(xiàn)在集中精力來(lái)看一下BEGIN_MESSAGE_MAP,END_MESSAGE_MAP和ON_COMMAND三個(gè)宏,它們?cè)贛FC中定義如下(其中ON_COMMAND與另外兩個(gè)宏并沒(méi)有定義在同一個(gè)文件中,把它放到一起是為了好看):

    C++代碼
  • #define?BEGIN_MESSAGE_MAP(theClass,?baseClass)?/???
  • const?AFX_MSGMAP*?theClass::GetMessageMap()?const?/ ??
  • {?return?&theClass::messageMap;?}?/ ??
  • AFX_COMDAT?AFX_DATADEF?const?AFX_MSGMAP?theClass::messageMap?=?/ ??
  • {?&baseClass::messageMap,?&theClass::_messageEntries[0]?};?/ ??
  • AFX_COMDAT?const?AFX_MSGMAP_ENTRY?theClass::_messageEntries[]?=?/ ??
  • {?/ ??
  • ? ??
  • #define?ON_COMMAND(id,?memberFxn)?/???
  • ???????{?WM_COMMAND,?CN_COMMAND,?(WORD)id,?(WORD)id,?AfxSig_vv,?(AFX_PMSG)&memberFxn?}, ??
  • ? ??
  • #define?END_MESSAGE_MAP()?/???
  • {0,?0,?0,?0,?AfxSig_end,?(AFX_PMSG)0?}?/ ??
  • };?/??
  • ????? 一下子看三個(gè)宏覺(jué)得有點(diǎn)復(fù)雜,但這僅僅是復(fù)雜,公式性的文字代換并不是很難。且看下面例子,假設(shè)我們框架中有一菜單項(xiàng)為“Test”,即定義了如下宏:

    C++代碼
  • BEGIN_MESSAGE_MAP(CMainFrame,?CFrameWnd) ??
  • ??????ON_COMMAND(ID_TEST,?OnTest) ??
  • END_MESSAGE_MAP()??
  • ?????? 那么宏展開(kāi)之后得到如下代碼:

    C++代碼
  • const?AFX_MSGMAP*?CMainFrame::GetMessageMap()?const? ??
  • ??
  • {?return?&CMainFrame::messageMap;?}? ??
  • ??
  • ///以下填入消息表映射信息???
  • ??
  • const?AFX_MSGMAP?CMainFrame::messageMap?=?????????? ??
  • ??
  • {?&CFrameWnd::messageMap,?&CMainFrame::_messageEntries[0]?};? ??
  • ??
  • //下面填入保存著當(dāng)前類(lèi)感興趣的消息,可填入多個(gè)AFX_MSGMAP_ENTRY對(duì)象???
  • ??
  • const?AFX_MSGMAP_ENTRY?CMainFrame::_messageEntries[]?=? ??
  • ??
  • {? ??
  • ??
  • {?WM_COMMAND,?CN_COMMAND,?(WORD)ID_TEST,?(WORD)ID_TEST,?AfxSig_vv,?(AFX_PMSG)&OnTest?},???????//???????加入的ID_TEST消息參數(shù)???
  • ??
  • {0,?0,?0,?0,?AfxSig_end,?(AFX_PMSG)0?}?//本類(lèi)的消息映射的結(jié)束項(xiàng)???
  • ??
  • };??
  • ?????? 大家知道,要完成ID_TEST消息映射,還要定義和實(shí)現(xiàn)OnTest函數(shù)。即在頭文件中寫(xiě)afx_msg void OnTest()并在源文件中實(shí)現(xiàn)它。根據(jù)以上所學(xué)的東西,我們知道了當(dāng)ID為ID_TEST的命令消息發(fā)生,最終會(huì)執(zhí)行到我們寫(xiě)的OnTest函數(shù)。

    ?????? 至此,MFC六大關(guān)鍵技術(shù)寫(xiě)完了。其中寫(xiě)得最難的是消息映射與命令傳遞,除了技術(shù)復(fù)雜之外,最難的是有許多避不開(kāi)的代碼。為了大家看得輕松一點(diǎn),我把那繁雜的宏放在文章最后,希望能給你閱讀帶來(lái)方便。

    總結(jié)

    以上是生活随笔為你收集整理的MFC六大核心机制之五、六:消息映射和命令传递的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

    如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。