1. 程式人生 > >Qt 之在執行時載入共享庫

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_EXPORTQ_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 模式下執行專案,結果會顯示在控制檯輸出上。