Inno Setup for Windows Service

Inno Setup: Installing Windows services using sc create

I used this code and both of my services are installing and uninstalling:

[run]
Filename: {sys}\sc.exe; Parameters: "create mysrv start= auto binPath= ""{app}\mysrv.exe""" ; Flags: runhidden

[UninstallRun]
Filename: {sys}\sc.exe; Parameters: "stop mysrv" ; Flags: runhidden
Filename: {sys}\sc.exe; Parameters: "delete mysrv" ; Flags: runhidden

This solved my problem, so why should I use Pascal in this case.?

Inno Setup for Windows service?

You don't need installutil.exe and probably you don't even have rights to redistribute it.

Here is the way I'm doing it in my application:

using System;
using System.Collections.Generic;
using System.Configuration.Install;
using System.IO;
using System.Linq;
using System.Reflection;
using System.ServiceProcess;
using System.Text;

static void Main(string[] args)
{
if (System.Environment.UserInteractive)
{
string parameter = string.Concat(args);
switch (parameter)
{
case "--install":
ManagedInstallerClass.InstallHelper(new string[] { Assembly.GetExecutingAssembly().Location });
break;
case "--uninstall":
ManagedInstallerClass.InstallHelper(new string[] { "/u", Assembly.GetExecutingAssembly().Location });
break;
}
}
else
{
ServiceBase.Run(new WindowsService());
}
}

Basically you can have your service to install/uninstall on its own by using ManagedInstallerClass as shown in my example.

Then it's just matter of adding into your InnoSetup script something like this:

[Run]
Filename: "{app}\MYSERVICE.EXE"; Parameters: "--install"

[UninstallRun]
Filename: "{app}\MYSERVICE.EXE"; Parameters: "--uninstall"

Stop a Windows service on re-installation and uninstallation in Inno Setup

Inno Setup reports that, only if you set AppMutex directive.

If you do not want the installer to detect that the application (service) is running, do not set the directive.

If you want to stop the application (service) before the detection, you can do it in InitializeSetup event function in installer, and using InitializeUninstall in uninstaller. Though note that making changes to user's machine before the user confirms anything, is not the right thing to do.

Inno Setup query Windows service status

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 'QueryServiceStatus@advapi32.dll stdcall';
function CloseServiceHandle(hSCObject: TSCHandle): BOOL;
external 'CloseServiceHandle@advapi32.dll 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;

How to install windows service using Inno Setup?

You just need the *.exe files.

*.pbd and other are not needed there.

Only DLLs if nedded by your main project (service).

Install a service on Windows (Vista/XP/7) using Inno Setup

  1. Use sc.exe, It is simple, the only drawback is you may have to intercept and parse output to know what went wrong, if it did.
  2. Use my Inno Setup service library. It's a Pascal Script wrapper over the SCM API, and let you to control the services fully. A little more complex to use, but it allows for full error checking and handling. There are some higher level functions designed to display errors in standard suppressible Inno Setup dialog boxes.

PS: don't install your service in any of the Windows systems folder. They should be regarded as Windows private folders. Unless you have very, very good reasons to write there (i.e. drivers), you should never install software there. Install it in your application folders.



Related Topics



Leave a reply



Submit