简介
我们可以像JAVA一样规定一个接口,该接口为一个类,类内部全是虚方法,而该类的实现类及具体方法由DLL文件实现。这样,我们就可以动态更换或添加新的功能了。
QT 中提供了显式加载DLL文件的方法,所以不要用 <Windows.h> 文件中加载DLL的方式,这样会影响移植性。
DLL项目的写法
- 新建项目后,把 xxx_global.h文件中的内容拷贝到 另一个.h文件开头
- 拷贝后把 xxx_global.h 文件删除掉
.h文件的写法示例(dlllib.h)
- 拷贝后把 xxx_global.h 文件删除掉
#ifndef DLLLIB_H
#define DLLLIB_H
// 这下面6行代码是从 xxx_global.h 文件里拷贝过来的。
#include <QtCore/qglobal.h>
#if defined(DLLLIB_LIBRARY)
# define DLLLIB_EXPORT Q_DECL_EXPORT
#else
# define DLLLIB_EXPORT Q_DECL_IMPORT
#endif
// 这是对外暴露的类对象
class DLLLIB_EXPORT DllLib
{
public:
virtual int Add()=0;
};
// 需要提供一个创建该类的方法
// 由于显示调用必须通过函数名找到该方法,所以必须加 extern "C" 前缀,否则函数名会被改写,导致找不到该方法。
extern "C" DLLLIB_EXPORT DllLib* getDllLib(int a,int b);
// 其他方法
extern "C" DLLLIB_EXPORT int getDllLibResult(DllLib* dllLib);
#endif // DLLLIB_H
dlllib.cpp文件
#include "dlllib.h" // 这个是上面那个.h文件
// 我们需要在该文件里具体实现.h文件的接口,你也可以单独把子类抽成.h文件
class MyDllLib:public DllLib{
public:
MyDllLib(int a,int b):a(a),b(b){}
int Add(){
return this->a + this->b;
}
private:
int a;
int b;
};
// 这个方法很重要,由于是显式加载DLL文件,所以你无法通过new 类名来创建对象,所以DLL文件必须提供创建对象的方法。
DllLib* getDllLib(int a,int b){
return new MyDllLib(a,b);
}
// 其他方法
int getDllLibResult(DllLib* dllLib){
return dllLib->Add();
}
写好后点击编译,注意x86和x64是有区别的,选择对应的版本编译(具体是那个版本得看你主程序是哪个版本)
编译好后在输出文件夹里能找到 .lib,.dll 文件,如果是显式加载的话,只需要.dll文件。
QT程序使用
需要的文件:上面得到的dlllib.dll 和 dlllib.h 文件,先将 dlllib.h文件拷贝到主项目的 lib 文件夹(自己建立的)下。
再在项目的Headers上点击右键添加现有文件,把dlllib.h文件添加到项目中。然后先编译一次主项目,生成输出目录后,把 dlllib.dll 文件放到输出文件夹下(和.exe文件同级),因为接下来读取dll文件是直接从.exe同级目录下读取的。
接下来正式开始写主项目:
先在.h 文件里面
#include "lib/dlllib.h"
// 动态加载DLL的时候需要用到该库
#include <QLibrary>
显式加载:
QLibrary lib("DllLib.dll");// 加载DLL文件
if(lib.load()){
typedef DllLib* (*f_getDllLib)(int, int); // 定义函数指针类型
typedef int (*f_getDllLibResult)(DllLib*); // 定义函数指针类型
f_getDllLibResult pAdd2 = reinterpret_cast<f_getDllLibResult>(lib.resolve("getDllLibResult")); // 获取函数指针,获取对应的方法
f_getDllLib pAdd = reinterpret_cast<f_getDllLib>(lib.resolve("getDllLib")); // 获取函数指针,获取对应的方法
if(pAdd && pAdd2){
// 都加载成功
DllLib* dllLib = pAdd(25,30);
qDebug() << dllLib->Add();
int ret = pAdd2(dllLib);
qDebug() << QStringLiteral("方法结果为:") << ret;
}else{
qDebug() << "Failed to load function." << endl;
}
// 卸载插件
lib.unload();
}
后记
通过这种方式加载后,那么我们可以给项目规定一个插件接口,再后续编写插件的时候都要遵循这个插件接口的规则,然后我们就可以在不重新编译项目的情况下,对项目进行动态变更或者添加额外的功能。