我正在使用一个老而复杂的系统,它在几十个(有时是几百个)Win32进程之间共享内存。代码大多是非常老的Pascal,几年前被移植到 Delphi 。
(几乎)所有的代码都在一个DLL中,所有的进程都加载它。目前,我们强制该DLL的一个固定加载地址。在链接器设置中定义了图像库并禁用了ASLR。每个进程在启动时检查DLL加载地址,如果在所有进程中DLL不能加载到完全相同的地址,则整个系统拒绝工作。这当然是一个有问题的解决方案。有时客户有各种各样的第三方小工具,影响地址空间,并阻止我们的产品拥有它想要的DLL地址。
固定DLL加载地址的原因如下。我想知道是否有一种方法可以解决这个问题。
我一直在尝试介绍面向对象的编程。问题是,如果我在共享内存中示例化一个 Delphi 类,这个示例现在似乎依赖于DLL加载地址。例如,如果另一个进程试图破坏那个对象,它就会崩溃,除非这两个进程碰巧有相同的DLL地址。Delphi运行时似乎在对象示例中保存函数地址。假设它们在对象的生存期内保持固定。
一种可能的解决方案是将DLL内容复制到共享内存中,然后对DLL_PROCESS_ATTACH进行某种魔术般的欺骗,使进程运行该代码副本,而不是加载的DLL地址。我们拥有的共享内存总是Map到相同的地址。(是的,这有时也是一个问题,但很少,因为共享内存可以Map到很容易获得的高(2 GB以上)地址。)
或者有没有办法告诉 Delphi 编译器“请不要假设与这个类相关的函数的地址是固定的”?我使用的是Delphi 11.1。
1条答案
按热度按时间tsm1rwdh1#
我能够想出一个似乎效果很好的解决方案,所以让我回答我自己的问题。
问题是为了使动态调度工作,对象示例必须用类型信息“标记”。在Win32的 Delphi 中,该标记位于对象示例的前32位,并且它是DLL中的内存地址,其中有问题的类的代码位于其中。
如果您移动这个地址以匹配DLL的变量(特定于进程的)地址,动态调度的方法将正常工作。为此,您需要将这个地址与DLL的加载地址(或DLL中的任何其他引用地址)进行比较,并在创建对象时保存偏移量。
然后,在另一个行程序中呼叫对象的方法之前,先取得DLL的实际位址,加上位移,然后将总和写入对象的前32比特,以“当地语系化”对象。
现在您可以在任何流程中使用该对象,只要您先将其本地化即可:
这可以被巧妙地 Package 在一个类中。
Offset
只是一个私有的32位UInt 32。