@joebigelow / wix / commits / da1d1376

Remove orphan compatible MSI packages.

Reimplements #3190

Sean Hall committed Jan 14, 2022 at 21:37 UTC da1d1376953ef1c9afb32d5eee02b785e52e372e
43 files changed +1964 -84
src/api/burn/WixToolset.BootstrapperCore.Native/inc/BootstrapperApplication.h
+61
@@ -205,6 +205,10 @@ enum BOOTSTRAPPER_APPLICATION_MESSAGE
205 BOOTSTRAPPER_APPLICATION_MESSAGE_ONPLANROLLBACKBOUNDARY,
206 BOOTSTRAPPER_APPLICATION_MESSAGE_ONSETUPDATEBEGIN,
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,
212 };
213
214 enum BOOTSTRAPPER_APPLYCOMPLETE_ACTION
@@ -648,6 +652,20 @@ struct BA_ONDETECTBEGIN_RESULTS
652 BOOL fCancel;
653 };
654
655 +struct BA_ONDETECTCOMPATIBLEMSIPACKAGE_ARGS
656 +{
657 + DWORD cbSize;
658 + LPCWSTR wzPackageId;
659 + LPCWSTR wzCompatiblePackageId;
660 + LPCWSTR wzCompatiblePackageVersion;
661 +};
662 +
663 +struct BA_ONDETECTCOMPATIBLEMSIPACKAGE_RESULTS
664 +{
665 + DWORD cbSize;
666 + BOOL fCancel;
667 +};
668 +
669 struct BA_ONDETECTCOMPLETE_ARGS
670 {
671 DWORD cbSize;
@@ -1023,6 +1041,36 @@ struct BA_ONPLANBEGIN_RESULTS
1041 BOOL fCancel;
1042 };
1043
1044 +struct BA_ONPLANCOMPATIBLEMSIPACKAGEBEGIN_ARGS
1045 +{
1046 + DWORD cbSize;
1047 + LPCWSTR wzPackageId;
1048 + LPCWSTR wzCompatiblePackageId;
1049 + LPCWSTR wzCompatiblePackageVersion;
1050 + BOOL fRecommendedRemove;
1051 +};
1052 +
1053 +struct BA_ONPLANCOMPATIBLEMSIPACKAGEBEGIN_RESULTS
1054 +{
1055 + DWORD cbSize;
1056 + BOOL fCancel;
1057 + BOOL fRequestRemove;
1058 +};
1059 +
1060 +struct BA_ONPLANCOMPATIBLEMSIPACKAGECOMPLETE_ARGS
1061 +{
1062 + DWORD cbSize;
1063 + LPCWSTR wzPackageId;
1064 + LPCWSTR wzCompatiblePackageId;
1065 + HRESULT hrStatus;
1066 + BOOL fRequestedRemove;
1067 +};
1068 +
1069 +struct BA_ONPLANCOMPATIBLEMSIPACKAGECOMPLETE_RESULTS
1070 +{
1071 + DWORD cbSize;
1072 +};
1073 +
1074 struct BA_ONPLANCOMPLETE_ARGS
1075 {
1076 DWORD cbSize;
@@ -1086,6 +1134,19 @@ struct BA_ONPLANMSIPACKAGE_RESULTS
1134 BOOTSTRAPPER_MSI_FILE_VERSIONING fileVersioning;
1135 };
1136
1137 +struct BA_ONPLANNEDCOMPATIBLEPACKAGE_ARGS
1138 +{
1139 + DWORD cbSize;
1140 + LPCWSTR wzPackageId;
1141 + LPCWSTR wzCompatiblePackageId;
1142 + BOOL fRemove;
1143 +};
1144 +
1145 +struct BA_ONPLANNEDCOMPATIBLEPACKAGE_RESULTS
1146 +{
1147 + DWORD cbSize;
1148 +};
1149 +
1150 struct BA_ONPLANNEDPACKAGE_ARGS
1151 {
1152 DWORD cbSize;
src/api/burn/WixToolset.Mba.Core/BootstrapperApplication.cs
+99
@@ -63,6 +63,9 @@ namespace WixToolset.Mba.Core
63
64 /// <inheritdoc/>
65 public event EventHandler<DetectPackageBeginEventArgs> DetectPackageBegin;
66 +
67 + /// <inheritdoc/>
68 + public event EventHandler<DetectCompatibleMsiPackageEventArgs> DetectCompatibleMsiPackage;
69
70 /// <inheritdoc/>
71 public event EventHandler<DetectRelatedMsiPackageEventArgs> DetectRelatedMsiPackage;
@@ -91,6 +94,12 @@ namespace WixToolset.Mba.Core
94 /// <inheritdoc/>
95 public event EventHandler<PlanPackageBeginEventArgs> PlanPackageBegin;
96
97 + /// <inheritdoc/>
98 + public event EventHandler<PlanCompatibleMsiPackageBeginEventArgs> PlanCompatibleMsiPackageBegin;
99 +
100 + /// <inheritdoc/>
101 + public event EventHandler<PlanCompatibleMsiPackageCompleteEventArgs> PlanCompatibleMsiPackageComplete;
102 +
103 /// <inheritdoc/>
104 public event EventHandler<PlanPatchTargetEventArgs> PlanPatchTarget;
105
@@ -103,6 +112,9 @@ namespace WixToolset.Mba.Core
112 /// <inheritdoc/>
113 public event EventHandler<PlanPackageCompleteEventArgs> PlanPackageComplete;
114
115 + /// <inheritdoc/>
116 + public event EventHandler<PlannedCompatiblePackageEventArgs> PlannedCompatiblePackage;
117 +
118 /// <inheritdoc/>
119 public event EventHandler<PlannedPackageEventArgs> PlannedPackage;
120
@@ -414,6 +426,19 @@ namespace WixToolset.Mba.Core
426 }
427 }
428
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>
443 /// Called by the engine, raises the <see cref="DetectRelatedMsiPackage"/> event.
444 /// </summary>
@@ -530,6 +555,32 @@ namespace WixToolset.Mba.Core
555 }
556 }
557
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>
585 /// Called by the engine, raises the <see cref="PlanPatchTarget"/> event.
586 /// </summary>
@@ -582,6 +633,19 @@ namespace WixToolset.Mba.Core
633 }
634 }
635
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>
650 /// Called by the engine, raises the <see cref="PlannedPackage"/> event.
651 /// </summary>
@@ -1363,6 +1427,15 @@ namespace WixToolset.Mba.Core
1427 return args.HResult;
1428 }
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 +
1439 int IBootstrapperApplication.OnDetectRelatedMsiPackage(string wzPackageId, string wzUpgradeCode, string wzProductCode, bool fPerMachine, string wzVersion, RelatedOperation operation, ref bool fCancel)
1440 {
1441 DetectRelatedMsiPackageEventArgs args = new DetectRelatedMsiPackageEventArgs(wzPackageId, wzUpgradeCode, wzProductCode, fPerMachine, wzVersion, operation, fCancel);
@@ -1445,6 +1518,24 @@ namespace WixToolset.Mba.Core
1518 return args.HResult;
1519 }
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 +
1539 int IBootstrapperApplication.OnPlanPatchTarget(string wzPackageId, string wzProductCode, RequestState recommendedState, ref RequestState pRequestedState, ref bool fCancel)
1540 {
1541 PlanPatchTargetEventArgs args = new PlanPatchTargetEventArgs(wzPackageId, wzProductCode, recommendedState, pRequestedState, fCancel);
@@ -1486,6 +1577,14 @@ namespace WixToolset.Mba.Core
1577 return args.HResult;
1578 }
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 +
1588 int IBootstrapperApplication.OnPlannedPackage(string wzPackageId, ActionState execute, ActionState rollback, bool fPlannedCache, bool fPlannedUncache)
1589 {
1590 var args = new PlannedPackageEventArgs(wzPackageId, execute, rollback, fPlannedCache, fPlannedUncache);
src/api/burn/WixToolset.Mba.Core/EventArgs.cs
+135
@@ -495,6 +495,37 @@ namespace WixToolset.Mba.Core
495 public string PackageId { get; private set; }
496 }
497
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>
530 /// Event arguments for <see cref="IDefaultBootstrapperApplication.DetectRelatedMsiPackage"/>
531 /// </summary>
@@ -775,6 +806,80 @@ namespace WixToolset.Mba.Core
806 public BOOTSTRAPPER_CACHE_TYPE CacheType { get; set; }
807 }
808
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>
884 /// Event arguments for <see cref="IDefaultBootstrapperApplication.PlanRollbackBoundary"/>
885 /// </summary>
@@ -980,6 +1085,36 @@ namespace WixToolset.Mba.Core
1085 public RequestState Requested { get; private set; }
1086 }
1087
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>
1119 /// Event arguments for <see cref="IDefaultBootstrapperApplication.PlannedPackage"/>
1120 /// </summary>
src/api/burn/WixToolset.Mba.Core/IBootstrapperApplication.cs
+49 -8
@@ -181,17 +181,21 @@ namespace WixToolset.Mba.Core
181 [MarshalAs(UnmanagedType.Bool)] ref bool fCancel
182 );
183
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>
197 /// See <see cref="IDefaultBootstrapperApplication.DetectRelatedMsiPackage"/>.
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>
199 [PreserveSig]
200 [return: MarshalAs(UnmanagedType.I4)]
201 int OnDetectRelatedMsiPackage(
@@ -317,6 +321,32 @@ namespace WixToolset.Mba.Core
321 [MarshalAs(UnmanagedType.Bool)] ref bool fCancel
322 );
323
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>
351 /// See <see cref="IDefaultBootstrapperApplication.PlanPatchTarget"/>.
352 /// </summary>
@@ -387,6 +417,17 @@ namespace WixToolset.Mba.Core
417 [MarshalAs(UnmanagedType.U4)] RequestState requested
418 );
419
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>
432 /// See <see cref="IDefaultBootstrapperApplication.PlannedPackage"/>.
433 /// </summary>
src/api/burn/WixToolset.Mba.Core/IDefaultBootstrapperApplication.cs
+21 -1
@@ -133,6 +133,11 @@ namespace WixToolset.Mba.Core
133 /// </summary>
134 event EventHandler<DetectBeginEventArgs> DetectBegin;
135
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>
142 /// Fired when the detection phase has completed.
143 /// </summary>
@@ -268,6 +273,16 @@ namespace WixToolset.Mba.Core
273 /// </summary>
274 event EventHandler<PlanBeginEventArgs> PlanBegin;
275
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>
287 /// Fired when the engine has completed planning the installation.
288 /// </summary>
@@ -278,6 +293,11 @@ namespace WixToolset.Mba.Core
293 /// </summary>
294 event EventHandler<PlanForwardCompatibleBundleEventArgs> PlanForwardCompatibleBundle;
295
296 + /// <summary>
297 + /// Fired when the engine has completed planning a compatible package.
298 + /// </summary>
299 + event EventHandler<PlannedCompatiblePackageEventArgs> PlannedCompatiblePackage;
300 +
301 /// <summary>
302 /// Fired when the engine has completed planning a package.
303 /// </summary>
@@ -399,4 +419,4 @@ namespace WixToolset.Mba.Core
419 /// </summary>
420 event EventHandler<UnregisterCompleteEventArgs> UnregisterComplete;
421 }
402 -}
\ No newline at end of file
422 +}
src/api/burn/balutil/inc/BAFunctions.h
+4
@@ -84,6 +84,10 @@ enum BA_FUNCTIONS_MESSAGE
84 BA_FUNCTIONS_MESSAGE_ONPLANROLLBACKBOUNDARY = BOOTSTRAPPER_APPLICATION_MESSAGE_ONPLANROLLBACKBOUNDARY,
85 BA_FUNCTIONS_MESSAGE_ONSETUPDATEBEGIN = BOOTSTRAPPER_APPLICATION_MESSAGE_ONSETUPDATEBEGIN,
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,
91
92 BA_FUNCTIONS_MESSAGE_ONTHEMELOADED = 1024,
93 BA_FUNCTIONS_MESSAGE_WNDPROC,
src/api/burn/balutil/inc/BalBaseBAFunctions.h
+41
@@ -182,6 +182,16 @@ public: // IBootstrapperApplication
182 return S_OK;
183 }
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 +
195 virtual STDMETHODIMP OnDetectRelatedMsiPackage(
196 __in_z LPCWSTR /*wzPackageId*/,
197 __in_z LPCWSTR /*wzUpgradeCode*/,
@@ -276,6 +286,28 @@ public: // IBootstrapperApplication
286 return S_OK;
287 }
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 +
311 virtual STDMETHODIMP OnPlanPatchTarget(
312 __in_z LPCWSTR /*wzPackageId*/,
313 __in_z LPCWSTR /*wzProductCode*/,
@@ -322,6 +354,15 @@ public: // IBootstrapperApplication
354 return S_OK;
355 }
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 +
366 virtual STDMETHODIMP OnPlannedPackage(
367 __in_z LPCWSTR /*wzPackageId*/,
368 __in BOOTSTRAPPER_ACTION_STATE /*execute*/,
src/api/burn/balutil/inc/BalBaseBAFunctionsProc.h
+4
@@ -155,6 +155,10 @@ static HRESULT WINAPI BalBaseBAFunctionsProc(
155 case BA_FUNCTIONS_MESSAGE_ONPLANROLLBACKBOUNDARY:
156 case BA_FUNCTIONS_MESSAGE_ONSETUPDATEBEGIN:
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:
162 hr = BalBaseBootstrapperApplicationProc((BOOTSTRAPPER_APPLICATION_MESSAGE)message, pvArgs, pvResults, pvContext);
163 break;
164 case BA_FUNCTIONS_MESSAGE_ONTHEMELOADED:
src/api/burn/balutil/inc/BalBaseBootstrapperApplication.h
+43
@@ -189,6 +189,17 @@ public: // IBootstrapperApplication
189 return S_OK;
190 }
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 +
203 virtual STDMETHODIMP OnDetectRelatedMsiPackage(
204 __in_z LPCWSTR /*wzPackageId*/,
205 __in_z LPCWSTR /*wzUpgradeCode*/,
@@ -290,6 +301,29 @@ public: // IBootstrapperApplication
301 return S_OK;
302 }
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 +
327 virtual STDMETHODIMP OnPlanPatchTarget(
328 __in_z LPCWSTR /*wzPackageId*/,
329 __in_z LPCWSTR /*wzProductCode*/,
@@ -339,6 +373,15 @@ public: // IBootstrapperApplication
373 return S_OK;
374 }
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 +
385 virtual STDMETHODIMP OnPlannedPackage(
386 __in_z LPCWSTR /*wzPackageId*/,
387 __in BOOTSTRAPPER_ACTION_STATE /*execute*/,
src/api/burn/balutil/inc/BalBaseBootstrapperApplicationProc.h
+48
@@ -126,6 +126,15 @@ static HRESULT BalBaseBAProcOnDetectPackageBegin(
126 return pBA->OnDetectPackageBegin(pArgs->wzPackageId, &pResults->fCancel);
127 }
128
129 +static 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 +
138 static HRESULT BalBaseBAProcOnDetectRelatedMsiPackage(
139 __in IBootstrapperApplication* pBA,
140 __in BA_ONDETECTRELATEDMSIPACKAGE_ARGS* pArgs,
@@ -189,6 +198,24 @@ static HRESULT BalBaseBAProcOnPlanPackageBegin(
198 return pBA->OnPlanPackageBegin(pArgs->wzPackageId, pArgs->state, pArgs->fCached, pArgs->installCondition, pArgs->recommendedState, pArgs->recommendedCacheType, &pResults->requestedState, &pResults->requestedCacheType, &pResults->fCancel);
199 }
200
201 +static 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 +
210 +static 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 +
219 static HRESULT BalBaseBAProcOnPlanPatchTarget(
220 __in IBootstrapperApplication* pBA,
221 __in BA_ONPLANPATCHTARGET_ARGS* pArgs,
@@ -216,6 +243,15 @@ static HRESULT BalBaseBAProcOnPlanPackageComplete(
243 return pBA->OnPlanPackageComplete(pArgs->wzPackageId, pArgs->hrStatus, pArgs->requested);
244 }
245
246 +static 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 +
255 static HRESULT BalBaseBAProcOnPlannedPackage(
256 __in IBootstrapperApplication* pBA,
257 __in BA_ONPLANNEDPACKAGE_ARGS* pArgs,
@@ -928,6 +964,18 @@ static HRESULT WINAPI BalBaseBootstrapperApplicationProc(
964 case BOOTSTRAPPER_APPLICATION_MESSAGE_ONSETUPDATECOMPLETE:
965 hr = BalBaseBAProcOnSetUpdateComplete(pBA, reinterpret_cast<BA_ONSETUPDATECOMPLETE_ARGS*>(pvArgs), reinterpret_cast<BA_ONSETUPDATECOMPLETE_RESULTS*>(pvResults));
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;
979 }
980 }
981
src/api/burn/balutil/inc/IBootstrapperApplication.h
+34
@@ -101,6 +101,14 @@ DECLARE_INTERFACE_IID_(IBootstrapperApplication, IUnknown, "53C31D56-49C0-426B-A
101 __inout BOOL* pfCancel
102 ) = 0;
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 +
112 // OnDetectRelatedMsiPackage - called when the engine begins detects a related package.
113 STDMETHOD(OnDetectRelatedMsiPackage)(
114 __in_z LPCWSTR wzPackageId,
@@ -181,6 +189,25 @@ DECLARE_INTERFACE_IID_(IBootstrapperApplication, IUnknown, "53C31D56-49C0-426B-A
189 __inout BOOL* pfCancel
190 ) = 0;
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 +
211 // OnPlanPatchTarget - called when the engine is about to plan a target
212 // of an MSP package.
213 STDMETHOD(OnPlanPatchTarget)(
@@ -223,6 +250,13 @@ DECLARE_INTERFACE_IID_(IBootstrapperApplication, IUnknown, "53C31D56-49C0-426B-A
250 __in BOOTSTRAPPER_REQUEST_STATE requested
251 ) = 0;
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 +
260 // OnPlannedPackage - called after the engine has completed planning a package.
261 STDMETHOD(OnPlannedPackage)(
262 __in_z LPCWSTR wzPackageId,
src/burn/engine/apply.cpp
+134 -28
@@ -60,7 +60,7 @@ typedef struct _BURN_EXECUTE_CONTEXT
60 BURN_USER_EXPERIENCE* pUX;
61 BURN_APPLY_CONTEXT* pApplyContext;
62 BOOL fRollback;
63 - BURN_PACKAGE* pExecutingPackage;
63 + LPCWSTR wzExecutingPackageId;
64 DWORD cExecutedPackages;
65 DWORD cExecutePackagesTotal;
66 } BURN_EXECUTE_CONTEXT;
@@ -278,6 +278,19 @@ static void ResetTransactionRegistrationState(
278 __in BURN_ENGINE_STATE* pEngineState,
279 __in BOOL fCommit
280 );
281 +static 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 + );
289 +static HRESULT CleanCompatiblePackage(
290 + __in BURN_CACHE* pCache,
291 + __in HANDLE hElevatedPipe,
292 + __in BURN_PACKAGE* pPackage
293 + );
294 static HRESULT CleanPackage(
295 __in BURN_CACHE* pCache,
296 __in HANDLE hElevatedPipe,
@@ -300,7 +313,8 @@ static HRESULT ReportOverallProgressTicks(
313 static HRESULT ExecutePackageComplete(
314 __in BURN_USER_EXPERIENCE* pUX,
315 __in BURN_VARIABLES* pVariables,
303 - __in BURN_PACKAGE* pPackage,
316 + __in LPCWSTR wzPackageId,
317 + __in BOOL fPackageVital,
318 __in HRESULT hrOverall,
319 __in HRESULT hrExecute,
320 __in BOOL fRollback,
@@ -808,7 +822,20 @@ extern "C" void ApplyClean(
822 BURN_CLEAN_ACTION* pCleanAction = pPlan->rgCleanActions + i;
823 BURN_PACKAGE* pPackage = pCleanAction->pPackage;
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 + }
839 }
840 }
841
@@ -2350,6 +2377,11 @@ static HRESULT DoExecuteAction(
2377 ExitOnFailure(hr, "Failed to execute commit MSI transaction action.");
2378 break;
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 +
2385 default:
2386 hr = E_UNEXPECTED;
2387 ExitOnFailure(hr, "Invalid execute action.");
@@ -2509,7 +2541,7 @@ static HRESULT ExecuteRelatedBundle(
2541 }
2542
2543 Assert(pContext->fRollback == fRollback);
2512 - pContext->pExecutingPackage = pPackage;
2544 + pContext->wzExecutingPackageId = pPackage->sczId;
2545 fBeginCalled = TRUE;
2546
2547 // Send package execute begin to BA.
@@ -2550,7 +2582,7 @@ static HRESULT ExecuteRelatedBundle(
2582 LExit:
2583 if (fBeginCalled)
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);
2586 }
2587
2588 return hr;
@@ -2581,7 +2613,7 @@ static HRESULT ExecuteExePackage(
2613 }
2614
2615 Assert(pContext->fRollback == fRollback);
2584 - pContext->pExecutingPackage = pPackage;
2616 + pContext->wzExecutingPackageId = pPackage->sczId;
2617 fBeginCalled = TRUE;
2618
2619 // Send package execute begin to BA.
@@ -2629,7 +2661,7 @@ LExit:
2661
2662 if (fBeginCalled)
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);
2665 }
2666
2667 return hr;
@@ -2659,7 +2691,7 @@ static HRESULT ExecuteMsiPackage(
2691 }
2692
2693 Assert(pContext->fRollback == fRollback);
2662 - pContext->pExecutingPackage = pPackage;
2694 + pContext->wzExecutingPackageId = pPackage->sczId;
2695 fBeginCalled = TRUE;
2696
2697 // Send package execute begin to BA.
@@ -2693,7 +2725,7 @@ LExit:
2725
2726 if (fBeginCalled)
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);
2729 }
2730
2731 return hr;
@@ -2723,7 +2755,7 @@ static HRESULT ExecuteMspPackage(
2755 }
2756
2757 Assert(pContext->fRollback == fRollback);
2726 - pContext->pExecutingPackage = pPackage;
2758 + pContext->wzExecutingPackageId = pPackage->sczId;
2759 fBeginCalled = TRUE;
2760
2761 // Send package execute begin to BA.
@@ -2766,7 +2798,7 @@ LExit:
2798
2799 if (fBeginCalled)
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);
2802 }
2803
2804 return hr;
@@ -2798,7 +2830,7 @@ static HRESULT ExecuteMsuPackage(
2830 }
2831
2832 Assert(pContext->fRollback == fRollback);
2801 - pContext->pExecutingPackage = pPackage;
2833 + pContext->wzExecutingPackageId = pPackage->sczId;
2834 fBeginCalled = TRUE;
2835
2836 // Send package execute begin to BA.
@@ -2846,7 +2878,7 @@ LExit:
2878
2879 if (fBeginCalled)
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);
2882 }
2883
2884 return hr;
@@ -3110,6 +3142,79 @@ static void ResetTransactionRegistrationState(
3142 }
3143 }
3144
3145 +static 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 +
3188 +LExit:
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 +
3198 +static 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 +
3218 static HRESULT CleanPackage(
3219 __in BURN_CACHE* pCache,
3220 __in HANDLE hElevatedPipe,
@@ -3150,16 +3255,16 @@ static int GenericExecuteMessageHandler(
3255 case GENERIC_EXECUTE_MESSAGE_PROGRESS:
3256 {
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.
3259 }
3260 break;
3261
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.
3264 break;
3265
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.
3268 fPassthrough = TRUE;
3269 break;
3270 }
@@ -3188,25 +3293,25 @@ static int MsiExecuteMessageHandler(
3293 case WIU_MSI_EXECUTE_MESSAGE_PROGRESS:
3294 {
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.
3297 }
3298 break;
3299
3300 case WIU_MSI_EXECUTE_MESSAGE_ERROR:
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.
3303 break;
3304
3305 case WIU_MSI_EXECUTE_MESSAGE_MSI_MESSAGE:
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.
3308 break;
3309
3310 case WIU_MSI_EXECUTE_MESSAGE_MSI_RM_FILES_IN_USE:
3311 fRestartManager = TRUE;
3312 __fallthrough;
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.
3315 fPassthrough = TRUE;
3316 break;
3317 }
@@ -3246,7 +3351,8 @@ static HRESULT ReportOverallProgressTicks(
3351 static HRESULT ExecutePackageComplete(
3352 __in BURN_USER_EXPERIENCE* pUX,
3353 __in BURN_VARIABLES* pVariables,
3249 - __in BURN_PACKAGE* pPackage,
3354 + __in LPCWSTR wzPackageId,
3355 + __in BOOL fPackageVital,
3356 __in HRESULT hrOverall,
3357 __in HRESULT hrExecute,
3358 __in BOOL fRollback,
@@ -3256,10 +3362,10 @@ static HRESULT ExecutePackageComplete(
3362 )
3363 {
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;
3366
3367 // Send package execute complete to BA.
3262 - UserExperienceOnExecutePackageComplete(pUX, pPackage->sczId, hr, *pRestart, &executePackageCompleteAction);
3368 + UserExperienceOnExecutePackageComplete(pUX, wzPackageId, hr, *pRestart, &executePackageCompleteAction);
3369 if (BOOTSTRAPPER_EXECUTEPACKAGECOMPLETE_ACTION_RESTART == executePackageCompleteAction)
3370 {
3371 *pRestart = BOOTSTRAPPER_APPLY_RESTART_INITIATED;
@@ -3271,23 +3377,23 @@ static HRESULT ExecutePackageComplete(
3377 if (BOOTSTRAPPER_APPLY_RESTART_INITIATED == *pRestart)
3378 {
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);
3381 }
3382
3383 // If we're retrying, leave a message in the log file and say everything is okay.
3384 if (*pfRetry)
3385 {
3280 - LogId(REPORT_STANDARD, MSG_APPLY_RETRYING_PACKAGE, pPackage->sczId, hrExecute);
3386 + LogId(REPORT_STANDARD, MSG_APPLY_RETRYING_PACKAGE, wzPackageId, hrExecute);
3387 hr = S_OK;
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.
3390 {
3285 - LogId(REPORT_STANDARD, MSG_APPLY_CONTINUING_NONVITAL_PACKAGE, pPackage->sczId, hrExecute);
3391 + LogId(REPORT_STANDARD, MSG_APPLY_CONTINUING_NONVITAL_PACKAGE, wzPackageId, hrExecute);
3392 hr = S_OK;
3393 }
3394 else
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));
3397 }
3398
3399 return hr;
src/burn/engine/core.cpp
+7 -1
@@ -389,6 +389,7 @@ extern "C" HRESULT CoreDetect(
389 pPackage->currentState = BOOTSTRAPPER_PACKAGE_STATE_UNKNOWN;
390 pPackage->cacheRegistrationState = BURN_PACKAGE_REGISTRATION_STATE_UNKNOWN;
391 pPackage->installRegistrationState = BURN_PACKAGE_REGISTRATION_STATE_UNKNOWN;
392 + pPackage->compatiblePackage.fDetected = FALSE;
393 }
394 }
395
@@ -2240,7 +2241,7 @@ static void LogPackages(
2241 }
2242
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 +
2245 if (BURN_PACKAGE_TYPE_MSI == pPackage->type)
2246 {
2247 if (pPackage->Msi.cFeatures)
@@ -2266,6 +2267,11 @@ static void LogPackages(
2267 LogId(REPORT_STANDARD, MSG_PLANNED_SLIPSTREAMED_MSP_TARGET, pSlipstreamMsp->pMspPackage->sczId, LoggingActionStateToString(pSlipstreamMsp->execute), LoggingActionStateToString(pSlipstreamMsp->rollback));
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 + }
2275 }
2276 else if (BURN_PACKAGE_TYPE_MSP == pPackage->type && pPackage->Msp.cTargetProductCodes)
2277 {
src/burn/engine/dependency.cpp
+108 -15
@@ -54,6 +54,10 @@ static HRESULT AddPackageDependencyActions(
54 __in const BURN_DEPENDENCY_ACTION dependencyRollbackAction
55 );
56
57 +static LPCWSTR GetPackageProviderId(
58 + __in const BURN_PACKAGE* pPackage
59 + );
60 +
61 static HRESULT RegisterPackageProvider(
62 __in const BURN_PACKAGE* pPackage
63 );
@@ -299,6 +303,9 @@ extern "C" HRESULT DependencyDetectChainPackage(
303 hr = DetectPackageDependents(pPackage, pRegistration);
304 ExitOnFailure(hr, "Failed to detect dependents for package '%ls'", pPackage->sczId);
305
306 + hr = DependencyDetectCompatibleEntry(pPackage, pRegistration);
307 + ExitOnFailure(hr, "Failed to detect compatible package for package '%ls'", pPackage->sczId);
308 +
309 LExit:
310 return hr;
311 }
@@ -396,6 +403,8 @@ extern "C" HRESULT DependencyPlanPackageBegin(
403 STRINGDICT_HANDLE sdIgnoredDependents = NULL;
404 BURN_DEPENDENCY_ACTION dependencyExecuteAction = 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;
408
409 pPackage->dependencyExecute = BURN_DEPENDENCY_ACTION_NONE;
410 pPackage->dependencyRollback = BURN_DEPENDENCY_ACTION_NONE;
@@ -415,7 +424,7 @@ extern "C" HRESULT DependencyPlanPackageBegin(
424 }
425
426 // If we're uninstalling the package, check if any dependents are registered.
418 - if (BOOTSTRAPPER_ACTION_STATE_UNINSTALL == pPackage->execute)
427 + if (fAttemptingUninstall)
428 {
429 // Build up a list of dependents to ignore, including the current bundle.
430 hr = GetIgnoredDependents(pPackage, pPlan, &sdIgnoredDependents);
@@ -444,9 +453,9 @@ extern "C" HRESULT DependencyPlanPackageBegin(
453 {
454 hr = S_OK;
455
447 - if (!pPackage->fDependencyManagerWasHere)
456 + if (!fDependentBlocksUninstall)
457 {
449 - pPackage->fDependencyManagerWasHere = TRUE;
458 + fDependentBlocksUninstall = TRUE;
459
460 LogId(REPORT_STANDARD, MSG_DEPENDENCY_PACKAGE_HASDEPENDENTS, pPackage->sczId);
461 }
@@ -459,14 +468,20 @@ extern "C" HRESULT DependencyPlanPackageBegin(
468 }
469 }
470
471 + if (BOOTSTRAPPER_ACTION_STATE_UNINSTALL == pPackage->execute)
472 + {
473 + pPackage->fDependencyManagerWasHere = fDependentBlocksUninstall;
474 + }
475 +
476 // Calculate the dependency actions before the package itself is planned.
477 CalculateDependencyActionStates(pPackage, pPlan->action, &dependencyExecuteAction, &dependencyRollbackAction);
478
479 // If dependents were found, change the action to not uninstall the package.
466 - if (pPackage->fDependencyManagerWasHere)
480 + if (fDependentBlocksUninstall)
481 {
482 pPackage->execute = BOOTSTRAPPER_ACTION_STATE_NONE;
483 pPackage->rollback = BOOTSTRAPPER_ACTION_STATE_NONE;
484 + pPackage->compatiblePackage.fRemove = FALSE;
485 }
486 else
487 {
@@ -494,7 +509,7 @@ extern "C" HRESULT DependencyPlanPackageBegin(
509 }
510
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)
513 {
514 for (DWORD i = 0; i < pPackage->cDependencyProviders; ++i)
515 {
@@ -739,6 +754,73 @@ extern "C" void DependencyUnregisterBundle(
754 }
755 }
756
757 +extern "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 +
820 +LExit:
821 + return hr;
822 +}
823 +
824 // internal functions
825
826
@@ -1170,24 +1252,35 @@ LExit:
1252 return hr;
1253 }
1254
1255 +static 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 +
1274 static HRESULT RegisterPackageProvider(
1275 __in const BURN_PACKAGE* pPackage
1276 )
1277 {
1278 HRESULT hr = S_OK;
1178 - LPWSTR wzId = NULL;
1179 - HKEY hkRoot = pPackage->fPerMachine ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER;
1279
1280 if (pPackage->rgDependencyProviders)
1281 {
1183 - if (BURN_PACKAGE_TYPE_MSI == pPackage->type)
1184 - {
1185 - wzId = pPackage->Msi.sczProductCode;
1186 - }
1187 - else if (BURN_PACKAGE_TYPE_MSP == pPackage->type)
1188 - {
1189 - wzId = pPackage->Msp.sczPatchCode;
1190 - }
1282 + HKEY hkRoot = pPackage->fPerMachine ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER;
1283 + LPCWSTR wzId = GetPackageProviderId(pPackage);
1284
1285 for (DWORD i = 0; i < pPackage->cDependencyProviders; ++i)
1286 {
src/burn/engine/dependency.h
+5
@@ -191,6 +191,11 @@ void DependencyUnregisterBundle(
191 __in const BURN_PACKAGES* pPackages
192 );
193
194 +HRESULT DependencyDetectCompatibleEntry(
195 + __in BURN_PACKAGE* pPackage,
196 + __in BURN_REGISTRATION* pRegistration
197 + );
198 +
199 #if defined(__cplusplus)
200 }
201 #endif
src/burn/engine/detect.cpp
+2
@@ -98,6 +98,8 @@ extern "C" void DetectReset(
98 pProvider->rgDependents = NULL;
99 pProvider->cDependents = 0;
100 }
101 +
102 + PackageUninitializeCompatible(&pPackage->compatiblePackage);
103 }
104
105 for (DWORD iPatchInfo = 0; iPatchInfo < pPackages->cPatchInfo; ++iPatchInfo)
src/burn/engine/elevation.cpp
+297 -6
@@ -32,6 +32,8 @@ typedef enum _BURN_ELEVATION_MESSAGE_TYPE
32 BURN_ELEVATION_MESSAGE_TYPE_BEGIN_MSI_TRANSACTION,
33 BURN_ELEVATION_MESSAGE_TYPE_COMMIT_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,
37
38 BURN_ELEVATION_MESSAGE_TYPE_APPLY_INITIALIZE_PAUSE_AU_BEGIN,
39 BURN_ELEVATION_MESSAGE_TYPE_APPLY_INITIALIZE_PAUSE_AU_COMPLETE,
@@ -168,11 +170,16 @@ static HRESULT OnApplyInitialize(
170 __in HANDLE hPipe,
171 __in BURN_VARIABLES* pVariables,
172 __in BURN_REGISTRATION* pRegistration,
173 + __in BURN_PACKAGES* pPackages,
174 __in HANDLE* phLock,
175 __in BOOL* pfDisabledWindowsUpdate,
176 __in BYTE* pbData,
177 __in SIZE_T cbData
178 );
179 +static HRESULT ElevatedProcessDetect(
180 + __in BURN_REGISTRATION* pRegistration,
181 + __in BURN_PACKAGES* pPackages
182 + );
183 static HRESULT OnApplyUninitialize(
184 __in HANDLE* phLock
185 );
@@ -271,6 +278,14 @@ static HRESULT OnExecuteMsuPackage(
278 __in BYTE* pbData,
279 __in SIZE_T cbData
280 );
281 +static 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 + );
289 static HRESULT OnExecutePackageProviderAction(
290 __in BURN_PACKAGES* pPackages,
291 __in BURN_RELATED_BUNDLES* pRelatedBundles,
@@ -306,6 +321,12 @@ static int MsiExecuteMessageHandler(
321 __in WIU_MSI_EXECUTE_MESSAGE* pMessage,
322 __in_opt LPVOID pvContext
323 );
324 +static HRESULT OnCleanCompatiblePackage(
325 + __in BURN_CACHE* pCache,
326 + __in BURN_PACKAGES* pPackages,
327 + __in BYTE* pbData,
328 + __in SIZE_T cbData
329 + );
330 static HRESULT OnCleanPackage(
331 __in BURN_CACHE* pCache,
332 __in BURN_PACKAGES* pPackages,
@@ -1230,6 +1251,58 @@ LExit:
1251 return hr;
1252 }
1253
1254 +extern "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 +
1300 +LExit:
1301 + ReleaseBuffer(pbData);
1302 +
1303 + return hr;
1304 +}
1305 +
1306 extern "C" HRESULT ElevationExecutePackageProviderAction(
1307 __in HANDLE hPipe,
1308 __in BURN_EXECUTE_ACTION* pExecuteAction
@@ -1295,6 +1368,35 @@ LExit:
1368 return hr;
1369 }
1370
1371 +extern "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 +
1394 +LExit:
1395 + ReleaseBuffer(pbData);
1396 +
1397 + return hr;
1398 +}
1399 +
1400 /*******************************************************************
1401 ElevationCleanPackage -
1402
@@ -1940,7 +2042,7 @@ static HRESULT ProcessElevatedChildMessage(
2042 break;
2043
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);
2046 break;
2047
2048 case BURN_ELEVATION_MESSAGE_TYPE_APPLY_UNINITIALIZE:
@@ -2003,6 +2105,14 @@ static HRESULT ProcessElevatedChildMessage(
2105 hrResult = OnLaunchApprovedExe(pContext->hPipe, pContext->pApprovedExes, pContext->pCache, pContext->pVariables, (BYTE*)pMsg->pvData, pMsg->cbData);
2106 break;
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 +
2116 default:
2117 hr = E_INVALIDARG;
2118 ExitOnRootFailure(hr, "Unexpected elevated message sent to child process, msg: %u", pMsg->dwMessage);
@@ -2082,6 +2192,7 @@ static HRESULT OnApplyInitialize(
2192 __in HANDLE hPipe,
2193 __in BURN_VARIABLES* pVariables,
2194 __in BURN_REGISTRATION* pRegistration,
2195 + __in BURN_PACKAGES* pPackages,
2196 __in HANDLE* phLock,
2197 __in BOOL* pfDisabledWindowsUpdate,
2198 __in BYTE* pbData,
@@ -2113,11 +2224,9 @@ static HRESULT OnApplyInitialize(
2224 hr = ApplyLock(TRUE, phLock);
2225 ExitOnFailure(hr, "Failed to acquire lock due to setup in other session.");
2226
2116 - // Reset and reload the related bundles.
2117 - RelatedBundlesUninitialize(&pRegistration->relatedBundles);
2118 -
2119 - hr = RelatedBundlesInitializeForScope(TRUE, pRegistration, &pRegistration->relatedBundles);
2120 - ExitOnFailure(hr, "Failed to initialize per-machine related bundles.");
2227 + // Detect.
2228 + hr = ElevatedProcessDetect(pRegistration, pPackages);
2229 + ExitOnFailure(hr, "Failed to run detection in elevated process.");
2230
2231 // Attempt to pause AU with best effort.
2232 if (BURN_AU_PAUSE_ACTION_IFELEVATED == dwAUAction || BURN_AU_PAUSE_ACTION_IFELEVATED_NORESUME == dwAUAction)
@@ -2187,6 +2296,39 @@ LExit:
2296 return hr;
2297 }
2298
2299 +static 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 +
2328 +LExit:
2329 + return hr;
2330 +}
2331 +
2332 static HRESULT OnApplyUninitialize(
2333 __in HANDLE* phLock
2334 )
@@ -2659,6 +2801,11 @@ static HRESULT OnExecuteExePackage(
2801 hr = PackageFindById(pPackages, sczPackage, &executeAction.exePackage.pPackage);
2802 ExitOnFailure(hr, "Failed to find package: %ls", sczPackage);
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 +
2809 // Execute EXE package.
2810 hr = ExeEngineExecutePackage(&executeAction, pCache, pVariables, static_cast<BOOL>(dwRollback), GenericExecuteMessageHandler, hPipe, &exeRestart);
2811 ExitOnFailure(hr, "Failed to execute EXE package.");
@@ -2760,6 +2907,11 @@ static HRESULT OnExecuteMsiPackage(
2907 hr = VariableDeserialize(pVariables, FALSE, pbData, cbData, &iData);
2908 ExitOnFailure(hr, "Failed to read variables.");
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 +
2915 // Execute MSI package.
2916 hr = MsiEngineExecutePackage(hwndParent, &executeAction, pCache, pVariables, fRollback, MsiExecuteMessageHandler, hPipe, &msiRestart);
2917 ExitOnFailure(hr, "Failed to execute MSI package.");
@@ -2859,6 +3011,11 @@ static HRESULT OnExecuteMspPackage(
3011 hr = BuffReadNumber(pbData, cbData, &iData, (DWORD*)&fRollback);
3012 ExitOnFailure(hr, "Failed to read rollback flag.");
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 +
3019 // Execute MSP package.
3020 hr = MspEngineExecutePackage(hwndParent, &executeAction, pCache, pVariables, fRollback, MsiExecuteMessageHandler, hPipe, &restart);
3021 ExitOnFailure(hr, "Failed to execute MSP package.");
@@ -2920,6 +3077,11 @@ static HRESULT OnExecuteMsuPackage(
3077 hr = PackageFindById(pPackages, sczPackage, &executeAction.msuPackage.pPackage);
3078 ExitOnFailure(hr, "Failed to find package: %ls", sczPackage);
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 +
3085 // execute MSU package
3086 hr = MsuEngineExecutePackage(&executeAction, pCache, pVariables, static_cast<BOOL>(dwRollback), static_cast<BOOL>(dwStopWusaService), GenericExecuteMessageHandler, hPipe, &restart);
3087 ExitOnFailure(hr, "Failed to execute MSU package.");
@@ -2943,6 +3105,88 @@ LExit:
3105 return hr;
3106 }
3107
3108 +static 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 +
3170 +LExit:
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 +
3190 static HRESULT OnExecutePackageProviderAction(
3191 __in BURN_PACKAGES* pPackages,
3192 __in BURN_RELATED_BUNDLES* pRelatedBundles,
@@ -3258,6 +3502,53 @@ LExit:
3502 return nResult;
3503 }
3504
3505 +static 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 +
3546 +LExit:
3547 + ReleaseStr(sczPackageId);
3548 + ReleaseStr(sczCompatiblePackageId);
3549 + return hr;
3550 +}
3551 +
3552 static HRESULT OnCleanPackage(
3553 __in BURN_CACHE* pCache,
3554 __in BURN_PACKAGES* pPackages,
src/burn/engine/elevation.h
+12 -5
@@ -127,6 +127,16 @@ HRESULT ElevationExecuteMsuPackage(
127 __in LPVOID pvContext,
128 __out BOOTSTRAPPER_APPLY_RESTART* pRestart
129 );
130 +HRESULT 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 + );
140 HRESULT ElevationExecutePackageProviderAction(
141 __in HANDLE hPipe,
142 __in BURN_EXECUTE_ACTION* pExecuteAction
@@ -135,12 +145,9 @@ HRESULT ElevationExecutePackageDependencyAction(
145 __in HANDLE hPipe,
146 __in BURN_EXECUTE_ACTION* pExecuteAction
147 );
138 -HRESULT ElevationLaunchElevatedChild(
148 +HRESULT ElevationCleanCompatiblePackage(
149 __in HANDLE hPipe,
140 - __in BURN_PACKAGE* pPackage,
141 - __in LPCWSTR wzPipeName,
142 - __in LPCWSTR wzPipeToken,
143 - __out DWORD* pdwChildPid
150 + __in BURN_PACKAGE* pPackage
151 );
152 HRESULT ElevationCleanPackage(
153 __in HANDLE hPipe,
src/burn/engine/engine.mc
+21 -2
@@ -262,6 +262,13 @@ Detected forward compatible bundle: %1!ls!, type: %2!hs!, scope: %3!hs!, version
262 .
263
264 MessageId=108
265 +Severity=Success
266 +SymbolicName=MSG_DETECTED_COMPATIBLE_PACKAGE_FROM_PROVIDER
267 +Language=English
268 +Detected compatible package: %1!ls!, provider: %2!ls!, installed: %3!ls!, version: %4!ls!, chained: %5!ls!
269 +.
270 +
271 +MessageId=109
272 Severity=Warning
273 SymbolicName=MSG_DETECT_RELATED_BUNDLE_NOT_CACHED
274 Language=English
@@ -429,6 +436,13 @@ Language=English
436 Plan skipped related bundle: %1!ls!, type: %2!hs!, because it was previously scheduled.
437 .
438
439 +MessageId=215
440 +Severity=Success
441 +SymbolicName=MSG_PLANNED_ORPHAN_PACKAGE_FROM_PROVIDER
442 +Language=English
443 +Will remove orphan package: %1!ls!, installed: %2!ls!, chained: %3!ls!
444 +.
445 +
446 MessageId=216
447 Severity=Success
448 SymbolicName=MSG_PLAN_SKIPPED_RELATED_BUNDLE_EMBEDDED_BUNDLE_NEWER
@@ -604,7 +618,6 @@ Language=English
618 Failed to layout container: %2!ls! to layout directory: %3!ls!, error: %1!ls!.
619 .
620
607 -
621 MessageId=317
622 Severity=Error
623 SymbolicName=MSG_FAILED_LAYOUT_PAYLOAD
@@ -927,7 +940,6 @@ Language=English
940 Calculating whether to keep registration
941 .
942
930 -
943 MessageId=374
944 Severity=Success
945 SymbolicName=MSG_POST_APPLY_PACKAGE
@@ -1005,6 +1017,13 @@ Language=English
1017 Skipping the rest of non-vital rollback boundary, id: %1!ls!
1018 .
1019
1020 +MessageId=390
1021 +Severity=Success
1022 +SymbolicName=MSG_APPLYING_ORPHAN_COMPATIBLE_PACKAGE
1023 +Language=English
1024 +Applying %1!hs! compatible package: %2!ls!, parent package: %3!ls!, action: %4!hs!, arguments: '%5!ls!'
1025 +.
1026 +
1027 MessageId=399
1028 Severity=Success
1029 SymbolicName=MSG_APPLY_COMPLETE
src/burn/engine/logging.cpp
+43
@@ -227,6 +227,49 @@ extern "C" void LoggingIncrementPackageSequence()
227 ++vdwPackageSequence;
228 }
229
230 +extern "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 +
266 +LExit:
267 + ReleaseStr(sczLogPathVariable);
268 + ReleaseStr(sczLogPath);
269 +
270 + return hr;
271 +}
272 +
273 extern "C" HRESULT LoggingSetPackageVariable(
274 __in BURN_PACKAGE* pPackage,
275 __in_z_opt LPCWSTR wzSuffix,
src/burn/engine/logging.h
+7
@@ -53,6 +53,13 @@ void LoggingOpenFailed();
53
54 void LoggingIncrementPackageSequence();
55
56 +HRESULT LoggingSetCompatiblePackageVariable(
57 + __in BURN_PACKAGE* pPackage,
58 + __in BURN_LOGGING* pLog,
59 + __in BURN_VARIABLES* pVariables,
60 + __out_opt LPWSTR* psczLogPath
61 + );
62 +
63 HRESULT LoggingSetPackageVariable(
64 __in BURN_PACKAGE* pPackage,
65 __in_z_opt LPCWSTR wzSuffix,
src/burn/engine/msiengine.cpp
+181
@@ -506,6 +506,9 @@ extern "C" HRESULT MsiEngineDetectPackage(
506 {
507 BURN_RELATED_MSI* pRelatedMsi = &pPackage->Msi.rgRelatedMsis[i];
508
509 + ReleaseVerutilVersion(pVersion);
510 + pVersion = NULL;
511 +
512 for (DWORD iProduct = 0; ; ++iProduct)
513 {
514 // get product
@@ -708,6 +711,46 @@ extern "C" HRESULT MsiEngineDetectPackage(
711 hr = DependencyDetectChainPackage(pPackage, pRegistration);
712 ExitOnFailure(hr, "Failed to detect dependencies for MSI package.");
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 +
754 LExit:
755 ReleaseStr(sczInstalledLanguage);
756 ReleaseStr(sczInstalledVersion);
@@ -716,8 +759,49 @@ LExit:
759 return hr;
760 }
761
762 +extern "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 +
795 +LExit:
796 + ReleaseStr(sczVersion);
797 + ReleaseStr(sczCacheId);
798 +
799 + return hr;
800 +}
801 +
802 extern "C" HRESULT MsiEnginePlanInitializePackage(
803 __in BURN_PACKAGE* pPackage,
804 + __in BOOTSTRAPPER_ACTION overallAction,
805 __in BURN_VARIABLES* pVariables,
806 __in BURN_USER_EXPERIENCE* pUserExperience
807 )
@@ -747,6 +831,18 @@ extern "C" HRESULT MsiEnginePlanInitializePackage(
831 }
832 }
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 +
846 LExit:
847 return hr;
848 }
@@ -989,6 +1085,17 @@ extern "C" HRESULT MsiEnginePlanAddPackage(
1085 LoggingSetPackageVariable(pPackage, NULL, FALSE, pLog, pVariables, &pAction->msiPackage.sczLogPath); // ignore errors.
1086 pAction->msiPackage.dwLoggingAttributes = pLog->dwAttributes;
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 + }
1099
1100 LExit:
1101 ReleaseMem(rgFeatureActions);
@@ -1246,6 +1353,80 @@ LExit:
1353 return hr;
1354 }
1355
1356 +extern "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 +
1407 +LExit:
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 +
1430 extern "C" HRESULT MsiEngineConcatBurnProperties(
1431 __in BOOTSTRAPPER_ACTION_STATE action,
1432 __in BURN_MSI_PROPERTY actionMsiProperty,
src/burn/engine/msiengine.h
+14
@@ -35,8 +35,12 @@ HRESULT MsiEngineDetectPackage(
35 __in BURN_REGISTRATION* pRegistration,
36 __in BURN_USER_EXPERIENCE* pUserExperience
37 );
38 +HRESULT MsiEngineDetectCompatiblePackage(
39 + __in BURN_PACKAGE* pPackage
40 + );
41 HRESULT MsiEnginePlanInitializePackage(
42 __in BURN_PACKAGE* pPackage,
43 + __in BOOTSTRAPPER_ACTION overallAction,
44 __in BURN_VARIABLES* pVariables,
45 __in BURN_USER_EXPERIENCE* pUserExperience
46 );
@@ -71,6 +75,16 @@ HRESULT MsiEngineExecutePackage(
75 __in LPVOID pvContext,
76 __out BOOTSTRAPPER_APPLY_RESTART* pRestart
77 );
78 +HRESULT 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 + );
88 HRESULT MsiEngineConcatBurnProperties(
89 __in BOOTSTRAPPER_ACTION_STATE action,
90 __in BURN_MSI_PROPERTY actionMsiProperty,
src/burn/engine/package.cpp
+25
@@ -366,6 +366,31 @@ extern "C" void PackageUninitialize(
366 MsuEnginePackageUninitialize(pPackage); // TODO: Modularization
367 break;
368 }
369 +
370 + PackageUninitializeCompatible(&pPackage->compatiblePackage);
371 +}
372 +
373 +extern "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));
394 }
395
396 extern "C" void PackagesUninitialize(
src/burn/engine/package.h
+34
@@ -147,6 +147,14 @@ typedef struct _BURN_MSIFEATURE
147 BOOTSTRAPPER_FEATURE_ACTION rollback; // only valid during Plan.
148 } BURN_MSIFEATURE;
149
150 +typedef struct _BURN_COMPATIBLE_PROVIDER_ENTRY
151 +{
152 + LPWSTR sczProviderKey;
153 + LPWSTR sczId;
154 + LPWSTR sczName;
155 + LPWSTR sczVersion;
156 +} BURN_COMPATIBLE_PROVIDER_ENTRY;
157 +
158 typedef struct _BURN_RELATED_MSI
159 {
160 LPWSTR sczUpgradeCode;
@@ -206,6 +214,27 @@ typedef struct _BURN_PATCH_TARGETCODE
214 BURN_PATCH_TARGETCODE_TYPE type;
215 } BURN_PATCH_TARGETCODE;
216
217 +typedef 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 +
238 typedef struct _BURN_PACKAGE
239 {
240 LPWSTR sczId;
@@ -259,6 +288,8 @@ typedef struct _BURN_PACKAGE
288 BURN_DEPENDENCY_PROVIDER* rgDependencyProviders;
289 DWORD cDependencyProviders;
290
291 + BURN_COMPATIBLE_PACKAGE compatiblePackage;
292 +
293 BURN_PACKAGE_TYPE type;
294 union
295 {
@@ -371,6 +402,9 @@ HRESULT PackagesParseFromXml(
402 void PackageUninitialize(
403 __in BURN_PACKAGE* pPackage
404 );
405 +void PackageUninitializeCompatible(
406 + __in BURN_COMPATIBLE_PACKAGE* pCompatiblePackage
407 + );
408 void PackagesUninitialize(
409 __in BURN_PACKAGES* pPackages
410 );
src/burn/engine/plan.cpp
+74 -9
@@ -96,6 +96,10 @@ static HRESULT AppendRollbackCacheAction(
96 __in BURN_PLAN* pPlan,
97 __out BURN_CACHE_ACTION** ppCacheAction
98 );
99 +static HRESULT AppendCleanAction(
100 + __in BURN_PLAN* pPlan,
101 + __out BURN_CLEAN_ACTION** ppCleanAction
102 + );
103 static HRESULT ProcessPayloadGroup(
104 __in BURN_PLAN* pPlan,
105 __in BURN_PAYLOAD_GROUP* pPayloadGroup
@@ -286,6 +290,10 @@ extern "C" void PlanUninitializeExecuteAction(
290 case BURN_EXECUTE_ACTION_TYPE_PACKAGE_DEPENDENCY:
291 ReleaseStr(pExecuteAction->packageDependency.sczBundleProviderKey);
292 break;
293 +
294 + case BURN_EXECUTE_ACTION_TYPE_UNINSTALL_MSI_COMPATIBLE_PACKAGE:
295 + ReleaseStr(pExecuteAction->uninstallMsiCompatiblePackage.sczLogPath);
296 + break;
297 }
298 }
299
@@ -823,6 +831,11 @@ static HRESULT PlanPackagesHelper(
831 BURN_PACKAGE* pPackage = rgPackages + iPackage;
832
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 + }
839 }
840
841 LExit:
@@ -878,7 +891,7 @@ static HRESULT InitializePackage(
891
892 if (BURN_PACKAGE_TYPE_MSI == pPackage->type)
893 {
881 - hr = MsiEnginePlanInitializePackage(pPackage, pVariables, pUX);
894 + hr = MsiEnginePlanInitializePackage(pPackage, pPlan->action, pVariables, pUX);
895 ExitOnFailure(hr, "Failed to initialize plan package: %ls", pPackage->sczId);
896 }
897
@@ -918,7 +931,7 @@ static HRESULT ProcessPackage(
931 }
932 else
933 {
921 - if (BOOTSTRAPPER_REQUEST_STATE_NONE != pPackage->requested)
934 + if (BOOTSTRAPPER_REQUEST_STATE_NONE != pPackage->requested || pPackage->compatiblePackage.fRequested)
935 {
936 // If the package is in a requested state, plan it.
937 hr = PlanExecutePackage(fBundlePerMachine, pUX, pPlan, pPackage, pLog, pVariables);
@@ -1151,7 +1164,7 @@ extern "C" HRESULT PlanExecutePackage(
1164 ExitOnFailure(hr, "Failed to complete plan dependency actions for package: %ls", pPackage->sczId);
1165
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)
1168 {
1169 LoggingIncrementPackageSequence();
1170
@@ -1560,12 +1573,10 @@ extern "C" HRESULT PlanCleanPackage(
1573
1574 if (fPlanCleanPackage)
1575 {
1563 - hr = MemEnsureArraySize(reinterpret_cast<LPVOID*>(&pPlan->rgCleanActions), pPlan->cCleanActions + 1, sizeof(BURN_CLEAN_ACTION), 5);
1564 - ExitOnFailure(hr, "Failed to grow plan's array of clean actions.");
1565 -
1566 - pCleanAction = pPlan->rgCleanActions + pPlan->cCleanActions;
1567 - ++pPlan->cCleanActions;
1576 + hr = AppendCleanAction(pPlan, &pCleanAction);
1577 + ExitOnFailure(hr, "Failed to append clean action to plan.");
1578
1579 + pCleanAction->type = BURN_CLEAN_ACTION_TYPE_PACKAGE;
1580 pCleanAction->pPackage = pPackage;
1581
1582 pPackage->fPlannedUncache = TRUE;
@@ -1576,6 +1587,15 @@ extern "C" HRESULT PlanCleanPackage(
1587 }
1588 }
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 +
1599 LExit:
1600 return hr;
1601 }
@@ -2229,6 +2249,24 @@ LExit:
2249 return hr;
2250 }
2251
2252 +static 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 +
2266 +LExit:
2267 + return hr;
2268 +}
2269 +
2270 static HRESULT ProcessPayloadGroup(
2271 __in BURN_PLAN* pPlan,
2272 __in BURN_PAYLOAD_GROUP* pPayloadGroup
@@ -2505,6 +2543,8 @@ static HRESULT CalculateExecuteActions(
2543 ExitOnFailure(hr, "Invalid package type.");
2544 }
2545
2546 + pPackage->compatiblePackage.fRemove = pPackage->compatiblePackage.fPlannable && pPackage->compatiblePackage.fRequested;
2547 +
2548 LExit:
2549 return hr;
2550 }
@@ -2622,6 +2662,10 @@ static void ExecuteActionLog(
2662 }
2663 break;
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 +
2669 case BURN_EXECUTE_ACTION_TYPE_MSP_TARGET:
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);
2671 for (DWORD j = 0; j < pAction->mspTarget.cOrderedPatches; ++j)
@@ -2669,6 +2713,27 @@ static void ExecuteActionLog(
2713 }
2714 }
2715
2716 +static 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 +
2737 extern "C" void PlanDump(
2738 __in BURN_PLAN* pPlan
2739 )
@@ -2709,7 +2774,7 @@ extern "C" void PlanDump(
2774
2775 for (DWORD i = 0; i < pPlan->cCleanActions; ++i)
2776 {
2712 - LogStringLine(PlanDumpLevel, " Clean action[%u]: CLEAN_PACKAGE package id: %ls", i, pPlan->rgCleanActions[i].pPackage->sczId);
2777 + CleanActionLog(i, pPlan->rgCleanActions + i);
2778 }
2779
2780 for (DWORD i = 0; i < pPlan->cPlannedProviders; ++i)
src/burn/engine/plan.h
+9 -1
@@ -61,12 +61,13 @@ enum BURN_EXECUTE_ACTION_TYPE
61 BURN_EXECUTE_ACTION_TYPE_ROLLBACK_BOUNDARY_END,
62 BURN_EXECUTE_ACTION_TYPE_BEGIN_MSI_TRANSACTION,
63 BURN_EXECUTE_ACTION_TYPE_COMMIT_MSI_TRANSACTION,
64 + BURN_EXECUTE_ACTION_TYPE_UNINSTALL_MSI_COMPATIBLE_PACKAGE,
65 };
66
67 enum BURN_CLEAN_ACTION_TYPE
68 {
69 BURN_CLEAN_ACTION_TYPE_NONE,
69 - BURN_CLEAN_ACTION_TYPE_BUNDLE,
70 + BURN_CLEAN_ACTION_TYPE_COMPATIBLE_PACKAGE,
71 BURN_CLEAN_ACTION_TYPE_PACKAGE,
72 };
73
@@ -227,11 +228,18 @@ typedef struct _BURN_EXECUTE_ACTION
228 {
229 BURN_ROLLBACK_BOUNDARY* pRollbackBoundary;
230 } msiTransaction;
231 + struct
232 + {
233 + BURN_PACKAGE* pParentPackage;
234 + LPWSTR sczLogPath;
235 + DWORD dwLoggingAttributes;
236 + } uninstallMsiCompatiblePackage;
237 };
238 } BURN_EXECUTE_ACTION;
239
240 typedef struct _BURN_CLEAN_ACTION
241 {
242 + BURN_CLEAN_ACTION_TYPE type;
243 BURN_PACKAGE* pPackage;
244 } BURN_CLEAN_ACTION;
245
src/burn/engine/registration.cpp
+4
@@ -423,6 +423,10 @@ extern "C" void RegistrationUninitialize(
423 ReleaseStr(pRegistration->sczBundlePackageAncestors);
424 RelatedBundlesUninitialize(&pRegistration->relatedBundles);
425
426 + if (pRegistration->rgDependents)
427 + {
428 + ReleaseDependencyArray(pRegistration->rgDependents, pRegistration->cDependents);
429 + }
430 // clear struct
431 memset(pRegistration, 0, sizeof(BURN_REGISTRATION));
432 }
src/burn/engine/userexperience.cpp
+117 -1
@@ -104,7 +104,7 @@ extern "C" HRESULT UserExperienceLoad(
104 args.pCommand = pCommand;
105 args.pfnBootstrapperEngineProc = EngineForApplicationProc;
106 args.pvBootstrapperEngineProcContext = pEngineContext;
107 - args.qwEngineAPIVersion = MAKEQWORDVERSION(2021, 12, 30, 0);
107 + args.qwEngineAPIVersion = MAKEQWORDVERSION(2022, 1, 10, 0);
108
109 results.cbSize = sizeof(BOOTSTRAPPER_CREATE_RESULTS);
110
@@ -1015,6 +1015,36 @@ LExit:
1015 return hr;
1016 }
1017
1018 +EXTERN_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 +
1044 +LExit:
1045 + return hr;
1046 +}
1047 +
1048 EXTERN_C BAAPI UserExperienceOnDetectComplete(
1049 __in BURN_USER_EXPERIENCE* pUserExperience,
1050 __in HRESULT hrStatus,
@@ -1798,6 +1828,67 @@ LExit:
1828 return hr;
1829 }
1830
1831 +EXTERN_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 +
1861 +LExit:
1862 + return hr;
1863 +}
1864 +
1865 +EXTERN_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 +
1888 +LExit:
1889 + return hr;
1890 +}
1891 +
1892 EXTERN_C BAAPI UserExperienceOnPlanMsiFeature(
1893 __in BURN_USER_EXPERIENCE* pUserExperience,
1894 __in_z LPCWSTR wzPackageId,
@@ -1932,6 +2023,31 @@ LExit:
2023 return hr;
2024 }
2025
2026 +EXTERN_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 +
2047 +LExit:
2048 + return hr;
2049 +}
2050 +
2051 EXTERN_C BAAPI UserExperienceOnPlannedPackage(
2052 __in BURN_USER_EXPERIENCE* pUserExperience,
2053 __in_z LPCWSTR wzPackageId,
src/burn/engine/userexperience.h
+26
@@ -248,6 +248,12 @@ BAAPI UserExperienceOnDetectBegin(
248 __in BOOL fInstalled,
249 __in DWORD cPackages
250 );
251 +BAAPI UserExperienceOnDetectCompatibleMsiPackage(
252 + __in BURN_USER_EXPERIENCE* pUserExperience,
253 + __in_z LPCWSTR wzPackageId,
254 + __in_z LPCWSTR wzCompatiblePackageId,
255 + __in VERUTIL_VERSION* pCompatiblePackageVersion
256 + );
257 BAAPI UserExperienceOnDetectComplete(
258 __in BURN_USER_EXPERIENCE* pUserExperience,
259 __in HRESULT hrStatus,
@@ -414,6 +420,20 @@ BAAPI UserExperienceOnPlanBegin(
420 __in BURN_USER_EXPERIENCE* pUserExperience,
421 __in DWORD cPackages
422 );
423 +BAAPI 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 + );
430 +BAAPI 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 + );
437 BAAPI UserExperienceOnPlanComplete(
438 __in BURN_USER_EXPERIENCE* pUserExperience,
439 __in HRESULT hrStatus
@@ -443,6 +463,12 @@ BAAPI UserExperienceOnPlanMsiPackage(
463 __inout BOOL* pfDisableExternalUiHandler,
464 __inout BOOTSTRAPPER_MSI_FILE_VERSIONING* pFileVersioning
465 );
466 +BAAPI UserExperienceOnPlannedCompatiblePackage(
467 + __in BURN_USER_EXPERIENCE* pUserExperience,
468 + __in_z LPCWSTR wzPackageId,
469 + __in_z LPCWSTR wzCompatiblePackageId,
470 + __in BOOL fRemove
471 + );
472 BAAPI UserExperienceOnPlannedPackage(
473 __in BURN_USER_EXPERIENCE* pUserExperience,
474 __in_z LPCWSTR wzPackageId,
src/burn/test/BurnUnitTest/PlanTest.cpp
+132 -1
@@ -287,6 +287,74 @@ namespace Bootstrapper
287 ValidateNonPermanentPackageExpectedStates(&pEngineState->packages.rgPackages[2], L"PackageC", BURN_PACKAGE_REGISTRATION_STATE_ABSENT, BURN_PACKAGE_REGISTRATION_STATE_ABSENT);
288 }
289
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]
359 void RelatedBundleMissingFromCacheTest()
360 {
@@ -1026,6 +1094,36 @@ namespace Bootstrapper
1094 }
1095 }
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 +
1127 void DetectPackageAsAbsent(BURN_PACKAGE* pPackage)
1128 {
1129 pPackage->currentState = BOOTSTRAPPER_PACKAGE_STATE_ABSENT;
@@ -1261,10 +1359,27 @@ namespace Bootstrapper
1359 __in DWORD dwIndex,
1360 __in LPCWSTR wzPackageId
1361 )
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 {
1371 Assert::InRange(dwIndex + 1ul, 1ul, pPlan->cCleanActions);
1372 + return pPlan->rgCleanActions + dwIndex;
1373 + }
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);
1383 Assert::NotEqual((DWORD_PTR)0, (DWORD_PTR)pCleanAction->pPackage);
1384 NativeAssert::StringEqual(wzPackageId, pCleanAction->pPackage->sczId);
1385 }
@@ -1537,6 +1652,22 @@ namespace Bootstrapper
1652 NativeAssert::StringEqual(wzKey, pProvider->sczKey);
1653 NativeAssert::StringEqual(wzName, pProvider->sczName);
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 + }
1671 };
1672 }
1673 }
src/ext/Bal/wixstdba/WixStandardBootstrapperApplication.cpp
+46
@@ -1424,6 +1424,18 @@ public: // IBootstrapperApplication
1424 case BOOTSTRAPPER_APPLICATION_MESSAGE_ONSETUPDATECOMPLETE:
1425 OnSetUpdateCompleteFallback(reinterpret_cast<BA_ONSETUPDATECOMPLETE_ARGS*>(pvArgs), reinterpret_cast<BA_ONSETUPDATECOMPLETE_RESULTS*>(pvResults));
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;
1439 default:
1440 #ifdef DEBUG
1441 BalLog(BOOTSTRAPPER_LOG_LEVEL_STANDARD, "WIXSTDBA: Forwarding unknown BA message: %d", message);
@@ -1611,6 +1623,14 @@ private: // privates
1623 m_pfnBAFunctionsProc(BA_FUNCTIONS_MESSAGE_ONPLANPACKAGECOMPLETE, pArgs, pResults, m_pvBAFunctionsProcContext);
1624 }
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 +
1634 void OnPlannedPackageFallback(
1635 __in BA_ONPLANNEDPACKAGE_ARGS* pArgs,
1636 __inout BA_ONPLANNEDPACKAGE_RESULTS* pResults
@@ -2043,6 +2063,32 @@ private: // privates
2063 m_pfnBAFunctionsProc(BA_FUNCTIONS_MESSAGE_ONSETUPDATECOMPLETE, pArgs, pResults, m_pvBAFunctionsProcContext);
2064 }
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 +
2092
2093 public: //CBalBaseBootstrapperApplication
2094 virtual STDMETHODIMP Initialize(
src/ext/Bal/wixstdba/wixstdba.mc
+7
@@ -50,6 +50,13 @@ Language=English
50 WIXSTDBA: Planned related bundle: %1!ls!, wixstdba requested: %2!hs!, bafunctions requested: %3!hs!
51 .
52
53 +MessageId=4
54 +Severity=Success
55 +SymbolicName=MSG_WIXSTDBA_PLANNED_COMPATIBLE_MSI_PACKAGE
56 +Language=English
57 +WIXSTDBA: Planned compatible package: %2!ls! for %1!ls!, wixstdba requested remove: %3!hs!, bafunctions requested remove: %4!hs!
58 +.
59 +
60 MessageId=5
61 Severity=Success
62 SymbolicName=MSG_WIXSTDBA_PLANNED_TARGET_MSI_PACKAGE
src/test/burn/TestData/DependencyTests/BundleAv2/BundleAv2.wixproj new
+16
@@ -0,0 +1,16 @@
1 +<!-- Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. -->
2 +<Project Sdk="WixToolset.Sdk">
3 + <Import Project="..\BundleAv1\BundleA.props" />
4 + <PropertyGroup>
5 + <Version>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
src/test/burn/TestData/DependencyTests/BundleAv2/BundleAv2.wxs new
+21
@@ -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>
src/test/burn/TestData/DependencyTests/PackageAv1/PackageA.props
-1
@@ -3,7 +3,6 @@
3 <PropertyGroup>
4 <PackageName>PackageA</PackageName>
5 <ProductComponentsRef>true</ProductComponentsRef>
6 - <ProductCode>{6F171EC9-0774-4974-A8D1-493EF53CAB74}</ProductCode>
6 <UpgradeCode>{45E933B7-B56A-44D5-8EEC-625EC199085E}</UpgradeCode>
7 </PropertyGroup>
8 <ItemGroup>
src/test/burn/TestData/DependencyTests/PackageAv1/PackageAv1.props new
+7
@@ -0,0 +1,7 @@
1 +<!-- Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. -->
2 +<Project>
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
src/test/burn/TestData/DependencyTests/PackageAv1/PackageAv1.wixproj
+1 -1
@@ -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. -->
2 <Project Sdk="WixToolset.Sdk">
3 - <Import Project="PackageA.props" />
3 + <Import Project="PackageAv1.props" />
4 <ItemGroup>
5 <PackageReference Include="WixToolset.Dependency.wixext" />
6 </ItemGroup>
src/test/burn/TestData/DependencyTests/PackageAv1_0_1/PackageAv1_0_1.wixproj
+1 -1
@@ -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. -->
2 <Project Sdk="WixToolset.Sdk">
3 - <Import Project="..\PackageAv1\PackageA.props" />
3 + <Import Project="..\PackageAv1\PackageAv1.props" />
4 <PropertyGroup>
5 <Version>1.0.1.0</Version>
6 </PropertyGroup>
src/test/burn/TestData/DependencyTests/PackageAv1_0_2/PackageAv1_0_2.wixproj
+1 -1
@@ -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. -->
2 <Project Sdk="WixToolset.Sdk">
3 - <Import Project="..\PackageAv1\PackageA.props" />
3 + <Import Project="..\PackageAv1\PackageAv1.props" />
4 <PropertyGroup>
5 <Version>1.0.2.0</Version>
6 </PropertyGroup>
src/test/burn/TestData/DependencyTests/PackageAv2/PackageAv2.wixproj new
+13
@@ -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
src/test/burn/WixTestTools/BundleVerifier.cs
+2 -2
@@ -124,14 +124,14 @@ namespace WixTestTools
124 }
125 }
126
127 - public void VerifyPackageIsCached(string packageId)
127 + public void VerifyPackageIsCached(string packageId, bool cached = true)
128 {
129 using var wixOutput = WixOutput.Read(this.BundlePdb);
130 var intermediate = Intermediate.Load(wixOutput);
131 var section = intermediate.Sections.Single();
132 var packageSymbol = section.Symbols.OfType<WixBundlePackageSymbol>().Single(p => p.Id.Id == packageId);
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 }
136
137 public void VerifyExeTestRegistryRootDeleted(string name, bool x64 = false)
src/test/burn/WixToolsetTest.BurnE2E/DependencyTests.cs
+58
@@ -123,6 +123,64 @@ namespace WixToolsetTest.BurnE2E
123 bundleAv1.VerifyExeTestRegistryValue(testRegistryValueExe, "1.0.1.0");
124 }
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 +
184 [Fact(Skip = "https://github.com/wixtoolset/issues/issues/6401")]
185 public void CanMinorUpgradeDependencyPackageFromPatchBundle()
186 {