静态链接库(.lib)和动态链接库(.dll)的使用
文章目录
- 静态链接库(.lib)和动态链接库(.dll)的使用
- 一、静态链接库
- 1. 静态链接库概述
- 2. 创建静态链接库
- 3. 调用静态链接库
- 二、动态链接库(dynamic linking library)
- 1. 动态链接库概述
- 2. 创建动态链接库并导出函数
- 导出函数两种方式
- 1. 用.def文件导出
- 2. __declspec(dllexport)关键字导出
- 3. 从工程中导入dll中的函数
- 导入函数两种方式
- 1. 隐式链接
- 2. 显式链接
注--------本文所有测试均以VS2019为例
一、静态链接库
1. 静态链接库概述
静态链接库是一种资源的集合,包括函数、变量、类等,项目通过与其链接,
调用库中的资源,并且使用静态链接库时,静态链接库会整体塞入exe文件中,
也就是说,仅凭借一个exe(可执行)文件就可运行程序。
静态链接库不再包含其他静态库或者动态库,而动态链接库可以。
2. 创建静态链接库
在创建项目中选静态库
即可
例如,想在静态库实现功能如下函数,以备其他项目调用
int add(int a, int b)
{return a + b;
}int sub(int a, int b)
{return a - b;
}
则需要,头文件 + 源文件,头文件用来声明函数原型,源文件用来实现函数功能,
特别地,头文件声明函数原型时,需要在前面加上extern "C"
字样,完整例子如下
新建静态链接库项目名称:LibTest
头文件:Inc.h
#pragma once//只编译一次extern "C"
{int add(int a, int b);int sub(int a, int b);
}
源文件:LibTest.cpp
#include "pch.h"//包含预编译标准头文件,这个文件是一创建项目就有的,如果你选的不是空项目
#include "Inc.h"int add(int a, int b) {return a + b;}
int sub(int a, int b) {return a - b;}
点击生成,显示成功即可,此时,在该项目文件目录下,即LibTest文件下,会多出一个Debug文件夹,里面出现与项目同名.lib文件LibTest.lib
3. 调用静态链接库
- 头文件包含
- 目录链接
这是调用静态链接库的两个步骤,头文件包含意思是,新项目中必须包含以上静态链接库中函数声明的头文件,即Inc.h,因为这个头文件不在新项目下,所以要带上路径。
目录链接是用来链接新项目与上面生成的.lib文件,有多种链接方式,这里只用一种方法展示
新项目名称为LibCall,类型为控制台程序,这里为了方便叙述,将新项目与上述生成的lib项目放置在同一个解决方案中,也就是项目LibTest与项目LibCall在同一个文件夹下,如图
在LibCall项目的main函数中,进行头文件包含和目录链接操作,如下
main.cpp
#include <iostream>
#include "../LibTest/Inc.h"//头文件包含
#pragma comment(lib, "../LibTest/Debug/LibTest.lib")int main()
{int iRet = add(7, 8);std::cout << "7 + 8 = " << iRet << std::endl;return 0;
}
二、动态链接库(dynamic linking library)
1. 动态链接库概述
动态链接库与静态链接库相似之处在于“库”,是一种资源的集合,包括函数、变量和类,与后者不同的是,它可以包含其他的静态链接库、动态链接库。
新项目依靠动态链接库运行,那么这个.dll文件自然成了程序的一部分,即便是调试之后,对于生成的exe文件仍不能单独运行,即如果删除目录中的.dll文件,exe文件运行不了,并警报缺少dll。这点与静态链接库不同,因为静态链接库是直接塞入exe中的,删除.lib文件后exe运行照常。
2. 创建动态链接库并导出函数
例如创建了一个名为DllTest的动态链接库项目,只有将在dll中的编写过的函数进行导出才能使用,
所以接下来要导出函数。
导出函数两种方式
1. 用.def文件导出
在项目中新建.def文件,文件内容:
源文件(.def文件):DllTest.def
LIBRARY "DllTest"EXPORTSadd @1
另外:需要文件
头文件:fun.h
#pragma once
int add(int a, int b);
源文件:fun.cpp
#include "pch.h"//预编译头文件
#include "fun.h"int add(int a, int b)
{return a + b;
}
说明:.def文件的第一行必须是LIBRARY "XXX",其中XXX是dll的项目名称
,EXPORT下面是导出函数,@1即编号为1,接下来可以按照编号调用函数(本文略过)。当然该文件有特殊的注释形式,如
; This is a 注释
,即在要注释的内容前面加上一个分号。
2. __declspec(dllexport)关键字导出
头文件:fun.h
#pragma once
extern "C" __declspec(dllexport) int add(int a, int b);
源文件:fun.cpp
#include "pch.h"//预编译头
#include "fun.h"int add(int a, int b) { return a + b;}
3. 从工程中导入dll中的函数
导入函数两种方式
1. 隐式链接
隐式链接是对生成dll文件时一并生成的同名.lib文件进行操作,方式类似于静态链接
-
对用.def文件导出dll的函数,导入函数格式:
方式一:<1> int add(int a, int b); 方式二:<2> __declspec(dllimport) int add(int a, int b);
有如上两种形式的声明,均可,完整例子:
源文件:
#include <iostream> #pragma comment(lib, "../DllTest/Debug/DllTest.lib")//int add(int a, int b) __declspec(dllimport) int add(int a, int b);int main() {int iRet = add(5, 3);std::cout << "5 + 3 = " << iRet << std::endl;system("pause");return 0; }
-
对用__declspec(dllexport)关键字导出dll的函数,导入函数格式:
方式一:<1> extern "C" __declspec(dllimport) int add(int a, int b); 方式二:<2> extern "C" int add(int a, int b);
源文件:
#include <iostream> #pragma comment(lib, "../DllTest/Debug/DllTest.lib")//extern "C" int add(int a, int b); extern "C" __declspec(dllimport) int add(int a, int b);int main() {int iRet = add(5, 3);std::cout << "5 + 3 = " << iRet << std::endl;system("pause");return 0; }
2. 显式链接
这里不详细说明了,给出一般过程:
LoadLibrary(…);//该API用于加载指定的DLL
GetProAddress(…);//该API用于获取DLL中导出的函数
FreeLibrary(…); 该API用于卸载指定的DLL
注意:如果一个程序多次调用LoadLibrary()加载同一个dll,那么卸载时也要调用相同次数的
FreeLibrary()卸载。
优点:灵活性强,显式链接是完全由编程者用API加载和卸载的DLL,编程者可以决定什么时候加载、卸载,
以及加载哪个、卸载那个。
这里需要windows API和函数指针的知识,自行百度即可
THE END…