为什么 Delphi DLL 可以使用 WideString 而不使用 ShareMem? [英] Why can Delphi DLLs use WideString without using ShareMem?

查看:21
本文介绍了为什么 Delphi DLL 可以使用 WideString 而不使用 ShareMem?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

David 对另一个问题的回答显示了一个 Delphi DLL 函数返回一个 WideString.我从没想过不使用 ShareMem 就可以做到这一点.

David's answer to another question shows a Delphi DLL function returning a WideString. I never thought that was possible without the use of ShareMem.

我的测试 DLL:

function SomeFunction1: Widestring; stdcall;
begin
  Result := 'Hello';
end;

function SomeFunction2(var OutVar: Widestring): BOOL; stdcall;
begin
  OutVar := 'Hello';
  Result := True;
end;

我的调用程序:

function SomeFunction1: WideString; stdcall; external 'Test.dll';
function SomeFunction2(var OutVar: Widestring): BOOL; stdcall; external 'Test.dll';

procedure TForm1.Button1Click(Sender: TObject);
var
  W: WideString;
begin
  ShowMessage(SomeFunction1);
  SomeFunction2(W);
  ShowMessage(W);
end;

它有效,但我不明白如何.我知道的约定是 Windows API 使用的约定,例如 Windows GetClassNameW:

It works, and I don't understand how. The convention I know of is the one used by the Windows API, for example Windows GetClassNameW:

function GetClassNameW(hWnd: HWND; lpClassName: PWideChar; nMaxCount: Integer): Integer; stdcall;

意思是调用者提供缓冲区,以及最大长度.Windows DLL 以长度限制写入该缓冲区.调用者分配和释放内存.

Meaning the caller provides the buffer, and the maximum length. The Windows DLL writes to that buffer with the length limitation. The caller is allocates and deallocates the memory.

另一种选择是DLL分配内存,例如使用LocalAlloc,Caller通过调用LocalFree释放内存.

Another option is that the DLL allocate the memory for example by using LocalAlloc, and the Caller deallocates the memory by calling LocalFree.

内存分配和释放如何与我的 DLL 示例一起工作?魔法"是否因为结果是 WideString(BSTR) 而发生?为什么不使用如此方便的约定声明 Windows API?(是否有任何已知的 Win32 API 使用这种约定?)

How does the memory allocation and deallocation work with my DLL example? Does the "magic" happen because the result is WideString(BSTR)? And why aren't Windows APIs declared with such convenient convention? (Are there any known Win32 APIs that uses such convention?)

我用 C# 测试了 DLL.
调用 SomeFunction1 会导致 AV(尝试读取或写入受保护的内存).
SomeFunction2 工作正常.

I Tested the DLL with C#.
Calling SomeFunction1 causes an AV (Attempted to read or write protected memory).
SomeFunction2 works fine.

[DllImport(@"Test.dll")]
[return: MarshalAs(UnmanagedType.BStr)]
static extern string SomeFunction1();

[DllImport(@"Test.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool SomeFunction2([MarshalAs(UnmanagedType.BStr)] out string res);

...

string s;
SomeFunction2(out s);
MessageBox.Show(s); // works ok
MessageBox.Show(SomeFunction1()); // fails with AV!

这是一个跟进.

推荐答案

WideStringBSTR 相同,只是它的 Delphi 名称.内存分配由共享 COM 分配器 CoTaskMemAlloc 处理.因为所有各方都使用相同的分配器,所以您可以安全地在一个模块中分配并在另一个模块中解除分配.

A WideString is the same as a BSTR, it's just the Delphi name for it. The memory allocation is handled by the shared COM allocator, CoTaskMemAlloc. Because all parties use the same allocator you can safely allocate in one module and deallocate in another.

因此,您不需要使用 Sharemem 的原因是没有使用 Delphi 堆.而是使用 COM 堆.这在进程中的所有模块之间共享.

So, the reason you don't need to use Sharemem is that the Delphi heap is not being used. Instead the COM heap is used. And that is shared between all modules in a process.

如果您查看 WideString 的 Delphi 实现,您将看到对以下 API 的调用:SysAllocStringLen, SysFreeStringSysReAllocStringLen.这些是系统提供的BSTR API 函数.

If you look at the Delphi implementation of WideString you will see calls to the following APIs: SysAllocStringLen, SysFreeString and SysReAllocStringLen. These are the system provided BSTR API functions.

您提到的许多 Windows API 早于 COM 的发明.此外,使用由调用者分配的固定长度缓冲区具有性能优势.即它可以在堆栈上而不是堆上分配.我也可以想象,Windows 设计者不想强制每个进程都必须链接到 OleAut32.dll 并支付维护 COM 堆的代价.请记住,在设计大多数 Windows API 时,典型硬件的性能特征与现在大不相同.

Many of the Windows APIs you refer to pre-date the invention of COM. What's more, there are performance benefits to using a fixed length buffer, allocated by the caller. Namely that it can be allocated on the stack rather than a heap. I also can imagine that the Windows designers don't want to force every process to have to link to OleAut32.dll and pay the price of maintaining the COM heap. Remember that when most of the Windows API was designed, the performance characteristics of the typical hardware was very different from now.

没有更广泛地使用 BSTR 的另一个可能原因是 Windows API 是针对 C 的.从 C 管理 BSTR 的生命周期比从 C 管理要复杂得多来自 C++、C#、Delphi 等高级语言.

Another possible reason for not using BSTR more widely is that the Windows API is targeted at C. And managing the lifetime of BSTR from C is very much more tricky than from higher level languages like C++, C#, Delphi etc.

然而,还有一个额外的复杂问题.WideString 返回值的 Delphi ABI 与 Microsoft 工具不兼容.您不应该使用 WideString 作为返回类型,而是通过 out 参数返回它.有关更多详细信息,请参阅为什么不能将 WideString 用作互操作的函数返回值?

There is an extra complication however. The Delphi ABI for WideString return values is not compatible with Microsoft tools. You should not use WideString as a return type, instead return it via an out parameter. For more details see Why can a WideString not be used as a function return value for interop?

这篇关于为什么 Delphi DLL 可以使用 WideString 而不使用 ShareMem?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆