MFC漫谈(五)——消息的路由(2)

MFC漫谈(五)——消息的路由(2)

继续上一个主题


直线上溯的消息

上次说到消息被转发到了AfxWndProc,继续。

LRESULT CALLBACK
AfxWndProc(HWND hWnd, UINT nMsg, WPARAM wParam, LPARAM lParam) 
{
    
// …

    
// all other messages route through message map
    CWnd* pWnd = CWnd::FromHandlePermanent(hWnd);
    
// …
    return AfxCallWndProc(pWnd, hWnd, nMsg, wParam, lParam);
}


LRESULT AFXAPI AfxCallWndProc(CWnd
*  pWnd, HWND hWnd, UINT nMsg,
                        WPARAM wParam 
=   0 , LPARAM lParam  =   0 {
    
// …
    
// Catch exceptions thrown outside the scope of a callback
    
// in debug builds and warn the user.
    LRESULT lResult;
    
// …
    
// delegate to object's WindowProc
    lResult = pWnd->WindowProc(nMsg, wParam, lParam);
    
// …
    return lResult;
}

最后,消息被传到了WindowProc中,这是一个CWnd类中的虚函数,而MFC则通过虚函数机制把消息的处理直接转发到相应窗口的窗口过程(例如CFrameWnd),这里,我们来看CWnd::WndProc

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

首先利用OnWndMsg处理消息,如果OnWndMsg没能处理消息,调用DefWindowProc。先来看OnWndMsg,这是一个CWnd类的虚函数,这个函数的逻辑很简单,如果消息是WM_COMMAND或WM_NOTIFY,则把消息分别交给OnCommand和OnNotify处理,否则首先在MFC内建的消息缓存中查找消息,如果命中但没有相应的处理函数,则返回FALSE(这样的话会交由CWnd::DefWindowProc处理),如果命中,则进一步判断是用户自己注册的消息还是标准Windows消息,如果是前者,就跳到标LDispatchRegistered处理,调用相应的消息处理函数,否则就跳到LDispatch处理,调用正确的消息处理函数。

如果消息不在缓存中,那么就沿着某个类的继承路线,由AfxFindMessageEntry在每一个类的消息映射表中查找,如果找到匹配项,同样按照是否是用户注册的消息的逻辑对消息进行处理,如果始终没有找到匹配项,则返回FALSE,交由CWnd::DefWindowProc处理。

BOOL CWnd::OnWndMsg(UINT message, WPARAM wParam, LPARAM lParam, LRESULT *  pResult)
{
    
//  
    
//  special case for commands
     if  (message  ==  WM_COMMAND)
    
{
        
if  (OnCommand(wParam, lParam))   //  对WM_COMMAND交给OnCommand处理
         {
            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();  //  获取类的消息映射表
    
//  
    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)  //  在缓存中,但是没有相应的表项,返回FALSE
         return  FALSE;

        
//  cache hit, and it needs to be handled
         if  (message  <   0xC000 //  否则,按照是否是用户自定义的消息, 跳转到相应的位置
             goto  LDispatch;  //  标准Windows消息
         else
            
goto  LDispatchRegistered;  //  用户自定义消息
    }

    
else   //  如果消息不在缓存中,就只好挨家挨户检查一番
     {
        
//  not in cache, look for it
        pMsgCache -> nMsg  =  message;
        pMsgCache
-> pMessageMap  =  pMessageMap;
        
//  下面这个for循环从派生类到基类检查每一个类的消息映射表
         for  ( /**/ /*  pMessageMap already init'ed  */ ; pMessageMap  !=  NULL;
            pMessageMap 
=  pMessageMap -> pBaseMap)   {
            
if  (message  <   0xC000 //  如果消息是Windows标准消息
             {
                
//  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
                }

            }

        }

        
//  即不在缓存中,所有的类也都对此消息置之不理,那么就返回FALSE,交由CWnd::DefWndProc
        
//  处理
        pMsgCache -> lpEntry  =  NULL;
        AfxUnlockGlobals(CRIT_WINMSGCACHE);
        
return  FALSE;
    }

    
//  下面是对Windows标准消息和用户自定义消息的处理
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 ;

    
case  AfxSig_b_b_v:
        lResult 
=  ( this ->* mmf.pfn_b_b)(static_cast < BOOL > (wParam));
        
break ;

    
case  AfxSig_b_u_v:
        lResult 
=  ( this ->* mmf.pfn_b_u)(static_cast < UINT > (wParam));
        
break ;

    
case  AfxSig_b_h_v:
        lResult 
=  ( this ->* mmf.pfn_b_h)(reinterpret_cast < HANDLE > (wParam));
        
break ;
        
//  … …
    }

 
goto  LReturnTrue;
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;
}

写到这里,对于直线上溯的消息的处理过程,应该是很清楚了,概括一下,就是先准备好两个处理过程,一个用来处理标准Windows消息,一个用来处理用户自定义消息,之后,根据消息是不是在缓存中,进行不同的查找,如果找到,根据消息的类型转到不同的处理过程中去,如果处理过了,就返回TRUE,否则返回FALSE,交由CWnd::DefWindowProc处理。到此,关于直线上溯消息的处理就结束了,很简单,无非就是从派生类到基类的一个比较操作,真正复杂些的是MFC对于WM_COMMAND消息的处理,我们后面再说了。

你可能感兴趣的:(MFC漫谈(五)——消息的路由(2))