aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/api/burn/WixToolset.BootstrapperCore.Native/inc/BootstrapperApplication.h17
-rw-r--r--src/api/burn/WixToolset.Mba.Core/BootstrapperApplication.cs25
-rw-r--r--src/api/burn/WixToolset.Mba.Core/EventArgs.cs152
-rw-r--r--src/api/burn/WixToolset.Mba.Core/IBootstrapperApplication.cs14
-rw-r--r--src/api/burn/WixToolset.Mba.Core/IDefaultBootstrapperApplication.cs13
-rw-r--r--src/api/burn/WixToolset.Mba.Core/PackageInfo.cs17
-rw-r--r--src/api/burn/balutil/balinfo.cpp6
-rw-r--r--src/api/burn/balutil/inc/BAFunctions.h1
-rw-r--r--src/api/burn/balutil/inc/BalBaseBAFunctions.h12
-rw-r--r--src/api/burn/balutil/inc/BalBaseBAFunctionsProc.h1
-rw-r--r--src/api/burn/balutil/inc/BalBaseBootstrapperApplication.h13
-rw-r--r--src/api/burn/balutil/inc/BalBaseBootstrapperApplicationProc.h12
-rw-r--r--src/api/burn/balutil/inc/IBootstrapperApplication.h10
-rw-r--r--src/api/burn/balutil/inc/balinfo.h1
-rw-r--r--src/api/wix/WixToolset.Data/Symbols/SymbolDefinitions.cs4
-rw-r--r--src/api/wix/WixToolset.Data/Symbols/WixBundleBundlePackageSymbol.cs8
-rw-r--r--src/api/wix/WixToolset.Data/Symbols/WixBundlePackageRelatedBundleSymbol.cs60
-rw-r--r--src/burn/engine/bundlepackageengine.cpp277
-rw-r--r--src/burn/engine/bundlepackageengine.h15
-rw-r--r--src/burn/engine/core.cpp2
-rw-r--r--src/burn/engine/package.h19
-rw-r--r--src/burn/engine/registration.cpp87
-rw-r--r--src/burn/engine/relatedbundle.cpp50
-rw-r--r--src/burn/engine/relatedbundle.h3
-rw-r--r--src/burn/engine/userexperience.cpp36
-rw-r--r--src/burn/engine/userexperience.h8
-rw-r--r--src/burn/test/BurnUnitTest/TestData/PlanTest/BundlePackage_Multiple_manifest.xml2
-rw-r--r--src/ext/Bal/wixstdba/WixStandardBootstrapperApplication.cpp11
-rw-r--r--src/test/burn/TestData/BundlePackageTests/UpgradeBundlePackageBundlev1/UpgradeBundlePackageBundle.props14
-rw-r--r--src/test/burn/TestData/BundlePackageTests/UpgradeBundlePackageBundlev1/UpgradeBundlePackageBundlev1.wixproj7
-rw-r--r--src/test/burn/TestData/BundlePackageTests/UpgradeBundlePackageBundlev1/UpgradeBundlePackageBundlev1.wxs10
-rw-r--r--src/test/burn/TestData/BundlePackageTests/UpgradeBundlePackageBundlev2/UpgradeBundlePackageBundlev2.wixproj7
-rw-r--r--src/test/burn/TestData/BundlePackageTests/UpgradeBundlePackageBundlev2/UpgradeBundlePackageBundlev2.wxs10
-rw-r--r--src/test/burn/WixToolsetTest.BurnE2E/BundlePackageTests.cs30
-rw-r--r--src/wix/WixToolset.Core.Burn/Bind/GenerateManifestDataFromIRCommand.cs1
-rw-r--r--src/wix/WixToolset.Core.Burn/Bundles/CreateBurnManifestCommand.cs12
-rw-r--r--src/wix/WixToolset.Core.Burn/Bundles/ProcessBundlePackageCommand.cs25
-rw-r--r--src/wix/test/WixToolsetTest.CoreIntegration/BundlePackageFixture.cs19
38 files changed, 773 insertions, 238 deletions
diff --git a/src/api/burn/WixToolset.BootstrapperCore.Native/inc/BootstrapperApplication.h b/src/api/burn/WixToolset.BootstrapperCore.Native/inc/BootstrapperApplication.h
index 943f5ead..fbbd10ee 100644
--- a/src/api/burn/WixToolset.BootstrapperCore.Native/inc/BootstrapperApplication.h
+++ b/src/api/burn/WixToolset.BootstrapperCore.Native/inc/BootstrapperApplication.h
@@ -226,6 +226,7 @@ enum BOOTSTRAPPER_APPLICATION_MESSAGE
226 BOOTSTRAPPER_APPLICATION_MESSAGE_ONPLANRELATEDBUNDLETYPE, 226 BOOTSTRAPPER_APPLICATION_MESSAGE_ONPLANRELATEDBUNDLETYPE,
227 BOOTSTRAPPER_APPLICATION_MESSAGE_ONAPPLYDOWNGRADE, 227 BOOTSTRAPPER_APPLICATION_MESSAGE_ONAPPLYDOWNGRADE,
228 BOOTSTRAPPER_APPLICATION_MESSAGE_ONEXECUTEPROCESSCANCEL, 228 BOOTSTRAPPER_APPLICATION_MESSAGE_ONEXECUTEPROCESSCANCEL,
229 BOOTSTRAPPER_APPLICATION_MESSAGE_ONDETECTRELATEDBUNDLEPACKAGE,
229}; 230};
230 231
231enum BOOTSTRAPPER_APPLYCOMPLETE_ACTION 232enum BOOTSTRAPPER_APPLYCOMPLETE_ACTION
@@ -793,6 +794,22 @@ struct BA_ONDETECTRELATEDBUNDLE_RESULTS
793 BOOL fCancel; 794 BOOL fCancel;
794}; 795};
795 796
797struct BA_ONDETECTRELATEDBUNDLEPACKAGE_ARGS
798{
799 DWORD cbSize;
800 LPCWSTR wzPackageId;
801 LPCWSTR wzBundleId;
802 BOOTSTRAPPER_RELATION_TYPE relationType;
803 BOOL fPerMachine;
804 LPCWSTR wzVersion;
805};
806
807struct BA_ONDETECTRELATEDBUNDLEPACKAGE_RESULTS
808{
809 DWORD cbSize;
810 BOOL fCancel;
811};
812
796struct BA_ONDETECTRELATEDMSIPACKAGE_ARGS 813struct BA_ONDETECTRELATEDMSIPACKAGE_ARGS
797{ 814{
798 DWORD cbSize; 815 DWORD cbSize;
diff --git a/src/api/burn/WixToolset.Mba.Core/BootstrapperApplication.cs b/src/api/burn/WixToolset.Mba.Core/BootstrapperApplication.cs
index 5ed064fa..41738bf6 100644
--- a/src/api/burn/WixToolset.Mba.Core/BootstrapperApplication.cs
+++ b/src/api/burn/WixToolset.Mba.Core/BootstrapperApplication.cs
@@ -283,6 +283,9 @@ namespace WixToolset.Mba.Core
283 /// <inheritdoc/> 283 /// <inheritdoc/>
284 public event EventHandler<ExecuteProcessCancelEventArgs> ExecuteProcessCancel; 284 public event EventHandler<ExecuteProcessCancelEventArgs> ExecuteProcessCancel;
285 285
286 /// <inheritdoc/>
287 public event EventHandler<DetectRelatedBundlePackageEventArgs> DetectRelatedBundlePackage;
288
286 /// <summary> 289 /// <summary>
287 /// Entry point that is called when the bootstrapper application is ready to run. 290 /// Entry point that is called when the bootstrapper application is ready to run.
288 /// </summary> 291 /// </summary>
@@ -1385,6 +1388,19 @@ namespace WixToolset.Mba.Core
1385 } 1388 }
1386 } 1389 }
1387 1390
1391 /// <summary>
1392 /// Called by the engine, raises the <see cref="DetectRelatedBundlePackage"/> event.
1393 /// </summary>
1394 /// <param name="args">Additional arguments for this event.</param>
1395 protected virtual void OnDetectRelatedBundlePackage(DetectRelatedBundlePackageEventArgs args)
1396 {
1397 EventHandler<DetectRelatedBundlePackageEventArgs> handler = this.DetectRelatedBundlePackage;
1398 if (null != handler)
1399 {
1400 handler(this, args);
1401 }
1402 }
1403
1388 #region IBootstrapperApplication Members 1404 #region IBootstrapperApplication Members
1389 1405
1390 int IBootstrapperApplication.BAProc(int message, IntPtr pvArgs, IntPtr pvResults, IntPtr pvContext) 1406 int IBootstrapperApplication.BAProc(int message, IntPtr pvArgs, IntPtr pvResults, IntPtr pvContext)
@@ -2144,6 +2160,15 @@ namespace WixToolset.Mba.Core
2144 return args.HResult; 2160 return args.HResult;
2145 } 2161 }
2146 2162
2163 int IBootstrapperApplication.OnDetectRelatedBundlePackage(string wzPackageId, string wzProductCode, RelationType relationType, bool fPerMachine, string wzVersion, ref bool fCancel)
2164 {
2165 DetectRelatedBundlePackageEventArgs args = new DetectRelatedBundlePackageEventArgs(wzPackageId, wzProductCode, relationType, fPerMachine, wzVersion, fCancel);
2166 this.OnDetectRelatedBundlePackage(args);
2167
2168 fCancel = args.Cancel;
2169 return args.HResult;
2170 }
2171
2147 #endregion 2172 #endregion
2148 } 2173 }
2149} 2174}
diff --git a/src/api/burn/WixToolset.Mba.Core/EventArgs.cs b/src/api/burn/WixToolset.Mba.Core/EventArgs.cs
index c2c73067..07b14b3d 100644
--- a/src/api/burn/WixToolset.Mba.Core/EventArgs.cs
+++ b/src/api/burn/WixToolset.Mba.Core/EventArgs.cs
@@ -2030,10 +2030,7 @@ namespace WixToolset.Mba.Core
2030 [Serializable] 2030 [Serializable]
2031 public class LaunchApprovedExeBeginEventArgs : CancellableHResultEventArgs 2031 public class LaunchApprovedExeBeginEventArgs : CancellableHResultEventArgs
2032 { 2032 {
2033 /// <summary> 2033 /// <summary />
2034 ///
2035 /// </summary>
2036 /// <param name="cancelRecommendation"></param>
2037 public LaunchApprovedExeBeginEventArgs(bool cancelRecommendation) 2034 public LaunchApprovedExeBeginEventArgs(bool cancelRecommendation)
2038 : base(cancelRecommendation) 2035 : base(cancelRecommendation)
2039 { 2036 {
@@ -2046,27 +2043,18 @@ namespace WixToolset.Mba.Core
2046 [Serializable] 2043 [Serializable]
2047 public class LaunchApprovedExeCompleteEventArgs : StatusEventArgs 2044 public class LaunchApprovedExeCompleteEventArgs : StatusEventArgs
2048 { 2045 {
2049 private int processId; 2046 /// <summary />
2050
2051 /// <summary>
2052 ///
2053 /// </summary>
2054 /// <param name="hrStatus"></param>
2055 /// <param name="processId"></param>
2056 public LaunchApprovedExeCompleteEventArgs(int hrStatus, int processId) 2047 public LaunchApprovedExeCompleteEventArgs(int hrStatus, int processId)
2057 : base(hrStatus) 2048 : base(hrStatus)
2058 { 2049 {
2059 this.processId = processId; 2050 this.ProcessId = processId;
2060 } 2051 }
2061 2052
2062 /// <summary> 2053 /// <summary>
2063 /// Gets the ProcessId of the process that was launched. 2054 /// Gets the ProcessId of the process that was launched.
2064 /// This is only valid if the status reports success. 2055 /// This is only valid if the status reports success.
2065 /// </summary> 2056 /// </summary>
2066 public int ProcessId 2057 public int ProcessId { get; private set; }
2067 {
2068 get { return this.processId; }
2069 }
2070 } 2058 }
2071 2059
2072 /// <summary> 2060 /// <summary>
@@ -2075,26 +2063,17 @@ namespace WixToolset.Mba.Core
2075 [Serializable] 2063 [Serializable]
2076 public class BeginMsiTransactionBeginEventArgs : CancellableHResultEventArgs 2064 public class BeginMsiTransactionBeginEventArgs : CancellableHResultEventArgs
2077 { 2065 {
2078 private string transactionId; 2066 /// <summary />
2079
2080 /// <summary>
2081 ///
2082 /// </summary>
2083 /// <param name="transactionId"></param>
2084 /// <param name="cancelRecommendation"></param>
2085 public BeginMsiTransactionBeginEventArgs(string transactionId, bool cancelRecommendation) 2067 public BeginMsiTransactionBeginEventArgs(string transactionId, bool cancelRecommendation)
2086 : base(cancelRecommendation) 2068 : base(cancelRecommendation)
2087 { 2069 {
2088 this.transactionId = transactionId; 2070 this.TransactionId = transactionId;
2089 } 2071 }
2090 2072
2091 /// <summary> 2073 /// <summary>
2092 /// Gets the MSI transaction Id. 2074 /// Gets the MSI transaction Id.
2093 /// </summary> 2075 /// </summary>
2094 public string TransactionId 2076 public string TransactionId { get; private set; }
2095 {
2096 get { return this.transactionId; }
2097 }
2098 } 2077 }
2099 2078
2100 /// <summary> 2079 /// <summary>
@@ -2103,26 +2082,17 @@ namespace WixToolset.Mba.Core
2103 [Serializable] 2082 [Serializable]
2104 public class BeginMsiTransactionCompleteEventArgs : StatusEventArgs 2083 public class BeginMsiTransactionCompleteEventArgs : StatusEventArgs
2105 { 2084 {
2106 private string transactionId; 2085 /// <summary />
2107
2108 /// <summary>
2109 ///
2110 /// </summary>
2111 /// <param name="transactionId"></param>
2112 /// <param name="hrStatus"></param>
2113 public BeginMsiTransactionCompleteEventArgs(string transactionId, int hrStatus) 2086 public BeginMsiTransactionCompleteEventArgs(string transactionId, int hrStatus)
2114 : base(hrStatus) 2087 : base(hrStatus)
2115 { 2088 {
2116 this.transactionId = transactionId; 2089 this.TransactionId = transactionId;
2117 } 2090 }
2118 2091
2119 /// <summary> 2092 /// <summary>
2120 /// Gets the MSI transaction Id. 2093 /// Gets the MSI transaction Id.
2121 /// </summary> 2094 /// </summary>
2122 public string TransactionId 2095 public string TransactionId { get; private set; }
2123 {
2124 get { return this.transactionId; }
2125 }
2126 } 2096 }
2127 2097
2128 /// <summary> 2098 /// <summary>
@@ -2131,26 +2101,17 @@ namespace WixToolset.Mba.Core
2131 [Serializable] 2101 [Serializable]
2132 public class CommitMsiTransactionBeginEventArgs : CancellableHResultEventArgs 2102 public class CommitMsiTransactionBeginEventArgs : CancellableHResultEventArgs
2133 { 2103 {
2134 private string transactionId; 2104 /// <summary />
2135
2136 /// <summary>
2137 ///
2138 /// </summary>
2139 /// <param name="transactionId"></param>
2140 /// <param name="cancelRecommendation"></param>
2141 public CommitMsiTransactionBeginEventArgs(string transactionId, bool cancelRecommendation) 2105 public CommitMsiTransactionBeginEventArgs(string transactionId, bool cancelRecommendation)
2142 : base(cancelRecommendation) 2106 : base(cancelRecommendation)
2143 { 2107 {
2144 this.transactionId = transactionId; 2108 this.TransactionId = transactionId;
2145 } 2109 }
2146 2110
2147 /// <summary> 2111 /// <summary>
2148 /// Gets the MSI transaction Id. 2112 /// Gets the MSI transaction Id.
2149 /// </summary> 2113 /// </summary>
2150 public string TransactionId 2114 public string TransactionId { get; private set; }
2151 {
2152 get { return this.transactionId; }
2153 }
2154 } 2115 }
2155 2116
2156 /// <summary> 2117 /// <summary>
@@ -2159,26 +2120,17 @@ namespace WixToolset.Mba.Core
2159 [Serializable] 2120 [Serializable]
2160 public class CommitMsiTransactionCompleteEventArgs : StatusEventArgs 2121 public class CommitMsiTransactionCompleteEventArgs : StatusEventArgs
2161 { 2122 {
2162 private string transactionId; 2123 /// <summary />
2163
2164 /// <summary>
2165 ///
2166 /// </summary>
2167 /// <param name="transactionId"></param>
2168 /// <param name="hrStatus"></param>
2169 public CommitMsiTransactionCompleteEventArgs(string transactionId, int hrStatus) 2124 public CommitMsiTransactionCompleteEventArgs(string transactionId, int hrStatus)
2170 : base(hrStatus) 2125 : base(hrStatus)
2171 { 2126 {
2172 this.transactionId = transactionId; 2127 this.TransactionId = transactionId;
2173 } 2128 }
2174 2129
2175 /// <summary> 2130 /// <summary>
2176 /// Gets the MSI transaction Id. 2131 /// Gets the MSI transaction Id.
2177 /// </summary> 2132 /// </summary>
2178 public string TransactionId 2133 public string TransactionId { get; private set; }
2179 {
2180 get { return this.transactionId; }
2181 }
2182 } 2134 }
2183 2135
2184 /// <summary> 2136 /// <summary>
@@ -2187,24 +2139,16 @@ namespace WixToolset.Mba.Core
2187 [Serializable] 2139 [Serializable]
2188 public class RollbackMsiTransactionBeginEventArgs : HResultEventArgs 2140 public class RollbackMsiTransactionBeginEventArgs : HResultEventArgs
2189 { 2141 {
2190 private string transactionId; 2142 /// <summary />
2191
2192 /// <summary>
2193 ///
2194 /// </summary>
2195 /// <param name="transactionId"></param>
2196 public RollbackMsiTransactionBeginEventArgs(string transactionId) 2143 public RollbackMsiTransactionBeginEventArgs(string transactionId)
2197 { 2144 {
2198 this.transactionId = transactionId; 2145 this.TransactionId = transactionId;
2199 } 2146 }
2200 2147
2201 /// <summary> 2148 /// <summary>
2202 /// Gets the MSI transaction Id. 2149 /// Gets the MSI transaction Id.
2203 /// </summary> 2150 /// </summary>
2204 public string TransactionId 2151 public string TransactionId { get; private set; }
2205 {
2206 get { return this.transactionId; }
2207 }
2208 } 2152 }
2209 2153
2210 /// <summary> 2154 /// <summary>
@@ -2213,26 +2157,17 @@ namespace WixToolset.Mba.Core
2213 [Serializable] 2157 [Serializable]
2214 public class RollbackMsiTransactionCompleteEventArgs : StatusEventArgs 2158 public class RollbackMsiTransactionCompleteEventArgs : StatusEventArgs
2215 { 2159 {
2216 private string transactionId; 2160 /// <summary />
2217
2218 /// <summary>
2219 ///
2220 /// </summary>
2221 /// <param name="transactionId"></param>
2222 /// <param name="hrStatus"></param>
2223 public RollbackMsiTransactionCompleteEventArgs(string transactionId, int hrStatus) 2161 public RollbackMsiTransactionCompleteEventArgs(string transactionId, int hrStatus)
2224 : base(hrStatus) 2162 : base(hrStatus)
2225 { 2163 {
2226 this.transactionId = transactionId; 2164 this.TransactionId = transactionId;
2227 } 2165 }
2228 2166
2229 /// <summary> 2167 /// <summary>
2230 /// Gets the MSI transaction Id. 2168 /// Gets the MSI transaction Id.
2231 /// </summary> 2169 /// </summary>
2232 public string TransactionId 2170 public string TransactionId { get; private set; }
2233 {
2234 get { return this.transactionId; }
2235 }
2236 } 2171 }
2237 2172
2238 /// <summary> 2173 /// <summary>
@@ -2524,4 +2459,47 @@ namespace WixToolset.Mba.Core
2524 /// </summary> 2459 /// </summary>
2525 public BOOTSTRAPPER_EXECUTEPROCESSCANCEL_ACTION Action { get; set; } 2460 public BOOTSTRAPPER_EXECUTEPROCESSCANCEL_ACTION Action { get; set; }
2526 } 2461 }
2462
2463 /// <summary>
2464 /// Event arguments for <see cref="IDefaultBootstrapperApplication.DetectRelatedBundlePackage"/>
2465 /// </summary>
2466 [Serializable]
2467 public class DetectRelatedBundlePackageEventArgs : CancellableHResultEventArgs
2468 {
2469 /// <summary />
2470 public DetectRelatedBundlePackageEventArgs(string packageId, string productCode, RelationType relationType, bool perMachine, string version, bool cancelRecommendation)
2471 : base(cancelRecommendation)
2472 {
2473 this.PackageId = packageId;
2474 this.ProductCode = productCode;
2475 this.RelationType = relationType;
2476 this.PerMachine = perMachine;
2477 this.Version = version;
2478 }
2479
2480 /// <summary>
2481 /// Gets the identity of the product's package detected.
2482 /// </summary>
2483 public string PackageId { get; private set; }
2484
2485 /// <summary>
2486 /// Gets the identity of the related bundle detected.
2487 /// </summary>
2488 public string ProductCode { get; private set; }
2489
2490 /// <summary>
2491 /// Gets the relationship type of the related bundle.
2492 /// </summary>
2493 public RelationType RelationType { get; private set; }
2494
2495 /// <summary>
2496 /// Gets whether the detected bundle is per machine.
2497 /// </summary>
2498 public bool PerMachine { get; private set; }
2499
2500 /// <summary>
2501 /// Gets the version of the related bundle detected.
2502 /// </summary>
2503 public string Version { get; private set; }
2504 }
2527} 2505}
diff --git a/src/api/burn/WixToolset.Mba.Core/IBootstrapperApplication.cs b/src/api/burn/WixToolset.Mba.Core/IBootstrapperApplication.cs
index 7cf0957a..ae642474 100644
--- a/src/api/burn/WixToolset.Mba.Core/IBootstrapperApplication.cs
+++ b/src/api/burn/WixToolset.Mba.Core/IBootstrapperApplication.cs
@@ -1182,6 +1182,20 @@ namespace WixToolset.Mba.Core
1182 [MarshalAs(UnmanagedType.I4)] BOOTSTRAPPER_EXECUTEPROCESSCANCEL_ACTION recommendation, 1182 [MarshalAs(UnmanagedType.I4)] BOOTSTRAPPER_EXECUTEPROCESSCANCEL_ACTION recommendation,
1183 [MarshalAs(UnmanagedType.I4)] ref BOOTSTRAPPER_EXECUTEPROCESSCANCEL_ACTION pAction 1183 [MarshalAs(UnmanagedType.I4)] ref BOOTSTRAPPER_EXECUTEPROCESSCANCEL_ACTION pAction
1184 ); 1184 );
1185
1186 /// <summary>
1187 /// See <see cref="IDefaultBootstrapperApplication.DetectRelatedBundlePackage"/>.
1188 /// </summary>
1189 [PreserveSig]
1190 [return: MarshalAs(UnmanagedType.I4)]
1191 int OnDetectRelatedBundlePackage(
1192 [MarshalAs(UnmanagedType.LPWStr)] string wzPackageId,
1193 [MarshalAs(UnmanagedType.LPWStr)] string wzBundleId,
1194 [MarshalAs(UnmanagedType.U4)] RelationType relationType,
1195 [MarshalAs(UnmanagedType.Bool)] bool fPerMachine,
1196 [MarshalAs(UnmanagedType.LPWStr)] string wzVersion,
1197 [MarshalAs(UnmanagedType.Bool)] ref bool fCancel
1198 );
1185 } 1199 }
1186 1200
1187 /// <summary> 1201 /// <summary>
diff --git a/src/api/burn/WixToolset.Mba.Core/IDefaultBootstrapperApplication.cs b/src/api/burn/WixToolset.Mba.Core/IDefaultBootstrapperApplication.cs
index 21d99b32..77089e83 100644
--- a/src/api/burn/WixToolset.Mba.Core/IDefaultBootstrapperApplication.cs
+++ b/src/api/burn/WixToolset.Mba.Core/IDefaultBootstrapperApplication.cs
@@ -36,15 +36,15 @@ namespace WixToolset.Mba.Core
36 36
37 /// <summary> 37 /// <summary>
38 /// Fired when the engine has begun acquiring the payload or container. 38 /// Fired when the engine has begun acquiring the payload or container.
39 /// The BA can change the source using <see cref="IEngine.SetLocalSource(string, string, string)"/> 39 /// The BA can change the source using <see cref="IEngine.SetLocalSource(String, String, String)"/>
40 /// or <see cref="IEngine.SetDownloadSource(string, string, string, string, string)"/>. 40 /// or <see cref="IEngine.SetDownloadSource(String, String, String, String, String)"/>.
41 /// </summary> 41 /// </summary>
42 event EventHandler<CacheAcquireBeginEventArgs> CacheAcquireBegin; 42 event EventHandler<CacheAcquireBeginEventArgs> CacheAcquireBegin;
43 43
44 /// <summary> 44 /// <summary>
45 /// Fired when the engine has completed the acquisition of the payload or container. 45 /// Fired when the engine has completed the acquisition of the payload or container.
46 /// The BA can change the source using <see cref="IEngine.SetLocalSource(string, string, string)"/> 46 /// The BA can change the source using <see cref="IEngine.SetLocalSource(String, String, String)"/>
47 /// or <see cref="IEngine.SetDownloadSource(string, string, string, string, string)"/>. 47 /// or <see cref="IEngine.SetDownloadSource(String, String, String, String, String)"/>.
48 /// </summary> 48 /// </summary>
49 event EventHandler<CacheAcquireCompleteEventArgs> CacheAcquireComplete; 49 event EventHandler<CacheAcquireCompleteEventArgs> CacheAcquireComplete;
50 50
@@ -179,6 +179,11 @@ namespace WixToolset.Mba.Core
179 event EventHandler<DetectRelatedBundleEventArgs> DetectRelatedBundle; 179 event EventHandler<DetectRelatedBundleEventArgs> DetectRelatedBundle;
180 180
181 /// <summary> 181 /// <summary>
182 /// Fired when a related bundle has been detected for a bundle package.
183 /// </summary>
184 event EventHandler<DetectRelatedBundlePackageEventArgs> DetectRelatedBundlePackage;
185
186 /// <summary>
182 /// Fired when a related MSI package has been detected for a package. 187 /// Fired when a related MSI package has been detected for a package.
183 /// </summary> 188 /// </summary>
184 event EventHandler<DetectRelatedMsiPackageEventArgs> DetectRelatedMsiPackage; 189 event EventHandler<DetectRelatedMsiPackageEventArgs> DetectRelatedMsiPackage;
diff --git a/src/api/burn/WixToolset.Mba.Core/PackageInfo.cs b/src/api/burn/WixToolset.Mba.Core/PackageInfo.cs
index 3681a497..39fe4d73 100644
--- a/src/api/burn/WixToolset.Mba.Core/PackageInfo.cs
+++ b/src/api/burn/WixToolset.Mba.Core/PackageInfo.cs
@@ -56,6 +56,11 @@ namespace WixToolset.Mba.Core
56 /// 56 ///
57 /// </summary> 57 /// </summary>
58 UpdateBundle, 58 UpdateBundle,
59
60 /// <summary>
61 ///
62 /// </summary>
63 ChainBundle,
59 } 64 }
60 65
61 /// <summary> 66 /// <summary>
@@ -220,7 +225,11 @@ namespace WixToolset.Mba.Core
220 return null; 225 return null;
221 } 226 }
222 227
223 if (attributeValue.Equals("Exe", StringComparison.InvariantCulture)) 228 if (attributeValue.Equals("Bundle", StringComparison.InvariantCulture))
229 {
230 return PackageType.ChainBundle;
231 }
232 else if (attributeValue.Equals("Exe", StringComparison.InvariantCulture))
224 { 233 {
225 return PackageType.Exe; 234 return PackageType.Exe;
226 } 235 }
@@ -268,7 +277,7 @@ namespace WixToolset.Mba.Core
268 package.Type = PackageType.UpgradeBundle; 277 package.Type = PackageType.UpgradeBundle;
269 break; 278 break;
270 default: 279 default:
271 throw new Exception(string.Format("Unknown related bundle type: {0}", relationType)); 280 throw new Exception(String.Format("Unknown related bundle type: {0}", relationType));
272 } 281 }
273 282
274 return package; 283 return package;
@@ -302,7 +311,7 @@ namespace WixToolset.Mba.Core
302 311
303 if (!packagesById.TryGetValue(id, out var ipackage)) 312 if (!packagesById.TryGetValue(id, out var ipackage))
304 { 313 {
305 throw new Exception(string.Format("Failed to find package specified in WixBalPackageInfo: {0}", id)); 314 throw new Exception(String.Format("Failed to find package specified in WixBalPackageInfo: {0}", id));
306 } 315 }
307 316
308 var package = (PackageInfo)ipackage; 317 var package = (PackageInfo)ipackage;
@@ -322,7 +331,7 @@ namespace WixToolset.Mba.Core
322 331
323 if (!packagesById.TryGetValue(id, out var ipackage)) 332 if (!packagesById.TryGetValue(id, out var ipackage))
324 { 333 {
325 throw new Exception(string.Format("Failed to find package specified in WixMbaPrereqInformation: {0}", id)); 334 throw new Exception(String.Format("Failed to find package specified in WixMbaPrereqInformation: {0}", id));
326 } 335 }
327 336
328 var package = (PackageInfo)ipackage; 337 var package = (PackageInfo)ipackage;
diff --git a/src/api/burn/balutil/balinfo.cpp b/src/api/burn/balutil/balinfo.cpp
index d9cc9b76..f0eb9904 100644
--- a/src/api/burn/balutil/balinfo.cpp
+++ b/src/api/burn/balutil/balinfo.cpp
@@ -433,7 +433,11 @@ static HRESULT ParsePackagesFromXml(
433 hr = XmlGetAttributeEx(pNode, L"PackageType", &scz); 433 hr = XmlGetAttributeEx(pNode, L"PackageType", &scz);
434 ExitOnFailure(hr, "Failed to get package type for package."); 434 ExitOnFailure(hr, "Failed to get package type for package.");
435 435
436 if (CSTR_EQUAL == ::CompareStringW(LOCALE_NEUTRAL, 0, L"Exe", -1, scz, -1)) 436 if (CSTR_EQUAL == ::CompareStringW(LOCALE_NEUTRAL, 0, L"Bundle", -1, scz, -1))
437 {
438 prgPackages[iPackage].type = BAL_INFO_PACKAGE_TYPE_BUNDLE_CHAIN;
439 }
440 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_NEUTRAL, 0, L"Exe", -1, scz, -1))
437 { 441 {
438 prgPackages[iPackage].type = BAL_INFO_PACKAGE_TYPE_EXE; 442 prgPackages[iPackage].type = BAL_INFO_PACKAGE_TYPE_EXE;
439 } 443 }
diff --git a/src/api/burn/balutil/inc/BAFunctions.h b/src/api/burn/balutil/inc/BAFunctions.h
index 158e65b5..9be3f62f 100644
--- a/src/api/burn/balutil/inc/BAFunctions.h
+++ b/src/api/burn/balutil/inc/BAFunctions.h
@@ -92,6 +92,7 @@ enum BA_FUNCTIONS_MESSAGE
92 BA_FUNCTIONS_MESSAGE_ONPLANRELATEDBUNDLETYPE = BOOTSTRAPPER_APPLICATION_MESSAGE_ONPLANRELATEDBUNDLETYPE, 92 BA_FUNCTIONS_MESSAGE_ONPLANRELATEDBUNDLETYPE = BOOTSTRAPPER_APPLICATION_MESSAGE_ONPLANRELATEDBUNDLETYPE,
93 BA_FUNCTIONS_MESSAGE_ONAPPLYDOWNGRADE = BOOTSTRAPPER_APPLICATION_MESSAGE_ONAPPLYDOWNGRADE, 93 BA_FUNCTIONS_MESSAGE_ONAPPLYDOWNGRADE = BOOTSTRAPPER_APPLICATION_MESSAGE_ONAPPLYDOWNGRADE,
94 BA_FUNCTIONS_MESSAGE_ONEXECUTEPROCESSCANCEL = BOOTSTRAPPER_APPLICATION_MESSAGE_ONEXECUTEPROCESSCANCEL, 94 BA_FUNCTIONS_MESSAGE_ONEXECUTEPROCESSCANCEL = BOOTSTRAPPER_APPLICATION_MESSAGE_ONEXECUTEPROCESSCANCEL,
95 BA_FUNCTIONS_MESSAGE_ONDETECTRELATEDBUNDLEPACKAGE = BOOTSTRAPPER_APPLICATION_MESSAGE_ONDETECTRELATEDBUNDLEPACKAGE,
95 96
96 BA_FUNCTIONS_MESSAGE_ONTHEMELOADED = 1024, 97 BA_FUNCTIONS_MESSAGE_ONTHEMELOADED = 1024,
97 BA_FUNCTIONS_MESSAGE_WNDPROC, 98 BA_FUNCTIONS_MESSAGE_WNDPROC,
diff --git a/src/api/burn/balutil/inc/BalBaseBAFunctions.h b/src/api/burn/balutil/inc/BalBaseBAFunctions.h
index 614d4bcf..6bde05d2 100644
--- a/src/api/burn/balutil/inc/BalBaseBAFunctions.h
+++ b/src/api/burn/balutil/inc/BalBaseBAFunctions.h
@@ -887,6 +887,18 @@ public: // IBootstrapperApplication
887 return S_OK; 887 return S_OK;
888 } 888 }
889 889
890 virtual STDMETHODIMP OnDetectRelatedBundlePackage(
891 __in_z LPCWSTR /*wzPackageId*/,
892 __in_z LPCWSTR /*wzBundleId*/,
893 __in BOOTSTRAPPER_RELATION_TYPE /*relationType*/,
894 __in BOOL /*fPerMachine*/,
895 __in LPCWSTR /*wzVersion*/,
896 __inout BOOL* /*pfCancel*/
897 )
898 {
899 return S_OK;
900 }
901
890public: // IBAFunctions 902public: // IBAFunctions
891 virtual STDMETHODIMP OnPlan( 903 virtual STDMETHODIMP OnPlan(
892 ) 904 )
diff --git a/src/api/burn/balutil/inc/BalBaseBAFunctionsProc.h b/src/api/burn/balutil/inc/BalBaseBAFunctionsProc.h
index b96a180c..4564ad0c 100644
--- a/src/api/burn/balutil/inc/BalBaseBAFunctionsProc.h
+++ b/src/api/burn/balutil/inc/BalBaseBAFunctionsProc.h
@@ -163,6 +163,7 @@ static HRESULT WINAPI BalBaseBAFunctionsProc(
163 case BA_FUNCTIONS_MESSAGE_ONPLANRELATEDBUNDLETYPE: 163 case BA_FUNCTIONS_MESSAGE_ONPLANRELATEDBUNDLETYPE:
164 case BA_FUNCTIONS_MESSAGE_ONAPPLYDOWNGRADE: 164 case BA_FUNCTIONS_MESSAGE_ONAPPLYDOWNGRADE:
165 case BA_FUNCTIONS_MESSAGE_ONEXECUTEPROCESSCANCEL: 165 case BA_FUNCTIONS_MESSAGE_ONEXECUTEPROCESSCANCEL:
166 case BA_FUNCTIONS_MESSAGE_ONDETECTRELATEDBUNDLEPACKAGE:
166 hr = BalBaseBootstrapperApplicationProc((BOOTSTRAPPER_APPLICATION_MESSAGE)message, pvArgs, pvResults, pvContext); 167 hr = BalBaseBootstrapperApplicationProc((BOOTSTRAPPER_APPLICATION_MESSAGE)message, pvArgs, pvResults, pvContext);
167 break; 168 break;
168 case BA_FUNCTIONS_MESSAGE_ONTHEMELOADED: 169 case BA_FUNCTIONS_MESSAGE_ONTHEMELOADED:
diff --git a/src/api/burn/balutil/inc/BalBaseBootstrapperApplication.h b/src/api/burn/balutil/inc/BalBaseBootstrapperApplication.h
index 25570ffd..b661c7c9 100644
--- a/src/api/burn/balutil/inc/BalBaseBootstrapperApplication.h
+++ b/src/api/burn/balutil/inc/BalBaseBootstrapperApplication.h
@@ -1087,6 +1087,19 @@ public: // IBootstrapperApplication
1087 return S_OK; 1087 return S_OK;
1088 } 1088 }
1089 1089
1090 virtual STDMETHODIMP OnDetectRelatedBundlePackage(
1091 __in_z LPCWSTR /*wzPackageId*/,
1092 __in_z LPCWSTR /*wzBundleId*/,
1093 __in BOOTSTRAPPER_RELATION_TYPE /*relationType*/,
1094 __in BOOL /*fPerMachine*/,
1095 __in LPCWSTR /*wzVersion*/,
1096 __inout BOOL* pfCancel
1097 )
1098 {
1099 *pfCancel |= CheckCanceled();
1100 return S_OK;
1101 }
1102
1090public: //CBalBaseBootstrapperApplication 1103public: //CBalBaseBootstrapperApplication
1091 virtual STDMETHODIMP Initialize( 1104 virtual STDMETHODIMP Initialize(
1092 __in const BOOTSTRAPPER_CREATE_ARGS* pCreateArgs 1105 __in const BOOTSTRAPPER_CREATE_ARGS* pCreateArgs
diff --git a/src/api/burn/balutil/inc/BalBaseBootstrapperApplicationProc.h b/src/api/burn/balutil/inc/BalBaseBootstrapperApplicationProc.h
index b196d183..4ef7bac5 100644
--- a/src/api/burn/balutil/inc/BalBaseBootstrapperApplicationProc.h
+++ b/src/api/burn/balutil/inc/BalBaseBootstrapperApplicationProc.h
@@ -756,6 +756,15 @@ static HRESULT BalBaseBAProcOnApplyDowngrade(
756 return pBA->OnApplyDowngrade(pArgs->hrRecommended, &pResults->hrStatus); 756 return pBA->OnApplyDowngrade(pArgs->hrRecommended, &pResults->hrStatus);
757} 757}
758 758
759static HRESULT BalBaseBAProcOnDetectRelatedBundlePackage(
760 __in IBootstrapperApplication* pBA,
761 __in BA_ONDETECTRELATEDBUNDLEPACKAGE_ARGS* pArgs,
762 __inout BA_ONDETECTRELATEDBUNDLEPACKAGE_RESULTS* pResults
763 )
764{
765 return pBA->OnDetectRelatedBundlePackage(pArgs->wzPackageId, pArgs->wzBundleId, pArgs->relationType, pArgs->fPerMachine, pArgs->wzVersion, &pResults->fCancel);
766}
767
759/******************************************************************* 768/*******************************************************************
760BalBaseBootstrapperApplicationProc - requires pvContext to be of type IBootstrapperApplication. 769BalBaseBootstrapperApplicationProc - requires pvContext to be of type IBootstrapperApplication.
761 Provides a default mapping between the new message based BA interface and 770 Provides a default mapping between the new message based BA interface and
@@ -1024,6 +1033,9 @@ static HRESULT WINAPI BalBaseBootstrapperApplicationProc(
1024 case BOOTSTRAPPER_APPLICATION_MESSAGE_ONEXECUTEPROCESSCANCEL: 1033 case BOOTSTRAPPER_APPLICATION_MESSAGE_ONEXECUTEPROCESSCANCEL:
1025 hr = BalBaseBAProcOnExecuteProcessCancel(pBA, reinterpret_cast<BA_ONEXECUTEPROCESSCANCEL_ARGS*>(pvArgs), reinterpret_cast<BA_ONEXECUTEPROCESSCANCEL_RESULTS*>(pvResults)); 1034 hr = BalBaseBAProcOnExecuteProcessCancel(pBA, reinterpret_cast<BA_ONEXECUTEPROCESSCANCEL_ARGS*>(pvArgs), reinterpret_cast<BA_ONEXECUTEPROCESSCANCEL_RESULTS*>(pvResults));
1026 break; 1035 break;
1036 case BOOTSTRAPPER_APPLICATION_MESSAGE_ONDETECTRELATEDBUNDLEPACKAGE:
1037 hr = BalBaseBAProcOnDetectRelatedBundlePackage(pBA, reinterpret_cast<BA_ONDETECTRELATEDBUNDLEPACKAGE_ARGS*>(pvArgs), reinterpret_cast<BA_ONDETECTRELATEDBUNDLEPACKAGE_RESULTS*>(pvResults));
1038 break;
1027 } 1039 }
1028 } 1040 }
1029 1041
diff --git a/src/api/burn/balutil/inc/IBootstrapperApplication.h b/src/api/burn/balutil/inc/IBootstrapperApplication.h
index 6174c290..a4840228 100644
--- a/src/api/burn/balutil/inc/IBootstrapperApplication.h
+++ b/src/api/burn/balutil/inc/IBootstrapperApplication.h
@@ -723,4 +723,14 @@ DECLARE_INTERFACE_IID_(IBootstrapperApplication, IUnknown, "53C31D56-49C0-426B-A
723 __in BOOTSTRAPPER_EXECUTEPROCESSCANCEL_ACTION recommendation, 723 __in BOOTSTRAPPER_EXECUTEPROCESSCANCEL_ACTION recommendation,
724 __inout BOOTSTRAPPER_EXECUTEPROCESSCANCEL_ACTION* pAction 724 __inout BOOTSTRAPPER_EXECUTEPROCESSCANCEL_ACTION* pAction
725 ) = 0; 725 ) = 0;
726
727 // OnDetectRelatedBundlePackage - called when the engine detects a related bundle for a BundlePackage.
728 STDMETHOD(OnDetectRelatedBundlePackage)(
729 __in_z LPCWSTR wzPackageId,
730 __in_z LPCWSTR wzBundleId,
731 __in BOOTSTRAPPER_RELATION_TYPE relationType,
732 __in BOOL fPerMachine,
733 __in_z LPCWSTR wzVersion,
734 __inout BOOL* pfCancel
735 ) = 0;
726}; 736};
diff --git a/src/api/burn/balutil/inc/balinfo.h b/src/api/burn/balutil/inc/balinfo.h
index 8f61685f..0c7a5b93 100644
--- a/src/api/burn/balutil/inc/balinfo.h
+++ b/src/api/burn/balutil/inc/balinfo.h
@@ -17,6 +17,7 @@ typedef enum BAL_INFO_PACKAGE_TYPE
17 BAL_INFO_PACKAGE_TYPE_BUNDLE_ADDON, 17 BAL_INFO_PACKAGE_TYPE_BUNDLE_ADDON,
18 BAL_INFO_PACKAGE_TYPE_BUNDLE_PATCH, 18 BAL_INFO_PACKAGE_TYPE_BUNDLE_PATCH,
19 BAL_INFO_PACKAGE_TYPE_BUNDLE_UPDATE, 19 BAL_INFO_PACKAGE_TYPE_BUNDLE_UPDATE,
20 BAL_INFO_PACKAGE_TYPE_BUNDLE_CHAIN,
20} BAL_INFO_PACKAGE_TYPE; 21} BAL_INFO_PACKAGE_TYPE;
21 22
22typedef enum _BAL_INFO_RESTART 23typedef enum _BAL_INFO_RESTART
diff --git a/src/api/wix/WixToolset.Data/Symbols/SymbolDefinitions.cs b/src/api/wix/WixToolset.Data/Symbols/SymbolDefinitions.cs
index d4a91343..1fd8ded1 100644
--- a/src/api/wix/WixToolset.Data/Symbols/SymbolDefinitions.cs
+++ b/src/api/wix/WixToolset.Data/Symbols/SymbolDefinitions.cs
@@ -142,6 +142,7 @@ namespace WixToolset.Data
142 WixBundlePackageCommandLine, 142 WixBundlePackageCommandLine,
143 WixBundlePackageExitCode, 143 WixBundlePackageExitCode,
144 WixBundlePackageGroup, 144 WixBundlePackageGroup,
145 WixBundlePackageRelatedBundle,
145 WixBundlePatchTargetCode, 146 WixBundlePatchTargetCode,
146 WixBundlePayload, 147 WixBundlePayload,
147 WixBundlePayloadGroup, 148 WixBundlePayloadGroup,
@@ -618,6 +619,9 @@ namespace WixToolset.Data
618 case SymbolDefinitionType.WixBundlePackageGroup: 619 case SymbolDefinitionType.WixBundlePackageGroup:
619 return SymbolDefinitions.WixBundlePackageGroup; 620 return SymbolDefinitions.WixBundlePackageGroup;
620 621
622 case SymbolDefinitionType.WixBundlePackageRelatedBundle:
623 return SymbolDefinitions.WixBundlePackageRelatedBundle;
624
621 case SymbolDefinitionType.WixBundlePatchTargetCode: 625 case SymbolDefinitionType.WixBundlePatchTargetCode:
622 return SymbolDefinitions.WixBundlePatchTargetCode; 626 return SymbolDefinitions.WixBundlePatchTargetCode;
623 627
diff --git a/src/api/wix/WixToolset.Data/Symbols/WixBundleBundlePackageSymbol.cs b/src/api/wix/WixToolset.Data/Symbols/WixBundleBundlePackageSymbol.cs
index 36b9eb67..dcf59e28 100644
--- a/src/api/wix/WixToolset.Data/Symbols/WixBundleBundlePackageSymbol.cs
+++ b/src/api/wix/WixToolset.Data/Symbols/WixBundleBundlePackageSymbol.cs
@@ -12,6 +12,7 @@ namespace WixToolset.Data
12 { 12 {
13 new IntermediateFieldDefinition(nameof(WixBundleBundlePackageSymbolFields.Attributes), IntermediateFieldType.Number), 13 new IntermediateFieldDefinition(nameof(WixBundleBundlePackageSymbolFields.Attributes), IntermediateFieldType.Number),
14 new IntermediateFieldDefinition(nameof(WixBundleBundlePackageSymbolFields.BundleId), IntermediateFieldType.String), 14 new IntermediateFieldDefinition(nameof(WixBundleBundlePackageSymbolFields.BundleId), IntermediateFieldType.String),
15 new IntermediateFieldDefinition(nameof(WixBundleBundlePackageSymbolFields.Version), IntermediateFieldType.String),
15 new IntermediateFieldDefinition(nameof(WixBundleBundlePackageSymbolFields.InstallCommand), IntermediateFieldType.String), 16 new IntermediateFieldDefinition(nameof(WixBundleBundlePackageSymbolFields.InstallCommand), IntermediateFieldType.String),
16 new IntermediateFieldDefinition(nameof(WixBundleBundlePackageSymbolFields.RepairCommand), IntermediateFieldType.String), 17 new IntermediateFieldDefinition(nameof(WixBundleBundlePackageSymbolFields.RepairCommand), IntermediateFieldType.String),
17 new IntermediateFieldDefinition(nameof(WixBundleBundlePackageSymbolFields.UninstallCommand), IntermediateFieldType.String), 18 new IntermediateFieldDefinition(nameof(WixBundleBundlePackageSymbolFields.UninstallCommand), IntermediateFieldType.String),
@@ -28,6 +29,7 @@ namespace WixToolset.Data.Symbols
28 { 29 {
29 Attributes, 30 Attributes,
30 BundleId, 31 BundleId,
32 Version,
31 InstallCommand, 33 InstallCommand,
32 RepairCommand, 34 RepairCommand,
33 UninstallCommand, 35 UninstallCommand,
@@ -65,6 +67,12 @@ namespace WixToolset.Data.Symbols
65 set => this.Set((int)WixBundleBundlePackageSymbolFields.BundleId, value); 67 set => this.Set((int)WixBundleBundlePackageSymbolFields.BundleId, value);
66 } 68 }
67 69
70 public string Version
71 {
72 get => (string)this.Fields[(int)WixBundleBundlePackageSymbolFields.Version];
73 set => this.Set((int)WixBundleBundlePackageSymbolFields.Version, value);
74 }
75
68 public string InstallCommand 76 public string InstallCommand
69 { 77 {
70 get => (string)this.Fields[(int)WixBundleBundlePackageSymbolFields.InstallCommand]; 78 get => (string)this.Fields[(int)WixBundleBundlePackageSymbolFields.InstallCommand];
diff --git a/src/api/wix/WixToolset.Data/Symbols/WixBundlePackageRelatedBundleSymbol.cs b/src/api/wix/WixToolset.Data/Symbols/WixBundlePackageRelatedBundleSymbol.cs
new file mode 100644
index 00000000..dfb48714
--- /dev/null
+++ b/src/api/wix/WixToolset.Data/Symbols/WixBundlePackageRelatedBundleSymbol.cs
@@ -0,0 +1,60 @@
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
3namespace WixToolset.Data
4{
5 using WixToolset.Data.Symbols;
6
7 public static partial class SymbolDefinitions
8 {
9 public static readonly IntermediateSymbolDefinition WixBundlePackageRelatedBundle = new IntermediateSymbolDefinition(
10 SymbolDefinitionType.WixBundlePackageRelatedBundle,
11 new[]
12 {
13 new IntermediateFieldDefinition(nameof(WixBundlePackageRelatedBundleSymbolFields.PackageRef), IntermediateFieldType.String),
14 new IntermediateFieldDefinition(nameof(WixBundlePackageRelatedBundleSymbolFields.BundleId), IntermediateFieldType.String),
15 new IntermediateFieldDefinition(nameof(WixBundlePackageRelatedBundleSymbolFields.Action), IntermediateFieldType.Number),
16 },
17 typeof(WixBundlePackageRelatedBundleSymbol));
18 }
19}
20
21namespace WixToolset.Data.Symbols
22{
23 public enum WixBundlePackageRelatedBundleSymbolFields
24 {
25 PackageRef,
26 BundleId,
27 Action,
28 }
29
30 public class WixBundlePackageRelatedBundleSymbol : IntermediateSymbol
31 {
32 public WixBundlePackageRelatedBundleSymbol() : base(SymbolDefinitions.WixBundlePackageRelatedBundle, null, null)
33 {
34 }
35
36 public WixBundlePackageRelatedBundleSymbol(SourceLineNumber sourceLineNumber, Identifier id = null) : base(SymbolDefinitions.WixBundlePackageRelatedBundle, sourceLineNumber, id)
37 {
38 }
39
40 public IntermediateField this[WixBundlePackageRelatedBundleSymbolFields index] => this.Fields[(int)index];
41
42 public string PackageRef
43 {
44 get => (string)this.Fields[(int)WixBundlePackageRelatedBundleSymbolFields.PackageRef];
45 set => this.Set((int)WixBundlePackageRelatedBundleSymbolFields.PackageRef, value);
46 }
47
48 public string BundleId
49 {
50 get => (string)this.Fields[(int)WixBundlePackageRelatedBundleSymbolFields.BundleId];
51 set => this.Set((int)WixBundlePackageRelatedBundleSymbolFields.BundleId, value);
52 }
53
54 public RelatedBundleActionType Action
55 {
56 get => (RelatedBundleActionType)this.Fields[(int)WixBundlePackageRelatedBundleSymbolFields.Action].AsNumber();
57 set => this.Set((int)WixBundlePackageRelatedBundleSymbolFields.Action, (int)value);
58 }
59 }
60}
diff --git a/src/burn/engine/bundlepackageengine.cpp b/src/burn/engine/bundlepackageengine.cpp
index f3badfc1..ef08d417 100644
--- a/src/burn/engine/bundlepackageengine.cpp
+++ b/src/burn/engine/bundlepackageengine.cpp
@@ -2,6 +2,18 @@
2 2
3#include "precomp.h" 3#include "precomp.h"
4 4
5typedef struct _BUNDLE_QUERY_CONTEXT
6{
7 BURN_PACKAGE* pPackage;
8 BURN_USER_EXPERIENCE* pUserExperience;
9 BOOL fSelfFound;
10 BOOL fNewerFound;
11} BUNDLE_QUERY_CONTEXT;
12
13static BUNDLE_QUERY_CALLBACK_RESULT CALLBACK QueryRelatedBundlesCallback(
14 __in const BUNDLE_QUERY_RELATED_BUNDLE_RESULT* pBundle,
15 __in_opt LPVOID pvContext
16 );
5static HRESULT ExecuteBundle( 17static HRESULT ExecuteBundle(
6 __in BURN_CACHE* pCache, 18 __in BURN_CACHE* pCache,
7 __in BURN_VARIABLES* pVariables, 19 __in BURN_VARIABLES* pVariables,
@@ -35,6 +47,18 @@ extern "C" HRESULT BundlePackageEngineParsePackageFromXml(
35 hr = XmlGetAttributeEx(pixnBundlePackage, L"BundleId", &pPackage->Bundle.sczBundleId); 47 hr = XmlGetAttributeEx(pixnBundlePackage, L"BundleId", &pPackage->Bundle.sczBundleId);
36 ExitOnRequiredXmlQueryFailure(hr, "Failed to get @BundleId."); 48 ExitOnRequiredXmlQueryFailure(hr, "Failed to get @BundleId.");
37 49
50 // @Version
51 hr = XmlGetAttributeEx(pixnBundlePackage, L"Version", &scz);
52 ExitOnRequiredXmlQueryFailure(hr, "Failed to get @Version.");
53
54 hr = VerParseVersion(scz, 0, FALSE, &pPackage->Bundle.pVersion);
55 ExitOnFailure(hr, "Failed to parse @Version: %ls", scz);
56
57 if (pPackage->Bundle.pVersion->fInvalid)
58 {
59 LogId(REPORT_WARNING, MSG_MANIFEST_INVALID_VERSION, scz);
60 }
61
38 // @InstallArguments 62 // @InstallArguments
39 hr = XmlGetAttributeEx(pixnBundlePackage, L"InstallArguments", &pPackage->Bundle.sczInstallArguments); 63 hr = XmlGetAttributeEx(pixnBundlePackage, L"InstallArguments", &pPackage->Bundle.sczInstallArguments);
40 ExitOnOptionalXmlQueryFailure(hr, fFoundXml, "Failed to get @InstallArguments."); 64 ExitOnOptionalXmlQueryFailure(hr, fFoundXml, "Failed to get @InstallArguments.");
@@ -55,6 +79,9 @@ extern "C" HRESULT BundlePackageEngineParsePackageFromXml(
55 hr = XmlGetYesNoAttribute(pixnBundlePackage, L"Win64", &pPackage->Bundle.fWin64); 79 hr = XmlGetYesNoAttribute(pixnBundlePackage, L"Win64", &pPackage->Bundle.fWin64);
56 ExitOnRequiredXmlQueryFailure(hr, "Failed to get @Win64."); 80 ExitOnRequiredXmlQueryFailure(hr, "Failed to get @Win64.");
57 81
82 hr = BundlePackageEngineParseRelatedCodes(pixnBundlePackage, &pPackage->Bundle.rgsczDetectCodes, &pPackage->Bundle.cDetectCodes, &pPackage->Bundle.rgsczUpgradeCodes, &pPackage->Bundle.cUpgradeCodes, &pPackage->Bundle.rgsczAddonCodes, &pPackage->Bundle.cAddonCodes, &pPackage->Bundle.rgsczPatchCodes, &pPackage->Bundle.cPatchCodes);
83 ExitOnFailure(hr, "Failed to parse related codes.");
84
58 hr = ExeEngineParseExitCodesFromXml(pixnBundlePackage, &pPackage->Bundle.rgExitCodes, &pPackage->Bundle.cExitCodes); 85 hr = ExeEngineParseExitCodesFromXml(pixnBundlePackage, &pPackage->Bundle.rgExitCodes, &pPackage->Bundle.cExitCodes);
59 ExitOnFailure(hr, "Failed to parse exit codes."); 86 ExitOnFailure(hr, "Failed to parse exit codes.");
60 87
@@ -70,11 +97,101 @@ LExit:
70 return hr; 97 return hr;
71} 98}
72 99
100
101extern "C" HRESULT BundlePackageEngineParseRelatedCodes(
102 __in IXMLDOMNode* pixnBundle,
103 __in LPWSTR** prgsczDetectCodes,
104 __in DWORD* pcDetectCodes,
105 __in LPWSTR** prgsczUpgradeCodes,
106 __in DWORD* pcUpgradeCodes,
107 __in LPWSTR** prgsczAddonCodes,
108 __in DWORD* pcAddonCodes,
109 __in LPWSTR** prgsczPatchCodes,
110 __in DWORD* pcPatchCodes
111 )
112{
113 HRESULT hr = S_OK;
114 IXMLDOMNodeList* pixnNodes = NULL;
115 IXMLDOMNode* pixnElement = NULL;
116 LPWSTR sczAction = NULL;
117 LPWSTR sczId = NULL;
118 DWORD cElements = 0;
119
120 hr = XmlSelectNodes(pixnBundle, L"RelatedBundle", &pixnNodes);
121 ExitOnFailure(hr, "Failed to get RelatedBundle nodes");
122
123 hr = pixnNodes->get_length((long*)&cElements);
124 ExitOnFailure(hr, "Failed to get RelatedBundle element count.");
125
126 for (DWORD i = 0; i < cElements; ++i)
127 {
128 hr = XmlNextElement(pixnNodes, &pixnElement, NULL);
129 ExitOnFailure(hr, "Failed to get next RelatedBundle element.");
130
131 hr = XmlGetAttributeEx(pixnElement, L"Action", &sczAction);
132 ExitOnFailure(hr, "Failed to get @Action.");
133
134 hr = XmlGetAttributeEx(pixnElement, L"Id", &sczId);
135 ExitOnFailure(hr, "Failed to get @Id.");
136
137 if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, sczAction, -1, L"Detect", -1))
138 {
139 hr = MemEnsureArraySizeForNewItems(reinterpret_cast<LPVOID*>(prgsczDetectCodes), *pcDetectCodes, 1, sizeof(LPWSTR), 5);
140 ExitOnFailure(hr, "Failed to resize Detect code array");
141
142 *prgsczDetectCodes[*pcDetectCodes] = sczId;
143 sczId = NULL;
144 *pcDetectCodes += 1;
145 }
146 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, sczAction, -1, L"Upgrade", -1))
147 {
148 hr = MemEnsureArraySizeForNewItems(reinterpret_cast<LPVOID*>(prgsczUpgradeCodes), *pcUpgradeCodes, 1, sizeof(LPWSTR), 5);
149 ExitOnFailure(hr, "Failed to resize Upgrade code array");
150
151 *prgsczUpgradeCodes[*pcUpgradeCodes] = sczId;
152 sczId = NULL;
153 *pcUpgradeCodes += 1;
154 }
155 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, sczAction, -1, L"Addon", -1))
156 {
157 hr = MemEnsureArraySizeForNewItems(reinterpret_cast<LPVOID*>(prgsczAddonCodes), *pcAddonCodes, 1, sizeof(LPWSTR), 5);
158 ExitOnFailure(hr, "Failed to resize Addon code array");
159
160 *prgsczAddonCodes[*pcAddonCodes] = sczId;
161 sczId = NULL;
162 *pcAddonCodes += 1;
163 }
164 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, sczAction, -1, L"Patch", -1))
165 {
166 hr = MemEnsureArraySizeForNewItems(reinterpret_cast<LPVOID*>(prgsczPatchCodes), *pcPatchCodes, 1, sizeof(LPWSTR), 5);
167 ExitOnFailure(hr, "Failed to resize Patch code array");
168
169 *prgsczPatchCodes[*pcPatchCodes] = sczId;
170 sczId = NULL;
171 *pcPatchCodes += 1;
172 }
173 else
174 {
175 hr = E_INVALIDARG;
176 ExitOnFailure(hr, "Invalid value for @Action: %ls", sczAction);
177 }
178 }
179
180LExit:
181 ReleaseObject(pixnNodes);
182 ReleaseObject(pixnElement);
183 ReleaseStr(sczAction);
184 ReleaseStr(sczId);
185
186 return hr;
187}
188
73extern "C" void BundlePackageEnginePackageUninitialize( 189extern "C" void BundlePackageEnginePackageUninitialize(
74 __in BURN_PACKAGE* pPackage 190 __in BURN_PACKAGE* pPackage
75 ) 191 )
76{ 192{
77 ReleaseStr(pPackage->Bundle.sczBundleId); 193 ReleaseStr(pPackage->Bundle.sczBundleId);
194 ReleaseVerutilVersion(pPackage->Bundle.pVersion);
78 ReleaseStr(pPackage->Bundle.sczRegistrationKey); 195 ReleaseStr(pPackage->Bundle.sczRegistrationKey);
79 ReleaseStr(pPackage->Bundle.sczInstallArguments); 196 ReleaseStr(pPackage->Bundle.sczInstallArguments);
80 ReleaseStr(pPackage->Bundle.sczRepairArguments); 197 ReleaseStr(pPackage->Bundle.sczRepairArguments);
@@ -92,45 +209,95 @@ extern "C" void BundlePackageEnginePackageUninitialize(
92 MemFree(pPackage->Bundle.rgCommandLineArguments); 209 MemFree(pPackage->Bundle.rgCommandLineArguments);
93 } 210 }
94 211
212 for (DWORD i = 0; i < pPackage->Bundle.cDetectCodes; ++i)
213 {
214 ReleaseStr(pPackage->Bundle.rgsczDetectCodes[i]);
215 }
216 ReleaseMem(pPackage->Bundle.rgsczDetectCodes);
217
218 for (DWORD i = 0; i < pPackage->Bundle.cUpgradeCodes; ++i)
219 {
220 ReleaseStr(pPackage->Bundle.rgsczUpgradeCodes[i]);
221 }
222 ReleaseMem(pPackage->Bundle.rgsczUpgradeCodes);
223
224 for (DWORD i = 0; i < pPackage->Bundle.cAddonCodes; ++i)
225 {
226 ReleaseStr(pPackage->Bundle.rgsczAddonCodes[i]);
227 }
228 ReleaseMem(pPackage->Bundle.rgsczAddonCodes);
229
230 for (DWORD i = 0; i < pPackage->Bundle.cPatchCodes; ++i)
231 {
232 ReleaseStr(pPackage->Bundle.rgsczPatchCodes[i]);
233 }
234 ReleaseMem(pPackage->Bundle.rgsczPatchCodes);
235
95 // clear struct 236 // clear struct
96 memset(&pPackage->Bundle, 0, sizeof(pPackage->Bundle)); 237 memset(&pPackage->Bundle, 0, sizeof(pPackage->Bundle));
97} 238}
98 239
99extern "C" HRESULT BundlePackageEngineDetectPackage( 240extern "C" HRESULT BundlePackageEngineDetectPackage(
100 __in BURN_PACKAGE* pPackage 241 __in BURN_PACKAGE* pPackage,
242 __in BURN_REGISTRATION* /*pRegistration*/,
243 __in BURN_USER_EXPERIENCE* pUserExperience
101 ) 244 )
102{ 245{
103 HRESULT hr = S_OK; 246 HRESULT hr = S_OK;
104 HKEY hkRegistration = NULL; 247 BUNDLE_QUERY_CONTEXT queryContext = { };
105 DWORD dwInstalled = 0; 248
106 BOOL fDetected = FALSE; 249 queryContext.pPackage = pPackage;
107 HKEY hkRoot = pPackage->fPerMachine ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER; 250 queryContext.pUserExperience = pUserExperience;
108 REG_KEY_BITNESS bitness = pPackage->Bundle.fWin64 ? REG_KEY_64BIT : REG_KEY_32BIT; 251
109 252 hr = BundleQueryRelatedBundles(
110 // TODO: detect all related bundles, so that the Obsolete state can be detected. 253 BUNDLE_INSTALL_CONTEXT_MACHINE,
111 hr = RegOpenEx(hkRoot, pPackage->Bundle.sczRegistrationKey, KEY_QUERY_VALUE, bitness, &hkRegistration); 254 const_cast<LPCWSTR*>(pPackage->Bundle.rgsczDetectCodes),
112 if (SUCCEEDED(hr)) 255 pPackage->Bundle.cDetectCodes,
256 const_cast<LPCWSTR*>(pPackage->Bundle.rgsczUpgradeCodes),
257 pPackage->Bundle.cUpgradeCodes,
258 const_cast<LPCWSTR*>(pPackage->Bundle.rgsczAddonCodes),
259 pPackage->Bundle.cAddonCodes,
260 const_cast<LPCWSTR*>(pPackage->Bundle.rgsczPatchCodes),
261 pPackage->Bundle.cPatchCodes,
262 QueryRelatedBundlesCallback,
263 &queryContext);
264 ExitOnFailure(hr, "Failed to query per-machine related bundle packages.");
265
266 hr = BundleQueryRelatedBundles(
267 BUNDLE_INSTALL_CONTEXT_USER,
268 const_cast<LPCWSTR*>(pPackage->Bundle.rgsczDetectCodes),
269 pPackage->Bundle.cDetectCodes,
270 const_cast<LPCWSTR*>(pPackage->Bundle.rgsczUpgradeCodes),
271 pPackage->Bundle.cUpgradeCodes,
272 const_cast<LPCWSTR*>(pPackage->Bundle.rgsczAddonCodes),
273 pPackage->Bundle.cAddonCodes,
274 const_cast<LPCWSTR*>(pPackage->Bundle.rgsczPatchCodes),
275 pPackage->Bundle.cPatchCodes,
276 QueryRelatedBundlesCallback,
277 &queryContext);
278 ExitOnFailure(hr, "Failed to query per-user related bundle packages.");
279
280 if (queryContext.fNewerFound)
113 { 281 {
114 hr = RegReadNumber(hkRegistration, REGISTRY_BUNDLE_INSTALLED, &dwInstalled); 282 pPackage->currentState = queryContext.fSelfFound ? BOOTSTRAPPER_PACKAGE_STATE_SUPERSEDED : BOOTSTRAPPER_PACKAGE_STATE_OBSOLETE;
115 } 283 }
116 284 else
117 // Not finding the key or value is okay.
118 if (E_FILENOTFOUND == hr || E_PATHNOTFOUND == hr)
119 { 285 {
120 hr = S_OK; 286 pPackage->currentState = queryContext.fSelfFound ? BOOTSTRAPPER_PACKAGE_STATE_PRESENT : BOOTSTRAPPER_PACKAGE_STATE_ABSENT;
121 } 287 }
122 288
123 fDetected = (1 == dwInstalled);
124
125 // update detect state
126 pPackage->currentState = fDetected ? BOOTSTRAPPER_PACKAGE_STATE_PRESENT : BOOTSTRAPPER_PACKAGE_STATE_ABSENT;
127
128 if (pPackage->fCanAffectRegistration) 289 if (pPackage->fCanAffectRegistration)
129 { 290 {
130 pPackage->installRegistrationState = BOOTSTRAPPER_PACKAGE_STATE_ABSENT < pPackage->currentState ? BURN_PACKAGE_REGISTRATION_STATE_PRESENT : BURN_PACKAGE_REGISTRATION_STATE_ABSENT; 291 pPackage->installRegistrationState = BOOTSTRAPPER_PACKAGE_STATE_ABSENT < pPackage->currentState ? BURN_PACKAGE_REGISTRATION_STATE_PRESENT : BURN_PACKAGE_REGISTRATION_STATE_ABSENT;
131 } 292 }
132 293
133 ReleaseRegKey(hkRegistration); 294 // TODO: The bundle is registering itself as a dependent when installed as a chain package, which prevents us from uninstalling it.
295 //hr = DependencyDetectChainPackage(pPackage, pRegistration);
296 //ExitOnFailure(hr, "Failed to detect dependencies for BUNDLE package.");
297
298 // TODO: uninstalling compatible Bundles like MsiEngine supports?
299
300LExit:
134 return hr; 301 return hr;
135} 302}
136 303
@@ -148,7 +315,8 @@ extern "C" HRESULT BundlePackageEnginePlanCalculatePackage(
148 // execute action 315 // execute action
149 switch (pPackage->currentState) 316 switch (pPackage->currentState)
150 { 317 {
151 case BOOTSTRAPPER_PACKAGE_STATE_PRESENT: 318 case BOOTSTRAPPER_PACKAGE_STATE_PRESENT: __fallthrough;
319 case BOOTSTRAPPER_PACKAGE_STATE_SUPERSEDED:
152 switch (pPackage->requested) 320 switch (pPackage->requested)
153 { 321 {
154 case BOOTSTRAPPER_REQUEST_STATE_PRESENT: 322 case BOOTSTRAPPER_REQUEST_STATE_PRESENT:
@@ -173,6 +341,7 @@ extern "C" HRESULT BundlePackageEnginePlanCalculatePackage(
173 } 341 }
174 break; 342 break;
175 343
344 case BOOTSTRAPPER_PACKAGE_STATE_OBSOLETE: __fallthrough;
176 case BOOTSTRAPPER_PACKAGE_STATE_ABSENT: 345 case BOOTSTRAPPER_PACKAGE_STATE_ABSENT:
177 switch (pPackage->requested) 346 switch (pPackage->requested)
178 { 347 {
@@ -200,7 +369,8 @@ extern "C" HRESULT BundlePackageEnginePlanCalculatePackage(
200 { 369 {
201 switch (pPackage->currentState) 370 switch (pPackage->currentState)
202 { 371 {
203 case BOOTSTRAPPER_PACKAGE_STATE_PRESENT: 372 case BOOTSTRAPPER_PACKAGE_STATE_PRESENT: __fallthrough;
373 case BOOTSTRAPPER_PACKAGE_STATE_SUPERSEDED:
204 switch (pPackage->requested) 374 switch (pPackage->requested)
205 { 375 {
206 case BOOTSTRAPPER_REQUEST_STATE_PRESENT: __fallthrough; 376 case BOOTSTRAPPER_REQUEST_STATE_PRESENT: __fallthrough;
@@ -218,6 +388,7 @@ extern "C" HRESULT BundlePackageEnginePlanCalculatePackage(
218 } 388 }
219 break; 389 break;
220 390
391 case BOOTSTRAPPER_PACKAGE_STATE_OBSOLETE: __fallthrough;
221 case BOOTSTRAPPER_PACKAGE_STATE_ABSENT: 392 case BOOTSTRAPPER_PACKAGE_STATE_ABSENT:
222 switch (pPackage->requested) 393 switch (pPackage->requested)
223 { 394 {
@@ -481,6 +652,66 @@ LExit:
481 return; 652 return;
482} 653}
483 654
655static BUNDLE_QUERY_CALLBACK_RESULT CALLBACK QueryRelatedBundlesCallback(
656 __in const BUNDLE_QUERY_RELATED_BUNDLE_RESULT* pBundle,
657 __in_opt LPVOID pvContext
658 )
659{
660 HRESULT hr = S_OK;
661 BUNDLE_QUERY_CALLBACK_RESULT result = BUNDLE_QUERY_CALLBACK_RESULT_CONTINUE;
662 LPWSTR sczBundleVersion = NULL;
663 VERUTIL_VERSION* pVersion = NULL;
664 int nCompare = 0;
665 BUNDLE_QUERY_CONTEXT* pContext = reinterpret_cast<BUNDLE_QUERY_CONTEXT*>(pvContext);
666 BURN_PACKAGE* pPackage = pContext->pPackage;
667 BOOTSTRAPPER_RELATION_TYPE relationType = RelatedBundleConvertRelationType(pBundle->relationType);
668 BOOL fPerMachine = BUNDLE_INSTALL_CONTEXT_MACHINE == pBundle->installContext;
669
670 if (CSTR_EQUAL == ::CompareStringW(LOCALE_NEUTRAL, NORM_IGNORECASE, pBundle->wzBundleId, -1, pPackage->Bundle.sczBundleId, -1))
671 {
672 Assert(BOOTSTRAPPER_RELATION_UPGRADE == relationType);
673 Assert(pPackage->Bundle.fWin64 == (REG_KEY_64BIT == pBundle->regBitness));
674
675 pContext->fSelfFound = TRUE;
676 }
677
678 hr = RegReadString(pBundle->hkBundle, BURN_REGISTRATION_REGISTRY_BUNDLE_VERSION, &sczBundleVersion);
679 ExitOnFailure(hr, "Failed to read version from registry for related bundle package: %ls", pBundle->wzBundleId);
680
681 hr = VerParseVersion(sczBundleVersion, 0, FALSE, &pVersion);
682 ExitOnFailure(hr, "Failed to parse related bundle package version: %ls", sczBundleVersion);
683
684 if (pVersion->fInvalid)
685 {
686 LogId(REPORT_WARNING, MSG_RELATED_PACKAGE_INVALID_VERSION, pBundle->wzBundleId, sczBundleVersion);
687 }
688
689 if (BOOTSTRAPPER_RELATION_UPGRADE == relationType)
690 {
691 hr = VerCompareParsedVersions(pPackage->Bundle.pVersion, pVersion, &nCompare);
692 ExitOnFailure(hr, "Failed to compare related bundle package version: %ls", pVersion->sczVersion);
693
694 if (nCompare < 0)
695 {
696 pContext->fNewerFound = TRUE;
697 }
698 }
699
700 result = BUNDLE_QUERY_CALLBACK_RESULT_CANCEL;
701
702 // Pass to BA.
703 hr = UserExperienceOnDetectRelatedBundlePackage(pContext->pUserExperience, pPackage->sczId, pBundle->wzBundleId, relationType, fPerMachine, pVersion);
704 ExitOnRootFailure(hr, "BA aborted detect related BUNDLE package.");
705
706 result = BUNDLE_QUERY_CALLBACK_RESULT_CONTINUE;
707
708LExit:
709 ReleaseVerutilVersion(pVersion);
710 ReleaseStr(sczBundleVersion);
711
712 return result;
713}
714
484static HRESULT ExecuteBundle( 715static HRESULT ExecuteBundle(
485 __in BURN_CACHE* pCache, 716 __in BURN_CACHE* pCache,
486 __in BURN_VARIABLES* pVariables, 717 __in BURN_VARIABLES* pVariables,
diff --git a/src/burn/engine/bundlepackageengine.h b/src/burn/engine/bundlepackageengine.h
index 9271ac6a..e245f6ce 100644
--- a/src/burn/engine/bundlepackageengine.h
+++ b/src/burn/engine/bundlepackageengine.h
@@ -13,11 +13,24 @@ HRESULT BundlePackageEngineParsePackageFromXml(
13 __in IXMLDOMNode* pixnBundlePackage, 13 __in IXMLDOMNode* pixnBundlePackage,
14 __in BURN_PACKAGE* pPackage 14 __in BURN_PACKAGE* pPackage
15 ); 15 );
16HRESULT BundlePackageEngineParseRelatedCodes(
17 __in IXMLDOMNode* pixnBundle,
18 __in LPWSTR** prgsczDetectCodes,
19 __in DWORD* pcDetectCodes,
20 __in LPWSTR** prgsczUpgradeCodes,
21 __in DWORD* pcUpgradeCodes,
22 __in LPWSTR** prgsczAddonCodes,
23 __in DWORD* pcAddonCodes,
24 __in LPWSTR** prgsczPatchCodes,
25 __in DWORD* pcPatchCodes
26 );
16void BundlePackageEnginePackageUninitialize( 27void BundlePackageEnginePackageUninitialize(
17 __in BURN_PACKAGE* pPackage 28 __in BURN_PACKAGE* pPackage
18 ); 29 );
19HRESULT BundlePackageEngineDetectPackage( 30HRESULT BundlePackageEngineDetectPackage(
20 __in BURN_PACKAGE* pPackage 31 __in BURN_PACKAGE* pPackage,
32 __in BURN_REGISTRATION* pRegistration,
33 __in BURN_USER_EXPERIENCE* pUserExperience
21 ); 34 );
22HRESULT BundlePackageEnginePlanCalculatePackage( 35HRESULT BundlePackageEnginePlanCalculatePackage(
23 __in BURN_PACKAGE* pPackage 36 __in BURN_PACKAGE* pPackage
diff --git a/src/burn/engine/core.cpp b/src/burn/engine/core.cpp
index 3370ad05..0bbf7039 100644
--- a/src/burn/engine/core.cpp
+++ b/src/burn/engine/core.cpp
@@ -2121,7 +2121,7 @@ static HRESULT DetectPackage(
2121 switch (pPackage->type) 2121 switch (pPackage->type)
2122 { 2122 {
2123 case BURN_PACKAGE_TYPE_BUNDLE: 2123 case BURN_PACKAGE_TYPE_BUNDLE:
2124 hr = BundlePackageEngineDetectPackage(pPackage); 2124 hr = BundlePackageEngineDetectPackage(pPackage, &pEngineState->registration, &pEngineState->userExperience);
2125 break; 2125 break;
2126 2126
2127 case BURN_PACKAGE_TYPE_EXE: 2127 case BURN_PACKAGE_TYPE_EXE:
diff --git a/src/burn/engine/package.h b/src/burn/engine/package.h
index c13c651b..4021031f 100644
--- a/src/burn/engine/package.h
+++ b/src/burn/engine/package.h
@@ -303,14 +303,23 @@ typedef struct _BURN_PACKAGE
303 struct 303 struct
304 { 304 {
305 LPWSTR sczBundleId; 305 LPWSTR sczBundleId;
306 VERUTIL_VERSION* pVersion;
306 LPWSTR sczRegistrationKey; 307 LPWSTR sczRegistrationKey;
307 LPWSTR sczInstallArguments; 308 LPWSTR sczInstallArguments;
308 LPWSTR sczRepairArguments; 309 LPWSTR sczRepairArguments;
309 LPWSTR sczUninstallArguments; 310 LPWSTR sczUninstallArguments;
310 311
311 LPWSTR sczIgnoreDependencies; 312 LPWSTR* rgsczDetectCodes;
312 LPCWSTR wzAncestors; // points directly into engine state. 313 DWORD cDetectCodes;
313 LPCWSTR wzEngineWorkingDirectory; // points directly into engine state. 314
315 LPWSTR* rgsczUpgradeCodes;
316 DWORD cUpgradeCodes;
317
318 LPWSTR* rgsczAddonCodes;
319 DWORD cAddonCodes;
320
321 LPWSTR* rgsczPatchCodes;
322 DWORD cPatchCodes;
314 323
315 BOOL fWin64; 324 BOOL fWin64;
316 BOOL fSupportsBurnProtocol; 325 BOOL fSupportsBurnProtocol;
@@ -320,6 +329,10 @@ typedef struct _BURN_PACKAGE
320 329
321 BURN_EXE_COMMAND_LINE_ARGUMENT* rgCommandLineArguments; 330 BURN_EXE_COMMAND_LINE_ARGUMENT* rgCommandLineArguments;
322 DWORD cCommandLineArguments; 331 DWORD cCommandLineArguments;
332
333 LPWSTR sczIgnoreDependencies;
334 LPCWSTR wzAncestors; // points directly into engine state.
335 LPCWSTR wzEngineWorkingDirectory; // points directly into engine state.
323 } Bundle; 336 } Bundle;
324 struct 337 struct
325 { 338 {
diff --git a/src/burn/engine/registration.cpp b/src/burn/engine/registration.cpp
index 78f8eeb1..f2b8383d 100644
--- a/src/burn/engine/registration.cpp
+++ b/src/burn/engine/registration.cpp
@@ -71,10 +71,6 @@ static HRESULT UpdateResumeMode(
71 __in BOOTSTRAPPER_REGISTRATION_TYPE registrationType, 71 __in BOOTSTRAPPER_REGISTRATION_TYPE registrationType,
72 __in BOOL fRestartInitiated 72 __in BOOL fRestartInitiated
73 ); 73 );
74static HRESULT ParseRelatedCodes(
75 __in BURN_REGISTRATION* pRegistration,
76 __in IXMLDOMNode* pixnBundle
77 );
78static HRESULT FormatUpdateRegistrationKey( 74static HRESULT FormatUpdateRegistrationKey(
79 __in BURN_REGISTRATION* pRegistration, 75 __in BURN_REGISTRATION* pRegistration,
80 __out_z LPWSTR* psczKey 76 __out_z LPWSTR* psczKey
@@ -143,7 +139,7 @@ extern "C" HRESULT RegistrationParseFromXml(
143 hr = XmlGetAttributeEx(pixnRegistrationNode, L"Tag", &pRegistration->sczTag); 139 hr = XmlGetAttributeEx(pixnRegistrationNode, L"Tag", &pRegistration->sczTag);
144 ExitOnFailure(hr, "Failed to get @Tag."); 140 ExitOnFailure(hr, "Failed to get @Tag.");
145 141
146 hr = ParseRelatedCodes(pRegistration, pixnBundle); 142 hr = BundlePackageEngineParseRelatedCodes(pixnBundle, &pRegistration->rgsczDetectCodes, &pRegistration->cDetectCodes, &pRegistration->rgsczUpgradeCodes, &pRegistration->cUpgradeCodes, &pRegistration->rgsczAddonCodes, &pRegistration->cAddonCodes, &pRegistration->rgsczPatchCodes, &pRegistration->cPatchCodes);
147 ExitOnFailure(hr, "Failed to parse related bundles"); 143 ExitOnFailure(hr, "Failed to parse related bundles");
148 144
149 // @Version 145 // @Version
@@ -1395,87 +1391,6 @@ LExit:
1395 return hr; 1391 return hr;
1396} 1392}
1397 1393
1398static HRESULT ParseRelatedCodes(
1399 __in BURN_REGISTRATION* pRegistration,
1400 __in IXMLDOMNode* pixnBundle
1401 )
1402{
1403 HRESULT hr = S_OK;
1404 IXMLDOMNodeList* pixnNodes = NULL;
1405 IXMLDOMNode* pixnElement = NULL;
1406 LPWSTR sczAction = NULL;
1407 LPWSTR sczId = NULL;
1408 DWORD cElements = 0;
1409
1410 hr = XmlSelectNodes(pixnBundle, L"RelatedBundle", &pixnNodes);
1411 ExitOnFailure(hr, "Failed to get RelatedBundle nodes");
1412
1413 hr = pixnNodes->get_length((long*)&cElements);
1414 ExitOnFailure(hr, "Failed to get RelatedBundle element count.");
1415
1416 for (DWORD i = 0; i < cElements; ++i)
1417 {
1418 hr = XmlNextElement(pixnNodes, &pixnElement, NULL);
1419 ExitOnFailure(hr, "Failed to get next RelatedBundle element.");
1420
1421 hr = XmlGetAttributeEx(pixnElement, L"Action", &sczAction);
1422 ExitOnFailure(hr, "Failed to get @Action.");
1423
1424 hr = XmlGetAttributeEx(pixnElement, L"Id", &sczId);
1425 ExitOnFailure(hr, "Failed to get @Id.");
1426
1427 if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, sczAction, -1, L"Detect", -1))
1428 {
1429 hr = MemEnsureArraySize(reinterpret_cast<LPVOID*>(&pRegistration->rgsczDetectCodes), pRegistration->cDetectCodes + 1, sizeof(LPWSTR), 5);
1430 ExitOnFailure(hr, "Failed to resize Detect code array in registration");
1431
1432 pRegistration->rgsczDetectCodes[pRegistration->cDetectCodes] = sczId;
1433 sczId = NULL;
1434 ++pRegistration->cDetectCodes;
1435 }
1436 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, sczAction, -1, L"Upgrade", -1))
1437 {
1438 hr = MemEnsureArraySize(reinterpret_cast<LPVOID*>(&pRegistration->rgsczUpgradeCodes), pRegistration->cUpgradeCodes + 1, sizeof(LPWSTR), 5);
1439 ExitOnFailure(hr, "Failed to resize Upgrade code array in registration");
1440
1441 pRegistration->rgsczUpgradeCodes[pRegistration->cUpgradeCodes] = sczId;
1442 sczId = NULL;
1443 ++pRegistration->cUpgradeCodes;
1444 }
1445 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, sczAction, -1, L"Addon", -1))
1446 {
1447 hr = MemEnsureArraySize(reinterpret_cast<LPVOID*>(&pRegistration->rgsczAddonCodes), pRegistration->cAddonCodes + 1, sizeof(LPWSTR), 5);
1448 ExitOnFailure(hr, "Failed to resize Addon code array in registration");
1449
1450 pRegistration->rgsczAddonCodes[pRegistration->cAddonCodes] = sczId;
1451 sczId = NULL;
1452 ++pRegistration->cAddonCodes;
1453 }
1454 else if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, sczAction, -1, L"Patch", -1))
1455 {
1456 hr = MemEnsureArraySize(reinterpret_cast<LPVOID*>(&pRegistration->rgsczPatchCodes), pRegistration->cPatchCodes + 1, sizeof(LPWSTR), 5);
1457 ExitOnFailure(hr, "Failed to resize Patch code array in registration");
1458
1459 pRegistration->rgsczPatchCodes[pRegistration->cPatchCodes] = sczId;
1460 sczId = NULL;
1461 ++pRegistration->cPatchCodes;
1462 }
1463 else
1464 {
1465 hr = E_INVALIDARG;
1466 ExitOnFailure(hr, "Invalid value for @Action: %ls", sczAction);
1467 }
1468 }
1469
1470LExit:
1471 ReleaseObject(pixnNodes);
1472 ReleaseObject(pixnElement);
1473 ReleaseStr(sczAction);
1474 ReleaseStr(sczId);
1475
1476 return hr;
1477}
1478
1479static HRESULT FormatUpdateRegistrationKey( 1394static HRESULT FormatUpdateRegistrationKey(
1480 __in BURN_REGISTRATION* pRegistration, 1395 __in BURN_REGISTRATION* pRegistration,
1481 __out_z LPWSTR* psczKey 1396 __out_z LPWSTR* psczKey
diff --git a/src/burn/engine/relatedbundle.cpp b/src/burn/engine/relatedbundle.cpp
index 58911711..586446b1 100644
--- a/src/burn/engine/relatedbundle.cpp
+++ b/src/burn/engine/relatedbundle.cpp
@@ -143,6 +143,30 @@ extern "C" void RelatedBundlesSortPlan(
143 qsort_s(pRelatedBundles->rgpPlanSortedRelatedBundles, pRelatedBundles->cRelatedBundles, sizeof(BURN_RELATED_BUNDLE*), CompareRelatedBundlesPlan, NULL); 143 qsort_s(pRelatedBundles->rgpPlanSortedRelatedBundles, pRelatedBundles->cRelatedBundles, sizeof(BURN_RELATED_BUNDLE*), CompareRelatedBundlesPlan, NULL);
144} 144}
145 145
146extern "C" BOOTSTRAPPER_RELATION_TYPE RelatedBundleConvertRelationType(
147 __in BUNDLE_RELATION_TYPE relationType
148 )
149{
150 switch (relationType)
151 {
152 case BUNDLE_RELATION_DETECT:
153 return BOOTSTRAPPER_RELATION_DETECT;
154 case BUNDLE_RELATION_UPGRADE:
155 return BOOTSTRAPPER_RELATION_UPGRADE;
156 case BUNDLE_RELATION_ADDON:
157 return BOOTSTRAPPER_RELATION_ADDON;
158 case BUNDLE_RELATION_PATCH:
159 return BOOTSTRAPPER_RELATION_PATCH;
160 case BUNDLE_RELATION_DEPENDENT_ADDON:
161 return BOOTSTRAPPER_RELATION_DEPENDENT_ADDON;
162 case BUNDLE_RELATION_DEPENDENT_PATCH:
163 return BOOTSTRAPPER_RELATION_DEPENDENT_PATCH;
164 default:
165 AssertSz(BUNDLE_RELATION_NONE == relationType, "Unknown BUNDLE_RELATION_TYPE");
166 return BOOTSTRAPPER_RELATION_NONE;
167 }
168}
169
146 170
147// internal helper functions 171// internal helper functions
148 172
@@ -248,30 +272,6 @@ LExit:
248 return result; 272 return result;
249} 273}
250 274
251static BOOTSTRAPPER_RELATION_TYPE ConvertRelationType(
252 __in BUNDLE_RELATION_TYPE relationType
253 )
254{
255 switch (relationType)
256 {
257 case BUNDLE_RELATION_DETECT:
258 return BOOTSTRAPPER_RELATION_DETECT;
259 case BUNDLE_RELATION_UPGRADE:
260 return BOOTSTRAPPER_RELATION_UPGRADE;
261 case BUNDLE_RELATION_ADDON:
262 return BOOTSTRAPPER_RELATION_ADDON;
263 case BUNDLE_RELATION_PATCH:
264 return BOOTSTRAPPER_RELATION_PATCH;
265 case BUNDLE_RELATION_DEPENDENT_ADDON:
266 return BOOTSTRAPPER_RELATION_DEPENDENT_ADDON;
267 case BUNDLE_RELATION_DEPENDENT_PATCH:
268 return BOOTSTRAPPER_RELATION_DEPENDENT_PATCH;
269 default:
270 AssertSz(BUNDLE_RELATION_NONE == relationType, "Unknown BUNDLE_RELATION_TYPE");
271 return BOOTSTRAPPER_RELATION_NONE;
272 }
273}
274
275static HRESULT LoadIfRelatedBundle( 275static HRESULT LoadIfRelatedBundle(
276 __in const BUNDLE_QUERY_RELATED_BUNDLE_RESULT* pBundle, 276 __in const BUNDLE_QUERY_RELATED_BUNDLE_RESULT* pBundle,
277 __in BURN_REGISTRATION* pRegistration, 277 __in BURN_REGISTRATION* pRegistration,
@@ -280,7 +280,7 @@ static HRESULT LoadIfRelatedBundle(
280{ 280{
281 HRESULT hr = S_OK; 281 HRESULT hr = S_OK;
282 BOOL fPerMachine = BUNDLE_INSTALL_CONTEXT_MACHINE == pBundle->installContext; 282 BOOL fPerMachine = BUNDLE_INSTALL_CONTEXT_MACHINE == pBundle->installContext;
283 BOOTSTRAPPER_RELATION_TYPE relationType = ConvertRelationType(pBundle->relationType); 283 BOOTSTRAPPER_RELATION_TYPE relationType = RelatedBundleConvertRelationType(pBundle->relationType);
284 BURN_RELATED_BUNDLE* pRelatedBundle = NULL; 284 BURN_RELATED_BUNDLE* pRelatedBundle = NULL;
285 285
286 // If we found our bundle id, it's not a related bundle. 286 // If we found our bundle id, it's not a related bundle.
diff --git a/src/burn/engine/relatedbundle.h b/src/burn/engine/relatedbundle.h
index 24469f3d..e98d0ede 100644
--- a/src/burn/engine/relatedbundle.h
+++ b/src/burn/engine/relatedbundle.h
@@ -25,6 +25,9 @@ void RelatedBundlesSortDetect(
25void RelatedBundlesSortPlan( 25void RelatedBundlesSortPlan(
26 __in BURN_RELATED_BUNDLES* pRelatedBundles 26 __in BURN_RELATED_BUNDLES* pRelatedBundles
27 ); 27 );
28BOOTSTRAPPER_RELATION_TYPE RelatedBundleConvertRelationType(
29 __in BUNDLE_RELATION_TYPE relationType
30 );
28 31
29#if defined(__cplusplus) 32#if defined(__cplusplus)
30} 33}
diff --git a/src/burn/engine/userexperience.cpp b/src/burn/engine/userexperience.cpp
index 06f87363..a1a010d2 100644
--- a/src/burn/engine/userexperience.cpp
+++ b/src/burn/engine/userexperience.cpp
@@ -104,7 +104,7 @@ extern "C" HRESULT UserExperienceLoad(
104 args.pCommand = pCommand; 104 args.pCommand = pCommand;
105 args.pfnBootstrapperEngineProc = EngineForApplicationProc; 105 args.pfnBootstrapperEngineProc = EngineForApplicationProc;
106 args.pvBootstrapperEngineProcContext = pEngineContext; 106 args.pvBootstrapperEngineProcContext = pEngineContext;
107 args.qwEngineAPIVersion = MAKEQWORDVERSION(2022, 3, 17, 0); 107 args.qwEngineAPIVersion = MAKEQWORDVERSION(2022, 3, 31, 0);
108 108
109 results.cbSize = sizeof(BOOTSTRAPPER_CREATE_RESULTS); 109 results.cbSize = sizeof(BOOTSTRAPPER_CREATE_RESULTS);
110 110
@@ -1247,6 +1247,40 @@ LExit:
1247 return hr; 1247 return hr;
1248} 1248}
1249 1249
1250EXTERN_C BAAPI UserExperienceOnDetectRelatedBundlePackage(
1251 __in BURN_USER_EXPERIENCE* pUserExperience,
1252 __in_z LPCWSTR wzPackageId,
1253 __in_z LPCWSTR wzBundleId,
1254 __in BOOTSTRAPPER_RELATION_TYPE relationType,
1255 __in BOOL fPerMachine,
1256 __in VERUTIL_VERSION* pVersion
1257 )
1258{
1259 HRESULT hr = S_OK;
1260 BA_ONDETECTRELATEDBUNDLEPACKAGE_ARGS args = { };
1261 BA_ONDETECTRELATEDBUNDLEPACKAGE_RESULTS results = { };
1262
1263 args.cbSize = sizeof(args);
1264 args.wzPackageId = wzPackageId;
1265 args.wzBundleId = wzBundleId;
1266 args.relationType = relationType;
1267 args.fPerMachine = fPerMachine;
1268 args.wzVersion = pVersion->sczVersion;
1269
1270 results.cbSize = sizeof(results);
1271
1272 hr = SendBAMessage(pUserExperience, BOOTSTRAPPER_APPLICATION_MESSAGE_ONDETECTRELATEDBUNDLEPACKAGE, &args, &results);
1273 ExitOnFailure(hr, "BA OnDetectRelatedBundlePackage failed.");
1274
1275 if (results.fCancel)
1276 {
1277 hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT);
1278 }
1279
1280LExit:
1281 return hr;
1282}
1283
1250EXTERN_C BAAPI UserExperienceOnDetectRelatedMsiPackage( 1284EXTERN_C BAAPI UserExperienceOnDetectRelatedMsiPackage(
1251 __in BURN_USER_EXPERIENCE* pUserExperience, 1285 __in BURN_USER_EXPERIENCE* pUserExperience,
1252 __in_z LPCWSTR wzPackageId, 1286 __in_z LPCWSTR wzPackageId,
diff --git a/src/burn/engine/userexperience.h b/src/burn/engine/userexperience.h
index de558ad5..90a047ed 100644
--- a/src/burn/engine/userexperience.h
+++ b/src/burn/engine/userexperience.h
@@ -298,6 +298,14 @@ BAAPI UserExperienceOnDetectRelatedBundle(
298 __in VERUTIL_VERSION* pVersion, 298 __in VERUTIL_VERSION* pVersion,
299 __in BOOL fMissingFromCache 299 __in BOOL fMissingFromCache
300 ); 300 );
301BAAPI UserExperienceOnDetectRelatedBundlePackage(
302 __in BURN_USER_EXPERIENCE* pUserExperience,
303 __in_z LPCWSTR wzPackageId,
304 __in_z LPCWSTR wzBundleId,
305 __in BOOTSTRAPPER_RELATION_TYPE relationType,
306 __in BOOL fPerMachine,
307 __in VERUTIL_VERSION* pVersion
308 );
301BAAPI UserExperienceOnDetectRelatedMsiPackage( 309BAAPI UserExperienceOnDetectRelatedMsiPackage(
302 __in BURN_USER_EXPERIENCE* pUserExperience, 310 __in BURN_USER_EXPERIENCE* pUserExperience,
303 __in_z LPCWSTR wzPackageId, 311 __in_z LPCWSTR wzPackageId,
diff --git a/src/burn/test/BurnUnitTest/TestData/PlanTest/BundlePackage_Multiple_manifest.xml b/src/burn/test/BurnUnitTest/TestData/PlanTest/BundlePackage_Multiple_manifest.xml
index 6c60085f..0b521faa 100644
--- a/src/burn/test/BurnUnitTest/TestData/PlanTest/BundlePackage_Multiple_manifest.xml
+++ b/src/burn/test/BurnUnitTest/TestData/PlanTest/BundlePackage_Multiple_manifest.xml
@@ -1 +1 @@
<?xml version="1.0" encoding="utf-8"?><BurnManifest xmlns="http://wixtoolset.org/schemas/v4/2008/Burn"><Log PathVariable="WixBundleLog" Prefix="~BundlePackageTests_MultipleBundlePackagesBundle" Extension=".log" /><RelatedBundle Id="{86D214FB-8D74-456C-99B3-6557ECA6159C}" Action="Upgrade" /><Variable Id="TestGroupName" Value="BundlePackageTests" Type="string" Hidden="no" Persisted="no" /><Variable Id="WixBundleInProgressName" Hidden="no" Persisted="yes" /><Variable Id="WixBundleName" Hidden="no" Persisted="yes" /><Variable Id="WixBundleOriginalSource" Hidden="no" Persisted="yes" /><Variable Id="WixBundleOriginalSourceFolder" Hidden="no" Persisted="yes" /><Variable Id="WixBundleLastUsedSource" Hidden="no" Persisted="yes" /><RegistrySearch Id="NETFRAMEWORK45" Variable="NETFRAMEWORK45" Root="HKLM" Key="SOFTWARE\Microsoft\NET Framework Setup\NDP\v4\Full" Value="Release" Type="value" VariableType="string" /><UX><Payload Id="WixManagedBootstrapperApplicationHost" FilePath="mbahost.dll" SourcePath="u30" /><Payload Id="payO60IVK4ATGzPpMz3rwVbUWl6DyU" FilePath="WixToolset.Mba.Host.config" SourcePath="u0" /><Payload Id="payxj4zDAKL2NVlz4ohp0GvwFHepyI" FilePath="TestBA.dll" SourcePath="u1" /><Payload Id="pay1hOSAUC8_D633cD2TXpIXCL30OU" FilePath="mbanative.dll" SourcePath="u2" /><Payload Id="payujy6Izl_BlUNfHt2eI.ADfjYAv4" FilePath="WixToolset.Mba.Core.dll" SourcePath="u3" /><Payload Id="payR4EbR4OTDZpPEycWaSSM_gZRBWM" FilePath="mbapreq.thm" SourcePath="u4" /><Payload Id="paylVCy2Ecl8pHPdJTCQZryUG4T9us" FilePath="mbapreq.png" SourcePath="u5" /><Payload Id="payTaG4B_lob1aLcKFaOqSSG3MPMpU" FilePath="mbapreq.wxl" SourcePath="u6" /><Payload Id="payZwIGuiezVTitZOoZKxyh2DdRSGs" FilePath="1028\mbapreq.wxl" SourcePath="u7" /><Payload Id="pay.herBWX.LlOh8jLsx24aWdunV_0" FilePath="1029\mbapreq.wxl" SourcePath="u8" /><Payload Id="pay8DkMszYsoxxdgX14huLDMYXylQg" FilePath="1030\mbapreq.wxl" SourcePath="u9" /><Payload Id="payPaHpoTeOdkW.TK99IDwktNLhTAg" FilePath="1031\mbapreq.wxl" SourcePath="u10" /><Payload Id="pay45AtAzterLTMzZgdxxtuYvaiXwU" FilePath="1032\mbapreq.wxl" SourcePath="u11" /><Payload Id="payA2VEKIqhePyNIEmr14eyH3JoVLc" FilePath="1035\mbapreq.wxl" SourcePath="u12" /><Payload Id="payvre23ObscjzhcaFIifUAkXMdPa8" FilePath="1036\mbapreq.wxl" SourcePath="u13" /><Payload Id="paytxUV3vuBbG2c.a9c.d_sZX2x6wA" FilePath="1038\mbapreq.wxl" SourcePath="u14" /><Payload Id="payYvMWRK9xelo5.sQn7jRkJIaBp9A" FilePath="1040\mbapreq.wxl" SourcePath="u15" /><Payload Id="pay68KKSApyQimbA25t6kSbqhdeH10" FilePath="1041\mbapreq.wxl" SourcePath="u16" /><Payload Id="paypiqxaHpYZqx.9eDVjQrj1igLbRY" FilePath="1042\mbapreq.wxl" SourcePath="u17" /><Payload Id="payTO0YwZzxKpbqdrBVUcVRTu3BFe8" FilePath="1043\mbapreq.wxl" SourcePath="u18" /><Payload Id="payIXg2ldBJukRzhqWolJVOEbTmF34" FilePath="1044\mbapreq.wxl" SourcePath="u19" /><Payload Id="payOHIZbSkIvrpwKkkXI173tv3u3B4" FilePath="1045\mbapreq.wxl" SourcePath="u20" /><Payload Id="payQRQ_UZl_R2UtV0xDXB2yeH2bg3E" FilePath="1046\mbapreq.wxl" SourcePath="u21" /><Payload Id="payhrejLLBfc1i27iN._QPhQ4K337I" FilePath="1049\mbapreq.wxl" SourcePath="u22" /><Payload Id="payqEzaDNzxB68vGp29jgDcCos6dvg" FilePath="1051\mbapreq.wxl" SourcePath="u23" /><Payload Id="paydz8Vk8xSTyYohgGXTSIxWGXL5.Q" FilePath="1053\mbapreq.wxl" SourcePath="u24" /><Payload Id="pay0HRUZTlbC3taSOffJBsEj92Br8Y" FilePath="1055\mbapreq.wxl" SourcePath="u25" /><Payload Id="payIvUOkc_EMH7laMFehefNolV8hZo" FilePath="1060\mbapreq.wxl" SourcePath="u26" /><Payload Id="payLFhOb.rHuk4sW5CYAPMShG0NjGI" FilePath="2052\mbapreq.wxl" SourcePath="u27" /><Payload Id="payqIKCmERK7Nhxx_nNXvRxdKqKDbI" FilePath="2070\mbapreq.wxl" SourcePath="u28" /><Payload Id="payqeWUzIVaEqjuRXN0z8ECC3Y4tCc" FilePath="3082\mbapreq.wxl" SourcePath="u29" /><Payload Id="paylfeHEjJSSTnNzY9QMZM2Ye3Ipy4" FilePath="mbapreq.dll" SourcePath="u31" /><Payload Id="payDPxs6uy8nbky.R7zhir2RRAfc.c" FilePath="WixToolset.Mba.Host.dll" SourcePath="u32" /><Payload Id="uxTxMXPVMXwQrPTMIGa5WGt93w0Ns" FilePath="BootstrapperApplicationData.xml" SourcePath="u33" /><Payload Id="uxYRbgitOs0K878jn5L_z7LdJ21KI" FilePath="BundleExtensionData.xml" SourcePath="u34" /></UX><Container Id="WixAttachedContainer" FileSize="15696370" Hash="0F9966B421400E481D394DB4C4D7F0F92548E5BEB79B98880C926E817E8C1F381EC8A17053E2E66AE7132A3C9ECE441629E6A1FB3452C5C9282280C40252F141" FilePath="MultipleBundlePackagesBundle.exe" AttachedIndex="1" Attached="yes" Primary="yes" /><Payload Id="NetFx48Web" FilePath="redist\ndp48-web.exe" FileSize="1439328" CertificateRootPublicKeyIdentifier="F49F9B33E25E33CCA0BFB15A62B7C29FFAB3880B" CertificateRootThumbprint="ABDCA79AF9DD48A0EA702AD45260B3C03093FB4B" DownloadUrl="https://go.microsoft.com/fwlink/?LinkId=2085155" Packaging="external" SourcePath="redist\ndp48-web.exe" /><Payload Id="PackageA" FilePath="BundleA.exe" FileSize="5241635" Hash="20E1AFF76DE4693CB2876DC6BCCA0152DB16BE49AEDE2CD581C03FC39AB89DEA12BC25CB435F06E4D7D2B4443CE8A8935D5E92E2E49A4981B60A273980E4B29B" Packaging="embedded" SourcePath="a0" Container="WixAttachedContainer" /><Payload Id="PackageB" FilePath="BundleB_x64.exe" FileSize="10450821" Hash="43A58873D61D6E0FA83F6C5266F2F05FEA9BC85D11C195493B7FD9F0B4AA799C1EFCB78D76DCED32124D2EC62A4E7114B62CDE6F0B87E42A7E28CDBB0DA0FF8E" Packaging="embedded" SourcePath="a1" Container="WixAttachedContainer" /><RollbackBoundary Id="WixDefaultBoundary" Vital="yes" Transaction="no" /><Registration Id="{35192ED0-C70A-49B2-9D12-3B1FA39B5E6F}" ExecutableName="MultipleBundlePackagesBundle.exe" PerMachine="yes" Tag="" Version="1.0.0.0" ProviderKey="{35192ED0-C70A-49B2-9D12-3B1FA39B5E6F}"><Arp Register="yes" DisplayName="~BundlePackageTests - MultipleBundlePackagesBundle" DisplayVersion="1.0.0.0" /></Registration><Chain><ExePackage Id="NetFx48Web" Cache="remove" CacheId="642721C60D52051C7F3434D8710FE3406A7CFE10B2B39E90EA847719ED1697D7C614F2DF44AD50412B1DF8C98DD78FDC57CA1D047D28C81AC158092E5FB18040" InstallSize="1439328" Size="1439328" PerMachine="yes" Permanent="yes" Vital="yes" RollbackBoundaryForward="WixDefaultBoundary" LogPathVariable="NetFx48WebLog" RollbackLogPathVariable="WixBundleRollbackLog_NetFx48Web" DetectCondition="NETFRAMEWORK45 &gt;= 528040" InstallArguments="/q /norestart /log &quot;[NetFx48WebLog].html&quot;" UninstallArguments="" Uninstallable="no" RepairArguments="" Repairable="no" Protocol="netfx4"><PayloadRef Id="NetFx48Web" /></ExePackage><BundlePackage Id="PackageA" Cache="keep" CacheId="{B39CEE4D-CCD7-4797-BE3A-6613BD1DC4BE}v1.0.0.0" InstallSize="2169" Size="5241635" PerMachine="yes" Permanent="no" Vital="yes" LogPathVariable="WixBundleLog_PackageA" RollbackLogPathVariable="WixBundleRollbackLog_PackageA" BundleId="{B39CEE4D-CCD7-4797-BE3A-6613BD1DC4BE}" InstallArguments="" UninstallArguments="" RepairArguments="" SupportsBurnProtocol="yes" Win64="no"><Provides Key="{B39CEE4D-CCD7-4797-BE3A-6613BD1DC4BE}" Version="1.0.0.0" DisplayName="~BasicFunctionalityTests - BundleA" Imported="yes" /><PayloadRef Id="PackageA" /></BundlePackage><BundlePackage Id="PackageB" Cache="keep" CacheId="{7506235A-7C59-4750-82C7-EB460A87ED3A}v1.0.0.0" InstallSize="1441497" Size="10450821" PerMachine="yes" Permanent="no" Vital="yes" RollbackBoundaryBackward="WixDefaultBoundary" LogPathVariable="WixBundleLog_PackageB" RollbackLogPathVariable="WixBundleRollbackLog_PackageB" BundleId="{7506235A-7C59-4750-82C7-EB460A87ED3A}" InstallArguments="" UninstallArguments="" RepairArguments="" SupportsBurnProtocol="yes" Win64="yes"><Provides Key="{7506235A-7C59-4750-82C7-EB460A87ED3A}" Version="1.0.0.0" DisplayName="~BasicFunctionalityTests - BundleB_x64" Imported="yes" /><PayloadRef Id="PackageB" /></BundlePackage></Chain><CommandLine Variables="upperCase" /></BurnManifest> \ No newline at end of file <?xml version="1.0" encoding="utf-8"?><BurnManifest xmlns="http://wixtoolset.org/schemas/v4/2008/Burn"><Log PathVariable="WixBundleLog" Prefix="~BundlePackageTests_MultipleBundlePackagesBundle" Extension=".log" /><RelatedBundle Id="{86D214FB-8D74-456C-99B3-6557ECA6159C}" Action="Upgrade" /><Variable Id="TestGroupName" Value="BundlePackageTests" Type="string" Hidden="no" Persisted="no" /><Variable Id="WixBundleInProgressName" Hidden="no" Persisted="yes" /><Variable Id="WixBundleName" Hidden="no" Persisted="yes" /><Variable Id="WixBundleOriginalSource" Hidden="no" Persisted="yes" /><Variable Id="WixBundleOriginalSourceFolder" Hidden="no" Persisted="yes" /><Variable Id="WixBundleLastUsedSource" Hidden="no" Persisted="yes" /><RegistrySearch Id="NETFRAMEWORK45" Variable="NETFRAMEWORK45" Root="HKLM" Key="SOFTWARE\Microsoft\NET Framework Setup\NDP\v4\Full" Value="Release" Type="value" VariableType="string" /><UX><Payload Id="WixManagedBootstrapperApplicationHost" FilePath="mbahost.dll" SourcePath="u30" /><Payload Id="payO60IVK4ATGzPpMz3rwVbUWl6DyU" FilePath="WixToolset.Mba.Host.config" SourcePath="u0" /><Payload Id="payxj4zDAKL2NVlz4ohp0GvwFHepyI" FilePath="TestBA.dll" SourcePath="u1" /><Payload Id="pay1hOSAUC8_D633cD2TXpIXCL30OU" FilePath="mbanative.dll" SourcePath="u2" /><Payload Id="payujy6Izl_BlUNfHt2eI.ADfjYAv4" FilePath="WixToolset.Mba.Core.dll" SourcePath="u3" /><Payload Id="payR4EbR4OTDZpPEycWaSSM_gZRBWM" FilePath="mbapreq.thm" SourcePath="u4" /><Payload Id="paylVCy2Ecl8pHPdJTCQZryUG4T9us" FilePath="mbapreq.png" SourcePath="u5" /><Payload Id="payTaG4B_lob1aLcKFaOqSSG3MPMpU" FilePath="mbapreq.wxl" SourcePath="u6" /><Payload Id="payZwIGuiezVTitZOoZKxyh2DdRSGs" FilePath="1028\mbapreq.wxl" SourcePath="u7" /><Payload Id="pay.herBWX.LlOh8jLsx24aWdunV_0" FilePath="1029\mbapreq.wxl" SourcePath="u8" /><Payload Id="pay8DkMszYsoxxdgX14huLDMYXylQg" FilePath="1030\mbapreq.wxl" SourcePath="u9" /><Payload Id="payPaHpoTeOdkW.TK99IDwktNLhTAg" FilePath="1031\mbapreq.wxl" SourcePath="u10" /><Payload Id="pay45AtAzterLTMzZgdxxtuYvaiXwU" FilePath="1032\mbapreq.wxl" SourcePath="u11" /><Payload Id="payA2VEKIqhePyNIEmr14eyH3JoVLc" FilePath="1035\mbapreq.wxl" SourcePath="u12" /><Payload Id="payvre23ObscjzhcaFIifUAkXMdPa8" FilePath="1036\mbapreq.wxl" SourcePath="u13" /><Payload Id="paytxUV3vuBbG2c.a9c.d_sZX2x6wA" FilePath="1038\mbapreq.wxl" SourcePath="u14" /><Payload Id="payYvMWRK9xelo5.sQn7jRkJIaBp9A" FilePath="1040\mbapreq.wxl" SourcePath="u15" /><Payload Id="pay68KKSApyQimbA25t6kSbqhdeH10" FilePath="1041\mbapreq.wxl" SourcePath="u16" /><Payload Id="paypiqxaHpYZqx.9eDVjQrj1igLbRY" FilePath="1042\mbapreq.wxl" SourcePath="u17" /><Payload Id="payTO0YwZzxKpbqdrBVUcVRTu3BFe8" FilePath="1043\mbapreq.wxl" SourcePath="u18" /><Payload Id="payIXg2ldBJukRzhqWolJVOEbTmF34" FilePath="1044\mbapreq.wxl" SourcePath="u19" /><Payload Id="payOHIZbSkIvrpwKkkXI173tv3u3B4" FilePath="1045\mbapreq.wxl" SourcePath="u20" /><Payload Id="payQRQ_UZl_R2UtV0xDXB2yeH2bg3E" FilePath="1046\mbapreq.wxl" SourcePath="u21" /><Payload Id="payhrejLLBfc1i27iN._QPhQ4K337I" FilePath="1049\mbapreq.wxl" SourcePath="u22" /><Payload Id="payqEzaDNzxB68vGp29jgDcCos6dvg" FilePath="1051\mbapreq.wxl" SourcePath="u23" /><Payload Id="paydz8Vk8xSTyYohgGXTSIxWGXL5.Q" FilePath="1053\mbapreq.wxl" SourcePath="u24" /><Payload Id="pay0HRUZTlbC3taSOffJBsEj92Br8Y" FilePath="1055\mbapreq.wxl" SourcePath="u25" /><Payload Id="payIvUOkc_EMH7laMFehefNolV8hZo" FilePath="1060\mbapreq.wxl" SourcePath="u26" /><Payload Id="payLFhOb.rHuk4sW5CYAPMShG0NjGI" FilePath="2052\mbapreq.wxl" SourcePath="u27" /><Payload Id="payqIKCmERK7Nhxx_nNXvRxdKqKDbI" FilePath="2070\mbapreq.wxl" SourcePath="u28" /><Payload Id="payqeWUzIVaEqjuRXN0z8ECC3Y4tCc" FilePath="3082\mbapreq.wxl" SourcePath="u29" /><Payload Id="paylfeHEjJSSTnNzY9QMZM2Ye3Ipy4" FilePath="mbapreq.dll" SourcePath="u31" /><Payload Id="payDPxs6uy8nbky.R7zhir2RRAfc.c" FilePath="WixToolset.Mba.Host.dll" SourcePath="u32" /><Payload Id="uxTxMXPVMXwQrPTMIGa5WGt93w0Ns" FilePath="BootstrapperApplicationData.xml" SourcePath="u33" /><Payload Id="uxYRbgitOs0K878jn5L_z7LdJ21KI" FilePath="BundleExtensionData.xml" SourcePath="u34" /></UX><Container Id="WixAttachedContainer" FileSize="15696370" Hash="0F9966B421400E481D394DB4C4D7F0F92548E5BEB79B98880C926E817E8C1F381EC8A17053E2E66AE7132A3C9ECE441629E6A1FB3452C5C9282280C40252F141" FilePath="MultipleBundlePackagesBundle.exe" AttachedIndex="1" Attached="yes" Primary="yes" /><Payload Id="NetFx48Web" FilePath="redist\ndp48-web.exe" FileSize="1439328" CertificateRootPublicKeyIdentifier="F49F9B33E25E33CCA0BFB15A62B7C29FFAB3880B" CertificateRootThumbprint="ABDCA79AF9DD48A0EA702AD45260B3C03093FB4B" DownloadUrl="https://go.microsoft.com/fwlink/?LinkId=2085155" Packaging="external" SourcePath="redist\ndp48-web.exe" /><Payload Id="PackageA" FilePath="BundleA.exe" FileSize="5241635" Hash="20E1AFF76DE4693CB2876DC6BCCA0152DB16BE49AEDE2CD581C03FC39AB89DEA12BC25CB435F06E4D7D2B4443CE8A8935D5E92E2E49A4981B60A273980E4B29B" Packaging="embedded" SourcePath="a0" Container="WixAttachedContainer" /><Payload Id="PackageB" FilePath="BundleB_x64.exe" FileSize="10450821" Hash="43A58873D61D6E0FA83F6C5266F2F05FEA9BC85D11C195493B7FD9F0B4AA799C1EFCB78D76DCED32124D2EC62A4E7114B62CDE6F0B87E42A7E28CDBB0DA0FF8E" Packaging="embedded" SourcePath="a1" Container="WixAttachedContainer" /><RollbackBoundary Id="WixDefaultBoundary" Vital="yes" Transaction="no" /><Registration Id="{35192ED0-C70A-49B2-9D12-3B1FA39B5E6F}" ExecutableName="MultipleBundlePackagesBundle.exe" PerMachine="yes" Tag="" Version="1.0.0.0" ProviderKey="{35192ED0-C70A-49B2-9D12-3B1FA39B5E6F}"><Arp Register="yes" DisplayName="~BundlePackageTests - MultipleBundlePackagesBundle" DisplayVersion="1.0.0.0" /></Registration><Chain><ExePackage Id="NetFx48Web" Cache="remove" CacheId="642721C60D52051C7F3434D8710FE3406A7CFE10B2B39E90EA847719ED1697D7C614F2DF44AD50412B1DF8C98DD78FDC57CA1D047D28C81AC158092E5FB18040" InstallSize="1439328" Size="1439328" PerMachine="yes" Permanent="yes" Vital="yes" RollbackBoundaryForward="WixDefaultBoundary" LogPathVariable="NetFx48WebLog" RollbackLogPathVariable="WixBundleRollbackLog_NetFx48Web" DetectCondition="NETFRAMEWORK45 &gt;= 528040" InstallArguments="/q /norestart /log &quot;[NetFx48WebLog].html&quot;" UninstallArguments="" Uninstallable="no" RepairArguments="" Repairable="no" Protocol="netfx4"><PayloadRef Id="NetFx48Web" /></ExePackage><BundlePackage Id="PackageA" Cache="keep" CacheId="{B39CEE4D-CCD7-4797-BE3A-6613BD1DC4BE}v1.0.0.0" InstallSize="2169" Size="5241635" Version="1.0.0.0" PerMachine="yes" Permanent="no" Vital="yes" LogPathVariable="WixBundleLog_PackageA" RollbackLogPathVariable="WixBundleRollbackLog_PackageA" BundleId="{B39CEE4D-CCD7-4797-BE3A-6613BD1DC4BE}" InstallArguments="" UninstallArguments="" RepairArguments="" SupportsBurnProtocol="yes" Win64="no"><Provides Key="{B39CEE4D-CCD7-4797-BE3A-6613BD1DC4BE}" Version="1.0.0.0" DisplayName="~BasicFunctionalityTests - BundleA" Imported="yes" /><RelatedBundle Id="{8C7E2C47-1EE7-4BBE-99A2-EAB7F3693F48}" Action="Upgrade" /><PayloadRef Id="PackageA" /></BundlePackage><BundlePackage Id="PackageB" Cache="keep" CacheId="{7506235A-7C59-4750-82C7-EB460A87ED3A}v1.0.0.0" InstallSize="1441497" Size="10450821" Version="1.0.0.0" PerMachine="yes" Permanent="no" Vital="yes" RollbackBoundaryBackward="WixDefaultBoundary" LogPathVariable="WixBundleLog_PackageB" RollbackLogPathVariable="WixBundleRollbackLog_PackageB" BundleId="{7506235A-7C59-4750-82C7-EB460A87ED3A}" InstallArguments="" UninstallArguments="" RepairArguments="" SupportsBurnProtocol="yes" Win64="yes"><Provides Key="{7506235A-7C59-4750-82C7-EB460A87ED3A}" Version="1.0.0.0" DisplayName="~BasicFunctionalityTests - BundleB_x64" Imported="yes" /><RelatedBundle Id="{79F45B7A-D990-46E4-819B-078D87C3321A}" Action="Upgrade" /><PayloadRef Id="PackageB" /></BundlePackage></Chain><CommandLine Variables="upperCase" /></BurnManifest>
diff --git a/src/ext/Bal/wixstdba/WixStandardBootstrapperApplication.cpp b/src/ext/Bal/wixstdba/WixStandardBootstrapperApplication.cpp
index 444a917f..969aca9d 100644
--- a/src/ext/Bal/wixstdba/WixStandardBootstrapperApplication.cpp
+++ b/src/ext/Bal/wixstdba/WixStandardBootstrapperApplication.cpp
@@ -1489,6 +1489,9 @@ public: // IBootstrapperApplication
1489 case BOOTSTRAPPER_APPLICATION_MESSAGE_ONAPPLYDOWNGRADE: 1489 case BOOTSTRAPPER_APPLICATION_MESSAGE_ONAPPLYDOWNGRADE:
1490 OnApplyDowngradeFallback(reinterpret_cast<BA_ONAPPLYDOWNGRADE_ARGS*>(pvArgs), reinterpret_cast<BA_ONAPPLYDOWNGRADE_RESULTS*>(pvResults)); 1490 OnApplyDowngradeFallback(reinterpret_cast<BA_ONAPPLYDOWNGRADE_ARGS*>(pvArgs), reinterpret_cast<BA_ONAPPLYDOWNGRADE_RESULTS*>(pvResults));
1491 break; 1491 break;
1492 case BOOTSTRAPPER_APPLICATION_MESSAGE_ONDETECTRELATEDBUNDLEPACKAGE:
1493 OnDetectRelatedBundlePackageFallback(reinterpret_cast<BA_ONDETECTRELATEDBUNDLEPACKAGE_ARGS*>(pvArgs), reinterpret_cast<BA_ONDETECTRELATEDBUNDLEPACKAGE_RESULTS*>(pvResults));
1494 break;
1492 default: 1495 default:
1493#ifdef DEBUG 1496#ifdef DEBUG
1494 BalLog(BOOTSTRAPPER_LOG_LEVEL_STANDARD, "WIXSTDBA: Forwarding unknown BA message: %d", message); 1497 BalLog(BOOTSTRAPPER_LOG_LEVEL_STANDARD, "WIXSTDBA: Forwarding unknown BA message: %d", message);
@@ -2178,6 +2181,14 @@ private: // privates
2178 m_pfnBAFunctionsProc(BA_FUNCTIONS_MESSAGE_ONEXECUTEPROCESSCANCEL, pArgs, pResults, m_pvBAFunctionsProcContext); 2181 m_pfnBAFunctionsProc(BA_FUNCTIONS_MESSAGE_ONEXECUTEPROCESSCANCEL, pArgs, pResults, m_pvBAFunctionsProcContext);
2179 } 2182 }
2180 2183
2184 void OnDetectRelatedBundlePackageFallback(
2185 __in BA_ONDETECTRELATEDBUNDLEPACKAGE_ARGS* pArgs,
2186 __inout BA_ONDETECTRELATEDBUNDLEPACKAGE_RESULTS* pResults
2187 )
2188 {
2189 m_pfnBAFunctionsProc(BA_FUNCTIONS_MESSAGE_ONDETECTRELATEDBUNDLEPACKAGE, pArgs, pResults, m_pvBAFunctionsProcContext);
2190 }
2191
2181 2192
2182public: //CBalBaseBootstrapperApplication 2193public: //CBalBaseBootstrapperApplication
2183 virtual STDMETHODIMP Initialize( 2194 virtual STDMETHODIMP Initialize(
diff --git a/src/test/burn/TestData/BundlePackageTests/UpgradeBundlePackageBundlev1/UpgradeBundlePackageBundle.props b/src/test/burn/TestData/BundlePackageTests/UpgradeBundlePackageBundlev1/UpgradeBundlePackageBundle.props
new file mode 100644
index 00000000..1c761dfc
--- /dev/null
+++ b/src/test/burn/TestData/BundlePackageTests/UpgradeBundlePackageBundlev1/UpgradeBundlePackageBundle.props
@@ -0,0 +1,14 @@
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 <PropertyGroup>
4 <OutputType>Bundle</OutputType>
5 <BA>hyperlinkLicense</BA>
6 <UpgradeCode>{B9636854-B76B-4171-B63D-7C659F61DE3D}</UpgradeCode>
7 </PropertyGroup>
8 <ItemGroup>
9 <Compile Include="..\..\Templates\Bundle.wxs" Link="Bundle.wxs" />
10 </ItemGroup>
11 <ItemGroup>
12 <PackageReference Include="WixToolset.Bal.wixext" />
13 </ItemGroup>
14</Project>
diff --git a/src/test/burn/TestData/BundlePackageTests/UpgradeBundlePackageBundlev1/UpgradeBundlePackageBundlev1.wixproj b/src/test/burn/TestData/BundlePackageTests/UpgradeBundlePackageBundlev1/UpgradeBundlePackageBundlev1.wixproj
new file mode 100644
index 00000000..e1e0d601
--- /dev/null
+++ b/src/test/burn/TestData/BundlePackageTests/UpgradeBundlePackageBundlev1/UpgradeBundlePackageBundlev1.wixproj
@@ -0,0 +1,7 @@
1<!-- Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. -->
2<Project Sdk="WixToolset.Sdk">
3 <Import Project="UpgradeBundlePackageBundle.props" />
4 <ItemGroup>
5 <ProjectReference Include="..\..\UpgradeRelatedBundleTests\BundleAv1\BundleAv1.wixproj" />
6 </ItemGroup>
7</Project> \ No newline at end of file
diff --git a/src/test/burn/TestData/BundlePackageTests/UpgradeBundlePackageBundlev1/UpgradeBundlePackageBundlev1.wxs b/src/test/burn/TestData/BundlePackageTests/UpgradeBundlePackageBundlev1/UpgradeBundlePackageBundlev1.wxs
new file mode 100644
index 00000000..ae70ebdb
--- /dev/null
+++ b/src/test/burn/TestData/BundlePackageTests/UpgradeBundlePackageBundlev1/UpgradeBundlePackageBundlev1.wxs
@@ -0,0 +1,10 @@
1<!-- Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. -->
2
3
4<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs">
5 <Fragment>
6 <PackageGroup Id="BundlePackages">
7 <BundlePackage Id="PackageA" SourceFile="$(var.BundleAv1.TargetPath)" />
8 </PackageGroup>
9 </Fragment>
10</Wix>
diff --git a/src/test/burn/TestData/BundlePackageTests/UpgradeBundlePackageBundlev2/UpgradeBundlePackageBundlev2.wixproj b/src/test/burn/TestData/BundlePackageTests/UpgradeBundlePackageBundlev2/UpgradeBundlePackageBundlev2.wixproj
new file mode 100644
index 00000000..61247a66
--- /dev/null
+++ b/src/test/burn/TestData/BundlePackageTests/UpgradeBundlePackageBundlev2/UpgradeBundlePackageBundlev2.wixproj
@@ -0,0 +1,7 @@
1<!-- Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. -->
2<Project Sdk="WixToolset.Sdk">
3 <Import Project="..\UpgradeBundlePackageBundlev1\UpgradeBundlePackageBundle.props" />
4 <ItemGroup>
5 <ProjectReference Include="..\..\UpgradeRelatedBundleTests\BundleAv2\BundleAv2.wixproj" />
6 </ItemGroup>
7</Project> \ No newline at end of file
diff --git a/src/test/burn/TestData/BundlePackageTests/UpgradeBundlePackageBundlev2/UpgradeBundlePackageBundlev2.wxs b/src/test/burn/TestData/BundlePackageTests/UpgradeBundlePackageBundlev2/UpgradeBundlePackageBundlev2.wxs
new file mode 100644
index 00000000..742881c7
--- /dev/null
+++ b/src/test/burn/TestData/BundlePackageTests/UpgradeBundlePackageBundlev2/UpgradeBundlePackageBundlev2.wxs
@@ -0,0 +1,10 @@
1<!-- Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. -->
2
3
4<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs">
5 <Fragment>
6 <PackageGroup Id="BundlePackages">
7 <BundlePackage Id="PackageA" SourceFile="$(var.BundleAv2.TargetPath)" />
8 </PackageGroup>
9 </Fragment>
10</Wix>
diff --git a/src/test/burn/WixToolsetTest.BurnE2E/BundlePackageTests.cs b/src/test/burn/WixToolsetTest.BurnE2E/BundlePackageTests.cs
index 2e95aedb..1e6cda9c 100644
--- a/src/test/burn/WixToolsetTest.BurnE2E/BundlePackageTests.cs
+++ b/src/test/burn/WixToolsetTest.BurnE2E/BundlePackageTests.cs
@@ -45,7 +45,37 @@ namespace WixToolsetTest.BurnE2E
45 // Source file should *not* be installed 45 // Source file should *not* be installed
46 Assert.False(File.Exists(packageA32SourceCodeFilePath), $"PackageA payload should have been removed by uninstall from: {packageA32SourceCodeFilePath}"); 46 Assert.False(File.Exists(packageA32SourceCodeFilePath), $"PackageA payload should have been removed by uninstall from: {packageA32SourceCodeFilePath}");
47 Assert.False(File.Exists(packageA64SourceCodeFilePath), $"PackageA_x64 payload should have been removed by uninstall from: {packageA64SourceCodeFilePath}"); 47 Assert.False(File.Exists(packageA64SourceCodeFilePath), $"PackageA_x64 payload should have been removed by uninstall from: {packageA64SourceCodeFilePath}");
48 }
49
50 [Fact]
51 public void CanInstallUpgradeBundlePackage()
52 {
53 var bundleAv1 = this.CreateBundleInstaller(@"..\UpgradeRelatedBundleTests\BundleAv1");
54 var bundleAv2 = this.CreateBundleInstaller(@"..\UpgradeRelatedBundleTests\BundleAv2");
55 var upgradeBundlePackageBundlev2 = this.CreateBundleInstaller("UpgradeBundlePackageBundlev2");
56
57 bundleAv1.Install();
58 bundleAv1.VerifyRegisteredAndInPackageCache();
59
60 upgradeBundlePackageBundlev2.Install();
61 upgradeBundlePackageBundlev2.VerifyRegisteredAndInPackageCache();
62 bundleAv2.VerifyRegisteredAndInPackageCache();
63 bundleAv1.VerifyUnregisteredAndRemovedFromPackageCache();
64 }
65
66 [Fact]
67 public void CanSkipObsoleteBundlePackage()
68 {
69 var bundleAv1 = this.CreateBundleInstaller(@"..\UpgradeRelatedBundleTests\BundleAv1");
70 var bundleAv2 = this.CreateBundleInstaller(@"..\UpgradeRelatedBundleTests\BundleAv2");
71 var upgradeBundlePackageBundlev1 = this.CreateBundleInstaller("UpgradeBundlePackageBundlev1");
72
73 bundleAv2.Install();
74 bundleAv2.VerifyRegisteredAndInPackageCache();
48 75
76 upgradeBundlePackageBundlev1.Install();
77 upgradeBundlePackageBundlev1.VerifyUnregisteredAndRemovedFromPackageCache();
78 bundleAv1.VerifyUnregisteredAndRemovedFromPackageCache();
49 } 79 }
50 } 80 }
51} 81}
diff --git a/src/wix/WixToolset.Core.Burn/Bind/GenerateManifestDataFromIRCommand.cs b/src/wix/WixToolset.Core.Burn/Bind/GenerateManifestDataFromIRCommand.cs
index 6b37580e..9d214211 100644
--- a/src/wix/WixToolset.Core.Burn/Bind/GenerateManifestDataFromIRCommand.cs
+++ b/src/wix/WixToolset.Core.Burn/Bind/GenerateManifestDataFromIRCommand.cs
@@ -82,6 +82,7 @@ namespace WixToolset.Core.Burn.Bind
82 case SymbolDefinitionType.WixBundlePackageCommandLine: 82 case SymbolDefinitionType.WixBundlePackageCommandLine:
83 case SymbolDefinitionType.WixBundlePackageExitCode: 83 case SymbolDefinitionType.WixBundlePackageExitCode:
84 case SymbolDefinitionType.WixBundlePackageGroup: 84 case SymbolDefinitionType.WixBundlePackageGroup:
85 case SymbolDefinitionType.WixBundlePackageRelatedBundle:
85 case SymbolDefinitionType.WixBundlePatchTargetCode: 86 case SymbolDefinitionType.WixBundlePatchTargetCode:
86 case SymbolDefinitionType.WixBundlePayload: 87 case SymbolDefinitionType.WixBundlePayload:
87 case SymbolDefinitionType.WixBundlePayloadGroup: 88 case SymbolDefinitionType.WixBundlePayloadGroup:
diff --git a/src/wix/WixToolset.Core.Burn/Bundles/CreateBurnManifestCommand.cs b/src/wix/WixToolset.Core.Burn/Bundles/CreateBurnManifestCommand.cs
index 84923998..c0e54300 100644
--- a/src/wix/WixToolset.Core.Burn/Bundles/CreateBurnManifestCommand.cs
+++ b/src/wix/WixToolset.Core.Burn/Bundles/CreateBurnManifestCommand.cs
@@ -319,6 +319,7 @@ namespace WixToolset.Core.Burn.Bundles
319 var targetCodesByPatch = this.Section.Symbols.OfType<WixBundlePatchTargetCodeSymbol>().ToLookup(r => r.PackageRef); 319 var targetCodesByPatch = this.Section.Symbols.OfType<WixBundlePatchTargetCodeSymbol>().ToLookup(r => r.PackageRef);
320 var msiFeaturesByPackage = this.Section.Symbols.OfType<WixBundleMsiFeatureSymbol>().ToLookup(r => r.PackageRef); 320 var msiFeaturesByPackage = this.Section.Symbols.OfType<WixBundleMsiFeatureSymbol>().ToLookup(r => r.PackageRef);
321 var msiPropertiesByPackage = this.Section.Symbols.OfType<WixBundleMsiPropertySymbol>().ToLookup(r => r.PackageRef); 321 var msiPropertiesByPackage = this.Section.Symbols.OfType<WixBundleMsiPropertySymbol>().ToLookup(r => r.PackageRef);
322 var relatedBundlesByPackage = this.Section.Symbols.OfType<WixBundlePackageRelatedBundleSymbol>().ToLookup(r => r.PackageRef);
322 var relatedPackagesByPackage = this.Section.Symbols.OfType<WixBundleRelatedPackageSymbol>().ToLookup(r => r.PackageRef); 323 var relatedPackagesByPackage = this.Section.Symbols.OfType<WixBundleRelatedPackageSymbol>().ToLookup(r => r.PackageRef);
323 var slipstreamMspsByPackage = this.Section.Symbols.OfType<WixBundleSlipstreamMspSymbol>().ToLookup(r => r.TargetPackageRef); 324 var slipstreamMspsByPackage = this.Section.Symbols.OfType<WixBundleSlipstreamMspSymbol>().ToLookup(r => r.TargetPackageRef);
324 var exitCodesByPackage = this.Section.Symbols.OfType<WixBundlePackageExitCodeSymbol>().ToLookup(r => r.ChainPackageId); 325 var exitCodesByPackage = this.Section.Symbols.OfType<WixBundlePackageExitCodeSymbol>().ToLookup(r => r.ChainPackageId);
@@ -384,6 +385,7 @@ namespace WixToolset.Core.Burn.Bundles
384 if (package.SpecificPackageSymbol is WixBundleBundlePackageSymbol bundlePackage) // BUNDLE 385 if (package.SpecificPackageSymbol is WixBundleBundlePackageSymbol bundlePackage) // BUNDLE
385 { 386 {
386 writer.WriteAttributeString("BundleId", bundlePackage.BundleId); 387 writer.WriteAttributeString("BundleId", bundlePackage.BundleId);
388 writer.WriteAttributeString("Version", bundlePackage.Version);
387 writer.WriteAttributeString("InstallArguments", bundlePackage.InstallCommand); 389 writer.WriteAttributeString("InstallArguments", bundlePackage.InstallCommand);
388 writer.WriteAttributeString("UninstallArguments", bundlePackage.UninstallCommand); 390 writer.WriteAttributeString("UninstallArguments", bundlePackage.UninstallCommand);
389 writer.WriteAttributeString("RepairArguments", bundlePackage.RepairCommand); 391 writer.WriteAttributeString("RepairArguments", bundlePackage.RepairCommand);
@@ -536,6 +538,16 @@ namespace WixToolset.Core.Burn.Bundles
536 writer.WriteEndElement(); 538 writer.WriteEndElement();
537 } 539 }
538 540
541 var packageRelatedBundles = relatedBundlesByPackage[package.PackageId];
542
543 foreach (var relatedBundle in packageRelatedBundles)
544 {
545 writer.WriteStartElement("RelatedBundle");
546 writer.WriteAttributeString("Id", relatedBundle.BundleId);
547 writer.WriteAttributeString("Action", relatedBundle.Action.ToString());
548 writer.WriteEndElement();
549 }
550
539 var packageRelatedPackages = relatedPackagesByPackage[package.PackageId]; 551 var packageRelatedPackages = relatedPackagesByPackage[package.PackageId];
540 552
541 foreach (var related in packageRelatedPackages) 553 foreach (var related in packageRelatedPackages)
diff --git a/src/wix/WixToolset.Core.Burn/Bundles/ProcessBundlePackageCommand.cs b/src/wix/WixToolset.Core.Burn/Bundles/ProcessBundlePackageCommand.cs
index fdb07a56..e8c68faa 100644
--- a/src/wix/WixToolset.Core.Burn/Bundles/ProcessBundlePackageCommand.cs
+++ b/src/wix/WixToolset.Core.Burn/Bundles/ProcessBundlePackageCommand.cs
@@ -102,6 +102,8 @@ namespace WixToolset.Core.Burn.Bundles
102 102
103 var version = registrationElement.GetAttribute("Version"); 103 var version = registrationElement.GetAttribute("Version");
104 packagePayload.Version = version; 104 packagePayload.Version = version;
105 bundlePackage.Version = version;
106 this.Facade.PackageSymbol.Version = version;
105 107
106 if (String.IsNullOrEmpty(this.Facade.PackageSymbol.CacheId)) 108 if (String.IsNullOrEmpty(this.Facade.PackageSymbol.CacheId))
107 { 109 {
@@ -125,6 +127,8 @@ namespace WixToolset.Core.Burn.Bundles
125 127
126 this.ProcessPackages(document, namespaceManager); 128 this.ProcessPackages(document, namespaceManager);
127 129
130 this.ProcessRelatedBundles(document, namespaceManager);
131
128 // TODO: Add payloads? 132 // TODO: Add payloads?
129 } 133 }
130 } 134 }
@@ -148,5 +152,26 @@ namespace WixToolset.Core.Burn.Bundles
148 152
149 this.Facade.PackageSymbol.InstallSize = packageInstallSize; 153 this.Facade.PackageSymbol.InstallSize = packageInstallSize;
150 } 154 }
155
156 private void ProcessRelatedBundles(XmlDocument document, XmlNamespaceManager namespaceManager)
157 {
158 foreach (XmlElement relatedBundleElement in document.SelectNodes("/burn:BurnManifest/burn:RelatedBundle", namespaceManager))
159 {
160 var id = relatedBundleElement.GetAttribute("Id");
161
162 if (!Enum.TryParse(relatedBundleElement.GetAttribute("Action"), out RelatedBundleActionType action))
163 {
164 // TODO: warning
165 continue;
166 }
167
168 this.Section.AddSymbol(new WixBundlePackageRelatedBundleSymbol
169 {
170 PackageRef = this.Facade.PackageId,
171 BundleId = id,
172 Action = action,
173 });
174 }
175 }
151 } 176 }
152} 177}
diff --git a/src/wix/test/WixToolsetTest.CoreIntegration/BundlePackageFixture.cs b/src/wix/test/WixToolsetTest.CoreIntegration/BundlePackageFixture.cs
index 268920a6..6ccddbb7 100644
--- a/src/wix/test/WixToolsetTest.CoreIntegration/BundlePackageFixture.cs
+++ b/src/wix/test/WixToolsetTest.CoreIntegration/BundlePackageFixture.cs
@@ -95,7 +95,11 @@ namespace WixToolsetTest.CoreIntegration
95 .ToArray(); 95 .ToArray();
96 WixAssert.CompareLineByLine(new string[] 96 WixAssert.CompareLineByLine(new string[]
97 { 97 {
98 $"<BundlePackage Id='chain.exe' Cache='keep' CacheId='{chainBundleId}v1.0.0.0' InstallSize='34' Size='*' PerMachine='yes' Permanent='no' Vital='yes' RollbackBoundaryForward='WixDefaultBoundary' RollbackBoundaryBackward='WixDefaultBoundary' LogPathVariable='WixBundleLog_chain.exe' RollbackLogPathVariable='WixBundleRollbackLog_chain.exe' BundleId='{chainBundleId}' InstallArguments='' UninstallArguments='' RepairArguments='' SupportsBurnProtocol='yes' Win64='no'><Provides Key='MyProviderKey,v1.0' Version='1.0.0.0' DisplayName='BurnBundle' Imported='yes' /><PayloadRef Id='chain.exe' /></BundlePackage>", 98 $"<BundlePackage Id='chain.exe' Cache='keep' CacheId='{chainBundleId}v1.0.0.0' InstallSize='34' Size='*' PerMachine='yes' Permanent='no' Vital='yes' RollbackBoundaryForward='WixDefaultBoundary' RollbackBoundaryBackward='WixDefaultBoundary' LogPathVariable='WixBundleLog_chain.exe' RollbackLogPathVariable='WixBundleRollbackLog_chain.exe' BundleId='{chainBundleId}' Version='1.0.0.0' InstallArguments='' UninstallArguments='' RepairArguments='' SupportsBurnProtocol='yes' Win64='no'>" +
99 "<Provides Key='MyProviderKey,v1.0' Version='1.0.0.0' DisplayName='BurnBundle' Imported='yes' />" +
100 "<RelatedBundle Id='{B94478B1-E1F3-4700-9CE8-6AA090854AEC}' Action='Upgrade' />" +
101 "<PayloadRef Id='chain.exe' />" +
102 "</BundlePackage>",
99 }, bundlePackages); 103 }, bundlePackages);
100 104
101 var registrations = extractResult.SelectManifestNodes("/burn:BurnManifest/burn:Registration") 105 var registrations = extractResult.SelectManifestNodes("/burn:BurnManifest/burn:Registration")
@@ -108,6 +112,19 @@ namespace WixToolsetTest.CoreIntegration
108 "<Arp Register='yes' DisplayName='BundlePackageBundle' DisplayVersion='1.0.1.0' Publisher='Example Corporation' />" + 112 "<Arp Register='yes' DisplayName='BundlePackageBundle' DisplayVersion='1.0.1.0' Publisher='Example Corporation' />" +
109 "</Registration>" 113 "</Registration>"
110 }, registrations); 114 }, registrations);
115
116 ignoreAttributesByElementName = new Dictionary<string, List<string>>
117 {
118 { "WixPackageProperties", new List<string> { "DownloadSize", "PackageSize" } },
119 };
120 var packageElements = extractResult.SelectBADataNodes("/ba:BootstrapperApplicationData/ba:WixPackageProperties")
121 .Cast<XmlElement>()
122 .Select(e => e.GetTestXml(ignoreAttributesByElementName))
123 .ToArray();
124 WixAssert.CompareLineByLine(new string[]
125 {
126 "<WixPackageProperties Package='chain.exe' Vital='yes' DisplayName='BurnBundle' Description='BurnBundle' DownloadSize='*' PackageSize='*' InstalledSize='34' PackageType='Bundle' Permanent='no' LogPathVariable='WixBundleLog_chain.exe' RollbackLogPathVariable='WixBundleRollbackLog_chain.exe' Compressed='yes' Version='1.0.0.0' Cache='keep' />",
127 }, packageElements);
111 } 128 }
112 } 129 }
113 } 130 }