1.前言
在前面第九章Windows编程入门我们讲到了一个最基本的Window
窗口程序的书写。
经历过这个过程的我们可以很明显的发现,我们写了很多的代码也才只完成了一个最简单的窗口制作,这极大延长了程序员开发软件的周期。
最重要的是,这些窗口开发流程代码基本都是固定的!
而这就是框架的作用,将底层实现的细节进行封装,让我们可以专心实现应用逻辑。
本文要介绍的MFC这是这样一个框架,只需要你拖一拖、点一点,不用敲一行代码,就能生成一个窗口。
之所以要学MFC,主要是因为它是微软官方推出的产品,在VS中可以很容易使用,很方便我们开发一些小软件.
而且它封装了大量的win API
,但封装的又不是很彻底,可以让我们了解win API
编程的原理。
根据我过去数年的经验来说,MFC适合用来学习了解WIndows系统机制,以及用来开发一些外挂软件的界面比较方便。
如果你想要做出一款如同QQ、微信这类面向大众的软件,那么MFC框架一定不是你的首选,因为它真的很难做出很好看的界面,更加现代化的electron、tauri等前端框架或许是你所需要的。
本站同样拥有它们的相关教程,感兴趣的可以阅读:
2.安装MFC
在VS中安装MFC非常简单,直接进入vs,点击获取“工具和功能”:
然后会来到安装页面,将MFC选上:
然后等待安装完成即可。
3.初步使用MFC
还是老规矩,在解决方案中新建一个项目,不过这次我们就需要选择MFC程序了:
输入项目名day11--MFC
后,会出现下面这种界面:
一般我们都是使用基于对话框的MFC。
当然也还有其它选项,比如基于单个文档,多个文档,多个顶层文档等等,这些基本都是用于开发那种大型软件的框架,比如word
这种,一般我们个人开发用不着。
而且如果真要开发这种商业级的大型软件,如今一般也不会用MFC了,更可能会使用Qt等框架。
因为一但MFC本身出现问题,那基本是无解的,因为它并不开源。
所以如今看起来就感觉很鸡肋,至少我基本没用过这些,都是用基于对话框的MFC写一些小工具,同时里面自动生成的代码相对比较简单,可以更好的入门与理解。
还有下面那个在静态库中使用MFC,还有另一个选项是在DLL中使用MFC,分别就是上一章讲过的静态库与动态库。
如果使用动态库,那么你的软件想要发给别人使用,就还得找到dll
文件,放到exe
文件一起。
而静态库生成后,只需要拿着exe
文件就可以直接用,不过体积会稍微大一点。
其它的选项就没什么必要了,默认即可,来到下一个界面,因为我们不使用文档模板,所以直接来到第三个界面:
这个界面的东西就很好理解了,根据自己需要随意勾选即可。
- 系统菜单:就是当你右键任意一个窗口的最上边,弹出的那个菜单。
- 关于框:多生成一个显示软件信息的窗口。
- 标题:窗口左上角显示的文字。
- 最大最小化:窗口右上角的最大最小化按钮,上面三个框是显示样式的,一般默认即可,可以自己试一试有什么区别。
然后来到下一页:
这些东西,基本就默认即可不用管,以后当你了解多了windows编程的相关知识,自然就懂了各个选项的意义,继续来到下一页:
这里是将要自动生成的两个类,可以自行更改类名,文件名等等,不过一般都是默认即可。
这两个类一个是App
类,代表了我们这整个程序,另一个Dlg
类,是主窗口类,代表我们运行程序最开始看到的那个窗口。
然后点击完成,等等自动生成默认的MFC程序即可,生成完毕后,首先展示的就是我们的主窗口,先不管这么多,运行一下试一试,别忘了将该项目设置为启动项:
我们到目前为止,一行代码都没有敲,就完成了一个窗口的显示,对比前面我们使用代码显示一个窗口,是不是相当的简单!
但同时也带来了麻烦,那就是这个项目自动生成了相当多的代码,我们必须得先看懂这些代码,才能自己在这个已有的窗口基础上改造。
4.概览MFC代码
首先来看一下给我们生成了哪些代码文件:
pch
,framework
就不用说了,前面提到过,前者是预编译文件,后者是框架文件,一般用于包含一些特殊宏和头文件的。
4.1 App
然后就是这个直接以我们项目名为文件名的day11--MFC
,先看头文件,看一下它大致干了些什么事情:
可以看到,它包含了resource.h
这个头文件,其作用我们后面讲:
#include "resource.h" // 主符号
然后就定义了一个类,其类名就是去除我们项目名中的符号得到day11MFC
,前面加C
,表示类(class
),后面加了app
,表示应用程序(application
),整体意思是这是一个应用程序类:
class Cday11MFCApp : public CWinApp
它继承自CWinApp
这个类,看名字,就知道这是一个windows
程序类,win
一般为windows
简写。
这是一个MFC程序的核心类,通俗一点说,这个类里面就写了我们前面提到过的入口函数winmain
,只不过被这个类封装了,我们看不到,所以这个类是一个MFC程序所必需的。
然后就是一个构造函数,这个就不用多说了:
Cday11MFCApp();
接着这个类里面写了一个函数InitInstance
,看前面的修饰符virtual
就知道这是一个虚函数,前面应该提到过,这是父类写好的函数,让子类继承,使用了类的多态特性:
virtual BOOL InitInstance();
只不过前面我们只在父类的函数前面加virtual
,其实子类实现父类的虚函数时,也可以加上,方便我们识别这是一个虚函数!
这个函数的名字意思也很明显,就是初始化实例:Initialize instance
它的作用是当这个程序启动时,就会调用这个函数里面的内容,用于初始化这个程序的一些数据。
这和构造函数的作用很类似,不过它并不是构造函数,虽说它的主要用法也是将我们所需要初始化的东西放在这个函数里面。
紧接着就是一个宏DECLARE_MESSAGE_MAP
,其后面带有括号,这是宏的进阶用法,不要认为它是一个函数:
DECLARE_MESSAGE_MAP()
有兴趣的可以看看这篇文章:宏,但说实话我很少用到,因为用宏写代码,一旦出错很难查找出问题。
它的内部代码我们就不讨论了,这里只谈它的作用,看名字就知道了,就是在声明消息映射。
也就是我们前面写窗口程序时那个非常重要的消息循环,但我们完全不用管它的实现原理,那是MFC的事情。
然后最后似乎还在类的外面实例化了一个本类theApp
,并且添加了extern
关键字:
extern Cday11MFCApp theApp;
这里注意一下,extern
关键字上一章我们用到过,用于函数前面的告诉编译器这是用C
语言写的函数,但那只是它的一个作用。
其最主要的作用就是这里这种用法,告诉编译器后面申明的这个变量在代码中是存在的,只是不在这里,请去其它代码文件里面找找。
所以这里的变量theApp
其实并不是真的实例化了一个变量,它只是告诉编译器这个变量存在于代码中,但不在这里,下面的源码文件介绍我们会看到其真正的实例化变量。
现在来到源码文件:
首先是包含了一些头文件,pch.h
前面说过,一旦项目中启用了预编译,那么每一个源文件中都必须包含它:
#include "pch.h"
至于这里包含framework.h
,看上去似乎是没有必要的,因为pch.h
中就已经包含了该头文件,可能是有什么考虑在内,这里就不纠结了。
然后就包含了该类的头文件,以及另一个生成的文件day11--MFCDlg.h
,这个文件的作用后面再讲:
#include "day11--MFC.h"
#include "day11--MFCDlg.h"
然后就是定义了一些宏,意思就是如果定义了_DEBUG
,那么就把new
定义为DEBUG_NEW
,而DEBUG_NEW
又被定义为了更复杂的东西:
#ifdef _DEBUG
#define new DEBUG_NEW
#endif
注意,一旦遇到这种问题,只要不影响程序运行的情况下,最好就是不管,在VS中当然就很简单了,就是不断右键,然后速览定义就可以找到源头:
// Memory tracking allocation
void* AFX_CDECL operator new(size_t nSize, LPCSTR lpszFileName, int nLine);
可以看到,返回值为void*
,表示返回的无定义的内存地址,AFX_CDECL
定义为__cdecl
,这是一种编译规定,这里就不再细究,有兴趣的可以搜一搜。
然后看到operator
,这不是前面我们提到过的运算符重载吗,咋后面跟着new
?