直接导出c++类不可行,因编译器只导出函数/变量符号,类成员函数经name mangling后getprocaddress无法定位;虚表布局、abi不一致易致崩溃。必须用extern "c"导出工厂函数与纯虚接口实现跨dll多态。

直接导出 C++ 类本身不可行,必须用 extern "C 导出工厂函数,配合纯虚接口实现跨 DLL 多态调用。否则 GetProcAddress 找不到符号,或虚函数调用崩溃。
为什么不能直接导出 class?
C++ 类没有“导出”概念——编译器只导出函数或变量符号。即使你给类加 __declspec(dllexport),它也只是导出类中所有非内联成员函数(带 name mangling),而主程序无法通过名字安全定位;更严重的是,虚表布局、sizeof、析构顺序在不同编译单元中极易错位。
-
GetProcAddress(hMod, "MyPlugin::Create")必然返回nullptr,因为实际导出名类似?Create@MyPlugin@@SAPAV1@XZ - 哪怕硬编码 mangled 名字,主程序调用虚函数时也可能跳到非法地址,尤其 MSVC 2019/2022 混用时
- 含
std::string成员或虚继承的接口,在插件和主程序间会因 ABI 不一致直接 crash
extern "C" 工厂函数怎么写才安全?
必须同时满足:C 风格符号、显式导出、返回裸指针、配套销毁函数。缺一不可。
- 导出函数签名必须是:
extern "C" __declspec(dllexport) IPlugin* create_plugin(); - 不能返回
std::unique_ptr<iplugin></iplugin>或引用——STL 智能指针跨 DLL 析构会触发 CRT 堆不匹配 - 必须同步导出销毁函数:
extern "C" __declspec(dllexport) void destroy_plugin(IPlugin*); - 工厂内部用
new,销毁函数内部用delete,且两者必须在同一个 DLL 的堆上执行
验证方式:Windows 下运行 DumpBin /exports plugin.dll,确认导出表里是 create_plugin,不是乱码。
纯虚接口 IPlugin 的定义红线
接口不是越“面向对象”越可靠,而是越接近 C 越稳。任何偏离都会在运行时放大 ABI 风险。
- 只允许纯虚函数 +
virtual ~IPlugin() = default;,禁止任何数据成员、禁止 inline 实现 - 参数和返回值只能是 POD 类型:
const char*、int、void*、自定义结构体(需#pragma pack(1)对齐) - 禁用
std::string、std::vector、异常、dynamic_cast、RTTI - 头文件必须被主程序和插件共同包含,且编译选项完全一致(/MD vs /MT、/Zi vs /Zi、结构体对齐等)
dlopen/LoadLibrary 失败后该查什么?
返回 NULL 是结果,不是原因。90% 的问题藏在错误码里,不查就重试只会掩盖链路断裂点。
- Windows:立刻调
GetLastError()——ERROR_PROC_NOT_FOUND表示没加extern "C";ERROR_MOD_NOT_FOUND很可能是路径不对或工作目录变更;ERROR_DLL_NOT_FOUND是缺 vcruntime140.dll 等运行时 - Linux:立刻调
dlerror()—— 返回"undefined symbol"通常不是函数名错,而是插件依赖的另一个.so没找到 - 加载时务必用
RTLD_LAZY | RTLD_GLOBAL(Linux)或确保LoadLibrary路径为绝对路径(Windows)
最易被忽略的一点:插件 DLL 内部若使用了静态全局对象(如单例、std::mutex),其构造/析构可能在 LoadLibrary 或 FreeLibrary 期间触发,而此时 CRT 状态不稳定——这类隐式依赖必须主动清理或改用显式 init/shutdown 函数控制。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











