分享
 
 
 

MFC技术内幕系列之(一)---MFC应用程序“生死因果”内幕

王朝vc·作者佚名  2006-01-08
窄屏简体版  字體: |||超大  

///////////////////////////////////////////////////////////////////////////////////

/********* 文章系列:MFC技术内幕系列***********/

/************MFC技术内幕系列之(一)***********/

/****文章题目:MFC应用程序“生死因果”内幕*****/

/* Copyright(c)2002 bigwhite */

/* All rights Reserved */

/***********关键字:MFC,生死因果**************/

/* 时间:2002.7.23 */

/* 注释:本文所涉及的程序源代码均在Microsoft */

/ Visual Studio.Net Enterprise Architect Edition /

/* 开发工具包提供的源代码中 */

////////////////////////////////////////////////////////////////////////////////////////////////

引言:

侯捷老师在他那本著名的"深入浅出MFC"(第二版)的第六章中对比着传统的Win32API编程,详细讲解了MFC应用程序“生死因果”,而且侯捷老师还在"深入浅出MFC"(第二版)一书的“无责任书评”中称应用程序和MFC Framework的因果关系,是学习MFC程序设计的关键,并把它作为学习MFC程序设计的"第一个台阶".

作为已是“过来人”的我非常赞同侯捷老师的观点,特写下此篇文章以供大家参考,本文章特别对MFC程序设计的初学者大有裨益。

正文:

初学MFC程序设计的人(甚至包括已经很精通Win32API编程的大虾们)都会感到很疑惑,对MFC应用程序的运行流程不能马上领悟,多数人都会提出类似"WinMain函数跑到哪里去了?","窗口函数(WinProc),消息循环好像一下子都消失了?"等问题。下面就让我们看一个MFC SDI应用程序的运行流程并挖掘一下MFC库的源代码,来尽力争取弄清MFC应用程序“生死因果”的内幕。

////////////////////////////////////////////

/* 1. Windows 帮忙 */

/* 程序诞生! */

//////////////////////////////////////////

Windows 操作系统为应用程序创建进程核心对象,并为该应用程序分配4GB的进程地址空间,系统加载器

将应用程序可执行文件映像以及一些必要的代码(包括数据和一些应用程序使用的dlls)加载到应用程序的进程地址空间中。

/////////////////////////////////////////////

/* 2.启动函数是什么? */

/////////////////////////////////////////////

Windows 操作系统在初始化该应用程序进程的同时,将自动为该应用程序创建一个主线程,该主线程与

C/C++运行时库的启动函数一道开始运行。很多初学者并不知道C/C++运行时库的启动函数是何方神圣,这里我

简单介绍一下:当你的应用程序编译后开始链接时,系统的链接器会根据你的应用程序的设置为你的应用程序

选择一个C/C++运行时库的启动函数(注释:这些函数声明在..\Visual Studio.NET\vc7\crt\src\crt0.c中)

一般的ANSI版本的GUI的应用程序的C/C++运行时库的启动函数为:

int WinMainCRTStartup(void);

其它版本的C/C++运行时库的启动函数如下:

ANSI版本的CUI的应用程序: int mainCRTStartup(void);

Unicode版本的CUI的应用程序: int wmainCRTStartup(void);

Unicode版本的GUI的应用程序: int wWinMainCRTStartup(void);

C/C++运行时库的启动函数的主要功能为初始化C/C++运行时库和为所有全局和静态的C++类对象调用构造函数。

/////////////////////////////////////////////////

/* 3.侯捷老师所说的"引爆器" */

/////////////////////////////////////////////////

前面所说的C/C++运行时库的启动函数的主要功能之一是为所有全局和静态的C++类对象调用构造函数。侯捷老师所说的"引爆器"---CMyWinApp theApp这个Application Object就是由启动函数调用其构造函数构造出来的。CWinApp的构造函数到底作了什么?看看源代码吧,源代码最能说明问题了。

注释:CWinApp的构造函数定义在..\Visual Studio.NET\vc7\atlmfc\src\mfc\appcore.cpp

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_hLangResourceDLL = 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

...//

// initialize current printer state

...//

// initialize DAO state

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

// other initialization

...//

}

从源代码中可以看出CWinApp的构造函数主要收集了一些关于应用程序主线程的信息及初始化一些相关应用程序的信息。值得注意的是CWinApp类的一些主要的数据成员如:m_hInstance,m_lpCmdLine,m_pCmdInfo及m_atomApp等都初始化为NULL,这些成员在后面将被重新赋值。

//////////////////////////////////////////////

/* 4. WinMain函数登场了 */

//////////////////////////////////////////////

C/C++运行时库的启动函数int WinMainCRTStartup(void);所调用的WinMain函数---同时也是主线程的入口函数为:

int WINAPI _tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,LPTSTR lpCmdLine

,int nCmdShow);

注释1:该函数定义在..\Visual Studio.NET\vc7\atlmfc\src\mfc\appmodul.cpp中

注释2:_t 是为了照顾Unicode版本而定义的宏。

讲到这个时候你也许会稍稍展开你那紧皱的眉头,不过也许你还会问:"MFC中的WinMain函数到底作了什么?" 其实很简单,看看源代码就知道了。

extern "C" int WINAPI _tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,

LPTSTR lpCmdLine, int nCmdShow)

{

// call shared/exported WinMain

return AfxWinMain(hInstance, hPrevInstance, lpCmdLine, nCmdShow);

}

这一下清楚了,MFC中的WinMain函数其实什么也没做,只是调用了一个函数AfxWinMain。

/////////////////////////////////////////////////

/* 5.MFC程序的入口点函数 */

//////////////////////////////////////////////////

MFC作了一个"乾坤大挪移",将WinMain函数的全部责任转移交给了MFC程序的入口点函数---AfxWinMain。

注释:该函数定义在..\Visual Studio.NET\vc7\atlmfc\src\mfc\winmain.cpp中。

// Standard WinMain implementation

// Can be replaced as long as 'AfxWinInit' is called first

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)

{

TRACE(traceAppMsg, 0, "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)

{

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

AfxGetModuleThreadState()->m_nTempMapLock);

}

AfxLockTempMaps();

AfxUnlockTempMaps(-1);

#endif

AfxWinTerm();

return nReturnCode;

}

从上面源代码可以看出AfxWinMain函数主要由四大模块组成,他们分别是AfxWinInit,InitApplication,

InitInstance,Run。下面将分别介绍这四大模块的功能。

///////////////////////////////////////////////

/* 5.1 AFX的内部初始化 */

///////////////////////////////////////////////

AfxWinInit函数是既CWinApp类构造函数后的又一个重量级的函数。不妨看一下它的源代码:

注释:该函数定义在..\Visual Studio.NET\vc7\atlmfc\src\mfc\appinit.cpp中。

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;

hPrevInstance; // Obsolete.

pApp->m_lpCmdLine = lpCmdLine;

pApp->m_nCmdShow = nCmdShow;

pApp->SetCurrentHandles();

}

// initialize thread specific data (for main thread)

if (!afxContextIsDLL)

AfxInitThread();

// Initialize CWnd::m_pfnNotifyWinEvent

HMODULE hModule = ::GetModuleHandle(_T("user32.dll"));

if (hModule != NULL)

{

CWnd::m_pfnNotifyWinEvent = (CWnd::PFNNOTIFYWINEVENT)::GetProcAddress(hModule, "NotifyWinEvent");

}

return TRUE;

}

还记得我在第三个标题---侯捷老师所说的"引爆器"处 的话么,"CWinApp类的一些主要的数据成员在后面将被重新赋值。",AfxWinInit函数就是这些数据成员被赋值的地方,它重新初始化这些在整个程中都扮演重要角色的成员,并且调用AfxInitThread()为主线程作了一些初始化工作,这些都为以后MFC框架的正常运作铺平了道路。

/////////////////////////////////////////////////

/* 5.2 应用程序的全局初始化 */

/////////////////////////////////////////////////

InitApplication函数(virtual)为程序进行全局初始化:

注释1:该函数定义在..\Visual Studio.NET\vc7\atlmfc\src\mfc\appcore.cpp中。

BOOL CWinApp::InitApplication()

{

if (CDocManager::pStaticDocManager != NULL)

{

if (m_pDocManager == NULL)

m_pDocManager = CDocManager::pStaticDocManager;

CDocManager::pStaticDocManager = NULL;

}

if (m_pDocManager != NULL)

m_pDocManager->AddDocTemplate(NULL);

else

CDocManager::bStaticInit = FALSE;

LoadSysPolicies();

return TRUE;

}

由于初次调用时CDocManager::pStaticDocManager==0x00000000;m_pDocManager==0x00000000;所以InitApplication函数只是调用了CWinApp::LoadSysPolicies();而后者将加载一些注册表的信息用来初始化一些程序定义的结构并为程序注册一些基本信息。(由于该函数可能尚未文档化,所以关于LoadSysPolicies函数的说明只是看了源代码后的推测,下面列出了它的部分源代码仅供参考)

注释2:该函数定义在..\Visual Studio.NET\vc7\atlmfc\src\mfc\appcore.cpp中。

BOOL CWinApp::LoadSysPolicies()

{

HKEY hkPolicy = NULL;

DWORD dwValue = 0;

DWORD dwDataLen = sizeof(dwValue);

DWORD dwType = 0;

// clear current policy settings.

m_dwPolicies = _AFX_SYSPOLICY_NOTINITIALIZED;

static _AfxSysPolicyData rgExplorerData[] =

{

{_T("NoRun"), _AFX_SYSPOLICY_NORUN},

{_T("NoDrives"), _AFX_SYSPOLICY_NODRIVES},

{_T("RestrictRun"), _AFX_SYSPOLICY_RESTRICTRUN},

{_T("NoNetConnectDisconnect"), _AFX_SYSPOLICY_NONETCONNECTDISCONNECTD},

{_T("NoRecentDocsHistory"), _AFX_SYSPOLICY_NORECENTDOCHISTORY},

{_T("NoClose"), _AFX_SYSPOLICY_NOCLOSE},

{NULL, NULL}

};

...//

static _AfxSysPolicyData rgComDlgData[] =

{

{_T("NoPlacesBar"), _AFX_SYSPOLICY_NOPLACESBAR},

{_T("NoBackButton"), _AFX_SYSPOLICY_NOBACKBUTTON},

{_T("NoFileMru"), _AFX_SYSPOLICY_NOFILEMRU},

{NULL, NULL}

};

static _AfxSysPolicies rgPolicies[] =

{

{_T("Software\\Microsoft\\Windows\\CurrentVersion\\Policies\\Explorer"),

rgExplorerData},

{_T("Software\\Microsoft\\Windows\\CurrentVersion\\Policies\\Network"),

rgNetworkData},

{_T("Software\\Microsoft\\Windows\\CurrentVersion\\Policies\\Comdlg32"),

rgComDlgData},

{NULL, NULL}

};

_AfxSysPolicies *pPolicies = rgPolicies;

_AfxSysPolicyData *pData = NULL;

...//

}

注释3:在MFC文档中有这么一句话"The CWinApp::InitApplication member function is obsolete in MFC.",所以你大多情况下不用在意这个virtual函数。

/////////////////////////////////////////////////

/* 5.3 应用程序的标准实例化 */

//////////////////////////////////////////////////

CWinApp::InitInstance()是一个虚函数,大多数应用程序都要override这个函数。让我们看看应用程序向导MFC AppWizard(.exe)为SDI 程序作出的override后的代码吧!

BOOL CMyWinApp::InitInstance()

{

// 如果一个运行在 Windows XP 上的应用程序清单指定要

// 使用 ComCtl32.dll 版本 6 或更高版本来启用可视化方式,

//则需要 InitCommonControls()。否则,将无法创建窗口。

InitCommonControls();

CWinApp::InitInstance();//显式调用基类的InitInstance()

// 初始化 OLE 库

if (!AfxOleInit())

{

AfxMessageBox(IDP_OLE_INIT_FAILED);

return FALSE;

}

AfxEnableControlContainer();

// 标准初始化

SetRegistryKey(_T("应用程序向导生成的本地应用程序"));

LoadStdProfileSettings(4); // 加载标准 INI 文件选项(包括 MRU)

// 注册应用程序的文档模板。

// 文档模板将用作文档、框架窗口和视图之间的连接

CSingleDocTemplate* pDocTemplate;

pDocTemplate = new CSingleDocTemplate(

IDR_MAINFRAME,

RUNTIME_CLASS(CTestDoc),

RUNTIME_CLASS(CMainFrame), // 主 SDI 框架窗口

RUNTIME_CLASS(CTestView));

pDocTemplate->SetContainerInfo(IDR_CNTR_INPLACE);

AddDocTemplate(pDocTemplate);

// 分析标准外壳命令、DDE、打开文件操作的命令行

CCommandLineInfo cmdInfo;

ParseCommandLine(cmdInfo);

if (!ProcessShellCommand(cmdInfo))

return FALSE;

// 唯一的一个窗口已初始化,因此显示它并对其进行更新

m_pMainWnd->ShowWindow(SW_HIDE);

m_pMainWnd->UpdateWindow();

return TRUE;

}

CMyWinApp::InitInstance()先显式调用了基类的InitInstance();

我们先看看这个基类的函数的定义吧!

注释1:该函数定义在..\Visual Studio.NET\vc7\atlmfc\src\mfc\appcore.cpp中。

BOOL CWinApp::InitInstance()

{

InitLibId();

m_hLangResourceDLL = LoadAppLangResourceDLL();

if(m_hLangResourceDLL != NULL)

{

AfxSetResourceHandle(m_hLangResourceDLL);

_AtlBaseModule.SetResourceInstance(m_hLangResourceDLL);

}

return TRUE;

}

注释2:vc.net中的CWinApp::InitInstance()已与vc6.0中的CWinApp::InitInstance()有所区别。

基类的InitInstance()

先 调用InitLibId()函数用于Initializes the data member containing the GUID of the current module;不过该函数现在为空,估计以后微软会填充该函数。

之后调用LoadAppLangResourceDLL()函数加载应用程序所需资源;在vc6.0中的CWinApp::InitInstance()函数只有一条语句:即return TRUE;

CMyWinApp::InitInstance()在其基类的帮助后,开始执行它自己的一系列代码来完成诸如"初始化 OLE 库","设置注册表主键以使程序能保存信息到注册表中","分析标准外壳命令","生成程序主框架,文档和视图结构","显示程序主窗口"等工作。

注释3:有关应用程序是如何在CMyWinApp::InitInstance()完成上面一系列工作的,将在本系列文章之二 的“MFC文档视图结构内幕”一文中详述。

注释4:在MSDN中有关InitInstance的叙述如下:"Windows allows several copies of the same program to run at the same time."

////////////////////////////////////////////

/* 5.4 "消息泵"启动了 */

////////////////////////////////////////////

众所周知,Windows是一个以消息为基础,以事件驱动的操作系统,每一个Win32程序也都是如此。那么

MFC应用程序是如何实现消息机制的呢?MFC应用程序框架将这种消息机制包装到了一个"消息泵"中,而这个"消息泵"在CMyWinApp::InitInstance()中被启动了。其源代码如下:

注释1:该函数定义在..\Visual Studio.NET\vc7\atlmfc\src\mfc\appcore.cpp中。

// Main running routine until application exits

int CWinApp::Run()

{

if (m_pMainWnd == NULL && AfxOleGetUserCtrl())

{

// Not launched /Embedding or /Automation, but has no main window!

TRACE(traceAppMsg, 0, "Warning: m_pMainWnd is NULL in CWinApp::Run - quitting application.\n");

AfxPostQuitMessage(0);

}

return CWinThread::Run();

}

由上面的源代码看出:CWinApp::Run()调用了其基类的Run()函数,继续看源代码:

int CWinThread::Run()

{

ASSERT_VALID(this);

_AFX_THREAD_STATE* pState = AfxGetThreadState();

// for tracking the idle time state

BOOL bIdle = TRUE;

LONG lIdleCount = 0;

// acquire and dispatch messages until a WM_QUIT message is received.

for (;;)

{

// phase1: check to see if we can do idle work

while (bIdle &&

!::PeekMessage(&(pState->m_msgCur), NULL, NULL, NULL, PM_NOREMOVE))

{

// call OnIdle while in bIdle state

if (!OnIdle(lIdleCount++))

bIdle = FALSE; // assume "no idle" state

}

// phase2: pump messages while available

do

{

// pump message, but quit on WM_QUIT

if (!PumpMessage())

return ExitInstance();

// reset "no idle" state after pumping "normal" message

//if (IsIdleMessage(&m_msgCur))

if (IsIdleMessage(&(pState->m_msgCur)))

{

bIdle = TRUE;

lIdleCount = 0;

}

} while (::PeekMessage(&(pState->m_msgCur), NULL, NULL, NULL, PM_NOREMOVE));

}

}

//CWinThread implementation helpers

BOOL CWinThread::PumpMessage()

{

return AfxInternalPumpMessage();

}

BOOL AFXAPI AfxInternalPumpMessage()//部分源码

{

_AFX_THREAD_STATE *pState = AfxGetThreadState();

if (!::GetMessage(&(pState->m_msgCur), NULL, NULL, NULL))

{

...//

return FALSE;

}

...//

// process this message

if (pState->m_msgCur.message != WM_KICKIDLE && !AfxPreTranslateMessage(&(pState->m_msgCur)))

{

::TranslateMessage(&(pState->m_msgCur));

::DispatchMessage(&(pState->m_msgCur));

}

return TRUE;

}

终于出现了::TranslateMessage和::DispatchMessage,熟悉Win32API编程的人一定会眼睛一亮,终于挖出源头了。

/////////////////////////////////////////

/* 6. 收尾工作 */

////////////////////////////////////////

MFC应用程序的主要流程几乎都已被挖掘完了,下面看一下收尾工作是如何进行的。

当应用程序发现消息队列中出现了WM_QUIT消息时, nReturnCode = pThread->Run();CWinApp::Run()返回,并设置返回值。下面将执行AfxWinTerm函数。其源代码如下:

注释1:该函数定义在..\Visual Studio.NET\vc7\atlmfc\src\mfc\appterm.cpp中。

// Standard cleanup called by WinMain and AfxAbort

void AFXAPI AfxWinTerm(void)

{

// unregister Window classes

AFX_MODULE_STATE* pModuleState = AfxGetModuleState();

AfxLockGlobals(CRIT_REGCLASSLIST);

LPTSTR lpsz = pModuleState->m_szUnregisterList;

while (*lpsz != 0)

{

LPTSTR lpszEnd = _tcschr(lpsz, '\n');

ASSERT(lpszEnd != NULL);

*lpszEnd = 0;

UnregisterClass(lpsz, AfxGetInstanceHandle());

lpsz = lpszEnd + 1;

}

pModuleState->m_szUnregisterList[0] = 0;

AfxUnlockGlobals(CRIT_REGCLASSLIST);

// cleanup OLE if required

CWinThread* pThread = AfxGetApp();

if (pThread != NULL && pThread->m_lpfnOleTermOrFreeLib != NULL)

(*pThread->m_lpfnOleTermOrFreeLib)(TRUE, FALSE);

// cleanup thread local tooltip window

AFX_MODULE_THREAD_STATE* pModuleThreadState = AfxGetModuleThreadState();

if (pModuleThreadState->m_pToolTip != NULL)

{

if (pModuleThreadState->m_pToolTip->DestroyToolTipCtrl())

pModuleThreadState->m_pToolTip = NULL;

}

_AFX_THREAD_STATE* pThreadState = AfxGetThreadState();

if (!afxContextIsDLL)

{

// unhook windows hooks

if (pThreadState->m_hHookOldMsgFilter != NULL)

{

::UnhookWindowsHookEx(pThreadState->m_hHookOldMsgFilter);

pThreadState->m_hHookOldMsgFilter = NULL;

}

if (pThreadState->m_hHookOldCbtFilter != NULL)

{

::UnhookWindowsHookEx(pThreadState->m_hHookOldCbtFilter);

pThreadState->m_hHookOldCbtFilter = NULL;

}

}

}

由源代码可以看出:该函数主要作一些清除工作,将该释放的东西释放,比如卸载钩子等。

等到AfxWinTerm函数结束,AfxWinMain函数返回nReturnCode值,且该值也将作为_WinMain函数的返回值返回。

让我们回过头来再看一看C/C++运行时库的启动函数的源代码:

注释2:函数定义在..\Visual Studio.NET\vc7\crt\src\crtexe.c中

int WinMainCRTStartup(void)//部分源代码

{

int argc; /* three standard arguments to main */

_TSCHAR **argv;

_TSCHAR **envp;

int argret;

int mainret;

int managedapp;

#ifdef _WINMAIN_

_TUCHAR *lpszCommandLine;

STARTUPINFO StartupInfo;

#endif /* _WINMAIN_ */

_startupinfo startinfo;

/*

* Determine if this is a managed application

*/

managedapp = check_managed_app();

...//

StartupInfo.dwFlags = 0;

GetStartupInfo( &StartupInfo );

#ifdef WPRFLAG

mainret = wWinMain(

#else /* WPRFLAG */

mainret = WinMain(

#endif /* WPRFLAG */

GetModuleHandleA(NULL),

NULL,

lpszCommandLine,

StartupInfo.dwFlags & STARTF_USESHOWWINDOW

? StartupInfo.wShowWindow

: SW_SHOWDEFAULT);

...//

if ( !managedapp )

exit(mainret);

...//

return mainret;

}

WinMain函数将返回值传给mainret,WinMainCRTStartup调用C运行时函数exit(int status);后者做什么了呢?看看微软自己的文档重视如何说的:

"The exit functions terminate the calling process. exit calls, in last-in-first-out (LIFO) order, the functions registered by atexit and _onexit, then flushes all file buffers before terminating the process.The status value is typically set to 0 to indicate a normal exit and set to some other value to indicate an error."

事实上,exit函数除了做以上工作外,还为所有全局的和静态的C++类对象调用析构函数(如~CMyWinApp),

将返回值传递给Windows操作系统的ExitProcess函数,使得操作系统可以撤销该进程并设置它的exit代码。

//////////////////////////////////////

/* 7. 结束了 */

/////////////////////////////////////

经过这么大篇幅的挖掘,一个MFC应用程序从生到死的流程我们都已目睹完了,相信你一定有所收获,在本文中涉及到一些细节的时候我有时只是一句话代过,因为我将在以后的系列文章中详细讲解,比如:MFC文档视图结构等细节,希望读者能继续关注我的文章。

////////////////////////////////////////

/* 8. "下集预告" */

////////////////////////////////////////

MFC技术内幕系列之(二)---《MFC文档视图结构内幕 》

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有