MFC的消息处理机制及相关的宏
2015-11-15 08:42
232 查看
这段话是我在中文维基的“类成员函数指针”词条写的:
MFC类体系中,Windows消息传递处理机制是基于CCmdTarget类及其派生类的静态数据成员与静态成员函数GetThisMessageMap()。用户所写的类中的Windows消息处理函数(例如OnCommand)必须转换为CCmdTarget::*的成员函数指针类型AFX_PMSG,保存在该用户类的_messageEntries静态数组中。
调用用户类中该消息处理函数时,根据该函数保存在_messageEntries中的signature(一个无符号整型表示的函数的形参类型列表与返回值类型),把类型为void(CCmdTarget::*AFX_PMSG)(void)的成员函数指针强制转为其它类型的CCmdTarget成员函数指针(例如void(AFX_MSG_CALLCWnd::*pfn_v_i_i)(int,int),目前在unionMessageMapFunctions中列出了近百种CCmdTarget成员函数指针),然后调用转换后的成员函数指针。这是基于Visual
C++编译器把单继承的成员函数指针编译为只保存了函数的内存起始地址,因此可以在同一个单继承类中把一种类型的成员函数指针强制转换为另一种成员函数指针,或者把单继承派生类的成员函数指针强制转换为基类成员函数指针。这是打破了C++标准的违例办法。例如,对于CWnd::OnCommand函数,转换过程是:
该宏实际上增加了两个静态数据成员、一个虚函数:
这两个宏实际上定义为:
注意这三个宏中的内容共同完成了_messageEntries结构体数组的填写。
结构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(表示键值)由小写字母的值该为大写字母的值就实现了这个功能。
typedefvoid(CCmdTarget::*AFX_PMSG)(void);
调用用户类中该消息处理函数时,根据该函数保存在_messageEntries中的signature(一个无符号整型表示的函数的形参类型列表与返回值类型),把类型为void(CCmdTarget::*AFX_PMSG)(void)的成员函数指针强制转为其它类型的CCmdTarget成员函数指针(例如void(AFX_MSG_CALLCWnd::*pfn_v_i_i)(int,int),目前在unionMessageMapFunctions中列出了近百种CCmdTarget成员函数指针),然后调用转换后的成员函数指针。这是基于Visual
C++编译器把单继承的成员函数指针编译为只保存了函数的内存起始地址,因此可以在同一个单继承类中把一种类型的成员函数指针强制转换为另一种成员函数指针,或者把单继承派生类的成员函数指针强制转换为基类成员函数指针。这是打破了C++标准的违例办法。例如,对于CWnd::OnCommand函数,转换过程是:
BOOL(CWnd::*)(WPARAM,LPARAMlParam)=>void(CWnd::*)()=>void(CCmdTarget::*)()
头文件中的DECLARE_MESSAGE_MAP()
该宏实际上增加了两个静态数据成员、一个虚函数:
staticAFX_MSGMAP_ENTRY_messageEntries[]; staticAFX_MSGMAPmessageMap; virtualAFX_MSGMAP*GetMessageMap()const;
源文件中的BEGIN_MESSAGE_MAP与END_MESSAGE_MAP
这两个宏实际上定义为:
#defineBEGIN_MESSAGE_MAP(class_name,base_class)\ AFX_MSGMAP*class_name::GetMessageMap()const\ {return&class_name::message;}\ AFX_MSGMAPmessageMap=\ {&base_class::messageMap,class_name::_messageEntries}\ AFX_MSGMAP_ENTRY_messageEntries[]=\ { #defineON_COMMAND(id,memFunc)\ {WM_COMMAND,0,id,id,AFx_sig_vv,(AFX_PMSG)memFunc}, #defineEND_MESSAGE_MAP()\ {0,0,0,0,AfxSig_end,(AFX_PMSG)0}\ };
注意这三个宏中的内容共同完成了_messageEntries结构体数组的填写。
相关的数据结构
structAFX_MSGMAP{
AFX_MSGMAP*pBaseMessageMap;//指向基类的本结构。
AFX_MSGMAP_ENTRY*lpEntries;//指向本类的消息映射表。
};
structAFX_MSGMAP_ENTRY//该结构体可以存储一条消息的所有相关信息。
{
UINTnMessage;//消息ID
UINTnCode;//控件通知代码,对于窗口消息该值为0。处理命令消息和控件通知的函数使用
UINTnID;//命令ID,对于窗口消息该值为0
UINTnLastID;//是以nID开始的命令ID范围内的最后一个命令ID,对于窗口消息该值为0。处理命令消息的函数能够处理某一范围的ID
UINTnSig;//消息处理函数时成员函数指针类型,其具体的类型信息(signature)的代号
AFX_PMSGpfn;//成员函数指针类型typedefvoid(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(表示键值)由小写字母的值该为大写字母的值就实现了这个功能。
相关文章推荐
- Visual C++中MFC消息的分类
- MFC中Radio Button的用法详解
- MFC对话框中添加状态栏的方法
- MFC创建右键弹出菜单的方法
- MFC中动态创建控件以及事件响应实现方法
- C++ 关于MFC多线程编程的注意事项
- MFC程序对文件的处理方法
- MFC自定义消息的实现方法
- MFC实现在文件尾追加数据的方法
- MFC之ComboBox控件用法实例教程
- MFC绘制不规则窗体的方法
- IE8引发 VS2005/2008 MFC向导出错的解决方案
- MFC实现全屏功能代码实例
- 使用VS2010创建MFC ActiveX工程项目
- C++中MFC Tab Control控件的使用详解
- MFC程序设计常用技巧汇总
- MFC扩展DLL中导出类和对话框的实现方法
- VC中SDK与MFC的区别浅析
- 浅谈MFC 改变控件大小和位置
- VC MFC非模态对话框的实现方法