八宝书库 > 文学其他电子书 > 深入浅出MFC第2版(PDF格式) >

第113部分

深入浅出MFC第2版(PDF格式)-第113部分

小说: 深入浅出MFC第2版(PDF格式) 字数: 每页4000字

按键盘上方向键 ← 或 → 可快速上下翻页,按键盘上的 Enter 键可回到本书目录页,按键盘上方向键 ↑ 可回到本页顶部!
————未阅读完?加入书签已便下次继续阅读!






  这个网就是所谓的消息映射地图(Message map )。最后,MFC 还得实现一个消息推动 



  引擎,让消息依Framework  的意旨前进,该拐的时候拐,该弯的时候弯,这个邦浦机制 



  埋藏在各个类别的WindowProc、Onmand、OnCmdMsg、DefWindowProc 虚拟函数 



  中。 



  没有命令绕行机制,Document/View 架构就像断了条胳臂,会少掉许多功用。 



  很快你就会看到所有的秘密。很快地,它们统统不再对你构成神秘。 



消息分类 



  Windows  的消息都是以WM_xxx 为名,WM_  的意思是〃Windows Message〃。消息可以 



  是来自硬件的「输入消息」,例如WM_LBUTTONDOWN ,也可以是来自USER 模块的 



   「窗口管理消息」,例如WM_CREATE。这些消息在MFC 程序中都是隐晦的(我的意 



  思是不像在SDK 程序中那般显明),我们不必在MFC 程序中撰写switch case 指令, 



  不必一一识别并处理由系统送过来的消息;所有消息都将依循Framework 制定的路线, 



  并参照路中是否有拦路虎(你的消息映射表格)而流动。WM_PAINT 一定流往你的 



  OnPaint 函数去,WM_SIZE 一定流往你的OnSize 函数去。 



  所有的消息在MFC 程序中都是暗潮汹涌,但是表面无波。 



  MFC 把消息分为三大类: 



  ■  命令消息(WM_MAND):命令消息意味着「使用者命令程序做某些动作」。 



    凡由UI 对象产生的消息都是这种命令消息,可能来自菜单或加速键或工具栏 



                                                             549 


…………………………………………………………Page 612……………………………………………………………

              第篇    深入  MFC  程式設計 



              按钮,并且都以WM_MAND 呈现。如何分辨来自各处的命令消息?SDK 



              程序主要靠消息的wParam 辨识之,MFC 程序则主要靠菜单项目的识别码 



              (menu ID )辨识之…两者其实是相同的。 



              什么样的类别有资格接受命令消息?凡衍生自CCmdTarget 之类别,皆有资格。从 



              mand target 的字面意义可知,这是命令消息的目的地。也就是说,凡衍生自 



              CCmdTarget 者,它的骨子里就有了一种特殊的机制。把整张MFC 类别阶层图摊开 



              来看,几乎构造应用程序的最重要的几个类别都衍生自CCmdTarget,剩下的不能接 



              收消息的,是像CFile、CArchive、CPoint、CDao (数据库)、Collection Classes (纯 



              粹数据处理)、GDI 等等「非主流」类别。 



              ■ 标准消息  除WM_MAND 之外,任何以WM_ 开头的都算是这一类。任何 



                衍生自CWnd 之类别,均可接收此消息。 



              ■  Control Notification 这种消息由控制组件产生,为的是向其父窗口(通常是对 



                话盒)通知某种情况。例如当你在ListBox 上选择其中一个项目,ListBox 就 



                会产生LBN_SELCHANGE 传送给父窗口。这类消息也是以WM_MAND 形 



                式呈现。 



         万流归宗mand Target (CCmdTarget) 



              你可以在程序的许多类别之中设计拦路虎(我是指「消息映射表格」),接收并处理讯 



              息。只要是CWnd 衍生类别,就可以拦下任何Windows 消息。与窗口无关的MFC 类 



              别(例如CDocument 和CWinApp)如果也想处理消息,必须衍生自CCmdTarget,并 



              且只可能收到WM_MAND 命令消息。 



              会产生命令消息的,不外就是UI 对象:菜单项目和工具栏按钮都是。命令消息必须有 



              一个对应的处理函数,把消息和其处理函数「绑」在一块儿,这动作称为mand 



              Binding,这个动作将由一堆宏完成。通常我们不直接手工完成这些宏内容,也就是 



              说我们并不以文字编辑器一行一行地撰写相关的码,而是藉助于ClassWizard 。 



              一个mand Target 对象如何知道它可以处理某个消息?答案是它会看看自己的消息映 



550 


…………………………………………………………Page 613……………………………………………………………

                                                  第9章   訊息映射與命令繞行   



      射表。消息映射表使得消息和函数的对映关系形成一份表格,进而全体形成一张网,当 



      mand Target 对象收到某个消息,便可由表格得知其处理函数的名称。 



三个奇怪的宏,一张巨大的网 



      早在本书第1章我就介绍过消息映射的雏形了,不过那是小把戏,不登大雅之堂。第3 



      章以DOS 程序仿真消息映射,就颇有可观之处,因为那是「偷」MFC  的源代码完成的, 



      可以说具体而微。 



      试着思考这个问题:C++  的继承与多态性质,使衍生类别与基础类别的成员函数之间有 



      着特殊的关联。但这当中并没有牵扯到Windows 消息。的确,C++ 语言完全没有考虑 



      Windows 消息这一回事(那当然)。如何让Windows 消息也能够在对象导向以及继承 



      性质中扮演一个角色?既然语言没有支持,只好自求多福了。消息映射机制的三个相关 



      宏就是MFC  自求多福的结果。 



       「消息映射」是MFC  内建的一个消息分派机制,只要利用数个宏以及固定形式的写 



      法,类似填表格,就可以让Framework 知道,一旦消息发生,该循哪一条路递送。每一 



      个类别只能拥有一个消息映射表格,但也可以没有。下面是Scribble Document 建立消息 



      映射表的动作: 



      首先你必须在类别声明档(。H )声明拥有消息映射表格: 



       class CScribbleDoc : public CDocument 

       { 

               。。。 

               DECLARE_MESSAGE_MAP() 

       }; 



      然后在类别实作档(。CPP )实现此一表格: 



       BEGIN_MESSAGE_MAP(CScribbleDoc; CDocument) 

               //{{AFX_MSG_MAP(CScribbleDoc) 

               ON_MAND(ID_EDIT_CLEAR_ALL; OnEditClearAll) 

               ON_MAND(ID_PEN_THICK_OR_THIN; OnPenThickOrThin) 

               。。。 

               //}}AFX_MSG_MAP 

       END_MESSAGE_MAP() 



                                                                            551 


…………………………………………………………Page 614……………………………………………………………

               第篇    深入  MFC  程式設計 



                  这其中出现三个宏。第一个宏BEGIN_MESSAGE_MAP 有两个参数,分别是拥 



                  有此消息映射表之类别,及其父类别。第二个宏是ON_MAND,指定命令讯 



                  息的处理函数名称。第三个宏END_MESSAGE_MAP 作为结尾记号。至于夹在 



                  BEGIN_ 和END_ 之中奇奇怪怪的说明符号//}} 和//{{,是ClassWizard 产生 



                  的,也是用来给它自己看的。记住,前面我就说了,很少人会自己亲手键入每一行码, 



                  因为ClassWizard 的表现相当不俗。 



               夹在BEGIN_ 和END_ 之中的宏,除了ON_MAND,还可以有许多种。标准的 



               Windows 消息并不需要由我们指定处理函数的名称。标准消息的处理函数,其名称也是 



                「标准」的(预设的),像是: 



               宏名称                    对映消息              消息处理函数 



               ON_WM_CHAR           WM_CHAR              OnChar 



               ON_WM_CLOSE          WM_CLOSE             OnClose 



               ON_WM_CREATE         WM_CREATE            OnCreate 



               ON_WM_DESTROY        WM_DESTROY           OnDestroy 



               ON_WM_LBUTTONDOWN    WM_LBUTTONDOWN       OnLButtonDown 



               ON_WM_LBUTTONUP      WM_LBUTTONUP         OnLButtonUp 



               ON_WM_MOUSEMOVE      WM_MOUSEMOVE         OnMouseMove 



               ON_WM_PAINT          WM_PAINT             OnPaint 



               。。。 



          DECLARE_ MESSAGE_ MAP 宏 



                消息映射的本质其实是一个巨大的数据结构,用来为诸如WM_PAINT 这样的标准消息 



                决定流动路线,使它得以流到父类别去;也用来为WM_MAND 这个特殊消息决定 



                流动路线,使它能够七拐八弯地流到类别阶层结构的旁支去。 



                观察机密的最好方法就是挖掘源代码: 



552 


…………………………………………………………Page 615……………………………………………………………

                                                      第9章   訊息映射與命令繞行   



// in AFXWIN。H 

#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;  



注意: static 修饰词限制了资料的配置,使得每个「类别」仅有一份资料,而不是每一 



个「对象」各有一份资料。 



我们看到两个陌生的类型:AFX_MSGMAP_ENTRY 和AFX_MSGMAP 。继续挖源代码, 



发现前者是一个struct : 



// in AFXWIN。H 

struct AFX_MSGMAP_ENTRY 

{ 

        UINT nMessage;   // windows message 

        UINT nCode;      // control code or WM_NOTIFY code 

        UINT nID;        // control ID (or 0 for windows messages) 

        UINT nLastID;    // used for entries specifying a range of control id's 

        UINT nSig;       // signature type  (action) or pointer to message # 

        AFX_PMSG pfn;    // routine to call (or special value) 

}; 



很明显你可以看出它的最主要作用,就是让消息nMessage 对应于函数pfn 。其中pfn  的 



数据类型AFX_PMSG 被定义为一个函数指针: 



typedef void (AFX_MSG_CALL CCmdTarget::*AFX_PMSG)(void); 



 出现在DECLARE_MESSAGE_MAP 宏中的另一个struct ,AFX_MSGMAP ,定义如下: 



// in AFXWIN。H 

struct AFX_MSGMAP 

{ 

        const AFX_MSGMAP* pBaseMap; 

        const AFX_MSGMAP_ENTRY* lpEntries; 

}; 



其中pBaseMap  是一个指向「基础类别之消息映射表」的指针,它提供了一个走访整个 



继承串链的方法,有效地实作出消息映射的继承性。衍生类别将自动地「继承」其基础 



                                                                                    553 


…………………………………………………………Page 616……………………………………………………………

                 第篇    深入  MFC  程式設計 



                 类别中所处理的消息,意思是,如果基础类别处理过A消息,其衍生类别即使未设计A 



                 消息之消息映射表项目,也具有对A消息的处理能力。当然啦,衍生类别也可以针对A 



                 消息设计自己的消息映射表项。 



                 喝,真像虚拟函数!但Message Map 没有虚拟函数所带来的巨大的overhead                (额外负担) 



                 透过DECLARE_MESSAGE_MAP 这么简单的一个宏,相当于为类别声明了图9…1 的 



                 数据类型。注意,只是声明而已,还没有真正的实体。 



                                                                    pBaseMap 

                                  _messageEntries'' 

                                                                     lpEntries 

                         nMessage; nCode; nID; nLastID; nSig; pfn 



                                                                   messageMap 



                          图9…1 DEC

返回目录 上一页 下一页 回到顶部 0 0

你可能喜欢的