VisualStudio 制作Dynamic Link Library動態鏈接庫文件的詳細過程

工具集

借助工具可以獲得Dll庫函數的訪問地址,以下推薦兩款工具以供使用:

Dependency Walker官網

Depends22_x64.zip

Dllexp-x64.zip

如何生成

__declspec(dllexport)

將一個函數聲名為導出函數,就是說這個函數要被其他程序調用,即作為DLL的一個對外函數接口。

__declspec(dllexport) RETURN_TYPE FUNCTION()

extern “C”

由於在制作DLL導出函數時由於C++存在函數重載

  • 因此__declspec(dllexport) FUNCTION(int,int)在DLL會被decorate,例如: 被decorate成為function_int_int,
  • 而且不同的編譯器decorate的方法不同,造成瞭在用GetProcAddress取得FUNCTION地址時的不便
  • 使用extern "C"時,上述的decorate不會發生,因為C沒有函數重載,如此一來被extern"C"修飾的函數,就不具備重載能力
extern "C" {
  __declspec(dllexport) RETURN_TYPE FUNCTION(){
    ;
  }
}

如何使用

  • 動態載入方式是指在編譯之前並不知道將會調用哪些 DLL 函數, 完全是在運行過程中根據需要決定應調用哪些函數。
  • 方法是:用 LoadLibrary 函數加載動態鏈接庫到內存,用 GetProcAddress函數動態獲得 DLL 函數的入口地址。
  • 當一個 DLL 文件用 LoadLibrary 顯式加載後,在任何時刻均可以通過調用 FreeLibrary 函數顯式地從內存中把它給卸載。
  • 動態調用使用的 Windows API 函數主要有 3 個, 分別是 LoadLibraryGetProcAddressFreeLibrary

聲明調用

註意DLL函數調用約定,必須一致

  • __stdcall Windows API默認的函數調用協議
  • __cdecl C/C++默認的函數調用協議
  • __fastcall 適用於對性能要求較高的場合

Example

假如我有一個函數接口如下:

//@ GETCOMCHECKSUM_API是一個宏定義
//@ #define GETCOMCHECKSUM_API __declspec(dllexport)
GETCOMCHECKSUM_API int fnGetComCheckSum(
                        const unsigned char*  iCsArray,       //[In]數組
                        const unsigned int    iCsSize,        //[In]數值
                        unsigned char&        ioCsValue)      //[In/Out]數值

那麼我的調用應該這麼寫:

//@ __cdecl * 後面函數名可以自定義
typedef int(__cdecl *GetComCheckSum)(
            unsigned char const *,
            unsigned int,
            unsigned char&);

LoadLibrary

  • [格式] function LoadLibrary(LibFileName : PChar): Thandle;
  • [功能] 加載由參數 LibFileName 指定的 DLL 文件
  • [說明] 參數 LibFileName 指定瞭要裝載的 DLL 文件名

如果 LibFileName 沒有包含一個路徑,系統將按照:當前目錄、Windows 目錄、Windows 系統目錄、包含當前任務可執行文件的目錄、列在 PATH 環境變量中的目錄等順序查找文件。

如果函數操作成功,將返回裝載 DLL 庫模塊的實例句柄,否則,將返回一個錯誤代碼,錯誤代碼的定義如下表所示

錯誤代碼             含義
      0             系統內存不夠,可執行文件被破壞或調用非法
      2             文件沒有被發現
      3             路徑沒有被發現
      5             企圖動態鏈接一個任務錯誤或者有一個共享或網絡保護錯誤
      6             庫需要為每個任務建立分離的數據段  
      8             沒有足夠的內存啟動應用程序  
      10            Windows  版本不正確  
      11            可執行文件非法或不是Windows  應用程序,或在.  EXE映像中有錯誤  
      12            應用程序為一個不同的操作系統設計(如  OS/2)  
      13            應用程序為  MS  DOS   4. 0  設計  
      14            可執行文件的類型不知道  
      15            試圖裝載一個實模式應用程序(為早期Windows  版本設計)
      16            試圖裝載包含可寫的多個數據段的可執行文件的第二個實例  
      19            試圖裝載一個壓縮的可執行文件(文件必須被解壓後才能被裝載)  
      20            DLL  文件非法
      21            應用程序需要  32  位擴展

Example

//@ 定義句柄
HINSTANCE hSnKLib;

//@ 獲取鏈接庫句柄       Getchecksum為dll的文件名 即 Getchecksum.dll
//@ 系統將會在當前目錄下尋找名為Getchecksum.dll的文件
//@ 至於為什麼使用_T("") ,_T是一個宏,作用是讓你的程序支持Unicode編碼,Windows使用兩種字符集ANSI和UNICODE
hSnKLib = LoadLibrary(_T("Getchecksum"))

//@ 如果未能成功獲取,拋出錯誤
if (hSnKLib == NULL)
{
    FreeLibrary(hSnKLib);
  	printf("LoadLibrary err\n");
  	getchar();
  	return 1;
}

GetProcAddress

  • 格式:function GetProcAddress(Module:Thandle; ProcName:PChar): TfarProc;
  • 功能: 返回參數 Module 指定的模塊中,由參數 ProcName 指定的過程或函數的入口地址
  • 說明: 參數 Module 包含被調用函數的 DLL 句柄,這個值由 LoadLibrary 返回,procName是指向含有函數名的以 nil 結尾的字符串指針,或者可以是函數的次序值.

大多數情況下,用函數名是一種更穩妥的選擇。

如果該函數執行成功,則返回 DLL 中由參數 ProcName 指定的過程或函數的入口地址,否則返回 nil 。

Example

//前面我們在頭文件中聲明瞭下述函數
typedef int(__cdecl *GetComCheckSum)(
            unsigned char const *,
            unsigned int,
            unsigned char&);


//實例化並且獲取函數地址
//fnGetComCheckSum為dll export出來的函數名,GetComCheckSum為我們引用時候聲明的函數名
//這裡做的工作就是將dll中函數與我們聲明的聯系到一塊。
GetComCheckSum getcom = (GetComCheckSum)GetProcAddress(hSnKLib, "fnGetComCheckSum")

if (!getcom)
{
	FreeLibrary(hSnKLib);			//釋放dll文件
	//Add your code here
}

FreeLibrary

  • 格式:procedure FreeLibrary(Module: Thandle);
  • 說明:將由參數 Module 指定的 DLL 文件從內存中卸載 1 次。
  • 說明:Module 為 DLL 庫的句柄。這個值由 LoadLibrary 返回。由於 DLL 在內存中隻裝載一次,因此調用 FreeLibrary 首先使 DLL 的引用計數減 1,如果計數減為 0 則卸載該 DLL
  • 註意:每調用一次 LoadLibrary 函數就應調用一次 FreeLibrary 函數,以保證不會有多餘的庫模塊在應用程序結束後仍留在內存中,否則導致內存泄漏。

Example

FreeLibrary(hSnKLib);

FAQS

Question 1: GetLastError獲取錯誤代碼127

問題描述:

  • 采用"運行期間動態鏈接"自己的dll文件
  • LoadLibrary成功獲取dll模塊句柄
  • 但是GetProcAddress(hModule, “ExportFunc”)卻返回NULL,GetLastError獲取錯誤代碼127,意思是“找不到指定程序”

問題定位:

  • 用Depends工具(VS2010默認沒有,需另行下載:http://www.dependencywalker.com/),查看dll的導出函數名稱。
  • 發現導出函數名不再是“ExportFunc”,而根據函數的返回類型和參數進行瞭“decorate”,變為瞭“?ExportFunc@@YAXPB_W@Z”。

解決方法兩種:

  • 修改GetProcAddress的第二個參數為真正的導出函數名稱即可
  • 在dll工程中添加DEF文件,寫入如下內容:
EXPORTS
               ExportFunc
  • 重新編譯dll工程。再次用Depends工具查看導出函數名稱,即為“ExportFunc”。
  • 工程–鏈接器–輸入 的模塊定義文件中,將自己的DEF文件加上

在這裡插入圖片描述

參考案例

GetProcAddress 使用註意事項

到此這篇關於VisualStudio 制作Dynamic Link Library動態鏈接庫文件的文章就介紹到這瞭,更多相關VisualStudio動態鏈接庫文件內容請搜索WalkonNet以前的文章或繼續瀏覽下面的相關文章希望大傢以後多多支持WalkonNet!

推薦閱讀: