VC驿站

 找回密码
 加入驿站

QQ登录

只需一步,快速开始

搜索
查看: 205|回复: 2

基于CTK Plugin Framework的插件版本动态升级

[复制链接]
57_avatar_middle
最佳答案
22 
在线会员 发表于 2020-4-22 19:32:58 | 显示全部楼层 |阅读模式
20驿站币
最近在研究CTK框架,在实现基于CTK Plugin Framework的插件版本动态升级时遇到的问题。
环境:
1.windows 10 专业版 64位
2.qt5.12.4+vs2015
主要参考:
CTK相关文章

需实现功能:
程序启动时遍历程序路径下plugins目录,加载目录中的插件PluginA和PluginB,并监视plugins目录。运行时目录下的文件发生变化,再次遍历plugins目录,如果是新的插件则加载,若是已有插件但版本更高则升级。
在程序运行中手动将PluginA的升级版插件PluginAUpgrade拷贝进plugins中触发升级插件事假。

细节描述:
①监视plugins的相关代码如下:
  1. QString sPluginsPath      = QString("%1/plugins").arg(QApplication::applicationDirPath());
  2.     this->pFileSystemWatcher_ = new QFileSystemWatcher();
  3.     this->pFileSystemWatcher_->addPath(sPluginsPath);
  4.     connect(pFileSystemWatcher_, SIGNAL(directoryChanged(QString)), this,SLOT(slotTriggerDirectoryChanged(QString)));
复制代码

②PluginA、PluginB、PluginAUpgrade分别实现打印PluginA、PluginB、PluginAUpgrade的功能。
③程序启动时加载CTK插件的代码如下:
  1. void CTKPluginsManager::loadCTKPlugins() {
  2.     QDirIterator itPlugin(sPluginPath_,QStringList() << "*.dll" << "*.so",QDir::Files);

  3.     while (itPlugin.hasNext()) {
  4.         QString strPlugin = itPlugin.next();
  5.         try {
  6.             QSharedPointer<ctkPlugin> plugin =this->pContext_->installPlugin(QUrl::fromLocalFile(strPlugin));
  7.             // 获取符号名
  8.             QString sYmb = plugin->getSymbolicName();
  9.             plugin->start(ctkPlugin::START_TRANSIENT);
  10.             mapCTKPlugins.insert(std::make_pair(sYmb.toStdString(), plugin));

  11.             ctkServiceReference reference = this->pContext_->getServiceReference<ToolPluginService>();
  12.             if (reference) {
  13.                 ToolPluginService *service = qobject_cast<ToolPluginService *>(this->pContext_->getService(reference));
  14.                 if (service != Q_NULLPTR) {
  15.                     mapCTKPluginServices_.insert(std::make_pair(sYmb.toStdString(), service));
  16.                 }
  17.             }
  18.             emit signalRegisterTool(symb);
  19.         } catch (const ctkPluginException &e) {
  20.             Q_UNUSED(e)
  21.             return;
  22.         }
  23.     }
  24. }
复制代码

④此处是目录中文件发生改变的处理,我这里考虑的更新方法是CTKPluginsManager(单例)抛出信号,外部处理。
  1. void CTKPluginsManager::slotTriggerDirectoryChanged(const QString &sPluginPath) {
  2.      QDirIterator ditPlugin(sPluginPath,QStringList() << "*.dll"<< "*.so",QDir::Files);

  3.     while (ditPlugin.hasNext()) {
  4.         QString strPlugin = ditPlugin.next();
  5.         
  6.                 try {
  7.                         //此处需要优化处理,应获取PluginName和PluginVersion判断。
  8.                         //如果已经安装且版本不高于当前版本则不处理。
  9.                         //目前没思考到在不安装插件的情况下读取相关信息,若安装已经安装的插件会抛出异常。
  10.                         if (sPlugin != QApplication::applicationDirPath() + "/plugins/PluginAUpgrade.dll")
  11.                                 continue;
  12.        
  13.                         QString                   strPlugin = sPlugin;
  14.                         QSharedPointer<ctkPlugin> plugin = this->pContext_->installPlugin(QUrl::fromLocalFile(sPlugin));
  15.                         // 获取符号名
  16.                         QString sYmb = plugin->getSymbolicName();
  17.                         plugin->start(ctkPlugin::START_TRANSIENT);

  18.                         //此处应增加处理,根据version判断是升级时,将内存中的相关信息移除。
  19.                         mapCTKPlugins.insert(std::make_pair(sYmb.toStdString(), plugin));
  20.        
  21.                         ctkServiceReference reference = this->pContext_->getServiceReference<ToolPluginService>();
  22.                         if (reference) {
  23.                                 ToolPluginService *service = qobject_cast<ToolPluginService *>(this->pContext_->getService(reference));
  24.                                 if (service != Q_NULLPTR) {
  25.                                         mapCTKPluginServices_.insert(std::make_pair(sYmb.toStdString(), service));
  26.                                 }
  27.                         }

  28.                         emit signalRegisterTool(sYmb);
  29.                 } catch (const ctkPluginException &e) {
  30.                         QString m_strPluginLog = QObject::tr("Failed to load %1: ctkPluginException(%2).\r\n").arg(sPlugin).arg(e.what());
  31.                         break;
  32.                 }
  33.     }
  34. }
复制代码

⑤插件的实现比较基础,Service,Activator,Impl。MANIFEST.MF中信息包含PluginName、PluginVersion。
⑥PluginA和PluginAUpgrade区别:
PluginA提供的接口打印PluginA,PluginAUpgrade提供的接口打印PluginAUpgrade。
MANIFEST.MF中,PluginA的version为1.0,PluginAUpgrade的version为1.1
PluginA输出文件PluginA1.0.dll,PluginAUpgrade输出文件PluginA1.1.dll
PluginAUpgrade是在PluginA的基础上做出以上调整。

目前手动将PluginAUpgrade插件的输出文件PluginA1.1.dll拷贝至plugin中时程序运行出错:
(此处将无关的目录信息修改为***)
  1. QIODevice::read (QFile, ":\PluginA1\META-INF\MANIFEST.MF"): device not open
  2. Exception at 0x7ffe7763a859, code: 0xe06d7363: C++ exception, flags=0x1 (execution cannot be continued) (first chance) at ***\libs\pluginframework\ctkpluginarchivesql.cpp:58
  3. Exception at 0x7ffe7763a859, code: 0xe06d7363: C++ exception, flags=0x1 (execution cannot be continued) (first chance) at ***\libs\pluginframework\ctkpluginstoragesql.cpp:409
  4. Exception at 0x7ffe7763a859, code: 0xe06d7363: C++ exception, flags=0x1 (execution cannot be continued) (first chance) in VCRUNTIME140D!_BuildCatchObjectHelper
  5. Exception at 0x7ffe7763a859, code: 0xe06d7363: C++ exception, flags=0x1 (execution cannot be continued) (first chance) at ***\libs\pluginframework\ctkpluginstoragesql.cpp:390
  6. Exception at 0x7ffe7763a859, code: 0xe06d7363: C++ exception, flags=0x1 (execution cannot be continued) (first chance) in VCRUNTIME140D!_BuildCatchObjectHelper

  7. Exception at 0x7ffe7763a859, code: 0xe06d7363: C++ exception, flags=0x1 (execution cannot be continued) (first chance) at ***\libs\pluginframework\ctkplugins.cpp:136

  8. Exception at 0x7ffe7763a859, code: 0xe06d7363: C++ exception, flags=0x1 (execution cannot be continued) (first chance) at ***\libs\pluginframework\ctkpluginstoragesql.cpp:82
  9. QWaitCondition: Destroyed while threads are still waiting
复制代码


此处我看懂了无法打开":\PluginA1\META-INF\MANIFEST.MF",但后续没有太好的研究方向了。
核心的代码基本上展示出来了,如果各位需要本地调试在github上获取demo进行调整:https://github.com/knowledge0603/ctk-demo-plugin-qt
,下载后需要CTK源码编译好的库加入进项目中。
希望有人可以帮我完成CTK插件的动态更新,或者给我提供一些思路,如果思路或者代码有不足之处请指正,提前感谢各位。





上一篇:dll注入问题
下一篇:高级班数据库P9环境搭建
57_avatar_middle
最佳答案
22 
ico_lz  楼主| 发表于 2020-4-23 10:38:57 | 显示全部楼层
关于:QIODevice::read (QFile, ":\PluginA1\META-INF\MANIFEST.MF"): device not open
具体原因:pro文件中TARGET设置和qrc中MANIFEST.MF文件的路径/PluginA/META-INF
TARGET:指定可执行文件或库的基本文件名,默认为当前目录名
①CTK中查找MANIFEST.MF路径::/TARGET/META-INF/MANIFEST.MF
②pro文件中设置TARGET后,CTK获取项目名称时,对“.”的处理不完善,如:
TARGET=PluginA1.0.0,则路径为 :/PluginA1/META-INF/MANIFEST.MF
TARGET=PluginA.1.0.0,则路径为 :/PluginA/META-INF/MANIFEST.MF
TARGET=PluginA_1_0_0,则路径为 :\PluginA.1.0.0\META-INF\MANIFEST.MF
目前仅测试当前几种情况
77_avatar_middle
最佳答案
31 
online_vip 发表于 2020-4-23 16:19:31 | 显示全部楼层
JLDawson 发表于 2020-4-23 10:38
关于:QIODevice::read (QFile, ":\PluginA1\META-INF\MANIFEST.MF"): device not open
具体原因:pro文件 ...

这个东西还真是没接触过,帮不上楼主了。。。
您需要登录后才可以回帖 登录 | 加入驿站 qq_login

本版积分规则

×【发帖 友情提示】
1、请回复有意义的内容,请勿恶意灌水;
2、纯数字、字母、表情等无意义的内容系统将自动删除;
3、若正常回复后帖子被自动删除,为系统误删的情况,请重新回复其他正常内容或等待管理员审核通过后会自动发布;
4、感谢您对VC驿站一如既往的支持,谢谢合作!

关闭

站长提醒上一条 /2 下一条

QQ|小黑屋|手机版|VC驿站 ( 辽ICP备09019393号 )|网站地图wx_jqr

GMT+8, 2020-10-24 22:44

Powered by CcTry.CoM

© 2009-2020 cctry.com

快速回复 返回顶部 返回列表