raii封装loadlibrary/freelibrary需用类管理hmodule,构造加载、析构安全释放,禁用拷贝、支持移动,失败抛异常;getprocaddress须判空并正确类型转换,导出函数需extern "c"避免name mangling。

如何用 RAII 封装 LoadLibrary 和 FreeLibrary
直接裸调 LoadLibrary / FreeLibrary 容易漏掉释放、重复释放或提前释放,RAII 是唯一靠谱的解法。核心是把句柄生命周期绑定到对象生命周期,析构函数里无条件调用 FreeLibrary —— 但必须先判空,且不能对无效句柄调用 FreeLibrary(它会静默失败,但可能掩盖逻辑错误)。
关键点:
-
HMODULE成员变量初始化为nullptr,构造时只在LoadLibrary成功后才赋值 - 移动语义必须支持:移动构造/赋值后,原对象的
m_hModule置为nullptr,避免双重释放 - 拷贝应禁用(
= delete),DLL 句柄不支持共享所有权 - 构造失败时抛异常(比如封装成
std::runtime_error,附带GetLastError()错误码和字符串)
怎么安全获取函数指针?GetProcAddress 的坑在哪
GetProcAddress 返回的是 FARPROC,必须显式转成目标函数签名类型,否则调用时崩溃或行为未定义。C++11 起推荐用 reinterpret_cast,别用 C 风格强制转换。
常见错误:
- 没检查返回值是否为
nullptr(函数名拼错、导出名不匹配、函数未__declspec(dllexport)) - 类型声明和 DLL 中实际函数签名不一致(参数个数、
const、调用约定)—— 最典型是忘了__cdecl或__stdcall,导致栈不平衡 - 把函数指针存进类成员时,类型要完整写清楚,例如:
using func_t = int(__cdecl*)(int, const char*);,再声明func_t m_func = nullptr;
建议封装一个模板方法:template<typename funct> FuncT get_proc_address(const char* name) { ... }</typename>,内部做判空 + 类型转换,调用方更干净。
Windows DLL 导出名与 C++ 函数重载冲突怎么破
C++ 编译器会对函数名做 name mangling,GetProcAddress 查不到。除非你用 extern "C" 声明导出函数,否则必须用 dumpbin /exports your.dll 看真实导出名(比如 ?add@@YAHHH@Z),这根本没法维护。
正确做法:
- DLL 工程中,导出函数一律加
extern "C"和__declspec(dllexport),例如:extern "C" __declspec(dllexport) int add(int a, int b); - 头文件里对应声明也加
extern "C",确保调用方看到的签名一致 - 如果必须导出类或重载函数,改用 COM 接口或纯虚基类 + 工厂函数方式,绕过 name mangling
不这么做,封装类再漂亮也没法拿到函数指针。
为什么 Release 模式下 LoadLibrary 失败但没报错
最常被忽略的是路径问题:LoadLibrary("mylib.dll") 是按 Windows DLL 搜索顺序找的(当前目录、系统目录、PATH 等),不是按源码相对路径。Debug 下可能碰巧工作,Release 下因为工作目录不同就失败。
解决办法只有两个:
- 用绝对路径加载:
GetFullPathName+LoadLibrary,尤其适合插件目录固定的情况 - 把 DLL 放到 EXE 同目录,或显式修改搜索路径(
AddDllDirectory,注意 Vista+ 才支持,XP 不行) - 调试时务必用
GetLastError()拿错误码,ERROR_FILE_NOT_FOUND和ERROR_INVALID_HANDLE很容易混淆
还有种隐性失败:DLL 依赖的其他 DLL 缺失(比如 VC++ 运行库),LoadLibrary 也会失败,但错误码是 ERROR_MOD_NOT_FOUND。用 Dependency Walker 或 dumpbin /dependents 提前查清依赖链。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











