由于某项目需要,在Qt下开发及调用带界面的DLL。由于中间折腾时间较长才搞定,在这记录一下。
本帖子中所用Qt版本为QtCreator 4.10.2.基于Qt5.13.2(MSVC 2017,32位)
新建工程,选择: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导出符号的宏定义。
新建窗体,选择Qt->Qt 设计师界面类,如下图所示:
选Main Window,并添加到之前的项目:
在窗体中加入一个Label,并修改显示字符串为:LaserRangerDLL
。
然后修改LaserRangerDLL
的头文件和源文件:
头文件中做如下修改:
头文件中添加ui_mainwindow.h
和QWidget
头文件
并将给LaserRangerDLL
类添加基类:QMainWindow,并修改其构造函数原型。
添加私有成员:Ui::MainWindow ui;
修改源文件如下:
在构造函数里添加ui.setupUi(this);
在项目上点击右键:构建,生成DLL和Lib。
则在工程对应的Debug(或Release,和构建配置有关)文件夹里生成LaserRangerDLL.dll和LaserRangerDLL.lib文件。
DLL调用分为两种:隐式调用和显式调用。
其中,隐式调用是在编译时包含.lib文件和.h头文件,这两个文件中包含了动态库中导出的接口信息。然后,在运行时调用dll中封装的二进制代码。
显式调用只有.dll,在运行时通过代码显式的加载dll文件,声明函数原型,并使用dll中的接口。
以第1建立的动态库项目LaserRangerDLL
为例,建立LaserRangerCaller
项目,来调用生成的DLL。
在项目文件夹下建立include文件夹,并将生成的LaserRangerDLL.lib
、laserrangerdll.h
、LaserRangerDLL_global.h
,ui_mainwindow.h
拷贝进include文件夹。
注意:ui_mainwindow.h
需要将dll的项目编译后,在build文件夹中找到
如下图所示:
然后,在LaserRangerCaller
工程中头文件中加入include文件夹
添加完之后如下图所示:
然后再在项目上右击,依次"添加库"->"外部库",库文件定位到LaserRangerDLL.lib
,链接选动态。注意,在Windows选项中去掉为debug版本添加‘d’作为后缀
的勾选(该选项默认为选中)。
如下图所示:
先编译一遍LaserRangerCaller
工程。
并将生成的dll文件拷贝进该工程的build\debug文件夹中。
然后在main.cpp中加入头文件引用和对象调用。
然后运行laserRangerCaller,出现如下窗口:
证明隐式调用成功。
显式调用共享库通过QLibrary类实现,QLibrary类可以实现动态库中的导出函数加载,相关成员函数如下:
load():用于手动载入DLL文件到内存里,一般无需手动调用此函数,在DLL里的函数第一次被使用时QLibrary会自动调用从函数
isLoaded():用于判断DLL是否已经被载入内存
unload():用于将DLL从内存中卸载
resolve():解析DLL文件中的函数
这些函数的详细解析可以看Qt的帮助文档。
下面通过例程,对第1部分生成的dll进行显式调用。
新建一个Qt Widget工程LaserRangerLoader
并自动生成一个MainWindow窗体,在窗体中加入一个按钮, 命名成pushButton_dll_loader
。
extern "C" { LASERRANGERDLL_EXPORT LaserRangerDLL* getLaserRangerDLLObj(); LASERRANGERDLL_EXPORT QString disp(); LASERRANGERDLL_EXPORT int addInt(int a,int b); }
然后在源文件里也加入对应实现代码:
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.h
、LaserRangerDLL_global.h
,ui_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中的窗体。