unit MainForm;
{*******************************************************************************
  ServiceController Demo
  Written by David Clegg, davidclegg@optusnet.com.au.

  This application is designed to demonstrate using the System.ServiceProcess
  and System.Management namespaces to control Windows Service Applications.
*******************************************************************************}

interface

uses
  System.Drawing, System.Collections, System.ComponentModel,
  System.Windows.Forms, System.Data, System.ServiceProcess, Service,
  ChangeStatusForm;

type
  TMainForm = class(System.Windows.Forms.Form)
  {$REGION 'Designer Managed Code'}
  strict private
    /// <summary>
    /// Required designer variable.
    /// </summary>
    Components: System.ComponentModel.Container;
    Panel1: System.Windows.Forms.Panel;
    btnClose: System.Windows.Forms.Button;
    lvServices: System.Windows.Forms.ListView;
    btnRefresh: System.Windows.Forms.Button;
    chName: System.Windows.Forms.ColumnHeader;
    chDesc: System.Windows.Forms.ColumnHeader;
    chStatus: System.Windows.Forms.ColumnHeader;
    Label1: System.Windows.Forms.Label;
    mmMain: System.Windows.Forms.MainMenu;
    MenuItem1: System.Windows.Forms.MenuItem;
    miClose: System.Windows.Forms.MenuItem;
    MenuItem3: System.Windows.Forms.MenuItem;
    miAbout: System.Windows.Forms.MenuItem;
    cmMain: System.Windows.Forms.ContextMenu;
    miDetails: System.Windows.Forms.MenuItem;
    MenuItem4: System.Windows.Forms.MenuItem;
    miStart: System.Windows.Forms.MenuItem;
    miStop: System.Windows.Forms.MenuItem;
    miPause: System.Windows.Forms.MenuItem;
    miResume: System.Windows.Forms.MenuItem;
    miAction: System.Windows.Forms.MenuItem;
    miDetails2: System.Windows.Forms.MenuItem;
    miStart2: System.Windows.Forms.MenuItem;
    miStop2: System.Windows.Forms.MenuItem;
    miPause2: System.Windows.Forms.MenuItem;
    miResume2: System.Windows.Forms.MenuItem;
    MenuItem5: System.Windows.Forms.MenuItem;
    miConnect: System.Windows.Forms.MenuItem;
    MenuItem6: System.Windows.Forms.MenuItem;
    /// <summary>
    /// Required method for Designer support - do not modify
    /// the contents of this method with the code editor.
    /// </summary>
    procedure InitializeComponent;
    procedure btnClose_Click(sender: System.Object; e: System.EventArgs);
    procedure btnRefresh_Click(sender: System.Object; e: System.EventArgs);
    procedure lvServices_ColumnClick(sender: System.Object; e: System.Windows.Forms.ColumnClickEventArgs);
    procedure TMainForm_Load(sender: System.Object; e: System.EventArgs);
    procedure lvServices_DoubleClick(sender: System.Object; e: System.EventArgs);
    procedure miClose_Click(sender: System.Object; e: System.EventArgs);
    procedure miAbout_Click(sender: System.Object; e: System.EventArgs);
    procedure cmMain_Popup(sender: System.Object; e: System.EventArgs);
    procedure miDetails_Click(sender: System.Object; e: System.EventArgs);
    procedure miStart_Click(sender: System.Object; e: System.EventArgs);
    procedure miStop_Click(sender: System.Object; e: System.EventArgs);
    procedure miPause_Click(sender: System.Object; e: System.EventArgs);
    procedure miResume_Click(sender: System.Object; e: System.EventArgs);
    procedure miConnect_Click(sender: System.Object; e: System.EventArgs);
    procedure lvServices_KeyPress(sender: System.Object; e: System.Windows.Forms.KeyPressEventArgs);
  {$ENDREGION}
  strict protected
    /// <summary>
    /// Clean up any resources being used.
    /// </summary>
    procedure Dispose(Disposing: Boolean); override;
  private
    type
      /// <summary>
      /// Class to be added to the ListView. It simplifies the creation of the
      /// SubItems to be displayed, and contains a reference to the
      /// Service that the ListViewItem refers to.
      /// Thanks to Petr Vones for this technique :-)
      /// </summary>
      TServiceListItem = class(ListViewItem)
      private
        FServiceController: ServiceController;
      public
        property Service: ServiceController read FServiceController;
        procedure Refresh;
        constructor Create(pServiceController: ServiceController);
      end;

      /// <summary>
      /// Class used by the ListView to perform column sorts.
      /// Thanks again, Petr.
      /// </summary>
      TListViewItemSorter = class(TObject, IComparer)
      private
        FColumn: Integer;
        FDescending: Boolean;
        procedure SetColumn(Value: Integer);
      public
        function Compare(x: TObject; y: TObject): Integer;
        property Column: Integer read FColumn write SetColumn;
      end;
    procedure UpdateFormCaption;

    private
      FComputerName: string;
      procedure RefreshServiceList;
      procedure EnableContextMenu(pServiceListItem: TServiceListItem);
      function GetSelectedServiceListItem: TServiceListItem;
      property SelectedServiceListItem: TServiceListItem read GetSelectedServiceListItem;
    procedure ShowServiceDetails;
    procedure ChangeServiceStatus(const pChangeType: TChangeType);
  public
    constructor Create;
  end;

implementation

uses
  DetailsForm, AboutForm, System.Text, SysUtils, ConnectForm;

{$REGION 'Windows Form Designer generated code'}
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
procedure TMainForm.InitializeComponent;
type
  TSystem_Windows_Forms_ColumnHeaderArray = array of System.Windows.Forms.ColumnHeader;
  TSystem_Windows_Forms_MenuItemArray = array of System.Windows.Forms.MenuItem;
begin
  Self.Panel1 := System.Windows.Forms.Panel.Create;
  Self.Label1 := System.Windows.Forms.Label.Create;
  Self.btnRefresh := System.Windows.Forms.Button.Create;
  Self.btnClose := System.Windows.Forms.Button.Create;
  Self.lvServices := System.Windows.Forms.ListView.Create;
  Self.chName := System.Windows.Forms.ColumnHeader.Create;
  Self.chDesc := System.Windows.Forms.ColumnHeader.Create;
  Self.chStatus := System.Windows.Forms.ColumnHeader.Create;
  Self.cmMain := System.Windows.Forms.ContextMenu.Create;
  Self.miDetails := System.Windows.Forms.MenuItem.Create;
  Self.MenuItem4 := System.Windows.Forms.MenuItem.Create;
  Self.miStart := System.Windows.Forms.MenuItem.Create;
  Self.miStop := System.Windows.Forms.MenuItem.Create;
  Self.miPause := System.Windows.Forms.MenuItem.Create;
  Self.miResume := System.Windows.Forms.MenuItem.Create;
  Self.mmMain := System.Windows.Forms.MainMenu.Create;
  Self.MenuItem1 := System.Windows.Forms.MenuItem.Create;
  Self.miConnect := System.Windows.Forms.MenuItem.Create;
  Self.MenuItem6 := System.Windows.Forms.MenuItem.Create;
  Self.miClose := System.Windows.Forms.MenuItem.Create;
  Self.miAction := System.Windows.Forms.MenuItem.Create;
  Self.miDetails2 := System.Windows.Forms.MenuItem.Create;
  Self.MenuItem5 := System.Windows.Forms.MenuItem.Create;
  Self.miStart2 := System.Windows.Forms.MenuItem.Create;
  Self.miStop2 := System.Windows.Forms.MenuItem.Create;
  Self.miPause2 := System.Windows.Forms.MenuItem.Create;
  Self.miResume2 := System.Windows.Forms.MenuItem.Create;
  Self.MenuItem3 := System.Windows.Forms.MenuItem.Create;
  Self.miAbout := System.Windows.Forms.MenuItem.Create;
  Self.Panel1.SuspendLayout;
  Self.SuspendLayout;
  // 
  // Panel1
  // 
  Self.Panel1.Controls.Add(Self.Label1);
  Self.Panel1.Controls.Add(Self.btnRefresh);
  Self.Panel1.Controls.Add(Self.btnClose);
  Self.Panel1.Dock := System.Windows.Forms.DockStyle.Bottom;
  Self.Panel1.Location := System.Drawing.Point.Create(0, 214);
  Self.Panel1.Name := 'Panel1';
  Self.Panel1.Size := System.Drawing.Size.Create(600, 56);
  Self.Panel1.TabIndex := 0;
  // 
  // Label1
  // 
  Self.Label1.AutoSize := True;
  Self.Label1.Font := System.Drawing.Font.Create('Microsoft Sans Serif', 8.25, System.Drawing.FontStyle.Italic, System.Drawing.GraphicsUnit.Point, (Byte(0)));
  Self.Label1.Location := System.Drawing.Point.Create(8, 5);
  Self.Label1.Name := 'Label1';
  Self.Label1.Size := System.Drawing.Size.Create(128, 16);
  Self.Label1.TabIndex := 2;
  Self.Label1.Text := '(Double-Click for details)';
  // 
  // btnRefresh
  // 
  Self.btnRefresh.Location := System.Drawing.Point.Create(16, 25);
  Self.btnRefresh.Name := 'btnRefresh';
  Self.btnRefresh.TabIndex := 1;
  Self.btnRefresh.Text := 'Refresh';
  Include(Self.btnRefresh.Click, Self.btnRefresh_Click);
  // 
  // btnClose
  // 
  Self.btnClose.Anchor := (System.Windows.Forms.AnchorStyles((System.Windows.Forms.AnchorStyles.Bottom 
    or System.Windows.Forms.AnchorStyles.Right)));
  Self.btnClose.Location := System.Drawing.Point.Create(1212, -58);
  Self.btnClose.Name := 'btnClose';
  Self.btnClose.Size := System.Drawing.Size.Create(75, 29);
  Self.btnClose.TabIndex := 0;
  Self.btnClose.Text := 'Close';
  Include(Self.btnClose.Click, Self.btnClose_Click);
  // 
  // lvServices
  // 
  Self.lvServices.Columns.AddRange(TSystem_Windows_Forms_ColumnHeaderArray.Create(Self.chName, Self.chDesc, Self.chStatus));
  Self.lvServices.ContextMenu := Self.cmMain;
  Self.lvServices.Dock := System.Windows.Forms.DockStyle.Fill;
  Self.lvServices.FullRowSelect := True;
  Self.lvServices.GridLines := True;
  Self.lvServices.Location := System.Drawing.Point.Create(0, 0);
  Self.lvServices.Name := 'lvServices';
  Self.lvServices.Size := System.Drawing.Size.Create(600, 214);
  Self.lvServices.TabIndex := 1;
  Self.lvServices.View := System.Windows.Forms.View.Details;
  Include(Self.lvServices.KeyPress, Self.lvServices_KeyPress);
  Include(Self.lvServices.DoubleClick, Self.lvServices_DoubleClick);
  Include(Self.lvServices.ColumnClick, Self.lvServices_ColumnClick);
  // 
  // chName
  // 
  Self.chName.Text := 'Name';
  Self.chName.Width := 152;
  // 
  // chDesc
  // 
  Self.chDesc.Text := 'Description';
  Self.chDesc.Width := 319;
  // 
  // chStatus
  // 
  Self.chStatus.Text := 'Status';
  Self.chStatus.Width := 96;
  // 
  // cmMain
  // 
  Self.cmMain.MenuItems.AddRange(TSystem_Windows_Forms_MenuItemArray.Create(Self.miDetails, Self.MenuItem4, Self.miStart, Self.miStop, Self.miPause, Self.miResume));
  Include(Self.cmMain.Popup, Self.cmMain_Popup);
  // 
  // miDetails
  // 
  Self.miDetails.DefaultItem := True;
  Self.miDetails.Index := 0;
  Self.miDetails.Text := 'Show Details...';
  Include(Self.miDetails.Click, Self.miDetails_Click);
  // 
  // MenuItem4
  // 
  Self.MenuItem4.Index := 1;
  Self.MenuItem4.Text := '-';
  // 
  // miStart
  // 
  Self.miStart.Index := 2;
  Self.miStart.Text := 'Start';
  Include(Self.miStart.Click, Self.miStart_Click);
  // 
  // miStop
  // 
  Self.miStop.Index := 3;
  Self.miStop.Text := 'Stop';
  Include(Self.miStop.Click, Self.miStop_Click);
  // 
  // miPause
  // 
  Self.miPause.Index := 4;
  Self.miPause.Text := 'Pause';
  Include(Self.miPause.Click, Self.miPause_Click);
  // 
  // miResume
  // 
  Self.miResume.Index := 5;
  Self.miResume.Text := 'Resume';
  Include(Self.miResume.Click, Self.miResume_Click);
  // 
  // mmMain
  // 
  Self.mmMain.MenuItems.AddRange(TSystem_Windows_Forms_MenuItemArray.Create(Self.MenuItem1, Self.miAction, Self.MenuItem3));
  // 
  // MenuItem1
  // 
  Self.MenuItem1.Index := 0;
  Self.MenuItem1.MenuItems.AddRange(TSystem_Windows_Forms_MenuItemArray.Create(Self.miConnect, Self.MenuItem6, Self.miClose));
  Self.MenuItem1.Text := '&File';
  // 
  // miConnect
  // 
  Self.miConnect.Index := 0;
  Self.miConnect.Text := 'C&onnect...';
  Include(Self.miConnect.Click, Self.miConnect_Click);
  // 
  // MenuItem6
  // 
  Self.MenuItem6.Index := 1;
  Self.MenuItem6.Text := '-';
  // 
  // miClose
  // 
  Self.miClose.Index := 2;
  Self.miClose.Text := '&Close';
  Include(Self.miClose.Click, Self.miClose_Click);
  // 
  // miAction
  // 
  Self.miAction.Index := 1;
  Self.miAction.MenuItems.AddRange(TSystem_Windows_Forms_MenuItemArray.Create(Self.miDetails2, Self.MenuItem5, Self.miStart2, Self.miStop2, Self.miPause2, Self.miResume2));
  Self.miAction.Text := '&Action';
  Include(Self.miAction.Popup, Self.cmMain_Popup);
  // 
  // miDetails2
  // 
  Self.miDetails2.Index := 0;
  Self.miDetails2.Text := 'Show Details...';
  Include(Self.miDetails2.Click, Self.miDetails_Click);
  // 
  // MenuItem5
  // 
  Self.MenuItem5.Index := 1;
  Self.MenuItem5.Text := '-';
  // 
  // miStart2
  // 
  Self.miStart2.Index := 2;
  Self.miStart2.Text := 'Start';
  Include(Self.miStart2.Click, Self.miStart_Click);
  // 
  // miStop2
  // 
  Self.miStop2.Index := 3;
  Self.miStop2.Text := 'Stop';
  Include(Self.miStop2.Click, Self.miStop_Click);
  // 
  // miPause2
  // 
  Self.miPause2.Index := 4;
  Self.miPause2.Text := 'Pause';
  Include(Self.miPause2.Click, Self.miPause_Click);
  // 
  // miResume2
  // 
  Self.miResume2.Index := 5;
  Self.miResume2.Text := 'Resume';
  Include(Self.miResume2.Click, Self.miResume_Click);
  // 
  // MenuItem3
  // 
  Self.MenuItem3.Index := 2;
  Self.MenuItem3.MenuItems.AddRange(TSystem_Windows_Forms_MenuItemArray.Create(Self.miAbout));
  Self.MenuItem3.Text := '&Help';
  // 
  // miAbout
  // 
  Self.miAbout.Index := 0;
  Self.miAbout.Text := '&About...';
  Include(Self.miAbout.Click, Self.miAbout_Click);
  // 
  // TMainForm
  // 
  Self.AutoScaleBaseSize := System.Drawing.Size.Create(5, 13);
  Self.ClientSize := System.Drawing.Size.Create(600, 270);
  Self.Controls.Add(Self.lvServices);
  Self.Controls.Add(Self.Panel1);
  Self.Menu := Self.mmMain;
  Self.Name := 'TMainForm';
  Self.StartPosition := System.Windows.Forms.FormStartPosition.CenterScreen;
  Include(Self.Load, Self.TMainForm_Load);
  Self.Panel1.ResumeLayout(False);
  Self.ResumeLayout(False);
end;
{$ENDREGION}

/// <summary>
/// Create an instance of a TServiceListItem.
/// </summary>
/// <param name='pServiceController'>
/// The ServiceController instance to associate with the TServiceListItem.
/// </param>
constructor TMainForm.TServiceListItem.Create(pServiceController: ServiceController);
begin
  inherited Create;
  FServiceController := pServiceController;
  Text := FServiceController.ServiceName;
  SubItems.Add(FServiceController.DisplayName);
  SubItems.Add(TObject(FServiceController.Status).ToString);
end;

/// <summary>
/// Refreshes the information displayed for the TServiceListItem instance.
/// </summary>
procedure TMainForm.TServiceListItem.Refresh;
begin
  FServiceController.Refresh;
  Text := FServiceController.ServiceName;
  SubItems[1].Text := FServiceController.DisplayName;
  SubItems[2].Text := TObject(FServiceController.Status).ToString;
end;

/// <summary>
/// Function to compares two values when sorting a ListView.
/// </summary>
/// <param name='x'>The first item to compare.</param>
/// <param name='y'>The second item to compare.</param>
/// <returns>Returns -1 if x < y, 0 if x = y, 1 if x > y</returns>
function TMainForm.TListViewItemSorter.Compare(x, y: TObject): Integer;
begin
  //Compare the text value for the relevant subitems, dependant on the column clicked
  Result := System.String.Compare(ListViewItem(x).SubItems[FColumn].Text, ListViewItem(y).SubItems[FColumn].Text);
  if FDescending then
    //If doing a descending sort, reverse the result
    Result := -Result;
end;

/// <summary>
/// Sets the index of the column to perform the sort on.
/// </summary>
/// <param name='Value'>The index of the column to sort on.</param>
procedure TMainForm.TListViewItemSorter.SetColumn(Value: integer);
begin
  //If the column is the same then simply change the sort direction
  if FColumn = Value then
    FDescending := not FDescending
  else
  begin
    //A new column so ensure we do an ascending sort
    FDescending := False;
    FColumn := Value;
  end;
end;

procedure TMainForm.Dispose(Disposing: Boolean);
begin
  if Disposing then
  begin
    if Components <> nil then
      Components.Dispose();
  end;
  inherited Dispose(Disposing);
end;

constructor TMainForm.Create;
begin
  inherited Create;
  //
  // Required for Windows Form Designer support
  //
  InitializeComponent;
end;

procedure TMainForm.lvServices_KeyPress(sender: System.Object; e: System.Windows.Forms.KeyPressEventArgs);
const
  ENTER_KEY = #13;
begin
  if e.KeyChar = ENTER_KEY then
    ShowServiceDetails;
end;

procedure TMainForm.miConnect_Click(sender: System.Object; e: System.EventArgs);
begin
  if TConnectForm.ShowConnectForm(FComputerName) = System.Windows.Forms.DialogResult.OK then
    RefreshServiceList;
end;

procedure TMainForm.miResume_Click(sender: System.Object; e: System.EventArgs);
begin
  ChangeServiceStatus(ctResume);
end;

procedure TMainForm.miPause_Click(sender: System.Object; e: System.EventArgs);
begin
  ChangeServiceStatus(ctPause);
end;

procedure TMainForm.miStop_Click(sender: System.Object; e: System.EventArgs);
begin
  ChangeServiceStatus(ctStop);
end;

procedure TMainForm.miStart_Click(sender: System.Object; e: System.EventArgs);
begin
  ChangeServiceStatus(ctStart);
end;

procedure TMainForm.miDetails_Click(sender: System.Object; e: System.EventArgs);
begin
  ShowServiceDetails;
end;

procedure TMainForm.cmMain_Popup(sender: System.Object; e: System.EventArgs);
begin
  EnableContextMenu(SelectedServiceListItem);
end;

/// <summary>
/// Returns the currently selected item in the lvServicesListView.
/// </summary>
/// <returns>
/// Returns a TServiceListItem reference the currently selected item.
/// </returns>
function TMainForm.GetSelectedServiceListItem: TServiceListItem;
begin
  if lvServices.SelectedItems.Count <> 0 then
    Result := lvServices.SelectedItems[0] as TServiceListItem
  else
    Result := nil;
end;

/// <summary>
/// Enable or disable the cmMain MenuItems based on the status of the specified
/// ServiceController.
/// </summary>
/// <param name="pService">
/// The ServiceController which should be examined to determine the state of
/// the MenuItems.
/// </param>
procedure TMainForm.EnableContextMenu(pServiceListItem: TServiceListItem);
begin
  if Assigned(pServiceListItem) then
  begin
    if Assigned(pServiceListItem.Service) then
    begin
      miDetails.Enabled := true;
      miStart.Enabled := pServiceListItem.Service.Status = ServiceControllerStatus.Stopped;
      miStop.Enabled := not miStart.Enabled;
      miResume.Enabled := pServiceListItem.Service.Status = ServiceControllerStatus.Paused;
      miPause.Enabled := miStop.Enabled and not (miResume.Enabled);
    end
  end
  else
  begin
    miDetails.Enabled := false;
    miStart.Enabled := false;
    miStop.Enabled := false;
    miPause.Enabled := false;
    miResume.Enabled := false;
  end;
  //Do the same for the MainMenu items
  miDetails2.Enabled := miDetails.Enabled;
  miStart2.Enabled := miStart.Enabled;
  miStop2.Enabled := miStop.Enabled;
  miPause2.Enabled := miPause.Enabled;
  miResume2.Enabled := miResume.Enabled;
end;

procedure TMainForm.miAbout_Click(sender: System.Object; e: System.EventArgs);
begin
  TAboutForm.ShowAboutForm;
end;

procedure TMainForm.miClose_Click(sender: System.Object; e: System.EventArgs);
begin
  Close;
end;

procedure TMainForm.lvServices_DoubleClick(sender: System.Object; e: System.EventArgs);
begin
  ShowServiceDetails;
end;

/// <summary>
/// Show details of the currently selected service.
/// </summary>
procedure TMainForm.ShowServiceDetails;
var
  lServiceListItem: TServiceListItem;
  lCursor: System.Windows.Forms.Cursor;
begin
  lCursor := Cursor;
  Cursor := Cursors.WaitCursor;
  try
    lServiceListItem := SelectedServiceListItem;
    if Assigned(lServiceListItem) then
      if TDetailsForm.ShowDetails(lServiceListItem.Service) <> System.Windows.Forms.DialogResult.Cancel then
        lServiceListItem.Refresh;
  finally
    Cursor := lCursor;
  end;
end;

/// <summary>
/// Change the status of the currently selectd service based on pChangeType.
/// </summary>
/// <param name='pChangeType'>The type of status change to perform.</param>
procedure TMainForm.ChangeServiceStatus(const pChangeType: TChangeType);
var
  lErrorString: StringBuilder;
  lServiceListItem: TServiceListItem;
begin
  lServiceListItem := SelectedServiceListItem;
  if Assigned(lServiceListItem) then
  begin
    try
      if TChangeStatusForm.ShowChangeStatusForm(lServiceListItem.Service, pChangeType) = System.Windows.Forms.DialogResult.Cancel then
        MessageBox.Show(Format('Operation timeout when %s the service',
          [TChangeStatusForm.GetChangeTypeString(pChangeType).ToLower]));
    except
      on E: Exception do
      begin
        lErrorString := StringBuilder.Create(Format('There was an error %s the service: %s. ',
          [TChangeStatusForm.GetChangeTypeString(pChangeType).ToLower, E.Message]));
        if Assigned(E.InnerException) then
          lErrorString.Append(E.InnerException.Message);
        MessageBox.Show(lErrorString.ToString);
      end;
    end;
    lServiceListItem.Refresh;
  end;
end;


procedure TMainForm.TMainForm_Load(sender: System.Object; e: System.EventArgs);
begin
  //In order to sort a ListView you need to provide an instance of a class
  //which implements the IComparer interface
  lvServices.ListViewItemSorter := TListViewItemSorter.Create;
  FComputerName := '';
  RefreshServiceList;
end;

procedure TMainForm.lvServices_ColumnClick(sender: System.Object; e: System.Windows.Forms.ColumnClickEventArgs);
begin
  //Set the column so the sorter knows which items to compare. The sorter class
  //will also check the direction of the sort, dependant on whether or not the
  //same column has just been clicked.
  TMainForm.TListViewItemSorter(lvServices.ListViewItemSorter).Column := e.Column;

  lvServices.Sort;
end;

procedure TMainForm.btnRefresh_Click(sender: System.Object; e: System.EventArgs);
var
  lCursor: System.Windows.Forms.Cursor;
begin
  lCursor := Cursor;
  Cursor := Cursors.WaitCursor;
  try
    RefreshServiceList;
  finally
    Cursor := lCursor;
  end;
end;

procedure TMainForm.btnClose_Click(sender: System.Object; e: System.EventArgs);
begin
  Close;
end;

/// <summary>
/// Get details of all Services installed on the system, and populate the
/// ListView accordingly.
/// </summary>
procedure TMainForm.RefreshServiceList;
var
  lServices: Array of ServiceController;
  i: integer;
begin
  try
    if FComputerName = '' then
      lServices := ServiceController.GetServices
    else
      lServices := ServiceController.GetServices(FComputerName);
    lvServices.BeginUpdate;
    try
      lvServices.Items.Clear;
      for i := 0 to Length(lServices) -1 do
        lvServices.Items.Add(TServiceListItem.Create(lServices[i]));
      lvServices.Sort;
    finally
      lvServices.EndUpdate;
    end;
    UpdateFormCaption;
  except on E: Exception do
    MessageBox.Show(Format('An error occured retrieving the Services:%s%s',
      [Environment.NewLine, E.Message]));
  end;
end;

/// <summary>
/// Updates the Form's caption to indicate which computer we are currently
/// managing the services on.
/// </summary>
procedure TMainForm.UpdateFormCaption;
var
  lComputerName: string;
begin
  if FComputerName = '' then
    lComputerName := 'Local'
  else
    lComputerName := FComputerName;
  Self.Text := Format('Service Controller [%s]', [lComputerName]);
end;

end.
