esp-idf/tools/windows/tool_setup/system_check_page.iss.inc
2021-03-24 12:27:46 +01:00

706 lines
23 KiB
PHP

{ Copyright 2019-2021 Espressif Systems (Shanghai) CO LTD
SPDX-License-Identifier: Apache-2.0 }
{ SystemCheck states }
const
SYSTEM_CHECK_STATE_INIT = 0; { No check was executed yet. }
SYSTEM_CHECK_STATE_RUNNING = 1; { Check is in progress and can be cancelled. }
SYSTEM_CHECK_STATE_COMPLETE = 2; { Check is complete. }
SYSTEM_CHECK_STATE_STOPPED = 3; { User stopped the check. }
var
{ RTF View to display content of system check. }
SystemCheckViewer: TNewMemo;
{ Indicate state of System Check. }
SystemCheckState:Integer;
{ Text representation of log messages which are then converte to RTF. }
SystemLogText: TStringList;
{ Message for user which gives a hint how to correct the problem. }
SystemCheckHint: String;
{ Setup Page which displays progress/result of system check. }
SystemCheckPage: TOutputMsgWizardPage;
{ TimeCounter for Spinner animation invoked during command execution. }
TimeCounter:Integer;
{ Spinner is TStringList, because characters like backslash must be escaped and stored on two bytes. }
Spinner: TStringList;
{ Button to request display of full log of system check/installation. }
FullLogButton: TNewButton;
{ Button to request application of available fixtures. }
ApplyFixesButton: TNewButton;
{ Commands which should be executed to fix problems discovered during system check. }
Fixes: TStringList;
{ Button to request Stop of System Checks manually. }
StopSystemCheckButton: TNewButton;
{ Count number of createde virtualenv to avoid collision with previous runs. }
VirtualEnvCounter: Integer;
{ Indicates whether system check was able to find running Windows Defender. }
var IsWindowsDefenderEnabled: Boolean;
{ Const values for user32.dll which allows scrolling of the text view. }
const
WM_VSCROLL = $0115;
SB_BOTTOM = 7;
type
TMsg = record
hwnd: HWND;
message: UINT;
wParam: Longint;
lParam: Longint;
time: DWORD;
pt: TPoint;
end;
const
PM_REMOVE = 1;
{ Functions to communicate via Windows API. }
function PeekMessage(var lpMsg: TMsg; hWnd: HWND; wMsgFilterMin, wMsgFilterMax, wRemoveMsg: UINT): BOOL; external 'PeekMessageW@user32.dll stdcall';
function TranslateMessage(const lpMsg: TMsg): BOOL; external 'TranslateMessage@user32.dll stdcall';
function DispatchMessage(const lpMsg: TMsg): Longint; external 'DispatchMessageW@user32.dll stdcall';
procedure AppProcessMessage;
var
Msg: TMsg;
begin
while PeekMessage(Msg, WizardForm.Handle, 0, 0, PM_REMOVE) do begin
TranslateMessage(Msg);
DispatchMessage(Msg);
end;
end;
{ Render text message for view, add spinner if necessary and scroll the window. }
procedure SystemLogRefresh();
begin
SystemCheckViewer.Lines := SystemLogText;
{ Add Spinner to message. }
if ((TimeCounter > 0) and (TimeCounter < 6)) then begin
SystemCheckViewer.Lines[SystemCheckViewer.Lines.Count - 1] := SystemCheckViewer.Lines[SystemCheckViewer.Lines.Count - 1] + ' [' + Spinner[TimeCounter - 1] + ']';
end;
{ 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 }
SendMessage(SystemCheckViewer.Handle, WM_VSCROLL, SB_BOTTOM, 0);
end;
{ Log message to file and display just a '.' to user so that user is not overloaded by details. }
procedure SystemLogProgress(message:String);
begin
Log(message);
if (SystemLogText.Count = 0) then begin
SystemLogText.Append('');
end;
SystemLogText[SystemLogText.Count - 1] := SystemLogText[SystemLogText.Count - 1] + '.';
SystemLogRefresh();
end;
{ Log message to file and display it to user as title message with asterisk prefix. }
procedure SystemLogTitle(message:String);
begin
message := '* ' + message;
Log(message);
SystemLogText.Append(message);
SystemLogRefresh();
end;
{ Log message to file and display it to user. }
procedure SystemLog(message:String);
begin
Log(message);
if (SystemLogText.Count = 0) then begin
SystemLogText.Append('');
end;
SystemLogText[SystemLogText.Count - 1] := SystemLogText[SystemLogText.Count - 1] + message;
SystemLogRefresh();
end;
{ Process timer tick during command execution so that the app keeps communicating with user. }
procedure TimerTick();
begin
{ TimeCounter for animating Spinner. }
TimeCounter:=TimeCounter+1;
if (TimeCounter = 5) then begin
TimeCounter := 1;
end;
{ Redraw Log with Spinner animation. }
SystemLogRefresh();
{ Give control back to UI so that it can be updated. https://gist.github.com/jakoch/33ac13800c17eddb2dd4 }
AppProcessMessage;
end;
{ --- Command line nonblocking exec --- }
function NonBlockingExec(command, workdir: String): Integer;
var
Res: Integer;
Handle: Longword;
ExitCode: Integer;
LogTextAnsi: AnsiString;
LogText, LeftOver: String;
begin
if (SystemCheckState = SYSTEM_CHECK_STATE_STOPPED) then begin
ExitCode := -3;
Exit;
end;
try
ExitCode := -1;
{ SystemLog('Workdir: ' + workdir); }
SystemLogProgress(' $ ' + command);
Handle := ProcStart(command, workdir)
if Handle = 0 then
begin
SystemLog('[' + CustomMessage('SystemCheckResultError') + ']');
Result := -2;
Exit;
end;
while (ExitCode = -1) and (SystemCheckState <> SYSTEM_CHECK_STATE_STOPPED) do
begin
ExitCode := ProcGetExitCode(Handle);
SetLength(LogTextAnsi, 4096);
Res := ProcGetOutput(Handle, LogTextAnsi, 4096)
if Res > 0 then
begin
SetLength(LogTextAnsi, Res);
LogText := LeftOver + String(LogTextAnsi);
SystemLogProgress(LogText);
end;
TimerTick();
Sleep(200);
end;
ProcEnd(Handle);
finally
if (SystemCheckState = SYSTEM_CHECK_STATE_STOPPED) then
begin
Result := -1;
end else begin
Result := ExitCode;
end;
end;
end;
{ Execute command for SystemCheck and reset timer so that Spinner will disappear after end of execution. }
function SystemCheckExec(command, workdir: String): Integer;
begin
TimeCounter := 0;
Result := NonBlockingExec(command, workdir);
TimeCounter := 0;
end;
{ Get formated line from SystemCheck for user. }
function GetSystemCheckHint(Command: String; CustomCheckMessageKey:String):String;
begin
Result := CustomMessage('SystemCheckUnableToExecute') + ' ' + Command + #13#10 + CustomMessage(CustomCheckMessageKey);
end;
{ Add command to list of fixes which can be executed by installer. }
procedure AddFix(Command:String);
begin
{ Do not add possible fix command when check command was stopped by user. }
if (SystemCheckState = SYSTEM_CHECK_STATE_STOPPED) then begin
Exit;
end;
Fixes.Append(Command);
end;
{ Execute checks to determine whether Python installation is valid so thet user can choose it to install IDF. }
function IsPythonInstallationValid(displayName: String; pythonPath:String): Boolean;
var
ResultCode: Integer;
ScriptFile: String;
TempDownloadFile: String;
Command: String;
VirtualEvnPath: String;
VirtualEnvPython: String;
RemedyCommand: String;
begin
SystemLogTitle(CustomMessage('SystemCheckForComponent') + ' ' + displayName + ' ');
SystemCheckHint := '';
pythonPath := pythonPath + ' ';
Command := pythonPath + '-m pip --version';
ResultCode := SystemCheckExec(Command, ExpandConstant('{tmp}'));
if (ResultCode <> 0) then begin
SystemCheckHint := GetSystemCheckHint(Command, 'SystemCheckRemedyMissingPip');
Result := False;
Exit;
end;
Command := pythonPath + '-m virtualenv --version';
ResultCode := SystemCheckExec(Command, ExpandConstant('{tmp}'));
if (ResultCode <> 0) then begin
SystemCheckHint := GetSystemCheckHint(Command, 'SystemCheckRemedyMissingVirtualenv') + #13#10 + pythonPath + '-m pip install --upgrade pip' + #13#10 + pythonPath + '-m pip install virtualenv';
AddFix(pythonPath + '-m pip install --upgrade pip');
AddFix(pythonPath + '-m pip install virtualenv');
Result := False;
Exit;
end;
VirtualEnvCounter := VirtualEnvCounter + 1;
VirtualEvnPath := ExpandConstant('{tmp}\') + IntToStr(VirtualEnvCounter) + '-idf-test-venv\';
VirtualEnvPython := VirtualEvnPath + 'Scripts\python.exe ';
Command := pythonPath + '-m virtualenv ' + VirtualEvnPath;
ResultCode := SystemCheckExec(Command, ExpandConstant('{tmp}'));
if (ResultCode <> 0) then begin
SystemCheckHint := GetSystemCheckHint(Command, 'SystemCheckRemedyCreateVirtualenv');
Result := False;
Exit;
end;
ScriptFile := ExpandConstant('{tmp}\system_check_virtualenv.py')
Command := VirtualEnvPython + ScriptFile + ' ' + VirtualEnvPython;
ResultCode := SystemCheckExec(Command, ExpandConstant('{tmp}'));
if (ResultCode <> 0) then begin
SystemCheckHint := GetSystemCheckHint(Command, 'SystemCheckRemedyPythonInVirtualenv');
Result := False;
Exit;
end;
Command := VirtualEnvPython + '-m pip install --only-binary ":all:" "cryptography>=2.1.4" --no-binary future';
ResultCode := SystemCheckExec(Command, ExpandConstant('{tmp}'));
if (ResultCode <> 0) then begin
SystemCheckHint := GetSystemCheckHint(Command, 'SystemCheckRemedyBinaryPythonWheel');
Result := False;
Exit;
end;
TempDownloadFile := IntToStr(VirtualEnvCounter) + '-idf-exe-v1.0.1.zip';
ScriptFile := ExpandConstant('{tmp}\system_check_download.py');
Command := VirtualEnvPython + ScriptFile + ExpandConstant(' https://dl.espressif.com/dl/idf-exe-v1.0.1.zip ' + TempDownloadFile);
ResultCode := SystemCheckExec(Command , ExpandConstant('{tmp}'));
if (ResultCode <> 0) then begin
SystemCheckHint := GetSystemCheckHint(Command, 'SystemCheckRemedyFailedHttpsDownload');
Result := False;
Exit;
end;
if (not FileExists(ExpandConstant('{tmp}\') + TempDownloadFile)) then begin
SystemLog(' [' + CustomMessage('SystemCheckResultFail') + '] - ' + CustomMessage('SystemCheckUnableToFindFile') + ' ' + ExpandConstant('{tmp}\') + TempDownloadFile);
Result := False;
Exit;
end;
ScriptFile := ExpandConstant('{tmp}\system_check_subprocess.py');
Command := pythonPath + ScriptFile;
ResultCode := SystemCheckExec(Command, ExpandConstant('{tmp}'));
if (ResultCode <> 0) then begin
RemedyCommand := pythonPath + '-m pip uninstall subprocess.run';
SystemCheckHint := GetSystemCheckHint(Command, 'SystemCheckRemedyFailedSubmoduleRun') + #13#10 + RemedyCommand;
AddFix(RemedyCommand);
Result := False;
Exit;
end;
SystemLog(' [' + CustomMessage('SystemCheckResultOk') + ']');
Result := True;
end;
procedure FindPythonVersionsFromKey(RootKey: Integer; SubKeyName: String);
var
CompanyNames: TArrayOfString;
CompanyName, CompanySubKey, TagName, TagSubKey: String;
ExecutablePath, DisplayName, Version: String;
TagNames: TArrayOfString;
CompanyId, TagId: Integer;
BaseDir: String;
begin
if not RegGetSubkeyNames(RootKey, SubKeyName, CompanyNames) then
begin
Log('Nothing found in ' + IntToStr(RootKey) + '\' + SubKeyName);
Exit;
end;
for CompanyId := 0 to GetArrayLength(CompanyNames) - 1 do
begin
CompanyName := CompanyNames[CompanyId];
if CompanyName = 'PyLauncher' then
continue;
CompanySubKey := SubKeyName + '\' + CompanyName;
Log('In ' + IntToStr(RootKey) + '\' + CompanySubKey);
if not RegGetSubkeyNames(RootKey, CompanySubKey, TagNames) then
continue;
for TagId := 0 to GetArrayLength(TagNames) - 1 do
begin
TagName := TagNames[TagId];
TagSubKey := CompanySubKey + '\' + TagName;
Log('In ' + IntToStr(RootKey) + '\' + TagSubKey);
if not GetPythonVersionInfoFromKey(RootKey, SubKeyName, CompanyName, TagName, Version, DisplayName, ExecutablePath, BaseDir) then
continue;
if (SystemCheckState = SYSTEM_CHECK_STATE_STOPPED) then begin
Exit;
end;
{ Verify Python installation and display hint in case of invalid version or env. }
if not IsPythonInstallationValid(DisplayName, ExecutablePath) then begin
if ((Length(SystemCheckHint) > 0) and (SystemCheckState <> SYSTEM_CHECK_STATE_STOPPED)) then begin
SystemLogTitle(CustomMessage('SystemCheckHint') + ': ' + SystemCheckHint);
end;
continue;
end;
PythonVersionAdd(Version, DisplayName, ExecutablePath);
end;
end;
end;
procedure FindInstalledPythonVersions();
begin
FindPythonVersionsFromKey(HKEY_CURRENT_USER, 'Software\Python');
FindPythonVersionsFromKey(HKEY_LOCAL_MACHINE, 'Software\Python');
FindPythonVersionsFromKey(HKEY_LOCAL_MACHINE, 'Software\Wow6432Node\Python');
end;
{ Get Boolean for UI to determine whether it make sense to register exceptions to Defender. }
function GetWindowsDefenderStatus(): Boolean;
var
bHasWD: Boolean;
szWDPath: String;
listPSModulePath: TStringList;
ResultCode: Integer;
x: Integer;
begin
Log('Checking PSMODULEPATH for Windows Defender module');
listPSModulePath := TStringList.Create;
listPSModulePath.Delimiter := ';';
listPSModulePath.StrictDelimiter := True;
listPSModulePath.DelimitedText := GetEnv('PsModulePath');
for x:=0 to (listPSModulePath.Count-1) do
begin
szWDPath := listPSModulePath[x] + '\Defender'
bHasWD := DirExists(szWDPath);
if bHasWD then
begin
break;
end
end;
if not bHasWD then begin
Result := False;
Exit;
end;
Log('Checking Windows Services Defender is enabled: (Get-MpComputerStatus).AntivirusEnabled');
ResultCode := SystemCheckExec('powershell -ExecutionPolicy Bypass "if((Get-MpComputerStatus).AntivirusEnabled) { Exit 0 } else { Exit 1 }"', ExpandConstant('{tmp}'));
if (ResultCode <> 0) then begin
Log('Result code: ' + IntToStr(ResultCode));
Result := False;
Exit;
end;
Result := True;
end;
{ Process user request to stop system checks. }
function SystemCheckStopRequest():Boolean;
begin
{ In case of stopped check by user, procees to next/previous step. }
if (SystemCheckState = SYSTEM_CHECK_STATE_STOPPED) then begin
Result := True;
Exit;
end;
if (SystemCheckState = SYSTEM_CHECK_STATE_RUNNING) then begin
if (MsgBox(CustomMessage('SystemCheckNotCompleteConsent'), mbConfirmation, MB_YESNO) = IDYES) then begin
SystemCheckState := SYSTEM_CHECK_STATE_STOPPED;
Result := True;
Exit;
end;
end;
if (SystemCheckState = SYSTEM_CHECK_STATE_COMPLETE) then begin
Result := True;
end else begin
Result := False;
end;
end;
{ Process request to proceed to next page. If the scan is running ask user for confirmation. }
function OnSystemCheckValidate(Sender: TWizardPage): Boolean;
begin
Result := SystemCheckStopRequest();
end;
{ Process request to go to previous screen (license). Prompt user for confirmation when system check is running. }
function OnSystemCheckBackButton(Sender: TWizardPage): Boolean;
begin
Result := SystemCheckStopRequest();
end;
{ Process request to stop System Check directly on the screen with System Check by Stop button. }
procedure StopSystemCheckButtonClick(Sender: TObject);
begin
SystemCheckStopRequest();
end;
{ Check whether site is reachable and that system trust the certificate. }
procedure VerifyRootCertificates();
var
ResultCode: Integer;
Command: String;
OutFile: String;
begin
SystemLogTitle(CustomMessage('SystemCheckRootCertificates') + ' ');
{ It's necessary to invoke PowerShell *BEFORE* Python. Invoke-Request will retrieve and add Root Certificate if necessary. }
{ Without the certificate Python is failing to connect to https. }
{ Windows command to list current certificates: certlm.msc }
OutFile := ExpandConstant('{tmp}\check');
Command := 'powershell -ExecutionPolicy Bypass ';
Command := Command + 'Invoke-WebRequest -Uri "https://dl.espressif.com/dl/?system_check=win' + GetWindowsVersionString + '" -OutFile "' + OutFile + '-1.txt";';
Command := Command + 'Invoke-WebRequest -Uri "https://github.com/espressif" -OutFile "' + OutFile + '-2.txt";';
{Command := Command + 'Invoke-WebRequest -Uri "https://www.s3.amazonaws.com/" -OutFile "' + OutFile + '-3.txt";';}
ResultCode := SystemCheckExec(Command, ExpandConstant('{tmp}'));
if (ResultCode <> 0) then begin
SystemLog(' [' + CustomMessage('SystemCheckResultWarn') + ']');
SystemLog(CustomMessage('SystemCheckRootCertificateWarning'));
end else begin
SystemLog(' [' + CustomMessage('SystemCheckResultOk') + ']');
end;
end;
{ Wrapper function for Run task. Run tasks requires calling function. }
function GetIsWindowsDefenderEnabled(): Boolean;
begin
Result := IsWindowsDefenderEnabled;
end;
{ Execute system check }
procedure ExecuteSystemCheck();
begin
{ Execute system check only once. Avoid execution in case of back button. }
if (SystemCheckState <> SYSTEM_CHECK_STATE_INIT) then begin
Exit;
end;
SystemCheckState := SYSTEM_CHECK_STATE_RUNNING;
SystemLogTitle(CustomMessage('SystemCheckStart'));
StopSystemCheckButton.Enabled := True;
if (not IsOfflineMode) then begin
VerifyRootCertificates();
end;
{ Search for the installed Python version only on explicit user request. }
if (not UseEmbeddedPython) then begin
FindInstalledPythonVersions();
end;
if (SystemCheckState <> SYSTEM_CHECK_STATE_STOPPED) then begin
SystemLogTitle(CustomMessage('SystemCheckForDefender') + ' ');
IsWindowsDefenderEnabled := GetWindowsDefenderStatus();
if (IsWindowsDefenderEnabled) then begin
SystemLog(' [' + CustomMessage('SystemCheckResultFound') + ']');
end else begin
SystemLog(' [' + CustomMessage('SystemCheckResultNotFound') + ']');
end;
end else begin
{ User cancelled the check, let's enable Defender script so that use can decide to disable it. }
IsWindowsDefenderEnabled := True;
end;
if (SystemCheckState = SYSTEM_CHECK_STATE_STOPPED) then begin
SystemLog('');
SystemLogTitle(CustomMessage('SystemCheckStopped'));
end else begin
SystemLogTitle(CustomMessage('SystemCheckComplete'));
SystemCheckState := SYSTEM_CHECK_STATE_COMPLETE;
end;
{ Enable Apply Script button if some fixes are available. }
if (Fixes.Count > 0) then begin
ApplyFixesButton.Enabled := True;
end;
StopSystemCheckButton.Enabled := False;
end;
{ Invoke scan of system environment. }
procedure OnSystemCheckActivate(Sender: TWizardPage);
begin
{ Display special controls. For some reason the first call of the page does not invoke SystemCheckOnCurPageChanged. }
FullLogButton.Visible := True;
ApplyFixesButton.Visible := True;
StopSystemCheckButton.Visible := True;
SystemCheckViewer.Visible := True;
if (SkipSystemCheck) then begin
SystemCheckState := SYSTEM_CHECK_STATE_STOPPED;
SystemLog('System Check disabled by command line option /SKIPSYSTEMCHECK.');
end;
ExecuteSystemCheck();
end;
{ Handle request to display full log from the installation. Open the log in notepad. }
procedure FullLogButtonClick(Sender: TObject);
var
ResultCode: Integer;
begin
Exec(ExpandConstant('{win}\notepad.exe'), ExpandConstant('{log}'), '', SW_SHOW, ewNoWait, ResultCode);
end;
{ Handle request to apply available fixes. }
procedure ApplyFixesButtonClick(Sender: TObject);
var
ResultCode: Integer;
FixIndex: Integer;
AreFixesApplied: Boolean;
begin
if (MsgBox(CustomMessage('SystemCheckApplyFixesConsent'), mbConfirmation, MB_YESNO) = IDNO) then begin
Exit;
end;
ApplyFixesButton.Enabled := false;
SystemCheckState := SYSTEM_CHECK_STATE_INIT;
SystemLog('');
SystemLogTitle('Starting application of fixes');
AreFixesApplied := True;
for FixIndex := 0 to Fixes.Count - 1 do
begin
ResultCode := SystemCheckExec(Fixes[FixIndex], ExpandConstant('{tmp}'));
if (ResultCode <> 0) then begin
AreFixesApplied := False;
break;
end;
end;
SystemLog('');
if (AreFixesApplied) then begin
SystemLogTitle(CustomMessage('SystemCheckFixesSuccessful'));
end else begin
SystemLogTitle(CustomMessage('SystemCheckFixesFailed'));
end;
SystemLog('');
Fixes.Clear();
{ Restart system check. }
ExecuteSystemCheck();
end;
{ Add Page for System Check so that user is informed about readiness of the system. }
<event('InitializeWizard')>
procedure CreateSystemCheckPage();
begin
{ Initialize data structure for Python }
InstalledPythonVersions := TStringList.Create();
InstalledPythonDisplayNames := TStringList.Create();
InstalledPythonExecutables := TStringList.Create();
PythonVersionAdd('{#PythonVersion}', 'Use Python {#PythonVersion} Embedded (Recommended)', 'tools\idf-python\{#PythonVersion}\python.exe');
{ Create Spinner animation. }
Spinner := TStringList.Create();
Spinner.Append('-');
Spinner.Append('\');
Spinner.Append('|');
Spinner.Append('/');
VirtualEnvCounter := 0;
Fixes := TStringList.Create();
SystemCheckState := SYSTEM_CHECK_STATE_INIT;
SystemCheckPage := CreateOutputMsgPage(wpLicense, CustomMessage('PreInstallationCheckTitle'), CustomMessage('PreInstallationCheckSubtitle'), '');
with SystemCheckPage do
begin
OnActivate := @OnSystemCheckActivate;
OnBackButtonClick := @OnSystemCheckBackButton;
OnNextButtonClick := @OnSystemCheckValidate;
end;
SystemCheckViewer := TNewMemo.Create(WizardForm);
with SystemCheckViewer do
begin
Parent := WizardForm;
Left := ScaleX(10);
Top := ScaleY(60);
ReadOnly := True;
Font.Name := 'Courier New';
Height := WizardForm.CancelButton.Top - ScaleY(40);
Width := WizardForm.ClientWidth + ScaleX(80);
WordWrap := True;
Visible := False;
end;
SystemLogText := TStringList.Create;
FullLogButton := TNewButton.Create(WizardForm);
with FullLogButton do
begin
Parent := WizardForm;
Left := WizardForm.ClientWidth;
Top := SystemCheckViewer.Top + SystemCheckViewer.Height + ScaleY(5);
Width := WizardForm.CancelButton.Width;
Height := WizardForm.CancelButton.Height;
Caption := CustomMessage('SystemCheckFullLogButtonCaption');
OnClick := @FullLogButtonClick;
Visible := False;
end;
ApplyFixesButton := TNewButton.Create(WizardForm);
with ApplyFixesButton do
begin
Parent := WizardForm;
Left := WizardForm.ClientWidth - FullLogButton.Width;
Top := FullLogButton.Top;
Width := WizardForm.CancelButton.Width;
Height := WizardForm.CancelButton.Height;
Caption := CustomMessage('SystemCheckApplyFixesButtonCaption');
OnClick := @ApplyFixesButtonClick;
Visible := False;
Enabled := False;
end;
StopSystemCheckButton := TNewButton.Create(WizardForm);
with StopSystemCheckButton do
begin
Parent := WizardForm;
Left := ApplyFixesButton.Left - ApplyFixesButton.Width;
Top := FullLogButton.Top;
Width := WizardForm.CancelButton.Width;
Height := WizardForm.CancelButton.Height;
Caption := CustomMessage('SystemCheckStopButtonCaption');
OnClick := @StopSystemCheckButtonClick;
Visible := False;
Enabled := False;
end;
{ Extract helper files for sanity check of Python environment. }
ExtractTemporaryFile('system_check_download.py')
ExtractTemporaryFile('system_check_subprocess.py')
ExtractTemporaryFile('system_check_virtualenv.py')
end;
{ Process Cancel Button Click event. Prompt user to confirm Cancellation of System check. }
{ Then continue with normal cancel window. }
procedure CancelButtonClick(CurPageID: Integer; var Cancel, Confirm: Boolean);
begin
if ((CurPageId = SystemCheckPage.ID) and (SystemCheckState = SYSTEM_CHECK_STATE_RUNNING)) then begin
SystemCheckStopRequest();
end;
end;
{ Display control specific for System Check page. }
<event('CurPageChanged')>
procedure SystemCheckOnCurPageChanged(CurPageID: Integer);
begin
FullLogButton.Visible := CurPageID = SystemCheckPage.ID;
ApplyFixesButton.Visible := CurPageID = SystemCheckPage.ID;
StopSystemCheckButton.Visible := CurPageID = SystemCheckPage.ID;
SystemCheckViewer.Visible := CurPageID = SystemCheckPage.ID;
end;