MFC中的命令传递(Command Routing)

        我们已经在下一节把整个消息传递网架设起来了。当消息进来时,会有一个泵推动它前进。消息如何进来,以及泵函数如何推动,都是属于Windows程序设计的范畴,暂时

不管。我现在要仿真出消息的流动循环路线--我常喜欢称之为消息的“二万五千里长征”。

        消息如果是从子类流向父类(纵向流动),那么事情再简单不过,整个Message Map消息映射表已规划出十分明确的路线。但是正如卜一节一开始我说的,MFC之中用来处
理消息的C++类并不旱单线发展,作为application framework的重要结构之一的document/view,也具有处理消息的能力(你现在可能还不清楚什么是document/view,没
有关系),因此,消息应该有横向流动的机会。MFC对于消息循环的规定是:
1)如果是一般的Window、消息(WM xxx,则一定是由派生类流向基类,   没有旁流的可能。
2) 如果是命令消息WM COMMAND,那就有奇特的路线了:

    MFC中的命令传递(Command Routing)_第1张图片

图(1)MFC对于命令消息WM_COMMAND的特殊处理

       

      不管这个规则是怎么定下来的,现在我要设计一个推动引擎,把它仿真出来。以下这些函数名称以及函数内容,完全仿真MFC内部。有些函数似乎赘余,那是因为我删掉了
许多卞题以外的操作。不把看似赘余的函数拿掉或合并,是为了留下MFC的足迹。此外,为了追踪调用过程(call stack),我在各函数的第一行输出一串识别文字。
    首先我把新增加的一些成员函数做个列表:


       

    全局函数AfxWhdPrn。就是我所谓的推动引擎的起始点。它本来应该是在CWihThread::Ruh中被调用,但为了实验目的,我在main中调用它,每调用一次便推送
一个消息。这个函数在1V}'C中有四个参数,为了方便,我加卜第五个,用以表示是谁获得消息(成为循环的起点)。例如:
          AfxWndProc(0,WM CREATE,0,0,pMyFrame);
    表示pM夕Fram。获得了一个WM_CREATE,而:
        AfxWndProc(0,WM COMMAND,0,0,pMyView);
    表示pMyView获得了一个WM_COMMAND

    下面是消息的传递过程:

LRESULT AfxWndProc(HWND hWnd, UINT nMsg, WPARAM wParam, LPARAM lParam,
                   CWnd *pWnd)  // last param. pWnd is added by JJHou.
{
  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;
}



      pWhd->WihdnwProc。究竟是调用哪一个函数?不一定,得视pW}d到底指向何种类之对象而定—别忘了WihdnwPrnc是虚函数。这正是虚函数发挥它功效的地方呀:
        a)如果pWi}d指向CMyFrameWhd对象,那么调用的是CFrameWhd::WihdnwProc
              而因为CFrameWhd并没有改写WihdnwProc,所以调用的其实是
                             CWhd:: WihdnwProc。
       b)如果pWnd指向CMyView对象,那么调用的是CView:: Wi}dnwProc。而因为 CView并没有改写WihdnwProc,所以调用的其实是CWhd:: WihdnwProc

      虽然殊途同归,但意义却是不相同的。切记!切记!
      CWhd::WihdnwPrn。首先判断消息是否为WM COMMAND。如果不是,事情最单纯,就把消息往父类推去,父类再往祖父类推去。每到一个类的消息映射表,原本应该比较
AFX_MSGMAP_ENTRY的每一个元素,比较成功就调用对应的处理程序。

不过在这里我不作比较,只是把AFX_MSGMAP_ENTRY中的类识别代码印出来(就像上一节的Frame7程序一样),以表示“到此一游”:

       如果消息是WM COMMAND }  CWhd::WihdnwPrn。调用} OhCnmmahd。好,注意这又是一个CW}d的虚函数:
        1.如果thi、指向CMyFrameWhd对象,那么调用的是CFrameWhd::OhCnmmahd
       2.如果thi、指向CMyView对象,那么调用的是CView::OhCnmmahd。而因为
      CView并没有改写OhCnmmahd,所以调用的其实是CWhd: :OhCnmmahd,这次可就没有殊途同归了。

      消息的传递路线,如下:


     Frame8测试四种情况:分别从frame对象和view 对象中推动消息,消息一般分为 Windows消息和 WM_COMMADND两种:

// test Message Routing
    
    AfxWndProc(0, WM_CREATE, 0, 0, pMyFrame);    
    AfxWndProc(0, WM_PAINT, 0, 0, pMyView);    
    AfxWndProc(0, WM_COMMAND, 0, 0, pMyView);    
    AfxWndProc(0, WM_COMMAND, 0, 0, pMyFrame);
     效果如下:

CWinApp::InitApplication
CMyWinApp::InitInstance
CFrameWnd::Create
CWnd::CreateEx
CFrameWnd::PreCreateWindow
CWinApp::Run
CWinThread::Run

pMyFrame receive WM_CREATE, routing path and call stack:
AfxWndProc()
AfxCallWndProc()
CWnd::WindowProc()
1211    CMyFrameWnd
121    CFrameWnd
12    CWnd
1    CCmdTarget

pMyView receive WM_PAINT, routing path and call stack:
AfxWndProc()
AfxCallWndProc()
CWnd::WindowProc()
1221    CMyView
122    CView
12    CWnd
1    CCmdTarget

pMyView receive WM_COMMAND, routing path and call stack:
AfxWndProc()
AfxCallWndProc()
CWnd::WindowProc()
CWnd::OnCommand()
CView::OnCmdMsg()
CCmdTarget::OnCmdMsg()
1221    CMyView
122    CView
12    CWnd
1    CCmdTarget
CDocument::OnCmdMsg()
CCmdTarget::OnCmdMsg()
131    CMyDoc
13    CDocument
1    CCmdTarget
CWnd::DefWindowProc()

pMyFrame receive WM_COMMAND, routing path and call stack:
AfxWndProc()
AfxCallWndProc()
CWnd::WindowProc()
CFrameWnd::OnCommand()
CWnd::OnCommand()
CFrameWnd::OnCmdMsg()
CFrameWnd::GetActiveView()
CView::OnCmdMsg()
CCmdTarget::OnCmdMsg()
1221    CMyView
122    CView
12    CWnd
1    CCmdTarget
CDocument::OnCmdMsg()
CCmdTarget::OnCmdMsg()
131    CMyDoc
13    CDocument
1    CCmdTarget
CCmdTarget::OnCmdMsg()
1211    CMyFrameWnd
121    CFrameWnd
12    CWnd
1    CCmdTarget
CCmdTarget::OnCmdMsg()
1111    CMyWinApp
111    CWinApp
1    CCmdTarget
CWnd::DefWindowProc()
Press any key to continue

    控制台应用程序Frame8的详细代码,如下:

//mfc.h

#define TRUE 1
#define FALSE 0

typedef char* LPSTR;
typedef const char* LPCSTR;

typedef unsigned long  DWORD;
typedef int            BOOL;
typedef unsigned char  BYTE;
typedef unsigned short WORD;
typedef int            INT;
typedef unsigned int   UINT;
typedef long           LONG;

typedef UINT           WPARAM;
typedef LONG           LPARAM;
typedef LONG           LRESULT;
typedef int            HWND;

#define WM_COMMAND     0x0111  // following windows.h
#define WM_CREATE      0x0001
#define WM_PAINT       0x000F
#define WM_NOTIFY      0x004E

#define CObjectid              0xffff
#define   CCmdTargetid         1
#define     CWinThreadid       11
#define       CWinAppid        111
#define         CMyWinAppid    1111
#define     CWndid             12
#define       CFrameWndid      121
#define         CMyFrameWndid  1211
#define       CViewid          122
#define         CMyViewid      1221
#define     CDocumentid        13
#define       CMyDocid         131

#include <iostream.h>

////////////////////////////////////////////////////////////////////
// Window message map handling

struct AFX_MSGMAP_ENTRY;

struct AFX_MSGMAP
{
        AFX_MSGMAP* pBaseMessageMap;
        AFX_MSGMAP_ENTRY* lpEntries;
};

#define DECLARE_MESSAGE_MAP() \
        static AFX_MSGMAP_ENTRY _messageEntries[]; \
        static AFX_MSGMAP messageMap; \
        virtual AFX_MSGMAP* GetMessageMap() const;

#define BEGIN_MESSAGE_MAP(theClass, baseClass) \
        AFX_MSGMAP* theClass::GetMessageMap() const \
                { return &theClass::messageMap; } \
        AFX_MSGMAP theClass::messageMap = \
        { &(baseClass::messageMap), \
                (AFX_MSGMAP_ENTRY*) &(theClass::_messageEntries) }; \
        AFX_MSGMAP_ENTRY theClass::_messageEntries[] = \
        {

#define END_MESSAGE_MAP() \
        { 0, 0, 0, 0, AfxSig_end, (AFX_PMSG)0 } \
        };

// Message map signature values and macros in separate header
#include "afxmsg_.h"

class CObject
{
public:
  CObject::CObject()  {
                      }
  CObject::~CObject() {
                      }
};

class CCmdTarget : public CObject
{
public:
  CCmdTarget::CCmdTarget()  {
                            }
  CCmdTarget::~CCmdTarget() {
                            }

  virtual BOOL OnCmdMsg(UINT nID, int nCode);

  DECLARE_MESSAGE_MAP()       // base class - no {{ }} macros
};

typedef void (CCmdTarget::*AFX_PMSG)(void);

struct AFX_MSGMAP_ENTRY  // MFC 4.0 format
{
    UINT nMessage; // windows message
    UINT nCode;    // control code or WM_NOTIFY code
    UINT nID;      // control ID (or 0 for windows messages)
    UINT nLastID;  // used for entries specifying a range of control id's
    UINT nSig;     // signature type (action) or pointer to message #
    AFX_PMSG pfn;  // routine to call (or special value)
};

class CWinThread : public CCmdTarget
{
public:
  CWinThread::CWinThread()  {
                            }
  CWinThread::~CWinThread() {
                            }

  virtual BOOL InitInstance() {
                                cout << "CWinThread::InitInstance \n";
                                return TRUE;
                              }
  virtual int Run() {
                      cout << "CWinThread::Run \n";
                      // AfxWndProc(...);
                      return 1;
                    }
};

class CWnd;

class CWinApp : public CWinThread
{
public:
  CWinApp* m_pCurrentWinApp;
  CWnd* m_pMainWnd;

public:
  CWinApp::CWinApp()  {
                        m_pCurrentWinApp = this;
                      }
  CWinApp::~CWinApp() {
                      }

  virtual BOOL InitApplication() {
                                   cout << "CWinApp::InitApplication \n";
                                   return TRUE;
                                 }
  virtual BOOL InitInstance()    {
                                   cout << "CWinApp::InitInstance \n";
                                   return TRUE;
                                 }
  virtual int Run() {
                      cout << "CWinApp::Run \n";
                      return CWinThread::Run();
                    }

  DECLARE_MESSAGE_MAP()
};

typedef void (CWnd::*AFX_PMSGW)(void);
                // like 'AFX_PMSG' but for CWnd derived classes only

class CDocument : public CCmdTarget
{
public:
  CDocument::CDocument()   {
                           }
  CDocument::~CDocument()  {
                           }

  virtual BOOL OnCmdMsg(UINT nID, int nCode);

  DECLARE_MESSAGE_MAP()
};

class CWnd : public CCmdTarget
{
public:
  CWnd::CWnd()   {
                 }
  CWnd::~CWnd()  {
                 }

  virtual BOOL Create();
  BOOL CreateEx();
  virtual BOOL PreCreateWindow();
  virtual LRESULT WindowProc(UINT nMsg, WPARAM wParam, LPARAM lParam);
  virtual LRESULT DefWindowProc(UINT message, WPARAM wParam, LPARAM lParam);
  virtual BOOL OnCommand(WPARAM wParam, LPARAM lParam);

  DECLARE_MESSAGE_MAP()
};

class CView;

class CFrameWnd : public CWnd
{
public:
  CView* m_pViewActive;       // current active view

public:
  CFrameWnd::CFrameWnd()   {
                           }
  CFrameWnd::~CFrameWnd()  {
                           }
  BOOL Create();
  virtual BOOL PreCreateWindow();
  CView* GetActiveView() const;
  virtual BOOL OnCommand(WPARAM wParam, LPARAM lParam);
  virtual BOOL OnCmdMsg(UINT nID, int nCode);

  DECLARE_MESSAGE_MAP()

  friend CView;
};

class CView : public CWnd
{
public:
  CDocument* m_pDocument;

public:
  CView::CView()   {
                   }
  CView::~CView()  {
                   }

  virtual BOOL OnCmdMsg(UINT nID, int nCode);

  DECLARE_MESSAGE_MAP()

  friend CFrameWnd;
};

// global function
CWinApp* AfxGetApp();
LRESULT AfxWndProc(HWND hWnd, UINT nMsg, WPARAM wParam, LPARAM lParam,
                   CWnd* pWnd); // last param. pWnd is added by JJHOU.
LRESULT AfxCallWndProc(CWnd* pWnd, HWND hWnd, UINT nMsg, WPARAM wParam,
                       LPARAM lParam);


//mfc.cpp

#include "my.h"  

extern CMyWinApp theApp;
extern void printlpEntries(AFX_MSGMAP_ENTRY* lpEntry);

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->lpEntries;
            printlpEntries(lpEntry);
    }

    return FALSE;   // not handled
}

BOOL CWnd::Create()
{
    cout << "CWnd::Create \n";
    return TRUE;
}

BOOL CWnd::CreateEx()
{
    cout << "CWnd::CreateEx \n";
    PreCreateWindow();
    return TRUE;
}

BOOL CWnd::PreCreateWindow()
{
  cout << "CWnd::PreCreateWindow \n";
  return TRUE;
}

LRESULT CWnd::WindowProc(UINT nMsg, WPARAM wParam, LPARAM lParam)
{
    AFX_MSGMAP* pMessageMap;
    AFX_MSGMAP_ENTRY* lpEntry;

    cout << "CWnd::WindowProc()" << endl;
    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->lpEntries;
            printlpEntries(lpEntry);
    }
    return 0;  // J.J.Hou: if find, should call lpEntry->pfn,
               // otherwise should call DefWindowProc.
               // for simplization we just return 0.
}

LRESULT CWnd::DefWindowProc(UINT message, WPARAM wParam, LPARAM lParam)
{
    cout << "CWnd::DefWindowProc()" << endl;
    return TRUE;
}

BOOL CWnd::OnCommand(WPARAM wParam, LPARAM lParam)
{
    cout << "CWnd::OnCommand()" << endl;
    // ...
    return OnCmdMsg(0, 0);
}

BOOL CFrameWnd::OnCommand(WPARAM wParam, LPARAM lParam)
{
    cout << "CFrameWnd::OnCommand()" << endl;
    // ...
    // route as normal command
    return CWnd::OnCommand(wParam, lParam);
}

BOOL CFrameWnd::Create()
{
    cout << "CFrameWnd::Create \n";
    CreateEx();
    return TRUE;
}

BOOL CFrameWnd::PreCreateWindow()
{
    cout << "CFrameWnd::PreCreateWindow \n";
    return TRUE;
}

CView* CFrameWnd::GetActiveView() const
{
    cout << "CFrameWnd::GetActiveView()" << endl;
    return m_pViewActive;
}

BOOL CFrameWnd::OnCmdMsg(UINT nID, int nCode)
{
    cout << "CFrameWnd::OnCmdMsg()" << 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;
}

BOOL CDocument::OnCmdMsg(UINT nID, int nCode)
{
    cout << "CDocument::OnCmdMsg()" << endl;
    if (CCmdTarget::OnCmdMsg(nID, nCode))
        return TRUE;

    return FALSE;
}

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;
}

AFX_MSGMAP* CCmdTarget::GetMessageMap() const  // JJHou: in MFC 40 cmdtarg.cpp
{
    return &CCmdTarget::messageMap;
}

AFX_MSGMAP CCmdTarget::messageMap =   // JJHou: in in MFC 40 cmdtarg.cpp
{
    NULL,
    &CCmdTarget::_messageEntries[0]
};

AFX_MSGMAP_ENTRY CCmdTarget::_messageEntries[] = // JJHou: in in MFC 40 cmdtarg.cpp
{
    { 0, 0, CCmdTargetid, 0, AfxSig_end, 0 }  // nothing here
};

BEGIN_MESSAGE_MAP(CWnd, CCmdTarget)
ON_COMMAND(CWndid, 0)
END_MESSAGE_MAP()

BEGIN_MESSAGE_MAP(CFrameWnd, CWnd)
ON_COMMAND(CFrameWndid, 0)
END_MESSAGE_MAP()

BEGIN_MESSAGE_MAP(CDocument, CCmdTarget)
ON_COMMAND(CDocumentid, 0)
END_MESSAGE_MAP()

BEGIN_MESSAGE_MAP(CView, CWnd)
ON_COMMAND(CViewid, 0)
END_MESSAGE_MAP()

BEGIN_MESSAGE_MAP(CWinApp, CCmdTarget)
ON_COMMAND(CWinAppid, 0)
END_MESSAGE_MAP()

CWinApp* AfxGetApp()
{
  return theApp.m_pCurrentWinApp;
}

LRESULT AfxWndProc(HWND hWnd, UINT nMsg, WPARAM wParam, LPARAM lParam,
                   CWnd *pWnd)  // last param. pWnd is added by JJHou.
{
  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;
}

//my.h

#include <iostream.h>
#include "mfc.h"

class CMyWinApp : public CWinApp
{
public:
	CMyWinApp::CMyWinApp()   {
	}
	CMyWinApp::~CMyWinApp()  {
	}
	virtual BOOL InitInstance();
	DECLARE_MESSAGE_MAP()
};

class CMyFrameWnd : public CFrameWnd
{
public:
	CMyFrameWnd();
	~CMyFrameWnd()  {
	}
	DECLARE_MESSAGE_MAP()
};

class CMyDoc : public CDocument
{
public:
	CMyDoc::CMyDoc()  {
	}
	CMyDoc::~CMyDoc() {
	}
	DECLARE_MESSAGE_MAP()
};

class CMyView : public CView
{
public:
	CMyView::CMyView()   {
	}
	CMyView::~CMyView()  {
	}
	DECLARE_MESSAGE_MAP()
};

//my.cpp

#include "my.h"

CMyWinApp theApp;  // global object

BOOL CMyWinApp::InitInstance()
{
    cout << "CMyWinApp::InitInstance \n";
    m_pMainWnd = new CMyFrameWnd;
    return TRUE;
}

CMyFrameWnd::CMyFrameWnd()
{
    Create();
}

BEGIN_MESSAGE_MAP(CMyWinApp, CWinApp)
ON_COMMAND(CMyWinAppid, 0)
END_MESSAGE_MAP()

BEGIN_MESSAGE_MAP(CMyFrameWnd, CFrameWnd)
ON_COMMAND(CMyFrameWndid, 0)
END_MESSAGE_MAP()

BEGIN_MESSAGE_MAP(CMyDoc, CDocument)
ON_COMMAND(CMyDocid, 0)
END_MESSAGE_MAP()

BEGIN_MESSAGE_MAP(CMyView, CView)
ON_COMMAND(CMyViewid, 0)
END_MESSAGE_MAP()

void  printlpEntries(AFX_MSGMAP_ENTRY* lpEntry)
{
	struct {
		int classid;
		char* classname;
	} classinfo[] = {
		CCmdTargetid ,  "CCmdTarget   ",
			CWinThreadid ,  "CWinThread   ",
			CWinAppid    ,  "CWinApp      ",
			CMyWinAppid  ,  "CMyWinApp    ",
			CWndid       ,  "CWnd         ",
			CFrameWndid  ,  "CFrameWnd    ",
			CMyFrameWndid,  "CMyFrameWnd  ",
			CViewid      ,  "CView        ",
			CMyViewid    ,  "CMyView      ",
			CDocumentid  ,  "CDocument    ",
			CMyDocid     ,  "CMyDoc       ",
			0            ,  "             "
	};
	
    for (int i=0; classinfo[i].classid != 0; i++)
    {
        if (classinfo[i].classid == lpEntry->nID)
        {
            cout << lpEntry->nID << "    ";
            cout << classinfo[i].classname << endl;
            break;
        }
    }
}
//------------------------------------------------------------------
// main
//------------------------------------------------------------------
void main()
{
    CWinApp* pApp = AfxGetApp();
	
    pApp->InitApplication();
    pApp->InitInstance();
    pApp->Run();
	
    CMyDoc* pMyDoc = new CMyDoc;
    CMyView* pMyView = new CMyView;
    CFrameWnd* pMyFrame = (CFrameWnd*)pApp->m_pMainWnd;
    pMyFrame->m_pViewActive = pMyView;
    pMyView->m_pDocument = pMyDoc;
	
    // test Message Routing
    cout << endl << "pMyFrame receive WM_CREATE, ";
    cout << "routing path and call stack:" << endl;
    AfxWndProc(0, WM_CREATE, 0, 0, pMyFrame);
	
    cout << endl << "pMyView receive WM_PAINT, ";
    cout << "routing path and call stack:" << endl;
    AfxWndProc(0, WM_PAINT, 0, 0, pMyView);
	
    cout << endl << "pMyView receive WM_COMMAND, ";
    cout << "routing path and call stack:" << endl;
    AfxWndProc(0, WM_COMMAND, 0, 0, pMyView);
	
    cout << endl << "pMyFrame receive WM_COMMAND, ";
    cout << "routing path and call stack:" << endl;
    AfxWndProc(0, WM_COMMAND, 0, 0, pMyFrame);
}

//afxmsg_.h

enum AfxSig
{
	AfxSig_end = 0,     // [marks end of message map]
        AfxSig_vv,
};

#define ON_COMMAND(id, memberFxn) \
{ WM_COMMAND, 0, (WORD)id, (WORD)id, AfxSig_vv, (AFX_PMSG)memberFxn },


你可能感兴趣的:(C++,command,mfc)