MFC很古老的東西了,以前沒怎麼用過它,翻出來看了看,總結學習,不知有無錯誤,請各位指出。
預設情況下,OnCmdMsg是類CCmdTarget的虛函數,用來尋找和指派訊息,其具體處理流程如下:
以標準MFC SDI應用程式test為例:在啟動時,執行特定初始化:
ProcessShellCommand(cmdInfo);中對Shell Command 命令FileNew進行了指派處理:
//Shell command FILENEW process</p><p>//使用CCmdTraget的OnCmdMsg()執行該命令。此處由於衍生類別沒有覆蓋該虛函數。<br />BOOL CCmdTarget::OnCmdMsg(UINT nID, int nCode, void* pExtra,<br />AFX_CMDHANDLERINFO* pHandlerInfo)<br />{(1)</p><p>//AfxMsg_.h中定義了幾個nCode<br />//#define CN_COMMAND 0 // void ()<br />//#define CN_UPDATE_COMMAND_UI ((UINT)(-1)) // void (CCmdUI*)<br />//#define CN_EVENT ((UINT)(-2)) // OLE event<br />//#define CN_OLECOMMAND ((UINT)(-3)) // OLE document command<br />//#define CN_OLE_UNREGISTER ((UINT)(-4)) // OLE unregister<br />//首先對OLE事件特殊處理,此處沒有,跳過<br />if (nCode == CN_EVENT)<br />{<br />ASSERT(afxOccManager != NULL);<br />return afxOccManager->OnEvent(this, nID, (AFX_EVENT*)pExtra, pHandlerInfo);<br />}</p><p>// determine the message number and code (packed into nCode)<br />/*AfxWin.h中定義<br />struct AFX_MSGMAP<br />{<br />#ifdef _AFXDLL<br />const AFX_MSGMAP* (PASCAL* pfnGetBaseMap)();<br />#else<br />const AFX_MSGMAP* pBaseMap;<br />#endif<br />const AFX_MSGMAP_ENTRY* lpEntries;<br />};*/<br />const AFX_MSGMAP* pMessageMap;//定義了一個指向訊息映射的指標</p><p>/*AfxWin.h<br />struct AFX_MSGMAP_ENTRY<br />{<br />UINT nMessage; // windows message<br />UINT nCode; // control code or WM_NOTIFY code<br />UINT nID; // control ID (or 0 for windows messages)<br />UINT nLastID; // used for entries specifying a range of control id's<br />UINT nSig; // signature type (action) or pointer to message #<br />AFX_PMSG pfn; // routine to call (or special value)<br />};<br />*/<br />const AFX_MSGMAP_ENTRY* lpEntry; //定義一個指向訊息映射條目的指標<br />UINT nMsg = 0; //nMsg</p><p>//此處跳過對Ole Command的處理;<br />//<br />//判斷是否是UPDATE UI<br />if (nCode != CN_UPDATE_COMMAND_UI)<br />{<br />nMsg = HIWORD(nCode);<br />nCode = LOWORD(nCode);<br />}<br />// for backward compatibility HIWORD(nCode)==0 is WM_COMMAND<br />if (nMsg == 0)<br />nMsg = WM_COMMAND;//nMsg Now is 273</p><p>//尋找訊息映射,看看有沒有對該命令的處理常式<br />// look through message map to see if it applies to us<br />//迴圈調用GetMessageMap()尋找App對象中以及基類中是否存在對該命令的處理常式(handler)<br />//因為此時this指標指向的是CTestApp,所以此處的第一個GetMessageMap(),擷取到的是對CTestApp對象的MessageMap成員變數的指標</p><p>#ifdef _AFXDLL//使用MFC動態庫<br />for (pMessageMap = GetMessageMap(); pMessageMap != NULL;<br /> pMessageMap = (*pMessageMap->pfnGetBaseMap)())<br />#else<br />for (pMessageMap = GetMessageMap(); pMessageMap != NULL;<br /> pMessageMap = pMessageMap->pBaseMap)<br />#endif<br />{<br />// Note: catches BEGIN_MESSAGE_MAP(CMyClass, CMyClass)!<br />#ifdef _AFXDLL<br />ASSERT(pMessageMap != (*pMessageMap->pfnGetBaseMap)());//檢查是否有把宏定義錯的情況。衍生類別=基類<br />#else<br />ASSERT(pMessageMap != pMessageMap->pBaseMap);<br />#endif</p><p>lpEntry = AfxFindMessageEntry(pMessageMap->lpEntries, nMsg, nCode, nID);(2)//調用函數AfxFindMessageEntry尋找匹配命令的處理函數<br />if (lpEntry != NULL)//在該類的_messageEntries[]數組找到了handler//找到就退出函數,具體分析,見下文(2)<br />{<br />// found it<br />#ifdef _DEBUG<br />if (afxTraceFlags & traceCmdRouting)//如果定義了調試標誌,調試時,會在調試視窗輸出命令和通知跟蹤訊息<br />{<br />if (nCode == CN_COMMAND)<br />{<br />TRACE2("SENDING command id 0x%04X to %hs target./n", nID,<br />GetRuntimeClass()->m_lpszClassName);<br />}<br />else if (nCode > CN_COMMAND)<br />{<br />if (afxTraceFlags & traceWinMsg)<br />{<br />TRACE3("SENDING control notification %d from control id 0x%04X to %hs window./n",<br />nCode, nID, GetRuntimeClass()->m_lpszClassName);<br />}<br />}<br />}<br />#endif //_DEBUG//返回_AfxDispatchCmdMsg,返回結果<br />return _AfxDispatchCmdMsg(this, nID, nCode,(3)//以上面找到的訊息映射條目調用_AfxDispatchCmdMsg函數指派<br />lpEntry->pfn, pExtra, lpEntry->nSig, pHandlerInfo);//具體分析見下文(3)<br />}<br />}<br />return FALSE; // not handled</p><p>}(1)</p><p>(2)//尋找訊息映射條目的函數<br />//AfxFindMessageEntry(pMessageMap->lpEntries, nMsg, nCode, nID);<br />/////////////////////////////////////////////////////////////////////////////<br />// Routines for fast search of message maps</p><p>const AFX_MSGMAP_ENTRY* AFXAPI<br />AfxFindMessageEntry(const AFX_MSGMAP_ENTRY* lpEntry,<br />UINT nMsg, UINT nCode, UINT nID)<br />{<br />#if defined(_M_IX86) && !defined(_AFX_PORTABLE)//沒指定可移植標誌&X86<br />// 32-bit Intel 386/486 version.//針對32bit x86架構做了最佳化,使用部分彙編代碼提高尋找效率</p><p>ASSERT(offsetof(AFX_MSGMAP_ENTRY, nMessage) == 0);//檢查變數長度及位移<br />ASSERT(offsetof(AFX_MSGMAP_ENTRY, nCode) == 4);<br />ASSERT(offsetof(AFX_MSGMAP_ENTRY, nID) == 8);<br />ASSERT(offsetof(AFX_MSGMAP_ENTRY, nLastID) == 12);<br />ASSERT(offsetof(AFX_MSGMAP_ENTRY, nSig) == 16);</p><p>_asm<br />{<br />MOV EBX,lpEntry<br />MOV EAX,nMsg<br />MOV EDX,nCode<br />MOV ECX,nID<br />__loop:<br />CMP DWORD PTR [EBX+16],0 ; nSig (0 => end)//找到訊息映射數組末尾<br />JZ __failed//跳轉到失敗處理<br />CMP EAX,DWORD PTR [EBX] ; nMessage<br />JE __found_message//找到匹配的訊息,跳轉到nCode的匹配<br />__next:<br />ADD EBX,SIZE AFX_MSGMAP_ENTRY<br />JMP short __loop<br />__found_message:<br />CMP EDX,DWORD PTR [EBX+4] ; nCode<br />JNE __next//nCode不匹配,尋找下一條<br />// message and code good so far<br />// check the ID//nCode也匹配<br />CMP ECX,DWORD PTR [EBX+8] ; nID//驗證nID是否在條目中ID範圍內(nID-nLastID之間)<br />JB __next//比nID小,尋找下一條<br />CMP ECX,DWORD PTR [EBX+12] ; nLastID<br />JA __next//比nID大,尋找下一條<br />// found a match//nID也匹配<br />MOV lpEntry,EBX ; return EBX<br />JMP short __end//找到了<br />__failed:<br />XOR EAX,EAX ; return NULL<br />MOV lpEntry,EAX<br />__end:<br />}<br />return lpEntry;//返回該訊息映射條目的指標<br />#else // _AFX_PORTABLE<br />// C version of search routine//可移植版本,目的在於移植到非X86架構<br />while (lpEntry->nSig != AfxSig_end)//尋找方法同上述一致<br />{<br />if (lpEntry->nMessage == nMsg && lpEntry->nCode == nCode &&<br />nID >= lpEntry->nID && nID <= lpEntry->nLastID)<br />{<br />return lpEntry;<br />}<br />lpEntry++;<br />}<br />return NULL; // not found//找不到只能返回NULL<br />#endif // _AFX_PORTABLE<br />}</p><p>(3)//以上面找到的訊息映射條目調用_AfxDispatchCmdMsg函數指派</p><p>//_AfxDispatchCmdMsg(this, nID, nCode,lpEntry->pfn, pExtra, lpEntry->nSig, pHandlerInfo)</p><p>/////////////////////////////////////////////////////////////////////////////<br />// CCmdTarget windows message dispatching<br />//原型:<br />AFX_STATIC BOOL AFXAPI _AfxDispatchCmdMsg(CCmdTarget* pTarget, UINT nID, int nCode,<br />AFX_PMSG pfn, void* pExtra, UINT nSig, AFX_CMDHANDLERINFO* pHandlerInfo)<br />// return TRUE to stop routing<br />{<br />ASSERT_VALID(pTarget);<br />UNUSED(nCode); // unused in release builds</p><p>//定義了聯合結構 mmf,其具體結構見下文:(2)//MessageMapFunctions聯合結構<br />union MessageMapFunctions mmf;<br />mmf.pfn = pfn;//將訊息條目指標賦給pfn;<br />BOOL bResult = TRUE; // default is ok</p><p>if (pHandlerInfo != NULL)//pHandlerInfo不為NULL,填儲值後返回<br />{<br />// just fill in the information, don't do it<br />pHandlerInfo->pTarget = pTarget;<br />pHandlerInfo->pmf = mmf.pfn;<br />return TRUE;<br />}</p><p>switch (nSig)//根據訊息映射函數標記,匹配命令處理函數原型<br />{<br />case AfxSig_vv://匹配執行<br />// normal command or control notification<br />ASSERT(CN_COMMAND == 0); // CN_COMMAND same as BN_CLICKED<br />ASSERT(pExtra == NULL);<br />(pTarget->*mmf.pfn_COMMAND)();//調用mmf相應原型函數,無傳回值,直接返回bResault;<br />break;//此處即調用了訊息條目中的指定函數。CWinApp::OnFileNew()<br />//見下文(3):分析。<br />case AfxSig_bv:<br />// normal command or control notification<br />ASSERT(CN_COMMAND == 0); // CN_COMMAND same as BN_CLICKED<br />ASSERT(pExtra == NULL);<br />bResult = (pTarget->*mmf.pfn_bCOMMAND)();<br />break;</p><p>case AfxSig_vw:<br />// normal command or control notification in a range<br />ASSERT(CN_COMMAND == 0); // CN_COMMAND same as BN_CLICKED<br />ASSERT(pExtra == NULL);<br />(pTarget->*mmf.pfn_COMMAND_RANGE)(nID);<br />break;</p><p>case AfxSig_bw:<br />// extended command (passed ID, returns bContinue)<br />ASSERT(pExtra == NULL);<br />bResult = (pTarget->*mmf.pfn_COMMAND_EX)(nID);<br />break;</p><p>case AfxSig_vNMHDRpl:<br />{<br />AFX_NOTIFY* pNotify = (AFX_NOTIFY*)pExtra;<br />ASSERT(pNotify != NULL);<br />ASSERT(pNotify->pResult != NULL);<br />ASSERT(pNotify->pNMHDR != NULL);<br />(pTarget->*mmf.pfn_NOTIFY)(pNotify->pNMHDR, pNotify->pResult);<br />}<br />break;<br />case AfxSig_bNMHDRpl:<br />{<br />AFX_NOTIFY* pNotify = (AFX_NOTIFY*)pExtra;<br />ASSERT(pNotify != NULL);<br />ASSERT(pNotify->pResult != NULL);<br />ASSERT(pNotify->pNMHDR != NULL);<br />bResult = (pTarget->*mmf.pfn_bNOTIFY)(pNotify->pNMHDR, pNotify->pResult);<br />}<br />break;<br />case AfxSig_vwNMHDRpl:<br />{<br />AFX_NOTIFY* pNotify = (AFX_NOTIFY*)pExtra;<br />ASSERT(pNotify != NULL);<br />ASSERT(pNotify->pResult != NULL);<br />ASSERT(pNotify->pNMHDR != NULL);<br />(pTarget->*mmf.pfn_NOTIFY_RANGE)(nID, pNotify->pNMHDR,<br />pNotify->pResult);<br />}<br />break;<br />case AfxSig_bwNMHDRpl:<br />{<br />AFX_NOTIFY* pNotify = (AFX_NOTIFY*)pExtra;<br />ASSERT(pNotify != NULL);<br />ASSERT(pNotify->pResult != NULL);<br />ASSERT(pNotify->pNMHDR != NULL);<br />bResult = (pTarget->*mmf.pfn_NOTIFY_EX)(nID, pNotify->pNMHDR,<br />pNotify->pResult);<br />}<br />break;<br />case AfxSig_cmdui:<br />{<br />// ON_UPDATE_COMMAND_UI or ON_UPDATE_COMMAND_UI_REFLECT case<br />ASSERT(CN_UPDATE_COMMAND_UI == (UINT)-1);<br />ASSERT(nCode == CN_UPDATE_COMMAND_UI || nCode == 0xFFFF);<br />ASSERT(pExtra != NULL);<br />CCmdUI* pCmdUI = (CCmdUI*)pExtra;<br />ASSERT(!pCmdUI->m_bContinueRouting); // idle - not set<br />(pTarget->*mmf.pfn_UPDATE_COMMAND_UI)(pCmdUI);<br />bResult = !pCmdUI->m_bContinueRouting;<br />pCmdUI->m_bContinueRouting = FALSE; // go back to idle<br />}<br />break;</p><p>case AfxSig_cmduiw:<br />{<br />// ON_UPDATE_COMMAND_UI case<br />ASSERT(nCode == CN_UPDATE_COMMAND_UI);<br />ASSERT(pExtra != NULL);<br />CCmdUI* pCmdUI = (CCmdUI*)pExtra;<br />ASSERT(pCmdUI->m_nID == nID); // sanity assert<br />ASSERT(!pCmdUI->m_bContinueRouting); // idle - not set<br />(pTarget->*mmf.pfn_UPDATE_COMMAND_UI_RANGE)(pCmdUI, nID);<br />bResult = !pCmdUI->m_bContinueRouting;<br />pCmdUI->m_bContinueRouting = FALSE; // go back to idle<br />}<br />break;</p><p>// general extensibility hooks<br />case AfxSig_vpv:<br />(pTarget->*mmf.pfn_OTHER)(pExtra);<br />break;<br />case AfxSig_bpv:<br />bResult = (pTarget->*mmf.pfn_OTHER_EX)(pExtra);<br />break;</p><p>default: // illegal<br />ASSERT(FALSE);<br />return 0;<br />}<br />return bResult;<br />}</p><p>(2)//MessageMapFunctions聯合結構<br />//AfxIMpl.h</p><p>union MessageMapFunctions<br />{<br />AFX_PMSG pfn; // generic member function pointer</p><p>// specific type safe variants for WM_COMMAND and WM_NOTIFY messages<br />void (AFX_MSG_CALL CCmdTarget::*pfn_COMMAND)();<br />BOOL (AFX_MSG_CALL CCmdTarget::*pfn_bCOMMAND)();<br />void (AFX_MSG_CALL CCmdTarget::*pfn_COMMAND_RANGE)(UINT);<br />BOOL (AFX_MSG_CALL CCmdTarget::*pfn_COMMAND_EX)(UINT);</p><p>void (AFX_MSG_CALL CCmdTarget::*pfn_UPDATE_COMMAND_UI)(CCmdUI*);<br />void (AFX_MSG_CALL CCmdTarget::*pfn_UPDATE_COMMAND_UI_RANGE)(CCmdUI*, UINT);<br />void (AFX_MSG_CALL CCmdTarget::*pfn_OTHER)(void*);<br />BOOL (AFX_MSG_CALL CCmdTarget::*pfn_OTHER_EX)(void*);</p><p>void (AFX_MSG_CALL CCmdTarget::*pfn_NOTIFY)(NMHDR*, LRESULT*);<br />BOOL (AFX_MSG_CALL CCmdTarget::*pfn_bNOTIFY)(NMHDR*, LRESULT*);<br />void (AFX_MSG_CALL CCmdTarget::*pfn_NOTIFY_RANGE)(UINT, NMHDR*, LRESULT*);<br />BOOL (AFX_MSG_CALL CCmdTarget::*pfn_NOTIFY_EX)(UINT, NMHDR*, LRESULT*);</p><p>// type safe variant for thread messages</p><p>void (AFX_MSG_CALL CWinThread::*pfn_THREAD)(WPARAM, LPARAM);</p><p>// specific type safe variants for WM-style messages<br />BOOL (AFX_MSG_CALL CWnd::*pfn_bD)(CDC*);<br />BOOL (AFX_MSG_CALL CWnd::*pfn_bb)(BOOL);<br />BOOL (AFX_MSG_CALL CWnd::*pfn_bWww)(CWnd*, UINT, UINT);<br />BOOL (AFX_MSG_CALL CWnd::*pfn_bHELPINFO)(HELPINFO*);<br />BOOL (AFX_MSG_CALL CWnd::*pfn_bWCDS)(CWnd*, COPYDATASTRUCT*);<br />HBRUSH (AFX_MSG_CALL CWnd::*pfn_hDWw)(CDC*, CWnd*, UINT);<br />HBRUSH (AFX_MSG_CALL CWnd::*pfn_hDw)(CDC*, UINT);<br />int (AFX_MSG_CALL CWnd::*pfn_iwWw)(UINT, CWnd*, UINT);<br />int (AFX_MSG_CALL CWnd::*pfn_iww)(UINT, UINT);<br />int (AFX_MSG_CALL CWnd::*pfn_iWww)(CWnd*, UINT, UINT);<br />int (AFX_MSG_CALL CWnd::*pfn_is)(LPTSTR);<br />LRESULT (AFX_MSG_CALL CWnd::*pfn_lwl)(WPARAM, LPARAM);<br />LRESULT (AFX_MSG_CALL CWnd::*pfn_lwwM)(UINT, UINT, CMenu*);<br />void (AFX_MSG_CALL CWnd::*pfn_vv)(void);</p><p>void (AFX_MSG_CALL CWnd::*pfn_vw)(UINT);<br />void (AFX_MSG_CALL CWnd::*pfn_vww)(UINT, UINT);<br />void (AFX_MSG_CALL CWnd::*pfn_vvii)(int, int);<br />void (AFX_MSG_CALL CWnd::*pfn_vwww)(UINT, UINT, UINT);<br />void (AFX_MSG_CALL CWnd::*pfn_vwii)(UINT, int, int);<br />void (AFX_MSG_CALL CWnd::*pfn_vwl)(WPARAM, LPARAM);<br />void (AFX_MSG_CALL CWnd::*pfn_vbWW)(BOOL, CWnd*, CWnd*);<br />void (AFX_MSG_CALL CWnd::*pfn_vD)(CDC*);<br />void (AFX_MSG_CALL CWnd::*pfn_vM)(CMenu*);<br />void (AFX_MSG_CALL CWnd::*pfn_vMwb)(CMenu*, UINT, BOOL);</p><p>void (AFX_MSG_CALL CWnd::*pfn_vW)(CWnd*);<br />void (AFX_MSG_CALL CWnd::*pfn_vWww)(CWnd*, UINT, UINT);<br />void (AFX_MSG_CALL CWnd::*pfn_vWp)(CWnd*, CPoint);<br />void (AFX_MSG_CALL CWnd::*pfn_vWh)(CWnd*, HANDLE);<br />void (AFX_MSG_CALL CWnd::*pfn_vwW)(UINT, CWnd*);<br />void (AFX_MSG_CALL CWnd::*pfn_vwWb)(UINT, CWnd*, BOOL);<br />void (AFX_MSG_CALL CWnd::*pfn_vwwW)(UINT, UINT, CWnd*);<br />void (AFX_MSG_CALL CWnd::*pfn_vwwx)(UINT, UINT);<br />void (AFX_MSG_CALL CWnd::*pfn_vs)(LPTSTR);<br />void (AFX_MSG_CALL CWnd::*pfn_vOWNER)(int, LPTSTR); // force return TRUE<br />int (AFX_MSG_CALL CWnd::*pfn_iis)(int, LPTSTR);<br />UINT (AFX_MSG_CALL CWnd::*pfn_wp)(CPoint);<br />UINT (AFX_MSG_CALL CWnd::*pfn_wv)(void);<br />void (AFX_MSG_CALL CWnd::*pfn_vPOS)(WINDOWPOS*);<br />void (AFX_MSG_CALL CWnd::*pfn_vCALC)(BOOL, NCCALCSIZE_PARAMS*);<br />void (AFX_MSG_CALL CWnd::*pfn_vwp)(UINT, CPoint);<br />void (AFX_MSG_CALL CWnd::*pfn_vwwh)(UINT, UINT, HANDLE);<br />BOOL (AFX_MSG_CALL CWnd::*pfn_bwsp)(UINT, short, CPoint);<br />void (AFX_MSG_CALL CWnd::*pfn_vws)(UINT, LPCTSTR);<br />};(2)</p><p>