Inno Setup replace buttons at wpFinished page - inno-setup

I'm trying to override Next/Cancel buttons on wpFinished page - NextButton should show downloaded file and exit the installator - it's working ok but CancelButton doesn't do nothing - it should close the installator with standard confirm. I wonder it is possible with standard inno events or I need to write own code to exit the application and show the confirm?
function NextButtonClick(CurPage: Integer): Boolean;
begin
if CurPage = wpFinished then begin
ShowDownloadedFile();
end;
Result := True;
end;
procedure CurPageChanged(CurPageID: Integer);
begin
if CurPageID = wpFinished then begin
WizardForm.NextButton.Caption := SetupMessage(msgButtonInstall);
WizardForm.CancelButton.Caption := SetupMessage(msgButtonFinish);
WizardForm.CancelButton.Visible := True;
end;
end;

Here it is, but don't do this at home kids :-)
[Setup]
AppName=My Program
AppVersion=1.5
DefaultDirName={pf}\My Program
[Code]
procedure ExitProcess(uExitCode: UINT);
external 'ExitProcess#kernel32.dll stdcall';
function NextButtonClick(CurPage: Integer): Boolean;
begin
Result := True;
// if the fake Finish button was clicked...
if CurPage = wpFinished then
MsgBox('Welcome to the next installation!', mbInformation, MB_OK);
end;
procedure CancelButtonClickFinishedPage(Sender: TObject);
begin
// display the "Exit Setup ?" message box and if the user selects "Yes",
// then exit the process; it is currently the only way how to exit setup
// process manually
if ExitSetupMsgBox then
ExitProcess(0);
end;
procedure CurPageChanged(CurPageID: Integer);
begin
if CurPageID = wpFinished then
begin
WizardForm.NextButton.Caption := SetupMessage(msgButtonInstall);
WizardForm.CancelButton.Caption := SetupMessage(msgButtonFinish);
WizardForm.CancelButton.Visible := True;
// bind your own OnClick event for the Cancel button; the original one
// is already disconnected at this stage
WizardForm.CancelButton.OnClick := #CancelButtonClickFinishedPage;
end;
end;

The proper alternative to what you're trying to do is to include a [Run] entry like so:
[Run]
Filename: {app}\yourfile.exe; Description: Run my application; Flags: postinstall nowait
This will display a checkbox on the wpFinished page giving them the choice to run the app or not.

Related

Run Files and Programs according to custom checkboxes after clicking on Finish Button in Inno Setup

I have created some custom checkboxes in the finished page of Inno Setup.
For example launching an app, opening a text file etc.
I need when the user clicks on the finish button I check those checkboxes and do whatever that is needed. How can I do such a thing in Inno Setup?
Here is the code:
procedure CurPageChanged(CurPageID: Integer);
begin
if CurPageID = wpFinished then
begin
Launch := TNewCheckBox.Create(WizardForm);
Launch.Parent := WizardForm;
Launch.Left := WizardForm.ClientWidth - 350;
Launch.Top := WizardForm.CancelButton.Top;
Launch.Width := 120;
Launch.Height := WizardForm.CancelButton.Height;
Launch.Caption := 'Launch';
end;
end;
In NextButtonClick event handler, test if your checkbox is checked and act accordingly.
function NextButtonClick(CurPageID: Integer): Boolean;
var
ResultCode: Integer;
Path: string;
Message: string;
begin
if CurPageID = wpFinished then
begin
if Launch.Checked then
begin
Path := ExpandConstant('{app}\MyProg.exe');
if ExecAsOriginalUser(Path, '', '', SW_SHOW, ewNoWait, ResultCode) then
begin
Log('Executed MyProg');
end
else
begin
Message := 'Error executing MyProg: ' + SysErrorMessage(ResultCode);
MsgBox(Message, mbError, MB_OK);
end;
end;
end;
Result := True;
end;
Simply check the checkbox state:
if (Launch.Checked = True) then
begin
// checkbox is checked
end
else
begin
// Checkbox is unchecked
end;
The best place is to use function NextButtonClick(CurPageID: Integer): Boolean;
however in that case you need to make your checkbox a global variable (so it is accessible).

Inno Setup remove/hide/disable the NextButton on a custom wizard page

I would like to disable the "Next" button from my custom wizard page.
The fact is that I've no issue to change the caption of it for example, but if I set the NextButton.Enabled to False, Inno Setup show me the Welcome page instead of my Custom Page. Any Idea?
procedure CurPageChanged(CurPageID: Integer);
begin
WizardForm.NextButton.Caption := 'test'; { Works }
WizardForm.NextButton.Enabled := false ; { delete my custom page }
WizardForm.CancelButton.Caption := 'Finish';
end;
procedure CreateTheWizardPages;
var
Page: TWizardPage;
TestConnectivityButton: TButton;
begin
Page := CreateCustomPage(wpWelcome, 'Connectivity Test', '');
CurPageChanged(Page.ID);
TestConnectivityButton := TButton.Create(Page);
TestConnectivityButton.Width := ScaleX(100);
TestConnectivityButton.Height := ScaleY(30);
TestConnectivityButton.Caption := CustomMessage('TestConnectivityAccessButtonLabel');
TestConnectivityButton.OnClick := #TestConnectivityWindow;
TestConnectivityButton.Parent := Page.Surface;
end;
procedure InitializeWizard;
begin
CreateTheWizardPages;
end;
You have to make the changes, when you enter your custom page only – When CurPageChanged event function is called with CurPageID equal to Page.ID.
And you cannot call CurPageChanged yourself!
var
Page: TWizardPage;
procedure CurPageChanged(CurPageID: Integer);
begin
if CurPageID = Page.ID then
begin
WizardForm.NextButton.Caption := 'Test';
WizardForm.NextButton.Enabled := False ;
WizardForm.CancelButton.Caption := 'Finish';
end;
end;
procedure CreateTheWizardPages;
var
TestConnectivityButton: TButton;
begin
Page := CreateCustomPage(wpWelcome, 'Connectivity Test', '');
TestConnectivityButton := TButton.Create(Page);
TestConnectivityButton.Width := ScaleX(100);
TestConnectivityButton.Height := ScaleY(30);
TestConnectivityButton.Caption :=
CustomMessage('TestConnectivityAccessButtonLabel');
TestConnectivityButton.OnClick := #TestConnectivityWindow;
TestConnectivityButton.Parent := Page.Surface;
end;
Another option is using Page.OnActivate event instead of CurPageChanged event function.

Inno Setup: How to use user inputs to execute codes conditionally

The below code asks user to select Yes or No using CreateInputOptionPage, with the below code I want to prompt message "Yes selected" if user selects "Yes" if user selects no then prompt "No selected".
My code doesn't seem to work. Please help.
[Setup]
AppName=My Program
AppVersion=1.5
DefaultDirName={pf}\My Program
DisableProgramGroupPage=yes
UninstallDisplayIcon={app}\MyProg.exe
OutputDir=D:\authorized\Builds\Custom wizard
[Code]
//#include "Solo - detectVersionFInal.iss"
var
UsagePage: TInputOptionWizardPage;
InstallationTypeIsClient: boolean;
Initialize:boolean;
procedure InitializeWizard;
begin
{ Create the pages }
UsagePage := CreateInputOptionPage(wpWelcome,
'KMOffline setup information', 'How would you like to install KMOffline?',
'Would you like to insall KMOffline as a service?.',
True, False);
UsagePage.Add('Yes');
UsagePage.Add('No');
UsagePage.Values[0] := False;
end;
function NextButtonClick(CurPageID: Integer): Boolean;
begin
if CurPageID=UsagePage.ID then
begin
InstallationTypeIsClient := UsagePage.Values[0];
MsgBox('InstallationTypeIsClient value is ' + Format('%d', [InstallationTypeIsClient]), mbInformation, MB_OK);
end;
Result := True;
end;
function InitializeSetup: Boolean;
var
begin
if (InstallationTypeIsClient=True) then
begin
MsgBox('Yes selected' + Format('%d', [InstallationTypeIsClient]), mbInformation, MB_OK);
end;
if (InstallationTypeIsClient=False) then
begin
MsgBox('No selected ' + Format('%d', [InstallationTypeIsClient]), mbInformation, MB_OK);
end;
Result := True;
end;
Just move your code from InitializeSetup to NextButtonClick:
function NextButtonClick(CurPageID: Integer): Boolean;
begin
if CurPageID = UsagePage.ID then
begin
if UsagePage.Values[0] then
begin
MsgBox('Yes selected', mbInformation, MB_OK);
end
else
begin
MsgBox('No selected', mbInformation, MB_OK);
end;
end;
Result := True;
end;

How to disable NextButton when file is not selected on InputFilePage (CreateInputFilePage)?

My Inno Setup program have a custom "input file page" that was created using CreateInputFilePage.
How can I disable the NextButton in this page until the file path is properly picked by the user?
In other words, I need to make the NextButton unclickable while the file selection form is empty, and clickable when the file selection form is filled.
Thank you.
The easiest way is to use NextButtonClick to validate the inputs and display an error message when the validation fails.
var
FilePage: TInputFileWizardPage;
procedure InitializeWizard();
begin
FilePage := CreateInputFilePage(wpSelectDir, 'caption', 'description', 'sub caption');
FilePage.Add('prompt', '*.*', '.dat');
end;
function NextButtonClick(CurPageID: Integer): Boolean;
begin
Result := True;
if (CurPageID = FilePage.ID) and
(Length(FilePage.Edits[0].Text) = 0) then
begin
MsgBox('Please select a file.', mbError, MB_OK);
WizardForm.ActiveControl := FilePage.Edits[0];
Result := False;
end;
end;
If you really want to update the "Next" button state while the input changes, it is a bit more complicated:
procedure FilePageEditChange(Sender: TObject);
begin
WizardForm.NextButton.Enabled := (Length(TEdit(Sender).Text) > 0);
end;
procedure FilePageActivate(Sender: TWizardPage);
begin
FilePageEditChange(TInputFileWizardPage(Sender).Edits[0]);
end;
procedure InitializeWizard();
var
Page: TInputFileWizardPage;
Edit: TEdit;
begin
Page := CreateInputFilePage(wpSelectDir, 'caption', 'description', 'sub caption');
{ To update the Next button state when the page is entered }
Page.OnActivate := #FilePageActivate;
Edit := Page.Edits[Page.Add('prompt', '*.*', '.dat')];
{ To update the Next button state when the edit contents changes }
Edit.OnChange := #FilePageEditChange;
end;

Inno Setup disable component selection when a specific component is selected

I would like to be able to disable the selection of a component based on a specific component being selected. I cannot do this through nesting of components, as the component needs to be selectable on it's own, but not if another specific component is selected. Currently I handle this using the NextButtonClick event displaying a message:
if IsComponentSelected('Client') and IsComponentSelected('Sync') then
begin
MsgBox('A Client installation cannot have the Synchronisation component selected.',
mbError, MB_OK);
Result := False;
end;
which prevents the user from continuing until they deselect the incompatible combination. However, it would be far more elegant if I could simply disable the selection of the component rather than displaying a message:
if CurPageID = wpSelectComponents then
begin
if IsComponentSelected('Client') then
begin
WizardForm.ComponentsList.Checked[15] := False;
WizardForm.ComponentsList.ItemEnabled[15] := False;
end
else
begin
WizardForm.ComponentsList.ItemEnabled[15] := True;
end;
end;
The problem is there doesn't seem to be an event that allows this to change dynamically as the user selects or deselects components. Is there an event I can place this code in or another way to do this?
You had TLama's solution which was probably better than the following, but still this code is also a way to achieve the goal (though you will need innocallback.dll):
[Setup]
AppName=My Program
AppVersion=1.5
DefaultDirName={pf}\My Program
[Files]
Source: ".\InnoCallback.dll"; DestDir: "{tmp}"; Flags: dontcopy nocompression
[Components]
Name: "Client"; Description: "Client"; Types: custom
Name: "Sync"; Description: "Sync"; Types: custom
[Code]
var
TimerID: Integer;
type
TTimerProc = procedure(Wnd: HWND; Msg: UINT; TimerID: UINT_PTR;
SysTime: DWORD);
function WrapTimerProc(Callback: TTimerProc; ParamCount: Integer): LongWord;
external 'wrapcallback#files:InnoCallback.dll stdcall';
function SetTimer(hWnd: HWND; nIDEvent, uElapse: UINT;
lpTimerFunc: UINT): UINT; external 'SetTimer#user32.dll stdcall';
function KillTimer(hWnd: HWND; uIDEvent: UINT): BOOL;
external 'KillTimer#user32.dll stdcall';
procedure KillComponentsTimer;
begin
if TimerID <> 0 then
begin
if KillTimer(0, TimerID) then
TimerID := 1;
end;
end;
procedure OnComponentsCheck(Wnd: HWND; Msg: UINT; TimerID: UINT_PTR;
SysTime: DWORD);
begin
if IsComponentSelected('Client') then begin
WizardForm.ComponentsList.Checked[1] := False;
WizardForm.ComponentsList.ItemEnabled[1] := False;
end
else begin
WizardForm.ComponentsList.ItemEnabled[1] := True;
end;
if IsComponentSelected('Sync') then begin
WizardForm.ComponentsList.Checked[0] := False;
WizardForm.ComponentsList.ItemEnabled[0] := False;
end
else begin
WizardForm.ComponentsList.ItemEnabled[0] := True;
end;
end;
procedure ComponentsCheck();
var
TimerCallback: LongWord;
begin
TimerCallback := WrapTimerProc(#OnComponentsCheck, 4);
TimerID := SetTimer(0, 0, 100, TimerCallback);
end;
procedure CurPageChanged(CurPageID: Integer);
begin
if CurPageID = wpSelectComponents then
begin
ComponentsCheck;
end;
if CurPageID = not wpSelectComponents then
begin
KillComponentsTimer;
end;
end;
The code I used in the end is based entirely on #TLama's code that he provided previously as this does not require the use of an external DLL.
[Code]
const
//Define global constants
CompIndexSync = 15;
CompIndexSyncClient = 16;
CompIndexSyncServer = 17;
var
//Define global variables
CompPageVisited: Boolean;
DefaultCompClickCheck: TNotifyEvent;
DefaultCompTypeChange: TNotifyEvent;
//Uncheck and set the enabled state of the Sync components based on whether the Client component is selected
procedure UpdateComponents;
begin
with WizardForm.ComponentsList do
begin
if IsComponentSelected('Client') then
begin
CheckItem(CompIndexSync, coUncheck);
end;
ItemEnabled[CompIndexSync] := not IsComponentSelected('Client');
ItemEnabled[CompIndexSyncClient] := not IsComponentSelected('Client');
ItemEnabled[CompIndexSyncServer] := not IsComponentSelected('Client');
Invalidate; //required for text state to update correctly
end;
end;
//Update the component states if the component states change and restore the original event handler procedures
procedure ComponentsClickCheck(Sender: TObject);
begin
DefaultCompClickCheck(Sender);
UpdateComponents;
end;
procedure ComponentsTypesComboChange(Sender: TObject);
begin
DefaultCompTypeChange(Sender);
UpdateComponents;
end;
procedure InitializeWizard();
begin
//Store the original Components Page OnClickCheck and Types Combo Box OnChange event procedures and assign custom procedures
DefaultCompClickCheck := WizardForm.ComponentsList.OnClickCheck;
WizardForm.ComponentsList.OnClickCheck := #ComponentsClickCheck;
DefaultCompTypeChange := WizardForm.TypesCombo.OnChange;
WizardForm.TypesCombo.OnChange := #ComponentsTypesComboChange;
end;
procedure CurPageChanged(CurPageID: Integer);
begin
//Update the Components Page if entered for the first time
if (CurPageID = wpSelectComponents) and not CompPageVisited then
begin
CompPageVisited := True;
UpdateComponents;
end;
end;
Although this and #RobeN's solutions both work, they both exhibit a graphical update issue i.e. when changing the status of the components from active to disabled and vice versa, the text doesn't automatically dim or undim until the scroll bar is dragged.
Note that the refresh issue described above is resolved by adding an `Invalidate;' line. Code updated above with a comment to reflect this. See Inno Setup Components graphical refresh issue for further information. Thanks #TLama.

Resources