Setting task value based on active file type associations when upgrading - windows

I have the following tasks as part of my installer:
[Tasks]
Name: register32; Description: "Meeting Schedule Assistant (32 bit)"; \
GroupDescription: "{cm:FileAssociations}"; flags: unchecked exclusive;
Name: register64; Description: "Meeting Schedule Assistant (64 bit)"; \
GroupDescription: "{cm:FileAssociations}"; Check: IsWin64; Flags: exclusive;
By design, Inno Setup has UsePreviousTasks set to Yes. However, my software installs both bit editions and the user may subsequently override the installer default via application settings.
Therefore, when my installer is upgrading, can it determine which bit edition is actively registered and leave it set as that value?

Based on your previous question, I know that your registrations look like:
[HKEY_CLASSES_ROOT\MeetSchedAssist.MWB\Shell\Open\Command]
#="\"C:\\Program Files (x86)\MeetSchedAssist\MeetSchedAssist.exe\" \"%1\""
[HKEY_CLASSES_ROOT\MeetSchedAssist.MWB\Shell\Open\Command]
#="\"C:\\Program Files\MeetSchedAssist\MeetSchedAssist_x64.exe\" \"%1\""
So you can query the registered command and look for a respective executable name in the command.
procedure InitDefaultFileAssociationsTaskValue;
var
SubKeyName, Command: string;
begin
SubKeyName := 'MeetSchedAssist.MWB\Shell\Open\Command';
if not RegQueryStringValue(HKCR, SubKeyName, '', Command) then
begin
Log('MWB registration not found');
end
else
begin
Log(Format('Command registered for MWB is [%s]', [Command]));
Command := Lowercase(Command);
if Pos('meetschedassist_x64.exe', Command) > 0 then
begin
Log('Detected 64-bit registration');
WizardSelectTasks('register64');
end
else
if Pos('meetschedassist.exe', Command) > 0 then
begin
Log('Detected 32-bit registration');
WizardSelectTasks('register32');
end
else
begin
Log('Registration not recognised');
end;
end;
end;
procedure CurPageChanged(CurPageID: Integer);
begin
if CurPageID = wpSelectTasks then
begin
{ Only now is the task list initialized. }
InitDefaultFileAssociationsTaskValue;
end;
end;
You may want to modify this to change the task selection only the first time the user enters the tasks page.
var
SelectTasksVisited: Boolean;
procedure CurPageChanged(CurPageID: Integer);
begin
if CurPageID = wpSelectTasks then
begin
{ Only now is the task list initialized. }
if not SelectTasksVisited then
begin
InitDefaultFileAssociationsTaskValue;
SelectTasksVisited := True;
end;
end;
end;

Related

Waiting for uninstaller prompt in usPostUninstall before proceeding with installation in Inno Setup

Is there any way to pause the execution of Inno Setup until user makes some interactions with the message box. I am using a message box to confirm whether or not to keep user data. I want to stop all other executions in setup until the user selects yes or no.
procedure CurUninstallStepChanged(CurUninstallStep: TUninstallStep);
begin
if CurUninstallStep = usPostUninstall then
begin
if DirExists(ExpandConstant('{localappdata}\{#MyBuildId}\storage')) then
if MsgBox('Do you want to delete the saved user data?',
mbConfirmation, MB_YESNO) = IDYES
then
DelTree(ExpandConstant('{localappdata}\{#MyBuildId}\storage'), True, True, True);
end;
end;
I am using a separate procedure to uninstall the previous version in the beginning of the install.
procedure CurStepChanged(CurStep: TSetupStep);
begin
if (CurStep=ssInstall) then
begin
if (IsUpgrade()) then
begin
UnInstallOldVersion();
end;
end;
end;
So when starting to install a new setup first it uninstalls the old version. The user data deletion message box is also shown. But the execution is not pausing. It uninstalls and reinstalls the app while the message box is showing
function GetUninstallString(): String;
var
sUnInstPath: String;
sUnInstallString: String;
begin
sUnInstPath :=
ExpandConstant('Software\Microsoft\Windows\CurrentVersion\Uninstall\{#MyAppId}_is1');
sUnInstallString := '';
if not RegQueryStringValue(HKLM, sUnInstPath, 'UninstallString', sUnInstallString) then
RegQueryStringValue(HKCU, sUnInstPath, 'UninstallString', sUnInstallString);
Result := sUnInstallString;
end;
function UnInstallOldVersion(): Integer;
var
sUnInstallString: String;
iResultCode: Integer;
begin
{ Return Values: }
{ 1 - uninstall string is empty }
{ 2 - error executing the UnInstallString }
{ 3 - successfully executed the UnInstallString }
// default return value
Result := 0;
{ get the uninstall string of the old app }
sUnInstallString := GetUninstallString();
if sUnInstallString <> '' then begin
sUnInstallString := RemoveQuotes(sUnInstallString);
if Exec(sUnInstallString, '/SILENT /NORESTART','', SW_HIDE,
ewWaitUntilTerminated, iResultCode) then
Result := 3
else
Result := 2;
end else
Result := 1;
end;
When you execute the Inno Setup uninstaller .exe, it clones itself to a temporary folder and runs the clone internally. The main process waits for the clone to (almost) finish, before it terminates itself. The clone can then delete the main uninstaller .exe (as it is not locked anymore). The main process is terminated just after the actual uninstallation completes. But before CurUninstallStepChanged(usPostUninstall). So if you display your message box there, the main uninstaller process is terminated already and so is the Exec in UnInstallOldVersion.
If possible, do the data deletion on usUninstall, not on usPostUninstall.

How to force a reboot in post-install in Inno Setup

In my setup I have to install an external driver.
In some rare cases the installation fails and I have to remove the old driver and reboot before I can try again.
I install the external driver in the ssPostInstall.
procedure CurStepChanged(CurStep: TSetupStep);
begin
if CurStep = ssPostInstall then
begin
if Exec(ExpandConstant('{app}\external.exe'), '-install', '', SW_SHOW,
ewWaitUntilTerminated, ResultCode) then
begin
{ handle success if necessary; ResultCode contains the exit code }
end
else begin
{ handle failure if necessary; ResultCode contains the error code }
bReboot := true;
end;
end;
function NeedRestart(): Boolean;
begin
Result := bReboot;
end;
Unfortunately this does not work since NeedRestart is called before ssPostInstall.
Is there any other way to trigger a reboot?
I don't want to set AlwaysRestart = yes
I could let pop up a MsgBox to inform the user and tell them what to do. But it would be much nicer if it could be handled within of the setup automatically.
You can do the installation sooner. For example immediately after the external.exe is installed using AfterInstall:
[Files]
Source: "external.exe"; DestDir: "{app}"; AfterInstall: InstallDriver
[Code]
procedure InstallDriver;
begin
if Exec(ExpandConstant('{app}\external.exe'), '-install', '', SW_SHOW,
ewWaitUntilTerminated, ResultCode) then
begin
{ handle success if necessary; ResultCode contains the exit code }
end
else
begin
{ handle failure if necessary; ResultCode contains the error code }
bReboot := true;
end;
end;
Another option is to use ssInstall step (or even PrepareToInstall event) and extract the file programmatically using ExtractTemporaryFile.
Btw, if external.exe is only an installer, you may want to "install" it to {tmp} (to get it automatically deleted).

Inno Setup : The Installed Program Never Launches after Setup Completion

I have a problem in [Run] Section of my Inno Setup Script.
Whether I check or uncheck the CheckBox which appears in the CurPageID = wpFinished, my program never launches.
I set the default value of it to Checked.
Parts of my script whose belongs to this :
#define AppExec "hddbsfinder.exe"
#define AppName "HDD Bad Sectors Finder"
[Run]
Filename: "{app}\{#AppExec}"; Check: CheckLaunching; Description: "{cm:LaunchProgram,{#StringChange(AppName, '&', '&&')}}"; Flags: NoWait PostInstall
function CheckLaunching: Boolean;
begin
Result := not LauncherCB.Checked;
end;
var
LauncherCB: TNewCheckbox;
LauncherCB := TNewCheckBox.Create(WizardForm);
with LauncherCB do
begin
Parent := WizardForm;
Left := (225);
Top := (245);
Width := ScaleX(14);
Height := ScaleY(15);
end;
if CurPageID=wpSelectTasks then begin
LauncherCB.Hide;
LauncherCB.Checked := True;
end;
if CurPageID = wpFinished then begin
with WizardForm do begin
LauncherCB.Show;
end;
end;
My Program never launches even I check or uncheck that LauncherCB.
(The default value is Checked.)
Thanks in Advance.
The Check parameter of postinstall run entries is used to evaluate if to show the checkbox at all, not if to run the entry.
You have two options:
Implement the launching yourself in the NextButtonClick(wpFinished) using the Exec function.
Use the standard run checklist box, just it move to the location you need it at. You will probably need to change the list's .Parent to WizardForm to remove it from the "Finished" page.

Inno Setup conditional restart based on result of executable call

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;

How to install DirectX redistributable from Inno-setup?

I didn't find any tip about DirectX installation at Inno-Setup web site. So, is there any sample installation script? I know that I have to add to [Run] sction something like this:
Filename: "{src}\DirectX\DXSETUP.exe"; WorkingDir: "{src}\DirectX"; Parameters: "/silent"; Check: DirectX; Flags: waituntilterminated; BeforeInstall: DirectXProgress;
But how to include it into setup file (temp folder?), how to extract it, ect?
To include it in the setup, you can install it to {tmp} and then [Run] it from there.
The correct way to install this sort of requirement is to extract in code and call Exec() on it in the PrepareToInstall() event function:
function PrepareToInstall(var NeedsRestart: Boolean): String;
var
InstallerResult: integer;
begin
//Check if .Net is available already
if NeedsDirectX() then begin
ExtractTemporaryFile('DXSETUP.exe');
if Exec(ExpandConstant('{tmp}\DXSETUP.exe'), '', '', SW_SHOW, ewWaitUntilTerminated, InstallerResult) then begin
case InstallerResult of
0: begin
//It installed successfully (Or already was), we can continue
end;
else begin
//Some other error
result := 'DirectX installation failed. Exit code ' + IntToStr(InstallerResult);
end;
end;
end else begin
result := 'DirectX installation failed. ' + SysErrorMessage(InstallerResult);
end;
end;
end;
The ISXKB has an article on how to detect the versions installed.

Resources