?? mfc教程_ mfc的dll.htm
字號:
<P
align=justify>該類DLL應用程序動態鏈接到MFC,它輸出的函數僅可以被使用MFC且動態鏈接到MFC的應用程序使用。和規則DLL相比,有以下不同:</P>
<OL>
<P align=justify>
<LI>它沒有一個從CWinApp派生的對象;
<P></P>
<P align=justify></P>
<LI>它必須有一個DllMain函數;
<P></P>
<P align=justify></P>
<LI>DllMain調用AfxInitExtensionModule函數,必須檢查該函數的返回值,如果返回0,DllMmain也返回0;
<P></P>
<P align=justify></P>
<LI>如果它希望輸出CRuntimeClass類型的對象或者資源(Resources),則需要提供一個初始化函數來創建一個CDynLinkLibrary對象。并且,有必要把初始化函數輸出。
<P></P>
<P align=justify></P>
<LI>使用擴展DLL的MFC應用程序必須有一個從CWinApp派生的類,而且,一般在InitInstance里調用擴展DLL的初始化函數。
<P></P></LI></OL>
<P align=justify>為什么要這樣做和具體的代碼形式,將在后面9.4.2節說明。</P>
<P align=justify></P>
<P align=justify>MFC類庫也是以DLL的形式提供的。通常所說的動態鏈接到MFC
的DLL,指的就是實現MFC核心功能的MFCXX.DLL或者MFCXXD.DLL(XX是版本號,XXD表示調試版)。至于提供OLE(MFCOXXD.DLL或者MFCOXX0.DLL)和NET(MFCNXXD.DLL或者MFCNXX.DLL)服務的DLL就是動態鏈接到MFC核心DLL的擴展DLL。</P>
<P align=justify>其實,MFCXX.DLL可以認為是擴展DLL的一個特例,因為它也具備擴展DLL的上述特點。</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的幾點說明</B>
<P></P></LI></OL></OL>
<OL>
<P align=justify>
<LI>DLL應用程序的入口點是DllMain。
<P></P>
<P align=justify>對程序員來說,DLL應用程序的入口點是DllMain。</P>
<P
align=justify>DllMain負責初始化(Initialization)和結束(Termination)工作,每當一個新的進程或者該進程的新的線程訪問DLL時,或者訪問DLL的每一個進程或者線程不再使用DLL或者結束時,都會調用DllMain。但是,使用TerminateProcess或TerminateThread結束進程或者線程,不會調用DllMain。</P>
<P align=justify>DllMain的函數原型符合DllEntryPoint的要求,有如下結構:</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>參數1是模塊句柄;</P>
<P
align=justify>參數2是指調用DllMain的類別,四種取值:新的進程要訪問DLL;新的線程要訪問DLL;一個進程不再使用DLL(Detach
from DLL);一個線程不再使用DLL(Detach from DLL)。</P>
<P align=justify>參數3保留。</P>
<P align=justify>如果程序員不指定DllMain,則編譯器使用它自己的DllMain,該函數僅僅返回TRUE。</P>
<P
align=justify>規則DLL應用程序使用了MFC的DllMain,它將調用DLL程序的應用程序對象(從CWinApp派生)的InitInstance函數和ExitInstance函數。</P>
<P align=justify>擴展DLL必須實現自己的DllMain。</P>
<P align=justify></P>
<LI>_DllMainCRTStartup
<P></P>
<P align=justify>為了使用“C”運行庫(CRT,C Run time
Library)的DLL版本(多線程),一個DLL應用程序必須指定_DllMainCRTStartup為入口函數,DLL的初始化函數必須是DllMain。</P>
<P align=justify>_DllMainCRTStartup完成以下任務:當進程或線程捆綁(Attach)到DLL時為“C”運行時的數據(C
Runtime Data)分配空間和初始化并且構造全局“C++”對象,當進程或者線程終止使用DLL(Detach)時,清理C Runtime
Data并且銷毀全局“C++”對象。它還調用DllMain和RawDllMain函數。</P>
<P align=justify>RawDllMain在DLL應用程序動態鏈接到MFC
DLL時被需要,但它是靜態的鏈接到DLL應用程序的。在講述狀態管理時解釋其原因。</P>
<P align=justify></P>
<LI>DLL的函數和數據
<P></P>
<P align=justify>DLL的函數分為兩類:輸出函數和內部函數。輸出函數可以被其他模塊調用,內部函數在定義它們的DLL程序內部使用。</P>
<P align=justify>雖然DLL可以輸出數據,但一般的DLL程序的數據僅供內部使用。</P>
<P align=justify></P>
<P align=justify></P>
<LI>DLL程序和調用其輸出函數的程序的關系
<P></P></LI></OL>
<P align=justify>DLL模塊被映射到調用它的進程的虛擬地址空間。</P>
<P align=justify>DLL使用的內存從調用進程的虛擬地址空間分配,只能被該進程的線程所訪問。</P>
<P align=justify>DLL的句柄可以被調用進程使用;調用進程的句柄可以被DLL使用。</P>
<P align=justify>DLL使用調用進程的棧。</P>
<P
align=justify>DLL定義的全局變量可以被調用進程訪問;DLL可以訪問調用進程的全局數據。使用同一DLL的每一個進程都有自己的DLL全局變量實例。如果多個線程并發訪問同一變量,則需要使用同步機制;對一個DLL的變量,如果希望每個使用DLL的線程都有自己的值,則應該使用線程局部存儲(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>輸出函數的方法</B>
<P></P></LI></OL></OL>
<OL>
<P align=justify>
<LI>傳統的方法
<P></P>
<P align=justify>在模塊定義文件的EXPORT部分指定要輸入的函數或者變量。語法格式如下:</P>
<P align=justify>entryname[=internalname] [@ordinal[NONAME]] [DATA]
[PRIVATE]</P>
<P align=justify>其中:</P>
<P align=justify>entryname是輸出的函數或者數據被引用的名稱;</P>
<P align=justify>internalname同entryname;</P>
<P align=justify>@ordinal表示在輸出表中的順序號(index);</P>
<P align=justify>NONAME僅僅在按順序號輸出時被使用(不使用entryname);</P>
<P align=justify>DATA表示輸出的是數據項,使用DLL輸出數據的程序必須聲明該數據項為_declspec(dllimport)。</P>
<P align=justify>上述各項中,只有entryname項是必須的,其他可以省略。</P>
<P
align=justify>對于“C”函數來說,entryname可以等同于函數名;但是對“C++”函數(成員函數、非成員函數)來說,entryname是修飾名。可以從.map映像文件中得到要輸出函數的修飾名,或者使用DUMPBIN
/SYMBOLS得到,然后把它們寫在.def文件的輸出模塊。DUMPBIN是VC提供的一個工具。</P>
<P align=justify>如果要輸出一個“C++”類,則把要輸出的數據和成員的修飾名都寫入.def模塊定義文件。</P>
<P align=justify></P>
<LI>在命令行輸出
<P></P>
<P align=justify>對鏈接程序LINK指定/EXPORT命令行參數,輸出有關函數。</P>
<P align=justify></P>
<LI>使用MFC提供的修飾符號_declspec(dllexport)
<P></P></LI></OL>
<P
align=justify>在要輸出的函數、類、數據的聲明前加上_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>宏內容 </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應用程序的實現中,則表示輸出(因為_AFX_EXT被定義,通常是在編譯器的標識參數中指定該選項/D_AFX_EXT);如果用于使用DLL的應用程序中,則表示輸入(_AFX_EXT沒有定義)。</P>
<P
align=justify>要輸出整個的類,對類使用_declspec(_dllexpot);要輸出類的成員函數,則對該函數使用_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>這幾種方法中,最好采用第三種,方便好用;其次是第一種,如果按順序號輸出,調用效率會高些;最次是第二種。</P>
<P align=justify>在“C++”下定義“C”函數,需要加extern “C”關鍵詞。輸出的“C”函數可以從“C”代碼里調用。</P>
<HR>
<TABLE cellSpacing=0 cellPadding=0 width="100%" align=center border=0>
<TBODY>
<TR>
<TD align=middle><A href="http://www.vczx.com/tutorial/mfc/mfc6.php"
target=_self>上一章</A> <A href="http://www.vczx.com/tutorial/mfc/mfc.php"
target=_self>回目錄</A> <A href="http://www.vczx.com/tutorial/mfc/mfc8.php"
target=_self>下一章</A></TD></TR></TBODY></TABLE>
<P> </P>
<P align=justify></P></BODY></HTML>
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -