2010-01-12 21 views
18

¿Alguien puede publicar aquí un ejemplo de cómo alojar CLR en Delphi? He leído question similar aquí pero no puedo usar JCL porque quiero alojarlo en Delphi 5. Gracias.Hosting CLR en Delphi con/sin JCL - ejemplo


EDIT: Este article sobre el recibimiento de CLR en Fox Pro parece prometedor, pero no sé cómo acceder a clrhost.dll de Delphi.


Edición 2: que renunciar a Delphi 5 requisito. Ahora estoy probando JCL con Delphi 7. Pero nuevamente no puedo encontrar ningún ejemplo. Aquí es lo que tengo hasta ahora:

Mi C# ensamblaje:

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

he recopilado a DelphiNET.dll.

Ahora quiero utilizar este conjunto de 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; 

Esto termina con el error: EOleError: Variante no hace referencia a un objeto de automatización

No he trabajado con Delphi durante mucho tiempo, así que estoy atrapado aquí ...


Solución: T Aquí había un problema en la visibilidad COM que no es por defecto. Este es el ensamblado de .NET correcta:

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

Nota importante:

Cuando se trabaja con .NET de Delphi, es importante llamar Set8087CW($133F); al comienzo de su programa (es decir, antes Application.Initialize;). Delphi ha habilitado excepciones de coma flotante de forma predeterminada (consulte this) y al CLR no le gustan. Cuando los tuve habilitados, mi programa se bloqueó extrañamente.

+2

¿Por qué no puedes usar JCL en Delphi 5? ¿También no después de quizás algunos cambios menores? –

+0

El JclDotNet.pas está desarrollado con Delphi 6 y no parece cambios menores para usarlo en Delphi 5. –

+0

¿Ha considerado Managed VCL? –

Respuesta

8

La clase tiene que ser comvisible. Lo cual podría no ser el caso si tiene ComVisible (falso) para toda la asamblea.

clases .NET serán compatibles IDispatch de forma predeterminada, por lo que la muestra debería funcionar bien, si la clase es muy ComVisible ..

Pero tira hacia abajo al mínimo en primer lugar. Coloque su archivo exe en la misma carpeta que su ensamblado .Net y omita el archivo de configuración y la base de la aplicación.

Antes de que algo se mezcle, la excepción sucede aquí, ¿no?

ov := obj.Unwrap; 
+0

+1 por sugerir el uso de COM para llamar a los métodos .Net, pero en realidad no es lo que él pidió. Es posible que no pueda realizar ningún cambio en el ensamblaje. Como se muestra en mi respuesta, se puede hacer sin COM y sin hacer que las cosas estén visibles. –

+1

no utiliza COM, pero marca la clase para ser ComVisible. Su muestra se basa en la infraestructura COM/Interop del CLR para obtener una instancia .Net organizada como IDispatch. Esto solo funciona si la clase es ComVisible (que es la predeterminada, pero puede ser deshabilitada para el ensamble) –

+0

Me corresponde corregir. No esperaba que fuera cierto por defecto. Mi ejemplo de hecho falla si el tipo no es COM visible. Esto limita su uso significativamente en mi humilde opinión. –

6

Aquí van:

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: asume que el tipo DelphiNET.NETAdder y el método Add3 en DelphiNet.dll es ComVisible. Gracias a Robert.

actualización:

Cuando se utiliza la reflexión que no es necesario el atributo ComVisible. El siguiente ejemplo incluso funciona sin ser 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

Eso funciona, porque no necesita IDispatch o IUnknown y por lo tanto no COM/Interop. Es un excelente ejemplo para los casos en los que tienes que lidiar con clases que no son comisionables. Por lo tanto: +1 –

+0

+1 ¡El ejemplo actualizado (sin ComVisible) es genial! Necesita lo siguiente para ejecutarlo: usa ActiveX; var Assm: _Assembly; var T: _Type; var Params: variante; –

+0

Sin embargo, va a ser mucho más lento. Tengo que estar en una reunión 3 min. Hace, pero le daré otra muestra (sin IDispatch) más tarde. –

12

Aquí hay otra opción.

Ese es el código C#. E incluso si no quiere usar my unmanaged exports, aún así le explicará cómo usar mscoree (el material de CLR hosting) sin pasar por IDispatch (IDispatch es bastante 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(); 
     } 
    } 
} 

Esta es la declaración de interfaz de Delphi:

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

Si utiliza las exportaciones no administrados, puede hacerlo de esta manera:

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. 

Cuando me adapto muestra de Lars', lo haría se ve así:

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. 

En este caso, podría eliminar la clase "UnmanagedExports" del código C#, por supuesto.