概要
静态库:函数声明和实现都放在lib文件中
动态库:函数声明放在lib文件,函数实现放在dll文件
不管是动态库的lib还是静态库的lib都会在程序编译时和程序源文件一起编译,dll在编译时不需要,在运行调用时需要,因静态库函数声明和实现都在lib中每次编译都会将全部内容编译到可执行文件比较浪费资源
注意:本文只介绍DLL的使用,至于怎么写一个DLL文本暂不详细介绍。
技术细节
_declspec(dllexport)
声明一个导出函数,是说这个函数要从本DLL导出。我要给别人用。如果不给外部调用可以不用加,_declspec(dllexport) ,正常类的写法就行
__declspec(dllimport)
声明一个导入函数,是说这个函数是从别的DLL导入。我要用。一般用于使用某个dll的exe中 。必须使用 __declspec(dllimport) 才能导入 DLL 中使用的变量。
在DLL头文件声明一些变量时常出现一下错误:
#pragma once #include <opencv2/opencv.hpp> #include <iostream> using namespace cv; using namespace std; typedef enum _declspec(dllexport) Rotate { ANITCLOCKWISE, CLOCKWISE, NORATATION }ROTATE; struct _declspec(dllexport) HotPixPos { int x = 0; int y = 0; }; cv::Mat frame; class _declspec(dllexport) TestDLL { };
声明的变量中出现重定义错误!!!
正常DLL头文件中可以声明函数、类、结构体、枚举、宏,但如果声明普通变量、数组、结构变量、枚举变量等在项目编译时常会出现变量 重定义错误。
原因在于编译DLL项目时内部CPP文件包含了该DLL项目头文件,头文件中的变量被定义了一次,然而在其他项目调用该DLL时也会包含DLL头文件,故在调用之际头文件中的变量会被再次定义一次,故而会出现重定义错误。
解决办法:
(1)把所以变量的声明放到CPP文件中;
(2)对所有变量的声明加#ifdef限制,如下所示
#ifdef variable cv::Mat frame; bool isImage = false; ROTATE rotate = ANITCLOCKWISE; #endif
在其他项目属性—C/C++—预处理器中把variable宏加入进去,DLL项目没有加入variable宏,故在编译时无法进入#ifdef代码块内部就不会声明变量,而其他项目定义了该宏,在调用DLL时会定义这些变量,整个过程这些变量只会被的声明一次,故可以正常编译通过。
DLL调用方式一:
VS中ImageProcessing是一个DLL项目,想用test去调用它。
第一步:在test引用中把需要调用的dll加入进来
第二步:在test项目属性——C/C++——常规——附加包含目录中把imageProcessing头文件目录加入进来
第三步:在test中包含imageProcessing头文件,并调用DLL中类和方法
DLL调用方式二:
主要针对第三方外部库的调用:
还是以test项目调用外部库为例:
第一步:test项目属性——C/C++——常规——附加包含目录把外部库的头文件路径包含进来
第二步:test项目属性——链接器——常规——附加库目录把外部库的.lib文件路径包含进来
第三步:test项目属性——链接器——输入——附加依赖项中把.lib文件的名称复制进来
第四步:test项目属性——生成事件——生成后事件把外部库的.dll路径设置为每次编译都拷贝到test项目的debug或release目录下,这样才能正常调用外部库中的类和方法。关于生成事件的使用如下:
红色框为固定结构,蓝色框为第三方外部库.dll的目录,“$xxxxxxxxx.dll”,记得加上引号和美元符号,绿色框是输出目录,基本也是固定,格式:美元符号(输出路径) /Y,这里输出路径用了OutDir来代替,这是一个固定的宏,代表本项目debug或release路径。使用生成事件的目的是为了在编译项目的时候能够把想要的文件自动编译到自己项目下省去了手动拷贝。
DLL调用常见的报错处理方式:
无法解析的外部符号:
(1)调用dll创建对象时报无法解析的外部符号,查看dll中的类是否有__declspec(dllexport)修饰,如果没有则该类不能被外部创建对象
(2)是否在自己项目附加目录,附加依赖项没有把相关外部库目录加进来?
(3)dll中的所有函数没有被封装在类中,每个都是独立的函数,那么想要函数被外部调用,函数返回修饰符后面需要加__declspec(dllexport)
(4)如果外部库有CPP文件考虑是否把CPP文件加到自己的项目中
(5)出现scalar deleting destrutor,xxx引用了该符号,查看是否在类的头文件中new了一个对象,或者考虑是否外部库CPP文件直接加到自己项目中
(6)缺少其他dll
一个便捷的解决办法:网上直接下载depend软件,运行软件把要调用的dll直接拖入depend查看器,就能很清楚看到当前调用的dll还调用了哪些其他dll,缺少的dll会标红显示。
值得注意的地方:
我们写了一个DLL项目MLKayaCamera,头文件为MLKayaCamera.h,该DLL项目又调用了第三方外部库KYFGLib,并在MLKayaCamera头文件中包含了外部库的头文件KYFGLib.h,MLKayaCamera类中在私有里面创建了一个KYFGLib对象
如果有项目去调用MLKayaCamera的DLL,除了在该项目属性中把MLKayaCamera相关路径配置好还需要把KYFGLib的DLL相关路径配置好,如果只配置MLKayaCamera编译会报缺少外部库错误。那么问题来了,我向调用者只提供了MLKayaCamera的DLL,肯定只希望调用者只对MLKayaCamera进行配置,至于MLKayaCamera内部还调用了哪些DLL不应该是我们去关心的,怎么解决?
(1)首先把#include “KYFGLib.h”放到MLKayaCamera的CPP文件
(2)把KYFGLib类在MLKayaCamera中写成一个声明,并在私有中创建KYFGLib类的指针,对象在CPP中new一个
小结
如果您有更多想法和建议欢迎一起讨论!