牧光小院

被约束的日日夜夜,停不下来的时间。

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消息的处理,我们后面再说了。

posted on 2006-05-19 09:08 nacci 阅读(4584) 评论(3)  编辑 收藏 引用 所属分类: C++漫谈

评论

# re: MFC漫谈(五)——消息的路由(2) 2006-09-05 11:21 lll

11  回复  更多评论   

# re: MFC漫谈(五)——消息的路由(2) 2007-10-11 16:05 seamonst

全明了。非常感谢!
期待对OnCommand的进一步分析。  回复  更多评论   

# re: MFC漫谈(五)——消息的路由(2)[未登录] 2008-07-08 15:03 sky

very good! thanks! Up!  回复  更多评论   


只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理


<2006年5月>
30123456
78910111213
14151617181920
21222324252627
28293031123
45678910

导航

统计

常用链接

留言簿(2)

随笔分类

收藏夹

大家的声音

积分与排名

最新评论

阅读排行榜

评论排行榜