为什么 WideString 不能用作互操作的函数返回值? [英] Why can a WideString not be used as a function return value for interop?
问题描述
我不止一次建议人们使用 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;用途ActiveX;函数 TestWideString: WideString;标准调用;开始结果 := 'TestWideString';结尾;函数 TestBSTR: TBstr;标准调用;开始结果 := SysAllocString('TestBSTR');结尾;程序 TestWideStringOutParam(out str: WideString);标准调用;开始str := 'TestWideStringOutParam';结尾;出口TestWideString、TestBSTR、TestWideStringOutParam;开始结尾.
以及以下 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.
同样,如果我们尝试从 C# 中使用 p/invoke 调用它,则会失败:
[DllImport(@"path omydll")][返回:MarshalAs(UnmanagedType.BStr)]静态外部字符串 TestWideString();
错误是:
<块引用>ConsoleApplication10.exe 中发生类型为System.Runtime.InteropServices.SEHException"的未处理异常
附加信息:外部组件抛出异常.
通过 p/invoke 调用 TestWideString
可以正常工作.
因此,使用带有 WideString 参数的引用传递并将它们映射到 BSTR
似乎工作得很好.但不适用于函数返回值.我已经在 Delphi 5、2010 和 XE2 上对此进行了测试,并在所有版本上观察到了相同的行为.
执行进入Delphi并几乎立即失败.对 Result
的赋值变成了对 System._WStrAsg
的调用,它的第一行是:
现在,EAX
是 $00000000
,自然存在访问冲突.
谁能解释一下?难道我做错了什么?我期望 WideString
函数值是可行的 BSTR
是不合理的吗?还是只是 Delphi 的缺陷?
在常规的 Delphi 函数中,函数返回实际上是一个通过引用传递的参数,尽管在语法上它看起来和感觉起来像一个输出"参数.您可以像这样测试(这可能取决于版本):
function DoNothing: IInterface;开始如果已分配(结果)然后ShowMessage('调用前分配的结果')别的ShowMessage('调用前未分配结果');结尾;程序 TestParameterPassingMechanismOfFunctions;无功X:I接口;开始X := TInterfaceObject.Create;X := 什么都不做;结尾;
演示调用TestParameterPassingMechanismOfFunctions()
您的代码失败是因为 Delphi 和 C++ 对与函数结果传递机制相关的调用约定的理解不匹配.在 C++ 中,函数返回就像语法建议的那样:一个 out
参数.但对于 Delphi,它是一个 var
参数.
要修复,试试这个:
function TestWideString: WideString;标准调用;开始指针(结果):= 零;结果 := 'TestWideString';结尾;
I have, on more than one occasion, advised people to use a return value of type WideString
for interop purposes.
- Accessing Delphi DLL throwing ocasional exception
- ASP.NET web app calling Delphi DLL on IIS webserver, locks up when returning PChar string
- Why can Delphi DLLs use WideString without using ShareMem?
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
", str);
SysFreeString(str);
str = NULL;
TestWideStringOutParam(str);
wprintf(L"%s
", str);
SysFreeString(str);
str = NULL;
str = TestWideString();//fails here
wprintf(L"%s
", 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 omydll")]
[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 BSTR
s? 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 不能用作互操作的函数返回值?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!