【笔记】MFC消息映射机制详解
个人觉得,学习VC的过程里很重要的一点就是全局概念的理解,很多时候我们写代码,貌似知道某个函数的原型,可以准确使用。但是项目稍微一大,很多函数的互相使用,调动流程等等就会让人摸不着头脑。所以花点时间了解一下平台的运作机制,在使用起来会头脑会更加清晰一些。特此总结一下MFC消息映射机制。
1.Windows程序设计是一种事件驱动方式的程序设计模式,主要是基于消息的。
所谓消息,就是点击鼠标、键盘输入、自己定义的等等,在Windows中由MSG结构体来表示:
typedef struct tagMSG{
HWND hwnd;//消息所属窗口
UINT message;//消息标识符,数值不便记忆,所以定义为宏WM_XXX
WPARAM wParam;//unsigned int
LPARAM lParam;//long
DWORD time;//投递到消息队列中的时间
POINT pt;//鼠标当前位置
}
2.关于消息映射的代码
一个MFC消息响应函数在程序中有三处相关信息:
(1)头文件中的声明:afx_msg是一个宏,说明后面的函数是个“消息响应函数”。
// 生成的消息映射函数
protected:
DECLARE_MESSAGE_MAP()
public:
afx_msg void OnLButtonDown(UINT nFlags, CPoint point);
(2)源文件中的关联:该类的消息映射表,中间那个消息映射宏的作用,就是把鼠标左键按下消息与一个消息响应函数关联起来。
// CDrawView
IMPLEMENT_DYNCREATE(CDrawView, CView)
BEGIN_MESSAGE_MAP(CDrawView, CView)
ON_WM_LBUTTONDOWN()
END_MESSAGE_MAP()
(3)源文件中的实现:消息响应函数的具体定义(函数体)。
void CDrawView::OnLButtonDown(UINT nFlags, CPoint point)
{
// TODO: 在此添加消息处理程序代码和/或调用默认值
MessageBox("鼠标左键已按下!");
CView::OnLButtonDown(nFlags, point);
}
3.MFC消息映射机制的具体实现方法:
在每个能接收和处理消息的类中,定义一个消息和消息函数静态对照表,即消息映射表。
在消息映射表中,消息与对应的消息处理函数指针是成对出现的。某个类能处理的所有消息及其对应的消息处理函数的地址都列在这个类所对应的静态表中。当有消息需要处理时,程序只要搜索该消息静态表,查看表中是否含有该消息,就可知道该类能否处理此消息。如果能处理该消息,则同样依照静态表能很容易找到并调用对应的消息处理函数。
4.消息函数处理的过程
(1)首先判断是否有消息响应函数:
在响应窗口类中查找所需的消息响应函数。在相应的头文件中查找是否有声明。
(2)再到子类源文件中,查看消息地图,是否有消息映射宏。
如果找到消息响应函数,则调用它对消息进行处理。
如果没有找到,就交给基类进行处理。
补充:其实消息可以有多种实现方式,比如像以下两种:
<1>消息产生->操作系统将它放入应用程序的消息队列->应用程序取出消息->交给操作系统->系统调用“窗口过程函数”->函数利用switch-case结构对消息进行判断处理
<2>在基类中对每一种消息定义一个虚函数->子类需要时,重写虚函数->程序调用时,根据多态性正确调用
先说第一种:这种消息机制对于我们起初认识消息响应这个概念,是比较易于理解的。但是真正应用到实际中,就会发现它不是很方便,至少对于编程人员来讲,不是很清晰明了。当想要查看某消息响应时都需要看“窗口过程函数”,读代码显得比较费时。
再说第二种:第二种方式从原理上讲是可以的,但从编程角度,就会发现造成了内存资源的巨大浪费。因为虚函数是有"虚函数表"的(空表4个字节,VPtr指针),在MFC中派生的层次可多了去了,敢想象那么多的虚表吗?显然不太合适。
参考资料:VC++深入详解——孙鑫