VS2017搭建动态链接库dll项目实例

2 篇文章 0 订阅
2 篇文章 0 订阅

一、前言

  • 最近接触了一些动态库相关的项目,发觉对于相关的知识点还不是特别清晰,不够系统化,所以写下这边博文串联下相关的知识,并提供简单的例子帮助理解。

二、链接

  • 既然提到了动态库,那么链接的概念肯定是绕不过,进而C++程序编译也不得不提一句。
  • 简单说,对于C++程序来说,程序由代码变为可执行程序(.exe)需要经过四步,分别是预处理、编译、汇编、链接。它们分别对应了一些操作,这些解释网上相关的资料很多,读者自行查阅。
  • 这里我们只关注最后一步链接,所谓链接,直观上的理解就是将我们代码中的函数调用串联起来,建立映射关系,从而使程序得到正确的执行。
  • 前面的编译、汇编流程后只是将我们自己写的代码变成了二进制形式,它还需要和系统组件(比如标准库、动态链接库等)结合起来,这些组件都是程序运行所必须的。[①]
  • 链接(Link)其实就是一个“打包”的过程,它将所有二进制形式的目标文件和系统组件组合成一个可执行文件。完成链接的过程也需要一个特殊的软件,叫做链接器(Linker)。
  • 随着我们学习的深入,我们编写的代码越来越多,最终需要将它们分散到多个源文件中,编译器每次只能编译一个源文件,生成一个目标文件,这个时候,链接器除了将目标文件和系统组件组合起来,还需要将编译器生成的多个目标文件组合起来。

三、静态链接与静态链接

3.1、静态链接与动态链接简介[②]

  • 链接又分为动态链接和静态链接,静态链接是由链接器在链接时将库的内容加入到可执行程序中的做法。这里的库指的是静态链接库,Windows下以.lib为后缀(library的缩写),Linux下以.a为后缀。

  • 动态链接(Dynamic Linking),把链接这个过程推迟到了运行时再进行,在可执行文件装载时或运行时,由操作系统的装载程序加载库。这里的库指的是动态链接库,Windows下以.dll为后缀(Dynamic Linking library的缩写),Linux下以.so为后缀。

  • 值得一提的是,在Windows下的动态链接也用到.lib为后缀的文件,但这里的.lib文件叫做导入库,是由.dll文件生成的,这也将是我们后文实例的内容。

  • 它包含函数的描述和在DLL中的位置,也就是说,它为存放函数实现的dll提供索引功能,为了找到dll中的函数实现的入口点。

  • 库中的函数和数据并不复制到可执行文件中,因此在应用程序的可执行文件中,存放的不是被调用的函数代码,而是DLL中所要调用的函数的内存地址,这样当一个或多个应用程序运行是再把程序代码和被调用的函数代码链接起来,从而节省了内存资源。[④]

  • 博文②中有一个简单例子可以帮助理解,使用静态库生成可执行文件后,即使删除中间产物.lib文件,exe也可以正常执行。而如果试着删除.dll之后再运行,将会提示无法找到.dll的错误。

3.2、静态链接与动态链接对比[②]

3.2.1、静态链接的优缺点

  • 优点:代码装载速度快,执行速度略比动态链接库快;只需保证在开发者的计算机中有正确的.lib文件,在以二进制形式发布程序时不需考虑在用户的计算机上.lib文件是否存在及版本问题。

  • 缺点:使用静态链接生成的可执行文件体积较大,包含相同的公共代码,造成浪费。

  • 3.2.2、动态链接的优缺点

  • 优点:生成的可执行文件较静态链接生成的可执行文件小;适用于大规模的软件开发,使开发过程独立、耦合度小,便于不同开发者和开发组织之间进行开发和测试;不同编程语言编写的程序只要按照函数调用约定就可以调用同一个DLL函数;DLL文件与EXE文件独立,只要输出接口不变(即名称、参数、返回值类型和调用约定不变),更换DLL文件不会对EXE文件造成任何影响,因而极大地提高了可维护性和可扩展性;

  • 缺点:使用动态链接库的应用程序不是自完备的,它依赖的DLL模块也要存在,如果使用载入时动态链接,程序启动时发现DLL不存在,系统将终止程序并给出错误信息;速度比静态链接慢;

四、自定义动态链接库

  • 下面将搭建一个简单的示例工程,主要流程是参考微软官方给出的演练,环境为VS2017,各位看客请先行阅读之。演练:创建和使用自己的动态链接库 (C++) | Microsoft Learn

  • 搭建时需要特别注意的一点是:在“MathClient 属性页”对话框中,将“配置”下拉列表设置为“所有配置” 。 将“平台”下拉列表设置为“所有平台” 。

  • 搭建的项目结构如下:
    请添加图片描述

  • 程序运行结果如下:

  • 请添加图片描述

  • 例子中提到的下面这段宏定义的处理决定了同一份代码文件在不同项目中的不同表现。

  • 在MathLibrary项目中,预先定义了MATHLIBRARY_EXPORTS,所以就表示把它导出,而DLL的使用者由于没有定义这个宏,所以它包含这个头文件时把你的函数看作导入的。

#ifdef MATHLIBRARY_EXPORTS
#define MATHLIBRARY_API __declspec(dllexport)
#else
#define MATHLIBRARY_API __declspec(dllimport)
#endif

请添加图片描述

  • 之后我们做两个小实验以验证我们前文的两个结论:
  • 首先我们找到MathLibrary\Debug目录,删除MathLibrary.dll文件,此时生成MathClient工程,会发现程序正常编译、链接。而如果删除了MathLibrary.lib后就无法生成报错找不到MathLibrary.lib

结论:生成(这里特指链接)时只用到了MathLibrary.lib,并不使用MathLibrary.dll。

  • 之后找到MathClient\Debug目录,尝试删除MathLibrary.dll文件,之后双击运行MathClient.exe,此时就会报出那个令无数人心碎的提示。

请添加图片描述

结论:.dll在程序运行时会被使用到,因为没有了它程序无法运行。

五、动态库的测试

  • 动态库不是完整的可执行程序,所以我们直接调试是不可以的。
    请添加图片描述

  • 想要验证程序的正确性就需要借助外部可执行程序的调用。

  • 前文的例子MathClient 是一个不错的选择,但如果调用的程序很大,单次调试就会很慢,效率也较为低下。

  • 而且如果考虑到单元测试等内容,测试的代码也会很多,所以我们考虑新建一个工程用于测试。

  • 按照下图的步骤新增MathLibraryTest工程
    请添加图片描述

  • 也同样选择一个简单的控制台应用,对于路径的选择,我们放置在MathClient 中,以示二者的包含关系。
    请添加图片描述

  • 之后也需要同样配置三个路径附加包含目录附加库目录附加依赖项,他们的值如下:

附加包含目录    ..\MathLibrary
附加库目录       ..\$(IntDir)
附加依赖项      MathLibrary.lib
  • 配置完成后,将MathLibraryTest设置为启动项目添加测试代码就可以成功运行了。

六、动态链接库的替换

  • 之后我们尝试修改MathLibrary中的逻辑,即限制其差值不能超过100000,重新生成.dll
    if ((100000 - previous_ < current_) ||
        (UINT_MAX == index_))
    {
        return false;
    }
  • 替换Release文件夹下MathClient.exe使用的dll
    请添加图片描述

  • 运行exe程序结果如下:
    请添加图片描述

  • 也就是说我们可以在不重装软件的情况下,只更新.dll文件便可以达到热修复的目的。这动辄几个G或者十几个G的游戏或者大型软件来说尤为重要。

  • 所以我们经常也会见到游戏或者软件打补丁的说法,当然如何精准对dll进行替换,方法也很多,比如做一个小型的.exe安装包等,这并不是我们讨论的重点,不多赘述。

  • 另外,一些软件/游戏的破解也有使用到替换.dll来绕过激活码验证程序这种朴实无华且高效的方式。
    请添加图片描述

七、Qt Class Library

  • 对于Qt程序来说,我们也可以将一个通用样式的组件封装成为一个.dll,并在多个项目中使用。

  • Qt的官方文档给出了使用QtCreator的一个示例[⑤],实际上整体流程和前文所述一致,不过项目类型需要更换为Qt Class Library
    请添加图片描述

  • 而且如果dll中使用到了QtGui的库,那么相应的测试工程也需要Qt GUI才可运行。

参考资料

①http://c.biancheng.net/view/1736.html

C语言 | 什么是动态链接与静态链接?_嵌入式大杂烩的博客-CSDN博客_动态链接

演练:创建和使用自己的动态链接库 (C++) | Microsoft Learn

④https://blog.csdn.net/u012999985/article/details/50429715

⑤https://wiki.qt.io/How_to_create_a_library_with_Qt_and_use_it_in_an_application

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值