Hosting the .NET runtime in a Delphi Program



Answers

Hosting the CLR yourself is not all that difficult (especially if you are just using a single AppDomain). You can use the COM based hosting APIs to start up the runtime, load assemblies, create objects and invoke methods on them.

There is a lot of info online, for example the MSDN documentation on "Hosting the Common Language Runtime".

Question

I was looking into using some .NET code from within a Delphi program, I will need to make my program extensible using .net assemblies and predefined functions (I already support regular DLLs).

After a lot of searching online, I found Managed-VCL, but I'm not ready to pay $250 for what I need, I also found some newsgroups with code that's incomplete and doesn't work.

I'm using Delphi 2007 for win32. What can I use to dynamically execute a function from an assembly with predefined parameters?

Something like:

procedure ExecAssembly(AssemblyFileName:String; Parameters: Variant);

I just want to add that I need to be able to load an arbitrary assemblies (maybe all the assemblies in a specific folder), so creating a C# wrapper may not work.




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.







Create a C# DLL That Can Be Imported in a Delphi App Using stdcall - Possible?

This is not possible in pure C#, but this is an article that shows how to add an unmanaged export table to your C# library, which can then be used in any other language. Note that the hordes of references to Blitz should not put you off - they relate to the author's own context and have nothing to do with the basic concept and how it works.

There is also a section in Brian Long's one conference paper. In a twist that you could see as somewhat ironic, Delphi.Net actually supported unmanaged exports directly despite C# not doing so. I have no idea if this is true of Delphi Prism as well.




How to use .Net assembly from Win32 without registration?

Strangely enough, I couldn't find an answer on , and there is not much on the Net, especially for Delphi. I found the solution from examples posted here. Here's what I got at the end:

function ClrCreateManagedInstance(pTypeName: PWideChar; const riid: TIID;
out ppObject): HRESULT; stdcall; external 'mscoree.dll';

procedure TMyDotNetInterop.InitDotNetAssemblyLibrary;
var
  MyIntf: IMyIntf;
hr: HRESULT;
NetClassName: WideString;
begin
//Partial assembly name works but full assembly name is preffered.
    NetClassName := 'MyCompany.MyDLLName.MyClassThatImplementsIMyIntf,
          MyCompany.MyDLLName';
    hr := ClrCreateManagedInstance(PWideChar(NetClassName), IMyIntf, MyIntf);
    //Check for error. Possible exception is EOleException with ErrorCode
    //FUSION_E_INVALID_NAME = $80131047 2148732999 : The given assembly name 
    //or codebase was invalid.
    //COR_E_TYPELOAD = $80131522 - "Could not find or load a specific type 
    //(class, enum, etc)"
    //E_NOINTERFACE = $80004002 - "Interface not supported".
    OleCheck(hr);
end;

BTW, depending on the situation, you might want to load mscoree.dll dynamically, because it might be not present on the system (XP with no .Net Framework)

EDIT: Unfortunately, this was deprecated and stopped working with .Net4 as I just found out. This leaves only two options - CLR hosting and unmanaged export. Also, debugging of .Net4 COM code is broken.




Unfortunately this can't be done (to my knowledge) without exposing a COM object, as .NET dll's are not TRULY compiled to a library that Delphi (or anything else) can load, since that is all done by the JIT compiler at runtime.




Links