2010-01-12 6 views
18

Qualcuno può pubblicare qui un esempio su come ospitare CLR in Delphi? Ho letto lo stesso question qui, ma non posso usare JCL perché lo voglio ospitare in Delphi 5. Grazie.Hosting CLR in Delphi con/senza JCL - esempio


EDIT: Questo article sull'hosting CLR a Fox Pro sembra essere molto promettente, ma non so come accedere clrhost.dll da Delphi.


Edit 2: mi arrendo su Delphi 5 requisito. Ora sto provando JCL con Delphi 7. Ma ancora non riesco a trovare alcun esempio. Ecco quello che ho fino ad ora:

mio C# di montaggio:

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

ho compilato a DelphiNET.dll.

Ora voglio utilizzare questo assembly da 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; 

Questo finisce con l'errore: EOleError: Variant non fa riferimento a un oggetto di automazione

non ho lavorato con Delphi per un lungo tempo così mi sono bloccato qui ...


Soluzione: T qui c'era un problema nella visibilità di COM che non era di default. Questo è l'assembly .NET corretta:

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

Nota importante:

Quando si lavora con .NET da Delphi, è importante chiamare Set8087CW($133F); all'inizio del programma (vale a dire prima del Application.Initialize;). Delphi ha abilitato le eccezioni in virgola mobile per impostazione predefinita (vedere this) e CLR non le apprezza. Quando li ho abilitati, il mio programma si è stranamente congelato.

+2

Perché non è possibile utilizzare JCL in Delphi 5? Inoltre, non dopo forse alcune piccole modifiche? –

+0

JclDotNet.pas è sviluppato con Delphi 6 e non presenta modifiche minori per l'utilizzo in Delphi 5. –

+0

Hai considerato Managed VCL? –

risposta

8

La classe deve essere comprensibile. Quale potrebbe non essere il caso se si dispone di ComVisible (false) per l'intero assembly.

classi .NET saranno IDispatch compatibili per impostazione predefinita, in modo che il campione dovrebbe funzionare bene, se la classe è davvero ComVisible ..

Ma striscia verso il basso per il minimo indispensabile prima. Metti il ​​tuo exe nella stessa cartella del tuo .Net assembly e salta il file di configurazione e la base dell'applicazione.

Prima che qualcosa si mischi, l'eccezione è felice qui, giusto?

ov := obj.Unwrap; 
+0

+1 per suggerire di utilizzare la COM per chiamare i metodi .Net, ma non è proprio quello che ha chiesto. Potrebbe non essere in grado di apportare modifiche all'assieme. Come mostrato nella mia risposta, può essere fatto senza COM e senza rendere visibili le cose COM. –

+1

non utilizzando COM, ma contrassegnando la classe come ComVisible. Il tuo campione si basa sull'infrastruttura COM/Interop del CLR per ottenere un'istanza .Net controllata come IDispatch. Funziona solo se la classe è ComVisible (che è l'impostazione predefinita, ma potrebbe essere disabilitata per l'assembly) –

+0

Sono corretto. Non mi aspettavo che fosse vero per impostazione predefinita. Il mio esempio infatti fallisce se il tipo non è COM visibile. Questo limita il suo uso in modo significativo IMHO. –

6

Qui si va:

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. 

Nota: dal presupposto che il tipo e il metodo DelphiNET.NETAdder Add3 in DelphiNet.dll è ComVisible. Grazie a Robert.

Aggiornamento:

Quando si utilizza la riflessione non è necessario l'attributo ComVisible. Il prossimo esempio funziona anche senza essere 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

Funziona, perché non ha bisogno di IDispatch o IUnknown e quindi non ha COM/Interop. È un esempio piuttosto eccellente per i casi in cui devi affrontare classi non comvisibili. Quindi: +1 –

+0

+1 L'esempio aggiornato (senza ComVisible) è eccezionale! Per eseguirlo è necessario quanto segue: usa ActiveX; var Assm: _Assembly; var T: _Type; var Params: variante; –

+0

Tuttavia, è molto più lento. Devo essere in un incontro 3 min. fa, ma ti darò un altro campione (senza IDispatch) più tardi. –

12

Ecco un'altra opzione.

Questo è il codice C#. E anche se non vuoi usare my unmanaged exports, sarebbe ancora spiegare come usare mscoree (il CLR che ospita roba) senza passare attraverso IDispatch (IDispatch è piuttosto lento).

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

Questa è la dichiarazione di interfaccia Delphi:

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

Se si utilizzano le esportazioni non gestiti, si può fare in questo modo:

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. 

Quando mi adatto campione Lars', sarebbe assomiglia:

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 questo caso è possibile rimuovere la classe "UnmanagedExports" dal codice C#, ovviamente.