为什么Delphi DLL可以在不使用ShareMem的情况下使用WideString?

时间:2012-02-17 15:32:06

标签: delphi dll widestring

David's answer to another question显示返回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示例一起使用?发生“魔法”是因为结果是WideStringBSTR)吗?为什么不使用这种方便的约定声明Windows API? (有没有使用此类约定的已知Win32 API?)


修改

我用C#测试了DLL 调用SomeFunction1会导致AV(Attempted to read or write protected memorySomeFunction2工作正常。

[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!

这是followup

1 个答案:

答案 0 :(得分:25)

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

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

如果查看WideString的Delphi实现,您将看到对以下API的调用:SysAllocStringLenSysFreeStringSysReAllocStringLen。这些是BSTR API functions提供的系统。

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

更广泛地使用BSTR的另一个可能原因是Windows API针对的是C.从C管理BSTR的生命周期比使用C ++这样的高级语言更加棘手,C#,Delphi等。

然而,有一个额外的复杂性。 WideString返回值的Delphi ABI与Microsoft工具不兼容。您不应该使用WideString作为返回类型,而是通过out参数返回它。有关详细信息,请参阅Why can a WideString not be used as a function return value for interop?

相关问题