继续上一个主题
直线上溯的消息
上次说到消息被转发到了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消息的处理,我们后面再说了。