- MFC的狀態(tài)
MFC定義了多種狀態(tài)信息,這里要介紹的是模塊狀態(tài)、進(jìn)程狀態(tài)、線程狀態(tài)。這些狀態(tài)可以組合在一起,例如MFC句柄映射就是模塊和線程局部有效的,屬于模塊-線程狀態(tài)的一部分。 - 模塊狀態(tài)
這里模塊的含義是:一個(gè)可執(zhí)行的程序或者一個(gè)使用MFC DLL的DLL,比如一個(gè)OLE控件就是一個(gè)模塊。 一個(gè)應(yīng)用程序的每一個(gè)模塊都有一個(gè)狀態(tài),模塊狀態(tài)包括這樣一些信息:用來(lái)加載資源的 Windows實(shí)例句柄、指向當(dāng)前CWinApp或者CWinThread對(duì)象的指針、OLE模塊的引用計(jì)數(shù)、Windows對(duì)象與相應(yīng)的MFC對(duì)象之間的映射。只有單一模塊的應(yīng)用程序的狀態(tài)如圖9-1所示。 m_pModuleState 指針是線程對(duì)象的成員變量,指向當(dāng)前模塊狀態(tài)信息(一個(gè)AFX_MODULE_STATE結(jié)構(gòu)變量)。當(dāng)程序運(yùn)行進(jìn)入某個(gè)特定的模塊時(shí),必須保證當(dāng)前使用的模塊狀態(tài)是有效的模塊狀態(tài)──是這個(gè)特定模塊的模塊狀態(tài)。所以,每個(gè)線程對(duì)象都有一個(gè)指針指向有效的模塊狀態(tài),每當(dāng)進(jìn)入某個(gè)模塊時(shí)都要使它指向有效模塊狀態(tài),這對(duì)維護(hù)應(yīng)用程序全局狀態(tài)和每個(gè)模塊狀態(tài)的完整性來(lái)說(shuō)是非常重要的。為了作到這一點(diǎn),每個(gè)模塊的所有入口點(diǎn)有責(zé)任實(shí)現(xiàn)模塊狀態(tài)的切換。模塊的入口點(diǎn)包括:DLL的輸出函數(shù);OLE/COM界面的成員函數(shù);窗口過(guò)程。 在講述窗口過(guò)程和動(dòng)態(tài)鏈接到MFC DLL的規(guī)則DLL時(shí),曾提到了語(yǔ)句AFX_MANAGE_STATE(AfxGetStaticModuleState( )),它就是用來(lái)在入口點(diǎn)切換模塊狀態(tài)的。其實(shí)現(xiàn)機(jī)制將在后面9.4.1節(jié)講解。 多個(gè)模塊狀態(tài)之間切換的示意圖如圖9-2所示。 圖9-2中,m_pModuleState總是指向當(dāng)前模塊的狀態(tài)。 - 模塊、進(jìn)程和線程狀態(tài)的數(shù)據(jù)結(jié)構(gòu)
MFC定義了一系列類(lèi)或者結(jié)構(gòu),通過(guò)它們來(lái)實(shí)現(xiàn)狀態(tài)信息的管理。這一節(jié)將描述它們的關(guān)系,并逐一解釋它們的數(shù)據(jù)結(jié)構(gòu)、成員函數(shù)等。 - 層次關(guān)系
圖9-3顯示了線程狀態(tài)、模塊狀態(tài)、線程-模塊狀態(tài)等幾個(gè)類(lèi)的層次關(guān)系: 線程狀態(tài)用類(lèi)_AFX_THREAD_STATE描述,模塊狀態(tài)用類(lèi)AFX_MODULE_STATE描述,模塊-線程狀態(tài)用類(lèi)AFX_MODULE_THREAD_STATE描述。這些類(lèi)從類(lèi)CNoTrackObject派生。進(jìn)程狀態(tài)類(lèi)用_AFX_BASE_MODULE_STATE描述,從模塊狀態(tài)類(lèi)AFX_MODULE_STATE派生。進(jìn)程狀態(tài)是了一個(gè)可以獨(dú)立執(zhí)行的MFC應(yīng)用程序的模塊狀態(tài)。還有其他狀態(tài)如DLL的模塊狀態(tài)等也從模塊狀態(tài)類(lèi)_AFX_MODULE_STATE派生。 圖9-4顯示了這幾個(gè)類(lèi)的交互關(guān)系。 從圖9-4可以看出:首先,每個(gè)線程有一個(gè)線程狀態(tài),線程狀態(tài)的指針m_pModuleState和m_pPreModuleState分別指向線程當(dāng)前運(yùn)行模塊的狀態(tài)或前一運(yùn)行模塊的狀態(tài);其次,每一個(gè)模塊狀態(tài)都有一個(gè)線程局部的變量用來(lái)存儲(chǔ)模塊-線程狀態(tài)。 下面各小節(jié)列出狀態(tài)信息管理所涉及的各個(gè)類(lèi)的定義。 - CNoTrackObject類(lèi)
在圖9-3中, CnoTrackObject是根類(lèi),所有狀態(tài)類(lèi)都是從它這里派生的,其定義如下: 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() { } }; 該類(lèi)的析構(gòu)函數(shù)是虛擬函數(shù);而且,CNoTrackObject重載new操作符用來(lái)分配內(nèi)存,重載delete操作符號(hào)用來(lái)釋放內(nèi)存,內(nèi)部通過(guò)LocalAlloc/LocalFree提供了一個(gè)低層內(nèi)存分配器(Low_level alloctor)。 - AFX_MODULE_STATE類(lèi)
AFX_MODULE_STATE類(lèi)的定義如下: // 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) }; 從上面的定義可以看出,模塊狀態(tài)信息分為如下幾類(lèi): 模塊信息,資源信息,對(duì)動(dòng)態(tài)鏈接到MFC DLL的支持信息,對(duì)擴(kuò)展DLL的支持信息,對(duì)DAO的支持信息,對(duì)OLE的支持信息,模塊-線程狀態(tài)信息。 模塊信息包括實(shí)例句柄、資源句柄、應(yīng)用程序名稱(chēng)、指向應(yīng)用程序的指針、是否為DLL模塊、模塊注冊(cè)的窗口類(lèi),等等。其中,成員變量m_fRegisteredClasses、m_szUnregisterList曾經(jīng)在討論MFC的窗口注冊(cè)時(shí)提到過(guò)它們的用處。 在“#ifdef _AFXDLL…#endif”條件編譯范圍內(nèi)的是支持MFC DLL的數(shù)據(jù); 在“#ifndef _AFX_NO_OLE_SUPPOR…#endif”條件編譯范圍內(nèi)的是支持OLE的數(shù)據(jù); 在“#ifndef _AFX_NO_OCC_SUPPOR…#endif”條件編譯范圍內(nèi)的是支持OLE控件的數(shù)據(jù); 在“#ifndef _AFX_NO_DAO_SUPPORT”條件編譯范圍內(nèi)的是支持DAO的數(shù)據(jù)。 THREAD_LOCAL宏定義了線程私有的模塊-線程類(lèi)型的變量m_thread。 - _AFX_BASE_MODULE_STATE
該類(lèi)定義如下: 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 { } }; 由定義可見(jiàn),該類(lèi)沒(méi)有在_AFX_MODULE_STATE類(lèi)的基礎(chǔ)上增加數(shù)據(jù)。它類(lèi)用來(lái)實(shí)現(xiàn)一個(gè)MFC應(yīng)用程序模塊的狀態(tài)信息。 - _AFX_THREAD_STATE
該類(lèi)定義如下: 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 }; 從定義可以看出,線程狀態(tài)的成員數(shù)據(jù)分如下幾類(lèi): 指向模塊狀態(tài)信息的指針,支持本線程的窗口創(chuàng)建的變量,MFC命令和消息處理用到的信息,處理工具條提示信息(tooltip)的結(jié)構(gòu),和處理OLE相關(guān)的變量,等等。 - AFX_MODULE_THREAD_STATE
該類(lèi)定義如下: // 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 }; 模塊-線程狀態(tài)的數(shù)據(jù)成員主要有: 指向當(dāng)前線程對(duì)象(CWinThread對(duì)象)的指針m_pCurrentWinThread; 當(dāng)前線程的框架窗口對(duì)象(CFrameWnd對(duì)象)列表m_frameList(邊框窗口在創(chuàng)建時(shí)(見(jiàn)圖5-8)把自身添加到m-frameList中,銷(xiāo)毀時(shí)則刪除掉,通過(guò)列表m_frameList可以遍歷模塊所有的邊框窗口); new操作的例外處理函數(shù)m_pfnNewHandler; 臨時(shí)映射鎖定標(biāo)識(shí)m_nTempMapLock,防止并發(fā)修改臨時(shí)映射。 系列Windows對(duì)象-MFC對(duì)象的映射,如m_pmapHWND等。 這些數(shù)據(jù)成員都是線程和模塊私有的。 下一節(jié)討論MFC如何通過(guò)上述這些類(lèi)來(lái)實(shí)現(xiàn)其狀態(tài)的管理。 - 線程局部存儲(chǔ)機(jī)制和狀態(tài)的實(shí)現(xiàn)
MFC實(shí)現(xiàn)線程、模塊或者線程-模塊私有狀態(tài)的基礎(chǔ)是MFC的線程局部存儲(chǔ)機(jī)制。MFC定義了CThreadSlotData類(lèi)型的全局變量_afxThreadData來(lái)為進(jìn)程的線程分配線程局部存儲(chǔ)空間: CThreadSlotData* _afxThreadData; 在此基礎(chǔ)上,MFC定義了變量_afxThreadState來(lái)管理線程狀態(tài),定義了變量_afxBaseModuleState來(lái)管理進(jìn)程狀態(tài)。 THREAD_LOCAL(_AFX_THREAD_STATE, _afxThreadState) PROCESS_LOCAL(_AFX_BASE_MODULE_STATE, _afxBaseModuleState) 對(duì)于每個(gè)THREAD_LOCAL宏定義的變量,進(jìn)程的每個(gè)線程都有自己獨(dú)立的拷貝,這個(gè)變量在不同的線程里頭可以有不同的取值。 對(duì)于每個(gè)PROCESS_LOCAL宏定義的變量,每個(gè)進(jìn)程都有自己獨(dú)立的拷貝,這個(gè)變量在不同的進(jìn)程里頭可以有不同的取值。 分別解釋這三個(gè)變量。 - CThreadSlotData和_afxThreadData
- CThreadSlotData的定義
以Win32線程局部存儲(chǔ)機(jī)制為基礎(chǔ),MFC設(shè)計(jì)了類(lèi)CThreadSlotData來(lái)提供管理線程局部存儲(chǔ)的功能,MFC應(yīng)用程序使用該類(lèi)的對(duì)象──全局變量_afxThreadData來(lái)管理本進(jìn)程的線程局部存儲(chǔ)。CThreadSlotData類(lèi)的定義如下: 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(); }; 通過(guò)TLS索引m_tlsIndex,CThreadSlotData對(duì)象(_afxThreadData)為每一個(gè)線程分配一個(gè)線程私有的存儲(chǔ)空間并管理該空間。它把這個(gè)空間劃分為若干個(gè)槽,每個(gè)槽放一個(gè)線程私有的數(shù)據(jù)指針,這樣每個(gè)線程就可以存放任意個(gè)線程私有的數(shù)據(jù)指針。 - CThreadSlotData的一些數(shù)據(jù)成員
在CThreadSlotData類(lèi)的定義中所涉及的類(lèi)或者結(jié)構(gòu)定義如下: (1)m_sect m_sect是一個(gè)關(guān)鍵段變量,在_afxThreadData創(chuàng)建時(shí)初始化。因?yàn)開(kāi)afxThreadData是一個(gè)全局變量,所以必須通過(guò)m_sect來(lái)同步多個(gè)線程對(duì)該變量的并發(fā)訪問(wèn)。 (2)m_nAlloc和m_pSlotData m_nAlloc表示已經(jīng)分配槽的數(shù)目,它代表了線程局部變量的個(gè)數(shù)。每一個(gè)線程局部變量都對(duì)應(yīng)一個(gè)槽,每個(gè)槽對(duì)應(yīng)一個(gè)線程局部變量。槽使用CSlotData類(lèi)來(lái)管理。 CSlotData的定義如下: struct CSlotData{ DWORD dwFlags; // slot flags (allocated/not allocated) HINSTANCE hInst; // module which owns this slot }; 該結(jié)構(gòu)用來(lái)描述槽的使用: 域dwFlags表示槽的狀態(tài),即被占用或者沒(méi)有; 域hInst表示使用該槽的模塊的句柄。 m_pSlotData表示一個(gè)CSlotData類(lèi)型的數(shù)組,用來(lái)描述各個(gè)槽。該數(shù)組通過(guò)成員函數(shù)AllocSlot和FreeSlot來(lái)動(dòng)態(tài)地管理,見(jiàn)圖9-6。 (3)m_list 先討論CThreadData 類(lèi)。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) }; 該結(jié)構(gòu)用來(lái)描述CThreadSlotData為每個(gè)線程管理的線程局部空間: 域pNext把各個(gè)線程的CThreadData項(xiàng)目鏈接成一個(gè)表,即把各個(gè)線程的線程私有空間鏈接起來(lái); 域nCount表示域pData的尺寸,即存儲(chǔ)了多少個(gè)線程私有數(shù)據(jù); pData表示一個(gè)LPVOID類(lèi)型的數(shù)組,數(shù)組中的每一個(gè)元素保存一個(gè)指針,即線程私有數(shù)據(jù)指針,該指針指向一個(gè)在堆中分配的真正存儲(chǔ)線程私有數(shù)據(jù)的地址。數(shù)組元素的個(gè)數(shù)和槽的個(gè)數(shù)相同,每個(gè)線程局部變量(THREAD_LOCAL定義的變量)都有一個(gè)對(duì)應(yīng)的槽號(hào),用該槽號(hào)作為下標(biāo)來(lái)引用pData。 m_list表示一個(gè)CThreadData類(lèi)型的指針數(shù)組,數(shù)組中的各項(xiàng)指向各個(gè)線程的線程私有空間,每個(gè)線程在數(shù)組中都有一個(gè)對(duì)應(yīng)項(xiàng)。該數(shù)組通過(guò)GetValue、SetValue、DeleteValues等成員函數(shù)來(lái)管理,見(jiàn)圖9-6。 - _afxThreadData
_afxThreadData僅僅定義為一個(gè)CThreadSlotData類(lèi)型的指針,所指對(duì)象在第一次被引用時(shí)創(chuàng)建,在此之前該指針為空。下文_afxThreadData含義是它所指的對(duì)象。圖9-5、9-6圖解了MFC的線程局部存儲(chǔ)機(jī)制的實(shí)現(xiàn)。 圖9-5表示_afxTheadData使用TLS技術(shù)負(fù)責(zé)給進(jìn)程分配一個(gè)TLS索引,然后使用TLS索引為進(jìn)程的每一個(gè)線程分配線程局部存儲(chǔ)空間。 圖9-6表示每個(gè)線程的的局部存儲(chǔ)空間可以分多個(gè)槽,每個(gè)槽可以放一個(gè)線程私有的數(shù)據(jù)指針。_afxThreadData負(fù)責(zé)給線程局部變量分配槽號(hào)并根據(jù)槽號(hào)存取數(shù)據(jù)。圖的左半部分描述了管理槽的m_pSlotData及類(lèi)CSlotData的結(jié)構(gòu),右半部分描述了管理MFC線程私有空間的m_list及類(lèi)CThreadData的結(jié)構(gòu)。 結(jié)合圖9-6,對(duì)MFC線程局部存儲(chǔ)機(jī)制總結(jié)如下: - 每個(gè)線程局部變量(宏THREAD_LOCAL定義)占用一個(gè)槽,并有一個(gè)槽號(hào)。。
- 每個(gè)線程都有自己的MFC局部存儲(chǔ)空間(下文多次使用“線程的MFC局部存儲(chǔ)空間”,表示和此處相同的概念)。
- 通過(guò)TLS索引得到的是一個(gè)指針P1,它指向線程的MFC局部存儲(chǔ)空間。
- 通過(guò)指針P1和線程局部變量在空間所占用的槽號(hào),得到該槽所存儲(chǔ)的線程私有的數(shù)據(jù)指針,即真正的線程私有數(shù)據(jù)的地址P2;
- 從地址P2得到數(shù)據(jù)D。
這個(gè)過(guò)程相當(dāng)于幾重間接尋址:先得到TLS線程私有數(shù)據(jù)指針,從TLS線程私有數(shù)據(jù)指針得到線程的MFC線程局部存儲(chǔ)空間,再?gòu)腗FC局部存儲(chǔ)空間的對(duì)應(yīng)槽得到一個(gè)線程私有的數(shù)據(jù)指針,從該指針得到最終的線程私有數(shù)據(jù)。如果沒(méi)有這種機(jī)制,使用Win32 TLS只要一次間接尋址:得到TLS線程私有數(shù)據(jù)指針,從該指針得到最終的線程私有數(shù)據(jù)。 - 線程狀態(tài)_afxThreadState
從上一節(jié)知道了MFC的線程局部存儲(chǔ)機(jī)制。但有一點(diǎn)還不清楚,即某個(gè)線程局部變量所占用的槽號(hào)是怎么保存的呢?關(guān)于這點(diǎn)可從線程局部的線程狀態(tài)變量_afxThreadState的實(shí)現(xiàn)來(lái)分析MFC的作法。變量_afxThreadState的定義如下: THREAD_LOCAL(_AFX_THREAD_STATE, _afxThreadState) THREAD_LOCAL 是一個(gè)宏,THREAD_LOCAL(class_name, ident_name)宏展開(kāi)后如下: AFX_DATADEF CThreadLocal<class_name> ident_name; 這里,CThreadLocal是一個(gè)類(lèi)模板,從CThreadLocalObject類(lèi)繼承。 CThreadLocalObject和CThreadLocal的定義如下: class CThreadLocalObject { public: // Attributes CNoTrackObject* GetData(CNoTrackObject* (AFXAPI* pfnCreateObject)()); CNoTrackObject* GetDataNA(); // Implementation int m_nSlot; ~CThreadLocalObject(); }; CThreadLocalObject用來(lái)幫助實(shí)現(xiàn)一個(gè)線程局部的變量。成員變量m_nSlot表示線程局部變量在MFC線程局部存儲(chǔ)空間中占據(jù)的槽號(hào)。GetDataNA用來(lái)返回變量的值。GetData也可以返回變量的值,但是如果發(fā)現(xiàn)還沒(méi)有給該變量分配槽號(hào)(m_slot=0),則給它分配槽號(hào)并在線程的MFC局部空間為之分配一個(gè)槽;如果在槽m_nSlot還沒(méi)有數(shù)據(jù)(為空),則調(diào)用參數(shù)pfnCreateObject傳遞的函數(shù)創(chuàng)建一個(gè)數(shù)據(jù)項(xiàng),并保存到槽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模板用來(lái)聲明任意類(lèi)型的線程私有的變量,因?yàn)橥ㄟ^(guò)模板可以自動(dòng)的正確的轉(zhuǎn)化(cast)指針類(lèi)型。程序員可以使用它來(lái)實(shí)現(xiàn)自己的線程局部變量,正如MFC實(shí)現(xiàn)線程局部的線程狀態(tài)變量和模塊-線程變量一樣。 CThrealLocal的成員函數(shù)CreateObject用來(lái)創(chuàng)建動(dòng)態(tài)的指定類(lèi)型的對(duì)象。成員函數(shù)GetData調(diào)用了基類(lèi)CThreadLocalObject的同名函數(shù),并且把CreateObject函數(shù)的地址作為參數(shù)傳遞給它。 另外,CThreadLocal模板重載了操作符號(hào)“*”、“->”,這樣編譯器將自動(dòng)地進(jìn)行有關(guān)類(lèi)型轉(zhuǎn)換,例如: _AFX_THREAD_STATE *pStata = _afxThreadState 是可以被編譯器接收的。 現(xiàn)在回頭來(lái)看_afxThreadState的定義: 從以上分析可以知道,THREAD_LOCAL(class_name, ident_name)定義的結(jié)果并沒(méi)有產(chǎn)生一個(gè)名為ident_name的class_name類(lèi)的實(shí)例,而是產(chǎn)生一個(gè)CThreadLocal模板類(lèi)(確切地說(shuō),是其派生類(lèi))的實(shí)例,m_nSlot初始化為0。所以,_afxThreadState實(shí)質(zhì)上是一個(gè)CThreadLocal模板類(lèi)的全局變量。每一個(gè)線程局部變量都對(duì)應(yīng)了一個(gè)全局的CThreadLoacl模板類(lèi)對(duì)象,模板對(duì)象的m_nSlot記錄了線程局部變量對(duì)象的槽號(hào)。 - 進(jìn)程模塊狀態(tài)afxBaseModuleState
進(jìn)程模塊狀態(tài)定義如下: PROCESS_LOCAL(_AFX_BASE_MODULE_STATE, _afxBaseModuleState) 表示它是一個(gè)_AFX_BASE_MODULE_STATE類(lèi)型的進(jìn)程局部(process local)的變量。 進(jìn)程局部變量的實(shí)現(xiàn)方法主要是為了用于Win32s下。在Win32s下,一個(gè)DLL模塊如果被多個(gè)應(yīng)用程序調(diào)用,它將讓這些程序共享它的全局?jǐn)?shù)據(jù)。為了DLL的全局?jǐn)?shù)據(jù)一個(gè)進(jìn)程有一份獨(dú)立的拷貝,MFC設(shè)計(jì)了進(jìn)程私有的實(shí)現(xiàn)方法,實(shí)際上就是在進(jìn)程的堆(Heap)中分配全局?jǐn)?shù)據(jù)的內(nèi)存空間。 在Win32下,DLL模塊的數(shù)據(jù)和代碼被映射到調(diào)用進(jìn)程的虛擬空間,也就是說(shuō),DLL定義的全局變量是進(jìn)程私有的;所以進(jìn)程局部變量的實(shí)現(xiàn)并不為Win32所關(guān)心。但是,不是說(shuō)afxBaseModuleState不重要,僅僅是采用PROCESS_LOCAL技術(shù)聲明它是進(jìn)程局部變量不是很必要了。PROCESS_LOCAL(class_name, ident_name)宏展開(kāi)后如下: AFX_DATADEF CProcessLocal<class_name> ident_name; 這里,CProcessLocal是一個(gè)類(lèi)模板,從CProcessLocalObject類(lèi)繼承。 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; } }; 類(lèi)似于線程局部對(duì)象,每一個(gè)進(jìn)程局部變量都有一個(gè)對(duì)應(yīng)的全局CProcessLocal模板對(duì)象。 - 狀態(tài)對(duì)象的創(chuàng)建
- 狀態(tài)對(duì)象的創(chuàng)建過(guò)程
回顧前一節(jié)的三個(gè)定義: CThreadSlotData* _afxThreadData; THREAD_LOCAL(_AFX_THREAD_STATE, _afxThreadState) PROCESS_LOCAL(_AFX_BASE_MODULE_STATE, _afxBaseModuleState) 第一個(gè)僅僅定義了一個(gè)指針;第二和第三個(gè)定義了一個(gè)模板類(lèi)的實(shí)例。相應(yīng)的CThreadSlotData對(duì)象(全局)、_AFX_THREAD_STATE對(duì)象(線程局部)以及_AFX_BASE_MODULE_STATE對(duì)象(進(jìn)程局部)并沒(méi)有創(chuàng)建。當(dāng)然,模塊狀態(tài)對(duì)象的成員模塊-線程對(duì)象也沒(méi)有被創(chuàng)建。這些對(duì)象要到第一次被訪問(wèn)時(shí),才會(huì)被創(chuàng)建,這樣做會(huì)提高加載DLL的速度。 下面以一個(gè)動(dòng)態(tài)鏈接到MFC DLL的單模塊應(yīng)用程序?yàn)槔f(shuō)明這些對(duì)象的創(chuàng)建過(guò)程。 當(dāng)?shù)谝淮卧L問(wèn)狀態(tài)信息時(shí),比如使用 AfxGetModuleState得到模塊狀態(tài),導(dǎo)致系列創(chuàng)建過(guò)程的開(kāi)始,如圖9-7所示。 首先分析語(yǔ)句pState=_afxThreadState。如果_afxThreadData、線程狀態(tài)和模塊狀態(tài)還沒(méi)有創(chuàng)建,該語(yǔ)句可以導(dǎo)致這些數(shù)據(jù)的創(chuàng)建。 pState聲明為CNoTrackObject對(duì)象的指針,_afxThreadState聲明為一個(gè)模板CThreadLocal的實(shí)例,pState=_afxThreadData為什么可以通過(guò)編譯器的檢查呢?因?yàn)镃ThreadLocal模板重載了操作符“”*”和“->”,這兩個(gè)運(yùn)算返回CNoTrackObject類(lèi)型的對(duì)象。回顧3.2節(jié)CThreadLocalObject、CThreadLocal的定義,這兩個(gè)操作符運(yùn)算到最后都是調(diào)用CThreadLocalObject的成員函數(shù)GetData。 - 創(chuàng)建_afxThreadData所指對(duì)象和線程狀態(tài)
CThreadLocalObject::GetData用來(lái)獲取線程局部變量(這個(gè)例子中是線程狀態(tài))的值,其參數(shù)用來(lái)創(chuàng)建動(dòng)態(tài)的線程局部變量。圖9-7的上面的虛線框表示其流程: 它檢查成員變量m_nSlot是否等于0(線程局部變量是否曾經(jīng)被分配了MFC線程私有空間槽位),檢查全局變量_afxTheadData指針是否為空。如果_afxThreadData空,則創(chuàng)建一個(gè)CThreadSlotData類(lèi)對(duì)象,讓_afxThreadData指向它,這樣本程序的MFC線程局部存儲(chǔ)的管理者被創(chuàng)建。如果m_nSlot等于0,則讓_afxThreadDtata調(diào)用AllocSlot分配一個(gè)槽位并把槽號(hào)保存在m_nSlot中。 得到了線程局部變量(線程狀態(tài))所占用的槽位后,委托_afxThreadData調(diào)用GetThreadValue(m_nSlot)得到線程狀態(tài)值(指針)。如果結(jié)果非空,則返回它;如果結(jié)果是NULL,則表明該線程狀態(tài)還沒(méi)有被創(chuàng)建,于是使用參數(shù)創(chuàng)建一個(gè)動(dòng)態(tài)的線程狀態(tài),并使用SetValue把其指針保存在槽m_nSlot中,返回該指針。 得到了線程狀態(tài)的值后,通過(guò)它得到模塊狀態(tài)m_pModuleState。如果m_pModuleState為空,表明該線程狀態(tài)是才創(chuàng)建的,其許多成員變量還沒(méi)有賦值,程序的進(jìn)程模塊狀態(tài)還沒(méi)有被創(chuàng)建。于是調(diào)用函數(shù)_afxBaseModule.GetData,導(dǎo)致進(jìn)程模塊狀態(tài)被創(chuàng)建。 圖9-7的下面一個(gè)虛線框表示了CProcessLocalObject::GetData的創(chuàng)建過(guò)程: _afxBaseModule首先檢查成員變量m_pObject是否空,如果非空就返回它,即進(jìn)程模塊狀態(tài)指針;否則,在堆中創(chuàng)建一個(gè)動(dòng)態(tài)的_AFX_BASE_MODULE_STATE對(duì)象,返回。 從上述兩個(gè)GetData的實(shí)現(xiàn)可以看出,CThreadLocal模板對(duì)象負(fù)責(zé)線程局部變量的創(chuàng)建和管理(查詢(xún),修改,刪除);CProcessLocal模板對(duì)象負(fù)責(zé)進(jìn)程局部變量的創(chuàng)建和管理(查詢(xún),修改,刪除)。 模塊狀態(tài)的成員模塊-線程狀態(tài)m_thread的創(chuàng)建類(lèi)似于線程狀態(tài)的創(chuàng)建:當(dāng)?shù)谝淮卧L問(wèn)m_thread所對(duì)應(yīng)的CThreadLocal模板對(duì)象時(shí),給m_thread分配MFC線程局部存儲(chǔ)的私有槽號(hào)m_nSlot,并動(dòng)態(tài)地創(chuàng)建_AFX_MODULE_THREAD_STATE對(duì)象,保存對(duì)象指針在m_nSlot槽中。 - 創(chuàng)建過(guò)程所涉及的幾個(gè)重要函數(shù)的算法
創(chuàng)建過(guò)程所涉及的幾個(gè)重要函數(shù)的算法描述如下: - AllocSlot
AllocSlot用來(lái)分配線程的MFC私有存儲(chǔ)空間的槽號(hào)。由于該函數(shù)要修改全局變量_afxThreadData,所以必須使用m_sect關(guān)鍵段對(duì)象來(lái)同步多個(gè)線程對(duì)該函數(shù)的調(diào)用。 CThreadSlotData::AllocSlot() { 進(jìn)入關(guān)鍵段代碼(EnterCriticalSection(m_sect);) 搜索m_pSlotData,查找空槽(SLOT) 如果不存在空槽(第一次進(jìn)入時(shí),肯定不存在) 分配或再分配內(nèi)存以創(chuàng)建新槽, 指針m_pSlotData指向分配的地址。 得到新槽(SLOT) 標(biāo)志該SLOT為已用 記錄最新可用的SLOT到成員變量m_nRover中。 離開(kāi)關(guān)鍵段代碼(LeaveCriticalSection(m_sect);) 返回槽號(hào) } - GetThreadValue
GetThreadValue用來(lái)獲取調(diào)用線程的第slot個(gè)線程局部變量的值。每一個(gè)線程局部變量都占用一個(gè)且只一個(gè)槽位。 CThreadSlotData::GetThreadValue(int slot) { //得到一個(gè)CThreadData型的指針pData //pData指向MFC線程私有存儲(chǔ)空間。 //m_tlsIndex在_afxThreadData創(chuàng)建時(shí)由構(gòu)造函數(shù)創(chuàng)建 pData=(CThreadData*)TlsGetValue(m_tlsIndex),。 如果指針空或slot>pData->nCount, 則返回空。 否則,返回pData } - SetValue
SetValue用來(lái)把調(diào)用線程的第slot個(gè)線程局部變量的值(指針)存放到線程的MFC私有存儲(chǔ)空間的第slot個(gè)槽位。 CThreadSlotData::SetValue(int slot, void *pValue) { //通過(guò)TLS索引得到線程的MFC私有存儲(chǔ)空間 pData = (CThreadData*)TlsGetValue(m_tlsIndex) //沒(méi)有得到值或者pValue非空且當(dāng)前槽號(hào),即 //線程局部變量的個(gè)數(shù) //大于使用當(dāng)前局部變量的線程個(gè)數(shù)時(shí) if (pData NULL or slot > pData->nCount && pValue!=NULL) { if pData NULL //當(dāng)前線程第一次訪問(wèn)該線程局部變量 { 創(chuàng)建一個(gè)CThreadData實(shí)例; 添加到CThreadSlotData::m_list; 令pData指向它; } 按目前為止,線程局部變量的個(gè)數(shù)為pData->pData分配或重分配內(nèi)存, 用來(lái)容納指向真正線程數(shù)據(jù)的指針 調(diào)用TlsSetValue(pData)保存pData } //把指向真正線程數(shù)據(jù)的pValue保存在pData對(duì)應(yīng)的slot中 pData->pData[slot] = pValue } - 管理狀態(tài)
在描述了MFC狀態(tài)的實(shí)現(xiàn)機(jī)制之后,現(xiàn)在來(lái)討論MFC的狀態(tài)管理和相關(guān)狀態(tài)的作用。 - 模塊狀態(tài)切換
模塊狀態(tài)切換就是把當(dāng)前線程的線程狀態(tài)的m_pModuleState指針指向即將運(yùn)行模塊的模塊狀態(tài)。 MFC使用AFX_MANAGE_STATE宏來(lái)完成模塊狀態(tài)的切換,即進(jìn)入模塊時(shí)使用當(dāng)前模板的模板狀態(tài),并保存原模板狀態(tài);退出模塊時(shí)恢復(fù)原來(lái)的模塊狀態(tài)。這相當(dāng)于狀態(tài)的壓棧和出棧。實(shí)現(xiàn)原理如下。 先看MFC關(guān)于AFX_MANAGE_STATE的定義: #ifdef _AFXDLL struct AFX_MAINTAIN_STATE { AFX_MAINTAIN_STATE(AFX_MODULE_STATE* pModuleState); ~AFX_MAINTAIN_STATE(); protected: AFX_MODULE_STATE* m_pPrevModuleState; }; //AFX_MANAGE_STATE宏的定義: #define AFX_MANAGE_STATE(p) AFX_MAINTAIN_STATE _ctlState(p); #else // _AFXDLL #define AFX_MANAGE_STATE(p) #endif //!_AFXDLL 如果使用MFC DLL,MFC提供類(lèi)AFX_MAINTAIN_STATE來(lái)實(shí)現(xiàn)狀態(tài)的壓棧和出棧,AFX_MANAGE_SATATE宏的作用是定義一個(gè)AFX_MAINTAIN_STATE類(lèi)型的局部變量_ctlState。 AFX_MAINTAIN_STATE的構(gòu)造函數(shù)在其成員變量m_pPrevModuleState中保存當(dāng)前的模塊狀態(tài)對(duì)象,并把參數(shù)指定的模塊狀態(tài)設(shè)定為當(dāng)前模塊狀態(tài)。所以該宏作為入口點(diǎn)的第一條語(yǔ)句就切換了模塊狀態(tài)。 在退出模塊時(shí),局部變量_ctlState將自動(dòng)地銷(xiāo)毀,這導(dǎo)致AFX_MAINTAIN_STATE的析構(gòu)函數(shù)被調(diào)用,析構(gòu)函數(shù)把保存在m_pPrevModuleState的狀態(tài)設(shè)置為當(dāng)前狀態(tài)。 AFX_MANAGE_SATATE的參數(shù)在不同場(chǎng)合是不一樣的,例如, AFX_MANAGE_SATATE(AfxGetStaticModuleState()); 其中,AfxGetStaticModuleState返回DLL的模塊狀態(tài)afxModuleState。 AFX_MANAGE_STATE(_afxBaseModuleState.GetData()); 其中,_afxBaseModuleState.GetData()返回的是應(yīng)用程序的全局模塊狀態(tài)。 OLE使用的模塊切換方法有所不同,這里不作討論。 上面討論了線程執(zhí)行行不同模塊的代碼時(shí)切換模塊狀態(tài)的情況。在線程創(chuàng)建時(shí)怎么處理模塊狀態(tài)呢? - 一個(gè)進(jìn)程(使用MFC的應(yīng)用程序)的主線程創(chuàng)建線程模塊狀態(tài)和進(jìn)程模塊狀態(tài),前者是_AFX_THREAD_STATE類(lèi)的實(shí)例,后者是_AFX_BASE_MODULE_STATE類(lèi)的實(shí)例。
- 當(dāng)進(jìn)程的新的線程被創(chuàng)建時(shí),它創(chuàng)建自己的線程狀態(tài),繼承父線程的模塊狀態(tài)。在線程的入口函數(shù)_AfxThreadEntry完成這樣的處理,該函數(shù)的描述見(jiàn)8.5.3節(jié)。
- 擴(kuò)展DLL的模塊狀態(tài)
7.3.1節(jié)指出擴(kuò)展DLL的實(shí)現(xiàn)必須遵循五條規(guī)則,為此,首先在擴(kuò)展DLL實(shí)現(xiàn)文件里頭,定義AFX_EXTENSION_MODULE類(lèi)型的靜態(tài)擴(kuò)展模塊變量,然后在DllMain入口函數(shù)里頭使用AfxInitExtension初始化擴(kuò)展模塊變量,并且實(shí)現(xiàn)和輸出一個(gè)初始化函數(shù)供擴(kuò)展DLL的使用者調(diào)用。 使用者必須具備一個(gè)CWinApp對(duì)象,通常在它的InitInstance函數(shù)中調(diào)用擴(kuò)展DLL提供的初始化函數(shù)。 一般用以下的幾段代碼完成上述任務(wù)。首先是擴(kuò)展模塊變量的定義和初始化: static AFX_EXTENSION_MODULE extensionDLL; DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID) { if (dwReason == DLL_PROCESS_ATTACH) { // Extension DLL one-time initialization if (!AfxInitExtensionModule(extensionDLL,hInstance)) return 0; …… } } 然后是擴(kuò)展DLL的初始化函數(shù),假定初始化函數(shù)命名為InitMyDll,InitMyDll被定義為“C”鏈接的全局函數(shù),并且被輸出。 // wire up this DLL into the resource chain extern “C” void WINAPI InitMyDll() { CDynLinkLibrary* pDLL = new CDynLinkLibrary(extensionDLL, TRUE); ASSERT(pDLL != NULL); ... } 最后是調(diào)用者的處理,假定在應(yīng)用程序?qū)ο蟮腎nitInstance函數(shù)中調(diào)用初始化函數(shù): BOOL CMyApp::InitInstance() { InitMyMyDll(); … } 上述這些代碼只有在動(dòng)態(tài)鏈接到MFC DLL時(shí)才有用。下面,對(duì)這些代碼進(jìn)行分析和解釋 - _AFX_EXTENSION_MODULE
在分析代碼之前,先討論描述擴(kuò)展模塊狀態(tài)的_AFX_EXTENSION_MODULE類(lèi)。_AFX_EXTENSION_MODULE沒(méi)有基類(lèi),其定義如下: struct AFX_EXTENSION_MODULE { BOOL bInitialized; HMODULE hModule; HMODULE hResource; CRuntimeClass* pFirstSharedClass; COleObjectFactory* pFirstSharedFactory; }; 其中: 第一個(gè)域表示該結(jié)構(gòu)變量是否已經(jīng)被初始化了; 第二個(gè)域用來(lái)保存擴(kuò)展DLL的模塊句柄; 第三個(gè)域用來(lái)保存擴(kuò)展DLL的資源句柄; 第四個(gè)域用來(lái)保存擴(kuò)展DLL要輸出的CRuntimeClass類(lèi); 第五個(gè)域用來(lái)保存擴(kuò)展DLL的OLE Factory。 該結(jié)構(gòu)用來(lái)描述一個(gè)擴(kuò)展DLL的模塊狀態(tài)信息,每一個(gè)擴(kuò)展DLL都要定義一個(gè)該類(lèi)型的靜態(tài)變量,例如extensionDLL。 在DllMain中,調(diào)用AfxInitExtensionModule函數(shù)來(lái)初始化本DLL的靜態(tài)變量該變量(擴(kuò)展模塊狀態(tài)),如extensionDLL。函數(shù)AfxInitExtensionModule原型如下: BOOL AFXAPI AfxInitExtensionModule( AFX_EXTENSION_MODULE& state, HMODULE hModule) 其中: 參數(shù)1是DllMain傳遞給它的擴(kuò)展DLL的模塊狀態(tài),如extensionDLL; 參數(shù)2是DllMain傳遞給它的模塊句柄。 AfxInitExtensionModule函數(shù)主要作以下事情: (1)把擴(kuò)展DLL模塊的模塊句柄hModule、資源句柄hModule分別保存到參數(shù)state的成員變量hModule、hResource中; (2)把當(dāng)前模塊狀態(tài)的m_classList列表的頭保存到state的成員變量pFirstSharedClass中,m_classInit的頭設(shè)置為模塊狀態(tài)的m_pClassInit。在擴(kuò)展DLL模塊進(jìn)入DllMain之前,如果該擴(kuò)展模塊構(gòu)造了靜態(tài)AFX_CLASSINIT對(duì)象,則在初始化時(shí)把有關(guān)CRuntimeClass信息保存在當(dāng)前模塊狀態(tài)(注意不是擴(kuò)展DLL模塊,而是應(yīng)用程序模塊)的m_classList列表中。因此,擴(kuò)展DLL模塊初始化的CRuntimeClass信息從模塊狀態(tài)的m_classList中轉(zhuǎn)存到擴(kuò)展模塊狀態(tài)state的pFirstSharedClass中,模塊狀態(tài)的m_classInit恢復(fù)被該DLL改變前的狀態(tài)。 關(guān)于CRuntimeclass信息和AFX_CLASSINIT對(duì)象的構(gòu)造,在3.3.1節(jié)曾經(jīng)討論過(guò)。一個(gè)擴(kuò)展DLL在初始化時(shí),如果需要輸出它的CRuntimeClass對(duì)象,就可以使用相應(yīng)的CRuntimeClass對(duì)象定義一個(gè)靜態(tài)的AFX_CLASSINIT對(duì)象,而不一定要使用IMPLEMENT_SERIAL宏。當(dāng)然,可以序列化的類(lèi)必定導(dǎo)致可以輸出的CRuntimeClass對(duì)象。 (3)若支持OLE的話,把當(dāng)前模塊狀態(tài)的m_factoryList的頭保存到state的成員變量pFirstSharedFactory中。m_factoryList的頭設(shè)置為模塊狀態(tài)的m_m_pFactoryInit。 (4)這樣,經(jīng)過(guò)初始化之后,擴(kuò)展DLL模塊包含了擴(kuò)展DLL的模塊句柄、資源句柄、本模塊初始化的CRuntimeClass類(lèi)等等。 擴(kuò)展DLL的初始化函數(shù)將使用擴(kuò)展模塊狀態(tài)信息。下面,討論初始化函數(shù)的作用。 - 擴(kuò)展DLL的初始化函數(shù)
在初始化函數(shù)InitMyDll中,創(chuàng)建了一個(gè)動(dòng)態(tài)的CDynLinkLibrary對(duì)象,并把對(duì)象指針保存在pDLL中。CDynLinkLibrary類(lèi)從CCmdTarget派生,定義如下: class CDynLinkLibrary : public CCmdTarget { DECLARE_DYNAMIC(CDynLinkLibrary) public: // Constructor CDynLinkLibrary(AFX_EXTENSION_MODULE& state, BOOL bSystem = FALSE); // Attributes HMODULE m_hModule; HMODULE m_hResource; // for shared resources CTypedSimpleList<CRuntimeClass*> m_classList; #ifndef _AFX_NO_OLE_SUPPORT CTypedSimpleList<COleObjectFactory*> m_factoryList; #endif BOOL m_bSystem; // TRUE only for MFC DLLs // Implementation public: CDynLinkLibrary* m_pNextDLL; // simple singly linked list virtual ~CDynLinkLibrary(); #ifdef _DEBUG virtual void AssertValid() const; virtual void Dump(CDumpContext& dc) const; #endif //_DEBUG }; CDynLinkLibrary的結(jié)構(gòu)和AFX_EXTENSION_MODULE有一定的相似性,存在對(duì)應(yīng)關(guān)系。 CDynLinkLibrary構(gòu)造函數(shù)的第一個(gè)參數(shù)就是經(jīng)過(guò)AfxInitExtensionModule初始化后的擴(kuò)展DLL的模塊狀態(tài),如extensionDLL,第二個(gè)參數(shù)表示該DLL模塊是否是系統(tǒng)模塊。 創(chuàng)建CDynLinkLibrary對(duì)象導(dǎo)致CCmdTarget和CDynLinkLibrary類(lèi)的構(gòu)造函數(shù)被調(diào)用。CCmdTarget的構(gòu)造函數(shù)將獲取模塊狀態(tài)并且保存在成員變量m_pModuleState中。CDynLinkLibrary的構(gòu)造函數(shù)完成以下動(dòng)作: 構(gòu)造列表m_classList和m_factoryList; 把參數(shù)state的域hModule、hResource復(fù)制到對(duì)應(yīng)的成員變量m_hModule、m_hResource中; 把state的pFirstSharedClass、pFirstSharedFactory分別插入到m_classList列表、m_factoryList列表的表頭; 把參數(shù)2的值賦值給成員變量m_bSystem中; 至此,CDynLinkLibrary對(duì)象已經(jīng)構(gòu)造完畢。之后,CDynLinkLibrary構(gòu)造函數(shù)把CDynLinkLibrary對(duì)象自身添加到當(dāng)前模塊狀態(tài)(調(diào)用擴(kuò)展DLL的應(yīng)用程序模塊或者規(guī)則DLL模塊)的CDynLinkLibrary列表m_libraryList的表頭。為了防止多個(gè)線程修改模塊狀態(tài)的m_libraryList,訪問(wèn)m_libraryList時(shí)使用了同步機(jī)制。 這樣,調(diào)用模塊執(zhí)行完擴(kuò)展模塊的初始化函數(shù)之后,就把該擴(kuò)展DLL的資源、CRuntimeClass類(lèi)、OLE Factory等鏈接到調(diào)用者的模塊狀態(tài)中,形成一個(gè)鏈表。圖9-8表明了這種關(guān)系鏈。 綜合以上分析,可以知道: 擴(kuò)展DLL的模塊僅僅在該DLL調(diào)用DllMain期間和調(diào)用初始化函數(shù)期間被使用,在這些初始化完畢之后,擴(kuò)展DLL模塊被鏈接到當(dāng)前調(diào)用模塊的模塊狀態(tài)中,因此它所包含的資源信息等也就被鏈接到調(diào)用擴(kuò)展DLL的應(yīng)用程序或者規(guī)則DLL的模塊狀態(tài)中了。擴(kuò)展DLL擴(kuò)展了調(diào)用者的資源等,這是“擴(kuò)展DLL”得名的原因之一。 也正因?yàn)閿U(kuò)展DLL沒(méi)有自己的模塊狀態(tài)(指AFX_MODULE_STATE對(duì)象,擴(kuò)展DLL模塊狀態(tài)不是),而且必須由有模塊狀態(tài)的模塊來(lái)使用,所以只有動(dòng)態(tài)鏈接到MFC的應(yīng)用程序或者規(guī)則DLL才可以使用擴(kuò)展DLL模塊的輸出函數(shù)或者輸出類(lèi)。 - 核心MFC DLL
所謂核心MFC DLL,就是MFC核心類(lèi)庫(kù)形成的DLL,通常說(shuō)動(dòng)態(tài)鏈接到MFC,就是指核心MFC DLL。 核心MFC DLL實(shí)際上也是一種擴(kuò)展DLL,因?yàn)樗x了自己的擴(kuò)展模塊狀態(tài)coreDLL,實(shí)現(xiàn)了自己的DllMain函數(shù),使用AfxInitExtensionModule初始化核心DLL的擴(kuò)展模塊狀態(tài)coreDLL,并且DllMain還創(chuàng)建了CDynLinkLibrary,把核心DLL的擴(kuò)展模塊狀態(tài)coreDLL鏈接到當(dāng)前應(yīng)用程序的模塊狀態(tài)中。所有這些,都符合擴(kuò)展DLL的處理標(biāo)準(zhǔn)。 但是,核心MFC DLL是一種特殊的擴(kuò)展DLL,因?yàn)樗x和實(shí)現(xiàn)了MFC類(lèi)庫(kù),模塊狀態(tài)、線程狀態(tài)、進(jìn)程狀態(tài)、狀態(tài)管理和使用的機(jī)制就是核心MFC DLL定義和實(shí)現(xiàn)的。例如核心MFC DLL定義和輸出的模塊狀態(tài)變量,即_afxBaseModuleState,就是動(dòng)態(tài)鏈接到MFC的DLL的應(yīng)用程序的模塊狀態(tài)。 但是MFC DLL不作為獨(dú)立的模塊表現(xiàn)出來(lái),而是把自己作為一個(gè)擴(kuò)展模塊來(lái)處理。當(dāng)應(yīng)用程序動(dòng)態(tài)鏈接到MFC DLL時(shí),MFC DLL把自己的擴(kuò)展模塊狀態(tài)coreDLL鏈接到模塊狀態(tài)afxBaseModuleState,模塊狀態(tài)的成員變量m_hCurrentInstanceHandle指定為應(yīng)用程序的句柄。當(dāng)規(guī)則DLL動(dòng)態(tài)鏈接到MFC DLL時(shí),由規(guī)則DLL的DllMain把核心MFC DLL的擴(kuò)展模塊狀態(tài)coreDLL鏈接到規(guī)則DLL的模塊狀態(tài)afxModuleState中,模塊狀態(tài)afxModuleState的m_hCurrentInstanceHandle指定為規(guī)則DLL的句柄。 關(guān)于afxModuleState和規(guī)則DLL的模塊狀態(tài),見(jiàn)下一節(jié)的討論。 - 動(dòng)態(tài)鏈接的規(guī)則DLL的模塊狀態(tài)的實(shí)現(xiàn)
在本節(jié)中,動(dòng)態(tài)鏈接到MFC DLL(定義了_AFXDLL)的規(guī)則DLL在下文簡(jiǎn)稱(chēng)為規(guī)則DLL。 (1)規(guī)則DLL的模塊狀態(tài)的定義 規(guī)則DLL有自己的模塊狀態(tài)_afxModuleState,它是一個(gè)靜態(tài)變量,定義如下: static _AFX_DLL_MODULE_STATE afxModuleState; _AFX_DLL_MODULE_STATE的基類(lèi)是AFX_MODULE_STATE。 在前面的模塊狀態(tài)切換中提到的AfxGetStaticModuleState函數(shù),其定義和實(shí)現(xiàn)如下: _AFX_MODULE_STATE* AFXAPI AfxGetStaticModuleState() { AFX_MODULE_STATE* pModuleState = &afxModuleState; return pModuleState; } 它返回規(guī)則DLL的模塊狀態(tài)afxModuleState。 規(guī)則DLL的內(nèi)部函數(shù)使用afxModuleState作為模塊狀態(tài);輸出函數(shù)在被調(diào)用的時(shí)候首先切換到該模塊狀態(tài),然后進(jìn)一步處理。 (2)規(guī)則DLL的模塊狀態(tài)的初始化 從用戶(hù)角度來(lái)看,動(dòng)態(tài)鏈接到MFC DLL的規(guī)則DLL不需要DllMain函數(shù),只要提供CWinApp對(duì)象即可。其實(shí),MFC內(nèi)部是在實(shí)現(xiàn)擴(kuò)展DLL的方法基礎(chǔ)上來(lái)實(shí)現(xiàn)規(guī)則DLL的,它不僅為規(guī)則DLL提供了DllMain函數(shù),而且規(guī)則DLL也有擴(kuò)展DLL模塊狀態(tài)controlDLL。 順便指出,和擴(kuò)展DLL相比,規(guī)則DLL有一個(gè)CWinApp(或其派生類(lèi))應(yīng)用程序?qū)ο蠛鸵粋€(gè)模塊狀態(tài)afxModuleState。應(yīng)用程序?qū)ο笫侨謱?duì)象,所以在進(jìn)入規(guī)則DLL的DllMain之前已經(jīng)被創(chuàng)建,DllMain可以調(diào)用它的初始化函數(shù)InitInstance。模塊狀態(tài)afxModuleState是靜態(tài)全局變量,也在進(jìn)入DllMain之前被創(chuàng)建,DllMain訪問(wèn)模塊狀態(tài)時(shí)得到的就是該變量。擴(kuò)展DLL是沒(méi)有CWinApp對(duì)象和模塊狀態(tài)的,它只能使用應(yīng)用程序或者規(guī)則DLL的CWinApp對(duì)象和模塊狀態(tài)。 由于核心MFC DLL的DllMain被調(diào)用的時(shí)候,訪問(wèn)的必定是應(yīng)用程序的模塊狀態(tài),要把核心DLL的擴(kuò)展模塊狀態(tài)鏈接到規(guī)則DLL的模塊狀態(tài)中,必須通過(guò)規(guī)則DLL的DllMain來(lái)實(shí)現(xiàn)。 規(guī)則DLL的DllMain(MFC內(nèi)部實(shí)現(xiàn))把參數(shù)1表示的模塊和資源句柄通過(guò)AfxWinInit函數(shù)保存到規(guī)則DLL的模塊狀態(tài)中。順便指出,WinMain也通過(guò)AfxWinInit函數(shù)把資源和模塊句柄保存到應(yīng)用程序的模塊狀態(tài)中。 然后,該DllMain還創(chuàng)建了一個(gè)CDynLinkLibrary對(duì)象,把核心MFC DLL的擴(kuò)展模塊 coreDLL鏈接到本DLL的模塊狀態(tài)afxModuleState。 接著,DllMain得到自己的應(yīng)用程序?qū)ο蟛⒄{(diào)用InitInstance初始化。 之后,DllMain創(chuàng)建另一個(gè)CDynLinkLibrary對(duì)象,把本DLL的擴(kuò)展模塊controlDLL鏈接到本DLL的模塊狀態(tài)afxModuleState。 (3)使用規(guī)則DLL的應(yīng)用程序可不需要CwinApp對(duì)象 規(guī)則DLL的資源等是由DLL內(nèi)部使用的,不存在資源或者CRuntimeClass類(lèi)輸出的問(wèn)題,這樣調(diào)用規(guī)則DLL的程序不必具有模塊狀態(tài),不必關(guān)心規(guī)則DLL的內(nèi)部實(shí)現(xiàn),不一定需要CwinApp對(duì)象,所以可以是任意Win32應(yīng)用程序, 還有一點(diǎn)需要指出,DllMain也是規(guī)則DLL的入口點(diǎn),在它之前,調(diào)用DllMain的RawDllMain已經(jīng)切換了模塊狀態(tài),RawDllMain是靜態(tài)鏈接的,所以不必考慮狀態(tài)切換。 - 狀態(tài)信息的作用
在分析了MFC模塊狀態(tài)的實(shí)現(xiàn)基礎(chǔ)和管理機(jī)制之后,現(xiàn)在對(duì)狀態(tài)信息的作用進(jìn)行專(zhuān)門(mén)的討論。 - 模塊信息的保存和管理
傳統(tǒng)上,線程狀態(tài)、模塊狀態(tài)等包含的信息是全局變量,但是為了支持Win32s、多線程、DLL等,這些變量必須是限于進(jìn)程或者線程范圍內(nèi)有效,或者限于某個(gè)模塊內(nèi)有效。也就是,不再可能把它們作為全局變量處理。因此,MFC引入模塊、線程、模塊-線程狀態(tài)等來(lái)保存和管理一些重要的信息。 例如:一個(gè)模塊注冊(cè)了一個(gè)“窗口類(lèi)”之后,應(yīng)用程序要保存“窗口類(lèi)”的名字,以便在模塊退出時(shí)取消注冊(cè)的“窗口類(lèi)”。因此,模塊狀態(tài)使用成員變量m_szUnregisterList在注冊(cè)成功之后保存的“窗口類(lèi)”名字。窗口注冊(cè)見(jiàn)2.2.1節(jié)。 又如:Tooltip窗口是線程相關(guān)的,每個(gè)線程一個(gè),所以線程狀態(tài)用成員變量m_pToolTip來(lái)保存本線程的MFC Tooltip窗口對(duì)象。Tooltip窗口見(jiàn)13.2.4.4節(jié)。 還有,MFC對(duì)象是線程和模塊相關(guān)的,所以模塊線程中有一組變量用來(lái)管理本線程的MFC對(duì)象到Windows對(duì)象的映射關(guān)系。關(guān)于MFC對(duì)象和Windows對(duì)象的映射,見(jiàn)稍后的討論。 模塊狀態(tài)、線程狀態(tài)、模塊線程狀態(tài)的每個(gè)成員變量都有自己存在的必要和作用,這里就不一一論述了,在此,只是強(qiáng)調(diào)模塊狀態(tài)自動(dòng)地實(shí)現(xiàn)對(duì)模塊句柄和資源句柄等信息的保存和管理,這對(duì)MFC應(yīng)用程序是非常重要的。 SDK 下的應(yīng)用程序或者DLL,通常使用一個(gè)全局變量來(lái)保存模塊/資源句柄。有了模塊狀態(tài)之后,程序員就不必這么作了。規(guī)則DLL或者應(yīng)用程序的模塊和資源句柄在調(diào)用DllMain或WinMain時(shí)被保存到了當(dāng)前模塊的模塊狀態(tài)中。如果是擴(kuò)展DLL,則其句柄被保存到擴(kuò)展模塊狀態(tài)中,并通過(guò)CDynLinkLibrary對(duì)象鏈接到主模塊的模塊狀態(tài)。 圖9-8示意了MFC模塊狀態(tài)對(duì)資源、CRuntimeClass對(duì)象、OLE工廠等模塊信息的管理。 圖9-8的說(shuō)明: 左邊的主模塊狀態(tài)表示動(dòng)態(tài)鏈接到MFC DLL的應(yīng)用程序或者規(guī)則DLL的模塊狀態(tài),其資源句柄和模塊句柄用來(lái)查找和獲取資源,資源句柄一般是應(yīng)用程序的模塊句柄;CRuntimeClass對(duì)象列表和COleObjectFactory對(duì)象列表分別表示該模塊初始化了的CRuntimeClass對(duì)象和該模塊的OLE工廠對(duì)象;CDynLinkLibrary列表包含了它引用的系列擴(kuò)展DLL的擴(kuò)展模塊狀態(tài)(包括核心MFC DLL的狀態(tài)),鏈表中的每一個(gè)CDynLinkLibrary對(duì)象對(duì)應(yīng)一個(gè)擴(kuò)展模塊狀態(tài),代表了創(chuàng)建該對(duì)象的擴(kuò)展DLL的有關(guān)資源、信息。 MFC查找資源、CRuntimeClass類(lèi)、OLE工廠時(shí),首先查找模塊狀態(tài),然后,遍歷CDynLinkLibrary表搜索相應(yīng)的對(duì)象。下面兩節(jié)舉例說(shuō)明。 - MFC資源、運(yùn)行類(lèi)信息的查找
MFC內(nèi)部使用的資源查找函數(shù)是: HINSTANCE AfxFindResourceHandle(LPCTSTR lpszName, LPCTSTR lpszType): 其中: 參數(shù)1是要查找的資源名稱(chēng),參數(shù)2是要查找的資源類(lèi)型。 返回包含指定資源的模塊的句柄。 上述函數(shù)的查找算法如下: - 如果進(jìn)程模塊狀態(tài)(主模塊)不是系統(tǒng)模塊,則使用::FindResource(下同)搜索它,成功則返回;
- 如果沒(méi)有找到,則遍歷CDynLinkLibrary對(duì)象列表,搜索所有的非系統(tǒng)模塊,成功則返回;
- 如果沒(méi)有找到,則檢查主模塊的語(yǔ)言資源,成功則返回;
- 如果沒(méi)有找到,并且主模塊是系統(tǒng)模塊,則搜索它,成功則返回;
- 如果沒(méi)有找到,則遍歷CDynLinkLibrary對(duì)象列表,搜索所有的系統(tǒng)模塊,成功則返回;
- 如果沒(méi)有找到,則使用AfxGetResourceHanlde返回應(yīng)用程序的資源。
需要指出的是,遍歷CDynLinkLibrary對(duì)象列表時(shí),必須采取同步措施,防止其他線程改變鏈表。MFC是通過(guò)鎖定全局變量CRIT_DYNLINKLIST來(lái)實(shí)現(xiàn)的,類(lèi)似的全局變量MFC定義了多個(gè)。 運(yùn)行時(shí)類(lèi)信息的查找算法類(lèi)似。 3.3.4節(jié)指出,對(duì)象進(jìn)行“<<”序列化操作時(shí),首先需要搜索到指定類(lèi)的運(yùn)行時(shí)信息,方法如下: CRuntimeClass* PASCAL CRuntimeClass::Load( CArchive& ar, UINT* pwSchemaNum) - 遍歷主模塊的CRuntimeClass對(duì)象列表m_classList,搜索主模塊是否實(shí)現(xiàn)了指定的CRuntimeClass類(lèi);
- 遍歷CDynLinkLibrary對(duì)象列表m_libraryList;對(duì)每一個(gè)CDynLinkLibrary對(duì)象,遍歷它的CRuntimeClass對(duì)象列表m_classList。這樣,所有的擴(kuò)展DLL模塊的CRuntimeClass對(duì)象都會(huì)被搜索到。
- 模塊信息的顯示
遍歷模塊狀態(tài)和CDynLinkLibrary列表,可以顯示模塊狀態(tài)及其擴(kuò)展模塊狀態(tài)的有關(guān)信息。下面,給出一個(gè)實(shí)現(xiàn),它顯示程序的當(dāng)前模塊名稱(chēng)、句柄和初始化的CRuntimeClass類(lèi),然后顯示所有擴(kuò)展模塊的名稱(chēng)名稱(chēng)、句柄和初始化的CRuntimeClass類(lèi)。 #ifdef _DEBUG AFX_MODULE_STATE* pState = AfxGetModuleState(); //顯示應(yīng)用程序的名稱(chēng)和句柄 TRACE("APP %s HANDLE %x\r\n", pState->m_lpszCurrentAppName, pState->m_hCurrentInstanceHandle); TCHAR szT[256]; int nClasses; nClasses=0; //顯示CRuntimeClass類(lèi)信息 AfxLockGlobals(CRIT_RUNTIMECLASSLIST); for (CRuntimeClass* pClass = pModuleState->m_classList; pClass != NULL;pClass = pClass->m_pNextClass) { nClasses++; TRACE("CRuntimeClass: %s\r\n",pClass->m_lpszClassName, ); } AfxUnlockGlobals(CRIT_RUNTIMECLASSLIST); TRACE("all %d classes\r\n", nClasses); //遍歷CDynLinkLibrary列表 AfxLockGlobals(CRIT_DYNLINKLIST); for (CDynLinkLibrary* pDLL = pState->m_libraryList; pDLL != NULL; pDLL = pDLL->m_pNextDLL) { // 得到模塊名并且顯示 TCHAR szName[64]; GetModuleFileName(pDLL->m_hModule, szName, sizeof(szName)); TRACE("MODULE %s HANDLE IS %x \r\n", szName, pDLL->m_hModule); //得到CRuntimeClass信息并顯示 nClasses = 0; for (CRuntimeClass* pClass = pDLL->m_classList; pClass != NULL; pClass = pClass->m_pNextClass) { nClasses++; TRACE("CRuntimeClass: %s\r\n",pClass->m_lpszClassName, ); } wsprintf(szT, _T(" Module %s has %d classes"),szName, nClasses); } AfxUnlockGlobals(CRIT_DYNLINKLIST); #endif 使用MFC提供的調(diào)試函數(shù)AfxDoForAllClasses可以得到DLL模塊的輸出CRuntimeClass類(lèi)的信息。上述實(shí)現(xiàn)類(lèi)似于AfxDoForAllClasses函數(shù)的處理,只不過(guò)增加了模塊名和模塊句柄信息。 - 模塊-線程狀態(tài)的作用
由模塊-線程狀態(tài)類(lèi)的定義可知,一個(gè)模塊-線程狀態(tài)包含了幾類(lèi)Windows對(duì)象—MFC對(duì)象的映射。下面討論它們的作用。 - 只能訪問(wèn)本線程MFC對(duì)象的原因
MFC規(guī)定: - 不能從一個(gè)非MFC線程創(chuàng)建和訪問(wèn)MFC對(duì)象
如果一個(gè)線程被創(chuàng)建時(shí)沒(méi)有用到CWinThread對(duì)象,比如,直接使用“C”的_beginthread或者_(dá)beginthreadex創(chuàng)建的線程,則該線程不能訪問(wèn)MFC對(duì)象;換句話說(shuō),只有通過(guò)CWinThread創(chuàng)建MFC線程對(duì)象和Win32線程,才可能在創(chuàng)建的線程中使用MFC對(duì)象。 - 一個(gè)線程僅僅能訪問(wèn)它所創(chuàng)建的MFC對(duì)象
這兩個(gè)規(guī)定的原因是: 為了防止多個(gè)線程并發(fā)地訪問(wèn)同一個(gè)MFC對(duì)象,MFC對(duì)象和Windows對(duì)象之間有一個(gè)一一對(duì)應(yīng)的關(guān)系,這種關(guān)系以映射的形式保存在創(chuàng)建線程的當(dāng)前模塊的模塊-線程狀態(tài)信息中。當(dāng)一個(gè)線程使用某個(gè)MFC對(duì)象指針P時(shí),ASSERT_VALID(P)將驗(yàn)證當(dāng)前線程的當(dāng)前模塊是否有Windows句柄和P對(duì)應(yīng),即是否創(chuàng)建了P所指的Windows對(duì)象,驗(yàn)證失敗導(dǎo)致ASSERT斷言中斷程序的執(zhí)行。如果一個(gè)線程要使用其他線程的Windows對(duì)象,則必須傳遞Windows對(duì)象句柄,不能傳遞MFC對(duì)象指針。 當(dāng)然一般來(lái)說(shuō),MFC應(yīng)用程序僅僅在Debug版本下才檢查這種映射關(guān)系,所以訪問(wèn)其他線程的MFC對(duì)象的程序在Realease版本下表面上不會(huì)有問(wèn)題,但是MFC對(duì)象被并發(fā)訪問(wèn)的后果是不可預(yù)見(jiàn)的。 - 實(shí)現(xiàn)MFC對(duì)象和Windows對(duì)象之間的映射
MFC提供了幾個(gè)函數(shù)完成MFC對(duì)象和Windows對(duì)象之間的映射或者解除這種映射關(guān)系,以及從MFC對(duì)象得到Windows對(duì)象或者從Windows對(duì)象得到或創(chuàng)建相應(yīng)的MFC對(duì)象。 每一個(gè)MFC對(duì)象類(lèi)都有成員函數(shù)Attach和Detach,F(xiàn)romHandle和FromHandlePermanent,AssertValid。這些成員函數(shù)的形式如下: - Attach(HANDLE Windows_Object_Handle)
例如:CWnd類(lèi)的是Attach(HANLDE hWnd),CDC類(lèi)的是Attach(HDC hDc)。 Attach用來(lái)把一個(gè)句柄永久性(Perment)地映射到一個(gè)MFC對(duì)象上:它把一個(gè)Windows對(duì)象捆綁(Attach)到一個(gè)MFC對(duì)象上,MFC對(duì)象的句柄成員變量賦值為Windows對(duì)象句柄,該MFC對(duì)象應(yīng)該已經(jīng)存在,但是句柄成員變量為空。 Detach用來(lái)取消Windows對(duì)象到MFC對(duì)象的永久性映射。如果該Windows對(duì)象有一個(gè)臨時(shí)的映射存在,則Detach不理會(huì)它。MFC讓線程的Idle清除臨時(shí)映射和臨時(shí)MFC對(duì)象。 - FromHandle(HANDLE Windows_Object)
它是一個(gè)靜態(tài)成員函數(shù)。如果該Windows對(duì)象沒(méi)有映射到一個(gè)MFC對(duì)象,F(xiàn)romHandle則創(chuàng)建一個(gè)臨時(shí)的MFC對(duì)象,并把Windows對(duì)象映射到臨時(shí)的MFC對(duì)象上,然后返回臨時(shí)MFC對(duì)象。 - FromHandlePermanent(HANDLE Windows_Object)
它是一個(gè)靜態(tài)成員函數(shù)。如果該Windows對(duì)象沒(méi)有永久地映射到一個(gè)MFC對(duì)象上,則返回NULL,否則返回對(duì)應(yīng)的MFC對(duì)象。 它是從CObject類(lèi)繼承來(lái)的虛擬函數(shù)。MFC覆蓋該函數(shù),實(shí)現(xiàn)了至少一個(gè)功能:判斷當(dāng)前MFC對(duì)象的指針this是否映射到一個(gè)對(duì)應(yīng)的可靠的Windows對(duì)象。 圖 9-9示意了MFC對(duì)映射結(jié)構(gòu)的實(shí)現(xiàn)層次,對(duì)圖9-9解釋如下。 圖中上面的虛線框表示使用映射關(guān)系的高層調(diào)用,包括上面講述的幾類(lèi)函數(shù)。MFC和應(yīng)用程序通過(guò)它們創(chuàng)建、銷(xiāo)毀、使用映射關(guān)系。 圖中中間的虛線框表示MFC使用CHandleMap類(lèi)實(shí)現(xiàn)對(duì)映射關(guān)系的管理。一個(gè)CHandleMap對(duì)象可以通過(guò)兩個(gè)成員變量來(lái)管理兩種映射數(shù)據(jù):臨時(shí)映射和永久映射。模塊-線程狀態(tài)給每一類(lèi)MFC對(duì)象分派一個(gè)CHandleMap對(duì)象來(lái)管理其映射數(shù)據(jù)(見(jiàn)模塊-線程類(lèi)的定義),例如m_pmapHWND所指對(duì)象用來(lái)保存CWnd對(duì)象(或派生類(lèi)對(duì)象)和Windows window之間的映射。 下面的虛線框表示映射關(guān)系的最底層實(shí)現(xiàn),MFC使用通用類(lèi)CMapPtrToPtr來(lái)管理MFC對(duì)象指針和Windows句柄之間的映射數(shù)據(jù)。 對(duì)本節(jié)總結(jié)如下: - MFC的映射數(shù)據(jù)保存在模塊-線程狀態(tài)中,是線程和模塊局部的。每個(gè)線程管理自己映射的數(shù)據(jù),其他線程不能訪問(wèn)到本線程的映射數(shù)據(jù),也就不允許使用本線程的MFC對(duì)象。
- 每一個(gè)MFC對(duì)象類(lèi)(CWnd、CDC等)負(fù)責(zé)創(chuàng)建或者管理這類(lèi)線程-模塊狀態(tài)的對(duì)應(yīng)CHandleMap類(lèi)對(duì)象。例如,CWnd::Attach創(chuàng)建一個(gè)永久性的映射保存在m_pmapHwnd所指對(duì)象中,如果m_pmapHand還沒(méi)有創(chuàng)建,則使用AfxMapHWND創(chuàng)建相應(yīng)的CHandleMap對(duì)象。
- 映射分兩類(lèi):永久性的或者臨時(shí)的。
- 臨時(shí)對(duì)象的處理
在2.4節(jié)就曾經(jīng)提到了臨時(shí)對(duì)象,現(xiàn)在是深入了解它們的時(shí)候了。 - 臨時(shí)對(duì)象指MFC對(duì)象,是MFC或者程序員使用FromHandle或者SelectObject等從一個(gè)Windows對(duì)象句柄創(chuàng)建的對(duì)應(yīng)的MFC對(duì)象。
- 在模塊-線程狀態(tài)中,臨時(shí)MFC對(duì)象的映射是和永久映射分開(kāi)保存的。
- 臨時(shí)MFC對(duì)象在使用完畢后由MFC框架自動(dòng)刪除,MFC在線程的Idle處理中刪除本線程的臨時(shí)MFC對(duì)象,為了防止并發(fā)修改,通過(guò)線程狀態(tài)m_nTempMapLock(等于0,可以修改,大于0,等待)來(lái)同步。所以,臨時(shí)MFC對(duì)象不能保存?zhèn)溆谩?p>
- 狀態(tài)對(duì)象的刪除和銷(xiāo)毀
至此,本章討論了MFC的線程局部存儲(chǔ)機(jī)制,MFC狀態(tài)的定義、實(shí)現(xiàn)和用途。在程序或者DLL退出之前,模塊狀態(tài)被銷(xiāo)毀;在線程退出時(shí),線程狀態(tài)被銷(xiāo)毀。狀態(tài)對(duì)象被銷(xiāo)毀之前,它活動(dòng)期間所動(dòng)態(tài)創(chuàng)建的對(duì)象被銷(xiāo)毀,動(dòng)態(tài)分配的內(nèi)存被釋放。 先解釋幾個(gè)函數(shù): AfxTermExtensionModule(HANDLE hInstanceOfDll,BOOL bAll); 若bAll為真,則該函數(shù)銷(xiāo)毀本模塊(hInstanceOfDll標(biāo)識(shí)的模塊)的模塊狀態(tài)的m_libraryList列表中所有動(dòng)態(tài)分配的CDynLinkLibrary對(duì)象,否則,該函數(shù)清理本DLL動(dòng)態(tài)分配的CDynLinkLibrary對(duì)象,并調(diào)用AfxTerLocalData釋放本DLL模塊為當(dāng)前線程的線程局部變量分配的堆空間。 AfxTermLocalData(HANDLE hInstance, BOOL bAll); 若bAll為真,則刪除MFC線程局部存儲(chǔ)的所有槽的指針?biāo)傅膶?duì)象,也就是銷(xiāo)毀當(dāng)前線程的全部局部變量,釋放為這些線程局部變量分配的內(nèi)存;否則,僅僅刪除、清理當(dāng)前線程在hInstance表示的DLL模塊中創(chuàng)建的線程局部變量。 參與清理工作的函數(shù)有多種、多個(gè),下面結(jié)合具體情況簡(jiǎn)要描述它們的作用。 (1)對(duì)動(dòng)態(tài)鏈接到MFC DLL的應(yīng)用程序 動(dòng)態(tài)鏈接到MFC DLL的應(yīng)用程序退出時(shí),將在DllMain和RawDllMain處理進(jìn)程分離時(shí)清理狀態(tài)對(duì)象,該DllMain和RawDllMain是核心MFC DLL的入口和出口,在DLLINIT.CPP文件中實(shí)現(xiàn),和進(jìn)程分離時(shí)完成如下動(dòng)作: DllMain調(diào)用AfxTermExtensionModule(coreDll)清理核心MFC DLL的模塊狀態(tài);調(diào)用AfxTermExtensionModule(coreDll, TRUE)清理OLE私有的模塊狀態(tài);調(diào)用AfxTermLocalData(NULL, TRUE)釋放本進(jìn)程或者線程所有的局部變量。 RawDllMain在DllMain之后調(diào)用,它調(diào)用AfxTlsRealease;AfxTlsRealease減少對(duì)_afxThreadData的引用計(jì)數(shù),如果引用數(shù)為零,則調(diào)用對(duì)應(yīng)的CThreadSlotData析構(gòu)函數(shù)清理_afxThreadData所指對(duì)象。 (2)對(duì)靜態(tài)鏈接到MFC DLL的應(yīng)用程序 如果是靜態(tài)鏈接到MFC DLL的應(yīng)用程序,由于RawDllMain和DllMain不起作用,將由一個(gè)靜態(tài)變量析構(gòu)時(shí)完成狀態(tài)的清除: 有一個(gè)AFX_TERM_APP_STATE類(lèi)型的靜態(tài)變量,在程序結(jié)束時(shí)將被銷(xiāo)毀,導(dǎo)致析構(gòu)函數(shù)被調(diào)用,析構(gòu)函數(shù)完成以下動(dòng)作: 調(diào)用AfxTermLocalData(NULL, TRUE)釋放本進(jìn)程(主線程)的所用局部數(shù)據(jù)。 (3)對(duì)于動(dòng)態(tài)鏈接到MFC DLL的規(guī)則DLL 對(duì)于動(dòng)態(tài)鏈接到MFC DLL的規(guī)則DLL,將在RawDllMain和DllMain中清理狀態(tài)對(duì)象。這兩個(gè)函數(shù)在DllModule.cpp中定義,是規(guī)則DLL的入口和出口。當(dāng)和進(jìn)程分離時(shí),分別有如下動(dòng)作: DllMain清除該模塊的模塊-線程狀態(tài)中的所有臨時(shí)映射,清除臨時(shí)MFC對(duì)象;調(diào)用AfxWinTerm;調(diào)用AfxTermExtensionModule(controlDLL, TRUE),釋放本DLL模塊狀態(tài)m_libraryList中的所有CDynLinkLibrary對(duì)象。 RawDllMain設(shè)置線程狀態(tài)的模塊狀態(tài)指針,使它指向線程狀態(tài)的m_PrevModuleState所指狀態(tài)。 (4)對(duì)于靜態(tài)鏈接到MFC DLL的DLL 對(duì)于靜態(tài)鏈接到MFC DLL的DLL,只有DllMain會(huì)被調(diào)用,執(zhí)行以下動(dòng)作: 清除該模塊的模塊-線程狀態(tài)中的所有臨時(shí)映射,清除臨時(shí)MFC對(duì)象;調(diào)用AfxWinTerm;調(diào)用AfxTermLocalData(hInstance, TRUE)清理本DLL模塊的當(dāng)前線程的線程局部數(shù)據(jù)。 另外,它定義一個(gè)_AFX_TERM_DLL_STATE類(lèi)型的靜態(tài)變量,在DLL退出時(shí)該變量被銷(xiāo)毀,導(dǎo)致其析構(gòu)函數(shù)被調(diào)用。析構(gòu)函數(shù)完成如下動(dòng)作: 調(diào)用AfxTermateLocalData(NULL, TRUE);調(diào)用AfxCriticlTerm結(jié)束關(guān)鍵變量;調(diào)用AfxTlsRealease。 (5)線程終止時(shí) 當(dāng)使用AFxBeginThread創(chuàng)建的線程終止時(shí),將調(diào)用AfxTermThread(HANDLE hInstance)作結(jié)束線程的清理工作(參數(shù)為NULL):銷(xiāo)毀臨時(shí)MFC對(duì)象,銷(xiāo)毀本線程的線程局部變量,等等。 另外,當(dāng)DLL模塊和AfxBeginThread創(chuàng)建的線程分離時(shí),也調(diào)用AfxTermThread(hInstance),參數(shù)是模塊的句柄,銷(xiāo)毀臨時(shí)MFC對(duì)象,銷(xiāo)毀本線程在本DLL創(chuàng)建的線程局部變量,等等。所以,AfxTermThread可能被調(diào)用兩次。 最后,CThreadLocal和CProcessLocal的實(shí)例將被銷(xiāo)毀,析構(gòu)函數(shù)被調(diào)用:如果MFC線程局部存儲(chǔ)空間的槽m_nSlot所指的線程局部對(duì)象還沒(méi)有銷(xiāo)毀,則銷(xiāo)毀它。 _afxThreadData在MFC DLL的RawDllMain或者隨著_AFX_TERM_APP_STATE析構(gòu)函數(shù)的調(diào)用,_afxThreadData所指對(duì)象被銷(xiāo)毀。_afxThreadData所指對(duì)象銷(xiāo)毀之后,所有的狀態(tài)相關(guān)的內(nèi)存都被釋放。
|