aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRob Mensching <rob@firegiant.com>2022-09-27 01:47:25 -0700
committerRob Mensching <rob@firegiant.com>2022-09-27 16:25:20 -0700
commit188952d84f5789128ddd32e7adf09e60899af43a (patch)
tree088f33ac35c17eb47b6b80c88505dbccddc553c3
parent586d83b42ef69c576303720a6d9c727889842b62 (diff)
downloadwix-188952d84f5789128ddd32e7adf09e60899af43a.tar.gz
wix-188952d84f5789128ddd32e7adf09e60899af43a.tar.bz2
wix-188952d84f5789128ddd32e7adf09e60899af43a.zip
Implement transform decompiling and use it in patch filter tests
-rw-r--r--src/api/wix/WixToolset.Extensibility/Data/IWindowsInstallerDecompileResult.cs6
-rw-r--r--src/test/burn/WixToolsetTest.BurnE2E/SlipstreamTests.cs2
-rw-r--r--src/wix/WixToolset.Core.WindowsInstaller/Bind/CreatePatchTransformsCommand.cs2
-rw-r--r--src/wix/WixToolset.Core.WindowsInstaller/CommandLine/DecompilerSubcommand.cs51
-rw-r--r--src/wix/WixToolset.Core.WindowsInstaller/CommandLine/TransformSubcommand.cs2
-rw-r--r--src/wix/WixToolset.Core.WindowsInstaller/Unbind/UnbindDatabaseCommand.cs42
-rw-r--r--src/wix/WixToolset.Core.WindowsInstaller/Unbind/UnbindTransformCommand.cs314
-rw-r--r--src/wix/WixToolset.Core.WindowsInstaller/WindowsInstallerDecompileResult.cs3
-rw-r--r--src/wix/WixToolset.Core.WindowsInstaller/WindowsInstallerDecompiler.cs37
-rw-r--r--src/wix/test/WixToolsetTest.CoreIntegration/PatchFixture.cs86
-rw-r--r--src/wix/test/WixToolsetTest.CoreIntegration/TestData/PatchFamilyFileFilter/.data/Av1.0.0.txt (renamed from src/wix/test/WixToolsetTest.CoreIntegration/TestData/PatchFamilyFilter/.data/Av1.0.0.txt)0
-rw-r--r--src/wix/test/WixToolsetTest.CoreIntegration/TestData/PatchFamilyFileFilter/.data/Av1.0.1.txt (renamed from src/wix/test/WixToolsetTest.CoreIntegration/TestData/PatchFamilyFilter/.data/Av1.0.1.txt)0
-rw-r--r--src/wix/test/WixToolsetTest.CoreIntegration/TestData/PatchFamilyFileFilter/.data/Bv1.0.0.txt (renamed from src/wix/test/WixToolsetTest.CoreIntegration/TestData/PatchFamilyFilter/.data/Bv1.0.0.txt)0
-rw-r--r--src/wix/test/WixToolsetTest.CoreIntegration/TestData/PatchFamilyFileFilter/.data/Bv1.0.1.txt (renamed from src/wix/test/WixToolsetTest.CoreIntegration/TestData/PatchFamilyFilter/.data/Bv1.0.1.txt)0
-rw-r--r--src/wix/test/WixToolsetTest.CoreIntegration/TestData/PatchFamilyFileFilter/Package.wxs (renamed from src/wix/test/WixToolsetTest.CoreIntegration/TestData/PatchFamilyFilter/Package.wxs)4
-rw-r--r--src/wix/test/WixToolsetTest.CoreIntegration/TestData/PatchFamilyFileFilter/Patch.wxs (renamed from src/wix/test/WixToolsetTest.CoreIntegration/TestData/PatchFamilyFilter/Patch.wxs)2
-rw-r--r--src/wix/test/WixToolsetTest.CoreIntegration/TestData/PatchFamilyRegistryFilter/Patch.wxs16
17 files changed, 390 insertions, 177 deletions
diff --git a/src/api/wix/WixToolset.Extensibility/Data/IWindowsInstallerDecompileResult.cs b/src/api/wix/WixToolset.Extensibility/Data/IWindowsInstallerDecompileResult.cs
index 724dd7fc..4e528bee 100644
--- a/src/api/wix/WixToolset.Extensibility/Data/IWindowsInstallerDecompileResult.cs
+++ b/src/api/wix/WixToolset.Extensibility/Data/IWindowsInstallerDecompileResult.cs
@@ -5,6 +5,7 @@ namespace WixToolset.Extensibility.Data
5 using System.Collections.Generic; 5 using System.Collections.Generic;
6 using System.Xml.Linq; 6 using System.Xml.Linq;
7 using WixToolset.Data; 7 using WixToolset.Data;
8 using WixToolset.Data.WindowsInstaller;
8 9
9 /// <summary> 10 /// <summary>
10 /// The result from decompiling a Windows Installer database. 11 /// The result from decompiling a Windows Installer database.
@@ -12,6 +13,11 @@ namespace WixToolset.Extensibility.Data
12 public interface IWindowsInstallerDecompileResult 13 public interface IWindowsInstallerDecompileResult
13 { 14 {
14 /// <summary> 15 /// <summary>
16 /// Decompiled <c>WindowsInstallerData</c>.
17 /// </summary>
18 WindowsInstallerData Data { get; set; }
19
20 /// <summary>
15 /// Decompiled document. 21 /// Decompiled document.
16 /// </summary> 22 /// </summary>
17 XDocument Document { get; set; } 23 XDocument Document { get; set; }
diff --git a/src/test/burn/WixToolsetTest.BurnE2E/SlipstreamTests.cs b/src/test/burn/WixToolsetTest.BurnE2E/SlipstreamTests.cs
index 9ef100ce..aa005cb1 100644
--- a/src/test/burn/WixToolsetTest.BurnE2E/SlipstreamTests.cs
+++ b/src/test/burn/WixToolsetTest.BurnE2E/SlipstreamTests.cs
@@ -266,7 +266,7 @@ namespace WixToolsetTest.BurnE2E
266 packageBv1.VerifyTestRegistryRootDeleted(); 266 packageBv1.VerifyTestRegistryRootDeleted();
267 } 267 }
268 268
269 [RuntimeFact(Skip = "https://github.com/wixtoolset/issues/issues/6402")] 269 [RuntimeFact]
270 public void CanAutomaticallyPredetermineSlipstreamPatchesAtBuildTime() 270 public void CanAutomaticallyPredetermineSlipstreamPatchesAtBuildTime()
271 { 271 {
272 var testRegistryValueA = "PackageA"; 272 var testRegistryValueA = "PackageA";
diff --git a/src/wix/WixToolset.Core.WindowsInstaller/Bind/CreatePatchTransformsCommand.cs b/src/wix/WixToolset.Core.WindowsInstaller/Bind/CreatePatchTransformsCommand.cs
index 17583e96..5cdafe7e 100644
--- a/src/wix/WixToolset.Core.WindowsInstaller/Bind/CreatePatchTransformsCommand.cs
+++ b/src/wix/WixToolset.Core.WindowsInstaller/Bind/CreatePatchTransformsCommand.cs
@@ -94,7 +94,7 @@ namespace WixToolset.Core.WindowsInstaller.Bind
94 var exportBasePath = Path.Combine(this.IntermediateFolder, stageFolder); 94 var exportBasePath = Path.Combine(this.IntermediateFolder, stageFolder);
95 var extractFilesFolder = Path.Combine(exportBasePath, "File"); 95 var extractFilesFolder = Path.Combine(exportBasePath, "File");
96 96
97 var command = new UnbindDatabaseCommand(this.Messaging, this.BackendHelper, this.PathResolver, path, OutputType.Product, exportBasePath, extractFilesFolder, this.IntermediateFolder, enableDemodularization: false, skipSummaryInfo: false); 97 var command = new UnbindDatabaseCommand(this.Messaging, this.BackendHelper, this.PathResolver, path, null, OutputType.Product, exportBasePath, extractFilesFolder, this.IntermediateFolder, enableDemodularization: false, skipSummaryInfo: false);
98 data = command.Execute(); 98 data = command.Execute();
99 } 99 }
100 100
diff --git a/src/wix/WixToolset.Core.WindowsInstaller/CommandLine/DecompilerSubcommand.cs b/src/wix/WixToolset.Core.WindowsInstaller/CommandLine/DecompilerSubcommand.cs
index b46df22a..f59d83b6 100644
--- a/src/wix/WixToolset.Core.WindowsInstaller/CommandLine/DecompilerSubcommand.cs
+++ b/src/wix/WixToolset.Core.WindowsInstaller/CommandLine/DecompilerSubcommand.cs
@@ -34,6 +34,8 @@ namespace WixToolset.Core.WindowsInstaller.CommandLine
34 34
35 private string ExportBasePath { get; set; } 35 private string ExportBasePath { get; set; }
36 36
37 private bool SaveAsData { get; set; }
38
37 private bool SuppressCustomTables { get; set; } 39 private bool SuppressCustomTables { get; set; }
38 40
39 private bool SuppressDroppingEmptyTables { get; set; } 41 private bool SuppressDroppingEmptyTables { get; set; }
@@ -47,13 +49,14 @@ namespace WixToolset.Core.WindowsInstaller.CommandLine
47 return new CommandLineHelp("Converts a Windows Installer database back into source code.", "msi decompile [options] inputfile", new[] 49 return new CommandLineHelp("Converts a Windows Installer database back into source code.", "msi decompile [options] inputfile", new[]
48 { 50 {
49 new CommandLineHelpSwitch("-cub", "Optional path to a custom validation .CUBe file."), 51 new CommandLineHelpSwitch("-cub", "Optional path to a custom validation .CUBe file."),
52 new CommandLineHelpSwitch("-data", "Save output as data instead of as a source file."),
50 new CommandLineHelpSwitch("-sct", "Suppress decompiling custom tables."), 53 new CommandLineHelpSwitch("-sct", "Suppress decompiling custom tables."),
51 new CommandLineHelpSwitch("-sdet", "Suppress dropping empty tables."), 54 new CommandLineHelpSwitch("-sdet", "Suppress dropping empty tables."),
52 new CommandLineHelpSwitch("-sras", "Suppress relative action sequencing."), 55 new CommandLineHelpSwitch("-sras", "Suppress relative action sequencing."),
53 new CommandLineHelpSwitch("-sui", "Suppress decompiling UI tables."), 56 new CommandLineHelpSwitch("-sui", "Suppress decompiling UI tables."),
54 new CommandLineHelpSwitch("-type", "Optional specify the input file type: msi or msm. If not specified, type will be inferred by file extension."), 57 new CommandLineHelpSwitch("-type", "Optional specify the input file type: msi or msm. If not specified, type will be inferred by file extension."),
55 new CommandLineHelpSwitch("-intermediateFolder", "Optional working folder. If not specified %TMP% will be used."), 58 new CommandLineHelpSwitch("-intermediateFolder", "Optional working folder. If not specified %TMP% will be used."),
56 new CommandLineHelpSwitch("-out", "-o", "Path to output the decompiled .wxs file. If not specified, outputs next to inputfile"), 59 new CommandLineHelpSwitch("-out", "-o", "Path to output the decompiled output file. If not specified, outputs next to inputfile"),
57 new CommandLineHelpSwitch("-x", "Folder to export embedded binaries and icons to."), 60 new CommandLineHelpSwitch("-x", "Folder to export embedded binaries and icons to."),
58 }); 61 });
59 } 62 }
@@ -77,7 +80,9 @@ namespace WixToolset.Core.WindowsInstaller.CommandLine
77 80
78 if (String.IsNullOrEmpty(this.OutputPath)) 81 if (String.IsNullOrEmpty(this.OutputPath))
79 { 82 {
80 this.OutputPath = Path.ChangeExtension(this.InputPath, ".wxs"); 83 var defaultExtension = this.CalculateExtensionFromDecompileType(decompileType);
84
85 this.OutputPath = Path.ChangeExtension(this.InputPath, defaultExtension);
81 } 86 }
82 87
83 var extensionManager = this.ServiceProvider.GetService<IExtensionManager>(); 88 var extensionManager = this.ServiceProvider.GetService<IExtensionManager>();
@@ -92,7 +97,7 @@ namespace WixToolset.Core.WindowsInstaller.CommandLine
92 context.SymbolDefinitionCreator = creator; 97 context.SymbolDefinitionCreator = creator;
93 context.OutputPath = this.OutputPath; 98 context.OutputPath = this.OutputPath;
94 99
95 context.ExtractFolder = this.ExportBasePath ?? this.IntermediateFolder; 100 context.ExtractFolder = this.ExportBasePath;
96 context.SuppressCustomTables = this.SuppressCustomTables; 101 context.SuppressCustomTables = this.SuppressCustomTables;
97 context.SuppressDroppingEmptyTables = this.SuppressDroppingEmptyTables; 102 context.SuppressDroppingEmptyTables = this.SuppressDroppingEmptyTables;
98 context.SuppressRelativeActionSequencing = this.SuppressRelativeActionSequencing; 103 context.SuppressRelativeActionSequencing = this.SuppressRelativeActionSequencing;
@@ -106,7 +111,17 @@ namespace WixToolset.Core.WindowsInstaller.CommandLine
106 if (!this.Messaging.EncounteredError) 111 if (!this.Messaging.EncounteredError)
107 { 112 {
108 Directory.CreateDirectory(Path.GetDirectoryName(Path.GetFullPath(context.OutputPath))); 113 Directory.CreateDirectory(Path.GetDirectoryName(Path.GetFullPath(context.OutputPath)));
109 result.Document.Save(context.OutputPath, SaveOptions.OmitDuplicateNamespaces); 114 if (this.SaveAsData || result.Document == null)
115 {
116 using (var output = WixOutput.Create(context.OutputPath))
117 {
118 result.Data.Save(output);
119 }
120 }
121 else
122 {
123 result.Document.Save(context.OutputPath, SaveOptions.OmitDuplicateNamespaces);
124 }
110 } 125 }
111 } 126 }
112 catch (WixException e) 127 catch (WixException e)
@@ -129,6 +144,10 @@ namespace WixToolset.Core.WindowsInstaller.CommandLine
129 this.IntermediateFolder = parser.GetNextArgumentAsDirectoryOrError(argument); 144 this.IntermediateFolder = parser.GetNextArgumentAsDirectoryOrError(argument);
130 return true; 145 return true;
131 146
147 case "data":
148 this.SaveAsData = true;
149 return true;
150
132 case "o": 151 case "o":
133 case "out": 152 case "out":
134 this.OutputPath = parser.GetNextArgumentAsFilePathOrError(argument, "output file"); 153 this.OutputPath = parser.GetNextArgumentAsFilePathOrError(argument, "output file");
@@ -197,9 +216,33 @@ namespace WixToolset.Core.WindowsInstaller.CommandLine
197 case ".msm": 216 case ".msm":
198 decompileType = OutputType.Module; 217 decompileType = OutputType.Module;
199 break; 218 break;
219
220 case "transform":
221 case "mst":
222 case ".mst":
223 decompileType = OutputType.Transform;
224 break;
200 } 225 }
201 226
202 return decompileType != OutputType.Unknown; 227 return decompileType != OutputType.Unknown;
203 } 228 }
229
230 private string CalculateExtensionFromDecompileType(OutputType decompileType)
231 {
232 switch (decompileType)
233 {
234 case OutputType.Product:
235 return this.SaveAsData ? ".wixmsi" : ".wxs";
236
237 case OutputType.Module:
238 return this.SaveAsData ? ".wixmsm" : ".wxs";
239
240 case OutputType.Transform:
241 return ".wixmst";
242
243 default:
244 return this.SaveAsData ? ".wixdata" : ".wxs";
245 }
246 }
204 } 247 }
205} 248}
diff --git a/src/wix/WixToolset.Core.WindowsInstaller/CommandLine/TransformSubcommand.cs b/src/wix/WixToolset.Core.WindowsInstaller/CommandLine/TransformSubcommand.cs
index 68cb6554..e37267bb 100644
--- a/src/wix/WixToolset.Core.WindowsInstaller/CommandLine/TransformSubcommand.cs
+++ b/src/wix/WixToolset.Core.WindowsInstaller/CommandLine/TransformSubcommand.cs
@@ -379,7 +379,7 @@ namespace WixToolset.Core.WindowsInstaller.CommandLine
379 { 379 {
380 if (!DataLoader.TryLoadWindowsInstallerData(path, out var data)) 380 if (!DataLoader.TryLoadWindowsInstallerData(path, out var data))
381 { 381 {
382 var unbindCommand = new UnbindDatabaseCommand(this.Messaging, this.BackendHelper, this.PathResolver, path, OutputType.Product, this.ExportBasePath, null, this.IntermediateFolder, enableDemodularization: false, skipSummaryInfo: false); 382 var unbindCommand = new UnbindDatabaseCommand(this.Messaging, this.BackendHelper, this.PathResolver, path, null, OutputType.Product, this.ExportBasePath, null, this.IntermediateFolder, enableDemodularization: false, skipSummaryInfo: false);
383 data = unbindCommand.Execute(); 383 data = unbindCommand.Execute();
384 } 384 }
385 385
diff --git a/src/wix/WixToolset.Core.WindowsInstaller/Unbind/UnbindDatabaseCommand.cs b/src/wix/WixToolset.Core.WindowsInstaller/Unbind/UnbindDatabaseCommand.cs
index 5eeb67c8..7f6537e5 100644
--- a/src/wix/WixToolset.Core.WindowsInstaller/Unbind/UnbindDatabaseCommand.cs
+++ b/src/wix/WixToolset.Core.WindowsInstaller/Unbind/UnbindDatabaseCommand.cs
@@ -21,12 +21,13 @@ namespace WixToolset.Core.WindowsInstaller.Unbind
21 { 21 {
22 private static readonly Regex Modularization = new Regex(@"\.[0-9A-Fa-f]{8}_[0-9A-Fa-f]{4}_[0-9A-Fa-f]{4}_[0-9A-Fa-f]{4}_[0-9A-Fa-f]{12}"); 22 private static readonly Regex Modularization = new Regex(@"\.[0-9A-Fa-f]{8}_[0-9A-Fa-f]{4}_[0-9A-Fa-f]{4}_[0-9A-Fa-f]{4}_[0-9A-Fa-f]{12}");
23 23
24 public UnbindDatabaseCommand(IMessaging messaging, IBackendHelper backendHelper, IPathResolver pathResolver, string databasePath, OutputType outputType, string exportBasePath, string extractFilesFolder, string intermediateFolder, bool enableDemodularization, bool skipSummaryInfo) 24 public UnbindDatabaseCommand(IMessaging messaging, IBackendHelper backendHelper, IPathResolver pathResolver, string databasePath, Database database, OutputType outputType, string exportBasePath, string extractFilesFolder, string intermediateFolder, bool enableDemodularization, bool skipSummaryInfo)
25 { 25 {
26 this.Messaging = messaging; 26 this.Messaging = messaging;
27 this.BackendHelper = backendHelper; 27 this.BackendHelper = backendHelper;
28 this.PathResolver = pathResolver; 28 this.PathResolver = pathResolver;
29 this.DatabasePath = databasePath; 29 this.DatabasePath = databasePath;
30 this.Database = database;
30 this.OutputType = outputType; 31 this.OutputType = outputType;
31 this.ExportBasePath = exportBasePath; 32 this.ExportBasePath = exportBasePath;
32 this.ExtractFilesFolder = extractFilesFolder; 33 this.ExtractFilesFolder = extractFilesFolder;
@@ -45,21 +46,21 @@ namespace WixToolset.Core.WindowsInstaller.Unbind
45 46
46 private Database Database { get; set; } 47 private Database Database { get; set; }
47 48
48 public string DatabasePath { get; } 49 private string DatabasePath { get; }
49 50
50 public OutputType OutputType { get; } 51 private OutputType OutputType { get; }
51 52
52 public string ExportBasePath { get; } 53 private string ExportBasePath { get; }
53 54
54 public string ExtractFilesFolder { get; } 55 private string ExtractFilesFolder { get; }
55 56
56 public string IntermediateFolder { get; } 57 private string IntermediateFolder { get; }
57 58
58 public bool EnableDemodularization { get; } 59 private bool EnableDemodularization { get; }
59 60
60 public bool SkipSummaryInfo { get; } 61 private bool SkipSummaryInfo { get; }
61 62
62 public TableDefinitionCollection TableDefinitions { get; } 63 private TableDefinitionCollection TableDefinitions { get; }
63 64
64 public bool AdminImage { get; private set; } 65 public bool AdminImage { get; private set; }
65 66
@@ -77,22 +78,24 @@ namespace WixToolset.Core.WindowsInstaller.Unbind
77 Type = this.OutputType 78 Type = this.OutputType
78 }; 79 };
79 80
81 Database database = null;
80 try 82 try
81 { 83 {
82 using (var database = new Database(this.DatabasePath, OpenDatabase.ReadOnly)) 84 if (this.Database == null)
83 { 85 {
86 database = new Database(this.DatabasePath, OpenDatabase.ReadOnly);
84 this.Database = database; 87 this.Database = database;
88 }
85 89
86 Directory.CreateDirectory(this.IntermediateFolder); 90 Directory.CreateDirectory(this.IntermediateFolder);
87 91
88 data.Codepage = this.GetCodePage(); 92 data.Codepage = this.GetCodePage();
89 93
90 var modularizationGuid = this.ProcessTables(data, exportedFiles); 94 var modularizationGuid = this.ProcessTables(data, exportedFiles);
91 95
92 var summaryInfo = this.ProcessSummaryInfo(data, modularizationGuid); 96 var summaryInfo = this.ProcessSummaryInfo(data, modularizationGuid);
93 97
94 this.UpdateUnrealFileColumns(this.DatabasePath, data, summaryInfo, exportedFiles); 98 this.UpdateUnrealFileColumns(this.DatabasePath, data, summaryInfo, exportedFiles);
95 }
96 } 99 }
97 catch (Win32Exception e) 100 catch (Win32Exception e)
98 { 101 {
@@ -103,6 +106,10 @@ namespace WixToolset.Core.WindowsInstaller.Unbind
103 106
104 throw; 107 throw;
105 } 108 }
109 finally
110 {
111 database?.Dispose();
112 }
106 113
107 this.AdminImage = adminImage; 114 this.AdminImage = adminImage;
108 this.Data = data; 115 this.Data = data;
@@ -542,9 +549,8 @@ namespace WixToolset.Core.WindowsInstaller.Unbind
542 source = Path.Combine(this.ExtractFilesFolder, fileRow.File); 549 source = Path.Combine(this.ExtractFilesFolder, fileRow.File);
543 } 550 }
544 } 551 }
545 else 552 else if (componentDirectoryIndex.TryGetValue(fileRow.Component, out var directoryId)) // this can happen when unbinding an invalid MSI file or MST file with select table modifications.
546 { 553 {
547 var directoryId = componentDirectoryIndex[fileRow.Component];
548 var relativeFileLayoutPath = this.PathResolver.GetFileSourcePath(directories, directoryId, fileRow.FileName, compressed: false, useLongName: summaryInformation.LongFilenames); 554 var relativeFileLayoutPath = this.PathResolver.GetFileSourcePath(directories, directoryId, fileRow.FileName, compressed: false, useLongName: summaryInformation.LongFilenames);
549 555
550 source = Path.Combine(databaseFolder, relativeFileLayoutPath); 556 source = Path.Combine(databaseFolder, relativeFileLayoutPath);
diff --git a/src/wix/WixToolset.Core.WindowsInstaller/Unbind/UnbindTransformCommand.cs b/src/wix/WixToolset.Core.WindowsInstaller/Unbind/UnbindTransformCommand.cs
index bce60e40..f2567ebb 100644
--- a/src/wix/WixToolset.Core.WindowsInstaller/Unbind/UnbindTransformCommand.cs
+++ b/src/wix/WixToolset.Core.WindowsInstaller/Unbind/UnbindTransformCommand.cs
@@ -1,11 +1,8 @@
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. 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 2
3#if TODO_KEEP_FOR_PATCH_UNBINDING_CONSIDERATION_IN_FUTURE
4
5namespace WixToolset.Core.WindowsInstaller.Unbind 3namespace WixToolset.Core.WindowsInstaller.Unbind
6{ 4{
7 using System; 5 using System;
8 using System.Collections;
9 using System.Collections.Generic; 6 using System.Collections.Generic;
10 using System.ComponentModel; 7 using System.ComponentModel;
11 using System.Globalization; 8 using System.Globalization;
@@ -19,10 +16,11 @@ namespace WixToolset.Core.WindowsInstaller.Unbind
19 16
20 internal class UnbindTransformCommand 17 internal class UnbindTransformCommand
21 { 18 {
22 public UnbindTransformCommand(IMessaging messaging, IBackendHelper backendHelper, FileSystemManager fileSystemManager, string transformFile, string exportBasePath, string intermediateFolder) 19 public UnbindTransformCommand(IMessaging messaging, IBackendHelper backendHelper, IPathResolver pathResolver, FileSystemManager fileSystemManager, string transformFile, string exportBasePath, string intermediateFolder)
23 { 20 {
24 this.Messaging = messaging; 21 this.Messaging = messaging;
25 this.BackendHelper = backendHelper; 22 this.BackendHelper = backendHelper;
23 this.PathResolver = pathResolver;
26 this.FileSystemManager = fileSystemManager; 24 this.FileSystemManager = fileSystemManager;
27 this.TransformFile = transformFile; 25 this.TransformFile = transformFile;
28 this.ExportBasePath = exportBasePath; 26 this.ExportBasePath = exportBasePath;
@@ -35,6 +33,8 @@ namespace WixToolset.Core.WindowsInstaller.Unbind
35 33
36 private IBackendHelper BackendHelper { get; } 34 private IBackendHelper BackendHelper { get; }
37 35
36 private IPathResolver PathResolver { get; }
37
38 private FileSystemManager FileSystemManager { get; } 38 private FileSystemManager FileSystemManager { get; }
39 39
40 private string TransformFile { get; } 40 private string TransformFile { get; }
@@ -49,8 +49,10 @@ namespace WixToolset.Core.WindowsInstaller.Unbind
49 49
50 public WindowsInstallerData Execute() 50 public WindowsInstallerData Execute()
51 { 51 {
52 var transform = new WindowsInstallerData(new SourceLineNumber(this.TransformFile)); 52 var transform = new WindowsInstallerData(new SourceLineNumber(this.TransformFile))
53 transform.Type = OutputType.Transform; 53 {
54 Type = OutputType.Transform
55 };
54 56
55 // get the summary information table 57 // get the summary information table
56 using (var summaryInformation = new SummaryInformation(this.TransformFile)) 58 using (var summaryInformation = new SummaryInformation(this.TransformFile))
@@ -71,181 +73,215 @@ namespace WixToolset.Core.WindowsInstaller.Unbind
71 } 73 }
72 74
73 // create a schema msi which hopefully matches the table schemas in the transform 75 // create a schema msi which hopefully matches the table schemas in the transform
74 var schemaOutput = new WindowsInstallerData(null); 76 var schemaDatabasePath = Path.Combine(this.IntermediateFolder, "schema.msi");
77 var schemaData = this.CreateSchemaData(schemaDatabasePath);
78
79 // Bind the schema msi.
80 this.GenerateDatabase(schemaData);
81
82 var transformViewTable = this.OpenTransformViewForAddedAndModifiedRows(schemaDatabasePath);
83
84 var addedRows = this.CreatePlaceholdersForModifiedRowsAndIndexAddedRows(schemaData, transformViewTable);
85
86 // Re-bind the schema output with the placeholder rows over top the original schema database.
87 this.GenerateDatabase(schemaData);
88
89 this.PopulateTransformFromView(schemaDatabasePath, transform, transformViewTable, addedRows);
90
91 return transform;
92 }
93
94 private WindowsInstallerData CreateSchemaData(string schemaDatabasePath)
95 {
96 var schemaData = new WindowsInstallerData(new SourceLineNumber(schemaDatabasePath))
97 {
98 Type = OutputType.Product,
99 };
100
75 foreach (var tableDefinition in this.TableDefinitions) 101 foreach (var tableDefinition in this.TableDefinitions)
76 { 102 {
77 // skip unreal tables and the Patch table 103 // skip unreal tables and the Patch table
78 if (!tableDefinition.Unreal && "Patch" != tableDefinition.Name) 104 if (!tableDefinition.Unreal && "Patch" != tableDefinition.Name)
79 { 105 {
80 schemaOutput.EnsureTable(tableDefinition); 106 schemaData.EnsureTable(tableDefinition);
81 } 107 }
82 } 108 }
83 109
84 var addedRows = new Dictionary<string, Row>(); 110 return schemaData;
85 Table transformViewTable; 111 }
86
87 // Bind the schema msi.
88 var msiDatabaseFile = Path.Combine(this.IntermediateFolder, "schema.msi");
89 this.GenerateDatabase(schemaOutput, msiDatabaseFile);
90 112
91 // Apply the transform to the database and retrieve the modifications. 113 private Table OpenTransformViewForAddedAndModifiedRows(string schemaDatabasePath)
92 using (var msiDatabase = new Database(msiDatabaseFile, OpenDatabase.Transact)) 114 {
115 // Apply the transform with the ViewTransform option to collect all the modifications.
116 using (var msiDatabase = this.ApplyTransformToSchemaDatabase(schemaDatabasePath, TransformErrorConditions.All | TransformErrorConditions.ViewTransform))
93 { 117 {
94 // apply the transform with the ViewTransform option to collect all the modifications
95 msiDatabase.ApplyTransform(this.TransformFile, TransformErrorConditions.All | TransformErrorConditions.ViewTransform);
96
97 // unbind the database 118 // unbind the database
98 var unbindCommand = new UnbindDatabaseCommand(this.Messaging, this.BackendHelper, msiDatabase, msiDatabaseFile, OutputType.Product, this.ExportBasePath, this.IntermediateFolder, enableDemodularization: false, skipSummaryInfo: true); 119 var unbindCommand = new UnbindDatabaseCommand(this.Messaging, this.BackendHelper, this.PathResolver, schemaDatabasePath, msiDatabase, OutputType.Product, null, null, this.IntermediateFolder, enableDemodularization: false, skipSummaryInfo: true);
99 var transformViewOutput = unbindCommand.Execute(); 120 var transformViewOutput = unbindCommand.Execute();
100 121
101 // index the added and possibly modified rows (added rows may also appears as modified rows) 122 return transformViewOutput.Tables["_TransformView"];
102 transformViewTable = transformViewOutput.Tables["_TransformView"]; 123 }
103 var modifiedRows = new Hashtable(); 124 }
104 foreach (var row in transformViewTable.Rows)
105 {
106 var tableName = (string)row[0];
107 var columnName = (string)row[1];
108 var primaryKeys = (string)row[2];
109
110 if ("INSERT" == columnName)
111 {
112 var index = String.Concat(tableName, ':', primaryKeys);
113 125
114 addedRows.Add(index, null); 126 private Dictionary<string, Row> CreatePlaceholdersForModifiedRowsAndIndexAddedRows(WindowsInstallerData schemaData, Table transformViewTable)
115 } 127 {
116 else if ("CREATE" != columnName && "DELETE" != columnName && "DROP" != columnName && null != primaryKeys) // modified row 128 // Index the added and possibly modified rows (added rows may also appears as modified rows).
117 { 129 var addedRows = new Dictionary<string, Row>();
118 var index = String.Concat(tableName, ':', primaryKeys); 130 var modifiedRows = new Dictionary<string, TableNameWithPrimaryKeys>();
119 131
120 modifiedRows[index] = row; 132 foreach (var row in transformViewTable.Rows)
121 } 133 {
122 } 134 var tableName = row.FieldAsString(0);
135 var columnName = row.FieldAsString(1);
136 var primaryKeys = row.FieldAsString(2);
123 137
124 // create placeholder rows for modified rows to make the transform insert the updated values when its applied 138 if ("INSERT" == columnName)
125 foreach (Row row in modifiedRows.Values)
126 { 139 {
127 var tableName = (string)row[0]; 140 var index = String.Concat(tableName, ':', primaryKeys);
128 var columnName = (string)row[1];
129 var primaryKeys = (string)row[2];
130 141
142 addedRows.Add(index, null);
143 }
144 else if ("CREATE" != columnName && "DELETE" != columnName && "DROP" != columnName && null != primaryKeys) // modified row
145 {
131 var index = String.Concat(tableName, ':', primaryKeys); 146 var index = String.Concat(tableName, ':', primaryKeys);
132 147
133 // ignore information for added rows 148 if (!modifiedRows.ContainsKey(index))
134 if (!addedRows.ContainsKey(index))
135 { 149 {
136 var table = schemaOutput.Tables[tableName]; 150 modifiedRows.Add(index, new TableNameWithPrimaryKeys { TableName = tableName, PrimaryKeys = primaryKeys });
137 this.CreateRow(table, primaryKeys, true);
138 } 151 }
139 } 152 }
140 } 153 }
141 154
142 // Re-bind the schema output with the placeholder rows. 155 // Create placeholder rows for modified rows to make the transform insert the updated values when its applied.
143 this.GenerateDatabase(schemaOutput, msiDatabaseFile); 156 foreach (var kvp in modifiedRows)
144
145 // apply the transform to the database and retrieve the modifications
146 using (var msiDatabase = new Database(msiDatabaseFile, OpenDatabase.Transact))
147 { 157 {
148 try 158 var index = kvp.Key;
149 { 159 var tableNameWithPrimaryKey = kvp.Value;
150 // apply the transform
151 msiDatabase.ApplyTransform(this.TransformFile, TransformErrorConditions.All);
152 160
153 // commit the database to guard against weird errors with streams 161 // Ignore added rows.
154 msiDatabase.Commit(); 162 if (!addedRows.ContainsKey(index))
155 }
156 catch (Win32Exception ex)
157 { 163 {
158 if (0x65B == ex.NativeErrorCode) 164 var table = schemaData.Tables[tableNameWithPrimaryKey.TableName];
159 { 165 this.CreateRow(table, tableNameWithPrimaryKey.PrimaryKeys, setRequiredFields: true);
160 // this commonly happens when the transform was built
161 // against a database schema different from the internal
162 // table definitions
163 throw new WixException(ErrorMessages.TransformSchemaMismatch());
164 }
165 } 166 }
167 }
168
169 return addedRows;
170 }
171
172 private void PopulateTransformFromView(string schemaDatabasePath, WindowsInstallerData transform, Table transformViewTable, Dictionary<string, Row> addedRows)
173 {
174 WindowsInstallerData output;
175 // Apply the transform to the database and retrieve the modifications
176 using (var database = this.ApplyTransformToSchemaDatabase(schemaDatabasePath, TransformErrorConditions.All))
177 {
166 178
167 // unbind the database 179 // unbind the database
168 var unbindCommand = new UnbindDatabaseCommand(this.Messaging, this.BackendHelper, msiDatabase, msiDatabaseFile, OutputType.Product, this.ExportBasePath, this.IntermediateFolder, enableDemodularization: false, skipSummaryInfo: true); 180 var unbindCommand = new UnbindDatabaseCommand(this.Messaging, this.BackendHelper, this.PathResolver, schemaDatabasePath, database, OutputType.Product, this.ExportBasePath, null, this.IntermediateFolder, enableDemodularization: false, skipSummaryInfo: true);
169 var output = unbindCommand.Execute(); 181 output = unbindCommand.Execute();
182 }
170 183
171 // index all the rows to easily find modified rows 184 // index all the rows to easily find modified rows
172 var rows = new Dictionary<string, Row>(); 185 var rows = new Dictionary<string, Row>();
173 foreach (var table in output.Tables) 186 foreach (var table in output.Tables)
187 {
188 foreach (var row in table.Rows)
174 { 189 {
175 foreach (var row in table.Rows) 190 rows.Add(String.Concat(table.Name, ':', row.GetPrimaryKey('\t', " ")), row);
176 {
177 rows.Add(String.Concat(table.Name, ':', row.GetPrimaryKey('\t', " ")), row);
178 }
179 } 191 }
192 }
180 193
181 // process the _TransformView rows into transform rows 194 // process the _TransformView rows into transform rows
182 foreach (var row in transformViewTable.Rows) 195 foreach (var row in transformViewTable.Rows)
183 { 196 {
184 var tableName = (string)row[0]; 197 var tableName = row.FieldAsString(0);
185 var columnName = (string)row[1]; 198 var columnName = row.FieldAsString(1);
186 var primaryKeys = (string)row[2]; 199 var primaryKeys = row.FieldAsString(2);
187 200
188 var table = transform.EnsureTable(this.TableDefinitions[tableName]); 201 var table = transform.EnsureTable(this.TableDefinitions[tableName]);
189 202
190 if ("CREATE" == columnName) // added table 203 if ("CREATE" == columnName) // added table
191 { 204 {
192 table.Operation = TableOperation.Add; 205 table.Operation = TableOperation.Add;
193 } 206 }
194 else if ("DELETE" == columnName) // deleted row 207 else if ("DELETE" == columnName) // deleted row
195 { 208 {
196 var deletedRow = this.CreateRow(table, primaryKeys, false); 209 var deletedRow = this.CreateRow(table, primaryKeys, false);
197 deletedRow.Operation = RowOperation.Delete; 210 deletedRow.Operation = RowOperation.Delete;
198 } 211 }
199 else if ("DROP" == columnName) // dropped table 212 else if ("DROP" == columnName) // dropped table
200 { 213 {
201 table.Operation = TableOperation.Drop; 214 table.Operation = TableOperation.Drop;
202 } 215 }
203 else if ("INSERT" == columnName) // added row 216 else if ("INSERT" == columnName) // added row
204 { 217 {
205 var index = String.Concat(tableName, ':', primaryKeys); 218 var index = String.Concat(tableName, ':', primaryKeys);
206 var addedRow = rows[index]; 219 var addedRow = rows[index];
207 addedRow.Operation = RowOperation.Add; 220 addedRow.Operation = RowOperation.Add;
208 table.Rows.Add(addedRow); 221 table.Rows.Add(addedRow);
209 } 222 }
210 else if (null != primaryKeys) // modified row 223 else if (null != primaryKeys) // modified row
224 {
225 var index = String.Concat(tableName, ':', primaryKeys);
226
227 // the _TransformView table includes information for added rows
228 // that looks like modified rows so it sometimes needs to be ignored
229 if (!addedRows.ContainsKey(index))
211 { 230 {
212 var index = String.Concat(tableName, ':', primaryKeys); 231 var modifiedRow = rows[index];
213 232
214 // the _TransformView table includes information for added rows 233 // mark the field as modified
215 // that looks like modified rows so it sometimes needs to be ignored 234 var indexOfModifiedValue = -1;
216 if (!addedRows.ContainsKey(index)) 235 for (var i = 0; i < modifiedRow.TableDefinition.Columns.Length; ++i)
217 { 236 {
218 var modifiedRow = rows[index]; 237 if (columnName.Equals(modifiedRow.TableDefinition.Columns[i].Name, StringComparison.Ordinal))
219
220 // mark the field as modified
221 var indexOfModifiedValue = -1;
222 for (var i = 0; i < modifiedRow.TableDefinition.Columns.Length; ++i)
223 { 238 {
224 if (columnName.Equals(modifiedRow.TableDefinition.Columns[i].Name, StringComparison.Ordinal)) 239 indexOfModifiedValue = i;
225 { 240 break;
226 indexOfModifiedValue = i;
227 break;
228 }
229 } 241 }
230 modifiedRow.Fields[indexOfModifiedValue].Modified = true; 242 }
243 modifiedRow.Fields[indexOfModifiedValue].Modified = true;
231 244
232 // move the modified row into the transform the first time its encountered 245 // move the modified row into the transform the first time its encountered
233 if (RowOperation.None == modifiedRow.Operation) 246 if (RowOperation.None == modifiedRow.Operation)
234 { 247 {
235 modifiedRow.Operation = RowOperation.Modify; 248 modifiedRow.Operation = RowOperation.Modify;
236 table.Rows.Add(modifiedRow); 249 table.Rows.Add(modifiedRow);
237 }
238 } 250 }
239 } 251 }
240 else // added column 252 }
241 { 253 else // added column
242 var column = table.Definition.Columns.Single(c => c.Name.Equals(columnName, StringComparison.Ordinal)); 254 {
243 column.Added = true; 255 var column = table.Definition.Columns.Single(c => c.Name.Equals(columnName, StringComparison.Ordinal));
244 } 256 column.Added = true;
245 } 257 }
246 } 258 }
259 }
247 260
248 return transform; 261 private Database ApplyTransformToSchemaDatabase(string schemaDatabasePath, TransformErrorConditions transformConditions)
262 {
263 var msiDatabase = new Database(schemaDatabasePath, OpenDatabase.Transact);
264
265 try
266 {
267 // apply the transform
268 msiDatabase.ApplyTransform(this.TransformFile, transformConditions);
269
270 // commit the database to guard against weird errors with streams
271 msiDatabase.Commit();
272 }
273 catch (Win32Exception ex)
274 {
275 if (0x65B == ex.NativeErrorCode)
276 {
277 // this commonly happens when the transform was built
278 // against a database schema different from the internal
279 // table definitions
280 throw new WixException(ErrorMessages.TransformSchemaMismatch());
281 }
282 }
283
284 return msiDatabase;
249 } 285 }
250 286
251 /// <summary> 287 /// <summary>
@@ -305,11 +341,17 @@ namespace WixToolset.Core.WindowsInstaller.Unbind
305 return row; 341 return row;
306 } 342 }
307 343
308 private void GenerateDatabase(WindowsInstallerData output, string databaseFile) 344 private void GenerateDatabase(WindowsInstallerData data)
309 { 345 {
310 var command = new GenerateDatabaseCommand(this.Messaging, this.BackendHelper, this.FileSystemManager, output, databaseFile, this.TableDefinitions, this.IntermediateFolder, keepAddedColumns: true, suppressAddingValidationRows: true, useSubdirectory: false); 346 var command = new GenerateDatabaseCommand(this.Messaging, this.BackendHelper, this.FileSystemManager, data, data.SourceLineNumbers.FileName, this.TableDefinitions, this.IntermediateFolder, keepAddedColumns: true, suppressAddingValidationRows: true, useSubdirectory: false);
311 command.Execute(); 347 command.Execute();
312 } 348 }
349
350 private class TableNameWithPrimaryKeys
351 {
352 public string TableName { get; set; }
353
354 public string PrimaryKeys { get; set; }
355 }
313 } 356 }
314} 357}
315#endif
diff --git a/src/wix/WixToolset.Core.WindowsInstaller/WindowsInstallerDecompileResult.cs b/src/wix/WixToolset.Core.WindowsInstaller/WindowsInstallerDecompileResult.cs
index 272fb3c2..0db00c50 100644
--- a/src/wix/WixToolset.Core.WindowsInstaller/WindowsInstallerDecompileResult.cs
+++ b/src/wix/WixToolset.Core.WindowsInstaller/WindowsInstallerDecompileResult.cs
@@ -5,10 +5,13 @@ namespace WixToolset.Core.WindowsInstaller
5 using System.Collections.Generic; 5 using System.Collections.Generic;
6 using System.Xml.Linq; 6 using System.Xml.Linq;
7 using WixToolset.Data; 7 using WixToolset.Data;
8 using WixToolset.Data.WindowsInstaller;
8 using WixToolset.Extensibility.Data; 9 using WixToolset.Extensibility.Data;
9 10
10 internal class WindowsInstallerDecompileResult : IWindowsInstallerDecompileResult 11 internal class WindowsInstallerDecompileResult : IWindowsInstallerDecompileResult
11 { 12 {
13 public WindowsInstallerData Data { get; set; }
14
12 public XDocument Document { get; set; } 15 public XDocument Document { get; set; }
13 16
14 public IList<string> ExtractedFilePaths { get; set; } 17 public IList<string> ExtractedFilePaths { get; set; }
diff --git a/src/wix/WixToolset.Core.WindowsInstaller/WindowsInstallerDecompiler.cs b/src/wix/WixToolset.Core.WindowsInstaller/WindowsInstallerDecompiler.cs
index c78ea93a..f3d116f6 100644
--- a/src/wix/WixToolset.Core.WindowsInstaller/WindowsInstallerDecompiler.cs
+++ b/src/wix/WixToolset.Core.WindowsInstaller/WindowsInstallerDecompiler.cs
@@ -3,13 +3,15 @@
3namespace WixToolset.Core.WindowsInstaller 3namespace WixToolset.Core.WindowsInstaller
4{ 4{
5 using System; 5 using System;
6 using System.ComponentModel.Design; 6 using System.Collections.Generic;
7 using System.IO; 7 using System.IO;
8 using System.Linq; 8 using System.Linq;
9 using WixToolset.Core.WindowsInstaller.Bind;
9 using WixToolset.Core.WindowsInstaller.Decompile; 10 using WixToolset.Core.WindowsInstaller.Decompile;
10 using WixToolset.Core.WindowsInstaller.Unbind; 11 using WixToolset.Core.WindowsInstaller.Unbind;
11 using WixToolset.Data; 12 using WixToolset.Data;
12 using WixToolset.Data.WindowsInstaller; 13 using WixToolset.Data.WindowsInstaller;
14 using WixToolset.Extensibility;
13 using WixToolset.Extensibility.Data; 15 using WixToolset.Extensibility.Data;
14 using WixToolset.Extensibility.Services; 16 using WixToolset.Extensibility.Services;
15 17
@@ -22,12 +24,15 @@ namespace WixToolset.Core.WindowsInstaller
22 { 24 {
23 this.ServiceProvider = serviceProvider; 25 this.ServiceProvider = serviceProvider;
24 this.Messaging = serviceProvider.GetService<IMessaging>(); 26 this.Messaging = serviceProvider.GetService<IMessaging>();
27 this.ExtensionManager = serviceProvider.GetService<IExtensionManager>();
25 } 28 }
26 29
27 private IServiceProvider ServiceProvider { get; } 30 private IServiceProvider ServiceProvider { get; }
28 31
29 private IMessaging Messaging { get; } 32 private IMessaging Messaging { get; }
30 33
34 private IExtensionManager ExtensionManager { get; }
35
31 public IWindowsInstallerDecompileResult Decompile(IWindowsInstallerDecompileContext context) 36 public IWindowsInstallerDecompileResult Decompile(IWindowsInstallerDecompileContext context)
32 { 37 {
33 if (context.SymbolDefinitionCreator == null) 38 if (context.SymbolDefinitionCreator == null)
@@ -62,7 +67,7 @@ namespace WixToolset.Core.WindowsInstaller
62 private IWindowsInstallerDecompileResult Execute(IWindowsInstallerDecompileContext context) 67 private IWindowsInstallerDecompileResult Execute(IWindowsInstallerDecompileContext context)
63 { 68 {
64 // Delete the directory and its files to prevent cab extraction failure due to an existing file. 69 // Delete the directory and its files to prevent cab extraction failure due to an existing file.
65 if (Directory.Exists(context.ExtractFolder)) 70 if (!String.IsNullOrEmpty(context.ExtractFolder) && Directory.Exists(context.ExtractFolder))
66 { 71 {
67 Directory.Delete(context.ExtractFolder, true); 72 Directory.Delete(context.ExtractFolder, true);
68 } 73 }
@@ -71,11 +76,23 @@ namespace WixToolset.Core.WindowsInstaller
71 76
72 var pathResolver = context.ServiceProvider.GetService<IPathResolver>(); 77 var pathResolver = context.ServiceProvider.GetService<IPathResolver>();
73 78
79 if (context.DecompileType == OutputType.Transform)
80 {
81 return this.DecompileTransform(context, backendHelper, pathResolver);
82 }
83 else
84 {
85 return this.DecompileDatabase(context, backendHelper, pathResolver);
86 }
87 }
88
89 private IWindowsInstallerDecompileResult DecompileDatabase(IWindowsInstallerDecompileContext context, IWindowsInstallerBackendHelper backendHelper, IPathResolver pathResolver)
90 {
74 var extractFilesFolder = context.SuppressExtractCabinets || (String.IsNullOrEmpty(context.CabinetExtractFolder) && String.IsNullOrEmpty(context.ExtractFolder)) ? null : 91 var extractFilesFolder = context.SuppressExtractCabinets || (String.IsNullOrEmpty(context.CabinetExtractFolder) && String.IsNullOrEmpty(context.ExtractFolder)) ? null :
75 String.IsNullOrEmpty(context.CabinetExtractFolder) ? Path.Combine(context.ExtractFolder, "File") : context.CabinetExtractFolder; 92 String.IsNullOrEmpty(context.CabinetExtractFolder) ? Path.Combine(context.ExtractFolder, "File") : context.CabinetExtractFolder;
76 93
77 var outputType = context.TreatProductAsModule ? OutputType.Module : context.DecompileType; 94 var outputType = context.TreatProductAsModule ? OutputType.Module : context.DecompileType;
78 var unbindCommand = new UnbindDatabaseCommand(this.Messaging, backendHelper, pathResolver, context.DecompilePath, outputType, context.ExtractFolder, extractFilesFolder, context.IntermediateFolder, enableDemodularization: true, skipSummaryInfo: false); 95 var unbindCommand = new UnbindDatabaseCommand(this.Messaging, backendHelper, pathResolver, context.DecompilePath, null, outputType, context.ExtractFolder, extractFilesFolder, context.IntermediateFolder, enableDemodularization: true, skipSummaryInfo: false);
79 var output = unbindCommand.Execute(); 96 var output = unbindCommand.Execute();
80 var extractedFilePaths = unbindCommand.ExportedFiles; 97 var extractedFilePaths = unbindCommand.ExportedFiles;
81 98
@@ -84,10 +101,24 @@ namespace WixToolset.Core.WindowsInstaller
84 var document = decompiler.Decompile(output); 101 var document = decompiler.Decompile(output);
85 102
86 var result = context.ServiceProvider.GetService<IWindowsInstallerDecompileResult>(); 103 var result = context.ServiceProvider.GetService<IWindowsInstallerDecompileResult>();
104 result.Data = output;
87 result.Document = document; 105 result.Document = document;
88 result.Platform = GetPlatformFromOutput(output); 106 result.Platform = GetPlatformFromOutput(output);
89 result.ExtractedFilePaths = extractedFilePaths.ToList(); 107 result.ExtractedFilePaths = extractedFilePaths.ToList();
108 return result;
109 }
110
111 private IWindowsInstallerDecompileResult DecompileTransform(IWindowsInstallerDecompileContext context, IWindowsInstallerBackendHelper backendHelper, IPathResolver pathResolver)
112 {
113 var fileSystemExtensions = this.ExtensionManager.GetServices<IFileSystemExtension>();
90 114
115 var fileSystemManager = new FileSystemManager(fileSystemExtensions);
116
117 var unbindCommand = new UnbindTransformCommand(this.Messaging, backendHelper, pathResolver, fileSystemManager, context.DecompilePath, context.ExtractFolder, context.IntermediateFolder);
118 var output = unbindCommand.Execute();
119
120 var result = context.ServiceProvider.GetService<IWindowsInstallerDecompileResult>();
121 result.Data = output;
91 return result; 122 return result;
92 } 123 }
93 124
diff --git a/src/wix/test/WixToolsetTest.CoreIntegration/PatchFixture.cs b/src/wix/test/WixToolsetTest.CoreIntegration/PatchFixture.cs
index b0d49c05..123e5742 100644
--- a/src/wix/test/WixToolsetTest.CoreIntegration/PatchFixture.cs
+++ b/src/wix/test/WixToolsetTest.CoreIntegration/PatchFixture.cs
@@ -18,6 +18,8 @@ namespace WixToolsetTest.CoreIntegration
18 using WixToolset.Core.TestPackage; 18 using WixToolset.Core.TestPackage;
19 using WixToolset.Data; 19 using WixToolset.Data;
20 using WixToolset.Data.Burn; 20 using WixToolset.Data.Burn;
21 using WixToolset.Data.WindowsInstaller;
22 using WixToolset.Dtf.Compression.Cab;
21 using Xunit; 23 using Xunit;
22 24
23 public class PatchFixture : IDisposable 25 public class PatchFixture : IDisposable
@@ -395,9 +397,9 @@ namespace WixToolsetTest.CoreIntegration
395 } 397 }
396 398
397 [Fact] 399 [Fact]
398 public void CanBuildPatchWithFiltering() 400 public void CanBuildPatchWithFileFiltering()
399 { 401 {
400 var sourceFolder = TestData.Get(@"TestData", "PatchFamilyFilter"); 402 var sourceFolder = TestData.Get(@"TestData", "PatchFamilyFileFilter");
401 403
402 using (var fs = new DisposableFileSystem()) 404 using (var fs = new DisposableFileSystem())
403 { 405 {
@@ -406,23 +408,50 @@ namespace WixToolsetTest.CoreIntegration
406 408
407 var patchPath = BuildMsp("Patch1.msp", sourceFolder, tempFolderPatch, "1.0.1", bindpaths: new[] { Path.GetDirectoryName(this.templateBaselinePdb), Path.GetDirectoryName(this.templateUpdatePdb) }); 409 var patchPath = BuildMsp("Patch1.msp", sourceFolder, tempFolderPatch, "1.0.1", bindpaths: new[] { Path.GetDirectoryName(this.templateBaselinePdb), Path.GetDirectoryName(this.templateUpdatePdb) });
408 410
409 var doc = GetExtractPatchXml(patchPath); 411 var mainTransform = ExtractBaselinePatch(patchPath, "RTM.1", baseFolder);
410 WixAssert.StringEqual("{11111111-2222-3333-4444-555555555555}", doc.Root.Element(TargetProductCodeName).Value); 412 Assert.Null(mainTransform.Tables["Registry"]);
411 413 var fileRow = mainTransform.Tables["File"].Rows.Single();
412 var names = Query.GetSubStorageNames(patchPath); 414 Assert.Equal("a.txt", fileRow.FieldAsString(0));
413 WixAssert.CompareLineByLine(new[] { "#RTM.1", "RTM.1" }, names); 415 Assert.Equal(152, fileRow.FieldAsInteger(3));
414 416
415 var cab = Path.Combine(baseFolder, "foo.cab"); 417 var pairedTransform = ExtractBaselinePatch(patchPath, "#RTM.1", baseFolder);
416 Query.ExtractStream(patchPath, "foo.cab", cab); 418 fileRow = mainTransform.Tables["File"].Rows.Single();
419 Assert.Equal("a.txt", fileRow.FieldAsString(0));
420 Assert.Equal(152, fileRow.FieldAsInteger(3));
417 421
418 var files = Query.GetCabinetFiles(cab); 422 var files = ExtractFilesFromPatchCabinet(patchPath, "foo.cab", baseFolder);
419 var file = files.Single(); 423 var file = files.Single();
420 WixAssert.StringEqual("a.txt", file.Name);
421 var contents = file.OpenText().ReadToEnd(); 424 var contents = file.OpenText().ReadToEnd();
422 WixAssert.StringEqual("This is A v1.0.1 from the '.update-data' folder in 'PatchTemplatePackage'.\r\n\r\nLorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.\r\n", contents); 425 WixAssert.StringEqual("This is A v1.0.1 from the '.update-data' folder in 'PatchTemplatePackage'.\r\n\r\nLorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod.\r\n", contents);
423 } 426 }
424 } 427 }
425 428
429 [Fact]
430 public void CanBuildPatchWithRegistryFiltering()
431 {
432 var sourceFolder = TestData.Get(@"TestData", "PatchFamilyRegistryFilter");
433
434 using (var fs = new DisposableFileSystem())
435 {
436 var baseFolder = fs.GetFolder();
437 var tempFolderPatch = Path.Combine(baseFolder, "patch");
438
439 var patchPath = BuildMsp("Patch1.msp", sourceFolder, tempFolderPatch, "1.0.1", bindpaths: new[] { Path.GetDirectoryName(this.templateBaselinePdb), Path.GetDirectoryName(this.templateUpdatePdb) }, warningsAsErrors: false);
440
441 var mainTransform = ExtractBaselinePatch(patchPath, "RTM.1", baseFolder);
442 Assert.Null(mainTransform.Tables["File"]);
443 var row = mainTransform.Tables["Registry"].Rows.Single();
444 Assert.Equal("regUty0zLJ5uYhRlGzmOzENKmnAtno", row.FieldAsString(0));
445 Assert.Equal("1.0.1", row.FieldAsString(4));
446
447 var pairedTransform = ExtractBaselinePatch(patchPath, "#RTM.1", baseFolder);
448 Assert.Null(pairedTransform.Tables["File"]);
449
450 var files = ExtractFilesFromPatchCabinet(patchPath, "foo.cab", baseFolder);
451 Assert.Empty(files);
452 }
453 }
454
426 private static string BuildMsi(string outputName, string sourceFolder, string baseFolder, string defineV, string defineA, string defineB, IEnumerable<string> bindpaths = null) 455 private static string BuildMsi(string outputName, string sourceFolder, string baseFolder, string defineV, string defineA, string defineB, IEnumerable<string> bindpaths = null)
427 { 456 {
428 var extensionPath = Path.GetFullPath(new Uri(typeof(ExampleExtensionFactory).Assembly.CodeBase).LocalPath); 457 var extensionPath = Path.GetFullPath(new Uri(typeof(ExampleExtensionFactory).Assembly.CodeBase).LocalPath);
@@ -552,6 +581,41 @@ namespace WixToolsetTest.CoreIntegration
552 Assert.Equal(0, proc.ExitCode); 581 Assert.Equal(0, proc.ExitCode);
553 } 582 }
554 583
584 private static WindowsInstallerData DecompileMst(string transformPath, string baseFolder)
585 {
586 var outputPath = Path.ChangeExtension(transformPath, ".wixmst");
587
588 var args = new List<string>
589 {
590 "msi", "decompile",
591 transformPath,
592 "-intermediateFolder", Path.Combine(baseFolder),
593 "-o", outputPath,
594 };
595
596 var result = WixRunner.Execute(args.ToArray());
597
598 result.AssertSuccess();
599
600 return WindowsInstallerData.Load(outputPath);
601 }
602
603 private static WindowsInstallerData ExtractBaselinePatch(string patchPath, string substorageName, string baseFolder)
604 {
605 var mstPath = Path.Combine(baseFolder, substorageName, substorageName + ".mst");
606 Query.ExtractSubStorage(patchPath, substorageName, mstPath);
607
608 return DecompileMst(mstPath, baseFolder);
609 }
610
611 private static CabFileInfo[] ExtractFilesFromPatchCabinet(string patchPath, string cabinetName, string baseFolder)
612 {
613 var cab = Path.Combine(baseFolder, cabinetName);
614 Query.ExtractStream(patchPath, cabinetName, cab);
615
616 return Query.GetCabinetFiles(cab);
617 }
618
555 private static XDocument GetExtractPatchXml(string path) 619 private static XDocument GetExtractPatchXml(string path)
556 { 620 {
557 var buffer = new StringBuilder(65535); 621 var buffer = new StringBuilder(65535);
diff --git a/src/wix/test/WixToolsetTest.CoreIntegration/TestData/PatchFamilyFilter/.data/Av1.0.0.txt b/src/wix/test/WixToolsetTest.CoreIntegration/TestData/PatchFamilyFileFilter/.data/Av1.0.0.txt
index 6fd385bd..6fd385bd 100644
--- a/src/wix/test/WixToolsetTest.CoreIntegration/TestData/PatchFamilyFilter/.data/Av1.0.0.txt
+++ b/src/wix/test/WixToolsetTest.CoreIntegration/TestData/PatchFamilyFileFilter/.data/Av1.0.0.txt
diff --git a/src/wix/test/WixToolsetTest.CoreIntegration/TestData/PatchFamilyFilter/.data/Av1.0.1.txt b/src/wix/test/WixToolsetTest.CoreIntegration/TestData/PatchFamilyFileFilter/.data/Av1.0.1.txt
index b1f0bc01..b1f0bc01 100644
--- a/src/wix/test/WixToolsetTest.CoreIntegration/TestData/PatchFamilyFilter/.data/Av1.0.1.txt
+++ b/src/wix/test/WixToolsetTest.CoreIntegration/TestData/PatchFamilyFileFilter/.data/Av1.0.1.txt
diff --git a/src/wix/test/WixToolsetTest.CoreIntegration/TestData/PatchFamilyFilter/.data/Bv1.0.0.txt b/src/wix/test/WixToolsetTest.CoreIntegration/TestData/PatchFamilyFileFilter/.data/Bv1.0.0.txt
index ece55fec..ece55fec 100644
--- a/src/wix/test/WixToolsetTest.CoreIntegration/TestData/PatchFamilyFilter/.data/Bv1.0.0.txt
+++ b/src/wix/test/WixToolsetTest.CoreIntegration/TestData/PatchFamilyFileFilter/.data/Bv1.0.0.txt
diff --git a/src/wix/test/WixToolsetTest.CoreIntegration/TestData/PatchFamilyFilter/.data/Bv1.0.1.txt b/src/wix/test/WixToolsetTest.CoreIntegration/TestData/PatchFamilyFileFilter/.data/Bv1.0.1.txt
index cf3372fd..cf3372fd 100644
--- a/src/wix/test/WixToolsetTest.CoreIntegration/TestData/PatchFamilyFilter/.data/Bv1.0.1.txt
+++ b/src/wix/test/WixToolsetTest.CoreIntegration/TestData/PatchFamilyFileFilter/.data/Bv1.0.1.txt
diff --git a/src/wix/test/WixToolsetTest.CoreIntegration/TestData/PatchFamilyFilter/Package.wxs b/src/wix/test/WixToolsetTest.CoreIntegration/TestData/PatchFamilyFileFilter/Package.wxs
index c9dcdd72..6e1db0bc 100644
--- a/src/wix/test/WixToolsetTest.CoreIntegration/TestData/PatchFamilyFilter/Package.wxs
+++ b/src/wix/test/WixToolsetTest.CoreIntegration/TestData/PatchFamilyFileFilter/Package.wxs
@@ -1,4 +1,5 @@
1<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs"> 1<!-- TODO: DELETE ???
2<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs">
2 <Package Name="~Test Package" Version="$(var.V)" Manufacturer="Example Corporation" Language="1033" UpgradeCode="e703bf17-4765-444c-91fd-88550fa681d4" Scope="perMachine" ProductCode="e703bf17-4765-444c-91fd-88550fa681d4"> 3 <Package Name="~Test Package" Version="$(var.V)" Manufacturer="Example Corporation" Language="1033" UpgradeCode="e703bf17-4765-444c-91fd-88550fa681d4" Scope="perMachine" ProductCode="e703bf17-4765-444c-91fd-88550fa681d4">
3 4
4 5
@@ -26,3 +27,4 @@
26 </ComponentGroup> 27 </ComponentGroup>
27 </Fragment> 28 </Fragment>
28</Wix> 29</Wix>
30-->
diff --git a/src/wix/test/WixToolsetTest.CoreIntegration/TestData/PatchFamilyFilter/Patch.wxs b/src/wix/test/WixToolsetTest.CoreIntegration/TestData/PatchFamilyFileFilter/Patch.wxs
index f48fd1ef..3723af48 100644
--- a/src/wix/test/WixToolsetTest.CoreIntegration/TestData/PatchFamilyFilter/Patch.wxs
+++ b/src/wix/test/WixToolsetTest.CoreIntegration/TestData/PatchFamilyFileFilter/Patch.wxs
@@ -10,7 +10,7 @@
10 10
11 <Fragment> 11 <Fragment>
12 <PatchFamily Id="SamplePatchFamily" Version="$(var.V)" Supersede="yes"> 12 <PatchFamily Id="SamplePatchFamily" Version="$(var.V)" Supersede="yes">
13 <ComponentRef Id="A" /> 13 <ComponentRef Id="a.txt" />
14 </PatchFamily> 14 </PatchFamily>
15 </Fragment> 15 </Fragment>
16</Wix> 16</Wix>
diff --git a/src/wix/test/WixToolsetTest.CoreIntegration/TestData/PatchFamilyRegistryFilter/Patch.wxs b/src/wix/test/WixToolsetTest.CoreIntegration/TestData/PatchFamilyRegistryFilter/Patch.wxs
new file mode 100644
index 00000000..dab89379
--- /dev/null
+++ b/src/wix/test/WixToolsetTest.CoreIntegration/TestData/PatchFamilyRegistryFilter/Patch.wxs
@@ -0,0 +1,16 @@
1<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs">
2 <Patch AllowRemoval="yes" Manufacturer="FireGiant" MoreInfoURL="http://www.example.com/" DisplayName="~Test Patch v$(var.V)" Description="~Test Small Update Patch v($var.V)" Classification="Update">
3
4 <Media Id="1" Cabinet="foo.cab">
5 <PatchBaseline Id="RTM" BaselineFile="Baseline.wixpdb" UpdateFile="Update.wixpdb" />
6 </Media>
7
8 <PatchFamilyRef Id="SamplePatchFamily" />
9 </Patch>
10
11 <Fragment>
12 <PatchFamily Id="SamplePatchFamily" Version="$(var.V)" Supersede="yes">
13 <ComponentRef Id="regUty0zLJ5uYhRlGzmOzENKmnAtno" />
14 </PatchFamily>
15 </Fragment>
16</Wix>