標題: MFC消息映射與消息傳遞 [打印本頁]

作者: 51黑tt    時間: 2016-3-5 18:42
標題: MFC消息映射與消息傳遞
     Windows操作系統(tǒng)是以消息為基礎,事件驅(qū)動的。作為程序員了解操作系統(tǒng)的消息傳遞機制是非常必要的。Microsoft的MFC又它自己的一套支持Windows操作系統(tǒng)消息機制的技術--消息映射(Message Mapping)和命令傳遞(Command Routing),在這篇文章中我就詳細的挖掘一下MFC的消息映射技術以及命令傳遞技術。
正文:
                       ///////////////////////////////////////////////
                       /*     1.Windows消息概覽      */
                       //////////////////////////////////////////////
    對于消息,程序員應該不陌生。WM_CREATE,WM_PAINT等等都是Windows程序設計中必不可缺少的組成部分。大多有關MFC Win32編程的書籍都將Windows消息分為三大類即:
    * 標準消息:   任何以WM_開頭的消息(WM_COMMAND除外);如:WM_QUIT,WM_CREATE;
    * 命令消息:   WM_COMMAND;
    * 子窗口通知: 由子窗口(大多為控件)產(chǎn)生并發(fā)送到該控件所屬的父窗口的消息。(注意:此類消息也                    以WM_COMMAND形式出現(xiàn))
    消息類型我們已經(jīng)了解了,下面我們就來看看消息映射是如何工作的:
                       //////////////////////////////////////////////////////
                       /* 2.MFC消息映射網(wǎng)的組成元素 */
                       //////////////////////////////////////////////////////   
   我的前幾篇文章中涉及到了MFC內(nèi)部建立的一些“網(wǎng)”技術,比如“執(zhí)行期類型識別網(wǎng)”等,這回我們將建立一個消息映射網(wǎng),這個網(wǎng)的建立與前面相同的是它也利用了一些神秘的宏。下面我們就來掀開它們的神秘面紗。
   我們先簡單地看看這些宏在程序源文件中的什么地方?
   //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和消息入口結構數(shù)組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)();//基類的映射表指針,本程序?qū)⑹褂?br /> #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<AFX_PMSG> (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<AFX_PMSG> ( OnMyCommand) },        
          {0, 0, 0, 0, AfxSig_end, (AFX_PMSG)0 }
};
   現(xiàn)在一切都清楚了吧!
                       //////////////////////////////////////////////////
                       /*    5.MFC消息映射網(wǎng)的連接   */
                       //////////////////////////////////////////////////
   MFC消息映射網(wǎng)的連接也是在初始化過程中完成的,其建立過程很簡單。主要有關成員有:
   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消息映射網(wǎng)的連接
就建立了,最終的基類都是CCmdTarget
                       //////////////////////////////////////////////////
                       /*    6.MFC命令傳遞機制概述   */
                       //////////////////////////////////////////////////
   有了MFC消息映射網(wǎng)就為命令傳遞打下了堅實的基礎。Win32API程序員都熟悉傳統(tǒng)的API編程都有一個
WndProc回調(diào)函數(shù)來集中處理各種的Windows消息,然而在MFC中我們卻怎么也看不見WndProc回調(diào)函數(shù)的蹤影了。而MFC命令傳遞機制恰是為了將各種消息“拐彎抹角”地送到各個對應的"WndProc"函數(shù)的一種技術。MFC是如何將各種Windows消息準確的送到期該區(qū)的地方呢? MFC使用了鉤子函數(shù)等技術來保證其準確性和全面性。
   不知大家是否還記得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函數(shù),那么實際上是否消息都是由它處理的呢?顯然不可能,MFC又不知道我們要處理什么消息。那么還有什么函數(shù)能幫我們處理消息呢?這就是我要講的; 在MFC技術內(nèi)幕系列之(二)----《 MFC文檔視圖結構內(nèi)幕》中曾提到“CWnd::CreateEx函數(shù)調(diào)用了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();
}
...//
}
該函數(shù)設置了消息鉤子,其鉤子處理函數(shù)為_AfxCbtFilterHook;這里簡介一下鉤子函數(shù):
用我的理解,鉤子就是能給你一個在某個消息到達其默認的處理函數(shù)之前處理該消息機會的工具。
與鉤子有關的函數(shù)主要有三個:
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);
關于這三個函數(shù)我也不想多解釋,大家看看MFC有關文檔吧!這里主要講的是在AfxHookWindowCreate(CWnd* pWnd)中注冊的鉤子函數(shù)的類型(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.偷換“窗口函數(shù)”      */
                       /////////////////////////////////////////////
   這會知道了吧,當發(fā)生窗口(包括子窗口)發(fā)生被激活,創(chuàng)建,撤銷,最小化等時候,應用程序?qū)⒄{(diào)用
_AfxCbtFilterHook函數(shù);下面就讓我們看看_AfxCbtFilterHook函數(shù)做了個啥:
//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函數(shù)偷換了窗口函數(shù),將原來的DefWndProc換成AfxWndProc函數(shù).
                       ////////////////////////////////////////////////////
                       /*   8.MFC的“WndProc”函數(shù)   */
                       ////////////////////////////////////////////////////
   AfxWndProc函數(shù)就可以說是MFC的“WndProc”函數(shù),它也是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;
union MessageMapFunctions mmf;
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<HDC>(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源碼中最長的一個函數(shù),在這里我只列出部分有代表性的源碼。從源代碼中大家也可以看得出該函數(shù)主要用于分辨消息并將消息交于其處理函數(shù)。MFC為了加快消息得分檢速度在AfxFindMessageEntry函數(shù)中甚至使用了匯編代碼。
在CWnd::OnWndMsg中得不到處理的消息則交給CWnd::DefWindowProc(相當于MFC的默認DefWindowProc函數(shù))處理,其代碼為:
// 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這調(diào)用了傳統(tǒng)win32程序員熟悉的::DefWindowProc(m_hWnd, nMsg, wParam, lParam);
在挖掘上面源代碼的同時你也看到了消息的傳遞路線。在MFC中CWnd以及派生于CWnd的類都擁有虛函數(shù)
CWnd::WndProc(...)。
                       /////////////////////////////////////////////////////
                       /* 9.MFC各類消息的"行走路徑 " */
                       /////////////////////////////////////////////////////
   在篇頭我就將消息分了類而且到目前我們已經(jīng)了解了消息的傳遞機制了,下面我就具體的某類消息來看看其傳遞路徑。
   無論什么消息都有AfxWndProc進入,到達CWnd::OnWndMsg函數(shù)分檢消息;
   對于標準消息:
       標準消息一般都沿其消息映射表從本類到父類逐層查找其處理函數(shù),若沒查到著交給::DefWindowProc        處理。
   對于命令消息:
       命令消息除了能像標準消息一樣從本類到父類逐層查找其處理函數(shù)外,有時他們可能還要拐彎。
       再回頭看看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形式出現(xiàn),所以它的傳遞路徑也大致與WM_COMMAND相同,這里就不詳述了。
                       ///////////////////////////////////////////
                       /*       10.收尾工作          */
                       /////////////////////////////////////////






歡迎光臨 (http://www.torrancerestoration.com/bbs/) Powered by Discuz! X3.1