Class not registered when calling my Delphi COM object from a WPF project in .NET

1

I get the following error when I try to call my Delphi COM object from my 32-bit WPF .NET project in Visual Studio 2019 (var x = new DelphiCOMServiceImplementation();):

Retrieving the COM class factory for component with CLSID {D448873F-EAF7-4F40-8BC7-EF9853E64A0F} failed due to the following error: 80040154 Class not registered (Exception from HRESULT: 0x80040154 (REGDB_E_CLASSNOTREG)).

On the 32-bit Delphi ActiveX project side (I am running Delphi 10.3), my COM interface looks like this:

enter image description here

enter image description here

enter image description here

enter image description here

enter image description here

The TLB's Pascal interface is generated as follows:

interface

uses Winapi.Windows, System.Classes, System.Variants, System.Win.StdVCL, Vcl.Graphics, Vcl.OleServer, Winapi.ActiveX;


// *********************************************************************//
// GUIDS declared in the TypeLibrary. Following prefixes are used:
//   Type Libraries     : LIBID_xxxx
//   CoClasses          : CLASS_xxxx
//   DISPInterfaces     : DIID_xxxx
//   Non-DISP interfaces: IID_xxxx
// *********************************************************************//
const
  // TypeLibrary Major and minor versions
  DelphiCOMServiceMajorVersion = 1;
  DelphiCOMServiceMinorVersion = 0;

  LIBID_DelphiCOMService: TGUID = '{E999851C-1E08-4C64-B82A-C3A979F96C2F}';

  IID_IDelphiCOMService: TGUID = '{D91553DB-A372-4279-AD31-9A1AE0C68F23}';
  CLASS_DelphiCOMServiceImplementation: TGUID = '{D448873F-EAF7-4F40-8BC7-EF9853E64A0F}';
type

// *********************************************************************//
// Forward declaration of types defined in TypeLibrary
// *********************************************************************//
  IDelphiCOMService = interface;
  IDelphiCOMServiceDisp = dispinterface;

// *********************************************************************//
// Declaration of CoClasses defined in Type Library
// (NOTE: Here we map each CoClass to its Default Interface)
// *********************************************************************//
  DelphiCOMServiceImplementation = IDelphiCOMService;


// *********************************************************************//
// Interface: IDelphiCOMService
// Flags:     (4416) Dual OleAutomation Dispatchable
// GUID:      {D91553DB-A372-4279-AD31-9A1AE0C68F23}
// *********************************************************************//
  IDelphiCOMService = interface(IDispatch)
    ['{D91553DB-A372-4279-AD31-9A1AE0C68F23}']
    procedure EmbedWPFWindow(Pointer: LongWord; Width: SYSINT; Height: SYSINT); safecall;
    procedure WindowResized(Width: SYSINT; Height: SYSINT); safecall;
  end;

// *********************************************************************//
// DispIntf:  IDelphiCOMServiceDisp
// Flags:     (4416) Dual OleAutomation Dispatchable
// GUID:      {D91553DB-A372-4279-AD31-9A1AE0C68F23}
// *********************************************************************//
  IDelphiCOMServiceDisp = dispinterface
    ['{D91553DB-A372-4279-AD31-9A1AE0C68F23}']
    procedure EmbedWPFWindow(Pointer: LongWord; Width: SYSINT; Height: SYSINT); dispid 201;
    procedure WindowResized(Width: SYSINT; Height: SYSINT); dispid 202;
  end;

// *********************************************************************//
// The Class CoDelphiCOMServiceImplementation provides a Create and CreateRemote method to
// create instances of the default interface IDelphiCOMService exposed by
// the CoClass DelphiCOMServiceImplementation. The functions are intended to be used by
// clients wishing to automate the CoClass objects exposed by the
// server of this typelibrary.
// *********************************************************************//
  CoDelphiCOMServiceImplementation = class
    class function Create: IDelphiCOMService;
    class function CreateRemote(const MachineName: string): IDelphiCOMService;
  end;

implementation

uses System.Win.ComObj;

class function CoDelphiCOMServiceImplementation.Create: IDelphiCOMService;
begin
  Result := CreateComObject(CLASS_DelphiCOMServiceImplementation) as IDelphiCOMService;
end;

class function CoDelphiCOMServiceImplementation.CreateRemote(const MachineName: string): IDelphiCOMService;
begin
  Result := CreateRemoteComObject(MachineName, CLASS_DelphiCOMServiceImplementation) as IDelphiCOMService;
end;

end.

My Delphi CoClass implementation is as follows (this is the class it can't seem to find as registered, so I am wondering if my implementation is correct or if I missed some detail):

unit DelphiCOMServiceUnit;

interface

uses ComObj, DelphiCOMService_TLB, Winapi.ActiveX, StdVcl;

type
  DelphiCOMServiceImplementation = class(TAutoObject, IDelphiCOMService)
  public
    procedure EmbedWPFWindow(Pointer: LongWord; Width: SYSINT; Height: SYSINT); safecall;
    procedure WindowResized(Width: SYSINT; Height: SYSINT); safecall;
  end;

implementation

procedure DelphiCOMServiceImplementation.EmbedWPFWindow(Pointer: LongWord; Width: SYSINT; Height: SYSINT); safecall;
begin

end;

procedure DelphiCOMServiceImplementation.WindowResized(Width: SYSINT; Height: SYSINT); safecall;
begin

end;

end.

I have tried both 32-bit and 64-bit versions of regsvr32 without any issues (it always registers successfully):

regsvr32 DelphiCOMService.dll

C:\Windows\SysWOW64\regsvr32.exe DelphiCOMService.dll

I have tried running dumpbin /exports on DelphiCOMService.dll, and it looks okay to me:

Dump of file DelphiCOMService.dll

File Type: DLL

Section contains the following exports for DelphiCOMService.dll

00000000 characteristics
       0 time date stamp
    0.00 version
       1 ordinal base
       8 number of functions
       8 number of names

ordinal hint RVA      name

      7    0 00101BE0 DllCanUnloadNow
      8    1 00101B98 DllGetClassObject
      4    2 00101C90 DllInstall
      6    3 00101C08 DllRegisterServer
      5    4 00101C4C DllUnregisterServer
      3    5 00065514 TMethodImplementationIntercept
      2    6 00010890 __dbk_fcall_wrapper
      1    7 00214640 dbkFCallWrapperAddr

Summary

    7000 .bss
    6000 .data
  997000 .debug
    1000 .didata
    1000 .edata
    4000 .idata
    2000 .itext
    1000 .rdata
   30000 .reloc
    7000 .rsrc
  208000 .text

Could anyone tell me what I can do to get C# to be able to call into my Delphi COM object's function? Did I miss something when I created that unit which houses the DelphiCOMServiceImplementation type, or miss a detail when implementing it, and thus, that causes the CoClass to not be found as a registered object? I tried to find the COM wizards that I keep reading about in my version of Delphi in order to create a class using them and comparing what works to what doesn’t, but I couldn’t find any wizard in my version of Delphi for creating COM classes.

c#
.net
wpf
delphi
pascal
asked on Stack Overflow Nov 16, 2019 by Alexandru • edited Nov 16, 2019 by Alexandru

1 Answer

1

Following this guide and with the help of MartynA, I had a few issues:

Firstly, nothing was registering my DelphiCOMServiceImplementation class, even though it had the right name and it implemented the right interface, this class needs to tell COM that it exists. To do this, you add an initialization section:

initialization

TComObjectFactory.Create(ComServer, DelphiCOMServiceImplementation, StringToGUID('{D448873F-EAF7-4F40-8BC7-EF9853E64A0F}'), 'DelphiCOMServiceImplementation', '', ciMultiInstance, tmApartment);

Secondly, I believe that the above TComObjectFactory.Create function only works on TComObject derived types, so I changed the class DelphiCOMServiceImplementation inherits as follows (it now derives itself from TComObject):

DelphiCOMServiceImplementation = class(TComObject, IDelphiCOMService)

Thirdly, my interface was originally using IDispatch in the *.ridl designer. This was totally over-kill and I suspect I would have needed to do some extra registration steps in order to make it work with this interface (such as reverting back to TAutoObject and using TAutoObjectFactory.Create(…), but I don't plan to use automation), so since I don't need it, I switched my interface to IUnknown (as shown in the image below) and then did the old Save All, Refresh Implementation, Register Type Library, Save As Type Library File, Build (right clicking on the solution), Run -> ActiveX Server -> Register:

enter image description here

Now it just works like a charm! My WPF project calls into my functions without any worries. And finally, just to bring things full circle, here is the complete Delphi CoClass implementation:

unit DelphiCOMServiceUnit;

interface

uses ComObj, ComServ, DelphiCOMService_TLB, Winapi.ActiveX, StdVcl;

type
  DelphiCOMServiceImplementation = class(TComObject, IDelphiCOMService)
  protected
    procedure EmbedWPFWindow(Pointer: LongWord; Width: SYSINT; Height: SYSINT); safecall;
    procedure WindowResized(Width: SYSINT; Height: SYSINT); safecall;
  end;

implementation

procedure DelphiCOMServiceImplementation.EmbedWPFWindow(Pointer: LongWord; Width: SYSINT; Height: SYSINT); safecall;
begin

end;

procedure DelphiCOMServiceImplementation.WindowResized(Width: SYSINT; Height: SYSINT); safecall;
begin

end;

initialization

TComObjectFactory.Create(ComServer, DelphiCOMServiceImplementation, StringToGUID('{D448873F-EAF7-4F40-8BC7-EF9853E64A0F}'), 'DelphiCOMServiceImplementation', '', ciMultiInstance, tmApartment);

end.
answered on Stack Overflow Nov 16, 2019 by Alexandru • edited Nov 19, 2019 by Alexandru

User contributions licensed under CC BY-SA 3.0