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

  Form showing details for a specified service.
*******************************************************************************}

interface

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

type
  TDependancyType = (dtDependant, dtDependedOn);

  TParseDependancyProc = procedure(pService: TService; pDependancyType: TDependancyType; pNodes: TreeNodeCollection) of object;

  TDetailsForm = class(System.Windows.Forms.Form)
  {$REGION 'Designer Managed Code'}
  strict private
    /// <summary>
    /// Required designer variable.
    /// </summary>
    components: System.ComponentModel.IContainer;
    btnOK: System.Windows.Forms.Button;
    tcMain: System.Windows.Forms.TabControl;
    tpGeneral: System.Windows.Forms.TabPage;
    tpDependancies: System.Windows.Forms.TabPage;
    Label1: System.Windows.Forms.Label;
    Label2: System.Windows.Forms.Label;
    lblServiceName: System.Windows.Forms.Label;
    lblDisplayName: System.Windows.Forms.Label;
    tvDependsOn: System.Windows.Forms.TreeView;
    Label3: System.Windows.Forms.Label;
    Label4: System.Windows.Forms.Label;
    tvDependants: System.Windows.Forms.TreeView;
    ilSmall: System.Windows.Forms.ImageList;
    cmShow: System.Windows.Forms.ContextMenu;
    miShow: System.Windows.Forms.MenuItem;
    Label5: System.Windows.Forms.Label;
    lblDescription: System.Windows.Forms.Label;
    lblExePath: System.Windows.Forms.Label;
    Label7: System.Windows.Forms.Label;
    lblStatus: System.Windows.Forms.Label;
    Label8: System.Windows.Forms.Label;
    Label6: System.Windows.Forms.Label;
    cmbStartupType: System.Windows.Forms.ComboBox;
    lblBevel: System.Windows.Forms.Label;
    btnStart: System.Windows.Forms.Button;
    btnStop: System.Windows.Forms.Button;
    btnPause: System.Windows.Forms.Button;
    btnResume: System.Windows.Forms.Button;
    Label9: System.Windows.Forms.Label;
    Label10: System.Windows.Forms.Label;
    tbStartParams: System.Windows.Forms.TextBox;
    btnCancel: System.Windows.Forms.Button;
    btnApply: System.Windows.Forms.Button;
    btnProperties: System.Windows.Forms.Button;
    /// <summary>
    /// Required method for Designer support - do not modify
    /// the contents of this method with the code editor.
    /// </summary>
    procedure InitializeComponent;
    procedure TDetailsForm_Load(sender: System.Object; e: System.EventArgs);
    procedure miShow_Click(sender: System.Object; e: System.EventArgs);
    procedure cmShow_Popup(sender: System.Object; e: System.EventArgs);
    procedure btnStart_Click(sender: System.Object; e: System.EventArgs);
    procedure btnStop_Click(sender: System.Object; e: System.EventArgs);
    procedure btnPause_Click(sender: System.Object; e: System.EventArgs);
    procedure btnResume_Click(sender: System.Object; e: System.EventArgs);
    procedure cmbStartupType_SelectedIndexChanged(sender: System.Object; e: System.EventArgs);
    procedure btnApply_Click(sender: System.Object; e: System.EventArgs);
    procedure btnCancel_Click(sender: System.Object; e: System.EventArgs);
    procedure btnOK_Click(sender: System.Object; e: System.EventArgs);
    procedure btnProperties_Click(sender: System.Object; e: System.EventArgs);
  {$ENDREGION}
  strict protected
    /// <summary>
    /// Clean up any resources being used.
    /// </summary>
    procedure Dispose(Disposing: Boolean); override;
  private
    FService: TService;
    FStatusChanged: boolean;
    type
      /// <summmary>
      /// Class to use when adding a node to a TreeView.
      /// </summary>
      TDependancyNode = class(TreeNode)
      private
        FService: TService;
        FDependancyType: TDependancyType;
        FParseProc: TParseDependancyProc;
        procedure PopulateDetails;
      public
        property Service: TService read FService;
        constructor Create(pService: TService; pDependancyType: TDependancyType;
          pParseProc: TParseDependancyProc);
      end;

    procedure PopulateControls;
    procedure PopulateDependancies;
    procedure PopulateDependedOn;
    procedure PopulateDependants;
    procedure ParseDependancy(pService: TService; pDependancyType: TDependancyType; pNodes: TreeNodeCollection);
    procedure EnableButtons;
    procedure ChangeServiceStatus(const pChangeType: TChangeType; pStartParams: string = '');
    function ConvertFromDisplayStartMode(const pStartMode: string): string;
    function ConvertToDisplayStartMode(const pStartMode: string): string;
    procedure ApplyChanges;
  public
    constructor Create;
    class function ShowDetails(pService: ServiceController): System.Windows.Forms.DialogResult;
  end;

implementation

uses
  SysUtils, System.Text;

{$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 TDetailsForm.InitializeComponent;
type
  TSystem_ObjectArray = array of System.Object;
  TSystem_Windows_Forms_MenuItemArray = array of System.Windows.Forms.MenuItem;
var
  resources: System.Resources.ResourceManager;
begin
  Self.components := System.ComponentModel.Container.Create;
  resources := System.Resources.ResourceManager.Create(TypeOf(TDetailsForm));
  Self.btnOK := System.Windows.Forms.Button.Create;
  Self.tcMain := System.Windows.Forms.TabControl.Create;
  Self.tpGeneral := System.Windows.Forms.TabPage.Create;
  Self.btnProperties := System.Windows.Forms.Button.Create;
  Self.tbStartParams := System.Windows.Forms.TextBox.Create;
  Self.Label10 := System.Windows.Forms.Label.Create;
  Self.Label9 := System.Windows.Forms.Label.Create;
  Self.btnResume := System.Windows.Forms.Button.Create;
  Self.btnPause := System.Windows.Forms.Button.Create;
  Self.btnStop := System.Windows.Forms.Button.Create;
  Self.btnStart := System.Windows.Forms.Button.Create;
  Self.lblBevel := System.Windows.Forms.Label.Create;
  Self.cmbStartupType := System.Windows.Forms.ComboBox.Create;
  Self.Label6 := System.Windows.Forms.Label.Create;
  Self.lblStatus := System.Windows.Forms.Label.Create;
  Self.Label8 := System.Windows.Forms.Label.Create;
  Self.lblExePath := System.Windows.Forms.Label.Create;
  Self.Label7 := System.Windows.Forms.Label.Create;
  Self.lblDescription := System.Windows.Forms.Label.Create;
  Self.Label5 := System.Windows.Forms.Label.Create;
  Self.lblDisplayName := System.Windows.Forms.Label.Create;
  Self.lblServiceName := System.Windows.Forms.Label.Create;
  Self.Label2 := System.Windows.Forms.Label.Create;
  Self.Label1 := System.Windows.Forms.Label.Create;
  Self.tpDependancies := System.Windows.Forms.TabPage.Create;
  Self.Label4 := System.Windows.Forms.Label.Create;
  Self.tvDependants := System.Windows.Forms.TreeView.Create;
  Self.cmShow := System.Windows.Forms.ContextMenu.Create;
  Self.miShow := System.Windows.Forms.MenuItem.Create;
  Self.ilSmall := System.Windows.Forms.ImageList.Create(Self.components);
  Self.Label3 := System.Windows.Forms.Label.Create;
  Self.tvDependsOn := System.Windows.Forms.TreeView.Create;
  Self.btnCancel := System.Windows.Forms.Button.Create;
  Self.btnApply := System.Windows.Forms.Button.Create;
  Self.tcMain.SuspendLayout;
  Self.tpGeneral.SuspendLayout;
  Self.tpDependancies.SuspendLayout;
  Self.SuspendLayout;
  // 
  // btnOK
  // 
  Self.btnOK.DialogResult := System.Windows.Forms.DialogResult.OK;
  Self.btnOK.Location := System.Drawing.Point.Create(152, 400);
  Self.btnOK.Name := 'btnOK';
  Self.btnOK.TabIndex := 0;
  Self.btnOK.Text := 'OK';
  Include(Self.btnOK.Click, Self.btnOK_Click);
  // 
  // tcMain
  // 
  Self.tcMain.Controls.Add(Self.tpGeneral);
  Self.tcMain.Controls.Add(Self.tpDependancies);
  Self.tcMain.Location := System.Drawing.Point.Create(8, 8);
  Self.tcMain.Name := 'tcMain';
  Self.tcMain.SelectedIndex := 0;
  Self.tcMain.Size := System.Drawing.Size.Create(392, 384);
  Self.tcMain.TabIndex := 1;
  // 
  // tpGeneral
  // 
  Self.tpGeneral.Controls.Add(Self.btnProperties);
  Self.tpGeneral.Controls.Add(Self.tbStartParams);
  Self.tpGeneral.Controls.Add(Self.Label10);
  Self.tpGeneral.Controls.Add(Self.Label9);
  Self.tpGeneral.Controls.Add(Self.btnResume);
  Self.tpGeneral.Controls.Add(Self.btnPause);
  Self.tpGeneral.Controls.Add(Self.btnStop);
  Self.tpGeneral.Controls.Add(Self.btnStart);
  Self.tpGeneral.Controls.Add(Self.lblBevel);
  Self.tpGeneral.Controls.Add(Self.cmbStartupType);
  Self.tpGeneral.Controls.Add(Self.Label6);
  Self.tpGeneral.Controls.Add(Self.lblStatus);
  Self.tpGeneral.Controls.Add(Self.Label8);
  Self.tpGeneral.Controls.Add(Self.lblExePath);
  Self.tpGeneral.Controls.Add(Self.Label7);
  Self.tpGeneral.Controls.Add(Self.lblDescription);
  Self.tpGeneral.Controls.Add(Self.Label5);
  Self.tpGeneral.Controls.Add(Self.lblDisplayName);
  Self.tpGeneral.Controls.Add(Self.lblServiceName);
  Self.tpGeneral.Controls.Add(Self.Label2);
  Self.tpGeneral.Controls.Add(Self.Label1);
  Self.tpGeneral.Location := System.Drawing.Point.Create(4, 22);
  Self.tpGeneral.Name := 'tpGeneral';
  Self.tpGeneral.Size := System.Drawing.Size.Create(384, 358);
  Self.tpGeneral.TabIndex := 0;
  Self.tpGeneral.Text := 'General';
  // 
  // btnProperties
  // 
  Self.btnProperties.Location := System.Drawing.Point.Create(16, 96);
  Self.btnProperties.Name := 'btnProperties';
  Self.btnProperties.TabIndex := 20;
  Self.btnProperties.Text := 'Properties...';
  Include(Self.btnProperties.Click, Self.btnProperties_Click);
  // 
  // tbStartParams
  // 
  Self.tbStartParams.Location := System.Drawing.Point.Create(104, 318);
  Self.tbStartParams.Name := 'tbStartParams';
  Self.tbStartParams.Size := System.Drawing.Size.Create(264, 20);
  Self.tbStartParams.TabIndex := 19;
  Self.tbStartParams.Text := '';
  // 
  // Label10
  // 
  Self.Label10.AutoSize := True;
  Self.Label10.Location := System.Drawing.Point.Create(16, 320);
  Self.Label10.Name := 'Label10';
  Self.Label10.Size := System.Drawing.Size.Create(93, 16);
  Self.Label10.TabIndex := 18;
  Self.Label10.Text := 'Start Parameters:';
  // 
  // Label9
  // 
  Self.Label9.Location := System.Drawing.Point.Create(16, 281);
  Self.Label9.Name := 'Label9';
  Self.Label9.Size := System.Drawing.Size.Create(374, 29);
  Self.Label9.TabIndex := 17;
  Self.Label9.Text := 'You can specify the start parameters that apply when you start the service from'+ 
  ' here.';
  // 
  // btnResume
  // 
  Self.btnResume.Location := System.Drawing.Point.Create(288, 247);
  Self.btnResume.Name := 'btnResume';
  Self.btnResume.TabIndex := 16;
  Self.btnResume.Text := '&Resume';
  Include(Self.btnResume.Click, Self.btnResume_Click);
  // 
  // btnPause
  // 
  Self.btnPause.Location := System.Drawing.Point.Create(200, 247);
  Self.btnPause.Name := 'btnPause';
  Self.btnPause.TabIndex := 15;
  Self.btnPause.Text := '&Pause';
  Include(Self.btnPause.Click, Self.btnPause_Click);
  // 
  // btnStop
  // 
  Self.btnStop.Location := System.Drawing.Point.Create(104, 247);
  Self.btnStop.Name := 'btnStop';
  Self.btnStop.TabIndex := 14;
  Self.btnStop.Text := 'S&top';
  Include(Self.btnStop.Click, Self.btnStop_Click);
  // 
  // btnStart
  // 
  Self.btnStart.Location := System.Drawing.Point.Create(16, 247);
  Self.btnStart.Name := 'btnStart';
  Self.btnStart.TabIndex := 13;
  Self.btnStart.Text := '&Start';
  Include(Self.btnStart.Click, Self.btnStart_Click);
  // 
  // lblBevel
  // 
  Self.lblBevel.BorderStyle := System.Windows.Forms.BorderStyle.Fixed3D;
  Self.lblBevel.Location := System.Drawing.Point.Create(16, 210);
  Self.lblBevel.Name := 'lblBevel';
  Self.lblBevel.Size := System.Drawing.Size.Create(352, 2);
  Self.lblBevel.TabIndex := 12;
  // 
  // cmbStartupType
  // 
  Self.cmbStartupType.Items.AddRange(TSystem_ObjectArray.Create('Automatic', 'Manual', 'Disabled'));
  Self.cmbStartupType.Location := System.Drawing.Point.Create(104, 174);
  Self.cmbStartupType.Name := 'cmbStartupType';
  Self.cmbStartupType.Size := System.Drawing.Size.Create(264, 21);
  Self.cmbStartupType.TabIndex := 11;
  Include(Self.cmbStartupType.SelectedIndexChanged, Self.cmbStartupType_SelectedIndexChanged);
  // 
  // Label6
  // 
  Self.Label6.Location := System.Drawing.Point.Create(16, 176);
  Self.Label6.Name := 'Label6';
  Self.Label6.Size := System.Drawing.Size.Create(80, 16);
  Self.Label6.TabIndex := 10;
  Self.Label6.Text := 'Startup Type:';
  // 
  // lblStatus
  // 
  Self.lblStatus.Location := System.Drawing.Point.Create(104, 224);
  Self.lblStatus.Name := 'lblStatus';
  Self.lblStatus.Size := System.Drawing.Size.Create(144, 14);
  Self.lblStatus.TabIndex := 9;
  Self.lblStatus.Text := 'lblStatus';
  // 
  // Label8
  // 
  Self.Label8.AutoSize := True;
  Self.Label8.Location := System.Drawing.Point.Create(16, 224);
  Self.Label8.Name := 'Label8';
  Self.Label8.Size := System.Drawing.Size.Create(80, 16);
  Self.Label8.TabIndex := 8;
  Self.Label8.Text := 'Service Status:';
  // 
  // lblExePath
  // 
  Self.lblExePath.BorderStyle := System.Windows.Forms.BorderStyle.Fixed3D;
  Self.lblExePath.Location := System.Drawing.Point.Create(16, 148);
  Self.lblExePath.Name := 'lblExePath';
  Self.lblExePath.Size := System.Drawing.Size.Create(352, 16);
  Self.lblExePath.TabIndex := 7;
  Self.lblExePath.Text := 'lblExePath';
  // 
  // Label7
  // 
  Self.Label7.AutoSize := True;
  Self.Label7.Location := System.Drawing.Point.Create(16, 132);
  Self.Label7.Name := 'Label7';
  Self.Label7.Size := System.Drawing.Size.Create(102, 16);
  Self.Label7.TabIndex := 6;
  Self.Label7.Text := 'Path to Executable:';
  // 
  // lblDescription
  // 
  Self.lblDescription.BorderStyle := System.Windows.Forms.BorderStyle.Fixed3D;
  Self.lblDescription.Location := System.Drawing.Point.Create(104, 56);
  Self.lblDescription.Name := 'lblDescription';
  Self.lblDescription.Size := System.Drawing.Size.Create(264, 64);
  Self.lblDescription.TabIndex := 5;
  Self.lblDescription.Text := 'lblDescription';
  // 
  // Label5
  // 
  Self.Label5.AutoSize := True;
  Self.Label5.Location := System.Drawing.Point.Create(16, 56);
  Self.Label5.Name := 'Label5';
  Self.Label5.Size := System.Drawing.Size.Create(61, 16);
  Self.Label5.TabIndex := 4;
  Self.Label5.Text := 'Description';
  // 
  // lblDisplayName
  // 
  Self.lblDisplayName.BorderStyle := System.Windows.Forms.BorderStyle.Fixed3D;
  Self.lblDisplayName.Location := System.Drawing.Point.Create(104, 32);
  Self.lblDisplayName.Name := 'lblDisplayName';
  Self.lblDisplayName.Size := System.Drawing.Size.Create(264, 16);
  Self.lblDisplayName.TabIndex := 3;
  Self.lblDisplayName.Text := 'lblDisplayName';
  // 
  // lblServiceName
  // 
  Self.lblServiceName.AutoSize := True;
  Self.lblServiceName.Location := System.Drawing.Point.Create(104, 8);
  Self.lblServiceName.Name := 'lblServiceName';
  Self.lblServiceName.Size := System.Drawing.Size.Create(84, 16);
  Self.lblServiceName.TabIndex := 2;
  Self.lblServiceName.Text := 'lblServiceName';
  // 
  // Label2
  // 
  Self.Label2.AutoSize := True;
  Self.Label2.Location := System.Drawing.Point.Create(16, 32);
  Self.Label2.Name := 'Label2';
  Self.Label2.Size := System.Drawing.Size.Create(78, 16);
  Self.Label2.TabIndex := 1;
  Self.Label2.Text := 'Display Name:';
  // 
  // Label1
  // 
  Self.Label1.AutoSize := True;
  Self.Label1.Location := System.Drawing.Point.Create(16, 8);
  Self.Label1.Name := 'Label1';
  Self.Label1.Size := System.Drawing.Size.Create(78, 16);
  Self.Label1.TabIndex := 0;
  Self.Label1.Text := 'Service Name:';
  // 
  // tpDependancies
  // 
  Self.tpDependancies.Controls.Add(Self.Label4);
  Self.tpDependancies.Controls.Add(Self.tvDependants);
  Self.tpDependancies.Controls.Add(Self.Label3);
  Self.tpDependancies.Controls.Add(Self.tvDependsOn);
  Self.tpDependancies.Location := System.Drawing.Point.Create(4, 22);
  Self.tpDependancies.Name := 'tpDependancies';
  Self.tpDependancies.Size := System.Drawing.Size.Create(384, 358);
  Self.tpDependancies.TabIndex := 1;
  Self.tpDependancies.Text := 'Dependancies';
  // 
  // Label4
  // 
  Self.Label4.AutoSize := True;
  Self.Label4.Location := System.Drawing.Point.Create(8, 184);
  Self.Label4.Name := 'Label4';
  Self.Label4.Size := System.Drawing.Size.Create(291, 16);
  Self.Label4.TabIndex := 3;
  Self.Label4.Text := 'The following system components depend on this service';
  // 
  // tvDependants
  // 
  Self.tvDependants.ContextMenu := Self.cmShow;
  Self.tvDependants.ImageList := Self.ilSmall;
  Self.tvDependants.Location := System.Drawing.Point.Create(8, 200);
  Self.tvDependants.Name := 'tvDependants';
  Self.tvDependants.Size := System.Drawing.Size.Create(368, 144);
  Self.tvDependants.TabIndex := 2;
  // 
  // cmShow
  // 
  Self.cmShow.MenuItems.AddRange(TSystem_Windows_Forms_MenuItemArray.Create(Self.miShow));
  Include(Self.cmShow.Popup, Self.cmShow_Popup);
  // 
  // miShow
  // 
  Self.miShow.Index := 0;
  Self.miShow.Text := 'Show Details...';
  Include(Self.miShow.Click, Self.miShow_Click);
  // 
  // ilSmall
  // 
  Self.ilSmall.ImageSize := System.Drawing.Size.Create(16, 16);
  Self.ilSmall.ImageStream := (System.Windows.Forms.ImageListStreamer(resources.GetObject('ilSmall.ImageStream')));
  Self.ilSmall.TransparentColor := System.Drawing.Color.Fuchsia;
  // 
  // Label3
  // 
  Self.Label3.AutoSize := True;
  Self.Label3.Location := System.Drawing.Point.Create(8, 8);
  Self.Label3.Name := 'Label3';
  Self.Label3.Size := System.Drawing.Size.Create(296, 16);
  Self.Label3.TabIndex := 1;
  Self.Label3.Text := 'This service depends on the following system components';
  // 
  // tvDependsOn
  // 
  Self.tvDependsOn.ContextMenu := Self.cmShow;
  Self.tvDependsOn.ImageList := Self.ilSmall;
  Self.tvDependsOn.Location := System.Drawing.Point.Create(8, 32);
  Self.tvDependsOn.Name := 'tvDependsOn';
  Self.tvDependsOn.Size := System.Drawing.Size.Create(368, 136);
  Self.tvDependsOn.TabIndex := 0;
  // 
  // btnCancel
  // 
  Self.btnCancel.Location := System.Drawing.Point.Create(238, 400);
  Self.btnCancel.Name := 'btnCancel';
  Self.btnCancel.TabIndex := 2;
  Self.btnCancel.Text := 'Cancel';
  Include(Self.btnCancel.Click, Self.btnCancel_Click);
  // 
  // btnApply
  // 
  Self.btnApply.Enabled := False;
  Self.btnApply.Location := System.Drawing.Point.Create(324, 400);
  Self.btnApply.Name := 'btnApply';
  Self.btnApply.TabIndex := 4;
  Self.btnApply.Text := 'Apply';
  Include(Self.btnApply.Click, Self.btnApply_Click);
  // 
  // TDetailsForm
  // 
  Self.AcceptButton := Self.btnOK;
  Self.AutoScaleBaseSize := System.Drawing.Size.Create(5, 13);
  Self.ClientSize := System.Drawing.Size.Create(408, 434);
  Self.Controls.Add(Self.btnApply);
  Self.Controls.Add(Self.btnCancel);
  Self.Controls.Add(Self.tcMain);
  Self.Controls.Add(Self.btnOK);
  Self.FormBorderStyle := System.Windows.Forms.FormBorderStyle.FixedSingle;
  Self.MaximizeBox := False;
  Self.MinimizeBox := False;
  Self.Name := 'TDetailsForm';
  Self.ShowInTaskbar := False;
  Self.StartPosition := System.Windows.Forms.FormStartPosition.CenterScreen;
  Self.Text := 'Service Details';
  Include(Self.Load, Self.TDetailsForm_Load);
  Self.tcMain.ResumeLayout(False);
  Self.tpGeneral.ResumeLayout(False);
  Self.tpDependancies.ResumeLayout(False);
  Self.ResumeLayout(False);
end;
{$ENDREGION}

{ TDependancyNode }
/// <summary>TDependancyNode Constructor.</summary>
/// <param name="pService">The service that the node represents.</param>
/// <param name="pDependancyType">The type of dependancy.</param>
/// <param name="pParseProc">Pointer to a method to parse the services dependancies.</param>
constructor TDetailsForm.TDependancyNode.Create(pService: TService;
  pDependancyType: TDependancyType; pParseProc: TParseDependancyProc);
begin
  inherited Create;
  FService := pService;
  FDependancyType := pDependancyType;
  FParseProc := pParseProc;
  PopulateDetails;
end;

/// <summary>
/// Populate all the controls on the form.
/// /<summary>
procedure TDetailsForm.TDependancyNode.PopulateDetails;
begin
  Self.Text := FService.DisplayName;
  FParseProc(FService, FDependancyType, Self.Nodes);
end;

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

constructor TDetailsForm.Create;
begin
  inherited Create;
  //
  // Required for Windows Form Designer support
  //
  InitializeComponent;
  //
  // TODO: Add any constructor code after InitializeComponent call
  //
end;

/// <summary>
/// Show the details for a specified service in a modal dialog.
/// </summary>
/// <param name="pService">The service to show the details for.</param>
/// <returns>The System.Windows.Forms.DialogResult for the form.</returns>
class function TDetailsForm.ShowDetails(
  pService: ServiceController): System.Windows.Forms.DialogResult;
var
  lDetailsForm: TDetailsForm;
begin
  lDetailsForm := TDetailsForm.Create;

  //If the reference isn't a TService then we will create one so we have access
  //to the extra information not exposed by ServiceController.
  if pService is TService then
    lDetailsForm.FService := pService as TService
  else
    lDetailsForm.FService := TService.Create(pService.ServiceName, pService.MachineName);
  Result := lDetailsForm.ShowDialog;
end;

procedure TDetailsForm.btnProperties_Click(sender: System.Object; e: System.EventArgs);
begin
  FService.ShowProperties;
end;

procedure TDetailsForm.btnOK_Click(sender: System.Object; e: System.EventArgs);
begin
  if btnApply.Enabled then
    ApplyChanges;
end;

procedure TDetailsForm.btnCancel_Click(sender: System.Object; e: System.EventArgs);
begin
  //Return DialogResult.No if changes have been made to the service which have
  //already been applied. Return DialogResult.Cancel if nothing has changed
  if FStatusChanged then
    DialogResult := System.Windows.Forms.DialogResult.No
  else
    DialogResult := System.Windows.Forms.DialogResult.Cancel;
end;

procedure TDetailsForm.btnApply_Click(sender: System.Object; e: System.EventArgs);
begin
  ApplyChanges;
end;

procedure TDetailsForm.cmbStartupType_SelectedIndexChanged(sender: System.Object;
    e: System.EventArgs);
var
  lStartMode: string;
begin
  lStartMode := ConvertFromDisplayStartMode(cmbStartupType.Text);
  if lStartMode <> FService.StartMode then
  begin
    FService.StartMode := lStartMode;
    btnApply.Enabled := True;
  end;
end;

/// <summary>
/// Apply any changes made to FService.
/// </summary>
procedure TDetailsForm.ApplyChanges;
begin
  try
    FService.Save;
    btnApply.Enabled := False;
    FStatusChanged := True;
  except on E: Exception do
    MessageBox.Show(Format('There was an error applying the changes for this service: %s',
      [E.Message]));
  end;
end;

procedure TDetailsForm.btnResume_Click(sender: System.Object; e: System.EventArgs);
begin
  ChangeServiceStatus(ctResume);
end;

procedure TDetailsForm.btnPause_Click(sender: System.Object; e: System.EventArgs);
begin
  ChangeServiceStatus(ctPause);
end;

procedure TDetailsForm.btnStop_Click(sender: System.Object; e: System.EventArgs);
begin
  ChangeServiceStatus(ctStop);
end;

procedure TDetailsForm.btnStart_Click(sender: System.Object; e: System.EventArgs);
begin
  ChangeServiceStatus(ctStart, tbStartParams.Text);
end;

/// <summary>
/// Change the status of FService based on pChangeType.
/// </summary>
/// <param name='pChangeType'>The type of status change to perform.</param>
procedure TDetailsForm.ChangeServiceStatus(const pChangeType: TChangeType; pStartParams: string = '');
var
  lErrorString: StringBuilder;
begin
  try
    if TChangeStatusForm.ShowChangeStatusForm(FService, pChangeType, pStartParams) = System.Windows.Forms.DialogResult.Cancel then
      MessageBox.Show(Format('Operation timeout when %s the service',
        [TChangeStatusForm.GetChangeTypeString(pChangeType).ToLower]))
    else
      FStatusChanged := true;
  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;
  FService.Refresh;
  EnableButtons;
end;

procedure TDetailsForm.cmShow_Popup(sender: System.Object; e: System.EventArgs);
var
  lTreeView: TreeView;
begin
  lTreeView := TreeView(System.Windows.Forms.ContextMenu(Sender).SourceControl);
  miShow.Enabled := Assigned(lTreeView.SelectedNode);
end;

procedure TDetailsForm.miShow_Click(sender: System.Object; e: System.EventArgs);
var
  lTreeView: TreeView;
  lNode: TDependancyNode;
  lCursor: System.Windows.Forms.Cursor;
begin
  lCursor := Self.Cursor;
  Self.Cursor := Cursors.WaitCursor;
  try
    lTreeView := TreeView(System.Windows.Forms.ContextMenu(MenuItem(Sender).Parent).SourceControl);
    lNode := TDependancyNode(lTreeView.SelectedNode);
    if Assigned(lNode) then
      TDetailsForm.ShowDetails(lNode.Service);
  finally
    Self.Cursor := lCursor;
  end;
end;

procedure TDetailsForm.TDetailsForm_Load(sender: System.Object; e: System.EventArgs);
begin
  FStatusChanged := False;
  PopulateControls;
end;

/// <summary>
/// Populate the form to show details for this service.
/// </summary>
procedure TDetailsForm.PopulateControls;
begin
  tcMain.TabIndex := 0;
  Text := Format('%s properties', [FService.DisplayName]);
  lblServiceName.Text := FService.ServiceName;
  lblDisplayName.Text := FService.DisplayName;
  lblDescription.Text := FService.Description;
  lblExePath.Text := FService.ExePath;
  cmbStartupType.SelectedItem := ConvertToDisplayStartMode(FService.StartMode);
  EnableButtons;
  PopulateDependancies;
end;

/// <summary>
/// Returns the TService.StartMode in a format for display.
/// </summary>
/// <param name="pStartMode">
/// The StartMode value to return a display value for.
/// </param>
function TDetailsForm.ConvertToDisplayStartMode(const pStartMode: string): string;
begin
  if pStartMode = 'Auto' then
    Result := 'Automatic'
  else
    Result := pStartMode;
end;

/// <summary>
/// Returns the TService.StartMode value converted from a displayed value.
/// </summary>
/// <param name="pStartMode">
/// The StartMode value formatted for display.
/// </param>
function TDetailsForm.ConvertFromDisplayStartMode(const pStartMode: string): string;
begin
  if pStartMode = 'Automatic' then
    Result := 'Auto'
  else
    Result := pStartMode;
end;

/// <summary>
/// Enable the buttons to control the service, based on its current status.
/// Also ensure lblStatus shows the current status.
/// </summary>
procedure TDetailsForm.EnableButtons;
begin
  lblStatus.Text := TObject(FService.Status).ToString;
  btnStart.Enabled := FService.Status = ServiceControllerStatus.Stopped;
  tbStartParams.Enabled := btnStart.Enabled;
  btnStop.Enabled := not btnStart.Enabled;
  btnResume.Enabled := FService.Status = ServiceControllerStatus.Paused;
  btnPause.Enabled := btnStop.Enabled and (not btnResume.Enabled);
end;

/// <summary>
/// Populate the service dependancy TreeViews.
/// </summary>
procedure TDetailsForm.PopulateDependancies;
begin
  PopulateDependedOn;
  PopulateDependants;
end;

/// <summary>
/// Populate the TreeView displaying the services that this service is dependant
/// on.
/// </summary>
procedure TDetailsForm.PopulateDependedOn;
begin
  ParseDependancy(FService, dtDependedOn, tvDependsOn.Nodes);
end;

/// <summary>
/// Populate the TreeView displaying the services that depend on this service.
/// </summary>
procedure TDetailsForm.PopulateDependants;
begin
  ParseDependancy(FService, dtDependant, tvDependants.Nodes);
end;

/// <summary>
/// Parse either the dependant services, or services depended on, for the
/// specified service.
/// </summary>
/// <param name="pService">The service to parse the dependancies for.</param>
/// <param name="pDependancyType">The type of dependancy to parse.</param>
/// <param name="pNodes">The TreeNodeCollection to append the dependancies to.</param>
procedure TDetailsForm.ParseDependancy(pService: TService;
  pDependancyType: TDependancyType; pNodes: TreeNodeCollection);
var
  lServices: Array of ServiceController;
  i: integer;
begin
  if pDependancyType = dtDependant then
    lServices := pService.DependentServices
  else
    lServices := pService.ServicesDependedOn;

  if Length(lServices) <> 0 then
    for i := Low(lServices) to High(lServices) do
      pNodes.Add(TDependancyNode.Create(TService.Create(lServices[i].ServiceName, lServices[i].MachineName),
        pDependancyType, ParseDependancy))
end;

end.
