aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSean Hall <r.sean.hall@gmail.com>2022-01-14 21:37:24 -0600
committerSean Hall <r.sean.hall@gmail.com>2022-01-16 10:30:28 -0600
commitda1d1376953ef1c9afb32d5eee02b785e52e372e (patch)
tree0df8550960259d7b13f5cd90f04d21b5576f16b7
parentabe316b80fae80eba54b0b79e76b6362105fa098 (diff)
downloadwix-da1d1376953ef1c9afb32d5eee02b785e52e372e.tar.gz
wix-da1d1376953ef1c9afb32d5eee02b785e52e372e.tar.bz2
wix-da1d1376953ef1c9afb32d5eee02b785e52e372e.zip
Remove orphan compatible MSI packages.
Reimplements #3190
-rw-r--r--src/api/burn/WixToolset.BootstrapperCore.Native/inc/BootstrapperApplication.h61
-rw-r--r--src/api/burn/WixToolset.Mba.Core/BootstrapperApplication.cs99
-rw-r--r--src/api/burn/WixToolset.Mba.Core/EventArgs.cs135
-rw-r--r--src/api/burn/WixToolset.Mba.Core/IBootstrapperApplication.cs57
-rw-r--r--src/api/burn/WixToolset.Mba.Core/IDefaultBootstrapperApplication.cs22
-rw-r--r--src/api/burn/balutil/inc/BAFunctions.h4
-rw-r--r--src/api/burn/balutil/inc/BalBaseBAFunctions.h41
-rw-r--r--src/api/burn/balutil/inc/BalBaseBAFunctionsProc.h4
-rw-r--r--src/api/burn/balutil/inc/BalBaseBootstrapperApplication.h43
-rw-r--r--src/api/burn/balutil/inc/BalBaseBootstrapperApplicationProc.h48
-rw-r--r--src/api/burn/balutil/inc/IBootstrapperApplication.h34
-rw-r--r--src/burn/engine/apply.cpp162
-rw-r--r--src/burn/engine/core.cpp8
-rw-r--r--src/burn/engine/dependency.cpp123
-rw-r--r--src/burn/engine/dependency.h5
-rw-r--r--src/burn/engine/detect.cpp2
-rw-r--r--src/burn/engine/elevation.cpp303
-rw-r--r--src/burn/engine/elevation.h17
-rw-r--r--src/burn/engine/engine.mc23
-rw-r--r--src/burn/engine/logging.cpp43
-rw-r--r--src/burn/engine/logging.h7
-rw-r--r--src/burn/engine/msiengine.cpp181
-rw-r--r--src/burn/engine/msiengine.h14
-rw-r--r--src/burn/engine/package.cpp25
-rw-r--r--src/burn/engine/package.h34
-rw-r--r--src/burn/engine/plan.cpp83
-rw-r--r--src/burn/engine/plan.h10
-rw-r--r--src/burn/engine/registration.cpp4
-rw-r--r--src/burn/engine/userexperience.cpp118
-rw-r--r--src/burn/engine/userexperience.h26
-rw-r--r--src/burn/test/BurnUnitTest/PlanTest.cpp133
-rw-r--r--src/ext/Bal/wixstdba/WixStandardBootstrapperApplication.cpp46
-rw-r--r--src/ext/Bal/wixstdba/wixstdba.mc7
-rw-r--r--src/test/burn/TestData/DependencyTests/BundleAv2/BundleAv2.wixproj16
-rw-r--r--src/test/burn/TestData/DependencyTests/BundleAv2/BundleAv2.wxs21
-rw-r--r--src/test/burn/TestData/DependencyTests/PackageAv1/PackageA.props1
-rw-r--r--src/test/burn/TestData/DependencyTests/PackageAv1/PackageAv1.props7
-rw-r--r--src/test/burn/TestData/DependencyTests/PackageAv1/PackageAv1.wixproj2
-rw-r--r--src/test/burn/TestData/DependencyTests/PackageAv1_0_1/PackageAv1_0_1.wixproj2
-rw-r--r--src/test/burn/TestData/DependencyTests/PackageAv1_0_2/PackageAv1_0_2.wixproj2
-rw-r--r--src/test/burn/TestData/DependencyTests/PackageAv2/PackageAv2.wixproj13
-rw-r--r--src/test/burn/WixTestTools/BundleVerifier.cs4
-rw-r--r--src/test/burn/WixToolsetTest.BurnE2E/DependencyTests.cs58
43 files changed, 1964 insertions, 84 deletions
diff --git a/src/api/burn/WixToolset.BootstrapperCore.Native/inc/BootstrapperApplication.h b/src/api/burn/WixToolset.BootstrapperCore.Native/inc/BootstrapperApplication.h
index 4fbfc890..659901be 100644
--- a/src/api/burn/WixToolset.BootstrapperCore.Native/inc/BootstrapperApplication.h
+++ b/src/api/burn/WixToolset.BootstrapperCore.Native/inc/BootstrapperApplication.h
@@ -205,6 +205,10 @@ enum BOOTSTRAPPER_APPLICATION_MESSAGE
205 BOOTSTRAPPER_APPLICATION_MESSAGE_ONPLANROLLBACKBOUNDARY, 205 BOOTSTRAPPER_APPLICATION_MESSAGE_ONPLANROLLBACKBOUNDARY,
206 BOOTSTRAPPER_APPLICATION_MESSAGE_ONSETUPDATEBEGIN, 206 BOOTSTRAPPER_APPLICATION_MESSAGE_ONSETUPDATEBEGIN,
207 BOOTSTRAPPER_APPLICATION_MESSAGE_ONSETUPDATECOMPLETE, 207 BOOTSTRAPPER_APPLICATION_MESSAGE_ONSETUPDATECOMPLETE,
208 BOOTSTRAPPER_APPLICATION_MESSAGE_ONDETECTCOMPATIBLEMSIPACKAGE,
209 BOOTSTRAPPER_APPLICATION_MESSAGE_ONPLANCOMPATIBLEMSIPACKAGEBEGIN,
210 BOOTSTRAPPER_APPLICATION_MESSAGE_ONPLANCOMPATIBLEMSIPACKAGECOMPLETE,
211 BOOTSTRAPPER_APPLICATION_MESSAGE_ONPLANNEDCOMPATIBLEPACKAGE,
208}; 212};
209 213
210enum BOOTSTRAPPER_APPLYCOMPLETE_ACTION 214enum BOOTSTRAPPER_APPLYCOMPLETE_ACTION
@@ -648,6 +652,20 @@ struct BA_ONDETECTBEGIN_RESULTS
648 BOOL fCancel; 652 BOOL fCancel;
649}; 653};
650 654
655struct BA_ONDETECTCOMPATIBLEMSIPACKAGE_ARGS
656{
657 DWORD cbSize;
658 LPCWSTR wzPackageId;
659 LPCWSTR wzCompatiblePackageId;
660 LPCWSTR wzCompatiblePackageVersion;
661};
662
663struct BA_ONDETECTCOMPATIBLEMSIPACKAGE_RESULTS
664{
665 DWORD cbSize;
666 BOOL fCancel;
667};
668
651struct BA_ONDETECTCOMPLETE_ARGS 669struct BA_ONDETECTCOMPLETE_ARGS
652{ 670{
653 DWORD cbSize; 671 DWORD cbSize;
@@ -1023,6 +1041,36 @@ struct BA_ONPLANBEGIN_RESULTS
1023 BOOL fCancel; 1041 BOOL fCancel;
1024}; 1042};
1025 1043
1044struct BA_ONPLANCOMPATIBLEMSIPACKAGEBEGIN_ARGS
1045{
1046 DWORD cbSize;
1047 LPCWSTR wzPackageId;
1048 LPCWSTR wzCompatiblePackageId;
1049 LPCWSTR wzCompatiblePackageVersion;
1050 BOOL fRecommendedRemove;
1051};
1052
1053struct BA_ONPLANCOMPATIBLEMSIPACKAGEBEGIN_RESULTS
1054{
1055 DWORD cbSize;
1056 BOOL fCancel;
1057 BOOL fRequestRemove;
1058};
1059
1060struct BA_ONPLANCOMPATIBLEMSIPACKAGECOMPLETE_ARGS
1061{
1062 DWORD cbSize;
1063 LPCWSTR wzPackageId;
1064 LPCWSTR wzCompatiblePackageId;
1065 HRESULT hrStatus;
1066 BOOL fRequestedRemove;
1067};
1068
1069struct BA_ONPLANCOMPATIBLEMSIPACKAGECOMPLETE_RESULTS
1070{
1071 DWORD cbSize;
1072};
1073
1026struct BA_ONPLANCOMPLETE_ARGS 1074struct BA_ONPLANCOMPLETE_ARGS
1027{ 1075{
1028 DWORD cbSize; 1076 DWORD cbSize;
@@ -1086,6 +1134,19 @@ struct BA_ONPLANMSIPACKAGE_RESULTS
1086 BOOTSTRAPPER_MSI_FILE_VERSIONING fileVersioning; 1134 BOOTSTRAPPER_MSI_FILE_VERSIONING fileVersioning;
1087}; 1135};
1088 1136
1137struct BA_ONPLANNEDCOMPATIBLEPACKAGE_ARGS
1138{
1139 DWORD cbSize;
1140 LPCWSTR wzPackageId;
1141 LPCWSTR wzCompatiblePackageId;
1142 BOOL fRemove;
1143};
1144
1145struct BA_ONPLANNEDCOMPATIBLEPACKAGE_RESULTS
1146{
1147 DWORD cbSize;
1148};
1149
1089struct BA_ONPLANNEDPACKAGE_ARGS 1150struct BA_ONPLANNEDPACKAGE_ARGS
1090{ 1151{
1091 DWORD cbSize; 1152 DWORD cbSize;
diff --git a/src/api/burn/WixToolset.Mba.Core/BootstrapperApplication.cs b/src/api/burn/WixToolset.Mba.Core/BootstrapperApplication.cs
index 34b63a50..f277425e 100644
--- a/src/api/burn/WixToolset.Mba.Core/BootstrapperApplication.cs
+++ b/src/api/burn/WixToolset.Mba.Core/BootstrapperApplication.cs
@@ -63,6 +63,9 @@ namespace WixToolset.Mba.Core
63 63
64 /// <inheritdoc/> 64 /// <inheritdoc/>
65 public event EventHandler<DetectPackageBeginEventArgs> DetectPackageBegin; 65 public event EventHandler<DetectPackageBeginEventArgs> DetectPackageBegin;
66
67 /// <inheritdoc/>
68 public event EventHandler<DetectCompatibleMsiPackageEventArgs> DetectCompatibleMsiPackage;
66 69
67 /// <inheritdoc/> 70 /// <inheritdoc/>
68 public event EventHandler<DetectRelatedMsiPackageEventArgs> DetectRelatedMsiPackage; 71 public event EventHandler<DetectRelatedMsiPackageEventArgs> DetectRelatedMsiPackage;
@@ -92,6 +95,12 @@ namespace WixToolset.Mba.Core
92 public event EventHandler<PlanPackageBeginEventArgs> PlanPackageBegin; 95 public event EventHandler<PlanPackageBeginEventArgs> PlanPackageBegin;
93 96
94 /// <inheritdoc/> 97 /// <inheritdoc/>
98 public event EventHandler<PlanCompatibleMsiPackageBeginEventArgs> PlanCompatibleMsiPackageBegin;
99
100 /// <inheritdoc/>
101 public event EventHandler<PlanCompatibleMsiPackageCompleteEventArgs> PlanCompatibleMsiPackageComplete;
102
103 /// <inheritdoc/>
95 public event EventHandler<PlanPatchTargetEventArgs> PlanPatchTarget; 104 public event EventHandler<PlanPatchTargetEventArgs> PlanPatchTarget;
96 105
97 /// <inheritdoc/> 106 /// <inheritdoc/>
@@ -104,6 +113,9 @@ namespace WixToolset.Mba.Core
104 public event EventHandler<PlanPackageCompleteEventArgs> PlanPackageComplete; 113 public event EventHandler<PlanPackageCompleteEventArgs> PlanPackageComplete;
105 114
106 /// <inheritdoc/> 115 /// <inheritdoc/>
116 public event EventHandler<PlannedCompatiblePackageEventArgs> PlannedCompatiblePackage;
117
118 /// <inheritdoc/>
107 public event EventHandler<PlannedPackageEventArgs> PlannedPackage; 119 public event EventHandler<PlannedPackageEventArgs> PlannedPackage;
108 120
109 /// <inheritdoc/> 121 /// <inheritdoc/>
@@ -415,6 +427,19 @@ namespace WixToolset.Mba.Core
415 } 427 }
416 428
417 /// <summary> 429 /// <summary>
430 /// Called by the engine, raises the <see cref="DetectCompatibleMsiPackage"/> event.
431 /// </summary>
432 /// <param name="args">Additional arguments for this event.</param>
433 protected virtual void OnDetectCompatibleMsiPackage(DetectCompatibleMsiPackageEventArgs args)
434 {
435 EventHandler<DetectCompatibleMsiPackageEventArgs> handler = this.DetectCompatibleMsiPackage;
436 if (null != handler)
437 {
438 handler(this, args);
439 }
440 }
441
442 /// <summary>
418 /// Called by the engine, raises the <see cref="DetectRelatedMsiPackage"/> event. 443 /// Called by the engine, raises the <see cref="DetectRelatedMsiPackage"/> event.
419 /// </summary> 444 /// </summary>
420 /// <param name="args">Additional arguments for this event.</param> 445 /// <param name="args">Additional arguments for this event.</param>
@@ -531,6 +556,32 @@ namespace WixToolset.Mba.Core
531 } 556 }
532 557
533 /// <summary> 558 /// <summary>
559 /// Called by the engine, raises the <see cref="PlanCompatibleMsiPackageBegin"/> event.
560 /// </summary>
561 /// <param name="args">Additional arguments for this event.</param>
562 protected virtual void OnPlanCompatibleMsiPackageBegin(PlanCompatibleMsiPackageBeginEventArgs args)
563 {
564 EventHandler<PlanCompatibleMsiPackageBeginEventArgs> handler = this.PlanCompatibleMsiPackageBegin;
565 if (null != handler)
566 {
567 handler(this, args);
568 }
569 }
570
571 /// <summary>
572 /// Called by the engine, raises the <see cref="PlanCompatibleMsiPackageComplete"/> event.
573 /// </summary>
574 /// <param name="args">Additional arguments for this event.</param>
575 protected virtual void OnPlanCompatibleMsiPackageComplete(PlanCompatibleMsiPackageCompleteEventArgs args)
576 {
577 EventHandler<PlanCompatibleMsiPackageCompleteEventArgs> handler = this.PlanCompatibleMsiPackageComplete;
578 if (null != handler)
579 {
580 handler(this, args);
581 }
582 }
583
584 /// <summary>
534 /// Called by the engine, raises the <see cref="PlanPatchTarget"/> event. 585 /// Called by the engine, raises the <see cref="PlanPatchTarget"/> event.
535 /// </summary> 586 /// </summary>
536 /// <param name="args">Additional arguments for this event.</param> 587 /// <param name="args">Additional arguments for this event.</param>
@@ -583,6 +634,19 @@ namespace WixToolset.Mba.Core
583 } 634 }
584 635
585 /// <summary> 636 /// <summary>
637 /// Called by the engine, raises the <see cref="PlannedCompatiblePackage"/> event.
638 /// </summary>
639 /// <param name="args">Additional arguments for this event.</param>
640 protected virtual void OnPlannedCompatiblePackage(PlannedCompatiblePackageEventArgs args)
641 {
642 EventHandler<PlannedCompatiblePackageEventArgs> handler = this.PlannedCompatiblePackage;
643 if (null != handler)
644 {
645 handler(this, args);
646 }
647 }
648
649 /// <summary>
586 /// Called by the engine, raises the <see cref="PlannedPackage"/> event. 650 /// Called by the engine, raises the <see cref="PlannedPackage"/> event.
587 /// </summary> 651 /// </summary>
588 /// <param name="args">Additional arguments for this event.</param> 652 /// <param name="args">Additional arguments for this event.</param>
@@ -1363,6 +1427,15 @@ namespace WixToolset.Mba.Core
1363 return args.HResult; 1427 return args.HResult;
1364 } 1428 }
1365 1429
1430 int IBootstrapperApplication.OnDetectCompatibleMsiPackage(string wzPackageId, string wzCompatiblePackageId, string wzCompatiblePackageVersion, ref bool fCancel)
1431 {
1432 DetectCompatibleMsiPackageEventArgs args = new DetectCompatibleMsiPackageEventArgs(wzPackageId, wzCompatiblePackageId, wzCompatiblePackageVersion, fCancel);
1433 this.OnDetectCompatibleMsiPackage(args);
1434
1435 fCancel = args.Cancel;
1436 return args.HResult;
1437 }
1438
1366 int IBootstrapperApplication.OnDetectRelatedMsiPackage(string wzPackageId, string wzUpgradeCode, string wzProductCode, bool fPerMachine, string wzVersion, RelatedOperation operation, ref bool fCancel) 1439 int IBootstrapperApplication.OnDetectRelatedMsiPackage(string wzPackageId, string wzUpgradeCode, string wzProductCode, bool fPerMachine, string wzVersion, RelatedOperation operation, ref bool fCancel)
1367 { 1440 {
1368 DetectRelatedMsiPackageEventArgs args = new DetectRelatedMsiPackageEventArgs(wzPackageId, wzUpgradeCode, wzProductCode, fPerMachine, wzVersion, operation, fCancel); 1441 DetectRelatedMsiPackageEventArgs args = new DetectRelatedMsiPackageEventArgs(wzPackageId, wzUpgradeCode, wzProductCode, fPerMachine, wzVersion, operation, fCancel);
@@ -1445,6 +1518,24 @@ namespace WixToolset.Mba.Core
1445 return args.HResult; 1518 return args.HResult;
1446 } 1519 }
1447 1520
1521 int IBootstrapperApplication.OnPlanCompatibleMsiPackageBegin(string wzPackageId, string wzCompatiblePackageId, string wzCompatiblePackageVersion, bool recommendedRemove, ref bool pRequestedRemove, ref bool fCancel)
1522 {
1523 PlanCompatibleMsiPackageBeginEventArgs args = new PlanCompatibleMsiPackageBeginEventArgs(wzPackageId, wzCompatiblePackageId, wzCompatiblePackageVersion, recommendedRemove, pRequestedRemove, fCancel);
1524 this.OnPlanCompatibleMsiPackageBegin(args);
1525
1526 pRequestedRemove = args.RequestRemove;
1527 fCancel = args.Cancel;
1528 return args.HResult;
1529 }
1530
1531 int IBootstrapperApplication.OnPlanCompatibleMsiPackageComplete(string wzPackageId, string wzCompatiblePackageId, int hrStatus, bool requestedRemove)
1532 {
1533 PlanCompatibleMsiPackageCompleteEventArgs args = new PlanCompatibleMsiPackageCompleteEventArgs(wzPackageId, wzCompatiblePackageId, hrStatus, requestedRemove);
1534 this.OnPlanCompatibleMsiPackageComplete(args);
1535
1536 return args.HResult;
1537 }
1538
1448 int IBootstrapperApplication.OnPlanPatchTarget(string wzPackageId, string wzProductCode, RequestState recommendedState, ref RequestState pRequestedState, ref bool fCancel) 1539 int IBootstrapperApplication.OnPlanPatchTarget(string wzPackageId, string wzProductCode, RequestState recommendedState, ref RequestState pRequestedState, ref bool fCancel)
1449 { 1540 {
1450 PlanPatchTargetEventArgs args = new PlanPatchTargetEventArgs(wzPackageId, wzProductCode, recommendedState, pRequestedState, fCancel); 1541 PlanPatchTargetEventArgs args = new PlanPatchTargetEventArgs(wzPackageId, wzProductCode, recommendedState, pRequestedState, fCancel);
@@ -1486,6 +1577,14 @@ namespace WixToolset.Mba.Core
1486 return args.HResult; 1577 return args.HResult;
1487 } 1578 }
1488 1579
1580 int IBootstrapperApplication.OnPlannedCompatiblePackage(string wzPackageId, string wzCompatiblePackageId, bool remove)
1581 {
1582 var args = new PlannedCompatiblePackageEventArgs(wzPackageId, wzCompatiblePackageId, remove);
1583 this.OnPlannedCompatiblePackage(args);
1584
1585 return args.HResult;
1586 }
1587
1489 int IBootstrapperApplication.OnPlannedPackage(string wzPackageId, ActionState execute, ActionState rollback, bool fPlannedCache, bool fPlannedUncache) 1588 int IBootstrapperApplication.OnPlannedPackage(string wzPackageId, ActionState execute, ActionState rollback, bool fPlannedCache, bool fPlannedUncache)
1490 { 1589 {
1491 var args = new PlannedPackageEventArgs(wzPackageId, execute, rollback, fPlannedCache, fPlannedUncache); 1590 var args = new PlannedPackageEventArgs(wzPackageId, execute, rollback, fPlannedCache, fPlannedUncache);
diff --git a/src/api/burn/WixToolset.Mba.Core/EventArgs.cs b/src/api/burn/WixToolset.Mba.Core/EventArgs.cs
index 93831be6..d4d70651 100644
--- a/src/api/burn/WixToolset.Mba.Core/EventArgs.cs
+++ b/src/api/burn/WixToolset.Mba.Core/EventArgs.cs
@@ -496,6 +496,37 @@ namespace WixToolset.Mba.Core
496 } 496 }
497 497
498 /// <summary> 498 /// <summary>
499 /// Event arguments for <see cref="IDefaultBootstrapperApplication.DetectCompatibleMsiPackage"/>
500 /// </summary>
501 [Serializable]
502 public class DetectCompatibleMsiPackageEventArgs : CancellableHResultEventArgs
503 {
504 /// <summary />
505 public DetectCompatibleMsiPackageEventArgs(string packageId, string compatiblePackageId, string compatiblePackageVersion, bool cancelRecommendation)
506 : base(cancelRecommendation)
507 {
508 this.PackageId = packageId;
509 this.CompatiblePackageId = compatiblePackageId;
510 this.CompatiblePackageVersion = compatiblePackageVersion;
511 }
512
513 /// <summary>
514 /// Gets the identity of the package that was not detected.
515 /// </summary>
516 public string PackageId { get; private set; }
517
518 /// <summary>
519 /// Gets the identity of the compatible package that was detected.
520 /// </summary>
521 public string CompatiblePackageId { get; private set; }
522
523 /// <summary>
524 /// Gets the version of the compatible package that was detected.
525 /// </summary>
526 public string CompatiblePackageVersion { get; private set; }
527 }
528
529 /// <summary>
499 /// Event arguments for <see cref="IDefaultBootstrapperApplication.DetectRelatedMsiPackage"/> 530 /// Event arguments for <see cref="IDefaultBootstrapperApplication.DetectRelatedMsiPackage"/>
500 /// </summary> 531 /// </summary>
501 [Serializable] 532 [Serializable]
@@ -776,6 +807,80 @@ namespace WixToolset.Mba.Core
776 } 807 }
777 808
778 /// <summary> 809 /// <summary>
810 /// Event arguments for <see cref="IDefaultBootstrapperApplication.PlanCompatibleMsiPackageBegin"/>
811 /// </summary>
812 [Serializable]
813 public class PlanCompatibleMsiPackageBeginEventArgs : CancellableHResultEventArgs
814 {
815 /// <summary />
816 public PlanCompatibleMsiPackageBeginEventArgs(string packageId, string compatiblePackageId, string compatiblePackageVersion, bool recommendedRemove, bool requestRemove, bool cancelRecommendation)
817 : base(cancelRecommendation)
818 {
819 this.PackageId = packageId;
820 this.CompatiblePackageId = compatiblePackageId;
821 this.CompatiblePackageVersion = compatiblePackageVersion;
822 this.RecommendedRemove = recommendedRemove;
823 this.RequestRemove = requestRemove;
824 }
825
826 /// <summary>
827 /// Gets the identity of the package that was not detected.
828 /// </summary>
829 public string PackageId { get; private set; }
830
831 /// <summary>
832 /// Gets the identity of the compatible package detected.
833 /// </summary>
834 public string CompatiblePackageId { get; private set; }
835
836 /// <summary>
837 /// Gets the version of the compatible package detected.
838 /// </summary>
839 public string CompatiblePackageVersion { get; private set; }
840
841 /// <summary>
842 /// Gets the recommended state to use for the compatible package for planning.
843 /// </summary>
844 public bool RecommendedRemove { get; private set; }
845
846 /// <summary>
847 /// Gets or sets whether to uninstall the compatible package.
848 /// </summary>
849 public bool RequestRemove { get; set; }
850 }
851
852 /// <summary>
853 /// Event arguments for <see cref="IDefaultBootstrapperApplication.PlanCompatibleMsiPackageComplete"/>
854 /// </summary>
855 [Serializable]
856 public class PlanCompatibleMsiPackageCompleteEventArgs : StatusEventArgs
857 {
858 /// <summary />
859 public PlanCompatibleMsiPackageCompleteEventArgs(string packageId, string compatiblePackageId, int hrStatus, bool requestedRemove)
860 : base(hrStatus)
861 {
862 this.PackageId = packageId;
863 this.CompatiblePackageId = compatiblePackageId;
864 this.RequestedRemove = requestedRemove;
865 }
866
867 /// <summary>
868 /// Gets the identity of the package planned for.
869 /// </summary>
870 public string PackageId { get; private set; }
871
872 /// <summary>
873 /// Gets the identity of the compatible package detected.
874 /// </summary>
875 public string CompatiblePackageId { get; private set; }
876
877 /// <summary>
878 /// Gets the requested state of the package.
879 /// </summary>
880 public bool RequestedRemove { get; private set; }
881 }
882
883 /// <summary>
779 /// Event arguments for <see cref="IDefaultBootstrapperApplication.PlanRollbackBoundary"/> 884 /// Event arguments for <see cref="IDefaultBootstrapperApplication.PlanRollbackBoundary"/>
780 /// </summary> 885 /// </summary>
781 [Serializable] 886 [Serializable]
@@ -981,6 +1086,36 @@ namespace WixToolset.Mba.Core
981 } 1086 }
982 1087
983 /// <summary> 1088 /// <summary>
1089 /// Event arguments for <see cref="IDefaultBootstrapperApplication.PlannedCompatiblePackage"/>
1090 /// </summary>
1091 [Serializable]
1092 public class PlannedCompatiblePackageEventArgs : HResultEventArgs
1093 {
1094 /// <summary />
1095 public PlannedCompatiblePackageEventArgs(string packageId, string compatiblePackageId, bool remove)
1096 {
1097 this.PackageId = packageId;
1098 this.CompatiblePackageId = compatiblePackageId;
1099 this.Remove = remove;
1100 }
1101
1102 /// <summary>
1103 /// Gets the identity of the package planned for.
1104 /// </summary>
1105 public string PackageId { get; private set; }
1106
1107 /// <summary>
1108 /// Gets the identity of the compatible package detected.
1109 /// </summary>
1110 public string CompatiblePackageId { get; private set; }
1111
1112 /// <summary>
1113 /// Gets the planned state of the package.
1114 /// </summary>
1115 public bool Remove { get; private set; }
1116 }
1117
1118 /// <summary>
984 /// Event arguments for <see cref="IDefaultBootstrapperApplication.PlannedPackage"/> 1119 /// Event arguments for <see cref="IDefaultBootstrapperApplication.PlannedPackage"/>
985 /// </summary> 1120 /// </summary>
986 [Serializable] 1121 [Serializable]
diff --git a/src/api/burn/WixToolset.Mba.Core/IBootstrapperApplication.cs b/src/api/burn/WixToolset.Mba.Core/IBootstrapperApplication.cs
index babd523a..05f96106 100644
--- a/src/api/burn/WixToolset.Mba.Core/IBootstrapperApplication.cs
+++ b/src/api/burn/WixToolset.Mba.Core/IBootstrapperApplication.cs
@@ -182,16 +182,20 @@ namespace WixToolset.Mba.Core
182 ); 182 );
183 183
184 /// <summary> 184 /// <summary>
185 /// See <see cref="IDefaultBootstrapperApplication.DetectCompatibleMsiPackage"/>.
186 /// </summary>
187 [PreserveSig]
188 [return: MarshalAs(UnmanagedType.I4)]
189 int OnDetectCompatibleMsiPackage(
190 [MarshalAs(UnmanagedType.LPWStr)] string wzPackageId,
191 [MarshalAs(UnmanagedType.LPWStr)] string wzCompatiblePackageId,
192 [MarshalAs(UnmanagedType.LPWStr)] string wzCompatiblePackageVersion,
193 [MarshalAs(UnmanagedType.Bool)] ref bool fCancel
194 );
195
196 /// <summary>
185 /// See <see cref="IDefaultBootstrapperApplication.DetectRelatedMsiPackage"/>. 197 /// See <see cref="IDefaultBootstrapperApplication.DetectRelatedMsiPackage"/>.
186 /// </summary> 198 /// </summary>
187 /// <param name="wzPackageId"></param>
188 /// <param name="wzUpgradeCode"></param>
189 /// <param name="wzProductCode"></param>
190 /// <param name="fPerMachine"></param>
191 /// <param name="wzVersion"></param>
192 /// <param name="operation"></param>
193 /// <param name="fCancel"></param>
194 /// <returns></returns>
195 [PreserveSig] 199 [PreserveSig]
196 [return: MarshalAs(UnmanagedType.I4)] 200 [return: MarshalAs(UnmanagedType.I4)]
197 int OnDetectRelatedMsiPackage( 201 int OnDetectRelatedMsiPackage(
@@ -318,6 +322,32 @@ namespace WixToolset.Mba.Core
318 ); 322 );
319 323
320 /// <summary> 324 /// <summary>
325 /// See <see cref="IDefaultBootstrapperApplication.PlanCompatibleMsiPackageBegin"/>.
326 /// </summary>
327 [PreserveSig]
328 [return: MarshalAs(UnmanagedType.I4)]
329 int OnPlanCompatibleMsiPackageBegin(
330 [MarshalAs(UnmanagedType.LPWStr)] string wzPackageId,
331 [MarshalAs(UnmanagedType.LPWStr)] string wzCompatiblePackageId,
332 [MarshalAs(UnmanagedType.LPWStr)] string wzCompatiblePackageVersion,
333 [MarshalAs(UnmanagedType.Bool)] bool fRecommendedRemove,
334 [MarshalAs(UnmanagedType.Bool)] ref bool fRequestRemove,
335 [MarshalAs(UnmanagedType.Bool)] ref bool fCancel
336 );
337
338 /// <summary>
339 /// See <see cref="IDefaultBootstrapperApplication.PlanCompatibleMsiPackageComplete"/>.
340 /// </summary>
341 [PreserveSig]
342 [return: MarshalAs(UnmanagedType.I4)]
343 int OnPlanCompatibleMsiPackageComplete(
344 [MarshalAs(UnmanagedType.LPWStr)] string wzPackageId,
345 [MarshalAs(UnmanagedType.LPWStr)] string wzCompatiblePackageId,
346 int hrStatus,
347 [MarshalAs(UnmanagedType.Bool)] bool fRequestedRemove
348 );
349
350 /// <summary>
321 /// See <see cref="IDefaultBootstrapperApplication.PlanPatchTarget"/>. 351 /// See <see cref="IDefaultBootstrapperApplication.PlanPatchTarget"/>.
322 /// </summary> 352 /// </summary>
323 /// <param name="wzPackageId"></param> 353 /// <param name="wzPackageId"></param>
@@ -388,6 +418,17 @@ namespace WixToolset.Mba.Core
388 ); 418 );
389 419
390 /// <summary> 420 /// <summary>
421 /// See <see cref="IDefaultBootstrapperApplication.PlannedCompatiblePackage"/>.
422 /// </summary>
423 [PreserveSig]
424 [return: MarshalAs(UnmanagedType.I4)]
425 int OnPlannedCompatiblePackage(
426 [MarshalAs(UnmanagedType.LPWStr)] string wzPackageId,
427 [MarshalAs(UnmanagedType.LPWStr)] string wzCompatiblePackageId,
428 [MarshalAs(UnmanagedType.Bool)] bool fRemove
429 );
430
431 /// <summary>
391 /// See <see cref="IDefaultBootstrapperApplication.PlannedPackage"/>. 432 /// See <see cref="IDefaultBootstrapperApplication.PlannedPackage"/>.
392 /// </summary> 433 /// </summary>
393 [PreserveSig] 434 [PreserveSig]
diff --git a/src/api/burn/WixToolset.Mba.Core/IDefaultBootstrapperApplication.cs b/src/api/burn/WixToolset.Mba.Core/IDefaultBootstrapperApplication.cs
index e809a965..ce06408e 100644
--- a/src/api/burn/WixToolset.Mba.Core/IDefaultBootstrapperApplication.cs
+++ b/src/api/burn/WixToolset.Mba.Core/IDefaultBootstrapperApplication.cs
@@ -134,6 +134,11 @@ namespace WixToolset.Mba.Core
134 event EventHandler<DetectBeginEventArgs> DetectBegin; 134 event EventHandler<DetectBeginEventArgs> DetectBegin;
135 135
136 /// <summary> 136 /// <summary>
137 /// Fired when a package was not detected but a package using the same provider key was.
138 /// </summary>
139 event EventHandler<DetectCompatibleMsiPackageEventArgs> DetectCompatibleMsiPackage;
140
141 /// <summary>
137 /// Fired when the detection phase has completed. 142 /// Fired when the detection phase has completed.
138 /// </summary> 143 /// </summary>
139 event EventHandler<DetectCompleteEventArgs> DetectComplete; 144 event EventHandler<DetectCompleteEventArgs> DetectComplete;
@@ -269,6 +274,16 @@ namespace WixToolset.Mba.Core
269 event EventHandler<PlanBeginEventArgs> PlanBegin; 274 event EventHandler<PlanBeginEventArgs> PlanBegin;
270 275
271 /// <summary> 276 /// <summary>
277 /// Fired when the engine plans a new, compatible package using the same provider key.
278 /// </summary>
279 event EventHandler<PlanCompatibleMsiPackageBeginEventArgs> PlanCompatibleMsiPackageBegin;
280
281 /// <summary>
282 /// Fired when the engine has completed planning the installation of a specific package.
283 /// </summary>
284 event EventHandler<PlanCompatibleMsiPackageCompleteEventArgs> PlanCompatibleMsiPackageComplete;
285
286 /// <summary>
272 /// Fired when the engine has completed planning the installation. 287 /// Fired when the engine has completed planning the installation.
273 /// </summary> 288 /// </summary>
274 event EventHandler<PlanCompleteEventArgs> PlanComplete; 289 event EventHandler<PlanCompleteEventArgs> PlanComplete;
@@ -279,6 +294,11 @@ namespace WixToolset.Mba.Core
279 event EventHandler<PlanForwardCompatibleBundleEventArgs> PlanForwardCompatibleBundle; 294 event EventHandler<PlanForwardCompatibleBundleEventArgs> PlanForwardCompatibleBundle;
280 295
281 /// <summary> 296 /// <summary>
297 /// Fired when the engine has completed planning a compatible package.
298 /// </summary>
299 event EventHandler<PlannedCompatiblePackageEventArgs> PlannedCompatiblePackage;
300
301 /// <summary>
282 /// Fired when the engine has completed planning a package. 302 /// Fired when the engine has completed planning a package.
283 /// </summary> 303 /// </summary>
284 event EventHandler<PlannedPackageEventArgs> PlannedPackage; 304 event EventHandler<PlannedPackageEventArgs> PlannedPackage;
@@ -399,4 +419,4 @@ namespace WixToolset.Mba.Core
399 /// </summary> 419 /// </summary>
400 event EventHandler<UnregisterCompleteEventArgs> UnregisterComplete; 420 event EventHandler<UnregisterCompleteEventArgs> UnregisterComplete;
401 } 421 }
402} \ No newline at end of file 422}
diff --git a/src/api/burn/balutil/inc/BAFunctions.h b/src/api/burn/balutil/inc/BAFunctions.h
index 2698a6e3..84359d65 100644
--- a/src/api/burn/balutil/inc/BAFunctions.h
+++ b/src/api/burn/balutil/inc/BAFunctions.h
@@ -84,6 +84,10 @@ enum BA_FUNCTIONS_MESSAGE
84 BA_FUNCTIONS_MESSAGE_ONPLANROLLBACKBOUNDARY = BOOTSTRAPPER_APPLICATION_MESSAGE_ONPLANROLLBACKBOUNDARY, 84 BA_FUNCTIONS_MESSAGE_ONPLANROLLBACKBOUNDARY = BOOTSTRAPPER_APPLICATION_MESSAGE_ONPLANROLLBACKBOUNDARY,
85 BA_FUNCTIONS_MESSAGE_ONSETUPDATEBEGIN = BOOTSTRAPPER_APPLICATION_MESSAGE_ONSETUPDATEBEGIN, 85 BA_FUNCTIONS_MESSAGE_ONSETUPDATEBEGIN = BOOTSTRAPPER_APPLICATION_MESSAGE_ONSETUPDATEBEGIN,
86 BA_FUNCTIONS_MESSAGE_ONSETUPDATECOMPLETE = BOOTSTRAPPER_APPLICATION_MESSAGE_ONSETUPDATECOMPLETE, 86 BA_FUNCTIONS_MESSAGE_ONSETUPDATECOMPLETE = BOOTSTRAPPER_APPLICATION_MESSAGE_ONSETUPDATECOMPLETE,
87 BA_FUNCTIONS_MESSAGE_ONDETECTCOMPATIBLEMSIPACKAGE = BOOTSTRAPPER_APPLICATION_MESSAGE_ONDETECTCOMPATIBLEMSIPACKAGE,
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,
87 91
88 BA_FUNCTIONS_MESSAGE_ONTHEMELOADED = 1024, 92 BA_FUNCTIONS_MESSAGE_ONTHEMELOADED = 1024,
89 BA_FUNCTIONS_MESSAGE_WNDPROC, 93 BA_FUNCTIONS_MESSAGE_WNDPROC,
diff --git a/src/api/burn/balutil/inc/BalBaseBAFunctions.h b/src/api/burn/balutil/inc/BalBaseBAFunctions.h
index 22e16f1b..c6d0924f 100644
--- a/src/api/burn/balutil/inc/BalBaseBAFunctions.h
+++ b/src/api/burn/balutil/inc/BalBaseBAFunctions.h
@@ -182,6 +182,16 @@ public: // IBootstrapperApplication
182 return S_OK; 182 return S_OK;
183 } 183 }
184 184
185 virtual STDMETHODIMP OnDetectCompatibleMsiPackage(
186 __in_z LPCWSTR /*wzPackageId*/,
187 __in_z LPCWSTR /*wzCompatiblePackageId*/,
188 __in LPCWSTR /*wzCompatiblePackageVersion*/,
189 __inout BOOL* /*pfCancel*/
190 )
191 {
192 return S_OK;
193 }
194
185 virtual STDMETHODIMP OnDetectRelatedMsiPackage( 195 virtual STDMETHODIMP OnDetectRelatedMsiPackage(
186 __in_z LPCWSTR /*wzPackageId*/, 196 __in_z LPCWSTR /*wzPackageId*/,
187 __in_z LPCWSTR /*wzUpgradeCode*/, 197 __in_z LPCWSTR /*wzUpgradeCode*/,
@@ -276,6 +286,28 @@ public: // IBootstrapperApplication
276 return S_OK; 286 return S_OK;
277 } 287 }
278 288
289 virtual STDMETHODIMP OnPlanCompatibleMsiPackageBegin(
290 __in_z LPCWSTR /*wzPackageId*/,
291 __in_z LPCWSTR /*wzCompatiblePackageId*/,
292 __in LPCWSTR /*wzCompatiblePackageVersion*/,
293 __in BOOL /*fRecommendedRemove*/,
294 __inout BOOL* /*pfRequestRemove*/,
295 __inout BOOL* /*pfCancel*/
296 )
297 {
298 return S_OK;
299 }
300
301 virtual STDMETHODIMP OnPlanCompatibleMsiPackageComplete(
302 __in_z LPCWSTR /*wzPackageId*/,
303 __in_z LPCWSTR /*wzCompatiblePackageId*/,
304 __in HRESULT /*hrStatus*/,
305 __in BOOL /*fRequestedRemove*/
306 )
307 {
308 return S_OK;
309 }
310
279 virtual STDMETHODIMP OnPlanPatchTarget( 311 virtual STDMETHODIMP OnPlanPatchTarget(
280 __in_z LPCWSTR /*wzPackageId*/, 312 __in_z LPCWSTR /*wzPackageId*/,
281 __in_z LPCWSTR /*wzProductCode*/, 313 __in_z LPCWSTR /*wzProductCode*/,
@@ -322,6 +354,15 @@ public: // IBootstrapperApplication
322 return S_OK; 354 return S_OK;
323 } 355 }
324 356
357 virtual STDMETHODIMP OnPlannedCompatiblePackage(
358 __in_z LPCWSTR /*wzPackageId*/,
359 __in_z LPCWSTR /*wzCompatiblePackageId*/,
360 __in BOOL /*fRemove*/
361 )
362 {
363 return S_OK;
364 }
365
325 virtual STDMETHODIMP OnPlannedPackage( 366 virtual STDMETHODIMP OnPlannedPackage(
326 __in_z LPCWSTR /*wzPackageId*/, 367 __in_z LPCWSTR /*wzPackageId*/,
327 __in BOOTSTRAPPER_ACTION_STATE /*execute*/, 368 __in BOOTSTRAPPER_ACTION_STATE /*execute*/,
diff --git a/src/api/burn/balutil/inc/BalBaseBAFunctionsProc.h b/src/api/burn/balutil/inc/BalBaseBAFunctionsProc.h
index 1ab0df59..5d5ff098 100644
--- a/src/api/burn/balutil/inc/BalBaseBAFunctionsProc.h
+++ b/src/api/burn/balutil/inc/BalBaseBAFunctionsProc.h
@@ -155,6 +155,10 @@ static HRESULT WINAPI BalBaseBAFunctionsProc(
155 case BA_FUNCTIONS_MESSAGE_ONPLANROLLBACKBOUNDARY: 155 case BA_FUNCTIONS_MESSAGE_ONPLANROLLBACKBOUNDARY:
156 case BA_FUNCTIONS_MESSAGE_ONSETUPDATEBEGIN: 156 case BA_FUNCTIONS_MESSAGE_ONSETUPDATEBEGIN:
157 case BA_FUNCTIONS_MESSAGE_ONSETUPDATECOMPLETE: 157 case BA_FUNCTIONS_MESSAGE_ONSETUPDATECOMPLETE:
158 case BA_FUNCTIONS_MESSAGE_ONDETECTCOMPATIBLEMSIPACKAGE:
159 case BA_FUNCTIONS_MESSAGE_ONPLANCOMPATIBLEMSIPACKAGEBEGIN:
160 case BA_FUNCTIONS_MESSAGE_ONPLANCOMPATIBLEMSIPACKAGECOMPLETE:
161 case BA_FUNCTIONS_MESSAGE_ONPLANNEDCOMPATIBLEPACKAGE:
158 hr = BalBaseBootstrapperApplicationProc((BOOTSTRAPPER_APPLICATION_MESSAGE)message, pvArgs, pvResults, pvContext); 162 hr = BalBaseBootstrapperApplicationProc((BOOTSTRAPPER_APPLICATION_MESSAGE)message, pvArgs, pvResults, pvContext);
159 break; 163 break;
160 case BA_FUNCTIONS_MESSAGE_ONTHEMELOADED: 164 case BA_FUNCTIONS_MESSAGE_ONTHEMELOADED:
diff --git a/src/api/burn/balutil/inc/BalBaseBootstrapperApplication.h b/src/api/burn/balutil/inc/BalBaseBootstrapperApplication.h
index 631d3c62..e1a36fdf 100644
--- a/src/api/burn/balutil/inc/BalBaseBootstrapperApplication.h
+++ b/src/api/burn/balutil/inc/BalBaseBootstrapperApplication.h
@@ -189,6 +189,17 @@ public: // IBootstrapperApplication
189 return S_OK; 189 return S_OK;
190 } 190 }
191 191
192 virtual STDMETHODIMP OnDetectCompatibleMsiPackage(
193 __in_z LPCWSTR /*wzPackageId*/,
194 __in_z LPCWSTR /*wzCompatiblePackageId*/,
195 __in LPCWSTR /*wzCompatiblePackageVersion*/,
196 __inout BOOL* pfCancel
197 )
198 {
199 *pfCancel |= CheckCanceled();
200 return S_OK;
201 }
202
192 virtual STDMETHODIMP OnDetectRelatedMsiPackage( 203 virtual STDMETHODIMP OnDetectRelatedMsiPackage(
193 __in_z LPCWSTR /*wzPackageId*/, 204 __in_z LPCWSTR /*wzPackageId*/,
194 __in_z LPCWSTR /*wzUpgradeCode*/, 205 __in_z LPCWSTR /*wzUpgradeCode*/,
@@ -290,6 +301,29 @@ public: // IBootstrapperApplication
290 return S_OK; 301 return S_OK;
291 } 302 }
292 303
304 virtual STDMETHODIMP OnPlanCompatibleMsiPackageBegin(
305 __in_z LPCWSTR /*wzPackageId*/,
306 __in_z LPCWSTR /*wzCompatiblePackageId*/,
307 __in LPCWSTR /*wzCompatiblePackageVersion*/,
308 __in BOOL /*fRecommendedRemove*/,
309 __inout BOOL* /*pfRequestRemove*/,
310 __inout BOOL* pfCancel
311 )
312 {
313 *pfCancel |= CheckCanceled();
314 return S_OK;
315 }
316
317 virtual STDMETHODIMP OnPlanCompatibleMsiPackageComplete(
318 __in_z LPCWSTR /*wzPackageId*/,
319 __in_z LPCWSTR /*wzCompatiblePackageId*/,
320 __in HRESULT /*hrStatus*/,
321 __in BOOL /*fRequestedRemove*/
322 )
323 {
324 return S_OK;
325 }
326
293 virtual STDMETHODIMP OnPlanPatchTarget( 327 virtual STDMETHODIMP OnPlanPatchTarget(
294 __in_z LPCWSTR /*wzPackageId*/, 328 __in_z LPCWSTR /*wzPackageId*/,
295 __in_z LPCWSTR /*wzProductCode*/, 329 __in_z LPCWSTR /*wzProductCode*/,
@@ -339,6 +373,15 @@ public: // IBootstrapperApplication
339 return S_OK; 373 return S_OK;
340 } 374 }
341 375
376 virtual STDMETHODIMP OnPlannedCompatiblePackage(
377 __in_z LPCWSTR /*wzPackageId*/,
378 __in_z LPCWSTR /*wzCompatiblePackageId*/,
379 __in BOOL /*fRemove*/
380 )
381 {
382 return S_OK;
383 }
384
342 virtual STDMETHODIMP OnPlannedPackage( 385 virtual STDMETHODIMP OnPlannedPackage(
343 __in_z LPCWSTR /*wzPackageId*/, 386 __in_z LPCWSTR /*wzPackageId*/,
344 __in BOOTSTRAPPER_ACTION_STATE /*execute*/, 387 __in BOOTSTRAPPER_ACTION_STATE /*execute*/,
diff --git a/src/api/burn/balutil/inc/BalBaseBootstrapperApplicationProc.h b/src/api/burn/balutil/inc/BalBaseBootstrapperApplicationProc.h
index b9866e4b..1ee5258e 100644
--- a/src/api/burn/balutil/inc/BalBaseBootstrapperApplicationProc.h
+++ b/src/api/burn/balutil/inc/BalBaseBootstrapperApplicationProc.h
@@ -126,6 +126,15 @@ static HRESULT BalBaseBAProcOnDetectPackageBegin(
126 return pBA->OnDetectPackageBegin(pArgs->wzPackageId, &pResults->fCancel); 126 return pBA->OnDetectPackageBegin(pArgs->wzPackageId, &pResults->fCancel);
127} 127}
128 128
129static HRESULT BalBaseBAProcOnDetectCompatiblePackage(
130 __in IBootstrapperApplication* pBA,
131 __in BA_ONDETECTCOMPATIBLEMSIPACKAGE_ARGS* pArgs,
132 __inout BA_ONDETECTCOMPATIBLEMSIPACKAGE_RESULTS* pResults
133 )
134{
135 return pBA->OnDetectCompatibleMsiPackage(pArgs->wzPackageId, pArgs->wzCompatiblePackageId, pArgs->wzCompatiblePackageVersion, &pResults->fCancel);
136}
137
129static HRESULT BalBaseBAProcOnDetectRelatedMsiPackage( 138static HRESULT BalBaseBAProcOnDetectRelatedMsiPackage(
130 __in IBootstrapperApplication* pBA, 139 __in IBootstrapperApplication* pBA,
131 __in BA_ONDETECTRELATEDMSIPACKAGE_ARGS* pArgs, 140 __in BA_ONDETECTRELATEDMSIPACKAGE_ARGS* pArgs,
@@ -189,6 +198,24 @@ static HRESULT BalBaseBAProcOnPlanPackageBegin(
189 return pBA->OnPlanPackageBegin(pArgs->wzPackageId, pArgs->state, pArgs->fCached, pArgs->installCondition, pArgs->recommendedState, pArgs->recommendedCacheType, &pResults->requestedState, &pResults->requestedCacheType, &pResults->fCancel); 198 return pBA->OnPlanPackageBegin(pArgs->wzPackageId, pArgs->state, pArgs->fCached, pArgs->installCondition, pArgs->recommendedState, pArgs->recommendedCacheType, &pResults->requestedState, &pResults->requestedCacheType, &pResults->fCancel);
190} 199}
191 200
201static HRESULT BalBaseBAProcOnPlanCompatibleMsiPackageBegin(
202 __in IBootstrapperApplication* pBA,
203 __in BA_ONPLANCOMPATIBLEMSIPACKAGEBEGIN_ARGS* pArgs,
204 __inout BA_ONPLANCOMPATIBLEMSIPACKAGEBEGIN_RESULTS* pResults
205 )
206{
207 return pBA->OnPlanCompatibleMsiPackageBegin(pArgs->wzPackageId, pArgs->wzCompatiblePackageId, pArgs->wzCompatiblePackageVersion, pArgs->fRecommendedRemove, &pResults->fRequestRemove, &pResults->fCancel);
208}
209
210static HRESULT BalBaseBAProcOnPlanCompatibleMsiPackageComplete(
211 __in IBootstrapperApplication* pBA,
212 __in BA_ONPLANCOMPATIBLEMSIPACKAGECOMPLETE_ARGS* pArgs,
213 __inout BA_ONPLANCOMPATIBLEMSIPACKAGECOMPLETE_RESULTS* /*pResults*/
214 )
215{
216 return pBA->OnPlanCompatibleMsiPackageComplete(pArgs->wzPackageId, pArgs->wzCompatiblePackageId, pArgs->hrStatus, pArgs->fRequestedRemove);
217}
218
192static HRESULT BalBaseBAProcOnPlanPatchTarget( 219static HRESULT BalBaseBAProcOnPlanPatchTarget(
193 __in IBootstrapperApplication* pBA, 220 __in IBootstrapperApplication* pBA,
194 __in BA_ONPLANPATCHTARGET_ARGS* pArgs, 221 __in BA_ONPLANPATCHTARGET_ARGS* pArgs,
@@ -216,6 +243,15 @@ static HRESULT BalBaseBAProcOnPlanPackageComplete(
216 return pBA->OnPlanPackageComplete(pArgs->wzPackageId, pArgs->hrStatus, pArgs->requested); 243 return pBA->OnPlanPackageComplete(pArgs->wzPackageId, pArgs->hrStatus, pArgs->requested);
217} 244}
218 245
246static HRESULT BalBaseBAProcOnPlannedCompatiblePackage(
247 __in IBootstrapperApplication* pBA,
248 __in BA_ONPLANNEDCOMPATIBLEPACKAGE_ARGS* pArgs,
249 __inout BA_ONPLANNEDCOMPATIBLEPACKAGE_RESULTS* /*pResults*/
250 )
251{
252 return pBA->OnPlannedCompatiblePackage(pArgs->wzPackageId, pArgs->wzCompatiblePackageId, pArgs->fRemove);
253}
254
219static HRESULT BalBaseBAProcOnPlannedPackage( 255static HRESULT BalBaseBAProcOnPlannedPackage(
220 __in IBootstrapperApplication* pBA, 256 __in IBootstrapperApplication* pBA,
221 __in BA_ONPLANNEDPACKAGE_ARGS* pArgs, 257 __in BA_ONPLANNEDPACKAGE_ARGS* pArgs,
@@ -928,6 +964,18 @@ static HRESULT WINAPI BalBaseBootstrapperApplicationProc(
928 case BOOTSTRAPPER_APPLICATION_MESSAGE_ONSETUPDATECOMPLETE: 964 case BOOTSTRAPPER_APPLICATION_MESSAGE_ONSETUPDATECOMPLETE:
929 hr = BalBaseBAProcOnSetUpdateComplete(pBA, reinterpret_cast<BA_ONSETUPDATECOMPLETE_ARGS*>(pvArgs), reinterpret_cast<BA_ONSETUPDATECOMPLETE_RESULTS*>(pvResults)); 965 hr = BalBaseBAProcOnSetUpdateComplete(pBA, reinterpret_cast<BA_ONSETUPDATECOMPLETE_ARGS*>(pvArgs), reinterpret_cast<BA_ONSETUPDATECOMPLETE_RESULTS*>(pvResults));
930 break; 966 break;
967 case BOOTSTRAPPER_APPLICATION_MESSAGE_ONDETECTCOMPATIBLEMSIPACKAGE:
968 hr = BalBaseBAProcOnDetectCompatiblePackage(pBA, reinterpret_cast<BA_ONDETECTCOMPATIBLEMSIPACKAGE_ARGS*>(pvArgs), reinterpret_cast<BA_ONDETECTCOMPATIBLEMSIPACKAGE_RESULTS*>(pvResults));
969 break;
970 case BOOTSTRAPPER_APPLICATION_MESSAGE_ONPLANCOMPATIBLEMSIPACKAGEBEGIN:
971 hr = BalBaseBAProcOnPlanCompatibleMsiPackageBegin(pBA, reinterpret_cast<BA_ONPLANCOMPATIBLEMSIPACKAGEBEGIN_ARGS*>(pvArgs), reinterpret_cast<BA_ONPLANCOMPATIBLEMSIPACKAGEBEGIN_RESULTS*>(pvResults));
972 break;
973 case BOOTSTRAPPER_APPLICATION_MESSAGE_ONPLANCOMPATIBLEMSIPACKAGECOMPLETE:
974 hr = BalBaseBAProcOnPlanCompatibleMsiPackageComplete(pBA, reinterpret_cast<BA_ONPLANCOMPATIBLEMSIPACKAGECOMPLETE_ARGS*>(pvArgs), reinterpret_cast<BA_ONPLANCOMPATIBLEMSIPACKAGECOMPLETE_RESULTS*>(pvResults));
975 break;
976 case BOOTSTRAPPER_APPLICATION_MESSAGE_ONPLANNEDCOMPATIBLEPACKAGE:
977 hr = BalBaseBAProcOnPlannedCompatiblePackage(pBA, reinterpret_cast<BA_ONPLANNEDCOMPATIBLEPACKAGE_ARGS*>(pvArgs), reinterpret_cast<BA_ONPLANNEDCOMPATIBLEPACKAGE_RESULTS*>(pvResults));
978 break;
931 } 979 }
932 } 980 }
933 981
diff --git a/src/api/burn/balutil/inc/IBootstrapperApplication.h b/src/api/burn/balutil/inc/IBootstrapperApplication.h
index 577a705b..640f609d 100644
--- a/src/api/burn/balutil/inc/IBootstrapperApplication.h
+++ b/src/api/burn/balutil/inc/IBootstrapperApplication.h
@@ -101,6 +101,14 @@ DECLARE_INTERFACE_IID_(IBootstrapperApplication, IUnknown, "53C31D56-49C0-426B-A
101 __inout BOOL* pfCancel 101 __inout BOOL* pfCancel
102 ) = 0; 102 ) = 0;
103 103
104 // OnDetectCompatibleMsiPackage - called when the engine detects that a package is not installed but a newer package using the same provider key is.
105 STDMETHOD(OnDetectCompatibleMsiPackage)(
106 __in_z LPCWSTR wzPackageId,
107 __in_z LPCWSTR wzCompatiblePackageId,
108 __in_z LPCWSTR wzCompatiblePackageVersion,
109 __inout BOOL* pfCancel
110 ) = 0;
111
104 // OnDetectRelatedMsiPackage - called when the engine begins detects a related package. 112 // OnDetectRelatedMsiPackage - called when the engine begins detects a related package.
105 STDMETHOD(OnDetectRelatedMsiPackage)( 113 STDMETHOD(OnDetectRelatedMsiPackage)(
106 __in_z LPCWSTR wzPackageId, 114 __in_z LPCWSTR wzPackageId,
@@ -181,6 +189,25 @@ DECLARE_INTERFACE_IID_(IBootstrapperApplication, IUnknown, "53C31D56-49C0-426B-A
181 __inout BOOL* pfCancel 189 __inout BOOL* pfCancel
182 ) = 0; 190 ) = 0;
183 191
192 // OnPlanCompatibleMsiPackageBegin - called when the engine plans a newer, compatible package using the same provider key.
193 STDMETHOD(OnPlanCompatibleMsiPackageBegin)(
194 __in_z LPCWSTR wzPackageId,
195 __in_z LPCWSTR wzCompatiblePackageId,
196 __in_z LPCWSTR wzCompatiblePackageVersion,
197 __in BOOL fRecommendedRemove,
198 __inout BOOL* pfRequestRemove,
199 __inout BOOL* pfCancel
200 ) = 0;
201
202 // OnPlanCompatibleMsiPackageComplete - called after the engine plans the package.
203 //
204 STDMETHOD(OnPlanCompatibleMsiPackageComplete)(
205 __in_z LPCWSTR wzPackageId,
206 __in_z LPCWSTR wzCompatiblePackageId,
207 __in HRESULT hrStatus,
208 __in BOOL fRequestedRemove
209 ) = 0;
210
184 // OnPlanPatchTarget - called when the engine is about to plan a target 211 // OnPlanPatchTarget - called when the engine is about to plan a target
185 // of an MSP package. 212 // of an MSP package.
186 STDMETHOD(OnPlanPatchTarget)( 213 STDMETHOD(OnPlanPatchTarget)(
@@ -223,6 +250,13 @@ DECLARE_INTERFACE_IID_(IBootstrapperApplication, IUnknown, "53C31D56-49C0-426B-A
223 __in BOOTSTRAPPER_REQUEST_STATE requested 250 __in BOOTSTRAPPER_REQUEST_STATE requested
224 ) = 0; 251 ) = 0;
225 252
253 // OnPlannedCompatiblePackage - called after the engine has completed planning a compatible package.
254 STDMETHOD(OnPlannedCompatiblePackage)(
255 __in_z LPCWSTR wzPackageId,
256 __in_z LPCWSTR wzCompatiblePackageId,
257 __in BOOL fRemove
258 ) = 0;
259
226 // OnPlannedPackage - called after the engine has completed planning a package. 260 // OnPlannedPackage - called after the engine has completed planning a package.
227 STDMETHOD(OnPlannedPackage)( 261 STDMETHOD(OnPlannedPackage)(
228 __in_z LPCWSTR wzPackageId, 262 __in_z LPCWSTR wzPackageId,
diff --git a/src/burn/engine/apply.cpp b/src/burn/engine/apply.cpp
index 99884234..6bf3020c 100644
--- a/src/burn/engine/apply.cpp
+++ b/src/burn/engine/apply.cpp
@@ -60,7 +60,7 @@ typedef struct _BURN_EXECUTE_CONTEXT
60 BURN_USER_EXPERIENCE* pUX; 60 BURN_USER_EXPERIENCE* pUX;
61 BURN_APPLY_CONTEXT* pApplyContext; 61 BURN_APPLY_CONTEXT* pApplyContext;
62 BOOL fRollback; 62 BOOL fRollback;
63 BURN_PACKAGE* pExecutingPackage; 63 LPCWSTR wzExecutingPackageId;
64 DWORD cExecutedPackages; 64 DWORD cExecutedPackages;
65 DWORD cExecutePackagesTotal; 65 DWORD cExecutePackagesTotal;
66} BURN_EXECUTE_CONTEXT; 66} BURN_EXECUTE_CONTEXT;
@@ -278,6 +278,19 @@ static void ResetTransactionRegistrationState(
278 __in BURN_ENGINE_STATE* pEngineState, 278 __in BURN_ENGINE_STATE* pEngineState,
279 __in BOOL fCommit 279 __in BOOL fCommit
280 ); 280 );
281static HRESULT ExecuteUninstallMsiCompatiblePackage(
282 __in BURN_ENGINE_STATE* pEngineState,
283 __in BURN_EXECUTE_ACTION* pExecuteAction,
284 __in BURN_EXECUTE_CONTEXT* pContext,
285 __out BOOL* pfRetry,
286 __out BOOL* pfSuspend,
287 __out BOOTSTRAPPER_APPLY_RESTART* pRestart
288 );
289static HRESULT CleanCompatiblePackage(
290 __in BURN_CACHE* pCache,
291 __in HANDLE hElevatedPipe,
292 __in BURN_PACKAGE* pPackage
293 );
281static HRESULT CleanPackage( 294static HRESULT CleanPackage(
282 __in BURN_CACHE* pCache, 295 __in BURN_CACHE* pCache,
283 __in HANDLE hElevatedPipe, 296 __in HANDLE hElevatedPipe,
@@ -300,7 +313,8 @@ static HRESULT ReportOverallProgressTicks(
300static HRESULT ExecutePackageComplete( 313static HRESULT ExecutePackageComplete(
301 __in BURN_USER_EXPERIENCE* pUX, 314 __in BURN_USER_EXPERIENCE* pUX,
302 __in BURN_VARIABLES* pVariables, 315 __in BURN_VARIABLES* pVariables,
303 __in BURN_PACKAGE* pPackage, 316 __in LPCWSTR wzPackageId,
317 __in BOOL fPackageVital,
304 __in HRESULT hrOverall, 318 __in HRESULT hrOverall,
305 __in HRESULT hrExecute, 319 __in HRESULT hrExecute,
306 __in BOOL fRollback, 320 __in BOOL fRollback,
@@ -808,7 +822,20 @@ extern "C" void ApplyClean(
808 BURN_CLEAN_ACTION* pCleanAction = pPlan->rgCleanActions + i; 822 BURN_CLEAN_ACTION* pCleanAction = pPlan->rgCleanActions + i;
809 BURN_PACKAGE* pPackage = pCleanAction->pPackage; 823 BURN_PACKAGE* pPackage = pCleanAction->pPackage;
810 824
811 hr = CleanPackage(pPlan->pCache, hPipe, pPackage); 825 switch (pCleanAction->type)
826 {
827 case BURN_CLEAN_ACTION_TYPE_COMPATIBLE_PACKAGE:
828 hr = CleanCompatiblePackage(pPlan->pCache, hPipe, pPackage);
829 break;
830
831 case BURN_CLEAN_ACTION_TYPE_PACKAGE:
832 hr = CleanPackage(pPlan->pCache, hPipe, pPackage);
833 break;
834
835 default:
836 AssertSz(FALSE, "Unknown clean action.");
837 break;
838 }
812 } 839 }
813} 840}
814 841
@@ -2350,6 +2377,11 @@ static HRESULT DoExecuteAction(
2350 ExitOnFailure(hr, "Failed to execute commit MSI transaction action."); 2377 ExitOnFailure(hr, "Failed to execute commit MSI transaction action.");
2351 break; 2378 break;
2352 2379
2380 case BURN_EXECUTE_ACTION_TYPE_UNINSTALL_MSI_COMPATIBLE_PACKAGE:
2381 hr = ExecuteUninstallMsiCompatiblePackage(pEngineState, pExecuteAction, pContext, &fRetry, pfSuspend, &restart);
2382 ExitOnFailure(hr, "Failed to execute uninstall MSI compatible package.");
2383 break;
2384
2353 default: 2385 default:
2354 hr = E_UNEXPECTED; 2386 hr = E_UNEXPECTED;
2355 ExitOnFailure(hr, "Invalid execute action."); 2387 ExitOnFailure(hr, "Invalid execute action.");
@@ -2509,7 +2541,7 @@ static HRESULT ExecuteRelatedBundle(
2509 } 2541 }
2510 2542
2511 Assert(pContext->fRollback == fRollback); 2543 Assert(pContext->fRollback == fRollback);
2512 pContext->pExecutingPackage = pPackage; 2544 pContext->wzExecutingPackageId = pPackage->sczId;
2513 fBeginCalled = TRUE; 2545 fBeginCalled = TRUE;
2514 2546
2515 // Send package execute begin to BA. 2547 // Send package execute begin to BA.
@@ -2550,7 +2582,7 @@ static HRESULT ExecuteRelatedBundle(
2550LExit: 2582LExit:
2551 if (fBeginCalled) 2583 if (fBeginCalled)
2552 { 2584 {
2553 hr = ExecutePackageComplete(&pEngineState->userExperience, &pEngineState->variables, pPackage, hr, hrExecute, fRollback, pRestart, pfRetry, pfSuspend); 2585 hr = ExecutePackageComplete(&pEngineState->userExperience, &pEngineState->variables, pPackage->sczId, pPackage->fVital, hr, hrExecute, fRollback, pRestart, pfRetry, pfSuspend);
2554 } 2586 }
2555 2587
2556 return hr; 2588 return hr;
@@ -2581,7 +2613,7 @@ static HRESULT ExecuteExePackage(
2581 } 2613 }
2582 2614
2583 Assert(pContext->fRollback == fRollback); 2615 Assert(pContext->fRollback == fRollback);
2584 pContext->pExecutingPackage = pPackage; 2616 pContext->wzExecutingPackageId = pPackage->sczId;
2585 fBeginCalled = TRUE; 2617 fBeginCalled = TRUE;
2586 2618
2587 // Send package execute begin to BA. 2619 // Send package execute begin to BA.
@@ -2629,7 +2661,7 @@ LExit:
2629 2661
2630 if (fBeginCalled) 2662 if (fBeginCalled)
2631 { 2663 {
2632 hr = ExecutePackageComplete(&pEngineState->userExperience, &pEngineState->variables, pPackage, hr, hrExecute, fRollback, pRestart, pfRetry, pfSuspend); 2664 hr = ExecutePackageComplete(&pEngineState->userExperience, &pEngineState->variables, pPackage->sczId, pPackage->fVital, hr, hrExecute, fRollback, pRestart, pfRetry, pfSuspend);
2633 } 2665 }
2634 2666
2635 return hr; 2667 return hr;
@@ -2659,7 +2691,7 @@ static HRESULT ExecuteMsiPackage(
2659 } 2691 }
2660 2692
2661 Assert(pContext->fRollback == fRollback); 2693 Assert(pContext->fRollback == fRollback);
2662 pContext->pExecutingPackage = pPackage; 2694 pContext->wzExecutingPackageId = pPackage->sczId;
2663 fBeginCalled = TRUE; 2695 fBeginCalled = TRUE;
2664 2696
2665 // Send package execute begin to BA. 2697 // Send package execute begin to BA.
@@ -2693,7 +2725,7 @@ LExit:
2693 2725
2694 if (fBeginCalled) 2726 if (fBeginCalled)
2695 { 2727 {
2696 hr = ExecutePackageComplete(&pEngineState->userExperience, &pEngineState->variables, pPackage, hr, hrExecute, fRollback, pRestart, pfRetry, pfSuspend); 2728 hr = ExecutePackageComplete(&pEngineState->userExperience, &pEngineState->variables, pPackage->sczId, pPackage->fVital, hr, hrExecute, fRollback, pRestart, pfRetry, pfSuspend);
2697 } 2729 }
2698 2730
2699 return hr; 2731 return hr;
@@ -2723,7 +2755,7 @@ static HRESULT ExecuteMspPackage(
2723 } 2755 }
2724 2756
2725 Assert(pContext->fRollback == fRollback); 2757 Assert(pContext->fRollback == fRollback);
2726 pContext->pExecutingPackage = pPackage; 2758 pContext->wzExecutingPackageId = pPackage->sczId;
2727 fBeginCalled = TRUE; 2759 fBeginCalled = TRUE;
2728 2760
2729 // Send package execute begin to BA. 2761 // Send package execute begin to BA.
@@ -2766,7 +2798,7 @@ LExit:
2766 2798
2767 if (fBeginCalled) 2799 if (fBeginCalled)
2768 { 2800 {
2769 hr = ExecutePackageComplete(&pEngineState->userExperience, &pEngineState->variables, pPackage, hr, hrExecute, fRollback, pRestart, pfRetry, pfSuspend); 2801 hr = ExecutePackageComplete(&pEngineState->userExperience, &pEngineState->variables, pPackage->sczId, pPackage->fVital, hr, hrExecute, fRollback, pRestart, pfRetry, pfSuspend);
2770 } 2802 }
2771 2803
2772 return hr; 2804 return hr;
@@ -2798,7 +2830,7 @@ static HRESULT ExecuteMsuPackage(
2798 } 2830 }
2799 2831
2800 Assert(pContext->fRollback == fRollback); 2832 Assert(pContext->fRollback == fRollback);
2801 pContext->pExecutingPackage = pPackage; 2833 pContext->wzExecutingPackageId = pPackage->sczId;
2802 fBeginCalled = TRUE; 2834 fBeginCalled = TRUE;
2803 2835
2804 // Send package execute begin to BA. 2836 // Send package execute begin to BA.
@@ -2846,7 +2878,7 @@ LExit:
2846 2878
2847 if (fBeginCalled) 2879 if (fBeginCalled)
2848 { 2880 {
2849 hr = ExecutePackageComplete(&pEngineState->userExperience, &pEngineState->variables, pPackage, hr, hrExecute, fRollback, pRestart, pfRetry, pfSuspend); 2881 hr = ExecutePackageComplete(&pEngineState->userExperience, &pEngineState->variables, pPackage->sczId, pPackage->fVital, hr, hrExecute, fRollback, pRestart, pfRetry, pfSuspend);
2850 } 2882 }
2851 2883
2852 return hr; 2884 return hr;
@@ -3110,6 +3142,79 @@ static void ResetTransactionRegistrationState(
3110 } 3142 }
3111} 3143}
3112 3144
3145static HRESULT ExecuteUninstallMsiCompatiblePackage(
3146 __in BURN_ENGINE_STATE* pEngineState,
3147 __in BURN_EXECUTE_ACTION* pExecuteAction,
3148 __in BURN_EXECUTE_CONTEXT* pContext,
3149 __out BOOL* pfRetry,
3150 __out BOOL* pfSuspend,
3151 __out BOOTSTRAPPER_APPLY_RESTART* pRestart
3152 )
3153{
3154 HRESULT hr = S_OK;
3155 HRESULT hrExecute = S_OK;
3156 BOOL fRollback = FALSE;
3157 BOOTSTRAPPER_ACTION_STATE action = BOOTSTRAPPER_ACTION_STATE_UNINSTALL;
3158 INSTALLUILEVEL uiLevel = INSTALLUILEVEL_NONE;
3159 BOOL fDisableExternalUiHandler = FALSE;
3160 BOOL fBeginCalled = FALSE;
3161 BURN_PACKAGE* pParentPackage = pExecuteAction->uninstallMsiCompatiblePackage.pParentPackage;
3162
3163 Assert(pContext->fRollback == fRollback);
3164 pContext->wzExecutingPackageId = pParentPackage->compatiblePackage.compatibleEntry.sczId;
3165 fBeginCalled = TRUE;
3166
3167 // Send package execute begin to BA.
3168 hr = UserExperienceOnExecutePackageBegin(&pEngineState->userExperience, pContext->wzExecutingPackageId, !fRollback, action, uiLevel, fDisableExternalUiHandler);
3169 ExitOnRootFailure(hr, "BA aborted execute MSI compatible package begin.");
3170
3171 // execute package
3172 if (pParentPackage->fPerMachine)
3173 {
3174 hrExecute = ElevationUninstallMsiCompatiblePackage(pEngineState->companionConnection.hPipe, pEngineState->userExperience.hwndApply, pExecuteAction, &pEngineState->variables, fRollback, MsiExecuteMessageHandler, pContext, pRestart);
3175 ExitOnFailure(hrExecute, "Failed to uninstall per-machine MSI compatible package.");
3176 }
3177 else
3178 {
3179 hrExecute = MsiEngineUninstallCompatiblePackage(pEngineState->userExperience.hwndApply, pExecuteAction, pContext->pCache, &pEngineState->variables, fRollback, MsiExecuteMessageHandler, pContext, pRestart);
3180 ExitOnFailure(hrExecute, "Failed to uninstall per-user MSI compatible package.");
3181 }
3182
3183 pContext->cExecutedPackages += fRollback ? -1 : 1;
3184
3185 hr = ReportOverallProgressTicks(&pEngineState->userExperience, fRollback, pEngineState->plan.cOverallProgressTicksTotal, pContext->pApplyContext);
3186 ExitOnRootFailure(hr, "BA aborted MSI compatible package execute progress.");
3187
3188LExit:
3189
3190 if (fBeginCalled)
3191 {
3192 hr = ExecutePackageComplete(&pEngineState->userExperience, &pEngineState->variables, pContext->wzExecutingPackageId, FALSE, hr, hrExecute, fRollback, pRestart, pfRetry, pfSuspend);
3193 }
3194
3195 return hr;
3196}
3197
3198static HRESULT CleanCompatiblePackage(
3199 __in BURN_CACHE* pCache,
3200 __in HANDLE hElevatedPipe,
3201 __in BURN_PACKAGE* pPackage
3202 )
3203{
3204 HRESULT hr = S_OK;
3205
3206 if (pPackage->fPerMachine)
3207 {
3208 hr = ElevationCleanCompatiblePackage(hElevatedPipe, pPackage);
3209 }
3210 else
3211 {
3212 hr = CacheRemovePackage(pCache, FALSE, pPackage->compatiblePackage.compatibleEntry.sczId, pPackage->compatiblePackage.sczCacheId);
3213 }
3214
3215 return hr;
3216}
3217
3113static HRESULT CleanPackage( 3218static HRESULT CleanPackage(
3114 __in BURN_CACHE* pCache, 3219 __in BURN_CACHE* pCache,
3115 __in HANDLE hElevatedPipe, 3220 __in HANDLE hElevatedPipe,
@@ -3150,16 +3255,16 @@ static int GenericExecuteMessageHandler(
3150 case GENERIC_EXECUTE_MESSAGE_PROGRESS: 3255 case GENERIC_EXECUTE_MESSAGE_PROGRESS:
3151 { 3256 {
3152 DWORD dwOverallProgress = pContext->cExecutePackagesTotal ? (pContext->cExecutedPackages * 100 + pMessage->progress.dwPercentage) / (pContext->cExecutePackagesTotal) : 0; 3257 DWORD dwOverallProgress = pContext->cExecutePackagesTotal ? (pContext->cExecutedPackages * 100 + pMessage->progress.dwPercentage) / (pContext->cExecutePackagesTotal) : 0;
3153 UserExperienceOnExecuteProgress(pContext->pUX, pContext->pExecutingPackage->sczId, pMessage->progress.dwPercentage, dwOverallProgress, &nResult); // ignore return value. 3258 UserExperienceOnExecuteProgress(pContext->pUX, pContext->wzExecutingPackageId, pMessage->progress.dwPercentage, dwOverallProgress, &nResult); // ignore return value.
3154 } 3259 }
3155 break; 3260 break;
3156 3261
3157 case GENERIC_EXECUTE_MESSAGE_ERROR: 3262 case GENERIC_EXECUTE_MESSAGE_ERROR:
3158 UserExperienceOnError(pContext->pUX, BOOTSTRAPPER_ERROR_TYPE_EXE_PACKAGE, pContext->pExecutingPackage->sczId, pMessage->error.dwErrorCode, pMessage->error.wzMessage, pMessage->dwUIHint, 0, NULL, &nResult); // ignore return value. 3263 UserExperienceOnError(pContext->pUX, BOOTSTRAPPER_ERROR_TYPE_EXE_PACKAGE, pContext->wzExecutingPackageId, pMessage->error.dwErrorCode, pMessage->error.wzMessage, pMessage->dwUIHint, 0, NULL, &nResult); // ignore return value.
3159 break; 3264 break;
3160 3265
3161 case GENERIC_EXECUTE_MESSAGE_NETFX_FILES_IN_USE: 3266 case GENERIC_EXECUTE_MESSAGE_NETFX_FILES_IN_USE:
3162 UserExperienceOnExecuteFilesInUse(pContext->pUX, pContext->pExecutingPackage->sczId, pMessage->filesInUse.cFiles, pMessage->filesInUse.rgwzFiles, BOOTSTRAPPER_FILES_IN_USE_TYPE_NETFX, &nResult); // ignore return value. 3267 UserExperienceOnExecuteFilesInUse(pContext->pUX, pContext->wzExecutingPackageId, pMessage->filesInUse.cFiles, pMessage->filesInUse.rgwzFiles, BOOTSTRAPPER_FILES_IN_USE_TYPE_NETFX, &nResult); // ignore return value.
3163 fPassthrough = TRUE; 3268 fPassthrough = TRUE;
3164 break; 3269 break;
3165 } 3270 }
@@ -3188,25 +3293,25 @@ static int MsiExecuteMessageHandler(
3188 case WIU_MSI_EXECUTE_MESSAGE_PROGRESS: 3293 case WIU_MSI_EXECUTE_MESSAGE_PROGRESS:
3189 { 3294 {
3190 DWORD dwOverallProgress = pContext->cExecutePackagesTotal ? (pContext->cExecutedPackages * 100 + pMessage->progress.dwPercentage) / (pContext->cExecutePackagesTotal) : 0; 3295 DWORD dwOverallProgress = pContext->cExecutePackagesTotal ? (pContext->cExecutedPackages * 100 + pMessage->progress.dwPercentage) / (pContext->cExecutePackagesTotal) : 0;
3191 UserExperienceOnExecuteProgress(pContext->pUX, pContext->pExecutingPackage->sczId, pMessage->progress.dwPercentage, dwOverallProgress, &nResult); // ignore return value. 3296 UserExperienceOnExecuteProgress(pContext->pUX, pContext->wzExecutingPackageId, pMessage->progress.dwPercentage, dwOverallProgress, &nResult); // ignore return value.
3192 } 3297 }
3193 break; 3298 break;
3194 3299
3195 case WIU_MSI_EXECUTE_MESSAGE_ERROR: 3300 case WIU_MSI_EXECUTE_MESSAGE_ERROR:
3196 nResult = pMessage->nResultRecommendation; 3301 nResult = pMessage->nResultRecommendation;
3197 UserExperienceOnError(pContext->pUX, BOOTSTRAPPER_ERROR_TYPE_WINDOWS_INSTALLER, pContext->pExecutingPackage->sczId, pMessage->error.dwErrorCode, pMessage->error.wzMessage, pMessage->dwUIHint, pMessage->cData, pMessage->rgwzData, &nResult); // ignore return value. 3302 UserExperienceOnError(pContext->pUX, BOOTSTRAPPER_ERROR_TYPE_WINDOWS_INSTALLER, pContext->wzExecutingPackageId, pMessage->error.dwErrorCode, pMessage->error.wzMessage, pMessage->dwUIHint, pMessage->cData, pMessage->rgwzData, &nResult); // ignore return value.
3198 break; 3303 break;
3199 3304
3200 case WIU_MSI_EXECUTE_MESSAGE_MSI_MESSAGE: 3305 case WIU_MSI_EXECUTE_MESSAGE_MSI_MESSAGE:
3201 nResult = pMessage->nResultRecommendation; 3306 nResult = pMessage->nResultRecommendation;
3202 UserExperienceOnExecuteMsiMessage(pContext->pUX, pContext->pExecutingPackage->sczId, pMessage->msiMessage.mt, pMessage->dwUIHint, pMessage->msiMessage.wzMessage, pMessage->cData, pMessage->rgwzData, &nResult); // ignore return value. 3307 UserExperienceOnExecuteMsiMessage(pContext->pUX, pContext->wzExecutingPackageId, pMessage->msiMessage.mt, pMessage->dwUIHint, pMessage->msiMessage.wzMessage, pMessage->cData, pMessage->rgwzData, &nResult); // ignore return value.
3203 break; 3308 break;
3204 3309
3205 case WIU_MSI_EXECUTE_MESSAGE_MSI_RM_FILES_IN_USE: 3310 case WIU_MSI_EXECUTE_MESSAGE_MSI_RM_FILES_IN_USE:
3206 fRestartManager = TRUE; 3311 fRestartManager = TRUE;
3207 __fallthrough; 3312 __fallthrough;
3208 case WIU_MSI_EXECUTE_MESSAGE_MSI_FILES_IN_USE: 3313 case WIU_MSI_EXECUTE_MESSAGE_MSI_FILES_IN_USE:
3209 UserExperienceOnExecuteFilesInUse(pContext->pUX, pContext->pExecutingPackage->sczId, pMessage->msiFilesInUse.cFiles, pMessage->msiFilesInUse.rgwzFiles, fRestartManager ? BOOTSTRAPPER_FILES_IN_USE_TYPE_MSI_RM : BOOTSTRAPPER_FILES_IN_USE_TYPE_MSI, &nResult); // ignore return value. 3314 UserExperienceOnExecuteFilesInUse(pContext->pUX, pContext->wzExecutingPackageId, pMessage->msiFilesInUse.cFiles, pMessage->msiFilesInUse.rgwzFiles, fRestartManager ? BOOTSTRAPPER_FILES_IN_USE_TYPE_MSI_RM : BOOTSTRAPPER_FILES_IN_USE_TYPE_MSI, &nResult); // ignore return value.
3210 fPassthrough = TRUE; 3315 fPassthrough = TRUE;
3211 break; 3316 break;
3212 } 3317 }
@@ -3246,7 +3351,8 @@ static HRESULT ReportOverallProgressTicks(
3246static HRESULT ExecutePackageComplete( 3351static HRESULT ExecutePackageComplete(
3247 __in BURN_USER_EXPERIENCE* pUX, 3352 __in BURN_USER_EXPERIENCE* pUX,
3248 __in BURN_VARIABLES* pVariables, 3353 __in BURN_VARIABLES* pVariables,
3249 __in BURN_PACKAGE* pPackage, 3354 __in LPCWSTR wzPackageId,
3355 __in BOOL fPackageVital,
3250 __in HRESULT hrOverall, 3356 __in HRESULT hrOverall,
3251 __in HRESULT hrExecute, 3357 __in HRESULT hrExecute,
3252 __in BOOL fRollback, 3358 __in BOOL fRollback,
@@ -3256,10 +3362,10 @@ static HRESULT ExecutePackageComplete(
3256 ) 3362 )
3257{ 3363{
3258 HRESULT hr = FAILED(hrOverall) ? hrOverall : hrExecute; // if the overall function failed use that otherwise use the execution result. 3364 HRESULT hr = FAILED(hrOverall) ? hrOverall : hrExecute; // if the overall function failed use that otherwise use the execution result.
3259 BOOTSTRAPPER_EXECUTEPACKAGECOMPLETE_ACTION executePackageCompleteAction = FAILED(hrOverall) || SUCCEEDED(hrExecute) || pPackage->fVital ? BOOTSTRAPPER_EXECUTEPACKAGECOMPLETE_ACTION_NONE : BOOTSTRAPPER_EXECUTEPACKAGECOMPLETE_ACTION_IGNORE; 3365 BOOTSTRAPPER_EXECUTEPACKAGECOMPLETE_ACTION executePackageCompleteAction = FAILED(hrOverall) || SUCCEEDED(hrExecute) || fPackageVital ? BOOTSTRAPPER_EXECUTEPACKAGECOMPLETE_ACTION_NONE : BOOTSTRAPPER_EXECUTEPACKAGECOMPLETE_ACTION_IGNORE;
3260 3366
3261 // Send package execute complete to BA. 3367 // Send package execute complete to BA.
3262 UserExperienceOnExecutePackageComplete(pUX, pPackage->sczId, hr, *pRestart, &executePackageCompleteAction); 3368 UserExperienceOnExecutePackageComplete(pUX, wzPackageId, hr, *pRestart, &executePackageCompleteAction);
3263 if (BOOTSTRAPPER_EXECUTEPACKAGECOMPLETE_ACTION_RESTART == executePackageCompleteAction) 3369 if (BOOTSTRAPPER_EXECUTEPACKAGECOMPLETE_ACTION_RESTART == executePackageCompleteAction)
3264 { 3370 {
3265 *pRestart = BOOTSTRAPPER_APPLY_RESTART_INITIATED; 3371 *pRestart = BOOTSTRAPPER_APPLY_RESTART_INITIATED;
@@ -3271,23 +3377,23 @@ static HRESULT ExecutePackageComplete(
3271 if (BOOTSTRAPPER_APPLY_RESTART_INITIATED == *pRestart) 3377 if (BOOTSTRAPPER_APPLY_RESTART_INITIATED == *pRestart)
3272 { 3378 {
3273 // Best effort to set the forced restart package variable. 3379 // Best effort to set the forced restart package variable.
3274 VariableSetString(pVariables, BURN_BUNDLE_FORCED_RESTART_PACKAGE, pPackage->sczId, TRUE, FALSE); 3380 VariableSetString(pVariables, BURN_BUNDLE_FORCED_RESTART_PACKAGE, wzPackageId, TRUE, FALSE);
3275 } 3381 }
3276 3382
3277 // If we're retrying, leave a message in the log file and say everything is okay. 3383 // If we're retrying, leave a message in the log file and say everything is okay.
3278 if (*pfRetry) 3384 if (*pfRetry)
3279 { 3385 {
3280 LogId(REPORT_STANDARD, MSG_APPLY_RETRYING_PACKAGE, pPackage->sczId, hrExecute); 3386 LogId(REPORT_STANDARD, MSG_APPLY_RETRYING_PACKAGE, wzPackageId, hrExecute);
3281 hr = S_OK; 3387 hr = S_OK;
3282 } 3388 }
3283 else if (SUCCEEDED(hrOverall) && FAILED(hrExecute) && BOOTSTRAPPER_EXECUTEPACKAGECOMPLETE_ACTION_IGNORE == executePackageCompleteAction && !pPackage->fVital) // If we *only* failed to execute and the BA ignored this *not-vital* package, say everything is okay. 3389 else if (SUCCEEDED(hrOverall) && FAILED(hrExecute) && BOOTSTRAPPER_EXECUTEPACKAGECOMPLETE_ACTION_IGNORE == executePackageCompleteAction && !fPackageVital) // If we *only* failed to execute and the BA ignored this *not-vital* package, say everything is okay.
3284 { 3390 {
3285 LogId(REPORT_STANDARD, MSG_APPLY_CONTINUING_NONVITAL_PACKAGE, pPackage->sczId, hrExecute); 3391 LogId(REPORT_STANDARD, MSG_APPLY_CONTINUING_NONVITAL_PACKAGE, wzPackageId, hrExecute);
3286 hr = S_OK; 3392 hr = S_OK;
3287 } 3393 }
3288 else 3394 else
3289 { 3395 {
3290 LogId(REPORT_STANDARD, MSG_APPLY_COMPLETED_PACKAGE, LoggingRollbackOrExecute(fRollback), pPackage->sczId, hr, LoggingRestartToString(*pRestart)); 3396 LogId(REPORT_STANDARD, MSG_APPLY_COMPLETED_PACKAGE, LoggingRollbackOrExecute(fRollback), wzPackageId, hr, LoggingRestartToString(*pRestart));
3291 } 3397 }
3292 3398
3293 return hr; 3399 return hr;
diff --git a/src/burn/engine/core.cpp b/src/burn/engine/core.cpp
index d70810f2..7f7a915e 100644
--- a/src/burn/engine/core.cpp
+++ b/src/burn/engine/core.cpp
@@ -389,6 +389,7 @@ extern "C" HRESULT CoreDetect(
389 pPackage->currentState = BOOTSTRAPPER_PACKAGE_STATE_UNKNOWN; 389 pPackage->currentState = BOOTSTRAPPER_PACKAGE_STATE_UNKNOWN;
390 pPackage->cacheRegistrationState = BURN_PACKAGE_REGISTRATION_STATE_UNKNOWN; 390 pPackage->cacheRegistrationState = BURN_PACKAGE_REGISTRATION_STATE_UNKNOWN;
391 pPackage->installRegistrationState = BURN_PACKAGE_REGISTRATION_STATE_UNKNOWN; 391 pPackage->installRegistrationState = BURN_PACKAGE_REGISTRATION_STATE_UNKNOWN;
392 pPackage->compatiblePackage.fDetected = FALSE;
392 } 393 }
393 } 394 }
394 395
@@ -2240,7 +2241,7 @@ static void LogPackages(
2240 } 2241 }
2241 2242
2242 LogId(REPORT_STANDARD, MSG_PLANNED_PACKAGE, pPackage->sczId, LoggingPackageStateToString(pPackage->currentState), LoggingRequestStateToString(pPackage->defaultRequested), LoggingRequestStateToString(pPackage->requested), LoggingActionStateToString(pPackage->execute), LoggingActionStateToString(pPackage->rollback), LoggingCacheTypeToString(pPackage->authoredCacheType), LoggingCacheTypeToString(pPackage->cacheType), LoggingBoolToString(pPackage->fPlannedCache), LoggingBoolToString(pPackage->fPlannedUncache), LoggingDependencyActionToString(pPackage->dependencyExecute), LoggingPackageRegistrationStateToString(pPackage->fCanAffectRegistration, pPackage->expectedInstallRegistrationState), LoggingPackageRegistrationStateToString(pPackage->fCanAffectRegistration, pPackage->expectedCacheRegistrationState)); 2243 LogId(REPORT_STANDARD, MSG_PLANNED_PACKAGE, pPackage->sczId, LoggingPackageStateToString(pPackage->currentState), LoggingRequestStateToString(pPackage->defaultRequested), LoggingRequestStateToString(pPackage->requested), LoggingActionStateToString(pPackage->execute), LoggingActionStateToString(pPackage->rollback), LoggingCacheTypeToString(pPackage->authoredCacheType), LoggingCacheTypeToString(pPackage->cacheType), LoggingBoolToString(pPackage->fPlannedCache), LoggingBoolToString(pPackage->fPlannedUncache), LoggingDependencyActionToString(pPackage->dependencyExecute), LoggingPackageRegistrationStateToString(pPackage->fCanAffectRegistration, pPackage->expectedInstallRegistrationState), LoggingPackageRegistrationStateToString(pPackage->fCanAffectRegistration, pPackage->expectedCacheRegistrationState));
2243 2244
2244 if (BURN_PACKAGE_TYPE_MSI == pPackage->type) 2245 if (BURN_PACKAGE_TYPE_MSI == pPackage->type)
2245 { 2246 {
2246 if (pPackage->Msi.cFeatures) 2247 if (pPackage->Msi.cFeatures)
@@ -2266,6 +2267,11 @@ static void LogPackages(
2266 LogId(REPORT_STANDARD, MSG_PLANNED_SLIPSTREAMED_MSP_TARGET, pSlipstreamMsp->pMspPackage->sczId, LoggingActionStateToString(pSlipstreamMsp->execute), LoggingActionStateToString(pSlipstreamMsp->rollback)); 2267 LogId(REPORT_STANDARD, MSG_PLANNED_SLIPSTREAMED_MSP_TARGET, pSlipstreamMsp->pMspPackage->sczId, LoggingActionStateToString(pSlipstreamMsp->execute), LoggingActionStateToString(pSlipstreamMsp->rollback));
2267 } 2268 }
2268 } 2269 }
2270
2271 if (pPackage->compatiblePackage.fRemove)
2272 {
2273 LogId(REPORT_STANDARD, MSG_PLANNED_ORPHAN_PACKAGE_FROM_PROVIDER, pPackage->sczId, pPackage->compatiblePackage.compatibleEntry.sczId, pPackage->Msi.sczProductCode);
2274 }
2269 } 2275 }
2270 else if (BURN_PACKAGE_TYPE_MSP == pPackage->type && pPackage->Msp.cTargetProductCodes) 2276 else if (BURN_PACKAGE_TYPE_MSP == pPackage->type && pPackage->Msp.cTargetProductCodes)
2271 { 2277 {
diff --git a/src/burn/engine/dependency.cpp b/src/burn/engine/dependency.cpp
index 5d7e1a94..6ee95935 100644
--- a/src/burn/engine/dependency.cpp
+++ b/src/burn/engine/dependency.cpp
@@ -54,6 +54,10 @@ static HRESULT AddPackageDependencyActions(
54 __in const BURN_DEPENDENCY_ACTION dependencyRollbackAction 54 __in const BURN_DEPENDENCY_ACTION dependencyRollbackAction
55 ); 55 );
56 56
57static LPCWSTR GetPackageProviderId(
58 __in const BURN_PACKAGE* pPackage
59 );
60
57static HRESULT RegisterPackageProvider( 61static HRESULT RegisterPackageProvider(
58 __in const BURN_PACKAGE* pPackage 62 __in const BURN_PACKAGE* pPackage
59 ); 63 );
@@ -299,6 +303,9 @@ extern "C" HRESULT DependencyDetectChainPackage(
299 hr = DetectPackageDependents(pPackage, pRegistration); 303 hr = DetectPackageDependents(pPackage, pRegistration);
300 ExitOnFailure(hr, "Failed to detect dependents for package '%ls'", pPackage->sczId); 304 ExitOnFailure(hr, "Failed to detect dependents for package '%ls'", pPackage->sczId);
301 305
306 hr = DependencyDetectCompatibleEntry(pPackage, pRegistration);
307 ExitOnFailure(hr, "Failed to detect compatible package for package '%ls'", pPackage->sczId);
308
302LExit: 309LExit:
303 return hr; 310 return hr;
304} 311}
@@ -396,6 +403,8 @@ extern "C" HRESULT DependencyPlanPackageBegin(
396 STRINGDICT_HANDLE sdIgnoredDependents = NULL; 403 STRINGDICT_HANDLE sdIgnoredDependents = NULL;
397 BURN_DEPENDENCY_ACTION dependencyExecuteAction = BURN_DEPENDENCY_ACTION_NONE; 404 BURN_DEPENDENCY_ACTION dependencyExecuteAction = BURN_DEPENDENCY_ACTION_NONE;
398 BURN_DEPENDENCY_ACTION dependencyRollbackAction = BURN_DEPENDENCY_ACTION_NONE; 405 BURN_DEPENDENCY_ACTION dependencyRollbackAction = BURN_DEPENDENCY_ACTION_NONE;
406 BOOL fDependentBlocksUninstall = FALSE;
407 BOOL fAttemptingUninstall = BOOTSTRAPPER_ACTION_STATE_UNINSTALL == pPackage->execute || pPackage->compatiblePackage.fRemove;
399 408
400 pPackage->dependencyExecute = BURN_DEPENDENCY_ACTION_NONE; 409 pPackage->dependencyExecute = BURN_DEPENDENCY_ACTION_NONE;
401 pPackage->dependencyRollback = BURN_DEPENDENCY_ACTION_NONE; 410 pPackage->dependencyRollback = BURN_DEPENDENCY_ACTION_NONE;
@@ -415,7 +424,7 @@ extern "C" HRESULT DependencyPlanPackageBegin(
415 } 424 }
416 425
417 // If we're uninstalling the package, check if any dependents are registered. 426 // If we're uninstalling the package, check if any dependents are registered.
418 if (BOOTSTRAPPER_ACTION_STATE_UNINSTALL == pPackage->execute) 427 if (fAttemptingUninstall)
419 { 428 {
420 // Build up a list of dependents to ignore, including the current bundle. 429 // Build up a list of dependents to ignore, including the current bundle.
421 hr = GetIgnoredDependents(pPackage, pPlan, &sdIgnoredDependents); 430 hr = GetIgnoredDependents(pPackage, pPlan, &sdIgnoredDependents);
@@ -444,9 +453,9 @@ extern "C" HRESULT DependencyPlanPackageBegin(
444 { 453 {
445 hr = S_OK; 454 hr = S_OK;
446 455
447 if (!pPackage->fDependencyManagerWasHere) 456 if (!fDependentBlocksUninstall)
448 { 457 {
449 pPackage->fDependencyManagerWasHere = TRUE; 458 fDependentBlocksUninstall = TRUE;
450 459
451 LogId(REPORT_STANDARD, MSG_DEPENDENCY_PACKAGE_HASDEPENDENTS, pPackage->sczId); 460 LogId(REPORT_STANDARD, MSG_DEPENDENCY_PACKAGE_HASDEPENDENTS, pPackage->sczId);
452 } 461 }
@@ -459,14 +468,20 @@ extern "C" HRESULT DependencyPlanPackageBegin(
459 } 468 }
460 } 469 }
461 470
471 if (BOOTSTRAPPER_ACTION_STATE_UNINSTALL == pPackage->execute)
472 {
473 pPackage->fDependencyManagerWasHere = fDependentBlocksUninstall;
474 }
475
462 // Calculate the dependency actions before the package itself is planned. 476 // Calculate the dependency actions before the package itself is planned.
463 CalculateDependencyActionStates(pPackage, pPlan->action, &dependencyExecuteAction, &dependencyRollbackAction); 477 CalculateDependencyActionStates(pPackage, pPlan->action, &dependencyExecuteAction, &dependencyRollbackAction);
464 478
465 // If dependents were found, change the action to not uninstall the package. 479 // If dependents were found, change the action to not uninstall the package.
466 if (pPackage->fDependencyManagerWasHere) 480 if (fDependentBlocksUninstall)
467 { 481 {
468 pPackage->execute = BOOTSTRAPPER_ACTION_STATE_NONE; 482 pPackage->execute = BOOTSTRAPPER_ACTION_STATE_NONE;
469 pPackage->rollback = BOOTSTRAPPER_ACTION_STATE_NONE; 483 pPackage->rollback = BOOTSTRAPPER_ACTION_STATE_NONE;
484 pPackage->compatiblePackage.fRemove = FALSE;
470 } 485 }
471 else 486 else
472 { 487 {
@@ -494,7 +509,7 @@ extern "C" HRESULT DependencyPlanPackageBegin(
494 } 509 }
495 510
496 // If the package will be removed, add its providers to the growing list in the plan. 511 // If the package will be removed, add its providers to the growing list in the plan.
497 if (BOOTSTRAPPER_ACTION_STATE_UNINSTALL == pPackage->execute) 512 if (fAttemptingUninstall)
498 { 513 {
499 for (DWORD i = 0; i < pPackage->cDependencyProviders; ++i) 514 for (DWORD i = 0; i < pPackage->cDependencyProviders; ++i)
500 { 515 {
@@ -739,6 +754,73 @@ extern "C" void DependencyUnregisterBundle(
739 } 754 }
740} 755}
741 756
757extern "C" HRESULT DependencyDetectCompatibleEntry(
758 __in BURN_PACKAGE* pPackage,
759 __in BURN_REGISTRATION* pRegistration
760 )
761{
762 HRESULT hr = S_OK;
763 LPWSTR sczId = NULL;
764 LPWSTR sczName = NULL;
765 LPWSTR sczVersion = NULL;
766 LPCWSTR wzPackageProviderId = GetPackageProviderId(pPackage);
767 HKEY hkHive = pRegistration->fPerMachine ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER;
768
769 switch (pPackage->type)
770 {
771 case BURN_PACKAGE_TYPE_MSI:
772 // Only MSI packages can handle compatible entries.
773 break;
774 default:
775 ExitFunction();
776 }
777
778 for (DWORD i = 0; i < pPackage->cDependencyProviders; ++i)
779 {
780 BURN_DEPENDENCY_PROVIDER* pProvider = &pPackage->rgDependencyProviders[i];
781
782 hr = DepGetProviderInformation(hkHive, pProvider->sczKey, &sczId, &sczName, &sczVersion);
783 if (E_NOTFOUND == hr)
784 {
785 hr = S_OK;
786 continue;
787 }
788 ExitOnFailure(hr, "Failed to get provider information for compatible package: %ls", pProvider->sczKey);
789
790 // Make sure the compatible package is not the package itself.
791 if (!wzPackageProviderId)
792 {
793 if (!sczId)
794 {
795 continue;
796 }
797 }
798 else if (sczId && CSTR_EQUAL == ::CompareStringW(LOCALE_NEUTRAL, NORM_IGNORECASE, wzPackageProviderId, -1, sczId, -1))
799 {
800 continue;
801 }
802
803 pPackage->compatiblePackage.fDetected = TRUE;
804
805 hr = StrAllocString(&pPackage->compatiblePackage.compatibleEntry.sczProviderKey, pProvider->sczKey, 0);
806 ExitOnFailure(hr, "Failed to copy provider key for compatible entry.");
807
808 pPackage->compatiblePackage.compatibleEntry.sczId = sczId;
809 sczId = NULL;
810
811 pPackage->compatiblePackage.compatibleEntry.sczName = sczName;
812 sczName = NULL;
813
814 pPackage->compatiblePackage.compatibleEntry.sczVersion = sczVersion;
815 sczVersion = NULL;
816
817 break;
818 }
819
820LExit:
821 return hr;
822}
823
742// internal functions 824// internal functions
743 825
744 826
@@ -1170,24 +1252,35 @@ LExit:
1170 return hr; 1252 return hr;
1171} 1253}
1172 1254
1255static LPCWSTR GetPackageProviderId(
1256 __in const BURN_PACKAGE* pPackage
1257 )
1258{
1259 LPCWSTR wzId = NULL;
1260
1261 switch (pPackage->type)
1262 {
1263 case BURN_PACKAGE_TYPE_MSI:
1264 wzId = pPackage->Msi.sczProductCode;
1265 break;
1266 case BURN_PACKAGE_TYPE_MSP:
1267 wzId = pPackage->Msp.sczPatchCode;
1268 break;
1269 }
1270
1271 return wzId;
1272}
1273
1173static HRESULT RegisterPackageProvider( 1274static HRESULT RegisterPackageProvider(
1174 __in const BURN_PACKAGE* pPackage 1275 __in const BURN_PACKAGE* pPackage
1175 ) 1276 )
1176{ 1277{
1177 HRESULT hr = S_OK; 1278 HRESULT hr = S_OK;
1178 LPWSTR wzId = NULL;
1179 HKEY hkRoot = pPackage->fPerMachine ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER;
1180 1279
1181 if (pPackage->rgDependencyProviders) 1280 if (pPackage->rgDependencyProviders)
1182 { 1281 {
1183 if (BURN_PACKAGE_TYPE_MSI == pPackage->type) 1282 HKEY hkRoot = pPackage->fPerMachine ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER;
1184 { 1283 LPCWSTR wzId = GetPackageProviderId(pPackage);
1185 wzId = pPackage->Msi.sczProductCode;
1186 }
1187 else if (BURN_PACKAGE_TYPE_MSP == pPackage->type)
1188 {
1189 wzId = pPackage->Msp.sczPatchCode;
1190 }
1191 1284
1192 for (DWORD i = 0; i < pPackage->cDependencyProviders; ++i) 1285 for (DWORD i = 0; i < pPackage->cDependencyProviders; ++i)
1193 { 1286 {
diff --git a/src/burn/engine/dependency.h b/src/burn/engine/dependency.h
index 8d7344eb..41718066 100644
--- a/src/burn/engine/dependency.h
+++ b/src/burn/engine/dependency.h
@@ -191,6 +191,11 @@ void DependencyUnregisterBundle(
191 __in const BURN_PACKAGES* pPackages 191 __in const BURN_PACKAGES* pPackages
192 ); 192 );
193 193
194HRESULT DependencyDetectCompatibleEntry(
195 __in BURN_PACKAGE* pPackage,
196 __in BURN_REGISTRATION* pRegistration
197 );
198
194#if defined(__cplusplus) 199#if defined(__cplusplus)
195} 200}
196#endif 201#endif
diff --git a/src/burn/engine/detect.cpp b/src/burn/engine/detect.cpp
index be828366..37b034ae 100644
--- a/src/burn/engine/detect.cpp
+++ b/src/burn/engine/detect.cpp
@@ -98,6 +98,8 @@ extern "C" void DetectReset(
98 pProvider->rgDependents = NULL; 98 pProvider->rgDependents = NULL;
99 pProvider->cDependents = 0; 99 pProvider->cDependents = 0;
100 } 100 }
101
102 PackageUninitializeCompatible(&pPackage->compatiblePackage);
101 } 103 }
102 104
103 for (DWORD iPatchInfo = 0; iPatchInfo < pPackages->cPatchInfo; ++iPatchInfo) 105 for (DWORD iPatchInfo = 0; iPatchInfo < pPackages->cPatchInfo; ++iPatchInfo)
diff --git a/src/burn/engine/elevation.cpp b/src/burn/engine/elevation.cpp
index 221d8b6d..9ebba7c5 100644
--- a/src/burn/engine/elevation.cpp
+++ b/src/burn/engine/elevation.cpp
@@ -32,6 +32,8 @@ typedef enum _BURN_ELEVATION_MESSAGE_TYPE
32 BURN_ELEVATION_MESSAGE_TYPE_BEGIN_MSI_TRANSACTION, 32 BURN_ELEVATION_MESSAGE_TYPE_BEGIN_MSI_TRANSACTION,
33 BURN_ELEVATION_MESSAGE_TYPE_COMMIT_MSI_TRANSACTION, 33 BURN_ELEVATION_MESSAGE_TYPE_COMMIT_MSI_TRANSACTION,
34 BURN_ELEVATION_MESSAGE_TYPE_ROLLBACK_MSI_TRANSACTION, 34 BURN_ELEVATION_MESSAGE_TYPE_ROLLBACK_MSI_TRANSACTION,
35 BURN_ELEVATION_MESSAGE_TYPE_UNINSTALL_MSI_COMPATIBLE_PACKAGE,
36 BURN_ELEVATION_MESSAGE_TYPE_CLEAN_COMPATIBLE_PACKAGE,
35 37
36 BURN_ELEVATION_MESSAGE_TYPE_APPLY_INITIALIZE_PAUSE_AU_BEGIN, 38 BURN_ELEVATION_MESSAGE_TYPE_APPLY_INITIALIZE_PAUSE_AU_BEGIN,
37 BURN_ELEVATION_MESSAGE_TYPE_APPLY_INITIALIZE_PAUSE_AU_COMPLETE, 39 BURN_ELEVATION_MESSAGE_TYPE_APPLY_INITIALIZE_PAUSE_AU_COMPLETE,
@@ -168,11 +170,16 @@ static HRESULT OnApplyInitialize(
168 __in HANDLE hPipe, 170 __in HANDLE hPipe,
169 __in BURN_VARIABLES* pVariables, 171 __in BURN_VARIABLES* pVariables,
170 __in BURN_REGISTRATION* pRegistration, 172 __in BURN_REGISTRATION* pRegistration,
173 __in BURN_PACKAGES* pPackages,
171 __in HANDLE* phLock, 174 __in HANDLE* phLock,
172 __in BOOL* pfDisabledWindowsUpdate, 175 __in BOOL* pfDisabledWindowsUpdate,
173 __in BYTE* pbData, 176 __in BYTE* pbData,
174 __in SIZE_T cbData 177 __in SIZE_T cbData
175 ); 178 );
179static HRESULT ElevatedProcessDetect(
180 __in BURN_REGISTRATION* pRegistration,
181 __in BURN_PACKAGES* pPackages
182 );
176static HRESULT OnApplyUninitialize( 183static HRESULT OnApplyUninitialize(
177 __in HANDLE* phLock 184 __in HANDLE* phLock
178 ); 185 );
@@ -271,6 +278,14 @@ static HRESULT OnExecuteMsuPackage(
271 __in BYTE* pbData, 278 __in BYTE* pbData,
272 __in SIZE_T cbData 279 __in SIZE_T cbData
273 ); 280 );
281static HRESULT OnUninstallMsiCompatiblePackage(
282 __in HANDLE hPipe,
283 __in BURN_CACHE* pCache,
284 __in BURN_PACKAGES* pPackages,
285 __in BURN_VARIABLES* pVariables,
286 __in BYTE* pbData,
287 __in SIZE_T cbData
288 );
274static HRESULT OnExecutePackageProviderAction( 289static HRESULT OnExecutePackageProviderAction(
275 __in BURN_PACKAGES* pPackages, 290 __in BURN_PACKAGES* pPackages,
276 __in BURN_RELATED_BUNDLES* pRelatedBundles, 291 __in BURN_RELATED_BUNDLES* pRelatedBundles,
@@ -306,6 +321,12 @@ static int MsiExecuteMessageHandler(
306 __in WIU_MSI_EXECUTE_MESSAGE* pMessage, 321 __in WIU_MSI_EXECUTE_MESSAGE* pMessage,
307 __in_opt LPVOID pvContext 322 __in_opt LPVOID pvContext
308 ); 323 );
324static HRESULT OnCleanCompatiblePackage(
325 __in BURN_CACHE* pCache,
326 __in BURN_PACKAGES* pPackages,
327 __in BYTE* pbData,
328 __in SIZE_T cbData
329 );
309static HRESULT OnCleanPackage( 330static HRESULT OnCleanPackage(
310 __in BURN_CACHE* pCache, 331 __in BURN_CACHE* pCache,
311 __in BURN_PACKAGES* pPackages, 332 __in BURN_PACKAGES* pPackages,
@@ -1230,6 +1251,58 @@ LExit:
1230 return hr; 1251 return hr;
1231} 1252}
1232 1253
1254extern "C" HRESULT ElevationUninstallMsiCompatiblePackage(
1255 __in HANDLE hPipe,
1256 __in_opt HWND hwndParent,
1257 __in BURN_EXECUTE_ACTION* pExecuteAction,
1258 __in BURN_VARIABLES* pVariables,
1259 __in BOOL fRollback,
1260 __in PFN_MSIEXECUTEMESSAGEHANDLER pfnMessageHandler,
1261 __in LPVOID pvContext,
1262 __out BOOTSTRAPPER_APPLY_RESTART* pRestart
1263 )
1264{
1265 HRESULT hr = S_OK;
1266 BYTE* pbData = NULL;
1267 SIZE_T cbData = 0;
1268 BURN_ELEVATION_MSI_MESSAGE_CONTEXT context = { };
1269 DWORD dwResult = 0;
1270
1271 // serialize message data
1272 hr = BuffWriteNumber(&pbData, &cbData, (DWORD)fRollback);
1273 ExitOnFailure(hr, "Failed to write rollback flag to message buffer.");
1274
1275 hr = BuffWriteString(&pbData, &cbData, pExecuteAction->uninstallMsiCompatiblePackage.pParentPackage->sczId);
1276 ExitOnFailure(hr, "Failed to write package id to message buffer.");
1277
1278 hr = BuffWriteString(&pbData, &cbData, pExecuteAction->uninstallMsiCompatiblePackage.pParentPackage->compatiblePackage.compatibleEntry.sczId);
1279 ExitOnFailure(hr, "Failed to write compatible package id to message buffer.");
1280
1281 hr = BuffWritePointer(&pbData, &cbData, (DWORD_PTR)hwndParent);
1282 ExitOnFailure(hr, "Failed to write parent hwnd to message buffer.");
1283
1284 hr = BuffWriteString(&pbData, &cbData, pExecuteAction->uninstallMsiCompatiblePackage.sczLogPath);
1285 ExitOnFailure(hr, "Failed to write package log to message buffer.");
1286
1287 hr = VariableSerialize(pVariables, FALSE, &pbData, &cbData);
1288 ExitOnFailure(hr, "Failed to write variables.");
1289
1290
1291 // send message
1292 context.pfnMessageHandler = pfnMessageHandler;
1293 context.pvContext = pvContext;
1294
1295 hr = PipeSendMessage(hPipe, BURN_ELEVATION_MESSAGE_TYPE_UNINSTALL_MSI_COMPATIBLE_PACKAGE, pbData, cbData, ProcessMsiPackageMessages, &context, &dwResult);
1296 ExitOnFailure(hr, "Failed to send BURN_ELEVATION_MESSAGE_TYPE_UNINSTALL_MSI_COMPATIBLE_PACKAGE message to per-machine process.");
1297
1298 hr = ProcessResult(dwResult, pRestart);
1299
1300LExit:
1301 ReleaseBuffer(pbData);
1302
1303 return hr;
1304}
1305
1233extern "C" HRESULT ElevationExecutePackageProviderAction( 1306extern "C" HRESULT ElevationExecutePackageProviderAction(
1234 __in HANDLE hPipe, 1307 __in HANDLE hPipe,
1235 __in BURN_EXECUTE_ACTION* pExecuteAction 1308 __in BURN_EXECUTE_ACTION* pExecuteAction
@@ -1295,6 +1368,35 @@ LExit:
1295 return hr; 1368 return hr;
1296} 1369}
1297 1370
1371extern "C" HRESULT ElevationCleanCompatiblePackage(
1372 __in HANDLE hPipe,
1373 __in BURN_PACKAGE* pPackage
1374 )
1375{
1376 HRESULT hr = S_OK;
1377 BYTE* pbData = NULL;
1378 SIZE_T cbData = 0;
1379 DWORD dwResult = 0;
1380
1381 // serialize message data
1382 hr = BuffWriteString(&pbData, &cbData, pPackage->sczId);
1383 ExitOnFailure(hr, "Failed to write clean package id to message buffer.");
1384
1385 hr = BuffWriteString(&pbData, &cbData, pPackage->compatiblePackage.compatibleEntry.sczId);
1386 ExitOnFailure(hr, "Failed to write compatible package id to message buffer.");
1387
1388 // send message
1389 hr = PipeSendMessage(hPipe, BURN_ELEVATION_MESSAGE_TYPE_CLEAN_COMPATIBLE_PACKAGE, pbData, cbData, NULL, NULL, &dwResult);
1390 ExitOnFailure(hr, "Failed to send BURN_ELEVATION_MESSAGE_TYPE_CLEAN_COMPATIBLE_PACKAGE message to per-machine process.");
1391
1392 hr = (HRESULT)dwResult;
1393
1394LExit:
1395 ReleaseBuffer(pbData);
1396
1397 return hr;
1398}
1399
1298/******************************************************************* 1400/*******************************************************************
1299 ElevationCleanPackage - 1401 ElevationCleanPackage -
1300 1402
@@ -1940,7 +2042,7 @@ static HRESULT ProcessElevatedChildMessage(
1940 break; 2042 break;
1941 2043
1942 case BURN_ELEVATION_MESSAGE_TYPE_APPLY_INITIALIZE: 2044 case BURN_ELEVATION_MESSAGE_TYPE_APPLY_INITIALIZE:
1943 hrResult = OnApplyInitialize(pContext->hPipe, pContext->pVariables, pContext->pRegistration, pContext->phLock, pContext->pfDisabledAutomaticUpdates, (BYTE*)pMsg->pvData, pMsg->cbData); 2045 hrResult = OnApplyInitialize(pContext->hPipe, pContext->pVariables, pContext->pRegistration, pContext->pPackages, pContext->phLock, pContext->pfDisabledAutomaticUpdates, (BYTE*)pMsg->pvData, pMsg->cbData);
1944 break; 2046 break;
1945 2047
1946 case BURN_ELEVATION_MESSAGE_TYPE_APPLY_UNINITIALIZE: 2048 case BURN_ELEVATION_MESSAGE_TYPE_APPLY_UNINITIALIZE:
@@ -2003,6 +2105,14 @@ static HRESULT ProcessElevatedChildMessage(
2003 hrResult = OnLaunchApprovedExe(pContext->hPipe, pContext->pApprovedExes, pContext->pCache, pContext->pVariables, (BYTE*)pMsg->pvData, pMsg->cbData); 2105 hrResult = OnLaunchApprovedExe(pContext->hPipe, pContext->pApprovedExes, pContext->pCache, pContext->pVariables, (BYTE*)pMsg->pvData, pMsg->cbData);
2004 break; 2106 break;
2005 2107
2108 case BURN_ELEVATION_MESSAGE_TYPE_UNINSTALL_MSI_COMPATIBLE_PACKAGE:
2109 hrResult = OnUninstallMsiCompatiblePackage(pContext->hPipe, pContext->pCache, pContext->pPackages, pContext->pVariables, (BYTE*)pMsg->pvData, pMsg->cbData);
2110 break;
2111
2112 case BURN_ELEVATION_MESSAGE_TYPE_CLEAN_COMPATIBLE_PACKAGE:
2113 hrResult = OnCleanCompatiblePackage(pContext->pCache, pContext->pPackages, (BYTE*)pMsg->pvData, pMsg->cbData);
2114 break;
2115
2006 default: 2116 default:
2007 hr = E_INVALIDARG; 2117 hr = E_INVALIDARG;
2008 ExitOnRootFailure(hr, "Unexpected elevated message sent to child process, msg: %u", pMsg->dwMessage); 2118 ExitOnRootFailure(hr, "Unexpected elevated message sent to child process, msg: %u", pMsg->dwMessage);
@@ -2082,6 +2192,7 @@ static HRESULT OnApplyInitialize(
2082 __in HANDLE hPipe, 2192 __in HANDLE hPipe,
2083 __in BURN_VARIABLES* pVariables, 2193 __in BURN_VARIABLES* pVariables,
2084 __in BURN_REGISTRATION* pRegistration, 2194 __in BURN_REGISTRATION* pRegistration,
2195 __in BURN_PACKAGES* pPackages,
2085 __in HANDLE* phLock, 2196 __in HANDLE* phLock,
2086 __in BOOL* pfDisabledWindowsUpdate, 2197 __in BOOL* pfDisabledWindowsUpdate,
2087 __in BYTE* pbData, 2198 __in BYTE* pbData,
@@ -2113,11 +2224,9 @@ static HRESULT OnApplyInitialize(
2113 hr = ApplyLock(TRUE, phLock); 2224 hr = ApplyLock(TRUE, phLock);
2114 ExitOnFailure(hr, "Failed to acquire lock due to setup in other session."); 2225 ExitOnFailure(hr, "Failed to acquire lock due to setup in other session.");
2115 2226
2116 // Reset and reload the related bundles. 2227 // Detect.
2117 RelatedBundlesUninitialize(&pRegistration->relatedBundles); 2228 hr = ElevatedProcessDetect(pRegistration, pPackages);
2118 2229 ExitOnFailure(hr, "Failed to run detection in elevated process.");
2119 hr = RelatedBundlesInitializeForScope(TRUE, pRegistration, &pRegistration->relatedBundles);
2120 ExitOnFailure(hr, "Failed to initialize per-machine related bundles.");
2121 2230
2122 // Attempt to pause AU with best effort. 2231 // Attempt to pause AU with best effort.
2123 if (BURN_AU_PAUSE_ACTION_IFELEVATED == dwAUAction || BURN_AU_PAUSE_ACTION_IFELEVATED_NORESUME == dwAUAction) 2232 if (BURN_AU_PAUSE_ACTION_IFELEVATED == dwAUAction || BURN_AU_PAUSE_ACTION_IFELEVATED_NORESUME == dwAUAction)
@@ -2187,6 +2296,39 @@ LExit:
2187 return hr; 2296 return hr;
2188} 2297}
2189 2298
2299static HRESULT ElevatedProcessDetect(
2300 __in BURN_REGISTRATION* pRegistration,
2301 __in BURN_PACKAGES* pPackages
2302 )
2303{
2304 HRESULT hr = S_OK;
2305
2306 DetectReset(pRegistration, pPackages);
2307
2308 hr = RelatedBundlesInitializeForScope(TRUE, pRegistration, &pRegistration->relatedBundles);
2309 ExitOnFailure(hr, "Failed to initialize per-machine related bundles.");
2310
2311 for (DWORD i = 0; i < pPackages->cPackages; ++i)
2312 {
2313 BURN_PACKAGE* pPackage = pPackages->rgPackages + i;
2314
2315 hr = DependencyDetectCompatibleEntry(pPackage, pRegistration);
2316 ExitOnFailure(hr, "Failed to detect per-machine compatible entry for package: %ls", pPackage->sczId);
2317
2318 switch (pPackage->type)
2319 {
2320 case BURN_PACKAGE_TYPE_MSI:
2321 hr = MsiEngineDetectCompatiblePackage(pPackage);
2322 ExitOnFailure(hr, "Failed to detect per-machine compatible package for package: %ls", pPackage->sczId);
2323
2324 break;
2325 }
2326 }
2327
2328LExit:
2329 return hr;
2330}
2331
2190static HRESULT OnApplyUninitialize( 2332static HRESULT OnApplyUninitialize(
2191 __in HANDLE* phLock 2333 __in HANDLE* phLock
2192 ) 2334 )
@@ -2659,6 +2801,11 @@ static HRESULT OnExecuteExePackage(
2659 hr = PackageFindById(pPackages, sczPackage, &executeAction.exePackage.pPackage); 2801 hr = PackageFindById(pPackages, sczPackage, &executeAction.exePackage.pPackage);
2660 ExitOnFailure(hr, "Failed to find package: %ls", sczPackage); 2802 ExitOnFailure(hr, "Failed to find package: %ls", sczPackage);
2661 2803
2804 if (BURN_PACKAGE_TYPE_EXE != executeAction.exePackage.pPackage->type)
2805 {
2806 ExitWithRootFailure(hr, E_INVALIDARG, "Package is not an EXE package: %ls", sczPackage);
2807 }
2808
2662 // Execute EXE package. 2809 // Execute EXE package.
2663 hr = ExeEngineExecutePackage(&executeAction, pCache, pVariables, static_cast<BOOL>(dwRollback), GenericExecuteMessageHandler, hPipe, &exeRestart); 2810 hr = ExeEngineExecutePackage(&executeAction, pCache, pVariables, static_cast<BOOL>(dwRollback), GenericExecuteMessageHandler, hPipe, &exeRestart);
2664 ExitOnFailure(hr, "Failed to execute EXE package."); 2811 ExitOnFailure(hr, "Failed to execute EXE package.");
@@ -2760,6 +2907,11 @@ static HRESULT OnExecuteMsiPackage(
2760 hr = VariableDeserialize(pVariables, FALSE, pbData, cbData, &iData); 2907 hr = VariableDeserialize(pVariables, FALSE, pbData, cbData, &iData);
2761 ExitOnFailure(hr, "Failed to read variables."); 2908 ExitOnFailure(hr, "Failed to read variables.");
2762 2909
2910 if (BURN_PACKAGE_TYPE_MSI != executeAction.msiPackage.pPackage->type)
2911 {
2912 ExitWithRootFailure(hr, E_INVALIDARG, "Package is not an MSI package: %ls", sczPackage);
2913 }
2914
2763 // Execute MSI package. 2915 // Execute MSI package.
2764 hr = MsiEngineExecutePackage(hwndParent, &executeAction, pCache, pVariables, fRollback, MsiExecuteMessageHandler, hPipe, &msiRestart); 2916 hr = MsiEngineExecutePackage(hwndParent, &executeAction, pCache, pVariables, fRollback, MsiExecuteMessageHandler, hPipe, &msiRestart);
2765 ExitOnFailure(hr, "Failed to execute MSI package."); 2917 ExitOnFailure(hr, "Failed to execute MSI package.");
@@ -2859,6 +3011,11 @@ static HRESULT OnExecuteMspPackage(
2859 hr = BuffReadNumber(pbData, cbData, &iData, (DWORD*)&fRollback); 3011 hr = BuffReadNumber(pbData, cbData, &iData, (DWORD*)&fRollback);
2860 ExitOnFailure(hr, "Failed to read rollback flag."); 3012 ExitOnFailure(hr, "Failed to read rollback flag.");
2861 3013
3014 if (BURN_PACKAGE_TYPE_MSP != executeAction.mspTarget.pPackage->type)
3015 {
3016 ExitWithRootFailure(hr, E_INVALIDARG, "Package is not an MSP package: %ls", sczPackage);
3017 }
3018
2862 // Execute MSP package. 3019 // Execute MSP package.
2863 hr = MspEngineExecutePackage(hwndParent, &executeAction, pCache, pVariables, fRollback, MsiExecuteMessageHandler, hPipe, &restart); 3020 hr = MspEngineExecutePackage(hwndParent, &executeAction, pCache, pVariables, fRollback, MsiExecuteMessageHandler, hPipe, &restart);
2864 ExitOnFailure(hr, "Failed to execute MSP package."); 3021 ExitOnFailure(hr, "Failed to execute MSP package.");
@@ -2920,6 +3077,11 @@ static HRESULT OnExecuteMsuPackage(
2920 hr = PackageFindById(pPackages, sczPackage, &executeAction.msuPackage.pPackage); 3077 hr = PackageFindById(pPackages, sczPackage, &executeAction.msuPackage.pPackage);
2921 ExitOnFailure(hr, "Failed to find package: %ls", sczPackage); 3078 ExitOnFailure(hr, "Failed to find package: %ls", sczPackage);
2922 3079
3080 if (BURN_PACKAGE_TYPE_MSU != executeAction.msuPackage.pPackage->type)
3081 {
3082 ExitWithRootFailure(hr, E_INVALIDARG, "Package is not an MSU package: %ls", sczPackage);
3083 }
3084
2923 // execute MSU package 3085 // execute MSU package
2924 hr = MsuEngineExecutePackage(&executeAction, pCache, pVariables, static_cast<BOOL>(dwRollback), static_cast<BOOL>(dwStopWusaService), GenericExecuteMessageHandler, hPipe, &restart); 3086 hr = MsuEngineExecutePackage(&executeAction, pCache, pVariables, static_cast<BOOL>(dwRollback), static_cast<BOOL>(dwStopWusaService), GenericExecuteMessageHandler, hPipe, &restart);
2925 ExitOnFailure(hr, "Failed to execute MSU package."); 3087 ExitOnFailure(hr, "Failed to execute MSU package.");
@@ -2943,6 +3105,88 @@ LExit:
2943 return hr; 3105 return hr;
2944} 3106}
2945 3107
3108static HRESULT OnUninstallMsiCompatiblePackage(
3109 __in HANDLE hPipe,
3110 __in BURN_CACHE* pCache,
3111 __in BURN_PACKAGES* pPackages,
3112 __in BURN_VARIABLES* pVariables,
3113 __in BYTE* pbData,
3114 __in SIZE_T cbData
3115 )
3116{
3117 HRESULT hr = S_OK;
3118 SIZE_T iData = 0;
3119 LPWSTR sczPackageId = NULL;
3120 LPWSTR sczCompatiblePackageId = NULL;
3121 HWND hwndParent = NULL;
3122 BOOL fRollback = 0;
3123 BURN_EXECUTE_ACTION executeAction = { };
3124 BURN_PACKAGE* pPackage = NULL;
3125 BURN_COMPATIBLE_PACKAGE* pCompatiblePackage = NULL;
3126 BOOTSTRAPPER_APPLY_RESTART msiRestart = BOOTSTRAPPER_APPLY_RESTART_NONE;
3127
3128 executeAction.type = BURN_EXECUTE_ACTION_TYPE_MSI_PACKAGE;
3129
3130 // Deserialize message data.
3131 hr = BuffReadNumber(pbData, cbData, &iData, (DWORD*)&fRollback);
3132 ExitOnFailure(hr, "Failed to read rollback flag.");
3133
3134 hr = BuffReadString(pbData, cbData, &iData, &sczPackageId);
3135 ExitOnFailure(hr, "Failed to read MSI package id.");
3136
3137 hr = BuffReadString(pbData, cbData, &iData, &sczCompatiblePackageId);
3138 ExitOnFailure(hr, "Failed to read MSI compatible package id.");
3139
3140 hr = BuffReadPointer(pbData, cbData, &iData, (DWORD_PTR*)&hwndParent);
3141 ExitOnFailure(hr, "Failed to read parent hwnd.");
3142
3143 hr = BuffReadString(pbData, cbData, &iData, &executeAction.uninstallMsiCompatiblePackage.sczLogPath);
3144 ExitOnFailure(hr, "Failed to read package log.");
3145
3146 hr = VariableDeserialize(pVariables, FALSE, pbData, cbData, &iData);
3147 ExitOnFailure(hr, "Failed to read variables.");
3148
3149 hr = PackageFindById(pPackages, sczPackageId, &pPackage);
3150 ExitOnFailure(hr, "Failed to find package: %ls", sczPackageId);
3151
3152 executeAction.uninstallMsiCompatiblePackage.pParentPackage = pPackage;
3153 pCompatiblePackage = &pPackage->compatiblePackage;
3154
3155 if (!pCompatiblePackage->fDetected || BURN_PACKAGE_TYPE_MSI != pCompatiblePackage->type || !pCompatiblePackage->compatibleEntry.sczId)
3156 {
3157 ExitWithRootFailure(hr, E_INVALIDARG, "Package '%ls' has no compatible MSI package", sczPackageId);
3158 }
3159
3160 if (!sczCompatiblePackageId || !*sczCompatiblePackageId ||
3161 CSTR_EQUAL != ::CompareStringW(LOCALE_NEUTRAL, NORM_IGNORECASE, pCompatiblePackage->compatibleEntry.sczId, -1, sczCompatiblePackageId, -1))
3162 {
3163 ExitWithRootFailure(hr, E_INVALIDARG, "Package '%ls' has no compatible package with id: %ls", sczPackageId, sczCompatiblePackageId);
3164 }
3165
3166 // Uninstall MSI compatible package.
3167 hr = MsiEngineUninstallCompatiblePackage(hwndParent, &executeAction, pCache, pVariables, fRollback, MsiExecuteMessageHandler, hPipe, &msiRestart);
3168 ExitOnFailure(hr, "Failed to execute MSI package.");
3169
3170LExit:
3171 ReleaseStr(sczPackageId);
3172 ReleaseStr(sczCompatiblePackageId);
3173 PlanUninitializeExecuteAction(&executeAction);
3174
3175 if (SUCCEEDED(hr))
3176 {
3177 if (BOOTSTRAPPER_APPLY_RESTART_REQUIRED == msiRestart)
3178 {
3179 hr = HRESULT_FROM_WIN32(ERROR_SUCCESS_REBOOT_REQUIRED);
3180 }
3181 else if (BOOTSTRAPPER_APPLY_RESTART_INITIATED == msiRestart)
3182 {
3183 hr = HRESULT_FROM_WIN32(ERROR_SUCCESS_REBOOT_INITIATED);
3184 }
3185 }
3186
3187 return hr;
3188}
3189
2946static HRESULT OnExecutePackageProviderAction( 3190static HRESULT OnExecutePackageProviderAction(
2947 __in BURN_PACKAGES* pPackages, 3191 __in BURN_PACKAGES* pPackages,
2948 __in BURN_RELATED_BUNDLES* pRelatedBundles, 3192 __in BURN_RELATED_BUNDLES* pRelatedBundles,
@@ -3258,6 +3502,53 @@ LExit:
3258 return nResult; 3502 return nResult;
3259} 3503}
3260 3504
3505static HRESULT OnCleanCompatiblePackage(
3506 __in BURN_CACHE* pCache,
3507 __in BURN_PACKAGES* pPackages,
3508 __in BYTE* pbData,
3509 __in SIZE_T cbData
3510 )
3511{
3512 HRESULT hr = S_OK;
3513 SIZE_T iData = 0;
3514 LPWSTR sczPackageId = NULL;
3515 LPWSTR sczCompatiblePackageId = NULL;
3516 BURN_PACKAGE* pPackage = NULL;
3517 BURN_COMPATIBLE_PACKAGE* pCompatiblePackage = NULL;
3518
3519 // Deserialize message data.
3520 hr = BuffReadString(pbData, cbData, &iData, &sczPackageId);
3521 ExitOnFailure(hr, "Failed to read package id.");
3522
3523 hr = BuffReadString(pbData, cbData, &iData, &sczCompatiblePackageId);
3524 ExitOnFailure(hr, "Failed to read compatible package id.");
3525
3526 hr = PackageFindById(pPackages, sczPackageId, &pPackage);
3527 ExitOnFailure(hr, "Failed to find package: %ls", sczPackageId);
3528
3529 pCompatiblePackage = &pPackage->compatiblePackage;
3530
3531 if (!pCompatiblePackage->fDetected || !pCompatiblePackage->compatibleEntry.sczId || !pCompatiblePackage->sczCacheId || !*pCompatiblePackage->sczCacheId)
3532 {
3533 ExitWithRootFailure(hr, E_INVALIDARG, "Package '%ls' has no compatible package to clean.", sczPackageId);
3534 }
3535
3536 if (!sczCompatiblePackageId || !*sczCompatiblePackageId ||
3537 CSTR_EQUAL != ::CompareStringW(LOCALE_NEUTRAL, NORM_IGNORECASE, pCompatiblePackage->compatibleEntry.sczId, -1, sczCompatiblePackageId, -1))
3538 {
3539 ExitWithRootFailure(hr, E_INVALIDARG, "Package '%ls' has no compatible package with id: %ls", sczPackageId, sczCompatiblePackageId);
3540 }
3541
3542 // Remove the package from the cache.
3543 hr = CacheRemovePackage(pCache, TRUE, pCompatiblePackage->compatibleEntry.sczId, pCompatiblePackage->sczCacheId);
3544 ExitOnFailure(hr, "Failed to remove from cache compatible package: %ls", pCompatiblePackage->compatibleEntry.sczId);
3545
3546LExit:
3547 ReleaseStr(sczPackageId);
3548 ReleaseStr(sczCompatiblePackageId);
3549 return hr;
3550}
3551
3261static HRESULT OnCleanPackage( 3552static HRESULT OnCleanPackage(
3262 __in BURN_CACHE* pCache, 3553 __in BURN_CACHE* pCache,
3263 __in BURN_PACKAGES* pPackages, 3554 __in BURN_PACKAGES* pPackages,
diff --git a/src/burn/engine/elevation.h b/src/burn/engine/elevation.h
index 0e63c687..b4d0ca83 100644
--- a/src/burn/engine/elevation.h
+++ b/src/burn/engine/elevation.h
@@ -127,6 +127,16 @@ HRESULT ElevationExecuteMsuPackage(
127 __in LPVOID pvContext, 127 __in LPVOID pvContext,
128 __out BOOTSTRAPPER_APPLY_RESTART* pRestart 128 __out BOOTSTRAPPER_APPLY_RESTART* pRestart
129 ); 129 );
130HRESULT ElevationUninstallMsiCompatiblePackage(
131 __in HANDLE hPipe,
132 __in_opt HWND hwndParent,
133 __in BURN_EXECUTE_ACTION* pExecuteAction,
134 __in BURN_VARIABLES* pVariables,
135 __in BOOL fRollback,
136 __in PFN_MSIEXECUTEMESSAGEHANDLER pfnMessageHandler,
137 __in LPVOID pvContext,
138 __out BOOTSTRAPPER_APPLY_RESTART* pRestart
139 );
130HRESULT ElevationExecutePackageProviderAction( 140HRESULT ElevationExecutePackageProviderAction(
131 __in HANDLE hPipe, 141 __in HANDLE hPipe,
132 __in BURN_EXECUTE_ACTION* pExecuteAction 142 __in BURN_EXECUTE_ACTION* pExecuteAction
@@ -135,12 +145,9 @@ HRESULT ElevationExecutePackageDependencyAction(
135 __in HANDLE hPipe, 145 __in HANDLE hPipe,
136 __in BURN_EXECUTE_ACTION* pExecuteAction 146 __in BURN_EXECUTE_ACTION* pExecuteAction
137 ); 147 );
138HRESULT ElevationLaunchElevatedChild( 148HRESULT ElevationCleanCompatiblePackage(
139 __in HANDLE hPipe, 149 __in HANDLE hPipe,
140 __in BURN_PACKAGE* pPackage, 150 __in BURN_PACKAGE* pPackage
141 __in LPCWSTR wzPipeName,
142 __in LPCWSTR wzPipeToken,
143 __out DWORD* pdwChildPid
144 ); 151 );
145HRESULT ElevationCleanPackage( 152HRESULT ElevationCleanPackage(
146 __in HANDLE hPipe, 153 __in HANDLE hPipe,
diff --git a/src/burn/engine/engine.mc b/src/burn/engine/engine.mc
index 5c1b0b69..6ce11564 100644
--- a/src/burn/engine/engine.mc
+++ b/src/burn/engine/engine.mc
@@ -262,6 +262,13 @@ Detected forward compatible bundle: %1!ls!, type: %2!hs!, scope: %3!hs!, version
262. 262.
263 263
264MessageId=108 264MessageId=108
265Severity=Success
266SymbolicName=MSG_DETECTED_COMPATIBLE_PACKAGE_FROM_PROVIDER
267Language=English
268Detected compatible package: %1!ls!, provider: %2!ls!, installed: %3!ls!, version: %4!ls!, chained: %5!ls!
269.
270
271MessageId=109
265Severity=Warning 272Severity=Warning
266SymbolicName=MSG_DETECT_RELATED_BUNDLE_NOT_CACHED 273SymbolicName=MSG_DETECT_RELATED_BUNDLE_NOT_CACHED
267Language=English 274Language=English
@@ -429,6 +436,13 @@ Language=English
429Plan skipped related bundle: %1!ls!, type: %2!hs!, because it was previously scheduled. 436Plan skipped related bundle: %1!ls!, type: %2!hs!, because it was previously scheduled.
430. 437.
431 438
439MessageId=215
440Severity=Success
441SymbolicName=MSG_PLANNED_ORPHAN_PACKAGE_FROM_PROVIDER
442Language=English
443Will remove orphan package: %1!ls!, installed: %2!ls!, chained: %3!ls!
444.
445
432MessageId=216 446MessageId=216
433Severity=Success 447Severity=Success
434SymbolicName=MSG_PLAN_SKIPPED_RELATED_BUNDLE_EMBEDDED_BUNDLE_NEWER 448SymbolicName=MSG_PLAN_SKIPPED_RELATED_BUNDLE_EMBEDDED_BUNDLE_NEWER
@@ -604,7 +618,6 @@ Language=English
604Failed to layout container: %2!ls! to layout directory: %3!ls!, error: %1!ls!. 618Failed to layout container: %2!ls! to layout directory: %3!ls!, error: %1!ls!.
605. 619.
606 620
607
608MessageId=317 621MessageId=317
609Severity=Error 622Severity=Error
610SymbolicName=MSG_FAILED_LAYOUT_PAYLOAD 623SymbolicName=MSG_FAILED_LAYOUT_PAYLOAD
@@ -927,7 +940,6 @@ Language=English
927Calculating whether to keep registration 940Calculating whether to keep registration
928. 941.
929 942
930
931MessageId=374 943MessageId=374
932Severity=Success 944Severity=Success
933SymbolicName=MSG_POST_APPLY_PACKAGE 945SymbolicName=MSG_POST_APPLY_PACKAGE
@@ -1005,6 +1017,13 @@ Language=English
1005Skipping the rest of non-vital rollback boundary, id: %1!ls! 1017Skipping the rest of non-vital rollback boundary, id: %1!ls!
1006. 1018.
1007 1019
1020MessageId=390
1021Severity=Success
1022SymbolicName=MSG_APPLYING_ORPHAN_COMPATIBLE_PACKAGE
1023Language=English
1024Applying %1!hs! compatible package: %2!ls!, parent package: %3!ls!, action: %4!hs!, arguments: '%5!ls!'
1025.
1026
1008MessageId=399 1027MessageId=399
1009Severity=Success 1028Severity=Success
1010SymbolicName=MSG_APPLY_COMPLETE 1029SymbolicName=MSG_APPLY_COMPLETE
diff --git a/src/burn/engine/logging.cpp b/src/burn/engine/logging.cpp
index 1e436f68..7c048523 100644
--- a/src/burn/engine/logging.cpp
+++ b/src/burn/engine/logging.cpp
@@ -227,6 +227,49 @@ extern "C" void LoggingIncrementPackageSequence()
227 ++vdwPackageSequence; 227 ++vdwPackageSequence;
228} 228}
229 229
230extern "C" HRESULT LoggingSetCompatiblePackageVariable(
231 __in BURN_PACKAGE* pPackage,
232 __in BURN_LOGGING* pLog,
233 __in BURN_VARIABLES* pVariables,
234 __out_opt LPWSTR* psczLogPath
235 )
236{
237 HRESULT hr = S_OK;
238 LPWSTR sczLogPathVariable = NULL;
239 LPWSTR sczLogPath = NULL;
240
241 // Make sure that no package log files are created when logging has been disabled via Log element.
242 if (BURN_LOGGING_STATE_DISABLED == pLog->state)
243 {
244 ExitFunction();
245 }
246
247 if (pPackage->sczLogPathVariable && *pPackage->sczLogPathVariable)
248 {
249 // Format a suitable log path variable from the original package.
250 hr = StrAllocFormatted(&sczLogPathVariable, L"%ls_Compatible", pPackage->sczLogPathVariable);
251 ExitOnFailure(hr, "Failed to format log path variable for compatible package.");
252
253 hr = StrAllocFormatted(&sczLogPath, L"%ls_%03u_%ls_%ls.%ls", pLog->sczPrefix, vdwPackageSequence, pPackage->sczId, pPackage->compatiblePackage.compatibleEntry.sczId, pLog->sczExtension);
254 ExitOnFailure(hr, "Failed to allocate path for package log.");
255
256 hr = VariableSetString(pVariables, sczLogPathVariable, sczLogPath, FALSE, FALSE);
257 ExitOnFailure(hr, "Failed to set log path into variable.");
258
259 if (psczLogPath)
260 {
261 hr = StrAllocString(psczLogPath, sczLogPath, 0);
262 ExitOnFailure(hr, "Failed to copy package log path.");
263 }
264 }
265
266LExit:
267 ReleaseStr(sczLogPathVariable);
268 ReleaseStr(sczLogPath);
269
270 return hr;
271}
272
230extern "C" HRESULT LoggingSetPackageVariable( 273extern "C" HRESULT LoggingSetPackageVariable(
231 __in BURN_PACKAGE* pPackage, 274 __in BURN_PACKAGE* pPackage,
232 __in_z_opt LPCWSTR wzSuffix, 275 __in_z_opt LPCWSTR wzSuffix,
diff --git a/src/burn/engine/logging.h b/src/burn/engine/logging.h
index 11f676b3..ef603931 100644
--- a/src/burn/engine/logging.h
+++ b/src/burn/engine/logging.h
@@ -53,6 +53,13 @@ void LoggingOpenFailed();
53 53
54void LoggingIncrementPackageSequence(); 54void LoggingIncrementPackageSequence();
55 55
56HRESULT LoggingSetCompatiblePackageVariable(
57 __in BURN_PACKAGE* pPackage,
58 __in BURN_LOGGING* pLog,
59 __in BURN_VARIABLES* pVariables,
60 __out_opt LPWSTR* psczLogPath
61 );
62
56HRESULT LoggingSetPackageVariable( 63HRESULT LoggingSetPackageVariable(
57 __in BURN_PACKAGE* pPackage, 64 __in BURN_PACKAGE* pPackage,
58 __in_z_opt LPCWSTR wzSuffix, 65 __in_z_opt LPCWSTR wzSuffix,
diff --git a/src/burn/engine/msiengine.cpp b/src/burn/engine/msiengine.cpp
index 87ae77e9..68582d29 100644
--- a/src/burn/engine/msiengine.cpp
+++ b/src/burn/engine/msiengine.cpp
@@ -506,6 +506,9 @@ extern "C" HRESULT MsiEngineDetectPackage(
506 { 506 {
507 BURN_RELATED_MSI* pRelatedMsi = &pPackage->Msi.rgRelatedMsis[i]; 507 BURN_RELATED_MSI* pRelatedMsi = &pPackage->Msi.rgRelatedMsis[i];
508 508
509 ReleaseVerutilVersion(pVersion);
510 pVersion = NULL;
511
509 for (DWORD iProduct = 0; ; ++iProduct) 512 for (DWORD iProduct = 0; ; ++iProduct)
510 { 513 {
511 // get product 514 // get product
@@ -708,6 +711,46 @@ extern "C" HRESULT MsiEngineDetectPackage(
708 hr = DependencyDetectChainPackage(pPackage, pRegistration); 711 hr = DependencyDetectChainPackage(pPackage, pRegistration);
709 ExitOnFailure(hr, "Failed to detect dependencies for MSI package."); 712 ExitOnFailure(hr, "Failed to detect dependencies for MSI package.");
710 713
714 if (BOOTSTRAPPER_PACKAGE_STATE_PRESENT > pPackage->currentState)
715 {
716 hr = MsiEngineDetectCompatiblePackage(pPackage);
717 ExitOnFailure(hr, "Failed to detect compatible package for MSI package.");
718
719 if (BURN_PACKAGE_TYPE_MSI == pPackage->compatiblePackage.type)
720 {
721 LPCWSTR wzCompatibleProductCode = pPackage->compatiblePackage.compatibleEntry.sczId;
722 LPCWSTR wzCompatibleInstalledVersion = pPackage->compatiblePackage.Msi.sczVersion;
723
724 ReleaseVerutilVersion(pVersion);
725 pVersion = NULL;
726
727 hr = VerParseVersion(wzCompatibleInstalledVersion, 0, FALSE, &pVersion);
728 ExitOnFailure(hr, "Failed to parse dependency version: '%ls' for ProductCode: %ls", wzCompatibleInstalledVersion, wzCompatibleProductCode);
729
730 if (pVersion->fInvalid)
731 {
732 LogId(REPORT_WARNING, MSG_DETECTED_MSI_PACKAGE_INVALID_VERSION, wzCompatibleProductCode, wzCompatibleInstalledVersion);
733 }
734
735 pPackage->compatiblePackage.Msi.pVersion = pVersion;
736 pVersion = NULL;
737
738 // compare versions
739 hr = VerCompareParsedVersions(pPackage->Msi.pVersion, pPackage->compatiblePackage.Msi.pVersion, &nCompareResult);
740 ExitOnFailure(hr, "Failed to compare version '%ls' to dependency version: '%ls'", pPackage->Msi.pVersion->sczVersion, wzCompatibleInstalledVersion);
741
742 if (nCompareResult < 0)
743 {
744 pPackage->compatiblePackage.fPlannable = TRUE;
745
746 LogId(REPORT_STANDARD, MSG_DETECTED_COMPATIBLE_PACKAGE_FROM_PROVIDER, pPackage->sczId, pPackage->compatiblePackage.compatibleEntry.sczProviderKey, wzCompatibleProductCode, wzCompatibleInstalledVersion, pPackage->Msi.sczProductCode);
747
748 hr = UserExperienceOnDetectCompatibleMsiPackage(pUserExperience, pPackage->sczId, wzCompatibleProductCode, pPackage->compatiblePackage.Msi.pVersion);
749 ExitOnRootFailure(hr, "BA aborted detect compatible MSI package.");
750 }
751 }
752 }
753
711LExit: 754LExit:
712 ReleaseStr(sczInstalledLanguage); 755 ReleaseStr(sczInstalledLanguage);
713 ReleaseStr(sczInstalledVersion); 756 ReleaseStr(sczInstalledVersion);
@@ -716,8 +759,49 @@ LExit:
716 return hr; 759 return hr;
717} 760}
718 761
762extern "C" HRESULT MsiEngineDetectCompatiblePackage(
763 __in BURN_PACKAGE* pPackage
764 )
765{
766 HRESULT hr = S_OK;
767 LPWSTR sczVersion = NULL;
768 LPWSTR sczCacheId = NULL;
769 LPCWSTR wzCompatibleProductCode = pPackage->compatiblePackage.compatibleEntry.sczId;
770
771 if (!pPackage->compatiblePackage.fDetected)
772 {
773 ExitFunction();
774 }
775
776 hr = WiuGetProductInfoEx(wzCompatibleProductCode, NULL, pPackage->fPerMachine ? MSIINSTALLCONTEXT_MACHINE : MSIINSTALLCONTEXT_USERUNMANAGED, INSTALLPROPERTY_VERSIONSTRING, &sczVersion);
777 if (HRESULT_FROM_WIN32(ERROR_UNKNOWN_PRODUCT) == hr || HRESULT_FROM_WIN32(ERROR_UNKNOWN_PROPERTY) == hr)
778 {
779 ExitFunction1(hr = S_OK);
780 }
781 ExitOnFailure(hr, "Failed to get product information for compatible ProductCode: %ls", wzCompatibleProductCode);
782
783 // Assume the package used the default cache ID generation from the binder.
784 hr = StrAllocFormatted(&sczCacheId, L"%lsv%ls", wzCompatibleProductCode, sczVersion);
785 ExitOnFailure(hr, "Failed to format cache ID for compatible package.");
786
787 pPackage->compatiblePackage.sczCacheId = sczCacheId;
788 sczCacheId = NULL;
789
790 pPackage->compatiblePackage.Msi.sczVersion = sczVersion;
791 sczVersion = NULL;
792
793 pPackage->compatiblePackage.type = BURN_PACKAGE_TYPE_MSI;
794
795LExit:
796 ReleaseStr(sczVersion);
797 ReleaseStr(sczCacheId);
798
799 return hr;
800}
801
719extern "C" HRESULT MsiEnginePlanInitializePackage( 802extern "C" HRESULT MsiEnginePlanInitializePackage(
720 __in BURN_PACKAGE* pPackage, 803 __in BURN_PACKAGE* pPackage,
804 __in BOOTSTRAPPER_ACTION overallAction,
721 __in BURN_VARIABLES* pVariables, 805 __in BURN_VARIABLES* pVariables,
722 __in BURN_USER_EXPERIENCE* pUserExperience 806 __in BURN_USER_EXPERIENCE* pUserExperience
723 ) 807 )
@@ -747,6 +831,18 @@ extern "C" HRESULT MsiEnginePlanInitializePackage(
747 } 831 }
748 } 832 }
749 833
834 if (pPackage->compatiblePackage.fPlannable)
835 {
836 Assert(BURN_PACKAGE_TYPE_MSI == pPackage->compatiblePackage.type);
837
838 pPackage->compatiblePackage.fDefaultRequested = BOOTSTRAPPER_ACTION_UNINSTALL == overallAction;
839 pPackage->compatiblePackage.fRequested = pPackage->compatiblePackage.fDefaultRequested;
840
841 hr = UserExperienceOnPlanCompatibleMsiPackageBegin(pUserExperience, pPackage->sczId, pPackage->compatiblePackage.compatibleEntry.sczId, pPackage->compatiblePackage.Msi.pVersion, &pPackage->compatiblePackage.fRequested);
842 UserExperienceOnPlanCompatibleMsiPackageComplete(pUserExperience, pPackage->sczId, pPackage->compatiblePackage.compatibleEntry.sczId, hr, pPackage->compatiblePackage.fRequested);
843 ExitOnRootFailure(hr, "BA aborted plan compatible MSI package begin.");
844 }
845
750LExit: 846LExit:
751 return hr; 847 return hr;
752} 848}
@@ -989,6 +1085,17 @@ extern "C" HRESULT MsiEnginePlanAddPackage(
989 LoggingSetPackageVariable(pPackage, NULL, FALSE, pLog, pVariables, &pAction->msiPackage.sczLogPath); // ignore errors. 1085 LoggingSetPackageVariable(pPackage, NULL, FALSE, pLog, pVariables, &pAction->msiPackage.sczLogPath); // ignore errors.
990 pAction->msiPackage.dwLoggingAttributes = pLog->dwAttributes; 1086 pAction->msiPackage.dwLoggingAttributes = pLog->dwAttributes;
991 } 1087 }
1088 else if (pPackage->compatiblePackage.fRemove)
1089 {
1090 hr = PlanAppendExecuteAction(pPlan, &pAction);
1091 ExitOnFailure(hr, "Failed to append execute action.");
1092
1093 pAction->type = BURN_EXECUTE_ACTION_TYPE_UNINSTALL_MSI_COMPATIBLE_PACKAGE;
1094 pAction->uninstallMsiCompatiblePackage.pParentPackage = pPackage;
1095 pAction->uninstallMsiCompatiblePackage.dwLoggingAttributes = pLog->dwAttributes;
1096
1097 LoggingSetCompatiblePackageVariable(pPackage, pLog, pVariables, &pAction->uninstallMsiCompatiblePackage.sczLogPath); // ignore errors.
1098 }
992 1099
993LExit: 1100LExit:
994 ReleaseMem(rgFeatureActions); 1101 ReleaseMem(rgFeatureActions);
@@ -1246,6 +1353,80 @@ LExit:
1246 return hr; 1353 return hr;
1247} 1354}
1248 1355
1356extern "C" HRESULT MsiEngineUninstallCompatiblePackage(
1357 __in_opt HWND hwndParent,
1358 __in BURN_EXECUTE_ACTION* pExecuteAction,
1359 __in BURN_CACHE* /*pCache*/,
1360 __in BURN_VARIABLES* /*pVariables*/,
1361 __in BOOL fRollback,
1362 __in PFN_MSIEXECUTEMESSAGEHANDLER pfnMessageHandler,
1363 __in LPVOID pvContext,
1364 __out BOOTSTRAPPER_APPLY_RESTART* pRestart
1365 )
1366{
1367 HRESULT hr = S_OK;
1368 WIU_MSI_EXECUTE_CONTEXT context = { };
1369 WIU_RESTART restart = WIU_RESTART_NONE;
1370 LPWSTR sczProperties = NULL;
1371 BOOTSTRAPPER_ACTION_STATE action = BOOTSTRAPPER_ACTION_STATE_UNINSTALL;
1372 BURN_MSI_PROPERTY burnMsiProperty = BURN_MSI_PROPERTY_NONE;
1373 BOOTSTRAPPER_MSI_FILE_VERSIONING fileVersioning = BOOTSTRAPPER_MSI_FILE_VERSIONING_MISSING_OR_OLDER;
1374 BURN_PACKAGE* pParentPackage = pExecuteAction->uninstallMsiCompatiblePackage.pParentPackage;
1375 BURN_COMPATIBLE_PROVIDER_ENTRY* pCompatibleEntry = &pExecuteAction->uninstallMsiCompatiblePackage.pParentPackage->compatiblePackage.compatibleEntry;
1376
1377 // Default to "verbose" logging and set extra debug mode only if explicitly required.
1378 DWORD dwLogMode = WIU_LOG_DEFAULT | INSTALLLOGMODE_VERBOSE;
1379
1380 if (pExecuteAction->uninstallMsiCompatiblePackage.dwLoggingAttributes & BURN_LOGGING_ATTRIBUTE_EXTRADEBUG)
1381 {
1382 dwLogMode |= INSTALLLOGMODE_EXTRADEBUG;
1383 }
1384
1385 hr = WiuInitializeExternalUI(pfnMessageHandler, INSTALLUILEVEL_NONE, hwndParent, pvContext, fRollback, &context);
1386 ExitOnFailure(hr, "Failed to initialize external UI handler.");
1387
1388 if (pExecuteAction->uninstallMsiCompatiblePackage.sczLogPath && *pExecuteAction->uninstallMsiCompatiblePackage.sczLogPath)
1389 {
1390 hr = WiuEnableLog(dwLogMode, pExecuteAction->uninstallMsiCompatiblePackage.sczLogPath, INSTALLLOGATTRIBUTES_APPEND);
1391 ExitOnFailure(hr, "Failed to enable logging for compatible package: %ls to: %ls", pCompatibleEntry->sczId, pExecuteAction->uninstallMsiCompatiblePackage.sczLogPath);
1392 }
1393
1394 hr = MsiEngineConcatBurnProperties(action, burnMsiProperty, fileVersioning, TRUE, FALSE, &sczProperties);
1395 ExitOnFailure(hr, "Failed to add action property to argument string.");
1396
1397 LogId(REPORT_STANDARD, MSG_APPLYING_ORPHAN_COMPATIBLE_PACKAGE, LoggingRollbackOrExecute(fRollback), pCompatibleEntry->sczId, pParentPackage->sczId, LoggingActionStateToString(action), sczProperties ? sczProperties : L"");
1398
1399 hr = WiuConfigureProductEx(pCompatibleEntry->sczId, INSTALLLEVEL_DEFAULT, INSTALLSTATE_ABSENT, sczProperties, &restart);
1400 if (HRESULT_FROM_WIN32(ERROR_UNKNOWN_PRODUCT) == hr)
1401 {
1402 LogId(REPORT_STANDARD, MSG_ATTEMPTED_UNINSTALL_ABSENT_PACKAGE, pCompatibleEntry->sczId);
1403 hr = S_OK;
1404 }
1405 ExitOnFailure(hr, "Failed to uninstall compatible MSI package.");
1406
1407LExit:
1408 WiuUninitializeExternalUI(&context);
1409
1410 StrSecureZeroFreeString(sczProperties);
1411
1412 switch (restart)
1413 {
1414 case WIU_RESTART_NONE:
1415 *pRestart = BOOTSTRAPPER_APPLY_RESTART_NONE;
1416 break;
1417
1418 case WIU_RESTART_REQUIRED:
1419 *pRestart = BOOTSTRAPPER_APPLY_RESTART_REQUIRED;
1420 break;
1421
1422 case WIU_RESTART_INITIATED:
1423 *pRestart = BOOTSTRAPPER_APPLY_RESTART_INITIATED;
1424 break;
1425 }
1426
1427 return hr;
1428}
1429
1249extern "C" HRESULT MsiEngineConcatBurnProperties( 1430extern "C" HRESULT MsiEngineConcatBurnProperties(
1250 __in BOOTSTRAPPER_ACTION_STATE action, 1431 __in BOOTSTRAPPER_ACTION_STATE action,
1251 __in BURN_MSI_PROPERTY actionMsiProperty, 1432 __in BURN_MSI_PROPERTY actionMsiProperty,
diff --git a/src/burn/engine/msiengine.h b/src/burn/engine/msiengine.h
index fbb251e0..bc356fab 100644
--- a/src/burn/engine/msiengine.h
+++ b/src/burn/engine/msiengine.h
@@ -35,8 +35,12 @@ HRESULT MsiEngineDetectPackage(
35 __in BURN_REGISTRATION* pRegistration, 35 __in BURN_REGISTRATION* pRegistration,
36 __in BURN_USER_EXPERIENCE* pUserExperience 36 __in BURN_USER_EXPERIENCE* pUserExperience
37 ); 37 );
38HRESULT MsiEngineDetectCompatiblePackage(
39 __in BURN_PACKAGE* pPackage
40 );
38HRESULT MsiEnginePlanInitializePackage( 41HRESULT MsiEnginePlanInitializePackage(
39 __in BURN_PACKAGE* pPackage, 42 __in BURN_PACKAGE* pPackage,
43 __in BOOTSTRAPPER_ACTION overallAction,
40 __in BURN_VARIABLES* pVariables, 44 __in BURN_VARIABLES* pVariables,
41 __in BURN_USER_EXPERIENCE* pUserExperience 45 __in BURN_USER_EXPERIENCE* pUserExperience
42 ); 46 );
@@ -71,6 +75,16 @@ HRESULT MsiEngineExecutePackage(
71 __in LPVOID pvContext, 75 __in LPVOID pvContext,
72 __out BOOTSTRAPPER_APPLY_RESTART* pRestart 76 __out BOOTSTRAPPER_APPLY_RESTART* pRestart
73 ); 77 );
78HRESULT MsiEngineUninstallCompatiblePackage(
79 __in_opt HWND hwndParent,
80 __in BURN_EXECUTE_ACTION* pExecuteAction,
81 __in BURN_CACHE* pCache,
82 __in BURN_VARIABLES* pVariables,
83 __in BOOL fRollback,
84 __in PFN_MSIEXECUTEMESSAGEHANDLER pfnMessageHandler,
85 __in LPVOID pvContext,
86 __out BOOTSTRAPPER_APPLY_RESTART* pRestart
87 );
74HRESULT MsiEngineConcatBurnProperties( 88HRESULT MsiEngineConcatBurnProperties(
75 __in BOOTSTRAPPER_ACTION_STATE action, 89 __in BOOTSTRAPPER_ACTION_STATE action,
76 __in BURN_MSI_PROPERTY actionMsiProperty, 90 __in BURN_MSI_PROPERTY actionMsiProperty,
diff --git a/src/burn/engine/package.cpp b/src/burn/engine/package.cpp
index d9087f79..d9f92d3d 100644
--- a/src/burn/engine/package.cpp
+++ b/src/burn/engine/package.cpp
@@ -366,6 +366,31 @@ extern "C" void PackageUninitialize(
366 MsuEnginePackageUninitialize(pPackage); // TODO: Modularization 366 MsuEnginePackageUninitialize(pPackage); // TODO: Modularization
367 break; 367 break;
368 } 368 }
369
370 PackageUninitializeCompatible(&pPackage->compatiblePackage);
371}
372
373extern "C" void PackageUninitializeCompatible(
374 __in BURN_COMPATIBLE_PACKAGE* pCompatiblePackage
375 )
376{
377 ReleaseStr(pCompatiblePackage->compatibleEntry.sczId);
378 ReleaseStr(pCompatiblePackage->compatibleEntry.sczName);
379 ReleaseStr(pCompatiblePackage->compatibleEntry.sczProviderKey);
380 ReleaseStr(pCompatiblePackage->compatibleEntry.sczVersion);
381
382 ReleaseStr(pCompatiblePackage->sczCacheId);
383
384 switch (pCompatiblePackage->type)
385 {
386 case BURN_PACKAGE_TYPE_MSI:
387 ReleaseStr(pCompatiblePackage->Msi.sczVersion);
388 ReleaseVerutilVersion(pCompatiblePackage->Msi.pVersion);
389 break;
390 }
391
392 // clear struct
393 memset(pCompatiblePackage, 0, sizeof(BURN_COMPATIBLE_PACKAGE));
369} 394}
370 395
371extern "C" void PackagesUninitialize( 396extern "C" void PackagesUninitialize(
diff --git a/src/burn/engine/package.h b/src/burn/engine/package.h
index 10b5f33c..934355e1 100644
--- a/src/burn/engine/package.h
+++ b/src/burn/engine/package.h
@@ -147,6 +147,14 @@ typedef struct _BURN_MSIFEATURE
147 BOOTSTRAPPER_FEATURE_ACTION rollback; // only valid during Plan. 147 BOOTSTRAPPER_FEATURE_ACTION rollback; // only valid during Plan.
148} BURN_MSIFEATURE; 148} BURN_MSIFEATURE;
149 149
150typedef struct _BURN_COMPATIBLE_PROVIDER_ENTRY
151{
152 LPWSTR sczProviderKey;
153 LPWSTR sczId;
154 LPWSTR sczName;
155 LPWSTR sczVersion;
156} BURN_COMPATIBLE_PROVIDER_ENTRY;
157
150typedef struct _BURN_RELATED_MSI 158typedef struct _BURN_RELATED_MSI
151{ 159{
152 LPWSTR sczUpgradeCode; 160 LPWSTR sczUpgradeCode;
@@ -206,6 +214,27 @@ typedef struct _BURN_PATCH_TARGETCODE
206 BURN_PATCH_TARGETCODE_TYPE type; 214 BURN_PATCH_TARGETCODE_TYPE type;
207} BURN_PATCH_TARGETCODE; 215} BURN_PATCH_TARGETCODE;
208 216
217typedef struct _BURN_COMPATIBLE_PACKAGE
218{
219 BOOL fDetected;
220 BOOL fPlannable;
221 BOOL fDefaultRequested;
222 BOOL fRequested;
223 BOOL fRemove;
224 LPWSTR sczCacheId;
225 BURN_COMPATIBLE_PROVIDER_ENTRY compatibleEntry;
226
227 BURN_PACKAGE_TYPE type;
228 union
229 {
230 struct
231 {
232 LPWSTR sczVersion;
233 VERUTIL_VERSION* pVersion;
234 } Msi;
235 };
236} BURN_COMPATIBLE_PACKAGE;
237
209typedef struct _BURN_PACKAGE 238typedef struct _BURN_PACKAGE
210{ 239{
211 LPWSTR sczId; 240 LPWSTR sczId;
@@ -259,6 +288,8 @@ typedef struct _BURN_PACKAGE
259 BURN_DEPENDENCY_PROVIDER* rgDependencyProviders; 288 BURN_DEPENDENCY_PROVIDER* rgDependencyProviders;
260 DWORD cDependencyProviders; 289 DWORD cDependencyProviders;
261 290
291 BURN_COMPATIBLE_PACKAGE compatiblePackage;
292
262 BURN_PACKAGE_TYPE type; 293 BURN_PACKAGE_TYPE type;
263 union 294 union
264 { 295 {
@@ -371,6 +402,9 @@ HRESULT PackagesParseFromXml(
371void PackageUninitialize( 402void PackageUninitialize(
372 __in BURN_PACKAGE* pPackage 403 __in BURN_PACKAGE* pPackage
373 ); 404 );
405void PackageUninitializeCompatible(
406 __in BURN_COMPATIBLE_PACKAGE* pCompatiblePackage
407 );
374void PackagesUninitialize( 408void PackagesUninitialize(
375 __in BURN_PACKAGES* pPackages 409 __in BURN_PACKAGES* pPackages
376 ); 410 );
diff --git a/src/burn/engine/plan.cpp b/src/burn/engine/plan.cpp
index e0e9a82b..1dd78773 100644
--- a/src/burn/engine/plan.cpp
+++ b/src/burn/engine/plan.cpp
@@ -96,6 +96,10 @@ static HRESULT AppendRollbackCacheAction(
96 __in BURN_PLAN* pPlan, 96 __in BURN_PLAN* pPlan,
97 __out BURN_CACHE_ACTION** ppCacheAction 97 __out BURN_CACHE_ACTION** ppCacheAction
98 ); 98 );
99static HRESULT AppendCleanAction(
100 __in BURN_PLAN* pPlan,
101 __out BURN_CLEAN_ACTION** ppCleanAction
102 );
99static HRESULT ProcessPayloadGroup( 103static HRESULT ProcessPayloadGroup(
100 __in BURN_PLAN* pPlan, 104 __in BURN_PLAN* pPlan,
101 __in BURN_PAYLOAD_GROUP* pPayloadGroup 105 __in BURN_PAYLOAD_GROUP* pPayloadGroup
@@ -286,6 +290,10 @@ extern "C" void PlanUninitializeExecuteAction(
286 case BURN_EXECUTE_ACTION_TYPE_PACKAGE_DEPENDENCY: 290 case BURN_EXECUTE_ACTION_TYPE_PACKAGE_DEPENDENCY:
287 ReleaseStr(pExecuteAction->packageDependency.sczBundleProviderKey); 291 ReleaseStr(pExecuteAction->packageDependency.sczBundleProviderKey);
288 break; 292 break;
293
294 case BURN_EXECUTE_ACTION_TYPE_UNINSTALL_MSI_COMPATIBLE_PACKAGE:
295 ReleaseStr(pExecuteAction->uninstallMsiCompatiblePackage.sczLogPath);
296 break;
289 } 297 }
290} 298}
291 299
@@ -823,6 +831,11 @@ static HRESULT PlanPackagesHelper(
823 BURN_PACKAGE* pPackage = rgPackages + iPackage; 831 BURN_PACKAGE* pPackage = rgPackages + iPackage;
824 832
825 UserExperienceOnPlannedPackage(pUX, pPackage->sczId, pPackage->execute, pPackage->rollback, pPackage->fPlannedCache, pPackage->fPlannedUncache); 833 UserExperienceOnPlannedPackage(pUX, pPackage->sczId, pPackage->execute, pPackage->rollback, pPackage->fPlannedCache, pPackage->fPlannedUncache);
834
835 if (pPackage->compatiblePackage.fPlannable)
836 {
837 UserExperienceOnPlannedCompatiblePackage(pUX, pPackage->sczId, pPackage->compatiblePackage.compatibleEntry.sczId, pPackage->compatiblePackage.fRemove);
838 }
826 } 839 }
827 840
828LExit: 841LExit:
@@ -878,7 +891,7 @@ static HRESULT InitializePackage(
878 891
879 if (BURN_PACKAGE_TYPE_MSI == pPackage->type) 892 if (BURN_PACKAGE_TYPE_MSI == pPackage->type)
880 { 893 {
881 hr = MsiEnginePlanInitializePackage(pPackage, pVariables, pUX); 894 hr = MsiEnginePlanInitializePackage(pPackage, pPlan->action, pVariables, pUX);
882 ExitOnFailure(hr, "Failed to initialize plan package: %ls", pPackage->sczId); 895 ExitOnFailure(hr, "Failed to initialize plan package: %ls", pPackage->sczId);
883 } 896 }
884 897
@@ -918,7 +931,7 @@ static HRESULT ProcessPackage(
918 } 931 }
919 else 932 else
920 { 933 {
921 if (BOOTSTRAPPER_REQUEST_STATE_NONE != pPackage->requested) 934 if (BOOTSTRAPPER_REQUEST_STATE_NONE != pPackage->requested || pPackage->compatiblePackage.fRequested)
922 { 935 {
923 // If the package is in a requested state, plan it. 936 // If the package is in a requested state, plan it.
924 hr = PlanExecutePackage(fBundlePerMachine, pUX, pPlan, pPackage, pLog, pVariables); 937 hr = PlanExecutePackage(fBundlePerMachine, pUX, pPlan, pPackage, pLog, pVariables);
@@ -1151,7 +1164,7 @@ extern "C" HRESULT PlanExecutePackage(
1151 ExitOnFailure(hr, "Failed to complete plan dependency actions for package: %ls", pPackage->sczId); 1164 ExitOnFailure(hr, "Failed to complete plan dependency actions for package: %ls", pPackage->sczId);
1152 1165
1153 // If we are going to take any action on this package, add progress for it. 1166 // If we are going to take any action on this package, add progress for it.
1154 if (BOOTSTRAPPER_ACTION_STATE_NONE != pPackage->execute || BOOTSTRAPPER_ACTION_STATE_NONE != pPackage->rollback) 1167 if (BOOTSTRAPPER_ACTION_STATE_NONE != pPackage->execute || BOOTSTRAPPER_ACTION_STATE_NONE != pPackage->rollback || pPackage->compatiblePackage.fRemove)
1155 { 1168 {
1156 LoggingIncrementPackageSequence(); 1169 LoggingIncrementPackageSequence();
1157 1170
@@ -1560,12 +1573,10 @@ extern "C" HRESULT PlanCleanPackage(
1560 1573
1561 if (fPlanCleanPackage) 1574 if (fPlanCleanPackage)
1562 { 1575 {
1563 hr = MemEnsureArraySize(reinterpret_cast<LPVOID*>(&pPlan->rgCleanActions), pPlan->cCleanActions + 1, sizeof(BURN_CLEAN_ACTION), 5); 1576 hr = AppendCleanAction(pPlan, &pCleanAction);
1564 ExitOnFailure(hr, "Failed to grow plan's array of clean actions."); 1577 ExitOnFailure(hr, "Failed to append clean action to plan.");
1565
1566 pCleanAction = pPlan->rgCleanActions + pPlan->cCleanActions;
1567 ++pPlan->cCleanActions;
1568 1578
1579 pCleanAction->type = BURN_CLEAN_ACTION_TYPE_PACKAGE;
1569 pCleanAction->pPackage = pPackage; 1580 pCleanAction->pPackage = pPackage;
1570 1581
1571 pPackage->fPlannedUncache = TRUE; 1582 pPackage->fPlannedUncache = TRUE;
@@ -1576,6 +1587,15 @@ extern "C" HRESULT PlanCleanPackage(
1576 } 1587 }
1577 } 1588 }
1578 1589
1590 if (pPackage->compatiblePackage.fRemove)
1591 {
1592 hr = AppendCleanAction(pPlan, &pCleanAction);
1593 ExitOnFailure(hr, "Failed to append clean action to plan.");
1594
1595 pCleanAction->type = BURN_CLEAN_ACTION_TYPE_COMPATIBLE_PACKAGE;
1596 pCleanAction->pPackage = pPackage;
1597 }
1598
1579LExit: 1599LExit:
1580 return hr; 1600 return hr;
1581} 1601}
@@ -2229,6 +2249,24 @@ LExit:
2229 return hr; 2249 return hr;
2230} 2250}
2231 2251
2252static HRESULT AppendCleanAction(
2253 __in BURN_PLAN* pPlan,
2254 __out BURN_CLEAN_ACTION** ppCleanAction
2255 )
2256{
2257 HRESULT hr = S_OK;
2258
2259 hr = MemEnsureArraySizeForNewItems(reinterpret_cast<LPVOID*>(&pPlan->rgCleanActions), pPlan->cCleanActions, 1, sizeof(BURN_CLEAN_ACTION), 5);
2260 ExitOnFailure(hr, "Failed to grow plan's array of clean actions.");
2261
2262
2263 *ppCleanAction = pPlan->rgCleanActions + pPlan->cCleanActions;
2264 ++pPlan->cCleanActions;
2265
2266LExit:
2267 return hr;
2268}
2269
2232static HRESULT ProcessPayloadGroup( 2270static HRESULT ProcessPayloadGroup(
2233 __in BURN_PLAN* pPlan, 2271 __in BURN_PLAN* pPlan,
2234 __in BURN_PAYLOAD_GROUP* pPayloadGroup 2272 __in BURN_PAYLOAD_GROUP* pPayloadGroup
@@ -2505,6 +2543,8 @@ static HRESULT CalculateExecuteActions(
2505 ExitOnFailure(hr, "Invalid package type."); 2543 ExitOnFailure(hr, "Invalid package type.");
2506 } 2544 }
2507 2545
2546 pPackage->compatiblePackage.fRemove = pPackage->compatiblePackage.fPlannable && pPackage->compatiblePackage.fRequested;
2547
2508LExit: 2548LExit:
2509 return hr; 2549 return hr;
2510} 2550}
@@ -2622,6 +2662,10 @@ static void ExecuteActionLog(
2622 } 2662 }
2623 break; 2663 break;
2624 2664
2665 case BURN_EXECUTE_ACTION_TYPE_UNINSTALL_MSI_COMPATIBLE_PACKAGE:
2666 LogStringLine(PlanDumpLevel, "%ls action[%u]: UNINSTALL_MSI_COMPATIBLE_PACKAGE package id: %ls, compatible package id: %ls, cache id: %ls, log path: %ls, logging attrib: %u", wzBase, iAction, pAction->uninstallMsiCompatiblePackage.pParentPackage->sczId, pAction->uninstallMsiCompatiblePackage.pParentPackage->compatiblePackage.compatibleEntry.sczId, pAction->uninstallMsiCompatiblePackage.pParentPackage->compatiblePackage.sczCacheId, pAction->uninstallMsiCompatiblePackage.sczLogPath, pAction->uninstallMsiCompatiblePackage.dwLoggingAttributes);
2667 break;
2668
2625 case BURN_EXECUTE_ACTION_TYPE_MSP_TARGET: 2669 case BURN_EXECUTE_ACTION_TYPE_MSP_TARGET:
2626 LogStringLine(PlanDumpLevel, "%ls action[%u]: MSP_TARGET package id: %ls, action: %hs, target product code: %ls, target per-machine: %hs, action msi property: %ls, ui level: %u, disable externaluihandler: %hs, file versioning: %hs, log path: %ls", wzBase, iAction, pAction->mspTarget.pPackage->sczId, LoggingActionStateToString(pAction->mspTarget.action), pAction->mspTarget.sczTargetProductCode, LoggingBoolToString(pAction->mspTarget.fPerMachineTarget), LoggingBurnMsiPropertyToString(pAction->mspTarget.actionMsiProperty), pAction->mspTarget.uiLevel, LoggingBoolToString(pAction->mspTarget.fDisableExternalUiHandler), LoggingMsiFileVersioningToString(pAction->mspTarget.fileVersioning), pAction->mspTarget.sczLogPath); 2670 LogStringLine(PlanDumpLevel, "%ls action[%u]: MSP_TARGET package id: %ls, action: %hs, target product code: %ls, target per-machine: %hs, action msi property: %ls, ui level: %u, disable externaluihandler: %hs, file versioning: %hs, log path: %ls", wzBase, iAction, pAction->mspTarget.pPackage->sczId, LoggingActionStateToString(pAction->mspTarget.action), pAction->mspTarget.sczTargetProductCode, LoggingBoolToString(pAction->mspTarget.fPerMachineTarget), LoggingBurnMsiPropertyToString(pAction->mspTarget.actionMsiProperty), pAction->mspTarget.uiLevel, LoggingBoolToString(pAction->mspTarget.fDisableExternalUiHandler), LoggingMsiFileVersioningToString(pAction->mspTarget.fileVersioning), pAction->mspTarget.sczLogPath);
2627 for (DWORD j = 0; j < pAction->mspTarget.cOrderedPatches; ++j) 2671 for (DWORD j = 0; j < pAction->mspTarget.cOrderedPatches; ++j)
@@ -2669,6 +2713,27 @@ static void ExecuteActionLog(
2669 } 2713 }
2670} 2714}
2671 2715
2716static void CleanActionLog(
2717 __in DWORD iAction,
2718 __in BURN_CLEAN_ACTION* pAction
2719 )
2720{
2721 switch (pAction->type)
2722 {
2723 case BURN_CLEAN_ACTION_TYPE_COMPATIBLE_PACKAGE:
2724 LogStringLine(PlanDumpLevel, " Clean action[%u]: CLEAN_COMPATIBLE_PACKAGE package id: %ls", iAction, pAction->pPackage->sczId);
2725 break;
2726
2727 case BURN_CLEAN_ACTION_TYPE_PACKAGE:
2728 LogStringLine(PlanDumpLevel, " Clean action[%u]: CLEAN_PACKAGE package id: %ls", iAction, pAction->pPackage->sczId);
2729 break;
2730
2731 default:
2732 AssertSz(FALSE, "Unknown clean action type.");
2733 break;
2734 }
2735}
2736
2672extern "C" void PlanDump( 2737extern "C" void PlanDump(
2673 __in BURN_PLAN* pPlan 2738 __in BURN_PLAN* pPlan
2674 ) 2739 )
@@ -2709,7 +2774,7 @@ extern "C" void PlanDump(
2709 2774
2710 for (DWORD i = 0; i < pPlan->cCleanActions; ++i) 2775 for (DWORD i = 0; i < pPlan->cCleanActions; ++i)
2711 { 2776 {
2712 LogStringLine(PlanDumpLevel, " Clean action[%u]: CLEAN_PACKAGE package id: %ls", i, pPlan->rgCleanActions[i].pPackage->sczId); 2777 CleanActionLog(i, pPlan->rgCleanActions + i);
2713 } 2778 }
2714 2779
2715 for (DWORD i = 0; i < pPlan->cPlannedProviders; ++i) 2780 for (DWORD i = 0; i < pPlan->cPlannedProviders; ++i)
diff --git a/src/burn/engine/plan.h b/src/burn/engine/plan.h
index 6be19a10..9bf72828 100644
--- a/src/burn/engine/plan.h
+++ b/src/burn/engine/plan.h
@@ -61,12 +61,13 @@ enum BURN_EXECUTE_ACTION_TYPE
61 BURN_EXECUTE_ACTION_TYPE_ROLLBACK_BOUNDARY_END, 61 BURN_EXECUTE_ACTION_TYPE_ROLLBACK_BOUNDARY_END,
62 BURN_EXECUTE_ACTION_TYPE_BEGIN_MSI_TRANSACTION, 62 BURN_EXECUTE_ACTION_TYPE_BEGIN_MSI_TRANSACTION,
63 BURN_EXECUTE_ACTION_TYPE_COMMIT_MSI_TRANSACTION, 63 BURN_EXECUTE_ACTION_TYPE_COMMIT_MSI_TRANSACTION,
64 BURN_EXECUTE_ACTION_TYPE_UNINSTALL_MSI_COMPATIBLE_PACKAGE,
64}; 65};
65 66
66enum BURN_CLEAN_ACTION_TYPE 67enum BURN_CLEAN_ACTION_TYPE
67{ 68{
68 BURN_CLEAN_ACTION_TYPE_NONE, 69 BURN_CLEAN_ACTION_TYPE_NONE,
69 BURN_CLEAN_ACTION_TYPE_BUNDLE, 70 BURN_CLEAN_ACTION_TYPE_COMPATIBLE_PACKAGE,
70 BURN_CLEAN_ACTION_TYPE_PACKAGE, 71 BURN_CLEAN_ACTION_TYPE_PACKAGE,
71}; 72};
72 73
@@ -227,11 +228,18 @@ typedef struct _BURN_EXECUTE_ACTION
227 { 228 {
228 BURN_ROLLBACK_BOUNDARY* pRollbackBoundary; 229 BURN_ROLLBACK_BOUNDARY* pRollbackBoundary;
229 } msiTransaction; 230 } msiTransaction;
231 struct
232 {
233 BURN_PACKAGE* pParentPackage;
234 LPWSTR sczLogPath;
235 DWORD dwLoggingAttributes;
236 } uninstallMsiCompatiblePackage;
230 }; 237 };
231} BURN_EXECUTE_ACTION; 238} BURN_EXECUTE_ACTION;
232 239
233typedef struct _BURN_CLEAN_ACTION 240typedef struct _BURN_CLEAN_ACTION
234{ 241{
242 BURN_CLEAN_ACTION_TYPE type;
235 BURN_PACKAGE* pPackage; 243 BURN_PACKAGE* pPackage;
236} BURN_CLEAN_ACTION; 244} BURN_CLEAN_ACTION;
237 245
diff --git a/src/burn/engine/registration.cpp b/src/burn/engine/registration.cpp
index a5b061eb..c76dde53 100644
--- a/src/burn/engine/registration.cpp
+++ b/src/burn/engine/registration.cpp
@@ -423,6 +423,10 @@ extern "C" void RegistrationUninitialize(
423 ReleaseStr(pRegistration->sczBundlePackageAncestors); 423 ReleaseStr(pRegistration->sczBundlePackageAncestors);
424 RelatedBundlesUninitialize(&pRegistration->relatedBundles); 424 RelatedBundlesUninitialize(&pRegistration->relatedBundles);
425 425
426 if (pRegistration->rgDependents)
427 {
428 ReleaseDependencyArray(pRegistration->rgDependents, pRegistration->cDependents);
429 }
426 // clear struct 430 // clear struct
427 memset(pRegistration, 0, sizeof(BURN_REGISTRATION)); 431 memset(pRegistration, 0, sizeof(BURN_REGISTRATION));
428} 432}
diff --git a/src/burn/engine/userexperience.cpp b/src/burn/engine/userexperience.cpp
index 6ea16905..1439f5f2 100644
--- a/src/burn/engine/userexperience.cpp
+++ b/src/burn/engine/userexperience.cpp
@@ -104,7 +104,7 @@ extern "C" HRESULT UserExperienceLoad(
104 args.pCommand = pCommand; 104 args.pCommand = pCommand;
105 args.pfnBootstrapperEngineProc = EngineForApplicationProc; 105 args.pfnBootstrapperEngineProc = EngineForApplicationProc;
106 args.pvBootstrapperEngineProcContext = pEngineContext; 106 args.pvBootstrapperEngineProcContext = pEngineContext;
107 args.qwEngineAPIVersion = MAKEQWORDVERSION(2021, 12, 30, 0); 107 args.qwEngineAPIVersion = MAKEQWORDVERSION(2022, 1, 10, 0);
108 108
109 results.cbSize = sizeof(BOOTSTRAPPER_CREATE_RESULTS); 109 results.cbSize = sizeof(BOOTSTRAPPER_CREATE_RESULTS);
110 110
@@ -1015,6 +1015,36 @@ LExit:
1015 return hr; 1015 return hr;
1016} 1016}
1017 1017
1018EXTERN_C BAAPI UserExperienceOnDetectCompatibleMsiPackage(
1019 __in BURN_USER_EXPERIENCE* pUserExperience,
1020 __in_z LPCWSTR wzPackageId,
1021 __in_z LPCWSTR wzCompatiblePackageId,
1022 __in VERUTIL_VERSION* pCompatiblePackageVersion
1023 )
1024{
1025 HRESULT hr = S_OK;
1026 BA_ONDETECTCOMPATIBLEMSIPACKAGE_ARGS args = { };
1027 BA_ONDETECTCOMPATIBLEMSIPACKAGE_RESULTS results = { };
1028
1029 args.cbSize = sizeof(args);
1030 args.wzPackageId = wzPackageId;
1031 args.wzCompatiblePackageId = wzCompatiblePackageId;
1032 args.wzCompatiblePackageVersion = pCompatiblePackageVersion->sczVersion;
1033
1034 results.cbSize = sizeof(results);
1035
1036 hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONDETECTCOMPATIBLEMSIPACKAGE, &args, &results);
1037 ExitOnFailure(hr, "BA OnDetectCompatibleMsiPackage failed.");
1038
1039 if (results.fCancel)
1040 {
1041 hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT);
1042 }
1043
1044LExit:
1045 return hr;
1046}
1047
1018EXTERN_C BAAPI UserExperienceOnDetectComplete( 1048EXTERN_C BAAPI UserExperienceOnDetectComplete(
1019 __in BURN_USER_EXPERIENCE* pUserExperience, 1049 __in BURN_USER_EXPERIENCE* pUserExperience,
1020 __in HRESULT hrStatus, 1050 __in HRESULT hrStatus,
@@ -1798,6 +1828,67 @@ LExit:
1798 return hr; 1828 return hr;
1799} 1829}
1800 1830
1831EXTERN_C BAAPI UserExperienceOnPlanCompatibleMsiPackageBegin(
1832 __in BURN_USER_EXPERIENCE* pUserExperience,
1833 __in_z LPCWSTR wzPackageId,
1834 __in_z LPCWSTR wzCompatiblePackageId,
1835 __in VERUTIL_VERSION* pCompatiblePackageVersion,
1836 __inout BOOL* pfRequested
1837 )
1838{
1839 HRESULT hr = S_OK;
1840 BA_ONPLANCOMPATIBLEMSIPACKAGEBEGIN_ARGS args = { };
1841 BA_ONPLANCOMPATIBLEMSIPACKAGEBEGIN_RESULTS results = { };
1842
1843 args.cbSize = sizeof(args);
1844 args.wzPackageId = wzPackageId;
1845 args.wzCompatiblePackageId = wzCompatiblePackageId;
1846 args.wzCompatiblePackageVersion = pCompatiblePackageVersion->sczVersion;
1847 args.fRecommendedRemove = *pfRequested;
1848
1849 results.cbSize = sizeof(results);
1850 results.fRequestRemove = *pfRequested;
1851
1852 hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONPLANCOMPATIBLEMSIPACKAGEBEGIN, &args, &results);
1853 ExitOnFailure(hr, "BA OnPlanCompatibleMsiPackageBegin failed.");
1854
1855 if (results.fCancel)
1856 {
1857 hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT);
1858 }
1859 *pfRequested = results.fRequestRemove;
1860
1861LExit:
1862 return hr;
1863}
1864
1865EXTERN_C BAAPI UserExperienceOnPlanCompatibleMsiPackageComplete(
1866 __in BURN_USER_EXPERIENCE* pUserExperience,
1867 __in_z LPCWSTR wzPackageId,
1868 __in_z LPCWSTR wzCompatiblePackageId,
1869 __in HRESULT hrStatus,
1870 __in BOOL fRequested
1871 )
1872{
1873 HRESULT hr = S_OK;
1874 BA_ONPLANCOMPATIBLEMSIPACKAGECOMPLETE_ARGS args = { };
1875 BA_ONPLANCOMPATIBLEMSIPACKAGECOMPLETE_RESULTS results = { };
1876
1877 args.cbSize = sizeof(args);
1878 args.wzPackageId = wzPackageId;
1879 args.wzCompatiblePackageId = wzCompatiblePackageId;
1880 args.hrStatus = hrStatus;
1881 args.fRequestedRemove = fRequested;
1882
1883 results.cbSize = sizeof(results);
1884
1885 hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONPLANCOMPATIBLEMSIPACKAGECOMPLETE, &args, &results);
1886 ExitOnFailure(hr, "BA OnPlanCompatibleMsiPackageComplete failed.");
1887
1888LExit:
1889 return hr;
1890}
1891
1801EXTERN_C BAAPI UserExperienceOnPlanMsiFeature( 1892EXTERN_C BAAPI UserExperienceOnPlanMsiFeature(
1802 __in BURN_USER_EXPERIENCE* pUserExperience, 1893 __in BURN_USER_EXPERIENCE* pUserExperience,
1803 __in_z LPCWSTR wzPackageId, 1894 __in_z LPCWSTR wzPackageId,
@@ -1932,6 +2023,31 @@ LExit:
1932 return hr; 2023 return hr;
1933} 2024}
1934 2025
2026EXTERN_C BAAPI UserExperienceOnPlannedCompatiblePackage(
2027 __in BURN_USER_EXPERIENCE* pUserExperience,
2028 __in_z LPCWSTR wzPackageId,
2029 __in_z LPCWSTR wzCompatiblePackageId,
2030 __in BOOL fRemove
2031 )
2032{
2033 HRESULT hr = S_OK;
2034 BA_ONPLANNEDCOMPATIBLEPACKAGE_ARGS args = { };
2035 BA_ONPLANNEDCOMPATIBLEPACKAGE_RESULTS results = { };
2036
2037 args.cbSize = sizeof(args);
2038 args.wzPackageId = wzPackageId;
2039 args.wzCompatiblePackageId = wzCompatiblePackageId;
2040 args.fRemove = fRemove;
2041
2042 results.cbSize = sizeof(results);
2043
2044 hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONPLANNEDCOMPATIBLEPACKAGE, &args, &results);
2045 ExitOnFailure(hr, "BA OnPlannedCompatiblePackage failed.");
2046
2047LExit:
2048 return hr;
2049}
2050
1935EXTERN_C BAAPI UserExperienceOnPlannedPackage( 2051EXTERN_C BAAPI UserExperienceOnPlannedPackage(
1936 __in BURN_USER_EXPERIENCE* pUserExperience, 2052 __in BURN_USER_EXPERIENCE* pUserExperience,
1937 __in_z LPCWSTR wzPackageId, 2053 __in_z LPCWSTR wzPackageId,
diff --git a/src/burn/engine/userexperience.h b/src/burn/engine/userexperience.h
index e4c5d3ee..e8341120 100644
--- a/src/burn/engine/userexperience.h
+++ b/src/burn/engine/userexperience.h
@@ -248,6 +248,12 @@ BAAPI UserExperienceOnDetectBegin(
248 __in BOOL fInstalled, 248 __in BOOL fInstalled,
249 __in DWORD cPackages 249 __in DWORD cPackages
250 ); 250 );
251BAAPI UserExperienceOnDetectCompatibleMsiPackage(
252 __in BURN_USER_EXPERIENCE* pUserExperience,
253 __in_z LPCWSTR wzPackageId,
254 __in_z LPCWSTR wzCompatiblePackageId,
255 __in VERUTIL_VERSION* pCompatiblePackageVersion
256 );
251BAAPI UserExperienceOnDetectComplete( 257BAAPI UserExperienceOnDetectComplete(
252 __in BURN_USER_EXPERIENCE* pUserExperience, 258 __in BURN_USER_EXPERIENCE* pUserExperience,
253 __in HRESULT hrStatus, 259 __in HRESULT hrStatus,
@@ -414,6 +420,20 @@ BAAPI UserExperienceOnPlanBegin(
414 __in BURN_USER_EXPERIENCE* pUserExperience, 420 __in BURN_USER_EXPERIENCE* pUserExperience,
415 __in DWORD cPackages 421 __in DWORD cPackages
416 ); 422 );
423BAAPI UserExperienceOnPlanCompatibleMsiPackageBegin(
424 __in BURN_USER_EXPERIENCE* pUserExperience,
425 __in_z LPCWSTR wzPackageId,
426 __in_z LPCWSTR wzCompatiblePackageId,
427 __in VERUTIL_VERSION* pCompatiblePackageVersion,
428 __inout BOOL* pfRequested
429 );
430BAAPI UserExperienceOnPlanCompatibleMsiPackageComplete(
431 __in BURN_USER_EXPERIENCE* pUserExperience,
432 __in_z LPCWSTR wzPackageId,
433 __in_z LPCWSTR wzCompatiblePackageId,
434 __in HRESULT hrStatus,
435 __in BOOL fRequested
436 );
417BAAPI UserExperienceOnPlanComplete( 437BAAPI UserExperienceOnPlanComplete(
418 __in BURN_USER_EXPERIENCE* pUserExperience, 438 __in BURN_USER_EXPERIENCE* pUserExperience,
419 __in HRESULT hrStatus 439 __in HRESULT hrStatus
@@ -443,6 +463,12 @@ BAAPI UserExperienceOnPlanMsiPackage(
443 __inout BOOL* pfDisableExternalUiHandler, 463 __inout BOOL* pfDisableExternalUiHandler,
444 __inout BOOTSTRAPPER_MSI_FILE_VERSIONING* pFileVersioning 464 __inout BOOTSTRAPPER_MSI_FILE_VERSIONING* pFileVersioning
445 ); 465 );
466BAAPI UserExperienceOnPlannedCompatiblePackage(
467 __in BURN_USER_EXPERIENCE* pUserExperience,
468 __in_z LPCWSTR wzPackageId,
469 __in_z LPCWSTR wzCompatiblePackageId,
470 __in BOOL fRemove
471 );
446BAAPI UserExperienceOnPlannedPackage( 472BAAPI UserExperienceOnPlannedPackage(
447 __in BURN_USER_EXPERIENCE* pUserExperience, 473 __in BURN_USER_EXPERIENCE* pUserExperience,
448 __in_z LPCWSTR wzPackageId, 474 __in_z LPCWSTR wzPackageId,
diff --git a/src/burn/test/BurnUnitTest/PlanTest.cpp b/src/burn/test/BurnUnitTest/PlanTest.cpp
index 7e704c23..fc2b8472 100644
--- a/src/burn/test/BurnUnitTest/PlanTest.cpp
+++ b/src/burn/test/BurnUnitTest/PlanTest.cpp
@@ -288,6 +288,74 @@ namespace Bootstrapper
288 } 288 }
289 289
290 [Fact] 290 [Fact]
291 void OrphanCompatiblePackageTest()
292 {
293 HRESULT hr = S_OK;
294 BURN_ENGINE_STATE engineState = { };
295 BURN_ENGINE_STATE* pEngineState = &engineState;
296 BURN_PLAN* pPlan = &engineState.plan;
297
298 InitializeEngineStateForCorePlan(wzSingleMsiManifestFileName, pEngineState);
299 DetectPackagesAsAbsent(pEngineState);
300 DetectCompatibleMsiPackage(pEngineState->packages.rgPackages, L"{C24F3903-38E7-4D44-8037-D9856B3C5046}", L"2.0.0.0");
301
302 hr = CorePlan(pEngineState, BOOTSTRAPPER_ACTION_UNINSTALL);
303 NativeAssert::Succeeded(hr, "CorePlan failed");
304
305 Assert::Equal<DWORD>(BOOTSTRAPPER_ACTION_UNINSTALL, pPlan->action);
306 Assert::Equal<BOOL>(TRUE, pPlan->fPerMachine);
307 Assert::Equal<BOOL>(FALSE, pPlan->fDisableRollback);
308
309 BOOL fRollback = FALSE;
310 DWORD dwIndex = 0;
311 Assert::Equal(dwIndex, pPlan->cCacheActions);
312
313 fRollback = TRUE;
314 dwIndex = 0;
315 Assert::Equal(dwIndex, pPlan->cRollbackCacheActions);
316
317 Assert::Equal(0ull, pPlan->qwEstimatedSize);
318 Assert::Equal(0ull, pPlan->qwCacheSizeTotal);
319
320 fRollback = FALSE;
321 dwIndex = 0;
322 DWORD dwExecuteCheckpointId = 1;
323 ValidateExecuteRollbackBoundaryStart(pPlan, fRollback, dwIndex++, L"WixDefaultBoundary", TRUE, FALSE);
324 ValidateExecutePackageDependency(pPlan, fRollback, dwIndex++, L"PackageA", L"{A6F0CBF7-1578-450C-B9D7-9CF2EEC40002}", BURN_DEPENDENCY_ACTION_UNREGISTER);
325 ValidateExecutePackageProvider(pPlan, fRollback, dwIndex++, L"PackageA", BURN_DEPENDENCY_ACTION_UNREGISTER);
326 ValidateUninstallMsiCompatiblePackage(pPlan, fRollback, dwIndex++, L"PackageA", 0);
327 ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++);
328 ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++);
329 ValidateExecuteRollbackBoundaryEnd(pPlan, fRollback, dwIndex++);
330 Assert::Equal(dwIndex, pPlan->cExecuteActions);
331
332 fRollback = TRUE;
333 dwIndex = 0;
334 dwExecuteCheckpointId = 1;
335 ValidateExecuteRollbackBoundaryStart(pPlan, fRollback, dwIndex++, L"WixDefaultBoundary", TRUE, FALSE);
336 ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++);
337 ValidateExecuteCheckpoint(pPlan, fRollback, dwIndex++, dwExecuteCheckpointId++);
338 ValidateExecuteRollbackBoundaryEnd(pPlan, fRollback, dwIndex++);
339 Assert::Equal(dwIndex, pPlan->cRollbackActions);
340
341 Assert::Equal(1ul, pPlan->cExecutePackagesTotal);
342 Assert::Equal(1ul, pPlan->cOverallProgressTicksTotal);
343
344 dwIndex = 0;
345 ValidateCleanAction(pPlan, dwIndex++, L"PackageA");
346 ValidateCleanCompatibleAction(pPlan, dwIndex++, L"PackageA");
347 Assert::Equal(dwIndex, pPlan->cCleanActions);
348
349 UINT uIndex = 0;
350 ValidatePlannedProvider(pPlan, uIndex++, L"{A6F0CBF7-1578-450C-B9D7-9CF2EEC40002}", NULL);
351 ValidatePlannedProvider(pPlan, uIndex++, L"{64633047-D172-4BBB-B202-64337D15C952}", NULL);
352 Assert::Equal(uIndex, pPlan->cPlannedProviders);
353
354 Assert::Equal(1ul, pEngineState->packages.cPackages);
355 ValidateNonPermanentPackageExpectedStates(&pEngineState->packages.rgPackages[0], L"PackageA", BURN_PACKAGE_REGISTRATION_STATE_ABSENT, BURN_PACKAGE_REGISTRATION_STATE_ABSENT);
356 }
357
358 [Fact]
291 void RelatedBundleMissingFromCacheTest() 359 void RelatedBundleMissingFromCacheTest()
292 { 360 {
293 HRESULT hr = S_OK; 361 HRESULT hr = S_OK;
@@ -1026,6 +1094,36 @@ namespace Bootstrapper
1026 } 1094 }
1027 } 1095 }
1028 1096
1097 void DetectCompatibleMsiPackage(BURN_PACKAGE* pPackage, LPCWSTR wzProductCode, LPCWSTR wzVersion)
1098 {
1099 HRESULT hr = S_OK;
1100 Assert(BOOTSTRAPPER_PACKAGE_STATE_PRESENT > pPackage->currentState);
1101 Assert(0 < pPackage->cDependencyProviders);
1102 BURN_DEPENDENCY_PROVIDER* pProvider = pPackage->rgDependencyProviders;
1103 BURN_COMPATIBLE_PACKAGE* pCompatiblePackage = &pPackage->compatiblePackage;
1104 pCompatiblePackage->fDetected = TRUE;
1105 pCompatiblePackage->fPlannable = TRUE;
1106 pCompatiblePackage->type = BURN_PACKAGE_TYPE_MSI;
1107
1108 hr = StrAllocFormatted(&pCompatiblePackage->sczCacheId, L"%lsv%ls", wzProductCode, wzVersion);
1109 NativeAssert::Succeeded(hr, "Failed to format cache id");
1110
1111 hr = StrAllocString(&pCompatiblePackage->Msi.sczVersion, wzVersion, 0);
1112 NativeAssert::Succeeded(hr, "Failed to copy MSI version");
1113
1114 hr = VerParseVersion(wzVersion, 0, FALSE, &pCompatiblePackage->Msi.pVersion);
1115 NativeAssert::Succeeded(hr, "Failed to parse MSI version");
1116
1117 hr = StrAllocString(&pCompatiblePackage->compatibleEntry.sczId, wzProductCode, 0);
1118 NativeAssert::Succeeded(hr, "Failed to copy product code");
1119
1120 hr = StrAllocString(&pCompatiblePackage->compatibleEntry.sczVersion, wzVersion, 0);
1121 NativeAssert::Succeeded(hr, "Failed to copy version");
1122
1123 hr = StrAllocString(&pCompatiblePackage->compatibleEntry.sczProviderKey, pProvider->sczKey, 0);
1124 NativeAssert::Succeeded(hr, "Failed to copy provider key");
1125 }
1126
1029 void DetectPackageAsAbsent(BURN_PACKAGE* pPackage) 1127 void DetectPackageAsAbsent(BURN_PACKAGE* pPackage)
1030 { 1128 {
1031 pPackage->currentState = BOOTSTRAPPER_PACKAGE_STATE_ABSENT; 1129 pPackage->currentState = BOOTSTRAPPER_PACKAGE_STATE_ABSENT;
@@ -1262,9 +1360,26 @@ namespace Bootstrapper
1262 __in LPCWSTR wzPackageId 1360 __in LPCWSTR wzPackageId
1263 ) 1361 )
1264 { 1362 {
1363 BURN_CLEAN_ACTION* pCleanAction = ValidateCleanActionExists(pPlan, dwIndex);
1364 Assert::Equal<DWORD>(BURN_CLEAN_ACTION_TYPE_PACKAGE, pCleanAction->type);
1365 Assert::NotEqual((DWORD_PTR)0, (DWORD_PTR)pCleanAction->pPackage);
1366 NativeAssert::StringEqual(wzPackageId, pCleanAction->pPackage->sczId);
1367 }
1368
1369 BURN_CLEAN_ACTION* ValidateCleanActionExists(BURN_PLAN* pPlan, DWORD dwIndex)
1370 {
1265 Assert::InRange(dwIndex + 1ul, 1ul, pPlan->cCleanActions); 1371 Assert::InRange(dwIndex + 1ul, 1ul, pPlan->cCleanActions);
1372 return pPlan->rgCleanActions + dwIndex;
1373 }
1266 1374
1267 BURN_CLEAN_ACTION* pCleanAction = pPlan->rgCleanActions + dwIndex; 1375 void ValidateCleanCompatibleAction(
1376 __in BURN_PLAN* pPlan,
1377 __in DWORD dwIndex,
1378 __in LPCWSTR wzPackageId
1379 )
1380 {
1381 BURN_CLEAN_ACTION* pCleanAction = ValidateCleanActionExists(pPlan, dwIndex);
1382 Assert::Equal<DWORD>(BURN_CLEAN_ACTION_TYPE_COMPATIBLE_PACKAGE, pCleanAction->type);
1268 Assert::NotEqual((DWORD_PTR)0, (DWORD_PTR)pCleanAction->pPackage); 1383 Assert::NotEqual((DWORD_PTR)0, (DWORD_PTR)pCleanAction->pPackage);
1269 NativeAssert::StringEqual(wzPackageId, pCleanAction->pPackage->sczId); 1384 NativeAssert::StringEqual(wzPackageId, pCleanAction->pPackage->sczId);
1270 } 1385 }
@@ -1537,6 +1652,22 @@ namespace Bootstrapper
1537 NativeAssert::StringEqual(wzKey, pProvider->sczKey); 1652 NativeAssert::StringEqual(wzKey, pProvider->sczKey);
1538 NativeAssert::StringEqual(wzName, pProvider->sczName); 1653 NativeAssert::StringEqual(wzName, pProvider->sczName);
1539 } 1654 }
1655
1656 void ValidateUninstallMsiCompatiblePackage(
1657 __in BURN_PLAN* pPlan,
1658 __in BOOL fRollback,
1659 __in DWORD dwIndex,
1660 __in_z LPCWSTR wzPackageId,
1661 __in DWORD dwLoggingAttributes
1662 )
1663 {
1664 BURN_EXECUTE_ACTION* pAction = ValidateExecuteActionExists(pPlan, fRollback, dwIndex);
1665 Assert::Equal<DWORD>(BURN_EXECUTE_ACTION_TYPE_UNINSTALL_MSI_COMPATIBLE_PACKAGE, pAction->type);
1666 NativeAssert::StringEqual(wzPackageId, pAction->uninstallMsiCompatiblePackage.pParentPackage->sczId);
1667 NativeAssert::NotNull(pAction->msiPackage.sczLogPath);
1668 Assert::Equal<DWORD>(dwLoggingAttributes, pAction->uninstallMsiCompatiblePackage.dwLoggingAttributes);
1669 Assert::Equal<BOOL>(FALSE, pAction->fDeleted);
1670 }
1540 }; 1671 };
1541} 1672}
1542} 1673}
diff --git a/src/ext/Bal/wixstdba/WixStandardBootstrapperApplication.cpp b/src/ext/Bal/wixstdba/WixStandardBootstrapperApplication.cpp
index 7b70d772..02c10472 100644
--- a/src/ext/Bal/wixstdba/WixStandardBootstrapperApplication.cpp
+++ b/src/ext/Bal/wixstdba/WixStandardBootstrapperApplication.cpp
@@ -1424,6 +1424,18 @@ public: // IBootstrapperApplication
1424 case BOOTSTRAPPER_APPLICATION_MESSAGE_ONSETUPDATECOMPLETE: 1424 case BOOTSTRAPPER_APPLICATION_MESSAGE_ONSETUPDATECOMPLETE:
1425 OnSetUpdateCompleteFallback(reinterpret_cast<BA_ONSETUPDATECOMPLETE_ARGS*>(pvArgs), reinterpret_cast<BA_ONSETUPDATECOMPLETE_RESULTS*>(pvResults)); 1425 OnSetUpdateCompleteFallback(reinterpret_cast<BA_ONSETUPDATECOMPLETE_ARGS*>(pvArgs), reinterpret_cast<BA_ONSETUPDATECOMPLETE_RESULTS*>(pvResults));
1426 break; 1426 break;
1427 case BOOTSTRAPPER_APPLICATION_MESSAGE_ONDETECTCOMPATIBLEMSIPACKAGE:
1428 OnDetectCompatibleMsiPackageFallback(reinterpret_cast<BA_ONDETECTCOMPATIBLEMSIPACKAGE_ARGS*>(pvArgs), reinterpret_cast<BA_ONDETECTCOMPATIBLEMSIPACKAGE_RESULTS*>(pvResults));
1429 break;
1430 case BOOTSTRAPPER_APPLICATION_MESSAGE_ONPLANCOMPATIBLEMSIPACKAGEBEGIN:
1431 OnPlanCompatibleMsiPackageBeginFallback(reinterpret_cast<BA_ONPLANCOMPATIBLEMSIPACKAGEBEGIN_ARGS*>(pvArgs), reinterpret_cast<BA_ONPLANCOMPATIBLEMSIPACKAGEBEGIN_RESULTS*>(pvResults));
1432 break;
1433 case BOOTSTRAPPER_APPLICATION_MESSAGE_ONPLANCOMPATIBLEMSIPACKAGECOMPLETE:
1434 OnPlanCompatibleMsiPackageCompleteFallback(reinterpret_cast<BA_ONPLANCOMPATIBLEMSIPACKAGECOMPLETE_ARGS*>(pvArgs), reinterpret_cast<BA_ONPLANCOMPATIBLEMSIPACKAGECOMPLETE_RESULTS*>(pvResults));
1435 break;
1436 case BOOTSTRAPPER_APPLICATION_MESSAGE_ONPLANNEDCOMPATIBLEPACKAGE:
1437 OnPlannedCompatiblePackageFallback(reinterpret_cast<BA_ONPLANNEDCOMPATIBLEPACKAGE_ARGS*>(pvArgs), reinterpret_cast<BA_ONPLANNEDCOMPATIBLEPACKAGE_RESULTS*>(pvResults));
1438 break;
1427 default: 1439 default:
1428#ifdef DEBUG 1440#ifdef DEBUG
1429 BalLog(BOOTSTRAPPER_LOG_LEVEL_STANDARD, "WIXSTDBA: Forwarding unknown BA message: %d", message); 1441 BalLog(BOOTSTRAPPER_LOG_LEVEL_STANDARD, "WIXSTDBA: Forwarding unknown BA message: %d", message);
@@ -1611,6 +1623,14 @@ private: // privates
1611 m_pfnBAFunctionsProc(BA_FUNCTIONS_MESSAGE_ONPLANPACKAGECOMPLETE, pArgs, pResults, m_pvBAFunctionsProcContext); 1623 m_pfnBAFunctionsProc(BA_FUNCTIONS_MESSAGE_ONPLANPACKAGECOMPLETE, pArgs, pResults, m_pvBAFunctionsProcContext);
1612 } 1624 }
1613 1625
1626 void OnPlannedCompatiblePackageFallback(
1627 __in BA_ONPLANNEDCOMPATIBLEPACKAGE_ARGS* pArgs,
1628 __inout BA_ONPLANNEDCOMPATIBLEPACKAGE_RESULTS* pResults
1629 )
1630 {
1631 m_pfnBAFunctionsProc(BA_FUNCTIONS_MESSAGE_ONPLANNEDCOMPATIBLEPACKAGE, pArgs, pResults, m_pvBAFunctionsProcContext);
1632 }
1633
1614 void OnPlannedPackageFallback( 1634 void OnPlannedPackageFallback(
1615 __in BA_ONPLANNEDPACKAGE_ARGS* pArgs, 1635 __in BA_ONPLANNEDPACKAGE_ARGS* pArgs,
1616 __inout BA_ONPLANNEDPACKAGE_RESULTS* pResults 1636 __inout BA_ONPLANNEDPACKAGE_RESULTS* pResults
@@ -2043,6 +2063,32 @@ private: // privates
2043 m_pfnBAFunctionsProc(BA_FUNCTIONS_MESSAGE_ONSETUPDATECOMPLETE, pArgs, pResults, m_pvBAFunctionsProcContext); 2063 m_pfnBAFunctionsProc(BA_FUNCTIONS_MESSAGE_ONSETUPDATECOMPLETE, pArgs, pResults, m_pvBAFunctionsProcContext);
2044 } 2064 }
2045 2065
2066 void OnDetectCompatibleMsiPackageFallback(
2067 __in BA_ONDETECTCOMPATIBLEMSIPACKAGE_ARGS* pArgs,
2068 __inout BA_ONDETECTCOMPATIBLEMSIPACKAGE_RESULTS* pResults
2069 )
2070 {
2071 m_pfnBAFunctionsProc(BA_FUNCTIONS_MESSAGE_ONDETECTCOMPATIBLEMSIPACKAGE, pArgs, pResults, m_pvBAFunctionsProcContext);
2072 }
2073
2074 void OnPlanCompatibleMsiPackageBeginFallback(
2075 __in BA_ONPLANCOMPATIBLEMSIPACKAGEBEGIN_ARGS* pArgs,
2076 __inout BA_ONPLANCOMPATIBLEMSIPACKAGEBEGIN_RESULTS* pResults
2077 )
2078 {
2079 BOOL fRequestRemove = pResults->fRequestRemove;
2080 m_pfnBAFunctionsProc(BA_FUNCTIONS_MESSAGE_ONPLANCOMPATIBLEMSIPACKAGEBEGIN, pArgs, pResults, m_pvBAFunctionsProcContext);
2081 BalLogId(BOOTSTRAPPER_LOG_LEVEL_STANDARD, MSG_WIXSTDBA_PLANNED_COMPATIBLE_MSI_PACKAGE, m_hModule, pArgs->wzPackageId, pArgs->wzCompatiblePackageId, LoggingBoolToString(fRequestRemove), LoggingBoolToString(pResults->fRequestRemove));
2082 }
2083
2084 void OnPlanCompatibleMsiPackageCompleteFallback(
2085 __in BA_ONPLANCOMPATIBLEMSIPACKAGECOMPLETE_ARGS* pArgs,
2086 __inout BA_ONPLANCOMPATIBLEMSIPACKAGECOMPLETE_RESULTS* pResults
2087 )
2088 {
2089 m_pfnBAFunctionsProc(BA_FUNCTIONS_MESSAGE_ONPLANCOMPATIBLEMSIPACKAGECOMPLETE, pArgs, pResults, m_pvBAFunctionsProcContext);
2090 }
2091
2046 2092
2047public: //CBalBaseBootstrapperApplication 2093public: //CBalBaseBootstrapperApplication
2048 virtual STDMETHODIMP Initialize( 2094 virtual STDMETHODIMP Initialize(
diff --git a/src/ext/Bal/wixstdba/wixstdba.mc b/src/ext/Bal/wixstdba/wixstdba.mc
index 659ccd01..40acfe54 100644
--- a/src/ext/Bal/wixstdba/wixstdba.mc
+++ b/src/ext/Bal/wixstdba/wixstdba.mc
@@ -50,6 +50,13 @@ Language=English
50WIXSTDBA: Planned related bundle: %1!ls!, wixstdba requested: %2!hs!, bafunctions requested: %3!hs! 50WIXSTDBA: Planned related bundle: %1!ls!, wixstdba requested: %2!hs!, bafunctions requested: %3!hs!
51. 51.
52 52
53MessageId=4
54Severity=Success
55SymbolicName=MSG_WIXSTDBA_PLANNED_COMPATIBLE_MSI_PACKAGE
56Language=English
57WIXSTDBA: Planned compatible package: %2!ls! for %1!ls!, wixstdba requested remove: %3!hs!, bafunctions requested remove: %4!hs!
58.
59
53MessageId=5 60MessageId=5
54Severity=Success 61Severity=Success
55SymbolicName=MSG_WIXSTDBA_PLANNED_TARGET_MSI_PACKAGE 62SymbolicName=MSG_WIXSTDBA_PLANNED_TARGET_MSI_PACKAGE
diff --git a/src/test/burn/TestData/DependencyTests/BundleAv2/BundleAv2.wixproj b/src/test/burn/TestData/DependencyTests/BundleAv2/BundleAv2.wixproj
new file mode 100644
index 00000000..eab737f8
--- /dev/null
+++ b/src/test/burn/TestData/DependencyTests/BundleAv2/BundleAv2.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>2.0.0.0</Version>
6 </PropertyGroup>
7 <ItemGroup>
8 <ProjectReference Include="..\PackageAv2\PackageAv2.wixproj" />
9 <ProjectReference Include="..\..\TestBA\TestBAWixlib\testbawixlib.wixproj" />
10 </ItemGroup>
11 <ItemGroup>
12 <PackageReference Include="WixToolset.Bal.wixext" />
13 <PackageReference Include="WixToolset.NetFx.wixext" />
14 <PackageReference Include="WixToolset.Util.wixext" />
15 </ItemGroup>
16</Project> \ No newline at end of file
diff --git a/src/test/burn/TestData/DependencyTests/BundleAv2/BundleAv2.wxs b/src/test/burn/TestData/DependencyTests/BundleAv2/BundleAv2.wxs
new file mode 100644
index 00000000..570f633f
--- /dev/null
+++ b/src/test/burn/TestData/DependencyTests/BundleAv2/BundleAv2.wxs
@@ -0,0 +1,21 @@
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<?define TestExeRegistryKey = Software\WiX\Tests\$(var.TestGroupName)\ExeA?>
4
5<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs" xmlns:util="http://wixtoolset.org/schemas/v4/wxs/util">
6 <Fragment>
7 <util:RegistrySearch Root="HKLM" Key="$(var.TestExeRegistryKey)" Value="Version" Variable="ExeA_Version" />
8
9 <PackageGroup Id="BundlePackages">
10 <MsiPackage Id="PackageA" SourceFile="$(var.PackageAv2.TargetPath)" />
11 <ExePackage Id="ExeA" Cache="remove" PerMachine="yes"
12 DetectCondition="ExeA_Version AND ExeA_Version &gt;= v$(var.Version)"
13 InstallArguments="/regw &quot;HKLM\$(var.TestExeRegistryKey),Version,String,$(var.Version)&quot;"
14 RepairArguments="/regw &quot;HKLM\$(var.TestExeRegistryKey),Version,String,$(var.Version)&quot;"
15 UninstallArguments="/regd &quot;HKLM\$(var.TestExeRegistryKey),Version&quot;">
16 <Provides Key="$(var.TestGroupName)_ExeA,v1.0" Version="$(var.Version)" />
17 <PayloadGroupRef Id="TestExePayloads" />
18 </ExePackage>
19 </PackageGroup>
20 </Fragment>
21</Wix>
diff --git a/src/test/burn/TestData/DependencyTests/PackageAv1/PackageA.props b/src/test/burn/TestData/DependencyTests/PackageAv1/PackageA.props
index 8cbe9aa9..d6778261 100644
--- a/src/test/burn/TestData/DependencyTests/PackageAv1/PackageA.props
+++ b/src/test/burn/TestData/DependencyTests/PackageAv1/PackageA.props
@@ -3,7 +3,6 @@
3 <PropertyGroup> 3 <PropertyGroup>
4 <PackageName>PackageA</PackageName> 4 <PackageName>PackageA</PackageName>
5 <ProductComponentsRef>true</ProductComponentsRef> 5 <ProductComponentsRef>true</ProductComponentsRef>
6 <ProductCode>{6F171EC9-0774-4974-A8D1-493EF53CAB74}</ProductCode>
7 <UpgradeCode>{45E933B7-B56A-44D5-8EEC-625EC199085E}</UpgradeCode> 6 <UpgradeCode>{45E933B7-B56A-44D5-8EEC-625EC199085E}</UpgradeCode>
8 </PropertyGroup> 7 </PropertyGroup>
9 <ItemGroup> 8 <ItemGroup>
diff --git a/src/test/burn/TestData/DependencyTests/PackageAv1/PackageAv1.props b/src/test/burn/TestData/DependencyTests/PackageAv1/PackageAv1.props
new file mode 100644
index 00000000..e83247c0
--- /dev/null
+++ b/src/test/burn/TestData/DependencyTests/PackageAv1/PackageAv1.props
@@ -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>
3 <Import Project="PackageA.props" />
4 <PropertyGroup>
5 <ProductCode>{6F171EC9-0774-4974-A8D1-493EF53CAB74}</ProductCode>
6 </PropertyGroup>
7</Project> \ No newline at end of file
diff --git a/src/test/burn/TestData/DependencyTests/PackageAv1/PackageAv1.wixproj b/src/test/burn/TestData/DependencyTests/PackageAv1/PackageAv1.wixproj
index 5ebce845..add55d60 100644
--- a/src/test/burn/TestData/DependencyTests/PackageAv1/PackageAv1.wixproj
+++ b/src/test/burn/TestData/DependencyTests/PackageAv1/PackageAv1.wixproj
@@ -1,6 +1,6 @@
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. --> 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"> 2<Project Sdk="WixToolset.Sdk">
3 <Import Project="PackageA.props" /> 3 <Import Project="PackageAv1.props" />
4 <ItemGroup> 4 <ItemGroup>
5 <PackageReference Include="WixToolset.Dependency.wixext" /> 5 <PackageReference Include="WixToolset.Dependency.wixext" />
6 </ItemGroup> 6 </ItemGroup>
diff --git a/src/test/burn/TestData/DependencyTests/PackageAv1_0_1/PackageAv1_0_1.wixproj b/src/test/burn/TestData/DependencyTests/PackageAv1_0_1/PackageAv1_0_1.wixproj
index 73a7cb6f..e1081745 100644
--- a/src/test/burn/TestData/DependencyTests/PackageAv1_0_1/PackageAv1_0_1.wixproj
+++ b/src/test/burn/TestData/DependencyTests/PackageAv1_0_1/PackageAv1_0_1.wixproj
@@ -1,6 +1,6 @@
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. --> 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"> 2<Project Sdk="WixToolset.Sdk">
3 <Import Project="..\PackageAv1\PackageA.props" /> 3 <Import Project="..\PackageAv1\PackageAv1.props" />
4 <PropertyGroup> 4 <PropertyGroup>
5 <Version>1.0.1.0</Version> 5 <Version>1.0.1.0</Version>
6 </PropertyGroup> 6 </PropertyGroup>
diff --git a/src/test/burn/TestData/DependencyTests/PackageAv1_0_2/PackageAv1_0_2.wixproj b/src/test/burn/TestData/DependencyTests/PackageAv1_0_2/PackageAv1_0_2.wixproj
index 37e88de9..60b64c9f 100644
--- a/src/test/burn/TestData/DependencyTests/PackageAv1_0_2/PackageAv1_0_2.wixproj
+++ b/src/test/burn/TestData/DependencyTests/PackageAv1_0_2/PackageAv1_0_2.wixproj
@@ -1,6 +1,6 @@
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. --> 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"> 2<Project Sdk="WixToolset.Sdk">
3 <Import Project="..\PackageAv1\PackageA.props" /> 3 <Import Project="..\PackageAv1\PackageAv1.props" />
4 <PropertyGroup> 4 <PropertyGroup>
5 <Version>1.0.2.0</Version> 5 <Version>1.0.2.0</Version>
6 </PropertyGroup> 6 </PropertyGroup>
diff --git a/src/test/burn/TestData/DependencyTests/PackageAv2/PackageAv2.wixproj b/src/test/burn/TestData/DependencyTests/PackageAv2/PackageAv2.wixproj
new file mode 100644
index 00000000..b61d09f5
--- /dev/null
+++ b/src/test/burn/TestData/DependencyTests/PackageAv2/PackageAv2.wixproj
@@ -0,0 +1,13 @@
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>2.0.0.0</Version>
6 </PropertyGroup>
7 <ItemGroup>
8 <Compile Include="..\PackageAv1\ProductComponents.wxs" Link="ProductComponents.wxs" />
9 </ItemGroup>
10 <ItemGroup>
11 <PackageReference Include="WixToolset.Dependency.wixext" />
12 </ItemGroup>
13</Project> \ No newline at end of file
diff --git a/src/test/burn/WixTestTools/BundleVerifier.cs b/src/test/burn/WixTestTools/BundleVerifier.cs
index 54f90870..db14c228 100644
--- a/src/test/burn/WixTestTools/BundleVerifier.cs
+++ b/src/test/burn/WixTestTools/BundleVerifier.cs
@@ -124,14 +124,14 @@ namespace WixTestTools
124 } 124 }
125 } 125 }
126 126
127 public void VerifyPackageIsCached(string packageId) 127 public void VerifyPackageIsCached(string packageId, bool cached = true)
128 { 128 {
129 using var wixOutput = WixOutput.Read(this.BundlePdb); 129 using var wixOutput = WixOutput.Read(this.BundlePdb);
130 var intermediate = Intermediate.Load(wixOutput); 130 var intermediate = Intermediate.Load(wixOutput);
131 var section = intermediate.Sections.Single(); 131 var section = intermediate.Sections.Single();
132 var packageSymbol = section.Symbols.OfType<WixBundlePackageSymbol>().Single(p => p.Id.Id == packageId); 132 var packageSymbol = section.Symbols.OfType<WixBundlePackageSymbol>().Single(p => p.Id.Id == packageId);
133 var cachePath = this.GetPackageCachePathForCacheId(packageSymbol.CacheId, packageSymbol.PerMachine == YesNoDefaultType.Yes); 133 var cachePath = this.GetPackageCachePathForCacheId(packageSymbol.CacheId, packageSymbol.PerMachine == YesNoDefaultType.Yes);
134 Assert.True(Directory.Exists(cachePath)); 134 Assert.Equal(cached, Directory.Exists(cachePath));
135 } 135 }
136 136
137 public void VerifyExeTestRegistryRootDeleted(string name, bool x64 = false) 137 public void VerifyExeTestRegistryRootDeleted(string name, bool x64 = false)
diff --git a/src/test/burn/WixToolsetTest.BurnE2E/DependencyTests.cs b/src/test/burn/WixToolsetTest.BurnE2E/DependencyTests.cs
index b08cd54f..825160c5 100644
--- a/src/test/burn/WixToolsetTest.BurnE2E/DependencyTests.cs
+++ b/src/test/burn/WixToolsetTest.BurnE2E/DependencyTests.cs
@@ -123,6 +123,64 @@ namespace WixToolsetTest.BurnE2E
123 bundleAv1.VerifyExeTestRegistryValue(testRegistryValueExe, "1.0.1.0"); 123 bundleAv1.VerifyExeTestRegistryValue(testRegistryValueExe, "1.0.1.0");
124 } 124 }
125 125
126 [Fact]
127 public void UninstallsOrphanCompatiblePackages()
128 {
129 var testRegistryValueExe = "ExeA";
130
131 var packageAv1 = this.CreatePackageInstaller("PackageAv1");
132 var packageAv2 = this.CreatePackageInstaller("PackageAv2");
133 var packageB = this.CreatePackageInstaller("PackageB");
134 var bundleAv1 = this.CreateBundleInstaller("BundleAv1");
135 var bundleAv2 = this.CreateBundleInstaller("BundleAv2");
136 var bundleB = this.CreateBundleInstaller("BundleB");
137
138 packageAv1.VerifyInstalled(false);
139 packageAv2.VerifyInstalled(false);
140 packageB.VerifyInstalled(false);
141
142 bundleAv1.Install();
143 bundleAv1.VerifyRegisteredAndInPackageCache();
144
145 packageAv1.VerifyInstalled(true);
146 bundleAv1.VerifyPackageIsCached("PackageA");
147 bundleAv1.VerifyExeTestRegistryValue(testRegistryValueExe, "1.0.0.0");
148
149 bundleB.Install();
150 bundleB.VerifyRegisteredAndInPackageCache();
151
152 packageAv1.VerifyInstalled(true);
153 bundleAv1.VerifyPackageIsCached("PackageA");
154 bundleAv1.VerifyExeTestRegistryValue(testRegistryValueExe, "1.0.0.0");
155 packageB.VerifyInstalled(true);
156
157 bundleAv2.Install();
158 bundleAv2.VerifyRegisteredAndInPackageCache();
159 bundleAv1.VerifyUnregisteredAndRemovedFromPackageCache();
160
161 packageAv1.VerifyInstalled(false);
162 bundleAv1.VerifyPackageIsCached("PackageA", false);
163 packageAv2.VerifyInstalled(true);
164 bundleAv2.VerifyPackageIsCached("PackageA");
165 bundleAv1.VerifyExeTestRegistryValue(testRegistryValueExe, "2.0.0.0");
166
167 bundleAv2.Uninstall();
168 bundleAv2.VerifyUnregisteredAndRemovedFromPackageCache();
169
170 packageAv2.VerifyInstalled(true);
171 bundleAv2.VerifyPackageIsCached("PackageA");
172 bundleAv1.VerifyExeTestRegistryValue(testRegistryValueExe, "2.0.0.0");
173
174 // Verify https://github.com/wixtoolset/issues/issues/3190
175 bundleB.Uninstall();
176
177 packageAv1.VerifyInstalled(false);
178 packageAv2.VerifyInstalled(false);
179 bundleAv2.VerifyPackageIsCached("PackageA", false);
180 packageB.VerifyInstalled(false);
181 bundleAv1.VerifyExeTestRegistryRootDeleted(testRegistryValueExe);
182 }
183
126 [Fact(Skip = "https://github.com/wixtoolset/issues/issues/6401")] 184 [Fact(Skip = "https://github.com/wixtoolset/issues/issues/6401")]
127 public void CanMinorUpgradeDependencyPackageFromPatchBundle() 185 public void CanMinorUpgradeDependencyPackageFromPatchBundle()
128 { 186 {