?? mfc教程9.htm
字號:
<LI>一個進程(使用MFC的應用程序)的主線程創建線程模塊狀態和進程模塊狀態,前者是_AFX_THREAD_STATE類的實例,后者是_AFX_BASE_MODULE_STATE類的實例。
<P></P>
<P align=justify></P>
<LI>當進程的新的線程被創建時,它創建自己的線程狀態,繼承父線程的模塊狀態。在線程的入口函數_AfxThreadEntry完成這樣的處理,該函數的描述見8.5.3節。
<P></P></LI></UL>
<OL>
<OL>
<OL>
<P align=justify>
<LI><A name=_Toc445889112></A><A name=_Toc445782515></A><A
name=_Toc452640975></A><A name=_Toc457299078></A><B>擴展DLL的模塊狀態</B>
<P></P>
<P
align=justify>7.3.1節指出擴展DLL的實現必須遵循五條規則,為此,首先在擴展DLL實現文件里頭,定義AFX_EXTENSION_MODULE類型的靜態擴展模塊變量,然后在DllMain入口函數里頭使用AfxInitExtension初始化擴展模塊變量,并且實現和輸出一個初始化函數供擴展DLL的使用者調用。</P>
<P
align=justify>使用者必須具備一個CWinApp對象,通常在它的InitInstance函數中調用擴展DLL提供的初始化函數。</P>
<P align=justify>一般用以下的幾段代碼完成上述任務。首先是擴展模塊變量的定義和初始化:</P>
<P align=justify>static AFX_EXTENSION_MODULE extensionDLL;</P>
<P align=justify>DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID)</P>
<P align=justify>{</P>
<P align=justify>if (dwReason == DLL_PROCESS_ATTACH)</P>
<P align=justify>{</P>
<P align=justify>// Extension DLL one-time initialization</P>
<P align=justify>if (!AfxInitExtensionModule(extensionDLL,hInstance))</P>
<P align=justify>return 0;</P>
<P align=justify>……</P>
<P align=justify>}</P>
<P align=justify>}</P>
<P
align=justify>然后是擴展DLL的初始化函數,假定初始化函數命名為InitMyDll,InitMyDll被定義為“C”鏈接的全局函數,并且被輸出。</P>
<P align=justify>// wire up this DLL into the resource chain</P>
<P align=justify>extern “C” void WINAPI InitMyDll()</P>
<P align=justify>{</P>
<P align=justify>CDynLinkLibrary* pDLL = new</P>
<P align=justify>CDynLinkLibrary(extensionDLL, TRUE);</P>
<P align=justify>ASSERT(pDLL != NULL);</P>
<P align=justify>...</P>
<P align=justify>}</P>
<P align=justify>最后是調用者的處理,假定在應用程序對象的InitInstance函數中調用初始化函數:</P>
<P align=justify>BOOL CMyApp::InitInstance()</P>
<P align=justify>{</P>
<P align=justify>InitMyMyDll();</P>
<P align=justify>…</P>
<P align=justify>}</P>
<P align=justify>上述這些代碼只有在動態鏈接到MFC DLL時才有用。下面,對這些代碼進行分析和解釋</P>
<OL>
<P align=justify>
<LI><A name=_Toc445889113></A><A name=_Toc445782516></A><A
name=_Toc457299079></A><B>_AFX_EXTENSION_MODULE</B>
<P></P>
<P
align=justify>在分析代碼之前,先討論描述擴展模塊狀態的_AFX_EXTENSION_MODULE類。_AFX_EXTENSION_MODULE沒有基類,其定義如下:</P>
<P align=justify>struct AFX_EXTENSION_MODULE</P>
<P align=justify>{</P>
<P align=justify>BOOL bInitialized;</P>
<P align=justify>HMODULE hModule;</P>
<P align=justify>HMODULE hResource;</P>
<P align=justify>CRuntimeClass* pFirstSharedClass;</P>
<P align=justify>COleObjectFactory* pFirstSharedFactory;</P>
<P align=justify>};</P>
<P align=justify>其中:</P>
<P align=justify>第一個域表示該結構變量是否已經被初始化了;</P>
<P align=justify>第二個域用來保存擴展DLL的模塊句柄;</P>
<P align=justify>第三個域用來保存擴展DLL的資源句柄;</P>
<P align=justify>第四個域用來保存擴展DLL要輸出的CRuntimeClass類;</P>
<P align=justify>第五個域用來保存擴展DLL的OLE Factory。</P>
<P
align=justify>該結構用來描述一個擴展DLL的模塊狀態信息,每一個擴展DLL都要定義一個該類型的靜態變量,例如extensionDLL。</P>
<P align=justify></P>
<P
align=justify>在DllMain中,調用AfxInitExtensionModule函數來初始化本DLL的靜態變量該變量(擴展模塊狀態),如extensionDLL。函數AfxInitExtensionModule原型如下:</P>
<P align=justify>BOOL AFXAPI AfxInitExtensionModule(</P>
<P align=justify>AFX_EXTENSION_MODULE& state, HMODULE hModule)</P>
<P align=justify>其中:</P>
<P align=justify>參數1是DllMain傳遞給它的擴展DLL的模塊狀態,如extensionDLL;</P>
<P align=justify>參數2是DllMain傳遞給它的模塊句柄。</P>
<P align=justify>AfxInitExtensionModule函數主要作以下事情:</P>
<P
align=justify>(1)把擴展DLL模塊的模塊句柄hModule、資源句柄hModule分別保存到參數state的成員變量hModule、hResource中;</P>
<P
align=justify>(2)把當前模塊狀態的m_classList列表的頭保存到state的成員變量pFirstSharedClass中,m_classInit的頭設置為模塊狀態的m_pClassInit。在擴展DLL模塊進入DllMain之前,如果該擴展模塊構造了靜態AFX_CLASSINIT對象,則在初始化時把有關CRuntimeClass信息保存在當前模塊狀態(注意不是擴展DLL模塊,而是應用程序模塊)的m_classList列表中。因此,擴展DLL模塊初始化的CRuntimeClass信息從模塊狀態的m_classList中轉存到擴展模塊狀態state的pFirstSharedClass中,模塊狀態的m_classInit恢復被該DLL改變前的狀態。</P>
<P
align=justify>關于CRuntimeclass信息和AFX_CLASSINIT對象的構造,在3.3.1節曾經討論過。一個擴展DLL在初始化時,如果需要輸出它的CRuntimeClass對象,就可以使用相應的CRuntimeClass對象定義一個靜態的AFX_CLASSINIT對象,而不一定要使用IMPLEMENT_SERIAL宏。當然,可以序列化的類必定導致可以輸出的CRuntimeClass對象。</P>
<P
align=justify>(3)若支持OLE的話,把當前模塊狀態的m_factoryList的頭保存到state的成員變量pFirstSharedFactory中。m_factoryList的頭設置為模塊狀態的m_m_pFactoryInit。</P>
<P
align=justify>(4)這樣,經過初始化之后,擴展DLL模塊包含了擴展DLL的模塊句柄、資源句柄、本模塊初始化的CRuntimeClass類等等。</P>
<P align=justify>擴展DLL的初始化函數將使用擴展模塊狀態信息。下面,討論初始化函數的作用。</P>
<P align=justify></P>
<LI><A name=_Toc445889114></A><A name=_Toc445782517></A><A
name=_Toc457299080></A><B>擴展DLL的初始化函數</B>
<P></P></LI></OL>
<P
align=justify>在初始化函數InitMyDll中,創建了一個動態的CDynLinkLibrary對象,并把對象指針保存在pDLL中。CDynLinkLibrary類從CCmdTarget派生,定義如下:</P>
<P align=justify>class CDynLinkLibrary : public CCmdTarget</P>
<P align=justify>{</P>
<P align=justify>DECLARE_DYNAMIC(CDynLinkLibrary)</P>
<P align=justify>public:</P>
<P align=justify>// Constructor</P>
<P align=justify>CDynLinkLibrary(AFX_EXTENSION_MODULE& state,</P>
<P align=justify>BOOL bSystem = FALSE);</P>
<P align=justify></P>
<P align=justify>// Attributes</P>
<P align=justify>HMODULE m_hModule;</P>
<P align=justify>HMODULE m_hResource; // for shared resources</P>
<P align=justify>CTypedSimpleList<CRuntimeClass*> m_classList;</P>
<P align=justify>#ifndef _AFX_NO_OLE_SUPPORT</P>
<P align=justify>CTypedSimpleList<COleObjectFactory*>
m_factoryList;</P>
<P align=justify>#endif</P>
<P align=justify>BOOL m_bSystem; // TRUE only for MFC DLLs</P>
<P align=justify></P>
<P align=justify>// Implementation</P>
<P align=justify>public:</P>
<P align=justify>CDynLinkLibrary* m_pNextDLL; // simple singly linked
list</P>
<P align=justify>virtual ~CDynLinkLibrary();</P>
<P align=justify></P>
<P align=justify>#ifdef _DEBUG</P>
<P align=justify>virtual void AssertValid() const;</P>
<P align=justify>virtual void Dump(CDumpContext& dc) const;</P>
<P align=justify>#endif //_DEBUG</P>
<P align=justify>};</P>
<P
align=justify>CDynLinkLibrary的結構和AFX_EXTENSION_MODULE有一定的相似性,存在對應關系。</P>
<P
align=justify>CDynLinkLibrary構造函數的第一個參數就是經過AfxInitExtensionModule初始化后的擴展DLL的模塊狀態,如extensionDLL,第二個參數表示該DLL模塊是否是系統模塊。</P>
<P
align=justify>創建CDynLinkLibrary對象導致CCmdTarget和CDynLinkLibrary類的構造函數被調用。CCmdTarget的構造函數將獲取模塊狀態并且保存在成員變量m_pModuleState中。CDynLinkLibrary的構造函數完成以下動作:</P>
<P align=justify>構造列表m_classList和m_factoryList;</P>
<P
align=justify>把參數state的域hModule、hResource復制到對應的成員變量m_hModule、m_hResource中;</P>
<P
align=justify>把state的pFirstSharedClass、pFirstSharedFactory分別插入到m_classList列表、m_factoryList列表的表頭;</P>
<P align=justify>把參數2的值賦值給成員變量m_bSystem中;</P>
<P
align=justify>至此,CDynLinkLibrary對象已經構造完畢。之后,CDynLinkLibrary構造函數把CDynLinkLibrary對象自身添加到當前模塊狀態(調用擴展DLL的應用程序模塊或者規則DLL模塊)的CDynLinkLibrary列表m_libraryList的表頭。為了防止多個線程修改模塊狀態的m_libraryList,訪問m_libraryList時使用了同步機制。</P>
<P align=justify>這樣,調用模塊執行完擴展模塊的初始化函數之后,就把該擴展DLL的資源、CRuntimeClass類、OLE
Factory等鏈接到調用者的模塊狀態中,形成一個鏈表。圖9-8表明了這種關系鏈。</P>
<P align=justify></P>
<P align=justify>綜合以上分析,可以知道:</P>
<P
align=justify>擴展DLL的模塊僅僅在該DLL調用DllMain期間和調用初始化函數期間被使用,在這些初始化完畢之后,擴展DLL模塊被鏈接到當前調用模塊的模塊狀態中,因此它所包含的資源信息等也就被鏈接到調用擴展DLL的應用程序或者規則DLL的模塊狀態中了。擴展DLL擴展了調用者的資源等,這是“擴展DLL”得名的原因之一。</P>
<P
align=justify>也正因為擴展DLL沒有自己的模塊狀態(指AFX_MODULE_STATE對象,擴展DLL模塊狀態不是),而且必須由有模塊狀態的模塊來使用,所以只有動態鏈接到MFC的應用程序或者規則DLL才可以使用擴展DLL模塊的輸出函數或者輸出類。</P>
<P align=justify></P>
<LI><A name=_Toc445889111></A><A name=_Toc445782514></A><A
name=_Toc452640976></A><A name=_Toc457299081></A><B>核心MFC DLL</B>
<P></P>
<P align=justify>所謂核心MFC DLL,就是MFC核心類庫形成的DLL,通常說動態鏈接到MFC,就是指核心MFC DLL。</P>
<P align=justify>核心MFC
DLL實際上也是一種擴展DLL,因為它定義了自己的擴展模塊狀態coreDLL,實現了自己的DllMain函數,使用AfxInitExtensionModule初始化核心DLL的擴展模塊狀態coreDLL,并且DllMain還創建了CDynLinkLibrary,把核心DLL的擴展模塊狀態coreDLL鏈接到當前應用程序的模塊狀態中。所有這些,都符合擴展DLL的處理標準。</P>
<P align=justify>但是,核心MFC
DLL是一種特殊的擴展DLL,因為它定義和實現了MFC類庫,模塊狀態、線程狀態、進程狀態、狀態管理和使用的機制就是核心MFC
DLL定義和實現的。例如核心MFC
DLL定義和輸出的模塊狀態變量,即_afxBaseModuleState,就是動態鏈接到MFC的DLL的應用程序的模塊狀態。</P>
<P align=justify>但是MFC DLL不作為獨立的模塊表現出來,而是把自己作為一個擴展模塊來處理。當應用程序動態鏈接到MFC
DLL時,MFC
DLL把自己的擴展模塊狀態coreDLL鏈接到模塊狀態afxBaseModuleState,模塊狀態的成員變量m_hCurrentInstanceHandle指定為應用程序的句柄。當規則DLL動態鏈接到MFC
DLL時,由規則DLL的DllMain把核心MFC
DLL的擴展模塊狀態coreDLL鏈接到規則DLL的模塊狀態afxModuleState中,模塊狀態afxModuleState的m_hCurrentInstanceHandle指定為規則DLL的句柄。</P>
<P align=justify>關于afxModuleState和規則DLL的模塊狀態,見下一節的討論。</P>
<P align=justify></P>
<LI><A name=_Toc452640977></A><A
name=_Toc457299082></A><B>動態鏈接的規則DLL的模塊狀態的實現</B>
<P></P></LI></OL></OL></OL>
<P align=justify>在本節中,動態鏈接到MFC DLL(定義了_AFXDLL)的規則DLL在下文簡稱為規則DLL。</P>
<P align=justify>(1)規則DLL的模塊狀態的定義</P>
<P align=justify>規則DLL有自己的模塊狀態_afxModuleState,它是一個靜態變量,定義如下:</P>
<P align=justify>static _AFX_DLL_MODULE_STATE afxModuleState;</P>
<P align=justify>_AFX_DLL_MODULE_STATE的基類是AFX_MODULE_STATE。</P>
<P align=justify>在前面的模塊狀態切換中提到的AfxGetStaticModuleState函數,其定義和實現如下:</P>
<P align=justify>_AFX_MODULE_STATE* AFXAPI AfxGetStaticModuleState()</P>
<P align=justify>{</P>
<P align=justify>AFX_MODULE_STATE* pModuleState = &afxModuleState;</P>
<P align=justify>return pModuleState;</P>
<P align=justify>}</P>
<P align=justify>它返回規則DLL的模塊狀態afxModuleState。</P>
<P
align=justify>規則DLL的內部函數使用afxModuleState作為模塊狀態;輸出函數在被調用的時候首先切換到該模塊狀態,然后進一步處理。</P>
<P align=justify>(2)規則DLL的模塊狀態的初始化</P>
<P align=justify>從用戶角度來看,動態鏈接到MFC
DLL的規則DLL不需要DllMain函數,只要提供CWinApp對象即可。其實,MFC內部是在實現擴展DLL的方法基礎上來實現規則DLL的,它不僅為規則DLL提供了DllMain函數,而且規則DLL也有擴展DLL模塊狀態controlDLL。</P>
<P
align=justify>順便指出,和擴展DLL相比,規則DLL有一個CWinApp(或其派生類)應用程序對象和一個模塊狀態afxModuleState。應用程序對象是全局對象,所以在進入規則DLL的DllMain之前已經被創建,DllMain可以調用它的初始化函數InitInstance。模塊狀態afxModuleState是靜態全局變量,也在進入DllMain之前被創建,DllMain訪問模塊狀態時得到的就是該變量。擴展DLL是沒有CWinApp對象和模塊狀態的,它只能使用應用程序或者規則DLL的CWinApp對象和模塊狀態。</P>
<P align=justify>由于核心MFC
DLL的DllMain被調用的時候,訪問的必定是應用程序的模塊狀態,要把核心DLL的擴展模塊狀態鏈接到規則DLL的模塊狀態中,必須通過規則DLL的DllMain來實現。</P>
<P
align=justify>規則DLL的DllMain(MFC內部實現)把參數1表示的模塊和資源句柄通過AfxWinInit函數保存到規則DLL的模塊狀態中。順便指出,WinMain也通過AfxWinInit函數把資源和模塊句柄保存到應用程
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -