加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 大数据 > 正文

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

发布时间:2020-12-15 05:24:37 所属栏目:大数据 来源:网络整理
导读:我有不止一次,建议人们使用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 with
我有不止一次,建议人们使用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"%sn",str);
SysFreeString(str);
str = NULL;

TestWideStringOutParam(str);
wprintf(L"%sn",str);
SysFreeString(str);
str = NULL;

str = TestWideString();//fails here
wprintf(L"%sn",str);
SysFreeString(str);

调用TestWideString失败,出现此错误:

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

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

[DllImport(@"pathtomydll")]
[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;

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读