Browse Source

Split main form and main functions+vars.

Martijn Laan 1 year ago
parent
commit
c745379b5f

+ 1 - 0
Projects/Setup.dpr

@@ -20,6 +20,7 @@ uses
   Shared.CommonFunc.Vcl in 'Src\Shared.CommonFunc.Vcl.pas',
   Shared.CommonFunc in 'Src\Shared.CommonFunc.pas',
   Setup.MainForm in 'Src\Setup.MainForm.pas' {MainForm},
+  Setup.MainFunc in 'Src\Setup.MainFunc.pas',
   Setup.Install in 'Src\Setup.Install.pas',
   SetupLdrAndSetup.Messages in 'Src\SetupLdrAndSetup.Messages.pas',
   Shared.SetupMessageIDs in 'Src\Shared.SetupMessageIDs.pas',

+ 1 - 0
Projects/Setup.dproj

@@ -81,6 +81,7 @@
         <DCCReference Include="Src\Setup.MainForm.pas">
             <Form>MainForm</Form>
         </DCCReference>
+        <DCCReference Include="Src\Setup.MainFunc.pas"/>
         <DCCReference Include="Src\Setup.Install.pas"/>
         <DCCReference Include="Src\SetupLdrAndSetup.Messages.pas"/>
         <DCCReference Include="Src\Shared.SetupMessageIDs.pas"/>

+ 1 - 1
Projects/Src/Setup.DebugClient.pas

@@ -36,7 +36,7 @@ procedure SetDebugServerWnd(Wnd: HWND; WantCodeText: Boolean);
 implementation
 
 uses
-  Forms, Classes, Shared.CommonFunc, Shared.Struct, Setup.InstFunc, Setup.MainForm;
+  Forms, Classes, Shared.CommonFunc, Shared.Struct, Setup.InstFunc, Setup.MainFunc;
 
 type
   TDummyClass = class

+ 1 - 1
Projects/Src/Setup.FileExtractor.pas

@@ -50,7 +50,7 @@ procedure FreeFileExtractor;
 implementation
 
 uses
-  PathFunc, Shared.CommonFunc, Setup.MainForm, SetupLdrAndSetup.Messages, Shared.SetupMessageIDs,
+  PathFunc, Shared.CommonFunc, Setup.MainFunc, SetupLdrAndSetup.Messages, Shared.SetupMessageIDs,
   Setup.InstFunc, Compression.Zlib, Compression.bzlib,
   Compression.LZMADecompressor, SHA1, Setup.LoggingFunc, Setup.NewDiskForm;
 

+ 1 - 1
Projects/Src/Setup.Helper.pas

@@ -31,7 +31,7 @@ implementation
 {x$DEFINE HELPERDEBUG}
 
 uses
-  Forms, Shared.Int64Em, Shared.CommonFunc.Vcl, Shared.CommonFunc, PathFunc, Setup.MainForm, Setup.InstFunc,
+  Forms, Shared.Int64Em, Shared.CommonFunc.Vcl, Shared.CommonFunc, PathFunc, Setup.MainFunc, Setup.InstFunc,
   Setup.LoggingFunc, SetupLdrAndSetup.Messages, Shared.SetupMessageIDs;
 
 const

+ 1 - 1
Projects/Src/Setup.InstFunc.Ole.pas

@@ -23,7 +23,7 @@ function UnpinShellLink(const Filename: String): Boolean;
 implementation
 
 uses
-  Windows, SysUtils, PathFunc, Shared.CommonFunc, Setup.InstFunc, Setup.MainForm,
+  Windows, SysUtils, PathFunc, Shared.CommonFunc, Setup.InstFunc, Setup.MainFunc,
   SetupLdrAndSetup.Messages, Shared.SetupMessageIDs,
   ActiveX, ComObj, PropSys, ShellAPI, ShlObj;
 

+ 1 - 1
Projects/Src/Setup.Install.pas

@@ -30,7 +30,7 @@ implementation
 uses
   Windows, SysUtils, Messages, Classes, Forms, ShlObj, Shared.Struct, Setup.UninstallLog, Shared.SetupTypes,
   SetupLdrAndSetup.InstFunc, Setup.InstFunc, Setup.InstFunc.Ole, Setup.SecurityFunc, SetupLdrAndSetup.Messages,
-  Setup.MainForm, Setup.LoggingFunc, Setup.FileExtractor, Shared.FileClass,
+  Setup.MainFunc, Setup.LoggingFunc, Setup.FileExtractor, Shared.FileClass,
   Compression.Base, SHA1, PathFunc, Shared.CommonFunc.Vcl, Shared.CommonFunc, SetupLdrAndSetup.RedirFunc, Shared.Int64Em, Shared.SetupMessageIDs,
   Setup.WizardForm, Shared.DebugStruct, Setup.DebugClient, Shared.VerInfoFunc, Setup.ScriptRunner, Setup.RegDLL, Setup.Helper,
   Shared.ResUpdateFunc, Setup.DotNetFunc, TaskbarProgressFunc, NewProgressBar, RestartManager,

+ 767 - 0
Projects/Src/Setup.MainForm.pas

@@ -0,0 +1,767 @@
+unit Setup.MainForm;
+
+{
+  Inno Setup
+  Copyright (C) 1997-2024 Jordan Russell
+  Portions by Martijn Laan
+  For conditions of distribution and use, see LICENSE.TXT.
+
+  Background form
+}
+
+interface
+
+uses
+  Windows, Messages, SysUtils, Classes,
+  Shared.Struct, Setup.MainFunc, Setup.SetupForm, Shared.SetupTypes;
+
+type
+  TMainForm = class(TSetupForm)
+    procedure FormResize(Sender: TObject);
+    procedure FormCloseQuery(Sender: TObject; var CanClose: Boolean);
+    procedure FormPaint(Sender: TObject);
+    procedure FormKeyDown(Sender: TObject; var Key: Word;
+      Shift: TShiftState);
+  private
+    IsMinimized, HideWizard: Boolean;
+    function MainWindowHook(var Message: TMessage): Boolean;
+    procedure UpdateWizardFormVisibility;
+    procedure WMSysCommand(var Message: TWMSysCommand); message WM_SYSCOMMAND;
+    procedure WMEraseBkgnd(var Message: TWMEraseBkgnd); message WM_ERASEBKGND;
+    procedure WMGetDlgCode(var Message: TWMGetDlgCode); message WM_GETDLGCODE;
+    procedure WMShowWindow(var Message: TWMShowWindow); message WM_SHOWWINDOW;
+  public
+    CurStep: TSetupStep;
+    constructor Create(AOwner: TComponent); override;
+    destructor Destroy; override;
+    procedure Finish(const FromPreparingPage: Boolean);
+    procedure InitializeWizard;
+    function Install: Boolean;
+    procedure SetStep(const AStep: TSetupStep; const HandleExceptions: Boolean);
+    class procedure ShowException(Sender: TObject; E: Exception);
+    class procedure ShowExceptionMsg(const S: String);
+    procedure ShowAboutBox;
+  end;
+
+var
+  MainForm: TMainForm;
+
+implementation
+
+uses
+  Forms, Graphics, ShlObj, MD5, RestartManager,
+  Shared.CommonFunc, Shared.CommonFunc.Vcl, Shared.SetupMessageIDs,
+  SetupLdrAndSetup.Messages, SetupLdrAndSetup.RedirFunc, Setup.Install,
+  Setup.InstFunc, Setup.WizardForm, Setup.LoggingFunc;
+
+{$R *.DFM}
+
+constructor TMainForm.Create(AOwner: TComponent);
+var
+  SystemMenu: HMenu;
+begin
+  inherited;
+
+  InitializeFont;
+
+  if shWindowVisible in SetupHeader.Options then begin
+    { Should the main window not be sizable? }
+    if not(shWindowShowCaption in SetupHeader.Options) then
+      BorderStyle := bsNone
+    else
+    if not(shWindowResizable in SetupHeader.Options) then
+      BorderStyle := bsSingle;
+
+    { Make the main window full-screen. If the window is resizable, limit it
+      to just the work area because full-screen resizable windows don't cover
+      over the taskbar. }
+    BoundsRect := GetRectOfPrimaryMonitor(BorderStyle = bsSizeable);
+    { Before maximizing the window, ensure Handle is created now so the correct
+      'restored' position is saved properly }
+    HandleNeeded;
+
+    { Maximize the window so that the taskbar is still accessible }
+    if shWindowStartMaximized in SetupHeader.Options then
+      WindowState := wsMaximized;
+  end
+  else begin
+    Application.ShowMainForm := False;
+  end;
+
+  if shDisableWelcomePage in SetupHeader.Options then
+    Caption := FmtSetupMessage1(msgSetupWindowTitle, ExpandedAppVerName)
+  else
+    Caption := FmtSetupMessage1(msgSetupWindowTitle, ExpandedAppName);
+
+  { Append the 'About Setup' item to the system menu }
+  SystemMenu := GetSystemMenu(Handle, False);
+  AppendMenu(SystemMenu, MF_SEPARATOR, 0, nil);
+  AppendMenu(SystemMenu, MF_STRING, 9999, PChar(SetupMessages[msgAboutSetupMenuItem]));
+
+  Application.HookMainWindow(MainWindowHook);
+
+  if Application.ShowMainForm then
+    { Show this form now, so that the focus stays on the wizard form that
+      InitializeWizard (called in the .dpr) shows }
+    Visible := True;
+end;
+
+destructor TMainForm.Destroy;
+begin
+  Application.UnhookMainWindow(MainWindowHook);
+  inherited;
+end;
+
+procedure TMainForm.WMSysCommand(var Message: TWMSysCommand);
+begin
+  if Message.CmdType = 9999 then
+    ShowAboutBox
+  else
+    inherited;
+end;
+
+procedure TMainForm.WMEraseBkgnd(var Message: TWMEraseBkgnd);
+begin
+  { Since the form paints its entire client area in FormPaint, there is
+    no need for the VCL to ever erase the client area with the brush color.
+    Doing so only slows it down, so this message handler disables that default
+    behavior. }
+  Message.Result := 0;
+end;
+
+procedure TMainForm.FormPaint(Sender: TObject);
+
+  function BlendRGB(const Color1, Color2: TColor; const Blend: Integer): TColor;
+  { Blends Color1 and Color2. Blend must be between 0 and 255; 0 = all Color1,
+    255 = all Color2. }
+  type
+    TColorBytes = array[0..3] of Byte;
+  var
+    I: Integer;
+  begin
+    Result := 0;
+    for I := 0 to 2 do
+      TColorBytes(Result)[I] := Integer(TColorBytes(Color1)[I] +
+        ((TColorBytes(Color2)[I] - TColorBytes(Color1)[I]) * Blend) div 255);
+  end;
+
+var
+  C1, C2: TColor;
+  CS: TPoint;
+  Z: Integer;
+  DrawTextFlags: UINT;
+  R, R2: TRect;
+begin
+  with Canvas do begin
+    { Draw the blue background }
+    if SetupHeader.BackColor = SetupHeader.BackColor2 then begin
+      Brush.Color := SetupHeader.BackColor;
+      FillRect(ClientRect);
+    end
+    else begin
+      C1 := ColorToRGB(SetupHeader.BackColor);
+      C2 := ColorToRGB(SetupHeader.BackColor2);
+      CS := ClientRect.BottomRight;
+      for Z := 0 to 255 do begin
+        Brush.Color := BlendRGB(C1, C2, Z);
+        if not(shBackColorHorizontal in SetupHeader.Options) then
+          FillRect(Rect(0, MulDiv(CS.Y, Z, 255), CS.X, MulDiv(CS.Y, Z+1, 255)))
+        else
+          FillRect(Rect(MulDiv(CS.X, Z, 255), 0, MulDiv(CS.X, Z+1, 255), CS.Y));
+      end;
+    end;
+
+    { Draw the application name and copyright }
+    SetBkMode(Handle, TRANSPARENT);
+
+    DrawTextFlags := DT_WORDBREAK or DT_NOPREFIX or DT_NOCLIP;
+    if RightToLeft then
+      DrawTextFlags := DrawTextFlags or (DT_RIGHT or DT_RTLREADING);
+    SetFontNameSize(Font, LangOptions.TitleFontName,
+      LangOptions.TitleFontSize, 'Arial', 29);
+    if IsMultiByteString(AnsiString(ExpandedAppName)) then
+      { Don't use italics on Japanese characters }
+      Font.Style := [fsBold]
+    else
+      Font.Style := [fsBold, fsItalic];
+    R := ClientRect;
+    InflateRect(R, -8, -8);
+    R2 := R;
+    if RightToLeft then
+      OffsetRect(R2, -4, 4)
+    else
+      OffsetRect(R2, 4, 4);
+    Font.Color := clBlack;
+    DrawText(Handle, PChar(ExpandedAppName), -1, R2, DrawTextFlags);
+    Font.Color := clWhite;
+    DrawText(Handle, PChar(ExpandedAppName), -1, R, DrawTextFlags);
+
+    DrawTextFlags := DrawTextFlags xor DT_RIGHT;
+    SetFontNameSize(Font, LangOptions.CopyrightFontName,
+      LangOptions.CopyrightFontSize, 'Arial', 8);
+    Font.Style := [];
+    R := ClientRect;
+    InflateRect(R, -6, -6);
+    R2 := R;
+    DrawText(Handle, PChar(ExpandedAppCopyright), -1, R2, DrawTextFlags or
+      DT_CALCRECT);
+    R.Top := R.Bottom - (R2.Bottom - R2.Top);
+    R2 := R;
+    if RightToLeft then
+      OffsetRect(R2, -1, 1)
+    else
+      OffsetRect(R2, 1, 1);
+    Font.Color := clBlack;
+    DrawText(Handle, PChar(ExpandedAppCopyright), -1, R2, DrawTextFlags);
+    Font.Color := clWhite;
+    DrawText(Handle, PChar(ExpandedAppCopyright), -1, R, DrawTextFlags);
+  end;
+end;
+
+procedure TMainForm.FormResize(Sender: TObject);
+begin
+  { Needs to redraw the background whenever the form is resized }
+  Repaint;
+end;
+
+procedure TMainForm.ShowAboutBox;
+var
+  S: String;
+begin
+  { Removing the About box or modifying any existing text inside it is a
+    violation of the Inno Setup license agreement; see LICENSE.TXT.
+    However, adding additional lines to the end of the About box is
+    permitted. }
+  S := SetupTitle + ' version ' + SetupVersion + SNewLine;
+  if SetupTitle <> 'Inno Setup' then
+    S := S + (SNewLine + 'Based on Inno Setup' + SNewLine);
+  S := S + ('Copyright (C) 1997-2024 Jordan Russell' + SNewLine +
+    'Portions Copyright (C) 2000-2024 Martijn Laan' + SNewLine +
+    'All rights reserved.' + SNewLine2 +
+    'Inno Setup home page:' + SNewLine +
+    'https://www.innosetup.com/');
+  S := S + SNewLine2 + 'RemObjects Pascal Script home page:' + SNewLine +
+    'https://www.remobjects.com/ps';
+  if SetupMessages[msgAboutSetupNote] <> '' then
+    S := S + SNewLine2 + SetupMessages[msgAboutSetupNote];
+  if SetupMessages[msgTranslatorNote] <> '' then
+    S := S + SNewLine2 + SetupMessages[msgTranslatorNote];
+  StringChangeEx(S, '(C)', #$00A9, True);
+  LoggedMsgBox(S, SetupMessages[msgAboutSetupTitle], mbInformation, MB_OK, False, 0);
+end;
+
+class procedure TMainForm.ShowExceptionMsg(const S: String);
+begin
+  Log('Exception message:');
+  LoggedAppMessageBox(PChar(S), PChar(Application.Title), MB_OK or MB_ICONSTOP, True, IDOK);
+end;
+
+class procedure TMainForm.ShowException(Sender: TObject; E: Exception);
+begin
+  ShowExceptionMsg(AddPeriod(E.Message));
+end;
+
+procedure TMainForm.SetStep(const AStep: TSetupStep; const HandleExceptions: Boolean);
+begin
+  CurStep := AStep;
+  if CodeRunner <> nil then begin
+    try
+      CodeRunner.RunProcedures('CurStepChanged', [Ord(CurStep)], False);
+    except
+      if HandleExceptions then begin
+        Log('CurStepChanged raised an exception.');
+        Application.HandleException(Self);
+      end
+      else begin
+        Log('CurStepChanged raised an exception (fatal).');
+        raise;
+      end;
+    end;
+  end;
+end;
+
+procedure TMainForm.InitializeWizard;
+begin
+  WizardForm := TWizardForm.Create(Application);
+  if CodeRunner <> nil then begin
+    try
+      CodeRunner.RunProcedures('InitializeWizard', [''], False);
+    except
+      Log('InitializeWizard raised an exception (fatal).');
+      raise;
+    end;
+  end;
+  WizardForm.FlipSizeAndCenterIfNeeded(shWindowVisible in SetupHeader.Options, MainForm, True);
+  WizardForm.SetCurPage(wpWelcome);
+  if InstallMode = imNormal then begin
+    WizardForm.ClickToStartPage; { this won't go past wpReady  }
+    SetActiveWindow(Application.Handle);  { ensure taskbar button is selected }
+    WizardForm.Show;
+  end
+  else
+    WizardForm.ClickThroughPages;
+end;
+
+procedure TerminateApp;
+begin
+  { Work around shell32 bug: Don't use PostQuitMessage/Application.Terminate
+    here.
+    When ShellExecute is called with the name of a folder, it internally
+    creates a window used for DDE communication with Windows Explorer. After
+    ShellExecute returns, this window eventually receives a posted WM_DDE_ACK
+    message back from the DDE server (Windows Explorer), and in response, it
+    tries to flush the queue of DDE messages by using a PeekMessage loop.
+    Problem is, PeekMessage will return WM_QUIT messages posted with
+    PostQuitMessage regardless of the message range specified, and the loop was
+    not written with this in mind.
+    In previous IS versions, this was causing our WM_QUIT message to be eaten
+    if Application.Terminate was called very shortly after a shellexec [Run]
+    entry was processed (e.g. if DisableFinishedPage=yes).
+    A WM_QUIT message posted with PostMessage instead of PostQuitMessage will
+    not be returned by a GetMessage/PeekMessage call with a message range that
+    does not include WM_QUIT. }
+  PostMessage(0, WM_QUIT, 0, 0);
+end;
+
+function TMainForm.Install: Boolean;
+
+  procedure ProcessRunEntries;
+  var
+    CheckIfRestartNeeded: Boolean;
+    ChecksumBefore, ChecksumAfter: TMD5Digest;
+    WindowDisabler: TWindowDisabler;
+    I: Integer;
+    RunEntry: PSetupRunEntry;
+  begin
+    if Entries[seRun].Count <> 0 then begin
+      CheckIfRestartNeeded := (shRestartIfNeededByRun in SetupHeader.Options) and
+        not NeedsRestart;
+      if CheckIfRestartNeeded then
+        ChecksumBefore := MakePendingFileRenameOperationsChecksum;
+      WindowDisabler := nil;
+      try
+        for I := 0 to Entries[seRun].Count-1 do begin
+          RunEntry := PSetupRunEntry(Entries[seRun][I]);
+          if not(roPostInstall in RunEntry.Options) and
+             ShouldProcessRunEntry(WizardComponents, WizardTasks, RunEntry) then begin
+            { Disable windows during execution of [Run] entries so that a nice
+              "beep" is produced if the user tries clicking on WizardForm }
+            if WindowDisabler = nil then
+              WindowDisabler := TWindowDisabler.Create;
+            if RunEntry.StatusMsg <> '' then begin
+              try
+                WizardForm.StatusLabel.Caption := ExpandConst(RunEntry.StatusMsg);
+              except
+                { Don't die if the expansion fails with an exception. Just
+                  display the exception message, and proceed with the default
+                  status message. }
+                Application.HandleException(Self);
+                WizardForm.StatusLabel.Caption := SetupMessages[msgStatusRunProgram];
+              end;
+            end
+            else
+              WizardForm.StatusLabel.Caption := SetupMessages[msgStatusRunProgram];
+            WizardForm.StatusLabel.Update;
+            if roHideWizard in RunEntry.Options then begin
+              if WizardForm.Visible and not HideWizard then begin
+                HideWizard := True;
+                UpdateWizardFormVisibility;
+              end;
+            end
+            else begin
+              if HideWizard then begin
+                HideWizard := False;
+                UpdateWizardFormVisibility;
+              end;
+            end;
+            DebugNotifyEntry(seRun, I);
+            NotifyBeforeInstallEntry(RunEntry.BeforeInstall);
+            ProcessRunEntry(RunEntry);
+            NotifyAfterInstallEntry(RunEntry.AfterInstall);
+          end;
+        end;
+      finally
+        if HideWizard then begin
+          HideWizard := False;
+          UpdateWizardFormVisibility;
+        end;
+        WindowDisabler.Free;
+        if CheckIfRestartNeeded then begin
+          ChecksumAfter := MakePendingFileRenameOperationsChecksum;
+          if not MD5DigestsEqual(ChecksumBefore, ChecksumAfter) then
+            NeedsRestart := True;
+        end;
+      end;
+      Application.BringToFront;
+    end;
+  end;
+
+  procedure RestartApplications;
+  const
+    ERROR_FAIL_RESTART = 353;
+  var
+    Error: DWORD;
+    WindowDisabler: TWindowDisabler;
+  begin
+    if not NeedsRestart then begin
+      WizardForm.StatusLabel.Caption := SetupMessages[msgStatusRestartingApplications];
+      WizardForm.StatusLabel.Update;
+
+      Log('Attempting to restart applications.');
+
+      { Disable windows during application restart so that a nice
+        "beep" is produced if the user tries clicking on WizardForm }
+      WindowDisabler := TWindowDisabler.Create;
+      try
+        Error := RmRestart(RmSessionHandle, 0, nil);
+      finally
+        WindowDisabler.Free;
+      end;
+      Application.BringToFront;
+
+      if Error = ERROR_FAIL_RESTART then
+        Log('One or more applications could not be restarted.')
+      else if Error <> ERROR_SUCCESS then begin
+        RmEndSession(RmSessionHandle);
+        RmSessionStarted := False;
+        LogFmt('RmRestart returned an error: %d', [Error]);
+      end;
+    end else
+      Log('Need to restart Windows, not attempting to restart applications');
+  end;
+
+var
+  Succeeded, ChangesEnvironment, ChangesAssociations: Boolean;
+  S: String;
+begin
+  Result := False;
+  try
+    if not WizardForm.ValidateDirEdit then
+      Abort;
+    WizardDirValue := WizardForm.DirEdit.Text;
+    if not WizardForm.ValidateGroupEdit then
+      Abort;
+    WizardGroupValue := WizardForm.GroupEdit.Text;
+    WizardNoIcons := WizardForm.NoIconsCheck.Checked;
+    WizardSetupType := WizardForm.GetSetupType();
+    WizardForm.GetComponents(WizardComponents, WizardDeselectedComponents);
+    WizardForm.GetTasks(WizardTasks, WizardDeselectedTasks);
+    WizardPreparingYesRadio := WizardForm.PreparingYesRadio.Checked;
+    if InitSaveInf <> '' then
+      SaveInf(InitSaveInf);
+
+    Application.Restore;
+    Update;
+    if InstallMode = imSilent then begin
+      SetActiveWindow(Application.Handle);  { ensure taskbar button is selected }
+      WizardForm.Show;
+    end;
+    WizardForm.Update;
+
+    SetStep(ssInstall, False);
+    
+    ChangesEnvironment := EvalDirectiveCheck(SetupHeader.ChangesEnvironment);
+    ChangesAssociations := EvalDirectiveCheck(SetupHeader.ChangesAssociations);
+
+    PerformInstall(Succeeded, ChangesEnvironment, ChangesAssociations);
+    if not Succeeded then begin
+      { The user canceled the install or there was a fatal error }
+      TerminateApp;
+      Exit;
+    end;
+    { Can't cancel at any point after PerformInstall, so disable the button }
+    WizardForm.CancelButton.Enabled := False;
+
+    ProcessRunEntries;
+
+    if RmDoRestart and
+       (InitRestartApplications or
+        ((shRestartApplications in SetupHeader.Options) and not InitNoRestartApplications)) then
+      RestartApplications;
+
+    SetStep(ssPostInstall, True);
+
+    { Notify Windows of assocations/environment changes *after* ssPostInstall
+      since user might set more stuff there }
+    if ChangesAssociations then
+      SHChangeNotify(SHCNE_ASSOCCHANGED, SHCNF_IDLIST, nil, nil);
+    if ChangesEnvironment then
+      RefreshEnvironment;
+
+    if InstallMode <> imNormal then
+      WizardForm.Hide;
+
+    LogFmt('Need to restart Windows? %s', [SYesNo[NeedsRestart]]);
+    if NeedsRestart and not InitNoRestart then begin
+      with WizardForm do begin
+        ChangeFinishedLabel(ExpandSetupMessage(msgFinishedRestartLabel));
+        YesRadio.Visible := True;
+        NoRadio.Visible := True;
+      end;
+    end else begin
+      if CreatedIcon then
+        S := ExpandSetupMessage(msgFinishedLabel)
+      else
+        S := ExpandSetupMessage(msgFinishedLabelNoIcons);
+      with WizardForm do begin
+        ChangeFinishedLabel(S + SNewLine2 + SetupMessages[msgClickFinish]);
+        if not NeedsRestart then begin
+          UpdateRunList(WizardComponents, WizardTasks);
+          RunList.Visible := RunList.Items.Count > 0;
+        end;
+      end;
+    end;
+
+    if InstallMode = imNormal then begin
+      Application.Restore;
+      Update;
+    end;
+
+    Result := True;
+  except
+    { If an exception was raised, display the message, then terminate }
+    Application.HandleException(Self);
+    SetupExitCode := ecNextStepError;
+    TerminateApp;
+  end;
+end;
+
+procedure ProcessMessagesProc; far;
+begin
+  Application.ProcessMessages;
+end;
+
+procedure TMainForm.Finish(const FromPreparingPage: Boolean);
+
+  procedure WaitForForegroundLoss;
+
+    function IsForegroundProcess: Boolean;
+    var
+      W: HWND;
+      PID: DWORD;
+    begin
+      W := GetForegroundWindow;
+      Result := False;
+      if (W <> 0) and (GetWindowThreadProcessId(W, @PID) <> 0) then
+        Result := (PID = GetCurrentProcessId);
+    end;
+
+  var
+    StartTick: DWORD;
+  begin
+    StartTick := GetTickCount;
+    while IsForegroundProcess do begin
+      { Stop if it's taking too long (e.g. if the spawned process never
+        displays a window) }
+      if Cardinal(GetTickCount - StartTick) >= Cardinal(1000) then
+        Break;
+      ProcessMessagesProc;
+      WaitMessageWithTimeout(10);
+      ProcessMessagesProc;
+    end;
+  end;
+
+  procedure ProcessPostInstallRunEntries;
+  var
+    WindowDisabler: TWindowDisabler;
+    ProcessedNoWait: Boolean;
+    I: Integer;
+    RunEntry: PSetupRunEntry;
+  begin
+    WindowDisabler := nil;
+    try
+      ProcessedNoWait := False;
+      with WizardForm do begin
+        for I := 0 to RunList.Items.Count-1 do begin
+          if RunList.Checked[I] then begin
+            { Disable windows before processing the first entry }
+            if WindowDisabler = nil then
+              WindowDisabler := TWindowDisabler.Create;
+            RunEntry := PSetupRunEntry(Entries[seRun][Integer(RunList.ItemObject[I])]);
+            DebugNotifyEntry(seRun, Integer(RunList.ItemObject[I]));
+            NotifyBeforeInstallEntry(RunEntry.BeforeInstall);
+            ProcessRunEntry(RunEntry);
+            NotifyAfterInstallEntry(RunEntry.AfterInstall);
+            if RunEntry.Wait = rwNoWait then
+              ProcessedNoWait := True;
+          end;
+        end;
+      end;
+      { Give nowait processes some time to bring themselves to the
+        foreground before Setup exits. Without this delay, the application
+        underneath Setup can end up coming to the foreground instead.
+        (Note: Windows are already disabled at this point.) }
+      if ProcessedNoWait then
+        WaitForForegroundLoss;
+    finally
+      WindowDisabler.Free;
+    end;
+  end;
+
+var
+  S: String;
+begin
+  try
+    { Deactivate WizardForm so another application doesn't come to the
+      foreground when Hide is called. (Needed by WaitForForegroundLoss.) }
+    if GetForegroundWindow = WizardForm.Handle then
+      SetActiveWindow(Application.Handle);
+    WizardForm.Hide;
+
+    if not FromPreparingPage and not NeedsRestart then begin
+      ProcessPostInstallRunEntries;
+    end else begin
+      if FromPreparingPage then
+        SetupExitCode := ecPrepareToInstallFailedRestartNeeded
+      else if InitRestartExitCode <> 0 then
+        SetupExitCode := InitRestartExitCode;
+
+      if InitNoRestart then
+        RestartSystem := False
+      else begin
+        case InstallMode of
+          imNormal:
+            if FromPreparingPage then
+              RestartSystem := WizardForm.PreparingYesRadio.Checked
+            else
+              RestartSystem := WizardForm.YesRadio.Checked;
+          imSilent:
+            begin
+              if FromPreparingPage then
+                S := WizardForm.PrepareToInstallFailureMessage + SNewLine +
+                  SNewLine + SNewLine + ExpandSetupMessage(msgPrepareToInstallNeedsRestart)
+              else
+                S := ExpandSetupMessage(msgFinishedRestartMessage);
+              RestartSystem :=
+                LoggedMsgBox(S, '', mbConfirmation, MB_YESNO, True, IDYES) = IDYES;
+            end;
+          imVerySilent:
+            RestartSystem := True;
+        end;
+      end;
+      if not RestartSystem then
+        Log('Will not restart Windows automatically.');
+    end;
+
+    SetStep(ssDone, True);
+  except
+    Application.HandleException(Self);
+    SetupExitCode := ecNextStepError;
+  end;
+  TerminateApp;
+end;
+
+procedure TMainForm.FormCloseQuery(Sender: TObject; var CanClose: Boolean);
+
+  function ConfirmCancel(const DefaultConfirm: Boolean): Boolean;
+  var
+    Cancel, Confirm: Boolean;
+  begin
+    Cancel := True;
+    Confirm := DefaultConfirm;
+    WizardForm.CallCancelButtonClick(Cancel, Confirm);
+    Result := Cancel and (not Confirm or ExitSetupMsgBox);
+  end;
+
+begin
+  { Note: Setting CanClose to True causes Application.Terminate to be called;
+    we don't want that. }
+  CanClose := False;
+  if Assigned(WizardForm) and WizardForm.HandleAllocated and
+     IsWindowVisible(WizardForm.Handle) and IsWindowEnabled(WizardForm.Handle) and
+     WizardForm.CancelButton.CanFocus then begin
+    case CurStep of
+      ssPreInstall:
+        if ConfirmCancel((WizardForm.CurPageID <> wpPreparing) or (WizardForm.PrepareToInstallFailureMessage = '')) then begin
+          if WizardForm.CurPageID = wpPreparing then
+            SetupExitCode := ecPrepareToInstallFailed
+          else
+            SetupExitCode := ecCancelledBeforeInstall;
+          TerminateApp;
+        end;
+      ssInstall:
+        if (shAllowCancelDuringInstall in SetupHeader.Options) and not InitNoCancel then
+          if ConfirmCancel(True) then
+            NeedToAbortInstall := True;
+    end;
+  end;
+end;
+
+procedure TMainForm.WMGetDlgCode(var Message: TWMGetDlgCode);
+begin
+  Message.Result := Message.Result or DLGC_WANTTAB;
+end;
+
+function EWP(Wnd: HWND; Param: LPARAM): BOOL; stdcall;
+begin
+  { Note: GetParent is not used here because the other windows are not
+    actually child windows since they don't have WS_CHILD set. }
+  if GetWindowLong(Wnd, GWL_HWNDPARENT) <> Param then
+    Result := True
+  else begin
+    Result := False;
+    BringWindowToTop(Wnd);
+  end;
+end;
+
+procedure TMainForm.FormKeyDown(Sender: TObject; var Key: Word;
+  Shift: TShiftState);
+begin
+  { If, for some reason, the user doesn't have a mouse and the main form was
+    activated, there would normally be no way to reactivate the child form.
+    But this reactivates the form if the user hits a key on the keyboard }
+  if not(ssAlt in Shift) then begin
+    Key := 0;
+    EnumThreadWindows(GetCurrentThreadId, @EWP, Handle);
+  end;
+end;
+
+procedure TMainForm.UpdateWizardFormVisibility;
+var
+  ShouldShow: Boolean;
+begin
+  { Note: We don't adjust WizardForm.Visible because on Delphi 3+, if all forms
+    have Visible set to False, the application taskbar button disappears. }
+  if Assigned(WizardForm) and WizardForm.HandleAllocated then begin
+    ShouldShow := WizardForm.Showing and not HideWizard and
+      not IsIconic(Application.Handle);
+    if (GetWindowLong(WizardForm.Handle, GWL_STYLE) and WS_VISIBLE <> 0) <> ShouldShow then begin
+      if ShouldShow then
+        ShowWindow(WizardForm.Handle, SW_SHOW)
+      else
+        ShowWindow(WizardForm.Handle, SW_HIDE);
+    end;
+  end;
+end;
+
+function TMainForm.MainWindowHook(var Message: TMessage): Boolean;
+var
+  IsIcon: Boolean;
+begin
+  Result := False;
+  case Message.Msg of
+    WM_WINDOWPOSCHANGED: begin
+        { When the application window is minimized or restored, also hide or
+          show WizardForm.
+          Note: MainForm is hidden/shown automatically because its owner
+          window is Application.Handle. }
+        IsIcon := IsIconic(Application.Handle);
+        if IsMinimized <> IsIcon then begin
+          IsMinimized := IsIcon;
+          UpdateWizardFormVisibility;
+        end;
+      end;
+  end;
+end;
+
+procedure TMainForm.WMShowWindow(var Message: TWMShowWindow);
+begin
+  inherited;
+  { When showing, ensure WizardForm is the active window, not MainForm }
+  if Message.Show and (GetActiveWindow = Handle) and
+     Assigned(WizardForm) and WizardForm.HandleAllocated and
+     IsWindowVisible(WizardForm.Handle) then
+    SetActiveWindow(WizardForm.Handle);
+end;
+
+end.

+ 9 - 747
Projects/Src/Setup.MainFunc.pas

@@ -1,4 +1,4 @@
-unit Setup.MainForm;
+unit Setup.MainFunc;
 
 {
   Inno Setup
@@ -13,39 +13,10 @@ interface
 
 uses
   Windows, SysUtils, Messages, Classes, Graphics, Controls, Forms, Dialogs,
-  Setup.SetupForm, StdCtrls, Shared.Struct, Shared.DebugStruct, Shared.Int64Em, Shared.CommonFunc.Vcl, Shared.CommonFunc,
-  Shared.SetupTypes, Setup.ScriptRunner, BidiUtils, RestartManager;
+  StdCtrls, Shared.Struct, Shared.DebugStruct, Shared.Int64Em, Shared.CommonFunc.Vcl, Shared.CommonFunc,
+  Shared.SetupTypes, Setup.ScriptRunner, RestartManager;
 
 type
-  TMainForm = class(TSetupForm)
-    procedure FormResize(Sender: TObject);
-    procedure FormCloseQuery(Sender: TObject; var CanClose: Boolean);
-    procedure FormPaint(Sender: TObject);
-    procedure FormKeyDown(Sender: TObject; var Key: Word;
-      Shift: TShiftState);
-  private
-    { Private declarations }
-    IsMinimized, HideWizard: Boolean;
-    function MainWindowHook(var Message: TMessage): Boolean;
-    procedure UpdateWizardFormVisibility;
-    procedure WMSysCommand(var Message: TWMSysCommand); message WM_SYSCOMMAND;
-    procedure WMEraseBkgnd(var Message: TWMEraseBkgnd); message WM_ERASEBKGND;
-    procedure WMGetDlgCode(var Message: TWMGetDlgCode); message WM_GETDLGCODE;
-    procedure WMShowWindow(var Message: TWMShowWindow); message WM_SHOWWINDOW;
-  public
-    { Public declarations }
-    CurStep: TSetupStep;
-    constructor Create(AOwner: TComponent); override;
-    destructor Destroy; override;
-    procedure Finish(const FromPreparingPage: Boolean);
-    procedure InitializeWizard;
-    function Install: Boolean;
-    procedure SetStep(const AStep: TSetupStep; const HandleExceptions: Boolean);
-    class procedure ShowException(Sender: TObject; E: Exception);
-    class procedure ShowExceptionMsg(const S: String);
-    procedure ShowAboutBox;
-  end;
-
   TEntryType = (seLanguage, seCustomMessage, sePermission, seType, seComponent,
     seTask, seDir, seFile, seFileLocation, seIcon, seIni, seRegistry,
     seInstallDelete, seUninstallDelete, seRun, seUninstallRun);
@@ -94,8 +65,6 @@ const
   CodeRunnerNamingAttribute = 'Event';
 
 var
-  MainForm: TMainForm;
-
   { Variables for command line parameters }
   SetupLdrMode: Boolean;
   SetupLdrOriginalFilename: String;
@@ -240,12 +209,14 @@ function PreviousInstallCompleted(const WizardComponents, WizardTasks: TStringLi
 function CodeRegisterExtraCloseApplicationsResource(const DisableFsRedir: Boolean; const AFilename: String): Boolean;
 procedure RegisterResourcesWithRestartManager(const WizardComponents, WizardTasks: TStringList);
 procedure RemoveTempInstallDir;
+procedure SaveInf(const FileName: String);
 procedure SaveResourceToTempFile(const ResName, Filename: String);
 procedure SetActiveLanguage(const I: Integer);
 procedure SetTaskbarButtonVisibility(const AVisible: Boolean);
 procedure ShellExecuteAsOriginalUser(hWnd: HWND; Operation, FileName, Parameters, Directory: LPWSTR; ShowCmd: Integer); stdcall;
 function ShouldDisableFsRedirForFileEntry(const FileEntry: PSetupFileEntry): Boolean;
 function ShouldDisableFsRedirForRunEntry(const RunEntry: PSetupRunEntry): Boolean;
+procedure ProcessRunEntry(const RunEntry: PSetupRunEntry);
 function EvalArchitectureIdentifier(const Name: String): Boolean;
 function EvalDirectiveCheck(const Expression: String): Boolean;
 function ShouldProcessEntry(const WizardComponents, WizardTasks: TStringList;
@@ -268,16 +239,13 @@ implementation
 uses
   ShellAPI, ShlObj, StrUtils,
   SetupLdrAndSetup.Messages, Shared.SetupMessageIDs, Setup.Install, SetupLdrAndSetup.InstFunc,
-  Setup.InstFunc, Setup.InstFunc.Ole, SetupLdrAndSetup.RedirFunc, PathFunc,
+  Setup.InstFunc, SetupLdrAndSetup.RedirFunc, PathFunc,
   Compression.Base, Compression.Zlib, Compression.bzlib, Compression.LZMADecompressor,
   Shared.ArcFour, Shared.SetupEntFunc, Setup.SelectLanguageForm,
   Setup.WizardForm, Setup.DebugClient, Shared.VerInfoFunc, Setup.FileExtractor,
-  Shared.FileClass, Setup.LoggingFunc, MD5, SHA1, ActiveX,
+  Shared.FileClass, Setup.LoggingFunc, SHA1, ActiveX,
   SimpleExpression, Setup.Helper, Setup.SpawnClient, Setup.SpawnServer,
-  Setup.DotNetFunc, BitmapImage,
-  Shared.TaskDialogFunc, RegStr;
-
-{$R *.DFM}
+  Setup.DotNetFunc, Shared.TaskDialogFunc, RegStr, Setup.MainForm;
 
 var
   ShellFolders: array[Boolean, TShellFolderID] of String;
@@ -531,7 +499,7 @@ end;
 class function TDummyClass.EvalArchitectureIdentifier(Sender: TSimpleExpression;
   const Name: String; const Parameters: array of const): Boolean;
 begin
-  Result := Setup.MainForm.EvalArchitectureIdentifier(Name);
+  Result := Setup.MainFunc.EvalArchitectureIdentifier(Name);
 end;
 
 class function TDummyClass.EvalComponentOrTaskIdentifier(Sender: TSimpleExpression;
@@ -3591,240 +3559,12 @@ begin
   end;
 end;
 
-function BlendRGB(const Color1, Color2: TColor; const Blend: Integer): TColor;
-{ Blends Color1 and Color2. Blend must be between 0 and 255; 0 = all Color1,
-  255 = all Color2. }
-type
-  TColorBytes = array[0..3] of Byte;
-var
-  I: Integer;
-begin
-  Result := 0;
-  for I := 0 to 2 do
-    TColorBytes(Result)[I] := Integer(TColorBytes(Color1)[I] +
-      ((TColorBytes(Color2)[I] - TColorBytes(Color1)[I]) * Blend) div 255);
-end;
-
 function ExitSetupMsgBox: Boolean;
 begin
   Result := LoggedMsgBox(SetupMessages[msgExitSetupMessage], SetupMessages[msgExitSetupTitle],
     mbConfirmation, MB_YESNO or MB_DEFBUTTON2, False, 0) = IDYES;
 end;
 
-procedure TerminateApp;
-begin
-  { Work around shell32 bug: Don't use PostQuitMessage/Application.Terminate
-    here.
-    When ShellExecute is called with the name of a folder, it internally
-    creates a window used for DDE communication with Windows Explorer. After
-    ShellExecute returns, this window eventually receives a posted WM_DDE_ACK
-    message back from the DDE server (Windows Explorer), and in response, it
-    tries to flush the queue of DDE messages by using a PeekMessage loop.
-    Problem is, PeekMessage will return WM_QUIT messages posted with
-    PostQuitMessage regardless of the message range specified, and the loop was
-    not written with this in mind.
-    In previous IS versions, this was causing our WM_QUIT message to be eaten
-    if Application.Terminate was called very shortly after a shellexec [Run]
-    entry was processed (e.g. if DisableFinishedPage=yes).
-    A WM_QUIT message posted with PostMessage instead of PostQuitMessage will
-    not be returned by a GetMessage/PeekMessage call with a message range that
-    does not include WM_QUIT. }
-  PostMessage(0, WM_QUIT, 0, 0);
-end;
-
-
-{ TMainForm }
-
-constructor TMainForm.Create(AOwner: TComponent);
-var
-  SystemMenu: HMenu;
-begin
-  inherited;
-
-  InitializeFont;
-
-  if shWindowVisible in SetupHeader.Options then begin
-    { Should the main window not be sizable? }
-    if not(shWindowShowCaption in SetupHeader.Options) then
-      BorderStyle := bsNone
-    else
-    if not(shWindowResizable in SetupHeader.Options) then
-      BorderStyle := bsSingle;
-
-    { Make the main window full-screen. If the window is resizable, limit it
-      to just the work area because full-screen resizable windows don't cover
-      over the taskbar. }
-    BoundsRect := GetRectOfPrimaryMonitor(BorderStyle = bsSizeable);
-    { Before maximizing the window, ensure Handle is created now so the correct
-      'restored' position is saved properly }
-    HandleNeeded;
-
-    { Maximize the window so that the taskbar is still accessible }
-    if shWindowStartMaximized in SetupHeader.Options then
-      WindowState := wsMaximized;
-  end
-  else begin
-    Application.ShowMainForm := False;
-  end;
-
-  if shDisableWelcomePage in SetupHeader.Options then
-    Caption := FmtSetupMessage1(msgSetupWindowTitle, ExpandedAppVerName)
-  else
-    Caption := FmtSetupMessage1(msgSetupWindowTitle, ExpandedAppName);
-
-  { Append the 'About Setup' item to the system menu }
-  SystemMenu := GetSystemMenu(Handle, False);
-  AppendMenu(SystemMenu, MF_SEPARATOR, 0, nil);
-  AppendMenu(SystemMenu, MF_STRING, 9999, PChar(SetupMessages[msgAboutSetupMenuItem]));
-
-  Application.HookMainWindow(MainWindowHook);
-
-  if Application.ShowMainForm then
-    { Show this form now, so that the focus stays on the wizard form that
-      InitializeWizard (called in the .dpr) shows }
-    Visible := True;
-end;
-
-destructor TMainForm.Destroy;
-begin
-  Application.UnhookMainWindow(MainWindowHook);
-  inherited;
-end;
-
-procedure TMainForm.WMSysCommand(var Message: TWMSysCommand);
-begin
-  if Message.CmdType = 9999 then
-    ShowAboutBox
-  else
-    inherited;
-end;
-
-procedure TMainForm.WMEraseBkgnd(var Message: TWMEraseBkgnd);
-begin
-  { Since the form paints its entire client area in FormPaint, there is
-    no need for the VCL to ever erase the client area with the brush color.
-    Doing so only slows it down, so this message handler disables that default
-    behavior. }
-  Message.Result := 0;
-end;
-
-procedure TMainForm.FormPaint(Sender: TObject);
-var
-  C1, C2: TColor;
-  CS: TPoint;
-  Z: Integer;
-  DrawTextFlags: UINT;
-  R, R2: TRect;
-begin
-  with Canvas do begin
-    { Draw the blue background }
-    if SetupHeader.BackColor = SetupHeader.BackColor2 then begin
-      Brush.Color := SetupHeader.BackColor;
-      FillRect(ClientRect);
-    end
-    else begin
-      C1 := ColorToRGB(SetupHeader.BackColor);
-      C2 := ColorToRGB(SetupHeader.BackColor2);
-      CS := ClientRect.BottomRight;
-      for Z := 0 to 255 do begin
-        Brush.Color := BlendRGB(C1, C2, Z);
-        if not(shBackColorHorizontal in SetupHeader.Options) then
-          FillRect(Rect(0, MulDiv(CS.Y, Z, 255), CS.X, MulDiv(CS.Y, Z+1, 255)))
-        else
-          FillRect(Rect(MulDiv(CS.X, Z, 255), 0, MulDiv(CS.X, Z+1, 255), CS.Y));
-      end;
-    end;
-
-    { Draw the application name and copyright }
-    SetBkMode(Handle, TRANSPARENT);
-
-    DrawTextFlags := DT_WORDBREAK or DT_NOPREFIX or DT_NOCLIP;
-    if RightToLeft then
-      DrawTextFlags := DrawTextFlags or (DT_RIGHT or DT_RTLREADING);
-    SetFontNameSize(Font, LangOptions.TitleFontName,
-      LangOptions.TitleFontSize, 'Arial', 29);
-    if IsMultiByteString(AnsiString(ExpandedAppName)) then
-      { Don't use italics on Japanese characters }
-      Font.Style := [fsBold]
-    else
-      Font.Style := [fsBold, fsItalic];
-    R := ClientRect;
-    InflateRect(R, -8, -8);
-    R2 := R;
-    if RightToLeft then
-      OffsetRect(R2, -4, 4)
-    else
-      OffsetRect(R2, 4, 4);
-    Font.Color := clBlack;
-    DrawText(Handle, PChar(ExpandedAppName), -1, R2, DrawTextFlags);
-    Font.Color := clWhite;
-    DrawText(Handle, PChar(ExpandedAppName), -1, R, DrawTextFlags);
-
-    DrawTextFlags := DrawTextFlags xor DT_RIGHT;
-    SetFontNameSize(Font, LangOptions.CopyrightFontName,
-      LangOptions.CopyrightFontSize, 'Arial', 8);
-    Font.Style := [];
-    R := ClientRect;
-    InflateRect(R, -6, -6);
-    R2 := R;
-    DrawText(Handle, PChar(ExpandedAppCopyright), -1, R2, DrawTextFlags or
-      DT_CALCRECT);
-    R.Top := R.Bottom - (R2.Bottom - R2.Top);
-    R2 := R;
-    if RightToLeft then
-      OffsetRect(R2, -1, 1)
-    else
-      OffsetRect(R2, 1, 1);
-    Font.Color := clBlack;
-    DrawText(Handle, PChar(ExpandedAppCopyright), -1, R2, DrawTextFlags);
-    Font.Color := clWhite;
-    DrawText(Handle, PChar(ExpandedAppCopyright), -1, R, DrawTextFlags);
-  end;
-end;
-
-procedure TMainForm.FormResize(Sender: TObject);
-begin
-  { Needs to redraw the background whenever the form is resized }
-  Repaint;
-end;
-
-procedure TMainForm.ShowAboutBox;
-var
-  S: String;
-begin
-  { Removing the About box or modifying any existing text inside it is a
-    violation of the Inno Setup license agreement; see LICENSE.TXT.
-    However, adding additional lines to the end of the About box is
-    permitted. }
-  S := SetupTitle + ' version ' + SetupVersion + SNewLine;
-  if SetupTitle <> 'Inno Setup' then
-    S := S + (SNewLine + 'Based on Inno Setup' + SNewLine);
-  S := S + ('Copyright (C) 1997-2024 Jordan Russell' + SNewLine +
-    'Portions Copyright (C) 2000-2024 Martijn Laan' + SNewLine +
-    'All rights reserved.' + SNewLine2 +
-    'Inno Setup home page:' + SNewLine +
-    'https://www.innosetup.com/');
-  S := S + SNewLine2 + 'RemObjects Pascal Script home page:' + SNewLine +
-    'https://www.remobjects.com/ps';
-  if SetupMessages[msgAboutSetupNote] <> '' then
-    S := S + SNewLine2 + SetupMessages[msgAboutSetupNote];
-  if SetupMessages[msgTranslatorNote] <> '' then
-    S := S + SNewLine2 + SetupMessages[msgTranslatorNote];
-  StringChangeEx(S, '(C)', #$00A9, True);
-  LoggedMsgBox(S, SetupMessages[msgAboutSetupTitle], mbInformation, MB_OK, False, 0);
-end;
-
-class procedure TMainForm.ShowExceptionMsg(const S: String);
-begin
-  Log('Exception message:');
-  LoggedAppMessageBox(PChar(S), PChar(Application.Title), MB_OK or MB_ICONSTOP, True, IDOK);
-end;
-
-class procedure TMainForm.ShowException(Sender: TObject; E: Exception);
-begin
-  ShowExceptionMsg(AddPeriod(E.Message));
-end;
-
 procedure ProcessMessagesProc; far;
 begin
   Application.ProcessMessages;
@@ -3837,47 +3577,6 @@ begin
   Log(S);
 end;
 
-procedure TMainForm.SetStep(const AStep: TSetupStep; const HandleExceptions: Boolean);
-begin
-  CurStep := AStep;
-  if CodeRunner <> nil then begin
-    try
-      CodeRunner.RunProcedures('CurStepChanged', [Ord(CurStep)], False);
-    except
-      if HandleExceptions then begin
-        Log('CurStepChanged raised an exception.');
-        Application.HandleException(Self);
-      end
-      else begin
-        Log('CurStepChanged raised an exception (fatal).');
-        raise;
-      end;
-    end;
-  end;
-end;
-
-procedure TMainForm.InitializeWizard;
-begin
-  WizardForm := TWizardForm.Create(Application);
-  if CodeRunner <> nil then begin
-    try
-      CodeRunner.RunProcedures('InitializeWizard', [''], False);
-    except
-      Log('InitializeWizard raised an exception (fatal).');
-      raise;
-    end;
-  end;
-  WizardForm.FlipSizeAndCenterIfNeeded(shWindowVisible in SetupHeader.Options, MainForm, True);
-  WizardForm.SetCurPage(wpWelcome);
-  if InstallMode = imNormal then begin
-    WizardForm.ClickToStartPage; { this won't go past wpReady  }
-    SetActiveWindow(Application.Handle);  { ensure taskbar button is selected }
-    WizardForm.Show;
-  end
-  else
-    WizardForm.ClickThroughPages;
-end;
-
 function ShouldDisableFsRedirForRunEntry(const RunEntry: PSetupRunEntry): Boolean;
 begin
   Result := InstallDefaultDisableFsRedir;
@@ -3968,443 +3667,6 @@ begin
   InstShellExecEx(True, Operation, Filename, Parameters, Directory, ewNoWait, ShowCmd, ProcessMessagesProc, ErrorCode);
 end;
 
-function TMainForm.Install: Boolean;
-
-  procedure ProcessRunEntries;
-  var
-    CheckIfRestartNeeded: Boolean;
-    ChecksumBefore, ChecksumAfter: TMD5Digest;
-    WindowDisabler: TWindowDisabler;
-    I: Integer;
-    RunEntry: PSetupRunEntry;
-  begin
-    if Entries[seRun].Count <> 0 then begin
-      CheckIfRestartNeeded := (shRestartIfNeededByRun in SetupHeader.Options) and
-        not NeedsRestart;
-      if CheckIfRestartNeeded then
-        ChecksumBefore := MakePendingFileRenameOperationsChecksum;
-      WindowDisabler := nil;
-      try
-        for I := 0 to Entries[seRun].Count-1 do begin
-          RunEntry := PSetupRunEntry(Entries[seRun][I]);
-          if not(roPostInstall in RunEntry.Options) and
-             ShouldProcessRunEntry(WizardComponents, WizardTasks, RunEntry) then begin
-            { Disable windows during execution of [Run] entries so that a nice
-              "beep" is produced if the user tries clicking on WizardForm }
-            if WindowDisabler = nil then
-              WindowDisabler := TWindowDisabler.Create;
-            if RunEntry.StatusMsg <> '' then begin
-              try
-                WizardForm.StatusLabel.Caption := ExpandConst(RunEntry.StatusMsg);
-              except
-                { Don't die if the expansion fails with an exception. Just
-                  display the exception message, and proceed with the default
-                  status message. }
-                Application.HandleException(Self);
-                WizardForm.StatusLabel.Caption := SetupMessages[msgStatusRunProgram];
-              end;
-            end
-            else
-              WizardForm.StatusLabel.Caption := SetupMessages[msgStatusRunProgram];
-            WizardForm.StatusLabel.Update;
-            if roHideWizard in RunEntry.Options then begin
-              if WizardForm.Visible and not HideWizard then begin
-                HideWizard := True;
-                UpdateWizardFormVisibility;
-              end;
-            end
-            else begin
-              if HideWizard then begin
-                HideWizard := False;
-                UpdateWizardFormVisibility;
-              end;
-            end;
-            DebugNotifyEntry(seRun, I);
-            NotifyBeforeInstallEntry(RunEntry.BeforeInstall);
-            ProcessRunEntry(RunEntry);
-            NotifyAfterInstallEntry(RunEntry.AfterInstall);
-          end;
-        end;
-      finally
-        if HideWizard then begin
-          HideWizard := False;
-          UpdateWizardFormVisibility;
-        end;
-        WindowDisabler.Free;
-        if CheckIfRestartNeeded then begin
-          ChecksumAfter := MakePendingFileRenameOperationsChecksum;
-          if not MD5DigestsEqual(ChecksumBefore, ChecksumAfter) then
-            NeedsRestart := True;
-        end;
-      end;
-      Application.BringToFront;
-    end;
-  end;
-
-  procedure RestartApplications;
-  const
-    ERROR_FAIL_RESTART = 353;
-  var
-    Error: DWORD;
-    WindowDisabler: TWindowDisabler;
-  begin
-    if not NeedsRestart then begin
-      WizardForm.StatusLabel.Caption := SetupMessages[msgStatusRestartingApplications];
-      WizardForm.StatusLabel.Update;
-
-      Log('Attempting to restart applications.');
-
-      { Disable windows during application restart so that a nice
-        "beep" is produced if the user tries clicking on WizardForm }
-      WindowDisabler := TWindowDisabler.Create;
-      try
-        Error := RmRestart(RmSessionHandle, 0, nil);
-      finally
-        WindowDisabler.Free;
-      end;
-      Application.BringToFront;
-
-      if Error = ERROR_FAIL_RESTART then
-        Log('One or more applications could not be restarted.')
-      else if Error <> ERROR_SUCCESS then begin
-        RmEndSession(RmSessionHandle);
-        RmSessionStarted := False;
-        LogFmt('RmRestart returned an error: %d', [Error]);
-      end;
-    end else
-      Log('Need to restart Windows, not attempting to restart applications');
-  end;
-
-var
-  Succeeded, ChangesEnvironment, ChangesAssociations: Boolean;
-  S: String;
-begin
-  Result := False;
-  try
-    if not WizardForm.ValidateDirEdit then
-      Abort;
-    WizardDirValue := WizardForm.DirEdit.Text;
-    if not WizardForm.ValidateGroupEdit then
-      Abort;
-    WizardGroupValue := WizardForm.GroupEdit.Text;
-    WizardNoIcons := WizardForm.NoIconsCheck.Checked;
-    WizardSetupType := WizardForm.GetSetupType();
-    WizardForm.GetComponents(WizardComponents, WizardDeselectedComponents);
-    WizardForm.GetTasks(WizardTasks, WizardDeselectedTasks);
-    WizardPreparingYesRadio := WizardForm.PreparingYesRadio.Checked;
-    if InitSaveInf <> '' then
-      SaveInf(InitSaveInf);
-
-    Application.Restore;
-    Update;
-    if InstallMode = imSilent then begin
-      SetActiveWindow(Application.Handle);  { ensure taskbar button is selected }
-      WizardForm.Show;
-    end;
-    WizardForm.Update;
-
-    SetStep(ssInstall, False);
-    
-    ChangesEnvironment := EvalDirectiveCheck(SetupHeader.ChangesEnvironment);
-    ChangesAssociations := EvalDirectiveCheck(SetupHeader.ChangesAssociations);
-
-    PerformInstall(Succeeded, ChangesEnvironment, ChangesAssociations);
-    if not Succeeded then begin
-      { The user canceled the install or there was a fatal error }
-      TerminateApp;
-      Exit;
-    end;
-    { Can't cancel at any point after PerformInstall, so disable the button }
-    WizardForm.CancelButton.Enabled := False;
-
-    ProcessRunEntries;
-
-    if RmDoRestart and
-       (InitRestartApplications or
-        ((shRestartApplications in SetupHeader.Options) and not InitNoRestartApplications)) then
-      RestartApplications;
-
-    SetStep(ssPostInstall, True);
-
-    { Notify Windows of assocations/environment changes *after* ssPostInstall
-      since user might set more stuff there }
-    if ChangesAssociations then
-      SHChangeNotify(SHCNE_ASSOCCHANGED, SHCNF_IDLIST, nil, nil);
-    if ChangesEnvironment then
-      RefreshEnvironment;
-
-    if InstallMode <> imNormal then
-      WizardForm.Hide;
-
-    LogFmt('Need to restart Windows? %s', [SYesNo[NeedsRestart]]);
-    if NeedsRestart and not InitNoRestart then begin
-      with WizardForm do begin
-        ChangeFinishedLabel(ExpandSetupMessage(msgFinishedRestartLabel));
-        YesRadio.Visible := True;
-        NoRadio.Visible := True;
-      end;
-    end else begin
-      if CreatedIcon then
-        S := ExpandSetupMessage(msgFinishedLabel)
-      else
-        S := ExpandSetupMessage(msgFinishedLabelNoIcons);
-      with WizardForm do begin
-        ChangeFinishedLabel(S + SNewLine2 + SetupMessages[msgClickFinish]);
-        if not NeedsRestart then begin
-          UpdateRunList(WizardComponents, WizardTasks);
-          RunList.Visible := RunList.Items.Count > 0;
-        end;
-      end;
-    end;
-
-    if InstallMode = imNormal then begin
-      Application.Restore;
-      Update;
-    end;
-
-    Result := True;
-  except
-    { If an exception was raised, display the message, then terminate }
-    Application.HandleException(Self);
-    SetupExitCode := ecNextStepError;
-    TerminateApp;
-  end;
-end;
-
-procedure TMainForm.Finish(const FromPreparingPage: Boolean);
-
-  procedure WaitForForegroundLoss;
-
-    function IsForegroundProcess: Boolean;
-    var
-      W: HWND;
-      PID: DWORD;
-    begin
-      W := GetForegroundWindow;
-      Result := False;
-      if (W <> 0) and (GetWindowThreadProcessId(W, @PID) <> 0) then
-        Result := (PID = GetCurrentProcessId);
-    end;
-
-  var
-    StartTick: DWORD;
-  begin
-    StartTick := GetTickCount;
-    while IsForegroundProcess do begin
-      { Stop if it's taking too long (e.g. if the spawned process never
-        displays a window) }
-      if Cardinal(GetTickCount - StartTick) >= Cardinal(1000) then
-        Break;
-      ProcessMessagesProc;
-      WaitMessageWithTimeout(10);
-      ProcessMessagesProc;
-    end;
-  end;
-
-  procedure ProcessPostInstallRunEntries;
-  var
-    WindowDisabler: TWindowDisabler;
-    ProcessedNoWait: Boolean;
-    I: Integer;
-    RunEntry: PSetupRunEntry;
-  begin
-    WindowDisabler := nil;
-    try
-      ProcessedNoWait := False;
-      with WizardForm do begin
-        for I := 0 to RunList.Items.Count-1 do begin
-          if RunList.Checked[I] then begin
-            { Disable windows before processing the first entry }
-            if WindowDisabler = nil then
-              WindowDisabler := TWindowDisabler.Create;
-            RunEntry := PSetupRunEntry(Entries[seRun][Integer(RunList.ItemObject[I])]);
-            DebugNotifyEntry(seRun, Integer(RunList.ItemObject[I]));
-            NotifyBeforeInstallEntry(RunEntry.BeforeInstall);
-            ProcessRunEntry(RunEntry);
-            NotifyAfterInstallEntry(RunEntry.AfterInstall);
-            if RunEntry.Wait = rwNoWait then
-              ProcessedNoWait := True;
-          end;
-        end;
-      end;
-      { Give nowait processes some time to bring themselves to the
-        foreground before Setup exits. Without this delay, the application
-        underneath Setup can end up coming to the foreground instead.
-        (Note: Windows are already disabled at this point.) }
-      if ProcessedNoWait then
-        WaitForForegroundLoss;
-    finally
-      WindowDisabler.Free;
-    end;
-  end;
-
-var
-  S: String;
-begin
-  try
-    { Deactivate WizardForm so another application doesn't come to the
-      foreground when Hide is called. (Needed by WaitForForegroundLoss.) }
-    if GetForegroundWindow = WizardForm.Handle then
-      SetActiveWindow(Application.Handle);
-    WizardForm.Hide;
-
-    if not FromPreparingPage and not NeedsRestart then begin
-      ProcessPostInstallRunEntries;
-    end else begin
-      if FromPreparingPage then
-        SetupExitCode := ecPrepareToInstallFailedRestartNeeded
-      else if InitRestartExitCode <> 0 then
-        SetupExitCode := InitRestartExitCode;
-
-      if InitNoRestart then
-        RestartSystem := False
-      else begin
-        case InstallMode of
-          imNormal:
-            if FromPreparingPage then
-              RestartSystem := WizardForm.PreparingYesRadio.Checked
-            else
-              RestartSystem := WizardForm.YesRadio.Checked;
-          imSilent:
-            begin
-              if FromPreparingPage then
-                S := WizardForm.PrepareToInstallFailureMessage + SNewLine +
-                  SNewLine + SNewLine + ExpandSetupMessage(msgPrepareToInstallNeedsRestart)
-              else
-                S := ExpandSetupMessage(msgFinishedRestartMessage);
-              RestartSystem :=
-                LoggedMsgBox(S, '', mbConfirmation, MB_YESNO, True, IDYES) = IDYES;
-            end;
-          imVerySilent:
-            RestartSystem := True;
-        end;
-      end;
-      if not RestartSystem then
-        Log('Will not restart Windows automatically.');
-    end;
-
-    SetStep(ssDone, True);
-  except
-    Application.HandleException(Self);
-    SetupExitCode := ecNextStepError;
-  end;
-  TerminateApp;
-end;
-
-procedure TMainForm.FormCloseQuery(Sender: TObject; var CanClose: Boolean);
-
-  function ConfirmCancel(const DefaultConfirm: Boolean): Boolean;
-  var
-    Cancel, Confirm: Boolean;
-  begin
-    Cancel := True;
-    Confirm := DefaultConfirm;
-    WizardForm.CallCancelButtonClick(Cancel, Confirm);
-    Result := Cancel and (not Confirm or ExitSetupMsgBox);
-  end;
-
-begin
-  { Note: Setting CanClose to True causes Application.Terminate to be called;
-    we don't want that. }
-  CanClose := False;
-  if Assigned(WizardForm) and WizardForm.HandleAllocated and
-     IsWindowVisible(WizardForm.Handle) and IsWindowEnabled(WizardForm.Handle) and
-     WizardForm.CancelButton.CanFocus then begin
-    case CurStep of
-      ssPreInstall:
-        if ConfirmCancel((WizardForm.CurPageID <> wpPreparing) or (WizardForm.PrepareToInstallFailureMessage = '')) then begin
-          if WizardForm.CurPageID = wpPreparing then
-            SetupExitCode := ecPrepareToInstallFailed
-          else
-            SetupExitCode := ecCancelledBeforeInstall;
-          TerminateApp;
-        end;
-      ssInstall:
-        if (shAllowCancelDuringInstall in SetupHeader.Options) and not InitNoCancel then
-          if ConfirmCancel(True) then
-            NeedToAbortInstall := True;
-    end;
-  end;
-end;
-
-procedure TMainForm.WMGetDlgCode(var Message: TWMGetDlgCode);
-begin
-  Message.Result := Message.Result or DLGC_WANTTAB;
-end;
-
-function EWP(Wnd: HWND; Param: LPARAM): BOOL; stdcall;
-begin
-  { Note: GetParent is not used here because the other windows are not
-    actually child windows since they don't have WS_CHILD set. }
-  if GetWindowLong(Wnd, GWL_HWNDPARENT) <> Param then
-    Result := True
-  else begin
-    Result := False;
-    BringWindowToTop(Wnd);
-  end;
-end;
-
-procedure TMainForm.FormKeyDown(Sender: TObject; var Key: Word;
-  Shift: TShiftState);
-begin
-  { If, for some reason, the user doesn't have a mouse and the main form was
-    activated, there would normally be no way to reactivate the child form.
-    But this reactivates the form if the user hits a key on the keyboard }
-  if not(ssAlt in Shift) then begin
-    Key := 0;
-    EnumThreadWindows(GetCurrentThreadId, @EWP, Handle);
-  end;
-end;
-
-procedure TMainForm.UpdateWizardFormVisibility;
-var
-  ShouldShow: Boolean;
-begin
-  { Note: We don't adjust WizardForm.Visible because on Delphi 3+, if all forms
-    have Visible set to False, the application taskbar button disappears. }
-  if Assigned(WizardForm) and WizardForm.HandleAllocated then begin
-    ShouldShow := WizardForm.Showing and not HideWizard and
-      not IsIconic(Application.Handle);
-    if (GetWindowLong(WizardForm.Handle, GWL_STYLE) and WS_VISIBLE <> 0) <> ShouldShow then begin
-      if ShouldShow then
-        ShowWindow(WizardForm.Handle, SW_SHOW)
-      else
-        ShowWindow(WizardForm.Handle, SW_HIDE);
-    end;
-  end;
-end;
-
-function TMainForm.MainWindowHook(var Message: TMessage): Boolean;
-var
-  IsIcon: Boolean;
-begin
-  Result := False;
-  case Message.Msg of
-    WM_WINDOWPOSCHANGED: begin
-        { When the application window is minimized or restored, also hide or
-          show WizardForm.
-          Note: MainForm is hidden/shown automatically because its owner
-          window is Application.Handle. }
-        IsIcon := IsIconic(Application.Handle);
-        if IsMinimized <> IsIcon then begin
-          IsMinimized := IsIcon;
-          UpdateWizardFormVisibility;
-        end;
-      end;
-  end;
-end;
-
-procedure TMainForm.WMShowWindow(var Message: TWMShowWindow);
-begin
-  inherited;
-  { When showing, ensure WizardForm is the active window, not MainForm }
-  if Message.Show and (GetActiveWindow = Handle) and
-     Assigned(WizardForm) and WizardForm.HandleAllocated and
-     IsWindowVisible(WizardForm.Handle) then
-    SetActiveWindow(WizardForm.Handle);
-end;
-
-
 procedure InitIsWin64AndProcessorArchitectureAndMachineTypesSupportedBySystem;
 const
   PROCESSOR_ARCHITECTURE_ARM64 = 12;

+ 1 - 1
Projects/Src/Setup.NewDiskForm.pas

@@ -41,7 +41,7 @@ implementation
 
 uses
   SetupLdrAndSetup.Messages, Shared.SetupMessageIDs, PathFunc, Shared.CommonFunc.Vcl, Shared.CommonFunc, BrowseFunc,
-  Setup.MainForm, Setup.WizardForm;
+  Setup.MainFunc, Setup.WizardForm;
 
 {$R *.DFM}
 

+ 1 - 1
Projects/Src/Setup.RegDLL.pas

@@ -21,7 +21,7 @@ implementation
 
 uses
   SysUtils, Forms, PathFunc, Shared.CommonFunc.Vcl, Shared.CommonFunc, Setup.InstFunc, SetupLdrAndSetup.Messages, Shared.SetupMessageIDs,
-  Setup.LoggingFunc, SetupLdrAndSetup.RedirFunc, Setup.MainForm;
+  Setup.LoggingFunc, SetupLdrAndSetup.RedirFunc, Setup.MainFunc;
 
 function WaitForAndCloseProcessHandle(var AProcessHandle: THandle): DWORD;
 var

+ 1 - 1
Projects/Src/Setup.RegSvr.pas

@@ -17,7 +17,7 @@ implementation
 
 uses
   Windows, SysUtils, Classes, Forms, PathFunc, Shared.CommonFunc, Setup.InstFunc, Setup.InstFunc.Ole,
-  Shared.FileClass, Shared.CommonFunc.Vcl, Shared.Struct, Setup.MainForm,
+  Shared.FileClass, Shared.CommonFunc.Vcl, Shared.Struct, Setup.MainFunc,
   SetupLdrAndSetup.Messages, Shared.SetupMessageIDs, Setup.RegDLL, Setup.Helper;
 
 procedure DeleteOldTempFiles(const Path: String);

+ 1 - 1
Projects/Src/Setup.ScriptDlg.pas

@@ -201,7 +201,7 @@ implementation
 
 uses
   StrUtils,
-  Shared.Struct, Setup.MainForm, Setup.SelectFolderForm, SetupLdrAndSetup.Messages,
+  Shared.Struct, Setup.MainFunc, Setup.SelectFolderForm, SetupLdrAndSetup.Messages,
   Shared.SetupMessageIDs, PathFunc, Shared.CommonFunc.Vcl, Shared.CommonFunc,
   BrowseFunc, Setup.LoggingFunc, Setup.InstFunc;
 

+ 1 - 1
Projects/Src/Setup.ScriptFunc.pas

@@ -21,7 +21,7 @@ implementation
 uses
   Windows, Shared.ScriptFunc,
   Forms, uPSUtils, SysUtils, Classes, Graphics, Controls, TypInfo, ActiveX,
-  Shared.Struct, Setup.ScriptDlg, Setup.MainForm, PathFunc, Shared.CommonFunc.Vcl,
+  Shared.Struct, Setup.ScriptDlg, Setup.MainForm, Setup.MainFunc, PathFunc, Shared.CommonFunc.Vcl,
   Shared.CommonFunc, Shared.FileClass, SetupLdrAndSetup.RedirFunc,
   Setup.Install, SetupLdrAndSetup.InstFunc, Setup.InstFunc, Setup.InstFunc.Ole, SetupLdrAndSetup.Messages,
   Shared.SetupMessageIDs, Setup.NewDiskForm, BrowseFunc, Setup.WizardForm, Shared.VerInfoFunc,

+ 1 - 1
Projects/Src/Setup.SelectFolderForm.pas

@@ -42,7 +42,7 @@ function ShowSelectFolderDialog(const StartMenu, AppendDir: Boolean;
 implementation
 
 uses
-  PathFunc, SetupLdrAndSetup.Messages, Shared.SetupMessageIDs, Setup.MainForm,
+  PathFunc, SetupLdrAndSetup.Messages, Shared.SetupMessageIDs, Setup.MainFunc,
   Shared.SetupTypes, Setup.WizardForm, Shared.CommonFunc;
 
 {$R *.DFM}

+ 1 - 1
Projects/Src/Setup.SelectLanguageForm.pas

@@ -36,7 +36,7 @@ function AskForLanguage: Boolean;
 implementation
 
 uses
-  Shared.Struct, SetupLdrAndSetup.Messages, Shared.SetupMessageIDs, Setup.MainForm;
+  Shared.Struct, SetupLdrAndSetup.Messages, Shared.SetupMessageIDs, Setup.MainFunc;
 
 {$R *.DFM}
 

+ 1 - 1
Projects/Src/Setup.SetupForm.pas

@@ -74,7 +74,7 @@ implementation
 
 uses
   Generics.Collections, UITypes,
-  Shared.CommonFunc, Setup.MainForm, SetupLdrAndSetup.Messages, BidiUtils;
+  Shared.CommonFunc, Setup.MainFunc, SetupLdrAndSetup.Messages, BidiUtils;
 
 var
   WM_QueryCancelAutoPlay: UINT;

+ 1 - 1
Projects/Src/Setup.Uninstall.pas

@@ -22,7 +22,7 @@ uses
   Shared.SetupMessageIDs, SetupLdrAndSetup.InstFunc, Setup.InstFunc, Shared.Struct,
   Shared.SetupEntFunc, Setup.UninstallProgressForm, Setup.UninstallSharedFileForm,
   Shared.FileClass, Setup.ScriptRunner, Setup.DebugClient, Shared.SetupTypes,
-  Setup.LoggingFunc, Setup.MainForm, Setup.SpawnServer;
+  Setup.LoggingFunc, Setup.MainFunc, Setup.SpawnServer;
 
 type
   TExtUninstallLog = class(TUninstallLog)

+ 1 - 1
Projects/Src/Setup.WizardForm.pas

@@ -342,7 +342,7 @@ function ValidateCustomDirEdit(const AEdit: TEdit;
 implementation
 
 uses
-  ShellApi, ShlObj, Types, SetupLdrAndSetup.Messages, Setup.MainForm, PathFunc, Shared.CommonFunc.Vcl, Shared.CommonFunc,
+  ShellApi, ShlObj, Types, SetupLdrAndSetup.Messages, Setup.MainForm, Setup.MainFunc, PathFunc, Shared.CommonFunc.Vcl, Shared.CommonFunc,
   MD5, Setup.InstFunc, Setup.SelectFolderForm, Setup.FileExtractor, Setup.LoggingFunc, RestartManager, Setup.ScriptRunner;
 
 {$R *.DFM}