Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Inno Setup query Windows service status

Tags:

inno-setup

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?

like image 1000
Robert Wigley Avatar asked Sep 08 '15 17:09

Robert Wigley


2 Answers

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;
like image 179
TLama Avatar answered Nov 04 '22 14:11

TLama


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;
like image 4
Jens A. Koch Avatar answered Nov 04 '22 14:11

Jens A. Koch