前面我们已经完成了一个插件 Troll,也已经将文件位置放到了合适的位置(通过 .pro 文件的配置)。现在,我们将来完善下main()
函数,让我们的小游戏(姑且这么认为吧)能够加载插件。
下面就是完整的main()
函数代码:
#include <QtCore> #include "monsterinterface.h" int main(int argc, char *argv[]) { QCoreApplication app(argc, argv); QDir pluginsDir(QDir(app.applicationDirPath()) .canonicalPath() .append(QDir::separator()) .append("plugins")); foreach (QString fileName, pluginsDir.entryList(QDir::Files)) { QFileInfo pluginFileInfo(fileName); if (pluginFileInfo.completeSuffix() == "dll" || pluginFileInfo.completeSuffix() == "so") { QPluginLoader *pluginLoader = new QPluginLoader(pluginsDir.absoluteFilePath(fileName), &app); QObject *pluginObject = pluginLoader->instance(); if (pluginObject) { MonsterInterface *monster = qobject_cast<MonsterInterface *>(pluginObject); qDebug() << monster->name(); } } } return app.exec(); }
我们将来着重分析下这段代码。在QCoreApplication
实例创建之间,我们需要添加我们自己的代码。首先,我们创建一个QDir
对象。这个目录将会是我们的程序扫描的插件目录。为了保持程序的可移植性,我们不能使用硬编码设置插件目录。第一,目录的基础路径是app.applicationDirPath()
,该目录即可执行文件所在目录。canonicalPath()
函数将applicationDirPath()
中包含的 .、.. 或者其它一些相对符号全部过滤掉,得到一个规范的路径。然后在后面追加一个QDir::separator()
,即路径分隔符(使用这个函数可以获得平台无关的路径分隔符,而不需要是 / 或者 \ 这种);最后再追加 plugins 目录名。
举个例子,如果app.applicationDirPath()
返回的是 C:\Demos\GameSystem\.,那么其canonicalPath()
返回的是规范的路径,去除 .、.. 以及连接符号(Unix 平台)等,也就是 C:\Demos\GameSystem。在后面追加QDir::separator()
,Windows 平台上将返回 \,那么就变成了 C:\Demos\GameSystem\,再追加 plugins,最终得到插件目录 C:\Demos\GameSystem\plugins。由于 exe 文件就是在 C:\Demos\GameSystem 中,这个插件目录就是在 exe 同目录下的 plugins 文件夹中。(回忆一下我们设置的 .pro,就是得到了这种目录结构。)
现在已经获得插件目录,接下来的 for 循环,遍历目录中每一个文件(注意,由于我们的目录结构是,plugins 目录下直接就是插件文件,因此不需要递归遍历其中所有子目录)。首先判断文件后缀名是不是 dll 或者 so(插件在 Windows 平台上后缀名是 dll;类 Unix 平台上是 so)。如果是的话,进行下面的操作:创建一个QPluginLoader
对象,提供两个参数,第一个是插件文件名,以绝对地址给出;第二个是 parent 指针,这里就是 app 的指针。在QPluginLoader
对象创建完毕,使用其instance()
函数可以获得这个插件对象,该函数返回插件实现的那个对象(我们的插件一定是继承QObject
的,还记得吗?),其生命周期与整个 loader 相同,也就是说,当你第二次调用instance()
的时候,其实返回的是同一个对象。如果创建成功,则使用qobject_cast
将其转换成我们的接口对象。这样,便可以调用接口中定义的各个函数。
接下来,运行一下我们的程序吧!如果顺利的话,应该在控制台看到 Troll 这个名字。
然后,我们可以仿照 Troll 再新建一个项目,比如就叫 Argus(阿耳戈斯,百眼巨人)。代码几乎和 Troll 的一样,只是把name()
函数的返回值设置为 Argus。最后,将生成的 dll 放在 plugins 目录下,重新运行程序。如果你看到了两个怪物,那就说明我们的插件已经加载进来了!
虽然例子很简单,我们已经由此看出,如果要增加更多的怪物,只需创建更多的插件即可。这就是插件机制扩展应用程序的一般方法。
6 评论
无意搜索发现了您的网站。看了不少。受益匪浅。十分感谢。这个系列我也一直在关注。希望这个系列能讲的更深入些。多写一些。呵呵。比如插件国际化什么的。自己研究了下没搞懂。 😀
插件的主要问题还是在于与主程序的交互。国际化方面,同一般程序没有什么区别,就是用 tr() 就可以了。
呵呵。就在刚刚。解决了问题。谢谢DevBean的回复。期待这您的更新
很好,基本每篇文章我都会看
感谢支持!
看了你的文章,受益匪浅,比自己琢磨快捷很多。过去,自己琢磨,彻夜难眠,但不得其解。感谢