system_check_page.iss.inc 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702
  1. { Copyright 2019-2021 Espressif Systems (Shanghai) CO LTD
  2. SPDX-License-Identifier: Apache-2.0 }
  3. { SystemCheck states }
  4. const
  5. SYSTEM_CHECK_STATE_INIT = 0; { No check was executed yet. }
  6. SYSTEM_CHECK_STATE_RUNNING = 1; { Check is in progress and can be cancelled. }
  7. SYSTEM_CHECK_STATE_COMPLETE = 2; { Check is complete. }
  8. SYSTEM_CHECK_STATE_STOPPED = 3; { User stopped the check. }
  9. var
  10. { RTF View to display content of system check. }
  11. SystemCheckViewer: TNewMemo;
  12. { Indicate state of System Check. }
  13. SystemCheckState:Integer;
  14. { Text representation of log messages which are then converte to RTF. }
  15. SystemLogText: TStringList;
  16. { Message for user which gives a hint how to correct the problem. }
  17. SystemCheckHint: String;
  18. { Setup Page which displays progress/result of system check. }
  19. SystemCheckPage: TOutputMsgWizardPage;
  20. { TimeCounter for Spinner animation invoked during command execution. }
  21. TimeCounter:Integer;
  22. { Spinner is TStringList, because characters like backslash must be escaped and stored on two bytes. }
  23. Spinner: TStringList;
  24. { Button to request display of full log of system check/installation. }
  25. FullLogButton: TNewButton;
  26. { Button to request application of available fixtures. }
  27. ApplyFixesButton: TNewButton;
  28. { Commands which should be executed to fix problems discovered during system check. }
  29. Fixes: TStringList;
  30. { Button to request Stop of System Checks manually. }
  31. StopSystemCheckButton: TNewButton;
  32. { Count number of createde virtualenv to avoid collision with previous runs. }
  33. VirtualEnvCounter: Integer;
  34. { Indicates whether system check was able to find running Windows Defender. }
  35. var IsWindowsDefenderEnabled: Boolean;
  36. { Const values for user32.dll which allows scrolling of the text view. }
  37. const
  38. WM_VSCROLL = $0115;
  39. SB_BOTTOM = 7;
  40. type
  41. TMsg = record
  42. hwnd: HWND;
  43. message: UINT;
  44. wParam: Longint;
  45. lParam: Longint;
  46. time: DWORD;
  47. pt: TPoint;
  48. end;
  49. const
  50. PM_REMOVE = 1;
  51. { Functions to communicate via Windows API. }
  52. function PeekMessage(var lpMsg: TMsg; hWnd: HWND; wMsgFilterMin, wMsgFilterMax, wRemoveMsg: UINT): BOOL; external 'PeekMessageW@user32.dll stdcall';
  53. function TranslateMessage(const lpMsg: TMsg): BOOL; external 'TranslateMessage@user32.dll stdcall';
  54. function DispatchMessage(const lpMsg: TMsg): Longint; external 'DispatchMessageW@user32.dll stdcall';
  55. procedure AppProcessMessage;
  56. var
  57. Msg: TMsg;
  58. begin
  59. while PeekMessage(Msg, WizardForm.Handle, 0, 0, PM_REMOVE) do begin
  60. TranslateMessage(Msg);
  61. DispatchMessage(Msg);
  62. end;
  63. end;
  64. { Render text message for view, add spinner if necessary and scroll the window. }
  65. procedure SystemLogRefresh();
  66. begin
  67. SystemCheckViewer.Lines := SystemLogText;
  68. { Add Spinner to message. }
  69. if ((TimeCounter > 0) and (TimeCounter < 6)) then begin
  70. SystemCheckViewer.Lines[SystemCheckViewer.Lines.Count - 1] := SystemCheckViewer.Lines[SystemCheckViewer.Lines.Count - 1] + ' [' + Spinner[TimeCounter - 1] + ']';
  71. end;
  72. { Scroll window to the bottom of the log - https://stackoverflow.com/questions/64587596/is-it-possible-to-display-the-install-actions-in-a-list-in-inno-setup }
  73. SendMessage(SystemCheckViewer.Handle, WM_VSCROLL, SB_BOTTOM, 0);
  74. end;
  75. { Log message to file and display just a '.' to user so that user is not overloaded by details. }
  76. procedure SystemLogProgress(message:String);
  77. begin
  78. Log(message);
  79. if (SystemLogText.Count = 0) then begin
  80. SystemLogText.Append('');
  81. end;
  82. SystemLogText[SystemLogText.Count - 1] := SystemLogText[SystemLogText.Count - 1] + '.';
  83. SystemLogRefresh();
  84. end;
  85. { Log message to file and display it to user as title message with asterisk prefix. }
  86. procedure SystemLogTitle(message:String);
  87. begin
  88. message := '* ' + message;
  89. Log(message);
  90. SystemLogText.Append(message);
  91. SystemLogRefresh();
  92. end;
  93. { Log message to file and display it to user. }
  94. procedure SystemLog(message:String);
  95. begin
  96. Log(message);
  97. if (SystemLogText.Count = 0) then begin
  98. SystemLogText.Append('');
  99. end;
  100. SystemLogText[SystemLogText.Count - 1] := SystemLogText[SystemLogText.Count - 1] + message;
  101. SystemLogRefresh();
  102. end;
  103. { Process timer tick during command execution so that the app keeps communicating with user. }
  104. procedure TimerTick();
  105. begin
  106. { TimeCounter for animating Spinner. }
  107. TimeCounter:=TimeCounter+1;
  108. if (TimeCounter = 5) then begin
  109. TimeCounter := 1;
  110. end;
  111. { Redraw Log with Spinner animation. }
  112. SystemLogRefresh();
  113. { Give control back to UI so that it can be updated. https://gist.github.com/jakoch/33ac13800c17eddb2dd4 }
  114. AppProcessMessage;
  115. end;
  116. { --- Command line nonblocking exec --- }
  117. function NonBlockingExec(command, workdir: String): Integer;
  118. var
  119. Res: Integer;
  120. Handle: Longword;
  121. ExitCode: Integer;
  122. LogTextAnsi: AnsiString;
  123. LogText, LeftOver: String;
  124. begin
  125. if (SystemCheckState = SYSTEM_CHECK_STATE_STOPPED) then begin
  126. ExitCode := -3;
  127. Exit;
  128. end;
  129. try
  130. ExitCode := -1;
  131. { SystemLog('Workdir: ' + workdir); }
  132. SystemLogProgress(' $ ' + command);
  133. Handle := ProcStart(command, workdir)
  134. if Handle = 0 then
  135. begin
  136. SystemLog('[' + CustomMessage('SystemCheckResultError') + ']');
  137. Result := -2;
  138. Exit;
  139. end;
  140. while (ExitCode = -1) and (SystemCheckState <> SYSTEM_CHECK_STATE_STOPPED) do
  141. begin
  142. ExitCode := ProcGetExitCode(Handle);
  143. SetLength(LogTextAnsi, 4096);
  144. Res := ProcGetOutput(Handle, LogTextAnsi, 4096)
  145. if Res > 0 then
  146. begin
  147. SetLength(LogTextAnsi, Res);
  148. LogText := LeftOver + String(LogTextAnsi);
  149. SystemLogProgress(LogText);
  150. end;
  151. TimerTick();
  152. Sleep(200);
  153. end;
  154. ProcEnd(Handle);
  155. finally
  156. if (SystemCheckState = SYSTEM_CHECK_STATE_STOPPED) then
  157. begin
  158. Result := -1;
  159. end else begin
  160. Result := ExitCode;
  161. end;
  162. end;
  163. end;
  164. { Execute command for SystemCheck and reset timer so that Spinner will disappear after end of execution. }
  165. function SystemCheckExec(command, workdir: String): Integer;
  166. begin
  167. TimeCounter := 0;
  168. Result := NonBlockingExec(command, workdir);
  169. TimeCounter := 0;
  170. end;
  171. { Get formated line from SystemCheck for user. }
  172. function GetSystemCheckHint(Command: String; CustomCheckMessageKey:String):String;
  173. begin
  174. Result := CustomMessage('SystemCheckUnableToExecute') + ' ' + Command + #13#10 + CustomMessage(CustomCheckMessageKey);
  175. end;
  176. { Add command to list of fixes which can be executed by installer. }
  177. procedure AddFix(Command:String);
  178. begin
  179. { Do not add possible fix command when check command was stopped by user. }
  180. if (SystemCheckState = SYSTEM_CHECK_STATE_STOPPED) then begin
  181. Exit;
  182. end;
  183. Fixes.Append(Command);
  184. end;
  185. { Execute checks to determine whether Python installation is valid so thet user can choose it to install IDF. }
  186. function IsPythonInstallationValid(displayName: String; pythonPath:String): Boolean;
  187. var
  188. ResultCode: Integer;
  189. ScriptFile: String;
  190. TempDownloadFile: String;
  191. Command: String;
  192. VirtualEvnPath: String;
  193. VirtualEnvPython: String;
  194. RemedyCommand: String;
  195. begin
  196. SystemLogTitle(CustomMessage('SystemCheckForComponent') + ' ' + displayName + ' ');
  197. SystemCheckHint := '';
  198. pythonPath := pythonPath + ' ';
  199. Command := pythonPath + '-m pip --version';
  200. ResultCode := SystemCheckExec(Command, ExpandConstant('{tmp}'));
  201. if (ResultCode <> 0) then begin
  202. SystemCheckHint := GetSystemCheckHint(Command, 'SystemCheckRemedyMissingPip');
  203. Result := False;
  204. Exit;
  205. end;
  206. Command := pythonPath + '-m virtualenv --version';
  207. ResultCode := SystemCheckExec(Command, ExpandConstant('{tmp}'));
  208. if (ResultCode <> 0) then begin
  209. SystemCheckHint := GetSystemCheckHint(Command, 'SystemCheckRemedyMissingVirtualenv') + #13#10 + pythonPath + '-m pip install --upgrade pip' + #13#10 + pythonPath + '-m pip install virtualenv';
  210. AddFix(pythonPath + '-m pip install --upgrade pip');
  211. AddFix(pythonPath + '-m pip install virtualenv');
  212. Result := False;
  213. Exit;
  214. end;
  215. VirtualEnvCounter := VirtualEnvCounter + 1;
  216. VirtualEvnPath := ExpandConstant('{tmp}\') + IntToStr(VirtualEnvCounter) + '-idf-test-venv\';
  217. VirtualEnvPython := VirtualEvnPath + 'Scripts\python.exe ';
  218. Command := pythonPath + '-m virtualenv ' + VirtualEvnPath;
  219. ResultCode := SystemCheckExec(Command, ExpandConstant('{tmp}'));
  220. if (ResultCode <> 0) then begin
  221. SystemCheckHint := GetSystemCheckHint(Command, 'SystemCheckRemedyCreateVirtualenv');
  222. Result := False;
  223. Exit;
  224. end;
  225. ScriptFile := ExpandConstant('{tmp}\system_check_virtualenv.py')
  226. Command := VirtualEnvPython + ScriptFile + ' ' + VirtualEnvPython;
  227. ResultCode := SystemCheckExec(Command, ExpandConstant('{tmp}'));
  228. if (ResultCode <> 0) then begin
  229. SystemCheckHint := GetSystemCheckHint(Command, 'SystemCheckRemedyPythonInVirtualenv');
  230. Result := False;
  231. Exit;
  232. end;
  233. Command := VirtualEnvPython + '-m pip install --only-binary ":all:" "cryptography>=2.1.4" --no-binary future';
  234. ResultCode := SystemCheckExec(Command, ExpandConstant('{tmp}'));
  235. if (ResultCode <> 0) then begin
  236. SystemCheckHint := GetSystemCheckHint(Command, 'SystemCheckRemedyBinaryPythonWheel');
  237. Result := False;
  238. Exit;
  239. end;
  240. TempDownloadFile := IntToStr(VirtualEnvCounter) + '-idf-exe-v1.0.1.zip';
  241. ScriptFile := ExpandConstant('{tmp}\system_check_download.py');
  242. Command := VirtualEnvPython + ScriptFile + ExpandConstant(' https://dl.espressif.com/dl/idf-exe-v1.0.1.zip ' + TempDownloadFile);
  243. ResultCode := SystemCheckExec(Command , ExpandConstant('{tmp}'));
  244. if (ResultCode <> 0) then begin
  245. SystemCheckHint := GetSystemCheckHint(Command, 'SystemCheckRemedyFailedHttpsDownload');
  246. Result := False;
  247. Exit;
  248. end;
  249. if (not FileExists(ExpandConstant('{tmp}\') + TempDownloadFile)) then begin
  250. SystemLog(' [' + CustomMessage('SystemCheckResultFail') + '] - ' + CustomMessage('SystemCheckUnableToFindFile') + ' ' + ExpandConstant('{tmp}\') + TempDownloadFile);
  251. Result := False;
  252. Exit;
  253. end;
  254. ScriptFile := ExpandConstant('{tmp}\system_check_subprocess.py');
  255. Command := pythonPath + ScriptFile;
  256. ResultCode := SystemCheckExec(Command, ExpandConstant('{tmp}'));
  257. if (ResultCode <> 0) then begin
  258. RemedyCommand := pythonPath + '-m pip uninstall subprocess.run';
  259. SystemCheckHint := GetSystemCheckHint(Command, 'SystemCheckRemedyFailedSubmoduleRun') + #13#10 + RemedyCommand;
  260. AddFix(RemedyCommand);
  261. Result := False;
  262. Exit;
  263. end;
  264. SystemLog(' [' + CustomMessage('SystemCheckResultOk') + ']');
  265. Result := True;
  266. end;
  267. procedure FindPythonVersionsFromKey(RootKey: Integer; SubKeyName: String);
  268. var
  269. CompanyNames: TArrayOfString;
  270. CompanyName, CompanySubKey, TagName, TagSubKey: String;
  271. ExecutablePath, DisplayName, Version: String;
  272. TagNames: TArrayOfString;
  273. CompanyId, TagId: Integer;
  274. BaseDir: String;
  275. begin
  276. if not RegGetSubkeyNames(RootKey, SubKeyName, CompanyNames) then
  277. begin
  278. Log('Nothing found in ' + IntToStr(RootKey) + '\' + SubKeyName);
  279. Exit;
  280. end;
  281. for CompanyId := 0 to GetArrayLength(CompanyNames) - 1 do
  282. begin
  283. CompanyName := CompanyNames[CompanyId];
  284. if CompanyName = 'PyLauncher' then
  285. continue;
  286. CompanySubKey := SubKeyName + '\' + CompanyName;
  287. Log('In ' + IntToStr(RootKey) + '\' + CompanySubKey);
  288. if not RegGetSubkeyNames(RootKey, CompanySubKey, TagNames) then
  289. continue;
  290. for TagId := 0 to GetArrayLength(TagNames) - 1 do
  291. begin
  292. TagName := TagNames[TagId];
  293. TagSubKey := CompanySubKey + '\' + TagName;
  294. Log('In ' + IntToStr(RootKey) + '\' + TagSubKey);
  295. if not GetPythonVersionInfoFromKey(RootKey, SubKeyName, CompanyName, TagName, Version, DisplayName, ExecutablePath, BaseDir) then
  296. continue;
  297. if (SystemCheckState = SYSTEM_CHECK_STATE_STOPPED) then begin
  298. Exit;
  299. end;
  300. { Verify Python installation and display hint in case of invalid version or env. }
  301. if not IsPythonInstallationValid(DisplayName, ExecutablePath) then begin
  302. if ((Length(SystemCheckHint) > 0) and (SystemCheckState <> SYSTEM_CHECK_STATE_STOPPED)) then begin
  303. SystemLogTitle(CustomMessage('SystemCheckHint') + ': ' + SystemCheckHint);
  304. end;
  305. continue;
  306. end;
  307. PythonVersionAdd(Version, DisplayName, ExecutablePath);
  308. end;
  309. end;
  310. end;
  311. procedure FindInstalledPythonVersions();
  312. begin
  313. FindPythonVersionsFromKey(HKEY_CURRENT_USER, 'Software\Python');
  314. FindPythonVersionsFromKey(HKEY_LOCAL_MACHINE, 'Software\Python');
  315. FindPythonVersionsFromKey(HKEY_LOCAL_MACHINE, 'Software\Wow6432Node\Python');
  316. end;
  317. { Get Boolean for UI to determine whether it make sense to register exceptions to Defender. }
  318. function GetWindowsDefenderStatus(): Boolean;
  319. var
  320. bHasWD: Boolean;
  321. szWDPath: String;
  322. listPSModulePath: TStringList;
  323. ResultCode: Integer;
  324. x: Integer;
  325. begin
  326. Log('Checking PSMODULEPATH for Windows Defender module');
  327. listPSModulePath := TStringList.Create;
  328. listPSModulePath.Delimiter := ';';
  329. listPSModulePath.StrictDelimiter := True;
  330. listPSModulePath.DelimitedText := GetEnv('PsModulePath');
  331. for x:=0 to (listPSModulePath.Count-1) do
  332. begin
  333. szWDPath := listPSModulePath[x] + '\Defender'
  334. bHasWD := DirExists(szWDPath);
  335. if bHasWD then
  336. begin
  337. break;
  338. end
  339. end;
  340. if not bHasWD then begin
  341. Result := False;
  342. Exit;
  343. end;
  344. Log('Checking Windows Services Defender is enabled: (Get-MpComputerStatus).AntivirusEnabled');
  345. ResultCode := SystemCheckExec('powershell -ExecutionPolicy Bypass "if((Get-MpComputerStatus).AntivirusEnabled) { Exit 0 } else { Exit 1 }"', ExpandConstant('{tmp}'));
  346. if (ResultCode <> 0) then begin
  347. Log('Result code: ' + IntToStr(ResultCode));
  348. Result := False;
  349. Exit;
  350. end;
  351. Result := True;
  352. end;
  353. { Process user request to stop system checks. }
  354. function SystemCheckStopRequest():Boolean;
  355. begin
  356. { In case of stopped check by user, procees to next/previous step. }
  357. if (SystemCheckState = SYSTEM_CHECK_STATE_STOPPED) then begin
  358. Result := True;
  359. Exit;
  360. end;
  361. if (SystemCheckState = SYSTEM_CHECK_STATE_RUNNING) then begin
  362. if (MsgBox(CustomMessage('SystemCheckNotCompleteConsent'), mbConfirmation, MB_YESNO) = IDYES) then begin
  363. SystemCheckState := SYSTEM_CHECK_STATE_STOPPED;
  364. Result := True;
  365. Exit;
  366. end;
  367. end;
  368. if (SystemCheckState = SYSTEM_CHECK_STATE_COMPLETE) then begin
  369. Result := True;
  370. end else begin
  371. Result := False;
  372. end;
  373. end;
  374. { Process request to proceed to next page. If the scan is running ask user for confirmation. }
  375. function OnSystemCheckValidate(Sender: TWizardPage): Boolean;
  376. begin
  377. Result := SystemCheckStopRequest();
  378. end;
  379. { Process request to go to previous screen (license). Prompt user for confirmation when system check is running. }
  380. function OnSystemCheckBackButton(Sender: TWizardPage): Boolean;
  381. begin
  382. Result := SystemCheckStopRequest();
  383. end;
  384. { Process request to stop System Check directly on the screen with System Check by Stop button. }
  385. procedure StopSystemCheckButtonClick(Sender: TObject);
  386. begin
  387. SystemCheckStopRequest();
  388. end;
  389. { Check whether site is reachable and that system trust the certificate. }
  390. procedure VerifyRootCertificates();
  391. var
  392. ResultCode: Integer;
  393. Command: String;
  394. OutFile: String;
  395. begin
  396. SystemLogTitle(CustomMessage('SystemCheckRootCertificates') + ' ');
  397. { It's necessary to invoke PowerShell *BEFORE* Python. Invoke-Request will retrieve and add Root Certificate if necessary. }
  398. { Without the certificate Python is failing to connect to https. }
  399. { Windows command to list current certificates: certlm.msc }
  400. OutFile := ExpandConstant('{tmp}\check');
  401. Command := 'powershell -ExecutionPolicy Bypass ';
  402. Command := Command + 'Invoke-WebRequest -Uri "https://dl.espressif.com/dl/?system_check=win' + GetWindowsVersionString + '" -OutFile "' + OutFile + '-1.txt";';
  403. Command := Command + 'Invoke-WebRequest -Uri "https://github.com/espressif" -OutFile "' + OutFile + '-2.txt";';
  404. {Command := Command + 'Invoke-WebRequest -Uri "https://www.s3.amazonaws.com/" -OutFile "' + OutFile + '-3.txt";';}
  405. ResultCode := SystemCheckExec(Command, ExpandConstant('{tmp}'));
  406. if (ResultCode <> 0) then begin
  407. SystemLog(' [' + CustomMessage('SystemCheckResultWarn') + ']');
  408. SystemLog(CustomMessage('SystemCheckRootCertificateWarning'));
  409. end else begin
  410. SystemLog(' [' + CustomMessage('SystemCheckResultOk') + ']');
  411. end;
  412. end;
  413. { Execute system check }
  414. procedure ExecuteSystemCheck();
  415. var
  416. UseEmbeddedPythonParam: String;
  417. begin
  418. { Execute system check only once. Avoid execution in case of back button. }
  419. if (SystemCheckState <> SYSTEM_CHECK_STATE_INIT) then begin
  420. Exit;
  421. end;
  422. SystemCheckState := SYSTEM_CHECK_STATE_RUNNING;
  423. SystemLogTitle(CustomMessage('SystemCheckStart'));
  424. StopSystemCheckButton.Enabled := True;
  425. VerifyRootCertificates();
  426. { Search for the installed Python version only on explicit user request. }
  427. UseEmbeddedPythonParam := ExpandConstant('{param:USEEMBEDDEDPYTHON|yes}');
  428. if (UseEmbeddedPythonParam <> 'yes') then begin
  429. FindInstalledPythonVersions();
  430. end;
  431. if (SystemCheckState <> SYSTEM_CHECK_STATE_STOPPED) then begin
  432. SystemLogTitle(CustomMessage('SystemCheckForDefender') + ' ');
  433. IsWindowsDefenderEnabled := GetWindowsDefenderStatus();
  434. if (IsWindowsDefenderEnabled) then begin
  435. SystemLog(' [' + CustomMessage('SystemCheckResultFound') + ']');
  436. end else begin
  437. SystemLog(' [' + CustomMessage('SystemCheckResultNotFound') + ']');
  438. end;
  439. end else begin
  440. { User cancelled the check, let's enable Defender script so that use can decide to disable it. }
  441. IsWindowsDefenderEnabled := True;
  442. end;
  443. if (SystemCheckState = SYSTEM_CHECK_STATE_STOPPED) then begin
  444. SystemLog('');
  445. SystemLogTitle(CustomMessage('SystemCheckStopped'));
  446. end else begin
  447. SystemLogTitle(CustomMessage('SystemCheckComplete'));
  448. SystemCheckState := SYSTEM_CHECK_STATE_COMPLETE;
  449. end;
  450. { Enable Apply Script button if some fixes are available. }
  451. if (Fixes.Count > 0) then begin
  452. ApplyFixesButton.Enabled := True;
  453. end;
  454. StopSystemCheckButton.Enabled := False;
  455. end;
  456. { Invoke scan of system environment. }
  457. procedure OnSystemCheckActivate(Sender: TWizardPage);
  458. var SystemCheckParam:String;
  459. begin
  460. { Display special controls. For some reason the first call of the page does not invoke SystemCheckOnCurPageChanged. }
  461. FullLogButton.Visible := True;
  462. ApplyFixesButton.Visible := True;
  463. StopSystemCheckButton.Visible := True;
  464. SystemCheckViewer.Visible := True;
  465. SystemCheckParam := ExpandConstant('{param:SKIPSYSTEMCHECK|no}');
  466. if (SystemCheckParam = 'yes') then begin
  467. SystemCheckState := SYSTEM_CHECK_STATE_STOPPED;
  468. SystemLog('System Check disabled by command line option /SKIPSYSTEMCHECK.');
  469. end;
  470. ExecuteSystemCheck();
  471. end;
  472. { Handle request to display full log from the installation. Open the log in notepad. }
  473. procedure FullLogButtonClick(Sender: TObject);
  474. var
  475. ResultCode: Integer;
  476. begin
  477. Exec(ExpandConstant('{win}\notepad.exe'), ExpandConstant('{log}'), '', SW_SHOW, ewNoWait, ResultCode);
  478. end;
  479. { Handle request to apply available fixes. }
  480. procedure ApplyFixesButtonClick(Sender: TObject);
  481. var
  482. ResultCode: Integer;
  483. FixIndex: Integer;
  484. AreFixesApplied: Boolean;
  485. begin
  486. if (MsgBox(CustomMessage('SystemCheckApplyFixesConsent'), mbConfirmation, MB_YESNO) = IDNO) then begin
  487. Exit;
  488. end;
  489. ApplyFixesButton.Enabled := false;
  490. SystemCheckState := SYSTEM_CHECK_STATE_INIT;
  491. SystemLog('');
  492. SystemLogTitle('Starting application of fixes');
  493. AreFixesApplied := True;
  494. for FixIndex := 0 to Fixes.Count - 1 do
  495. begin
  496. ResultCode := SystemCheckExec(Fixes[FixIndex], ExpandConstant('{tmp}'));
  497. if (ResultCode <> 0) then begin
  498. AreFixesApplied := False;
  499. break;
  500. end;
  501. end;
  502. SystemLog('');
  503. if (AreFixesApplied) then begin
  504. SystemLogTitle(CustomMessage('SystemCheckFixesSuccessful'));
  505. end else begin
  506. SystemLogTitle(CustomMessage('SystemCheckFixesFailed'));
  507. end;
  508. SystemLog('');
  509. Fixes.Clear();
  510. { Restart system check. }
  511. ExecuteSystemCheck();
  512. end;
  513. { Add Page for System Check so that user is informed about readiness of the system. }
  514. <event('InitializeWizard')>
  515. procedure CreateSystemCheckPage();
  516. begin
  517. { Initialize data structure for Python }
  518. InstalledPythonVersions := TStringList.Create();
  519. InstalledPythonDisplayNames := TStringList.Create();
  520. InstalledPythonExecutables := TStringList.Create();
  521. PythonVersionAdd('{#PythonVersion}', 'Use Python {#PythonVersion} Embedded (Recommended)', 'tools\python\{#PythonVersion}\python.exe');
  522. { Create Spinner animation. }
  523. Spinner := TStringList.Create();
  524. Spinner.Append('-');
  525. Spinner.Append('\');
  526. Spinner.Append('|');
  527. Spinner.Append('/');
  528. VirtualEnvCounter := 0;
  529. Fixes := TStringList.Create();
  530. SystemCheckState := SYSTEM_CHECK_STATE_INIT;
  531. SystemCheckPage := CreateOutputMsgPage(wpLicense, CustomMessage('PreInstallationCheckTitle'), CustomMessage('PreInstallationCheckSubtitle'), '');
  532. with SystemCheckPage do
  533. begin
  534. OnActivate := @OnSystemCheckActivate;
  535. OnBackButtonClick := @OnSystemCheckBackButton;
  536. OnNextButtonClick := @OnSystemCheckValidate;
  537. end;
  538. SystemCheckViewer := TNewMemo.Create(WizardForm);
  539. with SystemCheckViewer do
  540. begin
  541. Parent := WizardForm;
  542. Left := ScaleX(10);
  543. Top := ScaleY(60);
  544. ReadOnly := True;
  545. Font.Name := 'Courier New';
  546. Height := WizardForm.CancelButton.Top - ScaleY(40);
  547. Width := WizardForm.ClientWidth + ScaleX(80);
  548. WordWrap := True;
  549. Visible := False;
  550. end;
  551. SystemLogText := TStringList.Create;
  552. FullLogButton := TNewButton.Create(WizardForm);
  553. with FullLogButton do
  554. begin
  555. Parent := WizardForm;
  556. Left := WizardForm.ClientWidth;
  557. Top := SystemCheckViewer.Top + SystemCheckViewer.Height + ScaleY(5);
  558. Width := WizardForm.CancelButton.Width;
  559. Height := WizardForm.CancelButton.Height;
  560. Caption := CustomMessage('SystemCheckFullLogButtonCaption');
  561. OnClick := @FullLogButtonClick;
  562. Visible := False;
  563. end;
  564. ApplyFixesButton := TNewButton.Create(WizardForm);
  565. with ApplyFixesButton do
  566. begin
  567. Parent := WizardForm;
  568. Left := WizardForm.ClientWidth - FullLogButton.Width;
  569. Top := FullLogButton.Top;
  570. Width := WizardForm.CancelButton.Width;
  571. Height := WizardForm.CancelButton.Height;
  572. Caption := CustomMessage('SystemCheckApplyFixesButtonCaption');
  573. OnClick := @ApplyFixesButtonClick;
  574. Visible := False;
  575. Enabled := False;
  576. end;
  577. StopSystemCheckButton := TNewButton.Create(WizardForm);
  578. with StopSystemCheckButton do
  579. begin
  580. Parent := WizardForm;
  581. Left := ApplyFixesButton.Left - ApplyFixesButton.Width;
  582. Top := FullLogButton.Top;
  583. Width := WizardForm.CancelButton.Width;
  584. Height := WizardForm.CancelButton.Height;
  585. Caption := CustomMessage('SystemCheckStopButtonCaption');
  586. OnClick := @StopSystemCheckButtonClick;
  587. Visible := False;
  588. Enabled := False;
  589. end;
  590. { Extract helper files for sanity check of Python environment. }
  591. ExtractTemporaryFile('system_check_download.py')
  592. ExtractTemporaryFile('system_check_subprocess.py')
  593. ExtractTemporaryFile('system_check_virtualenv.py')
  594. end;
  595. { Process Cancel Button Click event. Prompt user to confirm Cancellation of System check. }
  596. { Then continue with normal cancel window. }
  597. procedure CancelButtonClick(CurPageID: Integer; var Cancel, Confirm: Boolean);
  598. begin
  599. if ((CurPageId = SystemCheckPage.ID) and (SystemCheckState = SYSTEM_CHECK_STATE_RUNNING)) then begin
  600. SystemCheckStopRequest();
  601. end;
  602. end;
  603. { Display control specific for System Check page. }
  604. <event('CurPageChanged')>
  605. procedure SystemCheckOnCurPageChanged(CurPageID: Integer);
  606. begin
  607. FullLogButton.Visible := CurPageID = SystemCheckPage.ID;
  608. ApplyFixesButton.Visible := CurPageID = SystemCheckPage.ID;
  609. StopSystemCheckButton.Visible := CurPageID = SystemCheckPage.ID;
  610. SystemCheckViewer.Visible := CurPageID = SystemCheckPage.ID;
  611. end;