`
wo_deqing
  • 浏览: 64576 次
文章分类
社区版块
存档分类
最新评论

VC++深入详解:MFC消息映射机制

 
阅读更多

1:windows OS事件驱动策略基于3种消息。标准消息、通告消息、命令消息。

  2:“事件”就是“消息”,事件是有形形象的,是站在人类能理解的角度来定义的。消息是无形抽象的,是站在OS能理解的角度来定义的。

  3:我把按下鼠标左键这一事件转换成WM_LBUTOONDOWN消息来告诉OS我做了按下鼠标左键这了件事情。现在OS知道了我做了按下鼠标左键这了件事情了,那么OS怎么处理呢?

  3.1:消息响应函数原型

  

  //{{AFX_MSG(CMyView)

  afx_msg void OnLButtonDown(UINT nFlags,CPoint point);

  //}}AFX_MSG

  DECLARE_MESSAGE_MAP()


  3.2:ON_WM_LBUTTONDOWN消息映射宏

  

  BEGIN_MESSAGE_MAP(CMyView, CView)

  //{{AFX_MSG_MAP(CMyView)

  ON_WM_LBUTTONDOWN()

  //}}AFX_MSG_MAP

  // Standard printing commands

  ON_COMMAND(ID_FILE_PRINT, CView::OnFilePrint)

  ON_COMMAND(ID_FILE_PRINT_DIRECT, CView::OnFilePrint)

  ON_COMMAND(ID_FILE_PRINT_PREVIEW, CView::OnFilePrintPreview)

  END_MESSAGE_MAP()


  3.3:消息响应函数的定义

  

  void CMyView::OnLButtonDown(UINT nFlags, CPoint point)

  // TODO: Add your message handler code here and/or call default

  MessageBox(WM_LBUTTONDOWN);

  CView::OnLButtonDown(nFlags, point);

 

  4:至此,我们从按下鼠标左键到看到如下图的效果,OS完成了对事件做出的反应。

 

消息的传递与发送是Windows应用程序的核心所在,任何事件的触发与响应均要通过消息的作用才能得以完成。在SDK编程中,对消息的获取与分发主要是通过消息循环来完成的,而在MFC编程中则是通过采取消息映射的方式对其进行处理的。相比而言,这样的处理方式要简单许多,这也是符合面向对象编程中尽可能隐含实现细节的原则。

  一个完整的MFC消息映射包括对消息处理函数的原型声明、实现以及存在于消息映射中的消息入口。这几部分分别存在与类的头文件和实现文件中。一般情况下除了对自定义消息的响应外,对于标准Windows 消息的映射处理可以借助ClassWizard向导来完成。

  在选定了待处理的Windows 消息后,向导将会根据消息的不同而生成具有相应函数参数和返回值的消息处理代码框架。下面这段代码给出了一个完成的MFC消息映射过程:

 

// 在.h文件中的声明

//{{AFX_MSG(CMessageMapView)

afx_msg void On<wbr>Move(int x, int y);</wbr>

//}}AFX_MSG

DECLARE_MESSAGE_MAP()

……

// 在.cpp文件中的实现

BEGIN_MESSAGE_MAP(CMessageMapView, CView)

//{{AFX_MSG_MAP(CMessageMapView)

ON_WM_MOVE()

//}}AFX_MSG_MAP

END_MESSAGE_MAP()

……

void CMessageMapView::On<wbr>Move(int x, int y) </wbr>

{

CView::On<wbr>Move(x, y);</wbr>

// TODO: Add your message handler co<wbr>de here</wbr>

}

  这里对Windows标准消息WM_MOVE做了消息映射,其中用到的BEGIN_MESSAGE_MAP、END_MESSAGE_MAP和头文件中的DECLARE_MESSAGE_MAP等均是用于消息映射的宏。这些宏声明了在应用程序框架中可用于在系统中浏览所有对象映射的成员变量和函数。除了以上三个比较常见的宏之外,MFC还提供了其他一些用于消息映射的宏,详情可参见下表:

 

宏名 说明
DECLARE_MESSAGE_MAP 在头文件声明源文件中所含有的消息映射
BEGIN_MESSAGE_MAP 标记源文件消息映射的开始
END_MESSAGE_MAP 标记源文件消息映射的结束
ON_COMMAND 将特定命令的处理委派给类的一个成员函数
ON_CONTROL 映射一个函数到一个定制控制通知消息。其中,定制控制通知消息是从一个控制发送到其父窗口的消息。
ON_CONTROL_RANGE 将一个控制ID的范围映射到一个消息处理函数
ON_CONTROL_REFLECT 映射一个由父窗口反射回控制的通知消息
ON_MESSAGE 将一个用户自定义消息映射到一消息处理函数
ON_NOTIFY 映射一个控制消息到一个函数
ON_NOTIFY_RANGE 映射一个控制ID范围内的控制消息到一个函数
ON_NOTIFY_EX 映射一个控制消息到一个函数,该成员函数返回FALSE或TRUE来表明通知是否应被传送到下一个对象以进行其他反应。
ON_NOTIFY_EX_RANGE 映射一个控制ID范围内的控制消息到一个函数,该成员函数返回FALSE或TRUE来表明通知是否应被传送到下一个对象以进行其他反应
ON_NOTIFY_REFLECT 映射一个控制消息到一个函数。该消息将会被控制的父窗口反射回来。
ON_REGISTERED_MESSAGE 映射一个唯一的消息到一个将要处理该注册消息的函数上。该消息是由RegisterWindowMessage()函数注册的。
ON_UPDATE_COMMAND_UI 映射一个函数来处理一个用户接口更新命令消息
ON_UPDATE_COMMAND_UI_RANGE 映射一个命令ID的范围到一个更新消息处理函数

  一般作为基类使用的CWnd类为Windows消息定义了大量窗口消息的缺省处理函数,这些函数大部分只是简单地调用了Windows的缺省过程,可以在派生类中对其进行重载。但是MFC应用程序框架却并没有象使用普通虚函数那样使用Windows消息处理函数,而是通过宏将指定的消息映射到派生类的成员函数。如果MFC仍象普通虚函数一样对消息响应函数进行处理,那么CWnd类就要为这上百个消息声明虚函数。而C++将为在程序中使用的每一个派生类都提供一个被称作vtable的虚拟函数分配表,这个分配表需要为每一个虚函数提供一个4字节的入口,而不管这些函数在派生类中是否真正被重载,这将不能有效利用存储空间。而且对于每一个不同类型的窗口或控件,应用程序都要为其提供一个超过400字节的虚拟函数分配表来实现对消息的响应。而采用MFC的用宏将Windows消息映射到C++成员函数的方式则可避免产生庞大的虚拟函数分配表,其消耗的内存是同它所包含的消息入口数量成正比的。

 

消息映射的工作原理

  前面给出了消息映射的一般形式,下面就对消息映射的工作原理做更深入的分析。任何使用了MFC应用程序框架的Windows程序都含有一个从CWinApp派生的应用程序类对象,成员函数Run()将被隐含调用,其调用的CWinThread类成员函数Run()将通过对GetMessage()、TranslateMessage()和DispatchMessage()等函数的调用完成同WinMain()类似的消息循环。在消息处理中,几乎所有的窗口对象都使用AfxWndProc()窗口处理函数,并通过一个包含了窗口句柄和对象指针等信息的列表而获取到一个指向对象的指针,由此可以调用CWnd的虚函数WindowProc()。WindowProc()函数调用了CWnd的另一个成员函数On<wbr>WndMsg(),该函数首先检查到达的究竟是消息,命令还是通知(Notify),如果是消息就通过消息映射宏DECLARE_MESSAGE_MAP,BEGIN_MESSAGE_MAP和END_MESSAGE_MAP 完成对消息的映射。在宏定义中封装了部分代码,这些被封装的预定义代码可以在VC安装目录下的"\MFC\Include\Afxwin.h"中找到,在编译时将为编译器所展开。下面给出此预定义代码的实现清单:</wbr>

 

#ifdef _AFXDLL

#define DECLARE_MESSAGE_MAP() \

private: \

 static const AFX_MSGMAP_ENTRY _messageEntries[]; \

protected: \

 static AFX_DA<wbr>TA const AFX_MSGMAP messageMap; \</wbr>

 static const AFX_MSGMAP* PASCAL _GetBaseMessageMap(); \

 virtual const AFX_MSGMAP* GetMessageMap() const; \

#else

 #define DECLARE_MESSAGE_MAP() \

private: \

 static const AFX_MSGMAP_ENTRY _messageEntries[]; \

protected: \

 static AFX_DA<wbr>TA const AFX_MSGMAP messageMap; \</wbr>

 virtual const AFX_MSGMAP* GetMessageMap() const; \

#endif

#ifdef _AFXDLL

#define BEGIN_MESSAGE_MAP(theClass, baseClass) \

const AFX_MSGMAP* PASCAL theClass::_GetBaseMessageMap() \

 { return &baseClass::messageMap; } \

const AFX_MSGMAP* theClass::GetMessageMap() const \

 { return &theClass::messageMap; } \

AFX_COMDAT AFX_DATADEF const AFX_MSGMAP theClass::messageMap = \

 { &theClass::_GetBaseMessageMap, &theClass::_messageEntries[0] }; \

AFX_COMDAT const AFX_MSGMAP_ENTRY theClass::_messageEntries[] = \

 { \

  #else

   #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::_messageEntries[0] }; \

    AFX_COMDAT const AFX_MSGMAP_ENTRY theClass::_messageEntries[] = \

    { \

  #endif

  #define END_MESSAGE_MAP() \

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

 }; \

  图1展示了消息映射处理的过程示意。搜寻过程是从CMainWindow的消息入口开始的,DECLARE_MESSAGE_MAP,BEGIN_MESSAGE_MAP和END_MESSAGE_MAP等消息映射宏通过搜索派生类消息映射的函数允许访问积累消息映射的入口。如果由CFrameWnd类派生的类CMainWindow没有捕获通常由CFrameWnd捕获的消息,那么消息将由相同的由派生类所继承的CFrameWnd类函数捕获。同样,如果CFrameWnd类仍没有捕获通常由其父类CWnd捕获的消息,则将继续上溯下去。这种消息映射的继承性与C++的继承是一致的。

  另外,消息映射函数入口可以在在消息到达时为那些被隐含消息循环所调用的函数从中查看,并决定哪一个对象以及对象中的哪一个成员函数应该负责此消息的处理。虽然消息映射的内部工作原理比较复杂,但MFC通过预定义宏等手段将其完整的封装了起来,展现给开发人员的只是简单明了的MFC消息映射。

vc++消息映射与命令通知 - linda - Linda

图1 消息映射处理过程示意

命令和通知

  命令和通知实际都是一种特殊的消息类型。在SDK编程中,菜单和控件的动作均会产生一个WM_COMMAND命令消息,通过对消息参数wParam的区分可以识别出具体是哪个控件或菜单发出的命令。在MFC应用程序框架下,菜单和控件产生的消息将有所区分,选取菜单产生的消息被称作命令,而点击控件所产生的消息则被称作通知。由于命令和通知的本质仍是一种消息,因此在基本原理上仍是同消息一致的,即也是通过消息循环进入On<wbr>WndMsg()进而为对应的处理函数所响应。但是在使用上,命令和消息还是有区别的,其中一个最主要的区别是消息只有CWnd类的派生类所接收,而命令和通知则可以为所有从CCmdTarget派生出去的类对象所接收,从MFC类的继承关系可以看出,除CWnd外CWinThread、CDocument和CDocItem等也都可以接收命令和通知。除此之外,命令和通知在从消息循环进入到On<wbr>WndMsg()后的这段过程也是同消息传递略有出入的,图2(a)和(b)分别给出了命令和通知的传递流程:</wbr></wbr>

vc++消息映射与命令通知 - linda - Linda

图2 命令/通知传递流程

  这里CWnd::On<wbr>Command()在检查完各项细节后、CWnd::On<wbr>Notify()在检查完不同条件后都调用了虚函数On<wbr>CmdMsg()。这样,对于不同的菜单项和控件就可以有不同的实现。从下面给出的命令传递过程示例代码可以看出命令/通知的传递与消息的映射是非常类似的:</wbr></wbr></wbr>

 

// 头文件

//{{AFX_MSG(CDIP_SystemView)

afx_msg void On<wbr>Emboss();</wbr>

afx_msg void On<wbr>UpdateEmboss(CCmdUI* pCmdUI);</wbr>

//}}AFX_MSG

DECLARE_MESSAGE_MAP()

……

// 源文件

BEGIN_MESSAGE_MAP(CDIP_SystemView, CScrollView)

/{{AFX_MSG_MAP(CDIP_SystemView)

ON_COMMAND(IDM_EMBOSS, On<wbr>Emboss)</wbr>

ON_UPDATE_COMMAND_UI(IDM_EMBOSS, On<wbr>UpdateEmboss)</wbr>

//}}AFX_MSG_MAP

END_MESSAGE_MAP()

……

void CDIP_SystemView::On<wbr>Emboss () </wbr>

{

return;

}

……

void CDIP_SystemView::On<wbr>UpdateStartPos(CCmdUI* pCmdUI)</wbr>

{

pCmdUI->Enable(m_bCanUse);

}

  这里ON_COMMAND宏将特定命令的处理同一个类成员函数建立了关联。而宏ON_UPDATE_COMMAND_UI则负责对命令的更新,即通过CCmdUI对象控制菜单/控件的是否可用或其他一些状态变化的更新。对命令的更新也可以将其理解为存在一个含有每个菜单入口的大表,各菜单入口含有菜单是否可用的标志。在显示菜单时通过快速检查该表而做出其所对应的每一个菜单项是否可用的决定。如果可用标志发生了变化,该表也将得到及时的更新。

  小结

  消息和命令作为VC++编程中很基本的一种机制,在几乎所有的VC++程序中都有所涉及。在学习VC++其他编程技术之前必须首先对消息映射机制是如何运做的有一个清楚的认识。本文所述代码在Windows 2000 Professional下由Microsoft Visual C++ 6.0下调试通过。

 

 

 

分享到:
评论

相关推荐

    VC++深入详解+FC消息响应机制.7z

    《VC++深入详解+FC消息响应机制》是一个包含深度学习VC++编程技术和MFC消息处理机制的资源包。其中,MFC(Microsoft Foundation Classes)是微软提供的一个C++类库,用于简化Windows应用程序的开发,而消息响应机制...

    VC++深入详解.rar

    《VC++深入详解》这本书是面向有一定VC++基础的开发者,旨在揭示Windows程序设计的深层原理,特别是深入探讨Microsoft Foundation Class (MFC)框架的工作机制。MFC是微软提供的一套C++类库,用于简化Windows应用程序...

    VC++深入详解学习笔记(非常全面

    本学习笔记涵盖了VC++的基础知识、MFC程序框架、消息映射机制、DC的获取、菜单编程、对话框编程、图形绘制、文件操作、网络编程、多线程编程、进程间通信、ActiveX控件、动态链接库、Hook钩子函数等方面的知识点。...

    VC++深入详解code

    《VC++深入详解code》是孙鑫老师的一部经典之作,涵盖了MFC、Windows编程、GDI、多线程、同步以及Socket等多个关键领域。这些主题对于深入理解VC++编程至关重要,下面将逐一进行详细解释。 1. **MFC(Microsoft ...

    VC++深入详解 源码

    书中可能涵盖了MFC的基本结构、控件使用、消息映射机制以及事件处理等。 2. **编译与链接过程**:了解VC++的编译器如何将源代码转化为可执行文件至关重要。这包括预处理器、编译器、汇编器和链接器的作用,以及它们...

    VC++深入详解代码

    学习MFC的窗口类、消息映射机制、控件使用等是VC++开发的重要部分。 4. **资源管理**:在VC++中,了解如何管理和使用内存、文件、数据库连接等资源至关重要。这包括动态内存分配、智能指针的使用、异常处理等。 5....

    VC++深入详解代码3-4.rar

    《VC++深入详解》是一本专注于Microsoft Visual C++编程技术的专著,涵盖了从基础知识到高级特性的全面解析。在“VC++深入详解代码3-4”这个压缩包中,包含的是书中第三章和第四章的示例代码,旨在帮助读者通过实践...

    VC++深入详解3

    在Lesson3中,你将学习如何利用MFC创建窗口、对话框、控件以及处理消息映射,理解MFC的设计模式和类层次结构,从而更好地进行Windows程序设计。 二、STL(Standard Template Library) STL是C++标准库的一部分,...

    VC++深入详解code.rar

    3. **消息映射**:MFC通过消息映射机制将窗口消息与函数关联,使得消息处理更加直观和简洁。 4. **控件使用**:MFC支持各种Windows控件,如按钮、编辑框、列表视图等,开发者可以通过MFC类库轻松地添加和管理这些...

    VC++深入详解code chapter2

    - **消息映射**:MFC通过消息映射机制将消息与函数关联,实现事件驱动编程。 - **文档/视图架构**:这是MFC设计的一个核心思想,用于分离数据(文档)和用户界面(视图)。 - **控件和对话框**:MFC提供了丰富的...

    VC++ 深入详解

    3. **消息映射机制**:MFC通过消息映射机制将窗口消息与成员函数关联起来,简化了事件处理的过程。这种机制使得开发者能够更专注于应用程序逻辑本身,而不是复杂的Windows消息循环。 4. **控件和对话框**:MFC提供了...

    VC++深入详解源码

    《VC++深入详解源码》是一本专注于探讨Microsoft Visual C++编程语言的深度解析书籍,其核心内容可能涵盖了从基础语法、编译原理到高级特性、底层机制等多个方面。通过研究源码,读者可以深入了解VC++的工作原理,...

    VC++深入详解光盘代码!

    书中源码会展示如何利用MFC创建窗口、对话框、控件以及处理消息映射,理解MFC的架构和设计模式。 3. **Windows API调用**:虽然MFC提供了丰富的功能,但有时开发者仍需直接调用Windows API来实现特定功能。书中可能...

    孙鑫C++教程(全20讲)PPT讲义源码及电子书

    第四课:MFC消息映射机制的剖析,讲述如何运用ClassWizard 第五课:文本编程 第六课:菜单的工作原理及编写应用 第七课:对话框用户界面程序的编写 第八课:对话框第二部分 第九课:如何修改MFC AppWizard向导生成的框架...

    VC++深入详解学习笔记

    Lesson4中,笔记讲述了MFC消息映射机制的剖析,如何运用ClassWizard,了解发送给窗口的消息是如何被MFC框架通过窗口句柄映射表和消息映射表来用窗口类的函数进行响应的。 Lesson5中,笔记讲解了CDC的文字处理程序的...

    VC++ MFC类库详解

    **VC++ MFC类库详解** MFC(Microsoft Foundation Classes)是微软开发的一个C++类库,它基于面向对象编程,为Windows应用程序提供了一种快速开发的框架。MFC是Visual C++的重要组成部分,它封装了Windows API,...

    VC++深入详解源代码

    3. **事件处理**:讨论MFC中的消息映射和消息处理机制,如何编写响应用户操作的代码。 4. **控件使用**:涵盖各种标准控件(如按钮、编辑框、列表视图等)的使用,以及自定义控件的创建。 5. **用户界面设计**:...

Global site tag (gtag.js) - Google Analytics