前面我们已经完成了一个插件 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的回复。期待这您的更新
很好,基本每篇文章我都会看
感谢支持!
看了你的文章,受益匪浅,比自己琢磨快捷很多。过去,自己琢磨,彻夜难眠,但不得其解。感谢