为什么Delphi DLL在不使用ShareMem的情况下使用WideString? [英] Why can Delphi DLLs use WideString without using ShareMem?

查看:136
本文介绍了为什么Delphi DLL在不使用ShareMem的情况下使用WideString?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

David对另一个问题的回答显示了一个返回WideString的Delphi DLL函数。我从来没有想过这是可能的,没有使用 ShareMem



我的测试DLL:

  function SomeFunction1:Widestring;标准
begin
结果:='Hello';
结束

函数SomeFunction2(var OutVar:Widestring):BOOL;标准
begin
OutVar:='Hello';
结果:= True;
结束

我的来电程序:

  function SomeFunction1:WideString;标准外部Test.dll; 
函数SomeFunction2(var OutVar:Widestring):BOOL;标准外部Test.dll;

procedure TForm1.Button1Click(Sender:TObject);
var
W:WideString;
begin
ShowMessage(SomeFunction1);
SomeFunction2(W);
ShowMessage(W);
结束

它的工作原理,我不明白如何。我知道的惯例是Windows API使用的惯例,例如Windows GetClassNameW

 函数GetClassNameW(hWnd:HWND; lpClassName:PWideChar; nMaxCount:Integer):Integer;标准

含义调用者提供缓冲区和最大长度。 Windows DLL以长度限制写入该缓冲区。调用者分配并释放内存。



另一个选项是,DLL通过使用 LocalAlloc ,Caller通过调用 LocalFree 取消分配内存。



内存分配和取消分配与我的DLL示例?是否发生魔术,因为结果是 WideString BSTR )?为什么不通过这种方便的惯例声明Windows API? (有没有任何已知的Win32 API使用这样的惯例?)






编辑: p>

我用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()); // AV失败

这是一个 followup

解决方案

A WideString 是相同于 BSTR ,它只是Delphi的名字。内存分配由共享COM分配器 CoTaskMemAlloc 处理。因为所有各方使用相同的分配器,您可以安全地分配到一个模块中并释放另一个模块。



所以,你不需要使用 Sharemem 是没有使用Delphi堆。而是使用COM堆。这个过程中的所有模块之间共享。



如果您查看WideString的Delphi实现,您将看到对以下API的调用: SysAllocStringLen ,< a href =http://msdn.microsoft.com/en-us/library/windows/desktop/ms221481%28VS.85%29.aspx> SysFreeString SysReAllocStringLen 。这些是提供的系统 BSTR API功能



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



不使用是Windows API针对于C。从C开始管理 BSTR 的生命周期要比从更高级别级别语言如C ++,C#,Delphi等。


David's answer to another question shows a Delphi DLL function returning a WideString. I never thought that was possible without the use of ShareMem.

My test DLL:

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

function SomeFunction2(var OutVar: Widestring): BOOL; stdcall;
begin
  OutVar := 'Hello';
  Result := True;
end;

My caller program:

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;

It works, and I don't understand how. The convention I know of is the one used by the Windows API, for example Windows GetClassNameW:

function GetClassNameW(hWnd: HWND; lpClassName: PWideChar; nMaxCount: Integer): Integer; stdcall;

Meaning the caller provides the buffer, and the maximum length. The Windows DLL writes to that buffer with the length limitation. The caller is allocates and deallocates the memory.

Another option is that the DLL allocate the memory for example by using LocalAlloc, and the Caller deallocates the memory by calling LocalFree.

How does the memory allocation and deallocation work with my DLL example? Does the "magic" happen because the result is WideString(BSTR)? And why aren't Windows APIs declared with such convenient convention? (Are there any known Win32 APIs that uses such convention?)


EDIT:

I Tested the DLL with C#.
Calling SomeFunction1 causes an AV (Attempted to read or write protected memory).
SomeFunction2 works fine.

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

Here is a followup.

解决方案

A WideString is the same as a BSTR, it's just the Delphi name for it. The memory allocation is handled by the shared COM allocator, CoTaskMemAlloc. Because all parties use the same allocator you can safely allocate in one module and deallocate in another.

So, the reason you don't need to use Sharemem is that the Delphi heap is not being used. Instead the COM heap is used. And that is shared between all modules in a process.

If you look at the Delphi implementation of WideString you will see calls to the following APIs: SysAllocStringLen, SysFreeString and SysReAllocStringLen. These are the system provided BSTR API functions.

Many of the Windows APIs you refer to pre-date the invention of COM. What's more, there are performance benefits to using a fixed length buffer, allocated by the caller. Namely that it can be allocated on the stack rather than a heap. I also can imagine that the Windows designers don't want to force every process to have to link to OleAut32.dll and pay the price of maintaining the COM heap. Remember that when most of the Windows API was designed, the performance characteristics of the typical hardware was very different from now.

Another possible reason for not using BSTR more widely is that the Windows API is targeted at C. And managing the lifetime of BSTR from C is very much more tricky than from higher level languages like C++, C#, Delphi etc.

这篇关于为什么Delphi DLL在不使用ShareMem的情况下使用WideString?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆