MFC程序的theApp对象

来源:互联网 发布:招工软件 编辑:程序博客网 时间:2024/06/09 19:10

    每次创建一个MFC应用程序,不管是Doc/View类型的,还是对话框类型的,都会发现MFC会自动为我们创建一个CXXXApp的类,其中XXX表示我们的工程名。其实很多时候,我们都不会用到这个类,也不会在其中添加属性和方法,而是创建我们自己的类。那么MFC为什么会生成这个类呢?下面我以简单的MFC对话框程序为例子,谈一下自己的理解。

    利用Visual Studio 2010创建一个MFC对话框应用项目Test,VisualStudio会为我们创建CTestApp、CTestDlg两个类。很好理解CTestDlg这个类,它就是我要创建的对话框的类,可以在资源管理器中查看并编辑对话框的属性,以及在CTestDlg中重写对话框的初始化函数、处理对话框消息等。但是CTestApp是干什么的呢?

    CTestApp中其实就定义了一个构造函数和一个InitInstance函数,构造函数也基本上没有做任何事情,所以InitInstance应该是最重要的。打开InitInstance函数,其中最重要的功能就是定义一个对话框CTestDlg的对象实例,然后显示这个对话框。无论是Win32程序还是Console程序,都会有一个入口函数(主函数),WinMain或是main,那么在生成的Test项目中,我们根本找不到主函数的踪影,那是不是MFC程序不需要入口函数呢?肯定不是。

    Win32程序的WinMain函数中,需要注册窗口类、创建窗口后,Windows窗口才能够显示出来。CTestApp继承自MFC的CWinApp类,CWinApp中定义了InitApplication、InitInstance两个方法,其中,InitApplication是针对每个应用程序执行一次的初始化函数,其中就包括注册窗口类、设置窗口的窗口函数等属性。InitInstance函数式针对每个程序实例执行一次的初始化函数,但它并没有创建窗口,而是通过虚函数的形式将创建窗口的操作交由子类CTestApp去实现。通过CWinApp我们不难看出,实际上,MFC将Win32程序的初始化操作封装到CWinApp中去实现了。

    那么WinMain函数呢?通过单步调试,可以在appmodul.cpp文件中发现_tWinMain函数,它就是WinMain的函数。_tWinMain函数会调用AfxWinMain函数,在AfxWinMain函数中可以发现CWinApp的踪迹了,它通过AfxGetApp获得一个CWinApp的指针,然后调用它的InitApplication以及InitInstance函数。由于通过CWinApp的指针调用的都是虚函数,所以我们无法断定它究竟调用的哪一个具体类的实现,这就要通过AfxGetApp函数才能知道了。AfxGetApp的调用路径如下:

    AfxGetApp ——> afxCurrentWinApp ——> AfxGetModuleState()->m_pCurrentWinApp ——> _afxThreadState->m_pModuleState

    _afxThreadState是由宏EXTERN_THREAD_LOCAL(class_name, ident_name)传入参数_AFX_THREAD_STATE和_afxThreadState定义的一个全局的外部变量:extern CThreadLocal<class_name> ident_name;,即_afxThreadState是一个CThreadLocal对象。该类有一个成员变量pModuleState指向AFX_MODULE_STATE对象,而AFX_MODULE_STATE的成员就包括一个CWinApp指针。那么这个CWinApp指针是什么时候设置的呢?

    由于_afxThreadState是一个全局对象,所以它必定会在WinMain函数执行前就已经构造出来了。研究CWinApp的代码发现,在它的构造函数中,会通过_AFX_CMDTARGET_GETSTATE宏调用AfxGetModuleState函数,它得到的就是一个_afxThreadState对象,之后,CWinApp通过pModuleState->m_pCurrentWinApp = this;语句将这个全局对象的成员指向了自己。

    接下来的问题是,什么时候构造了CWinApp对象呢?MFC在工程一创建时就定义了CTestApp类,继承自CWinApp,并定义了全局变量CTestApp theApp,由于theApp也是全局对象,所以它的构造也必定是在WinMain函数之前完成的。这样,在theApp构造时会调用父类CWinApp的构造函数,将全局CWinApp指针指向了自己。

    由于虚函数的特性,WinMain中通过CWinApp指针调用的InitApplication和InitInstance函数,实际上是由CTestApp实现的,而CTestApp只继承了InitInstance函数,所以InitApplication仍然是CWinApp的默认实现。之后就如我们所见,CTestApp的InitInstance函数中,创建对话框并显示对话框了。通过改写InitInstance函数,我们就能够创建不同的窗口并显示了。

    因此,theApp对象的作用是事关全局的,它起到了初始化整个应用程序的功能,并将所有复杂的操作都通过MFC框架封装起来,而我们只需要在InitInstance函数中去创建自己关心的窗口就好了。

本文出自 “Jsl_mes” 博客,请务必保留此出处http://jslmes.blog.51cto.com/5008224/1159391

0 0
原创粉丝点击