GetProcAddress返回127错误的根本原因是C++名称修饰导致导出符号名与调用时函数名不匹配,必须同时使用extern "C"(禁用name mangling)和__declspec(dllexport)(确保导出到表),缺一不可。

直接用 LoadLibrary + GetProcAddress 就能跑起来,但 90% 的崩溃都出在符号导出和函数调用这两步——不是 DLL 没加载,是函数指针根本没拿到,或者拿到了却调错了。
导出函数必须同时加 extern "C" 和 __declspec(dllexport)
只加 extern "C",符号进不了 DLL 导出表;只加 __declspec(dllexport),C++ 名字修饰(name mangling)会让 GetProcAddress 查不到 "create_plugin",实际导出的是类似 ?create_plugin@@YAPAVIPlugin@@XZ 这种不可读名字。
- 正确写法必须套一层
extern "C"块,并在每个函数前加__declspec(dllexport) - 示例:
extern "C" { __declspec(dllexport) IPlugin* create_plugin(); __declspec(dllexport) void destroy_plugin(IPlugin* p); } - 别用
.def文件替代——它不参与编译检查,改了函数签名容易漏同步,出错难定位 - 验证方法:用
dumpbin /exports myplugin.dll(Windows)或nm -D myplugin.dll(Cygwin/MinGW)确认导出列表里有未修饰的函数名
GetProcAddress 返回空时,GetLastError() 才是真相
GetProcAddress 返回 nullptr 不代表函数不存在,大概率是名字对不上。此时 GetLastError() 往往返回 0(不是错误码),说明系统根本没查到匹配符号——不是找不到 DLL,是符号名错了。
- 常见误判:看到
nullptr就去检查路径,结果路径完全正确,问题出在导出声明漏了extern "C" - 必须在
GetProcAddress后立刻调GetLastError(),且仅用于诊断该次调用;之前 API 的错误值可能被覆盖 - 典型错误码:
ERROR_PROC_NOT_FOUND(127)= 符号名不匹配;ERROR_MOD_NOT_FOUND(126)= DLL 路径错或依赖缺失 - 路径建议用绝对路径,或通过
GetFullPathName转换相对路径,避免工作目录干扰
工厂函数返回裸指针,销毁必须配对调用插件自己的 destroy_plugin
不能把插件返回的 IPlugin* 直接塞进 std::shared_ptr 并用 delete 当 deleter——插件 DLL 的 operator delete 和主程序可能链接不同 CRT,跨模块 delete 是未定义行为,十有八九崩溃。
立即学习“C++免费学习笔记(深入)”;
- 安全做法:插件导出
destroy_plugin(IPlugin*),内部用和new匹配的delete或自定义 allocator - 主程序封装 RAII 类(如
PluginPtr),构造时调create_plugin,析构时调destroy_plugin,不碰delete - 禁止跨 DLL 边界传递任何 STL 类型:哪怕只是
std::string成员变量,在接口类里也导致虚表偏移错乱;纯虚类里只能有 POD 成员(int、char[64]、void*) - 所有参数和返回值限 POD 类型;字符串用
const char*+ 长度,或走 JSON 序列化
最易被忽略的一点:插件 DLL 的 ABI 必须和主程序严格一致——同一编译器版本、相同运行时(MD/MT)、相同结构体对齐(如 /Zp8)。哪怕只是 MSVC 2019 和 2022 混用,虚函数表布局就可能不同,create_plugin 返回的指针一调虚函数就跳飞。


















