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.
Related
My installer has a custom finish page with an image on it. I referred this Custom Welcome and Finished page with stretched image in Inno Setup solution for it. But the issue is with the check box at finish page, it is coming on top of the finish page image with a white background. If I removed postinstall flag, then it will automatically launch my app. But I want the user to be able to choose like how checkbox does. So is there any way to transparent the check box launch message on top of my image? Would TNewCheckBox help here?
[Run]
Filename: "app\My program.exe"; Description: "{cm:LaunchProgram}"; #
Flags: nowait postinstall skipifsilent
In standard Inno Setup, I do not think you can make WizardForm.RunList (TNewCheckListBox) transparent. But as the simple TNewCheckListBox is transparent, you can replace the WizardForm.RunList with TNewCheckListBox.
[Code]
procedure RunCheckBoxClick(Sender: TObject);
begin
WizardForm.RunList.Checked[0] := TNewCheckBox(Sender).Checked;
end;
procedure CurPageChanged(CurPageID: Integer);
var
RunCheckBox: TNewCheckBox;
begin
if CurPageID = wpFinished then
begin
if (not WizardForm.RunList.Visible) or
(WizardForm.RunList.Items.Count < 1) then
begin
Log('No items to run');
end
else
if WizardForm.RunList.Items.Count > 1 then
begin
Log('More than one item to run, keeping the standard non-transparent run list');
end
else
begin
Log('Replacing the one item in the run list with a simple transparent checkbox');
RunCheckBox := TNewCheckBox.Create(WizardForm);
RunCheckBox.Parent := WizardForm.RunList.Parent;
RunCheckBox.Left := WizardForm.RunList.Left + ScaleX(4);
RunCheckBox.Top := WizardForm.RunList.Top + ScaleY(4);
RunCheckBox.Width := WizardForm.RunList.Width;
RunCheckBox.Height := ScaleY(RunCheckBox.Height);
RunCheckBox.Checked := WizardForm.RunList.Checked[0];
RunCheckBox.Caption := WizardForm.RunList.ItemCaption[0];
RunCheckBox.OnClick := #RunCheckBoxClick;
WizardForm.RunList.Visible := False;
end
end;
end;
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;
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;
Is there a way to disable the Components Page for Upgrades? I would like to enable upgrades of my software but I don't want to allow the users to change the selection of components in case of an upgrade.
Instead the installer you upgrade all existing components from the first installation.
I am worried that it the user selects less components during the upgrade those missing components will stay installed as the old version and you get a mess.
I added the following to my script:
[Setup]
DisableDirPage=auto
DisableProgramGroupPage=auto
DirExistsWarning=auto
I just need a way to disable the components page and use the selection of the previous install (full install) for the upgrade. Is that possible?
I have found a related directive:
[Setup]
UsePreviousTasks=true
UsePreviousTasks is reading the existing section out of the registry which is good. Now I need to find a way to hide the selection window.
Thanks,
Wolfgang
To hide a page from user use the ShouldSkipPage event method. If you return True in this method, the page won't be shown to user. If False, the page will be displayed as usually. Here 's an example of how to check if the installation is an upgrade and if so, skip the Select Components wizard page:
[Setup]
AppId=B75E4823-1BC9-4AC6-A645-94027A16F5A5
AppName=My Program
AppVersion=1.5
DefaultDirName={pf}\My Program
; here is the place for your [Components] section and the rest of your script
[Code]
const
UninstallKey = 'Software\Microsoft\Windows\CurrentVersion\Uninstall\{#SetupSetting("AppId")}_is1';
function IsUpgrade: Boolean;
var
Value: string;
begin
Result := (RegQueryStringValue(HKLM, UninstallKey, 'UninstallString', Value) or
RegQueryStringValue(HKCU, UninstallKey, 'UninstallString', Value)) and (Value <> '');
end;
function ShouldSkipPage(PageID: Integer): Boolean;
begin
Result := (PageID = wpSelectComponents) and IsUpgrade;
end;
Another option you mentioned might be to disable all the controls of the page. The next script shows as the previous one how to check if the installation is an upgrade and if so, disables all the controls on the Select Components wizard page:
[Setup]
AppId=B75E4823-1BC9-4AC6-A645-94027A16F5A5
AppName=My Program
AppVersion=1.5
DefaultDirName={pf}\My Program
; here is the place for your [Components] section and the rest of your script
[Code]
const
UninstallKey = 'Software\Microsoft\Windows\CurrentVersion\Uninstall\{#SetupSetting("AppId")}_is1';
function IsUpgrade: Boolean;
var
Value: string;
begin
Result := (RegQueryStringValue(HKLM, UninstallKey, 'UninstallString', Value) or
RegQueryStringValue(HKCU, UninstallKey, 'UninstallString', Value)) and (Value <> '');
end;
procedure DisablePageControls(Page: TNewNotebookPage);
var
I: Integer;
begin
Page.Enabled := False;
for I := 0 to Page.ControlCount - 1 do
Page.Controls[I].Enabled := False;
end;
procedure InitializeWizard;
begin
if IsUpgrade then
DisablePageControls(WizardForm.SelectComponentsPage);
end;
The IsUpgrade function mentioned in TLama's answer has a bug. If AppId starts with a "{" which must be doubled, this isn't resolved and they registry key will not be found. Here's a corrected function that works for me:
function IsUpgrade: Boolean;
var
Value: string;
UninstallKey: string;
begin
UninstallKey := 'Software\Microsoft\Windows\CurrentVersion\Uninstall\' +
ExpandConstant('{#SetupSetting("AppId")}') + '_is1';
Result := (RegQueryStringValue(HKLM, UninstallKey, 'UninstallString', Value) or
RegQueryStringValue(HKCU, UninstallKey, 'UninstallString', Value)) and (Value <> '');
end;
Leave the separate const away for this function, it won't work with that extra function call.
Apart from that, 64-bit systems don't seem to cause any issues. If InnoSetup runs in 32-bit mode, the registry virtualisation is in effect and redirects you to the correct key already.
Something like that:
if CurPageID=wpSelectComponents then
begin
if ExtraOptionAvailable() then
begin
Wizardform.ComponentsList.Checked[6] := true;
Wizardform.ComponentsList.ItemEnabled[6] := true;
end else begin
Wizardform.ComponentsList.Checked[6] := false;
Wizardform.ComponentsList.ItemEnabled[6] := false;
end;
end;
I'm making a patch for an old game (Command & Conquer 1, Win95 edition), and in some cases, executing the patch requires going through a function written in the Pascal script that could take quite a while.
At the moment, I execute this at the moment the page is changed to the "installing" page, so, after the user has selected all options and confirmed to install, right before the installer starts actually adding (and deleting) files.
procedure CurPageChanged(CurPageID: Integer);
begin
if (CurPageID = wpInstalling) then
begin
// Rename all saveg_hi.### files to savegame.###
renameSaveGames();
// clean up the ginormous files mess left behind if the game was installed from the 'First Decade' compilation pack
cleanupTFD();
end;
end;
But since the process could be rather long, I'd prefer to somehow add it to the actual install progress bar. Is there any way to accomplish this?
You can control the ProgressGauge from the install page of the WizardForm. In the following script is shown how to update the progress bar from a loop (which you'll just replace with your actions). For safety are progress bar values like min, max and position saved before the custom actions are performed and restored when they're done.
[Code]
procedure CurPageChanged(CurPageID: Integer);
var
I: Integer;
ProgressMin: Longint;
ProgressMax: Longint;
ProgressPos: Longint;
begin
if CurPageID = wpInstalling then
begin
// save the original "configuration" of the progress bar
ProgressMin := WizardForm.ProgressGauge.Min;
ProgressMax := WizardForm.ProgressGauge.Max;
ProgressPos := WizardForm.ProgressGauge.Position;
// output some status and setup the min and max progress values
WizardForm.StatusLabel.Caption := 'Doing my own pre-install...';
WizardForm.ProgressGauge.Min := 0;
WizardForm.ProgressGauge.Max := 100;
// here will be your time consuming actions with the progress update
for I := 0 to 100 do
begin
WizardForm.FilenameLabel.Caption := 'I''m on ' + IntToStr(I) + '%';
WizardForm.ProgressGauge.Position := I;
Sleep(50);
end;
// restore the original "configuration" of the progress bar
WizardForm.ProgressGauge.Min := ProgressMin;
WizardForm.ProgressGauge.Max := ProgressMax;
WizardForm.ProgressGauge.Position := ProgressPos;
end;
end;