Inno Setup Script - Uninstall custom page with checkbox options [duplicate] - inno-setup

I need to ask the user for a password during install, which is then used as part of a command which runs after installation. I'm using a custom page to do this and it works fine.
I also need to ask the same question during an uninstall, which is used as part of a command which runs after uninstall.
I've checked the help and there does not seem to be a PageID for uninstalls, which I can use in my CreateInputQuery function. I don't particularly mind, if the page is displayed at the start, middle or end of the uninstall, as long as it is displayed.
I don't want to use the MsgBox for the uninstall as I want the look and feel of a standard page.
Any tips on how I can achieve this?

You can modify the uninstall form to behave like the install form (with pages and the Next/Back buttons).
In the InitializeUninstallProgressForm:
Create the new pages and insert them to the UninstallProgressForm.InnerNotebook (or the .OuterNotebook).
Implement the "Next" and the "Back" buttons.
You can also make the "Cancel" button working.
Run modal loop of the form using UninstallProgressForm.ShowModal.
Only after the modal loop exits, restore original layout of the form and let the uninstallation continue.
[Code]
var
UninstallFirstPage: TNewNotebookPage;
UninstallSecondPage: TNewNotebookPage;
UninstallBackButton: TNewButton;
UninstallNextButton: TNewButton;
procedure UpdateUninstallWizard;
begin
if UninstallProgressForm.InnerNotebook.ActivePage = UninstallFirstPage then
begin
UninstallProgressForm.PageNameLabel.Caption := 'First uninstall wizard page';
UninstallProgressForm.PageDescriptionLabel.Caption :=
'Lorem ipsum dolor sit amet, consectetur adipiscing elit.';
end
else
if UninstallProgressForm.InnerNotebook.ActivePage = UninstallSecondPage then
begin
UninstallProgressForm.PageNameLabel.Caption := 'Second uninstall wizard page';
UninstallProgressForm.PageDescriptionLabel.Caption :=
'Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.';
end;
UninstallBackButton.Visible :=
(UninstallProgressForm.InnerNotebook.ActivePage <> UninstallFirstPage);
if UninstallProgressForm.InnerNotebook.ActivePage <> UninstallSecondPage then
begin
UninstallNextButton.Caption := SetupMessage(msgButtonNext);
UninstallNextButton.ModalResult := mrNone;
end
else
begin
UninstallNextButton.Caption := 'Uninstall';
// Make the "Uninstall" button break the ShowModal loop
UninstallNextButton.ModalResult := mrOK;
end;
end;
procedure UninstallNextButtonClick(Sender: TObject);
begin
if UninstallProgressForm.InnerNotebook.ActivePage = UninstallSecondPage then
begin
UninstallNextButton.Visible := False;
UninstallBackButton.Visible := False;
end
else
begin
if UninstallProgressForm.InnerNotebook.ActivePage = UninstallFirstPage then
begin
UninstallProgressForm.InnerNotebook.ActivePage := UninstallSecondPage;
end;
UpdateUninstallWizard;
end;
end;
procedure UninstallBackButtonClick(Sender: TObject);
begin
if UninstallProgressForm.InnerNotebook.ActivePage = UninstallSecondPage then
begin
UninstallProgressForm.InnerNotebook.ActivePage := UninstallFirstPage;
end;
UpdateUninstallWizard;
end;
procedure InitializeUninstallProgressForm();
var
PageText: TNewStaticText;
PageNameLabel: string;
PageDescriptionLabel: string;
CancelButtonEnabled: Boolean;
CancelButtonModalResult: Integer;
begin
if not UninstallSilent then
begin
// Create the first page and make it active
UninstallFirstPage := TNewNotebookPage.Create(UninstallProgressForm);
UninstallFirstPage.Notebook := UninstallProgressForm.InnerNotebook;
UninstallFirstPage.Parent := UninstallProgressForm.InnerNotebook;
UninstallFirstPage.Align := alClient;
PageText := TNewStaticText.Create(UninstallProgressForm);
PageText.Parent := UninstallFirstPage;
PageText.Top := UninstallProgressForm.StatusLabel.Top;
PageText.Left := UninstallProgressForm.StatusLabel.Left;
PageText.Width := UninstallProgressForm.StatusLabel.Width;
PageText.Height := UninstallProgressForm.StatusLabel.Height;
PageText.AutoSize := False;
PageText.ShowAccelChar := False;
PageText.Caption := 'Press Next to proceeed with uninstallation.';
UninstallProgressForm.InnerNotebook.ActivePage := UninstallFirstPage;
PageNameLabel := UninstallProgressForm.PageNameLabel.Caption;
PageDescriptionLabel := UninstallProgressForm.PageDescriptionLabel.Caption;
// Create the second page
UninstallSecondPage := TNewNotebookPage.Create(UninstallProgressForm);
UninstallSecondPage.Notebook := UninstallProgressForm.InnerNotebook;
UninstallSecondPage.Parent := UninstallProgressForm.InnerNotebook;
UninstallSecondPage.Align := alClient;
PageText := TNewStaticText.Create(UninstallProgressForm);
PageText.Parent := UninstallSecondPage;
PageText.Top := UninstallProgressForm.StatusLabel.Top;
PageText.Left := UninstallProgressForm.StatusLabel.Left;
PageText.Width := UninstallProgressForm.StatusLabel.Width;
PageText.Height := UninstallProgressForm.StatusLabel.Height;
PageText.AutoSize := False;
PageText.ShowAccelChar := False;
PageText.Caption := 'Press Uninstall to proceeed with uninstallation.';
UninstallNextButton := TNewButton.Create(UninstallProgressForm);
UninstallNextButton.Parent := UninstallProgressForm;
UninstallNextButton.Left :=
UninstallProgressForm.CancelButton.Left -
UninstallProgressForm.CancelButton.Width -
ScaleX(10);
UninstallNextButton.Top := UninstallProgressForm.CancelButton.Top;
UninstallNextButton.Width := UninstallProgressForm.CancelButton.Width;
UninstallNextButton.Height := UninstallProgressForm.CancelButton.Height;
UninstallNextButton.OnClick := #UninstallNextButtonClick;
UninstallBackButton := TNewButton.Create(UninstallProgressForm);
UninstallBackButton.Parent := UninstallProgressForm;
UninstallBackButton.Left :=
UninstallNextButton.Left - UninstallNextButton.Width -
ScaleX(10);
UninstallBackButton.Top := UninstallProgressForm.CancelButton.Top;
UninstallBackButton.Width := UninstallProgressForm.CancelButton.Width;
UninstallBackButton.Height := UninstallProgressForm.CancelButton.Height;
UninstallBackButton.Caption := SetupMessage(msgButtonBack);
UninstallBackButton.OnClick := #UninstallBackButtonClick;
UninstallBackButton.TabOrder := UninstallProgressForm.CancelButton.TabOrder;
UninstallNextButton.TabOrder := UninstallBackButton.TabOrder + 1;
UninstallProgressForm.CancelButton.TabOrder :=
UninstallNextButton.TabOrder + 1;
// Run our wizard pages
UpdateUninstallWizard;
CancelButtonEnabled := UninstallProgressForm.CancelButton.Enabled
UninstallProgressForm.CancelButton.Enabled := True;
CancelButtonModalResult := UninstallProgressForm.CancelButton.ModalResult;
UninstallProgressForm.CancelButton.ModalResult := mrCancel;
if UninstallProgressForm.ShowModal = mrCancel then Abort;
// Restore the standard page payout
UninstallProgressForm.CancelButton.Enabled := CancelButtonEnabled;
UninstallProgressForm.CancelButton.ModalResult := CancelButtonModalResult;
UninstallProgressForm.PageNameLabel.Caption := PageNameLabel;
UninstallProgressForm.PageDescriptionLabel.Caption := PageDescriptionLabel;
UninstallProgressForm.InnerNotebook.ActivePage :=
UninstallProgressForm.InstallingPage;
end;
end;
See also How to create a OuterNotebook/welcome page in the Inno Setup uninstaller?

Luckily Inno Setup gives enough abilities to build own forms so you can imitate any page of uninstall form on another modal form.
Here's what I managed to create. The form takes dimensions from UninstallForm, contains a TNewNotebook control and buttons to jump between pages and to cancel the dialog.
const
ControlGap = 5; // determined empirically
// Set Back/Next buttons state according to current selected notebook page
procedure UpdateButtonsState(Form: TSetupForm);
var
Notebook: TNewNotebook;
BtnBack, BtnNext: TButton;
begin
Notebook := TNewNotebook(Form.FindComponent('Notebook'));
BtnBack := TButton(Form.FindComponent('BtnBack'));
BtnNext := TButton(Form.FindComponent('BtnNext'));
// Update buttons state
BtnBack.Enabled := (Notebook.ActivePage <> Notebook.Pages[0]);
if Notebook.ActivePage <> Notebook.Pages[Notebook.PageCount - 1] then
begin
BtnNext.Caption := SetupMessage(msgButtonNext)
BtnNext.ModalResult := mrNone;
end
else
begin
BtnNext.Caption := SetupMessage(msgButtonFinish);
BtnNext.ModalResult := mrYes;
end;
end;
// Change notebook page
procedure BtnPageChangeClick(Sender: TObject);
var
NextPage: TNewNotebookPage;
Notebook: TNewNotebook;
Form: TWinControl;
Button, BtnBack, BtnNext: TButton;
begin
Button := TButton(Sender);
Form := Button;
while not (Form is TSetupForm) do
Form := Form.Parent;
Notebook := TNewNotebook(Form.FindComponent('Notebook'));
BtnBack := TButton(Form.FindComponent('BtnBack'));
BtnNext := TButton(Form.FindComponent('BtnNext'));
// Avoid cycled style of Notebook's page looping
if (Button = BtnBack) and (Notebook.ActivePage = Notebook.Pages[0]) then
NextPage := nil
else
if (Button = BtnNext) and (Notebook.ActivePage = Notebook.Pages[Notebook.PageCount - 1]) then
NextPage := nil
else
NextPage := Notebook.FindNextPage(Notebook.ActivePage, Button = BtnNext);
Notebook.ActivePage := NextPage;
UpdateButtonsState(TSetupForm(Form));
end;
// Add a new page to notebook and return it
function AddPage(NotebookForm: TSetupForm): TNewNotebookPage;
var
Notebook: TNewNotebook;
begin
Notebook := TNewNotebook(NotebookForm.FindComponent('Notebook'));
Result := TNewNotebookPage.Create(Notebook);
Result.Notebook:=Notebook;
Result.Parent:=Notebook;
Result.Align := alClient;
if Notebook.ActivePage = nil then
Notebook.ActivePage := Result;
UpdateButtonsState(NotebookForm);
end;
// Create a form with notebook and 3 buttons: Back, Next, Cancel
function CreateNotebookForm: TSetupForm;
var
Notebook: TNewNotebook;
NotebookPage: TNewNotebookPage;
Pan: TPanel;
TmpLabel: TLabel;
MaxWidth, i: Integer;
BtnBack, BtnNext, BtnCancel: TButton;
BtnLabelMsgIDs: array of TSetupMessageID;
begin
Result := CreateCustomForm;
Result.SetBounds(0, 0, UninstallProgressForm.Width, UninstallProgressForm.Height);
Result.Position := poOwnerFormCenter;
Notebook := TNewNotebook.Create(Result);
Notebook.Parent := Result;
Notebook.Name := 'Notebook'; // will be used for searching
Notebook.Align := alClient;
Pan := TPanel.Create(Result);
Pan.Parent := Notebook;
Pan.Caption := '';
Pan.Align := alBottom;
// Create buttons
BtnNext := TNewButton.Create(Result);
with BtnNext do
begin
Parent := Pan;
Name := 'BtnNext'; // will be used for searching
Caption := SetupMessage(msgButtonNext);
OnClick := #BtnPageChangeClick;
ParentFont := True;
end;
BtnBack := TNewButton.Create(Result);
with BtnBack do
begin
Parent := Pan;
Caption := SetupMessage(msgButtonBack);
Name := 'BtnBack'; // will be used for searching
OnClick := #BtnPageChangeClick;
ParentFont := True;
end;
BtnCancel := TNewButton.Create(Result);
with BtnCancel do
begin
Parent := Pan;
Name := 'BtnCancel'; // will be used for searching
Caption := SetupMessage(msgButtonCancel);
ModalResult := mrCancel;
Cancel := True;
ParentFont := True;
end;
// Determine dimensions of buttons. Should use TCanvas.TextWidth here
// but it doesn't allow Font property assignment :(
TmpLabel := TLabel.Create(Result);
with TmpLabel do
begin
Left := 0;
Top := 0;
Parent := Pan;
ParentFont := True;
Visible := False;
WordWrap := False;
Autosize := True;
end;
// Determine max label width among these labels: Back, Next, Cancel, Finish
SetArrayLength(BtnLabelMsgIDs, 4);
BtnLabelMsgIDs[0] := msgButtonBack;
BtnLabelMsgIDs[1] := msgButtonNext;
BtnLabelMsgIDs[2] := msgButtonCancel;
BtnLabelMsgIDs[3] := msgButtonFinish;
MaxWidth := 0;
for i := Low(BtnLabelMsgIDs) to High(BtnLabelMsgIDs) do
begin
TmpLabel.Caption := SetupMessage(BtnLabelMsgIDs[i]) + 'WWW'; // Add letters for surrounding spaces
if MaxWidth < TmpLabel.Width then
MaxWidth := TmpLabel.Width;
end;
TmpLabel.Caption := 'Yy'; // Determine height
// Assign sizes and positions
Pan.ClientHeight := TmpLabel.Height*4;
with BtnBack do
begin
Width := MaxWidth;
Height := TmpLabel.Height*2;
Left := Parent.ClientWidth - 3*(MaxWidth + ScaleX(ControlGap));
Top := (Parent.ClientHeight - Height) div 2;
end;
with BtnNext do
begin
Width := MaxWidth;
Height := TmpLabel.Height*2;
Left := Parent.ClientWidth - 2*(MaxWidth + ScaleX(ControlGap));
Top := (Parent.ClientHeight - Height) div 2;
end;
with BtnCancel do
begin
Width := MaxWidth;
Height := TmpLabel.Height*2;
Left := Parent.ClientWidth - 1*(MaxWidth + ScaleX(ControlGap));
Top := (Parent.ClientHeight - Height) div 2;
end;
end;
Usage is like
// UninstallProgressForm is about to be shown
// Show modal dialog which allows to select additional components to uninstall
procedure InitializeUninstallProgressForm;
var
Form: TSetupForm;
i: Integer;
NotebookPage: TNewNotebookPage;
ModResult: Integer;
begin
Form := CreateNotebookForm;
for i := 1 to 4 do
begin
NotebookPage := AddPage(Form);
with NotebookPage do
begin
Color := clWindow;
with TLabel.Create(Form) do
begin
Parent := NotebookPage;
SetBounds(0, 0, 50, 30);
Autosize := true;
Font.Size := 14;
Caption := 'Label ' + IntToStr(i);
end;
end;
end;
ModResult := Form.ShowModal;
if ModResult = mrYes then
MsgBox('Continuing uninstall', mbInformation, MB_OK)
else
begin
MsgBox('Cancelled', mbInformation, MB_OK);
Abort;
end;
...
end;

Inno does not currently support wizard pages during uninstall. You will need to use Forms instead.

I'm using InnoSetup 5.4.2 and in the docs are several Uninstall Event Functions including:
procedure CurUninstallStepChanged(CurUninstallStep: TUninstallStep);
You should be able to create an input page within the [code] section.

Just in case that you only need one page, we can simplify Martin Prikryl answer.
It still is the same process but it's all implemented in the InitializeUninstallProgressForm:
Create the Uninstall button.
Create the new page and insert-it to the UninstallProgressForm.InnerNotebook (or the .OuterNotebook).
Add controls to the page.
You can also make the "Cancel" button working.
Run modal loop of the form using UninstallProgressForm.ShowModal.
Only after the modal loop exits, restore original layout of the form and let the uninstallation continue.
[Code]
procedure InitializeUninstallProgressForm();
var
UninstallPage: TNewNotebookPage;
UninstallButton: TNewButton;
OriginalPageNameLabel: string;
OriginalPageDescriptionLabel: string;
OriginalCancelButtonEnabled: Boolean;
OriginalCancelButtonModalResult: Integer;
ctrl: TWinControl;
begin
if not UninstallSilent then
begin
{ Create Uninstall button }
ctrl := UninstallProgressForm.CancelButton;
UninstallButton := TNewButton.Create(UninstallProgressForm)
UninstallButton.Parent := UninstallProgressForm;
UninstallButton.Left := ctrl.Left - ctrl.Width - ScaleX(10);
UninstallButton.Top := ctrl.Top;
UninstallButton.Width := ctrl.Width;
UninstallButton.Height := ctrl.Height;
UninstallButton.TabOrder := ctrl.TabOrder;
UninstallButton.Caption := 'Uninstall';
{ Make the "Uninstall" button break the ShowModal loop }
UninstallButton.ModalResult := mrOK;
UninstallProgressForm.CancelButton.TabOrder := UninstallButton.TabOrder + 1;
{ Create the page and make it active }
UninstallPage := TNewNotebookPage.Create(UninstallProgressForm);
UninstallPage.Notebook := UninstallProgressForm.InnerNotebook;
UninstallPage.Parent := UninstallProgressForm.InnerNotebook;
UninstallPage.Align := alClient;
UninstallProgressForm.InnerNotebook.ActivePage := UninstallPage;
{ Create and add a controls to the page }
ctrl := UninstallProgressForm.StatusLabel;
with TNewStaticText.Create(UninstallProgressForm) do
begin
Parent := UninstallPage;
Top := ctrl.Top;
Left := ctrl.Left;
Width := ctrl.Width;
Height := ctrl.Height;
AutoSize := False;
ShowAccelChar := False;
Caption := 'Press Next to proceeed with uninstallation.';
end;
{ Save state }
OriginalPageNameLabel := UninstallProgressForm.PageNameLabel.Caption;
OriginalPageDescriptionLabel := UninstallProgressForm.PageDescriptionLabel.Caption;
OriginalCancelButtonEnabled := UninstallProgressForm.CancelButton.Enabled;
OriginalCancelButtonModalResult := UninstallProgressForm.CancelButton.ModalResult;
{ Run our wizard pages }
UninstallProgressForm.PageNameLabel.Caption := 'First uninstall wizard page';
UninstallProgressForm.PageDescriptionLabel.Caption := 'Lorem ipsum dolor sit amet, consectetur adipiscing elit.';
UninstallProgressForm.CancelButton.Enabled := True;
UninstallProgressForm.CancelButton.ModalResult := mrCancel;
{ Show the form }
if UninstallProgressForm.ShowModal = mrCancel then Abort;
{ Restore the standard page layout }
UninstallButton.Visible := False;
UninstallProgressForm.PageNameLabel.Caption := OriginalPageNameLabel;
UninstallProgressForm.PageDescriptionLabel.Caption := OriginalPageDescriptionLabel;
UninstallProgressForm.CancelButton.Enabled := OriginalCancelButtonEnabled;
UninstallProgressForm.CancelButton.ModalResult := OriginalCancelButtonModalResult;
UninstallProgressForm.InnerNotebook.ActivePage := UninstallProgressForm.InstallingPage;
end;
end;

Related

Cover a check box in the image to require a code for its selection

I'm trying to create something like this:
I want the check box to be hidden behind the lock image, and clicking on it will open a form requesting code entry, and if the code is correct the image will be removed and the check box will be available.
Here's what I tried:
[files]
Source: "Lock.bmp"; Flags: dontcopy
{...}
[code]
var
CustomFuncPage: TWizardPage;
CBAdvFunctions: TNewCheckBox;
Locked: TBitmapImage;
procedure OnClickLock(Sender: TObject);
var
PassForm: TSetupForm;
Edit: TNewEdit;
OKButton, CancelButton: TNewButton;
begin
PassForm := CreateCustomForm();
PassForm.ClientWidth := ScaleX(256);
PassForm.ClientHeight := ScaleY(128);
PassForm.Caption := 'Password required';
Edit := TNewEdit.Create(PassForm);
Edit.Top := ScaleY(10);
Edit.Left := ScaleX(10);
Edit.Width := PassForm.ClientWidth - ScaleX(20);
Edit.Height := ScaleY(23);
Edit.Anchors := [akLeft, akTop, akRight];
Edit.Parent := PassForm;
OKButton := TNewButton.Create(PassForm);
OKButton.Parent := PassForm;
OKButton.Caption := 'OK';
OKButton.Left := PassForm.ClientWidth - ScaleX(166);
OKButton.Top := PassForm.ClientHeight - ScaleY(33);
OKButton.Height := ScaleY(23);
OKButton.Anchors := [akRight, akBottom]
OKButton.ModalResult := mrOk;
OKButton.Default := True;
CancelButton := TNewButton.Create(PassForm);
CancelButton.Parent := PassForm;
CancelButton.Caption := 'Cancel';
CancelButton.Left := PassForm.ClientWidth - ScaleX(85);
CancelButton.Top := PassForm.ClientHeight - ScaleY(33);
CancelButton.Height := ScaleY(23);
CancelButton.Anchors := [akRight, akBottom]
CancelButton.ModalResult := mrCancel;
CancelButton.Cancel := True;
PassForm.ActiveControl := Edit;
if PassForm.ShowModal() = mrOk then
begin
if Edit.Text = '1234' then
begin
Locked.Free;
PassForm.Free;
end else
begin
MsgBox('Password incorrect', mbError, MB_OK);
PassForm.Free;
end;
end else
begin
PassForm.Free;
end;
end;
Procedure InitializeWizard;
begin
CustomFuncPage := CreateCustomPage(wpSelectProgramGroup, {...}, {...});
CBAdvFunctions := TNewCheckBox.Create(CustomFuncPage);
CBAdvFunctions.Parent := CustomFuncPage.Surface;
CBAdvFunctions.Top := 0;
CBAdvFunctions.Left := 0;
CBAdvFunctions.Caption := 'Advanced Functions'
CBAdvFunctions.Width := ScaleX(130);
CBAdvFunctions.Cursor := 1;
CBAdvFunctions.SendToBack;
ExtractTemporaryFile('Lock.bmp');
Locked := TBitmapImage.Create(CustomFuncPage);
Locked.Parent := CustomFuncPage.Surface;
Locked.Bitmap.LoadFromFile(ExpandConstant('{tmp}\Lock.bmp'));
Locked.Bitmap.AlphaFormat := afPremultiplied;
Locked.SetBounds(0, 0, CBAdvFunctions.Width, CBAdvFunctions.Height);
Locked.Stretch := True;
Locked.BringToFront;
Locked.OnClick := #OnClickLock;
{...}
And this is the result:
This is the image I used:
I tried a few ways, but was unable to get the image to cover the checkbox.
Maybe someone has an idea?
By the way, maybe someone has an explanation for why the background of the image is not transparent?

How do you close then restart explorer.exe in Inno Setup uninstall using the Restart Manager?

Does anyone know of a sample or can provide a sample of how to use the Restart Manager built-in to Windows to close explorer.exe at the beginning of the uninstall process of Inno Setup then restarting it just after files removed ? I have some shell items that even after unregistered is still allocated and can't be removed until explorer.exe is closed.
TIA!!
Here's an example of how to do it:
{ Example how to use RestartManager and Offer Option to Delete App Data during Uninstall of Inno Setup }
{ Combined multiple open source ISS code found with some custom changes }
{ This could probably be cleaned up a bit, my goal was to get something to work. }
{ I also stripped it down a bit as I had multiple applications and data so hope }
{ everything removed without an issue, but I'm sure you can figure it out if so }
{-------------------------------------------------------------------------}
{ Copies a NULL-terminated array of characters to a string. }
function ArrayToString(Chars:array of Char):String;
var
Len,i:Longint;
begin
Len:=GetArrayLength(Chars);
SetLength(Result,Len);
i:=0;
while (i<Len) and (Chars[i]<>#0) do begin
Result[i+1]:=Chars[i];
i:=i+1;
end;
SetLength(Result,i);
end;
{-------------------------------------------------------------------------}
type
LONG = Longint;
ULONG = Cardinal;
IdList=array of DWORD;
ProcessEntry=record
ID:DWORD;
Name:String;
Restartable,ToTerminate:Boolean;
end;
ProcessList=array of ProcessEntry;
{
Code for Windows Vista and above
}
const
{ Return codes. }
ERROR_SUCCESS = 0;
ERROR_MORE_DATA = 234;
INVALID_HANDLE_VALUE = -1;
CCH_RM_SESSION_KEY = 32;
CCH_RM_MAX_APP_NAME = 255;
CCH_RM_MAX_SVC_NAME = 63;
RmUnknownApp = 0; { The application cannot be classified as any other type. An application of this type can only be shut down by a forced shutdown. }
RmMainWindow = 1; { A Windows application run as a stand-alone process that displays a top-level window. }
RmOtherWindow = 2; { A Windows application that does not run as a stand-alone process and does not display a top-level window. }
RmService = 3; { The application is a Windows service. }
RmExplorer = 4; { The application is Windows Explorer. }
RmConsole = 5; { The application is a stand-alone console application. }
RmCritical = 1000; { A system restart is required to complete the installation because a process cannot be shut down. }
RmStatusUnknown = $0000;
RmStatusRunning = $0001;
RmStatusStopped = $0002;
RmStatusStoppedOther = $0004;
RmStatusRestarted = $0008;
RmStatusErrorOnStop = $0010;
RmStatusErrorOnRestart = $0020;
RmStatusShutdownMasked = $0040;
RmStatusRestartMasked = $0080;
RmForceShutdown = $0001;
RmShutdownOnlyRegistered = $0010;
type
SessionKey=array[1..CCH_RM_SESSION_KEY+1] of Char;
FILETIME=record
dwLowDateTime,dwHighDateTime:DWORD;
end;
RM_UNIQUE_PROCESS=record
dwProcessId:DWORD;
ProcessStartTime:FILETIME;
end;
RM_APP_TYPE=DWORD;
RM_PROCESS_INFO=record
Process:RM_UNIQUE_PROCESS;
strAppName:array[1..CCH_RM_MAX_APP_NAME+1] of Char;
strServiceShortName:array[1..CCH_RM_MAX_SVC_NAME+1] of Char;
ApplicationType:RM_APP_TYPE;
AppStatus:ULONG;
TSSessionId:DWORD;
bRestartable:BOOL;
end;
RM_WRITE_STATUS_CALLBACK=DWORD;
function RmStartSession(var pSessionHandle:DWORD;dwSessionFlags:DWORD;strSessionKey:SessionKey):DWORD;
external 'RmStartSession#Rstrtmgr.dll stdcall delayload';
function RmEndSession(dwSessionHandle:DWORD):DWORD;
external 'RmEndSession#Rstrtmgr.dll stdcall delayload';
function RmRegisterResources(dwSessionHandle:DWORD;hFiles:UINT;rgsFilenames:TArrayOfString;nApplications:UINT;rgApplications:array of RM_UNIQUE_PROCESS;nServices:UINT;rgsServiceNames:TArrayOfString):DWORD;
external 'RmRegisterResources#Rstrtmgr.dll stdcall delayload';
function RmGetList(dwSessionHandle:DWORD;var pnProcInfoNeeded,pnProcInfo:UINT;rgAffectedApps:array of RM_PROCESS_INFO;var lpdwRebootReasons:DWORD):DWORD;
external 'RmGetList#Rstrtmgr.dll stdcall delayload';
function RmShutdown(dwSessionHandle:DWORD;lActionFlags:ULONG;fnStatus:RM_WRITE_STATUS_CALLBACK):DWORD;
external 'RmShutdown#Rstrtmgr.dll stdcall delayload';
function RmRestart(dwSessionHandle:DWORD;dwRestartFlags:DWORD;fnStatus:RM_WRITE_STATUS_CALLBACK):DWORD;
external 'RmRestart#Rstrtmgr.dll stdcall delayload';
var
Processes: ProcessList;
RestartManagerHandle:DWORD;
{-------------------------------------------------------------------------}
{ Returns a list of running processes that currectly use one of the specified modules. }
{ Each module has to be a full path and filename to a DLL. }
function FindProcessesUsingModules(Modules:TArrayOfString;var Processes:ProcessList):DWORD;
var
Handle:DWORD;
Name:SessionKey;
Apps:array of RM_UNIQUE_PROCESS;
Services:TArrayOfString;
Path:String;
PathLength:DWORD;
Needed,Have,i:UINT;
AppList:array of RM_PROCESS_INFO;
RebootReason:DWORD;
Success:DWORD;
begin
SetArrayLength(Processes,0);
Result:=INVALID_HANDLE_VALUE;
{ NULL-terminate the array of chars. }
Name[CCH_RM_SESSION_KEY+1]:=#0;
if RmStartSession(Handle,0,Name)<>ERROR_SUCCESS then begin
Exit;
end;
if RmRegisterResources(Handle,GetArrayLength(Modules),Modules,0,Apps,0,Services)=ERROR_SUCCESS then begin
{ Reallocate the arrays until they are large enough to hold the process information. }
Needed:=1;
repeat
Have:=Needed;
SetArrayLength(AppList,Have);
Success:=RmGetList(Handle,Needed,Have,AppList,RebootReason);
until (Success<>ERROR_MORE_DATA) or (Have>=Needed);
if (Success=ERROR_SUCCESS) and (Needed>0) then begin
for i:=0 to Needed-1 do begin
{ append to end of list }
Have:=GetArrayLength(Processes);
SetArrayLength(Processes,Have+1);
{ assign values to new entry }
Processes[Have].ID:=AppList[i].Process.dwProcessId;
Processes[Have].Name:=ArrayToString(AppList[i].strAppName);
Processes[Have].Restartable:=AppList[i].bRestartable;
Processes[Have].ToTerminate:=True;
end;
Result:=Handle;
end;
end;
if (Result=INVALID_HANDLE_VALUE) then
RmEndSession(Handle);
end;
{-------------------------------------------------------------------------}
{ Returns a list of running processes that currectly use the specified module.
{ The module has to be a full path and filename to a DLL. This starts the }
{ RestartManager and returns its handle or INVALID_HANDLE_VALUE if failed }
{ or nothing has lock }
function FindProcessesUsingModule(Module:String;var Processes:ProcessList):DWORD;
var
Modules:TArrayOfString;
begin
SetArrayLength(Modules,1);
Modules[0]:=Module;
Result:=FindProcessesUsingModules(Modules,Processes);
end;
{-------------------------------------------------------------------------}
var
UninstallDelDataPage: TNewNotebookPage;
UninstallConfirmPage: TNewNotebookPage;
UninstallProcessListPage: TNewNotebookPage;
UninstallBackButton: TNewButton;
UninstallNextButton: TNewButton;
UninstallAutoCloseRB: TNewRadioButton;
DeleteAppDataCheckBox: TNewCheckBox;
AppData: String;
{-------------------------------------------------------------------------}
procedure UpdateUninstallWizard;
begin
if UninstallProgressForm.InnerNotebook.ActivePage = UninstallDelDataPage then
begin
UninstallProgressForm.PageNameLabel.Caption := 'Select Data Uninstall Options';
UninstallProgressForm.PageDescriptionLabel.Caption := 'What data, if any, should be deleted?';
end
else
if UninstallProgressForm.InnerNotebook.ActivePage = UninstallConfirmPage then
begin
UninstallProgressForm.PageNameLabel.Caption := 'Confirm Uninstall';
UninstallProgressForm.PageDescriptionLabel.Caption := 'Confirm the uninstall options below.';
end
else
if UninstallProgressForm.InnerNotebook.ActivePage = UninstallProcessListPage then
begin
UninstallProgressForm.PageNameLabel.Caption := 'Preparing to Uninstall';
UninstallProgressForm.PageDescriptionLabel.Caption := 'Uninstall is preparing to uninstall {#MyAppName} from your computer.'
end;
UninstallBackButton.Visible :=
(UninstallProgressForm.InnerNotebook.ActivePage = UninstallConfirmPage) or
((UninstallProgressForm.InnerNotebook.ActivePage = UninstallDelDataPage) and (UninstallProcessListPage<>nil));
if (UninstallProgressForm.InnerNotebook.ActivePage = UninstallDelDataPage) or
((UninstallProgressForm.InnerNotebook.ActivePage = UninstallProcessListPage) and (UninstallDelDataPage<>nil)) then
begin
UninstallNextButton.Caption := SetupMessage(msgButtonNext);
UninstallNextButton.ModalResult := mrNone;
end
else
begin
UninstallNextButton.Caption := 'Uninstall';
{ Make the "Uninstall" button break the ShowModal loop }
UninstallNextButton.ModalResult := mrOK;
end;
end;
{-------------------------------------------------------------------------}
procedure CreateUninstallConfirmPage();
var
PageText: TNewStaticText;
begin
{ Create the second page }
if (UninstallConfirmPage<>nil) then UninstallConfirmPage.Free();
UninstallConfirmPage := TNewNotebookPage.Create(UninstallProgressForm);
UninstallConfirmPage.Notebook := UninstallProgressForm.InnerNotebook;
UninstallConfirmPage.Parent := UninstallProgressForm.InnerNotebook;
UninstallConfirmPage.Align := alClient;
PageText := TNewStaticText.Create(UninstallProgressForm);
PageText.Parent := UninstallConfirmPage;
PageText.Top := UninstallProgressForm.StatusLabel.Top;
PageText.Left := UninstallProgressForm.StatusLabel.Left;
PageText.Width := UninstallProgressForm.StatusLabel.Width;
PageText.Height := UninstallProgressForm.StatusLabel.Height;
PageText.AutoSize := True;
PageText.Wordwrap := True;
PageText.ShowAccelChar := False;
PageText.Font.Size := 9;
PageText.Caption := 'Click Uninstall to continue with removal of {#MyAppName}.';
if DeleteAppDataCheckBox.Checked then
PageText.Caption := PageText.Caption + #13#10#10 + 'WARNING: Application data will be deleted.';
end;
{-------------------------------------------------------------------------}
procedure UninstallNextButtonClick(Sender: TObject);
begin
if UninstallProgressForm.InnerNotebook.ActivePage = UninstallConfirmPage then
begin
UninstallNextButton.Visible := False;
UninstallBackButton.Visible := False;
end
else
begin
if UninstallProgressForm.InnerNotebook.ActivePage = UninstallDelDataPage then
begin
CreateUninstallConfirmPage();
UninstallProgressForm.InnerNotebook.ActivePage := UninstallConfirmPage;
end
else if UninstallDelDataPage<>nil then
begin
UninstallProgressForm.InnerNotebook.ActivePage := UninstallDelDataPage;
end
else
begin
UninstallNextButton.Visible := False;
UninstallBackButton.Visible := False;
end;
UpdateUninstallWizard;
end;
end;
{-------------------------------------------------------------------------}
procedure UninstallBackButtonClick(Sender: TObject);
begin
if UninstallProgressForm.InnerNotebook.ActivePage = UninstallConfirmPage then
begin
UninstallProgressForm.InnerNotebook.ActivePage := UninstallDelDataPage;
end
else if (UninstallProgressForm.InnerNotebook.ActivePage = UninstallDelDataPage) and (UninstallProcessListPage<>Nil) then
begin
UninstallProgressForm.InnerNotebook.ActivePage := UninstallProcessListPage;
end;
UpdateUninstallWizard;
end;
{-------------------------------------------------------------------------}
procedure InitializeUninstallProgressForm();
var
PageText: TNewStaticText;
PageNameLabel: string;
PageDescriptionLabel: string;
CancelButtonEnabled: Boolean;
CancelButtonModalResult: Integer;
appinstalled: Boolean;
top : Integer;
ListBox : TNewListBox;
i:Integer;
UninstallNoCloseRB : TNewRadioButton;
begin
RestartManagerHandle:=FindProcessesUsingModule(ExpandConstant('{app}\MyModule.dll'), Processes);
if not UninstallSilent then
begin
{ save labels / description }
PageNameLabel := UninstallProgressForm.PageNameLabel.Caption;
PageDescriptionLabel := UninstallProgressForm.PageDescriptionLabel.Caption;
if RestartManagerHandle<>INVALID_HANDLE_VALUE then
begin
UninstallProcessListPage:=TNewNotebookPage.Create(UninstallProgressForm);
UninstallProcessListPage.Notebook := UninstallProgressForm.InnerNotebook;
UninstallProcessListPage.Parent := UninstallProgressForm.InnerNotebook;
UninstallProcessListPage.Align := alClient;
PageText := TNewStaticText.Create(UninstallProgressForm);
PageText.Parent := UninstallProcessListPage;
PageText.Top := UninstallProgressForm.StatusLabel.Top;
PageText.Left := UninstallProgressForm.StatusLabel.Left;
PageText.Width := UninstallProgressForm.StatusLabel.Width;
PageText.Height := UninstallProgressForm.StatusLabel.Height;
PageText.AutoSize := True;
PageText.WordWrap := True;
PageText.ShowAccelChar := False;
PageText.Caption := 'The following applications are using files that need to be removed by the uninstaller. It is recommended that you allow Uninstall to automatically close these applications. After uninstall has completed, it will attempt to restart the applications.';
ListBox := TNewListBox.Create(UninstallProgressForm);
ListBox.Parent := UninstallProcessListPage;
ListBox.Top := PageText.Top + PageText.Height + 16;
ListBox.Left := PageText.Left;
ListBox.Width := PageText.Width;
ListBox.Height := ScaleY(97);
ListBox.TabStop:=False;
for i:=0 to GetArraylength(Processes)-1 do
begin
ListBox.Items.Add(Processes[i].Name);
end;
ListBox.ItemIndex:=-1;
UninstallAutoCloseRB := TNewRadioButton.Create(UninstallProgressForm);
UninstallAutoCloseRB .Parent := UninstallProcessListPage;
UninstallAutoCloseRB .Checked := True;
UninstallAutoCloseRB .Top := ListBox.Top+ListBox.Height+8;
UninstallAutoCloseRB .Left := ListBox.Left;
UninstallAutoCloseRB .Width := ListBox.Width;
UninstallAutoCloseRB.Font.Size := 9;
UninstallAutoCloseRB.Caption := '&Automatically close the applications';
UninstallNoCloseRB := TNewRadioButton.Create(UninstallProgressForm);
UninstallNoCloseRB .Parent := UninstallProcessListPage;
UninstallNoCloseRB .Checked := False;
UninstallNoCloseRB .Top := UninstallAutoCloseRB.Top+UninstallAutoCloseRB.Height+8;
UninstallNoCloseRB .Left := UninstallAutoCloseRB.Left;
UninstallNoCloseRB .Width := UninstallAutoCloseRB.Width;
UninstallNoCloseRB .Font.Size := 9;
UninstallNoCloseRB .Caption := '&Do not close the applications';
{ make page active }
UninstallProgressForm.InnerNotebook.ActivePage := UninstallProcessListPage;
end;
{ work around elevated uninstaller for single user}
AppData:=GetEnv('AppData');
Log('AppData reported as' + AppData);
if AppData<>'' then
begin
appinstalled:=FileExists(ExpandConstant('{app}/myapp.exe'));
if appinstalled then
begin
{ Create the first page and make it active }
UninstallDelDataPage := TNewNotebookPage.Create(UninstallProgressForm);
UninstallDelDataPage.Notebook := UninstallProgressForm.InnerNotebook;
UninstallDelDataPage.Parent := UninstallProgressForm.InnerNotebook;
UninstallDelDataPage.Align := alClient;
PageText := TNewStaticText.Create(UninstallProgressForm);
PageText.Parent := UninstallDelDataPage;
PageText.Top := UninstallProgressForm.StatusLabel.Top;
PageText.Left := UninstallProgressForm.StatusLabel.Left;
PageText.Width := UninstallProgressForm.StatusLabel.Width;
PageText.Height := UninstallProgressForm.StatusLabel.Height;
PageText.AutoSize := True;
PageText.WordWrap := True;
PageText.ShowAccelChar := False;
PageText.Caption := 'You have the option to delete the data assoicated with App Name';
top:=PageText.Top + PageText.Height + 16;
if appinstalled then
begin
DeleteAppDataCheckBox := TNewCheckBox.Create(UninstallProgressForm);
DeleteAppDataCheckBox.Parent := UninstallDelDataPage;
DeleteAppDataCheckBox.Checked := False;
DeleteAppDataCheckBox.Top := top;
DeleteAppDataCheckBox.Left := PageText.Left+10;
DeleteAppDataCheckBox.Width := PageText.Width - 10;
DeleteAppDataCheckBox.Font.Size := 9;
DeleteAppDataCheckBox.Caption := 'Delete all App Data';
top:=DeleteAppDataCheckBox.Top + DeleteAppDataCheckBox.Height + 8;
end;
{ set as first page if not already set }
if UninstallProgressForm.InnerNotebook.ActivePage=UninstallProgressForm.InstallingPage then
UninstallProgressForm.InnerNotebook.ActivePage:=UninstallDelDataPage;
{ next button }
UninstallNextButton := TNewButton.Create(UninstallProgressForm);
UninstallNextButton.Parent := UninstallProgressForm;
UninstallNextButton.Left := UninstallProgressForm.CancelButton.Left - UninstallProgressForm.CancelButton.Width - ScaleX(10);
UninstallNextButton.Top := UninstallProgressForm.CancelButton.Top;
UninstallNextButton.Width := UninstallProgressForm.CancelButton.Width;
UninstallNextButton.Height := UninstallProgressForm.CancelButton.Height;
UninstallNextButton.OnClick := #UninstallNextButtonClick;
{ back button }
UninstallBackButton := TNewButton.Create(UninstallProgressForm);
UninstallBackButton.Parent := UninstallProgressForm;
UninstallBackButton.Left := UninstallNextButton.Left - UninstallNextButton.Width - ScaleX(10);
UninstallBackButton.Top := UninstallProgressForm.CancelButton.Top;
UninstallBackButton.Width := UninstallProgressForm.CancelButton.Width;
UninstallBackButton.Height := UninstallProgressForm.CancelButton.Height;
UninstallBackButton.Caption := SetupMessage(msgButtonBack);
UninstallBackButton.OnClick := #UninstallBackButtonClick;
{ setup tab order }
UninstallBackButton.TabOrder := UninstallProgressForm.CancelButton.TabOrder;
UninstallNextButton.TabOrder := UninstallBackButton.TabOrder + 1;
UninstallProgressForm.CancelButton.TabOrder := UninstallNextButton.TabOrder + 1;
{ Run our wizard pages }
UpdateUninstallWizard;
CancelButtonEnabled := UninstallProgressForm.CancelButton.Enabled
UninstallProgressForm.CancelButton.Enabled := True;
{ save value }
CancelButtonModalResult := UninstallProgressForm.CancelButton.ModalResult;
{ set value to use }
UninstallProgressForm.CancelButton.ModalResult := mrCancel;
if UninstallProgressForm.ShowModal = mrCancel then
begin
if RestartManagerHandle<>INVALID_HANDLE_VALUE then
begin
RmEndSession(RestartManagerHandle);
RestartManagerHandle:=INVALID_HANDLE_VALUE;
end;
Abort;
end;
{ Restore the standard page payout }
UninstallProgressForm.CancelButton.Enabled := CancelButtonEnabled;
UninstallProgressForm.CancelButton.ModalResult := CancelButtonModalResult;
UninstallProgressForm.PageNameLabel.Caption := PageNameLabel;
UninstallProgressForm.PageDescriptionLabel.Caption := PageDescriptionLabel;
UninstallProgressForm.InnerNotebook.ActivePage := UninstallProgressForm.InstallingPage;
end;
end;
end;
end;
{-------------------------------------------------------------------------}
function IsDeleteAppData: Boolean;
begin
Result:=(DeleteAppDataCheckBox<>nil) and DeleteAppDataCheckBox.Checked;
end;
{-------------------------------------------------------------------------}
procedure CurUninstallStepChanged(CurUninstallStep: TUninstallStep);
var
ErrorCode: Integer;
Res : Boolean;
begin
if CurUninstallStep = usUninstall then
begin
if (RestartManagerHandle<>INVALID_HANDLE_VALUE) and (UninstallAutoCloseRB<>nil) and (UninstallAutoCloseRB.Checked) then
RmShutdown(RestartManagerHandle, 0, 0);
end
else if CurUninstallStep = usPostUninstall then
begin
if IsDeleteAppData then
begin
Res:=DelTree(AppData+'\MyAppDataDir', true, true, true);
Log('Delete '+AppData+'\MyAppDataDir result: '+IntToStr(Integer(Res)));
end;
if (RestartManagerHandle<>INVALID_HANDLE_VALUE) and (UninstallAutoCloseRB<>nil) and (UninstallAutoCloseRB.Checked) then
RmRestart(RestartManagerHandle, 0, 0);
if (RestartManagerHandle<>INVALID_HANDLE_VALUE) then
begin
RmEndSession(RestartManagerHandle);
RestartManagerHandle:=INVALID_HANDLE_VALUE;
end;
end;
end;

Inno Setup - Dynamic positioning of controls/checkboxes

I have a bunch of checkboxes on a page, and all of those are conditionally visible, and the Top position is defined relative to the previous checkbox, e.g.
CheckBox4.Top := CheckBox3.Top + CheckBox3.Height + 5;
When at least one of the components is set to be invisible, the result looks like this:
What I'd like the checkboxes to do, is to shift upwards, if the previous one is set to invisible. Prog3 should be right underneath Prog1, or, if both Prog2 and Prog3 are hidden, Prog4 should be right underneath Prog1.
EDIT: My code after the answer:
var
PageNameLabel,PageDescriptionLabel: TLabel;
TypesComboOnChangePrev: TNotifyEvent;
UninstallConfigssPage: TNewNotebookPage;
UninstallNextButton: TNewButton;
CheckListBox: TNewCheckListBox;
Dirs: TStringList;
procedure UpdateUninstallWizard;
begin
UninstallProgressForm.PageNameLabel.Caption := CustomMessage('UninstPNL');
UninstallProgressForm.PageDescriptionLabel.Caption := CustomMessage('UninstPDL');
UninstallNextButton.Caption := CustomMessage('UninstBtn');
UninstallNextButton.ModalResult := mrOK;
end;
procedure UninstallNextButtonClick(Sender: TObject);
begin
UninstallNextButton.Visible := False;
end;
procedure AddDirCheckbox(Path: string; Caption: string);
begin
if DirExists(Path) then
begin
Dirs.Add(Path);
CheckListBox.AddCheckBox(Caption, '', 0, False, True, False, False, nil);
end;
end;
procedure InitializeUninstallProgressForm();
var
PageText: TNewStaticText;
PageNameLabel,PageDescriptionLabel: string;
CancelButtonEnabled: Boolean;
CancelButtonModalResult: Integer;
begin
if not UninstallSilent then
begin
UninstallProgressForm.Caption := CustomMessage('Uninst');
UninstallConfigssPage:= TNewNotebookPage.Create(UninstallProgressForm);
UninstallConfigssPage.Notebook := UninstallProgressForm.InnerNotebook;
UninstallConfigssPage.Parent := UninstallProgressForm.InnerNotebook;
UninstallConfigssPage.Align := alClient;
PageText := TNewStaticText.Create(UninstallProgressForm);
PageText.Parent := UninstallConfigssPage;
PageText.Top := UninstallProgressForm.StatusLabel.Top;
PageText.Left := UninstallProgressForm.StatusLabel.Left - ScaleX(20);
PageText.Width := UninstallProgressForm.StatusLabel.Width;
PageText.Height := UninstallProgressForm.StatusLabel.Height;
PageText.AutoSize := True;
PageText.ShowAccelChar := False;
PageText.Caption := CustomMessage('UninstTxt');
Dirs := TStringList.Create();
CheckListBox := TNewCheckListBox.Create(UninstallConfigssPage);
CheckListBox.Parent := UninstallConfigssPage;
CheckListBox.SetBounds(PageText.Left,ScaleY(30),PageText.Width,ScaleY(220));
CheckListBox.BorderStyle := bsNone;
CheckListBox.Color := clBtnFace;
CheckListBox.MinItemHeight := ScaleY(20);
AddDirCheckbox(ExpandConstant('{app}\Alien Shooter'), 'Prog1');
AddDirCheckbox(ExpandConstant('{app}\Theseus'), 'Prog2');
AddDirCheckbox(ExpandConstant('{app}\Alien Shooter Revisited'), 'Prog3');
AddDirCheckbox(ExpandConstant('{app}\Alien Shooter 2'), 'Prog4');
AddDirCheckbox(ExpandConstant('{app}\Alien Shooter 2 Reloaded'), 'Prog5');
AddDirCheckbox(ExpandConstant('{app}\Alien Shooter 2 Conscription'), 'Prog6');
AddDirCheckbox(ExpandConstant('{app}\Zombie Shooter'), 'Prog7');
AddDirCheckbox(ExpandConstant('{app}\Zombie Shooter 2'), 'Prog8');
UninstallProgressForm.InnerNotebook.ActivePage := UninstallConfigssPage;
PageNameLabel := UninstallProgressForm.PageNameLabel.Caption;
PageDescriptionLabel := UninstallProgressForm.PageDescriptionLabel.Caption;
UninstallNextButton := TNewButton.Create(UninstallProgressForm);
UninstallNextButton.Parent := UninstallProgressForm;
UninstallNextButton.Left := UninstallProgressForm.CancelButton.Left - UninstallProgressForm.CancelButton.Width - ScaleX(35);
UninstallNextButton.Top := UninstallProgressForm.CancelButton.Top;
UninstallNextButton.Width := UninstallProgressForm.CancelButton.Width + ScaleX(30);
UninstallNextButton.Height := UninstallProgressForm.CancelButton.Height;
UninstallNextButton.OnClick := #UninstallNextButtonClick;
UninstallNextButton.TabOrder := UninstallProgressForm.CancelButton.TabOrder - 1;
UpdateUninstallWizard;
CancelButtonEnabled := UninstallProgressForm.CancelButton.Enabled
UninstallProgressForm.CancelButton.Enabled := True;
CancelButtonModalResult := UninstallProgressForm.CancelButton.ModalResult;
UninstallProgressForm.CancelButton.ModalResult := mrCancel;
if UninstallProgressForm.ShowModal = mrCancel then Abort;
UninstallProgressForm.CancelButton.Enabled := CancelButtonEnabled;
UninstallProgressForm.CancelButton.ModalResult := CancelButtonModalResult;
UninstallProgressForm.PageNameLabel.Caption := PageNameLabel;
UninstallProgressForm.PageDescriptionLabel.Caption := PageDescriptionLabel;
UninstallProgressForm.InnerNotebook.ActivePage := UninstallProgressForm.InstallingPage;
end;
end;
Use TInputOptionWizardPage, which is designed for this kind of tasks/layouts. Create it using CreateInputOptionPage.
Use TStringList (or array of string) to maintain association between the created checkboxes and the paths.
var
Page: TInputOptionWizardPage;
Dirs: TStringList;
procedure AddDirCheckbox(Path: string; Caption: string);
begin
if DirExists(Path) then
begin
Dirs.Add(Path);
Page.Add(Caption);
end;
end;
procedure InitializeWizard();
begin
Page :=
CreateInputOptionPage(
wpWelcome, 'Configuration files found', 'Choose an action for configuration files',
'Choose the configuration files you''d like to be deleted.', False, False);
Dirs := TStringList.Create();
AddDirCheckbox('C:\dir1', 'Prog 1');
AddDirCheckbox('C:\dir2', 'Prog 2');
AddDirCheckbox('C:\dir3', 'Prog 3');
end;
To process the selected checkboxes/paths, use a code like this:
procedure CurStepChanged(CurStep: TSetupStep);
var
Index: Integer;
begin
if CurStep = ssInstall then
begin
for Index := 0 to Dirs.Count - 1 do
begin
if Page.Values[Index] then
begin
MsgBox(Format('Deleting %s', [Dirs[Index]]), mbInformation, MB_OK);
end;
end;
end;
end;
Assuming C:\dir1 and C:\dir3 exist and C:\dir2 does not, you will get:
If you cannot use TInputOptionWizardPage, e.g. because you need the checkboxes on an uninstaller form or on a custom form, just create the TNewCheckListBox (what TInputOptionWizardPage uses internally).
The following example places the TNewCheckListBox on a blank custom TWizardPage, but you can of course place it, wherever you want.
var
Page: TWizardPage;
CheckListBox: TNewCheckListBox;
Dirs: TStringList;
procedure AddDirCheckbox(Path: string; Caption: string);
begin
if DirExists(Path) then
begin
Dirs.Add(Path);
CheckListBox.AddCheckBox(Caption, '', 0, False, True, False, False, nil);
end;
end;
procedure InitializeWizard();
begin
Page :=
CreateCustomPage(
wpWelcome, 'Configuration files found',
'Choose an action for configuration files');
Dirs := TStringList.Create();
CheckListBox := TNewCheckListBox.Create(Page);
CheckListBox.Parent := Page.Surface;
CheckListBox.Width := Page.SurfaceWidth;
CheckListBox.Height := Page.SurfaceHeight;
{ The same styling as used by TInputOptionWizardPage }
CheckListBox.BorderStyle := bsNone;
CheckListBox.Color := clBtnFace;
CheckListBox.WantTabs := True;
CheckListBox.MinItemHeight := ScaleY(22);
AddDirCheckbox('C:\dir1', 'Prog 1');
AddDirCheckbox('C:\dir2', 'Prog 2');
AddDirCheckbox('C:\dir3', 'Prog 3');
end;
procedure CurStepChanged(CurStep: TSetupStep);
var
Index: Integer;
begin
if CurStep = ssInstall then
begin
for Index := 0 to Dirs.Count - 1 do
begin
if CheckListBox.Checked[Index] then
begin
MsgBox(Format('Deleting %s', [Dirs[Index]]), mbInformation, MB_OK);
end;
end;
end;
end;

How to create a OuterNotebook/welcome page in the Inno Setup uninstaller?

I am using this code from the answer by Martin Prikryl to Custom Inno Setup Uninstall page (not MsgBox).
How to modify the first page of this code to show a "Welcome"-like page on the OuterNotebook page (i.e. without the MainPanel header)?
You create the page as any other, except that its parent will be the UninstallProgressForm.OuterNotebook, not the .InnerNotebook.
Tricky part is not how to create the page, but how to implement the Next/Back buttons.
The Next button on the "welcome" page must change the page of the OuterNotebook from the "welcome" page to the UninstallProgressForm.InnerPage. And of course make sure the active page on the .InnerNotebook is the first normal/inner page.
Conversely, the Back button on the first normal/inner page must change the page of the OuterNotebook from the UninstallProgressForm.InnerPage to the "welcome" page.
So modifying my answer to the Custom Inno Setup Uninstall page (not MsgBox) to cater for the above, you will get:
[Files]
Source: "compiler:WizModernImage-IS.bmp"; DestDir: {app}
[Code]
var
UninstallWelcomePage: TNewNotebookPage;
UninstallFirstPage: TNewNotebookPage;
UninstallSecondPage: TNewNotebookPage;
UninstallBackButton: TNewButton;
UninstallNextButton: TNewButton;
procedure UpdateUninstallWizard;
begin
if UninstallProgressForm.InnerNotebook.ActivePage = UninstallFirstPage then
begin
UninstallProgressForm.PageNameLabel.Caption := 'First uninstall wizard page';
UninstallProgressForm.PageDescriptionLabel.Caption :=
'Lorem ipsum dolor sit amet, consectetur adipiscing elit.';
end
else
if UninstallProgressForm.InnerNotebook.ActivePage = UninstallSecondPage then
begin
UninstallProgressForm.PageNameLabel.Caption := 'Second uninstall wizard page';
UninstallProgressForm.PageDescriptionLabel.Caption :=
'Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.';
end;
UninstallBackButton.Visible :=
(UninstallProgressForm.OuterNotebook.ActivePage <> UninstallWelcomePage);
if UninstallProgressForm.InnerNotebook.ActivePage <> UninstallSecondPage then
begin
UninstallNextButton.Caption := SetupMessage(msgButtonNext);
UninstallNextButton.ModalResult := mrNone;
end
else
begin
UninstallNextButton.Caption := 'Uninstall';
// Make the "Uninstall" button break the ShowModal loop
UninstallNextButton.ModalResult := mrOK;
end;
end;
procedure UninstallNextButtonClick(Sender: TObject);
begin
if UninstallProgressForm.OuterNotebook.ActivePage = UninstallWelcomePage then
begin
UninstallProgressForm.OuterNotebook.ActivePage :=
UninstallProgressForm.InnerPage;
UninstallProgressForm.InnerNotebook.ActivePage := UninstallFirstPage;
UpdateUninstallWizard;
end
else
begin
if UninstallProgressForm.InnerNotebook.ActivePage <> UninstallSecondPage then
begin
if UninstallProgressForm.InnerNotebook.ActivePage = UninstallFirstPage then
begin
UninstallProgressForm.InnerNotebook.ActivePage := UninstallSecondPage;
end;
UpdateUninstallWizard;
end
else
begin
UninstallNextButton.Visible := False;
UninstallBackButton.Visible := False;
end;
end;
end;
procedure UninstallBackButtonClick(Sender: TObject);
begin
if UninstallProgressForm.InnerNotebook.ActivePage = UninstallFirstPage then
begin
UninstallProgressForm.OuterNotebook.ActivePage := UninstallWelcomePage;
end
else
if UninstallProgressForm.InnerNotebook.ActivePage = UninstallSecondPage then
begin
UninstallProgressForm.InnerNotebook.ActivePage := UninstallFirstPage;
end;
UpdateUninstallWizard;
end;
procedure InitializeUninstallProgressForm();
var
PageText: TNewStaticText;
UninstallWizardBitmapImage: TBitmapImage;
PageNameLabel: string;
PageDescriptionLabel: string;
CancelButtonEnabled: Boolean;
CancelButtonModalResult: Integer;
begin
if not UninstallSilent then
begin
PageNameLabel := UninstallProgressForm.PageNameLabel.Caption;
PageDescriptionLabel := UninstallProgressForm.PageDescriptionLabel.Caption;
// Create the Welcome page and make it active
UninstallWelcomePage := TNewNotebookPage.Create(UninstallProgressForm);
UninstallWelcomePage.Notebook := UninstallProgressForm.OuterNotebook;
UninstallWelcomePage.Parent := UninstallProgressForm.OuterNotebook;
UninstallWelcomePage.Align := alClient;
UninstallWelcomePage.Color := clWindow;
UninstallWizardBitmapImage := TBitmapImage.Create(UninstallProgressForm);
UninstallWizardBitmapImage.Parent := UninstallWelcomePage;
UninstallWizardBitmapImage.Width := ScaleX(164);
UninstallWizardBitmapImage.Height := ScaleX(314);
UninstallWizardBitmapImage.Bitmap.LoadFromFile(
ExpandConstant('{app}\WizModernImage-IS.bmp'));
UninstallWizardBitmapImage.Center := True;
UninstallWizardBitmapImage.Stretch := True;
PageText := TNewStaticText.Create(UninstallProgressForm);
PageText.Parent := UninstallWelcomePage;
PageText.Left := ScaleX(176);
PageText.Top := ScaleX(16);
PageText.Width := ScaleX(301);
PageText.Height := ScaleX(54);
PageText.AutoSize := False;
PageText.Caption := 'Welcome to the My Program uninstall wizard';
PageText.ShowAccelChar := False;
PageText.WordWrap := True;
PageText.Font.Name := 'Verdana';
PageText.Font.Size := 12;
PageText.Font.Style := [fsBold];
PageText := TNewStaticText.Create(UninstallProgressForm);
PageText.Parent := UninstallWelcomePage;
PageText.Left := ScaleX(176);
PageText.Top := ScaleX(76);
PageText.Width := ScaleX(301);
PageText.Height := ScaleX(234);
PageText.AutoSize := False;
PageText.Caption :=
'This will uninstall My Program 1.5 from your computer.'#13#10#13#10 +
'It is recommended that your close all other applications ' +
'before continuing.'#13#10#13#10 +
'Click Next to continue, or Cancel to exit Uninstall.';
PageText.ShowAccelChar := False;
PageText.WordWrap := True;
UninstallProgressForm.OuterNotebook.ActivePage := UninstallWelcomePage;
// Create the first page
UninstallFirstPage := TNewNotebookPage.Create(UninstallProgressForm);
UninstallFirstPage.Notebook := UninstallProgressForm.InnerNotebook;
UninstallFirstPage.Parent := UninstallProgressForm.InnerNotebook;
UninstallFirstPage.Align := alClient;
PageText := TNewStaticText.Create(UninstallProgressForm);
PageText.Parent := UninstallFirstPage;
PageText.Top := UninstallProgressForm.StatusLabel.Top;
PageText.Left := UninstallProgressForm.StatusLabel.Left;
PageText.Width := UninstallProgressForm.StatusLabel.Width;
PageText.Height := UninstallProgressForm.StatusLabel.Height;
PageText.AutoSize := False;
PageText.ShowAccelChar := False;
PageText.Caption := 'Press Next to proceeed with uninstallation.';
// Create the second page
UninstallSecondPage := TNewNotebookPage.Create(UninstallProgressForm);
UninstallSecondPage.Notebook := UninstallProgressForm.InnerNotebook;
UninstallSecondPage.Parent := UninstallProgressForm.InnerNotebook;
UninstallSecondPage.Align := alClient;
PageText := TNewStaticText.Create(UninstallProgressForm);
PageText.Parent := UninstallSecondPage;
PageText.Top := UninstallProgressForm.StatusLabel.Top;
PageText.Left := UninstallProgressForm.StatusLabel.Left;
PageText.Width := UninstallProgressForm.StatusLabel.Width;
PageText.Height := UninstallProgressForm.StatusLabel.Height;
PageText.AutoSize := False;
PageText.ShowAccelChar := False;
PageText.Caption := 'Press Uninstall to proceeed with uninstallation.';
UninstallNextButton := TNewButton.Create(UninstallProgressForm);
UninstallNextButton.Parent := UninstallProgressForm;
UninstallNextButton.Left :=
UninstallProgressForm.CancelButton.Left -
UninstallProgressForm.CancelButton.Width -
ScaleX(10);
UninstallNextButton.Top := UninstallProgressForm.CancelButton.Top;
UninstallNextButton.Width := UninstallProgressForm.CancelButton.Width;
UninstallNextButton.Height := UninstallProgressForm.CancelButton.Height;
UninstallNextButton.OnClick := #UninstallNextButtonClick;
UninstallBackButton := TNewButton.Create(UninstallProgressForm);
UninstallBackButton.Parent := UninstallProgressForm;
UninstallBackButton.Left :=
UninstallNextButton.Left - UninstallNextButton.Width - ScaleX(10);
UninstallBackButton.Top := UninstallProgressForm.CancelButton.Top;
UninstallBackButton.Width := UninstallProgressForm.CancelButton.Width;
UninstallBackButton.Height := UninstallProgressForm.CancelButton.Height;
UninstallBackButton.Caption := SetupMessage(msgButtonBack);
UninstallBackButton.OnClick := #UninstallBackButtonClick;
UninstallBackButton.TabOrder := UninstallProgressForm.CancelButton.TabOrder;
UninstallNextButton.TabOrder := UninstallBackButton.TabOrder + 1;
UninstallProgressForm.CancelButton.TabOrder :=
UninstallNextButton.TabOrder + 1;
// Run our wizard pages
UpdateUninstallWizard;
CancelButtonEnabled := UninstallProgressForm.CancelButton.Enabled
UninstallProgressForm.CancelButton.Enabled := True;
CancelButtonModalResult := UninstallProgressForm.CancelButton.ModalResult;
UninstallProgressForm.CancelButton.ModalResult := mrCancel;
if UninstallProgressForm.ShowModal = mrCancel then Abort;
// Restore the standard page payout
UninstallProgressForm.CancelButton.Enabled := CancelButtonEnabled;
UninstallProgressForm.CancelButton.ModalResult := CancelButtonModalResult;
UninstallProgressForm.PageNameLabel.Caption := PageNameLabel;
UninstallProgressForm.PageDescriptionLabel.Caption := PageDescriptionLabel;
UninstallProgressForm.InnerNotebook.ActivePage :=
UninstallProgressForm.InstallingPage;
end;
end;
Note that there's no way to use the built-in images from the installer in the uninstaller. In my simple code above, I install the "welcome" page image to the {app} and load it from there.
If you want to avoid this, see my answer to the How keep uninstall files inside uninstaller?

Custom Inno Setup Uninstall page (not MsgBox)

I need to ask the user for a password during install, which is then used as part of a command which runs after installation. I'm using a custom page to do this and it works fine.
I also need to ask the same question during an uninstall, which is used as part of a command which runs after uninstall.
I've checked the help and there does not seem to be a PageID for uninstalls, which I can use in my CreateInputQuery function. I don't particularly mind, if the page is displayed at the start, middle or end of the uninstall, as long as it is displayed.
I don't want to use the MsgBox for the uninstall as I want the look and feel of a standard page.
Any tips on how I can achieve this?
You can modify the uninstall form to behave like the install form (with pages and the Next/Back buttons).
In the InitializeUninstallProgressForm:
Create the new pages and insert them to the UninstallProgressForm.InnerNotebook (or the .OuterNotebook).
Implement the "Next" and the "Back" buttons.
You can also make the "Cancel" button working.
Run modal loop of the form using UninstallProgressForm.ShowModal.
Only after the modal loop exits, restore original layout of the form and let the uninstallation continue.
[Code]
var
UninstallFirstPage: TNewNotebookPage;
UninstallSecondPage: TNewNotebookPage;
UninstallBackButton: TNewButton;
UninstallNextButton: TNewButton;
procedure UpdateUninstallWizard;
begin
if UninstallProgressForm.InnerNotebook.ActivePage = UninstallFirstPage then
begin
UninstallProgressForm.PageNameLabel.Caption := 'First uninstall wizard page';
UninstallProgressForm.PageDescriptionLabel.Caption :=
'Lorem ipsum dolor sit amet, consectetur adipiscing elit.';
end
else
if UninstallProgressForm.InnerNotebook.ActivePage = UninstallSecondPage then
begin
UninstallProgressForm.PageNameLabel.Caption := 'Second uninstall wizard page';
UninstallProgressForm.PageDescriptionLabel.Caption :=
'Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.';
end;
UninstallBackButton.Visible :=
(UninstallProgressForm.InnerNotebook.ActivePage <> UninstallFirstPage);
if UninstallProgressForm.InnerNotebook.ActivePage <> UninstallSecondPage then
begin
UninstallNextButton.Caption := SetupMessage(msgButtonNext);
UninstallNextButton.ModalResult := mrNone;
end
else
begin
UninstallNextButton.Caption := 'Uninstall';
// Make the "Uninstall" button break the ShowModal loop
UninstallNextButton.ModalResult := mrOK;
end;
end;
procedure UninstallNextButtonClick(Sender: TObject);
begin
if UninstallProgressForm.InnerNotebook.ActivePage = UninstallSecondPage then
begin
UninstallNextButton.Visible := False;
UninstallBackButton.Visible := False;
end
else
begin
if UninstallProgressForm.InnerNotebook.ActivePage = UninstallFirstPage then
begin
UninstallProgressForm.InnerNotebook.ActivePage := UninstallSecondPage;
end;
UpdateUninstallWizard;
end;
end;
procedure UninstallBackButtonClick(Sender: TObject);
begin
if UninstallProgressForm.InnerNotebook.ActivePage = UninstallSecondPage then
begin
UninstallProgressForm.InnerNotebook.ActivePage := UninstallFirstPage;
end;
UpdateUninstallWizard;
end;
procedure InitializeUninstallProgressForm();
var
PageText: TNewStaticText;
PageNameLabel: string;
PageDescriptionLabel: string;
CancelButtonEnabled: Boolean;
CancelButtonModalResult: Integer;
begin
if not UninstallSilent then
begin
// Create the first page and make it active
UninstallFirstPage := TNewNotebookPage.Create(UninstallProgressForm);
UninstallFirstPage.Notebook := UninstallProgressForm.InnerNotebook;
UninstallFirstPage.Parent := UninstallProgressForm.InnerNotebook;
UninstallFirstPage.Align := alClient;
PageText := TNewStaticText.Create(UninstallProgressForm);
PageText.Parent := UninstallFirstPage;
PageText.Top := UninstallProgressForm.StatusLabel.Top;
PageText.Left := UninstallProgressForm.StatusLabel.Left;
PageText.Width := UninstallProgressForm.StatusLabel.Width;
PageText.Height := UninstallProgressForm.StatusLabel.Height;
PageText.AutoSize := False;
PageText.ShowAccelChar := False;
PageText.Caption := 'Press Next to proceeed with uninstallation.';
UninstallProgressForm.InnerNotebook.ActivePage := UninstallFirstPage;
PageNameLabel := UninstallProgressForm.PageNameLabel.Caption;
PageDescriptionLabel := UninstallProgressForm.PageDescriptionLabel.Caption;
// Create the second page
UninstallSecondPage := TNewNotebookPage.Create(UninstallProgressForm);
UninstallSecondPage.Notebook := UninstallProgressForm.InnerNotebook;
UninstallSecondPage.Parent := UninstallProgressForm.InnerNotebook;
UninstallSecondPage.Align := alClient;
PageText := TNewStaticText.Create(UninstallProgressForm);
PageText.Parent := UninstallSecondPage;
PageText.Top := UninstallProgressForm.StatusLabel.Top;
PageText.Left := UninstallProgressForm.StatusLabel.Left;
PageText.Width := UninstallProgressForm.StatusLabel.Width;
PageText.Height := UninstallProgressForm.StatusLabel.Height;
PageText.AutoSize := False;
PageText.ShowAccelChar := False;
PageText.Caption := 'Press Uninstall to proceeed with uninstallation.';
UninstallNextButton := TNewButton.Create(UninstallProgressForm);
UninstallNextButton.Parent := UninstallProgressForm;
UninstallNextButton.Left :=
UninstallProgressForm.CancelButton.Left -
UninstallProgressForm.CancelButton.Width -
ScaleX(10);
UninstallNextButton.Top := UninstallProgressForm.CancelButton.Top;
UninstallNextButton.Width := UninstallProgressForm.CancelButton.Width;
UninstallNextButton.Height := UninstallProgressForm.CancelButton.Height;
UninstallNextButton.OnClick := #UninstallNextButtonClick;
UninstallBackButton := TNewButton.Create(UninstallProgressForm);
UninstallBackButton.Parent := UninstallProgressForm;
UninstallBackButton.Left :=
UninstallNextButton.Left - UninstallNextButton.Width -
ScaleX(10);
UninstallBackButton.Top := UninstallProgressForm.CancelButton.Top;
UninstallBackButton.Width := UninstallProgressForm.CancelButton.Width;
UninstallBackButton.Height := UninstallProgressForm.CancelButton.Height;
UninstallBackButton.Caption := SetupMessage(msgButtonBack);
UninstallBackButton.OnClick := #UninstallBackButtonClick;
UninstallBackButton.TabOrder := UninstallProgressForm.CancelButton.TabOrder;
UninstallNextButton.TabOrder := UninstallBackButton.TabOrder + 1;
UninstallProgressForm.CancelButton.TabOrder :=
UninstallNextButton.TabOrder + 1;
// Run our wizard pages
UpdateUninstallWizard;
CancelButtonEnabled := UninstallProgressForm.CancelButton.Enabled
UninstallProgressForm.CancelButton.Enabled := True;
CancelButtonModalResult := UninstallProgressForm.CancelButton.ModalResult;
UninstallProgressForm.CancelButton.ModalResult := mrCancel;
if UninstallProgressForm.ShowModal = mrCancel then Abort;
// Restore the standard page payout
UninstallProgressForm.CancelButton.Enabled := CancelButtonEnabled;
UninstallProgressForm.CancelButton.ModalResult := CancelButtonModalResult;
UninstallProgressForm.PageNameLabel.Caption := PageNameLabel;
UninstallProgressForm.PageDescriptionLabel.Caption := PageDescriptionLabel;
UninstallProgressForm.InnerNotebook.ActivePage :=
UninstallProgressForm.InstallingPage;
end;
end;
See also How to create a OuterNotebook/welcome page in the Inno Setup uninstaller?
Luckily Inno Setup gives enough abilities to build own forms so you can imitate any page of uninstall form on another modal form.
Here's what I managed to create. The form takes dimensions from UninstallForm, contains a TNewNotebook control and buttons to jump between pages and to cancel the dialog.
const
ControlGap = 5; // determined empirically
// Set Back/Next buttons state according to current selected notebook page
procedure UpdateButtonsState(Form: TSetupForm);
var
Notebook: TNewNotebook;
BtnBack, BtnNext: TButton;
begin
Notebook := TNewNotebook(Form.FindComponent('Notebook'));
BtnBack := TButton(Form.FindComponent('BtnBack'));
BtnNext := TButton(Form.FindComponent('BtnNext'));
// Update buttons state
BtnBack.Enabled := (Notebook.ActivePage <> Notebook.Pages[0]);
if Notebook.ActivePage <> Notebook.Pages[Notebook.PageCount - 1] then
begin
BtnNext.Caption := SetupMessage(msgButtonNext)
BtnNext.ModalResult := mrNone;
end
else
begin
BtnNext.Caption := SetupMessage(msgButtonFinish);
BtnNext.ModalResult := mrYes;
end;
end;
// Change notebook page
procedure BtnPageChangeClick(Sender: TObject);
var
NextPage: TNewNotebookPage;
Notebook: TNewNotebook;
Form: TWinControl;
Button, BtnBack, BtnNext: TButton;
begin
Button := TButton(Sender);
Form := Button;
while not (Form is TSetupForm) do
Form := Form.Parent;
Notebook := TNewNotebook(Form.FindComponent('Notebook'));
BtnBack := TButton(Form.FindComponent('BtnBack'));
BtnNext := TButton(Form.FindComponent('BtnNext'));
// Avoid cycled style of Notebook's page looping
if (Button = BtnBack) and (Notebook.ActivePage = Notebook.Pages[0]) then
NextPage := nil
else
if (Button = BtnNext) and (Notebook.ActivePage = Notebook.Pages[Notebook.PageCount - 1]) then
NextPage := nil
else
NextPage := Notebook.FindNextPage(Notebook.ActivePage, Button = BtnNext);
Notebook.ActivePage := NextPage;
UpdateButtonsState(TSetupForm(Form));
end;
// Add a new page to notebook and return it
function AddPage(NotebookForm: TSetupForm): TNewNotebookPage;
var
Notebook: TNewNotebook;
begin
Notebook := TNewNotebook(NotebookForm.FindComponent('Notebook'));
Result := TNewNotebookPage.Create(Notebook);
Result.Notebook:=Notebook;
Result.Parent:=Notebook;
Result.Align := alClient;
if Notebook.ActivePage = nil then
Notebook.ActivePage := Result;
UpdateButtonsState(NotebookForm);
end;
// Create a form with notebook and 3 buttons: Back, Next, Cancel
function CreateNotebookForm: TSetupForm;
var
Notebook: TNewNotebook;
NotebookPage: TNewNotebookPage;
Pan: TPanel;
TmpLabel: TLabel;
MaxWidth, i: Integer;
BtnBack, BtnNext, BtnCancel: TButton;
BtnLabelMsgIDs: array of TSetupMessageID;
begin
Result := CreateCustomForm;
Result.SetBounds(0, 0, UninstallProgressForm.Width, UninstallProgressForm.Height);
Result.Position := poOwnerFormCenter;
Notebook := TNewNotebook.Create(Result);
Notebook.Parent := Result;
Notebook.Name := 'Notebook'; // will be used for searching
Notebook.Align := alClient;
Pan := TPanel.Create(Result);
Pan.Parent := Notebook;
Pan.Caption := '';
Pan.Align := alBottom;
// Create buttons
BtnNext := TNewButton.Create(Result);
with BtnNext do
begin
Parent := Pan;
Name := 'BtnNext'; // will be used for searching
Caption := SetupMessage(msgButtonNext);
OnClick := #BtnPageChangeClick;
ParentFont := True;
end;
BtnBack := TNewButton.Create(Result);
with BtnBack do
begin
Parent := Pan;
Caption := SetupMessage(msgButtonBack);
Name := 'BtnBack'; // will be used for searching
OnClick := #BtnPageChangeClick;
ParentFont := True;
end;
BtnCancel := TNewButton.Create(Result);
with BtnCancel do
begin
Parent := Pan;
Name := 'BtnCancel'; // will be used for searching
Caption := SetupMessage(msgButtonCancel);
ModalResult := mrCancel;
Cancel := True;
ParentFont := True;
end;
// Determine dimensions of buttons. Should use TCanvas.TextWidth here
// but it doesn't allow Font property assignment :(
TmpLabel := TLabel.Create(Result);
with TmpLabel do
begin
Left := 0;
Top := 0;
Parent := Pan;
ParentFont := True;
Visible := False;
WordWrap := False;
Autosize := True;
end;
// Determine max label width among these labels: Back, Next, Cancel, Finish
SetArrayLength(BtnLabelMsgIDs, 4);
BtnLabelMsgIDs[0] := msgButtonBack;
BtnLabelMsgIDs[1] := msgButtonNext;
BtnLabelMsgIDs[2] := msgButtonCancel;
BtnLabelMsgIDs[3] := msgButtonFinish;
MaxWidth := 0;
for i := Low(BtnLabelMsgIDs) to High(BtnLabelMsgIDs) do
begin
TmpLabel.Caption := SetupMessage(BtnLabelMsgIDs[i]) + 'WWW'; // Add letters for surrounding spaces
if MaxWidth < TmpLabel.Width then
MaxWidth := TmpLabel.Width;
end;
TmpLabel.Caption := 'Yy'; // Determine height
// Assign sizes and positions
Pan.ClientHeight := TmpLabel.Height*4;
with BtnBack do
begin
Width := MaxWidth;
Height := TmpLabel.Height*2;
Left := Parent.ClientWidth - 3*(MaxWidth + ScaleX(ControlGap));
Top := (Parent.ClientHeight - Height) div 2;
end;
with BtnNext do
begin
Width := MaxWidth;
Height := TmpLabel.Height*2;
Left := Parent.ClientWidth - 2*(MaxWidth + ScaleX(ControlGap));
Top := (Parent.ClientHeight - Height) div 2;
end;
with BtnCancel do
begin
Width := MaxWidth;
Height := TmpLabel.Height*2;
Left := Parent.ClientWidth - 1*(MaxWidth + ScaleX(ControlGap));
Top := (Parent.ClientHeight - Height) div 2;
end;
end;
Usage is like
// UninstallProgressForm is about to be shown
// Show modal dialog which allows to select additional components to uninstall
procedure InitializeUninstallProgressForm;
var
Form: TSetupForm;
i: Integer;
NotebookPage: TNewNotebookPage;
ModResult: Integer;
begin
Form := CreateNotebookForm;
for i := 1 to 4 do
begin
NotebookPage := AddPage(Form);
with NotebookPage do
begin
Color := clWindow;
with TLabel.Create(Form) do
begin
Parent := NotebookPage;
SetBounds(0, 0, 50, 30);
Autosize := true;
Font.Size := 14;
Caption := 'Label ' + IntToStr(i);
end;
end;
end;
ModResult := Form.ShowModal;
if ModResult = mrYes then
MsgBox('Continuing uninstall', mbInformation, MB_OK)
else
begin
MsgBox('Cancelled', mbInformation, MB_OK);
Abort;
end;
...
end;
Inno does not currently support wizard pages during uninstall. You will need to use Forms instead.
I'm using InnoSetup 5.4.2 and in the docs are several Uninstall Event Functions including:
procedure CurUninstallStepChanged(CurUninstallStep: TUninstallStep);
You should be able to create an input page within the [code] section.
Just in case that you only need one page, we can simplify Martin Prikryl answer.
It still is the same process but it's all implemented in the InitializeUninstallProgressForm:
Create the Uninstall button.
Create the new page and insert-it to the UninstallProgressForm.InnerNotebook (or the .OuterNotebook).
Add controls to the page.
You can also make the "Cancel" button working.
Run modal loop of the form using UninstallProgressForm.ShowModal.
Only after the modal loop exits, restore original layout of the form and let the uninstallation continue.
[Code]
procedure InitializeUninstallProgressForm();
var
UninstallPage: TNewNotebookPage;
UninstallButton: TNewButton;
OriginalPageNameLabel: string;
OriginalPageDescriptionLabel: string;
OriginalCancelButtonEnabled: Boolean;
OriginalCancelButtonModalResult: Integer;
ctrl: TWinControl;
begin
if not UninstallSilent then
begin
{ Create Uninstall button }
ctrl := UninstallProgressForm.CancelButton;
UninstallButton := TNewButton.Create(UninstallProgressForm)
UninstallButton.Parent := UninstallProgressForm;
UninstallButton.Left := ctrl.Left - ctrl.Width - ScaleX(10);
UninstallButton.Top := ctrl.Top;
UninstallButton.Width := ctrl.Width;
UninstallButton.Height := ctrl.Height;
UninstallButton.TabOrder := ctrl.TabOrder;
UninstallButton.Caption := 'Uninstall';
{ Make the "Uninstall" button break the ShowModal loop }
UninstallButton.ModalResult := mrOK;
UninstallProgressForm.CancelButton.TabOrder := UninstallButton.TabOrder + 1;
{ Create the page and make it active }
UninstallPage := TNewNotebookPage.Create(UninstallProgressForm);
UninstallPage.Notebook := UninstallProgressForm.InnerNotebook;
UninstallPage.Parent := UninstallProgressForm.InnerNotebook;
UninstallPage.Align := alClient;
UninstallProgressForm.InnerNotebook.ActivePage := UninstallPage;
{ Create and add a controls to the page }
ctrl := UninstallProgressForm.StatusLabel;
with TNewStaticText.Create(UninstallProgressForm) do
begin
Parent := UninstallPage;
Top := ctrl.Top;
Left := ctrl.Left;
Width := ctrl.Width;
Height := ctrl.Height;
AutoSize := False;
ShowAccelChar := False;
Caption := 'Press Next to proceeed with uninstallation.';
end;
{ Save state }
OriginalPageNameLabel := UninstallProgressForm.PageNameLabel.Caption;
OriginalPageDescriptionLabel := UninstallProgressForm.PageDescriptionLabel.Caption;
OriginalCancelButtonEnabled := UninstallProgressForm.CancelButton.Enabled;
OriginalCancelButtonModalResult := UninstallProgressForm.CancelButton.ModalResult;
{ Run our wizard pages }
UninstallProgressForm.PageNameLabel.Caption := 'First uninstall wizard page';
UninstallProgressForm.PageDescriptionLabel.Caption := 'Lorem ipsum dolor sit amet, consectetur adipiscing elit.';
UninstallProgressForm.CancelButton.Enabled := True;
UninstallProgressForm.CancelButton.ModalResult := mrCancel;
{ Show the form }
if UninstallProgressForm.ShowModal = mrCancel then Abort;
{ Restore the standard page layout }
UninstallButton.Visible := False;
UninstallProgressForm.PageNameLabel.Caption := OriginalPageNameLabel;
UninstallProgressForm.PageDescriptionLabel.Caption := OriginalPageDescriptionLabel;
UninstallProgressForm.CancelButton.Enabled := OriginalCancelButtonEnabled;
UninstallProgressForm.CancelButton.ModalResult := OriginalCancelButtonModalResult;
UninstallProgressForm.InnerNotebook.ActivePage := UninstallProgressForm.InstallingPage;
end;
end;

Resources