Author: [email protected]
Branch: windowsinstaller
Changeset: r95801:fb576872b8ca
Date: 2018-12-24 19:53 +0000
http://bitbucket.org/pypy/pypy/changeset/fb576872b8ca/
Log: Got to the point where bootstrap is being built.
diff too long, truncating to 2000 out of 3893 lines
diff --git a/pypy/tool/release/windowsinstaller/bundle/bootstrap/LICENSE.txt
b/pypy/tool/release/windowsinstaller/bundle/bootstrap/LICENSE.txt
new file mode 100644
--- /dev/null
+++ b/pypy/tool/release/windowsinstaller/bundle/bootstrap/LICENSE.txt
@@ -0,0 +1,25 @@
+This license applies to the bootstrapper application that is embedded within
the installer. It has no impact on the licensing for the rest of the installer
or Python itself, as no code covered by this license exists in any other part
of the product.
+
+---
+
+Microsoft Reciprocal License (MS-RL)
+
+This license governs use of the accompanying software. If you use the
software, you accept this license. If you do not accept the license, do not use
the software.
+
+1. Definitions
+ The terms "reproduce," "reproduction," "derivative works," and "distribution"
have the same meaning here as under U.S. copyright law.
+ A "contribution" is the original software, or any additions or changes to the
software.
+ A "contributor" is any person that distributes its contribution under this
license.
+ "Licensed patents" are a contributor's patent claims that read directly on
its contribution.
+
+2. Grant of Rights
+ (A) Copyright Grant- Subject to the terms of this license, including the
license conditions and limitations in section 3, each contributor grants you a
non-exclusive, worldwide, royalty-free copyright license to reproduce its
contribution, prepare derivative works of its contribution, and distribute its
contribution or any derivative works that you create.
+ (B) Patent Grant- Subject to the terms of this license, including the license
conditions and limitations in section 3, each contributor grants you a
non-exclusive, worldwide, royalty-free license under its licensed patents to
make, have made, use, sell, offer for sale, import, and/or otherwise dispose of
its contribution in the software or derivative works of the contribution in the
software.
+
+3. Conditions and Limitations
+ (A) Reciprocal Grants- For any file you distribute that contains code from
the software (in source code or binary format), you must provide recipients the
source code to that file along with a copy of this license, which license will
govern that file. You may license other files that are entirely your own work
and do not contain code from the software under any terms you choose.
+ (B) No Trademark License- This license does not grant you rights to use any
contributors' name, logo, or trademarks.
+ (C) If you bring a patent claim against any contributor over patents that you
claim are infringed by the software, your patent license from such contributor
to the software ends automatically.
+ (D) If you distribute any portion of the software, you must retain all
copyright, patent, trademark, and attribution notices that are present in the
software.
+ (E) If you distribute any portion of the software in source code form, you
may do so only under this license by including a complete copy of this license
with your distribution. If you distribute any portion of the software in
compiled or object code form, you may only do so under a license that complies
with this license.
+ (F) The software is licensed "as-is." You bear the risk of using it. The
contributors give no express warranties, guarantees or conditions. You may have
additional consumer rights under your local laws which this license cannot
change. To the extent permitted under your local laws, the contributors exclude
the implied warranties of merchantability, fitness for a particular purpose and
non-infringement.
diff --git
a/pypy/tool/release/windowsinstaller/bundle/bootstrap/PythonBootstrapperApplication.cpp
b/pypy/tool/release/windowsinstaller/bundle/bootstrap/PythonBootstrapperApplication.cpp
new file mode 100644
--- /dev/null
+++
b/pypy/tool/release/windowsinstaller/bundle/bootstrap/PythonBootstrapperApplication.cpp
@@ -0,0 +1,3233 @@
+//-------------------------------------------------------------------------------------------------
+// <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);
+ }
+
+ _engine->SetVariableNumeric(L"Include_launcher", 1);
+ _engine->SetVariableNumeric(L"DetectedOldLauncher", 1);
+ _engine->SetVariableNumeric(L"InstallLauncherAllUsers",
fPerMachine ? 1 : 0);
+ }
+ 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);
+ }
+ }
+
+ 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)) {
+ 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.");
+
+ 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.");
+
+ if (_command.action == BOOTSTRAPPER_ACTION_MODIFY) {
+ LoadOptionalFeatureStates(_engine);
+ }
+
+ 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;
+ }
_______________________________________________
pypy-commit mailing list
[email protected]
https://mail.python.org/mailman/listinfo/pypy-commit