aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/api/burn/WixToolset.BootstrapperCore.Native/inc/BootstrapperApplication.h1
-rw-r--r--src/api/burn/WixToolset.Mba.Core/BootstrapperApplication.cs4
-rw-r--r--src/api/burn/WixToolset.Mba.Core/EventArgs.cs8
-rw-r--r--src/api/burn/WixToolset.Mba.Core/IBootstrapperApplication.cs15
-rw-r--r--src/api/burn/balutil/inc/BalBaseBAFunctions.h1
-rw-r--r--src/api/burn/balutil/inc/BalBaseBootstrapperApplication.h1
-rw-r--r--src/api/burn/balutil/inc/BalBaseBootstrapperApplicationProc.h2
-rw-r--r--src/api/burn/balutil/inc/IBootstrapperApplication.h1
-rw-r--r--src/burn/engine/core.cpp2
-rw-r--r--src/burn/engine/detect.cpp52
-rw-r--r--src/burn/engine/detect.h1
-rw-r--r--src/burn/engine/engine.mc2
-rw-r--r--src/burn/engine/userexperience.cpp4
-rw-r--r--src/burn/engine/userexperience.h1
-rw-r--r--src/ext/Bal/wixstdba/WixStandardBootstrapperApplication.cpp13
-rw-r--r--src/test/burn/TestData/WixStdBaTests/BundleA_v10/BundleA_v10.wixproj20
-rw-r--r--src/test/burn/WixToolset.WixBA/InstallationViewModel.cs47
-rw-r--r--src/test/burn/WixToolset.WixBA/RootViewModel.cs23
-rw-r--r--src/test/burn/WixToolsetTest.BurnE2E/DependencyTests.cs2
-rw-r--r--src/test/burn/WixToolsetTest.BurnE2E/UpgradeRelatedBundleTests.cs2
20 files changed, 94 insertions, 108 deletions
diff --git a/src/api/burn/WixToolset.BootstrapperCore.Native/inc/BootstrapperApplication.h b/src/api/burn/WixToolset.BootstrapperCore.Native/inc/BootstrapperApplication.h
index c2641b5f..4fbfc890 100644
--- a/src/api/burn/WixToolset.BootstrapperCore.Native/inc/BootstrapperApplication.h
+++ b/src/api/burn/WixToolset.BootstrapperCore.Native/inc/BootstrapperApplication.h
@@ -725,7 +725,6 @@ struct BA_ONDETECTRELATEDBUNDLE_ARGS
725 LPCWSTR wzBundleTag; 725 LPCWSTR wzBundleTag;
726 BOOL fPerMachine; 726 BOOL fPerMachine;
727 LPCWSTR wzVersion; 727 LPCWSTR wzVersion;
728 BOOTSTRAPPER_RELATED_OPERATION operation;
729 BOOL fMissingFromCache; 728 BOOL fMissingFromCache;
730}; 729};
731 730
diff --git a/src/api/burn/WixToolset.Mba.Core/BootstrapperApplication.cs b/src/api/burn/WixToolset.Mba.Core/BootstrapperApplication.cs
index b1fcaea4..34b63a50 100644
--- a/src/api/burn/WixToolset.Mba.Core/BootstrapperApplication.cs
+++ b/src/api/burn/WixToolset.Mba.Core/BootstrapperApplication.cs
@@ -1345,9 +1345,9 @@ namespace WixToolset.Mba.Core
1345 return args.HResult; 1345 return args.HResult;
1346 } 1346 }
1347 1347
1348 int IBootstrapperApplication.OnDetectRelatedBundle(string wzProductCode, RelationType relationType, string wzBundleTag, bool fPerMachine, string wzVersion, RelatedOperation operation, bool fMissingFromCache, ref bool fCancel) 1348 int IBootstrapperApplication.OnDetectRelatedBundle(string wzProductCode, RelationType relationType, string wzBundleTag, bool fPerMachine, string wzVersion, bool fMissingFromCache, ref bool fCancel)
1349 { 1349 {
1350 DetectRelatedBundleEventArgs args = new DetectRelatedBundleEventArgs(wzProductCode, relationType, wzBundleTag, fPerMachine, wzVersion, operation, fMissingFromCache, fCancel); 1350 DetectRelatedBundleEventArgs args = new DetectRelatedBundleEventArgs(wzProductCode, relationType, wzBundleTag, fPerMachine, wzVersion, fMissingFromCache, fCancel);
1351 this.OnDetectRelatedBundle(args); 1351 this.OnDetectRelatedBundle(args);
1352 1352
1353 fCancel = args.Cancel; 1353 fCancel = args.Cancel;
diff --git a/src/api/burn/WixToolset.Mba.Core/EventArgs.cs b/src/api/burn/WixToolset.Mba.Core/EventArgs.cs
index 65169b25..93831be6 100644
--- a/src/api/burn/WixToolset.Mba.Core/EventArgs.cs
+++ b/src/api/burn/WixToolset.Mba.Core/EventArgs.cs
@@ -434,7 +434,7 @@ namespace WixToolset.Mba.Core
434 public class DetectRelatedBundleEventArgs : CancellableHResultEventArgs 434 public class DetectRelatedBundleEventArgs : CancellableHResultEventArgs
435 { 435 {
436 /// <summary /> 436 /// <summary />
437 public DetectRelatedBundleEventArgs(string productCode, RelationType relationType, string bundleTag, bool perMachine, string version, RelatedOperation operation, bool missingFromCache, bool cancelRecommendation) 437 public DetectRelatedBundleEventArgs(string productCode, RelationType relationType, string bundleTag, bool perMachine, string version, bool missingFromCache, bool cancelRecommendation)
438 : base(cancelRecommendation) 438 : base(cancelRecommendation)
439 { 439 {
440 this.ProductCode = productCode; 440 this.ProductCode = productCode;
@@ -442,7 +442,6 @@ namespace WixToolset.Mba.Core
442 this.BundleTag = bundleTag; 442 this.BundleTag = bundleTag;
443 this.PerMachine = perMachine; 443 this.PerMachine = perMachine;
444 this.Version = version; 444 this.Version = version;
445 this.Operation = operation;
446 this.MissingFromCache = missingFromCache; 445 this.MissingFromCache = missingFromCache;
447 } 446 }
448 447
@@ -472,11 +471,6 @@ namespace WixToolset.Mba.Core
472 public string Version { get; private set; } 471 public string Version { get; private set; }
473 472
474 /// <summary> 473 /// <summary>
475 /// Gets the operation that will be taken on the detected bundle.
476 /// </summary>
477 public RelatedOperation Operation { get; private set; }
478
479 /// <summary>
480 /// Whether the related bundle is missing from the package cache. 474 /// Whether the related bundle is missing from the package cache.
481 /// </summary> 475 /// </summary>
482 public bool MissingFromCache { get; set; } 476 public bool MissingFromCache { get; set; }
diff --git a/src/api/burn/WixToolset.Mba.Core/IBootstrapperApplication.cs b/src/api/burn/WixToolset.Mba.Core/IBootstrapperApplication.cs
index 3df54bde..babd523a 100644
--- a/src/api/burn/WixToolset.Mba.Core/IBootstrapperApplication.cs
+++ b/src/api/burn/WixToolset.Mba.Core/IBootstrapperApplication.cs
@@ -156,15 +156,6 @@ namespace WixToolset.Mba.Core
156 /// <summary> 156 /// <summary>
157 /// See <see cref="IDefaultBootstrapperApplication.DetectRelatedBundle"/>. 157 /// See <see cref="IDefaultBootstrapperApplication.DetectRelatedBundle"/>.
158 /// </summary> 158 /// </summary>
159 /// <param name="wzBundleId"></param>
160 /// <param name="relationType"></param>
161 /// <param name="wzBundleTag"></param>
162 /// <param name="fPerMachine"></param>
163 /// <param name="wzVersion"></param>
164 /// <param name="operation"></param>
165 /// <param name="fMissingFromCache"></param>
166 /// <param name="fCancel"></param>
167 /// <returns></returns>
168 [PreserveSig] 159 [PreserveSig]
169 [return: MarshalAs(UnmanagedType.I4)] 160 [return: MarshalAs(UnmanagedType.I4)]
170 int OnDetectRelatedBundle( 161 int OnDetectRelatedBundle(
@@ -173,7 +164,6 @@ namespace WixToolset.Mba.Core
173 [MarshalAs(UnmanagedType.LPWStr)] string wzBundleTag, 164 [MarshalAs(UnmanagedType.LPWStr)] string wzBundleTag,
174 [MarshalAs(UnmanagedType.Bool)] bool fPerMachine, 165 [MarshalAs(UnmanagedType.Bool)] bool fPerMachine,
175 [MarshalAs(UnmanagedType.LPWStr)] string wzVersion, 166 [MarshalAs(UnmanagedType.LPWStr)] string wzVersion,
176 [MarshalAs(UnmanagedType.U4)] RelatedOperation operation,
177 [MarshalAs(UnmanagedType.Bool)] bool fMissingFromCache, 167 [MarshalAs(UnmanagedType.Bool)] bool fMissingFromCache,
178 [MarshalAs(UnmanagedType.Bool)] ref bool fCancel 168 [MarshalAs(UnmanagedType.Bool)] ref bool fCancel
179 ); 169 );
@@ -289,11 +279,6 @@ namespace WixToolset.Mba.Core
289 /// <summary> 279 /// <summary>
290 /// See <see cref="IDefaultBootstrapperApplication.PlanRelatedBundle"/>. 280 /// See <see cref="IDefaultBootstrapperApplication.PlanRelatedBundle"/>.
291 /// </summary> 281 /// </summary>
292 /// <param name="wzBundleId"></param>
293 /// <param name="recommendedState"></param>
294 /// <param name="pRequestedState"></param>
295 /// <param name="fCancel"></param>
296 /// <returns></returns>
297 [PreserveSig] 282 [PreserveSig]
298 [return: MarshalAs(UnmanagedType.I4)] 283 [return: MarshalAs(UnmanagedType.I4)]
299 int OnPlanRelatedBundle( 284 int OnPlanRelatedBundle(
diff --git a/src/api/burn/balutil/inc/BalBaseBAFunctions.h b/src/api/burn/balutil/inc/BalBaseBAFunctions.h
index 7f52f76e..22e16f1b 100644
--- a/src/api/burn/balutil/inc/BalBaseBAFunctions.h
+++ b/src/api/burn/balutil/inc/BalBaseBAFunctions.h
@@ -167,7 +167,6 @@ public: // IBootstrapperApplication
167 __in_z LPCWSTR /*wzBundleTag*/, 167 __in_z LPCWSTR /*wzBundleTag*/,
168 __in BOOL /*fPerMachine*/, 168 __in BOOL /*fPerMachine*/,
169 __in LPCWSTR /*wzVersion*/, 169 __in LPCWSTR /*wzVersion*/,
170 __in BOOTSTRAPPER_RELATED_OPERATION /*operation*/,
171 __in BOOL /*fMissingFromCache*/, 170 __in BOOL /*fMissingFromCache*/,
172 __inout BOOL* /*pfCancel*/ 171 __inout BOOL* /*pfCancel*/
173 ) 172 )
diff --git a/src/api/burn/balutil/inc/BalBaseBootstrapperApplication.h b/src/api/burn/balutil/inc/BalBaseBootstrapperApplication.h
index 5665fee3..631d3c62 100644
--- a/src/api/burn/balutil/inc/BalBaseBootstrapperApplication.h
+++ b/src/api/burn/balutil/inc/BalBaseBootstrapperApplication.h
@@ -172,7 +172,6 @@ public: // IBootstrapperApplication
172 __in_z LPCWSTR /*wzBundleTag*/, 172 __in_z LPCWSTR /*wzBundleTag*/,
173 __in BOOL /*fPerMachine*/, 173 __in BOOL /*fPerMachine*/,
174 __in LPCWSTR /*wzVersion*/, 174 __in LPCWSTR /*wzVersion*/,
175 __in BOOTSTRAPPER_RELATED_OPERATION /*operation*/,
176 __in BOOL /*fMissingFromCache*/, 175 __in BOOL /*fMissingFromCache*/,
177 __inout BOOL* pfCancel 176 __inout BOOL* pfCancel
178 ) 177 )
diff --git a/src/api/burn/balutil/inc/BalBaseBootstrapperApplicationProc.h b/src/api/burn/balutil/inc/BalBaseBootstrapperApplicationProc.h
index 2292cd64..b9866e4b 100644
--- a/src/api/burn/balutil/inc/BalBaseBootstrapperApplicationProc.h
+++ b/src/api/burn/balutil/inc/BalBaseBootstrapperApplicationProc.h
@@ -114,7 +114,7 @@ static HRESULT BalBaseBAProcOnDetectRelatedBundle(
114 __inout BA_ONDETECTRELATEDBUNDLE_RESULTS* pResults 114 __inout BA_ONDETECTRELATEDBUNDLE_RESULTS* pResults
115 ) 115 )
116{ 116{
117 return pBA->OnDetectRelatedBundle(pArgs->wzBundleId, pArgs->relationType, pArgs->wzBundleTag, pArgs->fPerMachine, pArgs->wzVersion, pArgs->operation, pArgs->fMissingFromCache, &pResults->fCancel); 117 return pBA->OnDetectRelatedBundle(pArgs->wzBundleId, pArgs->relationType, pArgs->wzBundleTag, pArgs->fPerMachine, pArgs->wzVersion, pArgs->fMissingFromCache, &pResults->fCancel);
118} 118}
119 119
120static HRESULT BalBaseBAProcOnDetectPackageBegin( 120static HRESULT BalBaseBAProcOnDetectPackageBegin(
diff --git a/src/api/burn/balutil/inc/IBootstrapperApplication.h b/src/api/burn/balutil/inc/IBootstrapperApplication.h
index 2ba1f503..577a705b 100644
--- a/src/api/burn/balutil/inc/IBootstrapperApplication.h
+++ b/src/api/burn/balutil/inc/IBootstrapperApplication.h
@@ -91,7 +91,6 @@ DECLARE_INTERFACE_IID_(IBootstrapperApplication, IUnknown, "53C31D56-49C0-426B-A
91 __in_z LPCWSTR wzBundleTag, 91 __in_z LPCWSTR wzBundleTag,
92 __in BOOL fPerMachine, 92 __in BOOL fPerMachine,
93 __in_z LPCWSTR wzVersion, 93 __in_z LPCWSTR wzVersion,
94 __in BOOTSTRAPPER_RELATED_OPERATION operation,
95 __in BOOL fMissingFromCache, 94 __in BOOL fMissingFromCache,
96 __inout BOOL* pfCancel 95 __inout BOOL* pfCancel
97 ) = 0; 96 ) = 0;
diff --git a/src/burn/engine/core.cpp b/src/burn/engine/core.cpp
index 96845655..30c64b01 100644
--- a/src/burn/engine/core.cpp
+++ b/src/burn/engine/core.cpp
@@ -359,7 +359,7 @@ extern "C" HRESULT CoreDetect(
359 ExitOnFailure(hr, "Failed to detect provider key bundle id."); 359 ExitOnFailure(hr, "Failed to detect provider key bundle id.");
360 360
361 // Report the related bundles. 361 // Report the related bundles.
362 hr = DetectReportRelatedBundles(&pEngineState->userExperience, &pEngineState->registration, pEngineState->command.relationType, pEngineState->command.action, &pEngineState->registration.fEligibleForCleanup); 362 hr = DetectReportRelatedBundles(&pEngineState->userExperience, &pEngineState->registration, pEngineState->command.relationType, &pEngineState->registration.fEligibleForCleanup);
363 ExitOnFailure(hr, "Failed to report detected related bundles."); 363 ExitOnFailure(hr, "Failed to report detected related bundles.");
364 364
365 // Do update detection. 365 // Do update detection.
diff --git a/src/burn/engine/detect.cpp b/src/burn/engine/detect.cpp
index 4eda240e..be828366 100644
--- a/src/burn/engine/detect.cpp
+++ b/src/burn/engine/detect.cpp
@@ -154,68 +154,20 @@ extern "C" HRESULT DetectReportRelatedBundles(
154 __in BURN_USER_EXPERIENCE* pUX, 154 __in BURN_USER_EXPERIENCE* pUX,
155 __in BURN_REGISTRATION* pRegistration, 155 __in BURN_REGISTRATION* pRegistration,
156 __in BOOTSTRAPPER_RELATION_TYPE relationType, 156 __in BOOTSTRAPPER_RELATION_TYPE relationType,
157 __in BOOTSTRAPPER_ACTION action,
158 __out BOOL* pfEligibleForCleanup 157 __out BOOL* pfEligibleForCleanup
159 ) 158 )
160{ 159{
161 HRESULT hr = S_OK; 160 HRESULT hr = S_OK;
162 int nCompareResult = 0;
163 BOOTSTRAPPER_REQUEST_STATE uninstallRequestState = BOOTSTRAPPER_REQUEST_STATE_NONE; 161 BOOTSTRAPPER_REQUEST_STATE uninstallRequestState = BOOTSTRAPPER_REQUEST_STATE_NONE;
164 *pfEligibleForCleanup = pRegistration->fInstalled || pRegistration->fCached; 162 *pfEligibleForCleanup = pRegistration->fInstalled || pRegistration->fCached;
165 163
166 for (DWORD iRelatedBundle = 0; iRelatedBundle < pRegistration->relatedBundles.cRelatedBundles; ++iRelatedBundle) 164 for (DWORD iRelatedBundle = 0; iRelatedBundle < pRegistration->relatedBundles.cRelatedBundles; ++iRelatedBundle)
167 { 165 {
168 const BURN_RELATED_BUNDLE* pRelatedBundle = pRegistration->relatedBundles.rgRelatedBundles + iRelatedBundle; 166 const BURN_RELATED_BUNDLE* pRelatedBundle = pRegistration->relatedBundles.rgRelatedBundles + iRelatedBundle;
169 BOOTSTRAPPER_RELATED_OPERATION operation = BOOTSTRAPPER_RELATED_OPERATION_NONE;
170
171 switch (pRelatedBundle->relationType)
172 {
173 case BOOTSTRAPPER_RELATION_UPGRADE:
174 if (BOOTSTRAPPER_RELATION_UPGRADE != relationType && BOOTSTRAPPER_ACTION_UNINSTALL < action)
175 {
176 hr = VerCompareParsedVersions(pRegistration->pVersion, pRelatedBundle->pVersion, &nCompareResult);
177 ExitOnFailure(hr, "Failed to compare bundle version '%ls' to related bundle version '%ls'", pRegistration->pVersion->sczVersion, pRelatedBundle->pVersion->sczVersion);
178
179 if (nCompareResult < 0)
180 {
181 operation = BOOTSTRAPPER_RELATED_OPERATION_DOWNGRADE;
182 }
183 else
184 {
185 operation = BOOTSTRAPPER_RELATED_OPERATION_MAJOR_UPGRADE;
186 }
187 }
188 break;
189
190 case BOOTSTRAPPER_RELATION_PATCH: __fallthrough;
191 case BOOTSTRAPPER_RELATION_ADDON:
192 if (BOOTSTRAPPER_ACTION_UNINSTALL == action)
193 {
194 operation = BOOTSTRAPPER_RELATED_OPERATION_REMOVE;
195 }
196 else if (BOOTSTRAPPER_ACTION_INSTALL == action || BOOTSTRAPPER_ACTION_MODIFY == action)
197 {
198 operation = BOOTSTRAPPER_RELATED_OPERATION_INSTALL;
199 }
200 else if (BOOTSTRAPPER_ACTION_REPAIR == action)
201 {
202 operation = BOOTSTRAPPER_RELATED_OPERATION_REPAIR;
203 }
204 break;
205
206 case BOOTSTRAPPER_RELATION_DETECT: __fallthrough;
207 case BOOTSTRAPPER_RELATION_DEPENDENT:
208 break;
209
210 default:
211 hr = E_FAIL;
212 ExitOnRootFailure(hr, "Unexpected relation type encountered: %d", pRelatedBundle->relationType);
213 break;
214 }
215 167
216 LogId(REPORT_STANDARD, MSG_DETECTED_RELATED_BUNDLE, pRelatedBundle->package.sczId, LoggingRelationTypeToString(pRelatedBundle->relationType), LoggingPerMachineToString(pRelatedBundle->package.fPerMachine), pRelatedBundle->pVersion->sczVersion, LoggingRelatedOperationToString(operation), LoggingBoolToString(pRelatedBundle->package.fCached)); 168 LogId(REPORT_STANDARD, MSG_DETECTED_RELATED_BUNDLE, pRelatedBundle->package.sczId, LoggingRelationTypeToString(pRelatedBundle->relationType), LoggingPerMachineToString(pRelatedBundle->package.fPerMachine), pRelatedBundle->pVersion->sczVersion, LoggingBoolToString(pRelatedBundle->package.fCached));
217 169
218 hr = UserExperienceOnDetectRelatedBundle(pUX, pRelatedBundle->package.sczId, pRelatedBundle->relationType, pRelatedBundle->sczTag, pRelatedBundle->package.fPerMachine, pRelatedBundle->pVersion, operation, !pRelatedBundle->package.fCached); 170 hr = UserExperienceOnDetectRelatedBundle(pUX, pRelatedBundle->package.sczId, pRelatedBundle->relationType, pRelatedBundle->sczTag, pRelatedBundle->package.fPerMachine, pRelatedBundle->pVersion, !pRelatedBundle->package.fCached);
219 ExitOnRootFailure(hr, "BA aborted detect related bundle."); 171 ExitOnRootFailure(hr, "BA aborted detect related bundle.");
220 172
221 // For now, if any related bundles will be executed during uninstall by default then never automatically clean up the bundle. 173 // For now, if any related bundles will be executed during uninstall by default then never automatically clean up the bundle.
diff --git a/src/burn/engine/detect.h b/src/burn/engine/detect.h
index 9bc34882..cdca2777 100644
--- a/src/burn/engine/detect.h
+++ b/src/burn/engine/detect.h
@@ -29,7 +29,6 @@ HRESULT DetectReportRelatedBundles(
29 __in BURN_USER_EXPERIENCE* pUX, 29 __in BURN_USER_EXPERIENCE* pUX,
30 __in BURN_REGISTRATION* pRegistration, 30 __in BURN_REGISTRATION* pRegistration,
31 __in BOOTSTRAPPER_RELATION_TYPE relationType, 31 __in BOOTSTRAPPER_RELATION_TYPE relationType,
32 __in BOOTSTRAPPER_ACTION action,
33 __out BOOL* pfEligibleForCleanup 32 __out BOOL* pfEligibleForCleanup
34 ); 33 );
35 34
diff --git a/src/burn/engine/engine.mc b/src/burn/engine/engine.mc
index dde4d40b..5c1b0b69 100644
--- a/src/burn/engine/engine.mc
+++ b/src/burn/engine/engine.mc
@@ -223,7 +223,7 @@ MessageId=102
223Severity=Success 223Severity=Success
224SymbolicName=MSG_DETECTED_RELATED_BUNDLE 224SymbolicName=MSG_DETECTED_RELATED_BUNDLE
225Language=English 225Language=English
226Detected related bundle: %1!ls!, type: %2!hs!, scope: %3!hs!, version: %4!ls!, operation: %5!hs!, cached: %6!hs! 226Detected related bundle: %1!ls!, type: %2!hs!, scope: %3!hs!, version: %4!ls!, cached: %5!hs!
227. 227.
228 228
229MessageId=103 229MessageId=103
diff --git a/src/burn/engine/userexperience.cpp b/src/burn/engine/userexperience.cpp
index ee22a318..6ea16905 100644
--- a/src/burn/engine/userexperience.cpp
+++ b/src/burn/engine/userexperience.cpp
@@ -104,7 +104,7 @@ extern "C" HRESULT UserExperienceLoad(
104 args.pCommand = pCommand; 104 args.pCommand = pCommand;
105 args.pfnBootstrapperEngineProc = EngineForApplicationProc; 105 args.pfnBootstrapperEngineProc = EngineForApplicationProc;
106 args.pvBootstrapperEngineProcContext = pEngineContext; 106 args.pvBootstrapperEngineProcContext = pEngineContext;
107 args.qwEngineAPIVersion = MAKEQWORDVERSION(2021, 12, 7, 0); 107 args.qwEngineAPIVersion = MAKEQWORDVERSION(2021, 12, 30, 0);
108 108
109 results.cbSize = sizeof(BOOTSTRAPPER_CREATE_RESULTS); 109 results.cbSize = sizeof(BOOTSTRAPPER_CREATE_RESULTS);
110 110
@@ -1164,7 +1164,6 @@ EXTERN_C BAAPI UserExperienceOnDetectRelatedBundle(
1164 __in_z LPCWSTR wzBundleTag, 1164 __in_z LPCWSTR wzBundleTag,
1165 __in BOOL fPerMachine, 1165 __in BOOL fPerMachine,
1166 __in VERUTIL_VERSION* pVersion, 1166 __in VERUTIL_VERSION* pVersion,
1167 __in BOOTSTRAPPER_RELATED_OPERATION operation,
1168 __in BOOL fMissingFromCache 1167 __in BOOL fMissingFromCache
1169 ) 1168 )
1170{ 1169{
@@ -1178,7 +1177,6 @@ EXTERN_C BAAPI UserExperienceOnDetectRelatedBundle(
1178 args.wzBundleTag = wzBundleTag; 1177 args.wzBundleTag = wzBundleTag;
1179 args.fPerMachine = fPerMachine; 1178 args.fPerMachine = fPerMachine;
1180 args.wzVersion = pVersion->sczVersion; 1179 args.wzVersion = pVersion->sczVersion;
1181 args.operation = operation;
1182 args.fMissingFromCache = fMissingFromCache; 1180 args.fMissingFromCache = fMissingFromCache;
1183 1181
1184 results.cbSize = sizeof(results); 1182 results.cbSize = sizeof(results);
diff --git a/src/burn/engine/userexperience.h b/src/burn/engine/userexperience.h
index 27d0a1e3..e4c5d3ee 100644
--- a/src/burn/engine/userexperience.h
+++ b/src/burn/engine/userexperience.h
@@ -286,7 +286,6 @@ BAAPI UserExperienceOnDetectRelatedBundle(
286 __in_z LPCWSTR wzBundleTag, 286 __in_z LPCWSTR wzBundleTag,
287 __in BOOL fPerMachine, 287 __in BOOL fPerMachine,
288 __in VERUTIL_VERSION* pVersion, 288 __in VERUTIL_VERSION* pVersion,
289 __in BOOTSTRAPPER_RELATED_OPERATION operation,
290 __in BOOL fMissingFromCache 289 __in BOOL fMissingFromCache
291 ); 290 );
292BAAPI UserExperienceOnDetectRelatedMsiPackage( 291BAAPI UserExperienceOnDetectRelatedMsiPackage(
diff --git a/src/ext/Bal/wixstdba/WixStandardBootstrapperApplication.cpp b/src/ext/Bal/wixstdba/WixStandardBootstrapperApplication.cpp
index ec3c268a..7b70d772 100644
--- a/src/ext/Bal/wixstdba/WixStandardBootstrapperApplication.cpp
+++ b/src/ext/Bal/wixstdba/WixStandardBootstrapperApplication.cpp
@@ -246,12 +246,12 @@ public: // IBootstrapperApplication
246 __in LPCWSTR wzBundleTag, 246 __in LPCWSTR wzBundleTag,
247 __in BOOL fPerMachine, 247 __in BOOL fPerMachine,
248 __in LPCWSTR wzVersion, 248 __in LPCWSTR wzVersion,
249 __in BOOTSTRAPPER_RELATED_OPERATION operation,
250 __in BOOL fMissingFromCache, 249 __in BOOL fMissingFromCache,
251 __inout BOOL* pfCancel 250 __inout BOOL* pfCancel
252 ) 251 )
253 { 252 {
254 BAL_INFO_PACKAGE* pPackage = NULL; 253 BAL_INFO_PACKAGE* pPackage = NULL;
254 int nCompare = 0;
255 255
256 if (!fMissingFromCache) 256 if (!fMissingFromCache)
257 { 257 {
@@ -261,14 +261,15 @@ public: // IBootstrapperApplication
261 } 261 }
262 262
263 // If we're not doing a prerequisite install, remember when our bundle would cause a downgrade. 263 // If we're not doing a prerequisite install, remember when our bundle would cause a downgrade.
264 if (!m_fPrereq && BOOTSTRAPPER_RELATED_OPERATION_DOWNGRADE == operation) 264 if (!m_fPrereq && BOOTSTRAPPER_RELATION_UPGRADE == relationType &&
265 SUCCEEDED(m_pEngine->CompareVersions(m_sczBundleVersion, wzVersion, &nCompare)) && 0 > nCompare)
265 { 266 {
266 BalLog(BOOTSTRAPPER_LOG_LEVEL_STANDARD, "A newer version (v%ls) of this product is installed.", wzVersion); 267 BalLog(BOOTSTRAPPER_LOG_LEVEL_STANDARD, "A newer version (v%ls) of this product is installed.", wzVersion);
267 m_fDowngrading = TRUE; 268 m_fDowngrading = TRUE;
268 } 269 }
269 } 270 }
270 271
271 return CBalBaseBootstrapperApplication::OnDetectRelatedBundle(wzBundleId, relationType, wzBundleTag, fPerMachine, wzVersion, operation, fMissingFromCache, pfCancel); 272 return CBalBaseBootstrapperApplication::OnDetectRelatedBundle(wzBundleId, relationType, wzBundleTag, fPerMachine, wzVersion, fMissingFromCache, pfCancel);
272 } 273 }
273 274
274 275
@@ -2091,7 +2092,8 @@ public: //CBalBaseBootstrapperApplication
2091 } 2092 }
2092 } 2093 }
2093 2094
2094 hr = S_OK; 2095 hr = BalGetStringVariable(L"WixBundleVersion", &m_sczBundleVersion);
2096 BalExitOnFailure(hr, "CWixStandardBootstrapperApplication initialization failed.");
2095 2097
2096 LExit: 2098 LExit:
2097 return hr; 2099 return hr;
@@ -4129,6 +4131,7 @@ public:
4129 m_plannedAction = BOOTSTRAPPER_ACTION_UNKNOWN; 4131 m_plannedAction = BOOTSTRAPPER_ACTION_UNKNOWN;
4130 4132
4131 m_sczAfterForcedRestartPackage = NULL; 4133 m_sczAfterForcedRestartPackage = NULL;
4134 m_sczBundleVersion = NULL;
4132 4135
4133 m_pWixLoc = NULL; 4136 m_pWixLoc = NULL;
4134 m_Bundle = { }; 4137 m_Bundle = { };
@@ -4367,6 +4370,7 @@ public:
4367 ReleaseStr(m_sczLanguage); 4370 ReleaseStr(m_sczLanguage);
4368 ReleaseStr(m_sczLicenseFile); 4371 ReleaseStr(m_sczLicenseFile);
4369 ReleaseStr(m_sczLicenseUrl); 4372 ReleaseStr(m_sczLicenseUrl);
4373 ReleaseStr(m_sczBundleVersion);
4370 ReleaseStr(m_sczAfterForcedRestartPackage); 4374 ReleaseStr(m_sczAfterForcedRestartPackage);
4371 ReleaseNullObject(m_pEngine); 4375 ReleaseNullObject(m_pEngine);
4372 4376
@@ -4391,6 +4395,7 @@ private:
4391 BOOTSTRAPPER_ACTION m_plannedAction; 4395 BOOTSTRAPPER_ACTION m_plannedAction;
4392 4396
4393 LPWSTR m_sczAfterForcedRestartPackage; 4397 LPWSTR m_sczAfterForcedRestartPackage;
4398 LPWSTR m_sczBundleVersion;
4394 4399
4395 WIX_LOCALIZATION* m_pWixLoc; 4400 WIX_LOCALIZATION* m_pWixLoc;
4396 BAL_INFO_BUNDLE m_Bundle; 4401 BAL_INFO_BUNDLE m_Bundle;
diff --git a/src/test/burn/TestData/WixStdBaTests/BundleA_v10/BundleA_v10.wixproj b/src/test/burn/TestData/WixStdBaTests/BundleA_v10/BundleA_v10.wixproj
new file mode 100644
index 00000000..602dc4bc
--- /dev/null
+++ b/src/test/burn/TestData/WixStdBaTests/BundleA_v10/BundleA_v10.wixproj
@@ -0,0 +1,20 @@
1<!-- Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. -->
2<Project Sdk="WixToolset.Sdk">
3 <PropertyGroup>
4 <OutputType>Bundle</OutputType>
5 <BA>hyperlinkLicense</BA>
6 <UpgradeCode>{7D977157-06C9-4176-A931-AC16E18AAB51}</UpgradeCode>
7 <DefineConstants>$(DefineConstants);Version=1.0</DefineConstants>
8 <OutputName>WixStdBaTest1_v10</OutputName>
9 </PropertyGroup>
10 <ItemGroup>
11 <Compile Include="..\..\Templates\Bundle.wxs" Link="Bundle.wxs" />
12 <Compile Include="..\BundleA\BundleA.wxs" Link="BundleA.wxs" />
13 </ItemGroup>
14 <ItemGroup>
15 <ProjectReference Include="..\PackageA\PackageA.wixproj" />
16 </ItemGroup>
17 <ItemGroup>
18 <PackageReference Include="WixToolset.Bal.wixext" />
19 </ItemGroup>
20</Project> \ No newline at end of file
diff --git a/src/test/burn/WixToolset.WixBA/InstallationViewModel.cs b/src/test/burn/WixToolset.WixBA/InstallationViewModel.cs
index 1846d51b..e056b943 100644
--- a/src/test/burn/WixToolset.WixBA/InstallationViewModel.cs
+++ b/src/test/burn/WixToolset.WixBA/InstallationViewModel.cs
@@ -19,6 +19,18 @@ namespace WixToolset.WixBA
19 { 19 {
20 Absent, 20 Absent,
21 Present, 21 Present,
22 }
23
24 /// <summary>
25 /// The states of upgrade detection.
26 /// </summary>
27 public enum UpgradeDetectionState
28 {
29 // There are no Upgrade related bundles installed.
30 None,
31 // All Upgrade related bundles that are installed are older than or the same version as this bundle.
32 Older,
33 // At least one Upgrade related bundle is installed that is newer than this bundle.
22 Newer, 34 Newer,
23 } 35 }
24 36
@@ -90,7 +102,7 @@ namespace WixToolset.WixBA
90 102
91 void RootPropertyChanged(object sender, PropertyChangedEventArgs e) 103 void RootPropertyChanged(object sender, PropertyChangedEventArgs e)
92 { 104 {
93 if (("DetectState" == e.PropertyName) || ("InstallState" == e.PropertyName)) 105 if (("DetectState" == e.PropertyName) || ("UpgradeDetectState" == e.PropertyName) || ("InstallState" == e.PropertyName))
94 { 106 {
95 base.OnPropertyChanged("RepairEnabled"); 107 base.OnPropertyChanged("RepairEnabled");
96 base.OnPropertyChanged("InstallEnabled"); 108 base.OnPropertyChanged("InstallEnabled");
@@ -276,7 +288,9 @@ namespace WixToolset.WixBA
276 { 288 {
277 if (this.installCommand == null) 289 if (this.installCommand == null)
278 { 290 {
279 this.installCommand = new RelayCommand(param => WixBA.Plan(LaunchAction.Install), param => this.root.DetectState == DetectionState.Absent && this.root.InstallState == InstallationState.Waiting); 291 this.installCommand = new RelayCommand(
292 param => WixBA.Plan(LaunchAction.Install),
293 param => this.root.DetectState == DetectionState.Absent && this.root.UpgradeDetectState != UpgradeDetectionState.Newer && this.root.InstallState == InstallationState.Waiting);
280 } 294 }
281 295
282 return this.installCommand; 296 return this.installCommand;
@@ -399,9 +413,19 @@ namespace WixToolset.WixBA
399 413
400 private void DetectedRelatedBundle(object sender, DetectRelatedBundleEventArgs e) 414 private void DetectedRelatedBundle(object sender, DetectRelatedBundleEventArgs e)
401 { 415 {
402 if (e.Operation == RelatedOperation.Downgrade) 416 if (e.RelationType == RelationType.Upgrade)
403 { 417 {
404 this.Downgrade = true; 418 if (WixBA.Model.Engine.CompareVersions(this.Version, e.Version) > 0)
419 {
420 if (this.root.UpgradeDetectState == UpgradeDetectionState.None)
421 {
422 this.root.UpgradeDetectState = UpgradeDetectionState.Older;
423 }
424 }
425 else
426 {
427 this.root.UpgradeDetectState = UpgradeDetectionState.Newer;
428 }
405 } 429 }
406 430
407 if (!WixBA.Model.BAManifest.Bundle.Packages.ContainsKey(e.ProductCode)) 431 if (!WixBA.Model.BAManifest.Bundle.Packages.ContainsKey(e.ProductCode))
@@ -432,19 +456,10 @@ namespace WixToolset.WixBA
432 } 456 }
433 else if (Hresult.Succeeded(e.Status)) 457 else if (Hresult.Succeeded(e.Status))
434 { 458 {
435 if (this.Downgrade) 459 if (this.root.UpgradeDetectState == UpgradeDetectionState.Newer)
436 { 460 {
437 this.root.DetectState = DetectionState.Newer; 461 this.Downgrade = true;
438 var relatedPackages = WixBA.Model.BAManifest.Bundle.Packages.Values.Where(p => p.Type == PackageType.UpgradeBundle); 462 this.DowngradeMessage = "There is already a newer version of WiX installed on this machine.";
439 var installedVersion = relatedPackages.Any() ? new Version(relatedPackages.Max(p => p.Version)) : null;
440 if (installedVersion != null && installedVersion < new Version(4, 1) && installedVersion.Build > 10)
441 {
442 this.DowngradeMessage = "You must uninstall WiX v" + installedVersion + " before you can install this.";
443 }
444 else
445 {
446 this.DowngradeMessage = "There is already a newer version of WiX installed on this machine.";
447 }
448 } 463 }
449 464
450 if (LaunchAction.Layout == WixBA.Model.Command.Action) 465 if (LaunchAction.Layout == WixBA.Model.Command.Action)
diff --git a/src/test/burn/WixToolset.WixBA/RootViewModel.cs b/src/test/burn/WixToolset.WixBA/RootViewModel.cs
index 8cff7274..2dfd214e 100644
--- a/src/test/burn/WixToolset.WixBA/RootViewModel.cs
+++ b/src/test/burn/WixToolset.WixBA/RootViewModel.cs
@@ -27,6 +27,7 @@ namespace WixToolset.WixBA
27 private bool canceled; 27 private bool canceled;
28 private InstallationState installState; 28 private InstallationState installState;
29 private DetectionState detectState; 29 private DetectionState detectState;
30 private UpgradeDetectionState upgradeDetectState;
30 31
31 /// <summary> 32 /// <summary>
32 /// Creates a new model of the root view. 33 /// Creates a new model of the root view.
@@ -120,6 +121,28 @@ namespace WixToolset.WixBA
120 } 121 }
121 122
122 /// <summary> 123 /// <summary>
124 /// Gets and sets the upgrade detect state of the view's model.
125 /// </summary>
126 public UpgradeDetectionState UpgradeDetectState
127 {
128 get
129 {
130 return this.upgradeDetectState;
131 }
132
133 set
134 {
135 if (this.upgradeDetectState != value)
136 {
137 this.upgradeDetectState = value;
138
139 // Notify all the properties derived from the state that the state changed.
140 base.OnPropertyChanged("UpgradeDetectState");
141 }
142 }
143 }
144
145 /// <summary>
123 /// Gets and sets the installation state of the view's model. 146 /// Gets and sets the installation state of the view's model.
124 /// </summary> 147 /// </summary>
125 public InstallationState InstallState 148 public InstallationState InstallState
diff --git a/src/test/burn/WixToolsetTest.BurnE2E/DependencyTests.cs b/src/test/burn/WixToolsetTest.BurnE2E/DependencyTests.cs
index e2975fc9..309241d9 100644
--- a/src/test/burn/WixToolsetTest.BurnE2E/DependencyTests.cs
+++ b/src/test/burn/WixToolsetTest.BurnE2E/DependencyTests.cs
@@ -596,7 +596,7 @@ namespace WixToolsetTest.BurnE2E
596 packageDv2.VerifyInstalled(true); 596 packageDv2.VerifyInstalled(true);
597 597
598 Assert.True(LogVerifier.MessageInLogFileRegex(bundleHv2InstallLogFilePath, @"Skipping cross-scope dependency registration on package: PackageA, bundle scope: PerUser, package scope: PerMachine")); 598 Assert.True(LogVerifier.MessageInLogFileRegex(bundleHv2InstallLogFilePath, @"Skipping cross-scope dependency registration on package: PackageA, bundle scope: PerUser, package scope: PerMachine"));
599 Assert.True(LogVerifier.MessageInLogFileRegex(bundleHv2InstallLogFilePath, @"Detected related bundle: \{[0-9A-Za-z\-]{36}\}, type: Upgrade, scope: PerUser, version: 1\.0\.0\.0, operation: MajorUpgrade, cached: Yes")); 599 Assert.True(LogVerifier.MessageInLogFileRegex(bundleHv2InstallLogFilePath, @"Detected related bundle: \{[0-9A-Za-z\-]{36}\}, type: Upgrade, scope: PerUser, version: 1\.0\.0\.0, cached: Yes"));
600 600
601 bundleHv2.Uninstall(); 601 bundleHv2.Uninstall();
602 bundleHv2.VerifyUnregisteredAndRemovedFromPackageCache(); 602 bundleHv2.VerifyUnregisteredAndRemovedFromPackageCache();
diff --git a/src/test/burn/WixToolsetTest.BurnE2E/UpgradeRelatedBundleTests.cs b/src/test/burn/WixToolsetTest.BurnE2E/UpgradeRelatedBundleTests.cs
index 70c0c474..35cc64f0 100644
--- a/src/test/burn/WixToolsetTest.BurnE2E/UpgradeRelatedBundleTests.cs
+++ b/src/test/burn/WixToolsetTest.BurnE2E/UpgradeRelatedBundleTests.cs
@@ -30,7 +30,7 @@ namespace WixToolsetTest.BurnE2E
30 bundleAv2.VerifyRegisteredAndInPackageCache(); 30 bundleAv2.VerifyRegisteredAndInPackageCache();
31 31
32 Assert.True(LogVerifier.MessageInLogFileRegex(bundleAv2InstallLogFilePath, @"OnDetectRelatedBundle\(\) - id: \{[0-9A-Za-z\-]{36}\}, missing from cache: True")); 32 Assert.True(LogVerifier.MessageInLogFileRegex(bundleAv2InstallLogFilePath, @"OnDetectRelatedBundle\(\) - id: \{[0-9A-Za-z\-]{36}\}, missing from cache: True"));
33 Assert.True(LogVerifier.MessageInLogFileRegex(bundleAv2InstallLogFilePath, @"Detected related bundle: \{[0-9A-Za-z\-]{36}\}, type: Upgrade, scope: PerMachine, version: 1\.0\.0\.0, operation: MajorUpgrade, cached: No")); 33 Assert.True(LogVerifier.MessageInLogFileRegex(bundleAv2InstallLogFilePath, @"Detected related bundle: \{[0-9A-Za-z\-]{36}\}, type: Upgrade, scope: PerMachine, version: 1\.0\.0\.0, cached: No"));
34 } 34 }
35 } 35 }
36} 36}