MFC的模块状态:从AfxGetApp()和AFX_MANAGE_STATE()看MFC的模块状态
2010-05-25 15:34
399 查看
1.
Introduction
当我们在用MFC
编程的时候,我们经常用到
AfxGetApp()
来获得当前的
CWinApp
的
Instance
。看看
MFC
的源代码中
AfxGetApp()
的实现,你会发现
AfxGetApp()
的实现并不像一般情况下面那样直接:
_AFXWIN_INLINE CWinApp* AFXAPI AfxGetApp() { return afxCurrentWinApp; } #define afxCurrentWinApp AfxGetModuleState()->m_pCurrentWinApp |
调用的是
AfxGetModuleState()
,该函数返回一个
AFX_MODULE_STATE
的指针,其中的一个成员保存着当前的
CWinApp
的指针。可
AfxGetModuleState()
的作用又是什么呢?
此外,当我们在开发
MFC
DLL
程序的时候,我们会在每个输出的
DLL
函数前面加上一句
AFX_MANAGE_STATE
:
void SomeMFCDllFunction() { AFX_MANAGE_STATE(AfxGetStaticModuleState()) … |
又是起什么作用呢?从字面上看来,它是
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; |
用
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; } }; |
的作用只是一个
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; } |
首先判断
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; } |
中保存着下列信息:
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; } |
之前的
m_pModuleState
,用来保存之前的
Module
State
,用于
Module State
切
换,可参考
AFX_MANAGE_STATE
3.
其他信息,具体可以参考
_AFX_THREAD_STATE
的定义
4.
Module State
ModuleState
保存着和
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_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; } |
是用
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 |
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
相关的
一些信息。这次就写到这里。
相关文章推荐
- MFC的模块状态:从AfxGetApp()和AFX_MANAGE_STATE()看MFC的模块状态
- MFC的模块状态:从AfxGetApp()和AFX_MANAGE_STATE()看MFC的模块状态
- MFC的模块状态:从AfxGetApp()和AFX_MANAGE_STATE()看MFC的模块状态
- MFC的模块状态:从AfxGetApp()和AFX_MANAGE_STATE()看MFC的模块状态
- MFC的模块状态:AfxGetApp()和AFX_MANAGE_STATE()
- MFC的模块状态:从AfxGetApp()和AFX_MANAGE_STATE()看MFC的模块状态
- MFC的模块状态:从AfxGetApp()和AFX_MANAGE_STATE()看MFC的模块状态
- MFC的模块状态:从AfxGetApp()和AFX_MANAGE_STATE()看MFC的模块状态
- MFC的模块状态:从AfxGetApp()和AFX_MANAGE_STATE()看MFC的模块状态
- MFC的模块状态:从AfxGetApp()和AFX_MANAGE_STATE()看MFC的模块状态
- MFC的模块状态:从AfxGetApp()和AFX_MANAGE_STATE()看MFC的模块状态
- MFC的模块状态:从AfxGetApp()和AFX_MANAGE_STATE()看MFC的模块状态
- MFC的模块状态:从AfxGetApp()和AFX_MANAGE_STATE()看MFC的模块状态
- MFC的模块状态:从AfxGetApp()和AFX_MANAGE_STATE()看MFC的模块状态
- MFC的模块状态:从AfxGetApp()和AFX_MANAGE_STATE()看MFC的模块状态
- 转:MFC的模块状态(AfxGetApp()和AFX_MANAGE_STATE())
- MFC的模块状态:从AfxGetApp()和AFX_MANAGE_STATE()看MFC的模块状态
- MFC的模块状态:从AfxGetApp()和AFX_MANAGE_STATE()看MFC的模块状态
- MFC的模块状态:从AfxGetApp()和AFX_MANAGE_STATE()看MFC的模块状态
- MFC模块状态(二)AFX_MANAGE_STATE(AfxGetStaticModuleState())