MFC定義了多種狀態信息,這裡要介紹的是模塊狀態、進程狀態、線程狀態。這些狀態可以組合在一起,例如MFC句柄映射就是模塊和線程局部有效的,屬於模塊-線程狀態的一部分。
模塊狀態
這裡模塊的含義是:一個可執行的程序或者一個使用MFC DLL的DLL,比如一個OLE控件就是一個模塊。
一個應用程序的每一個模塊都有一個狀態,模塊狀態包括這樣一些信息:用來加載資源的 Windows實例句柄、指向當前CWinApp或者CWinThread對象的指針、OLE模塊的引用計數、Windows對象與相應的MFC對象之間的映射。只有單一模塊的應用程序的狀態如圖9-1所示。
m_pModuleState 指針是線程對象的成員變量,指向當前模塊狀態信息(一個AFX_MODULE_STATE結構變量)。當程序運行進入某個特定的模塊時,必須保證當前使用的模塊狀態是有效的模塊狀態──是這個特定模塊的模塊狀態。所以,每個線程對象都有一個指針指向有效的模塊狀態,每當進入某個模塊時都要使它指向有效模塊狀態,這對維護應用程序全局狀態和每個模塊狀態的完整性來說是非常重要的。為了作到這一點,每個模塊的所有入口點有責任實現模塊狀態的切換。模塊的入口點包括:DLL的輸出函數;OLE/COM界面的成員函數;窗口過程。
在講述窗口過程和動態鏈接到MFC DLL的規則DLL時,曾提到了語句AFX_MANAGE_STATE(AfxGetStaticModuleState( )),它就是用來在入口點切換模塊狀態的。其實現機制將在後面9.4.1節講解。
多個模塊狀態之間切換的示意圖如圖9-2所示。
圖9-2中,m_pModuleState總是指向當前模塊的狀態。
模塊、進程和線程狀態的數據結構
MFC定義了一系列類或者結構,通過它們來實現狀態信息的管理。這一節將描述它們的關系,並逐一解釋它們的數據結構、成員函數等。
層次關系
圖9-3顯示了線程狀態、模塊狀態、線程-模塊狀態等幾個類的層次關系:
線程狀態用類_AFX_THREAD_STATE描述,模塊狀態用類AFX_MODULE_STATE描述,模塊-線程狀態用類AFX_MODULE_THREAD_STATE描述。這些類從類CNoTrackObject派生。進程狀態類用_AFX_BASE_MODULE_STATE描述,從模塊狀態類AFX_MODULE_STATE派生。進程狀態是了一個可以獨立執行的MFC應用程序的模塊狀態。還有其他狀態如DLL的模塊狀態等也從模塊狀態類_AFX_MODULE_STATE派生。
圖9-4顯示了這幾個類的交互關系。
從圖9-4可以看出:首先,每個線程有一個線程狀態,線程狀態的指針m_pModuleState和m_pPreModuleState分別指向線程當前運行模塊的狀態或前一運行模塊的狀態;其次,每一個模塊狀態都有一個線程局部的變量用來存儲模塊-線程狀態。
下面各小節列出狀態信息管理所涉及的各個類的定義。
CNoTrackObject類
在圖9-3中, CnoTrackObject是根類,所有狀態類都是從它這裡派生的,其定義如下:
class CNoTrackObject
{
public:
void* PASCAL operator new(size_t nSize);
void PASCAL operator delete(void*);
#if defined(_DEBUG) && !defined(_AFX_NO_DEBUG_CRT)
void* PASCAL operator new(size_t nSize, LPCSTR, int);
#endif
virtual ~CNoTrackObject() { }
};
該類的析構函數是虛擬函數;而且,CNoTrackObject重載new操作符用來分配內存,重載delete操作符號用來釋放內存,內部通過LocalAlloc/LocalFree提供了一個低層內存分配器(Low_level alloctor)。---www.bianceng.cn
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);
AFX_MODULE_STATE(BOOL bDLL, WNDPROC pfnAfxWndProc,
DWORD dwVersion,BOOL bSystem);
#else
AFX_MODULE_STATE(BOOL bDLL);
#endif
~AFX_MODULE_STATE();
CWinApp* m_pCurrentWinApp;
HINSTANCE m_hCurrentInstanceHandle;
HINSTANCE m_hCurrentResourceHandle;
LPCTSTR m_lpszCurrentAppName;
BYTE m_bDLL;// TRUE if module is a DLL, FALSE if it is an EXE
//TRUE if module is a "system" module, FALSE if not
BYTE m_bSystem;
BYTE m_bReserved[2]; // padding
//Runtime class data:
#ifdef _AFXDLL
CRuntimeClass* m_pClassInit;
#endif
CTypedSimpleList<CRuntimeClass*> m_classList;
// OLE object factories
#ifndef _AFX_NO_OLE_SUPPORT
#ifdef _AFXDLL
COleObjectFactory* m_pFactoryInit;
#endif
CTypedSimpleList<COleObjectFactory*> m_factoryList;
#endif
// number of locked OLE objects
long m_nObjectCount;
BOOL m_bUserCtrl;
// AfxRegisterClass and AfxRegisterWndClass data
TCHAR m_szUnregisterList[4096];
#ifdef _AFXDLL
WNDPROC m_pfnAfxWndProc;
DWORD m_dwVersion; // version that module linked against
#endif
// variables related to a given process in a module
// (used to be AFX_MODULE_PROCESS_STATE)
#ifdef _AFX_OLD_EXCEPTIONS
// exceptions
AFX_TERM_PROC m_pfnTerminate;
#endif
void (PASCAL *m_pfnFilterToolTipMessage)(MSG*, CWnd*);
#ifdef _AFXDLL
// CDynLinkLibrary objects (for resource chain)
CTypedSimpleList<CDynLinkLibrary*> m_libraryList;
// special case for MFCxxLOC.DLL (localized MFC resources)
HINSTANCE m_appLangDLL;
#endif
#ifndef _AFX_NO_OCC_SUPPORT
// OLE control container manager
COccManager* m_pOccManager;
// locked OLE controls
CTypedSimpleList<COleControlLock*> m_lockList;
#endif
#ifndef _AFX_NO_DAO_SUPPORT
_AFX_DAO_STATE* m_pDaoState;
#endif
#ifndef _AFX_NO_OLE_SUPPORT
// Type library caches
CTypeLibCache m_typeLibCache;
CMapPtrToPtr* m_pTypeLibCacheMap;
#endif
// define thread local portions of module state
THREAD_LOCAL(AFX_MODULE_THREAD_STATE, m_thread)
};
從上面的定義可以看出,模塊狀態信息分為如下幾類:
模塊信息,資源信息,對動態鏈接到MFC DLL的支持信息,對擴展DLL的支持信息,對DAO的支持信息,對OLE的支持信息,模塊-線程狀態信息。
模塊信息包括實例句柄、資源句柄、應用程序名稱、指向應用程序的指針、是否為DLL模塊、模塊注冊的窗口類,等等。其中,成員變量m_fRegisteredClasses、m_szUnregisterList曾經在討論MFC的窗口注冊時提到過它們的用處。
在“#ifdef _AFXDLL…#endif”條件編譯范圍內的是支持MFC DLL的數據;
在“#ifndef _AFX_NO_OLE_SUPPOR…#endif”條件編譯范圍內的是支持OLE的數據;
在“#ifndef _AFX_NO_OCC_SUPPOR…#endif”條件編譯范圍內的是支持OLE控件的數據;
在“#ifndef _AFX_NO_DAO_SUPPORT”條件編譯范圍內的是支持DAO的數據。
THREAD_LOCAL宏定義了線程私有的模塊-線程類型的變量m_thread。
_AFX_BASE_MODULE_STATE
該類定義如下:
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
{ }
};
由定義可見,該類沒有在_AFX_MODULE_STATE類的基礎上增加數據。它類用來實現一個MFC應用程序模塊的狀態信息。
_AFX_THREAD_STATE
該類定義如下:
class _AFX_THREAD_STATE : public CNoTrackObject
{
public:
_AFX_THREAD_STATE();
virtual ~_AFX_THREAD_STATE();
// override for m_pModuleState in _AFX_APP_STATE
AFX_MODULE_STATE* m_pModuleState;
AFX_MODULE_STATE* m_pPrevModuleState;
// memory safety pool for temp maps
void* m_pSafetyPoolBuffer; // current buffer
// thread local exception context
AFX_EXCEPTION_CONTEXT m_exceptionContext;
// CWnd create, gray dialog hook, and other hook data
CWnd* m_pWndInit;
CWnd* m_pAlternateWndInit; // special case commdlg hooking
DWORD m_dwPropStyle;
DWORD m_dwPropExStyle;
HWND m_hWndInit;
BOOL m_bDlgCreate;
HHOOK m_hHookOldCbtFilter;
HHOOK m_hHookOldMsgFilter;
// other CWnd modal data
MSG m_lastSentMsg; // see CWnd::WindowProc
HWND m_hTrackingWindow; // see CWnd::TrackPopupMenu
HMENU m_hTrackingMenu;
TCHAR m_szTempClassName[96]; // see AfxRegisterWndClass
HWND m_hLockoutNotifyWindow; // see CWnd::OnCommand
BOOL m_bInMsgFilter;
// other framework modal data
CView* m_pRoutingView; // see CCmdTarget::GetRoutingView
CFrameWnd*m_pRoutingFrame;//see CmdTarget::GetRoutingFrame
// MFC/DB thread-local data
BOOL m_bWaitForDataSource;
// common controls thread state
CToolTipCtrl* m_pToolTip;
CWnd* m_pLastHit; // last window to own tooltip
int m_nLastHit; // last hittest code
TOOLINFO m_lastInfo; // last TOOLINFO structure
int m_nLastStatus; // last flyby status message
CControlBar* m_pLastStatus; // last flyby status control bar
// OLE control thread-local data
CWnd* m_pWndPark; // "parking space" window
long m_nCtrlRef; // reference count on parking window
BOOL m_bNeedTerm; // TRUE if OleUninitialize needs to be called
};
從定義可以看出,線程狀態的成員數據分如下幾類:
指向模塊狀態信息的指針,支持本線程的窗口創建的變量,MFC命令和消息處理用到的信息,處理工具條提示信息(tooltip)的結構,和處理OLE相關的變量,等等。
AFX_MODULE_THREAD_STATE
該類定義如下:
// AFX_MODULE_THREAD_STATE (local to thread *and* module)
class AFX_MODULE_THREAD_STATE : public CNoTrackObject
{
public:
AFX_MODULE_THREAD_STATE();
virtual ~AFX_MODULE_THREAD_STATE();
// current CWinThread pointer
CWinThread* m_pCurrentWinThread;
// list of CFrameWnd objects for thread
CTypedSimpleList<CFrameWnd*> m_frameList;
// temporary/permanent map state
DWORD m_nTempMapLock; // if not 0, temp maps locked
CHandleMap* m_pmapHWND;
CHandleMap* m_pmapHMENU;
CHandleMap* m_pmapHDC;
CHandleMap* m_pmapHGDIOBJ;
CHandleMap* m_pmapHimageLIST;
// thread-local MFC new handler (separate from C-runtime)
_PNH m_pfnNewHandler;
#ifndef _AFX_NO_SOCKET_SUPPORT
// WinSock specific thread state
HWND m_hSocketWindow;
CMapPtrToPtr m_mapSocketHandle;
CMapPtrToPtr m_mapDeadSockets;
CPtrList m_listSocketNotifications;
#endif
};
模塊-線程狀態的數據成員主要有:
指向當前線程對象(CWinThread對象)的指針m_pCurrentWinThread;
當前線程的框架窗口對象(CFrameWnd對象)列表m_frameList(邊框窗口在創建時(見圖5-8)把自身添加到m-frameList中,銷毀時則刪除掉,通過列表m_frameList可以遍歷模塊所有的邊框窗口);
new操作的例外處理函數m_pfnNewHandler;
臨時映射鎖定標識m_nTempMapLock,防止並發修改臨時映射。
系列Windows對象-MFC對象的映射,如m_pmapHWND等。
這些數據成員都是線程和模塊私有的。
下一節討論MFC如何通過上述這些類來實現其狀態的管理。
線程局部存儲機制和狀態的實現
MFC實現線程、模塊或者線程-模塊私有狀態的基礎是MFC的線程局部存儲機制。MFC定義了CThreadSlotData類型的全局變量_afxThreadData來為進程的線程分配線程局部存儲空間:
CThreadSlotData* _afxThreadData;
在此基礎上,MFC定義了變量_afxThreadState來管理線程狀態,定義了變量_afxBaseModuleState來管理進程狀態。
THREAD_LOCAL(_AFX_THREAD_STATE, _afxThreadState)
PROCESS_LOCAL(_AFX_BASE_MODULE_STATE, _afxBaseModuleState)
對於每個THREAD_LOCAL宏定義的變量,進程的每個線程都有自己獨立的拷貝,這個變量在不同的線程裡頭可以有不同的取值。
對於每個PROCESS_LOCAL宏定義的變量,每個進程都有自己獨立的拷貝,這個變量在不同的進程裡頭可以有不同的取值。
分別解釋這三個變量。
CThreadSlotData和_afxThreadData
CThreadSlotData的定義
以Win32線程局部存儲機制為基礎,MFC設計了類CThreadSlotData來提供管理線程局部存儲的功能,MFC應用程序使用該類的對象──全局變量_afxThreadData來管理本進程的線程局部存儲。CThreadSlotData類的定義如下:
class CThreadSlotData
{
public:
CThreadSlotData();
//Operations
int AllocSlot();
void FreeSlot(int nSlot);
void* GetValue(int nSlot);
void SetValue(int nSlot, void* pValue);
// delete all values in process/thread
void DeleteValues(HINSTANCE hInst, BOOL bAll = FALSE);
// assign instance handle to just constructed slots
void AssignInstance(HINSTANCE hInst);
// Implementation
DWORD m_tlsIndex;// used to access system thread-local storage
int m_nAlloc; // number of slots allocated (in UINTs)
int m_nRover; // (optimization) for quick finding of free slots
int m_nMax; // size of slot table below (in bits)
CSlotData* m_pSlotData; // state of each slot (allocated or not)
//list of CThreadData structures
CTypedSimpleList<CThreadData*> m_list;
CRITICAL_SECTION m_sect;
// special version for threads only!
void* GetThreadValue(int nSlot);
void* PASCAL operator new(size_t, void* p){ return p; }
void DeleteValues(CThreadData* pData, HINSTANCE hInst);
~CThreadSlotData();
};
通過TLS索引m_tlsIndex,CThreadSlotData對象(_afxThreadData)為每一個線程分配一個線程私有的存儲空間並管理該空間。它把這個空間劃分為若干個槽,每個槽放一個線程私有的數據指針,這樣每個線程就可以存放任意個線程私有的數據指針。
CThreadSlotData的一些數據成員
在CThreadSlotData類的定義中所涉及的類或者結構定義如下:
(1)m_sect
m_sect是一個關鍵段變量,在_afxThreadData創建時初始化。因為_afxThreadData是一個全局變量,所以必須通過m_sect來同步多個線程對該變量的並發訪問。
(2)m_nAlloc和m_pSlotData
m_nAlloc表示已經分配槽的數目,它代表了線程局部變量的個數。每一個線程局部變量都對應一個槽,每個槽對應一個線程局部變量。槽使用CSlotData類來管理。
CSlotData的定義如下:
struct CSlotData{
DWORD dwFlags; // slot flags (allocated/not allocated)
HINSTANCE hInst; // module which owns this slot
};
該結構用來描述槽的使用:
域dwFlags表示槽的狀態,即被占用或者沒有;
域hInst表示使用該槽的模塊的句柄。
m_pSlotData表示一個CSlotData類型的數組,用來描述各個槽。該數組通過成員函數AllocSlot和FreeSlot來動態地管理,見圖9-6。
(3)m_list
先討論CThreadData 類。CThreadData定義如下:
struct CThreadData : public CNoTrackObject{
CThreadData* pNext; // required to be member of CSimpleList
int nCount; // current size of pData
LPVOID* pData; // actual thread local data (indexed by nSlot)
};
該結構用來描述CThreadSlotData為每個線程管理的線程局部空間:
域pNext把各個線程的CThreadData項目鏈接成一個表,即把各個線程的線程私有空間鏈接起來;
域nCount表示域pData的尺寸,即存儲了多少個線程私有數據;
pData表示一個LPVOID類型的數組,數組中的每一個元素保存一個指針,即線程私有數據指針,該指針指向一個在堆中分配的真正存儲線程私有數據的地址。數組元素的個數和槽的個數相同,每個線程局部變量(THREAD_LOCAL定義的變量)都有一個對應的槽號,用該槽號作為下標來引用pData。
m_list表示一個CThreadData類型的指針數組,數組中的各項指向各個線程的線程私有空間,每個線程在數組中都有一個對應項。該數組通過GetValue、SetValue、DeleteValues等成員函數來管理,見圖9-6。
_afxThreadData
_afxThreadData僅僅定義為一個CThreadSlotData類型的指針,所指對象在第一次被引用時創建,在此之前該指針為空。下文_afxThreadData含義是它所指的對象。圖9-5、9-6圖解了MFC的線程局部存儲機制的實現。
圖9-5表示_afxTheadData使用TLS技術負責給進程分配一個TLS索引,然後使用TLS索引為進程的每一個線程分配線程局部存儲空間。
圖9-6表示每個線程的的局部存儲空間可以分多個槽,每個槽可以放一個線程私有的數據指針。_afxThreadData負責給線程局部變量分配槽號並根據槽號存取數據。圖的左半部分描述了管理槽的m_pSlotData及類CSlotData的結構,右半部分描述了管理MFC線程私有空間的m_list及類CThreadData的結構。
結合圖9-6,對MFC線程局部存儲機制總結如下:
每個線程局部變量(宏THREAD_LOCAL定義)占用一個槽,並有一個槽號。。
每個線程都有自己的MFC局部存儲空間(下文多次使用“線程的MFC局部存儲空間”,表示和此處相同的概念)。
通過TLS索引得到的是一個指針P1,它指向線程的MFC局部存儲空間。
通過指針P1和線程局部變量在空間所占用的槽號,得到該槽所存儲的線程私有的數據指針,即真正的線程私有數據的地址P2;
從地址P2得到數據D。
這個過程相當於幾重間接尋址:先得到TLS線程私有數據指針,從TLS線程私有數據指針得到線程的MFC線程局部存儲空間,再從MFC局部存儲空間的對應槽得到一個線程私有的數據指針,從該指針得到最終的線程私有數據。如果沒有這種機制,使用Win32 TLS只要一次間接尋址:得到TLS線程私有數據指針,從該指針得到最終的線程私有數據。
線程狀態_afxThreadState
從上一節知道了MFC的線程局部存儲機制。但有一點還不清楚,即某個線程局部變量所占用的槽號是怎麼保存的呢?關於這點可從線程局部的線程狀態變量_afxThreadState的實現來分析MFC的作法。變量_afxThreadState的定義如下:
THREAD_LOCAL(_AFX_THREAD_STATE, _afxThreadState)
THREAD_LOCAL 是一個宏,THREAD_LOCAL(class_name, ident_name)宏展開後如下:
AFX_DATADEF CThreadLocal<class_name> ident_name;
這裡,CThreadLocal是一個類模板,從CThreadLocalObject類繼承。
CThreadLocalObject和CThreadLocal的定義如下:
class CThreadLocalObject
{
public:
// Attributes
CNoTrackObject* GetData(CNoTrackObject* (AFXAPI*
pfnCreateObject)());
CNoTrackObject* GetDataNA();
// Implementation
int m_nSlot;
~CThreadLocalObject();
};
CThreadLocalObject用來幫助實現一個線程局部的變量。成員變量m_nSlot表示線程局部變量在MFC線程局部存儲空間中占據的槽號。GetDataNA用來返回變量的值。GetData也可以返回變量的值,但是如果發現還沒有給該變量分配槽號(m_slot=0),則給它分配槽號並在線程的MFC局部空間為之分配一個槽;如果在槽m_nSlot還沒有數據(為空),則調用參數pfnCreateObject傳遞的函數創建一個數據項,並保存到槽m_nSlot中。
template<class TYPE>
class CThreadLocal : public CThreadLocalObject
{
// Attributes
public:
inline TYPE* GetData()
{
TYPE* pData = (TYPE*)CThreadLocalObject::GetData(&CreateObject);
ASSERT(pData != NULL);
return pData;
}
inline TYPE* GetDataNA()
{
TYPE* pData = (TYPE*)CThreadLocalObject::GetDataNA();
return pData;
}
inline operator TYPE*()
{ return GetData(); }
inline TYPE* operator->()
{ return GetData(); }
// Implementation
public:
static CNoTrackObject* AFXAPI CreateObject()
{ return new TYPE; }
};
CThreadLocal模板用來聲明任意類型的線程私有的變量,因為通過模板可以自動的正確的轉化(cast)指針類型。程序員可以使用它來實現自己的線程局部變量,正如MFC實現線程局部的線程狀態變量和模塊-線程變量一樣。
CThrealLocal的成員函數CreateObject用來創建動態的指定類型的對象。成員函數GetData調用了基類CThreadLocalObject的同名函數,並且把CreateObject函數的地址作為參數傳遞給它。
另外,CThreadLocal模板重載了操作符號“*”、“->”,這樣編譯器將自動地進行有關類型轉換,例如:
_AFX_THREAD_STATE *pStata = _afxThreadState
是可以被編譯器接收的。
現在回頭來看_afxThreadState的定義:
從以上分析可以知道,THREAD_LOCAL(class_name, ident_name)定義的結果並沒有產生一個名為ident_name的class_name類的實例,而是產生一個CThreadLocal模板類(確切地說,是其派生類)的實例,m_nSlot初始化為0。所以,_afxThreadState實質上是一個CThreadLocal模板類的全局變量。每一個線程局部變量都對應了一個全局的CThreadLoacl模板類對象,模板對象的m_nSlot記錄了線程局部變量對象的槽號。
進程模塊狀態afxBaseModuleState
進程模塊狀態定義如下:
PROCESS_LOCAL(_AFX_BASE_MODULE_STATE, _afxBaseModuleState)
表示它是一個_AFX_BASE_MODULE_STATE類型的進程局部(process local)的變量。
進程局部變量的實現方法主要是為了用於Win32s下。在Win32s下,一個DLL模塊如果被多個應用程序調用,它將讓這些程序共享它的全局數據。為了DLL的全局數據一個進程有一份獨立的拷貝,MFC設計了進程私有的實現方法,實際上就是在進程的堆(Heap)中分配全局數據的內存空間。
在Win32下,DLL模塊的數據和代碼被映射到調用進程的虛擬空間,也就是說,DLL定義的全局變量是進程私有的;所以進程局部變量的實現並不為Win32所關心。但是,不是說afxBaseModuleState不重要,僅僅是采用PROCESS_LOCAL技術聲明它是進程局部變量不是很必要了。PROCESS_LOCAL(class_name, ident_name)宏展開後如下:
AFX_DATADEF CProcessLocal<class_name> ident_name;
這裡,CProcessLocal是一個類模板,從CProcessLocalObject類繼承。
CProcessLocalObject和CProcessLocal的定義如下:
class CProcessLocalObject
{
public:
// Attributes
CNoTrackObject* GetData(CNoTrackObject* (AFXAPI*
pfnCreateObject)());
// Implementation
CNoTrackObject* volatile m_pObject;
~CProcessLocalObject();
};
template<class TYPE>
class CProcessLocal : public CProcessLocalObject
{
// Attributes
public:
inline TYPE* GetData()
{
TYPE* pData =(TYPE*)CProcessLocalObject::GetData(&CreateObject);
ASSERT(pData != NULL);
return pData;
}
inline TYPE* GetDataNA()
{ return (TYPE*)m_pObject; }
inline operator TYPE*()
{ return GetData(); }
inline TYPE* operator->()
{ return GetData(); }
// Implementation
public:
static CNoTrackObject* AFXAPI CreateObject()
{ return new TYPE; }
};
類似於線程局部對象,每一個進程局部變量都有一個對應的全局CProcessLocal模板對象。
狀態對象的創建
狀態對象的創建過程
回顧前一節的三個定義:
CThreadSlotData* _afxThreadData;
THREAD_LOCAL(_AFX_THREAD_STATE, _afxThreadState)
PROCESS_LOCAL(_AFX_BASE_MODULE_STATE, _afxBaseModuleState)
第一個僅僅定義了一個指針;第二和第三個定義了一個模板類的實例。相應的CThreadSlotData對象(全局)、_AFX_THREAD_STATE對象(線程局部)以及_AFX_BASE_MODULE_STATE對象(進程局部)並沒有創建。當然,模塊狀態對象的成員模塊-線程對象也沒有被創建。這些對象要到第一次被訪問時,才會被創建,這樣做會提高加載DLL的速度。