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

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 相关的 一些信息。这次就写到这里。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值