2010-01-12 13 views
18

Czy ktoś może zamieścić tutaj przykład jak hostować CLR w Delphi? Przeczytałem podobne tutaj question, ale nie mogę używać JCL, ponieważ chcę go hostować w Delphi 5. Dziękuję.Hostowanie CLR w Delphi za pomocą/bez JCL - przykład


EDIT: Ten article o hosting CLR w Fox Pro wygląda obiecująco, ale nie wiem, jak uzyskać dostęp do clrhost.dll z Delphi.


Edit 2: daję się na Delphi 5 wymogu. Teraz próbuję JCL z Delphi 7. Ale znowu nie mogę znaleźć żadnego przykładu. Oto co mam do tej pory:

Mój zespół C#:

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

Mam przygotował go do DelphiNET.dll.

Teraz chcę używać tego zestawu z 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; 

To kończy się błędem: EOleError: Variant nie odwoływać się do obiektu automatyzacji

ja nie pracowałem z Delphi za długa czas więc siedzę tutaj ...


Rozwiązanie: T tutaj był problem z widocznością COM, która nie jest domyślnie. Jest to prawidłowa NET:

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

Ważna uwaga:

Podczas pracy z .NET z Delphi, ważne jest wywołanie Set8087CW($133F); na początku programu (czyli przed Application.Initialize;). Delphi domyślnie włączył wyjątki zmiennoprzecinkowe (zobacz this), a CLR ich nie lubi. Kiedy miałem je włączone, mój program dziwnie się zawiesił.

+2

Dlaczego nie możesz używać JCL w Delphi 5? A może po drobnych zmianach? –

+0

JclDotNet.pas został opracowany w Delphi 6 i nie wygląda na niewielkie zmiany, aby można go było używać w Delphi 5. –

+0

Czy rozważałeś zarządzanie VCL? –

Odpowiedz

8

Klasa musi być niewidoczna. Które może nie być prawdą, jeśli masz ComVisible (fałsz) dla całego zespołu.

klas .NET będzie kompatybilny IDispatch domyślnie, więc próbka powinna działać dobrze, jeśli klasa jest naprawdę comvisible ..

Ale rozebrać go do minimum pierwszy. Umieść plik exe w tym samym folderze co zestaw .Net i pomiń plik konfiguracyjny i bazę aplikacji.

Zanim coś się pomyli, wyjątek się tu wydarzy, prawda?

ov := obj.Unwrap; 
+0

+1 za sugerowanie używania COM do wywoływania metod .Net, ale nie jest to tym, o co prosił. Może nie być w stanie dokonać żadnych zmian w zespole. Jak pokazano w mojej odpowiedzi można to zrobić bez COM i bez tworzenia rzeczy COM widoczne. –

+1

nie używając COM, ale oznaczając klasę jako ComVisible. Twoja próbka polega na infrastrukturze COM/Interop firmy CLR, aby uzyskać instancję .Net skonfigurowaną jako IDispatch. Działa to tylko wtedy, gdy klasa jest ComVisible (która jest domyślna, ale może być wyłączona dla zespołu). –

+0

Stoję poprawione. Domyślnie nie oczekiwałem, że to będzie prawda. Mój przykład rzeczywiście kończy się niepowodzeniem, jeśli typ nie jest widoczny w COM. Ogranicza to znacznie wykorzystanie IMHO. –

6

Proszę bardzo:

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. 

Uwaga: Zakłada się, że typ DelphiNET.NETAdder i metoda ADD3 w DelphiNet.dll to ComVisible. Dzięki Robert.

Aktualizacja:

Przy użyciu odbicia nie trzeba atrybut ComVisible. Następny przykład działa nawet bez 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))))); 
+1

To działa, ponieważ nie potrzebuje IDispatch lub IUnknown, a więc nie ma COM/Interop. Jest to bardzo dobry przykład w przypadkach, gdy musisz zajmować się lekcjami, które nie są niewidoczne. Tak więc: +1 –

+0

+1 Zaktualizowany przykład (bez ComVisible) jest świetny! Aby go uruchomić, potrzebne są następujące elementy: używa ActiveX; var Assm: _Asembling; var T: _Type; var Params: Variant; –

+0

Z pewnością jednak będzie dużo wolniej. Muszę być na spotkaniu 3 min. temu, ale dam ci kolejną próbkę (bez IDispatch) później. –

12

Oto kolejna opcja.

To jest kod C#. I nawet jeśli nie chcesz używać my unmanaged exports, to nadal będzie to opisywać w jaki sposób używać mscoree (rzeczy do przechowywania CLR) bez przechodzenia przez IDispatch (IDispatch jest dość wolny).

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(); 
     } 
    } 
} 

To jest deklaracja interfejsu Delphi:

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

Jeśli używasz eksportu niezarządzanymi, można to zrobić tak:

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. 

Kiedy dostosować próbkę Larsa, że ​​będzie wygląda tak:

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. 

W tym przypadku oczywiście można usunąć klasę "UnmanagedExports" z kodu C#.