mirror of
https://github.com/python/cpython.git
synced 2025-09-27 18:59:43 +00:00
gh-115554: Improved logic for handling multiple existing py.exe launcher installs (GH-115793)
(cherry picked from commit 9b7f253b55
)
Co-authored-by: Steve Dower <steve.dower@python.org>
This commit is contained in:
parent
6e89141d73
commit
34efd49280
4 changed files with 140 additions and 74 deletions
|
@ -0,0 +1,6 @@
|
||||||
|
The installer now has more strict rules about updating the :ref:`launcher`.
|
||||||
|
In general, most users only have a single launcher installed and will see no
|
||||||
|
difference. When multiple launchers have been installed, the option to
|
||||||
|
install the launcher is disabled until all but one have been removed.
|
||||||
|
Downgrading the launcher (which was never allowed) is now more obviously
|
||||||
|
blocked.
|
|
@ -88,6 +88,7 @@ Select Customize to review current options.</String>
|
||||||
<String Id="InstallAllUsersLabel">Install Python [ShortVersion] for &all users</String>
|
<String Id="InstallAllUsersLabel">Install Python [ShortVersion] for &all users</String>
|
||||||
<String Id="InstallLauncherAllUsersLabel">for &all users (requires admin privileges)</String>
|
<String Id="InstallLauncherAllUsersLabel">for &all users (requires admin privileges)</String>
|
||||||
<String Id="ShortInstallLauncherAllUsersLabel">Use admin privi&leges when installing py.exe</String>
|
<String Id="ShortInstallLauncherAllUsersLabel">Use admin privi&leges when installing py.exe</String>
|
||||||
|
<String Id="ShortInstallLauncherBlockedLabel">Python Launcher is already installed</String>
|
||||||
<String Id="PrecompileLabel">&Precompile standard library</String>
|
<String Id="PrecompileLabel">&Precompile standard library</String>
|
||||||
<String Id="Include_symbolsLabel">Download debugging &symbols</String>
|
<String Id="Include_symbolsLabel">Download debugging &symbols</String>
|
||||||
<String Id="Include_debugLabel">Download debu&g binaries (requires VS 2017 or later)</String>
|
<String Id="Include_debugLabel">Download debu&g binaries (requires VS 2017 or later)</String>
|
||||||
|
|
|
@ -442,6 +442,14 @@ class PythonBootstrapperApplication : public CBalBaseBootstrapperApplication {
|
||||||
ThemeControlElevates(_theme, ID_INSTALL_BUTTON, elevated);
|
ThemeControlElevates(_theme, ID_INSTALL_BUTTON, elevated);
|
||||||
ThemeControlElevates(_theme, ID_INSTALL_SIMPLE_BUTTON, elevated);
|
ThemeControlElevates(_theme, ID_INSTALL_SIMPLE_BUTTON, elevated);
|
||||||
ThemeControlElevates(_theme, ID_INSTALL_UPGRADE_BUTTON, elevated);
|
ThemeControlElevates(_theme, ID_INSTALL_UPGRADE_BUTTON, elevated);
|
||||||
|
|
||||||
|
LONGLONG blockedLauncher;
|
||||||
|
if (SUCCEEDED(BalGetNumericVariable(L"BlockedLauncher", &blockedLauncher)) && blockedLauncher) {
|
||||||
|
LOC_STRING *pLocString = nullptr;
|
||||||
|
if (SUCCEEDED(LocGetString(_wixLoc, L"#(loc.ShortInstallLauncherBlockedLabel)", &pLocString)) && pLocString) {
|
||||||
|
ThemeSetTextControl(_theme, ID_INSTALL_LAUNCHER_ALL_USERS_CHECKBOX, pLocString->wzText);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Custom1Page_Show() {
|
void Custom1Page_Show() {
|
||||||
|
@ -718,25 +726,67 @@ public: // IBootstrapperApplication
|
||||||
__in DWORD64 /*dw64Version*/,
|
__in DWORD64 /*dw64Version*/,
|
||||||
__in BOOTSTRAPPER_RELATED_OPERATION operation
|
__in BOOTSTRAPPER_RELATED_OPERATION operation
|
||||||
) {
|
) {
|
||||||
if (BOOTSTRAPPER_RELATED_OPERATION_MAJOR_UPGRADE == operation &&
|
// Only check launcher_AllUsers because we'll find the same packages
|
||||||
(CSTR_EQUAL == ::CompareStringW(LOCALE_NEUTRAL, 0, wzPackageId, -1, L"launcher_AllUsers", -1) ||
|
// twice if we check launcher_JustForMe as well.
|
||||||
CSTR_EQUAL == ::CompareStringW(LOCALE_NEUTRAL, 0, wzPackageId, -1, L"launcher_JustForMe", -1))) {
|
if (CSTR_EQUAL == ::CompareStringW(LOCALE_NEUTRAL, 0, wzPackageId, -1, L"launcher_AllUsers", -1)) {
|
||||||
auto hr = LoadAssociateFilesStateFromKey(_engine, fPerMachine ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER);
|
BalLog(BOOTSTRAPPER_LOG_LEVEL_STANDARD, "Detected existing launcher install");
|
||||||
if (hr == S_OK) {
|
|
||||||
_engine->SetVariableNumeric(L"AssociateFiles", 1);
|
LONGLONG blockedLauncher, detectedLauncher;
|
||||||
} else if (hr == S_FALSE) {
|
if (FAILED(BalGetNumericVariable(L"BlockedLauncher", &blockedLauncher))) {
|
||||||
_engine->SetVariableNumeric(L"AssociateFiles", 0);
|
blockedLauncher = 0;
|
||||||
} else if (FAILED(hr)) {
|
|
||||||
BalLog(BOOTSTRAPPER_LOG_LEVEL_ERROR, "Failed to load AssociateFiles state: error code 0x%08X", hr);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
LONGLONG includeLauncher;
|
// Get the prior DetectedLauncher value so we can see if we've
|
||||||
if (FAILED(BalGetNumericVariable(L"Include_launcher", &includeLauncher))
|
// detected more than one, and then update the stored variable
|
||||||
|| includeLauncher == -1) {
|
// (we use the original value later on via the local).
|
||||||
_engine->SetVariableNumeric(L"Include_launcher", 1);
|
if (FAILED(BalGetNumericVariable(L"DetectedLauncher", &detectedLauncher))) {
|
||||||
_engine->SetVariableNumeric(L"InstallLauncherAllUsers", fPerMachine ? 1 : 0);
|
detectedLauncher = 0;
|
||||||
|
}
|
||||||
|
if (!detectedLauncher) {
|
||||||
|
_engine->SetVariableNumeric(L"DetectedLauncher", 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (blockedLauncher) {
|
||||||
|
// Nothing else to do, we're already blocking
|
||||||
|
}
|
||||||
|
else if (BOOTSTRAPPER_RELATED_OPERATION_DOWNGRADE == operation) {
|
||||||
|
// Found a higher version, so we can't install ours.
|
||||||
|
BalLog(BOOTSTRAPPER_LOG_LEVEL_ERROR, "Higher version launcher has been detected.");
|
||||||
|
BalLog(BOOTSTRAPPER_LOG_LEVEL_ERROR, "Launcher will not be installed");
|
||||||
|
_engine->SetVariableNumeric(L"BlockedLauncher", 1);
|
||||||
|
}
|
||||||
|
else if (detectedLauncher) {
|
||||||
|
if (!blockedLauncher) {
|
||||||
|
BalLog(BOOTSTRAPPER_LOG_LEVEL_ERROR, "Multiple launcher installs have been detected.");
|
||||||
|
BalLog(BOOTSTRAPPER_LOG_LEVEL_ERROR, "No launcher will be installed or upgraded until one has been removed.");
|
||||||
|
_engine->SetVariableNumeric(L"BlockedLauncher", 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (BOOTSTRAPPER_RELATED_OPERATION_MAJOR_UPGRADE == operation) {
|
||||||
|
// Found an older version, so let's run the equivalent as an upgrade
|
||||||
|
// This overrides "unknown" all users options, but will leave alone
|
||||||
|
// any that have already been set/detected.
|
||||||
|
// User can deselect the option to include the launcher, but cannot
|
||||||
|
// change it from the current per user/machine setting.
|
||||||
|
LONGLONG includeLauncher, includeLauncherAllUsers;
|
||||||
|
if (FAILED(BalGetNumericVariable(L"Include_launcher", &includeLauncher))) {
|
||||||
|
includeLauncher = -1;
|
||||||
|
}
|
||||||
|
if (FAILED(BalGetNumericVariable(L"InstallLauncherAllUsers", &includeLauncherAllUsers))) {
|
||||||
|
includeLauncherAllUsers = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (includeLauncher < 0) {
|
||||||
|
_engine->SetVariableNumeric(L"Include_launcher", 1);
|
||||||
|
}
|
||||||
|
if (includeLauncherAllUsers < 0) {
|
||||||
|
_engine->SetVariableNumeric(L"InstallLauncherAllUsers", fPerMachine ? 1 : 0);
|
||||||
|
} else if (includeLauncherAllUsers != fPerMachine ? 1 : 0) {
|
||||||
|
// Requested AllUsers option is inconsistent, so block
|
||||||
|
_engine->SetVariableNumeric(L"BlockedLauncher", 1);
|
||||||
|
}
|
||||||
|
_engine->SetVariableNumeric(L"DetectedOldLauncher", 1);
|
||||||
}
|
}
|
||||||
_engine->SetVariableNumeric(L"DetectedOldLauncher", 1);
|
|
||||||
}
|
}
|
||||||
return CheckCanceled() ? IDCANCEL : IDNOACTION;
|
return CheckCanceled() ? IDCANCEL : IDNOACTION;
|
||||||
}
|
}
|
||||||
|
@ -784,48 +834,7 @@ public: // IBootstrapperApplication
|
||||||
__in LPCWSTR wzPackageId,
|
__in LPCWSTR wzPackageId,
|
||||||
__in HRESULT hrStatus,
|
__in HRESULT hrStatus,
|
||||||
__in BOOTSTRAPPER_PACKAGE_STATE state
|
__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 (hr == S_FALSE) {
|
|
||||||
_engine->SetVariableNumeric(L"AssociateFiles", 0);
|
|
||||||
} 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) {
|
virtual STDMETHODIMP_(void) OnDetectComplete(__in HRESULT hrStatus) {
|
||||||
|
@ -835,19 +844,67 @@ public: // IBootstrapperApplication
|
||||||
}
|
}
|
||||||
|
|
||||||
if (SUCCEEDED(hrStatus)) {
|
if (SUCCEEDED(hrStatus)) {
|
||||||
LONGLONG includeLauncher;
|
// Update launcher install states
|
||||||
if (SUCCEEDED(BalGetNumericVariable(L"Include_launcher", &includeLauncher))
|
// If we didn't detect any existing installs, Include_launcher and
|
||||||
&& includeLauncher == -1) {
|
// InstallLauncherAllUsers will both be -1, so we will set to their
|
||||||
if (BOOTSTRAPPER_ACTION_LAYOUT == _command.action ||
|
// defaults and leave the options enabled.
|
||||||
(BOOTSTRAPPER_ACTION_INSTALL == _command.action && !_upgrading)) {
|
// Otherwise, if we detected an existing install, we disable the
|
||||||
// When installing/downloading, we want to include the launcher
|
// options so they remain fixed.
|
||||||
// by default.
|
// The code in OnDetectRelatedMsiPackage is responsible for figuring
|
||||||
_engine->SetVariableNumeric(L"Include_launcher", 1);
|
// out whether existing installs are compatible with the settings in
|
||||||
} else {
|
// place during detection.
|
||||||
// Any other action, if we didn't detect the MSI then we want to
|
LONGLONG blockedLauncher;
|
||||||
// keep it excluded
|
if (SUCCEEDED(BalGetNumericVariable(L"BlockedLauncher", &blockedLauncher))
|
||||||
_engine->SetVariableNumeric(L"Include_launcher", 0);
|
&& blockedLauncher) {
|
||||||
_engine->SetVariableNumeric(L"AssociateFiles", 0);
|
_engine->SetVariableNumeric(L"Include_launcher", 0);
|
||||||
|
_engine->SetVariableNumeric(L"InstallLauncherAllUsers", 0);
|
||||||
|
_engine->SetVariableString(L"InstallLauncherAllUsersState", L"disable");
|
||||||
|
_engine->SetVariableString(L"Include_launcherState", L"disable");
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
LONGLONG includeLauncher, includeLauncherAllUsers, associateFiles;
|
||||||
|
|
||||||
|
if (FAILED(BalGetNumericVariable(L"Include_launcher", &includeLauncher))) {
|
||||||
|
includeLauncher = -1;
|
||||||
|
}
|
||||||
|
if (FAILED(BalGetNumericVariable(L"InstallLauncherAllUsers", &includeLauncherAllUsers))) {
|
||||||
|
includeLauncherAllUsers = -1;
|
||||||
|
}
|
||||||
|
if (FAILED(BalGetNumericVariable(L"AssociateFiles", &associateFiles))) {
|
||||||
|
associateFiles = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (includeLauncherAllUsers < 0) {
|
||||||
|
includeLauncherAllUsers = 0;
|
||||||
|
_engine->SetVariableNumeric(L"InstallLauncherAllUsers", includeLauncherAllUsers);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (includeLauncher < 0) {
|
||||||
|
if (BOOTSTRAPPER_ACTION_LAYOUT == _command.action ||
|
||||||
|
(BOOTSTRAPPER_ACTION_INSTALL == _command.action && !_upgrading)) {
|
||||||
|
// When installing/downloading, we include the launcher
|
||||||
|
// (though downloads should ignore this setting anyway)
|
||||||
|
_engine->SetVariableNumeric(L"Include_launcher", 1);
|
||||||
|
} else {
|
||||||
|
// Any other action, we should have detected an existing
|
||||||
|
// install (e.g. on remove/modify), so if we didn't, we
|
||||||
|
// assume it's not selected.
|
||||||
|
_engine->SetVariableNumeric(L"Include_launcher", 0);
|
||||||
|
_engine->SetVariableNumeric(L"AssociateFiles", 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (associateFiles < 0) {
|
||||||
|
auto hr = LoadAssociateFilesStateFromKey(
|
||||||
|
_engine,
|
||||||
|
includeLauncherAllUsers ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER
|
||||||
|
);
|
||||||
|
if (FAILED(hr)) {
|
||||||
|
BalLog(BOOTSTRAPPER_LOG_LEVEL_ERROR, "Failed to load AssociateFiles state: error code 0x%08X", hr);
|
||||||
|
} else if (hr == S_OK) {
|
||||||
|
associateFiles = 1;
|
||||||
|
}
|
||||||
|
_engine->SetVariableNumeric(L"AssociateFiles", associateFiles);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,10 +28,11 @@
|
||||||
|
|
||||||
<Variable Name="InstallAllUsers" Value="0" bal:Overridable="yes" />
|
<Variable Name="InstallAllUsers" Value="0" bal:Overridable="yes" />
|
||||||
<?if "$(var.PyTestExt)"="" ?>
|
<?if "$(var.PyTestExt)"="" ?>
|
||||||
<Variable Name="InstallLauncherAllUsers" Value="0" bal:Overridable="yes" />
|
<Variable Name="InstallLauncherAllUsers" Value="-1" bal:Overridable="yes" />
|
||||||
<?else ?>
|
<?else ?>
|
||||||
<Variable Name="InstallLauncherAllUsers" Value="0" />
|
<Variable Name="InstallLauncherAllUsers" Value="-1" />
|
||||||
<?endif ?>
|
<?endif ?>
|
||||||
|
|
||||||
<Variable Name="TargetDir" Value="" bal:Overridable="yes" />
|
<Variable Name="TargetDir" Value="" bal:Overridable="yes" />
|
||||||
<?if $(var.Platform)~="x64" ?>
|
<?if $(var.Platform)~="x64" ?>
|
||||||
<Variable Name="DefaultAllUsersTargetDir" Value="[ProgramFiles64Folder]Python[WinVerNoDot]" bal:Overridable="yes" />
|
<Variable Name="DefaultAllUsersTargetDir" Value="[ProgramFiles64Folder]Python[WinVerNoDot]" bal:Overridable="yes" />
|
||||||
|
@ -84,10 +85,11 @@
|
||||||
<Variable Name="Include_debug" Value="0" bal:Overridable="yes" />
|
<Variable Name="Include_debug" Value="0" bal:Overridable="yes" />
|
||||||
|
|
||||||
<Variable Name="LauncherOnly" Value="0" bal:Overridable="yes" />
|
<Variable Name="LauncherOnly" Value="0" bal:Overridable="yes" />
|
||||||
|
<Variable Name="BlockedLauncher" Value="0" />
|
||||||
<Variable Name="DetectedLauncher" Value="0" />
|
<Variable Name="DetectedLauncher" Value="0" />
|
||||||
<Variable Name="DetectedOldLauncher" Value="0" />
|
<Variable Name="DetectedOldLauncher" Value="0" />
|
||||||
|
|
||||||
<Variable Name="AssociateFiles" Value="1" bal:Overridable="yes" />
|
<Variable Name="AssociateFiles" Value="-1" bal:Overridable="yes" />
|
||||||
<Variable Name="Shortcuts" Value="1" bal:Overridable="yes" />
|
<Variable Name="Shortcuts" Value="1" bal:Overridable="yes" />
|
||||||
<Variable Name="PrependPath" Value="0" bal:Overridable="yes" />
|
<Variable Name="PrependPath" Value="0" bal:Overridable="yes" />
|
||||||
<Variable Name="AppendPath" Value="0" bal:Overridable="yes" />
|
<Variable Name="AppendPath" Value="0" bal:Overridable="yes" />
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue