Inno Setup: How to change Messages at runtime? - installation

I need to change Messages at runtime. I have a AfterInstall procedure that checks to see if a bat file was successful. If it is not, I want to change the value of ExitSetupMessage just before calling WizardForm.Close. I was hoping to do something like this english.ExitSetupMessage := 'THIS IS THE PART THAT DOES NOT WORK';. Code examples would be appreciated. Thank you.
[Languages]
Name: english; MessagesFile: compiler:Default.isl
[Files]
Source: {src}\test.bat; DestDir: {tmp}; AfterInstall: ValidateInstall
[Code]
procedure ValidateInstall();
var
ResultCode : Integer;
begin
if not Exec(ExpandConstant('{tmp}\test.bat'), '', '', SW_HIDE, ewWaitUntilTerminated, ResultCode) then
begin
english.ExitSetupMessage := 'THIS IS THE PART THAT DOES NOT WORK';
WizardForm.Close;
end;
end;

I don't know of a way to change the messages at runtime.
However in the case you posted I know of a workaround. You would set your CustomState before calling WizardForm.Close
var
CustomState : Boolean;
procedure CancelButtonClick(CurPageID: Integer; var Cancel, Confirm: Boolean);
var
Msg : String;
Res : Integer;
begin
Confirm := False; // Don't show the default dialog.
// Chose which message the custom or default message.
if CustomState then
Msg := 'My Custom Close Message'
else
Msg := SetupMessage(msgExitSetupMessage);
//as the Question
Res := MsgBox(Msg, mbConfirmation,MB_OKCANCEL);
// If they press OK then Cancel the install
Cancel := (Res = IDOK);
end;
The side effect is you lose the Exit Setup? title of the dialog box.
You can use function ExitSetupMsgBox: Boolean; when you don't want to change the message
to keep the title around.

According to http://www.jrsoftware.org/ishelp/index.php?topic=scriptclasses
it should be
WizardForm.FinishedLabel.Caption := 'Desired text goes here';

Related

Custom TLabel not displaying on FinishedPage in Inno Setup

I must be missing something obvious here. I'm trying to add a link to the release notes onto wpFinished but can't seem to make it show up:
I have a file finishedPage.iss which I include via #include "InnoDialogs\finishedPage.iss";
The file has the following content:
[Run]
Filename: "{app}\bin\{#MyAppExeName}"; \
Description: "{cm:LaunchProgram,{#StringChange(MyAppName, '&', '&&')}}"; \
Flags: nowait postinstall skipifsilen
[Code]
{ procedures to deal with page interaction }
procedure ReleaseNotesClick(Sender: TObject);
var
errorCode: Integer;
begin
ShellExec('','https://myUrl.com/Release_Notes', '', '', SW_SHOW, ewNoWait, errorCode)
end;
{ build the page }
procedure FinishedPage_Create;
var
ReleaseNotesLink: TLabel;
begin
ReleaseNotesLink := TLabel.Create(WizardForm);
ReleaseNotesLink.Parent := WizardForm.FinishedPage;
ReleaseNotesLink.Caption := 'Read the Releasenotes';
ReleaseNotesLink.Enabled := True;
ReleaseNotesLink.Visible := True;
ReleaseNotesLink.AutoSize := True;
ReleaseNotesLink.Left := WizardForm.FinishedLabel.Left;
ReleaseNotesLink.Top := WizardForm.FinishedLabel.Top + ScaleY(100);
ReleaseNotesLink.OnClick := #ReleaseNotesClick;
ReleaseNotesLink.ParentFont := True;
ReleaseNotesLink.Font.Style := ReleaseNotesLink.Font.Style + [fsUnderline, fsBold];
ReleaseNotesLink.Font.Color := clBlue;
ReleaseNotesLink.Cursor := crHand;
end;
In the CurPageChanged procedure in my main installer file I have:
procedure CurPageChanged(CurPageID: Integer);
begin
if CurPageID = wpFinished then
begin
FinishedPage_Create();
end;
end;
This compiles just fine, but I can't make it show up. I tried different positions as well, thinking perhaps it's just drawn behind something else. I'm using the same procedure for adding elements to other pages...
Any ideas what I'm missing?
Your label is hidden behind the RunList, which occupies the rest of the page.
You have to shrink the list. For example:
WizardForm.RunList.Height := ScaleY(24);
ReleaseNotesLink.Left := WizardForm.RunList.Left;
ReleaseNotesLink.Top := WizardForm.RunList.Top + WizardForm.RunList.Height + ScaleY(8);

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).

Download and run sub install - Inno Download Plugin progress bar does not move during download

Ok, so I created the following iss but I the progress bar does not move. I want the setup file to download and run the other setup program. Everything works fine except the progress bar does not move.
#define MyAppName "My Program Setup Downloader"
#define MySetupAppName "My Program Setup.exe"
#define MySetupUrlFolder "https://www.example.com/folder/"
#pragma include __INCLUDE__ + ";" + "c:\Program Files (x86)\Inno Download Plugin\"
[Setup]
AppName={#MyAppName}
AppVerName={#MyAppName}
DisableReadyPage=yes
DisableFinishedPage=yes
CreateAppDir=no
Uninstallable=no
#include <idp.iss>
[Code]
var FileName: string;
procedure InitializeWizard;
var DownloadUrl: String;
begin
FileName := ExpandConstant('{tmp}\{#MySetupAppName}');
DownloadUrl := '{#MySetupUrlFolder}{#MySetupAppName}';
idpAddFile(DownloadUrl, FileName);
idpDownloadAfter(wpSelectDir);
end;
function NextButtonClick(CurPageID: Integer) : boolean;
var ResultCode: Integer;
begin
if CurPageID = IDPForm.Page.ID then
begin
Result := Exec(FileName, '', '', SW_SHOW, ewWaitUntilTerminated, ResultCode);
if not Result then MsgBox('Error Running Downloaded Setup File', mbError, MB_OK);
Result := True;
end
else Result := True;
end;
Any Ideas? Everything else works fine.
Edit: I have a workaround that will show the details section. This might be more appropriate anyways. Still not sure why the Total progress is not updating.
procedure CurPageChanged(CurPageID: Integer);
begin
if CurPageID = IDPForm.Page.ID then
begin
idpShowDetails(True);
IDPForm.TotalProgressBar.Visible := false;
IDPForm.TotalProgressLabel.Visible := false;
IDPForm.TotalDownloaded.Visible := false;
IDPForm.CurrentFileLabel.Caption := 'Downloading...';
IDPForm.DetailsButton.Visible := False;
WizardForm.NextButton.Visible := False;
WizardForm.PageNameLabel.Caption := 'Downloading Setup File';
WizardForm.PageDescriptionLabel.Caption := 'Please wait while the Setup file is being downloaded.';
end;
end;
I indeed get the same behavior. I do not understand why.
But as you have a single file, you can replace the total progress bar with file progress bar:
procedure CurPageChanged(CurPageID: Integer);
begin
if CurPageID = IDPForm.Page.ID then
begin
IDPForm.TotalProgressBar.Visible := False;
IDPForm.FileProgressBar.Top := IDPForm.TotalProgressBar.Top;
IDPForm.FileProgressBar.Visible := True;
IDPForm.DetailsButton.Visible := False;
IDPForm.DetailsVisible := True;
end;
end;

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;

Inno Setup: Disable components page on upgrade

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;

Resources