0

0

c++如何实现动态加载dll/so插件_c++ dlopen与GetProcAddress使用【实战】

裘德小鎮的故事

裘德小鎮的故事

发布时间:2026-01-08 18:53:02

|

195人浏览过

|

来源于php中文网

原创

Windows用LoadLibrary/GetProcAddress、Linux/macOS用dlopen/dlsym加载动态库;需注意路径、导出声明、函数签名匹配、错误检查及资源释放。

c++如何实现动态加载dll/so插件_c++ dlopen与getprocaddress使用【实战】

Windows 下用 LoadLibraryGetProcAddress 加载 DLL

Windows 不支持 dlopen,必须用 Win32 API。核心是先加载模块,再按符号名取函数指针。常见错误是传错路径、没加 .dll 后缀、或函数声明与导出不一致。

  • LoadLibrary 返回 HMODULE,传入路径应为绝对路径或位于 PATH 或当前目录的相对路径;推荐用 GetFullPathName 预处理
  • 导出函数必须用 extern "C" + __declspec(dllexport)(DLL 编译时),否则 C++ 名字修饰会导致 GetProcAddress 找不到
  • 获取函数指针后务必用 typedef 声明匹配签名,强制类型转换容易因参数/调用约定不一致引发损坏
typedef int (*add_func)(int, int);
HMODULE hMod = LoadLibrary(L"myplugin.dll");
if (hMod) {
    add_func add = (add_func)GetProcAddress(hMod, "add");
    if (add) {
        int res = add(3, 5); // 正常调用
    }
    FreeLibrary(hMod); // 记得释放
}

Linux/macOS 下用 dlopen / dlsym 加载 SO

POSIX 标准接口,行为统一但细节更敏感:路径、符号可见性、链接方式都影响成败。最常踩的坑是未加 -fPIC 编译 SO,或忘记 -rdynamic 导致主程序符号不可见(当插件需回调主程序时)。

  • dlopen 第二个参数建议固定用 RTLD_LAZY | RTLD_LOCAL:延迟绑定降低启动开销,LOCAL 避免符号污染全局符号表
  • dlsym 返回 void*,必须显式转为目标函数指针类型,不能直接赋值给普通函数变量(C++ 类型检查严格)
  • SO 文件名必须带 .so 后缀,且运行时需确保在 LD_LIBRARY_PATH 中,或用 ./libxxx.so 绝对/相对路径
#include 
typedef int (*add_func)(int, int);
void* handle = dlopen("./myplugin.so", RTLD_LAZY | RTLD_LOCAL);
if (handle) {
    add_func add = reinterpret_cast(dlsym(handle, "add"));
    if (add) {
        int res = add(3, 5);
    }
    dlclose(handle);
}

跨平台封装要注意的三件事

写一套代码同时跑 Windows/Linux,不能只靠宏开关,底层语义差异必须抹平。最容易被忽略的是错误诊断和生命周期管理。

  • 错误信息不可靠:GetLastError()dlerror() 都是“上次调用”结果,必须在每次 LoadLibrary/dlopen 后立刻检查,中间穿插其他系统调用会覆盖它
  • 函数指针类型必须统一定义,例如用 using plugin_add_t = int(*)(int,int);,避免各处 typedef 不一致
  • 资源释放逻辑要对称:FreeLibrary 成功后句柄立即失效;dlclose 只是减引用计数,真正卸载可能延迟——若插件内有全局对象析构依赖主程序状态,可能 crash

GetProcAddress 找不到函数的五个真实原因

不是名字拼错了那么简单。调试时优先查这几点:

PodLM
PodLM

PodLM是一款强大的AI播客生成工具

下载

立即学习C++免费学习笔记(深入)”;

  • DLL 编译时没加 __declspec(dllexport),或用了 DEF 文件但导出名写错(比如写了 add@8 却用 "add" 查)
  • C++ 源文件里函数没加 extern "C",导致导出的是 ??add@@YAHHH@Z 这类修饰名
  • 目标函数是类成员函数或模板实例,无法直接导出——必须包装成自由函数
  • 进程位数不匹配:32 位程序加载 64 位 DLL 直接失败(LoadLibrary 返回 NULL),错误码是 ERROR_BAD_EXE_FORMAT
  • 依赖的其它 DLL 缺失,LoadLibrary 失败但你以为是函数没找到;用 Dependencies.exe(Windows)或 ldd(Linux)查依赖树

动态加载本身不难,难的是让符号能被稳定、可预测地找到。别省略导出声明,别绕过类型安全强制转换,更别假设路径一定存在——这些地方出问题,日志里往往只有一行空指针解引用。

相关专题

更多
c语言中null和NULL的区别
c语言中null和NULL的区别

c语言中null和NULL的区别是:null是C语言中的一个宏定义,通常用来表示一个空指针,可以用于初始化指针变量,或者在条件语句中判断指针是否为空;NULL是C语言中的一个预定义常量,通常用来表示一个空值,用于表示一个空的指针、空的指针数组或者空的结构体指针。

231

2023.09.22

java中null的用法
java中null的用法

在Java中,null表示一个引用类型的变量不指向任何对象。可以将null赋值给任何引用类型的变量,包括类、接口、数组、字符串等。想了解更多null的相关内容,可以阅读本专题下面的文章。

435

2024.03.01

typedef和define区别
typedef和define区别

typedef和define区别在类型检查、作用范围、可读性、错误处理和内存占用等。本专题为大家提供typedef和define相关的文章、下载、课程内容,供大家免费下载体验。

107

2023.09.26

c语言typedef的用法
c语言typedef的用法

c语言typedef的用法有定义基本类型别名、定义结构体别名、定义指针类型别名、定义枚举类型别名、定义数组类型别名等。本专题为大家提供typedef相关的文章、下载、课程内容,供大家免费下载体验。

96

2023.09.26

java进行强制类型转换
java进行强制类型转换

强制类型转换是Java中的一种重要机制,用于将一个数据类型转换为另一个数据类型。想了解更多强制类型转换的相关内容,可以阅读本专题下面的文章。

282

2023.12.01

string转int
string转int

在编程中,我们经常会遇到需要将字符串(str)转换为整数(int)的情况。这可能是因为我们需要对字符串进行数值计算,或者需要将用户输入的字符串转换为整数进行处理。php中文网给大家带来了相关的教程以及文章,欢迎大家前来学习阅读。

315

2023.08.02

int占多少字节
int占多少字节

int占4个字节,意味着一个int变量可以存储范围在-2,147,483,648到2,147,483,647之间的整数值,在某些情况下也可能是2个字节或8个字节,int是一种常用的数据类型,用于表示整数,需要根据具体情况选择合适的数据类型,以确保程序的正确性和性能。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

529

2024.08.29

c++怎么把double转成int
c++怎么把double转成int

本专题整合了 c++ double相关教程,阅读专题下面的文章了解更多详细内容。

50

2025.08.29

Golang 分布式缓存与高可用架构
Golang 分布式缓存与高可用架构

本专题系统讲解 Golang 在分布式缓存与高可用系统中的应用,涵盖缓存设计原理、Redis/Etcd集成、数据一致性与过期策略、分布式锁、缓存穿透/雪崩/击穿解决方案,以及高可用架构设计。通过实战案例,帮助开发者掌握 如何使用 Go 构建稳定、高性能的分布式缓存系统,提升大型系统的响应速度与可靠性。

58

2026.01.09

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
PostgreSQL 教程
PostgreSQL 教程

共48课时 | 6.8万人学习

Git 教程
Git 教程

共21课时 | 2.5万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号