@@ -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,68 @@ 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+ // Python 3.11 installer defaults to 1. Later versions use 0
879+ includeLauncherAllUsers = 1 ;
880+ _engine->SetVariableNumeric (L" InstallLauncherAllUsers" , includeLauncherAllUsers);
881+ }
882+
883+ if (includeLauncher < 0 ) {
884+ if (BOOTSTRAPPER_ACTION_LAYOUT == _command.action ||
885+ (BOOTSTRAPPER_ACTION_INSTALL == _command.action && !_upgrading)) {
886+ // When installing/downloading, we include the launcher
887+ // (though downloads should ignore this setting anyway)
888+ _engine->SetVariableNumeric (L" Include_launcher" , 1 );
889+ } else {
890+ // Any other action, we should have detected an existing
891+ // install (e.g. on remove/modify), so if we didn't, we
892+ // assume it's not selected.
893+ _engine->SetVariableNumeric (L" Include_launcher" , 0 );
894+ _engine->SetVariableNumeric (L" AssociateFiles" , 0 );
895+ }
896+ }
897+
898+ if (associateFiles < 0 ) {
899+ auto hr = LoadAssociateFilesStateFromKey (
900+ _engine,
901+ includeLauncherAllUsers ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER
902+ );
903+ if (FAILED (hr)) {
904+ BalLog (BOOTSTRAPPER_LOG_LEVEL_ERROR, " Failed to load AssociateFiles state: error code 0x%08X" , hr);
905+ } else if (hr == S_OK) {
906+ associateFiles = 1 ;
907+ }
908+ _engine->SetVariableNumeric (L" AssociateFiles" , associateFiles);
851909 }
852910 }
853911 }
0 commit comments