2010-01-12 51 views
18

有人可以在這裏發佈一個例子如何在Delphi中託管CLR?我在這裏看過類似的question,但是我不能使用JCL,因爲我想用Delphi 5託管它。謝謝。在Delphi中使用/不使用JCL託管CLR - 示例


編輯:article關於福克斯臨託管CLR看起來很有希望,但我不知道如何從德爾福訪問clrhost.dll。


編輯2:我放棄對德爾福5的要求。現在我正在用Delphi 7試用JCL。但是我再次找不到任何示例。下面是我至今:

我的C#程序集:

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

我把它編譯成DelphiNET.dll

現在我想利用這個組件與德爾福:

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:變種沒有引用一個自動化對象

我還沒有與德爾福工作了很時間,所以我在這裏停留...


解決方案:ŧ這是COM默認情況下的問題。這是正確的.NET程序集:

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

重要提示:

當德爾福.NET的工作,重要的是在你的程序的開始調用Set8087CW($133F);Application.Initialize;前即)。 Delphi默認啓用了浮點異常(請參見this),CLR不喜歡它們。當我啓用它們時,我的程序奇怪地凍結了。

+2

爲什麼不能在Delphi 5中使用JCL?也可能在一些小的變化之後呢? – 2010-01-12 11:52:52

+0

JclDotNet.pas是用Delphi 6開發的,在Delphi 5中使用它看起來不會有細微的變化。 – 2010-01-12 12:36:22

+0

您是否考慮過Managed VCL? – 2010-01-12 12:58:33

回答

8

該類必須是可見的。如果您對整個裝配體有ComVisible(false),則可能不是這種情況。

.NET類將IDispatch的默認兼容,所以你的樣品應該工作得很好,如果類確實是標記有ComVisible特性..

但首先剝離下來到最低限度。把你的EXE放在你的.Net程序集所在的文件夾中,並跳過配置文件和應用程序庫。

在事情混淆之前,這裏發生了異常,對不對?

ov := obj.Unwrap; 
+0

+1建議使用COM來調用.Net方法,但這並不是他所要求的。他可能無法對裝配進行任何更改。正如我的答案所示,可以在沒有COM的情況下完成,也不需要使COM可見。 – 2010-01-12 20:44:22

+1

不使用COM,但將該類標記爲ComVisible。您的示例依賴於CLR的COM/Interop基礎結構,將.Net實例編組爲IDispatch。這隻適用於類是ComVisible(這是默認的,但可能會禁用程序集) – 2010-01-12 20:50:00

+0

我站在更正。我沒有想到它默認是真的。如果類型不是COM可見,我的例子確實失敗。這限制了它的使用顯着恕我直言。 – 2010-01-12 20:57:30

6

在這裏你去:

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類型和DelphiNet的ADD3方法。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))))); 
+1

這是有效的,因爲它不需要IDispatch或IUnknown,因此不需要COM/Interop。對於需要處理不可知的類的情況,這是一個非常好的例子。因此:+1 – 2010-01-12 21:37:15

+0

+1更新的示例(無ComVisible)非常棒!您需要以下內容來運行它:使用ActiveX; var Assm:_Assembly; var T:_Type; var Params:Variant; – 2010-01-12 23:33:56

+0

雖然它肯定會慢很多。我必須在3分鐘的會議中。以前,但我稍後會再給你一個樣品(不帶IDispatch)。 – 2010-01-13 08:04:11

12

這是另一種選擇。

這就是C#代碼。即使你不想使用my unmanaged exports,它仍然會解釋如何在不通過IDispatch(IDispatch非常慢)的情況下使用mscoree(CLR託管的東西)。

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

這是德爾福接口聲明:

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. 

當我適應拉斯的樣品,它會看起來像這樣:

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. 

在這種情況下,您當然可以從C#代碼中刪除「UnmanagedExports」類。