.Net Runtime Library for Delphi

On this page you can learn more about the .Net Runtime Library for Delphi (CNClrLibrary). For more information about the product you are interested in, click on the learn more button or navigate to one of its information pages.

Download Buy
Sample Codes Migration Service

The .Net Runtime Library for Delphi (CNClrLibrary) is the best library to work with .Net framework and .Net libraries from Delphi. It is designed to provide a way to interact with applications written in anyone of the .Net languages (such as C#, Visual Basic.Net, Delphi.Net, JScript.Net, etc) from Delphi.

The .Net Runtime Library for Delphi (CNClrLibrary) allows Delphi to use .Net libraries without the need for registry entries, COM registrations, or changes to the .NET libraries.

Applications built with the .Net Runtime Library for Delphi will not require any dll files when deployed.

CNClrLibrary allows developers to create applications that can mix managed (.NET) and unmanaged ("native" Delphi) code in the same project.

A Brief History of .NET in the World of Delphi

Ever since it was originally announced, the Delphi community has had a love/hate relationship with .NET. Being spoiled by the richness in RAD development they had been enjoying for years, the community was quick to dismiss .NET as something that might bring benefits to MFC or Visual Basic developers, but would bring little benefit over Delphi and its powerful VCL class library and RAD form designers. Many also saw .NET as the latest development fad that would excite people for a while but quickly die out for lack of serious adoption.

Three significant releases later, it has now become obvious that these assumptions were wrong. The .NET 2.0 core framework has introduced significant development advances in the .NET languages, such as Generics that make .NET development attractive and, with the new services branded under 3.0, Microsoft has introduced a significant new paradigm for user interface design that is likely to revolutionize how tomorrow's Windows desktop applications will look and feel. The third party market for .NET has developed strongly, with a vast range of professional components being available in all imaginable areas – while at the same time the Delphi component market seems to be winding down, as many third party vendors scale down their support in favor of .NET or are leaving the market altogether.

Embracing .NET with Delphi

This is the situation that triggered the idea for the .Net Runtime Library for Delphi: what if there was a way for Delphi developers to preserve their existing investment in solid, working Win32/Win64 code written in Delphi, but at the same time enable them to extend that codebase, using new .NET-based technologies? What if developers could use .NET Framework Class Libraries and all the other benefits of the .NET framework when writing new code for their applications, while keeping the existing parts of their application code basically untouched without porting and retesting?

CNClrLibrary enables you to keep your investment in your existing native Delphi code base, while at the same time opening your applications to .Net Framework Class Libraries. At the same time, the existing code can be kept and maintained in Delphi, and both parts can contribute to what appears to the end user as a single, unified application. CNClrLibrary makes it possible to employ new and emerging .NET technologies within your existing Delphi-based application frame.

Competitive Advantages

The following are some of the advantages the .Net Runtime Library for Delphi have over it competitors:
  • Full access to .Net Framework Class Library (Including new and emerging .NET technologies).
  • No extra dll is required when deployed.
  • No COM registration of .Net Libraries is required when deployed.
  • There are tools to generate your .Net Libraries into Delphi pas files.
  • Allows Delphi to consume .Net libraries as if they were native code.
  • Easy to use.

CNClrLibrary can do so much:

  • Access .Net Framework Class Library (such as Collections, Data Configuration, DataSets, Data Access, Database Connectivity, Diagnostics, IO, Linq, Dynamic Linq, System, Device and Application Management, Networking, Reflections, Security, Encryption, Cryptogrphy, Character Encoding and String Manipulation, XML etc).
  • Access Third Party .Net Libraries.
  • Access Your .Net Libraries.
  • Hosts the .Net Common Language Runtime (CLR) in Delphi.
  • Can load and access assemblies and their types from third party .Net libraries or your own .Net libraries or executable files.
  • Can load and access assemblies and their types from Global Assembly Cache (GAC)
  • Can invoke members of the loaded assembly types which includes constructor, fields, properties, methods and events.
  • Can invoke static members of the loaded assembly types which includes constructor, fields, properties, methods and events.
  • Can load and access assemblies and their types from Global Assembly Cache (GAC)
  • Can create instance of .Net object from the types of the assembly loaded.
  • Can handle .Net exceptions.
  • Can handle, access and invoke .Net events.
  • Can host .Net controls in Delphi VCL Forms.
  • Contains a utility for importing .Net libraries(third party or your own .Net libraries) or WSDL and for generating Delphi classes from the types of the imported libraries.
  • ...and many more.

.Net Runtime Library for Delphi Consists of:

1. Host Class Library

This is also called Delphi Host Class Library or DHCL. This Library contains Delphi classes and interfaces for starting and hosting the .Net Common Language Runtime (CLR) which allows Delphi applications to load and access .net assemblies, create object instance of the types from the loaded assembly, invoke the members of the types etc. The CLR manages memory, thread execution, code execution and other system services.
Learn more »


2. Framework Class Library

The Delphi Framework Class Library (DFCL) is a Delphi interface representation of the .NET Framework class library which is a collection of reusable types that tightly integrate with the Delphi Host Class Library.
Learn more »

Tools and Components

  • .Net Assembly/WSDL Importer

    This is only available for Professional Edition User License. This tool allows users to import any .Net libraries(3p libraries or your own libraries) or WSDL and generates delphi pas files from the imported libraries using the classes and interfaces in the .Net Runtime Library.
    Learn more »

  • .Net VCL for Delphi

    1. Container VCL Control - This control allows a developer to host .Net controls in Delphi VCL Forms.
    2. ADO.Net VCL for Delphi - Allows developers to create fastest and most reliable database connectivity solutions for any database using ADO.Net technology in Delphi.

Example:

program XML;

{$APPTYPE CONSOLE}
{$R *.res}

uses
  CNClrLib.Xml,
  CNClrLib.Core;

var
  NameTable: _NameTable;
  Book, Price: Variant;
  Settings: _XmlReaderSettings;
  Reader: _XmlReader;
  ReaderHelper: _XmlReaderHelper;
  ObjectHelper: _ObjectHelper;
begin
  NameTable := CoNameTable.CreateInstance;

  Book := NameTable.Add('Book');
  Price := NameTable.Add('Price');

  // Create the Reader.
  Settings := CoXmlReaderSettings.CreateInstance;
  Settings.NameTable := NameTable.AsXmlNameTable;

  ReaderHelper := CoXmlReaderHelper.CreateInstance;
  Reader := ReaderHelper.Create_1('books.xml', Settings);

  Reader.MoveToContent;
  Reader.ReadToDescendant('Book');

  ObjectHelper := CoObjectHelper.CreateInstance;
  if ObjectHelper.ReferenceEquals(Book, Reader.Name) then
  begin
    // Do additional processing.
  end;
end.

Deployment

  • Microsoft .Net Framework (Latest versions of Windows have .Net Framework pre-installed)
  • Basic Copy Deployment
    • Your application

Delphi and .NET are different platforms. Each has unique features that the other does not. Support exists for all .NET functionalities.

Supported


.Net Runtime Library for Delphi comes in three editions: Standard, Enterprise and Professional.

Standard Edition is a cost-effective solution for developers to access .net assemblies from Delphi.

Enterprise Edition extends Standard Edition with full access to the .Net Framework Class Library.

Professional Edition extends Enterprise Edition with .net assembly and wsdl import tools.

All the editions contains the source codes for the library classes and interfaces.

The matrix below compares features of Runtime Library editions.

Features Standard Enterprise Professional
Host Class Library Yes Yes Yes
Framework Class Library No Yes Yes
.Net Assemblies/WSDL Importer Utility No Yes Yes
Unit Price $293.99 $349.99 $399.99

IDE Compatibility

The Runtime Library integrates with the following Embarcadero Delphi products:
  • Delphi 2010
  • Delphi XE
  • Delphi XE2
  • Delphi XE3
  • Delphi XE4
  • Delphi XE5
  • Delphi XE6
  • Delphi XE7
  • Delphi XE8
  • Delphi 10.0 Seattle
  • Delphi 10.1 Berlin
  • Delphi 10.2 Tokyo

.NET Framework 4.5 Service Pack 1 or higher is required for the Runtime Library.


6.0.5.1 24-Aug-2018
  • Added Cast methods (AsType<>, Cast<>, IsType<> etc) in TClrBaseObject for easy casting between inherited .Net types in Delphi.
  • .Net DLL/WSDL Import Utility
    • Removed Guid Strings from the generated interfaces (Not required).
    • Fixed error when converting some .Net generic types to Delphi.

6.0.5.0 06-Aug-2018
  • Fix error triggered when converting .Net object to it base generic type from Delphi.
  • No support for Delphi Codegear 2009.
  • .Net DLL/WSDL Import Utility
    • Improved performance.
    • Optimised Delphi codes generated by the utility.
    • Fix WSDL load error by modifying the WSDL engine to load any WSDL documents and generate Delphi classes from the loaded wsdl types.
    • The utility generates an interface unit from the types of the imported assemblies.
      Notes:
      • These interfaces are inherited by the classes also generated by the utility.
      • The purpose of introducing the interface is to fix memory issues in the generated pas files by allowing Delphi to free objects automatically when the object is out of scope.
      • Filename: xxxxxxxx.Intf.pas (xxxxxxxx is the full name of the .Net Assembly imported).
    • Implement Delphi generic nullable class (Nullable<>) in CNClrLib.Generic.Helper which can be used to convert between .Net and Delphi Generic Nullable types.
    • Fixed .Net type properties and fields which returns generic list or dictionary and not visible on the import .Net Type list.
    • Some unit pas files generated by the utility have changed:
      • xxxxxxxxConsts.pas unit is changed to xxxxxxxxx.Consts.pas
      • xxxxxxxxEnums.pas unit is changed to xxxxxxxxx.Enums.pas

6.0.4.0 09-May-2018
  • .Net DLL/WSDL Import Utility - Optimised Delphi codes generated by the utility.
  • .Net DLL/WSDL Import Utility - Fixed reflection type load exception when generating delphi codes from GAC Asemblies.
  • Fixed error triggered by invoking methods with generic parameters.
  • Register special key events in TClrContainer which allows hosted .Net Controls use tab, up arrow, down arrow, left arrow, right arrow etc keys in Delphi.

6.0.3.0 02-April-2018
  • Optimised Delphi codes generated by the .Net DLL/WSDL Import Utility.
  • Introduced a new method called AsType in TClrBaseObject class for casting .Net derived class to base class in Delphi.
  • Introduced a new unit called CNClrLib.Generic.Helper which contains helper class methods for converting .Net generic lists or dictionaries to Delphi generic List (TObjectList<>) or dictionary (TObjectDictionary<,>) and vice-versa.

6.0.2.0 22-Jan-2018
  • Fixed "Object not set to an instance" error if a nil parameter is passed to TClrBaseObject constructor.
  • Modified TClrBaseObject class to prevent the following error "This type has a ComVisible(false) parent in its hierarchy, therefore QueryInterface calls for IDispatch or class interfaces are disallowed."
  • .Net Assembly/WSDL Import Tools: Delphi Code generator engine modified to produce clean and optimised delphi codes.

6.0.1.0 20-Oct-2017
  • Introduced a new class for handling event source notification in the host unit.
  • Introduced a new wrapper class for static fields, methods and properties of System.Type in the host unit.
  • Changed any reference of TClrActivator in the host unit to TDispatchActivator Class.
  • Added extra methods to TDispatchActivator Class.
  • .Net Assembly/WSDL Import Tools: Fixed Object reference not set to an instance of an object error when generating Delphi Codes.
  • .Net Assembly/WSDL Import Tools: Fixed E2003 Undeclared identifier: 'IList' error in the generated Delphi Units. IList is changed to _IList.

6.0.0.1 14-Aug-2017
  • Fixed bug with creating an instance of the TClrObjectArray or TClrArray Class
  • Fixed bugs in .Net Assembly/WSDL Import Tools to generate Delphi codes from .Net assemblies/WSDL without compiler errors.
  • Changed return type of the methods for loading .net assemblies in TClrAssembly and TClrAppDomain Classes from TClrAssembly to _Assembly interface in order to prevent possible memory leaks if the user forgets to free the loaded .net assemblies.
  • Fixed "Constructor on type 'CNClrLibrary.Security.Cryptography.Cryptostream' not found" error

6.0.0.0 01-Jun-2017
  • Implemented mscorlib types as part of the runtime library
  • Added new classes and improve the existing classes in the host class library
  • Support .Net Framework 4.5.2 or Higher
  • Support Delphi 2009 or Higher
  • Added .Net Assembly/WSDL Import Tools
  • Usability improvements
  • Stability and performance improvements

5.0.0.0 10-Oct-2015
  • Major changes to the Runtime Library
  • Support 64bit Delphi application

4.0.3.1 05-May-2015
  • Support .Net Framework 4.5 or Higher

4.0.3.0 15-Feb-2015
  • Fixed bugs in CryptoStream type interface to avoid the error ""EOleException: Constructor on type 'CNCorLib.Sys.Security.Cryptography.CryptoStream' not found"

4.0.2.0 01-Dec-2014
  • Fixed bugs in Runtime Host class to prevent "System.NullReferenceException"

4.0.1.0 05-Sep-2014
  • Fixed bugs in Mscorlib type interfaces

4.0.0.0 29-Oct-2014
  • Added Delphi Framework Class Library
  • Support .Net Framework 4.0 or Higher
  • Support Delphi 7 and Higher

2.0.0.0 23-Jan-2014
  • Fixed bugs with I/O operations
  • Usability improvements
  • Stability and performance improvements

1.0.0.0 01-Aug-2013
  • First release of .Net Runtime Library
  • Delphi 7 is supported
  • Support .Net Framework 3.0

Source Code Examples

The following examples demonstrate how to use the .Net Runtime Library for Delphi to produce a software by combining pascal source codes with .Net framework and other .Net libraries.

How to write a simple Hello World using .Net Console

program ConsoleApp;

{$APPTYPE CONSOLE}
{$R *.res}

uses
{$IF CompilerVersion > 22}
  System.SysUtils,
{$ELSE}
  SysUtils,
{$IFEND}
  CNClrLib.Core;

var
  Console : _Console;
begin
  Console := CoConsole.CreateInstance;
  Console.WriteLine_14('Hello! Welcome to .Net Runtime Library for Delphi.');
  Console.WriteLine_14('==================================================');
  Console.WriteLine_14('The program displays the string Hello World!');
  Console.WriteLine;
  Console.WriteLine_14('Hello World!');
  Console.WriteLine_14('Press any key to exit.');
  Console.ReadKey;
end.

How to use AppDomain to Load .Net Assemblies

program LoadAssemblyUsingAppDomain;

{$APPTYPE CONSOLE}
{$R *.res}

uses
{$IF CompilerVersion > 22}
  System.SysUtils,
{$ELSE}
  SysUtils,
{$IFEND }
  CNClrLib.Host, CNClrLib.Core;

var
  Console : _Console;

  procedure DisplayAssemblyInfo(AAssembly : _Assembly);
  begin
    if AAssembly = nil then
      Console.WriteLine_14('Assembly cannot be loaded')
    else
    begin
      Console.WriteLine_14('Assembly has been loaded');
      Console.WriteLine_15('Is Fully Trusted:      {0}', AAssembly.IsFullyTrusted);
      Console.WriteLine_15('Global Assembly Cache: {0}', AAssembly.GlobalAssemblyCache);
      Console.WriteLine_15('Location:              {0}', AAssembly.Location);
      Console.WriteLine_15('Image Runtime Version: {0}', AAssembly.ImageRuntimeVersion);
      Console.WriteLine_15('Number of Types:       {0}', AAssembly.GetTypes.Length);
      Console.WriteLine;
      Console.WriteLine;
    end;
  end;
  
  procedure LoadAssemblyByAssemblyString;
  var
    m_assembly: _Assembly;
  begin
    Console.WriteLine_14('Loading System.Data.dll using Assembly String');

    m_assembly := TClrAppDomain.GetCurrentDomain.Load('System.Data, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089');
    DisplayAssemblyInfo(m_assembly);
  end;

  procedure LoadAssemblyByAssemblyName;
  var
    m_assemblyName : _AssemblyName;
    m_assembly : _Assembly;
  begin
    Console.WriteLine_14('Loading System.Data.dll using AssemblyName Properties and methods');

    m_assemblyName := CoAssemblyName.CreateInstance;
    m_assemblyName.Name := 'System.Data';
    m_assemblyName.Version := CoVersion.CreateInstance('2.0.0.0');
    m_assemblyName.CultureInfo := CoCultureInfo.CreateInstance('');
    m_assemblyName.SetPublicKeyToken(TClrArrayHelper.ToByteArray('b77a5c561934e089'));
    m_assembly := TClrAppDomain.GetCurrentDomain.Load(m_assemblyName);
    DisplayAssemblyInfo(m_assembly);
  end;

  procedure LoadAssemblyByAssemblyNameString;
  var
    m_assemblyName : _AssemblyName;
    m_assembly : _Assembly;
  begin
    Console.WriteLine_14('Loading System.Data.dll using AssemblyName with AssemblyString');

    m_assemblyName := CoAssemblyName.CreateInstance('System.Data, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089');

    m_assembly := TClrAppDomain.GetCurrentDomain.Load(m_assemblyName);
    DisplayAssemblyInfo(m_assembly);
  end;

begin
  Console := CoConsole.CreateInstance;
  Console.WriteLine_14('Hello! Welcome to .Net Runtime Library for Delphi.');
  Console.WriteLine_14('==================================================');
  Console.WriteLine_14('The program demonstrate how to use AppDomain to Load .Net Assemblies');
  Console.WriteLine;

  try
    LoadAssemblyByAssemblyString;
    LoadAssemblyByAssemblyName;
    LoadAssemblyByAssemblyNameString;
  except
    on E:Exception do
      Console.WriteLine_15('Exception : {0}', E.Message);
  end;
  Console.ReadKey;
end.

How to use Activator to Create instance of a .Net Object

program CreateInstanceUsingActivator;

{$APPTYPE CONSOLE}
{$R *.res}

uses
{$IF CompilerVersion > 22}
  System.SysUtils, System.Variants,
{$ELSE}
  SysUtils, Variants,
{$IFEND}
  CNClrLib.Host, CNClrLib.Core;

var
  Console : _Console;
  Activator : _Activator;

  procedure DisplayObjectTypeInfo(AObject : OleVariant);
  var
    m_type : _Type;
  begin
    if VarIsEmpty(AObject) or VarIsClear(AObject) then
      Console.WriteLine_14('object has not been instantiated')
    else
    begin
      m_type := TClrAssembly.GetObjectType(AObject);
      Console.WriteLine_14('object has been instantiated');
      Console.WriteLine_15('Assembly FullName:     {0}', m_type.Assembly.FullName);
      Console.WriteLine_15('FullName:              {0}', m_type.FullName);
      Console.WriteLine_15('GUID:                  {0}', m_type.GUID.ToString);
      Console.WriteLine_15('ToString:              {0}', m_type.ToString);
      Console.WriteLine;
      Console.WriteLine;
    end;
  end;

  procedure CreateInstanceFromAssemblyString;
  var
    m_sqlConnHnd : _ObjectHandle;
  begin
    m_sqlConnHnd := Activator.CreateInstance_5('System.Data, Version=2.0.0.0, Culture=neutral, ' +
                  'PublicKeyToken=b77a5c561934e089', 'System.Data.SqlClient.SqlConnection');
    DisplayObjectTypeInfo(m_sqlConnHnd.Unwrap);
  end;

  procedure CreateInstanceFromTypeName;
  var
    m_sqlConnobj : OleVariant;
    m_sqlConType : _Type;
  begin
    TClrAssembly.Load('System.Data, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089');
    m_sqlConType := TClrAssembly.GetType('System.Data.SqlClient.SqlConnection', True);
    m_sqlConnobj := Activator.CreateInstance_4(m_sqlConType);
    DisplayObjectTypeInfo(m_sqlConnobj);
  end;

  procedure CreateInstanceFromTypeWithParam;
  var
    m_qlConnHnd : OleVariant;
    m_objArray : _ObjectArray;
    m_sqlConType : _Type;
  begin
    //Create Instance with a parameter
    m_objArray := CoObjectArray.CreateInstance(1);
    m_objArray[0] := 'Data Source=myServerAddress;Initial Catalog=myDataBase;User ID=myDomain\myUsername;Password=myPassword;';

    //Assembly has been loaded in CreateInstance2, no need to reload assembly
    m_sqlConType := TClrAssembly.GetType('System.Data.SqlClient.SqlConnection', True);
    m_qlConnHnd := Activator.CreateInstance_2(m_sqlConType,  m_objArray);
    DisplayObjectTypeInfo(m_qlConnHnd);
  end;

  procedure CreateInstanceFromFile;
  var
    m_sqlConnobj : _ObjectHandle;
  begin
    m_sqlConnobj := Activator.CreateInstanceFrom('C:\Windows\Microsoft.NET\Framework\v2.0.50727\System.Data.dll',
        'System.Data.SqlClient.SqlConnection');
    DisplayObjectTypeInfo(m_sqlConnobj.Unwrap);
  end;

begin
  Console := CoConsole.CreateInstance;
  Activator := CoActivator.CreateInstance;
  Console.WriteLine_14('Hello! Welcome to .Net Runtime Library for Delphi.');
  Console.WriteLine_14('==================================================');
  Console.WriteLine_14('The program demonstrate how to use Activator interface to Create Object Instance');
  Console.WriteLine;
  try
    CreateInstanceFromAssemblyString;
    CreateInstanceFromTypeName;
    CreateInstanceFromTypeWithParam;
    CreateInstanceFromFile;
  except
    on E:Exception do
      Console.WriteLine_15('Exception : {0}', E.Message);
  end;
  Console.ReadKey;
end.

How to use Dispatch Activator to Create instance of a .Net Object as Dispatch Interface

program CreateInstanceUsingDispActivator;

{$APPTYPE CONSOLE}
{$R *.res}

uses
{$IF CompilerVersion > 22}
  System.SysUtils, System.Variants,
{$ELSE}
  SysUtils, Variants,
{$IFEND }
  CNClrLib.Host, CNClrLib.Core;

var
  Console : _Console;
  Activator : TClrDispatchActivator;

  procedure DisplayObjectTypeInfo(AObject : OleVariant);
  var
    m_type : _Type;
  begin
    if VarIsEmpty(AObject) or VarIsClear(AObject) then
      Console.WriteLine_14('object has not been instantiated')
    else begin
      m_type := TClrAssembly.GetObjectType(AObject);
      Console.WriteLine_14('object has been instantiated');
      Console.WriteLine_15('Assembly FullName:     {0}', m_type.Assembly.FullName);
      Console.WriteLine_15('FullName:              {0}', m_type.FullName);
      Console.WriteLine_15('GUID:                  {0}', m_type.GUID.ToString);
      Console.WriteLine_15('ToString:              {0}', m_type.ToString);
      Console.WriteLine;
      Console.WriteLine;
    end;
  end;

  procedure CreateInstanceFromTypeName;
  var
    m_sqlConnobj : OleVariant;
  begin
    TClrAssembly.Load('System.Data, Version=2.0.0.0, Culture=neutral, '+
      'PublicKeyToken=b77a5c561934e089');
    m_sqlConnobj := Activator.CreateInstance('System.Data.SqlClient.SqlConnection');
    DisplayObjectTypeInfo(m_sqlConnobj);
  end;

  procedure CreateInstanceFromTypeWithParam;
  var
    m_sqlConnHnd : OleVariant;
    m_objectArray : _ObjectArray;
  begin
    //Create Instance with a parameter
    m_objectArray := CoObjectArray.CreateInstance(1);
    m_objectArray[0] := 'Data Source=myServerAddress;Initial Catalog=myDataBase;User ID=myDomain\myUsername;Password=myPassword;';
    m_sqlConnHnd := Activator.CreateInstance('System.Data.SqlClient.SqlConnection', m_objectArray);
    DisplayObjectTypeInfo(m_sqlConnHnd);
  end;

begin
  Console := CoConsole.CreateInstance;
  Activator := TClrDispatchActivator.Create;
  Console.WriteLine_14('Hello! Welcome to .Net Runtime Library for Delphi.');
  Console.WriteLine_14('==================================================');
  Console.WriteLine_14('The program demonstrate how to use Dispatch Activator interface to Create Object Instance');
  Console.WriteLine;
  try
    CreateInstanceFromTypeName;
    CreateInstanceFromTypeWithParam;
  except
    on E:Exception do
      Console.WriteLine_15('Exception : {0}', E.Message);
  end;
  Console.ReadKey;
end.

How to load external .Net DLL and access the members of loaded assembly types

program COMDispInterface;

{$APPTYPE CONSOLE}
{$R *.res}

uses
{$IF CompilerVersion > 22}
  System.SysUtils,
{$ELSE}
  SysUtils,
{$IFEND}
  CNClrLib.Host, CNClrLib.Core;

(*  Mathematics.Dll Source Code
  namespace Mathematics
  {
      public class Mathematics
      {
          [DispId(0)]
          public int Add(int a, int b)
          {
              return a + b;
          }

          [DispId(1)]
          public int Subtract(int a, int b)
          {
              return a - b;
          }

          [DispId(2)]
          public bool Equal(int a, int b)
          {
              return a == b;
          }
      }
  }
*)

type
  //Corresponding Delphi DispInterface type of Mathematics type in the Mathematics.dll
  _Mathematics = dispinterface
  ['{D77959BD-C7AC-4D65-9980-A88510F776B8}']
    function Add(a, b : Integer) : Integer; dispid 0;
    function Subtract(a, b : Integer) : Integer; dispid 1;
    function Equal(a, b : Integer) : WordBool; dispid 2;
  end;

var
  Console : _Console;
  Mathematics : _Mathematics;


  procedure LoadMathematicAssembly;
  begin
    // If error occurs while executing LoadFrom then
    // Right-Click on the file and select properties, click on the
    // unblock button to allow access.
    TClrAssembly.LoadFrom('Mathematics.dll');
  end;

  procedure CreateMathematicTypeInstance;
  begin
    Mathematics := _Mathematics(TClrDispatchActivator.CreateInstance('Mathematics.Mathematics'));
  end;

  procedure AccessMathematicsObjectMethods;
  begin
    Console.WriteLine_15('Add(30, 50):      {0}', Mathematics.Add(30, 50));
    Console.WriteLine_15('Subtract(30, 50): {0}', Mathematics.Subtract(30, 50));
    Console.WriteLine_15('Equal(30, 50):    {0}', Mathematics.Equal(30, 50));
    Console.WriteLine_15('Equal(50, 50):    {0}', Mathematics.Equal(50, 50));
  end;

begin
  Console := CoConsole.CreateInstance;
  Console.WriteLine_14('Hello! Welcome to .Net Runtime Library for Delphi.');
  Console.WriteLine_14('==================================================');
  Console.WriteLine_14('This program demonstrate how to use COM DispInterface to communicate with .Net library type members');
  Console.WriteLine;
  try
    LoadMathematicAssembly;
    CreateMathematicTypeInstance;
    AccessMathematicsObjectMethods;
  except
    on E: Exception do
      Console.WriteLine_15('Exception: {0}', e.Message);
  end;
  Console.ReadKey;
end.


How to use ClrObject interface to connect to SQL Server

program UsingClrObject;

{$APPTYPE CONSOLE}
{$R *.res}

uses
{$IF CompilerVersion > 22}
  System.SysUtils, System.Variants,
{$ELSE}
  SysUtils, Variants,
{$IFEND}
  CNClrLib.Host, CNClrLib.Enums, CNClrLib.Core;

var
  Console : _Console;
  SQLConnection : IDispatch;
  ConnectionString : WideString;

  procedure CreateClrObjectUsingWrap;
  var
    m_SQLConnection : _ClrObject;
  begin
    Console.WriteLine_14('Wrap the connection object to ClrObject Interface');
    m_SQLConnection := CoClrObject.Wrap(SQLConnection);
    m_SQLConnection.SetPropertyValue('ConnectionString', ConnectionString);

    m_SQLConnection.InvokeMethod('Open');
    Console.WriteLine_14('Connection Opened');
      
    m_SQLConnection.InvokeMethod('Close');
    Console.WriteLine_14('Connection Closed');
    Console.WriteLine;
  end;  

  procedure CreateClrObjectUsingTClrObjectClass;
  var
    m_SQLConnection : TClrObject;
  begin
    Console.WriteLine_14('Using CreateInstance to create CrystalNet Object');
    m_SQLConnection := TClrObject.Create(SQLConnection);
    try
//      m_SQLConnection.OwnsObject := False;
      m_SQLConnection.SetPropertyValue('ConnectionString', ConnectionString);

      m_SQLConnection.InvokeMethod('Open');
      Console.WriteLine_14('Connection Opened');
      
      m_SQLConnection.InvokeMethod('Close');
      Console.WriteLine_14('Connection Closed');
      Console.WriteLine;
    finally
      m_SQLConnection.Free;
    end;
  end;  

begin
  Console := CoConsole.CreateInstance;
  Console.WriteLine_14('Hello! Welcome to .Net Runtime Library for Delphi.');
  Console.WriteLine_14('==================================================');
  Console.WriteLine_14('This program demonstrates how to use ClrObject interface to connect to Sql Server.');
  Console.WriteLine;
  try
    TClrAssembly.LoadWithPartialName('System.Data');

    Console.WriteLine_15('The Assembly [{0}] has been loaded.', 'System.Data');
    Console.WriteLine;

    //Create Instance of System.Data.SqlClient.SqlConnection Type
    SQLConnection := TClrDispatchActivator.CreateInstance('System.Data.SqlClient.SqlConnection');

    ConnectionString := 'Data Source=myServerAddress;Initial Catalog=myDataBase;User ID=myUsername;Password=myPassword;';
    Console.WriteLine_15('Connecting to : {0}', ConnectionString);
    Console.WriteLine;

    CreateClrObjectUsingWrap;
    CreateClrObjectUsingTClrObjectClass;
  except
    on E: Exception do
      Console.WriteLine_15('Exception: {0}', E.Message);
  end;
  Console.ReadKey;
end.

How to use .Net Collections in Delphi

program Collections;

{$APPTYPE CONSOLE}
{$R *.res}

uses
{$IF CompilerVersion > 22}
  System.SysUtils,
{$ELSE}
  SysUtils,
{$IFEND }
  CNClrLib.Host, CNClrLib.Core, CNClrLib.Collections,
  CNClrLib.TypeNames, CNClrLib.Host.Helper;

var
  Console: _Console;
  NamesArray : _StringArray;
  NamesEnumerator: _IEnumerator;
  ArrayList: _ArrayList;
  StringList: _StringCollection;
  StringDictionary: _StringDictionary;
  StringDictValuesEnumerator: _IEnumerator;
  SortedListValuesEnumerator: _IEnumerator;
  HashTableKeysEnumerator: _IEnumerator;
  AEnumerator: _IEnumerator;
  Stack: _Stack;
  Queue: _Queue;
  BitArray: _BitArray;
  HashTable: _Hashtable;
  SortedList: _SortedList;
  I, P: Integer;

begin
  try
    Console := CoConsole.CreateInstance;

    //========================================================================
    //                    Using _IEnumerable Interface
    //========================================================================
    //An enumerator is an object that provides a forward, read-only cursor for a set of items.
    //The _IEnumerable interface has one method called the GetEnumerator method.
    //This method returns an object that implements the IEnumerator interface.
    //The code snippet below illustrates how an enumerator can be used to iterate
    //though a list or collection of items.

    NamesArray := CoStringArray.CreateInstance(2);
    NamesArray[0] := 'Joydip';
    NamesArray[1] := 'Jini';
    NamesEnumerator := NamesArray.AsIEnumerator;
    while NamesEnumerator.MoveNext do
      Console.WriteLine_12(NamesEnumerator.Current);

    //Note that the GetEnumerator method returns an enumerator object each time it is called.
    //Further, the loop contains the Console.WriteLine_12 statement in its re-initializer portion,
    //which is perfectly valid. The condition being evaluated is whether the MoveNext method
    //returns a value of true. The MoveNext method returns true as long as there are items in
    //the collection. The Current property returns the current object and is automatically typecast
    //to string by making a call to the ToString method implicitly.


    //========================================================================
    //                        Using _ArrayList Interface
    //========================================================================
    //The _ArrayList interface is a dynamic array of heterogeneous objects. Note that in an array
    //we can store only objects of the same type. In an ArrayList, however, we can have different
    //type of objects; these in turn would be stored as object type only.  We can have an ArrayList
    //object that stores integer, float, string, etc., but all these objects would only be stored as
    //object type. An ArrayList uses its indexes to refer to a particular object stored in its collection.
    //The Count property gives the total number of items stored in the ArrayList object.
    //The Capacity property gets or sets the number of items that the ArrayList object can contain.
    //Objects are added using the Add method of the ArrayList and removed using its Remove method.
    //An example of usage of an ArrayList is given below.

    ArrayList := CoArrayList.CreateInstance;
    ArrayList.Add('Joydip');
    ArrayList.Add(100);
    ArrayList.Add(20.5);
    for I := 0 to ArrayList.Count - 1 do
      Console.WriteLine_12(ArrayList[I]);

    //It is to be noted here that the initial capacity of an ArrayList is 16, which is increased once the
    //17th item is stored onto it. This repeated memory allocation and copying of the items can be quite
    //expensive in some situations. For performance reasons we can set the initial capacity of the object
    //of an ArrayList by using the Capacity property or an overloaded constructor of the ArrayList class.
    //This is shown in the example below.

    ArrayList := CoArrayList.CreateInstance;
    ArrayList.Capacity := 3;
    ArrayList.Add('Joydip');
    ArrayList.Add(100);
    ArrayList.Add(20.5);
    for I := 0 to ArrayList.Count - 1 do
      Console.WriteLine_12(ArrayList[I]);


    //========================================================================
    //                  Using _StringCollection Interface
    //========================================================================
    //The StringCollection interface implements the IList interface and is like an ArrayList of strings.
    //The following code example shows how we can work with a StringCollection class.

    StringList := CoStringCollection.CreateInstance;
    StringList.Add('Manashi');
    StringList.Add('Joydip');
    StringList.Add('Jini');
    StringList.Add('Piku');

    for I := 0 to StringList.Count - 1 do
      Console.WriteLine_14(StringList[I]);


    //========================================================================
    //                 Using _StringDictionary Interface
    //========================================================================
    //Similar to the StringCollection interface we have the StringDictionary interface,
    //which is just a Hashtable that has its keys as strings only. Remember that a Hashtable
    //can contain any object type in its key. The following code shows how we can work with a
    //StringDictionary interface.

    StringDictionary := CoStringDictionary.CreateInstance;
    StringDictionary.Add('A', 'Manashi');
    StringDictionary.Add('B','Joydip');
    StringDictionary.Add('C','Jini');
    StringDictionary.Add('D','Piku');

    StringDictValuesEnumerator := StringDictionary.Values.AsIEnumerable.GetEnumerator;
    while StringDictValuesEnumerator.MoveNext do
      Console.WriteLine_14(StringDictValuesEnumerator.Current);


    //========================================================================
    //                        Using _Stack Interface
    //========================================================================
    //The _Stack interface is one that provides a Last-in-First-out (LIFO) collection of items
    //of the System.Object type. The last added item is always at the top of the Stack and is also
    //the first one to be removed. The following code sample shows how we can use a Stack class for
    //LIFO operation on its collection of items.

    Stack := CoStack.CreateInstance;
    Stack.Push('Joydip');
    Stack.Push('Steve');
    Stack.Push('Jini');
    while (Stack.Count > 0) do
      Console.WriteLine_12(Stack.Pop);

    //The Push method is responsible for storing items in the Stack and the method Pop
    //removes them one at a time from the top of the Stack.


    //========================================================================
    //                   Using _Queue Interface
    //========================================================================
    //Unlike the Stack interface, the Queue is a data structure that provides a First-in-First-out
    //collection of items of the Object type. The newly added items are stored at the end or
    //the rear of the Queue and items are deleted from the front of the Queue.
    //The following code shows how the Queue class can be used.

    Queue := CoQueue.CreateInstance;
    Queue.Enqueue('Joydip');
    Queue.Enqueue('Steve');
    Queue.Enqueue('Jini');
    while (Queue.Count > 0) do
      Console.WriteLine_12(Queue.Dequeue);

    //The Enqueue method is responsible for storing items at the rear of the Queue and the method Dequeue
    //removes them one at a time from the front of the Queue.


    //========================================================================
    //                     Using _BitArray Interface
    //========================================================================
    //The BitArray interface can be used to store bits in an array. They can be set to true or false,
    //depending on the parameter supplied at the time of creating the BitArray object.
    //The following is an example of its usage.

    BitArray := CoBitArray.CreateInstance(5, false);
    // Or
    BitArray := CoBitArray.CreateInstance(5, true);
    // Similar to the other collections discussed above, the BitArray interface also contains the
    //Count property to get the number of items stored in this collection of bit values.
    //The following methods of the BitArray class allow logical bit operation.
    // ·         And
    // ·         Or
    // ·         Not
    // ·         Xor



    //========================================================================
    //                     Using _Hashtable Interface
    //========================================================================
    //The Hashtable provides a faster way of storage and retrieval of items of the object type.
    //The Hashtable class provides support for key based searching. These keys are unique hash codes that
    //are unique to a specific type.  The GetHashCode method of the Hashtable class returns the hash code
    //for an object instance. The following code snippet shows how we can use a Hashtable interface.

    HashTable := CoHashtable.CreateInstance;
    HashTable.Add(1, 'Joydip');
    HashTable.Add(2, 'Manashi');
    HashTable.Add(3, 'Jini');
    HashTable.Add(4, 'Piku');
    Console.WriteLine_14('The keys are:--');
    HashTableKeysEnumerator := HashTable.Keys.AsIEnumerable.GetEnumerator;
    while HashTableKeysEnumerator.MoveNext do
      Console.WriteLine_12(HashTableKeysEnumerator.Current);

    try
      Console.WriteLine_14('Please enter the key to search');
      p := TClrInt32Helper.Parse(Console.ReadLine);
      Console.WriteLine_12(HashTable[p]);
    except
      on E: Exception do
        Console.WriteLine_12(E.Message);
    end;

    //To remove an item from the Hashtable interface, the Remove method is used.
    //The statement HashTable.Remove(3) would remove the item "Jini" from the Hashtable
    //object created in the above code.  The code shown above can also be written as shown below
    //to display the contents of the Hashtable object using IDictionaryEnumerator.

    HashTable := CoHashtable.CreateInstance;
    HashTable.Add(1, 'Joydip');
    HashTable.Add(2, 'Manashi');
    HashTable.Add(3, 'Jini');
    HashTable.Add(4, 'Piku');
    Console.WriteLine_14('The keysare:--');
    AEnumerator := HashTable.GetEnumerator.AsIEnumerator;
    while AEnumerator.MoveNext do
      Console.WriteLine_12(HashTable[p]);


    //========================================================================
    //                     Using _SortedList Interface
    //========================================================================
    //The _SortedList interface allows items of the Object type to be placed in the
    //collection using key value pairs and, at the same time, supports sorting.
    //The following code shows how we can use a SortedList.

    SortedList := CoSortedList.CreateInstance;
    SortedList.Add(1, 'Manashi');
    SortedList.Add(3, 'Joydip');
    SortedList.Add(2, 'Jini');
    SortedList.Add(4, 'Piku');

    Console.WriteLine_14('Displaying thenames');

    SortedListValuesEnumerator:= SortedList.Values.AsIEnumerable.GetEnumerator;
    while SortedListValuesEnumerator.MoveNext do
      Console.WriteLine_14(SortedListValuesEnumerator.Current);

    //The output of the above code is:
    //  Manashi
    //  Jini
    //  Joydip
    //  Piku

    //The same code can be written using IDictionaryEnumerator to display all the items of the
    //SortedList object, as shown below.

    SortedList := CoSortedList.CreateInstance;
    SortedList.Add(1, 'Manashi');
    SortedList.Add(3, 'Joydip');
    SortedList.Add(2, 'Jini');
    SortedList.Add(4, 'Piku');
    Console.WriteLine_14('Displaying thenames');
    AEnumerator := SortedList.Values.AsIEnumerable.GetEnumerator;
    while AEnumerator.MoveNext do
      Console.WriteLine_12(AEnumerator.Current);

    Console.ReadKey;
  except
    on E: Exception do
      Writeln(E.ClassName, ': ', E.Message);
  end;
end.

How to use .Net Generic Dictionary in Delphi

program GenericDictionary;

{$APPTYPE CONSOLE}
{$R *.res}

uses
{$IF CompilerVersion > 22}
  System.SysUtils,
{$ELSE}
  SysUtils,
{$IFEND }
  CNClrLib.Host,
  CNClrLib.Core,
  CNClrLib.Collections;

var
  OpenWith: _GenericDictionary;
  OpenWithEnumerator: _GenericDictionary_Enumerator;
  ValueCollection: _GenericDictionary_ValueCollection;
  ValueCollEnumerator: _GenericValueCollection_Enumerator;
  keyCollection: _GenericDictionary_KeyCollection;
  keyCollEnumerator: _GenericKeyCollection_Enumerator;
  Console: _Console;
  Value: OleVariant;
begin
  try
    Console := CoConsole.CreateInstance;

    // Create a new dictionary of strings, with string keys.
    OpenWith := CoGenericDictionary.CreateInstance(TClrAssembly.GetType('System.String'),
      TClrAssembly.GetType('System.String'));

    // Add some elements to the dictionary. There are no
    // duplicate keys, but some of the values are duplicates.
    OpenWith.Add('txt', 'notepad.exe');
    OpenWith.Add('bmp', 'paint.exe');
    OpenWith.Add('dib', 'paint.exe');
    OpenWith.Add('rtf', 'wordpad.exe');

    // The Add method throws an exception if the new key is
    // already in the dictionary.
    try
      OpenWith.Add('txt', 'winword.exe');
    except //(ArgumentException)
      Console.WriteLine_14('An element with Key = ''txt'' already exists.');
    end;

    // The Item property is another name for the indexer, so you
    // can omit its name when accessing elements.
    Console.WriteLine_15('For key = ''rtf'', value = {0}.', OpenWith['rtf']);

    // The indexer can be used to change the value associated
    // with a key.
    OpenWith['rtf'] := 'winword.exe';
    Console.WriteLine_15('For key = ''rtf'', value = {0}.', OpenWith['rtf']);

    // If a key does not exist, setting the indexer for that key
    // adds a new key/value pair.
    OpenWith['doc'] := 'winword.exe';

    // The indexer throws an exception if the requested key is
    // not in the dictionary.
    try
      Console.WriteLine_15('For key = ''tif'', value = {0}.', OpenWith['tif']);
    except //(KeyNotFoundException)
      Console.WriteLine_14('Key = ''tif'' is not found.');
    end;

    // When a program often has to try keys that turn out not to
    // be in the dictionary, TryGetValue can be a more efficient
    // way to retrieve values.
    if (OpenWith.TryGetValue('tif', Value)) then
      Console.WriteLine_15('For key = ''tif'', value = {0}.', Value)
    else
      Console.WriteLine_14('Key = ''tif'' is not found.');

    // ContainsKey can be used to test keys before inserting them.
    if (not OpenWith.ContainsKey('ht')) then
    begin
      OpenWith.Add('ht', 'hypertrm.exe');
      Console.WriteLine_15('Value added for key = ''ht'': {0}', OpenWith['ht']);
    end;

    // When you use when loop to enumerate dictionary elements from GetEnumerator,
    // the elements are retrieved as KeyValuePair objects.
    Console.WriteLine();
    OpenWithEnumerator := OpenWith.GetEnumerator;
    while OpenWithEnumerator.MoveNext do
      Console.WriteLine_17('Key = {0}, Value = {1}', OpenWithEnumerator.Current.Key, OpenWithEnumerator.Current.Value);

    // To get the values alone, use the Values property.
    ValueCollection := OpenWith.Values;
    ValueCollEnumerator := ValueCollection.GetEnumerator;

    // The elements of the ValueCollection are strongly typed
    // with the type that was specified for dictionary values.
    Console.WriteLine();
    while ValueCollEnumerator.MoveNext do
      Console.WriteLine_15('Value = {0}', ValueCollEnumerator.Current);

    // To get the keys alone, use the Keys property.
    keyCollection := OpenWith.Keys;
    keyCollEnumerator := keyCollection.GetEnumerator;

    // The elements of the KeyCollection are strongly typed
    // with the type that was specified for dictionary keys.
    Console.WriteLine();
    while keyCollEnumerator.MoveNext do
      Console.WriteLine_15('Key = {0}', keyCollEnumerator.Current);

    // Use the Remove method to remove a key/value pair.
    Console.WriteLine_14('Remove(''doc'')');
    OpenWith.Remove('doc');

    if (not OpenWith.ContainsKey('doc')) then
      Console.WriteLine_14('Key ''doc'' is not found.');

    Console.ReadKey;
  except
    on E: Exception do
      Writeln(E.ClassName, ': ', E.Message);
  end;
end.

How to raise and handle .Net Events in Delphi 1

program EventHandling;

{$APPTYPE CONSOLE}
{$R *.res}

uses
{$IF CompilerVersion > 22}
  System.SysUtils, System.Variants,
{$ELSE}
  SysUtils, Variants,
{$IFEND }
  CNClrLib.Host, CNClrLib.Core;

var
  Console : _Console;
  SQLConnection : TClrObject;
  ConnectionString : WideString;
  
(*
  //C# Delegate of SqlConnection.StateChange event
  
  public delegate void StateChangeEventHandler(object sender, StateChangeEventArgs e);

  public enum ConnectionState
  {
      Closed = 0,
      Open = 1,
      Connecting = 2,
      Executing = 4,
      Fetching = 8,
      Broken = 16,
  }

  public sealed class StateChangeEventArgs
  {
      public ConnectionState CurrentState { get; }
      public ConnectionState OriginalState { get; }
  }
*)

  //Corresponding Delphi Event Handler Callback method
  procedure StateChangeEventHandler(ASender : _ClrObject; StateChangeEventArgs : _ClrEventArgs); stdcall;
  var
    m_eventArgs : _ClrObject;
    m_currentState,
    m_originalState : Integer;

    procedure WriteStateChage(State : Integer);
    begin
      case State of
        0:  Console.Write_22('Closed');
        1:  Console.Write_22('Open');
        2:  Console.Write_22('Connecting');
        4:  Console.Write_22('Executing');
        8:  Console.Write_22('Fetching');
        16: Console.Write_22('Broken');
      end;
      Console.WriteLine;
    end;
  begin
    m_eventArgs :=  CoClrObject.Wrap(StateChangeEventArgs.EventArgs);
    m_currentState := m_eventArgs.GetPropertyValue('CurrentState');
    m_originalState := m_eventArgs.GetPropertyValue('OriginalState');

    Console.Write_22('Current State : ');
    WriteStateChage(m_currentState);

    Console.Write_22('Original State : ');
    WriteStateChage(m_originalState);
  end;

  procedure OpenAndCloseSQLConnection;
  begin
    SQLConnection.SetPropertyValue('ConnectionString', ConnectionString);

    SQLConnection.InvokeMethod('Open');
    Console.WriteLine_14('Connection Opened');

    SQLConnection.InvokeMethod('Close');
    Console.WriteLine_14('Connection Closed');
  end;

begin
  Console := CoConsole.CreateInstance;

  Console.WriteLine_14('Hello! Welcome to .Net Runtime Library for Delphi.');
  Console.WriteLine_14('==================================================');
  Console.WriteLine_14('This program demonstrates how to handle events and callback.');
  Console.WriteLine;
  try
    //Load Assembly
    TClrAssembly.LoadWithPartialName('System.Data');

    //Create Instance of System.Data.SqlClient.SqlConnection Type
    SQLConnection := TClrObject.Create(TClrActivator.ClrCreateInstance('System.Data.SqlClient.SqlConnection'));
    SQLConnection.RegisterEventCallBack('StateChange', @StateChangeEventHandler);
    try
      ConnectionString := 'Data Source=myServerAddress;Initial Catalog=myDataBase;User ID=myUsername;Password=myPassword';
      Console.WriteLine_15('Connecting to : {0}', ConnectionString);
      Console.WriteLine;

      OpenAndCloseSQLConnection;
    finally
      SQLConnection.Free;
    end;
  except
    on E: Exception do
      Console.WriteLine_15('Exception: {0}', E.Message);
  end;
  Console.ReadKey;
end.

How to raise and handle .Net Events in Delphi 2

program AdvancedEventHandler;

{$APPTYPE CONSOLE}
{$R *.res}

uses
{$IF CompilerVersion > 22}
  System.SysUtils, System.Variants,
{$ELSE}
  SysUtils, Variants,
{$IFEND }
  CNClrLib.Host, CNClrLib.Core;

type

  TEventHandler = class
  private
    FSQLConnection : TClrObject;
    FConnectionStr : WideString;

    FEventHandler : TClrEventHandler;
    procedure SetOnStateChangeEvent(Value : TClrEventHandler);
  public
    constructor Create;
    destructor Destroy; override;
    procedure LoadAssembly;
    procedure CreateSQLConnectionTypeInstance;
    procedure EventFired;
    procedure OpenAndCloseSQLConnection;
    property OnStateChangeEvent : TClrEventHandler read FEventHandler write SetOnStateChangeEvent;
  end;
  
var
  Console : _Console;
  EventHandler : TEventHandler;


  { TEventHandler }

  constructor TEventHandler.Create;
  begin
  end;

  destructor TEventHandler.Destroy;
  begin
    FSQLConnection := Nil;
    inherited Destroy;
  end;

  procedure TEventHandler.EventFired;
  begin
    Console.WriteLine_14('State Change Event has been fired');
  end;

  procedure TEventHandler.LoadAssembly;
  begin
    //Load Assembly
    TClrAssembly.LoadWithPartialName('System.Data');
  end;

  procedure TEventHandler.CreateSQLConnectionTypeInstance;
  begin
    //Create Instance of System.Data.SqlClient.SqlConnection Type
    FSQLConnection := TClrObject.Create(TClrDispatchActivator.CreateInstance('System.Data.SqlClient.SqlConnection'));
  end;

  procedure TEventHandler.OpenAndCloseSQLConnection;
  begin
    FConnectionStr := 'Data Source=MyDataSourceName;Initial Catalog=MyDBName;User ID=MyUserName;Password=MyPasswd';

    Console.WriteLine_15('Connecting to SQL Connection using : {0}', FConnectionStr);
    Console.WriteLine;

    FSQLConnection.SetPropertyValue('ConnectionString', FConnectionStr);

    FSQLConnection.InvokeMethod('Open');
    Console.WriteLine_14('Connection Opened');
    Console.WriteLine;

    FSQLConnection.InvokeMethod('Close');
    Console.WriteLine_14('Connection Closed');
  end;

  procedure TEventHandler.SetOnStateChangeEvent(Value: TClrEventHandler);
  begin
    if @FEventHandler <> nil then
      FSQLConnection.UnRegisterEventCallBack('StateChange', @FEventHandler);

    FEventHandler := Value;

    if @FEventHandler <> nil then
      FSQLConnection.RegisterEventCallBack('StateChange', @FEventHandler);
  end;

  (*
    //C# Delegate of SqlConnection.StateChange event
    public delegate void StateChangeEventHandler(object sender, StateChangeEventArgs e);
  
    public enum ConnectionState
    {
        Closed = 0,
        Open = 1,
        Connecting = 2,
        Executing = 4,
        Fetching = 8,
        Broken = 16,
    }
    public sealed class StateChangeEventArgs
    {
        public ConnectionState CurrentState { get; }
        public ConnectionState OriginalState { get; }
    }
  *)

  //Corresponding Delphi Event Handler Caller method
  procedure StateChangeEventHandler(ASender : _ClrObject; StateChangeEventArgs : _ClrEventArgs); stdcall;
  var
    m_eventArgs : _ClrObject;
    m_currentState,
    m_originalState : Integer;

    procedure WriteStateChage(State : Integer);
    begin
      case State of
        0:  Console.Write_22('Closed');
        1:  Console.Write_22('Open');
        2:  Console.Write_22('Connecting');
        4:  Console.Write_22('Executing');
        8:  Console.Write_22('Fetching');
        16: Console.Write_22('Broken');
      end;
      Console.WriteLine;
    end;
  begin
    m_eventArgs :=  CoClrObject.Wrap(StateChangeEventArgs.EventArgs);
    m_currentState := m_eventArgs.GetPropertyValue('CurrentState');
    m_originalState := m_eventArgs.GetPropertyValue('OriginalState');

    Console.Write_22('Current State : ');
    WriteStateChage(m_currentState);
    Console.Write_22('Original State : ');
    WriteStateChage(m_originalState);
  end;

begin
  Console := CoConsole.CreateInstance;
  EventHandler := TEventHandler.Create;
  try
    Console.WriteLine_14('Hello! Welcome to .Net Runtime Library for Delphi.');
    Console.WriteLine_14('==================================================');
    Console.WriteLine_14('This program demonstrates how to handle events and get '+
                          'Delphi class type which added the event.');
    Console.WriteLine;
    try
      with EventHandler do begin
        LoadAssembly;
        CreateSQLConnectionTypeInstance;
        OnStateChangeEvent := StateChangeEventHandler; 
        OpenAndCloseSQLConnection; 
      end;  
    except
      on E: Exception do
        Console.WriteLine_15('Exception: {0}', E.Message);
    end;  
    Console.ReadKey;
  finally
    FreeAndNil(EventHandler);
  end;
end.

How to use .Net XML Classes to compare two element names

program XML;

{$APPTYPE CONSOLE}
{$R *.res}

uses
  CNClrLib.Xml,
  CNClrLib.Core;

var
  NameTable: _NameTable;
  Book, Price: Variant;
  Settings: _XmlReaderSettings;
  Reader: _XmlReader;
  ReaderHelper: _XmlReaderHelper;
  ObjectHelper: _ObjectHelper;
begin
  NameTable := CoNameTable.CreateInstance;

  Book := NameTable.Add('Book');
  Price := NameTable.Add('Price');

  // Create the Reader.
  Settings := CoXmlReaderSettings.CreateInstance;
  Settings.NameTable := NameTable.AsXmlNameTable;

  ReaderHelper := CoXmlReaderHelper.CreateInstance;
  Reader := ReaderHelper.Create_1('books.xml', Settings);

  Reader.MoveToContent;
  Reader.ReadToDescendant('Book');

  ObjectHelper := CoObjectHelper.CreateInstance;
  if ObjectHelper.ReferenceEquals(Book, Reader.Name) then
  begin
    // Do additional processing.
  end;
end.

How to use .Net Trace in delphi to indicate the beginning and the end of a program's execution

program Diagnostics;

{$WARN SYMBOL_PLATFORM OFF}
{$APPTYPE CONSOLE}
{$R *.res}

//  The following example uses Trace to indicate the beginning and the end of a program's execution.
//  The example also uses the Trace.Indent and Trace.Unindent methods to distinguish the tracing output.

uses
  SysUtils,
  CNClrLib.Diagnostics,
  CNClrLib.Core;

var
  Console: _Console;
  Trace: _Trace;
begin
  try
    Console := CoConsole.CreateInstance;

    Trace := CoTrace.CreateInstance;
    Trace.Listeners.Add(CoTextWriterTraceListener.CreateInstance(Console.Out_).AsTraceListener);
    Trace.AutoFlush := true;
    Trace.Indent;
    Trace.WriteLine('Entering Main');
    Console.WriteLine_14('Hello World.');
    Trace.WriteLine('Exiting Main');
    Trace.Unindent;
  except
    on E: Exception do
    begin
      Console.WriteLine_14(E.message);
    end;
  end;
end.

How to encrypt and decrypt sample data using the .Net RijndaelManaged

program RijndaelSecurity;

{$APPTYPE CONSOLE}

{$R *.res}

uses
  System.SysUtils,
  CNClrLib.EnumTypes,
  CNClrLib.Host,
  CNClrLib.Core;

function EncryptStringToBytes(plainText: string; Key, IV: _ByteArray): _ByteArray;
var
  rijAlg: _RijndaelManaged;
  encryptor: _ICryptoTransform;
  msEncrypt: _MemoryStream;
  csEncrypt: _CryptoStream;
  swEncrypt: _StreamWriter;
begin
  // Check arguments.
  if plainText.Length <= 0 then
    raise Exception.Create('plainText argument is empty');

  if (Key = nil) or (Key.Length <= 0) then
    raise Exception.Create('Key argument is empty or nil');

  if (IV = nil) or (IV.Length <= 0) then
    raise Exception.Create('IV argument is empty or nil');

  // Create an RijndaelManaged object with the specified key and IV.
  rijAlg := CoRijndaelManaged.CreateInstance;
  rijAlg.Key := Key;
  rijAlg.IV := IV;

  // Create a decrytor to perform the stream transform.
  encryptor := rijAlg.CreateEncryptor(rijAlg.Key, rijAlg.IV);

  // Create the streams used for encryption.
  msEncrypt := CoMemoryStream.CreateInstance;
  csEncrypt := CoCryptoStream.CreateInstance(msEncrypt.AsStream, encryptor, csmWrite);

  swEncrypt := coStreamWriter.CreateInstance(csEncrypt.AsStream);
  //Write all data to the stream.
  swEncrypt.Write_3(plainText);
  swEncrypt.Close;

  // Return the encrypted bytes from the memory stream.
  result := msEncrypt.ToArray;
  msEncrypt.Close;
  csEncrypt.Close;
  swEncrypt.Close;
end;

function DecryptStringFromBytes(cipherText, Key, IV: _ByteArray): string;
var
  rijAlg: _RijndaelManaged;
  decryptor: _ICryptoTransform;
  msDecrypt: _MemoryStream;
  csDecrypt: _CryptoStream;
  srDecrypt: _StreamReader;
begin
  // Check arguments.
  if (cipherText = nil) or (cipherText.Length <= 0) then
    raise Exception.Create('cipherText argument is empty or nil');

  if (Key = nil) or (Key.Length <= 0) then
    raise Exception.Create('Key argument is empty or nil');

  if (IV = nil) or (IV.Length <= 0) then
    raise Exception.Create('IV argument is empty or nil');

  // Create an RijndaelManaged object
  // with the specified key and IV.
  rijAlg := CoRijndaelManaged.CreateInstance;
  rijAlg.Key := Key;
  rijAlg.IV := IV;

  // Create a decrytor to perform the stream transform.
  decryptor := rijAlg.CreateDecryptor(rijAlg.Key, rijAlg.IV);

  // Create the streams used for decryption.
  msDecrypt := CoMemoryStream.CreateInstance(cipherText);

  csDecrypt := CoCryptoStream.CreateInstance(msDecrypt.AsStream, decryptor, csmRead);

  srDecrypt := CoStreamReader.CreateInstance(csDecrypt.AsStream);

  // Read the decrypted bytes from the decrypting stream and place them in a string.
  result := srDecrypt.ReadToEnd;

  msDecrypt.Close;
  csDecrypt.Close;
  srDecrypt.Close;
end;


var
  key: Char;
  original,
  roundtrip: string;
  myRijndael: _RijndaelManaged;
  encrypted: _ByteArray;
begin
  try
    original := 'Here is some data to encrypt!';

    // Create a new instance of the RijndaelManaged
    // class.  This generates a new key and initialization
    // vector (IV).
    myRijndael := CoRijndaelManaged.CreateInstance;
    myRijndael.GenerateKey;
    myRijndael.GenerateIV;

    // Encrypt the string to an array of bytes.
    encrypted := EncryptStringToBytes(original, myRijndael.Key, myRijndael.IV);

    // Decrypt the bytes to a string.
    roundtrip := DecryptStringFromBytes(encrypted, myRijndael.Key, myRijndael.IV);

    //Display the original data and the decrypted data.
    Writeln('Original:   %s', original);
    Writeln('Round Trip: %s', roundtrip);

    Writeln('Press any key to continue.....');
    Readln(key);
  except
    on E: Exception do
      Writeln(E.ClassName, ': ', E.Message);
  end;
end.
To access the full source code examples, kindly download the Compiled demo zip file from the Download Page.