SDK之动态链接库开发—基本概念

news/2024/12/2 19:36:11/

动态链接库(Dynamic Link Library,简称 DLL)是一种在运行时加载的库,可用于在多个应用程序之间共享代码和数据。与静态链接库相比,动态链接库的主要优劣势如下:

优势:

  • 空间效率更高,因为库代码和数据不需要在每个应用程序中都有一份副本;
  • 更新和维护更容易,因为库的所有应用程序都会受益于更新,而无需重新编译每个应用程序;
  • 可以通过更新动态链接库的版本来解决库中的错误或安全漏洞,而不必重新编译每个应用程序。

劣势:

  • 运行时加载可能会导致性能损失;
  • 库的版本控制可能会变得复杂,因为多个应用程序可能需要不同版本的库;
  • 库的依赖关系可能会变得复杂,因为库本身可能依赖于其他库。

以下是一些动态链接库开发的基本步骤:

  1. 编写代码:编写您想要共享的代码和数据,将其放入一个或多个源文件中。

  2. 编译代码:使用适当的编译器和选项将源代码编译为共享对象文件(.so 文件)或动态链接库文件(.dll 文件)。

  3. 链接代码:将库的对象文件链接到动态链接库文件中,以便它们可以在运行时加载。

  4. 测试库:编写一个或多个测试程序,以确保库可以正常工作并且在不同的应用程序中正确地共享。

  5. 安装库:将库文件和任何必需的头文件、文档和其他资源安装在系统中,以便其他开发人员可以使用它。

  6. 文档化库:为库编写文档,以便其他开发人员可以了解如何使用它以及如何与它交互。

总的来说,动态链接库是一种非常有用的工具,可以帮助开发人员在多个应用程序之间共享代码和数据,减少代码冗余,并提高开发效率。

1、静态链接库和动态链接库

用静态库的话,目标电脑上如果没有这个文件的话能正常使用程序;用动态库的话,目标电脑上必须要有这个文件才能正常使用程序,或者你可以要发布程序的时候将这个文件一起打包。

 

2、动态链接库的连接方式:隐式链接和显式加载

隐式链接就是在程序开始执行时就将DLL文件加载到应用程序当中,在链接时需要用到lib文件,才能直接通过DLL中导出的函数名来调用函数;显式加载则是在运行时加入,在代码中需要显式调用LoadLibrary()和FreeLibrary(),而且在调用dll中导出的函数时,需要使用GetProcAddress()获取想要引入的函数。

两种方式对于你的程序调用动态库时没有任何区别,只是你在编程时,步骤是不一样的:显式调用麻烦了点,但可以没有相应的lib库;隐式调用,使用起来比较简单,有函数的声明就可以了,但必须有lib库。

隐式加载默认是加载到内存中的,始终占用内存;显式加载,你加载时占用内存,释放了就不占用内存了。如果该DLL已经载入,loadlibrary只是会增加一个引用计数,相同,freelibrary也只是减少引用计数,如果引用计数为0时,DLL才从内存中移除。

显式和隐式只是对于代码编写时来说的,最后产生的可执行程序,不管是显式和隐式,都是用loadlibrary载入的。显式与隐式不是用在这些方面的,显式加载适合需要动态的选用DLL的情况。使用导出类和导出结构体的时候,隐式链接较为方便,而显式链接则很麻烦。

显式链接库的好处:在需要的时候加载动态链接库某个函数。

隐式链接的缺点:使用比较简单,在程序的其他部分可以任意使用函数,但是当程序访问十来个dll动态链接库的时候,此时如果都使用隐式链接的时候,启动此程序的时候,这十来个动态链接库都需要加载到内存,映射到内存的地址空间,这就会加大进程的启动时间,而且程序运行过程中,只是在某个条件下使用某个函数,如果使用隐式链接会造成资源的浪费。这样需要采用显式(动态加载)的方式。

3、def和extern"C"

如果DLL使用的是def文件,要删除TestDll.h文件中关键字extern"C"。

 

4、__declspec(dllimport)__declspec(dllexport)

不使用__declspec(dllimport) 也能正确编译代码,但使用 __declspec(dllimport) 使编译器可以生成更好的代码。编译器之所以能够生成更好的代码,是因为它可以确定函数是否存在于 DLL 中,这使得编译器可以生成跳过间接寻址级别的代码,而这些代码通常会出现在跨 DLL 边界的函数调用中。但是,必须使用__declspec(dllimport) 才能导入 DLL 中使用的变量。

5、动态载入(显式链接)DLL需要的三个函数

(LoadLibrary,GetProcAddress,FreeLibrary)

动态载入(显式链接) DLL方式是指在编译之前并不知道将会调用哪些 DLL 函数, 完全是在运行过程中根据需要决定应调用哪些函数。

使用方法是:用LoadLibrary 函数加载动态链接库到内存,用GetProcAddress函数动态获得 DLL 函数的入口地址。当一个 DLL 文件用LoadLibrary 显式加载后,在任何时刻均可以通过调用FreeLibrary 函数显式地从内存中把它给卸载。

动态调用使用的Windows API 函数主要有 3 个, 分别是LoadLibrary、GetProcAddress 和FreeLibrary。

(1)LoadLibrary函数

注:Delphi中还提供了SafeLoadLibrary 函数,它封装了Loadlibrary 函数,可以装载由Filename 参数指定的WindowsDLL或Linux 共享对象。它简化了DLL的装载并且使装载更加安全。

[格式]:

functionLoadLibrary(LibFileName : PChar): Thandle;

[功能]:加载由参数LibFileName 指定的 DLL 文件。

[说明]:参数LibFileName 指定了要装载的 DLL 文件名,如果LibFileName 没有包含一个路径,系统将按照:当前目录、Windows目录、Windows系统目录、包含当前任务可执行文件的目录、列在 PATH环境变量中的目录等顺序查找文件。

如果函数操作成功,将返回装载 DLL 库模块的实例句柄,否则,将返回一个错误代码,错误代码的定义如下表所示。

错误代码

  含义

  0

  系统内存不够,可执行文件被破坏或调用非法

  2

  文件没有被发现

  3

  路径没有被发现

  5

  企图动态链接一个任务错误或者有一个共享或网络保护错误

  6

  库需要为每个任务建立分离的数据段  

  8

  没有足够的内存启动应用程序  

10

  Windows  版本不正确  

  11

  可执行文件非法或不是Windows  应用程序,或在.  EXE映像中有错误  

  12

  应用程序为一个不同的操作系统设计(如  OS/2  

  13

  应用程序为  MS  DOS   4. 0  设计  

  14

  可执行文件的类型不知道  

  15

  试图装载一个实模式应用程序(为早期Windows  版本设计)

  16

  试图装载包含可写的多个数据段的可执行文件的第二个实例  

  19

  试图装载一个压缩的可执行文件(文件必须被解压后才能被装载)  

  20

  DLL  文件非法

  21

  应用程序需要  32  位扩展

假如在应用程序中用 LoadLibrary 函数装入某一个 DLL 前,其他应用程序已把该 DLL 装入内存中了,则系统将不再装入该 DLL 的另一个实例,而是使该 DLL 引用计数 1

 

2GetProcAddress 函数

[格式]

function GetProcAddress(Module:Thandle;ProcName:PChar): TfarProc;

 

[功能]:返回参数 Module 指定的模块中,由参数 ProcName 指定的过程或函数的入口地址。

 

[说明]:参数 Module 包含被调用函数的 DLL 句柄,这个值由 LoadLibrary 返回, ProcName是指向含有函数名的以 nil 结尾的字符串指针,或者可以是函数的次序值,但大多数情况下,用函数名是一种更稳妥的选择。如果该函数执行成功,则返回 DLL 中由参数 ProcName 指定的过程或函数的入口地址,否则返回 nil

 

3FreeLibrary 函数

[格式]:

procedure  FreeLibrary(Module: Thandle);

[说明]:将由参数Module 指定的 DLL 文件从内存中卸载 1 次。

[说明]:Module为 DLL 库的句柄。这个值由LoadLibrary 返回。由于 DLL 在内存中只装载一次,因此调用FreeLibrary 首先使 DLL 的引用计数减 1,如果计数减为 0 则卸载该 DLL。

[注意]:每调用一次LoadLibrary 函数就应调用一次FreeLibrary 函数,以保证不会有多余的库模块在应用程序结束后仍留在内存中,否则导致内存泄漏


http://www.ppmy.cn/news/40409.html

相关文章

砝码称重(第十二届蓝桥杯省赛第一场C++A/B/研究生组)

突然决定要参加蓝桥,已经超级久没复习C/C的我考前还是决定打几道题捡一捡C/C的语法和思路。 2023年蓝桥的题之后会出,因为 AcWing上还没有出可以测试的程序,也没把握说自己考场上做的就是对的。 题目 你有一架天平和 N 个砝码,…

0201服务注册源码解析-nacos2.x-微服务架构

文章目录1 搭建测试服务1.1 搭建服务1.2 启动测试2 nacos 服务注册原理2.1 客户端注册2.2 服务端注册结语1 搭建测试服务 1.1 搭建服务 我们基于springboot来搭建简单的订单和库存服务,通过订单服务调用库存服务,项目代码和配置在文章最后的仓库有,项目…

关于FTP文件传输协议说明,带你了解更详细的文件传输协议

FTP是文件传输协议的缩写。顾名思义,FTP用于在网络上的计算机之间传输文件。您可以使用文件传输协议在计算机帐户之间交换文件,在帐户和台式计算机之间传输文件或访问在线软件档案。但是请记住,许多文件传输协议站点已被大量使用,…

Android Jetpack 从使用到源码深耕【数据库注解Room 从实践到原理 】(二)

上文,我们通过一个简单的sqlite应用实例,引入了Room,知道了Room使用的便捷和好处。然后用Room的方式,重新实现了应用实例中的场景,在这个过程中,我们结合自己已有的知识体系,从使用代码入手,对Room的实现原理,进行了猜想和简单的验证。 Room实现原理,是否真如我们猜想…

Typescript - function 函数(箭头函数 / 参数类型与返回类型 / 可选参数与默认参数 / 剩余参数 / 函数重载)通俗易懂详细示例教程

前言 在 Typescript 中,对 JavaScript 函数进行了 “升级”,继承了基本功能的同时又增加了一些新用法(使其更加严谨)。 用一个表格,可以大致描绘出异同点。 TypeScriptJavaScript含有类型无类型箭头函数箭头函数&…

java程序解析jts的geometry类型并入PG数据库

场景 GIS开发,会有需要将jts包中的geometry类型数据存入pg(postgis扩展后)数据库的需求。 工程是springboot,mybatis作为持久层框架。 解决方案 1. pg的geometry字段对应的类型为geometry类型,比如: 2.…

面向对象三大基本特征

面向对象三大基本特征封装继承多态封装 把客观事物封装成抽象的一个类,并且类可以把自己的数据和方法只让可信的类或者对象来操作。 一个类就是一个封装的数据,以及操作这些数据的代码的逻辑实体。 在一个对象的内部,某些代码或者是某些数…

NTP8835(30W内置DSP双通道D类音频功放芯片)

数字功放是一种具有失真小、噪音低、动态范围大等特点的音频功率放大器;由工采网代理的韩国耐福旗下NTP系列专业功率放大器是ClassD功放的一个新里程碑。 NTP8835是一款高性能、高保真功率驱动集成全数字音频放大器,工作电压范围:7V&#xf…