簡介
介紹 QT6,DLL 動態鏈接庫的開發,開發示例
詳細
DLL 入口函數
DllMain 是每個 dll 的入口函數,可以寫,也可以不寫,不影響其他庫函數運行。如果使用此函數需要包含頭文件#include <objbase.h>
。
BOOL WINAPI DllMain(HANDLE hModule, // dll 實例句柄, 也可以記作 HINSTANCEDWORD dwReason, // dll 當前所處狀態LPVOID ljReserved // 保留參數,和 dll 的狀態相關,使用較少
);
入口函數定義示例:
BOOL WINAPI DllMain(HANDLE hModule, DWORD dwReason, LPVOID ljReserved)
{switch (dwReason) {case DLL_PROCESS_ATTACH: // 進程加載 dll 時觸發qDebug() << "DLL_PROCESS_ATTACH trick...";break;case DLL_PROCESS_DETACH: // 進程卸載 dll 時觸發qDebug() << "DLL_PROCESS_DETACH trick...";break;case DLL_THREAD_ATTACH: // 線程加載 dll 時觸發qDebug() << "DLL_THREAD_ATTACH trick...";break;case DLL_THREAD_DETACH: // 線程卸載 dll 時觸發qDebug() << "DLL_THREAD_DETACH trick...";break;}return true;
}
DLL 自定義庫函數
導出函數
導出函數,即為使用 dll 庫時候,可以從外部調用到的函數。不聲明為導出函數,只能在庫內部調動,外部無法調用到未進行導出函數聲明的函數。其他關鍵字的說明如下:
extern "[C/C++]" __declspec(dllexport) void [CALLBACK] test(); // 簡單庫函數聲明導出函數示例
-
extern "C"
表示在用 C++ 代碼調用 dll 庫時候,需要聲明。是由于 C 和 C++ 編譯器對同一函數編譯后,生成的符號表有差異,導致調用時候可能有異常,加上此聲明,表示按照C語言的方式進行函數調用。如果只在 C 語言中進行使用,那么可以不寫此關鍵字。 -
__declspec(dllexport)
聲明函數為導出函數。 -
CALLBACK
如果自定義函數是 windows 窗口函數,還需要在函數聲明時候使用 (表示可以被操作系統進行調用)。
導出類
幾乎所有的Windows平臺的C++編譯器都支持從DLL中導出C++類,導出C++類與導出C函數非常相似。如果需要導出整個類,就是在類名前面使用說明符。
#define XYZAPI __declspec(dllexport)
// 導出整個 CXyz 類
class XYZAPI CXyz
{
public:int Foo(int n);
}// 僅導出 Foo 方法
class CXyz
{
public:XYZAPI int Foo(int n);
}
默認情況下,C++編譯器使用 __thiscall
的調用約定,由于不同的編譯器使用的名字修飾約定不同,因此導出的 C++ 類只能被相同的編譯器和相同版本的編譯器使用。
導出類使用示例:
#include "XyzLibrary.h"
CXyz xyz;
xyz.Foo(21);
但是在以下情況下,編譯器會警告你未導出基類和數據成員,如果要成功導出C++類,開發人員必須導出所有相關的基類和所有用于定義數據成員的類,
class Base {};
class Data {};
// 警告:未導出基類
class __declspec(dllexport) Child : Base
{...
private:Data m_data; // 警告:未導出成員
}
優缺點
優點:該導出方式導出的類與 C++ 其他類的使用幾乎相同。
缺點:
- 后期維護比較麻煩,導出的類可能產生非常大的依賴,導致代碼耦合度高
- 必須保證使用同一種編譯器,導出類的本質是導出類中的函數,因為語法上直接導出了類,沒有對函數的調用方式,重命名進行設置,導致產生的 dll 沒有通用性
- DLL HELL 問題
DLL Hell 是指當多個應用程序試圖共享一個公用組件(如某個動態連接庫(DLL)或某個組件對象模型(COM)類)時所引發的一系列問題。最典型的情況是,某個應用程序將要安裝一個新版本的共享組件,而該組件與機器上的現有版本不向后兼容。雖然剛安裝的應用程序運行正常,但原來依賴前一版本共享組件的應用程序也許已無法再工作。
**“Side by side” 是在同一臺機器上同時運行不同版本的相同組件的能力。**使用支持并列的組件,編程人員不必努力維護嚴格的向后兼容,因為不同的應用程序自由使用某個共享組件的不同版本。
導出變量
通過 __declspec
導出聲明
// 導出/導入變量聲明
DLL_SAMPLE_API extern int DLLData;
用模塊定義文件(.def)進行導出聲明
LIBRARY DLLSample
DESCRIPTION "my simple DLL"
EXPORTSDLLData DATA ;DATA表示這是數據(變量)
導出變量使用
導出變量的隱式使用示例:
#include <stdio.h>
#include "DLLSample.h"
#pragma comment(lib,"DLLSample.lib")int main(int argc, char *argv[])
{printf("%d ", DLLData);return 0;
}
導出變量的顯示使用示例:
#include <iostream>
#include <windows.h>int main()
{int my_int;HINSTANCE hInstLibrary = LoadLibrary("DLLSample.dll");if (hInstLibrary == NULL){FreeLibrary(hInstLibrary);}// 獲取變量值my_int = *(int*)GetProcAddress(hInstLibrary, "DLLData");if (dllFunc == NULL){FreeLibrary(hInstLibrary);}std::cout<<my_int;std::cin.get();FreeLibrary(hInstLibrary);return(1);
}