news 2026/10/28 2:02:52

Visual Studio 动态链接库(DLL)生成完全指南

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Visual Studio 动态链接库(DLL)生成完全指南

一、前言

一个完整的 DLL 工程必须满足两个核心条件:

  1. 正确的项目类型配置:必须生成.dll文件,而不是.exe。
  2. 显式的符号导出:必须将需要公开的函数/类声明为导出,否则外部程序无法链接。

本文将从项目创建、代码编写、项目配置、生成产物、验证方法到常见错误,系统梳理 VS 生成 DLL 的完整流程。


二、完整生成流程

步骤 1:创建正确的 DLL 项目

方式 A:新建项目时直接选择 DLL 模板
  1. 打开 Visual Studio,选择“创建新项目”。
  2. 搜索并选择“动态链接库 (DLL)”模板。
  3. 输入项目名称、位置,点击“创建”。

使用此模板时,VS 通常会自动设置好项目类型、预处理器宏等基础配置。

方式 B:将现有项目改为 DLL

如果已经创建的是"空项目"或"控制台应用",需要手动修改:

  1. 右键项目 →属性。
  2. 进入配置属性 → 常规 → 配置类型。
  3. 将值改为“动态库 (.dll)”。

修改后需重新生成解决方案。


步骤 2:编写头文件并声明导出

头文件是 DLL 与调用方之间的"契约"。必须使用__declspec(dllexport)/__declspec(dllimport)机制来控制符号的导入/导出。

推荐宏定义方式
// MyDll.h#pragmaonce#ifdefBUILD_MYDLL#defineAPI_SYMBOL__declspec(dllexport)#else#defineAPI_SYMBOL__declspec(dllimport)#endifextern"C"API_SYMBOLintadd(inta,intb);
关键点说明
元素作用
__declspec(dllexport)在 DLL 项目中使用时,表示该符号需要被导出到 DLL 中。
__declspec(dllimport)在调用方项目中使用时,表示该符号从外部 DLL 导入,可提升编译优化效率。
extern "C"关闭 C++ 名称修饰(Name Mangling),确保导出的函数名与声明名一致,便于 C 语言或其他语言调用。
BUILD_MYDLL宏在 DLL 项目内定义,用于切换导出/导入模式。
导出 C++ 类时的写法
// MyDll.h#ifdefBUILD_MYDLL#defineAPI_SYMBOL__declspec(dllexport)#else#defineAPI_SYMBOL__declspec(dllimport)#endifclassAPI_SYMBOLCalculator{public:intAdd(inta,intb);intSubtract(inta,intb);};

注意:导出整个类时,所有成员函数都会被导出。如果只想导出部分函数,可以单独对函数加API_SYMBOL。


步骤 3:实现源文件

// MyDll.cpp#include"pch.h"#defineBUILD_MYDLL#include"MyDll.h"intadd(inta,intb){returna+b;}

实现文件需要在包含头文件之前定义BUILD_MYDLL,这样头文件中的API_SYMBOL才会展开为__declspec(dllexport)。


步骤 4:确保定义了BUILD_MYDLL宏

这一步是最容易遗漏的。如果未定义该宏,头文件中的API_SYMBOL会展开为__declspec(dllimport),导致 DLL 项目自身也不会导出任何符号。

配置方法(推荐在源文件中定义)

在每个实现 DLL 的.cpp文件顶部、包含头文件之前添加:

#defineBUILD_MYDLL#include"MyDll.h"
替代方法:在项目属性中定义
  1. 右键 DLL 项目 →属性。
  2. 进入配置属性 → C/C++ → 预处理器 → 预处理器定义。
  3. 添加BUILD_MYDLL。
  4. 确保配置为所有配置 / 所有平台(或分别对 Debug/Release、x86/x64 配置)。

在源文件中定义的方式更直观,能明确看到导出宏的生效位置,也便于排查问题。


步骤 5:生成项目并检查产物

点击生成 → 生成解决方案(或按Ctrl+Shift+B)后,VS 会在输出目录(如Debug/或Release/)生成以下文件:

文件说明是否必需
MyDll.dll动态链接库本身,包含函数代码。运行时必需
MyDll.lib导入库(Import Library),包含 DLL 导出符号的索引。编译链接时必需
MyDll.exp导出文件,用于链接阶段。通常由链接器自动生成
MyDll.pdb程序数据库文件,用于调试。调试时必需

如果 DLL 没有任何导出的函数,VS通常不会生成.lib文件。这是一个重要的判断信号。


三、调用方(.exe 项目)如何使用 DLL

1. 引入头文件


已知文件名,仍需要添加搜索路径
调用方项目中包含 DLL 的头文件:

#include"MyDll.h"

此时由于调用方没有定义BUILD_MYDLL,API_SYMBOL会展开为__declspec(dllimport)。

2. 链接导入库.lib

在调用方项目中,需要链接 DLL 生成的.lib文件:

方式 A:通过项目属性链接



需要知晓库名和库路径

  1. 右键调用方项目 →属性。
  2. 进入配置属性 → 链接器 → 输入 → 附加依赖项。
  3. 添加MyDll.lib。
  4. 进入链接器 → 常规 → 附加库目录,添加.lib文件所在目录。
方式 B:通过代码指令链接
#pragmacomment(lib,"MyDll.lib")

需确保.lib文件在链接器的搜索路径中。

3. 部署.dll文件

程序运行时,系统需要找到.dll文件。常见放置位置:

  • 与调用方.exe同一目录(最常用)。

  • 系统 PATH 环境变量中的某个目录。

  • 项目的工作目录。

不建议将自定义 DLL 放入C:\Windows\System32,以免造成系统污染或权限问题。


四、验证 DLL 是否导出成功

方法 1:使用 dumpbin 工具

  1. 打开“Developer Command Prompt for VS”或“x64 Native Tools Command Prompt”。
  2. 切换到 DLL 所在目录。
  3. 执行:
dumpbin /exports MyDll.dll
  1. 查看输出中的ordinal / hint / name列表。

如果看到了你定义的函数名(如add),说明导出成功。

方法 2:使用 Dependency Walker / Dependencies 工具

  • 旧版工具:depends.exe(Dependency Walker)。

  • 新版推荐:Dependencies,支持 Windows 10/11。

打开 DLL 文件后,可以在导出表中查看所有导出的符号。

方法 3:检查是否生成了.lib文件

如果生成目录中没有.lib文件,几乎可以确定 DLL 没有成功导出任何符号。


五、常见问题与解决方案

问题 1:编译成功,但没有生成.lib文件

原因:DLL 项目没有成功导出任何符号。

排查方向:

  • 检查是否在源文件中包含了#define BUILD_MYDLL(或项目属性中定义了该宏)。

  • 检查头文件中是否使用了__declspec(dllexport)。

  • 检查函数实现是否包含了正确的头文件。

问题 2:调用方报错:LNK2019 无法解析的外部符号

原因:调用方没有找到 DLL 的导入库或头文件。

解决方案:

  • 确认.lib文件路径已加入"附加库目录"。

  • 确认MyDll.lib已加入"附加依赖项"。

  • 确认调用方包含的头文件与 DLL 项目一致。

问题 3:运行时报错:找不到 DLL / 无法定位程序输入点

原因:运行时系统找不到.dll文件,或找到的旧版本 DLL 不包含该函数。

解决方案:

  • 将.dll复制到.exe同目录。

  • 使用dumpbin /imports MyExe.exe检查依赖的 DLL 名称。

  • 检查 PATH 环境变量中是否存在同名旧 DLL。

问题 4:导出的函数名被 C++ 修饰,dumpbin 看到乱码

原因:没有用extern "C"包裹函数声明。

解决方案:对需要保持原名的函数使用extern "C"。

问题 5:Debug 和 Release 配置行为不一致

原因:预处理器定义、附加库目录等配置只设置了单一配置。

解决方案:在项目属性中,配置选择“所有配置”,平台选择“所有平台”,确保设置对 Debug/Release、x86/x64 均生效。


六、最佳实践与注意事项

  1. 命名规范

    • 导出宏名建议与项目名关联,如BUILD_MYPROJECT/API_SYMBOL。

    • 避免使用过于通用的宏名,防止冲突。

  2. 头文件设计

    • 头文件应同时服务于 DLL 自身和调用方。

    • 不要在头文件中暴露内部实现细节。

  3. ABI 兼容性

    • 跨 DLL 边界传递对象时,注意 C++ 类的内存布局、编译器版本、运行时库一致性。

    • 优先导出 C 风格接口,避免导出 STL 容器、模板类等 ABI 不稳定的类型。

  4. 运行时库匹配

    • DLL 项目和调用方项目应使用相同的运行时库配置(/MD或/MDd)。

    • 路径:项目属性 → C/C++ → 代码生成 → 运行库。

  5. 平台一致性

    • x86 的 DLL 不能被 x64 的 .exe 加载,反之亦然。

    • 确保调用方和 DLL 的平台架构一致。

  6. 版本管理

    • 为 DLL 添加版本信息资源(.rc 文件),便于后续识别和维护。
  7. 模块定义文件(.def)

    • 除了__declspec(dllexport),还可以使用.def文件导出符号。

    • 适用场景:需要精确控制导出序号、隐藏真实函数名、导出未修饰名称等。


七、可选方案:使用模块定义文件(.def)导出

创建MyDll.def

LIBRARY MyDll EXPORTS add subtract

配置项目链接 .def 文件

  1. 右键项目 →属性。
  2. 进入链接器 → 输入 → 模块定义文件。
  3. 添加MyDll.def。

使用.def文件时,即使不在代码中写__declspec(dllexport),这些函数也会被导出。


八、总结

生成一个可用的 Visual Studio DLL,标准流程如下:

  1. 选择正确的项目类型:新建"动态链接库 (DLL)"项目,或将现有项目配置类型改为.dll。
  2. 声明导出符号:头文件中使用宏 +__declspec(dllexport)/__declspec(dllimport)。(条件编译导入还是导出)
  3. extern “C” 禁止函数重载;默认使用预编译头,所以在每个源文件里需要#include "pch.h"
  4. 定义导出宏:在 DLL 源文件中包含头文件前添加#define BUILD_MYDLL(或在项目属性中定义)。
  5. 实现函数:源文件包含头文件并完成功能实现。
  6. 生成并检查产物:确认生成.dll和.lib文件。
  7. 验证导出:使用dumpbin /exports或 Dependencies 工具检查导出符号。
  8. 调用方配置:链接.lib,运行时部署.dll。

缺少项目类型配置或符号导出声明,都会导致"编译成功但 DLL 不可用"的问题。理解这两个核心点,是掌握 VS DLL 开发的关键。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/28 2:02:34

产品经理的“产品感“到底是什么?一套可训练的判断框架

你一定听过这样的评价:“这个PM产品感不错。“但真要追问"产品感到底是什么”,大多数人只能含糊地说"就是感觉对”。 产品感不是玄学,而是一种可被拆解、可被训练的判断力。 本文把它拆成三个构成,并给出一套日常可落地…

作者头像 李华
网站建设 2026/10/28 2:02:23

aubo学习:realsense手眼标定

手眼标定到底在做什么?手眼标定需要计算相机与机械臂末端之间的固定空间关系:base_link:机械臂基座坐标系wrist3_Link:机械臂末端附近的坐标系camera_link:相机主体坐标系camera_color_optical_frame:相机彩…

作者头像 李华
网站建设 2026/10/28 2:01:55

305米网线一箱能布多少米?为什么实际施工长度往往更短?

305米网线为什么实际布线长度不足?从路径测量到施工余量计算 正文 一箱标称305米的网线,并不意味着现场一定能够完成305米有效布线。实际可完成的线路数量和总长度,取决于每条线路的实际路径、两端预留、施工绕行以及剩余线缆是否能够继续利用…

作者头像 李华
网站建设 2026/10/28 2:01:48

Agent从0开始学习

前言 本篇博客用于记录本人学习Agent的过程以及理解,如有错误,欢迎批评指正! 目录 前言 一、Agent基础概念与运行机制 1.1 基础概念 1.2 运行机制 1.2.1 Agent与LLM单次调用的区别 1.2.2 自主决策能力 1.2.3 核心循环 补充 二、核心…

作者头像 李华
网站建设 2026/10/28 2:01:47

大模型算法工程师深度报告:薪资百万?小白也能入行的收藏指南!

本文深度剖析大模型算法工程师岗位,揭示薪资达60k的真相与人才紧缺现状。文章分析岗位热度排行、薪资分布、职业发展路径,为小白及程序员提供入行可行性评估与转型建议,强调工程化能力与行业背景的重要性,并警示市场泡沫化风险。 …

作者头像 李华