@joebigelow / wix / commits / 9453eaa9

Make engine skip planning if there are any downgrade related bundles.

Fixes 6677, 6722 Reverts 6537

Sean Hall committed Mar 13, 2022 at 23:51 UTC 9453eaa9a38f78e248526ddd996485140a5d4d9a
20 files changed +373 -41
src/api/burn/WixToolset.BootstrapperCore.Native/inc/BootstrapperApplication.h
+13
@@ -223,6 +223,7 @@ enum BOOTSTRAPPER_APPLICATION_MESSAGE
223 BOOTSTRAPPER_APPLICATION_MESSAGE_ONPLANNEDCOMPATIBLEPACKAGE,
224 BOOTSTRAPPER_APPLICATION_MESSAGE_ONPLANRESTORERELATEDBUNDLE,
225 BOOTSTRAPPER_APPLICATION_MESSAGE_ONPLANRELATEDBUNDLETYPE,
226 + BOOTSTRAPPER_APPLICATION_MESSAGE_ONAPPLYDOWNGRADE,
227 };
228
229 enum BOOTSTRAPPER_APPLYCOMPLETE_ACTION
@@ -354,6 +355,18 @@ struct BA_ONAPPLYCOMPLETE_RESULTS
355 BOOTSTRAPPER_APPLYCOMPLETE_ACTION action;
356 };
357
358 +struct BA_ONAPPLYDOWNGRADE_ARGS
359 +{
360 + DWORD cbSize;
361 + HRESULT hrRecommended;
362 +};
363 +
364 +struct BA_ONAPPLYDOWNGRADE_RESULTS
365 +{
366 + DWORD cbSize;
367 + HRESULT hrStatus;
368 +};
369 +
370 struct BA_ONBEGINMSITRANSACTIONBEGIN_ARGS
371 {
372 DWORD cbSize;
src/api/burn/WixToolset.Mba.Core/BootstrapperApplication.cs
+25
@@ -208,6 +208,9 @@ namespace WixToolset.Mba.Core
208 /// <inheritdoc/>
209 public event EventHandler<ApplyCompleteEventArgs> ApplyComplete;
210
211 + /// <inheritdoc/>
212 + public event EventHandler<ApplyDowngradeEventArgs> ApplyDowngrade;
213 +
214 /// <inheritdoc/>
215 public event EventHandler<ExecuteProgressEventArgs> ExecuteProgress;
216
@@ -1055,6 +1058,19 @@ namespace WixToolset.Mba.Core
1058 }
1059 }
1060
1061 + /// <summary>
1062 + /// Called by the engine, raises the <see cref="ApplyDowngrade"/> event.
1063 + /// </summary>
1064 + /// <param name="args">Additional arguments for this event.</param>
1065 + protected virtual void OnApplyDowngrade(ApplyDowngradeEventArgs args)
1066 + {
1067 + EventHandler<ApplyDowngradeEventArgs> handler = this.ApplyDowngrade;
1068 + if (null != handler)
1069 + {
1070 + handler(this, args);
1071 + }
1072 + }
1073 +
1074 /// <summary>
1075 /// Called by the engine, raises the <see cref="ExecuteProgress"/> event.
1076 /// </summary>
@@ -1907,6 +1923,15 @@ namespace WixToolset.Mba.Core
1923 return args.HResult;
1924 }
1925
1926 + int IBootstrapperApplication.OnApplyDowngrade(int hrRecommendation, ref int hrStatus)
1927 + {
1928 + ApplyDowngradeEventArgs args = new ApplyDowngradeEventArgs(hrRecommendation, hrStatus);
1929 + this.OnApplyDowngrade(args);
1930 +
1931 + hrStatus = args.Status;
1932 + return args.HResult;
1933 + }
1934 +
1935 int IBootstrapperApplication.OnLaunchApprovedExeBegin(ref bool fCancel)
1936 {
1937 LaunchApprovedExeBeginEventArgs args = new LaunchApprovedExeBeginEventArgs(fCancel);
src/api/burn/WixToolset.Mba.Core/EventArgs.cs
+24
@@ -1858,6 +1858,30 @@ namespace WixToolset.Mba.Core
1858 public ApplyRestart Restart { get; private set; }
1859 }
1860
1861 + /// <summary>
1862 + /// Event arguments for <see cref="IDefaultBootstrapperApplication.ApplyDowngrade"/>
1863 + /// </summary>
1864 + [Serializable]
1865 + public class ApplyDowngradeEventArgs : HResultEventArgs
1866 + {
1867 + /// <summary />
1868 + public ApplyDowngradeEventArgs(int hrRecommendation, int hrStatus)
1869 + {
1870 + this.Recommendation = hrRecommendation;
1871 + this.Status = hrStatus;
1872 + }
1873 +
1874 + /// <summary>
1875 + /// Gets the recommended HRESULT.
1876 + /// </summary>
1877 + public int Recommendation { get; private set; }
1878 +
1879 + /// <summary>
1880 + /// Gets or sets the HRESULT for Apply.
1881 + /// </summary>
1882 + public int Status { get; set; }
1883 + }
1884 +
1885 /// <summary>
1886 /// EventArgs for <see cref="IDefaultBootstrapperApplication.CacheAcquireResolving"/>.
1887 /// </summary>
src/api/burn/WixToolset.Mba.Core/IBootstrapperApplication.cs
+10
@@ -1160,6 +1160,16 @@ namespace WixToolset.Mba.Core
1160 [MarshalAs(UnmanagedType.U4)] ref RelatedBundlePlanType pRequestedType,
1161 [MarshalAs(UnmanagedType.Bool)] ref bool fCancel
1162 );
1163 +
1164 + /// <summary>
1165 + /// See <see cref="IDefaultBootstrapperApplication.ApplyDowngrade"/>.
1166 + /// </summary>
1167 + [PreserveSig]
1168 + [return: MarshalAs(UnmanagedType.I4)]
1169 + int OnApplyDowngrade(
1170 + [MarshalAs(UnmanagedType.I4)] int hrRecommended,
1171 + [MarshalAs(UnmanagedType.I4)] ref int hrStatus
1172 + );
1173 }
1174
1175 /// <summary>
src/api/burn/WixToolset.Mba.Core/IDefaultBootstrapperApplication.cs
+5
@@ -19,6 +19,11 @@ namespace WixToolset.Mba.Core
19 /// </summary>
20 event EventHandler<ApplyCompleteEventArgs> ApplyComplete;
21
22 + /// <summary>
23 + /// Fired when the plan determined that nothing should happen to prevent downgrading.
24 + /// </summary>
25 + event EventHandler<ApplyDowngradeEventArgs> ApplyDowngrade;
26 +
27 /// <summary>
28 /// Fired when the engine is about to begin an MSI transaction.
29 /// </summary>
src/api/burn/balutil/inc/BAFunctions.h
+1
@@ -90,6 +90,7 @@ enum BA_FUNCTIONS_MESSAGE
90 BA_FUNCTIONS_MESSAGE_ONPLANNEDCOMPATIBLEPACKAGE = BOOTSTRAPPER_APPLICATION_MESSAGE_ONPLANNEDCOMPATIBLEPACKAGE,
91 BA_FUNCTIONS_MESSAGE_ONPLANRESTORERELATEDBUNDLE = BOOTSTRAPPER_APPLICATION_MESSAGE_ONPLANRESTORERELATEDBUNDLE,
92 BA_FUNCTIONS_MESSAGE_ONPLANRELATEDBUNDLETYPE = BOOTSTRAPPER_APPLICATION_MESSAGE_ONPLANRELATEDBUNDLETYPE,
93 + BA_FUNCTIONS_MESSAGE_ONAPPLYDOWNGRADE = BOOTSTRAPPER_APPLICATION_MESSAGE_ONAPPLYDOWNGRADE,
94
95 BA_FUNCTIONS_MESSAGE_ONTHEMELOADED = 1024,
96 BA_FUNCTIONS_MESSAGE_WNDPROC,
src/api/burn/balutil/inc/BalBaseBAFunctions.h
+8
@@ -869,6 +869,14 @@ public: // IBootstrapperApplication
869 return S_OK;
870 }
871
872 + virtual STDMETHODIMP OnApplyDowngrade(
873 + __in HRESULT /*hrRecommended*/,
874 + __in HRESULT* /*phrStatus*/
875 + )
876 + {
877 + return S_OK;
878 + }
879 +
880 public: // IBAFunctions
881 virtual STDMETHODIMP OnPlan(
882 )
src/api/burn/balutil/inc/BalBaseBAFunctionsProc.h
+1
@@ -161,6 +161,7 @@ static HRESULT WINAPI BalBaseBAFunctionsProc(
161 case BA_FUNCTIONS_MESSAGE_ONPLANNEDCOMPATIBLEPACKAGE:
162 case BA_FUNCTIONS_MESSAGE_ONPLANRESTORERELATEDBUNDLE:
163 case BA_FUNCTIONS_MESSAGE_ONPLANRELATEDBUNDLETYPE:
164 + case BA_FUNCTIONS_MESSAGE_ONAPPLYDOWNGRADE:
165 hr = BalBaseBootstrapperApplicationProc((BOOTSTRAPPER_APPLICATION_MESSAGE)message, pvArgs, pvResults, pvContext);
166 break;
167 case BA_FUNCTIONS_MESSAGE_ONTHEMELOADED:
src/api/burn/balutil/inc/BalBaseBootstrapperApplication.h
+8
@@ -1069,6 +1069,14 @@ public: // IBootstrapperApplication
1069 return S_OK;
1070 }
1071
1072 + virtual STDMETHODIMP OnApplyDowngrade(
1073 + __in HRESULT /*hrRecommended*/,
1074 + __in HRESULT* /*phrStatus*/
1075 + )
1076 + {
1077 + return S_OK;
1078 + }
1079 +
1080 public: //CBalBaseBootstrapperApplication
1081 virtual STDMETHODIMP Initialize(
1082 __in const BOOTSTRAPPER_CREATE_ARGS* pCreateArgs
src/api/burn/balutil/inc/BalBaseBootstrapperApplicationProc.h
+12
@@ -738,6 +738,15 @@ static HRESULT BalBaseBAProcOnPlanRelatedBundleType(
738 return pBA->OnPlanRelatedBundleType(pArgs->wzBundleId, pArgs->recommendedType, &pResults->requestedType, &pResults->fCancel);
739 }
740
741 +static HRESULT BalBaseBAProcOnApplyDowngrade(
742 + __in IBootstrapperApplication* pBA,
743 + __in BA_ONAPPLYDOWNGRADE_ARGS* pArgs,
744 + __inout BA_ONAPPLYDOWNGRADE_RESULTS* pResults
745 + )
746 +{
747 + return pBA->OnApplyDowngrade(pArgs->hrRecommended, &pResults->hrStatus);
748 +}
749 +
750 /*******************************************************************
751 BalBaseBootstrapperApplicationProc - requires pvContext to be of type IBootstrapperApplication.
752 Provides a default mapping between the new message based BA interface and
@@ -1000,6 +1009,9 @@ static HRESULT WINAPI BalBaseBootstrapperApplicationProc(
1009 case BOOTSTRAPPER_APPLICATION_MESSAGE_ONPLANRELATEDBUNDLETYPE:
1010 hr = BalBaseBAProcOnPlanRelatedBundleType(pBA, reinterpret_cast<BA_ONPLANRELATEDBUNDLETYPE_ARGS*>(pvArgs), reinterpret_cast<BA_ONPLANRELATEDBUNDLETYPE_RESULTS*>(pvResults));
1011 break;
1012 + case BOOTSTRAPPER_APPLICATION_MESSAGE_ONAPPLYDOWNGRADE:
1013 + hr = BalBaseBAProcOnApplyDowngrade(pBA, reinterpret_cast<BA_ONAPPLYDOWNGRADE_ARGS*>(pvArgs), reinterpret_cast<BA_ONAPPLYDOWNGRADE_RESULTS*>(pvResults));
1014 + break;
1015 }
1016 }
1017
src/api/burn/balutil/inc/IBootstrapperApplication.h
+7
@@ -707,4 +707,11 @@ DECLARE_INTERFACE_IID_(IBootstrapperApplication, IUnknown, "53C31D56-49C0-426B-A
707 __inout BOOTSTRAPPER_RELATED_BUNDLE_PLAN_TYPE* pRequestedType,
708 __inout BOOL* pfCancel
709 ) = 0;
710 +
711 + // OnApplyDowngrade - called when the plan determined that nothing should happen to prevent downgrading.
712 + //
713 + STDMETHOD(OnApplyDowngrade)(
714 + __in HRESULT hrRecommended,
715 + __inout HRESULT* phrStatus
716 + ) = 0;
717 };
src/burn/engine/core.cpp
+30 -9
@@ -527,8 +527,15 @@ extern "C" HRESULT CorePlan(
527 hr = PlanRelatedBundlesInitialize(&pEngineState->userExperience, &pEngineState->registration, pEngineState->command.relationType, &pEngineState->plan);
528 ExitOnFailure(hr, "Failed to initialize related bundles for plan.");
529
530 - hr = PlanRegistration(&pEngineState->plan, &pEngineState->registration, &pEngineState->dependencies, pEngineState->command.resumeType, pEngineState->command.relationType, &fContinuePlanning);
531 - ExitOnFailure(hr, "Failed to plan registration.");
530 + if (pEngineState->plan.fDowngrade)
531 + {
532 + fContinuePlanning = FALSE;
533 + }
534 + else
535 + {
536 + hr = PlanRegistration(&pEngineState->plan, &pEngineState->registration, &pEngineState->dependencies, pEngineState->command.resumeType, pEngineState->command.relationType, &fContinuePlanning);
537 + ExitOnFailure(hr, "Failed to plan registration.");
538 + }
539
540 if (fContinuePlanning)
541 {
@@ -615,6 +622,7 @@ extern "C" HRESULT CoreApply(
622 {
623 HRESULT hr = S_OK;
624 HANDLE hLock = NULL;
625 + BOOL fApplyBegan = FALSE;
626 BOOL fApplyInitialize = FALSE;
627 BOOL fElevated = FALSE;
628 BOOL fRegistered = FALSE;
@@ -627,8 +635,6 @@ extern "C" HRESULT CoreApply(
635 DWORD dwPhaseCount = 0;
636 BOOTSTRAPPER_APPLYCOMPLETE_ACTION applyCompleteAction = BOOTSTRAPPER_APPLYCOMPLETE_ACTION_NONE;
637
630 - LogId(REPORT_STANDARD, MSG_APPLY_BEGIN);
631 -
638 if (!pEngineState->fPlanned)
639 {
640 ExitOnFailure(hr = E_INVALIDSTATE, "Apply cannot be done without a successful Plan.");
@@ -638,6 +644,10 @@ extern "C" HRESULT CoreApply(
644 ExitOnFailure(hr = E_INVALIDSTATE, "Plans cannot be applied multiple times.");
645 }
646
647 + fApplyBegan = TRUE;
648 +
649 + LogId(REPORT_STANDARD, MSG_APPLY_BEGIN);
650 +
651 // Ensure any previous attempts to execute are reset.
652 ApplyReset(&pEngineState->userExperience, &pEngineState->packages);
653
@@ -653,6 +663,14 @@ extern "C" HRESULT CoreApply(
663 hr = UserExperienceOnApplyBegin(&pEngineState->userExperience, dwPhaseCount);
664 ExitOnRootFailure(hr, "BA aborted apply begin.");
665
666 + if (pEngineState->plan.fDowngrade)
667 + {
668 + hr = HRESULT_FROM_WIN32(ERROR_PRODUCT_VERSION);
669 + UserExperienceOnApplyDowngrade(&pEngineState->userExperience, &hr);
670 +
671 + ExitFunction();
672 + }
673 +
674 pEngineState->plan.fAffectedMachineState = pEngineState->plan.fCanAffectMachineState;
675
676 hr = ApplyLock(FALSE, &hLock);
@@ -804,13 +822,16 @@ LExit:
822 DeleteCriticalSection(&applyContext.csApply);
823 }
824
807 - UserExperienceOnApplyComplete(&pEngineState->userExperience, hr, restart, &applyCompleteAction);
808 - if (BOOTSTRAPPER_APPLYCOMPLETE_ACTION_RESTART == applyCompleteAction)
825 + if (fApplyBegan)
826 {
810 - pEngineState->fRestart = TRUE;
811 - }
827 + UserExperienceOnApplyComplete(&pEngineState->userExperience, hr, restart, &applyCompleteAction);
828 + if (BOOTSTRAPPER_APPLYCOMPLETE_ACTION_RESTART == applyCompleteAction)
829 + {
830 + pEngineState->fRestart = TRUE;
831 + }
832
813 - LogId(REPORT_STANDARD, MSG_APPLY_COMPLETE, hr, LoggingRestartToString(restart), LoggingBoolToString(pEngineState->fRestart));
833 + LogId(REPORT_STANDARD, MSG_APPLY_COMPLETE, hr, LoggingRestartToString(restart), LoggingBoolToString(pEngineState->fRestart));
834 + }
835
836 return hr;
837 }
src/burn/engine/engine.mc
+14
@@ -499,6 +499,20 @@ Language=English
499 Ignoring bundle dependents due to action UnsafeUninstall...
500 .
501
502 +MessageId=224
503 +Severity=Warning
504 +SymbolicName=MSG_PLAN_SKIPPED_DUE_TO_DOWNGRADE
505 +Language=English
506 +Plan skipped due to related bundle of plan type Downgrade:
507 +.
508 +
509 +MessageId=225
510 +Severity=Warning
511 +SymbolicName=MSG_UPGRADE_BUNDLE_DOWNGRADE
512 +Language=English
513 + id: %1!ls!, version: %2!ls!
514 +.
515 +
516 MessageId=299
517 Severity=Success
518 SymbolicName=MSG_PLAN_COMPLETE
src/burn/engine/plan.cpp
+17 -2
@@ -1328,11 +1328,12 @@ LExit:
1328 extern "C" HRESULT PlanRelatedBundlesInitialize(
1329 __in BURN_USER_EXPERIENCE* pUserExperience,
1330 __in BURN_REGISTRATION* pRegistration,
1331 - __in BOOTSTRAPPER_RELATION_TYPE /*relationType*/,
1332 - __in BURN_PLAN* /*pPlan*/
1331 + __in BOOTSTRAPPER_RELATION_TYPE relationType,
1332 + __in BURN_PLAN* pPlan
1333 )
1334 {
1335 HRESULT hr = S_OK;
1336 + BOOL fUninstalling = BOOTSTRAPPER_ACTION_UNINSTALL == pPlan->action || BOOTSTRAPPER_ACTION_UNSAFE_UNINSTALL == pPlan->action;
1337
1338 for (DWORD i = 0; i < pRegistration->relatedBundles.cRelatedBundles; ++i)
1339 {
@@ -1356,6 +1357,19 @@ extern "C" HRESULT PlanRelatedBundlesInitialize(
1357
1358 hr = UserExperienceOnPlanRelatedBundleType(pUserExperience, pRelatedBundle->package.sczId, &pRelatedBundle->planRelationType);
1359 ExitOnRootFailure(hr, "BA aborted plan related bundle type.");
1360 +
1361 + if (BOOTSTRAPPER_RELATED_BUNDLE_PLAN_TYPE_DOWNGRADE == pRelatedBundle->planRelationType &&
1362 + pRelatedBundle->fPlannable && !fUninstalling && BOOTSTRAPPER_RELATION_UPGRADE != relationType)
1363 + {
1364 + if (!pPlan->fDowngrade)
1365 + {
1366 + pPlan->fDowngrade = TRUE;
1367 +
1368 + LogId(REPORT_STANDARD, MSG_PLAN_SKIPPED_DUE_TO_DOWNGRADE);
1369 + }
1370 +
1371 + LogId(REPORT_VERBOSE, MSG_UPGRADE_BUNDLE_DOWNGRADE, pRelatedBundle->package.sczId, pRelatedBundle->pVersion->sczVersion);
1372 + }
1373 }
1374
1375 RelatedBundlesSortPlan(&pRegistration->relatedBundles);
@@ -3011,6 +3025,7 @@ extern "C" void PlanDump(
3025 LogStringLine(PlanDumpLevel, " can affect machine state: %hs", LoggingTrueFalseToString(pPlan->fCanAffectMachineState));
3026 LogStringLine(PlanDumpLevel, " disable-rollback: %hs", LoggingTrueFalseToString(pPlan->fDisableRollback));
3027 LogStringLine(PlanDumpLevel, " disallow-removal: %hs", LoggingTrueFalseToString(pPlan->fDisallowRemoval));
3028 + LogStringLine(PlanDumpLevel, " downgrade: %hs", LoggingTrueFalseToString(pPlan->fDowngrade));
3029 LogStringLine(PlanDumpLevel, " registration options: %hs", LoggingRegistrationOptionsToString(pPlan->dwRegistrationOperations));
3030 LogStringLine(PlanDumpLevel, " estimated size: %llu", pPlan->qwEstimatedSize);
3031 if (pPlan->sczLayoutDirectory)
src/burn/engine/plan.h
+1
@@ -250,6 +250,7 @@ typedef struct _BURN_PLAN
250 BOOL fAffectedMachineState;
251 LPWSTR sczLayoutDirectory;
252 BOOL fPlanPackageCacheRollback;
253 + BOOL fDowngrade;
254
255 DWORD64 qwCacheSizeTotal;
256
src/burn/engine/userexperience.cpp
+24
@@ -335,6 +335,30 @@ LExit:
335 return hr;
336 }
337
338 +EXTERN_C BAAPI UserExperienceOnApplyDowngrade(
339 + __in BURN_USER_EXPERIENCE* pUserExperience,
340 + __inout HRESULT* phrStatus
341 + )
342 +{
343 + HRESULT hr = S_OK;
344 + BA_ONAPPLYDOWNGRADE_ARGS args = { };
345 + BA_ONAPPLYDOWNGRADE_RESULTS results = { };
346 +
347 + args.cbSize = sizeof(args);
348 + args.hrRecommended = *phrStatus;
349 +
350 + results.cbSize = sizeof(results);
351 + results.hrStatus = *phrStatus;
352 +
353 + hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONAPPLYDOWNGRADE, &args, &results);
354 + ExitOnFailure(hr, "BA OnApplyDowngrade failed.");
355 +
356 + *phrStatus = results.hrStatus;
357 +
358 +LExit:
359 + return hr;
360 +}
361 +
362 EXTERN_C BAAPI UserExperienceOnBeginMsiTransactionBegin(
363 __in BURN_USER_EXPERIENCE* pUserExperience,
364 __in LPCWSTR wzTransactionId
src/burn/engine/userexperience.h
+4
@@ -110,6 +110,10 @@ BAAPI UserExperienceOnApplyComplete(
110 __in BOOTSTRAPPER_APPLY_RESTART restart,
111 __inout BOOTSTRAPPER_APPLYCOMPLETE_ACTION* pAction
112 );
113 +BAAPI UserExperienceOnApplyDowngrade(
114 + __in BURN_USER_EXPERIENCE* pUserExperience,
115 + __inout HRESULT* phrStatus
116 + );
117 BAAPI UserExperienceOnBeginMsiTransactionBegin(
118 __in BURN_USER_EXPERIENCE* pUserExperience,
119 __in LPCWSTR wzTransactionId
src/burn/test/BurnUnitTest/PlanTest.cpp
+88
@@ -67,6 +67,7 @@ namespace Bootstrapper
67 Assert::Equal<BOOL>(TRUE, pPlan->fCanAffectMachineState);
68 Assert::Equal<BOOL>(FALSE, pPlan->fDisableRollback);
69 Assert::Equal<BOOL>(FALSE, pPlan->fDisallowRemoval);
70 + Assert::Equal<BOOL>(FALSE, pPlan->fDowngrade);
71
72 BOOL fRollback = FALSE;
73 DWORD dwIndex = 0;
@@ -226,6 +227,7 @@ namespace Bootstrapper
227 Assert::Equal<BOOL>(TRUE, pPlan->fCanAffectMachineState);
228 Assert::Equal<BOOL>(FALSE, pPlan->fDisableRollback);
229 Assert::Equal<BOOL>(FALSE, pPlan->fDisallowRemoval);
230 + Assert::Equal<BOOL>(FALSE, pPlan->fDowngrade);
231
232 BOOL fRollback = FALSE;
233 DWORD dwIndex = 0;
@@ -358,6 +360,7 @@ namespace Bootstrapper
360 Assert::Equal<BOOL>(TRUE, pPlan->fCanAffectMachineState);
361 Assert::Equal<BOOL>(FALSE, pPlan->fDisableRollback);
362 Assert::Equal<BOOL>(FALSE, pPlan->fDisallowRemoval);
363 + Assert::Equal<BOOL>(FALSE, pPlan->fDowngrade);
364
365 BOOL fRollback = FALSE;
366 DWORD dwIndex = 0;
@@ -457,6 +460,7 @@ namespace Bootstrapper
460 Assert::Equal<BOOL>(TRUE, pPlan->fCanAffectMachineState);
461 Assert::Equal<BOOL>(FALSE, pPlan->fDisableRollback);
462 Assert::Equal<BOOL>(FALSE, pPlan->fDisallowRemoval);
463 + Assert::Equal<BOOL>(FALSE, pPlan->fDowngrade);
464
465 BOOL fRollback = FALSE;
466 DWORD dwIndex = 0;
@@ -575,6 +579,7 @@ namespace Bootstrapper
579 Assert::Equal<BOOL>(TRUE, pPlan->fCanAffectMachineState);
580 Assert::Equal<BOOL>(FALSE, pPlan->fDisableRollback);
581 Assert::Equal<BOOL>(FALSE, pPlan->fDisallowRemoval);
582 + Assert::Equal<BOOL>(FALSE, pPlan->fDowngrade);
583
584 BOOL fRollback = FALSE;
585 DWORD dwIndex = 0;
@@ -677,6 +682,7 @@ namespace Bootstrapper
682 Assert::Equal<BOOL>(TRUE, pPlan->fCanAffectMachineState);
683 Assert::Equal<BOOL>(FALSE, pPlan->fDisableRollback);
684 Assert::Equal<BOOL>(FALSE, pPlan->fDisallowRemoval);
685 + Assert::Equal<BOOL>(FALSE, pPlan->fDowngrade);
686
687 BOOL fRollback = FALSE;
688 DWORD dwIndex = 0;
@@ -741,6 +747,76 @@ namespace Bootstrapper
747 ValidateNonPermanentPackageExpectedStates(&pEngineState->packages.rgPackages[0], L"PackageA", BURN_PACKAGE_REGISTRATION_STATE_PRESENT, BURN_PACKAGE_REGISTRATION_STATE_ABSENT);
748 }
749
750 + [Fact]
751 + void SingleMsiDowngradeTest()
752 + {
753 + HRESULT hr = S_OK;
754 + BURN_ENGINE_STATE engineState = { };
755 + BURN_ENGINE_STATE* pEngineState = &engineState;
756 + BURN_PLAN* pPlan = &engineState.plan;
757 +
758 + InitializeEngineStateForCorePlan(wzSingleMsiManifestFileName, pEngineState);
759 + DetectAttachedContainerAsAttached(pEngineState);
760 + DetectPackagesAsAbsent(pEngineState);
761 + DetectRelatedBundle(pEngineState, L"{AF8355C9-CCDD-4D61-BF5F-EA5F948D8F01}", L"1.1.0.0", BOOTSTRAPPER_RELATION_UPGRADE);
762 +
763 + hr = CorePlan(pEngineState, BOOTSTRAPPER_ACTION_INSTALL);
764 + NativeAssert::Succeeded(hr, "CorePlan failed");
765 +
766 + Assert::Equal<DWORD>(BOOTSTRAPPER_ACTION_INSTALL, pPlan->action);
767 + NativeAssert::StringEqual(L"{A6F0CBF7-1578-450C-B9D7-9CF2EEC40002}", pPlan->wzBundleId);
768 + NativeAssert::StringEqual(L"{A6F0CBF7-1578-450C-B9D7-9CF2EEC40002}", pPlan->wzBundleProviderKey);
769 + Assert::Equal<BOOL>(FALSE, pPlan->fEnabledForwardCompatibleBundle);
770 + Assert::Equal<BOOL>(TRUE, pPlan->fPerMachine);
771 + Assert::Equal<BOOL>(FALSE, pPlan->fCanAffectMachineState);
772 + Assert::Equal<BOOL>(FALSE, pPlan->fDisableRollback);
773 + Assert::Equal<BOOL>(FALSE, pPlan->fDisallowRemoval);
774 + Assert::Equal<BOOL>(TRUE, pPlan->fDowngrade);
775 +
776 + BOOL fRollback = FALSE;
777 + DWORD dwIndex = 0;
778 + Assert::Equal(dwIndex, pPlan->cRegistrationActions);
779 +
780 + fRollback = TRUE;
781 + dwIndex = 0;
782 + Assert::Equal(dwIndex, pPlan->cRollbackRegistrationActions);
783 +
784 + fRollback = FALSE;
785 + dwIndex = 0;
786 + Assert::Equal(dwIndex, pPlan->cCacheActions);
787 +
788 + fRollback = TRUE;
789 + dwIndex = 0;
790 + Assert::Equal(dwIndex, pPlan->cRollbackCacheActions);
791 +
792 + Assert::Equal(0ull, pPlan->qwEstimatedSize);
793 + Assert::Equal(0ull, pPlan->qwCacheSizeTotal);
794 +
795 + fRollback = FALSE;
796 + dwIndex = 0;
797 + Assert::Equal(dwIndex, pPlan->cExecuteActions);
798 +
799 + fRollback = TRUE;
800 + dwIndex = 0;
801 + Assert::Equal(dwIndex, pPlan->cRollbackActions);
802 +
803 + Assert::Equal(0ul, pPlan->cExecutePackagesTotal);
804 + Assert::Equal(0ul, pPlan->cOverallProgressTicksTotal);
805 +
806 + dwIndex = 0;
807 + Assert::Equal(dwIndex, pPlan->cRestoreRelatedBundleActions);
808 +
809 + dwIndex = 0;
810 + Assert::Equal(dwIndex, pPlan->cCleanActions);
811 +
812 + UINT uIndex = 0;
813 + ValidatePlannedProvider(pPlan, uIndex++, L"{A6F0CBF7-1578-450C-B9D7-9CF2EEC40002}", NULL);
814 + Assert::Equal(uIndex, pPlan->cPlannedProviders);
815 +
816 + Assert::Equal(1ul, pEngineState->packages.cPackages);
817 + ValidateNonPermanentPackageExpectedStates(&pEngineState->packages.rgPackages[0], L"PackageA", BURN_PACKAGE_REGISTRATION_STATE_UNKNOWN, BURN_PACKAGE_REGISTRATION_STATE_UNKNOWN);
818 + }
819 +
820 [Fact]
821 void SingleMsiForceAbsentTest()
822 {
@@ -770,6 +846,7 @@ namespace Bootstrapper
846 Assert::Equal<BOOL>(TRUE, pPlan->fCanAffectMachineState);
847 Assert::Equal<BOOL>(FALSE, pPlan->fDisableRollback);
848 Assert::Equal<BOOL>(FALSE, pPlan->fDisallowRemoval);
849 + Assert::Equal<BOOL>(FALSE, pPlan->fDowngrade);
850
851 BOOL fRollback = FALSE;
852 DWORD dwIndex = 0;
@@ -858,6 +935,7 @@ namespace Bootstrapper
935 Assert::Equal<BOOL>(TRUE, pPlan->fCanAffectMachineState);
936 Assert::Equal<BOOL>(FALSE, pPlan->fDisableRollback);
937 Assert::Equal<BOOL>(FALSE, pPlan->fDisallowRemoval);
938 + Assert::Equal<BOOL>(FALSE, pPlan->fDowngrade);
939
940 BOOL fRollback = FALSE;
941 DWORD dwIndex = 0;
@@ -948,6 +1026,7 @@ namespace Bootstrapper
1026 Assert::Equal<BOOL>(TRUE, pPlan->fCanAffectMachineState);
1027 Assert::Equal<BOOL>(FALSE, pPlan->fDisableRollback);
1028 Assert::Equal<BOOL>(FALSE, pPlan->fDisallowRemoval);
1029 + Assert::Equal<BOOL>(FALSE, pPlan->fDowngrade);
1030
1031 BOOL fRollback = FALSE;
1032 DWORD dwIndex = 0;
@@ -1053,6 +1132,7 @@ namespace Bootstrapper
1132 Assert::Equal<BOOL>(TRUE, pPlan->fCanAffectMachineState);
1133 Assert::Equal<BOOL>(FALSE, pPlan->fDisableRollback);
1134 Assert::Equal<BOOL>(FALSE, pPlan->fDisallowRemoval);
1135 + Assert::Equal<BOOL>(FALSE, pPlan->fDowngrade);
1136
1137 BOOL fRollback = FALSE;
1138 DWORD dwIndex = 0;
@@ -1131,6 +1211,7 @@ namespace Bootstrapper
1211 Assert::Equal<BOOL>(TRUE, pPlan->fCanAffectMachineState);
1212 Assert::Equal<BOOL>(FALSE, pPlan->fDisableRollback);
1213 Assert::Equal<BOOL>(FALSE, pPlan->fDisallowRemoval);
1214 + Assert::Equal<BOOL>(FALSE, pPlan->fDowngrade);
1215
1216 BOOL fRollback = FALSE;
1217 DWORD dwIndex = 0;
@@ -1225,6 +1306,7 @@ namespace Bootstrapper
1306 Assert::Equal<BOOL>(TRUE, pPlan->fCanAffectMachineState);
1307 Assert::Equal<BOOL>(FALSE, pPlan->fDisableRollback);
1308 Assert::Equal<BOOL>(TRUE, pPlan->fDisallowRemoval);
1309 + Assert::Equal<BOOL>(FALSE, pPlan->fDowngrade);
1310
1311 BOOL fRollback = FALSE;
1312 DWORD dwIndex = 0;
@@ -1294,6 +1376,7 @@ namespace Bootstrapper
1376 Assert::Equal<BOOL>(TRUE, pPlan->fCanAffectMachineState);
1377 Assert::Equal<BOOL>(FALSE, pPlan->fDisableRollback);
1378 Assert::Equal<BOOL>(FALSE, pPlan->fDisallowRemoval);
1379 + Assert::Equal<BOOL>(FALSE, pPlan->fDowngrade);
1380
1381 BOOL fRollback = FALSE;
1382 DWORD dwIndex = 0;
@@ -1377,6 +1460,7 @@ namespace Bootstrapper
1460 Assert::Equal<BOOL>(TRUE, pPlan->fCanAffectMachineState);
1461 Assert::Equal<BOOL>(TRUE, pPlan->fDisableRollback);
1462 Assert::Equal<BOOL>(FALSE, pPlan->fDisallowRemoval);
1463 + Assert::Equal<BOOL>(FALSE, pPlan->fDowngrade);
1464
1465 BOOL fRollback = FALSE;
1466 DWORD dwIndex = 0;
@@ -1471,6 +1555,7 @@ namespace Bootstrapper
1555 Assert::Equal<BOOL>(TRUE, pPlan->fCanAffectMachineState);
1556 Assert::Equal<BOOL>(FALSE, pPlan->fDisableRollback);
1557 Assert::Equal<BOOL>(FALSE, pPlan->fDisallowRemoval);
1558 + Assert::Equal<BOOL>(FALSE, pPlan->fDowngrade);
1559
1560 BOOL fRollback = FALSE;
1561 DWORD dwIndex = 0;
@@ -1601,6 +1686,7 @@ namespace Bootstrapper
1686 Assert::Equal<BOOL>(TRUE, pPlan->fCanAffectMachineState);
1687 Assert::Equal<BOOL>(FALSE, pPlan->fDisableRollback);
1688 Assert::Equal<BOOL>(FALSE, pPlan->fDisallowRemoval);
1689 + Assert::Equal<BOOL>(FALSE, pPlan->fDowngrade);
1690
1691 BOOL fRollback = FALSE;
1692 DWORD dwIndex = 0;
@@ -1716,6 +1802,7 @@ namespace Bootstrapper
1802 Assert::Equal<BOOL>(TRUE, pPlan->fCanAffectMachineState);
1803 Assert::Equal<BOOL>(FALSE, pPlan->fDisableRollback);
1804 Assert::Equal<BOOL>(FALSE, pPlan->fDisallowRemoval);
1805 + Assert::Equal<BOOL>(FALSE, pPlan->fDowngrade);
1806
1807 BOOL fRollback = FALSE;
1808 DWORD dwIndex = 0;
@@ -1810,6 +1897,7 @@ namespace Bootstrapper
1897 Assert::Equal<BOOL>(TRUE, pPlan->fCanAffectMachineState);
1898 Assert::Equal<BOOL>(FALSE, pPlan->fDisableRollback);
1899 Assert::Equal<BOOL>(FALSE, pPlan->fDisallowRemoval);
1900 + Assert::Equal<BOOL>(FALSE, pPlan->fDowngrade);
1901
1902 BOOL fRollback = FALSE;
1903 DWORD dwIndex = 0;
src/ext/Bal/wixstdba/WixStandardBootstrapperApplication.cpp
+26 -30
@@ -254,7 +254,6 @@ public: // IBootstrapperApplication
254 )
255 {
256 BAL_INFO_PACKAGE* pPackage = NULL;
257 - int nCompare = 0;
257
258 if (!fMissingFromCache)
259 {
@@ -262,14 +261,6 @@ public: // IBootstrapperApplication
261 {
262 InitializePackageInfoForPackage(pPackage);
263 }
265 -
266 - // If we're not doing a prerequisite install, remember when our bundle would cause a downgrade.
267 - if (!m_fPrereq && BOOTSTRAPPER_RELATION_UPGRADE == relationType &&
268 - SUCCEEDED(m_pEngine->CompareVersions(m_sczBundleVersion, wzVersion, &nCompare)) && 0 > nCompare)
269 - {
270 - BalLog(BOOTSTRAPPER_LOG_LEVEL_STANDARD, "A newer version (v%ls) of this product is installed.", wzVersion);
271 - m_fDowngrading = TRUE;
272 - }
264 }
265
266 return CBalBaseBootstrapperApplication::OnDetectRelatedBundle(wzBundleId, relationType, wzBundleTag, fPerMachine, wzVersion, fMissingFromCache, pfCancel);
@@ -345,23 +336,6 @@ public: // IBootstrapperApplication
336 }
337 }
338 }
348 - else if (m_fDowngrading && BOOTSTRAPPER_ACTION_UNINSTALL < m_command.action)
349 - {
350 - if (m_fSuppressDowngradeFailure)
351 - {
352 - BalLog(BOOTSTRAPPER_LOG_LEVEL_STANDARD, "Downgrade failure has been suppressed; exiting bundle.");
353 -
354 - hr = S_OK;
355 - SetState(WIXSTDBA_STATE_APPLIED, hr);
356 - ExitFunction();
357 - }
358 - else
359 - {
360 - // If we are going to apply a downgrade, bail.
361 - hr = HRESULT_FROM_WIN32(ERROR_PRODUCT_VERSION);
362 - BalExitOnFailure(hr, "Cannot install a product when a newer version is installed.");
363 - }
364 - }
339 }
340
341 SetState(WIXSTDBA_STATE_DETECTED, hrStatus);
@@ -371,7 +345,6 @@ public: // IBootstrapperApplication
345 ::PostMessageW(m_hWnd, WM_WIXSTDBA_PLAN_PACKAGES, 0, m_command.action);
346 }
347
374 - LExit:
348 return hr;
349 }
350
@@ -1092,6 +1065,20 @@ public: // IBootstrapperApplication
1065 return hr;
1066 }
1067
1068 + virtual STDMETHODIMP OnApplyDowngrade(
1069 + __in HRESULT /*hrRecommendation*/,
1070 + __in HRESULT* phrStatus
1071 + )
1072 + {
1073 + HRESULT hr = S_OK;
1074 +
1075 + if (m_fSuppressDowngradeFailure)
1076 + {
1077 + *phrStatus = S_OK;
1078 + }
1079 +
1080 + return hr;
1081 + }
1082
1083 virtual STDMETHODIMP OnApplyComplete(
1084 __in HRESULT hrStatus,
@@ -1445,6 +1432,9 @@ public: // IBootstrapperApplication
1432 case BOOTSTRAPPER_APPLICATION_MESSAGE_ONPLANRELATEDBUNDLETYPE:
1433 OnPlanRelatedBundleTypeFallback(reinterpret_cast<BA_ONPLANRELATEDBUNDLETYPE_ARGS*>(pvArgs), reinterpret_cast<BA_ONPLANRELATEDBUNDLETYPE_RESULTS*>(pvResults));
1434 break;
1435 + case BOOTSTRAPPER_APPLICATION_MESSAGE_ONAPPLYDOWNGRADE:
1436 + OnApplyDowngradeFallback(reinterpret_cast<BA_ONAPPLYDOWNGRADE_ARGS*>(pvArgs), reinterpret_cast<BA_ONAPPLYDOWNGRADE_RESULTS*>(pvResults));
1437 + break;
1438 default:
1439 #ifdef DEBUG
1440 BalLog(BOOTSTRAPPER_LOG_LEVEL_STANDARD, "WIXSTDBA: Forwarding unknown BA message: %d", message);
@@ -2118,6 +2108,14 @@ private: // privates
2108 BalLogId(BOOTSTRAPPER_LOG_LEVEL_STANDARD, MSG_WIXSTDBA_PLANNED_RESTORE_RELATED_BUNDLE, m_hModule, pArgs->wzBundleId, LoggingRequestStateToString(requestedState), LoggingRequestStateToString(pResults->requestedState));
2109 }
2110
2111 + void OnApplyDowngradeFallback(
2112 + __in BA_ONAPPLYDOWNGRADE_ARGS* pArgs,
2113 + __inout BA_ONAPPLYDOWNGRADE_RESULTS* pResults
2114 + )
2115 + {
2116 + m_pfnBAFunctionsProc(BA_FUNCTIONS_MESSAGE_ONAPPLYDOWNGRADE, pArgs, pResults, m_pvBAFunctionsProcContext);
2117 + }
2118 +
2119
2120 public: //CBalBaseBootstrapperApplication
2121 virtual STDMETHODIMP Initialize(
@@ -4195,7 +4193,7 @@ public:
4193 __in BOOL fPrereq,
4194 __in HRESULT hrHostInitialization,
4195 __in IBootstrapperEngine* pEngine
4198 - ) : CBalBaseBootstrapperApplication(pEngine, 3, 3000)
4196 + ) : CBalBaseBootstrapperApplication(pEngine, 3, 3000)
4197 {
4198 THEME_ASSIGN_CONTROL_ID* pAssignControl = NULL;
4199
@@ -4224,7 +4222,6 @@ public:
4222 m_state = WIXSTDBA_STATE_INITIALIZING;
4223 m_hrFinal = hrHostInitialization;
4224
4227 - m_fDowngrading = FALSE;
4225 m_restartResult = BOOTSTRAPPER_APPLY_RESTART_NONE;
4226 m_fRestartRequired = FALSE;
4227 m_fShouldRestart = FALSE;
@@ -4529,7 +4526,6 @@ private:
4526 DWORD m_dwCalculatedCacheProgress;
4527 DWORD m_dwCalculatedExecuteProgress;
4528
4532 - BOOL m_fDowngrading;
4529 BOOTSTRAPPER_APPLY_RESTART m_restartResult;
4530 BOOL m_fRestartRequired;
4531 BOOL m_fShouldRestart;
src/test/burn/WixToolsetTest.BurnE2E/WixStdBaTests.cs new
+55
@@ -0,0 +1,55 @@
1 +// 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.
2 +
3 +namespace WixToolsetTest.BurnE2E
4 +{
5 + using WixTestTools;
6 + using Xunit;
7 + using Xunit.Abstractions;
8 +
9 + public class WixStdBaTests : BurnE2ETests
10 + {
11 + public WixStdBaTests(ITestOutputHelper testOutputHelper) : base(testOutputHelper) { }
12 +
13 + [Fact]
14 + public void ExitsWithErrorWhenDowngradingWithoutSuppression()
15 + {
16 + var packageA = this.CreatePackageInstaller("PackageA");
17 + var bundle1v10 = this.CreateBundleInstaller("WixStdBaTest1_v10");
18 + var bundle1v11 = this.CreateBundleInstaller("WixStdBaTest1_v11");
19 +
20 + packageA.VerifyInstalled(false);
21 +
22 + bundle1v11.Install();
23 + bundle1v11.VerifyRegisteredAndInPackageCache();
24 +
25 + packageA.VerifyInstalled(true);
26 +
27 + bundle1v10.Install((int)MSIExec.MSIExecReturnCode.ERROR_PRODUCT_VERSION);
28 + bundle1v10.VerifyUnregisteredAndRemovedFromPackageCache();
29 + bundle1v11.VerifyRegisteredAndInPackageCache();
30 +
31 + packageA.VerifyInstalled(true);
32 + }
33 +
34 + [Fact]
35 + public void ExitsWithoutErrorWhenDowngradingWithSuppression()
36 + {
37 + var packageA = this.CreatePackageInstaller("PackageA");
38 + var bundle1v11 = this.CreateBundleInstaller("WixStdBaTest1_v11");
39 + var bundle1v12 = this.CreateBundleInstaller("WixStdBaTest1_v12");
40 +
41 + packageA.VerifyInstalled(false);
42 +
43 + bundle1v12.Install();
44 + bundle1v12.VerifyRegisteredAndInPackageCache();
45 +
46 + packageA.VerifyInstalled(true);
47 +
48 + bundle1v11.Install();
49 + bundle1v11.VerifyUnregisteredAndRemovedFromPackageCache();
50 + bundle1v12.VerifyRegisteredAndInPackageCache();
51 +
52 + packageA.VerifyInstalled(true);
53 + }
54 + }
55 +}