Bob Swart (aka Dr.Bob)
COM Objecten gebruiken in .NET

Elders op deze website staat mijn artikel over het importeren van .NET assemblies (geschreven in C#) als COM objecten in Delphi 7, alsmede verdere bevindingen met de Delphi for .NET preview command-line compiler. Dat was erg leuk om te doen, maar in dit stukje gaan we de andere kant op: bestaande COM objecten (bijvoorbeeld geschreven in Delphi) gebruiken in de .NET omgeving met de Delphi for .NET preview command-line compiler.

Delphi 7 COM Object
Als we nog geen COM Object hebben om voor dit voorbeeld te gebruiken, kunnen we er met Delphi snel eentje maken. Start Delphi en doe File | New - Other, ga nu naar de ActiveX tab van de Object Repository, kies voor het ActiveX Library icon en klik op OK. Bewaar het project in D7Win32COM.dpr
Doe nu weer File | New - Other, ga weer naar de ActiveX tab, maar kies nu voor een Com Object. In de COM Object Wizard vul je "eBob42" (of iets anders) in als Class Name en klik je vervolgens weer op OK. Binnen Delphi levert dit een interface op met een I voor de eBob42 (dus IeBob42), en een class die het interface implementeert waarvan de naam met een T begint (dus TeBob42).
We komen nu in de Type Library Editor terecht. Klik hier op het IeBob42 interface en vervolgens op de groene pijl om een nieuwe method toe te voegen. Geef deze nieuwe method bijvoorbeeld de naam Unmanaged (om even extra aan te geven dat het hier om een unmanaged methode gaat die in "gewoon" Delphi is geïmplementeerd).
Ga naar de Parameters tab van de Type Library Editor, maak het result type leeg (dat maakt er een procedure van) en geef de methode Unmanaged een argument genaamd Message, van type BSTR en [in]. Klik nu op de Refresh Implementation button en schrijf in de code editor de volgende code voor de Unmanaged methode:

  unit eBob42;
  {$WARN SYMBOL_PLATFORM OFF}
  interface
  uses
    Windows, ActiveX, Classes, ComObj, D7Win32COM_TLB, StdVcl;

  type
    TeBob42 = class(TTypedComObject, IeBob42)
    protected
      procedure Unmanaged(const Message: WideString); stdcall;
    end;

  implementation
  uses
    ComServ, Dialogs;

  procedure TeBob42.Unmanaged(const Message: WideString);
  begin
    ShowMessage(Message);
  end;

  initialization
    TTypedComObjectFactory.Create(ComServer, TeBob42, Class_eBob42,
      ciMultiInstance, tmApartment);
  end.
Bewaar deze unit nu in eBob42.pas en compileer het D7Win32COM project. Als dit lukt, kunnen we het COM Object registreren vanuit de Delphi IDE met Run | Register ActiveX Server, of vanaf de command-line met regsvr32 of TRegSvr (uit de DElphi7\bin directory).

Delphi for .NET
Nu we eenmaal een D7Win32COM.dll hebben met daarin een IeBob42 interface en een TeBob42 class, kunnen we een poging wagen om deze vanuit een .NET managed omgeving te gebruiken. Met in dit geval de Delphi for .NET preview command-line compiler.
Allereerst moeten we de Win32 DLL importeren in .NET met tlbimp (de Microsoft .NET Framework Type Library to Assembly Converter). Omdat ik echter de resulterende assembly straks in de GAC (Global Assembly Cache) wil zetten moet ik eerst een strong key aanmaken met behulp van sn (de Microsoft .NET Framework Strong Name Utility), en dat gaat als volgt:

  sn -k eBob42.snk

Hierna kunnen we tlbimp aanroepen op onze unsafe D7Win32COM.dll en daarbij aangeven dat we de keyfile eBob42.snk gebruiken voor de output file eBob42.dll. Let op dat de volgende aanroep op één lange regel moet gebeuren:

  tlbimp D7Win32COM.dll /keyfile:eBob42.snk /out:eBob42.dll

De eBob42.dll is nu een .NET assembly die gebruik maakt van de D7Win32COM.dll. Omdat we een Strong Key gebruikt hebben met eBob42.snk, kunnen we de assembly in de Global Assembly Cache (GAC) plaatsen. Dat kan in twee stappen. Eerst registreren met regasm en daarna in de GAC stoppen met gacutil:

  regasm eBob42.dll
  gacutil -i eBob42.dll

Het voordeel van de Global Assembly Cache (GAC) is dat de eBob42.dll nu niet langer in dezelfde directory hoeft te zitten als de .NET toepassing die we nu gaan maken. Lees ook mijn artikel over het importeren van .NET assemblies als COM objecten in Delphi voor meer informatie.
De Delphi for .NET preview command-line compiler kan gebruik maken van assemblies door deze mee te geven via de -LU compiler optie. Dat zou dus in dit geval met de eBob42 assembly als volgt gaan:

  dccil -LUeBob42

Helaas klaagt dccil dan dat de eBob42 package niet gevonden kan worden (en nee, dat komt niet doordat ik vergeten ben om .dll erachter te zetten).
Danny Thorpe van Borland's RAD Team R&D - een van de makers van de dccil Delphi for .NET preview command-line compiler - kwam met de verlossende tip: om de dccil compiler de met -LU opgegeven .NET assemblies te laten vinden, moeten deze assemblies in de C:\WinNT\Microsoft.NET\Framework\v1.0.3705\ directory te vinden zijn (dat is de enige plek waar dccil gaat kijken). Dus dat betekent in dit geval dat ik even handmatig eBob42.dll naar deze directory moet kopiëren. Daarkan kan dccil hem met de -LUeBob42 vlag wel vinden. Dit probleempje zal in een latere versie van dccil uiteraard opgelost zijn, zodat hij op z'n minst ook in de huidige directory zal zoeken naar opgegeven assemblies.

Om gebruik te maken van het COM Object zullen we nog een stukje code moeten schrijven. Het kortst mogelijke gebruik is als volgt:

  program DotNet;
  uses
    eBob42;

  var
    D7: eBob42.eBob42Class;

  begin
    D7 := eBob42.eBob42Class.Create;
    try
      D7.Unmanaged('Hello from Delphi for .NET')
    finally
      D7.Free
    end
  end.
Merk op dat ik de namespace eBob42 gebruik om aan te geven dat we informaties (types van classes en interfaces) uit de eBob42 assembly gebruiken. Merk tevens op dat er plotseling een class type eBob42Class staat en niet de TeBob42 die ik aan de Delphi 7 kant had aangemaakt. Dit is een feature van de tlbimp waar we mee moeten leren leven. Aan de Win32 kant zit nog steeds een TeBob42 class die verantwoordelijk is voor de implementatie van het IeBob42 interface, terwijl aan de andere kant een eBob42Class type gebruikt wordt als proxy class om het interface te gebruiken aan de .NET kant. Het interface heet in beide gevallen IeBob42.

Het compileren van de DotNet.dpr toepassing met de Delphi for .NET preview command-line compiler gaat uiteindelijk als volgt:

  dllil -LUeBob42 DotNet.dpr

En daarna kunnen we DotNet.exe uitvoeren, om hiermee vanuit een safe managed .NET toepassing (DotNet.exe) via een import assembly (eBob42.dll) naar een stukje unsafe en unmanaged code te gaan (D7Win32COM.dll).

Met alle risico's van dien (de code heet niet voor niks unsafe en unmanaged), maar ook de voordelen zoals hergebruik van bestaande COM objecten (totdat er tijd en geld is om alles helemaal opnieuw in .NET op te bouwen).

Meer Informatie
Mocht iemand nog vragen, opmerkingen of suggesties hebben, dan hoor ik die het liefst via . Wie meer wil zien betreffende Delphi en de samenwerking met .NET, zou zeker een bezoek aan mijn Delphi en .NET Clinic kunnen overwegen.


Dit artikel is eerder verschenen in SDGN Magazine #74 - oktober 2002

This webpage © 2002-2006 by webmaster drs. Robert E. Swart (aka - www.drbob42.com). All Rights Reserved.