WINDOWS 消息的种类
WINDOWS中消息主要有以下三种类型:
1、标准的WINDOWS消息:这类消息是以WM_为前缀,不过WM_COMMAND例外。 例如: WM_MOVE、WM_QUIT等.
2、命令消息:命令消息以WM_COMMAND为消息名.在消息中含有命令的标志符ID,以区分具体的命令.由菜单,工具栏等命令接口对象产生.
3、控件通知消息:控件通知消息也是以WM_COMMAND为消息名.由编辑框,列表框,子窗口发送给父窗口的通知消息.在消息中包含控件通知码.以区分具体控件的通知消息.
其中标准的WINDOWS消息及控件通知消息主要由窗口类即直接或间接由CWND类派生类处理.相对标准WINDOWS消息及控件通知消息而言,命令消息的处理对象范围就广得多.它不仅可以由窗口类处理,还可以由文档类,文档模板类及应用类所处理。
MFC消息映射
消息的传递与发送是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
}
这里对Windows标准消息WM_MOVE做了消息映射,其中用到的BEGIN_MESSAGE_MAP、END_MESSAGE_MAP和头文件中的DECLARE_MESSAGE_MAP等均是用于消息映射的宏。这些宏声明了在应用程序框架中可用于在系统中浏览所有对象映射的成员变量和函数。
一般作为基类使用的CWnd类为Windows消息定义了大量窗口消息的缺省处理函数,这些函数大部分只是简单地调用了Windows的缺省过程,可以在派生类中对其进行重载。但是MFC应用程序框架却并没有象使用普通虚函数那样使用Windows消息处理函数,而是通过宏将指定的消息映射到派生类的成员函数。如果MFC仍象普通虚函数一样对消息响应函数进行处理,那么CWnd类就要为这上百个消息声明虚函数。而C++将为在程序中使用的每一个派生类都提供一个被称作vtable的虚拟函数分配表,这个分配表需要为每一个虚函数提供一个4字节的入口,而不管这些函数在派生类中是否真正被重载,这将不能有效利用存储空间。而且对于每一个不同类型的窗口或控件,应用程序都要为其提供一个超过400字节的虚拟函数分配表来实现对消息的响应。而采用MFC的用宏将Windows消息映射到C++成员函数的方式则可避免产生庞大的虚拟函数分配表,其消耗的内存是同它所包含的消息入口数量成正比的。
上面代码中的消息处理函数的具体实现为函数void CMessageMapView::OnMove(int x, int y) ,函数体里面调用了父类的处理函数CView::OnMove(x, y),那么在派生类需要WM_MOVE消息时,才构造对应的消息映射,而不需要把Windows消息的所有的缺省处理函数都在派生类中进行重载,正是这样就节省了很多存储空间。
这里ON_COMMAND宏将特定命令的处理同一个类成员函数建立了关联。而宏ON_UPDATE_COMMAND_UI则负责对命令的更新,即通过CCmdUI对象控制菜单/控件的是否可用或其他一些状态变化的更新。对命令的更新也可以将其理解为存在一个含有每个菜单入口的大表,各菜单入口含有菜单是否可用的标志。在显示菜单时通过快速检查该表而做出其所对应的每一个菜单项是否可用的决定。如果可用标志发生了变化,该表也将得到及时的更新。
// 头文件 //{{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); } |