https://zhuanlan.zhihu.com/p/71372182
https://www.cnblogs.com/skynet/p/3372855.html
在使用动态库的时候,编译后往往提供两个文件:一个引入库(.lib)文件(也称“导入库文件”)和一个DLL(.dll)文件。当然到了后面会告诉你如果只提供一个DLL文件,使用显示连接的方式也可以调用,只是稍加麻烦而已。
虽然引入库的后缀名也是“lib”,但是,动态库的引入库文件和静态库文件有着本质的区别。对一个DLL文件来说,其引入库文件(.lib)包含该DLL导出的函数和变量的符号名,而.dll文件包含该DLL实际的函数和数据。在使用动态库的情况下,在编译链接可执行文件时,只需要链接该DLL的引入库文件,该DLL中的函数代码和数据并不可复制到可执行文件,直到可执行程序运行时,才去加载所需的DLL,将该DLL映射到进程的地址空间中,然后访问DLL中导出的函数。这时,在发布产品时,除了发布可执行文件以外,同时还需要发布该程序将要调用的动态链接库。
只有当EXE程序确实要调用这些DLL模块的情况下,系统才会将它们装载到内存空间中。这种方式不仅减少了EXE文件的大小和对内存空间的需求,而且使这些DLL模块可以同时被多个应用程序使用。如果DLL不在内存中,系统就将其加载到内存中。当链接Windows程序以产生一个可执行文件时,你必须链接由编程环境提供的专门的 “引入库(import library)”。这些引入库包含了动态链接库名称和所有Windows函数调用的引用信息。链接程序使用该信息在.EXE文件中构造一个表,当加载程序时,Windows使用它将调用转换为Windows函数。
_declspec(dllexport)
_declspec(dllexport) int add(int a, int b);
缺点:存在函数名字被编译器转换的问题(区别重名程序)。同一个编译器版本的c++程序调用该dll是没有问题的。但其它语言的程序(如C#、VB)调用该dll则会出错。因为在C++中存在函数的重载,允许函数重名,因此在编译器生成dll的时候,为了区别重名的程序,会进行名称转换。我们直接通过函数名是无法找到该函数的,从而导致调用失败。
例如,对于前面的add函数,实际的函数名称是如下形式:
为了解决这一问题,我们往往在函数前面再加一个extern "C"
,使用C方式的函数命名规则。
extern "C" _declspec(dllexport) int add(int a, int b);
在项目中添加一个**.def文件**,然后把要导出的函数放在def文件中。
一般C/C++默认的调用方式是__cdecl,这种方式下需要调用方对函数清栈。如果对外提供api共其它非C++程序使用时,应将接口声明为__stdcall,让api函数自己清栈,否则调用方会无法清栈而出错(C#会直接报函数声明不匹配的错误)。因此,对外提供api时还。这也是Windows API前面都加上了一个WINAPI的宏的原因。
def文件导出和_declspec导出区别以及示例
.lib称为导入库,相当于头文件;.dll是动态库文件,相当于cpp,头文件中函数的具体实现。
INCLUDEPATH += $$PWD/ \
$$PWD/../3rd_part/Include \
$$PWD/../3rd_part/Include/opencv2
DEPENDPATH += $$PWD/ \
$$PWD/../3rd_part/Include \
$$PWD/../3rd_part/Include/opencv2
CONFIG(debug, debug|release){
contains(TARGET_ARCH, x86_64){
DESTDIR += $$PWD/../bin/Debug/64
}else{
DESTDIR += $$PWD/../bin/Debug/32
}
TARGET = Functiond
LIBS += -L$$PWD/../3rd_part/Lib/Debug -lopencv_world455d
} else {
contains(TARGET_ARCH, x86_64){
DESTDIR += $$PWD/../bin/Release/64
}else{
DESTDIR += $$PWD/../bin/Release/32
}
TARGET = Function
LIBS += -L$$PWD/../3rd_part/Lib/Release -lopencv_world455
}
#ifdef _DEBUG
#pragma comment(lib,"../3rd_part/Lib/Debug/Qt5Pdfiumd.lib")
#else
#pragma comment(lib,"../3rd_part/Lib/Release/Qt5Pdfium.lib")
#endif
对于MT/MTd,由于连接运行时库是LIBCMT.lib/LIBCMTD.lib,这两个库是静态库,所以此种方式编译的程序,移到另一台机器上面也可以正常运行。
但是对于MD/MDd,连接的是动态库,所以如果另一台机器上没有MSVCRT.dll/MSVCRTD.dll时,就提示缺少动态库这样的错误。
曾经犯这样的错误,以为以MT/MTd方式编译,程序对所有的库都是静态链接的,其实错了,它只能决定运行时库是动态链接还是静态链接,对用户自己写的库或其他第三方库,其连接方式取决于代码(显式连接动态库Loadlibrary)或所提供的lib文件(为导入库还是静态库),移动程序到别的机器上时,还是要带上所需要的动态库的。
https://www.cnblogs.com/ShaneZhang/p/3480502.html
#define _AFXDLL
#define _AFXDLL
#include "../3rd_part/Include/xxx.h"