How do I know if a restart is needed in InnoSetup script? - inno-setup

TL;DR version:
In an InnoSetup script, how can I detect if a restart is needed because of files that were in use?
More detailed version:
I have an Inno Setup script with the following characteristics:
the ShouldSkipPage function is implemented so that all pages (except the welcome page) are skipped unless a custom "Advanced options" checkbox on the welcome page is checked:
function ShouldSkipPage(PageID: Integer): Boolean;
begin
if ((PageID = wpSelectDir) or
(PageID = wpSelectProgramGroup) or
(PageID = wpSelectTasks) or
(PageID = wpFinished) or
(PageID = wpReady)) then
begin
Result := not advancedCheckBox.Checked;
end;
end;
CloseApplications and RestartApplications are set to false (*), and some files have the restartreplace and uninsrestartdelete flags, so a restart will be required to complete the installation if the files were in use
Now, if a restart is needed, I want to show the Finished page regardless of the state of the "Advanced options" checkbox, because I don't want to cause a restart without prompting the user. So my code would be something like that:
function ShouldSkipPage(PageID: Integer): Boolean;
begin
if ((PageID = wpSelectDir) or
(PageID = wpSelectProgramGroup) or
(PageID = wpSelectTasks) or
(PageID = wpReady)) then
begin
Result := not advancedCheckBox.Checked;
end
else if ((PageID = wpFinished)) then
begin
Result := (not advancedCheckBox.Checked) and (not IsRestartNeeded);
end
end;
Unfortunately, there is no IsRestartNeeded function (NeedRestart exists, but it's an event function). I spent a long time looking at the documentation, but I didn't find any function that could give me this information.
The only option I can think of is to look at HKLM\System\CurrentControlSet\Control\Session Manager\PendingFileRenameOperations to see if it contains any of my files, but it's a rather ugly solution...
(*) The files I want to replace or remove are a shell extension and some DLLs used by this extension. The reason why I'm not relying on the Restart Manager is because it doesn't seem to work with explorer.exe: the process is immediately restarted, and my files are locked again.

The usual recommendation is to call MakePendingFileRenameOperationsChecksum near the start of your installation process, and then again whenever you want to check whether a restart will be required. As long as it keeps returning the same value, a restart is not required.
Note that this won't take into account "forced restarts" eg. from you implementing NeedRestart and returning true or from a component marked with the restart flag; you're expected to be able to figure that out on your own, since you're in control of that.

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.

How does the ShouldSkipPage procedure actually work in Inno Setup? [duplicate]

I've noticed, that ShouldSkipPage is called twice per each page - before page is actually shown, and after. You can easily check this, by simply adding Log to function:
function ShouldSkipPage(PageID: Integer): Boolean;
begin
if PageID = wpSelectDir then
Log('ShouldSkipPage for SelectDir was called');
Result := false;
end;
You will see logged message twice (and by executing script in compiler, you can see, that second call occurs after page was shown).
So, can someone explain, why it is called second time, already after page was shown? This makes no sense, and may be confusing and even lead to unexpected deviations in installer logic.
Also, is there any way to prevent second call?
The first call (or actually the first set of calls) is to find the next page to display.
The second call (or actually the second set of calls) is to find, if there's any page to return to (to decide if the Back button should be visible).
This way you can e.g. prevent a user to return back once a certain page is reached.
In general the ShouldSkipPage event function could be called any number of times and at any time. And your code must be able to handle that.
If you want to do special processing before and after a page is changed, use the NextButtonClick/BackButtonClick and the CurPageChanged, not the ShouldSkipPage.
The following example shows, how to prevent a user from modifying an installation, once the "Ready to Install" page is reached:
function ShouldSkipPage(PageID: Integer): Boolean;
begin
Result := False;
if (WizardForm.CurPageID >= wpReady) and (PageID < wpReady) then
begin
Result := True;
end;
end;
There won't be any Back button on the "Ready to Install" page:

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 - 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;

Inno Setup skip memo page if empty

I am using the Modular Dependencies scripts found here : http://www.codeproject.com/Articles/20868/NET-Framework-Installer-for-InnoSetup to install .Net and VC++.
Now, it's all well and good, and has allowed me to reduce my installer size by about 6MB.
But I want to provide the user with the fastest setup possible, and when the user already has all the necessary dependencies, he is presented with an empty "memo" screen. That's really less than optimal, and I'd wish to at least skip this step if there is nothing.
I can test if there is any component to install using if (GetArrayLength(products) = 0) then, but I can't find out where to place this test.
In the InitializeSetup function I can't find how to tell "skip this screen", and in the ShouldSkipPage function, I can't find how to tell "skip the Memo page".
Thanks !
It might be this way:
[Code]
function ShouldSkipPage(PageID: Integer): Boolean;
begin
// skip page when we are on the ready page and the memo is empty
Result := (PageID = wpReady) and (WizardForm.ReadyMemo.Text = '');
end;

Resources