Is it possible to exit the installation from a function in the [Code]
section of an installer created with Inno Setup?
I'm not interested in setting the exit code, what I want to do is perform a custom check for a requirement, and exit the installation if that requirement was not previously installed.
Go to Menu, Project, then Compile to compile and create the setup file. This will create a complete installer. Run the Setup and your application will be installed correctly. Innosetup offers an awesome alternative to create great looking Installers for free.
To compile an ISS file into an EXE file, open the ISS file in Inno Setup and then select Build → Compile from Inno Setup's menu bar.
Inno Setup does not support creating MSI installers.
To prevent the installer from running, when prerequisites test fails, just return False
from the InitializeSetup
. This will exit the installer even before the wizard shows.
function InitializeSetup(): Boolean;
begin
Result := True;
if not PrerequisitesTest then
begin
SuppressibleMsgBox('Prerequisites test failed', mbError, MB_OK, IDOK);
Result := False;
end;
end;
If you need to test prerequisites right before the installation starts only (i.e. the InitializeSetup
is too early), you can call the Abort
function from the CurStepChanged(ssInstall)
:
procedure CurStepChanged(CurStep: TSetupStep);
begin
if CurStep = ssInstall then
begin
if not PrerequisitesTest then
begin
SuppressibleMsgBox('Prerequisites test failed', mbError, MB_OK, IDOK);
Abort;
end;
end;
end;
Though for this scenario, consider using the PrepareToInstall
event function mechanism, instead of exiting the setup.
function PrepareToInstall(var NeedsRestart: Boolean): String;
begin
Result := '';
if not PrerequisitesTest then
begin
Result := 'Prerequisites test failed';
end;
end;
If you need to force terminate the installer any other time, use the ExitProcess
WinAPI call:
procedure ExitProcess(uExitCode: Integer);
external '[email protected] stdcall';
function NextButtonClick(CurPageID: Integer): Boolean;
begin
if CurPageID = wpReady then
begin
if not PrerequisitesTest then
begin
SuppressibleMsgBox('Prerequisites test failed', mbError, MB_OK, IDOK);
ExitProcess(1);
end;
end;
Result := True;
end;
Though this is rather unsafe exit, so use it only as the last resort approach. If you have any external DLL loaded, you might need to unload it first, to avoid crashes. This also does not cleanup the temporary directory.
This is a write up of what I fiddled out of my Inno 5.6.1 today and the sources you can find at https://github.com/jrsoftware/issrc [ref1]
[Code]
var _ImmediateInnoExit_was_invoked_flag: Boolean; // Inno/Pascal Script initializes all Boolean to False.
procedure ImmediateInnoExit();
var MainFormRef: TForm;
begin
_ImmediateInnoExit_was_invoked_flag := True;
try
MainFormRef := MainForm(); // calls GetMainForm() in Inno pascal code, which will raise an internal exception if the form is not yet initialized.
Log('INFO: ImmediateInnoExit: Calling MainForm.Close()!');
Log('NOTE: If the Event Fn CancelButtonClick is not coded to auto-Confirm, this will display the cancel dialog in the GUI case!');
Log('NOTE: Code will stall inside the Close() function while the Cancel confirmation dialog is displayed.');
MainFormRef.Close(); // this is only effective if the Wizard is visible, but we cann call it even when running siently (as long as the Wizard is initialized)
Log('NOTE: MainForm.Close() invoked. (If confirmed, setup will exit.)');
except
Log('INFO: ImmediateInnoExit did not resolve MainForm -> assuming we were call in an InitializeSetup() context before the Main form has been created!');
end;
Log('INFO: ImmediateInnoExit: Calling Abort() -> EAbort!');
Log('NOTE: Will exit the current scope.');
Log('NOTE: In GUI mode, it will just jump up to the Delphi event loop (and be ignored there). (But the WizardForm.Close() call should lead to exit!)');
Log('NOTE: In Silent Mode, it will be caught and exit the setup.');
Abort(); // Raise EAbort
end;
// This is called when the user clicks the cancel button or the [x] Close button
// the close/cancel can be invoked from code via WizardForm.Close!
procedure CancelButtonClick(CurPageID: Integer; var Cancel, Confirm: Boolean);
begin
Log(Format('IN: CancelButtonClick(%d <- Cancel=[%d], Confirm=[%d])', [CurPageID, Cancel, Confirm]));
Confirm := not _ImmediateInnoExit_was_invoked_flag; // if Confirm==False we don't get the dialog prompt.
Log(Format('IN: CancelButtonClick(%d -> [%d], [%d])', [CurPageID, Cancel, Confirm]));
end;
And now to what the point of the above code is:
Abort
The Inno docs for Abort
state:
Description: Escapes from the current execution path without reporting an error.
Abort raises a special "silent exception" which operates like any other exception, but does not display an error message to the end user.
Remarks:
Abort does not cause Setup or Uninstall to exit unless it's called from one of these event functions (or another function invoked by them):
InitializeSetup InitializeWizard CurStepChanged(ssInstall) InitializeUninstall CurUninstallStepChanged(usAppMutexCheck) CurUninstallStepChanged(usUninstall)
The reason the Abort function bevahes in this way is because, internally, Inno raises an EAbort
exception, and that exception is treated specially by the Delphi UI loop. Only in the functions listed, the Inno devs have either added special treatment for EAbort
(like in the case of CurStepChanged(ssInstall)
[ref2]), --
-- or the function os not called via the UI loop, like in the case of InitializeSetup
, which is called from the main program in Setup.dpr
, and any direct EAbort
is handled there specifically in the except
block there.
In all other Inno event function (e.g. NextButtonClick
etc.) the EAbort
exception will reach the main program/UI loop and be ignored there.
Which leads us nicely to:
/SILENT
(or /VERSILENT
)When Inno runs silently, it does not display the wizard form UI. The "Wizard" / Inno's progress is then not driven by the UI loop, but by WizardForm.ClickThroughPages
, which is invoked under same toplevel try/except
block as e.g. InitializeSetup
. [ref3]
Because of this, if Inno is being called silently, Abort()
will exit setup from every most [Code]
functions, and the list given in the docs for Abort
becomes moot if setup is being run silently.
To cancel the setup, the user can click the [Cancel]
button or the [X]
close button of the Setup Wizard.
In this case, Inno will invoke the callback function CancelButtonClick(CurPageID: Integer; var Cancel, Confirm: Boolean)
(if defined) and terminates setup, possible with an escape hatch dialog:
Called when the user clicks the Cancel button or clicks the window's Close button. The
Cancel
parameter specifies whether normal cancel processing should occur; it defaults toTrue
. TheConfirm
parameter specifies whether an "Exit Setup?" message box should be displayed;
User [Code]
can invoke the Cancel Button mechinism via calling WizardForm.Close()
, but this only works if Setup is displaying the Wizard Form, and doesn't work in silent mode.
WizardForm.Close
[ref4], or the click on the actual button, will eventually call TMainForm.FormCloseQuery
(in Main.pas), which will call CancelButtonClick
callbacks[ref5] and dependeing on Confirm
value, either call TerminateApp();
directly or first call the helper function ExitSetupMsgBox()
that will display the message box to the user.
.iss .pas
and .dpr
issrc\Projects\Main.pas
: TMainForm.Install
via SetStep(ssInstall, False);
and the except
block at the end of TMainForm.Install
where TerminateApp
is called.Setup.dpr
calling MainForm.InitializeWizard
from Main.pas
which calls WizardForm.ClickThroughPages
iff not InstallMode = imNormal
, i.e. in the silent case.WizardForm.Close()
internally calls MainForm.Close()
(see: TWizardForm.FormClose
)[Code]
: The global CancelButtonClick
procedure, and each Wizard page also has a OnCancelButtonClick: TWizardPageCancelEvent
that can be set.If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With