Path: blob/main/Tools/msi/bundle/bootstrap/PythonBootstrapperApplication.cpp
12 views
//-------------------------------------------------------------------------------------------------1// <copyright file="WixStandardBootstrapperApplication.cpp" company="Outercurve Foundation">2// Copyright (c) 2004, Outercurve Foundation.3// This software is released under Microsoft Reciprocal License (MS-RL).4// The license and further copyright text can be found in the file5// LICENSE.TXT at the root directory of the distribution.6// </copyright>7//-------------------------------------------------------------------------------------------------8910#include "pch.h"1112static const LPCWSTR PYBA_WINDOW_CLASS = L"PythonBA";13static const DWORD PYBA_ACQUIRE_PERCENTAGE = 30;14static const LPCWSTR PYBA_VARIABLE_BUNDLE_FILE_VERSION = L"WixBundleFileVersion";1516enum PYBA_STATE {17PYBA_STATE_INITIALIZING,18PYBA_STATE_INITIALIZED,19PYBA_STATE_HELP,20PYBA_STATE_DETECTING,21PYBA_STATE_DETECTED,22PYBA_STATE_PLANNING,23PYBA_STATE_PLANNED,24PYBA_STATE_APPLYING,25PYBA_STATE_CACHING,26PYBA_STATE_CACHED,27PYBA_STATE_EXECUTING,28PYBA_STATE_EXECUTED,29PYBA_STATE_APPLIED,30PYBA_STATE_FAILED,31};3233static const int WM_PYBA_SHOW_HELP = WM_APP + 100;34static const int WM_PYBA_DETECT_PACKAGES = WM_APP + 101;35static const int WM_PYBA_PLAN_PACKAGES = WM_APP + 102;36static const int WM_PYBA_APPLY_PACKAGES = WM_APP + 103;37static const int WM_PYBA_CHANGE_STATE = WM_APP + 104;38static const int WM_PYBA_SHOW_FAILURE = WM_APP + 105;3940// This enum must be kept in the same order as the PAGE_NAMES array.41enum PAGE {42PAGE_LOADING,43PAGE_HELP,44PAGE_INSTALL,45PAGE_UPGRADE,46PAGE_SIMPLE_INSTALL,47PAGE_CUSTOM1,48PAGE_CUSTOM2,49PAGE_MODIFY,50PAGE_PROGRESS,51PAGE_PROGRESS_PASSIVE,52PAGE_SUCCESS,53PAGE_FAILURE,54COUNT_PAGE,55};5657// This array must be kept in the same order as the PAGE enum.58static LPCWSTR PAGE_NAMES[] = {59L"Loading",60L"Help",61L"Install",62L"Upgrade",63L"SimpleInstall",64L"Custom1",65L"Custom2",66L"Modify",67L"Progress",68L"ProgressPassive",69L"Success",70L"Failure",71};7273enum CONTROL_ID {74// Non-paged controls75ID_CLOSE_BUTTON = THEME_FIRST_ASSIGN_CONTROL_ID,76ID_MINIMIZE_BUTTON,7778// Welcome page79ID_INSTALL_BUTTON,80ID_INSTALL_CUSTOM_BUTTON,81ID_INSTALL_SIMPLE_BUTTON,82ID_INSTALL_UPGRADE_BUTTON,83ID_INSTALL_UPGRADE_CUSTOM_BUTTON,84ID_INSTALL_CANCEL_BUTTON,85ID_INSTALL_LAUNCHER_ALL_USERS_CHECKBOX,8687// Customize Page88ID_TARGETDIR_EDITBOX,89ID_CUSTOM_ASSOCIATE_FILES_CHECKBOX,90ID_CUSTOM_INSTALL_ALL_USERS_CHECKBOX,91ID_CUSTOM_INSTALL_LAUNCHER_ALL_USERS_CHECKBOX,92ID_CUSTOM_INCLUDE_LAUNCHER_HELP_LABEL,93ID_CUSTOM_COMPILE_ALL_CHECKBOX,94ID_CUSTOM_BROWSE_BUTTON,95ID_CUSTOM_BROWSE_BUTTON_LABEL,96ID_CUSTOM_INSTALL_BUTTON,97ID_CUSTOM_NEXT_BUTTON,98ID_CUSTOM1_BACK_BUTTON,99ID_CUSTOM2_BACK_BUTTON,100ID_CUSTOM1_CANCEL_BUTTON,101ID_CUSTOM2_CANCEL_BUTTON,102103// Modify page104ID_MODIFY_BUTTON,105ID_REPAIR_BUTTON,106ID_UNINSTALL_BUTTON,107ID_MODIFY_CANCEL_BUTTON,108109// Progress page110ID_CACHE_PROGRESS_PACKAGE_TEXT,111ID_CACHE_PROGRESS_BAR,112ID_CACHE_PROGRESS_TEXT,113114ID_EXECUTE_PROGRESS_PACKAGE_TEXT,115ID_EXECUTE_PROGRESS_BAR,116ID_EXECUTE_PROGRESS_TEXT,117ID_EXECUTE_PROGRESS_ACTIONDATA_TEXT,118119ID_OVERALL_PROGRESS_PACKAGE_TEXT,120ID_OVERALL_PROGRESS_BAR,121ID_OVERALL_CALCULATED_PROGRESS_BAR,122ID_OVERALL_PROGRESS_TEXT,123124ID_PROGRESS_CANCEL_BUTTON,125126// Success page127ID_SUCCESS_TEXT,128ID_SUCCESS_RESTART_TEXT,129ID_SUCCESS_RESTART_BUTTON,130ID_SUCCESS_CANCEL_BUTTON,131ID_SUCCESS_MAX_PATH_BUTTON,132133// Failure page134ID_FAILURE_LOGFILE_LINK,135ID_FAILURE_MESSAGE_TEXT,136ID_FAILURE_RESTART_TEXT,137ID_FAILURE_RESTART_BUTTON,138ID_FAILURE_CANCEL_BUTTON139};140141static THEME_ASSIGN_CONTROL_ID CONTROL_ID_NAMES[] = {142{ ID_CLOSE_BUTTON, L"CloseButton" },143{ ID_MINIMIZE_BUTTON, L"MinimizeButton" },144145{ ID_INSTALL_BUTTON, L"InstallButton" },146{ ID_INSTALL_CUSTOM_BUTTON, L"InstallCustomButton" },147{ ID_INSTALL_SIMPLE_BUTTON, L"InstallSimpleButton" },148{ ID_INSTALL_UPGRADE_BUTTON, L"InstallUpgradeButton" },149{ ID_INSTALL_UPGRADE_CUSTOM_BUTTON, L"InstallUpgradeCustomButton" },150{ ID_INSTALL_CANCEL_BUTTON, L"InstallCancelButton" },151{ ID_INSTALL_LAUNCHER_ALL_USERS_CHECKBOX, L"InstallLauncherAllUsers" },152153{ ID_TARGETDIR_EDITBOX, L"TargetDir" },154{ ID_CUSTOM_ASSOCIATE_FILES_CHECKBOX, L"AssociateFiles" },155{ ID_CUSTOM_INSTALL_ALL_USERS_CHECKBOX, L"InstallAllUsers" },156{ ID_CUSTOM_INSTALL_LAUNCHER_ALL_USERS_CHECKBOX, L"CustomInstallLauncherAllUsers" },157{ ID_CUSTOM_INCLUDE_LAUNCHER_HELP_LABEL, L"Include_launcherHelp" },158{ ID_CUSTOM_COMPILE_ALL_CHECKBOX, L"CompileAll" },159{ ID_CUSTOM_BROWSE_BUTTON, L"CustomBrowseButton" },160{ ID_CUSTOM_BROWSE_BUTTON_LABEL, L"CustomBrowseButtonLabel" },161{ ID_CUSTOM_INSTALL_BUTTON, L"CustomInstallButton" },162{ ID_CUSTOM_NEXT_BUTTON, L"CustomNextButton" },163{ ID_CUSTOM1_BACK_BUTTON, L"Custom1BackButton" },164{ ID_CUSTOM2_BACK_BUTTON, L"Custom2BackButton" },165{ ID_CUSTOM1_CANCEL_BUTTON, L"Custom1CancelButton" },166{ ID_CUSTOM2_CANCEL_BUTTON, L"Custom2CancelButton" },167168{ ID_MODIFY_BUTTON, L"ModifyButton" },169{ ID_REPAIR_BUTTON, L"RepairButton" },170{ ID_UNINSTALL_BUTTON, L"UninstallButton" },171{ ID_MODIFY_CANCEL_BUTTON, L"ModifyCancelButton" },172173{ ID_CACHE_PROGRESS_PACKAGE_TEXT, L"CacheProgressPackageText" },174{ ID_CACHE_PROGRESS_BAR, L"CacheProgressbar" },175{ ID_CACHE_PROGRESS_TEXT, L"CacheProgressText" },176{ ID_EXECUTE_PROGRESS_PACKAGE_TEXT, L"ExecuteProgressPackageText" },177{ ID_EXECUTE_PROGRESS_BAR, L"ExecuteProgressbar" },178{ ID_EXECUTE_PROGRESS_TEXT, L"ExecuteProgressText" },179{ ID_EXECUTE_PROGRESS_ACTIONDATA_TEXT, L"ExecuteProgressActionDataText" },180{ ID_OVERALL_PROGRESS_PACKAGE_TEXT, L"OverallProgressPackageText" },181{ ID_OVERALL_PROGRESS_BAR, L"OverallProgressbar" },182{ ID_OVERALL_CALCULATED_PROGRESS_BAR, L"OverallCalculatedProgressbar" },183{ ID_OVERALL_PROGRESS_TEXT, L"OverallProgressText" },184{ ID_PROGRESS_CANCEL_BUTTON, L"ProgressCancelButton" },185186{ ID_SUCCESS_TEXT, L"SuccessText" },187{ ID_SUCCESS_RESTART_TEXT, L"SuccessRestartText" },188{ ID_SUCCESS_RESTART_BUTTON, L"SuccessRestartButton" },189{ ID_SUCCESS_CANCEL_BUTTON, L"SuccessCancelButton" },190{ ID_SUCCESS_MAX_PATH_BUTTON, L"SuccessMaxPathButton" },191192{ ID_FAILURE_LOGFILE_LINK, L"FailureLogFileLink" },193{ ID_FAILURE_MESSAGE_TEXT, L"FailureMessageText" },194{ ID_FAILURE_RESTART_TEXT, L"FailureRestartText" },195{ ID_FAILURE_RESTART_BUTTON, L"FailureRestartButton" },196{ ID_FAILURE_CANCEL_BUTTON, L"FailureCancelButton" },197};198199static struct { LPCWSTR regName; LPCWSTR variableName; } OPTIONAL_FEATURES[] = {200{ L"core_d", L"Include_debug" },201{ L"core_pdb", L"Include_symbols" },202{ L"dev", L"Include_dev" },203{ L"doc", L"Include_doc" },204{ L"exe", L"Include_exe" },205{ L"lib", L"Include_lib" },206{ L"path", L"PrependPath" },207{ L"appendpath", L"AppendPath" },208{ L"pip", L"Include_pip" },209{ L"tcltk", L"Include_tcltk" },210{ L"test", L"Include_test" },211{ L"tools", L"Include_tools" },212{ L"Shortcuts", L"Shortcuts" },213// Include_launcher and AssociateFiles are handled separately and so do214// not need to be included in this list.215{ nullptr, nullptr }216};217218219220class PythonBootstrapperApplication : public CBalBaseBootstrapperApplication {221void ShowPage(DWORD newPageId) {222// Process each control for special handling in the new page.223ProcessPageControls(ThemeGetPage(_theme, newPageId));224225// Enable disable controls per-page.226if (_pageIds[PAGE_INSTALL] == newPageId ||227_pageIds[PAGE_SIMPLE_INSTALL] == newPageId ||228_pageIds[PAGE_UPGRADE] == newPageId) {229InstallPage_Show();230} else if (_pageIds[PAGE_CUSTOM1] == newPageId) {231Custom1Page_Show();232} else if (_pageIds[PAGE_CUSTOM2] == newPageId) {233Custom2Page_Show();234} else if (_pageIds[PAGE_MODIFY] == newPageId) {235ModifyPage_Show();236} else if (_pageIds[PAGE_SUCCESS] == newPageId) {237SuccessPage_Show();238} else if (_pageIds[PAGE_FAILURE] == newPageId) {239FailurePage_Show();240}241242// Prevent repainting while switching page to avoid ugly flickering243_suppressPaint = TRUE;244ThemeShowPage(_theme, newPageId, SW_SHOW);245ThemeShowPage(_theme, _visiblePageId, SW_HIDE);246_suppressPaint = FALSE;247InvalidateRect(_theme->hwndParent, nullptr, TRUE);248_visiblePageId = newPageId;249250// On the install page set the focus to the install button or251// the next enabled control if install is disabled252if (_pageIds[PAGE_INSTALL] == newPageId) {253ThemeSetFocus(_theme, ID_INSTALL_BUTTON);254} else if (_pageIds[PAGE_SIMPLE_INSTALL] == newPageId) {255ThemeSetFocus(_theme, ID_INSTALL_SIMPLE_BUTTON);256}257}258259//260// Handles control clicks261//262void OnCommand(CONTROL_ID id) {263LPWSTR defaultDir = nullptr;264LPWSTR targetDir = nullptr;265LONGLONG elevated, crtInstalled, installAllUsers;266BOOL checked, launcherChecked;267WCHAR wzPath[MAX_PATH] = { };268BROWSEINFOW browseInfo = { };269PIDLIST_ABSOLUTE pidl = nullptr;270DWORD pageId;271HRESULT hr = S_OK;272273switch(id) {274case ID_CLOSE_BUTTON:275OnClickCloseButton();276break;277278// Install commands279case ID_INSTALL_SIMPLE_BUTTON: __fallthrough;280case ID_INSTALL_UPGRADE_BUTTON: __fallthrough;281case ID_INSTALL_BUTTON:282SavePageSettings();283284hr = BalGetNumericVariable(L"InstallAllUsers", &installAllUsers);285ExitOnFailure(hr, L"Failed to get install scope");286287hr = _engine->SetVariableNumeric(L"CompileAll", installAllUsers);288ExitOnFailure(hr, L"Failed to update CompileAll");289290hr = EnsureTargetDir();291ExitOnFailure(hr, L"Failed to set TargetDir");292293OnPlan(BOOTSTRAPPER_ACTION_INSTALL);294break;295296case ID_CUSTOM1_BACK_BUTTON:297SavePageSettings();298if (_modifying) {299GoToPage(PAGE_MODIFY);300} else if (_upgrading) {301GoToPage(PAGE_UPGRADE);302} else {303GoToPage(PAGE_INSTALL);304}305break;306307case ID_INSTALL_CUSTOM_BUTTON: __fallthrough;308case ID_INSTALL_UPGRADE_CUSTOM_BUTTON: __fallthrough;309case ID_CUSTOM2_BACK_BUTTON:310SavePageSettings();311GoToPage(PAGE_CUSTOM1);312break;313314case ID_CUSTOM_NEXT_BUTTON:315SavePageSettings();316GoToPage(PAGE_CUSTOM2);317break;318319case ID_CUSTOM_INSTALL_BUTTON:320SavePageSettings();321322hr = EnsureTargetDir();323ExitOnFailure(hr, L"Failed to set TargetDir");324325hr = BalGetStringVariable(L"TargetDir", &targetDir);326if (SUCCEEDED(hr)) {327// TODO: Check whether directory exists and contains another installation328ReleaseStr(targetDir);329}330331OnPlan(_command.action);332break;333334case ID_INSTALL_LAUNCHER_ALL_USERS_CHECKBOX:335checked = ThemeIsControlChecked(_theme, ID_INSTALL_LAUNCHER_ALL_USERS_CHECKBOX);336_engine->SetVariableNumeric(L"InstallLauncherAllUsers", checked);337338ThemeControlElevates(_theme, ID_INSTALL_BUTTON, WillElevate());339break;340341case ID_CUSTOM_INSTALL_LAUNCHER_ALL_USERS_CHECKBOX:342checked = ThemeIsControlChecked(_theme, ID_CUSTOM_INSTALL_LAUNCHER_ALL_USERS_CHECKBOX);343_engine->SetVariableNumeric(L"InstallLauncherAllUsers", checked);344345ThemeControlElevates(_theme, ID_CUSTOM_INSTALL_BUTTON, WillElevate());346break;347348case ID_CUSTOM_INSTALL_ALL_USERS_CHECKBOX:349checked = ThemeIsControlChecked(_theme, ID_CUSTOM_INSTALL_ALL_USERS_CHECKBOX);350_engine->SetVariableNumeric(L"InstallAllUsers", checked);351352ThemeControlElevates(_theme, ID_CUSTOM_INSTALL_BUTTON, WillElevate());353ThemeControlEnable(_theme, ID_CUSTOM_BROWSE_BUTTON_LABEL, !checked);354if (checked) {355_engine->SetVariableNumeric(L"CompileAll", 1);356ThemeSendControlMessage(_theme, ID_CUSTOM_COMPILE_ALL_CHECKBOX, BM_SETCHECK, BST_CHECKED, 0);357}358ThemeGetTextControl(_theme, ID_TARGETDIR_EDITBOX, &targetDir);359if (targetDir) {360// Check the current value against the default to see361// if we should switch it automatically.362hr = BalGetStringVariable(363checked ? L"DefaultJustForMeTargetDir" : L"DefaultAllUsersTargetDir",364&defaultDir365);366367if (SUCCEEDED(hr) && defaultDir) {368LPWSTR formatted = nullptr;369if (defaultDir[0] && SUCCEEDED(BalFormatString(defaultDir, &formatted))) {370if (wcscmp(formatted, targetDir) == 0) {371ReleaseStr(defaultDir);372defaultDir = nullptr;373ReleaseStr(formatted);374formatted = nullptr;375376hr = BalGetStringVariable(377checked ? L"DefaultAllUsersTargetDir" : L"DefaultJustForMeTargetDir",378&defaultDir379);380if (SUCCEEDED(hr) && defaultDir && defaultDir[0] && SUCCEEDED(BalFormatString(defaultDir, &formatted))) {381ThemeSetTextControl(_theme, ID_TARGETDIR_EDITBOX, formatted);382ReleaseStr(formatted);383}384} else {385ReleaseStr(formatted);386}387}388389ReleaseStr(defaultDir);390}391}392break;393394case ID_CUSTOM_BROWSE_BUTTON:395browseInfo.hwndOwner = _hWnd;396browseInfo.pszDisplayName = wzPath;397browseInfo.lpszTitle = _theme->sczCaption;398browseInfo.ulFlags = BIF_RETURNONLYFSDIRS | BIF_USENEWUI;399pidl = ::SHBrowseForFolderW(&browseInfo);400if (pidl && ::SHGetPathFromIDListW(pidl, wzPath)) {401ThemeSetTextControl(_theme, ID_TARGETDIR_EDITBOX, wzPath);402}403404if (pidl) {405::CoTaskMemFree(pidl);406}407break;408409// Modify commands410case ID_MODIFY_BUTTON:411// Some variables cannot be modified412_engine->SetVariableString(L"InstallAllUsersState", L"disable");413_engine->SetVariableString(L"InstallLauncherAllUsersState", L"disable");414_engine->SetVariableString(L"TargetDirState", L"disable");415_engine->SetVariableString(L"CustomBrowseButtonState", L"disable");416_modifying = TRUE;417GoToPage(PAGE_CUSTOM1);418break;419420case ID_REPAIR_BUTTON:421OnPlan(BOOTSTRAPPER_ACTION_REPAIR);422break;423424case ID_UNINSTALL_BUTTON:425OnPlan(BOOTSTRAPPER_ACTION_UNINSTALL);426break;427428case ID_SUCCESS_MAX_PATH_BUTTON:429EnableMaxPathSupport();430ThemeControlEnable(_theme, ID_SUCCESS_MAX_PATH_BUTTON, FALSE);431break;432}433434LExit:435return;436}437438void InstallPage_Show() {439// Ensure the All Users install button has a UAC shield440BOOL elevated = WillElevate();441ThemeControlElevates(_theme, ID_INSTALL_BUTTON, elevated);442ThemeControlElevates(_theme, ID_INSTALL_SIMPLE_BUTTON, elevated);443ThemeControlElevates(_theme, ID_INSTALL_UPGRADE_BUTTON, elevated);444}445446void Custom1Page_Show() {447LONGLONG installLauncherAllUsers;448449if (FAILED(BalGetNumericVariable(L"InstallLauncherAllUsers", &installLauncherAllUsers))) {450installLauncherAllUsers = 0;451}452453ThemeSendControlMessage(_theme, ID_CUSTOM_INSTALL_LAUNCHER_ALL_USERS_CHECKBOX, BM_SETCHECK,454installLauncherAllUsers ? BST_CHECKED : BST_UNCHECKED, 0);455456LOC_STRING *pLocString = nullptr;457LPCWSTR locKey = L"#(loc.Include_launcherHelp)";458LONGLONG detectedLauncher;459460if (SUCCEEDED(BalGetNumericVariable(L"DetectedLauncher", &detectedLauncher)) && detectedLauncher) {461locKey = L"#(loc.Include_launcherRemove)";462} else if (SUCCEEDED(BalGetNumericVariable(L"DetectedOldLauncher", &detectedLauncher)) && detectedLauncher) {463locKey = L"#(loc.Include_launcherUpgrade)";464}465466if (SUCCEEDED(LocGetString(_wixLoc, locKey, &pLocString)) && pLocString) {467ThemeSetTextControl(_theme, ID_CUSTOM_INCLUDE_LAUNCHER_HELP_LABEL, pLocString->wzText);468}469}470471void Custom2Page_Show() {472HRESULT hr;473LONGLONG installAll, includeLauncher;474475if (FAILED(BalGetNumericVariable(L"InstallAllUsers", &installAll))) {476installAll = 0;477}478479if (WillElevate()) {480ThemeControlElevates(_theme, ID_CUSTOM_INSTALL_BUTTON, TRUE);481ThemeShowControl(_theme, ID_CUSTOM_BROWSE_BUTTON_LABEL, SW_HIDE);482} else {483ThemeControlElevates(_theme, ID_CUSTOM_INSTALL_BUTTON, FALSE);484ThemeShowControl(_theme, ID_CUSTOM_BROWSE_BUTTON_LABEL, SW_SHOW);485}486487if (SUCCEEDED(BalGetNumericVariable(L"Include_launcher", &includeLauncher)) && includeLauncher) {488ThemeControlEnable(_theme, ID_CUSTOM_ASSOCIATE_FILES_CHECKBOX, TRUE);489} else {490ThemeSendControlMessage(_theme, ID_CUSTOM_ASSOCIATE_FILES_CHECKBOX, BM_SETCHECK, BST_UNCHECKED, 0);491ThemeControlEnable(_theme, ID_CUSTOM_ASSOCIATE_FILES_CHECKBOX, FALSE);492}493494LPWSTR targetDir = nullptr;495hr = BalGetStringVariable(L"TargetDir", &targetDir);496if (SUCCEEDED(hr) && targetDir && targetDir[0]) {497ThemeSetTextControl(_theme, ID_TARGETDIR_EDITBOX, targetDir);498StrFree(targetDir);499} else if (SUCCEEDED(hr)) {500StrFree(targetDir);501targetDir = nullptr;502503LPWSTR defaultTargetDir = nullptr;504hr = BalGetStringVariable(L"DefaultCustomTargetDir", &defaultTargetDir);505if (SUCCEEDED(hr) && defaultTargetDir && !defaultTargetDir[0]) {506StrFree(defaultTargetDir);507defaultTargetDir = nullptr;508509hr = BalGetStringVariable(510installAll ? L"DefaultAllUsersTargetDir" : L"DefaultJustForMeTargetDir",511&defaultTargetDir512);513}514if (SUCCEEDED(hr) && defaultTargetDir) {515if (defaultTargetDir[0] && SUCCEEDED(BalFormatString(defaultTargetDir, &targetDir))) {516ThemeSetTextControl(_theme, ID_TARGETDIR_EDITBOX, targetDir);517StrFree(targetDir);518}519StrFree(defaultTargetDir);520}521}522}523524void ModifyPage_Show() {525ThemeControlEnable(_theme, ID_REPAIR_BUTTON, !_suppressRepair);526}527528void SuccessPage_Show() {529// on the "Success" page, check if the restart button should be enabled.530BOOL showRestartButton = FALSE;531LOC_STRING *successText = nullptr;532HRESULT hr = S_OK;533534if (_restartRequired) {535if (BOOTSTRAPPER_RESTART_PROMPT == _command.restart) {536showRestartButton = TRUE;537}538}539540switch (_plannedAction) {541case BOOTSTRAPPER_ACTION_INSTALL:542hr = LocGetString(_wixLoc, L"#(loc.SuccessInstallMessage)", &successText);543break;544case BOOTSTRAPPER_ACTION_MODIFY:545hr = LocGetString(_wixLoc, L"#(loc.SuccessModifyMessage)", &successText);546break;547case BOOTSTRAPPER_ACTION_REPAIR:548hr = LocGetString(_wixLoc, L"#(loc.SuccessRepairMessage)", &successText);549break;550case BOOTSTRAPPER_ACTION_UNINSTALL:551hr = LocGetString(_wixLoc, L"#(loc.SuccessRemoveMessage)", &successText);552break;553}554555if (successText) {556LPWSTR formattedString = nullptr;557BalFormatString(successText->wzText, &formattedString);558if (formattedString) {559ThemeSetTextControl(_theme, ID_SUCCESS_TEXT, formattedString);560StrFree(formattedString);561}562}563564ThemeControlEnable(_theme, ID_SUCCESS_RESTART_TEXT, showRestartButton);565ThemeControlEnable(_theme, ID_SUCCESS_RESTART_BUTTON, showRestartButton);566567if (_command.action != BOOTSTRAPPER_ACTION_INSTALL ||568!IsWindowsVersionOrGreater(10, 0, 0)) {569ThemeControlEnable(_theme, ID_SUCCESS_MAX_PATH_BUTTON, FALSE);570} else {571DWORD dataType = 0, buffer = 0, bufferLen = sizeof(buffer);572HKEY hKey;573LRESULT res = RegOpenKeyExW(574HKEY_LOCAL_MACHINE,575L"SYSTEM\\CurrentControlSet\\Control\\FileSystem",5760,577KEY_READ,578&hKey579);580if (res == ERROR_SUCCESS) {581res = RegQueryValueExW(hKey, L"LongPathsEnabled", nullptr, &dataType,582(LPBYTE)&buffer, &bufferLen);583RegCloseKey(hKey);584}585else {586BalLog(BOOTSTRAPPER_LOG_LEVEL_STANDARD, "Failed to open SYSTEM\\CurrentControlSet\\Control\\FileSystem: error code %d", res);587}588if (res == ERROR_SUCCESS && dataType == REG_DWORD && buffer == 0) {589ThemeControlElevates(_theme, ID_SUCCESS_MAX_PATH_BUTTON, TRUE);590}591else {592if (res == ERROR_SUCCESS)593BalLog(BOOTSTRAPPER_LOG_LEVEL_STANDARD, "Failed to read LongPathsEnabled value: error code %d", res);594else595BalLog(BOOTSTRAPPER_LOG_LEVEL_STANDARD, "Hiding MAX_PATH button because it is already enabled");596ThemeControlEnable(_theme, ID_SUCCESS_MAX_PATH_BUTTON, FALSE);597}598}599}600601void FailurePage_Show() {602// on the "Failure" page, show error message and check if the restart button should be enabled.603604// if there is a log file variable then we'll assume the log file exists.605BOOL showLogLink = (_bundle.sczLogVariable && *_bundle.sczLogVariable);606BOOL showErrorMessage = FALSE;607BOOL showRestartButton = FALSE;608609if (FAILED(_hrFinal)) {610LPWSTR unformattedText = nullptr;611LPWSTR text = nullptr;612613// If we know the failure message, use that.614if (_failedMessage && *_failedMessage) {615StrAllocString(&unformattedText, _failedMessage, 0);616} else {617// try to get the error message from the error code.618StrAllocFromError(&unformattedText, _hrFinal, nullptr);619if (!unformattedText || !*unformattedText) {620StrAllocFromError(&unformattedText, E_FAIL, nullptr);621}622}623624if (E_WIXSTDBA_CONDITION_FAILED == _hrFinal) {625if (unformattedText) {626StrAllocString(&text, unformattedText, 0);627}628} else {629StrAllocFormatted(&text, L"0x%08x - %ls", _hrFinal, unformattedText);630}631632if (text) {633ThemeSetTextControl(_theme, ID_FAILURE_MESSAGE_TEXT, text);634showErrorMessage = TRUE;635}636637ReleaseStr(text);638ReleaseStr(unformattedText);639}640641if (_restartRequired && BOOTSTRAPPER_RESTART_PROMPT == _command.restart) {642showRestartButton = TRUE;643}644645ThemeControlEnable(_theme, ID_FAILURE_LOGFILE_LINK, showLogLink);646ThemeControlEnable(_theme, ID_FAILURE_MESSAGE_TEXT, showErrorMessage);647ThemeControlEnable(_theme, ID_FAILURE_RESTART_TEXT, showRestartButton);648ThemeControlEnable(_theme, ID_FAILURE_RESTART_BUTTON, showRestartButton);649}650651static void EnableMaxPathSupport() {652LPWSTR targetDir = nullptr, defaultDir = nullptr;653HRESULT hr = BalGetStringVariable(L"TargetDir", &targetDir);654if (FAILED(hr) || !targetDir || !targetDir[0]) {655BalLog(BOOTSTRAPPER_LOG_LEVEL_ERROR, "Failed to get TargetDir");656return;657}658659LPWSTR pythonw = nullptr;660StrAllocFormatted(&pythonw, L"%ls\\pythonw.exe", targetDir);661if (!pythonw || !pythonw[0]) {662BalLog(BOOTSTRAPPER_LOG_LEVEL_ERROR, "Failed to construct pythonw.exe path");663return;664}665666LPCWSTR arguments = L"-c \"import winreg; "667"winreg.SetValueEx("668"winreg.CreateKey(winreg.HKEY_LOCAL_MACHINE, "669"r'SYSTEM\\CurrentControlSet\\Control\\FileSystem'), "670"'LongPathsEnabled', "671"None, "672"winreg.REG_DWORD, "673"1"674")\"";675BalLog(BOOTSTRAPPER_LOG_LEVEL_STANDARD, "Executing %ls %ls", pythonw, arguments);676HINSTANCE res = ShellExecuteW(0, L"runas", pythonw, arguments, NULL, SW_HIDE);677BalLog(BOOTSTRAPPER_LOG_LEVEL_STANDARD, "return code 0x%08x", res);678}679680public: // IBootstrapperApplication681virtual STDMETHODIMP OnStartup() {682HRESULT hr = S_OK;683DWORD dwUIThreadId = 0;684685// create UI thread686_hUiThread = ::CreateThread(nullptr, 0, UiThreadProc, this, 0, &dwUIThreadId);687if (!_hUiThread) {688ExitWithLastError(hr, "Failed to create UI thread.");689}690691LExit:692return hr;693}694695696virtual STDMETHODIMP_(int) OnShutdown() {697int nResult = IDNOACTION;698699// wait for UI thread to terminate700if (_hUiThread) {701::WaitForSingleObject(_hUiThread, INFINITE);702ReleaseHandle(_hUiThread);703}704705// If a restart was required.706if (_restartRequired && _allowRestart) {707nResult = IDRESTART;708}709710return nResult;711}712713virtual STDMETHODIMP_(int) OnDetectRelatedMsiPackage(714__in_z LPCWSTR wzPackageId,715__in_z LPCWSTR /*wzProductCode*/,716__in BOOL fPerMachine,717__in DWORD64 /*dw64Version*/,718__in BOOTSTRAPPER_RELATED_OPERATION operation719) {720if (BOOTSTRAPPER_RELATED_OPERATION_MAJOR_UPGRADE == operation &&721(CSTR_EQUAL == ::CompareStringW(LOCALE_NEUTRAL, 0, wzPackageId, -1, L"launcher_AllUsers", -1) ||722CSTR_EQUAL == ::CompareStringW(LOCALE_NEUTRAL, 0, wzPackageId, -1, L"launcher_JustForMe", -1))) {723auto hr = LoadAssociateFilesStateFromKey(_engine, fPerMachine ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER);724if (hr == S_OK) {725_engine->SetVariableNumeric(L"AssociateFiles", 1);726} else if (hr == S_FALSE) {727_engine->SetVariableNumeric(L"AssociateFiles", 0);728} else if (FAILED(hr)) {729BalLog(BOOTSTRAPPER_LOG_LEVEL_ERROR, "Failed to load AssociateFiles state: error code 0x%08X", hr);730}731732LONGLONG includeLauncher;733if (FAILED(BalGetNumericVariable(L"Include_launcher", &includeLauncher))734|| includeLauncher == -1) {735_engine->SetVariableNumeric(L"Include_launcher", 1);736_engine->SetVariableNumeric(L"InstallLauncherAllUsers", fPerMachine ? 1 : 0);737}738_engine->SetVariableNumeric(L"DetectedOldLauncher", 1);739}740return CheckCanceled() ? IDCANCEL : IDNOACTION;741}742743virtual STDMETHODIMP_(int) OnDetectRelatedBundle(744__in LPCWSTR wzBundleId,745__in BOOTSTRAPPER_RELATION_TYPE relationType,746__in LPCWSTR /*wzBundleTag*/,747__in BOOL fPerMachine,748__in DWORD64 /*dw64Version*/,749__in BOOTSTRAPPER_RELATED_OPERATION operation750) {751BalInfoAddRelatedBundleAsPackage(&_bundle.packages, wzBundleId, relationType, fPerMachine);752753// Remember when our bundle would cause a downgrade.754if (BOOTSTRAPPER_RELATED_OPERATION_DOWNGRADE == operation) {755_downgradingOtherVersion = TRUE;756} else if (BOOTSTRAPPER_RELATED_OPERATION_MAJOR_UPGRADE == operation) {757BalLog(BOOTSTRAPPER_LOG_LEVEL_STANDARD, "Detected previous version - planning upgrade");758_upgrading = TRUE;759760LoadOptionalFeatureStates(_engine);761} else if (BOOTSTRAPPER_RELATED_OPERATION_NONE == operation) {762if (_command.action == BOOTSTRAPPER_ACTION_INSTALL) {763LOC_STRING *pLocString = nullptr;764if (SUCCEEDED(LocGetString(_wixLoc, L"#(loc.FailureExistingInstall)", &pLocString)) && pLocString) {765BalFormatString(pLocString->wzText, &_failedMessage);766} else {767BalFormatString(L"Cannot install [WixBundleName] because it is already installed.", &_failedMessage);768}769BalLog(770BOOTSTRAPPER_LOG_LEVEL_ERROR,771"Related bundle %ls is preventing install",772wzBundleId773);774SetState(PYBA_STATE_FAILED, E_WIXSTDBA_CONDITION_FAILED);775}776}777778return CheckCanceled() ? IDCANCEL : IDOK;779}780781782virtual STDMETHODIMP_(void) OnDetectPackageComplete(783__in LPCWSTR wzPackageId,784__in HRESULT hrStatus,785__in BOOTSTRAPPER_PACKAGE_STATE state786) {787if (FAILED(hrStatus)) {788return;789}790791BOOL detectedLauncher = FALSE;792HKEY hkey = HKEY_LOCAL_MACHINE;793if (CSTR_EQUAL == ::CompareStringW(LOCALE_NEUTRAL, 0, wzPackageId, -1, L"launcher_AllUsers", -1)) {794if (BOOTSTRAPPER_PACKAGE_STATE_PRESENT == state || BOOTSTRAPPER_PACKAGE_STATE_OBSOLETE == state) {795detectedLauncher = TRUE;796_engine->SetVariableNumeric(L"InstallLauncherAllUsers", 1);797}798} else if (CSTR_EQUAL == ::CompareStringW(LOCALE_NEUTRAL, 0, wzPackageId, -1, L"launcher_JustForMe", -1)) {799if (BOOTSTRAPPER_PACKAGE_STATE_PRESENT == state || BOOTSTRAPPER_PACKAGE_STATE_OBSOLETE == state) {800detectedLauncher = TRUE;801_engine->SetVariableNumeric(L"InstallLauncherAllUsers", 0);802}803}804805LONGLONG includeLauncher;806if (SUCCEEDED(BalGetNumericVariable(L"Include_launcher", &includeLauncher))807&& includeLauncher != -1) {808detectedLauncher = FALSE;809}810811if (detectedLauncher) {812/* When we detect the current version of the launcher. */813_engine->SetVariableNumeric(L"Include_launcher", 1);814_engine->SetVariableNumeric(L"DetectedLauncher", 1);815_engine->SetVariableString(L"Include_launcherState", L"disable");816_engine->SetVariableString(L"InstallLauncherAllUsersState", L"disable");817818auto hr = LoadAssociateFilesStateFromKey(_engine, hkey);819if (hr == S_OK) {820_engine->SetVariableNumeric(L"AssociateFiles", 1);821} else if (hr == S_FALSE) {822_engine->SetVariableNumeric(L"AssociateFiles", 0);823} else if (FAILED(hr)) {824BalLog(BOOTSTRAPPER_LOG_LEVEL_ERROR, "Failed to load AssociateFiles state: error code 0x%08X", hr);825}826}827}828829830virtual STDMETHODIMP_(void) OnDetectComplete(__in HRESULT hrStatus) {831if (SUCCEEDED(hrStatus) && _baFunction) {832BalLog(BOOTSTRAPPER_LOG_LEVEL_STANDARD, "Running detect complete BA function");833_baFunction->OnDetectComplete();834}835836if (SUCCEEDED(hrStatus)) {837LONGLONG includeLauncher;838if (SUCCEEDED(BalGetNumericVariable(L"Include_launcher", &includeLauncher))839&& includeLauncher == -1) {840if (BOOTSTRAPPER_ACTION_LAYOUT == _command.action ||841(BOOTSTRAPPER_ACTION_INSTALL == _command.action && !_upgrading)) {842// When installing/downloading, we want to include the launcher843// by default.844_engine->SetVariableNumeric(L"Include_launcher", 1);845} else {846// Any other action, if we didn't detect the MSI then we want to847// keep it excluded848_engine->SetVariableNumeric(L"Include_launcher", 0);849_engine->SetVariableNumeric(L"AssociateFiles", 0);850}851}852}853854if (SUCCEEDED(hrStatus)) {855hrStatus = EvaluateConditions();856}857858if (SUCCEEDED(hrStatus)) {859// Ensure the default path has been set860hrStatus = EnsureTargetDir();861}862863SetState(PYBA_STATE_DETECTED, hrStatus);864865// If we're not interacting with the user or we're doing a layout or we're just after a force restart866// then automatically start planning.867if (BOOTSTRAPPER_DISPLAY_FULL > _command.display ||868BOOTSTRAPPER_ACTION_LAYOUT == _command.action ||869BOOTSTRAPPER_ACTION_UNINSTALL == _command.action ||870BOOTSTRAPPER_RESUME_TYPE_REBOOT == _command.resumeType) {871if (SUCCEEDED(hrStatus)) {872::PostMessageW(_hWnd, WM_PYBA_PLAN_PACKAGES, 0, _command.action);873}874}875}876877878virtual STDMETHODIMP_(int) OnPlanRelatedBundle(879__in_z LPCWSTR /*wzBundleId*/,880__inout_z BOOTSTRAPPER_REQUEST_STATE* pRequestedState881) {882return CheckCanceled() ? IDCANCEL : IDOK;883}884885886virtual STDMETHODIMP_(int) OnPlanPackageBegin(887__in_z LPCWSTR wzPackageId,888__inout BOOTSTRAPPER_REQUEST_STATE *pRequestState889) {890HRESULT hr = S_OK;891BAL_INFO_PACKAGE* pPackage = nullptr;892893if (_nextPackageAfterRestart) {894// After restart we need to finish the dependency registration for our package so allow the package895// to go present.896if (CSTR_EQUAL == ::CompareStringW(LOCALE_NEUTRAL, 0, wzPackageId, -1, _nextPackageAfterRestart, -1)) {897// Do not allow a repair because that could put us in a perpetual restart loop.898if (BOOTSTRAPPER_REQUEST_STATE_REPAIR == *pRequestState) {899*pRequestState = BOOTSTRAPPER_REQUEST_STATE_PRESENT;900}901902ReleaseNullStr(_nextPackageAfterRestart); // no more skipping now.903} else {904// not the matching package, so skip it.905BalLog(BOOTSTRAPPER_LOG_LEVEL_STANDARD, "Skipping package: %ls, after restart because it was applied before the restart.", wzPackageId);906907*pRequestState = BOOTSTRAPPER_REQUEST_STATE_NONE;908}909} else if ((_plannedAction == BOOTSTRAPPER_ACTION_INSTALL || _plannedAction == BOOTSTRAPPER_ACTION_MODIFY) &&910SUCCEEDED(BalInfoFindPackageById(&_bundle.packages, wzPackageId, &pPackage))) {911BOOL f = FALSE;912if (SUCCEEDED(_engine->EvaluateCondition(pPackage->sczInstallCondition, &f)) && f) {913*pRequestState = BOOTSTRAPPER_REQUEST_STATE_PRESENT;914}915}916917return CheckCanceled() ? IDCANCEL : IDOK;918}919920virtual STDMETHODIMP_(int) OnPlanMsiFeature(921__in_z LPCWSTR wzPackageId,922__in_z LPCWSTR wzFeatureId,923__inout BOOTSTRAPPER_FEATURE_STATE* pRequestedState924) {925LONGLONG install;926927if (wcscmp(wzFeatureId, L"AssociateFiles") == 0 || wcscmp(wzFeatureId, L"Shortcuts") == 0) {928if (SUCCEEDED(_engine->GetVariableNumeric(wzFeatureId, &install)) && install) {929*pRequestedState = BOOTSTRAPPER_FEATURE_STATE_LOCAL;930} else {931*pRequestedState = BOOTSTRAPPER_FEATURE_STATE_ABSENT;932}933} else {934*pRequestedState = BOOTSTRAPPER_FEATURE_STATE_LOCAL;935}936return CheckCanceled() ? IDCANCEL : IDNOACTION;937}938939virtual STDMETHODIMP_(void) OnPlanComplete(__in HRESULT hrStatus) {940if (SUCCEEDED(hrStatus) && _baFunction) {941BalLog(BOOTSTRAPPER_LOG_LEVEL_STANDARD, "Running plan complete BA function");942_baFunction->OnPlanComplete();943}944945SetState(PYBA_STATE_PLANNED, hrStatus);946947if (SUCCEEDED(hrStatus)) {948::PostMessageW(_hWnd, WM_PYBA_APPLY_PACKAGES, 0, 0);949}950951_startedExecution = FALSE;952_calculatedCacheProgress = 0;953_calculatedExecuteProgress = 0;954}955956957virtual STDMETHODIMP_(int) OnCachePackageBegin(958__in_z LPCWSTR wzPackageId,959__in DWORD cCachePayloads,960__in DWORD64 dw64PackageCacheSize961) {962if (wzPackageId && *wzPackageId) {963BAL_INFO_PACKAGE* pPackage = nullptr;964HRESULT hr = BalInfoFindPackageById(&_bundle.packages, wzPackageId, &pPackage);965LPCWSTR wz = (SUCCEEDED(hr) && pPackage->sczDisplayName) ? pPackage->sczDisplayName : wzPackageId;966967ThemeSetTextControl(_theme, ID_CACHE_PROGRESS_PACKAGE_TEXT, wz);968969// If something started executing, leave it in the overall progress text.970if (!_startedExecution) {971ThemeSetTextControl(_theme, ID_OVERALL_PROGRESS_PACKAGE_TEXT, wz);972}973}974975return __super::OnCachePackageBegin(wzPackageId, cCachePayloads, dw64PackageCacheSize);976}977978979virtual STDMETHODIMP_(int) OnCacheAcquireProgress(980__in_z LPCWSTR wzPackageOrContainerId,981__in_z_opt LPCWSTR wzPayloadId,982__in DWORD64 dw64Progress,983__in DWORD64 dw64Total,984__in DWORD dwOverallPercentage985) {986WCHAR wzProgress[5] = { };987988#ifdef DEBUG989BalLog(BOOTSTRAPPER_LOG_LEVEL_STANDARD, "PYBA: OnCacheAcquireProgress() - container/package: %ls, payload: %ls, progress: %I64u, total: %I64u, overall progress: %u%%", wzPackageOrContainerId, wzPayloadId, dw64Progress, dw64Total, dwOverallPercentage);990#endif991992::StringCchPrintfW(wzProgress, countof(wzProgress), L"%u%%", dwOverallPercentage);993ThemeSetTextControl(_theme, ID_CACHE_PROGRESS_TEXT, wzProgress);994995ThemeSetProgressControl(_theme, ID_CACHE_PROGRESS_BAR, dwOverallPercentage);996997_calculatedCacheProgress = dwOverallPercentage * PYBA_ACQUIRE_PERCENTAGE / 100;998ThemeSetProgressControl(_theme, ID_OVERALL_CALCULATED_PROGRESS_BAR, _calculatedCacheProgress + _calculatedExecuteProgress);9991000SetTaskbarButtonProgress(_calculatedCacheProgress + _calculatedExecuteProgress);10011002return __super::OnCacheAcquireProgress(wzPackageOrContainerId, wzPayloadId, dw64Progress, dw64Total, dwOverallPercentage);1003}100410051006virtual STDMETHODIMP_(int) OnCacheAcquireComplete(1007__in_z LPCWSTR wzPackageOrContainerId,1008__in_z_opt LPCWSTR wzPayloadId,1009__in HRESULT hrStatus,1010__in int nRecommendation1011) {1012SetProgressState(hrStatus);1013return __super::OnCacheAcquireComplete(wzPackageOrContainerId, wzPayloadId, hrStatus, nRecommendation);1014}101510161017virtual STDMETHODIMP_(int) OnCacheVerifyComplete(1018__in_z LPCWSTR wzPackageId,1019__in_z LPCWSTR wzPayloadId,1020__in HRESULT hrStatus,1021__in int nRecommendation1022) {1023SetProgressState(hrStatus);1024return __super::OnCacheVerifyComplete(wzPackageId, wzPayloadId, hrStatus, nRecommendation);1025}102610271028virtual STDMETHODIMP_(void) OnCacheComplete(__in HRESULT /*hrStatus*/) {1029ThemeSetTextControl(_theme, ID_CACHE_PROGRESS_PACKAGE_TEXT, L"");1030SetState(PYBA_STATE_CACHED, S_OK); // we always return success here and let OnApplyComplete() deal with the error.1031}103210331034virtual STDMETHODIMP_(int) OnError(1035__in BOOTSTRAPPER_ERROR_TYPE errorType,1036__in LPCWSTR wzPackageId,1037__in DWORD dwCode,1038__in_z LPCWSTR wzError,1039__in DWORD dwUIHint,1040__in DWORD /*cData*/,1041__in_ecount_z_opt(cData) LPCWSTR* /*rgwzData*/,1042__in int nRecommendation1043) {1044int nResult = nRecommendation;1045LPWSTR sczError = nullptr;10461047if (BOOTSTRAPPER_DISPLAY_EMBEDDED == _command.display) {1048HRESULT hr = _engine->SendEmbeddedError(dwCode, wzError, dwUIHint, &nResult);1049if (FAILED(hr)) {1050nResult = IDERROR;1051}1052} else if (BOOTSTRAPPER_DISPLAY_FULL == _command.display) {1053// If this is an authentication failure, let the engine try to handle it for us.1054if (BOOTSTRAPPER_ERROR_TYPE_HTTP_AUTH_SERVER == errorType || BOOTSTRAPPER_ERROR_TYPE_HTTP_AUTH_PROXY == errorType) {1055nResult = IDTRYAGAIN;1056} else // show a generic error message box.1057{1058BalRetryErrorOccurred(wzPackageId, dwCode);10591060if (!_showingInternalUIThisPackage) {1061// If no error message was provided, use the error code to try and get an error message.1062if (!wzError || !*wzError || BOOTSTRAPPER_ERROR_TYPE_WINDOWS_INSTALLER != errorType) {1063HRESULT hr = StrAllocFromError(&sczError, dwCode, nullptr);1064if (FAILED(hr) || !sczError || !*sczError) {1065StrAllocFormatted(&sczError, L"0x%x", dwCode);1066}1067}10681069nResult = ::MessageBoxW(_hWnd, sczError ? sczError : wzError, _theme->sczCaption, dwUIHint);1070}1071}10721073SetProgressState(HRESULT_FROM_WIN32(dwCode));1074} else {1075// just take note of the error code and let things continue.1076BalRetryErrorOccurred(wzPackageId, dwCode);1077}10781079ReleaseStr(sczError);1080return nResult;1081}108210831084virtual STDMETHODIMP_(int) OnExecuteMsiMessage(1085__in_z LPCWSTR wzPackageId,1086__in INSTALLMESSAGE mt,1087__in UINT uiFlags,1088__in_z LPCWSTR wzMessage,1089__in DWORD cData,1090__in_ecount_z_opt(cData) LPCWSTR* rgwzData,1091__in int nRecommendation1092) {1093#ifdef DEBUG1094BalLog(BOOTSTRAPPER_LOG_LEVEL_STANDARD, "PYBA: OnExecuteMsiMessage() - package: %ls, message: %ls", wzPackageId, wzMessage);1095#endif1096if (BOOTSTRAPPER_DISPLAY_FULL == _command.display && (INSTALLMESSAGE_WARNING == mt || INSTALLMESSAGE_USER == mt)) {1097int nResult = ::MessageBoxW(_hWnd, wzMessage, _theme->sczCaption, uiFlags);1098return nResult;1099}11001101if (INSTALLMESSAGE_ACTIONSTART == mt) {1102ThemeSetTextControl(_theme, ID_EXECUTE_PROGRESS_ACTIONDATA_TEXT, wzMessage);1103}11041105return __super::OnExecuteMsiMessage(wzPackageId, mt, uiFlags, wzMessage, cData, rgwzData, nRecommendation);1106}110711081109virtual STDMETHODIMP_(int) OnProgress(__in DWORD dwProgressPercentage, __in DWORD dwOverallProgressPercentage) {1110WCHAR wzProgress[5] = { };11111112#ifdef DEBUG1113BalLog(BOOTSTRAPPER_LOG_LEVEL_STANDARD, "PYBA: OnProgress() - progress: %u%%, overall progress: %u%%", dwProgressPercentage, dwOverallProgressPercentage);1114#endif11151116::StringCchPrintfW(wzProgress, countof(wzProgress), L"%u%%", dwOverallProgressPercentage);1117ThemeSetTextControl(_theme, ID_OVERALL_PROGRESS_TEXT, wzProgress);11181119ThemeSetProgressControl(_theme, ID_OVERALL_PROGRESS_BAR, dwOverallProgressPercentage);1120SetTaskbarButtonProgress(dwOverallProgressPercentage);11211122return __super::OnProgress(dwProgressPercentage, dwOverallProgressPercentage);1123}112411251126virtual STDMETHODIMP_(int) OnExecutePackageBegin(__in_z LPCWSTR wzPackageId, __in BOOL fExecute) {1127LPWSTR sczFormattedString = nullptr;11281129_startedExecution = TRUE;11301131if (wzPackageId && *wzPackageId) {1132BAL_INFO_PACKAGE* pPackage = nullptr;1133BalInfoFindPackageById(&_bundle.packages, wzPackageId, &pPackage);11341135LPCWSTR wz = wzPackageId;1136if (pPackage) {1137LOC_STRING* pLocString = nullptr;11381139switch (pPackage->type) {1140case BAL_INFO_PACKAGE_TYPE_BUNDLE_ADDON:1141LocGetString(_wixLoc, L"#(loc.ExecuteAddonRelatedBundleMessage)", &pLocString);1142break;11431144case BAL_INFO_PACKAGE_TYPE_BUNDLE_PATCH:1145LocGetString(_wixLoc, L"#(loc.ExecutePatchRelatedBundleMessage)", &pLocString);1146break;11471148case BAL_INFO_PACKAGE_TYPE_BUNDLE_UPGRADE:1149LocGetString(_wixLoc, L"#(loc.ExecuteUpgradeRelatedBundleMessage)", &pLocString);1150break;1151}11521153if (pLocString) {1154// If the wix developer is showing a hidden variable in the UI, then obviously they don't care about keeping it safe1155// so don't go down the rabbit hole of making sure that this is securely freed.1156BalFormatString(pLocString->wzText, &sczFormattedString);1157}11581159wz = sczFormattedString ? sczFormattedString : pPackage->sczDisplayName ? pPackage->sczDisplayName : wzPackageId;1160}11611162_showingInternalUIThisPackage = pPackage && pPackage->fDisplayInternalUI;11631164ThemeSetTextControl(_theme, ID_EXECUTE_PROGRESS_PACKAGE_TEXT, wz);1165ThemeSetTextControl(_theme, ID_OVERALL_PROGRESS_PACKAGE_TEXT, wz);1166} else {1167_showingInternalUIThisPackage = FALSE;1168}11691170ReleaseStr(sczFormattedString);1171return __super::OnExecutePackageBegin(wzPackageId, fExecute);1172}117311741175virtual int __stdcall OnExecuteProgress(1176__in_z LPCWSTR wzPackageId,1177__in DWORD dwProgressPercentage,1178__in DWORD dwOverallProgressPercentage1179) {1180WCHAR wzProgress[8] = { };11811182#ifdef DEBUG1183BalLog(BOOTSTRAPPER_LOG_LEVEL_STANDARD, "PYBA: OnExecuteProgress() - package: %ls, progress: %u%%, overall progress: %u%%", wzPackageId, dwProgressPercentage, dwOverallProgressPercentage);1184#endif11851186::StringCchPrintfW(wzProgress, countof(wzProgress), L"%u%%", dwOverallProgressPercentage);1187ThemeSetTextControl(_theme, ID_EXECUTE_PROGRESS_TEXT, wzProgress);11881189ThemeSetProgressControl(_theme, ID_EXECUTE_PROGRESS_BAR, dwOverallProgressPercentage);11901191_calculatedExecuteProgress = dwOverallProgressPercentage * (100 - PYBA_ACQUIRE_PERCENTAGE) / 100;1192ThemeSetProgressControl(_theme, ID_OVERALL_CALCULATED_PROGRESS_BAR, _calculatedCacheProgress + _calculatedExecuteProgress);11931194SetTaskbarButtonProgress(_calculatedCacheProgress + _calculatedExecuteProgress);11951196return __super::OnExecuteProgress(wzPackageId, dwProgressPercentage, dwOverallProgressPercentage);1197}119811991200virtual STDMETHODIMP_(int) OnExecutePackageComplete(1201__in_z LPCWSTR wzPackageId,1202__in HRESULT hrExitCode,1203__in BOOTSTRAPPER_APPLY_RESTART restart,1204__in int nRecommendation1205) {1206SetProgressState(hrExitCode);12071208if (_wcsnicmp(wzPackageId, L"path_", 5) == 0 && SUCCEEDED(hrExitCode)) {1209SendMessageTimeoutW(1210HWND_BROADCAST,1211WM_SETTINGCHANGE,12120,1213reinterpret_cast<LPARAM>(L"Environment"),1214SMTO_ABORTIFHUNG,12151000,1216nullptr1217);1218}12191220int nResult = __super::OnExecutePackageComplete(wzPackageId, hrExitCode, restart, nRecommendation);12211222return nResult;1223}122412251226virtual STDMETHODIMP_(void) OnExecuteComplete(__in HRESULT hrStatus) {1227ThemeSetTextControl(_theme, ID_EXECUTE_PROGRESS_PACKAGE_TEXT, L"");1228ThemeSetTextControl(_theme, ID_EXECUTE_PROGRESS_ACTIONDATA_TEXT, L"");1229ThemeSetTextControl(_theme, ID_OVERALL_PROGRESS_PACKAGE_TEXT, L"");1230ThemeControlEnable(_theme, ID_PROGRESS_CANCEL_BUTTON, FALSE); // no more cancel.12311232SetState(PYBA_STATE_EXECUTED, S_OK); // we always return success here and let OnApplyComplete() deal with the error.1233SetProgressState(hrStatus);1234}123512361237virtual STDMETHODIMP_(int) OnResolveSource(1238__in_z LPCWSTR wzPackageOrContainerId,1239__in_z_opt LPCWSTR wzPayloadId,1240__in_z LPCWSTR wzLocalSource,1241__in_z_opt LPCWSTR wzDownloadSource1242) {1243int nResult = IDERROR; // assume we won't resolve source and that is unexpected.12441245if (BOOTSTRAPPER_DISPLAY_FULL == _command.display) {1246if (wzDownloadSource) {1247nResult = IDDOWNLOAD;1248} else {1249// prompt to change the source location.1250OPENFILENAMEW ofn = { };1251WCHAR wzFile[MAX_PATH] = { };12521253::StringCchCopyW(wzFile, countof(wzFile), wzLocalSource);12541255ofn.lStructSize = sizeof(ofn);1256ofn.hwndOwner = _hWnd;1257ofn.lpstrFile = wzFile;1258ofn.nMaxFile = countof(wzFile);1259ofn.lpstrFilter = L"All Files\0*.*\0";1260ofn.nFilterIndex = 1;1261ofn.Flags = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST;1262ofn.lpstrTitle = _theme->sczCaption;12631264if (::GetOpenFileNameW(&ofn)) {1265HRESULT hr = _engine->SetLocalSource(wzPackageOrContainerId, wzPayloadId, ofn.lpstrFile);1266nResult = SUCCEEDED(hr) ? IDRETRY : IDERROR;1267} else {1268nResult = IDCANCEL;1269}1270}1271} else if (wzDownloadSource) {1272// If doing a non-interactive install and download source is available, let's try downloading the package silently1273nResult = IDDOWNLOAD;1274}1275// else there's nothing more we can do in non-interactive mode12761277return CheckCanceled() ? IDCANCEL : nResult;1278}127912801281virtual STDMETHODIMP_(int) OnApplyComplete(__in HRESULT hrStatus, __in BOOTSTRAPPER_APPLY_RESTART restart) {1282_restartResult = restart; // remember the restart result so we return the correct error code no matter what the user chooses to do in the UI.12831284// If a restart was encountered and we are not suppressing restarts, then restart is required.1285_restartRequired = (BOOTSTRAPPER_APPLY_RESTART_NONE != restart && BOOTSTRAPPER_RESTART_NEVER < _command.restart);1286// If a restart is required and we're not displaying a UI or we are not supposed to prompt for restart then allow the restart.1287_allowRestart = _restartRequired && (BOOTSTRAPPER_DISPLAY_FULL > _command.display || BOOTSTRAPPER_RESTART_PROMPT < _command.restart);12881289// If we are showing UI, wait a beat before moving to the final screen.1290if (BOOTSTRAPPER_DISPLAY_NONE < _command.display) {1291::Sleep(250);1292}12931294SetState(PYBA_STATE_APPLIED, hrStatus);1295SetTaskbarButtonProgress(100); // show full progress bar, green, yellow, or red12961297return IDNOACTION;1298}12991300virtual STDMETHODIMP_(void) OnLaunchApprovedExeComplete(__in HRESULT hrStatus, __in DWORD /*processId*/) {1301}130213031304private:1305//1306// UiThreadProc - entrypoint for UI thread.1307//1308static DWORD WINAPI UiThreadProc(__in LPVOID pvContext) {1309HRESULT hr = S_OK;1310PythonBootstrapperApplication* pThis = (PythonBootstrapperApplication*)pvContext;1311BOOL comInitialized = FALSE;1312BOOL ret = FALSE;1313MSG msg = { };13141315// Initialize COM and theme.1316hr = ::CoInitialize(nullptr);1317BalExitOnFailure(hr, "Failed to initialize COM.");1318comInitialized = TRUE;13191320hr = ThemeInitialize(pThis->_hModule);1321BalExitOnFailure(hr, "Failed to initialize theme manager.");13221323hr = pThis->InitializeData();1324BalExitOnFailure(hr, "Failed to initialize data in bootstrapper application.");13251326// Create main window.1327pThis->InitializeTaskbarButton();1328hr = pThis->CreateMainWindow();1329BalExitOnFailure(hr, "Failed to create main window.");13301331pThis->ValidateOperatingSystem();13321333if (FAILED(pThis->_hrFinal)) {1334pThis->SetState(PYBA_STATE_FAILED, hr);1335::PostMessageW(pThis->_hWnd, WM_PYBA_SHOW_FAILURE, 0, 0);1336} else {1337// Okay, we're ready for packages now.1338pThis->SetState(PYBA_STATE_INITIALIZED, hr);1339::PostMessageW(pThis->_hWnd, BOOTSTRAPPER_ACTION_HELP == pThis->_command.action ? WM_PYBA_SHOW_HELP : WM_PYBA_DETECT_PACKAGES, 0, 0);1340}13411342// message pump1343while (0 != (ret = ::GetMessageW(&msg, nullptr, 0, 0))) {1344if (-1 == ret) {1345hr = E_UNEXPECTED;1346BalExitOnFailure(hr, "Unexpected return value from message pump.");1347} else if (!ThemeHandleKeyboardMessage(pThis->_theme, msg.hwnd, &msg)) {1348::TranslateMessage(&msg);1349::DispatchMessageW(&msg);1350}1351}13521353// Succeeded thus far, check to see if anything went wrong while actually1354// executing changes.1355if (FAILED(pThis->_hrFinal)) {1356hr = pThis->_hrFinal;1357} else if (pThis->CheckCanceled()) {1358hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT);1359}13601361LExit:1362// destroy main window1363pThis->DestroyMainWindow();13641365// initiate engine shutdown1366DWORD dwQuit = HRESULT_CODE(hr);1367if (BOOTSTRAPPER_APPLY_RESTART_INITIATED == pThis->_restartResult) {1368dwQuit = ERROR_SUCCESS_REBOOT_INITIATED;1369} else if (BOOTSTRAPPER_APPLY_RESTART_REQUIRED == pThis->_restartResult) {1370dwQuit = ERROR_SUCCESS_REBOOT_REQUIRED;1371}1372pThis->_engine->Quit(dwQuit);13731374ReleaseTheme(pThis->_theme);1375ThemeUninitialize();13761377// uninitialize COM1378if (comInitialized) {1379::CoUninitialize();1380}13811382return hr;1383}13841385//1386// ParseVariablesFromUnattendXml - reads options from unattend.xml if it1387// exists1388//1389HRESULT ParseVariablesFromUnattendXml() {1390HRESULT hr = S_OK;1391LPWSTR sczUnattendXmlPath = nullptr;1392IXMLDOMDocument *pixdUnattend = nullptr;1393IXMLDOMNodeList *pNodes = nullptr;1394IXMLDOMNode *pNode = nullptr;1395long cNodes;1396DWORD dwAttr;1397LPWSTR scz = nullptr;1398BOOL bValue;1399int iValue;1400BOOL tryConvert;1401BSTR bstrValue = nullptr;14021403hr = BalFormatString(L"[WixBundleOriginalSourceFolder]unattend.xml", &sczUnattendXmlPath);1404BalExitOnFailure(hr, "Failed to calculate path to unattend.xml");14051406if (!FileExistsEx(sczUnattendXmlPath, &dwAttr)) {1407BalLog(BOOTSTRAPPER_LOG_LEVEL_VERBOSE, "Did not find %ls", sczUnattendXmlPath);1408hr = S_FALSE;1409goto LExit;1410}14111412hr = XmlLoadDocumentFromFile(sczUnattendXmlPath, &pixdUnattend);1413BalExitOnFailure1(hr, "Failed to read %ls", sczUnattendXmlPath);14141415// get the list of variables users have overridden1416hr = XmlSelectNodes(pixdUnattend, L"/Options/Option", &pNodes);1417if (S_FALSE == hr) {1418ExitFunction1(hr = S_OK);1419}1420BalExitOnFailure(hr, "Failed to select option nodes.");14211422hr = pNodes->get_length((long*)&cNodes);1423BalExitOnFailure(hr, "Failed to get option node count.");14241425BalLog(BOOTSTRAPPER_LOG_LEVEL_STANDARD, "Reading settings from %ls", sczUnattendXmlPath);14261427for (DWORD i = 0; i < cNodes; ++i) {1428hr = XmlNextElement(pNodes, &pNode, nullptr);1429BalExitOnFailure(hr, "Failed to get next node.");14301431// @Name1432hr = XmlGetAttributeEx(pNode, L"Name", &scz);1433BalExitOnFailure(hr, "Failed to get @Name.");14341435tryConvert = TRUE;1436hr = XmlGetAttribute(pNode, L"Value", &bstrValue);1437if (FAILED(hr) || !bstrValue || !*bstrValue) {1438hr = XmlGetText(pNode, &bstrValue);1439tryConvert = FALSE;1440}1441BalExitOnFailure(hr, "Failed to get @Value.");14421443if (tryConvert &&1444CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, bstrValue, -1, L"yes", -1)) {1445_engine->SetVariableNumeric(scz, 1);1446} else if (tryConvert &&1447CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, bstrValue, -1, L"no", -1)) {1448_engine->SetVariableNumeric(scz, 0);1449} else if (tryConvert && ::StrToIntExW(bstrValue, STIF_DEFAULT, &iValue)) {1450_engine->SetVariableNumeric(scz, iValue);1451} else {1452_engine->SetVariableString(scz, bstrValue);1453}14541455ReleaseNullBSTR(bstrValue);1456ReleaseNullStr(scz);1457ReleaseNullObject(pNode);1458}14591460BalLog(BOOTSTRAPPER_LOG_LEVEL_STANDARD, "Finished reading from %ls", sczUnattendXmlPath);14611462LExit:1463ReleaseObject(pNode);1464ReleaseObject(pNodes);1465ReleaseObject(pixdUnattend);1466ReleaseStr(sczUnattendXmlPath);14671468return hr;1469}147014711472//1473// InitializeData - initializes all the package information.1474//1475HRESULT InitializeData() {1476HRESULT hr = S_OK;1477LPWSTR sczModulePath = nullptr;1478IXMLDOMDocument *pixdManifest = nullptr;14791480hr = BalManifestLoad(_hModule, &pixdManifest);1481BalExitOnFailure(hr, "Failed to load bootstrapper application manifest.");14821483hr = ParseOverridableVariablesFromXml(pixdManifest);1484BalExitOnFailure(hr, "Failed to read overridable variables.");14851486if (_command.action == BOOTSTRAPPER_ACTION_MODIFY) {1487LoadOptionalFeatureStates(_engine);1488}14891490hr = ParseVariablesFromUnattendXml();1491ExitOnFailure(hr, "Failed to read unattend.ini file.");14921493hr = ProcessCommandLine(&_language);1494ExitOnFailure(hr, "Unknown commandline parameters.");14951496hr = PathRelativeToModule(&sczModulePath, nullptr, _hModule);1497BalExitOnFailure(hr, "Failed to get module path.");14981499hr = LoadLocalization(sczModulePath, _language);1500ExitOnFailure(hr, "Failed to load localization.");15011502hr = LoadTheme(sczModulePath, _language);1503ExitOnFailure(hr, "Failed to load theme.");15041505hr = BalInfoParseFromXml(&_bundle, pixdManifest);1506BalExitOnFailure(hr, "Failed to load bundle information.");15071508hr = BalConditionsParseFromXml(&_conditions, pixdManifest, _wixLoc);1509BalExitOnFailure(hr, "Failed to load conditions from XML.");15101511hr = LoadBootstrapperBAFunctions();1512BalExitOnFailure(hr, "Failed to load bootstrapper functions.");15131514hr = UpdateUIStrings(_command.action);1515BalExitOnFailure(hr, "Failed to load UI strings.");15161517GetBundleFileVersion();1518// don't fail if we couldn't get the version info; best-effort only1519LExit:1520ReleaseObject(pixdManifest);1521ReleaseStr(sczModulePath);15221523return hr;1524}152515261527//1528// ProcessCommandLine - process the provided command line arguments.1529//1530HRESULT ProcessCommandLine(__inout LPWSTR* psczLanguage) {1531HRESULT hr = S_OK;1532int argc = 0;1533LPWSTR* argv = nullptr;1534LPWSTR sczVariableName = nullptr;1535LPWSTR sczVariableValue = nullptr;15361537if (_command.wzCommandLine && *_command.wzCommandLine) {1538argv = ::CommandLineToArgvW(_command.wzCommandLine, &argc);1539ExitOnNullWithLastError(argv, hr, "Failed to get command line.");15401541for (int i = 0; i < argc; ++i) {1542if (argv[i][0] == L'-' || argv[i][0] == L'/') {1543if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, L"lang", -1)) {1544if (i + 1 >= argc) {1545hr = E_INVALIDARG;1546BalExitOnFailure(hr, "Must specify a language.");1547}15481549++i;15501551hr = StrAllocString(psczLanguage, &argv[i][0], 0);1552BalExitOnFailure(hr, "Failed to copy language.");1553} else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, L"simple", -1)) {1554_engine->SetVariableNumeric(L"SimpleInstall", 1);1555}1556} else if (_overridableVariables) {1557int value;1558const wchar_t* pwc = wcschr(argv[i], L'=');1559if (pwc) {1560hr = StrAllocString(&sczVariableName, argv[i], pwc - argv[i]);1561BalExitOnFailure(hr, "Failed to copy variable name.");15621563hr = DictKeyExists(_overridableVariables, sczVariableName);1564if (E_NOTFOUND == hr) {1565BalLog(BOOTSTRAPPER_LOG_LEVEL_ERROR, "Ignoring attempt to set non-overridable variable: '%ls'.", sczVariableName);1566hr = S_OK;1567continue;1568}1569ExitOnFailure(hr, "Failed to check the dictionary of overridable variables.");15701571hr = StrAllocString(&sczVariableValue, ++pwc, 0);1572BalExitOnFailure(hr, "Failed to copy variable value.");15731574if (::StrToIntEx(sczVariableValue, STIF_DEFAULT, &value)) {1575hr = _engine->SetVariableNumeric(sczVariableName, value);1576} else {1577hr = _engine->SetVariableString(sczVariableName, sczVariableValue);1578}1579BalExitOnFailure(hr, "Failed to set variable.");1580} else {1581BalLog(BOOTSTRAPPER_LOG_LEVEL_STANDARD, "Ignoring unknown argument: %ls", argv[i]);1582}1583}1584}1585}15861587LExit:1588if (argv) {1589::LocalFree(argv);1590}15911592ReleaseStr(sczVariableName);1593ReleaseStr(sczVariableValue);15941595return hr;1596}15971598HRESULT LoadLocalization(__in_z LPCWSTR wzModulePath, __in_z_opt LPCWSTR wzLanguage) {1599HRESULT hr = S_OK;1600LPWSTR sczLocPath = nullptr;1601LPCWSTR wzLocFileName = L"Default.wxl";16021603hr = LocProbeForFile(wzModulePath, wzLocFileName, wzLanguage, &sczLocPath);1604BalExitOnFailure2(hr, "Failed to probe for loc file: %ls in path: %ls", wzLocFileName, wzModulePath);16051606hr = LocLoadFromFile(sczLocPath, &_wixLoc);1607BalExitOnFailure1(hr, "Failed to load loc file from path: %ls", sczLocPath);16081609if (WIX_LOCALIZATION_LANGUAGE_NOT_SET != _wixLoc->dwLangId) {1610::SetThreadLocale(_wixLoc->dwLangId);1611}16121613hr = StrAllocString(&_confirmCloseMessage, L"#(loc.ConfirmCancelMessage)", 0);1614ExitOnFailure(hr, "Failed to initialize confirm message loc identifier.");16151616hr = LocLocalizeString(_wixLoc, &_confirmCloseMessage);1617BalExitOnFailure1(hr, "Failed to localize confirm close message: %ls", _confirmCloseMessage);16181619LExit:1620ReleaseStr(sczLocPath);16211622return hr;1623}162416251626HRESULT LoadTheme(__in_z LPCWSTR wzModulePath, __in_z_opt LPCWSTR wzLanguage) {1627HRESULT hr = S_OK;1628LPWSTR sczThemePath = nullptr;1629LPCWSTR wzThemeFileName = L"Default.thm";1630LPWSTR sczCaption = nullptr;16311632hr = LocProbeForFile(wzModulePath, wzThemeFileName, wzLanguage, &sczThemePath);1633BalExitOnFailure2(hr, "Failed to probe for theme file: %ls in path: %ls", wzThemeFileName, wzModulePath);16341635hr = ThemeLoadFromFile(sczThemePath, &_theme);1636BalExitOnFailure1(hr, "Failed to load theme from path: %ls", sczThemePath);16371638hr = ThemeLocalize(_theme, _wixLoc);1639BalExitOnFailure1(hr, "Failed to localize theme: %ls", sczThemePath);16401641// Update the caption if there are any formatted strings in it.1642// If the wix developer is showing a hidden variable in the UI, then1643// obviously they don't care about keeping it safe so don't go down the1644// rabbit hole of making sure that this is securely freed.1645hr = BalFormatString(_theme->sczCaption, &sczCaption);1646if (SUCCEEDED(hr)) {1647ThemeUpdateCaption(_theme, sczCaption);1648}16491650LExit:1651ReleaseStr(sczCaption);1652ReleaseStr(sczThemePath);16531654return hr;1655}165616571658HRESULT ParseOverridableVariablesFromXml(__in IXMLDOMDocument* pixdManifest) {1659HRESULT hr = S_OK;1660IXMLDOMNode* pNode = nullptr;1661IXMLDOMNodeList* pNodes = nullptr;1662DWORD cNodes = 0;1663LPWSTR scz = nullptr;1664BOOL hidden = FALSE;16651666// get the list of variables users can override on the command line1667hr = XmlSelectNodes(pixdManifest, L"/BootstrapperApplicationData/WixStdbaOverridableVariable", &pNodes);1668if (S_FALSE == hr) {1669ExitFunction1(hr = S_OK);1670}1671ExitOnFailure(hr, "Failed to select overridable variable nodes.");16721673hr = pNodes->get_length((long*)&cNodes);1674ExitOnFailure(hr, "Failed to get overridable variable node count.");16751676if (cNodes) {1677hr = DictCreateStringList(&_overridableVariables, 32, DICT_FLAG_NONE);1678ExitOnFailure(hr, "Failed to create the string dictionary.");16791680for (DWORD i = 0; i < cNodes; ++i) {1681hr = XmlNextElement(pNodes, &pNode, nullptr);1682ExitOnFailure(hr, "Failed to get next node.");16831684// @Name1685hr = XmlGetAttributeEx(pNode, L"Name", &scz);1686ExitOnFailure(hr, "Failed to get @Name.");16871688hr = XmlGetYesNoAttribute(pNode, L"Hidden", &hidden);16891690if (!hidden) {1691hr = DictAddKey(_overridableVariables, scz);1692ExitOnFailure1(hr, "Failed to add \"%ls\" to the string dictionary.", scz);1693}16941695// prepare next iteration1696ReleaseNullObject(pNode);1697}1698}16991700LExit:1701ReleaseObject(pNode);1702ReleaseObject(pNodes);1703ReleaseStr(scz);1704return hr;1705}170617071708//1709// Get the file version of the bootstrapper and record in bootstrapper log file1710//1711HRESULT GetBundleFileVersion() {1712HRESULT hr = S_OK;1713ULARGE_INTEGER uliVersion = { };1714LPWSTR sczCurrentPath = nullptr;17151716hr = PathForCurrentProcess(&sczCurrentPath, nullptr);1717BalExitOnFailure(hr, "Failed to get bundle path.");17181719hr = FileVersion(sczCurrentPath, &uliVersion.HighPart, &uliVersion.LowPart);1720BalExitOnFailure(hr, "Failed to get bundle file version.");17211722hr = _engine->SetVariableVersion(PYBA_VARIABLE_BUNDLE_FILE_VERSION, uliVersion.QuadPart);1723BalExitOnFailure(hr, "Failed to set WixBundleFileVersion variable.");17241725LExit:1726ReleaseStr(sczCurrentPath);17271728return hr;1729}173017311732//1733// CreateMainWindow - creates the main install window.1734//1735HRESULT CreateMainWindow() {1736HRESULT hr = S_OK;1737HICON hIcon = reinterpret_cast<HICON>(_theme->hIcon);1738WNDCLASSW wc = { };1739DWORD dwWindowStyle = 0;1740int x = CW_USEDEFAULT;1741int y = CW_USEDEFAULT;1742POINT ptCursor = { };1743HMONITOR hMonitor = nullptr;1744MONITORINFO mi = { };1745COLORREF fg, bg;1746HBRUSH bgBrush;17471748// If the theme did not provide an icon, try using the icon from the bundle engine.1749if (!hIcon) {1750HMODULE hBootstrapperEngine = ::GetModuleHandleW(nullptr);1751if (hBootstrapperEngine) {1752hIcon = ::LoadIconW(hBootstrapperEngine, MAKEINTRESOURCEW(1));1753}1754}17551756fg = RGB(0, 0, 0);1757bg = RGB(255, 255, 255);1758bgBrush = (HBRUSH)(COLOR_WINDOW+1);1759if (_theme->dwFontId < _theme->cFonts) {1760THEME_FONT *font = &_theme->rgFonts[_theme->dwFontId];1761fg = font->crForeground;1762bg = font->crBackground;1763bgBrush = font->hBackground;1764RemapColor(&fg, &bg, &bgBrush);1765}17661767// Register the window class and create the window.1768wc.lpfnWndProc = PythonBootstrapperApplication::WndProc;1769wc.hInstance = _hModule;1770wc.hIcon = hIcon;1771wc.hCursor = ::LoadCursorW(nullptr, (LPCWSTR)IDC_ARROW);1772wc.hbrBackground = bgBrush;1773wc.lpszMenuName = nullptr;1774wc.lpszClassName = PYBA_WINDOW_CLASS;1775if (!::RegisterClassW(&wc)) {1776ExitWithLastError(hr, "Failed to register window.");1777}17781779_registered = TRUE;17801781// Calculate the window style based on the theme style and command display value.1782dwWindowStyle = _theme->dwStyle;1783if (BOOTSTRAPPER_DISPLAY_NONE >= _command.display) {1784dwWindowStyle &= ~WS_VISIBLE;1785}17861787// Don't show the window if there is a splash screen (it will be made visible when the splash screen is hidden)1788if (::IsWindow(_command.hwndSplashScreen)) {1789dwWindowStyle &= ~WS_VISIBLE;1790}17911792// Center the window on the monitor with the mouse.1793if (::GetCursorPos(&ptCursor)) {1794hMonitor = ::MonitorFromPoint(ptCursor, MONITOR_DEFAULTTONEAREST);1795if (hMonitor) {1796mi.cbSize = sizeof(mi);1797if (::GetMonitorInfoW(hMonitor, &mi)) {1798x = mi.rcWork.left + (mi.rcWork.right - mi.rcWork.left - _theme->nWidth) / 2;1799y = mi.rcWork.top + (mi.rcWork.bottom - mi.rcWork.top - _theme->nHeight) / 2;1800}1801}1802}18031804_hWnd = ::CreateWindowExW(18050,1806wc.lpszClassName,1807_theme->sczCaption,1808dwWindowStyle,1809x,1810y,1811_theme->nWidth,1812_theme->nHeight,1813HWND_DESKTOP,1814nullptr,1815_hModule,1816this1817);1818ExitOnNullWithLastError(_hWnd, hr, "Failed to create window.");18191820hr = S_OK;18211822LExit:1823return hr;1824}182518261827//1828// InitializeTaskbarButton - initializes taskbar button for progress.1829//1830void InitializeTaskbarButton() {1831HRESULT hr = S_OK;18321833hr = ::CoCreateInstance(CLSID_TaskbarList, nullptr, CLSCTX_ALL, __uuidof(ITaskbarList3), reinterpret_cast<LPVOID*>(&_taskbarList));1834if (REGDB_E_CLASSNOTREG == hr) {1835// not supported before Windows 71836ExitFunction1(hr = S_OK);1837}1838BalExitOnFailure(hr, "Failed to create ITaskbarList3. Continuing.");18391840_taskbarButtonCreatedMessage = ::RegisterWindowMessageW(L"TaskbarButtonCreated");1841BalExitOnNullWithLastError(_taskbarButtonCreatedMessage, hr, "Failed to get TaskbarButtonCreated message. Continuing.");18421843LExit:1844return;1845}18461847//1848// DestroyMainWindow - clean up all the window registration.1849//1850void DestroyMainWindow() {1851if (::IsWindow(_hWnd)) {1852::DestroyWindow(_hWnd);1853_hWnd = nullptr;1854_taskbarButtonOK = FALSE;1855}18561857if (_registered) {1858::UnregisterClassW(PYBA_WINDOW_CLASS, _hModule);1859_registered = FALSE;1860}1861}186218631864//1865// WndProc - standard windows message handler.1866//1867static LRESULT CALLBACK WndProc(1868__in HWND hWnd,1869__in UINT uMsg,1870__in WPARAM wParam,1871__in LPARAM lParam1872) {1873#pragma warning(suppress:4312)1874auto pBA = reinterpret_cast<PythonBootstrapperApplication*>(::GetWindowLongPtrW(hWnd, GWLP_USERDATA));18751876switch (uMsg) {1877case WM_NCCREATE: {1878LPCREATESTRUCT lpcs = reinterpret_cast<LPCREATESTRUCT>(lParam);1879pBA = reinterpret_cast<PythonBootstrapperApplication*>(lpcs->lpCreateParams);1880#pragma warning(suppress:4244)1881::SetWindowLongPtrW(hWnd, GWLP_USERDATA, reinterpret_cast<LONG_PTR>(pBA));1882break;1883}18841885case WM_NCDESTROY: {1886LRESULT lres = ThemeDefWindowProc(pBA ? pBA->_theme : nullptr, hWnd, uMsg, wParam, lParam);1887::SetWindowLongPtrW(hWnd, GWLP_USERDATA, 0);1888return lres;1889}18901891case WM_CREATE:1892if (!pBA->OnCreate(hWnd)) {1893return -1;1894}1895break;18961897case WM_QUERYENDSESSION:1898return IDCANCEL != pBA->OnSystemShutdown(static_cast<DWORD>(lParam), IDCANCEL);18991900case WM_CLOSE:1901// If the user chose not to close, do *not* let the default window proc handle the message.1902if (!pBA->OnClose()) {1903return 0;1904}1905break;19061907case WM_DESTROY:1908::PostQuitMessage(0);1909break;19101911case WM_PAINT: __fallthrough;1912case WM_ERASEBKGND:1913if (pBA && pBA->_suppressPaint) {1914return TRUE;1915}1916break;19171918case WM_PYBA_SHOW_HELP:1919pBA->OnShowHelp();1920return 0;19211922case WM_PYBA_DETECT_PACKAGES:1923pBA->OnDetect();1924return 0;19251926case WM_PYBA_PLAN_PACKAGES:1927pBA->OnPlan(static_cast<BOOTSTRAPPER_ACTION>(lParam));1928return 0;19291930case WM_PYBA_APPLY_PACKAGES:1931pBA->OnApply();1932return 0;19331934case WM_PYBA_CHANGE_STATE:1935pBA->OnChangeState(static_cast<PYBA_STATE>(lParam));1936return 0;19371938case WM_PYBA_SHOW_FAILURE:1939pBA->OnShowFailure();1940return 0;19411942case WM_COMMAND:1943switch (LOWORD(wParam)) {1944// Customize commands1945// Success/failure commands1946case ID_SUCCESS_RESTART_BUTTON: __fallthrough;1947case ID_FAILURE_RESTART_BUTTON:1948pBA->OnClickRestartButton();1949return 0;19501951case IDCANCEL: __fallthrough;1952case ID_INSTALL_CANCEL_BUTTON: __fallthrough;1953case ID_CUSTOM1_CANCEL_BUTTON: __fallthrough;1954case ID_CUSTOM2_CANCEL_BUTTON: __fallthrough;1955case ID_MODIFY_CANCEL_BUTTON: __fallthrough;1956case ID_PROGRESS_CANCEL_BUTTON: __fallthrough;1957case ID_SUCCESS_CANCEL_BUTTON: __fallthrough;1958case ID_FAILURE_CANCEL_BUTTON: __fallthrough;1959case ID_CLOSE_BUTTON:1960pBA->OnCommand(ID_CLOSE_BUTTON);1961return 0;19621963default:1964pBA->OnCommand((CONTROL_ID)LOWORD(wParam));1965}1966break;19671968case WM_NOTIFY:1969if (lParam) {1970LPNMHDR pnmhdr = reinterpret_cast<LPNMHDR>(lParam);1971switch (pnmhdr->code) {1972case NM_CLICK: __fallthrough;1973case NM_RETURN:1974switch (static_cast<DWORD>(pnmhdr->idFrom)) {1975case ID_FAILURE_LOGFILE_LINK:1976pBA->OnClickLogFileLink();1977return 1;1978}1979}1980}1981break;19821983case WM_CTLCOLORSTATIC:1984case WM_CTLCOLORBTN:1985if (pBA) {1986HBRUSH brush = nullptr;1987if (pBA->SetControlColor((HWND)lParam, (HDC)wParam, &brush)) {1988return (LRESULT)brush;1989}1990}1991break;1992}19931994if (pBA && pBA->_taskbarList && uMsg == pBA->_taskbarButtonCreatedMessage) {1995pBA->_taskbarButtonOK = TRUE;1996return 0;1997}19981999return ThemeDefWindowProc(pBA ? pBA->_theme : nullptr, hWnd, uMsg, wParam, lParam);2000}20012002//2003// OnCreate - finishes loading the theme.2004//2005BOOL OnCreate(__in HWND hWnd) {2006HRESULT hr = S_OK;20072008hr = ThemeLoadControls(_theme, hWnd, CONTROL_ID_NAMES, countof(CONTROL_ID_NAMES));2009BalExitOnFailure(hr, "Failed to load theme controls.");20102011C_ASSERT(COUNT_PAGE == countof(PAGE_NAMES));2012C_ASSERT(countof(_pageIds) == countof(PAGE_NAMES));20132014ThemeGetPageIds(_theme, PAGE_NAMES, _pageIds, countof(_pageIds));20152016// Initialize the text on all "application" (non-page) controls.2017for (DWORD i = 0; i < _theme->cControls; ++i) {2018THEME_CONTROL* pControl = _theme->rgControls + i;2019LPWSTR text = nullptr;20202021if (!pControl->wPageId && pControl->sczText && *pControl->sczText) {2022HRESULT hrFormat;20232024// If the wix developer is showing a hidden variable in the UI,2025// then obviously they don't care about keeping it safe so don't2026// go down the rabbit hole of making sure that this is securely2027// freed.2028hrFormat = BalFormatString(pControl->sczText, &text);2029if (SUCCEEDED(hrFormat)) {2030ThemeSetTextControl(_theme, pControl->wId, text);2031ReleaseStr(text);2032}2033}2034}20352036LExit:2037return SUCCEEDED(hr);2038}20392040void RemapColor(COLORREF *fg, COLORREF *bg, HBRUSH *bgBrush) {2041if (*fg == RGB(0, 0, 0)) {2042*fg = GetSysColor(COLOR_WINDOWTEXT);2043} else if (*fg == RGB(128, 128, 128)) {2044*fg = GetSysColor(COLOR_GRAYTEXT);2045}2046if (*bgBrush && *bg == RGB(255, 255, 255)) {2047*bg = GetSysColor(COLOR_WINDOW);2048*bgBrush = GetSysColorBrush(COLOR_WINDOW);2049}2050}20512052BOOL SetControlColor(HWND hWnd, HDC hDC, HBRUSH *brush) {2053for (int i = 0; i < _theme->cControls; ++i) {2054if (_theme->rgControls[i].hWnd != hWnd) {2055continue;2056}20572058DWORD fontId = _theme->rgControls[i].dwFontId;2059if (fontId > _theme->cFonts) {2060fontId = 0;2061}2062THEME_FONT *fnt = &_theme->rgFonts[fontId];20632064COLORREF fg = fnt->crForeground, bg = fnt->crBackground;2065*brush = fnt->hBackground;2066RemapColor(&fg, &bg, brush);2067::SetTextColor(hDC, fg);2068::SetBkColor(hDC, bg);20692070return TRUE;2071}2072return FALSE;2073}20742075//2076// OnShowFailure - display the failure page.2077//2078void OnShowFailure() {2079SetState(PYBA_STATE_FAILED, S_OK);20802081// If the UI should be visible, display it now and hide the splash screen2082if (BOOTSTRAPPER_DISPLAY_NONE < _command.display) {2083::ShowWindow(_theme->hwndParent, SW_SHOW);2084}20852086_engine->CloseSplashScreen();20872088return;2089}209020912092//2093// OnShowHelp - display the help page.2094//2095void OnShowHelp() {2096SetState(PYBA_STATE_HELP, S_OK);20972098// If the UI should be visible, display it now and hide the splash screen2099if (BOOTSTRAPPER_DISPLAY_NONE < _command.display) {2100::ShowWindow(_theme->hwndParent, SW_SHOW);2101}21022103_engine->CloseSplashScreen();21042105return;2106}210721082109//2110// OnDetect - start the processing of packages.2111//2112void OnDetect() {2113HRESULT hr = S_OK;21142115if (_baFunction) {2116BalLog(BOOTSTRAPPER_LOG_LEVEL_STANDARD, "Running detect BA function");2117hr = _baFunction->OnDetect();2118BalExitOnFailure(hr, "Failed calling detect BA function.");2119}21202121SetState(PYBA_STATE_DETECTING, hr);21222123// If the UI should be visible, display it now and hide the splash screen2124if (BOOTSTRAPPER_DISPLAY_NONE < _command.display) {2125::ShowWindow(_theme->hwndParent, SW_SHOW);2126}21272128_engine->CloseSplashScreen();21292130// Tell the core we're ready for the packages to be processed now.2131hr = _engine->Detect();2132BalExitOnFailure(hr, "Failed to start detecting chain.");21332134LExit:2135if (FAILED(hr)) {2136SetState(PYBA_STATE_DETECTING, hr);2137}21382139return;2140}21412142HRESULT UpdateUIStrings(__in BOOTSTRAPPER_ACTION action) {2143HRESULT hr = S_OK;2144LPCWSTR likeInstalling = nullptr;2145LPCWSTR likeInstallation = nullptr;2146switch (action) {2147case BOOTSTRAPPER_ACTION_INSTALL:2148likeInstalling = L"Installing";2149likeInstallation = L"Installation";2150break;2151case BOOTSTRAPPER_ACTION_MODIFY:2152// For modify, we actually want to pass INSTALL2153action = BOOTSTRAPPER_ACTION_INSTALL;2154likeInstalling = L"Modifying";2155likeInstallation = L"Modification";2156break;2157case BOOTSTRAPPER_ACTION_REPAIR:2158likeInstalling = L"Repairing";2159likeInstallation = L"Repair";2160break;2161case BOOTSTRAPPER_ACTION_UNINSTALL:2162likeInstalling = L"Uninstalling";2163likeInstallation = L"Uninstallation";2164break;2165}21662167if (likeInstalling) {2168LPWSTR locName = nullptr;2169LOC_STRING *locText = nullptr;2170hr = StrAllocFormatted(&locName, L"#(loc.%ls)", likeInstalling);2171if (SUCCEEDED(hr)) {2172hr = LocGetString(_wixLoc, locName, &locText);2173ReleaseStr(locName);2174}2175_engine->SetVariableString(2176L"ActionLikeInstalling",2177SUCCEEDED(hr) && locText ? locText->wzText : likeInstalling2178);2179}21802181if (likeInstallation) {2182LPWSTR locName = nullptr;2183LOC_STRING *locText = nullptr;2184hr = StrAllocFormatted(&locName, L"#(loc.%ls)", likeInstallation);2185if (SUCCEEDED(hr)) {2186hr = LocGetString(_wixLoc, locName, &locText);2187ReleaseStr(locName);2188}2189_engine->SetVariableString(2190L"ActionLikeInstallation",2191SUCCEEDED(hr) && locText ? locText->wzText : likeInstallation2192);2193}2194return hr;2195}21962197//2198// OnPlan - plan the detected changes.2199//2200void OnPlan(__in BOOTSTRAPPER_ACTION action) {2201HRESULT hr = S_OK;22022203_plannedAction = action;22042205hr = UpdateUIStrings(action);2206BalExitOnFailure(hr, "Failed to update strings");22072208// If we are going to apply a downgrade, bail.2209if (_downgradingOtherVersion && BOOTSTRAPPER_ACTION_UNINSTALL < action) {2210if (_suppressDowngradeFailure) {2211BalLog(BOOTSTRAPPER_LOG_LEVEL_STANDARD, "A newer version of this product is installed but downgrade failure has been suppressed; continuing...");2212} else {2213hr = HRESULT_FROM_WIN32(ERROR_PRODUCT_VERSION);2214BalExitOnFailure(hr, "Cannot install a product when a newer version is installed.");2215}2216}22172218SetState(PYBA_STATE_PLANNING, hr);22192220if (_baFunction) {2221BalLog(BOOTSTRAPPER_LOG_LEVEL_STANDARD, "Running plan BA function");2222_baFunction->OnPlan();2223}22242225hr = _engine->Plan(action);2226BalExitOnFailure(hr, "Failed to start planning packages.");22272228LExit:2229if (FAILED(hr)) {2230SetState(PYBA_STATE_PLANNING, hr);2231}22322233return;2234}223522362237//2238// OnApply - apply the packages.2239//2240void OnApply() {2241HRESULT hr = S_OK;22422243SetState(PYBA_STATE_APPLYING, hr);2244SetProgressState(hr);2245SetTaskbarButtonProgress(0);22462247hr = _engine->Apply(_hWnd);2248BalExitOnFailure(hr, "Failed to start applying packages.");22492250ThemeControlEnable(_theme, ID_PROGRESS_CANCEL_BUTTON, TRUE); // ensure the cancel button is enabled before starting.22512252LExit:2253if (FAILED(hr)) {2254SetState(PYBA_STATE_APPLYING, hr);2255}22562257return;2258}225922602261//2262// OnChangeState - change state.2263//2264void OnChangeState(__in PYBA_STATE state) {2265LPWSTR unformattedText = nullptr;22662267_state = state;22682269// If our install is at the end (success or failure) and we're not showing full UI2270// then exit (prompt for restart if required).2271if ((PYBA_STATE_APPLIED <= _state && BOOTSTRAPPER_DISPLAY_FULL > _command.display)) {2272// If a restart was required but we were not automatically allowed to2273// accept the reboot then do the prompt.2274if (_restartRequired && !_allowRestart) {2275StrAllocFromError(&unformattedText, HRESULT_FROM_WIN32(ERROR_SUCCESS_REBOOT_REQUIRED), nullptr);22762277_allowRestart = IDOK == ::MessageBoxW(2278_hWnd,2279unformattedText ? unformattedText : L"The requested operation is successful. Changes will not be effective until the system is rebooted.",2280_theme->sczCaption,2281MB_ICONEXCLAMATION | MB_OKCANCEL2282);2283}22842285// Quietly exit.2286::PostMessageW(_hWnd, WM_CLOSE, 0, 0);2287} else { // try to change the pages.2288DWORD newPageId = 0;2289DeterminePageId(_state, &newPageId);22902291if (_visiblePageId != newPageId) {2292ShowPage(newPageId);2293}2294}22952296ReleaseStr(unformattedText);2297}22982299//2300// Called before showing a page to handle all controls.2301//2302void ProcessPageControls(THEME_PAGE *pPage) {2303if (!pPage) {2304return;2305}23062307for (DWORD i = 0; i < pPage->cControlIndices; ++i) {2308THEME_CONTROL* pControl = _theme->rgControls + pPage->rgdwControlIndices[i];2309BOOL enableControl = TRUE;23102311// If this is a named control, try to set its default state.2312if (pControl->sczName && *pControl->sczName) {2313// If this is a checkable control, try to set its default state2314// to the state of a matching named Burn variable.2315if (IsCheckable(pControl)) {2316LONGLONG llValue = 0;2317HRESULT hr = BalGetNumericVariable(pControl->sczName, &llValue);23182319// If the control value isn't set then disable it.2320if (!SUCCEEDED(hr)) {2321enableControl = FALSE;2322} else {2323ThemeSendControlMessage(2324_theme,2325pControl->wId,2326BM_SETCHECK,2327SUCCEEDED(hr) && llValue ? BST_CHECKED : BST_UNCHECKED,232802329);2330}2331}23322333// Hide or disable controls based on the control name with 'State' appended2334LPWSTR controlName = nullptr;2335HRESULT hr = StrAllocFormatted(&controlName, L"%lsState", pControl->sczName);2336if (SUCCEEDED(hr)) {2337LPWSTR controlState = nullptr;2338hr = BalGetStringVariable(controlName, &controlState);2339if (SUCCEEDED(hr) && controlState && *controlState) {2340if (controlState[0] == '[') {2341LPWSTR formatted = nullptr;2342if (SUCCEEDED(BalFormatString(controlState, &formatted))) {2343StrFree(controlState);2344controlState = formatted;2345}2346}23472348if (CSTR_EQUAL == ::CompareStringW(LOCALE_NEUTRAL, 0, controlState, -1, L"disable", -1)) {2349BalLog(BOOTSTRAPPER_LOG_LEVEL_STANDARD, "Disable control %ls", pControl->sczName);2350enableControl = FALSE;2351} else if (CSTR_EQUAL == ::CompareStringW(LOCALE_NEUTRAL, 0, controlState, -1, L"hide", -1)) {2352BalLog(BOOTSTRAPPER_LOG_LEVEL_STANDARD, "Hide control %ls", pControl->sczName);2353// TODO: This doesn't work2354ThemeShowControl(_theme, pControl->wId, SW_HIDE);2355} else {2356// An explicit state can override the lack of a2357// backing variable.2358enableControl = TRUE;2359}2360}2361StrFree(controlState);2362}2363StrFree(controlName);2364controlName = nullptr;236523662367// If a command link has a note, then add it.2368if ((pControl->dwStyle & BS_TYPEMASK) == BS_COMMANDLINK ||2369(pControl->dwStyle & BS_TYPEMASK) == BS_DEFCOMMANDLINK) {2370hr = StrAllocFormatted(&controlName, L"#(loc.%lsNote)", pControl->sczName);2371if (SUCCEEDED(hr)) {2372LOC_STRING *locText = nullptr;2373hr = LocGetString(_wixLoc, controlName, &locText);2374if (SUCCEEDED(hr) && locText && locText->wzText && locText->wzText[0]) {2375LPWSTR text = nullptr;2376hr = BalFormatString(locText->wzText, &text);2377if (SUCCEEDED(hr) && text && text[0]) {2378ThemeSendControlMessage(_theme, pControl->wId, BCM_SETNOTE, 0, (LPARAM)text);2379ReleaseStr(text);2380text = nullptr;2381}2382}2383ReleaseStr(controlName);2384controlName = nullptr;2385}2386hr = S_OK;2387}2388}23892390ThemeControlEnable(_theme, pControl->wId, enableControl);23912392// Format the text in each of the new page's controls2393if (pControl->sczText && *pControl->sczText) {2394// If the wix developer is showing a hidden variable2395// in the UI, then obviously they don't care about2396// keeping it safe so don't go down the rabbit hole2397// of making sure that this is securely freed.2398LPWSTR text = nullptr;2399HRESULT hr = BalFormatString(pControl->sczText, &text);2400if (SUCCEEDED(hr)) {2401ThemeSetTextControl(_theme, pControl->wId, text);2402}2403}2404}2405}24062407//2408// OnClose - called when the window is trying to be closed.2409//2410BOOL OnClose() {2411BOOL close = FALSE;24122413// If we've already succeeded or failed or showing the help page, just close (prompts are annoying if the bootstrapper is done).2414if (PYBA_STATE_APPLIED <= _state || PYBA_STATE_HELP == _state) {2415close = TRUE;2416} else {2417// prompt the user or force the cancel if there is no UI.2418close = PromptCancel(2419_hWnd,2420BOOTSTRAPPER_DISPLAY_FULL != _command.display,2421_confirmCloseMessage ? _confirmCloseMessage : L"Are you sure you want to cancel?",2422_theme->sczCaption2423);2424}24252426// If we're doing progress then we never close, we just cancel to let rollback occur.2427if (PYBA_STATE_APPLYING <= _state && PYBA_STATE_APPLIED > _state) {2428// If we canceled disable cancel button since clicking it again is silly.2429if (close) {2430ThemeControlEnable(_theme, ID_PROGRESS_CANCEL_BUTTON, FALSE);2431}24322433close = FALSE;2434}24352436return close;2437}24382439//2440// OnClickCloseButton - close the application.2441//2442void OnClickCloseButton() {2443::SendMessageW(_hWnd, WM_CLOSE, 0, 0);2444}2445244624472448//2449// OnClickRestartButton - allows the restart and closes the app.2450//2451void OnClickRestartButton() {2452AssertSz(_restartRequired, "Restart must be requested to be able to click on the restart button.");24532454_allowRestart = TRUE;2455::SendMessageW(_hWnd, WM_CLOSE, 0, 0);24562457return;2458}245924602461//2462// OnClickLogFileLink - show the log file.2463//2464void OnClickLogFileLink() {2465HRESULT hr = S_OK;2466LPWSTR sczLogFile = nullptr;24672468hr = BalGetStringVariable(_bundle.sczLogVariable, &sczLogFile);2469BalExitOnFailure1(hr, "Failed to get log file variable '%ls'.", _bundle.sczLogVariable);24702471hr = ShelExec(L"notepad.exe", sczLogFile, L"open", nullptr, SW_SHOWDEFAULT, _hWnd, nullptr);2472BalExitOnFailure1(hr, "Failed to open log file target: %ls", sczLogFile);24732474LExit:2475ReleaseStr(sczLogFile);24762477return;2478}247924802481//2482// SetState2483//2484void SetState(__in PYBA_STATE state, __in HRESULT hrStatus) {2485if (FAILED(hrStatus)) {2486_hrFinal = hrStatus;2487}24882489if (FAILED(_hrFinal)) {2490state = PYBA_STATE_FAILED;2491}24922493if (_state != state) {2494::PostMessageW(_hWnd, WM_PYBA_CHANGE_STATE, 0, state);2495}2496}24972498//2499// GoToPage2500//2501void GoToPage(__in PAGE page) {2502_installPage = page;2503::PostMessageW(_hWnd, WM_PYBA_CHANGE_STATE, 0, _state);2504}25052506void DeterminePageId(__in PYBA_STATE state, __out DWORD* pdwPageId) {2507LONGLONG simple;25082509if (BOOTSTRAPPER_DISPLAY_PASSIVE == _command.display) {2510switch (state) {2511case PYBA_STATE_INITIALIZED:2512*pdwPageId = BOOTSTRAPPER_ACTION_HELP == _command.action2513? _pageIds[PAGE_HELP]2514: _pageIds[PAGE_LOADING];2515break;25162517case PYBA_STATE_HELP:2518*pdwPageId = _pageIds[PAGE_HELP];2519break;25202521case PYBA_STATE_DETECTING:2522*pdwPageId = _pageIds[PAGE_LOADING]2523? _pageIds[PAGE_LOADING]2524: _pageIds[PAGE_PROGRESS_PASSIVE]2525? _pageIds[PAGE_PROGRESS_PASSIVE]2526: _pageIds[PAGE_PROGRESS];2527break;25282529case PYBA_STATE_DETECTED: __fallthrough;2530case PYBA_STATE_PLANNING: __fallthrough;2531case PYBA_STATE_PLANNED: __fallthrough;2532case PYBA_STATE_APPLYING: __fallthrough;2533case PYBA_STATE_CACHING: __fallthrough;2534case PYBA_STATE_CACHED: __fallthrough;2535case PYBA_STATE_EXECUTING: __fallthrough;2536case PYBA_STATE_EXECUTED:2537*pdwPageId = _pageIds[PAGE_PROGRESS_PASSIVE]2538? _pageIds[PAGE_PROGRESS_PASSIVE]2539: _pageIds[PAGE_PROGRESS];2540break;25412542default:2543*pdwPageId = 0;2544break;2545}2546} else if (BOOTSTRAPPER_DISPLAY_FULL == _command.display) {2547switch (state) {2548case PYBA_STATE_INITIALIZING:2549*pdwPageId = 0;2550break;25512552case PYBA_STATE_INITIALIZED:2553*pdwPageId = BOOTSTRAPPER_ACTION_HELP == _command.action2554? _pageIds[PAGE_HELP]2555: _pageIds[PAGE_LOADING];2556break;25572558case PYBA_STATE_HELP:2559*pdwPageId = _pageIds[PAGE_HELP];2560break;25612562case PYBA_STATE_DETECTING:2563*pdwPageId = _pageIds[PAGE_LOADING];2564break;25652566case PYBA_STATE_DETECTED:2567if (_installPage == PAGE_LOADING) {2568switch (_command.action) {2569case BOOTSTRAPPER_ACTION_INSTALL:2570if (_upgrading) {2571_installPage = PAGE_UPGRADE;2572} else if (SUCCEEDED(BalGetNumericVariable(L"SimpleInstall", &simple)) && simple) {2573_installPage = PAGE_SIMPLE_INSTALL;2574} else {2575_installPage = PAGE_INSTALL;2576}2577break;25782579case BOOTSTRAPPER_ACTION_MODIFY: __fallthrough;2580case BOOTSTRAPPER_ACTION_REPAIR: __fallthrough;2581case BOOTSTRAPPER_ACTION_UNINSTALL:2582_installPage = PAGE_MODIFY;2583break;2584}2585}2586*pdwPageId = _pageIds[_installPage];2587break;25882589case PYBA_STATE_PLANNING: __fallthrough;2590case PYBA_STATE_PLANNED: __fallthrough;2591case PYBA_STATE_APPLYING: __fallthrough;2592case PYBA_STATE_CACHING: __fallthrough;2593case PYBA_STATE_CACHED: __fallthrough;2594case PYBA_STATE_EXECUTING: __fallthrough;2595case PYBA_STATE_EXECUTED:2596*pdwPageId = _pageIds[PAGE_PROGRESS];2597break;25982599case PYBA_STATE_APPLIED:2600*pdwPageId = _pageIds[PAGE_SUCCESS];2601break;26022603case PYBA_STATE_FAILED:2604*pdwPageId = _pageIds[PAGE_FAILURE];2605break;2606}2607}2608}26092610BOOL WillElevate() {2611static BAL_CONDITION WILL_ELEVATE_CONDITION = {2612L"not WixBundleElevated and ("2613/*Elevate when installing for all users*/2614L"InstallAllUsers or "2615/*Elevate when installing the launcher for all users and it was not detected*/2616L"(Include_launcher and InstallLauncherAllUsers and not DetectedLauncher)"2617L")",2618L""2619};2620BOOL result;26212622return SUCCEEDED(BalConditionEvaluate(&WILL_ELEVATE_CONDITION, _engine, &result, nullptr)) && result;2623}26242625BOOL IsCrtInstalled() {2626if (_crtInstalledToken > 0) {2627return TRUE;2628} else if (_crtInstalledToken == 0) {2629return FALSE;2630}26312632// Check whether at least CRT v10.0.10137.0 is available.2633// It should only be installed as a Windows Update package, which means2634// we don't need to worry about 32-bit/64-bit.2635LPCWSTR crtFile = L"ucrtbase.dll";26362637DWORD cbVer = GetFileVersionInfoSizeW(crtFile, nullptr);2638if (!cbVer) {2639_crtInstalledToken = 0;2640return FALSE;2641}26422643void *pData = malloc(cbVer);2644if (!pData) {2645_crtInstalledToken = 0;2646return FALSE;2647}26482649if (!GetFileVersionInfoW(crtFile, 0, cbVer, pData)) {2650free(pData);2651_crtInstalledToken = 0;2652return FALSE;2653}26542655VS_FIXEDFILEINFO *ffi;2656UINT cb;2657BOOL result = FALSE;26582659if (VerQueryValueW(pData, L"\\", (LPVOID*)&ffi, &cb) &&2660ffi->dwFileVersionMS == 0x000A0000 && ffi->dwFileVersionLS >= 0x27990000) {2661result = TRUE;2662}26632664free(pData);2665_crtInstalledToken = result ? 1 : 0;2666return result;2667}26682669HRESULT EvaluateConditions() {2670HRESULT hr = S_OK;2671BOOL result = FALSE;26722673for (DWORD i = 0; i < _conditions.cConditions; ++i) {2674BAL_CONDITION* pCondition = _conditions.rgConditions + i;26752676hr = BalConditionEvaluate(pCondition, _engine, &result, &_failedMessage);2677BalExitOnFailure(hr, "Failed to evaluate condition.");26782679if (!result) {2680// Hope they didn't have hidden variables in their message, because it's going in the log in plaintext.2681BalLog(BOOTSTRAPPER_LOG_LEVEL_ERROR, "%ls", _failedMessage);26822683hr = E_WIXSTDBA_CONDITION_FAILED;2684// todo: remove in WiX v4, in case people are relying on v3.x logging behavior2685BalExitOnFailure1(hr, "Bundle condition evaluated to false: %ls", pCondition->sczCondition);2686}2687}26882689ReleaseNullStrSecure(_failedMessage);26902691LExit:2692return hr;2693}269426952696void SetTaskbarButtonProgress(__in DWORD dwOverallPercentage) {2697HRESULT hr = S_OK;26982699if (_taskbarButtonOK) {2700hr = _taskbarList->SetProgressValue(_hWnd, dwOverallPercentage, 100UL);2701BalExitOnFailure1(hr, "Failed to set taskbar button progress to: %d%%.", dwOverallPercentage);2702}27032704LExit:2705return;2706}270727082709void SetTaskbarButtonState(__in TBPFLAG tbpFlags) {2710HRESULT hr = S_OK;27112712if (_taskbarButtonOK) {2713hr = _taskbarList->SetProgressState(_hWnd, tbpFlags);2714BalExitOnFailure1(hr, "Failed to set taskbar button state.", tbpFlags);2715}27162717LExit:2718return;2719}272027212722void SetProgressState(__in HRESULT hrStatus) {2723TBPFLAG flag = TBPF_NORMAL;27242725if (IsCanceled() || HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT) == hrStatus) {2726flag = TBPF_PAUSED;2727} else if (IsRollingBack() || FAILED(hrStatus)) {2728flag = TBPF_ERROR;2729}27302731SetTaskbarButtonState(flag);2732}273327342735HRESULT LoadBootstrapperBAFunctions() {2736HRESULT hr = S_OK;2737LPWSTR sczBafPath = nullptr;27382739hr = PathRelativeToModule(&sczBafPath, L"bafunctions.dll", _hModule);2740BalExitOnFailure(hr, "Failed to get path to BA function DLL.");27412742#ifdef DEBUG2743BalLog(BOOTSTRAPPER_LOG_LEVEL_STANDARD, "PYBA: LoadBootstrapperBAFunctions() - BA function DLL %ls", sczBafPath);2744#endif27452746_hBAFModule = ::LoadLibraryW(sczBafPath);2747if (_hBAFModule) {2748auto pfnBAFunctionCreate = reinterpret_cast<PFN_BOOTSTRAPPER_BA_FUNCTION_CREATE>(::GetProcAddress(_hBAFModule, "CreateBootstrapperBAFunction"));2749BalExitOnNullWithLastError1(pfnBAFunctionCreate, hr, "Failed to get CreateBootstrapperBAFunction entry-point from: %ls", sczBafPath);27502751hr = pfnBAFunctionCreate(_engine, _hBAFModule, &_baFunction);2752BalExitOnFailure(hr, "Failed to create BA function.");2753}2754#ifdef DEBUG2755else {2756BalLogError(HRESULT_FROM_WIN32(::GetLastError()), "PYBA: LoadBootstrapperBAFunctions() - Failed to load DLL %ls", sczBafPath);2757}2758#endif27592760LExit:2761if (_hBAFModule && !_baFunction) {2762::FreeLibrary(_hBAFModule);2763_hBAFModule = nullptr;2764}2765ReleaseStr(sczBafPath);27662767return hr;2768}27692770BOOL IsCheckable(THEME_CONTROL* pControl) {2771if (!pControl->sczName || !pControl->sczName[0]) {2772return FALSE;2773}27742775if (pControl->type == THEME_CONTROL_TYPE_CHECKBOX) {2776return TRUE;2777}27782779if (pControl->type == THEME_CONTROL_TYPE_BUTTON) {2780if ((pControl->dwStyle & BS_TYPEMASK) == BS_AUTORADIOBUTTON) {2781return TRUE;2782}2783}27842785return FALSE;2786}27872788void SavePageSettings() {2789DWORD pageId = 0;2790THEME_PAGE* pPage = nullptr;27912792DeterminePageId(_state, &pageId);2793pPage = ThemeGetPage(_theme, pageId);2794if (!pPage) {2795return;2796}27972798for (DWORD i = 0; i < pPage->cControlIndices; ++i) {2799// Loop through all the checkable controls and set a Burn variable2800// with that name to true or false.2801THEME_CONTROL* pControl = _theme->rgControls + pPage->rgdwControlIndices[i];2802if (IsCheckable(pControl) && ThemeControlEnabled(_theme, pControl->wId)) {2803BOOL checked = ThemeIsControlChecked(_theme, pControl->wId);2804_engine->SetVariableNumeric(pControl->sczName, checked ? 1 : 0);2805}28062807// Loop through all the editbox controls with names and set a2808// Burn variable with that name to the contents.2809if (THEME_CONTROL_TYPE_EDITBOX == pControl->type && pControl->sczName && *pControl->sczName) {2810LPWSTR sczValue = nullptr;2811ThemeGetTextControl(_theme, pControl->wId, &sczValue);2812_engine->SetVariableString(pControl->sczName, sczValue);2813}2814}2815}28162817static bool IsTargetPlatformx64(__in IBootstrapperEngine* pEngine) {2818WCHAR platform[8];2819DWORD platformLen = 8;28202821if (FAILED(pEngine->GetVariableString(L"TargetPlatform", platform, &platformLen))) {2822return S_FALSE;2823}28242825return ::CompareStringW(LOCALE_NEUTRAL, 0, platform, -1, L"x64", -1) == CSTR_EQUAL;2826}28272828static bool IsTargetPlatformARM64(__in IBootstrapperEngine* pEngine) {2829WCHAR platform[8];2830DWORD platformLen = 8;28312832if (FAILED(pEngine->GetVariableString(L"TargetPlatform", platform, &platformLen))) {2833return S_FALSE;2834}28352836return ::CompareStringW(LOCALE_NEUTRAL, 0, platform, -1, L"ARM64", -1) == CSTR_EQUAL;2837}28382839static HRESULT LoadOptionalFeatureStatesFromKey(2840__in IBootstrapperEngine* pEngine,2841__in HKEY hkHive,2842__in LPCWSTR subkey2843) {2844HKEY hKey;2845LRESULT res;28462847if (IsTargetPlatformx64(pEngine) || IsTargetPlatformARM64(pEngine)) {2848res = RegOpenKeyExW(hkHive, subkey, 0, KEY_READ | KEY_WOW64_64KEY, &hKey);2849} else {2850res = RegOpenKeyExW(hkHive, subkey, 0, KEY_READ | KEY_WOW64_32KEY, &hKey);2851}2852if (res == ERROR_FILE_NOT_FOUND) {2853return S_FALSE;2854}2855if (res != ERROR_SUCCESS) {2856return HRESULT_FROM_WIN32(res);2857}28582859for (auto p = OPTIONAL_FEATURES; p->regName; ++p) {2860res = RegQueryValueExW(hKey, p->regName, nullptr, nullptr, nullptr, nullptr);2861if (res == ERROR_FILE_NOT_FOUND) {2862pEngine->SetVariableNumeric(p->variableName, 0);2863} else if (res == ERROR_SUCCESS) {2864pEngine->SetVariableNumeric(p->variableName, 1);2865} else {2866RegCloseKey(hKey);2867return HRESULT_FROM_WIN32(res);2868}2869}28702871RegCloseKey(hKey);2872return S_OK;2873}28742875static HRESULT LoadTargetDirFromKey(2876__in IBootstrapperEngine* pEngine,2877__in HKEY hkHive,2878__in LPCWSTR subkey2879) {2880HKEY hKey;2881LRESULT res;2882DWORD dataType;2883BYTE buffer[1024];2884DWORD bufferLen = sizeof(buffer);28852886if (IsTargetPlatformx64(pEngine) || IsTargetPlatformARM64(pEngine)) {2887res = RegOpenKeyExW(hkHive, subkey, 0, KEY_READ | KEY_WOW64_64KEY, &hKey);2888} else {2889res = RegOpenKeyExW(hkHive, subkey, 0, KEY_READ | KEY_WOW64_32KEY, &hKey);2890}2891if (res == ERROR_FILE_NOT_FOUND) {2892return S_FALSE;2893}2894if (res != ERROR_SUCCESS) {2895return HRESULT_FROM_WIN32(res);2896}28972898res = RegQueryValueExW(hKey, nullptr, nullptr, &dataType, buffer, &bufferLen);2899if (res == ERROR_SUCCESS && dataType == REG_SZ && bufferLen < sizeof(buffer)) {2900pEngine->SetVariableString(L"TargetDir", reinterpret_cast<wchar_t*>(buffer));2901}2902RegCloseKey(hKey);2903return HRESULT_FROM_WIN32(res);2904}29052906static HRESULT LoadAssociateFilesStateFromKey(2907__in IBootstrapperEngine* pEngine,2908__in HKEY hkHive2909) {2910const LPCWSTR subkey = L"Software\\Python\\PyLauncher";2911HKEY hKey;2912LRESULT res;2913HRESULT hr;29142915res = RegOpenKeyExW(hkHive, subkey, 0, KEY_READ | KEY_WOW64_32KEY, &hKey);29162917if (res == ERROR_FILE_NOT_FOUND) {2918return S_FALSE;2919}2920if (res != ERROR_SUCCESS) {2921return HRESULT_FROM_WIN32(res);2922}29232924res = RegQueryValueExW(hKey, L"AssociateFiles", nullptr, nullptr, nullptr, nullptr);2925if (res == ERROR_FILE_NOT_FOUND) {2926hr = S_FALSE;2927} else if (res == ERROR_SUCCESS) {2928hr = S_OK;2929} else {2930hr = HRESULT_FROM_WIN32(res);2931}29322933RegCloseKey(hKey);2934return hr;2935}29362937static void LoadOptionalFeatureStates(__in IBootstrapperEngine* pEngine) {2938WCHAR subkeyFmt[256];2939WCHAR subkey[256];2940DWORD subkeyLen;2941HRESULT hr;2942HKEY hkHive;29432944BalLog(BOOTSTRAPPER_LOG_LEVEL_STANDARD, "Loading state of optional features");29452946// Get the registry key from the bundle, to save having to duplicate it2947// in multiple places.2948subkeyLen = sizeof(subkeyFmt) / sizeof(subkeyFmt[0]);2949hr = pEngine->GetVariableString(L"OptionalFeaturesRegistryKey", subkeyFmt, &subkeyLen);2950BalExitOnFailure(hr, "Failed to locate registry key");2951subkeyLen = sizeof(subkey) / sizeof(subkey[0]);2952hr = pEngine->FormatString(subkeyFmt, subkey, &subkeyLen);2953BalExitOnFailure1(hr, "Failed to format %ls", subkeyFmt);29542955// Check the current user's registry for existing features2956hkHive = HKEY_CURRENT_USER;2957hr = LoadOptionalFeatureStatesFromKey(pEngine, hkHive, subkey);2958BalExitOnFailure1(hr, "Failed to read from HKCU\\%ls", subkey);2959if (hr == S_FALSE) {2960// Now check the local machine registry2961hkHive = HKEY_LOCAL_MACHINE;2962hr = LoadOptionalFeatureStatesFromKey(pEngine, hkHive, subkey);2963BalExitOnFailure1(hr, "Failed to read from HKLM\\%ls", subkey);2964if (hr == S_OK) {2965// Found a system-wide install, so enable these settings.2966pEngine->SetVariableNumeric(L"InstallAllUsers", 1);2967pEngine->SetVariableNumeric(L"CompileAll", 1);2968}2969}29702971if (hr == S_OK) {2972// Cannot change InstallAllUsersState when upgrading. While there's2973// no good reason to not allow installing a per-user and an all-user2974// version simultaneously, Burn can't handle the state management2975// and will need to uninstall the old one.2976pEngine->SetVariableString(L"InstallAllUsersState", L"disable");29772978// Get the previous install directory. This can be changed by the2979// user.2980subkeyLen = sizeof(subkeyFmt) / sizeof(subkeyFmt[0]);2981hr = pEngine->GetVariableString(L"TargetDirRegistryKey", subkeyFmt, &subkeyLen);2982BalExitOnFailure(hr, "Failed to locate registry key");2983subkeyLen = sizeof(subkey) / sizeof(subkey[0]);2984hr = pEngine->FormatString(subkeyFmt, subkey, &subkeyLen);2985BalExitOnFailure1(hr, "Failed to format %ls", subkeyFmt);2986LoadTargetDirFromKey(pEngine, hkHive, subkey);2987}29882989LExit:2990return;2991}29922993HRESULT EnsureTargetDir() {2994LONGLONG installAllUsers;2995LPWSTR targetDir = nullptr, defaultDir = nullptr;2996HRESULT hr = BalGetStringVariable(L"TargetDir", &targetDir);2997if (FAILED(hr) || !targetDir || !targetDir[0]) {2998ReleaseStr(targetDir);2999targetDir = nullptr;30003001hr = BalGetNumericVariable(L"InstallAllUsers", &installAllUsers);3002ExitOnFailure(hr, L"Failed to get install scope");30033004hr = BalGetStringVariable(3005installAllUsers ? L"DefaultAllUsersTargetDir" : L"DefaultJustForMeTargetDir",3006&defaultDir3007);3008BalExitOnFailure(hr, "Failed to get the default install directory");30093010if (!defaultDir || !defaultDir[0]) {3011BalLogError(E_INVALIDARG, "Default install directory is blank");3012}30133014hr = BalFormatString(defaultDir, &targetDir);3015BalExitOnFailure1(hr, "Failed to format '%ls'", defaultDir);30163017hr = _engine->SetVariableString(L"TargetDir", targetDir);3018BalExitOnFailure(hr, "Failed to set install target directory");3019}3020LExit:3021ReleaseStr(defaultDir);3022ReleaseStr(targetDir);3023return hr;3024}30253026void ValidateOperatingSystem() {3027LOC_STRING *pLocString = nullptr;30283029if (IsWindowsServer()) {3030if (IsWindowsVersionOrGreater(6, 2, 0)) {3031BalLog(BOOTSTRAPPER_LOG_LEVEL_STANDARD, "Target OS is Windows Server 2012 or later");3032return;3033} else if (IsWindowsVersionOrGreater(6, 1, 1)) {3034BalLog(BOOTSTRAPPER_LOG_LEVEL_STANDARD, "Detected Windows Server 2008 R2");3035} else if (IsWindowsVersionOrGreater(6, 1, 0)) {3036BalLog(BOOTSTRAPPER_LOG_LEVEL_ERROR, "Detected Windows Server 2008 R2");3037} else if (IsWindowsVersionOrGreater(6, 0, 0)) {3038BalLog(BOOTSTRAPPER_LOG_LEVEL_ERROR, "Detected Windows Server 2008");3039} else {3040BalLog(BOOTSTRAPPER_LOG_LEVEL_ERROR, "Detected Windows Server 2003 or earlier");3041}3042BalLog(BOOTSTRAPPER_LOG_LEVEL_ERROR, "Windows Server 2012 or later is required to continue installation");3043} else {3044if (IsWindows10OrGreater()) {3045BalLog(BOOTSTRAPPER_LOG_LEVEL_STANDARD, "Target OS is Windows 10 or later");3046return;3047} else if (IsWindows8Point1OrGreater()) {3048BalLog(BOOTSTRAPPER_LOG_LEVEL_STANDARD, "Target OS is Windows 8.1");3049return;3050} else if (IsWindows8OrGreater()) {3051BalLog(BOOTSTRAPPER_LOG_LEVEL_ERROR, "Detected Windows 8");3052} else if (IsWindows7OrGreater()) {3053BalLog(BOOTSTRAPPER_LOG_LEVEL_ERROR, "Detected Windows 7");3054} else if (IsWindowsVistaOrGreater()) {3055BalLog(BOOTSTRAPPER_LOG_LEVEL_ERROR, "Detected Windows Vista");3056} else {3057BalLog(BOOTSTRAPPER_LOG_LEVEL_ERROR, "Detected Windows XP or earlier");3058}3059BalLog(BOOTSTRAPPER_LOG_LEVEL_ERROR, "Windows 8.1 or later is required to continue installation");3060}30613062LocGetString(_wixLoc, L"#(loc.FailureOldOS)", &pLocString);3063if (pLocString && pLocString->wzText) {3064BalFormatString(pLocString->wzText, &_failedMessage);3065}30663067_hrFinal = E_WIXSTDBA_CONDITION_FAILED;3068}30693070public:3071//3072// Constructor - initialize member variables.3073//3074PythonBootstrapperApplication(3075__in HMODULE hModule,3076__in BOOL fPrereq,3077__in HRESULT hrHostInitialization,3078__in IBootstrapperEngine* pEngine,3079__in const BOOTSTRAPPER_COMMAND* pCommand3080) : CBalBaseBootstrapperApplication(pEngine, pCommand, 3, 3000) {3081_hModule = hModule;3082memcpy_s(&_command, sizeof(_command), pCommand, sizeof(BOOTSTRAPPER_COMMAND));30833084LONGLONG llInstalled = 0;3085HRESULT hr = BalGetNumericVariable(L"WixBundleInstalled", &llInstalled);3086if (SUCCEEDED(hr) && BOOTSTRAPPER_RESUME_TYPE_REBOOT != _command.resumeType && 0 < llInstalled && BOOTSTRAPPER_ACTION_INSTALL == _command.action) {3087_command.action = BOOTSTRAPPER_ACTION_MODIFY;3088} else if (0 == llInstalled && (BOOTSTRAPPER_ACTION_MODIFY == _command.action || BOOTSTRAPPER_ACTION_REPAIR == _command.action)) {3089_command.action = BOOTSTRAPPER_ACTION_INSTALL;3090}30913092_plannedAction = BOOTSTRAPPER_ACTION_UNKNOWN;309330943095// When resuming from restart doing some install-like operation, try to find the package that forced the3096// restart. We'll use this information during planning.3097_nextPackageAfterRestart = nullptr;30983099if (BOOTSTRAPPER_RESUME_TYPE_REBOOT == _command.resumeType && BOOTSTRAPPER_ACTION_UNINSTALL < _command.action) {3100// Ensure the forced restart package variable is null when it is an empty string.3101HRESULT hr = BalGetStringVariable(L"WixBundleForcedRestartPackage", &_nextPackageAfterRestart);3102if (FAILED(hr) || !_nextPackageAfterRestart || !*_nextPackageAfterRestart) {3103ReleaseNullStr(_nextPackageAfterRestart);3104}3105}31063107_crtInstalledToken = -1;3108pEngine->SetVariableNumeric(L"CRTInstalled", IsCrtInstalled() ? 1 : 0);31093110_wixLoc = nullptr;3111memset(&_bundle, 0, sizeof(_bundle));3112memset(&_conditions, 0, sizeof(_conditions));3113_confirmCloseMessage = nullptr;3114_failedMessage = nullptr;31153116_language = nullptr;3117_theme = nullptr;3118memset(_pageIds, 0, sizeof(_pageIds));3119_hUiThread = nullptr;3120_registered = FALSE;3121_hWnd = nullptr;31223123_state = PYBA_STATE_INITIALIZING;3124_visiblePageId = 0;3125_installPage = PAGE_LOADING;3126_hrFinal = hrHostInitialization;31273128_downgradingOtherVersion = FALSE;3129_restartResult = BOOTSTRAPPER_APPLY_RESTART_NONE;3130_restartRequired = FALSE;3131_allowRestart = FALSE;31323133_suppressDowngradeFailure = FALSE;3134_suppressRepair = FALSE;3135_modifying = FALSE;3136_upgrading = FALSE;31373138_overridableVariables = nullptr;3139_taskbarList = nullptr;3140_taskbarButtonCreatedMessage = UINT_MAX;3141_taskbarButtonOK = FALSE;3142_showingInternalUIThisPackage = FALSE;31433144_suppressPaint = FALSE;31453146pEngine->AddRef();3147_engine = pEngine;31483149_hBAFModule = nullptr;3150_baFunction = nullptr;3151}315231533154//3155// Destructor - release member variables.3156//3157~PythonBootstrapperApplication() {3158AssertSz(!::IsWindow(_hWnd), "Window should have been destroyed before destructor.");3159AssertSz(!_theme, "Theme should have been released before destructor.");31603161ReleaseObject(_taskbarList);3162ReleaseDict(_overridableVariables);3163ReleaseStr(_failedMessage);3164ReleaseStr(_confirmCloseMessage);3165BalConditionsUninitialize(&_conditions);3166BalInfoUninitialize(&_bundle);3167LocFree(_wixLoc);31683169ReleaseStr(_language);3170ReleaseStr(_nextPackageAfterRestart);3171ReleaseNullObject(_engine);31723173if (_hBAFModule) {3174::FreeLibrary(_hBAFModule);3175_hBAFModule = nullptr;3176}3177}31783179private:3180HMODULE _hModule;3181BOOTSTRAPPER_COMMAND _command;3182IBootstrapperEngine* _engine;3183BOOTSTRAPPER_ACTION _plannedAction;31843185LPWSTR _nextPackageAfterRestart;31863187WIX_LOCALIZATION* _wixLoc;3188BAL_INFO_BUNDLE _bundle;3189BAL_CONDITIONS _conditions;3190LPWSTR _failedMessage;3191LPWSTR _confirmCloseMessage;31923193LPWSTR _language;3194THEME* _theme;3195DWORD _pageIds[countof(PAGE_NAMES)];3196HANDLE _hUiThread;3197BOOL _registered;3198HWND _hWnd;31993200PYBA_STATE _state;3201HRESULT _hrFinal;3202DWORD _visiblePageId;3203PAGE _installPage;32043205BOOL _startedExecution;3206DWORD _calculatedCacheProgress;3207DWORD _calculatedExecuteProgress;32083209BOOL _downgradingOtherVersion;3210BOOTSTRAPPER_APPLY_RESTART _restartResult;3211BOOL _restartRequired;3212BOOL _allowRestart;32133214BOOL _suppressDowngradeFailure;3215BOOL _suppressRepair;3216BOOL _modifying;3217BOOL _upgrading;32183219int _crtInstalledToken;32203221STRINGDICT_HANDLE _overridableVariables;32223223ITaskbarList3* _taskbarList;3224UINT _taskbarButtonCreatedMessage;3225BOOL _taskbarButtonOK;3226BOOL _showingInternalUIThisPackage;32273228BOOL _suppressPaint;32293230HMODULE _hBAFModule;3231IBootstrapperBAFunction* _baFunction;3232};32333234//3235// CreateBootstrapperApplication - creates a new IBootstrapperApplication object.3236//3237HRESULT CreateBootstrapperApplication(3238__in HMODULE hModule,3239__in BOOL fPrereq,3240__in HRESULT hrHostInitialization,3241__in IBootstrapperEngine* pEngine,3242__in const BOOTSTRAPPER_COMMAND* pCommand,3243__out IBootstrapperApplication** ppApplication3244) {3245HRESULT hr = S_OK;32463247if (fPrereq) {3248hr = E_INVALIDARG;3249ExitWithLastError(hr, "Failed to create UI thread.");3250}32513252PythonBootstrapperApplication* pApplication = nullptr;32533254pApplication = new PythonBootstrapperApplication(hModule, fPrereq, hrHostInitialization, pEngine, pCommand);3255ExitOnNull(pApplication, hr, E_OUTOFMEMORY, "Failed to create new standard bootstrapper application object.");32563257*ppApplication = pApplication;3258pApplication = nullptr;32593260LExit:3261ReleaseObject(pApplication);3262return hr;3263}326432653266