熟悉Dlg消息处理函数的调用流程,基于Frame8进行模拟。
断点下的调用栈:
CDlgSmbList::OnButtonSearch() line 117
_AfxDispatchCmdMsg(CCmdTarget * 0x0013fd50 {CDlgSmbList hWnd=0x00010c4a}, unsigned int 1028, int 0, void (void)* 0x004012b2 CDlgSmbList::OnButtonSearch(void), void * 0x00000000, unsigned int 12, AFX_CMDHANDLERINFO * 0x00000000) line 88
CCmdTarget::OnCmdMsg(unsigned int 1028, int 0, void * 0x00000000, AFX_CMDHANDLERINFO * 0x00000000) line 302 + 39 bytes
CDialog::OnCmdMsg(unsigned int 1028, int 0, void * 0x00000000, AFX_CMDHANDLERINFO * 0x00000000) line 97 + 24 bytes
CWnd::OnCommand(unsigned int 1028, long 68708) line 2099
CWnd::OnWndMsg(unsigned int 273, unsigned int 1028, long 68708, long * 0x0013f0e4) line 1608 + 28 bytes
CWnd::WindowProc(unsigned int 273, unsigned int 1028, long 68708) line 1596 + 30 bytes
AfxCallWndProc(CWnd * 0x0013fd50 {CDlgSmbList hWnd=0x00010c4a}, HWND__ * 0x00010c4a, unsigned int 273, unsigned int 1028, long 68708) line 215 + 26 bytes
AfxWndProc(HWND__ * 0x00010c4a, unsigned int 273, unsigned int 1028, long 68708) line 379
顺藤摸瓜看代码:
1,接收到消息,回调函数AfxWndProc被调用
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);
if (pWnd == NULL || pWnd->m_hWnd != hWnd)
return ::DefWindowProc(hWnd, nMsg, wParam, lParam);
return AfxCallWndProc(pWnd, hWnd, nMsg, wParam, lParam);
}
2,调用AfxCallWndProc,再delegate to object's WindowProc
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;
// Catch exceptions thrown outside the scope of a callback
// in debug builds and warn the user.
LRESULT lResult;
TRY
{
// 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)
{
CWinThread* pWinThread = AfxGetThread();
if ( pWinThread != NULL )
{
lResult = pWinThread->ProcessWndProcException(e, &pThreadState->m_lastSentMsg);
TRACE1("Warning: Uncaught exception in WindowProc (returning %ld)./n",
lResult);
}
else
{
TRACE0("Warning: Uncaught exception in WindowProc./n");
lResult = 0;
}
DELETE_EXCEPTION(e);
}
END_CATCH_ALL
pThreadState->m_lastSentMsg = oldState;
return lResult;
}
3,pWnd->WindowProc(nMsg, wParam, lParam)这里的pWnd是指向CDlgSmbList的
CDlgSmbList没有重写WindowProc,其父类class CDlgSmbList : public CDialog也没有重写,继续看父类的父类Cwnd:
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;
}
实际上是调用CDlgSmbList::OnWndMsg,Item4进行跟踪。
4,CDlgSmbList::OnWndMsg,进入WM_COMMAND分支,再调用CDlgSmbList:: OnCommand
CDlgSmbList没有重写OnWndMsg,其父类class CDlgSmbList : public CDialog也没有重写,继续看父类的父类Cwnd:
BOOL CWnd::OnWndMsg(UINT message, WPARAM wParam, LPARAM lParam, LRESULT* pResult)
{
LRESULT lResult = 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;
}
// special case for activation
if (message == WM_ACTIVATE)
_AfxHandleActivate(this, wParam, CWnd::FromHandle((HWND)lParam));
// special case for set cursor HTERROR
if (message == WM_SETCURSOR &&
_AfxHandleSetCursor(this, (short)LOWORD(lParam), HIWORD(lParam)))
{
lResult = 1;
goto LReturnTrue;
}
……
}
5,CDlgSmbList:: OnCommand中继续调用CDlgSmbList:: OnCmdMsg
CDlgSmbList没有重写OnCommand,其父类class CDlgSmbList : public CDialog也没有重写,继续看父类的父类Cwnd:
BOOL CWnd::OnCommand(WPARAM wParam, LPARAM lParam)
// return TRUE if command invocation was attempted
{
UINT nID = LOWORD(wParam);
HWND hWndCtrl = (HWND)lParam;
int nCode = HIWORD(wParam);
// default routing for command messages (through closure table)
if (hWndCtrl == NULL)
{
// zero IDs for normal commands are not allowed
if (nID == 0)
return FALSE;
// make sure command has not become disabled before routing
CTestCmdUI state;
state.m_nID = nID;
OnCmdMsg(nID, CN_UPDATE_COMMAND_UI, &state, NULL);
if (!state.m_bEnabled)
{
TRACE1("Warning: not executing disabled command %d/n", nID);
return TRUE;
}
// menu or accelerator
nCode = CN_COMMAND;
}
else
{
// control notification
ASSERT(nID == 0 || ::IsWindow(hWndCtrl));
if (_afxThreadState->m_hLockoutNotifyWindow == m_hWnd)
return TRUE; // locked out - ignore control notification
// reflect notification to child window control
if (ReflectLastMsg(hWndCtrl))
return TRUE; // eaten by child
// zero IDs for normal commands are not allowed
if (nID == 0)
return FALSE;
}
return OnCmdMsg(nID, nCode, NULL, NULL);
}
6,CDlgSmbList:: OnCmdMsg中显式调用CWnd::OnCmdMsg
CDlgSmbList没有重写OnCommand,其父类class CDlgSmbList : public CDialog进行了重写:
BOOL CDialog::OnCmdMsg(UINT nID, int nCode, void* pExtra,
AFX_CMDHANDLERINFO* pHandlerInfo)
{
// 显式调用CWnd::OnCmdMsg
if (CWnd::OnCmdMsg(nID, nCode, pExtra, pHandlerInfo))
return TRUE;
……
}
7,CWnd::OnCmdMsg进行遍历分发
OnCmdMsg没有重写OnCmdMsg,其父类CCmdTarget进行了重写:
BOOL CCmdTarget::OnCmdMsg(UINT nID, int nCode, void* pExtra,
AFX_CMDHANDLERINFO* pHandlerInfo)
{
#ifndef _AFX_NO_OCC_SUPPORT
// OLE control events are a special case
if (nCode == CN_EVENT)
{
ASSERT(afxOccManager != NULL);
return afxOccManager->OnEvent(this, nID, (AFX_EVENT*)pExtra, pHandlerInfo);
}
#endif // !_AFX_NO_OCC_SUPPORT
// determine the message number and code (packed into nCode)
const AFX_MSGMAP* pMessageMap;
const AFX_MSGMAP_ENTRY* lpEntry;
UINT nMsg = 0;
#ifndef _AFX_NO_DOCOBJECT_SUPPORT
if (nCode == CN_OLECOMMAND)
{
BOOL bResult = FALSE;
const AFX_OLECMDMAP* pOleCommandMap;
const AFX_OLECMDMAP_ENTRY* pEntry;
COleCmdUI* pUI = (COleCmdUI*) pExtra;
const GUID* pguidCmdGroup = pUI->m_pguidCmdGroup;
#ifdef _AFXDLL
for (pOleCommandMap = GetCommandMap(); pOleCommandMap != NULL && !bResult;
pOleCommandMap = pOleCommandMap->pfnGetBaseMap())
#else
for (pOleCommandMap = GetCommandMap(); pOleCommandMap != NULL && !bResult;
pOleCommandMap = pOleCommandMap->pBaseMap)
#endif
{
for (pEntry = pOleCommandMap->lpEntries;
pEntry->cmdID != 0 && pEntry->nID != 0 && !bResult;
pEntry++)
{
if (nID == pEntry->cmdID &&
IsEqualNULLGuid(pguidCmdGroup, pEntry->pguid))
{
pUI->m_nID = pEntry->nID;
bResult = TRUE;
}
}
}
return bResult;
}
#endif
if (nCode != CN_UPDATE_COMMAND_UI)
{
nMsg = HIWORD(nCode);
nCode = LOWORD(nCode);
}
// for backward compatibility HIWORD(nCode)==0 is WM_COMMAND
if (nMsg == 0)
nMsg = WM_COMMAND;
// look through message map to see if it applies to us
#ifdef _AFXDLL
for (pMessageMap = GetMessageMap(); pMessageMap != NULL;
pMessageMap = (*pMessageMap->pfnGetBaseMap)())
#else
for (pMessageMap = GetMessageMap(); pMessageMap != NULL;
pMessageMap = pMessageMap->pBaseMap)
#endif
{
// Note: catches BEGIN_MESSAGE_MAP(CMyClass, CMyClass)!
lpEntry = AfxFindMessageEntry(pMessageMap->lpEntries, nMsg, nCode, nID);
if (lpEntry != NULL)
{
// found it
return _AfxDispatchCmdMsg(this, nID, nCode,
lpEntry->pfn, pExtra, lpEntry->nSig, pHandlerInfo);
}
}
return FALSE; // not handled
}
8,_AfxDispatchCmdMsg全局函数,调用到具体的处理函数
AFX_STATIC BOOL AFXAPI _AfxDispatchCmdMsg(CCmdTarget* pTarget, UINT nID, int nCode,
AFX_PMSG pfn, void* pExtra, UINT nSig, AFX_CMDHANDLERINFO* pHandlerInfo)
// return TRUE to stop routing
{
ASSERT_VALID(pTarget);
UNUSED(nCode); // unused in release builds
union MessageMapFunctions mmf;
mmf.pfn = pfn;
BOOL bResult = TRUE; // default is ok
if (pHandlerInfo != NULL)
{
// just fill in the information, don't do it
pHandlerInfo->pTarget = pTarget;
pHandlerInfo->pmf = mmf.pfn;
return TRUE;
}
switch (nSig)
{
case AfxSig_vv:
// normal command or control notification
ASSERT(CN_COMMAND == 0); // CN_COMMAND same as BN_CLICKED
ASSERT(pExtra == NULL);
(pTarget->*mmf.pfn_COMMAND)();
break;
case AfxSig_bv:
// normal command or control notification
ASSERT(CN_COMMAND == 0); // CN_COMMAND same as BN_CLICKED
ASSERT(pExtra == NULL);
bResult = (pTarget->*mmf.pfn_bCOMMAND)();
break;
……
default: // illegal
ASSERT(FALSE);
return 0;
}
return bResult;
}