summaryrefslogtreecommitdiffstats
path: root/Tools/msi/bundle/bootstrap/PythonBootstrapperApplication.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'Tools/msi/bundle/bootstrap/PythonBootstrapperApplication.cpp')
-rw-r--r--Tools/msi/bundle/bootstrap/PythonBootstrapperApplication.cpp3273
1 files changed, 0 insertions, 3273 deletions
diff --git a/Tools/msi/bundle/bootstrap/PythonBootstrapperApplication.cpp b/Tools/msi/bundle/bootstrap/PythonBootstrapperApplication.cpp
deleted file mode 100644
index fbdf7b6..0000000
--- a/Tools/msi/bundle/bootstrap/PythonBootstrapperApplication.cpp
+++ /dev/null
@@ -1,3273 +0,0 @@
-//-------------------------------------------------------------------------------------------------
-// <copyright file="WixStandardBootstrapperApplication.cpp" company="Outercurve Foundation">
-// Copyright (c) 2004, Outercurve Foundation.
-// This software is released under Microsoft Reciprocal License (MS-RL).
-// The license and further copyright text can be found in the file
-// LICENSE.TXT at the root directory of the distribution.
-// </copyright>
-//-------------------------------------------------------------------------------------------------
-
-
-#include "pch.h"
-
-static const LPCWSTR PYBA_WINDOW_CLASS = L"PythonBA";
-static const DWORD PYBA_ACQUIRE_PERCENTAGE = 30;
-static const LPCWSTR PYBA_VARIABLE_BUNDLE_FILE_VERSION = L"WixBundleFileVersion";
-
-enum PYBA_STATE {
- PYBA_STATE_INITIALIZING,
- PYBA_STATE_INITIALIZED,
- PYBA_STATE_HELP,
- PYBA_STATE_DETECTING,
- PYBA_STATE_DETECTED,
- PYBA_STATE_PLANNING,
- PYBA_STATE_PLANNED,
- PYBA_STATE_APPLYING,
- PYBA_STATE_CACHING,
- PYBA_STATE_CACHED,
- PYBA_STATE_EXECUTING,
- PYBA_STATE_EXECUTED,
- PYBA_STATE_APPLIED,
- PYBA_STATE_FAILED,
-};
-
-static const int WM_PYBA_SHOW_HELP = WM_APP + 100;
-static const int WM_PYBA_DETECT_PACKAGES = WM_APP + 101;
-static const int WM_PYBA_PLAN_PACKAGES = WM_APP + 102;
-static const int WM_PYBA_APPLY_PACKAGES = WM_APP + 103;
-static const int WM_PYBA_CHANGE_STATE = WM_APP + 104;
-static const int WM_PYBA_SHOW_FAILURE = WM_APP + 105;
-
-// This enum must be kept in the same order as the PAGE_NAMES array.
-enum PAGE {
- PAGE_LOADING,
- PAGE_HELP,
- PAGE_INSTALL,
- PAGE_UPGRADE,
- PAGE_SIMPLE_INSTALL,
- PAGE_CUSTOM1,
- PAGE_CUSTOM2,
- PAGE_MODIFY,
- PAGE_PROGRESS,
- PAGE_PROGRESS_PASSIVE,
- PAGE_SUCCESS,
- PAGE_FAILURE,
- COUNT_PAGE,
-};
-
-// This array must be kept in the same order as the PAGE enum.
-static LPCWSTR PAGE_NAMES[] = {
- L"Loading",
- L"Help",
- L"Install",
- L"Upgrade",
- L"SimpleInstall",
- L"Custom1",
- L"Custom2",
- L"Modify",
- L"Progress",
- L"ProgressPassive",
- L"Success",
- L"Failure",
-};
-
-enum CONTROL_ID {
- // Non-paged controls
- ID_CLOSE_BUTTON = THEME_FIRST_ASSIGN_CONTROL_ID,
- ID_MINIMIZE_BUTTON,
-
- // Welcome page
- ID_INSTALL_BUTTON,
- ID_INSTALL_CUSTOM_BUTTON,
- ID_INSTALL_SIMPLE_BUTTON,
- ID_INSTALL_UPGRADE_BUTTON,
- ID_INSTALL_UPGRADE_CUSTOM_BUTTON,
- ID_INSTALL_CANCEL_BUTTON,
- ID_INSTALL_LAUNCHER_ALL_USERS_CHECKBOX,
-
- // Customize Page
- ID_TARGETDIR_EDITBOX,
- ID_CUSTOM_ASSOCIATE_FILES_CHECKBOX,
- ID_CUSTOM_INSTALL_ALL_USERS_CHECKBOX,
- ID_CUSTOM_INSTALL_LAUNCHER_ALL_USERS_CHECKBOX,
- ID_CUSTOM_INCLUDE_LAUNCHER_HELP_LABEL,
- ID_CUSTOM_COMPILE_ALL_CHECKBOX,
- ID_CUSTOM_BROWSE_BUTTON,
- ID_CUSTOM_BROWSE_BUTTON_LABEL,
- ID_CUSTOM_INSTALL_BUTTON,
- ID_CUSTOM_NEXT_BUTTON,
- ID_CUSTOM1_BACK_BUTTON,
- ID_CUSTOM2_BACK_BUTTON,
- ID_CUSTOM1_CANCEL_BUTTON,
- ID_CUSTOM2_CANCEL_BUTTON,
-
- // Modify page
- ID_MODIFY_BUTTON,
- ID_REPAIR_BUTTON,
- ID_UNINSTALL_BUTTON,
- ID_MODIFY_CANCEL_BUTTON,
-
- // Progress page
- ID_CACHE_PROGRESS_PACKAGE_TEXT,
- ID_CACHE_PROGRESS_BAR,
- ID_CACHE_PROGRESS_TEXT,
-
- ID_EXECUTE_PROGRESS_PACKAGE_TEXT,
- ID_EXECUTE_PROGRESS_BAR,
- ID_EXECUTE_PROGRESS_TEXT,
- ID_EXECUTE_PROGRESS_ACTIONDATA_TEXT,
-
- ID_OVERALL_PROGRESS_PACKAGE_TEXT,
- ID_OVERALL_PROGRESS_BAR,
- ID_OVERALL_CALCULATED_PROGRESS_BAR,
- ID_OVERALL_PROGRESS_TEXT,
-
- ID_PROGRESS_CANCEL_BUTTON,
-
- // Success page
- ID_SUCCESS_TEXT,
- ID_SUCCESS_RESTART_TEXT,
- ID_SUCCESS_RESTART_BUTTON,
- ID_SUCCESS_CANCEL_BUTTON,
- ID_SUCCESS_MAX_PATH_BUTTON,
-
- // Failure page
- ID_FAILURE_LOGFILE_LINK,
- ID_FAILURE_MESSAGE_TEXT,
- ID_FAILURE_RESTART_TEXT,
- ID_FAILURE_RESTART_BUTTON,
- ID_FAILURE_CANCEL_BUTTON
-};
-
-static THEME_ASSIGN_CONTROL_ID CONTROL_ID_NAMES[] = {
- { ID_CLOSE_BUTTON, L"CloseButton" },
- { ID_MINIMIZE_BUTTON, L"MinimizeButton" },
-
- { ID_INSTALL_BUTTON, L"InstallButton" },
- { ID_INSTALL_CUSTOM_BUTTON, L"InstallCustomButton" },
- { ID_INSTALL_SIMPLE_BUTTON, L"InstallSimpleButton" },
- { ID_INSTALL_UPGRADE_BUTTON, L"InstallUpgradeButton" },
- { ID_INSTALL_UPGRADE_CUSTOM_BUTTON, L"InstallUpgradeCustomButton" },
- { ID_INSTALL_CANCEL_BUTTON, L"InstallCancelButton" },
- { ID_INSTALL_LAUNCHER_ALL_USERS_CHECKBOX, L"InstallLauncherAllUsers" },
-
- { ID_TARGETDIR_EDITBOX, L"TargetDir" },
- { ID_CUSTOM_ASSOCIATE_FILES_CHECKBOX, L"AssociateFiles" },
- { ID_CUSTOM_INSTALL_ALL_USERS_CHECKBOX, L"InstallAllUsers" },
- { ID_CUSTOM_INSTALL_LAUNCHER_ALL_USERS_CHECKBOX, L"CustomInstallLauncherAllUsers" },
- { ID_CUSTOM_INCLUDE_LAUNCHER_HELP_LABEL, L"Include_launcherHelp" },
- { ID_CUSTOM_COMPILE_ALL_CHECKBOX, L"CompileAll" },
- { ID_CUSTOM_BROWSE_BUTTON, L"CustomBrowseButton" },
- { ID_CUSTOM_BROWSE_BUTTON_LABEL, L"CustomBrowseButtonLabel" },
- { ID_CUSTOM_INSTALL_BUTTON, L"CustomInstallButton" },
- { ID_CUSTOM_NEXT_BUTTON, L"CustomNextButton" },
- { ID_CUSTOM1_BACK_BUTTON, L"Custom1BackButton" },
- { ID_CUSTOM2_BACK_BUTTON, L"Custom2BackButton" },
- { ID_CUSTOM1_CANCEL_BUTTON, L"Custom1CancelButton" },
- { ID_CUSTOM2_CANCEL_BUTTON, L"Custom2CancelButton" },
-
- { ID_MODIFY_BUTTON, L"ModifyButton" },
- { ID_REPAIR_BUTTON, L"RepairButton" },
- { ID_UNINSTALL_BUTTON, L"UninstallButton" },
- { ID_MODIFY_CANCEL_BUTTON, L"ModifyCancelButton" },
-
- { ID_CACHE_PROGRESS_PACKAGE_TEXT, L"CacheProgressPackageText" },
- { ID_CACHE_PROGRESS_BAR, L"CacheProgressbar" },
- { ID_CACHE_PROGRESS_TEXT, L"CacheProgressText" },
- { ID_EXECUTE_PROGRESS_PACKAGE_TEXT, L"ExecuteProgressPackageText" },
- { ID_EXECUTE_PROGRESS_BAR, L"ExecuteProgressbar" },
- { ID_EXECUTE_PROGRESS_TEXT, L"ExecuteProgressText" },
- { ID_EXECUTE_PROGRESS_ACTIONDATA_TEXT, L"ExecuteProgressActionDataText" },
- { ID_OVERALL_PROGRESS_PACKAGE_TEXT, L"OverallProgressPackageText" },
- { ID_OVERALL_PROGRESS_BAR, L"OverallProgressbar" },
- { ID_OVERALL_CALCULATED_PROGRESS_BAR, L"OverallCalculatedProgressbar" },
- { ID_OVERALL_PROGRESS_TEXT, L"OverallProgressText" },
- { ID_PROGRESS_CANCEL_BUTTON, L"ProgressCancelButton" },
-
- { ID_SUCCESS_TEXT, L"SuccessText" },
- { ID_SUCCESS_RESTART_TEXT, L"SuccessRestartText" },
- { ID_SUCCESS_RESTART_BUTTON, L"SuccessRestartButton" },
- { ID_SUCCESS_CANCEL_BUTTON, L"SuccessCancelButton" },
- { ID_SUCCESS_MAX_PATH_BUTTON, L"SuccessMaxPathButton" },
-
- { ID_FAILURE_LOGFILE_LINK, L"FailureLogFileLink" },
- { ID_FAILURE_MESSAGE_TEXT, L"FailureMessageText" },
- { ID_FAILURE_RESTART_TEXT, L"FailureRestartText" },
- { ID_FAILURE_RESTART_BUTTON, L"FailureRestartButton" },
- { ID_FAILURE_CANCEL_BUTTON, L"FailureCancelButton" },
-};
-
-static struct { LPCWSTR regName; LPCWSTR variableName; } OPTIONAL_FEATURES[] = {
- { L"core_d", L"Include_debug" },
- { L"core_pdb", L"Include_symbols" },
- { L"dev", L"Include_dev" },
- { L"doc", L"Include_doc" },
- { L"exe", L"Include_exe" },
- { L"lib", L"Include_lib" },
- { L"path", L"PrependPath" },
- { L"pip", L"Include_pip" },
- { L"tcltk", L"Include_tcltk" },
- { L"test", L"Include_test" },
- { L"tools", L"Include_tools" },
- { L"Shortcuts", L"Shortcuts" },
- // Include_launcher and AssociateFiles are handled separately and so do
- // not need to be included in this list.
- { nullptr, nullptr }
-};
-
-
-
-class PythonBootstrapperApplication : public CBalBaseBootstrapperApplication {
- void ShowPage(DWORD newPageId) {
- // Process each control for special handling in the new page.
- ProcessPageControls(ThemeGetPage(_theme, newPageId));
-
- // Enable disable controls per-page.
- if (_pageIds[PAGE_INSTALL] == newPageId ||
- _pageIds[PAGE_SIMPLE_INSTALL] == newPageId ||
- _pageIds[PAGE_UPGRADE] == newPageId) {
- InstallPage_Show();
- } else if (_pageIds[PAGE_CUSTOM1] == newPageId) {
- Custom1Page_Show();
- } else if (_pageIds[PAGE_CUSTOM2] == newPageId) {
- Custom2Page_Show();
- } else if (_pageIds[PAGE_MODIFY] == newPageId) {
- ModifyPage_Show();
- } else if (_pageIds[PAGE_SUCCESS] == newPageId) {
- SuccessPage_Show();
- } else if (_pageIds[PAGE_FAILURE] == newPageId) {
- FailurePage_Show();
- }
-
- // Prevent repainting while switching page to avoid ugly flickering
- _suppressPaint = TRUE;
- ThemeShowPage(_theme, newPageId, SW_SHOW);
- ThemeShowPage(_theme, _visiblePageId, SW_HIDE);
- _suppressPaint = FALSE;
- InvalidateRect(_theme->hwndParent, nullptr, TRUE);
- _visiblePageId = newPageId;
-
- // On the install page set the focus to the install button or
- // the next enabled control if install is disabled
- if (_pageIds[PAGE_INSTALL] == newPageId) {
- ThemeSetFocus(_theme, ID_INSTALL_BUTTON);
- } else if (_pageIds[PAGE_SIMPLE_INSTALL] == newPageId) {
- ThemeSetFocus(_theme, ID_INSTALL_SIMPLE_BUTTON);
- }
- }
-
- //
- // Handles control clicks
- //
- void OnCommand(CONTROL_ID id) {
- LPWSTR defaultDir = nullptr;
- LPWSTR targetDir = nullptr;
- LONGLONG elevated, crtInstalled, installAllUsers;
- BOOL checked, launcherChecked;
- WCHAR wzPath[MAX_PATH] = { };
- BROWSEINFOW browseInfo = { };
- PIDLIST_ABSOLUTE pidl = nullptr;
- DWORD pageId;
- HRESULT hr = S_OK;
-
- switch(id) {
- case ID_CLOSE_BUTTON:
- OnClickCloseButton();
- break;
-
- // Install commands
- case ID_INSTALL_SIMPLE_BUTTON: __fallthrough;
- case ID_INSTALL_UPGRADE_BUTTON: __fallthrough;
- case ID_INSTALL_BUTTON:
- SavePageSettings();
-
- hr = BalGetNumericVariable(L"InstallAllUsers", &installAllUsers);
- ExitOnFailure(hr, L"Failed to get install scope");
-
- hr = _engine->SetVariableNumeric(L"CompileAll", installAllUsers);
- ExitOnFailure(hr, L"Failed to update CompileAll");
-
- hr = EnsureTargetDir();
- ExitOnFailure(hr, L"Failed to set TargetDir");
-
- OnPlan(BOOTSTRAPPER_ACTION_INSTALL);
- break;
-
- case ID_CUSTOM1_BACK_BUTTON:
- SavePageSettings();
- if (_modifying) {
- GoToPage(PAGE_MODIFY);
- } else if (_upgrading) {
- GoToPage(PAGE_UPGRADE);
- } else {
- GoToPage(PAGE_INSTALL);
- }
- break;
-
- case ID_INSTALL_CUSTOM_BUTTON: __fallthrough;
- case ID_INSTALL_UPGRADE_CUSTOM_BUTTON: __fallthrough;
- case ID_CUSTOM2_BACK_BUTTON:
- SavePageSettings();
- GoToPage(PAGE_CUSTOM1);
- break;
-
- case ID_CUSTOM_NEXT_BUTTON:
- SavePageSettings();
- GoToPage(PAGE_CUSTOM2);
- break;
-
- case ID_CUSTOM_INSTALL_BUTTON:
- SavePageSettings();
-
- hr = EnsureTargetDir();
- ExitOnFailure(hr, L"Failed to set TargetDir");
-
- hr = BalGetStringVariable(L"TargetDir", &targetDir);
- if (SUCCEEDED(hr)) {
- // TODO: Check whether directory exists and contains another installation
- ReleaseStr(targetDir);
- }
-
- OnPlan(_command.action);
- break;
-
- case ID_INSTALL_LAUNCHER_ALL_USERS_CHECKBOX:
- checked = ThemeIsControlChecked(_theme, ID_INSTALL_LAUNCHER_ALL_USERS_CHECKBOX);
- _engine->SetVariableNumeric(L"InstallLauncherAllUsers", checked);
-
- ThemeControlElevates(_theme, ID_INSTALL_BUTTON, WillElevate());
- break;
-
- case ID_CUSTOM_INSTALL_LAUNCHER_ALL_USERS_CHECKBOX:
- checked = ThemeIsControlChecked(_theme, ID_CUSTOM_INSTALL_LAUNCHER_ALL_USERS_CHECKBOX);
- _engine->SetVariableNumeric(L"InstallLauncherAllUsers", checked);
-
- ThemeControlElevates(_theme, ID_CUSTOM_INSTALL_BUTTON, WillElevate());
- break;
-
- case ID_CUSTOM_INSTALL_ALL_USERS_CHECKBOX:
- checked = ThemeIsControlChecked(_theme, ID_CUSTOM_INSTALL_ALL_USERS_CHECKBOX);
- _engine->SetVariableNumeric(L"InstallAllUsers", checked);
-
- ThemeControlElevates(_theme, ID_CUSTOM_INSTALL_BUTTON, WillElevate());
- ThemeControlEnable(_theme, ID_CUSTOM_BROWSE_BUTTON_LABEL, !checked);
- if (checked) {
- _engine->SetVariableNumeric(L"CompileAll", 1);
- ThemeSendControlMessage(_theme, ID_CUSTOM_COMPILE_ALL_CHECKBOX, BM_SETCHECK, BST_CHECKED, 0);
- }
- ThemeGetTextControl(_theme, ID_TARGETDIR_EDITBOX, &targetDir);
- if (targetDir) {
- // Check the current value against the default to see
- // if we should switch it automatically.
- hr = BalGetStringVariable(
- checked ? L"DefaultJustForMeTargetDir" : L"DefaultAllUsersTargetDir",
- &defaultDir
- );
-
- if (SUCCEEDED(hr) && defaultDir) {
- LPWSTR formatted = nullptr;
- if (defaultDir[0] && SUCCEEDED(BalFormatString(defaultDir, &formatted))) {
- if (wcscmp(formatted, targetDir) == 0) {
- ReleaseStr(defaultDir);
- defaultDir = nullptr;
- ReleaseStr(formatted);
- formatted = nullptr;
-
- hr = BalGetStringVariable(
- checked ? L"DefaultAllUsersTargetDir" : L"DefaultJustForMeTargetDir",
- &defaultDir
- );
- if (SUCCEEDED(hr) && defaultDir && defaultDir[0] && SUCCEEDED(BalFormatString(defaultDir, &formatted))) {
- ThemeSetTextControl(_theme, ID_TARGETDIR_EDITBOX, formatted);
- ReleaseStr(formatted);
- }
- } else {
- ReleaseStr(formatted);
- }
- }
-
- ReleaseStr(defaultDir);
- }
- }
- break;
-
- case ID_CUSTOM_BROWSE_BUTTON:
- browseInfo.hwndOwner = _hWnd;
- browseInfo.pszDisplayName = wzPath;
- browseInfo.lpszTitle = _theme->sczCaption;
- browseInfo.ulFlags = BIF_RETURNONLYFSDIRS | BIF_USENEWUI;
- pidl = ::SHBrowseForFolderW(&browseInfo);
- if (pidl && ::SHGetPathFromIDListW(pidl, wzPath)) {
- ThemeSetTextControl(_theme, ID_TARGETDIR_EDITBOX, wzPath);
- }
-
- if (pidl) {
- ::CoTaskMemFree(pidl);
- }
- break;
-
- // Modify commands
- case ID_MODIFY_BUTTON:
- // Some variables cannot be modified
- _engine->SetVariableString(L"InstallAllUsersState", L"disable");
- _engine->SetVariableString(L"InstallLauncherAllUsersState", L"disable");
- _engine->SetVariableString(L"TargetDirState", L"disable");
- _engine->SetVariableString(L"CustomBrowseButtonState", L"disable");
- _modifying = TRUE;
- GoToPage(PAGE_CUSTOM1);
- break;
-
- case ID_REPAIR_BUTTON:
- OnPlan(BOOTSTRAPPER_ACTION_REPAIR);
- break;
-
- case ID_UNINSTALL_BUTTON:
- OnPlan(BOOTSTRAPPER_ACTION_UNINSTALL);
- break;
-
- case ID_SUCCESS_MAX_PATH_BUTTON:
- EnableMaxPathSupport();
- ThemeControlEnable(_theme, ID_SUCCESS_MAX_PATH_BUTTON, FALSE);
- break;
- }
-
- LExit:
- return;
- }
-
- void InstallPage_Show() {
- // Ensure the All Users install button has a UAC shield
- BOOL elevated = WillElevate();
- ThemeControlElevates(_theme, ID_INSTALL_BUTTON, elevated);
- ThemeControlElevates(_theme, ID_INSTALL_SIMPLE_BUTTON, elevated);
- ThemeControlElevates(_theme, ID_INSTALL_UPGRADE_BUTTON, elevated);
- }
-
- void Custom1Page_Show() {
- LONGLONG installLauncherAllUsers;
-
- if (FAILED(BalGetNumericVariable(L"InstallLauncherAllUsers", &installLauncherAllUsers))) {
- installLauncherAllUsers = 0;
- }
-
- ThemeSendControlMessage(_theme, ID_CUSTOM_INSTALL_LAUNCHER_ALL_USERS_CHECKBOX, BM_SETCHECK,
- installLauncherAllUsers ? BST_CHECKED : BST_UNCHECKED, 0);
-
- LOC_STRING *pLocString = nullptr;
- LPCWSTR locKey = L"#(loc.Include_launcherHelp)";
- LONGLONG detectedLauncher;
-
- if (SUCCEEDED(BalGetNumericVariable(L"DetectedLauncher", &detectedLauncher)) && detectedLauncher) {
- locKey = L"#(loc.Include_launcherRemove)";
- } else if (SUCCEEDED(BalGetNumericVariable(L"DetectedOldLauncher", &detectedLauncher)) && detectedLauncher) {
- locKey = L"#(loc.Include_launcherUpgrade)";
- }
-
- if (SUCCEEDED(LocGetString(_wixLoc, locKey, &pLocString)) && pLocString) {
- ThemeSetTextControl(_theme, ID_CUSTOM_INCLUDE_LAUNCHER_HELP_LABEL, pLocString->wzText);
- }
- }
-
- void Custom2Page_Show() {
- HRESULT hr;
- LONGLONG installAll, includeLauncher;
-
- if (FAILED(BalGetNumericVariable(L"InstallAllUsers", &installAll))) {
- installAll = 0;
- }
-
- if (WillElevate()) {
- ThemeControlElevates(_theme, ID_CUSTOM_INSTALL_BUTTON, TRUE);
- ThemeShowControl(_theme, ID_CUSTOM_BROWSE_BUTTON_LABEL, SW_HIDE);
- } else {
- ThemeControlElevates(_theme, ID_CUSTOM_INSTALL_BUTTON, FALSE);
- ThemeShowControl(_theme, ID_CUSTOM_BROWSE_BUTTON_LABEL, SW_SHOW);
- }
-
- if (SUCCEEDED(BalGetNumericVariable(L"Include_launcher", &includeLauncher)) && includeLauncher) {
- ThemeControlEnable(_theme, ID_CUSTOM_ASSOCIATE_FILES_CHECKBOX, TRUE);
- } else {
- ThemeSendControlMessage(_theme, ID_CUSTOM_ASSOCIATE_FILES_CHECKBOX, BM_SETCHECK, BST_UNCHECKED, 0);
- ThemeControlEnable(_theme, ID_CUSTOM_ASSOCIATE_FILES_CHECKBOX, FALSE);
- }
-
- LPWSTR targetDir = nullptr;
- hr = BalGetStringVariable(L"TargetDir", &targetDir);
- if (SUCCEEDED(hr) && targetDir && targetDir[0]) {
- ThemeSetTextControl(_theme, ID_TARGETDIR_EDITBOX, targetDir);
- StrFree(targetDir);
- } else if (SUCCEEDED(hr)) {
- StrFree(targetDir);
- targetDir = nullptr;
-
- LPWSTR defaultTargetDir = nullptr;
- hr = BalGetStringVariable(L"DefaultCustomTargetDir", &defaultTargetDir);
- if (SUCCEEDED(hr) && defaultTargetDir && !defaultTargetDir[0]) {
- StrFree(defaultTargetDir);
- defaultTargetDir = nullptr;
-
- hr = BalGetStringVariable(
- installAll ? L"DefaultAllUsersTargetDir" : L"DefaultJustForMeTargetDir",
- &defaultTargetDir
- );
- }
- if (SUCCEEDED(hr) && defaultTargetDir) {
- if (defaultTargetDir[0] && SUCCEEDED(BalFormatString(defaultTargetDir, &targetDir))) {
- ThemeSetTextControl(_theme, ID_TARGETDIR_EDITBOX, targetDir);
- StrFree(targetDir);
- }
- StrFree(defaultTargetDir);
- }
- }
- }
-
- void ModifyPage_Show() {
- ThemeControlEnable(_theme, ID_REPAIR_BUTTON, !_suppressRepair);
- }
-
- void SuccessPage_Show() {
- // on the "Success" page, check if the restart button should be enabled.
- BOOL showRestartButton = FALSE;
- LOC_STRING *successText = nullptr;
- HRESULT hr = S_OK;
-
- if (_restartRequired) {
- if (BOOTSTRAPPER_RESTART_PROMPT == _command.restart) {
- showRestartButton = TRUE;
- }
- }
-
- switch (_plannedAction) {
- case BOOTSTRAPPER_ACTION_INSTALL:
- hr = LocGetString(_wixLoc, L"#(loc.SuccessInstallMessage)", &successText);
- break;
- case BOOTSTRAPPER_ACTION_MODIFY:
- hr = LocGetString(_wixLoc, L"#(loc.SuccessModifyMessage)", &successText);
- break;
- case BOOTSTRAPPER_ACTION_REPAIR:
- hr = LocGetString(_wixLoc, L"#(loc.SuccessRepairMessage)", &successText);
- break;
- case BOOTSTRAPPER_ACTION_UNINSTALL:
- hr = LocGetString(_wixLoc, L"#(loc.SuccessRemoveMessage)", &successText);
- break;
- }
-
- if (successText) {
- LPWSTR formattedString = nullptr;
- BalFormatString(successText->wzText, &formattedString);
- if (formattedString) {
- ThemeSetTextControl(_theme, ID_SUCCESS_TEXT, formattedString);
- StrFree(formattedString);
- }
- }
-
- ThemeControlEnable(_theme, ID_SUCCESS_RESTART_TEXT, showRestartButton);
- ThemeControlEnable(_theme, ID_SUCCESS_RESTART_BUTTON, showRestartButton);
-
- if (_command.action != BOOTSTRAPPER_ACTION_INSTALL ||
- !IsWindowsVersionOrGreater(10, 0, 0)) {
- ThemeControlEnable(_theme, ID_SUCCESS_MAX_PATH_BUTTON, FALSE);
- } else {
- DWORD dataType = 0, buffer = 0, bufferLen = sizeof(buffer);
- HKEY hKey;
- LRESULT res = RegOpenKeyExW(
- HKEY_LOCAL_MACHINE,
- L"SYSTEM\\CurrentControlSet\\Control\\FileSystem",
- 0,
- KEY_READ,
- &hKey
- );
- if (res == ERROR_SUCCESS) {
- res = RegQueryValueExW(hKey, L"LongPathsEnabled", nullptr, &dataType,
- (LPBYTE)&buffer, &bufferLen);
- RegCloseKey(hKey);
- }
- else {
- BalLog(BOOTSTRAPPER_LOG_LEVEL_STANDARD, "Failed to open SYSTEM\\CurrentControlSet\\Control\\FileSystem: error code %d", res);
- }
- if (res == ERROR_SUCCESS && dataType == REG_DWORD && buffer == 0) {
- ThemeControlElevates(_theme, ID_SUCCESS_MAX_PATH_BUTTON, TRUE);
- }
- else {
- if (res == ERROR_SUCCESS)
- BalLog(BOOTSTRAPPER_LOG_LEVEL_STANDARD, "Failed to read LongPathsEnabled value: error code %d", res);
- else
- BalLog(BOOTSTRAPPER_LOG_LEVEL_STANDARD, "Hiding MAX_PATH button because it is already enabled");
- ThemeControlEnable(_theme, ID_SUCCESS_MAX_PATH_BUTTON, FALSE);
- }
- }
- }
-
- void FailurePage_Show() {
- // on the "Failure" page, show error message and check if the restart button should be enabled.
-
- // if there is a log file variable then we'll assume the log file exists.
- BOOL showLogLink = (_bundle.sczLogVariable && *_bundle.sczLogVariable);
- BOOL showErrorMessage = FALSE;
- BOOL showRestartButton = FALSE;
-
- if (FAILED(_hrFinal)) {
- LPWSTR unformattedText = nullptr;
- LPWSTR text = nullptr;
-
- // If we know the failure message, use that.
- if (_failedMessage && *_failedMessage) {
- StrAllocString(&unformattedText, _failedMessage, 0);
- } else {
- // try to get the error message from the error code.
- StrAllocFromError(&unformattedText, _hrFinal, nullptr);
- if (!unformattedText || !*unformattedText) {
- StrAllocFromError(&unformattedText, E_FAIL, nullptr);
- }
- }
-
- if (E_WIXSTDBA_CONDITION_FAILED == _hrFinal) {
- if (unformattedText) {
- StrAllocString(&text, unformattedText, 0);
- }
- } else {
- StrAllocFormatted(&text, L"0x%08x - %ls", _hrFinal, unformattedText);
- }
-
- if (text) {
- ThemeSetTextControl(_theme, ID_FAILURE_MESSAGE_TEXT, text);
- showErrorMessage = TRUE;
- }
-
- ReleaseStr(text);
- ReleaseStr(unformattedText);
- }
-
- if (_restartRequired && BOOTSTRAPPER_RESTART_PROMPT == _command.restart) {
- showRestartButton = TRUE;
- }
-
- ThemeControlEnable(_theme, ID_FAILURE_LOGFILE_LINK, showLogLink);
- ThemeControlEnable(_theme, ID_FAILURE_MESSAGE_TEXT, showErrorMessage);
- ThemeControlEnable(_theme, ID_FAILURE_RESTART_TEXT, showRestartButton);
- ThemeControlEnable(_theme, ID_FAILURE_RESTART_BUTTON, showRestartButton);
- }
-
- static void EnableMaxPathSupport() {
- LPWSTR targetDir = nullptr, defaultDir = nullptr;
- HRESULT hr = BalGetStringVariable(L"TargetDir", &targetDir);
- if (FAILED(hr) || !targetDir || !targetDir[0]) {
- BalLog(BOOTSTRAPPER_LOG_LEVEL_ERROR, "Failed to get TargetDir");
- return;
- }
-
- LPWSTR pythonw = nullptr;
- StrAllocFormatted(&pythonw, L"%ls\\pythonw.exe", targetDir);
- if (!pythonw || !pythonw[0]) {
- BalLog(BOOTSTRAPPER_LOG_LEVEL_ERROR, "Failed to construct pythonw.exe path");
- return;
- }
-
- LPCWSTR arguments = L"-c \"import winreg; "
- "winreg.SetValueEx("
- "winreg.CreateKey(winreg.HKEY_LOCAL_MACHINE, "
- "r'SYSTEM\\CurrentControlSet\\Control\\FileSystem'), "
- "'LongPathsEnabled', "
- "None, "
- "winreg.REG_DWORD, "
- "1"
- ")\"";
- BalLog(BOOTSTRAPPER_LOG_LEVEL_STANDARD, "Executing %ls %ls", pythonw, arguments);
- HINSTANCE res = ShellExecuteW(0, L"runas", pythonw, arguments, NULL, SW_HIDE);
- BalLog(BOOTSTRAPPER_LOG_LEVEL_STANDARD, "return code 0x%08x", res);
- }
-
-public: // IBootstrapperApplication
- virtual STDMETHODIMP OnStartup() {
- HRESULT hr = S_OK;
- DWORD dwUIThreadId = 0;
-
- // create UI thread
- _hUiThread = ::CreateThread(nullptr, 0, UiThreadProc, this, 0, &dwUIThreadId);
- if (!_hUiThread) {
- ExitWithLastError(hr, "Failed to create UI thread.");
- }
-
- LExit:
- return hr;
- }
-
-
- virtual STDMETHODIMP_(int) OnShutdown() {
- int nResult = IDNOACTION;
-
- // wait for UI thread to terminate
- if (_hUiThread) {
- ::WaitForSingleObject(_hUiThread, INFINITE);
- ReleaseHandle(_hUiThread);
- }
-
- // If a restart was required.
- if (_restartRequired && _allowRestart) {
- nResult = IDRESTART;
- }
-
- return nResult;
- }
-
- virtual STDMETHODIMP_(int) OnDetectRelatedMsiPackage(
- __in_z LPCWSTR wzPackageId,
- __in_z LPCWSTR /*wzProductCode*/,
- __in BOOL fPerMachine,
- __in DWORD64 /*dw64Version*/,
- __in BOOTSTRAPPER_RELATED_OPERATION operation
- ) {
- if (BOOTSTRAPPER_RELATED_OPERATION_MAJOR_UPGRADE == operation &&
- (CSTR_EQUAL == ::CompareStringW(LOCALE_NEUTRAL, 0, wzPackageId, -1, L"launcher_AllUsers", -1) ||
- CSTR_EQUAL == ::CompareStringW(LOCALE_NEUTRAL, 0, wzPackageId, -1, L"launcher_JustForMe", -1))) {
- auto hr = LoadAssociateFilesStateFromKey(_engine, fPerMachine ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER);
- if (hr == S_OK) {
- _engine->SetVariableNumeric(L"AssociateFiles", 1);
- } else if (FAILED(hr)) {
- BalLog(BOOTSTRAPPER_LOG_LEVEL_ERROR, "Failed to load AssociateFiles state: error code 0x%08X", hr);
- }
-
- LONGLONG includeLauncher;
- if (FAILED(BalGetNumericVariable(L"Include_launcher", &includeLauncher))
- || includeLauncher == -1) {
- _engine->SetVariableNumeric(L"Include_launcher", 1);
- _engine->SetVariableNumeric(L"InstallLauncherAllUsers", fPerMachine ? 1 : 0);
- }
- _engine->SetVariableNumeric(L"DetectedOldLauncher", 1);
- }
- return CheckCanceled() ? IDCANCEL : IDNOACTION;
- }
-
- virtual STDMETHODIMP_(int) OnDetectRelatedBundle(
- __in LPCWSTR wzBundleId,
- __in BOOTSTRAPPER_RELATION_TYPE relationType,
- __in LPCWSTR /*wzBundleTag*/,
- __in BOOL fPerMachine,
- __in DWORD64 /*dw64Version*/,
- __in BOOTSTRAPPER_RELATED_OPERATION operation
- ) {
- BalInfoAddRelatedBundleAsPackage(&_bundle.packages, wzBundleId, relationType, fPerMachine);
-
- // Remember when our bundle would cause a downgrade.
- if (BOOTSTRAPPER_RELATED_OPERATION_DOWNGRADE == operation) {
- _downgradingOtherVersion = TRUE;
- } else if (BOOTSTRAPPER_RELATED_OPERATION_MAJOR_UPGRADE == operation) {
- BalLog(BOOTSTRAPPER_LOG_LEVEL_STANDARD, "Detected previous version - planning upgrade");
- _upgrading = TRUE;
-
- LoadOptionalFeatureStates(_engine);
- } else if (BOOTSTRAPPER_RELATED_OPERATION_NONE == operation) {
- if (_command.action == BOOTSTRAPPER_ACTION_INSTALL) {
- LOC_STRING *pLocString = nullptr;
- if (SUCCEEDED(LocGetString(_wixLoc, L"#(loc.FailureExistingInstall)", &pLocString)) && pLocString) {
- BalFormatString(pLocString->wzText, &_failedMessage);
- } else {
- BalFormatString(L"Cannot install [WixBundleName] because it is already installed.", &_failedMessage);
- }
- BalLog(
- BOOTSTRAPPER_LOG_LEVEL_ERROR,
- "Related bundle %ls is preventing install",
- wzBundleId
- );
- SetState(PYBA_STATE_FAILED, E_WIXSTDBA_CONDITION_FAILED);
- }
- }
-
- return CheckCanceled() ? IDCANCEL : IDOK;
- }
-
-
- virtual STDMETHODIMP_(void) OnDetectPackageComplete(
- __in LPCWSTR wzPackageId,
- __in HRESULT hrStatus,
- __in BOOTSTRAPPER_PACKAGE_STATE state
- ) {
- if (FAILED(hrStatus)) {
- return;
- }
-
- BOOL detectedLauncher = FALSE;
- HKEY hkey = HKEY_LOCAL_MACHINE;
- if (CSTR_EQUAL == ::CompareStringW(LOCALE_NEUTRAL, 0, wzPackageId, -1, L"launcher_AllUsers", -1)) {
- if (BOOTSTRAPPER_PACKAGE_STATE_PRESENT == state || BOOTSTRAPPER_PACKAGE_STATE_OBSOLETE == state) {
- detectedLauncher = TRUE;
- _engine->SetVariableNumeric(L"InstallLauncherAllUsers", 1);
- }
- } else if (CSTR_EQUAL == ::CompareStringW(LOCALE_NEUTRAL, 0, wzPackageId, -1, L"launcher_JustForMe", -1)) {
- if (BOOTSTRAPPER_PACKAGE_STATE_PRESENT == state || BOOTSTRAPPER_PACKAGE_STATE_OBSOLETE == state) {
- detectedLauncher = TRUE;
- _engine->SetVariableNumeric(L"InstallLauncherAllUsers", 0);
- }
- }
-
- LONGLONG includeLauncher;
- if (SUCCEEDED(BalGetNumericVariable(L"Include_launcher", &includeLauncher))
- && includeLauncher != -1) {
- detectedLauncher = FALSE;
- }
-
- if (detectedLauncher) {
- /* When we detect the current version of the launcher. */
- _engine->SetVariableNumeric(L"Include_launcher", 1);
- _engine->SetVariableNumeric(L"DetectedLauncher", 1);
- _engine->SetVariableString(L"Include_launcherState", L"disable");
- _engine->SetVariableString(L"InstallLauncherAllUsersState", L"disable");
-
- auto hr = LoadAssociateFilesStateFromKey(_engine, hkey);
- if (hr == S_OK) {
- _engine->SetVariableNumeric(L"AssociateFiles", 1);
- } else if (FAILED(hr)) {
- BalLog(BOOTSTRAPPER_LOG_LEVEL_ERROR, "Failed to load AssociateFiles state: error code 0x%08X", hr);
- }
- }
- }
-
-
- virtual STDMETHODIMP_(void) OnDetectComplete(__in HRESULT hrStatus) {
- if (SUCCEEDED(hrStatus) && _baFunction) {
- BalLog(BOOTSTRAPPER_LOG_LEVEL_STANDARD, "Running detect complete BA function");
- _baFunction->OnDetectComplete();
- }
-
- if (SUCCEEDED(hrStatus)) {
- LONGLONG includeLauncher;
- if (SUCCEEDED(BalGetNumericVariable(L"Include_launcher", &includeLauncher))
- && includeLauncher == -1) {
- _engine->SetVariableNumeric(L"Include_launcher", 1);
- }
- }
-
- if (SUCCEEDED(hrStatus)) {
- hrStatus = EvaluateConditions();
- }
-
- if (SUCCEEDED(hrStatus)) {
- // Ensure the default path has been set
- hrStatus = EnsureTargetDir();
- }
-
- SetState(PYBA_STATE_DETECTED, hrStatus);
-
- // If we're not interacting with the user or we're doing a layout or we're just after a force restart
- // then automatically start planning.
- if (BOOTSTRAPPER_DISPLAY_FULL > _command.display ||
- BOOTSTRAPPER_ACTION_LAYOUT == _command.action ||
- BOOTSTRAPPER_ACTION_UNINSTALL == _command.action ||
- BOOTSTRAPPER_RESUME_TYPE_REBOOT == _command.resumeType) {
- if (SUCCEEDED(hrStatus)) {
- ::PostMessageW(_hWnd, WM_PYBA_PLAN_PACKAGES, 0, _command.action);
- }
- }
- }
-
-
- virtual STDMETHODIMP_(int) OnPlanRelatedBundle(
- __in_z LPCWSTR /*wzBundleId*/,
- __inout_z BOOTSTRAPPER_REQUEST_STATE* pRequestedState
- ) {
- return CheckCanceled() ? IDCANCEL : IDOK;
- }
-
-
- virtual STDMETHODIMP_(int) OnPlanPackageBegin(
- __in_z LPCWSTR wzPackageId,
- __inout BOOTSTRAPPER_REQUEST_STATE *pRequestState
- ) {
- HRESULT hr = S_OK;
- BAL_INFO_PACKAGE* pPackage = nullptr;
-
- if (_nextPackageAfterRestart) {
- // After restart we need to finish the dependency registration for our package so allow the package
- // to go present.
- if (CSTR_EQUAL == ::CompareStringW(LOCALE_NEUTRAL, 0, wzPackageId, -1, _nextPackageAfterRestart, -1)) {
- // Do not allow a repair because that could put us in a perpetual restart loop.
- if (BOOTSTRAPPER_REQUEST_STATE_REPAIR == *pRequestState) {
- *pRequestState = BOOTSTRAPPER_REQUEST_STATE_PRESENT;
- }
-
- ReleaseNullStr(_nextPackageAfterRestart); // no more skipping now.
- } else {
- // not the matching package, so skip it.
- BalLog(BOOTSTRAPPER_LOG_LEVEL_STANDARD, "Skipping package: %ls, after restart because it was applied before the restart.", wzPackageId);
-
- *pRequestState = BOOTSTRAPPER_REQUEST_STATE_NONE;
- }
- } else if ((_plannedAction == BOOTSTRAPPER_ACTION_INSTALL || _plannedAction == BOOTSTRAPPER_ACTION_MODIFY) &&
- SUCCEEDED(BalInfoFindPackageById(&_bundle.packages, wzPackageId, &pPackage))) {
- BOOL f = FALSE;
- if (SUCCEEDED(_engine->EvaluateCondition(pPackage->sczInstallCondition, &f)) && f) {
- *pRequestState = BOOTSTRAPPER_REQUEST_STATE_PRESENT;
- }
- }
-
- return CheckCanceled() ? IDCANCEL : IDOK;
- }
-
- virtual STDMETHODIMP_(int) OnPlanMsiFeature(
- __in_z LPCWSTR wzPackageId,
- __in_z LPCWSTR wzFeatureId,
- __inout BOOTSTRAPPER_FEATURE_STATE* pRequestedState
- ) {
- LONGLONG install;
-
- if (wcscmp(wzFeatureId, L"AssociateFiles") == 0 || wcscmp(wzFeatureId, L"Shortcuts") == 0) {
- if (SUCCEEDED(_engine->GetVariableNumeric(wzFeatureId, &install)) && install) {
- *pRequestedState = BOOTSTRAPPER_FEATURE_STATE_LOCAL;
- } else {
- *pRequestedState = BOOTSTRAPPER_FEATURE_STATE_ABSENT;
- }
- } else {
- *pRequestedState = BOOTSTRAPPER_FEATURE_STATE_LOCAL;
- }
- return CheckCanceled() ? IDCANCEL : IDNOACTION;
- }
-
- virtual STDMETHODIMP_(void) OnPlanComplete(__in HRESULT hrStatus) {
- if (SUCCEEDED(hrStatus) && _baFunction) {
- BalLog(BOOTSTRAPPER_LOG_LEVEL_STANDARD, "Running plan complete BA function");
- _baFunction->OnPlanComplete();
- }
-
- SetState(PYBA_STATE_PLANNED, hrStatus);
-
- if (SUCCEEDED(hrStatus)) {
- ::PostMessageW(_hWnd, WM_PYBA_APPLY_PACKAGES, 0, 0);
- }
-
- _startedExecution = FALSE;
- _calculatedCacheProgress = 0;
- _calculatedExecuteProgress = 0;
- }
-
-
- virtual STDMETHODIMP_(int) OnCachePackageBegin(
- __in_z LPCWSTR wzPackageId,
- __in DWORD cCachePayloads,
- __in DWORD64 dw64PackageCacheSize
- ) {
- if (wzPackageId && *wzPackageId) {
- BAL_INFO_PACKAGE* pPackage = nullptr;
- HRESULT hr = BalInfoFindPackageById(&_bundle.packages, wzPackageId, &pPackage);
- LPCWSTR wz = (SUCCEEDED(hr) && pPackage->sczDisplayName) ? pPackage->sczDisplayName : wzPackageId;
-
- ThemeSetTextControl(_theme, ID_CACHE_PROGRESS_PACKAGE_TEXT, wz);
-
- // If something started executing, leave it in the overall progress text.
- if (!_startedExecution) {
- ThemeSetTextControl(_theme, ID_OVERALL_PROGRESS_PACKAGE_TEXT, wz);
- }
- }
-
- return __super::OnCachePackageBegin(wzPackageId, cCachePayloads, dw64PackageCacheSize);
- }
-
-
- virtual STDMETHODIMP_(int) OnCacheAcquireProgress(
- __in_z LPCWSTR wzPackageOrContainerId,
- __in_z_opt LPCWSTR wzPayloadId,
- __in DWORD64 dw64Progress,
- __in DWORD64 dw64Total,
- __in DWORD dwOverallPercentage
- ) {
- WCHAR wzProgress[5] = { };
-
-#ifdef DEBUG
- BalLog(BOOTSTRAPPER_LOG_LEVEL_STANDARD, "PYBA: OnCacheAcquireProgress() - container/package: %ls, payload: %ls, progress: %I64u, total: %I64u, overall progress: %u%%", wzPackageOrContainerId, wzPayloadId, dw64Progress, dw64Total, dwOverallPercentage);
-#endif
-
- ::StringCchPrintfW(wzProgress, countof(wzProgress), L"%u%%", dwOverallPercentage);
- ThemeSetTextControl(_theme, ID_CACHE_PROGRESS_TEXT, wzProgress);
-
- ThemeSetProgressControl(_theme, ID_CACHE_PROGRESS_BAR, dwOverallPercentage);
-
- _calculatedCacheProgress = dwOverallPercentage * PYBA_ACQUIRE_PERCENTAGE / 100;
- ThemeSetProgressControl(_theme, ID_OVERALL_CALCULATED_PROGRESS_BAR, _calculatedCacheProgress + _calculatedExecuteProgress);
-
- SetTaskbarButtonProgress(_calculatedCacheProgress + _calculatedExecuteProgress);
-
- return __super::OnCacheAcquireProgress(wzPackageOrContainerId, wzPayloadId, dw64Progress, dw64Total, dwOverallPercentage);
- }
-
-
- virtual STDMETHODIMP_(int) OnCacheAcquireComplete(
- __in_z LPCWSTR wzPackageOrContainerId,
- __in_z_opt LPCWSTR wzPayloadId,
- __in HRESULT hrStatus,
- __in int nRecommendation
- ) {
- SetProgressState(hrStatus);
- return __super::OnCacheAcquireComplete(wzPackageOrContainerId, wzPayloadId, hrStatus, nRecommendation);
- }
-
-
- virtual STDMETHODIMP_(int) OnCacheVerifyComplete(
- __in_z LPCWSTR wzPackageId,
- __in_z LPCWSTR wzPayloadId,
- __in HRESULT hrStatus,
- __in int nRecommendation
- ) {
- SetProgressState(hrStatus);
- return __super::OnCacheVerifyComplete(wzPackageId, wzPayloadId, hrStatus, nRecommendation);
- }
-
-
- virtual STDMETHODIMP_(void) OnCacheComplete(__in HRESULT /*hrStatus*/) {
- ThemeSetTextControl(_theme, ID_CACHE_PROGRESS_PACKAGE_TEXT, L"");
- SetState(PYBA_STATE_CACHED, S_OK); // we always return success here and let OnApplyComplete() deal with the error.
- }
-
-
- virtual STDMETHODIMP_(int) OnError(
- __in BOOTSTRAPPER_ERROR_TYPE errorType,
- __in LPCWSTR wzPackageId,
- __in DWORD dwCode,
- __in_z LPCWSTR wzError,
- __in DWORD dwUIHint,
- __in DWORD /*cData*/,
- __in_ecount_z_opt(cData) LPCWSTR* /*rgwzData*/,
- __in int nRecommendation
- ) {
- int nResult = nRecommendation;
- LPWSTR sczError = nullptr;
-
- if (BOOTSTRAPPER_DISPLAY_EMBEDDED == _command.display) {
- HRESULT hr = _engine->SendEmbeddedError(dwCode, wzError, dwUIHint, &nResult);
- if (FAILED(hr)) {
- nResult = IDERROR;
- }
- } else if (BOOTSTRAPPER_DISPLAY_FULL == _command.display) {
- // If this is an authentication failure, let the engine try to handle it for us.
- if (BOOTSTRAPPER_ERROR_TYPE_HTTP_AUTH_SERVER == errorType || BOOTSTRAPPER_ERROR_TYPE_HTTP_AUTH_PROXY == errorType) {
- nResult = IDTRYAGAIN;
- } else // show a generic error message box.
- {
- BalRetryErrorOccurred(wzPackageId, dwCode);
-
- if (!_showingInternalUIThisPackage) {
- // If no error message was provided, use the error code to try and get an error message.
- if (!wzError || !*wzError || BOOTSTRAPPER_ERROR_TYPE_WINDOWS_INSTALLER != errorType) {
- HRESULT hr = StrAllocFromError(&sczError, dwCode, nullptr);
- if (FAILED(hr) || !sczError || !*sczError) {
- StrAllocFormatted(&sczError, L"0x%x", dwCode);
- }
- }
-
- nResult = ::MessageBoxW(_hWnd, sczError ? sczError : wzError, _theme->sczCaption, dwUIHint);
- }
- }
-
- SetProgressState(HRESULT_FROM_WIN32(dwCode));
- } else {
- // just take note of the error code and let things continue.
- BalRetryErrorOccurred(wzPackageId, dwCode);
- }
-
- ReleaseStr(sczError);
- return nResult;
- }
-
-
- virtual STDMETHODIMP_(int) OnExecuteMsiMessage(
- __in_z LPCWSTR wzPackageId,
- __in INSTALLMESSAGE mt,
- __in UINT uiFlags,
- __in_z LPCWSTR wzMessage,
- __in DWORD cData,
- __in_ecount_z_opt(cData) LPCWSTR* rgwzData,
- __in int nRecommendation
- ) {
-#ifdef DEBUG
- BalLog(BOOTSTRAPPER_LOG_LEVEL_STANDARD, "PYBA: OnExecuteMsiMessage() - package: %ls, message: %ls", wzPackageId, wzMessage);
-#endif
- if (BOOTSTRAPPER_DISPLAY_FULL == _command.display && (INSTALLMESSAGE_WARNING == mt || INSTALLMESSAGE_USER == mt)) {
- int nResult = ::MessageBoxW(_hWnd, wzMessage, _theme->sczCaption, uiFlags);
- return nResult;
- }
-
- if (INSTALLMESSAGE_ACTIONSTART == mt) {
- ThemeSetTextControl(_theme, ID_EXECUTE_PROGRESS_ACTIONDATA_TEXT, wzMessage);
- }
-
- return __super::OnExecuteMsiMessage(wzPackageId, mt, uiFlags, wzMessage, cData, rgwzData, nRecommendation);
- }
-
-
- virtual STDMETHODIMP_(int) OnProgress(__in DWORD dwProgressPercentage, __in DWORD dwOverallProgressPercentage) {
- WCHAR wzProgress[5] = { };
-
-#ifdef DEBUG
- BalLog(BOOTSTRAPPER_LOG_LEVEL_STANDARD, "PYBA: OnProgress() - progress: %u%%, overall progress: %u%%", dwProgressPercentage, dwOverallProgressPercentage);
-#endif
-
- ::StringCchPrintfW(wzProgress, countof(wzProgress), L"%u%%", dwOverallProgressPercentage);
- ThemeSetTextControl(_theme, ID_OVERALL_PROGRESS_TEXT, wzProgress);
-
- ThemeSetProgressControl(_theme, ID_OVERALL_PROGRESS_BAR, dwOverallProgressPercentage);
- SetTaskbarButtonProgress(dwOverallProgressPercentage);
-
- return __super::OnProgress(dwProgressPercentage, dwOverallProgressPercentage);
- }
-
-
- virtual STDMETHODIMP_(int) OnExecutePackageBegin(__in_z LPCWSTR wzPackageId, __in BOOL fExecute) {
- LPWSTR sczFormattedString = nullptr;
-
- _startedExecution = TRUE;
-
- if (wzPackageId && *wzPackageId) {
- BAL_INFO_PACKAGE* pPackage = nullptr;
- BalInfoFindPackageById(&_bundle.packages, wzPackageId, &pPackage);
-
- LPCWSTR wz = wzPackageId;
- if (pPackage) {
- LOC_STRING* pLocString = nullptr;
-
- switch (pPackage->type) {
- case BAL_INFO_PACKAGE_TYPE_BUNDLE_ADDON:
- LocGetString(_wixLoc, L"#(loc.ExecuteAddonRelatedBundleMessage)", &pLocString);
- break;
-
- case BAL_INFO_PACKAGE_TYPE_BUNDLE_PATCH:
- LocGetString(_wixLoc, L"#(loc.ExecutePatchRelatedBundleMessage)", &pLocString);
- break;
-
- case BAL_INFO_PACKAGE_TYPE_BUNDLE_UPGRADE:
- LocGetString(_wixLoc, L"#(loc.ExecuteUpgradeRelatedBundleMessage)", &pLocString);
- break;
- }
-
- if (pLocString) {
- // If the wix developer is showing a hidden variable in the UI, then obviously they don't care about keeping it safe
- // so don't go down the rabbit hole of making sure that this is securely freed.
- BalFormatString(pLocString->wzText, &sczFormattedString);
- }
-
- wz = sczFormattedString ? sczFormattedString : pPackage->sczDisplayName ? pPackage->sczDisplayName : wzPackageId;
- }
-
- _showingInternalUIThisPackage = pPackage && pPackage->fDisplayInternalUI;
-
- ThemeSetTextControl(_theme, ID_EXECUTE_PROGRESS_PACKAGE_TEXT, wz);
- ThemeSetTextControl(_theme, ID_OVERALL_PROGRESS_PACKAGE_TEXT, wz);
- } else {
- _showingInternalUIThisPackage = FALSE;
- }
-
- ReleaseStr(sczFormattedString);
- return __super::OnExecutePackageBegin(wzPackageId, fExecute);
- }
-
-
- virtual int __stdcall OnExecuteProgress(
- __in_z LPCWSTR wzPackageId,
- __in DWORD dwProgressPercentage,
- __in DWORD dwOverallProgressPercentage
- ) {
- WCHAR wzProgress[8] = { };
-
-#ifdef DEBUG
- BalLog(BOOTSTRAPPER_LOG_LEVEL_STANDARD, "PYBA: OnExecuteProgress() - package: %ls, progress: %u%%, overall progress: %u%%", wzPackageId, dwProgressPercentage, dwOverallProgressPercentage);
-#endif
-
- ::StringCchPrintfW(wzProgress, countof(wzProgress), L"%u%%", dwOverallProgressPercentage);
- ThemeSetTextControl(_theme, ID_EXECUTE_PROGRESS_TEXT, wzProgress);
-
- ThemeSetProgressControl(_theme, ID_EXECUTE_PROGRESS_BAR, dwOverallProgressPercentage);
-
- _calculatedExecuteProgress = dwOverallProgressPercentage * (100 - PYBA_ACQUIRE_PERCENTAGE) / 100;
- ThemeSetProgressControl(_theme, ID_OVERALL_CALCULATED_PROGRESS_BAR, _calculatedCacheProgress + _calculatedExecuteProgress);
-
- SetTaskbarButtonProgress(_calculatedCacheProgress + _calculatedExecuteProgress);
-
- return __super::OnExecuteProgress(wzPackageId, dwProgressPercentage, dwOverallProgressPercentage);
- }
-
-
- virtual STDMETHODIMP_(int) OnExecutePackageComplete(
- __in_z LPCWSTR wzPackageId,
- __in HRESULT hrExitCode,
- __in BOOTSTRAPPER_APPLY_RESTART restart,
- __in int nRecommendation
- ) {
- SetProgressState(hrExitCode);
-
- if (_wcsnicmp(wzPackageId, L"path_", 5) == 0 && SUCCEEDED(hrExitCode)) {
- SendMessageTimeoutW(
- HWND_BROADCAST,
- WM_SETTINGCHANGE,
- 0,
- reinterpret_cast<LPARAM>(L"Environment"),
- SMTO_ABORTIFHUNG,
- 1000,
- nullptr
- );
- }
-
- int nResult = __super::OnExecutePackageComplete(wzPackageId, hrExitCode, restart, nRecommendation);
-
- return nResult;
- }
-
-
- virtual STDMETHODIMP_(void) OnExecuteComplete(__in HRESULT hrStatus) {
- ThemeSetTextControl(_theme, ID_EXECUTE_PROGRESS_PACKAGE_TEXT, L"");
- ThemeSetTextControl(_theme, ID_EXECUTE_PROGRESS_ACTIONDATA_TEXT, L"");
- ThemeSetTextControl(_theme, ID_OVERALL_PROGRESS_PACKAGE_TEXT, L"");
- ThemeControlEnable(_theme, ID_PROGRESS_CANCEL_BUTTON, FALSE); // no more cancel.
-
- SetState(PYBA_STATE_EXECUTED, S_OK); // we always return success here and let OnApplyComplete() deal with the error.
- SetProgressState(hrStatus);
- }
-
-
- virtual STDMETHODIMP_(int) OnResolveSource(
- __in_z LPCWSTR wzPackageOrContainerId,
- __in_z_opt LPCWSTR wzPayloadId,
- __in_z LPCWSTR wzLocalSource,
- __in_z_opt LPCWSTR wzDownloadSource
- ) {
- int nResult = IDERROR; // assume we won't resolve source and that is unexpected.
-
- if (BOOTSTRAPPER_DISPLAY_FULL == _command.display) {
- if (wzDownloadSource) {
- nResult = IDDOWNLOAD;
- } else {
- // prompt to change the source location.
- OPENFILENAMEW ofn = { };
- WCHAR wzFile[MAX_PATH] = { };
-
- ::StringCchCopyW(wzFile, countof(wzFile), wzLocalSource);
-
- ofn.lStructSize = sizeof(ofn);
- ofn.hwndOwner = _hWnd;
- ofn.lpstrFile = wzFile;
- ofn.nMaxFile = countof(wzFile);
- ofn.lpstrFilter = L"All Files\0*.*\0";
- ofn.nFilterIndex = 1;
- ofn.Flags = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST;
- ofn.lpstrTitle = _theme->sczCaption;
-
- if (::GetOpenFileNameW(&ofn)) {
- HRESULT hr = _engine->SetLocalSource(wzPackageOrContainerId, wzPayloadId, ofn.lpstrFile);
- nResult = SUCCEEDED(hr) ? IDRETRY : IDERROR;
- } else {
- nResult = IDCANCEL;
- }
- }
- } else if (wzDownloadSource) {
- // If doing a non-interactive install and download source is available, let's try downloading the package silently
- nResult = IDDOWNLOAD;
- }
- // else there's nothing more we can do in non-interactive mode
-
- return CheckCanceled() ? IDCANCEL : nResult;
- }
-
-
- virtual STDMETHODIMP_(int) OnApplyComplete(__in HRESULT hrStatus, __in BOOTSTRAPPER_APPLY_RESTART restart) {
- _restartResult = restart; // remember the restart result so we return the correct error code no matter what the user chooses to do in the UI.
-
- // If a restart was encountered and we are not suppressing restarts, then restart is required.
- _restartRequired = (BOOTSTRAPPER_APPLY_RESTART_NONE != restart && BOOTSTRAPPER_RESTART_NEVER < _command.restart);
- // 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.
- _allowRestart = _restartRequired && (BOOTSTRAPPER_DISPLAY_FULL > _command.display || BOOTSTRAPPER_RESTART_PROMPT < _command.restart);
-
- // If we are showing UI, wait a beat before moving to the final screen.
- if (BOOTSTRAPPER_DISPLAY_NONE < _command.display) {
- ::Sleep(250);
- }
-
- SetState(PYBA_STATE_APPLIED, hrStatus);
- SetTaskbarButtonProgress(100); // show full progress bar, green, yellow, or red
-
- return IDNOACTION;
- }
-
- virtual STDMETHODIMP_(void) OnLaunchApprovedExeComplete(__in HRESULT hrStatus, __in DWORD /*processId*/) {
- }
-
-
-private:
- //
- // UiThreadProc - entrypoint for UI thread.
- //
- static DWORD WINAPI UiThreadProc(__in LPVOID pvContext) {
- HRESULT hr = S_OK;
- PythonBootstrapperApplication* pThis = (PythonBootstrapperApplication*)pvContext;
- BOOL comInitialized = FALSE;
- BOOL ret = FALSE;
- MSG msg = { };
-
- // Initialize COM and theme.
- hr = ::CoInitialize(nullptr);
- BalExitOnFailure(hr, "Failed to initialize COM.");
- comInitialized = TRUE;
-
- hr = ThemeInitialize(pThis->_hModule);
- BalExitOnFailure(hr, "Failed to initialize theme manager.");
-
- hr = pThis->InitializeData();
- BalExitOnFailure(hr, "Failed to initialize data in bootstrapper application.");
-
- // Create main window.
- pThis->InitializeTaskbarButton();
- hr = pThis->CreateMainWindow();
- BalExitOnFailure(hr, "Failed to create main window.");
-
- pThis->ValidateOperatingSystem();
-
- if (FAILED(pThis->_hrFinal)) {
- pThis->SetState(PYBA_STATE_FAILED, hr);
- ::PostMessageW(pThis->_hWnd, WM_PYBA_SHOW_FAILURE, 0, 0);
- } else {
- // Okay, we're ready for packages now.
- pThis->SetState(PYBA_STATE_INITIALIZED, hr);
- ::PostMessageW(pThis->_hWnd, BOOTSTRAPPER_ACTION_HELP == pThis->_command.action ? WM_PYBA_SHOW_HELP : WM_PYBA_DETECT_PACKAGES, 0, 0);
- }
-
- // message pump
- while (0 != (ret = ::GetMessageW(&msg, nullptr, 0, 0))) {
- if (-1 == ret) {
- hr = E_UNEXPECTED;
- BalExitOnFailure(hr, "Unexpected return value from message pump.");
- } else if (!ThemeHandleKeyboardMessage(pThis->_theme, msg.hwnd, &msg)) {
- ::TranslateMessage(&msg);
- ::DispatchMessageW(&msg);
- }
- }
-
- // Succeeded thus far, check to see if anything went wrong while actually
- // executing changes.
- if (FAILED(pThis->_hrFinal)) {
- hr = pThis->_hrFinal;
- } else if (pThis->CheckCanceled()) {
- hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT);
- }
-
- LExit:
- // destroy main window
- pThis->DestroyMainWindow();
-
- // initiate engine shutdown
- DWORD dwQuit = HRESULT_CODE(hr);
- if (BOOTSTRAPPER_APPLY_RESTART_INITIATED == pThis->_restartResult) {
- dwQuit = ERROR_SUCCESS_REBOOT_INITIATED;
- } else if (BOOTSTRAPPER_APPLY_RESTART_REQUIRED == pThis->_restartResult) {
- dwQuit = ERROR_SUCCESS_REBOOT_REQUIRED;
- }
- pThis->_engine->Quit(dwQuit);
-
- ReleaseTheme(pThis->_theme);
- ThemeUninitialize();
-
- // uninitialize COM
- if (comInitialized) {
- ::CoUninitialize();
- }
-
- return hr;
- }
-
- //
- // ParseVariablesFromUnattendXml - reads options from unattend.xml if it
- // exists
- //
- HRESULT ParseVariablesFromUnattendXml() {
- HRESULT hr = S_OK;
- LPWSTR sczUnattendXmlPath = nullptr;
- IXMLDOMDocument *pixdUnattend = nullptr;
- IXMLDOMNodeList *pNodes = nullptr;
- IXMLDOMNode *pNode = nullptr;
- long cNodes;
- DWORD dwAttr;
- LPWSTR scz = nullptr;
- BOOL bValue;
- int iValue;
- BOOL tryConvert;
- BSTR bstrValue = nullptr;
-
- hr = BalFormatString(L"[WixBundleOriginalSourceFolder]unattend.xml", &sczUnattendXmlPath);
- BalExitOnFailure(hr, "Failed to calculate path to unattend.xml");
-
- if (!FileExistsEx(sczUnattendXmlPath, &dwAttr)) {
- BalLog(BOOTSTRAPPER_LOG_LEVEL_VERBOSE, "Did not find %ls", sczUnattendXmlPath);
- hr = S_FALSE;
- goto LExit;
- }
-
- hr = XmlLoadDocumentFromFile(sczUnattendXmlPath, &pixdUnattend);
- BalExitOnFailure1(hr, "Failed to read %ls", sczUnattendXmlPath);
-
- // get the list of variables users have overridden
- hr = XmlSelectNodes(pixdUnattend, L"/Options/Option", &pNodes);
- if (S_FALSE == hr) {
- ExitFunction1(hr = S_OK);
- }
- BalExitOnFailure(hr, "Failed to select option nodes.");
-
- hr = pNodes->get_length((long*)&cNodes);
- BalExitOnFailure(hr, "Failed to get option node count.");
-
- BalLog(BOOTSTRAPPER_LOG_LEVEL_STANDARD, "Reading settings from %ls", sczUnattendXmlPath);
-
- for (DWORD i = 0; i < cNodes; ++i) {
- hr = XmlNextElement(pNodes, &pNode, nullptr);
- BalExitOnFailure(hr, "Failed to get next node.");
-
- // @Name
- hr = XmlGetAttributeEx(pNode, L"Name", &scz);
- BalExitOnFailure(hr, "Failed to get @Name.");
-
- tryConvert = TRUE;
- hr = XmlGetAttribute(pNode, L"Value", &bstrValue);
- if (FAILED(hr) || !bstrValue || !*bstrValue) {
- hr = XmlGetText(pNode, &bstrValue);
- tryConvert = FALSE;
- }
- BalExitOnFailure(hr, "Failed to get @Value.");
-
- if (tryConvert &&
- CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, bstrValue, -1, L"yes", -1)) {
- _engine->SetVariableNumeric(scz, 1);
- } else if (tryConvert &&
- CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, bstrValue, -1, L"no", -1)) {
- _engine->SetVariableNumeric(scz, 0);
- } else if (tryConvert && ::StrToIntExW(bstrValue, STIF_DEFAULT, &iValue)) {
- _engine->SetVariableNumeric(scz, iValue);
- } else {
- _engine->SetVariableString(scz, bstrValue);
- }
-
- ReleaseNullBSTR(bstrValue);
- ReleaseNullStr(scz);
- ReleaseNullObject(pNode);
- }
-
- BalLog(BOOTSTRAPPER_LOG_LEVEL_STANDARD, "Finished reading from %ls", sczUnattendXmlPath);
-
- LExit:
- ReleaseObject(pNode);
- ReleaseObject(pNodes);
- ReleaseObject(pixdUnattend);
- ReleaseStr(sczUnattendXmlPath);
-
- return hr;
- }
-
-
- //
- // InitializeData - initializes all the package information.
- //
- HRESULT InitializeData() {
- HRESULT hr = S_OK;
- LPWSTR sczModulePath = nullptr;
- IXMLDOMDocument *pixdManifest = nullptr;
-
- hr = BalManifestLoad(_hModule, &pixdManifest);
- BalExitOnFailure(hr, "Failed to load bootstrapper application manifest.");
-
- hr = ParseOverridableVariablesFromXml(pixdManifest);
- BalExitOnFailure(hr, "Failed to read overridable variables.");
-
- if (_command.action == BOOTSTRAPPER_ACTION_MODIFY) {
- LoadOptionalFeatureStates(_engine);
- }
-
- hr = ParseVariablesFromUnattendXml();
- ExitOnFailure(hr, "Failed to read unattend.ini file.");
-
- hr = ProcessCommandLine(&_language);
- ExitOnFailure(hr, "Unknown commandline parameters.");
-
- hr = PathRelativeToModule(&sczModulePath, nullptr, _hModule);
- BalExitOnFailure(hr, "Failed to get module path.");
-
- hr = LoadLocalization(sczModulePath, _language);
- ExitOnFailure(hr, "Failed to load localization.");
-
- hr = LoadTheme(sczModulePath, _language);
- ExitOnFailure(hr, "Failed to load theme.");
-
- hr = BalInfoParseFromXml(&_bundle, pixdManifest);
- BalExitOnFailure(hr, "Failed to load bundle information.");
-
- hr = BalConditionsParseFromXml(&_conditions, pixdManifest, _wixLoc);
- BalExitOnFailure(hr, "Failed to load conditions from XML.");
-
- hr = LoadBootstrapperBAFunctions();
- BalExitOnFailure(hr, "Failed to load bootstrapper functions.");
-
- hr = UpdateUIStrings(_command.action);
- BalExitOnFailure(hr, "Failed to load UI strings.");
-
- GetBundleFileVersion();
- // don't fail if we couldn't get the version info; best-effort only
- LExit:
- ReleaseObject(pixdManifest);
- ReleaseStr(sczModulePath);
-
- return hr;
- }
-
-
- //
- // ProcessCommandLine - process the provided command line arguments.
- //
- HRESULT ProcessCommandLine(__inout LPWSTR* psczLanguage) {
- HRESULT hr = S_OK;
- int argc = 0;
- LPWSTR* argv = nullptr;
- LPWSTR sczVariableName = nullptr;
- LPWSTR sczVariableValue = nullptr;
-
- if (_command.wzCommandLine && *_command.wzCommandLine) {
- argv = ::CommandLineToArgvW(_command.wzCommandLine, &argc);
- ExitOnNullWithLastError(argv, hr, "Failed to get command line.");
-
- for (int i = 0; i < argc; ++i) {
- if (argv[i][0] == L'-' || argv[i][0] == L'/') {
- if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, L"lang", -1)) {
- if (i + 1 >= argc) {
- hr = E_INVALIDARG;
- BalExitOnFailure(hr, "Must specify a language.");
- }
-
- ++i;
-
- hr = StrAllocString(psczLanguage, &argv[i][0], 0);
- BalExitOnFailure(hr, "Failed to copy language.");
- } else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, &argv[i][1], -1, L"simple", -1)) {
- _engine->SetVariableNumeric(L"SimpleInstall", 1);
- }
- } else if (_overridableVariables) {
- int value;
- const wchar_t* pwc = wcschr(argv[i], L'=');
- if (pwc) {
- hr = StrAllocString(&sczVariableName, argv[i], pwc - argv[i]);
- BalExitOnFailure(hr, "Failed to copy variable name.");
-
- hr = DictKeyExists(_overridableVariables, sczVariableName);
- if (E_NOTFOUND == hr) {
- BalLog(BOOTSTRAPPER_LOG_LEVEL_ERROR, "Ignoring attempt to set non-overridable variable: '%ls'.", sczVariableName);
- hr = S_OK;
- continue;
- }
- ExitOnFailure(hr, "Failed to check the dictionary of overridable variables.");
-
- hr = StrAllocString(&sczVariableValue, ++pwc, 0);
- BalExitOnFailure(hr, "Failed to copy variable value.");
-
- if (::StrToIntEx(sczVariableValue, STIF_DEFAULT, &value)) {
- hr = _engine->SetVariableNumeric(sczVariableName, value);
- } else {
- hr = _engine->SetVariableString(sczVariableName, sczVariableValue);
- }
- BalExitOnFailure(hr, "Failed to set variable.");
- } else {
- BalLog(BOOTSTRAPPER_LOG_LEVEL_STANDARD, "Ignoring unknown argument: %ls", argv[i]);
- }
- }
- }
- }
-
- LExit:
- if (argv) {
- ::LocalFree(argv);
- }
-
- ReleaseStr(sczVariableName);
- ReleaseStr(sczVariableValue);
-
- return hr;
- }
-
- HRESULT LoadLocalization(__in_z LPCWSTR wzModulePath, __in_z_opt LPCWSTR wzLanguage) {
- HRESULT hr = S_OK;
- LPWSTR sczLocPath = nullptr;
- LPCWSTR wzLocFileName = L"Default.wxl";
-
- hr = LocProbeForFile(wzModulePath, wzLocFileName, wzLanguage, &sczLocPath);
- BalExitOnFailure2(hr, "Failed to probe for loc file: %ls in path: %ls", wzLocFileName, wzModulePath);
-
- hr = LocLoadFromFile(sczLocPath, &_wixLoc);
- BalExitOnFailure1(hr, "Failed to load loc file from path: %ls", sczLocPath);
-
- if (WIX_LOCALIZATION_LANGUAGE_NOT_SET != _wixLoc->dwLangId) {
- ::SetThreadLocale(_wixLoc->dwLangId);
- }
-
- hr = StrAllocString(&_confirmCloseMessage, L"#(loc.ConfirmCancelMessage)", 0);
- ExitOnFailure(hr, "Failed to initialize confirm message loc identifier.");
-
- hr = LocLocalizeString(_wixLoc, &_confirmCloseMessage);
- BalExitOnFailure1(hr, "Failed to localize confirm close message: %ls", _confirmCloseMessage);
-
- LExit:
- ReleaseStr(sczLocPath);
-
- return hr;
- }
-
-
- HRESULT LoadTheme(__in_z LPCWSTR wzModulePath, __in_z_opt LPCWSTR wzLanguage) {
- HRESULT hr = S_OK;
- LPWSTR sczThemePath = nullptr;
- LPCWSTR wzThemeFileName = L"Default.thm";
- LPWSTR sczCaption = nullptr;
-
- hr = LocProbeForFile(wzModulePath, wzThemeFileName, wzLanguage, &sczThemePath);
- BalExitOnFailure2(hr, "Failed to probe for theme file: %ls in path: %ls", wzThemeFileName, wzModulePath);
-
- hr = ThemeLoadFromFile(sczThemePath, &_theme);
- BalExitOnFailure1(hr, "Failed to load theme from path: %ls", sczThemePath);
-
- hr = ThemeLocalize(_theme, _wixLoc);
- BalExitOnFailure1(hr, "Failed to localize theme: %ls", sczThemePath);
-
- // Update the caption if there are any formatted strings in it.
- // If the wix developer is showing a hidden variable in the UI, then
- // obviously they don't care about keeping it safe so don't go down the
- // rabbit hole of making sure that this is securely freed.
- hr = BalFormatString(_theme->sczCaption, &sczCaption);
- if (SUCCEEDED(hr)) {
- ThemeUpdateCaption(_theme, sczCaption);
- }
-
- LExit:
- ReleaseStr(sczCaption);
- ReleaseStr(sczThemePath);
-
- return hr;
- }
-
-
- HRESULT ParseOverridableVariablesFromXml(__in IXMLDOMDocument* pixdManifest) {
- HRESULT hr = S_OK;
- IXMLDOMNode* pNode = nullptr;
- IXMLDOMNodeList* pNodes = nullptr;
- DWORD cNodes = 0;
- LPWSTR scz = nullptr;
- BOOL hidden = FALSE;
-
- // get the list of variables users can override on the command line
- hr = XmlSelectNodes(pixdManifest, L"/BootstrapperApplicationData/WixStdbaOverridableVariable", &pNodes);
- if (S_FALSE == hr) {
- ExitFunction1(hr = S_OK);
- }
- ExitOnFailure(hr, "Failed to select overridable variable nodes.");
-
- hr = pNodes->get_length((long*)&cNodes);
- ExitOnFailure(hr, "Failed to get overridable variable node count.");
-
- if (cNodes) {
- hr = DictCreateStringList(&_overridableVariables, 32, DICT_FLAG_NONE);
- ExitOnFailure(hr, "Failed to create the string dictionary.");
-
- for (DWORD i = 0; i < cNodes; ++i) {
- hr = XmlNextElement(pNodes, &pNode, nullptr);
- ExitOnFailure(hr, "Failed to get next node.");
-
- // @Name
- hr = XmlGetAttributeEx(pNode, L"Name", &scz);
- ExitOnFailure(hr, "Failed to get @Name.");
-
- hr = XmlGetYesNoAttribute(pNode, L"Hidden", &hidden);
-
- if (!hidden) {
- hr = DictAddKey(_overridableVariables, scz);
- ExitOnFailure1(hr, "Failed to add \"%ls\" to the string dictionary.", scz);
- }
-
- // prepare next iteration
- ReleaseNullObject(pNode);
- }
- }
-
- LExit:
- ReleaseObject(pNode);
- ReleaseObject(pNodes);
- ReleaseStr(scz);
- return hr;
- }
-
-
- //
- // Get the file version of the bootstrapper and record in bootstrapper log file
- //
- HRESULT GetBundleFileVersion() {
- HRESULT hr = S_OK;
- ULARGE_INTEGER uliVersion = { };
- LPWSTR sczCurrentPath = nullptr;
-
- hr = PathForCurrentProcess(&sczCurrentPath, nullptr);
- BalExitOnFailure(hr, "Failed to get bundle path.");
-
- hr = FileVersion(sczCurrentPath, &uliVersion.HighPart, &uliVersion.LowPart);
- BalExitOnFailure(hr, "Failed to get bundle file version.");
-
- hr = _engine->SetVariableVersion(PYBA_VARIABLE_BUNDLE_FILE_VERSION, uliVersion.QuadPart);
- BalExitOnFailure(hr, "Failed to set WixBundleFileVersion variable.");
-
- LExit:
- ReleaseStr(sczCurrentPath);
-
- return hr;
- }
-
-
- //
- // CreateMainWindow - creates the main install window.
- //
- HRESULT CreateMainWindow() {
- HRESULT hr = S_OK;
- HICON hIcon = reinterpret_cast<HICON>(_theme->hIcon);
- WNDCLASSW wc = { };
- DWORD dwWindowStyle = 0;
- int x = CW_USEDEFAULT;
- int y = CW_USEDEFAULT;
- POINT ptCursor = { };
- HMONITOR hMonitor = nullptr;
- MONITORINFO mi = { };
- COLORREF fg, bg;
- HBRUSH bgBrush;
-
- // If the theme did not provide an icon, try using the icon from the bundle engine.
- if (!hIcon) {
- HMODULE hBootstrapperEngine = ::GetModuleHandleW(nullptr);
- if (hBootstrapperEngine) {
- hIcon = ::LoadIconW(hBootstrapperEngine, MAKEINTRESOURCEW(1));
- }
- }
-
- fg = RGB(0, 0, 0);
- bg = RGB(255, 255, 255);
- bgBrush = (HBRUSH)(COLOR_WINDOW+1);
- if (_theme->dwFontId < _theme->cFonts) {
- THEME_FONT *font = &_theme->rgFonts[_theme->dwFontId];
- fg = font->crForeground;
- bg = font->crBackground;
- bgBrush = font->hBackground;
- RemapColor(&fg, &bg, &bgBrush);
- }
-
- // Register the window class and create the window.
- wc.lpfnWndProc = PythonBootstrapperApplication::WndProc;
- wc.hInstance = _hModule;
- wc.hIcon = hIcon;
- wc.hCursor = ::LoadCursorW(nullptr, (LPCWSTR)IDC_ARROW);
- wc.hbrBackground = bgBrush;
- wc.lpszMenuName = nullptr;
- wc.lpszClassName = PYBA_WINDOW_CLASS;
- if (!::RegisterClassW(&wc)) {
- ExitWithLastError(hr, "Failed to register window.");
- }
-
- _registered = TRUE;
-
- // Calculate the window style based on the theme style and command display value.
- dwWindowStyle = _theme->dwStyle;
- if (BOOTSTRAPPER_DISPLAY_NONE >= _command.display) {
- dwWindowStyle &= ~WS_VISIBLE;
- }
-
- // Don't show the window if there is a splash screen (it will be made visible when the splash screen is hidden)
- if (::IsWindow(_command.hwndSplashScreen)) {
- dwWindowStyle &= ~WS_VISIBLE;
- }
-
- // Center the window on the monitor with the mouse.
- if (::GetCursorPos(&ptCursor)) {
- hMonitor = ::MonitorFromPoint(ptCursor, MONITOR_DEFAULTTONEAREST);
- if (hMonitor) {
- mi.cbSize = sizeof(mi);
- if (::GetMonitorInfoW(hMonitor, &mi)) {
- x = mi.rcWork.left + (mi.rcWork.right - mi.rcWork.left - _theme->nWidth) / 2;
- y = mi.rcWork.top + (mi.rcWork.bottom - mi.rcWork.top - _theme->nHeight) / 2;
- }
- }
- }
-
- _hWnd = ::CreateWindowExW(
- 0,
- wc.lpszClassName,
- _theme->sczCaption,
- dwWindowStyle,
- x,
- y,
- _theme->nWidth,
- _theme->nHeight,
- HWND_DESKTOP,
- nullptr,
- _hModule,
- this
- );
- ExitOnNullWithLastError(_hWnd, hr, "Failed to create window.");
-
- hr = S_OK;
-
- LExit:
- return hr;
- }
-
-
- //
- // InitializeTaskbarButton - initializes taskbar button for progress.
- //
- void InitializeTaskbarButton() {
- HRESULT hr = S_OK;
-
- hr = ::CoCreateInstance(CLSID_TaskbarList, nullptr, CLSCTX_ALL, __uuidof(ITaskbarList3), reinterpret_cast<LPVOID*>(&_taskbarList));
- if (REGDB_E_CLASSNOTREG == hr) {
- // not supported before Windows 7
- ExitFunction1(hr = S_OK);
- }
- BalExitOnFailure(hr, "Failed to create ITaskbarList3. Continuing.");
-
- _taskbarButtonCreatedMessage = ::RegisterWindowMessageW(L"TaskbarButtonCreated");
- BalExitOnNullWithLastError(_taskbarButtonCreatedMessage, hr, "Failed to get TaskbarButtonCreated message. Continuing.");
-
- LExit:
- return;
- }
-
- //
- // DestroyMainWindow - clean up all the window registration.
- //
- void DestroyMainWindow() {
- if (::IsWindow(_hWnd)) {
- ::DestroyWindow(_hWnd);
- _hWnd = nullptr;
- _taskbarButtonOK = FALSE;
- }
-
- if (_registered) {
- ::UnregisterClassW(PYBA_WINDOW_CLASS, _hModule);
- _registered = FALSE;
- }
- }
-
-
- //
- // WndProc - standard windows message handler.
- //
- static LRESULT CALLBACK WndProc(
- __in HWND hWnd,
- __in UINT uMsg,
- __in WPARAM wParam,
- __in LPARAM lParam
- ) {
-#pragma warning(suppress:4312)
- auto pBA = reinterpret_cast<PythonBootstrapperApplication*>(::GetWindowLongPtrW(hWnd, GWLP_USERDATA));
-
- switch (uMsg) {
- case WM_NCCREATE: {
- LPCREATESTRUCT lpcs = reinterpret_cast<LPCREATESTRUCT>(lParam);
- pBA = reinterpret_cast<PythonBootstrapperApplication*>(lpcs->lpCreateParams);
-#pragma warning(suppress:4244)
- ::SetWindowLongPtrW(hWnd, GWLP_USERDATA, reinterpret_cast<LONG_PTR>(pBA));
- break;
- }
-
- case WM_NCDESTROY: {
- LRESULT lres = ThemeDefWindowProc(pBA ? pBA->_theme : nullptr, hWnd, uMsg, wParam, lParam);
- ::SetWindowLongPtrW(hWnd, GWLP_USERDATA, 0);
- return lres;
- }
-
- case WM_CREATE:
- if (!pBA->OnCreate(hWnd)) {
- return -1;
- }
- break;
-
- case WM_QUERYENDSESSION:
- return IDCANCEL != pBA->OnSystemShutdown(static_cast<DWORD>(lParam), IDCANCEL);
-
- case WM_CLOSE:
- // If the user chose not to close, do *not* let the default window proc handle the message.
- if (!pBA->OnClose()) {
- return 0;
- }
- break;
-
- case WM_DESTROY:
- ::PostQuitMessage(0);
- break;
-
- case WM_PAINT: __fallthrough;
- case WM_ERASEBKGND:
- if (pBA && pBA->_suppressPaint) {
- return TRUE;
- }
- break;
-
- case WM_PYBA_SHOW_HELP:
- pBA->OnShowHelp();
- return 0;
-
- case WM_PYBA_DETECT_PACKAGES:
- pBA->OnDetect();
- return 0;
-
- case WM_PYBA_PLAN_PACKAGES:
- pBA->OnPlan(static_cast<BOOTSTRAPPER_ACTION>(lParam));
- return 0;
-
- case WM_PYBA_APPLY_PACKAGES:
- pBA->OnApply();
- return 0;
-
- case WM_PYBA_CHANGE_STATE:
- pBA->OnChangeState(static_cast<PYBA_STATE>(lParam));
- return 0;
-
- case WM_PYBA_SHOW_FAILURE:
- pBA->OnShowFailure();
- return 0;
-
- case WM_COMMAND:
- switch (LOWORD(wParam)) {
- // Customize commands
- // Success/failure commands
- case ID_SUCCESS_RESTART_BUTTON: __fallthrough;
- case ID_FAILURE_RESTART_BUTTON:
- pBA->OnClickRestartButton();
- return 0;
-
- case IDCANCEL: __fallthrough;
- case ID_INSTALL_CANCEL_BUTTON: __fallthrough;
- case ID_CUSTOM1_CANCEL_BUTTON: __fallthrough;
- case ID_CUSTOM2_CANCEL_BUTTON: __fallthrough;
- case ID_MODIFY_CANCEL_BUTTON: __fallthrough;
- case ID_PROGRESS_CANCEL_BUTTON: __fallthrough;
- case ID_SUCCESS_CANCEL_BUTTON: __fallthrough;
- case ID_FAILURE_CANCEL_BUTTON: __fallthrough;
- case ID_CLOSE_BUTTON:
- pBA->OnCommand(ID_CLOSE_BUTTON);
- return 0;
-
- default:
- pBA->OnCommand((CONTROL_ID)LOWORD(wParam));
- }
- break;
-
- case WM_NOTIFY:
- if (lParam) {
- LPNMHDR pnmhdr = reinterpret_cast<LPNMHDR>(lParam);
- switch (pnmhdr->code) {
- case NM_CLICK: __fallthrough;
- case NM_RETURN:
- switch (static_cast<DWORD>(pnmhdr->idFrom)) {
- case ID_FAILURE_LOGFILE_LINK:
- pBA->OnClickLogFileLink();
- return 1;
- }
- }
- }
- break;
-
- case WM_CTLCOLORSTATIC:
- case WM_CTLCOLORBTN:
- if (pBA) {
- HBRUSH brush = nullptr;
- if (pBA->SetControlColor((HWND)lParam, (HDC)wParam, &brush)) {
- return (LRESULT)brush;
- }
- }
- break;
- }
-
- if (pBA && pBA->_taskbarList && uMsg == pBA->_taskbarButtonCreatedMessage) {
- pBA->_taskbarButtonOK = TRUE;
- return 0;
- }
-
- return ThemeDefWindowProc(pBA ? pBA->_theme : nullptr, hWnd, uMsg, wParam, lParam);
- }
-
- //
- // OnCreate - finishes loading the theme.
- //
- BOOL OnCreate(__in HWND hWnd) {
- HRESULT hr = S_OK;
-
- hr = ThemeLoadControls(_theme, hWnd, CONTROL_ID_NAMES, countof(CONTROL_ID_NAMES));
- BalExitOnFailure(hr, "Failed to load theme controls.");
-
- C_ASSERT(COUNT_PAGE == countof(PAGE_NAMES));
- C_ASSERT(countof(_pageIds) == countof(PAGE_NAMES));
-
- ThemeGetPageIds(_theme, PAGE_NAMES, _pageIds, countof(_pageIds));
-
- // Initialize the text on all "application" (non-page) controls.
- for (DWORD i = 0; i < _theme->cControls; ++i) {
- THEME_CONTROL* pControl = _theme->rgControls + i;
- LPWSTR text = nullptr;
-
- if (!pControl->wPageId && pControl->sczText && *pControl->sczText) {
- HRESULT hrFormat;
-
- // If the wix developer is showing a hidden variable in the UI,
- // then obviously they don't care about keeping it safe so don't
- // go down the rabbit hole of making sure that this is securely
- // freed.
- hrFormat = BalFormatString(pControl->sczText, &text);
- if (SUCCEEDED(hrFormat)) {
- ThemeSetTextControl(_theme, pControl->wId, text);
- ReleaseStr(text);
- }
- }
- }
-
- LExit:
- return SUCCEEDED(hr);
- }
-
- void RemapColor(COLORREF *fg, COLORREF *bg, HBRUSH *bgBrush) {
- if (*fg == RGB(0, 0, 0)) {
- *fg = GetSysColor(COLOR_WINDOWTEXT);
- } else if (*fg == RGB(128, 128, 128)) {
- *fg = GetSysColor(COLOR_GRAYTEXT);
- }
- if (*bgBrush && *bg == RGB(255, 255, 255)) {
- *bg = GetSysColor(COLOR_WINDOW);
- *bgBrush = GetSysColorBrush(COLOR_WINDOW);
- }
- }
-
- BOOL SetControlColor(HWND hWnd, HDC hDC, HBRUSH *brush) {
- for (int i = 0; i < _theme->cControls; ++i) {
- if (_theme->rgControls[i].hWnd != hWnd) {
- continue;
- }
-
- DWORD fontId = _theme->rgControls[i].dwFontId;
- if (fontId > _theme->cFonts) {
- fontId = 0;
- }
- THEME_FONT *fnt = &_theme->rgFonts[fontId];
-
- COLORREF fg = fnt->crForeground, bg = fnt->crBackground;
- *brush = fnt->hBackground;
- RemapColor(&fg, &bg, brush);
- ::SetTextColor(hDC, fg);
- ::SetBkColor(hDC, bg);
-
- return TRUE;
- }
- return FALSE;
- }
-
- //
- // OnShowFailure - display the failure page.
- //
- void OnShowFailure() {
- SetState(PYBA_STATE_FAILED, S_OK);
-
- // If the UI should be visible, display it now and hide the splash screen
- if (BOOTSTRAPPER_DISPLAY_NONE < _command.display) {
- ::ShowWindow(_theme->hwndParent, SW_SHOW);
- }
-
- _engine->CloseSplashScreen();
-
- return;
- }
-
-
- //
- // OnShowHelp - display the help page.
- //
- void OnShowHelp() {
- SetState(PYBA_STATE_HELP, S_OK);
-
- // If the UI should be visible, display it now and hide the splash screen
- if (BOOTSTRAPPER_DISPLAY_NONE < _command.display) {
- ::ShowWindow(_theme->hwndParent, SW_SHOW);
- }
-
- _engine->CloseSplashScreen();
-
- return;
- }
-
-
- //
- // OnDetect - start the processing of packages.
- //
- void OnDetect() {
- HRESULT hr = S_OK;
-
- if (_baFunction) {
- BalLog(BOOTSTRAPPER_LOG_LEVEL_STANDARD, "Running detect BA function");
- hr = _baFunction->OnDetect();
- BalExitOnFailure(hr, "Failed calling detect BA function.");
- }
-
- SetState(PYBA_STATE_DETECTING, hr);
-
- // If the UI should be visible, display it now and hide the splash screen
- if (BOOTSTRAPPER_DISPLAY_NONE < _command.display) {
- ::ShowWindow(_theme->hwndParent, SW_SHOW);
- }
-
- _engine->CloseSplashScreen();
-
- // Tell the core we're ready for the packages to be processed now.
- hr = _engine->Detect();
- BalExitOnFailure(hr, "Failed to start detecting chain.");
-
- LExit:
- if (FAILED(hr)) {
- SetState(PYBA_STATE_DETECTING, hr);
- }
-
- return;
- }
-
- HRESULT UpdateUIStrings(__in BOOTSTRAPPER_ACTION action) {
- HRESULT hr = S_OK;
- LPCWSTR likeInstalling = nullptr;
- LPCWSTR likeInstallation = nullptr;
- switch (action) {
- case BOOTSTRAPPER_ACTION_INSTALL:
- likeInstalling = L"Installing";
- likeInstallation = L"Installation";
- break;
- case BOOTSTRAPPER_ACTION_MODIFY:
- // For modify, we actually want to pass INSTALL
- action = BOOTSTRAPPER_ACTION_INSTALL;
- likeInstalling = L"Modifying";
- likeInstallation = L"Modification";
- break;
- case BOOTSTRAPPER_ACTION_REPAIR:
- likeInstalling = L"Repairing";
- likeInstallation = L"Repair";
- break;
- case BOOTSTRAPPER_ACTION_UNINSTALL:
- likeInstalling = L"Uninstalling";
- likeInstallation = L"Uninstallation";
- break;
- }
-
- if (likeInstalling) {
- LPWSTR locName = nullptr;
- LOC_STRING *locText = nullptr;
- hr = StrAllocFormatted(&locName, L"#(loc.%ls)", likeInstalling);
- if (SUCCEEDED(hr)) {
- hr = LocGetString(_wixLoc, locName, &locText);
- ReleaseStr(locName);
- }
- _engine->SetVariableString(
- L"ActionLikeInstalling",
- SUCCEEDED(hr) && locText ? locText->wzText : likeInstalling
- );
- }
-
- if (likeInstallation) {
- LPWSTR locName = nullptr;
- LOC_STRING *locText = nullptr;
- hr = StrAllocFormatted(&locName, L"#(loc.%ls)", likeInstallation);
- if (SUCCEEDED(hr)) {
- hr = LocGetString(_wixLoc, locName, &locText);
- ReleaseStr(locName);
- }
- _engine->SetVariableString(
- L"ActionLikeInstallation",
- SUCCEEDED(hr) && locText ? locText->wzText : likeInstallation
- );
- }
- return hr;
- }
-
- //
- // OnPlan - plan the detected changes.
- //
- void OnPlan(__in BOOTSTRAPPER_ACTION action) {
- HRESULT hr = S_OK;
-
- _plannedAction = action;
-
- hr = UpdateUIStrings(action);
- BalExitOnFailure(hr, "Failed to update strings");
-
- // If we are going to apply a downgrade, bail.
- if (_downgradingOtherVersion && BOOTSTRAPPER_ACTION_UNINSTALL < action) {
- if (_suppressDowngradeFailure) {
- BalLog(BOOTSTRAPPER_LOG_LEVEL_STANDARD, "A newer version of this product is installed but downgrade failure has been suppressed; continuing...");
- } else {
- hr = HRESULT_FROM_WIN32(ERROR_PRODUCT_VERSION);
- BalExitOnFailure(hr, "Cannot install a product when a newer version is installed.");
- }
- }
-
- SetState(PYBA_STATE_PLANNING, hr);
-
- if (_baFunction) {
- BalLog(BOOTSTRAPPER_LOG_LEVEL_STANDARD, "Running plan BA function");
- _baFunction->OnPlan();
- }
-
- hr = _engine->Plan(action);
- BalExitOnFailure(hr, "Failed to start planning packages.");
-
- LExit:
- if (FAILED(hr)) {
- SetState(PYBA_STATE_PLANNING, hr);
- }
-
- return;
- }
-
-
- //
- // OnApply - apply the packages.
- //
- void OnApply() {
- HRESULT hr = S_OK;
-
- SetState(PYBA_STATE_APPLYING, hr);
- SetProgressState(hr);
- SetTaskbarButtonProgress(0);
-
- hr = _engine->Apply(_hWnd);
- BalExitOnFailure(hr, "Failed to start applying packages.");
-
- ThemeControlEnable(_theme, ID_PROGRESS_CANCEL_BUTTON, TRUE); // ensure the cancel button is enabled before starting.
-
- LExit:
- if (FAILED(hr)) {
- SetState(PYBA_STATE_APPLYING, hr);
- }
-
- return;
- }
-
-
- //
- // OnChangeState - change state.
- //
- void OnChangeState(__in PYBA_STATE state) {
- LPWSTR unformattedText = nullptr;
-
- _state = state;
-
- // If our install is at the end (success or failure) and we're not showing full UI
- // then exit (prompt for restart if required).
- if ((PYBA_STATE_APPLIED <= _state && BOOTSTRAPPER_DISPLAY_FULL > _command.display)) {
- // If a restart was required but we were not automatically allowed to
- // accept the reboot then do the prompt.
- if (_restartRequired && !_allowRestart) {
- StrAllocFromError(&unformattedText, HRESULT_FROM_WIN32(ERROR_SUCCESS_REBOOT_REQUIRED), nullptr);
-
- _allowRestart = IDOK == ::MessageBoxW(
- _hWnd,
- unformattedText ? unformattedText : L"The requested operation is successful. Changes will not be effective until the system is rebooted.",
- _theme->sczCaption,
- MB_ICONEXCLAMATION | MB_OKCANCEL
- );
- }
-
- // Quietly exit.
- ::PostMessageW(_hWnd, WM_CLOSE, 0, 0);
- } else { // try to change the pages.
- DWORD newPageId = 0;
- DeterminePageId(_state, &newPageId);
-
- if (_visiblePageId != newPageId) {
- ShowPage(newPageId);
- }
- }
-
- ReleaseStr(unformattedText);
- }
-
- //
- // Called before showing a page to handle all controls.
- //
- void ProcessPageControls(THEME_PAGE *pPage) {
- if (!pPage) {
- return;
- }
-
- for (DWORD i = 0; i < pPage->cControlIndices; ++i) {
- THEME_CONTROL* pControl = _theme->rgControls + pPage->rgdwControlIndices[i];
- BOOL enableControl = TRUE;
-
- // If this is a named control, try to set its default state.
- if (pControl->sczName && *pControl->sczName) {
- // If this is a checkable control, try to set its default state
- // to the state of a matching named Burn variable.
- if (IsCheckable(pControl)) {
- LONGLONG llValue = 0;
- HRESULT hr = BalGetNumericVariable(pControl->sczName, &llValue);
-
- // If the control value isn't set then disable it.
- if (!SUCCEEDED(hr)) {
- enableControl = FALSE;
- } else {
- ThemeSendControlMessage(
- _theme,
- pControl->wId,
- BM_SETCHECK,
- SUCCEEDED(hr) && llValue ? BST_CHECKED : BST_UNCHECKED,
- 0
- );
- }
- }
-
- // Hide or disable controls based on the control name with 'State' appended
- LPWSTR controlName = nullptr;
- HRESULT hr = StrAllocFormatted(&controlName, L"%lsState", pControl->sczName);
- if (SUCCEEDED(hr)) {
- LPWSTR controlState = nullptr;
- hr = BalGetStringVariable(controlName, &controlState);
- if (SUCCEEDED(hr) && controlState && *controlState) {
- if (controlState[0] == '[') {
- LPWSTR formatted = nullptr;
- if (SUCCEEDED(BalFormatString(controlState, &formatted))) {
- StrFree(controlState);
- controlState = formatted;
- }
- }
-
- if (CSTR_EQUAL == ::CompareStringW(LOCALE_NEUTRAL, 0, controlState, -1, L"disable", -1)) {
- BalLog(BOOTSTRAPPER_LOG_LEVEL_STANDARD, "Disable control %ls", pControl->sczName);
- enableControl = FALSE;
- } else if (CSTR_EQUAL == ::CompareStringW(LOCALE_NEUTRAL, 0, controlState, -1, L"hide", -1)) {
- BalLog(BOOTSTRAPPER_LOG_LEVEL_STANDARD, "Hide control %ls", pControl->sczName);
- // TODO: This doesn't work
- ThemeShowControl(_theme, pControl->wId, SW_HIDE);
- } else {
- // An explicit state can override the lack of a
- // backing variable.
- enableControl = TRUE;
- }
- }
- StrFree(controlState);
- }
- StrFree(controlName);
- controlName = nullptr;
-
-
- // If a command link has a note, then add it.
- if ((pControl->dwStyle & BS_TYPEMASK) == BS_COMMANDLINK ||
- (pControl->dwStyle & BS_TYPEMASK) == BS_DEFCOMMANDLINK) {
- hr = StrAllocFormatted(&controlName, L"#(loc.%lsNote)", pControl->sczName);
- if (SUCCEEDED(hr)) {
- LOC_STRING *locText = nullptr;
- hr = LocGetString(_wixLoc, controlName, &locText);
- if (SUCCEEDED(hr) && locText && locText->wzText && locText->wzText[0]) {
- LPWSTR text = nullptr;
- hr = BalFormatString(locText->wzText, &text);
- if (SUCCEEDED(hr) && text && text[0]) {
- ThemeSendControlMessage(_theme, pControl->wId, BCM_SETNOTE, 0, (LPARAM)text);
- ReleaseStr(text);
- text = nullptr;
- }
- }
- ReleaseStr(controlName);
- controlName = nullptr;
- }
- hr = S_OK;
- }
- }
-
- ThemeControlEnable(_theme, pControl->wId, enableControl);
-
- // Format the text in each of the new page's controls
- if (pControl->sczText && *pControl->sczText) {
- // If the wix developer is showing a hidden variable
- // in the UI, then obviously they don't care about
- // keeping it safe so don't go down the rabbit hole
- // of making sure that this is securely freed.
- LPWSTR text = nullptr;
- HRESULT hr = BalFormatString(pControl->sczText, &text);
- if (SUCCEEDED(hr)) {
- ThemeSetTextControl(_theme, pControl->wId, text);
- }
- }
- }
- }
-
- //
- // OnClose - called when the window is trying to be closed.
- //
- BOOL OnClose() {
- BOOL close = FALSE;
-
- // If we've already succeeded or failed or showing the help page, just close (prompts are annoying if the bootstrapper is done).
- if (PYBA_STATE_APPLIED <= _state || PYBA_STATE_HELP == _state) {
- close = TRUE;
- } else {
- // prompt the user or force the cancel if there is no UI.
- close = PromptCancel(
- _hWnd,
- BOOTSTRAPPER_DISPLAY_FULL != _command.display,
- _confirmCloseMessage ? _confirmCloseMessage : L"Are you sure you want to cancel?",
- _theme->sczCaption
- );
- }
-
- // If we're doing progress then we never close, we just cancel to let rollback occur.
- if (PYBA_STATE_APPLYING <= _state && PYBA_STATE_APPLIED > _state) {
- // If we canceled disable cancel button since clicking it again is silly.
- if (close) {
- ThemeControlEnable(_theme, ID_PROGRESS_CANCEL_BUTTON, FALSE);
- }
-
- close = FALSE;
- }
-
- return close;
- }
-
- //
- // OnClickCloseButton - close the application.
- //
- void OnClickCloseButton() {
- ::SendMessageW(_hWnd, WM_CLOSE, 0, 0);
- }
-
-
-
- //
- // OnClickRestartButton - allows the restart and closes the app.
- //
- void OnClickRestartButton() {
- AssertSz(_restartRequired, "Restart must be requested to be able to click on the restart button.");
-
- _allowRestart = TRUE;
- ::SendMessageW(_hWnd, WM_CLOSE, 0, 0);
-
- return;
- }
-
-
- //
- // OnClickLogFileLink - show the log file.
- //
- void OnClickLogFileLink() {
- HRESULT hr = S_OK;
- LPWSTR sczLogFile = nullptr;
-
- hr = BalGetStringVariable(_bundle.sczLogVariable, &sczLogFile);
- BalExitOnFailure1(hr, "Failed to get log file variable '%ls'.", _bundle.sczLogVariable);
-
- hr = ShelExec(L"notepad.exe", sczLogFile, L"open", nullptr, SW_SHOWDEFAULT, _hWnd, nullptr);
- BalExitOnFailure1(hr, "Failed to open log file target: %ls", sczLogFile);
-
- LExit:
- ReleaseStr(sczLogFile);
-
- return;
- }
-
-
- //
- // SetState
- //
- void SetState(__in PYBA_STATE state, __in HRESULT hrStatus) {
- if (FAILED(hrStatus)) {
- _hrFinal = hrStatus;
- }
-
- if (FAILED(_hrFinal)) {
- state = PYBA_STATE_FAILED;
- }
-
- if (_state != state) {
- ::PostMessageW(_hWnd, WM_PYBA_CHANGE_STATE, 0, state);
- }
- }
-
- //
- // GoToPage
- //
- void GoToPage(__in PAGE page) {
- _installPage = page;
- ::PostMessageW(_hWnd, WM_PYBA_CHANGE_STATE, 0, _state);
- }
-
- void DeterminePageId(__in PYBA_STATE state, __out DWORD* pdwPageId) {
- LONGLONG simple;
-
- if (BOOTSTRAPPER_DISPLAY_PASSIVE == _command.display) {
- switch (state) {
- case PYBA_STATE_INITIALIZED:
- *pdwPageId = BOOTSTRAPPER_ACTION_HELP == _command.action
- ? _pageIds[PAGE_HELP]
- : _pageIds[PAGE_LOADING];
- break;
-
- case PYBA_STATE_HELP:
- *pdwPageId = _pageIds[PAGE_HELP];
- break;
-
- case PYBA_STATE_DETECTING:
- *pdwPageId = _pageIds[PAGE_LOADING]
- ? _pageIds[PAGE_LOADING]
- : _pageIds[PAGE_PROGRESS_PASSIVE]
- ? _pageIds[PAGE_PROGRESS_PASSIVE]
- : _pageIds[PAGE_PROGRESS];
- break;
-
- case PYBA_STATE_DETECTED: __fallthrough;
- case PYBA_STATE_PLANNING: __fallthrough;
- case PYBA_STATE_PLANNED: __fallthrough;
- case PYBA_STATE_APPLYING: __fallthrough;
- case PYBA_STATE_CACHING: __fallthrough;
- case PYBA_STATE_CACHED: __fallthrough;
- case PYBA_STATE_EXECUTING: __fallthrough;
- case PYBA_STATE_EXECUTED:
- *pdwPageId = _pageIds[PAGE_PROGRESS_PASSIVE]
- ? _pageIds[PAGE_PROGRESS_PASSIVE]
- : _pageIds[PAGE_PROGRESS];
- break;
-
- default:
- *pdwPageId = 0;
- break;
- }
- } else if (BOOTSTRAPPER_DISPLAY_FULL == _command.display) {
- switch (state) {
- case PYBA_STATE_INITIALIZING:
- *pdwPageId = 0;
- break;
-
- case PYBA_STATE_INITIALIZED:
- *pdwPageId = BOOTSTRAPPER_ACTION_HELP == _command.action
- ? _pageIds[PAGE_HELP]
- : _pageIds[PAGE_LOADING];
- break;
-
- case PYBA_STATE_HELP:
- *pdwPageId = _pageIds[PAGE_HELP];
- break;
-
- case PYBA_STATE_DETECTING:
- *pdwPageId = _pageIds[PAGE_LOADING];
- break;
-
- case PYBA_STATE_DETECTED:
- if (_installPage == PAGE_LOADING) {
- switch (_command.action) {
- case BOOTSTRAPPER_ACTION_INSTALL:
- if (_upgrading) {
- _installPage = PAGE_UPGRADE;
- } else if (SUCCEEDED(BalGetNumericVariable(L"SimpleInstall", &simple)) && simple) {
- _installPage = PAGE_SIMPLE_INSTALL;
- } else {
- _installPage = PAGE_INSTALL;
- }
- break;
-
- case BOOTSTRAPPER_ACTION_MODIFY: __fallthrough;
- case BOOTSTRAPPER_ACTION_REPAIR: __fallthrough;
- case BOOTSTRAPPER_ACTION_UNINSTALL:
- _installPage = PAGE_MODIFY;
- break;
- }
- }
- *pdwPageId = _pageIds[_installPage];
- break;
-
- case PYBA_STATE_PLANNING: __fallthrough;
- case PYBA_STATE_PLANNED: __fallthrough;
- case PYBA_STATE_APPLYING: __fallthrough;
- case PYBA_STATE_CACHING: __fallthrough;
- case PYBA_STATE_CACHED: __fallthrough;
- case PYBA_STATE_EXECUTING: __fallthrough;
- case PYBA_STATE_EXECUTED:
- *pdwPageId = _pageIds[PAGE_PROGRESS];
- break;
-
- case PYBA_STATE_APPLIED:
- *pdwPageId = _pageIds[PAGE_SUCCESS];
- break;
-
- case PYBA_STATE_FAILED:
- *pdwPageId = _pageIds[PAGE_FAILURE];
- break;
- }
- }
- }
-
- BOOL WillElevate() {
- static BAL_CONDITION WILL_ELEVATE_CONDITION = {
- L"not WixBundleElevated and ("
- /*Elevate when installing for all users*/
- L"InstallAllUsers or "
- /*Elevate when installing the launcher for all users and it was not detected*/
- L"(Include_launcher and InstallLauncherAllUsers and not DetectedLauncher)"
- L")",
- L""
- };
- BOOL result;
-
- return SUCCEEDED(BalConditionEvaluate(&WILL_ELEVATE_CONDITION, _engine, &result, nullptr)) && result;
- }
-
- BOOL IsCrtInstalled() {
- if (_crtInstalledToken > 0) {
- return TRUE;
- } else if (_crtInstalledToken == 0) {
- return FALSE;
- }
-
- // Check whether at least CRT v10.0.10137.0 is available.
- // It should only be installed as a Windows Update package, which means
- // we don't need to worry about 32-bit/64-bit.
- LPCWSTR crtFile = L"ucrtbase.dll";
-
- DWORD cbVer = GetFileVersionInfoSizeW(crtFile, nullptr);
- if (!cbVer) {
- _crtInstalledToken = 0;
- return FALSE;
- }
-
- void *pData = malloc(cbVer);
- if (!pData) {
- _crtInstalledToken = 0;
- return FALSE;
- }
-
- if (!GetFileVersionInfoW(crtFile, 0, cbVer, pData)) {
- free(pData);
- _crtInstalledToken = 0;
- return FALSE;
- }
-
- VS_FIXEDFILEINFO *ffi;
- UINT cb;
- BOOL result = FALSE;
-
- if (VerQueryValueW(pData, L"\\", (LPVOID*)&ffi, &cb) &&
- ffi->dwFileVersionMS == 0x000A0000 && ffi->dwFileVersionLS >= 0x27990000) {
- result = TRUE;
- }
-
- free(pData);
- _crtInstalledToken = result ? 1 : 0;
- return result;
- }
-
- HRESULT EvaluateConditions() {
- HRESULT hr = S_OK;
- BOOL result = FALSE;
-
- for (DWORD i = 0; i < _conditions.cConditions; ++i) {
- BAL_CONDITION* pCondition = _conditions.rgConditions + i;
-
- hr = BalConditionEvaluate(pCondition, _engine, &result, &_failedMessage);
- BalExitOnFailure(hr, "Failed to evaluate condition.");
-
- if (!result) {
- // Hope they didn't have hidden variables in their message, because it's going in the log in plaintext.
- BalLog(BOOTSTRAPPER_LOG_LEVEL_ERROR, "%ls", _failedMessage);
-
- hr = E_WIXSTDBA_CONDITION_FAILED;
- // todo: remove in WiX v4, in case people are relying on v3.x logging behavior
- BalExitOnFailure1(hr, "Bundle condition evaluated to false: %ls", pCondition->sczCondition);
- }
- }
-
- ReleaseNullStrSecure(_failedMessage);
-
- LExit:
- return hr;
- }
-
-
- void SetTaskbarButtonProgress(__in DWORD dwOverallPercentage) {
- HRESULT hr = S_OK;
-
- if (_taskbarButtonOK) {
- hr = _taskbarList->SetProgressValue(_hWnd, dwOverallPercentage, 100UL);
- BalExitOnFailure1(hr, "Failed to set taskbar button progress to: %d%%.", dwOverallPercentage);
- }
-
- LExit:
- return;
- }
-
-
- void SetTaskbarButtonState(__in TBPFLAG tbpFlags) {
- HRESULT hr = S_OK;
-
- if (_taskbarButtonOK) {
- hr = _taskbarList->SetProgressState(_hWnd, tbpFlags);
- BalExitOnFailure1(hr, "Failed to set taskbar button state.", tbpFlags);
- }
-
- LExit:
- return;
- }
-
-
- void SetProgressState(__in HRESULT hrStatus) {
- TBPFLAG flag = TBPF_NORMAL;
-
- if (IsCanceled() || HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT) == hrStatus) {
- flag = TBPF_PAUSED;
- } else if (IsRollingBack() || FAILED(hrStatus)) {
- flag = TBPF_ERROR;
- }
-
- SetTaskbarButtonState(flag);
- }
-
-
- HRESULT LoadBootstrapperBAFunctions() {
- HRESULT hr = S_OK;
- LPWSTR sczBafPath = nullptr;
-
- hr = PathRelativeToModule(&sczBafPath, L"bafunctions.dll", _hModule);
- BalExitOnFailure(hr, "Failed to get path to BA function DLL.");
-
-#ifdef DEBUG
- BalLog(BOOTSTRAPPER_LOG_LEVEL_STANDARD, "PYBA: LoadBootstrapperBAFunctions() - BA function DLL %ls", sczBafPath);
-#endif
-
- _hBAFModule = ::LoadLibraryW(sczBafPath);
- if (_hBAFModule) {
- auto pfnBAFunctionCreate = reinterpret_cast<PFN_BOOTSTRAPPER_BA_FUNCTION_CREATE>(::GetProcAddress(_hBAFModule, "CreateBootstrapperBAFunction"));
- BalExitOnNullWithLastError1(pfnBAFunctionCreate, hr, "Failed to get CreateBootstrapperBAFunction entry-point from: %ls", sczBafPath);
-
- hr = pfnBAFunctionCreate(_engine, _hBAFModule, &_baFunction);
- BalExitOnFailure(hr, "Failed to create BA function.");
- }
-#ifdef DEBUG
- else {
- BalLogError(HRESULT_FROM_WIN32(::GetLastError()), "PYBA: LoadBootstrapperBAFunctions() - Failed to load DLL %ls", sczBafPath);
- }
-#endif
-
- LExit:
- if (_hBAFModule && !_baFunction) {
- ::FreeLibrary(_hBAFModule);
- _hBAFModule = nullptr;
- }
- ReleaseStr(sczBafPath);
-
- return hr;
- }
-
- BOOL IsCheckable(THEME_CONTROL* pControl) {
- if (!pControl->sczName || !pControl->sczName[0]) {
- return FALSE;
- }
-
- if (pControl->type == THEME_CONTROL_TYPE_CHECKBOX) {
- return TRUE;
- }
-
- if (pControl->type == THEME_CONTROL_TYPE_BUTTON) {
- if ((pControl->dwStyle & BS_TYPEMASK) == BS_AUTORADIOBUTTON) {
- return TRUE;
- }
- }
-
- return FALSE;
- }
-
- void SavePageSettings() {
- DWORD pageId = 0;
- THEME_PAGE* pPage = nullptr;
-
- DeterminePageId(_state, &pageId);
- pPage = ThemeGetPage(_theme, pageId);
- if (!pPage) {
- return;
- }
-
- for (DWORD i = 0; i < pPage->cControlIndices; ++i) {
- // Loop through all the checkable controls and set a Burn variable
- // with that name to true or false.
- THEME_CONTROL* pControl = _theme->rgControls + pPage->rgdwControlIndices[i];
- if (IsCheckable(pControl) && ThemeControlEnabled(_theme, pControl->wId)) {
- BOOL checked = ThemeIsControlChecked(_theme, pControl->wId);
- _engine->SetVariableNumeric(pControl->sczName, checked ? 1 : 0);
- }
-
- // Loop through all the editbox controls with names and set a
- // Burn variable with that name to the contents.
- if (THEME_CONTROL_TYPE_EDITBOX == pControl->type && pControl->sczName && *pControl->sczName) {
- LPWSTR sczValue = nullptr;
- ThemeGetTextControl(_theme, pControl->wId, &sczValue);
- _engine->SetVariableString(pControl->sczName, sczValue);
- }
- }
- }
-
- static bool IsTargetPlatformx64(__in IBootstrapperEngine* pEngine) {
- WCHAR platform[8];
- DWORD platformLen = 8;
-
- if (FAILED(pEngine->GetVariableString(L"TargetPlatform", platform, &platformLen))) {
- return S_FALSE;
- }
-
- return ::CompareStringW(LOCALE_NEUTRAL, 0, platform, -1, L"x64", -1) == CSTR_EQUAL;
- }
-
- static HRESULT LoadOptionalFeatureStatesFromKey(
- __in IBootstrapperEngine* pEngine,
- __in HKEY hkHive,
- __in LPCWSTR subkey
- ) {
- HKEY hKey;
- LRESULT res;
-
- if (IsTargetPlatformx64(pEngine)) {
- res = RegOpenKeyExW(hkHive, subkey, 0, KEY_READ | KEY_WOW64_64KEY, &hKey);
- } else {
- res = RegOpenKeyExW(hkHive, subkey, 0, KEY_READ | KEY_WOW64_32KEY, &hKey);
- }
- if (res == ERROR_FILE_NOT_FOUND) {
- return S_FALSE;
- }
- if (res != ERROR_SUCCESS) {
- return HRESULT_FROM_WIN32(res);
- }
-
- for (auto p = OPTIONAL_FEATURES; p->regName; ++p) {
- res = RegQueryValueExW(hKey, p->regName, nullptr, nullptr, nullptr, nullptr);
- if (res == ERROR_FILE_NOT_FOUND) {
- pEngine->SetVariableNumeric(p->variableName, 0);
- } else if (res == ERROR_SUCCESS) {
- pEngine->SetVariableNumeric(p->variableName, 1);
- } else {
- RegCloseKey(hKey);
- return HRESULT_FROM_WIN32(res);
- }
- }
-
- RegCloseKey(hKey);
- return S_OK;
- }
-
- static HRESULT LoadTargetDirFromKey(
- __in IBootstrapperEngine* pEngine,
- __in HKEY hkHive,
- __in LPCWSTR subkey
- ) {
- HKEY hKey;
- LRESULT res;
- DWORD dataType;
- BYTE buffer[1024];
- DWORD bufferLen = sizeof(buffer);
-
- if (IsTargetPlatformx64(pEngine)) {
- res = RegOpenKeyExW(hkHive, subkey, 0, KEY_READ | KEY_WOW64_64KEY, &hKey);
- } else {
- res = RegOpenKeyExW(hkHive, subkey, 0, KEY_READ | KEY_WOW64_32KEY, &hKey);
- }
- if (res == ERROR_FILE_NOT_FOUND) {
- return S_FALSE;
- }
- if (res != ERROR_SUCCESS) {
- return HRESULT_FROM_WIN32(res);
- }
-
- res = RegQueryValueExW(hKey, nullptr, nullptr, &dataType, buffer, &bufferLen);
- if (res == ERROR_SUCCESS && dataType == REG_SZ && bufferLen < sizeof(buffer)) {
- pEngine->SetVariableString(L"TargetDir", reinterpret_cast<wchar_t*>(buffer));
- }
- RegCloseKey(hKey);
- return HRESULT_FROM_WIN32(res);
- }
-
- static HRESULT LoadAssociateFilesStateFromKey(
- __in IBootstrapperEngine* pEngine,
- __in HKEY hkHive
- ) {
- const LPCWSTR subkey = L"Software\\Python\\PyLauncher";
- HKEY hKey;
- LRESULT res;
- HRESULT hr;
-
- res = RegOpenKeyExW(hkHive, subkey, 0, KEY_READ | KEY_WOW64_32KEY, &hKey);
-
- if (res == ERROR_FILE_NOT_FOUND) {
- return S_FALSE;
- }
- if (res != ERROR_SUCCESS) {
- return HRESULT_FROM_WIN32(res);
- }
-
- res = RegQueryValueExW(hKey, L"AssociateFiles", nullptr, nullptr, nullptr, nullptr);
- if (res == ERROR_FILE_NOT_FOUND) {
- hr = S_FALSE;
- } else if (res == ERROR_SUCCESS) {
- hr = S_OK;
- } else {
- hr = HRESULT_FROM_WIN32(res);
- }
-
- RegCloseKey(hKey);
- return hr;
- }
-
- static void LoadOptionalFeatureStates(__in IBootstrapperEngine* pEngine) {
- WCHAR subkeyFmt[256];
- WCHAR subkey[256];
- DWORD subkeyLen;
- HRESULT hr;
- HKEY hkHive;
-
- // The launcher installation is separate from the Python install, so we
- // check its state later. For now, assume we don't want the launcher or
- // file associations, and if they have already been installed then
- // loading the state will reactivate these settings.
- pEngine->SetVariableNumeric(L"Include_launcher", 0);
- pEngine->SetVariableNumeric(L"AssociateFiles", 0);
-
- // Get the registry key from the bundle, to save having to duplicate it
- // in multiple places.
- subkeyLen = sizeof(subkeyFmt) / sizeof(subkeyFmt[0]);
- hr = pEngine->GetVariableString(L"OptionalFeaturesRegistryKey", subkeyFmt, &subkeyLen);
- BalExitOnFailure(hr, "Failed to locate registry key");
- subkeyLen = sizeof(subkey) / sizeof(subkey[0]);
- hr = pEngine->FormatString(subkeyFmt, subkey, &subkeyLen);
- BalExitOnFailure1(hr, "Failed to format %ls", subkeyFmt);
-
- // Check the current user's registry for existing features
- hkHive = HKEY_CURRENT_USER;
- hr = LoadOptionalFeatureStatesFromKey(pEngine, hkHive, subkey);
- BalExitOnFailure1(hr, "Failed to read from HKCU\\%ls", subkey);
- if (hr == S_FALSE) {
- // Now check the local machine registry
- hkHive = HKEY_LOCAL_MACHINE;
- hr = LoadOptionalFeatureStatesFromKey(pEngine, hkHive, subkey);
- BalExitOnFailure1(hr, "Failed to read from HKLM\\%ls", subkey);
- if (hr == S_OK) {
- // Found a system-wide install, so enable these settings.
- pEngine->SetVariableNumeric(L"InstallAllUsers", 1);
- pEngine->SetVariableNumeric(L"CompileAll", 1);
- }
- }
-
- if (hr == S_OK) {
- // Cannot change InstallAllUsersState when upgrading. While there's
- // no good reason to not allow installing a per-user and an all-user
- // version simultaneously, Burn can't handle the state management
- // and will need to uninstall the old one.
- pEngine->SetVariableString(L"InstallAllUsersState", L"disable");
-
- // Get the previous install directory. This can be changed by the
- // user.
- subkeyLen = sizeof(subkeyFmt) / sizeof(subkeyFmt[0]);
- hr = pEngine->GetVariableString(L"TargetDirRegistryKey", subkeyFmt, &subkeyLen);
- BalExitOnFailure(hr, "Failed to locate registry key");
- subkeyLen = sizeof(subkey) / sizeof(subkey[0]);
- hr = pEngine->FormatString(subkeyFmt, subkey, &subkeyLen);
- BalExitOnFailure1(hr, "Failed to format %ls", subkeyFmt);
- LoadTargetDirFromKey(pEngine, hkHive, subkey);
- }
-
- LExit:
- return;
- }
-
- HRESULT EnsureTargetDir() {
- LONGLONG installAllUsers;
- LPWSTR targetDir = nullptr, defaultDir = nullptr;
- HRESULT hr = BalGetStringVariable(L"TargetDir", &targetDir);
- if (FAILED(hr) || !targetDir || !targetDir[0]) {
- ReleaseStr(targetDir);
- targetDir = nullptr;
-
- hr = BalGetNumericVariable(L"InstallAllUsers", &installAllUsers);
- ExitOnFailure(hr, L"Failed to get install scope");
-
- hr = BalGetStringVariable(
- installAllUsers ? L"DefaultAllUsersTargetDir" : L"DefaultJustForMeTargetDir",
- &defaultDir
- );
- BalExitOnFailure(hr, "Failed to get the default install directory");
-
- if (!defaultDir || !defaultDir[0]) {
- BalLogError(E_INVALIDARG, "Default install directory is blank");
- }
-
- hr = BalFormatString(defaultDir, &targetDir);
- BalExitOnFailure1(hr, "Failed to format '%ls'", defaultDir);
-
- hr = _engine->SetVariableString(L"TargetDir", targetDir);
- BalExitOnFailure(hr, "Failed to set install target directory");
- }
- LExit:
- ReleaseStr(defaultDir);
- ReleaseStr(targetDir);
- return hr;
- }
-
- void ValidateOperatingSystem() {
- LOC_STRING *pLocString = nullptr;
-
- if (IsWindowsServer()) {
- if (IsWindowsVersionOrGreater(6, 2, 0)) {
- BalLog(BOOTSTRAPPER_LOG_LEVEL_STANDARD, "Target OS is Windows Server 2012 or later");
- return;
- } else if (IsWindowsVersionOrGreater(6, 1, 1)) {
- HMODULE hKernel32 = GetModuleHandleW(L"kernel32");
- if (hKernel32 && !GetProcAddress(hKernel32, "AddDllDirectory")) {
- BalLog(BOOTSTRAPPER_LOG_LEVEL_ERROR, "Detected Windows Server 2008 R2 without KB2533623");
- BalLog(BOOTSTRAPPER_LOG_LEVEL_ERROR, "KB2533623 update is required to continue.");
- /* The "MissingSP1" error also specifies updates are required */
- LocGetString(_wixLoc, L"#(loc.FailureWS2K8R2MissingSP1)", &pLocString);
- } else {
- BalLog(BOOTSTRAPPER_LOG_LEVEL_STANDARD, "Target OS is Windows Server 2008 R2 or later");
- return;
- }
- } else if (IsWindowsVersionOrGreater(6, 1, 0)) {
- BalLog(BOOTSTRAPPER_LOG_LEVEL_ERROR, "Detected Windows Server 2008 R2");
- BalLog(BOOTSTRAPPER_LOG_LEVEL_ERROR, "Service Pack 1 is required to continue installation");
- LocGetString(_wixLoc, L"#(loc.FailureWS2K8R2MissingSP1)", &pLocString);
- } else if (IsWindowsVersionOrGreater(6, 0, 2)) {
- BalLog(BOOTSTRAPPER_LOG_LEVEL_ERROR, "Target OS is Windows Server 2008 SP2 or later");
- return;
- } else if (IsWindowsVersionOrGreater(6, 0, 0)) {
- BalLog(BOOTSTRAPPER_LOG_LEVEL_ERROR, "Detected Windows Server 2008");
- BalLog(BOOTSTRAPPER_LOG_LEVEL_ERROR, "Service Pack 2 is required to continue installation");
- LocGetString(_wixLoc, L"#(loc.FailureWS2K8MissingSP2)", &pLocString);
- } else {
- BalLog(BOOTSTRAPPER_LOG_LEVEL_ERROR, "Detected Windows Server 2003 or earlier");
- BalLog(BOOTSTRAPPER_LOG_LEVEL_ERROR, "Windows Server 2008 SP2 or later is required to continue installation");
- LocGetString(_wixLoc, L"#(loc.FailureWS2K3OrEarlier)", &pLocString);
- }
- } else {
- if (IsWindows8OrGreater()) {
- BalLog(BOOTSTRAPPER_LOG_LEVEL_STANDARD, "Target OS is Windows 8 or later");
- return;
- } else if (IsWindows7SP1OrGreater()) {
- HMODULE hKernel32 = GetModuleHandleW(L"kernel32");
- if (hKernel32 && !GetProcAddress(hKernel32, "AddDllDirectory")) {
- BalLog(BOOTSTRAPPER_LOG_LEVEL_ERROR, "Detected Windows 7 SP1 without KB2533623");
- BalLog(BOOTSTRAPPER_LOG_LEVEL_ERROR, "KB2533623 update is required to continue.");
- /* The "MissingSP1" error also specifies updates are required */
- LocGetString(_wixLoc, L"#(loc.FailureWin7MissingSP1)", &pLocString);
- } else {
- BalLog(BOOTSTRAPPER_LOG_LEVEL_STANDARD, "Target OS is Windows 7 SP1 or later");
- return;
- }
- } else if (IsWindows7OrGreater()) {
- BalLog(BOOTSTRAPPER_LOG_LEVEL_ERROR, "Detected Windows 7 RTM");
- BalLog(BOOTSTRAPPER_LOG_LEVEL_ERROR, "Service Pack 1 is required to continue installation");
- LocGetString(_wixLoc, L"#(loc.FailureWin7MissingSP1)", &pLocString);
- } else if (IsWindowsVistaSP2OrGreater()) {
- BalLog(BOOTSTRAPPER_LOG_LEVEL_ERROR, "Target OS is Windows Vista SP2");
- return;
- } else if (IsWindowsVistaOrGreater()) {
- BalLog(BOOTSTRAPPER_LOG_LEVEL_ERROR, "Detected Windows Vista RTM or SP1");
- BalLog(BOOTSTRAPPER_LOG_LEVEL_ERROR, "Service Pack 2 is required to continue installation");
- LocGetString(_wixLoc, L"#(loc.FailureVistaMissingSP2)", &pLocString);
- } else {
- BalLog(BOOTSTRAPPER_LOG_LEVEL_ERROR, "Detected Windows XP or earlier");
- BalLog(BOOTSTRAPPER_LOG_LEVEL_ERROR, "Windows Vista SP2 or later is required to continue installation");
- LocGetString(_wixLoc, L"#(loc.FailureXPOrEarlier)", &pLocString);
- }
- }
-
- if (pLocString && pLocString->wzText) {
- BalFormatString(pLocString->wzText, &_failedMessage);
- }
-
- _hrFinal = E_WIXSTDBA_CONDITION_FAILED;
- }
-
-public:
- //
- // Constructor - initialize member variables.
- //
- PythonBootstrapperApplication(
- __in HMODULE hModule,
- __in BOOL fPrereq,
- __in HRESULT hrHostInitialization,
- __in IBootstrapperEngine* pEngine,
- __in const BOOTSTRAPPER_COMMAND* pCommand
- ) : CBalBaseBootstrapperApplication(pEngine, pCommand, 3, 3000) {
- _hModule = hModule;
- memcpy_s(&_command, sizeof(_command), pCommand, sizeof(BOOTSTRAPPER_COMMAND));
-
- LONGLONG llInstalled = 0;
- HRESULT hr = BalGetNumericVariable(L"WixBundleInstalled", &llInstalled);
- if (SUCCEEDED(hr) && BOOTSTRAPPER_RESUME_TYPE_REBOOT != _command.resumeType && 0 < llInstalled && BOOTSTRAPPER_ACTION_INSTALL == _command.action) {
- _command.action = BOOTSTRAPPER_ACTION_MODIFY;
- } else if (0 == llInstalled && (BOOTSTRAPPER_ACTION_MODIFY == _command.action || BOOTSTRAPPER_ACTION_REPAIR == _command.action)) {
- _command.action = BOOTSTRAPPER_ACTION_INSTALL;
- }
-
- _plannedAction = BOOTSTRAPPER_ACTION_UNKNOWN;
-
-
- // When resuming from restart doing some install-like operation, try to find the package that forced the
- // restart. We'll use this information during planning.
- _nextPackageAfterRestart = nullptr;
-
- if (BOOTSTRAPPER_RESUME_TYPE_REBOOT == _command.resumeType && BOOTSTRAPPER_ACTION_UNINSTALL < _command.action) {
- // Ensure the forced restart package variable is null when it is an empty string.
- HRESULT hr = BalGetStringVariable(L"WixBundleForcedRestartPackage", &_nextPackageAfterRestart);
- if (FAILED(hr) || !_nextPackageAfterRestart || !*_nextPackageAfterRestart) {
- ReleaseNullStr(_nextPackageAfterRestart);
- }
- }
-
- _crtInstalledToken = -1;
- pEngine->SetVariableNumeric(L"CRTInstalled", IsCrtInstalled() ? 1 : 0);
-
- _wixLoc = nullptr;
- memset(&_bundle, 0, sizeof(_bundle));
- memset(&_conditions, 0, sizeof(_conditions));
- _confirmCloseMessage = nullptr;
- _failedMessage = nullptr;
-
- _language = nullptr;
- _theme = nullptr;
- memset(_pageIds, 0, sizeof(_pageIds));
- _hUiThread = nullptr;
- _registered = FALSE;
- _hWnd = nullptr;
-
- _state = PYBA_STATE_INITIALIZING;
- _visiblePageId = 0;
- _installPage = PAGE_LOADING;
- _hrFinal = hrHostInitialization;
-
- _downgradingOtherVersion = FALSE;
- _restartResult = BOOTSTRAPPER_APPLY_RESTART_NONE;
- _restartRequired = FALSE;
- _allowRestart = FALSE;
-
- _suppressDowngradeFailure = FALSE;
- _suppressRepair = FALSE;
- _modifying = FALSE;
- _upgrading = FALSE;
-
- _overridableVariables = nullptr;
- _taskbarList = nullptr;
- _taskbarButtonCreatedMessage = UINT_MAX;
- _taskbarButtonOK = FALSE;
- _showingInternalUIThisPackage = FALSE;
-
- _suppressPaint = FALSE;
-
- pEngine->AddRef();
- _engine = pEngine;
-
- _hBAFModule = nullptr;
- _baFunction = nullptr;
- }
-
-
- //
- // Destructor - release member variables.
- //
- ~PythonBootstrapperApplication() {
- AssertSz(!::IsWindow(_hWnd), "Window should have been destroyed before destructor.");
- AssertSz(!_theme, "Theme should have been released before destructor.");
-
- ReleaseObject(_taskbarList);
- ReleaseDict(_overridableVariables);
- ReleaseStr(_failedMessage);
- ReleaseStr(_confirmCloseMessage);
- BalConditionsUninitialize(&_conditions);
- BalInfoUninitialize(&_bundle);
- LocFree(_wixLoc);
-
- ReleaseStr(_language);
- ReleaseStr(_nextPackageAfterRestart);
- ReleaseNullObject(_engine);
-
- if (_hBAFModule) {
- ::FreeLibrary(_hBAFModule);
- _hBAFModule = nullptr;
- }
- }
-
-private:
- HMODULE _hModule;
- BOOTSTRAPPER_COMMAND _command;
- IBootstrapperEngine* _engine;
- BOOTSTRAPPER_ACTION _plannedAction;
-
- LPWSTR _nextPackageAfterRestart;
-
- WIX_LOCALIZATION* _wixLoc;
- BAL_INFO_BUNDLE _bundle;
- BAL_CONDITIONS _conditions;
- LPWSTR _failedMessage;
- LPWSTR _confirmCloseMessage;
-
- LPWSTR _language;
- THEME* _theme;
- DWORD _pageIds[countof(PAGE_NAMES)];
- HANDLE _hUiThread;
- BOOL _registered;
- HWND _hWnd;
-
- PYBA_STATE _state;
- HRESULT _hrFinal;
- DWORD _visiblePageId;
- PAGE _installPage;
-
- BOOL _startedExecution;
- DWORD _calculatedCacheProgress;
- DWORD _calculatedExecuteProgress;
-
- BOOL _downgradingOtherVersion;
- BOOTSTRAPPER_APPLY_RESTART _restartResult;
- BOOL _restartRequired;
- BOOL _allowRestart;
-
- BOOL _suppressDowngradeFailure;
- BOOL _suppressRepair;
- BOOL _modifying;
- BOOL _upgrading;
-
- int _crtInstalledToken;
-
- STRINGDICT_HANDLE _overridableVariables;
-
- ITaskbarList3* _taskbarList;
- UINT _taskbarButtonCreatedMessage;
- BOOL _taskbarButtonOK;
- BOOL _showingInternalUIThisPackage;
-
- BOOL _suppressPaint;
-
- HMODULE _hBAFModule;
- IBootstrapperBAFunction* _baFunction;
-};
-
-//
-// CreateBootstrapperApplication - creates a new IBootstrapperApplication object.
-//
-HRESULT CreateBootstrapperApplication(
- __in HMODULE hModule,
- __in BOOL fPrereq,
- __in HRESULT hrHostInitialization,
- __in IBootstrapperEngine* pEngine,
- __in const BOOTSTRAPPER_COMMAND* pCommand,
- __out IBootstrapperApplication** ppApplication
- ) {
- HRESULT hr = S_OK;
-
- if (fPrereq) {
- hr = E_INVALIDARG;
- ExitWithLastError(hr, "Failed to create UI thread.");
- }
-
- PythonBootstrapperApplication* pApplication = nullptr;
-
- pApplication = new PythonBootstrapperApplication(hModule, fPrereq, hrHostInitialization, pEngine, pCommand);
- ExitOnNull(pApplication, hr, E_OUTOFMEMORY, "Failed to create new standard bootstrapper application object.");
-
- *ppApplication = pApplication;
- pApplication = nullptr;
-
-LExit:
- ReleaseObject(pApplication);
- return hr;
-}