命令傳遞(Command routing)
消息如果是僅僅從派生類流向父類,那就非常簡單了。然而MFC用來處理消息的C++類,並不是單線發展的。document/view也具有處理消息的能力。因此,消息應該有橫向流動的機會。
MFC對消息循環的規定爲:
1:若是一般的windows消息(WM_xx)則一定是由派生類流向基類。
2:如果是WM_COMMAND消息,就非常複雜了。要區分接受者的類型:
1:接受者若爲Frame窗口:處理次序爲:View-> Frame窗口本身->CWinApp類。
2:接受者若爲View :處理次序爲:View本身->Document;
3:接受者若爲Document:處理次序爲:Document本身->Document template
因此,接下來我們的任務就是仿真以上的消息傳遞路線。
以下爲需要添加的函數:
全局函數AfxWndProc,它是整個消息循環的起始點,本來應該在CWinThread::Run中被調用,每調用一次就推送一個消息。模擬windows的disPatch函數。
LRESULT AfxWndPro(HWND hWnd,UINT nMsg,WPARAM
wParam,LPARAM lParam,CWnd *pWnd)
{
cout<<"AfxWndProc()"<<endl;
return AfxCallWndProc (pWnd,hWnd,nMsg,wParam,lParam);
}
LRESULT AfxCallWndProc(CWnd*pWnd,HWND hWnd,UINT nMsg,
WPARAM wParam,LPARAM lParam)
{
cout<<"AfxCallWndProc"<<endl;
LRESULT lResult=pWnd->windowProc(nMsg,wParam,lParam);
return lResult;
}
全局函數AfxCallWndProc用於調用接受消息的類的消息處理函數。pWnd->WindowProc調用哪個函數,取決於pWnd指向的對象類型。
如果pWnd指向CMyFrameWnd對象,則調用CFrameWnd::WindowProc。因爲CFrameWnd斌沒有改寫WindowProc,因此調用的是CWnd::WindowProc。。
如果pWnd指向CMyView對象,那麼調用CView::windowProc。而CMyView沒有改寫WIndowProc所以調用的是CWnd::WindowProc。
在CWnd::windowProc中,首先判斷消息是否爲WM_COMMAND消息,
如不是,則傳遞給父類進行處理。
如果是WM_COMMAND消息,CWnd::windowProc調用OnCommand。此函數爲虛函數。有以下幾種情況:
1:如果this指向CMyFrameWnd對象,則調用的是CFrameWnd::OnComamnd。
2:如果this指向CMyView對象,那麼調用的是CView::OnCommand。因爲CView並沒有改寫OnComamnd所以調用的是CWnd::OnCommand。
bool CFrameWnd::OnComamnd(WPARAM wParam,LPARAM lParam)
{
cout<<"CFrameWnd::OnCommand()"<<endl;
return CWnd::OnCommand(wParam,lParam);
}
bool CWnd::OnComamnd(WPARAM wParam,LPARAM lParam)
{
cout<<"CWnd::OnComamnd()"<<endl;
return OnCmdMsg(0,0);
}
OnCmdMsg仍然是虛函數,
1:如果this指向CMyFrameWnd對象,那麼調用的是CFrameWnd::OnCmdMsg。
2:如果this指向CMyView對象,則調用CView::OnCmdMsg。
3:如果this指向CMyDoc對象,則調用CDocument::OnCmdMsg。
4:如果this指向CM與WinApp對象,則調用CWinApp:: nCmdMsg。因爲CWinApp沒有改寫OnCmdMsg因此調用的是CCmdTarget::OnCmdMsg。
Bool CFrameWnd::OnCmdMsg(UINT nID,int nCode)
{
cout<<"CFrameWnd::OnCmdMsg()"<<endl;
CView*pView=GetActiveView();
if(pView->OnCmdMsg(nID,nCode))//處理則返回否則繼續傳遞。
return true;
if(CWnd::OnCmdMsg(nID,nCode))
return true;
CWinApp*pApp=AfxGetApp();
if(pApp->OnCmdMsg(nID,nCode)
return true;
return fasle;
}
bool CView::OnCmdMsg(UINT nID,int nCode)
{
cout<<"CView::OnCmdMsg()"<<endl;
if(CWnd::OnCmdMsg(nID,nCode))
return true;
bool bHandled=false;
bHandled=m_pDocument->OnCmdMsg(nID,nCode);
return bHandled;
}
Bool CDocument::OnCmdMsg(UINT nID,int nCode)
{
cout<<"CDocument::OnCmdMsg()"<<endl;
if(CCmdTarget::OnCmdMsg(nID,nCode))
return true;
return false;
}
真正的消息傳遞路徑是從OnCmdMsg開始的。在每個類的OnCmdMsg函數中,會調用其他類的OnCmdMsg函數,從而決定每個消息的傳遞路徑。
如果消息在前一個OnCmdMsg中被處理,就不會繼續傳遞。如果沒有被處理,則會繼續沿着路徑傳遞下去。無論如何,最終消息的比對是在CCmdTarget類中進行的,只是調用GetMessageMap的this指針不同,會決定調用哪個類的消息映射表。理解這一點很重要!!!
bool CCmdTarget::OnCmdMsg(UINT nID,int nCode)
{
cout<<"CCmdTarget::OnCmdMsg()"<<endl;
for(pMessageMap=GetMessageMap();pMessageMap;
pMessageMap=pMessageMap->pBaseMessageMap()
{
If(找到)
//執行消息處理函數。
}
}