Qt 之在執行時載入共享庫
簡述
在 Windows 上,共享庫由 .dll
表示;在 Linux 上,由 .so
表示。一個共享庫中的符號被設計為匯出的,以便客戶端可以從中匯入符號。
要使用共享庫,除了 Qt之建立並使用共享庫 中介紹的方式之外,Qt 還提供了一種機制,可以在執行時載入共享庫,通過 QLibrary
來實現。
|
認識 QLibrary
QLibrary 用於在執行時載入共享庫,一個完整的載入流程大概分為以下幾步:
- 構造
QLibrary
例項 setFileName()
:指定共享庫的檔名(也可以通過QLibrary
的建構函式來設定)load()
:動態載入共享庫(isload()
檢查載入是否成功)resolve()
:解析共享庫中的符號(如果庫還沒有載入,那麼resolve()
將隱式地嘗試載入)unload()
:解除安裝共享庫
QLibrary
有一個很強大的特性 - 在執行時對共享庫提供了平臺獨立的訪問。對於庫路徑,QLibrary
在內部會做以下處理:
- 如果檔名是絕對路徑,則首先嚐試載入該路徑。若無法找到檔案,則嘗試使用不同平臺的特定檔案字首(例如:Unix 和 Mac 上的 lib)和字尾(例如:Unix 上的
.so
、Mac 上的.dylib
、或 Windows 中的.dll
)的名稱。 - 如果檔案路徑不是絕對的,那麼
QLibrary
會修改搜尋順序,首先嚐試系統特定的字首和字尾,然後再指定檔案路徑。
這使得可以指定僅由其 basename(即:沒有 .dll
或 .so
字尾)標識的共享庫,因此相同的程式碼可以在不同的作業系統上工作。儘管如此,但仍建議儘量減少查詢庫的次數。
注意: 一個共享庫可以被 QLibrary
的多個例項訪問,載入完成後,庫將一直儲存在記憶體中,直到應用程式終止。在使用 unload()
解除安裝庫時,如果 QLibrary
的其他例項使用了相同的庫,那麼呼叫將失敗,並且只有當每個例項都呼叫 unload()
時才會解除安裝。
使用 QLibrary 的優點
在執行時使用 QLibrary
載入共享庫,有很多優點:
- 無需使用
.h
標頭檔案和.lib
- 只需將 dll 檔案和可執行程式放在一起
- 可以在沒有 dll 的情況下啟動可執行程式,因為 dll 將在執行時(按需)載入。
- 有助於生成一個較小的可執行程式
建立共享庫
和 Qt之建立並使用共享庫 一樣,在 Qt Creator 中建立兩個專案:
- SharedLib:是一個 C++ 共享庫專案,其中有一個匯出符號。
- SharedLibClient:是一個 Qt 控制檯應用程式,在執行時呼叫 SharedLib。
sharedlib_global.h 可以確保正確的巨集能夠被呼叫:
#ifndef SHAREDLIB_GLOBAL_H
#define SHAREDLIB_GLOBAL_H
#include <QtCore/qglobal.h>
#if defined(SHAREDLIB_LIBRARY)
# define SHAREDLIBSHARED_EXPORT Q_DECL_EXPORT
#else
# define SHAREDLIBSHARED_EXPORT Q_DECL_IMPORT
#endif
#endif // SHAREDLIB_GLOBAL_H
sharedlib.h 包含了匯出的符號:
#ifndef SHAREDLIB_H
#define SHAREDLIB_H
#include "sharedlib_global.h"
extern "C" {
SHAREDLIBSHARED_EXPORT int subtract(int x, int y);
}
#endif // SHAREDLIB_H
sharedlib.cpp 包含了具體的實現:
#include "sharedlib.h"
int subtract(int x, int y)
{
return x - y;
}
注意: 對於要解析的函式名,必須將其匯出為 C 函式。這意味著如果庫是用 C++ 編譯器編譯的,那麼函式必須被包裝在一個 extern "C"
塊中。
此外,還必須使用 Q_DECL_EXPORT
和 Q_DECL_IMPORT
從庫中顯式匯出該函式。
在執行時載入共享庫
建立一個簡單的客戶端(SharedLibClient) - Qt Console Application,它將使用 SharedLib 庫中的 subtract()
函式。效果如下:
在 main.cpp 檔案中,使用 QLibrary
在執行時載入共享庫:
#include <QCoreApplication>
#include <QLibrary>
#include <qDebug>
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
// SharedLibd.dll 與可執行程式位於同一目錄
QLibrary lib("SharedLibd");
// 載入共享庫
if (lib.load()) {
typedef int (*Fun)(int, int);
// 解析符號
Fun sub = (Fun) lib.resolve("subtract");
if (sub) {
int result = sub(5, 2);
qDebug() << result;
} else {
qDebug() << "Can not resolve subtract";
}
// 解除安裝共享庫
lib.unload();
} else {
qDebug() << lib.errorString();
}
return a.exec();
}
在 Debug 模式下執行專案,結果會顯示在控制檯輸出上。