MFC的快速理解:

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

现MFC的消息映射的机制就是其中之一。

2.MFC消息映射机制有关的宏有下面几个:

 1)DECLARE_MESSAGE_MAP()宏

 2)BEGIN_MESSAGE_MAP(theClass, baseClass)宏

 3)END_MESSAGE_MAP()宏

弄懂MFC消息映射机制的最好办法是将找出一个具体的实例,将这些宏展开,并找出相关的数据结构。

  DECLARE_MESSAGE_MAP()

  DECLARE_MESSAGE_MAP()宏的定义如下:

  #define DECLARE_MESSAGE_MAP()

  private:

  static const AFX_MSGMAP_ENTRY _messageEntries];

  protected:

  static AFX_DATA const AFX_MSGMAP messageMap;

  virtual const AFX_MSGMAP* GetMessageMap() const;

从上面的定义可以看出,DECLARE_MESSAGE_MAP()作下面三件事:

定义一个长度不定的静态数组变量_messageEntries];

定义一个静态变量messageMap;

定义一个虚拟函数GetMessageMap();  

在DECLARE_MESSAGE_MAP()宏中,涉及到MFC中两个对外不公开的数据结构AFX_MSGMAP_ENTRY和AFX_MSGMAP。为了弄清楚消息映射,有必要考察一下这两

个数据结构的定义。

  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_ENTRY结构实际上定义了消息和处理此消息的动作之间的映射关系。

因此静态数组变量_messageEntries]实际上定义了一张表,表中的每一项指定了相应的对象所要处理的消息和处理此消息的函数的对应关系,这张表也称为消息映射表。

再看看AFX_MSGMAP的定义。

  struct AFX_MSGMAP
  {
   const AFX_MSGMAP* pBaseMap;
   const AFX_MSGMAP_ENTRY* lpEntries;
   };  

不难看出,AFX_MSGMAP定义了一单向链表,链表中每一项的值是一指向消息映射表的指针(实际上就是_messageEntries的值)。通过这个链表,使得在某个类中调用

基类的的消息处理函数很容易,因此,“父类的消息处理函数是子类的缺省消息处理函数”就“顺理成章”了。

在后面的“MFC窗口的消息处理”一节中会对此作详细的讲解。

由上述可见,在类的头文件中主要定义了两个数据结构:消息映射表和单向链表。

  BEGIN_MESSAGE_MAP()和END_MESSAGE_MAP()

  它们的定义如下:

  #define BEGIN_MESSAGE_MAP(theClass, baseClass)

  const AFX_MSGMAP* theClass::GetMessageMap() const

  {

     return &theClass::messageMap;

   }

  AFX_COMDAT AFX_DATADEF const AFX_MSGMAP theClass::messageMap

  {

     &baseClass::messageMap, &theClass::_messageEntries0]

  };

  AFX_COMDAT const AFX_MSGMAP_ENTRY theClass::_messageEntries]

  { 
   #define END_MESSAGE_MAP()

   {0, 0, 0, 0, AfxSig_end, (AFX_PMSG)0 }

  };

对应BEGIN_MESSAGE_MAP()的定义可能不是一下子就看得明白,不过不要紧,举一例子就很清楚了。对于BEGIN_MESSAGE_MAP(CView, CWnd),VC预编译器将其展

开成下面的形式:

  const AFX_MSGMAP* CView::GetMessageMap() const
  {
   return &CView::messageMap;
  }

  AFX_COMDAT AFX_DATADEF const AFX_MSGMAP CView::messageMap 
  {
   &CWnd::messageMap,
   &CView::_messageEntries0
  };
  AFX_COMDAT const AFX_MSGMAP_ENTRY CView::_messageEntries
  {

至于END_MESSAGE_MAP()则不过定义了一个表示映射表结束的标志项,我想大家对于这种简单的技巧应该是很熟悉的,无需多述。

我想大家也已经想到了ON_COMMAND这样的宏的具体作用了,不错它们只不过定义了一种类型的消息映射项,看看ON_COMMAND的定义:

  #define ON_COMMAND(id, memberFxn) 
  {

    WM_COMMAND, CN_COMMAND, (WORD)id, (WORD)id, AfxSig_vv, (AFX_PMSG)&memberFxn

  },  

  根据上面的定义,ON_COMMAND(ID_FILE_NEW, OnFileNew)将被VC预编译器展开如下:

  {    

    WM_COMMAND, CN_COMMAND, (WORD)id, (WORD)id, AfxSig_vv, (AFX_PMSG)&OnFileNew

  },

3.MFC的消息映射机制已经清楚了,现在提出并解答两个问题以作为对这一节的小结。

为什么不直接使用虚拟函数实现消息处理函数呢?

  MFC的设计者们在设计MFC时有一个很明确的目标,就是使得“MFC的代码尽可能小,速度尽可能快”,如果采用虚拟函数,那么对于所有的窗口消息,都必须有一个与之对

应的虚拟函数,因而对每一个从CWnd派生的类而言,都会有一张很大的虚拟函数表vtbl。但是在实际应用中,一般只对少数的消息进行处理,大部分都交给系统缺省处理,所以

表中的大部分项都是无用项,这样做就浪费了很多内存资源,这同MFC设计者们的设计目标是相违背的。当然,MFC所使用的方法只是解决这类问题的方式之一,不排除还有

其他的解决方式,但就我个人观点而言,这是一种最好的解决方式,体现了很高的技巧性,值得我们学习。  

  第二个问题,是由上面的问题引申出来的。如果在子类和父类中出现了相同的消息出来函数,VC编译器会怎么处理这个问题呢?VC不会将它们看作错误,而会象对

待虚拟函数类似的方式去处理,但对于消息处理函数(带afx_msg前缀),则不会生成虚拟函数表vtbl。

MFC下一个消息的处理过程是一般是这样的。

1)_AfxCbtFilterHook截获消息(这是一个钩子函数)

2)_AfxCbtFilterHook把窗口过程设定为AfxWndProc。

3)函数AfxWndProc接收Windows操作系统发送的消息。

4)函数AfxWndProc调用函数AfxCallWndProc进行消息处理。

5)函数AfxCallWndProc调用CWnd类的方法WindowProc进行消息处理。

4.如何添加自己的消息?

我们已经了解了WINDOW的消息机制,如何加入我们自己的消息呢?好我们来看一个标准的消息处理程序是这个样子的,在 CWnd 类中预定义了标准 Windows 消息 (WM_XXXX

WM是WINDOW MESSAGE的缩写) 的默认处理程序。类库基于消息名命名这些处理程序。例如,WM_PAINT 消息的处理程序在 CWnd 中被声明为:

afx_msg void OnPaint();

afx_msg 关键字通过使这些处理程序区别于其他 CWnd 成员函数来表明 C++ virtual 关键字的作用。但是请注意,这些函数实际上并不是虚拟的,而是通过消息映射实现的。

我们在本文的一开始便说明了为什么要这样做。

所有能够进行消息处理的类都是基于CCmdTarget类的,也就是说CCmdTarget类是所有可以进行消息处理类的父类。CCmdTarget类是MFC处理命令消息的基础和核心。

若要重写基类中定义的处理程序,只需在派生类中定义一个具有相同原型的函数,并创建此处理程序的消息映射项。我们通过ClassWizard可以建立大多数窗口消息或自定义的

消息,通过ClassWizard可以自动建立消息映射,和消息处理函数的框架,我们只需要把我们要做的事情填空,添加你要做的事情到处理函数。这个非常简单,就不细说了。但是

也许我们需要添加一些ClassWizard不支持的窗口消息或自定义消息,那么就需要我们亲自动手建立消息映射和消息处理的框架,通常步骤如下:

第一步:定义消息。Microsoft推荐用户自定义消息至少是WM_USER+100,因为很多新控件也要使用WM_USER消息。

#define WM_MYMESSAGE (WM_USER + 100)

第二步:实现消息处理函数。该函数使用WPRAM和LPARAM参数并返回LPESULT。

LPESULT CMainFrame::OnMyMessage(WPARAM wParam, LPARAM lParam)
{
  // TODO: 处理用户自定义消息,填空就是要填到这里。
  return 0;
}
第三步:在类头文件的AFX_MSG块中说明消息处理函数:

// {{AFX_MSG(CMainFrame)

afx_msg LRESULT OnMyMessage(WPARAM wParam, LPARAM lParam);

//}}AFX_MSG

DECLARE_MESSAGE_MAP()

第四步:在用户类的消息块中,使用ON_MESSAGE宏指令将消息映射到消息处理函数中。

ON_MESSAGE( WM_MYMESSAGE, OnMyMessage )

5.MFC的机制大概就是这些了,如需在深入学习,可以看孙鑫的《VC++深入详解》或者是侯杰的《深入浅出MFC》。

                                              改变自己,从现在做起-----------久馆

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

  1. MFC中消息响应机制

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

  2. MFC消息响应机制分析

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

  3. MFC消息响应机制 q

    MFC消息响应机制分析 1 引言微软公司提供的MFC基本类库(Microsoft Foundation Classes),是进行可视化编程时使用最为流行的一个类 库.MFC封装了大部分Windows ...

  4. MFC的消息映射机制揭秘

    MFC的设计者们在设计MFC时,紧紧把握一个目标,那就是尽可能使得MFC的代码要小,速度尽可能快.为了这个目标,他们使用了许多技巧,其中很多技巧体现在宏的运用上,实现MFC的消息映射的机制就是其中之一 ...

  5. MFC的消息反射机制

    1.消息反射解释: 父窗口将子窗口发给它的通知消息,首先反射回子窗口进行处理(即给子窗口一个机会,让子窗口处理此消息),这样通知消息就有机会能被子窗口自身进行处理. 2.MFC中引入消息反射的原因: ...

  6. Mfc资源消息的响应机制

    Mfc消息的响应机制 Mfc中有很多资源,如图标资源,菜单资源,工具栏资源等等:那么,资源是如何进行消息响应和消息映射的呢? 它们的流程是: 某种资源——对应的ID号——消息映射——响应函数的声明与实 ...

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

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

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

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

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

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

  10. MFC的消息机制

    MFC的消息循环(::GetMessage,::PeekMessage)消息泵(CWinThread::PumpMessage)和MFC的消息在窗口之间的路由是两件不同的事情 分两个步骤完成: 1 “ ...

随机推荐

  1. BZOJ2134——单选错位

    1.题意:这就是说考试的时候抄串了一位能对几个(雾) 2.分析:这是一个期望问题,期望就是平均,E(a+b)=E(a)+E(b),所以我们直接算出每个点能对几个就好,那么就是1/max(a[i],a[ ...

  2. heartbleed漏洞利用

    1.  heartbleed漏洞扫描: 2.  heartbleed漏洞利用: poc.py      117.52.93.111 貌似没有打到管理员账号密码,可能是管理员没登录,其实,可以写一个自动 ...

  3. Linux/Android 系统怎么修改mac地址

    使用 busybox ifconfig eth0 hw ether AA:BB:CC:DD:EE 可以修改, 但是每次重启都会改回原来的. 所以要修改 /etc/init.mini210.sh (可能 ...

  4. Dispose模式

    Dispose模式 Dispose模式是.NET中很基础也很重要的一个模式,今天重新复习一下相关的东西并记录下来. 什么是Dispose模式? 什么时候我们该为一个类型实现Dispose模式 使用Di ...

  5. JWT 超详细分析

    请参考以下链接 https://learnku.com/articles/17883

  6. c语言: 修改参数的地址,及注意事项

    如果需要在函数中修改参数的地址,首先参数肯定要是指针类型,同时传递的参数不能直接使用数组变量,至少需要先转换一下. 比如: char str[] = "123"; 不能直接传 ab ...

  7. Oracle 客户端 NLS_LANG 的设置

    参考链接1: https://blog.csdn.net/xinzhan0/article/details/78311417#t3 参考链接2: https://blog.csdn.net/xinzh ...

  8. Javascript 对象复制

    如果对象只是一个数据集,可采用json化再反json化的方式克隆一个对象,这个过程会丢失对象的方法.效率比较低. 可以采用如下递归的方式复制一个对象. function clone(target) { ...

  9. Azure编程笔记(3):用Fiddler调试Azure的应用程序

     内容提要 Azure的服务是通过RESTfulAPI提供的. 尽管Azure针对非常多编程语言都提供了SDK.但这些SDK也仅仅是RESTfulAPI的一层封装. 在调用SDK或者RESTful ...

  10. 题目1461:Tempter of the bone(深度优先遍历DFS)

    题目链接:http://ac.jobdu.com/problem.php?pid=1461 详解链接:https://github.com/zpfbuaa/JobduInCPlusPlus 参考代码: ...