Hosting CLR in Delphi with/without JCL - example

Lukas Cenovsky picture Lukas Cenovsky · Jan 12, 2010 · Viewed 8.6k times · Source

Can somebody please post here an example how to host CLR in Delphi? I have read similar question here but I cannot use JCL as I want to host it in Delphi 5. Thank you.


EDIT: This article about hosting CLR in Fox Pro looks promising but I don't know how to access clrhost.dll from Delphi.


Edit 2: I give up on Delphi 5 requirement. Now I'm trying JCL with Delphi 7. But again I am unable to find any example. Here is what I have till now:

My C# assembly:

namespace DelphiNET
{
    public class NETAdder
    {
        public int Add3(int left)
        {
            return left + 3;
        }
    }
}

I have compiled it to DelphiNET.dll.

Now I want to use this assembly from Delphi:

uses JclDotNet, mscorlib_TLB;

procedure TForm1.Button1Click(Sender: TObject);
var
  clr: TJclClrHost;
  ads: TJclClrAppDomainSetup;
  ad: TJclClrAppDomain;
  ass: TJclClrAssembly;
  obj: _ObjectHandle;
  ov: OleVariant;
begin
  clr := TJclClrHost.Create();
  clr.Start;
  ads := clr.CreateDomainSetup;
  ads.ApplicationBase := 'C:\Delhi.NET';
  ads.ConfigurationFile := 'C:\Delhi.NET\my.config';
  ad := clr.CreateAppDomain('myNET', ads);
  obj := (ad as _AppDomain).CreateInstanceFrom('DelphiNET.dll', 'DelphiNET.NETAdder');
  ov := obj.Unwrap;
  Button1.Caption := 'done ' + string(ov.Add3(5));
end;

This ends with error: EOleError: Variant does not reference an automation object

I have not worked with Delphi for a long time so I am stuck here...


Solution: There was problem in COM visibility which is not by default. This is the correct .NET assembly:

namespace DelphiNET
{
    [ComVisible(true)]
    public class NETAdder
    {
        public int Add3(int left)
        {
            return left + 3;
        }
    }
}

Important note:

When working with .NET from Delphi, it is important calling Set8087CW($133F); at the beginning of your program (i.e. before Application.Initialize;). Delphi has enabled floating point exceptions by default (see this) and the CLR doesn’t like them. When I had them enabled, my program weirdly freezed.

Answer

Robert Giesecke picture Robert Giesecke · Jan 13, 2010

Here's another option.

That's the C# Code. And even if you do not want to use my unmanaged exports, it would still explain how to use mscoree (the CLR hosting stuff) without going through IDispatch (IDispatch is pretty slow).

using System;
using System.Collections.Generic;
using System.Text;
using RGiesecke.DllExport;
using System.Runtime.InteropServices;

namespace DelphiNET
{

   [ComVisible(true)]
   [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
   [Guid("ACEEED92-1A35-43fd-8FD8-9BA0F2D7AC31")]
   public interface IDotNetAdder
   {
      int Add3(int left);
   }

   [ComVisible(true)]
   [ClassInterface(ClassInterfaceType.None)]
   public class DotNetAdder : DelphiNET.IDotNetAdder
   {
      public int Add3(int left)
      {
         return left + 3;
      }
   }

   internal static class UnmanagedExports
   {
      [DllExport("createdotnetadder", CallingConvention = System.Runtime.InteropServices.CallingConvention.StdCall)]
      static void CreateDotNetAdderInstance([MarshalAs(UnmanagedType.Interface)]out IDotNetAdder instance)
      {
         instance = new DotNetAdder();
      }
   }
}

This is the Delphi interface declaration:

type
  IDotNetAdder = interface
  ['{ACEEED92-1A35-43fd-8FD8-9BA0F2D7AC31}']
    function Add3(left : Integer) : Integer; safecall;
  end;

If you use unmanaged exports, you can do it like so:

procedure CreateDotNetAdder(out instance :  IDotNetAdder); stdcall;
  external 'DelphiNET' name 'createdotnetadder';

var
  adder : IDotNetAdder;
begin
  try
   CreateDotNetAdder(adder);
   Writeln('4 + 3 = ', adder.Add3(4));
  except
    on E: Exception do
      Writeln(E.ClassName, ': ', E.Message);
  end;
end.

When I adapt Lars' sample, it would look like so:

var
  Host: TJclClrHost;
  Obj: IDotNetAdder;
begin
  try
    Host := TJclClrHost.Create;
    Host.Start();
    WriteLn('CLRVersion = ' + Host.CorVersion);

    Obj := Host.DefaultAppDomain
               .CreateInstance('DelphiNET', 
                               'DelphiNET.DotNetAdder')
               .UnWrap() as IDotNetAdder;
    WriteLn('2 + 3 = ', Obj.Add3(2));

    Host.Stop();
  except
    on E: Exception do
      Writeln(E.Classname, ': ', E.Message);
  end;
end.

In this case you could remove the "UnmanagedExports" class from the C# code, of course.