⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 03.2.1 mfc程序中的winmain函数(2).txt

📁 网上第一本以TXT格式的VC++深入详解孙鑫的书.全文全以TXT格式,并每一章节都分了目录,清晰易读
💻 TXT
字号:
接下来,把全局变量a换成一个全局对象,看看结果如何。修改如例3-3所示的代码,新定义一个CPoint类,并定义该类的一个全局变量pt,结果如例3-4所示。

例3-4

1.#include <iostream.h>

 

2.//int a=6;

 

3.class CPoint

4.{

5.public:

6.CPoint()

7.{

8.}

9.};

 

10.CPoint pt;

11.void main()

12.{

13.//  cout<<a<<endl;

14.}

设置三个断点:CPoint构造函数处(第6行代码处)、pt全局对象定义处(第10行代码处)和main函数定义处(第12行代码处)。选择调试运行main函数,将会看到程序代码执行的先后顺序。这时我们将发现main程序首先到达pt全局对象定义处(第10行代码处);继续运行程序,程序到达CPoint类的构造函数(第6行代码处);再继续运行程序,程序到达main函数处(第12行代码处)。由此可见,无论全局变量,还是全局对象,程序在运行时,在加载main函数之前,就已经为全局变量或全局对象分配了内存空间。对一个全局对象来说,此时就会调用该对象的构造函数,构造该对象,并进行初始化操作。

至此,读者应该明白了先前创建的Test程序的运行顺序,也就是为什么全局变量theApp的构造函数会在WinMain函数之前执行了。那么,为什么要定义一个全局对象theApp,让它在WinMain函数之前执行呢?该对象的作用是什么呢?

先关闭main工程,返回Test程序,并使其处于编辑状态。在前面介绍Win32 SDK应用程序时,曾经讲过应用程序的实例是由实例句柄(WinMain函数的参数hInstance)来标识的。而对MFC程序来说,通过产生一个应用程序类的对象来惟一标识应用程序的实例。每一个MFC程序有且仅有一个从应用程序类(CWinApp)派生的类。每一个MFC程序实例有且仅有一个该派生类的实例化对象,也就是theApp全局对象。该对象就表示了应用程序本身。

我们在第2章中阐述了子类构造函数的执行过程,当一个子类在构造之前会先调用其父类的构造函数。因此theApp对象的构造函数CTestApp在调用之前,会调用其父类CWinApp的构造函数,从而就把我们程序自己创建的类与Microsoft提供的基类关联起来了。CWinApp的构造函数完成程序运行时的一些初始化工作。

下面让我们看看CWinApp类构造函数的定义。像前面搜索“WinMain”函数那样,找到Microsoft提供的CWinApp类定义的源文件:appcore.cpp,并在编辑环境中打开,其中CWinApp构造函数的代码如例3-5所示。

例3-5

CWinApp::CWinApp(LPCTSTR lpszAppName)

{

    if (lpszAppName != NULL)

        m_pszAppName = _tcsdup(lpszAppName);

    else

        m_pszAppName = NULL;

 

    // initialize CWinThread state

    AFX_MODULE_STATE* pModuleState = _AFX_CMDTARGET_GETSTATE();

    AFX_MODULE_THREAD_STATE* pThreadState = pModuleState->m_thread;

    ASSERT(AfxGetThread() == NULL);

    pThreadState->m_pCurrentWinThread = this;

    ASSERT(AfxGetThread() == this);

    m_hThread = ::GetCurrentThread();

    m_nThreadID = ::GetCurrentThreadId();

 

    // initialize CWinApp state

    ASSERT(afxCurrentWinApp == NULL); // only one CWinApp object please

    pModuleState->m_pCurrentWinApp = this;

    ASSERT(AfxGetApp() == this);

 

    // in non-running state until WinMain

    m_hInstance = NULL;

    m_pszHelpFilePath = NULL;

    m_pszProfileName = NULL;

    m_pszRegistryKey = NULL;

    m_pszExeName = NULL;

    m_pRecentFileList = NULL;

    m_pDocManager = NULL;

    m_atomApp = m_atomSystemTopic = NULL;

    m_lpCmdLine = NULL;

    m_pCmdInfo = NULL;

 

    // initialize wait cursor state

    m_nWaitCursorCount = 0;

    m_hcurWaitCursorRestore = NULL;

 

    // initialize current printer state

    m_hDevMode = NULL;

    m_hDevNames = NULL;

    m_nNumPreviewPages = 0;     // not specified (defaults to 1)

 

    // initialize DAO state

    m_lpfnDaoTerm = NULL;   // will be set if AfxDaoInit called

 

    // other initialization

    m_bHelpMode = FALSE;

    m_nSafetyPoolSize = 512;        // default size

}

上述CWinApp的构造函数中有这样一句代码:

pModuleState->m_pCurrentWinApp = this;

根据C++继承性原理,这个this对象代表的是子类CTestApp的对象,即theApp。同时,可以发现CWinApp的构造函数有一个LPCTSTR类型的形参:lpszAppName。但是我们程序中CTestApp的构造函数是没有参数的。在第2章介绍C++编程知识时,曾经介绍,如果基类的构造函数带有一个形参,那么子类构造函数需要显式地调用基类带参数的构造函数。那么,为什么我们程序中的CTestApp构造函数没有这么做呢?

我们知道,如果某个函数的参数有默认值,那么在调用该函数时可以传递该参数的值,也可以不传递,直接使用默认值即可。我们可以在例3-5所示代码中的CWinApp类名上单击鼠标右键,利用【Go to Definition of CWinApp】命令,定位到CWinApp类的定义处,代码如例3-6所示。

例3-6

class CWinApp : public CWinThread

{

    DECLARE_DYNAMIC(CWinApp)

public:

 

// Constructor

    CWinApp(LPCTSTR lpszAppName = NULL);     // app name defaults to EXE name

 

……

从例3-6所示代码中,可以看到CWinApp构造函数的形参确实有一个默认值(NULL)。这样,在调用CWinApp类的构造函数时,就不用显式地去传递这个参数的值。

2.AfxWinMain函数
当程序调用了CWinApp类的构造函数,并执行了CTestApp类的构造函数,且产生了theApp对象之后,接下来就进入WinMain函数。根据前面例3-1所示代码,可以发现WinMain函数实际上是通过调用AfxWinMain函数来完成它的功能的。

 知识点  Afx前缀的函数代表应用程序框架(Application Framework)函数。应用程序框架实际上是一套辅助我们生成应用程序的框架模型。该模型把多个类进行了一个有机的集成,可以根据该模型提供的方案来设计我们自己的应用程序。在MFC中,以Afx为前缀的函数都是全局函数,可以在程序的任何地方调用它们。

我们可以采取同样的方式查找定义AfxWinMain函数的源文件,在搜索到的文件中双击WINMAIN.CPP,并在其中找到AfxWinMain函数的定义代码,如例3-7所示。

例3-7

int AFXAPI AfxWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,

    LPTSTR lpCmdLine, int nCmdShow)

{

    ASSERT(hPrevInstance == NULL);

 

    int nReturnCode = -1;

①  CWinThread* pThread = AfxGetThread();

    CWinApp* pApp = AfxGetApp();

 

    // AFX internal initialization

    if (!AfxWinInit(hInstance, hPrevInstance, lpCmdLine, nCmdShow))

        goto InitFailure;

 

    // App global initializations (rare)

②  if (pApp != NULL && !pApp->InitApplication())

        goto InitFailure;

 

    // Perform specific initializations

③  if (!pThread->InitInstance())

    {

        if (pThread->m_pMainWnd != NULL)

        {

            TRACE0("Warning: Destroying non-NULL m_pMainWnd\n");

            pThread->m_pMainWnd->DestroyWindow();

        }

        nReturnCode = pThread->ExitInstance();

        goto InitFailure;

    }

④  nReturnCode = pThread->Run();

 

InitFailure:

#ifdef _DEBUG

    // Check for missing AfxLockTempMap calls

    if (AfxGetModuleThreadState()->m_nTempMapLock != 0)

    {

        TRACE1("Warning: Temp map lock count non-zero (%ld).\n",

            AfxGetModuleThreadState()->m_nTempMapLock);

    }

    AfxLockTempMaps();

    AfxUnlockTempMaps(-1);

#endif

 

    AfxWinTerm();

    return nReturnCode;

}

在例3-7所示的代码中,AfxWinMain首先调用AfxGetThread函数获得一个CWinThread类型的指针,接着调用AfxGetApp函数获得一个CWinApp类型的指针。从MFC类库组织结构图(读者可以按照前面介绍的方法在MSDN中找到该结构图)中,可以知道CWinApp派生于CWinThread。例3-8是AfxGetThread函数的源代码,位于THRDCORE.CPP文件中。

例3-8

CWinThread* AFXAPI AfxGetThread()

{

    // check for current thread in module thread state

    AFX_MODULE_THREAD_STATE* pState = AfxGetModuleThreadState();

    CWinThread* pThread = pState->m_pCurrentWinThread;

 

    // if no CWinThread for the module, then use the global app

    if (pThread == NULL)

        pThread = AfxGetApp();

 

    return pThread;

}

从例3-8所示代码中可以发现,AfxGetThread函数返回的就是AfxGetApp函数的结果。因此,AfxWinMain函数中的pThread和pApp这两个指针是一致的。

AfxGetApp是一个全局函数,定义于AFXWIN1.INL中:

_AFXWIN_INLINE CWinApp* AFXAPI AfxGetApp()

    { return afxCurrentWinApp; }

而afxCurrentWinApp的定义位于AFXWIN.H文件中,代码如下:

#define afxCurrentWinApp    AfxGetModuleState()->m_pCurrentWinApp

我们返回去看看前面例3-5所示的CWinApp构造函数代码,就可以知道AfxGetApp函数返回的是在CWinApp构造函数中保存的this指针。对Test程序来说,这个this指针实际上指向的是CTestApp的对象:theApp。也就是说,对Test程序来说,pThread和pApp所指向的都是CTestApp类的对象,即theApp全局对象。

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -