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

kob*_*bik 21 delphi dll widestring

David对另一个问题的回答显示Delphi DLL函数返回一个WideString.我从没想过如果没有使用它是可能的ShareMem.

我的测试DLL:

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

function SomeFunction2(var OutVar: Widestring): BOOL; stdcall;
begin
  OutVar := 'Hello';
  Result := True;
end;
Run Code Online (Sandbox Code Playgroud)

我的来电计划:

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;
Run Code Online (Sandbox Code Playgroud)

它有效,我不明白怎么做.我所知道的约定是Windows API使用的约定,例如Windows GetClassNameW:

function GetClassNameW(hWnd: HWND; lpClassName: PWideChar; nMaxCount: Integer): Integer; stdcall;
Run Code Online (Sandbox Code Playgroud)

意味着调用者提供缓冲区和最大长度.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!
Run Code Online (Sandbox Code Playgroud)

这是一个后续行动.

Dav*_*nan 27

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

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

如果你看一下Delphi实现WideString的,你会看到下面的API的调用:SysAllocStringLen,SysFreeStringSysReAllocStringLen.这些是系统提供的BSTRAPI函数.

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

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

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

  • @kobik,David,这是一个有趣的问题/讨论......谢谢! (8认同)