.Net Component Suite for Delphi

Flexible and lightweight libraries of reusable codes which enables object sharing between projects or applications. Built on .Net framework and advanced .Net functionalities and delivers innovative features and leverage many cutting-edge desktop technologies. Contains top-performing .Net Components in Delphi for every aspect of Delphi desktop development.

Buy Download
.Net VCL 4 Delphi




DirectoryEntry

The DirectoryEntry component (TCnDirectoryEntry) is a .Net component in Delphi which presents a node or object in the Active Directory hierarchy. The Add method creates a request to create a new entry in the container. The Find method returns the child with the specified name. The Remove method deletes a child DirectoryEntry from this collection.

Use this component for binding to objects, or reading and updating attributes. TCnDirectoryEntry provides support for life-cycle management and navigation methods. These include creating, deleting, renaming, moving a child node, and enumerating children. After you modify a node, you must commit your changes in order for them to be saved to the tree. For more information, see the CommitChanges property.

TCnDirectoryEntry can be used to access regular entries and some, but not all, information from schema entries.

To create a directory entry in the hierarchy, use the Children property. The Children property is a collection that provides an Add method, through which you add a node to the collection directly below the parent node that you are currently bound to. When adding a node to the collection, you must specify a name for the new node and the name of a schema template that you want to associate with the node. For example, you might want to use a schema titled "Computer" to add new computers to the hierarchy.

This component also contains attribute caching, which can be useful for optimizing network traffic. To use attribute caching, see the UsePropertyCache property.

The TCnDirectoryEntry component can be used with any of the Active Directory Domain Services service providers. Some of the current providers are Internet Information Services (IIS), Lightweight Directory Access Protocol (LDAP), Novell NetWare Directory Service (NDS), and WinNT.

The following example shows how to create a new TCnDirectoryEntry object with the specified path, then creates a new entry in the container and saves it. It attempts to retrieve the newly- created entry.

program Project1;

{$APPTYPE CONSOLE}

{$R *.res}

uses
  System.SysUtils,
  CNClrLib.Component.DirectoryEntry;

var
  myDE: TCnDirectoryEntry;
  myEntry1: _DirectoryEntry;
  myEntry2: _DirectoryEntry;
  myEntries: _DirectoryEntries;
  strPath: String;
begin
  try
    strPath := 'LDAP://DC=fabrikam,DC=com';

    // Create a 'DirectoryEntry' object with the given path.
    myDE := TCnDirectoryEntry.Create(nil);
    myDE.Path := strPath;

    myEntries := myDE.Children;

    // Create a new entry in the container.
    myEntry1 := myEntries.Add('CN=Sample Entry', myDE.SchemaClassName);
    // Save changes in the 'Active Directory Domain Services' store.
    myEntry1.CommitChanges();

    // Find a child in the 'DirectoryEntries' collection which has the
    // specified name and type.
    myEntry2 := myEntries.Find_1('CN=Sample Entry', myDE.SchemaClassName);
    Writeln(myEntry2.Name + ' is found in container.');
  except
    on E: Exception do
      Writeln('The following exception was raised : ', E.Message);
  end;
end. 

DirectorySearcher

The DirectorySearcher component (TCnDirectorySearcher) is a .Net component in Delphi that performs queries against the Active Directory.

The code below searches the MCBCorp.Com Windows Active Directory domain. It outputs all of the Active Directory objects and their properties, and then all the data inside, recursively.

 
program Project1;

{$APPTYPE CONSOLE}

{$R *.res}

uses
  System.SysUtils,
  CNClrLib.Component.DirectorySearcher,
  CNClrLib.Component.DirectoryEntry,
  CNCLrLib.Core, CNClrLib.DirectoryServices;

var
  entry: TCnDirectoryEntry;
  mySearcher: TCnDirectorySearcher;
  searchResults: _SearchResultCollection;
  resEnt: _SearchResult;
  key: String;
  PropertyNames: _IEnumerator;
  PropertValues: _PropertyValueCollection;
  I: Integer;
begin
  try
    // the name of the domain
    entry := TCnDirectoryEntry.Create(nil);
    entry.Path := 'LDAP://MCBcorp, DC=com';
    Writeln('Name = ' + entry.Name);
    Writeln('Path = ' + entry.Path);
    Writeln('SchemaClassName = ' + entry.SchemaClassName);
    Writeln('Properties:');
    Writeln('=====================================');

    PropertyNames := entry.Properties.PropertyNames.AsIEnumerable.GetEnumerator;
    while PropertyNames.MoveNext do
    begin
      try
        key := PropertyNames.Current;
        Writeln(chr(9) + key + ' = ');
        PropertValues := entry.Properties.Item[key];
        for I := 0 to PropertValues.Count - 1 do
          Writeln(chr(9) + chr(9) + PropertValues[I]);
      except
      end;
    end;

    mySearcher := TCnDirectorySearcher.Create(nil);
    mySearcher.SearchRoot := entry;
    mySearcher.Filter := '(objectClass=*)';
    Writeln('Active Directory Information');
    Writeln('=====================================');

    searchResults := mySearcher.FindAll();
    for I := 0 to searchResults.Count - 1 do
    begin
      try
        resEnt := searchResults[I];
        Writeln(resEnt.GetDirectoryEntry().Name);
        Writeln(resEnt.GetDirectoryEntry().Path);
        Writeln(resEnt.GetDirectoryEntry().NativeGuid);
        Writeln('===================================');
      except
      end;
    end;
  except
    on E: Exception do
      Writeln('The following exception was raised : ', E.Message);
  end;
end.

You can create entries and properties in the Active Directory. You simply create a new directory or use an existing one with the TCnDirectoryEntry component and then assign the values you want to the specific properties. When you have finished assigning the values, call the CommitChanges() method to cause the changes to occur in the Active Directory. The sample code below achieves this update operation.

The sample code below shows you how to pick individual properties of Active Directory objects! Active Directory properties are an array of adjustable object property members with specific names determined by the Active Directory schema. For example, you can set the following properties for objects: sn, givenName, title, or mycustomproperty. The properties change depending on their class definition in the Active Directory schema.

Updating Active Directory

program Project1;

{$APPTYPE CONSOLE}

{$R *.res}

uses
  System.SysUtils,
  CNClrLib.Component.DirectoryEntry;

var
  entry: TCnDirectoryEntry;
begin
  try
    // get the handle to MCBuser from Active Directory database
    entry := TCnDirectoryEntry.Create(nil);
    entry.Path := 'LDAP://DC=MyDC, O=MyOrg, OU=MyOU, cn=MCBuser';
    entry.Password := 'mcb'; // password
    entry.Properties['myprop'][0] := 'myvalue'; // properties
    entry.CommitChanges();
  except
    on E: Exception do
      Writeln('The following exception was raised : ', E.Message);
  end;
end.

Use a TCnDirectorySearcher object to search and perform queries against an Active Directory Domain Services hierarchy using Lightweight Directory Access Protocol (LDAP). LDAP is the only system-supplied Active Directory Service Interfaces (ADSI) provider that supports directory searching. An administrator can make, alter, and delete objects that are found in the hierarchy.

When you create an instance of TCnDirectorySearcher, you specify the root you want to retrieve, and an optional list of properties to retrieve. The SearchRoot property enables you to set additional properties to do the following tasks:
  • Cache the search results on the local computer. Set the CacheResults property to true to store directory information on the local computer. Updates are made to this local cache and committed to Active Directory Domain Services only when the TCnDirectoryEntry.CommitChanges method is called.
  • Specify the length of time to search, using the ServerTimeLimit property.
  • Retrieve attribute names only. Set the PropertyNamesOnly property to true to retrieve only the names of attributes to which values have been assigned.
  • Perform a paged search. Set the PageSize property to specify the maximum number of objects that are returned in a paged search. If you do not want to perform a paged search, set the PageSize property to its default of zero.
  • Specify the maximum number of entries to return, using the SizeLimit property. If you set the SizeLimit property to its default of zero, the server-determined default is 1000 entries.

The Filter property of the TCnDirectorySearcher component gets or sets the LDAP filter string format. The FindAll method in the DirectorySearcher class executes the search and returns a collection of entries found.

ADChangeNotifier

The ADChangeNotifier component (TCnADChangeNotifier) is a .Net component in Delphi which monitors Active Directory changes on Users and Groups and raises events notification when a group or User information changes. The changes required to trigger event includes, adding new user or group, modifying existing group or user and delete existng groups or users.

The following code example demonstrates how to use this components to raise event notifications when an active directory user or group changes.

program Project12;

{$APPTYPE CONSOLE}

{$R *.res}

uses
  System.SysUtils,
  CNClrLib.Host,
  CNClrLib.Core,
  CNClrLib.Control.EnumTypes,
  CNClrLib.Component.ADChangeNotifier;

var
  adNotifier: TCnADChangeNotifier;

type
  TADChangeNotifierObjectChanged = class
    class procedure DoOnObjectChanged(Sender: TObject; E: _ObjectChangedEventArgs);
  end;

class procedure TADChangeNotifierObjectChanged.DoOnObjectChanged(Sender: TObject;
  E: _ObjectChangedEventArgs);
var
  attributeEnum: _IEnumerator;
  attrName: String;
  values: _ObjectArray;
  I: Integer;
begin
  attributeEnum := E.Result.Attributes.AttributeNames.AsIEnumerable.GetEnumerator;
  Writeln(E.Result.DistinguishedName);
  while attributeEnum.MoveNext do
  begin
    //For more information about Active Directory Attributes, see http://www.kouti.com/tables/userattributes.htm
    attrName := attributeEnum.Current;

    Writeln(attrName + 'AD Attribute:');
    if attrName = 'whenCreated' then //Object created
    begin
      Writeln(chr(9) + 'Object Created');
    end
    else if attrName = 'whenChanged' then  //Object Modified
    begin
      Writeln(chr(9) + 'Object Modified');
    end
    else if attrName = 'isDeleted' then
    begin
      Writeln(chr(9) + 'Object is Deleted');
    end;

    values := E.Result.Attributes[attrName].GetValues(TClrAssembly.GetType('System.String'));
    for I := 0 to values.Length - 1 do
    begin
      Writeln(chr(9) + chr(9) + values[I]);
    end;
  end;
  Writeln('-------------------------------------------------------');
  Writeln('');
end;

begin
  try
    adNotifier := TCnADChangeNotifier.Create(nil);
    try
      adNotifier.AuthType := TAuthType.autNegotiate;
      adNotifier.Server := 'example.com';//Specify the LDAP server
      adNotifier.OnObjectChanged := TADChangeNotifierObjectChanged.DoOnObjectChanged;

      //Register and monitor the active directory search request
      adNotifier.RegisterNotification('cn=madhuka+sn=udantha,ou=users,dc=example,dc=com', '(objectClass=*)', TSearchScope.ssBase, []);

      //OR
      //
      //// Alternatively, you can add the search request this way;
      //with adNotifier.SearchRequests.Add do
      //begin
      //  DistinguishedName := 'cn=madhuka+sn=udantha,ou=users,dc=example,dc=com';
      //  Filter := '(objectClass=*)';
      //  Scope := TSearchScope.ssBase;
      //end;
      //adNotifier.RegisterNotifications;
    finally
      adNotifier.AbortNotifications;
    end;
  except
    on E: Exception do
      Writeln('The following exception was raised : ', E.Message);
  end;
end.

One of the challenges for Active Directory is knowing how to monitor changes to it. While you may think that when you're lone wolf working as a main Administrator you don't need to monitor your Active Directory it may not always be the case. Things get even more complicated with more Administrators having access to your AD or Service Desk agents having some access rights to part of it. The ADChangeNotifier component helps resolve these challenges by monitoring the changes in the Active directory groups and users and raises an event notification if a change occurs.

The ADChangeNotifier component monitor for changes that happen on users and groups in Active Directory. It can tell you:
  • When and who changed the group membership of any group within your Active Directory Domain.
  • When and who changed the user data including Password, UserPrincipalName, SamAccountName, and so on…
  • When and who changed passwords
  • When and who locked out account and where did it happen

BackgroundWorker

The BackgroundWorker component (TCnBackgroundWorker) is a .Net component in Delphi which enables your VCL form or control to run an operation asynchronously.

The following code example demonstrates the basics of the TCnBackgroundWorker component for executing a time-consuming operation asynchronously. The following illustration shows an example of the output.

To try this code, create a VCL Form application. Add a Label control named resultLabel and add two Button controls named startAsyncButton and cancelAsyncButton. Create Click event handlers for both buttons. From the Tool Palette, add a TCnBackgroundWorker component named backgroundWorker1. Create DoWork, ProgressChanged, and RunWorkerCompleted event handlers for the BackgroundWorker. In the code for the form, replace the existing code with the following code.

***************** DFM **********************

object BackgroundWorkerForm: TBackgroundWorkerForm
  Left = 0
  Top = 0
  Caption = 'BackgroundWorker'
  ClientHeight = 131
  ClientWidth = 292
  Color = clBtnFace
  Font.Charset = DEFAULT_CHARSET
  Font.Color = clWindowText
  Font.Height = -11
  Font.Name = 'Tahoma'
  Font.Style = []
  OldCreateOrder = False
  OnCreate = FormCreate
  PixelsPerInch = 96
  TextHeight = 13
  object resultLabel: TLabel
    Left = 64
    Top = 24
    Width = 17
    Height = 13
    Caption = '0%'
  end
  object startAsyncButton: TButton
    Left = 40
    Top = 72
    Width = 99
    Height = 25
    Caption = 'Start'
    TabOrder = 0
    OnClick = startAsyncButtonClick
  end
  object cancelAsyncButton: TButton
    Left = 161
    Top = 72
    Width = 104
    Height = 25
    Caption = 'Cancel'
    TabOrder = 1
    OnClick = cancelAsyncButtonClick
  end
  object CnBackgroundWorker1: TCnBackgroundWorker
    OnDoWork = CnBackgroundWorker1DoWork
    OnProgressChanged = CnBackgroundWorker1ProgressChanged
    OnRunWorkerCompleted = CnBackgroundWorker1RunWorkerCompleted
    Left = 200
    Top = 16
  end
end
***************** PAS **********************

unit BackgroundWorker;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, CNClrLib.Control.Base,
  CNClrLib.Component.BackgroundWorker;

type
  TBackgroundWorkerForm = class(TForm)
    startAsyncButton: TButton;
    cancelAsyncButton: TButton;
    resultLabel: TLabel;
    CnBackgroundWorker1: TCnBackgroundWorker;
    procedure startAsyncButtonClick(Sender: TObject);
    procedure cancelAsyncButtonClick(Sender: TObject);
    procedure CnBackgroundWorker1DoWork(Sender: TObject; E: _DoWorkEventArgs);
    procedure CnBackgroundWorker1ProgressChanged(Sender: TObject;
      E: _ProgressChangedEventArgs);
    procedure CnBackgroundWorker1RunWorkerCompleted(Sender: TObject;
      E: _RunWorkerCompletedEventArgs);
    procedure FormCreate(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  BackgroundWorkerForm: TBackgroundWorkerForm;

implementation

{$R *.dfm}

procedure TBackgroundWorkerForm.cancelAsyncButtonClick(Sender: TObject);
begin
  if CnBackgroundWorker1.WorkerSupportsCancellation then
  begin
    // Cancel the asynchronous operation.
    CnBackgroundWorker1.CancelAsync();
  end;
end;

procedure TBackgroundWorkerForm.CnBackgroundWorker1DoWork(Sender: TObject;
  E: _DoWorkEventArgs);
var
  worker: TCnBackgroundWorker;
  I: Integer;
begin
  worker := Sender as TCnBackgroundWorker;

  for I := 0 to 10 do
  begin
    if worker.CancellationPending then
    begin
      E.Cancel := True;
      Break;
    end
    else
    begin
      // Perform a time consuming operation and report progress.
      Sleep(500);
      worker.ReportProgress(I * 10);
    end;
  end;
end;

procedure TBackgroundWorkerForm.CnBackgroundWorker1ProgressChanged(Sender: TObject;
  E: _ProgressChangedEventArgs);
begin
 resultLabel.Caption := (IntToStr(E.ProgressPercentage) + '%');
end;

procedure TBackgroundWorkerForm.CnBackgroundWorker1RunWorkerCompleted(Sender: TObject;
  E: _RunWorkerCompletedEventArgs);
begin
  if e.Cancelled then
    resultLabel.Caption := 'Canceled!'
  else if e.Error <> nil then
    resultLabel.Caption := 'Error: ' + e.Error.Message
  else
    resultLabel.Caption := 'Done!';
end;

procedure TBackgroundWorkerForm.FormCreate(Sender: TObject);
begin
  CnBackgroundWorker1.WorkerReportsProgress := true;
  CnBackgroundWorker1.WorkerSupportsCancellation := true;
end;

procedure TBackgroundWorkerForm.startAsyncButtonClick(Sender: TObject);
begin
  if not CnBackgroundWorker1.IsBusy then
  begin
    // Start the asynchronous operation.
    CnBackgroundWorker1.RunWorkerAsync();
  end;
end;

end.

The TCnBackgroundWorker component allows you to run an operation on a separate, dedicated thread. Time-consuming operations like downloads and database transactions can cause your user interface (UI) to seem as though it has stopped responding while they are running. When you want a responsive UI and you are faced with long delays associated with such operations, the TCnBackgroundWorker provides a convenient solution.

To execute a time-consuming operation in the background, create a TCnBackgroundWorker and listen for events that report the progress of your operation and signal when your operation is finished. You can create the TCnBackgroundWorker programmatically or you can drag it onto your form from the Tool Palette.

To set up for a background operation, add an event handler for the OnDoWork event. Call your time-consuming operation in this event handler. To start the operation, call RunWorkerAsync. To receive notifications of progress updates, handle the OnProgressChanged event. To receive a notification when the operation is completed, handle the OnRunWorkerCompleted event.

Note:
You must be careful not to manipulate any user-interface objects in your OnDoWork event handler. Instead, communicate to the user interface through the OnProgressChanged and OnRunWorkerCompleted events.

If your background operation requires a parameter, call RunWorkerAsync with your parameter. Inside the OnDoWork event handler, you can extract the parameter from the DoWorkEventArgs.Argument property.

EventLog

The EventLog component (TCnEventLog) is a .Net component in Delphi which allows you to access or customize Windows NT, 2000, and XP event logs, and record information about important software or hardware events. Using the TCnEventLog component, you can read from existing logs, write entries to logs, create or delete event sources, delete logs, and respond to log entries.

Event logging provides a standard, centralized way for you to have your applications record important software and hardware events. Windows supplies a standard user interface for viewing the event logs (you can open Event Viewer MMC from Control Panel?Administrative Tools?Computer Management?Event Viewer). Using the TCnEventLog component, you can easily connect to existing event logs on both local and remote computers and read entries from those logs programmatically.

The example below illustrates how you can create an event source, check the existence of the Application and Demo event sources (which will be created by us) as an event log or in Event Viewer, enumerate and read event log entries, write entries to a log, and monitor the event log source for any new entries written to the log.

program Project1;

{$APPTYPE CONSOLE}

{$R *.res}

uses
  System.SysUtils,
  CNClrLib.Core,
  CNClrLib.Control.EnumTypes,
  CNClrLib.Component.EventLog;

var
  myLog: TCnEventLog;
  Console: _Console;
  I: Integer;

type
  TEventLogEntryWritten = class
    class procedure DoOnEntryWritten(Sender: TObject; E: _EntryWrittenEventArgs);
  end;

class procedure TEventLogEntryWritten.DoOnEntryWritten(Sender: TObject;
  E: _EntryWrittenEventArgs);
begin
  Console.WriteLine_14('written entry: ' + e.Entry.Message);
end;
begin
  try
    Console := CoConsole.CreateInstance;
    // check for the event log source on specified machine
    // the Application event log source on MCBcomputer
    if not TCnEventLog.Exists('Application', 'MCBcomputer') then
    begin
      Console.WriteLine_14('The log does not exist!');
      exit;
    end;

    myLog := TCnEventLog.Create(nil);
    myLog.Log := 'Application';
    myLog.MachineName := 'MCBcomputer';
    Console.WriteLine_14('There are ' + myLog.Entries.Count.ToString + ' entr[y|ies] in the Application log:');

    for I := 0 to myLog.Entries.Count -1 do
    begin
      Console.WriteLine_14(Chr(9) + 'Entry: ' + myLog.Entries[I].Message);
    end;

    // check for Demo event log source existence
    // create it if it not exist
    if not TCnEventLog.SourceExists('Demo') then
    begin
      TCnEventLog.CreateEventSource('Demo', 'Demo');
    end;

    TCnEventLog.WriteEntry('AnySource', 'writing error to demo log.', TEventLogEntryType.eletError);
    Console.WriteLine_14('Monitoring of Application event log began...');
    Console.WriteLine_14('Press ''q'' and ''Enter'' to quit');

    while Console.ReadKey.KeyChar.ToString <> 'q' do
    begin
      // Now we will monitor the new entries that will be written.
      // When you create an EntryWrittenEventHandler delegate
      // you identify the method that will handle the event.
      myLog.OnEntryWritten := TEventLogEntryWritten.DoOnEntryWritten;

      // EnableRaisingEvents gets or sets a value indicating whether the
      // EventLog instance receives EntryWritten event notifications.
      myLog.EnableRaisingEvents := True;
    end;
  except
    on E: Exception do
      Console.WriteLine_15('The following exception was raised : {0}', E.Message);
  end;
end.

In addition to providing access to individual event logs and their entries, the TCnEventLog component lets you access the collection of all event logs. You can use the static members of TCnEventLog to delete logs, get log lists, create or delete a source, or determine if a computer already contains a particular source.

There are three default event logs: Application, System, and Security. A Security log is read-only. Other applications and services you install, such as Active Directory, might have additional event logs.

There are security considerations when using the TCnEventLog component. TCnEventLog requires EventLogPermission permissions for specific actions in the .NET Framework 2.0 and later versions, or full trust in the .NET Framework 1.0 and 1.1. We recommend that EventLogPermission not be granted to partially trusted code. You should never pass any event log object, including EventLogEntryCollection and EventLogEntry objects, to less trusted code. For example, creating an EventLog object, writing an entry, and then passing the EventLog object to partially trusted code can create a security issue, because the ability to read and write to the event log allows code to perform actions such as issuing event log messages in the name of another application.

To read from an event log, specify the log name (Log property) and server computer name (MachineName property for the event log. If you don't specify the server computer name, the local computer, ".", is assumed. It's not necessary to specify the event source (Source property), because a source is required only for writing to logs. The Entries property is automatically populated with the event log's list of entries.

To write to an event log, specify or create an event source (Source property). You must have administrative credentials on the computer to create a new event source. The event source registers your application with the event log as a valid source of entries. You can use the event source to write to only one log at a time. The Source property can be any random string, but the name must be distinct from other sources on the computer. The event source is typically the name of the application or another identifying string. Trying to create a duplicate Source value throws an exception. However, a single event log can be associated with multiple sources.

If the event source for the event log associated with the TCnEventLog instance doesn't exist, a new event source is created. To create an event source in Windows Vista and later or Windows Server 2003, you must have administrative credentials.

Applications and services should write to the Application log or to a custom log. Device drivers should write to the System log. If you do not explicitly set the Log property, the event log defaults to the Application log.

Use the WriteEvent and WriteEntry methods to write events to an event log. You must specify an event source to write events; you must create and configure the event source before writing the first entry with the source.

If your application writes string values directly to the event log, you do not have to set the resource file properties for the source. The source must be configured either for writing localized entries or for writing direct strings. If your application writes entries using both resource identifiers and string values, you must register two separate sources. For example, configure one source with resource files, and then use that source in the WriteEvent method to write entries using resource identifiers to the event log. Then create a different source without resource files, and use that source in the WriteEntry method to write strings directly to the event log using that source.

When writing events, you must at least specify either a message string or the resource identifier for a message string. Other event properties are optional. Examples of optional event settings include the following:
  • You can set the EventLogEntryType to specify the icon that the Event Viewer displays for the entry.
  • You can specify a category identifier for the event, if your application uses categories for filtering the events.
  • You can attach binary data to your event entry if you want to associate additional information with a given event.

FileSystemWatcher

The FileSystemWatcher component (TCnFileSystemWatcher) is a .Net component in Delphi which Listens to the file system change notifications and raises events when a directory, or file in a directory, changes.

The following example creates a TCnFileSystemWatcher to watch the directory specified at run time. The component is set to watch for changes in LastWrite and LastAccess time, the creation, deletion, or renaming of text files in the directory. If a file is changed, created, or deleted, the path to the file prints to the console. When a file is renamed, the old and new paths print to the console.

program Project1;

{$APPTYPE CONSOLE}

{$R *.res}

uses
  System.SysUtils,
  CNClrLib.Core,
  CNClrLib.Enums,
  CNClrLib.Control.EnumTypes,
  CNClrLib.Component.FileSystemWatcher;


var
  watcher: TCnFileSystemWatcher;
  Console: _Console;
  path: String;

type
  TFileSystemWatcherEventHandlers = class
    class procedure DoOnChanged(Sender: TObject; E: _FileSystemEventArgs);
    class procedure DoOnRenamed(Sender: TObject; E: _RenamedEventArgs);
  end;

class procedure TFileSystemWatcherEventHandlers.DoOnChanged(Sender: TObject;
  E: _FileSystemEventArgs);
var
  strChangeType: String;
begin
  // Specify what is done when a file is changed, created, or deleted.
  case E.ChangeType of
    WatcherChangeTypes_Created: strChangeType := 'Created';
    WatcherChangeTypes_Deleted: strChangeType := 'Deleted';
    WatcherChangeTypes_Changed: strChangeType := 'Changed';
    WatcherChangeTypes_Renamed: strChangeType := 'Renamed';
  else
    strChangeType := 'Created, Deleted, Changed, Renamed';
  end;
  Console.WriteLine_14('File: ' +  e.FullPath + ' ' + strChangeType);
end;

class procedure TFileSystemWatcherEventHandlers.DoOnRenamed(Sender: TObject;
  E: _RenamedEventArgs);
begin
  // Specify what is done when a file is renamed.
  Console.WriteLine_17('File: {0} renamed to {1}', e.OldFullPath, e.FullPath);
end;

begin
  try
    Console := CoConsole.CreateInstance;
    path := 'C:\Temp';

    // Create a new FileSystemWatcher and set its properties.
    watcher := TCnFileSystemWatcher.Create(nil);
    watcher.Path := path;
    // Watch for changes in LastAccess and LastWrite times, and
    // the renaming of files or directories. */
    watcher.NotifyFilter := [TNotifyFilters.nfLastAccess, TNotifyFilters.nfLastWrite,
                             TNotifyFilters.nfFileName, TNotifyFilters.nfDirectoryName];
        // Only watch text files.
    watcher.Filter := '*.txt';

    // Add event handlers.
    watcher.OnChanged := TFileSystemWatcherEventHandlers.DoOnChanged;
    watcher.OnCreated := TFileSystemWatcherEventHandlers.DoOnChanged;
    watcher.OnDeleted := TFileSystemWatcherEventHandlers.DoOnChanged;
    watcher.OnRenamed := TFileSystemWatcherEventHandlers.DoOnRenamed;

    // Begin watching.
    watcher.EnableRaisingEvents := True;

    // Wait for the user to quit the program.
    Console.WriteLine_14('Press ''q'' to quit the sample.');
    while Console.ReadKey.KeyChar.ToString <> 'q' do;
  except
    on E: Exception do
      Console.WriteLine_15('The following exception was raised : {0}', E.Message);
  end;
end.

Use TCnFileSystemWatcher to watch for changes in a specified directory. You can watch for changes in files and subdirectories of the specified directory. You can create a component to watch files on a local computer, a network drive, or a remote computer.

To watch for changes in all files, set the Filter property to an empty string ('') or use wildcards ('*.*'). To watch a specific file, set the Filter property to the file name. For example, to watch for changes in the file MyDoc.txt, set the Filter property to 'MyDoc.txt'. You can also watch for changes in a certain type of file. For example, to watch for changes in text files, set the Filter property to '*.txt'.

There are several types of changes you can watch for in a directory or file. For example, you can watch for changes in Attributes, the LastWrite date and time, or the Size of files or directories. This is done by setting the NotifyFilter property to one of the NotifyFilters values.

You can watch for renaming, deletion, or creation of files or directories. For example, to watch for renaming of text files, set the Filter property to '*.txt' and call the WaitForChanged method with a Renamed specified for its parameter.

The Windows operating system notifies your component of file changes in a buffer created by the TCnFileSystemWatcher. If there are many changes in a short time, the buffer can overflow. This causes the component to lose track of changes in the directory, and it will only provide blanket notification. Increasing the size of the buffer with the InternalBufferSize property is expensive, as it comes from non-paged memory that cannot be swapped out to disk, so keep the buffer as small yet large enough to not miss any file change events. To avoid a buffer overflow, use the NotifyFilter and IncludeSubdirectories properties so you can filter out unwanted change notifications.

Please note the following when using the TCnFileSystemWatcher component.
  • Hidden files are not ignored.
  • In some systems, FileSystemWatcher reports changes to files using the short 8.3 file name format. For example, a change to "LongFileName.LongExtension" could be reported as "LongFil~.Lon".
  • This component contains a link demand and an inheritance demand at the class level that applies to all members. A SecurityException is thrown when either the immediate caller or the derived class does not have full-trust permission.
  • The maximum size you can set for the InternalBufferSize property for monitoring a directory over the network is 64 KB.

Copying and moving folders

The operating system and FileSystemWatcher object interpret a cut-and-paste action or a move action as a rename action for a folder and its contents. If you cut and paste a folder with files into a folder being watched, the FileSystemWatcher object reports only the folder as new, but not its contents because they are essentially only renamed.

To be notified that the contents of folders have been moved or copied into a watched folder, provide OnChanged and OnRenamed event handler methods as suggested in the following table.

Event Handler Events Handled Performs
ChangedEventHandler OnChanged, OnCreated, OnDeleted Report changes in file attributes, created files, and deleted files.
RenamedEventHandler OnRenamed List the old and new paths of renamed files and folders, expanding recursively if needed.

Events and Buffer Sizes

Note that several factors can affect which file system change events are raised, as described by the following:
  • Common file system operations might raise more than one event. For example, when a file is moved from one directory to another, several OnChanged and some OnCreated and OnDeleted events might be raised. Moving a file is a complex operation that consists of multiple simple operations, therefore raising multiple events. Likewise, some applications (for example, antivirus software) might cause additional file system events that are detected by TCnFileSystemWatcher.
  • The TCnFileSystemWatcher can watch disks as long as they are not switched or removed. The TCnFileSystemWatcher does not raise events for CDs and DVDs, because time stamps and properties cannot change. Remote computers must have one of the required platforms installed for the component to function properly.
  • If multiple TCnFileSystemWatcher objects are watching the same UNC path in Windows XP prior to Service Pack 1, or Windows 2000 SP2 or earlier, then only one of the objects will raise an event. On machines running Windows XP SP1 and newer, Windows 2000 SP3 or newer or Windows Server 2003, all TCnFileSystemWatcher objects will raise the appropriate events.
Note that a TCnFileSystemWatcher may miss an event when the buffer size is exceeded. To avoid missing events, follow these guidelines:
  • Increase the buffer size by setting the InternalBufferSize property.
  • Avoid watching files with long file names, because a long file name contributes to filling up the buffer. Consider renaming these files using shorter names.
  • Keep your event handling code as short as possible.

Process

The Process component (TCnProcess) is a .Net component in Delphi which Provides access to local and remote processes and enables you to start and stop local system processes.

The following example uses an instance of the TCnProcess component to start a process.

program Project1;

{$APPTYPE CONSOLE}

{$R *.res}

uses
  System.SysUtils,
  CNClrLib.Component.Process;

var
  myProcess: TCnProcess;

begin
  try
    myProcess := TCnProcess.Create(nil);

    myProcess.StartInfo.UseShellExecute := False;
    // You can start any process, HelloWorld is a do-nothing example.
    myProcess.StartInfo.FileName := 'C:\\HelloWorld.exe';
    myProcess.StartInfo.CreateNoWindow := True;
    myProcess.Start();
    // This code assumes the process you are starting will terminate itself.
    // Given that is is started without a window so you cannot terminate it
    // on the desktop, it must terminate itself or you can do it programmatically
    // from this application using the Kill method.
  except
    on E: Exception do
      Writeln('The following exception was raised : ', E.Message);
  end;
end.

The following example uses the TCnProcess component itself and a static Start method to start a process.

program Project1;

{$APPTYPE CONSOLE}

{$R *.res}

uses
  System.SysUtils,
  CNClrLib.Core,
  CNClrLib.Enums,
  CNClrLib.Control.EnumTypes,
  CNClrLib.Component.Process;

// Opens the Internet Explorer application.
procedure OpenApplication(myFavoritesPath: string);
begin
  // Start Internet Explorer. Defaults to the home page.
  TCnProcess.Start('IExplore.exe');

  // Display the contents of the favorites folder in the browser.
  TCnProcess.Start(myFavoritesPath);
end;

// Opens urls and .html documents using Internet Explorer.
procedure OpenWithArguments();
begin
  // url's are not considered documents. They can only be opened
  // by passing them as arguments.
  TCnProcess.Start('IExplore.exe', 'www.northwindtraders.com');

  // Start a Web page using a browser associated with .html and .asp files.
  TCnProcess.Start('IExplore.exe', 'C:\myPath\myFile.htm');
  TCnProcess.Start('IExplore.exe', 'C:\myPath\myFile.asp');
end;

// Uses the ProcessStartInfo class to start new processes,
// both in a minimized mode.
procedure OpenWithStartInfo();
var
  startInfo: TClrProcessStartInfo;
begin
  startInfo := TClrProcessStartInfo.Create('IExplore.exe');
  try
    startInfo.WindowStyle := TProcessWindowStyle.pwsMinimized;
    TCnProcess.Start(startInfo);

    startInfo.Arguments := 'www.northwindtraders.com';
    TCnProcess.Start(startInfo);
  finally
    startInfo.Free;
  end;
end;

var
  myProcess: TCnProcess;
  myFavoritesPath: String;
begin
  try
    // Get the path that stores favorite links.
    myFavoritesPath := CoEnvironment.CreateInstance.GetFolderPath(SpecialFolder_Favorites);

    myProcess := TCnProcess.Create(nil);

    OpenApplication(myFavoritesPath);
    OpenWithArguments();
    OpenWithStartInfo();
  except
    on E: Exception do
      Writeln('The following exception was raised : ', E.Message);
  end;
end.

A TCnProcess component provides access to a process that is running on a computer. A process, in the simplest terms, is a running app. A thread is the basic unit to which the operating system allocates processor time. A thread can execute any part of the code of the process, including parts currently being executed by another thread.

The TCnProcess component is a useful tool for starting, stopping, controlling, and monitoring apps. You can use the TCnProcess component, to obtain a list of the processes that are running, or you can start a new process. A TCnProcess component is used to access system processes. After a TCnProcess component has been initialized, it can be used to obtain information about the running process. Such information includes the set of threads, the loaded modules (.dll and .exe files), and performance information such as the amount of memory the process is using.

Note:
32-bit processes cannot access the modules of a 64-bit process. If you try to get information about a 64-bit process from a 32-bit process, you will get a Win32Exception exception. A 64-bit process, on the other hand, can access the modules of a 32-bit process.

The TCnProcess component obtains information about a group of properties all at once. After the TCnProcess component has obtained information about one member of any group, it will cache the values for the other properties in that group and not obtain new information about the other members of the group until you call the Refresh method. Therefore, a property value is not guaranteed to be any newer than the last call to the Refresh method. The group breakdowns are operating-system dependent.

If you have a path variable declared in your system using quotes, you must fully qualify that path when starting any process found in that location. Otherwise, the system will not find the path. For example, if c:\mypath is not in your path, and you add it using quotation marks: path = %path%;'c:\mypath', you must fully qualify any process in c:\mypath when starting it.

ServiceController

The ServiceController component (TCnServiceController) is a .Net component in Delphi which represents a Windows service and allows you to connect to a running or stopped service, manipulate it, or get information about it. A TCnServiceController component allows us to access and manage Windows Services running on a machine.

The following example demonstrates the use of the TCnServiceController component to control the SimpleService service example.

program Project1;

{$APPTYPE CONSOLE}

{$R *.res}

uses
  System.SysUtils,
  System.Rtti,
  CNClrLib.Host.Helper,
  CNClrLib.Control.EnumTypes,
  CNClrLib.Component.EventLog,
  CNClrLib.Component.ServiceController;

type
  TSimpleServiceCustomCommands = (ccStopWorker = 128, ccRestartWorker, ccCheckWorker);

var
  svcController: TCnServiceController;
  scServices: _ServiceControllerArray;
  evtLog: TCnEventLog;
  elec: _EventLogEntryCollection;
  I, X: Integer;
begin
  try
    svcController := TCnServiceController.Create(nil);
    scServices := TCnServiceController.GetServices;
    for I := 0 to scServices.Length - 1 do
    begin
      if scServices[I].ServiceName = 'Simple Service' then
      begin
        // Display properties for the Simple Service sample
        // from the ServiceBase example.
        svcController.ServiceName := 'Simple Service';
        writeln('Status = ' + TRttiEnumerationType.GetName(svcController.Status));
        writeln('Can Pause and Continue = ' + BoolToStr(svcController.CanPauseAndContinue, True));
        writeln('Can ShutDown = ' +  BoolToStr(svcController.CanShutdown, True));
        writeln('Can Stop = ' +  BoolToStr(svcController.CanStop, True));
        if svcController.Status = TServiceControllerStatus.scsStopped then
        begin
          svcController.Start;
          while svcController.Status = TServiceControllerStatus.scsStopped do
          begin
            Sleep(1000);
            svcController.Refresh;
          end;
        end;
        // Issue custom commands to the service
        // enum TSimpleServiceCustomCommands
        //    (ccStopWorker = 128, ccRestartWorker, ccCheckWorker);
        svcController.ExecuteCommand(Ord(TSimpleServiceCustomCommands.ccStopWorker));
        svcController.ExecuteCommand(Ord(TSimpleServiceCustomCommands.ccRestartWorker));
        svcController.Pause();
        while svcController.Status <> TServiceControllerStatus.scsPaused do
        begin
          Sleep(1000);
          svcController.Refresh;
        end;
        writeln('Status = ' + TRttiEnumerationType.GetName(svcController.Status));

        svcController.Continue();
        while svcController.Status = TServiceControllerStatus.scsPaused do
        begin
          Sleep(1000);
          svcController.Refresh;
        end;
        writeln('Status = ' + TRttiEnumerationType.GetName(svcController.Status));

        svcController.Stop();
        while svcController.Status <> TServiceControllerStatus.scsStopped do
        begin
          Sleep(1000);
          svcController.Refresh;
        end;
        writeln('Status = ' + TRttiEnumerationType.GetName(svcController.Status));

  //      String[] argArray = new string[] {  };
        svcController.Start(['ServiceController arg1', 'ServiceController arg2']);
        while svcController.Status = TServiceControllerStatus.scsStopped do
        begin
          Sleep(1000);
          svcController.Refresh;
        end;
        writeln('Status = ' + TRttiEnumerationType.GetName(svcController.Status));
        // Display the event log entries for the custom commands
        // and the start arguments.

        evtLog := TCnEventLog.Create(nil);
        evtLog.Log := 'Application';
        elec := evtLog.Entries;
        for X := 0 to elec.Count - 1 do
        begin
          if (TClrStringHelper.IndexOf(elec[X].Source, 'SimpleService.OnCustomCommand') >= 0) or
             (TClrStringHelper.IndexOf(elec[X].Source,'SimpleService.Arguments') >= 0) then
          begin
            writeln(elec[X].Message);
          end;
        end;
        writeln('==============================================');
        writeln('');
      end;
    end;
  except
    on E: Exception do
      Writeln('The following exception was raised : ', E.Message);
  end;
end.
// This sample displays the following output if the Simple Service
// sample is running:
//Status = Running
//Can Pause and Continue = True
//Can ShutDown = True
//Can Stop = True
//Status = Paused
//Status = Running
//Status = Stopped
//Status = Running
//4:14:49 PM - Custom command received: 128
//4:14:49 PM - Custom command received: 129
//ServiceController arg1
//ServiceController arg2

You can use the TCnServiceController component to connect to and control the behavior of existing services. When you create an instance of the TCnServiceController component, you set its properties so it interacts with a specific Windows service. You can then use the component to start, stop, and otherwise manipulate the service.

You will most likely use the TCnServiceController component in an administrative capacity. For example, you could create a Windows or Web application that sends custom commands to a service through the ServiceController instance. This would be useful, because the Service Control Manager (SCM) Microsoft Management Console snap-in does not support custom commands.

After you create an instance of TCnServiceController, you must set two properties on it to identify the service with which it interacts: the computer name and the name of the service you want to control.

Note: By default, MachineName is set to the local computer, so you do not need to change it unless you want to set the instance to point to another computer.

SerialPort

The SerialPort component (TCnSerialPort) is a .Net component in Delphi which Represents a serial port resource.

The following code example demonstrates the use of the SerialPort class to allow two users to chat from two separate computers connected by a null modem cable. In this example, the users are prompted for the port settings and a username before chatting. Both computers must be executing the program to achieve full functionality of this example.

The design for the codes below should look like the image below:

************************** DFM ***********************************

object frmSerialPort: TfrmSerialPort
  Left = 0
  Top = 0
  BorderIcons = [biSystemMenu]
  BorderStyle = bsDialog
  Caption = 'Serial Port Interface'
  ClientHeight = 291
  ClientWidth = 650
  Color = clBtnFace
  Font.Charset = DEFAULT_CHARSET
  Font.Color = clWindowText
  Font.Height = -11
  Font.Name = 'Tahoma'
  Font.Style = []
  OldCreateOrder = False
  OnClose = FormClose
  OnCreate = FormCreate
  PixelsPerInch = 96
  TextHeight = 13
  object Label2: TLabel
    Left = 312
    Top = 216
    Width = 50
    Height = 13
    Caption = 'Send Data'
  end
  object GroupBox1: TGroupBox
    Left = 8
    Top = 8
    Width = 281
    Height = 169
    Caption = 'COM Serial Port Settings'
    TabOrder = 0
    object Label1: TLabel
      Left = 16
      Top = 24
      Width = 50
      Height = 13
      Caption = 'COM Port:'
    end
    object Label3: TLabel
      Left = 16
      Top = 51
      Width = 54
      Height = 13
      Caption = 'Baud Rate:'
    end
    object Label4: TLabel
      Left = 16
      Top = 79
      Width = 32
      Height = 13
      Caption = 'Parity:'
    end
    object Label5: TLabel
      Left = 16
      Top = 107
      Width = 47
      Height = 13
      Caption = 'Data Bits:'
    end
    object Label6: TLabel
      Left = 16
      Top = 135
      Width = 46
      Height = 13
      Caption = 'Stop Bits:'
    end
    object cmbPortName: TComboBox
      Left = 104
      Top = 21
      Width = 145
      Height = 21
      TabOrder = 0
      Text = 'Select Port Name'
    end
    object cmbBauderate: TComboBox
      Left = 104
      Top = 48
      Width = 145
      Height = 21
      TabOrder = 1
      Text = 'Select Baude Rate'
      Items.Strings = (
        '1200'
        '2400'
        '4800'
        '9600'
        '19200'
        '38400'
        '57600'
        '115200')
    end
    object cmbParity: TComboBox
      Left = 104
      Top = 76
      Width = 145
      Height = 21
      TabOrder = 2
      Text = 'Select Parity'
      Items.Strings = (
        'None'
        'Odd'
        'Even'
        'Mark'
        'Space'
        '')
    end
    object cmbDataBits: TComboBox
      Left = 104
      Top = 104
      Width = 145
      Height = 21
      TabOrder = 3
      Text = 'Select Data Bits'
      Items.Strings = (
        '7'
        '8'
        '9')
    end
    object cmbStopBits: TComboBox
      Left = 104
      Top = 132
      Width = 145
      Height = 21
      TabOrder = 4
      Text = 'Select Stop Bits'
      Items.Strings = (
        'None'
        'One'
        'Two'
        'OnePointFive')
    end
  end
  object btnConnect: TButton
    Left = 183
    Top = 216
    Width = 106
    Height = 49
    Caption = 'Connect'
    TabOrder = 1
    OnClick = btnConnectClick
  end
  object GroupBox2: TGroupBox
    Left = 8
    Top = 183
    Width = 169
    Height = 82
    Caption = 'Data Mode'
    TabOrder = 2
    object rdText: TRadioButton
      Left = 32
      Top = 24
      Width = 113
      Height = 17
      Caption = 'Text'
      Checked = True
      TabOrder = 0
      TabStop = True
    end
    object rdHex: TRadioButton
      Left = 32
      Top = 47
      Width = 113
      Height = 17
      Caption = 'Hex'
      TabOrder = 1
    end
  end
  object txtSend: TEdit
    Left = 368
    Top = 213
    Width = 257
    Height = 21
    TabOrder = 3
  end
  object btnSend: TButton
    Left = 469
    Top = 240
    Width = 75
    Height = 25
    Caption = 'Send'
    Enabled = False
    TabOrder = 4
    OnClick = btnSendClick
  end
  object btnClear: TButton
    Left = 550
    Top = 240
    Width = 75
    Height = 25
    Caption = 'Clear'
    TabOrder = 5
    OnClick = btnClearClick
  end
  object rtxtDataArea: TRichEdit
    Left = 312
    Top = 8
    Width = 313
    Height = 199
    Font.Charset = ANSI_CHARSET
    Font.Color = clWindowText
    Font.Height = -11
    Font.Name = 'Tahoma'
    Font.Style = []
    ParentFont = False
    ReadOnly = True
    TabOrder = 6
    Zoom = 100
  end
  object CnSerialPort1: TCnSerialPort
    OnDataReceived = CnSerialPort1DataReceived
    Left = 480
    Top = 136
  end
end
******************** PAS ****************************

unit uSerialPort;

// This is a simple demonstration on how to use the SerialPort component
// for communicating with your PC's COM Port.

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, Vcl.ExtCtrls,
  CNClrLib.Control.EnumTypes, CNClrLib.Control.Base,  
  CNClrLib.Component.SerialPort, Vcl.ComCtrls;

type
  TfrmSerialPort = class(TForm)
    GroupBox1: TGroupBox;
    Label1: TLabel;
    Label3: TLabel;
    Label4: TLabel;
    Label5: TLabel;
    Label6: TLabel;
    cmbPortName: TComboBox;
    cmbBauderate: TComboBox;
    cmbParity: TComboBox;
    cmbDataBits: TComboBox;
    cmbStopBits: TComboBox;
    btnConnect: TButton;
    GroupBox2: TGroupBox;
    rdText: TRadioButton;
    rdHex: TRadioButton;
    Label2: TLabel;
    txtSend: TEdit;
    btnSend: TButton;
    btnClear: TButton;
    CnSerialPort1: TCnSerialPort;
    rtxtDataArea: TRichEdit;
    procedure FormCreate(Sender: TObject);
    procedure btnConnectClick(Sender: TObject);
    procedure btnClearClick(Sender: TObject);
    procedure btnSendClick(Sender: TObject);
    procedure FormClose(Sender: TObject; var Action: TCloseAction);
    procedure CnSerialPort1DataReceived(Sender: TObject;
      E: _SerialDataReceivedEventArgs);
  private
    procedure UpdatePorts();
    procedure Connect();
    procedure Disconnect();
    procedure SendData();
    function HexStringToByteArray(S: String): TArray<Byte>;
  public
    { Public declarations }
  end;

var
  frmSerialPort: TfrmSerialPort;

implementation

{$R *.dfm}

uses CNClrLib.Host, CNClrLib.Host.Utils, CNClrLib.EnumArrays, CNClrLib.Host.Helper;

// Whenever the connect button is clicked, it will check if the port is already open,
// call the disconnect function.
procedure TfrmSerialPort.btnClearClick(Sender: TObject);
begin
  // Clear the screen
  rtxtDataArea.Clear;
  txtSend.Clear;
end;

procedure TfrmSerialPort.btnConnectClick(Sender: TObject);
begin
  if CnSerialPort1.IsOpen then
    Disconnect
  else
    Connect;
end;

procedure TfrmSerialPort.btnSendClick(Sender: TObject);
begin
  SendData;
end;

// When data is received on the port, it will raise this event
procedure TfrmSerialPort.CnSerialPort1DataReceived(Sender: TObject;
  E: _SerialDataReceivedEventArgs);
var
  receivedData: String;
begin
  receivedData := CnSerialPort1.ReadExisting; // Read all available data in the receiving buffer
  rtxtDataArea.Lines.Add(receivedData + 'n');
end;

procedure TfrmSerialPort.Connect;
var
  error: Boolean;
  clrEx: EClrException;
begin
  error := False;

  // Check if all settings have been selected
  if (cmbPortName.ItemIndex <> -1) and (cmbBauderate.ItemIndex <> -1) and
     (cmbParity.ItemIndex <> -1) and (cmbDataBits.ItemIndex <> -1) and
     (cmbStopBits.ItemIndex <> -1) then
  begin
    CnSerialPort1.PortName := cmbPortName.Text;
    CnSerialPort1.BaudRate := StrToInt(cmbBauderate.Text);
    CnSerialPort1.Parity := TParity(OleEnumToOrd(ParityValues, cmbParity.ItemIndex));
    CnSerialPort1.DataBits := StrToInt(cmbDataBits.Text);
    CnSerialPort1.StopBits := TStopBits(OleEnumToOrd(StopBitsValues, cmbStopBits.ItemIndex));

    try
      //Open Port;
      CnSerialPort1.Open;
    except
      on E: Exception do
      begin
        clrEx := EClrException.CreateEx(E);
        try
          if clrEx.IsTypeOf('System.UnauthorizedAccessException') or
             clrEx.IsTypeOf('System.IO.IOException') or
             clrEx.IsTypeOf('System.ArgumentException') then
          begin
            error := True;
          end;
        finally
          clrEx.Free;
        end;
      end;
    end;

    if error then
    begin
      TClrMessageBox.Show(Self, 'Could not open the COM port. Most likely it is '+
        'already in use, has been removed or is unavailable.', 'COM Port unavailable',
        TMessageBoxButtons.mbbsOK, TMessageBoxIcon.mbiStop);
    end
    else
    begin
      TClrMessageBox.Show(self, 'Please select all the COM Serial Port Settings', 'Serial Port Interface',
        TMessageBoxButtons.mbbsOK, TMessageBoxIcon.mbiStop);
    end;

    // If the port is open, change the Connect button to disconnect, enable the send button
    if CnSerialPort1.IsOpen then
    begin
      btnConnect.Caption := 'Disconnect';
      btnSend.Enabled := True;
      GroupBox1.Enabled := False;
    end;
  end;
end;

// Call this function to close the port
procedure TfrmSerialPort.Disconnect;
begin
  CnSerialPort1.Close;
  btnConnect.Caption := 'Connect';
  btnSend.Enabled := True;
  GroupBox1.Enabled := True;
end;

procedure TfrmSerialPort.FormClose(Sender: TObject; var Action: TCloseAction);
begin
  if CnSerialPort1.IsOpen then
    CnSerialPort1.Close; //Close the port if open when exiting the application
end;

procedure TfrmSerialPort.FormCreate(Sender: TObject);
begin
  UpdatePorts;
end;

// Convert a string of hex digits (example: E1 FF 1B) to a byte array.
// The string containing the hex digits (with or without spaces)
// returns TArray<Byte>
function TfrmSerialPort.HexStringToByteArray(S: String): TArray<Byte>;
var
  I: Integer;
  m_length: Integer;
begin
  S := StringReplace(S, ' ', '', [rfReplaceAll]);
  m_length := Trunc(Length(S)/2);
  SetLength(Result, m_length);
  for I := 0 to Length(S) - 1 do
  begin
    if I mod 2 <> 0 then Continue;

    Result[Trunc(I/2)] := TClrConvert.ToByte(Copy(S, i, 2), 16);            
  end;
end;

// a function to send data to the serial port
procedure TfrmSerialPort.SendData;
var
  error: Boolean;
  data: TArray<Byte>;
  clrEx: EClrException;
begin
  error := False;
  if rdText.Checked then //If text mode is selected, send data has text.
  begin
    // send the user's text straight out the port
    CnSerialPort1.Write(txtSend.Text);

    // show in the terminal window
    rtxtDataArea.Lines.Add(txtSend.Text + 'n');
    txtSend.Clear; //Clear screen after sending data    
  end
  else //If Hex Mode is selecetd, send data in hexidecimal
  begin
    try
      // Convert the user's string to hex digits (example: E1 FF 1B) to a byte array;
      data := HexStringToByteArray(txtSend.Text);

      // Send the binary data out the port
      CnSerialPort1.Write(data, 0, Length(data));

      //Show the hex digits in the terminal window
      rtxtDataArea.Lines.Add(UpperCase(txtSend.Text) + 'n');
      txtSend.Clear; // Clear screen after sending      
    except 
      on E: Exception do
      begin
        clrEx := EClrException.CreateEx(E);
        try
          if clrEx.IsTypeOf('System.FormatException') or // Inform the user if the hex string was not properly formatted
             clrEx.IsTypeOf('System.ArgumentException') then
          begin
            error := True;
          end;
        finally
          clrEx.Free;
        end;
      end;
    end;

    if error then
    begin
      TClrMessageBox.Show(Self, 'Not properly formated hex string: '+ txtSend.Text + 'n', 'Format Error',
        TMessageBoxButtons.mbbsOK, TMessageBoxIcon.mbiStop);  
    end;
  end;
end;

procedure TfrmSerialPort.UpdatePorts;
var
  port: String;
  ports: TArray<<<String>;
begin
  ports := TCnSerialPort.GetPortNames;
  for port in ports do
  begin
    cmbPortName.Items.Add(port);
  end;
end;

end.

Use this TCnSerialPort to control a serial port file resource. This TCnSerialPort provides synchronous and event-driven I/O, access to pin and break states, and access to serial driver properties. Additionally, the functionality of this TCnSerialPort can be wrapped in an internal Stream object, accessible through the BaseStream property, and passed to classes that wrap or use streams.

The TCnSerialPort component supports the following encodings: ASCIIEncoding, UTF8Encoding, UnicodeEncoding, UTF32Encoding, and any encoding defined in mscorlib.dll where the code page is less than 50000 or the code page is 54936. You can use alternate encodings, but you must use the ReadByte or Write method and perform the encoding yourself.

You use the GetPortNames method to retrieve the valid ports for the current computer.

If a TCnSerialPort object becomes blocked during a read operation, do not abort the thread. Instead, either close the base stream or dispose of the TCnSerialPort object.

Timer

The Timer component (TCnTimer) is a .Net component in Delphi which implements a timer that raises an event at user-defined intervals. This timer component is optimized for use in Windows Forms applications and must be used in a window.

A TCnTimer is used to raise an event at user-defined intervals. This Windows timer is designed for a single-threaded environment where UI threads are used to perform processing. It requires that the user code have a UI message pump available and always operate from the same thread, or marshal the call onto another thread.

When you use this timer component, use the OnTick event to perform a polling operation or to display a splash screen for a specified period of time. Whenever the Enabled property is set to true and the Interval property is greater than zero, the OnTick event is raised at intervals based on the Interval property setting.

ClientWebSocket

The ClientWebSocket component (TCnClientWebSocket) is a .Net component in Delphi which provides a client for connecting to WebSocket services.

The following example uses TCnClientWebSocket to communicate between a client/agent and a server.

program ClientWebSocketExample;

{$APPTYPE CONSOLE}

{$R *.res}

uses
  System.SysUtils,
  CNClrLib.Core,
  CNClrLib.Control.EnumTypes,
  CNClrLib.Component.ClientWebSocket;

var
  Console: _Console;
  ws: TCnClientWebSocket;
  serverUri: _Uri;
  connectAwait, 
  awaitASync: _Task;
  genAwaitAsync: _GenericTask;
  bytesToSend: _ByteArray; 
  bytesReceived: _ByteArray;
  msg: String;
  WSRResult: _WebSocketReceiveResult;
  UTF8Encoding: _Encoding;   
begin
  try
    Console := CoConsole.CreateInstance;

    ws := TCnClientWebSocket.Create(nil);
    try
      serverUri := TUriActivator.CreateInstance('ws://localhost:49889/');
      connectAwait := ws.ConnectAsync(serverUri, nil);
      connectAwait.wait;
    
      while ws.State = TWebSocketState.wssOpen do
      begin
        Console.Write_22('Input message (''exit'' to exit): ');
        msg := Console.ReadLine();
        if msg = 'exit' then
          break;

        UTF8Encoding := CoEncodingHelper.CreateInstance.UTF8;
        
        bytesToSend := UTF8Encoding.GetBytes_3(msg);
        awaitASync := ws.SendAsync(bytesToSend, TWebSocketMessageType.wsmtText, true, nil);
        awaitASync.Wait;

        bytesReceived := CoByteArray.CreateInstance(1024);
        genAwaitAsync := ws.ReceiveAsync(bytesReceived, nil);
        genAwaitAsync.Wait;

        WSRResult := TWebSocketReceiveResultActivator.Wrap(genAwaitAsync.Result__);
        Console.WriteLine_14(UTF8Encoding.GetString_1(bytesReceived, 0, WSRResult.Count));
      end;
    finally
      ws.Free;
    end;                                       
  except
    on E: Exception do
      Writeln('The following exception was raised : ', E.Message);
  end;
end.

HttpClient

The HttpClient component (TCnHttpClient) is a .Net component in Delphi which provides a base class for sending HTTP requests and receiving HTTP responses from a resource identified by a URI.

The following example shows how to use TCnHttpClient component

program HttpClient;

{$APPTYPE CONSOLE}

{$R *.res}

uses
  System.SysUtils,
  CNClrLib.Host,
  CNClrLib.Core,
  CNClrLib.Component.HttpClient;

var
  Console: _Console;

//Here is a simple program written to demonstrate the use of HttpClient:
procedure CreateHttpClientAndGetAsync;
var
  client: TCnHttpClient;
  responseTask: _GenericTask;
  response: _HttpResponseMessage;
  responseBodyTask: _GenericTask;
  responseBody: String;
begin
  // Create a New HttpClient object.
  client := TCnHttpClient.Create(nil);
  try
    // Call asynchronous network methods in a try/catch block to handle exceptions
    try
      responseTask := client.GetAsync('http://www.contoso.com/');

      response := THttpResponseMessageActivitor.Wrap(responseTask.Result_);
      response.EnsureSuccessStatusCode();

      responseBodyTask := response.Content.ReadAsStringAsync();
      responseBody := responseBodyTask.Result_;

      Console.WriteLine_14(responseBody);
    except
      on E: Exception do
      begin
        if EClrException.IsTypeOf(E, 'System.Net.Http.HttpRequestException') then
        begin
          Console.WriteLine_14(#13#10+'Exception Caught!');
          Console.WriteLine_15('Message :{0} ', E.Message);
        end;
      end;
    end;
  finally
    client.Free;
  end;
end;

//This method is similar and simple to the above CreateHttpClientAndGetAsync method.
//Here is a simple program written to demonstrate the use of HttpClient:
procedure CreateHttpClientAndReadAsStringAsync;
var
  client: TCnHttpClient;
  responseBodyTask: _GenericTask;
  responseBody: String;
begin
  // Create a New HttpClient object.
  client := TCnHttpClient.Create(nil);
  try
    // Call asynchronous network methods in a try/catch block to handle exceptions
    try
      // Above five lines can be replaced with new helper method below
      responseBodyTask := client.GetStringAsync('http://www.contoso.com/');
      responseBody := responseBodyTask.Result_;

      Console.WriteLine_14(responseBody);
    except
      on E: Exception do
      begin
        if EClrException.IsTypeOf(E, 'System.Net.Http.HttpRequestException') then
        begin
          Console.WriteLine_14(#13#10+'Exception Caught!');
          Console.WriteLine_15('Message :{0} ', E.Message);
        end;
      end;
    end;
  finally
    client.Free;
  end;
end;

begin
  Console := CoConsole.CreateInstance;
  try
    CreateHttpClientAndGetAsync;
    CreateHttpClientAndReadAsStringAsync;
  except
    on E: Exception do
      Writeln(E.ClassName, ': ', E.Message);
  end;
end.

The TCnHttpClient component instance acts as a session to send HTTP requests. A TCnHttpClient instance is a collection of settings applied to all requests executed by that instance. In addition, every TCnHttpClient instance uses its own connection pool, isolating its requests from requests executed by other TCnHttpClient instances.

If an app using TCnHttpClient intends to download large amounts of data (50 megabytes or more), then the app should stream those downloads and not use the default buffering. If the default buffering is used the client memory usage will get very large, potentially resulting in substantially reduced performance.

HttpListener

The HttpListener component (TCnHttpListener) is a .Net component in Delphi which provides a simple, programmatically controlled HTTP protocol listener. The TCnHttpListener is active for the lifetime of the HttpListener object and runs within your application.

The following code example demonstrates using a TCnHttpListener component.

program HttpListener;

{$APPTYPE CONSOLE}

{$R *.res}

//The following code example demonstrates using a TCnHttpListener.

uses
  System.SysUtils,
  CNClrLib.Core,
  CNCLrLib.Component.HttpListener;

var
  Console: _Console;

// This example requires the System and System.Net namespaces.
procedure SimpleListenerExample(prefixes: TArray<String>);
var
  listener: TCnHttpListener;
  context: _HttpListenerContext;
  request: _HttpListenerRequest;
  response: _HttpListenerResponse;
  responseString, pfx: String;
  buffer: _ByteArray;
  output: _Stream;
begin
  if not TCnHttpListener.IsSupported then
  begin
    Console.WriteLine_14('Windows XP SP2 or Server 2003 is required to use the TCnHttpListener component.');
    Exit;
  end;

  // URI prefixes are required,
  // for example 'http://contoso.com:8080/index/'.
  if Length(prefixes) = 0 then
    raise Exception.Create('prefixes has not been specified.');

  // Create a listener.
  listener := TCnHttpListener.Create(nil);
  try
    // Add the prefixes.
    for pfx in prefixes do
      listener.Prefixes.Add(pfx);

    listener.Start();
    Console.WriteLine_14('Listening...');
    // Note: The GetContext method blocks while waiting for a request.
    context := listener.GetContext();
    request := context.Request;
    // Obtain a response object.
    response := context.Response;
    // Construct a response.
    responseString := '<HTML><BODY>Hello world!</BODY></HTML>';
    buffer := CoEncodingHelper.CreateInstance.UTF8.GetBytes_3(responseString);
    // Get a response stream and write the response to it.
    response.ContentLength64 := buffer.Length;
    output := response.OutputStream;
    output.Write(buffer,0,buffer.Length);
    // You must close the output stream.
    output.Close();
    listener.Stop();
  finally
    listener.Free;
  end;

end;

begin
  Console := CoConsole.CreateInstance;
  try
    SimpleListenerExample(['http://contoso.com:8080/index/'])
  except
    on E: Exception do
      Writeln(E.ClassName, ': ', E.Message);
  end;
end.

Using the TCnHttpListener component, you can create a simple HTTP protocol listener that responds to HTTP requests. The listener is active for the lifetime of the HttpListener object and runs within your application with its permissions.

To use TCnHttpListener, create a new instance of the component using the TCnHttpListener constructor and use the Prefixes property to gain access to the collection that holds the strings that specify which Uniform Resource Identifier (URI) prefixes the HttpListener should process.

A URI prefix string is composed of a scheme (http or https), a host, an optional port, and an optional path. An example of a complete prefix string is http://www.contoso.com:8080/customerData/. Prefixes must end in a forward slash ("/"). The HttpListener object with the prefix that most closely matches a requested URI responds to the request. Multiple HttpListener objects cannot add the same prefix; a Win32Exception exception is thrown if a HttpListener adds a prefix that is already in use.

When a port is specified, the host element can be replaced with "*" to indicate that the HttpListener accepts requests sent to the port if the requested URI does not match any other prefix. For example, to receive all requests sent to port 8080 when the requested URI is not handled by any HttpListener, the prefix is http://*:8080/. Similarly, to specify that the HttpListener accepts all requests sent to a port, replace the host element with the "+" character. For example, https://+:8080. The "*" and "+" characters can be present in prefixes that include paths.

To begin listening for requests from clients, add the URI prefixes to the collection and call the Start method. HttpListener offers both synchronous and asynchronous models for processing client requests. Requests and their associated responses are accessed using the HttpListenerContext object returned by the GetContext method or its asynchronous counterparts, the BeginGetContext and EndGetContext methods.

The synchronous model is appropriate if your application should block while waiting for a client request and if you want to process only one request at a time. Using the synchronous model, call the GetContext method, which waits for a client to send a request. The method returns an HttpListenerContext object to you for processing when one occurs.

In the more complex asynchronous model, your application does not block while waiting for requests and each request is processed in its own execution thread. Use the BeginGetContext method to specify an application-defined method to be called for each incoming request. Within that method, call the EndGetContext method to obtain the request, process it, and respond.

In either model, incoming requests are accessed using the HttpListenerContext.Request property and are represented by HttpListenerRequest objects. Similarly, responses are accessed using the HttpListenerContext.Response property and are represented by HttpListenerResponse objects. These objects share some functionality with the HttpWebRequest and HttpWebResponse objects, but the latter objects cannot be used in conjunction with TCnHttpListener because they implement client, not server, behaviors.

Ping

The Ping component (TCnPing) is a .Net component in Delphi which allows an application to determine whether a remote computer is accessible over the network.

The following code example demonstrates using the TCnPing component synchronously.

program Project1;

{$APPTYPE CONSOLE}

{$R *.res}

uses
  System.SysUtils,
  System.Rtti,
  CNClrLib.Core,
  CNClrLib.Control.EnumTypes,
  CNClrLib.Component.Ping;

var
  pingSender: TCnPing;
  options: TPingOptions;
  ipAddressOrHostName: String;
  data: String;
  buffer: _ByteArray;
  timeout: Integer;
  reply: IPingReply;
begin
  try
    ipAddressOrHostName := '192.168.0.2'; //or Specify just the name of the host
    pingSender := TCnPing.Create(nil);

    // Use the default Ttl value which is 128,
    // but change the fragmentation behavior.
    options.DontFragment := True;

    // Create a buffer of 32 bytes of data to be transmitted.
    data := 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa';
    buffer := CoEncodingHelper.CreateInstance.ASCII.GetBytes_3(data);
    timeout := 120;
    reply := pingSender.Send(ipAddressOrHostName, timeout, buffer, options);
    //make sure we dont have a null reply
    if reply <> nil then
    begin
      case reply.Status of
        TIPStatus.ipsSuccess:
          begin
            Writeln('Address: ', reply.Address.ToString);
            Writeln('RoundTrip time: ', reply.RoundtripTime);
            Writeln('Time to live: ', reply.Options.Ttl);
            Writeln('Don''t fragment: ', reply.Options.DontFragment);
            Writeln('Buffer size: ', Length(reply.Buffer));
          end;
        TIPStatus.ipsTimedOut:
          begin
            Writeln('Connection has timed out...');
          end;
      else
        Writeln(Format('Ping failed: %s', [TRttiEnumerationType.GetName(reply.Status)]));
      end;
    end
    else
    begin
      Writeln('Connection failed for an unknown reason...');
    end;
  except
    on E: Exception do
      Writeln(E.ClassName, ': ', E.Message);
  end;
end.

Applications use the TCnPing component to detect whether a remote computer is reachable.

Network topology can determine whether Ping can successfully contact a remote host. The presence and configuration of proxies, network address translation (NAT) equipment, or firewalls can prevent Ping from succeeding. A successful Ping indicates only that the remote host can be reached on the network; the presence of higher level services (such as a Web server) on the remote host is not guaranteed.

This component provides functionality similar to the Ping.exe command line tool. The Send and SendAsync methods send an Internet Control Message Protocol (ICMP) echo request message to a remote computer and waits for an ICMP echo reply message from that computer. For a detailed description of ICMP messages, see RFC 792, available at https://www.ietf.org.

The Send and SendAsync methods return the reply in a PingReply interface object. The PingReply.Status property returns an IPStatus value to indicate the outcome of the request.

When sending the request, you must specify the remote computer. You can do this by providing a host name string, an IP address in string format, or an IPAddress object.

The TCnPing component offers both synchronous and asynchronous methods for sending the request. If your application should block while waiting for a reply, use the Send methods; these methods are synchronous. If your application should not block, use the asynchronous SendAsync methods. A call to SendAsync executes in its own thread that is automatically allocated from the thread pool. When the asynchronous operation completes, it raises the OnPingCompleted event. Applications use a PingCompletedEventHandler delegate to specify the method that is called for PingCompleted events. You must add a PingCompletedEventHandler delegate to the event before calling SendAsync. The delegate's method receives a PingCompletedEventArgs object that contains a PingReply object that describes the result of the SendAsync call.

You cannot use the same instance of the TCnPing component to generate multiple simultaneous ICMP Echo requests. Calling Send while a SendAsync call is in progress or calling SendAsync multiple times before all previous calls have completed causes an InvalidOperationException.

SmtpClient

The SmtpClient component (TCnSmtpClient) is a .Net component in Delphi which allows applications to send email by using the Simple Mail Transfer Protocol (SMTP).

The following code example demonstrates sending an email message asynchronously.

program SimpleAsynchronousExample;

{$APPTYPE CONSOLE}

{$R *.res}

uses
  System.SysUtils,
  CNClrLib.Core,
  CNClrLib.Component.SmtpClient;

var
  mailSent: Boolean = False;

type
  TSmtpClientSendCompletedCallback = class
    class procedure DoOnSendCompletedCallback(sender: TObject; e: _AsyncCompletedEventArgs);
  end;

{ TSmtpClientSendCompletedCallback }

class procedure TSmtpClientSendCompletedCallback.DoOnSendCompletedCallback(
  sender: TObject; e: _AsyncCompletedEventArgs);
var
  token: String;
begin
  // Get the unique identifier for this asynchronous operation.
  token := e.UserState;

  if e.Cancelled then
     Writeln(Format('[%s] Send canceled.', [token]));

  if e.Error <> nil then
     Writeln(Format('[%s] %s', [token, e.Error.ToString()]))
  else
    Writeln('Message sent.');

  mailSent := true;
end;

var
  client: TCnSmtpClient;
  SmtpHost: String;
  from: _MailAddress;
  to_: _MailAddress;
  message: _MailMessage;
  UTF8Encoding: _Encoding;
  userState, answer: String;
begin
  try
    SmtpHost := 'smtp.gmail.com'; //Gmail SmtpHost

    client := TCnSmtpClient.Create(nil);
    client.Host := SmtpHost;
    client.Port := 587;

    UTF8Encoding := CoEncodingHelper.CreateInstance.UTF8;

    // Specify the email sender.
    // Create a mailing address that includes a UTF8 character in the display name.
    from := TMailAddressActivator.CreateInstance('jane@contoso.com', 'Jane Clayton', UTF8Encoding);

    // Set destinations for the email message.
    to_ := TMailAddressActivator.CreateInstance('ben@contoso.com');

    // Specify the message content.
    message := TMailMessageActivator.CreateInstance(from, to_);
    message.Body := 'This is a test email message sent by an application. ';
    message.BodyEncoding := UTF8Encoding;
    message.Subject := 'test message 1';
    message.SubjectEncoding := UTF8Encoding;

    // Set the method that is called back when the send operation ends.
    client.OnSendCompleted := TSmtpClientSendCompletedCallback.DoOnSendCompletedCallback;

    // The userState can be any object that allows your callback
    // method to identify this send operation.
    // For this example, the userToken is a string constant.
    userState := 'test message1';
    client.SendAsync(message, userState);
    Writeln('Sending message... press c to cancel mail. Press any other key to exit.');

    Readln(answer);
    // If the user canceled the send, and mail hasn't been sent yet,
    // then cancel the pending operation.
    if answer.StartsWith('c') and not mailSent then
      client.SendAsyncCancel();

    // Clean up.
    message.Dispose();
    client.Free;
    Writeln('Goodbye.');
  except
    on E: Exception do
      Writeln(E.ClassName, ': ', E.Message);
  end;
end.

The TCnSmtpClient component is used to send email to an SMTP server for delivery. The SMTP protocol is defined in RFC 2821, which is available at https://www.ietf.org.

To construct and send an email message by using SmtpClient, you must specify the following information:
  • The SMTP host server that you use to send email. See the Host and Port properties.
  • Credentials for authentication, if required by the SMTP server. See the Credentials property.
  • The email address of the sender. See the Send and SendAsync methods that take a from parameter. Also see the MailMessage.From property.
  • The email address or addresses of the recipients. See the Send and SendAsync methods that take a recipient parameter. Also see the MailMessage.To property.
  • The message content. See the Send and SendAsync methods that take a body parameter. Also see the MailMessage.Body property.

To include an attachment with an email message, first create the attachment by using the Attachment class, and then add it to the message by using the MailMessage.Attachments property. Depending on the email reader used by the recipients and the file type of the attachment, some recipients might not be able to read the attachment. For clients that cannot display the attachment in its original form, you can specify alternate views by using the MailMessage.AlternateViews property.

To send the email message and block while waiting for the email to be transmitted to the SMTP server, use one of the synchronous Send methods. To allow your program's main thread to continue executing while the email is transmitted, use one of the asynchronous SendAsync methods. The OnSendCompleted event is raised when a SendAsync operation completes. To receive this event, you must add a SendCompletedEventHandler delegate to OnSendCompleted. The SendCompletedEventHandler delegate must reference a callback method that handles notification of SendCompleted events. To cancel an asynchronous email transmission, use the SendAsyncCancel method.

The connection established by the current instance of the TCnSmtpClient component to the SMTP server may be re-used if an application wishes to send multiple messages to the same SMTP server. This is particularly useful when authentication or encryption are used establish a connection to the SMTP server. The process of authenticating and establishing a TLS session can be expensive operations. A requirement to re-establish a connection for each message when sending a large quantity of email to the same SMTP server could have a significant impact on performance. There are a number of high-volume email applications that send email status updates, newsletter distributions, or email alerts. Also many email client applications support an off-line mode where users can compose many email messages that are sent later when a connection to the SMTP server is established. It is typical for an email client to send all SMTP messages to a specific SMTP server (provided by the Internet service provider) that then forwards this email to other SMTP servers.

The TCnSmtpClient component implementation pools SMTP connections so that it can avoid the overhead of re-establishing a connection for every message to the same server. An application may re-use the same TCnSmtpClient object to send many different emails to the same SMTP server and to many different SMTP servers. As a result, there is no way to determine when an application is finished using the TCnSmtpClient object and it should be cleaned up.

When an SMTP session is finished and the client wishes to terminate the connection, it must send a QUIT message to the server to indicate that it has no more messages to send. This allows the server to free up resources associated with the connection from the client and process the messages which were sent by the client.

An application must call Free method to free up resources. The Free method calls the underlining smtpClient object Dispose method which iterates through all established connections to the SMTP server specified in the Host property and sends a QUIT message followed by gracefully ending the TCP connection. The Dispose method also releases the unmanaged resources used by the Socket and optionally disposes of the managed resources.

TcpClient

The TcpClient component (TCnTcpClient) is a .Net component in Delphi which provides client connections for TCP network services.

The following code example establishes a TcpClient connection.

procedure Connect(server: String; message: String);
var
  client: TCnTcpClient;
  port: Integer;
  data: _ByteArray;
  stream: _NetworkStream;
  responseData: String;
  ACIIEncoding: _Encoding;
  bytes: Integer;
begin
  try
    // Create a TcpClient component.
    // Note, for this client to work you need to have a TcpServer
    // connected to the same address as specified by the server, port
    // combination.
    port := 13000;
    client := TCnTcpClient.Create(nil);
    try
      ACIIEncoding := CoEncodingHelper.CreateInstance.ASCII;

      // Translate the passed message into ASCII and store it as a Byte array.
      data := ACIIEncoding.GetBytes_3(message);

      client.Connect(server, port);

      // Get a client stream for reading and writing.
      //  Stream stream = client.GetStream();
      stream := client.GetStream;

      // Send the message to the connected TcpServer.
      stream.Write(data, 0, data.Length);

      Writeln('Sent: ', message);

      // Receive the TcpServer.response.

      // Buffer to store the response bytes.
      data := CoByteArray.CreateInstance(256);

      // String to store the response ASCII representation.
      responseData := '';

      // Read the first batch of the TcpServer response bytes.
      bytes := stream.Read(data, 0, data.Length);
      responseData := ACIIEncoding.GetString_1(data, 0, bytes);
      Writeln('Received: ', responseData);

      // Close everything.
      stream.Close_1;
      client.Close;
    finally
      client.Free;
    end;
  except
    on E: Exception do
      Writeln('The following exception was raised : ', E.Message);
  end;
end;

The TCnTcpClient component provides simple methods for connecting, sending, and receiving stream data over a network in synchronous blocking mode.

In order for TCnTcpClient to connect and exchange data, a TcpListener or Socket created with the TCP ProtocolType must be listening for incoming connection requests. You can connect to this listener in one of the following two ways:
  • Create a TcpClient and call one of the three available Connect methods.
  • Create a TcpClient using the host name and port number of the remote host. This constructor will automatically attempt a connection.

To send and receive data, use the GetStream() method to obtain a NetworkStream. Call the Write(Byte[], Int32, Int32) and Read(Byte[], Int32, Int32) methods of the NetworkStream to send and receive data with the remote host. Use the Close(Int32) method to release all resources associated with the TCnTcpClient.

UdpClient

The UdpClient component (TCnUdpClient) is a .Net component in Delphi which provides User Datagram Protocol (UDP) network services.

The following example establishes a UdpClient connection using the host name www.contoso.com on port 11000. A small string message is sent to two separate remote host machines. The Receive method blocks execution until a message is received. Using the IPEndPoint passed to Receive, the identity of the responding host is revealed.

program UdpClientExample;

{$APPTYPE CONSOLE}

{$R *.res}

uses
  System.SysUtils,
  CNClrLib.Core,
  CNClrLib.Component.UdpClient;

var
  udpClient, udpClientB: TCnUdpClient;
  sendBytes: _ByteArray;
  receiveBytes: _ByteArray;
  returnData: String;
  ACIIEncoding: _Encoding;
  RemoteIpEndPoint: _IPEndPoint;

begin
  try
    udpClient := TCnUdpClient.Create(nil);
    try
      udpClient.Connect('www.contoso.com', 11000);

      ACIIEncoding := CoEncodingHelper.CreateInstance.ASCII;

      // Sends a message to the host to which you have connected.
      sendBytes := ACIIEncoding.GetBytes_3('Is anybody there?');

      udpClient.Send(sendBytes, sendBytes.Length);

      // Sends a message to a different host using optional hostname and port parameters.
      udpClientB := TCnUdpClient.Create(nil);
      udpClientB.Send(sendBytes, sendBytes.Length, 'AlternateHostMachineName', 11000);

      //IPEndPoint object will allow us to read datagrams sent from any source.
      RemoteIpEndPoint := TIPEndPointActivator.CreateInstance(TIPAddressHelperActivator.CreateInstance.Any, 0);

      // Blocks until a message returns on this socket from a remote host.
      receiveBytes := udpClient.Receive(RemoteIpEndPoint);
      returnData := ACIIEncoding.GetString(receiveBytes);

      // Uses the IPEndPoint object to determine which of these two hosts responded.
      Writeln('This is the message you received ' + returnData);
      Writeln('This message was sent from ' +
                                 RemoteIpEndPoint.Address.ToString() +
                                 ' on their port number ' +
                                 RemoteIpEndPoint.Port.ToString());

      udpClient.Close();
      udpClientB.Close();
    finally
      udpClient.Free;
    end;
  except
    on E: Exception do
      Writeln('The following exception was raised : ', E.Message);
  end;
end.
The TCnUdpClient component provides simple methods for sending and receiving connectionless UDP datagrams in blocking synchronous mode. Because UDP is a connectionless transport protocol, you do not need to establish a remote host connection prior to sending and receiving data. You do, however, have the option of establishing a default remote host in one of the following two ways:
  • Create an instance of the UdpClient class using the remote host name and port number as parameters.
  • Create an instance of the UdpClient class and then call the Connect method.

You can use any of the send methods provided in the TCnUdpClient to send data to a remote device. Use the Receive method to receive data from remote hosts.

TCnUdpClient methods also allow you to send and receive multicast datagrams. Use the JoinMulticastGroup method to subscribe a UdpClient to a multicast group. Use the DropMulticastGroup method to unsubscribe a UdpClient from a multicast group.

Note
Do not call Send using a host name or IPEndPoint if you have already specified a default remote host. If you do, TCnUdpClient will throw an exception.

WebClient

The WebClient component (TCnWebClient) is a .Net component in Delphi which provides common methods for sending data to and receiving data from a resource identified by a URI. TCnWebClient component can be used to download web pages and files. TCnWebClient is powerful.

The following code example takes the URI of a resource, retrieves it, and displays the response.

program WebClientExample;

{$APPTYPE CONSOLE}

{$R *.res}

uses
  System.SysUtils,
  CNClrLib.Core,
  CNClrLib.Component.WebClient;
var
  client: TCnWebClient;
  data: _Stream;
  reader: _StreamReader;
  s: String;

begin
  try
    if ParamCount = 0 then
      raise Exception.Create('Specify the URI of the resource to retrieve.');

    client := TCnWebClient.Create(nil);
    try
      // Add a user agent header in case the
      // requested URI contains a query.
      client.Headers.Add('user-agent=Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.2; .NET CLR 1.0.3705;)');

      data := client.OpenRead(ParamStr(0));
      reader := CoStreamReader.CreateInstance(data);
      s := reader.ReadToEnd;
      Writeln(s);
      data.Close;
      reader.Close;
    finally
      client.Free;
    end;
  except
    on E: Exception do
      Writeln('The following exception was raised : ', E.Message);
  end;
end.

The TCnWebClient component provides common methods for sending data to or receiving data from any local, intranet, or Internet resource identified by a URI.

The TCnWebClient component uses the WebRequest class to provide access to resources. TCnWebClient instances can access data with any WebRequest descendant registered with the WebRequest.RegisterPrefix method.

WebProxy

The WebProxy component (TCnWebProxy) is a .Net component in Delphi which contains the proxy settings that WebRequest instances use to determine whether a Web proxy is used to send requests.

WebRequestHandler

The WebRequestHandler component (TCnWebRequestHandler) is a .Net component in Delphi which implements a transport handler using HttpWebRequest instances to send HTTP requests to servers..

PrinterSettings

The PrinterSettings component (TCnPrinterSettings) is a .Net component in Delphi which specifies information about how a document is printed, including the printer that prints it, when printing from a Windows Forms application.

The following code example prints a document on the specified printer. The example has three prerequisites:
  • A variable named filePath has been set to the path of the file to print.
  • A method named pd_PrintPage, which handles the PrintPage event, has been defined.
  • A variable named printer has been set to the printer's name.
uses
  System.SysUtils,
  CNClrLib.Core,
  CNClrLib.Drawing,
  CNClrLib.Control.Base,
  CNClrLib.Component.PrintSettings,
  CNClrLib.Component.PrintDocument;

procedure Printing(printer: string);
var
  streamToPrint: _StreamReader;
  printFont: _Font;
  pd: TCnPrintDocument;
begin
  try
    streamToPrint := CoStreamReader.CreateInstance(filePath);
    try
      printFont := CoFont.CreateInstance('Arial', 10);
      pd := TCnPrintDocument.Create(nil);
      pd.OnPrintPage := pd_PrintPage;
      // Specify the printer to use.
      pd.PrinterSettings.PrinterName := printer;

      if pd.PrinterSettings.IsValid then
        pd.Print;
      else
        TClrMessageBox.Show('Printer is invalid.');
    finally
      streamToPrint.Close();
    end;
  except
    on Ex: Exception do
      TClrMessageBox.Show(ex.Message);
  end;
end;

Typically, you access a PrinterSettings through TCnPrintDocument.PrinterSettings or TClrPageSettings.PrinterSettings properties to modify printer settings. The most common printer setting is PrinterName, which specifies the printer to print to.

PageSettings

The PageSettings component (TCnPageSettings) is a .Net component in Delphi which specifies settings that apply to a single, printed page.

The following example demonstrates handling the OnPrintPage event and checking the PageSettings argument of PrintPageEventArgs. To use this code, add a TCnPrintDocument component named PrintDocument1 to a VCL Form and associate its OnPrintPage event with the printDocument1.PrintPage method in the example.

procedure printDocument1.DoOnPrintPage(sender: TObject;
  e: _PrintPageEventArgs);
begin
  if e.PageSettings.Color and not printDocument1.PrinterSettings.SupportsColor then
    TClrMessageBox.Show('Color printing not supported on selected printer.', 'Printer Warning', TMessageBoxButtons.mbbsOKCancel);
end;

The TCnPageSettings component is used to specify settings that modify the way a page will be printed. Typically, you set default settings for all pages to be printed through the TCnPrintDocument.DefaultPageSettings property. To specify settings on a page-by-page basis, handle the TCnPrintDocument.PrintPage or TcnPrintDocument.QueryPageSettings event and modify the TCnPageSettings argument included in the PrintPageEventArgs or QueryPageSettingsEventArgs, respectively.

PageSetupDialog

The PageSetupDialog component (TCnPageSetupDialog) is a .Net component in Delphi which is a pre-configured dialog box used to set page details for printing in Windows-based applications. Use it within your Windows-based application as a simple solution for users to set page preferences in lieu of configuring your own dialog box. You can enable users to set border and margin adjustments, headers and footers, and portrait vs. landscape orientation. By relying on standard Windows dialog boxes, you create applications whose basic functionality is immediately familiar to users. Enables users to change page-related print settings, including margins and paper orientation.

The following code example demonstrates the PageSetupDialog using the PageSettings, PrinterSettings, and ShowNetwork properties. To run this example, place it in a form containing a Button named Button1, a ListBox named ListBox1, and a PageSetupDialog named PageSetupDialog1. Ensure the button's click event is connected to the event-handling method in this example.

//This method displays a PageSetupDialog object. If the
// user clicks OK in the dialog, selected results of
// the dialog are displayed in ListBox1.
procedure Button1.Click(sender: TObject);
var
  PageSettings: TCnPageSettings;
  PrinterSettings: TCnPrinterSettings;
  dialogResult: TDialogResult;
begin
  // Initialize the dialog's PrinterSettings property to hold user
  // defined printer settings.
  PageSettings := TCnPageSettings.Create(nil);
  PageSetupDialog1.PageSettings := PageSettings;

  // Initialize dialog's PrinterSettings property to hold user
  // set printer settings.
  PrinterSettings:= TCnPrinterSettings.Create(nil);
  PageSetupDialog1.PrinterSettings := PrinterSettings;

  //Do not show the network in the printer dialog.
  PageSetupDialog1.ShowNetwork := False;

  //Show the dialog storing the result.
   dialogResult := PageSetupDialog1.ShowDialog();

  // If the result is OK, display selected settings in
  // ListBox1. These values can be used when printing the
  // document.
  if dialogResult = TDialogResult.drOK then
  begin
    ListBox1.Items.Add(PageSetupDialog1.PageSettings.Margins);
    ListBox1.Items.Add(PageSetupDialog1.PageSettings.PaperSize);
    ListBox1.Items.Add(PageSetupDialog1.PageSettings.Landscape);
    ListBox1.Items.Add(PageSetupDialog1.PrinterSettings.PrinterName);
    ListBox1.Items.Add(PageSetupDialog1.PrinterSettings.PrintRange);
  end;
end;

The TCnPageSetupDialog dialog box modifies the TCnPageSettings and TCnPrinterSettings information for a given Document. The user can enable sections of the dialog box to manipulate printing and margins; paper orientation, size, and source; and to show Help and network buttons. The MinMargins property defines the minimum margins a user can select.

When you create an instance of the TCnPageSetupDialog component, the read/write properties are set to initial values.

Because a TCnPageSetupDialog needs page settings to display, you need to set the Document, PrinterSettings, or PageSettings property before calling ShowDialog; otherwise, an exception will occur.

PrintDialog

The PrintDialog component (TCnPrintDialog) is a .Net component in Delphi which is a pre-configured dialog box used to select a printer, choose the pages to print, and determine other print-related settings in Windows-based applications. Use it as a simple solution for printer and print-related settings selection in lieu of configuring your own dialog box. You can enable users to print many parts of their documents: print all, print a specified page range, or print a selection. By relying on standard Windows dialog boxes, you create applications whose basic functionality is immediately familiar to users. Lets users select a printer and choose which sections of the document to print from a Windows Forms application.

The following code example demonstrates how to use the PrintDialog control to set the AllowSomePages, ShowHelp, and Document properties. To run this example, paste the following code into a form that contains a PrintDialog control named PrintDialog1 and a button named Button1. This example requires that the button's Click event and the PrintPage event of docToPrint have been connected to the event-handling methods defined in this example.

var
  // Declare the PrintDocument object.
  docToPrint: TCnPrintDocument;

// This method will set properties on the PrintDialog object and
// then display the dialog.
procedure TDocumentPrintDialog.Button1Click(sender: TObject);
begin

	// Allow the user to choose the page range he or she would
	// like to print.
	PrintDialog1.AllowSomePages := True;

	// Show the help button.
	PrintDialog1.ShowHelp := True;

	// Set the Document property to the PrintDocument for 
	// which the PrintPage Event has been handled. To display the
	// dialog, either this property or the PrinterSettings property 
	// must be set 
	PrintDialog1.Document := docToPrint;

	// If the result is OK then print the document.
	if PrintDialog1.ShowDialog = TDialogResult.drOK then
  begin
		docToPrint.Print();  
  end;
end;

// The PrintDialog will print the document by handling the document's PrintPage event.
procedure TDocumentPrintDialog.DoOnPrintPage(sender: TObject; e: _PrintPageEventArgs);
var
  text: String;
  printFont: _Font;
begin
  // Insert code to render the page here.
  // This code will be called when the control is drawn.

  // The following code will render a simple
  // message on the printed document.
  text := 'In document_PrintPage method.';
  printFont := CoFont.CreateInstance('Arial', 35, [TFontStyle.fsRegular]);

  // Draw the content.
  e.Graphics.DrawString(text, printFont, CoBrushes.CreateInstance.Black, 10, 10);  
end;

When you create an instance of TCnPrintDialog, the read/write properties are set to initial values. To get printer settings that are modified by the user with the TCnPrintDialog, use the PrinterSettings property.

PrintDocument

The PrintDocument component (TCnPrintDocument) is a .Net component in Delphi which is used to set the properties that describe what to print and then to print the document within Windows-based applications. It can be used in conjunction with the TCnPrintDialog component to be in command of all aspects of document printing.

The following code example prints the file named C:\My Documents\MyFile.txt on the default printer. To run the example, create a new VCL Forms project and paste the example code into the form, replacing the file contents. Also, change the path to the file you want to print.

This is the image of the designer screen after pasting the codes below

*************************** DFM ******************************
    object frmPrintDocument: TfrmPrintDocument
  Left = 0
  Top = 0
  BorderIcons = [biSystemMenu]
  Caption = 'Print Document'
  ClientHeight = 368
  ClientWidth = 290
  Color = clBtnFace
  Font.Charset = DEFAULT_CHARSET
  Font.Color = clWindowText
  Font.Height = -11
  Font.Name = 'Tahoma'
  Font.Style = []
  OldCreateOrder = False
  PixelsPerInch = 96
  TextHeight = 13
  object btnPrint: TButton
    Left = 207
    Top = 335
    Width = 75
    Height = 25
    Caption = 'Print'
    TabOrder = 0
    OnClick = btnPrintClick
  end
  object RichEdit1: TRichEdit
    Left = 8
    Top = 16
    Width = 273
    Height = 313
    Font.Charset = ANSI_CHARSET
    Font.Color = clWindowText
    Font.Height = -21
    Font.Name = 'Tahoma'
    Font.Style = []
    Lines.Strings = (
      'The CnPrintDocument '
      'component allows users to '
      'send an output to a printer.')
    ParentFont = False
    TabOrder = 1
    Zoom = 100
  end
  object CnPrintDocument1: TCnPrintDocument
    OnPrintPage = CnPrintDocument1PrintPage
    Left = 80
    Top = 144
  end
  object CnPrintDialog1: TCnPrintDialog
    Document = CnPrintDocument1
    PrinterSettings = CnPrinterSettings1
    Left = 80
    Top = 208
  end
  object CnPrinterSettings1: TCnPrinterSettings
    Left = 176
    Top = 160
  end
end
******************* PAS *********************
unit uPrintDialog;

interface

uses
{$IF CompilerVersion > 22}
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, Vcl.ComCtrls,
{$ELSE}
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls, ComCtrls,
{$IFEND}
  CNClrLib.Component.PrintPreviewDialog, CNClrLib.Control.EnumTypes, CNClrLib.Component.PrintDialog,
  CNClrLib.Control.Base, CNClrLib.Component.PrintDocument, CNClrLib.Control.Utils,
  CNClrLib.Component.PrintSettings;

type
  TfrmPrintDocument = class(TForm)
    CnPrintDocument1: TCnPrintDocument;
    CnPrintDialog1: TCnPrintDialog;
    btnPrint: TButton;
    RichEdit1: TRichEdit;
    CnPrinterSettings1: TCnPrinterSettings;
    procedure CnPrintDocument1PrintPage(Sender: TObject;
      E: _PrintPageEventArgs);
    procedure btnPrintClick(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  frmPrintDocument: TfrmPrintDocument;

implementation

{$R *.dfm}

uses CNClrLib.Drawing, CNClrLib.Enums;

procedure TfrmPrintDocument.btnPrintClick(Sender: TObject);
begin
  if CnPrintDialog1.ShowDialog = TDialogResult.drOK then
  begin
    CnPrintDocument1.Print;
  end;
end;

procedure TfrmPrintDocument.CnPrintDocument1PrintPage(Sender: TObject;
  E: _PrintPageEventArgs);
begin
  E.Graphics.DrawString(RichEdit1.Text, TClrControlHelper.ConvertFrom(RichEdit1.Font), CoBrushes.CreateInstance.Black, 100, 20);
  E.Graphics.PageUnit := GraphicsUnit_Inch;
end;

end.

Typically, you create an instance of the TCnPrintDocument component, set properties such as the DocumentName and PrinterSettings, and call the Print method to start the printing process. Handle the OnPrintPage event where you specify the output to print, by using the Graphics property of the PrintPageEventArgs.

PrintPreviewDialog

The PrintPreviewDialog component (TCnPrintPreviewDialog) is a .Net component in Delphi which is a pre-configured dialog box used to display how a document will appear when printed. Use it within your VCL application as a simple solution in lieu of configuring your own dialog box. The control contains buttons for printing, zooming in, displaying one or multiple pages, and closing the dialog box.

The following code example demonstrates the TCnPrintPreviewDialog setting the Document and UseAntiAlias properties. The example assumes the form contains a TCnTreeView named TreeView1 that contains TreeNode objects. The Tag property of each TreeNode object must be set to a fully qualified document name that can be accessed by the machine running the example. Set each TreeNode.Text property to a string that identifies the file specified by the TreeNode.Tag property. For example, you could set TreeNode1.Tag to 'c:\myDocuments\recipe.doc' and TreeNode1.Text to 'recipe.doc'. The example also assumes the form contains a TCnPrintPreviewDialog named PrintPreviewDialog1 and a button named Button1. To run this example, call the InitializePrintPreviewDialog method in the form's OnCreate or OnShow event handler.

var
  // Declare the PrintDocument object.
  document: TCnPrintDocument;
  // Declare the dialog.
  PrintPreviewDialog1: TCnPrintPreviewDialog;

// Initalize the dialog.
procedure TDocumentPrintDialog.InitializePrintPreviewDialog;
begin
	// Create a new PrintPreviewDialog using constructor.
	PrintPreviewDialog1 := TCnPrintPreviewDialog.Create(nil);
	
	// Associate the event-handling method with the 
	// document's PrintPage event.
	document.OnPrintPage := DoOnPrintPage;

	// Set the UseAntiAlias property to true, which will allow the 
	// operating system to smooth fonts.
	PrintPreviewDialog1.UseAntiAlias := True;
end;

   // This method will set properties on the PrintDialog object and
// then display the dialog.
procedure TDocumentPrintDialog.Button1Click(sender: TObject);
begin
  if TreeView1.SelectedNode <> nil then
	begin
		// Set the PrintDocument object's name to the selectedNode
		// object's  tag, which in this case contains the 
		// fully-qualified name of the document. This value will 
		// show when the dialog reports progress.
		document.DocumentName := VarToStr(TreeView1.SelectedNode.Tag);
  end;

	// Set the PrintPreviewDialog.Document property to
	// the PrintDocument object selected by the user.
	PrintPreviewDialog1.Document := document;

	// Call the ShowDialog method. This will trigger the document's
	//  PrintPage event.
	PrintPreviewDialog1.ShowDialog();
end;

// The PrintDialog will print the document by handling the document's PrintPage event.
procedure TDocumentPrintDialog.DoOnPrintPage(sender: TObject; e: _PrintPageEventArgs);
var
  text: String;
  printFont: _Font;
begin
// Insert code to render the page here.
	// This code will be called when the PrintPreviewDialog.Show 
	// method is called.

	// The following code will render a simple
	// message on the document in the dialog.
	text := 'In document_PrintPage method.';
	printFont := CoFont.CreateInstance('Arial', 35, [TFontStyle.fsRegular]);

	e.Graphics.DrawString(text, printFont, CoBrushes.CreateInstance.Black, 0, 0);
end;

When you create an instance of the TCnPrintPreviewDialog, some of the read/write properties are set to initial values.

PrintPreviewControl

The PrintPreviewControl control (TCnPrintPreviewControl) is a .Net control in Delphi which is used to display a document as it will appear when printed. This control has no buttons or other user interface elements, so typically you use the TCnPrintPreviewControl only if you wish to write your own print-preview user interface. If you want the standard user interface, use a TCnPrintPreviewDialog control.

The following code example demonstrates the Document, UseAntiAlias, and Zoom properties of the TCnPrintPreviewControl. To run this example, place the following code in a form and call the InitializePrintPreviewControl method from the form's OnCreate or OnShow event-handling method.

var
  // Declare the PrintDocument object.
  docToPrint: TCnPrintDocument;
  // Declare the PrintPreviewControl.
  PrintPreviewControl1: TCnPrintPreviewControl;

// Initalize the dialog.
procedure TDocumentPrintDialog.InitializePrintPreviewControl;
begin
	// Construct the PrintPreviewControl.
	PrintPreviewControl1 := TCnPrintPreviewControl.Create(Self);

	// Set location, name, and dock style for PrintPreviewControl1.
	PrintPreviewControl1.Name := 'PrintPreviewControl1';
	PrintPreviewControl1.Align := alClient;

	// Set the Document property to the PrintDocument
	// for which the PrintPage event has been handled.
	PrintPreviewControl1.Document := docToPrint;

	// Set the zoom to 25 percent.
	PrintPreviewControl1.Zoom := 0.25;

	// Set the document name. This will show be displayed when
	// the document is loading into the control.
	PrintPreviewControl1.Document.DocumentName := 'c:\\someFile';

	// Set the UseAntiAlias property to true so fonts are smoothed
	// by the operating system.
	PrintPreviewControl1.UseAntiAlias := True;

	// Add the control to the form.
  PrintPreviewControl1.Parent := Self

	// Associate the event-handling method with the
	// document's PrintPage event.
	docToPrint.OnPrintPage := DoOnPrintPage;
end;

// The PrintPreviewControl will display the document
// by handling the documents PrintPage event
procedure TDocumentPrintDialog.DoOnPrintPage(sender: TObject; e: _PrintPageEventArgs);
var
  text: String;
  printFont: _Font;
begin
	// Insert code to render the page here.
	// This code will be called when the control is drawn.

	// The following code will render a simple
	// message on the document in the control.
	text := 'In DoOnPrintPage method.';
	printFont := CoFont.CreateInstance('Arial', 35, [TFontStyle.fsRegular]);

	e.Graphics.DrawString(text, printFont, CoBrushes.CreateInstance.Black, 0, 0);
end;

When you create an instance of the TCnPrintPreviewControl, some of the read/write properties are set to initial values.

MessageBox

The MessageBox class (TClrMessageBox) is a .Net component in Delphi which Displays a message window, also known as a dialog box, which presents a message to the user. It is a modal window, blocking other actions in the application until the user closes it. A MessageBox can contain text, buttons, and symbols that inform and instruct the user.

Simple MessageBox

The simplest form of a MessageBox is a dialog with a text and OK button. When you click OK button, the box disappears.

The following code snippet creates a simple Message Box.

var
  message: String;
begin
  message := 'Simple MessageBox';
  TClrMessageBox.Show(message);
end;


MessageBox with Buttons

A MessageBox can have different button combinations such as YesNo and OKCancel. The TMessageBoxButtons enumeration represents the buttons to be displayed on a MessageBox and has following values.

  • OK
  • OKCancel
  • AbortRetryIgnore
  • YesNoCancel
  • YesNo
  • RetryCancel

The following code snippet creates a MessageBox with a title and Yes and No buttons. This is a typical MessageBox you may call when you want to close an application. If the Yes button is clicked, the application will be closed. The Show method returns a TDialogResult enumeration.

var
  message: String;
  title: String;
  buttons: TMessageBoxButtons;
  result: TDialogResult;
begin
  message := 'Do you want to close this window?';
  title := 'Close Window';
  buttons := TMessageBoxButtons.mbbsYesNo;
  result := TClrMessageBox.Show(message, title, buttons);
  if result = TDialogResult.drYes then
  begin
    Close
  end
  else
  begin
    //Do something
  end;
end;


MessageBox with Icon

A MessageBox can display an icon on the dialog. A TMessageBoxIcons enumeration represents an icon to be displayed on a MessageBox and has following values.

  • None
  • Hand
  • Question
  • Exclamation
  • Asterisk
  • Stop
  • Error
  • Warning
  • Information

The following code snippet creates a MessageBox with a title, buttons, and an icon.

var
  message: String;
  title: String;
  buttons: TMessageBoxButtons;
  result: TDialogResult;
begin
  message := 'Do you want to abort this operation?';
  title := 'Close Window';
  buttons := TMessageBoxButtons.mbbsAbortRetryIgnore;
  result := TClrMessageBox.Show(message, title, buttons, TMessageBoxIcon.mbiWarning);
  if result = TDialogResult.drAbort then
  begin
    Close
  end
  else if result = TDialogResult.drRetry then
  begin
     // Do nothing
  end
  else
  begin
    //Do something
  end;
end;


MessageBox with Default Button

We can also set the default button on a MessageBox. By default, the first button is the default button. The TMessageBoxDefaultButton enumeration is used for this purpose and it has the following three values.

  • Button1
  • Button2
  • Button3

The following code snippet creates a MessageBox with a title, buttons, and an icon and sets second button as a default button.

var
  message: String;
  title: String;
  buttons: TMessageBoxButtons;
  result: TDialogResult;
begin
  message := 'Do you want to abort this operation?';
  title := 'Close Window';
  buttons := TMessageBoxButtons.mbbsAbortRetryIgnore;
  result := TClrMessageBox.Show(message, title, buttons, TMessageBoxIcon.mbiWarning, TMessageBoxDefaultButton.mbdbButton2);
  if result = TDialogResult.drAbort then
  begin
    Close
  end
  else if result = TDialogResult.drRetry then
  begin
     // Do nothing
  end
  else
  begin
    //Do something
  end;
end;


MessageBox with Message Options

MessageBoxOptions enumeration represents various options and has following values.

  • ServiceNotification
  • DefaultDesktopOnly
  • RightAlign
  • RtlReading

The following code snippet creates a MessageBox with various options.

var
  message: String;
  title: String;
  buttons: TMessageBoxButtons;
  result: TDialogResult;
begin
  message := 'Do you want to abort this operation?';
  title := 'Close Window';
  buttons := TMessageBoxButtons.mbbsAbortRetryIgnore;
  result := TClrMessageBox.Show(message, title, buttons, TMessageBoxIcon.mbiWarning,
    TMessageBoxDefaultButton.mbdbButton2, [TMessageBoxOptions.mboRightAlign, TMessageBoxOptions.mboRtlReading]);
  if result = TDialogResult.drAbort then
  begin
    Close
  end
  else if result = TDialogResult.drRetry then
  begin
     // Do nothing
  end
  else
  begin
    //Do something
  end;
end;


MessageBox with Help Button

A MessageBox can have an extra button called Help button. This is useful when we need to display a help file. The following code snippet creates a MessageBox with a Help button.

var
  message: String;
  title: String;
  buttons: TMessageBoxButtons;
  result: TDialogResult;
begin
  message := 'Do you want to abort this operation?';
  title := 'Close Window';
  buttons := TMessageBoxButtons.mbbsYesNo;
  result := TClrMessageBox.Show(message, title, buttons, TMessageBoxIcon.mbiWarning,
    TMessageBoxDefaultButton.mbdbButton2, [TMessageBoxOptions.mboRightAlign], True);
  if result = TDialogResult.drYes then
  begin
    Close
  end
  else
  begin
    //Do something
  end;
end;


We can also specify a help file when the Help button is clicked. The following code snippet references a help file.

  result := TClrMessageBox.Show(message, title, buttons, TMessageBoxIcon.mbiWarning,
    TMessageBoxDefaultButton.mbdbButton2, [TMessageBoxOptions.mboNone], 'helpfile.chm');

You cannot create an instance of the TClrMessageBox class. To display a message box, call the static method TClrMessageBox.Show. The title, message, buttons, and icons displayed in the message box are determined by parameters that you pass to this method.

ColorDialog

The ColorDialog component (TCnColorDialog) is a .Net component in Delphi which represents a common dialog box that displays available colors along with controls that enable the user to define custom colors.

A ColorDialog object is a dialog box with a list of colors that are defined for the display system. The user can select or create a particular color from the list, which is then reported back to the application when the dialog box exits.

The following example illustrates the creation of new TCnColorDialog. This example requires that the method is called from within an existing VCL form that has a TCnTextBox or TEdit control and Button placed on it.


procedure TForm8.Button1Click(Sender: TObject);
var
  MyDialog: TCnColorDialog;
begin
  MyDialog := TCnColorDialog.Create(nil);
  try
    // Keeps the user from selecting a custom color.
    MyDialog.AllowFullOpen := False;
    // Allows the user to get help. (The default is false.)
    MyDialog.ShowHelp := True;
    // Sets the initial color select to the current text color.
    MyDialog.Color.SetColor(Edit1.Font.Color);

    // Update the text box color if the user clicks OK
    if MyDialog.ShowDialog() = TDialogResult.drOK then
      Edit1.Font.Color := MyDialog.Color.ToWin32Color;
  finally
    MyDialog.Free;
  end;
end;

FolderBrowserDialog

The FolderBrowserDialog component (TCnFolderBrowserDialog) is a .Net component in Delphi which displays a directory selection window. Once the user selects a folder, we access it from the Delphi source. This control provides a convenient way to select folders (not files).

The following example illustrates the creation of new TCnFolderBrowserDialog. This example requires that the method is called from within an existing VCL form that has a TCnTextBox or TEdit control and Button placed on it.


procedure TForm8.Button1Click(Sender: TObject);
var
  MyDialog: TCnFolderBrowserDialog;
begin
  MyDialog := TCnFolderBrowserDialog.Create(nil);
  try
    // Set the dialog description.
    MyDialog.Description := 'Example Folder';
    // Set the root Folder to Desktop
    MyDialog.RootFolder := TSpecialFolder.sfDesktop;

    //Don't show the New Folder button
    MyDialog.ShowNewFolderButton := False;

    if MyDialog.ShowDialog() = TDialogResult.drOK then
      Edit1.Text := MyDialog.SelectedPath;
  finally
    MyDialog.Free;
  end;
end;

FontDialog

The FontDialog component (TCnFontDialog) is a .Net component in Delphi which represents a common dialog box that displays a list of fonts that are currently installed on the system. The Font dialog box lets the user choose attributes for a logical font, such as font family and associated font style, point size, effects , and a script.

The following example illustrates the creation of new TCnFontDialog. This example requires that the method is called from within an existing VCL form that has a TCnTextBox or TEdit control and Button placed on it.


procedure TForm8.Button1Click(Sender: TObject);
var
  MyDialog: TCnFontDialog;
begin
  MyDialog := TCnFontDialog.Create(nil);
  try
    MyDialog.ShowColor := True;

    MyDialog.Font.SetTFont(Edit1.Font);
    MyDialog.Color.SetColor(Edit1.Font.Color);

    if MyDialog.ShowDialog() <> TDialogResult.drCancel then
    begin
      Edit1.Font := MyDialog.Font.ToTFont;
      Edit1.Font.Color := MyDialog.Color.ToWin32Color;
    end;
  finally
    MyDialog.Free;
  end;
end;

OpenFileDialog

The OpenFileDialog component (TCnOpenFileDialog) is a .Net component in Delphi which is used to browse and select a file on a computer.

The following code example creates an TCnOpenFileDialog, sets several properties to define the file extension filter and dialog behavior, and displays the dialog box using the ShowDialog method. The example requires a form with a Button placed on it and a reference to the CNCLrLib.Core namespace added to it.


procedure TForm8.Button1Click(Sender: TObject);
var
  openFileDialog: TCnOpenFileDialog;
  fileContent : String;
  filePath: String;
  fileStream: _Stream;
  reader: _StreamReader;
begin
  openFileDialog := TCnOpenFileDialog.Create(nil);
  try
    openFileDialog.InitialDirectory := 'c:\\';
    openFileDialog.Filter := 'txt files (*.txt)|*.txt|All files (*.*)|*.*';
    openFileDialog.FilterIndex := 2;
    openFileDialog.RestoreDirectory := True;

    if openFileDialog.ShowDialog() = TDialogResult.drOK then
    begin
      //Get the path of specified file
      filePath := openFileDialog.FileName;

      //Read the contents of the file into a stream
      fileStream := openFileDialog.OpenFile();

      reader := CoStreamReader.CreateInstance(fileStream); 
      try
        fileContent := reader.ReadToEnd();
        reader.Close;
      finally
        reader.Close
      end;
    end;
  finally
    openFileDialog.Free;
  end;
end;

This component allows you to check whether a file exists and to open it. The ShowReadOnly property determines whether a read-only check box appears in the dialog box. The ReadOnlyChecked property indicates whether the read-only check box is checked.

SaveFileDialog

The SaveFileDialog component (TCnSaveFileDialog) is a .Net component in Delphi which prompts the user to select a location for saving a file.

The following code example illustrates creating a TCnSaveFileDialog, setting members, calling the dialog box using the ShowDialog method, and saving the current file. The example requires a form with a button placed on it.


procedure TForm8.Button1Click(Sender: TObject);
var
  saveFileDialog: TCnSaveFileDialog;
  myStream: _Stream;
begin
  saveFileDialog := TCnSaveFileDialog.Create(nil);
  try
    saveFileDialog.InitialDirectory := 'c:\\';
    saveFileDialog.Filter := 'txt files (*.txt)|*.txt|All files (*.*)|*.*';
    saveFileDialog.FilterIndex := 2;
    saveFileDialog.RestoreDirectory := True;

    if saveFileDialog.ShowDialog() = TDialogResult.drOK then
    begin
      //Read the contents of the file into a stream
      myStream := saveFileDialog.OpenFile();
      if myStream <> nil then
      begin
        // Code to write the stream goes here.
        myStream.Close;
      end;
    end;
  finally
    saveFileDialog.Free;
  end;
end;

XmlDocument

The XmlDocument component (TCnXmlDocument) is a .Net component in Delphi which represents an XML document. You can use this component to load, validate, edit, add, and position XML in a document.

The TCnXmlDocument compoonent is an in-memory representation of an XML document. It implements the W3C XML Document Object Model (DOM) Level 1 Core and the Core DOM Level 2. DOM stands for document object model.

You can load XML into the DOM by using this component, and then programmatically read, modify, and remove XML in the document.

Tasks:
  • Load XML into the document object model
  • Navigate the document tree
  • Find nodes
  • Edit nodes
  • Add nodes
  • Remove nodes
  • Position nodes

Load XML into the document object model

Start with an XML document like this one. it's just a few books in a collection. But it does contain the basic things that you'd find in any XML document; a namespace, elements that represent data and attributes that describe the data.

XML Data saved as booksData.xml
<?xml version="1.0" encoding="utf-8"?>  
<books xmlns="http://www.contoso.com/books">  
  <book genre="novel" ISBN="1-861001-57-8" publicationdate="1823-01-28">  
    <title>Pride And Prejudice</title>  
    <price>24.95</price>  
  </book>  
  <book genre="novel" ISBN="1-861002-30-1" publicationdate="1985-01-01">  
    <title>The Handmaid's Tale</title>  
    <price>29.95</price>  
  </book>  
  <book genre="novel" ISBN="1-861001-45-3" publicationdate="1811-01-01">  
    <title>Sense and Sensibility</title>  
    <price>19.95</price>  
  </book>  
</books>
</pre>

Next, load this data into the DOM so that you can work with it in memory. The most popular way to do this is refer to a file on your local computer or on a network.

This example loads XML from a file.

var
  doc: TCnXmlDocument;
begin
  doc := TCnXmlDocument.Create(nil);
  try
    try
      doc.PreserveWhitespace := True;
      doc.Load('booksData.xml');
    except
      on E: Exception do
        Writeln('The following exception was raised : ', E.Message);
    end;
  finally
    doc.Free;
  end;
end.

Navigate the document tree

You can use properties to navigate around an XML document. But before you use any of them, let's quickly review a few terms. Your document is composed of nodes. Each node has as single parent node directly above it. The only node that does not have a parent node is the document root, as it is the top-level node. Most nodes can have child nodes, which are nodes directly below them. Nodes that are at the same level are siblings.

The following examples, show you how to obtain the root node, jump to the first child node of the root node, access any of its child nodes, get back out to the parent node, and then navigate across sibling nodes.

Start with the root node

This example gets the root node and then uses that node to output the contents of the document to the console.

program Sample;

{$APPTYPE CONSOLE}

{$R *.res}

uses
  System.SysUtils,
  CNClrlib.Component.XmlDocument;

var
  doc: TCnXmlDocument;
begin
  doc := TCnXmlDocument.Create(nil);
  try
    try
      doc.PreserveWhitespace := True;
      doc.LoadXml('<?xml version=''1.0'' ?>' +
                  '<book genre=''novel'' ISBN=''1-861001-57-5''>' +
                  '<title>Pride And Prejudice</title>' +
                  '</book>');

      //Display the document element.
      Writeln(doc.DocumentElement.OuterXml);

    except
      on E: Exception do
        Writeln('The following exception was raised : ', E.Message);
    end;
  finally
    doc.Free;
  end;
end.

Get child nodes

This example jumps to the first child node of the root node and then iterates through the child nodes of that node if any exist.

program Sample;

{$APPTYPE CONSOLE}

{$R *.res}

uses
  System.SysUtils,
  CNClrlib.Component.XmlDocument;

var
  doc: TCnXmlDocument;
  root: _XmlNode;
  i: Integer;
begin
  doc := TCnXmlDocument.Create(nil);
  try
    try
      doc.PreserveWhitespace := True;
      doc.LoadXml('<book ISBN=''1-861001-57-5''>' +
                  '<title>Pride And Prejudice</title>' +
                  '<price>19.95</price>' +
                  '</book>');

      root := doc.FirstChild;

      //Display the contents of the child nodes.
      if root.HasChildNodes then
      begin
        for i := 0 to root.ChildNodes.Count - 1 do
        begin
          Writeln(root.ChildNodes[i].InnerText);
        end;
      end;
    except
      on E: Exception do
        Writeln('The following exception was raised : ', E.Message);
    end;
  finally
    doc.Free;
  end;
end.

Refer to the last child node

This example writes the price of a book to the console (which is the last child node of a book node).

program Sample;

{$APPTYPE CONSOLE}

{$R *.res}

uses
  System.SysUtils,
  CNClrlib.Component.XmlDocument;

var
  doc: TCnXmlDocument;
  root: _XmlNode;
begin
  doc := TCnXmlDocument.Create(nil);
  try
    try
      doc.PreserveWhitespace := True;
      doc.LoadXml('<book ISBN=''1-861001-57-5''>' +
                  '<title>Pride And Prejudice</title>' +
                  '<price>19.95</price>' +
                  '</book>');

      root := doc.FirstChild;

      Writeln('Display the price element...');
      Writeln(root.LastChild.OuterXml);
    except
      on E: Exception do
        Writeln('The following exception was raised : ', E.Message);
    end;
  finally
    doc.Free;
  end;
end.

Navigate forward across siblings

This example moves forward from book to book. Book nodes are siblings to one another.

program Sample;

{$APPTYPE CONSOLE}

{$R *.res}

uses
  System.SysUtils,
  CNClrlib.Component.XmlDocument;

var
  doc: TCnXmlDocument;
  currNode: _XmlNode;
  nextNode: _XmlNode;
begin
  doc := TCnXmlDocument.Create(nil);
  try
    try
      doc.PreserveWhitespace := True;
      doc.Load('books.xml');

      currNode := doc.DocumentElement.FirstChild;
      writeln('First book...');
      writeln(currNode.OuterXml);

      nextNode := currNode.NextSibling;
      writeln(#13#10+'Second book...');
      writeln(nextNode.OuterXml);
    except
      on E: Exception do
        Writeln('The following exception was raised : ', E.Message);
    end;
  finally
    doc.Free;
  end;
end.

Navigate backwards across siblings

This example moves backwards from book to book.

program Sample;

{$APPTYPE CONSOLE}

{$R *.res}

uses
  System.SysUtils,
  CNClrlib.Component.XmlDocument;

var
  doc: TCnXmlDocument;
  lastNode: _XmlNode;
  prevNode: _XmlNode;
begin
  doc := TCnXmlDocument.Create(nil);
  try
    try
      doc.PreserveWhitespace := True;
      doc.Load('books.xml');

      lastNode := doc.DocumentElement.LastChild;
      writeln('Last book...');
      writeln(lastNode.OuterXml);

      prevNode := lastNode.PreviousSibling;
      writeln(#13#10+'nPrevious book...');
      writeln(prevNode.OuterXml);
    except
      on E: Exception do
        Writeln('The following exception was raised : ', E.Message);
    end;
  finally
    doc.Free;
  end;
end.

Find nodes

The most popular way to find one or more nodes of data is to use an XPath query string, but there are also methods that don't require one.

Get a single node

This example locates a book by using the ISBN number.

function GetBook(uniqueAttribute: string; doc: TCnXmlDocument): _XmlNode;
var
  nsmgr: _XmlNamespaceManager;
  xPathString: String;
begin
  nsmgr := TXmlNamespaceManagerActivator.CreateInstance(doc.NameTable);
  nsmgr.AddNamespace('bk', 'http://www.contoso.com/books');
  xPathString := '//bk:books/bk:book[@ISBN=''' + uniqueAttribute + ''']';
  Result := doc.DocumentElement.SelectSingleNode_1(xPathString, nsmgr);
end;

The string used in this example is an Xpath query.

You can also use the GetElementById to retrieve nodes. To use this approach, you'll have to define ID's in the document type definition declarations of your XML file.

After you get a node, you get the value of attributes or child nodes. This example does that with a book node.

procedure GetBookInformation(var title, ISBN, publicationDate, price, genre: string; book: _XmlNode);
var
  bookElement: _XmlElement;
  attr: _XmlAttribute;
begin
  bookElement := TXmlElementActivator.Wrap(book);

  // Get the attributes of a book.
  attr := bookElement.GetAttributeNode('ISBN');
  ISBN := attr.InnerXml;

  attr := bookElement.GetAttributeNode('genre');
  genre := attr.InnerXml;

  attr := bookElement.GetAttributeNode('publicationdate');
  publicationDate := attr.InnerXml;

  // Get the values of child elements of a book.
  title := bookElement['title'].InnerText;
  price := bookElement['price'].InnerText;
end;

XDocument

The XDocument component (TCnXDocument) is a .Net component in Delphi which represents an XML document.

The following example creates a document, and then adds a comment and an element to it. It then composes another document using the results of a query.

program Sample;

{$APPTYPE CONSOLE}

{$R *.res}

uses
  System.SysUtils,
  CNClrLib.Core,
  CNClrLib.Xml,
  CNClrLib.Component.XDocument;

var
  Console: _Console;

//The following example creates a document, and then adds a comment and an element to it.
//It then composes another document using the results of a query.
procedure CreateXDocument;
var
  srcTree: TCnXDocument;
  srcComment: _XComment;
  srcRoot: _XElement;
begin
  srcTree := TCnXDocument.Create(nil);
  try
    srcComment := CoXComment.CreateInstance('This is a comment');
    srcTree.Add(srcComment);

    srcRoot := CoXElement.CreateInstance(CoXName.Wrap('Root'));
    srcRoot.Add(CoXElement.CreateInstance(CoXName.Wrap('Child1'), CoXName.Wrap('data1')));
    srcRoot.Add(CoXElement.CreateInstance(CoXName.Wrap('Child2'), CoXName.Wrap('data2')));
    srcRoot.Add(CoXElement.CreateInstance(CoXName.Wrap('Child3'), CoXName.Wrap('data3')));
    srcRoot.Add(CoXElement.CreateInstance(CoXName.Wrap('Child2'), CoXName.Wrap('data4')));
    srcRoot.Add(CoXElement.CreateInstance(CoXName.Wrap('Info5'), CoXName.Wrap('info5')));
    srcRoot.Add(CoXElement.CreateInstance(CoXName.Wrap('Info6'), CoXName.Wrap('info6')));
    srcRoot.Add(CoXElement.CreateInstance(CoXName.Wrap('Info7'), CoXName.Wrap('info7')));
    srcRoot.Add(CoXElement.CreateInstance(CoXName.Wrap('Info8'), CoXName.Wrap('Info8')));
    srcTree.Add(srcRoot);

    Console.WriteLine_12(srcTree.Document);
  finally
    srcTree.Free;
  end;

//This example produces the following output:
{
  <!--This is a comment-->
  <Root>
    <Child1>data1</Child1>
    <Child2>data2</Child2>
    <Child3>data3</Child3>
    <Child2>data4</Child2>
    <Child2>info5</Child2>
    <Child2>info6</Child2>
    <Child2>info7</Child2>
    <Child2>Info8</Child2>
  </Root>
}
end;

//Creates a new XDocument from a file specified by a URI, from an TextReader, or from an XmlReader.
procedure LoadXml;
var
  xDoc: TCnXDocument;
  xmlData: String;
  strReader: _StringReader;
begin
  xDoc := TCnXDocument.Create(nil);
  try
    xmlData := '<Root>Content</Root>';
    //Load from String
    xDoc.Load(xmlData);
    Console.WriteLine_13(xDoc.Document.AsClrObject);

    //Load from StringReader
    strReader := CoStringReader.CreateInstance(xmlData);
    xDoc.Load(strReader.AsTextReader);
    Console.WriteLine_13(xDoc.Document.AsClrObject);

    //Load from Xml File
    strReader := CoStringReader.CreateInstance(xmlData);
    xDoc.Load('PurchaseOrder.xml');
    Console.WriteLine_13(xDoc.Document.AsClrObject);
  finally
    xDoc.Free;
  end;

// The Load from Xml File produces the following output:
{
   <PurchaseOrder PurchaseOrderNumber="99503" OrderDate="1999-10-20">
    <Address Type="Shipping">
      <Name>Ellen Adams</Name>
      <Street>123 Maple Street</Street>
      <City>Mill Valley</City>
      <State>CA</State>
      <Zip>10999</Zip>
      <Country>USA</Country>
    </Address>
    <Address Type="Billing">
      <Name>Tai Yee</Name>
      <Street>8 Oak Avenue</Street>
      <City>Old Town</City>
      <State>PA</State>
      <Zip>95819</Zip>
      <Country>USA</Country>
    </Address>
    <DeliveryNotes>Please leave packages in shed by driveway.</DeliveryNotes>
    <Items>
      <Item PartNumber="872-AA">
        <ProductName>Lawnmower</ProductName>
        <Quantity>1</Quantity>
        <USPrice>148.95</USPrice>
        <Comment>Confirm this is electric</Comment>
      </Item>
      <Item PartNumber="926-AA">
        <ProductName>Baby Monitor</ProductName>
        <Quantity>2</Quantity>
        <USPrice>39.98</USPrice>
        <ShipDate>1999-05-21</ShipDate>
      </Item>
    </Items>
  </PurchaseOrder>
}
end;

begin
  Console := CoConsole.CreateInstance;
  try
    CreateXDocument();
    LoadXML();
  except
    on E: Exception do
      Writeln(E.ClassName, ': ', E.Message);
  end;
end.

This example produces the following output:

<!--This is a comment-->  
<Root>  
  <Child1>data1</Child1>  
  <Child2>data2</Child2>  
  <Child3>data3</Child3>  
  <Child2>data4</Child2>  
</Root>