Delphi动态链接库 - 全局变量

6
我正在编写一个 DLL,为宿主应用程序提供多个函数。该应用程序动态调用 DLL,在每次函数调用后加载和释放它。
我无法控制宿主应用程序,只能在 DLL 内部工作。有没有办法将某些变量保存在内存中,以便我可以在每个函数中重复使用它们?显然,全局变量在宿主应用程序卸载 DLL 时会被清除。将 DLL 保存到文件中听起来非常混乱!
有人能建议一种分配全局变量的方法吗?
谢谢

3
你的设计存在很多问题。你考虑过主机应用程序(在终端服务器下使用时)的多个实例或者主机应用程序从多个线程调用DLL函数的可能性吗?尽量避免全局/共享状态。 - mghie
8个回答

4

警告,这是一个不太正规的方法:

你可以自己加载。

每次调用LoadLibrary都会增加一个引用计数器,FreeLibrary会将其减少。只有当计数器达到零时,DLL才会被卸载。

因此,如果第一次加载您的DLL时,您只需重新加载您的库,从而增加引用计数器。如果调用应用程序调用FreeLibrary,则引用计数器会减少,但DLL不会被卸载。

编辑:正如mghi所指出的那样,无论引用计数是否为零,进程终止时都会卸载DLL。


1
是的,但是这种方法会增加很多混乱,因为需要决定dll何时真正卸载自身,因为它不需要一直运行。然后,dll应该定期检查主机应用程序是否仍然存在,我不太喜欢这样做,但这只是我的个人看法。 - zz1433
3
不正确-如果只有一个进程使用 DLL,那么只要该进程退出,该 DLL 就会被卸载,无论参考计数是否达到 0。 - mghie

4
我认为你有两个主要选项。
  1. 提供两个版本的功能。一个是你现在拥有的版本,另一个是他们可以传递一个缓冲区(记录、等等),你可以从中读取以前的状态,并当然更新状态。将其称为函数的高性能版本。他们会想使用它。

  2. 像保存 cookie 一样(基本上就是这样),将状态保存在某个文件中。

选项1需要对主机应用程序进行修改,但对主机应用程序开发人员非常具有吸引力,选项2不需要对主机应用程序进行任何更改,但性能不会很好。
我个人不倾向于开始修改参考计数,因为假设主机应用程序卸载是有原因的,如果我是主机应用程序开发人员,那会很恼火。

谢谢 - 我觉得这是最好的选择。我宁愿保存到文件并承受性能损失,也不愿冒险去调整dll引用。也许主机应用程序开发人员会采用高性能版本。 - Crudler

2
如果您有大量的全局数据需要共享,另一种解决方案是创建一个Windows服务来“缓存”状态数据。您还需要实现一些跨进程边界的IPC,例如内存映射文件、邮箱、COM(对于此情况为单个实例)、TCP/IP等。您可能会发现这种开销比仅将状态写入文件更多,因此我只建议在状态数据量过大或每个请求到您的dll中只处理整体的部分时才采用这种方法。
对于COM方法,服务除了请求(并保留)您将用于维护状态的com对象的实例外,不必做太多事情。由于它是单个实例的com对象,所有请求都将发送到同一实例,从而允许您在请求之间保存状态。对象的请求是序列化的,因此如果您在同一台机器上有多个客户端请求数据,则可能会影响性能。

1
为推进这个想法,你可以将 DLL 设计为非常薄的一层,重定向调用到服务。工作的主要部分可以在服务中完成,并且它可以维护状态。这样做的另一个优点是 DLL 的大小会减小,并且可以加快加载 DLL 的时间(你说应用程序在每个函数调用时都加载 DLL)。 - Kieveli
@Kieveli - 很好的观点,但仍需要建立某种形式的IPC。Com(单实例)将是一个简单的解决方案,在这种情况下,服务仍然不能做任何事情,只能请求并保留对持久化对象的引用。 - skamradt
但我想我还是会坚持使用文件,如你所说,开销可能会更大,而且有更多的机会出错。谢谢! - Crudler

1

最好的方法是使用一个包含“全局变量”的类。您实例化一个对象并将其作为参数传递给dll函数。但这对您没有帮助,因为您无法更改调用应用程序。

如果您必须将全局数据保留在dll中,则解决方案是将它们写入文件。但这会严重影响性能。


0

这也可能会有所帮助

选项1: 创建一个由分页文件支持的保存变量的共享内存区域 - 如果您能够打开该共享内存区域,则您的dll先前已加载(假设“私有”共享内存名称,可能命名为process_id_yourdllname) - 如果无法打开它,则首次创建并初始化它。如果您创建它,则不必删除它 - 但是如果您打开它,则在卸载时应将其关闭。我相信该区域将在应用程序关闭时释放,因为没有其他应用程序应该具有对此特定的“私有”共享内存区域的句柄。

选项2: 创建第二个.dll,仅用于管理全局变量。 您的dll A可以加载该dll B,并且不释放它,在dll B中放置任何您需要管理全局变量的内容。当应用程序消失时,它应该消失,而且我认为您可能不需要关心涉及的(可能无用的)引用计数(因为您不会卸载dll B)。


0

如果我是你,我会在释放 dll 文件时将这些全局变量的值保存到文件 a 中,在初始化时再加载它们。我认为没有必要将 dll 的内存转储保存到磁盘上。


0
在 DLL 释放时将值写入注册表,并在 DLL 加载时从注册表中读取这些值。当读取发现某个键未设置时,请不要忘记提供默认值。
-Al.

0

我同意先前评论中关于全局状态信息的危险之处,但我想象它可能是必要的。

我提出了DR的不太干净的hack的更清洁版本,它没有像skamradt的答案那样永久的缺点:

一个非常小的应用程序:

它没有任何外观,它避免在任务栏上显示。

任务#1:加载DLL

任务#2:获取它的命令行,运行它并等待其终止。

任务#3:卸载DLL

任务#4:退出。

安装程序:

它找到主应用程序的快捷方式并修改它们,使小应用程序运行,原始位置指向的快捷方式成为第一个参数。

结果:只要主应用程序正在运行,DLL就会保留在内存中,但每次程序将其转储时都不会被卸载。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接