我曾多次建议人们在互操作方面使用返回值类型为WideString
。
- 访问Delphi DLL时出现偶发异常
- ASP.NET Web应用程序在调用位于IIS Web服务器上的Delphi DLL并返回PChar字符串时会锁死
- 为什么Delphi DLL可以使用WideString而不使用ShareMem?
想法是WideString
与BSTR
相同。因为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”的未处理异常
其他信息:外部组件引发了异常。
通过 p/invoke 调用 TestWideString
函数可以正常工作。
因此,使用传递引用方式来传递 WideString 参数并将它们映射到 BSTR
上似乎非常有效。但对于函数返回值则不然。我在 Delphi 5、2010 和 XE2 上进行了测试,并在所有版本上观察到相同的行为。
程序进入 Delphi 后几乎立即失败。对 Result
的赋值变成了对 System._WStrAsg
的调用,其第一行读取如下:
CMP [EAX],EDX
现在,EAX
是 $00000000
,自然会出现访问冲突。
有人能解释这是什么原因吗?我做错了什么吗?我是否不应该期望 WideString
函数返回值是可行的 BSTR
呢?还是这只是 Delphi 的缺陷?
HRESULT
的COM方法。虽然我说的不是在COM中使用BSTR,而是把它作为在不同模块之间共享堆的一种便捷方式。 - David Heffernanprocedure TestWideStringOutParam(var str: WideString); stdcall
(注意var
)不能工作吗?还是我理解错了?(因为它确实可以工作) - kobik