I have some commands in the [Run]
section of my Inno Setup script. Right now, if any of them returns a failure code (non-zero return value), the setup continues without any warning to the user. The desired behavior is to have the setup fail and roll back.
How do I enable this? I couldn't find any flag for the Run
entry that would force this behavior. Am I missing something?
As far as I'm concerned, you have to use [Code]
section for that, run the files with Exec
function, check ResultCode
upon return and run your uninstall script.
I did it this way:
Write error message (either abort confirmation message or just notification message) to temporary file {tmp}\install.error
using Inno Setup's BeforeInstall
parameter with SaveStringToUTF8File
procedure. You can use Inno Setup's constants, such as {cm:YourCustomMessage}
.
Use Windows command shell cmd.exe /s /c
to run desired program. Also use conditional execution of del
command with &&
- https://www.microsoft.com/resources/documentation/windows/xp/all/proddocs/en-us/ntcmds_shelloverview.mspx. So error message file would be deleted if command succeed (exit code 0). Please be aware of special quotes handling in cmd.exe /s /c
. Use code below as example.
Check existence of error message file {tmp}\install.error
using Inno Setup's AfterInstall
parameter with either ConfirmInstallAbortOnError
or NotifyInstallAbortOnError
procedures depending on error severity. They will abort install with proper notification or confirmation (and optional presenting of log file) and perform rollback using Exec(ExpandConstant('{uninstallexe}'), ...
ShouldAbortInstallation
global variable is used to keep status. Inno Setup's ShouldSkipPage(PageID: Integer)
function is used to hide final page. All commands in [Run]
section should use Check
parameter with CheckInstallationIsNotAborted
function. It will prevent their execution after failure at some point.
See example below. Hope this helps.
[CustomMessages]
InstallAbortOnErrorConfirmationMessage=An error has occurred during setup.%nAbort installation?
InstallAbortOnErrorNotificationMessage=An error has occurred during setup.%nInstallation will be aborted.
RunProgram1ErrorMsg=Post installation phase 1 failed. Should abort install?
RunProgram2ErrorMsg=Post installation phase 2 failed. Installation will be aborted. Please, contact tech support.
RunProgram1StatusMsg=Post installation phase 1 is in progress
RunProgram2StatusMsg=Post installation phase 2 is in progress
[Run]
; Write error text to file. Delete file on succeed. Abort installation if file exists after command execution.
Filename: "cmd.exe"; Parameters: "/s /c "" ""{app}\program1.exe"" /param1 /param2:""val2"" && del /F /Q ""{tmp}\install.error"" """; \
WorkingDir:"{app}"; Flags: runhidden; \
BeforeInstall: SaveStringToUTF8File('{tmp}\install.error', '{cm:RunProgram1ErrorMsg}', False); \
AfterInstall: ConfirmInstallAbortOnError('{tmp}\install.error', '{app}\logs\setup.log'); \
StatusMsg: "{cm:RunProgram1StatusMsg}"; \
Check: CheckInstallationIsNotAborted;
Filename: "cmd.exe"; Parameters: "/s /c "" ""{app}\program2.exe"" && del /F /Q ""{tmp}\install.error"" """; \
WorkingDir:"{app}"; Flags: runhidden; \
BeforeInstall: SaveStringToUTF8File('{tmp}\install.error', '{cm:RunProgram2ErrorMsg}', False); \
AfterInstall: NotifyInstallAbortOnError('{tmp}\install.error', '{app}\logs\setup.log'); \
StatusMsg: "{cm:RunProgram2StatusMsg}"; \
Check: CheckInstallationIsNotAborted;
[Code]
var
ShouldAbortInstallation: Boolean;
procedure SaveStringToUTF8File(const FileName, Content: String; const Append: Boolean);
var
Text: array [1..1] of String;
begin
Text[1] := Content;
SaveStringsToUTF8File(ExpandConstant(FileName), Text, Append);
end;
function LoadAndConcatStringsFromFile(const FileName: String): String;
var
Strings: TArrayOfString;
i: Integer;
begin
LoadStringsFromFile(FileName, Strings);
Result := '';
if High(Strings) >= Low(Strings) then
Result := Strings[Low(Strings)];
for i := Low(Strings) + 1 to High(Strings) do
if Length(Strings[i]) > 0 then
Result := Result + #13#10 + Strings[i];
end;
procedure ConfirmInstallAbortOnError(ErrorMessageFile, LogFileToShow: String);
var
ErrorCode: Integer;
ErrorMessage: String;
begin
ErrorMessageFile := ExpandConstant(ErrorMessageFile);
LogFileToShow := ExpandConstant(LogFileToShow);
Log('ConfirmInstallAbortOnError is examining file: ' + ErrorMessageFile);
if FileExists(ErrorMessageFile) then
begin
Log('ConfirmInstallAbortOnError: error file exists');
{ Show log file to the user }
if Length(LogFileToShow) > 0 then
ShellExec('', LogFileToShow, '', '', SW_SHOW, ewNoWait, ErrorCode);
ErrorMessage := LoadAndConcatStringsFromFile(ErrorMessageFile);
if Length(ErrorMessage) = 0 then
ErrorMessage := '{cm:InstallAbortOnErrorConfirmationMessage}';
if MsgBox(ExpandConstant(ErrorMessage), mbConfirmation, MB_YESNO) = IDYES then
begin
Log('ConfirmInstallAbortOnError: should abort');
ShouldAbortInstallation := True;
WizardForm.Hide;
MainForm.Hide;
Exec(ExpandConstant('{uninstallexe}'), '/SILENT', '', SW_HIDE,
ewWaitUntilTerminated, ErrorCode);
MainForm.Close;
end;
end;
Log('ConfirmInstallAbortOnError finish');
end;
procedure NotifyInstallAbortOnError(ErrorMessageFile, LogFileToShow: String);
var
ErrorCode: Integer;
ErrorMessage: String;
begin
ErrorMessageFile := ExpandConstant(ErrorMessageFile);
LogFileToShow := ExpandConstant(LogFileToShow);
Log('NotifyInstallAbortOnError is examining file: ' + ErrorMessageFile);
if FileExists(ErrorMessageFile) then
begin
Log('NotifyInstallAbortOnError: error file exists');
{ Show log file to the user }
if Length(LogFileToShow) > 0 then
ShellExec('', LogFileToShow, '', '', SW_SHOW, ewNoWait, ErrorCode);
ErrorMessage := LoadAndConcatStringsFromFile(ErrorMessageFile);
if Length(ErrorMessage) = 0 then
ErrorMessage := '{cm:InstallAbortOnErrorNotificationMessage}';
MsgBox(ExpandConstant(ErrorMessage), mbError, MB_OK);
Log('NotifyInstallAbortOnError: should abort');
ShouldAbortInstallation := True;
WizardForm.Hide;
MainForm.Hide;
Exec(ExpandConstant('{uninstallexe}'), '/SILENT', '', SW_HIDE,
ewWaitUntilTerminated, ErrorCode);
MainForm.Close;
end;
Log('NotifyInstallAbortOnError finish');
end;
function ShouldSkipPage(PageID: Integer): Boolean;
begin
Result := ShouldAbortInstallation;
end;
function CheckInstallationIsNotAborted(): Boolean;
begin
Result := not ShouldAbortInstallation;
end;
You can use the AfterInstall
flag in the Run
section to trigger the execution of your program and catch the result code.
See my answer here.
Then according to the result code you can cancel the installation.