<menu id="w8yyk"><menu id="w8yyk"></menu></menu>
  • <dd id="w8yyk"><nav id="w8yyk"></nav></dd>
    <menu id="w8yyk"></menu>
    <menu id="w8yyk"><code id="w8yyk"></code></menu>
    <menu id="w8yyk"></menu>
    <xmp id="w8yyk">
    <xmp id="w8yyk"><nav id="w8yyk"></nav>
  • 網站首頁 > 物聯資訊 > 技術分享

    MFC技術內幕系列之(四)---MFC消息映射與消息傳遞內幕

    2016-09-28 00:00:00 廣州睿豐德信息科技有限公司 閱讀
    睿豐德科技 專注RFID識別技術和條碼識別技術與管理軟件的集成項目。質量追溯系統、MES系統、金蝶與條碼系統對接、用友與條碼系統對接

     ////////////////////////////////////////////////////////////////////////////////////
                         /********* 文章系列:MFC技術內幕系列***********/
                         /************MFC技術內幕系列之(四)***********/
                         /*****文章題目:MFC消息映射與消息傳遞內幕******/

                         /*                            All rights Reserved                        */
                         /   *********關鍵字:消息映射,消息傳遞************/

                         /*      注釋:本文所涉及的程序源代碼均在Microsoft   */
                         /           Visual Studio.net EntERPrise Architect Edition       /
                         /*                   開發工具包提供的源代碼中                  */
                        
    //////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    引言:
        Windows操作系統是以消息為基礎,事件驅動的。作為程序員了解操作系統的消息傳遞機制是非常必要的。Microsoft的MFC又它自己的一套支持Windows操作系統消息機制的技術--消息映射(Message Mapping)和命令傳遞(Command Routing),在這篇文章中我就詳細的挖掘一下MFC的消息映射技術以及命令傳遞技術。

        正文:
                           ///////////////////////////////////////////////
                           /*     1.Windows消息概覽      */
                           //////////////////////////////////////////////
        對于消息,程序員應該不陌生。WM_CREATE,WM_PAINT等等都是Windows程序設計中必不可缺少的組成部分。大多有關MFC Win32編程的書籍都將Windows消息分為三大類即:
        * 標準消息:   任何以WM_開頭的消息(WM_COMMAND除外);如:WM_QUIT,WM_CREATE;
        * 命令消息:   WM_COMMAND;
        * 子窗口通知: 由子窗口(大多為控件)產生并發送到該控件所屬的父窗口的消息。(注意:此類消息也                    以WM_COMMAND形式出現)
        消息類型我們已經了解了,下面我們就來看看消息映射是如何工作的:
                           //////////////////////////////////////////////////////
                           /*  2.MFC消息映射網的組成元素 */
                           //////////////////////////////////////////////////////   
       我的前幾篇文章中涉及到了MFC內部建立的一些“網”技術,比如“執行期類型識別網”等,這回我們將建立一個消息映射網,這個網的建立與前面相同的是它也利用了一些神秘的宏。下面我們就來掀開它們的神秘面紗。
       我們先簡單地看看這些宏在程序源文件中的什么地方?
       //in xx.h
       class theClass
      {
          ...//
         DECLARE_MESSAGE_MAP
       };
       //in xx.cpp
       BEGIN_MESSAGE_MAP(theClass, baseClass)
     ON_COMMAND( ID_MYCMD, OnMyCommand )
            ON_WM_CREATE
       END_MESSAGE_MAP
       ...//
      
       這些宏的定義如下:
       //in Afxwin.h
       #define DECLARE_MESSAGE_MAP /
       private: /
     static const AFX_MSGMAP_ENTRY _messageEntries; /
       protected: /
     static const AFX_MSGMAP messageMap; /
     static const AFX_MSGMAP* PASCAL GetThisMessageMap; /
     virtual const AFX_MSGMAP* GetMessageMap const; /

       #define BEGIN_MESSAGE_MAP(theClass, baseClass) /
     const AFX_MSGMAP* PASCAL theClass::GetThisMessageMap /
      { return &theClass::messageMap; } /
     const AFX_MSGMAP* theClass::GetMessageMap const /
      { return &theClass::messageMap; } /
     AFX_COMDAT const AFX_MSGMAP theClass::messageMap = /
     { &baseClass::GetThisMessageMap, &theClass::_messageEntries[0] }; /
     AFX_COMDAT const AFX_MSGMAP_ENTRY theClass::_messageEntries = /
     { /

       #define END_MESSAGE_MAP /
      {0, 0, 0, 0, AfxSig_end, (AFX_PMSG)0 } /
     }; /
       DECLARE_MESSAGE_MAP宏為每個類添加了四個東東,包括那個重要的消息映射表messageMap和消息入口結構數組AFX_MSGMAP_ENTRY _messageEntries;BEGIN_MESSAGE_MAP(theClass, baseClass)和END_MESSAGE_MAP宏則初始化了它們,隨后我將帶領大家看看這個初始化過程。

                           ///////////////////////////////////////////////
                           /*      3.MFC消息映射表       */
                           //////////////////////////////// //////////////
       下面我們看看消息映射表messageMap和消息入口結構AFX_MSGMAP_ENTRY的定義:
     //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_PTR nSig;   // signature type (action) or pointer to message #
     AFX_PMSG pfn;    // routine to call (or special value)
     };


     struct AFX_MSGMAP
     {
     #ifdef _AFXDLL
     const AFX_MSGMAP* (PASCAL* pfnGetBaseMap);//基類的映射表指針,本程序將使用
     #else
     const AFX_MSGMAP* PBaseMap;
     #endif
     const AFX_MSGMAP_ENTRY* lpEntries;
     };
     
      其中AFX_MSGMAP結構中包含一個基類的映射表指針和一個指向消息入口結構AFX_MSGMAP_ENTRY的指針。

                           /////////////////////////////////////////////////
                           /*    4.MFC消息映射宏展開     */
                           /////////////////////////////////////////////////

      上面的宏展開后代碼如下:(以CMaimFrame為例)
       //in MaimFrm.h
       class CMaimFrame : public CFrameWnd
      {
        ...//
        private:
            static const AFX_MSGMAP_ENTRY _messageEntries;
        protected:
     static const AFX_MSGMAP messageMap;
     static const AFX_MSGMAP* PASCAL GetThisMessageMap;
     virtual const AFX_MSGMAP* GetMessageMap const;
      };
      //in MaimFrm.cpp
        const AFX_MSGMAP* PASCAL CMaimFrame::GetThisMessageMap
      { return &CMaimFrame::messageMap; }
        const AFX_MSGMAP* CMaimFrame::GetMessageMap const
      { return &CMaimFrame::messageMap; }
        AFX_COMDAT const AFX_MSGMAP theClass::messageMap =
     { &CFrameWnd::GetThisMessageMap, &CMaimFrame::_messageEntries[0] };
        AFX_COMDAT const AFX_MSGMAP_ENTRY CMaimFrame::_messageEntries =
            {
              {         ...//                      }
                        ...
              {0, 0, 0, 0, AfxSig_end, (AFX_PMSG)0 }
     };
       相信大家看了后大多源代碼都能夠理解,但是AFX_MSGMAP_ENTRY結構還是能夠引起我們的興趣的。下面讓我們看看_messageEntries是如何被初始化的:
       我們還是舉例來說明吧!(還是CMainFrame為例吧)
       BEGIN_MESSAGE_MAP(CMainFrame, CFrameWnd)
     ON_WM_CREATE
            ON_COMMAND( ID_MYCMD, OnMyCommand )
       END_MESSAGE_MAP
      
       大家看到了夾在BEGIN_MESSAGE_MAP和END_MESSAGE_MAP之間的宏,這些宏可分為基類,一類是Windows預定義消息宏(比如:ON_WM_CREATE,ON_WM_DESTROY等定義在afxmsg_.h中的Message map tables for Windows messages),一類是自定義的ON_COMMAND宏以及類似的如ON_UPDATE_COMMAND_UI等宏 。
       //in afxmsg_.h
       // Message map tables for Windows messages
       #define ON_WM_CREATE /
     { WM_CREATE, 0, 0, 0, AfxSig_is, /
      (AFX_PMSG) (AFX_PMSGW) /
      (static_cast< int (AFX_MSG_CALL CWnd::*)(LPCREATESTRUCT) > (OnCreate)) },
     
       #define ON_COMMAND(id, memberFxn) /
     { WM_COMMAND, CN_COMMAND, (WORD)id, (WORD)id, AfxSigCmd_v, /
      static_cast (memberFxn) },

       AFX_MSGMAP_ENTRY結構初始化過程:
       AFX_COMDAT const AFX_MSGMAP_ENTRY CMaimFrame::_messageEntries =
            {
              { WM_CREATE, 0, 0, 0, AfxSig_is,
      (AFX_PMSG) (AFX_PMSGW)
                  (static_cast< int (AFX_MSG_CALL CWnd::*)(LPCREATESTRUCT) > (OnCreate)) },
              { WM_COMMAND, CN_COMMAND, (WORD)ID_MYCMD, (WORD)ID_MYCMD, AfxSigCmd_v, /
      static_cast ( OnMyCommand) },       
              {0, 0, 0, 0, AfxSig_end, (AFX_PMSG)0 }
     };
       現在一切都清楚了吧!

                           //////////////////////////////////////////////////
                           /*    5.MFC消息映射網的連接   */
                           //////////////////////////////////////////////////
       MFC消息映射網的連接也是在初始化過程中完成的,其建立過程很簡單。主要有關成員有:
       private:
            static const AFX_MSGMAP_ENTRY _messageEntries;
       protected:
     static const AFX_MSGMAP messageMap;
       和BEGIN_MESSAGE_MAP宏開后的
       AFX_COMDAT const AFX_MSGMAP theClass::messageMap =
     { &baseClass::GetThisMessageMap, &theClass::_messageEntries[0] };
       該宏將pfnGetBaseMap賦值為其基類的messageMap地址;將AFX_MSGMAP_ENTRY* lpEntries賦值為該類的
    _messageEntries[0];
       這樣一個類不僅擁有本類的messageMap,而且還擁有其基類的messageMap,依此類推MFC消息映射網的連接
    就建立了,最終的基類都是CCmdTarget

                           //////////////////////////////////////////////////
                           /*    6.MFC命令傳遞機制概述   */
                           //////////////////////////////////////////////////
       有了MFC消息映射網就為命令傳遞打下了堅實的基礎。Win32API程序員都熟悉傳統的API編程都有一個
    WndProc回調函數來集中處理各種的Windows消息,然而在MFC中我們卻怎么也看不見WndProc回調函數的蹤影了。而MFC命令傳遞機制恰是為了將各種消息“拐彎抹角”地送到各個對應的"WndProc"函數的一種技術。MFC是如何將各種Windows消息準確的送到期該區的地方呢? MFC使用了鉤子函數等技術來保證其準確性和全面性。
       不知大家是否還記得MFC在注冊窗口類時作了什么?
       BOOL AFXAPI AfxEndDeferReGISterClass(LONG fToRegister)//部分源代碼
      {
            ...//
            // common initialization
     WNDCLASS wndcls;
     memset(&wndcls, 0, sizeof(WNDCLASS));   // start with NULL defaults
     wndcls.lpfnWndProc = DefWindowProc;
     ...//
       }
      可以看到MFC注冊時將wndcls.lpfnWndProc賦值為DefWindowProc函數,那么實際上是否消息都是由它處理的呢?顯然不可能,MFC又不知道我們要處理什么消息。那么還有什么函數能幫我們處理消息呢?這就是我要講的;  在MFC技術內幕系列之(二)----《 MFC文檔視圖結構內幕》中曾提到“CWnd::CreateEx函數調用了AfxHookWindowCreate(this);后者是干什么的呢?其實它與消息映射和命令傳遞有關。”
       實際上MFC命令傳遞機制就是從這里AfxHookWindowCreate(this)開始的。還是老辦法看看代碼吧:
     //in wincore.cpp
     void AFXAPI AfxHookWindowCreate(CWnd* pWnd)
     {
     ...//
     if (pThreadState->m_hHookOldCbtFilter == NULL)
     {
      pThreadState->m_hHookOldCbtFilter = ::SetWindowsHookEx(WH_CBT,
       _AfxCbtFilterHook, NULL, ::GetCurrentThreadId);
      if (pThreadState->m_hHookOldCbtFilter == NULL)
       AfxThrowMemoryException;
     }
     ...//
     }
      該函數設置了消息鉤子,其鉤子處理函數為_AfxCbtFilterHook;這里簡介一下鉤子函數:
      用我的理解,鉤子就是能給你一個在某個消息到達其默認的處理函數之前處理該消息機會的工具。
    與鉤子有關的函數主要有三個:
    HHOOK SetWindowsHookEx(int idHook, HOOKPROC lpfn, HINSTANCE hMod, DWORD dwThreadId );
    LRESULT CallNextHookEx(HHOOK hhk,  int nCode,    WPARAM wParam,   LPARAM lParam  );
    BOOL UnhookWindowsHookEx( HHOOK hhk   // handle to hook procedure);
    關于這三個函數我也不想多解釋,大家看看MFC有關文檔吧!這里主要講的是在AfxHookWindowCreate(CWnd* pWnd)中注冊的鉤子函數的類型(hook type)--WH_CBT;
    有關WH_CBT,MFC文檔時如是說的:
      Installs a hook procedure that receives notifications useful to a computer-based training (CBT) application. The system calls this function(這里指的是_AfxCbtFilterHook) before activating, creating, destroying, minimizing, maximizing, moving, or sizing a window; before completing a system command; before removing a mouse or keyboard event from the system message queue; before setting the keyboard focus; or before synchronizing with the system message queue. A computer-based training (CBT) application uses this hook procedure to receive useful notifications from the system.
     
                           /////////////////////////////////////////////
                           /*    7.偷換“窗口函數”      */
                           /////////////////////////////////////////////

       這會知道了吧,當發生窗口(包括子窗口)發生被激活,創建,撤銷,最小化等時候,應用程序將調用
    _AfxCbtFilterHook函數;下面就讓我們看看_AfxCbtFilterHook函數做了個啥:
     //in wincore.cpp
    // Window creation hooks
     LRESULT CALLBACK _AfxCbtFilterHook(int code, WPARAM wParam, LPARAM lParam)
     {
     _AFX_THREAD_STATE* pThreadState = _afxThreadState.GetData;
     if (code != HCBT_CREATEWND)
     {
      // wait for HCBT_CREATEWND just pass others on...
      return CallNextHookEx(pThreadState->m_hHookOldCbtFilter, code,
       wParam, lParam);
     }

     ...//   CWnd* pWndInit = pThreadState->m_pWndInit;
      HWND hWnd = (HWND)wParam;
      WNDPROC oldWndProc;
      if (pWndInit != NULL)
      {
                     #ifdef _AFXDLL
       AFX_MANAGE_STATE(pWndInit->m_pModuleState);
                     #endif

       // the window should not be in the permanent map at this time
       ASSERT(CWnd::FromHandlePermanent(hWnd) == NULL);

       // connect the HWND to pWndInit...
       pWndInit->Attach(hWnd);
       // allow other subclassing to occur first
       pWndInit->PreSubclassWindow;//***

       WNDPROC *pOldWndProc = pWndInit->GetSuperWndProcAddr;
       ASSERT(pOldWndProc != NULL);

       // subclass the window with standard AfxWndProc
       WNDPROC afxWndProc = AfxGetAfxWndProc;//***
       oldWndProc = (WNDPROC)SetWindowLongPtr(hWnd, GWLP_WNDPROC,
        (DWORD_PTR)afxWndProc);//***
       ASSERT(oldWndProc != NULL);
       if (oldWndProc != afxWndProc)
        *pOldWndProc = oldWndProc;

       pThreadState->m_pWndInit = NULL;
      }
     ...//
     lCallNextHook:
     LRESULT lResult = CallNextHookEx(pThreadState->m_hHookOldCbtFilter, code,
      wParam, lParam);

     #ifndef _AFXDLL
     if (bContextIsDLL)
     {
      ::UnhookWindowsHookEx(pThreadState->m_hHookOldCbtFilter);
      pThreadState->m_hHookOldCbtFilter = NULL;
     }
     #endif
     return lResult;
     }

     void CWnd::PreSubclassWindow
    {
     // no default processing
     }
     
     // always indirectly Accessed via AfxGetAfxWndProc
     WNDPROC AFXAPI AfxGetAfxWndProc
     {
     #ifdef _AFXDLL
     return AfxGetModuleState->m_pfnAfxWndProc;
     #else
     return &AfxWndProc;
     #endif
     }

      原來_AfxCbtFilterHook函數偷換了窗口函數,將原來的DefWndProc換成AfxWndProc函數.
                           ////////////////////////////////////////////////////
                           /*   8.MFC的“WndProc”函數   */
                           ////////////////////////////////////////////////////
       AfxWndProc函數就可以說是MFC的“WndProc”函數,它也是MFC中消息傳遞的開始,其代碼如下:
    //in wincore.cpp
    // The WndProc for all CWnd's and derived classes
    LRESULT CALLBACK AfxWndProc(HWND hWnd, UINT nMsg, WPARAM wParam, LPARAM lParam)
    {
     // special message which identifies the window as using AfxWndProc
     if (nMsg == WM_QUERYAFXWNDPROC)
      return 1;

     // all other messages route through message map
     CWnd* pWnd = CWnd::FromHandlePermanent(hWnd);
     ASSERT(pWnd != NULL);
     ASSERT(pWnd->m_hWnd == hWnd);
     return AfxCallWndProc(pWnd, hWnd, nMsg, wParam, lParam);
    }

    // Official way to send message to a CWnd
    LRESULT AFXAPI AfxCallWndProc(CWnd* pWnd, HWND hWnd, UINT nMsg,
     WPARAM wParam = 0, LPARAM lParam = 0)
    {
     _AFX_THREAD_STATE* pThreadState = _afxThreadState.GetData;
     MSG oldState = pThreadState->m_lastSentMsg;   // save for nesting
     pThreadState->m_lastSentMsg.hwnd = hWnd;
     pThreadState->m_lastSentMsg.message = nMsg;
     pThreadState->m_lastSentMsg.wParam = wParam;
     pThreadState->m_lastSentMsg.lParam = lParam;
             ...//
           // in debug builds and warn the user.
     LRESULT lResult;
     TRY
     {
    #ifndef _AFX_NO_OCC_SUPPORT
      // special case for WM_DESTROY
      if ((nMsg == WM_DESTROY) && (pWnd->m_pCtrlCont != NULL))
       pWnd->m_pCtrlCont->OnUIActivate(NULL);
    #endif
                   // special case for WM_INITDIALOG
      CRect rectOld;
      DWORD dwStyle = 0;
      if (nMsg == WM_INITDIALOG)
       _AfxPreInitDialog(pWnd, &rectOld, &dwStyle);

      // delegate to object's WindowProc
      lResult = pWnd->WindowProc(nMsg, wParam, lParam);//***

      // more special case for WM_INITDIALOG
      if (nMsg == WM_INITDIALOG)
       _AfxPostInitDialog(pWnd, rectOld, dwStyle);
     }
     CATCH_ALL(e)
     {
      ...//
     }
     END_CATCH_ALL

     pThreadState->m_lastSentMsg = oldState;
     return lResult;
    }

    // main WindowProc implementation
    LRESULT CWnd::WindowProc(UINT message, WPARAM wParam, LPARAM lParam)
    {
     // OnWndMsg does most of the work, except for DefWindowProc call
     LRESULT lResult = 0;
     if (!OnWndMsg(message, wParam, lParam, &lResult))
      lResult = DefWindowProc(message, wParam, lParam);
     return lResult;
    }

        BOOL CWnd::OnWndMsg(UINT message, WPARAM wParam, LPARAM lParam, LRESULT* pResult)
    {
     LRESULT lResult = 0;

     mmf.pfn = 0;
            // special case for commands
     if (message == WM_COMMAND)
     {
      if (OnCommand(wParam, lParam))
      {
       lResult = 1;
       goto LReturnTrue;
      }
      return FALSE;
     }
            // special case for notifies
     if (message == WM_NOTIFY)
     {
      NMHDR* pNMHDR = (NMHDR*)lParam;
      if (pNMHDR->hwndFrom != NULL && OnNotify(wParam, lParam, &lResult))
       goto LReturnTrue;
      return FALSE;
     }
            ...//
            const AFX_MSGMAP* pMessageMap; pMessageMap = GetMessageMap;
     UINT iHash; iHash = (LOWORD((DWORD_PTR)pMessageMap) ^ message) & (iHashMax-1);
     AfxLockGlobals(CRIT_WINMSGCACHE);
     AFX_MSG_CACHE* pMsgCache; pMsgCache = &_afxMsgCache[iHash];
     const AFX_MSGMAP_ENTRY* lpEntry;
     if (message == pMsgCache->nMsg && pMessageMap == pMsgCache->pMessageMap)
     {
      // cache hit
      lpEntry = pMsgCache->lpEntry;
      AfxUnlockGlobals(CRIT_WINMSGCACHE);
      if (lpEntry == NULL)
       return FALSE;

      // cache hit, and it needs to be handled
      if (message < 0xC000)
       goto LDispatch;
      else
       goto LDispatchRegistered;
     }
     else
     {
      // not in cache, look for it
      pMsgCache->nMsg = message;
      pMsgCache->pMessageMap = pMessageMap;

      #ifdef _AFXDLL
      for (/* pMessageMap already init'ed */; pMessageMap->pfnGetBaseMap != NULL;
       pMessageMap = (*pMessageMap->pfnGetBaseMap))
      #else
      for (/* pMessageMap already init'ed */; pMessageMap != NULL;
       pMessageMap = pMessageMap->pBaseMap)
      #endif
      {
       // Note: catch not so common but fatal mistake!!
       //      BEGIN_MESSAGE_MAP(CMyWnd, CMyWnd)
      #ifdef _AFXDLL
       ASSERT(pMessageMap != (*pMessageMap->pfnGetBaseMap));
      #else
       ASSERT(pMessageMap != pMessageMap->pBaseMap);
      #endif

       if (message < 0xC000)
       {
        // constant window message
        if ((lpEntry = AfxFindMessageEntry(pMessageMap->lpEntries,
         message, 0, 0)) != NULL)
        {
         pMsgCache->lpEntry = lpEntry;
         AfxUnlockGlobals(CRIT_WINMSGCACHE);
         goto LDispatch;
        }
       }
       else
       {
         // registered windows message
               lpEntry = pMessageMap->lpEntries;
       while ((lpEntry = AfxFindMessageEntry(lpEntry, 0xC000, 0, 0)) != NULL)
        {
         UINT* pnID = (UINT*)(lpEntry->nSig);
         ASSERT(*pnID >= 0xC000 || *pnID == 0);
          // must be successfully registered
         if (*pnID == message)
         {
          pMsgCache->lpEntry = lpEntry;
          AfxUnlockGlobals(CRIT_WINMSGCACHE);
          goto LDispatchRegistered;
         }
         lpEntry++;      // keep looking past this one
        }
       }
      }

      pMsgCache->lpEntry = NULL;
      AfxUnlockGlobals(CRIT_WINMSGCACHE);
      return FALSE;
     }
       LDispatch:
     ASSERT(message < 0xC000);

     mmf.pfn = lpEntry->pfn;

     switch (lpEntry->nSig)
     {
     default:
      ASSERT(FALSE);
      break;

     case AfxSig_b_D_v:
      lResult = (this->*mmf.pfn_b_D)(CDC::FromHandle(reinterpret_cast(wParam)));
      break;
            ...//
      LDispatchRegistered:    // for registered windows messages
     ASSERT(message >= 0xC000);
     ASSERT(sizeof(mmf) == sizeof(mmf.pfn));
     mmf.pfn = lpEntry->pfn;
     lResult = (this->*mmf.pfn_l_w_l)(wParam, lParam);

      LReturnTrue:
     if (pResult != NULL)
      *pResult = lResult;
     return TRUE;
     }
       該源代碼有整整700行,不知道是不是MFC源碼中最長的一個函數,在這里我只列出部分有代表性的源碼。從源代碼中大家也可以看得出該函數主要用于分辨消息并將消息交于其處理函數。MFC為了加快消息得分檢速度在AfxFindMessageEntry函數中甚至使用了匯編代碼。
    在CWnd::OnWndMsg中得不到處理的消息則交給CWnd::DefWindowProc(相當于MFC的默認DefWindowProc函數)處理,其代碼為:
     // Default CWnd implementation
     LRESULT CWnd::DefWindowProc(UINT nMsg, WPARAM wParam, LPARAM lParam)
     {
     if (m_pfnSuper != NULL)
      return ::CallWindowProc(m_pfnSuper, m_hWnd, nMsg, wParam, lParam);

     WNDPROC pfnWndProc;
     if ((pfnWndProc = *GetSuperWndProcAddr) == NULL)
      return ::DefWindowProc(m_hWnd, nMsg, wParam, lParam);
     else
      return ::CallWindowProc(pfnWndProc, m_hWnd, nMsg, wParam, lParam);
     }
      CWnd::DefWindowProc這調用了傳統win32程序員熟悉的::DefWindowProc(m_hWnd, nMsg, wParam, lParam);
    在挖掘上面源代碼的同時你也看到了消息的傳遞路線。在MFC中CWnd以及派生于CWnd的類都擁有虛函數
    CWnd::WndProc(...)。
     
                           /////////////////////////////////////////////////////
                           /* 9.MFC各類消息的"行走路徑 " */
                           /////////////////////////////////////////////////////
       在篇頭我就將消息分了類而且到目前我們已經了解了消息的傳遞機制了,下面我就具體的某類消息來看看其傳遞路徑。
       無論什么消息都有AfxWndProc進入,到達CWnd::OnWndMsg函數分檢消息;
       對于標準消息:
           標準消息一般都沿其消息映射表從本類到父類逐層查找其處理函數,若沒查到著交給::DefWindowProc        處理。
       對于命令消息:
           命令消息除了能像標準消息一樣從本類到父類逐層查找其處理函數外,有時他們可能還要拐彎。
           再回頭看看CWnd::OnWndMsg源碼是如何處理WM_COMMAND消息的:
            if (message == WM_COMMAND)
     {
      if (OnCommand(wParam, lParam))
      {
       lResult = 1;
       goto LReturnTrue;
      }
      return FALSE;
     }
            原來交給了CWnd::OnCommand(wParam, lParam)
            下面看看一個SDI程序中命令消息在Frame,View,Document以及CWinApp對象之間的傳遞路線。
            //in winfrm.cpp
            BOOL CFrameWnd::OnCommand(WPARAM wParam, LPARAM lParam)
     // return TRUE if command invocation was attempted
           {
     HWND hWndCtrl = (HWND)lParam;
     UINT nID = LOWORD(wParam);

     CFrameWnd* pFrameWnd = GetTopLevelFrame;
     ASSERT_VALID(pFrameWnd);
     if (pFrameWnd->m_bHelpMode && hWndCtrl == NULL &&
      nID != ID_HELP && nID != ID_DEFAULT_HELP && nID != ID_CONTEXT_HELP)
     {
      // route as help
      if (!SendMessage(WM_COMMANDHELP, 0, HID_BASE_COMMAND+nID))
       SendMessage(WM_COMMAND, ID_DEFAULT_HELP);
      return TRUE;
     }

     // route as normal command
     return CWnd::OnCommand(wParam, lParam);
            }
           
            //in wincore.cpp
            // CWnd command handling
            BOOL CWnd::OnCommand(WPARAM wParam, LPARAM lParam)
     // return TRUE if command invocation was attempted
           { ...//
             return OnCmdMsg(nID, nCode, NULL, NULL);//CFrameWnd::OnCmdMsg
           }
          
          // CFrameWnd command/message routing
           BOOL CFrameWnd::OnCmdMsg(UINT nID, int nCode, void* pExtra,
     AFX_CMDHANDLERINFO* pHandlerInfo)
          {
     CPUshRoutingFrame push(this);

     // 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;
           }
           Frame的COMMAND傳遞順序是View--->Frame本身-->CWinApp對象。
           
           //in viewcore.cpp
           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
     if (m_pDocument != NULL)
     {
      // special state for saving view before routing to document
      CPushRoutingView push(this);
      return m_pDocument->OnCmdMsg(nID, nCode, pExtra, pHandlerInfo);
     }

     return FALSE;
          }
          View的COMMAND傳遞順序是View本身--->Document

         //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;
         }
         Document的COMMAND傳遞順序是Document本身--->Document Template
         由這個例子我們可以清楚地看到WM_COMMAND的傳遞路徑了。
     
      對于子窗口通知:由于子窗口通知通常以WM_COMMAND形式出現,所以它的傳遞路徑也大致與WM_COMMAND相同,這里就不詳述了。  

                           ///////////////////////////////////////////
                           /*       10.收尾工作          */
                           /////////////////////////////////////////
      至此,MFC消息映射與消息傳遞的內幕已基本被揭開,若想更深刻的理解,你就得在平時的程序開發中奪觀察多思考了。

                           /////////////////////////////////////////
                           /*       11.下期預告          */
                           /////////////////////////////////////////

        MFC技術內幕系列之(五)-------《MFC文檔序列化內幕》

    RFID管理系統集成商 RFID中間件 條碼系統中間層 物聯網軟件集成
    最近免费观看高清韩国日本大全