Qt 插件路径(笔记)

晨曦之光 发布于 2012/05/08 10:15
阅读 1K+
收藏 0

Qt Manual 已经专门介绍了Deploying Plugins 的问题。半年前Qt 插件学习(一) 也简单整理了一点路径相关的问题。

可是,一直以来没理清:图片插件、编解码插件、数据库插件... 到底是如何被加载的?

走马观花

如果我们需要打开或保存一个jpg格式的图片,那么需要加载jpg的插件。程序去何处找插件:

表面的答案:

$QTDIR/plugins/imageformats/
you_app_path/imageformats/

注:如果你只是想让插件能工作,对其他不感兴趣,直接在你的应用程序所在目录下创建一个 imageformats 目录,把插件放进去就行了。其他插件类推,分别创建相应的子目录即可。

真实答案

 

QStringList QCoreApplication::libraryPaths()

对于图片插件,程序遍历这个字符串列表,将每一个字符串分别和/imageformats 连接后构成新的路径,然后依次尝试加载这些路径下的动态库。

看点代码(有删节)

void QFactoryLoader::update()
{
    QStringList paths = QCoreApplication::libraryPaths();
    for (int i = 0; i < paths.count(); ++i) {
        const QString &pluginDir = paths.at(i);
        QString path = pluginDir + d->suffix;

        QStringList plugins = QDir(path).entryList(QDir::Files);
        QLibraryPrivate *library = 0;
        for (int j = 0; j < plugins.count(); ++j) {
            QString fileName = QDir::cleanPath(path + QLatin1Char('/') + plugins.at(j));
             library = QLibraryPrivate::findOrCreate(QFileInfo(fileName).canonicalFilePath());
        }
     }
...

对于图片插件来说,d->suffix 就是/imageformats ,这是通过一个返回值为QFactoryLoader* 的静态 loader() 函数来实现的:

Q_GLOBAL_STATIC_WITH_ARGS(QFactoryLoader, loader,
                          (QImageIOHandlerFactoryInterface_iid, QLatin1String("/imageformats")))

感觉不对?

前面似乎不对啊?不光和我们的表面答案对不上,而且Manual中或其他人的文章都提到还有很多东西,比如:

  • QLibraryInfo::location(QLibraryInfo::PluginsPath )

  • :/qt/etc/qt.conf
  • QT_PLUGIN_PATH
  • ...
  • QCoreApplication::addLibraryPath()
  • QCoreApplication::setLibraryPaths()

除了后面两个直接相关外,其他合libraryPaths似乎没什么联系。你怎么能说 和 QCoreApplication::libraryPaths() 有关呢??!!

尝试用事实说话...

QCoreApplication::libraryPaths()

  • 创建一个空的QStringList
  •   QLibraryInfo::location(QLibraryInfo::PluginsPath)   路径加入

  • 确保将应用程序本身所在路径QCoreApplication::applicationFilePath() 加入

  •   QT_PLUGIN_PATH 环境变量指定的路径依次加入

源码:

QStringList QCoreApplication::libraryPaths()
{
    if (!coreappdata()->app_libpaths) {
        QStringList *app_libpaths = coreappdata()->app_libpaths = new QStringList;
        QString installPathPlugins =  QLibraryInfo::location(QLibraryInfo::PluginsPath);
        if (QFile::exists(installPathPlugins)) {
            // Make sure we convert from backslashes to slashes.
            installPathPlugins = QDir(installPathPlugins).canonicalPath();
            if (!app_libpaths->contains(installPathPlugins))
                app_libpaths->append(installPathPlugins);
        }
        // If QCoreApplication is not yet instantiated,
        // make sure we add the application path when we construct the QCoreApplication
        if (self) self->d_func()->appendApplicationPathToLibraryPaths();

        const QByteArray libPathEnv = qgetenv("QT_PLUGIN_PATH");
        if (!libPathEnv.isEmpty()) {
#if defined(Q_OS_WIN) || defined(Q_OS_SYMBIAN)
            QLatin1Char pathSep(';');
#else
            QLatin1Char pathSep(':');
#endif
            QStringList paths = QString::fromLatin1(libPathEnv).split(pathSep, QString::SkipEmptyParts);
            for (QStringList::const_iterator it = paths.constBegin(); it != paths.constEnd(); ++it) {
                QString canonicalPath = QDir(*it).canonicalPath();
                if (!canonicalPath.isEmpty()
                    && !app_libpaths->contains(canonicalPath)) {
                    app_libpaths->append(canonicalPath);
                }
            }
        }
...

现在似乎舒服多了,可是QLibraryInfo::location(QLibraryInfo::PluginsPath) 又是怎么来的呢?

QLibraryInfo::location(QLibraryInfo::PluginsPath)

它受两方面影响:

  • 如果有符合条件的 qt.conf 文件,将使用该文件的内容
  • 如果没有这样的 qt.conf 文件,将使用编译Qt时写死的内容

先看后者:

configure

编译Qt的第一步是configure,它将在 src/corelib/global目录下生成 qconfig.h 和 qconfig.cpp 两个文件。

打开 qconfig.cpp 文件(可以看到类似下面的内容):

static const char qt_configure_installation          [11  + 12] = "qt_instdate=2011-06-08";

static const char qt_configure_prefix_path_str       [512 + 12] = "qt_prfxpath=E://Qt5//qtbase-build";
static const char qt_configure_binaries_path_str     [512 + 12] = "qt_binspath=E://Qt5//qtbase-build//bin";
static const char qt_configure_plugins_path_str      [512 + 12] = "qt_plugpath=E://Qt5//qtbase-build//plugins";

#define QT_CONFIGURE_BINARIES_PATH qt_configure_binaries_path_str + 12;
#define QT_CONFIGURE_PLUGINS_PATH qt_configure_plugins_path_str + 12;

注意一点:宏 QT_CONFIGURE_PLUGINS_PATH 指向   "qt_plugpath=E://Qt5//qtbase-build//plugins";

qt.conf

  • 先看资源文件中有无  :/qt/etc/qt.conf

  • 不存在则检查应用程序所在路径下有无 qt.conf 文件
  • 找到则返回相应的QSettings,否则返回0

 

QSettings *QLibraryInfoPrivate::findConfiguration()
{
    QString qtconfig = QLatin1String(":/qt/etc/qt.conf");
    if (!QFile::exists(qtconfig) && QCoreApplication::instance()) {
            {
                QDir pwd(QCoreApplication::applicationDirPath());
                qtconfig = pwd.filePath(QLatin1String("qt.conf"));
            }
    }
    if (QFile::exists(qtconfig))
        return new QSettings(qtconfig, QSettings::IniFormat);
    return 0; 
}

合二为一

直接贴点代码片段,不解释

QString
QLibraryInfo::location(LibraryLocation loc)
{
    QString ret;
    if(!QLibraryInfoPrivate::configuration()) {
        const char *path = 0;
        switch (loc) {
...
        case PluginsPath:
            path = QT_CONFIGURE_PLUGINS_PATH;
            break;
...
        default:
            break;
        }

        if (path)
            ret = QString::fromLocal8Bit(path);
    } else {
        QString key;
        QString defaultValue;
        switch(loc) {
        case PluginsPath:
            key = QLatin1String("Plugins");
            defaultValue = QLatin1String("plugins");
            break;
...

cache

为了加快插件的加载和校验,会用QSettings保存一些插件的信息。

看个代码片段:

QFactoryLoader::update()
{
    QSettings settings(QSettings::UserScope, QLatin1String("Trolltech"));
    QString regkey = QString::fromLatin1("Qt Factory Cache %1.%2/%3:/%4")
                             .arg((QT_VERSION & 0xff0000) >> 16)
                             .arg((QT_VERSION & 0xff00) >> 8)
                             .arg(QLatin1String(d->iid))
                             .arg(fileName);
    QStringList reg;
    reg << library->lastModified;
    reg += keys;
    settings.setValue(regkey, reg);

以及

bool QLibraryPrivate::isPlugin(QSettings *settings)
{
...
    QString regkey = QString::fromLatin1("Qt Plugin Cache %1.%2.%3/%4")
                     .arg((QT_VERSION & 0xff0000) >> 16)
                     .arg((QT_VERSION & 0xff00) >> 8)
                     .arg(QLIBRARY_AS_DEBUG ? QLatin1String("debug") : QLatin1String("false"))
                     .arg(fileName);
...

看看Windows下的注册表内容:

HKCU/Software/Trolltech/OrganizationDefaults/Qt Factory Cache 4.7/com.trolltech.Qt.QImageIOHandlerFactoryInterface:/F:/Qt4/plugins/imageformats/qtjpeg4.dll = 2010-09-29T14:40:30 jpeg jpg

linux下 ~/.config/Trolltech.conf

[Qt Factory Cache 4.7]
com.trolltech.Qt.QImageIOHandlerFactoryInterface:/home/Qt4/plugins/imageformats/qtjpeg4.dll = 2011-04-22T17:21:23 jpeg jpg

参考


原文链接:http://blog.csdn.net/dbzhang800/article/details/6543489
加载中
返回顶部
顶部