百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术文章 > 正文

QLibrary 加载动态库(qlibrary加载动态库调用函数)

cac55 2024-10-07 06:43 70 浏览 0 评论

阅读本文大概需要 6.6分钟

一般情况下在没有头文件支持情况下,想要引入某个动态库,最好的办法就是使用「动态加载」的方法,在Qt中一般使用QLibyary来操作

常用 api

 QLibrary(const QString& fileName, QObject *parent = nullptr);
 QFunctionPointer resolve(const char *symbol);
 bool load();
 bool unload();
 bool isLoaded() const;

官方说明[1]

下面我们来看下详细的使用过程

创建动态库

我们创建一个「子工程」,用来编写我们的测试方法,该工程需要导出为动态库,完整的示例如下所示

├─App
│      App.pro
│      main.cpp
│
└─Libs
        Libs.pro
        SDKCore.cpp
        SDKCore.h
        SDKCoreImpl.cpp
        SDKCoreImpl.h
        SDKCore_global.h
        SDKStruct.h

我们创建一个函数,并且导出

SDKCore.h

extern "C" SDK_EXPORT int runFunction(const char * funName, const DS_Request &request, DS_Request& response);

SDKCore.cpp

int runFunction(const char *funName, const DS_Request &request, DS_Request &response)
{
    Internal::SDKCoreImpl m_pCoreImpl;
    int nRet = -1;
    QMetaObject::invokeMethod(&m_pCoreImpl, funName,
                              Qt::DirectConnection,
                              Q_RETURN_ARG(int, nRet),
                              Q_ARG(DS_Request, request),
                              Q_ARG(DS_Request&,response));
    return nRet;
}

这里为了访问方便,具体的函数都放在了SDKCoreImpl中实现,调用采用了反射,调用的时候直接传方法字符串即可

上面中入参和出参数据格式是自定义的,为了统一而自定义,具体如下所示

struct DS_Request{
    QString m_strData;
    QByteArray m_baStream;
};

这样就兼容所有的格式了,不管是传 字符串还是数据流都使用统一的格式

这里写一个简单的加法运算

int SDKCoreImpl::addNum(const DS_Request &request, DS_Request &response)
{
    QVariantMap map = QJsonDocument::fromJson(request.m_strData.toLatin1()).toVariant().toMap();
    int nValue1 = map.value("nValue1").toInt();
    int nValue2 = map.value("nValue2").toInt();
    int nResult = nValue1 + nValue2;
    QVariantMap mapResult;
    mapResult.insert("result", nResult);
    QJsonDocument doc = QJsonDocument::fromVariant(mapResult);
    response.m_strData = QString(doc.toJson());
    return SDKCODE_SUCCEED;
}

加载动态库

加载动态库就变的很简单了,只需要几行代码即可获取到对应的函数符号

 QLibrary library(strSDKName);
 bool bLoad = library.load();

只要动态库路径合法就会加载成功,一般加载失败可能就是路径后者导出符号有问题,遇到这些问题可以排查下

使用前先定义一个函数指针

typedef int (* pRunFunctionFun)(const char *, const DS_Request &, DS_Request&);

调用addNum函数

pRunFunctionFun pFun = (pRunFunctionFun) library.resolve("runFunction");
if ( pFun)
{
    int nRet = pFun("addNum", request, response);
    qDebug() << "add Resut:" << nRet << response.m_strData;
}

这样一个完整的调用流程就跑通了

优化代码

但是,如果你认为完了,那就错了,仔细看下就会发现上述代码有冗余,难道每调用不同的函数,都要重复上述过程么?肯定不会呀,程序员不会自己干重复的事情,要重复只能让机器去做

下面优化下,把重复的代码单独封装下,定义成宏

通过上述代码,我们可以看出来,首先这个方法可以封装成模板,每一次调用都是一样的

library.resolve("")

封装成模板调用

template <typename T>
T getFunction(QLibrary *lib, const char *symbol)
{
    T f = (T)lib->resolve(func);
    if (f == nullptr)
    {
        return nullptr;
    }
    return f;
}

然后接着优化,获取函数符号流程封装成宏

#define GET_SDK_FUNC(type, func, symbol)  GET_FUNC(sdk, type, func, symbol);
#define GET_FUNC(sdk, type, func, symbol) \
    type func = getFunction<type>(&sdk, symbol); \
    if (func == nullptr) \
{ \
    qDebug()<<"fun is null: "<< symbol; \
    return 0; \
}

优化完成后,我们看下调用过程会如何更简单

QLibrary library(strSDKName);
bool bLoad = library.load();
GET_FUNC(library, pRunFunctionFun, runFun, "runFunction");
int nRet = runFun("addNum", request, response);

完整代码已经上传到GitHub,感兴趣的可以自行去下载

demo 源码下载[2]

总结

在平时工作中,很多大型工程都是动态库调用,这样做的好处就是方便他人直接调用使用现有功能和业务,减少重复复杂的工作,提高我们的工作效率

上述代码中有没有发现一条迷惑之处,就是动态库的路径和名称没有看到呀,是怎么加载的?

这个其实用到了之前介绍的一种技术,qmake语法糖

阅读过源码你就会发现,我在pri文件中编写了字符串宏定义,这样在代码中就可以直接调用对应的字符串了,如果一天要修改,那么只修改pri文件即可,其它都不需要动

DEFINES += LIB_DIR_PATH=\"\\\"$LIB_DIR_PATH\\\"\"
DEFINES += FILE_DLL_EXT=\"\\\"$FILE_DLL_EXT\\\"\"
DEFINES += LIB_NAME=\"\\\"$LIB_NAME\\\"\"

参考资料

[1]官方api说明: https://doc.qt.io/qt-5/qlibrary.html

[2]示例源码下载地址: https://github.com/kevinlq/DynamicLibLoadDemo

相关推荐

Mac电脑强制删除任何软件方法-含自启动应用

对于打工者来说,进入企业上班使用的电脑大概率是会被监控起来,比如各种流行的数据防泄漏DLP,奇安信天擎,甚至360安全卫士,这些安全软件你想卸载是非常困难的,甚至卸载后它自己又安装回来了,并且还在你不...

Linux基础知识 | 文件与目录大全讲解

1.linux文件权限与目录配置1.文件属性Linux一般将文件可存取的身份分为三个类别,分别是owner/group/others,且三种身份各read/write/execute等权限文...

文件保护不妥协:2025 年 10 款顶级加密工具推荐

数据安全无小事,2025年这10款加密工具凭借独特功能脱颖而出,从个人到企业场景全覆盖,第一款为Ping32,其余为国外英文软件。1.Ping32企业级加密核心工具,支持200+文件格...

省心省力 一个软件搞定系统维护_省心安装在哪里能找到

◆系统类似于我们居住的房间,需要经常打理才能保持清洁、高效。虽然它本身也自带一些清理和优化的工具,但借助于好用的第三方工具来执行这方面的任务,会更让人省心省力。下面笔者就为大家介绍一款集多项功能于一身...

JAVA程序员常用的几个工具类_java程序员一般用什么软件写程序

好的工具做起事来常常事半功倍,下面介绍几个开发中常用到的工具类,收藏一下,也许后面真的会用到。字符串处理:org.apache.commons.lang.StringUtilsisBlank(Char...

手工解决Windows10的若干难题_windows10系统卡顿怎么解决

【电脑报在线】很多朋友已经开始使用Win10,估计还只是测试版本的原因,使用过程中难免会出现一些问题,这里介绍解决一些解决难题的技巧。技巧1:让ProjectSpartan“重归正途”从10074...

System32文件夹千万不能删除,看完这篇你就知道为什么了

C:\Windows\System32目录是Windows操作系统的关键部分,重要的系统文件存储在该目录中。网上的一些恶作剧者可能会告诉你删除它,但你不应该尝试去操作,如果你尝试的话,我们会告诉你会发...

Windows.old 文件夹:系统备份的解析与安全删除指南

Windows.old是Windows系统升级(如Win10升Win11)或重装时,系统自动在C盘创建的备份文件夹,其核心作用是保留旧系统的文件、程序与配置,为“回退旧系统”提供保...

遇到疑难杂症?Windows 10回收站问题巧解决

回收站是Windows10的一个重要组件。然而,我们在使用过程中,可能会遇到一些问题。例如,不论回收站里有没有文件,都显示同一个图标,让人无法判别回收站的空和满的真实情况;没有了像Windows7...

卸载软件怎么彻底删掉?简单几个步骤彻底卸载,电脑小白看过来

日常工作学习生活中,我们需要在安装一些软件程序,但随着软件的更新迭代速度,很多时候我们需要重新下载安装新的程序,这时就需要将旧的一些软件程序进行卸载。但是卸载软件虽然很简单,但是很多小伙伴们表示卸载不...

用不上就删!如何完全卸载OneDrive?

作为Windows10自带的云盘,OneDrive为资料的自动备份和同步提供了方便。然而,从隐私或其他方面考虑,有些人不愿意使用OneDrive。但Windows10本身不提供直接卸载OneDri...

【Linux知识】Linux下快速删除大量文件/文件夹方法

在Linux下,如果需要快速删除大量文件或文件夹,可以使用如下方法:使用rm命令删除文件:可以使用rm命令删除文件,例如:rm-rf/path/to/directory/*这个命令会递...

清理系统不用第三方工具_清理系统垃圾用什么软件

清理优化系统一定要借助于优化工具吗?其实,手动优化系统也没有那么神秘,掌握了方法和技巧,系统清理也是一件简单和随心的事。一方面要为每一个可能产生累赘的文件找到清理的方法,另一方面要寻找能够提高工作效率...

系统小技巧:软件卸载不了?这里办法多

在正常情况下,我们都是通过软件程序组中的卸载图标,或利用控制面板中的“程序和功能”模块来卸载软件的。但有时,我们也会发现利用卸载图标无法卸载软件或者卸载图标干脆丢失找不到了,甚至控制面板中卸载软件的功...

麒麟系统无法删除文件夹_麒麟系统删除文件权限不够

删除文件夹方法例:sudorm-rf文件夹名称。删除文件方法例:sudorm-r文件名包括扩展名。如果没有权限,给文件夹加一下权限再删。加最高权限chmod775文件名加可执行权限...

取消回复欢迎 发表评论: