为什么一个WideString不能用作interop的函数返回值? [英] Why can a WideString not be used as a function return value for interop?
问题描述
我有不止一次建议人们使用类型为 WideString
的返回值进行互操作。
- 访问Delphi DLL抛出ocasional异常
- ASP.NET Web应用程序在IIS webserver上调用Delphi DLL,返回PChar字符串时锁定
- 为什么Delphi DLL可以使用WideString没有使用ShareMem?
这个想法是一个 WideString
与 BSTR
。因为在共享COM堆上分配了一个 BSTR
,所以在一个模块中分配并在另一个模块中释放是没有问题的。这是因为所有方都同意使用相同的堆,COM堆。
然而,似乎 WideString
不能用作interop的函数返回值。
考虑以下Delphi DLL。
library WideStringTest;
使用
ActiveX;
函数TestWideString:WideString;标准
begin
结果:='TestWideString';
结束
函数TestBSTR:TBstr;标准
begin
结果:= SysAllocString('TestBSTR');
结束
procedure TestWideStringOutParam(out str:WideString);标准
begin
str:='TestWideStringOutParam';
结束
export
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\\\
,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
< blockquote>
同样,如果我们尝试用p / invoke从C#调用这个,我们有一个失败:
[DllImport(@path\to\my\dll)]
[return:MarshalAs(UnmanagedType.BStr)]
static extern string TestWideString();
错误是:
ConsoleApplication10.exe中出现类型为System.Runtime.InteropServices.SEHException的未处理异常。
附加信息:外部组件已抛出异常。 / p>
通过p / invoke调用
TestWideString
按预期工作。
所以,使用WideString参数传递参考,并将它们映射到
BSTR
似乎工作得很好。但不是函数返回值。我在Delphi 5,2010和XE2上进行了测试,并在所有版本上观察到相同的行为。
执行进入Delphi,几乎立即失败。对
Result
的赋值变成调用System._WStrAsg
,其第一行为:
CMP [EAX],EDX
现在,code> EAX 是
$ 00000000
,自然就存在访问冲突。
有人可以解释一下吗我做错了吗?我希望
WideString
函数值可行 BSTR 是不合理的?或者是只是Delphi的缺陷?解决方案在常规Delphi函数中,函数返回实际上是一个通过引用传递的参数,甚至虽然在语法上它看起来和感觉像一个出参数。你可以这样测试(这可能取决于版本):
function DoNothing:IInterface;
begin
如果分配(结果)然后
ShowMessage('调用前分配的结果)
else
ShowMessage('result not assigned before invocation');
结束
procedure TestParameterPassingMechanismOfFunction;
var
X:IInterface;
begin
X:= TInterfaceObject.Create;
X:= DoNothing;
结束
演示调用
TestParameterPassingMechanismOfFunction()
p>
由于Delphi和C ++对于函数结果的传递机制的调用约定的理解不匹配,您的代码失败。在C ++中,函数返回的行为类似于语法建议:
out
参数。但是对于Delphi来说,它是一个var
参数。
要修复,请尝试:
function TestWideString:WideString;标准
begin
指针(Result):= nil;
结果:='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 aBSTR
. Because aBSTR
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\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);
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\to\my\dll")] [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 toSystem._WStrAsg
, the first line of which reads:CMP [EAX],EDXNow,
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 viableBSTR
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 avar
parameter.To fix, try this:
function TestWideString: WideString; stdcall; begin Pointer(Result) := nil; Result := 'TestWideString'; end;
这篇关于为什么一个WideString不能用作interop的函数返回值?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!