为什么Delphi DLL可以使用WideString而不使用ShareMem?
-
27-10-2019 - |
题
大卫对另一个问题的回答 显示返回 WideString 的 Delphi DLL 函数。我从来没有想过如果不使用 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
:
function GetClassNameW(hWnd: HWND; lpClassName: PWideChar; nMaxCount: Integer): Integer; stdcall;
这意味着调用者提供缓冲区和最大长度。Windows DLL 写入该缓冲区时有长度限制。调用者分配和释放内存。
另一种选择是 DLL 分配内存,例如使用 LocalAlloc
, ,调用者通过调用释放内存 LocalFree
.
我的 DLL 示例中的内存分配和释放如何工作?“魔法”发生是因为结果是 WideString
(BSTR
)?为什么 Windows API 不使用如此方便的约定来声明?(是否有任何已知的 Win32 API 使用此类约定?)
编辑:
我用 C# 测试了 DLL。
呼唤 SomeFunction1
导致 AV(Attempted to read or write protected memory
).
SomeFunction2
工作正常。
[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!
这里有一个 跟进.
解决方案
A WideString
与 a 相同 BSTR
, ,这只是它的 Delphi 名称。内存分配由共享 COM 分配器处理, CoTaskMemAlloc
. 。由于各方使用相同的分配器,因此您可以安全地在一个模块中分配并在另一个模块中取消分配。
所以,你不需要使用的原因 Sharemem
问题是 Delphi 堆没有被使用。相反,使用 COM 堆。这是在进程中的所有模块之间共享的。
如果您查看 WideString 的 Delphi 实现,您将看到对以下 API 的调用: SysAllocStringLen
, SysFreeString
和 SysReAllocStringLen
. 。这些是系统提供的 BSTR
API函数.
您提到的许多 Windows API 早于 COM 的发明。此外,使用由调用者分配的固定长度缓冲区具有性能优势。也就是说,它可以分配在堆栈上而不是堆上。我还可以想象 Windows 设计者不想强迫每个进程都必须链接到 OleAut32.dll
并付出维护 COM 堆的代价。请记住,当设计大多数 Windows API 时,典型硬件的性能特征与现在有很大不同。
不使用的另一个可能原因 BSTR
更广泛的是,Windows API 是针对 C 的。并管理生命周期 BSTR
来自 C 的代码比来自 C++、C#、Delphi 等高级语言的代码要棘手得多。
然而,还有一个额外的并发症。德尔福 ABI WideString
返回值与 Microsoft 工具不兼容。你不应该使用 WideString
作为返回类型,而是通过 out
范围。欲了解更多详情,请参阅 为什么 WideString 不能用作互操作的函数返回值?