Windows-Dienste steuern mit Delphi

In meiner Software UpdateRepair war es nötig bestimmte Windows-Dienste zu steuern. Das heißt Dienst starten, beenden und konfigurieren. Zu diesem Zweck habe ich mir eine Klasse geschrieben.

Download Klasse – Unit –   Download Demo AppDownload Demo Code

Die Anwendung ist denkbar einfach:

  // Via statische Methoden
  TCsWindowsService.Start('Dienst name');
  TCsWindowsService.Stop('Dienst name');
  TCsWindowsService.Restart('Dienst name');  

oder 

// Via Klassen-Instanz
var
  WinService : TCsWindowsService;
begin
  WinService := TCsWindowsService.Create('Dienst Name');
  try
    WinService.Start;
    WinService.Stop;
    WinService.Restart;
  finally
    FreeAndNil(WinService);
  end;
end;

Die Unit „CapSystems.Framework.Windows.Service.pas“:

  
unit CapSystems.Framework.Windows.Service;

interface

uses
  SysUtils, Classes, Windows, WinSvc;

type
  TCsWindowsService = class;

  { -------------------------------------------------------------------------- }
  { Enumeratoren ------------------------------------------------------------- }
  { -------------------------------------------------------------------------- }

  TCsWindowsServiceStartMode = (
    wssmDisabled,
    wssmAuto,
    wssmAutoDelay,
    wssmManual
  );

  { -------------------------------------------------------------------------- }
  { TCsWindowsService -------------------------------------------------------- }
  { -------------------------------------------------------------------------- }

  TCsWindowsService = class(TPersistent)
  const
    SERVICES_REGISTRY_ROOT = HKEY_LOCAL_MACHINE;
    SERVICES_REGISTRY_PATH = '\SYSTEM\CurrentControlSet\services\';
    SERVICES_REGISTRY_STARTMODE_AUTO = 2;
    SERVICES_REGISTRY_STARTMODE_AUTODELAY = 2;
    SERVICES_REGISTRY_STARTMODE_MANUEL = 3;
    SERVICES_REGISTRY_STARTMODE_DISABLED = 4;
  private
    FName: String;
    FDescription: String;
    FStartMode: TCsWindowsServiceStartMode;
    FImagePath: String;
    FDisplayName: String;
  protected
    class function StartModeToDWORD(AStartMode: TCsWindowsServiceStartMode): DWORD;
  public
    constructor Create(AServiceName: String;
      ALoadProperties: Boolean = False);

    class procedure OpenServices; virtual;

    class procedure LoadServiceProperties(AName: string; var ADisplayName: string;
      var ADescription: string; var AImagePath: string;
      var AStartMode: TCsWindowsServiceStartMode); overload;
    procedure LoadServiceProperties; overload;

    class function Start(AName: String): Boolean; overload;
    function Start: Boolean; overload;

    class function Stop(AName: String): Boolean; overload;
    function Stop: Boolean; overload;

    class function Restart(AName: String): Boolean; overload;
    function Restart: Boolean; overload;

    class function IsRunning(AName: String): Boolean; overload;
    function IsRunning: Boolean; overload;

    class function SetStartMode(AName: String;
      AStartMode: TCsWindowsServiceStartMode): Boolean; overload;
    function SetStartMode(
      AStartMode: TCsWindowsServiceStartMode): Boolean; overload;

    class function GetStartMode(AName: String): TCsWindowsServiceStartMode; overload;
  published
    property Name: String read FName;
    property StartMode: TCsWindowsServiceStartMode read FStartMode;
    property Description: String read FDescription;
    property ImagePath: String read FImagePath;
    property DisplayName: String read FDisplayName;
  end;

implementation

uses
  Registry,
  CapSystems.Framework.Windows.Process,
  CapSystems.Framework.FileSystem;

{ ---------------------------------------------------------------------------- }
{ TCsService------------------------------------------------------------------ }
{ ---------------------------------------------------------------------------- }

constructor TCsWindowsService.Create(AServiceName: string;
  ALoadProperties: Boolean = False);
begin
  inherited Create;
  FName := AServiceName;
  if ALoadProperties then
    Self.LoadServiceProperties;
end;

class procedure TCsWindowsService.OpenServices;
begin
  //TCsProcess.Start('msc.exe', 'open', 'services.msc');
  TCsProcess.Start(TCsPath.GetSystemRootDirectory + 'services.msc');
end;

class procedure TCsWindowsService.LoadServiceProperties(AName: string;
  var ADisplayName: string; var ADescription: string; var AImagePath: string;
  var AStartMode: TCsWindowsServiceStartMode);
begin
  //
end;

procedure TCsWindowsService.LoadServiceProperties;
begin
  Self.LoadServiceProperties(
    FName,
    FDisplayName,
    FDescription,
    FImagePath,
    FStartMode
  );
end;

class function TCsWindowsService.StartModeToDWORD(
  AStartMode: TCsWindowsServiceStartMode): DWORD;
begin
  Result := Self.SERVICES_REGISTRY_STARTMODE_DISABLED;
  case AStartMode of
    wssmAuto: Result := Self.SERVICES_REGISTRY_STARTMODE_AUTO;
    wssmAutoDelay: Result := Self.SERVICES_REGISTRY_STARTMODE_AUTODELAY;
    wssmManual: Result := Self.SERVICES_REGISTRY_STARTMODE_MANUEL;
  end;
end;

class function TCsWindowsService.Start(AName: string): Boolean;
var
  SCManager, SCService: THandle;
  ServiceStatus: TServiceStatus;
  p: PChar;
begin
  Result := False;
  SCManager := OpenSCManager(nil, nil, SC_MANAGER_CONNECT);
  if SCManager = 0 then Exit;
  try
    SCService := OpenService(SCManager, PChar(AName),
      SERVICE_START or SERVICE_STOP or SERVICE_QUERY_STATUS);
    if SCService = 0 then Exit;
    try
      ZeroMemory(@ServiceStatus, SizeOf(ServiceStatus));
      p := nil;
      if StartService(SCService, 0, p) then
      begin
       // warten bis der Service gestartet ist
        repeat
          if not QueryServiceStatus(SCService, ServiceStatus) then Break;
        until ServiceStatus.dwCurrentState <> SERVICE_START_PENDING;
      end else Exit;
      Result := True;
    finally
      CloseServiceHandle(SCService);
    end;
  finally
    CloseServiceHandle(SCManager);
  end;
end;

function TCsWindowsService.Start: Boolean;
begin
  Result := Self.Start(FName);
end;

class function TCsWindowsService.Stop(AName: string): Boolean;
var
  SCManager, SCService: THandle;
  ServiceStatus: TServiceStatus;
  p: PChar;
begin
  Result := False;
  SCManager := OpenSCManager(nil, nil, SC_MANAGER_CONNECT);
  if SCManager = 0 then Exit;
  try
    SCService := OpenService(SCManager, PChar(AName),
      SERVICE_START or SERVICE_STOP or SERVICE_QUERY_STATUS);
    if SCService = 0 then Exit;
    try
      ZeroMemory(@ServiceStatus, SizeOf(ServiceStatus));
    // Service beenden
      if not ControlService(SCService, SERVICE_CONTROL_STOP, ServiceStatus) then
      begin
        if GetLastError <> ERROR_SERVICE_NOT_ACTIVE then Exit;
      end else
      begin
       // warten bis der Service beendet ist
        repeat
          if not QueryServiceStatus(SCService, ServiceStatus) then Break;
        until ServiceStatus.dwCurrentState <> SERVICE_STOP_PENDING;
      end;
      Result := True;
    finally
      CloseServiceHandle(SCService);
    end;
  finally
    CloseServiceHandle(SCManager);
  end;
end;

function TCsWindowsService.Stop: Boolean;
begin
  Result := Self.Stop(FName);
end;

class function TCsWindowsService.Restart(AName: string): Boolean;
var
  SCManager, SCService: THandle;
  ServiceStatus: TServiceStatus;
  p: PChar;
begin
  Result := False;
  SCManager := OpenSCManager(nil, nil, SC_MANAGER_CONNECT);
  if SCManager = 0 then Exit;
  try
    SCService := OpenService(SCManager, PChar(AName),
      SERVICE_START or SERVICE_STOP or SERVICE_QUERY_STATUS);
    if SCService = 0 then Exit;
    try
      ZeroMemory(@ServiceStatus, SizeOf(ServiceStatus));
    // Service beenden
      if not ControlService(SCService, SERVICE_CONTROL_STOP, ServiceStatus) then
      begin
        if GetLastError <> ERROR_SERVICE_NOT_ACTIVE then Exit;
      end else
      begin
       // warten bis der Service beendet ist
        repeat
          if not QueryServiceStatus(SCService, ServiceStatus) then Break;
        until ServiceStatus.dwCurrentState <> SERVICE_STOP_PENDING;
      end;
     // Service starten
      p := nil;
      if StartService(SCService, 0, p) then
      begin
       // warten bis der Service gestartet ist
        repeat
          if not QueryServiceStatus(SCService, ServiceStatus) then Break;
        until ServiceStatus.dwCurrentState <> SERVICE_START_PENDING;
      end else Exit;
      Result := True;
    finally
      CloseServiceHandle(SCService);
    end;
  finally
    CloseServiceHandle(SCManager);
  end;
end;

function TCsWindowsService.Restart: Boolean;
begin
  Result := Self.Restart(FName);
end;

class function TCsWindowsService.IsRunning(AName: string): Boolean;
var
  SCManager, SCService: THandle;
  ServiceStatus: TServiceStatus;
  p: PChar;
begin
  Result := False;
  SCManager := OpenSCManager(nil, nil, SC_MANAGER_CONNECT);
  if SCManager = 0 then Exit;
  try
    SCService := OpenService(SCManager, PChar(AName),
      SERVICE_START or SERVICE_STOP or SERVICE_QUERY_STATUS);
    if SCService = 0 then Exit;
    try
      ZeroMemory(@ServiceStatus, SizeOf(ServiceStatus));
      QueryServiceStatus(SCService, ServiceStatus);
      Result := ServiceStatus.dwCurrentState = SERVICE_RUNNING;
    finally
      CloseServiceHandle(SCService);
    end;
  finally
    CloseServiceHandle(SCManager);
  end;
end;

function TCsWindowsService.IsRunning: Boolean;
begin
  Result := Self.IsRunning(FName);
end;

class function TCsWindowsService.SetStartMode(AName: string;
  AStartMode: TCsWindowsServiceStartMode): Boolean;
var
  Reg: TRegistry;
  StartModeWord: DWORD;
begin
  Result := False;

  Reg := TRegistry.Create;
  try
    reg.RootKey := HKEY_LOCAL_MACHINE;
    if Reg.OpenKey(Self.SERVICES_REGISTRY_PATH, False) then
    begin
      if Reg.OpenKey(AName, False) then
      begin
        StartModeWord := 4;
        case AStartMode of
          wssmDisabled: StartModeWord := 4;
          wssmAuto: StartModeWord := 2;
          wssmAutoDelay: StartModeWord := 2;
          wssmManual: StartModeWord := 3;
        end;
        Reg.WriteInteger('Start', StartModeWord);

        if AStartMode = wssmAutoDelay then
          Reg.WriteInteger('DelayedAutoStart', 1)
        else
          Reg.WriteInteger('DelayedAutoStart', 0);

        Result := True;
      end;
    end;
  finally
    FreeAndNil(Reg);
  end;
end;

function TCsWindowsService.SetStartMode(
  AStartMode: TCsWindowsServiceStartMode): Boolean;
begin
  Result := Self.SetStartMode(FName, AStartMode);
end;

class function TCsWindowsService.GetStartMode(AName: string): TCsWindowsServiceStartMode;
var
  TempStartMode: TCsWindowsServiceStartMode;
  TempDescription: String;
  TempImagePath: String;
  TempDisplayName: String;
begin
  Self.LoadServiceProperties(
    AName,
    TempDisplayName,
    TempDescription,
    TempImagePath,
    TempStartMode
  );
  Result := TempStartMode;
end;

end.

Wichtig: Nur mit Admin-Rechten kann man auf die Dienste zu greifen. Hat man nicht Berechtigung geben die Funktionen False zurück. Du musst also deiner Anwendung ein Manifest hinzufügen.

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.