【问题标题】:Hosting CLR in Delphi with/without JCL - example使用/不使用 JCL 在 Delphi 中托管 CLR - 示例
【发布时间】:2023-03-05 07:13:02
【问题描述】:

有人可以在这里发布一个如何在 Delphi 中托管 CLR 的示例吗?我在这里读过类似的question,但我不能使用 JCL,因为我想在 Delphi 5 中托管它。谢谢。


编辑:这个关于在 Fox Pro 中托管 CLR 的 article 看起来很有希望,但我不知道如何从 Delphi 访问 clrhost.dll。


编辑 2: 我放弃了 Delphi 5 的要求。现在我正在尝试使用 Delphi 7 进行 JCL。但我再次找不到任何示例。这是我到目前为止所拥有的:

我的 C# 程序集:

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 does not reference an automation object

我很久没有和Delphi合作了,所以我被困在这里......


解决方案: COM 可见性存在问题,默认情况下不存在。这是正确的 .NET 程序集:

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

重要提示:

在使用 Delphi 的 .NET 时,在程序的开头(即在 Application.Initialize; 之前)调用 Set8087CW($133F); 很重要。 Delphi 默认启用了浮点异常(参见this),CLR 不喜欢它们。当我启用它们时,我的程序奇怪地冻结了。

【问题讨论】:

  • 为什么不能在Delphi 5 中使用JCL?也不是经过一些小改动?
  • JclDotNet.pas 是使用 Delphi 6 开发的,在 Delphi 5 中使用它看起来不像是微小的变化。
  • 你考虑过托管 VCL 吗?
  • 托管 VCL 是我最后的选择,因为它不是免费的。
  • 解决您在 Jcl 中可能遇到的问题是恕我直言的更好选择。不可能那么难。从使用中删除变体开始?无论如何,你现在有两个例子来说明如何做到这一点。问题回答了吗?或者您真的想准备好使用 Delphi 5 代码而不仅仅是一个示例?

标签: c# .net delphi clr host


【解决方案1】:

这是另一种选择。

那是 C# 代码。即使你不想使用my unmanaged exports,它仍然会 解释如何在不通过 IDispatch 的情况下使用 mscoree(CLR 托管的东西)(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.

当然,在这种情况下,您可以从 C# 代码中删除“UnmanagedExports”类。

【讨论】:

    【解决方案2】:

    类必须是comvisible。如果您为整个程序集设置了 ComVisible(false),则情况可能并非如此。

    .Net 类默认与 IDispatch 兼容,因此如果该类确实是 comvisible,您的示例应该可以正常工作。

    但首先将其剥离到最低限度。将您的 exe 放在与 .Net 程序集相同的文件夹中,并跳过配置文件和应用程序库。

    在事情搞混之前,这里发生了异常,对吧?

     ov := obj.Unwrap;
    

    【讨论】:

    • +1 建议使用 COM 调用 .Net 方法,但这并不是他真正要求的。他可能无法对程序集进行任何更改。如我的回答所示,它可以在没有 COM 且不使 COM 可见的情况下完成。
    • 不使用 COM,但将类标记为 ComVisible。您的示例依赖 CLR 的 COM/Interop 基础结构来获取编组为 IDispatch 的 .Net 实例。这仅在类为 ComVisible 时才有效(这是默认设置,但可能会为程序集禁用)
    • 我的立场是正确的。我没想到默认情况下它是真的。如果类型不是 COM 可见的,我的示例确实会失败。这大大限制了它的使用恕我直言。
    • ComVisible 是问题所在!谢谢。
    • 这里有非常好的特殊知识点。我不会猜到这个。
    【解决方案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 类型和 DelphiNet.dll 中的 Add3 方法为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)))));
    

    【讨论】:

    • 这行得通,因为它不需要 IDispatch 或 IUnknown,因此不需要 COM/Interop。对于必须处理不可见的类的情况,这是一个相当出色的示例。因此:+1
    • +1 更新后的示例(没有 ComVisible)很棒!您需要以下内容才能运行它:使用 ActiveX; var Assm: _Assembly;变量 T: _Type; var 参数:变体;
    • 不过,它肯定会慢很多。我必须在 3 分钟后开会。以前,但稍后我会给你另一个示例(不带 IDispatch)。
    【解决方案4】:

    我在使用“TJclClrHost”组件时遇到了一些问题(参见 src 代码中的 cmets)。搜索后,我发现“CppHostCLR”Microsoft 示例是新的接口路径,以便在 Win32/64 应用程序中托管 .NET 运行时...

    这是一个用 Delphi 编写的快速(和肮脏)示例版本(也可以在这里找到:http://chapsandchips.com/Download/DelphiNETHost_v1.zip

    此示例代码中仅实现了 Delphi 接口(使用“OleVariant”/后期绑定)。

    hth,问候。

    帕斯卡

    unit uDelphiNETHosting;
    
    interface
    
    // Juin 2018 - "CorBindToRuntime*" deprecated API alternative by Pascal Chapuis with "Delphi 10.1 Berlin" version
    //
    // Sample implementation with .NET 4.0 interfaces defined in "metaHost.h" SDK with Delphi header (partial) source code
    // "CLRCreateInstance" (mscorlib) API with "ICLRMetaHost", "ICLRRuntimeInfo", "ICorRuntimeHost" interfaces are used.
    //
    // This Delphi sample provides :
    //  - Delphi Win32 .NET runtime advanced hosting
    //  - .NET class late binding interface with Delphi (OleVariant) Win32/64 application (no REGASM is needed)
    //  - Interfaced C# class is the same than provided in "CppHostCLR" Microsoft C++ sample available at :
    //      https://code.msdn.microsoft.com/windowsdesktop/CppHostCLR-e6581ee0/sourcecode?fileId=21953&pathId=1366553273
    //
    // This sample was inspired by "TJclClrHost" troubles with "_AppDomain.CreateInstanceFrom" with .NET 4.0 :
    //  - "CorBindToRuntime*" = deprecated API : "old-fashion" interfaced library vs. new interfaced COM/Interop API.
    //  - AppDomainSetup "ApplicationBase" property (assembly loading with custom path implementation) : no delegated resolver impl.
    //  - ComVisible .NET annotation is needed at least at class level or/and assembly level.
    //
    
    
    uses
        mscorlib_TLB, // imported from C:\Windows\Microsoft.NET\Framework\v4.0.30319\mscorlib.tlb
        mscoree_tlb,  // imported from C:\Windows\Microsoft.NET\Framework\v4.0...\mscoree.dll
        System.Classes, Vcl.Controls, Vcl.StdCtrls,
        Windows, Messages, SysUtils, Variants, Graphics, Forms,
        Dialogs, activeX, Vcl.ComCtrls;
    
    Const
    
      // ICLRMetaHost GUID
      // EXTERN_GUID(IID_ICLRMetaHost, 0xD332DB9E, 0xB9B3, 0x4125, 0x82, 0x07, 0xA1, 0x48, 0x84, 0xF5, 0x32, 0x16);
      IID_ICLRMetaHost : TGuid = '{D332DB9E-B9B3-4125-8207-A14884F53216}';
      // EXTERN_GUID(CLSID_CLRMetaHost, 0x9280188d, 0xe8e, 0x4867, 0xb3, 0xc, 0x7f, 0xa8, 0x38, 0x84, 0xe8, 0xde);
      CLSID_CLRMetaHost : TGuid = '{9280188d-0e8e-4867-b30c-7fa83884e8de}';
    
      // ICLRRuntimeInfo GUID
      // EXTERN_GUID(IID_ICLRRuntimeInfo, 0xBD39D1D2, 0xBA2F, 0x486a, 0x89, 0xB0, 0xB4, 0xB0, 0xCB, 0x46, 0x68, 0x91);
      IID_ICLRRuntimeInfo   : TGuid   = '{BD39D1D2-BA2F-486A-89B0-B4B0CB466891}';
      CLASS_ICLRRuntimeInfo : TGuid = '{BD39D1D2-BA2F-486a-89B0-B4B0CB466891}';
    
    type
    
      // .NET interface (defined in "metahost.h" SDK header)
      ICLRRuntimeInfo = interface(IUnknown)
        ['{BD39D1D2-BA2F-486a-89B0-B4B0CB466891}']
        function GetVersionString( pwzBuffer : PWideChar; var pcchBuffer : DWORD) : HResult; stdcall;
        function GetRuntimeDirectory(pwzBuffer : PWideChar; var pcchBuffer : DWORD) : HResult; stdcall;
        function IsLoaded( hndProcess  : THANDLE; out bLoaded : bool): HResult; stdcall;
        function LoadErrorString(iResourceID: UINT; pwzBuffer: PWideChar; var pcchBuffer : DWORD; iLocaleID :LONG): HResult; stdcall;
        function LoadLibrary(pwzDllName : PWideChar; phndModule : PHMODULE): HResult; stdcall;
        function GetProcAddress( pszProcName : PChar; var ppProc : Pointer) : HResult; stdcall;
        function GetInterface( const rclsid : TCLSID;const riid : TIID;  out ppUnk : IUnknown) : HResult;  stdcall;
        function IsLoadable( var pbLoadable :  Bool) : HResult; stdcall;
        function SetDefaultStartupFlags(dwStartupFlags : DWORD; pwzHostConfigFile : LPCWSTR) : HResult; stdcall;
        function GetDefaultStartupFlags(var pdwStartupFlags : PDWORD;pwzHostConfigFile : LPWSTR;var pcchHostConfigFile : DWORD )  : HResult; stdcall;
        function BindAsLegacyV2Runtime() : HResult;  stdcall;
        function IsStarted( var  pbStarted : bool;var pdwStartupFlags : DWORD ) : HResult;  stdcall;
       end;
    
      // .NET interface (defined in "metahost.h" SDK header)
      ICLRMetaHost = interface(IUnknown)
        ['{D332DB9E-B9B3-4125-8207-A14884F53216}']
        function GetRuntime(pwzVersion: LPCWSTR; const riid: TIID; out ppRuntime : IUnknown): HResult; stdcall;
        function GetVersionFromFile(const pwzFilePath: PWideChar; pwzBuffer: PWideChar; var pcchBuffer: DWORD): HResult; stdcall;
        function EnumerateInstalledRuntimes(out ppEnumerator: IEnumUnknown): HResult; stdcall;
        function EnumerateLoadedRuntimes(const hndProcess: THandle;  out ppEnumerator: IEnumUnknown): HResult; stdcall;
        function RequestRuntimeLoadedNotification(out pCallbackFunction: PPointer): HResult; stdcall;
        function QueryLegacyV2RuntimeBinding(const riid: TGUID;out ppUnk: PPointer): HResult; stdcall;
        procedure ExitProcess(out iExitCode: Int32); stdcall;
      end;
    
      TSampleForm = class(TForm)
        BtnTest: TButton;
        StatusBar1: TStatusBar;
        Label1: TLabel;
        Label2: TLabel;
        procedure BtnTestClick(Sender: TObject);
      private
        // CLR
        FPtrClr            : ICLRMetaHost;
        // CLR runtime info
        FPtrRunTime        : ICLRRuntimeInfo;
        // CLR Core runtime
        FPtrCorHost        : ICorRuntimeHost;
        FDefaultNetInterface : ICorRuntimeHost;
        //
        Procedure LoadAndBindAssembly();
      public
      end;
    
      // Main .NET hosting API entry point (before interfaced stuff)
      function CLRCreateInstance(const clsid,iid: TIID; out ppv : IUnknown): HRESULT; stdcall; external 'MSCorEE.dll';
    
    var
      SampleForm: TSampleForm;
    
    implementation
    
    uses //JcldotNet  // original "TJclClrHost" component unit
          math,
          ComObj;     // COM init + uninit
    
    {$R *.dfm}
    
    Procedure TSampleForm.LoadAndBindAssembly();
    Const
      NetApp_Base_Dir : WideString  = '.\Debug\';
      Sample_Test_Value             = 3.1415;
    var
      hr            : HResult;
      Ov            : OleVariant;
      ws            : WideString;
      iDomAppSetup  : IUnknown;
      iDomApp       : IUnknown;
      // .Net interfaces...
      iDomAppSetup2 : IAppDomainSetup;
      iDomApp2      : AppDomain;
      objNET        : ObjectHandle;
    begin
      // Delphi sample : https://adamjohnston.me/delphi-dotnet-interop-with-jvcl/
      // DomainSetup
      hr := FDefaultNetInterface.CreateDomainSetup( iDomAppSetup );
      if ( hr = S_OK) then
      begin
         // Domain Setup Application...
         iDomAppSetup2 := iDomAppSetup as IAppDomainSetup;
         // NB. Set "ApplicationBase" root directory is NOT ok WITHOUT additional "ResolveEventHandler" (cf 1*)
         // https://weblog.west-wind.com/posts/2009/Jan/19/Assembly-Loading-across-AppDomains
         hr := iDomAppSetup2.Set_ApplicationBase( NetApp_Base_Dir );
         //hr := iDomAppSetup2.Set_PrivateBinPath(  NetApp_Base_Dir );
         //hr := iDomAppSetup2.Set_DynamicBase(  NetApp_Base_Dir );
         if ( hr = S_OK ) then
         begin
           hr := iDomAppSetup2.Set_ConfigurationFile('CSClassLibrary.config');
           if ( hr = S_OK ) then
           begin
             hr := FDefaultNetInterface.CreateDomainEx( PWideChar('aNETClassHostSample'), iDomAppSetup2, nil, iDomApp );
             if ( hr = S_OK ) then
             begin
               iDomApp2 := iDomApp as AppDomain;
               iDomApp2.Get_BaseDirectory(ws); // *** Check setup directory is OK
               // CoBindEx... API troubles begins here... alternative (not deprecated implementation) solves them !
               // CreateInstanceFrom Doc : https://msdn.microsoft.com/en-us/library/we62chk6(v=vs.110).aspx
               //hr := (iDomApp as _AppDomain).CreateInstanceFrom( 'C:\Data\dev\delphi\NetHosting\Sample\CppHostCLR\C# and C++\C#,C++\CppHostCLR\CSClassLibrary\obj\Debug\CSClassLibrary.dll', 'CSClassLibrary.CSSimpleObject', objNET );
               hr := iDomApp2.CreateInstanceFrom( NetApp_Base_Dir+'CSClassLibrary.dll', // (1*) : NO ResolveEventHandler => absolute path
                                                  'CSClassLibrary.CSSimpleObject', objNET );
               if ( hr = S_OK ) then
               begin
                 // *** NB. ***
                 // [ComVisible(true)] annotation on class definition is NEEDED (to invoke via late binding with COM)
                 // *** and/or ***
                 // .NET project option "Make assembly COM visible" (cf. AssemblyInfo.cs) : [assembly: ComVisible(true)]
                 ov := objNET.Unwrap;
                 ov.FloatProperty := Sample_Test_Value;
                 ShowMessage( 'Result FloatProperty=' +FloatToStr( Currency(ov.FloatProperty) ) );  // Interop data type between Delphi and C# (Currency <=> float)
               end
               else ShowMessage( 'CreateInstanceFrom error: ' + SysErrorMessage(hr) );
             end
             else ShowMessage( 'CreateDomainEx error: ' + SysErrorMessage(hr) );
           end
           else ShowMessage( 'Set_ConfigurationFile error: ' + SysErrorMessage(hr) );
         end
         else ShowMessage( 'Set_ApplicationBase error: ' + SysErrorMessage(hr) );
      end
      else  ShowMessage( 'CreateDomainSetup error: ' + SysErrorMessage(hr) );
    end;
    
    procedure TSampleForm.BtnTestClick(Sender: TObject);
    var
      // CLR status flags
      FLoadable         : Bool;  // framework is loadable ?
      FStarted          : Bool;  // framework is started ?
      FLoaded           : Bool;  // framework is loaded ?
    
      arrWideChar       : Array[0..30] of WChar;
    
      lArr              : Cardinal;
    
      Flags             : DWORD;
    
      hr1,hr2,hr3       : HResult;
    begin
    
     // Part-1/2 : Host targetted .NET framework version with "CLRCreateInstance" entry point
    
     //CoInitializeEx(nil,COINIT_APARTMENTTHREADED); //COINIT_MULTITHREADED
     try
        FLoadable := false;
        FStarted  := false;
        FLoaded   := false;
        Flags := $ffff;
        try
         FPtrClr     := nil;
         FPtrRunTime := nil;
         FPtrCorHost := nil;
         hr1 := CLRCreateInstance(CLSID_CLRMetaHost, IID_ICLRMetaHost, IUnknown(FPtrClr) );  //  CLSID + IID
         if ( hr1 = S_OK) then
          begin
            FPtrRunTime := nil;
            hr1 := FPtrClr.GetRuntime( PWideChar('v4.0.30319'), IID_ICLRRuntimeInfo, IUnknown(FPtrRunTime) );
            if ( hr1 = S_OK ) then
            begin
             // Usefull to check overflow in case of wrong API prototype : call second method overflow other results...
             hr1 := FPtrRunTime.IsLoadable( FLoadable );
             hr2 := FPtrRunTime.IsStarted( FStarted, Flags ); // NB. OVERFLOW by defining FLoadable, FLoaded... local var. as "boolean" NOT "Bool"...
             hr3 := FPtrRunTime.IsLoaded( GetCurrentProcess(), FLoaded );
             if ( hr1 = S_OK ) and ( hr2 = S_OK ) and ( hr3 = S_OK ) then
             begin
                if ( not FLoaded ) and ( FLoadable ) and ( not FStarted ) then
                begin
                  hr1 := FPtrRunTime.GetInterface( CLASS_CorRuntimeHost, IID_ICorRuntimeHost, IUnknown(FPtrCorHost) ); // IID_ICorRuntimeHost,
                  if ( hr1 = S_OK ) then
                  begin
                    if ( FPtrCorHost <> nil ) then
                        FDefaultNetInterface := (FPtrCorHost  as Iunknown)  as ICorRuntimeHost
                    else ; //  NOT available...
                  end
                  else ShowMessage( 'GetInterface error : ' + SysErrorMessage(hr1) );
                end
                else
                begin
                  if (FLoaded and FStarted) then ShowMessage( '.NET Framework version is already loaded and started...')
                  else ShowMessage( '.NET Framework version is N0T loadable...');
                end;
              end
              else
              begin
                ShowMessage( 'IID_ICLRRuntimeInfo.IsLoadable error : ' + SysErrorMessage( Min(hr1,hr2) ) );
              end;
            end
            else ShowMessage( 'GetRuntime error : ' + SysErrorMessage(hr1) );
          end
          else ShowMessage( 'CLRCreateInstance error: ' + SysErrorMessage(hr1) );
        Except on e:exception do
          if Assigned( e.InnerException ) then ShowMessage( e.InnerException.ToString )
          else ShowMessage( e.ToString );
        end;
        // Check a call to an assembly...
       if ( Assigned( FDefaultNetInterface )) then
        begin
          lArr := SizeOf( arrWideChar );
          FillChar( arrWideChar, SizeOf(arrWideChar), #0);
          hr1 := FPtrRunTime.GetVersionString( PWideChar(@arrWideChar[0]), lArr);;
          if ( hr1 = S_OK ) then ShowMessage('Framework version '+arrWideChar+' is available...')
          else  ShowMessage( 'GetVersionString error: ' + SysErrorMessage(hr1));
          hr1 := FDefaultNetInterface.Start();
          if ( hr1 <> S_OK ) then ShowMessage( 'CLRCreateInstance error: ' + SysErrorMessage(hr1) );
        end;
     finally
    //   if (PtrClr<>nil) then
    //   begin
    //     PtrClr._Release;
    //     //PtrClr := nil;
    //   end;
    //   if (PtrRunTime<>nil) then
    //   begin
    //     PtrRunTime._Release;
    //  ///   PtrRunTime := nil;
    //   end;
    //   if (PtrCorHost<>nil) then
    //   begin
    //     PtrCorHost._Release;
    //     //PtrCorHost := nil;
    //   end;
       //FDefaultInterface._Release;
    
      //CoUnInitialize();
     end;
    
     // Part-2/2 : load, bind a class call sample assembly class with loaded framework...
     LoadAndBindAssembly();
    
    end;
    
    end.
    

    【讨论】:

      【解决方案5】:

      感谢这个论坛,我找到了在 Lazarus 中使用 dll C# 的最佳解决方案:

      C#:

      using System;
      using System.Collections.Generic;
      using System.Text;
      using RGiesecke.DllExport;
      using System.Runtime.InteropServices;
      
      namespace DelphiNET
      {
      
          [ComVisible(true)]
          [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
          [Guid("BA7DFB53-6CEC-4ADA-BE5E-16F1A46DFAC5")]
          public interface IDotNetAdder
          {
              int Add3(int left);
      
              int Mult3(int left);
      
              string Expr3(string palavra);
          }
      
          [ComVisible(true)]
          [ClassInterface(ClassInterfaceType.None)]
          public class DotNetAdder : DelphiNET.IDotNetAdder
          {
              public int Add3(int left)
              {
                  return left + 3;
              }
      
              public int Mult3(int left)
              {
                  return left * 3;
              }
      
              public string Expr3(string palavra)
              {
                  return palavra + " é a palavra que estou esperando!";
              }
          }
      
          internal static class UnmanagedExports
          {
              [DllExport("createdotnetadder", CallingConvention = System.Runtime.InteropServices.CallingConvention.StdCall)]
              static void CreateDotNetAdderInstance([MarshalAs(UnmanagedType.Interface)]out IDotNetAdder instance)
              {
                  instance = new DotNetAdder();
              }
          }
      }
      

      拉撒路:

      unit uDLLC01;
      
      {$mode objfpc}{$H+}
      
      interface
      
      uses
        Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, StdCtrls{, dynlibs};
      
      type
      
         IDotNetAdder = interface
         ['{BA7DFB53-6CEC-4ADA-BE5E-16F1A46DFAC5}']
          function Add3(left : integer):integer; safecall; {stdcall nao funciona}
          function Mult3(left : integer):integer; safecall;
          function Expr3(left : WideString):WideString; safecall;
         end;
      
        { TForm1 }
        TForm1 = class(TForm)
          Button1: TButton;
          Label1: TLabel;
          procedure Button1Click(Sender: TObject);
        private
          { private declarations }
        public
          { public declarations }
        end;
      
      
      var
        Form1: TForm1;
      
      implementation
      
      {stdcall e cdecl work well; cdecl say is C style to Lazarus}
      procedure createdotnetadder(out instance:IDotNetAdder); cdecl external 'IDotNetAdder.dll' name 'createdotnetadder';
      
      {$R *.lfm}
      
      { TForm1 }
      
      procedure TForm1.Button1Click(Sender: TObject);
      var
        x     : LongInt = 0;
        y     : LongInt = 0;
        z     : WideString;
        nTam  : integer;
        adder : IDotNetAdder;
      begin
        try
           createdotnetadder(adder);
           z := adder.Expr3('Antonio');
           nTam := Length(z);
           x := adder.Add3(4);
           y := adder.Mult3(4);
        finally
           showmessage('4 + 3 = '+ (inttostr(x)));
           showmessage('4 * 3 = '+ (inttostr(y)));
           showmessage('Expressão = ' + String(z));
        end;
      end;
      
      end.
      

      观察到 var Z 是 WideString,唯一可以用作字符串类型的类型,我尝试了 String、AnsiString 和 PChar,但都不起作用,它们只返回第一个字符。 我有口音问题,比如“Antônio”这个词作为参数发送,我试图找到一个 C# 可以理解的转换器并发回相同的词。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2012-01-31
        • 1970-01-01
        • 2013-07-30
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2010-10-22
        • 2013-10-28
        相关资源
        最近更新 更多