Command Routing (命令传递)
MFC对于消息循环的规定:
#若是一般的Windows消息(WM_xxx),则一定是由派生类流向基类,没有旁流的可能。
#若是命令消息WM_COMMAND,就有奇特的路线了。
不管规则是怎么制定的。现在需要一个推动引擎。全局函数AfxWndProc就是推动引擎的起始点。它原本在CwinThread::Run()中被调用。但为了实验目的,我们在main函数中调用它。每调用一次边推送一个消息,这个函数在MFC中MFC中有四个参数,我们加入第五个参数,表示谁获得了消息(成为循环的起点)。例如:
AfxWndProc( 0 ,WM_CREATE ,0 ,0 ,pMyFrame );
表示pMyFrame获得一个WM_CREATE,而
AfxWndProc( 0 ,WM_COMMANDE ,0 ,0 ,pMyFrame );
表示pMyFrame获得一个WM_ COMMANDE.
下面是消息传递的过程:
LRESULT AfxWndProc(HWND hWnd ,UINT nMsg, WPAARAM mParam, LPARAM lParam, Cwnd * pWnd)
{
cout<<
”
AfxWndProc
”
<<endl;
return AfxCallWnfProc(pWnd,hWnd,nMsg,wParam,lParam);
}
LRESULT AfxCallWnfProc (CWnd * pWnd ,HWND hWnd ,UINT nMsg, WPAARAM mParam, LPARAM lParam)
{
cout<<
”
AfxCallWndProc()
”
<<endl;
LRESULT lResult = pWnd->WindowProc(nMsg,wParam,lParam);
Return lResult;
}
由于WindowProc是虚函数,若pWnd指向CMyFrameWnd对象,则调用CMyFrameWnd::WindowProc。而因为CMyFrameWnd并没有改写WindowProc,所以调用的其实是CWnd:: WindowProc;若pWnd指向CMyView对象,则调用CView::WindowProc。而因为CView并没有改写WindowProc,所以调用的其实是CWnd:: WindowProc。
虽然殊途同归,但意义不同。
CWnd:: WindowProc首先判断消息是否为WM_COMMAND。若不是,事情最简单,就把信息往父类推去,父类再往祖父类推去。每到一个类的消息映射表,原本应该比较AFX_MSGMAP_ENREY的每一个元素,比较成功就调用对应的处理程序。
LRESULT CWnd::WindowProc(UINT nMsg,WPARAM wParam,LPARAM lParam)
{
AFX_MSGMAP * pMessageMap;
AFX_MSGMAP_ENTRY * lpEntry;
If(nMsg==WM_COMMAND) //special case for commands
{
if(oncommand(wParam,lParam)) //
…
①
return 1L;//command handled
else
return (LRESULT)DefWindowProc(nMsg,wParam,lParam);//
⑨
}
pMessageMap = GetMessageMap();
for(;pMessageMap!=NULL; pMessageMap= pMessageMap-> pBaseMessageMap)
{
lpEntry = pMessageMap->lpEntry;
printflpEntries(lpEntry);
}
return 0;
}
如果消息是WM_COMMAND,CWnd::WindowProc调用
①
处代码。注意oncommand也是一个CWnd的虚函数:若this指向CMyFrameWnd对象,那么调用的是CFrameWnd::OnCommand;若this指向CMyView对象,则调用CView::OnCommand。而因为CView并没有改写OnCpmmand,所以实际调用的是CWnd::OnCommand.
我们以第一种情况为例,再向下看:
BOOL CFrameWnd::OnCommand(WPARAM wParam , LPARAM lParam)
{
cout<<
”
CFreameWnd::OnCommand()
”
<<endl;
//
…
//route as normal Command
return CWnd::OnCOmmand(wParaM,lParam);//
②
}
BOOL CWnd:: OnCommand(WPARAM wParam , LPARAM lParam)
{
cout<<
”
CWnd::OnCommand()
”
<<endl;
…
return OnCmdMsg(0,0);//
③
}
③
处代码OnCmdMsg是CCmdTarget类的虚函数,所以:
#若this指向CMyFrameWnd对象,则调用CMyFrameWnd::OnCmdMsg;
#若this指向CMyView对象,则调用CMyView::OnCmdMsg;
#若this指向CMyDoc对象,则调用CMyDoc::OnCmdMsg;
#若this指向CMyWinApp对象,则调用CMyWinApp::OnCmdMsg;。而因为CwinApp并没有改写OnCmdMsg,所以其实调用的是CCmdTarget::OnCmdMsg。
当前的情况是第一种,于是调用CframeWnd::OnCmdMsg;
BOOL CFrameWnd:: OnCmdMsg(UINT nID ,int nCode)
{
cout<<
”
CFrameWnd::OnCommand()
”
<<endl;
//pump through current view First
CView *pView = GetActiveView();
If(pView-> OnCmdMsg(nID,nCode)) //
④
Return TRUE;
//then pump through frame
if(CWnd:: OnCmdMsg(nID,nCode))//
⑦
return TRUE;
//last but not least,pump through app
CWinApp *pApp = AfxGetApp();
If(pApp-> OnCmdMsg(nID,nCode))//
⑧
Return TRUE;
Return FALSE;
}
这个函数反映了Frame窗口处理WM_COMMAND的次序.最先调用的 ④pView->OnCmdMsg,于是:
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 ;
}
这又反映了View窗口处理WM_COMMAND的次序.最好调用的CWnd::OnCmdMsg,而CWnd并未改写OnCmdMsg,所以其实就是调用CCmdTarget::OnCmdMsg:
BOOL CCmdTarget::OnCmdMsg(UINT nID,int nCode)
{
cout<<"CCmdTarget::OnCmdMsg()"<<endl;
//now look through message map to see if it applies to us
AFX_MSGMAP *pMessageMap;
AFX_MSGMAP_ENTRY * lpEntry;
for(pMessageMap = GetMessageMap();pMessageMap!=NULL;
pMessageMap=pMessageMap->pBasemessageMap)
{
lpEntry = pMessageMap-<lpEntry;
printlpEntry(lpEntry);
}
return FALSE:
}
这是一个走访消息映射表的操作.注意,GetMessageMap也是个虚函数(隐藏在DECLARE_MESSAGE_MAP宏定义中),所以它所获得的消息映射表将是this所指对象的影射表.
如果在映射表中找到了对应的消息,就调用对应的处理程序.如果没有找到就退回到CView::OnCmdMsg,调用⑥CDocument::OnCmdMsg:
BOOL CDocument::OnCmdMsg(UINT nID,int nCode)
{
cout<<"CDocument::OnCmdMsg()"<<endl;
if(CCmdTarget::OnCmdMsg(nID,nCode))
return TRUE;
return FALSE;
}
如果在映射表还没有找到对应消息,那么这时退回到CFrameWnd::OnCmdMsg,调用⑦CWnd::OnCmdMsg(即CCmdTarge::OnCmdMsg).
如果在映射表还没有找到对应消息,再退回到CFrameWnd::OnCmdMsg,调用⑧CWinApp::OnCmdMsg(亦即CCmdTarge::OnCmdMsg).
万一还没有找到,就只有退回到CWnd::WindowProc,调用⑨CWnd::DefWindoeProc.