命令传递(Commandrouting)
消息如果是仅仅从派生类流向父类,那就非常简单了。然而MFC用来处理消息的C++类,并不是单线发展的。document/view也具有处理消息的能力。因此,消息应该有横向流动的机会。
MFC对消息循环的规定为:
1:若是一般的windows消息(WM_xx)则一定是由派生类流向基类。
2:如果是WM_COMMAND消息,就非常复杂了。要区分接受者的类型:
1:接受者若为Frame窗口:处理次序为:View->Frame窗口本身->CWinApp类。
2:接受者若为View:处理次序为:View本身->Document;
3:接受者若为Document:处理次序为:Document本身->Documenttemplate
因此,接下来我们的任务就是仿真以上的消息传递路线。
以下为需要添加的函数:
全局函数AfxWndProc,它是整个消息循环的起始点,本来应该在CWinThread::Run中被调用,每调用一次就推送一个消息。模拟windows的disPatch函数。
LRESULTAfxWndPro(HWNDhWnd,UINTnMsg,WPARAM
wParam,LPARAMlParam,CWnd*pWnd)
{
cout<<"AfxWndProc()"<<endl;
returnAfxCallWndProc(pWnd,hWnd,nMsg,wParam,lParam);
}
LRESULTAfxCallWndProc(CWnd*pWnd,HWNDhWnd,UINTnMsg,
WPARAMwParam,LPARAMlParam)
{
cout<<"AfxCallWndProc"<<endl;
LRESULTlResult=pWnd->windowProc(nMsg,wParam,lParam);
returnlResult;
}
全局函数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。
boolCFrameWnd::OnComamnd(WPARAMwParam,LPARAMlParam)
{
cout<<"CFrameWnd::OnCommand()"<<endl;
returnCWnd::OnCommand(wParam,lParam);
}
boolCWnd::OnComamnd(WPARAMwParam,LPARAMlParam)
{
cout<<"CWnd::OnComamnd()"<<endl;
returnOnCmdMsg(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。
BoolCFrameWnd::OnCmdMsg(UINTnID,intnCode)
{
cout<<"CFrameWnd::OnCmdMsg()"<<endl;
CView*pView=GetActiveView();
if(pView->OnCmdMsg(nID,nCode))//处理则返回否则继续传递。
returntrue;
if(CWnd::OnCmdMsg(nID,nCode))
returntrue;
CWinApp*pApp=AfxGetApp();
if(pApp->OnCmdMsg(nID,nCode)
returntrue;
returnfasle;
}
boolCView::OnCmdMsg(UINTnID,intnCode)
{
cout<<"CView::OnCmdMsg()"<<endl;
if(CWnd::OnCmdMsg(nID,nCode))
returntrue;
boolbHandled=false;
bHandled=m_pDocument->OnCmdMsg(nID,nCode);
returnbHandled;
}
BoolCDocument::OnCmdMsg(UINTnID,intnCode)
{
cout<<"CDocument::OnCmdMsg()"<<endl;
if(CCmdTarget::OnCmdMsg(nID,nCode))
returntrue;
returnfalse;
}
真正的消息传递路径是从OnCmdMsg开始的。在每个类的OnCmdMsg函数中,会调用其他类的OnCmdMsg函数,从而决定每个消息的传递路径。
如果消息在前一个OnCmdMsg中被处理,就不会继续传递。如果没有被处理,则会继续沿着路径传递下去。无论如何,最终消息的比对是在CCmdTarget类中进行的,只是调用GetMessageMap的this指针不同,会决定调用哪个类的消息映射表。理解这一点很重要!!!
boolCCmdTarget::OnCmdMsg(UINTnID,intnCode)
{
cout<<"CCmdTarget::OnCmdMsg()"<<endl;
for(pMessageMap=GetMessageMap();pMessageMap;
pMessageMap=pMessageMap->pBaseMessageMap()
{
If(找到)
//执行消息处理函数。
}
}