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