说明

近期入职新公司,新公司的项目用到了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. Jenkins 批量创建任务的三种方法

    最近,要搭建多套测试环境,需要把 Jenkins 中 dev 视图下的所有任务批量复制到 sit 等视图下. 说明 Jenkins 任务名称规则为:[测试环境标识]-[工程名称],如:dev-daod ...

  2. stand up meeting 12/4/2015 -12/6/2015

    part 组员 今日工作 工作耗时/h 明日计划 工作耗时/h UI 冯晓云        ------  --- 数据库 朱玉影  等待张葳出关        0  foxit reader  6 ...

  3. 【简单了解系列】从基础的使用来深挖HashMap

    HashMap定义 说的专业一点,HashMap是常用的用于存储key-value键值对数据的一个集合,底层是基于对Map的接口实现.每一个键值对又叫Entry,这些Entry分散的存储在一个由数组和 ...

  4. PrestoSPI安全扩展

    由于Presto官方文档和谷歌搜索都没有相关的内容,git项目中也没有支持sentry的安全插件扩展,因此只能从源码中寻找答案,在梳理完SPI包的安全相关源码结构后,已实现了一个自定义的安全插件,经验 ...

  5. webform repeater 的使用

    1  repeater    定义: 重复器    根据数据库里的内容将repeater里的内容重复赋值 ,在itemtemplate下   配合<%# Eval(" ") ...

  6. idea 激活方法

    转载自: https://www.jianshu.com/p/7d60ea5e51e9

  7. 两种异常(CPU异常、用户模拟异常)的收集

    Windows内核分析索引目录:https://www.cnblogs.com/onetrainee/p/11675224.html 两种异常(CPU异常.用户模拟异常)的收集  文章的核心:异常收集 ...

  8. SpringCloud(六)学习笔记之Zuul

    Zuul 在云平台上提供动态路由,监控,弹性,安全等边缘服务的框架.Zuul 相当于是设备和 Netflix 流应用的 Web 网站后端所有请求的前门 Hystrix+Ribbon(不使用Feign) ...

  9. JDBC 中的事务和批处理 batch

    JDBC事务处理: 事务处理一般在事务开始前把事务提交设置为false 所有DML语句执行完成后提交事务 demo: package com.xzlf.jdbc; import java.sql.Co ...

  10. BypassUAC

    BypassUAC 本篇主要介绍如何以ICMLuaUtil方式BypassUAC,主要内容如下: 过掉UAC提示框的方法总结 UACME项目 什么类型的COM interface可以利用? 如何快速找 ...