@joebigelow / wix / commits / 39b9a611

Detect related bundles for BundlePackages.

Sean Hall committed Apr 1, 2022 at 15:44 UTC 39b9a6112c2ff97f31f195749e2142538e47a2eb
38 files changed +773 -238
src/api/burn/WixToolset.BootstrapperCore.Native/inc/BootstrapperApplication.h
+17
@@ -226,6 +226,7 @@ enum BOOTSTRAPPER_APPLICATION_MESSAGE
226 BOOTSTRAPPER_APPLICATION_MESSAGE_ONPLANRELATEDBUNDLETYPE,
227 BOOTSTRAPPER_APPLICATION_MESSAGE_ONAPPLYDOWNGRADE,
228 BOOTSTRAPPER_APPLICATION_MESSAGE_ONEXECUTEPROCESSCANCEL,
229 + BOOTSTRAPPER_APPLICATION_MESSAGE_ONDETECTRELATEDBUNDLEPACKAGE,
230 };
231
232 enum BOOTSTRAPPER_APPLYCOMPLETE_ACTION
@@ -793,6 +794,22 @@ struct BA_ONDETECTRELATEDBUNDLE_RESULTS
794 BOOL fCancel;
795 };
796
797 +struct 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 +
807 +struct BA_ONDETECTRELATEDBUNDLEPACKAGE_RESULTS
808 +{
809 + DWORD cbSize;
810 + BOOL fCancel;
811 +};
812 +
813 struct BA_ONDETECTRELATEDMSIPACKAGE_ARGS
814 {
815 DWORD cbSize;
src/api/burn/WixToolset.Mba.Core/BootstrapperApplication.cs
+25
@@ -283,6 +283,9 @@ namespace WixToolset.Mba.Core
283 /// <inheritdoc/>
284 public event EventHandler<ExecuteProcessCancelEventArgs> ExecuteProcessCancel;
285
286 + /// <inheritdoc/>
287 + public event EventHandler<DetectRelatedBundlePackageEventArgs> DetectRelatedBundlePackage;
288 +
289 /// <summary>
290 /// Entry point that is called when the bootstrapper application is ready to run.
291 /// </summary>
@@ -1385,6 +1388,19 @@ namespace WixToolset.Mba.Core
1388 }
1389 }
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 +
1404 #region IBootstrapperApplication Members
1405
1406 int IBootstrapperApplication.BAProc(int message, IntPtr pvArgs, IntPtr pvResults, IntPtr pvContext)
@@ -2144,6 +2160,15 @@ namespace WixToolset.Mba.Core
2160 return args.HResult;
2161 }
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 +
2172 #endregion
2173 }
2174 }
src/api/burn/WixToolset.Mba.Core/EventArgs.cs
+65 -87
@@ -2030,10 +2030,7 @@ namespace WixToolset.Mba.Core
2030 [Serializable]
2031 public class LaunchApprovedExeBeginEventArgs : CancellableHResultEventArgs
2032 {
2033 - /// <summary>
2034 - ///
2035 - /// </summary>
2036 - /// <param name="cancelRecommendation"></param>
2033 + /// <summary />
2034 public LaunchApprovedExeBeginEventArgs(bool cancelRecommendation)
2035 : base(cancelRecommendation)
2036 {
@@ -2046,27 +2043,18 @@ namespace WixToolset.Mba.Core
2043 [Serializable]
2044 public class LaunchApprovedExeCompleteEventArgs : StatusEventArgs
2045 {
2049 - private int processId;
2050 -
2051 - /// <summary>
2052 - ///
2053 - /// </summary>
2054 - /// <param name="hrStatus"></param>
2055 - /// <param name="processId"></param>
2046 + /// <summary />
2047 public LaunchApprovedExeCompleteEventArgs(int hrStatus, int processId)
2048 : base(hrStatus)
2049 {
2059 - this.processId = processId;
2050 + this.ProcessId = processId;
2051 }
2052
2053 /// <summary>
2054 /// Gets the ProcessId of the process that was launched.
2055 /// This is only valid if the status reports success.
2056 /// </summary>
2066 - public int ProcessId
2067 - {
2068 - get { return this.processId; }
2069 - }
2057 + public int ProcessId { get; private set; }
2058 }
2059
2060 /// <summary>
@@ -2075,26 +2063,17 @@ namespace WixToolset.Mba.Core
2063 [Serializable]
2064 public class BeginMsiTransactionBeginEventArgs : CancellableHResultEventArgs
2065 {
2078 - private string transactionId;
2079 -
2080 - /// <summary>
2081 - ///
2082 - /// </summary>
2083 - /// <param name="transactionId"></param>
2084 - /// <param name="cancelRecommendation"></param>
2066 + /// <summary />
2067 public BeginMsiTransactionBeginEventArgs(string transactionId, bool cancelRecommendation)
2068 : base(cancelRecommendation)
2069 {
2088 - this.transactionId = transactionId;
2070 + this.TransactionId = transactionId;
2071 }
2072
2073 /// <summary>
2074 /// Gets the MSI transaction Id.
2075 /// </summary>
2094 - public string TransactionId
2095 - {
2096 - get { return this.transactionId; }
2097 - }
2076 + public string TransactionId { get; private set; }
2077 }
2078
2079 /// <summary>
@@ -2103,26 +2082,17 @@ namespace WixToolset.Mba.Core
2082 [Serializable]
2083 public class BeginMsiTransactionCompleteEventArgs : StatusEventArgs
2084 {
2106 - private string transactionId;
2107 -
2108 - /// <summary>
2109 - ///
2110 - /// </summary>
2111 - /// <param name="transactionId"></param>
2112 - /// <param name="hrStatus"></param>
2085 + /// <summary />
2086 public BeginMsiTransactionCompleteEventArgs(string transactionId, int hrStatus)
2087 : base(hrStatus)
2088 {
2116 - this.transactionId = transactionId;
2089 + this.TransactionId = transactionId;
2090 }
2091
2092 /// <summary>
2093 /// Gets the MSI transaction Id.
2094 /// </summary>
2122 - public string TransactionId
2123 - {
2124 - get { return this.transactionId; }
2125 - }
2095 + public string TransactionId { get; private set; }
2096 }
2097
2098 /// <summary>
@@ -2131,26 +2101,17 @@ namespace WixToolset.Mba.Core
2101 [Serializable]
2102 public class CommitMsiTransactionBeginEventArgs : CancellableHResultEventArgs
2103 {
2134 - private string transactionId;
2135 -
2136 - /// <summary>
2137 - ///
2138 - /// </summary>
2139 - /// <param name="transactionId"></param>
2140 - /// <param name="cancelRecommendation"></param>
2104 + /// <summary />
2105 public CommitMsiTransactionBeginEventArgs(string transactionId, bool cancelRecommendation)
2106 : base(cancelRecommendation)
2107 {
2144 - this.transactionId = transactionId;
2108 + this.TransactionId = transactionId;
2109 }
2110
2111 /// <summary>
2112 /// Gets the MSI transaction Id.
2113 /// </summary>
2150 - public string TransactionId
2151 - {
2152 - get { return this.transactionId; }
2153 - }
2114 + public string TransactionId { get; private set; }
2115 }
2116
2117 /// <summary>
@@ -2159,26 +2120,17 @@ namespace WixToolset.Mba.Core
2120 [Serializable]
2121 public class CommitMsiTransactionCompleteEventArgs : StatusEventArgs
2122 {
2162 - private string transactionId;
2163 -
2164 - /// <summary>
2165 - ///
2166 - /// </summary>
2167 - /// <param name="transactionId"></param>
2168 - /// <param name="hrStatus"></param>
2123 + /// <summary />
2124 public CommitMsiTransactionCompleteEventArgs(string transactionId, int hrStatus)
2125 : base(hrStatus)
2126 {
2172 - this.transactionId = transactionId;
2127 + this.TransactionId = transactionId;
2128 }
2129
2130 /// <summary>
2131 /// Gets the MSI transaction Id.
2132 /// </summary>
2178 - public string TransactionId
2179 - {
2180 - get { return this.transactionId; }
2181 - }
2133 + public string TransactionId { get; private set; }
2134 }
2135
2136 /// <summary>
@@ -2187,24 +2139,16 @@ namespace WixToolset.Mba.Core
2139 [Serializable]
2140 public class RollbackMsiTransactionBeginEventArgs : HResultEventArgs
2141 {
2190 - private string transactionId;
2191 -
2192 - /// <summary>
2193 - ///
2194 - /// </summary>
2195 - /// <param name="transactionId"></param>
2142 + /// <summary />
2143 public RollbackMsiTransactionBeginEventArgs(string transactionId)
2144 {
2198 - this.transactionId = transactionId;
2145 + this.TransactionId = transactionId;
2146 }
2147
2148 /// <summary>
2149 /// Gets the MSI transaction Id.
2150 /// </summary>
2204 - public string TransactionId
2205 - {
2206 - get { return this.transactionId; }
2207 - }
2151 + public string TransactionId { get; private set; }
2152 }
2153
2154 /// <summary>
@@ -2213,26 +2157,17 @@ namespace WixToolset.Mba.Core
2157 [Serializable]
2158 public class RollbackMsiTransactionCompleteEventArgs : StatusEventArgs
2159 {
2216 - private string transactionId;
2217 -
2218 - /// <summary>
2219 - ///
2220 - /// </summary>
2221 - /// <param name="transactionId"></param>
2222 - /// <param name="hrStatus"></param>
2160 + /// <summary />
2161 public RollbackMsiTransactionCompleteEventArgs(string transactionId, int hrStatus)
2162 : base(hrStatus)
2163 {
2226 - this.transactionId = transactionId;
2164 + this.TransactionId = transactionId;
2165 }
2166
2167 /// <summary>
2168 /// Gets the MSI transaction Id.
2169 /// </summary>
2232 - public string TransactionId
2233 - {
2234 - get { return this.transactionId; }
2235 - }
2170 + public string TransactionId { get; private set; }
2171 }
2172
2173 /// <summary>
@@ -2524,4 +2459,47 @@ namespace WixToolset.Mba.Core
2459 /// </summary>
2460 public BOOTSTRAPPER_EXECUTEPROCESSCANCEL_ACTION Action { get; set; }
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 + }
2505 }
src/api/burn/WixToolset.Mba.Core/IBootstrapperApplication.cs
+14
@@ -1182,6 +1182,20 @@ namespace WixToolset.Mba.Core
1182 [MarshalAs(UnmanagedType.I4)] BOOTSTRAPPER_EXECUTEPROCESSCANCEL_ACTION recommendation,
1183 [MarshalAs(UnmanagedType.I4)] ref BOOTSTRAPPER_EXECUTEPROCESSCANCEL_ACTION pAction
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 + );
1199 }
1200
1201 /// <summary>
src/api/burn/WixToolset.Mba.Core/IDefaultBootstrapperApplication.cs
+9 -4
@@ -36,15 +36,15 @@ namespace WixToolset.Mba.Core
36
37 /// <summary>
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)"/>
40 - /// or <see cref="IEngine.SetDownloadSource(string, string, 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)"/>.
41 /// </summary>
42 event EventHandler<CacheAcquireBeginEventArgs> CacheAcquireBegin;
43
44 /// <summary>
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)"/>
47 - /// or <see cref="IEngine.SetDownloadSource(string, string, 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)"/>.
48 /// </summary>
49 event EventHandler<CacheAcquireCompleteEventArgs> CacheAcquireComplete;
50
@@ -178,6 +178,11 @@ namespace WixToolset.Mba.Core
178 /// </summary>
179 event EventHandler<DetectRelatedBundleEventArgs> DetectRelatedBundle;
180
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>
187 /// Fired when a related MSI package has been detected for a package.
188 /// </summary>
src/api/burn/WixToolset.Mba.Core/PackageInfo.cs
+13 -4
@@ -56,6 +56,11 @@ namespace WixToolset.Mba.Core
56 ///
57 /// </summary>
58 UpdateBundle,
59 +
60 + /// <summary>
61 + ///
62 + /// </summary>
63 + ChainBundle,
64 }
65
66 /// <summary>
@@ -220,7 +225,11 @@ namespace WixToolset.Mba.Core
225 return null;
226 }
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))
233 {
234 return PackageType.Exe;
235 }
@@ -268,7 +277,7 @@ namespace WixToolset.Mba.Core
277 package.Type = PackageType.UpgradeBundle;
278 break;
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));
281 }
282
283 return package;
@@ -302,7 +311,7 @@ namespace WixToolset.Mba.Core
311
312 if (!packagesById.TryGetValue(id, out var ipackage))
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));
315 }
316
317 var package = (PackageInfo)ipackage;
@@ -322,7 +331,7 @@ namespace WixToolset.Mba.Core
331
332 if (!packagesById.TryGetValue(id, out var ipackage))
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));
335 }
336
337 var package = (PackageInfo)ipackage;
src/api/burn/balutil/balinfo.cpp
+5 -1
@@ -433,7 +433,11 @@ static HRESULT ParsePackagesFromXml(
433 hr = XmlGetAttributeEx(pNode, L"PackageType", &scz);
434 ExitOnFailure(hr, "Failed to get package type for package.");
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))
441 {
442 prgPackages[iPackage].type = BAL_INFO_PACKAGE_TYPE_EXE;
443 }
src/api/burn/balutil/inc/BAFunctions.h
+1
@@ -92,6 +92,7 @@ enum BA_FUNCTIONS_MESSAGE
92 BA_FUNCTIONS_MESSAGE_ONPLANRELATEDBUNDLETYPE = BOOTSTRAPPER_APPLICATION_MESSAGE_ONPLANRELATEDBUNDLETYPE,
93 BA_FUNCTIONS_MESSAGE_ONAPPLYDOWNGRADE = BOOTSTRAPPER_APPLICATION_MESSAGE_ONAPPLYDOWNGRADE,
94 BA_FUNCTIONS_MESSAGE_ONEXECUTEPROCESSCANCEL = BOOTSTRAPPER_APPLICATION_MESSAGE_ONEXECUTEPROCESSCANCEL,
95 + BA_FUNCTIONS_MESSAGE_ONDETECTRELATEDBUNDLEPACKAGE = BOOTSTRAPPER_APPLICATION_MESSAGE_ONDETECTRELATEDBUNDLEPACKAGE,
96
97 BA_FUNCTIONS_MESSAGE_ONTHEMELOADED = 1024,
98 BA_FUNCTIONS_MESSAGE_WNDPROC,
src/api/burn/balutil/inc/BalBaseBAFunctions.h
+12
@@ -887,6 +887,18 @@ public: // IBootstrapperApplication
887 return S_OK;
888 }
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 +
902 public: // IBAFunctions
903 virtual STDMETHODIMP OnPlan(
904 )
src/api/burn/balutil/inc/BalBaseBAFunctionsProc.h
+1
@@ -163,6 +163,7 @@ static HRESULT WINAPI BalBaseBAFunctionsProc(
163 case BA_FUNCTIONS_MESSAGE_ONPLANRELATEDBUNDLETYPE:
164 case BA_FUNCTIONS_MESSAGE_ONAPPLYDOWNGRADE:
165 case BA_FUNCTIONS_MESSAGE_ONEXECUTEPROCESSCANCEL:
166 + case BA_FUNCTIONS_MESSAGE_ONDETECTRELATEDBUNDLEPACKAGE:
167 hr = BalBaseBootstrapperApplicationProc((BOOTSTRAPPER_APPLICATION_MESSAGE)message, pvArgs, pvResults, pvContext);
168 break;
169 case BA_FUNCTIONS_MESSAGE_ONTHEMELOADED:
src/api/burn/balutil/inc/BalBaseBootstrapperApplication.h
+13
@@ -1087,6 +1087,19 @@ public: // IBootstrapperApplication
1087 return S_OK;
1088 }
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 +
1103 public: //CBalBaseBootstrapperApplication
1104 virtual STDMETHODIMP Initialize(
1105 __in const BOOTSTRAPPER_CREATE_ARGS* pCreateArgs
src/api/burn/balutil/inc/BalBaseBootstrapperApplicationProc.h
+12
@@ -756,6 +756,15 @@ static HRESULT BalBaseBAProcOnApplyDowngrade(
756 return pBA->OnApplyDowngrade(pArgs->hrRecommended, &pResults->hrStatus);
757 }
758
759 +static 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 +
768 /*******************************************************************
769 BalBaseBootstrapperApplicationProc - requires pvContext to be of type IBootstrapperApplication.
770 Provides a default mapping between the new message based BA interface and
@@ -1024,6 +1033,9 @@ static HRESULT WINAPI BalBaseBootstrapperApplicationProc(
1033 case BOOTSTRAPPER_APPLICATION_MESSAGE_ONEXECUTEPROCESSCANCEL:
1034 hr = BalBaseBAProcOnExecuteProcessCancel(pBA, reinterpret_cast<BA_ONEXECUTEPROCESSCANCEL_ARGS*>(pvArgs), reinterpret_cast<BA_ONEXECUTEPROCESSCANCEL_RESULTS*>(pvResults));
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;
1039 }
1040 }
1041
src/api/burn/balutil/inc/IBootstrapperApplication.h
+10
@@ -723,4 +723,14 @@ DECLARE_INTERFACE_IID_(IBootstrapperApplication, IUnknown, "53C31D56-49C0-426B-A
723 __in BOOTSTRAPPER_EXECUTEPROCESSCANCEL_ACTION recommendation,
724 __inout BOOTSTRAPPER_EXECUTEPROCESSCANCEL_ACTION* pAction
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;
736 };
src/api/burn/balutil/inc/balinfo.h
+1
@@ -17,6 +17,7 @@ typedef enum BAL_INFO_PACKAGE_TYPE
17 BAL_INFO_PACKAGE_TYPE_BUNDLE_ADDON,
18 BAL_INFO_PACKAGE_TYPE_BUNDLE_PATCH,
19 BAL_INFO_PACKAGE_TYPE_BUNDLE_UPDATE,
20 + BAL_INFO_PACKAGE_TYPE_BUNDLE_CHAIN,
21 } BAL_INFO_PACKAGE_TYPE;
22
23 typedef enum _BAL_INFO_RESTART
src/api/wix/WixToolset.Data/Symbols/SymbolDefinitions.cs
+4
@@ -142,6 +142,7 @@ namespace WixToolset.Data
142 WixBundlePackageCommandLine,
143 WixBundlePackageExitCode,
144 WixBundlePackageGroup,
145 + WixBundlePackageRelatedBundle,
146 WixBundlePatchTargetCode,
147 WixBundlePayload,
148 WixBundlePayloadGroup,
@@ -618,6 +619,9 @@ namespace WixToolset.Data
619 case SymbolDefinitionType.WixBundlePackageGroup:
620 return SymbolDefinitions.WixBundlePackageGroup;
621
622 + case SymbolDefinitionType.WixBundlePackageRelatedBundle:
623 + return SymbolDefinitions.WixBundlePackageRelatedBundle;
624 +
625 case SymbolDefinitionType.WixBundlePatchTargetCode:
626 return SymbolDefinitions.WixBundlePatchTargetCode;
627
src/api/wix/WixToolset.Data/Symbols/WixBundleBundlePackageSymbol.cs
+8
@@ -12,6 +12,7 @@ namespace WixToolset.Data
12 {
13 new IntermediateFieldDefinition(nameof(WixBundleBundlePackageSymbolFields.Attributes), IntermediateFieldType.Number),
14 new IntermediateFieldDefinition(nameof(WixBundleBundlePackageSymbolFields.BundleId), IntermediateFieldType.String),
15 + new IntermediateFieldDefinition(nameof(WixBundleBundlePackageSymbolFields.Version), IntermediateFieldType.String),
16 new IntermediateFieldDefinition(nameof(WixBundleBundlePackageSymbolFields.InstallCommand), IntermediateFieldType.String),
17 new IntermediateFieldDefinition(nameof(WixBundleBundlePackageSymbolFields.RepairCommand), IntermediateFieldType.String),
18 new IntermediateFieldDefinition(nameof(WixBundleBundlePackageSymbolFields.UninstallCommand), IntermediateFieldType.String),
@@ -28,6 +29,7 @@ namespace WixToolset.Data.Symbols
29 {
30 Attributes,
31 BundleId,
32 + Version,
33 InstallCommand,
34 RepairCommand,
35 UninstallCommand,
@@ -65,6 +67,12 @@ namespace WixToolset.Data.Symbols
67 set => this.Set((int)WixBundleBundlePackageSymbolFields.BundleId, value);
68 }
69
70 + public string Version
71 + {
72 + get => (string)this.Fields[(int)WixBundleBundlePackageSymbolFields.Version];
73 + set => this.Set((int)WixBundleBundlePackageSymbolFields.Version, value);
74 + }
75 +
76 public string InstallCommand
77 {
78 get => (string)this.Fields[(int)WixBundleBundlePackageSymbolFields.InstallCommand];
src/api/wix/WixToolset.Data/Symbols/WixBundlePackageRelatedBundleSymbol.cs new
+60
@@ -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 +
3 +namespace 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 +
21 +namespace 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 +}
src/burn/engine/bundlepackageengine.cpp
+254 -23
@@ -2,6 +2,18 @@
2
3 #include "precomp.h"
4
5 +typedef 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 +
13 +static BUNDLE_QUERY_CALLBACK_RESULT CALLBACK QueryRelatedBundlesCallback(
14 + __in const BUNDLE_QUERY_RELATED_BUNDLE_RESULT* pBundle,
15 + __in_opt LPVOID pvContext
16 + );
17 static HRESULT ExecuteBundle(
18 __in BURN_CACHE* pCache,
19 __in BURN_VARIABLES* pVariables,
@@ -35,6 +47,18 @@ extern "C" HRESULT BundlePackageEngineParsePackageFromXml(
47 hr = XmlGetAttributeEx(pixnBundlePackage, L"BundleId", &pPackage->Bundle.sczBundleId);
48 ExitOnRequiredXmlQueryFailure(hr, "Failed to get @BundleId.");
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 +
62 // @InstallArguments
63 hr = XmlGetAttributeEx(pixnBundlePackage, L"InstallArguments", &pPackage->Bundle.sczInstallArguments);
64 ExitOnOptionalXmlQueryFailure(hr, fFoundXml, "Failed to get @InstallArguments.");
@@ -55,6 +79,9 @@ extern "C" HRESULT BundlePackageEngineParsePackageFromXml(
79 hr = XmlGetYesNoAttribute(pixnBundlePackage, L"Win64", &pPackage->Bundle.fWin64);
80 ExitOnRequiredXmlQueryFailure(hr, "Failed to get @Win64.");
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 +
85 hr = ExeEngineParseExitCodesFromXml(pixnBundlePackage, &pPackage->Bundle.rgExitCodes, &pPackage->Bundle.cExitCodes);
86 ExitOnFailure(hr, "Failed to parse exit codes.");
87
@@ -70,11 +97,101 @@ LExit:
97 return hr;
98 }
99
100 +
101 +extern "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 +
180 +LExit:
181 + ReleaseObject(pixnNodes);
182 + ReleaseObject(pixnElement);
183 + ReleaseStr(sczAction);
184 + ReleaseStr(sczId);
185 +
186 + return hr;
187 +}
188 +
189 extern "C" void BundlePackageEnginePackageUninitialize(
190 __in BURN_PACKAGE* pPackage
191 )
192 {
193 ReleaseStr(pPackage->Bundle.sczBundleId);
194 + ReleaseVerutilVersion(pPackage->Bundle.pVersion);
195 ReleaseStr(pPackage->Bundle.sczRegistrationKey);
196 ReleaseStr(pPackage->Bundle.sczInstallArguments);
197 ReleaseStr(pPackage->Bundle.sczRepairArguments);
@@ -92,45 +209,95 @@ extern "C" void BundlePackageEnginePackageUninitialize(
209 MemFree(pPackage->Bundle.rgCommandLineArguments);
210 }
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 +
236 // clear struct
237 memset(&pPackage->Bundle, 0, sizeof(pPackage->Bundle));
238 }
239
240 extern "C" HRESULT BundlePackageEngineDetectPackage(
100 - __in BURN_PACKAGE* pPackage
241 + __in BURN_PACKAGE* pPackage,
242 + __in BURN_REGISTRATION* /*pRegistration*/,
243 + __in BURN_USER_EXPERIENCE* pUserExperience
244 )
245 {
246 HRESULT hr = S_OK;
104 - HKEY hkRegistration = NULL;
105 - DWORD dwInstalled = 0;
106 - BOOL fDetected = FALSE;
107 - HKEY hkRoot = pPackage->fPerMachine ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER;
108 - REG_KEY_BITNESS bitness = pPackage->Bundle.fWin64 ? REG_KEY_64BIT : REG_KEY_32BIT;
109 -
110 - // TODO: detect all related bundles, so that the Obsolete state can be detected.
111 - hr = RegOpenEx(hkRoot, pPackage->Bundle.sczRegistrationKey, KEY_QUERY_VALUE, bitness, &hkRegistration);
112 - if (SUCCEEDED(hr))
247 + BUNDLE_QUERY_CONTEXT queryContext = { };
248 +
249 + queryContext.pPackage = pPackage;
250 + queryContext.pUserExperience = pUserExperience;
251 +
252 + hr = BundleQueryRelatedBundles(
253 + BUNDLE_INSTALL_CONTEXT_MACHINE,
254 + const_cast<LPCWSTR*>(pPackage->Bundle.rgsczDetectCodes),
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)
281 {
114 - hr = RegReadNumber(hkRegistration, REGISTRY_BUNDLE_INSTALLED, &dwInstalled);
282 + pPackage->currentState = queryContext.fSelfFound ? BOOTSTRAPPER_PACKAGE_STATE_SUPERSEDED : BOOTSTRAPPER_PACKAGE_STATE_OBSOLETE;
283 }
116 -
117 - // Not finding the key or value is okay.
118 - if (E_FILENOTFOUND == hr || E_PATHNOTFOUND == hr)
284 + else
285 {
120 - hr = S_OK;
286 + pPackage->currentState = queryContext.fSelfFound ? BOOTSTRAPPER_PACKAGE_STATE_PRESENT : BOOTSTRAPPER_PACKAGE_STATE_ABSENT;
287 }
288
123 - fDetected = (1 == dwInstalled);
124 -
125 - // update detect state
126 - pPackage->currentState = fDetected ? BOOTSTRAPPER_PACKAGE_STATE_PRESENT : BOOTSTRAPPER_PACKAGE_STATE_ABSENT;
127 -
289 if (pPackage->fCanAffectRegistration)
290 {
291 pPackage->installRegistrationState = BOOTSTRAPPER_PACKAGE_STATE_ABSENT < pPackage->currentState ? BURN_PACKAGE_REGISTRATION_STATE_PRESENT : BURN_PACKAGE_REGISTRATION_STATE_ABSENT;
292 }
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 +
300 +LExit:
301 return hr;
302 }
303
@@ -148,7 +315,8 @@ extern "C" HRESULT BundlePackageEnginePlanCalculatePackage(
315 // execute action
316 switch (pPackage->currentState)
317 {
151 - case BOOTSTRAPPER_PACKAGE_STATE_PRESENT:
318 + case BOOTSTRAPPER_PACKAGE_STATE_PRESENT: __fallthrough;
319 + case BOOTSTRAPPER_PACKAGE_STATE_SUPERSEDED:
320 switch (pPackage->requested)
321 {
322 case BOOTSTRAPPER_REQUEST_STATE_PRESENT:
@@ -173,6 +341,7 @@ extern "C" HRESULT BundlePackageEnginePlanCalculatePackage(
341 }
342 break;
343
344 + case BOOTSTRAPPER_PACKAGE_STATE_OBSOLETE: __fallthrough;
345 case BOOTSTRAPPER_PACKAGE_STATE_ABSENT:
346 switch (pPackage->requested)
347 {
@@ -200,7 +369,8 @@ extern "C" HRESULT BundlePackageEnginePlanCalculatePackage(
369 {
370 switch (pPackage->currentState)
371 {
203 - case BOOTSTRAPPER_PACKAGE_STATE_PRESENT:
372 + case BOOTSTRAPPER_PACKAGE_STATE_PRESENT: __fallthrough;
373 + case BOOTSTRAPPER_PACKAGE_STATE_SUPERSEDED:
374 switch (pPackage->requested)
375 {
376 case BOOTSTRAPPER_REQUEST_STATE_PRESENT: __fallthrough;
@@ -218,6 +388,7 @@ extern "C" HRESULT BundlePackageEnginePlanCalculatePackage(
388 }
389 break;
390
391 + case BOOTSTRAPPER_PACKAGE_STATE_OBSOLETE: __fallthrough;
392 case BOOTSTRAPPER_PACKAGE_STATE_ABSENT:
393 switch (pPackage->requested)
394 {
@@ -481,6 +652,66 @@ LExit:
652 return;
653 }
654
655 +static 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 +
708 +LExit:
709 + ReleaseVerutilVersion(pVersion);
710 + ReleaseStr(sczBundleVersion);
711 +
712 + return result;
713 +}
714 +
715 static HRESULT ExecuteBundle(
716 __in BURN_CACHE* pCache,
717 __in BURN_VARIABLES* pVariables,
src/burn/engine/bundlepackageengine.h
+14 -1
@@ -13,11 +13,24 @@ HRESULT BundlePackageEngineParsePackageFromXml(
13 __in IXMLDOMNode* pixnBundlePackage,
14 __in BURN_PACKAGE* pPackage
15 );
16 +HRESULT 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 + );
27 void BundlePackageEnginePackageUninitialize(
28 __in BURN_PACKAGE* pPackage
29 );
30 HRESULT BundlePackageEngineDetectPackage(
20 - __in BURN_PACKAGE* pPackage
31 + __in BURN_PACKAGE* pPackage,
32 + __in BURN_REGISTRATION* pRegistration,
33 + __in BURN_USER_EXPERIENCE* pUserExperience
34 );
35 HRESULT BundlePackageEnginePlanCalculatePackage(
36 __in BURN_PACKAGE* pPackage
src/burn/engine/core.cpp
+1 -1
@@ -2121,7 +2121,7 @@ static HRESULT DetectPackage(
2121 switch (pPackage->type)
2122 {
2123 case BURN_PACKAGE_TYPE_BUNDLE:
2124 - hr = BundlePackageEngineDetectPackage(pPackage);
2124 + hr = BundlePackageEngineDetectPackage(pPackage, &pEngineState->registration, &pEngineState->userExperience);
2125 break;
2126
2127 case BURN_PACKAGE_TYPE_EXE:
src/burn/engine/package.h
+16 -3
@@ -303,14 +303,23 @@ typedef struct _BURN_PACKAGE
303 struct
304 {
305 LPWSTR sczBundleId;
306 + VERUTIL_VERSION* pVersion;
307 LPWSTR sczRegistrationKey;
308 LPWSTR sczInstallArguments;
309 LPWSTR sczRepairArguments;
310 LPWSTR sczUninstallArguments;
311
311 - LPWSTR sczIgnoreDependencies;
312 - LPCWSTR wzAncestors; // points directly into engine state.
313 - LPCWSTR wzEngineWorkingDirectory; // points directly into engine state.
312 + LPWSTR* rgsczDetectCodes;
313 + DWORD cDetectCodes;
314 +
315 + LPWSTR* rgsczUpgradeCodes;
316 + DWORD cUpgradeCodes;
317 +
318 + LPWSTR* rgsczAddonCodes;
319 + DWORD cAddonCodes;
320 +
321 + LPWSTR* rgsczPatchCodes;
322 + DWORD cPatchCodes;
323
324 BOOL fWin64;
325 BOOL fSupportsBurnProtocol;
@@ -320,6 +329,10 @@ typedef struct _BURN_PACKAGE
329
330 BURN_EXE_COMMAND_LINE_ARGUMENT* rgCommandLineArguments;
331 DWORD cCommandLineArguments;
332 +
333 + LPWSTR sczIgnoreDependencies;
334 + LPCWSTR wzAncestors; // points directly into engine state.
335 + LPCWSTR wzEngineWorkingDirectory; // points directly into engine state.
336 } Bundle;
337 struct
338 {
src/burn/engine/registration.cpp
+1 -86
@@ -71,10 +71,6 @@ static HRESULT UpdateResumeMode(
71 __in BOOTSTRAPPER_REGISTRATION_TYPE registrationType,
72 __in BOOL fRestartInitiated
73 );
74 -static HRESULT ParseRelatedCodes(
75 - __in BURN_REGISTRATION* pRegistration,
76 - __in IXMLDOMNode* pixnBundle
77 - );
74 static HRESULT FormatUpdateRegistrationKey(
75 __in BURN_REGISTRATION* pRegistration,
76 __out_z LPWSTR* psczKey
@@ -143,7 +139,7 @@ extern "C" HRESULT RegistrationParseFromXml(
139 hr = XmlGetAttributeEx(pixnRegistrationNode, L"Tag", &pRegistration->sczTag);
140 ExitOnFailure(hr, "Failed to get @Tag.");
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);
143 ExitOnFailure(hr, "Failed to parse related bundles");
144
145 // @Version
@@ -1395,87 +1391,6 @@ LExit:
1391 return hr;
1392 }
1393
1398 -static 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 -
1470 -LExit:
1471 - ReleaseObject(pixnNodes);
1472 - ReleaseObject(pixnElement);
1473 - ReleaseStr(sczAction);
1474 - ReleaseStr(sczId);
1475 -
1476 - return hr;
1477 -}
1478 -
1394 static HRESULT FormatUpdateRegistrationKey(
1395 __in BURN_REGISTRATION* pRegistration,
1396 __out_z LPWSTR* psczKey
src/burn/engine/relatedbundle.cpp
+25 -25
@@ -143,6 +143,30 @@ extern "C" void RelatedBundlesSortPlan(
143 qsort_s(pRelatedBundles->rgpPlanSortedRelatedBundles, pRelatedBundles->cRelatedBundles, sizeof(BURN_RELATED_BUNDLE*), CompareRelatedBundlesPlan, NULL);
144 }
145
146 +extern "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 +
170
171 // internal helper functions
172
@@ -248,30 +272,6 @@ LExit:
272 return result;
273 }
274
251 -static 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 -
275 static HRESULT LoadIfRelatedBundle(
276 __in const BUNDLE_QUERY_RELATED_BUNDLE_RESULT* pBundle,
277 __in BURN_REGISTRATION* pRegistration,
@@ -280,7 +280,7 @@ static HRESULT LoadIfRelatedBundle(
280 {
281 HRESULT hr = S_OK;
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;
285
286 // If we found our bundle id, it's not a related bundle.
src/burn/engine/relatedbundle.h
+3
@@ -25,6 +25,9 @@ void RelatedBundlesSortDetect(
25 void RelatedBundlesSortPlan(
26 __in BURN_RELATED_BUNDLES* pRelatedBundles
27 );
28 +BOOTSTRAPPER_RELATION_TYPE RelatedBundleConvertRelationType(
29 + __in BUNDLE_RELATION_TYPE relationType
30 + );
31
32 #if defined(__cplusplus)
33 }
src/burn/engine/userexperience.cpp
+35 -1
@@ -104,7 +104,7 @@ extern "C" HRESULT UserExperienceLoad(
104 args.pCommand = pCommand;
105 args.pfnBootstrapperEngineProc = EngineForApplicationProc;
106 args.pvBootstrapperEngineProcContext = pEngineContext;
107 - args.qwEngineAPIVersion = MAKEQWORDVERSION(2022, 3, 17, 0);
107 + args.qwEngineAPIVersion = MAKEQWORDVERSION(2022, 3, 31, 0);
108
109 results.cbSize = sizeof(BOOTSTRAPPER_CREATE_RESULTS);
110
@@ -1247,6 +1247,40 @@ LExit:
1247 return hr;
1248 }
1249
1250 +EXTERN_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 +
1280 +LExit:
1281 + return hr;
1282 +}
1283 +
1284 EXTERN_C BAAPI UserExperienceOnDetectRelatedMsiPackage(
1285 __in BURN_USER_EXPERIENCE* pUserExperience,
1286 __in_z LPCWSTR wzPackageId,
src/burn/engine/userexperience.h
+8
@@ -298,6 +298,14 @@ BAAPI UserExperienceOnDetectRelatedBundle(
298 __in VERUTIL_VERSION* pVersion,
299 __in BOOL fMissingFromCache
300 );
301 +BAAPI 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 + );
309 BAAPI UserExperienceOnDetectRelatedMsiPackage(
310 __in BURN_USER_EXPERIENCE* pUserExperience,
311 __in_z LPCWSTR wzPackageId,
src/burn/test/BurnUnitTest/TestData/PlanTest/BundlePackage_Multiple_manifest.xml
+1 -1
@@ -1 +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
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" 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>
src/ext/Bal/wixstdba/WixStandardBootstrapperApplication.cpp
+11
@@ -1489,6 +1489,9 @@ public: // IBootstrapperApplication
1489 case BOOTSTRAPPER_APPLICATION_MESSAGE_ONAPPLYDOWNGRADE:
1490 OnApplyDowngradeFallback(reinterpret_cast<BA_ONAPPLYDOWNGRADE_ARGS*>(pvArgs), reinterpret_cast<BA_ONAPPLYDOWNGRADE_RESULTS*>(pvResults));
1491 break;
1492 + case BOOTSTRAPPER_APPLICATION_MESSAGE_ONDETECTRELATEDBUNDLEPACKAGE:
1493 + OnDetectRelatedBundlePackageFallback(reinterpret_cast<BA_ONDETECTRELATEDBUNDLEPACKAGE_ARGS*>(pvArgs), reinterpret_cast<BA_ONDETECTRELATEDBUNDLEPACKAGE_RESULTS*>(pvResults));
1494 + break;
1495 default:
1496 #ifdef DEBUG
1497 BalLog(BOOTSTRAPPER_LOG_LEVEL_STANDARD, "WIXSTDBA: Forwarding unknown BA message: %d", message);
@@ -2178,6 +2181,14 @@ private: // privates
2181 m_pfnBAFunctionsProc(BA_FUNCTIONS_MESSAGE_ONEXECUTEPROCESSCANCEL, pArgs, pResults, m_pvBAFunctionsProcContext);
2182 }
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 +
2192
2193 public: //CBalBaseBootstrapperApplication
2194 virtual STDMETHODIMP Initialize(
src/test/burn/TestData/BundlePackageTests/UpgradeBundlePackageBundlev1/UpgradeBundlePackageBundle.props new
+14
@@ -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>
src/test/burn/TestData/BundlePackageTests/UpgradeBundlePackageBundlev1/UpgradeBundlePackageBundlev1.wixproj new
+7
@@ -0,0 +1,7 @@
1 +<!-- Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. -->
2 +<Project Sdk="WixToolset.Sdk">
3 + <Import Project="UpgradeBundlePackageBundle.props" />
4 + <ItemGroup>
5 + <ProjectReference Include="..\..\UpgradeRelatedBundleTests\BundleAv1\BundleAv1.wixproj" />
6 + </ItemGroup>
7 +</Project>
\ No newline at end of file
src/test/burn/TestData/BundlePackageTests/UpgradeBundlePackageBundlev1/UpgradeBundlePackageBundlev1.wxs new
+10
@@ -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>
src/test/burn/TestData/BundlePackageTests/UpgradeBundlePackageBundlev2/UpgradeBundlePackageBundlev2.wixproj new
+7
@@ -0,0 +1,7 @@
1 +<!-- Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. -->
2 +<Project Sdk="WixToolset.Sdk">
3 + <Import Project="..\UpgradeBundlePackageBundlev1\UpgradeBundlePackageBundle.props" />
4 + <ItemGroup>
5 + <ProjectReference Include="..\..\UpgradeRelatedBundleTests\BundleAv2\BundleAv2.wixproj" />
6 + </ItemGroup>
7 +</Project>
\ No newline at end of file
src/test/burn/TestData/BundlePackageTests/UpgradeBundlePackageBundlev2/UpgradeBundlePackageBundlev2.wxs new
+10
@@ -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>
src/test/burn/WixToolsetTest.BurnE2E/BundlePackageTests.cs
+30
@@ -45,7 +45,37 @@ namespace WixToolsetTest.BurnE2E
45 // Source file should *not* be installed
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}");
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();
75
76 + upgradeBundlePackageBundlev1.Install();
77 + upgradeBundlePackageBundlev1.VerifyUnregisteredAndRemovedFromPackageCache();
78 + bundleAv1.VerifyUnregisteredAndRemovedFromPackageCache();
79 }
80 }
81 }
src/wix/WixToolset.Core.Burn/Bind/GenerateManifestDataFromIRCommand.cs
+1
@@ -82,6 +82,7 @@ namespace WixToolset.Core.Burn.Bind
82 case SymbolDefinitionType.WixBundlePackageCommandLine:
83 case SymbolDefinitionType.WixBundlePackageExitCode:
84 case SymbolDefinitionType.WixBundlePackageGroup:
85 + case SymbolDefinitionType.WixBundlePackageRelatedBundle:
86 case SymbolDefinitionType.WixBundlePatchTargetCode:
87 case SymbolDefinitionType.WixBundlePayload:
88 case SymbolDefinitionType.WixBundlePayloadGroup:
src/wix/WixToolset.Core.Burn/Bundles/CreateBurnManifestCommand.cs
+12
@@ -319,6 +319,7 @@ namespace WixToolset.Core.Burn.Bundles
319 var targetCodesByPatch = this.Section.Symbols.OfType<WixBundlePatchTargetCodeSymbol>().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);
322 + var relatedBundlesByPackage = this.Section.Symbols.OfType<WixBundlePackageRelatedBundleSymbol>().ToLookup(r => r.PackageRef);
323 var relatedPackagesByPackage = this.Section.Symbols.OfType<WixBundleRelatedPackageSymbol>().ToLookup(r => r.PackageRef);
324 var slipstreamMspsByPackage = this.Section.Symbols.OfType<WixBundleSlipstreamMspSymbol>().ToLookup(r => r.TargetPackageRef);
325 var exitCodesByPackage = this.Section.Symbols.OfType<WixBundlePackageExitCodeSymbol>().ToLookup(r => r.ChainPackageId);
@@ -384,6 +385,7 @@ namespace WixToolset.Core.Burn.Bundles
385 if (package.SpecificPackageSymbol is WixBundleBundlePackageSymbol bundlePackage) // BUNDLE
386 {
387 writer.WriteAttributeString("BundleId", bundlePackage.BundleId);
388 + writer.WriteAttributeString("Version", bundlePackage.Version);
389 writer.WriteAttributeString("InstallArguments", bundlePackage.InstallCommand);
390 writer.WriteAttributeString("UninstallArguments", bundlePackage.UninstallCommand);
391 writer.WriteAttributeString("RepairArguments", bundlePackage.RepairCommand);
@@ -536,6 +538,16 @@ namespace WixToolset.Core.Burn.Bundles
538 writer.WriteEndElement();
539 }
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 +
551 var packageRelatedPackages = relatedPackagesByPackage[package.PackageId];
552
553 foreach (var related in packageRelatedPackages)
src/wix/WixToolset.Core.Burn/Bundles/ProcessBundlePackageCommand.cs
+25
@@ -102,6 +102,8 @@ namespace WixToolset.Core.Burn.Bundles
102
103 var version = registrationElement.GetAttribute("Version");
104 packagePayload.Version = version;
105 + bundlePackage.Version = version;
106 + this.Facade.PackageSymbol.Version = version;
107
108 if (String.IsNullOrEmpty(this.Facade.PackageSymbol.CacheId))
109 {
@@ -125,6 +127,8 @@ namespace WixToolset.Core.Burn.Bundles
127
128 this.ProcessPackages(document, namespaceManager);
129
130 + this.ProcessRelatedBundles(document, namespaceManager);
131 +
132 // TODO: Add payloads?
133 }
134 }
@@ -148,5 +152,26 @@ namespace WixToolset.Core.Burn.Bundles
152
153 this.Facade.PackageSymbol.InstallSize = packageInstallSize;
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 + }
176 }
177 }
src/wix/test/WixToolsetTest.CoreIntegration/BundlePackageFixture.cs
+18 -1
@@ -95,7 +95,11 @@ namespace WixToolsetTest.CoreIntegration
95 .ToArray();
96 WixAssert.CompareLineByLine(new string[]
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>",
103 }, bundlePackages);
104
105 var registrations = extractResult.SelectManifestNodes("/burn:BurnManifest/burn:Registration")
@@ -108,6 +112,19 @@ namespace WixToolsetTest.CoreIntegration
112 "<Arp Register='yes' DisplayName='BundlePackageBundle' DisplayVersion='1.0.1.0' Publisher='Example Corporation' />" +
113 "</Registration>"
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);
128 }
129 }
130 }