| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264 |
- unit Setup.Start;
- {
- Inno Setup
- Copyright (C) 1997-2026 Jordan Russell
- Portions by Martijn Laan
- For conditions of distribution and use, see LICENSE.TXT.
- Setup program .dpr code
- }
- interface
- procedure Start;
- implementation
- {$SETPEOSVERSION 6.1}
- {$SETPESUBSYSVERSION 6.1}
- {$WEAKLINKRTTI ON}
- { SetupCustomStyle: The compiler may delete some of the resources included here }
- {$R Res\Setup.icon.res}
- {$R Res\Setup.images.res}
- {$R Res\Setup.version.res}
- uses
- Windows, Messages,
- SysUtils,
- Forms,
- RichEditViewer,
- Shared.CommonFunc, Shared.SetupMessageIDs, Shared.FileClass, Shared.Struct,
- SetupLdrAndSetup.Messages, SetupLdrAndSetup.InstFunc,
- Setup.LoggingFunc, Setup.MainFunc, Setup.Uninstall, Setup.RegSvr, Setup.MainForm;
- type
- TDummyClass = class
- private
- class function AntiShutdownHook(var Message: TMessage): Boolean;
- class procedure ShowException(Sender: TObject; E: Exception);
- end;
- class function TDummyClass.AntiShutdownHook(var Message: TMessage): Boolean;
- begin
- { This causes Setup/Uninstall/RegSvr to all deny shutdown attempts.
- - If we were to return 1, Windows will send us a WM_ENDSESSION message and
- TApplication.WndProc will call Halt in response. This is no good because
- it would cause an unclean shutdown of Setup, and it would also prevent
- the right exit code from being returned.
- Even if TApplication.WndProc didn't call Halt, it is my understanding
- that Windows could kill us off after sending us the WM_ENDSESSION message
- (see the Remarks section of the WM_ENDSESSION docs).
- - SetupLdr denys shutdown attempts as well, so there is little point in
- Setup trying to handle them. (Depending on the version of Windows, we
- may never even get a WM_QUERYENDSESSION message because of that.)
- Note: TSetupForm also has a WM_QUERYENDSESSION handler of its own to
- prevent CloseQuery from being called. }
- Result := False;
- case Message.Msg of
- WM_QUERYENDSESSION: begin
- { Return zero, except if RestartInitiatedByThisProcess is set
- (which means we called RestartComputer previously) }
- if RestartInitiatedByThisProcess or (IsUninstaller and AllowUninstallerShutdown) then begin
- AcceptedQueryEndSessionInProgress := True;
- Message.Result := 1
- end else
- Message.Result := 0;
- Result := True;
- end;
- WM_ENDSESSION: begin
- { Should only get here if RestartInitiatedByThisProcess is set or an
- Uninstaller shutdown was allowed, or if the user forced a shutdown.
- Skip the default handling which calls Halt. No code of ours depends
- on the Halt call to clean up, and it could theoretically result in
- obscure reentrancy bugs.
- Example: I've found that combo boxes pump incoming sent messages
- when they are destroyed*; if one of those messages were a
- WM_ENDSESSION, the Halt call could cause another destructor to be
- to be entered (DoneApplication frees all still-existing forms)
- before the combo box's destructor has returned.
- * arguably a Windows bug. The internal ComboLBox window is created
- as a child (WS_CHILD) of GetDesktopWindow(); when it is destroyed,
- a WM_PARENTNOTIFY(WM_DESTROY) message is sent to the desktop window.
- Because the desktop window is on a separate thread, pending sent
- messages are dispatched during the SendMessage call. }
- if Bool(Message.wParam) = True then begin
- if not RestartInitiatedByThisProcess and IsUninstaller then
- HandleUninstallerEndSession;
- end else
- AcceptedQueryEndSessionInProgress := False;
- Result := True;
- end;
- end;
- end;
- class procedure TDummyClass.ShowException(Sender: TObject; E: Exception);
- begin
- ShowExceptionMsgText(AddPeriod(E.Message));
- end;
- procedure DisableWindowGhosting;
- var
- Proc: procedure; stdcall;
- begin
- Proc := GetProcAddress(GetModuleHandle(user32), 'DisableProcessWindowsGhosting');
- if Assigned(Proc) then
- Proc;
- end;
- procedure SelectMode;
- { Determines whether we should run as Setup, Uninstall, or RegSvr }
- var
- ParamName, ParamValue: String;
- Mode: (smSetup, smUninstaller, smRegSvr);
- F: TFile;
- ID: Longint;
- I: Integer;
- begin
- { When SignedUninstaller=yes, the EXE header specifies uninstaller mode by
- default. Use Setup mode instead if we're being called from SetupLdr. }
- SplitNewParamStr(1, ParamName, ParamValue);
- if SameText(ParamName, '/SL5=') then
- Exit;
- Mode := smSetup;
- for I := 1 to NewParamCount do begin
- if SameText(NewParamStr(I), '/UNINSTMODE') then begin
- Mode := smUninstaller;
- Break;
- end;
- if SameText(NewParamStr(I), '/REGSVRMODE') then begin
- Mode := smRegSvr;
- Break;
- end;
- end;
- if Mode = smSetup then begin
- { No mode specified on the command line; check the EXE header for one }
- F := TFile.Create(NewParamStr(0), fdOpenExisting, faRead, fsRead);
- try
- F.Seek(SetupExeModeOffset);
- F.ReadBuffer(ID, SizeOf(ID));
- finally
- F.Free;
- end;
- case ID of
- SetupExeModeUninstaller: Mode := smUninstaller;
- SetupExeModeRegSvr: Mode := smRegSvr;
- end;
- end;
- case Mode of
- smUninstaller: begin
- IsUninstaller := True;
- AllowUninstallerShutdown := False;
- RunUninstaller;
- { Shouldn't get here; RunUninstaller should Halt itself }
- Halt(1);
- end;
- smRegSvr: begin
- try
- RunRegSvr;
- except
- ShowExceptionMsg;
- end;
- Halt;
- end;
- end;
- end;
- procedure Start;
- begin
- { The proper localized, mode-specific title gets assigned later. But for
- now, set it to "Setup" so that if an exception is raised very early, the
- message box won't show the EXE name for its title. }
- Application.Title := 'Setup';
- Application.OnException := TDummyClass.ShowException;
- try
- SetErrorMode(SEM_FAILCRITICALERRORS);
- DisableWindowGhosting;
- Application.HookMainWindow(TDummyClass.AntiShutdownHook);
- TRichEditViewer.CustomShellExecute := ShellExecuteAsOriginalUser;
- { Don't respect the show command passed by the parent process.
- "Maximized" makes no sense as our windows don't have maximize/restore
- buttons, and "Minimized" is problematic as the VCL doesn't realize the
- app is minimized (Application.Restore has no effect because
- FAppIconic=False).
- If the parent process is SetupLdr, then there shouldn't be a non-normal
- show command because SetupLdr doesn't specify a show command when
- starting Setup. So this should really only matter when UseSetupLdr=no.
- First, overwrite the System.CmdShow variable to ensure that
- Application.Run (if called) doesn't mess with the main form's
- WindowState.
- Second, because ShowWindow overrides the value of nCmdShow on the first
- call if it's SW_SHOWNORMAL, SW_SHOW, or SW_SHOWDEFAULT (which isn't
- specifically documented; I tested each value), make a first call to
- ShowWindow here that doesn't actually do anything (the app window is
- already hidden at this point, and SW_HIDE is not one of the values that
- get overridden), so that when we show our first form, it will be the
- second call to ShowWindow and won't have its SW_SHOWNORMAL nCmdShow
- value overridden. }
- CmdShow := SW_SHOWNORMAL;
- ShowWindow(Application.Handle, SW_HIDE);
- SelectMode; { Only returns if we should run as Setup }
- except
- { Halt on any exception }
- ShowExceptionMsg;
- Halt(ecInitializationError);
- end;
- { Initialize (Setup mode) }
- Application.ShowMainForm := False;
- try
- Application.Initialize;
- Application.MainFormOnTaskBar := True;
- InitializeSetup;
- MainForm := TMainForm.Create(Application);
- InitializeWizard;
- except
- { Halt on any exception }
- ShowExceptionMsg;
- try
- DeinitSetup(False);
- except
- { don't propagate any exceptions, so that Halt is always called }
- ShowExceptionMsg;
- end;
- if SetupExitCode <> 0 then begin
- System.ExitCode := SetupExitCode;
- Halt;
- end else
- Halt(ecInitializationError);
- end;
- { Run }
- try
- Application.Run;
- except
- { Show any exception and continue }
- ShowExceptionMsg;
- end;
- { Deinitialize (clean up) }
- try
- DeinitSetup(SetupExitCode = 0);
- except
- { Show any exception and continue }
- ShowExceptionMsg;
- end;
- {$IFDEF DEBUG}
- { Prevent a warning about an expected memory leak, at least when exiting normally }
- Application.UnhookMainWindow(TDummyClass.AntiShutdownHook);
- {$ENDIF}
- System.ExitCode := SetupExitCode;
- Halt;
- end;
- end.
|