delphi – 为什么WideString不能用作interop的函数返回值?

前端之家收集整理的这篇文章主要介绍了delphi – 为什么WideString不能用作interop的函数返回值?前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。
我有不止一次,建议人们使用WideString类型的返回值用于互操作。

> Accessing Delphi DLL throwing ocasional exception
> ASP.NET web app calling Delphi DLL on IIS webserver,locks up when returning PChar string
> Why can Delphi DLLs use WideString without using ShareMem?

这个想法是WideString与BSTR相同。因为BSTR被分配在共享的COM堆上,所以在一个模块中分配和在不同的模块中释放是没有问题的。这是因为所有各方都同意使用同一个堆,即COM堆。

但是,似乎WideString不能用作interop的函数返回值。

考虑下面的Delphi DLL。

library WideStringTest;

uses
  ActiveX;

function TestWideString: WideString; stdcall;
begin
  Result := 'TestWideString';
end;

function TestBSTR: TBstr; stdcall;
begin
  Result := SysAllocString('TestBSTR');
end;

procedure TestWideStringOutParam(out str: WideString); stdcall;
begin
  str := 'TestWideStringOutParam';
end;

exports
  TestWideString,TestBSTR,TestWideStringOutParam;

begin
end.

和以下C代码

typedef BSTR (__stdcall *Func)();
typedef void (__stdcall *OutParam)(BSTR &pstr);

HMODULE lib = LoadLibrary(DLLNAME);
Func TestWideString = (Func) GetProcAddress(lib,"TestWideString");
Func TestBSTR = (Func) GetProcAddress(lib,"TestBSTR");
OutParam TestWideStringOutParam = (OutParam) GetProcAddress(lib,"TestWideStringOutParam");

BSTR str = TestBSTR();
wprintf(L"%s\n",str);
SysFreeString(str);
str = NULL;

TestWideStringOutParam(str);
wprintf(L"%s\n",str);
SysFreeString(str);
str = NULL;

str = TestWideString();//fails here
wprintf(L"%s\n",str);
SysFreeString(str);

调用TestWideString失败,出现此错误

Unhandled exception at 0x772015de in BSTRtest.exe: 0xC0000005: Access violation reading location 0x00000000.

类似地,如果我们尝试从C#用p / invoke调用它,我们有一个失败:

[DllImport(@"path\to\my\dll")]
[return: MarshalAs(UnmanagedType.BStr)]
static extern string TestWideString();

错误是:

An unhandled exception of type ‘System.Runtime.InteropServices.SEHException’ occurred in ConsoleApplication10.exe

Additional information: External component has thrown an exception.

通过p / invoke调用TestWideString按预期工作。

因此,使用传递引用与WideString参数和映射到BSTR似乎工作完美。但不能用于函数返回值。我已经测试了这个Delphi 5,2010和XE2,并观察相同的行为在所有版本。

执行进入Delphi并几乎立即失败。对Result的赋值变成对System._WStrAsg的调用,第一行读为:

CMP     [EAX],EDX

现在,EAX是$ 00000000,自然有一个访问冲突。

任何人都可以解释这个?我做错了什么?我不合理的期望WideString函数值是可行的BSTRs?还是只是一个Delphi缺陷?

解决方法

在常规Delphi函数中,函数return实际上是通过引用传递的参数,即使在语法上它看起来像一个“out”参数。你可以这样测试(这可能是版本相关的):
function DoNothing: IInterface;
begin
  if Assigned(Result) then
    ShowMessage('result assigned before invocation')
  else
    ShowMessage('result NOT assigned before invocation');
end;

procedure TestParameterPassingMechanismOfFunctions;
var
  X: IInterface;
begin
  X := TInterfaceObject.Create;
  X := DoNothing; 
end;

为了演示调用TestParameterPassingMechanismOfFunctions()

您的代码失败,因为Delphi和C之间的不匹配的调用约定相对于函数结果的传递机制的理解。在C中,函数返回的行为类似于语法建议:out参数。但对于Delphi它是一个var参数。

解决这个问题,请尝试:

function TestWideString: WideString; stdcall;
begin
  Pointer(Result) := nil;
  Result := 'TestWideString';
end;

猜你在找的Delphi相关文章