 |
我的日历 |
|
|
分类日志 |
|
|
友情链接 |
|
|
最新评论 |
|
|
搜索日志 |
|
|
访问计数 |
|
|
获取 RSS |
| |
 | |
|
MFC消息响应机制 [2005-11-9] youalwayscan 发表在 VC/MFC
| (以下根据网文总结)
1。SDK下的消息机制实现:
TranslateMessage函数用来把虚键消息翻译成字符消息并放到响应的消息队列里面, DispatchMessage函数把消息分发到相关的窗口过程。 窗口过程根据消息的类型对不同的消息进行相关的处理。
(附:一个消息的结构 typedef struct tagMSG { HWND hwnd; //和窗口过程相关的窗口的句柄 UINT message; //消息的ID号 WPARAM wParam; //和消息相关的参数 LPARAM lParam; //和消息相关的参数 DWORD time; //消息发送的时间 POINT pt; //消息发送时的鼠标的位置 }MSG; )
在SDK编程过程中,用户需要在窗口过程中分析消息的类型和跟消息一起的参数的含义,做不同的处理,相对比较麻烦,而MFC把消息调用的过程给封装起来,使用户能够通过ClassWizard方便的使用和处理Windows的各种消息。
2。MFC的消息实现相关数据结构:
MFC中所有能够进行消息处理的类都是基于CCmdTarget类的,也就是说CCmdTarget类是所有可以进行消息处理类的父类。CCmdTarget类是MFC处理命令消息的基础和核心。
在MFC的框架结构下,可以进行消息处理的类的头文件里面都会含有DECLARE_MESSAGE_MAP()宏,这里主要进行消息映射和消息处理函数的声明。 BEGIN_MESSAGE_MAP(CInheritClass, CBaseClass) //{{AFX_MSG_MAP(CInheritClass) //}}AFX_MSG_MAP END_MESSAGE_MAP()
同时,MFC定义了下面的两个主要结构: AFX_MSGMAP_ENTRY struct AFX_MSGMAP_ENTRY { UINT nMessage; // windows message (Windows消息的ID号) UINT nCode; // control code or WM_NOTIFY code (控制消息的通知码) UINT nID; // control ID (or 0 for windows messages) (Windows控制消息的ID) UINT nLastID; // used for entries specifying a range of control id's UINT nSig; // signature type (action) or pointer to message (消息的动作标识) AFX_PMSG pfn; // routine to call (or special value) (一个指向和该消息相应的执行函数的指针) };
AFX_MSGMAP: //AFX_MSGMAP主要作用是两个:1,用来得到基类的消息映射入口地址。2,得到本身的消息映射入口地址。 struct AFX_MSGMAP { #ifdef _AFXDLL const AFX_MSGMAP* (PASCAL* pfnGetBaseMap)(); #else const AFX_MSGMAP* pBaseMap; #endif const AFX_MSGMAP_ENTRY* lpEntries; };
根据以上MFC系统定义的数据结构, MFC把所有的消息一条条填入到AFX_MSGMAP_ENTRY结构中去,形成一个数组,该数组存放了所有的消息和与它们相关的参数。同时通过AFX_MSGMAP能得到该数组的首地址,同时得到基类的消息映射入口地址,这是为了当本身对该消息不响应的时候,就调用其基类的消息响应。
3。MFC下消息机制的实现过程:
实际上所有MFC的窗口类都通过钩子函数_AfxCbtFilterHook截获消息,并且在钩子函数_AfxCbtFilterHook中把窗口过程设定为AfxWndProc。原来的窗口过程保存在成员变量m_pfnSuper中。所以在MFC框架下,一般一个消息的处理过程是这样的:
(1) 函数AfxWndProc接收Windows操作系统发送的消息。 (2) 函数AfxWndProc调用函数AfxCallWndProc进行消息处理,把对句柄的操作转换成对CWnd对象的操作。 (3) 函数AfxCallWndProc调用CWnd类的方法WindowProc进行消息处理。 (注意AfxWndProc和AfxCallWndProc都是AFX的API函数。而WindowProc已经是CWnd的一个方法。所以可以注意到在WindowProc中已经没有关于句柄或者是CWnd的参数了。) (4) 方法WindowProc调用方法OnWndMsg进行正式的消息处理,即把消息派送到相关的方法中去处理。如果nWndMsg方法没有对消息进行处理的话,就调用DefWindowProc对消息进行处理。这是实际上是调用原来的窗口过程进行缺省的消息处理。
第(4)步中消息派送的实现过程:
实际上在CWnd类中都保存了一个AFX_MSGMAP的结构,而在AFX_MSGMAP结构中保存有所有我们用ClassWizard生成的消息的数组的入口,我们把传给OnWndMsg的message和数组中的所有的message进行比较,找到匹配的那一个消息。这些由系统通过函数AfxFindMessageEntry来实现。
找到了那个message,实际上我们就得到一个AFX_MSGMAP_ENTRY结构,而我们在上面已经提到AFX_MSGMAP_ENTRY保存了和该消息相关的所有信息,其中主要的是消息的动作标识和跟消息相关的执行函数。然后我们就可以根据消息的动作标识调用相关的执行函数,而这个执行函数实际上就是通过ClassWizard在类实现中定义的一个方法。这样就把消息的处理转化到类中的一个方法的实现上。
4。与SDK下的消息机制实现过程进行比较:
所以如果正常的消息处理的话,MFC窗口类是完全脱离了原来的窗口过程,用自己的一套体系结构实现消息的映射和处理。即先调用MFC窗口类挂上去的窗口过程,再调用原先的窗口过程。并且用户面对和消息相关的参数不再是死板的wParam和lParam,而是和消息类型具体相关的参数。比如和消息WM_LbuttonDown相对应的方法OnLButtonDown的两个参数是nFlags和point。nFlags表示在按下鼠标左键的时候是否有其他虚键按下,point更简单,就是表示鼠标的位置。
5。知识深入、扩充:
同时MFC窗口类消息传递中还提供了两个函数,分别为WalkPreTranslateTree和PreTranslateMessage。
MFC框架生成的程序,都是从CWinApp开始执行的,而CWinapp实际继承了CWinThread类。在CWinThread的运行过程中会调用窗口类中的WalkPreTranslateTree方法。而WalkPreTranslateTree方法实际上就是从当前窗口开始查找愿意进行消息翻译的类,直到找到窗口没有父类为止。 在WalkPreTranslateTree方法中调用了PreTranslateMessage方法。实际上PreTranslateMessage最大的好处是我们在消息处理前可以在这个方法里面先做一些事情。举一个简单的例子,比如我们希望在一个CEdit对象里,把所有的输入的字母都以大写的形式出现。我们只需要在PreTranslateMessage方法中判断message是否为WM_CHAR,如果是的话,把wParam(表示键值)由小写字母的值该为大写字母的值就实现了这个功能。
因此: (一)在消息的处理方法里面即OnChar中,当然最后我们不再调用CEdit::OnChar(nChar, nRepCnt, nFlags),而是直接调用DefWindowProc(WM_CHAR,nChar,MAKELPARAM (nRepCnt,nFlags))。因为从我们上面的分析可以知道CEdit::OnChar(nChar, nRepCnt, nFlags)实际上也就是对DefWindowProc方法的调用。 (二)我们可以直接重载DefWindowProc方法,对message类型等于WM_CHAR的,直接修改nChar的值即可。
| |
|
|