Files
HeidiSQL/components/mORMot/SQLite3/SQLite3Service.pas

752 lines
28 KiB
ObjectPascal

/// Win NT Service managment classes
// - this unit is a part of the freeware Synopse mORMot framework,
// licensed under a MPL/GPL/LGPL tri-license; version 1.17
unit SQLite3Service;
{
This file is part of Synopse mORMot framework.
Synopse mORMot framework. Copyright (C) 2012 Arnaud Bouchez
Synopse Informatique - http://synopse.info
*** BEGIN LICENSE BLOCK *****
Version: MPL 1.1/GPL 2.0/LGPL 2.1
The contents of this file are subject to the Mozilla Public License Version
1.1 (the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at
http://www.mozilla.org/MPL
Software distributed under the License is distributed on an "AS IS" basis,
WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
for the specific language governing rights and limitations under the License.
The Original Code is Synopse SQLite3/mORMot database framework.
The Initial Developer of the Original Code is Arnaud Bouchez.
Portions created by the Initial Developer are Copyright (C) 2012
the Initial Developer. All Rights Reserved.
Contributor(s):
Alternatively, the contents of this file may be used under the terms of
either the GNU General Public License Version 2 or later (the "GPL"), or
the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
in which case the provisions of the GPL or the LGPL are applicable instead
of those above. If you wish to allow use of your version of this file only
under the terms of either the GPL or the LGPL, and not to allow others to
use your version of this file under the terms of the MPL, indicate your
decision by deleting the provisions above and replace them with the notice
and other provisions required by the GPL or the LGPL. If you do not delete
the provisions above, a recipient may use your version of this file under
the terms of any one of the MPL, the GPL or the LGPL.
***** END LICENSE BLOCK *****
Win NT Service managment classes
--------------------------------
Version 1.3
- TService debug and enhancements
- can compile without SQLite3Commons dependency for smaller executables
Version 1.4 - February 8, 2010
- whole Synopse SQLite3 database framework released under the GNU Lesser
General Public License version 3, instead of generic "Public Domain"
Version 1.16
- code refactoring after Leander007 proposals for better compatibility -
see http://synopse.info/forum/viewtopic.php?id=584
}
interface
{$I Synopse.inc}
{$ifdef LVCL}
{$define NOSQLITE3COMMONS}
// define it if you don't need to include the SQLite3Commons unit
// - could save some code size in the final executable
{$endif}
uses
Windows, WinSVC, Messages, Classes, SysUtils, SynCommons
{$ifndef NOSQLITE3COMMONS}, SQLite3Commons{$endif} // translated caption needed only if with full UI
;
const
CM_SERVICE_CONTROL_CODE = WM_USER+1000;
type
{{ all possible states of the service }
TServiceState =
(ssNotInstalled, ssStopped, ssStarting, ssStopping, ssRunning,
ssResuming, ssPausing, ssPaused, ssErrorRetrievingState);
{{ TServiceControler class is intended to create a new service instance or
to maintain (that is start, stop, pause, resume...) an existing service
- to provide the service itself, use the TService class }
TServiceController = class
protected
FSCHandle: THandle;
FHandle: THandle;
FStatus: TServiceStatus;
private
function GetStatus: TServiceStatus;
function GetState: TServiceState;
public
{{ Creates a new service and allows to control it and/or its configuration.
Expected Parameters (strings are unicode-ready since Delphi 2009):
- TargetComputer - set it to empty string if local computer is the target.
- DatabaseName - set it to empty string if the default database is supposed
('ServicesActive').
- Name - name of a service.
- DisplayName - display name of a service.
- Path - a path to binary (executable) of the service created.
- OrderGroup - an order group name (unnecessary)
- Dependances - string containing a list with names of services, which must
start before (every name should be separated with #0, entire
list should be separated with #0#0. Or, an empty string can be
passed if there are no dependances).
- Username - login name. For service type SERVICE_WIN32_OWN_PROCESS, the
account name in the form of "DomainName\Username"; If the account
belongs to the built-in domain, ".\Username" can be specified;
Services of type SERVICE_WIN32_SHARE_PROCESS are not allowed to
specify an account other than LocalSystem. If '' is specified, the
service will be logged on as the 'LocalSystem' account, in which
case, the Password parameter must be empty too.
- Password - a password for login name. If the service type is
SERVICE_KERNEL_DRIVER or SERVICE_FILE_SYSTEM_DRIVER,
this parameter is ignored.
- DesiredAccess - a combination of following flags:
SERVICE_ALL_ACCESS (default value), SERVICE_CHANGE_CONFIG,
SERVICE_ENUMERATE_DEPENDENTS, SERVICE_INTERROGATE, SERVICE_PAUSE_CONTINUE,
SERVICE_QUERY_CONFIG, SERVICE_QUERY_STATUS, SERVICE_START, SERVICE_STOP,
SERVICE_USER_DEFINED_CONTROL
- ServiceType - a set of following flags:
SERVICE_WIN32_OWN_PROCESS (default value, which specifies a Win32 service
that runs in its own process), SERVICE_WIN32_SHARE_PROCESS,
SERVICE_KERNEL_DRIVER, SERVICE_FILE_SYSTEM_DRIVER,
SERVICE_INTERACTIVE_PROCESS (default value, which enables a Win32 service
process to interact with the desktop)
- StartType - one of following values:
SERVICE_BOOT_START, SERVICE_SYSTEM_START,
SERVICE_AUTO_START (which specifies a device driver or service started by
the service control manager automatically during system startup),
SERVICE_DEMAND_START (default value, which specifies a service started by
a service control manager when a process calls the StartService function,
that is the TServiceController.Start method), SERVICE_DISABLED
- ErrorControl - one of following:
SERVICE_ERROR_IGNORE, SERVICE_ERROR_NORMAL (default value, by which
the startup program logs the error and displays a message but continues
the startup operation), SERVICE_ERROR_SEVERE,
SERVICE_ERROR_CRITICAL }
constructor CreateNewService(const TargetComputer, DatabaseName,
Name, DisplayName, Path: string;
const OrderGroup: string = ''; const Dependances: string = '';
const Username: string = ''; const Password: string = '';
DesiredAccess: DWORD = SERVICE_ALL_ACCESS;
ServiceType: DWORD = SERVICE_WIN32_OWN_PROCESS or SERVICE_INTERACTIVE_PROCESS;
StartType: DWORD = SERVICE_DEMAND_START; ErrorControl: DWORD = SERVICE_ERROR_NORMAL);
{{ Opens an existing service, in order to control it or its configuration
from your application. Parameters (strings are unicode-ready since Delphi 2009):
- TargetComputer - set it to empty string if local computer is the target.
- DatabaseName - set it to empty string if the default database is supposed
('ServicesActive').
- Name - name of a service.
- DesiredAccess - a combination of following flags:
SERVICE_ALL_ACCESS, SERVICE_CHANGE_CONFIG, SERVICE_ENUMERATE_DEPENDENTS,
SERVICE_INTERROGATE, SERVICE_PAUSE_CONTINUE, SERVICE_QUERY_CONFIG,
SERVICE_QUERY_STATUS, SERVICE_START, SERVICE_STOP, SERVICE_USER_DEFINED_CONTROL }
constructor CreateOpenService(const TargetComputer, DataBaseName, Name: String;
DesiredAccess: DWORD = SERVICE_ALL_ACCESS);
{{ release memory and handles }
destructor Destroy; override;
{{ Handle of SC manager }
property SCHandle: THandle read FSCHandle;
{{ Handle of service opened or created
- its value is 0 if something failed in any Create*() method }
property Handle: THandle read FHandle;
{{ Retrieve the Current status of the service }
property Status: TServiceStatus read GetStatus;
{{ Retrive the Current state of the service }
property State: TServiceState read GetState;
{{ Requests the service to stop }
function Stop: boolean;
{{ Requests the service to pause }
function Pause: boolean;
{{ Requests the paused service to resume }
function Resume: boolean;
{{ Requests the service to update immediately its current status information
to the service control manager }
function Refresh: boolean;
{{ Request the service to shutdown
- this function always return false }
function Shutdown: boolean;
{{ Removes service from the system, i.e. close the Service }
function Delete: boolean;
{{ starts the execution of a service with some specified arguments
- this version expect PChar pointers, either AnsiString (for FPC and old
Delphi compiler), either UnicodeString (till Delphi 2009) }
function Start(const Args: array of PChar): boolean;
end;
TService = class;
/// callback procedure for Windows Service Controller
TServiceCtrlHandler = procedure(CtrlCode: DWORD); stdcall;
/// event triggered for Control handler
TServiceControlEvent = procedure(Sender: TService; Code: DWORD) of object;
/// event triggered to implement the Service functionality
TServiceEvent = procedure(Sender: TService) of object;
{{ TService is the class used to implement a service provided by an application }
TService = class
protected
fSName: String;
fDName: String;
fStartType: DWORD;
fServiceType: DWORD;
fData: DWORD;
fCtrlHandler: TServiceCtrlHandler;
fOnControl: TServiceControlEvent;
fOnInterrogate: TServiceEvent;
fOnPause: TServiceEvent;
fOnShutdown: TServiceEvent;
fOnStart: TServiceEvent;
fOnExecute: TServiceEvent;
fOnResume: TServiceEvent;
fOnStop: TServiceEvent;
fStatusRec: TServiceStatus;
fArgsList: array of string;
fJumper: PByteArray;
fStatusHandle: THandle;
function GetArgCount: Integer;
function GetArgs(Idx: Integer): String;
function GetInstalled: boolean;
procedure SetStatus(const Value: TServiceStatus);
procedure CtrlHandle(Code: DWORD);
function GetCtrlHandler: TServiceCtrlHandler;
procedure SetCtrlHandler(const Value: TServiceCtrlHandler);
public
/// this method is the main service entrance, from the OS point of view
// - it will call OnControl/OnStop/OnPause/OnResume/OnShutdown events
// - and report the service status to the system (via ReportStatus method)
procedure DoCtrlHandle(Code: DWORD); virtual;
/// Creates the service
// - the service is added to the internal registered services
// - main application must call the global ServicesRun procedure to actually
// start the services
// - caller must free the TService instance when it's no longer used
constructor Create(const aServiceName, aDisplayName: String); reintroduce; virtual;
/// free memory and release handles
destructor Destroy; override;
{{ Reports new status to the system }
function ReportStatus(dwState, dwExitCode, dwWait: DWORD): BOOL;
{{ Installs the service in the database
- return true on success
- create a local TServiceController with the current executable file,
with the supplied command line parameters}
function Install(const Params: string=''): boolean;
{{ Removes the service from database
- uses a local TServiceController with the current Service Name }
procedure Remove;
{{ Starts the service
- uses a local TServiceController with the current Service Name }
procedure Start;
{{ Stops the service
- uses a local TServiceController with the current Service Name }
procedure Stop;
{{ this is the main method, in which the Service should implement its run }
procedure Execute; virtual;
{{ Name of the service. Must be unique }
property ServiceName: String read fSName;
{{ Display name of the service }
property DisplayName: String read fDName write fDName;
{{ Type of service }
property ServiceType: DWORD read fServiceType write fServiceType;
{{ Type of start of service }
property StartType: DWORD read fStartType write fStartType;
{{ Number of arguments passed to the service by the service controler }
property ArgCount: Integer read GetArgCount;
{{ List of arguments passed to the service by the service controler }
property Args[Idx: Integer]: String read GetArgs;
{{ Current service status
- To report new status to the system, assign another
value to this record, or use ReportStatus method (better) }
property Status: TServiceStatus read fStatusRec write SetStatus;
{{ Any data You wish to associate with the service object }
property Data: DWORD read FData write FData;
{{ Whether service is installed in DataBase
- uses a local TServiceController to check if the current Service Name exists }
property Installed: boolean read GetInstalled;
{{ Callback handler for Windows Service Controller
- if handler is not set, then auto generated handler calls DoCtrlHandle }
property ControlHandler: TServiceCtrlHandler read GetCtrlHandler write SetCtrlHandler;
{{ Start event is executed before the main service thread (i.e. in the Execute method) }
property OnStart: TServiceEvent read fOnStart write fOnStart;
{{ custom Execute event
- launched in the main service thread (i.e. in the Execute method) }
property OnExecute: TServiceEvent read fOnExecute write fOnExecute;
{{ custom event trigerred when a Control Code is received from Windows }
property OnControl: TServiceControlEvent read fOnControl write fOnControl;
{{ custom event trigerred when the service is stopped }
property OnStop: TServiceEvent read fOnStop write fOnStop;
{{ custom event trigerred when the service is paused }
property OnPause: TServiceEvent read fOnPause write fOnPause;
{{ custom event trigerred when the service is resumed }
property OnResume: TServiceEvent read fOnResume write fOnResume;
{{ custom event trigerred when the service receive an Interrogate }
property OnInterrogate: TServiceEvent read fOnInterrogate write fOnInterrogate;
{{ custom event trigerred when the service is shut down }
property OnShutdown: TServiceEvent read fOnShutdown write fOnShutdown;
end;
var
/// the internal list of Services handled by this unit
// - not to be accessed directly: create TService instances, and they will
// be added/registered to this list
// - then run the global ServicesRun procedure
// - every TService instance is to be freed by the main application, when
// it's no more used
Services: TList = nil;
{{ launch the registered Services execution
- the registered list of service provided by the aplication is sent
to the operating system }
procedure ServicesRun;
{{ convert the Control Code retrieved from Windows into a service state
enumeration item }
function CurrentStateToServiceState(CurrentState: DWORD): TServiceState;
/// return the ready to be displayed text of a TServiceState value
function ServiceStateText(State: TServiceState): string;
implementation
{ TServiceController }
constructor TServiceController.CreateNewService(const TargetComputer,
DatabaseName, Name, DisplayName, Path, OrderGroup, Dependances, Username,
Password: String; DesiredAccess, ServiceType, StartType,
ErrorControl: DWORD);
begin
inherited Create;
FSCHandle := OpenSCManager(pointer(TargetComputer), pointer(DatabaseName),
SC_MANAGER_ALL_ACCESS);
if FSCHandle = 0 then Exit;
FHandle := CreateService(FSCHandle, pointer(Name), pointer(DisplayName),
DesiredAccess, ServiceType, StartType, ErrorControl, pointer(Path),
pointer(OrderGroup), nil, pointer(Dependances),
pointer(Username), pointer(Password));
end;
constructor TServiceController.CreateOpenService(const TargetComputer,
DataBaseName, Name: String; DesiredAccess: DWORD);
begin
inherited Create;
FSCHandle := OpenSCManager(pointer(TargetComputer), pointer(DatabaseName),
SC_MANAGER_ALL_ACCESS);
if FSCHandle = 0 then Exit;
FHandle := OpenService(FSCHandle, pointer(Name), DesiredAccess);
end;
function TServiceController.Delete: boolean;
begin
Result := FALSE;
if FHandle <> 0 then
if DeleteService(FHandle) then begin
Result := CloseServiceHandle(FHandle);
FHandle := 0;
end;
end;
destructor TServiceController.Destroy;
begin
if FHandle <> 0 then
CloseServiceHandle(FHandle);
if FSCHandle <> 0 then
CloseServiceHandle(FSCHandle);
inherited;
end;
function TServiceController.GetState: TServiceState;
begin
if (self=nil) or (FSCHandle=0) then
result := ssErrorRetrievingState else
if FHandle=0 then
result := ssNotInstalled else
result := CurrentStateToServiceState(Status.dwCurrentState);
end;
function TServiceController.GetStatus: TServiceStatus;
begin
FillChar(FStatus, Sizeof(FStatus), 0);
QueryServiceStatus(FHandle, FStatus);
Result := FStatus;
end;
function TServiceController.Pause: boolean;
begin
Result := ControlService(FHandle, SERVICE_CONTROL_PAUSE, FStatus);
end;
function TServiceController.Refresh: boolean;
begin
Result := ControlService(FHandle, SERVICE_CONTROL_INTERROGATE, FStatus);
end;
function TServiceController.Resume: boolean;
begin
Result := ControlService(FHandle, SERVICE_CONTROL_CONTINUE, FStatus);
end;
function TServiceController.Shutdown: boolean;
begin
Result := ControlService(FHandle, SERVICE_CONTROL_SHUTDOWN, FStatus);
end;
function StartService(hService: SC_HANDLE; dwNumServiceArgs: DWORD;
lpServiceArgVectors: Pointer): BOOL; stdcall; external advapi32
name {$ifdef UNICODE}'StartServiceW';{$else}'StartServiceA';{$endif}
function TServiceController.Start(const Args: array of PChar): boolean;
begin
Result := StartService(FHandle, length(Args), @Args[0]);
end;
function TServiceController.Stop: boolean;
begin
Result := ControlService(FHandle, SERVICE_CONTROL_STOP, FStatus);
end;
{ TService }
function FindServiceIndex(const Name: String): integer;
begin
if Services<>nil then
for result := 0 to Services.Count-1 do
if TService(Services[result]).ServiceName=Name then
exit;
result := -1;
end;
procedure JumpToService;
asm
{$ifdef CPU64}
{$ifdef CPUX64}
.NOFRAME
{$endif}
pop rax
mov rax,[rax]
mov rdx,[esp+8]
call TService.CtrlHandle
ret 8
{$else}
pop eax
mov eax, [eax] // retrieve self value
mov edx, [esp+4]
call TService.CtrlHandle
ret 4
{$endif}
end;
constructor TService.Create(const aServiceName, aDisplayName: String);
begin
if FindServiceIndex(aServiceName)>=0 then
raise Exception.CreateFmt('Attempt to install a service ' +
'with duplicated name: %s', [aServiceName]);
fSName := aServiceName;
fDName := aDisplayName;
if aDisplayName = '' then
fDName := aServiceName;
if Services=nil then begin
Services := TList.Create;
GarbageCollector.Add(Services);
end;
Services.Add(self);
fServiceType := SERVICE_WIN32_OWN_PROCESS or SERVICE_INTERACTIVE_PROCESS;
fStartType := SERVICE_AUTO_START;
fStatusRec.dwServiceType := fServiceType;
fStatusRec.dwCurrentState := SERVICE_STOPPED;
fStatusRec.dwControlsAccepted := 31;
fStatusRec.dwWin32ExitCode := NO_ERROR;
end;
procedure TService.CtrlHandle(Code: DWORD);
begin
DoCtrlHandle(Code);
end;
destructor TService.Destroy;
var i: integer;
begin
i := FindServiceIndex(fsName);
if i<0 then
raise Exception.CreateFmt('Cannot find service %s to remove from the list',
[fsName]);
Services.Delete(i);
if Assigned(fJumper) then
VirtualFree(fJumper, 0, MEM_RELEASE);
inherited;
end;
procedure TService.DoCtrlHandle(Code: DWORD);
begin
case Code of
SERVICE_CONTROL_STOP: begin
ReportStatus(SERVICE_STOP_PENDING, NO_ERROR, 0);
if Assigned(fOnStop) then fOnStop(Self);
ReportStatus(SERVICE_STOPPED, NO_ERROR, 0);
end;
SERVICE_CONTROL_PAUSE: begin
ReportStatus(SERVICE_PAUSE_PENDING, NO_ERROR, 0);
if Assigned(fOnPause) then fOnPause(Self);
ReportStatus(SERVICE_PAUSED, NO_ERROR, 0)
end;
SERVICE_CONTROL_CONTINUE: begin
ReportStatus(SERVICE_CONTINUE_PENDING, NO_ERROR, 0);
if Assigned(fOnResume) then fOnResume(Self);
ReportStatus(SERVICE_RUNNING, NO_ERROR, 0);
end;
SERVICE_CONTROL_SHUTDOWN: begin
if Assigned(fOnShutdown) then fOnShutdown(Self);
Code := 0;
end;
SERVICE_CONTROL_INTERROGATE: begin
SetServiceStatus(FStatusHandle, fStatusRec);
if Assigned(fOnInterrogate) then fOnInterrogate(Self);
end;
end;
if Assigned(fOnControl) then fOnControl(Self, Code);
end;
procedure TService.Execute;
begin
if Assigned(fOnStart) then
fOnStart(@Self);
ReportStatus(SERVICE_RUNNING, 0, 0);
SetServiceStatus(FStatusHandle, fStatusRec);
if Assigned(fOnExecute) then
fOnExecute(@Self);
end;
function TService.GetArgCount: Integer;
begin
result := length(FArgsList);
end;
function TService.GetArgs(Idx: Integer): String;
begin
if cardinal(Idx)>cardinal(high(FArgsList)) then
result := '' else // avoid GPF
result := FArgsList[Idx];
end;
function TService.GetCtrlHandler: TServiceCtrlHandler;
var AfterCallAddr: Pointer;
Offset: Integer;
begin
Result := fCtrlHandler;
if not Assigned(Result) then
begin
if fJumper=nil then begin
fJumper := VirtualAlloc(nil, 5+sizeof(Pointer), MEM_COMMIT, PAGE_EXECUTE_READWRITE);
if fJumper=nil then
raise Exception.CreateFmt('Cannot allocate memory for service jump gate: %s',
[fSName]);
AfterCallAddr := Pointer(PtrUInt(fJumper)+5);
Offset := PtrInt(@JumpToService)-PtrInt(AfterCallAddr);
fJumper[0] := $E8; // call opcode
PInteger(@fJumper[1])^ := Offset; // points to JumpToService
PPtrUInt(@fJumper[5])^ := PtrUInt(self); // will be set as EAX=self
end;
Result := pointer(fJumper);
end;
end;
function CurrentStateToServiceState(CurrentState: DWORD): TServiceState;
begin
case CurrentState of
SERVICE_STOPPED: result := ssStopped;
SERVICE_START_PENDING: result := ssStarting;
SERVICE_STOP_PENDING: result := ssStopping;
SERVICE_RUNNING: result := ssRunning;
SERVICE_CONTINUE_PENDING: result := ssResuming;
SERVICE_PAUSE_PENDING: result := ssPausing;
SERVICE_PAUSED: result := ssPaused;
else result := ssNotInstalled; // e.g. SERVICE_CONTROL_SHUTDOWN
end;
end;
{$ifdef NOSQLITE3COMMONS} // translated caption needed only if with full UI
function ServiceStateText(State: TServiceState): string;
var P: PShortString;
begin
P := GetEnumName(TypeInfo(TServiceState),ord(State));
result := string(copy(P^,3,length(P^)-2));
end;
{$else}
function ServiceStateText(State: TServiceState): string;
begin
result := PTypeInfo(TypeInfo(TServiceState))^.EnumBaseType^.GetCaption(State);
end;
{$endif}
function TService.GetInstalled: boolean;
begin
with TServiceController.CreateOpenService('','',fSName,SERVICE_QUERY_STATUS) do
try
result := Handle<>0;
finally
Free;
end;
end;
function TService.Install(const Params: string): boolean;
var schService: SC_HANDLE;
schSCManager: SC_HANDLE;
ServicePath: String;
begin
result := false;
if installed then
exit;
ServicePath := paramstr(0);
if Params<>'' then
ServicePath := ServicePath+' '+Params;
schSCManager := OpenSCManager(nil, nil, SC_MANAGER_ALL_ACCESS);
if (schSCManager>0) then begin
schService := CreateService(schSCManager,
pointer(fSName), pointer(fDName), SERVICE_ALL_ACCESS,
fServiceType, fStartType, SERVICE_ERROR_NORMAL,
pointer(ServicePath), nil, nil, nil, nil, nil);
if (schService>0) then begin
result := true;
CloseServiceHandle(schService);
end;
end;
end;
{procedure TService.MessageExecute(EndCode: DWORD);
var aMsg: TMsg;
begin
while fCurrentCode<>EndCode do begin
while PeekMessage(aMsg,0,0,0,PM_REMOVE) do begin
TranslateMessage(aMsg);
DispatchMessage(aMsg);
end;
if fCurrentCode=EndCode then
break;
WaitMessage;
end;
end;}
procedure TService.Remove;
begin
with TServiceController.CreateOpenService('','',fSName,SERVICE_ALL_ACCESS) do
try
if Handle=0 then exit;
Stop;
Delete;
finally
Free;
end;
end;
function TService.ReportStatus(dwState, dwExitCode, dwWait: DWORD): BOOL;
begin
if dwState = SERVICE_START_PENDING then
fStatusRec.dwControlsAccepted := 0 else
fStatusRec.dwControlsAccepted := 31;
fStatusRec.dwCurrentState := dwState;
fStatusRec.dwWin32ExitCode := dwExitCode;
fStatusRec.dwWaitHint := dwWait;
if (dwState = SERVICE_RUNNING) or (dwState = SERVICE_STOPPED) then
fStatusRec.dwCheckPoint := 0 else
inc(fStatusRec.dwCheckPoint);
Result := SetServiceStatus(FStatusHandle, fStatusRec);
end;
procedure TService.SetCtrlHandler(const Value: TServiceCtrlHandler);
begin
fCtrlHandler := Value;
if Assigned(fJumper) then
VirtualFree(fJumper, 0, MEM_RELEASE);
end;
procedure TService.SetStatus(const Value: TServiceStatus);
begin
fStatusRec := Value;
if FStatusHandle <> 0 then
SetServiceStatus(FStatusHandle, fStatusRec);
end;
procedure TService.Start;
begin
with TServiceController.CreateOpenService('','',fSName,SERVICE_ALL_ACCESS) do
try
Start([]);
finally
Free;
end;
end;
procedure TService.Stop;
begin
with TServiceController.CreateOpenService('','',fSName,SERVICE_ALL_ACCESS) do
try
Stop;
finally
Free;
end;
end;
{ function that a service process specifies as the entry point function
of a particular service. The function can have any application-defined name
- Args points to an array of pointers that point to null-terminated
argument strings. The first argument in the array is the name of the service,
and subsequent arguments are any strings passed to the service by the process
that called the StartService function to start the service. }
procedure ServiceProc(ArgCount: DWORD; Args: PPChar); stdcall;
var i: integer;
Srv: TService;
begin
i := FindServiceIndex(Args^);
if i<0 then
exit; // avoid any GPF
Srv := Services.Items[i];
for i := 1 to ArgCount-1 do begin
Inc(Args);
SetLength(Srv.FArgsList, length(Srv.FArgsList)+1);
Srv.FArgsList[high(Srv.FArgsList)] := Args^;
end;
Srv.FStatusHandle := RegisterServiceCtrlHandler(pointer(Srv.fSName), @Srv.ControlHandler);
if Srv.FStatusHandle = 0 then begin
Srv.ReportStatus(SERVICE_STOPPED, GetLastError, 0);
Exit;
end;
Srv.ReportStatus(SERVICE_START_PENDING, 0, 0);
Srv.Execute;
end;
procedure ServicesRun;
var S: array of TServiceTableEntry;
i: integer;
begin
SetLength(S,Services.Count+1);
for i := 0 to Services.Count-1 do begin
S[i].lpServiceName := pointer(TService(Services.List[i]).ServiceName);
S[i].lpServiceProc := @ServiceProc;
end;
StartServiceCtrlDispatcher(S[0]);
end;
end.