AppMutex with Inno Setup: Wait few seconds before prompt - inno-setup

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;

Related

Inno Setup close already open application without user interaction [duplicate]

Referring to the question Basic or Advanced installation mode choice to skip or use advanced options pages, I need to skip the Preparing to Install wizard page now.
In my case this page is displayed because one or more programs are using files that need to be replaced by the installer; so the installer asks to the user if they want the setup to automatically close the applications and restart at the end of the setup.
I need that this page is hide from the setup process in Basic mode, and if some files are used, that the setup automatically closes the applications using them without asking anything to the user.
I've tried editing ShouldSkipPage as:
function ShouldSkipPage(PageID: Integer): Boolean;
begin
{ If "Basic" mode is selected, skip Directory and Components pages }
Result :=
ModePage.Values[0] and
((PageID = wpSelectDir) or (PageID = wpSelectComponents) or (PageID = wpReady) or (PageID = wpPreparing));
end;
adding (PageID = wpPreparing) but the page still displayed in Basic mode.
Is there a way to implement this using Inno Setup?
ShouldSkipPage event is not even called for wpPreparing. That page is not to be skipped.
If you still want to skip it, you have to use hacks like these:
How to skip all the wizard pages and go directly to the installation process?
Inno Setup - How to close finished installer after a certain time?
With the first approach, your code would look like:
[Code]
const
BN_CLICKED = 0;
WM_COMMAND = $0111;
CN_BASE = $BC00;
CN_COMMAND = CN_BASE + WM_COMMAND;
procedure CurPageChanged(CurPageID: Integer);
var
Param: Longint;
begin
{ If Basic mode is selected, skip Preparing page }
if (CurPageID = wpPreparing) and ModePage.Values[0] then
begin
Param := 0 or BN_CLICKED shl 16;
PostMessage(WizardForm.NextButton.Handle, CN_COMMAND, Param, 0);
end;
end;
Just don't do that. Ever. It is absolutely unacceptable for you to close an arbitrary list of applications without prompting the user. It's equally impolite to barrel ahead and then require a reboot at the end of the install. (It's unforgivable to then trigger the reboot without asking.)
What you can do is to put some code in the PrepareToInstall [Code] function which will automatically close your application. This executes before the user is prompted to close apps, so if it was only your apps involved then they will not be prompted.

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)

Inno Setup uninstaller: An attempt was made to access WizardForm before it has been created

I've a problem with WizardForm's, when I trying to uninstall the program I have this error:
Runtime Error:
Internal error: An attempt was made to access WizardForm before it has been created.
I need to create soft abort uninstallation process with loop (e.g. when application is running and user run the uninstall, program must check processes and if application is running, notify user and if user press the cancel button the program abort uninstallation), I've tried with ExitProcess(0); but it isn't gentle.
Code section:
procedure CurUninstallStepChanged(CurUninstallStep: TUninstallStep);
begin
case CurUninstallStep of
usUninstall:
begin
if MsgBox('Close the {#AppName}, before uninstallation.', mbConfirmation, MB_YESNO) = IDYES then
begin
{ user clicked Yes }
end
else
begin
MsgBox('Error',mbError,MB_OK);
CancelWithoutPrompt := true;
{ ExitProcess(0); }
WizardForm.close;
end
end;
end;
end;
Your approach to implementing your problem is wrong, see at the end.
Anyway, to address your immediate issue: The WizardForm is an installer form. It does not exist in an uninstaller. In the uninstaller, you have UninstallProgressForm. See documentation.
But you do not want to call UninstallProgressForm.Close. That's wrong for the reasons given below.
Inno Setup has a built-in mechanism to prevent an (un)installer from proceeding, while an application is running. The AppMutex directive.
Even if you want to build your own solution, use the InitializeUninstall event function, where you can exit the uninstaller easily and cleanly, by returning False. And you can, of course, display any message you like, before you exit. Just use MsgBox function.
All this is covered in my answer to Uninstall fails because program is running. How do I make Inno Setup check for running process prior to attempting delete?

Inno Setup - prevent executing the installer multiple times simultaneously

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;

Resources