?? mfc7.php
字號:
<P
align=justify>該類DLL應(yīng)用程序動態(tài)鏈接到MFC,它輸出的函數(shù)僅可以被使用MFC且動態(tài)鏈接到MFC的應(yīng)用程序使用。和規(guī)則DLL相比,有以下不同:</P>
<OL>
<P align=justify>
<LI>它沒有一個從CWinApp派生的對象;
<P></P>
<P align=justify></P>
<LI>它必須有一個DllMain函數(shù);
<P></P>
<P align=justify></P>
<LI>DllMain調(diào)用AfxInitExtensionModule函數(shù),必須檢查該函數(shù)的返回值,如果返回0,DllMmain也返回0;
<P></P>
<P align=justify></P>
<LI>如果它希望輸出CRuntimeClass類型的對象或者資源(Resources),則需要提供一個初始化函數(shù)來創(chuàng)建一個CDynLinkLibrary對象。并且,有必要把初始化函數(shù)輸出。
<P></P>
<P align=justify></P>
<LI>使用擴(kuò)展DLL的MFC應(yīng)用程序必須有一個從CWinApp派生的類,而且,一般在InitInstance里調(diào)用擴(kuò)展DLL的初始化函數(shù)。
<P></P></LI></OL>
<P align=justify>為什么要這樣做和具體的代碼形式,將在后面9.4.2節(jié)說明。</P>
<P align=justify></P>
<P align=justify>MFC類庫也是以DLL的形式提供的。通常所說的動態(tài)鏈接到MFC
的DLL,指的就是實(shí)現(xiàn)MFC核心功能的MFCXX.DLL或者M(jìn)FCXXD.DLL(XX是版本號,XXD表示調(diào)試版)。至于提供OLE(MFCOXXD.DLL或者M(jìn)FCOXX0.DLL)和NET(MFCNXXD.DLL或者M(jìn)FCNXX.DLL)服務(wù)的DLL就是動態(tài)鏈接到MFC核心DLL的擴(kuò)展DLL。</P>
<P align=justify>其實(shí),MFCXX.DLL可以認(rèn)為是擴(kuò)展DLL的一個特例,因?yàn)樗簿邆鋽U(kuò)展DLL的上述特點(diǎn)。</P>
<OL>
<OL>
<P align=justify>
<LI><A name=_Toc445889074></A><A name=_Toc445782477></A><A
name=_Toc452640938></A><A name=_Toc457299036></A><B>DLL的幾點(diǎn)說明</B>
<P></P></LI></OL></OL>
<OL>
<P align=justify>
<LI>DLL應(yīng)用程序的入口點(diǎn)是DllMain。
<P></P>
<P align=justify>對程序員來說,DLL應(yīng)用程序的入口點(diǎn)是DllMain。</P>
<P
align=justify>DllMain負(fù)責(zé)初始化(Initialization)和結(jié)束(Termination)工作,每當(dāng)一個新的進(jìn)程或者該進(jìn)程的新的線程訪問DLL時,或者訪問DLL的每一個進(jìn)程或者線程不再使用DLL或者結(jié)束時,都會調(diào)用DllMain。但是,使用TerminateProcess或TerminateThread結(jié)束進(jìn)程或者線程,不會調(diào)用DllMain。</P>
<P align=justify>DllMain的函數(shù)原型符合DllEntryPoint的要求,有如下結(jié)構(gòu):</P>
<P align=justify>BOOL WINAPI DllMain (HANDLE hInst, </P>
<P align=justify>ULONG ul_reason_for_call,LPVOID lpReserved)</P>
<P align=justify>{</P>
<P align=justify>switch( ul_reason_for_call ) {</P>
<P align=justify>case DLL_PROCESS_ATTACH:</P>
<P align=justify>...</P>
<P align=justify>case DLL_THREAD_ATTACH:</P>
<P align=justify>...</P>
<P align=justify>case DLL_THREAD_DETACH:</P>
<P align=justify>...</P>
<P align=justify>case DLL_PROCESS_DETACH:</P>
<P align=justify>...</P>
<P align=justify>}</P>
<P align=justify>return TRUE;</P>
<P align=justify>}</P>
<P align=justify>其中:</P>
<P align=justify>參數(shù)1是模塊句柄;</P>
<P
align=justify>參數(shù)2是指調(diào)用DllMain的類別,四種取值:新的進(jìn)程要訪問DLL;新的線程要訪問DLL;一個進(jìn)程不再使用DLL(Detach
from DLL);一個線程不再使用DLL(Detach from DLL)。</P>
<P align=justify>參數(shù)3保留。</P>
<P align=justify>如果程序員不指定DllMain,則編譯器使用它自己的DllMain,該函數(shù)僅僅返回TRUE。</P>
<P
align=justify>規(guī)則DLL應(yīng)用程序使用了MFC的DllMain,它將調(diào)用DLL程序的應(yīng)用程序?qū)ο?從CWinApp派生)的InitInstance函數(shù)和ExitInstance函數(shù)。</P>
<P align=justify>擴(kuò)展DLL必須實(shí)現(xiàn)自己的DllMain。</P>
<P align=justify></P>
<LI>_DllMainCRTStartup
<P></P>
<P align=justify>為了使用“C”運(yùn)行庫(CRT,C Run time
Library)的DLL版本(多線程),一個DLL應(yīng)用程序必須指定_DllMainCRTStartup為入口函數(shù),DLL的初始化函數(shù)必須是DllMain。</P>
<P align=justify>_DllMainCRTStartup完成以下任務(wù):當(dāng)進(jìn)程或線程捆綁(Attach)到DLL時為“C”運(yùn)行時的數(shù)據(jù)(C
Runtime Data)分配空間和初始化并且構(gòu)造全局“C++”對象,當(dāng)進(jìn)程或者線程終止使用DLL(Detach)時,清理C Runtime
Data并且銷毀全局“C++”對象。它還調(diào)用DllMain和RawDllMain函數(shù)。</P>
<P align=justify>RawDllMain在DLL應(yīng)用程序動態(tài)鏈接到MFC
DLL時被需要,但它是靜態(tài)的鏈接到DLL應(yīng)用程序的。在講述狀態(tài)管理時解釋其原因。</P>
<P align=justify></P>
<LI>DLL的函數(shù)和數(shù)據(jù)
<P></P>
<P align=justify>DLL的函數(shù)分為兩類:輸出函數(shù)和內(nèi)部函數(shù)。輸出函數(shù)可以被其他模塊調(diào)用,內(nèi)部函數(shù)在定義它們的DLL程序內(nèi)部使用。</P>
<P align=justify>雖然DLL可以輸出數(shù)據(jù),但一般的DLL程序的數(shù)據(jù)僅供內(nèi)部使用。</P>
<P align=justify></P>
<P align=justify></P>
<LI>DLL程序和調(diào)用其輸出函數(shù)的程序的關(guān)系
<P></P></LI></OL>
<P align=justify>DLL模塊被映射到調(diào)用它的進(jìn)程的虛擬地址空間。</P>
<P align=justify>DLL使用的內(nèi)存從調(diào)用進(jìn)程的虛擬地址空間分配,只能被該進(jìn)程的線程所訪問。</P>
<P align=justify>DLL的句柄可以被調(diào)用進(jìn)程使用;調(diào)用進(jìn)程的句柄可以被DLL使用。</P>
<P align=justify>DLL使用調(diào)用進(jìn)程的棧。</P>
<P
align=justify>DLL定義的全局變量可以被調(diào)用進(jìn)程訪問;DLL可以訪問調(diào)用進(jìn)程的全局?jǐn)?shù)據(jù)。使用同一DLL的每一個進(jìn)程都有自己的DLL全局變量實(shí)例。如果多個線程并發(fā)訪問同一變量,則需要使用同步機(jī)制;對一個DLL的變量,如果希望每個使用DLL的線程都有自己的值,則應(yīng)該使用線程局部存儲(TLS,Thread
Local Strorage)。</P>
<OL>
<OL>
<P align=justify>
<LI><A name=_Toc445889075></A><A name=_Toc445782478></A><A
name=_Toc452640939></A><A name=_Toc457299037></A><B>輸出函數(shù)的方法</B>
<P></P></LI></OL></OL>
<OL>
<P align=justify>
<LI>傳統(tǒng)的方法
<P></P>
<P align=justify>在模塊定義文件的EXPORT部分指定要輸入的函數(shù)或者變量。語法格式如下:</P>
<P align=justify>entryname[=internalname] [@ordinal[NONAME]] [DATA]
[PRIVATE]</P>
<P align=justify>其中:</P>
<P align=justify>entryname是輸出的函數(shù)或者數(shù)據(jù)被引用的名稱;</P>
<P align=justify>internalname同entryname;</P>
<P align=justify>@ordinal表示在輸出表中的順序號(index);</P>
<P align=justify>NONAME僅僅在按順序號輸出時被使用(不使用entryname);</P>
<P align=justify>DATA表示輸出的是數(shù)據(jù)項(xiàng),使用DLL輸出數(shù)據(jù)的程序必須聲明該數(shù)據(jù)項(xiàng)為_declspec(dllimport)。</P>
<P align=justify>上述各項(xiàng)中,只有entryname項(xiàng)是必須的,其他可以省略。</P>
<P
align=justify>對于“C”函數(shù)來說,entryname可以等同于函數(shù)名;但是對“C++”函數(shù)(成員函數(shù)、非成員函數(shù))來說,entryname是修飾名。可以從.map映像文件中得到要輸出函數(shù)的修飾名,或者使用DUMPBIN
/SYMBOLS得到,然后把它們寫在.def文件的輸出模塊。DUMPBIN是VC提供的一個工具。</P>
<P align=justify>如果要輸出一個“C++”類,則把要輸出的數(shù)據(jù)和成員的修飾名都寫入.def模塊定義文件。</P>
<P align=justify></P>
<LI>在命令行輸出
<P></P>
<P align=justify>對鏈接程序LINK指定/EXPORT命令行參數(shù),輸出有關(guān)函數(shù)。</P>
<P align=justify></P>
<LI>使用MFC提供的修飾符號_declspec(dllexport)
<P></P></LI></OL>
<P
align=justify>在要輸出的函數(shù)、類、數(shù)據(jù)的聲明前加上_declspec(dllexport)的修飾符,表示輸出。MFC提供了一些宏,就有這樣的作用,如表7-2所示。</P>
<P align=center>表7-2 MFC定義的輸入輸出修飾符</P>
<P align=left>
<TABLE cellSpacing=1 cellPadding=7 width=406 border=1>
<TBODY>
<TR>
<TD vAlign=top width="45%">
<DIR>
<P align=justify>宏名稱 </P></DIR></TD>
<TD vAlign=top width="55%">
<P align=justify>宏內(nèi)容 </P></TD></TR>
<TR>
<TD vAlign=top width="45%">
<DIR>
<P align=justify>AFX_CLASS_IMPORT </P></DIR></TD>
<TD vAlign=top width="55%">
<P align=justify>__declspec(dllexport) </P></TD></TR>
<TR>
<TD vAlign=top width="45%">
<DIR>
<P align=justify>AFX_API_IMPORT </P></DIR></TD>
<TD vAlign=top width="55%">
<P align=justify>__declspec(dllexport) </P></TD></TR>
<TR>
<TD vAlign=top width="45%">
<DIR>
<P align=justify>AFX_DATA_IMPORT </P></DIR></TD>
<TD vAlign=top width="55%">
<P align=justify>__declspec(dllexport) </P></TD></TR>
<TR>
<TD vAlign=top width="45%">
<DIR>
<P align=justify>AFX_CLASS_EXPORT </P></DIR></TD>
<TD vAlign=top width="55%">
<P align=justify>__declspec(dllexport) </P></TD></TR>
<TR>
<TD vAlign=top width="45%">
<DIR>
<P align=justify>AFX_API_EXPORT </P></DIR></TD>
<TD vAlign=top width="55%">
<P align=justify>__declspec(dllexport) </P></TD></TR>
<TR>
<TD vAlign=top width="45%">
<DIR>
<P align=justify>AFX_DATA_EXPORT </P></DIR></TD>
<TD vAlign=top width="55%">
<P align=justify>__declspec(dllexport) </P></TD></TR>
<TR>
<TD vAlign=top width="45%">
<P align=justify>AFX_EXT_CLASS </P></TD>
<TD vAlign=top width="55%">
<P align=justify>#ifdef _AFXEXT</P>
<P align=justify>AFX_CLASS_EXPORT</P>
<P align=justify>#else</P>
<P align=justify>AFX_CLASS_IMPORT </P></TD></TR>
<TR>
<TD vAlign=top width="45%">
<P align=justify>AFX_EXT_API </P></TD>
<TD vAlign=top width="55%">
<P align=justify>#ifdef _AFXEXT</P>
<P align=justify>AFX_API_EXPORT</P>
<P align=justify>#else</P>
<P align=justify>AFX_API_IMPORT </P></TD></TR>
<TR>
<TD vAlign=top width="45%">
<P align=justify>AFX_EXT_DATA </P></TD>
<TD vAlign=top width="55%">
<P align=justify>#ifdef _AFXEXT</P>
<P align=justify>AFX_DATA_EXPORT</P>
<P align=justify>#else</P>
<P align=justify>AFX_DATA_IMPORT </P></TD></TR>
<TR>
<TD vAlign=top width="45%">
<P align=justify>AFX_EXT_DATADEF </P></TD>
<TD vAlign=top width="55%"> </TD></TR></TBODY></TABLE>
<P></P>
<P align=justify></P>
<P
align=justify>像AFX_EXT_CLASS這樣的宏,如果用于DLL應(yīng)用程序的實(shí)現(xiàn)中,則表示輸出(因?yàn)開AFX_EXT被定義,通常是在編譯器的標(biāo)識參數(shù)中指定該選項(xiàng)/D_AFX_EXT);如果用于使用DLL的應(yīng)用程序中,則表示輸入(_AFX_EXT沒有定義)。</P>
<P
align=justify>要輸出整個的類,對類使用_declspec(_dllexpot);要輸出類的成員函數(shù),則對該函數(shù)使用_declspec(_dllexport)。如:</P>
<P align=justify>class AFX_EXT_CLASS CTextDoc : public CDocument</P>
<P align=justify>{</P>
<P align=justify>…</P>
<P align=justify>}</P>
<P align=justify></P>
<P align=justify>extern "C" AFX_EXT_API void WINAPI InitMYDLL();</P>
<P align=justify></P>
<P align=justify>這幾種方法中,最好采用第三種,方便好用;其次是第一種,如果按順序號輸出,調(diào)用效率會高些;最次是第二種。</P>
<P align=justify>在“C++”下定義“C”函數(shù),需要加extern “C”關(guān)鍵詞。輸出的“C”函數(shù)可以從“C”代碼里調(diào)用。</P>
<hr>
<table width="100%" border="0" cellspacing="0" cellpadding="0" align="center">
<tr>
<td align="center"><a href="mfc6.php" target="_self">上一章</a> <a href="mfc.php" target="_self">回目錄</a> <a href="mfc8.php" target="_self">下一章</a></td>
</tr>
</table>
<p> </p>
<P align=justify></P>
</BODY></HTML>
?? 快捷鍵說明
復(fù)制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -