Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

COM Server sends empty string which is converted to a NULL pointer [duplicate]

Tags:

c#

com

delphi

i'm define in C# this interface for a COM-Server:

[ComVisible(true)]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
[Guid("58C77969-0E7D-3778-9999-B7716E4E1111")]
public interface IMyInterface    
{
    string MyName { get; }
}

This interface is imported and implemented in a Delphi XE5 program.

The import looks like this:

IMyInterface = interface(IUnknown)
  ['{58C77969-0E7D-3778-9999-B7716E4E1111}']
  function Get_MyName (out pRetVal: WideString): HResult; stdcall;
end;

The implementation like this:

type
  TMyImpl = class(TInterfacedObject, IMyInterface)
  public
    function Get_MyName (out pRetVal: WideString): HResult; stdcall;    
 end;

 function TMyImpl.Get_MyName (out pRetVal: WideString): HResult;
 var
  s: string;
 begin
   s:=''; // empty!
   pRetVal:=s;
   result:=S_OK;
 end;

When i call that server from c# like this:

var server = new Server();
string s = server.MyName;

Then s is NULL and not an empty string as excepted.

How i can force that empty strings are transferred in COM as empty string and not replace by marshaling to NULL?

like image 926
coding Bott Avatar asked Sep 02 '26 20:09

coding Bott


2 Answers

Delphi implements empty strings as nil pointers (see System._NewUnicodeString). You can allocate an empty COM-compatible string manually:

function TMyImpl.Get_MyName(out pRetVal: WideString): HResult;
var
  BStr: TBstr;
begin
  BStr := SysAllocString('');
  if Assigned(BStr) then
  begin
    Pointer(pRetVal) := BStr;
    Result := S_OK;
  end
  else
    Result := E_FAIL;
end;

or you could create a helper function:

function EmptyWideString: WideString;
begin
  Pointer(Result) := SysAllocString('');
end;
like image 58
Ondrej Kelle Avatar answered Sep 05 '26 10:09

Ondrej Kelle


Try this on the Delphi side:

IMyInterface = interface(IUnknown)
  ['{58C77969-0E7D-3778-9999-B7716E4E1111}']
  function Get_MyName (out pRetVal: BSTR): HResult; stdcall;
end;

function TMyImpl.Get_MyName (out pRetVal: BSTR): HResult;
begin
  pRetVal := SysAllocString('');
  Result := S_OK;
end;

If you wish to handle the case where SysAllocString fails then you would write it like this:

function TMyImpl.Get_MyName (out pRetVal: BSTR): HResult;
begin
  pRetVal := SysAllocString('');
  Result := IfThen(Assigned(pRetVal), S_OK, E_FAIL);
end;

Although personally I feel that it is reasonable to draw the line at check for errors on a call to SysAllocString('').

My guess is that Delphi marshals an empty WideString as a nil pointer rather than an empty BSTR. Which in my view is a defect.

like image 43
David Heffernan Avatar answered Sep 05 '26 08:09

David Heffernan



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!