getprocaddress用于从已加载dll中获取导出函数地址,必须传入loadlibrary返回的hmodule句柄,不能直接传路径;失败返回null,需结合getlasterror排查原因,且函数名大小写敏感、c++需extern "c"避免修饰。

Windows下用GetProcAddress获取DLL导出函数地址
必须先用LoadLibrary或LoadLibraryEx加载DLL,返回的HMODULE才是GetProcAddress的合法输入。直接传文件路径会失败——GetProcAddress只认已映射进进程地址空间的模块句柄。
常见错误是调用GetProcAddress后没检查返回值:NULL不只表示函数不存在,也可能是DLL根本没加载成功(但LoadLibrary没报错,比如依赖项缺失)。建议连带检查GetLastError():
auto hMod = LoadLibrary(L"mylib.dll");
if (!hMod) {
DWORD err = GetLastError(); // 查看具体原因
}
auto proc = GetProcAddress(hMod, "MyExportedFunc");
if (!proc) {
DWORD err = GetLastError(); // 可能是名称拼错、C++修饰名、或函数未__declspec(dllexport)
}
注意函数名大小写敏感,且C++编译器会对函数名做修饰(mangling)。如果导出的是C++成员函数或重载函数,几乎肯定找不到——除非用extern "C"声明,或在.def文件里显式指定未修饰名。
Linux下用dlsym获取SO导出符号地址
dlsym行为和GetProcAddress类似,但依赖dlopen返回的void*句柄。关键区别在于:默认dlopen使用RTLD_LOCAL,此时dlsym只能看到本SO显式导出的符号(即__attribute__((visibility("default")))或编译时加-fvisibility=default);若用RTLD_GLOBAL,则该SO的符号会进入全局符号表,可能影响后续dlopen的解析结果。
容易被忽略的点:
-
dlsym返回void*,必须强制转为对应函数指针类型,否则调用时ABI不匹配(比如参数个数/类型/调用约定错位),运行时崩溃而非编译报错 - SO里未加
visibility("default")的函数,默认被隐藏,dlsym查不到,即使它在源码里是public -
dlerror()必须在每次dlsym后立即调用——它不是线程安全的,且会被后续dl系列函数覆盖
示例:
void* handle = dlopen("./libmy.so", RTLD_LAZY);
if (!handle) { fprintf(stderr, "%s\n", dlerror()); }
// 必须转型,不能直接用void*
typedef int (*my_func_t)(const char*);
my_func_t func = (my_func_t)dlsym(handle, "my_exported_func");
if (!func) { fprintf(stderr, "%s\n", dlerror()); }
跨平台封装时要注意符号可见性与调用约定差异
Windows DLL默认不导出任何符号,必须显式标记__declspec(dllexport);Linux SO默认隐藏所有符号,需显式标记__attribute__((visibility("default")))。二者语义相反,混用宏定义容易漏导出。
调用约定也常踩坑:__cdecl(Windows默认)和__stdcall不兼容。若DLL导出函数用了__stdcall,而你用__cdecl类型去接,栈清理错乱,程序大概率崩在第一次调用后。SO虽无此问题(Linux统一用System V ABI),但跨平台封装时仍需统一约定。
建议做法:
- 头文件中统一用宏控制导出:如
#define MYAPI __declspec(dllexport)(Win)或#define MYAPI __attribute__((visibility("default")))(Linux) - 所有导出函数声明加
extern "C",避免C++ name mangling - 函数指针类型定义放在头文件里,确保调用端和SO/DLL端ABI一致
运行时解析失败的典型原因和快速定位方法
最常遇到的不是代码写错,而是环境或构建配置问题:
-
GetProcAddress返回NULL但GetLastError()是0 → 函数名确实不存在,检查DEF文件或dumpbin /exports mylib.dll -
dlsym失败且dlerror()返回"undefined symbol" → 符号未导出,用nm -D libmy.so确认是否在dynamic symbol table里 - 加载成功但调用崩溃 → 检查函数签名是否完全匹配(参数类型、const、指针层级)、调用约定、结构体内存布局(尤其含虚函数或packed属性)
- Windows上
LoadLibrary失败且GetLastError()是126(ERROR_MOD_NOT_FOUND)→ 缺少依赖DLL,用Dependencies.exe或ldd查依赖链
动态获取函数地址本身很简单,难的是让符号能被正确暴露、被稳定识别、并在不同平台保持二进制兼容——这些不在GetProcAddress或dlsym职责范围内,但恰恰是实际项目里卡住最多的地方。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











