阅读本文大概需要 6.6分钟

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

常用 api

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

官方说明

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

创建动态库

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

├─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 源码下载

总结

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

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

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

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

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

QLibrary 加载动态库的更多相关文章

  1. QT常用代码之加载动态库和弹出对话框

    作者:朱金灿 来源:http://blog.csdn.net/clever101 加载动态库的代码: typedef void (*Execute)(); // 定义导出函数类型 QString st ...

  2. Linux下c函数dlopen实现加载动态库so文件代码举例

    dlopen()是一个强大的库函数.该函数将打开一个新库,并把它装入内存.该函数主要用来加载库中的符号,这些符号在编译的时候是不知道的.这种机制使得在系统中添加或者删除一个模块时,都不需要重新编译了. ...

  3. LoadLibrary加载动态库失败

    [1]LoadLibrary加载动态库失败的可能原因以及解决方案: (1)dll动态库文件路径不对.此场景细分为以下几种情况: 1.1 文件路径的确错误.比如:本来欲加载的是A文件夹下的动态库a.dl ...

  4. 【转载】cocos2dx 中 Android NDK 加载动态库的问题

     原文地址:http://blog.csdn.net/sozell/article/details/10551309 cocos2dx 中 Android NDK 加载动态库的问题 闲聊 最近在接入各 ...

  5. Windows平台LoadLibrary加载动态库搜索路径的问题

    一.背景 在给Adobe Premiere/After Effects等后期制作软件开发第三方插件的时候,我们总希望插件依赖的动态库能够脱离插件的位置,单独存储到另外一个地方.这样一方面可以与其他程序 ...

  6. 使用dlopen加载动态库

    目录 概述 接口 C CMakeLists.txt src/main.c src/add.c ./dlopen_test C++ CMakeLists.txt src/main.cpp src/add ...

  7. lua加载动态库缺乏相应的系统库

    错误信息: 使用lua测试lm2动态库时,加载时出现如下错误 jfyuan@jfy11-B85M-D2V:~/temp/service/soft/code/ginger_resty/cores/lm2 ...

  8. macOS下加载动态库dylib报"code signature invalid"错误的解决办法

    一.现象描述 在macOS上搞开发也有一段时间了,也积攒了一定的经验.然而,今天在替换工程中的一个动态库时还是碰到了一个问题.原来工程中用的是一个静态库,调试时发现有问题就把它替换成了动态库.这本来没 ...

  9. C++加载动态库的形式来实现封装

    目录结构 └── test ├── CMakeLists.txt ├── base.h //设置接口 ├── drive.cpp //具体实现 └── main.cpp //test CMakeLis ...

随机推荐

  1. 精尽Spring MVC源码分析 - 寻找遗失的 web.xml

    该系列文档是本人在学习 Spring MVC 的源码过程中总结下来的,可能对读者不太友好,请结合我的源码注释 Spring MVC 源码分析 GitHub 地址 进行阅读 Spring 版本:5.2. ...

  2. 去掉RedisDesktopManager更新提示弹窗

    去掉RedisDesktopManager更新提示弹窗 起因 每次打开RDM都要弹出一个更新提示弹窗,虽然打开次数不频繁,总是有个弹窗再点一次OK按钮,还不能设置关闭更新检查.更新下载还要各种登录麻烦 ...

  3. Tomcat文件包含漏洞的搭建与复现:CVE-2020-1938

    Tomcat文件包含漏洞的搭建与复现:CVE-2020-1938 漏洞描述 2020年2月20日,国家信息安全漏洞共享平台(CNVD)发布了Apache Tomcat文件包含漏洞(CNVD-2020- ...

  4. Linux 修改权限,查看进程,结束进程 命令

    在linux终端先输入ll,可以看到bai如:-rwx-r--r-- (一共10个参数) 表示文件所属组和du用户的对应权限.zhi第一个跟dao参数属于管理员,跟chmod无关,先不管.2-4参数: ...

  5. Sqlmap 学习笔记1:sqlmap参数

    SQLMP参数分析 1 目录 1.Target Options 2.Requests Options 3.Injection Options 4.Detection Options 5.Techniq ...

  6. java基础模拟考试三套试卷

    卷1: 一.单选题(15道,共30分) //1.下列关于JDK.JRE.JVM关系描述正确的是 (A) A:JDK是开发工具,包含了JRE.JRE是运行环境,包含了JVM.JVM是虚拟机,可以保证跨平 ...

  7. Hive动态分区详解

    目录 动态分区调整 注意 动态分区插入 动静分区结合 例子 动态分区调整 动态分区属性:设置为true表示开启动态分区功能(默认为false)hive.exec.dynamic.partition=t ...

  8. IIS放置的APP安装包在浏览器无法打开

    无法打开的提示   操作步骤 1.将APP安装包放置到指定的文件夹中. 2.在IIS中MIME中添加MIME类型 扩展名:.apk MIME类型:application/vnd.android.pac ...

  9. 如何查看打印机的IP地址和MAC地址

    1.  打开控制面板,选择设备和打印机: 2.  选中打印机,右键单机,选择打印机 "属性": 3. 选择web服务,可以直接查看打印机的IP地址或MAC地址,如下图所示: 4. ...

  10. git文件锁定不更新和忽略

    git文件的忽略 新建未提交的文件直接添加.gitignore 提交之后的文件已被git追踪 这时需要清除git缓存 忽略文件 git rm --cached ./src/main/resources ...