《c++徒步》MFC篇——消息映射机制

发布时间 2023-04-13 09:29:07作者: Fusio

MFC消息映射机制

什么是消息映射机制?

MFC使用消息映射机制来处理消息,引入了消息映射表的概念,表中存消息消息处理函数二者对应关系。当鼠标点击事件发生时,会产生对应消息,然后去消息映射表中查找对应的消息处理函数并执行。

什么是句柄?

句柄相当于一个编号,Windows对于我们来说相当于一个黑盒,我们只能通过这个编号,也就是句柄来获得我们想要的数据。(个人理解,句柄存了一个地址,地址所在位置存储了一些必须的信息,如,调用打印机句柄,获得打印机信息,也可以调用打印功能)

消息机制为什么要使用宏?

设计者在设计MFC时,尽可能的使MFC的代码要小,速度尽可能快。为了这个目的,设计师使用了很多奇技淫巧,使用宏就是其中之一。

消息映射机制的宏

MFC消息机制的宏有下面几个:

// 1、
DECLARE_MESSAGE_MAP()
// 2、
BEGIN_MESSAGE_MAP()(theClass,baseClass)
// 3、
END_MESSAGE_MAP()

DELCARE_MESSAGE_MAP()

DECLARE_MESSAGE_MAP()宏展开:

#define DECLARE_MESSAGE_MAP()
private:
	static const AX_MSGMAP_ENTRY _messageEntries[];
protected:
	static AFX_DATA const AFX_MSGMAP messageMap;
	virtual const AFX_MSGMAP* GetMessageMap() const;

从上面定义可以看出,DECLARE_MESSAGE_MAP()做了三件事,
(1)定义了一个长度不定的静态数组变量_messageEntries[];
(2)定义了一个静态变量messageMap;
(3)定义了一个虚拟函数GetMessageMap();

AFX_MSGMAP_ENTRY和AFX_MSGMAPX

DECLARE_MESSAGE_MAP()宏中的两个对外不公开的结构struct,即AFX_MSGMAP_ENTRY和AFX_MSGMAPX。
AFX_MSGMAP_ENTRY的定义:

struct AFX_MSGMAP_ENTRY
{
	UNIT nMessage;// windows message
	UNIT nCode;// control code or WM_NOTIFY code
	UNIT nID;// control ID
	UNIT nLastID;// used for entries specifying a range of control id’s
	UNIT nSig;// signature type (action) or pointer to messagge
	AFX_PMSG pfn;// routine to call (or special value)
};

从注释可以看出,AFX_MSGMAP_ENTRY结构实际上定义了消息和处理此消息的函数之间的映射关系。因此静态数组_messageEntries[],实际上定义了一张表,表中每一项指定了消息和消息处理函数的对应关系,也就是消息映射表。nMessage和nCode确定一条消息的内容,nID和nLastID确定了一条消息的来源,nSig和pfn确定了消息处理函数和调用方式。
此外,名字ENTRY也有入口的意思(即消息和消息处理函数之间的映射)。
AFX_MSGMAP的定义:

struct AFX_MSGMAP
	{
		const AFX_MSGMAP* pBaseMap;
		consgt AFX_MSGMAP_ENTRY* lpEntries;
}

AFX_MSGMAP定义了两个指针,pBaseMap指向另一个AFX_MSGMAP,lpEntries指针指向消息映射表_messageEntries。通过这种方式,使得在某个类中调用基类的消息处理函数很容易,因此,“父类的消息处理函数是子类的缺省消息处理函数”就“顺理成章”了。

BEGIN_MESSAGE_MAP(theClass,baseClass)

作用:定义DECLARE_MESSAGE_MAP宏声明的静态变量。

#define BEEGIN_MESSAGE_MAP(theClass,baseClass)
   const AFX_MSGMAP* theClass::GetMessageMap() const 
   {
	return &theClass::messageMap;
   }
   AFX_COMDAT const AFX_MSGMAP theClass::messageMap =
   { &baseClass::messageMap,&theClass::_messageEntries[0]};
   AFX_COMDAT const AFX_MSGMAP_ENTRY theClass::_messageEntries[]=
   {
	#define END_MESSAGE_MAP()
	{0,0,0,0,AfxSig_end,(AFX_PMSG)0}
   };

BEGIN_MESSAGE_MAP有两个参数,theClass表示当前类,baseClass表示父类。
BEGIN_MESSAGE_MAO首先定义了函数GetMessageMap的函数体,返回成员变量messageMap的地址。
然后,AFX_COMDAT const AFX_MSGMAP theClass::messageMap=??,初始化了成员变量messageMap。messageMap的pBaseMap指向父类的messageMap,lpEntries指针指向当前类的_messageEntries数组的首地址。
最后,AFX_COMDAT const AFX_MSGMAP_ENTRY theClass::_messageEntries[]=??,定义了_messageEntries数组初始化的开始部分代码, 即一个“{”。

END_MESSAGE_MAP()

作用:定义_messageEntries数组初始化的结束部分代码,即“{0,0,0,0,AfxSig_end,(AFX_PMSG)0} };”

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

其他宏

在DECLARE_MESSAGE_MAP和END_MESSAGE_MAP之间还有一些宏,如ON_COMMAND、ON_WM_CREATE等,这些宏最终都会被生成一条AFX_MSGMAP_ENTRY结构的数据,并成为消息映射表_messageEntries的一个元素。
以ON_COMMAND宏为例:

#define ON_COMMAND(id,memberFxn)
	{
		WM_COMMAND, CN_COMMAND, (WORD)id,(WORD)id, AfxSigCmd_v,
		static_cast<AFX_PMSG>(memberFxn)
	}

参考
https://blog.csdn.net/weixin_42083441/article/details/109726786
https://www.jianshu.com/p/c1cdba489b88
https://www.cnblogs.com/zzw19940404/p/14008341.html
https://blog.csdn.net/lovemysea/article/details/74893961
https://blog.csdn.net/zhangchuan7758/article/details/121653589