我有不止一次,建议人们使用WideString类型的返回值用于互操作。
@H_502_2@> 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? @H_502_2@这个想法是WideString与
> 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? @H_502_2@这个想法是WideString与
BSTR
相同。因为BSTR被分配在共享的COM堆上,所以在一个模块中分配和在不同的模块中释放是没有问题的。这是因为所有各方都同意使用同一个堆,即COM堆。
@H_502_2@但是,似乎WideString不能用作interop的函数返回值。
@H_502_2@考虑下面的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.@H_502_2@和以下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);@H_502_2@调用TestWideString失败,出现此错误:
@H_502_2@Unhandled exception at 0x772015de in BSTRtest.exe: 0xC0000005: Access violation reading location 0x00000000.@H_502_2@类似地,如果我们尝试从C#用p / invoke调用它,我们有一个失败:
[DllImport(@"path\to\my\dll")] [return: MarshalAs(UnmanagedType.BStr)] static extern string TestWideString();@H_502_2@错误是:
@H_502_2@An unhandled exception of type ‘System.Runtime.InteropServices.SEHException’ occurred in ConsoleApplication10.exe @H_502_2@Additional information: External component has thrown an exception.@H_502_2@通过p / invoke调用TestWideString按预期工作。 @H_502_2@因此,使用传递引用与WideString参数和映射到BSTR似乎工作完美。但不能用于函数返回值。我已经测试了这个Delphi 5,2010和XE2,并观察相同的行为在所有版本。 @H_502_2@执行进入Delphi并几乎立即失败。对Result的赋值变成对System._WStrAsg的调用,第一行读为:
CMP [EAX],EDX@H_502_2@现在,EAX是$ 00000000,自然有一个访问冲突。 @H_502_2@任何人都可以解释这个?我做错了什么?我不合理的期望WideString函数值是可行的BSTRs?还是只是一个Delphi缺陷?
解决方法
在常规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;@H_502_2@为了演示调用TestParameterPassingMechanismOfFunctions() @H_502_2@您的代码失败,因为Delphi和C之间的不匹配的调用约定相对于函数结果的传递机制的理解。在C中,函数返回的行为类似于语法建议:out参数。但对于Delphi它是一个var参数。 @H_502_2@要解决这个问题,请尝试:
function TestWideString: WideString; stdcall; begin Pointer(Result) := nil; Result := 'TestWideString'; end;