diff options
author | Rob Mensching <rob@firegiant.com> | 2022-09-27 01:47:25 -0700 |
---|---|---|
committer | Rob Mensching <rob@firegiant.com> | 2022-09-27 16:25:20 -0700 |
commit | 188952d84f5789128ddd32e7adf09e60899af43a (patch) | |
tree | 088f33ac35c17eb47b6b80c88505dbccddc553c3 | |
parent | 586d83b42ef69c576303720a6d9c727889842b62 (diff) | |
download | wix-188952d84f5789128ddd32e7adf09e60899af43a.tar.gz wix-188952d84f5789128ddd32e7adf09e60899af43a.tar.bz2 wix-188952d84f5789128ddd32e7adf09e60899af43a.zip |
Implement transform decompiling and use it in patch filter tests
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 | |||
5 | namespace WixToolset.Core.WindowsInstaller.Unbind | 3 | namespace 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 @@ | |||
3 | namespace WixToolset.Core.WindowsInstaller | 3 | namespace 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> | ||