GetProcAddress返回NULL且GetLastError为127的根本原因是调用约定不匹配导致函数名修饰不一致,使GetProcAddress无法在导出表中精确匹配函数名;需确保函数声明使用extern "C"、正确指定调用约定(如__stdcall),并用dumpbin验证真实导出名。

Windows 下用 GetProcAddress 获取函数指针要先拿到模块句柄
直接调用 GetProcAddress 会返回 nullptr,除非你已经有合法的 HMODULE。它不认文件路径,只认已加载的模块句柄。
常见错误是传入 DLL 路径字符串,比如:GetProcAddress("mylib.dll", "my_func") —— 这根本编译不过,因为第一个参数类型是 HMODULE。
- 必须先用
LoadLibrary或LoadLibraryEx加载 DLL,获取HMODULE - 若 DLL 依赖其他 DLL,确保它们在
PATH或当前目录中,否则LoadLibrary失败 - 函数名区分大小写,且需与 DLL 导出符号完全一致(可用
dumpbin /exports mylib.dll查看真实导出名) - C++ 编译的函数默认有名字修饰(name mangling),导出 C 风格函数要用
extern "C"声明,否则导出名类似?add@Math@@SAHHH@Z,GetProcAddress找不到
HMODULE hMod = LoadLibrary(L"mylib.dll");
if (!hMod) { /* 错误处理 */ }
typedef int (*add_func)(int, int);
add_func pAdd = (add_func)GetProcAddress(hMod, "add"); // 注意:必须是 "add",不是 "add@8" 或修饰名
Linux/macOS 下用 dlsym 前得先 dlopen,且注意符号可见性
dlsym 不会自动解析未导出的符号,哪怕函数在源码里是 static 或编译时被优化掉也不行。
典型失败场景:编译动态库时没加 -fPIC,或链接时没加 -shared,导致 dlopen 返回 nullptr;或者函数没加 __attribute__((visibility("default"))),导致符号被隐藏。
立即学习“C++免费学习笔记(深入)”;
- 编译动态库必须用:
g++ -fPIC -shared -o libmylib.so mylib.cpp - 若使用
-fvisibility=hidden(常见于大型项目),导出函数必须显式标记:extern "C" __attribute__((visibility("default"))) int add(int a, int b) { ... } -
dlopen的路径可以是绝对路径、相对路径,或仅文件名(此时依赖LD_LIBRARY_PATH或/etc/ld.so.cache) - 获取函数指针后,务必用
reinterpret_cast转成目标函数类型,C 风格强制转换在 C++ 中不安全
void* handle = dlopen("libmylib.so", RTLD_LAZY);
if (!handle) { /* dlerror() 查错 */ }
using add_func = int(*)(int, int);
add_func pAdd = reinterpret_cast<add_func>(dlsym(handle, "add"));
跨平台封装时别硬写宏判断,优先用 std::function + RAII 管理句柄
手动配对 LoadLibrary/FreeLibrary 或 dlopen/dlclose 容易漏掉释放,尤其异常路径下。更麻烦的是函数签名变化时,一堆 typedef 和 reinterpret_cast 很难维护。
- 把模块句柄和函数指针打包进一个类,构造时
LoadLibrary/dlopen,析构时自动清理 - 用模板推导函数签名,避免手写
typedef,例如:auto pAdd = lib.get_function<int(int, int)>("add") - Windows 下导出名若含修饰(如 C++ 成员函数),不要尝试硬解,改用模块定义文件(.def)或
extern "C"导出简单接口 - macOS 的
dlsym行为和 Linux 基本一致,但需注意:若用clang++编译,-fvisibility=hidden默认启用,比 GCC 更激进
调试时最常卡在符号找不到,优先查三件事
90% 的 “GetProcAddress 返回空” 或 “dlsym 返回 nullptr” 不是代码写错,而是环境或构建配置问题。
- 用
dumpbin /exports(Windows)或nm -D/objdump -T(Linux/macOS)确认函数确实导出了,且名字匹配 - 检查 DLL/.so 是否真的被加载:Windows 可用 Process Explorer 查进程模块列表;Linux 用
lsof -p <pid> | grep mylib - 动态库依赖缺失时,
LoadLibrary和dlopen都静默失败——Windows 用Dependency Walker或Dependencies.exe,Linux 用ldd libmylib.so
导出符号名、调用约定、ABI 兼容性、加载路径,这四点串起来才跑得通。少盯代码,多查二进制本身。


















