From 702b38fe565781f3bfc5b1d92a3404b535ace2a1 Mon Sep 17 00:00:00 2001 From: Bob Arnson Date: Tue, 10 Feb 2026 20:33:03 -0500 Subject: Log detected package scope and lock bundle scope. - Fixes https://github.com/wixtoolset/issues/issues/9232 - Fixes https://github.com/wixtoolset/issues/issues/9240 --- src/burn/engine/core.cpp | 4 +-- src/burn/engine/core.h | 3 +- src/burn/engine/engine.mc | 9 ++++- src/burn/engine/msiengine.cpp | 14 ++++++-- src/burn/engine/package.h | 1 + src/burn/engine/plan.cpp | 10 +++++- src/burn/engine/plan.h | 1 + src/burn/engine/registration.cpp | 40 +++++++++++++++------- src/burn/engine/registration.h | 3 +- src/burn/engine/variable.cpp | 3 +- .../ConfigurableScopeTests.cs | 17 +++++++-- 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( pEngineState->registration.fEligibleForCleanup = FALSE; } - 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)); + 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)); if (BURN_PACKAGE_TYPE_MSI == pPackage->type) { @@ -469,7 +469,7 @@ extern "C" HRESULT CorePlan( pEngineState->plan.fDisableRollback = pEngineState->fDisableRollback || BOOTSTRAPPER_ACTION_UNSAFE_UNINSTALL == pEngineState->plan.action; pEngineState->plan.fPlanPackageCacheRollback = BOOTSTRAPPER_REGISTRATION_TYPE_NONE == pEngineState->registration.detectedRegistrationType; - hr = PlanPackagesAndBundleScope(pEngineState->packages.rgPackages, pEngineState->packages.cPackages, pEngineState->plan.plannedScope, pEngineState->registration.scope, pEngineState->command.commandLineScope, &pEngineState->plan.plannedScope, &pEngineState->registration.fPerMachine); + 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); ExitOnFailure(hr, "Failed to determine packages and bundle scope."); 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."; const LPCWSTR BURN_BUNDLE_ACTION = L"WixBundleAction"; const LPCWSTR BURN_BUNDLE_ACTIVE_PARENT = L"WixBundleActiveParent"; const LPCWSTR BURN_BUNDLE_COMMAND_LINE_ACTION = L"WixBundleCommandLineAction"; +const LPCWSTR BURN_BUNDLE_DETECTED_SCOPE = L"WixBundleDetectedScope"; const LPCWSTR BURN_BUNDLE_EXECUTE_PACKAGE_CACHE_FOLDER = L"WixBundleExecutePackageCacheFolder"; const LPCWSTR BURN_BUNDLE_EXECUTE_PACKAGE_ACTION = L"WixBundleExecutePackageAction"; const LPCWSTR BURN_BUNDLE_FORCED_RESTART_PACKAGE = L"WixBundleForcedRestartPackage"; @@ -47,7 +48,7 @@ const LPCWSTR BURN_BUNDLE_INSTALLED = L"WixBundleInstalled"; const LPCWSTR BURN_BUNDLE_ELEVATED = L"WixBundleElevated"; const LPCWSTR BURN_BUNDLE_PLANNED_SCOPE = L"WixBundlePlannedScope"; const LPCWSTR BURN_BUNDLE_PROVIDER_KEY = L"WixBundleProviderKey"; -const LPCWSTR BURN_BUNDLE_SCOPE = L"WixBundleScope"; +const LPCWSTR BURN_BUNDLE_AUTHORED_SCOPE = L"WixBundleAuthoredScope"; const LPCWSTR BURN_BUNDLE_SOURCE_PROCESS_PATH = L"WixBundleSourceProcessPath"; const LPCWSTR BURN_BUNDLE_SOURCE_PROCESS_FOLDER = L"WixBundleSourceProcessFolder"; const 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 Severity=Success SymbolicName=MSG_DETECTED_PACKAGE Language=English -Detected package: %1!ls!, state: %2!hs!, authored scope: %6!hs!, cached: %3!hs!, install registration state: %4!hs!, cache registration state: %5!hs! +Detected 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! . MessageId=102 @@ -414,6 +414,13 @@ Language=English Planned configurable scope: %1!hs! . +MessageId=227 +Severity=Success +SymbolicName=MSG_PLAN_INSTALLED_SCOPE +Language=English +Bundle was already installed with scope: %1!hs! +. + MessageId=201 Severity=Success SymbolicName=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( if (BOOTSTRAPPER_PACKAGE_SCOPE_PER_MACHINE_OR_PER_USER == pPackage->scope || BOOTSTRAPPER_PACKAGE_SCOPE_PER_USER_OR_PER_MACHINE == pPackage->scope) { hr = WiuGetProductInfoEx(pPackage->Msi.sczProductCode, NULL, MSIINSTALLCONTEXT_MACHINE, INSTALLPROPERTY_VERSIONSTRING, &sczInstalledVersion); - if (FAILED(hr)) + if (SUCCEEDED(hr)) + { + pPackage->fDetectedPerMachine = TRUE; + } + else { hr = WiuGetProductInfoEx(pPackage->Msi.sczProductCode, NULL, MSIINSTALLCONTEXT_USERUNMANAGED, INSTALLPROPERTY_VERSIONSTRING, &sczInstalledVersion); + if (SUCCEEDED(hr)) + { + pPackage->fDetectedPerMachine = FALSE; + } } } else { - hr = WiuGetProductInfoEx(pPackage->Msi.sczProductCode, NULL, pPackage->scope == BOOTSTRAPPER_PACKAGE_SCOPE_PER_MACHINE ? MSIINSTALLCONTEXT_MACHINE : MSIINSTALLCONTEXT_USERUNMANAGED, INSTALLPROPERTY_VERSIONSTRING, &sczInstalledVersion); + pPackage->fDetectedPerMachine = BOOTSTRAPPER_PACKAGE_SCOPE_PER_MACHINE == pPackage->scope; + + hr = WiuGetProductInfoEx(pPackage->Msi.sczProductCode, NULL, pPackage->fDetectedPerMachine ? MSIINSTALLCONTEXT_MACHINE : MSIINSTALLCONTEXT_USERUNMANAGED, INSTALLPROPERTY_VERSIONSTRING, &sczInstalledVersion); } if (SUCCEEDED(hr)) { 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 BURN_ROLLBACK_BOUNDARY* pRollbackBoundaryForward; // used during install and repair. BURN_ROLLBACK_BOUNDARY* pRollbackBoundaryBackward; // used during uninstall. + BOOL fDetectedPerMachine; // only valid after Detect. BOOTSTRAPPER_PACKAGE_STATE currentState; // only valid after Detect. BOOL fCached; // only valid after Detect. 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( hr = VariableSetNumeric(pVariables, BURN_BUNDLE_ACTION, action, TRUE); ExitOnFailure(hr, "Failed to set the bundle action built-in variable."); - hr = VariableSetNumeric(pVariables, BURN_BUNDLE_SCOPE, authoredScope, TRUE); + hr = VariableSetNumeric(pVariables, BURN_BUNDLE_AUTHORED_SCOPE, authoredScope, TRUE); ExitOnFailure(hr, "Failed to set the bundle authored scope built-in variable."); hr = VariableSetNumeric(pVariables, BURN_BUNDLE_PLANNED_SCOPE, plannedScope, TRUE); @@ -826,6 +826,7 @@ extern "C" HRESULT PlanPackagesAndBundleScope( __in BOOTSTRAPPER_SCOPE scope, __in BOOTSTRAPPER_PACKAGE_SCOPE authoredScope, __in BOOTSTRAPPER_SCOPE commandLineScope, + __in BOOTSTRAPPER_SCOPE detectedScope, __out BOOTSTRAPPER_SCOPE* pResultingScope, __out BOOL* pfRegistrationPerMachine ) @@ -854,6 +855,13 @@ extern "C" HRESULT PlanPackagesAndBundleScope( } } + if (BOOTSTRAPPER_SCOPE_DEFAULT != detectedScope) + { + scope = detectedScope; + + LogId(REPORT_WARNING, MSG_PLAN_INSTALLED_SCOPE, LoggingBundleScopeToString(detectedScope)); + } + for (DWORD i = 0; i < cPackages; ++i) { 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( __in BOOTSTRAPPER_SCOPE scope, __in BOOTSTRAPPER_PACKAGE_SCOPE authoredScope, __in BOOTSTRAPPER_SCOPE commandLineScope, + __in BOOTSTRAPPER_SCOPE detectedScope, __out BOOTSTRAPPER_SCOPE* pResultingScope, __out BOOL* pfPerMachine ); 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"; const LPCWSTR REGISTRY_BUNDLE_RESUME_COMMAND_LINE = L"BundleResumeCommandLine"; const LPCWSTR REGISTRY_BUNDLE_VERSION_MAJOR = L"VersionMajor"; const LPCWSTR REGISTRY_BUNDLE_VERSION_MINOR = L"VersionMinor"; +const LPCWSTR REGISTRY_BUNDLE_SCOPE = L"BundleScope"; const LPCWSTR SWIDTAG_FOLDER = L"swidtag"; const LPCWSTR REGISTRY_BUNDLE_VARIABLE_KEY = L"variables"; @@ -303,6 +304,10 @@ extern "C" HRESULT RegistrationParseFromXml( pRegistration->hkRoot = reinterpret_cast(0ull); } + // build uninstall registry key path + hr = StrAllocFormatted(&pRegistration->sczRegistrationKey, L"%ls\\%ls", BURN_REGISTRATION_REGISTRY_UNINSTALL_KEY, pRegistration->sczCode); + ExitOnFailure(hr, "Failed to build uninstall registry key path."); + LExit: ReleaseObject(pixnRegistrationNode); ReleaseObject(pixnArpNode); @@ -458,6 +463,9 @@ extern "C" HRESULT RegistrationSetDynamicVariables( hr = VariableSetNumeric(pVariables, BURN_BUNDLE_INSTALLED, llInstalled, TRUE); ExitOnFailure(hr, "Failed to set the bundle installed built-in variable."); + hr = VariableSetNumeric(pVariables, BURN_BUNDLE_DETECTED_SCOPE, pRegistration->detectedScope, TRUE); + ExitOnFailure(hr, "Failed to set the bundle detected scope built-in variable."); + hr = VariableSetNumeric(pVariables, VARIABLE_REBOOTPENDING, IsWuRebootPending() || IsRegistryRebootPending(), TRUE); ExitOnFailure(hr, "Failed to overwrite the bundle reboot-pending built-in variable."); @@ -479,16 +487,20 @@ extern "C" HRESULT RegistrationDetectInstalled( { // For PUOM/PMOU bundles, check per-machine then fall back to per-user. hr = DetectInstalled(pRegistration, HKEY_LOCAL_MACHINE); - ExitOnFailure(hr, "Failed to detect HKEY_LOCAL_MACHINE bundle registration install state."); - if (BOOTSTRAPPER_REGISTRATION_TYPE_NONE == pRegistration->detectedRegistrationType) + if (FAILED(hr)) { hr = DetectInstalled(pRegistration, HKEY_CURRENT_USER); - ExitOnFailure(hr, "Failed to detect HKEY_CURRENT_USER bundle registration install state."); } } -LExit: +//LExit: + // Not finding the key or value is okay. + if (E_FILENOTFOUND == hr || E_PATHNOTFOUND == hr) + { + hr = S_OK; + } + return hr; } @@ -654,6 +666,9 @@ extern "C" HRESULT RegistrationSessionBegin( hr = RegWriteStringFormatted(hkRegistration, REGISTRY_BUNDLE_DISPLAY_ICON, L"%s,0", pRegistration->sczCacheExecutablePath); ExitOnFailure(hr, "Failed to write %ls value.", REGISTRY_BUNDLE_DISPLAY_ICON); + hr = RegWriteNumber(hkRegistration, REGISTRY_BUNDLE_SCOPE, pRegistration->fPerMachine ? BOOTSTRAPPER_SCOPE_PER_MACHINE : BOOTSTRAPPER_SCOPE_PER_USER); + ExitOnFailure(hr, "Failed to write %ls value.", REGISTRY_BUNDLE_SCOPE); + // update display name hr = UpdateBundleNameRegistration(pRegistration, pVariables, hkRegistration, BOOTSTRAPPER_REGISTRATION_TYPE_INPROGRESS == registrationType); ExitOnFailure(hr, "Failed to update name and publisher."); @@ -1054,10 +1069,6 @@ extern "C" HRESULT RegistrationSetPaths( // save registration key root pRegistration->hkRoot = pRegistration->fPerMachine ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER; - // build uninstall registry key path - hr = StrAllocFormatted(&pRegistration->sczRegistrationKey, L"%ls\\%ls", BURN_REGISTRATION_REGISTRY_UNINSTALL_KEY, pRegistration->sczCode); - ExitOnFailure(hr, "Failed to build uninstall registry key path."); - // build cache directory hr = CacheGetCompletedPath(pCache, pRegistration->fPerMachine, pRegistration->sczCode, &sczCacheDirectory); ExitOnFailure(hr, "Failed to build cache directory."); @@ -1754,25 +1765,28 @@ static HRESULT DetectInstalled( HRESULT hr = S_OK; HKEY hkRegistration = NULL; DWORD dwInstalled = 0; + DWORD dwScope = 0; pRegistration->fCached = pRegistration->sczCacheExecutablePath && FileExistsEx(pRegistration->sczCacheExecutablePath, NULL); pRegistration->detectedRegistrationType = BOOTSTRAPPER_REGISTRATION_TYPE_NONE; + pRegistration->detectedScope = BOOTSTRAPPER_SCOPE_DEFAULT; // open registration key hr = RegOpen(hkRoot, pRegistration->sczRegistrationKey, KEY_QUERY_VALUE, &hkRegistration); if (SUCCEEDED(hr)) { hr = RegReadNumber(hkRegistration, REGISTRY_BUNDLE_INSTALLED, &dwInstalled); + ExitOnFailure(hr, "Failed to read registration %ls@%ls.", pRegistration->sczRegistrationKey, REGISTRY_BUNDLE_INSTALLED); pRegistration->detectedRegistrationType = (1 == dwInstalled) ? BOOTSTRAPPER_REGISTRATION_TYPE_FULL : BOOTSTRAPPER_REGISTRATION_TYPE_INPROGRESS; - } - // Not finding the key or value is okay. - if (E_FILENOTFOUND == hr || E_PATHNOTFOUND == hr) - { - hr = S_OK; + hr = RegReadNumber(hkRegistration, REGISTRY_BUNDLE_SCOPE, &dwScope); + ExitOnFailure(hr, "Failed to read registration %ls@%ls.", pRegistration->sczRegistrationKey, REGISTRY_BUNDLE_SCOPE); + + pRegistration->detectedScope = static_cast(dwScope); } +LExit: ReleaseRegKey(hkRegistration); 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 // For configurable-scope bundles, fPerMachine is only valid after // planning when scope is known. For fixed per-machine or per-user // bundles, valid immediately. - BOOL fPerMachine; + BOOL fPerMachine; BOOL fForceSystemComponent; BOOL fDisableResume; BOOL fCached; BOOTSTRAPPER_REGISTRATION_TYPE detectedRegistrationType; + BOOTSTRAPPER_SCOPE detectedScope; BOOTSTRAPPER_PACKAGE_SCOPE scope; LPWSTR sczCode; 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( {BURN_BUNDLE_TAG, InitializeVariableString, (DWORD_PTR)L"", FALSE, TRUE}, {BURN_BUNDLE_UILEVEL, InitializeVariableNumeric, 0, FALSE, TRUE}, {BURN_BUNDLE_VERSION, InitializeVariableVersion, (DWORD_PTR)L"0", FALSE, TRUE}, - {BURN_BUNDLE_SCOPE, InitializeVariableNumeric, 0, FALSE, TRUE}, + {BURN_BUNDLE_AUTHORED_SCOPE, InitializeVariableNumeric, 0, FALSE, TRUE}, {BURN_BUNDLE_PLANNED_SCOPE, InitializeVariableNumeric, 0, FALSE, TRUE}, + {BURN_BUNDLE_DETECTED_SCOPE, InitializeVariableNumeric, 0, FALSE, TRUE}, }; 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 Assert.True(LogVerifier.MessageInLogFile(log, "Planned package: PmouPkg1.msi, state: Absent, default requested: Present, ba requested: Present, execute: Install, rollback: Uninstall, scope: PerUser")); Assert.True(LogVerifier.MessageInLogFile(log, "Planned package: PmouPkg2.msi, state: Absent, default requested: Present, ba requested: Present, execute: Install, rollback: Uninstall, scope: PerUser")); - bundle.Uninstall(arguments: "/peruser"); + bundle.Uninstall(); bundle.VerifyUnregisteredAndRemovedFromPackageCache(plannedPerMachine: false); pkg1.VerifyInstalled(false); pkg2.VerifyInstalled(false); @@ -89,7 +89,7 @@ namespace WixToolsetTest.BurnE2E Assert.True(LogVerifier.MessageInLogFile(log, "Planned package: PuomPkg1.msi, state: Absent, default requested: Present, ba requested: Present, execute: Install, rollback: Uninstall, scope: PerMachine,")); Assert.True(LogVerifier.MessageInLogFile(log, "Planned package: PuomPkg2.msi, state: Absent, default requested: Present, ba requested: Present, execute: Install, rollback: Uninstall, scope: PerMachine,")); - bundle.Uninstall(arguments: "/permachine"); + bundle.Uninstall(); bundle.VerifyUnregisteredAndRemovedFromPackageCache(plannedPerMachine: true); pkg1.VerifyInstalled(false); pkg2.VerifyInstalled(false); @@ -115,6 +115,11 @@ namespace WixToolsetTest.BurnE2E Assert.True(LogVerifier.MessageInLogFile(log, "Planned package: PmouPkg1.msi, state: Absent, default requested: Present, ba requested: Present, execute: Install, rollback: Uninstall, scope: PerMachine,")); Assert.True(LogVerifier.MessageInLogFile(log, "Planned package: PmouPkg2.msi, state: Absent, default requested: Present, ba requested: Present, execute: Install, rollback: Uninstall, scope: PerMachine,")); + log = bundle.Repair(); + Assert.True(LogVerifier.MessageInLogFile(log, "Bundle was already installed with scope: PerMachine")); + Assert.True(LogVerifier.MessageInLogFile(log, "Detected package: PmouPkg1.msi, state: Present, authored scope: PerMachineOrUser, detected scope: PerMachine,")); + Assert.True(LogVerifier.MessageInLogFile(log, "Detected package: PmouPkg2.msi, state: Present, authored scope: PerMachineOrUser, detected scope: PerMachine,")); + bundle.Uninstall(); bundle.VerifyUnregisteredAndRemovedFromPackageCache(plannedPerMachine: true); pkg1.VerifyInstalled(false); @@ -450,9 +455,15 @@ namespace WixToolsetTest.BurnE2E Assert.True(LogVerifier.MessageInLogFile(log, "Plan begin, 5 packages, action: Install, planned scope: Default")); Assert.True(LogVerifier.MessageInLogFile(log, "Planned package: PerMachinePkg.msi, state: Absent, default requested: Present, ba requested: Present, execute: Install, rollback: Uninstall, scope: PerMachine,")); - Assert.True(LogVerifier.MessageInLogFile(log, "Planned package: PerUserPkg.msi, state: Absent, default requested: Present, ba requested: Present, execute: Install, rollback: Uninstall, scope: PerUser")); Assert.True(LogVerifier.MessageInLogFile(log, "Planned package: PmouPkg1.msi, state: Absent, default requested: Present, ba requested: Present, execute: Install, rollback: Uninstall, scope: PerMachine,")); Assert.True(LogVerifier.MessageInLogFile(log, "Planned package: PmouPkg2.msi, state: Absent, default requested: Present, ba requested: Present, execute: Install, rollback: Uninstall, scope: PerMachine,")); + Assert.True(LogVerifier.MessageInLogFile(log, "Planned package: PerUserPkg.msi, state: Absent, default requested: Present, ba requested: Present, execute: Install, rollback: Uninstall, scope: PerUser")); + + log = bundle.Repair(); + Assert.True(LogVerifier.MessageInLogFile(log, "Detected package: PerMachinePkg.msi, state: Present, authored scope: PerMachine, detected scope: PerMachine,")); + Assert.True(LogVerifier.MessageInLogFile(log, "Detected package: PmouPkg1.msi, state: Present, authored scope: PerMachineOrUser, detected scope: PerMachine,")); + Assert.True(LogVerifier.MessageInLogFile(log, "Detected package: PmouPkg2.msi, state: Present, authored scope: PerMachineOrUser, detected scope: PerMachine,")); + Assert.True(LogVerifier.MessageInLogFile(log, "Detected package: PerUserPkg.msi, state: Present, authored scope: PerUser, detected scope: PerUser,")); bundle.Uninstall(); bundle.VerifyUnregisteredAndRemovedFromPackageCache(plannedPerMachine: true); -- cgit v1.2.3-55-g6feb