installationinno-setup

Is it possible to display the install actions in a list in Inno Setup?


Is it possible to list the progress like it does in other installers?

enter image description here


Solution

  • You can monitor changes in WizardForm.StatusLabel and WizardForm.FilenameLabel and add them to your custom list box progress view.

    const
      GWL_WNDPROC = -4;
      SB_VERT = 1;
      SB_BOTTOM = 7;
      WM_VSCROLL = $0115;
      WM_ERASEBKGND = $0014;
    
    type
      WPARAM = UINT_PTR;
      LPARAM = LongInt;
      LRESULT = LongInt;
    
    var
      OldStatusLabelWndProc: LongInt;
      OldFilenameLabelWndProc: LongInt;
      OldProgressListBoxWndProc: LongInt;
      ProgressListBox: TNewListBox;
      PrevStatus: string;
      PrevFileName: string;
    
    function CallWindowProc(
      lpPrevWndFunc: LongInt; hWnd: HWND; Msg: UINT; wParam: WPARAM;
      lParam: LPARAM): LRESULT; external 'CallWindowProcW@user32.dll stdcall';  
    function SetWindowLong(hWnd: HWND; nIndex: Integer; dwNewLong: LongInt): LongInt;
      external 'SetWindowLongW@user32.dll stdcall';
    
    procedure AddProgress(S: string);
    begin
      if S <> '' then
      begin
        ProgressListBox.Items.Add(S);
        ProgressListBox.ItemIndex := ProgressListBox.Items.Count;
        SendMessage(ProgressListBox.Handle, WM_VSCROLL, SB_BOTTOM, 0);
      end;
    end;
    
    function StatusLabelWndProc(
      hwnd: HWND; uMsg: UINT; wParam: WPARAM; lParam: LPARAM): LRESULT;
    begin
      Result := CallWindowProc(OldStatusLabelWndProc, hwnd, uMsg, wParam, lParam);
      if PrevStatus <> WizardForm.StatusLabel.Caption then
      begin
        AddProgress(WizardForm.StatusLabel.Caption);
        PrevStatus := WizardForm.StatusLabel.Caption;
      end;
    end;
    
    function FilenameLabelWndProc(
      hwnd: HWND; uMsg: UINT; wParam: WPARAM; lParam: LPARAM): LRESULT;
    begin
      Result := CallWindowProc(OldFilenameLabelWndProc, hwnd, uMsg, wParam, lParam);
      if PrevFileName <> WizardForm.FilenameLabel.Caption then
      begin
        AddProgress(WizardForm.FilenameLabel.Caption);
        PrevFileName := WizardForm.FilenameLabel.Caption;
      end;
    end;
    
    function ProgressListBoxWndProc(
      hwnd: HWND; uMsg: UINT; wParam: WPARAM; lParam: LPARAM): LRESULT;
    begin
      // reduce flicker
      if uMsg = WM_ERASEBKGND then
      begin
        Result := 1;
      end
        else
      begin
        Result :=
          CallWindowProc(OldProgressListBoxWndProc, hwnd, uMsg, wParam, lParam);
      end;
    end;
    
    procedure InitializeWizard();
    begin
      OldStatusLabelWndProc :=
        SetWindowLong(WizardForm.StatusLabel.Handle, GWL_WNDPROC,
          CreateCallback(@StatusLabelWndProc));
      OldFilenameLabelWndProc :=
        SetWindowLong(WizardForm.FilenameLabel.Handle, GWL_WNDPROC,
          CreateCallback(@FilenameLabelWndProc));
    
      WizardForm.ProgressGauge.Top := WizardForm.FilenameLabel.Top;
    
      ProgressListBox := TNewListBox.Create(WizardForm);
      ProgressListBox.Parent := WizardForm.ProgressGauge.Parent;
      ProgressListBox.Top :=
        WizardForm.ProgressGauge.Top + WizardForm.ProgressGauge.Height + ScaleY(8);
      ProgressListBox.Width := WizardForm.FilenameLabel.Width;
      ProgressListBox.Height :=
        ProgressListBox.Parent.ClientHeight - ProgressListBox.Top - ScaleY(16);
      ProgressListBox.Anchors := [akLeft, akTop, akRight, akBottom];
      OldProgressListBoxWndProc :=
        SetWindowLong(ProgressListBox.Handle, GWL_WNDPROC,
          CreateCallback(@ProgressListBoxWndProc));
      // Lame way to shrink width of labels to client width of the list box,
      // so that particularly when the file paths in FilenameLabel are shortened
      // to fit to the label, they actually fit even to the list box.
      WizardForm.StatusLabel.Width := WizardForm.StatusLabel.Width - ScaleY(24);
      WizardForm.FilenameLabel.Width := WizardForm.FilenameLabel.Width - ScaleY(24);
    end;
    
    procedure DeinitializeSetup();
    begin
      // In case you are using VCL styles or similar, this needs to be done before
      // you unload the style.
      SetWindowLong(
        WizardForm.StatusLabel.Handle, GWL_WNDPROC, OldStatusLabelWndProc);
      SetWindowLong(
        WizardForm.FilenameLabel.Handle, GWL_WNDPROC, OldFilenameLabelWndProc);
      SetWindowLong(
        ProgressListBox.Handle, GWL_WNDPROC, OldProgressListBoxWndProc);
    end;
    

    enter image description here