動態鏈接庫dll的兩種加載方式:隱式和顯式

1 前言

在第一篇技術博客動態鏈接庫簡介中說到了兩種加載方式,當時沒有詳細說明,這裏詳細說明一下
可以通過兩種方式

  1. 隱式鏈接(需要.dll,.lib,.h)
  2. 顯式鏈接(需要.dll)

2 隱式鏈接----需要.lib,.dll,.h文件

隱式鏈接就是在程序開始執行時就將DLL文件加載到內存當中,而顯示鏈接,是實時加載,程序需要的時候加載,不需要的時候,卸載。
這種方式需要DLL文件,以及相應的Lib文件和頭文件。
只要沒有在程序中顯式鏈接的,都是隱式鏈接
Windows程序bin目錄包含了可執行文件(.exe)和動態鏈接庫(.dlll),lib目錄包含了靜態庫。
隱式鏈接步驟:
第一步:將.dll,lib,.h文件放入對應的搜索路徑

  • 其中動態庫的搜索路徑點擊這裏查看,記住最重要的兩個
    1. 項目當前目錄(.cpp)目錄
    2. path環境變量中的目錄

  • 靜態庫的搜索路徑包括
    1. 項目當前目錄.cpp目錄(項目和解決方案的Debug不行)(也不是解決方案目錄)
    2. VC設置中的庫目錄(Library Directories)

在這裏插入圖片描述

注:如果lib庫不放到搜索路徑中,也可以在程序中添加

 #pragma comment(lib,"D:/DLLTest.lib")//若是相對路徑,則爲項目當前.cpp目錄
  • 頭文件搜索路徑包括
    1. VC設置中的包含目錄(Include Directories)

在這裏插入圖片描述

第二步:一定要在VC設置中的依賴項中添加你用到的庫的名字
在這裏插入圖片描述

2.1 第一種方式(對應上面每種搜索路徑1):針對數量較少的庫

設置.dll,.lib搜索路徑:直接將.dll,.lib,.h放入項目當前目錄下(即含有項目源文件.cpp的目錄)
在這裏插入圖片描述
設置.h的搜索路徑:VC設置中的包含目錄(Include Directories)加入你的.h路徑
然後在鏈接->輸入->附加依賴項中加入你要使用的靜態庫。
這裏只添加靜態庫
在這裏插入圖片描述
就可以使用DLL中的函數了。

2.2 第二種方式(對應上面每種搜索路徑2):針對數量比較多的庫(Opencv)

如Opencv,OSG等開源庫的配置

這種方式也是通常開源庫的配置方式,如Opencv,OSG等

步驟如下:

步驟1.添加DLL的目錄

  • ★將dll目錄(通常爲bin目錄)放入環境變量Path中(需要重啓),path是搜索動態庫的目錄

步驟2.添加靜態庫lib庫目錄和頭文件的目錄
在工程中的VC++目錄中添加靜態庫目錄和頭文件目錄。
在這裏插入圖片描述
步驟3.添加靜態庫

在鏈接->輸入->附加依賴項中加入你要使用的靜態庫(當程序中調用時,會在上述配置的庫目錄中尋找這個庫)。
這裏只添加靜態庫,動態庫的目錄已放入環境變量中,系統會搜索這個路徑的,自動加載.dll
如RmwRoadBoundaryStraightLineDLL.lib
然後,在程序中添加頭文件 (在上述配置的頭文件目錄中尋找),就可以使用DLL中的函數了。

3 顯式鏈接—需要DLL(不需要.lib,.h文件)

就是顯式加載DLL庫
在這裏插入圖片描述在這裏插入圖片描述在這裏插入圖片描述
注意:顯示鏈接,導出DLL的時候,採用extern “C”的方式,而不採用_declspec(dllexport),因爲_declspec(dllexport)會有一個”名字改編”的問題(採用了_cdecl調用規約的C++編譯方式)。

3.1 示例1:採用extern “C”的方式

如庫文件對應的頭文件如下

#ifndef DLLTEST_H
#define DLLTEST_H
//該宏完成在dll項目內部使用__declspec(dllexport)導出
//在dll項目外部使用時,用__declspec(dllimport)導入
//宏DLL_EXPORTS在.cpp中定義
#ifdef DLL_EXPORTS
#define DLL_EXPORTS extern"C"_declspec(dllexport)
#else
#define DLL_EXPORTS extern"C"_declspec(dllimport)
#endif
//函數聲明
DLL_EXPORTS int  Add(int a,int b);
DLL_EXPORTS int  Sub(int a,int b);
DLL_EXPORTS int  Divide(int a,int b);
#endif//DLLTEST_H

測試代碼:

typedefint(*Add)(int a, int b);
typedefint(*Sub)(int a, int b);
HINSTANCE hDLL;
Add Add_;//函數指針
hDLL =LoadLibrary(_T("D:/DLLTest.dll"));//加載動態鏈接庫DLLTest.dll文件;
Add_ = (Add)GetProcAddress(hDLL,"Add");
intresult = Add_(5, 8);
printf("5+8:%d\n",result);
FreeLibrary(hDLL);//卸載.dll文件;

以下是採用__declspec(dllexport)方式導出,不推薦這種方式需要注意函數名的書寫問題!
現在DLLTest.h中有函數(採用__declspec(dllexport)方式導出)

3.2 示例2:_declspec(dllexport)

#ifndef DLLTEST_H
#define DLLTEST_H
//該宏完成在dll項目內部使用__declspec(dllexport)導出
//在dll項目外部使用時,用__declspec(dllimport)導入
//宏DLL_EXPORTS在.cpp中定義
#ifdef DLL_EXPORTS
#define DLL_EXPORTS __declspec(dllexport)
#else
#define DLL_EXPORTS __declspec(dllimport)
#endif
int DLL_EXPORTS Add(int a,int b);
int DLL_EXPORTS Sub(int a,int b);
int DLL_EXPORTS Divide(int a,int b);
#endif//DLLTEST_H

測試代碼:

typedefint(*Add)(int a, int b);
typedefint(*Sub)(int a, int b);
HINSTANCE hDLL;
Add Add_;//函數指針
//也可以採用hDLL =LoadLibrary(_T("D:/DLLTest.dll"));
hDLL =LoadLibraryA(("D:/DLLTest.dll"));//加載動態鏈接庫DLLTest.dll文件;
Add_ = (Add)GetProcAddress(hDLL,"?Add@@YAHHH@Z");//!!!!獲取函數地址
intresult = Add_(5, 8);
printf("5+8:%d\n",result);
FreeLibrary(hDLL);//卸載.dll文件;

這裏需要注意的是:GetProcAddress(hDLL,“函數名”);中的函數名要是DLL中的函數名,這個函數名可以用PE Explorer軟件查看
由於採用的是VC++處理函數名方式,所以
GetProcAddress(hDLL,"?Add@@YAHHH@Z");// GetProcAddress(hDLL,“函數名”);
在這裏插入圖片描述

而不是簡單的“Add”,因爲DLL中的函數名是結果VC++方式處理過的函數名
顯示調用,最好採用extern “C”的方式導出DLL
在這裏插入圖片描述
所以採用隱式鏈接方式的時候,只加載需要的DLL,在附加依賴項中,只添加需要的DLL對應的lib,不要多加,否則會造成1.加大程序啓動時間 2.內存浪費

參考文獻

1、《C++ Primer》(第4版特別版)
2、《VC++深入詳解》孫鑫

原文鏈接:動態鏈接庫dll的兩種加載方式

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章