一、什么是 dll?
dll(dynamic link library) 是 windows 下的动态链接库,包含可被多个程序共享的函数、资源或类。使用 dll 可以实现代码复用、模块化设计和插件机制。
在 c++ 中,调用 dll 中的函数有两种主要方式:
- 隐式链接(implicit linking)
- 显式链接(explicit linking)
二、隐式链接(implicit linking)
1. 原理
程序启动时自动加载 dll,通过 .lib 导入库将 dll 中的函数符号链接到可执行文件中。
2. 使用步骤
(1)准备三个文件:
mydll.dll:动态库文件mydll.lib:导入库(由 dll 生成)mydll.h:声明导出函数
(2)头文件示例(mydll.h)
#ifdef __cplusplus
extern "c" {
#endif
__declspec(dllimport) int add(int a, int b);
#ifdef __cplusplus
}
#endif注意:dllimport 表示从 dll 导入函数。
(3)链接 .lib 文件
在项目中添加 .lib 路径,并链接:
#pragma comment(lib, "mydll.lib")
(4)直接调用函数
#include "mydll.h" int result = add(3, 4); // 直接像普通函数一样调用
3. 特点
| 优点 | 缺点 |
|---|---|
| 使用简单,像调用本地函数 | 启动时必须找到 dll,否则程序无法启动 |
| 编译期检查函数签名 | 不支持动态选择或延迟加载 |
| 性能略高(无需查表) | 难以实现插件系统或热更新 |
三、显式链接(explicit linking)
1. 原理
运行时通过 loadlibrary 和 getprocaddress 手动加载 dll 并获取函数地址。
2. 使用步骤
(1)不需要 .lib 文件,只需:
mydll.dllmydll.h(知道函数原型)
(2)加载 dll 并获取函数指针
#include <windows.h>
#include <iostream>
// 定义函数指针类型
typedef int (*addfunc)(int, int);
int main()
{
hmodule hdll = loadlibrary(_t("mydll.dll")); // 加载 dll
if (hdll == null) {
std::cout << "无法加载 dll!" << std::endl;
return -1;
}
// 获取函数地址
addfunc add = (addfunc)getprocaddress(hdll, "add");
if (!add) {
std::cout << "无法找到函数 add!" << std::endl;
freelibrary(hdll);
return -1;
}
// 调用函数
int result = add(3, 4);
std::cout << "结果:" << result << std::endl;
// 卸载 dll
freelibrary(hdll);
return 0;
}3. 特点
| 优点 | 缺点 |
|---|---|
| 运行时动态加载,灵活 | 使用复杂,需手动管理函数指针 |
| 可判断 dll 是否存在,提供降级方案 | 无编译期检查,易出错(函数名拼错) |
| 支持插件系统、热更新、按需加载 | 性能稍低(需查表) |
| 程序可容忍缺失 dll | 需要正确处理 freelibrary 防止内存泄漏 |
四、核心区别对比表
| 对比项 | 隐式链接 | 显式链接 |
|---|---|---|
| 加载时机 | 程序启动时自动加载 | 运行时手动加载(loadlibrary) |
| 是否需要 .lib | 是 | 否(可选) |
| 函数调用方式 | 直接调用(如 add(1,2)) | 通过函数指针调用 |
| 启动依赖 | 必须存在 dll,否则无法启动 | 可容忍缺失,运行时报错 |
| 灵活性 | 低 | 高(可动态选择、卸载、替换) |
| 适用场景 | 核心功能、稳定依赖 | 插件、可选模块、第三方组件 |
| 错误处理 | 启动失败 | 可在运行时提示用户 |
| 性能 | 略高 | 略低(需查找符号) |
| 典型 api | 无(编译器自动处理) | loadlibrary, getprocaddress, freelibrary |
五、如何选择?
| 场景 | 推荐方式 |
|---|---|
| 程序核心功能依赖的 dll(如运行库) | ✅ 隐式链接 |
| 第三方 sdk、硬件驱动接口 | ✅ 显式链接(容错更好) |
| 实现插件系统(如 photoshop 滤镜) | ✅ 显式链接 |
| 需要热更新或动态替换模块 | ✅ 显式链接 |
| 小项目、简单调用、dll 一定存在 | ✅ 隐式链接更方便 |
六、最佳实践建议
- 优先考虑显式链接 用于第三方或可选模块,提升程序健壮性。
- 使用 raii 封装
hmodule,避免忘记freelibrary:
class dllloader {
public:
dllloader(const tchar* name) { hdll = loadlibrary(name); }
~dllloader() { if (hdll) freelibrary(hdll); }
hmodule get() { return hdll; }
private:
hmodule hdll = nullptr;
};- 导出 c 函数(用
extern "c")避免 c++ 名称修饰问题。 - 在发布程序时,确保 dll 路径正确(当前目录、系统路径、应用程序目录等)。
七、补充:如何导出 dll 函数?
// mydll.cpp
extern "c" __declspec(dllexport) int add(int a, int b)
{
return a + b;
}extern "c" 防止 c++ 编译器名称修饰,便于 getprocaddress 查找。
总结
| 方式 | 何时用 | 关键词 |
|---|---|---|
| 隐式链接 | 简单、固定依赖 | .lib、启动加载、直接调用 |
| 显式链接 | 灵活、容错、插件 | loadlibrary、getprocaddress、运行时加载 |
💬 一句话总结:
- 要简单直接 → 用 隐式链接
- 要灵活可控 → 用 显式链接
显示声明与隐式声明的使用与区别
到此这篇关于c++中显示与隐式加载dll的使用与区别的文章就介绍到这了,更多相关c++ 显示与隐式加载dll内容请搜索代码网以前的文章或继续浏览下面的相关文章希望大家以后多多支持代码网!
发表评论