日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

DLL 的导入与导出

發布時間:2024/4/17 编程问答 44 豆豆
生活随笔 收集整理的這篇文章主要介紹了 DLL 的导入与导出 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

動態鏈接庫(DLL)是從C語言函數庫和Pascal庫單元的概念發展而來的。所有的C語言標準庫函數都存放在某一函數庫中。在鏈接應用程序的過程中,鏈接器從庫文件中拷貝程序調用的函數代碼,并把這些函數代碼添加到可執行文件中。這種方法同只把函數儲存在已編譯的OBJ文件中相比更有利于代碼的重用。但隨著Windows這樣的多任務環境的出現,函數庫的方法顯得過于累贅。如果為了完成屏幕輸出、消息處理、內存管理、對話框等操作,每個程序都不得不擁有自己的函數,那么Windows程序將變得非常龐大。Windows的發展要求允許同時運行的幾個程序共享一組函數的單一拷貝。動態鏈接庫就是在這種情況下出現的。動態鏈接庫不用重復編譯或鏈接,一旦裝入內存,DLL函數可以被系統中的任何正在運行的應用程序軟件所使用,而不必再將DLL函數的另一拷貝裝入內存。

下面我們一步一步來建立一個DLL。

一、建立一個DLL工程
新建一個工程,選擇Win32 控制臺項目(Win32 Console Application),并且在應用程序設置標簽(the advanced tab)上,選擇DLL和空項目選項。

二、聲明導出函數
這里有兩種方法聲明導出函數:一種是通過使用__declspec(dllexport),添加到需要導出的函數前,進行聲明;另外一種就是通過模塊定義文件(Module-Definition File即.DEF)來進行聲明。
第一種方法,建立頭文件DLLSample.h,在頭文件中,對需要導出的函數進行聲明。

#ifndef _DLL_SAMPLE_H
#define?_DLL_SAMPLE_H

//?如果定義了C++編譯器,那么聲明為C鏈接方式
#ifdef __cplusplus
extern?"C"?{
#endif
?
//?通過宏來控制是導入還是導出
?#ifdef _DLL_SAMPLE
#define?DLL_SAMPLE_API __declspec(dllexport)
?#else
?#define?DLL_SAMPLE_API __declspec(dllimport)
?#endif
?
//?導出/導入函數聲明
?DLL_SAMPLE_API?void?TestDLL(int);

#undef?DLL_SAMPLE_API

#ifdef __cplusplus
?}
#endif

#endif


這個頭文件會分別被DLL和調用DLL的應用程序引入,當被DLL引入時,在DLL中定義_DLL_SAMPLE宏,這樣就會在DLL模塊中聲明函數為導出函數;當被調用DLL的應用程序引入時,就沒有定義_DLL_SAMPLE,這樣就會聲明頭文件中的函數為從DLL中的導入函數。

第二種方法:模塊定義文件是一個有著.def文件擴展名的文本文件。它被用于導出一個DLL的函數,和__declspec(dllexport)很相似,但是.def文件并不是Microsoft定義的。一個.def文件中只有兩個必需的部分:LIBRARY 和 EXPORTS。

LIBRARY DLLSample
DESCRIPTION?"my simple DLL"
EXPORTS
?TestDLL @1?;@1表示這是第一個導出函數

第一行,''LIBRARY''是一個必需的部分。它告訴鏈接器(linker)如何命名你的DLL。下面被標識為''DESCRIPTION''的部分并不是必需的。該語句將字符串寫入 .rdata 節,它告訴人們誰可能使用這個DLL,這個DLL做什么或它為了什么(存在)。再下面的部分標識為''EXPORTS''是另一個必需的部分;這個部分使得該函數可以被其它應用程序訪問到并且它創建一個導入庫。當你生成這個項目時,不僅是一個.dll文件被創建,而且一個文件擴展名為.lib的導出庫也被創建了。除了前面的部分以外,這里還有其它四個部分標識為:NAME, STACKSIZE, SECTIONS, 和 VERSION。另外,一個分號(;)開始一個注解,如同''//''在C++中一樣。定義了這個文件之后,頭文件中的__declspec(dllexport)就不需要聲明了。

三、編寫DllMain函數和導出函數
DllMain函數是DLL模塊的默認入口點。當Windows加載DLL模塊時調用這一函數。系統首先調用全局對象的構造函數,然后調用全局函數DLLMain。DLLMain函數不僅在將DLL鏈接加載到進程時被調用,在DLL模塊與進程分離時(以及其它時候)也被調用。

#include?"stdafx.h"
#define?_DLL_SAMPLE

#ifndef _DLL_SAMPLE_H
#include?"DLLSample.h"
#endif

#include?"stdio.h"

//APIENTRY聲明DLL函數入口點
BOOL APIENTRY DllMain(HANDLE hModule, DWORD ul_reason_for_call, LPVOID lpReserved)
{
 switch?(ul_reason_for_call)
 {
  case?DLL_PROCESS_ATTACH:
  case?DLL_THREAD_ATTACH:
  case?DLL_THREAD_DETACH:
  case?DLL_PROCESS_DETACH:
   break;
? }
 return?TRUE;
?}

void?TestDLL(int?arg)
{
?printf("DLL output arg %d/n", arg);
?}

如果程序員沒有為DLL模塊編寫一個DLLMain函數,系統會從其它運行庫中引入一個不做任何操作的缺省DLLMain函數版本。在單個線程啟動和終止時,DLLMain函數也被調用。
然后,F7編譯,就得到一個DLL了。

變量導出類似。

?

?

?

從 DLL 導出

?

.DLL 文件的布局與 .exe 文件非常相似,但有一個重要的差異:DLL 文件包含導出表。導出表包含 DLL 導出到其他可執行文件的每個函數的名稱。這些函數是 DLL 中的入口點;只有導出表中的函數可由其他可執行文件訪問。DLL 中的任何其他函數都是 DLL 私有的。通過使用帶 /EXPORTS 選項的Dumpbin?工具,可以查看 DLL 的導出表。

有兩種從 DLL 導出函數的方法:

  • 在生成 DLL 時,創建一個模塊定義 (.def) 文件并使用該 .def 文件。如果希望按序號而不是按名稱從 DLL 導出函數,則請使用此方法。

  • 在函數的定義中使用?__declspec(dllexport)?關鍵字。

用上述任何方法導出函數時,確保使用?__stdcall?調用約定。

? 使用 DEF 文件從 DLL 導出

?

模塊定義 (.def) 文件是包含一個或多個描述 DLL 各種屬性的 Module 語句的文本文件。如果不使用__declspec(dllexport)?關鍵字導出 DLL 的函數,則 DLL 需要 .def 文件。

.def 文件必須至少包含下列模塊定義語句:

  • 文件中的第一個語句必須是 LIBRARY 語句。此語句將 .def 文件標識為屬于 DLL。LIBRARY 語句的后面是 DLL 的名稱。鏈接器將此名稱放到 DLL 的導入庫中。

  • EXPORTS 語句列出名稱,可能的話還會列出 DLL 導出函數的序號值。通過在函數名的后面加上 @ 符和一個數字,給函數分配序號值。當指定序號值時,序號值的范圍必須是從 1 到 N,其中 N 是 DLL 導出函數的個數。如果希望按序號導出函數,請參見按序號而不是按名稱從 DLL 導出函數以及本主題。

例如,包含實現二進制搜索樹的代碼的 DLL 看上去可能像下面這樣:

?復制代碼 LIBRARY BTREE EXPORTSInsert @1Delete @2Member @3Min @4

如果使用?MFC DLL 向導創建 MFC DLL,則向導將為您創建主干 .def 文件并將其自動添加到項目中。添加要導出到此文件的函數名。對于非 MFC DLL,必須親自創建 .def 文件并將其添加到項目中。

如果導出 C++ 文件中的函數,必須將修飾名放到 .def 文件中,或者通過使用外部“C”定義具有標準 C 鏈接的導出函數。如果需要將修飾名放到 .def 文件中,則可以通過使用?DUMPBIN?工具或?/MAP?鏈接器選項來獲取修飾名。請注意,編譯器產生的修飾名是編譯器特定的。如果將 Visual C++ 編譯器產生的修飾名放到 .def 文件中,則鏈接到 DLL 的應用程序必須也是用相同版本的 Visual C++ 生成的,這樣調用應用程序中的修飾名才能與 DLL 的 .def 文件中的導出名相匹配。

如果生成擴展 DLL?并使用 .def 文件導出,則將下列代碼放在包含導出類的頭文件的開頭和結尾:

?復制代碼 #undef AFX_DATA #define AFX_DATA AFX_EXT_DATA // <body of your header file> #undef AFX_DATA #define AFX_DATA

這些代碼行確保內部使用的 MFC 變量或添加到類的變量是從擴展 DLL 導出(或導入)的。例如,當使用?DECLARE_DYNAMIC?派生類時,該宏擴展以將?CRuntimeClass?成員變量添加到類。省去這四行代碼可能會導致不能正確編譯或鏈接 DLL,或在客戶端應用程序鏈接到 DLL 時導致錯誤。

當生成 DLL 時,鏈接器使用 .def 文件創建導出 (.exp) 文件和導入庫 (.lib) 文件。然后,鏈接器使用導出文件生成 DLL 文件。隱式鏈接到 DLL 的可執行文件在生成時鏈接到導入庫。

請注意,MFC 本身使用 .def 文件從 MFCx0.dll 導出函數和類。

使用 __declspec(dllexport) 從 DLL 導出

?

Microsoft 在 Visual C++ 的 16 位編譯器版本中引入了?__export,使編譯器得以自動生成導出名并將它們放到一個 .lib 文件中。然后,此 .lib 文件就可以像靜態 .lib 那樣用于與 DLL 鏈接。

在更新的編譯器版本中,可以使用?__declspec(dllexport)?關鍵字從 DLL 導出數據、函數、類或類成員函數。__declspec(dllexport)?會將導出指令添加到對象文件中,因此您不需要使用 .def 文件。

當試圖導出 C++ 修飾函數名時,這種便利最明顯。由于對名稱修飾沒有標準規范,因此導出函數的名稱在不同的編譯器版本中可能有所變化。如果使用?__declspec(dllexport),僅當解決任何命名約定更改時才必須重新編譯 DLL 和依賴 .exe 文件。

許多導出指令(如序號、NONAME 和 PRIVATE)只能在 .def 文件中創建,并且必須使用 .def 文件來指定這些屬性。不過,在 .def 文件的基礎上另外使用?__declspec(dllexport)?不會導致生成錯誤。

若要導出函數,__declspec(dllexport)?關鍵字必須出現在調用約定關鍵字的左邊(如果指定了關鍵字)。例如:

?復制代碼 __declspec(dllexport) void __cdecl Function1(void);

若要導出類中的所有公共數據成員和成員函數,關鍵字必須出現在類名的左邊,如下所示:

?復制代碼 class __declspec(dllexport) CExampleExport : public CObject { ... class definition ... };

生成 DLL 時,通常創建一個包含正在導出的函數原型和/或類的頭文件,并將?__declspec(dllexport)?添加到頭文件中的聲明中。若要提高代碼的可讀性,請為?__declspec(dllexport)?定義一個宏并對正在導出的每個符號使用該宏:

?復制代碼 #define DllExport __declspec( dllexport )

__declspec(dllexport)?將函數名存儲在 DLL 的導出表中。如果希望優化表的大小,請參見按序號而不是按名稱從 DLL 導出函數。

注意

將 DLL 源代碼從 Win16 移植到 Win32 時,請用?__declspec(dllexport)?替換?__export?的每個實例。

轉載于:https://www.cnblogs.com/yangxx-1990/p/4847459.html

總結

以上是生活随笔為你收集整理的DLL 的导入与导出的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。