MFC六大关键技术之仿真学习笔记(七)

       从学习Windows的消息机制学习到MFC的消息映射,这是我最喜欢的一节。在Win32编程中,你回去定义各种消息,在WndProc中通过switch ..case..进行消息的分类处理。带着这种思维走进MFC,你会发现MFC采用一种消息映射表模式来简化消息处理。虽说书上理解起来很简单,关于消息如何在MFC框架中流动,其中原理是什么,这才是我们应该学会的,而不是仅仅学会如何使用。


*Message Mapping(消息映射)

当然我们希望消得以下流动


消息流动的终点在CCmdTarget,我们来看看消息如何实现以上的流动的,先来介绍两个数据结构:

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

struct AFX_MSGMAP_ENTRY
{
	UINT nMessage;
	UINT nCode;
	UINT nID;
	UINT nLastID;
	UINT nSig;
	AFX_PMSG pfn;
};
即:定义一个AFX_MSGMAP_ENTRY,通过结构我们可以获得消息对应的处理函数指针。

struct AFX_MSGMAP
{
	AFX_MSGMAP* pBaseMessageMap;
	AFX_MSGMAP_ENTRY* lpEntries;
};
即:通过AFX_MSGMAP,可以获得类成员的AFX_MSGMAP_ENTRY指针,从而获得类中所有消息映射,通过pBaseMessageMap获得基类的AFX_MSGMAP指针,这样可以实现"同宗"的流动。


同样,MFC也为了降低我们的工作量,构建了几个宏: DECLARE_MESSAGE_MAP,BEGIN_MESSAGE_MAP,END_MESSAGE_MAP。

来看看具体的定义:

#define DECLARE_MESSAGE_MAP() \
	static AFX_MSGMAP_ENTRY _messageEntries[]; \
	static AFX_MSGMAP messageMap; \
	virtual AFX_MSGMAP* GetMessageMap() const;
即:还是老套路,声明static变量来保证每个类中静态成员唯一,_messageEntries[] 保存了类的消息映射信息,messageMap是子类和父类消息映射的纽带,消息通过向上流动可以实现遍历。虚函数GetMessageMap() 返回类对应的messageMap。

如图:


#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} \
    };
即:BEGIN_MESSAGE_MAP与END_MESSAGE_MAP是成对出现的,看了宏定义确实如此,当然在实际代码中我们经常写到:

BEGIN_MESSAGE_MAP(CMyWinApp, CWinApp)
	ON_COMMAND(CMyWinAppid, 0)
END_MESSAGE_MAP()
在俩宏的中间加入了我们的消息映射,当然ON_COMMAND宏我们也可以自行实现, 只需要实现宏完成一个_messageEntries的初始化。

#define ON_COMMAND(id, memberFxn) \
	{ WM_COMMAND, CN_COMMAND, (WORD)id, (WORD)id, AfxSigCmd_v, \
		static_cast<AFX_PMSG> (memberFxn) },
当然,我们对END_MESSAGE_MAP()中的AfxSig_end定义为:

enum AfxSig
{
	AfxSig_end = 0,     // [marks end of message map]
}
代表消息映射的结束。


当然,这两个宏怎么用呢,其实很简单,下面用CView作为Demo:

头文件:

class CView :public CWnd
{
public:
	CView()
	{
		std::cout << "CView Constructor \n";
	}
	~CView()
	{
		std::cout << "CView Destructor \n";
	}
	DECLARE_MESSAGE_MAP()
};
Cpp文件:

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

我们在main函数中测试一下:

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)
		{
			std::cout << lpEntry->nID << "  ";
			std::cout << classinfo[i].classname << std::endl;
			break;
		}
	}
}

void MsgMapPrinting(AFX_MSGMAP * pMessageMap)
{
	for (; pMessageMap != NULL; pMessageMap = pMessageMap->pBaseMessageMap)
	{
		AFX_MSGMAP_ENTRY * lpEntry = pMessageMap->lpEntries;
		printlpEntries(lpEntry);
	}
}


int _tmain(int argc, _TCHAR* argv[])
{
	CWinApp * pApp = AfxGetApp();
	pApp->InitApplication();
	pApp->InitInstance();
	pApp->Run();

	CMyDoc *pMyDoc = new CMyDoc;
	CMyView *pMyView = new CMyView;
	CFrameWnd * pMyFrame = (CFrameWnd *)pApp->m_pMainWnd;

	AFX_MSGMAP *pMessageMap = pMyDoc->GetMessageMap();
	std::cout << std::endl << "MyDoc MessageMap : " << std::endl;
	MsgMapPrinting(pMessageMap);

	pMessageMap = pMyView->GetMessageMap();
	std::cout << std::endl << "MyView MessageMap : " << std::endl;
	MsgMapPrinting(pMessageMap);

	pMessageMap = pMyFrame->GetMessageMap();
	std::cout << std::endl << "MyFrame MessageMap : " << std::endl;
	MsgMapPrinting(pMessageMap);

	pMessageMap = pApp->GetMessageMap();
	std::cout << std::endl << "pApp MessageMap : " << std::endl;
	MsgMapPrinting(pMessageMap);
	system("pause");
	return 0;
}

结果:



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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值