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

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

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



        AFX_CMDHANDLERINFO* pHandlerInfo) 

{ 

        // pump through current view FIRST 

        CView* pView = GetActiveView(); 

        if (pView != NULL && pView…》OnCmdMsg (nID; nCode; pExtra; pHandlerInfo)) 

                return TRUE; 



        // then pump through frame 

        if  (CWnd::OnCmdMsg (nID; nCode; pExtra; pHandlerInfo)) 

                return TRUE; 



        // last but not least; pump through app 

        CWinApp* pApp = AfxGetApp(); 

        if (pApp != NULL && pApp…》OnCmdMsg (nID; nCode; pExtra; pHandlerInfo)) 

                return TRUE; 



        return FALSE; 

} 



这里非常明显地兵分三路,正是为了实践MFC 这个Application Framework 对于命令讯 



息的绕行路线的规划: 



     命令消息接收物的类型        处理次序 



     Frame                          1。 View 

           窗口 

                                    2。 Frame  窗口本身 

                                    3。 CWinApp 对象 



    View                            1。 View 本身 

                                    2。 Document 



     Document                       1。 Document  本身 

                                    2。 Document Template 



         图9…4 MFC 对于命令消息WM_MAND 的特殊处理顺序。 



  让我们锲而不舍地追踪下去: 



                                                                                       573 


…………………………………………………………Page 636……………………………………………………………

                    第篇    深入  MFC  程式設計 



                                          (        ) 

                    // in VIEWCORE。CPP  MFC 4。0 

                    BOOL CView::OnCmdMsg(UINT nID; int nCode; void* pExtra; 

                            AFX_CMDHANDLERINFO* pHandlerInfo) 

                     { 

                        // first pump through pane 

                        if (CWnd::OnCmdMsg (nID; nCode; pExtra; pHandlerInfo)) 

                            return TRUE; 



                        // then pump through document 

                        BOOL bHandled = FALSE; 

                        if (m_pDocument != NULL) 

                        { 

                            // special state for saving view before routing to document 

                            _AFX_THREAD_STATE* pThreadState = AfxGetThreadState(); 

                            CView* pOldRoutingView = pThreadState…》m_pRoutingView; 

                            pThreadState…》m_pRoutingView = this; 

                            bHandled = m_pDocument…》OnCmdMsg (nID; nCode; pExtra; pHandlerInfo); 

                            pThreadState…》m_pRoutingView = pOldRoutingView; 

                        } 



                        return bHandled; 

                     } 



                    这反应出图9…4 搜寻路径中「先View 而后Document 」的规划。由于CWnd 并未改写 



                    OnCmdMsg,所以函数中调用的CWnd::OnCmdMsg,其实就是CCmdTarget::OnCmdMsg: 



                                         (        ) 

                    // in CMDTARG。CPP  MFC 4。0 

                    BOOL CCmdTarget::OnCmdMsg (UINT nID; int nCode; void* pExtra; 

                            AFX_CMDHANDLERINFO* pHandlerInfo) 

                     { 

                        。。。 

                        // look through message map to see if it applies to us 

                        for (pMessageMap = GetMessageMap(); pMessageMap != NULL; 

                             pMessageMap = pMessageMap…》pBaseMap) 

                        { 

                            lpEntry = AfxFindMessageEntry (pMessageMap…》lpEntries; nMsg; nCode; nID); 

                            if (lpEntry != NULL) 

                            { 

                                // found it 

                                return DispatchCmdMsg (this; nID; nCode; 

                                        lpEntry…》pfn; pExtra; lpEntry…》nSig; pHandlerInfo); 

                            } 

                        } 

                        return FALSE;   // not handled 

                     } 



574 


…………………………………………………………Page 637……………………………………………………………

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



其中的AfxFindMessageEntry  动作稍早我已列出。 



当命令消息兵分三路的第一路走到消息映射网的末尾一个类别CCmdTarget,没有办法再 



 「节外生枝」,只能乖乖比对CCmdTarget 的消息映射表。如果没有发现吻合者,传回 



FALSE ,引起CView::OnCmdMsg 接下去调用m_pDocument…》OnCmdMsg 。如果有吻合 



者,调用全域函数DispatchCmdMsg : 



static BOOL DispatchCmdMsg (CCmdTarget* pTarget; UINT nID; int nCode; 

        AFX_PMSG pfn; void* pExtra; UINT nSig; AFX_CMDHANDLERINFO* pHandlerInfo) 

                // return TRUE to stop routing 

{ 

        ASSERT_VALID(pTarget); 

        UNUSED(nCode);   // unused in release builds 



        union MessageMapFunctions mmf; 

        mmf。pfn = pfn; 

        BOOL bResult = TRUE; // default is ok 

        。。。 

        switch (nSig) 

        { 

        case AfxSig_vv: 

                // normal mand or control notification 

                (pTarget…》*mmf。pfn_MAND)(); 

                break; 



        case AfxSig_bv: 

                // normal mand or control notification 

                bResult = (pTarget…》*mmf。pfn_bMAND)(); 

                break; 



        case AfxSig_vw: 

                // normal mand or control notification in a range 

                (pTarget…》*mmf。pfn_MAND_RANGE)(nID); 

                break; 



        case AfxSig_bw: 

                // extended mand (passed ID; returns bContinue) 

                bResult = (pTarget…》*mmf。pfn_MAND_EX)(nID); 

                break; 

        。。。 

        default:    // illegal 

                ASSERT(FALSE); 



                                                                                          575 


…………………………………………………………Page 638……………………………………………………………

                    第篇    深入  MFC  程式設計 



                                    return 0; 

                            } 

                            return bResult; 

                     } 



                    以下是另一路CDocument 的动作: 



                    // in DOCCORE。CPP 

                    BOOL CDocument::OnCmdMsg (UINT nID; int nCode; void* pExtra; 

                            AFX_CMDHANDLERINFO* pHandlerInfo) 

                     { 

                            if (CCmdTarget::OnCmdMsg (nID; nCode; pExtra; pHandlerInfo)) 

                                    return TRUE; 



                            // otherwise check template 

                            if (m_pDocTemplate != NULL && 

                              m_pDocTemplate…》OnCmdMsg (nID; nCode; pExtra; pHandlerInfo)) 

                                    return TRUE; 



                            return FALSE; 

                     } 



                     图9…5 画出FrameWnd 窗口收到命令消息后的四个尝试路径。第3章曾经以一个简单的 



                     DOS 程序仿真出这样的绕行路线。 



576 


…………………………………………………………Page 639……………………………………………………………

                                                                      第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          WM_MAND                 e 

       0;0;0;0;0;0 



                          CDocument     CMyDocument 



                                                    

                                                            当CMyFrameWnd 收到WM_MAND , 



                                                            消息唧筒尝试数种绕行路线,使命令消息有 

                               ; ; ; ; ;       ; ; ; ; ;    机会经过任何一个类别。图中的     



                             0;0;0;0;0;0     0;0;0;0;0;0    即绕行的优先次序(请参考图9…4 )。 



          图9…5 FrameWnd 窗口收到命令消息后的四个尝试路径。第 3 章曾经以一 



                 个简单的DOS 程序仿真出这样的绕行路线。 



OnCmdMsg 是各类别专门用来对付命令消息的函数。每一个「可接受命令消息之对象」 



 (mand Target )在处理命令消息时都会(都应该)遵循一个游戏规则:调用另一个 



目标类别的OnCmdMsg 。这才能够将命令消息传送下去。如果说AfxWndProc  是消息流 



动的「唧筒」,各类别的OnCmdMsg 就是消息流动的「转辙器」。 



以下我举一个具体例子。假设命令消息从Scribble 的【Edit/Clear All 】发出,其处理常 



式位在CScribbleDoc,下面是这个命令消息的流浪过程: 



                                                                                                             577 


…………………………………………………………Page 640……………………………………………………………

                第篇    深入  MFC  程式設計 



                 1。 MDI 主窗口( CMDIFrameWnd ) 收到命令消息WM_MAND, 其ID 为 



                  ID_EDIT_CLEAR_ALL 。 



                 2。 MDI 主窗口把命令消息交给目前作用中的MDI 子窗口(CMDIChildWnd)。 



                 3。 MDI 子窗口给它自己的子窗口(也就是View )一个机会。 



                 4。 View 检查自己的Message Map 。 



                 5。 View 发现没有任何处理例程可以处理此命令消息,只好把它传给Document 。 



                 6。 Document 检查自己的Message Map ,它发现了一个吻合项: 



                   BEGIN_MESSAGE_MAP(CScribbleDoc; CDocument) 



                       ON_MAND(ID_EDIT_CLEAR_ALL; OnEditClearAll) 



                       。。。 



                   END_MESSAGE_MAP() 



                 于是调用该函数,命令消息的流动路线也告终止。 



                 如果上述的步骤6 仍没有找到处理函数,那么就: 



                 7。 Document 把这个命令消息再送到Document Template 对象去。 



                 8。 还是没被处理,于是命令消息回到View 。 



                 9。 View 没有处理,于是又回给MDI 子窗口本身。 



                10。 传给CWinApp 对象…无主消息的终极归属。 



                 图9…6 是构成「消息邦浦」之各个函数的调用次序。此图可以对前面所列之各个 



                 源代码组织出一个大局观来。 



578 


…………………………………………………………Page 641……………………………………………………………

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



          AfxWndProc 

           AfxWndProc 

                                  message 



        AfxCallWndProc 

         AfxCallWndProc 



       CWnd::WindowProc 

       CWnd::WindowProc 



       CWnd::OnWndMsg 

        CWnd::OnWndMsg                                     ::DefWindowProc 

                           CWnd::DefWindowProc             ::DefWindowProc 

                           CWnd::DefWindowProc 



                                                         AfxFindMessageEntry 

 WM_MAND               regular window message         AfxFindMessageEntry 



 CFrameWnd::Onmand 

 CFrameWnd::Onmand 

                                                            msg handler 



      CWnd::Onmand 

       CWnd::Onmand 



  CFrameWnd::OnCmdMsg 

  CFrameWnd::OnCmdMsg 



              

                                           

       CView::OnCmdMsg 

        CView::OnCmdMsg                          CWnd::OnCmdMsg             

                                                 CWnd::OnCmdMsg 

                                              (CCmdTarget::OnCmdMsg) 

                                             (CCmdTarget::OnCmdMsg) 



   CWnd::OnCmdMsg          CDocument::OnCmdMsg 

    CWnd::OnCmdMsg         CDocument::OnCmdMsg                      CWinApp::OnCmdMsg 

                                                                    CWinApp::OnCmdMsg 

(CCmdTarget::OnCmdMsg) 

(CCmdTarget::OnCmdMsg)                                            (CCmdTarget::OnCmdMag) 

                                                                  (CCmdTarget::OnCmdMag) 

                           CCmdTarget::OnCmdMsg 

                            CCmdTarget::OnCmdMsg 

        DispatchCmdMsg 

        DispatchCmdMsg 

                                                  DispatchCmdMsg       DispatchCmdMsg 

                                                   DispatchCmdMsg      DispatchCmdMsg 

                              DispatchCmdMsg 

                              DispatchCmdMsg 

       msg handler   FALSE                                      FALSE                FALSE 

                                           FALSE  msg handler          msg handler 

                             msg handler 



           图9…6 构成 「消息邦浦」之各个函数的调用次序 



                                                                                         579 


…………………………………………………………Page 642……………………………………………………………

                 第篇    深入  MFC  程式設計 



             罗塞达碑石:AfxSig_xx 的奥秘 



                大架构建立起来了,但我还没有很仔细地解释在消息映射「网」中的_messageEntries '' 



                数组内容。为什么消息经由推动引擎(上一节谈的那整套家伙)推过这些数组,就可以 



                找到它的处理例程? 



                Paul DiLascia 在他的文章(! ¨ Meandering Through the Maze of MFC Message and mand 



                Routing!  ¨,Microsoft Systems Journal ,1995/07)中形容这些数组之内一笔一笔的记录像 



                是罗塞达碑石,呵呵,就靠它们揭开消息映射的最后谜底了。 



                 罗塞达碑石(Rosetta Stone ),1799 年拿破仑远征埃及时,由一名官员在尼罗河口罗塞 



                 达发现,揭开了古埃及象形文字之谜。石碑是黑色玄武岩,高114 公分,厚28 公分,宽 



                 72 公分。经法国学者Jean…Francois Champollion 研究后,世人因得顺利研读古埃及文献。 



                 消息映射表的每一笔记录是这样的形式: 



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