MFC的消息处理机制及相关的宏

这段话是我在中文维基的“类成员函数指针”词条写的:

MFC类体系中,Windows消息传递处理机制是基于CCmdTarget类及其派生类的静态数据成员与静态成员函数GetThisMessageMap()。用户所写的类中的Windows消息处理函数(例如OnCommand)必须转换为CCmdTarget::*的成员函数指针类型AFX_PMSG,保存在该用户类的_messageEntries静态数组中。

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

调用用户类中该消息处理函数时,根据该函数保存在_messageEntries中的signature(一个无符号整型表示的函数的形参类型列表与返回值类型),把类型为void (CCmdTarget::*AFX_PMSG)(void)的成员函数指针强制转为其它类型的CCmdTarget成员函数指针(例如void (AFX_MSG_CALL CWnd::*pfn_v_i_i)(int, int),目前在union MessageMapFunctions中列出了近百种CCmdTarget成员函数指针),然后调用转换后的成员函数指针。这是基于Visual C++编译器把单继承的成员函数指针编译为只保存了函数的内存起始地址,因此可以在同一个单继承类中把一种类型的成员函数指针强制转换为另一种成员函数指针,或者把单继承派生类的成员函数指针强制转换为基类成员函数指针。这是打破了C++标准的违例办法。例如,对于CWnd::OnCommand函数,转换过程是:

BOOL (CWnd::*)(WPARAM, LPARAM lParam) => void (CWnd::*)() => void (CCmdTarget::*)()

 

头文件中的DECLARE_MESSAGE_MAP()

 

该宏实际上增加了两个静态数据成员、一个虚函数:

static AFX_MSGMAP_ENTRY _messageEntries[]; 

static AFX_MSGMAP messageMap;

virtual AFX_MSGMAP*GetMessageMap()const; 


源文件中的BEGIN_MESSAGE_MAP与END_MESSAGE_MAP

 

这两个宏实际上定义为:

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

#define ON_COMMAND(id,memFunc)\
              {WM_COMMAND,0,id,id,AFx_sig_vv,(AFX_PMSG)memFunc }, 

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

注意这三个宏中的内容共同完成了_messageEntries结构体数组的填写。


 

相关的数据结构

struct AFX_MSGMAP { 
          AFX_MSGMAP *pBaseMessageMap;//指向基类的本结构。
          AFX_MSGMAP_ENTRY*lpEntries;//指向本类的消息映射表。
}; 

struct AFX_MSGMAP_ENTRY //该结构体可以存储一条消息的所有相关信息。 
{ 
	UINT nMessage; //消息ID
	UINT nCode; //控件通知代码,对于窗口消息该值为0。处理命令消息和控件通知的函数使用
	UINT nID; //命令ID,对于窗口消息该值为0
	UINT nLastID; //是以nID开始的命令ID范围内的最后一个命令ID,对于窗口消息该值为0。处理命令消息的函数能够处理某一范围的ID
	UINT nSig; //消息处理函数时成员函数指针类型,其具体的类型信息(signature)的代号
	AFX_PMSG pfn; //成员函数指针类型 typedef void (CCmdTarget::*AFX_PMSG)(void); 
}; 

 

高版本MFC的相关数据结构的新变化


在高版本MFC中(如VC++2013),类中仅定义一个静态程雨函数GetThisMessageMap,一个虚函数GetMessageMap。在类的实现源文件中,则定义两个在类静态成员函数GetThisMessageMap内部的静态数据对象_messageEntries与messageMap

 

结构AFX_MSGMAP的第一个数据成员也改为一个函数指针用来返回基类的AFX_MSGMAP。原来老版本是用这个数据结构形成一个从最派生类开始的单向链表。

消息的搜索过程(映射机制)

 

一般搜索过程

 

函数AfxWndProc接收Windows操作系统发送的消息。

函数AfxWndProc调用函数AfxCallWndProc进行消息处理,这里一个进步是把对句柄的操作转换成对CWnd对象的操作。

函数AfxCallWndProc调用CWnd类的方法WindowProc进行消息处理。


方法WindowProc调用方法OnWndMsg进行正式的消息处理,通过函数AfxFindMessageEntry来搜索各个类中的AFX_MSGMAP_ENTRY数组(从最派生类开始向基类)找到匹配的消息处理函数。大多数的缺省消息处理是调用CWnd的Default()方法。 而Default()方法所做的工作就是调用DefWindowProc对消息进行处理。

 

一般的窗口消息搜索过程:

 

 

WM_COMMAND的搜索过程

 

WalkPreTranslateTree和PreTranslateMessage

利用MFC框架生成的程序,都是从CWinApp开始执行的,而CWinapp实际继承了CWinThread类。在CWinThread的运行过程中会调用窗口类中的WalkPreTranslateTree方法。而WalkPreTranslateTree方法实际上就是从当前窗口开始查找愿意进行消息翻译的类,直到找到窗口没有父类为止。在WalkPreTranslateTree方法中调用了PreTranslateMessage方法。实际上PreTranslateMessage最大的好处是我们在消息处理前可以在这个方法里面先做一些事情。

 

例如希望在一个CEdit对象里,把所有的输入的字母都以大写的形式出现。只需要在PreTranslateMessage方法中判断message是否为WM_CHAR,如果是的话,把wParam(表示键值)由小写字母的值该为大写字母的值就实现了这个功能。


 

  • 0
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值