一、MFC的结构
MFC(Microsoft Foundation Class Library)是微软公司开发的一组C++类库,用于开发Windows图形用户界面(GUI)应用程序。MFC的主要组成包括以下几个方面:
-
应用程序框架(Application Framework):包括CWinApp类、CFrameWnd类、CMDIFrameWnd类、CDialog类等,用于创建应用程序的主窗口、菜单、工具栏、状态栏等界面元素,以及处理消息、事件等应用程序逻辑。
-
控件库(Control Library):包括CButton类、CEdit类、CListCtrl类、CTreeCtrl类、CComboBox类等,用于创建各种常见的界面控件,例如按钮、文本框、列表框、树形控件、下拉框等。
-
文档视图模型(Document/View Architecture):包括CDocument类、CView类、CScrollView类、CFormView类等,用于实现文档视图模型架构,即将数据模型(Document)和用户界面(View)分离,使得数据和界面可以独立地进行操作和管理。
-
数据库支持(Database Support):包括CRecordset类、CDatabase类、CDataSource类等,用于实现对关系型数据库的访问和操作,例如打开数据库连接、执行SQL语句、读取和写入数据等。
-
ActiveX控件支持(ActiveX Control Support):包括COleControl类、COleControlSite类、COleDispatchDriver类等,用于实现ActiveX控件的开发和使用,例如创建和嵌入ActiveX控件、调用控件的方法和属性等。
MFC的主要组成部分可以帮助开发者快速构建Windows图形用户界面应用程序,并且提供了丰富的功能和工具,使得开发过程更加高效和简便。
一个标准的MFC程序外貌
二、 MFC程序的启动与关闭顺序
普通win32程序的执行顺序
MFC程序的执行顺序
那么 ,现在来经历一个MFC程序的完整生命过程
1、创建Application theApp
第一件事情就是找出MFC程序的进入点。MFC程序也是Windows程序,所以它应该也有一个WinMain,,但是我们在程序看不到它的踪影。是的,但先别急,在程序进入点之前,还有一个(而且仅有一个)全局对象(本例名为hApp),这是所谓的application object,当操作系统将程序加载并激活时,这个全局对象获得配置,其构造函数会先执行,比WinMain更早。所以以时间顺序来说,我们先看看这个application object。
下图所示,创建一个MFC工程,在MFCApplication1.cpp中有个全局变量CMFCApplication1App theApp;这个全局对象一产生,便执行其构造函数。CWinApp之中的成员变量将因为theApp这个全局对象的诞生而获得配置与初值。
2、WinMain登场
用SDK编程序时,程序的入口点是WinMain函数,而在MFC程序里我们并没有看到WinMain函数,原来它是被隐藏在MFC代码里面了。当theApp配置完成后,WinMain登场。细看程序,并没看到WinMain函数的代码啊!在MFC程序中,微软把WinMain函数封装起来,在MFC中是看不到的,代码实际上是在安装目录中,我的是在“C:\Program Files (x86)\Microsoft Visual Studio\2017\Enterprise\VC\Tools\MSVC\14.16.27023\atlmfc\src\mfc\appmodul.cpp”,原来它在appmodul.cpp里面,好,我们就认为当theApp配置完成后,程序就转到appmodul.cpp来了。那执行什么呢?看看下面从appmodul.cpp摘出来的代码:
extern "C" int WINapi
_tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow)
{
// call shared/exported WinMain
return AfxWinMain(hInstance, hPrevInstance, lpCmdLine, nCmdShow);
}
_tWinMain函数的“_t”是为了支持Unicode而准备的一个宏。
_tWinMain函数返回值是AfxWinMain函数的返回值,AfxWinMain函数定义于winmain.cpp第21行,稍加整理,去芜存菁,就可以看到这个“程序进入点”主要做些什么事:
int AFXAPI AfxWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow)
{
int nReturnCode = -1;
CWinApp* pApp = AfxGetApp();
AfxWinInit(hInstance, hPrevInstance, lpCmdLine, nCmdShow);
pApp->InitApplication();
pApp->InitInstance()
nReturnCode = pApp->Run();
AfxWinTerm();
return nReturnCode;
}
3、AfxWinInit——AFX内部初始化操作
AfxWinInit是继CWinApp构造函数之后的第一个操作,主要做的是AFX内部初始化操作,该函数定义于APPINIT.CPP第24行,
BOOL AFXAPI AfxWinInit(HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPTSTR lpCmdLine, int nCmdShow)
{
ASSERT(hPrevInstance == NULL);
// handle critical errors and avoid Windows message boxes
SetErrorMode(SetErrorMode(0) |
SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
// set resource handles
AFX_MODULE_STATE* pModuleState = AfxGetModuleState();
pModuleState->m_hCurrentInstanceHandle = hInstance;
pModuleState->m_hCurrentResourceHandle = hInstance;
// fill in the initial state for the application
CWinApp* pApp = AfxGetApp();
if (pApp != NULL)
{
// Windows specific initialization (not done if no CWinApp)
pApp->m_hInstance = hInstance;
pApp->m_hPrevInstance = hPrevInstance;
pApp->m_lpCmdLine = lpCmdLine;
pApp->m_nCmdShow = nCmdShow;
pApp->SetCurrentHandles();
}
// initialize thread specific data (for main thread)
if (!afxContextIsDLL)
AfxInitThread();
return TRUE;
}
4、执行CWinApp::InitApplication
AfxWinInit之后的操作是pApp->InitApplication,我们已知道pApp指向CMyWinApp对象,当调用:
pApp->InitApplication();
相当于调用:
CMyWinApp::InitApplication();
但是你要知道,CMyWinApp继承自CWinApp,而InitApplication又是CWinApp的一个虚拟函数,我们并没有改写它(大部分情况下不需改写它),所以上述操作相当于调用:
CWinApp::InitApplication();
此函数定义于APPCORE.CPP第125行,你自己搜出来看吧!我就不搬出来了,里面的操作都是MFC为了内部管理而做的(其实我也看不懂,知道有这回事就好了)。
5、执行CWinApp::InitInstance
继InitApplication函数之后,AfxWinMain调用pApp->InitInstance。当程序调用:
pApp->InitInstance(); 相当于调用: CMyWinApp::InitInstance();
但是你要知道,CMyWinApp继承自CWinApp,而InitInstance又是CWinApp的一个虚拟函数。由于我们改写了它,所以上述操作就是调用我们自己(CMyWinApp)的这个InitInstance函数。
6、CFrameWnd::Create产生主窗口(并先注册窗口类)
在已经来到CWinApp::InitInstance了,该函数先new一个CMyFrameWnd对象,从而产生主窗口。在创建CMyFrameWnd对之前,要先执行构造函数CMyFrameWnd::CMyFrameWnd(),该函数用Create函数产生窗口:
CMyFrameWnd::CMyFrameWnd()
{
Create(NULL, "Hello MFC", WS_OVERLAPPEDWINDOW, rectDefault, NULL, "MainMenu");
}
7、窗口显示与更新
CMyFrameWnd::CMyFrameWnd结束后,窗口已经诞生出来;程序流程又回到CMyWinApp::InitInstance,于是调用ShowWindow函数令窗口显示出来,并调用UpdateWindow函数令程序送出WM_PAINT消息。在SDK程序中,消息是通过窗口函数来处理,而现在窗口函数在哪里、又如何送到窗口函数手中呢?那要从CWinApp::Run说起了。
8、执行CWinApp::Run
在执行完CMyWinApp::InitInstance函数后,程序的脚步到了AfxWinMain函数的pApp->Run了,现在我们已知道这将执行CWinApp::Run函数,该函数定义于APPCORE.CPP第391行,下面是程序代码:
int CWinApp::Run()
{
if (m_pMainWnd == NULL && AfxOleGetUserCtrl())
{
// Not launched /Embedding or /Automation, but has no main window!
TRACE0("Warning: m_pMainWnd is NULL in CWinApp::Run - quitting application.n");
AfxPostQuitMessage(0);
}
return CWinThread::Run();
}
9、把消息与处理函数连接在一起——Message Map机制
到此,主窗口已经产生,等待的就是各种消息了,然后调用相应的处理函数,然而消息和处理函数怎样连接在一起呢?MFC采用了Message Map机制(消息映射机制),提供给应用程序使用的“很方便的接口”的两组宏,这里就不细讲了。
所以,总的调用结构就是
三、 总结
参考: