您的位置:首页 > 移动开发

MFC的模块状态:从AfxGetApp()和AFX_MANAGE_STATE()看MFC的模块状态

2009-08-30 19:39 429 查看
本文转自CSDN博客:http://blog.csdn.net/ATField/archive/2007/03/04/1520379.aspx


作者:张羿

1.

Introduction

当我们在用
MFC
编程的时候,我们经常用到
AfxGetApp()
来获得当前的
CWinApp

Instance
。看看
MFC
的源代码中
AfxGetApp()
的实现,你会发现
AfxGetApp()
的实现并不像一般情况下面那样直接:

_AFXWIN_INLINE CWinApp* AFXAPI AfxGetApp()

{ return afxCurrentWinApp; }

#define afxCurrentWinApp
AfxGetModuleState()->m_pCurrentWinApp

AfxGetApp()
调用的是
AfxGetModuleState()
,该函数返回一个
AFX_MODULE_STATE
的指针,其中的一个成员保存着当前的
CWinApp
的指针。可
AfxGetModuleState()
的作用又是什么呢?

此外,当我们在开发
MFC DLL
程序的时候,我们会在每个输出的
DLL
函数前面加上一句
AFX_MANAGE_STATE


void SomeMFCDllFunction()

{

AFX_MANAGE_STATE(AfxGetStaticModuleState())



AFX_MANAGE_STATE
又是起什么作用呢?从字面上看来,它是
Manage
某种
State
,而
AfxGetStaticModuleState
又是获得
State
的,那么
State
究竟是什么呢?


MFC
中,
States
用来保存某种相关的状态信息,分为下面几类:

1.

Process State
,和某个单独的进程绑定起来

2.

Thread State
,和某个单独的线程绑定

3.

Module State
,和
Module
相关

前两种
State
和一般的全局变量十分类似,只是根据需求的不同被绑定于不同的进程
/
线程,如多线程支持等。而
Module State
本身比较特别,
Module State
根据情况的不同,可以是全局,线程,或者进程相关的
State
,并且可以根据要求快速切换。

2.

Process State

常见的
Process State
有:

1.

_AFX_WIN_STATE

2.

_AFX_DB_STATE

3.

_AFX_DEBUG_STATE

4.

_AFX_SOCK_STATE

5.

……

从字面上面可以很容易猜出这些状态的用处。

MFC
通过下面的宏来定义
Process State:

#define PROCESS_LOCAL(class_name, ident_name) /

AFX_COMDAT CProcessLocal<class_name> ident_name;

#define EXTERN_PROCESS_LOCAL(class_name, ident_name) /

extern CProcessLocal<class_name> ident_name;

PROCESS_LOCAL

CProcessLocal
模板类定义了一个
CProcessLocal<class_name>
的一个实例作为状态变量,而
EXTERN_PROCESS_LOCAL
则使在头文件中声明此状态变量。
CProcessLocal
的定义如下:

class AFX_NOVTABLE CProcessLocalObject

{

public:

// Attributes

CNoTrackObject* GetData(CNoTrackObject* (AFXAPI* pfnCreateObject)());

// Implementation

CNoTrackObject* volatile m_pObject;

~CProcessLocalObject();

};

template<class TYPE>

class CProcessLocal : public CProcessLocalObject

{

// Attributes

public:

AFX_INLINE TYPE* GetData()

{

TYPE* pData = (TYPE*)CProcessLocalObject::GetData(&CreateObject);

ENSURE(pData != NULL);

return pData;

}

AFX_INLINE TYPE* GetDataNA()

{ return (TYPE*)m_pObject; }

AFX_INLINE operator TYPE*()

{ return GetData(); }

AFX_INLINE TYPE* operator->()

{ return GetData(); }

// Implementation

public:

static CNoTrackObject* AFXAPI CreateObject()

{ return new TYPE; }

};

CProcessLocal
的作用只是一个
Wrapper

Hold
一个
TYPE*
的指针,一旦用户调用
GetData
来获得这个指针,
GetData
会首先判断该指针是否为空,如果为空,则创建一个新的实例保存起来,否则返回已有的指针。前提条件是,
TYPE
必须从
CNoTrackObject
继承。任何从
CNoTrackObject
继承的类都拥有自己的
new/delete
,这样此对象便不会被
Debug
的内存分配系统所跟踪而误判为
Leak


CNoTrackObject* CProcessLocalObject::GetData(

CNoTrackObject* (AFXAPI* pfnCreateObject)())

{

if (m_pObject == NULL)

{

AfxLockGlobals(CRIT_PROCESSLOCAL);

TRY

{

if (m_pObject == NULL)

m_pObject = (*pfnCreateObject)();

}

CATCH_ALL(e)

{

AfxUnlockGlobals(CRIT_PROCESSLOCAL);

THROW_LAST();

}

END_CATCH_ALL

AfxUnlockGlobals(CRIT_PROCESSLOCAL);

}

return m_pObject;

}

3.

Thread State


Process State
类似,
Thread State
和某个线程绑定起来,
Thread State
有:

1.

_AFX_THREAD_STATE

2.

_AFXCTL_AMBIENT_CACHE

同样的,
Thread State
是被
THREAD_LOCAL

EXTERN_THREAD_LOCAL
定义,也有
CThreadLocal

CThreadLocalObject

Hold

Thread State
的指针。
CThreadLocal

CProcessLocal
的实现方式不太一样,
CThreadLocal
利用
TLS(Thread Local Storage)
来保存指针,而不是用成员变量。简单来说,
Thread Local Storage

Windows
支持的功能,可以在任意线程中保存多个
DWORD
数据,每个这样的
DWORD
数据所占的位置称之为
Slot
,分配数据需要分配一个
Slot
,获得和修改数据
CThreadLocalObject::GetData
的实现如下:

CNoTrackObject* CThreadLocalObject::GetData(

CNoTrackObject* (AFXAPI* pfnCreateObject)())

{

ENSURE(pfnCreateObject);

if (m_nSlot == 0)

{

if (_afxThreadData == NULL)

{

_afxThreadData = new(__afxThreadData) CThreadSlotData;

ENSURE(_afxThreadData != NULL);

}

m_nSlot = _afxThreadData->AllocSlot();

ENSURE(m_nSlot != 0);

}

CNoTrackObject* pValue = static_cast<CNoTrackObject*>(_afxThreadData->GetThreadValue(m_nSlot));

if (pValue == NULL)

{

// allocate zero-init object

pValue = (*pfnCreateObject)();

// set tls data to newly created object

_afxThreadData->SetValue(m_nSlot, pValue);

ASSERT(_afxThreadData->GetThreadValue(m_nSlot) == pValue);

}

return pValue;

}

CThreadLocalObject::GetData
首先判断
m_nSlot
,如果
m_nSlot == 0
,说明该
Thread State
未曾分配,
GetData
函数将会使用
_afxThreadData->AllocSlot
函数分配一个新的
TLS

Slot
,保存在
m_nSlot
之中,然后调用
GetThreadValue
检查
pValue
是否为
NULL
,如果是,则创建一个新的对象然后调用
SetValue

pValue
设置到该
Slot
之中。
_afxThreadData
的类型为
CThreadSlotData
,是对
TLS API
的一个简单的封装。

_AFX_THREAD_STATE
是一个很常用的
Thread State
,每个
Thread
,都会有自己的一份
_AFX_THREAD_STATE

MFC
提供了一个函数
AfxGetThreadState
来获得当前进程的
Thread State
,如果当前的线程还没有
Thread State
,该函数会创建一个新的
Thread State


_AFX_THREAD_STATE* AFXAPI AfxGetThreadState()

{

_AFX_THREAD_STATE *pState =_afxThreadState.GetData();

ENSURE(pState != NULL);

return pState;

}

_AFX_THREAD_STATE
中保存着下列信息:

1.

当前的
m_pModuleState
,每个线程都知道它当前的
Module State
,这个信息被用来获得当前的
Module State

AfxGetModuleState
正是这么做的:

AFX_MODULE_STATE* AFXAPI AfxGetModuleState()

{

_AFX_THREAD_STATE* pState = _afxThreadState;

ENSURE(pState);

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();

}

ENSURE(pResult != NULL);

return pResult;

}

2.

之前的
m_pModuleState
,用来保存之前的
Module State
,用于
Module State
切换,可参考
AFX_MANAGE_STATE

3.

其他信息,具体可以参考
_AFX_THREAD_STATE
的定义

4.

Module State

Module State
保存着和
Module
相关的状态信息。
Module

Windows
的术语,代表任何一个可执行的代码文件,
EXE

DLL
都是
Module
的一种。
Module State
有下面几种:

1.

AFX_MODULE_STATE
,保存
MODULE
的信息,是
_AFX_BASE_MODULE_STATE

_AFX_DLL_MODULE_STATE
的基类

2.

_AFX_BASE_MODULE_STATE
,保存
MFC Module
的状态信息,没有定义其他的成员

3.

_AFX_DLL_MODULE_STATE
,保存
DLL
的状态信息,没有定义其他的成员

4.

AFX_MODULE_THREAD_STATE
,保存主线程的有关状态信息,虽然
AFX_MODULE_THREAD_STATE
是保存的线程的状态信息,但是它只保存
Module
的主线程的状态信息,所以可以看作是
Module State
的一种。

这些
Module State
保存了
MFC
中的大量重要信息:

1.

CWinApp
指针

2.

实例句柄

3.

资源
Module
的句柄

4.

句柄表

5.

OLE
相关信息

6.

窗口过程

7.

Activation Context

8.

……

4.1

AFX_MODULE_STATE

AFX_MODULE_STATE
的定义如下:

// AFX_MODULE_STATE (global data for a module)

class AFX_MODULE_STATE : public CNoTrackObject

{

public:

#ifdef _AFXDLL

AFX_MODULE_STATE(BOOL bDLL, WNDPROC pfnAfxWndProc, DWORD dwVersion,

BOOL bSystem = FALSE);

#else

explicit AFX_MODULE_STATE(BOOL bDLL);

#endif

~AFX_MODULE_STATE();

CWinApp* m_pCurrentWinApp;

HINSTANCE m_hCurrentInstanceHandle;

HINSTANCE m_hCurrentResourceHandle;

LPCTSTR m_lpszCurrentAppName;

// ……
其他成员,从略

};

可以看到:

1.

AFX_MODULE_STATE

CNoTrackObject
继承。
CNoTrackObject
定义了自己的
new/delete
保证自己不会被各种调试版本的
new/delete

Track
,以免自己被错误的当作
Leak


2.

AFX_MODULE_STATE

DLL
和非
DLL
(也就是
EXE
)的情况下具有不同的构造函数(和成员)

3.

AFX_MODULE_STATE
在成员中保存了一些和
Module
相关的重要信息

实际上,
AFX_MODULE_STATE
并没有被直接使用,而是作为
_AFX_BASE_MODULE_STATE

_AFX_DLL_MODULE_STATE
的基类:

_AFX_BASE_MODULE_STATE
被用于
Module
,其定义如下:

class _AFX_BASE_MODULE_STATE : public AFX_MODULE_STATE

{

public:

#ifdef _AFXDLL

_AFX_BASE_MODULE_STATE() : AFX_MODULE_STATE(TRUE, AfxWndProcBase, _MFC_VER)

#else

_AFX_BASE_MODULE_STATE() : AFX_MODULE_STATE(TRUE)

#endif

{ }

};

PROCESS_LOCAL(_AFX_BASE_MODULE_STATE, _afxBaseModuleState)

_AFX_DLL_MODULE_STATE

_AFX_BASE_MODULE_STATE
类似,只是仅用于
DLL


class _AFX_DLL_MODULE_STATE : public AFX_MODULE_STATE

{

public:

_AFX_DLL_MODULE_STATE() : AFX_MODULE_STATE(TRUE, AfxWndProcDllStatic, _MFC_VER)

{ }

};

static _AFX_DLL_MODULE_STATE afxModuleState;

这两个
class
都没有定义额外的成员,比较简单,只是传入到基类
AFX_MODULE_STATE
的参数不同。此外,他们定义的方式不太一样,前者使用的是
PROCESS_LOCAL
宏,定义了一个变量
_afxBaseModuleState
。后者只是简单的定义了一个
static
变量
afxModuleState


下面这些函数可以用来获得
Module

State


1.

AfxGetModuleState

AfxGetModuleState
首先获得
_afxThreadState

m_pModuleState
,如果当前的
Thread State

m_pModuleState
返回
NULL
,说明当前的
Thread State
没有正确的初始化(通常的原因是创建线程的时候调用的是
CreateThread
函数而非
AfxBeginThread
),则使用
_afxBaseModuleState


AFX_MODULE_STATE* AFXAPI AfxGetModuleState()

{

_AFX_THREAD_STATE* pState = _afxThreadState;

ENSURE(pState);

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();

}

ENSURE(pResult != NULL);

return pResult;

}

_afxBaseModuleState
是用
PROCESS_LOCAL
定义的:

PROCESS_LOCAL(_AFX_BASE_MODULE_STATE, _afxBaseModuleState)

它代表整个
MFC Module

State
。当你的程序是动态链接到
MFC DLL
的时候,该
State
只有一份。如果你的程序是静态链接到
MFC
的话,有几个模块(
EXE/DLL
)静态链接到
MFC

MFC
的代码就有几份,那么
_afxBaseModuleState
也就有几份。

2.

AfxGetStaticModuleState

AfxGetStaticModuleState
在不同的
Project
下面有着不同的行为:在
DLL
项目中,
AfxGetSaticModuleState
返回
afxModuleState
,也就是定义好的
_AFX_DLL_MODULE_STATE
,而在非
DLL
项目中,
AfxGetStaticModuleState
直接调用
AfxGetModuleState
。可以看到,在
DLL
的情况下,必须使用
AfxGetStaticModuleState
才可以获得
DLL
本身的
Module State


#ifdef _AFXDLL

static _AFX_DLL_MODULE_STATE afxModuleState;

AFX_MODULE_STATE* AFXAPI AfxGetStaticModuleState()

{

AFX_MODULE_STATE* pModuleState = &afxModuleState;

return pModuleState;

}

#else

AFX_MODULE_STATE* AFXAPI AfxGetStaticModuleState()

{

AFX_MODULE_STATE* pModuleState = AfxGetModuleState();

return pModuleState;

}

#endif

3.

AfxGetAppModuleState

AfxGetAppModuleState
是最简单的,直接返回
_afxBaseModuleState


AFX_MODULE_STATE* AFXAPI AfxGetAppModuleState()

{

return _afxBaseModuleState.GetData();

}

从上面的讨论可以看出,当前处于那个
MFC Module
的状态之中,返回的就是那个
MFC Module
所相关联的
CWinApp
对象。如果你有多个
Module
都是动态链接到
MFC DLL
的话,那么
AfxGetAppModuleState
返回的总是同一个
CWinApp


5.

AFX_MANAGE_STATE

AFX_MANAGE_STATE
的作用切换到指定的
Module State
,当出了作用域的时候将
Module State
恢复到原来的值。是在不同的
Module State
之中切换,原因有
2


1.

在不同的
MFC DLL

MFC EXE

Module State
之间切换,保持正确的
AFX_MODULE_STATE
,最常见的问题是在
DLL
输出的函数之中无法获得
DLL
本身相关的资源,这就是没有正确维护
Module State
的原因造成的,因为当前
Resource DLL
的句柄就保存在
Module State
之中。

2.

切换
Activation Context
,不同的
Module
必然有着不同的
Activation Context
,需要切换。这是属于
Side By Side
的内容,以后我会专门写一篇文章来讲述
Side By Side

manifest
的相关信息。

一般的用法如下:

void SomeMFCDllFunction()

{

AFX_MANAGE_STATE(AfxGetStaticModuleState())



注意这里使用的是
AfxGetStaticModuleState
,而非
AfxGetModuleState
。原因是在
DLL
项目中,
AfxGetStaticModuleState
返回的是
DLL
本身的
Module State
,而
AfxGetModuleState
则是返回当前线程相关的
Module State
,由于一般
DLL
输出的函数是被其他
Module
调用,那么大部分情况下当前线程的
Module State
都是错误的,所以必须得使用
DLL
本身的
Module State


AFX_MANAGE_STATE
只是一个宏,如下:

struct AFX_MAINTAIN_STATE2

{

explicit AFX_MAINTAIN_STATE2(AFX_MODULE_STATE* pModuleState) throw();

~AFX_MAINTAIN_STATE2();

protected:

#ifdef _AFXDLL

AFX_MODULE_STATE* m_pPrevModuleState;

_AFX_THREAD_STATE* m_pThreadState;

#endif

ULONG_PTR m_ulActCtxCookie;

BOOL m_bValidActCtxCookie;

};

#define AFX_MANAGE_STATE_NO_INIT_MANAGED(p) AFX_MAINTAIN_STATE2 _ctlState(p);

#define AFX_MANAGE_STATE(p) _AfxInitManaged(); AFX_MANAGE_STATE_NO_INIT_MANAGED(p)

可以看到
AFX_MANAGE_STATE
声明了一个栈上的局部变量
_ctrlState
,类型为
AFX_MAINTAIN_STATE2
。这是一个很常用的
Pattern

AFX_MAINTAIN_STATE2
在构造函数的时候会将当前的
Module State
切换为参数中指定的
Module State


AFX_MAINTAIN_STATE2::AFX_MAINTAIN_STATE2(AFX_MODULE_STATE* pNewState) throw()

{

#ifdef _AFXDLL

m_pThreadState = _afxThreadState.GetData();

ASSERT(m_pThreadState);

if(m_pThreadState)

{

m_pPrevModuleState = m_pThreadState->m_pModuleState;

m_pThreadState->m_pModuleState = pNewState;

}

else

{

// This is a very bad state; we have no good way to report the error at this moment

// since exceptions from here are not expected

m_pPrevModuleState=NULL;

m_pThreadState=NULL;

}

#endif

if (AfxGetAmbientActCtx() &&

pNewState->m_hActCtx != INVALID_HANDLE_VALUE)

{

m_bValidActCtxCookie = AfxActivateActCtx(pNewState->m_hActCtx, &m_ulActCtxCookie);

}

else

{

m_bValidActCtxCookie = FALSE;

}

}

然后在析构函数的时候将其恢复回来:

// AFX_MAINTAIN_STATE2 functions

_AFXWIN_INLINE AFX_MAINTAIN_STATE2::~AFX_MAINTAIN_STATE2()

{

#ifdef _AFXDLL

// Not a good place to report errors here, so just be safe

if(m_pThreadState)

{

m_pThreadState->m_pModuleState = m_pPrevModuleState;

}

#endif

if (m_bValidActCtxCookie)

{

BOOL bRet;

bRet = AfxDeactivateActCtx(0, m_ulActCtxCookie);

ASSERT(bRet == TRUE);

}

}

可以看到,
AFX_MAINTAIN_STATE2
将当前
_afxThreadState

m_pThreadState
中存起来,然后将所指向的
Module State
保存在
m_pPrevModuleState
中。在析构函数中,则使用保存起来的
m_pPrevModuleState
恢复到
m_pThreadState

Module State
。除了保存恢复
Module state
之外,
AFX_MAINTAIN_STATE2
也会在切换
Activation Context
。这个
Activation Context
被用来查找
Side By Side Assemblies
,我以后会专门写一篇文章讲述
Side By Side

Manifest
相关的一些信息。这次就写到这里。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐