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

本文转自优快云博客:http://blog.youkuaiyun.com/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 相关的一些信息。这次就写到这里。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值