我不止一次建议人们使用类型的返回值WideString
出于互操作目的。
- 访问 Delphi DLL 偶尔抛出异常 https://stackoverflow.com/questions/8533505/accessing-delphi-dll-throwing-ocasional-exception
- ASP.NET Web 应用程序在 IIS Web 服务器上调用 Delphi DLL,在返回 PChar 字符串时锁定 https://stackoverflow.com/questions/9322938/asp-net-web-app-calling-delphi-dll-on-iis-webserver-locks-up-when-returning-pch/9328272#9328272
- 为什么Delphi DLL可以使用WideString而不使用ShareMem? https://stackoverflow.com/questions/9331026/why-can-delphi-dlls-use-widestring-without-using-sharemem
这个想法是WideString
与 a 相同BSTR http://msdn.microsoft.com/en-us/library/windows/desktop/ms221105.aspx。因为一个BSTR
如果是在共享 COM 堆上分配的,那么在一个模块中分配并在另一模块中释放是没有问题的。这是因为各方都同意使用相同的堆,即 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”的未处理异常
附加信息:外部组件引发异常。
Calling TestWideString
通过 p/invoke 按预期工作。
因此,使用带 WideString 参数的引用传递并将它们映射到BSTR
看起来工作得很好。但不适用于函数返回值。我已经在 Delphi 5、2010 和 XE2 上对此进行了测试,并在所有版本上观察到相同的行为。
执行进入 Delphi 后几乎立即失败。分配给Result
变成一个电话System._WStrAsg
,第一行内容如下:
CMP [EAX],EDX
Now, EAX
is $00000000
自然会出现访问冲突。
谁能解释一下吗?难道我做错了什么?我的期待是否不合理WideString
函数值是可行的BSTR
是?或者这只是德尔福的缺陷?