一区二区三区日韩精品-日韩经典一区二区三区-五月激情综合丁香婷婷-欧美精品中文字幕专区

分享

第九章 MFC的狀態(tài)

 Alex@ZW 2010-11-05
  1. MFC的狀態(tài)

     

    MFC定義了多種狀態(tài)信息,這里要介紹的是模塊狀態(tài)、進(jìn)程狀態(tài)、線程狀態(tài)。這些狀態(tài)可以組合在一起,例如MFC句柄映射就是模塊和線程局部有效的,屬于模塊-線程狀態(tài)的一部分。

    1. 模塊狀態(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)。

    2. 模塊、進(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ù)等。

      1. 層次關(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)的定義。

      2. 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)。

      3. 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。

      4. _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)信息。

      5. _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)的變量,等等。

      6. 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)的管理。

    3. 線程局部存儲(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è)變量。

      1. CThreadSlotData和_afxThreadData

         

        1. 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ù)指針。

        2. 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。

        3. _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ù)。

      1. 線程狀態(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)。

      2. 進(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ì)象。

      3. 狀態(tài)對(duì)象的創(chuàng)建

         

        1. 狀態(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中,返回該指針。

  • 創(chuàng)建模塊狀態(tài)

     

得到了線程狀態(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)的創(chuàng)建

     

模塊狀態(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槽中。

        1. 創(chuàng)建過(guò)程所涉及的幾個(gè)重要函數(shù)的算法

           

創(chuàng)建過(guò)程所涉及的幾個(gè)重要函數(shù)的算法描述如下:

  1. 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)

    }

  2. 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

    }

  3. 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

}

    1. 管理狀態(tài)

       

      在描述了MFC狀態(tài)的實(shí)現(xiàn)機(jī)制之后,現(xiàn)在來(lái)討論MFC的狀態(tài)管理和相關(guān)狀態(tài)的作用。

      1. 模塊狀態(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)合是不一樣的,例如,

  • DLL的輸出函數(shù)使用

     

AFX_MANAGE_SATATE(AfxGetStaticModuleState());

其中,AfxGetStaticModuleState返回DLL的模塊狀態(tài)afxModuleState。

  • 窗口函數(shù)使用

     

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é)。

     

      1. 擴(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)行分析和解釋

        1. _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ù)的作用。

        2. 擴(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)。

      2. 核心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é)的討論。

      3. 動(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)切換。

    1. 狀態(tài)信息的作用

       

      在分析了MFC模塊狀態(tài)的實(shí)現(xiàn)基礎(chǔ)和管理機(jī)制之后,現(xiàn)在對(duì)狀態(tài)信息的作用進(jìn)行專(zhuān)門(mén)的討論。

        1. 模塊信息的保存和管理

           

        傳統(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ō)明。

      1. MFC資源、運(yùn)行類(lèi)信息的查找

         

MFC內(nèi)部使用的資源查找函數(shù)是:

HINSTANCE AfxFindResourceHandle(LPCTSTR lpszName, LPCTSTR lpszType):

其中:

參數(shù)1是要查找的資源名稱(chēng),參數(shù)2是要查找的資源類(lèi)型。

返回包含指定資源的模塊的句柄。

上述函數(shù)的查找算法如下:

  1. 如果進(jìn)程模塊狀態(tài)(主模塊)不是系統(tǒng)模塊,則使用::FindResource(下同)搜索它,成功則返回;

     

  2. 如果沒(méi)有找到,則遍歷CDynLinkLibrary對(duì)象列表,搜索所有的非系統(tǒng)模塊,成功則返回;

     

  3. 如果沒(méi)有找到,則檢查主模塊的語(yǔ)言資源,成功則返回;

     

  4. 如果沒(méi)有找到,并且主模塊是系統(tǒng)模塊,則搜索它,成功則返回;

     

  5. 如果沒(méi)有找到,則遍歷CDynLinkLibrary對(duì)象列表,搜索所有的系統(tǒng)模塊,成功則返回;

     

  6. 如果沒(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)

  1. 遍歷主模塊的CRuntimeClass對(duì)象列表m_classList,搜索主模塊是否實(shí)現(xiàn)了指定的CRuntimeClass類(lèi);

     

  2. 遍歷CDynLinkLibrary對(duì)象列表m_libraryList;對(duì)每一個(gè)CDynLinkLibrary對(duì)象,遍歷它的CRuntimeClass對(duì)象列表m_classList。這樣,所有的擴(kuò)展DLL模塊的CRuntimeClass對(duì)象都會(huì)被搜索到。

     

      1. 模塊信息的顯示

         

        遍歷模塊狀態(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ò)增加了模塊名和模塊句柄信息。

      2. 模塊-線程狀態(tài)的作用

         

        由模塊-線程狀態(tài)類(lèi)的定義可知,一個(gè)模塊-線程狀態(tài)包含了幾類(lèi)Windows對(duì)象—MFC對(duì)象的映射。下面討論它們的作用。

        1. 只能訪問(wèn)本線程MFC對(duì)象的原因

           

MFC規(guī)定:

  1. 不能從一個(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ì)象。

  2. 一個(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)的。

        1. 實(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()

     

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ì)象。

  • AssertValid()

     

它是從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í)的。

     

        1. 臨時(shí)對(duì)象的處理

           

在2.4節(jié)就曾經(jīng)提到了臨時(shí)對(duì)象,現(xiàn)在是深入了解它們的時(shí)候了。

  1. 臨時(shí)對(duì)象指MFC對(duì)象,是MFC或者程序員使用FromHandle或者SelectObject等從一個(gè)Windows對(duì)象句柄創(chuàng)建的對(duì)應(yīng)的MFC對(duì)象。

     

  2. 在模塊-線程狀態(tài)中,臨時(shí)MFC對(duì)象的映射是和永久映射分開(kāi)保存的。

     

  3. 臨時(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> 

    1. 狀態(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)存都被釋放。


    本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶(hù)發(fā)布,不代表本站觀點(diǎn)。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購(gòu)買(mǎi)等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評(píng)論

    發(fā)表

    請(qǐng)遵守用戶(hù) 評(píng)論公約

    類(lèi)似文章 更多

    激情丁香激情五月婷婷| 国产又粗又猛又爽又黄| 蜜臀人妻一区二区三区| 一二区不卡不卡在线观看| 日韩三级黄色大片免费观看| 国产又猛又大又长又粗| 视频在线免费观看你懂的| 国产在线视频好看不卡| 观看日韩精品在线视频| 中文字幕在线区中文色| 国产av一区二区三区久久不卡 | 国产麻豆成人精品区在线观看 | 欧美日韩国产二三四区| 欧美色婷婷综合狠狠爱| 高中女厕偷拍一区二区三区| 久久亚洲午夜精品毛片| 色婷婷视频免费在线观看| 69老司机精品视频在线观看| 熟女乱一区二区三区四区| 国产白丝粉嫩av在线免费观看| 日韩中文字幕欧美亚洲| 91人妻久久精品一区二区三区| 高清欧美大片免费在线观看| 国内外免费在线激情视频| 欧美丰满人妻少妇精品| 国产在线成人免费高清观看av| 成人亚洲国产精品一区不卡| 国产欧美日韩综合精品二区| 五月天丁香亚洲综合网| 亚洲国产成人精品一区刚刚| 中文字幕人妻日本一区二区| 国产又粗又猛又爽又黄| 一区二区三区亚洲天堂| 内射精子视频欧美一区二区| 国产亚洲欧美日韩国亚语| 成人午夜在线视频观看| 成人精品一级特黄大片| 少妇高潮呻吟浪语91| 午夜色午夜视频之日本| 女人高潮被爽到呻吟在线观看| 国产精品流白浆无遮挡|