Qt 4 插件开发(4)

前面我们已经完成了一个插件 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&lt;MonsterInterface *&gt;(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 Comments

  1. 米米 2012年8月2日
    • DevBean 2012年8月2日
  2. 米米 2012年8月2日
  3. 精英王子 2012年8月5日
    • DevBean 2012年8月5日
  4. aben 2013年11月28日

Leave a Reply