我不止一次建议人们使用 WideString
类型的返回值进行互操作 .
这个想法是 WideString
与BSTR相同 . 因为在共享COM堆上分配 BSTR
,所以在一个模块中分配并在另一个模块中解除分配是没有问题的 . 这是因为所有各方都同意使用相同的堆,即COM堆 .
但是,似乎 WideString
不能用作互操作的函数返回值 .
考虑以下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
的调用失败,出现此错误:
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”类型的异常附加信息:外部组件引发了异常 .
通过p / invoke调用 TestWideString
按预期工作 .
因此,使用带有WideString参数的pass-by-reference并将它们映射到 BSTR
似乎工作得非常好 . 但不是函数返回值 . 我在Delphi 5,2010和XE2上测试了这一点,并在所有版本上观察到相同的行为 .
执行进入Delphi并几乎立即失败 . 对 Result
的赋值变为对 System._WStrAsg
的调用,其第一行读取:
CMP [EAX],EDX
现在, EAX
是 $00000000
,并且自然存在访问冲突 .
有谁能解释一下?难道我做错了什么?我期望 WideString
函数值可行 BSTR
s是不合理的吗?或者它只是一个Delphi缺陷?
2 回答
在常规的Delphi函数中,函数return实际上是一个通过引用传递的参数,即使在语法上它看起来和感觉就像一个'out'参数 . 您可以像这样测试它(这可能取决于版本):
要演示来电
TestParameterPassingMechanismOfFunctions()
您的代码失败是因为Delphi和C对函数结果传递机制的调用约定的理解不匹配 . 在C中,函数返回的行为类似于语法建议:
out
参数 . 但对于Delphi来说,这是一个var
参数 .要修复,请尝试以下方法:
在C#/ C中,您需要将结果定义为
out
参数,以保持stdcall
调用约定的二进制代码兼容性:Returning Strings and Interface References From DLL Functions
Delphi代码保持不变:
C#代码: