Inno Setup - prevent executing the installer multiple times simultaneously - inno-setup

I've got a bit of a pickle with Inno Setup: on a user machine, my installer was running slowly (something I've yet to diagnose, might be a problem specific with that computer, I still don't know). This lead to said user to run the installer again, while the first instance was still executing - and to my surprise, they both seemed to be running for a time, before crashing and burning...
I searched around but have not found any way to disable this behavior - most of my queries wound up on Inno Setup mutex feature, which is not really what I'm looking for. Anyone got tips on how to make sure there is only one instance / process of the installer executing? Thank you!

Since Inno Setup 5.5.6 you can use the SetupMutex directive:
[Setup]
AppId=MyProgram
SetupMutex=SetupMutex{#SetupSetting("AppId")}
If you want to change a text of the message, that displays when another installer is running already, use:
[Messages]
SetupAppRunningError=Setup has detected that %1 is currently running.%n%nPlease close all instances of it now, then click OK to continue, or Cancel to exit.
Before this version, there was no built-in mechanism available. But you could write your own pretty simply. Principle is that you create a unique mutex when the setup starts. But, as first you check if there is no such mutex already created. If so, you exit the setup, if not, you create the mutex:
[Setup]
AppName=My Program
AppVersion=1.5
DefaultDirName={pf}\My Program
[Code]
const
// this needs to be system-wide unique name of the mutex (up to MAX_PATH long),
// there is a discussion on this topic http://stackoverflow.com/q/464253/960757
// you can expand here e.g. the AppId directive and add it some 'salt'
MySetupMutex = 'My Program Setup 2336BF63-DF20-445F-AAE6-70FD7E2CE1CF';
function InitializeSetup: Boolean;
begin
// allow the setup to run only if there is no thread owning our mutex (in other
// words it means, there's no other instance of this process running), so allow
// the setup if there is no such mutex (there is no other instance)
Result := not CheckForMutexes(MySetupMutex);
// if this is the only instance of the setup, create our mutex
if Result then
CreateMutex(MySetupMutex)
// otherwise tell the user the setup will exit
else
MsgBox('Another instance is running. Setup will exit.', mbError, MB_OK);
end;

If your installer was called setup.exe for example, then you could use the following code to check if setup.exe is running and terminate the install.
[Code]
function IsAppRunning(const FileName : string): Boolean;
var
FSWbemLocator: Variant;
FWMIService : Variant;
FWbemObjectSet: Variant;
begin
Result := false;
FSWbemLocator := CreateOleObject('WBEMScripting.SWBEMLocator');
FWMIService := FSWbemLocator.ConnectServer('', 'root\CIMV2', '', '');
FWbemObjectSet := FWMIService.ExecQuery(Format('SELECT Name FROM Win32_Process Where Name="%s"',[FileName]));
Result := (FWbemObjectSet.Count > 0);
FWbemObjectSet := Unassigned;
FWMIService := Unassigned;
FSWbemLocator := Unassigned;
end;
function InitializeSetup: boolean;
begin
result := not IsAppRunning('setup.exe');
if not result then
MsgBox('setup.exe is already running', mbError, MB_OK);
end;

Related

Handling and customizing errors and messages in Inno Setup

Inno Setup will show different message boxes to the user.
For example,
When it tries to install a file that is in use, it will show a message to abort/ignore/cancel.
When the setup is installing the files and the target path will have low space while installing, the setup will give an error.
I need to customize these message boxes on my own. (I do not want these native messages to be shown to the user) for example, I need to show another message box, or even totally do not show a specific message from Inno Setup, or changing a label when that message is going to fire.
All Inno Setup messages can be customized using the [Messages] section.
Some examples:
How to modify error message in Inno Setup?
Show a custom message for unsupported architectures
How can I show my own message and then exit setup if the current version of Windows is not supported?
As for the change of the message box layout/design. You cannot really change it. With some fancy implementation of Check and BeforeInstall parameters, you might be able to catch some problems before Inno Setup detects them and handle them your custom way. But that's lot of work with unreliable results.
If you tell us what are you trying to achieve more specifically, you may get more specific answer.
If you need a solution that allows everything that Inno Setup allows on error, including clean abort of the installation, Check or BeforeInstall won't help as they do not have a way to cleanly abort it.
You would have to do all the checks before the installation, e.g. in CurStepChanged(ssInstall).
[Files]
Source: "MyProg.exe"; DestDir: "{app}"; Check: ShouldInstallFile
[Code]
var
DontInstallFile: Boolean;
function ShouldInstallFile: Boolean;
begin
Result := not DontInstallFile;
end;
procedure CurStepChanged(CurStep: TSetupStep);
var
FileName: string;
Msg: string;
Response: Integer;
begin
if CurStep = ssInstall then
begin
FileName := ExpandConstant('{app}\MyProg.exe');
repeat
if FileExists(FileName) and (not DeleteFile(FileName)) then
begin
Msg := Format('File %s cannot be replaced', [FileName]);
Response := MsgBox(Msg, mbError, MB_ABORTRETRYIGNORE)
case Response of
IDABORT: Abort;
IDIGNORE: DontInstallFile := True;
end;
end;
until (Response <> IDRETRY);
end;
end;

Is it possible to check if program is already running before trying to install it? (Inno Setup)

I'm using Inno Setup to create the installer. When I launch the installer I've created I need to check whether the program I'm trying to install is already running or not and if it is running then I have to show the corresponding message until I close it or exit.
Is it possible to do that?
If it is your application, make it create a mutex. Then you can use AppMutex directive.
[Setup]
AppMutex=MyProgMutex
If you cannot modify the application, you need to code the check for running application in Inno Setup. You can for example use IsAppRunning function from the answer by #RRUZ to How to check with Inno Setup, if a process is running at a Windows 2008 R2 64bit? in InitializeSetup event function.
function InitializeSetup(): Boolean;
var
Answer: Integer;
begin
Result := True;
while IsAppRunning('MyProg.exe') do
begin
Answer := MsgBox('Program is running, please close it', mbError, MB_OKCANCEL);
if Answer = IDCANCEL then
begin
Result := False
Exit;
end;
end;
end;
Based on a similar question on uninstaller:
Preparing to Uninstall like Preparing to Install Page - Inno Setup

Allow only one instance of Inno Setup without prompting

I need to allow only one instance of Inno Setup. I used SetupMutex, but when I run the second setup it will prompt the user. I need the setup do nothing and close without any prompt, if another instance is running.
I do not think that what you are trying to do is an improvement to a user experience, quite on the contrary, anyway...
Remove your SetupMutex directive and use this code instead:
[Code]
const
SetupMutexName = 'MyProgSetup';
function InitializeSetup(): Boolean;
begin
Result := True;
if CheckForMutexes(SetupMutexName) then
begin
Log('Mutex exists, setup is running already, silently aborting');
Result := False;
end
else
begin
Log('Creating mutex');
CreateMutex(SetupMutexName);
end;
end;
(There's a negligible chance for a race condition between CheckForMutexes and CreateMutex)

AppMutex with Inno Setup: Wait few seconds before prompt

Using Inno Setup together with an AppMutex works fine - when the setup is started and the mutex still exits, the user is prompted to close this application.
But following question:
Is there a way to tell Inno Setup to wait 2-3 seconds if the program closes itself before showing the user this prompt?
The reason is that I'm running the Inno Setup from the program itself for auto-update purpose. Directly after the setup file is executed the program closes itself, but obviously that takes too long (at least on some systems). So Inno Setup shows this - in this case - useless dialog to the user although the program is closing itself already.
Therefore I would like to accomplish that Inno Setup waits 2-3 seconds and only if the mutex still exists after that time it should show the prompt to the user.
Is there a way to accomplish this?
With such requirement, you cannot use the built-in AppMutex directive.
You have to implement the mutex check yourself using CheckForMutexes function in a loop, as you have been suggested to in your previous question:
[Code]
const
MutexName = 'MutexName';
function InitializeSetup: Boolean;
var
WaitInterval: Integer;
Wait: Integer;
begin
Wait := 3000;
WaitInterval := 250;
while (Wait > 0) and CheckForMutexes(MutexName) do
begin
Log('Application is still running, waiting');
Sleep(WaitInterval);
Wait := Wait - WaitInterval;
end;
while CheckForMutexes(MutexName) do
begin
if MsgBox(
FmtMessage(SetupMessage(msgSetupAppRunningError), ['MyApplication']),
mbError, MB_OKCANCEL) <> IDOK then
begin
Abort;
end;
end;
Result := True;
end;

How to call an exe when Inno Setup installation fails (within the installer itself)?

I've been using Inno Setup for several months now, but I'm struggling to find how to detect, from within the installer itself, an error that would cause Inno Setup to end with a non-zero exit code.
I've thought about using CurStepChanged with the ssDone step, or even DeinitializeSetup, but I can't find how to get access to the wizard's exit-code.
Did I miss something? There must be a way to do it...
I'd like to know if anything went wrong so that I can start a rollback procedure on the machine. Your proposed answer did the trick.
You cannot find out installer exit code from the Pascal Scripting.
If you want to detect that the installer failed, remember if CurStepChanged was called with ssDone and test that in DeinitializeSetup.
var
Succeeded: Boolean;
procedure DeinitializeSetup();
begin
if Succeeded then
begin
Log('Installation succeeded');
end
else
begin
Log('Installation failed');
end;
end;
procedure CurStepChanged(CurStep: TSetupStep);
begin
if CurStep = ssDone then
begin
Succeeded := True;
end;
end;
There are edge cases, when ssDone is used even, if the installer fails.
For example, when it fails because a machine was not restarted to complete the previous installation. In this case the CurStepChanged is not called with ssPostInstall. So you may want to check for both steps, if this scenario can happen in your installer.

Resources