From 9453eaa9a38f78e248526ddd996485140a5d4d9a Mon Sep 17 00:00:00 2001 From: Sean Hall Date: Sun, 13 Mar 2022 23:51:36 -0500 Subject: Make engine skip planning if there are any downgrade related bundles. Fixes 6677, 6722 Reverts 6537 --- .../inc/BootstrapperApplication.h | 13 ++++ .../WixToolset.Mba.Core/BootstrapperApplication.cs | 25 ++++++ src/api/burn/WixToolset.Mba.Core/EventArgs.cs | 24 ++++++ .../IBootstrapperApplication.cs | 10 +++ .../IDefaultBootstrapperApplication.cs | 5 ++ src/api/burn/balutil/inc/BAFunctions.h | 1 + src/api/burn/balutil/inc/BalBaseBAFunctions.h | 8 ++ src/api/burn/balutil/inc/BalBaseBAFunctionsProc.h | 1 + .../balutil/inc/BalBaseBootstrapperApplication.h | 8 ++ .../inc/BalBaseBootstrapperApplicationProc.h | 12 +++ .../burn/balutil/inc/IBootstrapperApplication.h | 7 ++ src/burn/engine/core.cpp | 39 +++++++--- src/burn/engine/engine.mc | 14 ++++ src/burn/engine/plan.cpp | 19 ++++- src/burn/engine/plan.h | 1 + src/burn/engine/userexperience.cpp | 24 ++++++ src/burn/engine/userexperience.h | 4 + src/burn/test/BurnUnitTest/PlanTest.cpp | 88 ++++++++++++++++++++++ .../WixStandardBootstrapperApplication.cpp | 56 +++++++------- .../burn/WixToolsetTest.BurnE2E/WixStdBaTests.cs | 55 ++++++++++++++ 20 files changed, 373 insertions(+), 41 deletions(-) create mode 100644 src/test/burn/WixToolsetTest.BurnE2E/WixStdBaTests.cs diff --git a/src/api/burn/WixToolset.BootstrapperCore.Native/inc/BootstrapperApplication.h b/src/api/burn/WixToolset.BootstrapperCore.Native/inc/BootstrapperApplication.h index b507b167..0b81b35a 100644 --- a/src/api/burn/WixToolset.BootstrapperCore.Native/inc/BootstrapperApplication.h +++ b/src/api/burn/WixToolset.BootstrapperCore.Native/inc/BootstrapperApplication.h @@ -223,6 +223,7 @@ enum BOOTSTRAPPER_APPLICATION_MESSAGE BOOTSTRAPPER_APPLICATION_MESSAGE_ONPLANNEDCOMPATIBLEPACKAGE, BOOTSTRAPPER_APPLICATION_MESSAGE_ONPLANRESTORERELATEDBUNDLE, BOOTSTRAPPER_APPLICATION_MESSAGE_ONPLANRELATEDBUNDLETYPE, + BOOTSTRAPPER_APPLICATION_MESSAGE_ONAPPLYDOWNGRADE, }; enum BOOTSTRAPPER_APPLYCOMPLETE_ACTION @@ -354,6 +355,18 @@ struct BA_ONAPPLYCOMPLETE_RESULTS BOOTSTRAPPER_APPLYCOMPLETE_ACTION action; }; +struct BA_ONAPPLYDOWNGRADE_ARGS +{ + DWORD cbSize; + HRESULT hrRecommended; +}; + +struct BA_ONAPPLYDOWNGRADE_RESULTS +{ + DWORD cbSize; + HRESULT hrStatus; +}; + struct BA_ONBEGINMSITRANSACTIONBEGIN_ARGS { DWORD cbSize; diff --git a/src/api/burn/WixToolset.Mba.Core/BootstrapperApplication.cs b/src/api/burn/WixToolset.Mba.Core/BootstrapperApplication.cs index fd36cf26..8a2e0e93 100644 --- a/src/api/burn/WixToolset.Mba.Core/BootstrapperApplication.cs +++ b/src/api/burn/WixToolset.Mba.Core/BootstrapperApplication.cs @@ -208,6 +208,9 @@ namespace WixToolset.Mba.Core /// public event EventHandler ApplyComplete; + /// + public event EventHandler ApplyDowngrade; + /// public event EventHandler ExecuteProgress; @@ -1055,6 +1058,19 @@ namespace WixToolset.Mba.Core } } + /// + /// Called by the engine, raises the event. + /// + /// Additional arguments for this event. + protected virtual void OnApplyDowngrade(ApplyDowngradeEventArgs args) + { + EventHandler handler = this.ApplyDowngrade; + if (null != handler) + { + handler(this, args); + } + } + /// /// Called by the engine, raises the event. /// @@ -1907,6 +1923,15 @@ namespace WixToolset.Mba.Core return args.HResult; } + int IBootstrapperApplication.OnApplyDowngrade(int hrRecommendation, ref int hrStatus) + { + ApplyDowngradeEventArgs args = new ApplyDowngradeEventArgs(hrRecommendation, hrStatus); + this.OnApplyDowngrade(args); + + hrStatus = args.Status; + return args.HResult; + } + int IBootstrapperApplication.OnLaunchApprovedExeBegin(ref bool fCancel) { LaunchApprovedExeBeginEventArgs args = new LaunchApprovedExeBeginEventArgs(fCancel); diff --git a/src/api/burn/WixToolset.Mba.Core/EventArgs.cs b/src/api/burn/WixToolset.Mba.Core/EventArgs.cs index d8ec7998..c93c2885 100644 --- a/src/api/burn/WixToolset.Mba.Core/EventArgs.cs +++ b/src/api/burn/WixToolset.Mba.Core/EventArgs.cs @@ -1858,6 +1858,30 @@ namespace WixToolset.Mba.Core public ApplyRestart Restart { get; private set; } } + /// + /// Event arguments for + /// + [Serializable] + public class ApplyDowngradeEventArgs : HResultEventArgs + { + /// + public ApplyDowngradeEventArgs(int hrRecommendation, int hrStatus) + { + this.Recommendation = hrRecommendation; + this.Status = hrStatus; + } + + /// + /// Gets the recommended HRESULT. + /// + public int Recommendation { get; private set; } + + /// + /// Gets or sets the HRESULT for Apply. + /// + public int Status { get; set; } + } + /// /// EventArgs for . /// diff --git a/src/api/burn/WixToolset.Mba.Core/IBootstrapperApplication.cs b/src/api/burn/WixToolset.Mba.Core/IBootstrapperApplication.cs index 4ab0f8d9..d4fe8320 100644 --- a/src/api/burn/WixToolset.Mba.Core/IBootstrapperApplication.cs +++ b/src/api/burn/WixToolset.Mba.Core/IBootstrapperApplication.cs @@ -1160,6 +1160,16 @@ namespace WixToolset.Mba.Core [MarshalAs(UnmanagedType.U4)] ref RelatedBundlePlanType pRequestedType, [MarshalAs(UnmanagedType.Bool)] ref bool fCancel ); + + /// + /// See . + /// + [PreserveSig] + [return: MarshalAs(UnmanagedType.I4)] + int OnApplyDowngrade( + [MarshalAs(UnmanagedType.I4)] int hrRecommended, + [MarshalAs(UnmanagedType.I4)] ref int hrStatus + ); } /// diff --git a/src/api/burn/WixToolset.Mba.Core/IDefaultBootstrapperApplication.cs b/src/api/burn/WixToolset.Mba.Core/IDefaultBootstrapperApplication.cs index ebd1580b..c9284b69 100644 --- a/src/api/burn/WixToolset.Mba.Core/IDefaultBootstrapperApplication.cs +++ b/src/api/burn/WixToolset.Mba.Core/IDefaultBootstrapperApplication.cs @@ -19,6 +19,11 @@ namespace WixToolset.Mba.Core /// event EventHandler ApplyComplete; + /// + /// Fired when the plan determined that nothing should happen to prevent downgrading. + /// + event EventHandler ApplyDowngrade; + /// /// Fired when the engine is about to begin an MSI transaction. /// diff --git a/src/api/burn/balutil/inc/BAFunctions.h b/src/api/burn/balutil/inc/BAFunctions.h index f772eb3f..58c26166 100644 --- a/src/api/burn/balutil/inc/BAFunctions.h +++ b/src/api/burn/balutil/inc/BAFunctions.h @@ -90,6 +90,7 @@ enum BA_FUNCTIONS_MESSAGE BA_FUNCTIONS_MESSAGE_ONPLANNEDCOMPATIBLEPACKAGE = BOOTSTRAPPER_APPLICATION_MESSAGE_ONPLANNEDCOMPATIBLEPACKAGE, BA_FUNCTIONS_MESSAGE_ONPLANRESTORERELATEDBUNDLE = BOOTSTRAPPER_APPLICATION_MESSAGE_ONPLANRESTORERELATEDBUNDLE, BA_FUNCTIONS_MESSAGE_ONPLANRELATEDBUNDLETYPE = BOOTSTRAPPER_APPLICATION_MESSAGE_ONPLANRELATEDBUNDLETYPE, + BA_FUNCTIONS_MESSAGE_ONAPPLYDOWNGRADE = BOOTSTRAPPER_APPLICATION_MESSAGE_ONAPPLYDOWNGRADE, BA_FUNCTIONS_MESSAGE_ONTHEMELOADED = 1024, BA_FUNCTIONS_MESSAGE_WNDPROC, diff --git a/src/api/burn/balutil/inc/BalBaseBAFunctions.h b/src/api/burn/balutil/inc/BalBaseBAFunctions.h index f558828f..fe5c99ba 100644 --- a/src/api/burn/balutil/inc/BalBaseBAFunctions.h +++ b/src/api/burn/balutil/inc/BalBaseBAFunctions.h @@ -869,6 +869,14 @@ public: // IBootstrapperApplication return S_OK; } + virtual STDMETHODIMP OnApplyDowngrade( + __in HRESULT /*hrRecommended*/, + __in HRESULT* /*phrStatus*/ + ) + { + return S_OK; + } + public: // IBAFunctions virtual STDMETHODIMP OnPlan( ) diff --git a/src/api/burn/balutil/inc/BalBaseBAFunctionsProc.h b/src/api/burn/balutil/inc/BalBaseBAFunctionsProc.h index ede00f28..100e5c30 100644 --- a/src/api/burn/balutil/inc/BalBaseBAFunctionsProc.h +++ b/src/api/burn/balutil/inc/BalBaseBAFunctionsProc.h @@ -161,6 +161,7 @@ static HRESULT WINAPI BalBaseBAFunctionsProc( case BA_FUNCTIONS_MESSAGE_ONPLANNEDCOMPATIBLEPACKAGE: case BA_FUNCTIONS_MESSAGE_ONPLANRESTORERELATEDBUNDLE: case BA_FUNCTIONS_MESSAGE_ONPLANRELATEDBUNDLETYPE: + case BA_FUNCTIONS_MESSAGE_ONAPPLYDOWNGRADE: hr = BalBaseBootstrapperApplicationProc((BOOTSTRAPPER_APPLICATION_MESSAGE)message, pvArgs, pvResults, pvContext); break; case BA_FUNCTIONS_MESSAGE_ONTHEMELOADED: diff --git a/src/api/burn/balutil/inc/BalBaseBootstrapperApplication.h b/src/api/burn/balutil/inc/BalBaseBootstrapperApplication.h index 49f4b7ca..fd06a83f 100644 --- a/src/api/burn/balutil/inc/BalBaseBootstrapperApplication.h +++ b/src/api/burn/balutil/inc/BalBaseBootstrapperApplication.h @@ -1069,6 +1069,14 @@ public: // IBootstrapperApplication return S_OK; } + virtual STDMETHODIMP OnApplyDowngrade( + __in HRESULT /*hrRecommended*/, + __in HRESULT* /*phrStatus*/ + ) + { + return S_OK; + } + public: //CBalBaseBootstrapperApplication virtual STDMETHODIMP Initialize( __in const BOOTSTRAPPER_CREATE_ARGS* pCreateArgs diff --git a/src/api/burn/balutil/inc/BalBaseBootstrapperApplicationProc.h b/src/api/burn/balutil/inc/BalBaseBootstrapperApplicationProc.h index 698349f7..4e413e4e 100644 --- a/src/api/burn/balutil/inc/BalBaseBootstrapperApplicationProc.h +++ b/src/api/burn/balutil/inc/BalBaseBootstrapperApplicationProc.h @@ -738,6 +738,15 @@ static HRESULT BalBaseBAProcOnPlanRelatedBundleType( return pBA->OnPlanRelatedBundleType(pArgs->wzBundleId, pArgs->recommendedType, &pResults->requestedType, &pResults->fCancel); } +static HRESULT BalBaseBAProcOnApplyDowngrade( + __in IBootstrapperApplication* pBA, + __in BA_ONAPPLYDOWNGRADE_ARGS* pArgs, + __inout BA_ONAPPLYDOWNGRADE_RESULTS* pResults + ) +{ + return pBA->OnApplyDowngrade(pArgs->hrRecommended, &pResults->hrStatus); +} + /******************************************************************* BalBaseBootstrapperApplicationProc - requires pvContext to be of type IBootstrapperApplication. Provides a default mapping between the new message based BA interface and @@ -1000,6 +1009,9 @@ static HRESULT WINAPI BalBaseBootstrapperApplicationProc( case BOOTSTRAPPER_APPLICATION_MESSAGE_ONPLANRELATEDBUNDLETYPE: hr = BalBaseBAProcOnPlanRelatedBundleType(pBA, reinterpret_cast(pvArgs), reinterpret_cast(pvResults)); break; + case BOOTSTRAPPER_APPLICATION_MESSAGE_ONAPPLYDOWNGRADE: + hr = BalBaseBAProcOnApplyDowngrade(pBA, reinterpret_cast(pvArgs), reinterpret_cast(pvResults)); + break; } } diff --git a/src/api/burn/balutil/inc/IBootstrapperApplication.h b/src/api/burn/balutil/inc/IBootstrapperApplication.h index 462df0cc..c9cf3126 100644 --- a/src/api/burn/balutil/inc/IBootstrapperApplication.h +++ b/src/api/burn/balutil/inc/IBootstrapperApplication.h @@ -707,4 +707,11 @@ DECLARE_INTERFACE_IID_(IBootstrapperApplication, IUnknown, "53C31D56-49C0-426B-A __inout BOOTSTRAPPER_RELATED_BUNDLE_PLAN_TYPE* pRequestedType, __inout BOOL* pfCancel ) = 0; + + // OnApplyDowngrade - called when the plan determined that nothing should happen to prevent downgrading. + // + STDMETHOD(OnApplyDowngrade)( + __in HRESULT hrRecommended, + __inout HRESULT* phrStatus + ) = 0; }; diff --git a/src/burn/engine/core.cpp b/src/burn/engine/core.cpp index 9d4ea43e..37872e52 100644 --- a/src/burn/engine/core.cpp +++ b/src/burn/engine/core.cpp @@ -527,8 +527,15 @@ extern "C" HRESULT CorePlan( hr = PlanRelatedBundlesInitialize(&pEngineState->userExperience, &pEngineState->registration, pEngineState->command.relationType, &pEngineState->plan); ExitOnFailure(hr, "Failed to initialize related bundles for plan."); - hr = PlanRegistration(&pEngineState->plan, &pEngineState->registration, &pEngineState->dependencies, pEngineState->command.resumeType, pEngineState->command.relationType, &fContinuePlanning); - ExitOnFailure(hr, "Failed to plan registration."); + if (pEngineState->plan.fDowngrade) + { + fContinuePlanning = FALSE; + } + else + { + hr = PlanRegistration(&pEngineState->plan, &pEngineState->registration, &pEngineState->dependencies, pEngineState->command.resumeType, pEngineState->command.relationType, &fContinuePlanning); + ExitOnFailure(hr, "Failed to plan registration."); + } if (fContinuePlanning) { @@ -615,6 +622,7 @@ extern "C" HRESULT CoreApply( { HRESULT hr = S_OK; HANDLE hLock = NULL; + BOOL fApplyBegan = FALSE; BOOL fApplyInitialize = FALSE; BOOL fElevated = FALSE; BOOL fRegistered = FALSE; @@ -627,8 +635,6 @@ extern "C" HRESULT CoreApply( DWORD dwPhaseCount = 0; BOOTSTRAPPER_APPLYCOMPLETE_ACTION applyCompleteAction = BOOTSTRAPPER_APPLYCOMPLETE_ACTION_NONE; - LogId(REPORT_STANDARD, MSG_APPLY_BEGIN); - if (!pEngineState->fPlanned) { ExitOnFailure(hr = E_INVALIDSTATE, "Apply cannot be done without a successful Plan."); @@ -638,6 +644,10 @@ extern "C" HRESULT CoreApply( ExitOnFailure(hr = E_INVALIDSTATE, "Plans cannot be applied multiple times."); } + fApplyBegan = TRUE; + + LogId(REPORT_STANDARD, MSG_APPLY_BEGIN); + // Ensure any previous attempts to execute are reset. ApplyReset(&pEngineState->userExperience, &pEngineState->packages); @@ -653,6 +663,14 @@ extern "C" HRESULT CoreApply( hr = UserExperienceOnApplyBegin(&pEngineState->userExperience, dwPhaseCount); ExitOnRootFailure(hr, "BA aborted apply begin."); + if (pEngineState->plan.fDowngrade) + { + hr = HRESULT_FROM_WIN32(ERROR_PRODUCT_VERSION); + UserExperienceOnApplyDowngrade(&pEngineState->userExperience, &hr); + + ExitFunction(); + } + pEngineState->plan.fAffectedMachineState = pEngineState->plan.fCanAffectMachineState; hr = ApplyLock(FALSE, &hLock); @@ -804,13 +822,16 @@ LExit: DeleteCriticalSection(&applyContext.csApply); } - UserExperienceOnApplyComplete(&pEngineState->userExperience, hr, restart, &applyCompleteAction); - if (BOOTSTRAPPER_APPLYCOMPLETE_ACTION_RESTART == applyCompleteAction) + if (fApplyBegan) { - pEngineState->fRestart = TRUE; - } + UserExperienceOnApplyComplete(&pEngineState->userExperience, hr, restart, &applyCompleteAction); + if (BOOTSTRAPPER_APPLYCOMPLETE_ACTION_RESTART == applyCompleteAction) + { + pEngineState->fRestart = TRUE; + } - LogId(REPORT_STANDARD, MSG_APPLY_COMPLETE, hr, LoggingRestartToString(restart), LoggingBoolToString(pEngineState->fRestart)); + LogId(REPORT_STANDARD, MSG_APPLY_COMPLETE, hr, LoggingRestartToString(restart), LoggingBoolToString(pEngineState->fRestart)); + } return hr; } diff --git a/src/burn/engine/engine.mc b/src/burn/engine/engine.mc index a5c4e2ba..53e6b256 100644 --- a/src/burn/engine/engine.mc +++ b/src/burn/engine/engine.mc @@ -499,6 +499,20 @@ Language=English Ignoring bundle dependents due to action UnsafeUninstall... . +MessageId=224 +Severity=Warning +SymbolicName=MSG_PLAN_SKIPPED_DUE_TO_DOWNGRADE +Language=English +Plan skipped due to related bundle of plan type Downgrade: +. + +MessageId=225 +Severity=Warning +SymbolicName=MSG_UPGRADE_BUNDLE_DOWNGRADE +Language=English + id: %1!ls!, version: %2!ls! +. + MessageId=299 Severity=Success SymbolicName=MSG_PLAN_COMPLETE diff --git a/src/burn/engine/plan.cpp b/src/burn/engine/plan.cpp index dcb919c7..46680636 100644 --- a/src/burn/engine/plan.cpp +++ b/src/burn/engine/plan.cpp @@ -1328,11 +1328,12 @@ LExit: extern "C" HRESULT PlanRelatedBundlesInitialize( __in BURN_USER_EXPERIENCE* pUserExperience, __in BURN_REGISTRATION* pRegistration, - __in BOOTSTRAPPER_RELATION_TYPE /*relationType*/, - __in BURN_PLAN* /*pPlan*/ + __in BOOTSTRAPPER_RELATION_TYPE relationType, + __in BURN_PLAN* pPlan ) { HRESULT hr = S_OK; + BOOL fUninstalling = BOOTSTRAPPER_ACTION_UNINSTALL == pPlan->action || BOOTSTRAPPER_ACTION_UNSAFE_UNINSTALL == pPlan->action; for (DWORD i = 0; i < pRegistration->relatedBundles.cRelatedBundles; ++i) { @@ -1356,6 +1357,19 @@ extern "C" HRESULT PlanRelatedBundlesInitialize( hr = UserExperienceOnPlanRelatedBundleType(pUserExperience, pRelatedBundle->package.sczId, &pRelatedBundle->planRelationType); ExitOnRootFailure(hr, "BA aborted plan related bundle type."); + + if (BOOTSTRAPPER_RELATED_BUNDLE_PLAN_TYPE_DOWNGRADE == pRelatedBundle->planRelationType && + pRelatedBundle->fPlannable && !fUninstalling && BOOTSTRAPPER_RELATION_UPGRADE != relationType) + { + if (!pPlan->fDowngrade) + { + pPlan->fDowngrade = TRUE; + + LogId(REPORT_STANDARD, MSG_PLAN_SKIPPED_DUE_TO_DOWNGRADE); + } + + LogId(REPORT_VERBOSE, MSG_UPGRADE_BUNDLE_DOWNGRADE, pRelatedBundle->package.sczId, pRelatedBundle->pVersion->sczVersion); + } } RelatedBundlesSortPlan(&pRegistration->relatedBundles); @@ -3011,6 +3025,7 @@ extern "C" void PlanDump( LogStringLine(PlanDumpLevel, " can affect machine state: %hs", LoggingTrueFalseToString(pPlan->fCanAffectMachineState)); LogStringLine(PlanDumpLevel, " disable-rollback: %hs", LoggingTrueFalseToString(pPlan->fDisableRollback)); LogStringLine(PlanDumpLevel, " disallow-removal: %hs", LoggingTrueFalseToString(pPlan->fDisallowRemoval)); + LogStringLine(PlanDumpLevel, " downgrade: %hs", LoggingTrueFalseToString(pPlan->fDowngrade)); LogStringLine(PlanDumpLevel, " registration options: %hs", LoggingRegistrationOptionsToString(pPlan->dwRegistrationOperations)); LogStringLine(PlanDumpLevel, " estimated size: %llu", pPlan->qwEstimatedSize); if (pPlan->sczLayoutDirectory) diff --git a/src/burn/engine/plan.h b/src/burn/engine/plan.h index 1f3fe07c..e73cbcfa 100644 --- a/src/burn/engine/plan.h +++ b/src/burn/engine/plan.h @@ -250,6 +250,7 @@ typedef struct _BURN_PLAN BOOL fAffectedMachineState; LPWSTR sczLayoutDirectory; BOOL fPlanPackageCacheRollback; + BOOL fDowngrade; DWORD64 qwCacheSizeTotal; diff --git a/src/burn/engine/userexperience.cpp b/src/burn/engine/userexperience.cpp index 8668cf6f..81ce8bb9 100644 --- a/src/burn/engine/userexperience.cpp +++ b/src/burn/engine/userexperience.cpp @@ -335,6 +335,30 @@ LExit: return hr; } +EXTERN_C BAAPI UserExperienceOnApplyDowngrade( + __in BURN_USER_EXPERIENCE* pUserExperience, + __inout HRESULT* phrStatus + ) +{ + HRESULT hr = S_OK; + BA_ONAPPLYDOWNGRADE_ARGS args = { }; + BA_ONAPPLYDOWNGRADE_RESULTS results = { }; + + args.cbSize = sizeof(args); + args.hrRecommended = *phrStatus; + + results.cbSize = sizeof(results); + results.hrStatus = *phrStatus; + + hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONAPPLYDOWNGRADE, &args, &results); + ExitOnFailure(hr, "BA OnApplyDowngrade failed."); + + *phrStatus = results.hrStatus; + +LExit: + return hr; +} + EXTERN_C BAAPI UserExperienceOnBeginMsiTransactionBegin( __in BURN_USER_EXPERIENCE* pUserExperience, __in LPCWSTR wzTransactionId diff --git a/src/burn/engine/userexperience.h b/src/burn/engine/userexperience.h index 11344365..2f18acdd 100644 --- a/src/burn/engine/userexperience.h +++ b/src/burn/engine/userexperience.h @@ -110,6 +110,10 @@ BAAPI UserExperienceOnApplyComplete( __in BOOTSTRAPPER_APPLY_RESTART restart, __inout BOOTSTRAPPER_APPLYCOMPLETE_ACTION* pAction ); +BAAPI UserExperienceOnApplyDowngrade( + __in BURN_USER_EXPERIENCE* pUserExperience, + __inout HRESULT* phrStatus + ); BAAPI UserExperienceOnBeginMsiTransactionBegin( __in BURN_USER_EXPERIENCE* pUserExperience, __in LPCWSTR wzTransactionId diff --git a/src/burn/test/BurnUnitTest/PlanTest.cpp b/src/burn/test/BurnUnitTest/PlanTest.cpp index a3b58426..0a8ac369 100644 --- a/src/burn/test/BurnUnitTest/PlanTest.cpp +++ b/src/burn/test/BurnUnitTest/PlanTest.cpp @@ -67,6 +67,7 @@ namespace Bootstrapper Assert::Equal(TRUE, pPlan->fCanAffectMachineState); Assert::Equal(FALSE, pPlan->fDisableRollback); Assert::Equal(FALSE, pPlan->fDisallowRemoval); + Assert::Equal(FALSE, pPlan->fDowngrade); BOOL fRollback = FALSE; DWORD dwIndex = 0; @@ -226,6 +227,7 @@ namespace Bootstrapper Assert::Equal(TRUE, pPlan->fCanAffectMachineState); Assert::Equal(FALSE, pPlan->fDisableRollback); Assert::Equal(FALSE, pPlan->fDisallowRemoval); + Assert::Equal(FALSE, pPlan->fDowngrade); BOOL fRollback = FALSE; DWORD dwIndex = 0; @@ -358,6 +360,7 @@ namespace Bootstrapper Assert::Equal(TRUE, pPlan->fCanAffectMachineState); Assert::Equal(FALSE, pPlan->fDisableRollback); Assert::Equal(FALSE, pPlan->fDisallowRemoval); + Assert::Equal(FALSE, pPlan->fDowngrade); BOOL fRollback = FALSE; DWORD dwIndex = 0; @@ -457,6 +460,7 @@ namespace Bootstrapper Assert::Equal(TRUE, pPlan->fCanAffectMachineState); Assert::Equal(FALSE, pPlan->fDisableRollback); Assert::Equal(FALSE, pPlan->fDisallowRemoval); + Assert::Equal(FALSE, pPlan->fDowngrade); BOOL fRollback = FALSE; DWORD dwIndex = 0; @@ -575,6 +579,7 @@ namespace Bootstrapper Assert::Equal(TRUE, pPlan->fCanAffectMachineState); Assert::Equal(FALSE, pPlan->fDisableRollback); Assert::Equal(FALSE, pPlan->fDisallowRemoval); + Assert::Equal(FALSE, pPlan->fDowngrade); BOOL fRollback = FALSE; DWORD dwIndex = 0; @@ -677,6 +682,7 @@ namespace Bootstrapper Assert::Equal(TRUE, pPlan->fCanAffectMachineState); Assert::Equal(FALSE, pPlan->fDisableRollback); Assert::Equal(FALSE, pPlan->fDisallowRemoval); + Assert::Equal(FALSE, pPlan->fDowngrade); BOOL fRollback = FALSE; DWORD dwIndex = 0; @@ -741,6 +747,76 @@ namespace Bootstrapper ValidateNonPermanentPackageExpectedStates(&pEngineState->packages.rgPackages[0], L"PackageA", BURN_PACKAGE_REGISTRATION_STATE_PRESENT, BURN_PACKAGE_REGISTRATION_STATE_ABSENT); } + [Fact] + void SingleMsiDowngradeTest() + { + HRESULT hr = S_OK; + BURN_ENGINE_STATE engineState = { }; + BURN_ENGINE_STATE* pEngineState = &engineState; + BURN_PLAN* pPlan = &engineState.plan; + + InitializeEngineStateForCorePlan(wzSingleMsiManifestFileName, pEngineState); + DetectAttachedContainerAsAttached(pEngineState); + DetectPackagesAsAbsent(pEngineState); + DetectRelatedBundle(pEngineState, L"{AF8355C9-CCDD-4D61-BF5F-EA5F948D8F01}", L"1.1.0.0", BOOTSTRAPPER_RELATION_UPGRADE); + + hr = CorePlan(pEngineState, BOOTSTRAPPER_ACTION_INSTALL); + NativeAssert::Succeeded(hr, "CorePlan failed"); + + Assert::Equal(BOOTSTRAPPER_ACTION_INSTALL, pPlan->action); + NativeAssert::StringEqual(L"{A6F0CBF7-1578-450C-B9D7-9CF2EEC40002}", pPlan->wzBundleId); + NativeAssert::StringEqual(L"{A6F0CBF7-1578-450C-B9D7-9CF2EEC40002}", pPlan->wzBundleProviderKey); + Assert::Equal(FALSE, pPlan->fEnabledForwardCompatibleBundle); + Assert::Equal(TRUE, pPlan->fPerMachine); + Assert::Equal(FALSE, pPlan->fCanAffectMachineState); + Assert::Equal(FALSE, pPlan->fDisableRollback); + Assert::Equal(FALSE, pPlan->fDisallowRemoval); + Assert::Equal(TRUE, pPlan->fDowngrade); + + BOOL fRollback = FALSE; + DWORD dwIndex = 0; + Assert::Equal(dwIndex, pPlan->cRegistrationActions); + + fRollback = TRUE; + dwIndex = 0; + Assert::Equal(dwIndex, pPlan->cRollbackRegistrationActions); + + fRollback = FALSE; + dwIndex = 0; + Assert::Equal(dwIndex, pPlan->cCacheActions); + + fRollback = TRUE; + dwIndex = 0; + Assert::Equal(dwIndex, pPlan->cRollbackCacheActions); + + Assert::Equal(0ull, pPlan->qwEstimatedSize); + Assert::Equal(0ull, pPlan->qwCacheSizeTotal); + + fRollback = FALSE; + dwIndex = 0; + Assert::Equal(dwIndex, pPlan->cExecuteActions); + + fRollback = TRUE; + dwIndex = 0; + Assert::Equal(dwIndex, pPlan->cRollbackActions); + + Assert::Equal(0ul, pPlan->cExecutePackagesTotal); + Assert::Equal(0ul, pPlan->cOverallProgressTicksTotal); + + dwIndex = 0; + Assert::Equal(dwIndex, pPlan->cRestoreRelatedBundleActions); + + dwIndex = 0; + Assert::Equal(dwIndex, pPlan->cCleanActions); + + UINT uIndex = 0; + ValidatePlannedProvider(pPlan, uIndex++, L"{A6F0CBF7-1578-450C-B9D7-9CF2EEC40002}", NULL); + Assert::Equal(uIndex, pPlan->cPlannedProviders); + + Assert::Equal(1ul, pEngineState->packages.cPackages); + ValidateNonPermanentPackageExpectedStates(&pEngineState->packages.rgPackages[0], L"PackageA", BURN_PACKAGE_REGISTRATION_STATE_UNKNOWN, BURN_PACKAGE_REGISTRATION_STATE_UNKNOWN); + } + [Fact] void SingleMsiForceAbsentTest() { @@ -770,6 +846,7 @@ namespace Bootstrapper Assert::Equal(TRUE, pPlan->fCanAffectMachineState); Assert::Equal(FALSE, pPlan->fDisableRollback); Assert::Equal(FALSE, pPlan->fDisallowRemoval); + Assert::Equal(FALSE, pPlan->fDowngrade); BOOL fRollback = FALSE; DWORD dwIndex = 0; @@ -858,6 +935,7 @@ namespace Bootstrapper Assert::Equal(TRUE, pPlan->fCanAffectMachineState); Assert::Equal(FALSE, pPlan->fDisableRollback); Assert::Equal(FALSE, pPlan->fDisallowRemoval); + Assert::Equal(FALSE, pPlan->fDowngrade); BOOL fRollback = FALSE; DWORD dwIndex = 0; @@ -948,6 +1026,7 @@ namespace Bootstrapper Assert::Equal(TRUE, pPlan->fCanAffectMachineState); Assert::Equal(FALSE, pPlan->fDisableRollback); Assert::Equal(FALSE, pPlan->fDisallowRemoval); + Assert::Equal(FALSE, pPlan->fDowngrade); BOOL fRollback = FALSE; DWORD dwIndex = 0; @@ -1053,6 +1132,7 @@ namespace Bootstrapper Assert::Equal(TRUE, pPlan->fCanAffectMachineState); Assert::Equal(FALSE, pPlan->fDisableRollback); Assert::Equal(FALSE, pPlan->fDisallowRemoval); + Assert::Equal(FALSE, pPlan->fDowngrade); BOOL fRollback = FALSE; DWORD dwIndex = 0; @@ -1131,6 +1211,7 @@ namespace Bootstrapper Assert::Equal(TRUE, pPlan->fCanAffectMachineState); Assert::Equal(FALSE, pPlan->fDisableRollback); Assert::Equal(FALSE, pPlan->fDisallowRemoval); + Assert::Equal(FALSE, pPlan->fDowngrade); BOOL fRollback = FALSE; DWORD dwIndex = 0; @@ -1225,6 +1306,7 @@ namespace Bootstrapper Assert::Equal(TRUE, pPlan->fCanAffectMachineState); Assert::Equal(FALSE, pPlan->fDisableRollback); Assert::Equal(TRUE, pPlan->fDisallowRemoval); + Assert::Equal(FALSE, pPlan->fDowngrade); BOOL fRollback = FALSE; DWORD dwIndex = 0; @@ -1294,6 +1376,7 @@ namespace Bootstrapper Assert::Equal(TRUE, pPlan->fCanAffectMachineState); Assert::Equal(FALSE, pPlan->fDisableRollback); Assert::Equal(FALSE, pPlan->fDisallowRemoval); + Assert::Equal(FALSE, pPlan->fDowngrade); BOOL fRollback = FALSE; DWORD dwIndex = 0; @@ -1377,6 +1460,7 @@ namespace Bootstrapper Assert::Equal(TRUE, pPlan->fCanAffectMachineState); Assert::Equal(TRUE, pPlan->fDisableRollback); Assert::Equal(FALSE, pPlan->fDisallowRemoval); + Assert::Equal(FALSE, pPlan->fDowngrade); BOOL fRollback = FALSE; DWORD dwIndex = 0; @@ -1471,6 +1555,7 @@ namespace Bootstrapper Assert::Equal(TRUE, pPlan->fCanAffectMachineState); Assert::Equal(FALSE, pPlan->fDisableRollback); Assert::Equal(FALSE, pPlan->fDisallowRemoval); + Assert::Equal(FALSE, pPlan->fDowngrade); BOOL fRollback = FALSE; DWORD dwIndex = 0; @@ -1601,6 +1686,7 @@ namespace Bootstrapper Assert::Equal(TRUE, pPlan->fCanAffectMachineState); Assert::Equal(FALSE, pPlan->fDisableRollback); Assert::Equal(FALSE, pPlan->fDisallowRemoval); + Assert::Equal(FALSE, pPlan->fDowngrade); BOOL fRollback = FALSE; DWORD dwIndex = 0; @@ -1716,6 +1802,7 @@ namespace Bootstrapper Assert::Equal(TRUE, pPlan->fCanAffectMachineState); Assert::Equal(FALSE, pPlan->fDisableRollback); Assert::Equal(FALSE, pPlan->fDisallowRemoval); + Assert::Equal(FALSE, pPlan->fDowngrade); BOOL fRollback = FALSE; DWORD dwIndex = 0; @@ -1810,6 +1897,7 @@ namespace Bootstrapper Assert::Equal(TRUE, pPlan->fCanAffectMachineState); Assert::Equal(FALSE, pPlan->fDisableRollback); Assert::Equal(FALSE, pPlan->fDisallowRemoval); + Assert::Equal(FALSE, pPlan->fDowngrade); BOOL fRollback = FALSE; DWORD dwIndex = 0; diff --git a/src/ext/Bal/wixstdba/WixStandardBootstrapperApplication.cpp b/src/ext/Bal/wixstdba/WixStandardBootstrapperApplication.cpp index 91033a26..2a5d839a 100644 --- a/src/ext/Bal/wixstdba/WixStandardBootstrapperApplication.cpp +++ b/src/ext/Bal/wixstdba/WixStandardBootstrapperApplication.cpp @@ -254,7 +254,6 @@ public: // IBootstrapperApplication ) { BAL_INFO_PACKAGE* pPackage = NULL; - int nCompare = 0; if (!fMissingFromCache) { @@ -262,14 +261,6 @@ public: // IBootstrapperApplication { InitializePackageInfoForPackage(pPackage); } - - // If we're not doing a prerequisite install, remember when our bundle would cause a downgrade. - if (!m_fPrereq && BOOTSTRAPPER_RELATION_UPGRADE == relationType && - SUCCEEDED(m_pEngine->CompareVersions(m_sczBundleVersion, wzVersion, &nCompare)) && 0 > nCompare) - { - BalLog(BOOTSTRAPPER_LOG_LEVEL_STANDARD, "A newer version (v%ls) of this product is installed.", wzVersion); - m_fDowngrading = TRUE; - } } return CBalBaseBootstrapperApplication::OnDetectRelatedBundle(wzBundleId, relationType, wzBundleTag, fPerMachine, wzVersion, fMissingFromCache, pfCancel); @@ -345,23 +336,6 @@ public: // IBootstrapperApplication } } } - else if (m_fDowngrading && BOOTSTRAPPER_ACTION_UNINSTALL < m_command.action) - { - if (m_fSuppressDowngradeFailure) - { - BalLog(BOOTSTRAPPER_LOG_LEVEL_STANDARD, "Downgrade failure has been suppressed; exiting bundle."); - - hr = S_OK; - SetState(WIXSTDBA_STATE_APPLIED, hr); - ExitFunction(); - } - else - { - // If we are going to apply a downgrade, bail. - hr = HRESULT_FROM_WIN32(ERROR_PRODUCT_VERSION); - BalExitOnFailure(hr, "Cannot install a product when a newer version is installed."); - } - } } SetState(WIXSTDBA_STATE_DETECTED, hrStatus); @@ -371,7 +345,6 @@ public: // IBootstrapperApplication ::PostMessageW(m_hWnd, WM_WIXSTDBA_PLAN_PACKAGES, 0, m_command.action); } - LExit: return hr; } @@ -1092,6 +1065,20 @@ public: // IBootstrapperApplication return hr; } + virtual STDMETHODIMP OnApplyDowngrade( + __in HRESULT /*hrRecommendation*/, + __in HRESULT* phrStatus + ) + { + HRESULT hr = S_OK; + + if (m_fSuppressDowngradeFailure) + { + *phrStatus = S_OK; + } + + return hr; + } virtual STDMETHODIMP OnApplyComplete( __in HRESULT hrStatus, @@ -1445,6 +1432,9 @@ public: // IBootstrapperApplication case BOOTSTRAPPER_APPLICATION_MESSAGE_ONPLANRELATEDBUNDLETYPE: OnPlanRelatedBundleTypeFallback(reinterpret_cast(pvArgs), reinterpret_cast(pvResults)); break; + case BOOTSTRAPPER_APPLICATION_MESSAGE_ONAPPLYDOWNGRADE: + OnApplyDowngradeFallback(reinterpret_cast(pvArgs), reinterpret_cast(pvResults)); + break; default: #ifdef DEBUG BalLog(BOOTSTRAPPER_LOG_LEVEL_STANDARD, "WIXSTDBA: Forwarding unknown BA message: %d", message); @@ -2118,6 +2108,14 @@ private: // privates BalLogId(BOOTSTRAPPER_LOG_LEVEL_STANDARD, MSG_WIXSTDBA_PLANNED_RESTORE_RELATED_BUNDLE, m_hModule, pArgs->wzBundleId, LoggingRequestStateToString(requestedState), LoggingRequestStateToString(pResults->requestedState)); } + void OnApplyDowngradeFallback( + __in BA_ONAPPLYDOWNGRADE_ARGS* pArgs, + __inout BA_ONAPPLYDOWNGRADE_RESULTS* pResults + ) + { + m_pfnBAFunctionsProc(BA_FUNCTIONS_MESSAGE_ONAPPLYDOWNGRADE, pArgs, pResults, m_pvBAFunctionsProcContext); + } + public: //CBalBaseBootstrapperApplication virtual STDMETHODIMP Initialize( @@ -4195,7 +4193,7 @@ public: __in BOOL fPrereq, __in HRESULT hrHostInitialization, __in IBootstrapperEngine* pEngine - ) : CBalBaseBootstrapperApplication(pEngine, 3, 3000) + ) : CBalBaseBootstrapperApplication(pEngine, 3, 3000) { THEME_ASSIGN_CONTROL_ID* pAssignControl = NULL; @@ -4224,7 +4222,6 @@ public: m_state = WIXSTDBA_STATE_INITIALIZING; m_hrFinal = hrHostInitialization; - m_fDowngrading = FALSE; m_restartResult = BOOTSTRAPPER_APPLY_RESTART_NONE; m_fRestartRequired = FALSE; m_fShouldRestart = FALSE; @@ -4529,7 +4526,6 @@ private: DWORD m_dwCalculatedCacheProgress; DWORD m_dwCalculatedExecuteProgress; - BOOL m_fDowngrading; BOOTSTRAPPER_APPLY_RESTART m_restartResult; BOOL m_fRestartRequired; BOOL m_fShouldRestart; diff --git a/src/test/burn/WixToolsetTest.BurnE2E/WixStdBaTests.cs b/src/test/burn/WixToolsetTest.BurnE2E/WixStdBaTests.cs new file mode 100644 index 00000000..e3418cc1 --- /dev/null +++ b/src/test/burn/WixToolsetTest.BurnE2E/WixStdBaTests.cs @@ -0,0 +1,55 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +namespace WixToolsetTest.BurnE2E +{ + using WixTestTools; + using Xunit; + using Xunit.Abstractions; + + public class WixStdBaTests : BurnE2ETests + { + public WixStdBaTests(ITestOutputHelper testOutputHelper) : base(testOutputHelper) { } + + [Fact] + public void ExitsWithErrorWhenDowngradingWithoutSuppression() + { + var packageA = this.CreatePackageInstaller("PackageA"); + var bundle1v10 = this.CreateBundleInstaller("WixStdBaTest1_v10"); + var bundle1v11 = this.CreateBundleInstaller("WixStdBaTest1_v11"); + + packageA.VerifyInstalled(false); + + bundle1v11.Install(); + bundle1v11.VerifyRegisteredAndInPackageCache(); + + packageA.VerifyInstalled(true); + + bundle1v10.Install((int)MSIExec.MSIExecReturnCode.ERROR_PRODUCT_VERSION); + bundle1v10.VerifyUnregisteredAndRemovedFromPackageCache(); + bundle1v11.VerifyRegisteredAndInPackageCache(); + + packageA.VerifyInstalled(true); + } + + [Fact] + public void ExitsWithoutErrorWhenDowngradingWithSuppression() + { + var packageA = this.CreatePackageInstaller("PackageA"); + var bundle1v11 = this.CreateBundleInstaller("WixStdBaTest1_v11"); + var bundle1v12 = this.CreateBundleInstaller("WixStdBaTest1_v12"); + + packageA.VerifyInstalled(false); + + bundle1v12.Install(); + bundle1v12.VerifyRegisteredAndInPackageCache(); + + packageA.VerifyInstalled(true); + + bundle1v11.Install(); + bundle1v11.VerifyUnregisteredAndRemovedFromPackageCache(); + bundle1v12.VerifyRegisteredAndInPackageCache(); + + packageA.VerifyInstalled(true); + } + } +} -- cgit v1.2.3-55-g6feb