欢迎光临散文网 会员登陆 & 注册

【滴水基础】5.MFC(1)

2023-03-13 04:09 作者:沙漠里的鲸  | 我要投稿

第一:MFC本质

#MFC介绍(Microsoft Fundation Classes)

---由微软提供的放置Win32Api的,面向对象的包装C++类库

---MFC中大约封装了2000个类,分别封装了WinApiWinSDK中的结构和过程

---另外,MFC提供了1个应用程序框架,例如:程序向导、类向导的自助代码生成,提高了编码效率

#Win32的Api、SDK和MFC区别

---API就是应用程序接口,是由系统提供的一些函数,比如你想创建一个文件,就要调用CreateFile,这个CreateFile就是一个API。
---SDK是指一些公司针对某一项技术为软件开发人员制作的一套辅助开发的工具。一般专指Windows系统提供的相关的头文件LIB文件
---MFC是MS对API的一个封装,也就是一个C++类库,当然MFC比一般类库庞大,所以有人称之为应用程序框架。但其本质还是一个类库

#MFC的本质

---对于Win32Api和Win32SDK的封装

#使用Win32创建窗口(窗口的消息流程)

---1.OS捕获键盘输入,传递给线程消息队列(RegisterClass)

---2.线程不断的获取消息(GetMessage),对获取的消息,进行翻译(TranslateMessage)为字符码

---3.转发(DispachMessage):线程浸入内核,利用窗口句柄将不同的消息分发给各个窗口

---4.窗口调用窗口函数,执行特定的代码

---相关的代码

---窗口创建和消息传递处理的流程

---效果如下:可以放大、拖拽、关闭等

#VC6创建一个MFC窗口,命名HelloMFC

---采用基本对话框

---使用静态链接库,直接把库编译到exe文件

----可以自定义窗口

#总结(利用MFC写一个窗口程序)

---优点:简单、方便;缺点:代码冗余复杂

第二节:第一个MFC程序

#本节需要掌握的知识点:

---1.CWinApp可以覆盖的虚函数InitInstance(在里面创建窗口)

---2.CWinApp成员变量:m_pMainWnd(相当于WinMain函数)

---3.CFramWnd的成员函数:Create以及参数(相当于窗口函数)

#需要简单了解的内容

---1.通过MSDN去看MFC的层次结构图

---2.对CWinApp有初步的认知

---3.对CFramWnd有初步的认知

#MFC的层次结构图(Hierarchy Chart)

#CWinApp(应用程序框架)

---提供了初始化应用程序运行应用程序的成员函数

---使用MFC的每个应用程序只能包含1个WinApp的派生对象,

---当你从WinApp派生应用程序,覆盖InitInstance成员函数以及创建应用程序的主窗口对象

---InitInstance成员函数的成员变量:m_pMainWnd用来记录主窗口对象

---BOOL InitInstance()是MFC的CWinApp类的成员函数,而WinMain才是真正的入口点,但是MFC不允许程序中有WinMain这个函数

---因为MFC自己编写了WinMain函数,如果程序中再定义就重复定义了,

---而MFC编写的的WinMain函数则调用了CWinApp::InitInstance函数,所以InitInstance看起来似乎就是MFC程序的入口点

---除了CWinApp成员函数外,Microsoft基类库提供了以下全局函数

---来访问CWinApp对象和其它全局信息

---1.AfxGetApp:获取一个指向CWinApp对象的指针

---2.AfxGetInstanceHandle获取当前应用程序实例(exe)的句柄

---3.AfxGetResourceHandle获取应用程序资源(dll)的句柄

---4.AfxGetAppName获取指向包含应用程序名称的字符串指针

#总结

---MFC的核心:基于CWinApp的应用程序对象(只能1个)

---CWinApp的派生对象:代表1个程序本体(和程序本身有关,和窗口无关的数据和动作)

#CFrameWnd类


---提供了Windows单文档界面(SDI),重叠或者弹出框架窗口的功能,以及用于管理窗口的成员

---要为应用程序创建窗口,从FrameWnd派生类向派生类添加成员变量,以存储特定的应用程序数据

---在派生类中实现:消息处理程序成员和消息映射(窗口函数

#创建和初始化Windows框架窗口

----CFrameWnd::Create(相当于CreateWindow)

---这里可以只填写LPCTSTR lpszClassName、LPCTSTR lpszWindowName两个参数,其它参数都存在默认值

---如果LPCTSTR lpszClassName=NULL,则以MFC内建窗口类产生一个标准的外框窗口

#创建一个CFrameWnd

#总结

---1,基于MFC的窗口程序,必须也只有1个CWinApp对象

---2.必须覆盖CWinApp的虚函数InitInstance,在里面创建窗口,并把窗口对象保存在InitInstance()函数的成员变量m_pMainWnd

---3.创建窗口是通过CFrameWnd对象,在它的构造函数里面调用成员函数Create

#MFC程序的注意事项

---1.使用Win32Application创建工程(空项目)

---New一个Hello.cpp和Hello.h

---2.使用静态链接库

---3.在Hello.h中写下防止头文件被重复引用,防止被重复编译的宏:

---ifndef 它是if not define的简写,是宏定义的一种:条件编译
---#ifndef可以避免以下错误:如果在.h文件中定义了全局变量,一个C文件包含了.h文件多次

---如果不加#ifndef宏定义,会出现变量重复定义的错误;如果加了#ifndef则不会出现这种错误.

---4.使用头文件afxwin.h

#Hello.h的代码

---Hello.cpp的代码:

---m_pMainWnd相当于窗口句柄,InitInstance相当于WinMain函数

---效果如下

---在UpdateShow()下断点,F5调试,然后单步步入F11

---发现AfxWinMain就是以前的WinMain(),因此:InitInstance就是MFC对于WinMain()的封装

---而且,在AfxWinMain内部,调用了CWinApp的InitInstance()成员函数

#存在的问题

---1.WinMain在哪里? 2.消息循环在哪里? 3.窗口过程函数在哪里?

---关于作业:创建窗口,右侧带滚动条,大小300*300

---可以看到结构体RECT是4个长整型的结构体

第三:MFC的初始化过程1

#全局变量、全局对象总是在任何其它代码之前执行

---发现在main函数之前,a已经被赋值

---全局对象也是一样

---发现构造函数早于main函数执行

#总结

---1.全局对象的构建,会早于程序的入口点

---2.而WinMain又广泛使用了应用程序对象(CWinApp)的InitInstance()方法

---3.所以CWinApp被构建成了全局对象

#模拟MFC的初始化

---CWinApp类的继承关系:CObject > CCmdTarget > CWinThread > CWinApp

---CFrameWnd类的继承关系: CObject > CCmdTarget > CWND > CFrameWnd

---编写一个控制台程序模拟MFC初始化

---注意:可以通过新建类来指定继承关系

---先创建CObject.h

---再创建CObject.cpp

---考虑到要使用cout,创建一个public.h

---再创建CCmdTarget类,并且继承CObject类

---声明CCmdTarget的函数

---以此类推,分别创建CWinThread 和 CWinApp类

---在Mian.cpp中创建全局对象CWinApp

---发现逐级调用了父类的构造函数和析构函数

---CObject > CCmdTarget > CWND > CFrameWnd也是同理

第四:MFC的初始化过程2

#知识点

---MFC如何使用应用程序对象

---CWinApp的2个可以覆盖的虚函数:

---从创建MFC窗口的头文件可以看出

---CMyApp继承CWinApp对象,而CMainWindow继承CFrameWnd对象

---对象的继承图

---依次创建对象的构造函数和析构函数

---在mian()中创建全局对象

---注意:这里只是模拟,WinMain()是由系统调用,和平时程序调用存在本质区别

---WinMain()没有封装在CWinApp类或者其它MFC的类中,因此WinMain()不是其它类的成员

---最后会调用AfxWinMain函数

---WinMain()的本质是AfxWinMain函数

---执行结果如下

--但是,相当于MFC的程序,还缺少

---1.InitInstance()的虚函数

---2.m_pMainWnd的指针(指向CFrameWnd::CMainWindow对象),而m_pMainWnd指针是和InitInstance()同一级或者更上一级的类的成员变量

---查看InitInstance()的类:这里有3个类,但是最大的还是CWinThread类

---同样的m_pMainWnd是CWinThread()的成员

---在CWinThread.h中定义纯虚函数InitInstance()和m_pMainWnd指针

---注意包含WND.h

---在CMyApp.h中重定义虚函数,

---在CMyApp.cpp中重写虚函数,并且new一个CMainWindow()对象(注意包含)

----在CMainWindow类里面重新定义一个方法

---然后在MainWindow.cpp中打印Create,并且在构造函数时调用Create

---修改主函数,在WinMain()里面调用

---1.WinMain()之前,全局对象的初始化会调用:

CObject > CCmdTarget > CWinThread > CWinApp > CMyApp

---2.进入WinMain()之后,创建CMainWindow对象

CObject > CCmdTarget > CWnd > CFrameWnd > CMainWindow > Create

---3.最后调用的析构函数(奇怪:CMainWindow系列的父类的析构函数没有调用)

第五:MFC运行时类型识别:

#什么是RTTI(Runtime Type Information)

---运行时类型信息程序,能够使用父类的指针或者引用,来检查这些指针或者引用所指向的对象的实际派生类

---帮助我们在实际程序运行时,判断某个对象是否属于某个类


---在项目设置里面选择C++语言,勾选允许RTTI

---RTTI需要包含头文件<typeinfo.h>

---typeid运算符用来获取一个表达式的类型信息,

---typeid 会把获取到的类型信息保存到一个 type_info 类型的对象里面,

---并返回该对象的常引用;当需要具体的类型信息时,可以通过成员函数来提取

#static关键字

---相当于一个全局变量,独立于该类的任何对象(和类关联,但是和类的任何对象不关联)

---不能在类的构造函数中声明(初始化),可以不需要创建对象,就使用类的static成员变量/方法

#const关键字

---定义一个只能初始化一次(不可以改变)的变量

---如果是类里面const,直接在构造函数进行声明

---如果是static const,就在类外面进行声明

---在之前的HelloMFC项目中,在Hello.h中,加入宏:DECLARE_DYNAMIC,在Hello.cpp中加入宏:IMPLEMENT_DYNAMIC

---和typeid类似,MFC也存在IsKindOf()函数来检测:

---(1)对象是否属于指定的类,(2)对象是否属于指定类派生的类

---在Hello.h中

---在Hello.cpp中:

---下断点发现i=1,说明CWinApp是CMyApp的父类(父子类关系)

---查看DECLARE_DYNAMIC()的宏定义

---定义了一个静态常量CRuntimeClass结构体,名为class##class_name

---定义了一个返回CRuntimeClass指针的GetRuntimeClass()的虚函数,并且该函数的成员方法/变量都是常量(无法更改)

---AFX_DATA 实际是定义为空的,也许在MFC内部编译里有用,可以别管它

---##表示连接符,可以进行字符的拼接,如class##CMyApp就是classCMyApp

---#表示字符串化,如#CMyApp就是"CMyApp"

----CRuntimeClass:类型记录链表结构:

---对于MFC中每个CObject派生类来说,都有一个相关的CRuntimeClass结构体,在程序运行时可以访问该结构体获取对象及其基类的运行时信息

---在运行时确定一个对象的类型是很重要的,尤其是在做类型检查时;而C++语言本身并不支持运行时类信息

---每一个类拥有这样一个CRuntimeClass成员变量,并且有一定的命名规则(在类名称之前冠以“class”作为它的名称)

---然后通过某种手段将整个类库构建好,“类别型录网”能呈现类似的风貌

---这里的class_name就是CMyApp

---1.定义一个classCMyApp的CRuntimeClass结构体的静态常量

---2.定义一个获取当前类的CRuntimeClass结构体地址的指针

---在Hello.cpp中查看声明classCMyApp和重写虚函数的宏:

IMPLEMENT_DYNAMIC(CMyApp,CWinApp)

---这里是另外一个宏:

IMPLEMENT_RUNTIMECLASS(class_name, base_class_name, 0xFFFF, NULL)

---跟进去查看这个宏

---AFX_COMDAT是一种描述,用来控制编译用的,可以去掉

---将这个宏转移到Hello.cpp中

---这里的RUNTIME_CLASS也是一个宏目的是获取指定类的CRuntimeClass地址

---替换之后的Hello.cpp

#总结

---在头文件中定义当前类的CRuntimeClass静态常量结构体classCMyApp,并且定义获取当前类的CRuntimeClass指针的虚函数GetRuntimeClass()

---初始化classCMyApp的值(指明类名、父类CRuntimeClass指针等)

---重写GetRuntimeClass()虚函数, 返回当前CRuntimeClass结构体的指针

#在int i = IsKindOf(((CRuntimeClass*)(&CWinApp::classCWinApp)));下断点单步调试

---这里会删除以下为了防止报错的代码

---注意:这里pClass是父类的CRuntimeClass指针

---pClassThis是自己当前类的CRuntimeClass指针

---this是当前类CMyApp的指针

---进入IsDerivedFrom函数,这里的pBaseClass就是父类CWinApp的CRuntimeClass结构体地址

---这里的pClassThis就是当前类结构体的CRuntimeClass地址

---进入while循环:如果当前类的this(当前类的CRuntimeClass地址)地址,不等于指定对象的CRuntimeClass地址

---就在当前类的CRuntimeClass基础上,向上遍历父类的CRuntimeClass地址,直到父类遍历完,m_pBaseClass指向NULL时退出循环(成功返回TRUE)

#作业:让CMianWindow支持RTTI,自己写函数打印父类的CRuntimeClass

---头文件

---遇到几个问题:1.MFC里面不能使用printf函数

---2.想用m_pMainWnd->GetBaseRuntimeClass()调用,发现失败(不知道是const限制还是我不能使用virtual,没有办法只能在构造函数里面调用)

---3.这里调用CRuntimeClass指针的时候,直接pClassThis->m_pBaseClass,VC提示的成员变量是基于动态链接库的,我们这里是静态链接库

---4.如果要输出LPCSTR,需要使用cout和for循环,或者printf("%s")

第六:MFC六大核心机制:动态创建

#什么是动态创建

---MFC的动态创建和C++的new几乎没有区别,但是回避了C++不让如下语句的缺点

---编译器不知道className是一个变量名,而不是类名,从className里面构造的对象可能是错误的

#面对对象之永久保存

---把内存里面的东西,写入到文件里面

---MFC永久保存:数据存储入文件,读出之后,根据文件的记录,new一个对象

#类型记录链表结构(CRuntimeClass)

---在CFrameWnd中,存在宏:DECLARE_DYNCREATE

---在Hello.h中

---在Hello.cpp中:这里的逻辑是:变量class_name,根据class_name获取到该类的结构体CRuntimeclass,然后根据CRuntimeClass里面的CreateObject()创建该类的对象

---在MFC里面,根据宏,来创建当前类或者父类的对象(前提是该类存在CRuntimeclass结构体)

---查看DECLARE_DYNCREATE(CMainWindow)

---本质上是上一节的DECLARE_DYNAMIC

---PASCAL 就是_stdcall调用约定,在DECLARE_DYNAMIC的 基础上,创建了一个静态函数,并且返回CObject*

---查看IMPLEMENT_DYNCREATE

---查看IMPLEMENT_RUNTIMECLASS宏(这个就是IMPLEMENT_DYNMIC)

---因此,在Hello.h中

---在Hello,cpp中

#总结

第七:MFC的六大核心机制:消息映射

#MFC的消息映射

---消息映射是MFC的内建的一个消息分配机制,利用数个宏固定形式的写法

---类似于填表格,就可以让框架知道,一旦消息产生

---该往哪一个类进行传递每一个类只能拥有一个消息映射表(也可以没有)

#Win32的消息机制

---1.创建窗口对象,然后初始化窗口对象的背景、类名、句柄、窗口程序(结合WinMain)

---2.将窗口类和OS关联(鼠标操作,OS传递消息给线程)

---3.线程(WinMain)获取消息(GetMessage),然后翻译(TranslateMessage)消息为虚拟码(类似ASCII)才能被识别

---4.分发(DispachMessage)消息给OS,OS根据msg结构中的hWnd窗口句柄,找到相应的窗口类,然后根据注册窗口时wndclass类结构找到相应的窗口函数WndPorc()

---相关的Win32代码

#MFC的消息处理

---在Hello.h定义消息的处理函数

---在Win32里面是每个窗口设置消息处理函数,而在MFC中,在类中定义的消息和对应的消息处理函数

---在Hello,cpp中,在固定的宏里面,实现消息类型,然后根据消息类型设定消息处理函数

---发现打印了内容,并且弹出了消息盒子


---在CMainWindow::OnPaint()中,需要明确设备对象设备上下文图形对象

---画图的代码如下:设备对象就是窗口(窗口句柄为空就是桌面),根据窗口句柄获取设备上下文

---自定义图形对象,然后和设备上下文关联,然后通过设备上下文进行画图

---绘制图形如下(这个只能显示一次,而窗口是不停的绘制、渲染)

---这也是为什么上面MFC要利用dc对象来进行画图

---查看声明DeCLARE_MESSAGE_MAP()

---定义了2个静态常量,声明2个结构体AFX_MSGMAP_ENTRY和AFX_MSGMAP

---虚函数:GetMessageMap(),获取messageMap的地址

---AFX_MSGMAP_ENTRY

----AFX_MSGMAP(查看静态链接库)

---查看BEGIN_MESSAGE_MAP和END_MESSAGE_MAP()

---查看ON_WM_LBUTTONDOWN():根据消息类型,指定消息响应函数

---查看AfxSig_vwp(指明了消息响应函数的:返回类型、参数列表)

---查看ON_WM_PAINT()

---汇总起来,在Hello.h中

---private是完全私有的,只有当前类中的成员能访问到;protected是受保护的,只有当前类的成员与继承该类的类才能访问

---声明了2个结构体AFX_MSGMAP_ENTRY和AFX_MSGMAP,以及获取AFX_MSGMAP结构体地址的虚函数

---注意:afx_msg就是消息响应函数的返回类型

---Hello.cpp

---每一个类都有一个私有的AFX_MSGMAP_ENTRY结构体,里面存储着:不同类型的消息所对应的消息处理函数、消息ID等

---然后每一个类存在一个保护的类型的结构体AFX_MSGMAP,这个结构体2个指针分别指向父类的AFX_MSGMAP,和自己的AFX_MSGMAP_ENTRY

---因此,我们可以通过AFX_MSGMAP获取自己和父类的AFX_MSGMAP_ENTRY

---AFX_MSGMAP_ENTRY结构体的类型是{{消息结构体1},{消息结构体2},{空消息结构体}}

---注意:消息结构体里面的消息响应函数的参数、返回类型和命名是固定的

#MFC的三大类消息

#MFC处理消息的原理

---MFC内部存在一个窗口过程处理函数

---根据MessageMap链表找到对应的消息函数AFX_MSGMAP_ENTRY

---MessageMap存在2个指针,pBaseMap指向父类的AFX_MSGMAP结构体,最终指向CCmdTarget::messageMap

---lpEntries指向自己的AFX_MSGMAP_ENTRY结构体,在结构体内存在不同消息的:消息ID、消息响应函数等

---因此:一个支持消息映射的类(标准消息),必须继承CCmdTarget

#作业

---新建一个类,继承于CMainWind(CNewWnd),给CMainWind添加鼠标左键点击事件,创建新的CNewWnd窗口,新窗口添加鼠标左键点击事件,弹出MessageBox

---在Hello.h中,声明新建的类CNewWnd,并且声明消息映射和消息响应函数

---在hello.cpp中定义CNewWnd的指针,来进行新窗口的创建

---创建效果如下

第八:MFC六大核心机制:命令的传递

#MFC命令传递

---消息会按照规定的路线,游走于各个对象之间,直到找到它的消息处理函数

---如果找不到,就交给DefWindowPro函数处理


---在Create(NULL,"主窗口")下断点

---单步进入CreateEx

---CREATESTRUCT结构体用来存储创建窗口的参数

---单步步入预窗口创建:PreCreateWindow(cs),设置窗口的风格,并且注册(根据所属的类来进行定义窗口的风格)

---单步步入AfxDeferRegisterClass

---AfxHookWindowCreate(this)函数分析(this是CWnd类地址)

---_AfxCbtFilterHook函数分析

---在D:\VC6.0\VC98\MFC\SRC路径下WINCORE.CPP文件

---在里面将Win32里面的窗口过程处理函数替换成afxWndProc

#总结MFC创建窗口流程

---1.判断创建的窗口是否存在菜单,如果存在则加载菜单

---2.获取窗口类的类名,根据类名调用CreateEx()函数

---3.声明CREATESTRUCT结构体cs,用来存储Win32中CreateWindow()的参数

---4.调用PreCreateWindow(cs)函数,判断窗口类名是否为空(是否默认的窗口对象)

---5.如果是默认的窗口类,就调用AfxDeferRegisterClass函数,初始化窗口类,根据不同的类来设置窗口的风格、背景和如果没有消息响应的默认窗口过程函数,最后调用AfxRegisterClass()注册窗口类

---6.在调用CreateWindowEx创建窗口之前,先调用AfxHookWindowCreate(this)下钩子,也就是说,在DispatchMessage之后,AfxHookWindowCreate(this)最先受到消息

---7.通过SetWindowsHookEx安装WH_CBT的过滤函数,Windows系统在进行窗口操作(最大化、最小化窗口)执行之前,调用这个_AfxCbtFilterHook过滤函数

---8._AfxCbtFilterHook将Win32里面的窗口过程处理函数替换成afxWndProc

---在上一节的作业的消息响应函数这里下一个断点

---首先进入的是消息响应函数的调用格式

---WindowProc中,调用OnWndMsg()对消息进行处理

---如果处理失败,调用DefWindowProc()对消息进行默认处理

---执行完WindowProc之后,进入AfxCallWndProc()的

---执行完AfxCallWndProc()之后,进入AfxWndProc(真正的窗口过程处理函数)

---DispatchMessage之后,AfxWndProc最先接受到消息

---本质是调用AfxCallWndProc函数,把消息送给CWnd类或其派生类的对象

---该函数主要是把消息和消息参数(nMsg、wParam、lParam)传递给MFC窗口对象的成员函数WindowProc(pWnd->WindowProc)作进一步处理。

---如果是WM_INITDIALOG消息,则在调用WindowProc前后要作一些处理。

#总结MFC的窗口过程(消息响应函数)

---每一个“窗口类”都有自己的窗口过程,使用该“窗口类”创建的窗口都使用它的窗口过程

---MFC的窗口对象在创建窗口时,也使用已经注册的“窗口类”(使用应用程序提供的窗口过程),AfxWndProc或AfxWndProcBase(动态链接库)(本质是AfxCallWndProc)

---窗口创建最终是通过调用CWnd::CreateEx函数完成的(根据窗口类名调用),CreateEx函数流程如下

---在创建窗口之前,创建了一个WH_CBT类型的钩子(Hook)。这样,创建窗口时所有的消息都会被钩子过程函数_AfxCbtFilterHook截获

---AfxCbtFilterHook函数首先检查是不是希望处理的 Hook──HCBT_CREATEWND。如果是,则先把MFC窗口对象和刚刚创建的Windows窗口对象捆绑在一起,建立它们之间的映射(见后面模块-线程状态);

---然后,调用::SetWindowLong设置窗口过程为AfxWndProc,并保存原窗口过程在窗口类成员变量m_pfnSuper中,这样形成一个窗口过程链。需要的时候,原窗口过程地址可以通过窗口类成员函数GetSuperWndProcAddr得到。

---AfxWndProc就成为CWnd或其派生类的窗口过程。不论队列消息,还是非队列消息,都送到AfxWndProc窗口过程来处理

---Windows消息送给AfxWndProc窗口过程之后,AfxWndProc得到HWND窗口对应的MFC窗口对象

---然后,搜索该MFC窗口对象和其基类的消息映射数组,判定它们是否处理当前消息,如果是则调用对应的消息处理函数,否则,进行缺省处理

#MFC中每一个窗口,都对应了一个窗口类

---在这个窗口产生的消息,CWnd或者派生类的对象调用OnWndMsg搜索本对象或者基类的消息映射数组messageMap,寻找当前消息的消息处理函数(在_messageEntries数组里面)

---在OnWndMsg搜索本对象对应的消息映射数组,找到对应的消息响应函数

#总结

---在MFC中,每一个窗口都对应一个MFC类(继承于CCmdTarget)

---通过CWinApp的派生类,重写InitInstance方法(相当于WinMain入口)

---在InitInstance中,创建CFrameWnd对象(子类也可以),进而调用CFrameWnd的构造函数

---在构造函数中,调用Create()创建MFC窗口(区别于CreateWindow),本质是调用CreateEx

---CreateEx通过结构体CREATESTRUCT传递定义窗口类的参数,然后调用PreCreateWindow(cs)进行窗口对象的自定义,以及窗口类的注册

---调用AfxHookWindowCreate(this)在窗口创建前,创建了一个WH_CBT类型的钩子(Hook),所有的消息都会被钩子过程函数_AfxCbtFilterHook截获

---_AfxCbtFilterHook,则先把MFC窗口对象(该对象必须已经创建了)和刚刚创建的Windows窗口对象捆绑在一起建立它们之间的映射(将窗口类::WindowProc的消息处理过程和MFC窗口进行关联)

---然后调用::SetWindowLong设置将Win32里面的窗口过程为AfxWndProc(本质是AfxCallWndProc

---AfxWndProc就成为CWnd或其派生类的窗口过程。而经过消息分发之后没有被处理的消息,将送给原窗口过程处理。

---Windows消息送给AfxWndProc窗口过程之后(Win32是OS直接DispatchMessage给窗口句柄),AfxWndProc得到HWND窗口对应的MFC窗口对象

---在MFC类的窗口过程函数中AfxWndProc,调用OnWndMsg()对消息进行过滤识别,如果是标准消息,就搜索该MFC窗口对象和其基类的消息映射数组,判定它们是否处理当前消息,如果是则调用对应的消息处理函数,否则,进行缺省处理

#再次分析窗口过程处理函数AfxWndProc的本质AfxCallWndProc(注意:这个顺序应该是反着来)

---步骤3:注意每一个CWnd对象的派生类(MFC窗口对象)都存在函数

---序员可以在CWnd的派生类中覆盖它,改变MFC分发消息的方式。例如,MFC的CControlBar就覆盖了WindowProc,对某些消息作了自己的特别处理,其他消息处理由基类的WindowProc函数完成

---在当前例子中,当前对象的类CTview没有覆盖该函数,所以CWnd的WindowProc被调用。---这个函数把下一步的工作交给OnWndMsg函数来处理。如果OnWndMsg没有处理,则交给DefWindowProc来处理

---OnWndMsg和DefWindowProc都是CWnd类的虚拟函数。

---步骤2

---步骤1:

#总结

---DispatchMessage之后,MFC窗口对象::AfxWndProc接受到消息,调用 AfxCallWndProc

---AfxCallWndProc根据接到的消息,调用消息处理函数CWnd::WindowProc

---WindowProc函数调用OnWndMsg遍历父子类的messageMap结构体,找到对应消息的响应过程函数

#为什么要使用消息映射

#MFC消息传递总结


【滴水基础】5.MFC(1)的评论 (共 条)

分享到微博请遵守国家法律