21

David 对另一个问题的回答显示了一个返回 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 例如通过 usingLocalAlloc分配内存,而调用者通过调用LocalFree.

内存分配和释放如何与我的 DLL 示例一起使用?是否因为结果是WideStringBSTR)而发生“魔术”?为什么不使用如此方便的约定声明 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!

这是后续行动

4

1 回答 1

28

AWideString与 a 相同BSTR,只是它的 Delphi 名称。内存分配由共享 COM 分配器处理,CoTaskMemAlloc. 因为各方使用相同的分配器,您可以安全地在一个模块中分配并在另一个模块中释放。

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

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

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

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

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?

于 2012-02-17T15:35:09.630 回答