Setup.Start.pas 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276
  1. unit Setup.Start;
  2. {
  3. Inno Setup
  4. Copyright (C) 1997-2025 Jordan Russell
  5. Portions by Martijn Laan
  6. For conditions of distribution and use, see LICENSE.TXT.
  7. Setup program .dpr code
  8. }
  9. interface
  10. procedure Start;
  11. implementation
  12. {$SETPEOSVERSION 6.1}
  13. {$SETPESUBSYSVERSION 6.1}
  14. {$WEAKLINKRTTI ON}
  15. { SetupCustomStyle: The compiler may delete some of the resources included here }
  16. {$R Res\Setup.icon.res}
  17. {$R Res\Setup.images.res}
  18. {$R Res\Setup.version.res}
  19. uses
  20. Windows, Messages,
  21. SysUtils,
  22. Forms,
  23. RichEditViewer,
  24. Shared.CommonFunc, Shared.SetupMessageIDs, Shared.FileClass, Shared.Struct,
  25. SetupLdrAndSetup.Messages, SetupLdrAndSetup.InstFunc,
  26. Setup.LoggingFunc, Setup.MainFunc, Setup.Uninstall, Setup.RegSvr, Setup.MainForm;
  27. procedure ShowExceptionMsg;
  28. begin
  29. { Also see ShowExceptionMsg in Setup.Uninstall.pas }
  30. if ExceptObject is EAbort then begin
  31. Log('Got EAbort exception.');
  32. Exit;
  33. end;
  34. const Msg = GetExceptMessage;
  35. Log('Exception message:');
  36. LoggedMsgBox(PChar(Msg), Pointer(SetupMessages[msgErrorTitle]),
  37. MB_OK or MB_ICONSTOP, True, IDOK);
  38. { ^ use a Pointer cast instead of a PChar cast so that it will use "nil"
  39. if SetupMessages[msgErrorTitle] is empty due to the messages not being
  40. loaded yet. LoggedMsgBox displays 'Error' as the caption if the lpCaption
  41. parameter is nil, see GetMessageBoxCaption. }
  42. end;
  43. type
  44. TDummyClass = class
  45. private
  46. class function AntiShutdownHook(var Message: TMessage): Boolean;
  47. end;
  48. class function TDummyClass.AntiShutdownHook(var Message: TMessage): Boolean;
  49. begin
  50. { This causes Setup/Uninstall/RegSvr to all deny shutdown attempts.
  51. - If we were to return 1, Windows will send us a WM_ENDSESSION message and
  52. TApplication.WndProc will call Halt in response. This is no good because
  53. it would cause an unclean shutdown of Setup, and it would also prevent
  54. the right exit code from being returned.
  55. Even if TApplication.WndProc didn't call Halt, it is my understanding
  56. that Windows could kill us off after sending us the WM_ENDSESSION message
  57. (see the Remarks section of the WM_ENDSESSION docs).
  58. - SetupLdr denys shutdown attempts as well, so there is little point in
  59. Setup trying to handle them. (Depending on the version of Windows, we
  60. may never even get a WM_QUERYENDSESSION message because of that.)
  61. Note: TSetupForm also has a WM_QUERYENDSESSION handler of its own to
  62. prevent CloseQuery from being called. }
  63. Result := False;
  64. case Message.Msg of
  65. WM_QUERYENDSESSION: begin
  66. { Return zero, except if RestartInitiatedByThisProcess is set
  67. (which means we called RestartComputer previously) }
  68. if RestartInitiatedByThisProcess or (IsUninstaller and AllowUninstallerShutdown) then begin
  69. AcceptedQueryEndSessionInProgress := True;
  70. Message.Result := 1
  71. end else
  72. Message.Result := 0;
  73. Result := True;
  74. end;
  75. WM_ENDSESSION: begin
  76. { Should only get here if RestartInitiatedByThisProcess is set or an
  77. Uninstaller shutdown was allowed, or if the user forced a shutdown.
  78. Skip the default handling which calls Halt. No code of ours depends
  79. on the Halt call to clean up, and it could theoretically result in
  80. obscure reentrancy bugs.
  81. Example: I've found that combo boxes pump incoming sent messages
  82. when they are destroyed*; if one of those messages were a
  83. WM_ENDSESSION, the Halt call could cause another destructor to be
  84. to be entered (DoneApplication frees all still-existing forms)
  85. before the combo box's destructor has returned.
  86. * arguably a Windows bug. The internal ComboLBox window is created
  87. as a child (WS_CHILD) of GetDesktopWindow(); when it is destroyed,
  88. a WM_PARENTNOTIFY(WM_DESTROY) message is sent to the desktop window.
  89. Because the desktop window is on a separate thread, pending sent
  90. messages are dispatched during the SendMessage call. }
  91. if Bool(Message.wParam) = True then begin
  92. if not RestartInitiatedByThisProcess and IsUninstaller then
  93. HandleUninstallerEndSession;
  94. end else
  95. AcceptedQueryEndSessionInProgress := False;
  96. Result := True;
  97. end;
  98. end;
  99. end;
  100. procedure DisableWindowGhosting;
  101. var
  102. Proc: procedure; stdcall;
  103. begin
  104. Proc := GetProcAddress(GetModuleHandle(user32), 'DisableProcessWindowsGhosting');
  105. if Assigned(Proc) then
  106. Proc;
  107. end;
  108. procedure SelectMode;
  109. { Determines whether we should run as Setup, Uninstall, or RegSvr }
  110. var
  111. ParamName, ParamValue: String;
  112. Mode: (smSetup, smUninstaller, smRegSvr);
  113. F: TFile;
  114. ID: Longint;
  115. I: Integer;
  116. begin
  117. { When SignedUninstaller=yes, the EXE header specifies uninstaller mode by
  118. default. Use Setup mode instead if we're being called from SetupLdr. }
  119. SplitNewParamStr(1, ParamName, ParamValue);
  120. if SameText(ParamName, '/SL5=') then
  121. Exit;
  122. Mode := smSetup;
  123. for I := 1 to NewParamCount do begin
  124. if SameText(NewParamStr(I), '/UNINSTMODE') then begin
  125. Mode := smUninstaller;
  126. Break;
  127. end;
  128. if SameText(NewParamStr(I), '/REGSVRMODE') then begin
  129. Mode := smRegSvr;
  130. Break;
  131. end;
  132. end;
  133. if Mode = smSetup then begin
  134. { No mode specified on the command line; check the EXE header for one }
  135. F := TFile.Create(NewParamStr(0), fdOpenExisting, faRead, fsRead);
  136. try
  137. F.Seek(SetupExeModeOffset);
  138. F.ReadBuffer(ID, SizeOf(ID));
  139. finally
  140. F.Free;
  141. end;
  142. case ID of
  143. SetupExeModeUninstaller: Mode := smUninstaller;
  144. SetupExeModeRegSvr: Mode := smRegSvr;
  145. end;
  146. end;
  147. case Mode of
  148. smUninstaller: begin
  149. IsUninstaller := True;
  150. AllowUninstallerShutdown := False;
  151. RunUninstaller;
  152. { Shouldn't get here; RunUninstaller should Halt itself }
  153. Halt(1);
  154. end;
  155. smRegSvr: begin
  156. try
  157. RunRegSvr;
  158. except
  159. ShowExceptionMsg;
  160. end;
  161. Halt;
  162. end;
  163. end;
  164. end;
  165. procedure Start;
  166. begin
  167. try
  168. {$IFDEF DEBUG}
  169. ReportMemoryLeaksOnShutdown := True;
  170. {$ENDIF}
  171. SetErrorMode(SEM_FAILCRITICALERRORS);
  172. DisableWindowGhosting;
  173. Application.HookMainWindow(TDummyClass.AntiShutdownHook);
  174. TRichEditViewer.CustomShellExecute := ShellExecuteAsOriginalUser;
  175. { Don't respect the show command passed by the parent process.
  176. "Maximized" makes no sense as our windows don't have maximize/restore
  177. buttons, and "Minimized" is problematic as the VCL doesn't realize the
  178. app is minimized (Application.Restore has no effect because
  179. FAppIconic=False).
  180. If the parent process is SetupLdr, then there shouldn't be a non-normal
  181. show command because SetupLdr doesn't specify a show command when
  182. starting Setup. So this should really only matter when UseSetupLdr=no.
  183. First, overwrite the System.CmdShow variable to ensure that
  184. Application.Run (if called) doesn't mess with the main form's
  185. WindowState.
  186. Second, because ShowWindow overrides the value of nCmdShow on the first
  187. call if it's SW_SHOWNORMAL, SW_SHOW, or SW_SHOWDEFAULT (which isn't
  188. specifically documented; I tested each value), make a first call to
  189. ShowWindow here that doesn't actually do anything (the app window is
  190. already hidden at this point, and SW_HIDE is not one of the values that
  191. get overridden), so that when we show our first form, it will be the
  192. second call to ShowWindow and won't have its SW_SHOWNORMAL nCmdShow
  193. value overridden. }
  194. CmdShow := SW_SHOWNORMAL;
  195. ShowWindow(Application.Handle, SW_HIDE);
  196. SelectMode; { Only returns if we should run as Setup }
  197. except
  198. { Halt on any exception }
  199. ShowExceptionMsg;
  200. Halt(ecInitializationError);
  201. end;
  202. { Initialize.
  203. Note: There's no need to localize the following line since it's changed in
  204. InitializeSetup }
  205. Application.Title := 'Setup';
  206. Application.ShowMainForm := False;
  207. Application.OnException := TMainForm.ShowException;
  208. try
  209. Application.Initialize;
  210. Application.MainFormOnTaskBar := True;
  211. InitializeSetup;
  212. MainForm := TMainForm.Create(Application);
  213. InitializeWizard;
  214. except
  215. { Halt on any exception }
  216. ShowExceptionMsg;
  217. try
  218. DeinitSetup(False);
  219. except
  220. { don't propagate any exceptions, so that Halt is always called }
  221. ShowExceptionMsg;
  222. end;
  223. if SetupExitCode <> 0 then begin
  224. System.ExitCode := SetupExitCode;
  225. Halt;
  226. end else
  227. Halt(ecInitializationError);
  228. end;
  229. { Run }
  230. try
  231. Application.Run;
  232. except
  233. { Show any exception and continue }
  234. ShowExceptionMsg;
  235. end;
  236. { Deinitialize (clean up) }
  237. try
  238. DeinitSetup(SetupExitCode = 0);
  239. except
  240. { Show any exception and continue }
  241. ShowExceptionMsg;
  242. end;
  243. {$IFDEF DEBUG}
  244. { Prevent a warning about an expected memory leak, at least when exiting normally }
  245. Application.UnhookMainWindow(TDummyClass.AntiShutdownHook);
  246. {$ENDIF}
  247. System.ExitCode := SetupExitCode;
  248. Halt;
  249. end;
  250. end.