InstallShield seems ubiquitous when it comes to installer scripting, at least based on my experience with companies I’ve worked for so far. But for my side projects, I’ve always been a fan of InnoSetup. It is simple to use and Pascal is not really that bad. It is free and can do probably all the things you require for an installer.
Here’s an example of an InnoSetup script that I have been using as baseline for creating installers for Windows.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
; Defines | |
#define MyAppVersion "1.0.0" | |
#define MyAppVersionInfoVersion "1.0.0" | |
#define MyAppName "My Superb App That Will Save The World" | |
#define MyAppPublisher "Superb, Inc." | |
#define MyAppURL "http://www.a-very-superb-url-to-my-app.com/" | |
#define MyAppCopyright "Copyright (C) Copyright 3056. All rights reserved." | |
#define MyAppId "{{88C6A6D9-324C-46E8-BA87-563D14021442}" | |
#define MyAppPath "{pf}\Superb\WorldSavingApp" | |
[Setup] | |
AlwaysRestart = No | |
UninstallRestartComputer = No | |
CloseApplications = No | |
RestartApplications = No | |
SetupIconFile = .\logo.ico | |
DisableReadyPage = Yes | |
UninstallDisplayName = {#MyAppName} | |
UninstallDisplayIcon = {app}\logo.ico | |
ArchitecturesInstallIn64BitMode = x64 | |
ArchitecturesAllowed = x64 | |
AppId = {#MyAppId} | |
AppName = {#MyAppName} | |
AppVerName = {#MyAppName} {#MyAppVersion} | |
AppVersion = {#MyAppVersion} | |
AppPublisher = {#MyAppPublisher} | |
DefaultDirName = {#MyAppPath} | |
DisableDirPage = Yes | |
DefaultGroupName = Superb | |
DisableProgramGroupPage = Yes | |
OutputDir = Installer | |
OutputBaseFilename = SuperbWorldSavingApp_{#MyAppVersion} | |
SetupLogging = Yes | |
SolidCompression = Yes | |
VersionInfoDescription = {#MyAppName} | |
VersionInfoCopyRight = {#MyAppCopyright} | |
VersionInfoProductVersion = {#MyAppVersion} | |
VersionInfoProductName = {#MyAppName} | |
VersionInfoVersion = {#MyAppVersionInfoVersion} | |
VersionInfoCompany = {#MyAppPublisher} | |
VersionInfoTextVersion = {#MyAppVersion} | |
[Languages] | |
Name: english; MessagesFile: compiler:Default.isl | |
[Files] | |
; x64 | |
Source: .\bin\x64\Release\superb.exe; DestDir: {code:InstallDir}; Flags: ignoreversion restartreplace uninsrestartdelete | |
Source: .\bin\x64\Release\superb.exe.config; DestDir: {code:InstallDir}; Flags: ignoreversion restartreplace uninsrestartdelete | |
Source: .\logo.ico; DestDir: {code:InstallDir}; Flags: ignoreversion restartreplace uninsrestartdelete | |
[Icons] | |
[Registry] | |
[Run] | |
; Install app. | |
Filename: {code:InstallDir}\superb.exe; Parameters: --install; Flags: waituntilterminated runhidden | |
Filename: cmd.exe; Parameters: /c net start superb; Flags: waituntilterminated runhidden | |
[UninstallRun] | |
; Uninstall service | |
Filename: cmd.exe; Parameters: /c net stop superb; Flags: waituntilterminated runhidden | |
Filename: {code:InstallDir}\superb.exe; Parameters: --uninstall; Flags: waituntilterminated runhidden | |
[Dirs] | |
Name: {app}; Flags: uninsalwaysuninstall | |
[InstallDelete] | |
[UninstallDelete] | |
Type: filesandordirs; Name: {code:InstallDir}\InstallUtil.InstallLog | |
Type: filesandordirs; Name: {code:InstallDir}\superb.InstallLog | |
[Code] | |
var | |
strInstallPath: String; | |
bInstallPathCached: Boolean; | |
strCheckSumBefore: String; | |
strCheckSumAfter: String; | |
bInstallSuccess: Boolean; | |
// Return install directory (default or custom). Caller can specify destination directory via /DIR="<dir>" paramerter to setup.exe installer. | |
function InstallDir(param: String): String; | |
var | |
strParamTail: String; | |
strDirTail: String; | |
strPfEnv: String; | |
strInstallDirTemp: String; | |
begin | |
if bInstallPathCached = false then | |
begin | |
strInstallPath := '{#SetupSetting("DefaultDirName")}'; | |
strPfEnv := ExpandConstant('{%ProgramFiles}') | |
StringChange(strInstallPath, '{pf}', strPfEnv); | |
// Do not use ..(x86) dir in x64 bit systems by default. Note that this will have no meaning if user will input | |
// custom path in installation. | |
StringChange(strInstallPath, ' (x86)', ''); | |
strParamTail := GetCmdTail(); | |
strDirTail := Copy(strParamTail, Pos('/DIR=', strParamTail), Length(strParamTail)); | |
if Pos('/DIR=', strDirTail) = 1 then | |
begin | |
strInstallPath := ''; | |
strInstallDirTemp := Copy(strDirTail, 7, Length(strDirTail)); | |
strInstallPath := Copy(strInstallDirTemp, 1, Pos('"', strInstallDirTemp) - 1); | |
end; | |
bInstallPathCached := true; | |
end; | |
result := strInstallPath; | |
end; | |
// Called during Setup's initialization. Return False to abort Setup, True otherwise. Note: This is an override function. | |
function InitializeSetup(): Boolean; | |
begin | |
result := true; | |
exit; | |
// strError := SetupMessage(msgWinVersionTooLowError); | |
// StringChange(strError, '%1', 'Windows'); | |
// StringChange(strError, '%2', '8'); | |
// SuppressibleMsgBox(strError, mbInformation, MB_OK, MB_OK); | |
result := false; | |
end; | |
// Note: This is an override function. | |
// | |
// PrepareToInstall() override function ensures that all processes are stopped before starting installation. And for some | |
// reasons that installation will not succeed (or cancelled), we need to restart processes as well. We do it here. | |
procedure DeinitializeSetup(); | |
var | |
resultCode: Integer; | |
begin | |
if bInstallSuccess = false then | |
begin | |
Exec(ExpandConstant('{sys}\net.exe'), 'start superb', '', SW_HIDE, ewWaitUntilTerminated, resultCode); | |
end; | |
end; | |
// Note: This is an override function. | |
// | |
// Quit applications that needs to be overwritten during installation. Added to support upgrade to InnoSetup 5.5.3. Return | |
// emptry string for success, error string to cancel and display returned string. | |
function PrepareToInstall(var NeedsRestart: Boolean): String; | |
var | |
resultCode: Integer; | |
strPrep: String; | |
begin | |
strPrep := SetupMessage(msgWizardPreparing) + '...'; | |
WizardForm.StatusLabel.Caption := strPrep; | |
Exec(ExpandConstant('{sys}\net.exe'), 'stop superb', '', SW_HIDE, ewWaitUntilTerminated, resultCode); | |
result := ''; | |
end; | |
// You can use this event function to perform your own pre-install and post-install tasks. Called with CurStep=ssInstall | |
// just before the actual installation starts, with CurStep=ssPostInstall just after the actual installation finishes, | |
// and with CurStep=ssDone just before Setup terminates after a successful install. | |
// Note: This is an override function. | |
procedure CurStepChanged(CurStep: TSetupStep); | |
var | |
resultCode: Integer; | |
strPrep: String; | |
bModifyRestart: Boolean; | |
begin | |
if CurStep = ssInstall then | |
begin | |
// Pre-install phase | |
strPrep := SetupMessage(msgWizardPreparing) + '...'; | |
WizardForm.StatusLabel.Caption := strPrep; | |
strCheckSumBefore := MakePendingFileRenameOperationsChecksum; | |
end | |
else if CurStep = ssPostInstall then | |
begin | |
// Post-install phase | |
strCheckSumAfter := MakePendingFileRenameOperationsChecksum; | |
end | |
else if CurStep = ssDone then | |
begin | |
// Before setup terminates after a successful install. | |
strCheckSumAfter := MakePendingFileRenameOperationsChecksum; | |
bModifyRestart := false; | |
if strCheckSumAfter = strCheckSumBefore then | |
begin | |
// Only do this when InnoSetup will not require the system to be restarted. | |
Exec(ExpandConstant('{sys}\net.exe'), 'start superb', '', SW_HIDE, ewWaitUntilTerminated, resultCode); | |
bInstallSuccess := true; | |
end | |
else | |
begin | |
bModifyRestart := true; | |
end; | |
end; | |
end; | |
// You can use this event function to perform your own pre-uninstall and post-uninstall tasks. CurUninstallStep = ssInstall | |
// during uninstallation phase then CurUninstallStep = usPostUninstall after successful uninstallation. | |
// Note: This is an override function. | |
procedure CurUninstallStepChanged(CurUninstallStep: TUninstallStep); | |
var | |
resultCode: Integer; | |
begin | |
if CurUninstallStep = usUninstall then | |
begin | |
// Uninstall phase | |
Exec(ExpandConstant('{sys}\net.exe'), 'stop superb', '', SW_HIDE, ewWaitUntilTerminated, resultCode); | |
end | |
else if CurUninstallStep = usPostUninstall then | |
begin | |
// Post-uninstall phase | |
; | |
end | |
else if CurUninstallStep = usDone then | |
begin | |
// Uninstallation done phase | |
; | |
end; | |
end; |