原文位址:http://hi.baidu.com/rootlife/blog/item/2f37e354ad8cdc5bd10906be.html 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 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->() 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( 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 #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 _AFX_BASE_MODULE_STATE() : AFX_MODULE_STATE(TRUE, AfxWndProcBase, _MFC_VER) #else _AFX_BASE_MODULE_STATE() : AFX_MODULE_STATE(TRUE) { } 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。 pResult = _afxBaseModuleState.GetData(); _afxBaseModuleState是用PROCESS_LOCAL定義的: 它代表整個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。 static _AFX_DLL_MODULE_STATE afxModuleState; AFX_MODULE_STATE* AFXAPI AfxGetStaticModuleState() AFX_MODULE_STATE* pModuleState = &afxModuleState; return pModuleState; AFX_MODULE_STATE* pModuleState = AfxGetModuleState(); #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的相關資訊。 一般的用法如下: 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: AFX_MODULE_STATE* m_pPrevModuleState; _AFX_THREAD_STATE* m_pThreadState; 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; // 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; if (AfxGetAmbientActCtx() && pNewState->m_hActCtx != INVALID_HANDLE_VALUE) m_bValidActCtxCookie = AfxActivateActCtx(pNewState->m_hActCtx, &m_ulActCtxCookie); m_bValidActCtxCookie = FALSE; 然後在析構函數的時候将其恢複回來: // AFX_MAINTAIN_STATE2 functions _AFXWIN_INLINE AFX_MAINTAIN_STATE2::~AFX_MAINTAIN_STATE2() // Not a good place to report errors here, so just be safe m_pThreadState->m_pModuleState = m_pPrevModuleState; if (m_bValidActCtxCookie) BOOL bRet; bRet = AfxDeactivateActCtx(0, m_ulActCtxCookie); |