深入浅出MFC:MFC的CommandRouting(命令传递)

上回,我们成功地将消息传递网架构了起来。

在这个传递网之中,如果消息需要从子类流向父类,那么是再简单不过的事了,可是在MFC中用来消息传递的C++类并不是单线传递的,作为Application Framework的重要结构之一 Document/View框架也具有处理消息的能力,(可能你还不了解Document/View框架,不过一点关系也没有),反正在MFC的设计中,消息应当也是有横向流动的机会的。

MFC中对于消息的规定如下:

如果是一般的Windows消息(WM_xxx),则只会按单线传递,由派生类流向基类。。。。

而如果消息是 WM_COMMAMD,那就有特殊的路线了,如下图:

命令消息的接收者处理次序
FrameWnd1.View
2.FrameWnd
3.CWinApp
View1.View
2.Document
Document1.Document
2.Document template
附注:暂时先不用管Document template

现在来仿真这整个流程,所有新增的一些成员函数如下:

与消息循环有关的成员函数特征
noneAfxWndProcglobal
noneAfxCallWndProcglobal
CCmdTargetOnCmdMsgvirtual
CDocumentOnCmdMsgvirtual
CWndWindowProcvirtual
 OnCommandvirtual
 DefWindowProcvirtual
CFrameWndOnCommandvirtual
 OnCmdMsgvirtual
CViewOnCmdMsgvirtual

其中全局函数AfxWndProc即是整个消息传递的起点,没调用一次该函数,便会推送一条消息,该函数原本在CWinThread::Run中被调用, 为了仿真的便利,这里将会直接在主函数main中调用,该函数原来有4个参数,为了使用的方便,这里再多加一个参数,以表示该消息的获得者。(推测消息驱动的起点就在CWinThread这里)

仿真的消息传递的过程如下:

LRESULT AfxWndProc(HWND hWnd, UINT nMsg, WPARAM wParam, LPARAM lParam,
	CWnd* pWnd)
{
	return AfxCallWndProc(pWnd,hWnd ,nMsg,wParam , lParam);
}

LRESULT AfxCallWndProc(CWnd* pWnd, HWND hWnd, UINT nMsg,
	WPARAM wParam, LPARAM lParam)
{
	LRESULT lResult = pWnd->WindowProc(nMsg , wParam , lParam);
	return lResult;
}
这里LRESULT是一个长整形的整数。

由于CMyFrameWnd和CMyView都没有重写WindowProc这个方法,所以到头来调用的还是CWnd的WindowProc,虽然看起来相似,但其实具体执行的效果都是不相同的,具体往下面看:

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

	if (nMsg == WM_COMMAND)
	{
		if (OnCommand(wParam, lParam))
		{
			return 1L;
		}
		else
			return (LRESULT)DefWindowProc(nMsg , wParam , lParam);
	}

	pMessageMap = GetMessageMap();

	for (; pMessageMap != NULL;
		pMessageMap = pMessageMap->pBaseMessageMap)
	{
		lpEntry = pMessageMap->lpEntries;
		printlpEntries(lpEntry);
	}

	return 0;
}

在这里如果消息是WM_COMMAND,则CWnd::WindowProc调用OnCommand,这里的OnCommannd是一个虚方法,假如指向CMyFrameWnd,由于CMyFrameWnd重写了该方法,所以调用的是CMyFrameWnd::OnCommand,而如果指向CMyView,由于CMyView没有重写,所以调用的是CWnd::OnCommand ,这样调用的效果就完全不同了,具体实现如下:

BOOL CWnd::OnCommand(WPARAM wParam, LPARAM lParam)
{
	return OnCmdMsg(0,0);
}

BOOL CFrameWnd::OnCommand(WPARAM wParam, LPARAM lParam)
{
	return CWnd::OnCommand(wParam, lParam);
}

除了这里的一点点小小的不同之外,还有我们需要考虑的,之后的OnCmdMsg也是完全不同的,别忘了CFrameWnd和CView都重写了OnCmdMsg,而CWnd本身却没有实现(实现了也没用),所以,这个调用还是很绕的,调用进去了之后

假如消息指向CFrameWnd为:

那么接下来的反应就是:

BOOL CFrameWnd ::OnCmdMsg(UINT nID ,int nCode)
{
	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 FALSE;
}
接下来,就会调用CView的OnCmdMsg , 在此之后,CView的OnCmdMsg实现中:

BOOL CView::OnCmdMsg(UINT nID, int nCode)
{
	if (CWnd::OnCmdMsg(nID, nCode))
		return TRUE;

	BOOL bHandled = FALSE;
	bHandled = m_pDocument->OnCmdMsg(nID,nCode);
	return bHandled;
}
再去调用该CView的CWnd的OnCmdMsg,之后再去调用CDocuent的OnCmdMsg,在CDocument中

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

	return FALSE;
}
在这里又调用了CCmdTarget::OnCmdMsg ,仿真如下:

BOOL CCmdTarget::OnCmdMsg(UINT nID, int nCode)
{
	AFX_MSGMAP* pMessageMap;
	AFX_MSGMAP_ENTRY* lpEntry;
	for (pMessageMap = GetMessageMap();
		pMessageMap != NULL;
		pMessageMap = pMessageMap->pBaseMessageMap)
	{
		lpEntry = pMessageMap->lpEntries;
		printlpEntries(lpEntry);
	}
	return FALSE;
}
这就是尽头了么?NO!,别忘了GetMessageMap也是虚函数,其实现被隐藏在宏之中,算了,不想写了,后面的就自己推一遍吧!

我这里直接给出整个消息传递的图出来~~~

先说一句,以上代码纯属仿真,所以去掉了一些多余的部分,所以看起来可能有一些莫名其妙,不用纠结,你只要知道这就是MFC的消息传递的顺序就可以了

这一张可能更清楚一点~~~

---摘自《深入浅出MFC》

完整的样例代码如下:

//MFC.h
#pragma once
#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
#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>
using namespace std;
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} \
		};

#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()
};

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

struct AFX_MSGMAP_ENTRY
{
	UINT nMessage;
	UINT nCode;
	UINT nID;
	UINT nLastID;
	UINT nSig;
	AFX_PMSG pfn;
};

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

	virtual BOOL InitInstance() {
		cout << "CWinThread::InitInstance" << endl;
		return TRUE;
	}

	virtual int Run() {
		cout << "CWinThread::Run" << endl;
		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" << endl;
		return TRUE;
	}

	virtual BOOL InitInstance() {
		cout << "CWinApp::InitInstance" << endl;
		return TRUE;
	}

	virtual int Run() {
		cout << "CWinApp::Run" << endl;
		return CWinThread::Run();
	}

	DECLARE_MESSAGE_MAP()
};


typedef void(CWnd::*AFX_PMSGW)(void);

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;

public:
	CFrameWnd::CFrameWnd(){}
	CFrameWnd::~CFrameWnd(){}

	BOOL Create();
	CView* GetActiveView() const;
	virtual BOOL PreCreateWindow();
	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;
};

CWinApp* AfxGetApp();
LRESULT AfxWndProc(HWND hWnd, UINT nMsg, WPARAM wParam, LPARAM lParam,
	CWnd* pWnd);
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)
{
	AFX_MSGMAP* pMessageMap;
	AFX_MSGMAP_ENTRY* lpEntry;
	for (pMessageMap = GetMessageMap();
		pMessageMap != NULL;
		pMessageMap = pMessageMap->pBaseMessageMap)
	{
		lpEntry = pMessageMap->lpEntries;
		printlpEntries(lpEntry);
	}
	return FALSE;
}

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

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

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

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

	if (nMsg == WM_COMMAND)
	{
		if (OnCommand(wParam, lParam))
		{
			return 1L;
		}
		else
			return (LRESULT)DefWindowProc(nMsg , wParam , lParam);
	}

	pMessageMap = GetMessageMap();

	for (; pMessageMap != NULL;
		pMessageMap = pMessageMap->pBaseMessageMap)
	{
		lpEntry = pMessageMap->lpEntries;
		printlpEntries(lpEntry);
	}

	return 0;
}

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

BOOL CWnd::OnCommand(WPARAM wParam, LPARAM lParam)
{
	return OnCmdMsg(0,0);
}

BOOL CFrameWnd::OnCommand(WPARAM wParam, LPARAM lParam)
{
	return CWnd::OnCommand(wParam, lParam);
}

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

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

CView* CFrameWnd::GetActiveView() const
{
	return m_pViewActive;
}

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

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

	return FALSE;
}

BOOL CView::OnCmdMsg(UINT nID, int nCode)
{
	if (CWnd::OnCmdMsg(nID, nCode))
		return TRUE;

	BOOL bHandled = FALSE;
	bHandled = m_pDocument->OnCmdMsg(nID,nCode);
	return bHandled;
}

AFX_MSGMAP* CCmdTarget::GetMessageMap() const
{
	return &CCmdTarget::messageMap;
}

AFX_MSGMAP CCmdTarget::messageMap = {
	NULL,
	&CCmdTarget::_messageEntries[0]
};

AFX_MSGMAP_ENTRY CCmdTarget::_messageEntries[] =
{
	{0,0,CCmdTargetid , 0, AfxSig_end ,0 }
};

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(CWinAppid ,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)
{
	return AfxCallWndProc(pWnd,hWnd ,nMsg,wParam , lParam);
}

LRESULT AfxCallWndProc(CWnd* pWnd, HWND hWnd, UINT nMsg,
	WPARAM wParam, LPARAM lParam)
{
	LRESULT lResult = pWnd->WindowProc(nMsg , wParam , lParam);
	return lResult;
}

//MY.h
#pragma once
#include"MFC.h"
#include<iostream>
using namespace std;

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;

BOOL CMyWinApp::InitInstance()
{
	cout << "CMyWinApp::InitInstance" << endl;
	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;
		}
	}
}


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;

	cout << endl << "pMyFrame received a WM_CREATE, routing path:" << endl;
	AfxWndProc(0, WM_CREATE, 0, 0, pMyFrame);

	cout << endl << "pMyView received a WM_PAINT, routing path:" << endl;
	AfxWndProc(0, WM_PAINT, 0, 0, pMyView);

	cout << endl << "pView received a WM_COMMAND, routing path:" << endl;
	AfxWndProc(0, WM_COMMAND, 0, 0, pMyView);

	cout << endl << "pMyFrame received a WM_COMMAND, routing path:" << endl;
	AfxWndProc(0, WM_COMMAND, 0, 0, pMyFrame);

	system("pause");
}





  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值