Wednesday, December 30, 2009

Reporting Progress status using Asynchronous / BackgroundWorker


Well, this is an example to show how to create an Asynchronous methods / calls conforming to the Event-based Asynchronous pattern. This example demonstrate how to use BackgroundWorker and Asynchronous methods to report the progress status of the task you are performing.

File 1: Entity.cs

using System;
using System.ComponentModel;
using System.Threading;
using System.Runtime.Remoting.Messaging;

namespace BackgroundWorkerClient
{
 internal class Entity
 {
  #region Private Declarations
  private int _fileCount;
  private int _folderCount;
  private int _progress;
  private ProgressStatus _status = ProgressStatus.Idle;
  private bool _taskRunning;
  private readonly object _sync = new object();
  private AsyncContext _taskContext;

  #endregion

  #region Public Properties
  public bool IsBusy { get { return _taskRunning; } }
  #endregion

  #region EventHandlers & Delegates
  public event AsyncCompletedEventHandler OnTaskCompleted;
  public event TaskProgressChangedEventHandler OnTaskProgressChanged;

  private delegate void TaskWorkerDelegate(string path, AsyncOperation asyncOperation, AsyncContext asyncContext, out bool cancelled);
  #endregion

  #region Protected Virtual Methods
  protected virtual void DoOnTaskCompleted(AsyncCompletedEventArgs e)
  {
   if (OnTaskCompleted != null)
    OnTaskCompleted(this, e);
  }
  protected virtual void DoOnTaskProgressChanged(TaskProgressChangedEventArgs eventArgs)
  {
   if (OnTaskProgressChanged != null)
    OnTaskProgressChanged(this, eventArgs);
  }
  #endregion

  #region Asynchronous Operations

  private void StartProcessing(string path, AsyncOperation asyncOperation, AsyncContext asyncContext, out bool cancelled)
  {
   cancelled = false;
   _status = ProgressStatus.Started;
   _fileCount = 0;
   _folderCount = 0;
   _progress = 0;
   for (int folderCount = 0; folderCount < 100; folderCount++)
   {
    // if (_progress >= 100) _progress = 0;

    // compute progress
    // int _progress = 100 * (i + 1) / SelectedFiles.Length;

    _progress = 100 * (folderCount + 1) / 100;
    _status = ProgressStatus.ProcessingFolders;
    _folderCount = folderCount;
    path = string.Format("Project{0}", _folderCount);
    asyncOperation.Post(e => DoOnTaskProgressChanged((TaskProgressChangedEventArgs)e), new TaskProgressChangedEventArgs(_status, path, _progress, null));
    Thread.Sleep(100);
    for (int fileCount = 0; fileCount < 100; fileCount++)
    {
     _status = ProgressStatus.ProcessingFiles;
     _fileCount = fileCount;
     path = string.Format("Project{0}, Class{1}.cs", _folderCount, _fileCount);
     asyncOperation.Post(e => DoOnTaskProgressChanged((TaskProgressChangedEventArgs)e), new TaskProgressChangedEventArgs(_status, path, _progress, null));

     if (asyncContext.IsCancelling)
     {
      cancelled = true;
      _status = ProgressStatus.Stopped;
      return;
     }
     Thread.Sleep(10);
    }

    if (asyncContext.IsCancelling)
    {
     cancelled = true;
     _status = ProgressStatus.Stopped;
     return;
    }
   }
   _status = ProgressStatus.Completed;
  }
  private void TaskCompletedCallback(IAsyncResult asyncResult)
  {
   // Retrieve the delegate.
   AsyncResult result = (AsyncResult)asyncResult;
   // get the original worker delegate and the AsyncOperation instance
   TaskWorkerDelegate worker = (TaskWorkerDelegate)result.AsyncDelegate;
   AsyncOperation async = (AsyncOperation)asyncResult.AsyncState;
   bool cancelled;

   // finish the asynchronous operation
   worker.EndInvoke(out cancelled, asyncResult);

   // clear the running task flag
   lock (_sync)
   {
    _taskRunning = false;
    _taskContext = null;
   }

   // raise the completed event
   AsyncCompletedEventArgs completedArgs = new AsyncCompletedEventArgs(null, cancelled, null);
   async.PostOperationCompleted(e => DoOnTaskCompleted((AsyncCompletedEventArgs)e), completedArgs);
  }
  #endregion

  #region Public Methods
  public void StartAsync(string path)
  {
   TaskWorkerDelegate worker = StartProcessing;
   AsyncCallback completedCallback = TaskCompletedCallback;

   lock (_sync)
   {
    if (_taskRunning)
     throw new InvalidOperationException("The control is currently busy.");

    AsyncOperation async = AsyncOperationManager.CreateOperation(null);
    AsyncContext asyncContext = new AsyncContext();
    bool cancelled;
    worker.BeginInvoke(path, async, asyncContext, out cancelled, completedCallback, async);
    _taskRunning = true;
    _taskContext = asyncContext;
   }
  }
  public void CancelAsync()
  {
   lock (_sync)
   {
    if (_taskContext != null)
     _taskContext.Cancel();
   }
  }
  #endregion

 }

 internal class AsyncContext
 {
  private readonly object _sync = new object();
  private bool _isCancelling;

  public bool IsCancelling
  {
   get
   {
    lock (_sync) { return _isCancelling; }
   }
  }

  public void Cancel()
  {
   lock (_sync) { _isCancelling = true; }
  }
 }

 public enum ProgressStatus
 {
  Idle,
  Started,
  Stopped,
  Completed,
  ProcessingFiles,
  ProcessingFolders
 }

 public class TaskProgressChangedEventArgs : ProgressChangedEventArgs
 {
  public string FileName { get; private set; }
  public ProgressStatus Status { get; private set; }
  public TaskProgressChangedEventArgs(ProgressStatus status, string fileName, int progressPercent, object userState)
   : base(progressPercent, userState)
  {
   FileName = fileName;
   Status = status;
  }
 }

 public delegate void TaskProgressChangedEventHandler(object sender, TaskProgressChangedEventArgs e);
}
File 2: ProgressController.cs
using System.ComponentModel;

namespace BackgroundWorkerClient
{
 public class ProgressController
 {
  private Entity _entity;
  public event AsyncCompletedEventHandler OnCompleted;
  public event TaskProgressChangedEventHandler OnProgressChanged;

  public ProgressController()
  {
   Initialize();
  }

  private void Initialize()
  {
   _entity = new Entity();
   _entity.OnTaskCompleted += DoOnCompleted;
   _entity.OnTaskProgressChanged += DoOnProgressChanged;
  }

  public void Start(string path)
  {
   _entity.StartAsync(path);
  }

  public void Stop()
  {
   _entity.CancelAsync();
  }


  void DoOnProgressChanged(object sender, TaskProgressChangedEventArgs progressArgs)
  {
   if (OnProgressChanged != null)
    OnProgressChanged(this, progressArgs);
  }

  void DoOnCompleted(object sender, AsyncCompletedEventArgs completedArgs)
  {
   if (OnCompleted != null)
   {
    OnCompleted(this, completedArgs);  
   }
  }

 }
}

File 3: AsyncClient.cs
using System;
using System.Threading;
using System.ComponentModel;
using System.Windows.Forms;
using System.Diagnostics;

namespace BackgroundWorkerClient
{
 public class AsyncClient : Form
 {
  Label _lblStatus;
  Button _btnStart;
  Button _btnCancel;
  ProgressBar _progressBar;
  BackgroundWorker _backgroundWorker;
  CheckBox _ckUseController;
  ProgressController _controller;

  public AsyncClient()
  {
   InitializeComponent();
   InitializeController();
   SetButtons(true);
  }

  private void InitializeController()
  {
   _controller = new ProgressController();
   _controller.OnCompleted += OnControllerTaskCompleted;
   _controller.OnProgressChanged += OnControllerProgressChanged;
  }

  #region Windows Form Designer generated code
  void InitializeComponent()
  {
   this._lblStatus = new System.Windows.Forms.Label();
   this._progressBar = new System.Windows.Forms.ProgressBar();
   this._btnCancel = new System.Windows.Forms.Button();
   this._btnStart = new System.Windows.Forms.Button();
   this._backgroundWorker = new System.ComponentModel.BackgroundWorker();
   this._ckUseController = new System.Windows.Forms.CheckBox();
   this.SuspendLayout();
   // 
   // _lblStatus
   // 
   this._lblStatus.AutoSize = true;
   this._lblStatus.Location = new System.Drawing.Point(12, 25);
   this._lblStatus.Name = "_lblStatus";
   this._lblStatus.Size = new System.Drawing.Size(101, 13);
   this._lblStatus.TabIndex = 0;
   this._lblStatus.Text = "Status: Not Started";
   // 
   // _progressBar
   // 
   this._progressBar.Location = new System.Drawing.Point(12, 43);
   this._progressBar.MarqueeAnimationSpeed = 50;
   this._progressBar.Name = "_progressBar";
   this._progressBar.Size = new System.Drawing.Size(337, 21);
   this._progressBar.Style = System.Windows.Forms.ProgressBarStyle.Continuous;
   this._progressBar.TabIndex = 1;
   // 
   // _btnCancel
   // 
   this._btnCancel.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
   this._btnCancel.Enabled = false;
   this._btnCancel.Location = new System.Drawing.Point(274, 82);
   this._btnCancel.Name = "_btnCancel";
   this._btnCancel.Size = new System.Drawing.Size(75, 23);
   this._btnCancel.TabIndex = 2;
   this._btnCancel.Text = "&Cancel";
   this._btnCancel.Click += new System.EventHandler(this.OnCancel);
   // 
   // _btnStart
   // 
   this._btnStart.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
   this._btnStart.Location = new System.Drawing.Point(183, 82);
   this._btnStart.Name = "_btnStart";
   this._btnStart.Size = new System.Drawing.Size(75, 23);
   this._btnStart.TabIndex = 3;
   this._btnStart.Text = "&Start";
   this._btnStart.Click += new System.EventHandler(this.OnStart);
   // 
   // _backgroundWorker
   // 
   this._backgroundWorker.WorkerReportsProgress = true;
   this._backgroundWorker.WorkerSupportsCancellation = true;
   this._backgroundWorker.DoWork += new System.ComponentModel.DoWorkEventHandler(this.OnDoWork);
   this._backgroundWorker.RunWorkerCompleted += new System.ComponentModel.RunWorkerCompletedEventHandler(this.OnCompleted);
   this._backgroundWorker.ProgressChanged += new System.ComponentModel.ProgressChangedEventHandler(this.OnProgressChanged);
   // 
   // _ckUseController
   // 
   this._ckUseController.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
   this._ckUseController.AutoSize = true;
   this._ckUseController.Location = new System.Drawing.Point(15, 86);
   this._ckUseController.Name = "_ckUseController";
   this._ckUseController.Size = new System.Drawing.Size(141, 17);
   this._ckUseController.TabIndex = 4;
   this._ckUseController.Text = "&Process using Controller";
   this._ckUseController.UseVisualStyleBackColor = true;
   // 
   // AsyncClient
   // 
   this.ClientSize = new System.Drawing.Size(361, 117);
   this.Controls.Add(this._ckUseController);
   this.Controls.Add(this._lblStatus);
   this.Controls.Add(this._btnStart);
   this.Controls.Add(this._btnCancel);
   this.Controls.Add(this._progressBar);
   this.Font = new System.Drawing.Font("Tahoma", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
   this.MaximizeBox = false;
   this.MinimizeBox = false;
   this.Name = "AsyncClient";
   this.ShowInTaskbar = false;
   this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen;
   this.Text = "Async Client";
   this.ResumeLayout(false);
   this.PerformLayout();

  }
  #endregion

  [STAThread]
  static void Main()
  {
   Application.EnableVisualStyles();
   Application.Run(new AsyncClient());
  }

  void OnDoWork(object sender, DoWorkEventArgs doWorkArgs)
  {
   Debug.Assert(doWorkArgs.Argument != null);
   BackgroundWorker backgroundWorker = sender as BackgroundWorker;
   Debug.Assert(backgroundWorker != null);

   int count = (int)doWorkArgs.Argument;

   doWorkArgs.Result = null;

   for (int progress = 0; progress <= count; progress += count / 10)
   {
    if (backgroundWorker.CancellationPending)
    {
     doWorkArgs.Cancel = true;
     break;
    }
    Thread.Sleep(500);

    backgroundWorker.ReportProgress(progress);
   }
  }

  void OnProgressChanged(object sender, ProgressChangedEventArgs progressArgs)
  {
   _progressBar.Value = progressArgs.ProgressPercentage;
  }

  void OnCompleted(object sender, RunWorkerCompletedEventArgs completedEventArgs)
  {
   _lblStatus.Text = completedEventArgs.Cancelled ? "Process: Cancelled" : "Process: Completed";
   SetButtons(true);
  }

  void OnControllerProgressChanged(object sender, TaskProgressChangedEventArgs progressArgs)
  {
   _lblStatus.Text = string.Format("{0} : {1}", progressArgs.Status, progressArgs.FileName);
   _progressBar.Value = progressArgs.ProgressPercentage;
  }

  void OnControllerTaskCompleted(object sender, AsyncCompletedEventArgs completedArgs)
  {
   _lblStatus.Text = completedArgs.Cancelled ? "Process: Cancelled" : "Process: Completed";
   SetButtons(true);
  }

  private void SetButtons(bool isCompleted)
  {
   _btnCancel.Enabled = !isCompleted;
   _btnStart.Enabled = isCompleted;
   _ckUseController.Enabled = isCompleted;
  }

  public void TraceThread()
  {
   Trace.WriteLine(Thread.CurrentThread.ManagedThreadId);
  }

  void OnCancel(object sender, EventArgs e)
  {
   if (_ckUseController.Checked)
    _controller.Stop();
   else
    _backgroundWorker.CancelAsync();

   SetButtons(true);   
  }

  void OnStart(object sender, EventArgs e)
  {
   if (_ckUseController.Checked)
    _controller.Start("empty");
   else
    _backgroundWorker.RunWorkerAsync(100);

   _lblStatus.Text = "Status: In Progress";
   SetButtons(false);
  }

 }
}

Some of the useful links from MSDN:

Thursday, December 10, 2009

Difference between IServiceBehavior and IEndpointBehavior

I believe this could sound silly, but for those who are fairly new to WCF definitely would like know. By looking at the interface names, we can say that one is for extending the "Service" and the other is for extending "Endpoints". We have recently done couple of interesting things in our organization and thought would share this information with you guys.

The ApplyDispatchBehavior method on IServiceBehavior has access to all endpoints and their runtime components and so shouldn't that be enough for wiring up customization? The short answer is yes, but there are subtle differences.

Some usability differences are:
  • ServiceBehavior applies only on a Service while EndpointBehavior applies on both client and service.

  • ServiceBehavior can be specified via Attributes/Code/Config file while Endpointbehavior can be specified through Code or Config file.

  • ServiceBehavior has access to all ServiceEndpoints dispatch runtime and so could modify all dispatch runtimes while EndpointBehavior gets called with the runtime for that endpoint only.


Look at it this way, ServiceBehavior lets you access runtime parameters for all endpoints while EndpointBehavior lets you access runtime components only for that endpoint. So if you have a need to extend functionality that spawns the entire contract (or multiple contracts) then use ServiceBehavior and if you are interested in extending one specific endpoint then use EndpointBehavior.

And of course, the biggest difference is if you want to customize endpoints on client/consumer then the only option is IEndpointBehavior.

Thursday, August 06, 2009

Get Ready for Delphi RAD Studio 2010

New features include:
  • IDE Insight – a timesaving tool to easily find files, components, features and settings using simple keystrokes and search terms

  • Code Formatter – to implement consistent coding styles with minimal effort

  • Class Explorer – for a configurable hierarchical view of class libraries throughout a project and enabling fast navigation to declarations and implementations and now available for C++Builder

  • Data Visualizers – that make debugging easier by displaying visual representations of data in definable forms

  • Debugger Thread Control – to freeze, thaw and isolate individual threads within applications during debugging to track down problems faster
  • Tuesday, August 19, 2008

    What is LINQ?

    LINQ stands for Language-Integrated Query is a set of features in Visual Studio 2008 (code name "Orcas") that extends powerful query capabilities to the language syntax of C# and Visual Basic. Microsoft previous efforts (Windows Communication Foundation WCF, Windows Workflow Foundation WWF, Windows Presentation Foundation WPF, Windows CardSpace and LINQ) are integrated in this Studio. LINQ introduces standard, easily-learned patterns for querying and updating data, and the technology can be extended to support potentially any kind of data store. Visual Studio 2008 includes LINQ provider assemblies that enable the use of LINQ with .NET Framework collections, SQL Server databases, ADO.NET Datasets, and XML documents.

    From past one and half years Anders Hejlsberg team has done a wonderful job in overcoming the gap between the data impedance. Mr. Anders team gives a native syntax to developers in the form LINQ to C# and VB.Net for accessing data from any repository. The repository could be in memory object, database (still MS SQL Server only) and XML files.

    What is LINQ?
    Basically LINQ address the current database development model in the context of Object Oriented Programming Model. If some one wants to develop database application on .Net platform the very simple approach he uses ADO.Net. ADO.Net is serving as middle ware in application and provides complete object oriented wrapper around the database SQL. Developing application in C# and VB.Net so developer must have good knowledge of object oriented concept as well as SQL, so it means developer must be familiar with both technologies to develop an application. If here I can say SQL statements are become part of the C# and VB.Net code so it’s not mistaken in form of LINQ. According to Anders Hejlsberg the chief architect of C#.

    Some of the examples are:

    Select:
    This sample code prints a sequence of integers one greater than those in an input array. The sample uses the expression in the select clause to add one to each element in the new sequence.


    public void Linq6()
    {
    int[] numbers = { 5, 4, 1, 3, 9, 8, 6, 7, 2, 0 };
    var numsPlusOne = from n in numbers select n + 1;

    Console.WriteLine("Numbers + 1:");
    foreach (var i in numsPlusOne)
    {
    Console.WriteLine(i);
    }
    }

    Result:

    Numbers + 1:
    6
    5
    2
    4
    10
    9
    7
    8
    3
    1

    Where
    This sample prints each element of an input integer array whose value is less than 5. The sample uses a query expression to create a new sequence of integers and then iterates over each element in the sequence, printing its value.
     
    public void Linq1()
    {
    int[] numbers = { 5, 4, 1, 3, 9, 8, 6, 7, 2, 0 };

    var lowNums =
    from n in numbers
    where n < 5
    select n;

    Console.WriteLine("Numbers < 5:");
    foreach (var x in lowNums)
    {
    Console.WriteLine(x);
    }
    }

    Result:

    Numbers < 5:
    4
    1
    3
    2
    0

    OrderBy

    This sample prints an alphabetically sorted version of an input string array. The sample uses orderby to perform the sort.

    public void Linq28()
    {
    string[] words = { "cherry", "apple", "blueberry" };

    var sortedWords =
    from w in words
    orderby w
    select w;

    Console.WriteLine("The sorted list of words:");
    foreach (var w in sortedWords)
    {
    Console.WriteLine(w);
    }
    }

    Result:
    The sorted list of words:
    apple
    blueberry
    cherry

    There are 101 sample snippets available on MSDN.

    Monday, August 18, 2008

    Consuming ASP.NET 2.0 Web Services in Delphi for Win32

    This is a very good article by Dr.Bob. Please read it whenever you find some time, it really helps! Thanks so much to Dr.Bob.

    Friday, June 13, 2008

    Difference between two different time stamps

    Today I had a cute requirement which is pretty small. The user want to
    see the time difference between two different time stamps.Steps to do
    this1) Create a column Date1 of DateTime2) Create a column Date2 of
    DateTime3) Create another column of Datatype Calculated type.then enter
    the following formula in that column settings =TEXT([DATE2]-[DATE1],"H:MM").
    then you are done

    --
    Posted By "Murali Krishna" (muki) to Delphi Tips & Tricks on 6/10/2008 03:55:00 AM

    Thursday, May 29, 2008

    Delphi 7 is now compatible with Vista


    Delphi 7 is now compatible with Vista, or Vista is now compatible with Delphi 7... anyway you look at it, I find this amusing.
    Read it on marco's tech world.

    Managing NT Services from Delphi


    {-------------------------------------------------------------------------------------
    Unit: uServiceManager.pas
    Purpose: Wrapper around some of the Windows API Functions supporting NT-Services.
    The following class TServiceManager can be used to manage your NT-Services.
    You can do things like start, stop, pause or querying a services status.
    Author: Kiran Kurapaty.
    Copyright: Kurapaty Solutions
    Dated: 18th September, 2003 at 19.10 hours IST.
    Email Id: kiran@kurapaty.co.uk / kiran.delphi@gmail.com
    -------------------------------------------------------------------------------------}

    unit uServiceManager;

    interface

    uses
    SysUtils, Windows, WinSvc;

    type

    TServiceManager = class
    private
    { Private declarations }
    ServiceControlManager: SC_Handle;
    ServiceHandle: SC_Handle;
    fServiceName: String;
    protected
    function DoStartService(NumberOfArgument: DWORD; ServiceArgVectors: PChar): Boolean;
    public
    { Public declarations }
    destructor Destroy; override;
    function Connect(MachineName: PChar = nil; DatabaseName: PChar = nil;
    Access: DWORD = SC_MANAGER_ALL_ACCESS): Boolean; // Access may be SC_MANAGER_ALL_ACCESS
    function OpenServiceConnection(aServiceName: PChar): Boolean;
    function StartService: Boolean; overload; // Simple start
    function StartService(NumberOfArgument: DWORD; ServiceArgVectors: PChar): Boolean; overload; // More complex start
    function StopService: Boolean;
    function PauseService: Boolean;
    function ContinueService: Boolean;
    function ShutdownService: Boolean;
    function DisableService: Boolean;
    function GetStatus: DWORD;
    function IsServiceRunning: Boolean;
    function IsServiceStopped: Boolean;
    published
    property ServiceName : String read fServiceName write fServiceName;
    end;

    implementation

    { TServiceManager }

    function TServiceManager.Connect(MachineName, DatabaseName: PChar; Access: DWORD): Boolean;
    begin
    { open a connection to the windows service manager }
    ServiceControlManager := OpenSCManager(MachineName, DatabaseName, Access);
    Result := (ServiceControlManager <> 0);
    end;


    function TServiceManager.OpenServiceConnection(aServiceName: PChar): Boolean;
    begin
    { open a connetcion to a specific service }
    fServiceName := aServiceName;
    ServiceHandle := OpenService(ServiceControlManager, aServiceName, SERVICE_ALL_ACCESS);
    Result := (ServiceHandle <> 0);
    end;

    function TServiceManager.PauseService: Boolean;
    var
    ServiceStatus: TServiceStatus;
    begin
    { Pause the service: attention not supported by all services }
    Result := ControlService(ServiceHandle, SERVICE_CONTROL_PAUSE, ServiceStatus);
    end;

    function TServiceManager.StopService: Boolean;
    var
    ServiceStatus: TServiceStatus;
    begin
    { Stop the service }
    Result := ControlService(ServiceHandle, SERVICE_CONTROL_STOP, ServiceStatus);
    end;

    function TServiceManager.ContinueService: Boolean;
    var
    ServiceStatus: TServiceStatus;
    begin
    { Continue the service after a pause: attention not supported by all services }
    Result := ControlService(ServiceHandle, SERVICE_CONTROL_CONTINUE, ServiceStatus);
    end;

    function TServiceManager.ShutdownService: Boolean;
    var
    ServiceStatus: TServiceStatus;
    begin
    { Shut service down: attention not supported by all services }
    Result := ControlService(ServiceHandle, SERVICE_CONTROL_SHUTDOWN, ServiceStatus);
    end;

    function TServiceManager.StartService: Boolean;
    begin
    Result := DoStartService(0, '');
    end;

    function TServiceManager.StartService(NumberOfArgument: DWORD; ServiceArgVectors: PChar): Boolean;
    begin
    Result := DoStartService(NumberOfArgument, ServiceArgVectors);
    end;

    function TServiceManager.GetStatus: DWORD;
    var
    ServiceStatus: TServiceStatus;
    begin
    { Returns the status of the service. Maybe you want to check this
    more than once, so just call this function again.
    Results may be: SERVICE_STOPPED
    SERVICE_START_PENDING
    SERVICE_STOP_PENDING
    SERVICE_RUNNING
    SERVICE_CONTINUE_PENDING
    SERVICE_PAUSE_PENDING
    SERVICE_PAUSED }
    QueryServiceStatus(ServiceHandle, ServiceStatus);
    Result := ServiceStatus.dwCurrentState;
    end;

    function TServiceManager.DisableService: Boolean;
    begin
    { Need to Implement... }
    Result := False;
    end;

    function TServiceManager.IsServiceRunning: Boolean;
    begin
    Result := (GetStatus = SERVICE_RUNNING);
    end;

    function TServiceManager.IsServiceStopped: Boolean;
    begin
    Result := (GetStatus = SERVICE_STOPPED);
    end;

    function TServiceManager.DoStartService(NumberOfArgument: DWORD;
    ServiceArgVectors: PChar): Boolean;
    begin
    Result := WinSvc.StartService(ServiceHandle, NumberOfArgument, ServiceArgVectors);
    end;

    destructor TServiceManager.Destroy;
    begin
    if (ServiceHandle <> 0) then
    CloseServiceHandle(ServiceHandle);
    inherited;
    end;

    end.

    Friday, February 15, 2008

    Adding CheckBox to a TShellTreeView


    unit Unit1;

    interface

    uses
    Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, ShellCtrls,
    ComCtrls;

    type
    TCheckBoxShellTreeView = class(TShellTreeView)
    public
    procedure CreateParams(var Params: TCreateParams); override;
    end;

    TForm1 = class(TForm)
    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
    private
    { Private declarations }
    FShellTreeView: TCheckBoxShellTreeView;
    public
    { Public declarations }
    end;

    var
    Form1: TForm1;

    implementation

    {$R *.dfm}

    const
    TVS_CHECKBOXES = $00000100;

    procedure TCheckBoxShellTreeView.CreateParams(var Params: TCreateParams);
    begin
    inherited;
    Params.Style := Params.Style or TVS_CHECKBOXES;
    end;

    procedure TForm1.FormCreate(Sender: TObject);
    begin
    FShellTreeView := TCheckBoxShellTreeView.Create(Self);
    with FShellTreeView do
    begin
    Name := 'FShellTreeView';
    Parent := Self;
    Root := 'rfDesktop';
    UseShellImages := True;
    Align := alClient;
    AutoRefresh := False;
    BorderStyle := bsNone;
    Indent := 19;
    ParentColor := False;
    RightClickSelect := True;
    ShowRoot := False;
    TabOrder := 0;
    end;
    end;

    procedure TForm1.FormDestroy(Sender: TObject);
    begin
    FShellTreeView.Free;
    end;

    end.

    Wednesday, January 30, 2008

    WNet Enum Class

    WNetEnumClass. This class implements the discovery of connected computers, drives, and printers, using the WNet functions.

    TODO: Add an array of all TNetResources, and functions to allow their access from the calling app. Or more properly, functions that will return useful info from the TNetResources, simplifying the determination of device type, and so on.

    USAGE:

    var
    Obj :TWNetEnumClass;
    begin
    Obj := TWNetEnumClass.Create(Self);
    try
    Obj.Refresh;
    Memo1.Lines := Obj.GetAllNames;
    Memo2.Lines := Obj.GetCompNames;
    Memo3.Lines := Obj.GetDiskNames;
    Memo4.Lines := Obj.GetDomainNames;
    Memo5.Lines := Obj.GetErrors;
    Memo6.Lines := Obj.GetPrintNames;
    finally
    Obj.Free;
    end;
    end;



    unit uWNetEnumClass;

    interface

    uses
    Classes, Sysutils, Windows;

    type
    TWNetEnumClass = class(TObject)
    private
    FslAllNames: TStringList;
    FslCompNames: TStringList;
    FslDiskNames: TStringList;
    FslDomainNames: TStringList;
    FslErrors: TStringList;
    FslPrintNames: TStringList;
    procedure ErrorHandler(errorNum: Cardinal; s: string);
    function EnumerateResources(startingPoint: TNetResource): Boolean;
    protected
    procedure EnumResources;
    public
    constructor Create(Owner: TComponent); virtual;
    destructor Destroys; virtual;

    function GetAllNames: TStringList;
    function GetCompNames: TStringList;
    function GetDiskNames: TStringList;
    function GetDomainNames: TStringList;
    function GetErrors: TStringList;
    function GetPrintNames: TStringList;

    procedure Refresh; // used by calling apps to populate the lists
    end;

    implementation

    { TWNetEnumClass }

    const
    BASE_RES = 128;
    MAX_RES = 8192;

    var
    // establish a buffer to use to prime the drill-down process
    base_buffer: array of TNetResource;

    constructor TWNetEnumClass.Create(Owner: TComponent);
    begin
    inherited Create;

    SetLength(base_buffer, BASE_RES); // initialize the base buffer

    // now create the stringlists we will use
    FslAllNames := TStringList.Create;
    FslCompNames := TStringList.Create;
    FslDiskNames := TStringList.Create;
    FslDomainNames := TStringList.Create;
    FslErrors := TStringList.Create;
    FslPrintNames := TStringList.Create;
    end;

    destructor TWNetEnumClass.Destroys;
    begin
    // free the stringlists
    FslPrintNames.Free;
    FslErrors.Free;
    FslDomainNames.Free;
    FslDiskNames.Free;
    FslCompNames.Free;
    FslAllNames.Free;

    base_buffer := nil; // free the base buffer

    inherited Destroy;
    end;

    //

    function TWNetEnumClass.EnumerateResources(startingPoint: TNetResource): Boolean;
    var
    res: Cardinal;
    resEnum: Cardinal;
    enumHandle: THandle;
    buffer: array of TNetResource;
    bufferSize: Cardinal;
    numEntries: Cardinal;
    i: Cardinal;
    begin // EnumerateResources
    // Open a container
    res := WNetOpenEnum(
    RESOURCE_GLOBALNET, RESOURCETYPE_ANY, 0, @startingPoint, enumHandle);
    if (res <> NO_ERROR) then
    ErrorHandler(res, 'WNetOpenEnum');

    // loop through all the elements in the container
    repeat
    numEntries := Cardinal(-1);
    SetLength(buffer, MAX_RES);
    bufferSize := SizeOf(TNetResource) * MAX_RES;

    // get resources
    resEnum := WNetEnumResource(enumHandle, numEntries, buffer, bufferSize);
    if (resEnum = NO_ERROR) then
    begin
    // loop through all entries
    for i := 0 to numEntries - 1 do
    begin
    if (buffer[i].dwDisplayType = RESOURCEDISPLAYTYPE_SERVER) then
    FslCompNames.Add(buffer[i].lpRemoteName)
    else if (buffer[i].dwType = RESOURCETYPE_PRINT) then
    FslPrintNames.Add(buffer[i].lpRemoteName)
    else if (buffer[i].dwType = RESOURCETYPE_DISK) then
    FslDiskNames.Add(buffer[i].lpRemoteName)
    else if (buffer[i].dwDisplayType = RESOURCEDISPLAYTYPE_DOMAIN) then
    FslDomainNames.Add(buffer[i].lpRemoteName);
    // if the entry is a container, recursively open it
    if (buffer[i].dwUsage and RESOURCEUSAGE_CONTAINER > 0) then
    if (not EnumerateResources(buffer[i])) then
    FslErrors.Add('Enumeration failed');
    end;
    end
    else if (resEnum <> ERROR_NO_MORE_ITEMS) then
    ErrorHandler(resEnum, 'WNetEnumResource');
    { added the test for ERROR_INVALID_HANDLE to deal with the case where a "remembered"
    connection is no longer in existence. I need to look for a cleaner fix. }
    until (resEnum = ERROR_NO_MORE_ITEMS) or (resEnum = ERROR_INVALID_HANDLE);

    // clean up
    buffer := nil;
    res := WNetCloseEnum(enumHandle);
    if (res <> NO_ERROR) then
    begin
    ErrorHandler(res, 'WNetCloseEnum');
    result := False;
    end
    else
    result := True;
    end;

    procedure TWNetEnumClass.EnumResources;
    begin
    EnumerateResources(base_buffer[0]);
    end;

    procedure TWNetEnumClass.ErrorHandler(errorNum: Cardinal; s: string);
    var
    res: Cardinal;
    error: Cardinal;
    errorStr: string;
    nameStr: string;
    begin
    if (errorNum <> ERROR_EXTENDED_ERROR) then
    begin
    FslErrors.Add('Error number ' + IntToStr(errorNum) + ' returned by ' + s);
    end
    else
    begin
    res := WNetGetLastError(
    error, PChar(errorStr), 1000, PChar(nameStr), 1000);
    if (res <> NO_ERROR) then
    FslErrors.Add('Failure in WNetGetLastError: ' + IntToStr(error))
    else
    begin
    FslErrors.Add('Extended Error: ' + errorStr + '. Provider: ' + nameStr);
    end;
    end;
    end;

    function TWNetEnumClass.GetAllNames: TStringList;
    begin
    FslAllNames.Sort;
    Result := FslAllNames;
    end;

    function TWNetEnumClass.GetCompNames: TStringList;
    begin
    FslCompNames.Sort;
    Result := FslCompNames;
    end;

    function TWNetEnumClass.GetDiskNames: TStringList;
    begin
    FslDiskNames.Sort;
    Result := FslDiskNames;
    end;

    function TWNetEnumClass.GetDomainNames: TStringList;
    begin
    FslDomainNames.Sort;
    Result := FslDomainNames;
    end;

    function TWNetEnumClass.GetErrors: TStringList;
    begin
    Result := FslErrors;
    end;

    function TWNetEnumClass.GetPrintNames: TStringList;
    begin
    FslPrintNames.Sort;
    Result := FslPrintNames;
    end;

    procedure TWNetEnumClass.Refresh;
    begin
    FslAllNames.Clear;
    FslCompNames.Clear;
    FslDiskNames.Clear;
    FslDomainNames.Clear;
    FslErrors.Clear;
    FslPrintNames.Clear;
    EnumResources;
    end;

    end.

    Creating Excel (XLS) from Delphi


    const
    CXlsBof: array[0..5] of Word = ($809, 8, 00, $10, 0, 0);
    CXlsEof: array[0..1] of Word = ($0A, 00);
    CXlsLabel: array[0..5] of Word = ($204, 0, 0, 0, 0, 0);
    CXlsNumber: array[0..4] of Word = ($203, 14, 0, 0, 0);
    CXlsRk: array[0..4] of Word = ($27E, 10, 0, 0, 0);

    procedure XlsBeginStream(XlsStream: TStream; const BuildNumber: Word);
    begin
    CXlsBof[4] := BuildNumber;
    XlsStream.WriteBuffer(CXlsBof, SizeOf(CXlsBof));
    end;

    procedure XlsEndStream(XlsStream: TStream);
    begin
    XlsStream.WriteBuffer(CXlsEof, SizeOf(CXlsEof));
    end;

    procedure XlsWriteCellRk(XlsStream: TStream; const ACol, ARow: Word; const AValue: Integer);
    var
    V: Integer;
    begin
    CXlsRk[2] := ARow;
    CXlsRk[3] := ACol;
    XlsStream.WriteBuffer(CXlsRk, SizeOf(CXlsRk));
    V := (AValue shl 2) or 2;
    XlsStream.WriteBuffer(V, 4);
    end;

    procedure XlsWriteCellNumber(XlsStream: TStream; const ACol, ARow: Word; const AValue: Double);
    begin
    CXlsNumber[2] := ARow;
    CXlsNumber[3] := ACol;
    XlsStream.WriteBuffer(CXlsNumber, SizeOf(CXlsNumber));
    XlsStream.WriteBuffer(AValue, 8);
    end;

    procedure XlsWriteCellLabel(XlsStream: TStream; const ACol, ARow: Word; const AValue: string);
    var
    L: Word;
    begin
    L := Length(AValue);
    CXlsLabel[1] := 8 + L;
    CXlsLabel[2] := ARow;
    CXlsLabel[3] := ACol;
    CXlsLabel[5] := L;
    XlsStream.WriteBuffer(CXlsLabel, SizeOf(CXlsLabel));
    XlsStream.WriteBuffer(Pointer(AValue)^, L);
    end;

    procedure TForm1.Button1Click(Sender: TObject);
    var
    FStream: TFileStream;
    I, J: Integer;
    begin
    FStream := TFileStream.Create('J:\e.xls', fmCreate);
    try
    XlsBeginStream(FStream, 0);
    for I := 0 to 99 do
    for J := 0 to 99 do
    begin
    XlsWriteCellNumber(FStream, I, J, 34.34);
    // XlsWriteCellRk(FStream, I, J, 3434);
    // XlsWriteCellLabel(FStream, I, J, Format('Cell: %d,%d', [I, J]));
    end;
    XlsEndStream(FStream);
    finally
    FStream.Free;
    end;
    end;

    Paint Dock Frame


    procedure TDockTree.PaintDockFrame(Canvas: TCanvas; Control: TControl; const ARect: TRect);

    procedure DrawCloseButton(Left, Top: Integer);
    begin
    DrawFrameControl(Canvas.Handle, Rect(Left, Top, Left+FGrabberSize-2,
    Top+FGrabberSize-2), DFC_CAPTION, DFCS_CAPTIONCLOSE);
    end;

    procedure DrawGrabberLine(Left, Top, Right, Bottom: Integer);
    begin
    with Canvas do
    begin
    Pen.Color := clBtnHighlight;
    MoveTo(Right, Top);
    LineTo(Left, Top);
    LineTo(Left, Bottom);
    Pen.Color := clBtnShadow;
    LineTo(Right, Bottom);
    LineTo(Right, Top-1);
    end;
    end;

    begin
    with ARect do
    if FDockSite.Align in [alTop, alBottom] then
    begin
    DrawCloseButton(Left+1, Top+1);
    DrawGrabberLine(Left+3, Top+FGrabberSize+1, Left+5, Bottom-2);
    DrawGrabberLine(Left+6, Top+FGrabberSize+1, Left+8, Bottom-2);
    end
    else
    begin
    DrawCloseButton(Right-FGrabberSize+1, Top+1);
    DrawGrabberLine(Left+2, Top+3, Right-FGrabberSize-2, Top+5);
    DrawGrabberLine(Left+2, Top+6, Right-FGrabberSize-2, Top+8);
    end;
    end;

    Convert Shockwave SWF to EXE

    Example:
     
    Swf2Exe('C:\somefile.swf', 'C:\somefile.exe', 'C:\Program Files\MacromediaFlash MX\Players\SAFlashPlayer.exe');



    function Swf2Exe(SourceSWF, exeFile, FlashPlayer : string): string;
    var
    SourceStream, DestinyStream, LinkStream : TFileStream;
    flag : Cardinal;
    SwfFileSize : integer;
    begin
    result := 'Error';
    DestinyStream := TFileStream.Create(exeFile, fmCreate);
    try
    LinkStream := TFileStream.Create(FlashPlayer, fmOpenRead or fmShareExclusive);
    try
    DestinyStream.CopyFrom(LinkStream, 0);
    finally
    LinkStream.Free;
    end;

    SourceStream := TFileStream.Create(SourceSWF, fmOpenRead or fmShareExclusive);
    try
    DestinyStream.CopyFrom(SourceStream, 0);
    flag := $FA123456;
    DestinyStream.WriteBuffer(flag, sizeof(integer));
    SwfFileSize := SourceStream.Size;
    DestinyStream.WriteBuffer(SwfFileSize, sizeof(integer));
    result := '';
    finally
    SourceStream.Free;
    end;
    finally
    DestinyStream.Free;
    end;
    end;

    Customise your Message Dialog


    procedure TForm1.Button2Click(Sender: TObject);
    var
    Dlg: TForm;
    Rslt: Integer;
    begin
    Dlg := CreateMessageDialog('Customised MessageBox, hope this is helpful', mtConfirmation, [mbYes, mbNo, mbNoToAll]);

    { change the messagedlg caption }
    Dlg.Caption := 'Please Confirm';

    {change the button texts }
    TButton(Dlg.FindComponent('Yes')).Caption := 'Indeed!';
    TButton(Dlg.FindComponent('No')).Caption := 'Surely Not!';
    TButton(Dlg.FindComponent('NoToAll')).Caption := 'None please!';

    { change the button fonts }
    TButton(Dlg.FindComponent('Yes')).Font.Name := 'C4 Text';
    TButton(Dlg.FindComponent('No')).Font.Name := 'C4 TextMedium' ;
    TButton(Dlg.FindComponent('NoToAll')).Font.Name := 'Tahoma';
    //this sets the buttons font to Bold & Underlined
    TButton(Dlg.FindComponent('NoToAll')).Font.Style:=[fsBold];

    { change the Message's appearance }
    TLabel(Dlg.FindComponent('Message')).Font.Name := 'Courier New';
    TLabel(Dlg.FindComponent('Message')).Font.Size := 12;
    TLabel(Dlg.FindComponent('Message')).Font.Color := clRed;

    Rslt := Dlg.ShowModal;

    case Rslt of
    mrYes: { do "Yes" stuff };
    mrNo: { do "No" stuff };
    mrNoToAll: { do "No to All" stuff };
    end;
    end;

    Calculate estimated Download time of a File


    { Add this under your type declaration }

    type
    TDRec = record
    H, M, S: Integer;
    end;

    const
    Count = 6;
    BpsArray: array [0..Count] of Integer = (14400, 28800, 33600, 56000, 64000, 128000, 512000);

    function CalculateDLTime(const Value, Units, Connection: Integer): TDRec;
    var
    i, size_bits, filedltimesec, hourmod, HH, MM, SS: Integer;
    Rec: TDRec;

    function pow(a, b: Integer): Integer;
    function sl(nr, times: Integer): Integer;
    var
    i: Integer;
    begin
    Result := nr * nr;
    for i := 0 to times do Result := Result + nr * nr;
    end;
    begin
    if a > b then
    Result := sl(a, b)
    else
    Result := sl(b, a);
    end;
    begin
    case Units of
    1: size_bits := (8 div 1) * Value; // bytes
    2: size_bits := (8 div 1) * ((pow(2,10)) div 1) * Value; // kilobytes
    3: size_bits := (8 div 1) * ((pow(2,20)) div 1) * Value; // Megabytes
    end;

    // Calculate
    filedltimesec := Round(size_bits) div BpsArray[Connection];

    hourmod := filedltimesec mod (60 * 60); // Modulus.
    HH := Floor(filedltimesec / (60 * 60));
    MM := Floor(hourmod / 60);
    SS := Floor(filedltimesec mod 60); // Modulus.

    if SS > 0 then Inc(SS);

    with Rec do
    begin
    H := HH;
    M := MM;
    S := SS;
    end;

    Result := Rec;
    end;

    procedure TForm1.Button1Click(Sender: TObject);
    var
    i: Integer;
    Rec: TDRec;
    begin
    ListView1.Items.Clear;

    for i := 0 to Count do
    begin
    Rec := CalculateDLTime(StrToInt(Edit1.Text), ComboBox1.ItemIndex + 1,i);

    with ListView1.Items.Add do
    begin
    Caption := NameArray[i];
    SubItems.Add(IntToStr(Rec.H));
    SubItems.Add(IntToStr(Rec.M));
    SubItems.Add(IntToStr(Rec.S));
    end;
    end;
    end;

    Thursday, October 25, 2007

    CRC Check / CRC Values

    CRC values are calculated using an algorithm known as the Cyclic Redundancy Check, or "CRC" for short. Basically, this involves generating a 32-bit number (or "CRC value") based on the contents of a file. If the contents of a file change, its CRC value changes as well. This allows the CRC number to be used as a "checksum" in order to identify whether or not the file has changed. It also allows you to distinguish between different versions of a file by comparing its CRC value to the CRC values of the originals.

    The basic idea of the CRC algorithm is to treat all the bits in a file as one big binary number, and then divide that number by a standard value. The remainder from the division is the CRC value.

    You can think of this value as being like a fingerprint for each file. Unlike human fingerprints, however, it isn't impossible for two files to have the same CRC-32 value. UpdatesDownloader uses an industry-standard CRC-32 algorithm which generates CRC values that are 32 bits in length. This means that one in every 4,294,967,296 files could have the same CRC "fingerprint."

    A file doesn't have to change much for its CRC value to be different. In fact, if even just one bit in a file changes, the CRC value for that file will change as well. If all you did was change one letter in a readme.txt file between version 1 and version 2, the CRC value for that readme.txt file would be completely different.

    CRC values can be calculated for any type of file.

    Although the chances of any two files having the same CRC value are incredibly small, the CRC value alone isn't enough to guarantee an accurate identification. If you need to be absolutely sure, check the CRC in addition to other information about the file, such as the size of the file in bytes and its location on the user's system.

    Thursday, August 30, 2007

    Sending an email using MAPI

    There are lot of different methods to send mail using MAPI (Windows Simple Mail API)
    If you do not want to rely on Outlook to send an email but you know that MAPI is installed, then you can also send mails with the following handy routine SendMailMAPI(). You need to add unit MAPI to your uses clause. Note that MAPI is not always installed with Windows.

    unit uMAPIMail;
    
    interface
    
    uses MAPI;
    
    function SendMailMAPI(const Subject, Body, FileName, SenderName, SenderEMail,
    RecepientName, RecepientEMail: String) : Integer;
    
    implementation
    
    function SendMailMAPI(const Subject, Body, FileName, SenderName, SenderEMail,
    RecepientName, RecepientEMail: String) : Integer;
    var
    message: TMapiMessage;
    lpSender,
    lpRecepient: TMapiRecipDesc;
    FileAttach: TMapiFileDesc;
    SM: TFNMapiSendMail;
    MAPIModule: HModule;
    begin
    FillChar(message, SizeOf(message), 0);
    with message do
    begin
    if (Subject<>'') then
    begin
    lpszSubject := PChar(Subject)
    end;
    if (Body<>'') then
    begin
    lpszNoteText := PChar(Body)
    end;
    if (SenderEMail<>'') then
    begin
    lpSender.ulRecipClass := MAPI_ORIG;
    if (SenderName='') then
    begin
    lpSender.lpszName := PChar(SenderEMail)
    end
    else
    begin
    lpSender.lpszName := PChar(SenderName)
    end;
    lpSender.lpszAddress := PChar('SMTP:'+SenderEMail);
    lpSender.ulReserved := 0;
    lpSender.ulEIDSize := 0;
    lpSender.lpEntryID := nil;
    lpOriginator := @lpSender;
    end;
    if (RecepientEMail<>'') then
    begin
    lpRecepient.ulRecipClass := MAPI_TO;
    if (RecepientName='') then
    begin
    lpRecepient.lpszName := PChar(RecepientEMail)
    end
    else
    begin
    lpRecepient.lpszName := PChar(RecepientName)
    end;
    lpRecepient.lpszAddress := PChar('SMTP:'+RecepientEMail);
    lpRecepient.ulReserved := 0;
    lpRecepient.ulEIDSize := 0;
    lpRecepient.lpEntryID := nil;
    nRecipCount := 1;
    lpRecips := @lpRecepient;
    end
    else
    begin
    lpRecips := nil
    end;
    if (FileName='') then
    begin
    nFileCount := 0;
    lpFiles := nil;
    end
    else
    begin
    FillChar(FileAttach, SizeOf(FileAttach), 0);
    FileAttach.nPosition := Cardinal($FFFFFFFF);
    FileAttach.lpszPathName := PChar(FileName);
    nFileCount := 1;
    lpFiles := @FileAttach;
    end;
    end;
    MAPIModule := LoadLibrary(PChar(MAPIDLL));
    if MAPIModule=0 then
    begin
    Result := -1
    end else
    begin
    try
    @SM := GetProcAddress(MAPIModule, 'MAPISendMail');
    if @SM<>nil then
    begin
    Result := SM(0, Application.Handle, message, MAPI_DIALOG or MAPI_LOGON_UI,0);
    end else
    begin
    Result := 1
    end;
    
    finally
    FreeLibrary(MAPIModule);
    end;
    end
    if Result<>0 then
    begin
    MessageDlg('Error sending mail ('+IntToStr(Result)+').', mtError, [mbOk], 0)
    end;
    end;
    
    end.
    

    Monday, August 20, 2007

    Preventing second instance of the application

    There are many ways, but we'll see couple of examples here...

    a) Using "GlobalAddAtom" & "GlobalFindAtom" API calls

     
    procedure TForm1.FormCreate(Sender: TObject);
    begin
    {Searchs table to see if the program is already running}
    if GlobalFindAtom('PROGRAM_RUNNING') = 0 then
    { If not found then add it }
    atom := GlobalAddAtom('PROGRAM_RUNNING')
    else begin
    { If program is already running the show message and halt }
    MessageDlg('You have the program running all ready!!', mtWarning, [mbOK], 0);
    Halt;
    end;
    end;

    procedure TForm1.FormDestroy(Sender: TObject);
    begin
    {Removes the item from the table so it can be run again}
    GlobalDeleteAtom(atom);
    end;



    b) Using "CreateMutex" & "OpenMutex" you can control this from your *.dpr code itself. Here we will try to create the mutex (kernel) object on the OS when your application starts for the first time.
    ex:
    MutexHandle := CreateMutex(Nil, False, 'ANYTHING-WHICH-IS-UNIQUE-TO-UR-APP');
    if MutexHandle returns zero(0) or some error, that means the mutex is already created and your application is running in the background. Here you can just quit the application or you throw some error message to the user, using the below code...

    MutexHandle := OpenMutex(MUTEX_ALL_ACCESS, False, 'ANYTHING-WHICH-IS-UNIQUE-TO-UR-APP');

    If MutexHandle retruns zero (0) or some error, then there is some problem with your kernel object just exit from there, otherwise you can show the error message like I have shown in the above example and quit from the application.
    Finally, dont forget to free the MutexHandle. You will get more information when you search for "CreateMutex" "OpenMutex" in Google.com

    c) Using "CreateFileMapping" / "MapViewOfFile" / "UnmapViewOfFile", its bit complex to understand, but if you are familiar with Windows API calls then it would probably help you.