友情提示:如果本网页打开太慢或显示不完整,请尝试鼠标右键“刷新”本网页!
富士康小说网 返回本书目录 加入书签 我的书架 我的书签 TXT全本下载 『收藏到我的浏览器』

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

快捷操作: 按键盘上方向键 ← 或 → 可快速上下翻页 按键盘上的 Enter 键可回到本书目录页 按键盘上方向键 ↑ 可回到本页顶部! 如果本书没有阅读完,想下次继续接着阅读,可使用上方 "收藏到我的浏览器" 功能 和 "加入书签" 功能!



               //{{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 DECLARE_MESSAGE_MAP 宏相当于声明了这样的数据结构。 



        消息映射网的形成 :                                   宏 

                              BEGIN_/ON_/END_ 



              前置准备工作完成了,接下来的课题是如何实现并填充图                       的数据结构内容。当然你 

                                                           9…1  



              马上就猜到了,使用的是另一组宏: 



                 BEGIN_MESSAGE_MAP(CMyView; CView) 

                     ON_WM_PAINT() 

                     ON_WM_CREATE() 

                     。。。 

                 END_MESSAGE_MAP() 



               奥秘还是在源代码中: 



554 


…………………………………………………………Page 617……………………………………………………………

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



// 以下源代码在AFXWIN。H 



 #define BEGIN_MESSAGE_MAP (theClass; baseClass)  

        const AFX_MSGMAP* theClass::GetMessageMap() const  

                { return &theClass::messageMap; }  

        AFX_DATADEF const AFX_MSGMAP theClass::messageMap =  

        { &baseClass::messageMap; &theClass::_messageEntries'0' };  

        const AFX_MSGMAP_ENTRY theClass::_messageEntries'' =  

        {  



 #define END_MESSAGE_MAP ()  

                {0; 0; 0; 0; AfxSig_end; (AFX_PMSG)0 }  

        };  



   注意:AfxSig_end 在AFXMSG_。H  中被定义为0 



 // 以下源代码在AFXMSG_。H 



 #define ON_MAND (id; memberFxn)  

        { WM_MAND; CN_MAND; (WORD)id; (WORD)id; AfxSig_vv; (AFX_PMSG)memberFxn }; 



 #define ON_WM_CREATE ()  

        { WM_CREATE; 0; 0; 0; AfxSig_is;  

              (AFX_PMSG)(AFX_PMSGW)(int (AFX_MSG_CALL CWnd::*)(LPCREATESTRUCT))OnCreate }; 

 #define ON_WM_DESTROY ()  

        { WM_DESTROY; 0; 0; 0; AfxSig_vv;  

                (AFX_PMSG)(AFX_PMSGW)(void (AFX_MSG_CALL CWnd::*)(void))OnDestroy }; 

 #define ON_WM_MOVE ()  

        { WM_MOVE; 0; 0; 0; AfxSig_vvii;  

                (AFX_PMSG)(AFX_PMSGW)(void (AFX_MSG_CALL CWnd::*)(int; int))OnMove }; 

 #define ON_WM_SIZE ()  

        { WM_SIZE; 0; 0; 0; AfxSig_vwii;  

                (AFX_PMSG)(AFX_PMSGW)(void (AFX_MSG_CALL CWnd::*)(UINT; int; int))OnSize }; 

 #define ON_WM_ACTIVATE ()  

        { WM_ACTIVATE; 0; 0; 0; AfxSig_vwWb;  

                (AFX_PMSG)(AFX_PMSGW)(void (AFX_MSG_CALL CWnd::*)(UINT; CWnd*; 

BOOL))OnActivate }; 

 #define ON_WM_SETFOCUS ()  

        { WM_SETFOCUS; 0; 0; 0; AfxSig_vW;  

                (AFX_PMSG)(AFX_PMSGW)(void (AFX_MSG_CALL CWnd::*)(CWnd*))OnSetFocus }; 

 #define ON_WM_PAINT ()  

        { WM_PAINT; 0; 0; 0; AfxSig_vv;  

                (AFX_PMSG)(AFX_PMSGW)(void (AFX_MSG_CALL CWnd::*)(void))OnPaint }; 

 #define ON_WM_CLOSE ()  

        { WM_CLOSE; 0; 0; 0; AfxSig_vv;  

                (AFX_PMSG)(AFX_PMSGW)(void (AFX_MSG_CALL CWnd::*)(void))OnClose }; 

 。。。 



                                                                                        555 


…………………………………………………………Page 618……………………………………………………………

                  第篇    深入  MFC  程式設計 



                  于是,这样的宏: 



                  BEGIN_MESSAGE_MAP(CMyView; CView) 

                      ON_WM_CREATE() 

                      ON_WM_PAINT() 

                  END_MESSAGE_MAP() 



                  便被展开成为这样的码: 

                  const AFX_MSGMAP* CMyView::GetMessageMap() const 

                          { return &CMyView::messageMap; } 

                  AFX_DATADEF const AFX_MSGMAP CMyView::messageMap = 

                   { &CView::messageMap; &CMyView::_messageEntries'0' }; 

                  const AFX_MSGMAP_ENTRY CMyView::_messageEntries'' = 

                   { 

                      { WM_CREATE; 0; 0; 0; AfxSig_is;  

                          (AFX_PMSG)(AFX_PMSGW)(int (AFX_MSG_CALL CWnd::*)(LPCREATESTRUCT))OnCreate }; 

                      { WM_PAINT; 0; 0; 0; AfxSig_vv;  

                          (AFX_PMSG)(AFX_PMSGW)(void (AFX_MSG_CALL CWnd::*)(void))OnPaint }; 

                      {0; 0; 0; 0; AfxSig_end; (AFX_PMSG)0 } 

                   }; 



                  其中AFX_DATADEF  和AFX_MSG_CALL 又是两个看起来很奇怪的常数。你可以在两 



                  个文件中找到它们的定义: 



                  // in DEVSTUDIOVCMFCINCLUDEAFXVER_。H 

                  #define AFX_DATA 

                  #define AFX_DATADEF 



                  // in DEVSTUDIOVCMFCINCLUDEAFXWIN。H 

                  #define AFX_MSG_CALL 



                  显然它们就像afx_msg 一样(我曾经在第6章的HellpMFC 源代码一出现之后解释 



                  过),都只是个〃intentional placeholder〃  (刻意保留的空间),可能在将来会用到,目前 



                  则为「无物」。 



556 


…………………………………………………………Page 619……………………………………………………………

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



以图表示BEGIN_!  K/ON_!  K/END_!  K 宏的结果为: 



                                               CView::messageMap 



                                                      pBaseMap 

          CMyView::_messageEntries'' 

                                                       lpEntries 

       WM_CREATE; 0; 0; 0; AfxSig_is; OnCreate 



         WM_PAINT; 0; 0; 0; AfxSig_vv; OnPaint  CMyView::messageMap 



                0; 0; 0; 0; AfxSig_end; 0 



注意:图中的AfxSig_vv  和AfxSig_is 都代表签名符号(Signature)。这些常数在AFXMSG_。H 



中定义,稍后再述。 



前面我说过了,所有能够接收消息的类别,都应该衍生自CCmdTarget。那么我们这么推 



论应该是合情合理的: 每一个衍生自CCmdTarget 的类别都应该有 



DECLARE_/BEGIN_/END_ 宏组? 



唔,错了,CWinThread 就没有! 



可是这么一来,CWinApp 通往CCmdTarget 的路径不就断掉了吗?呵呵,难道CWinApp 



不能跳过CWinThread 直接连上CCmdTarget 吗?看看下面的MFC 源代码: 



 // in AFXWIN。H 

 class CWinApp : public CWinThread 

 { 

 。。。 

         DECLARE_MESSAGE_MAP() 

 }; 



 // in APPCORE。CPP 

 BEGIN_MESSAGE_MAP(CWinApp; CCmdTarget) //注意第二个参数是CCmdTarget, 

       //{{AFX_MSG_MAP(CWinApp)            //而不是CWinThread。 

       // Global File mands 



                                                                                      557 


…………………………………………………………Page 620……………………………………………………………

                 第篇    深入  MFC  程式設計 



                       ON_MAND(ID_APP_EXIT; OnAppExit) 

                       // MRU most recently used file menu 

                       ON_UPDATE_MAND_UI(ID_FILE_MRU_FILE1; OnUpdateRecentFileMenu) 

                       ON_MAND_EX_RANGE(ID_FILE_MRU_FILE1; ID_FILE_MRU_FILE16; OnOpenRecentFile) 

                       //}}AFX_MSG_MAP 

                 END_MESSAGE_MAP() 



                  让我们看看具体的情况。图9…2 就是MFC  的消息映射表。当你的衍生类别使用了 



                  DECLARE_/BEGIN_/END_ 宏,你也就把自己的消息映射表挂上去了…当然是挂在尾 



                  端。 



                  如果没有把BEGIN_MESSAGE_MAP 宏中的两个参数(也就是类别本身及其父类别的 



                  名称)按照规矩来写,可能会发生什么结果呢?消息可能在不应该流向某个类别时流了 



                  过去,在应该被处理时却又跳离了。总之,完美的机制有了破绽。程序没当掉算你幸运! 



558 


…………………………………………………………Page 621……………………………………………………………

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



                          CWinThread        CWinApp         CMyWinApp 



                                                 ; ; ; ; ;        ; ; ; ; ; 



                                               0;0;0;0;0;0      0;0;0;0;0;0 

                                                                                CView           CMyView 



                                                                                                                      m 

                                                                                                                       e 

CCmdTarget                  CWnd           CFrameWnd        CMyFrameWnd 

                                                                                                                       s 

                                                                                   ; ; ; ; ;         ; ; ; ; ; 



                                                                                                                       s 

                                                                                 0;0;0;0;0;0       0;0;0;0;0;0         a 



                               ; ; ; ; ;         ; ; ; ; ;        ; ; ; ; ; 

      ; ; ; ; ;                                                                                                        g 

                             0;0;0;0;0;0       0;0;0;0;0;0      0;0;0;0;0;0                                            e 

    0;0;0;0;0;0 



                          CDocument       CMyDocument 



                               ; ; ; ; ;         ; ; ; ; ; 



                             0;0;0;0;0;0       0;0;0;0;0;0 



               图9…2 MFC 消息映射表 ( 也就是消息流动网) 



我们终于了解,Message Map 既可说是一套宏,也可以说是宏展开后所代表的一套 



数据结构;甚至也可以说Message Map 是一种动作,这个动作,就是在刚刚所提的资料 



结构中寻找与消息相吻合的项目,从而获得消息的处理例程的函数指针。 



虽然,C++ 程序员看到多态(Polymorphism),直觉的反应就是虚拟函数,但请注意, 



各个Message Map 中的各个同名函数虽有多态的味道,却不是虚拟函数。乍想之下使用 



虚拟函数是合理的:你产生一个与窗口有关的C++ 类别,然后为此窗口所可能接收的 



任何消息都提供一个对应的虚拟函数。这的确散发着C++  的味道和对象导向的精神, 



但现实与理想之间总是有些距离。 



                                                                                                                         55
返回目录 上一页 下一页 回到顶部 9 10
快捷操作: 按键盘上方向键 ← 或 → 可快速上下翻页 按键盘上的 Enter 键可回到本书目录页 按键盘上方向键 ↑ 可回到本页顶部!
温馨提示: 温看小说的同时发表评论,说出自己的看法和其它小伙伴们分享也不错哦!发表书评还可以获得积分和经验奖励,认真写原创书评 被采纳为精评可以获得大量金币、积分和经验奖励哦!