Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 34efd49

Browse filesBrowse files
gh-115554: Improved logic for handling multiple existing py.exe launcher installs (GH-115793)
(cherry picked from commit 9b7f253) Co-authored-by: Steve Dower <steve.dower@python.org>
1 parent 6e89141 commit 34efd49
Copy full SHA for 34efd49

File tree

Expand file treeCollapse file tree

4 files changed

+140
-74
lines changed
Filter options
Expand file treeCollapse file tree

4 files changed

+140
-74
lines changed
+6Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
The installer now has more strict rules about updating the :ref:`launcher`.
2+
In general, most users only have a single launcher installed and will see no
3+
difference. When multiple launchers have been installed, the option to
4+
install the launcher is disabled until all but one have been removed.
5+
Downgrading the launcher (which was never allowed) is now more obviously
6+
blocked.

‎Tools/msi/bundle/Default.wxl

Copy file name to clipboardExpand all lines: Tools/msi/bundle/Default.wxl
+1Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,7 @@ Select Customize to review current options.</String>
8888
<String Id="InstallAllUsersLabel">Install Python [ShortVersion] for &amp;all users</String>
8989
<String Id="InstallLauncherAllUsersLabel">for &amp;all users (requires admin privileges)</String>
9090
<String Id="ShortInstallLauncherAllUsersLabel">Use admin privi&amp;leges when installing py.exe</String>
91+
<String Id="ShortInstallLauncherBlockedLabel">Python Launcher is already installed</String>
9192
<String Id="PrecompileLabel">&amp;Precompile standard library</String>
9293
<String Id="Include_symbolsLabel">Download debugging &amp;symbols</String>
9394
<String Id="Include_debugLabel">Download debu&amp;g binaries (requires VS 2017 or later)</String>

‎Tools/msi/bundle/bootstrap/PythonBootstrapperApplication.cpp

Copy file name to clipboardExpand all lines: Tools/msi/bundle/bootstrap/PythonBootstrapperApplication.cpp
+128-71Lines changed: 128 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -442,6 +442,14 @@ class PythonBootstrapperApplication : public CBalBaseBootstrapperApplication {
442442
ThemeControlElevates(_theme, ID_INSTALL_BUTTON, elevated);
443443
ThemeControlElevates(_theme, ID_INSTALL_SIMPLE_BUTTON, elevated);
444444
ThemeControlElevates(_theme, ID_INSTALL_UPGRADE_BUTTON, elevated);
445+
446+
LONGLONG blockedLauncher;
447+
if (SUCCEEDED(BalGetNumericVariable(L"BlockedLauncher", &blockedLauncher)) && blockedLauncher) {
448+
LOC_STRING *pLocString = nullptr;
449+
if (SUCCEEDED(LocGetString(_wixLoc, L"#(loc.ShortInstallLauncherBlockedLabel)", &pLocString)) && pLocString) {
450+
ThemeSetTextControl(_theme, ID_INSTALL_LAUNCHER_ALL_USERS_CHECKBOX, pLocString->wzText);
451+
}
452+
}
445453
}
446454

447455
void Custom1Page_Show() {
@@ -718,25 +726,67 @@ class PythonBootstrapperApplication : public CBalBaseBootstrapperApplication {
718726
__in DWORD64 /*dw64Version*/,
719727
__in BOOTSTRAPPER_RELATED_OPERATION operation
720728
) {
721-
if (BOOTSTRAPPER_RELATED_OPERATION_MAJOR_UPGRADE == operation &&
722-
(CSTR_EQUAL == ::CompareStringW(LOCALE_NEUTRAL, 0, wzPackageId, -1, L"launcher_AllUsers", -1) ||
723-
CSTR_EQUAL == ::CompareStringW(LOCALE_NEUTRAL, 0, wzPackageId, -1, L"launcher_JustForMe", -1))) {
724-
auto hr = LoadAssociateFilesStateFromKey(_engine, fPerMachine ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER);
725-
if (hr == S_OK) {
726-
_engine->SetVariableNumeric(L"AssociateFiles", 1);
727-
} else if (hr == S_FALSE) {
728-
_engine->SetVariableNumeric(L"AssociateFiles", 0);
729-
} else if (FAILED(hr)) {
730-
BalLog(BOOTSTRAPPER_LOG_LEVEL_ERROR, "Failed to load AssociateFiles state: error code 0x%08X", hr);
729+
// Only check launcher_AllUsers because we'll find the same packages
730+
// twice if we check launcher_JustForMe as well.
731+
if (CSTR_EQUAL == ::CompareStringW(LOCALE_NEUTRAL, 0, wzPackageId, -1, L"launcher_AllUsers", -1)) {
732+
BalLog(BOOTSTRAPPER_LOG_LEVEL_STANDARD, "Detected existing launcher install");
733+
734+
LONGLONG blockedLauncher, detectedLauncher;
735+
if (FAILED(BalGetNumericVariable(L"BlockedLauncher", &blockedLauncher))) {
736+
blockedLauncher = 0;
737+
}
738+
739+
// Get the prior DetectedLauncher value so we can see if we've
740+
// detected more than one, and then update the stored variable
741+
// (we use the original value later on via the local).
742+
if (FAILED(BalGetNumericVariable(L"DetectedLauncher", &detectedLauncher))) {
743+
detectedLauncher = 0;
744+
}
745+
if (!detectedLauncher) {
746+
_engine->SetVariableNumeric(L"DetectedLauncher", 1);
747+
}
748+
749+
if (blockedLauncher) {
750+
// Nothing else to do, we're already blocking
751+
}
752+
else if (BOOTSTRAPPER_RELATED_OPERATION_DOWNGRADE == operation) {
753+
// Found a higher version, so we can't install ours.
754+
BalLog(BOOTSTRAPPER_LOG_LEVEL_ERROR, "Higher version launcher has been detected.");
755+
BalLog(BOOTSTRAPPER_LOG_LEVEL_ERROR, "Launcher will not be installed");
756+
_engine->SetVariableNumeric(L"BlockedLauncher", 1);
757+
}
758+
else if (detectedLauncher) {
759+
if (!blockedLauncher) {
760+
BalLog(BOOTSTRAPPER_LOG_LEVEL_ERROR, "Multiple launcher installs have been detected.");
761+
BalLog(BOOTSTRAPPER_LOG_LEVEL_ERROR, "No launcher will be installed or upgraded until one has been removed.");
762+
_engine->SetVariableNumeric(L"BlockedLauncher", 1);
763+
}
731764
}
765+
else if (BOOTSTRAPPER_RELATED_OPERATION_MAJOR_UPGRADE == operation) {
766+
// Found an older version, so let's run the equivalent as an upgrade
767+
// This overrides "unknown" all users options, but will leave alone
768+
// any that have already been set/detected.
769+
// User can deselect the option to include the launcher, but cannot
770+
// change it from the current per user/machine setting.
771+
LONGLONG includeLauncher, includeLauncherAllUsers;
772+
if (FAILED(BalGetNumericVariable(L"Include_launcher", &includeLauncher))) {
773+
includeLauncher = -1;
774+
}
775+
if (FAILED(BalGetNumericVariable(L"InstallLauncherAllUsers", &includeLauncherAllUsers))) {
776+
includeLauncherAllUsers = -1;
777+
}
732778

733-
LONGLONG includeLauncher;
734-
if (FAILED(BalGetNumericVariable(L"Include_launcher", &includeLauncher))
735-
|| includeLauncher == -1) {
736-
_engine->SetVariableNumeric(L"Include_launcher", 1);
737-
_engine->SetVariableNumeric(L"InstallLauncherAllUsers", fPerMachine ? 1 : 0);
779+
if (includeLauncher < 0) {
780+
_engine->SetVariableNumeric(L"Include_launcher", 1);
781+
}
782+
if (includeLauncherAllUsers < 0) {
783+
_engine->SetVariableNumeric(L"InstallLauncherAllUsers", fPerMachine ? 1 : 0);
784+
} else if (includeLauncherAllUsers != fPerMachine ? 1 : 0) {
785+
// Requested AllUsers option is inconsistent, so block
786+
_engine->SetVariableNumeric(L"BlockedLauncher", 1);
787+
}
788+
_engine->SetVariableNumeric(L"DetectedOldLauncher", 1);
738789
}
739-
_engine->SetVariableNumeric(L"DetectedOldLauncher", 1);
740790
}
741791
return CheckCanceled() ? IDCANCEL : IDNOACTION;
742792
}
@@ -784,48 +834,7 @@ class PythonBootstrapperApplication : public CBalBaseBootstrapperApplication {
784834
__in LPCWSTR wzPackageId,
785835
__in HRESULT hrStatus,
786836
__in BOOTSTRAPPER_PACKAGE_STATE state
787-
) {
788-
if (FAILED(hrStatus)) {
789-
return;
790-
}
791-
792-
BOOL detectedLauncher = FALSE;
793-
HKEY hkey = HKEY_LOCAL_MACHINE;
794-
if (CSTR_EQUAL == ::CompareStringW(LOCALE_NEUTRAL, 0, wzPackageId, -1, L"launcher_AllUsers", -1)) {
795-
if (BOOTSTRAPPER_PACKAGE_STATE_PRESENT == state || BOOTSTRAPPER_PACKAGE_STATE_OBSOLETE == state) {
796-
detectedLauncher = TRUE;
797-
_engine->SetVariableNumeric(L"InstallLauncherAllUsers", 1);
798-
}
799-
} else if (CSTR_EQUAL == ::CompareStringW(LOCALE_NEUTRAL, 0, wzPackageId, -1, L"launcher_JustForMe", -1)) {
800-
if (BOOTSTRAPPER_PACKAGE_STATE_PRESENT == state || BOOTSTRAPPER_PACKAGE_STATE_OBSOLETE == state) {
801-
detectedLauncher = TRUE;
802-
_engine->SetVariableNumeric(L"InstallLauncherAllUsers", 0);
803-
}
804-
}
805-
806-
LONGLONG includeLauncher;
807-
if (SUCCEEDED(BalGetNumericVariable(L"Include_launcher", &includeLauncher))
808-
&& includeLauncher != -1) {
809-
detectedLauncher = FALSE;
810-
}
811-
812-
if (detectedLauncher) {
813-
/* When we detect the current version of the launcher. */
814-
_engine->SetVariableNumeric(L"Include_launcher", 1);
815-
_engine->SetVariableNumeric(L"DetectedLauncher", 1);
816-
_engine->SetVariableString(L"Include_launcherState", L"disable");
817-
_engine->SetVariableString(L"InstallLauncherAllUsersState", L"disable");
818-
819-
auto hr = LoadAssociateFilesStateFromKey(_engine, hkey);
820-
if (hr == S_OK) {
821-
_engine->SetVariableNumeric(L"AssociateFiles", 1);
822-
} else if (hr == S_FALSE) {
823-
_engine->SetVariableNumeric(L"AssociateFiles", 0);
824-
} else if (FAILED(hr)) {
825-
BalLog(BOOTSTRAPPER_LOG_LEVEL_ERROR, "Failed to load AssociateFiles state: error code 0x%08X", hr);
826-
}
827-
}
828-
}
837+
) { }
829838

830839

831840
virtual STDMETHODIMP_(void) OnDetectComplete(__in HRESULT hrStatus) {
@@ -835,19 +844,67 @@ class PythonBootstrapperApplication : public CBalBaseBootstrapperApplication {
835844
}
836845

837846
if (SUCCEEDED(hrStatus)) {
838-
LONGLONG includeLauncher;
839-
if (SUCCEEDED(BalGetNumericVariable(L"Include_launcher", &includeLauncher))
840-
&& includeLauncher == -1) {
841-
if (BOOTSTRAPPER_ACTION_LAYOUT == _command.action ||
842-
(BOOTSTRAPPER_ACTION_INSTALL == _command.action && !_upgrading)) {
843-
// When installing/downloading, we want to include the launcher
844-
// by default.
845-
_engine->SetVariableNumeric(L"Include_launcher", 1);
846-
} else {
847-
// Any other action, if we didn't detect the MSI then we want to
848-
// keep it excluded
849-
_engine->SetVariableNumeric(L"Include_launcher", 0);
850-
_engine->SetVariableNumeric(L"AssociateFiles", 0);
847+
// Update launcher install states
848+
// If we didn't detect any existing installs, Include_launcher and
849+
// InstallLauncherAllUsers will both be -1, so we will set to their
850+
// defaults and leave the options enabled.
851+
// Otherwise, if we detected an existing install, we disable the
852+
// options so they remain fixed.
853+
// The code in OnDetectRelatedMsiPackage is responsible for figuring
854+
// out whether existing installs are compatible with the settings in
855+
// place during detection.
856+
LONGLONG blockedLauncher;
857+
if (SUCCEEDED(BalGetNumericVariable(L"BlockedLauncher", &blockedLauncher))
858+
&& blockedLauncher) {
859+
_engine->SetVariableNumeric(L"Include_launcher", 0);
860+
_engine->SetVariableNumeric(L"InstallLauncherAllUsers", 0);
861+
_engine->SetVariableString(L"InstallLauncherAllUsersState", L"disable");
862+
_engine->SetVariableString(L"Include_launcherState", L"disable");
863+
}
864+
else {
865+
LONGLONG includeLauncher, includeLauncherAllUsers, associateFiles;
866+
867+
if (FAILED(BalGetNumericVariable(L"Include_launcher", &includeLauncher))) {
868+
includeLauncher = -1;
869+
}
870+
if (FAILED(BalGetNumericVariable(L"InstallLauncherAllUsers", &includeLauncherAllUsers))) {
871+
includeLauncherAllUsers = -1;
872+
}
873+
if (FAILED(BalGetNumericVariable(L"AssociateFiles", &associateFiles))) {
874+
associateFiles = -1;
875+
}
876+
877+
if (includeLauncherAllUsers < 0) {
878+
includeLauncherAllUsers = 0;
879+
_engine->SetVariableNumeric(L"InstallLauncherAllUsers", includeLauncherAllUsers);
880+
}
881+
882+
if (includeLauncher < 0) {
883+
if (BOOTSTRAPPER_ACTION_LAYOUT == _command.action ||
884+
(BOOTSTRAPPER_ACTION_INSTALL == _command.action && !_upgrading)) {
885+
// When installing/downloading, we include the launcher
886+
// (though downloads should ignore this setting anyway)
887+
_engine->SetVariableNumeric(L"Include_launcher", 1);
888+
} else {
889+
// Any other action, we should have detected an existing
890+
// install (e.g. on remove/modify), so if we didn't, we
891+
// assume it's not selected.
892+
_engine->SetVariableNumeric(L"Include_launcher", 0);
893+
_engine->SetVariableNumeric(L"AssociateFiles", 0);
894+
}
895+
}
896+
897+
if (associateFiles < 0) {
898+
auto hr = LoadAssociateFilesStateFromKey(
899+
_engine,
900+
includeLauncherAllUsers ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER
901+
);
902+
if (FAILED(hr)) {
903+
BalLog(BOOTSTRAPPER_LOG_LEVEL_ERROR, "Failed to load AssociateFiles state: error code 0x%08X", hr);
904+
} else if (hr == S_OK) {
905+
associateFiles = 1;
906+
}
907+
_engine->SetVariableNumeric(L"AssociateFiles", associateFiles);
851908
}
852909
}
853910
}

‎Tools/msi/bundle/bundle.wxs

Copy file name to clipboardExpand all lines: Tools/msi/bundle/bundle.wxs
+5-3Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,10 +28,11 @@
2828

2929
<Variable Name="InstallAllUsers" Value="0" bal:Overridable="yes" />
3030
<?if "$(var.PyTestExt)"="" ?>
31-
<Variable Name="InstallLauncherAllUsers" Value="0" bal:Overridable="yes" />
31+
<Variable Name="InstallLauncherAllUsers" Value="-1" bal:Overridable="yes" />
3232
<?else ?>
33-
<Variable Name="InstallLauncherAllUsers" Value="0" />
33+
<Variable Name="InstallLauncherAllUsers" Value="-1" />
3434
<?endif ?>
35+
3536
<Variable Name="TargetDir" Value="" bal:Overridable="yes" />
3637
<?if $(var.Platform)~="x64" ?>
3738
<Variable Name="DefaultAllUsersTargetDir" Value="[ProgramFiles64Folder]Python[WinVerNoDot]" bal:Overridable="yes" />
@@ -84,10 +85,11 @@
8485
<Variable Name="Include_debug" Value="0" bal:Overridable="yes" />
8586

8687
<Variable Name="LauncherOnly" Value="0" bal:Overridable="yes" />
88+
<Variable Name="BlockedLauncher" Value="0" />
8789
<Variable Name="DetectedLauncher" Value="0" />
8890
<Variable Name="DetectedOldLauncher" Value="0" />
8991

90-
<Variable Name="AssociateFiles" Value="1" bal:Overridable="yes" />
92+
<Variable Name="AssociateFiles" Value="-1" bal:Overridable="yes" />
9193
<Variable Name="Shortcuts" Value="1" bal:Overridable="yes" />
9294
<Variable Name="PrependPath" Value="0" bal:Overridable="yes" />
9395
<Variable Name="AppendPath" Value="0" bal:Overridable="yes" />

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.