我之前有一篇文章讲过静态链接库和动态链接库,大家可以点击链接去看一下:静态链接库,动态链接库【滴水逆向三期48笔记】,由于不是系统学习Windows编程,感觉那时候就是囫囵吞枣,今天又学习了一遍,再来写一篇文章,如果大家这篇文章感觉看的不是很懂的话,大家可以两篇文章结合来看。
本篇文章主要讲解静态库和动态库的特点以及创建静态链接库和动态链接库的方法。
一.静态库(.lib)
1.静态库的特点
- 运行不存在
- 静态库源码将被直接编译到调用它的应用程序中(这里个人感觉就像将函数分文件写到项目中没啥区别)
- 目标程序的归档
2.C语言静态库的创建示例
- 1.首先,我们创建一个静态库项目:
- 添加库程序,我们可以看到以及生成了需要的文件:
- 添加一个头文件,写入我们需要导出的函数声明:
#pragma once int Plus(int x, int y); int Sub(int x, int y); int Mul(int x, int y); int Div(int x, int y);
之后在为我们生成的源文件中添加函数的实现过程:
// MyLib.cpp : 定义静态库的函数。 // #include "pch.h" #include "framework.h" // TODO: 这是一个库函数示例 void fnMyLib() { } #include "MyLib.h" int Plus(int x, int y) { return x + y; } int Sub(int x, int y) { return x - y; } int Mul(int x, int y) { return x * y; } int Div(int x, int y) { return x / y; }
之后点击生成,我们在文件中Debug目录下就可以看到生成了.lib文件:
3.静态库使用示例
我们来新建一个控制台项目来调用一下静态库,看看效果怎样:
- 创建项目调用静态库:
#include <iostream> using namespace std; int Plus(int x, int y); int Sub(int x, int y); int Mul(int x, int y); int Div(int x, int y); #pragma comment(lib,"F:\\网安\\二进制安全\\Reverse\\滴水逆向三期\\Win32\\MyLib\\x64\\Debug\\MyLib.lib") int main() { std::cout << "Hello World!\n"; int plus = Plus(2, 3); int sub = Sub(3, 2); int mul = Mul(5, 8); int div = Div(2, 3); cout << "plus = " << plus << endl << "sub = " << sub << endl << "mul = " << mul << endl << "div = " << div << endl; }
我们来看看实现效果:
我们可以看到函数调用成功,说明我们的静态库创建也是成功的。
二.动态库
1.动态库特点
- 运行时独立存在
- 源码不会被链接到程序
- 使用时加载(使用动态库必须让动态库执行)
2.与静态库比较:
- 由于静态库是将源码嵌入到实用程序中,多个程序使用时,会有多份代码,所以程序体积会很大。动态库的代码只要存在一份,其他程序使用时,通过函数地址调用,所以程序体积小。
- 静态库发生变化之后,新的代码需要重新链接嵌入到程序中,动态库发生变化之后,只要函数定义未发生变化,则其他使用动态库的程序不需要重新连接。
3.动态库的创建
- 创建动态链接库项目:
- 添加库程序:
还是一样,我们创建一个头文件声明我们需要的函数,但是需要注意的是需要在函数之前加上_declspec(dllexport)
关键字:
#pragma once _declspec(dllexport) int Plus(int x, int y); _declspec(dllexport) int Sub(int x, int y); _declspec(dllexport) int Mul(int x, int y); _declspec(dllexport) int Div(int x, int y);
之后在MyDll.cpp文件中写入函数实现过程:
#include "MyDll.h" int Plus(int x, int y) { return x + y; } int Sub(int x, int y) { return x - y; } int Mul(int x, int y) { return x * y; } int Div(int x, int y) { return x / y; }
创建成功后我们可以使用微软提供的工具DEPENDS.EXE查看DLL文件:
- 库程序导出:提供给使用者库中的函数等信息
- 方法一:
声明导出:使用_declspec(dllexport)
函数导出
注意:动态库编译链接后,也会有lib文件的生成,是作为动态库函数映射使用,与静态库不完全相同
4. 动态链接库的使用
<1>. 方法一:隐式链接:
- 头文件中加入函数原型:
可以在函数原型声明前,加入_declspec(dllimport)
- 导入动态库的lib文件(注意是lib文件)
#pragma comment(lib,"文件路径") • 1
- 完成前两步后,就可以在程序中使用函数了
隐式链接的情况,dll文件可以存放的路径:
1.与执行文件同一个目录
2.当前工作目录
3.windows目录
4.windows/System32目录
5.Windows/System
6.环境变量PATH指定目录
<3>. 显式调用
- 定义函数指针
- 加载动态库:
HMODULE LoadLibrary( LPCTSTR hpFileName //动态库文件名或全路径 );返回dll实例句柄(HINSTANCE)
- 获取函数地址,我们需要使用哪个函数,就获取哪个函数地址,传给函数指针
FARPORC GetProcAddress( HMODULE hModule, //Dll句柄 LPCTSTR lpProcName //函数名称 );成功返回函数地址
- 使用函数
- 卸载动态库
BOOL FreeLibrary( HMODULE hModule //dll的实例句柄 );
5.函数名问题
我们在创建了动态链接库后,使用微软提供的工具查看函数,发现函数名变成了非常奇怪的函数名,这是因为我们创建的动态链接库是C++语言实现的,而C++具有的函数重载机制,编译器会考虑到函数重载问题,自己帮我们改了函数名称。
这里给出解决方法:
- 模块定义文件.def
增加def文件
写入:
LIBRARY DLLFunc //库
EXPORT //库导出表
DLLNUL @1 //自定义导出序号
今天的分享就到这里,如果大家还有不理解的地方,大家随时可以私信我,如果发现文章中的错误,希望大家及时指出来,我会非常虚心地学习,希望大家共同进步!!!