一. 静态库的创建
i. 创建一个空项目(也可以直接选择动态库项目,VS会自动生成框架)。
在已安装的模板中选择“常规”,在右边的类型下选择“空项目”,在名称和解决方案名称中输入staticlib。点击确定。
ii. 添加头文件和源文件
在解决方案资源管理器的头文件中添加,mylib.h文件,在源文件添加mylib.c文件(即实现文件)
iii. 在mylib.h文件中添加如下代码
#ifndef TEST_H
#define TEST_H
int myadd(int a,int b);
#endif
iv. 在mylib.c文件中添加如下代码:
#include"test.h"
int myadd(int a, int b)
{
return a + b;
}
v. 配置项目属性。因为这是一个静态链接库,所以应在项目属性的“配置属性”下选择“常规”,在其下的配置类型中选择“静态库(.lib)。
vi. 编译生成新的解决方案,在Debug文件夹下会得到mylib.lib (对象文件库),将该.lib文件和相应头文件给用户,用户就可以使用该库里的函数了。
二.静态库的使用
-
方法一: 配置项目属性
i. 添加工程的头文件目录:工程—属性—配置属性—c/c+±–常规—附加包含目录:加上头文件存放目录。
ii. 添加文件引用的lib静态库路径:工程—属性—配置属性—链接器—常规—附加库目录:加上lib文件存放目录。
iii. 然后添加工程引用的lib文件名:工程—属性—配置属性—链接器—输入—附加依赖项:加上lib文件名。
iV. 引入需要的头文件
-
方法二: 使用编译语句
i. 参照方法一的第一步。
ii. 添加如下语句:
#pragma comment(lib,"./mylib.lib")
三.静态库优缺点
- 静态库对函数库的链接是放在编译时期完成的,静态库在程序的链接阶段被复制到了程序中,和程序运行的时候没有关系;
- 程序在运行时与函数库再无瓜葛,移植方便。
- 浪费空间和资源,所有相关的目标文件与牵涉到的函数库被链接合成一个可执行文件。
四. 动态库创建
- 方法一:
i. 创建一个空项目,项目名称为mydll
ii. 在解决方案资源管理器的头文件中添加,test.h文件,在源文件添加test.c文件(即实现文件)。
iii. 在test.h文件中添加如下代码:
iv. 在test.c文件中添加如下代码:#ifndef _TEST_H_ #define _TEST_H_ #ifdef add_EXPORTS #define add_EXPORT __declspec(dllexport) #else #define add_EXPORT __declspec(dllimport) #endif add_EXPORT int add(int x,int y); //中间一段的意思是: 如果在工程里添加预定义宏addEXPORTS 那addEXPORT 就指代__declspec(dllexport) 用于dll的导出(函数 变量 类等) 导出变量用 __declspec(dllexport) int a; 导出函数用 __declspec(dllexport) void foo(); 导出类用 class __declspec(dllexport) a{}; 如果应用程序需要调用dll中的函数,则需要用__declspec(dllimport)修饰,因此当工程中不包含addEXPORTS预定义时,addEXPORT 就指代__declspec(dllimport) 这样生成dll程序和调用dll的程序可以使用同一个头文件 (ps:工程中会自动添加一个add_EXPORTS预定义,在属性—c\c++ —preprocesspr里面) #endif
v. 配置项目属性。因为这是一个动态链接库,所以应在项目属性的“配置属性”下选择“常规”,在其下的配置类型中选择“动态库(.dll)。(方法同静态库)#include "test.h" int add(int x,int y) { return x+y; }
vi. 编译生成新的解决方案,在Debug文件夹下会得到mydll.dll (对象文件库),将该.dll文件、.lib文件和相应头文件给用户,用户就可以使用该库里的函数了。 - 方法二:
i. 首先创建一个空项目dll,创建头文件dll.h,再创建源文件main.cpp,再创建模块定义文件即defdllcall.def文件(这个文件添加在源文件里,跟添加源文件的方法一样,可以在右上角搜索框输入.def查找),这样创建一个动态库所需的条件就满足了。
ii. 在头文件dll.h中写入如下代码
iii. 在源文件main.c中写入如下代码#pragma once int _stdcall add(int x, int y); int _stdcall sub(int x, int y);
iv. 在模块定义文件defdllcall.def中写入如下代码#include "dll.h" int _stdcall add(int x, int y) { return x + y; } int _stdcall sub(int x, int y) { return x - y; }
v. 把文件配置成动态库文件,右键项目生成即可。LIBRARY "defdllcall" EXPORTS add @1 sub @2
五. 动态库使用
-
方法一:隐式调用
本次是对方法一创建的动态库进行调用
i. 创建主程序TestDll,将test.h、mydll.dll和mydll.lib复制到源代码目录下。
ii. 在程序中指定链接引用链接库 : #pragma comment(lib,"./mydll.lib")
iii. 引入需要的头文件,如下:
-
方法二:显式调用
本次是对方法二创建的动态库进行调用,创建一个控制台程序,调用代码如下:#include <iostream> #include <Windows.h> #include "tchar.h" using namespace std; typedef int (WINAPI* AddFunc)(int x, int y); typedef int (WINAPI* SubFunc)(int x, int y); int main() { //使用_T要加头文件"tchar.h",加入动态库Dll.dll文件路径 HMODULE hdll = LoadLibrary(_T("..\\Debug\\Dll.dll")); std::cout << "Hello World!\n"; if (hdll != NULL) { AddFunc ADD1 = (AddFunc)GetProcAddress(hdll, (char*)(1)); SubFunc SUB1 = (SubFunc)GetProcAddress(hdll, "sub"); if (ADD1 != NULL) { cout << "显试调用add" << endl; cout << ADD1(3, 4) << endl;//对add函数调用 } if (SUB1 ) { cout << "显试调用sub" << endl; cout << SUB1(4,2) << endl;//对sub函数调用 } FreeLibrary(hdll); } }
运行结果如下图:
-
在显式调用中,由于使用的是.def的方法,在获取动态库中函数地址时有两种方法
方法一: AddFunc ADD1 = (AddFunc)GetProcAddress(hdll,(char)(1));* 这是在.def文件中add后面加@1的使用方法
方法二: 当然也可以使用通用获取地址方法 AddFunc ADD1 = (AddFuncGetProcAddress(hdll,“add”);
AddFunc ADD1 = (AddFunc)GetProcAddress(hdll,(char*)(1));这句(char*)(1)中1是add后面@的数字,相当于add在动态库中的地址,GetProcAddress的函数原型里1这个位置数据类型是char*类型. -
附.def的一些小知识:
.def 文件中的第一条 LIBRARY 语句不是必须的,但LIBRARY 语句后面的 DLL 的名称必须正确,即与生成的 动态链接库的名称必须匹配。此语句将 .def 文件标识为属于 DLL。 链接器将此名称放到 DLL 的 导入库中。
EXPORTS 语句列出名称,可能的话还会列出 DLL 导出函数的序号值。通过在函数名的后面加上 @ 符和一个数字,给函数分配序号值。当指定序号值时,序号值的范围必须是从 1 到 N,其中 N 是 DLL 导出函数的个数。
LIBRARY BTREE
EXPORTS
Insert @1
Delete @2
Member @3
Min @4
def 文件中的注释由每个注释行开始处的分号 ( ; ) 指定。注释不能与语句共享一行,但可以在多行语句的规范间出现。
六. 动态库相关的问题
- 疑问一:__declspec(dllexport)是什么意思?
动态链接库中定义有两种函数:导出函数(export function)和内部函数(internal function)。 导出函数可以被其它模块调用,内部函数在定义它们的DLL程序内部使用。 - 疑问二: 动态库的lib文件和静态库的lib文件的区别?
eclspec(dllexport)是什么意思?
动态链接库中定义有两种函数:导出函数(export function)和内部函数(internal function)。 导出函数可以被其它模块调用,内部函数在定义它们的DLL程序内部使用。 - 疑问二: 动态库的lib文件和静态库的lib文件的区别?
在使用动态库的时候,往往提供两个文件:一个引入库(.lib)文件(也称“导入库文件”)和一个DLL(.dll)文件。虽然引入库的后缀名也是“lib”,但是,动态库的引入库文件和静态库文件有着本质的区别,对一个DLL文件来说,其引入库文件(.lib)包含该DLL导出的函数和变量的符号名,而.dll文件包含该DLL实际的函数和数据。在使用动态库的情况下,在编译链接可执行文件时,只需要链接该DLL的引入库文件,该DLL中的函数代码和数据并不复制到可执行文件,直到可执行程序运行时,才去加载所需的DLL,将该DLL映射到进程的地址空间中,然后访问DLL中导出的函数。