静态链接库
何为静态链接库?
win
平台下xxx.lib
文件即静态链接库。静态库只有lib
文件,该文件包含了函数代码本身,在编译时会直接将代码完整添加到程序之中。
使用VS2019
创建一个静态库项目
选择静态库,在下面选项中勾选空项目
testlib.h
#ifndef TESTLIB_H
#define TESTLIB_H
int add(int a, int b);
#endif
testlib.cpp
#include "testlib.h"
int add(int a, int b)
{
return a + b;
}
生成解决方案并生成静态库
接下来尝试使用静态库,在同一解决方案下新建项目test
,并右击将其设置为启动项目
test.cpp
#include <stdio.h>
int main()
{
int a, b = 0;
scanf_s("%d %d", &a, &b);
int c = add(a, b);
printf("%d", c);
return 0;
}
直接编译,报错
那是因为程序不知道add
函数是什么,或者说不知道去哪里调用
包含一下静态库项目中的头文件testlib.h
#include <stdio.h>
#include "../testlib/testlib.h"
int main()
{
int a, b = 0;
scanf_s("%d %d", &a, &b);
int c = add(a, b);
printf("%d", c);
return 0;
}
可通过编译,但运行继续报错
那是因为头文件中只有声明,没有实现。因此,可以引出静态库如何调用的问题
静态库的调用需要两个文件:xxx.h
、xxx.lib
方式一:将testlib.lib
文件复制至test
项目工作目录,并添加附加依赖项
#include <stdio.h>
#include "../testlib/testlib.h"
// #pragma comment(lib, "testlib.lib")
int main()
{
int a, b = 0;
scanf_s("%d %d", &a, &b);
int c = add(a, b);
printf("%d", c);
return 0;
}
方式二:test
项目中添加静态库的库目录相对路径,并在程序中添加导入对应库的代码
#include <stdio.h>
#include "../testlib/testlib.h"
#pragma comment(lib, "testlib.lib")
int main()
{
int a, b = 0;
scanf_s("%d %d", &a, &b);
int c = add(a, b);
printf("%d", c);
return 0;
}
方式三:test
项目中添加静态库的库目录相对路径,并在程序中添加附加依赖项
#include <stdio.h>
#include "../testlib/testlib.h"
// #pragma comment(lib, "testlib.lib")
int main()
{
int a, b = 0;
scanf_s("%d %d", &a, &b);
int c = add(a, b);
printf("%d", c);
return 0;
}
可见,无论是哪种方式,都要使test
项目找得到静态库。实际开发中方式二使用居多。
方式三与方式二的区别在于:方式二使用预处理指令导入库文件;而方式三通过设置工程属性导入库文件。
#pragma comment(lib, "XXX.lib")
动态链接库
windows
操作系统有三个核心的动态链接库:
kernel32.dll
kernel32.dll
是Windows
中非常重要的32位动态链接库文件,属于内核级文件。它控制着系统的内存管理、数据的输入输出操作和中断处理,当Windows
启动时,kernel32.dll
就驻留在内存中特定的写保护区域,使别的程序无法占用这个内存区域。
在Windows
系统中,每个.exe
文件在双击打开时都会加载kernel32.dll
这个系统模块,该模块中有一个LoadLibrary()
函数,可以将DLL
文件加载到自身进程中。可以用CreateRemoteThread()
函数创建一个远程线程,让目标进程调用LoadLibrary()
来加载我们自己写的DLL
。
user32.dll
user32.dll
是Windows
用户界面相关应用程序接口,用于包括Windows
处理,基本用户界面等特性,如创建窗口和发送消息。
gdi32.dll
gdi32.dll
是Windows GDI
图形用户界面相关程序,包含的函数用来绘制图像和显示文字
何为动态链接库?
win
平台下xxx.dll
即为动态链接库。动态链接库不会将完整代码拷贝进xxx.exe
文件,仅仅会在程序运行的时候,才会将具体的代码加载进去。
使用VS2019
创建一个动态库项目
testdll.h
#ifndef TESTDLL_H
#define TESTDLL_H
#ifdef _DLLAPI
#define DLLAPI _declspec(dllexport)
#else
#define DLLAPI _declspec(dllimport)
#endif
int DLLAPI add(int a, int b);
#endif
这里使用条件编译指令是更加规范的做法,可以让testdll
项目和测试项目test
共用一个头文件testdll.h
设置testdll
项目的预处理器中添加宏_DLLAPI
如此一来,可以使得在生成dll文件时将DLLAPI
替换成_declspec(dllexport)
,而在测试项目中导入头文件时,将DLLAPI
替换成_declspec(dllimport)
testdll.cpp
#include "testdll.h"
int add(int a, int b)
{
return a + b;
}
右击项目,重新生成解决方案并生成动态库(注意,dll项目不能调试,点击调试会报错的)
此时,我们去下载一个工具 dllexp,打开软件并导入testdll.dll
,可见函数名被修饰了
接下来尝试使用动态库,在同一解决方案下新建项目test
,并右击将其设置为启动项目
动态库调用有两种方式:静态调用和动态调用
1. 静态调用
静态调用需要三个文件:xxx.h
、xxx.lib
、xxx.dll
使用相对路径设置test
项目的库目录路径
test.cpp
#include <stdio.h>
#include "../testdll/testdll.h"
#pragma comment(lib, "testdll.lib") //这个不是真正的静态库
int main()
{
int a = 1;
int b = 2;
int c = add(a, b);
printf("%d\n", c);
return 0;
}
为什么不需要如网上其他帖子所说:在调用dll文件时,需要xxx.h
、xxx.lib
、xxx.dll
缺一不可。而我这里没有将dll文件复制到test
项目的可执行目录中,是因为他们俩同处在一个解决方案下面。
但是神奇的是,test.exe
可执行文件不在原项目test/Debug
中,而是跑到了testdll/Debug
中。纵然满足exe
和dll
文件在同一目录下,但仍然令我费解。
2. 动态调用
动态调用只要一个文件:xxx.dll
由于testdll
项目中是用cpp
文件写的,即以C++
的方式导出动态库的函数,那么编译器为了实现C++
的函数重载会在函数导出时对函数名进行修饰。因此,修改一下testdll.h
文件。
#ifndef TESTDLL_H
#define TESTDLL_H
#ifdef _DLLAPI
#define DLLAPI _declspec(dllexport)
#else
#define DLLAPI _declspec(dllimport)
#endif
// extern "C" 编译时用C的编译方式
extern "C" DLLAPI int add(int a, int b);
#endif
重新生成解决方案后的dll文件中add
函数名就不会被修饰
test.cpp
#include <stdio.h>
#include <Windows.h>
//#include "../testdll/testdll.h"
//#pragma comment(lib, "testdll.lib") //这个不是真正的静态库
typedef int (*pAdd)(int a, int b);
int main()
{
HMODULE hDll = LoadLibrary(L"../testdll/Debug/testdll.dll"); //加载库文件
if (hDll == NULL)
{
printf("加载DLL文件失败\n");
return 0;
}
pAdd testAdd = (pAdd)GetProcAddress(hDll, "add");
int a = 1;
int b = 2;
printf("add(a, b) = %d\n", testAdd(a, b));
FreeLibrary(hDll);
return 0;
}
成功运行