LoadLibrary动态加载DLL并GetProcAddress获取函数指针是Windows下C++运行时调用DLL的核心方法;需注意导出符号匹配、函数指针类型严格一致、错误检查及跨语言接口规范。

怎么用 LoadLibrary 加载 DLL 并调用函数
Windows 下 C++ 调用 DLL 的核心路径就是 LoadLibrary + GetProcAddress,不是头文件包含、也不是链接 .lib,那是编译期绑定;动态加载得靠运行时手动取地址。
常见错误是直接写 MyFunc() 调用——这会链接失败或崩溃,因为编译器根本不知道这个符号在哪。必须先拿到函数指针。
-
LoadLibrary返回HMODULE,传入 DLL 路径(支持相对路径,但当前工作目录得对) - 用
GetProcAddress拿函数地址,注意:C++ 编译的函数名会被修饰(mangled),导出时得用extern "C"或 .def 文件,否则GetProcAddress(h, "Add")找不到 - 函数指针类型必须严格匹配,建议用
typedef或using定义,比如typedef int (*AddFunc)(int, int) - 调用前务必检查
LoadLibrary和GetProcAddress返回值是否为NULL,否则一调就崩
auto hDll = LoadLibrary(L"mylib.dll");
if (!hDll) { /* GetLastError() 看具体错因 */ }
auto pAdd = (AddFunc)GetProcAddress(hDll, "Add");
if (!pAdd) { /* 函数名不匹配 or 未导出 */ }
int r = pAdd(3, 5);为什么 GetProcAddress 找不到函数名
绝大多数“找不到函数”问题不是路径错,而是符号导出没配对。C++ 默认 name mangling,void calc(int) 可能被编译成 ?calc@@YAXH@Z,你传 "calc" 当然查不到。
- DLL 工程里函数声明加
extern "C",禁用修饰:extern "C" __declspec(dllexport) int Add(int a, int b); - 或者用 .def 文件显式导出,内容写
EXPORTS\nAdd,这样不管 C/C++ 都按原名导出 - 用
dumpbin /exports mylib.dll确认实际导出名(命令行工具,VS 自带) - 64 位程序不能加载 32 位 DLL,反之亦然——
GetLastError()返回126(ERROR_MOD_NOT_FOUND)有时其实是位数不匹配,不是文件不存在
静态链接 .lib 和动态 LoadLibrary 哪个更合适
不是“哪个更好”,是“场景不同”。静态链接(.lib + 隐式加载)启动快、调用快,但 DLL 必须在启动时就存在;LoadLibrary 支持按需加载、热替换、插件化,代价是每次调用多一次指针解引用,且要自己管生命周期。
立即学习“C++免费学习笔记(深入)”;
- 需要运行时决定加载哪个模块(比如用户选皮肤、算法插件)→ 必须用
LoadLibrary - DLL 可能缺失或版本不兼容,程序要降级运行 → 动态加载可捕获失败,走备用逻辑
- 隐式链接(即项目里加 .lib、声明
__declspec(dllimport))会在进程启动时自动调LoadLibrary,失败直接弹 Windows 错误框,无法拦截 -
FreeLibrary不是必须调,进程退出时系统自动释放,但若反复加载/卸载同一 DLL,不调可能泄漏模块句柄(尤其调试时)
跨语言调用 DLL(比如 Python 或 C#)要注意什么
C++ 写的 DLL 被其他语言调,本质还是导出 C 接口。哪怕你内部全用 STL,对外接口也得是纯 C 风格:无类、无引用、无异常、参数和返回值是基本类型或指针。
- 字符串传参一律用
const char*或const wchar_t*,别传std::string—— 其他语言没法构造或析构它 - 资源(如分配的内存)谁分配谁释放:如果 DLL 里
malloc了内存并返回指针,必须提供一个FreeBuffer函数,由调用方传回给 DLL 释放,不能让 Python 用free() - 调用约定必须显式指定,比如
__stdcall(Windows API 默认)或__cdecl(C 默认),Python 的ctypes需要显式设winmode=WINFUNCTYPE或CALLBACK - 结构体对齐要一致,C# 用
[StructLayout(LayoutKind.Sequential, Pack = 1)],C++ 侧也得#pragma pack(1),否则字段偏移错位
真正麻烦的从来不是加载动作本身,而是 DLL 和主程序之间那条边界上——数据怎么传、内存谁管、错误怎么报、线程怎么安全。这些细节漏一个,就卡在“能加载但一调就崩”上。











