DLL介紹
DLL(動態連結庫,Dynamic Link Library)是一種可執行檔案,它包含可以在其他程式中呼叫的函式和資料。他是Windows作業系統中的一個重要概念,用於程式碼共享和模組化。
特點
- 程式碼共享:多個程式可以同時使用同一個DLL檔案,而不需要將其程式碼編譯到每個程式中。這樣可以節省磁碟空間和記憶體,並且可以簡化程式的更新和維護。
- 執行時連結:與靜態連結庫(
.lib
檔案)不同,DLL不是在編譯時連結到程式中的,而是在程式執行時連結。這意味著,如果更新了DLL,使用該DLL的程式可以在不重新編譯的情況下直接使用新版本。 - 多語言支援:DLL可以由不同的語言編寫,例如C,C++,Delphi等,只要它們遵循一定的呼叫約定。
- 可擴充性:應用程式可以透過載入和解除安裝DLL來動態地增加或減少功能。
- 資源共享:DLL在記憶體中只有一個例項,所有使用它的應用程式都共享這個例項,從而節約了資源。
生成DLL
新建dll專案
新建專案-選擇“win32控制檯應用程式”
接著在彈出框中選擇dll
和空專案
完成後得到空專案的目錄結構,建立DLL1.h
和DLL1.cpp
兩個檔案
書寫程式碼
- 在
DLL1.cpp
中實現兩個數求和與求差的介面:
int sum(int a, int b){
return a + b;
}
int sub(int a, int b){
return a - b;
}
- 在
DLL1.h
中宣告函式:
int sum(int a, int b);
int sub(int a, int b);
-
為了能在dll中使用,還需要給函式加上字首:
extern "C" __declspec(dllexport)
-
其中
_declspec(dllexport)
的意思是指定需要匯出到dll的目標(用於生成dll) -
extern "C"
表示將C語言程式匯出為DLL
-
extern "C" __declspec(dllexport) int sum(int a, int b);
extern "C" __declspec(dllexport) int sub(int a, int b);
也可以使用宏定義,使程式碼更具可讀性
#define SumAndSub_API __declspec(dllexport)
extern "C" SumAndSub_API int sum(int a, int b);
extern "C" SumAndSub_API int sub(int a, int b);
生成DLL
在.h
和.cpp
中新增程式碼之後,右擊專案選擇“生成”
生成成功後,在專案Debug
資料夾下即可找到生成的dll檔案
.h
所在目錄也需要記錄一下
到這DLL的封裝算是完成了
呼叫DLL
隱式呼叫
首先需要重新建立一個空專案來呼叫測試:
建立完成之後,還需引入三個檔案即前面生成的DLL1.dll
和DLL1.lib
以及專案DLL1.h
檔案
其中DLL1.dll
需要放在當前專案的Debug
目錄下,其他兩個在屬性中配置路徑即可(DLL1.lib
還需新增依賴項)
右擊專案-選擇屬性
選擇VC++目錄
其中“包含目錄” 新增.h
檔案所在資料夾
“庫目錄”新增.lib
所在目錄
新增完成後如圖:
然後在連結器-輸入選項中新增依賴項
完成後應用屬性儲存,然後將DLL檔案複製到當前專案的Debug
目錄下
下面新建一個test.cpp
來測試下是否能夠成功呼叫到DLL
#include "iostream"
#include "DLL1.h"
using namespace std;
int main(){
cout << sum(2, 5) << endl;
cout << sub(5, 2) << endl;
system("pause");
return 0;
}
成功呼叫到DLL
顯式呼叫
顯式呼叫中又包含靜態呼叫與動態呼叫。
靜態顯式呼叫
- 靜態呼叫:
.lib
檔案包含了函式程式碼本身,在編譯時直接將程式碼加入程式當中,稱為靜態連結庫(static link library)。靜態呼叫使用靜態連結庫,連結器從靜態連結庫LIB獲取所有被引用函式,並將庫同程式碼一起放到可執行檔案中。
先建立一個新的空專案,建立完成後新增一個.cpp
原始檔並寫入簡單的主函式
int main(){
}
右擊專案選擇-生成(生成Debug
目錄)
把DLL1.dll
放到當前專案的Debug
目錄下
建立.cpp
原始檔透過以下程式碼進行靜態呼叫
#include "iostream"
using namespace std;
#pragma comment(lib, "D:\\Code\\C++\\dll\\DLL1\\Debug\\DLL1.lib")
// dll中封裝的函式
extern "C" __declspec(dllimport) int sum(int, int);
extern "C" __declspec(dllimport) int sub(int, int);
int main(){
cout << sum(2, 5) << endl;
cout << sub(5, 2) << endl;
system("pause");
return 0;
}
其中#pragma comment(lib, "D:\\Code\\C++\\dll\\DLL1\\Debug\\DLL1.lib")
表示連結DLL1.lib
這個庫,與在專案屬性中的“VC++目錄”中的“庫目錄”新增目錄以及連結器-輸入新增依賴性操作是等價的(一個顯式一個隱式的區別)。
動態顯式呼叫
- 動態呼叫:
.lib
檔案包含了函式所在的DLL檔案和檔案中函式位置的資訊(入口),程式碼由執行時載入在程序空間中的DLL提供,稱為動態連結庫(dynamic link library)。動態呼叫使用動態連結庫,可執行模組(.dll檔案或.exe檔案)本身不包含它呼叫的DLL函式的程式碼,僅包含在執行時定位DLL函式的程式碼所需的資訊,在程式執行時能夠找到並連結到正確的DLL檔案和函式。(這些資訊通常包括函式名、引數型別等,足以讓作業系統在執行時解析並呼叫正確的函式。)
同樣建立專案後先生成Debug
目錄
生成之後將DLL1.dll
放入Debug目錄中
透過如下方式進行動態呼叫
- 首先透過
LoadLibrary()
函式來載入指定的dll檔案,載入到程式的記憶體中(DLL沒有自己的記憶體) GetProcAddress()
函式檢索指定dll檔案輸出庫函式地址,透過函式指標typedef int(*func)(int a, int b);
來裝載函式並使用。FreeLibrary()
釋放dll所佔的空間。
#include "iostream"
#include "windows.h"
using namespace std;
typedef int(*func)(int a, int b);
int main(){
// 動態載入dll
HMODULE hModule = LoadLibrary("DLL1.dll");
if (!hModule){
cout << "Error!" << endl;
}
// 裝載函式
func sum = func(GetProcAddress(hModule, "sum"));
func sub = func(GetProcAddress(hModule, "sub"));
if (sum != NULL){
cout << sum(5, 2) << endl;
}
if (sub != NULL){
cout << sub(5, 2) << endl;
}
// 釋放
FreeLibrary(hModule);
system("pause");
return 0;
}
載入dll的LoadLibrary()函式同樣可以使用絕對路徑,就無需將dll檔案放到Debug目錄下了
// 動態載入dll
HMODULE hModule = LoadLibrary("D:\\Code\\C++\\dll\\DLL1\\Debug\\DLL1.dll");
注意:動態呼叫方式通常是隻有.dll
檔案,而缺少.h
和.lib
檔案時使用,當三個檔案都齊全時,應採用更加簡單方便的隱式呼叫。