【3D技术宅公社】XR数字艺术论坛  XR技术讨论 XR互动电影 定格动画

 找回密码
 立即注册

QQ登录

只需一步,快速开始

调查问卷
论坛即将给大家带来全新的技术服务,面向三围图形学、游戏、动画的全新服务论坛升级为UTF8版本后,中文用户名和用户密码中有中文的都无法登陆,请发邮件到324007255(at)QQ.com联系手动修改密码

3D技术论坛将以计算机图形学为核心,面向教育 推出国内的三维教育引擎该项目在持续研发当中,感谢大家的关注。

查看: 3643|回复: 3

MFC技术内幕系列之(四)---MFC消息映射与消息传递内幕

[复制链接]
发表于 2007-4-6 17:06:05 | 显示全部楼层 |阅读模式
////////////////////////////////////////////////////////////////////////////////////<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /********* 文章系列:MFC<nobr><a id="key4" target="_blank">技术</a></nobr>内幕系列***********/<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /************MFC技术内幕系列之(四)***********/<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /*****文章题目:MFC消息映射与消息传递内幕******/<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /*&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Copyright(c)2002 bigwhite&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; */<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /*&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; All rights Reserved&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp; */<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /&nbsp;&nbsp; *********关键字:消息映射,消息传递************/<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /*&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; <nobr><a id="key7" target="_blank">时间</a></nobr>:2002.7.23&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; */<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /*&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 注释:本文所涉及的程序源代码均在Microsoft&nbsp; &nbsp;*/<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Visual Studio<a href="http://dev.21tx.com/dotnet/" target="_blank"><font color="#3366cc">.net</font></a> Ent<a href="http://dev.21tx.com/corp/solution/erp/" target="_blank"><font color="#3366cc">ERP</font></a>rise Architect Edition&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /*&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <nobr><a id="key3" target="_blank">开发</a></nobr>工具包提供的源代码中&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; */<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br/>//////////////////////////////////////////////////////////////////////////////////////////////////////////////////<br/>引言:<br/>&nbsp;&nbsp;&nbsp; <a href="http://dev.21tx.com/os/windows/" target="_blank"><font color="#3366cc">Windows</font></a><a href="http://dev.21tx.com/os/" target="_blank"><font color="#3366cc">操作系统</font></a>是以消息为基础,事件<a href="http://drivers.21tx.com/" target="_blank"><font color="#3366cc">驱动</font></a>的。作为程序员了解操作系统的消息传递机制是非常必要的。Microsoft的MFC又它自己的一套支持Windows操作系统消息机制的技术--消息映射(Message Mapping)和命令传递(Command Routing),在这篇文章中我就详细的挖掘一下MFC的消息映射技术以及命令传递技术。<p>正文:<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ///////////////////////////////////////////////<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /*&nbsp;&nbsp;&nbsp;&nbsp; 1.Windows消息概览&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; */<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //////////////////////////////////////////////<br/>&nbsp;&nbsp;&nbsp; 对于消息,程序员应该不陌生。WM_CREATE,WM_PAINT等等都是Windows程序<nobr><a id="key1" target="_blank">设计</a></nobr>中必不可缺少的组成部分。大多有关MFC Win32编程的书籍都将Windows消息分为三大类即:<br/>&nbsp;&nbsp;&nbsp; * 标准消息:&nbsp;&nbsp; 任何以WM_开头的消息(WM_COMMAND除外);如:WM_QUIT,WM_CREATE;<br/>&nbsp;&nbsp;&nbsp; * 命令消息:&nbsp;&nbsp; WM_COMMAND;<br/>&nbsp;&nbsp;&nbsp; * 子窗口通知: 由子窗口(大多为控件)产生并发送到该控件所属的父窗口的消息。(注意:此类消息也&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 以WM_COMMAND形式出现)<br/>&nbsp;&nbsp;&nbsp; 消息类型我们已经了解了,下面我们就来看看消息映射是如何<nobr><a id="key6" target="_blank">工作</a></nobr>的:<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //////////////////////////////////////////////////////<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /*&nbsp; 2.MFC消息映射网的组成元素 */<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //////////////////////////////////////////////////////&nbsp;&nbsp;&nbsp; <br/>&nbsp;&nbsp; 我的前几篇文章中涉及到了MFC内部建立的一些“网”技术,比如“<nobr><a id="key8" target="_blank">执行</a></nobr>期类型识别网”等,这回我们将建立一个消息映射网,这个网的建立与前面相同的是它也利用了一些神秘的宏。下面我们就来掀开它们的神秘面纱。<br/>&nbsp;&nbsp; 我们先简单地看看这些宏在程序源文件中的什么地方? <br/>&nbsp;&nbsp; //in xx.h<br/>&nbsp;&nbsp; class theClass<br/>&nbsp; {<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ...//<br/>&nbsp;&nbsp;&nbsp;&nbsp; DECLARE_MESSAGE_MAP()<br/>&nbsp;&nbsp; };<br/>&nbsp;&nbsp; //in xx.cpp<br/>&nbsp;&nbsp; BEGIN_MESSAGE_MAP(theClass, baseClass)<br/>&nbsp;ON_COMMAND( ID_MYCMD, OnMyCommand )<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ON_WM_CREATE()<br/>&nbsp;&nbsp; END_MESSAGE_MAP()<br/>&nbsp;&nbsp; ...//<br/>&nbsp;&nbsp; <br/>&nbsp;&nbsp; 这些宏的定义如下:<br/>&nbsp;&nbsp; //in Afxwin.h<br/>&nbsp;&nbsp; #define DECLARE_MESSAGE_MAP() \<br/>&nbsp;&nbsp; private: \<br/>&nbsp;static const AFX_MSGMAP_ENTRY _messageEntries[]; \<br/>&nbsp;&nbsp; protected: \<br/>&nbsp;static const AFX_MSGMAP messageMap; \<br/>&nbsp;static const AFX_MSGMAP* PASCAL GetThisMessageMap(); \<br/>&nbsp;virtual const AFX_MSGMAP* GetMessageMap() const; \</p><p>&nbsp;&nbsp; #define BEGIN_MESSAGE_MAP(theClass, baseClass) \<br/>&nbsp;const AFX_MSGMAP* PASCAL theClass::GetThisMessageMap() \<br/>&nbsp;&nbsp;{ return &amp;theClass::messageMap; } \<br/>&nbsp;const AFX_MSGMAP* theClass::GetMessageMap() const \<br/>&nbsp;&nbsp;{ return &amp;theClass::messageMap; } \<br/>&nbsp;AFX_COMDAT const AFX_MSGMAP theClass::messageMap = \<br/>&nbsp;{ &amp;baseClass::GetThisMessageMap, &amp;theClass::_messageEntries[0] }; \<br/>&nbsp;AFX_COMDAT const AFX_MSGMAP_ENTRY theClass::_messageEntries[] = \<br/>&nbsp;{ \</p><p>&nbsp;&nbsp; #define END_MESSAGE_MAP() \<br/>&nbsp;&nbsp;{0, 0, 0, 0, AfxSig_end, (AFX_PMSG)0 } \<br/>&nbsp;}; \<br/>&nbsp;&nbsp; DECLARE_MESSAGE_MAP()宏为每个类添加了四个东东,包括那个重要的消息映射表messageMap和消息入口结构数组AFX_MSGMAP_ENTRY _messageEntries[];BEGIN_MESSAGE_MAP(theClass, baseClass)和END_MESSAGE_MAP()宏则初始化了它们,随后我将带领大家看看这个初始化过程。</p><p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ///////////////////////////////////////////////<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /*&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 3.MFC消息映射表&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; */<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //////////////////////////////// //////////////<br/>&nbsp;&nbsp; 下面我们看看消息映射表messageMap和消息入口结构AFX_MSGMAP_ENTRY的定义:<br/>&nbsp;//in Afxwin.h<br/>&nbsp;struct AFX_MSGMAP_ENTRY<br/>&nbsp;{<br/>&nbsp;UINT nMessage;&nbsp;&nbsp; // windows message<br/>&nbsp;UINT nCode;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // control code or WM_NOTIFY code<br/>&nbsp;UINT nID;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // control ID (or 0 for windows messages)<br/>&nbsp;UINT nLastID;&nbsp;&nbsp;&nbsp; // used for entries specifying a range of control id's<br/>&nbsp;UINT_PTR nSig;&nbsp;&nbsp; // signature type (action) or pointer to message #<br/>&nbsp;AFX_PMSG pfn;&nbsp;&nbsp;&nbsp; // routine to call (or special value)<br/>&nbsp;};</p><p><br/>&nbsp;struct AFX_MSGMAP<br/>&nbsp;{<br/>&nbsp;#ifdef _AFXDLL<br/>&nbsp;const AFX_MSGMAP* (PASCAL* pfnGetBaseMap)();//基类的映射表指针,本程序将使用<br/>&nbsp;#else<br/>&nbsp;const AFX_MSGMAP* <a href="http://dev.21tx.com/language/pb/" target="_blank"><font color="#3366cc">B</font></a>aseMap;<br/>&nbsp;#endif<br/>&nbsp;const AFX_MSGMAP_ENTRY* lpEntries;<br/>&nbsp;};<br/>&nbsp;<br/>&nbsp; 其中AFX_MSGMAP结构中包含一个基类的映射表指针和一个指向消息入口结构AFX_MSGMAP_ENTRY的指针。</p><p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /////////////////////////////////////////////////<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /*&nbsp;&nbsp;&nbsp; 4.MFC消息映射宏展开&nbsp;&nbsp;&nbsp;&nbsp; */<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ///////////////////////////////////////////////// </p><p>&nbsp; 上面的宏展开后代码如下:(以CMaimFrame为例)<br/>&nbsp;&nbsp; //in MaimFrm.h<br/>&nbsp;&nbsp; class CMaimFrame : public CFrameWnd<br/>&nbsp; { <br/>&nbsp;&nbsp;&nbsp; ...//<br/>&nbsp;&nbsp;&nbsp; private:<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; static const AFX_MSGMAP_ENTRY _messageEntries[];<br/>&nbsp;&nbsp;&nbsp; protected: <br/>&nbsp;static const AFX_MSGMAP messageMap; <br/>&nbsp;static const AFX_MSGMAP* PASCAL GetThisMessageMap(); <br/>&nbsp;virtual const AFX_MSGMAP* GetMessageMap() const; <br/>&nbsp; };<br/>&nbsp; //in MaimFrm.cpp<br/>&nbsp;&nbsp;&nbsp; const AFX_MSGMAP* PASCAL CMaimFrame::GetThisMessageMap() <br/>&nbsp;&nbsp;{ return &amp;CMaimFrame::messageMap; } <br/>&nbsp;&nbsp;&nbsp; const AFX_MSGMAP* CMaimFrame::GetMessageMap() const <br/>&nbsp;&nbsp;{ return &amp;CMaimFrame::messageMap; } <br/>&nbsp;&nbsp;&nbsp; AFX_COMDAT const AFX_MSGMAP theClass::messageMap = <br/>&nbsp;{ &amp;CFrameWnd::GetThisMessageMap, &amp;CMaimFrame::_messageEntries[0] }; <br/>&nbsp;&nbsp;&nbsp; AFX_COMDAT const AFX_MSGMAP_ENTRY CMaimFrame::_messageEntries[] = <br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; { <br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ...//&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ...<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {0, 0, 0, 0, AfxSig_end, (AFX_PMSG)0 } <br/>&nbsp;}; <br/>&nbsp;&nbsp; 相信大家看了后大多源代码都能够理解,但是AFX_MSGMAP_ENTRY结构还是能够引起我们的兴趣的。下面让我们看看_messageEntries[]是如何被初始化的:<br/>&nbsp;&nbsp; 我们还是举例来说明吧!(还是CMainFrame为例吧)<br/>&nbsp;&nbsp; BEGIN_MESSAGE_MAP(CMainFrame, CFrameWnd)<br/>&nbsp;ON_WM_CREATE()<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ON_COMMAND( ID_MYCMD, OnMyCommand )<br/>&nbsp;&nbsp; END_MESSAGE_MAP()<br/>&nbsp;&nbsp; <br/>&nbsp;&nbsp; 大家看到了夹在BEGIN_MESSAGE_MAP和END_MESSAGE_MAP()之间的宏,这些宏可分为基类,一类是Windows预定义消息宏(比如:ON_WM_CREATE(),ON_WM_DESTROY()等定义在afxmsg_.h中的Message map tables for Windows messages),一类是自定义的ON_COMMAND宏以及类似的如ON_UPDATE_COMMAND_UI等宏 。<br/>&nbsp;&nbsp; //in afxmsg_.h<br/>&nbsp;&nbsp; // Message map tables for Windows messages<br/>&nbsp;&nbsp; #define ON_WM_CREATE() \<br/>&nbsp;{ WM_CREATE, 0, 0, 0, AfxSig_is, \<br/>&nbsp;&nbsp;(AFX_PMSG) (AFX_PMSGW) \<br/>&nbsp;&nbsp;(static_cast&lt; int (AFX_MSG_CALL CWnd::*)(LPCREATESTRUCT) &gt; (OnCreate)) },<br/>&nbsp; <br/>&nbsp;&nbsp; #define ON_COMMAND(id, memberFxn) \<br/>&nbsp;{ WM_COMMAND, CN_COMMAND, (WORD)id, (WORD)id, AfxSigCmd_v, \<br/>&nbsp;&nbsp;static_cast&lt;AFX_PMSG&gt; (memberFxn) },</p><p>&nbsp;&nbsp; AFX_MSGMAP_ENTRY结构初始化过程:<br/>&nbsp;&nbsp; AFX_COMDAT const AFX_MSGMAP_ENTRY CMaimFrame::_messageEntries[] = <br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; { <br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; { WM_CREATE, 0, 0, 0, AfxSig_is, <br/>&nbsp;&nbsp;(AFX_PMSG) (AFX_PMSGW) <br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; (static_cast&lt; int (AFX_MSG_CALL CWnd::*)(LPCREATESTRUCT) &gt; (OnCreate)) },<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; { WM_COMMAND, CN_COMMAND, (WORD)ID_MYCMD, (WORD)ID_MYCMD, AfxSigCmd_v, \<br/>&nbsp;&nbsp;static_cast&lt;AFX_PMSG&gt; ( OnMyCommand) },&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {0, 0, 0, 0, AfxSig_end, (AFX_PMSG)0 } <br/>&nbsp;};<br/>&nbsp;&nbsp; 现在一切都清楚了吧!</p><p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //////////////////////////////////////////////////<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /*&nbsp;&nbsp;&nbsp; 5.MFC消息映射网的连接&nbsp;&nbsp; */<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ////////////////////////////////////////////////// <br/>&nbsp;&nbsp; MFC消息映射网的连接也是在初始化过程中完成的,其建立过程很简单。主要有关成员有:<br/>&nbsp;&nbsp; private:<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; static const AFX_MSGMAP_ENTRY _messageEntries[];<br/>&nbsp;&nbsp; protected: <br/>&nbsp;static const AFX_MSGMAP messageMap; <br/>&nbsp;&nbsp; 和BEGIN_MESSAGE_MAP()宏开后的<br/>&nbsp;&nbsp; AFX_COMDAT const AFX_MSGMAP theClass::messageMap = <br/>&nbsp;{ &amp;baseClass::GetThisMessageMap, &amp;theClass::_messageEntries[0] };<br/>&nbsp;&nbsp; 该宏将pfnGetBaseMap赋值为其基类的messageMap地址;将AFX_MSGMAP_ENTRY* lpEntries赋值为该类的<br/>_messageEntries[0];<br/>&nbsp;&nbsp; 这样一个类不仅拥有本类的messageMap,而且还拥有其基类的messageMap,依此类推MFC消息映射网的连接<br/>就建立了,最终的基类都是CCmdTarget</p>
 楼主| 发表于 2007-4-6 17:07:42 | 显示全部楼层
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //////////////////////////////////////////////////<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /*&nbsp;&nbsp;&nbsp; 6.MFC命令传递机制概述&nbsp;&nbsp; */<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //////////////////////////////////////////////////<br/>&nbsp;&nbsp; 有了MFC消息映射网就为命令传递打下了坚实的基础。Win32API程序员都熟悉传统的API<nobr><a id="key5" target="_blank">编程</a></nobr>都有一个<br/>WndProc回调函数来集中处理各种的Windows消息,然而在MFC中我们却怎么也看不见WndProc回调函数的踪影了。而MFC命令传递机制恰是为了将各种消息“拐弯抹角”地送到各个对应的"WndProc"函数的一种技术。MFC是如何将各种Windows消息准确的送到期该区的地方呢? MFC使用了钩子函数等技术来保证其准确性和全面性。<br/>&nbsp;&nbsp; 不知大家是否还记得MFC在注册窗口类时作了什么?<br/>&nbsp;&nbsp; BOOL AFXAPI AfxEndDeferRe<a href="http://dev.21tx.com/corp/gis/" target="_blank"><font color="#3366cc">GIS</font></a>terClass(LONG fToRegister)//部分源代码<br/>&nbsp; {<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ...//<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // common initialization<br/>&nbsp;WNDCLASS wndcls;<br/>&nbsp;memset(&amp;wndcls, 0, sizeof(WNDCLASS));&nbsp;&nbsp; // start with NULL defaults<br/>&nbsp;wndcls.lpfnWndProc = DefWindowProc;<br/>&nbsp;...//<br/>&nbsp;&nbsp; }<br/>&nbsp; 可以看到MFC注册时将wndcls.lpfnWndProc赋值为DefWindowProc函数,那么实际上是否消息都是由它处理的呢?显然不可能,MFC又不知道我们要处理什么消息。那么还有什么函数能帮我们处理消息呢?这就是我要讲的;&nbsp; 在MFC技术内幕系列之(二)----《 MFC文档视图结构内幕》中曾提到“CWnd::CreateEx函数调用了AfxHookWindowCreate(this);后者是干什么的呢?其实它与消息映射和命令传递有关。”<br/>&nbsp;&nbsp; 实际上MFC命令传递机制就是从这里AfxHookWindowCreate(this)开始的。还是老办法看看代码吧:<br/>&nbsp;//in wincore.cpp<br/>&nbsp;void AFXAPI AfxHookWindowCreate(CWnd* pWnd)<br/>&nbsp;{<br/>&nbsp;...//<br/>&nbsp;if (pThreadState-&gt;m_hHookOldCbtFilter == NULL)<br/>&nbsp;{<br/>&nbsp;&nbsp;pThreadState-&gt;m_hHookOldCbtFilter = ::SetWindowsHookEx(WH_CBT,<br/>&nbsp;&nbsp;&nbsp;_AfxCbtFilterHook, NULL, ::GetCurrentThreadId());<br/>&nbsp;&nbsp;if (pThreadState-&gt;m_hHookOldCbtFilter == NULL)<br/>&nbsp;&nbsp;&nbsp;AfxThrowMemoryException();<br/>&nbsp;}<br/>&nbsp;...//<br/>&nbsp;}<br/>&nbsp; 该函数设置了消息钩子,其钩子处理函数为_AfxCbtFilterHook;这里简介一下钩子函数:<br/>&nbsp; 用我的理解,钩子就是能给你一个在某个消息到达其默认的处理函数之前处理该消息机会的工具。<br/>与钩子有关的函数主要有三个:<br/>HHOOK SetWindowsHookEx(int idHook, HOOKPROC lpfn, HINSTANCE hMod, DWORD dwThreadId );<br/>LRESULT CallNextHookEx(HHOOK hhk,&nbsp; int nCode,&nbsp;&nbsp;&nbsp; WPARAM wParam,&nbsp;&nbsp; LPARAM lParam&nbsp; );<br/>BOOL UnhookWindowsHookEx( HHOOK hhk&nbsp;&nbsp; // handle to hook procedure);<br/>关于这三个函数我也不想多解释,大家看看MFC有关文档吧!这里主要讲的是在AfxHookWindowCreate(CWnd* pWnd)中注册的钩子函数的类型(hook type)--WH_CBT;<br/>有关WH_CBT,MFC文档时如是说的:<br/>&nbsp; Installs a hook procedure that receives notifications useful to a computer-based training (CBT) application. The system calls this function(这里指的是_AfxCbtFilterHook) before activating, creating, destroying, minimizing, maximizing, moving, or sizing a window; before completing a system command; before removing a mouse or keyboard event from the system message queue; before setting the keyboard focus; or before synchronizing with the system message queue. A computer-based training (CBT) application uses this hook procedure to receive useful notifications from the system. <br/>&nbsp; <br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /////////////////////////////////////////////<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /*&nbsp;&nbsp;&nbsp; 7.偷换“窗口函数”&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; */<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /////////////////////////////////////////////<p>&nbsp;&nbsp; 这会知道了吧,当发生窗口(包括子窗口)发生被激活,创建,撤销,最小化等时候,应用程序将调用<br/>_AfxCbtFilterHook函数;下面就让我们看看_AfxCbtFilterHook函数做了个啥:<br/>&nbsp;//in wincore.cpp<br/>// Window creation hooks<br/>&nbsp;LRESULT CALLBACK _AfxCbtFilterHook(int code, WPARAM wParam, LPARAM lParam)<br/>&nbsp;{<br/>&nbsp;_AFX_THREAD_STATE* pThreadState = _afxThreadState.GetData();<br/>&nbsp;if (code != HCBT_CREATEWND)<br/>&nbsp;{<br/>&nbsp;&nbsp;// wait for HCBT_CREATEWND just pass others on...<br/>&nbsp;&nbsp;return CallNextHookEx(pThreadState-&gt;m_hHookOldCbtFilter, code,<br/>&nbsp;&nbsp;&nbsp;wParam, lParam);<br/>&nbsp;}</p><p>&nbsp;...//&nbsp;&nbsp; CWnd* pWndInit = pThreadState-&gt;m_pWndInit;<br/>&nbsp;&nbsp;HWND hWnd = (HWND)wParam;<br/>&nbsp;&nbsp;WNDPROC oldWndProc;<br/>&nbsp;&nbsp;if (pWndInit != NULL)<br/>&nbsp;&nbsp;{<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; #ifdef _AFXDLL<br/>&nbsp;&nbsp;&nbsp;AFX_MANAGE_STATE(pWndInit-&gt;m_pModuleState);<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; #endif</p><p>&nbsp;&nbsp;&nbsp;// the window should not be in the permanent map at this time<br/>&nbsp;&nbsp;&nbsp;ASSERT(CWnd::FromHandlePermanent(hWnd) == NULL);</p><p>&nbsp;&nbsp;&nbsp;// connect the HWND to pWndInit...<br/>&nbsp;&nbsp;&nbsp;pWndInit-&gt;Attach(hWnd);<br/>&nbsp;&nbsp;&nbsp;// allow other subclassing to occur first<br/>&nbsp;&nbsp;&nbsp;pWndInit-&gtreSubclassWindow();//***</p><p>&nbsp;&nbsp;&nbsp;WNDPROC *pOldWndProc = pWndInit-&gt;GetSuperWndProcAddr();<br/>&nbsp;&nbsp;&nbsp;ASSERT(pOldWndProc != NULL);</p><p>&nbsp;&nbsp;&nbsp;// subclass the window with standard AfxWndProc<br/>&nbsp;&nbsp;&nbsp;WNDPROC afxWndProc = AfxGetAfxWndProc();//***<br/>&nbsp;&nbsp;&nbsp;oldWndProc = (WNDPROC)SetWindowLongPtr(hWnd, GWLP_WNDPROC,<br/>&nbsp;&nbsp;&nbsp;&nbsp;(DWORD_PTR)afxWndProc);//***<br/>&nbsp;&nbsp;&nbsp;ASSERT(oldWndProc != NULL);<br/>&nbsp;&nbsp;&nbsp;if (oldWndProc != afxWndProc)<br/>&nbsp;&nbsp;&nbsp;&nbsp;*pOldWndProc = oldWndProc;</p><p>&nbsp;&nbsp;&nbsp;pThreadState-&gt;m_pWndInit = NULL;<br/>&nbsp;&nbsp;}<br/>&nbsp;...//<br/>&nbsp;lCallNextHook:<br/>&nbsp;LRESULT lResult = CallNextHookEx(pThreadState-&gt;m_hHookOldCbtFilter, code,<br/>&nbsp;&nbsp;wParam, lParam);</p><p>&nbsp;#ifndef _AFXDLL<br/>&nbsp;if (bContextIsDLL)<br/>&nbsp;{<br/>&nbsp;&nbsp;::UnhookWindowsHookEx(pThreadState-&gt;m_hHookOldCbtFilter);<br/>&nbsp;&nbsp;pThreadState-&gt;m_hHookOldCbtFilter = NULL;<br/>&nbsp;}<br/>&nbsp;#endif<br/>&nbsp;return lResult;<br/>&nbsp;}</p><p>&nbsp;void CWnd:reSubclassWindow()<br/>{<br/>&nbsp;// no default processing<br/>&nbsp;}<br/>&nbsp; <br/>&nbsp;// always indirectly <a href="http://dev.21tx.com/database/access/" target="_blank"><font color="#3366cc">Access</font></a>ed via AfxGetAfxWndProc<br/>&nbsp;WNDPROC AFXAPI AfxGetAfxWndProc()<br/>&nbsp;{<br/>&nbsp;#ifdef _AFXDLL<br/>&nbsp;return AfxGetModuleState()-&gt;m_pfnAfxWndProc;<br/>&nbsp;#else<br/>&nbsp;return &amp;AfxWndProc;<br/>&nbsp;#endif<br/>&nbsp;}</p><p>&nbsp; 原来_AfxCbtFilterHook函数偷换了窗口函数,将原来的DefWndProc换成AfxWndProc函数.<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</p>
 楼主| 发表于 2007-4-6 17:08:57 | 显示全部楼层
<p>&nbsp; ////////////////////////////////////////////////////<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /*&nbsp;&nbsp; 8.MFC的“WndProc”函数&nbsp;&nbsp; */<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ////////////////////////////////////////////////////<br/>&nbsp;&nbsp; AfxWndProc函数就可以说是MFC的“WndProc”函数,它也是MFC中消息传递的开始,其代码如下:<br/>//in wincore.cpp<br/>// The WndProc for all CWnd's and derived classes<br/>LRESULT CALLBACK AfxWndProc(HWND hWnd, UINT nMsg, WPARAM wParam, LPARAM lParam)<br/>{<br/>&nbsp;// special message which identifies the window as using AfxWndProc<br/>&nbsp;if (nMsg == WM_QUERYAFXWNDPROC)<br/>&nbsp; return 1;</p><p>&nbsp;// all other messages route through message map<br/>&nbsp;CWnd* pWnd = CWnd::FromHandlePermanent(hWnd);<br/>&nbsp;ASSERT(pWnd != NULL);<br/>&nbsp;ASSERT(pWnd-&gt;m_hWnd == hWnd);<br/>&nbsp;return AfxCallWndProc(pWnd, hWnd, nMsg, wParam, lParam);<br/>}</p><p>// Official way to send message to a CWnd<br/>LRESULT AFXAPI AfxCallWndProc(CWnd* pWnd, HWND hWnd, UINT nMsg,<br/>&nbsp;WPARAM wParam = 0, LPARAM lParam = 0)<br/>{<br/>&nbsp;_AFX_THREAD_STATE* pThreadState = _afxThreadState.GetData();<br/>&nbsp;MSG oldState = pThreadState-&gt;m_lastSentMsg;&nbsp;&nbsp; // save for nesting<br/>&nbsp;pThreadState-&gt;m_lastSentMsg.hwnd = hWnd;<br/>&nbsp;pThreadState-&gt;m_lastSentMsg.message = nMsg;<br/>&nbsp;pThreadState-&gt;m_lastSentMsg.wParam = wParam;<br/>&nbsp;pThreadState-&gt;m_lastSentMsg.lParam = lParam;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ...//<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // in debug builds and warn the user.<br/>&nbsp;LRESULT lResult;<br/>&nbsp;TRY<br/>&nbsp;{<br/>#ifndef _AFX_NO_OCC_SUPPORT<br/>&nbsp; // special case for WM_DESTROY<br/>&nbsp; if ((nMsg == WM_DESTROY) &amp;&amp; (pWnd-&gt;m_pCtrlCont != NULL))<br/>&nbsp;&nbsp; pWnd-&gt;m_pCtrlCont-&gt;OnUIActivate(NULL);<br/>#endif<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // special case for WM_INITDIALOG<br/>&nbsp; CRect rectOld;<br/>&nbsp; DWORD dwStyle = 0;<br/>&nbsp; if (nMsg == WM_INITDIALOG)<br/>&nbsp;&nbsp; _AfxPreInitDialog(pWnd, &amp;rectOld, &amp;dwStyle);</p><p>&nbsp; // delegate to object's WindowProc<br/>&nbsp; lResult = pWnd-&gt;WindowProc(nMsg, wParam, lParam);//***</p><p>&nbsp; // more special case for WM_INITDIALOG<br/>&nbsp; if (nMsg == WM_INITDIALOG)<br/>&nbsp;&nbsp; _AfxPostInitDialog(pWnd, rectOld, dwStyle);<br/>&nbsp;}<br/>&nbsp;CATCH_ALL(e)<br/>&nbsp;{<br/>&nbsp; ...//<br/>&nbsp;}<br/>&nbsp;END_CATCH_ALL</p><p>&nbsp;pThreadState-&gt;m_lastSentMsg = oldState;<br/>&nbsp;return lResult;<br/>}</p><p>// main WindowProc implementation<br/>LRESULT CWnd::WindowProc(UINT message, WPARAM wParam, LPARAM lParam)<br/>{<br/>&nbsp;// OnWndMsg does most of the work, except for DefWindowProc call<br/>&nbsp;LRESULT lResult = 0;<br/>&nbsp;if (!OnWndMsg(message, wParam, lParam, &amp;lResult))<br/>&nbsp; lResult = DefWindowProc(message, wParam, lParam);<br/>&nbsp;return lResult;<br/>}</p><p>BOOL CWnd::OnWndMsg(UINT message, WPARAM wParam, LPARAM lParam, LRESULT* pResult)<br/>{<br/>&nbsp;LRESULT lResult = 0;<br/>&nbsp;union MessageMapFunctions mmf;<br/>&nbsp;mmf.pfn = 0;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // special case for commands<br/>&nbsp;if (message == WM_COMMAND)<br/>&nbsp;{<br/>&nbsp; if (OnCommand(wParam, lParam))<br/>&nbsp; {<br/>&nbsp;&nbsp; lResult = 1;<br/>&nbsp;&nbsp; goto LReturnTrue;<br/>&nbsp; }<br/>&nbsp; return FALSE;<br/>&nbsp;}<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // special case for notifies<br/>&nbsp;if (message == WM_NOTIFY)<br/>&nbsp;{<br/>&nbsp; NMHDR* pNMHDR = (NMHDR*)lParam;<br/>&nbsp; if (pNMHDR-&gt;hwndFrom != NULL &amp;&amp; OnNotify(wParam, lParam, &amp;lResult))<br/>&nbsp;&nbsp; goto LReturnTrue;<br/>&nbsp; return FALSE;<br/>&nbsp;}<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ...//<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; const AFX_MSGMAP* pMessageMap; pMessageMap = GetMessageMap();<br/>&nbsp;UINT iHash; iHash = (LOWORD((DWORD_PTR)pMessageMap) ^ message) &amp; (iHashMax-1);<br/>&nbsp;AfxLockGlobals(CRIT_WINMSGCACHE);<br/>&nbsp;AFX_MSG_CACHE* pMsgCache; pMsgCache = &amp;_afxMsgCache[iHash];<br/>&nbsp;const AFX_MSGMAP_ENTRY* lpEntry;<br/>&nbsp;if (message == pMsgCache-&gt;nMsg &amp;&amp; pMessageMap == pMsgCache-&gt;pMessageMap)<br/>&nbsp;{<br/>&nbsp; // cache hit<br/>&nbsp; lpEntry = pMsgCache-&gt;lpEntry;<br/>&nbsp; AfxUnlockGlobals(CRIT_WINMSGCACHE);<br/>&nbsp; if (lpEntry == NULL)<br/>&nbsp;&nbsp; return FALSE;</p><p>&nbsp; // cache hit, and it needs to be handled<br/>&nbsp; if (message &lt; 0xC000)<br/>&nbsp;&nbsp; goto LDispatch;<br/>&nbsp; else<br/>&nbsp;&nbsp; goto LDispatchRegistered;<br/>&nbsp;}<br/>&nbsp;else<br/>&nbsp;{<br/>&nbsp; // not in cache, look for it<br/>&nbsp; pMsgCache-&gt;nMsg = message;<br/>&nbsp; pMsgCache-&gt;pMessageMap = pMessageMap;</p><p>&nbsp; #ifdef _AFXDLL<br/>&nbsp; for (/* pMessageMap already init'ed */; pMessageMap-&gt;pfnGetBaseMap != NULL;<br/>&nbsp;&nbsp; pMessageMap = (*pMessageMap-&gt;pfnGetBaseMap)())<br/>&nbsp; #else<br/>&nbsp; for (/* pMessageMap already init'ed */; pMessageMap != NULL;<br/>&nbsp;&nbsp; pMessageMap = pMessageMap-&gt;pBaseMap)<br/>&nbsp; #endif<br/>&nbsp; {<br/>&nbsp;&nbsp; // Note: catch not so common but fatal mistake!!<br/>&nbsp;&nbsp; //&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; BEGIN_MESSAGE_MAP(CMyWnd, CMyWnd)<br/>&nbsp; #ifdef _AFXDLL<br/>&nbsp;&nbsp; ASSERT(pMessageMap != (*pMessageMap-&gt;pfnGetBaseMap)());<br/>&nbsp; #else<br/>&nbsp;&nbsp; ASSERT(pMessageMap != pMessageMap-&gt;pBaseMap);<br/>&nbsp; #endif</p><p>&nbsp;&nbsp; if (message &lt; 0xC000)<br/>&nbsp;&nbsp; {<br/>&nbsp;&nbsp;&nbsp; // constant window message<br/>&nbsp;&nbsp;&nbsp; if ((lpEntry = AfxFindMessageEntry(pMessageMap-&gt;lpEntries,<br/>&nbsp;&nbsp;&nbsp;&nbsp; message, 0, 0)) != NULL)<br/>&nbsp;&nbsp;&nbsp; {<br/>&nbsp;&nbsp;&nbsp;&nbsp; pMsgCache-&gt;lpEntry = lpEntry;<br/>&nbsp;&nbsp;&nbsp;&nbsp; AfxUnlockGlobals(CRIT_WINMSGCACHE);<br/>&nbsp;&nbsp;&nbsp;&nbsp; goto LDispatch;<br/>&nbsp;&nbsp;&nbsp; }<br/>&nbsp;&nbsp; }<br/>&nbsp;&nbsp; else<br/>&nbsp;&nbsp; {<br/>&nbsp;&nbsp;&nbsp;&nbsp; // registered windows message<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; lpEntry = pMessageMap-&gt;lpEntries;<br/>&nbsp;&nbsp; while ((lpEntry = AfxFindMessageEntry(lpEntry, 0xC000, 0, 0)) != NULL)<br/>&nbsp;&nbsp;&nbsp; {<br/>&nbsp;&nbsp;&nbsp;&nbsp; UINT* pnID = (UINT*)(lpEntry-&gt;nSig);<br/>&nbsp;&nbsp;&nbsp;&nbsp; ASSERT(*pnID &gt;= 0xC000 || *pnID == 0);<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // must be successfully registered<br/>&nbsp;&nbsp;&nbsp;&nbsp; if (*pnID == message)<br/>&nbsp;&nbsp;&nbsp;&nbsp; {<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; pMsgCache-&gt;lpEntry = lpEntry;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; AfxUnlockGlobals(CRIT_WINMSGCACHE);<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; goto LDispatchRegistered;<br/>&nbsp;&nbsp;&nbsp;&nbsp; }<br/>&nbsp;&nbsp;&nbsp;&nbsp; lpEntry++;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // keep looking past this one<br/>&nbsp;&nbsp;&nbsp; }<br/>&nbsp;&nbsp; }<br/>&nbsp; }</p><p>&nbsp; pMsgCache-&gt;lpEntry = NULL;<br/>&nbsp; AfxUnlockGlobals(CRIT_WINMSGCACHE);<br/>&nbsp; return FALSE;<br/>&nbsp;}<br/>&nbsp;&nbsp; LDispatch:<br/>&nbsp;ASSERT(message &lt; 0xC000);</p><p>&nbsp;mmf.pfn = lpEntry-&gt;pfn;</p><p>&nbsp;switch (lpEntry-&gt;nSig)<br/>&nbsp;{<br/>&nbsp;default:<br/>&nbsp; ASSERT(FALSE);<br/>&nbsp; break;</p><p>&nbsp;case AfxSig_b_D_v:<br/>&nbsp; lResult = (this-&gt;*mmf.pfn_b_D)(CDC::FromHandle(reinterpret_cast&lt;HDC&gt;(wParam)));<br/>&nbsp; break;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ...//<br/>&nbsp; LDispatchRegistered:&nbsp;&nbsp;&nbsp; // for registered windows messages<br/>&nbsp;ASSERT(message &gt;= 0xC000);<br/>&nbsp;ASSERT(sizeof(mmf) == sizeof(mmf.pfn));<br/>&nbsp;mmf.pfn = lpEntry-&gt;pfn;<br/>&nbsp;lResult = (this-&gt;*mmf.pfn_l_w_l)(wParam, lParam);</p><p>&nbsp; LReturnTrue:<br/>&nbsp;if (pResult != NULL)<br/>&nbsp; *pResult = lResult;<br/>&nbsp;return TRUE;<br/>&nbsp;}<br/>&nbsp;&nbsp; </p>
 楼主| 发表于 2007-4-6 17:09:12 | 显示全部楼层
该源代码有整整700行,不知道是不是MFC源码中最长的一个函数,在这里我只列出部分有代表性的源码。从源代码中大家也可以看得出该函数主要用于分辨消息并将消息交于其处理函数。MFC为了加快消息得分检速度在AfxFindMessageEntry函数中甚至使用了汇编代码。<br/>在CWnd::OnWndMsg中得不到处理的消息则交给CWnd:efWindowProc(相当于MFC的默认DefWindowProc函数)处理,其代码为:<br/>&nbsp;// Default CWnd implementation<br/>&nbsp;LRESULT CWnd:efWindowProc(UINT nMsg, WPARAM wParam, LPARAM lParam)<br/>&nbsp;{<br/>&nbsp;if (m_pfnSuper != NULL)<br/>&nbsp; return ::CallWindowProc(m_pfnSuper, m_hWnd, nMsg, wParam, lParam);<p>&nbsp;WNDPROC pfnWndProc;<br/>&nbsp;if ((pfnWndProc = *GetSuperWndProcAddr()) == NULL)<br/>&nbsp; return :efWindowProc(m_hWnd, nMsg, wParam, lParam);<br/>&nbsp;else<br/>&nbsp; return ::CallWindowProc(pfnWndProc, m_hWnd, nMsg, wParam, lParam);<br/>&nbsp;}<br/>&nbsp; CWnd:efWindowProc这调用了传统win32程序员熟悉的:efWindowProc(m_hWnd, nMsg, wParam, lParam);<br/>在挖掘上面源代码的同时你也看到了消息的传递路线。在MFC中CWnd以及派生于CWnd的类都拥有虚函数<br/>CWnd::WndProc(...)。</p><p><br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /////////////////////////////////////////////////////<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /* 9.MFC各类消息的"行走路径 " */<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /////////////////////////////////////////////////////<br/>&nbsp;&nbsp; 在篇头我就将消息分了类而且到目前我们已经了解了消息的传递机制了,下面我就具体的某类消息来看看其传递路径。<br/>&nbsp;&nbsp; 无论什么消息都有AfxWndProc进入,到达CWnd::OnWndMsg函数分检消息;<br/>&nbsp;&nbsp; 对于标准消息:<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 标准消息一般都沿其消息映射表从本类到父类逐层查找其处理函数,若没查到着交给:efWindowProc&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 处理。<br/>&nbsp;&nbsp; 对于命令消息:<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 命令消息除了能像标准消息一样从本类到父类逐层查找其处理函数外,有时他们可能还要拐弯。<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 再回头看看CWnd::OnWndMsg源码是如何处理WM_COMMAND消息的:<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (message == WM_COMMAND)<br/>&nbsp;{<br/>&nbsp; if (OnCommand(wParam, lParam))<br/>&nbsp; {<br/>&nbsp;&nbsp; lResult = 1;<br/>&nbsp;&nbsp; goto LReturnTrue;<br/>&nbsp; }<br/>&nbsp; return FALSE;<br/>&nbsp;}<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 原来交给了CWnd::OnCommand(wParam, lParam)<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 下面看看一个SDI程序中命令消息在Frame,View,Document以及CWinApp对象之间的传递路线。<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //in winfrm.cpp<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; BOOL CFrameWnd::OnCommand(WPARAM wParam, LPARAM lParam)<br/>&nbsp;// return TRUE if command invocation was attempted<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br/>&nbsp;HWND hWndCtrl = (HWND)lParam;<br/>&nbsp;UINT nID = LOWORD(wParam);</p><p>&nbsp;CFrameWnd* pFrameWnd = GetTopLevelFrame();<br/>&nbsp;ASSERT_VALID(pFrameWnd);<br/>&nbsp;if (pFrameWnd-&gt;m_bHelpMode &amp;&amp; hWndCtrl == NULL &amp;&amp;<br/>&nbsp; nID != ID_HELP &amp;&amp; nID != ID_DEFAULT_HELP &amp;&amp; nID != ID_CONTEXT_HELP)<br/>&nbsp;{<br/>&nbsp; // route as help<br/>&nbsp; if (!SendMessage(WM_COMMANDHELP, 0, HID_BASE_COMMAND+nID))<br/>&nbsp;&nbsp; SendMessage(WM_COMMAND, ID_DEFAULT_HELP);<br/>&nbsp; return TRUE;<br/>&nbsp;}</p><p>&nbsp;// route as normal command<br/>&nbsp;return CWnd::OnCommand(wParam, lParam);<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //in wincore.cpp<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // CWnd command handling<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; BOOL CWnd::OnCommand(WPARAM wParam, LPARAM lParam)<br/>&nbsp;// return TRUE if command invocation was attempted<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; { ...//<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return OnCmdMsg(nID, nCode, NULL, NULL);//CFrameWnd::OnCmdMsg<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // CFrameWnd command/message routing<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; BOOL CFrameWnd::OnCmdMsg(UINT nID, int nCode, void* pExtra,<br/>&nbsp;AFX_CMDHANDLERINFO* pHandlerInfo)<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br/>&nbsp;CPushRoutingFrame push(this);</p><p>&nbsp;// pump through current view FIRST<br/>&nbsp;CView* pView = GetActiveView();<br/>&nbsp;if (pView != NULL &amp;&amp; pView-&gt;OnCmdMsg(nID, nCode, pExtra, pHandlerInfo))<br/>&nbsp; return TRUE;</p><p>&nbsp;// then pump through frame<br/>&nbsp;if (CWnd::OnCmdMsg(nID, nCode, pExtra, pHandlerInfo))<br/>&nbsp; return TRUE;</p><p>&nbsp;// last but not least, pump through app<br/>&nbsp;CWinApp* pApp = AfxGetApp();<br/>&nbsp;if (pApp != NULL &amp;&amp; pApp-&gt;OnCmdMsg(nID, nCode, pExtra, pHandlerInfo))<br/>&nbsp; return TRUE;</p><p>&nbsp;return FALSE;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Frame的COMMAND传递顺序是View---&gt;Frame本身--&gt;CWinApp对象。<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //in viewcore.cpp<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; BOOL CView::OnCmdMsg(UINT nID, int nCode, void* pExtra,<br/>&nbsp;AFX_CMDHANDLERINFO* pHandlerInfo)<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br/>&nbsp;// first pump through pane<br/>&nbsp;if (CWnd::OnCmdMsg(nID, nCode, pExtra, pHandlerInfo))<br/>&nbsp; return TRUE;</p><p>&nbsp;// then pump through document<br/>&nbsp;if (m_pDocument != NULL)<br/>&nbsp;{<br/>&nbsp; // special state for saving view before routing to document<br/>&nbsp; CPushRoutingView push(this);<br/>&nbsp; return m_pDocument-&gt;OnCmdMsg(nID, nCode, pExtra, pHandlerInfo);<br/>&nbsp;}</p><p>&nbsp;return FALSE;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; View的COMMAND传递顺序是View本身---&gt;Document</p><p>&nbsp;&nbsp;&nbsp;&nbsp; //in doccore.cpp<br/>&nbsp;&nbsp;&nbsp;&nbsp; BOOL CDocument::OnCmdMsg(UINT nID, int nCode, void* pExtra,<br/>&nbsp;AFX_CMDHANDLERINFO* pHandlerInfo)<br/>&nbsp;&nbsp;&nbsp;&nbsp; {<br/>&nbsp;if (CCmdTarget::OnCmdMsg(nID, nCode, pExtra, pHandlerInfo))<br/>&nbsp; return TRUE;</p><p>&nbsp;// otherwise check template<br/>&nbsp;if (m_pDocTemplate != NULL &amp;&amp;<br/>&nbsp;&nbsp; m_pDocTemplate-&gt;OnCmdMsg(nID, nCode, pExtra, pHandlerInfo))<br/>&nbsp; return TRUE;</p><p>&nbsp;return FALSE;<br/>&nbsp;&nbsp;&nbsp;&nbsp; }<br/>&nbsp;&nbsp;&nbsp;&nbsp; Document的COMMAND传递顺序是Document本身---&gt;Document Template<br/>&nbsp;&nbsp;&nbsp;&nbsp; 由这个例子我们可以清楚地看到WM_COMMAND的传递路径了。<br/>&nbsp; <br/>&nbsp; 对于子窗口通知:由于子窗口通知通常以WM_COMMAND形式出现,所以它的传递路径也大致与WM_COMMAND相同,这里就不详述了。&nbsp;&nbsp; </p><p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ///////////////////////////////////////////<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /*&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 10.收尾工作&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; */<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /////////////////////////////////////////<br/>&nbsp; 至此,MFC消息映射与消息传递的内幕已基本被揭开,若想更深刻的理解,你就得在平时的程序开发中夺观察多思考了。</p>
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

手机版|小黑屋|3D数字艺术论坛 ( 沪ICP备14023054号 )

GMT+8, 2025-5-6 17:20

Powered by Discuz! X3.4

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表