Win32编程之动态库(七)
一、动态库的特点
- 运行时独立存在
- 源码不会链接到执行程序
- 使用时加载(使用动态库必须使用动态库执行)
- 与静态库的比较:由于静态库是将代码嵌入到使用程序中,多个程序使用时,会有多份代码,所有代码体积会增大,动态库的代码只需要存在一份,其他程序通过函数地址使用,所以代码体积小;静态库发生变化后,新的代码需要重新链接嵌入到执行程序中,动态库发生变化后,如果库中函数的定义(或地址)未变化,其他使用DLL的程序不需要重新链接
二、动态库的创建
- 创建动态库项目
- 添加库程序
- 库程序导出:提供给使用者库中的函数等信息
1.声明导出
使用_declspec(dllexport)导出函数,注意:动态库编译连接后,也很会有LIB文件,是作为动态库函数映射使用,与静态库不完全相同。
库头文件:
1 2 3 4 5 | #pragma once _declspec( dllexport ) int CPPdll_add( int add1, int add2); _declspec( dllexport ) int CPPdll_sub( int sub1, int sub2); _declspec( dllexport ) int CPPdll_mul( int mul1, int mul2); |
库源文件:
1 2 3 4 5 6 7 8 9 10 11 12 13 | #include "dllmain.h" int CPPdll_add( int add1, int add2) { return add1 + add2; } int CPPdll_sub( int sub1, int sub2) { return sub1 - sub2; } int CPPdll_mul( int mul1, int mul2) { return mul1 * mul2; } |
2.模块定义文件.def
例如:LIBRARY DLLFunc(库),EXPORTS(库导出表),DLL_Mul @1(导出的函数)
库头文件:
1 2 3 4 5 | #pragma once int CPPdll_add( int add1, int add2); int CPPdll_sub( int sub1, int sub2); int CPPdll_mul( int mul1, int mul2); |
库源文件:
1 2 3 4 5 6 7 8 9 10 11 12 13 | #include "dllmain.h" int CPPdll_add( int add1, int add2) { return add1 + add2; } int CPPdll_sub( int sub1, int sub2) { return sub1 - sub2; } int CPPdll_mul( int mul1, int mul2) { return mul1 * mul2; } |
def文件:
1 2 3 4 5 | LIBRARY CPPdll EXPORTS CPPdll_add @1 CPPdll_sub @2 CPPdll_mul @3 |
三、动态库的使用
动态库中的lib文件存放着函数名称和每个函数的编号,当运行程序加载dll文件时,会根据lib文件中的编号去dll文件中找到对应的函数名称,从而找到对应的函数地址
1.隐式链接(操作系统负责动态库执行)
(1).头文件和函数原型:可以在函数原型的声明前,增加_declspec(dllimport)
(2).导入动态库的LIB文件
(3).在程序中使用函数
(4).隐式链接的情况,dll文件可以存放的路径:
- 与执行文件在同一个目录
- 当前工作目录
- Windows目录
- Windows/System32目录
- Windows/System
- 环境变量PATH指定目录
示例代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | #include <iostream> #include "../CPPDll/dllmain.h" using namespace std; //通过链接器到哪去抓取编号和dll文件名("CPPdll.dll") #pragma comment(lib, "../Debug/CPPdll.lib") int main() { int sum = CPPdll_add(5, 4); int sub = CPPdll_sub(5, 4); int mul = CPPdll_mul(5, 4); cout << "sum:" << sum << endl; cout << "sub:" << sub << endl; cout << "mul:" << mul << endl; return 1; } |
2.显示链接(程序员自己负责动态库的执行)
(1).定义函数指针类型(typedef)
(2).加载动态库:
1 2 3 | HMODULE LoadLibrary( LPCTSTR lpFileName //动态库文件名或全路径 );返回DLL的示例句柄( HINSTANCE ) |
(3).获取函数地址:
1 2 3 4 | FARPROC GetProcAddress( HMODUKE hModule, //DLL句柄 LPCSTR lpPricName //函数名称 );成功返回函数地址 |
(4).使用函数
(5).卸载动态库
1 2 3 | BOOL FreeLibrary( HMODULE hModule //DLL的实例句柄 ); |
显示调用的动态库必须先定义def文件,将def文件中将函数导出,下面是示例代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | #include <Windows.h> #include <iostream> using namespace std; typedef int (*SHOW_FUNC)( int m, int n); int main() { HINSTANCE hDll = LoadLibrary(TEXT( "CPPdll.dll" )); if (hDll != NULL) { SHOW_FUNC myAdd = (SHOW_FUNC)GetProcAddress(hDll, "CPPdll_add" ); cout << "myAdd(5, 4) = " << myAdd(5, 4) << endl; SHOW_FUNC mySub = (SHOW_FUNC)GetProcAddress(hDll, "CPPdll_sub" ); cout << "mySub(5, 4) = " << mySub(5, 4) << endl; SHOW_FUNC myMul = (SHOW_FUNC)GetProcAddress(hDll, "CPPdll_mul" ); cout << "myMul(5, 4) = " << myMul(5, 4) << endl; FreeLibrary(hDll); } return 1; } |
四、动态库中封装类
在类名称前增加_declspec(dllexport)定义,例如:
1 2 3 | class _declspec( dllexport ) CMath { ... } |
通常使用预编译开关奇幻类的导入导出定义,例如:
1 2 3 4 5 6 7 8 | #ifdef DLLCLASS_EXPORTS #define EXT_CLASS _declspec(dllexport)//dDLL #else #define EXT_CLASS _declspec(dllimport)//使用者 #endif class EXT_CLASS CMath { ... } |
库头文件:
1 2 3 4 5 6 7 8 9 10 11 12 | #pragma once #ifdef DLLCLASS_EXPORTS #define EXT_CLASS _declspec(dllexport) #else #define EXT_CLASS _declspec(dllimport) #endif class EXT_CLASS CMath { public : int Add( int add1, int add2); int Sub( int sub1, int sub2); }; |
库源文件:
1 2 3 4 5 6 7 8 9 10 | #define DLLCLASS_EXPORTS #include "dllmain.h" int CMath::Add( int add1, int add2) { return add1 + add2; } int CMath::Sub( int sub1, int sub2) { return sub1 - sub2; } |
示例代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | #include <iostream> #include "../ClassDll/dllmain.h" using namespace std; #pragma comment(lib, "../Debug/ClassDll.lib") int main() { CMath math; int sum = math.Add(5, 6); int sub = math.Sub(5, 6); cout << "sum = " << sum << endl; cout << "sub = " << sub << endl; return 1; } |
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?