Services.pas

Top  Previous  Next

 

unit Services;

// Service Control Manager functions and structures

// Feel free to use this code for whatever purpose you like,

// but be aware that it is provided "as is", and that no

// guarantees are given on correctness or usability.

 

// Erik Sperling Johansen, erik@info-pro.no

 

interface

uses

Windows;

 

const

AdvAPI32 = 'advapi32.dll';

 

SERVICE_KERNEL_DRIVER          = $00000001;

SERVICE_FILE_SYSTEM_DRIVER     = $00000002;

SERVICE_ADAPTER                = $00000004;

SERVICE_RECOGNIZER_DRIVER      = $00000008;

 

SERVICE_DRIVER                 =

  (SERVICE_KERNEL_DRIVER or

  SERVICE_FILE_SYSTEM_DRIVER or

  SERVICE_RECOGNIZER_DRIVER);

 

SERVICE_WIN32_OWN_PROCESS      = $00000010;

SERVICE_WIN32_SHARE_PROCESS    = $00000020;

SERVICE_WIN32                  =

  (SERVICE_WIN32_OWN_PROCESS or

  SERVICE_WIN32_SHARE_PROCESS);

 

SERVICE_INTERACTIVE_PROCESS    = $00000100;

 

SERVICE_TYPE_ALL               =

  (SERVICE_WIN32  or

  SERVICE_ADAPTER or

  SERVICE_DRIVER  or

  SERVICE_INTERACTIVE_PROCESS);

 

//

// Start Type

//

 

SERVICE_BOOT_START             = $00000000;

SERVICE_SYSTEM_START           = $00000001;

SERVICE_AUTO_START             = $00000002;

SERVICE_DEMAND_START           = $00000003;

SERVICE_DISABLED               = $00000004;

 

//

// Error control type

//

SERVICE_ERROR_IGNORE           = $00000000;

SERVICE_ERROR_NORMAL           = $00000001;

SERVICE_ERROR_SEVERE           = $00000002;

SERVICE_ERROR_CRITICAL         = $00000003;

 

 

//

// Service State -- for Enum Requests (Bit Mask)

//

const

SERVICE_ACTIVE                      = 1;

SERVICE_INACTIVE                    = 2;

SERVICE_STATE_ALL                   = SERVICE_ACTIVE + SERVICE_INACTIVE;

 

//

// Controls

//

const

SERVICE_CONTROL_STOP                = 1;

SERVICE_CONTROL_PAUSE               = 2;

SERVICE_CONTROL_CONTINUE            = 3;

SERVICE_CONTROL_INTERROGATE         = 4;

SERVICE_CONTROL_SHUTDOWN            = 5;

 

//

// Service State -- for CurrentState

//

const

SERVICE_STOPPED                     = 1;

SERVICE_START_PENDING               = 2;

SERVICE_STOP_PENDING                = 3;

SERVICE_RUNNING                     = 4;

SERVICE_CONTINUE_PENDING            = 5;

SERVICE_PAUSE_PENDING               = 6;

SERVICE_PAUSED                      = 7;

 

//

// Controls Accepted  (Bit Mask)

//

const

SERVICE_ACCEPT_STOP                 = 1;

SERVICE_ACCEPT_PAUSE_CONTINUE       = 2;

SERVICE_ACCEPT_SHUTDOWN             = 4;

 

//

// Service Control Manager object specific access types

//

const

SC_MANAGER_CONNECT                  = $0001;

SC_MANAGER_CREATE_SERVICE           = $0002;

SC_MANAGER_ENUMERATE_SERVICE        = $0004;

SC_MANAGER_LOCK                     = $0008;

SC_MANAGER_QUERY_LOCK_STATUS        = $0010;

SC_MANAGER_MODIFY_BOOT_CONFIG       = $0020;

 

SC_MANAGER_ALL_ACCESS               =

  STANDARD_RIGHTS_REQUIRED or

  SC_MANAGER_CONNECT or

  SC_MANAGER_CREATE_SERVICE or

  SC_MANAGER_ENUMERATE_SERVICE or

  SC_MANAGER_LOCK or

  SC_MANAGER_QUERY_LOCK_STATUS or

  SC_MANAGER_MODIFY_BOOT_CONFIG;

 

//

// Service object specific access type

//

const

SERVICE_QUERY_CONFIG                = $0001;

SERVICE_CHANGE_CONFIG               = $0002;

SERVICE_QUERY_STATUS                = $0004;

SERVICE_ENUMERATE_DEPENDENTS        = $0008;

SERVICE_START                       = $0010;

SERVICE_STOP                        = $0020;

SERVICE_PAUSE_CONTINUE              = $0040;

SERVICE_INTERROGATE                 = $0080;

SERVICE_USER_DEFINED_CONTROL        = $0100;

 

SERVICE_ALL_ACCESS                  =

  (STANDARD_RIGHTS_REQUIRED     or

  SERVICE_QUERY_CONFIG         or

  SERVICE_CHANGE_CONFIG        or

  SERVICE_QUERY_STATUS         or

  SERVICE_ENUMERATE_DEPENDENTS or

  SERVICE_START                or

  SERVICE_STOP                 or

  SERVICE_PAUSE_CONTINUE       or

  SERVICE_INTERROGATE          or

  SERVICE_USER_DEFINED_CONTROL);

 

 

//

// Handle Types

//

type

TSCHandle = THandle;

PSCHandle = ^TSCHandle;

TServiceStatusHandle = INTEGER;

 

//

// Service Status Structure

//

PServiceStatus = ^TServiceStatus;

TServiceStatus =

packed record

  dwServiceType             : INTEGER;

  dwCurrentState            : INTEGER;

  dwControlsAccepted        : INTEGER;

  dwWin32ExitCode           : INTEGER;

  dwServiceSpecificExitCode : INTEGER;

  dwCheckpoint              : INTEGER;

  dwWaitHint                : INTEGER;

end;

 

 

//

// Service Status Enumeration Structure

//

type

PEnumServiceStatusA = ^TEnumServiceStatusA;

TEnumServiceStatusA =

packed record

  lpServiceName     : PChar;

  lpDisplayName     : PChar;

  ServiceStatus     : TServiceStatus;

end;

 

TEnumServiceStatus = TEnumServiceStatusA;

PEnumServiceStatus = ^TEnumServiceStatus;

 

//

// Structures for the Lock API functions

//

type

TSCLock = POINTER;

 

PQueryServiceLockStatus = ^TQueryServiceLockStatus;

TQueryServiceLockStatus =

packed record

  fIsLocked            : INTEGER;

  lpLockOwner          : PChar;

  dwLockDuration       : INTEGER;

end;

 

 

//

// Query Service Configuration Structure

//

PQueryServiceConfig = ^TQueryServiceConfig;

TQueryServiceConfig =

packed record

  dwServiceType      : INTEGER;

  dwStartType        : INTEGER;

  dwErrorControl     : INTEGER;

  lpBinaryPathName   : PChar;

  lpLoadOrderGroup   : PChar;

  dwTagID            : INTEGER;

  lpDependencies     : PChar;

  lpServiceStartName : PChar;

  lpDisplayName      : PChar;

end;

 

 

//

// Function Prototype for the Service Main Function

//

type

TServiceMainFunction = procedure (dwNumServicesArgs : INTEGER; var lpServiceArgVectors : PChar); stdcall;

 

 

//

// Service Start Table

//

type

PServiceTableEntry = ^TServiceTableEntry;

TServiceTableEntry =

record

  lpServiceName    : PChar;

  lpServiceProc    : TServiceMainFunction;

end;

 

//

// Prototype for the Service Control Handler Function

//

type

THandlerFunction = procedure (dwControl : INTEGER); stdcall;

 

 

///////////////////////////////////////////////////////////////////////////

// API Function Prototypes

///////////////////////////////////////////////////////////////////////////

 

function ChangeServiceConfig

(

  hService              : TSCHandle;

  dwServiceType,

  dwStartType,

  dwErrorControl        : INTEGER;

  lpBinaryPathName,

  lpLoadOrderGroup      : PChar;

  var dwTagID           : INTEGER;

  lpDependencies,

  lpServiceStartName,

  lpPassword,

  lpDisplayName         : PChar

) : LONGBOOL; stdcall;

 

function CloseServiceHandle

(

  hSCObject             : TSCHandle

) : LONGBOOL; stdcall;

 

 

function ControlService

(

  hService              : TSCHandle;

  dwControl             : INTEGER;

  VAR lpServiceStatus   : tServiceStatus

) : LONGBOOL; stdcall;

 

function CreateService

(

  hSZManager            : TSCHandle;

  lpServiceName,

  lpDisplayName         : PChar;

  dwDesiredAccess,

  dwServiceType,

  dwStartType,

  dwErrorControl        : INTEGER;

  lpBinaryPathName,

  lpLoadOrderGroup      : PChar;

  dwTagID               : PINTEGER;

  lpDependencies,

  lpServiceStartName,

  lpPassword            : PChar

) : TSCHandle; stdcall;

 

function DeleteService

(

  hService              : TSCHandle

) : LONGBOOL; stdcall;

 

function EnumDependentServices

(

  hService               : TSCHandle;

  dwServiceState         : INTEGER;

  var lpServices         : TEnumServiceStatus;

  cbBufSize              : INTEGER;

  var pcbBytesNeeded     : INTEGER;

  var lpServicesReturned : INTEGER

) : LONGBOOL; stdcall;

 

function EnumServicesStatus

(

  hSCManager             : TSCHandle;

  dwServiceType,

  dwServiceState         : INTEGER;

  lPServices             : POINTER;

  cbBufSize              : INTEGER;

  var pcbBytesNeeded     : INTEGER;

  var lpServicesReturned : INTEGER;

  var lpResumeHandle     : INTEGER

) : LONGBOOL; stdcall;

 

function GetServiceKeyName

(

  hSCManager             : TSCHandle;

  lpDisplayName,

  lpServiceName          : PChar;

  var lpcchBuffer        : INTEGER

) : LONGBOOL; stdcall;

 

function GetServiceDisplayName

(

  hSCManager             : TSCHandle;

  lpServiceName,

  lpDisplayName          : PChar;

  var lpcchBuffer        : INTEGER

) : LONGBOOL; stdcall;

 

function LockServiceDatabase

(

  hSCManager             : TSCHandle

) : TSCLock; stdcall;

 

function NotifyBootConfigStatus

(

  BootAcceptable         : LONGBOOL

) : LONGBOOL; stdcall;

 

function OpenSCManager

(

  lpMachineName,

  lpDatabaseName         : PChar;

  dwDesiredAccess        : INTEGER

) : TSCHandle; stdcall;

 

function OpenService

(

  hSCManager             : TSCHandle;

  lpServiceName          : PChar;

  dwDesiredAccess        : INTEGER

) : TSCHandle; stdcall;

 

function QueryServiceConfig

(

  hService               : TSCHandle;

  lpServiceConfig        : PQueryServiceConfig;

  cbBufSize              : INTEGER;

  var pcbBytesNeeded     : INTEGER

) : LONGBOOL; stdcall;

 

function QueryServiceLockStatus

(

  hSCManager             : TSCHandle;

  var lpLockStatus       : TQueryServiceLockStatus;

  cbBufSize              : INTEGER;

  var pcbBytesNeeded     : INTEGER

) : LONGBOOL; stdcall;

 

(*

function QueryServiceObjectSecurity

(

  hService               : TSCHandle;

  dwSecurityInformation  : TSecurityInformation;

  var SecurityDescriptor : TSecurityDescriptor;

  cbBufSize              : INTEGER;

  var pcbBytesNeeded     : INTEGER

) : LONGBOOL; stdcall;

*)

 

function QueryServiceStatus

(

  hService               : TSCHandle;

  var lpServiceStatus    : TServiceStatus

) : LONGBOOL; stdcall;

 

function RegisterServiceCtrlHandler

(

  lpServiceName          : PChar;

  lpHandlerProc          : THandlerFunction

) : TServiceStatusHandle; stdcall;

 

(*

function SetServiceObjectSecurity

(

  hService               : TSCHandle;

  dwSecurityInformation  : TSecurityInformation;

  var lpSecurityDescriptor : TSecurityDescriptor

) : LONGBOOL; stdcall;

*)

 

function SetServiceStatus

(

  hServiceStatus         : TServiceStatusHandle;

  lpServiceStatus        : PServiceStatus

) : LONGBOOL; stdcall;

 

function StartServiceCtrlDispatcher

(

  lpServiceStartTable    : PServiceTableEntry

) : LONGBOOL; stdcall;

 

function StartService

(

  hService                : TSCHandle;

  dwNumServiceArgs        : INTEGER;

  lpServiceArgVectors     : PChar

) : LONGBOOL; stdcall;

 

function UnlockServiceDatabase

(

  scLock                  : TSCLock

) : LONGBOOL; stdcall;

 

implementation

 

function ChangeServiceConfig;          external AdvAPI32 name 'ChangeServiceConfigA';

function CloseServiceHandle;           external AdvAPI32;

function ControlService;               external AdvAPI32;

function CreateService;                external AdvAPI32 name 'CreateServiceA';

function DeleteService;                external AdvAPI32;

function EnumDependentServices;        external AdvAPI32 name 'EnumDependentServicesA';

function EnumServicesStatus;           external AdvAPI32 name 'EnumServicesStatusA';

function GetServiceKeyName;            external AdvAPI32 name 'GetServiceKeyNameA';

function GetServiceDisplayName;        external AdvAPI32 name 'GetServiceDisplayNameA';

function LockServiceDatabase;          external AdvAPI32;

function NotifyBootConfigStatus;       external AdvAPI32;

function OpenSCManager;                external AdvAPI32 name 'OpenSCManagerA';

function OpenService;                  external AdvAPI32 name 'OpenServiceA';

function QueryServiceConfig;           external AdvAPI32 name 'QueryServiceConfigA';

function QueryServiceLockStatus;       external AdvAPI32 name 'QueryServiceLockStatusA';

function QueryServiceStatus;           external AdvAPI32;

function RegisterServiceCtrlHandler;   external AdvAPI32 name 'RegisterServiceCtrlHandlerA';

function SetServiceStatus;             external AdvAPI32;

function StartServiceCtrlDispatcher;   external AdvAPI32 name 'StartServiceCtrlDispatcherA';

function StartService;                 external AdvAPI32 name 'StartServiceA';

function UnlockServiceDatabase;        external AdvAPI32;

 

end.

 

1)

To start / stop any service, the service must ofcourse be registered in the SCM (Service Control Manager) database.

 

All of the functions below needs a handle to SCM, get this by using OpenSCManager.

 

Having the name shown in the "services" applet in control panel, use GetServiceKeyName to retrieve the actual service name. Use OpenService to get a handle to this service, StartService to run it and ControlService (Handle, SERVICE_CONTROL_STOP, ...) to stop it.

 

2)

Yes, definitely.

 

First of all, you should not reference the Forms.pas unit, as this will automatically create TApplication, and then you've got a lot of work trying to make Application behave properly for a service. A service should rarely be invisible, if this is necessarry then dynamically load a DLL which contains the forms and so on.

 

The scope from a service is started by SCM.

 

Main thread is created, and program entry point gets control.

 

Program should fill in an 0-terminated array of TServiceTableEntry records, and call StartServiceCtrlDispatcher.

 

StartServiceCtrlDispatcher will suspend the calling (main) thread, create a new one and call the entry point defined in the passed dispatch table in the context of this new thread.

 

This procedure should first of all call RegisterServiceCtrlDispatcher to register the procedure to receive control codes. Then, it should report status using SetServiceStatus with regular intervals, until it is ready to accept control codes. Then use SetServiceStatus to set SERVICE_RUNNING, and enter a loop which does whatever work needs to be done (like e.g. accepting network connections), and with regular interval check a global boolean variable which indicates whether the service has been stopped. If so, exit the loop and use SetServiceStatus to set SERVICE_STOPPED.

 

The procedure passed in RegisterServiceCtrlHandler will be called whenever some application uses ControlService to send a control code to you. This proc is usually just a case statement responding to SERVICE_CONTROL_XXX control codes, and possibly your own private codes. Some of the codes require that you call SetServiceStatus, some are just for informing you of what has happened. This is all documented quite well in the Win API help file. Lookup RegisterServiceCtrlHandler, and then go to the SeeAlso topic "Handler"

 

 

In short. It is not too complicated to control other services, you should be able to create such code without NT available. Writing a service, however, would be almost impossible without access to NT.

 

If you find these service methodics complicated, there is a shareware Delphi expert "ServiceMaker" written by a guy called Peter Antypas. I don't know whether he has finished beta-testing it, if not it'll probably not be long. I've had a glance at a beta of this product, and it looks very good. It handles all the service wrapping functionality for you, allowing you to concentrate on what your service actually should do. I'll strongly suggest using this if you need to write NT services.