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;
}  

 

posted @   TechNomad  阅读(85)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?
点击右上角即可分享
微信分享提示