Island COM support

Island COM support is a work in progress, IDispatch isn’t supported at this point, (that is, there’s no dynamic support that ends up calling into IDispatch, the interface itself could be imported and called directly). There is no marshaling going on in Island at all. Which is the reason we have these elements for each interface, since Elements interfaces aren’t compatible with COM:

  • IUnknown: Elements only interface type, this is what a class that implements IUnknown should implement. This is not compatible with a COM interface and thus should not be used in parameter signatures that pass between different types.
  • IUnknownPtr COM compatible interface type, this is cast compatible (back and forward) with an IUnknown type, and is used when passing things to and from another COM language, this should be used in exported signatures, and when calling external methods that implement IUnknown.
  • IUnknown_UID GUID for IUnknown (readonly variable).

Differences in COM support between Delphi and Island:

  • Result interface types/string types should be passed as a last out parameter in Elements.

Now we don’t want people to have to write this all themselves, so there are two aspects that can be used to make this all be generated automatically. These aspects are:

  • ComInterface This can be applied to an (elements) interface that has a Guid attribute, it will create *_Vmt, *Ptr for you for this interface.
  • ComObject This can be applied to a class that implements COM interfaces so the AddRef, Release, queryinteface methods are generated automatically (and reference counting works).

Internal types/variables:

  • IUnknown_VMT The VMT for the default IUnknown bridge implementation (readonly variable)
  • ElementsCOMInterface Record type that’s used to make reference work for Island based objects that implement a COM interface.
  • COMHelpers Helper class that can be used to create and retrieve COM

The source to all of this can be found here:

A simple example that shows this working with a Delphi/Lazarus “Host” would look like:

(Island Code, Win32 dll)

unit issuecomsupporttest;
interface

type
  [ComInterface, Guid("59A7CCB9-B724-44A3-AA7E-E5601E1654FB")] 
// ComInterface makes sure ITestPtr gets filled and the other code gets generated.
  ITest = public interface
    [CallingConvention(CallingConvention.stdcall)]
    method Sum(a, b: Integer): Integer;
    [CallingConvention(CallingConvention.stdcall)]
    method DoIt;
  end;
  ITestPtr = public record end; // We predefine this so it shows in code completion, the aspect fills it.
  
  [ComObject] // Implements Require, AddRef, QueryInterface for us, based on the interfaces with a guid.
  abc = class(ITest, IUnknown)
  [CallingConvention(CallingConvention.stdcall)]
    method Sum(a, b: Integer): Integer;
    begin
      exit a + b;
    end;
    [CallingConvention(CallingConvention.stdcall)]
    method DoIt;
    begin
      writeLn('DoIt!');
    end;
  end;

{$G+} // enable globals
[SymbolName('GetABC'),  // sets the symbol name (which will be the export name)
DLLExport, CallingConvention(CallingConvention.stdcall)] // Exports it as a PE export and sets the calling convention.
method GetABC(out co: ITestPtr);
implementation


method GetABC(out co: ITestPtr);
begin
  writeLn('in getabc');
  var ptr := ITestPtr(new abc);
  writeLn('Got ptr: '+ptr);
  co := ptr;
end;
    
end.

Delphi/Lazarus:

type
  ITest = interface(IUnknown)
    ['{59A7CCB9-B724-44A3-AA7E-E5601E1654FB}']
    function Sum(a, b: Integer): Integer;stdcall;
    procedure DoIt;stdcall;
  end;
procedure GetABC(out co: IUnknown); stdcall;
external 'ClassLibrary12.dll' name 'GetABC@4';

procedure TForm1.Button1Click(Sender: TObject);
var
  q: IUnknown;
  r: ITest;
  z: Integer;
begin
  GetABC(q);
  if Supports(q, ITest, r) then begin
    z := r.Sum(15, 12);
    ShowMessage('Sum: (15+12)=' + inttostr(z));
    r.DoIt;
  end;

end;  

Why out co: ITestPtr?

out co:IUnknown doesn’t work?

Is this empty record the required trick?

This is old. At that time native COM support was not implemented. Now it would be simply out co: ITest
The interface must be defined with the COM attribute, or inherit from IUnknown

Thank you Jose.

What if I would like to initiate a COM instance inside Island application? Any existing working Sample?

For example, inside Island application, I would like to call a Excel automation server. Do Island support importing TypeLib file?

Have you checked out https://docs.elementscompiler.com/Platforms/Island/COM/?

right now I don’t believe we do yet, no. I’ll log a feature request.

1 Like

Thanks, logged as bugs://82706

Thank you. May I push for a fast-track feature request, so a thirsty user like me can have it asap? Without TypeLib import support, Island/COM is basically moot!

i’ll discuss with Carlo tomorrow, but i cannot make any promises; we have a lot of stuff going on right now, and i don’t know what level of work would be involved in this…

Totally understand. Alternatively, is it possible to use Delphi’s import tool and convert Delphi imported type lib into Oxygene? Would the Oxygene converter work that way?

I’d probably take some manual adjustments, but it should give you base start yes over manually writing the interface definitions, yes. Let’s wait for Carlo to chime in tomorrow, as he knows this stuff better than I do…

I just realized

I can import the COM lib into Oxygene/.NET reference. Not sure if we could “export” that into something Island can use?

Nope, that generates a managed .NET dll wrapper. What we’ll need is something that generates Elements source with the interface declarations, similar how Delphi’s import would handle this.

As mentioned elsewhere, I believe, a workaround might be using Delphi’s import and adjusting the code to fit.