消息映射
消息的传递与发送是Windows应用程序的核心所在,任何事件的触发与响应均要通过消息的作用才能得以完成。在SDK编程中,对消息的获取与分发主要是通过消息循环来完成的,而在MFC编程中则是通过采取消息映射的方式对其进行处理的。相比而言,这样的处理方式要简单许多,这也是符合面向对象编程中尽可能隐含实现细节的原则。
一个完整的MFC消息映射包括对消息处理函数的原型声明、实现以及存在于消息映射中的消息入口。这几部分分别存在与类的头文件和实现文件中。一般情况下除了对自定义消息的响应外,对于标准Windows 消息的映射处理可以借助ClassWizard向导来完成。
在选定了待处理的Windows 消息后,向导将会根据消息的不同而生成具有相应函数参数和返回值的消息处理代码框架。下面这段代码给出了一个完成的MFC消息映射过程:
// 在.h文件中的声明 //{{AFX_MSG(CMessageMapView) afx_msg void OnMove(int x, int y); //}}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::OnMove(int x, int y) { CView::OnMove(x, y); // TODO: Add your message handler code here } |
宏名 | 说明 |
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的另一个成员函数OnWndMsg(),该函数首先检查到达的究竟是消息,命令还是通知(Notify),如果是消息就通过消息映射宏DECLARE_MESSAGE_MAP,BEGIN_MESSAGE_MAP和END_MESSAGE_MAP 完成对消息的映射。在宏定义中封装了部分代码,这些被封装的预定义代码可以在VC安装目录下的"\MFC\Include\Afxwin.h"中找到,在编译时将为编译器所展开。下面给出此预定义代码的实现清单:
#ifdef _AFXDLL #define DECLARE_MESSAGE_MAP() \ private: \ static const AFX_MSGMAP_ENTRY _messageEntries[]; \ protected: \ static AFX_DATA const AFX_MSGMAP messageMap; \ 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_DATA const AFX_MSGMAP messageMap; \ 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 } \ }; \ |
// 头文件 //{{AFX_MSG(CDIP_SystemView) afx_msg void OnEmboss(); afx_msg void OnUpdateEmboss(CCmdUI* pCmdUI); //}}AFX_MSG DECLARE_MESSAGE_MAP() …… // 源文件 BEGIN_MESSAGE_MAP(CDIP_SystemView, CScrollView) /{{AFX_MSG_MAP(CDIP_SystemView) ON_COMMAND(IDM_EMBOSS, OnEmboss) ON_UPDATE_COMMAND_UI(IDM_EMBOSS, OnUpdateEmboss) //}}AFX_MSG_MAP END_MESSAGE_MAP() …… void CDIP_SystemView::OnEmboss () { return; } …… void CDIP_SystemView::OnUpdateStartPos(CCmdUI* pCmdUI) { pCmdUI->Enable(m_bCanUse); } |
文章评论(0条评论)
登录后参与讨论