MFC消息响应机制分析
1 引言
微软公司提供的MFC基本类库(Microsoft Foundation Classes),是进行可视化编程时使用最为流行的一个类
库。MFC封装了大部分Windows API函数和Windows控件,使得程序的开发变得简单,极大的缩短了程序的开发
周期。MFC独创的Document/View框架结构,能够将管理数据的代码和显示数据的程序代码分开,并且设计了
一套方便的消息映射和命令传递机制,方便程序员的开发使用。其中消息映射机制本身比较庞大和复杂,对
它的分析和了解无疑有助于我们写出更为合理的高效的程序。这里我们分析一下MFC的消息映射机制,以了解
MFC是如何对Windows的消息加以封装,方便用户的开发。
2 SDK下的消息机制实现
首先,简单回顾一下SDK下我们是如何进行Windows的程序开发的。Windows程序的运行是依靠外部发生的事件
来驱动的,事件由操作系统捕捉,以消息的形式进入消息队列,然后通过消息循环从队列中不断取出消息,
送到对应的窗口过程里处理。相对于DOS程序,Windows是以WinMain作为程序的入口点,以下就是一个简化的
Win32程序的主体,通过while语句实现消息循环:
WinMain(…)
{
   MSG msg;
   RegisterClass(…);           // 注册窗口类
   CreateWindow(…);            // 创建窗口
   ShowWindow(…);              // 显示窗口
   UpdateWindow(…);
   While(GetMessage(&msg,…)){   // 消息循环
TranslateMessage(…);
DispatchMessage(…);
}
return msg.wParam;
}
其中,msg代表消息,程序是通过GetMessage函数从和某个线程相对应的消息队列里面把消息取出来并放到消
息变量msg里面。然后TranslateMessage函数用来把键盘消息转化并放到响应的消息队列里面,最后
DispatchMessage函数把消息分发到相关的窗口过程去处理。窗口过程根据消息的类型对不同的消息进行相关
的处理。在SDK编程过程中,用户需要在窗口过程中分析消息的类型及其参数的含义,然后做不同的处理,相
对比较麻烦;而MFC把消息调用的过程给封装起来,使用户能够通过ClassWizard方便的使用和处理Windows的
各种消息。
3 MFC中的消息映射机制
在MFC的框架结构下,“消息映射”是通过巧妙的宏定义,形成一张消息映射表格来进行的。这样一旦消息发
生,Framework就可以根据消息映射表格来进行消息映射和命令传递。
首先在需要进行消息处理的类的头文件(.H)里,都会含有DECLARE_MESSAGE_MAP()宏,声明该类拥有消息映
射表格:
class CscribbleDoc:public Cdocument
{
    …
DECLARE_MESSAGE_MAP()
};
然后在类应用程序文件(.CPP)实现这一表格
BEGIN_MESSAGE_MAP(CInheritClass, CBaseClass)
          //{{AFX_MSG_MAP(CInheritClass)
         ON_COMMAND(ID_EDIT_COPY,OnEditCopy)
         ………
          //}}AFX_MSG_MAP
END_MESSAGE_MAP()
----这里主要进行消息映射的实现,把它和消息处理函数联系在一起。其中出现三个宏,第一个宏是
BEGIN_MESSAGE_MAP有两个参数,分别是拥有消息表格的类,及其父类。第二个宏是ON_COMMAND,指定命令消
息的处理函数名称。第三个宏是END_MESSAGE_MAP()作为结尾符号。中间的奇怪符号//}}和//{{,是
ClassWizard产生的,对程序无影响。
观察DECLARE_MESSAGE_MAP的定义:
#define DECLARE_MESSAGE_MAP () \
private: \
static const AFX_MESSAGE_ENTRY _messageEntries[]; \
protected: \
static AFX_DATA const AFX_MSGMAP messageMap; \
virtual const AFX_MSGMAP* GetMessageMap() const; \
里面又包含了MFC新定义的两个数据结构,如下:
AFX_MSGMAP_ENTRY
struct AFX_MSGMAP_ENTRY
{
          UINT nMessage;   // windows message
          UINT nCode;     // control code or WM_NOTIFY code
          UINT nID;        // control ID (or 0 for windows messages)
          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
struct AFX_MSGMAP
{
          const AFX_MSGMAP* pBaseMap;
          const AFX_MSGMAP_ENTRY* lpEntries;
};
其中AFX_MSGMAP_ENTRY结构包含了一个消息的所有相关信息,而AFX_MSGMAP主要作用有两个,一是用来得到基
类的消息映射入口地址。二是得到本身的消息映射入口地址。
实际上,MFC把所有的消息一条条填入到AFX_MSGMAP_ENTRY结构中去,形成一个数组,该数组存放了所有的消
息和与它们相关的参数。同时通过AFX_MSGMAP能得到该数组的首地址,同时得到基类的消息映射入口地址。
当本身对该消息不响应的时候,就可以上溯到基类的消息映射表寻找对应的消息响应。
现在我们来分析MFC是如何让窗口过程来处理消息的,实际上所有MFC的窗口类都通过钩子函数
_AfxCbtFilterHook截获消息,并且在钩子函数_AfxCbtFilterHook中把窗口过程设定为AfxWndProc。原来的
窗口过程保存在成员变量m_pfnSuper中。
在MFC框架下,一般一个消息的处理过程是这样的。
(1)函数AfxWndProc接收Windows操作系统发送的消息。
(2)函数AfxWndProc调用函数AfxCallWndProc进行消息处理,这里一个进步是把对句柄的操作转换成对CWnd对
象的操作。
(3)函数AfxCallWndProc调用CWnd类的方法WindowProc进行消息处理。
(4)WindowProc调用OnWndMsg进行正式的消息处理,即把消息派送到相关的方法中去处理。在CWnd类中都保存
了一个AFX_MSGMAP的结构,而在AFX_MSGMAP结构中保存有所有我们用ClassWizard生成的消息的数组的入口,
我们把传给OnWndMsg的message和数组中的所有的message进行比较,找到匹配的那一个消息。实际上系统是
通过函数AfxFindMessageEntry来实现的。找到了那个message,实际上我们就得到一个AFX_MSGMAP_ENTRY结
构,而我们在上面已经提到AFX_MSGMAP_ENTRY保存了和该消息相关的所有信息,其中主要是消息的动作标识
和相关的执行函数。然后我们就可以根据消息的动作标识调用相关的执行函数,而这个执行函数实际上就是
通过ClassWizard在类实现中定义的一个方法。这样就把消息的处理转化到类中的一个方法的实现上。
(5)如果OnWndMsg方法没有对消息进行处理的话,就调用DefWindowProc对消息进行处理。这是实际上是调用
原来的窗口过程进行缺省的消息处理。 所以如果正常的消息处理的话,MFC窗口类是完全脱离了原来的窗口
过程,用自己的一套体系结构实现消息的映射和处理。即先调用MFC窗口类挂上去的窗口过程,再调用原先的
窗口过程。用户面对的消息参数将不再是固定的wParam和lParam,而是和消息类型具体相关的参数。比如和消
息WM_LButtonDown相对应的方法OnLButtonDown的两个参数是nFlags和point。nFlags表示在按下鼠标左键的
时候是否有其他虚拟键按下,point更简单,就是表示鼠标的位置。同时MFC窗口类消息传递中还提供了两个
函数,分别为WalkPreTranslateTree和PreTranslateMessage。我们知道利用MFC框架生成的程序,都是从
CWinApp开始执行的,而CWinapp实际继承了CWinThread类。在CWinThread的运行过程中会调用窗口类中的
WalkPreTranslateTree方法。而WalkPreTranslateTree方法实际上就是从当前窗口开始查找愿意进行消息翻
译的类,直到找到窗口没有父类为止。在WalkPreTranslateTree方法中调用了PreTranslateMessage方法。实
际上PreTranslateMessage最大的好处是我们在消息处理前可以在这个方法里面先做一些事情。举一个简单的
例子,比如我们希望在一个CEdit对象里,把所有的输入的字母都以大写的形式出现。我们只需要在
PreTranslateMessage方法中判断message是否为WM_CHAR,如果是的话,把wParam(表示键值)由小写字母的值
该为大写字母的值就实现了这个功能。
4 小结
MFC通过巧妙的宏定义把消息调用的过程给封装起来,使用户能够通过ClassWizard方便的使用和处理Windows
的各种消息。通过对MFC消息映射机制的分析,不仅能够使我们更好的使用MFC类库,同时,对于我们自己设
计程序框架和类,无疑也有相当大的帮助。

MFC消息响应机制 q的更多相关文章

  1. MFC消息响应机制分析

    ---- 摘要: ---- MFC是Windows下程序设计的最流行的一个类库,但是该类库比较庞杂,尤其是它的消息映射机制,更是涉及到很多低层的东西,我们在这里,对它的整个消息映射机制进行了系统的分析 ...

  2. MFC中消息响应机制

    由于视类窗口始终覆盖在框架类窗口之上,因此所有操作,包括鼠标单击.鼠标移动等操作都只能由视类窗口捕获.一个MFC消息响应函数在程序中有三处相关信息:函数原型.函数实现和以及用来关联消息和消息响应函数的 ...

  3. MFC的消息响应机制说明

    MFC的快速理解: 1.MFC的设计者们在设计MFC时,有一个主要的方向就是尽可能使得MFC的代码要小,速度尽可能快.为了这个方向,工程师们使用了许多技巧,主要表现在宏的运用上,实 现MFC的消息映射 ...

  4. MFC消息映射机制以及画线功能实现

    ---此仅供用于学习交流,切勿用于商业用途,转载请注明http://www.cnblogs.com/mxbs/p/6213404.html. 利用VS2010创建一个单文档标准MFC工程,工程名为Dr ...

  5. MFC编程入门之五(MFC消息映射机制概述)

    在MFC软件开发中,界面操作或者线程之间通信都会经常用到消息,通过对消息的处理实现相应的操作.比较典型的过程是,用户操作窗口,然后有消息产生,送给窗口的消息处理函数处理,对用户的操作做出响应. 一.什 ...

  6. VS2010/MFC编程入门之五(MFC消息映射机制概述)

    VS2010/MFC编程入门之五(MFC消息映射机制概述)-软件开发-鸡啄米 http://www.jizhuomi.com/software/147.html 上一讲鸡啄米为大家简单分析了MFC应用 ...

  7. MFC消息映射机制

    1.MFC应用框架主要类之间的关系 MFC自动生成的框架中重要的类有:C-App.CMainFrame.C-Doc和C-View. 其他的类如CClassView.CFileView等都是在框架窗口( ...

  8. VS2010-MFC(MFC消息映射机制概述)

    转自:http://www.jizhuomi.com/software/147.html 前面已经说过,Windows应用程序是消息驱动的.在MFC软件开发中,界面操作或者线程之间通信都会经常用到消息 ...

  9. 自制MFC消息响应定位器+原理分析

    mfc里面有张消息映射表(MESSAGE_MAP),消息都是通过这张表来分发到相应函数里的. 这个是我自制的定位器,从vc6.0到现在的2013生成的mfc都可以用,全静态扫描并已处理动态基址. 下面 ...

随机推荐

  1. github每次推送都要输入用户名和密码

    /***************************************************************************** * github每次推送都要输入用户名和密 ...

  2. android.os.NetworkOnMainThreadException解决

    很早就知道Android4.0以后,要把耗时的网络操作放在多线程中,处理方法有: 1) setContentView(R.layout.activity_main)下面加上如下代码 if (andro ...

  3. Web Developer可以做得更多

    美国雅虎前端工程师Hedger Wang.这位原雅虎奇摩的第一位Web Developer,非常慷慨的与我们分享了他丰富的经验.现身说法,比空洞的理论更有感染力,我们发现现在遇到的很多问题也都是他曾经 ...

  4. oracle sql语句跟踪

    select b.SQL_TEXT,b.FIRST_LOAD_TIME,b.SQL_FULLTEXT    from v$sqlarea b    order by  b.FIRST_LOAD_TIM ...

  5. android edittext不弹出软键盘

    方法一: 在AndroidMainfest.xml中选择哪个activity,设置windowSoftInputMode属性为adjustUnspecified|stateHidden 例如:< ...

  6. 页面性能测试&提升方式

    性能测试包括:web系统页面测试.web系统后台测试 2种方式来提升你的web 应用程序的速度: ● 减少请求和响应的往返次数 ● 减少请求和响应的往返字节大小. 详细的看此文http://www.5 ...

  7. 判断图片加载完成,自适应iframe高度

    在做RSS订阅的时候遇到这样一个问题:点击文章标题时,弹出文章的详细界面.本来打算直接用弹出div层来显示文章的内容,但是设置div的overflow:scroll滚动条不好看,还有就是在androi ...

  8. php sortable 动态排序

    php sortable 动态排序未分页版.php 预览图: <?php mysql_connect("localhost","root","r ...

  9. Dzz任务板初版完成笔记-仿trello私有部署的一款轻量团队任务协作工具。

    刚完成了第一个版本Dzz任务板的设计开发,记录下设计思路和完成情况. Dzz任务板是DzzOffice中的团队协作套件中的一款应用,它需要安装在DzzOffice中使用. 主界面中需要能够快速简单的创 ...

  10. 获取 windows地址栏 网页地址栏 文件名

    int start=str.LastIndexOf("\\"); fileName=str.SubString(start+1,str.Length-start-1); int s ...