Qt在linux环境下调用动态库,pro工程文件加载库和QLibrary加载库两种方式

QT调用动态库,在编译时和运行时的方式不同,编译时可在pro文件加载或使用QLibrary类加载;运行时依赖环境变量,windows下直接把动态库拷贝到可执行文件目录即可,linux需要配置固定路径或相对路径搜索动态库(ps:windows程序默认在可执行文件所在路径搜索动态库,linux则不会)。

编译时加载:pro工程文件加载动态库so

INCLUDEPATH += 库的头文件路径
LIBS += -L PATH -lLIB,PATH是so库所在路径,LIB是库的名字(-L是库路径,-l是库名字,例如库文件是libReceiptPrint.so,后面应添加-lReceiptPrint)
collect2:ld returned 1 exit status解决方案:没有正确加载库,检测库路径,32位还是64位,库是debug版还是release版,定义了槽函数没有实现,定义了构造函数和析构函数没有实现。

INCLUDEPATH += /home/chw/HSCompany/HardWareTest/source/HardWareTest/HardWareTest/sores
CONFIG(debug,debug|release){
LIBS += -L /home/chw/HSCompany/HardWareTest/source/HardWareTest/build-HardWareTest-Desktop_Qt_5_10_1_GCC_64bit-Debug/bin/ -lReceiptPrint
LIBS += -L /home/chw/HSCompany/HardWareTest/source/HardWareTest/build-HardWareTest-Desktop_Qt_5_10_1_GCC_64bit-Debug/bin/ -lInfrared
}
else{
LIBS += -L /home/chw/HSCompany/HardWareTest/source/HardWareTest/build-HardWareTest-Desktop_Qt_5_10_1_GCC_64bit-Release/bin -lReceiptPrint
LIBS += -L /home/chw/HSCompany/HardWareTest/source/HardWareTest/build-HardWareTest-Desktop_Qt_5_10_1_GCC_64bit-Release/bin -lInfrared
}

cannot open shared object file: No such file or directory解决方案:
运行报错原因分析,LIBS += -LPATH -l中添加的库路径是编译链接中使用,用于生成makefile,运行时的库路径是在环境变量中搜索,linux不会搜索当前路径,库路径加入到LD_LIBRARY_PATH变量中即可(使用 LD_DEBUG=libs xxx 可查看程序查找路径);
加载库后编译正常,但是运行报错,尝试添加环境变量,QT-左侧项目-Bulid-构建环境,点击添加,变量LD_LIBRARY_PATH,值$(LD_LIBRARY_PATH):库文件路径;
Qt在linux环境下调用动态库,pro工程文件加载库和QLibrary加载库两种方式_第1张图片
上述是在qt中添加环境变量,软件发布以后还要在linux系统中设置环境变量,否则还会报上面的错误。

编译时加载:QLibrary加载动态库so

契机:在工程文件加载so动态库方便快捷,但遇到过一种情况,当加载多个so文件,且多个so文件有同名函数时,linux系统会以先加载的为准,后加载的被忽略,导致调用混乱,解决方法很多,大多数需要重新编译库,这里介绍使用QLibrary类加载so,不用重新编译so;
QLibrary简介:
使用QLibrary可以在程序运行时加载动态链接库,一个QLibrary的实例作用于一个单一的共享库上,QLibrary库的典型用法是去解析一个库中的导出符号,并调用该符号表示的C函数,如果该库是由C++编译器编译的,那么该函数必须被包在extern "C"块中。并且在Windows平台上,还需要使用dllexport宏来修饰该函数。
代码示例:

//例如动态库接口函数为:extern "C" int OpenPort(char* pcInPort, int iInBaud);
typedef int (*Cs_OpenPort)(char* , int );//根据动态库接口函数原型,定义一个函数指针类型
Cs_OpenPort OpenPort;//用新的类型定义一个变量

QLibrary m_ReceiptPrint;//实例化一个QLibrary对象
//加载动态库
m_ReceiptPrint.setFileName("ReceiptPrint");//关联动态库名称
bool isok = m_ReceiptPrint.load();//加载动态库,成功返回true,失败返回false
OpenPort = (Cs_OpenPort)m_ReceiptPrint.resolve("OpenPort");//解析并调用动态库的函数
int ret = OpenPort((char*)"port",38400);//到这里就可以调用动态库接口

ps:QLibrary方式代码量大容易出错,不建议大量使用,多个so库有同名函数问题在封装阶段就要想办法避免。

运行时搜索库路径:固定路径

linux系统不会默认搜索应用程序所在目录的库文件,使用指定的路径搜索动态库;
固定路径有三种方式,生成临时环境变量、写入当前用户配置文件、写入系统配置文件。
1、可在终端执行:
把=号后面替换为库的路径,验证是不是环境变量路径问题,该方法在重启系统后失效;

export LD_LIBRARY_PATH=/usr/local/lib

2、把下面语句加到 ~/.bashrc中,重启后对当前用户还有效:

export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/local/lib

3、还有一种方法对所有用户有效,修改共享库配置文件/etc/ld.so.conf,把库路径复制到该文件,执行下面命令立即生效:

sudo ldconfig

4、另一种对所有用户有效的方法:

vim /etc/profile
#文档最后添加
export PATH=环境目录:$PATH
#生效
source /etc/profile

运行时搜索库路径:相对路径

固定路径使用中有很多问题,比如部署时需要修改配置文件;当有别的程序依赖相同名称库,但库的版本不一样时,会导致程序起不来,现场遇到过teamviewer依赖的QT库和我们用QT开发时使用的库名字一样,但版本不一样,导致程序起不来。

因此建议使用相对路径,在QT开发阶段,在pro文件指定库的搜索路径,使用-rpath指定路径为库搜索路径:

QMAKE_LFLAGS += -Wl,--rpath=./lib	#指定应用程序所在路径的相对路径为库搜索路径
QMAKE_LFLAGS += -Wl,--rpath=.		#指定应用程序所在路径为库搜索路径

补充1

qt环境加载库报错: undefined reference to symbol dlclose
在QT工程的.pro文件中添加如下内容:

LIBS += -ldl		#显式加载动态库的动态函数库

补充2:QT加载静态库.a文件

只能在pro文件加载,不能通过QLibrary加载,在pro文件加载方式和动态库类似,比如静态库名是libTool_Fun.a,加载方式如下:

INCLUDEPATH += /home/chw/HSCompany/QSRM/Pdjserver/dbTest/dbTest//头文件所在路径
LIBS += -L /home/chw/HSCompany/QSRM/Pdjserver/dbTest/build-dbTest-Desktop_Qt_5_10_1_GCC_64bit-Debug/bin  -lTool_Fun//LIBS += -L 库路径 -l库名称

包含头文件后即可使用,注意静态库是直接编译进可执行文件,因此发布以后不需再依赖库。
ps1:生成静态库时命名规则是lib***.a,qt生成的静态库默认是这种命名规则,gcc 编译-o指定静态库文件名也要用这种命名规则,否则在qt中加载时可能失败。
ps2:QT在调用c语言编译的静态库时,发现pro加载库成功,但调用接口时报错,可能是C++和C兼容问题,把头文件中的静态库接口用extern "C"包裹:

extern "C" {
	//c静态库接口
}

extern "C"的作用:指示编译器这部分代码按C语言方式进行编译,而不是C++方式。

你可能感兴趣的:(QT,linux,c++)