【问题标题】:Delphi DLL that is compatible with other programming languages与其他编程语言兼容的 Delphi DLL
【发布时间】:2010-08-11 05:30:46
【问题描述】:

我想构建一个导出返回字符串的函数的 DLL。这个 DLL 应该可以与其他编程语言一起使用!! 我已经找到了各种讨厌的解决方案/黑客,最好的方法是让我的函数返回 Pchar,然后调用同一个 DLL 中包含的另一个函数(我们称之为 ReleaseMemory)来释放为 PChar 保留的内存。

无论如何,最近我发现了 FastShareMem 库。它说它可以做我想做的事,而无需调用 ReleaseMemory。另一方面,FastMM 似乎与 LONG 一样,因为 DLL 和应用程序都使用 FastMM 作为内存管理器。这立即扼杀了使用 FastMM 作为我的通用 DLL 的内存管理器的机会。对吧?

=====================

FastShareMem (http://www.codexterity.com/fastsharemem.htm)、Delphi 7、Windows XP 32 位、Windows 7 64 位

【问题讨论】:

    标签: delphi fastmm


    【解决方案1】:

    如果您返回 Delphi string,那么您的 DLL 将无法与其他编程语言一起使用,因为没有其他编程语言使用 Delphi 的字符串类型。如果 types 不同,你如何分配内存都没有关系。如果您使用文本,请遵循 Windows API 的模型并使用普通的旧字符指针。

    您找到的解决方案 — 返回一个指针,然后为您的 DLL 提供另一个函数以释放内存 — 这不是 hack,而且一点也不讨厌。这是一个非常普通的解决方案,使用您的 DLL 的任何人在看到它时都会不屑一顾。 FormatMessage API 函数使用类似的模型:它为您分配一个字符串,并指定它分配的字符串必须用LocalFree 释放。

    只要你是一致的并且你的 DLL 的消费者可以使用它,你使用什么内存管理器并不重要。一种方法是指定用于分配和释放字符串的 Windows API 函数,例如 LocalAllocLocalFree,或 SysAllocStringSysFreeString。另一种方法是根本不分配任何东西——如果调用者需要你返回一个字符串,调用者会提供缓冲区并告诉你它有多大。如果缓冲区太小,则返回所需的大小,以便调用者可以重新分配缓冲区并重新调用函数。有关示例,请参阅GetLongPathName

    FastSharemem 对how Delphi's memory manager works 提供了很长的解释,然后它说您只需在程序中使用该单元即可避免所有麻烦。但请记住我上面所说的:DLL 的使用者需要能够使用与您相同的内存管理器。如果你的 DLL 的使用者不是用 Delphi 编写的,那么它就不能使用 FastSharemem 单元。 FastSharemem 在同构的 Delphi 环境中很好,但在混合环境中使用时,它会遇到与任何其他内存管理器相同的缺陷。

    【讨论】:

    • 对不起。当然,没有其他编程语言具有像 Delphi STRING 这样令人敬畏的类型。所以我确实需要更兼容的东西。我忘了提到我打算用返回 PChar 的函数替换我的字符串返回函数。
    • 他说他返回 PChar,然后用 DllReleaseString 释放它。所以类型很好,只是如果调用者不使用相同的内存管理器,内存管理器不会让他免于调用 DllReleaseString 的需要。由于 Delphi 内存管理器只能在 Delphi 中工作,所以用所有其他语言编写的应用程序甚至都无法使用这些内存管理器。
    • 请注意,Delphi 版本控制也可能会导致问题。 D2009+ ansistring 与 D2007- 不同,虽然我不知道这是否会导致二进制接口出现问题。
    • 你说得对,@Marco。它肯定会导致二进制接口出现问题,但它不仅仅是字符串。 FastSharemem 建议它也可以用来让你在模块之间传递 objects。虽然这是真的,但我们在这个问题中提到的网页并没有提到所有模块都需要使用相同的 Delphi 版本进行编译。不同版本编译的类的内存布局可能会有所不同。
    • 让调用者提供缓冲区。你不会错的。
    【解决方案2】:

    您正在混合两种不同的场景:

    1. 使用 Delphi DLL 的 Delphi 应用程序
    2. 任何使用 Delphi DLL 的应用程序

    在第一种情况下,除非您混合使用 Delphi 版本或做一些奇怪的事情,否则内存管理器是相同的,编译器也是如此。因此,有多种方法可以共享内存管理器,然后编译器就能够正确处理分配/解除分配。这就是 FastMM 和 FastShareMem 都适用的场景——而且只有这个场景。

    在第二种情况下,应用程序和 DLL 将使用不同的内存管理器,可能非常不同,而且通常没有办法共享一个。在这种情况下,最好的方法是永远不要返回在 DLL 中分配的 PChar,即使您提供了一个释放函数,因为您无法确定调用语言稍后会单独使用您的 PChar 做什么,以及如果调用者有机会在编译器/解释器之前调用正确的释放例程。 COM 以您所说的方式工作,但它通过自己的内存管理器强制执行内存分配/释放,因此它是安全的。你不能用普通的 DLL 来强制它,因此它是不安全的。

    最好的方法是让调用语言给你一个足够大的缓冲区,然后在那里写你的 PChar。当然,您需要有一些方法来告诉调用者缓冲区的大小。这就是 Windows 本身的工作方式,他们做出这个选择是有充分理由的。

    【讨论】:

    • +1。我真的希望我可以返回 PChar,但你的论点似乎很可靠。
    • 您可以像 COM 一样强制执行。如果您不使用 COM 函数来释放 COM 分配的内存,则会出现错误。这就是它“强制执行”事情的方式。您可以以同样的方式强制使用您的 DLL 自己的内存管理代码。从 DLL 分配和返回指针没有危险。 EXE 存在滥用内存的风险,但总是存在其他代码存在错误的风险。那不是你的问题。
    • 问题是那些不知道指针但可以传递缓冲区的语言并且这些缓冲区是自动管理的。恕我直言,让调用者管理其内存并仅操作已分配的缓冲区比分配一个并要求调用者释放它要好得多,特别是如果您不能像 COM 那样强制执行 onw 数据类型。代码可能存在错误,但某些技术比其他技术更容易引入错误。
    【解决方案3】:

    我是 FastSharemem 的作者,我想贡献我的 2 美分。 Rob 是对的,FastSharemem 假设所有的模块都是用 Delphi 编写的。在不同语言的模块之间传递数据可能很棘手,尤其是对于像字符串这样的动态数据。

    这就是为什么在处理复杂的数据结构时使用 Windows API 经常会很痛苦,这也是微软的 COM (OLE) 提供自己的内存管理功能和特殊类型的原因之一;目标是从不同来源编译的模块之间的二进制兼容性。

    因此,由于 Windows 之前已经这样做了,您可以使用 Windows 执行此操作的两种方式之一。要么:

    1) 公开 C 风格的 API(PChars 等)并详细指定 API。您可以公开客户端需要调用的内存分配例程,或者让客户端进行分配。 Windows API 在不同的时间同时执行这两种操作。客户端可能还需要一个 SDK 来方便地与您的模块通信,并记住统一使用 stdcall 调用约定。

    或者,

    2) 使用 COM 类型并传入和传出数据。 Delphi 具有出色的、几乎透明的 COM 支持。例如,对于字符串,您可以使用 COM 的 BSTR(Delphi 中的 WideString)。

    希望这会有所帮助。

    【讨论】:

      【解决方案4】:

      发生的事情基本上是这样的。每段单独编译的代码(DLL 或 EXE)都包含自己的代码,这些代码从系统分配内存并对其进行管理,称为内存管理器。简单地说,当那段代码被初始化时,它会从系统中分配一大块内存。稍后,当它执行 GetMem 或分配字符串、数组等时,内存管理器将该大块的部分标记为已使用。当你 FreeMem/deallocate 它们时,它们被标记为未使用。

      现在假设您有 EXE 和 DLL,它们都有自己的内存管理器。 EXE 调用 DLL 程序,DLL 分配一个字符串(PChar),从而将其大内存块的一部分标记为已使用。然后它返回指向 EXE 的指针,EXE 使用它并随后决定释放。 EXE 将指针指向它自己的内存管理器并要求释放它,但它甚至不是来自 EXE 的大内存块! EXE 的内存管理器不知道如何“释放”别人的内存。

      这就是为什么您需要调用 DllReleaseString(),从而将借用的内存指针返回给 DLL,并让 DLL 自己的内部内存管理器释放它。

      现在,共享内存管理器所做的是,它们相互连接。 DLL 中的内存管理器和 EXE 中的内存管理器知道如何相互通信,当您将 DLL 的内存指针提供给 EXE 的内存管理器时,它会理解它来自 DLL,并让 DLL 内存管理器释放它。当然,只有当 DLL 和 EXE 内存管理器都是从相同的内存管理器代码构建时才有可能(否则它们不会相互识别!)。如果您的 DLL 内存管理器是共享的,而您的 EXE 内存管理器是别的东西,则 DLL 内存管理器将无法“要求”EXE 释放内存,并且 EXE 内存管理器甚至不会尝试(它不是共享)。

      因此,如果您希望您的 DLL 具有通用性,则不能依赖内存管理器相互通信。您的 DLL 可能与依赖于不同内存管理器的 EXE 或 DLL 一起使用,可能完全用不同的语言编写。只有当您控制项目的所有部分并且可以在任何地方明确设置一个相同的管理器时,才有可能共享内存管理器。

      【讨论】:

      • 但就是这样。这个 Delphi 内存管理器说它可以完成(读取最后两行):codexterity.com/memmgr.htm。是我理解错了还是真的可以做到?
      • 即使一侧(EXE 或 DLL)不使用 FastSharemem,我也看不到解决方案在哪里可以工作。
      • @他自己:他们正在谈论关于在不同编程语言创建的应用程序之间共享字符串的整篇文章,最后他们提供了 5 个丑陋的解决方案。然后他们说“但有一个更简单的选择。FastSharemem 单元”。他们给人的印象是 FastSharemem 可以与 misc 一起使用。编程语言。
      • 不,Altar,那不是那篇文章在说什么。本文假设 DLL 和 EXE 都是用相同版本的 Delphi 编写的。这就是为什么第三个“丑陋的解决方案”能够建议假设 DLL 是用其他语言编写的。如果这已经是本文的前提,那么它根本不会被列为解决方案。用其他语言编写的 DLL 根本无法使用 Delphi 类,但本文演示了返回 Delphi 类的 DLL 函数。因此,这篇文章不是讨论在其他语言中使用 DLL。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-04-23
      • 2011-07-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多