猿问

为什么不能将WideString用作互操作的函数返回值?

为什么不能将WideString用作互操作的函数返回值?

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

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

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

考虑下面的DelphiDLL。

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;beginend.

以及下面的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 = (Out
Param) GetProcAddress(lib,
                   "TestWideStringOutParam");BSTR str = TestBSTR();wprintf(L"%s\n", str);SysFreeString(str);str = NULL;TestWideSt
                   ringOutParam(str);wprintf(L"%s\n", str);SysFreeString(str);str = NULL;str = TestWideString();
                   //fails herewprintf(L"%s\n", str);SysFreeString(str);

打电话给TestWideString此错误失败:

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

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

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

错误是:

ConsoleApplication10.exe中发生了“System.Runtime.InteropServices.SEHException”类型的未处理异常

附加信息:外部组件引发异常。

呼叫TestWideStringVia/Invoke按预期工作。

因此,使用WideString参数按引用传递,并将它们映射到BSTR似乎运作得很好。但不适用于函数返回值。我已经在Delphi 5,2010和XE2上测试了这一点,并且在所有版本上都观察到了相同的行为。

执行进入Delphi,几乎立即失败。分配给Result变成打电话到System._WStrAsg,第一行内容如下:

CMP     [EAX],EDX

现在,EAX$00000000当然会有访问违规行为。

有人能解释一下吗?我做错什么了吗?我的期望是不是不合理?WideString函数值是可行的BSTR是吗?还是仅仅是德尔菲的缺陷?


qq_花开花谢_0
浏览 431回答 3
3回答

白衣非少年

在常规的Delphi函数中,函数返回实际上是一个通过引用传递的参数,即使在语法上它看起来和感觉都像是一个“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+在函数结果传递机制方面对调用约定的理解不匹配。out参数。但对德尔菲来说var参数。要解决这个问题,请尝试如下:function TestWideString: WideString; stdcall;begin   Pointer(Result) := nil;   Result := 'TestWideString';end;
随时随地看视频慕课网APP
我要回答