MFC入口点及初始化分析

本文探讨了MFC程序的入口点和初始化过程,详细分析了从PE文件加载到CWinApp构造函数的执行,解释了_AFX_CMDTARGET_GETSTATE()、CWinThread和CWinApp之间的关系,以及如何通过AfxGetApp()获取应用实例并调用InitInstance()函数。

写的很随意,这是自己在学习及调试过程的随笔吧,希望能给大家带来一些益处。

 

很多人对于MFC的入口点及初始化很迷茫,从而导致学习MFC的畏惧心理(当初和我一起学习的3~4个人基本都放弃了),但愿这篇文章给你以启示。

 

很多人在大学的时候,都被教会程序的入口点是main()函数。但是在main()函数之前有什么操作呢?

这个问题涉及到了程序的加载流程。程序的加载流程大致如下:

加载PE文件的.idata段---->加载PE文件的.data段---->加载代码.text段---->然后执行程序。

 

.idata是PE文件的引入段。PE文件对外部符号的调用在这里做中转。

.date是PE文件数据段(存放全局的变量)。

.test是PE文件的代码段。

 

而对于C++程序来说,如果定义了全局的对象,则要调用该对象类的构造函数。

该对象为全局对象,那么该对象将在.data中定义,因此按照加载流程,会在进入入口点之前先调用该对象类的构造函数。

下面来分析下MFC的入口点。

 

我使用VC6.0新建了一个单文档的工程:MY

 

在MY.CPP中有一个全局对象CMYApp theApp;

按照上边的分析,会调用该对象的构造函数:

CMYApp::CMYApp()
{
 // TODO: add construction code here,
 // Place all significant initialization in InitInstance
}//该处没有任何动作,那么会调用其基类的构造函数CWinApp::CWinApp(LPCTSTR lpszAppName)。

 

那么我们看看CWinApp::CWinApp(LPCTSTR lpszAppName)的定义吧。

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
.........
}

只看其中重要的一部分吧。

pModuleState = _AFX_CMDTARGET_GETSTATE();
pModuleState 保存了MFC中的大量重要信息:
1.CWinApp指针
2.实例句柄
3.资源Module的句柄
4.句柄表
5.OLE相关信息
6.窗口过程
7.Activation Context
8.……

那么_AFX_CMDTARGET_GETSTATE()是怎么回事呢?有以下内容可知

#define _AFX_CMDTARGET_GETSTATE() (AfxGetModuleState())

那么AfxGetModuleState()呢?以下是其定义

AFX_MODULE_STATE* AFXAPI AfxGetModuleState()
{
 _AFX_THREAD_STATE* pState = _afxThreadState;
 AFX_MODULE_STATE* pResult;
 if (pState->m_pModuleState != NULL)
 {
  // thread state's module state serves as override
  pResult = pState->m_pModuleState;
 }
 else
 {
  // otherwise, use global app state
  pResult = _afxBaseModuleState.GetData();
 }
 ASSERT(pResult != NULL);
 return pResult;
}

 

_afxThreadState 是一个全局的变量,由来如下:

在 AFXSTAT_.H 中 EXTERN_THREAD_LOCAL(_AFX_THREAD_STATE, _afxThreadState)

 

#define THREAD_LOCAL(class_name, ident_name) /
 AFX_DATADEF CThreadLocal<class_name> ident_name; //这里便是 _afxThreadState 被定义的地方。
#define EXTERN_THREAD_LOCAL(class_name, ident_name) /
 extern AFX_DATA THREAD_LOCAL(class_name, ident_name)

 

在AFXTLS_.H中定义
template<class TYPE>
class CThreadLocal : public CThreadLocalObject

{}

为了加深你的理解,你可以继续往下追 CThreadLocal 和 CThreadLocalObject 的定义,也比较的简单了,就是new了一个对象。

 

到了这里就要思考一个问题了:那么我们自己定义的对象和程序的入口点有什么关系呢?

哈哈,往上看看CWinApp::CWinApp(LPCTSTR lpszAppName)的定义,其中有两条如下的语句:

 pThreadState->m_pCurrentWinThread = this;
 pModuleState->m_pCurrentWinApp = this;

这两条语句就是我们应用程序和MFC的入口点 AfxWinMain(hInstance, hPrevInstance, lpCmdLine, nCmdShow) 的关键所在了。

 

int AFXAPI AfxWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow)
{
...........
 CWinThread* pThread = AfxGetThread();//线程对象 THRDCORE.CPP:CWinThread* AFXAPI AfxGetThread()
 CWinApp* pApp = AfxGetApp();// 主对象 AFXWIN1.INL:_AFXWIN_INLINE CWinApp* AFXAPI AfxGetApp()

...........

}

 

看到这里应该明白了吧。

在CWinApp::CWinApp(LPCTSTR lpszAppName)中对全局的变量_afxThreadState进行了初始化,然后到了入口点后,就能够通过AfxGetApp()获取到theApp,并使用它了。

pApp->InitApplication();就能够调用我们所建工程MY的BOOL CMYApp::InitInstance()函数了。因为CWinApp::InitInstance()是虚函数。

 

本人也是刚参加工作,在各方面还有很多欠缺,希望没有给你带来误导就好。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值