Qt下開發及調用帶界面的DLL


0.背景

由於某項目需要,在Qt下開發及調用帶界面的DLL。由於中間折騰時間較長才搞定,在這記錄一下。
本帖子中所用Qt版本為QtCreator 4.10.2.基於Qt5.13.2(MSVC 2017,32位)

1. Qt DLL開發

1.1 工程建立

新建工程,選擇:Library->C++ Library在工程細節中Qt module中選擇 Widgets,如下圖所示:

生成的項目中文件列表如下:

其中,在LaerRangerDLL_globall.h中,定義了宏
# define LASERRANGERDLL_EXPORT Q_DECL_EXPORT
查看Q_DECL_EXPORT的定義可以看出# define Q_DECL_EXPORT __declspec(dllexport)
該定義即為dll導出符號的宏定義。

1.2 添加窗體

新建窗體,選擇Qt->Qt 設計師界面類,如下圖所示:

選Main Window,並添加到之前的項目:

在窗體中加入一個Label,並修改顯示字符串為:LaserRangerDLL
然后修改LaserRangerDLL的頭文件和源文件:
頭文件中做如下修改:

頭文件中添加ui_mainwindow.hQWidget頭文件
並將給LaserRangerDLL類添加基類:QMainWindow,並修改其構造函數原型。
添加私有成員:Ui::MainWindow ui;
修改源文件如下:

在構造函數里添加ui.setupUi(this);

1.3 生成DLL

在項目上點擊右鍵:構建,生成DLL和Lib。
則在工程對應的Debug(或Release,和構建配置有關)文件夾里生成LaserRangerDLL.dll和LaserRangerDLL.lib文件。

2. 調用DLL

DLL調用分為兩種:隱式調用和顯式調用。
其中,隱式調用是在編譯時包含.lib文件和.h頭文件,這兩個文件中包含了動態庫中導出的接口信息。然后,在運行時調用dll中封裝的二進制代碼。
顯式調用只有.dll,在運行時通過代碼顯式的加載dll文件,聲明函數原型,並使用dll中的接口。

2.1 隱式調用

以第1建立的動態庫項目LaserRangerDLL為例,建立LaserRangerCaller項目,來調用生成的DLL。

在項目文件夾下建立include文件夾,並將生成的LaserRangerDLL.liblaserrangerdll.hLaserRangerDLL_global.hui_mainwindow.h拷貝進include文件夾。
注意:ui_mainwindow.h需要將dll的項目編譯后,在build文件夾中找到
如下圖所示:

然后,在LaserRangerCaller工程中頭文件中加入include文件夾

添加完之后如下圖所示:

然后再在項目上右擊,依次"添加庫"->"外部庫",庫文件定位到LaserRangerDLL.lib,鏈接選動態。注意,在Windows選項中去掉為debug版本添加‘d’作為后綴的勾選(該選項默認為選中)。
如下圖所示:

先編譯一遍LaserRangerCaller工程。
並將生成的dll文件拷貝進該工程的build\debug文件夾中。
然后在main.cpp中加入頭文件引用和對象調用。

然后運行laserRangerCaller,出現如下窗口:

證明隱式調用成功。

2.2 顯式調用

顯式調用共享庫通過QLibrary類實現,QLibrary類可以實現動態庫中的導出函數加載,相關成員函數如下:
load():用於手動載入DLL文件到內存里,一般無需手動調用此函數,在DLL里的函數第一次被使用時QLibrary會自動調用從函數

isLoaded():用於判斷DLL是否已經被載入內存

unload():用於將DLL從內存中卸載

resolve():解析DLL文件中的函數
這些函數的詳細解析可以看Qt的幫助文檔。
下面通過例程,對第1部分生成的dll進行顯式調用。
新建一個Qt Widget工程LaserRangerLoader
並自動生成一個MainWindow窗體,在窗體中加入一個按鈕, 命名成pushButton_dll_loader

  • 加入接口函數
    注意要通過顯式調用的話,只能調用導出函數,所以在laserrangerdll工程中加入導出函數:
    在頭文件中加入:
extern "C" {
    LASERRANGERDLL_EXPORT LaserRangerDLL* getLaserRangerDLLObj();

    LASERRANGERDLL_EXPORT const char* disp();

    LASERRANGERDLL_EXPORT int addInt(int a,int b);
}

注意,由於C++具有多態特性,可能會給函數名在編譯時添加其他后綴,所以在此用extern "C"關鍵字,保證導出符號與定義函數名一致。
然后在源文件里也加入對應實現代碼:

LaserRangerDLL* getLaserRangerDLLObj()
{
    return new LaserRangerDLL();
}

const char* disp()
{
    return "This is a test function for LaserRangerDLL";
}

int addInt(int a,int b)
{
    return a+b;
}

然后編譯dll工程
在工程目錄下建立include文件夾,並將laserrangerdll.hLaserRangerDLL_global.hui_mainwindow.h拷貝進include文件夾。
編譯一下該工程,將動態庫LaserRangerDLL.dll放入編譯后的build目錄的debug目錄下。

  • 顯式調用
    pushButton_dll_loader的槽函數中加入dll的加載和調用。
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include "QLibrary"
#include "QDebug"
#include "iostream"
#include "QMessageBox"
#include "./include/laserrangerdll.h"

typedef LaserRangerDLL* (*getLaserRangerDLLObj_fcn)();
typedef const char* (disp_fcn)();
typedef int (intAdd_fcn)(int,int);

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    ui->setupUi(this);
}

MainWindow::~MainWindow()
{
    delete ui;
}


void MainWindow::on_pushButton_dll_loader_clicked()
{
    QLibrary* laser_ranger_dll_lib = new QLibrary("LaserRangerDLL.dll");
    if(laser_ranger_dll_lib->load())
    {
        getLaserRangerDLLObj_fcn get_obj_fcn = (getLaserRangerDLLObj_fcn)
                laser_ranger_dll_lib->resolve("getLaserRangerDLLObj");
        LaserRangerDLL* laser_ranger_dll = get_obj_fcn();
        laser_ranger_dll->show();
    }
}

然后運行,點擊按鈕,則出現dll中的窗體。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM