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
{