﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>C++博客-MFC笔记-随笔分类-MFC</title><link>http://www.cppblog.com/jojo100/category/17201.html</link><description /><language>zh-cn</language><lastBuildDate>Tue, 26 Jul 2011 13:41:25 GMT</lastBuildDate><pubDate>Tue, 26 Jul 2011 13:41:25 GMT</pubDate><ttl>60</ttl><item><title>MFC消息响应机制及映射机制理解</title><link>http://www.cppblog.com/jojo100/archive/2011/07/24/151759.html</link><dc:creator>逗你玩</dc:creator><author>逗你玩</author><pubDate>Sun, 24 Jul 2011 14:10:00 GMT</pubDate><guid>http://www.cppblog.com/jojo100/archive/2011/07/24/151759.html</guid><wfw:comment>http://www.cppblog.com/jojo100/comments/151759.html</wfw:comment><comments>http://www.cppblog.com/jojo100/archive/2011/07/24/151759.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/jojo100/comments/commentRss/151759.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/jojo100/services/trackbacks/151759.html</trackback:ping><description><![CDATA[<a name="_Toc166944423"><font size="3"><font color="#0000ff"><strong>一．</strong><strong>MFC</strong></font></font></a><strong><font size="3" color="#0000ff">消息响应机制分析</font></strong>
<p>---- MFC<font face="宋体">是</font>Windows<font face="宋体">下程序设计的最流行的一个类库，但是该类库比较庞杂，尤其是它的消息映射机制，更是涉及到很多低层的东西，我们在这里，对它的整个消息映射机制进行了系统的分析，可以帮助程序开发人员对</font>MFC<font face="宋体">的消息映射机制有一个比较透彻的了解。</font></p>
<p>----&nbsp;<font face="宋体">关键词：面向对象</font>&nbsp;<font face="宋体">消息映射</font>&nbsp;MFC&nbsp;<font face="宋体">程序设计</font></p>
<p><a name="_Toc166944424"><strong>1</strong></a><strong>．引言&nbsp;</strong><br />
---- VC++<font face="宋体">的</font>MFC<font face="宋体">类库实际上是</font>Windows<font face="宋体">下</font>C++<font face="宋体">编程的一套最为流行的类库。</font>MFC<font face="宋体">的框架结构大大方便了程序员的编程工作，但是为了更加有效、灵活的使用</font>MFC<font face="宋体">编程，了解</font>MFC<font face="宋体">的体系结构往往可以使编程工作事半功倍。它合理的封装了</font>WIN32 API<font face="宋体">函数，并设计了一套方便的消息映射机制。但这套机制本身比较庞大和复杂，对它的分析和了解无疑有助于我们写出更为合理的高效的程序。这里我们简单的分析</font>MFC<font face="宋体">的消息响应机制，以了解</font>MFC<font face="宋体">是如何对</font>Windows<font face="宋体">的消息加以封装，方便用户的开发。</font>&nbsp;<br />
<strong>2. SDK</strong><strong>下的消息机制实现</strong><font size="3" face="宋体">&nbsp;</font><br />
----&nbsp;<font face="宋体">这里简单的回顾一下</font>SDK<font face="宋体">下我们是如何进行</font>Windows<font face="宋体">的程序开发的。一般来说，</font>Windows<font face="宋体">的消息都是和线程相对应的。即</font>Windows<font face="宋体">会把消息发送给和该消息相对应的线程。在</font>SDK<font face="宋体">的模式下，程序是通过</font>GetMessage<font face="宋体">函数从和某个线程相对应的消息队列里面把消息取出来并放到一个特殊的结构里面，一个消息的结构是一个如下的</font>STRUCTURE<font face="宋体">。</font>&nbsp;<br />
typedef struct tagMSG {<br />
HWND hwnd;<br />
UINT message;&nbsp;<br />
WPARAM wParam;<br />
LPARAM lParam;<br />
DWORD time;<br />
POINT pt;&nbsp;<br />
}MSG;<br />
----&nbsp;<font face="宋体">其中</font>hwnd<font face="宋体">表示和窗口过程相关的窗口的句柄，</font>message<font face="宋体">表示消息的</font>ID<font face="宋体">号，</font>wParam<font face="宋体">和</font>lParam<font face="宋体">表示和消息相关的参数，</font>time<font face="宋体">表示消息发送的时间，</font>pt<font face="宋体">表示消息发送时的鼠标的位置。</font></p>
<p>----&nbsp;<font face="宋体">然后</font>TranslateMessage<font face="宋体">函数用来把虚键消息翻译成字符消息并放到响应的消息队列里面，最后</font>DispatchMessage<font face="宋体">函数把消息分发到相关的窗口过程。然后窗口过程根据消息的类型对不同的消息进行相关的处理。在</font>SDK<font face="宋体">编程过程中，用户需要在窗口过程中分析消息的类型和跟消息一起的参数的含义，做不同的处理，相对比较麻烦，而</font>MFC<font face="宋体">把消息调用的过程给封装起来，使用户能够通过</font>ClassWizard<font face="宋体">方便的使用和处理</font>Windows<font face="宋体">的各种消息。</font></p>
<p><img id="0" anchorname="_Toc166945242" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif"  alt="" /><img id="1" anchorname="_Toc166945170" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif"  alt="" /><img id="2" anchorname="_Toc166945090" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif"  alt="" /><img id="3" anchorname="_Toc166944572" src="/CuteSoft_Client/CuteEditor/Images/anchor.gif"  alt="" /><a name="_Toc166944425"><strong>3</strong></a><strong>．MFC的消息实现机制&nbsp;</strong><br />
----&nbsp;<font face="宋体">我们可以看到，在</font>MFC<font face="宋体">的框架结构下，可以进行消息处理的类的头文件里面都会含有</font>DECLARE_MESSAGE_MAP()<font face="宋体">宏</font>,<font face="宋体">这里主要进行消息映射和消息处理函数的声明。可以进行消息处理的类的实现文件里一般都含有如下的结构。</font>&nbsp;<br />
BEGIN_MESSAGE_MAP(CInheritClass, CBaseClass)<br />
//{{AFX_MSG_MAP(CInheritClass)<br />
//}}AFX_MSG_MAP<br />
END_MESSAGE_MAP()</p>
<p><br />
----&nbsp;<font face="宋体">这里主要进行消息映射的实现和消息处理函数的实现。</font>&nbsp;<br />
----&nbsp;<font face="宋体">所有能够进行消息处理的类都是基于</font>CCmdTarget<font face="宋体">类的，也就是说</font>CCmdTarget<font face="宋体">类是所有可以进行消息处理类的父类。</font>CCmdTarget<font face="宋体">类是</font>MFC<font face="宋体">处理命令消息的基础和核心。</font></p>
<p>----&nbsp;<font face="宋体">同时</font>MFC<font face="宋体">定义了下面的两个主要结构</font>:</p>
<p>AFX_MSGMAP_ENTRY<br />
struct AFX_MSGMAP_ENTRY<br />
{<br />
UINT nMessage; // windows message<br />
UINT nCode; // control code or WM_NOTIFY code<br />
UINT nID;&nbsp;<br />
// control ID (or 0 for windows messages)<br />
UINT nLastID;&nbsp;<br />
// used for entries specifying a range of control id's<br />
UINT nSig;&nbsp;<br />
// signature type (action) or pointer to message #<br />
AFX_PMSG pfn; // routine to call (or special value)<br />
};<br />
<font face="宋体">和</font>AFX_MSGMAP<br />
struct AFX_MSGMAP<br />
{<br />
#ifdef _AFXDLL<br />
const AFX_MSGMAP* (PASCAL* pfnGetBaseMap)();<br />
#else<br />
const AFX_MSGMAP* pBaseMap;<br />
#endif<br />
const AFX_MSGMAP_ENTRY* lpEntries;<br />
};</p>
<p><font face="宋体">其中</font>AFX_MSGMAP_ENTRY<font face="宋体">结构包含了</font><br />
<font face="宋体">一个消息的所有相关信息</font>,<font face="宋体">其中</font></p>
<p>nMessage<font face="宋体">为</font>Windows<font face="宋体">消息的</font>ID<font face="宋体">号</font><br />
nCode<font face="宋体">为控制消息的通知码</font><br />
nID<font face="宋体">为</font>Windows<font face="宋体">控制消息的</font>ID<br />
nLastID<font face="宋体">表示如果是一个指定范围的消息被映射的话，</font><br />
nLastID<font face="宋体">用来表示它的范围。</font><br />
nSig<font face="宋体">表示消息的动作标识</font><br />
AFX_PMSG pfn&nbsp;<font face="宋体">它实际上是一个指向</font><br />
<font face="宋体">和该消息相应的执行函数的指针。</font></p>
<p>----&nbsp;<font face="宋体">而</font>AFX_MSGMAP<font face="宋体">主要作用是两个，一：用来得到基类的消息映射入口地址。二：得到本身的消息映射入口地址。</font></p>
<p>----&nbsp;<font face="宋体">实际上，</font>MFC<font face="宋体">把所有的消息一条条填入到</font>AFX_MSGMAP_ENTRY<font face="宋体">结构中去，形成一个数组，该数组存放了所有的消息和与它们相关的参数。同时通过</font>AFX_MSGMAP<font face="宋体">能得到该数组的首地址，同时得到基类的消息映射入口地址，这是为了当本身对该消息不响应的时候，就调用其基类的消息响应。</font></p>
<p>----&nbsp;<font face="宋体">现在我们来分析</font>MFC<font face="宋体">是如何让窗口过程来处理消息的，实际上所有</font>MFC<font face="宋体">的窗口类都通过钩子函数</font>_AfxCbtFilterHook<font face="宋体">截获消息，并且在钩子函数</font>_AfxCbtFilterHook<font face="宋体">中把窗口过程设定为</font>AfxWndProc<font face="宋体">。原来的窗口过程保存在成员变量</font>m_pfnSuper<font face="宋体">中。</font></p>
<p>----&nbsp;<font face="宋体">所以在</font>MFC<font face="宋体">框架下，一般一个消息的处理过程是这样的。</font></p>
<p><font face="宋体">函数</font>AfxWndProc<font face="宋体">接收</font>Windows<font face="宋体">操作系统发送的消息。</font></p>
<p><font face="宋体">函数</font>AfxWndProc<font face="宋体">调用函数</font>AfxCallWndProc<font face="宋体">进行消息处理，这里一个进步是把对句柄的操作转换成对</font>CWnd<font face="宋体">对象的操作。</font></p>
<p><font face="宋体">函数</font>AfxCallWndProc<font face="宋体">调用</font>CWnd<font face="宋体">类的方法</font>WindowProc<font face="宋体">进行消息处理。注意</font>AfxWndProc<font face="宋体">和</font>AfxCallWndProc<font face="宋体">都是</font>AFX<font face="宋体">的</font>API<font face="宋体">函数。而</font>WindowProc<font face="宋体">已经是</font>CWnd<font face="宋体">的一个方法。所以可以注意到在</font>WindowProc<font face="宋体">中已经没有关于句柄或者是</font>CWnd<font face="宋体">的参数了。</font></p>
<p><font face="宋体">方法</font>WindowProc<font face="宋体">调用方法</font>OnWndMsg<font face="宋体">进行正式的消息处理，即把消息派送到相关的方法中去处理。消息是如何派送的呢？实际上在</font>CWnd<font face="宋体">类中都保存了一个</font>AFX_MSGMAP<font face="宋体">的结构，而在</font>AFX_MSGMAP<font face="宋体">结构中保存有所有我们用</font>ClassWizard<font face="宋体">生成的消息的数组的入口，我们把传给</font>OnWndMsg<font face="宋体">的</font>message<font face="宋体">和数组中的所有的</font>message<font face="宋体">进行比较，找到匹配的那一个消息。实际上系统是通过函数</font>AfxFindMessageEntry<font face="宋体">来实现的。找到了那个</font>message<font face="宋体">，实际上我们就得到一个</font>AFX_MSGMAP_ENTRY<font face="宋体">结构，而我们在上面已经提到</font>AFX_MSGMAP_ENTRY<font face="宋体">保存了和该消息相关的所有信息，其中主要的是消息的动作标识和跟消息相关的执行函数。然后我们就可以根据消息的动作标识调用相关的执行函数，而这个执行函数实际上就是通过</font>ClassWizard<font face="宋体">在类实现中定义的一个方法。这样就把消息的处理转化到类中的一个方法的实现上。举一个简单的例子，比如在</font>View<font face="宋体">中对</font>WM_LButtonDown<font face="宋体">消息的处理就转化成对如下一个方法的操作。</font>&nbsp;<br />
void CInheritView::OnLButtonDown<br />
(UINT nFlags, CPoint point)&nbsp;<br />
{<br />
// TODO: Add your message&nbsp;<br />
handler code here and/or call default&nbsp;<br />
CView::OnLButtonDown(nFlags, point);<br />
}</p>
<p><font face="宋体">注意这里</font>CView::OnLButtonDown(nFlags, point)<font face="宋体">实际上就是调用</font>CWnd<font face="宋体">的</font>Default()<font face="宋体">方法。</font>&nbsp;<font face="宋体">而</font>Default()<font face="宋体">方法所做的工作就是调用</font>DefWindowProc<font face="宋体">对消息进行处理。这实际上是调用原来的窗口过程进行缺省的消息处理。</font></p>
<p><font face="宋体">如果</font>OnWndMsg<font face="宋体">方法没有对消息进行处理的话，就调用</font>DefWindowProc<font face="宋体">对消息进行处理。这是实际上是调用原来的窗口过程进行缺省的消息处理。</font>&nbsp;<br />
----&nbsp;<font face="宋体">所以如果正常的消息处理的话，</font>MFC<font face="宋体">窗口类是完全脱离了原来的窗口过程，用自己的一套体系结构实现消息的映射和处理。即先调用</font>MFC<font face="宋体">窗口类挂上去的窗口过程，再调用原先的窗口过程。并且用户面对和消息相关的参数不再是死板的</font>wParam<font face="宋体">和</font>lParam,<font face="宋体">而是和消息类型具体相关的参数。比如和消息</font>WM_LbuttonDown<font face="宋体">相对应的方法</font>OnLButtonDown<font face="宋体">的两个参数是</font>nFlags<font face="宋体">和</font>point<font face="宋体">。</font>nFlags<font face="宋体">表示在按下鼠标左键的时候是否有其他虚键按下，</font>point<font face="宋体">更简单，就是表示鼠标的位置。</font>&nbsp;<br />
----&nbsp;<font face="宋体">同时</font>MFC<font face="宋体">窗口类消息传递中还提供了两个函数，分别为</font>WalkPreTranslateTree<font face="宋体">和</font>PreTranslateMessage<font face="宋体">。我们知道利用</font>MFC<font face="宋体">框架生成的程序，都是从</font>CWinApp<font face="宋体">开始执行的，而</font>CWinapp<font face="宋体">实际继承了</font>CWinThread<font face="宋体">类。在</font>CWinThread<font face="宋体">的运行过程中会调用窗口类中的</font>WalkPreTranslateTree<font face="宋体">方法。而</font>WalkPreTranslateTree<font face="宋体">方法实际上就是从当前窗口开始查找愿意进行消息翻译的类，直到找到窗口没有父类为止。在</font>WalkPreTranslateTree<font face="宋体">方法中调用了</font>PreTranslateMessage<font face="宋体">方法。实际上</font>PreTranslateMessage<font face="宋体">最大的好处是我们在消息处理前可以在这个方法里面先做一些事情。举一个简单的例子，比如我们希望在一个</font>CEdit<font face="宋体">对象里，把所有的输入的字母都以大写的形式出现。我们只需要在</font>PreTranslateMessage<font face="宋体">方法中判断</font>message<font face="宋体">是否为</font>WM_CHAR,<font face="宋体">如果是的话，把</font>wParam(<font face="宋体">表示键值</font>)<font face="宋体">由小写字母的值该为大写字母的值就实现了这个功能。</font></p>
<p>----&nbsp;<font face="宋体">继续上面的例子，根据我们对</font>MFC<font face="宋体">消息机制的分析，我们很容易得到除了上面的方法，我们至少还可以在另外两个地方进行操作。</font></p>
<p>----&nbsp;<font face="宋体">一：在消息的处理方法里面即</font>OnChar<font face="宋体">中，当然最后我们不再调用</font>CEdit::OnChar(nChar, nRepCnt, nFlags)<font face="宋体">，而是直接调用</font>DefWindowProc(WM_CHAR,nChar,MAKELPARAM (nRepCnt,nFlags))<font face="宋体">。因为从我们上面的分析可以知道</font>CEdit::OnChar(nChar, nRepCnt, nFlags)<font face="宋体">实际上也就是对</font>DefWindowProc<font face="宋体">方法的调用。</font></p>
<p>----&nbsp;<font face="宋体">二：我们可以直接重载</font>DefWindowProc<font face="宋体">方法</font>,<font face="宋体">对</font>message<font face="宋体">类型等于</font>WM_CHAR<font face="宋体">的，直接修改</font>nChar<font face="宋体">的值即可。</font></p>
<p><a name="_Toc166944426"><strong>4</strong></a><strong>．小结</strong>&nbsp;<br />
----&nbsp;<font face="宋体">通过对</font>MFC<font face="宋体">类库的分析和了解，不仅能够使我们更好的使用</font>MFC<font face="宋体">类库，同时，对于我们自己设计和实现框架和类，无疑也有相当大的帮助。</font></p>
<p><font size="3" face="宋体">&nbsp;</font></p>
<p><font size="3" face="宋体">&nbsp;</font></p>
<p><a name="_Toc166944427"><font size="3"><font color="#0000ff"><strong>二．</strong><strong>MFC</strong></font></font></a><font size="3"><font color="#0000ff"><strong>的消息映射机制</strong><strong>&nbsp;</strong></font></font></p>
<p>MFC<font face="宋体">的设计者们在设计</font>MFC<font face="宋体">时，紧紧把握一个目标，那就是尽可能使得</font>MFC<font face="宋体">的代码要小，速度尽可能快。为了这个目标，他们使用了许多技巧，其中很多技巧体现在宏的运用上，实现</font>MFC<font face="宋体">的消息映射的机制就是其中之一。</font><br />
<br />
<font face="宋体">　　同</font>MFC<font face="宋体">消息映射机制有关的宏有下面几个：</font><br />
<br />
<font face="宋体">　　</font>DECLARE_MESSAGE_MAP()<font face="宋体">宏</font><br />
<br />
<font face="宋体">　　</font>BEGIN_MESSAGE_MAP(theClass, baseClass)<font face="宋体">和</font>END_MESSAGE_MAP()<font face="宋体">宏</font><br />
<br />
<font face="宋体">　　弄懂</font>MFC<font face="宋体">消息映射机制的最好办法是将找出一个具体的实例，将这些宏展开，并找出相关的数据结构。</font><br />
<br />
<font face="宋体">　　</font>DECLARE_MESSAGE_MAP()<strong><br />
<br />
</strong><strong><font face="宋体">　　</font></strong><strong>DECLARE_MESSAGE_MAP()</strong><strong><font face="宋体">宏的定义如下：</font></strong><strong><br />
<br />
</strong><strong><font face="宋体">　　</font></strong><strong>#define DECLARE_MESSAGE_MAP() \<br />
<br />
</strong><strong><font face="宋体">　　</font></strong><strong>private: \<br />
<br />
</strong><strong><font face="宋体">　　</font></strong><strong>static const AFX_MSGMAP_ENTRY _messageEntries[]; \<br />
<br />
</strong><strong><font face="宋体">　　</font></strong><strong>protected: \<br />
<br />
</strong><strong><font face="宋体">　　</font></strong><strong>static AFX_DATA const AFX_MSGMAP messageMap; \<br />
<br />
</strong><strong><font face="宋体">　　</font></strong><strong>virtual const AFX_MSGMAP* GetMessageMap() const; \<br />
</strong><br />
<font face="宋体">从上面的定义可以看出，</font><strong>DECLARE_MESSAGE_MAP()</strong><strong><font face="宋体">作下面三件事：</font></strong><strong><br />
<br />
</strong><strong><font face="宋体">　　定义一个长度不定的静态数组变量</font></strong><strong>_messageEntries[]</strong><strong><font face="宋体">；</font></strong><strong><br />
<br />
</strong><strong><font face="宋体">　　定义一个静态变量</font></strong><strong>messageMap</strong><strong><font face="宋体">；</font></strong><strong><br />
<br />
</strong><strong><font face="宋体">　　定义一个虚拟函数</font></strong><strong>GetMessageMap()</strong><strong><font face="宋体">；</font></strong><strong><br />
</strong><br />
<font face="宋体">在</font>DECLARE_MESSAGE_MAP()<font face="宋体">宏中，涉及到</font>MFC<font face="宋体">中两个对外不公开的数据结构</font><br />
<br />
<strong>AFX_MSGMAP_ENTRY</strong><strong><font face="宋体">和</font></strong><strong>AFX_MSGMAP</strong><font face="宋体">。为了弄清楚消息映射，有必要考察一下这两个数据结构的定义。</font><br />
<br />
<font face="宋体">　<strong>　</strong></font><strong>AFX_MSGMAP_ENTRY</strong><strong><font face="宋体">的定义</font></strong><strong><br />
<br />
</strong><strong><font face="宋体">　　</font></strong><strong>struct AFX_MSGMAP_ENTRY<br />
<br />
</strong><strong><font face="宋体">　　</font></strong><strong>{<br />
<br />
</strong><strong><font face="宋体">　　　</font></strong><strong>UINT nMessage; // windows message<br />
<br />
</strong><strong><font face="宋体">　　　</font></strong><strong>UINT nCode; // control code or WM_NOTIFY code<br />
<br />
</strong><strong><font face="宋体">　　　</font></strong><strong>UINT nID; // control ID (or 0 for windows messages)<br />
<br />
</strong><strong><font face="宋体">　　　</font></strong><strong>UINT nLastID; // used for entries specifying a range of control id's<br />
<br />
</strong><strong><font face="宋体">　　　</font></strong><strong>UINT nSig; // signature type (action) or pointer to message #<br />
<br />
</strong><strong><font face="宋体">　　　</font></strong><strong>AFX_PMSG pfn; // routine to call (or special value)<br />
<br />
</strong><strong><font face="宋体">　　</font></strong><strong>};<br />
</strong><br />
<font face="宋体">结构中各项的含义注释已经说明得很清楚了，这里不再多述，从上面的定义你是否看出，</font>AFX_MSGMAP_ENTRY<font face="宋体">结构实际上定义了消息和处理此消息的动作之间的映射关系。因此静态数组变量</font>_messageEntries[]<font face="宋体">实际上定义了一张表，表中的每一项指定了相应的对象所要处理的消息和处理此消息的函数的对应关系，因而这张表也称为消息映射表。再看看</font>AFX_MSGMAP<font face="宋体">的定义。</font><br />
<br />
<font face="宋体">　　（</font>2<font face="宋体">）</font>AFX_MSGMAP<font face="宋体">的定义</font><br />
<br />
<font face="宋体">　　</font><strong>struct AFX_MSGMAP<br />
<br />
</strong><strong><font face="宋体">　　</font></strong><strong>{<br />
<br />
</strong><strong><font face="宋体">　　　</font></strong><strong>const AFX_MSGMAP* pBaseMap;<br />
<br />
</strong><strong><font face="宋体">　　　</font></strong><strong>const AFX_MSGMAP_ENTRY* lpEntries;<br />
<br />
</strong><strong><font face="宋体">　　　</font></strong><strong>};<br />
</strong><br />
<font face="宋体">不难看出，</font>AFX_MSGMAP<font face="宋体">定义了一单向链表，链表中每一项的值是一指向消息映射表的指针（实际上就是</font>_messageEntries<font face="宋体">的值）。通过这个链表，使得在某个类中调用基类的的消息处理函数很容易，因此，</font>&#8220;<font face="宋体">父类的消息处理函数是子类的缺省消息处理函数</font>&#8221;<font face="宋体">就</font>&#8220;<font face="宋体">顺理成章</font>&#8221;<font face="宋体">了。在后面的</font>&#8220;MFC<font face="宋体">窗口的消息处理</font>&#8221;<font face="宋体">一节中会对此作详细的讲解。</font></p>
<p><font face="宋体">由上述可见，<strong>在类的头文件中主要定义了两个数据结构：消息映射表和单向链表。</strong>（孙建东总结）</font><br />
<br />
<font face="宋体">　　</font>BEGIN_MESSAGE_MAP()<font face="宋体">和</font>END_MESSAGE_MAP()<br />
<br />
<font face="宋体">　　它们的定义如下：</font><br />
<br />
<font face="宋体">　　</font><strong>#define BEGIN_MESSAGE_MAP(theClass, baseClass) \<br />
<br />
</strong><strong><font face="宋体">　　</font></strong><strong>const AFX_MSGMAP* theClass::GetMessageMap() const \<br />
<br />
</strong><strong><font face="宋体">　　</font></strong><strong>{ return &amp;theClass::messageMap; } \<br />
<br />
</strong><strong><font face="宋体">　　</font></strong><strong>AFX_COMDAT AFX_DATADEF const AFX_MSGMAP theClass::messageMap = \<br />
<br />
</strong><strong><font face="宋体">　　</font></strong><strong>{ &amp;baseClass::messageMap, &amp;theClass::_messageEntries[0] }; \<br />
<br />
</strong><strong><font face="宋体">　　</font></strong><strong>AFX_COMDAT const AFX_MSGMAP_ENTRY theClass::_messageEntries[] = \<br />
<br />
</strong><strong><font face="宋体">　　</font></strong><strong>{ \<br />
</strong><br />
<font face="宋体">　　<strong>　</strong></font><strong>#define END_MESSAGE_MAP() \<br />
<br />
</strong><strong><font face="宋体">　　　</font></strong><strong>{0, 0, 0, 0, AfxSig_end, (AFX_PMSG)0 } \<br />
<br />
</strong><strong><font face="宋体">　　　</font></strong><strong>}; \<br />
</strong><br />
<font face="宋体">对应</font>BEGIN_MESSAGE_MAP()<font face="宋体">的定义可能不是一下子就看得明白，不过不要紧，举一例子就很清楚了。对于</font>BEGIN_MESSAGE_MAP(CView, CWnd)<font face="宋体">，</font>VC<font face="宋体">预编译器将其展开成下面的形式：</font><br />
<br />
<font face="宋体">　　</font>const AFX_MSGMAP* CView::GetMessageMap() const<br />
<br />
<font face="宋体">　　</font>{&nbsp;<br />
<br />
<font face="宋体">　　　</font>return &amp;CView::messageMap;&nbsp;<br />
<br />
<font face="宋体">　　　</font>}<br />
<br />
<font face="宋体">　　</font>AFX_COMDAT AFX_DATADEF const AFX_MSGMAP CView::messageMap =<br />
<br />
<font face="宋体">　　</font>{&nbsp;<br />
<br />
<font face="宋体">　　　</font>&amp;CWnd::messageMap,&nbsp;<br />
<br />
<font face="宋体">　　　</font>&amp;CView::_messageEntries[0]&nbsp;<br />
<br />
<font face="宋体">　　</font>};<br />
<br />
<font face="宋体">　　</font>AFX_COMDAT const AFX_MSGMAP_ENTRY CView::_messageEntries[] =<br />
<br />
<font face="宋体">　　</font>{<br />
<br />
<font face="宋体">　　至于</font>END_MESSAGE_MAP()<font face="宋体">则不过定义了一个表示映射表结束的标志项，我想大家对于这种简单的技巧应该是很熟悉的，无需多述。</font></p>
<font face="宋体">到此为止，我想大家也已经想到了象</font>ON_COMMAND<font face="宋体">这样的宏的具体作用了，不错它们只不过定义了一种类型的消息映射项，看看</font>ON_COMMAND<font face="宋体">的定义：</font><br />
<br />
<font face="宋体">　　</font>#define ON_COMMAND(id, memberFxn) \<br />
<br />
<font face="宋体">　　</font>{ WM_COMMAND, CN_COMMAND, (WORD)id, (WORD)id, AfxSig_vv, (AFX_PMSG)&amp;memberFxn },<br />
<br />
<font face="宋体">　　　根据上面的定义，</font>ON_COMMAND(ID_FILE_NEW, OnFileNew)<font face="宋体">将被</font>VC<font face="宋体">预编译器展开</font><br />
<br />
<font face="宋体">　　　如下：</font><br />
<br />
<font face="宋体">　　</font>{WM_COMMAND, CN_COMMAND, (WORD)id, (WORD)id, AfxSig_vv,&nbsp;<br />
<br />
<font face="宋体">　　</font>(AFX_PMSG)&amp;OnFileNew},<br />
<br />
<font face="宋体">到此，</font>MFC<font face="宋体">的消息映射机制已经清楚了，现在提出并解答两个问题以作为对这一节的小结。</font><br />
<br />
<font face="宋体">　　为什么不直接使用虚拟函数实现消息处理函数呢？这是一个</font>GOOD QUESTION<font face="宋体">。前面已经说过，</font>MFC<font face="宋体">的设计者们在设计</font>MFC<font face="宋体">时有一个很明确的目标，就是使得</font>&#8220;MFC<font face="宋体">的代码尽可能小，速度尽可能快</font>&#8221;<font face="宋体">，如果采用虚拟函数，那么对于所有的窗口消息，都必须有一个与之对应的虚拟函数，因而对每一个从</font>CWnd<font face="宋体">派生的类而言，都会有一张很大的虚拟函数表</font>vtbl<font face="宋体">。但是在实际应用中，一般只对少数的消息进行处理，大部分都交给系统缺省处理，所以表中的大部分项都是无用项，这样做就浪费了很多内存资源，这同</font>MFC<font face="宋体">设计者们的设计目标是相违背的。当然，</font>MFC<font face="宋体">所使用的方法只是解决这类问题的方式之一，不排除还有其他的解决方式，但就我个人观点而言，这是一种最好的解决方式，体现了很高的技巧性，值得我们学习。</font><br />
<br />
<font face="宋体">　　至于这第二个问题，是由上面的问题引申出来的。如果在子类和父类中出现了相同的消息出来函数，</font>VC<font face="宋体">编译器会怎么处理这个问题呢？</font>VC<font face="宋体">不会将它们看作错误，而会象对待虚拟函数类似的方式去处理，但对于消息处理函数</font>(<font face="宋体">带</font>afx_msg<font face="宋体">前缀</font>)<font face="宋体">，则不会生成虚拟函数表</font>vtbl<font face="宋体">。</font>
@import url(http://www.cppblog.com/CuteSoft_Client/CuteEditor/Load.ashx?type=style&file=SyntaxHighlighter.css);@import url(/css/cuteeditor.css);<img src ="http://www.cppblog.com/jojo100/aggbug/151759.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/jojo100/" target="_blank">逗你玩</a> 2011-07-24 22:10 <a href="http://www.cppblog.com/jojo100/archive/2011/07/24/151759.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>