I am trying to find a way of querying the status of a Windows service i.e. whether it is Running, Stopped or Disabled. I know I can use sc.exe query ServiceName
, but this would involve using a batch file to find the status in the output, piping to a file and then reading this in, which seems a little convoluted. I have found the following Windows API function on MSDN, which I believe will probably do what I want, but am not sure how or if this could be incorporated into Inno Setup. Alternatively, is there another method that could be used for returning the status of a Windows service natively with Inno Setup?
WinAPI is the best way you can choose to control services from Inno Setup so far. For your purpose is enough to use the QueryServiceStatus
function. It has been superseded by the Ex
version just to return things that you don't need for your task; it's not deprecated. The following code uses the lowest necessary access rights, so it can run even without admin elevation:
[Code]
#ifdef UNICODE
#define AW "W"
#else
#define AW "A"
#endif
const
SC_MANAGER_CONNECT = $0001;
SERVICE_QUERY_STATUS = $0004;
SERVICE_STOPPED = $00000001;
SERVICE_START_PENDING = $00000002;
SERVICE_STOP_PENDING = $00000003;
SERVICE_RUNNING = $00000004;
SERVICE_CONTINUE_PENDING = $00000005;
SERVICE_PAUSE_PENDING = $00000006;
SERVICE_PAUSED = $00000007;
type
TSCHandle = THandle;
TServiceStatus = record
dwServiceType: DWORD;
dwCurrentState: DWORD;
dwControlsAccepted: DWORD;
dwWin32ExitCode: DWORD;
dwServiceSpecificExitCode: DWORD;
dwCheckPoint: DWORD;
dwWaitHint: DWORD;
end;
function OpenService(hSCManager: TSCHandle; lpServiceName: string;
dwDesiredAccess: DWORD): TSCHandle;
external 'OpenService{#AW}@advapi32.dll stdcall';
function OpenSCManager(lpMachineName: string; lpDatabaseName: string;
dwDesiredAccess: DWORD): TSCHandle;
external 'OpenSCManager{#AW}@advapi32.dll stdcall';
function QueryServiceStatus(hService: TSCHandle;
out lpServiceStatus: TServiceStatus): BOOL;
external '[email protected] stdcall';
function CloseServiceHandle(hSCObject: TSCHandle): BOOL;
external '[email protected] stdcall';
function GetServiceState(const SvcName: string): DWORD;
var
Status: TServiceStatus;
Manager: TSCHandle;
Service: TSCHandle;
begin
// open service manager with the lowest required access rights for this task
Manager := OpenSCManager('', '', SC_MANAGER_CONNECT);
if Manager <> 0 then
try
// open service with the only required access right needed for this task
Service := OpenService(Manager, SvcName, SERVICE_QUERY_STATUS);
if Service <> 0 then
try
// and query service status
if QueryServiceStatus(Service, Status) then
Result := Status.dwCurrentState
else
RaiseException('QueryServiceStatus failed. ' + SysErrorMessage(DLLGetLastError));
finally
CloseServiceHandle(Service);
end
else
RaiseException('OpenService failed. ' + SysErrorMessage(DLLGetLastError));
finally
CloseServiceHandle(Manager);
end
else
RaiseException('OpenSCManager failed. ' + SysErrorMessage(DLLGetLastError));
end;
An example usage:
try
case GetServiceState('netman') of
SERVICE_STOPPED: MsgBox('The service is not running.', mbInformation, MB_OK);
SERVICE_START_PENDING: MsgBox('The service is starting.', mbInformation, MB_OK);
SERVICE_STOP_PENDING: MsgBox('The service is stopping.', mbInformation, MB_OK);
SERVICE_RUNNING: MsgBox('The service is running.', mbInformation, MB_OK);
SERVICE_CONTINUE_PENDING: MsgBox('The service continue is pending.', mbInformation, MB_OK);
SERVICE_PAUSE_PENDING: MsgBox('The service pause is pending.', mbInformation, MB_OK);
SERVICE_PAUSED: MsgBox('The service is paused.', mbInformation, MB_OK);
else
RaiseException('GetServiceState returned unknown state.');
end;
except
MsgBox(GetExceptionMessage, mbError, MB_OK);
end;
Or without reporting what has failed you can write a function like this:
function TryGetServiceState(const SvcName: string; out State: DWORD): Boolean;
var
Status: TServiceStatus;
Manager: TSCHandle;
Service: TSCHandle;
begin
Result := False;
Manager := OpenSCManager('', '', SC_MANAGER_CONNECT);
if Manager <> 0 then
begin
Service := OpenService(Manager, SvcName, SERVICE_QUERY_STATUS);
if Service <> 0 then
begin
if QueryServiceStatus(Service, Status) then
begin
Result := True;
State := Status.dwCurrentState;
end;
CloseServiceHandle(Service);
end;
CloseServiceHandle(Manager);
end;
end;
And possible usage:
var
State: DWORD;
begin
if TryGetServiceState('netman', State) then
begin
case State of
SERVICE_STOPPED: MsgBox('The service is not running.', mbInformation, MB_OK);
SERVICE_START_PENDING: MsgBox('The service is starting.', mbInformation, MB_OK);
SERVICE_STOP_PENDING: MsgBox('The service is stopping.', mbInformation, MB_OK);
SERVICE_RUNNING: MsgBox('The service is running.', mbInformation, MB_OK);
SERVICE_CONTINUE_PENDING: MsgBox('The service continue is pending.', mbInformation, MB_OK);
SERVICE_PAUSE_PENDING: MsgBox('The service pause is pending.', mbInformation, MB_OK);
SERVICE_PAUSED: MsgBox('The service is paused.', mbInformation, MB_OK);
else
MsgBox('GetServiceState returned unknown state.', mbError, MB_OK);
end;
end
else
MsgBox('Something failed during service state checking.', mbError, MB_OK);
end;
Alternatively, is there another method that could be used for returning the status of a Windows service natively with Inno Setup?
InnoSetup doesn't provide a native method to check the installation / run state of a service.
I have found the following Windows API function on MSDN, which I believe will probably do what I want, but am not sure how or if this could be incorporated into Inno Setup.
Yes, there is some old code flying around over at vincenzo.net which makes use of QueryServiceStatus()
. Feel free to adapt the code to your needs. I have not tested it.
[Code]
// function IsServiceInstalled(ServiceName: string) : boolean;
// function IsServiceRunning(ServiceName: string) : boolean;
// function InstallService(FileName, ServiceName, DisplayName, Description : string;ServiceType,StartType :cardinal) : boolean;
// function RemoveService(ServiceName: string) : boolean;
// function StartService(ServiceName: string) : boolean;
// function StopService(ServiceName: string) : boolean;
// function SetupService(service, port, comment: string) : boolean;
type
SERVICE_STATUS = record
dwServiceType : cardinal;
dwCurrentState : cardinal;
dwControlsAccepted : cardinal;
dwWin32ExitCode : cardinal;
dwServiceSpecificExitCode : cardinal;
dwCheckPoint : cardinal;
dwWaitHint : cardinal;
end;
HANDLE = cardinal;
const
SERVICE_QUERY_CONFIG = $1;
SERVICE_CHANGE_CONFIG = $2;
SERVICE_QUERY_STATUS = $4;
SERVICE_START = $10;
SERVICE_STOP = $20;
SERVICE_ALL_ACCESS = $f01ff;
SC_MANAGER_ALL_ACCESS = $f003f;
SERVICE_WIN32_OWN_PROCESS = $10;
SERVICE_WIN32_SHARE_PROCESS = $20;
SERVICE_WIN32 = $30;
SERVICE_INTERACTIVE_PROCESS = $100;
SERVICE_BOOT_START = $0;
SERVICE_SYSTEM_START = $1;
SERVICE_AUTO_START = $2;
SERVICE_DEMAND_START = $3;
SERVICE_DISABLED = $4;
SERVICE_DELETE = $10000;
SERVICE_CONTROL_STOP = $1;
SERVICE_CONTROL_PAUSE = $2;
SERVICE_CONTROL_CONTINUE = $3;
SERVICE_CONTROL_INTERROGATE = $4;
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;
// #######################################################################################
// nt based service utilities
// #######################################################################################
function OpenSCManager(lpMachineName, lpDatabaseName: string; dwDesiredAccess :cardinal): HANDLE;
external '[email protected] stdcall';
function OpenService(hSCManager :HANDLE;lpServiceName: string; dwDesiredAccess :cardinal): HANDLE;
external '[email protected] stdcall';
function CloseServiceHandle(hSCObject :HANDLE): boolean;
external '[email protected] stdcall';
function CreateService(hSCManager :HANDLE;lpServiceName, lpDisplayName: string;dwDesiredAccess,dwServiceType,dwStartType,dwErrorControl: cardinal;lpBinaryPathName,lpLoadOrderGroup: String; lpdwTagId : cardinal;lpDependencies,lpServiceStartName,lpPassword :string): cardinal;
external '[email protected] stdcall';
function DeleteService(hService :HANDLE): boolean;
external '[email protected] stdcall';
function StartNTService(hService :HANDLE;dwNumServiceArgs : cardinal;lpServiceArgVectors : cardinal) : boolean;
external '[email protected] stdcall';
function ControlService(hService :HANDLE; dwControl :cardinal;var ServiceStatus :SERVICE_STATUS) : boolean;
external '[email protected] stdcall';
function QueryServiceStatus(hService :HANDLE;var ServiceStatus :SERVICE_STATUS) : boolean;
external '[email protected] stdcall';
function QueryServiceStatusEx(hService :HANDLE;ServiceStatus :SERVICE_STATUS) : boolean;
external '[email protected] stdcall';
function GetLastError() : cardinal;
external '[email protected] stdcall';
function OpenServiceManager() : HANDLE;
begin
if UsingWinNT() = true then begin
Result := OpenSCManager('','',SC_MANAGER_ALL_ACCESS);
if Result = 0 then
MsgBox('the servicemanager is not available', mbError, MB_OK)
end
else begin
MsgBox('only nt based systems support services', mbError, MB_OK)
Result := 0;
end
end;
function IsServiceInstalled(ServiceName: string) : boolean;
var
hSCM : HANDLE;
hService: HANDLE;
begin
hSCM := OpenServiceManager();
Result := false;
if hSCM <> 0 then begin
hService := OpenService(hSCM,ServiceName,SERVICE_QUERY_CONFIG);
if hService <> 0 then begin
Result := true;
CloseServiceHandle(hService)
end;
CloseServiceHandle(hSCM)
end
end;
function InstallService(FileName, ServiceName, DisplayName, Description : string;ServiceType,StartType :cardinal) : boolean;
var
hSCM : HANDLE;
hService: HANDLE;
begin
hSCM := OpenServiceManager();
Result := false;
if hSCM <> 0 then begin
hService := CreateService(hSCM,ServiceName,DisplayName,SERVICE_ALL_ACCESS,ServiceType,StartType,0,FileName,'',0,'','','');
if hService <> 0 then begin
Result := true;
// Win2K & WinXP supports aditional description text for services
if Description<> '' then
RegWriteStringValue(HKLM,'System\CurrentControlSet\Services\' + ServiceName,'Description',Description);
CloseServiceHandle(hService)
end;
CloseServiceHandle(hSCM)
end
end;
function RemoveService(ServiceName: string) : boolean;
var
hSCM : HANDLE;
hService: HANDLE;
begin
hSCM := OpenServiceManager();
Result := false;
if hSCM <> 0 then begin
hService := OpenService(hSCM,ServiceName,SERVICE_DELETE);
if hService <> 0 then begin
Result := DeleteService(hService);
CloseServiceHandle(hService)
end;
CloseServiceHandle(hSCM)
end
end;
function StartService(ServiceName: string) : boolean;
var
hSCM : HANDLE;
hService: HANDLE;
begin
hSCM := OpenServiceManager();
Result := false;
if hSCM <> 0 then begin
hService := OpenService(hSCM,ServiceName,SERVICE_START);
if hService <> 0 then begin
Result := StartNTService(hService,0,0);
CloseServiceHandle(hService)
end;
CloseServiceHandle(hSCM)
end;
end;
function StopService(ServiceName: string) : boolean;
var
hSCM : HANDLE;
hService: HANDLE;
Status : SERVICE_STATUS;
begin
hSCM := OpenServiceManager();
Result := false;
if hSCM <> 0 then begin
hService := OpenService(hSCM,ServiceName,SERVICE_STOP);
if hService <> 0 then begin
Result := ControlService(hService,SERVICE_CONTROL_STOP,Status);
CloseServiceHandle(hService)
end;
CloseServiceHandle(hSCM)
end;
end;
function IsServiceRunning(ServiceName: string) : boolean;
var
hSCM : HANDLE;
hService: HANDLE;
Status : SERVICE_STATUS;
begin
hSCM := OpenServiceManager();
Result := false;
if hSCM <> 0 then begin
hService := OpenService(hSCM,ServiceName,SERVICE_QUERY_STATUS);
if hService <> 0 then begin
if QueryServiceStatus(hService,Status) then begin
Result :=(Status.dwCurrentState = SERVICE_RUNNING)
end;
CloseServiceHandle(hService)
end;
CloseServiceHandle(hSCM)
end
end;
My suggestion is to put the code into an external iss file (service.iss) and then #include it. This will keep the code section of the installer itself a bit cleaner.
Usage:
function InitializeSetup(): boolean;
begin
if IsServiceInstalled('myservice') = false then begin
if InstallService('c:\winnt\system32\myservice.exe','myservice','my service','my service is doing usefull things',SERVICE_WIN32_OWN_PROCESS,SERVICE_AUTO_START) = true then begin
StartService('myservice');
StopService('myservice');
// after stopping a service you should wait some seconds before removing
RemoveService('myservice');
// otherwise removing can fail
end
end
else if IsServiceRunning('myservice') then
MsgBox('myservice is running',mbInformation, MB_OK);
Result := false
end;
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With