说明

近期入职新公司,新公司的项目用到了Qt的插件系统,花时间了解了一下,还以为Qt的插件系统有多么高级呢,原来归根到底还是 dll 的动态调用时获取其中的类那一招啊,原理和之前的文章《DLL的动态加载》 的里使用 使用dll中的类 描述的方法如出一辙,只是Qt利用了其库的优势。

动态加载dll获取类

《DLL的动态加载》 文章说已经说明,dll只可以导出函数,不可以导出指针,但是为了能得到dll中的类,可以导出一个接口,使用接口获取对象指针。但是在dll的调用一方,却无法识别获取到的类指针,所以利用C++的多态和继承的特性来完成。使用抽象类,声明好抽象接口,将需要导出的类继承自该抽象类,并实现其声明的接口。dll的导出方和调用方公用一个抽象类声明文件,在dll导出函数内部,利用多态,将抽象的类指针new成子类对象,并返回出来,则调用方就可以识别得到的类指针了。具体方法说明请参考文章《DLL的动态加载》

Qt的插件系统

Qt的插件系统原理(这里只讨论High-Level API)与此相同,只是Qt利用了其内部非特性,使用QPluginLoader替代了导出类指针的接口,并使用宏定义进行导入和导出,避免了在导出导出上因为为了兼容C语言而导致的一些问题,并简化了一些操作。

使用方法

既然原理相同,则需要的文件也相同,需要导出的dll文件和抽象类的声明文件。不同点是,不需要在使用C风格的函数导出类指针了,Qt里面使用宏定义替代。

步骤:

  1. 定义抽象类,并声明需要用到的接口;
  2. 使用 Q_DECLARE_INTERFACE() 宏定义将这个抽象类的信息告诉Qt的元对象系统;
  3. 建立dll项目,继承自 QObject 和这个抽象类,并实现其接口,我们称这个项目为插件;
  4. 在插件的头文件中使用 Q_PLUGIN_METADATA() 宏定义导出这个插件;
  5. 在插件项目的.pro文件中配置导出设置,编译生成dll;
  6. 在dll调用程序中加载dll;

示例代码:

插件项目plugin:
HEADERS += \
plugin.h SOURCES += \
plugin.cpp QT += widgets DISTFILES += \
plugin.json CONFIG += plugin #表示用于lib模板:库是一个插件
TEMPLATE = lib #表示生成库文件
//plugin.h
class Plugin : public QObject, public PluginInterface
{
Q_OBJECT
Q_PLUGIN_METADATA(IID pluginInterface_iid FILE "plugin.json")
Q_INTERFACES(PluginInterface)
public:
explicit Plugin(QObject *parent = 0);
~Plugin();
void SayHello(QWidget* parent) Q_DECL_OVERRIDE;
};

说明:

  • 必须多继承自 QObject 和抽象类;
  • Q_PLUGIN_METADATA 用于声明元数据,IID 是必须且唯一的,FILE 是可选的,后面跟着一个json文件,用于描述插件的相关数据信息;
  • Q_INTERFACES 的作用是将所实现的插件接口通知给元类型系统,参数是抽象类类名;
  • Q_DECL_OVERRIDE 用于表示这是一个虚函数,编译器会检查这个方法是不是父类中所有的,若没有则报错(类似java中的@Override注解);
//plugin.cpp
Plugin::Plugin(QObject *parent) : QObject(parent)
{
qDebug()<<"constructor";
} Plugin::~Plugin()
{
qDebug()<<"destructor";
} void Plugin::SayHello(QWidget *parent)
{
QMessageBox::information(parent, "plugin", "hello, this is dynamically loaded.");
}
{
"Keys" : [ "plugin" ]
}
调用项目test:

插件生成的dll文件copy到执行目录下,共调用方加载

//interface.h
class PluginInterface
{
public:
virtual ~PluginInterface(){}
virtual void SayHello(QWidget* parent) = 0;
}; #define pluginInterface_iid "io.qt.dynamicplugin"
Q_DECLARE_INTERFACE(PluginInterface, pluginInterface_iid)

说明:

  • Q_DECLARE_INTERFACE 宏告诉Qt,这是一个插件接口,第一个参数是接口类名,第二个参数是插件标识符,大小写敏感,且唯一;
//调用部分
PluginInterface* interface = NULL;
QPluginLoader plugin_loader("plugin.dll");
QObject* plugin = plugin_loader.instance();
if(plugin)
{
interface = qobject_cast<PluginInterface*>(plugin);
if(interface)
{
interface->SayHello(this);
}
//delete plugin;
plugin_loader.unload();
}

注:Qt文档中说明,QPluginLoader 释放之后,其获取到的插件不会释放,所以,需要手动 delete 插件,或者使用 unload() 方法释放。

Qt插件系统的更多相关文章

  1. 【大话QT之十六】使用ctkPluginFramework插件系统构建项目实战

    "使用ctkPluginFramework插件系统构建项目实战",这篇文章是写博客以来最纠结的一篇文章. 倒不是由于技术都多么困难,而是想去描写叙述一个项目架构採用ctkPlugi ...

  2. 【夸QT在十五】ctkPluginFrameWork插件系统Windows编译器

    采用ctkPluginFramework作为一个插件系统开发框架确实有很多优点. 有些车站最近收到的一封信,每个人都想用ctkPluginFramework但我不知道如何建立,本教程对谈ctkPlug ...

  3. ctkPlugin插件系统实现项目插件式开发

    插件式开发体会: 自开始写[大话QT]系列就开始接触渲染客户端的开发,说是开发不如更多的说是维护以及重构,在接手这块的东西之前自己还有点犹豫,因为之前我一直认为客户端嘛,没什么技术含量,总是想做比较有 ...

  4. QT皮肤系统的动态切换

    应用需求: 提供皮肤切换选项,在不重启应用程序的情况下实现皮肤的动态切换. 理论基础: 1) 图片资源是如何被利用的 这里先简要说明一下实现原理,皮肤的动态切换其关键在于图片资源的加载方式.QT中每个 ...

  5. 【Win10+eclipse+MinGW+QT安装教程】已有eclipse环境下配置QT插件出错详解

    (纪念一下装了一整天濒临绝望的自己[/doge]) (以下所有下载项一律使用32位) step1:下载安装eclipse.适配的jdk.MinGW和QT 1.下载32位eclipse for c++以 ...

  6. [Qt Creator 快速入门] 第9章 国际化、帮助系统和Qt插件

    一.国际化 国际化的英文表述为Internationalization,通常简写为I18N(首尾字母加中间的字符数),一个应用程序的国际化就是使该应用程序可以让其他国家的用户使用的过程. Qt支持现在 ...

  7. 设置qt插件路径

    1.在Qt中使用 WebKit 浏览器核心 使用 QtWebKit 需要在工程文件(*.pro)中加入: QT +=webkitQT += network 2.QtWebKit的flash支持 QtW ...

  8. Win7下安装VS2017、安装Qt5.10.1以及在VS2017添加qt插件

    一.安装VS2017 1.下载VS2017 进入vs下载官网https://www.visualstudio.com/zh-hans/downloads/,选择所需要的vs版本,进行在线安装. 2.安 ...

  9. CloudNotes之桌面客户端篇:插件系统的实现

    [CloudNotes版本更新历史与各版本下载地址请点击此处] [CloudNotes中文系列文章汇总列表请点击此处] [查看CloudNotes源代码请点击此处] 有时候,同一个名词,针对不同的人群 ...

随机推荐

  1. 1 - Apache HttpClient 简单使用

    Apache HttpClient 是Apache 开源的实现Http协议的java开源库. HttpClien 是客户端的HTTP通信实现库,实现HTTP GET 和POST请求,获取响应内容. A ...

  2. Cobalt Stike使用教程

    目录: 安装与连接 监听器Listner 基本使用方法--Cobalt Strike生成后门 Beacon详解 菜单栏与视图 文件管理与进程管理 浏览器代理 Cobalt Strike扩展 提权 横向 ...

  3. 再接再厉,JSONViewer现已支持Firefox、Microsoft Edge、360浏览器,可能是最好用的JSON格式化工具

    之前写的JSONViewer,截至目前在谷歌商店里已经有1000+的自然下载量了 为什么开发JSONViewer? 日常开发中,拿到接口输出的JSON一般会去在线的JSON格式化网站查看,但是在线格式 ...

  4. python 异步 I/O

    如果你想了解异步编程,那么必然会涉及出许多相关概念. 堵塞/非堵塞 同步/异步 多进程/多线程/协程 为什么我要学习这个话,因为我想搞懂异步框架和异步接口的调用.所以,我的学习路线是这样的: 1.py ...

  5. 谈谈MySQL的索引

    目录 索引 前言 是什么 B树 B+树 B树和B+树结构上异同 有什么用 怎么用 索引 前言 总所周知,数据库查询是数据库的最主要功能之一.我们都希望查询数据的速度能尽可能的快.而支撑这一快速的背后就 ...

  6. 理解RESTful API

    近日妹子向我求助RESTful API到底是个什么东西.原因是她们公司一个新启动的项目因为RESTful API起了争执.服务端同学坚持要用RESTful API,而前端同学则认为服务端用RESTfu ...

  7. Android 开发技术周报 Issue#277

    新闻 Android 11界面再调整:加入快速截屏.多任务向国产ROM看齐 最新版Android 11推送 谷歌Pixel 5被曝光:支持反向充电 4月Android系统版本分布:8.0 Oreo最主

  8. 苹果登录服务端JWT算法验证-PHP

    验证参数 可用的验证参数有 userID.authorizationCode.identityToken,需要iOS客户端传过来 验证方式 苹果登录验证可以选择两种验证方式 具体可参考这篇文章 htt ...

  9. Linux 日常操作

    Linux 日常操作 */--> Linux 日常操作 Table of Contents 1. 查看硬件信息 1.1. 服务器型号序列号 1.2. 主板型号 1.3. 查看BIOS信息 1.4 ...

  10. [wp]xctf newscenter

    手工注入 查询所有数据库名称和表名 ' union select 1,table_schema,table_name from information_schema.tables# 发现就两个数据库i ...