为什么Delphi DLL可以使用WideString而不使用ShareMem?

为什么Delphi DLL可以使用WideString而不使用ShareMem?,delphi,dll,widestring,Delphi,Dll,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; 我的

显示返回宽字符串的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(
尝试读取或写入受保护内存
)。
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的调用:、和。这些是提供的系统

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

<> P>另一个可能的原因是C++不使用<代码> BSTR ,Windows API是以C为目标的,管理C的代码> BSTR < /C> >的生命周期比C语言、C语言、Delphi等

高级语言要复杂得多。
然而,还有一个额外的复杂性。
WideString
返回值的Delphi ABI与Microsoft工具不兼容。您不应该使用
WideString
作为返回类型,而是通过
out
参数返回它。有关更多详细信息,请参见

谢谢您的回答。看。我的理解是DLL应该是调用
CoTaskMemAlloc
。Delphi在分配WideString时调用它吗?也,什么时候调用
CoTaskMemFree
函数?
SysXXX
函数对COM分配器进行必要的调用。您的注释中的链接仅说明了一个事实,即p/invoke封送拆收器假设
string
类型的返回值已分配给
CoTaskMemAlloc
,因此它调用
CoTaskMemFree
当它完成编组时。@kobik,David,这是一个有趣的问题/讨论。。。谢谢@kobik:大多数Win32 API函数不使用
CoTaskMem…()
来管理内存。它们使用
GlobalAlloc()
LocalAlloc()
代替,这两种方法都是调用进程的本地方法,不需要跨进程边界共享。函数文档通常说明如何分配内存以及如何释放内存。