原文地址: http://hi.baidu.com/%CD%F8%C2%E7%C3%D4%CD%BE%C9%D9%C4%EA/blog/item/db8aa0c2bf5e0853b219a857.html
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 .