_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又是起什麼作用呢?從字面上看來,它是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 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: ENSURE(pData != NULL); return pData; { return (TYPE*)m_pObject; } AFX_INLINE operator TYPE*() { return GetData(); } AFX_INLINE TYPE* operator->() { return GetData(); } // Implementation public: { 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) m_pObject = (*pfnCreateObject)(); END_CATCH_ALL AfxUnlockGlobals(CRIT_PROCESSLOCAL); 3. 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) { ENSURE(_afxThreadData != NULL); ENSURE(m_nSlot != 0); if (pValue == NULL) pValue = (*pfnCreateObject)(); // set tls data to newly created object _afxThreadData->SetValue(m_nSlot, pValue); ASSERT(_afxThreadData->GetThreadValue(m_nSlot) == 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() { ENSURE(pState != NULL); return pState; _AFX_THREAD_STATE中保存着下列信息: 1. 當前的m_pModuleState,每個線程都知道它當前的Module State,這個信息被用來獲得當前的Module State,AfxGetModuleState正是這麼做的: AFX_MODULE_STATE* AFXAPI AfxGetModuleState() ENSURE(pState); AFX_MODULE_STATE* pResult; if (pState->m_pModuleState != NULL) pResult = pState->m_pModuleState; ENSURE(pResult != NULL); return pResult; 2. 之前的m_pModuleState,用來保存之前的Module State,用於Module State切換,可參考AFX_MANAGE_STATE 3. 其他信息,具體可以參考_AFX_THREAD_STATE的定義 4. 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 (global data for a module) class AFX_MODULE_STATE : public CNoTrackObject public: #ifdef _AFXDLL BOOL bSystem = FALSE); #endif 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 { }
這兩個class都沒有定義額外的成員,比較簡單,只是傳入到基類AFX_MODULE_STATE的參數不同。此外,他們定義的方式不太一樣,前者使用的是PROCESS_LOCAL宏,定義了一個變量_afxBaseModuleState。後者只是簡單的定義了一個static變量afxModuleState。
1. AfxGetModuleState AfxGetModuleState首先獲得_afxThreadState的m_pModuleState,如果當前的Thread State的m_pModuleState返回NULL,說明當前的Thread State沒有正確的初始化(通常的原因是創建線程的時候調用的是CreateThread函數而非AfxBeginThread),則使用_afxBaseModuleState。 AFX_MODULE_STATE* AFXAPI AfxGetModuleState() { ENSURE(pState); AFX_MODULE_STATE* pResult; if (pState->m_pModuleState != NULL) pResult = pState->m_pModuleState; pResult = _afxBaseModuleState.GetData(); ENSURE(pResult != NULL); return pResult; 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() return pModuleState; #else AFX_MODULE_STATE* AFXAPI AfxGetStaticModuleState() return pModuleState; 3. AfxGetAppModuleState AfxGetAppModuleState是最簡單的,直接返回_afxBaseModuleState: AFX_MODULE_STATE* AFXAPI AfxGetAppModuleState() { 從上面的討論可以看出,當前處於那個MFC Module的狀態之中,返回的就是那個MFC Module所相關聯的CWinApp對象。如果你有多個Module都是動態鏈接到MFC DLL的話,那麼AfxGetAppModuleState返回的總是同一個CWinApp。
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()) … AFX_MANAGE_STATE只是一個宏,如下: struct AFX_MAINTAIN_STATE2 ~AFX_MAINTAIN_STATE2(); #ifdef _AFXDLL _AFX_THREAD_STATE* m_pThreadState; 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() m_pThreadState = _afxThreadState.GetData(); ASSERT(m_pThreadState); if(m_pThreadState) m_pThreadState->m_pModuleState = pNewState; // since exceptions from here are not expected m_pPrevModuleState=NULL; m_pThreadState=NULL; 然後在析構函數的時候將其恢復回來: // AFX_MAINTAIN_STATE2 functions _AFXWIN_INLINE AFX_MAINTAIN_STATE2::~AFX_MAINTAIN_STATE2() // Not a good place to report errors here, so just be safe if(m_pThreadState) 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相關的一些信息。這次就寫到這裏。 |