MFC深入浅出--命令传递

来源:互联网 发布:中国银行业数据库 编辑:程序博客网 时间:2024/05/18 03:46
  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.
原创粉丝点击