@joebigelow / wix-1 / commits / 27a0db40

When rolling back a bundle failure, reinstall all upgrade related bundles.

Fixes #3421

Sean Hall committed Feb 10, 2022 at 18:09 UTC 27a0db4070a2b5756282bf15b957dd7f0021417f
31 files changed +515 -28
src/api/burn/WixToolset.BootstrapperCore.Native/inc/BootstrapperApplication.h
+15
@@ -209,6 +209,7 @@ enum BOOTSTRAPPER_APPLICATION_MESSAGE
209 BOOTSTRAPPER_APPLICATION_MESSAGE_ONPLANCOMPATIBLEMSIPACKAGEBEGIN,
210 BOOTSTRAPPER_APPLICATION_MESSAGE_ONPLANCOMPATIBLEMSIPACKAGECOMPLETE,
211 BOOTSTRAPPER_APPLICATION_MESSAGE_ONPLANNEDCOMPATIBLEPACKAGE,
212 + BOOTSTRAPPER_APPLICATION_MESSAGE_ONPLANRESTORERELATEDBUNDLE,
213 };
214
215 enum BOOTSTRAPPER_APPLYCOMPLETE_ACTION
@@ -1208,6 +1209,20 @@ struct BA_ONPLANRELATEDBUNDLE_RESULTS
1209 BOOTSTRAPPER_REQUEST_STATE requestedState;
1210 };
1211
1212 +struct BA_ONPLANRESTORERELATEDBUNDLE_ARGS
1213 +{
1214 + DWORD cbSize;
1215 + LPCWSTR wzBundleId;
1216 + BOOTSTRAPPER_REQUEST_STATE recommendedState;
1217 +};
1218 +
1219 +struct BA_ONPLANRESTORERELATEDBUNDLE_RESULTS
1220 +{
1221 + DWORD cbSize;
1222 + BOOL fCancel;
1223 + BOOTSTRAPPER_REQUEST_STATE requestedState;
1224 +};
1225 +
1226 struct BA_ONPLANROLLBACKBOUNDARY_ARGS
1227 {
1228 DWORD cbSize;
src/api/burn/WixToolset.Mba.Core/BootstrapperApplication.cs
+26
@@ -271,6 +271,9 @@ namespace WixToolset.Mba.Core
271 /// <inheritdoc/>
272 public event EventHandler<SetUpdateCompleteEventArgs> SetUpdateComplete;
273
274 + /// <inheritdoc/>
275 + public event EventHandler<PlanRestoreRelatedBundleEventArgs> PlanRestoreRelatedBundle;
276 +
277 /// <summary>
278 /// Entry point that is called when the bootstrapper application is ready to run.
279 /// </summary>
@@ -1321,6 +1324,19 @@ namespace WixToolset.Mba.Core
1324 }
1325 }
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 +
1340 #region IBootstrapperApplication Members
1341
1342 int IBootstrapperApplication.BAProc(int message, IntPtr pvArgs, IntPtr pvResults, IntPtr pvContext)
@@ -2042,6 +2058,16 @@ namespace WixToolset.Mba.Core
2058 return args.HResult;
2059 }
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 +
2071 #endregion
2072 }
2073 }
src/api/burn/WixToolset.Mba.Core/EventArgs.cs
+31
@@ -2402,4 +2402,35 @@ namespace WixToolset.Mba.Core
2402 /// </summary>
2403 public string NewPackageId { get; private set; }
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 + }
2436 }
src/api/burn/WixToolset.Mba.Core/IBootstrapperApplication.cs
+12
@@ -1136,6 +1136,18 @@ namespace WixToolset.Mba.Core
1136 [MarshalAs(UnmanagedType.LPWStr)] string wzPreviousPackageId,
1137 [MarshalAs(UnmanagedType.LPWStr)] string wzNewPackageId
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 + );
1151 }
1152
1153 /// <summary>
src/api/burn/WixToolset.Mba.Core/IDefaultBootstrapperApplication.cs
+5
@@ -333,6 +333,11 @@ namespace WixToolset.Mba.Core
333 /// </summary>
334 event EventHandler<PlanRelatedBundleEventArgs> PlanRelatedBundle;
335
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>
342 /// Fired when the engine is planning a rollback boundary.
343 /// </summary>
src/api/burn/balutil/inc/BAFunctions.h
+1
@@ -88,6 +88,7 @@ enum BA_FUNCTIONS_MESSAGE
88 BA_FUNCTIONS_MESSAGE_ONPLANCOMPATIBLEMSIPACKAGEBEGIN = BOOTSTRAPPER_APPLICATION_MESSAGE_ONPLANCOMPATIBLEMSIPACKAGEBEGIN,
89 BA_FUNCTIONS_MESSAGE_ONPLANCOMPATIBLEMSIPACKAGECOMPLETE = BOOTSTRAPPER_APPLICATION_MESSAGE_ONPLANCOMPATIBLEMSIPACKAGECOMPLETE,
90 BA_FUNCTIONS_MESSAGE_ONPLANNEDCOMPATIBLEPACKAGE = BOOTSTRAPPER_APPLICATION_MESSAGE_ONPLANNEDCOMPATIBLEPACKAGE,
91 + BA_FUNCTIONS_MESSAGE_ONPLANRESTORERELATEDBUNDLE = BOOTSTRAPPER_APPLICATION_MESSAGE_ONPLANRESTORERELATEDBUNDLE,
92
93 BA_FUNCTIONS_MESSAGE_ONTHEMELOADED = 1024,
94 BA_FUNCTIONS_MESSAGE_WNDPROC,
src/api/burn/balutil/inc/BalBaseBAFunctions.h
+10
@@ -849,6 +849,16 @@ public: // IBootstrapperApplication
849 return S_OK;
850 }
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 +
862 public: // IBAFunctions
863 virtual STDMETHODIMP OnPlan(
864 )
src/api/burn/balutil/inc/BalBaseBAFunctionsProc.h
+1
@@ -159,6 +159,7 @@ static HRESULT WINAPI BalBaseBAFunctionsProc(
159 case BA_FUNCTIONS_MESSAGE_ONPLANCOMPATIBLEMSIPACKAGEBEGIN:
160 case BA_FUNCTIONS_MESSAGE_ONPLANCOMPATIBLEMSIPACKAGECOMPLETE:
161 case BA_FUNCTIONS_MESSAGE_ONPLANNEDCOMPATIBLEPACKAGE:
162 + case BA_FUNCTIONS_MESSAGE_ONPLANRESTORERELATEDBUNDLE:
163 hr = BalBaseBootstrapperApplicationProc((BOOTSTRAPPER_APPLICATION_MESSAGE)message, pvArgs, pvResults, pvContext);
164 break;
165 case BA_FUNCTIONS_MESSAGE_ONTHEMELOADED:
src/api/burn/balutil/inc/BalBaseBootstrapperApplication.h
+11
@@ -1047,6 +1047,17 @@ public: // IBootstrapperApplication
1047 return S_OK;
1048 }
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 +
1061 public: //CBalBaseBootstrapperApplication
1062 virtual STDMETHODIMP Initialize(
1063 __in const BOOTSTRAPPER_CREATE_ARGS* pCreateArgs
src/api/burn/balutil/inc/BalBaseBootstrapperApplicationProc.h
+12
@@ -720,6 +720,15 @@ static HRESULT BalBaseBAProcOnSetUpdateComplete(
720 return pBA->OnSetUpdateComplete(pArgs->hrStatus, pArgs->wzPreviousPackageId, pArgs->wzNewPackageId);
721 }
722
723 +static 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 +
732 /*******************************************************************
733 BalBaseBootstrapperApplicationProc - requires pvContext to be of type IBootstrapperApplication.
734 Provides a default mapping between the new message based BA interface and
@@ -976,6 +985,9 @@ static HRESULT WINAPI BalBaseBootstrapperApplicationProc(
985 case BOOTSTRAPPER_APPLICATION_MESSAGE_ONPLANNEDCOMPATIBLEPACKAGE:
986 hr = BalBaseBAProcOnPlannedCompatiblePackage(pBA, reinterpret_cast<BA_ONPLANNEDCOMPATIBLEPACKAGE_ARGS*>(pvArgs), reinterpret_cast<BA_ONPLANNEDCOMPATIBLEPACKAGE_RESULTS*>(pvResults));
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;
991 }
992 }
993
src/api/burn/balutil/inc/IBootstrapperApplication.h
+8
@@ -691,4 +691,12 @@ DECLARE_INTERFACE_IID_(IBootstrapperApplication, IUnknown, "53C31D56-49C0-426B-A
691 __in_z_opt LPCWSTR wzPreviousPackageId,
692 __in_z_opt LPCWSTR wzNewPackageId
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;
702 };
src/burn/engine/apply.cpp
+50
@@ -210,6 +210,11 @@ static HRESULT ExecuteRelatedBundle(
210 __out BOOL* pfSuspend,
211 __out BOOTSTRAPPER_APPLY_RESTART* pRestart
212 );
213 +static HRESULT DoRestoreRelatedBundleActions(
214 + __in BURN_ENGINE_STATE* pEngineState,
215 + __in BURN_EXECUTE_CONTEXT* pContext,
216 + __out BOOTSTRAPPER_APPLY_RESTART* pRestart
217 + );
218 static HRESULT ExecuteExePackage(
219 __in BURN_ENGINE_STATE* pEngineState,
220 __in BURN_EXECUTE_ACTION* pExecuteAction,
@@ -788,6 +793,9 @@ extern "C" HRESULT ApplyExecute(
793 {
794 if (pCheckpoint->pActiveRollbackBoundary->fVital)
795 {
796 + hrRollback = DoRestoreRelatedBundleActions(pEngineState, &context, pRestart);
797 + IgnoreRollbackError(hrRollback, "Failed rollback actions");
798 +
799 // If the rollback boundary is vital, end execution here.
800 break;
801 }
@@ -2590,6 +2598,48 @@ LExit:
2598 return hr;
2599 }
2600
2601 +static 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 +
2639 +LExit:
2640 + return hr;
2641 +}
2642 +
2643 static HRESULT ExecuteExePackage(
2644 __in BURN_ENGINE_STATE* pEngineState,
2645 __in BURN_EXECUTE_ACTION* pExecuteAction,
src/burn/engine/bundlepackageengine.cpp
+1 -1
@@ -51,7 +51,7 @@ extern "C" HRESULT BundlePackageEnginePlanCalculatePackage(
51 execute = BOOTSTRAPPER_ACTION_STATE_NONE;
52 break;
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;
56 case BOOTSTRAPPER_REQUEST_STATE_ABSENT: __fallthrough;
57 case BOOTSTRAPPER_REQUEST_STATE_CACHE:
src/burn/engine/core.cpp
+2 -2
@@ -558,7 +558,7 @@ extern "C" HRESULT CorePlan(
558 ExitOnFailure(hr, "Failed to plan packages.");
559
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.");
563 }
564 }
@@ -2309,7 +2309,7 @@ static void LogRelatedBundles(
2309
2310 if (pRelatedBundle->fPlannable)
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 }
2314 }
2315 }
src/burn/engine/engine.mc
+1 -8
@@ -352,13 +352,6 @@ Language=English
352 Planned 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 .
354
355 -MessageId=202
356 -Severity=Success
357 -SymbolicName=MSG_PLANNED_BUNDLE_UX_CHANGED_REQUEST
358 -Language=English
359 -Planned bundle: %1!ls!, ba requested state: %2!hs! over default: %3!hs!
360 -.
361 -
355 MessageId=203
356 Severity=Success
357 SymbolicName=MSG_PLANNED_MSI_FEATURE
@@ -391,7 +384,7 @@ MessageId=207
384 Severity=Success
385 SymbolicName=MSG_PLANNED_RELATED_BUNDLE
386 Language=English
394 -Planned related bundle: %1!ls!, type: %2!hs!, default requested: %3!hs!, ba requested: %4!hs!, execute: %5!hs!, rollback: %6!hs!, dependency: %7!hs!
387 +Planned 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!
388 .
389
390 MessageId=208
src/burn/engine/package.h
-1
@@ -309,7 +309,6 @@ typedef struct _BURN_PACKAGE
309 LPCWSTR wzAncestors; // points directly into engine state.
310 LPCWSTR wzEngineWorkingDirectory; // points directly into engine state.
311
312 - BOOL fRepairable;
312 BOOL fSupportsBurnProtocol;
313
314 BURN_EXE_EXIT_CODE* rgExitCodes;
src/burn/engine/plan.cpp
+130 -13
@@ -103,6 +103,10 @@ static HRESULT AppendCleanAction(
103 __in BURN_PLAN* pPlan,
104 __out BURN_CLEAN_ACTION** ppCleanAction
105 );
106 +static HRESULT AppendRestoreRelatedBundleAction(
107 + __in BURN_PLAN* pPlan,
108 + __out BURN_EXECUTE_ACTION** ppExecuteAction
109 + );
110 static HRESULT ProcessPayloadGroup(
111 __in BURN_PLAN* pPlan,
112 __in BURN_PAYLOAD_GROUP* pPayloadGroup
@@ -196,6 +200,15 @@ extern "C" void PlanReset(
200 MemFree(pPlan->rgRollbackActions);
201 }
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 +
212 if (pPlan->rgCleanActions)
213 {
214 // Nothing needs to be freed inside clean actions today.
@@ -1276,6 +1289,9 @@ extern "C" HRESULT PlanRelatedBundlesBegin(
1289 continue;
1290 }
1291
1292 + pRelatedBundle->defaultRequestedRestore = BOOTSTRAPPER_REQUEST_STATE_NONE;
1293 + pRelatedBundle->requestedRestore = BOOTSTRAPPER_REQUEST_STATE_NONE;
1294 + pRelatedBundle->restore = BOOTSTRAPPER_ACTION_STATE_NONE;
1295 pRelatedBundle->package.defaultRequested = BOOTSTRAPPER_REQUEST_STATE_NONE;
1296 pRelatedBundle->package.requested = BOOTSTRAPPER_REQUEST_STATE_NONE;
1297
@@ -1312,12 +1328,6 @@ extern "C" HRESULT PlanRelatedBundlesBegin(
1328 hr = UserExperienceOnPlanRelatedBundle(pUserExperience, pRelatedBundle->package.sczId, &pRelatedBundle->package.requested);
1329 ExitOnRootFailure(hr, "BA aborted plan related bundle.");
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 -
1331 // If uninstalling and the dependent related bundle may be executed, ignore its provider key to allow for downgrades with ref-counting.
1332 if (BOOTSTRAPPER_ACTION_UNINSTALL == pPlan->action && BOOTSTRAPPER_RELATION_DEPENDENT == pRelatedBundle->relationType && BOOTSTRAPPER_REQUEST_STATE_NONE != pRelatedBundle->package.requested)
1333 {
@@ -1340,6 +1350,7 @@ LExit:
1350 }
1351
1352 extern "C" HRESULT PlanRelatedBundlesComplete(
1353 + __in BURN_USER_EXPERIENCE* pUserExperience,
1354 __in BURN_REGISTRATION* pRegistration,
1355 __in BURN_PLAN* pPlan,
1356 __in BURN_LOGGING* pLog,
@@ -1359,16 +1370,19 @@ extern "C" HRESULT PlanRelatedBundlesComplete(
1370 ExitOnFailure(hr, "Failed to create dictionary for planned packages.");
1371
1372 BOOL fExecutingAnyPackage = FALSE;
1373 + BOOL fInstallingAnyPackage = FALSE;
1374
1375 for (DWORD i = 0; i < pPlan->cExecuteActions; ++i)
1376 {
1377 + BOOTSTRAPPER_ACTION_STATE packageAction = BOOTSTRAPPER_ACTION_STATE_NONE;
1378 +
1379 switch (pPlan->rgExecuteActions[i].type)
1380 {
1381 case BURN_EXECUTE_ACTION_TYPE_RELATED_BUNDLE:
1368 - if (BOOTSTRAPPER_ACTION_STATE_NONE != pPlan->rgExecuteActions[i].relatedBundle.action)
1369 - {
1370 - fExecutingAnyPackage = TRUE;
1382 + packageAction = pPlan->rgExecuteActions[i].relatedBundle.action;
1383
1384 + if (BOOTSTRAPPER_ACTION_STATE_NONE != packageAction)
1385 + {
1386 BURN_PACKAGE* pPackage = &pPlan->rgExecuteActions[i].relatedBundle.pRelatedBundle->package;
1387 if (pPackage->cDependencyProviders)
1388 {
@@ -1380,21 +1394,24 @@ extern "C" HRESULT PlanRelatedBundlesComplete(
1394 break;
1395
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;
1398 break;
1399
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;
1402 break;
1403
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;
1406 break;
1407
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;
1410 break;
1411 }
1412 +
1413 + fExecutingAnyPackage |= BOOTSTRAPPER_ACTION_STATE_NONE != packageAction;
1414 + fInstallingAnyPackage |= BOOTSTRAPPER_ACTION_STATE_INSTALL == packageAction || BOOTSTRAPPER_ACTION_STATE_MINOR_UPGRADE == packageAction;
1415 }
1416
1417 for (DWORD i = 0; i < pRegistration->relatedBundles.cRelatedBundles; ++i)
@@ -1492,6 +1509,62 @@ extern "C" HRESULT PlanRelatedBundlesComplete(
1509 hr = DependencyPlanPackageComplete(&pRelatedBundle->package, pPlan);
1510 ExitOnFailure(hr, "Failed to complete plan dependency actions for related bundle package: %ls", pRelatedBundle->package.sczId);
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 + }
1568 }
1569
1570 LExit:
@@ -2269,6 +2342,23 @@ LExit:
2342 return hr;
2343 }
2344
2345 +static 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 +
2358 +LExit:
2359 + return hr;
2360 +}
2361 +
2362 static HRESULT ProcessPayloadGroup(
2363 __in BURN_PLAN* pPlan,
2364 __in BURN_PAYLOAD_GROUP* pPayloadGroup
@@ -2725,6 +2815,28 @@ static void ExecuteActionLog(
2815 }
2816 }
2817
2818 +static 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 +
2840 static void CleanActionLog(
2841 __in DWORD iAction,
2842 __in BURN_CLEAN_ACTION* pAction
@@ -2784,6 +2896,11 @@ extern "C" void PlanDump(
2896 ExecuteActionLog(i, pPlan->rgRollbackActions + i, TRUE);
2897 }
2898
2899 + for (DWORD i = 0; i < pPlan->cRestoreRelatedBundleActions; ++i)
2900 + {
2901 + RestoreRelatedBundleActionLog(i, pPlan->rgRestoreRelatedBundleActions + i);
2902 + }
2903 +
2904 for (DWORD i = 0; i < pPlan->cCleanActions; ++i)
2905 {
2906 CleanActionLog(i, pPlan->rgCleanActions + i);
src/burn/engine/plan.h
+4
@@ -280,6 +280,9 @@ typedef struct _BURN_PLAN
280 BURN_EXECUTE_ACTION* rgRollbackActions;
281 DWORD cRollbackActions;
282
283 + BURN_EXECUTE_ACTION* rgRestoreRelatedBundleActions;
284 + DWORD cRestoreRelatedBundleActions;
285 +
286 BURN_CLEAN_ACTION* rgCleanActions;
287 DWORD cCleanActions;
288
@@ -394,6 +397,7 @@ HRESULT PlanRelatedBundlesBegin(
397 __in BURN_PLAN* pPlan
398 );
399 HRESULT PlanRelatedBundlesComplete(
400 + __in BURN_USER_EXPERIENCE* pUserExperience,
401 __in BURN_REGISTRATION* pRegistration,
402 __in BURN_PLAN* pPlan,
403 __in BURN_LOGGING* pLog,
src/burn/engine/pseudobundle.cpp
-1
@@ -51,7 +51,6 @@ extern "C" HRESULT PseudoBundleInitializeRelated(
51 pPackage->fVital = FALSE;
52
53 pPackage->fPermanent = FALSE;
54 - pPackage->Bundle.fRepairable = TRUE;
54 pPackage->Bundle.fSupportsBurnProtocol = fSupportsBurnProtocol;
55
56 hr = StrAllocString(&pPackage->sczId, wzId, 0);
src/burn/engine/registration.h
+4
@@ -61,6 +61,10 @@ typedef struct _BURN_RELATED_BUNDLE
61 BOOL fPlannable;
62
63 BURN_PACKAGE package;
64 +
65 + BOOTSTRAPPER_REQUEST_STATE defaultRequestedRestore;
66 + BOOTSTRAPPER_REQUEST_STATE requestedRestore;
67 + BOOTSTRAPPER_ACTION_STATE restore;
68 } BURN_RELATED_BUNDLE;
69
70 typedef struct _BURN_RELATED_BUNDLES
src/burn/engine/userexperience.cpp
+30
@@ -2176,6 +2176,36 @@ LExit:
2176 return hr;
2177 }
2178
2179 +EXTERN_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 +
2205 +LExit:
2206 + return hr;
2207 +}
2208 +
2209 EXTERN_C BAAPI UserExperienceOnPlanRollbackBoundary(
2210 __in BURN_USER_EXPERIENCE* pUserExperience,
2211 __in_z LPCWSTR wzRollbackBoundaryId,
src/burn/engine/userexperience.h
+5
@@ -497,6 +497,11 @@ BAAPI UserExperienceOnPlanRelatedBundle(
497 __in_z LPCWSTR wzBundleId,
498 __inout BOOTSTRAPPER_REQUEST_STATE* pRequestedState
499 );
500 +BAAPI UserExperienceOnPlanRestoreRelatedBundle(
501 + __in BURN_USER_EXPERIENCE* pUserExperience,
502 + __in_z LPCWSTR wzBundleId,
503 + __inout BOOTSTRAPPER_REQUEST_STATE* pRequestedState
504 + );
505 BAAPI UserExperienceOnPlanRollbackBoundary(
506 __in BURN_USER_EXPERIENCE* pUserExperience,
507 __in_z LPCWSTR wzRollbackBoundaryId,
src/burn/test/BurnUnitTest/PlanTest.cpp
+64
@@ -169,6 +169,10 @@ namespace Bootstrapper
169 Assert::Equal(4ul, pPlan->cExecutePackagesTotal);
170 Assert::Equal(7ul, pPlan->cOverallProgressTicksTotal);
171
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;
177 Assert::Equal(dwIndex, pPlan->cCleanActions);
178
@@ -276,6 +280,9 @@ namespace Bootstrapper
280 Assert::Equal(3ul, pPlan->cExecutePackagesTotal);
281 Assert::Equal(3ul, pPlan->cOverallProgressTicksTotal);
282
283 + dwIndex = 0;
284 + Assert::Equal(dwIndex, pPlan->cRestoreRelatedBundleActions);
285 +
286 dwIndex = 0;
287 ValidateCleanAction(pPlan, dwIndex++, L"PackageC");
288 ValidateCleanAction(pPlan, dwIndex++, L"PackageB");
@@ -349,6 +356,9 @@ namespace Bootstrapper
356 Assert::Equal(1ul, pPlan->cExecutePackagesTotal);
357 Assert::Equal(1ul, pPlan->cOverallProgressTicksTotal);
358
359 + dwIndex = 0;
360 + Assert::Equal(dwIndex, pPlan->cRestoreRelatedBundleActions);
361 +
362 dwIndex = 0;
363 ValidateCleanAction(pPlan, dwIndex++, L"PackageA");
364 ValidateCleanCompatibleAction(pPlan, dwIndex++, L"PackageA");
@@ -437,6 +447,9 @@ namespace Bootstrapper
447 Assert::Equal(1ul, pPlan->cExecutePackagesTotal);
448 Assert::Equal(2ul, pPlan->cOverallProgressTicksTotal);
449
450 + dwIndex = 0;
451 + Assert::Equal(dwIndex, pPlan->cRestoreRelatedBundleActions);
452 +
453 dwIndex = 0;
454 Assert::Equal(dwIndex, pPlan->cCleanActions);
455
@@ -507,6 +520,9 @@ namespace Bootstrapper
520 Assert::Equal(0ul, pPlan->cExecutePackagesTotal);
521 Assert::Equal(1ul, pPlan->cOverallProgressTicksTotal);
522
523 + dwIndex = 0;
524 + Assert::Equal(dwIndex, pPlan->cRestoreRelatedBundleActions);
525 +
526 dwIndex = 0;
527 Assert::Equal(dwIndex, pPlan->cCleanActions);
528
@@ -578,6 +594,9 @@ namespace Bootstrapper
594 Assert::Equal(2ul, pPlan->cExecutePackagesTotal);
595 Assert::Equal(2ul, pPlan->cOverallProgressTicksTotal);
596
597 + dwIndex = 0;
598 + Assert::Equal(dwIndex, pPlan->cRestoreRelatedBundleActions);
599 +
600 dwIndex = 0;
601 ValidateCleanAction(pPlan, dwIndex++, L"PackageA");
602 Assert::Equal(dwIndex, pPlan->cCleanActions);
@@ -657,6 +676,10 @@ namespace Bootstrapper
676 Assert::Equal(2ul, pPlan->cExecutePackagesTotal);
677 Assert::Equal(3ul, pPlan->cOverallProgressTicksTotal);
678
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;
684 Assert::Equal(dwIndex, pPlan->cCleanActions);
685
@@ -743,6 +766,10 @@ namespace Bootstrapper
766 Assert::Equal(2ul, pPlan->cExecutePackagesTotal);
767 Assert::Equal(3ul, pPlan->cOverallProgressTicksTotal);
768
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;
774 Assert::Equal(dwIndex, pPlan->cCleanActions);
775
@@ -804,6 +831,9 @@ namespace Bootstrapper
831 Assert::Equal(0ul, pPlan->cExecutePackagesTotal);
832 Assert::Equal(0ul, pPlan->cOverallProgressTicksTotal);
833
834 + dwIndex = 0;
835 + Assert::Equal(dwIndex, pPlan->cRestoreRelatedBundleActions);
836 +
837 dwIndex = 0;
838 ValidateCleanAction(pPlan, dwIndex++, L"PackageA");
839 Assert::Equal(dwIndex, pPlan->cCleanActions);
@@ -878,6 +908,9 @@ namespace Bootstrapper
908 Assert::Equal(1ul, pPlan->cExecutePackagesTotal);
909 Assert::Equal(1ul, pPlan->cOverallProgressTicksTotal);
910
911 + dwIndex = 0;
912 + Assert::Equal(dwIndex, pPlan->cRestoreRelatedBundleActions);
913 +
914 dwIndex = 0;
915 ValidateCleanAction(pPlan, dwIndex++, L"PackageA");
916 Assert::Equal(dwIndex, pPlan->cCleanActions);
@@ -945,6 +978,9 @@ namespace Bootstrapper
978 Assert::Equal(0ul, pPlan->cExecutePackagesTotal);
979 Assert::Equal(0ul, pPlan->cOverallProgressTicksTotal);
980
981 + dwIndex = 0;
982 + Assert::Equal(dwIndex, pPlan->cRestoreRelatedBundleActions);
983 +
984 dwIndex = 0;
985 Assert::Equal(dwIndex, pPlan->cCleanActions);
986
@@ -1056,6 +1092,9 @@ namespace Bootstrapper
1092 Assert::Equal(2ul, pPlan->cExecutePackagesTotal);
1093 Assert::Equal(5ul, pPlan->cOverallProgressTicksTotal);
1094
1095 + dwIndex = 0;
1096 + Assert::Equal(dwIndex, pPlan->cRestoreRelatedBundleActions);
1097 +
1098 dwIndex = 0;
1099 Assert::Equal(dwIndex, pPlan->cCleanActions);
1100
@@ -1146,6 +1185,9 @@ namespace Bootstrapper
1185 Assert::Equal(2ul, pPlan->cExecutePackagesTotal);
1186 Assert::Equal(2ul, pPlan->cOverallProgressTicksTotal);
1187
1188 + dwIndex = 0;
1189 + Assert::Equal(dwIndex, pPlan->cRestoreRelatedBundleActions);
1190 +
1191 dwIndex = 0;
1192 ValidateCleanAction(pPlan, dwIndex++, L"PatchA");
1193 ValidateCleanAction(pPlan, dwIndex++, L"PackageA");
@@ -1829,6 +1871,28 @@ namespace Bootstrapper
1871 NativeAssert::StringEqual(wzName, pProvider->sczName);
1872 }
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 +
1896 void ValidateUninstallMsiCompatiblePackage(
1897 __in BURN_PLAN* pPlan,
1898 __in BOOL fRollback,
src/ext/Bal/wixstdba/WixStandardBootstrapperApplication.cpp
+10
@@ -2089,6 +2089,16 @@ private: // privates
2089 m_pfnBAFunctionsProc(BA_FUNCTIONS_MESSAGE_ONPLANCOMPATIBLEMSIPACKAGECOMPLETE, pArgs, pResults, m_pvBAFunctionsProcContext);
2090 }
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 +
2102
2103 public: //CBalBaseBootstrapperApplication
2104 virtual STDMETHODIMP Initialize(
src/ext/Bal/wixstdba/wixstdba.mc
+7
@@ -85,3 +85,10 @@ Language=English
85 WIXSTDBA: Planned rollback boundary: %1!ls!, wixstdba requested transaction: %2!hs!, bafunctions requested transaction: %3!hs!
86 .
87
88 +MessageId=9
89 +Severity=Success
90 +SymbolicName=MSG_WIXSTDBA_PLANNED_RESTORE_RELATED_BUNDLE
91 +Language=English
92 +WIXSTDBA: Planned restore related bundle: %1!ls!, wixstdba requested: %2!hs!, bafunctions requested: %3!hs!
93 +.
94 +
src/test/burn/TestData/UpgradeRelatedBundleTests/BundleAv3/BundleAv3.wixproj new
+16
@@ -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
src/test/burn/TestData/UpgradeRelatedBundleTests/BundleAv3/BundleAv3.wxs new
+11
@@ -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>
src/test/burn/TestData/UpgradeRelatedBundleTests/PackageAv3/PackageAv3.wixproj new
+7
@@ -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
src/test/burn/TestData/UpgradeRelatedBundleTests/PackageF/PackageF.wixproj new
+12
@@ -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
src/test/burn/WixToolsetTest.BurnE2E/DependencyTests.cs
+2 -2
@@ -539,7 +539,7 @@ namespace WixToolsetTest.BurnE2E
539 }
540 }
541
542 - [Fact(Skip = "https://github.com/wixtoolset/issues/issues/3421")]
542 + [Fact]
543 public void DoesntLoseDependenciesOnFailedMajorUpgradeBundleFromMajorUpdateMsiFifo()
544 {
545 var packageAv1 = this.CreatePackageInstaller("PackageAv1");
@@ -611,7 +611,7 @@ namespace WixToolsetTest.BurnE2E
611 packageGv2.VerifyInstalled(false);
612 }
613
614 - [Fact(Skip = "https://github.com/wixtoolset/issues/issues/3421")]
614 + [Fact]
615 public void DoesntLoseDependenciesOnFailedMajorUpgradeBundleFromMajorUpdateMsiLifo()
616 {
617 var packageAv1 = this.CreatePackageInstaller("PackageAv1");
src/test/burn/WixToolsetTest.BurnE2E/UpgradeRelatedBundleTests.cs
+27
@@ -12,6 +12,33 @@ namespace WixToolsetTest.BurnE2E
12 {
13 public UpgradeRelatedBundleTests(ITestOutputHelper testOutputHelper) : base(testOutputHelper) { }
14
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]
43 public void ReportsRelatedBundleMissingFromCache()
44 {