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