windows下c++动态加载dll需用loadlibrary获取模块句柄,再用getprocaddress获取函数地址;注意路径为宽字符绝对路径、强制类型转换函数指针、调用后freelibrary释放。

怎么用 LoadLibrary 和 GetProcAddress 手动加载 DLL
Windows 下 C++ 调用 DLL 最灵活的方式是运行时动态加载,不依赖 .lib 导入库。核心就是两步:先用 LoadLibrary 拿到模块句柄,再用 GetProcAddress 拿函数地址。
常见错误是传错路径:LoadLibrary 接收的是宽字符字符串(L"xxx.dll"),传窄字符串或没加 L 前缀会返回 NULL;另外路径必须是绝对路径,或确保 DLL 在系统搜索路径(如当前目录、PATH 环境变量所列目录)中。
- 推荐用
GetFullPathName或GetModuleFileName+ 目录拼接生成绝对路径,避免相对路径行为不一致 - 调用完记得用
FreeLibrary释放,否则 DLL 句柄泄漏,下次LoadLibrary可能返回旧句柄而非重新加载 -
GetProcAddress返回的是FARPROC,必须强制转成对应函数指针类型,否则调用时参数压栈错位,直接崩溃
示例:
HMODULE hMod = LoadLibrary(L"mylib.dll");
if (!hMod) { /* 处理错误,GetLastError() 看具体原因 */ }
typedef int (*func_t)(int, const char*);
func_t fn = (func_t)GetProcAddress(hMod, "do_work");
if (fn) fn(42, "hello");为什么 __declspec(dllimport) 链接方式常出 “unresolved external”
这是头文件声明和链接阶段不匹配的典型表现:你写了 __declspec(dllimport),但链接时没提供对应的 .lib,或者 .lib 是用不同编译器/运行时生成的。
立即学习“C++免费学习笔记(深入)”;
根本原因是 Windows DLL 导出符号依赖 .lib 文件做“桩跳转”,而这个 .lib 必须和 DLL 编译时完全匹配(同编译器版本、同 CRT 类型:MT/MD、同架构 x64/x86)。
- 检查是否漏了
#pragma comment(lib, "mylib.lib")或项目里没加 .lib 到链接器输入项 - 确认 DLL 是用
__declspec(dllexport)正确导出的,且函数名没被 C++ 名字修饰干扰(加extern "C"或 .def 文件控制导出名) - 如果 DLL 是别人给的,务必确认它提供的是 VS2019 编译的 MD 版本,而你工程设的是 MT,那 .lib 就不能用
调用 C++ 类成员函数从 DLL 导出为什么总失败
直接导出类本身(比如 class __declspec(dllexport) Foo { ... };)在跨模块使用时极不可靠:对象内存布局、vtable 排布、异常处理机制都可能因编译器或设置不同而错乱。
安全做法是只导出 C 风格函数接口,把类封装在 DLL 内部,通过工厂函数创建/销毁实例。
- 导出两个函数:
Foo* create_foo()和void destroy_foo(Foo*),且都在 DLL 内实现 new/delete - 头文件里只放纯虚接口(
struct IFoo { virtual int work() = 0; virtual ~IFoo() = default; };),DLL 返回IFoo*,调用方只调虚函数 - 禁止跨 DLL 边界传递 STL 容器、异常、RTTI —— 这些都依赖一致的运行时,一旦不匹配就崩溃或静默错误
Unicode 和 ANSI 字符串传参在 DLL 调用中容易踩哪些坑
Windows API 默认是 Unicode(wchar_t*),但很多老 DLL 用 ANSI(char*)。混用会导致中文全变问号或崩溃,尤其在 GetProcAddress 后手动调用时极易忽略编码差异。
- 先查清 DLL 函数实际接受的是
LPCSTR还是LPCWSTR—— 看它的原始声明或文档,别猜 - 如果 DLL 是 ANSI 版,但你的程序是 Unicode 工程(默认),必须用
WideCharToMultiByte转字符串,且指定代码页(通常CP_ACP或CP_UTF8) - 不要用
TEXT("...")或_T("...")包裹字符串后直接传给 ANSI 函数:宏展开后仍是宽字符串,指针类型不匹配
最稳妥的做法:统一用 Unicode 编译整个方案,要求 DLL 提供宽字符接口;若做不到,就在调用层做显式转换,并验证转换前后长度和内容。
跨 DLL 的字符串生命周期要自己管好:DLL 不会帮你 malloc 或 delete 你传进去的缓冲区,谁分配谁释放。











