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

标签 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 示例一起工作? “魔法”发生是因为结果是 WideString(BSTR) 吗?为什么 Windows API 不使用如此方便的约定来声明? (是否有任何已知的 Win32 API 使用此类约定?)

<小时/>

编辑:

我用 C# 测试了 DLL。
调用 SomeFunction1 会导致 AV(尝试读取或写入 protected 内存)。
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!

这是一个followup .

最佳答案

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

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

如果您查看 WideString 的 Delphi 实现,您将看到对以下 API 的调用:SysAllocStringLen , SysFreeStringSysReAllocStringLen 。这些是系统提供的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?

关于delphi - 为什么Delphi DLL可以使用WideString而不使用ShareMem?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/9331026/

相关文章:

c# - COM Interop,客户端在进程外 COM 中找不到接口(interface)

c# - 在生产数据库上部署应用程序时无法加载文件或程序集“System.Data”错误

c++ - 如何将原始 MBCS 字符串 (SHIFT-JIS) 从 Windows 转换为 Linux 上的 UTF-8

c++ - 如果没有正确删除,为什么 wstring::c_str 不会导致内存泄漏

c# - 找不到从我的 Windows 服务中删除/重新启动内存中的 DLL 状态的方法

delphi - 让 C++Builder/Delphi 在启动时创建 TForms,还是手动创建?

delphi - 如何同步2个TTreeview的滚动?

delphi - 是否可以对字符串类型使用两个记录助手?

c# - 如何找到无法加载 "A procedure imported by ' xxx.dll 的源。”异常?