Хостинг CLR в Delphi с/без JCL - пример
Может кто-нибудь, пожалуйста, напишите здесь пример того, как размещать CLR в Delphi? Я прочитал аналогичный question здесь, но я не могу использовать JCL, поскольку хочу разместить его в Delphi 5. Спасибо.
EDIT: Эта статья о размещении CLR в Fox Pro выглядит многообещающе, но я не знаю, как Доступ к clrhost.dll из Delphi.
Изменить 2: Я отказываюсь от требования Delphi 5. Теперь я пытаюсь использовать JCL с Delphi 7. Но опять же я не могу найти ни одного примера. Вот что я имею до сих пор:
Моя сборка С#:
namespace DelphiNET
{
public class NETAdder
{
public int Add3(int left)
{
return left + 3;
}
}
}
Я скомпилировал его в DelphiNET.dll
.
Теперь я хочу использовать эту сборку из 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;
Это заканчивается ошибкой: EOleError: Variant не ссылается на объект автоматизации
Я не работал с Delphi в течение долгого времени, поэтому я застрял здесь...
Решение: В просмотре COM была проблема, которая по умолчанию отсутствует. Это правильная сборка .NET:
namespace DelphiNET
{
[ComVisible(true)]
public class NETAdder
{
public int Add3(int left)
{
return left + 3;
}
}
}
Важное примечание:
При работе с .NET из Delphi важно вызвать Set8087CW($133F);
в начале вашей программы (т.е. до Application.Initialize;
). По умолчанию Delphi разрешает исключения с плавающей запятой (см. this), и CLR им не нравится. Когда я включил их, моя программа была заморожена.
Ответы
Ответ 1
Класс должен быть понятным. Что может быть не так, если у вас есть ComVisible (false) для всей сборки.
.Net-классы по умолчанию будут совместимы с IDispatch, поэтому ваш образец должен работать нормально, если класс действительно доступен..
Но сначала разделите его на минимальный минимум. Поместите ваш exe в ту же папку, что и ваша сборка .Net, и пропустите файл конфигурации и базу приложений.
Прежде чем что-то перепутается, исключение происходит здесь, верно?
ov := obj.Unwrap;
Ответ 2
Вот еще один вариант.
Это код С#. И даже если вы не хотите использовать мой неуправляемый экспорт, он все равно будет
объясните, как использовать mscoree (материал для хоста CLR), не пропуская IDispatch (IDispatch выполняется довольно медленно).
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();
}
}
}
Это объявление интерфейса Delphi:
type
IDotNetAdder = interface
['{ACEEED92-1A35-43fd-8FD8-9BA0F2D7AC31}']
function Add3(left : Integer) : Integer; safecall;
end;
Если вы используете неуправляемый экспорт, вы можете сделать это следующим образом:
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.
Когда я адаптирую образец Lars, он будет выглядеть так:
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.
В этом случае, конечно, вы можете удалить класс "UnmanagedExports" из кода С#.
Ответ 3
Здесь вы идете:
program CallDotNetFromDelphiWin32;
{$APPTYPE CONSOLE}
uses
Variants, JclDotNet, mscorlib_TLB, SysUtils;
var
Host: TJclClrHost;
Obj: OleVariant;
begin
try
Host := TJclClrHost.Create;
Host.Start;
WriteLn('CLRVersion = ' + Host.CorVersion);
Obj := Host.DefaultAppDomain.CreateInstance('DelphiNET', 'DelphiNET.NETAdder').UnWrap;
WriteLn('2 + 3 = ' + IntToStr(Obj.Add3(2)));
Host.Stop;
except
on E: Exception do
Writeln(E.Classname, ': ', E.Message);
end;
end.
Примечание.. Предполагается, что тип DelphiNET.NETAdder и метод Add3 в DelphiNet.dll ComVisible. Благодаря Robert.
Обновление
При использовании отражения вам не нужен атрибут ComVisible. Следующий пример даже работает без ComVisible.
Assm := Host.DefaultAppDomain.Load_2('NetAddr');
T := Assm.GetType_2('DelphiNET.NETAdder');
Obj := T.InvokeMember_3('ctor', BindingFlags_CreateInstance, nil, null, nil);
Params := VarArrayOf([2]);
WriteLn('2 + 3 = ' + IntToStr(T.InvokeMember_3('Add3', BindingFlags_InvokeMethod, nil, Obj, PSafeArray(VarArrayAsPSafeArray(Params)))));