400 8949 560

NEWS/新闻

分享你我感悟

您当前位置> 主页 > 新闻 > 技术开发

c++怎么编写动态链接库dll_c++ __declspec(dllexport)导出与调用【方法】

发表时间:2026-01-02 00:00:00

文章作者:冰火之心

浏览次数:

根本原因是C++名称修饰导致符号名不可预测,解决需用extern "C"禁用修饰或查修饰名;dllimport非必须但推荐以避免跳转开销;GetProcAddress调用须严格匹配函数签名及调用约定;DLL路径错误会导致加载失败,应置于exe同目录。

__declspec(dllexport) 导出函数时,为什么调用方找不到符号?

根本原因是 C++ 编译器对函数名做了 名称修饰(name mangling),比如 int add(int, int) 可能被编译成 ?add@@YAHHH@Z。而隐式链接(#pragma comment(lib, "..."))或 LoadLibrary + GetProcAddress 方式都依赖**可预测的符号名**。

解决办法只有两个方向:

  • extern "C" 禁用 C++ 名称修饰,导出 C 风格符号(推荐用于隐式链接)
  • 不加 extern "C",但调用时用修饰后的名字(极难维护,仅限 GetProcAddress 场景且需用 dumpbin /exports 查看真实符号)
extern "C" {
    __declspec(dllexport) int add(int a, int b) {
        return a + b;
    }
}

隐式链接 DLL 时,__declspec(dllimport) 是必须写的吗?

不是必须,但强烈建议写。不写也能链接成功(尤其同项目下),但会导致额外跳转开销:编译器生成“桩函数(thunk)”,运行时再跳进 DLL 的实际地址;写了 dllimport 后,编译器直接生成对导入表的间接调用,更高效。

通用写法是用宏统一控制:

立即学习“C++免费学习笔记(深入)”;

#ifdef BUILDING_MYDLL
    #define MYDLL_API __declspec(dllexport)
#else
    #define MYDLL_API __declspec(dllimport)
#endif

MYDLL_API int add(int, int);

编译 DLL 时定义 BUILDING_MYDLL,调用方不定义 —— 这样头文件可复用,且语义清晰。

LoadLibraryGetProcAddress 调用时,函数指针类型怎么写才安全?

类型不匹配会导致栈错乱、崩溃或返回垃圾值。必须与 DLL 中导出函数的签名完全一致,包括调用约定(__cdecl / __stdcall)。Windows API 默认是 __stdcall,但 C++ 普通函数默认是 __cdecl

安全做法:

  • 导出函数显式声明调用约定,如 extern "C" __declspec(dllexport) int __cdecl add(int, int);
  • 调用方用 typedef 定义精确匹配的函数指针类型
typedef int (__cdecl *AddFunc)(int, int);

HMODULE hDll = LoadLibrary(L"mydll.dll");
if (hDll) {
    AddFunc pAdd = (AddFunc)GetProcAddress(hDll, "add");
    if (pAdd) {
        int result = pAdd(3, 4); // 正确调用
    }
}

生成 DLL 后,为什么程序运行时报“找不到 dll”或“无法启动此程序”?

不是代码问题,是 Windows 动态库加载路径规则导致的。系统按以下顺序搜索 DLL:

  • 应用程序所在目录
  • 当前工作目录(非 exe 目录!)
  • Windows 系统目录(System32
  • Windows 目录
  • PATH 环境变量中的路径

最稳妥的做法是:把 .dll 文件放在调用它的 .exe 同一目录下。开发阶段可用 Visual Studio 的“项目属性 → 配置属性 → 生成事件 → 后生成事件”自动复制:

copy "$(OutDir)mydll.dll" "$(OutDir)"

注意:Debug/Release 输出路径不同,要分别配置;若用相对路径,请确认 $(OutDir) 指向正确位置。

导出符号是否带类、模板、异常、STL 对象,会极大增加 ABI 兼容风险 —— 这些都不该跨 DLL 边界暴露,只导出纯 C 函数接口最稳。

相关案例查看更多