aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorSean Hall <r.sean.hall@gmail.com>2021-11-02 17:47:46 -0500
committerSean Hall <r.sean.hall@gmail.com>2021-11-03 10:53:41 -0500
commit323f62d3d0f4b73db5fde8977e2540194c6de006 (patch)
treeefcbb140502f5c274052bcc1ef86e202e8bf6a7b /src
parentce3aea757a01f0eea906fa610501a66735ef3a15 (diff)
downloadwix-323f62d3d0f4b73db5fde8977e2540194c6de006.tar.gz
wix-323f62d3d0f4b73db5fde8977e2540194c6de006.tar.bz2
wix-323f62d3d0f4b73db5fde8977e2540194c6de006.zip
Follow up for multiple attached container support
* validate cContainers * use previous embeddedid format and use intermediate folder when extracting attached containers * remove special cases for 0 byte containers in BurnCommon classes and Insignia * don't hardcode max containers * reduce properties in BurnCommon * add e2e test #6144
Diffstat (limited to 'src')
-rw-r--r--src/burn/engine/section.cpp24
-rw-r--r--src/burn/stub/StubSection.cpp3
-rw-r--r--src/test/burn/TestData/ContainerTests/BundleA/BundleA.wixproj19
-rw-r--r--src/test/burn/TestData/ContainerTests/BundleA/BundleA.wxs17
-rw-r--r--src/test/burn/TestData/ContainerTests/PackageA/PackageA.wixproj10
-rw-r--r--src/test/burn/TestData/ContainerTests/PackageB/PackageB.wixproj10
-rw-r--r--src/test/burn/WixToolsetTest.BurnE2E/ContainerTests.cs29
-rw-r--r--src/wix/WixToolset.Core.Burn/Bind/BindBundleCommand.cs4
-rw-r--r--src/wix/WixToolset.Core.Burn/BundleBackend.cs2
-rw-r--r--src/wix/WixToolset.Core.Burn/Bundles/BurnCommon.cs61
-rw-r--r--src/wix/WixToolset.Core.Burn/Bundles/BurnReader.cs24
-rw-r--r--src/wix/WixToolset.Core.Burn/Bundles/BurnWriter.cs80
-rw-r--r--src/wix/WixToolset.Core.Burn/Inscribe/InscribeBundleCommand.cs17
-rw-r--r--src/wix/WixToolset.Core.TestPackage/BundleExtractor.cs42
-rw-r--r--src/wix/WixToolset.Core.TestPackage/ExtractBAContainerResult.cs6
-rw-r--r--src/wix/test/WixToolsetTest.CoreIntegration/BundleFixture.cs8
-rw-r--r--src/wix/test/WixToolsetTest.CoreIntegration/ContainerFixture.cs58
-rw-r--r--src/wix/test/WixToolsetTest.CoreIntegration/PackagePayloadFixture.cs3
-rw-r--r--src/wix/test/WixToolsetTest.CoreIntegration/PayloadFixture.cs17
19 files changed, 276 insertions, 158 deletions
diff --git a/src/burn/engine/section.cpp b/src/burn/engine/section.cpp
index 1fd6cce4..a9c7927e 100644
--- a/src/burn/engine/section.cpp
+++ b/src/burn/engine/section.cpp
@@ -26,7 +26,7 @@ typedef struct _BURN_SECTION_HEADER
26 26
27 DWORD dwFormat; 27 DWORD dwFormat;
28 DWORD cContainers; 28 DWORD cContainers;
29 DWORD rgcbContainers[116]; 29 DWORD rgcbContainers[1];
30} BURN_SECTION_HEADER; 30} BURN_SECTION_HEADER;
31 31
32static HRESULT VerifySectionMatchesMemoryPEHeader( 32static HRESULT VerifySectionMatchesMemoryPEHeader(
@@ -53,6 +53,7 @@ extern "C" HRESULT SectionInitialize(
53 IMAGE_SECTION_HEADER sectionHeader = { }; 53 IMAGE_SECTION_HEADER sectionHeader = { };
54 DWORD_PTR dwOriginalChecksumAndSignatureOffset = 0; 54 DWORD_PTR dwOriginalChecksumAndSignatureOffset = 0;
55 BURN_SECTION_HEADER* pBurnSectionHeader = NULL; 55 BURN_SECTION_HEADER* pBurnSectionHeader = NULL;
56 DWORD cMaxContainers = 0;
56 57
57 pSection->hEngineFile = hEngineFile; 58 pSection->hEngineFile = hEngineFile;
58 ExitOnInvalidHandleWithLastError(pSection->hEngineFile, hr, "Failed to open handle to engine process path."); 59 ExitOnInvalidHandleWithLastError(pSection->hEngineFile, hr, "Failed to open handle to engine process path.");
@@ -142,8 +143,7 @@ extern "C" HRESULT SectionInitialize(
142 } 143 }
143 if (sizeof(IMAGE_SECTION_HEADER) > cbRead) 144 if (sizeof(IMAGE_SECTION_HEADER) > cbRead)
144 { 145 {
145 hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); 146 ExitWithRootFailure(hr, E_INVALIDDATA, "Failed to read complete image section header, index: %u", i);
146 ExitOnRootFailure(hr, "Failed to read complete image section header, index: %u", i);
147 } 147 }
148 148
149 // compare header name 149 // compare header name
@@ -156,8 +156,7 @@ extern "C" HRESULT SectionInitialize(
156 // fail if we hit the end 156 // fail if we hit the end
157 if (i + 1 >= ntHeader.FileHeader.NumberOfSections) 157 if (i + 1 >= ntHeader.FileHeader.NumberOfSections)
158 { 158 {
159 hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); 159 ExitWithRootFailure(hr, E_INVALIDDATA, "Failed to find Burn section.");
160 ExitOnRootFailure(hr, "Failed to find Burn section.");
161 } 160 }
162 } 161 }
163 162
@@ -168,8 +167,7 @@ extern "C" HRESULT SectionInitialize(
168 // check size of section 167 // check size of section
169 if (sizeof(BURN_SECTION_HEADER) > sectionHeader.SizeOfRawData) 168 if (sizeof(BURN_SECTION_HEADER) > sectionHeader.SizeOfRawData)
170 { 169 {
171 hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); 170 ExitWithRootFailure(hr, E_INVALIDDATA, "Failed to read section info, data too short: %u", sectionHeader.SizeOfRawData);
172 ExitOnRootFailure(hr, "Failed to read section info, data to short: %u", sectionHeader.SizeOfRawData);
173 } 171 }
174 172
175 // allocate buffer for section info 173 // allocate buffer for section info
@@ -193,15 +191,19 @@ extern "C" HRESULT SectionInitialize(
193 } 191 }
194 else if (sectionHeader.SizeOfRawData > cbRead) 192 else if (sectionHeader.SizeOfRawData > cbRead)
195 { 193 {
196 hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); 194 ExitWithRootFailure(hr, E_INVALIDDATA, "Failed to read complete section info.");
197 ExitOnRootFailure(hr, "Failed to read complete section info.");
198 } 195 }
199 196
200 // validate version of section info 197 // validate version of section info
201 if (BURN_SECTION_VERSION != pBurnSectionHeader->dwVersion) 198 if (BURN_SECTION_VERSION != pBurnSectionHeader->dwVersion)
202 { 199 {
203 hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); 200 ExitWithRootFailure(hr, E_INVALIDDATA, "Failed to read section info, unsupported version: %08x", pBurnSectionHeader->dwVersion);
204 ExitOnRootFailure(hr, "Failed to read section info, unsupported version: %08x", pBurnSectionHeader->dwVersion); 201 }
202
203 cMaxContainers = (sectionHeader.SizeOfRawData - offsetof(BURN_SECTION_HEADER, rgcbContainers)) / sizeof(DWORD);
204 if (cMaxContainers < pBurnSectionHeader->cContainers)
205 {
206 ExitWithRootFailure(hr, E_INVALIDDATA, "Invalid section info, cContainers too large: %u", pBurnSectionHeader->cContainers);
205 } 207 }
206 208
207 hr = FileSizeByHandle(pSection->hSourceEngineFile, &llSize); 209 hr = FileSizeByHandle(pSection->hSourceEngineFile, &llSize);
diff --git a/src/burn/stub/StubSection.cpp b/src/burn/stub/StubSection.cpp
index 01b4b576..2191a138 100644
--- a/src/burn/stub/StubSection.cpp
+++ b/src/burn/stub/StubSection.cpp
@@ -18,5 +18,6 @@ static DWORD dwOriginalSignatureSize = 0;
18 18
19static DWORD dwContainerFormat = 1; 19static DWORD dwContainerFormat = 1;
20static DWORD dwContainerCount = 0; 20static DWORD dwContainerCount = 0;
21static DWORD qwAttachedContainerSizes[116]; // Including UX container 21// (512 (minimum section size) - 48 (size of above data)) / 4 (size of DWORD)
22static DWORD qwAttachedContainerSizes[116];
22#pragma data_seg(pop) 23#pragma data_seg(pop)
diff --git a/src/test/burn/TestData/ContainerTests/BundleA/BundleA.wixproj b/src/test/burn/TestData/ContainerTests/BundleA/BundleA.wixproj
new file mode 100644
index 00000000..1c2f1651
--- /dev/null
+++ b/src/test/burn/TestData/ContainerTests/BundleA/BundleA.wixproj
@@ -0,0 +1,19 @@
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 <UpgradeCode>{16798DF3-C365-410D-B376-E63A961E4822}</UpgradeCode>
6 </PropertyGroup>
7 <ItemGroup>
8 <Compile Include="..\..\Templates\Bundle.wxs" Link="Bundle.wxs" />
9 </ItemGroup>
10 <ItemGroup>
11 <ProjectReference Include="..\PackageA\PackageA.wixproj" />
12 <ProjectReference Include="..\PackageB\PackageB.wixproj" />
13 <ProjectReference Include="..\..\TestBA\TestBAWixlib\testbawixlib.wixproj" />
14 </ItemGroup>
15 <ItemGroup>
16 <PackageReference Include="WixToolset.Bal.wixext" />
17 <PackageReference Include="WixToolset.NetFx.wixext" />
18 </ItemGroup>
19</Project> \ No newline at end of file
diff --git a/src/test/burn/TestData/ContainerTests/BundleA/BundleA.wxs b/src/test/burn/TestData/ContainerTests/BundleA/BundleA.wxs
new file mode 100644
index 00000000..7933eca5
--- /dev/null
+++ b/src/test/burn/TestData/ContainerTests/BundleA/BundleA.wxs
@@ -0,0 +1,17 @@
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 <MsiPackage Id="PackageA" SourceFile="$(var.PackageA.TargetPath)" />
8 <PackageGroupRef Id="PackageB" />
9 </PackageGroup>
10 <PackageGroup Id="PackageB">
11 <MsiPackage Id="PackageB" SourceFile="$(var.PackageB.TargetPath)" />
12 </PackageGroup>
13 <Container Id="CustomAttachedContainer" Name="CustomAttachedContainer" Type="attached">
14 <PackageGroupRef Id="PackageB" />
15 </Container>
16 </Fragment>
17</Wix>
diff --git a/src/test/burn/TestData/ContainerTests/PackageA/PackageA.wixproj b/src/test/burn/TestData/ContainerTests/PackageA/PackageA.wixproj
new file mode 100644
index 00000000..67dfd894
--- /dev/null
+++ b/src/test/burn/TestData/ContainerTests/PackageA/PackageA.wixproj
@@ -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<Project Sdk="WixToolset.Sdk">
3 <PropertyGroup>
4 <CabPrefix>a</CabPrefix>
5 <UpgradeCode>{D452A40D-27B2-41A1-A103-4FD5744B548E}</UpgradeCode>
6 </PropertyGroup>
7 <ItemGroup>
8 <Compile Include="..\..\Templates\Package.wxs" Link="Package.wxs" />
9 </ItemGroup>
10</Project> \ No newline at end of file
diff --git a/src/test/burn/TestData/ContainerTests/PackageB/PackageB.wixproj b/src/test/burn/TestData/ContainerTests/PackageB/PackageB.wixproj
new file mode 100644
index 00000000..6ef92662
--- /dev/null
+++ b/src/test/burn/TestData/ContainerTests/PackageB/PackageB.wixproj
@@ -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<Project Sdk="WixToolset.Sdk">
3 <PropertyGroup>
4 <CabPrefix>b</CabPrefix>
5 <UpgradeCode>{EB8E7A16-9855-4019-90D6-F5A242A75250}</UpgradeCode>
6 </PropertyGroup>
7 <ItemGroup>
8 <Compile Include="..\..\Templates\Package.wxs" Link="Package.wxs" />
9 </ItemGroup>
10</Project> \ No newline at end of file
diff --git a/src/test/burn/WixToolsetTest.BurnE2E/ContainerTests.cs b/src/test/burn/WixToolsetTest.BurnE2E/ContainerTests.cs
new file mode 100644
index 00000000..cd6beaa9
--- /dev/null
+++ b/src/test/burn/WixToolsetTest.BurnE2E/ContainerTests.cs
@@ -0,0 +1,29 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3namespace WixToolsetTest.BurnE2E
4{
5 using Xunit;
6 using Xunit.Abstractions;
7
8 public class ContainerTests : BurnE2ETests
9 {
10 public ContainerTests(ITestOutputHelper testOutputHelper) : base(testOutputHelper) { }
11
12 [Fact]
13 public void CanSupportMultipleAttachedContainers()
14 {
15 var packageA = this.CreatePackageInstaller("PackageA");
16 var packageB = this.CreatePackageInstaller("PackageB");
17 var bundleA = this.CreateBundleInstaller("BundleA");
18
19 packageA.VerifyInstalled(false);
20 packageB.VerifyInstalled(false);
21
22 bundleA.Install();
23 bundleA.VerifyRegisteredAndInPackageCache();
24
25 packageA.VerifyInstalled(true);
26 packageB.VerifyInstalled(true);
27 }
28 }
29}
diff --git a/src/wix/WixToolset.Core.Burn/Bind/BindBundleCommand.cs b/src/wix/WixToolset.Core.Burn/Bind/BindBundleCommand.cs
index a60d3ddf..cd00232a 100644
--- a/src/wix/WixToolset.Core.Burn/Bind/BindBundleCommand.cs
+++ b/src/wix/WixToolset.Core.Burn/Bind/BindBundleCommand.cs
@@ -296,13 +296,15 @@ namespace WixToolset.Core.Burn
296 } 296 }
297 297
298 // Give the embedded payloads without an embedded id yet an embedded id. 298 // Give the embedded payloads without an embedded id yet an embedded id.
299 var payloadIndex = 0;
299 foreach (var payload in payloadSymbols.Values) 300 foreach (var payload in payloadSymbols.Values)
300 { 301 {
301 Debug.Assert(PackagingType.Unknown != payload.Packaging); 302 Debug.Assert(PackagingType.Unknown != payload.Packaging);
302 303
303 if (PackagingType.Embedded == payload.Packaging && String.IsNullOrEmpty(payload.EmbeddedId)) 304 if (PackagingType.Embedded == payload.Packaging && String.IsNullOrEmpty(payload.EmbeddedId))
304 { 305 {
305 payload.EmbeddedId = Guid.NewGuid().ToString("N"); 306 payload.EmbeddedId = String.Format(CultureInfo.InvariantCulture, BurnCommon.BurnAuthoredContainerEmbeddedIdFormat, payloadIndex);
307 ++payloadIndex;
306 } 308 }
307 } 309 }
308 } 310 }
diff --git a/src/wix/WixToolset.Core.Burn/BundleBackend.cs b/src/wix/WixToolset.Core.Burn/BundleBackend.cs
index 83572cda..518b77c8 100644
--- a/src/wix/WixToolset.Core.Burn/BundleBackend.cs
+++ b/src/wix/WixToolset.Core.Burn/BundleBackend.cs
@@ -67,7 +67,7 @@ namespace WixToolset.Core.Burn
67 using (var reader = BurnReader.Open(messaging, context.InputFilePath)) 67 using (var reader = BurnReader.Open(messaging, context.InputFilePath))
68 { 68 {
69 reader.ExtractUXContainer(uxExtractPath, context.IntermediateFolder); 69 reader.ExtractUXContainer(uxExtractPath, context.IntermediateFolder);
70 reader.ExtractAttachedContainers(context.ExportBasePath); 70 reader.ExtractAttachedContainers(context.ExportBasePath, context.IntermediateFolder);
71 } 71 }
72 72
73 return null; 73 return null;
diff --git a/src/wix/WixToolset.Core.Burn/Bundles/BurnCommon.cs b/src/wix/WixToolset.Core.Burn/Bundles/BurnCommon.cs
index 6a9d7950..27fad5b3 100644
--- a/src/wix/WixToolset.Core.Burn/Bundles/BurnCommon.cs
+++ b/src/wix/WixToolset.Core.Burn/Bundles/BurnCommon.cs
@@ -20,6 +20,7 @@ namespace WixToolset.Core.Burn.Bundles
20 { 20 {
21 public const string BurnNamespace = "http://wixtoolset.org/schemas/v4/2008/Burn"; 21 public const string BurnNamespace = "http://wixtoolset.org/schemas/v4/2008/Burn";
22 public const string BurnUXContainerEmbeddedIdFormat = "u{0}"; 22 public const string BurnUXContainerEmbeddedIdFormat = "u{0}";
23 public const string BurnAuthoredContainerEmbeddedIdFormat = "a{0}";
23 24
24 public const string BADataFileName = "BootstrapperApplicationData.xml"; 25 public const string BADataFileName = "BootstrapperApplicationData.xml";
25 public const string BADataNamespace = "http://wixtoolset.org/schemas/v4/BootstrapperApplicationData"; 26 public const string BADataNamespace = "http://wixtoolset.org/schemas/v4/BootstrapperApplicationData";
@@ -79,12 +80,11 @@ namespace WixToolset.Core.Burn.Bundles
79 protected const UInt32 BURN_SECTION_OFFSET_COUNT = 44; 80 protected const UInt32 BURN_SECTION_OFFSET_COUNT = 44;
80 protected const UInt32 BURN_SECTION_OFFSET_UXSIZE = 48; 81 protected const UInt32 BURN_SECTION_OFFSET_UXSIZE = 48;
81 protected const UInt32 BURN_SECTION_OFFSET_ATTACHEDCONTAINERSIZE0 = 52; 82 protected const UInt32 BURN_SECTION_OFFSET_ATTACHEDCONTAINERSIZE0 = 52;
83 protected const UInt32 BURN_SECTION_MIN_SIZE = BURN_SECTION_OFFSET_ATTACHEDCONTAINERSIZE0;
82 84
83 protected const UInt32 BURN_SECTION_MAGIC = 0x00f14300; 85 protected const UInt32 BURN_SECTION_MAGIC = 0x00f14300;
84 protected const UInt32 BURN_SECTION_VERSION = 0x00000003; 86 protected const UInt32 BURN_SECTION_VERSION = 0x00000003;
85 protected const UInt32 BURN_SECTION_COMPATIBLE_VERSION = 0x00000002; 87 protected const UInt32 BURN_SECTION_COMPATIBLE_VERSION = 0x00000002;
86 protected const UInt32 BURN_SECTION_SIZE = 512;
87 protected const UInt32 BURN_SECTION_MAX_ATTACHEDCONTAINER_COUNT = (BURN_SECTION_SIZE - BURN_SECTION_OFFSET_ATTACHEDCONTAINERSIZE0) / sizeof(UInt32);
88 88
89 protected string fileExe; 89 protected string fileExe;
90 protected UInt32 peOffset = UInt32.MaxValue; 90 protected UInt32 peOffset = UInt32.MaxValue;
@@ -94,6 +94,8 @@ namespace WixToolset.Core.Burn.Bundles
94 protected UInt32 certificateTableSignatureOffset; 94 protected UInt32 certificateTableSignatureOffset;
95 protected UInt32 certificateTableSignatureSize; 95 protected UInt32 certificateTableSignatureSize;
96 protected UInt32 wixburnDataOffset = UInt32.MaxValue; 96 protected UInt32 wixburnDataOffset = UInt32.MaxValue;
97 protected UInt32 wixburnRawDataSize;
98 protected UInt32 wixburnMaxContainers;
97 99
98 // TODO: does this enum exist in another form somewhere? 100 // TODO: does this enum exist in another form somewhere?
99 /// <summary> 101 /// <summary>
@@ -127,9 +129,7 @@ namespace WixToolset.Core.Burn.Bundles
127 public UInt32 OriginalSignatureOffset { get; protected set; } 129 public UInt32 OriginalSignatureOffset { get; protected set; }
128 public UInt32 OriginalSignatureSize { get; protected set; } 130 public UInt32 OriginalSignatureSize { get; protected set; }
129 public UInt32 EngineSize { get; protected set; } 131 public UInt32 EngineSize { get; protected set; }
130 public UInt32 ContainerCount { get; protected set; } 132 public UInt32 UXAddress { get { return this.StubSize; } }
131 public UInt32 UXAddress { get; protected set; }
132 public UInt32 UXSize { get; protected set; }
133 public List<ContainerSlot> AttachedContainers { get; protected set; } 133 public List<ContainerSlot> AttachedContainers { get; protected set; }
134 134
135 protected IMessaging Messaging { get; } 135 protected IMessaging Messaging { get; }
@@ -180,9 +180,7 @@ namespace WixToolset.Core.Burn.Bundles
180 } 180 }
181 181
182 reader.BaseStream.Seek(this.wixburnDataOffset, SeekOrigin.Begin); 182 reader.BaseStream.Seek(this.wixburnDataOffset, SeekOrigin.Begin);
183 List<byte> manifest = new List<byte>(); 183 byte[] bytes = reader.ReadBytes((int)this.wixburnRawDataSize);
184 manifest.AddRange(reader.ReadBytes((int)BURN_SECTION_SIZE));
185 byte[] bytes = manifest.ToArray();
186 UInt32 uint32 = 0; 184 UInt32 uint32 = 0;
187 185
188 uint32 = BurnCommon.ReadUInt32(bytes, BURN_SECTION_OFFSET_MAGIC); 186 uint32 = BurnCommon.ReadUInt32(bytes, BURN_SECTION_OFFSET_MAGIC);
@@ -211,40 +209,37 @@ namespace WixToolset.Core.Burn.Bundles
211 this.OriginalSignatureOffset = BurnCommon.ReadUInt32(bytes, BURN_SECTION_OFFSET_ORIGINALSIGNATUREOFFSET); 209 this.OriginalSignatureOffset = BurnCommon.ReadUInt32(bytes, BURN_SECTION_OFFSET_ORIGINALSIGNATUREOFFSET);
212 this.OriginalSignatureSize = BurnCommon.ReadUInt32(bytes, BURN_SECTION_OFFSET_ORIGINALSIGNATURESIZE); 210 this.OriginalSignatureSize = BurnCommon.ReadUInt32(bytes, BURN_SECTION_OFFSET_ORIGINALSIGNATURESIZE);
213 211
214 this.ContainerCount = BurnCommon.ReadUInt32(bytes, BURN_SECTION_OFFSET_COUNT); 212 uint containerCount = BurnCommon.ReadUInt32(bytes, BURN_SECTION_OFFSET_COUNT);
215 if (BURN_SECTION_MAX_ATTACHEDCONTAINER_COUNT < this.ContainerCount) 213 uint uxSize = 0;
214 if (this.wixburnMaxContainers < containerCount)
216 { 215 {
217 this.Messaging.Write(ErrorMessages.InvalidBundle(this.fileExe)); 216 this.Messaging.Write(ErrorMessages.InvalidBundle(this.fileExe));
218 return false; 217 return false;
219 } 218 }
220 this.UXAddress = this.StubSize; 219 else if (containerCount > 0)
221 this.UXSize = BurnCommon.ReadUInt32(bytes, BURN_SECTION_OFFSET_UXSIZE); 220 {
221 this.AttachedContainers.Clear();
222 for (uint i = 0; i < containerCount; ++i)
223 {
224 uint sizeOffset = BURN_SECTION_OFFSET_UXSIZE + (i * 4);
225 uint size = BurnCommon.ReadUInt32(bytes, sizeOffset);
226 this.AttachedContainers.Add(new ContainerSlot(size));
227 }
228 uxSize = this.AttachedContainers[0].Size;
229 }
222 230
223 // If there is an original signature use that to determine the engine size. 231 // If there is an original signature use that to determine the engine size.
224 if (0 < this.OriginalSignatureOffset) 232 if (0 < this.OriginalSignatureOffset)
225 { 233 {
226 this.EngineSize = this.OriginalSignatureOffset + this.OriginalSignatureSize; 234 this.EngineSize = this.OriginalSignatureOffset + this.OriginalSignatureSize;
227 } 235 }
228 else if (0 < this.SignatureOffset && 2 > this.ContainerCount) // if there is a signature and no attached containers, use the current signature. 236 else if (0 < this.SignatureOffset && 2 > containerCount) // if there is a signature and no attached containers, use the current signature.
229 { 237 {
230 this.EngineSize = this.SignatureOffset + this.SignatureSize; 238 this.EngineSize = this.SignatureOffset + this.SignatureSize;
231 } 239 }
232 else // just use the stub and UX container as the size of the engine. 240 else // just use the stub and UX container as the size of the engine.
233 { 241 {
234 this.EngineSize = this.StubSize + this.UXSize; 242 this.EngineSize = this.UXAddress + uxSize;
235 }
236
237 this.AttachedContainers.Clear();
238 uint nextAddress = this.EngineSize;
239 if (this.ContainerCount > 1)
240 {
241 for (uint i = 0; i < (this.ContainerCount - 1 /* Excluding UX */); ++i)
242 {
243 uint sizeOffset = BURN_SECTION_OFFSET_ATTACHEDCONTAINERSIZE0 + (i * 4);
244 uint size = BurnCommon.ReadUInt32(bytes, sizeOffset);
245 this.AttachedContainers.Add(new ContainerSlot(nextAddress, size));
246 nextAddress += size;
247 }
248 } 243 }
249 244
250 return true; 245 return true;
@@ -288,13 +283,17 @@ namespace WixToolset.Core.Burn.Bundles
288 return false; 283 return false;
289 } 284 }
290 285
291 // We need 512 bytes for the manifest header 286 this.wixburnRawDataSize = BurnCommon.ReadUInt32(bytes, IMAGE_SECTION_HEADER_OFFSET_SIZEOFRAWDATA);
292 if (BURN_SECTION_SIZE > BurnCommon.ReadUInt32(bytes, IMAGE_SECTION_HEADER_OFFSET_SIZEOFRAWDATA)) 287
288 // we need 52 bytes for the manifest header, which is always going to fit in
289 // the smallest alignment (512 bytes), but just to be paranoid...
290 if (BURN_SECTION_MIN_SIZE > this.wixburnRawDataSize)
293 { 291 {
294 this.Messaging.Write(ErrorMessages.StubWixburnSectionTooSmall(this.fileExe)); 292 this.Messaging.Write(ErrorMessages.StubWixburnSectionTooSmall(this.fileExe));
295 return false; 293 return false;
296 } 294 }
297 295
296 this.wixburnMaxContainers = (this.wixburnRawDataSize - BURN_SECTION_OFFSET_UXSIZE) / sizeof(UInt32);
298 this.wixburnDataOffset = BurnCommon.ReadUInt32(bytes, IMAGE_SECTION_HEADER_OFFSET_POINTERTORAWDATA); 297 this.wixburnDataOffset = BurnCommon.ReadUInt32(bytes, IMAGE_SECTION_HEADER_OFFSET_POINTERTORAWDATA);
299 } 298 }
300 299
@@ -404,13 +403,11 @@ namespace WixToolset.Core.Burn.Bundles
404 403
405 internal struct ContainerSlot 404 internal struct ContainerSlot
406 { 405 {
407 public ContainerSlot(uint address, uint size) : this() 406 public ContainerSlot(uint size) : this()
408 { 407 {
409 this.Address = address;
410 this.Size = size; 408 this.Size = size;
411 } 409 }
412 410
413 public uint Address { get; set; }
414 public uint Size { get; set; } 411 public uint Size { get; set; }
415 } 412 }
416} 413}
diff --git a/src/wix/WixToolset.Core.Burn/Bundles/BurnReader.cs b/src/wix/WixToolset.Core.Burn/Bundles/BurnReader.cs
index e3fd9f51..575252b0 100644
--- a/src/wix/WixToolset.Core.Burn/Bundles/BurnReader.cs
+++ b/src/wix/WixToolset.Core.Burn/Bundles/BurnReader.cs
@@ -78,7 +78,7 @@ namespace WixToolset.Core.Burn.Bundles
78 public bool ExtractUXContainer(string outputDirectory, string tempDirectory) 78 public bool ExtractUXContainer(string outputDirectory, string tempDirectory)
79 { 79 {
80 // No UX container to extract 80 // No UX container to extract
81 if (this.UXAddress == 0 || this.UXSize == 0) 81 if (this.AttachedContainers.Count == 0)
82 { 82 {
83 return false; 83 return false;
84 } 84 }
@@ -92,11 +92,12 @@ namespace WixToolset.Core.Burn.Bundles
92 string tempCabPath = Path.Combine(tempDirectory, "ux.cab"); 92 string tempCabPath = Path.Combine(tempDirectory, "ux.cab");
93 string manifestOriginalPath = Path.Combine(outputDirectory, "0"); 93 string manifestOriginalPath = Path.Combine(outputDirectory, "0");
94 string manifestPath = Path.Combine(outputDirectory, "manifest.xml"); 94 string manifestPath = Path.Combine(outputDirectory, "manifest.xml");
95 var uxContainerSlot = this.AttachedContainers[0];
95 96
96 this.binaryReader.BaseStream.Seek(this.UXAddress, SeekOrigin.Begin); 97 this.binaryReader.BaseStream.Seek(this.UXAddress, SeekOrigin.Begin);
97 using (Stream tempCab = File.Open(tempCabPath, FileMode.Create, FileAccess.Write)) 98 using (Stream tempCab = File.Open(tempCabPath, FileMode.Create, FileAccess.Write))
98 { 99 {
99 BurnCommon.CopyStream(this.binaryReader.BaseStream, tempCab, (int)this.UXSize); 100 BurnCommon.CopyStream(this.binaryReader.BaseStream, tempCab, (int)uxContainerSlot.Size);
100 } 101 }
101 102
102 var cabinet = new Cabinet(tempCabPath); 103 var cabinet = new Cabinet(tempCabPath);
@@ -152,14 +153,15 @@ namespace WixToolset.Core.Burn.Bundles
152 } 153 }
153 154
154 /// <summary> 155 /// <summary>
155 /// Gets the attached container from the exe and extracts its contents to the output directory. 156 /// Gets each non-UX attached container from the exe and extracts its contents to the output directory.
156 /// </summary> 157 /// </summary>
157 /// <param name="outputDirectory">Directory to write extracted files to.</param> 158 /// <param name="outputDirectory">Directory to write extracted files to.</param>
159 /// <param name="tempDirectory">Scratch directory.</param>
158 /// <returns>True if successful, false otherwise</returns> 160 /// <returns>True if successful, false otherwise</returns>
159 public bool ExtractAttachedContainers(string outputDirectory) 161 public bool ExtractAttachedContainers(string outputDirectory, string tempDirectory)
160 { 162 {
161 // No attached container to extract 163 // No attached containers to extract
162 if (this.AttachedContainers.Count == 0) 164 if (this.AttachedContainers.Count < 2)
163 { 165 {
164 return false; 166 return false;
165 } 167 }
@@ -170,11 +172,13 @@ namespace WixToolset.Core.Burn.Bundles
170 } 172 }
171 173
172 Directory.CreateDirectory(outputDirectory); 174 Directory.CreateDirectory(outputDirectory);
173 foreach (ContainerSlot cntnr in this.AttachedContainers) 175 uint nextAddress = this.EngineSize;
176 for (int i = 1; i < this.AttachedContainers.Count; i++)
174 { 177 {
175 string tempCabPath = Path.GetTempFileName(); 178 ContainerSlot cntnr = this.AttachedContainers[i];
179 string tempCabPath = Path.Combine(tempDirectory, $"a{i}.cab");
176 180
177 this.binaryReader.BaseStream.Seek(cntnr.Address, SeekOrigin.Begin); 181 this.binaryReader.BaseStream.Seek(nextAddress, SeekOrigin.Begin);
178 using (Stream tempCab = File.Open(tempCabPath, FileMode.Create, FileAccess.Write)) 182 using (Stream tempCab = File.Open(tempCabPath, FileMode.Create, FileAccess.Write))
179 { 183 {
180 BurnCommon.CopyStream(this.binaryReader.BaseStream, tempCab, (int)cntnr.Size); 184 BurnCommon.CopyStream(this.binaryReader.BaseStream, tempCab, (int)cntnr.Size);
@@ -182,6 +186,8 @@ namespace WixToolset.Core.Burn.Bundles
182 186
183 var cabinet = new Cabinet(tempCabPath); 187 var cabinet = new Cabinet(tempCabPath);
184 cabinet.Extract(outputDirectory); 188 cabinet.Extract(outputDirectory);
189
190 nextAddress += cntnr.Size;
185 } 191 }
186 192
187 foreach (DictionaryEntry entry in this.attachedContainerPayloadNames) 193 foreach (DictionaryEntry entry in this.attachedContainerPayloadNames)
diff --git a/src/wix/WixToolset.Core.Burn/Bundles/BurnWriter.cs b/src/wix/WixToolset.Core.Burn/Bundles/BurnWriter.cs
index c6419ba9..46835974 100644
--- a/src/wix/WixToolset.Core.Burn/Bundles/BurnWriter.cs
+++ b/src/wix/WixToolset.Core.Burn/Bundles/BurnWriter.cs
@@ -91,9 +91,9 @@ namespace WixToolset.Core.Burn.Bundles
91 this.WriteToBurnSectionOffset(BURN_SECTION_OFFSET_ORIGINALSIGNATUREOFFSET, 0); 91 this.WriteToBurnSectionOffset(BURN_SECTION_OFFSET_ORIGINALSIGNATUREOFFSET, 0);
92 this.WriteToBurnSectionOffset(BURN_SECTION_OFFSET_ORIGINALSIGNATURESIZE, 0); 92 this.WriteToBurnSectionOffset(BURN_SECTION_OFFSET_ORIGINALSIGNATURESIZE, 0);
93 this.WriteToBurnSectionOffset(BURN_SECTION_OFFSET_FORMAT, 1); // Hard-coded to CAB for now. 93 this.WriteToBurnSectionOffset(BURN_SECTION_OFFSET_FORMAT, 1); // Hard-coded to CAB for now.
94 this.AttachedContainers.Clear();
94 this.WriteToBurnSectionOffset(BURN_SECTION_OFFSET_COUNT, 0); 95 this.WriteToBurnSectionOffset(BURN_SECTION_OFFSET_COUNT, 0);
95 this.WriteToBurnSectionOffset(BURN_SECTION_OFFSET_UXSIZE, 0); 96 for (uint i = BURN_SECTION_OFFSET_UXSIZE; i < this.wixburnMaxContainers; i += sizeof(UInt32))
96 for (uint i = BURN_SECTION_OFFSET_ATTACHEDCONTAINERSIZE0; i < BURN_SECTION_SIZE; i += sizeof(UInt32))
97 { 97 {
98 this.WriteToBurnSectionOffset(i, 0); 98 this.WriteToBurnSectionOffset(i, 0);
99 } 99 }
@@ -119,6 +119,39 @@ namespace WixToolset.Core.Burn.Bundles
119 } 119 }
120 120
121 /// <summary> 121 /// <summary>
122 /// Appends the non-UX attached containers from the reader to this bundle.
123 /// </summary>
124 /// <param name="reader">The source bundle.</param>
125 /// <returns>true if the container data is successfully appended; false otherwise.</returns>
126 public bool ReattachContainers(BurnReader reader)
127 {
128 if (this.AttachedContainers.Count == 0 || reader.AttachedContainers.Count < 2)
129 {
130 return false;
131 }
132
133 this.RememberThenResetSignature();
134
135 var uxContainerSlot = this.AttachedContainers[0];
136 this.AttachedContainers.Clear();
137 this.AttachedContainers.Add(uxContainerSlot);
138
139 uint nextAddress = this.EngineSize;
140 for (int i = 1; i < reader.AttachedContainers.Count; i++)
141 {
142 ContainerSlot cntnr = reader.AttachedContainers[i];
143
144 reader.Stream.Seek(nextAddress, SeekOrigin.Begin);
145 // TODO: verify that the size in the section data is 0 or the same size.
146 this.AppendContainer(reader.Stream, cntnr.Size, BurnCommon.Container.Attached);
147
148 nextAddress += cntnr.Size;
149 }
150
151 return true;
152 }
153
154 /// <summary>
122 /// Appends a UX or Attached container to the exe and updates the ".wixburn" section data to point to it. 155 /// Appends a UX or Attached container to the exe and updates the ".wixburn" section data to point to it.
123 /// </summary> 156 /// </summary>
124 /// <param name="containerStream">File stream to append to the current exe.</param> 157 /// <param name="containerStream">File stream to append to the current exe.</param>
@@ -127,38 +160,23 @@ namespace WixToolset.Core.Burn.Bundles
127 /// <returns>true if the container data is successfully appended; false otherwise</returns> 160 /// <returns>true if the container data is successfully appended; false otherwise</returns>
128 public bool AppendContainer(Stream containerStream, long containerSize, BurnCommon.Container container) 161 public bool AppendContainer(Stream containerStream, long containerSize, BurnCommon.Container container)
129 { 162 {
130 UInt32 burnSectionCount = 0; 163 uint containerCount = (uint)this.AttachedContainers.Count;
131 UInt32 burnSectionOffsetSize = 0; 164 uint burnSectionOffsetSize = BURN_SECTION_OFFSET_UXSIZE + (containerCount * sizeof(UInt32));
132 165 var containerSlot = new ContainerSlot((uint)containerSize);
133 if (containerSize == 0)
134 {
135 return false;
136 }
137 166
138 switch (container) 167 switch (container)
139 { 168 {
140 case Container.UX: 169 case Container.UX:
141 burnSectionCount = 1; 170 if (containerCount != 0)
142 burnSectionOffsetSize = BURN_SECTION_OFFSET_UXSIZE;
143 // TODO: verify that the size in the section data is 0 or the same size.
144 this.EngineSize += (uint)containerSize;
145 this.UXSize = (uint)containerSize;
146 break;
147
148 case Container.Attached:
149 // TODO: verify that the size in the section data is 0 or the same size.
150 uint nextAddress = this.EngineSize;
151 foreach (ContainerSlot cntnr in this.AttachedContainers)
152 { 171 {
153 if (cntnr.Address >= nextAddress) 172 Debug.Assert(false);
154 { 173 return false;
155 nextAddress = cntnr.Address + cntnr.Size;
156 }
157 } 174 }
158 175
159 this.AttachedContainers.Add(new ContainerSlot(nextAddress, (uint)containerSize)); 176 this.EngineSize += containerSlot.Size;
160 burnSectionCount = 1 + (uint)this.AttachedContainers.Count; 177 break;
161 burnSectionOffsetSize = BURN_SECTION_OFFSET_UXSIZE + ((uint)this.AttachedContainers.Count * 4); 178
179 case Container.Attached:
162 break; 180 break;
163 181
164 default: 182 default:
@@ -166,7 +184,9 @@ namespace WixToolset.Core.Burn.Bundles
166 return false; 184 return false;
167 } 185 }
168 186
169 return this.AppendContainer(containerStream, (UInt32)containerSize, burnSectionOffsetSize, burnSectionCount); 187 this.AttachedContainers.Add(containerSlot);
188 ++containerCount;
189 return this.AppendContainer(containerStream, containerSlot.Size, burnSectionOffsetSize, containerCount);
170 } 190 }
171 191
172 public void RememberThenResetSignature() 192 public void RememberThenResetSignature()
@@ -225,10 +245,10 @@ namespace WixToolset.Core.Burn.Bundles
225 { 245 {
226 return false; 246 return false;
227 } 247 }
228 if (burnSectionOffsetSize > (BURN_SECTION_SIZE - sizeof(UInt32))) 248 if (burnSectionOffsetSize > (this.wixburnRawDataSize - sizeof(UInt32)))
229 { 249 {
230 this.invalidBundle = true; 250 this.invalidBundle = true;
231 this.Messaging.Write(BurnBackendErrors.TooManyAttachedContainers(BURN_SECTION_MAX_ATTACHEDCONTAINER_COUNT)); 251 this.Messaging.Write(BurnBackendErrors.TooManyAttachedContainers(this.wixburnMaxContainers));
232 return false; 252 return false;
233 } 253 }
234 254
diff --git a/src/wix/WixToolset.Core.Burn/Inscribe/InscribeBundleCommand.cs b/src/wix/WixToolset.Core.Burn/Inscribe/InscribeBundleCommand.cs
index 17030dd3..f835fd3a 100644
--- a/src/wix/WixToolset.Core.Burn/Inscribe/InscribeBundleCommand.cs
+++ b/src/wix/WixToolset.Core.Burn/Inscribe/InscribeBundleCommand.cs
@@ -31,22 +31,7 @@ namespace WixToolset.Core.Burn.Inscribe
31 FileSystem.CopyFile(this.Context.SignedEngineFile, tempFile, allowHardlink: false); 31 FileSystem.CopyFile(this.Context.SignedEngineFile, tempFile, allowHardlink: false);
32 using (BurnWriter writer = BurnWriter.Open(this.Messaging, tempFile)) 32 using (BurnWriter writer = BurnWriter.Open(this.Messaging, tempFile))
33 { 33 {
34 if (reader.Version != writer.Version) 34 inscribed = writer.ReattachContainers(reader);
35 {
36 this.Messaging.Write(BurnBackendErrors.IncompatibleWixBurnSection(this.Context.InputFilePath, reader.Version));
37 }
38
39 writer.AttachedContainers.Clear();
40 writer.RememberThenResetSignature();
41 foreach (ContainerSlot cntnr in reader.AttachedContainers)
42 {
43 if (cntnr.Size > 0)
44 {
45 reader.Stream.Seek(cntnr.Address, SeekOrigin.Begin);
46 writer.AppendContainer(reader.Stream, cntnr.Size, BurnCommon.Container.Attached);
47 inscribed = true;
48 }
49 }
50 } 35 }
51 } 36 }
52 37
diff --git a/src/wix/WixToolset.Core.TestPackage/BundleExtractor.cs b/src/wix/WixToolset.Core.TestPackage/BundleExtractor.cs
index affe8c17..bd13a9b2 100644
--- a/src/wix/WixToolset.Core.TestPackage/BundleExtractor.cs
+++ b/src/wix/WixToolset.Core.TestPackage/BundleExtractor.cs
@@ -22,22 +22,41 @@ namespace WixToolset.Core.TestPackage
22 /// <returns></returns> 22 /// <returns></returns>
23 public static ExtractBAContainerResult ExtractBAContainer(IMessaging messaging, string bundleFilePath, string destinationFolderPath, string tempFolderPath) 23 public static ExtractBAContainerResult ExtractBAContainer(IMessaging messaging, string bundleFilePath, string destinationFolderPath, string tempFolderPath)
24 { 24 {
25 return ExtractAllContainers(messaging, bundleFilePath, destinationFolderPath, null, tempFolderPath);
26 }
27
28 /// <summary>
29 /// Extracts the BA container.
30 /// </summary>
31 /// <param name="messaging"></param>
32 /// <param name="bundleFilePath">Path to the bundle.</param>
33 /// <param name="baFolderPath">Path to extract BA to.</param>
34 /// <param name="otherContainersFolderPath">Path to extract other attached containers to.</param>
35 /// <param name="tempFolderPath">Temp path for extraction.</param>
36 /// <returns></returns>
37 public static ExtractBAContainerResult ExtractAllContainers(IMessaging messaging, string bundleFilePath, string baFolderPath, string otherContainersFolderPath, string tempFolderPath)
38 {
25 var result = new ExtractBAContainerResult(); 39 var result = new ExtractBAContainerResult();
26 Directory.CreateDirectory(tempFolderPath); 40 Directory.CreateDirectory(tempFolderPath);
27 using (var burnReader = BurnReader.Open(messaging, bundleFilePath)) 41 using (var burnReader = BurnReader.Open(messaging, bundleFilePath))
28 { 42 {
29 result.Success = burnReader.ExtractUXContainer(destinationFolderPath, tempFolderPath); 43 result.Success = burnReader.ExtractUXContainer(baFolderPath, tempFolderPath);
44
45 if (otherContainersFolderPath != null)
46 {
47 result.AttachedContainersSuccess = burnReader.ExtractAttachedContainers(otherContainersFolderPath, tempFolderPath);
48 }
30 } 49 }
31 50
32 if (result.Success) 51 if (result.Success)
33 { 52 {
34 result.ManifestDocument = LoadBurnManifest(destinationFolderPath); 53 result.ManifestDocument = LoadBurnManifest(baFolderPath);
35 result.ManifestNamespaceManager = GetBurnNamespaceManager(result.ManifestDocument, "burn"); 54 result.ManifestNamespaceManager = GetBurnNamespaceManager(result.ManifestDocument, "burn");
36 55
37 result.BADataDocument = LoadBAData(destinationFolderPath); 56 result.BADataDocument = LoadBAData(baFolderPath);
38 result.BADataNamespaceManager = GetBADataNamespaceManager(result.BADataDocument, "ba"); 57 result.BADataNamespaceManager = GetBADataNamespaceManager(result.BADataDocument, "ba");
39 58
40 result.BundleExtensionDataDocument = LoadBundleExtensionData(destinationFolderPath); 59 result.BundleExtensionDataDocument = LoadBundleExtensionData(baFolderPath);
41 result.BundleExtensionDataNamespaceManager = GetBundleExtensionDataNamespaceManager(result.BundleExtensionDataDocument, "be"); 60 result.BundleExtensionDataNamespaceManager = GetBundleExtensionDataNamespaceManager(result.BundleExtensionDataDocument, "be");
42 } 61 }
43 62
@@ -45,21 +64,6 @@ namespace WixToolset.Core.TestPackage
45 } 64 }
46 65
47 /// <summary> 66 /// <summary>
48 /// Extracts the attached container.
49 /// </summary>
50 /// <param name="messaging"></param>
51 /// <param name="bundleFilePath">Path to the bundle.</param>
52 /// <param name="destinationFolderPath">Path to extract to.</param>
53 /// <returns>True if there was an attached container.</returns>
54 public static bool ExtractAttachedContainers(IMessaging messaging, string bundleFilePath, string destinationFolderPath)
55 {
56 using (var burnReader = BurnReader.Open(messaging, bundleFilePath))
57 {
58 return burnReader.ExtractAttachedContainers(destinationFolderPath);
59 }
60 }
61
62 /// <summary>
63 /// Gets an <see cref="XmlNamespaceManager"/> for BootstrapperApplicationData.xml with the given prefix assigned to the root namespace. 67 /// Gets an <see cref="XmlNamespaceManager"/> for BootstrapperApplicationData.xml with the given prefix assigned to the root namespace.
64 /// </summary> 68 /// </summary>
65 /// <param name="document"></param> 69 /// <param name="document"></param>
diff --git a/src/wix/WixToolset.Core.TestPackage/ExtractBAContainerResult.cs b/src/wix/WixToolset.Core.TestPackage/ExtractBAContainerResult.cs
index 277861ff..65528fe0 100644
--- a/src/wix/WixToolset.Core.TestPackage/ExtractBAContainerResult.cs
+++ b/src/wix/WixToolset.Core.TestPackage/ExtractBAContainerResult.cs
@@ -47,12 +47,18 @@ namespace WixToolset.Core.TestPackage
47 public bool Success { get; set; } 47 public bool Success { get; set; }
48 48
49 /// <summary> 49 /// <summary>
50 /// Whether attached containers extraction succeeded.
51 /// </summary>
52 public bool? AttachedContainersSuccess { get; set; }
53
54 /// <summary>
50 /// 55 ///
51 /// </summary> 56 /// </summary>
52 /// <returns></returns> 57 /// <returns></returns>
53 public ExtractBAContainerResult AssertSuccess() 58 public ExtractBAContainerResult AssertSuccess()
54 { 59 {
55 Assert.True(this.Success); 60 Assert.True(this.Success);
61 Assert.True(!this.AttachedContainersSuccess.HasValue || this.AttachedContainersSuccess.Value);
56 return this; 62 return this;
57 } 63 }
58 64
diff --git a/src/wix/test/WixToolsetTest.CoreIntegration/BundleFixture.cs b/src/wix/test/WixToolsetTest.CoreIntegration/BundleFixture.cs
index 3491def9..10d9a39a 100644
--- a/src/wix/test/WixToolsetTest.CoreIntegration/BundleFixture.cs
+++ b/src/wix/test/WixToolsetTest.CoreIntegration/BundleFixture.cs
@@ -125,8 +125,8 @@ namespace WixToolsetTest.CoreIntegration
125 125
126 var msiPayloads = extractResult.SelectManifestNodes("/burn:BurnManifest/burn:Payload[@Id='test.msi']"); 126 var msiPayloads = extractResult.SelectManifestNodes("/burn:BurnManifest/burn:Payload[@Id='test.msi']");
127 var msiPayload = (XmlNode)Assert.Single(msiPayloads); 127 var msiPayload = (XmlNode)Assert.Single(msiPayloads);
128 Assert.Equal("<Payload Id='test.msi' FilePath='test.msi' FileSize='*' Hash='*' Packaging='embedded' SourcePath='*' Container='WixAttachedContainer' />", 128 Assert.Equal("<Payload Id='test.msi' FilePath='test.msi' FileSize='*' Hash='*' Packaging='embedded' SourcePath='a0' Container='WixAttachedContainer' />",
129 msiPayload.GetTestXml(new Dictionary<string, List<string>>() { { "Payload", new List<string> { "FileSize", "Hash", "SourcePath" } } })); 129 msiPayload.GetTestXml(new Dictionary<string, List<string>>() { { "Payload", new List<string> { "FileSize", "Hash" } } }));
130 } 130 }
131 131
132 var manifestResource = new Resource(ResourceType.Manifest, "#1", 1033); 132 var manifestResource = new Resource(ResourceType.Manifest, "#1", 1033);
@@ -156,6 +156,7 @@ namespace WixToolsetTest.CoreIntegration
156 var exePath = Path.Combine(baseFolder, @"bin\test.exe"); 156 var exePath = Path.Combine(baseFolder, @"bin\test.exe");
157 var pdbPath = Path.Combine(baseFolder, @"bin\test.wixpdb"); 157 var pdbPath = Path.Combine(baseFolder, @"bin\test.wixpdb");
158 var baFolderPath = Path.Combine(baseFolder, "ba"); 158 var baFolderPath = Path.Combine(baseFolder, "ba");
159 var attachedFolderPath = Path.Combine(baseFolder, "attached");
159 var extractFolderPath = Path.Combine(baseFolder, "extract"); 160 var extractFolderPath = Path.Combine(baseFolder, "extract");
160 161
161 var result = WixRunner.Execute(false, new[] // TODO: go back to elevating warnings as errors. 162 var result = WixRunner.Execute(false, new[] // TODO: go back to elevating warnings as errors.
@@ -186,6 +187,9 @@ namespace WixToolsetTest.CoreIntegration
186 "<trustInfo xmlns=\"urn:schemas-microsoft-com:asm.v3\"><security><requestedPrivileges><requestedExecutionLevel level=\"asInvoker\" uiAccess=\"false\" /></requestedPrivileges></security></trustInfo>" + 187 "<trustInfo xmlns=\"urn:schemas-microsoft-com:asm.v3\"><security><requestedPrivileges><requestedExecutionLevel level=\"asInvoker\" uiAccess=\"false\" /></requestedPrivileges></security></trustInfo>" +
187 "<application xmlns=\"urn:schemas-microsoft-com:asm.v3\"><windowsSettings><dpiAware xmlns=\"http://schemas.microsoft.com/SMI/2005/WindowsSettings\">true/pm</dpiAware><dpiAwareness xmlns=\"http://schemas.microsoft.com/SMI/2016/WindowsSettings\">PerMonitorV2, PerMonitor</dpiAwareness></windowsSettings></application>" + 188 "<application xmlns=\"urn:schemas-microsoft-com:asm.v3\"><windowsSettings><dpiAware xmlns=\"http://schemas.microsoft.com/SMI/2005/WindowsSettings\">true/pm</dpiAware><dpiAwareness xmlns=\"http://schemas.microsoft.com/SMI/2016/WindowsSettings\">PerMonitorV2, PerMonitor</dpiAwareness></windowsSettings></application>" +
188 "</assembly>", actualManifestData); 189 "</assembly>", actualManifestData);
190
191 var extractResult = BundleExtractor.ExtractAllContainers(null, exePath, baFolderPath, attachedFolderPath, extractFolderPath);
192 extractResult.AssertSuccess();
189 } 193 }
190 } 194 }
191 195
diff --git a/src/wix/test/WixToolsetTest.CoreIntegration/ContainerFixture.cs b/src/wix/test/WixToolsetTest.CoreIntegration/ContainerFixture.cs
index 6e6f44be..482a6edd 100644
--- a/src/wix/test/WixToolsetTest.CoreIntegration/ContainerFixture.cs
+++ b/src/wix/test/WixToolsetTest.CoreIntegration/ContainerFixture.cs
@@ -49,11 +49,11 @@ namespace WixToolsetTest.CoreIntegration
49 49
50 var payloads = extractResult.SelectManifestNodes("/burn:BurnManifest/burn:Payload"); 50 var payloads = extractResult.SelectManifestNodes("/burn:BurnManifest/burn:Payload");
51 Assert.Equal(4, payloads.Count); 51 Assert.Equal(4, payloads.Count);
52 var ignoreAttributes = new Dictionary<string, List<string>> { { "Payload", new List<string> { "FileSize", "Hash", "SourcePath" } } }; 52 var ignoreAttributes = new Dictionary<string, List<string>> { { "Payload", new List<string> { "FileSize", "Hash" } } };
53 Assert.Equal(@"<Payload Id='FirstX64' FilePath='FirstX64\FirstX64.msi' FileSize='*' Hash='*' DownloadUrl='http://example.com//FirstX64/FirstX64/FirstX64.msi' Packaging='embedded' SourcePath='*' Container='BundlePackages' />", payloads[0].GetTestXml(ignoreAttributes)); 53 Assert.Equal(@"<Payload Id='FirstX64' FilePath='FirstX64\FirstX64.msi' FileSize='*' Hash='*' DownloadUrl='http://example.com//FirstX64/FirstX64/FirstX64.msi' Packaging='embedded' SourcePath='a0' Container='BundlePackages' />", payloads[0].GetTestXml(ignoreAttributes));
54 Assert.Equal(@"<Payload Id='FirstX86.msi' FilePath='FirstX86\FirstX86.msi' FileSize='*' Hash='*' DownloadUrl='http://example.com//FirstX86.msi/FirstX86/FirstX86.msi' Packaging='embedded' SourcePath='*' Container='BundlePackages' />", payloads[1].GetTestXml(ignoreAttributes)); 54 Assert.Equal(@"<Payload Id='FirstX86.msi' FilePath='FirstX86\FirstX86.msi' FileSize='*' Hash='*' DownloadUrl='http://example.com//FirstX86.msi/FirstX86/FirstX86.msi' Packaging='embedded' SourcePath='a1' Container='BundlePackages' />", payloads[1].GetTestXml(ignoreAttributes));
55 Assert.Equal(@"<Payload Id='fk1m38Cf9RZ2Bx_ipinRY6BftelU' FilePath='FirstX86\PFiles\MsiPackage\test.txt' FileSize='*' Hash='*' DownloadUrl='http://example.com/FirstX86.msi/fk1m38Cf9RZ2Bx_ipinRY6BftelU/FirstX86/PFiles/MsiPackage/test.txt' Packaging='embedded' SourcePath='*' Container='BundlePackages' />", payloads[2].GetTestXml(ignoreAttributes)); 55 Assert.Equal(@"<Payload Id='fk1m38Cf9RZ2Bx_ipinRY6BftelU' FilePath='FirstX86\PFiles\MsiPackage\test.txt' FileSize='*' Hash='*' DownloadUrl='http://example.com/FirstX86.msi/fk1m38Cf9RZ2Bx_ipinRY6BftelU/FirstX86/PFiles/MsiPackage/test.txt' Packaging='embedded' SourcePath='a2' Container='BundlePackages' />", payloads[2].GetTestXml(ignoreAttributes));
56 Assert.Equal(@"<Payload Id='ff2L_N_DLQ.nSUi.l8LxG14gd2V4' FilePath='FirstX64\PFiles\MsiPackage\test.txt' FileSize='*' Hash='*' DownloadUrl='http://example.com/FirstX64/ff2L_N_DLQ.nSUi.l8LxG14gd2V4/FirstX64/PFiles/MsiPackage/test.txt' Packaging='embedded' SourcePath='*' Container='BundlePackages' />", payloads[3].GetTestXml(ignoreAttributes)); 56 Assert.Equal(@"<Payload Id='ff2L_N_DLQ.nSUi.l8LxG14gd2V4' FilePath='FirstX64\PFiles\MsiPackage\test.txt' FileSize='*' Hash='*' DownloadUrl='http://example.com/FirstX64/ff2L_N_DLQ.nSUi.l8LxG14gd2V4/FirstX64/PFiles/MsiPackage/test.txt' Packaging='embedded' SourcePath='a3' Container='BundlePackages' />", payloads[3].GetTestXml(ignoreAttributes));
57 } 57 }
58 } 58 }
59 59
@@ -93,11 +93,11 @@ namespace WixToolsetTest.CoreIntegration
93 93
94 var payloads = extractResult.SelectManifestNodes("/burn:BurnManifest/burn:Payload"); 94 var payloads = extractResult.SelectManifestNodes("/burn:BurnManifest/burn:Payload");
95 Assert.Equal(4, payloads.Count); 95 Assert.Equal(4, payloads.Count);
96 var ignoreAttributes = new Dictionary<string, List<string>> { { "Payload", new List<string> { "FileSize", "Hash", "SourcePath" } } }; 96 var ignoreAttributes = new Dictionary<string, List<string>> { { "Payload", new List<string> { "FileSize", "Hash" } } };
97 Assert.Equal(@"<Payload Id='FirstX86.msi' FilePath='FirstX86.msi' FileSize='*' Hash='*' Packaging='embedded' SourcePath='*' Container='WixAttachedContainer' />", payloads[0].GetTestXml(ignoreAttributes)); 97 Assert.Equal(@"<Payload Id='FirstX86.msi' FilePath='FirstX86.msi' FileSize='*' Hash='*' Packaging='embedded' SourcePath='a0' Container='WixAttachedContainer' />", payloads[0].GetTestXml(ignoreAttributes));
98 Assert.Equal(@"<Payload Id='FirstX64.msi' FilePath='FirstX64.msi' FileSize='*' Hash='*' Packaging='embedded' SourcePath='*' Container='FirstX64' />", payloads[1].GetTestXml(ignoreAttributes)); 98 Assert.Equal(@"<Payload Id='FirstX64.msi' FilePath='FirstX64.msi' FileSize='*' Hash='*' Packaging='embedded' SourcePath='a1' Container='FirstX64' />", payloads[1].GetTestXml(ignoreAttributes));
99 Assert.Equal(@"<Payload Id='fk1m38Cf9RZ2Bx_ipinRY6BftelU' FilePath='PFiles\MsiPackage\test.txt' FileSize='*' Hash='*' Packaging='embedded' SourcePath='*' Container='WixAttachedContainer' />", payloads[2].GetTestXml(ignoreAttributes)); 99 Assert.Equal(@"<Payload Id='fk1m38Cf9RZ2Bx_ipinRY6BftelU' FilePath='PFiles\MsiPackage\test.txt' FileSize='*' Hash='*' Packaging='embedded' SourcePath='a2' Container='WixAttachedContainer' />", payloads[2].GetTestXml(ignoreAttributes));
100 Assert.Equal(@"<Payload Id='fC0n41rZK8oW3JK8LzHu6AT3CjdQ' FilePath='PFiles\MsiPackage\test.txt' FileSize='*' Hash='*' Packaging='embedded' SourcePath='*' Container='FirstX64' />", payloads[3].GetTestXml(ignoreAttributes)); 100 Assert.Equal(@"<Payload Id='fC0n41rZK8oW3JK8LzHu6AT3CjdQ' FilePath='PFiles\MsiPackage\test.txt' FileSize='*' Hash='*' Packaging='embedded' SourcePath='a3' Container='FirstX64' />", payloads[3].GetTestXml(ignoreAttributes));
101 } 101 }
102 } 102 }
103 103
@@ -203,14 +203,14 @@ namespace WixToolsetTest.CoreIntegration
203 var extractResult = BundleExtractor.ExtractBAContainer(null, bundlePath, baFolderPath, extractFolderPath); 203 var extractResult = BundleExtractor.ExtractBAContainer(null, bundlePath, baFolderPath, extractFolderPath);
204 extractResult.AssertSuccess(); 204 extractResult.AssertSuccess();
205 205
206 var ignoreAttributes = new Dictionary<string, List<string>> { { "Payload", new List<string> { "FileSize", "Hash", "SourcePath" } } }; 206 var ignoreAttributes = new Dictionary<string, List<string>> { { "Payload", new List<string> { "FileSize", "Hash" } } };
207 var payloads = extractResult.SelectManifestNodes("/burn:BurnManifest/burn:Payload[@Id='SharedPayload']") 207 var payloads = extractResult.SelectManifestNodes("/burn:BurnManifest/burn:Payload[@Id='SharedPayload']")
208 .Cast<XmlElement>() 208 .Cast<XmlElement>()
209 .Select(e => e.GetTestXml(ignoreAttributes)) 209 .Select(e => e.GetTestXml(ignoreAttributes))
210 .ToArray(); 210 .ToArray();
211 WixAssert.CompareLineByLine(new string[] 211 WixAssert.CompareLineByLine(new string[]
212 { 212 {
213 "<Payload Id='SharedPayload' FilePath='LayoutPayloadInContainer.wxs' FileSize='*' Hash='*' LayoutOnly='yes' Packaging='embedded' SourcePath='*' Container='FirstX64' />", 213 "<Payload Id='SharedPayload' FilePath='LayoutPayloadInContainer.wxs' FileSize='*' Hash='*' LayoutOnly='yes' Packaging='embedded' SourcePath='a1' Container='FirstX64' />",
214 }, payloads); 214 }, payloads);
215 } 215 }
216 } 216 }
@@ -227,8 +227,8 @@ namespace WixToolsetTest.CoreIntegration
227 var binFolder = Path.Combine(baseFolder, "bin"); 227 var binFolder = Path.Combine(baseFolder, "bin");
228 var bundlePath = Path.Combine(binFolder, "test.exe"); 228 var bundlePath = Path.Combine(binFolder, "test.exe");
229 var baFolderPath = Path.Combine(baseFolder, "ba"); 229 var baFolderPath = Path.Combine(baseFolder, "ba");
230 var attachedFolderPath = Path.Combine(baseFolder, "attached");
230 var extractFolderPath = Path.Combine(baseFolder, "extract"); 231 var extractFolderPath = Path.Combine(baseFolder, "extract");
231 var tempFolderPath = Path.Combine(baseFolder, "temp");
232 232
233 this.BuildMsis(folder, intermediateFolder, binFolder); 233 this.BuildMsis(folder, intermediateFolder, binFolder);
234 234
@@ -243,19 +243,27 @@ namespace WixToolsetTest.CoreIntegration
243 "-o", bundlePath 243 "-o", bundlePath
244 }); 244 });
245 245
246 Assert.Equal(0, result.ExitCode); 246 result.AssertSuccess();
247 Assert.True(File.Exists(bundlePath)); 247 Assert.True(File.Exists(bundlePath));
248 248
249 Directory.CreateDirectory(tempFolderPath); 249 var extractResult = BundleExtractor.ExtractAllContainers(null, bundlePath, baFolderPath, attachedFolderPath, extractFolderPath);
250 using (var burnReader = BurnReader.Open(null, bundlePath)) 250 extractResult.AssertSuccess();
251 {
252 // Extract the BA because that loads the payload target paths from the manifest
253 Assert.True(burnReader.ExtractUXContainer(baFolderPath, tempFolderPath));
254 Assert.True(burnReader.ExtractAttachedContainers(extractFolderPath));
255 }
256 251
257 Assert.True(File.Exists(Path.Combine(extractFolderPath, "FirstX64", "FirstX64.msi")), "Expected extracted container to contain FirstX64.msi"); 252 Assert.True(File.Exists(Path.Combine(attachedFolderPath, "FirstX64", "FirstX64.msi")), "Expected extracted container to contain FirstX64.msi");
258 Assert.True(File.Exists(Path.Combine(extractFolderPath, "WixAttachedContainer", "FirstX86.msi")), "Expected extracted container to contain FirstX86.msi"); 253 Assert.True(File.Exists(Path.Combine(attachedFolderPath, "WixAttachedContainer", "FirstX86.msi")), "Expected extracted container to contain FirstX86.msi");
254
255 var ignoreAttributes = new Dictionary<string, List<string>> { { "Payload", new List<string> { "FileSize", "Hash" } } };
256 var payloads = extractResult.SelectManifestNodes("/burn:BurnManifest/burn:Payload")
257 .Cast<XmlElement>()
258 .Select(e => e.GetTestXml(ignoreAttributes))
259 .ToArray();
260 WixAssert.CompareLineByLine(new string[]
261 {
262 "<Payload Id='FirstX86.msi' FilePath='FirstX86.msi' FileSize='*' Hash='*' Packaging='embedded' SourcePath='a0' Container='WixAttachedContainer' />",
263 "<Payload Id='FirstX64.msi' FilePath='FirstX64.msi' FileSize='*' Hash='*' Packaging='embedded' SourcePath='a1' Container='FirstX64' />",
264 "<Payload Id='fk1m38Cf9RZ2Bx_ipinRY6BftelU' FilePath='PFiles\\MsiPackage\\test.txt' FileSize='*' Hash='*' Packaging='embedded' SourcePath='a2' Container='WixAttachedContainer' />",
265 "<Payload Id='fC0n41rZK8oW3JK8LzHu6AT3CjdQ' FilePath='PFiles\\MsiPackage\\test.txt' FileSize='*' Hash='*' Packaging='embedded' SourcePath='a3' Container='FirstX64' />",
266 }, payloads);
259 } 267 }
260 } 268 }
261 269
@@ -297,14 +305,14 @@ namespace WixToolsetTest.CoreIntegration
297 var extractResult = BundleExtractor.ExtractBAContainer(null, bundlePath, baFolderPath, extractFolderPath); 305 var extractResult = BundleExtractor.ExtractBAContainer(null, bundlePath, baFolderPath, extractFolderPath);
298 extractResult.AssertSuccess(); 306 extractResult.AssertSuccess();
299 307
300 var ignoreAttributes = new Dictionary<string, List<string>> { { "Payload", new List<string> { "FileSize", "Hash", "SourcePath" } } }; 308 var ignoreAttributes = new Dictionary<string, List<string>> { { "Payload", new List<string> { "FileSize", "Hash" } } };
301 var payloads = extractResult.SelectManifestNodes("/burn:BurnManifest/burn:Payload[@Id='SharedPayload']") 309 var payloads = extractResult.SelectManifestNodes("/burn:BurnManifest/burn:Payload[@Id='SharedPayload']")
302 .Cast<XmlElement>() 310 .Cast<XmlElement>()
303 .Select(e => e.GetTestXml(ignoreAttributes)) 311 .Select(e => e.GetTestXml(ignoreAttributes))
304 .ToArray(); 312 .ToArray();
305 WixAssert.CompareLineByLine(new string[] 313 WixAssert.CompareLineByLine(new string[]
306 { 314 {
307 "<Payload Id='SharedPayload' FilePath='PayloadInMultipleContainers.wxs' FileSize='*' Hash='*' Packaging='embedded' SourcePath='*' Container='FirstX86' />", 315 "<Payload Id='SharedPayload' FilePath='PayloadInMultipleContainers.wxs' FileSize='*' Hash='*' Packaging='embedded' SourcePath='a2' Container='FirstX86' />",
308 }, payloads); 316 }, payloads);
309 } 317 }
310 } 318 }
diff --git a/src/wix/test/WixToolsetTest.CoreIntegration/PackagePayloadFixture.cs b/src/wix/test/WixToolsetTest.CoreIntegration/PackagePayloadFixture.cs
index cbd1f32f..6b2d8bfa 100644
--- a/src/wix/test/WixToolsetTest.CoreIntegration/PackagePayloadFixture.cs
+++ b/src/wix/test/WixToolsetTest.CoreIntegration/PackagePayloadFixture.cs
@@ -46,14 +46,13 @@ namespace WixToolsetTest.CoreIntegration
46 var ignoreAttributesByElementName = new Dictionary<string, List<string>> 46 var ignoreAttributesByElementName = new Dictionary<string, List<string>>
47 { 47 {
48 { "ExePackage", new List<string> { "CacheId", "InstallSize", "Size" } }, 48 { "ExePackage", new List<string> { "CacheId", "InstallSize", "Size" } },
49 { "Payload", new List<string> { "SourcePath" } },
50 }; 49 };
51 Assert.Equal(1, exePackageElements.Count); 50 Assert.Equal(1, exePackageElements.Count);
52 Assert.Equal("<ExePackage Id='PackagePayloadInPayloadGroup' Cache='keep' CacheId='*' InstallSize='*' Size='*' PerMachine='yes' Permanent='yes' Vital='yes' RollbackBoundaryForward='WixDefaultBoundary' RollbackBoundaryBackward='WixDefaultBoundary' LogPathVariable='WixBundleLog_PackagePayloadInPayloadGroup' RollbackLogPathVariable='WixBundleRollbackLog_PackagePayloadInPayloadGroup' DetectCondition='none' InstallArguments='' UninstallArguments='' RepairArguments='' Repairable='no'><PayloadRef Id='burn.exe' /></ExePackage>", exePackageElements[0].GetTestXml(ignoreAttributesByElementName)); 51 Assert.Equal("<ExePackage Id='PackagePayloadInPayloadGroup' Cache='keep' CacheId='*' InstallSize='*' Size='*' PerMachine='yes' Permanent='yes' Vital='yes' RollbackBoundaryForward='WixDefaultBoundary' RollbackBoundaryBackward='WixDefaultBoundary' LogPathVariable='WixBundleLog_PackagePayloadInPayloadGroup' RollbackLogPathVariable='WixBundleRollbackLog_PackagePayloadInPayloadGroup' DetectCondition='none' InstallArguments='' UninstallArguments='' RepairArguments='' Repairable='no'><PayloadRef Id='burn.exe' /></ExePackage>", exePackageElements[0].GetTestXml(ignoreAttributesByElementName));
53 52
54 var payloadElements = extractResult.SelectManifestNodes("/burn:BurnManifest/burn:Payload[@Id='burn.exe']"); 53 var payloadElements = extractResult.SelectManifestNodes("/burn:BurnManifest/burn:Payload[@Id='burn.exe']");
55 Assert.Equal(1, payloadElements.Count); 54 Assert.Equal(1, payloadElements.Count);
56 Assert.Equal("<Payload Id='burn.exe' FilePath='burn.exe' FileSize='463360' Hash='F6E722518AC3AB7E31C70099368D5770788C179AA23226110DCF07319B1E1964E246A1E8AE72E2CF23E0138AFC281BAFDE45969204405E114EB20C8195DA7E5E' Packaging='embedded' SourcePath='*' Container='WixAttachedContainer' />", payloadElements[0].GetTestXml(ignoreAttributesByElementName)); 55 Assert.Equal("<Payload Id='burn.exe' FilePath='burn.exe' FileSize='463360' Hash='F6E722518AC3AB7E31C70099368D5770788C179AA23226110DCF07319B1E1964E246A1E8AE72E2CF23E0138AFC281BAFDE45969204405E114EB20C8195DA7E5E' Packaging='embedded' SourcePath='a0' Container='WixAttachedContainer' />", payloadElements[0].GetTestXml());
57 } 56 }
58 } 57 }
59 58
diff --git a/src/wix/test/WixToolsetTest.CoreIntegration/PayloadFixture.cs b/src/wix/test/WixToolsetTest.CoreIntegration/PayloadFixture.cs
index 1e6fd0e3..cb35976a 100644
--- a/src/wix/test/WixToolsetTest.CoreIntegration/PayloadFixture.cs
+++ b/src/wix/test/WixToolsetTest.CoreIntegration/PayloadFixture.cs
@@ -189,15 +189,14 @@ namespace WixToolsetTest.CoreIntegration
189 .Cast<XmlElement>() 189 .Cast<XmlElement>()
190 .Select(e => e.GetTestXml(ignoreAttributesByElementName)) 190 .Select(e => e.GetTestXml(ignoreAttributesByElementName))
191 .ToArray(); 191 .ToArray();
192 192 WixAssert.CompareLineByLine(new string[]
193 var ignoreAttributes = new Dictionary<string, List<string>> { { "Payload", new List<string> { "FileSize", "Hash"} } }; 193 {
194 var ignoreAttributesWithSrc = new Dictionary<string, List<string>> { { "Payload", new List<string> { "FileSize", "Hash", "SourcePath" } } }; 194 "<Payload Id='burn.exe' FilePath='burn.exe' FileSize='*' Hash='*' Packaging='embedded' SourcePath='a0' Container='PackagesContainer' />",
195 Assert.Equal(5, payloads.Length); 195 "<Payload Id='test.msi' FilePath='test.msi' FileSize='*' Hash='*' DownloadUrl='http://example.com/id/test.msi/test.msi' Packaging='external' SourcePath='test.msi' />",
196 Assert.Equal(@"<Payload Id='burn.exe' FilePath='burn.exe' FileSize='*' Hash='*' Packaging='embedded' SourcePath='*' Container='PackagesContainer' />", payloads[0].GetTestXml(ignoreAttributesWithSrc)); 196 "<Payload Id='LayoutOnlyPayload' FilePath='DownloadUrlPlaceholdersBundle.wxs' FileSize='*' Hash='*' LayoutOnly='yes' DownloadUrl='http://example.com/id/LayoutOnlyPayload/DownloadUrlPlaceholdersBundle.wxs' Packaging='external' SourcePath='DownloadUrlPlaceholdersBundle.wxs' />",
197 Assert.Equal(@"<Payload Id='test.msi' FilePath='test.msi' FileSize='*' Hash='*' DownloadUrl='http://example.com/id/test.msi/test.msi' Packaging='external' SourcePath='test.msi' />", payloads[1].GetTestXml(ignoreAttributes)); 197 @"<Payload Id='fhuZsOcBDTuIX8rF96kswqI6SnuI' FilePath='MsiPackage\test.txt' FileSize='*' Hash='*' DownloadUrl='http://example.com/test.msiid/fhuZsOcBDTuIX8rF96kswqI6SnuI/MsiPackage/test.txt' Packaging='external' SourcePath='MsiPackage\test.txt' />",
198 Assert.Equal(@"<Payload Id='LayoutOnlyPayload' FilePath='DownloadUrlPlaceholdersBundle.wxs' FileSize='*' Hash='*' LayoutOnly='yes' DownloadUrl='http://example.com/id/LayoutOnlyPayload/DownloadUrlPlaceholdersBundle.wxs' Packaging='external' SourcePath='DownloadUrlPlaceholdersBundle.wxs' />", payloads[2].GetTestXml(ignoreAttributes)); 198 @"<Payload Id='faf_OZ741BG7SJ6ZkcIvivZ2Yzo8' FilePath='MsiPackage\Shared.dll' FileSize='*' Hash='*' DownloadUrl='http://example.com/test.msiid/faf_OZ741BG7SJ6ZkcIvivZ2Yzo8/MsiPackage/Shared.dll' Packaging='external' SourcePath='MsiPackage\Shared.dll' />",
199 Assert.Equal(@"<Payload Id='fhuZsOcBDTuIX8rF96kswqI6SnuI' FilePath='MsiPackage\test.txt' FileSize='*' Hash='*' DownloadUrl='http://example.com/test.msiid/fhuZsOcBDTuIX8rF96kswqI6SnuI/MsiPackage/test.txt' Packaging='external' SourcePath='MsiPackage\test.txt' />", payloads[3].GetTestXml(ignoreAttributes)); 199 }, payloads);
200 Assert.Equal(@"<Payload Id='faf_OZ741BG7SJ6ZkcIvivZ2Yzo8' FilePath='MsiPackage\Shared.dll' FileSize='*' Hash='*' DownloadUrl='http://example.com/test.msiid/faf_OZ741BG7SJ6ZkcIvivZ2Yzo8/MsiPackage/Shared.dll' Packaging='external' SourcePath='MsiPackage\Shared.dll' />", payloads[4].GetTestXml(ignoreAttributes));
201 200
202 var containers = extractResult.SelectManifestNodes("/burn:BurnManifest/burn:Container") 201 var containers = extractResult.SelectManifestNodes("/burn:BurnManifest/burn:Container")
203 .Cast<XmlElement>() 202 .Cast<XmlElement>()