aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSean Hall <r.sean.hall@gmail.com>2022-04-13 10:12:50 -0500
committerSean Hall <r.sean.hall@gmail.com>2022-04-13 13:13:48 -0500
commit866413ec39c573a50b7ec0753f643918a1939dee (patch)
tree2470d79151e45a379a98cc7b1f54ba08a7c0057e
parent17d4ba9d93814cffa688e9152d11a340f9e0f754 (diff)
downloadwix-866413ec39c573a50b7ec0753f643918a1939dee.tar.gz
wix-866413ec39c573a50b7ec0753f643918a1939dee.tar.bz2
wix-866413ec39c573a50b7ec0753f643918a1939dee.zip
Only allow MsiPackage and MspPackage inside MSI transactions.
Improve 64-bit package detection.
-rw-r--r--src/api/wix/WixToolset.Data/ErrorMessages.cs24
-rw-r--r--src/api/wix/WixToolset.Data/WarningMessages.cs6
-rw-r--r--src/wix/WixToolset.Core.Burn/Bundles/OrderPackagesAndRollbackBoundariesCommand.cs33
-rw-r--r--src/wix/WixToolset.Core.Burn/Bundles/ProcessMsiPackageCommand.cs26
-rw-r--r--src/wix/WixToolset.Core.Burn/BurnBackendWarnings.cs14
-rw-r--r--src/wix/test/WixToolsetTest.CoreIntegration/MsiTransactionFixture.cs31
-rw-r--r--src/wix/test/WixToolsetTest.CoreIntegration/RollbackBoundaryFixture.cs1
-rw-r--r--src/wix/test/WixToolsetTest.CoreIntegration/TestData/MsiTransaction/ExeInMsiTransactionBundle.wxs9
8 files changed, 124 insertions, 20 deletions
diff --git a/src/api/wix/WixToolset.Data/ErrorMessages.cs b/src/api/wix/WixToolset.Data/ErrorMessages.cs
index a833452d..186acd29 100644
--- a/src/api/wix/WixToolset.Data/ErrorMessages.cs
+++ b/src/api/wix/WixToolset.Data/ErrorMessages.cs
@@ -1494,9 +1494,24 @@ namespace WixToolset.Data
1494 return Message(null, Ids.MissingValidatorExtension, "The validator requires at least one extension. Add \"ValidatorExtension, Wix\" for the default implementation."); 1494 return Message(null, Ids.MissingValidatorExtension, "The validator requires at least one extension. Add \"ValidatorExtension, Wix\" for the default implementation.");
1495 } 1495 }
1496 1496
1497 public static Message MsiTransactionX86BeforeX64(SourceLineNumber sourceLineNumbers) 1497 public static Message MsiTransactionInvalidPackage(SourceLineNumber sourceLineNumbers, string packageId, string packageType)
1498 { 1498 {
1499 return Message(sourceLineNumbers, Ids.MsiTransactionX86BeforeX64, "MSI transactions must install all x64 packages before any x86 package."); 1499 return Message(sourceLineNumbers, Ids.MsiTransactionInvalidPackage, "Invalid package '{0}' in MSI transaction. It is type '{1}' but must be Msi or Msp.", packageId, packageType);
1500 }
1501
1502 public static Message MsiTransactionInvalidPackage2(SourceLineNumber sourceLineNumbers)
1503 {
1504 return Message(sourceLineNumbers, Ids.MsiTransactionInvalidPackage2, "Location of rollback boundary related to previous error.");
1505 }
1506
1507 public static Message MsiTransactionX86BeforeX64Package(SourceLineNumber sourceLineNumbers, string x64PackageId, string x86PackageId)
1508 {
1509 return Message(sourceLineNumbers, Ids.MsiTransactionX86BeforeX64Package, "Package '{0}' is x64 but Package '{1}' is x86. MSI transactions must install all x64 packages before any x86 package.", x64PackageId, x86PackageId);
1510 }
1511
1512 public static Message MsiTransactionX86BeforeX64Package2(SourceLineNumber sourceLineNumbers)
1513 {
1514 return Message(sourceLineNumbers, Ids.MsiTransactionX86BeforeX64Package2, "Location of x86 package related to previous error.");
1500 } 1515 }
1501 1516
1502 public static Message MultipleEntrySections(SourceLineNumber sourceLineNumbers, string sectionName1, string sectionName2) 1517 public static Message MultipleEntrySections(SourceLineNumber sourceLineNumbers, string sectionName1, string sectionName2)
@@ -2672,7 +2687,7 @@ namespace WixToolset.Data
2672 InlineDirectorySyntaxRequiresPath = 387, 2687 InlineDirectorySyntaxRequiresPath = 387,
2673 InsecureBundleFilename = 388, 2688 InsecureBundleFilename = 388,
2674 PayloadMustBeRelativeToCache = 389, 2689 PayloadMustBeRelativeToCache = 389,
2675 MsiTransactionX86BeforeX64 = 390, 2690 MsiTransactionX86BeforeX64Package = 390,
2676 NoSourceFiles = 391, 2691 NoSourceFiles = 391,
2677 WixiplSourceFileIsExclusive = 392, 2692 WixiplSourceFileIsExclusive = 392,
2678 UnableToConvertFieldToNumber = 393, 2693 UnableToConvertFieldToNumber = 393,
@@ -2692,6 +2707,9 @@ namespace WixToolset.Data
2692 MissingPackagePayload = 407, 2707 MissingPackagePayload = 407,
2693 ExpectedAttributeWithoutOtherAttributes = 408, 2708 ExpectedAttributeWithoutOtherAttributes = 408,
2694 InvalidBundleCondition = 409, 2709 InvalidBundleCondition = 409,
2710 MsiTransactionX86BeforeX64Package2 = 410,
2711 MsiTransactionInvalidPackage = 411,
2712 MsiTransactionInvalidPackage2 = 412,
2695 } 2713 }
2696 } 2714 }
2697} 2715}
diff --git a/src/api/wix/WixToolset.Data/WarningMessages.cs b/src/api/wix/WixToolset.Data/WarningMessages.cs
index 0c026b68..ecd76392 100644
--- a/src/api/wix/WixToolset.Data/WarningMessages.cs
+++ b/src/api/wix/WixToolset.Data/WarningMessages.cs
@@ -222,6 +222,11 @@ namespace WixToolset.Data
222 return Message(sourceLineNumbers, Ids.DiscardedRollbackBoundary, "The RollbackBoundary '{0}' was discarded because it was not followed by a package. Without a package the rollback boundary doesn't do anything. Verify that the RollbackBoundary element is not followed by another RollbackBoundary and that the element is not at the end of the chain.", rollbackBoundaryId); 222 return Message(sourceLineNumbers, Ids.DiscardedRollbackBoundary, "The RollbackBoundary '{0}' was discarded because it was not followed by a package. Without a package the rollback boundary doesn't do anything. Verify that the RollbackBoundary element is not followed by another RollbackBoundary and that the element is not at the end of the chain.", rollbackBoundaryId);
223 } 223 }
224 224
225 public static Message DiscardedRollbackBoundary2(SourceLineNumber sourceLineNumbers)
226 {
227 return Message(sourceLineNumbers, Ids.DiscardedRollbackBoundary2, "Location of rollback boundary related to previous warning.");
228 }
229
225 public static Message DiscouragedAllUsersValue(SourceLineNumber sourceLineNumbers, string path, string machineOrUser) 230 public static Message DiscouragedAllUsersValue(SourceLineNumber sourceLineNumbers, string path, string machineOrUser)
226 { 231 {
227 return Message(sourceLineNumbers, Ids.DiscouragedAllUsersValue, "Bundles require a package to be either per-machine or per-user. The MSI '{0}' ALLUSERS Property is set to '2' which may change from per-user to per-machine at install time. The Bundle will assume the package is per-{1} and will not work correctly if that changes. If possible, remove the Property with Id='ALLUSERS' and use Package/@InstallScope attribute instead.", path, machineOrUser); 232 return Message(sourceLineNumbers, Ids.DiscouragedAllUsersValue, "Bundles require a package to be either per-machine or per-user. The MSI '{0}' ALLUSERS Property is set to '2' which may change from per-user to per-machine at install time. The Bundle will assume the package is per-{1} and will not work correctly if that changes. If possible, remove the Property with Id='ALLUSERS' and use Package/@InstallScope attribute instead.", path, machineOrUser);
@@ -821,6 +826,7 @@ namespace WixToolset.Data
821 InvalidEnvironmentVariable = 1157, 826 InvalidEnvironmentVariable = 1157,
822 WindowsInstallerFileTooLarge = 1158, 827 WindowsInstallerFileTooLarge = 1158,
823 UnavailableBundleConditionVariable = 1159, 828 UnavailableBundleConditionVariable = 1159,
829 DiscardedRollbackBoundary2 = 1160,
824 } 830 }
825 } 831 }
826} 832}
diff --git a/src/wix/WixToolset.Core.Burn/Bundles/OrderPackagesAndRollbackBoundariesCommand.cs b/src/wix/WixToolset.Core.Burn/Bundles/OrderPackagesAndRollbackBoundariesCommand.cs
index df798583..87c10190 100644
--- a/src/wix/WixToolset.Core.Burn/Bundles/OrderPackagesAndRollbackBoundariesCommand.cs
+++ b/src/wix/WixToolset.Core.Burn/Bundles/OrderPackagesAndRollbackBoundariesCommand.cs
@@ -48,7 +48,7 @@ namespace WixToolset.Core.Burn.Bundles
48 // defined. 48 // defined.
49 var pendingRollbackBoundary = new WixBundleRollbackBoundarySymbol(null, new Identifier(AccessModifier.Section, BurnConstants.BundleDefaultBoundaryId)) { Vital = true }; 49 var pendingRollbackBoundary = new WixBundleRollbackBoundarySymbol(null, new Identifier(AccessModifier.Section, BurnConstants.BundleDefaultBoundaryId)) { Vital = true };
50 var lastRollbackBoundary = pendingRollbackBoundary; 50 var lastRollbackBoundary = pendingRollbackBoundary;
51 var boundaryHadX86Package = false; 51 PackageFacade msiTransactionX86Package = null;
52 var warnedMsiTransaction = false; 52 var warnedMsiTransaction = false;
53 53
54 foreach (var groupSymbol in groupSymbols) 54 foreach (var groupSymbol in groupSymbols)
@@ -57,7 +57,7 @@ namespace WixToolset.Core.Burn.Bundles
57 { 57 {
58 if (this.PackageFacades.TryGetValue(groupSymbol.ChildId, out var facade)) 58 if (this.PackageFacades.TryGetValue(groupSymbol.ChildId, out var facade))
59 { 59 {
60 var insideMsiTransaction = lastRollbackBoundary?.Transaction ?? false; 60 var insideMsiTransaction = lastRollbackBoundary.Transaction ?? false;
61 61
62 if (null != pendingRollbackBoundary) 62 if (null != pendingRollbackBoundary)
63 { 63 {
@@ -76,18 +76,32 @@ namespace WixToolset.Core.Burn.Bundles
76 usedBoundaries.Add(pendingRollbackBoundary); 76 usedBoundaries.Add(pendingRollbackBoundary);
77 facade.PackageSymbol.RollbackBoundaryRef = pendingRollbackBoundary.Id.Id; 77 facade.PackageSymbol.RollbackBoundaryRef = pendingRollbackBoundary.Id.Id;
78 pendingRollbackBoundary = null; 78 pendingRollbackBoundary = null;
79 79 msiTransactionX86Package = null;
80 boundaryHadX86Package = !facade.PackageSymbol.Win64;
81 } 80 }
82 81
83 // Error if MSI transaction has x86 package preceding x64 packages 82 if (insideMsiTransaction)
84 if (insideMsiTransaction && boundaryHadX86Package && facade.PackageSymbol.Win64)
85 { 83 {
86 this.Messaging.Write(ErrorMessages.MsiTransactionX86BeforeX64(facade.PackageSymbol.SourceLineNumbers)); 84 if (facade.PackageSymbol.Type != WixBundlePackageType.Msi && facade.PackageSymbol.Type != WixBundlePackageType.Msp)
85 {
86 this.Messaging.Write(ErrorMessages.MsiTransactionInvalidPackage(facade.PackageSymbol.SourceLineNumbers, facade.PackageId, facade.PackageSymbol.Type.ToString()));
87 this.Messaging.Write(ErrorMessages.MsiTransactionInvalidPackage2(lastRollbackBoundary.SourceLineNumbers));
88 }
89 // Not possible to tell the bitness of Msp.
90 else if (facade.PackageSymbol.Type == WixBundlePackageType.Msi)
91 {
92 if (msiTransactionX86Package == null && !facade.PackageSymbol.Win64)
93 {
94 msiTransactionX86Package = facade;
95 }
96 // Error if MSI transaction has x86 package preceding x64 packages
97 else if (msiTransactionX86Package != null && facade.PackageSymbol.Win64)
98 {
99 this.Messaging.Write(ErrorMessages.MsiTransactionX86BeforeX64Package(facade.PackageSymbol.SourceLineNumbers, facade.PackageId, msiTransactionX86Package.PackageId));
100 this.Messaging.Write(ErrorMessages.MsiTransactionX86BeforeX64Package2(msiTransactionX86Package.PackageSymbol.SourceLineNumbers));
101 }
102 }
87 } 103 }
88 104
89 boundaryHadX86Package |= !facade.PackageSymbol.Win64;
90
91 orderedFacades.Add(facade); 105 orderedFacades.Add(facade);
92 } 106 }
93 else // must be a rollback boundary. 107 else // must be a rollback boundary.
@@ -97,6 +111,7 @@ namespace WixToolset.Core.Burn.Bundles
97 if (null != pendingRollbackBoundary && pendingRollbackBoundary.Id.Id != BurnConstants.BundleDefaultBoundaryId) 111 if (null != pendingRollbackBoundary && pendingRollbackBoundary.Id.Id != BurnConstants.BundleDefaultBoundaryId)
98 { 112 {
99 this.Messaging.Write(WarningMessages.DiscardedRollbackBoundary(nextRollbackBoundary.SourceLineNumbers, nextRollbackBoundary.Id.Id)); 113 this.Messaging.Write(WarningMessages.DiscardedRollbackBoundary(nextRollbackBoundary.SourceLineNumbers, nextRollbackBoundary.Id.Id));
114 this.Messaging.Write(WarningMessages.DiscardedRollbackBoundary2(lastRollbackBoundary.SourceLineNumbers));
100 } 115 }
101 else 116 else
102 { 117 {
diff --git a/src/wix/WixToolset.Core.Burn/Bundles/ProcessMsiPackageCommand.cs b/src/wix/WixToolset.Core.Burn/Bundles/ProcessMsiPackageCommand.cs
index d9560cb0..c2bceb32 100644
--- a/src/wix/WixToolset.Core.Burn/Bundles/ProcessMsiPackageCommand.cs
+++ b/src/wix/WixToolset.Core.Burn/Bundles/ProcessMsiPackageCommand.cs
@@ -87,10 +87,9 @@ namespace WixToolset.Core.Burn.Bundles
87 // "Elevated privileges are not required to install this package." 87 // "Elevated privileges are not required to install this package."
88 // in MSI 4.5 and below, if this bit is 0, elevation is required. 88 // in MSI 4.5 and below, if this bit is 0, elevation is required.
89 var perMachine = (0 == (fileAndElevateFlags & 8)); 89 var perMachine = (0 == (fileAndElevateFlags & 8));
90 var x64 = platformsAndLanguages.Contains("x64");
91 90
92 this.Facade.PackageSymbol.PerMachine = perMachine ? YesNoDefaultType.Yes : YesNoDefaultType.No; 91 this.Facade.PackageSymbol.PerMachine = perMachine ? YesNoDefaultType.Yes : YesNoDefaultType.No;
93 this.Facade.PackageSymbol.Win64 = x64; 92 this.Facade.PackageSymbol.Win64 = this.IsWin64(packagePayload.SourceLineNumbers, sourcePath, platformsAndLanguages);
94 } 93 }
95 94
96 string packageName = null; 95 string packageName = null;
@@ -216,6 +215,29 @@ namespace WixToolset.Core.Burn.Bundles
216 return new HashSet<string>(properties, StringComparer.Ordinal); 215 return new HashSet<string>(properties, StringComparer.Ordinal);
217 } 216 }
218 217
218 // https://docs.microsoft.com/en-us/windows/win32/msi/template-summary
219 private bool IsWin64(SourceLineNumber sourceLineNumbers, string sourcePath, string platformsAndLanguages)
220 {
221 var separatorIndex = platformsAndLanguages.IndexOf(';');
222 var platformValue = separatorIndex > 0 ? platformsAndLanguages.Substring(0, separatorIndex) : platformsAndLanguages;
223
224 switch (platformValue)
225 {
226 case "Arm64":
227 case "Intel64":
228 case "x64":
229 return true;
230
231 case "Arm":
232 case "Intel":
233 return false;
234
235 default:
236 this.Messaging.Write(BurnBackendWarnings.UnknownMsiPackagePlatform(sourceLineNumbers, sourcePath, platformValue));
237 return true;
238 }
239 }
240
219 private void SetPerMachineAppropriately(string allusers, WixBundleMsiPackageSymbol msiPackage, string sourcePath) 241 private void SetPerMachineAppropriately(string allusers, WixBundleMsiPackageSymbol msiPackage, string sourcePath)
220 { 242 {
221 // Can ignore ALLUSERS from MsiProperties because it is not allowed there. 243 // Can ignore ALLUSERS from MsiProperties because it is not allowed there.
diff --git a/src/wix/WixToolset.Core.Burn/BurnBackendWarnings.cs b/src/wix/WixToolset.Core.Burn/BurnBackendWarnings.cs
index 1f9a4102..7235c792 100644
--- a/src/wix/WixToolset.Core.Burn/BurnBackendWarnings.cs
+++ b/src/wix/WixToolset.Core.Burn/BurnBackendWarnings.cs
@@ -26,9 +26,9 @@ namespace WixToolset.Core.Burn
26 return Message(sourceLineNumbers, Ids.FailedToExtractAttachedContainers, "Failed to extract attached container. This most often happens when extracting a stripped bundle from the package cache, which is not supported."); 26 return Message(sourceLineNumbers, Ids.FailedToExtractAttachedContainers, "Failed to extract attached container. This most often happens when extracting a stripped bundle from the package cache, which is not supported.");
27 } 27 }
28 28
29 public static Message UnknownCoffMachineType(SourceLineNumber sourceLineNumbers, string bundleExecutable, ushort machineType) 29 public static Message HiddenBundleNotSupported(SourceLineNumber sourceLineNumbers, string bundleExecutable)
30 { 30 {
31 return Message(sourceLineNumbers, Ids.UnknownCoffMachineType, "The bundle '{0}' has an unknown COFF machine type: {1}. It is assumed to be 32-bit.", bundleExecutable, machineType); 31 return Message(sourceLineNumbers, Ids.HiddenBundleNotSupported, "The bundle '{0}' does not support hiding its ARP registration.", bundleExecutable);
32 } 32 }
33 33
34 public static Message UnknownBundleRelationAction(SourceLineNumber sourceLineNumbers, string bundleExecutable, string action) 34 public static Message UnknownBundleRelationAction(SourceLineNumber sourceLineNumbers, string bundleExecutable, string action)
@@ -36,9 +36,14 @@ namespace WixToolset.Core.Burn
36 return Message(sourceLineNumbers, Ids.UnknownBundleRelationAction, "The manifest for the bundle '{0}' contains an unknown related bundle action '{1}'. It will be ignored.", bundleExecutable, action); 36 return Message(sourceLineNumbers, Ids.UnknownBundleRelationAction, "The manifest for the bundle '{0}' contains an unknown related bundle action '{1}'. It will be ignored.", bundleExecutable, action);
37 } 37 }
38 38
39 public static Message HiddenBundleNotSupported(SourceLineNumber sourceLineNumbers, string bundleExecutable) 39 public static Message UnknownCoffMachineType(SourceLineNumber sourceLineNumbers, string bundleExecutable, ushort machineType)
40 { 40 {
41 return Message(sourceLineNumbers, Ids.HiddenBundleNotSupported, "The bundle '{0}' does not support hiding its ARP registration.", bundleExecutable); 41 return Message(sourceLineNumbers, Ids.UnknownCoffMachineType, "The bundle '{0}' has an unknown COFF machine type: {1}. It is assumed to be 32-bit.", bundleExecutable, machineType);
42 }
43
44 public static Message UnknownMsiPackagePlatform(SourceLineNumber sourceLineNumbers, string msiPath, string platform)
45 {
46 return Message(sourceLineNumbers, Ids.UnknownMsiPackagePlatform, "The MsiPackage '{0}' has an unknown platform: '{1}'. It is assumed to be 64-bit.", msiPath, platform);
42 } 47 }
43 48
44 private static Message Message(SourceLineNumber sourceLineNumber, Ids id, string format, params object[] args) 49 private static Message Message(SourceLineNumber sourceLineNumber, Ids id, string format, params object[] args)
@@ -55,6 +60,7 @@ namespace WixToolset.Core.Burn
55 UnknownCoffMachineType = 8504, 60 UnknownCoffMachineType = 8504,
56 UnknownBundleRelationAction = 8505, 61 UnknownBundleRelationAction = 8505,
57 HiddenBundleNotSupported = 8506, 62 HiddenBundleNotSupported = 8506,
63 UnknownMsiPackagePlatform = 8507,
58 } // last available is 8999. 9000 is VerboseMessages. 64 } // last available is 8999. 9000 is VerboseMessages.
59 } 65 }
60} 66}
diff --git a/src/wix/test/WixToolsetTest.CoreIntegration/MsiTransactionFixture.cs b/src/wix/test/WixToolsetTest.CoreIntegration/MsiTransactionFixture.cs
index 573ae4b4..431c131b 100644
--- a/src/wix/test/WixToolsetTest.CoreIntegration/MsiTransactionFixture.cs
+++ b/src/wix/test/WixToolsetTest.CoreIntegration/MsiTransactionFixture.cs
@@ -11,7 +11,34 @@ namespace WixToolsetTest.CoreIntegration
11 public class MsiTransactionFixture 11 public class MsiTransactionFixture
12 { 12 {
13 [Fact] 13 [Fact]
14 public void CantBuildX64AfterX86Bundle() 14 public void CannotBuildExePackageInMsiTransaction()
15 {
16 var folder = TestData.Get(@"TestData");
17
18 using (var fs = new DisposableFileSystem())
19 {
20 var baseFolder = fs.GetFolder();
21 var intermediateFolder = Path.Combine(baseFolder, "obj");
22 var exePath = Path.Combine(baseFolder, @"bin\test.exe");
23
24 var result = WixRunner.Execute(new[]
25 {
26 "build",
27 "-sw1151", // this is expected for this test
28 Path.Combine(folder, "MsiTransaction", "ExeInMsiTransactionBundle.wxs"),
29 Path.Combine(folder, "BundleWithPackageGroupRef", "Bundle.wxs"),
30 "-bindpath", Path.Combine(folder, "SimpleBundle", "data"),
31 "-bindpath", Path.Combine(folder, ".Data"),
32 "-intermediateFolder", intermediateFolder,
33 "-o", exePath,
34 });
35
36 Assert.Equal(412, result.ExitCode);
37 }
38 }
39
40 [Fact]
41 public void CannotBuildX64AfterX86Bundle()
15 { 42 {
16 var folder = TestData.Get(@"TestData"); 43 var folder = TestData.Get(@"TestData");
17 44
@@ -36,7 +63,7 @@ namespace WixToolsetTest.CoreIntegration
36 "-o", exePath, 63 "-o", exePath,
37 }); 64 });
38 65
39 Assert.Equal(390, result.ExitCode); 66 Assert.Equal(410, result.ExitCode);
40 } 67 }
41 } 68 }
42 69
diff --git a/src/wix/test/WixToolsetTest.CoreIntegration/RollbackBoundaryFixture.cs b/src/wix/test/WixToolsetTest.CoreIntegration/RollbackBoundaryFixture.cs
index c69ff894..3a79878d 100644
--- a/src/wix/test/WixToolsetTest.CoreIntegration/RollbackBoundaryFixture.cs
+++ b/src/wix/test/WixToolsetTest.CoreIntegration/RollbackBoundaryFixture.cs
@@ -103,6 +103,7 @@ namespace WixToolsetTest.CoreIntegration
103 WixAssert.CompareLineByLine(new[] 103 WixAssert.CompareLineByLine(new[]
104 { 104 {
105 "The RollbackBoundary 'Second' was discarded because it was not followed by a package. Without a package the rollback boundary doesn't do anything. Verify that the RollbackBoundary element is not followed by another RollbackBoundary and that the element is not at the end of the chain.", 105 "The RollbackBoundary 'Second' was discarded because it was not followed by a package. Without a package the rollback boundary doesn't do anything. Verify that the RollbackBoundary element is not followed by another RollbackBoundary and that the element is not at the end of the chain.",
106 "Location of rollback boundary related to previous warning.",
106 }, result.Messages.Select(m => m.ToString()).ToArray()); 107 }, result.Messages.Select(m => m.ToString()).ToArray());
107 108
108 Assert.True(File.Exists(exePath)); 109 Assert.True(File.Exists(exePath));
diff --git a/src/wix/test/WixToolsetTest.CoreIntegration/TestData/MsiTransaction/ExeInMsiTransactionBundle.wxs b/src/wix/test/WixToolsetTest.CoreIntegration/TestData/MsiTransaction/ExeInMsiTransactionBundle.wxs
new file mode 100644
index 00000000..23bd47a1
--- /dev/null
+++ b/src/wix/test/WixToolsetTest.CoreIntegration/TestData/MsiTransaction/ExeInMsiTransactionBundle.wxs
@@ -0,0 +1,9 @@
1<?xml version="1.0" encoding="utf-8"?>
2<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs">
3 <Fragment>
4 <PackageGroup Id="BundlePackages">
5 <RollbackBoundary Id="HasExe" Transaction="yes" />
6 <ExePackage SourceFile="burn.exe" DetectCondition="none" Permanent="yes" />
7 </PackageGroup>
8 </Fragment>
9</Wix>