2010-08-11 39 views
3

我想构建一个导出返回字符串的函数的DLL。这个DLL应该与其他编程语言一起工作! 我发现了各种令人讨厌的解决方案/黑客攻击,最好的做法是让我的函数返回Pchar,然后调用包含在同一个DLL中的另一个函数(让我们称之为ReleaseMemory)释放为PChar保留的内存。与其他编程语言兼容的Delphi DLL

无论如何,最近我发现了FastShareMem库。它表示,它可以在不调用ReleaseMemory的情况下完成我想要的操作。另一方面,FastMM似乎与DLL和应用程序使用FastMM作为内存管理器一样执行相同的操作。这立即杀死了使用FastMM作为我的通用DLL的内存管理器的机会。对?

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

FastShareMem(http://www.codexterity.com/fastsharemem.htm),Delphi 7中,Windows XP的32位,视窗7 64位

回答

6

你混合两种不同的情况:

    采用Delphi的DLL
  1. 采用Delphi的DLL

在第一种情况下,除非你混德尔福版本或做一些奇怪的任何应用程序

  • Delphi应用程序,内存管理器是一样的,编译器也是如此。因此有办法共享内存管理器,然后编译器能够正确处理分配/释放。这就是FastMM和FastShareMem都可以使用的场景 - 只有这一个。

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

    最好的方法是让调用语言通过一个足够大的缓冲区,然后在那里写你的PChar。当然,你需要有一些方法来告诉调用者缓冲区应该是多少。这就是Windows本身的工作方式,他们为什么选择这个选择有很好的理由。

  • +0

    +1。我真的希望我能回到PChar,但你的论点似乎很稳固。 – Ampere 2010-08-11 20:46:38

    +1

    你可以像COM一样强制执行。如果您不使用COM函数来释放COM分配的内存,则会出现错误。这就是它“执行”事情的方式。您可以以同样的方式强制使用DLL自己的内存管理代码。从DLL中分配和返回指针没有任何危险。 EXE存在误用内存的风险,但总是存在其他代码存在缺陷的风险。这不是你的问题。 – 2010-08-11 21:22:17

    +2

    问题是那些不了解指针的语言可以通过缓冲区,这些缓冲区是自动管理的。恕我直言,让调用者管理其内存并仅仅操作已分配的缓冲区要比分配一个并请求调用者释放它更好,尤其是如果你不能像COM那样强制你的onw数据类型。代码可能有错误,但有些技术比其他技术更容易引入错误。 – 2010-08-12 07:19:53

    8

    如果你将返回一个Delphi string,那么你的DLL不能与其他编程语言一起工作,因为没有其他编程语言使用Delphi的字符串类型。如果类型不相同,分配内存的方式并不重要。如果您使用的是文本,请遵循Windows API的模型并使用普通的旧字符指针。

    您找到的解决方案 - 返回一个指针,然后为您的DLL释放内存提供另一个函数 - 不是一个黑客,并不是讨厌的。这是一个非常普通的解决方案,没有人使用DLL会在他们看到它时眨眼睛。 FormatMessage API函数使用类似的模型:它为您分配一个字符串,并指定它分配的字符串必须用LocalFree释放。

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

    FastSharemem对how Delphi's memory manager works提供了一个长时间的解释,然后它说你只需在程序中使用该单元就可以避免所有的麻烦。但请记住我上面所说的:您的DLL的使用者需要能够使用您使用的相同的内存管理器。当你的DLL的使用者没有用Delphi编写时,它就不能使用FastSharemem单元。 FastSharemem在同质的Delphi环境中很好,但在混合环境中使用时,它与所有其他内存管理器都有相同的缺陷。

    +0

    对不起。当然,没有其他编程语言具有像Delphi STRING这样的真棒类型。所以我需要更多兼容的东西。我忘了提及我打算用PChar返回函数替换我的字符串返回函数。 – Ampere 2010-08-11 07:03:24

    +0

    他说他会返回PChar,然后用DllReleaseString释放它。所以类型没问题,只是如果调用者不使用相同的内存管理器,内存管理器不会将他从需要调用DllReleaseString的地方解救出来。由于Delphi内存管理器只能在Delphi中工作,所以用其他语言编写的应用程序甚至无法使用这些内存管理器。 – himself 2010-08-11 07:04:14

    +1

    请注意,Delphi版本控制也可能导致问题。 D2009 + ansistring与D2007不同,但我不知道是否会导致二进制接口问题。 – 2010-08-11 15:34:33

    2

    基本上会发生什么。每个单独编译的代码片段(DLL或EXE)都包含自己的代码,用于从系统分配内存并对其进行管理,称为内存管理器。简单地说,当这段代码被初始化时,它会从系统中分配一大块内存。后来,当它执行GetMem或分配字符串,数组等等时,内存管理器标记所使用的那个大块的一部分。当你FreeMem /取消分配它们时,它们被标记为未使用。

    现在想象你有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,它们依赖于不同的内存管理器,可能完全用不同的语言编写。共享内存管理器只有当你控制你的项目的所有部分,并且可以在任何地方明确地设置一个和相同的管理器时才有可能。

    +0

    但它在这里。这个Delphi内存管理器说可以完成(读取最后两行):http://www.codexterity.com/memmgr.htm 。我理解错了,或者它真的可以做到吗? – Ampere 2010-08-11 11:23:05

    +0

    +1为您的非常完整的答案! – Ampere 2010-08-11 11:23:57

    +1

    即使单侧(EXE或DLL)没有使用FastSharemem,我也没有看到它说这个解决方案能够工作。 – himself 2010-08-11 15:06:37

    3

    我是FastSharemem的作者,我想贡献我的2美分的价值。 Rob是对的,FastSharemem假设这些模块将全部用德尔福写成。在不同语言的模块之间传递数据可能会非常棘手,尤其是对于像字符串这样的动态数据。

    这就是为什么Windows API在处理复杂数据结构时经常很痛苦的原因,也是微软的COM(OLE)提供自己的内存管理功能和特殊类型的原因之一;目标是从不同来源编译的模块之间的二进制兼容性。因此,既然Windows已经完成了它,你可以使用Windows所做的两种方法之一。或者:

    1)公开一个C风格的API(PChars等)并详细说明API。您可以公开客户端需要调用的内存分配例程,或者让客户端执行分配。 Windows API在不同的时间都会执行。客户可能还需要一个SDK来方便地与您的模块交谈,并记得统一使用stdcall调用约定。

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

    希望这会有所帮助。