Răsfoiți Sursa

Initial implementation

git-svn-id: trunk@6330 -
michael 18 ani în urmă
părinte
comite
348409b95c
2 a modificat fișierele cu 966 adăugiri și 0 ștergeri
  1. 1 0
      .gitattributes
  2. 965 0
      fcl/win/ServiceManager.pas

+ 1 - 0
.gitattributes

@@ -1099,6 +1099,7 @@ fcl/web/websession.pp svneol=native#text/plain
 fcl/web/webutil.pp svneol=native#text/plain
 fcl/web/wtagsimpl.inc svneol=native#text/plain
 fcl/web/wtagsintf.inc svneol=native#text/plain
+fcl/win/ServiceManager.pas -text
 fcl/win/daemonapp.inc svneol=native#text/plain
 fcl/win/eventlog.inc svneol=native#text/plain
 fcl/win/fclel.mc -text

+ 965 - 0
fcl/win/ServiceManager.pas

@@ -0,0 +1,965 @@
+{$mode objfpc}
+{$h+}
+unit ServiceManager;
+
+interface
+
+uses
+  Windows, SysUtils, Classes, jwawinnt, jwawinsvc;
+
+type
+
+  TServiceEntry = Class(TCollectionItem)
+  Private
+    FServiceName,
+    FDisplayName : String;
+    FServiceType,
+    FCurrentState,
+    FControlsAccepted,
+    FWin32ExitCode,
+    FServiceSpecificExitCode,
+    FCheckPoint,
+    FWaitHint: DWORD;
+  Private
+    Procedure SetStatusFields(Const Status : TServiceStatus);
+  Public
+    Property ServiceName : String Read FServiceName;
+    Property DisplayName : String read FDIsplayName;
+    Property ServiceType : DWord Read FServiceType;
+    Property CurrentState : DWord Read FCurrentState;
+    Property ControlsAccepted : DWord Read FControlsAccepted;
+    Property Win32ExitCode : DWord Read FWin32ExitCode;
+    Property ServiceSpecificExitCode : DWord Read FServiceSpecificExitCode;
+    Property CheckPoint : DWord Read FCheckPoint;
+    Property WaitHint: DWORD Read FWaitHint;
+  end;
+
+  TServiceEntries = Class(TOwnedCollection)
+  Private
+    Function GetService (Index : Integer) : TServiceEntry;
+  Public
+    Function FindService(ServiceName : String) : TServiceEntry;
+    Function ServiceByName(ServiceName : String) : TServiceEntry;
+    Property Items [index : Integer] : TServiceEntry Read GetService;default;
+  end;
+
+  { Record used in
+    registerservice,
+    configservice or
+    queryserviceconfig
+   }
+
+  TServiceDescriptor = Record
+    Name : ShortString;
+    DisplayName : ShortString;
+    DesiredAccess : DWord;
+    ServiceType : DWord;
+    StartType : DWord;
+    ErrorControl : DWord;
+    CommandLine : String;
+    LoadOrderGroup : String;
+    TagID : DWord;
+    Dependencies : String; // Separated by slash signs (/)
+    UserName : String;
+    Password : String;
+  end;
+
+  TServiceManager = class(TComponent)
+  private
+    { Private declarations }
+    FReconnect : Boolean;
+    FMachineName : String;
+    FAccess : DWord;
+    FHandle : THandle;
+    FDBLock : SC_LOCK;
+    FServices : TServiceEntries;
+    FAfterRefresh : TNotifyEvent;
+    FAfterConnect: TNotifyEvent;
+    FRefreshOnConnect: Boolean;
+    FBeforeDisConnect: TNotifyEvent;
+    function GetConnected: Boolean;
+    procedure SetConnected(const Value: Boolean);
+    procedure SetMachineName(const Value: string);
+  protected
+    { Protected declarations }
+    procedure Loaded;override;
+    Procedure SMError(Msg : String);
+    Procedure CheckConnected(Msg : String);
+    Procedure DoBeforeDisConnect; virtual;
+    Procedure DoAfterConnect; virtual;
+    Procedure DoAfterRefresh; virtual;
+  public
+    { Public declarations }
+    Constructor Create(AOwner: TComponent); override;
+    Destructor Destroy; override;
+    Procedure ClearServices;
+    Procedure Refresh;
+    Procedure Connect;
+    Procedure Disconnect;
+    function GetServiceHandle(ServiceName: String; SAccess: DWord): THandle;
+    procedure ContinueService(SHandle: THandle); overload;
+    procedure ContinueService(ServiceName : String); overload;
+    procedure StartService(SHandle: THandle; Args: TStrings);overload;
+    procedure StartService(ServiceName : String; Args: TStrings); overload;
+    procedure StopService(ServiceName: String; StopDependent: Boolean); overload;
+    procedure StopService(SHandle : THandle; StopDependent: Boolean); overload;
+    procedure PauseService(SHandle: THandle);overload;
+    procedure PauseService(ServiceName: String);Overload;
+    procedure CustomControlService(ServiceName : String; ControlCode : DWord); overload;
+    procedure CustomControlService(Shandle : THandle; ControlCode : DWord); overload;
+    procedure ListDependentServices(SHandle: THandle; ServiceState: DWord;  List: TStrings); overload;
+    procedure ListDependentServices(ServiceName : String; ServiceState : DWord; List : TStrings); overload;
+    Procedure LockServiceDatabase;
+    Procedure UnlockServiceDatabase;
+    procedure QueryServiceConfig(SHandle : THandle; Var Config : TServiceDescriptor);overload;
+    procedure QueryServiceConfig(ServiceName : String; Var Config : TServiceDescriptor);overload;
+    Function  RegisterService(Var Desc : TServiceDescriptor) : THandle;
+    procedure SetStartupType(ServiceName: String; StartupType: DWord); overload;
+    procedure SetStartupType(SHandle : THandle; StartupType: DWord); overload;
+    Procedure UnregisterService(ServiceName : String);
+    procedure ConfigService(SHandle: THandle; Config: TServiceDescriptor); overload;
+    procedure ConfigService(ServiceName : string; Config: TServiceDescriptor); overload;
+    procedure RefreshServiceStatus(ServiceName: String);
+    procedure GetServiceStatus(SHandle : THandle; Var Status : TServiceStatus); overload;
+    procedure GetServiceStatus(ServiceName : String; Var Status : TServiceStatus); overload;
+    Property  Handle : THandle Read FHandle;
+    Property  Acces : DWord read FAccess Write FAccess;
+    Property  Services : TServiceEntries Read FServices;
+  published
+    { Published declarations }
+    Property Connected : Boolean Read GetConnected Write SetConnected;
+    Property MachineName : string Read FMachineName Write SetMachineName;
+    Property RefreshOnConnect : Boolean Read FRefreshOnConnect Write FrefreshOnConnect;
+    Property AfterRefresh : TNotifyEvent Read FAfterRefresh Write FAfterRefresh;
+    Property AfterConnect : TNotifyEvent Read FAfterConnect Write FAfterConnect;
+    Property BeforeDisConnect : TNotifyEvent Read FBeforeDisConnect Write FBeforeDisConnect;
+  end;
+
+  EServiceManager = Class(Exception);
+
+Const
+  StartTypes : Array[0..4] of DWord = (
+    SERVICE_AUTO_START,SERVICE_BOOT_START, SERVICE_DEMAND_START,
+    SERVICE_SYSTEM_START, SERVICE_DISABLED );
+  ServiceTypes : Array[0..3] of DWord = (
+    SERVICE_FILE_SYSTEM_DRIVER, SERVICE_KERNEL_DRIVER,
+    SERVICE_WIN32_OWN_PROCESS, SERVICE_WIN32_SHARE_PROCESS );
+  StartErrors : Array[0..3] of DWord = (
+    SERVICE_ERROR_IGNORE, SERVICE_ERROR_NORMAL,
+    SERVICE_ERROR_SEVERE, SERVICE_ERROR_CRITICAL);
+
+Function ServiceTypeToString(AType : Dword) : String;
+Function ServiceStateToString(AState : DWord) : String;
+Function ControlsAcceptedToString(AValue : DWord) : String;
+Function IsInteractiveService(AType : Dword) : Boolean;
+
+implementation
+
+
+ResourceString
+  SErrConnected       = 'Operation not permitted while connected to Service Control Manager';
+  SErrNotConnected    = 'Not connected to Service control manager. Cannot %s';
+  SErrInvalidControlCode = 'Invalid custom control code : %d';
+  SQueryServiceList   = 'Query service list';
+  SActive             = 'Active';
+  SInactive           = 'Inactive';
+  SStopped            = 'Stopped';
+  SStartPending       = 'Start pending';
+  SStopPending        = 'Stop pending';
+  SRunning            = 'Running';
+  SContinuePending    = 'Continue pending';
+  SPausePending       = 'Pause pending';
+  SPaused             = 'Paused';
+  SUnknownState       = 'Unknown State (%d)';
+  SUnknownType        = 'Unknown type (%d)';
+  SStop               = 'Stop';
+  SPauseContinue      = 'Pause/continue';
+  SShutDown           = 'Shutdown';
+  SDeviceDriver       = 'Device driver';
+  SFileSystemDriver   = 'Filesystem driver';
+  SAdapter            = 'Adapter';
+  SRecognizer         = 'Recognizer';
+  SService            = 'Service';
+  SSHaredService      = 'Service (shared)';
+  SErrServiceNotFound = 'Service "%s" not found.';
+
+
+{ TServiceManager }
+
+{$ifdef ver130}
+
+Type
+  PPChar = ^PChar;
+  PCharArray = Array[Word] of PChar;
+  PPCharArray = ^PCharArray;
+
+Procedure RaiseLastOSError;
+
+begin
+  RaiseLastWin32Error;
+end;
+{$endif}
+
+procedure TServiceManager.CheckConnected(Msg: String);
+begin
+  If Not Connected then
+    SMError(Format(SErrNotConnected,[Msg]));
+end;
+
+procedure TServiceManager.ClearServices;
+begin
+  FServices.Clear;
+end;
+
+procedure TServiceManager.Connect;
+
+Var
+   P : PChar;
+
+begin
+  If (FHandle=0) then
+    begin
+    P:=Nil;
+    If (MachineName<>'') then
+      P:=PChar(MachineName);
+    FHandle:=OpenSCManager(P,Nil,FAccess);
+    If (FHandle=0) then
+      RaiseLastOSError;
+    DoAfterConnect;
+    If RefreshOnConnect then
+      Refresh;
+    end;
+end;
+
+constructor TServiceManager.Create(AOwner: TComponent);
+begin
+  inherited;
+  FServices:=TServiceEntries.Create(Self,TServiceEntry);
+  FAccess:=SC_MANAGER_ALL_ACCESS;
+end;
+
+destructor TServiceManager.Destroy;
+begin
+  FServices.Free;
+  Inherited;
+end;
+
+procedure TServiceManager.Disconnect;
+begin
+  IF (FHandle<>0) then
+    begin
+    DoBeforeDisConnect;
+    CloseServiceHandle(FHandle);
+    FHandle:=0;
+    end;
+end;
+
+function TServiceManager.GetConnected: Boolean;
+begin
+  Result:=(Handle<>0);
+end;
+
+procedure TServiceManager.Refresh;
+
+Var
+  BytesNeeded,
+  ServicesReturned,
+  ResumeHandle : DWord;
+  Info,P : PEnumServiceStatus;
+  E : TServiceEntry;
+  I : integer;
+
+begin
+  ClearServices;
+  CheckConnected(SQueryServiceList);
+  BytesNeeded:=0;
+  ServicesReturned:=0;
+  ResumeHandle:=0;
+  Info:=Nil;
+  EnumServicesStatus(FHandle,SERVICE_WIN32,SERVICE_STATE_ALL,Info,0,
+                     BytesNeeded,ServicesReturned,Resumehandle);
+  if (GetLastError<>ERROR_MORE_DATA) then
+    RaiseLastOSError;
+  Getmem(Info,BytesNeeded);
+  Try
+    P:=Info;
+    If Not EnumServicesStatus(FHandle,SERVICE_WIN32,SERVICE_STATE_ALL,Info,BytesNeeded,
+                       BytesNeeded,ServicesReturned,Resumehandle) then
+      RaiseLastOSError;
+    For I:=1 to Servicesreturned do
+      begin
+      E:=FServices.Add as TServiceEntry;
+      With E,P^ do
+        begin
+        FServiceName:=StrPas(lpServiceName);
+        FDisplayName:=StrPas(lpDisplayName);
+        SetStatusFields(ServiceStatus);
+        end;
+      PChar(P):=Pchar(P)+SizeOf(TEnumServiceStatus);
+      end;
+    Finally
+    FreeMem(Info);
+  end;
+  DoAfterRefresh;
+end;
+
+procedure TServiceManager.SetConnected(const Value: Boolean);
+begin
+  If (([csLoading,csdesigning] * ComponentState)<>[]) then
+    FReconnect:=Value
+  else
+    If Value<>GetConnected then
+      If Value then
+        Connect
+      Else
+        Disconnect;
+end;
+
+procedure TServiceManager.Loaded;
+
+begin
+  Inherited;
+  If FReconnect then
+    Connect;
+end;
+
+procedure TServiceManager.SetMachineName(const Value: string);
+begin
+  If Connected then
+    SMError(SErrConnected);
+  FMachineName := Value;
+end;
+
+procedure TServiceManager.SMError(Msg: String);
+begin
+  raise EServiceManager.Create(Msg);
+end;
+
+Function ServiceTypeToString(AType : Dword) : String;
+
+begin
+  Case (AType and $FF) of
+    SERVICE_KERNEL_DRIVER       : Result:=SDeviceDriver;
+    SERVICE_FILE_SYSTEM_DRIVER  : Result:=SFileSystemDriver;
+    SERVICE_ADAPTER             : Result:=SAdapter;
+    SERVICE_RECOGNIZER_DRIVER   : Result:=SRecognizer;
+    SERVICE_WIN32_OWN_PROCESS   : Result:=SService;
+    SERVICE_WIN32_SHARE_PROCESS : Result:=SSHaredService;
+  else
+    Result:=Format(SUnknownType,[AType]);
+  end;
+end;
+
+Function IsInteractiveService(AType : Dword) : Boolean;
+
+begin
+  Result:=(Atype and SERVICE_INTERACTIVE_PROCESS)<>0;
+end;
+
+Function ServiceStateToString(AState : Dword) : String;
+
+begin
+  Case AState of
+    SERVICE_STOPPED          : Result:=SStopped;
+    SERVICE_START_PENDING    : Result:=SStartPending;
+    SERVICE_STOP_PENDING     : Result:=SStopPending;
+    SERVICE_RUNNING          : Result:=SRunning;
+    SERVICE_CONTINUE_PENDING : Result:=SContinuePending;
+    SERVICE_PAUSE_PENDING    : Result:=SPausePending;
+    SERVICE_PAUSED           : Result:=SPaused;
+  else
+    Result:=Format(SUnknownState,[AState]);
+  end;
+end;
+
+Function ControlsAcceptedToString(AValue : DWord) : String;
+
+  Procedure AddToResult(S : String);
+  begin
+    If (Result='') then
+      Result:=S
+    else
+      Result:=Result+','+S
+  end;
+
+begin
+  Result:='';
+  If (AValue and SERVICE_ACCEPT_STOP)<>0 then
+    AddToResult(SStop);
+  If (AValue and SERVICE_ACCEPT_PAUSE_CONTINUE)<>0 then
+    AddToResult(SPauseContinue);
+  If (AValue and SERVICE_ACCEPT_SHUTDOWN)<>0 then
+    AddToResult(SShutDown)
+end;
+
+procedure TServiceManager.DoAfterConnect;
+begin
+  If Assigned(FAfterConnect) then
+    FAfterConnect(Self);
+end;
+
+procedure TServiceManager.DoAfterRefresh;
+begin
+  If Assigned(FAfterRefresh) then
+    FAfterRefresh(Self);
+
+end;
+
+procedure TServiceManager.DoBeforeDisConnect;
+begin
+  If Assigned(FBeforeDisconnect) then
+    FBeforeDisconnect(Self);
+end;
+
+Function AllocDependencyList (Const S : String) : PChar;
+
+Var
+  I,L : Integer;
+
+begin
+  Result:=Nil;
+  If (S<>'') then
+    begin
+    // Double Null terminated list of null-terminated strings.
+    L:=Length(S);
+    GetMem(Result,L+3);
+    Move(S[1],Result^,L+1); // Move terminating null as well.
+    Result[L+1]:=#0;
+    Result[L+2]:=#0;
+    For I:=0 to L-1 do
+      If Result[i]='/' then // Change / to #0.
+        Result[i]:=#0;
+    end;
+end;
+
+Function TServiceManager.RegisterService(var Desc: TServiceDescriptor) : Thandle;
+
+Var
+  PDep,PLO,PUser,PPWd : PChar; // We need Nil for some things.
+  N,D : String;
+  ReturnTag : DWord;
+
+begin
+  With Desc do
+    begin
+    N:=Name;
+    D:=DisplayName;
+    If (LoadOrderGroup='') then
+      PLO:=Nil
+    else
+      PLO:=PChar(LoadOrderGroup);
+    PPwd:=Nil;
+    PUser:=Nil;
+    If (UserName<>'') then
+      begin
+      PUser:=PChar(UserName);
+      If (Password<>'') then
+        PPWd:=PChar(Password);
+      end;
+    PDep:=AllocDependencyList(Dependencies);
+    Try
+      Result:=CreateService(Self.Handle,PChar(N),PChar(D),DesiredAccess,ServiceType,
+                            StartType,ErrorControl,PChar(CommandLine),PLO,Nil,
+                            PDep,PUser,PPwd);
+      If (Result=0) then
+        RaiseLastOSError;
+    Finally
+      If PDep<>Nil then
+        FreeMem(PDep);
+    end;
+    end;
+end;
+
+procedure TServiceManager.ListDependentServices(ServiceName : String; ServiceState : DWord; List : TStrings);
+
+Var
+  H : THandle;
+
+begin
+  H:=OpenService(Handle,PChar(ServiceName),SERVICE_ENUMERATE_DEPENDENTS);
+  try
+    ListDependentServices(H,ServiceState,List);
+  Finally
+    CloseServiceHandle(H);
+  end;
+end;
+
+
+procedure TServiceManager.ListDependentServices(SHandle: THandle; ServiceState : DWord; List : TStrings);
+
+Var
+  P,E : PEnumServiceStatus;
+  I,BytesNeeded,Count : DWord;
+
+begin
+  P:=Nil;
+  List.Clear;
+  // If call succeeds with size 0, then there are no dependent services...
+  if Not EnumDependentServices(SHandle,ServiceState,P,0,BytesNeeded,Count) then
+    begin
+    If (GetLastError<>ERROR_MORE_DATA) then
+      RaiseLastOSError;
+    GetMem(P,BytesNeeded);
+    Try
+      If Not EnumDependentServices(SHandle,ServiceState,P,bytesNeeded,BytesNeeded,Count) Then
+        RaiseLastOSError;
+      E:=P;
+      For I:=0 to Count-1 do
+        begin
+        List.Add(StrPas(E^.lpServiceName));
+        Pchar(E):=PChar(E)+SizeOf(TEnumServiceStatus);
+        end;
+    Finally
+      FreeMem(P);
+    end;
+    end;
+end;
+
+
+Procedure TServiceManager.StopService(SHandle : THandle; StopDependent : Boolean);
+
+Var
+  I : Integer;
+  List : TStrings;
+  Status : TServiceStatus;
+
+begin
+  If Not QueryServiceStatus(SHandle,Status) then
+    RaiseLastOSError;
+  If Not (Status.dwCurrentState=SERVICE_STOPPED) then
+    begin
+    If StopDependent then
+      begin
+      List:=TStringList.Create;
+      Try
+        ListDependentServices(SHandle,SERVICE_ACTIVE,List);
+        For I:=0 to List.Count-1 do
+          StopService(List[i],False); // Do not recurse !!
+      Finally
+        List.Free;
+      end;
+      end;
+    If Not ControlService(SHandle,SERVICE_CONTROL_STOP,Status) then
+      RaiseLastOSError;
+    end;
+end;
+
+Procedure TServiceManager.StopService(ServiceName : String; StopDependent : Boolean);
+
+Var
+  H : THandle;
+  A : DWORD;
+
+begin
+  A:=SERVICE_STOP or SERVICE_QUERY_STATUS;
+  If StopDependent then
+    A:=A or SERVICE_ENUMERATE_DEPENDENTS;
+  H:=OpenService(Handle,PChar(ServiceName),A);
+  Try
+    StopService(H,StopDependent);
+  Finally
+    CloseServiceHandle(H);
+  end;
+end;
+
+
+Function TServiceManager.GetServiceHandle(ServiceName : String; SAccess : DWord) : THandle;
+
+begin
+  Result:=OpenService(Handle,PChar(ServiceName),SAccess);
+  If (Result=0) then
+    RaiseLastOSError;
+end;
+
+procedure TServiceManager.UnregisterService(ServiceName: String);
+
+Var
+  H : THandle;
+  Status : TServiceStatus;
+
+begin
+  StopService(ServiceName,True);
+  H:=GetServiceHandle(ServiceName,SERVICE_STOP or SERVICE_QUERY_STATUS or SERVICE_DELETE);
+  Try
+    If Not DeleteService(H) then
+      RaiseLastOSError;
+  Finally
+    CloseServiceHandle(H);
+  end;
+end;
+
+Procedure TServiceManager.PauseService(SHandle : THandle);
+
+Var
+  Status : TServiceStatus;
+
+begin
+  If Not ControlService(SHandle,SERVICE_CONTROL_PAUSE,Status) then
+    RaiseLastOSError;
+end;
+
+Procedure TServiceManager.PauseService(ServiceName : String);
+
+Var
+  H : THandle;
+
+begin
+  H:=GetServiceHandle(ServiceName,SERVICE_PAUSE_CONTINUE);
+  Try
+    PauseService(H);
+  Finally
+    CloseServiceHandle(H);
+  end;
+end;
+
+Procedure TServiceManager.ContinueService(SHandle : THandle);
+
+Var
+  Status : TServiceStatus;
+
+begin
+  If Not ControlService(SHandle,SERVICE_CONTROL_CONTINUE,Status) then
+    RaiseLastOSError;
+end;
+
+Procedure TServiceManager.ContinueService(ServiceName : String);
+
+Var
+  H : THandle;
+
+begin
+  H:=GetServiceHandle(ServiceName,SERVICE_PAUSE_CONTINUE);
+  Try
+    ContinueService(H);
+  Finally
+    CloseServiceHandle(H);
+  end;
+end;
+
+Function StringsToPCharList(List : TStrings) : PPChar;
+
+Var
+  I : Integer;
+  S : String;
+
+begin
+  I:=(List.Count)+1;
+  GetMem(Result,I*sizeOf(PChar));
+  PPCharArray(Result)^[List.Count]:=Nil;
+  For I:=0 to List.Count-1 do
+    begin
+    S:=List[i];
+    PPCharArray(Result)^[i]:=StrNew(PChar(S));
+    end;
+end;
+
+Procedure FreePCharList(List : PPChar);
+
+Var
+  I : integer;
+
+begin
+  I:=0;
+  While PPChar(List)[i]<>Nil do
+    begin
+    StrDispose(PPChar(List)[i]);
+    Inc(I);
+    end;
+  FreeMem(List);
+end;
+
+Procedure TServiceManager.StartService(SHandle : THandle; Args : TStrings);
+
+Var
+  Argc : DWord;
+  PArgs : PPchar;
+
+begin
+  If (Args=Nil) or (Args.Count>0) then
+    begin
+    Argc:=0;
+    Pargs:=Nil;
+    end
+  else
+    begin
+    ArgC:=Args.Count;
+    Pargs:=StringsToPcharList(Args);
+    end;
+  Try
+    If not jwawinsvc.StartService(SHandle,Argc,PArgs^) then
+      RaiseLastOSError;
+  Finally
+    If (PArgs<>Nil) then
+      FreePCharList(PArgs);
+  end;
+end;
+
+
+Procedure TServiceManager.StartService(ServiceName : String; Args : TStrings);
+
+Var
+  H : THandle;
+
+begin
+  H:=GetServiceHandle(ServiceName,SERVICE_START);
+  Try
+    StartService(H,Args);
+  Finally
+    CloseServiceHandle(H);
+  end;
+end;
+
+Procedure TServiceManager.LockServiceDatabase;
+
+begin
+  FDBLock:=jwawinsvc.LockServiceDatabase(Handle);
+  If FDBLock=Nil then
+    RaiseLastOSError;
+end;
+
+procedure TServiceManager.UnlockServiceDatabase;
+begin
+  If (FDBLock<>Nil) then
+    begin
+    Try
+      If Not jwawinsvc.UnLockServiceDatabase(FDBLock) then
+        RaiseLastOSError;
+    Finally
+      FDBLock:=Nil;
+    end;
+    end;
+end;
+
+procedure TServiceManager.QueryServiceConfig(SHandle : THandle; Var Config : TServiceDescriptor);
+
+Var
+  SvcCfg : PQueryServiceConfig;
+  BytesNeeded : DWord;
+
+begin
+  jwawinsvc.QueryServiceConfig(SHandle,Nil,0,BytesNeeded);
+  If (GetLastError<>ERROR_INSUFFICIENT_BUFFER) then
+    RaiseLastOSError;
+  GetMem(SvcCfg,BytesNeeded);
+  Try
+    If Not jwawinsvc.QueryServiceConfig(SHandle,SvcCfg,BytesNeeded,BytesNeeded) then
+      RaiseLastOSError;
+    With config,SvcCfg^ do
+      begin
+      Password:='';
+      Name:='';
+      DesiredAccess:=0;
+      ErrorControl:=dwErrorControl;
+      ServiceType:=dwServiceType;
+      StartType:=dwStartType;
+      TagID:=dwTagID;
+      CommandLine:=lpBinaryPathName;
+      LoadOrderGroup:=lpLoadOrderGroup;
+      Dependencies:=lpDependencies;
+      UserName:=lpServiceStartName;
+      DisplayName:=lpDisplayName;
+      end;
+  Finally
+    FreeMem(SvcCfg,BytesNeeded);
+  end;
+end;
+
+procedure TServiceManager.QueryServiceConfig(ServiceName : String; Var Config : TServiceDescriptor);
+
+Var
+  H : THandle;
+
+begin
+  H:=GetServiceHandle(ServiceName,SERVICE_QUERY_CONFIG);
+  Try
+    QueryServiceConfig(H,Config);
+  Finally
+    CloseServiceHandle(H);
+  end;
+end;
+
+procedure TServiceManager.SetStartupType(ServiceName : String; StartupType : DWord);
+
+Var
+  H : THandle;
+
+begin
+  H:=GetServiceHandle(ServiceName,SERVICE_CHANGE_CONFIG);
+  Try
+    SetStartupType(H,StartupType);
+  Finally
+    CloseServiceHandle(H);
+  end;
+end;
+
+procedure TServiceManager.SetStartupType(SHandle : THandle; StartupType: DWord);
+
+Const
+  SNC = SERVICE_NO_CHANGE;  // Shortcut
+
+begin
+  If Not ChangeServiceConfig(SHandle,SNC,StartupType,SNC,Nil,Nil,Nil,Nil,Nil,Nil,Nil) then
+    RaiseLastOSError;
+end;
+
+procedure TServiceManager.ConfigService(SHandle : THandle ; Config : TServiceDescriptor);
+
+  Function SToPchar(Var S : String) : PChar;
+
+  begin
+    If (S='') then
+      Result:=Nil
+    else
+      Result:=PChar(S);
+  end;
+
+Var
+  PDep,PLO,PUser,PPWd,PCmd,PDisp : PChar; // We need Nil for some things.
+  D : String;
+  ReturnTag : DWord;
+
+begin
+  With Config do
+    begin
+    PCmd:=SToPChar(CommandLine);
+    D:=DisplayName;
+    PDisp:=StoPChar(D);
+    PLO:=SToPChar(LoadOrderGroup);
+    PUser:=SToPChar(UserName);
+    PPwd:=SToPchar(Password);
+    PDep:=AllocDependencyList(Dependencies);
+    Try
+      If Not ChangeServiceConfig(SHandle,ServiceType,StartType,ErrorControl,
+                                  PCmd,PLO,Nil,PDep,PUser,PPwd,PDisp) then
+        RaiseLastOSError;
+    Finally
+      If PDep<>Nil then
+        FreeMem(PDep);
+    end;
+    end;
+end;
+
+procedure TServiceManager.GetServiceStatus(SHandle : THandle; Var Status: TServiceStatus);
+
+begin
+  If Not QueryServiceStatus(SHandle,Status) then
+    RaiseLastOSError;
+end;
+
+procedure TServiceManager.GetServiceStatus(ServiceName : String; Var Status: TServiceStatus);
+
+Var
+  H : THandle;
+
+begin
+  H:=GetServiceHandle(ServiceName,SERVICE_QUERY_STATUS);
+  Try
+    GetServiceStatus(H,Status);
+  Finally
+    CloseServiceHandle(H);
+  end;
+end;
+
+procedure TServiceManager.RefreshServiceStatus(ServiceName : String);
+
+Var
+  Status : TServiceStatus;
+  SE : TServiceEntry;
+
+
+begin
+  SE:=Services.ServiceByName(ServiceName);
+  GetServiceStatus(ServiceName,Status);
+  SE.SetStatusFields(Status);
+end;
+
+
+procedure TServiceManager.ConfigService(ServiceName : String; Config : TServiceDescriptor);
+
+Var
+  H : THandle;
+
+begin
+  H:=GetServiceHandle(ServiceName,SERVICE_CHANGE_CONFIG);
+  Try
+    ConfigService(H,Config);
+  Finally
+    CloseServiceHandle(H);
+  end;
+end;
+
+
+procedure TServiceManager.CustomControlService(ServiceName: String; ControlCode: DWord);
+
+Var
+  H : THandle;
+
+begin
+  H:=GetServiceHandle(ServiceName,SERVICE_USER_DEFINED_CONTROL);
+  Try
+    CustomControlService(H,ControlCode);
+  Finally
+    CloseServiceHandle(H);
+  end;
+end;
+
+procedure TServiceManager.CustomControlService(Shandle: THandle;
+  ControlCode: DWord);
+
+Var
+  Status : TServiceStatus;
+
+begin
+  If (ControlCode<128) or (ControlCode>255) then
+    Raise EServiceManager.CreateFmt(SErrInvalidControlCode,[ControlCode]);
+  If Not ControlService(SHandle,ControlCode,Status) then
+    RaiseLastOSError;
+end;
+
+{ TServiceEntries }
+
+function TServiceEntries.FindService(ServiceName: String): TServiceEntry;
+
+Var
+  I : Integer;
+
+begin
+  Result:=Nil;
+  I:=Count-1;
+  While (I>=0) and (Result=Nil) do
+    If CompareText(Items[i].ServiceName,ServiceName)=0 then
+      Result:=Items[i]
+    else
+      Dec(I);
+end;
+
+function TServiceEntries.GetService(Index: Integer): TServiceEntry;
+begin
+  Result:=inherited Items[Index] as TServiceEntry;
+end;
+
+function TServiceEntries.ServiceByName(ServiceName: String): TServiceEntry;
+
+begin
+  Result:=FindService(ServiceName);
+  If Result=Nil then
+    Raise EServiceManager.CreateFmt(SErrServiceNotFound,[ServiceName]);
+end;
+
+{ TServiceEntry }
+
+procedure TServiceEntry.SetStatusFields(const Status: TServiceStatus);
+begin
+  With Status do
+    begin
+    FServiceType:=dwServiceType;
+    FCurrentState:=dwCurrentState;
+    FControlsAccepted:=dwControlsAccepted;
+    FWin32ExitCode:=dwWin32ExitCode;
+    FServiceSpecificExitCode:=dwServiceSpecificExitCode;
+    FCheckPoint:=dwCheckPoint;
+    FWaitHint:=dwWaitHint;
+    end;
+end;
+
+end.