当前位置: 首页 > 工具软件 > Qlib > 使用案例 >

Qt插件:QLibrary

杨阳飇
2023-12-01

一、描述

QLibrary 对象的实例对单个共享对象文件(库文件)进行操作。QLibrary 以独立于平台的方式提供对库中功能的访问。可以在构造函数中传递文件名,也可以使用 setFileName() 显式设置它。加载库时,除非文件名具有绝对路径,否则QLibrary 将搜索所有系统特定的库位置(例如 Unix 上的 LD_LIBRARY_PATH)。

如果文件名是绝对路径,则首先尝试加载此路径。如果找不到文件,QLibrary 会尝试使用不同平台特定的文件前缀(如 Unix 和 Mac 上的“lib”)和后缀(如 Unix 上的“.so”、Mac 上的“.dylib”或“.dylib”、在 Windows 上的“.dll”)。

如果文件名不是绝对路径,那么 QLibrary 会修改搜索顺序以首先尝试系统特定的前缀和后缀,然后是指定的文件路径。这使得指定仅由其基本名称(即没有后缀)标识的共享库成为可能,因此相同的代码可以在不同的操作系统上运行,但仍然可以最大限度地减少尝试查找库的次数。

因为 QLibrary 会自动查找具有适当后缀的文件,所以建议在文件名中省略文件的后缀。

QLibrary 的多个实例可用于访问同一个物理库。加载后,库将保留在内存中,直到应用程序终止。

解析库中的导出符号

QLibrary 的典型用途是解析库中的导出符号,并调用该符号表示的 C 函数。这被称为“显式链接”,与“隐式链接”相反,当将可执行文件链接到库时,这是由构建过程中的链接步骤完成的。

以下代码片段加载一个库,解析符号“mysymbol”,并在成功时调用该函数。如果出现问题,例如库文件不存在或符号未定义,函数指针将为 nullptr 且不会被调用。

QLibrary myLib("mylib");
typedef void (*MyPrototype)();
MyPrototype myFunction = (MyPrototype) myLib.resolve("mysymbol");
if (myFunction)
    myFunction();

符号必须从库中导出为 C 函数,resolve() 才能工作。 这意味着如果库是使用 C++ 编译器编译的,则该函数必须包含在 extern "C" 块中。 在 Windows 上,这也需要使用 dllexport 宏。 为方便起见,如果只想调用库中的函数而不先显式加载库,则可以使用静态 resolve() 函数:

typedef void (*MyPrototype)();
MyPrototype myFunction = (MyPrototype) QLibrary::resolve("mylib", "mysymbol");
if (myFunction)
    myFunction();

二、类型成员

1、enum QLibrary::LoadHint:此枚举描述了可用于更改库加载(load())时处理方式的可能标志。这些值指示加载库时如何解析符号。默认情况下,这些标志都没有设置,并且不会导出外部符号以在其他动态加载的库中解析。在库加载后设置标志无效,并且 loadHints() 不会反映标志更改。

  • ResolveAllSymbolsHint:库中的所有符号在加载时被解析,而不仅仅是在调用 resolve() 时。
  • ExportExternalSymbolsHint:导出库中未解析的和外部符号,以便它们可以在稍后加载的其他动态加载的库中解析。
  • LoadArchiveMemberHint:库的文件名指定存档文件中的特定目标文件。如果设置此标志,则文件名由两个部分组成:路径是对存档文件的引用,后跟是对存档成员的引用的第二个部分。例如,文件名 libGL.a(shr_64.o) 将引用名为 libGL.a 的存档文件中的库 shr_64.o。 这仅在 AIX 平台上受支持。
  • PreventUnloadHint:阻止库从地址空间通过close()卸载。如果之后再有open()调用,库中的静态变量不会重新初始化。
  • DeepBindHint:链接器在解析加载库中的外部符号时优先选择加载库中的定义而不是加载应用程序中的导出定义。此选项仅在 Linux 上受支持。

三、成员函数

1、QLibrary(const QString &fileName, const QString &version, QObject *parent = nullptr)

将加载由 fileName 和完整版本号 version 指定的库。版本号在 Windows 上被忽略。

2、QLibrary(const QString &fileName, int verNum, QObject *parent = nullptr)

将加载由 fileName 和主要版本号 verNum 指定的库。版本号在 Windows 上被忽略。

3、QLibrary(const QString &fileName, QObject *parent = nullptr)

将加载由 fileName 指定的库。

4、~QLibrary()

销毁 QLibrary 对象。除非显式调用 unload(),否则库将保留在内存中,直到应用程序终止。

5、QString errorString()

返回一个文本字符串,其中包含对发生的最后一个错误的描述。当前,仅当 load()、unload() 或 resolve() 由于某种原因失败时才会设置 errorString。 

6、【静态】bool isLibrary(const QString &fileName)

fileName 是否具有可加载库的有效后缀。

  • Windows:*.dll、*.DLL
  • Unix/Linux:*.so
  • AIX:*.a
  • HP-UX:*.sl、 *.so (HP-UXi)
  • macOS、iOS:*.dylib、*.bundle、*.so

Unix 上的尾随版本号将被忽略。

7、bool isLoaded() 

库是否已加载。

8、bool load()

加载库,如果库加载成功则返回true;否则返回false。由于 resolve() 总是在解析任何符号之前调用此函数,因此没有必要显式调用它。

9、QFunctionPointer resolve(const char *symbol)

【静态】QFunctionPointer resolve(const QString &fileName, const char *symbol)

【静态】QFunctionPointer resolve(const QString &fileName, int verNum, const char *symbol)

【静态】QFunctionPointer resolve(const QString &fileName, const QString &version, const char *symbol)

返回导出的符号符号的地址。如有必要,将加载库。 如果无法解析符号或无法加载库,则该函数返回 nullptr。

QFunctionPointer 是 void (*)() 的 typedef,指向不带参数并返回 void 的函数的指针。

例:

添加一个库工程。内容如下:

testlibrary.h

#ifndef TESTLIBRARY_H
#define TESTLIBRARY_H

extern "C" __declspec(dllexport) int sum(int a, int b);

#endif

testlibrary.cpp(必须有cpp文件)

#include "testlibrary.h"

int sum(int a, int b)
{
    return a+b;
}

创建一个工程导出整个库中的函数:

using Fun = int (*)(int, int);

int main(int argc, char *argv[])
{
    QLibrary lib("testLibrary");
//  Fun fun = reinterpret_cast<Fun>(QLibrary::resolve("testLibrary","sum"));
    Fun fun = reinterpret_cast<Fun>(lib.resolve("sum"));
    if (fun)
    {
        qDebug()<<fun(100,200);
    }
    else
    {
        qDebug()<<lib.errorString();
    }
}

知识点:类型转换 函数指针

10、void setFileNameAndVersion(const QString &fileName, int versionNumber)

将 fileName 属性和主要版本号分别设置为 fileName 和 versionNumber。在 Windows 上忽略 versionNumber。

void setFileNameAndVersion(const QString &fileName, const QString &version)

将 fileName 属性和完整版本号分别设置为 fileName 和 version。在 Windows 上忽略version 。

11、bool unload()

如果可以卸载库,则卸载库并返回 true;否则返回false。

这在应用程序终止时自动发生,因此通常不需要调用此函数。

如果 QLibrary 的其他实例正在使用相同的库,则调用将失败,并且只有在每个实例都调用了 unload() 时才会发生卸载。

注意,在 Mac OS X 10.3 (Panther) 上,无法卸载动态库。

 类似资料: