環境:visual studio 2019
本篇主要介紹如何操作並顯示鏈接dll。
win平臺dll的鏈接有兩種方式,在上一篇visual studio創建和鏈接dll-隱式鏈接中介紹的是如何隱式鏈接,和顯示鏈接的區別主要有兩點。
- 隱式鏈接不需要手動加載dll
- 隱式鏈接在程序啓動的時候,系統會自動加載exe和dll
- 顯示鏈接只在需要的時候才手動加載,同時也要手動釋放
- 顯示鏈接在編譯主程序的時候不需要靜態庫做符號鏈接,但需要手動做符號導出
創建DLL
創建DLL和隱式鏈接的方式一樣,這裏不重複介紹,參考visual studio創建和鏈接dll-隱式鏈接即可。
創建dll顯示加載代碼
手動加載dll需要做兩個操作,首先loadLibrary
加載dll到進程空間,然後GetProcAddress
獲得符號映射。
這兩個功能單獨放在math_loader.cpp中實現。
math_loader.h
#pragma once
#ifndef MATH_LOADER
#define MATH_LOADER
bool loadDLL();
int power(int a);
#endif // !MATH_LOADER
math_loader.cpp
#include "math_loader.h"
#include <Windows.h>
#include <iostream>
using namespace std;
typedef int (*LIB_power)(int a);
LIB_power lib_power;
bool loadDLL()
{
HMODULE _math_module;
_math_module = LoadLibraryA("math_lib.dll");
if (_math_module == NULL)
{
cout << "loader load dll failed" << endl;
return false;
}
lib_power = (LIB_power)GetProcAddress(_math_module, "power");
if (lib_power == NULL) {
cout << "get proc address failed" << endl;
return false;
}
return true;
}
int power(int a)
{
return lib_power(a);
}
loader程序中做了三件事。
加載dll:通過loadLibraryA把dll加載到進程空間
獲取符號表映射:GetProcAddress可以通過dll句柄和函數簽名獲得函數地址,它是個指針
聲明和定義dll中的函數:由於應用程序通過指針調用 DLL 函數,因此編譯器不生成外部引用,從而不需要與導入庫鏈接。 但是,必須有一個 typedef 或 using 語句來定義所調用的導出函數的調用簽名。
主程序代碼
顯示鏈接的主程序比隱式鏈接稍微多幾個步驟,我們已經封裝到loader程序中了。
main.cc
#include <iostream>
#include <windows.h>
#include "math_loader.h"
using namespace std;
int main(int argc, char const* argv[])
{
cout << "loading dll..." << endl;
if (!loadDLL()) {
cout << "load dll failed!!" << endl;
return -1;
}
cout << "load dll successfully" << endl;
int a = 2;
cout << "power by <math>: " << power(a) << endl;
return 0;
}
創建solution
顯示鏈接的solution是一個單獨的項目,跟DLL是分開的,不依賴dll項目。參考visual studio創建和鏈接dll-隱式鏈接的主程序創建部分操作即可。
需要修改幾個地方,
- main.cc的代碼用顯示鏈接的替換
- 不需要def文件
- 不需要dll的頭文件
完成後的項目如下,
編譯生成exe
生成exe的流程跟隱式鏈接一樣,生成後需要把dll也放到exe目錄下,
如果目錄下沒有dll,或者名稱不對,則會拋出異常,
.\app.exe
loading dll…
loader load dll failed
load dll failed!!
正確的如下,
執行exe,結果
.\app.exe
loading dll…
load dll successfully
using custom power
power by : 4