与其他编程语言兼容的Delphi DLL

发布于 2024-09-13 14:52:38 字数 536 浏览 4 评论 0原文

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

不管怎样,最近我发现了 FastShareMem 库。它说它可以完全执行我想要的操作,而无需调用 ReleaseMemory。另一方面,FastMM 似乎做同样的事情,因为 DLL 和应用程序都使用 FastMM 作为内存管理器。这立即消除了使用 FastMM 作为通用 DLL 内存管理器的机会。正确的?

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

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

I want to build a DLL that exports functions that return a string. This DLL should work with other programming languages!!
I have found all kind of nasty solutions/hacks to this, best one is to make my function return Pchar then call another function contained in the same DLL ( let's call it ReleaseMemory) to release the memory reserved for PChar.

Anyway, recently I have discovered FastShareMem library. It says it can do exactly what I want WITHOUT calling the ReleaseMemory. On the other side FastMM seems to do the same AS LONG as both the DLL and the application uses FastMM as memory manager. This instantly kill the chance of using FastMM as memory manger for my universal DLL. Right?

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

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

〃温暖了心ぐ 2024-09-20 14:52:38

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

您找到的解决方案(返回一个指针,然后为您的 DLL 提供另一个函数来释放内存)不是 hack,也一点也不令人讨厌。这是一个非常普通的解决方案,使用您的 DLL 的人看到它时都不会眨眼。 FormatMessage API 函数使用类似的model:它为您分配一个字符串,并指定必须使用 LocalFree 释放它分配的字符串。

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

FastSharemem 提供了Delphi 内存管理器如何工作的详细解释,然后它说你可以避免所有只需在程序中使用该单元即可解决问题。但请记住我上面所说的:DLL 的使用者需要能够使用与您使用的相同的内存管理器。当 DLL 的使用者不是用 Delphi 编写时,它就不能使用 FastSharemem 单元。 FastSharemem 在同质 Delphi 环境中表现良好,但在混合环境中使用时,它会遇到与任何其他内存管理器相同的陷阱。

If you return a Delphi string, then your DLL will not work with other programming languages because no other programming languages use Delphi's string type. It doesn't matter how you allocate memory if the types aren't the same. If you're working with text, follow the model of the Windows API and use plain old character pointers.

The solution you found — to return a pointer and then provide another function for your DLL to free the memory — is not a hack and is not nasty at all. It's a perfectly ordinary solution, and nobody using your DLL will bat an eye when they see it. The FormatMessage API function uses a similar model: It allocates a string for you, and it specifies that strings it allocates must be freed with LocalFree.

It doesn't really matter what memory manager you use as long as you're consistent and consumers of your DLL can use it. One approach is to specify Windows API functions for allocating and freeing strings, such as LocalAlloc and LocalFree, or SysAllocString and SysFreeString. Another way is to never allocate anything at all — if the caller needs you to return a string, the caller provides the buffer and tells you how big it is. If the buffer is too small, then you return the required size so the caller can re-allocate the buffer and re-call the function. For an example of that, see GetLongPathName.

FastSharemem provides a long explanation of how Delphi's memory manager works, and then it says you can avoid all the trouble by simply using that unit in your program. But remember what I said above: consumers of your DLL need to be able to use the same memory manager you use. When the consumer of your DLL isn't written in Delphi, then it can't use the FastSharemem unit. FastSharemem is fine in a homogenous Delphi environment, but it suffers all the same pitfalls as any other memory manager when used in a mixed environment.

橪书 2024-09-20 14:52:38

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

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

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

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

最好的方法是让调用语言向您传递一个足够大的缓冲区,并在那里写入您的 PChar。当然,您需要有某种方法来告诉调用者缓冲区应该有多大。这就是 Windows 本身的工作方式,他们做出这样的选择是有充分理由的。

You are mixing two different scenarios:

  1. Delphi applications using Delphi DLLs
  2. Any application using Delphi DLLs

In the first scenario, unless you mix Delphi version or do something weird, the memory manager is the same, and the compiler as well. Thereby there are ways to share the memory manager, and then the compiler is able to handle allocations/deallocations properly. That's the scenario in which both FastMM and FastShareMem works - and only this one.

In the second scenario, the application and the DLL will use different memory managers, maybe very different ones, and there is usually no way to share one. In such a situation the best approach is never to return a PChar allocated inside the DLL, even if you provide a deallocation function, because you can't be sure what the calling language would do later with your PChar on its own, and if the caller has any chance to call the proper deallocation routine before the compiler/interpreter. COM works somewhat in the way you said, but it enforces memory allocation/deallocation through its own memory manager, thereby it's safe. You can't enforce it with plain DLLs, thereby it is not safe.

The best approach is let the calling language pass you a buffer large enough, and write your PChar there. Of course you need to have some way to tell the caller what size the buffer should be. That's the way Windows itself works, and there are good reasons why they made this choice.

过气美图社 2024-09-20 14:52:38

我是 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)。

希望这有帮助。

I'm the author of FastSharemem and I'd like to contribute my 2 cents' worth. Rob is right, FastSharemem assumes that the modules will all be written in Delphi. Passing data between modules in different languages can be tricky, especially with dynamic data like strings.

This is why the Windows API is often a pain to work with when dealing with complex data structures, and it's also one reason why Microsoft's COM (OLE) provides its own memory management functions and special types; the goal is binary compatibility between modules compiled from different source.

So, since Windows has already done it before, you could use one of the two ways that Windows does it. Either:

1) Expose a C-style API (PChars etc) and specify API in painstaking detail. You could either expose memory-allocation routines that clients would need to call, or have clients do the allocation. The Windows API does both at different times. Clients may also need an SDK to talk to your module conveniently, and remember to use the stdcall calling convention uniformly.

Or,

2) Use COM types and to pass data in and out. Delphi has excellent, almost transparent COM support. For example, for strings you could use COM's BSTR (WideString in Delphi).

Hope this helps.

谁许谁一生繁华 2024-09-20 14:52:38

发生的事情基本上是这样的。每一段单独编译的代码(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 一起使用,可能完全是用不同的语言编写的。只有当您控制项目的所有部分并且可以在任何地方显式设置一个相同的管理器时,共享内存管理器才是可能的。

What happens is basically this. Every piece of code compiled separately (DLL or EXE) contains its own code which allocates memory from the system and manages it, called the memory manager. Simply speaking, when that piece of coded is initialized, it allocates a big block of memory from the system. Later, when it does GetMem or allocates strings, arrays et cetera, memory manager marks parts of that big block as used. When you FreeMem/deallocate them, they're marked as unused.

Now imagine you have EXE and DLL, both with their own memory managers. EXE calls DLL procedure, DLL allocates a string (PChar), thus marking a part of it's grand memory block as used. It then returns the pointer to the EXE, which uses it and later decides to free. EXE gives the pointer to its own memory manager and asks to free it, but its not even from EXE's grand memory block! EXE's memory manager does not know how to "Free" someone else's memory.

That's why you need to call DllReleaseString(), thus returning the borrowed memory pointer to the DLL and letting DLL's own internal memory manager free it.

Now, what Sharing Memory Managers do is, they connect to each other. Memory manager in your DLL and memory manager in your EXE know how to talk to each other, and when you give DLL's memory pointer to EXE's memory manager, it understands it's from DLL and lets DLL memory manager release it. Of course that's possible only when BOTH DLL and EXE memory managers are built from the same memory manager code (or else they wouldn't recognize each other!). If your DLL memory manager is a sharing one, and your EXE memory manager is something else, DLL memory manager will not be able to "ask" EXE to release memory, and EXE memory manager will not even try (it's not sharing).

Therefore, if you want your DLL to be universal, you cannot rely on memory managers talking to each other. Your DLL might be used with EXEs or DLLs which rely on a different memory manager, maybe written in a different language altogether. Having sharing memory managers is possible only when you control all parts of your project and can explicitly setup one and the same manager everywhere.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文