MFC的消息映射机制的理解

来源:互联网 发布:iis中将端口改为81 编辑:程序博客网 时间:2024/04/30 04:52

看过孙鑫老师的MFC视频后,对MFC的消息映射表和消息映射机制有了更深的理解,现在将个人理解总结如下。
一、映射机制原理
Windows程序是基于消息机制来编程的。
MFC消息映射机制的具体实现方法是:在每个能接收和处理消息的类中,定义一个消息和消息函数静态对照表,即消息映射表。在消息映射表中,消息与对应的消息处理函数指针是成对出现的,也就是他们是绑定在一起的。某个类能处理的所有消息及其对应的消息处理函数的地址都列在这个类所对应的静态对照表中。当有消息需要处理时,程序只要搜索该消息静态表,查看表中是否含有该消息,就可以知道该类能否处理此消息。如果能处理该消息,则同样依照静态表能很容易找到并调用对应的消息处理函数。
之所以没有采用:在基类中针对每种消息做一个虚函数(虚函数必须由一个虚函数表vtable来实现),当子类对消息响应时候,只要在子类中重写这个虚函数即可,是因为MFC类派生层次很多,如果在基类对每个消息进行虚函数处理,那么从基类派生的每个子类都将背负一个庞大的虚函数表,这样浪费内存,故MFC没有采取这种方式而采取消息映射方式。 
二、举例(vc视频中例子)
    假设我们给视类添加一个鼠标左键的消息响应函数,MFC会在三处添加代码:
1)视类的头文件中
// 生成的消息映射函数protected: //{{AFX_MSG(CDrawView) afx_msg void OnLButtonDown(UINT nFlags, CPoint point); //}}AFX_MSG DECLARE_MESSAGE_MAP() //afx_msg宏表示声明的是一个消息响应函数。
2)在视类的源文件中
BEGIN_MESSAGE_MAP(CDrawView, CView) //{{AFX_MSG_MAP(CDrawView) 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())
    在宏BEGIN_MESSAGE_MAP()与END_MESSAGE_MAP()之间进行消息映射。宏ON_WM_LBUTTONDOWN()把消息WM_LBUTTONDOWN与它的响应函数OnLButtonDown()相关联。这样一旦有消息的产生,就会自动调用相关联的消息响应函数去处理。 
#define ON_WM_LBUTTONDOWN() \
{ WM_LBUTTONDOWN, 0, 0, 0, AfxSig_vwp, \
(AFX_PMSG)(AFX_PMSGW) \
(static_cast< void (AFX_MSG_CALL CWnd::*)(UINT, CPoint) > ( &ThisClass :: OnLButtonDown)) },
3)还是在在视类的源文件中
    void CMFCTestView::OnLButtonDown(UINT nFlags, CPoint point)
{
// TODO: 在此添加消息处理程序代码和/或调用默认值
    MessageBox("View Clicked");
   CView::OnLButtonDown(nFlags, point);
}
    由上可见,一个MFC消息响应函数在程序中有三处相关信息:函数原型,函数实现,以及用来关联消息和消息响应函数的宏。
三、机理分析
MFC在后台维护了一个句柄和C++对象指针对照表,当收到一个消息后,通过消息结构里的资源(消息参数,可获得窗口句柄,查对照表)就可找到与它对应的一个C++对象指针,然后把这个指针传给应用程序框架窗口类的基类,基类利用这个指针调用WindowProc()函数(位于WinCore.cpp文件中)对消息进行处理。
在上面所说的例子中,C++对象指针即指CMFCTestView*,窗口句柄与CMFCTestView对象的指针CMFCTestView*存在一一对应的关系。
WindowProc()函数中调用OnWndMsg()函数,真正的消息路由及处理是由 OnWndMsg()函数完成的。由于WindowProc()和OnWndMsg()都是虚函数,而且是用派生类对象指针调用的,有C++的多态性可知,故最终调用子类的。在OnWndMsg()函数处理时,根据消息种类去查找消息映射,判断所发的消息有没有响应函数,具体方式是到相关的头文件和源文件中寻找消息响应函数声明,最终找到对应的消息处理函数。当然,如果子类中没有对消息进行处理,则消息交由基类处理。
virtual LRESULT WindowProc(UINT message, WPARAM wParam, LPARAM lParam);
virtual BOOL OnWndMsg(UINT message, WPARAM wParam, LPARAM lParam, LRESULT* pResult);
说明:
1、在VC6.0中删除已添加的消息响应函数
选中相关联的类,属性,在属性页的消息栏中,在要删除的消息上,点击,选择删除。VC6.0会帮我们删除与消息相关的三处代码(包含消息响应函数)。
2、句柄和指针
句柄是由系统分配的资源ID(我们认为它是一个标识某个系统资源的32位的整数),用于标识系统所分配的资源,这里的资源包含进程、线程等的广泛内容。句柄和指针都是地址。
句柄可以认为是某种意义上的指针,但不是指针;句柄是一些表的索引;也就是指向指针的指针(即二级指针),是windows系统在内存中维护的一个对象或一个窗口。因为以虚拟存储方式,需要调度页面,所以程序实际的物理地址是不断变化的,系统中用一个内存地址不变的表来维护这个变化的地址(进行登记),因此当访问到这个不变的表项时就能确定程序的物理地址(所以叫句柄为指向指针的指针。)
Win32里,句柄是指向一个“无类型对象”(void*)的指针,是一个4字节长的数据。
从构造上看,句柄是一个指针,尽管它没有指向用于存储某个对象的内存位置。事实上,句柄指向一个包含了对该对象进行的引用的位置。
句柄的声明是这样的: 
typedef void *HANDLE 
由于Windows是一个多任务操作系统,它可以同时运行多个程序或一个程序的多个副本,这些运行的程序称为一个实例。为了对同一程序的多个副本进行管理,Windows引入了实例句柄。Windows为每个应用程序建立一张表,实例句柄就好象是这张表的一个索引。 
不同在于: 
1)句柄所指的可以是一个很复杂的结构,并且很有可以是与系统有关的,比如说上面所说的线程的句柄,它指向的就是一个类或者结构,他和系统有很密切的关系,当一个线程由于不可预料的原因而终止时,系统就可以回收它所占用的资源,如CPU,内存等;这个句柄中的某一些项,是与系统进行交互的。
2)指针也可以指向一个复杂的结构,但通常是用户定义的,所以必需的工作都要用户完成,特别是在删除的时候。
3、视类窗口始终覆盖在框架类窗口之上,所以,我们的鼠标操作一般由视类窗口捕获。
0 0
原创粉丝点击