C / C++ 调用 DLL 的几种常见方法(详解 + 示例)

C / C++ 调用 DLL 的几种常见方法(详解 + 示例)

C / C++ 调用 DLL 的几种常见方法(详解 + 示例)

在 Windows 平台开发中,DLL(Dynamic Link Library,动态链接库) 是非常核心的一种代码复用方式。

无论是:

调用第三方 SDK

拆分大型工程

插件化设计

COM / OPC / 工控 / 驱动相关开发

都会不可避免地用到 C / C++ 调用 DLL。

本文将从 零基础视角 出发,系统讲清楚:

DLL 是什么

C/C++ 调用 DLL 的 3 种主流方式

每种方式的使用场景

常见坑点与避坑建议

一、DLL 是什么?(一句话理解)

DLL 是一个“在运行时被加载的函数集合”

和静态库(.lib)的核心区别:

项目

DLL

静态库

链接时间

运行时

编译期

文件

.dll + .lib

.lib

更新

可单独替换

需重新编译

内存

多进程共享

各自一份

二、最基础示例:先做一个 DLL

1、 编写 DLL 源码

// mydll.h

#pragma once

#ifdef MYDLL_EXPORTS

#define MYDLL_API __declspec(dllexport)

#else

#define MYDLL_API __declspec(dllimport)

#endif

extern "C" MYDLL_API int Add(int a, int b);

// mydll.cpp

#define MYDLL_EXPORTS

#include "mydll.h"

int Add(int a, int b)

{

return a + b;

}

关键点解释:

__declspec(dllexport):导出函数

__declspec(dllimport):导入函数

extern "C":防止 C++ 名字修饰(非常重要)

2、 编译生成

打开 VS 开发者命令行,在开始菜单中找到并打开:

x64 Native Tools Command Prompt for VS 2022

注意位数:

x86 DLL → 用 x86 Native Tools

x64 DLL → 用 x64 Native Tools

使用 cl.exe 生成 Debug 版 DLL(带 PDB)

最常用 Debug 编译命令(推荐)

cl /LD /Zi /Od /MDd /EHsc mydll.cpp

各参数含义,编译选项参数说明

参数

含义

/LD

生成 DLL

/Zi

生成调试信息(PDB)

/Od

关闭优化(便于调试)

/MDd

使用 Debug CRT(msvcrtd.dll)

/EHsc

标准 C++ 异常处理(推荐)

D:\test\dll>cl /LD /Zi /Od /MDd /EHsc mydll.cpp

用于 x86 的 Microsoft (R) C/C++ 优化编译器 19.50.35720 版

版权所有(C) Microsoft Corporation。保留所有权利。

mydll.cpp

Microsoft (R) Incremental Linker Version 14.50.35720.0

Copyright (C) Microsoft Corporation. All rights reserved.

/out:mydll.dll

/dll

/implib:mydll.lib

/debug

mydll.obj

D:\test\dll>

编译结果

mydll.dll ← Debug DLL

mydll.lib ← 导入库

mydll.pdb ← 调试符号文件(关键)

指定 Debug 输出目录

cl /LD /Zi /Od /MDd mydll.cpp ^

/Fe:Debug\mydll.dll ^

/Fd:Debug\mydll.pdb

生成结果:

Debug\

├── mydll.dll

├── mydll.lib

└── mydll.pdb

Release 版对照

cl /LD /O2 /MD mydll.cpp

Debug

Release

/Zi /Od /MDd

/O2 /MD

有 pdb

可选 pdb

可单步

优化后难调试

编译选项参数说明

最终你会得到:

mydll.dll

mydll.lib

三、方式一:隐式链接(最推荐,最常用)

适用场景

DLL 在开发时就确定

SDK / 内部模块

不需要动态卸载

1、调用方代码

#include

#include "mydll.h"

int main()

{

int r = Add(2, 3);

std::cout << "result = " << r << std::endl;

return 0;

}

2、工程设置(Visual Studio)

添加 lib:

项目属性

链接器 → 输入 → 附加依赖项

添加:mydll.lib

放置 dll:

mydll.dll 放在:

exe 同目录

或 PATH 目录

优点

语法最简单

编译期检查函数签名

性能最好

缺点

DLL 缺失 → 程序启动直接失败

四、方式二:显式加载(LoadLibrary / GetProcAddress)

适用场景

插件系统

可选功能

DLL 可能不存在

逆向 / 热更新 / 工控系统常用

示例代码

#include

#include

typedef int (*AddFunc)(int, int);

int main()

{

HMODULE hDll = LoadLibraryA("mydll.dll");

if (!hDll)

{

std::cout << "LoadLibrary failed" << std::endl;

return -1;

}

AddFunc Add = (AddFunc)GetProcAddress(hDll, "Add");

if (!Add)

{

std::cout << "GetProcAddress failed" << std::endl;

FreeLibrary(hDll);

return -1;

}

int r = Add(3, 4);

std::cout << "result = " << r << std::endl;

FreeLibrary(hDll);

return 0;

}

关键注意点

函数名必须是导出名

C++ 函数如果没有 extern "C",名字会被修饰

函数指针签名必须完全一致,可以使用下面的命令查看函数指针签名

dumpbin /exports mydll.dll

调用约定要一致(__cdecl / __stdcall)

优点

程序可在 DLL 缺失时继续运行

灵活性极高

缺点

写法复杂

容易崩溃(函数签名不匹配)

五、方式三:DLL + .lib,但运行时可选(进阶)

这是 方式一和方式二的混合体:

编译期链接 .lib

运行时通过 LoadLibrary 判断是否可用

适合大型工程 / 插件化 SDK。

六、C++ 类如何导出 / 调用?

1、DLL 导出类(示例)

class __declspec(dllexport) MyClass

{

public:

int Add(int a, int b);

};

不推荐直接跨 DLL 导出 C++ 类,原因:

ABI 不稳定

编译器 / STL 版本耦合

内存管理容易炸

工程实践建议:

DLL 对外只暴露 C 接口,内部再用 C++

七、常见坑点总结(新手必看)

1、忘记 extern "C"

extern "C" __declspec(dllexport) int Func();

否则 GetProcAddress 很容易失败。

2、32 位 / 64 位不匹配

32 位 exe ❌ 加载 64 位 dll

会直接 LoadLibrary 失败

用 dumpbin /headers xxx.dll 查看位数

3、 调用约定不一致

__stdcall vs __cdecl

不一致 → 栈损坏 → 程序随机崩溃

4、 DLL 放置路径错误

Windows 查找 DLL 顺序:

exe 同目录

系统目录

PATH 环境变量

八、如何查看 DLL 导出函数?(实用技巧)

dumpbin /exports mydll.dll

可以看到:

函数名

序号

是否被修饰

这是调试 GetProcAddress 的神器。

九、实际工程建议(经验总结)

新项目:优先 extern "C" + 隐式链接

插件系统:显式加载

不要跨 DLL 传 STL 容器

不要在不同 CRT 中 free 内存

十、总结一句话

DLL 是 Windows 下模块化的核心能力,

会用 DLL,才算真正进入 C/C++ 工程开发。

相关推荐

365beat中文版 2025最新的小说网站,全网最全的小说网站,全在这儿了
365彩票所有官方app下载平台 《诛仙世界》青云星魄搭配全攻略:剑青云&雷青云最优解
365beat中文版 问道宠物飞升怎么做?(问道宠物飞升在哪里接任务)