我不止一次建议人们使用类型的返回值WideString进行互操作.
这个想法是a WideString和a一样BSTR.因为a 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.
Run Code Online (Sandbox Code Playgroud)
和以下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);
Run Code Online (Sandbox Code Playgroud)
调用TestWideString失败并显示以下错误:
BSTRtest.exe中0x772015de处的未处理异常:0xC0000005:访问冲突读取位置0x00000000.
同样,如果我们尝试使用p/invoke从C#调用它,我们就会失败:
[DllImport(@"path\to\my\dll")]
[return: MarshalAs(UnmanagedType.BStr)]
static extern string TestWideString();
Run Code Online (Sandbox Code Playgroud)
错误是:
ConsoleApplication10.exe中发生未处理的"System.Runtime.InteropServices.SEHException"类型异常
附加信息:外部组件抛出异常.
调用TestWideString通过P/Invoke的作品如预期.
因此,使用带有WideString参数的pass-by-reference并将它们映射到BSTR看起来非常好.但不是函数返回值.我在Delphi 5,2010和XE2上测试了这一点,并在所有版本上观察到相同的行为.
执行进入Delphi并几乎立即失败.Result转换为调用的赋值System._WStrAsg,其第一行读取:
CMP [EAX],EDX
现在,EAX是$00000000自然存在访问冲突.
有谁能解释一下?难道我做错了什么?我期望WideString函数值是可行BSTR的,这是不合理的吗?或者它只是一个Delphi缺陷?
Sea*_*kin 25
在常规的Delphi函数中,函数return实际上是一个通过引用传递的参数,即使在语法上它看起来和感觉就像一个'out'参数.您可以像这样测试它(这可能取决于版本):
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;
Run Code Online (Sandbox Code Playgroud)
演示电话 TestParameterPassingMechanismOfFunctions()
您的代码失败是因为Delphi和C++对函数结果传递机制的调用约定的理解不匹配.在C++中,函数返回的行为类似于语法建议:out参数.但对于Delphi来说,这是一个var参数.
要修复,请尝试以下方法:
function TestWideString: WideString; stdcall;
begin
Pointer(Result) := nil;
Result := 'TestWideString';
end;
Run Code Online (Sandbox Code Playgroud)
kob*_*bik 19
在C#/ C++中,您需要将Result定义为outParameter,以便维护stdcall调用约定的二进制代码兼容性:
在
stdcall调用约定中,函数的结果通过CPU的EAX寄存器传递.但是,Visual C++和Delphi为这些例程生成不同的二进制代码.
Delphi代码保持不变:
function TestWideString: WideString; stdcall;
begin
Result := 'TestWideString';
end;
Run Code Online (Sandbox Code Playgroud)
C#代码:
// declaration
[DllImport(@"Test.dll")]
static extern void TestWideString([MarshalAs(UnmanagedType.BStr)] out string Result);
...
string s;
TestWideString(out s);
MessageBox.Show(s);
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
8541 次 |
| 最近记录: |