为什么Delphi DLL可以使用WideString而不使用ShareMem?
显示返回宽字符串的Delphi DLL函数。我从未想过如果不使用为什么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; 我的
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使用的约定,例如WindowsGetClassNameW
:
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
与aBSTR
相同,它只是它的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()
代替,这两种方法都是调用进程的本地方法,不需要跨进程边界共享。函数文档通常说明如何分配内存以及如何释放内存。