简要清理一下MFC的消息机制

第 1 节            MFC定义了哪些消息

根据《深入浅出MFC》上所描述的,MFC的消息分为:消息和命令和Control Notification,消息的命令是以WM_作为开头的,命令是以ID开头的,那么这些消息或命令到底是在哪儿进行定义的呢?

首先,我们来看命令,我们在编译器中跳转到定义,发现它们被定义在afxres.h之中。如下:

 

分为Filecommands,编号从0xE100到0xE10C,Edit commands,编号从0xE120到0xE12C,Windows commands编号从0xE130到0xE135,Help and App commands编号从0xE140到0xE147,OLE commands编号从0xE200到0xE21F,View commands编号从0xE800到0xE815,RecordForm commands,编号从0xE900到0xE903。

那消息的定义在哪儿呢?在编译器里面跟踪,可以发现消息的定义在WinUser.h里面,如下图所示:

 

上网查资料得知,系统保留的消息标识符的取值范围为0×0000~0x03FF(0~1023),专门用于系统定义的消息;应用定义的消息不能使用这些值,应用定义的消息取值范围为0×0400~0x7FFF(0~32767)。

第 2 节            跟踪一条系统消息

MFC的消息机制在《深入浅出MFC》中已经讲得很清楚,但是为了更熟悉这个流程,我决定跟踪一条系统的消息,看看它被处理的过程。我用Visual Studio 2010建立了一个多文档的工程,命名为mfcmul,在mfcmulview是这个工程的视图类,在这个工程里面添加WM_LButtonDown的消息处理函数:

 

首先找到整个消息的源头,Windows的回调函数,在文件wincore.cpp里面AfxCallWndProc函数。此函数调用函数pWnd->WindowProc。但是不知道什么原因,跟踪AfxCallWndProc出现的困难,所以从pWnd->WindowProc开始。

在View里面的消息首先送到pWnd->WindowProc里面,然后pWnd->WindowProc调用虚函数pWnd->OnWndMsg

 

在OnWndMsg里面会根据消息的类型判断是不是WM_COMMAND,如果是WM_COMMAND,则调用OnCommand函数,再判断是不是WM_NOTIFY,如果是WM_NOTIFY则调用函数OnNotify,再判断消息是不是WM_ACTIVATE,显然WM_LButtonDown不属于这些消息,则继续查找,然后在该函数的一个地方循环消息的Map,然后调用相应的函数进行处理:

 

第 3 节            跟踪一条命令

建立了一个名为mfcmulview的工程,并在消息处理的总的函数CWnd::WindowProc里面打断点,逐步跟踪。点击“文件保存”按钮,进入了Cwnd::OnWndMsg函数,默认情况下,无论消息还是命令都会在该函数里面进行处理,这里面会区分消息、命令然后进行不同的处理,现在是命令,所以再调用OnCommand函数进行处理。其实可以把“命令”看作一种特殊的消息,无论是点击“文件保存”、“文件打开”所有的命令都是一样的,都是占用一个消息的地址,WM_COMMAND值为0×111。那么命令之间的区分可能是通过wParam和lParam两个参数来进行区分。

 

OnCommand是一个虚函数,现在进入了CMDIFrameWndEx::OnCommand,然后该函数又调用了CMDIFrameWnd::OnCommand。

 第 4 节            消息和命令的不同

命令其实是一种特殊的消息,它的消息编号为:0×111,但是在Windows里面却将命令和消息进行了分开的不同的处理。消息一般只从子类向父类流动,它的处理函数一般是一个虚函数,它的指针一般指向子类,如果子类重载了这个函数,那么一般它先处理消息,然后再调用父类来处理消息。如果子类没有重载这个函数,那么父类来处理这个函数。

而命令有一个奇怪的流程,比如:“文件打开”这个命令,视图View可以处理它、文档Document也可以处理它,命令一般不会传递数据,且View如果处理了这个命令,那么Document就不会得到这个命令了。所以MFC的各个命令你可以选择一个位置如Document、如View来进行这个命令的处理。

第 5 节            消息和事件的关系

在我理解,其实事件就相当于一个函数,它是对消息进行的处理。消息是用户的操作或者说是发生了什么样的改变。一个消息往往会引发出多个事件,比如:遮住的窗口显示出来,这时会重绘,但是重绘会引发出一系列的事件,首先,会调用OnEraseBkgnd,然后再调用OnDraw,所以会引发一系列的改变。或者可能被遮掩的窗口被显示出来后,系统自动发送了一系列的消息。

2013 七月6

MFC的文档视图结构分析

工作日志  0 views  编辑» 0

第 1 节            文档视图结构

MFC能够成为“应用程序框架”,最重要的东西除了巧妙地实现了消息循环,另一个很精华的东西可能就要算文档视图结构了。文档视图结构包含4个东西:文档、视图、框架和文档模板,暂且我们把它称成4元素。其实它是仿照精典的MVC模式,将显示、控制逻辑、数据分离开来。在MFC中,文档存储数据,而View将数据显示出来,一个文档可以有多个视图,而一个视图却只能有一个文档。文档和视图的关系由“文档模板”来进行管理。那么框架的作用是什么呢?框架是用来放视图等元素的。如果要增加一个视图,必须再增加一个框架,然后用文档模板将它们管理起来。

那么一个MFC的应用程序的对象是怎么构成的呢?首先,所有的都是一个theApp对象,这个对象是整个应用程序。theApp对象是CXXXApp的实例化,而CXXXApp的父类是CWinAppEx,CWinAppEx的父类是CWinApp。

第 2 节            框架的产生

4元素中的框架是怎么产生出来的呢?首先,多文档结构可能有多个框架,单文档结构却只有一个框架。无论单文档和多文档结构,它们都有一个主框架m_pMainWnd,这是定义在CWinApp的父类CWinThread里面里的一个指针。当然,如果我们想将单文档结构的应用程序改成多文档的结构,可以添加框架的指针。

 

这个框架的指针是怎么产生对象的呢,是在CXXXApp的InitInstance函数里面被new出来的,如下所示。

 

注意单文档和多文档所继承的框架是不同的,单文档继承的是CFrameWndEx,这是CFrameWnd的子类。单文档的框架下是不能再放子框架的了。

 

而多文档继承的是CMDIFrameWndEx,它是CMDIFrameWnd的子类,它是可以放子框架CMDIChildWndEx类。

 

第 3 节            视图的产生

视图是怎么产生出来的呢,首先,视图的指针定义在CMainFrame里面,所以说视图应该看作框架的一部分。看到m_wndView并不是一个指针,而是直接定义的一个对象。

 

在CMainFrame的函数OnCreate(这个函数在建造主框架的时候调用)的时候调用如下函数:

第 4 节            文档的产生

文档的指针是定义在XXXView的你类CView里面的,这预示着什么呢,预示着一个View会对应一个Document,而且一个View只有一个文档的指针,说明一个View只能对应一个Document(用户当然可以自己加,但是违背设计的目的)。

 

但是它怎么生成一个对象,或者它怎么绑定到一个文档上的呢。调用函数可以将一个视图绑定在一个文档上面:

 

在CView的OnCreate函数里面调用AddView进行视图和文档的绑定的。

 

但是在CView里面有个函数GetDocument可以获得这个文档:

 

第 5 节            文档模板的产生

文档模板的生成是在CXXXApp的InitInstance函数里面实现的,它将文档、视图和框架绑在一起,并管理它们。

 

函数AddDocTemplate是将文档模板加入一个文档模板的列表里面,然后有一套系统里面管理。只是单文档的时候,加入的文档模板是CSingleDocTemplate,而在多文档的时候,加入的文档模板为:CMultiDocTemplate。

第 6 节            文档视图的互访

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值