为什么一个WideString不能用作interop的函数返回值? [英] Why can a WideString not be used as a function return value for interop?

查看:174
本文介绍了为什么一个WideString不能用作interop的函数返回值?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有不止一次建议人们使用类型为 WideString 的返回值进行互操作。





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



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



考虑以下Delphi DLL。

  library WideStringTest; 

使用
ActiveX;

函数TestWideString:WideString;标准
begin
结果:='TestWideString';
结束

函数TestBSTR:TBstr;标准
begin
结果:= SysAllocString('TestBSTR');
结束

procedure TestWideStringOutParam(out str:WideString);标准
begin
str:='TestWideStringOutParam';
结束

export
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\\\
,str);
SysFreeString(str);
str = NULL;

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

str = TestWideString(); //这里失败
wprintf(L%s\\\
,str);
SysFreeString(str);

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


BSTRtest.exe中的0x772015de处理异常:0xC0000005:访问冲突读取位置0x00000000


< blockquote>

同样,如果我们尝试用p / invoke从C#调用这个,我们有一个失败:

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

错误是:


ConsoleApplication10.exe中出现类型为System.Runtime.InteropServices.SEHException的未处理异常。



附加信息:外部组件已抛出异常。 / p>

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



所以,使用WideString参数传递参考,并将它们映射到 BSTR 似乎工作得很好。但不是函数返回值。我在Delphi 5,2010和XE2上进行了测试,并在所有版本上观察到相同的行为。



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

 
CMP [EAX],EDX

现在,code> EAX 是 $ 00000000 ,自然就存在访问冲突。



有人可以解释一下吗我做错了吗?我希望 WideString 函数值可行 BSTR 是不合理的?或者是只是Delphi的缺陷?

解决方案

在常规Delphi函数中,函数返回实际上是一个通过引用传递的参数,甚至虽然在语法上它看起来和感觉像一个出参数。你可以这样测试(这可能取决于版本):

  function DoNothing:IInterface; 
begin
如果分配(结果)然后
ShowMessage('调用前分配的结果)
else
ShowMessage('result not assigned before invocation');
结束

procedure TestParameterPassingMechanismOfFunction;
var
X:IInterface;
begin
X:= TInterfaceObject.Create;
X:= DoNothing;
结束

演示调用 TestParameterPassingMechanismOfFunction() p>

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



要修复,请尝试:

  function TestWideString:WideString;标准
begin
指针(Result):= nil;
结果:='TestWideString';
结束


I have, on more than one occasion, advised people to use a return value of type WideString for interop purposes.

The idea is that a WideString is the same as a BSTR. Because a BSTR is allocated on the shared COM heap then it is no problem to allocate in one module and deallocate in a different module. This is because all parties have agreed to use the same heap, the COM heap.

However, it seems that WideString cannot be used as a function return value for interop.

Consider the following 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.

and the following C++ code:

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);

The call to TestWideString fails with this error:

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

Similarly, if we try to call this from C# with p/invoke, we have a failure:

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

The error is:

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

Additional information: External component has thrown an exception.

Calling TestWideString via p/invoke works as expected.

So, use pass-by-reference with WideString parameters and mapping them onto BSTR appears to work perfectly well. But not for function return values. I have tested this on Delphi 5, 2010 and XE2 and observe the same behaviour on all versions.

Execution enters the Delphi and fails almost immediately. The assignment to Result turns into a call to System._WStrAsg, the first line of which reads:

CMP     [EAX],EDX

Now, EAX is $00000000 and naturally there is an access violation.

Can anyone explain this? Am I doing something wrong? Am I unreasonable in expecting WideString function values to be viable BSTRs? Or is it just a Delphi defect?

解决方案

In regular Delphi functions, the function return is actually a parameter passed by reference, even though syntactically it looks and feels like an 'out' parameter. You can test this out like so (this may be version dependent):

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;

To demonstrate call TestParameterPassingMechanismOfFunctions()

Your code is failing because of a mismatch between Delphi and C++'s understanding of the calling convention in relation to the passing mechanism for function results. In C++ a function return acts like the syntax suggests: an out parameter. But for Delphi it is a var parameter.

To fix, try this:

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

这篇关于为什么一个WideString不能用作interop的函数返回值?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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