Setup.Install.HelperFunc.pas 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572
  1. unit Setup.Install.HelperFunc;
  2. {
  3. Inno Setup
  4. Copyright (C) 1997-2026 Jordan Russell
  5. Portions by Martijn Laan
  6. For conditions of distribution and use, see LICENSE.TXT.
  7. Installation helper functions which don't need install state such as UninstLog and RegisterFileList
  8. Only to be called by Setup.Install: if you want to reuse any of these functions from another unit
  9. you should move the function so somewhere else, like Setup.InstFunc
  10. }
  11. interface
  12. uses
  13. Windows, SHA256, Shared.FileClass, Shared.Struct, Setup.UninstallLog;
  14. type
  15. TSetupUninstallLog = class(TUninstallLog)
  16. protected
  17. procedure HandleException; override;
  18. end;
  19. TRegErrorFunc = (reRegSetValueEx, reRegCreateKeyEx, reRegOpenKeyEx);
  20. procedure SetFilenameLabelText(const S: String; const CallUpdate: Boolean);
  21. procedure SetStatusLabelText(const S: String;
  22. const ClearFilenameLabelText: Boolean = True);
  23. procedure InstallMessageBoxCallback(const Flags: Cardinal; const After: Boolean;
  24. const Param: NativeInt);
  25. procedure CalcFilesSize(var InstallFilesSize, AfterInstallFilesSize: Int64);
  26. procedure InitProgressGauge(const InstallFilesSize: Int64);
  27. procedure UpdateProgressGauge;
  28. procedure FinishProgressGauge(const HideGauge: Boolean);
  29. procedure SetProgress(const AProgress: Int64);
  30. procedure IncProgress(const N: Int64);
  31. function CurProgress: Int64;
  32. procedure ProcessEvents;
  33. procedure InternalProgressProc(const Bytes: Cardinal);
  34. procedure ExternalProgressProc64(const Bytes, MaxProgress: Int64);
  35. procedure JustProcessEventsProc64(const Bytes, Param: Int64);
  36. function AbortRetryIgnoreTaskDialogMsgBox(const Text: String;
  37. const RetryIgnoreAbortButtonLabels: array of String): Boolean;
  38. function FileTimeToStr(const AFileTime: TFileTime): String;
  39. function TryToGetSHA256OfFile(const Filename: String; var Sum: TSHA256Digest): Boolean;
  40. procedure CopySourceFileToDestFile(const SourceF, DestF: TFile;
  41. [ref] const Verification: TSetupFileVerification; const ISSigSourceFilename: String;
  42. const AExpectedSize: Int64);
  43. function ShortenFontFilename(const Filename: String): String;
  44. function GetLocalTimeAsStr: String;
  45. procedure PackCustomMessagesIntoString(var S: String);
  46. function PackCompiledCodeTextIntoString(const CompiledCodeText: AnsiString): String;
  47. procedure RegError(const Func: TRegErrorFunc; const RootKey: HKEY;
  48. const KeyName: String; const ErrorCode: DWORD);
  49. procedure WriteMsgData(const F: TFile);
  50. procedure MarkExeHeader(const F: TFile; const ModeID: Longint);
  51. procedure ProcessInstallDeleteEntries;
  52. procedure ProcessNeedRestartEvent;
  53. procedure ProcessComponentEntries;
  54. procedure ProcessTasksEntries;
  55. procedure ShutdownApplications;
  56. implementation
  57. uses
  58. Classes, SysUtils, Forms,
  59. NewProgressBar, PathFunc, RestartManager, TaskbarProgressFunc, UnsignedFunc,
  60. Shared.CommonFunc, Shared.CommonFunc.Vcl, Shared.SetupMessageIDs, Shared.SetupTypes,
  61. SetupLdrAndSetup.Messages,
  62. Setup.InstFunc, Setup.ISSigVerifyFunc, Setup.LoggingFunc, Setup.MainFunc, Setup.ScriptRunner,
  63. Setup.WizardForm, Setup.PathRedir;
  64. procedure TSetupUninstallLog.HandleException;
  65. begin
  66. Application.HandleException(Self);
  67. end;
  68. procedure SetFilenameLabelText(const S: String; const CallUpdate: Boolean);
  69. begin
  70. WizardForm.FilenameLabel.Caption := MinimizePathName(S, WizardForm.FilenameLabel.Font, WizardForm.FileNameLabel.Width);
  71. if CallUpdate then
  72. WizardForm.FilenameLabel.Update;
  73. end;
  74. procedure SetStatusLabelText(const S: String;
  75. const ClearFilenameLabelText: Boolean = True);
  76. begin
  77. if WizardForm.StatusLabel.Caption <> S then begin
  78. WizardForm.StatusLabel.Caption := S;
  79. WizardForm.StatusLabel.Update;
  80. end;
  81. if ClearFilenameLabelText then
  82. SetFilenameLabelText('', True);
  83. end;
  84. procedure InstallMessageBoxCallback(const Flags: Cardinal; const After: Boolean;
  85. const Param: NativeInt);
  86. const
  87. States: array [TNewProgressBarState] of TTaskbarProgressState =
  88. (tpsNormal, tpsError, tpsPaused);
  89. var
  90. NewState: TNewProgressBarState;
  91. begin
  92. if After then
  93. NewState := npbsNormal
  94. else if (Flags and MB_ICONSTOP) <> 0 then
  95. NewState := npbsError
  96. else
  97. NewState := npbsPaused;
  98. with WizardForm.ProgressGauge do begin
  99. State := NewState;
  100. Invalidate;
  101. end;
  102. SetAppTaskbarProgressState(States[NewState]);
  103. end;
  104. procedure CalcFilesSize(var InstallFilesSize, AfterInstallFilesSize: Int64);
  105. var
  106. CurFile: PSetupFileEntry;
  107. begin
  108. InstallFilesSize := 0;
  109. AfterInstallFilesSize := InstallFilesSize;
  110. for var N := 0 to Entries[seFile].Count-1 do begin
  111. CurFile := PSetupFileEntry(Entries[seFile][N]);
  112. if ShouldProcessFileEntry(WizardComponents, WizardTasks, CurFile, False) then begin
  113. with CurFile^ do begin
  114. var FileSize: Int64;
  115. if LocationEntry <> -1 then { not an "external" file }
  116. FileSize := PSetupFileLocationEntry(Entries[seFileLocation][
  117. LocationEntry])^.OriginalSize
  118. else
  119. FileSize := ExternalSize;
  120. Inc(InstallFilesSize, FileSize);
  121. if not (foDeleteAfterInstall in Options) then
  122. Inc(AfterInstallFilesSize, FileSize);
  123. end;
  124. end;
  125. end;
  126. end;
  127. var
  128. CurProgressValue: Int64;
  129. ProgressShiftCount: Cardinal;
  130. procedure InitProgressGauge(const InstallFilesSize: Int64);
  131. begin
  132. { Calculate the MaxValue for the progress meter }
  133. var NewMaxValue: Int64 := 1000 * Entries[seIcon].Count;
  134. if Entries[seIni].Count <> 0 then Inc(NewMaxValue, 1000);
  135. if Entries[seRegistry].Count <> 0 then Inc(NewMaxValue, 1000);
  136. Inc(NewMaxValue, InstallFilesSize);
  137. { To avoid progress updates that are too small to result in any visible
  138. change, divide the Max value by 2 until it's under 1500 }
  139. ProgressShiftCount := 0;
  140. while NewMaxValue >= 1500 do begin
  141. NewMaxValue := NewMaxValue shr 1;
  142. Inc(ProgressShiftCount);
  143. end;
  144. WizardForm.ProgressGauge.Max := Integer(NewMaxValue);
  145. SetMessageBoxCallbackFunc(InstallMessageBoxCallback, 0);
  146. end;
  147. procedure UpdateProgressGauge;
  148. begin
  149. var NewPosition := Integer(CurProgressValue shr ProgressShiftCount);
  150. if WizardForm.ProgressGauge.Position <> NewPosition then begin
  151. WizardForm.ProgressGauge.Position := NewPosition;
  152. WizardForm.ProgressGauge.Update;
  153. end;
  154. SetAppTaskbarProgressValue(UInt64(NewPosition), UInt64(WizardForm.ProgressGauge.Max));
  155. if (CodeRunner <> nil) and CodeRunner.FunctionExists('CurInstallProgressChanged', True) then begin
  156. try
  157. CodeRunner.RunProcedures('CurInstallProgressChanged', [NewPosition,
  158. WizardForm.ProgressGauge.Max], False);
  159. except
  160. Log('CurInstallProgressChanged raised an exception.');
  161. Application.HandleException(nil);
  162. end;
  163. end;
  164. end;
  165. procedure FinishProgressGauge(const HideGauge: Boolean);
  166. begin
  167. SetMessageBoxCallbackFunc(nil, 0);
  168. if HideGauge then
  169. WizardForm.ProgressGauge.Visible := False;
  170. SetAppTaskbarProgressState(tpsNoProgress);
  171. end;
  172. procedure SetProgress(const AProgress: Int64);
  173. begin
  174. CurProgressValue := AProgress;
  175. UpdateProgressGauge;
  176. end;
  177. procedure IncProgress(const N: Int64);
  178. begin
  179. Inc(CurProgressValue, N);
  180. UpdateProgressGauge;
  181. end;
  182. function CurProgress: Int64;
  183. begin
  184. Result := CurProgressValue;
  185. end;
  186. procedure ProcessEvents;
  187. { Processes any waiting events. Must call this this periodically or else
  188. events like clicking the Cancel button won't be processed.
  189. Calls Abort if NeedToAbortInstall is True, which is usually the result of
  190. the user clicking Cancel and the form closing. }
  191. begin
  192. if NeedToAbortInstall then Abort;
  193. Application.ProcessMessages;
  194. if NeedToAbortInstall then Abort;
  195. end;
  196. procedure InternalProgressProc(const Bytes: Cardinal);
  197. begin
  198. IncProgress(Bytes);
  199. ProcessEvents;
  200. end;
  201. procedure ExternalProgressProc64(const Bytes, MaxProgress: Int64);
  202. begin
  203. var NewProgress := CurProgress;
  204. Inc(NewProgress, Bytes);
  205. { In case the source file was larger than we thought it was, stop the
  206. progress bar at the maximum amount. Also see CopySourceFileToDestFile. }
  207. if NewProgress > MaxProgress then
  208. NewProgress := MaxProgress;
  209. SetProgress(NewProgress);
  210. ProcessEvents;
  211. end;
  212. procedure JustProcessEventsProc64(const Bytes, Param: Int64);
  213. begin
  214. ProcessEvents;
  215. end;
  216. function AbortRetryIgnoreTaskDialogMsgBox(const Text: String;
  217. const RetryIgnoreAbortButtonLabels: array of String): Boolean;
  218. { Returns True if Ignore was selected, False if Retry was selected, or
  219. calls Abort if Abort was selected. }
  220. begin
  221. Result := False;
  222. case LoggedTaskDialogMsgBox('', SetupMessages[msgAbortRetryIgnoreSelectAction], Text, '',
  223. mbError, MB_ABORTRETRYIGNORE, RetryIgnoreAbortButtonLabels, 0, True, IDABORT) of
  224. IDABORT: Abort;
  225. IDRETRY: ;
  226. IDIGNORE: Result := True;
  227. else
  228. Log('LoggedTaskDialogMsgBox returned an unexpected value. Assuming Abort.');
  229. Abort;
  230. end;
  231. end;
  232. function FileTimeToStr(const AFileTime: TFileTime): String;
  233. { Converts a TFileTime into a string for log purposes. }
  234. var
  235. FT: TFileTime;
  236. ST: TSystemTime;
  237. begin
  238. FileTimeToLocalFileTime(AFileTime, FT);
  239. if FileTimeToSystemTime(FT, ST) then
  240. Result := Format('%.4u-%.2u-%.2u %.2u:%.2u:%.2u.%.3u',
  241. [ST.wYear, ST.wMonth, ST.wDay, ST.wHour, ST.wMinute, ST.wSecond,
  242. ST.wMilliseconds])
  243. else
  244. Result := '(invalid)';
  245. end;
  246. function TryToGetSHA256OfFile(const Filename: String; var Sum: TSHA256Digest): Boolean;
  247. { Like GetSHA256OfFile but traps exceptions locally. Returns True if successful. }
  248. begin
  249. try
  250. Sum := GetSHA256OfFile(Filename);
  251. Result := True;
  252. except
  253. Result := False;
  254. end;
  255. end;
  256. procedure CopySourceFileToDestFile(const SourceF, DestF: TFile;
  257. [ref] const Verification: TSetupFileVerification; const ISSigSourceFilename: String;
  258. const AExpectedSize: Int64);
  259. { Copies all bytes from SourceF to DestF, incrementing process meter as it
  260. goes. Assumes file pointers of both are 0. }
  261. var
  262. Buf: array[0..16383] of Byte;
  263. Context: TSHA256Context;
  264. begin
  265. var ExpectedFileHash: TSHA256Digest;
  266. if Verification.Typ <> fvNone then begin
  267. if Verification.Typ = fvHash then
  268. ExpectedFileHash := Verification.Hash
  269. else
  270. DoISSigVerify(SourceF, nil, ISSigSourceFilename, True, Verification.ISSigAllowedKeys, ExpectedFileHash);
  271. { ExpectedFileHash checked below after copy }
  272. SHA256Init(Context);
  273. end;
  274. var MaxProgress := CurProgress;
  275. Inc(MaxProgress, AExpectedSize);
  276. var BytesLeft := SourceF.Size;
  277. { To avoid file system fragmentation, preallocate all of the bytes in the
  278. destination file }
  279. DestF.Seek(BytesLeft);
  280. DestF.Truncate;
  281. DestF.Seek(0);
  282. while BytesLeft > 0 do begin
  283. var BufSize: Cardinal := SizeOf(Buf);
  284. if BytesLeft < BufSize then
  285. BufSize := Cardinal(BytesLeft);
  286. SourceF.ReadBuffer(Buf, BufSize);
  287. DestF.WriteBuffer(Buf, BufSize);
  288. Dec(BytesLeft, BufSize);
  289. if Verification.Typ <> fvNone then
  290. SHA256Update(Context, Buf, BufSize);
  291. ExternalProgressProc64(BufSize, MaxProgress);
  292. end;
  293. if Verification.Typ <> fvNone then begin
  294. if not SHA256DigestsEqual(SHA256Final(Context), ExpectedFileHash) then
  295. VerificationError(veFileHashIncorrect);
  296. Log(VerificationSuccessfulLogMessage);
  297. end;
  298. { In case the source file was shorter than we thought it was, bump the
  299. progress bar to the maximum amount }
  300. SetProgress(MaxProgress);
  301. end;
  302. function ShortenFontFilename(const Filename: String): String;
  303. { Removes the path from Filename if it's in the Fonts directory.
  304. Filename should be a super path. }
  305. var
  306. FontDir: String;
  307. begin
  308. Result := Filename;
  309. FontDir := GetShellFolder(False, sfFonts);
  310. if FontDir <> '' then begin
  311. { Filename/Result is a super path but FontDir is not. So using
  312. PathConvertSuperToNormal because PathSame does not consider
  313. a super path and its normal form to be the same. This use of
  314. PathConvertSuperToNormal does not introduce a limitation. }
  315. if PathSame(PathExtractDir(PathConvertSuperToNormal(Result)), FontDir) then
  316. Result := PathExtractName(Result);
  317. end;
  318. end;
  319. function GetLocalTimeAsStr: String;
  320. var
  321. SysTime: TSystemTime;
  322. begin
  323. GetLocalTime(SysTime);
  324. SetString(Result, PChar(@SysTime), SizeOf(SysTime) div SizeOf(Char));
  325. end;
  326. procedure PackCustomMessagesIntoString(var S: String);
  327. var
  328. M: TMemoryStream;
  329. Count, N: Integer;
  330. begin
  331. M := TMemoryStream.Create;
  332. try
  333. Count := 0;
  334. M.WriteBuffer(Count, SizeOf(Count)); { overwritten later }
  335. for var I := 0 to Entries[seCustomMessage].Count-1 do begin
  336. with PSetupCustomMessageEntry(Entries[seCustomMessage][I])^ do begin
  337. if (LangIndex = -1) or (LangIndex = ActiveLanguage) then begin
  338. N := Length(Name);
  339. M.WriteBuffer(N, SizeOf(N));
  340. M.WriteBuffer(Name[1], N*SizeOf(Name[1]));
  341. N := Length(Value);
  342. M.WriteBuffer(N, SizeOf(N));
  343. M.WriteBuffer(Value[1], N*SizeOf(Value[1]));
  344. Inc(Count);
  345. end;
  346. end;
  347. end;
  348. M.Seek(0, soFromBeginning);
  349. M.WriteBuffer(Count, SizeOf(Count));
  350. SetString(S, PChar(M.Memory), M.Size div SizeOf(Char));
  351. finally
  352. M.Free;
  353. end;
  354. end;
  355. function PackCompiledCodeTextIntoString(const CompiledCodeText: AnsiString): String;
  356. var
  357. N: Integer;
  358. begin
  359. N := Length(CompiledCodeText);
  360. if N mod 2 = 1 then
  361. Inc(N); { This will lead to 1 extra byte being moved but that's ok since it is the #0 }
  362. N := N div 2;
  363. SetString(Result, PChar(Pointer(CompiledCodeText)), N);
  364. end;
  365. procedure RegError(const Func: TRegErrorFunc; const RootKey: HKEY;
  366. const KeyName: String; const ErrorCode: DWORD);
  367. const
  368. ErrorMsgs: array[TRegErrorFunc] of TSetupMessageID =
  369. (msgErrorRegWriteKey, msgErrorRegCreateKey, msgErrorRegOpenKey);
  370. FuncNames: array[TRegErrorFunc] of String =
  371. ('RegSetValueEx', 'RegCreateKeyEx', 'RegOpenKeyEx');
  372. begin
  373. raise Exception.Create(FmtSetupMessage(ErrorMsgs[Func],
  374. [GetRegRootKeyName(RootKey), KeyName]) + SNewLine2 +
  375. FmtSetupMessage(msgErrorFunctionFailedWithMessage,
  376. [FuncNames[Func], IntToStr(ErrorCode), Win32ErrorString(DWORD(ErrorCode))]));
  377. end;
  378. procedure WriteMsgData(const F: TFile);
  379. var
  380. MsgLangOpts: TMessagesLangOptions;
  381. LangEntry: PSetupLanguageEntry;
  382. begin
  383. FillChar(MsgLangOpts, SizeOf(MsgLangOpts), 0);
  384. MsgLangOpts.ID := MessagesLangOptionsID;
  385. { TMessagesLangOptions fields and flags from LangOptions - together these are a simplified
  386. version of TSetupLanguageEntry }
  387. StrPLCopy(MsgLangOpts.DialogFontName, LangOptions.DialogFontName,
  388. (SizeOf(MsgLangOpts.DialogFontName) div SizeOf(MsgLangOpts.DialogFontName[0])) - 1);
  389. MsgLangOpts.DialogFontSize := LangOptions.DialogFontSize;
  390. MsgLangOpts.DialogFontBaseScaleWidth := LangOptions.DialogFontBaseScaleWidth;
  391. MsgLangOpts.DialogFontBaseScaleHeight := LangOptions.DialogFontBaseScaleHeight;
  392. if LangOptions.RightToLeft then
  393. Include(MsgLangOpts.Flags, lfRightToLeft);
  394. { Other TMessagesLangOptions fields and flags - all appearance only }
  395. MsgLangOpts.WizardSizePercentX := SetupHeader.WizardSizePercentX;
  396. MsgLangOpts.WizardSizePercentY := SetupHeader.WizardSizePercentY;
  397. MsgLangOpts.WizardBackColor := OrigSetupHeaderWizardBackColor; { See Setup.MainFunc }
  398. MsgLangOpts.WizardBackColorDynamicDark := SetupHeader.WizardBackColorDynamicDark;
  399. MsgLangOpts.WizardLightControlStyling := SetupHeader.WizardLightControlStyling;
  400. if shWizardModern in SetupHeader.Options then
  401. Include(MsgLangOpts.Flags, lfWizardModern);
  402. if shWizardBorderStyled in SetupHeader.Options then
  403. Include(MsgLangOpts.Flags, lfWizardBorderStyled);
  404. if shWizardKeepAspectRatio in SetupHeader.Options then
  405. Include(MsgLangOpts.Flags, lfWizardKeepAspectRatio);
  406. if shWizardBevelsHidden in SetupHeader.Options then
  407. Include(MsgLangOpts.Flags, lfWizardBevelsHidden);
  408. if SetupHeader.WizardDarkStyle = wdsDark then
  409. Include(MsgLangOpts.Flags, lfWizardDarkStyleDark)
  410. else if SetupHeader.WizardDarkStyle = wdsDynamic then
  411. Include(MsgLangOpts.Flags, lfWizardDarkStyleDynamic);
  412. LangEntry := Entries[seLanguage][ActiveLanguage];
  413. F.WriteBuffer(LangEntry.Data[1], ULength(LangEntry.Data));
  414. F.WriteBuffer(MsgLangOpts, SizeOf(MsgLangOpts));
  415. end;
  416. procedure MarkExeHeader(const F: TFile; const ModeID: Longint);
  417. begin
  418. F.Seek(SetupExeModeOffset);
  419. F.WriteBuffer(ModeID, SizeOf(ModeID));
  420. end;
  421. procedure ProcessInstallDeleteEntries;
  422. begin
  423. for var I := 0 to Entries[seInstallDelete].Count-1 do
  424. with PSetupDeleteEntry(Entries[seInstallDelete][I])^ do
  425. if ShouldProcessEntry(WizardComponents, WizardTasks, Components, Tasks, Languages, Check) then begin
  426. DebugNotifyEntry(seInstallDelete, I);
  427. NotifyBeforeInstallEntry(BeforeInstall);
  428. const Path = ApplyPathRedirRules(InstallDefault64Bit, ExpandConst(Name), tpCurrent);
  429. case DeleteType of
  430. dfFiles, dfFilesAndOrSubdirs:
  431. DelTree(Path, False, True, DeleteType = dfFilesAndOrSubdirs, False,
  432. nil, nil, nil);
  433. dfDirIfEmpty:
  434. DelTree(Path, True, False, False, False, nil, nil, nil);
  435. end;
  436. NotifyAfterInstallEntry(AfterInstall);
  437. end;
  438. end;
  439. procedure ProcessNeedRestartEvent;
  440. begin
  441. if (CodeRunner <> nil) and CodeRunner.FunctionExists('NeedRestart', True) then begin
  442. if not NeedsRestart then begin
  443. try
  444. if CodeRunner.RunBooleanFunctions('NeedRestart', [''], bcTrue, False, False) then begin
  445. NeedsRestart := True;
  446. Log('Will restart because NeedRestart returned True.');
  447. end;
  448. except
  449. Log('NeedRestart raised an exception.');
  450. Application.HandleException(nil);
  451. end;
  452. end
  453. else
  454. Log('Not calling NeedRestart because a restart has already been deemed necessary.');
  455. end;
  456. end;
  457. procedure ProcessComponentEntries;
  458. begin
  459. for var I := 0 to Entries[seComponent].Count-1 do begin
  460. with PSetupComponentEntry(Entries[seComponent][I])^ do begin
  461. if ShouldProcessEntry(WizardComponents, nil, Name, '', Languages, '') and (coRestart in Options) then begin
  462. NeedsRestart := True;
  463. Break;
  464. end;
  465. end;
  466. end;
  467. end;
  468. procedure ProcessTasksEntries;
  469. begin
  470. for var I := 0 to Entries[seTask].Count-1 do begin
  471. with PSetupTaskEntry(Entries[seTask][I])^ do begin
  472. if ShouldProcessEntry(nil, WizardTasks, '', Name, Languages, '') and (toRestart in Options) then begin
  473. NeedsRestart := True;
  474. Break;
  475. end;
  476. end;
  477. end;
  478. end;
  479. procedure ShutdownApplications;
  480. const
  481. ERROR_FAIL_SHUTDOWN = 351;
  482. ForcedStrings: array [Boolean] of String = ('', ' (forced)');
  483. ForcedActionFlag: array [Boolean] of ULONG = (0, RmForceShutdown);
  484. var
  485. Forced: Boolean;
  486. Error: DWORD;
  487. begin
  488. Forced := InitForceCloseApplications or
  489. ((shForceCloseApplications in SetupHeader.Options) and not InitNoForceCloseApplications);
  490. Log('Shutting down applications using our files.' + ForcedStrings[Forced]);
  491. RmDoRestart := True;
  492. Error := RmShutdown(RmSessionHandle, ForcedActionFlag[Forced], nil);
  493. while Error = ERROR_FAIL_SHUTDOWN do begin
  494. Log('Some applications could not be shut down.');
  495. if AbortRetryIgnoreTaskDialogMsgBox(
  496. SetupMessages[msgErrorCloseApplications],
  497. [SetupMessages[msgAbortRetryIgnoreRetry], SetupMessages[msgAbortRetryIgnoreIgnore], SetupMessages[msgAbortRetryIgnoreCancel]]) then
  498. Break;
  499. Log('Retrying to shut down applications using our files.' + ForcedStrings[Forced]);
  500. Error := RmShutdown(RmSessionHandle, ForcedActionFlag[Forced], nil);
  501. end;
  502. { Close session on all errors except for ERROR_FAIL_SHUTDOWN, should still call RmRestart in that case. }
  503. if (Error <> ERROR_SUCCESS) and (Error <> ERROR_FAIL_SHUTDOWN) then begin
  504. RmEndSession(RmSessionHandle);
  505. LogFmt('RmShutdown returned an error: %d', [Error]);
  506. RmDoRestart := False;
  507. end;
  508. end;
  509. end.