写在开头的话:
我们经常看到有的头文件中有这样的代码,比如
#ifdef __cplusplus
extern "C" {
#endif
// .cpp样式的代码声明
#ifdef __cplusplus
}
或者这样
#ifdef DLL_EXPORTS
#define SIMPLE_CLASS_EXPORT __declspec(dllexport)
#else
#define SIMPLE_CLASS_EXPORT __declspec(dllimport)
#endif
又或者这样
typedef void (*OSC_INT_HANDLER)( OSC_HANDLE hOSC, OSC_INT_RESULT *intResult);
这些都是动态链接库或者静态链接库的使用中遇到的
静态链接库和动态链接库都是为了方便函数调用创建的链接库
静态链接库 .lib 动态链接库 .dll 二进制文件.bin 包含目录 include
一..静态链接库的使用 :
首先要创建静态库程序类型
静态链接库的.cpp文件和.h文件和普通.cpp和.h文件声明及格式一样,如果需要在其他.cpp文件中调用静态链接库中的接口函数时,首先要包含静态链接库的头文件目录,其次要在附加库目录中包含要使用的静态链接库的路径(我这里使用的是相对路径,绝对路径也可以)
最后在.cpp文件声明 #pragma comment(lib,"Project1.lib")//表示链接xxx.lib这个库,告诉编译器你要用到xxx.lib库,这样就可以在其他工程中直接使用静态链接库的接口函数了
二.动态链接库的使用
首先要创建动态链接库类型
调用动态链接库的方法分为1.隐式调用动态链接库2.显示调用动态链接库,而显式调用动态链接库又分为两种,接下来依次介绍。
静态库生成的lib文件会全部嵌入到exe文件中,导致exe文件增大,而动态库会动态加载,更灵活
隐式调用动态链接库:动态链接库的.h和.cpp文件和普通.h和.cpp文件声明基本一致,但由于动态链接库需要导入和导出函数接口,如下图
因此需要使用_declspec(dllexport)(dll的导出关键字)和_declspec(dllimport)(dll的导入关键字)
这句话大概的意思是:如果声明了_DLLAPI ,那么执行第二行语句 把输出宏定义为DLLAPI,如果没有声明_DLLAPI,那么执行第四行语句,把输出宏定义为DLLAPI,并且在每个函数声明前加上DLLAPI关键字,而为了保证第二条编译指令执行,需要在下图
预定义DLLAPI,如果需要在其他.cpp文件中调用动态链接库中的接口函数时,首先要包含动态链接库的头文件目录,其次要在附加库目录中包含要使用的静态链接库的路径(我这里使用的是绝对路径,相对路径也可以)
最后在.cpp文件声明 #pragma comment(lib,"Project1.lib")//这个不是真正的静态库,是为了提供导出函数,这样就可以在其他工程中直接使用静态链接库的接口函数了
显示调用动态链接库:
此时不需要在.cpp文件声明 #pragma comment(lib,"Project1.lib"),首先要加载一下动态库,使用LoadLibrary(L"../Debug/testdll.dll");//L表示宽字符串 相对路径,加载成功后会返回一个HMODULE模块,此函数需要定义头文件#include<Windows.h>
GetProcAddress()获取模块函数指针,此时需要声明一个函数指针去接收函数返回值
最后需要释放FreeLibrary(HDLL);
在这里会出现一个小问题,就是.cpp导出的函数名会和.c导出的函数名不一样,为了使.cpp导出的函数名与C格式不发生变化,第一种方法需要使用extern "C"告诉编译器使用C语言的方式来进行编译
//声明导出函数
extern "C" DLLAPI int add(int a,int b);
extern "C" DLLAPI int sub(int a, int b);
第二种方法使用模块定义文件,此时动态链接库.h文件和普通.h文件没有区别,条件编译和导入导出都不需要加上
新建模块定义文件.def