aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorBob Arnson <bob@firegiant.com>2026-02-10 20:33:03 -0500
committerBob Arnson <github@bobs.org>2026-02-11 13:21:20 -0500
commitde80ff1102a1b34e430bbc718fe65a42bab196cf (patch)
tree1eb70c137c84ae7c9d8c73370c7e39b4cf76a377 /src
parentcd5f73d939b6d91ce3d9efcc5b8a800542041698 (diff)
downloadwix-de80ff1102a1b34e430bbc718fe65a42bab196cf.tar.gz
wix-de80ff1102a1b34e430bbc718fe65a42bab196cf.tar.bz2
wix-de80ff1102a1b34e430bbc718fe65a42bab196cf.zip
Log detected package scope and lock bundle scope.HEADmain
- Fixes https://github.com/wixtoolset/issues/issues/9232 - Fixes https://github.com/wixtoolset/issues/issues/9240
Diffstat (limited to '')
-rw-r--r--src/burn/engine/core.cpp4
-rw-r--r--src/burn/engine/core.h3
-rw-r--r--src/burn/engine/engine.mc9
-rw-r--r--src/burn/engine/msiengine.cpp14
-rw-r--r--src/burn/engine/package.h1
-rw-r--r--src/burn/engine/plan.cpp10
-rw-r--r--src/burn/engine/plan.h1
-rw-r--r--src/burn/engine/registration.cpp40
-rw-r--r--src/burn/engine/registration.h3
-rw-r--r--src/burn/engine/variable.cpp3
-rw-r--r--src/test/burn/WixToolsetTest.BurnE2E/ConfigurableScopeTests.cs17
11 files changed, 80 insertions, 25 deletions
diff --git a/src/burn/engine/core.cpp b/src/burn/engine/core.cpp
index 7937bd65..dc0af8ce 100644
--- a/src/burn/engine/core.cpp
+++ b/src/burn/engine/core.cpp
@@ -379,7 +379,7 @@ extern "C" HRESULT CoreDetect(
379 pEngineState->registration.fEligibleForCleanup = FALSE; 379 pEngineState->registration.fEligibleForCleanup = FALSE;
380 } 380 }
381 381
382 LogId(REPORT_STANDARD, MSG_DETECTED_PACKAGE, pPackage->sczId, LoggingPackageStateToString(pPackage->currentState), LoggingBoolToString(pPackage->fCached), LoggingPackageRegistrationStateToString(pPackage->fCanAffectRegistration, pPackage->installRegistrationState), LoggingPackageRegistrationStateToString(pPackage->fCanAffectRegistration, pPackage->cacheRegistrationState), LoggingPackageScopeToString(pPackage->scope)); 382 LogId(REPORT_STANDARD, MSG_DETECTED_PACKAGE, pPackage->sczId, LoggingPackageStateToString(pPackage->currentState), LoggingBoolToString(pPackage->fCached), LoggingPackageRegistrationStateToString(pPackage->fCanAffectRegistration, pPackage->installRegistrationState), LoggingPackageRegistrationStateToString(pPackage->fCanAffectRegistration, pPackage->cacheRegistrationState), LoggingPackageScopeToString(pPackage->scope), LoggingInstallScopeToString(pPackage->fDetectedPerMachine));
383 383
384 if (BURN_PACKAGE_TYPE_MSI == pPackage->type) 384 if (BURN_PACKAGE_TYPE_MSI == pPackage->type)
385 { 385 {
@@ -469,7 +469,7 @@ extern "C" HRESULT CorePlan(
469 pEngineState->plan.fDisableRollback = pEngineState->fDisableRollback || BOOTSTRAPPER_ACTION_UNSAFE_UNINSTALL == pEngineState->plan.action; 469 pEngineState->plan.fDisableRollback = pEngineState->fDisableRollback || BOOTSTRAPPER_ACTION_UNSAFE_UNINSTALL == pEngineState->plan.action;
470 pEngineState->plan.fPlanPackageCacheRollback = BOOTSTRAPPER_REGISTRATION_TYPE_NONE == pEngineState->registration.detectedRegistrationType; 470 pEngineState->plan.fPlanPackageCacheRollback = BOOTSTRAPPER_REGISTRATION_TYPE_NONE == pEngineState->registration.detectedRegistrationType;
471 471
472 hr = PlanPackagesAndBundleScope(pEngineState->packages.rgPackages, pEngineState->packages.cPackages, pEngineState->plan.plannedScope, pEngineState->registration.scope, pEngineState->command.commandLineScope, &pEngineState->plan.plannedScope, &pEngineState->registration.fPerMachine); 472 hr = PlanPackagesAndBundleScope(pEngineState->packages.rgPackages, pEngineState->packages.cPackages, pEngineState->plan.plannedScope, pEngineState->registration.scope, pEngineState->command.commandLineScope, pEngineState->registration.detectedScope, &pEngineState->plan.plannedScope, &pEngineState->registration.fPerMachine);
473 ExitOnFailure(hr, "Failed to determine packages and bundle scope."); 473 ExitOnFailure(hr, "Failed to determine packages and bundle scope.");
474 474
475 if (BOOTSTRAPPER_PACKAGE_SCOPE_PER_MACHINE_OR_PER_USER == pEngineState->registration.scope || BOOTSTRAPPER_PACKAGE_SCOPE_PER_USER_OR_PER_MACHINE == pEngineState->registration.scope) 475 if (BOOTSTRAPPER_PACKAGE_SCOPE_PER_MACHINE_OR_PER_USER == pEngineState->registration.scope || BOOTSTRAPPER_PACKAGE_SCOPE_PER_USER_OR_PER_MACHINE == pEngineState->registration.scope)
diff --git a/src/burn/engine/core.h b/src/burn/engine/core.h
index 75c0c941..520fdcd5 100644
--- a/src/burn/engine/core.h
+++ b/src/burn/engine/core.h
@@ -40,6 +40,7 @@ const LPCWSTR BURN_COMMANDLINE_SWITCH_PREFIX = L"burn.";
40const LPCWSTR BURN_BUNDLE_ACTION = L"WixBundleAction"; 40const LPCWSTR BURN_BUNDLE_ACTION = L"WixBundleAction";
41const LPCWSTR BURN_BUNDLE_ACTIVE_PARENT = L"WixBundleActiveParent"; 41const LPCWSTR BURN_BUNDLE_ACTIVE_PARENT = L"WixBundleActiveParent";
42const LPCWSTR BURN_BUNDLE_COMMAND_LINE_ACTION = L"WixBundleCommandLineAction"; 42const LPCWSTR BURN_BUNDLE_COMMAND_LINE_ACTION = L"WixBundleCommandLineAction";
43const LPCWSTR BURN_BUNDLE_DETECTED_SCOPE = L"WixBundleDetectedScope";
43const LPCWSTR BURN_BUNDLE_EXECUTE_PACKAGE_CACHE_FOLDER = L"WixBundleExecutePackageCacheFolder"; 44const LPCWSTR BURN_BUNDLE_EXECUTE_PACKAGE_CACHE_FOLDER = L"WixBundleExecutePackageCacheFolder";
44const LPCWSTR BURN_BUNDLE_EXECUTE_PACKAGE_ACTION = L"WixBundleExecutePackageAction"; 45const LPCWSTR BURN_BUNDLE_EXECUTE_PACKAGE_ACTION = L"WixBundleExecutePackageAction";
45const LPCWSTR BURN_BUNDLE_FORCED_RESTART_PACKAGE = L"WixBundleForcedRestartPackage"; 46const LPCWSTR BURN_BUNDLE_FORCED_RESTART_PACKAGE = L"WixBundleForcedRestartPackage";
@@ -47,7 +48,7 @@ const LPCWSTR BURN_BUNDLE_INSTALLED = L"WixBundleInstalled";
47const LPCWSTR BURN_BUNDLE_ELEVATED = L"WixBundleElevated"; 48const LPCWSTR BURN_BUNDLE_ELEVATED = L"WixBundleElevated";
48const LPCWSTR BURN_BUNDLE_PLANNED_SCOPE = L"WixBundlePlannedScope"; 49const LPCWSTR BURN_BUNDLE_PLANNED_SCOPE = L"WixBundlePlannedScope";
49const LPCWSTR BURN_BUNDLE_PROVIDER_KEY = L"WixBundleProviderKey"; 50const LPCWSTR BURN_BUNDLE_PROVIDER_KEY = L"WixBundleProviderKey";
50const LPCWSTR BURN_BUNDLE_SCOPE = L"WixBundleScope"; 51const LPCWSTR BURN_BUNDLE_AUTHORED_SCOPE = L"WixBundleAuthoredScope";
51const LPCWSTR BURN_BUNDLE_SOURCE_PROCESS_PATH = L"WixBundleSourceProcessPath"; 52const LPCWSTR BURN_BUNDLE_SOURCE_PROCESS_PATH = L"WixBundleSourceProcessPath";
52const LPCWSTR BURN_BUNDLE_SOURCE_PROCESS_FOLDER = L"WixBundleSourceProcessFolder"; 53const LPCWSTR BURN_BUNDLE_SOURCE_PROCESS_FOLDER = L"WixBundleSourceProcessFolder";
53const LPCWSTR BURN_BUNDLE_TAG = L"WixBundleTag"; 54const LPCWSTR BURN_BUNDLE_TAG = L"WixBundleTag";
diff --git a/src/burn/engine/engine.mc b/src/burn/engine/engine.mc
index 8e2f1a2e..f7ec0359 100644
--- a/src/burn/engine/engine.mc
+++ b/src/burn/engine/engine.mc
@@ -271,7 +271,7 @@ MessageId=101
271Severity=Success 271Severity=Success
272SymbolicName=MSG_DETECTED_PACKAGE 272SymbolicName=MSG_DETECTED_PACKAGE
273Language=English 273Language=English
274Detected package: %1!ls!, state: %2!hs!, authored scope: %6!hs!, cached: %3!hs!, install registration state: %4!hs!, cache registration state: %5!hs! 274Detected package: %1!ls!, state: %2!hs!, authored scope: %6!hs!, detected scope: %7!hs!, cached: %3!hs!, install registration state: %4!hs!, cache registration state: %5!hs!
275. 275.
276 276
277MessageId=102 277MessageId=102
@@ -414,6 +414,13 @@ Language=English
414Planned configurable scope: %1!hs! 414Planned configurable scope: %1!hs!
415. 415.
416 416
417MessageId=227
418Severity=Success
419SymbolicName=MSG_PLAN_INSTALLED_SCOPE
420Language=English
421Bundle was already installed with scope: %1!hs!
422.
423
417MessageId=201 424MessageId=201
418Severity=Success 425Severity=Success
419SymbolicName=MSG_PLANNED_PACKAGE 426SymbolicName=MSG_PLANNED_PACKAGE
diff --git a/src/burn/engine/msiengine.cpp b/src/burn/engine/msiengine.cpp
index 11bb8a0e..66e6b8fa 100644
--- a/src/burn/engine/msiengine.cpp
+++ b/src/burn/engine/msiengine.cpp
@@ -457,14 +457,24 @@ extern "C" HRESULT MsiEngineDetectPackage(
457 if (BOOTSTRAPPER_PACKAGE_SCOPE_PER_MACHINE_OR_PER_USER == pPackage->scope || BOOTSTRAPPER_PACKAGE_SCOPE_PER_USER_OR_PER_MACHINE == pPackage->scope) 457 if (BOOTSTRAPPER_PACKAGE_SCOPE_PER_MACHINE_OR_PER_USER == pPackage->scope || BOOTSTRAPPER_PACKAGE_SCOPE_PER_USER_OR_PER_MACHINE == pPackage->scope)
458 { 458 {
459 hr = WiuGetProductInfoEx(pPackage->Msi.sczProductCode, NULL, MSIINSTALLCONTEXT_MACHINE, INSTALLPROPERTY_VERSIONSTRING, &sczInstalledVersion); 459 hr = WiuGetProductInfoEx(pPackage->Msi.sczProductCode, NULL, MSIINSTALLCONTEXT_MACHINE, INSTALLPROPERTY_VERSIONSTRING, &sczInstalledVersion);
460 if (FAILED(hr)) 460 if (SUCCEEDED(hr))
461 {
462 pPackage->fDetectedPerMachine = TRUE;
463 }
464 else
461 { 465 {
462 hr = WiuGetProductInfoEx(pPackage->Msi.sczProductCode, NULL, MSIINSTALLCONTEXT_USERUNMANAGED, INSTALLPROPERTY_VERSIONSTRING, &sczInstalledVersion); 466 hr = WiuGetProductInfoEx(pPackage->Msi.sczProductCode, NULL, MSIINSTALLCONTEXT_USERUNMANAGED, INSTALLPROPERTY_VERSIONSTRING, &sczInstalledVersion);
467 if (SUCCEEDED(hr))
468 {
469 pPackage->fDetectedPerMachine = FALSE;
470 }
463 } 471 }
464 } 472 }
465 else 473 else
466 { 474 {
467 hr = WiuGetProductInfoEx(pPackage->Msi.sczProductCode, NULL, pPackage->scope == BOOTSTRAPPER_PACKAGE_SCOPE_PER_MACHINE ? MSIINSTALLCONTEXT_MACHINE : MSIINSTALLCONTEXT_USERUNMANAGED, INSTALLPROPERTY_VERSIONSTRING, &sczInstalledVersion); 475 pPackage->fDetectedPerMachine = BOOTSTRAPPER_PACKAGE_SCOPE_PER_MACHINE == pPackage->scope;
476
477 hr = WiuGetProductInfoEx(pPackage->Msi.sczProductCode, NULL, pPackage->fDetectedPerMachine ? MSIINSTALLCONTEXT_MACHINE : MSIINSTALLCONTEXT_USERUNMANAGED, INSTALLPROPERTY_VERSIONSTRING, &sczInstalledVersion);
468 } 478 }
469 if (SUCCEEDED(hr)) 479 if (SUCCEEDED(hr))
470 { 480 {
diff --git a/src/burn/engine/package.h b/src/burn/engine/package.h
index 5397e9cc..9828c120 100644
--- a/src/burn/engine/package.h
+++ b/src/burn/engine/package.h
@@ -283,6 +283,7 @@ typedef struct _BURN_PACKAGE
283 BURN_ROLLBACK_BOUNDARY* pRollbackBoundaryForward; // used during install and repair. 283 BURN_ROLLBACK_BOUNDARY* pRollbackBoundaryForward; // used during install and repair.
284 BURN_ROLLBACK_BOUNDARY* pRollbackBoundaryBackward; // used during uninstall. 284 BURN_ROLLBACK_BOUNDARY* pRollbackBoundaryBackward; // used during uninstall.
285 285
286 BOOL fDetectedPerMachine; // only valid after Detect.
286 BOOTSTRAPPER_PACKAGE_STATE currentState; // only valid after Detect. 287 BOOTSTRAPPER_PACKAGE_STATE currentState; // only valid after Detect.
287 BOOL fCached; // only valid after Detect. 288 BOOL fCached; // only valid after Detect.
288 BOOTSTRAPPER_CACHE_TYPE cacheType; // only valid during Plan. 289 BOOTSTRAPPER_CACHE_TYPE cacheType; // only valid during Plan.
diff --git a/src/burn/engine/plan.cpp b/src/burn/engine/plan.cpp
index 6c46269b..889ad68c 100644
--- a/src/burn/engine/plan.cpp
+++ b/src/burn/engine/plan.cpp
@@ -342,7 +342,7 @@ extern "C" HRESULT PlanSetVariables(
342 hr = VariableSetNumeric(pVariables, BURN_BUNDLE_ACTION, action, TRUE); 342 hr = VariableSetNumeric(pVariables, BURN_BUNDLE_ACTION, action, TRUE);
343 ExitOnFailure(hr, "Failed to set the bundle action built-in variable."); 343 ExitOnFailure(hr, "Failed to set the bundle action built-in variable.");
344 344
345 hr = VariableSetNumeric(pVariables, BURN_BUNDLE_SCOPE, authoredScope, TRUE); 345 hr = VariableSetNumeric(pVariables, BURN_BUNDLE_AUTHORED_SCOPE, authoredScope, TRUE);
346 ExitOnFailure(hr, "Failed to set the bundle authored scope built-in variable."); 346 ExitOnFailure(hr, "Failed to set the bundle authored scope built-in variable.");
347 347
348 hr = VariableSetNumeric(pVariables, BURN_BUNDLE_PLANNED_SCOPE, plannedScope, TRUE); 348 hr = VariableSetNumeric(pVariables, BURN_BUNDLE_PLANNED_SCOPE, plannedScope, TRUE);
@@ -826,6 +826,7 @@ extern "C" HRESULT PlanPackagesAndBundleScope(
826 __in BOOTSTRAPPER_SCOPE scope, 826 __in BOOTSTRAPPER_SCOPE scope,
827 __in BOOTSTRAPPER_PACKAGE_SCOPE authoredScope, 827 __in BOOTSTRAPPER_PACKAGE_SCOPE authoredScope,
828 __in BOOTSTRAPPER_SCOPE commandLineScope, 828 __in BOOTSTRAPPER_SCOPE commandLineScope,
829 __in BOOTSTRAPPER_SCOPE detectedScope,
829 __out BOOTSTRAPPER_SCOPE* pResultingScope, 830 __out BOOTSTRAPPER_SCOPE* pResultingScope,
830 __out BOOL* pfRegistrationPerMachine 831 __out BOOL* pfRegistrationPerMachine
831) 832)
@@ -854,6 +855,13 @@ extern "C" HRESULT PlanPackagesAndBundleScope(
854 } 855 }
855 } 856 }
856 857
858 if (BOOTSTRAPPER_SCOPE_DEFAULT != detectedScope)
859 {
860 scope = detectedScope;
861
862 LogId(REPORT_WARNING, MSG_PLAN_INSTALLED_SCOPE, LoggingBundleScopeToString(detectedScope));
863 }
864
857 for (DWORD i = 0; i < cPackages; ++i) 865 for (DWORD i = 0; i < cPackages; ++i)
858 { 866 {
859 BURN_PACKAGE* pPackage = rgPackages + i; 867 BURN_PACKAGE* pPackage = rgPackages + i;
diff --git a/src/burn/engine/plan.h b/src/burn/engine/plan.h
index a20d7c76..f9996ac0 100644
--- a/src/burn/engine/plan.h
+++ b/src/burn/engine/plan.h
@@ -488,6 +488,7 @@ HRESULT PlanPackagesAndBundleScope(
488 __in BOOTSTRAPPER_SCOPE scope, 488 __in BOOTSTRAPPER_SCOPE scope,
489 __in BOOTSTRAPPER_PACKAGE_SCOPE authoredScope, 489 __in BOOTSTRAPPER_PACKAGE_SCOPE authoredScope,
490 __in BOOTSTRAPPER_SCOPE commandLineScope, 490 __in BOOTSTRAPPER_SCOPE commandLineScope,
491 __in BOOTSTRAPPER_SCOPE detectedScope,
491 __out BOOTSTRAPPER_SCOPE* pResultingScope, 492 __out BOOTSTRAPPER_SCOPE* pResultingScope,
492 __out BOOL* pfPerMachine 493 __out BOOL* pfPerMachine
493); 494);
diff --git a/src/burn/engine/registration.cpp b/src/burn/engine/registration.cpp
index fa1b024a..e588bef0 100644
--- a/src/burn/engine/registration.cpp
+++ b/src/burn/engine/registration.cpp
@@ -29,6 +29,7 @@ const LPCWSTR REGISTRY_BUNDLE_UNINSTALL_STRING = L"UninstallString";
29const LPCWSTR REGISTRY_BUNDLE_RESUME_COMMAND_LINE = L"BundleResumeCommandLine"; 29const LPCWSTR REGISTRY_BUNDLE_RESUME_COMMAND_LINE = L"BundleResumeCommandLine";
30const LPCWSTR REGISTRY_BUNDLE_VERSION_MAJOR = L"VersionMajor"; 30const LPCWSTR REGISTRY_BUNDLE_VERSION_MAJOR = L"VersionMajor";
31const LPCWSTR REGISTRY_BUNDLE_VERSION_MINOR = L"VersionMinor"; 31const LPCWSTR REGISTRY_BUNDLE_VERSION_MINOR = L"VersionMinor";
32const LPCWSTR REGISTRY_BUNDLE_SCOPE = L"BundleScope";
32const LPCWSTR SWIDTAG_FOLDER = L"swidtag"; 33const LPCWSTR SWIDTAG_FOLDER = L"swidtag";
33const LPCWSTR REGISTRY_BUNDLE_VARIABLE_KEY = L"variables"; 34const LPCWSTR REGISTRY_BUNDLE_VARIABLE_KEY = L"variables";
34 35
@@ -303,6 +304,10 @@ extern "C" HRESULT RegistrationParseFromXml(
303 pRegistration->hkRoot = reinterpret_cast<HKEY>(0ull); 304 pRegistration->hkRoot = reinterpret_cast<HKEY>(0ull);
304 } 305 }
305 306
307 // build uninstall registry key path
308 hr = StrAllocFormatted(&pRegistration->sczRegistrationKey, L"%ls\\%ls", BURN_REGISTRATION_REGISTRY_UNINSTALL_KEY, pRegistration->sczCode);
309 ExitOnFailure(hr, "Failed to build uninstall registry key path.");
310
306LExit: 311LExit:
307 ReleaseObject(pixnRegistrationNode); 312 ReleaseObject(pixnRegistrationNode);
308 ReleaseObject(pixnArpNode); 313 ReleaseObject(pixnArpNode);
@@ -458,6 +463,9 @@ extern "C" HRESULT RegistrationSetDynamicVariables(
458 hr = VariableSetNumeric(pVariables, BURN_BUNDLE_INSTALLED, llInstalled, TRUE); 463 hr = VariableSetNumeric(pVariables, BURN_BUNDLE_INSTALLED, llInstalled, TRUE);
459 ExitOnFailure(hr, "Failed to set the bundle installed built-in variable."); 464 ExitOnFailure(hr, "Failed to set the bundle installed built-in variable.");
460 465
466 hr = VariableSetNumeric(pVariables, BURN_BUNDLE_DETECTED_SCOPE, pRegistration->detectedScope, TRUE);
467 ExitOnFailure(hr, "Failed to set the bundle detected scope built-in variable.");
468
461 hr = VariableSetNumeric(pVariables, VARIABLE_REBOOTPENDING, IsWuRebootPending() || IsRegistryRebootPending(), TRUE); 469 hr = VariableSetNumeric(pVariables, VARIABLE_REBOOTPENDING, IsWuRebootPending() || IsRegistryRebootPending(), TRUE);
462 ExitOnFailure(hr, "Failed to overwrite the bundle reboot-pending built-in variable."); 470 ExitOnFailure(hr, "Failed to overwrite the bundle reboot-pending built-in variable.");
463 471
@@ -479,16 +487,20 @@ extern "C" HRESULT RegistrationDetectInstalled(
479 { 487 {
480 // For PUOM/PMOU bundles, check per-machine then fall back to per-user. 488 // For PUOM/PMOU bundles, check per-machine then fall back to per-user.
481 hr = DetectInstalled(pRegistration, HKEY_LOCAL_MACHINE); 489 hr = DetectInstalled(pRegistration, HKEY_LOCAL_MACHINE);
482 ExitOnFailure(hr, "Failed to detect HKEY_LOCAL_MACHINE bundle registration install state.");
483 490
484 if (BOOTSTRAPPER_REGISTRATION_TYPE_NONE == pRegistration->detectedRegistrationType) 491 if (FAILED(hr))
485 { 492 {
486 hr = DetectInstalled(pRegistration, HKEY_CURRENT_USER); 493 hr = DetectInstalled(pRegistration, HKEY_CURRENT_USER);
487 ExitOnFailure(hr, "Failed to detect HKEY_CURRENT_USER bundle registration install state.");
488 } 494 }
489 } 495 }
490 496
491LExit: 497//LExit:
498 // Not finding the key or value is okay.
499 if (E_FILENOTFOUND == hr || E_PATHNOTFOUND == hr)
500 {
501 hr = S_OK;
502 }
503
492 return hr; 504 return hr;
493} 505}
494 506
@@ -654,6 +666,9 @@ extern "C" HRESULT RegistrationSessionBegin(
654 hr = RegWriteStringFormatted(hkRegistration, REGISTRY_BUNDLE_DISPLAY_ICON, L"%s,0", pRegistration->sczCacheExecutablePath); 666 hr = RegWriteStringFormatted(hkRegistration, REGISTRY_BUNDLE_DISPLAY_ICON, L"%s,0", pRegistration->sczCacheExecutablePath);
655 ExitOnFailure(hr, "Failed to write %ls value.", REGISTRY_BUNDLE_DISPLAY_ICON); 667 ExitOnFailure(hr, "Failed to write %ls value.", REGISTRY_BUNDLE_DISPLAY_ICON);
656 668
669 hr = RegWriteNumber(hkRegistration, REGISTRY_BUNDLE_SCOPE, pRegistration->fPerMachine ? BOOTSTRAPPER_SCOPE_PER_MACHINE : BOOTSTRAPPER_SCOPE_PER_USER);
670 ExitOnFailure(hr, "Failed to write %ls value.", REGISTRY_BUNDLE_SCOPE);
671
657 // update display name 672 // update display name
658 hr = UpdateBundleNameRegistration(pRegistration, pVariables, hkRegistration, BOOTSTRAPPER_REGISTRATION_TYPE_INPROGRESS == registrationType); 673 hr = UpdateBundleNameRegistration(pRegistration, pVariables, hkRegistration, BOOTSTRAPPER_REGISTRATION_TYPE_INPROGRESS == registrationType);
659 ExitOnFailure(hr, "Failed to update name and publisher."); 674 ExitOnFailure(hr, "Failed to update name and publisher.");
@@ -1054,10 +1069,6 @@ extern "C" HRESULT RegistrationSetPaths(
1054 // save registration key root 1069 // save registration key root
1055 pRegistration->hkRoot = pRegistration->fPerMachine ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER; 1070 pRegistration->hkRoot = pRegistration->fPerMachine ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER;
1056 1071
1057 // build uninstall registry key path
1058 hr = StrAllocFormatted(&pRegistration->sczRegistrationKey, L"%ls\\%ls", BURN_REGISTRATION_REGISTRY_UNINSTALL_KEY, pRegistration->sczCode);
1059 ExitOnFailure(hr, "Failed to build uninstall registry key path.");
1060
1061 // build cache directory 1072 // build cache directory
1062 hr = CacheGetCompletedPath(pCache, pRegistration->fPerMachine, pRegistration->sczCode, &sczCacheDirectory); 1073 hr = CacheGetCompletedPath(pCache, pRegistration->fPerMachine, pRegistration->sczCode, &sczCacheDirectory);
1063 ExitOnFailure(hr, "Failed to build cache directory."); 1074 ExitOnFailure(hr, "Failed to build cache directory.");
@@ -1754,25 +1765,28 @@ static HRESULT DetectInstalled(
1754 HRESULT hr = S_OK; 1765 HRESULT hr = S_OK;
1755 HKEY hkRegistration = NULL; 1766 HKEY hkRegistration = NULL;
1756 DWORD dwInstalled = 0; 1767 DWORD dwInstalled = 0;
1768 DWORD dwScope = 0;
1757 1769
1758 pRegistration->fCached = pRegistration->sczCacheExecutablePath && FileExistsEx(pRegistration->sczCacheExecutablePath, NULL); 1770 pRegistration->fCached = pRegistration->sczCacheExecutablePath && FileExistsEx(pRegistration->sczCacheExecutablePath, NULL);
1759 pRegistration->detectedRegistrationType = BOOTSTRAPPER_REGISTRATION_TYPE_NONE; 1771 pRegistration->detectedRegistrationType = BOOTSTRAPPER_REGISTRATION_TYPE_NONE;
1772 pRegistration->detectedScope = BOOTSTRAPPER_SCOPE_DEFAULT;
1760 1773
1761 // open registration key 1774 // open registration key
1762 hr = RegOpen(hkRoot, pRegistration->sczRegistrationKey, KEY_QUERY_VALUE, &hkRegistration); 1775 hr = RegOpen(hkRoot, pRegistration->sczRegistrationKey, KEY_QUERY_VALUE, &hkRegistration);
1763 if (SUCCEEDED(hr)) 1776 if (SUCCEEDED(hr))
1764 { 1777 {
1765 hr = RegReadNumber(hkRegistration, REGISTRY_BUNDLE_INSTALLED, &dwInstalled); 1778 hr = RegReadNumber(hkRegistration, REGISTRY_BUNDLE_INSTALLED, &dwInstalled);
1779 ExitOnFailure(hr, "Failed to read registration %ls@%ls.", pRegistration->sczRegistrationKey, REGISTRY_BUNDLE_INSTALLED);
1766 1780
1767 pRegistration->detectedRegistrationType = (1 == dwInstalled) ? BOOTSTRAPPER_REGISTRATION_TYPE_FULL : BOOTSTRAPPER_REGISTRATION_TYPE_INPROGRESS; 1781 pRegistration->detectedRegistrationType = (1 == dwInstalled) ? BOOTSTRAPPER_REGISTRATION_TYPE_FULL : BOOTSTRAPPER_REGISTRATION_TYPE_INPROGRESS;
1768 }
1769 1782
1770 // Not finding the key or value is okay. 1783 hr = RegReadNumber(hkRegistration, REGISTRY_BUNDLE_SCOPE, &dwScope);
1771 if (E_FILENOTFOUND == hr || E_PATHNOTFOUND == hr) 1784 ExitOnFailure(hr, "Failed to read registration %ls@%ls.", pRegistration->sczRegistrationKey, REGISTRY_BUNDLE_SCOPE);
1772 { 1785
1773 hr = S_OK; 1786 pRegistration->detectedScope = static_cast<BOOTSTRAPPER_SCOPE>(dwScope);
1774 } 1787 }
1775 1788
1789LExit:
1776 ReleaseRegKey(hkRegistration); 1790 ReleaseRegKey(hkRegistration);
1777 1791
1778 return hr; 1792 return hr;
diff --git a/src/burn/engine/registration.h b/src/burn/engine/registration.h
index 361c379d..f340999a 100644
--- a/src/burn/engine/registration.h
+++ b/src/burn/engine/registration.h
@@ -97,11 +97,12 @@ typedef struct _BURN_REGISTRATION
97 // For configurable-scope bundles, fPerMachine is only valid after 97 // For configurable-scope bundles, fPerMachine is only valid after
98 // planning when scope is known. For fixed per-machine or per-user 98 // planning when scope is known. For fixed per-machine or per-user
99 // bundles, valid immediately. 99 // bundles, valid immediately.
100 BOOL fPerMachine; 100 BOOL fPerMachine;
101 BOOL fForceSystemComponent; 101 BOOL fForceSystemComponent;
102 BOOL fDisableResume; 102 BOOL fDisableResume;
103 BOOL fCached; 103 BOOL fCached;
104 BOOTSTRAPPER_REGISTRATION_TYPE detectedRegistrationType; 104 BOOTSTRAPPER_REGISTRATION_TYPE detectedRegistrationType;
105 BOOTSTRAPPER_SCOPE detectedScope;
105 BOOTSTRAPPER_PACKAGE_SCOPE scope; 106 BOOTSTRAPPER_PACKAGE_SCOPE scope;
106 LPWSTR sczCode; 107 LPWSTR sczCode;
107 LPWSTR sczTag; 108 LPWSTR sczTag;
diff --git a/src/burn/engine/variable.cpp b/src/burn/engine/variable.cpp
index 1b7dc4d1..092b7e9d 100644
--- a/src/burn/engine/variable.cpp
+++ b/src/burn/engine/variable.cpp
@@ -305,8 +305,9 @@ extern "C" HRESULT VariableInitialize(
305 {BURN_BUNDLE_TAG, InitializeVariableString, (DWORD_PTR)L"", FALSE, TRUE}, 305 {BURN_BUNDLE_TAG, InitializeVariableString, (DWORD_PTR)L"", FALSE, TRUE},
306 {BURN_BUNDLE_UILEVEL, InitializeVariableNumeric, 0, FALSE, TRUE}, 306 {BURN_BUNDLE_UILEVEL, InitializeVariableNumeric, 0, FALSE, TRUE},
307 {BURN_BUNDLE_VERSION, InitializeVariableVersion, (DWORD_PTR)L"0", FALSE, TRUE}, 307 {BURN_BUNDLE_VERSION, InitializeVariableVersion, (DWORD_PTR)L"0", FALSE, TRUE},
308 {BURN_BUNDLE_SCOPE, InitializeVariableNumeric, 0, FALSE, TRUE}, 308 {BURN_BUNDLE_AUTHORED_SCOPE, InitializeVariableNumeric, 0, FALSE, TRUE},
309 {BURN_BUNDLE_PLANNED_SCOPE, InitializeVariableNumeric, 0, FALSE, TRUE}, 309 {BURN_BUNDLE_PLANNED_SCOPE, InitializeVariableNumeric, 0, FALSE, TRUE},
310 {BURN_BUNDLE_DETECTED_SCOPE, InitializeVariableNumeric, 0, FALSE, TRUE},
310 }; 311 };
311 312
312 const WELL_KNOWN_VARIABLE_DECLARATION vrgWellKnownVariableNames[] = 313 const WELL_KNOWN_VARIABLE_DECLARATION vrgWellKnownVariableNames[] =
diff --git a/src/test/burn/WixToolsetTest.BurnE2E/ConfigurableScopeTests.cs b/src/test/burn/WixToolsetTest.BurnE2E/ConfigurableScopeTests.cs
index 1b7d6d75..8b31ac00 100644
--- a/src/test/burn/WixToolsetTest.BurnE2E/ConfigurableScopeTests.cs
+++ b/src/test/burn/WixToolsetTest.BurnE2E/ConfigurableScopeTests.cs
@@ -63,7 +63,7 @@ namespace WixToolsetTest.BurnE2E
63 Assert.True(LogVerifier.MessageInLogFile(log, "Planned package: PmouPkg1.msi, state: Absent, default requested: Present, ba requested: Present, execute: Install, rollback: Uninstall, scope: PerUser")); 63 Assert.True(LogVerifier.MessageInLogFile(log, "Planned package: PmouPkg1.msi, state: Absent, default requested: Present, ba requested: Present, execute: Install, rollback: Uninstall, scope: PerUser"));
64 Assert.True(LogVerifier.MessageInLogFile(log, "Planned package: PmouPkg2.msi, state: Absent, default requested: Present, ba requested: Present, execute: Install, rollback: Uninstall, scope: PerUser")); 64 Assert.True(LogVerifier.MessageInLogFile(log, "Planned package: PmouPkg2.msi, state: Absent, default requested: Present, ba requested: Present, execute: Install, rollback: Uninstall, scope: PerUser"));
65 65
66 bundle.Uninstall(arguments: "/peruser"); 66 bundle.Uninstall();
67 bundle.VerifyUnregisteredAndRemovedFromPackageCache(plannedPerMachine: false); 67 bundle.VerifyUnregisteredAndRemovedFromPackageCache(plannedPerMachine: false);
68 pkg1.VerifyInstalled(false); 68 pkg1.VerifyInstalled(false);
69 pkg2.VerifyInstalled(false); 69 pkg2.VerifyInstalled(false);
@@ -89,7 +89,7 @@ namespace WixToolsetTest.BurnE2E
89 Assert.True(LogVerifier.MessageInLogFile(log, "Planned package: PuomPkg1.msi, state: Absent, default requested: Present, ba requested: Present, execute: Install, rollback: Uninstall, scope: PerMachine,")); 89 Assert.True(LogVerifier.MessageInLogFile(log, "Planned package: PuomPkg1.msi, state: Absent, default requested: Present, ba requested: Present, execute: Install, rollback: Uninstall, scope: PerMachine,"));
90 Assert.True(LogVerifier.MessageInLogFile(log, "Planned package: PuomPkg2.msi, state: Absent, default requested: Present, ba requested: Present, execute: Install, rollback: Uninstall, scope: PerMachine,")); 90 Assert.True(LogVerifier.MessageInLogFile(log, "Planned package: PuomPkg2.msi, state: Absent, default requested: Present, ba requested: Present, execute: Install, rollback: Uninstall, scope: PerMachine,"));
91 91
92 bundle.Uninstall(arguments: "/permachine"); 92 bundle.Uninstall();
93 bundle.VerifyUnregisteredAndRemovedFromPackageCache(plannedPerMachine: true); 93 bundle.VerifyUnregisteredAndRemovedFromPackageCache(plannedPerMachine: true);
94 pkg1.VerifyInstalled(false); 94 pkg1.VerifyInstalled(false);
95 pkg2.VerifyInstalled(false); 95 pkg2.VerifyInstalled(false);
@@ -115,6 +115,11 @@ namespace WixToolsetTest.BurnE2E
115 Assert.True(LogVerifier.MessageInLogFile(log, "Planned package: PmouPkg1.msi, state: Absent, default requested: Present, ba requested: Present, execute: Install, rollback: Uninstall, scope: PerMachine,")); 115 Assert.True(LogVerifier.MessageInLogFile(log, "Planned package: PmouPkg1.msi, state: Absent, default requested: Present, ba requested: Present, execute: Install, rollback: Uninstall, scope: PerMachine,"));
116 Assert.True(LogVerifier.MessageInLogFile(log, "Planned package: PmouPkg2.msi, state: Absent, default requested: Present, ba requested: Present, execute: Install, rollback: Uninstall, scope: PerMachine,")); 116 Assert.True(LogVerifier.MessageInLogFile(log, "Planned package: PmouPkg2.msi, state: Absent, default requested: Present, ba requested: Present, execute: Install, rollback: Uninstall, scope: PerMachine,"));
117 117
118 log = bundle.Repair();
119 Assert.True(LogVerifier.MessageInLogFile(log, "Bundle was already installed with scope: PerMachine"));
120 Assert.True(LogVerifier.MessageInLogFile(log, "Detected package: PmouPkg1.msi, state: Present, authored scope: PerMachineOrUser, detected scope: PerMachine,"));
121 Assert.True(LogVerifier.MessageInLogFile(log, "Detected package: PmouPkg2.msi, state: Present, authored scope: PerMachineOrUser, detected scope: PerMachine,"));
122
118 bundle.Uninstall(); 123 bundle.Uninstall();
119 bundle.VerifyUnregisteredAndRemovedFromPackageCache(plannedPerMachine: true); 124 bundle.VerifyUnregisteredAndRemovedFromPackageCache(plannedPerMachine: true);
120 pkg1.VerifyInstalled(false); 125 pkg1.VerifyInstalled(false);
@@ -450,9 +455,15 @@ namespace WixToolsetTest.BurnE2E
450 455
451 Assert.True(LogVerifier.MessageInLogFile(log, "Plan begin, 5 packages, action: Install, planned scope: Default")); 456 Assert.True(LogVerifier.MessageInLogFile(log, "Plan begin, 5 packages, action: Install, planned scope: Default"));
452 Assert.True(LogVerifier.MessageInLogFile(log, "Planned package: PerMachinePkg.msi, state: Absent, default requested: Present, ba requested: Present, execute: Install, rollback: Uninstall, scope: PerMachine,")); 457 Assert.True(LogVerifier.MessageInLogFile(log, "Planned package: PerMachinePkg.msi, state: Absent, default requested: Present, ba requested: Present, execute: Install, rollback: Uninstall, scope: PerMachine,"));
453 Assert.True(LogVerifier.MessageInLogFile(log, "Planned package: PerUserPkg.msi, state: Absent, default requested: Present, ba requested: Present, execute: Install, rollback: Uninstall, scope: PerUser"));
454 Assert.True(LogVerifier.MessageInLogFile(log, "Planned package: PmouPkg1.msi, state: Absent, default requested: Present, ba requested: Present, execute: Install, rollback: Uninstall, scope: PerMachine,")); 458 Assert.True(LogVerifier.MessageInLogFile(log, "Planned package: PmouPkg1.msi, state: Absent, default requested: Present, ba requested: Present, execute: Install, rollback: Uninstall, scope: PerMachine,"));
455 Assert.True(LogVerifier.MessageInLogFile(log, "Planned package: PmouPkg2.msi, state: Absent, default requested: Present, ba requested: Present, execute: Install, rollback: Uninstall, scope: PerMachine,")); 459 Assert.True(LogVerifier.MessageInLogFile(log, "Planned package: PmouPkg2.msi, state: Absent, default requested: Present, ba requested: Present, execute: Install, rollback: Uninstall, scope: PerMachine,"));
460 Assert.True(LogVerifier.MessageInLogFile(log, "Planned package: PerUserPkg.msi, state: Absent, default requested: Present, ba requested: Present, execute: Install, rollback: Uninstall, scope: PerUser"));
461
462 log = bundle.Repair();
463 Assert.True(LogVerifier.MessageInLogFile(log, "Detected package: PerMachinePkg.msi, state: Present, authored scope: PerMachine, detected scope: PerMachine,"));
464 Assert.True(LogVerifier.MessageInLogFile(log, "Detected package: PmouPkg1.msi, state: Present, authored scope: PerMachineOrUser, detected scope: PerMachine,"));
465 Assert.True(LogVerifier.MessageInLogFile(log, "Detected package: PmouPkg2.msi, state: Present, authored scope: PerMachineOrUser, detected scope: PerMachine,"));
466 Assert.True(LogVerifier.MessageInLogFile(log, "Detected package: PerUserPkg.msi, state: Present, authored scope: PerUser, detected scope: PerUser,"));
456 467
457 bundle.Uninstall(); 468 bundle.Uninstall();
458 bundle.VerifyUnregisteredAndRemovedFromPackageCache(plannedPerMachine: true); 469 bundle.VerifyUnregisteredAndRemovedFromPackageCache(plannedPerMachine: true);