aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/api/burn/WixToolset.BootstrapperCore.Native/inc/BootstrapperApplication.h13
-rw-r--r--src/api/burn/WixToolset.Mba.Core/BootstrapperApplication.cs25
-rw-r--r--src/api/burn/WixToolset.Mba.Core/EventArgs.cs24
-rw-r--r--src/api/burn/WixToolset.Mba.Core/IBootstrapperApplication.cs10
-rw-r--r--src/api/burn/WixToolset.Mba.Core/IDefaultBootstrapperApplication.cs5
-rw-r--r--src/api/burn/balutil/inc/BAFunctions.h1
-rw-r--r--src/api/burn/balutil/inc/BalBaseBAFunctions.h8
-rw-r--r--src/api/burn/balutil/inc/BalBaseBAFunctionsProc.h1
-rw-r--r--src/api/burn/balutil/inc/BalBaseBootstrapperApplication.h8
-rw-r--r--src/api/burn/balutil/inc/BalBaseBootstrapperApplicationProc.h12
-rw-r--r--src/api/burn/balutil/inc/IBootstrapperApplication.h7
-rw-r--r--src/burn/engine/core.cpp39
-rw-r--r--src/burn/engine/engine.mc14
-rw-r--r--src/burn/engine/plan.cpp19
-rw-r--r--src/burn/engine/plan.h1
-rw-r--r--src/burn/engine/userexperience.cpp24
-rw-r--r--src/burn/engine/userexperience.h4
-rw-r--r--src/burn/test/BurnUnitTest/PlanTest.cpp88
-rw-r--r--src/ext/Bal/wixstdba/WixStandardBootstrapperApplication.cpp56
-rw-r--r--src/test/burn/WixToolsetTest.BurnE2E/WixStdBaTests.cs55
20 files changed, 373 insertions, 41 deletions
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
223 BOOTSTRAPPER_APPLICATION_MESSAGE_ONPLANNEDCOMPATIBLEPACKAGE, 223 BOOTSTRAPPER_APPLICATION_MESSAGE_ONPLANNEDCOMPATIBLEPACKAGE,
224 BOOTSTRAPPER_APPLICATION_MESSAGE_ONPLANRESTORERELATEDBUNDLE, 224 BOOTSTRAPPER_APPLICATION_MESSAGE_ONPLANRESTORERELATEDBUNDLE,
225 BOOTSTRAPPER_APPLICATION_MESSAGE_ONPLANRELATEDBUNDLETYPE, 225 BOOTSTRAPPER_APPLICATION_MESSAGE_ONPLANRELATEDBUNDLETYPE,
226 BOOTSTRAPPER_APPLICATION_MESSAGE_ONAPPLYDOWNGRADE,
226}; 227};
227 228
228enum BOOTSTRAPPER_APPLYCOMPLETE_ACTION 229enum BOOTSTRAPPER_APPLYCOMPLETE_ACTION
@@ -354,6 +355,18 @@ struct BA_ONAPPLYCOMPLETE_RESULTS
354 BOOTSTRAPPER_APPLYCOMPLETE_ACTION action; 355 BOOTSTRAPPER_APPLYCOMPLETE_ACTION action;
355}; 356};
356 357
358struct BA_ONAPPLYDOWNGRADE_ARGS
359{
360 DWORD cbSize;
361 HRESULT hrRecommended;
362};
363
364struct BA_ONAPPLYDOWNGRADE_RESULTS
365{
366 DWORD cbSize;
367 HRESULT hrStatus;
368};
369
357struct BA_ONBEGINMSITRANSACTIONBEGIN_ARGS 370struct BA_ONBEGINMSITRANSACTIONBEGIN_ARGS
358{ 371{
359 DWORD cbSize; 372 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
@@ -209,6 +209,9 @@ namespace WixToolset.Mba.Core
209 public event EventHandler<ApplyCompleteEventArgs> ApplyComplete; 209 public event EventHandler<ApplyCompleteEventArgs> ApplyComplete;
210 210
211 /// <inheritdoc/> 211 /// <inheritdoc/>
212 public event EventHandler<ApplyDowngradeEventArgs> ApplyDowngrade;
213
214 /// <inheritdoc/>
212 public event EventHandler<ExecuteProgressEventArgs> ExecuteProgress; 215 public event EventHandler<ExecuteProgressEventArgs> ExecuteProgress;
213 216
214 /// <inheritdoc/> 217 /// <inheritdoc/>
@@ -1056,6 +1059,19 @@ namespace WixToolset.Mba.Core
1056 } 1059 }
1057 1060
1058 /// <summary> 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>
1059 /// Called by the engine, raises the <see cref="ExecuteProgress"/> event. 1075 /// Called by the engine, raises the <see cref="ExecuteProgress"/> event.
1060 /// </summary> 1076 /// </summary>
1061 /// <param name="args">Additional arguments for this event.</param> 1077 /// <param name="args">Additional arguments for this event.</param>
@@ -1907,6 +1923,15 @@ namespace WixToolset.Mba.Core
1907 return args.HResult; 1923 return args.HResult;
1908 } 1924 }
1909 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
1910 int IBootstrapperApplication.OnLaunchApprovedExeBegin(ref bool fCancel) 1935 int IBootstrapperApplication.OnLaunchApprovedExeBegin(ref bool fCancel)
1911 { 1936 {
1912 LaunchApprovedExeBeginEventArgs args = new LaunchApprovedExeBeginEventArgs(fCancel); 1937 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
@@ -1859,6 +1859,30 @@ namespace WixToolset.Mba.Core
1859 } 1859 }
1860 1860
1861 /// <summary> 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>
1862 /// EventArgs for <see cref="IDefaultBootstrapperApplication.CacheAcquireResolving"/>. 1886 /// EventArgs for <see cref="IDefaultBootstrapperApplication.CacheAcquireResolving"/>.
1863 /// </summary> 1887 /// </summary>
1864 [Serializable] 1888 [Serializable]
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
1160 [MarshalAs(UnmanagedType.U4)] ref RelatedBundlePlanType pRequestedType, 1160 [MarshalAs(UnmanagedType.U4)] ref RelatedBundlePlanType pRequestedType,
1161 [MarshalAs(UnmanagedType.Bool)] ref bool fCancel 1161 [MarshalAs(UnmanagedType.Bool)] ref bool fCancel
1162 ); 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 );
1163 } 1173 }
1164 1174
1165 /// <summary> 1175 /// <summary>
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
@@ -20,6 +20,11 @@ namespace WixToolset.Mba.Core
20 event EventHandler<ApplyCompleteEventArgs> ApplyComplete; 20 event EventHandler<ApplyCompleteEventArgs> ApplyComplete;
21 21
22 /// <summary> 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>
23 /// Fired when the engine is about to begin an MSI transaction. 28 /// Fired when the engine is about to begin an MSI transaction.
24 /// </summary> 29 /// </summary>
25 event EventHandler<BeginMsiTransactionBeginEventArgs> BeginMsiTransactionBegin; 30 event EventHandler<BeginMsiTransactionBeginEventArgs> BeginMsiTransactionBegin;
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
90 BA_FUNCTIONS_MESSAGE_ONPLANNEDCOMPATIBLEPACKAGE = BOOTSTRAPPER_APPLICATION_MESSAGE_ONPLANNEDCOMPATIBLEPACKAGE, 90 BA_FUNCTIONS_MESSAGE_ONPLANNEDCOMPATIBLEPACKAGE = BOOTSTRAPPER_APPLICATION_MESSAGE_ONPLANNEDCOMPATIBLEPACKAGE,
91 BA_FUNCTIONS_MESSAGE_ONPLANRESTORERELATEDBUNDLE = BOOTSTRAPPER_APPLICATION_MESSAGE_ONPLANRESTORERELATEDBUNDLE, 91 BA_FUNCTIONS_MESSAGE_ONPLANRESTORERELATEDBUNDLE = BOOTSTRAPPER_APPLICATION_MESSAGE_ONPLANRESTORERELATEDBUNDLE,
92 BA_FUNCTIONS_MESSAGE_ONPLANRELATEDBUNDLETYPE = BOOTSTRAPPER_APPLICATION_MESSAGE_ONPLANRELATEDBUNDLETYPE, 92 BA_FUNCTIONS_MESSAGE_ONPLANRELATEDBUNDLETYPE = BOOTSTRAPPER_APPLICATION_MESSAGE_ONPLANRELATEDBUNDLETYPE,
93 BA_FUNCTIONS_MESSAGE_ONAPPLYDOWNGRADE = BOOTSTRAPPER_APPLICATION_MESSAGE_ONAPPLYDOWNGRADE,
93 94
94 BA_FUNCTIONS_MESSAGE_ONTHEMELOADED = 1024, 95 BA_FUNCTIONS_MESSAGE_ONTHEMELOADED = 1024,
95 BA_FUNCTIONS_MESSAGE_WNDPROC, 96 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
869 return S_OK; 869 return S_OK;
870 } 870 }
871 871
872 virtual STDMETHODIMP OnApplyDowngrade(
873 __in HRESULT /*hrRecommended*/,
874 __in HRESULT* /*phrStatus*/
875 )
876 {
877 return S_OK;
878 }
879
872public: // IBAFunctions 880public: // IBAFunctions
873 virtual STDMETHODIMP OnPlan( 881 virtual STDMETHODIMP OnPlan(
874 ) 882 )
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(
161 case BA_FUNCTIONS_MESSAGE_ONPLANNEDCOMPATIBLEPACKAGE: 161 case BA_FUNCTIONS_MESSAGE_ONPLANNEDCOMPATIBLEPACKAGE:
162 case BA_FUNCTIONS_MESSAGE_ONPLANRESTORERELATEDBUNDLE: 162 case BA_FUNCTIONS_MESSAGE_ONPLANRESTORERELATEDBUNDLE:
163 case BA_FUNCTIONS_MESSAGE_ONPLANRELATEDBUNDLETYPE: 163 case BA_FUNCTIONS_MESSAGE_ONPLANRELATEDBUNDLETYPE:
164 case BA_FUNCTIONS_MESSAGE_ONAPPLYDOWNGRADE:
164 hr = BalBaseBootstrapperApplicationProc((BOOTSTRAPPER_APPLICATION_MESSAGE)message, pvArgs, pvResults, pvContext); 165 hr = BalBaseBootstrapperApplicationProc((BOOTSTRAPPER_APPLICATION_MESSAGE)message, pvArgs, pvResults, pvContext);
165 break; 166 break;
166 case BA_FUNCTIONS_MESSAGE_ONTHEMELOADED: 167 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
1069 return S_OK; 1069 return S_OK;
1070 } 1070 }
1071 1071
1072 virtual STDMETHODIMP OnApplyDowngrade(
1073 __in HRESULT /*hrRecommended*/,
1074 __in HRESULT* /*phrStatus*/
1075 )
1076 {
1077 return S_OK;
1078 }
1079
1072public: //CBalBaseBootstrapperApplication 1080public: //CBalBaseBootstrapperApplication
1073 virtual STDMETHODIMP Initialize( 1081 virtual STDMETHODIMP Initialize(
1074 __in const BOOTSTRAPPER_CREATE_ARGS* pCreateArgs 1082 __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(
738 return pBA->OnPlanRelatedBundleType(pArgs->wzBundleId, pArgs->recommendedType, &pResults->requestedType, &pResults->fCancel); 738 return pBA->OnPlanRelatedBundleType(pArgs->wzBundleId, pArgs->recommendedType, &pResults->requestedType, &pResults->fCancel);
739} 739}
740 740
741static 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
741/******************************************************************* 750/*******************************************************************
742BalBaseBootstrapperApplicationProc - requires pvContext to be of type IBootstrapperApplication. 751BalBaseBootstrapperApplicationProc - requires pvContext to be of type IBootstrapperApplication.
743 Provides a default mapping between the new message based BA interface and 752 Provides a default mapping between the new message based BA interface and
@@ -1000,6 +1009,9 @@ static HRESULT WINAPI BalBaseBootstrapperApplicationProc(
1000 case BOOTSTRAPPER_APPLICATION_MESSAGE_ONPLANRELATEDBUNDLETYPE: 1009 case BOOTSTRAPPER_APPLICATION_MESSAGE_ONPLANRELATEDBUNDLETYPE:
1001 hr = BalBaseBAProcOnPlanRelatedBundleType(pBA, reinterpret_cast<BA_ONPLANRELATEDBUNDLETYPE_ARGS*>(pvArgs), reinterpret_cast<BA_ONPLANRELATEDBUNDLETYPE_RESULTS*>(pvResults)); 1010 hr = BalBaseBAProcOnPlanRelatedBundleType(pBA, reinterpret_cast<BA_ONPLANRELATEDBUNDLETYPE_ARGS*>(pvArgs), reinterpret_cast<BA_ONPLANRELATEDBUNDLETYPE_RESULTS*>(pvResults));
1002 break; 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;
1003 } 1015 }
1004 } 1016 }
1005 1017
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
707 __inout BOOTSTRAPPER_RELATED_BUNDLE_PLAN_TYPE* pRequestedType, 707 __inout BOOTSTRAPPER_RELATED_BUNDLE_PLAN_TYPE* pRequestedType,
708 __inout BOOL* pfCancel 708 __inout BOOL* pfCancel
709 ) = 0; 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;
710}; 717};
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(
527 hr = PlanRelatedBundlesInitialize(&pEngineState->userExperience, &pEngineState->registration, pEngineState->command.relationType, &pEngineState->plan); 527 hr = PlanRelatedBundlesInitialize(&pEngineState->userExperience, &pEngineState->registration, pEngineState->command.relationType, &pEngineState->plan);
528 ExitOnFailure(hr, "Failed to initialize related bundles for plan."); 528 ExitOnFailure(hr, "Failed to initialize related bundles for plan.");
529 529
530 hr = PlanRegistration(&pEngineState->plan, &pEngineState->registration, &pEngineState->dependencies, pEngineState->command.resumeType, pEngineState->command.relationType, &fContinuePlanning); 530 if (pEngineState->plan.fDowngrade)
531 ExitOnFailure(hr, "Failed to plan registration."); 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 }
532 539
533 if (fContinuePlanning) 540 if (fContinuePlanning)
534 { 541 {
@@ -615,6 +622,7 @@ extern "C" HRESULT CoreApply(
615{ 622{
616 HRESULT hr = S_OK; 623 HRESULT hr = S_OK;
617 HANDLE hLock = NULL; 624 HANDLE hLock = NULL;
625 BOOL fApplyBegan = FALSE;
618 BOOL fApplyInitialize = FALSE; 626 BOOL fApplyInitialize = FALSE;
619 BOOL fElevated = FALSE; 627 BOOL fElevated = FALSE;
620 BOOL fRegistered = FALSE; 628 BOOL fRegistered = FALSE;
@@ -627,8 +635,6 @@ extern "C" HRESULT CoreApply(
627 DWORD dwPhaseCount = 0; 635 DWORD dwPhaseCount = 0;
628 BOOTSTRAPPER_APPLYCOMPLETE_ACTION applyCompleteAction = BOOTSTRAPPER_APPLYCOMPLETE_ACTION_NONE; 636 BOOTSTRAPPER_APPLYCOMPLETE_ACTION applyCompleteAction = BOOTSTRAPPER_APPLYCOMPLETE_ACTION_NONE;
629 637
630 LogId(REPORT_STANDARD, MSG_APPLY_BEGIN);
631
632 if (!pEngineState->fPlanned) 638 if (!pEngineState->fPlanned)
633 { 639 {
634 ExitOnFailure(hr = E_INVALIDSTATE, "Apply cannot be done without a successful Plan."); 640 ExitOnFailure(hr = E_INVALIDSTATE, "Apply cannot be done without a successful Plan.");
@@ -638,6 +644,10 @@ extern "C" HRESULT CoreApply(
638 ExitOnFailure(hr = E_INVALIDSTATE, "Plans cannot be applied multiple times."); 644 ExitOnFailure(hr = E_INVALIDSTATE, "Plans cannot be applied multiple times.");
639 } 645 }
640 646
647 fApplyBegan = TRUE;
648
649 LogId(REPORT_STANDARD, MSG_APPLY_BEGIN);
650
641 // Ensure any previous attempts to execute are reset. 651 // Ensure any previous attempts to execute are reset.
642 ApplyReset(&pEngineState->userExperience, &pEngineState->packages); 652 ApplyReset(&pEngineState->userExperience, &pEngineState->packages);
643 653
@@ -653,6 +663,14 @@ extern "C" HRESULT CoreApply(
653 hr = UserExperienceOnApplyBegin(&pEngineState->userExperience, dwPhaseCount); 663 hr = UserExperienceOnApplyBegin(&pEngineState->userExperience, dwPhaseCount);
654 ExitOnRootFailure(hr, "BA aborted apply begin."); 664 ExitOnRootFailure(hr, "BA aborted apply begin.");
655 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
656 pEngineState->plan.fAffectedMachineState = pEngineState->plan.fCanAffectMachineState; 674 pEngineState->plan.fAffectedMachineState = pEngineState->plan.fCanAffectMachineState;
657 675
658 hr = ApplyLock(FALSE, &hLock); 676 hr = ApplyLock(FALSE, &hLock);
@@ -804,13 +822,16 @@ LExit:
804 DeleteCriticalSection(&applyContext.csApply); 822 DeleteCriticalSection(&applyContext.csApply);
805 } 823 }
806 824
807 UserExperienceOnApplyComplete(&pEngineState->userExperience, hr, restart, &applyCompleteAction); 825 if (fApplyBegan)
808 if (BOOTSTRAPPER_APPLYCOMPLETE_ACTION_RESTART == applyCompleteAction)
809 { 826 {
810 pEngineState->fRestart = TRUE; 827 UserExperienceOnApplyComplete(&pEngineState->userExperience, hr, restart, &applyCompleteAction);
811 } 828 if (BOOTSTRAPPER_APPLYCOMPLETE_ACTION_RESTART == applyCompleteAction)
829 {
830 pEngineState->fRestart = TRUE;
831 }
812 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 }
814 835
815 return hr; 836 return hr;
816} 837}
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
499Ignoring bundle dependents due to action UnsafeUninstall... 499Ignoring bundle dependents due to action UnsafeUninstall...
500. 500.
501 501
502MessageId=224
503Severity=Warning
504SymbolicName=MSG_PLAN_SKIPPED_DUE_TO_DOWNGRADE
505Language=English
506Plan skipped due to related bundle of plan type Downgrade:
507.
508
509MessageId=225
510Severity=Warning
511SymbolicName=MSG_UPGRADE_BUNDLE_DOWNGRADE
512Language=English
513 id: %1!ls!, version: %2!ls!
514.
515
502MessageId=299 516MessageId=299
503Severity=Success 517Severity=Success
504SymbolicName=MSG_PLAN_COMPLETE 518SymbolicName=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:
1328extern "C" HRESULT PlanRelatedBundlesInitialize( 1328extern "C" HRESULT PlanRelatedBundlesInitialize(
1329 __in BURN_USER_EXPERIENCE* pUserExperience, 1329 __in BURN_USER_EXPERIENCE* pUserExperience,
1330 __in BURN_REGISTRATION* pRegistration, 1330 __in BURN_REGISTRATION* pRegistration,
1331 __in BOOTSTRAPPER_RELATION_TYPE /*relationType*/, 1331 __in BOOTSTRAPPER_RELATION_TYPE relationType,
1332 __in BURN_PLAN* /*pPlan*/ 1332 __in BURN_PLAN* pPlan
1333 ) 1333 )
1334{ 1334{
1335 HRESULT hr = S_OK; 1335 HRESULT hr = S_OK;
1336 BOOL fUninstalling = BOOTSTRAPPER_ACTION_UNINSTALL == pPlan->action || BOOTSTRAPPER_ACTION_UNSAFE_UNINSTALL == pPlan->action;
1336 1337
1337 for (DWORD i = 0; i < pRegistration->relatedBundles.cRelatedBundles; ++i) 1338 for (DWORD i = 0; i < pRegistration->relatedBundles.cRelatedBundles; ++i)
1338 { 1339 {
@@ -1356,6 +1357,19 @@ extern "C" HRESULT PlanRelatedBundlesInitialize(
1356 1357
1357 hr = UserExperienceOnPlanRelatedBundleType(pUserExperience, pRelatedBundle->package.sczId, &pRelatedBundle->planRelationType); 1358 hr = UserExperienceOnPlanRelatedBundleType(pUserExperience, pRelatedBundle->package.sczId, &pRelatedBundle->planRelationType);
1358 ExitOnRootFailure(hr, "BA aborted plan related bundle type."); 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 }
1359 } 1373 }
1360 1374
1361 RelatedBundlesSortPlan(&pRegistration->relatedBundles); 1375 RelatedBundlesSortPlan(&pRegistration->relatedBundles);
@@ -3011,6 +3025,7 @@ extern "C" void PlanDump(
3011 LogStringLine(PlanDumpLevel, " can affect machine state: %hs", LoggingTrueFalseToString(pPlan->fCanAffectMachineState)); 3025 LogStringLine(PlanDumpLevel, " can affect machine state: %hs", LoggingTrueFalseToString(pPlan->fCanAffectMachineState));
3012 LogStringLine(PlanDumpLevel, " disable-rollback: %hs", LoggingTrueFalseToString(pPlan->fDisableRollback)); 3026 LogStringLine(PlanDumpLevel, " disable-rollback: %hs", LoggingTrueFalseToString(pPlan->fDisableRollback));
3013 LogStringLine(PlanDumpLevel, " disallow-removal: %hs", LoggingTrueFalseToString(pPlan->fDisallowRemoval)); 3027 LogStringLine(PlanDumpLevel, " disallow-removal: %hs", LoggingTrueFalseToString(pPlan->fDisallowRemoval));
3028 LogStringLine(PlanDumpLevel, " downgrade: %hs", LoggingTrueFalseToString(pPlan->fDowngrade));
3014 LogStringLine(PlanDumpLevel, " registration options: %hs", LoggingRegistrationOptionsToString(pPlan->dwRegistrationOperations)); 3029 LogStringLine(PlanDumpLevel, " registration options: %hs", LoggingRegistrationOptionsToString(pPlan->dwRegistrationOperations));
3015 LogStringLine(PlanDumpLevel, " estimated size: %llu", pPlan->qwEstimatedSize); 3030 LogStringLine(PlanDumpLevel, " estimated size: %llu", pPlan->qwEstimatedSize);
3016 if (pPlan->sczLayoutDirectory) 3031 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
250 BOOL fAffectedMachineState; 250 BOOL fAffectedMachineState;
251 LPWSTR sczLayoutDirectory; 251 LPWSTR sczLayoutDirectory;
252 BOOL fPlanPackageCacheRollback; 252 BOOL fPlanPackageCacheRollback;
253 BOOL fDowngrade;
253 254
254 DWORD64 qwCacheSizeTotal; 255 DWORD64 qwCacheSizeTotal;
255 256
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:
335 return hr; 335 return hr;
336} 336}
337 337
338EXTERN_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
358LExit:
359 return hr;
360}
361
338EXTERN_C BAAPI UserExperienceOnBeginMsiTransactionBegin( 362EXTERN_C BAAPI UserExperienceOnBeginMsiTransactionBegin(
339 __in BURN_USER_EXPERIENCE* pUserExperience, 363 __in BURN_USER_EXPERIENCE* pUserExperience,
340 __in LPCWSTR wzTransactionId 364 __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(
110 __in BOOTSTRAPPER_APPLY_RESTART restart, 110 __in BOOTSTRAPPER_APPLY_RESTART restart,
111 __inout BOOTSTRAPPER_APPLYCOMPLETE_ACTION* pAction 111 __inout BOOTSTRAPPER_APPLYCOMPLETE_ACTION* pAction
112 ); 112 );
113BAAPI UserExperienceOnApplyDowngrade(
114 __in BURN_USER_EXPERIENCE* pUserExperience,
115 __inout HRESULT* phrStatus
116 );
113BAAPI UserExperienceOnBeginMsiTransactionBegin( 117BAAPI UserExperienceOnBeginMsiTransactionBegin(
114 __in BURN_USER_EXPERIENCE* pUserExperience, 118 __in BURN_USER_EXPERIENCE* pUserExperience,
115 __in LPCWSTR wzTransactionId 119 __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
67 Assert::Equal<BOOL>(TRUE, pPlan->fCanAffectMachineState); 67 Assert::Equal<BOOL>(TRUE, pPlan->fCanAffectMachineState);
68 Assert::Equal<BOOL>(FALSE, pPlan->fDisableRollback); 68 Assert::Equal<BOOL>(FALSE, pPlan->fDisableRollback);
69 Assert::Equal<BOOL>(FALSE, pPlan->fDisallowRemoval); 69 Assert::Equal<BOOL>(FALSE, pPlan->fDisallowRemoval);
70 Assert::Equal<BOOL>(FALSE, pPlan->fDowngrade);
70 71
71 BOOL fRollback = FALSE; 72 BOOL fRollback = FALSE;
72 DWORD dwIndex = 0; 73 DWORD dwIndex = 0;
@@ -226,6 +227,7 @@ namespace Bootstrapper
226 Assert::Equal<BOOL>(TRUE, pPlan->fCanAffectMachineState); 227 Assert::Equal<BOOL>(TRUE, pPlan->fCanAffectMachineState);
227 Assert::Equal<BOOL>(FALSE, pPlan->fDisableRollback); 228 Assert::Equal<BOOL>(FALSE, pPlan->fDisableRollback);
228 Assert::Equal<BOOL>(FALSE, pPlan->fDisallowRemoval); 229 Assert::Equal<BOOL>(FALSE, pPlan->fDisallowRemoval);
230 Assert::Equal<BOOL>(FALSE, pPlan->fDowngrade);
229 231
230 BOOL fRollback = FALSE; 232 BOOL fRollback = FALSE;
231 DWORD dwIndex = 0; 233 DWORD dwIndex = 0;
@@ -358,6 +360,7 @@ namespace Bootstrapper
358 Assert::Equal<BOOL>(TRUE, pPlan->fCanAffectMachineState); 360 Assert::Equal<BOOL>(TRUE, pPlan->fCanAffectMachineState);
359 Assert::Equal<BOOL>(FALSE, pPlan->fDisableRollback); 361 Assert::Equal<BOOL>(FALSE, pPlan->fDisableRollback);
360 Assert::Equal<BOOL>(FALSE, pPlan->fDisallowRemoval); 362 Assert::Equal<BOOL>(FALSE, pPlan->fDisallowRemoval);
363 Assert::Equal<BOOL>(FALSE, pPlan->fDowngrade);
361 364
362 BOOL fRollback = FALSE; 365 BOOL fRollback = FALSE;
363 DWORD dwIndex = 0; 366 DWORD dwIndex = 0;
@@ -457,6 +460,7 @@ namespace Bootstrapper
457 Assert::Equal<BOOL>(TRUE, pPlan->fCanAffectMachineState); 460 Assert::Equal<BOOL>(TRUE, pPlan->fCanAffectMachineState);
458 Assert::Equal<BOOL>(FALSE, pPlan->fDisableRollback); 461 Assert::Equal<BOOL>(FALSE, pPlan->fDisableRollback);
459 Assert::Equal<BOOL>(FALSE, pPlan->fDisallowRemoval); 462 Assert::Equal<BOOL>(FALSE, pPlan->fDisallowRemoval);
463 Assert::Equal<BOOL>(FALSE, pPlan->fDowngrade);
460 464
461 BOOL fRollback = FALSE; 465 BOOL fRollback = FALSE;
462 DWORD dwIndex = 0; 466 DWORD dwIndex = 0;
@@ -575,6 +579,7 @@ namespace Bootstrapper
575 Assert::Equal<BOOL>(TRUE, pPlan->fCanAffectMachineState); 579 Assert::Equal<BOOL>(TRUE, pPlan->fCanAffectMachineState);
576 Assert::Equal<BOOL>(FALSE, pPlan->fDisableRollback); 580 Assert::Equal<BOOL>(FALSE, pPlan->fDisableRollback);
577 Assert::Equal<BOOL>(FALSE, pPlan->fDisallowRemoval); 581 Assert::Equal<BOOL>(FALSE, pPlan->fDisallowRemoval);
582 Assert::Equal<BOOL>(FALSE, pPlan->fDowngrade);
578 583
579 BOOL fRollback = FALSE; 584 BOOL fRollback = FALSE;
580 DWORD dwIndex = 0; 585 DWORD dwIndex = 0;
@@ -677,6 +682,7 @@ namespace Bootstrapper
677 Assert::Equal<BOOL>(TRUE, pPlan->fCanAffectMachineState); 682 Assert::Equal<BOOL>(TRUE, pPlan->fCanAffectMachineState);
678 Assert::Equal<BOOL>(FALSE, pPlan->fDisableRollback); 683 Assert::Equal<BOOL>(FALSE, pPlan->fDisableRollback);
679 Assert::Equal<BOOL>(FALSE, pPlan->fDisallowRemoval); 684 Assert::Equal<BOOL>(FALSE, pPlan->fDisallowRemoval);
685 Assert::Equal<BOOL>(FALSE, pPlan->fDowngrade);
680 686
681 BOOL fRollback = FALSE; 687 BOOL fRollback = FALSE;
682 DWORD dwIndex = 0; 688 DWORD dwIndex = 0;
@@ -742,6 +748,76 @@ namespace Bootstrapper
742 } 748 }
743 749
744 [Fact] 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]
745 void SingleMsiForceAbsentTest() 821 void SingleMsiForceAbsentTest()
746 { 822 {
747 HRESULT hr = S_OK; 823 HRESULT hr = S_OK;
@@ -770,6 +846,7 @@ namespace Bootstrapper
770 Assert::Equal<BOOL>(TRUE, pPlan->fCanAffectMachineState); 846 Assert::Equal<BOOL>(TRUE, pPlan->fCanAffectMachineState);
771 Assert::Equal<BOOL>(FALSE, pPlan->fDisableRollback); 847 Assert::Equal<BOOL>(FALSE, pPlan->fDisableRollback);
772 Assert::Equal<BOOL>(FALSE, pPlan->fDisallowRemoval); 848 Assert::Equal<BOOL>(FALSE, pPlan->fDisallowRemoval);
849 Assert::Equal<BOOL>(FALSE, pPlan->fDowngrade);
773 850
774 BOOL fRollback = FALSE; 851 BOOL fRollback = FALSE;
775 DWORD dwIndex = 0; 852 DWORD dwIndex = 0;
@@ -858,6 +935,7 @@ namespace Bootstrapper
858 Assert::Equal<BOOL>(TRUE, pPlan->fCanAffectMachineState); 935 Assert::Equal<BOOL>(TRUE, pPlan->fCanAffectMachineState);
859 Assert::Equal<BOOL>(FALSE, pPlan->fDisableRollback); 936 Assert::Equal<BOOL>(FALSE, pPlan->fDisableRollback);
860 Assert::Equal<BOOL>(FALSE, pPlan->fDisallowRemoval); 937 Assert::Equal<BOOL>(FALSE, pPlan->fDisallowRemoval);
938 Assert::Equal<BOOL>(FALSE, pPlan->fDowngrade);
861 939
862 BOOL fRollback = FALSE; 940 BOOL fRollback = FALSE;
863 DWORD dwIndex = 0; 941 DWORD dwIndex = 0;
@@ -948,6 +1026,7 @@ namespace Bootstrapper
948 Assert::Equal<BOOL>(TRUE, pPlan->fCanAffectMachineState); 1026 Assert::Equal<BOOL>(TRUE, pPlan->fCanAffectMachineState);
949 Assert::Equal<BOOL>(FALSE, pPlan->fDisableRollback); 1027 Assert::Equal<BOOL>(FALSE, pPlan->fDisableRollback);
950 Assert::Equal<BOOL>(FALSE, pPlan->fDisallowRemoval); 1028 Assert::Equal<BOOL>(FALSE, pPlan->fDisallowRemoval);
1029 Assert::Equal<BOOL>(FALSE, pPlan->fDowngrade);
951 1030
952 BOOL fRollback = FALSE; 1031 BOOL fRollback = FALSE;
953 DWORD dwIndex = 0; 1032 DWORD dwIndex = 0;
@@ -1053,6 +1132,7 @@ namespace Bootstrapper
1053 Assert::Equal<BOOL>(TRUE, pPlan->fCanAffectMachineState); 1132 Assert::Equal<BOOL>(TRUE, pPlan->fCanAffectMachineState);
1054 Assert::Equal<BOOL>(FALSE, pPlan->fDisableRollback); 1133 Assert::Equal<BOOL>(FALSE, pPlan->fDisableRollback);
1055 Assert::Equal<BOOL>(FALSE, pPlan->fDisallowRemoval); 1134 Assert::Equal<BOOL>(FALSE, pPlan->fDisallowRemoval);
1135 Assert::Equal<BOOL>(FALSE, pPlan->fDowngrade);
1056 1136
1057 BOOL fRollback = FALSE; 1137 BOOL fRollback = FALSE;
1058 DWORD dwIndex = 0; 1138 DWORD dwIndex = 0;
@@ -1131,6 +1211,7 @@ namespace Bootstrapper
1131 Assert::Equal<BOOL>(TRUE, pPlan->fCanAffectMachineState); 1211 Assert::Equal<BOOL>(TRUE, pPlan->fCanAffectMachineState);
1132 Assert::Equal<BOOL>(FALSE, pPlan->fDisableRollback); 1212 Assert::Equal<BOOL>(FALSE, pPlan->fDisableRollback);
1133 Assert::Equal<BOOL>(FALSE, pPlan->fDisallowRemoval); 1213 Assert::Equal<BOOL>(FALSE, pPlan->fDisallowRemoval);
1214 Assert::Equal<BOOL>(FALSE, pPlan->fDowngrade);
1134 1215
1135 BOOL fRollback = FALSE; 1216 BOOL fRollback = FALSE;
1136 DWORD dwIndex = 0; 1217 DWORD dwIndex = 0;
@@ -1225,6 +1306,7 @@ namespace Bootstrapper
1225 Assert::Equal<BOOL>(TRUE, pPlan->fCanAffectMachineState); 1306 Assert::Equal<BOOL>(TRUE, pPlan->fCanAffectMachineState);
1226 Assert::Equal<BOOL>(FALSE, pPlan->fDisableRollback); 1307 Assert::Equal<BOOL>(FALSE, pPlan->fDisableRollback);
1227 Assert::Equal<BOOL>(TRUE, pPlan->fDisallowRemoval); 1308 Assert::Equal<BOOL>(TRUE, pPlan->fDisallowRemoval);
1309 Assert::Equal<BOOL>(FALSE, pPlan->fDowngrade);
1228 1310
1229 BOOL fRollback = FALSE; 1311 BOOL fRollback = FALSE;
1230 DWORD dwIndex = 0; 1312 DWORD dwIndex = 0;
@@ -1294,6 +1376,7 @@ namespace Bootstrapper
1294 Assert::Equal<BOOL>(TRUE, pPlan->fCanAffectMachineState); 1376 Assert::Equal<BOOL>(TRUE, pPlan->fCanAffectMachineState);
1295 Assert::Equal<BOOL>(FALSE, pPlan->fDisableRollback); 1377 Assert::Equal<BOOL>(FALSE, pPlan->fDisableRollback);
1296 Assert::Equal<BOOL>(FALSE, pPlan->fDisallowRemoval); 1378 Assert::Equal<BOOL>(FALSE, pPlan->fDisallowRemoval);
1379 Assert::Equal<BOOL>(FALSE, pPlan->fDowngrade);
1297 1380
1298 BOOL fRollback = FALSE; 1381 BOOL fRollback = FALSE;
1299 DWORD dwIndex = 0; 1382 DWORD dwIndex = 0;
@@ -1377,6 +1460,7 @@ namespace Bootstrapper
1377 Assert::Equal<BOOL>(TRUE, pPlan->fCanAffectMachineState); 1460 Assert::Equal<BOOL>(TRUE, pPlan->fCanAffectMachineState);
1378 Assert::Equal<BOOL>(TRUE, pPlan->fDisableRollback); 1461 Assert::Equal<BOOL>(TRUE, pPlan->fDisableRollback);
1379 Assert::Equal<BOOL>(FALSE, pPlan->fDisallowRemoval); 1462 Assert::Equal<BOOL>(FALSE, pPlan->fDisallowRemoval);
1463 Assert::Equal<BOOL>(FALSE, pPlan->fDowngrade);
1380 1464
1381 BOOL fRollback = FALSE; 1465 BOOL fRollback = FALSE;
1382 DWORD dwIndex = 0; 1466 DWORD dwIndex = 0;
@@ -1471,6 +1555,7 @@ namespace Bootstrapper
1471 Assert::Equal<BOOL>(TRUE, pPlan->fCanAffectMachineState); 1555 Assert::Equal<BOOL>(TRUE, pPlan->fCanAffectMachineState);
1472 Assert::Equal<BOOL>(FALSE, pPlan->fDisableRollback); 1556 Assert::Equal<BOOL>(FALSE, pPlan->fDisableRollback);
1473 Assert::Equal<BOOL>(FALSE, pPlan->fDisallowRemoval); 1557 Assert::Equal<BOOL>(FALSE, pPlan->fDisallowRemoval);
1558 Assert::Equal<BOOL>(FALSE, pPlan->fDowngrade);
1474 1559
1475 BOOL fRollback = FALSE; 1560 BOOL fRollback = FALSE;
1476 DWORD dwIndex = 0; 1561 DWORD dwIndex = 0;
@@ -1601,6 +1686,7 @@ namespace Bootstrapper
1601 Assert::Equal<BOOL>(TRUE, pPlan->fCanAffectMachineState); 1686 Assert::Equal<BOOL>(TRUE, pPlan->fCanAffectMachineState);
1602 Assert::Equal<BOOL>(FALSE, pPlan->fDisableRollback); 1687 Assert::Equal<BOOL>(FALSE, pPlan->fDisableRollback);
1603 Assert::Equal<BOOL>(FALSE, pPlan->fDisallowRemoval); 1688 Assert::Equal<BOOL>(FALSE, pPlan->fDisallowRemoval);
1689 Assert::Equal<BOOL>(FALSE, pPlan->fDowngrade);
1604 1690
1605 BOOL fRollback = FALSE; 1691 BOOL fRollback = FALSE;
1606 DWORD dwIndex = 0; 1692 DWORD dwIndex = 0;
@@ -1716,6 +1802,7 @@ namespace Bootstrapper
1716 Assert::Equal<BOOL>(TRUE, pPlan->fCanAffectMachineState); 1802 Assert::Equal<BOOL>(TRUE, pPlan->fCanAffectMachineState);
1717 Assert::Equal<BOOL>(FALSE, pPlan->fDisableRollback); 1803 Assert::Equal<BOOL>(FALSE, pPlan->fDisableRollback);
1718 Assert::Equal<BOOL>(FALSE, pPlan->fDisallowRemoval); 1804 Assert::Equal<BOOL>(FALSE, pPlan->fDisallowRemoval);
1805 Assert::Equal<BOOL>(FALSE, pPlan->fDowngrade);
1719 1806
1720 BOOL fRollback = FALSE; 1807 BOOL fRollback = FALSE;
1721 DWORD dwIndex = 0; 1808 DWORD dwIndex = 0;
@@ -1810,6 +1897,7 @@ namespace Bootstrapper
1810 Assert::Equal<BOOL>(TRUE, pPlan->fCanAffectMachineState); 1897 Assert::Equal<BOOL>(TRUE, pPlan->fCanAffectMachineState);
1811 Assert::Equal<BOOL>(FALSE, pPlan->fDisableRollback); 1898 Assert::Equal<BOOL>(FALSE, pPlan->fDisableRollback);
1812 Assert::Equal<BOOL>(FALSE, pPlan->fDisallowRemoval); 1899 Assert::Equal<BOOL>(FALSE, pPlan->fDisallowRemoval);
1900 Assert::Equal<BOOL>(FALSE, pPlan->fDowngrade);
1813 1901
1814 BOOL fRollback = FALSE; 1902 BOOL fRollback = FALSE;
1815 DWORD dwIndex = 0; 1903 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
254 ) 254 )
255 { 255 {
256 BAL_INFO_PACKAGE* pPackage = NULL; 256 BAL_INFO_PACKAGE* pPackage = NULL;
257 int nCompare = 0;
258 257
259 if (!fMissingFromCache) 258 if (!fMissingFromCache)
260 { 259 {
@@ -262,14 +261,6 @@ public: // IBootstrapperApplication
262 { 261 {
263 InitializePackageInfoForPackage(pPackage); 262 InitializePackageInfoForPackage(pPackage);
264 } 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 }
273 } 264 }
274 265
275 return CBalBaseBootstrapperApplication::OnDetectRelatedBundle(wzBundleId, relationType, wzBundleTag, fPerMachine, wzVersion, fMissingFromCache, pfCancel); 266 return CBalBaseBootstrapperApplication::OnDetectRelatedBundle(wzBundleId, relationType, wzBundleTag, fPerMachine, wzVersion, fMissingFromCache, pfCancel);
@@ -345,23 +336,6 @@ public: // IBootstrapperApplication
345 } 336 }
346 } 337 }
347 } 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 }
365 } 339 }
366 340
367 SetState(WIXSTDBA_STATE_DETECTED, hrStatus); 341 SetState(WIXSTDBA_STATE_DETECTED, hrStatus);
@@ -371,7 +345,6 @@ public: // IBootstrapperApplication
371 ::PostMessageW(m_hWnd, WM_WIXSTDBA_PLAN_PACKAGES, 0, m_command.action); 345 ::PostMessageW(m_hWnd, WM_WIXSTDBA_PLAN_PACKAGES, 0, m_command.action);
372 } 346 }
373 347
374 LExit:
375 return hr; 348 return hr;
376 } 349 }
377 350
@@ -1092,6 +1065,20 @@ public: // IBootstrapperApplication
1092 return hr; 1065 return hr;
1093 } 1066 }
1094 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 }
1095 1082
1096 virtual STDMETHODIMP OnApplyComplete( 1083 virtual STDMETHODIMP OnApplyComplete(
1097 __in HRESULT hrStatus, 1084 __in HRESULT hrStatus,
@@ -1445,6 +1432,9 @@ public: // IBootstrapperApplication
1445 case BOOTSTRAPPER_APPLICATION_MESSAGE_ONPLANRELATEDBUNDLETYPE: 1432 case BOOTSTRAPPER_APPLICATION_MESSAGE_ONPLANRELATEDBUNDLETYPE:
1446 OnPlanRelatedBundleTypeFallback(reinterpret_cast<BA_ONPLANRELATEDBUNDLETYPE_ARGS*>(pvArgs), reinterpret_cast<BA_ONPLANRELATEDBUNDLETYPE_RESULTS*>(pvResults)); 1433 OnPlanRelatedBundleTypeFallback(reinterpret_cast<BA_ONPLANRELATEDBUNDLETYPE_ARGS*>(pvArgs), reinterpret_cast<BA_ONPLANRELATEDBUNDLETYPE_RESULTS*>(pvResults));
1447 break; 1434 break;
1435 case BOOTSTRAPPER_APPLICATION_MESSAGE_ONAPPLYDOWNGRADE:
1436 OnApplyDowngradeFallback(reinterpret_cast<BA_ONAPPLYDOWNGRADE_ARGS*>(pvArgs), reinterpret_cast<BA_ONAPPLYDOWNGRADE_RESULTS*>(pvResults));
1437 break;
1448 default: 1438 default:
1449#ifdef DEBUG 1439#ifdef DEBUG
1450 BalLog(BOOTSTRAPPER_LOG_LEVEL_STANDARD, "WIXSTDBA: Forwarding unknown BA message: %d", message); 1440 BalLog(BOOTSTRAPPER_LOG_LEVEL_STANDARD, "WIXSTDBA: Forwarding unknown BA message: %d", message);
@@ -2118,6 +2108,14 @@ private: // privates
2118 BalLogId(BOOTSTRAPPER_LOG_LEVEL_STANDARD, MSG_WIXSTDBA_PLANNED_RESTORE_RELATED_BUNDLE, m_hModule, pArgs->wzBundleId, LoggingRequestStateToString(requestedState), LoggingRequestStateToString(pResults->requestedState)); 2108 BalLogId(BOOTSTRAPPER_LOG_LEVEL_STANDARD, MSG_WIXSTDBA_PLANNED_RESTORE_RELATED_BUNDLE, m_hModule, pArgs->wzBundleId, LoggingRequestStateToString(requestedState), LoggingRequestStateToString(pResults->requestedState));
2119 } 2109 }
2120 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
2121 2119
2122public: //CBalBaseBootstrapperApplication 2120public: //CBalBaseBootstrapperApplication
2123 virtual STDMETHODIMP Initialize( 2121 virtual STDMETHODIMP Initialize(
@@ -4195,7 +4193,7 @@ public:
4195 __in BOOL fPrereq, 4193 __in BOOL fPrereq,
4196 __in HRESULT hrHostInitialization, 4194 __in HRESULT hrHostInitialization,
4197 __in IBootstrapperEngine* pEngine 4195 __in IBootstrapperEngine* pEngine
4198 ) : CBalBaseBootstrapperApplication(pEngine, 3, 3000) 4196 ) : CBalBaseBootstrapperApplication(pEngine, 3, 3000)
4199 { 4197 {
4200 THEME_ASSIGN_CONTROL_ID* pAssignControl = NULL; 4198 THEME_ASSIGN_CONTROL_ID* pAssignControl = NULL;
4201 4199
@@ -4224,7 +4222,6 @@ public:
4224 m_state = WIXSTDBA_STATE_INITIALIZING; 4222 m_state = WIXSTDBA_STATE_INITIALIZING;
4225 m_hrFinal = hrHostInitialization; 4223 m_hrFinal = hrHostInitialization;
4226 4224
4227 m_fDowngrading = FALSE;
4228 m_restartResult = BOOTSTRAPPER_APPLY_RESTART_NONE; 4225 m_restartResult = BOOTSTRAPPER_APPLY_RESTART_NONE;
4229 m_fRestartRequired = FALSE; 4226 m_fRestartRequired = FALSE;
4230 m_fShouldRestart = FALSE; 4227 m_fShouldRestart = FALSE;
@@ -4529,7 +4526,6 @@ private:
4529 DWORD m_dwCalculatedCacheProgress; 4526 DWORD m_dwCalculatedCacheProgress;
4530 DWORD m_dwCalculatedExecuteProgress; 4527 DWORD m_dwCalculatedExecuteProgress;
4531 4528
4532 BOOL m_fDowngrading;
4533 BOOTSTRAPPER_APPLY_RESTART m_restartResult; 4529 BOOTSTRAPPER_APPLY_RESTART m_restartResult;
4534 BOOL m_fRestartRequired; 4530 BOOL m_fRestartRequired;
4535 BOOL m_fShouldRestart; 4531 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 @@
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
3namespace 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}