aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSean Hall <r.sean.hall@gmail.com>2022-02-10 18:09:34 -0600
committerSean Hall <r.sean.hall@gmail.com>2022-02-10 19:51:19 -0600
commit27a0db4070a2b5756282bf15b957dd7f0021417f (patch)
tree2d0cdfe80d5ccd6d207bdf664a4f8e512281c1cf
parent091573d459d6ab4947bd39bd3bc8faee3d18b4fc (diff)
downloadwix-27a0db4070a2b5756282bf15b957dd7f0021417f.tar.gz
wix-27a0db4070a2b5756282bf15b957dd7f0021417f.tar.bz2
wix-27a0db4070a2b5756282bf15b957dd7f0021417f.zip
When rolling back a bundle failure, reinstall all upgrade related bundles.
Fixes #3421
-rw-r--r--src/api/burn/WixToolset.BootstrapperCore.Native/inc/BootstrapperApplication.h15
-rw-r--r--src/api/burn/WixToolset.Mba.Core/BootstrapperApplication.cs26
-rw-r--r--src/api/burn/WixToolset.Mba.Core/EventArgs.cs31
-rw-r--r--src/api/burn/WixToolset.Mba.Core/IBootstrapperApplication.cs12
-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.h10
-rw-r--r--src/api/burn/balutil/inc/BalBaseBAFunctionsProc.h1
-rw-r--r--src/api/burn/balutil/inc/BalBaseBootstrapperApplication.h11
-rw-r--r--src/api/burn/balutil/inc/BalBaseBootstrapperApplicationProc.h12
-rw-r--r--src/api/burn/balutil/inc/IBootstrapperApplication.h8
-rw-r--r--src/burn/engine/apply.cpp50
-rw-r--r--src/burn/engine/bundlepackageengine.cpp2
-rw-r--r--src/burn/engine/core.cpp4
-rw-r--r--src/burn/engine/engine.mc9
-rw-r--r--src/burn/engine/package.h1
-rw-r--r--src/burn/engine/plan.cpp143
-rw-r--r--src/burn/engine/plan.h4
-rw-r--r--src/burn/engine/pseudobundle.cpp1
-rw-r--r--src/burn/engine/registration.h4
-rw-r--r--src/burn/engine/userexperience.cpp30
-rw-r--r--src/burn/engine/userexperience.h5
-rw-r--r--src/burn/test/BurnUnitTest/PlanTest.cpp64
-rw-r--r--src/ext/Bal/wixstdba/WixStandardBootstrapperApplication.cpp10
-rw-r--r--src/ext/Bal/wixstdba/wixstdba.mc7
-rw-r--r--src/test/burn/TestData/UpgradeRelatedBundleTests/BundleAv3/BundleAv3.wixproj16
-rw-r--r--src/test/burn/TestData/UpgradeRelatedBundleTests/BundleAv3/BundleAv3.wxs11
-rw-r--r--src/test/burn/TestData/UpgradeRelatedBundleTests/PackageAv3/PackageAv3.wixproj7
-rw-r--r--src/test/burn/TestData/UpgradeRelatedBundleTests/PackageF/PackageF.wixproj12
-rw-r--r--src/test/burn/WixToolsetTest.BurnE2E/DependencyTests.cs4
-rw-r--r--src/test/burn/WixToolsetTest.BurnE2E/UpgradeRelatedBundleTests.cs27
31 files changed, 515 insertions, 28 deletions
diff --git a/src/api/burn/WixToolset.BootstrapperCore.Native/inc/BootstrapperApplication.h b/src/api/burn/WixToolset.BootstrapperCore.Native/inc/BootstrapperApplication.h
index 659901be..5c6258d0 100644
--- a/src/api/burn/WixToolset.BootstrapperCore.Native/inc/BootstrapperApplication.h
+++ b/src/api/burn/WixToolset.BootstrapperCore.Native/inc/BootstrapperApplication.h
@@ -209,6 +209,7 @@ enum BOOTSTRAPPER_APPLICATION_MESSAGE
209 BOOTSTRAPPER_APPLICATION_MESSAGE_ONPLANCOMPATIBLEMSIPACKAGEBEGIN, 209 BOOTSTRAPPER_APPLICATION_MESSAGE_ONPLANCOMPATIBLEMSIPACKAGEBEGIN,
210 BOOTSTRAPPER_APPLICATION_MESSAGE_ONPLANCOMPATIBLEMSIPACKAGECOMPLETE, 210 BOOTSTRAPPER_APPLICATION_MESSAGE_ONPLANCOMPATIBLEMSIPACKAGECOMPLETE,
211 BOOTSTRAPPER_APPLICATION_MESSAGE_ONPLANNEDCOMPATIBLEPACKAGE, 211 BOOTSTRAPPER_APPLICATION_MESSAGE_ONPLANNEDCOMPATIBLEPACKAGE,
212 BOOTSTRAPPER_APPLICATION_MESSAGE_ONPLANRESTORERELATEDBUNDLE,
212}; 213};
213 214
214enum BOOTSTRAPPER_APPLYCOMPLETE_ACTION 215enum BOOTSTRAPPER_APPLYCOMPLETE_ACTION
@@ -1208,6 +1209,20 @@ struct BA_ONPLANRELATEDBUNDLE_RESULTS
1208 BOOTSTRAPPER_REQUEST_STATE requestedState; 1209 BOOTSTRAPPER_REQUEST_STATE requestedState;
1209}; 1210};
1210 1211
1212struct BA_ONPLANRESTORERELATEDBUNDLE_ARGS
1213{
1214 DWORD cbSize;
1215 LPCWSTR wzBundleId;
1216 BOOTSTRAPPER_REQUEST_STATE recommendedState;
1217};
1218
1219struct BA_ONPLANRESTORERELATEDBUNDLE_RESULTS
1220{
1221 DWORD cbSize;
1222 BOOL fCancel;
1223 BOOTSTRAPPER_REQUEST_STATE requestedState;
1224};
1225
1211struct BA_ONPLANROLLBACKBOUNDARY_ARGS 1226struct BA_ONPLANROLLBACKBOUNDARY_ARGS
1212{ 1227{
1213 DWORD cbSize; 1228 DWORD cbSize;
diff --git a/src/api/burn/WixToolset.Mba.Core/BootstrapperApplication.cs b/src/api/burn/WixToolset.Mba.Core/BootstrapperApplication.cs
index f277425e..b08e66c0 100644
--- a/src/api/burn/WixToolset.Mba.Core/BootstrapperApplication.cs
+++ b/src/api/burn/WixToolset.Mba.Core/BootstrapperApplication.cs
@@ -271,6 +271,9 @@ namespace WixToolset.Mba.Core
271 /// <inheritdoc/> 271 /// <inheritdoc/>
272 public event EventHandler<SetUpdateCompleteEventArgs> SetUpdateComplete; 272 public event EventHandler<SetUpdateCompleteEventArgs> SetUpdateComplete;
273 273
274 /// <inheritdoc/>
275 public event EventHandler<PlanRestoreRelatedBundleEventArgs> PlanRestoreRelatedBundle;
276
274 /// <summary> 277 /// <summary>
275 /// Entry point that is called when the bootstrapper application is ready to run. 278 /// Entry point that is called when the bootstrapper application is ready to run.
276 /// </summary> 279 /// </summary>
@@ -1321,6 +1324,19 @@ namespace WixToolset.Mba.Core
1321 } 1324 }
1322 } 1325 }
1323 1326
1327 /// <summary>
1328 /// Called by the engine, raises the <see cref="PlanRestoreRelatedBundle"/> event.
1329 /// </summary>
1330 /// <param name="args">Additional arguments for this event.</param>
1331 protected virtual void OnPlanRestoreRelatedBundle(PlanRestoreRelatedBundleEventArgs args)
1332 {
1333 EventHandler<PlanRestoreRelatedBundleEventArgs> handler = this.PlanRestoreRelatedBundle;
1334 if (null != handler)
1335 {
1336 handler(this, args);
1337 }
1338 }
1339
1324 #region IBootstrapperApplication Members 1340 #region IBootstrapperApplication Members
1325 1341
1326 int IBootstrapperApplication.BAProc(int message, IntPtr pvArgs, IntPtr pvResults, IntPtr pvContext) 1342 int IBootstrapperApplication.BAProc(int message, IntPtr pvArgs, IntPtr pvResults, IntPtr pvContext)
@@ -2042,6 +2058,16 @@ namespace WixToolset.Mba.Core
2042 return args.HResult; 2058 return args.HResult;
2043 } 2059 }
2044 2060
2061 int IBootstrapperApplication.OnPlanRestoreRelatedBundle(string wzBundleId, RequestState recommendedState, ref RequestState pRequestedState, ref bool fCancel)
2062 {
2063 PlanRestoreRelatedBundleEventArgs args = new PlanRestoreRelatedBundleEventArgs(wzBundleId, recommendedState, pRequestedState, fCancel);
2064 this.OnPlanRestoreRelatedBundle(args);
2065
2066 pRequestedState = args.State;
2067 fCancel = args.Cancel;
2068 return args.HResult;
2069 }
2070
2045 #endregion 2071 #endregion
2046 } 2072 }
2047} 2073}
diff --git a/src/api/burn/WixToolset.Mba.Core/EventArgs.cs b/src/api/burn/WixToolset.Mba.Core/EventArgs.cs
index d4d70651..2e1e1be3 100644
--- a/src/api/burn/WixToolset.Mba.Core/EventArgs.cs
+++ b/src/api/burn/WixToolset.Mba.Core/EventArgs.cs
@@ -2402,4 +2402,35 @@ namespace WixToolset.Mba.Core
2402 /// </summary> 2402 /// </summary>
2403 public string NewPackageId { get; private set; } 2403 public string NewPackageId { get; private set; }
2404 } 2404 }
2405
2406 /// <summary>
2407 /// Event arguments for <see cref="IDefaultBootstrapperApplication.PlanRestoreRelatedBundle"/>
2408 /// </summary>
2409 [Serializable]
2410 public class PlanRestoreRelatedBundleEventArgs : CancellableHResultEventArgs
2411 {
2412 /// <summary />
2413 public PlanRestoreRelatedBundleEventArgs(string bundleId, RequestState recommendedState, RequestState state, bool cancelRecommendation)
2414 : base(cancelRecommendation)
2415 {
2416 this.BundleId = bundleId;
2417 this.RecommendedState = recommendedState;
2418 this.State = state;
2419 }
2420
2421 /// <summary>
2422 /// Gets the identity of the bundle to plan for.
2423 /// </summary>
2424 public string BundleId { get; private set; }
2425
2426 /// <summary>
2427 /// Gets the recommended requested state for the bundle.
2428 /// </summary>
2429 public RequestState RecommendedState { get; private set; }
2430
2431 /// <summary>
2432 /// Gets or sets the requested state for the bundle.
2433 /// </summary>
2434 public RequestState State { get; set; }
2435 }
2405} 2436}
diff --git a/src/api/burn/WixToolset.Mba.Core/IBootstrapperApplication.cs b/src/api/burn/WixToolset.Mba.Core/IBootstrapperApplication.cs
index 05f96106..4fbe5e18 100644
--- a/src/api/burn/WixToolset.Mba.Core/IBootstrapperApplication.cs
+++ b/src/api/burn/WixToolset.Mba.Core/IBootstrapperApplication.cs
@@ -1136,6 +1136,18 @@ namespace WixToolset.Mba.Core
1136 [MarshalAs(UnmanagedType.LPWStr)] string wzPreviousPackageId, 1136 [MarshalAs(UnmanagedType.LPWStr)] string wzPreviousPackageId,
1137 [MarshalAs(UnmanagedType.LPWStr)] string wzNewPackageId 1137 [MarshalAs(UnmanagedType.LPWStr)] string wzNewPackageId
1138 ); 1138 );
1139
1140 /// <summary>
1141 /// See <see cref="IDefaultBootstrapperApplication.PlanRestoreRelatedBundle"/>.
1142 /// </summary>
1143 [PreserveSig]
1144 [return: MarshalAs(UnmanagedType.I4)]
1145 int OnPlanRestoreRelatedBundle(
1146 [MarshalAs(UnmanagedType.LPWStr)] string wzBundleId,
1147 [MarshalAs(UnmanagedType.U4)] RequestState recommendedState,
1148 [MarshalAs(UnmanagedType.U4)] ref RequestState pRequestedState,
1149 [MarshalAs(UnmanagedType.Bool)] ref bool fCancel
1150 );
1139 } 1151 }
1140 1152
1141 /// <summary> 1153 /// <summary>
diff --git a/src/api/burn/WixToolset.Mba.Core/IDefaultBootstrapperApplication.cs b/src/api/burn/WixToolset.Mba.Core/IDefaultBootstrapperApplication.cs
index ce06408e..c237cb9d 100644
--- a/src/api/burn/WixToolset.Mba.Core/IDefaultBootstrapperApplication.cs
+++ b/src/api/burn/WixToolset.Mba.Core/IDefaultBootstrapperApplication.cs
@@ -334,6 +334,11 @@ namespace WixToolset.Mba.Core
334 event EventHandler<PlanRelatedBundleEventArgs> PlanRelatedBundle; 334 event EventHandler<PlanRelatedBundleEventArgs> PlanRelatedBundle;
335 335
336 /// <summary> 336 /// <summary>
337 /// Fired when the engine has begun planning an upgrade related bundle for restoring in case of failure.
338 /// </summary>
339 event EventHandler<PlanRestoreRelatedBundleEventArgs> PlanRestoreRelatedBundle;
340
341 /// <summary>
337 /// Fired when the engine is planning a rollback boundary. 342 /// Fired when the engine is planning a rollback boundary.
338 /// </summary> 343 /// </summary>
339 event EventHandler<PlanRollbackBoundaryEventArgs> PlanRollbackBoundary; 344 event EventHandler<PlanRollbackBoundaryEventArgs> PlanRollbackBoundary;
diff --git a/src/api/burn/balutil/inc/BAFunctions.h b/src/api/burn/balutil/inc/BAFunctions.h
index 84359d65..cbef88df 100644
--- a/src/api/burn/balutil/inc/BAFunctions.h
+++ b/src/api/burn/balutil/inc/BAFunctions.h
@@ -88,6 +88,7 @@ enum BA_FUNCTIONS_MESSAGE
88 BA_FUNCTIONS_MESSAGE_ONPLANCOMPATIBLEMSIPACKAGEBEGIN = BOOTSTRAPPER_APPLICATION_MESSAGE_ONPLANCOMPATIBLEMSIPACKAGEBEGIN, 88 BA_FUNCTIONS_MESSAGE_ONPLANCOMPATIBLEMSIPACKAGEBEGIN = BOOTSTRAPPER_APPLICATION_MESSAGE_ONPLANCOMPATIBLEMSIPACKAGEBEGIN,
89 BA_FUNCTIONS_MESSAGE_ONPLANCOMPATIBLEMSIPACKAGECOMPLETE = BOOTSTRAPPER_APPLICATION_MESSAGE_ONPLANCOMPATIBLEMSIPACKAGECOMPLETE, 89 BA_FUNCTIONS_MESSAGE_ONPLANCOMPATIBLEMSIPACKAGECOMPLETE = BOOTSTRAPPER_APPLICATION_MESSAGE_ONPLANCOMPATIBLEMSIPACKAGECOMPLETE,
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 92
92 BA_FUNCTIONS_MESSAGE_ONTHEMELOADED = 1024, 93 BA_FUNCTIONS_MESSAGE_ONTHEMELOADED = 1024,
93 BA_FUNCTIONS_MESSAGE_WNDPROC, 94 BA_FUNCTIONS_MESSAGE_WNDPROC,
diff --git a/src/api/burn/balutil/inc/BalBaseBAFunctions.h b/src/api/burn/balutil/inc/BalBaseBAFunctions.h
index c6d0924f..e98ebc9f 100644
--- a/src/api/burn/balutil/inc/BalBaseBAFunctions.h
+++ b/src/api/burn/balutil/inc/BalBaseBAFunctions.h
@@ -849,6 +849,16 @@ public: // IBootstrapperApplication
849 return S_OK; 849 return S_OK;
850 } 850 }
851 851
852 virtual STDMETHODIMP OnPlanRestoreRelatedBundle(
853 __in_z LPCWSTR /*wzBundleId*/,
854 __in BOOTSTRAPPER_REQUEST_STATE /*recommendedState*/,
855 __inout BOOTSTRAPPER_REQUEST_STATE* /*pRequestedState*/,
856 __inout BOOL* /*pfCancel*/
857 )
858 {
859 return S_OK;
860 }
861
852public: // IBAFunctions 862public: // IBAFunctions
853 virtual STDMETHODIMP OnPlan( 863 virtual STDMETHODIMP OnPlan(
854 ) 864 )
diff --git a/src/api/burn/balutil/inc/BalBaseBAFunctionsProc.h b/src/api/burn/balutil/inc/BalBaseBAFunctionsProc.h
index 5d5ff098..09cc189e 100644
--- a/src/api/burn/balutil/inc/BalBaseBAFunctionsProc.h
+++ b/src/api/burn/balutil/inc/BalBaseBAFunctionsProc.h
@@ -159,6 +159,7 @@ static HRESULT WINAPI BalBaseBAFunctionsProc(
159 case BA_FUNCTIONS_MESSAGE_ONPLANCOMPATIBLEMSIPACKAGEBEGIN: 159 case BA_FUNCTIONS_MESSAGE_ONPLANCOMPATIBLEMSIPACKAGEBEGIN:
160 case BA_FUNCTIONS_MESSAGE_ONPLANCOMPATIBLEMSIPACKAGECOMPLETE: 160 case BA_FUNCTIONS_MESSAGE_ONPLANCOMPATIBLEMSIPACKAGECOMPLETE:
161 case BA_FUNCTIONS_MESSAGE_ONPLANNEDCOMPATIBLEPACKAGE: 161 case BA_FUNCTIONS_MESSAGE_ONPLANNEDCOMPATIBLEPACKAGE:
162 case BA_FUNCTIONS_MESSAGE_ONPLANRESTORERELATEDBUNDLE:
162 hr = BalBaseBootstrapperApplicationProc((BOOTSTRAPPER_APPLICATION_MESSAGE)message, pvArgs, pvResults, pvContext); 163 hr = BalBaseBootstrapperApplicationProc((BOOTSTRAPPER_APPLICATION_MESSAGE)message, pvArgs, pvResults, pvContext);
163 break; 164 break;
164 case BA_FUNCTIONS_MESSAGE_ONTHEMELOADED: 165 case BA_FUNCTIONS_MESSAGE_ONTHEMELOADED:
diff --git a/src/api/burn/balutil/inc/BalBaseBootstrapperApplication.h b/src/api/burn/balutil/inc/BalBaseBootstrapperApplication.h
index e1a36fdf..6a24f24b 100644
--- a/src/api/burn/balutil/inc/BalBaseBootstrapperApplication.h
+++ b/src/api/burn/balutil/inc/BalBaseBootstrapperApplication.h
@@ -1047,6 +1047,17 @@ public: // IBootstrapperApplication
1047 return S_OK; 1047 return S_OK;
1048 } 1048 }
1049 1049
1050 virtual STDMETHODIMP OnPlanRestoreRelatedBundle(
1051 __in_z LPCWSTR /*wzBundleId*/,
1052 __in BOOTSTRAPPER_REQUEST_STATE /*recommendedState*/,
1053 __inout BOOTSTRAPPER_REQUEST_STATE* /*pRequestedState*/,
1054 __inout BOOL* pfCancel
1055 )
1056 {
1057 *pfCancel |= CheckCanceled();
1058 return S_OK;
1059 }
1060
1050public: //CBalBaseBootstrapperApplication 1061public: //CBalBaseBootstrapperApplication
1051 virtual STDMETHODIMP Initialize( 1062 virtual STDMETHODIMP Initialize(
1052 __in const BOOTSTRAPPER_CREATE_ARGS* pCreateArgs 1063 __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 1ee5258e..d40390e5 100644
--- a/src/api/burn/balutil/inc/BalBaseBootstrapperApplicationProc.h
+++ b/src/api/burn/balutil/inc/BalBaseBootstrapperApplicationProc.h
@@ -720,6 +720,15 @@ static HRESULT BalBaseBAProcOnSetUpdateComplete(
720 return pBA->OnSetUpdateComplete(pArgs->hrStatus, pArgs->wzPreviousPackageId, pArgs->wzNewPackageId); 720 return pBA->OnSetUpdateComplete(pArgs->hrStatus, pArgs->wzPreviousPackageId, pArgs->wzNewPackageId);
721} 721}
722 722
723static HRESULT BalBaseBAProcOnPlanRestoreRelatedBundle(
724 __in IBootstrapperApplication* pBA,
725 __in BA_ONPLANRESTORERELATEDBUNDLE_ARGS* pArgs,
726 __inout BA_ONPLANRESTORERELATEDBUNDLE_RESULTS* pResults
727 )
728{
729 return pBA->OnPlanRestoreRelatedBundle(pArgs->wzBundleId, pArgs->recommendedState, &pResults->requestedState, &pResults->fCancel);
730}
731
723/******************************************************************* 732/*******************************************************************
724BalBaseBootstrapperApplicationProc - requires pvContext to be of type IBootstrapperApplication. 733BalBaseBootstrapperApplicationProc - requires pvContext to be of type IBootstrapperApplication.
725 Provides a default mapping between the new message based BA interface and 734 Provides a default mapping between the new message based BA interface and
@@ -976,6 +985,9 @@ static HRESULT WINAPI BalBaseBootstrapperApplicationProc(
976 case BOOTSTRAPPER_APPLICATION_MESSAGE_ONPLANNEDCOMPATIBLEPACKAGE: 985 case BOOTSTRAPPER_APPLICATION_MESSAGE_ONPLANNEDCOMPATIBLEPACKAGE:
977 hr = BalBaseBAProcOnPlannedCompatiblePackage(pBA, reinterpret_cast<BA_ONPLANNEDCOMPATIBLEPACKAGE_ARGS*>(pvArgs), reinterpret_cast<BA_ONPLANNEDCOMPATIBLEPACKAGE_RESULTS*>(pvResults)); 986 hr = BalBaseBAProcOnPlannedCompatiblePackage(pBA, reinterpret_cast<BA_ONPLANNEDCOMPATIBLEPACKAGE_ARGS*>(pvArgs), reinterpret_cast<BA_ONPLANNEDCOMPATIBLEPACKAGE_RESULTS*>(pvResults));
978 break; 987 break;
988 case BOOTSTRAPPER_APPLICATION_MESSAGE_ONPLANRESTORERELATEDBUNDLE:
989 hr = BalBaseBAProcOnPlanRestoreRelatedBundle(pBA, reinterpret_cast<BA_ONPLANRESTORERELATEDBUNDLE_ARGS*>(pvArgs), reinterpret_cast<BA_ONPLANRESTORERELATEDBUNDLE_RESULTS*>(pvResults));
990 break;
979 } 991 }
980 } 992 }
981 993
diff --git a/src/api/burn/balutil/inc/IBootstrapperApplication.h b/src/api/burn/balutil/inc/IBootstrapperApplication.h
index 640f609d..5932c06e 100644
--- a/src/api/burn/balutil/inc/IBootstrapperApplication.h
+++ b/src/api/burn/balutil/inc/IBootstrapperApplication.h
@@ -691,4 +691,12 @@ DECLARE_INTERFACE_IID_(IBootstrapperApplication, IUnknown, "53C31D56-49C0-426B-A
691 __in_z_opt LPCWSTR wzPreviousPackageId, 691 __in_z_opt LPCWSTR wzPreviousPackageId,
692 __in_z_opt LPCWSTR wzNewPackageId 692 __in_z_opt LPCWSTR wzNewPackageId
693 ) = 0; 693 ) = 0;
694
695 // OnPlanRestoreRelatedBundle - called when the engine begins planning an upgrade related bundle for restoring in case of failure.
696 STDMETHOD(OnPlanRestoreRelatedBundle)(
697 __in_z LPCWSTR wzBundleId,
698 __in BOOTSTRAPPER_REQUEST_STATE recommendedState,
699 __inout BOOTSTRAPPER_REQUEST_STATE* pRequestedState,
700 __inout BOOL* pfCancel
701 ) = 0;
694}; 702};
diff --git a/src/burn/engine/apply.cpp b/src/burn/engine/apply.cpp
index 5cc63d02..73f8fc72 100644
--- a/src/burn/engine/apply.cpp
+++ b/src/burn/engine/apply.cpp
@@ -210,6 +210,11 @@ static HRESULT ExecuteRelatedBundle(
210 __out BOOL* pfSuspend, 210 __out BOOL* pfSuspend,
211 __out BOOTSTRAPPER_APPLY_RESTART* pRestart 211 __out BOOTSTRAPPER_APPLY_RESTART* pRestart
212 ); 212 );
213static HRESULT DoRestoreRelatedBundleActions(
214 __in BURN_ENGINE_STATE* pEngineState,
215 __in BURN_EXECUTE_CONTEXT* pContext,
216 __out BOOTSTRAPPER_APPLY_RESTART* pRestart
217 );
213static HRESULT ExecuteExePackage( 218static HRESULT ExecuteExePackage(
214 __in BURN_ENGINE_STATE* pEngineState, 219 __in BURN_ENGINE_STATE* pEngineState,
215 __in BURN_EXECUTE_ACTION* pExecuteAction, 220 __in BURN_EXECUTE_ACTION* pExecuteAction,
@@ -788,6 +793,9 @@ extern "C" HRESULT ApplyExecute(
788 { 793 {
789 if (pCheckpoint->pActiveRollbackBoundary->fVital) 794 if (pCheckpoint->pActiveRollbackBoundary->fVital)
790 { 795 {
796 hrRollback = DoRestoreRelatedBundleActions(pEngineState, &context, pRestart);
797 IgnoreRollbackError(hrRollback, "Failed rollback actions");
798
791 // If the rollback boundary is vital, end execution here. 799 // If the rollback boundary is vital, end execution here.
792 break; 800 break;
793 } 801 }
@@ -2590,6 +2598,48 @@ LExit:
2590 return hr; 2598 return hr;
2591} 2599}
2592 2600
2601static HRESULT DoRestoreRelatedBundleActions(
2602 __in BURN_ENGINE_STATE* pEngineState,
2603 __in BURN_EXECUTE_CONTEXT* pContext,
2604 __out BOOTSTRAPPER_APPLY_RESTART* pRestart
2605 )
2606{
2607 HRESULT hr = S_OK;
2608 BOOL fRetryIgnored = FALSE;
2609 BOOL fSuspendIgnored = FALSE;
2610
2611 // execute restore related bundle actions
2612 for (DWORD i = 0; i < pEngineState->plan.cRestoreRelatedBundleActions; ++i)
2613 {
2614 BURN_EXECUTE_ACTION* pRestoreRelatedBundleAction = &pEngineState->plan.rgRestoreRelatedBundleActions[i];
2615 if (pRestoreRelatedBundleAction->fDeleted)
2616 {
2617 continue;
2618 }
2619
2620 BOOTSTRAPPER_APPLY_RESTART restart = BOOTSTRAPPER_APPLY_RESTART_NONE;
2621 switch (pRestoreRelatedBundleAction->type)
2622 {
2623 case BURN_EXECUTE_ACTION_TYPE_RELATED_BUNDLE:
2624 hr = ExecuteRelatedBundle(pEngineState, pRestoreRelatedBundleAction, pContext, TRUE, &fRetryIgnored, &fSuspendIgnored, &restart);
2625 IgnoreRollbackError(hr, "Failed to restore related bundle package.");
2626 break;
2627
2628 default:
2629 hr = E_UNEXPECTED;
2630 ExitOnFailure(hr, "Invalid restore related bundle action: %d.", pRestoreRelatedBundleAction->type);
2631 }
2632
2633 if (*pRestart < restart)
2634 {
2635 *pRestart = restart;
2636 }
2637 }
2638
2639LExit:
2640 return hr;
2641}
2642
2593static HRESULT ExecuteExePackage( 2643static HRESULT ExecuteExePackage(
2594 __in BURN_ENGINE_STATE* pEngineState, 2644 __in BURN_ENGINE_STATE* pEngineState,
2595 __in BURN_EXECUTE_ACTION* pExecuteAction, 2645 __in BURN_EXECUTE_ACTION* pExecuteAction,
diff --git a/src/burn/engine/bundlepackageengine.cpp b/src/burn/engine/bundlepackageengine.cpp
index cd84601f..b5fc51e5 100644
--- a/src/burn/engine/bundlepackageengine.cpp
+++ b/src/burn/engine/bundlepackageengine.cpp
@@ -51,7 +51,7 @@ extern "C" HRESULT BundlePackageEnginePlanCalculatePackage(
51 execute = BOOTSTRAPPER_ACTION_STATE_NONE; 51 execute = BOOTSTRAPPER_ACTION_STATE_NONE;
52 break; 52 break;
53 case BOOTSTRAPPER_REQUEST_STATE_REPAIR: 53 case BOOTSTRAPPER_REQUEST_STATE_REPAIR:
54 execute = pPackage->Bundle.fRepairable ? BOOTSTRAPPER_ACTION_STATE_REPAIR : BOOTSTRAPPER_ACTION_STATE_NONE; 54 execute = BOOTSTRAPPER_ACTION_STATE_REPAIR;
55 break; 55 break;
56 case BOOTSTRAPPER_REQUEST_STATE_ABSENT: __fallthrough; 56 case BOOTSTRAPPER_REQUEST_STATE_ABSENT: __fallthrough;
57 case BOOTSTRAPPER_REQUEST_STATE_CACHE: 57 case BOOTSTRAPPER_REQUEST_STATE_CACHE:
diff --git a/src/burn/engine/core.cpp b/src/burn/engine/core.cpp
index 7f7a915e..8fac7bd0 100644
--- a/src/burn/engine/core.cpp
+++ b/src/burn/engine/core.cpp
@@ -558,7 +558,7 @@ extern "C" HRESULT CorePlan(
558 ExitOnFailure(hr, "Failed to plan packages."); 558 ExitOnFailure(hr, "Failed to plan packages.");
559 559
560 // Schedule the update of related bundles last. 560 // Schedule the update of related bundles last.
561 hr = PlanRelatedBundlesComplete(&pEngineState->registration, &pEngineState->plan, &pEngineState->log, &pEngineState->variables, dwExecuteActionEarlyIndex); 561 hr = PlanRelatedBundlesComplete(&pEngineState->userExperience, &pEngineState->registration, &pEngineState->plan, &pEngineState->log, &pEngineState->variables, dwExecuteActionEarlyIndex);
562 ExitOnFailure(hr, "Failed to schedule related bundles."); 562 ExitOnFailure(hr, "Failed to schedule related bundles.");
563 } 563 }
564 } 564 }
@@ -2309,7 +2309,7 @@ static void LogRelatedBundles(
2309 2309
2310 if (pRelatedBundle->fPlannable) 2310 if (pRelatedBundle->fPlannable)
2311 { 2311 {
2312 LogId(REPORT_STANDARD, MSG_PLANNED_RELATED_BUNDLE, pPackage->sczId, LoggingRelationTypeToString(pRelatedBundle->relationType), LoggingRequestStateToString(pPackage->defaultRequested), LoggingRequestStateToString(pPackage->requested), LoggingActionStateToString(pPackage->execute), LoggingActionStateToString(pPackage->rollback), LoggingDependencyActionToString(pPackage->dependencyExecute)); 2312 LogId(REPORT_STANDARD, MSG_PLANNED_RELATED_BUNDLE, pPackage->sczId, LoggingRelationTypeToString(pRelatedBundle->relationType), LoggingRequestStateToString(pPackage->defaultRequested), LoggingRequestStateToString(pPackage->requested), LoggingActionStateToString(pPackage->execute), LoggingActionStateToString(pPackage->rollback), LoggingRequestStateToString(pRelatedBundle->defaultRequestedRestore), LoggingRequestStateToString(pRelatedBundle->requestedRestore), LoggingActionStateToString(pRelatedBundle->restore), LoggingDependencyActionToString(pPackage->dependencyExecute));
2313 } 2313 }
2314 } 2314 }
2315 } 2315 }
diff --git a/src/burn/engine/engine.mc b/src/burn/engine/engine.mc
index b8c9a2d3..675a5644 100644
--- a/src/burn/engine/engine.mc
+++ b/src/burn/engine/engine.mc
@@ -352,13 +352,6 @@ Language=English
352Planned package: %1!ls!, state: %2!hs!, default requested: %3!hs!, ba requested: %4!hs!, execute: %5!hs!, rollback: %6!hs!, default cache strategy: %7!hs!, ba requested strategy: %8!hs!, cache: %9!hs!, uncache: %10!hs!, dependency: %11!hs!, expected install registration state: %12!hs!, expected cache registration state: %13!hs! 352Planned package: %1!ls!, state: %2!hs!, default requested: %3!hs!, ba requested: %4!hs!, execute: %5!hs!, rollback: %6!hs!, default cache strategy: %7!hs!, ba requested strategy: %8!hs!, cache: %9!hs!, uncache: %10!hs!, dependency: %11!hs!, expected install registration state: %12!hs!, expected cache registration state: %13!hs!
353. 353.
354 354
355MessageId=202
356Severity=Success
357SymbolicName=MSG_PLANNED_BUNDLE_UX_CHANGED_REQUEST
358Language=English
359Planned bundle: %1!ls!, ba requested state: %2!hs! over default: %3!hs!
360.
361
362MessageId=203 355MessageId=203
363Severity=Success 356Severity=Success
364SymbolicName=MSG_PLANNED_MSI_FEATURE 357SymbolicName=MSG_PLANNED_MSI_FEATURE
@@ -391,7 +384,7 @@ MessageId=207
391Severity=Success 384Severity=Success
392SymbolicName=MSG_PLANNED_RELATED_BUNDLE 385SymbolicName=MSG_PLANNED_RELATED_BUNDLE
393Language=English 386Language=English
394Planned related bundle: %1!ls!, type: %2!hs!, default requested: %3!hs!, ba requested: %4!hs!, execute: %5!hs!, rollback: %6!hs!, dependency: %7!hs! 387Planned related bundle: %1!ls!, type: %2!hs!, default requested: %3!hs!, ba requested: %4!hs!, execute: %5!hs!, rollback: %6!hs!, default requested restore: %7!hs!, ba requested restore: %8!hs!, restore: %9!hs!, dependency: %10!hs!
395. 388.
396 389
397MessageId=208 390MessageId=208
diff --git a/src/burn/engine/package.h b/src/burn/engine/package.h
index 6d1b5dd9..1e61d92b 100644
--- a/src/burn/engine/package.h
+++ b/src/burn/engine/package.h
@@ -309,7 +309,6 @@ typedef struct _BURN_PACKAGE
309 LPCWSTR wzAncestors; // points directly into engine state. 309 LPCWSTR wzAncestors; // points directly into engine state.
310 LPCWSTR wzEngineWorkingDirectory; // points directly into engine state. 310 LPCWSTR wzEngineWorkingDirectory; // points directly into engine state.
311 311
312 BOOL fRepairable;
313 BOOL fSupportsBurnProtocol; 312 BOOL fSupportsBurnProtocol;
314 313
315 BURN_EXE_EXIT_CODE* rgExitCodes; 314 BURN_EXE_EXIT_CODE* rgExitCodes;
diff --git a/src/burn/engine/plan.cpp b/src/burn/engine/plan.cpp
index f850d49c..f1fb87b8 100644
--- a/src/burn/engine/plan.cpp
+++ b/src/burn/engine/plan.cpp
@@ -103,6 +103,10 @@ static HRESULT AppendCleanAction(
103 __in BURN_PLAN* pPlan, 103 __in BURN_PLAN* pPlan,
104 __out BURN_CLEAN_ACTION** ppCleanAction 104 __out BURN_CLEAN_ACTION** ppCleanAction
105 ); 105 );
106static HRESULT AppendRestoreRelatedBundleAction(
107 __in BURN_PLAN* pPlan,
108 __out BURN_EXECUTE_ACTION** ppExecuteAction
109 );
106static HRESULT ProcessPayloadGroup( 110static HRESULT ProcessPayloadGroup(
107 __in BURN_PLAN* pPlan, 111 __in BURN_PLAN* pPlan,
108 __in BURN_PAYLOAD_GROUP* pPayloadGroup 112 __in BURN_PAYLOAD_GROUP* pPayloadGroup
@@ -196,6 +200,15 @@ extern "C" void PlanReset(
196 MemFree(pPlan->rgRollbackActions); 200 MemFree(pPlan->rgRollbackActions);
197 } 201 }
198 202
203 if (pPlan->rgRestoreRelatedBundleActions)
204 {
205 for (DWORD i = 0; i < pPlan->cRestoreRelatedBundleActions; ++i)
206 {
207 PlanUninitializeExecuteAction(&pPlan->rgRestoreRelatedBundleActions[i]);
208 }
209 MemFree(pPlan->rgRestoreRelatedBundleActions);
210 }
211
199 if (pPlan->rgCleanActions) 212 if (pPlan->rgCleanActions)
200 { 213 {
201 // Nothing needs to be freed inside clean actions today. 214 // Nothing needs to be freed inside clean actions today.
@@ -1276,6 +1289,9 @@ extern "C" HRESULT PlanRelatedBundlesBegin(
1276 continue; 1289 continue;
1277 } 1290 }
1278 1291
1292 pRelatedBundle->defaultRequestedRestore = BOOTSTRAPPER_REQUEST_STATE_NONE;
1293 pRelatedBundle->requestedRestore = BOOTSTRAPPER_REQUEST_STATE_NONE;
1294 pRelatedBundle->restore = BOOTSTRAPPER_ACTION_STATE_NONE;
1279 pRelatedBundle->package.defaultRequested = BOOTSTRAPPER_REQUEST_STATE_NONE; 1295 pRelatedBundle->package.defaultRequested = BOOTSTRAPPER_REQUEST_STATE_NONE;
1280 pRelatedBundle->package.requested = BOOTSTRAPPER_REQUEST_STATE_NONE; 1296 pRelatedBundle->package.requested = BOOTSTRAPPER_REQUEST_STATE_NONE;
1281 1297
@@ -1312,12 +1328,6 @@ extern "C" HRESULT PlanRelatedBundlesBegin(
1312 hr = UserExperienceOnPlanRelatedBundle(pUserExperience, pRelatedBundle->package.sczId, &pRelatedBundle->package.requested); 1328 hr = UserExperienceOnPlanRelatedBundle(pUserExperience, pRelatedBundle->package.sczId, &pRelatedBundle->package.requested);
1313 ExitOnRootFailure(hr, "BA aborted plan related bundle."); 1329 ExitOnRootFailure(hr, "BA aborted plan related bundle.");
1314 1330
1315 // Log when the BA changed the bundle state so the engine doesn't get blamed for planning the wrong thing.
1316 if (pRelatedBundle->package.requested != pRelatedBundle->package.defaultRequested)
1317 {
1318 LogId(REPORT_STANDARD, MSG_PLANNED_BUNDLE_UX_CHANGED_REQUEST, pRelatedBundle->package.sczId, LoggingRequestStateToString(pRelatedBundle->package.requested), LoggingRequestStateToString(pRelatedBundle->package.defaultRequested));
1319 }
1320
1321 // If uninstalling and the dependent related bundle may be executed, ignore its provider key to allow for downgrades with ref-counting. 1331 // If uninstalling and the dependent related bundle may be executed, ignore its provider key to allow for downgrades with ref-counting.
1322 if (BOOTSTRAPPER_ACTION_UNINSTALL == pPlan->action && BOOTSTRAPPER_RELATION_DEPENDENT == pRelatedBundle->relationType && BOOTSTRAPPER_REQUEST_STATE_NONE != pRelatedBundle->package.requested) 1332 if (BOOTSTRAPPER_ACTION_UNINSTALL == pPlan->action && BOOTSTRAPPER_RELATION_DEPENDENT == pRelatedBundle->relationType && BOOTSTRAPPER_REQUEST_STATE_NONE != pRelatedBundle->package.requested)
1323 { 1333 {
@@ -1340,6 +1350,7 @@ LExit:
1340} 1350}
1341 1351
1342extern "C" HRESULT PlanRelatedBundlesComplete( 1352extern "C" HRESULT PlanRelatedBundlesComplete(
1353 __in BURN_USER_EXPERIENCE* pUserExperience,
1343 __in BURN_REGISTRATION* pRegistration, 1354 __in BURN_REGISTRATION* pRegistration,
1344 __in BURN_PLAN* pPlan, 1355 __in BURN_PLAN* pPlan,
1345 __in BURN_LOGGING* pLog, 1356 __in BURN_LOGGING* pLog,
@@ -1359,16 +1370,19 @@ extern "C" HRESULT PlanRelatedBundlesComplete(
1359 ExitOnFailure(hr, "Failed to create dictionary for planned packages."); 1370 ExitOnFailure(hr, "Failed to create dictionary for planned packages.");
1360 1371
1361 BOOL fExecutingAnyPackage = FALSE; 1372 BOOL fExecutingAnyPackage = FALSE;
1373 BOOL fInstallingAnyPackage = FALSE;
1362 1374
1363 for (DWORD i = 0; i < pPlan->cExecuteActions; ++i) 1375 for (DWORD i = 0; i < pPlan->cExecuteActions; ++i)
1364 { 1376 {
1377 BOOTSTRAPPER_ACTION_STATE packageAction = BOOTSTRAPPER_ACTION_STATE_NONE;
1378
1365 switch (pPlan->rgExecuteActions[i].type) 1379 switch (pPlan->rgExecuteActions[i].type)
1366 { 1380 {
1367 case BURN_EXECUTE_ACTION_TYPE_RELATED_BUNDLE: 1381 case BURN_EXECUTE_ACTION_TYPE_RELATED_BUNDLE:
1368 if (BOOTSTRAPPER_ACTION_STATE_NONE != pPlan->rgExecuteActions[i].relatedBundle.action) 1382 packageAction = pPlan->rgExecuteActions[i].relatedBundle.action;
1369 {
1370 fExecutingAnyPackage = TRUE;
1371 1383
1384 if (BOOTSTRAPPER_ACTION_STATE_NONE != packageAction)
1385 {
1372 BURN_PACKAGE* pPackage = &pPlan->rgExecuteActions[i].relatedBundle.pRelatedBundle->package; 1386 BURN_PACKAGE* pPackage = &pPlan->rgExecuteActions[i].relatedBundle.pRelatedBundle->package;
1373 if (pPackage->cDependencyProviders) 1387 if (pPackage->cDependencyProviders)
1374 { 1388 {
@@ -1380,21 +1394,24 @@ extern "C" HRESULT PlanRelatedBundlesComplete(
1380 break; 1394 break;
1381 1395
1382 case BURN_EXECUTE_ACTION_TYPE_EXE_PACKAGE: 1396 case BURN_EXECUTE_ACTION_TYPE_EXE_PACKAGE:
1383 fExecutingAnyPackage |= (BOOTSTRAPPER_ACTION_STATE_NONE != pPlan->rgExecuteActions[i].exePackage.action); 1397 packageAction = pPlan->rgExecuteActions[i].exePackage.action;
1384 break; 1398 break;
1385 1399
1386 case BURN_EXECUTE_ACTION_TYPE_MSI_PACKAGE: 1400 case BURN_EXECUTE_ACTION_TYPE_MSI_PACKAGE:
1387 fExecutingAnyPackage |= (BOOTSTRAPPER_ACTION_STATE_NONE != pPlan->rgExecuteActions[i].msiPackage.action); 1401 packageAction = pPlan->rgExecuteActions[i].msiPackage.action;
1388 break; 1402 break;
1389 1403
1390 case BURN_EXECUTE_ACTION_TYPE_MSP_TARGET: 1404 case BURN_EXECUTE_ACTION_TYPE_MSP_TARGET:
1391 fExecutingAnyPackage |= (BOOTSTRAPPER_ACTION_STATE_NONE != pPlan->rgExecuteActions[i].mspTarget.action); 1405 packageAction = pPlan->rgExecuteActions[i].mspTarget.action;
1392 break; 1406 break;
1393 1407
1394 case BURN_EXECUTE_ACTION_TYPE_MSU_PACKAGE: 1408 case BURN_EXECUTE_ACTION_TYPE_MSU_PACKAGE:
1395 fExecutingAnyPackage |= (BOOTSTRAPPER_ACTION_STATE_NONE != pPlan->rgExecuteActions[i].msuPackage.action); 1409 packageAction = pPlan->rgExecuteActions[i].msuPackage.action;
1396 break; 1410 break;
1397 } 1411 }
1412
1413 fExecutingAnyPackage |= BOOTSTRAPPER_ACTION_STATE_NONE != packageAction;
1414 fInstallingAnyPackage |= BOOTSTRAPPER_ACTION_STATE_INSTALL == packageAction || BOOTSTRAPPER_ACTION_STATE_MINOR_UPGRADE == packageAction;
1398 } 1415 }
1399 1416
1400 for (DWORD i = 0; i < pRegistration->relatedBundles.cRelatedBundles; ++i) 1417 for (DWORD i = 0; i < pRegistration->relatedBundles.cRelatedBundles; ++i)
@@ -1492,6 +1509,62 @@ extern "C" HRESULT PlanRelatedBundlesComplete(
1492 hr = DependencyPlanPackageComplete(&pRelatedBundle->package, pPlan); 1509 hr = DependencyPlanPackageComplete(&pRelatedBundle->package, pPlan);
1493 ExitOnFailure(hr, "Failed to complete plan dependency actions for related bundle package: %ls", pRelatedBundle->package.sczId); 1510 ExitOnFailure(hr, "Failed to complete plan dependency actions for related bundle package: %ls", pRelatedBundle->package.sczId);
1494 } 1511 }
1512
1513 if (fInstallingAnyPackage && BOOTSTRAPPER_RELATION_UPGRADE == pRelatedBundle->relationType)
1514 {
1515 BURN_EXECUTE_ACTION* pAction = NULL;
1516
1517 pRelatedBundle->defaultRequestedRestore = pRelatedBundle->requestedRestore = BOOTSTRAPPER_REQUEST_STATE_FORCE_PRESENT;
1518
1519 hr = UserExperienceOnPlanRestoreRelatedBundle(pUserExperience, pRelatedBundle->package.sczId, &pRelatedBundle->requestedRestore);
1520 ExitOnRootFailure(hr, "BA aborted plan restore related bundle.");
1521
1522 switch (pRelatedBundle->requestedRestore)
1523 {
1524 case BOOTSTRAPPER_REQUEST_STATE_REPAIR:
1525 pRelatedBundle->restore = BOOTSTRAPPER_ACTION_STATE_REPAIR;
1526 break;
1527 case BOOTSTRAPPER_REQUEST_STATE_ABSENT: __fallthrough;
1528 case BOOTSTRAPPER_REQUEST_STATE_CACHE: __fallthrough;
1529 case BOOTSTRAPPER_REQUEST_STATE_FORCE_ABSENT:
1530 pRelatedBundle->restore = BOOTSTRAPPER_ACTION_STATE_UNINSTALL;
1531 break;
1532 case BOOTSTRAPPER_REQUEST_STATE_FORCE_PRESENT:
1533 pRelatedBundle->restore = BOOTSTRAPPER_ACTION_STATE_INSTALL;
1534 break;
1535 default:
1536 pRelatedBundle->restore = BOOTSTRAPPER_ACTION_STATE_NONE;
1537 break;
1538 }
1539
1540 if (BOOTSTRAPPER_ACTION_STATE_NONE != pRelatedBundle->restore)
1541 {
1542 hr = AppendRestoreRelatedBundleAction(pPlan, &pAction);
1543 ExitOnFailure(hr, "Failed to append restore related bundle action to plan.");
1544
1545 pAction->type = BURN_EXECUTE_ACTION_TYPE_RELATED_BUNDLE;
1546 pAction->relatedBundle.pRelatedBundle = pRelatedBundle;
1547 pAction->relatedBundle.action = pRelatedBundle->restore;
1548
1549 if (pRelatedBundle->package.Bundle.sczIgnoreDependencies)
1550 {
1551 hr = StrAllocString(&pAction->relatedBundle.sczIgnoreDependencies, pRelatedBundle->package.Bundle.sczIgnoreDependencies, 0);
1552 ExitOnFailure(hr, "Failed to allocate the list of dependencies to ignore.");
1553 }
1554
1555 if (pRelatedBundle->package.Bundle.wzAncestors)
1556 {
1557 hr = StrAllocString(&pAction->relatedBundle.sczAncestors, pRelatedBundle->package.Bundle.wzAncestors, 0);
1558 ExitOnFailure(hr, "Failed to allocate the list of ancestors.");
1559 }
1560
1561 if (pRelatedBundle->package.Bundle.wzEngineWorkingDirectory)
1562 {
1563 hr = StrAllocString(&pAction->relatedBundle.sczEngineWorkingDirectory, pRelatedBundle->package.Bundle.wzEngineWorkingDirectory, 0);
1564 ExitOnFailure(hr, "Failed to allocate the custom working directory.");
1565 }
1566 }
1567 }
1495 } 1568 }
1496 1569
1497LExit: 1570LExit:
@@ -2269,6 +2342,23 @@ LExit:
2269 return hr; 2342 return hr;
2270} 2343}
2271 2344
2345static HRESULT AppendRestoreRelatedBundleAction(
2346 __in BURN_PLAN* pPlan,
2347 __out BURN_EXECUTE_ACTION** ppExecuteAction
2348 )
2349{
2350 HRESULT hr = S_OK;
2351
2352 hr = MemEnsureArraySizeForNewItems(reinterpret_cast<LPVOID*>(&pPlan->rgRestoreRelatedBundleActions), pPlan->cRestoreRelatedBundleActions, 1, sizeof(BURN_EXECUTE_ACTION), 5);
2353 ExitOnFailure(hr, "Failed to grow plan's array of restore related bundle actions.");
2354
2355 *ppExecuteAction = pPlan->rgRestoreRelatedBundleActions + pPlan->cRestoreRelatedBundleActions;
2356 ++pPlan->cRestoreRelatedBundleActions;
2357
2358LExit:
2359 return hr;
2360}
2361
2272static HRESULT ProcessPayloadGroup( 2362static HRESULT ProcessPayloadGroup(
2273 __in BURN_PLAN* pPlan, 2363 __in BURN_PLAN* pPlan,
2274 __in BURN_PAYLOAD_GROUP* pPayloadGroup 2364 __in BURN_PAYLOAD_GROUP* pPayloadGroup
@@ -2725,6 +2815,28 @@ static void ExecuteActionLog(
2725 } 2815 }
2726} 2816}
2727 2817
2818static void RestoreRelatedBundleActionLog(
2819 __in DWORD iAction,
2820 __in BURN_EXECUTE_ACTION* pAction
2821 )
2822{
2823 switch (pAction->type)
2824 {
2825 case BURN_EXECUTE_ACTION_TYPE_RELATED_BUNDLE:
2826 LogStringLine(PlanDumpLevel, "Restore action[%u]: RELATED_BUNDLE package id: %ls, action: %hs, ignore dependencies: %ls", iAction, pAction->relatedBundle.pRelatedBundle->package.sczId, LoggingActionStateToString(pAction->relatedBundle.action), pAction->relatedBundle.sczIgnoreDependencies);
2827 break;
2828
2829 default:
2830 AssertSz(FALSE, "Unknown execute action type.");
2831 break;
2832 }
2833
2834 if (pAction->fDeleted)
2835 {
2836 LogStringLine(PlanDumpLevel, " (deleted action)");
2837 }
2838}
2839
2728static void CleanActionLog( 2840static void CleanActionLog(
2729 __in DWORD iAction, 2841 __in DWORD iAction,
2730 __in BURN_CLEAN_ACTION* pAction 2842 __in BURN_CLEAN_ACTION* pAction
@@ -2784,6 +2896,11 @@ extern "C" void PlanDump(
2784 ExecuteActionLog(i, pPlan->rgRollbackActions + i, TRUE); 2896 ExecuteActionLog(i, pPlan->rgRollbackActions + i, TRUE);
2785 } 2897 }
2786 2898
2899 for (DWORD i = 0; i < pPlan->cRestoreRelatedBundleActions; ++i)
2900 {
2901 RestoreRelatedBundleActionLog(i, pPlan->rgRestoreRelatedBundleActions + i);
2902 }
2903
2787 for (DWORD i = 0; i < pPlan->cCleanActions; ++i) 2904 for (DWORD i = 0; i < pPlan->cCleanActions; ++i)
2788 { 2905 {
2789 CleanActionLog(i, pPlan->rgCleanActions + i); 2906 CleanActionLog(i, pPlan->rgCleanActions + i);
diff --git a/src/burn/engine/plan.h b/src/burn/engine/plan.h
index 0734e39f..3dce8e5d 100644
--- a/src/burn/engine/plan.h
+++ b/src/burn/engine/plan.h
@@ -280,6 +280,9 @@ typedef struct _BURN_PLAN
280 BURN_EXECUTE_ACTION* rgRollbackActions; 280 BURN_EXECUTE_ACTION* rgRollbackActions;
281 DWORD cRollbackActions; 281 DWORD cRollbackActions;
282 282
283 BURN_EXECUTE_ACTION* rgRestoreRelatedBundleActions;
284 DWORD cRestoreRelatedBundleActions;
285
283 BURN_CLEAN_ACTION* rgCleanActions; 286 BURN_CLEAN_ACTION* rgCleanActions;
284 DWORD cCleanActions; 287 DWORD cCleanActions;
285 288
@@ -394,6 +397,7 @@ HRESULT PlanRelatedBundlesBegin(
394 __in BURN_PLAN* pPlan 397 __in BURN_PLAN* pPlan
395 ); 398 );
396HRESULT PlanRelatedBundlesComplete( 399HRESULT PlanRelatedBundlesComplete(
400 __in BURN_USER_EXPERIENCE* pUserExperience,
397 __in BURN_REGISTRATION* pRegistration, 401 __in BURN_REGISTRATION* pRegistration,
398 __in BURN_PLAN* pPlan, 402 __in BURN_PLAN* pPlan,
399 __in BURN_LOGGING* pLog, 403 __in BURN_LOGGING* pLog,
diff --git a/src/burn/engine/pseudobundle.cpp b/src/burn/engine/pseudobundle.cpp
index 94b095c5..082c4487 100644
--- a/src/burn/engine/pseudobundle.cpp
+++ b/src/burn/engine/pseudobundle.cpp
@@ -51,7 +51,6 @@ extern "C" HRESULT PseudoBundleInitializeRelated(
51 pPackage->fVital = FALSE; 51 pPackage->fVital = FALSE;
52 52
53 pPackage->fPermanent = FALSE; 53 pPackage->fPermanent = FALSE;
54 pPackage->Bundle.fRepairable = TRUE;
55 pPackage->Bundle.fSupportsBurnProtocol = fSupportsBurnProtocol; 54 pPackage->Bundle.fSupportsBurnProtocol = fSupportsBurnProtocol;
56 55
57 hr = StrAllocString(&pPackage->sczId, wzId, 0); 56 hr = StrAllocString(&pPackage->sczId, wzId, 0);
diff --git a/src/burn/engine/registration.h b/src/burn/engine/registration.h
index 0ae61974..64191828 100644
--- a/src/burn/engine/registration.h
+++ b/src/burn/engine/registration.h
@@ -61,6 +61,10 @@ typedef struct _BURN_RELATED_BUNDLE
61 BOOL fPlannable; 61 BOOL fPlannable;
62 62
63 BURN_PACKAGE package; 63 BURN_PACKAGE package;
64
65 BOOTSTRAPPER_REQUEST_STATE defaultRequestedRestore;
66 BOOTSTRAPPER_REQUEST_STATE requestedRestore;
67 BOOTSTRAPPER_ACTION_STATE restore;
64} BURN_RELATED_BUNDLE; 68} BURN_RELATED_BUNDLE;
65 69
66typedef struct _BURN_RELATED_BUNDLES 70typedef struct _BURN_RELATED_BUNDLES
diff --git a/src/burn/engine/userexperience.cpp b/src/burn/engine/userexperience.cpp
index 1439f5f2..59988bef 100644
--- a/src/burn/engine/userexperience.cpp
+++ b/src/burn/engine/userexperience.cpp
@@ -2176,6 +2176,36 @@ LExit:
2176 return hr; 2176 return hr;
2177} 2177}
2178 2178
2179EXTERN_C BAAPI UserExperienceOnPlanRestoreRelatedBundle(
2180 __in BURN_USER_EXPERIENCE* pUserExperience,
2181 __in_z LPCWSTR wzBundleId,
2182 __inout BOOTSTRAPPER_REQUEST_STATE* pRequestedState
2183 )
2184{
2185 HRESULT hr = S_OK;
2186 BA_ONPLANRESTORERELATEDBUNDLE_ARGS args = { };
2187 BA_ONPLANRESTORERELATEDBUNDLE_RESULTS results = { };
2188
2189 args.cbSize = sizeof(args);
2190 args.wzBundleId = wzBundleId;
2191 args.recommendedState = *pRequestedState;
2192
2193 results.cbSize = sizeof(results);
2194 results.requestedState = *pRequestedState;
2195
2196 hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONPLANRESTORERELATEDBUNDLE, &args, &results);
2197 ExitOnFailure(hr, "BA OnPlanRestoreRelatedBundle failed.");
2198
2199 if (results.fCancel)
2200 {
2201 hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT);
2202 }
2203 *pRequestedState = results.requestedState;
2204
2205LExit:
2206 return hr;
2207}
2208
2179EXTERN_C BAAPI UserExperienceOnPlanRollbackBoundary( 2209EXTERN_C BAAPI UserExperienceOnPlanRollbackBoundary(
2180 __in BURN_USER_EXPERIENCE* pUserExperience, 2210 __in BURN_USER_EXPERIENCE* pUserExperience,
2181 __in_z LPCWSTR wzRollbackBoundaryId, 2211 __in_z LPCWSTR wzRollbackBoundaryId,
diff --git a/src/burn/engine/userexperience.h b/src/burn/engine/userexperience.h
index e8341120..8106d7f7 100644
--- a/src/burn/engine/userexperience.h
+++ b/src/burn/engine/userexperience.h
@@ -497,6 +497,11 @@ BAAPI UserExperienceOnPlanRelatedBundle(
497 __in_z LPCWSTR wzBundleId, 497 __in_z LPCWSTR wzBundleId,
498 __inout BOOTSTRAPPER_REQUEST_STATE* pRequestedState 498 __inout BOOTSTRAPPER_REQUEST_STATE* pRequestedState
499 ); 499 );
500BAAPI UserExperienceOnPlanRestoreRelatedBundle(
501 __in BURN_USER_EXPERIENCE* pUserExperience,
502 __in_z LPCWSTR wzBundleId,
503 __inout BOOTSTRAPPER_REQUEST_STATE* pRequestedState
504 );
500BAAPI UserExperienceOnPlanRollbackBoundary( 505BAAPI UserExperienceOnPlanRollbackBoundary(
501 __in BURN_USER_EXPERIENCE* pUserExperience, 506 __in BURN_USER_EXPERIENCE* pUserExperience,
502 __in_z LPCWSTR wzRollbackBoundaryId, 507 __in_z LPCWSTR wzRollbackBoundaryId,
diff --git a/src/burn/test/BurnUnitTest/PlanTest.cpp b/src/burn/test/BurnUnitTest/PlanTest.cpp
index 99644115..81484234 100644
--- a/src/burn/test/BurnUnitTest/PlanTest.cpp
+++ b/src/burn/test/BurnUnitTest/PlanTest.cpp
@@ -170,6 +170,10 @@ namespace Bootstrapper
170 Assert::Equal(7ul, pPlan->cOverallProgressTicksTotal); 170 Assert::Equal(7ul, pPlan->cOverallProgressTicksTotal);
171 171
172 dwIndex = 0; 172 dwIndex = 0;
173 ValidateRestoreRelatedBundle(pPlan, dwIndex++, L"{FD9920AD-DBCA-4C6C-8CD5-B47431CE8D21}", BOOTSTRAPPER_ACTION_STATE_INSTALL, NULL);
174 Assert::Equal(dwIndex, pPlan->cRestoreRelatedBundleActions);
175
176 dwIndex = 0;
173 Assert::Equal(dwIndex, pPlan->cCleanActions); 177 Assert::Equal(dwIndex, pPlan->cCleanActions);
174 178
175 UINT uIndex = 0; 179 UINT uIndex = 0;
@@ -277,6 +281,9 @@ namespace Bootstrapper
277 Assert::Equal(3ul, pPlan->cOverallProgressTicksTotal); 281 Assert::Equal(3ul, pPlan->cOverallProgressTicksTotal);
278 282
279 dwIndex = 0; 283 dwIndex = 0;
284 Assert::Equal(dwIndex, pPlan->cRestoreRelatedBundleActions);
285
286 dwIndex = 0;
280 ValidateCleanAction(pPlan, dwIndex++, L"PackageC"); 287 ValidateCleanAction(pPlan, dwIndex++, L"PackageC");
281 ValidateCleanAction(pPlan, dwIndex++, L"PackageB"); 288 ValidateCleanAction(pPlan, dwIndex++, L"PackageB");
282 ValidateCleanAction(pPlan, dwIndex++, L"PackageA"); 289 ValidateCleanAction(pPlan, dwIndex++, L"PackageA");
@@ -350,6 +357,9 @@ namespace Bootstrapper
350 Assert::Equal(1ul, pPlan->cOverallProgressTicksTotal); 357 Assert::Equal(1ul, pPlan->cOverallProgressTicksTotal);
351 358
352 dwIndex = 0; 359 dwIndex = 0;
360 Assert::Equal(dwIndex, pPlan->cRestoreRelatedBundleActions);
361
362 dwIndex = 0;
353 ValidateCleanAction(pPlan, dwIndex++, L"PackageA"); 363 ValidateCleanAction(pPlan, dwIndex++, L"PackageA");
354 ValidateCleanCompatibleAction(pPlan, dwIndex++, L"PackageA"); 364 ValidateCleanCompatibleAction(pPlan, dwIndex++, L"PackageA");
355 Assert::Equal(dwIndex, pPlan->cCleanActions); 365 Assert::Equal(dwIndex, pPlan->cCleanActions);
@@ -438,6 +448,9 @@ namespace Bootstrapper
438 Assert::Equal(2ul, pPlan->cOverallProgressTicksTotal); 448 Assert::Equal(2ul, pPlan->cOverallProgressTicksTotal);
439 449
440 dwIndex = 0; 450 dwIndex = 0;
451 Assert::Equal(dwIndex, pPlan->cRestoreRelatedBundleActions);
452
453 dwIndex = 0;
441 Assert::Equal(dwIndex, pPlan->cCleanActions); 454 Assert::Equal(dwIndex, pPlan->cCleanActions);
442 455
443 UINT uIndex = 0; 456 UINT uIndex = 0;
@@ -508,6 +521,9 @@ namespace Bootstrapper
508 Assert::Equal(1ul, pPlan->cOverallProgressTicksTotal); 521 Assert::Equal(1ul, pPlan->cOverallProgressTicksTotal);
509 522
510 dwIndex = 0; 523 dwIndex = 0;
524 Assert::Equal(dwIndex, pPlan->cRestoreRelatedBundleActions);
525
526 dwIndex = 0;
511 Assert::Equal(dwIndex, pPlan->cCleanActions); 527 Assert::Equal(dwIndex, pPlan->cCleanActions);
512 528
513 UINT uIndex = 0; 529 UINT uIndex = 0;
@@ -579,6 +595,9 @@ namespace Bootstrapper
579 Assert::Equal(2ul, pPlan->cOverallProgressTicksTotal); 595 Assert::Equal(2ul, pPlan->cOverallProgressTicksTotal);
580 596
581 dwIndex = 0; 597 dwIndex = 0;
598 Assert::Equal(dwIndex, pPlan->cRestoreRelatedBundleActions);
599
600 dwIndex = 0;
582 ValidateCleanAction(pPlan, dwIndex++, L"PackageA"); 601 ValidateCleanAction(pPlan, dwIndex++, L"PackageA");
583 Assert::Equal(dwIndex, pPlan->cCleanActions); 602 Assert::Equal(dwIndex, pPlan->cCleanActions);
584 603
@@ -658,6 +677,10 @@ namespace Bootstrapper
658 Assert::Equal(3ul, pPlan->cOverallProgressTicksTotal); 677 Assert::Equal(3ul, pPlan->cOverallProgressTicksTotal);
659 678
660 dwIndex = 0; 679 dwIndex = 0;
680 ValidateRestoreRelatedBundle(pPlan, dwIndex++, L"{FD9920AD-DBCA-4C6C-8CD5-B47431CE8D21}", BOOTSTRAPPER_ACTION_STATE_INSTALL, NULL);
681 Assert::Equal(dwIndex, pPlan->cRestoreRelatedBundleActions);
682
683 dwIndex = 0;
661 Assert::Equal(dwIndex, pPlan->cCleanActions); 684 Assert::Equal(dwIndex, pPlan->cCleanActions);
662 685
663 UINT uIndex = 0; 686 UINT uIndex = 0;
@@ -744,6 +767,10 @@ namespace Bootstrapper
744 Assert::Equal(3ul, pPlan->cOverallProgressTicksTotal); 767 Assert::Equal(3ul, pPlan->cOverallProgressTicksTotal);
745 768
746 dwIndex = 0; 769 dwIndex = 0;
770 ValidateRestoreRelatedBundle(pPlan, dwIndex++, L"{FD9920AD-DBCA-4C6C-8CD5-B47431CE8D21}", BOOTSTRAPPER_ACTION_STATE_INSTALL, NULL);
771 Assert::Equal(dwIndex, pPlan->cRestoreRelatedBundleActions);
772
773 dwIndex = 0;
747 Assert::Equal(dwIndex, pPlan->cCleanActions); 774 Assert::Equal(dwIndex, pPlan->cCleanActions);
748 775
749 UINT uIndex = 0; 776 UINT uIndex = 0;
@@ -805,6 +832,9 @@ namespace Bootstrapper
805 Assert::Equal(0ul, pPlan->cOverallProgressTicksTotal); 832 Assert::Equal(0ul, pPlan->cOverallProgressTicksTotal);
806 833
807 dwIndex = 0; 834 dwIndex = 0;
835 Assert::Equal(dwIndex, pPlan->cRestoreRelatedBundleActions);
836
837 dwIndex = 0;
808 ValidateCleanAction(pPlan, dwIndex++, L"PackageA"); 838 ValidateCleanAction(pPlan, dwIndex++, L"PackageA");
809 Assert::Equal(dwIndex, pPlan->cCleanActions); 839 Assert::Equal(dwIndex, pPlan->cCleanActions);
810 840
@@ -879,6 +909,9 @@ namespace Bootstrapper
879 Assert::Equal(1ul, pPlan->cOverallProgressTicksTotal); 909 Assert::Equal(1ul, pPlan->cOverallProgressTicksTotal);
880 910
881 dwIndex = 0; 911 dwIndex = 0;
912 Assert::Equal(dwIndex, pPlan->cRestoreRelatedBundleActions);
913
914 dwIndex = 0;
882 ValidateCleanAction(pPlan, dwIndex++, L"PackageA"); 915 ValidateCleanAction(pPlan, dwIndex++, L"PackageA");
883 Assert::Equal(dwIndex, pPlan->cCleanActions); 916 Assert::Equal(dwIndex, pPlan->cCleanActions);
884 917
@@ -946,6 +979,9 @@ namespace Bootstrapper
946 Assert::Equal(0ul, pPlan->cOverallProgressTicksTotal); 979 Assert::Equal(0ul, pPlan->cOverallProgressTicksTotal);
947 980
948 dwIndex = 0; 981 dwIndex = 0;
982 Assert::Equal(dwIndex, pPlan->cRestoreRelatedBundleActions);
983
984 dwIndex = 0;
949 Assert::Equal(dwIndex, pPlan->cCleanActions); 985 Assert::Equal(dwIndex, pPlan->cCleanActions);
950 986
951 UINT uIndex = 0; 987 UINT uIndex = 0;
@@ -1057,6 +1093,9 @@ namespace Bootstrapper
1057 Assert::Equal(5ul, pPlan->cOverallProgressTicksTotal); 1093 Assert::Equal(5ul, pPlan->cOverallProgressTicksTotal);
1058 1094
1059 dwIndex = 0; 1095 dwIndex = 0;
1096 Assert::Equal(dwIndex, pPlan->cRestoreRelatedBundleActions);
1097
1098 dwIndex = 0;
1060 Assert::Equal(dwIndex, pPlan->cCleanActions); 1099 Assert::Equal(dwIndex, pPlan->cCleanActions);
1061 1100
1062 UINT uIndex = 0; 1101 UINT uIndex = 0;
@@ -1147,6 +1186,9 @@ namespace Bootstrapper
1147 Assert::Equal(2ul, pPlan->cOverallProgressTicksTotal); 1186 Assert::Equal(2ul, pPlan->cOverallProgressTicksTotal);
1148 1187
1149 dwIndex = 0; 1188 dwIndex = 0;
1189 Assert::Equal(dwIndex, pPlan->cRestoreRelatedBundleActions);
1190
1191 dwIndex = 0;
1150 ValidateCleanAction(pPlan, dwIndex++, L"PatchA"); 1192 ValidateCleanAction(pPlan, dwIndex++, L"PatchA");
1151 ValidateCleanAction(pPlan, dwIndex++, L"PackageA"); 1193 ValidateCleanAction(pPlan, dwIndex++, L"PackageA");
1152 Assert::Equal(dwIndex, pPlan->cCleanActions); 1194 Assert::Equal(dwIndex, pPlan->cCleanActions);
@@ -1829,6 +1871,28 @@ namespace Bootstrapper
1829 NativeAssert::StringEqual(wzName, pProvider->sczName); 1871 NativeAssert::StringEqual(wzName, pProvider->sczName);
1830 } 1872 }
1831 1873
1874 void ValidateRestoreRelatedBundle(
1875 __in BURN_PLAN* pPlan,
1876 __in DWORD dwIndex,
1877 __in LPCWSTR wzPackageId,
1878 __in BOOTSTRAPPER_ACTION_STATE action,
1879 __in LPCWSTR wzIgnoreDependencies
1880 )
1881 {
1882 BURN_EXECUTE_ACTION* pAction = ValidateRestoreRelatedBundleActionExists(pPlan, dwIndex);
1883 Assert::Equal<DWORD>(BURN_EXECUTE_ACTION_TYPE_RELATED_BUNDLE, pAction->type);
1884 NativeAssert::StringEqual(wzPackageId, pAction->relatedBundle.pRelatedBundle->package.sczId);
1885 Assert::Equal<DWORD>(action, pAction->relatedBundle.action);
1886 NativeAssert::StringEqual(wzIgnoreDependencies, pAction->relatedBundle.sczIgnoreDependencies);
1887 Assert::Equal<BOOL>(FALSE, pAction->fDeleted);
1888 }
1889
1890 BURN_EXECUTE_ACTION* ValidateRestoreRelatedBundleActionExists(BURN_PLAN* pPlan, DWORD dwIndex)
1891 {
1892 Assert::InRange(dwIndex + 1ul, 1ul, pPlan->cRestoreRelatedBundleActions);
1893 return pPlan->rgRestoreRelatedBundleActions + dwIndex;
1894 }
1895
1832 void ValidateUninstallMsiCompatiblePackage( 1896 void ValidateUninstallMsiCompatiblePackage(
1833 __in BURN_PLAN* pPlan, 1897 __in BURN_PLAN* pPlan,
1834 __in BOOL fRollback, 1898 __in BOOL fRollback,
diff --git a/src/ext/Bal/wixstdba/WixStandardBootstrapperApplication.cpp b/src/ext/Bal/wixstdba/WixStandardBootstrapperApplication.cpp
index 02c10472..8c7bce7e 100644
--- a/src/ext/Bal/wixstdba/WixStandardBootstrapperApplication.cpp
+++ b/src/ext/Bal/wixstdba/WixStandardBootstrapperApplication.cpp
@@ -2089,6 +2089,16 @@ private: // privates
2089 m_pfnBAFunctionsProc(BA_FUNCTIONS_MESSAGE_ONPLANCOMPATIBLEMSIPACKAGECOMPLETE, pArgs, pResults, m_pvBAFunctionsProcContext); 2089 m_pfnBAFunctionsProc(BA_FUNCTIONS_MESSAGE_ONPLANCOMPATIBLEMSIPACKAGECOMPLETE, pArgs, pResults, m_pvBAFunctionsProcContext);
2090 } 2090 }
2091 2091
2092 void OnPlanRestoreRelatedBundleFallback(
2093 __in BA_ONPLANRESTORERELATEDBUNDLE_ARGS* pArgs,
2094 __inout BA_ONPLANRESTORERELATEDBUNDLE_RESULTS* pResults
2095 )
2096 {
2097 BOOTSTRAPPER_REQUEST_STATE requestedState = pResults->requestedState;
2098 m_pfnBAFunctionsProc(BA_FUNCTIONS_MESSAGE_ONPLANRESTORERELATEDBUNDLE, pArgs, pResults, m_pvBAFunctionsProcContext);
2099 BalLogId(BOOTSTRAPPER_LOG_LEVEL_STANDARD, MSG_WIXSTDBA_PLANNED_RESTORE_RELATED_BUNDLE, m_hModule, pArgs->wzBundleId, LoggingRequestStateToString(requestedState), LoggingRequestStateToString(pResults->requestedState));
2100 }
2101
2092 2102
2093public: //CBalBaseBootstrapperApplication 2103public: //CBalBaseBootstrapperApplication
2094 virtual STDMETHODIMP Initialize( 2104 virtual STDMETHODIMP Initialize(
diff --git a/src/ext/Bal/wixstdba/wixstdba.mc b/src/ext/Bal/wixstdba/wixstdba.mc
index 40acfe54..eeb69914 100644
--- a/src/ext/Bal/wixstdba/wixstdba.mc
+++ b/src/ext/Bal/wixstdba/wixstdba.mc
@@ -85,3 +85,10 @@ Language=English
85WIXSTDBA: Planned rollback boundary: %1!ls!, wixstdba requested transaction: %2!hs!, bafunctions requested transaction: %3!hs! 85WIXSTDBA: Planned rollback boundary: %1!ls!, wixstdba requested transaction: %2!hs!, bafunctions requested transaction: %3!hs!
86. 86.
87 87
88MessageId=9
89Severity=Success
90SymbolicName=MSG_WIXSTDBA_PLANNED_RESTORE_RELATED_BUNDLE
91Language=English
92WIXSTDBA: Planned restore related bundle: %1!ls!, wixstdba requested: %2!hs!, bafunctions requested: %3!hs!
93.
94
diff --git a/src/test/burn/TestData/UpgradeRelatedBundleTests/BundleAv3/BundleAv3.wixproj b/src/test/burn/TestData/UpgradeRelatedBundleTests/BundleAv3/BundleAv3.wixproj
new file mode 100644
index 00000000..958ceb47
--- /dev/null
+++ b/src/test/burn/TestData/UpgradeRelatedBundleTests/BundleAv3/BundleAv3.wixproj
@@ -0,0 +1,16 @@
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<Project Sdk="WixToolset.Sdk">
3 <Import Project="..\BundleAv1\BundleA.props" />
4 <PropertyGroup>
5 <Version>3.0.0.0</Version>
6 </PropertyGroup>
7 <ItemGroup>
8 <ProjectReference Include="..\PackageAv3\PackageAv3.wixproj" />
9 <ProjectReference Include="..\PackageF\PackageF.wixproj" />
10 <ProjectReference Include="..\..\TestBA\TestBAWixlib\testbawixlib.wixproj" />
11 </ItemGroup>
12 <ItemGroup>
13 <PackageReference Include="WixToolset.Bal.wixext" />
14 <PackageReference Include="WixToolset.NetFx.wixext" />
15 </ItemGroup>
16</Project> \ No newline at end of file
diff --git a/src/test/burn/TestData/UpgradeRelatedBundleTests/BundleAv3/BundleAv3.wxs b/src/test/burn/TestData/UpgradeRelatedBundleTests/BundleAv3/BundleAv3.wxs
new file mode 100644
index 00000000..b969b504
--- /dev/null
+++ b/src/test/burn/TestData/UpgradeRelatedBundleTests/BundleAv3/BundleAv3.wxs
@@ -0,0 +1,11 @@
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
4<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs">
5 <Fragment>
6 <PackageGroup Id="BundlePackages">
7 <MsiPackage Id="PackageA" SourceFile="$(var.PackageAv3.TargetPath)" />
8 <MsiPackage Id="PackageF" SourceFile="$(var.PackageF.TargetPath)" />
9 </PackageGroup>
10 </Fragment>
11</Wix>
diff --git a/src/test/burn/TestData/UpgradeRelatedBundleTests/PackageAv3/PackageAv3.wixproj b/src/test/burn/TestData/UpgradeRelatedBundleTests/PackageAv3/PackageAv3.wixproj
new file mode 100644
index 00000000..f3c121af
--- /dev/null
+++ b/src/test/burn/TestData/UpgradeRelatedBundleTests/PackageAv3/PackageAv3.wixproj
@@ -0,0 +1,7 @@
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<Project Sdk="WixToolset.Sdk">
3 <Import Project="..\PackageAv1\PackageA.props" />
4 <PropertyGroup>
5 <Version>3.0.0.0</Version>
6 </PropertyGroup>
7</Project> \ No newline at end of file
diff --git a/src/test/burn/TestData/UpgradeRelatedBundleTests/PackageF/PackageF.wixproj b/src/test/burn/TestData/UpgradeRelatedBundleTests/PackageF/PackageF.wixproj
new file mode 100644
index 00000000..63d32e28
--- /dev/null
+++ b/src/test/burn/TestData/UpgradeRelatedBundleTests/PackageF/PackageF.wixproj
@@ -0,0 +1,12 @@
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<Project Sdk="WixToolset.Sdk">
3 <PropertyGroup>
4 <UpgradeCode>{8F6C8C4B-573C-416B-B1B0-467273256BD9}</UpgradeCode>
5 </PropertyGroup>
6 <ItemGroup>
7 <Compile Include="..\..\Templates\PackageFail.wxs" Link="PackageFail.wxs" />
8 </ItemGroup>
9 <ItemGroup>
10 <PackageReference Include="WixToolset.Util.wixext" />
11 </ItemGroup>
12</Project> \ No newline at end of file
diff --git a/src/test/burn/WixToolsetTest.BurnE2E/DependencyTests.cs b/src/test/burn/WixToolsetTest.BurnE2E/DependencyTests.cs
index 69a1fa68..ba02d8ee 100644
--- a/src/test/burn/WixToolsetTest.BurnE2E/DependencyTests.cs
+++ b/src/test/burn/WixToolsetTest.BurnE2E/DependencyTests.cs
@@ -539,7 +539,7 @@ namespace WixToolsetTest.BurnE2E
539 } 539 }
540 } 540 }
541 541
542 [Fact(Skip = "https://github.com/wixtoolset/issues/issues/3421")] 542 [Fact]
543 public void DoesntLoseDependenciesOnFailedMajorUpgradeBundleFromMajorUpdateMsiFifo() 543 public void DoesntLoseDependenciesOnFailedMajorUpgradeBundleFromMajorUpdateMsiFifo()
544 { 544 {
545 var packageAv1 = this.CreatePackageInstaller("PackageAv1"); 545 var packageAv1 = this.CreatePackageInstaller("PackageAv1");
@@ -611,7 +611,7 @@ namespace WixToolsetTest.BurnE2E
611 packageGv2.VerifyInstalled(false); 611 packageGv2.VerifyInstalled(false);
612 } 612 }
613 613
614 [Fact(Skip = "https://github.com/wixtoolset/issues/issues/3421")] 614 [Fact]
615 public void DoesntLoseDependenciesOnFailedMajorUpgradeBundleFromMajorUpdateMsiLifo() 615 public void DoesntLoseDependenciesOnFailedMajorUpgradeBundleFromMajorUpdateMsiLifo()
616 { 616 {
617 var packageAv1 = this.CreatePackageInstaller("PackageAv1"); 617 var packageAv1 = this.CreatePackageInstaller("PackageAv1");
diff --git a/src/test/burn/WixToolsetTest.BurnE2E/UpgradeRelatedBundleTests.cs b/src/test/burn/WixToolsetTest.BurnE2E/UpgradeRelatedBundleTests.cs
index 9eb5081e..32a04e5c 100644
--- a/src/test/burn/WixToolsetTest.BurnE2E/UpgradeRelatedBundleTests.cs
+++ b/src/test/burn/WixToolsetTest.BurnE2E/UpgradeRelatedBundleTests.cs
@@ -13,6 +13,33 @@ namespace WixToolsetTest.BurnE2E
13 public UpgradeRelatedBundleTests(ITestOutputHelper testOutputHelper) : base(testOutputHelper) { } 13 public UpgradeRelatedBundleTests(ITestOutputHelper testOutputHelper) : base(testOutputHelper) { }
14 14
15 [Fact] 15 [Fact]
16 public void ReinstallsOlderBundleAfterFailure()
17 {
18 var packageAv2 = this.CreatePackageInstaller("PackageAv2");
19 var packageAv3 = this.CreatePackageInstaller("PackageAv3");
20 var bundleAv2 = this.CreateBundleInstaller("BundleAv2");
21 var bundleAv3 = this.CreateBundleInstaller("BundleAv3");
22
23 packageAv2.VerifyInstalled(false);
24 packageAv3.VerifyInstalled(false);
25
26 bundleAv2.Install();
27 bundleAv2.VerifyRegisteredAndInPackageCache();
28
29 packageAv2.VerifyInstalled(true);
30 packageAv3.VerifyInstalled(false);
31
32 // Verify https://github.com/wixtoolset/issues/issues/3421
33 var bundleAv3InstallLogFilePath = bundleAv3.Install((int)MSIExec.MSIExecReturnCode.ERROR_INSTALL_FAILURE);
34 bundleAv3.VerifyUnregisteredAndRemovedFromPackageCache();
35
36 Assert.True(LogVerifier.MessageInLogFileRegex(bundleAv3InstallLogFilePath, @"Applied execute package: PackageA, result: 0x0, restart: None"));
37
38 packageAv2.VerifyInstalled(true);
39 packageAv3.VerifyInstalled(false);
40 }
41
42 [Fact]
16 public void ReportsRelatedBundleMissingFromCache() 43 public void ReportsRelatedBundleMissingFromCache()
17 { 44 {
18 var packageAv1 = this.CreatePackageInstaller("PackageAv1"); 45 var packageAv1 = this.CreatePackageInstaller("PackageAv1");