Inno UninstallRun How To get global variable by executing - inno-setup

My target is to read a registry entry at [UninstallRun].
The issue is, if Inno Setup is running in an install process the Function GetRegistryVar() is called here ? It should be called when the UnInstall process is running. If I start the UnInstall process the Function GetRegistryVar() will not called here ? Only the CurUninstallStepChanged() and PGetRegistryVal(). So I can't receive my global variable gStrPrnName ?
[UninstallRun]
Filename: "{#dInst64bitDir}\{#dUnInstDrvExeName}"; Parameters: "-f {code:GetGlobalRegistryVar}"; WorkingDir: "{#dInst64bitDir}"; Flags: runhidden runascurrentuser;
[Code]
var
gStrPrnName: string;
function GetGlobalRegistryVar(Value: string): string;
begin
Result := gStrPrnName;
end;
procedure CurUninstallStepChanged(CurUninstallStep: TUninstallStep);
begin
case CurUninstallStep of
usUninstall:
begin
// ...insert code to perform pre-uninstall tasks here...
PGetRegistryVal(gStrPrnName);
end;
usPostUninstall:
begin
// ...insert code to perform post-uninstall tasks here...
end;
end;
end;
function PGetRegistryVal(Value: String): String;
var
strPrnName: string;
i: Integer;
begin
for i:=0 to 2 do
begin
if RegQueryStringValue(HKLM, 'SOFTWARE\Microsoft\Windows NT\CurrentVersion\GetDevice_1', 'Name', strPrnName) then
Result := strPrnName;
if RegQueryStringValue(HKLM, 'SOFTWARE\Microsoft\Windows NT\CurrentVersion\GetDevice_2', 'Name', strPrnName) then
Result := strPrnName;
...
end;
end;
Has somebody a solution for this issue ?
Thanks

Related

How to handle DotNet prerequisite with Inno Setup Install / DswinDs system?

I have an understanding now of how to use this DswinsHs for downloading a file (as we have it working for my help documentation).
But now I need to migrate some old code that optionally downloaded and installed Dot Net Framework.
Old Code
I have this code (used ISTool DLL):
const
// Changed to 4.6.2 download link (see: http://msdn.microsoft.com/en-us/library/ee942965%28v=vs.110%29.aspx#redist)
dotnetRedistURL = 'http://go.microsoft.com/fwlink/?LinkId=780600';
function PrepareToInstall(var NeedsRestart: Boolean): String;
var
IsInstalled: Cardinal;
begin
Result := '';
dotNetNeeded := true;
// Check for required netfx installation
// http://msdn.microsoft.com/en-us/library/hh925568%28v=vs.110%29.aspx#net_b
if(Is64BitInstallMode()) then begin
if (RegValueExists(HKLM, 'SOFTWARE\Wow6432Node\Microsoft\NET Framework Setup\NDP\v4\Full', 'Release')) then begin
RegQueryDWordValue(HKLM, 'SOFTWARE\Wow6432Node\Microsoft\NET Framework Setup\NDP\v4\Full', 'Release', IsInstalled);
if(IsInstalled >= 378675) then begin
dotNetNeeded := false;
downloadNeeded := false;
end;
end;
end
else begin
if (RegValueExists(HKLM, 'SOFTWARE\Microsoft\NET Framework Setup\NDP\v4\Full', 'Release')) then begin
RegQueryDWordValue(HKLM, 'SOFTWARE\Microsoft\NET Framework Setup\NDP\v4\Full', 'Release', IsInstalled);
if(IsInstalled >= 378675) then begin
dotNetNeeded := false;
downloadNeeded := false;
end;
end;
end;
if(dotNetNeeded) then begin
if (not IsAdminLoggedOn()) then begin
Result := ExpandConstant('{cm:DotNet_NeedAdminRights}');
end
else begin
dotnetRedistPath := ExpandConstant('{src}\NDP451-KB2858728-x86-x64-AllOS-ENU.exe');
if not FileExists(dotnetRedistPath) then begin
dotnetRedistPath := ExpandConstant('{tmp}\NDP451-KB2858728-x86-x64-AllOS-ENU.exe');
if not FileExists(dotnetRedistPath) then begin
isxdl_AddFile(dotnetRedistURL, dotnetRedistPath);
downloadNeeded := true;
end;
end;
if (downloadNeeded) then begin
if (MsgBox(ExpandConstant('{cm:DotNet_NeedToDownload}'), mbConfirmation, MB_OKCANCEL) = IDCANCEL) then begin
Result := ExpandConstant('{cm:DotNet_InstallAborted}');
end;
end;
end;
end;
// AJT v19.0.0 We always delete the existing local help file if it exists.
// The new version will be downloaded on the next wizard form if
// the user still wants the local help. ("downloadhelp" task selected).
if (bDownloadHelpDocSetup) then DoDeleteFile(ExpandConstant('{app}\CommunityTalks.chm'));
end;
Then I have:
procedure CurStepChanged(CurStep: TSetupStep);
var
hWnd: Integer;
ResultCode: Integer;
begin
if (CurStep = ssInstall) then
begin
hWnd := StrToInt(ExpandConstant('{wizardhwnd}'));
// Don't try to init isxdl if it's not needed because it will error on < ie 3
if (downloadNeeded) then begin
isxdl_SetOption('label', ExpandConstant('{cm:Downloading}'));
isxdl_SetOption('description', ExpandConstant('{cm:DownloadingInfo}'));
if (isxdl_DownloadFiles(hWnd) = 1) then begin
if (dotNetNeeded = true) then begin
if Exec(ExpandConstant(dotnetRedistPath), '/quiet', '',
SW_SHOW, ewWaitUntilTerminated, ResultCode) then begin
// handle success if necessary; ResultCode contains the exit code
if not (ResultCode = 0) then begin
// Microsoft present an array of options for this. But since
// The interface was visible I think it is safe to just say
// that the installation was not completed.
MsgBox(ExpandConstant('{cm:DotNet_InstallFailed}'), mbInformation, MB_OK);
Abort();
end;
end
else begin
// The execution failed for some reason
MsgBox(SysErrorMessage(ResultCode), mbInformation, MB_OK);
Abort();
end;
end;
end
else begin
// The user most likely cancelled the download of the file
MsgBox(ExpandConstant('{cm:DotNet_DownloadFailed}'), mbInformation, MB_OK);
Abort();
end;
end;
end;
end;
That needs changing.
How it is done with DswinsHs
For my download that does work with DwinsHs I basically have two bits as follows:
[Files] section:
; AJT v19.0.0 Download Help Documentation Setup file.
; This is associated with the "downloadhelp" task.
; It will be downloaded from the internet and deleted after install.
Source: "{tmp}\HelpDocSetup.exe"; \
DestDir: "{app}"; \
Flags: external deleteafterinstall; \
Tasks: downloadhelp; \
Check: DwinsHs_Check( ExpandConstant('{tmp}\HelpDocSetup.exe'), '{#HelpDocSetupURL}', \
'My_Setup', 'Get', {#HelpDocSetupFileSize}, 0 )
[Run] section:
; AJT v19.0.0 Installed the downloaded help documentation.
; This is only done if the "downloadhelp" task was selected.
Filename: "{app}\HelpDocSetup.exe"; \
Parameters: "/SP- /VERYSILENT /InstallPath=""{app}"""; \
WorkingDir: "{app}"; \
Flags: waituntilterminated runhidden; \
Description: "{cm:InstallingHelpDescription}"; \
StatusMsg: "{cm:InstallingHelpStatusMessage}"; \
Tasks: downloadhelp
The issue
I need to convert my previous code (DotNet prerequisite) into suitable file / run script lines (except this time I have to pass 0 for the file size as I do not know the size).
In short, my setup requires admin privileges, and technically, we need it to download and install dotnet (if it is not there) before continuing with the setup. The reason being we have these run entries:
Filename: "{dotnet40}\regasm.exe"; \
Parameters: "PTSTools_x86.dll /codebase"; \
WorkingDir: "{app}"; \
Flags: runhidden
Filename: "{dotnet4064}\regasm.exe"; \
Parameters: "PTSTools_x64.dll /codebase"; \
WorkingDir: "{app}"; \
Flags: runhidden; \
Check: IsWin64
Filename: "{dotnet40}\regasm.exe"; \
Parameters: "/u PTSTools_x86.dll"; \
WorkingDir: "{app}"; \
Flags: runhidden; \
Check: FileExists(ExpandConstant('{app}\PTSTools.dll')); \
AfterInstall: DoDeleteFile(ExpandConstant('{app}\PTSTools.dll'))
Filename: "{dotnet4064}\regasm.exe"; \
Parameters: "/u PTSTools.dll"; \
WorkingDir: "{app}"; \
Flags: runhidden; \
Check: IsWin64 and FileExists(ExpandConstant('{app}\PTSTools.dll')); \
AfterInstall: DoDeleteFile(ExpandConstant('{app}\PTSTools.dll'))
So having DotNet is a prerequisite for the installer to work. Should I be dealing with this differently?
Am I Doing This Right?
Based on the answer provided and my understanding of the documentation ...
Step 1
In PrepareToInstall we check to see if DotNet is needed and cache the result:
function PrepareToInstall(var NeedsRestart: Boolean): String;
var
IsInstalled: Cardinal;
begin
Result := '';
dotNetNeeded := true;
// Check for required netfx installation
// http://msdn.microsoft.com/en-us/library/hh925568%28v=vs.110%29.aspx#net_b
if(Is64BitInstallMode()) then begin
if (RegValueExists(HKLM, 'SOFTWARE\Wow6432Node\Microsoft\NET Framework Setup\NDP\v4\Full', 'Release')) then begin
RegQueryDWordValue(HKLM, 'SOFTWARE\Wow6432Node\Microsoft\NET Framework Setup\NDP\v4\Full', 'Release', IsInstalled);
if(IsInstalled >= 378675) then begin
dotNetNeeded := false;
end;
end;
end
else begin
if (RegValueExists(HKLM, 'SOFTWARE\Microsoft\NET Framework Setup\NDP\v4\Full', 'Release')) then begin
RegQueryDWordValue(HKLM, 'SOFTWARE\Microsoft\NET Framework Setup\NDP\v4\Full', 'Release', IsInstalled);
if(IsInstalled >= 378675) then begin
dotNetNeeded := false;
end;
end;
end;
if(dotNetNeeded) then begin
if (MsgBox(ExpandConstant('{cm:DotNet_NeedToDownload}'), mbConfirmation, MB_OKCANCEL) = IDCANCEL) then begin
Result := ExpandConstant('{cm:DotNet_InstallAborted}');
end;
end;
end;
Step 2
We add a BeforeDownload handler. This is where we get a chance to add the file we need to download to the list:
function BeforeDownload(): Boolean;
begin
if(dotNetNeeded) then
begin
dotNetRedistPath := ExpandConstant('{tmp}\NDP451-KB2858728-x86-x64-AllOS-ENU.exe');
DwinsHs_AppendRemoteFile( dotNetRedistPath, \
dotnetRedistURL, 'My_Setup', rmGet, FILESIZE_QUERY_SERVER );
end;
Result := True;
end;
Step 3
We add a AfterDownload handler. This is where we perform the install of DotNet.
procedure AfterDownload(State: Integer);
var
hWnd: Integer;
ResultCode: Integer;
begin
if (State = READ_OK) then
begin
if(dotNetNeeded) then
begin
if Exec(ExpandConstant(dotnetRedistPath), '/quiet', '',
SW_SHOW, ewWaitUntilTerminated, ResultCode) then begin
// handle success if necessary; ResultCode contains the exit code
if not (ResultCode = 0) then begin
// Microsoft present an array of options for this. But since
// The interface was visible I think it is safe to just say
// that the installation was not completed.
MsgBox(ExpandConstant('{cm:DotNet_InstallFailed}'), mbInformation, MB_OK);
Abort();
end;
end
else begin
// The execution failed for some reason
MsgBox(SysErrorMessage(ResultCode), mbInformation, MB_OK);
Abort();
end;
end;
end;
end;
I am not sure if "quiet" is the right way to go now ...
Step 4
We adjust the CurPageChanged handler:
procedure CurPageChanged(CurPage: Integer);
begin
DwinsHs_CurPageChanged(CurPage, #BeforeDownload, #AfterDownload);
end;
Just call DwinsHs_AppendRemoteFile when dotNetNeeded.
DwinsHs_AppendRemoteFile has basically the same arguments as DwinsHs_Check (DwinsHs_Check is actually only a Check-compatible wrapper around DwinsHs_AppendRemoteFile).
I believe that's all you need. The [Run] happens only after the download.

Inno Setup unzip file from input user

I try to unzip in installation file which I download from my repository. I found this code:
How to get Inno Setup to unzip a file it installed (all as part of the one installation process)
But I need to input from user in custom page about version of application in the repository, download it and try unzip. How send to value from input to ExtractMe('{tmp}\INPUT FROM USER VERSION.zip', '{app}\');
begin
{Page for input Version}
UserPage := CreateInputQueryPage(wpWelcome,
'Number of Version', 'example : 1.8.20',
'Program will download your input');
UserPage.Add('Version:', False);
UserPage.Values[0] := GetPreviousData('Version', '1.8.20');
end;
{Called when the user clicks the Next button}
function NextButtonClick(CurPageID: Integer): Boolean;
var
Version: string;
FileURL: string;
begin
if CurPageID = wpReady then
begin
Version := UserPage.Values[0];
{Download}
FileURL := Format('http://127.0.0.1/repository/ia/ats-apps/ia-client.zip/%s/ia-client.zip-%0:s.zip', [Version]); <-- FROM HERE TO BELOW
idpAddFile(FileURL, ExpandConstant(Format('{tmp}\%s.zip', [Version])));
idpDownloadAfter(wpReady);
end;
Result := True;
end;
procedure unzip(src, target: AnsiString);
external 'unzip#files:unzipper.dll stdcall delayload';
procedure ExtractMe(src, target : AnsiString);
begin
unzip(ExpandConstant(src), ExpandConstant(target));
end;
procedure CurStepChanged(CurStep: TSetupStep);
begin
if CurStep = ssPostInstall then
begin
ExtractMe('{tmp}\INPUT FROM USER VERSION.zip', '{app}\'); <--HERE
end;
end;
Thanks for tip.
The same way as you are already using in the NextButtonClick: Read the UserPage.Values[0].
ExtractMe(Format('{tmp}\%s.zip', [UserPage.Values[0]]), '{app}\');

Conditional reboot in innosetup installer [duplicate]

My Inno Setup script is used to install a driver. It runs my InstallDriver.exe after this file was copied during step ssInstall.
I need to ask the user to restart in some cases according to the value returned by InstallDriver.exe.
This means that I cannot put InstallDriver.exe in section [Run] because there's no way to monitor it's return value.
So I put it in function CurStepChanged() as follows:
procedure CurStepChanged(CurStep: TSetupStep);
var
TmpFileName, ExecStdout, msg: string;
ResultCode: Integer;
begin
if (CurStep=ssPostInstall) then
begin
Log('CurStepChanged(ssPostInstall)');
TmpFileName := ExpandConstant('{app}') + '\InstallDriver.exe';
if Exec(TmpFileName, 'I', '', SW_HIDE, ewWaitUntilTerminated, ResultCode) then .......
However, I can't find a way to make my script restart at this stage.
I thought of using function NeedRestart() to monitor the output of the driver installer, but it is called earlier in the process.
Does it make sense to call the driver installer from within NeedRestart()?
NeedRestart does not look like the right place to install anything. But it would work, as it's fortunately called only once. You will probably want to present a progress somehow though, as the wizard form is almost empty during a call to NeedRestart.
An alternative is to use AfterInstall parameter of the InstallDriver.exe or the driver binary itself (whichever is installed later).
#define InstallDriverName "InstallDriver.exe"
[Files]
Source: "driver.sys"; DestDir: ".."
Source: "{#InstallDriverName}"; DestDir: "{app}"; AfterInstall: InstallDriver
[Code]
var
NeedRestartFlag: Boolean;
const
NeedRestartResultCode = 1;
procedure InstallDriver();
var
InstallDriverPath: string;
ResultCode: Integer;
begin
Log('Installing driver');
InstallDriverPath := ExpandConstant('{app}') + '\{#InstallDriverName}';
if not Exec(InstallDriverPath, 'I', '', SW_HIDE, ewWaitUntilTerminated, ResultCode) then
begin
Log('Failed to execute driver installation');
end
else
begin
Log(Format('Driver installation finished with code %d', [ResultCode]))
if ResultCode = NeedRestartResultCode then
begin
Log('Need to restart to finish driver installation');
NeedRestartFlag := True;
end;
end;
end;
function NeedRestart(): Boolean;
begin
if NeedRestartFlag then
begin
Log('Need restart');
Result := True;
end
else
begin
Log('Do not need restart');
Result := False;
end;
end;

Use a part of a registry key/value in the Inno Setup script

I have a need to retrieve a path to be used for some stuffs in the installer according an other application previously installed on the system.
This previous application hosts a service and only provides one registry key/value hosting this information: HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\APPLICATION hosting the value ImagePath which Data is "E:\TestingDir\Filename.exe".
I need a way to only extract the installation path (E:\TestingDir) without the Filename.exe file.
Any suggestion?
thanks a lot
You can achieve this using a scripted constant.
You define a function that produces the value you need:
[Code]
function GetServiceInstallationPath(Param: string): string;
var
Value: string;
begin
if RegQueryStringValue(
HKEY_LOCAL_MACHINE, 'SYSTEM\CurrentControlSet\Services\APPLICATION',
'ImagePath', Value) then
begin
Result := ExtractFileDir(Value);
end
else
begin
Result := { Some fallback value }
end;
end;
And then you refer to it using {code:GetServiceInstallationPath} where you need it (like in the [Run] section).
For example:
[Run]
Filename: "{code:GetServiceIntallationPath}\SomeApp.exe"
Actually, you probably want to retrieve the value in InitializeSetup already, and cache the value in a global variable for use in the scripted constant. And abort the installation (by returning False from InitializeSetup), in case the other application is not installed (= the registry key does not exist).
[Code]
var
ServiceInstallationPath: string;
function InitializeSetup(): Boolean;
var
Value: string;
begin
if RegQueryStringValue(
HKEY_LOCAL_MACHINE, 'SYSTEM\CurrentControlSet\Services\APPLICATION',
'ImagePath', Value) then
begin
ServiceInstallationPath := ExtractFileDir(Value);
Log(Format('APPLICATION installed to %s', [ServiceInstallationPath]));
Result := True;
end
else
begin
MsgBox('APPLICATION not installed, aborting installation', mbError, MB_OK);
Result := False;
end;
end;
function GetServiceInstallationPath(Param: string): string;
begin
Result := ServiceInstallationPath;
end;
See also a similar question: Using global string script variable in Run section in Inno Setup.
Solved this way:
[code]
var
ServiceInstallationPath: string;
function MyProgCheck(): Boolean;
var
Value: string;
begin
if RegQueryStringValue(
HKEY_LOCAL_MACHINE, 'SYSTEM\ControlSet001\Services\JLR STONE VCATS TO MES',
'ImagePath', Value) then
begin
ServiceInstallationPath := ExtractFileDir(Value);
Result := True;
end
else
begin
Result := False;
end;
end;
and in the [RUN] section I put as check the TRUE condition or FALSE condition on this function according the needs...Thanks everybody answering!

How to force Inno Setup setup to fail when Run command fails?

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;
The [Run] section happens after installation is complete, so there's no rollback possible at that point, because it's already finalized.
However, what you can do is use AfterInstall in the [Files] section, after your .exe or whatever is required to execute your method. This runs before finalizing the installation, so canceling at this point does a rollback that removes all files.
If you combine that with the "CancelWithoutPrompt" from this answer you can do a rollback when running in interactive mode. Unfortunately, there doesn't seem to be a rollback for silent mode.
[Files]
Source: src\myapp.exe; DestDir: "{app}"; AfterInstall: RunMyAppCheck
[Code]
var CancelWithoutPrompt: boolean;
function InitializeSetup(): Boolean;
begin
CancelWithoutPrompt := false;
result := true;
end;
procedure CancelButtonClick(CurPageID: Integer; var Cancel, Confirm: Boolean);
begin
if CancelWithoutPrompt then
Confirm := false; { hide confirmation prompt }
end;
procedure RunMyAppCheck();
var
resultCode: Integer;
begin
Exec(ExpandConstant('{app}\myapp.exe'), '--verify --example-other-params',
'', SW_HIDE, ewWaitUntilTerminated, resultCode);
if resultCode <> 0 then
begin
SuppressibleMsgBox(
'MyApp failed, exit code ' + IntToStr(resultCode) + '. ' +
'Aborting installation.',
mbCriticalError, MB_OK, 0);
CancelWithoutPrompt := true;
WizardForm.Close;
end;
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.

Resources