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\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 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?
C++
,C#
tags also? – MajoriemajorityHRESULT
. I'm not talking about using BSTR in COM though. I'm talking about it as a convenient way to share a heap between different modules. – RollinsBSTR
is a POD type. It's just a pointer. Which other language do you know that has this problem? – RollinsSysAllocString
"magic". I still don't understand the explanation in the accepted answer. – MajoriemajorityTHandle
is not POD because you need to call a special function to make one. The issue, to the best of my understanding is a combination of automatic management ofBSTR
via the WideString compiler magic, and the semantics of return values being INOUT parameters. I have to say, it makes no sense to me that return values have IN semantics. – Rollinsprocedure TestWideStringOutParam(var str: WideString); stdcall
(note thevar
) wont work? or am I still getting it wrong? (because it does work) – MajoriemajorityOUT
but it is in factINOUT
. With the function return value, all languages that I know, other than Delphi, treat the return value as anOUT
. But Delphi treats it asINOUT
and therefore assume that the BSTR pointer is valid on entry. – RollinsBSTR
is a C type, it is a POD. The issue is not related to the type. The issue is with the mismatch in the semantics of return values between languages. Sadly Delphi is the odd one out here. – Rollins