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;
}
}
}
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.
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.