8

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?

Martin Prikryl
  • 188,800
  • 56
  • 490
  • 992
eyoopmeduck
  • 135
  • 2
  • 4

5 Answers5

13

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;

First page

Second page

Uninstalling


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

Martin Prikryl
  • 188,800
  • 56
  • 490
  • 992
6

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.

demo

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;
Martin Prikryl
  • 188,800
  • 56
  • 490
  • 992
Fr0sT
  • 2,959
  • 2
  • 25
  • 18
  • Interesting idea! Just, if you can *intercept* the uninstallation process this way, wouldn't be better to embed, let's say a `TPanel` stretched over the client area of the `UninstallProgressForm` rather than placing another form above the `UninstallProgressForm` ? Don't you have two separate (movable) forms with this code ? – TLama Oct 27 '14 at 13:07
  • @TLama sure, that was the 1st thing I tried but unfortunately that won't work. The uninstaller code just unconditionally goes further after showing UninstallForm regardless of any our actions. So it seems that currently the only way to intrude the uninstallation process is modal forms :( – Fr0sT Oct 27 '14 at 15:24
2

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.

mirtheil
  • 8,952
  • 1
  • 30
  • 29
  • I'm also using this version. Sorry, should have mentioned this in the original question. So, rather than define the dialog (and where it fits into the sequence of pages) in my InitializeWizard procedure, I manually code it using CreateCustomForm. I think the CodeClasses.iss sample can help. Thanks for the suggestion - I'll give it a go... – eyoopmeduck Sep 14 '11 at 14:32
2

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

Deanna
  • 23,876
  • 7
  • 71
  • 156
0

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;
PLopes
  • 93
  • 2
  • 6