{ $Id: header,v 1.1 2000/07/13 06:33:45 michael Exp $ This file is part of the Free Component Library (FCL) Copyright (c) 1999-2000 by the Free Pascal development team See the file COPYING.FPC, included in this distribution, for details about the copyright. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. **********************************************************************} {$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.