aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRob Mensching <rob@firegiant.com>2022-03-23 10:25:49 -0700
committerRob Mensching <rob@firegiant.com>2022-03-30 14:12:12 -0700
commitc86a2148f6dd7bfcd6637b6e1c9e7b5a9b53a996 (patch)
tree547736274aca4871f71578222e74d9972609c0c6
parenteedde7ee47ab7b3bef417f2d631814b586cdb011 (diff)
downloadwix-c86a2148f6dd7bfcd6637b6e1c9e7b5a9b53a996.tar.gz
wix-c86a2148f6dd7bfcd6637b6e1c9e7b5a9b53a996.tar.bz2
wix-c86a2148f6dd7bfcd6637b6e1c9e7b5a9b53a996.zip
Make "decompile" an MSI-only command instead of a backend requirement
As much as I'd like decompiling to be global functionality provided by all backends there are only two output types that support decompiling: MSI and MSM. In the future, perhaps we can invest in a generic symbols to source code converter that would reduce the redundant work backends need to do today. Until then, make decompile an MSI specific command
-rw-r--r--src/api/wix/WixToolset.Extensibility/Data/IWindowsInstallerDecompileContext.cs (renamed from src/api/wix/WixToolset.Extensibility/Data/IDecompileContext.cs)44
-rw-r--r--src/api/wix/WixToolset.Extensibility/Data/IWindowsInstallerDecompileResult.cs (renamed from src/api/wix/WixToolset.Extensibility/Data/IDecompileResult.cs)2
-rw-r--r--src/api/wix/WixToolset.Extensibility/IBackend.cs3
-rw-r--r--src/api/wix/WixToolset.Extensibility/IBackendFactory.cs6
-rw-r--r--src/api/wix/WixToolset.Extensibility/IDecompilerExtension.cs22
-rw-r--r--src/api/wix/WixToolset.Extensibility/IWindowsInstallerDecompilerExtension.cs (renamed from src/api/wix/WixToolset.Extensibility/IWindowsInstallerBackendDecompilerExtension.cs)6
-rw-r--r--src/api/wix/WixToolset.Extensibility/Services/ICommandLineParser.cs97
-rw-r--r--src/wix/WixToolset.Core.Burn/BundleBackend.cs6
-rw-r--r--src/wix/WixToolset.Core.WindowsInstaller/CommandLine/DecompilerSubcommand.cs185
-rw-r--r--src/wix/WixToolset.Core.WindowsInstaller/CommandLine/WindowsInstallerCommand.cs4
-rw-r--r--src/wix/WixToolset.Core.WindowsInstaller/Decompile/DecompileMsiOrMsmCommand.cs17
-rw-r--r--src/wix/WixToolset.Core.WindowsInstaller/Decompile/Decompiler.cs125
-rw-r--r--src/wix/WixToolset.Core.WindowsInstaller/IWindowsInstallerDecompiler.cs19
-rw-r--r--src/wix/WixToolset.Core.WindowsInstaller/MsiBackend.cs22
-rw-r--r--src/wix/WixToolset.Core.WindowsInstaller/MsmBackend.cs26
-rw-r--r--src/wix/WixToolset.Core.WindowsInstaller/MspBackend.cs5
-rw-r--r--src/wix/WixToolset.Core.WindowsInstaller/WindowsInstallerDecompileContext.cs (renamed from src/wix/WixToolset.Core/DecompileContext.cs)11
-rw-r--r--src/wix/WixToolset.Core.WindowsInstaller/WindowsInstallerDecompileResult.cs (renamed from src/wix/WixToolset.Core/DecompileResult.cs)4
-rw-r--r--src/wix/WixToolset.Core.WindowsInstaller/WindowsInstallerDecompiler.cs (renamed from src/wix/WixToolset.Core/Decompiler.cs)32
-rw-r--r--src/wix/WixToolset.Core.WindowsInstaller/WixToolsetCoreServiceProviderExtensions.cs6
-rw-r--r--src/wix/WixToolset.Core/CommandLine/CommandLine.cs23
-rw-r--r--src/wix/WixToolset.Core/CommandLine/CommandLineParser.cs25
-rw-r--r--src/wix/WixToolset.Core/CommandLine/DecompileCommand.cs202
-rw-r--r--src/wix/WixToolset.Core/IDecompiler.cs12
-rw-r--r--src/wix/WixToolset.Core/WixToolsetServiceProvider.cs3
-rw-r--r--src/wix/test/WixToolsetTest.Converters/ProductPackageFixture.cs2
-rw-r--r--src/wix/test/WixToolsetTest.CoreIntegration/CustomTableFixture.cs2
-rw-r--r--src/wix/test/WixToolsetTest.CoreIntegration/DecompileFixture.cs2
28 files changed, 468 insertions, 445 deletions
diff --git a/src/api/wix/WixToolset.Extensibility/Data/IDecompileContext.cs b/src/api/wix/WixToolset.Extensibility/Data/IWindowsInstallerDecompileContext.cs
index fe7d0465..f744121a 100644
--- a/src/api/wix/WixToolset.Extensibility/Data/IDecompileContext.cs
+++ b/src/api/wix/WixToolset.Extensibility/Data/IWindowsInstallerDecompileContext.cs
@@ -5,21 +5,40 @@ namespace WixToolset.Extensibility.Data
5 using System; 5 using System;
6 using System.Collections.Generic; 6 using System.Collections.Generic;
7 using WixToolset.Data; 7 using WixToolset.Data;
8 using WixToolset.Extensibility.Services;
9 8
10#pragma warning disable 1591 // TODO: add documentation 9 /// <summary>
11 public interface IDecompileContext 10 /// The context used to decompile Windows Installer packages.
11 /// </summary>
12 public interface IWindowsInstallerDecompileContext
12 { 13 {
14 /// <summary>
15 /// Gets or sets the service provider.
16 /// </summary>
13 IServiceProvider ServiceProvider { get; } 17 IServiceProvider ServiceProvider { get; }
14 18
19 /// <summary>
20 /// Gets or sets the path to the file to decompile.
21 /// </summary>
15 string DecompilePath { get; set; } 22 string DecompilePath { get; set; }
16 23
24 /// <summary>
25 /// Gets or sets the type to decompile.
26 /// </summary>
17 OutputType DecompileType { get; set; } 27 OutputType DecompileType { get; set; }
18 28
19 IReadOnlyCollection<IDecompilerExtension> Extensions { get; set; } 29 /// <summary>
30 /// Gets or sets the decompiler extensions.
31 /// </summary>
32 IReadOnlyCollection<IWindowsInstallerDecompilerExtension> Extensions { get; set; }
20 33
34 /// <summary>
35 /// Gets or sets the folder where content is extracted.
36 /// </summary>
21 string ExtractFolder { get; set; } 37 string ExtractFolder { get; set; }
22 38
39 /// <summary>
40 /// Gets or sets the folder where files are extracted.
41 /// </summary>
23 string CabinetExtractFolder { get; set; } 42 string CabinetExtractFolder { get; set; }
24 43
25 /// <summary> 44 /// <summary>
@@ -28,10 +47,19 @@ namespace WixToolset.Extensibility.Data
28 /// <remarks>Default value is "SourceDir" to enable use of BindPaths.</remarks> 47 /// <remarks>Default value is "SourceDir" to enable use of BindPaths.</remarks>
29 string BaseSourcePath { get; set; } 48 string BaseSourcePath { get; set; }
30 49
50 /// <summary>
51 /// Gets or sets the intermediate folder.
52 /// </summary>
31 string IntermediateFolder { get; set; } 53 string IntermediateFolder { get; set; }
32 54
55 /// <summary>
56 /// Gets or sets whether the decompiler admin image.
57 /// </summary>
33 bool IsAdminImage { get; set; } 58 bool IsAdminImage { get; set; }
34 59
60 /// <summary>
61 /// Gets or sets where to output the result.
62 /// </summary>
35 string OutputPath { get; set; } 63 string OutputPath { get; set; }
36 64
37 /// <summary> 65 /// <summary>
@@ -44,9 +72,17 @@ namespace WixToolset.Extensibility.Data
44 /// </summary> 72 /// </summary>
45 bool SuppressDroppingEmptyTables { get; set; } 73 bool SuppressDroppingEmptyTables { get; set; }
46 74
75 /// <summary>
76 /// Gets or sets whether to prevent extract cabinets.
77 /// </summary>
47 bool SuppressExtractCabinets { get; set; } 78 bool SuppressExtractCabinets { get; set; }
48 79
49 /// <summary> 80 /// <summary>
81 /// Gets or sets whether to suppress relative action sequencing.
82 /// </summary>
83 bool SuppressRelativeActionSequencing { get; set; }
84
85 /// <summary>
50 /// Gets or sets the option to suppress decompiling UI-related tables. 86 /// Gets or sets the option to suppress decompiling UI-related tables.
51 /// </summary> 87 /// </summary>
52 bool SuppressUI { get; set; } 88 bool SuppressUI { get; set; }
diff --git a/src/api/wix/WixToolset.Extensibility/Data/IDecompileResult.cs b/src/api/wix/WixToolset.Extensibility/Data/IWindowsInstallerDecompileResult.cs
index cffd0976..3b1dd815 100644
--- a/src/api/wix/WixToolset.Extensibility/Data/IDecompileResult.cs
+++ b/src/api/wix/WixToolset.Extensibility/Data/IWindowsInstallerDecompileResult.cs
@@ -7,7 +7,7 @@ namespace WixToolset.Extensibility.Data
7 using WixToolset.Data; 7 using WixToolset.Data;
8 8
9#pragma warning disable 1591 // TODO: add documentation 9#pragma warning disable 1591 // TODO: add documentation
10 public interface IDecompileResult 10 public interface IWindowsInstallerDecompileResult
11 { 11 {
12 XDocument Document { get; set; } 12 XDocument Document { get; set; }
13 13
diff --git a/src/api/wix/WixToolset.Extensibility/IBackend.cs b/src/api/wix/WixToolset.Extensibility/IBackend.cs
index cb151e05..720abe15 100644
--- a/src/api/wix/WixToolset.Extensibility/IBackend.cs
+++ b/src/api/wix/WixToolset.Extensibility/IBackend.cs
@@ -15,8 +15,5 @@ namespace WixToolset.Extensibility
15 /// <param name="context">Bind context.</param> 15 /// <param name="context">Bind context.</param>
16 /// <returns>Result of the bind operation.</returns> 16 /// <returns>Result of the bind operation.</returns>
17 IBindResult Bind(IBindContext context); 17 IBindResult Bind(IBindContext context);
18
19#pragma warning disable 1591 // TODO: add documentation
20 IDecompileResult Decompile(IDecompileContext context);
21 } 18 }
22} 19}
diff --git a/src/api/wix/WixToolset.Extensibility/IBackendFactory.cs b/src/api/wix/WixToolset.Extensibility/IBackendFactory.cs
index 7f9ef62d..8e84f5b6 100644
--- a/src/api/wix/WixToolset.Extensibility/IBackendFactory.cs
+++ b/src/api/wix/WixToolset.Extensibility/IBackendFactory.cs
@@ -3,7 +3,7 @@
3namespace WixToolset.Extensibility 3namespace WixToolset.Extensibility
4{ 4{
5 /// <summary> 5 /// <summary>
6 /// Implemented by extensions to create backends. 6 /// Implemented by extensions that are backends.
7 /// </summary> 7 /// </summary>
8 public interface IBackendFactory 8 public interface IBackendFactory
9 { 9 {
@@ -12,8 +12,8 @@ namespace WixToolset.Extensibility
12 /// </summary> 12 /// </summary>
13 /// <param name="outputType">Type of output being created.</param> 13 /// <param name="outputType">Type of output being created.</param>
14 /// <param name="outputPath">Path to the output to create.</param> 14 /// <param name="outputPath">Path to the output to create.</param>
15 /// <param name="backend">The backend for the output.</param> 15 /// <param name="binder">The backend for the output.</param>
16 /// <returns>True if the backend was created, otherwise false.</returns> 16 /// <returns>True if the backend was created, otherwise false.</returns>
17 bool TryCreateBackend(string outputType, string outputPath, out IBackend backend); 17 bool TryCreateBackend(string outputType, string outputPath, out IBackend binder);
18 } 18 }
19} 19}
diff --git a/src/api/wix/WixToolset.Extensibility/IDecompilerExtension.cs b/src/api/wix/WixToolset.Extensibility/IDecompilerExtension.cs
deleted file mode 100644
index 24ef3bff..00000000
--- a/src/api/wix/WixToolset.Extensibility/IDecompilerExtension.cs
+++ /dev/null
@@ -1,22 +0,0 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3namespace WixToolset.Extensibility
4{
5 using WixToolset.Extensibility.Data;
6
7 /// <summary>
8 /// Base class for creating a decompiler extension.
9 /// </summary>
10 public interface IDecompilerExtension
11 {
12 /// <summary>
13 /// Called before decompiling occurs.
14 /// </summary>
15 void PreDecompile(IDecompileContext context);
16
17 /// <summary>
18 /// Called after all decompiling occurs.
19 /// </summary>
20 void PostDecompile(IDecompileResult result);
21 }
22}
diff --git a/src/api/wix/WixToolset.Extensibility/IWindowsInstallerBackendDecompilerExtension.cs b/src/api/wix/WixToolset.Extensibility/IWindowsInstallerDecompilerExtension.cs
index a56b63c3..add5f886 100644
--- a/src/api/wix/WixToolset.Extensibility/IWindowsInstallerBackendDecompilerExtension.cs
+++ b/src/api/wix/WixToolset.Extensibility/IWindowsInstallerDecompilerExtension.cs
@@ -9,18 +9,18 @@ namespace WixToolset.Extensibility
9 /// <summary> 9 /// <summary>
10 /// Interface all binder extensions implement. 10 /// Interface all binder extensions implement.
11 /// </summary> 11 /// </summary>
12 public interface IWindowsInstallerBackendDecompilerExtension 12 public interface IWindowsInstallerDecompilerExtension
13 { 13 {
14 /// <summary> 14 /// <summary>
15 /// Called before decompiling occurs. 15 /// Called before decompiling occurs.
16 /// </summary> 16 /// </summary>
17 void PreBackendDecompile(IDecompileContext context); 17 void PreDecompile(IWindowsInstallerDecompileContext context);
18 18
19 // TODO: Redesign this interface to be useful. 19 // TODO: Redesign this interface to be useful.
20 20
21 /// <summary> 21 /// <summary>
22 /// Called after all output changes occur and right before the output is bound into its final format. 22 /// Called after all output changes occur and right before the output is bound into its final format.
23 /// </summary> 23 /// </summary>
24 void PostBackendDecompile(IDecompileResult result); 24 void PostDecompile(IWindowsInstallerDecompileResult result);
25 } 25 }
26} 26}
diff --git a/src/api/wix/WixToolset.Extensibility/Services/ICommandLineParser.cs b/src/api/wix/WixToolset.Extensibility/Services/ICommandLineParser.cs
index cd17f100..efd6600d 100644
--- a/src/api/wix/WixToolset.Extensibility/Services/ICommandLineParser.cs
+++ b/src/api/wix/WixToolset.Extensibility/Services/ICommandLineParser.cs
@@ -5,36 +5,111 @@ namespace WixToolset.Extensibility.Services
5 using System.Collections.Generic; 5 using System.Collections.Generic;
6 using WixToolset.Data; 6 using WixToolset.Data;
7 7
8#pragma warning disable 1591 // TODO: add documentation 8 /// <summary>
9 /// Provides the command-line arguments.
10 /// </summary>
9 public interface ICommandLineParser 11 public interface ICommandLineParser
10 { 12 {
13 /// <summary>
14 /// Gets the argument that caused the error.
15 /// </summary>
11 string ErrorArgument { get; } 16 string ErrorArgument { get; }
12 17
13 /// <summary> 18 /// <summary>
14 /// Validates that a valid switch (starts with "/" or "-"), and returns a bool indicating its validity 19 /// Validates that a valid switch (starts with "/" or "-"), and returns a bool indicating its validity
15 /// </summary> 20 /// </summary>
16 /// <param name="arg">The string check.</param> 21 /// <param name="argument">The string check.</param>
17 /// <returns>True if a valid switch, otherwise false.</returns> 22 /// <returns>True if a valid switch, otherwise false.</returns>
18 bool IsSwitch(string arg); 23 bool IsSwitch(string argument);
19 24
25 /// <summary>
26 /// Gets the current argument as a file or displays an error.
27 /// </summary>
28 /// <param name="argument">Current argument used in the error message if necessary.</param>
29 /// <param name="fileType">Type of file displayed in the error message if necessary.</param>
30 /// <returns>The fully expanded path if the argument is a file path, otherwise null.</returns>
20 string GetArgumentAsFilePathOrError(string argument, string fileType); 31 string GetArgumentAsFilePathOrError(string argument, string fileType);
21 32
22 void GetArgumentAsFilePathOrError(string argument, string fileType, IList<string> paths); 33 /// <summary>
34 /// Adds the current argument as a file to the list or displays an error.
35 /// </summary>
36 /// <param name="argument">Current argument used in the error message if necessary.</param>
37 /// <param name="fileType">Type of file displayed in the error message if necessary.</param>
38 /// <param name="paths">List to add the fully expanded path if the argument is a file path.</param>
39 /// <returns>True if the argument is a file path, otherwise false.</returns>
40 bool GetArgumentAsFilePathOrError(string argument, string fileType, IList<string> paths);
23 41
24 string GetNextArgumentOrError(string commandLineSwitch); 42 /// <summary>
43 /// Gets the next argument or displays error if no argument is available.
44 /// </summary>
45 /// <param name="argument">Current argument used in the error message if necessary.</param>
46 /// <returns>The next argument if present or null</returns>
47 string GetNextArgumentOrError(string argument);
25 48
26 bool GetNextArgumentOrError(string commandLineSwitch, IList<string> argument); 49 /// <summary>
50 /// Adds the next argument to a list or displays error if no argument is available.
51 /// </summary>
52 /// <param name="argument">Current argument used in the error message if necessary.</param>
53 /// <param name="arguments">List to add the argument to.</param>
54 /// <returns>True if an argument is available, otherwise false.</returns>
55 bool GetNextArgumentOrError(string argument, IList<string> arguments);
27 56
28 string GetNextArgumentAsDirectoryOrError(string commandLineSwitch); 57 /// <summary>
58 /// Gets the next argument as a directory or displays an error.
59 /// </summary>
60 /// <param name="argument">Current argument used in the error message if necessary.</param>
61 /// <returns>The fully expanded path if the argument is a directory, otherwise null.</returns>
62 string GetNextArgumentAsDirectoryOrError(string argument);
29 63
30 bool GetNextArgumentAsDirectoryOrError(string commandLineSwitch, IList<string> directories); 64 /// <summary>
65 /// Adds the next argument as a directory to the list or displays an error.
66 /// </summary>
67 /// <param name="argument">Current argument used in the error message if necessary.</param>
68 /// <param name="directories">List to add the fully expanded directory if the argument is a file path.</param>
69 /// <returns>True if the argument is a directory, otherwise false.</returns>
70 bool GetNextArgumentAsDirectoryOrError(string argument, IList<string> directories);
31 71
32 string GetNextArgumentAsFilePathOrError(string commandLineSwitch); 72 /// <summary>
73 /// Gets the next argument as a file or displays an error.
74 /// </summary>
75 /// <param name="argument">Current argument used in the error message if necessary.</param>
76 /// <returns>The fully expanded path if the argument is a file path, otherwise null.</returns>
77 string GetNextArgumentAsFilePathOrError(string argument);
33 78
34 bool GetNextArgumentAsFilePathOrError(string commandLineSwitch, string fileType, IList<string> paths); 79 /// <summary>
80 /// Adds the next argument as a file to the list or displays an error.
81 /// </summary>
82 /// <param name="argument">Current argument used in the error message if necessary.</param>
83 /// <param name="fileType">Type of file displayed in the error message if necessary.</param>
84 /// <param name="paths">List to add the fully expanded path if the argument is a file path.</param>
85 /// <returns>True if the argument is a file path, otherwise false.</returns>
86 bool GetNextArgumentAsFilePathOrError(string argument, string fileType, IList<string> paths);
35 87
88 /// <summary>
89 /// Reports a command line error for the provided argument.
90 /// </summary>
91 /// <param name="argument">Argument that caused the error.</param>
92 /// <param name="message">Message to report.</param>
36 void ReportErrorArgument(string argument, Message message = null); 93 void ReportErrorArgument(string argument, Message message = null);
37 94
38 bool TryGetNextSwitchOrArgument(out string arg); 95 /// <summary>
96 /// Tries to get the next argument.
97 /// </summary>
98 /// <param name="argument">Next argument if available.</param>
99 /// <returns>True if argument is available, otherwise false.</returns>
100 bool TryGetNextSwitchOrArgument(out string argument);
101
102 /// <summary>
103 /// Looks ahead to the next argument without moving to the next argument.
104 /// </summary>
105 /// <returns>Next argument if available, otherwise null.</returns>
106 string PeekNextArgument();
107
108 /// <summary>
109 /// Tries to looks ahead to the next argument without moving to the next argument.
110 /// </summary>
111 /// <param name="argument">Argument found if present.</param>
112 /// <returns>True if argument is found, otherwise false.</returns>
113 bool TryPeekNextArgument(out string argument);
39 } 114 }
40} 115}
diff --git a/src/wix/WixToolset.Core.Burn/BundleBackend.cs b/src/wix/WixToolset.Core.Burn/BundleBackend.cs
index a2003989..cf1971f6 100644
--- a/src/wix/WixToolset.Core.Burn/BundleBackend.cs
+++ b/src/wix/WixToolset.Core.Burn/BundleBackend.cs
@@ -2,7 +2,6 @@
2 2
3namespace WixToolset.Core.Burn 3namespace WixToolset.Core.Burn
4{ 4{
5 using System;
6 using WixToolset.Extensibility; 5 using WixToolset.Extensibility;
7 using WixToolset.Extensibility.Data; 6 using WixToolset.Extensibility.Data;
8 using WixToolset.Extensibility.Services; 7 using WixToolset.Extensibility.Services;
@@ -35,10 +34,5 @@ namespace WixToolset.Core.Burn
35 34
36 return result; 35 return result;
37 } 36 }
38
39 public IDecompileResult Decompile(IDecompileContext context)
40 {
41 throw new NotImplementedException();
42 }
43 } 37 }
44} 38}
diff --git a/src/wix/WixToolset.Core.WindowsInstaller/CommandLine/DecompilerSubcommand.cs b/src/wix/WixToolset.Core.WindowsInstaller/CommandLine/DecompilerSubcommand.cs
new file mode 100644
index 00000000..19d1c738
--- /dev/null
+++ b/src/wix/WixToolset.Core.WindowsInstaller/CommandLine/DecompilerSubcommand.cs
@@ -0,0 +1,185 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3namespace WixToolset.Core.WindowsInstaller.CommandLine
4{
5 using System;
6 using System.IO;
7 using System.Threading;
8 using System.Threading.Tasks;
9 using System.Xml.Linq;
10 using WixToolset.Data;
11 using WixToolset.Extensibility;
12 using WixToolset.Extensibility.Data;
13 using WixToolset.Extensibility.Services;
14
15 internal class DecompilerSubcommand : WindowsInstallerSubcommandBase
16 {
17 public DecompilerSubcommand(IServiceProvider serviceProvider)
18 {
19 this.ServiceProvider = serviceProvider;
20 this.Messaging = serviceProvider.GetService<IMessaging>();
21 }
22
23 private IServiceProvider ServiceProvider { get; }
24
25 private IMessaging Messaging { get; }
26
27 private string InputPath { get; set; }
28
29 private string DecompileType { get; set; }
30
31 private string IntermediateFolder { get; set; }
32
33 private string OutputPath { get; set; }
34
35 private string ExportBasePath { get; set; }
36
37 private bool SuppressCustomTables { get; set; }
38
39 private bool SuppressDroppingEmptyTables { get; set; }
40
41 private bool SuppressRelativeActionSequencing { get; set; }
42
43 private bool SuppressUI { get; set; }
44
45 public override Task<int> ExecuteAsync(CancellationToken cancellationToken)
46 {
47 if (String.IsNullOrEmpty(this.InputPath))
48 {
49 Console.Error.WriteLine("Input MSI or MSM database is required");
50 return Task.FromResult(-1);
51 }
52
53 if (!this.TryCalculateDecompileType(out var decompileType))
54 {
55 Console.Error.WriteLine("Unknown output type '{0}' from input: {1}", decompileType, this.InputPath);
56 return Task.FromResult(-1);
57 }
58
59 if (String.IsNullOrEmpty(this.IntermediateFolder))
60 {
61 this.IntermediateFolder = Path.GetTempPath();
62 }
63
64 if (String.IsNullOrEmpty(this.OutputPath))
65 {
66 this.OutputPath = Path.ChangeExtension(this.InputPath, ".wxs");
67 }
68
69 var context = this.ServiceProvider.GetService<IWindowsInstallerDecompileContext>();
70 context.Extensions = this.ServiceProvider.GetService<IExtensionManager>().GetServices<IWindowsInstallerDecompilerExtension>();
71 context.DecompilePath = this.InputPath;
72 context.DecompileType = decompileType;
73 context.IntermediateFolder = this.IntermediateFolder;
74 context.OutputPath = this.OutputPath;
75
76 context.ExtractFolder = this.ExportBasePath ?? this.IntermediateFolder;
77 context.SuppressCustomTables = this.SuppressCustomTables;
78 context.SuppressDroppingEmptyTables = this.SuppressDroppingEmptyTables;
79 context.SuppressRelativeActionSequencing = this.SuppressRelativeActionSequencing;
80 context.SuppressUI = this.SuppressUI;
81
82 try
83 {
84 var decompiler = this.ServiceProvider.GetService<IWindowsInstallerDecompiler>();
85 var result = decompiler.Decompile(context);
86
87 if (!this.Messaging.EncounteredError)
88 {
89 Directory.CreateDirectory(Path.GetDirectoryName(Path.GetFullPath(context.OutputPath)));
90 result.Document.Save(context.OutputPath, SaveOptions.OmitDuplicateNamespaces);
91 }
92 }
93 catch (WixException e)
94 {
95 this.Messaging.Write(e.Error);
96 }
97
98 return Task.FromResult(this.Messaging.LastErrorNumber);
99 }
100
101 public override bool TryParseArgument(ICommandLineParser parser, string argument)
102 {
103 if (parser.IsSwitch(argument))
104 {
105 var parameter = argument.Substring(1);
106 switch (parameter.ToLowerInvariant())
107 {
108 case "intermediatefolder":
109 this.IntermediateFolder = parser.GetNextArgumentAsDirectoryOrError(argument);
110 return true;
111
112 case "o":
113 case "out":
114 this.OutputPath = parser.GetNextArgumentAsFilePathOrError(argument);
115 return true;
116
117 case "sct":
118 this.SuppressCustomTables = true;
119 return true;
120
121 case "sdet":
122 this.SuppressDroppingEmptyTables = true;
123 return true;
124
125 case "sras":
126 this.SuppressRelativeActionSequencing = true;
127 return true;
128
129 case "sui":
130 this.SuppressUI = true;
131 return true;
132
133 case "type":
134 this.DecompileType = parser.GetNextArgumentOrError(argument);
135 return true;
136
137 case "x":
138 // Peek ahead to get the actual value provided on the command-line so the authoring
139 // matches what they typed on the command-line.
140 var originalExportBasePath = parser.PeekNextArgument();
141 parser.GetNextArgumentAsDirectoryOrError(argument); // ensure we actually got a directory.
142
143 this.ExportBasePath = originalExportBasePath;
144 return true;
145 }
146 }
147 else if (String.IsNullOrEmpty(this.InputPath))
148 {
149 this.InputPath = argument;
150 return true;
151 }
152
153 return false;
154 }
155
156 private bool TryCalculateDecompileType(out OutputType decompileType)
157 {
158 decompileType = OutputType.Unknown;
159
160 if (String.IsNullOrEmpty(this.DecompileType))
161 {
162 this.DecompileType = Path.GetExtension(this.InputPath);
163 }
164
165 switch (this.DecompileType.ToLowerInvariant())
166 {
167 case "product":
168 case "package":
169 case "msi":
170 case ".msi":
171 decompileType = OutputType.Product;
172 break;
173
174 case "mergemodule":
175 case "module":
176 case "msm":
177 case ".msm":
178 decompileType = OutputType.Module;
179 break;
180 }
181
182 return decompileType != OutputType.Unknown;
183 }
184 }
185}
diff --git a/src/wix/WixToolset.Core.WindowsInstaller/CommandLine/WindowsInstallerCommand.cs b/src/wix/WixToolset.Core.WindowsInstaller/CommandLine/WindowsInstallerCommand.cs
index ed0c0658..f11020c8 100644
--- a/src/wix/WixToolset.Core.WindowsInstaller/CommandLine/WindowsInstallerCommand.cs
+++ b/src/wix/WixToolset.Core.WindowsInstaller/CommandLine/WindowsInstallerCommand.cs
@@ -45,6 +45,10 @@ namespace WixToolset.Core.WindowsInstaller.CommandLine
45 { 45 {
46 switch (argument.ToLowerInvariant()) 46 switch (argument.ToLowerInvariant())
47 { 47 {
48 case "decompile":
49 this.Subcommand = new DecompilerSubcommand(this.ServiceProvider);
50 return true;
51
48 case "inscribe": 52 case "inscribe":
49 this.Subcommand = new InscribeSubcommand(this.ServiceProvider); 53 this.Subcommand = new InscribeSubcommand(this.ServiceProvider);
50 return true; 54 return true;
diff --git a/src/wix/WixToolset.Core.WindowsInstaller/Decompile/DecompileMsiOrMsmCommand.cs b/src/wix/WixToolset.Core.WindowsInstaller/Decompile/DecompileMsiOrMsmCommand.cs
index aeda4443..5001828d 100644
--- a/src/wix/WixToolset.Core.WindowsInstaller/Decompile/DecompileMsiOrMsmCommand.cs
+++ b/src/wix/WixToolset.Core.WindowsInstaller/Decompile/DecompileMsiOrMsmCommand.cs
@@ -1,4 +1,4 @@
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
3namespace WixToolset.Core.WindowsInstaller.Decompile 3namespace WixToolset.Core.WindowsInstaller.Decompile
4{ 4{
@@ -11,28 +11,24 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
11 using WixToolset.Core.WindowsInstaller.Unbind; 11 using WixToolset.Core.WindowsInstaller.Unbind;
12 using WixToolset.Data; 12 using WixToolset.Data;
13 using WixToolset.Data.WindowsInstaller; 13 using WixToolset.Data.WindowsInstaller;
14 using WixToolset.Extensibility;
15 using WixToolset.Extensibility.Data; 14 using WixToolset.Extensibility.Data;
16 using WixToolset.Extensibility.Services; 15 using WixToolset.Extensibility.Services;
17 16
18 internal class DecompileMsiOrMsmCommand 17 internal class DecompileMsiOrMsmCommand
19 { 18 {
20 public DecompileMsiOrMsmCommand(IDecompileContext context, IEnumerable<IWindowsInstallerBackendDecompilerExtension> backendExtensions) 19 public DecompileMsiOrMsmCommand(IWindowsInstallerDecompileContext context)
21 { 20 {
22 this.Context = context; 21 this.Context = context;
23 this.Extensions = backendExtensions;
24 this.Messaging = context.ServiceProvider.GetService<IMessaging>(); 22 this.Messaging = context.ServiceProvider.GetService<IMessaging>();
25 } 23 }
26 24
27 private IDecompileContext Context { get; } 25 private IWindowsInstallerDecompileContext Context { get; }
28
29 private IEnumerable<IWindowsInstallerBackendDecompilerExtension> Extensions { get; }
30 26
31 private IMessaging Messaging { get; } 27 private IMessaging Messaging { get; }
32 28
33 public IDecompileResult Execute() 29 public IWindowsInstallerDecompileResult Execute()
34 { 30 {
35 var result = this.Context.ServiceProvider.GetService<IDecompileResult>(); 31 var result = this.Context.ServiceProvider.GetService<IWindowsInstallerDecompileResult>();
36 32
37 try 33 try
38 { 34 {
@@ -50,7 +46,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
50 var output = unbindCommand.Execute(); 46 var output = unbindCommand.Execute();
51 var extractedFilePaths = new List<string>(unbindCommand.ExportedFiles); 47 var extractedFilePaths = new List<string>(unbindCommand.ExportedFiles);
52 48
53 var decompiler = new Decompiler(this.Messaging, backendHelper, this.Extensions, this.Context.BaseSourcePath, this.Context.SuppressCustomTables, this.Context.SuppressDroppingEmptyTables, this.Context.SuppressUI, this.Context.TreatProductAsModule); 49 var decompiler = new Decompiler(this.Messaging, backendHelper, this.Context.Extensions, this.Context.BaseSourcePath, this.Context.SuppressCustomTables, this.Context.SuppressDroppingEmptyTables, this.Context.SuppressRelativeActionSequencing, this.Context.SuppressUI, this.Context.TreatProductAsModule);
54 result.Document = decompiler.Decompile(output); 50 result.Document = decompiler.Decompile(output);
55 51
56 result.Platform = GetPlatformFromOutput(output); 52 result.Platform = GetPlatformFromOutput(output);
@@ -90,7 +86,6 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
90 var template = output.Tables["_SummaryInformation"]?.Rows.SingleOrDefault(row => row.FieldAsInteger(0) == 7)?.FieldAsString(1); 86 var template = output.Tables["_SummaryInformation"]?.Rows.SingleOrDefault(row => row.FieldAsInteger(0) == 7)?.FieldAsString(1);
91 87
92 return Decompiler.GetPlatformFromTemplateSummaryInformation(template?.Split(';')); 88 return Decompiler.GetPlatformFromTemplateSummaryInformation(template?.Split(';'));
93
94 } 89 }
95 } 90 }
96} 91}
diff --git a/src/wix/WixToolset.Core.WindowsInstaller/Decompile/Decompiler.cs b/src/wix/WixToolset.Core.WindowsInstaller/Decompile/Decompiler.cs
index 9c338293..4ccfaaa5 100644
--- a/src/wix/WixToolset.Core.WindowsInstaller/Decompile/Decompiler.cs
+++ b/src/wix/WixToolset.Core.WindowsInstaller/Decompile/Decompiler.cs
@@ -43,7 +43,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
43 /// <summary> 43 /// <summary>
44 /// Creates a new decompiler object with a default set of table definitions. 44 /// Creates a new decompiler object with a default set of table definitions.
45 /// </summary> 45 /// </summary>
46 public Decompiler(IMessaging messaging, IBackendHelper backendHelper, IEnumerable<IWindowsInstallerBackendDecompilerExtension> extensions, string baseSourcePath, bool suppressCustomTables, bool suppressDroppingEmptyTables, bool suppressUI, bool treatProductAsModule) 46 public Decompiler(IMessaging messaging, IBackendHelper backendHelper, IEnumerable<IWindowsInstallerDecompilerExtension> extensions, string baseSourcePath, bool suppressCustomTables, bool suppressDroppingEmptyTables, bool suppressRelativeActionSequencing, bool suppressUI, bool treatProductAsModule)
47 { 47 {
48 this.Messaging = messaging; 48 this.Messaging = messaging;
49 this.BackendHelper = backendHelper; 49 this.BackendHelper = backendHelper;
@@ -51,10 +51,11 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
51 this.BaseSourcePath = baseSourcePath ?? "SourceDir"; 51 this.BaseSourcePath = baseSourcePath ?? "SourceDir";
52 this.SuppressCustomTables = suppressCustomTables; 52 this.SuppressCustomTables = suppressCustomTables;
53 this.SuppressDroppingEmptyTables = suppressDroppingEmptyTables; 53 this.SuppressDroppingEmptyTables = suppressDroppingEmptyTables;
54 this.SuppressRelativeActionSequencing = suppressRelativeActionSequencing;
54 this.SuppressUI = suppressUI; 55 this.SuppressUI = suppressUI;
55 this.TreatProductAsModule = treatProductAsModule; 56 this.TreatProductAsModule = treatProductAsModule;
56 57
57 this.ExtensionsByTableName = new Dictionary<string, IWindowsInstallerBackendDecompilerExtension>(); 58 this.ExtensionsByTableName = new Dictionary<string, IWindowsInstallerDecompilerExtension>();
58 this.StandardActions = WindowsInstallerStandard.StandardActions().ToDictionary(a => a.Id.Id); 59 this.StandardActions = WindowsInstallerStandard.StandardActions().ToDictionary(a => a.Id.Id);
59 60
60 this.TableDefinitions = new TableDefinitionCollection(); 61 this.TableDefinitions = new TableDefinitionCollection();
@@ -64,9 +65,9 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
64 65
65 private IBackendHelper BackendHelper { get; } 66 private IBackendHelper BackendHelper { get; }
66 67
67 private IEnumerable<IWindowsInstallerBackendDecompilerExtension> Extensions { get; } 68 private IEnumerable<IWindowsInstallerDecompilerExtension> Extensions { get; }
68 69
69 private Dictionary<string, IWindowsInstallerBackendDecompilerExtension> ExtensionsByTableName { get; } 70 private Dictionary<string, IWindowsInstallerDecompilerExtension> ExtensionsByTableName { get; }
70 71
71 private string BaseSourcePath { get; } 72 private string BaseSourcePath { get; }
72 73
@@ -238,7 +239,10 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
238 /// </summary> 239 /// </summary>
239 /// <param name="row">The row corresponding to the element.</param> 240 /// <param name="row">The row corresponding to the element.</param>
240 /// <returns>The indexed element.</returns> 241 /// <returns>The indexed element.</returns>
241 private XElement GetIndexedElement(WixToolset.Data.WindowsInstaller.Row row) => this.GetIndexedElement(row.TableDefinition.Name, row.GetPrimaryKey(DecompilerConstants.PrimaryKeyDelimiter)); 242 private XElement GetIndexedElement(Row row)
243 {
244 return this.GetIndexedElement(row.TableDefinition.Name, row.GetPrimaryKey(DecompilerConstants.PrimaryKeyDelimiter));
245 }
242 246
243 /// <summary> 247 /// <summary>
244 /// Gets the element corresponding to the primary key of the given table. 248 /// Gets the element corresponding to the primary key of the given table.
@@ -246,7 +250,10 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
246 /// <param name="table">The table corresponding to the element.</param> 250 /// <param name="table">The table corresponding to the element.</param>
247 /// <param name="primaryKey">The primary key corresponding to the element.</param> 251 /// <param name="primaryKey">The primary key corresponding to the element.</param>
248 /// <returns>The indexed element.</returns> 252 /// <returns>The indexed element.</returns>
249 private XElement GetIndexedElement(string table, params string[] primaryKey) => this.IndexedElements[String.Concat(table, ':', String.Join(DecompilerConstants.PrimaryKeyDelimiterString, primaryKey))]; 253 private XElement GetIndexedElement(string table, params string[] primaryKey)
254 {
255 return this.TryGetIndexedElement(table, out var element, primaryKey) ? element : null;
256 }
250 257
251 /// <summary> 258 /// <summary>
252 /// Tries to get the element corresponding to the primary key of the given table. 259 /// Tries to get the element corresponding to the primary key of the given table.
@@ -254,7 +261,10 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
254 /// <param name="row">The table corresponding to the element.</param> 261 /// <param name="row">The table corresponding to the element.</param>
255 /// <param name="xElement">The indexed element.</param> 262 /// <param name="xElement">The indexed element.</param>
256 /// <returns>Whether the element was found.</returns> 263 /// <returns>Whether the element was found.</returns>
257 private bool TryGetIndexedElement(WixToolset.Data.WindowsInstaller.Row row, out XElement xElement) => this.TryGetIndexedElement(row.TableDefinition.Name, out xElement, row.GetPrimaryKey(DecompilerConstants.PrimaryKeyDelimiter)); 264 private bool TryGetIndexedElement(Row row, out XElement xElement)
265 {
266 return this.TryGetIndexedElement(row.TableDefinition.Name, out xElement, row.GetPrimaryKey(DecompilerConstants.PrimaryKeyDelimiter));
267 }
258 268
259 /// <summary> 269 /// <summary>
260 /// Tries to get the element corresponding to the primary key of the given table. 270 /// Tries to get the element corresponding to the primary key of the given table.
@@ -263,14 +273,17 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
263 /// <param name="xElement">The indexed element.</param> 273 /// <param name="xElement">The indexed element.</param>
264 /// <param name="primaryKey">The primary key corresponding to the element.</param> 274 /// <param name="primaryKey">The primary key corresponding to the element.</param>
265 /// <returns>Whether the element was found.</returns> 275 /// <returns>Whether the element was found.</returns>
266 private bool TryGetIndexedElement(string table, out XElement xElement, params string[] primaryKey) => this.IndexedElements.TryGetValue(String.Concat(table, ':', String.Join(DecompilerConstants.PrimaryKeyDelimiterString, primaryKey)), out xElement); 276 private bool TryGetIndexedElement(string table, out XElement xElement, params string[] primaryKey)
277 {
278 return this.IndexedElements.TryGetValue(String.Concat(table, ':', String.Join(DecompilerConstants.PrimaryKeyDelimiterString, primaryKey)), out xElement);
279 }
267 280
268 /// <summary> 281 /// <summary>
269 /// Index an element by its corresponding row. 282 /// Index an element by its corresponding row.
270 /// </summary> 283 /// </summary>
271 /// <param name="row">The row corresponding to the element.</param> 284 /// <param name="row">The row corresponding to the element.</param>
272 /// <param name="element">The element to index.</param> 285 /// <param name="element">The element to index.</param>
273 private void IndexElement(WixToolset.Data.WindowsInstaller.Row row, XElement element) 286 private void IndexElement(Row row, XElement element)
274 { 287 {
275 this.IndexedElements.Add(String.Concat(row.TableDefinition.Name, ':', row.GetPrimaryKey(DecompilerConstants.PrimaryKeyDelimiter)), element); 288 this.IndexedElements.Add(String.Concat(row.TableDefinition.Name, ':', row.GetPrimaryKey(DecompilerConstants.PrimaryKeyDelimiter)), element);
276 } 289 }
@@ -293,9 +306,15 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
293 .ToDictionary(lookup => lookup.Key, lookup => lookup.ToList()); 306 .ToDictionary(lookup => lookup.Key, lookup => lookup.ToList());
294 } 307 }
295 308
296 private Dictionary<string, List<XElement>> IndexTableOneToMany(TableIndexedCollection tables, string tableName, int column = 0) => this.IndexTableOneToMany(tables[tableName]?.Rows ?? Enumerable.Empty<Row>(), column); 309 private Dictionary<string, List<XElement>> IndexTableOneToMany(TableIndexedCollection tables, string tableName, int column = 0)
310 {
311 return this.IndexTableOneToMany(tables[tableName]?.Rows ?? Enumerable.Empty<Row>(), column);
312 }
297 313
298 private Dictionary<string, List<XElement>> IndexTableOneToMany(Table table, int column = 0) => this.IndexTableOneToMany(table?.Rows ?? Enumerable.Empty<Row>(), column); 314 private Dictionary<string, List<XElement>> IndexTableOneToMany(Table table, int column = 0)
315 {
316 return this.IndexTableOneToMany(table?.Rows ?? Enumerable.Empty<Row>(), column);
317 }
299 318
300 private void AddChildToParent(string parentName, XElement xChild, Row row, int column) 319 private void AddChildToParent(string parentName, XElement xChild, Row row, int column)
301 { 320 {
@@ -310,7 +329,10 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
310 } 329 }
311 } 330 }
312 331
313 private static XAttribute XAttributeIfNotNull(string attributeName, Row row, int column) => row.IsColumnNull(column) ? null : new XAttribute(attributeName, row.FieldAsString(column)); 332 private static XAttribute XAttributeIfNotNull(string attributeName, Row row, int column)
333 {
334 return row.IsColumnNull(column) ? null : new XAttribute(attributeName, row.FieldAsString(column));
335 }
314 336
315 private static void SetAttributeIfNotNull(XElement xElement, string attributeName, string value) 337 private static void SetAttributeIfNotNull(XElement xElement, string attributeName, string value)
316 { 338 {
@@ -1578,7 +1600,10 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
1578 /// Nests the Permission elements below their parent elements. There are no declared foreign 1600 /// Nests the Permission elements below their parent elements. There are no declared foreign
1579 /// keys for the parents of the LockPermissions table. 1601 /// keys for the parents of the LockPermissions table.
1580 /// </remarks> 1602 /// </remarks>
1581 private void FinalizeLockPermissionsTable(TableIndexedCollection tables) => this.FinalizePermissionsTable(tables, "LockPermissions"); 1603 private void FinalizeLockPermissionsTable(TableIndexedCollection tables)
1604 {
1605 this.FinalizePermissionsTable(tables, "LockPermissions");
1606 }
1582 1607
1583 /// <summary> 1608 /// <summary>
1584 /// Finalize the MsiLockPermissionsEx table. 1609 /// Finalize the MsiLockPermissionsEx table.
@@ -1588,7 +1613,10 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
1588 /// Nests the PermissionEx elements below their parent elements. There are no declared foreign 1613 /// Nests the PermissionEx elements below their parent elements. There are no declared foreign
1589 /// keys for the parents of the MsiLockPermissionsEx table. 1614 /// keys for the parents of the MsiLockPermissionsEx table.
1590 /// </remarks> 1615 /// </remarks>
1591 private void FinalizeMsiLockPermissionsExTable(TableIndexedCollection tables) => this.FinalizePermissionsTable(tables, "MsiLockPermissionsEx"); 1616 private void FinalizeMsiLockPermissionsExTable(TableIndexedCollection tables)
1617 {
1618 this.FinalizePermissionsTable(tables, "MsiLockPermissionsEx");
1619 }
1592 1620
1593 private static Dictionary<string, List<string>> IndexTable(Table table, int keyColumn, int? dataColumn) 1621 private static Dictionary<string, List<string>> IndexTable(Table table, int keyColumn, int? dataColumn)
1594 { 1622 {
@@ -2462,53 +2490,44 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
2462 // index the rows from the extension libraries 2490 // index the rows from the extension libraries
2463 var indexedExtensionTables = new Dictionary<string, HashSet<string>>(); 2491 var indexedExtensionTables = new Dictionary<string, HashSet<string>>();
2464#if TODO_DECOMPILER_EXTENSIONS 2492#if TODO_DECOMPILER_EXTENSIONS
2465 foreach (IDecompilerExtension extension in this.extensions) 2493 foreach (var extension in this.Extensions)
2466 { 2494 {
2467 // Get the optional library from the extension with the rows to be removed. 2495 // Get the optional library from the extension with the rows to be removed.
2468 Library library = extension.GetLibraryToRemove(this.tableDefinitions); 2496 var library = extension.GetLibraryToRemove(this.tableDefinitions);
2469 if (null != library) 2497 if (library != null)
2470 { 2498 {
2471 foreach (var section in library.Sections) 2499 foreach (var row in library.Sections.SelectMany(s => s.Tables).SelectMany(t => t.Rows))
2472 { 2500 {
2473 foreach (Table table in section.Tables) 2501 string primaryKey;
2474 { 2502 string tableName;
2475 foreach (Row row in table.Rows)
2476 {
2477 string primaryKey;
2478 string tableName;
2479
2480 // the Actions table needs to be handled specially
2481 if ("WixAction" == table.Name)
2482 {
2483 primaryKey = row.FieldAsString(1);
2484 2503
2485 if (OutputType.Module == this.outputType) 2504 // the Actions table needs to be handled specially
2486 { 2505 if (table.Name == "WixAction")
2487 tableName = String.Concat("Module", row.FieldAsString(0)); 2506 {
2488 } 2507 primaryKey = row.FieldAsString(1);
2489 else 2508 tableName = row.FieldAsString(0);
2490 {
2491 tableName = row.FieldAsString(0);
2492 }
2493 }
2494 else
2495 {
2496 primaryKey = row.GetPrimaryKey(DecompilerConstants.PrimaryKeyDelimiter);
2497 tableName = table.Name;
2498 }
2499 2509
2500 if (null != primaryKey) 2510 if (this.outputType == OutputType.Module)
2501 { 2511 {
2502 HashSet<string> indexedExtensionRows; 2512 tableName = "Module" + tableName;
2503 if (!indexedExtensionTables.TryGetValue(tableName, out indexedExtensionRows)) 2513 }
2504 { 2514 }
2505 indexedExtensionRows = new HashSet<string>(); 2515 else
2506 indexedExtensionTables.Add(tableName, indexedExtensionRows); 2516 {
2507 } 2517 primaryKey = row.GetPrimaryKey(DecompilerConstants.PrimaryKeyDelimiter);
2518 tableName = table.Name;
2519 }
2508 2520
2509 indexedExtensionRows.Add(primaryKey); 2521 if (primaryKey != null)
2510 } 2522 {
2523 HashSet<string> indexedExtensionRows;
2524 if (!indexedExtensionTables.TryGetValue(tableName, out indexedExtensionRows))
2525 {
2526 indexedExtensionRows = new HashSet<string>();
2527 indexedExtensionTables.Add(tableName, indexedExtensionRows);
2511 } 2528 }
2529
2530 indexedExtensionRows.Add(primaryKey);
2512 } 2531 }
2513 } 2532 }
2514 } 2533 }
diff --git a/src/wix/WixToolset.Core.WindowsInstaller/IWindowsInstallerDecompiler.cs b/src/wix/WixToolset.Core.WindowsInstaller/IWindowsInstallerDecompiler.cs
new file mode 100644
index 00000000..5bcda36c
--- /dev/null
+++ b/src/wix/WixToolset.Core.WindowsInstaller/IWindowsInstallerDecompiler.cs
@@ -0,0 +1,19 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3namespace WixToolset.Core
4{
5 using WixToolset.Extensibility.Data;
6
7 /// <summary>
8 /// Supports converting Windows Installer databases to source code.
9 /// </summary>
10 public interface IWindowsInstallerDecompiler
11 {
12 /// <summary>
13 /// Converts Windows Installer database back to source code.
14 /// </summary>
15 /// <param name="context">Context for decompiling.</param>
16 /// <returns>Result of decompilation.</returns>
17 IWindowsInstallerDecompileResult Decompile(IWindowsInstallerDecompileContext context);
18 }
19}
diff --git a/src/wix/WixToolset.Core.WindowsInstaller/MsiBackend.cs b/src/wix/WixToolset.Core.WindowsInstaller/MsiBackend.cs
index d1c1d3a6..f73791aa 100644
--- a/src/wix/WixToolset.Core.WindowsInstaller/MsiBackend.cs
+++ b/src/wix/WixToolset.Core.WindowsInstaller/MsiBackend.cs
@@ -47,27 +47,5 @@ namespace WixToolset.Core.WindowsInstaller
47 } 47 }
48 } 48 }
49 } 49 }
50
51 public IDecompileResult Decompile(IDecompileContext context)
52 {
53 var extensionManager = context.ServiceProvider.GetService<IExtensionManager>();
54
55 var backendExtensions = extensionManager.GetServices<IWindowsInstallerBackendDecompilerExtension>();
56
57 foreach (var extension in backendExtensions)
58 {
59 extension.PreBackendDecompile(context);
60 }
61
62 var command = new DecompileMsiOrMsmCommand(context, backendExtensions);
63 var result = command.Execute();
64
65 foreach (var extension in backendExtensions)
66 {
67 extension.PostBackendDecompile(result);
68 }
69
70 return result;
71 }
72 } 50 }
73} 51}
diff --git a/src/wix/WixToolset.Core.WindowsInstaller/MsmBackend.cs b/src/wix/WixToolset.Core.WindowsInstaller/MsmBackend.cs
index ea008c39..fa24745a 100644
--- a/src/wix/WixToolset.Core.WindowsInstaller/MsmBackend.cs
+++ b/src/wix/WixToolset.Core.WindowsInstaller/MsmBackend.cs
@@ -2,11 +2,7 @@
2 2
3namespace WixToolset.Core.WindowsInstaller 3namespace WixToolset.Core.WindowsInstaller
4{ 4{
5 using System;
6 using WixToolset.Core.WindowsInstaller.Bind; 5 using WixToolset.Core.WindowsInstaller.Bind;
7 using WixToolset.Core.WindowsInstaller.Decompile;
8 using WixToolset.Core.WindowsInstaller.Unbind;
9 using WixToolset.Data;
10 using WixToolset.Extensibility; 6 using WixToolset.Extensibility;
11 using WixToolset.Extensibility.Data; 7 using WixToolset.Extensibility.Data;
12 using WixToolset.Extensibility.Services; 8 using WixToolset.Extensibility.Services;
@@ -43,27 +39,5 @@ namespace WixToolset.Core.WindowsInstaller
43 throw; 39 throw;
44 } 40 }
45 } 41 }
46
47 public IDecompileResult Decompile(IDecompileContext context)
48 {
49 var extensionManager = context.ServiceProvider.GetService<IExtensionManager>();
50
51 var backendExtensions = extensionManager.GetServices<IWindowsInstallerBackendDecompilerExtension>();
52
53 foreach (var extension in backendExtensions)
54 {
55 extension.PreBackendDecompile(context);
56 }
57
58 var command = new DecompileMsiOrMsmCommand(context, backendExtensions);
59 var result = command.Execute();
60
61 foreach (var extension in backendExtensions)
62 {
63 extension.PostBackendDecompile(result);
64 }
65
66 return result;
67 }
68 } 42 }
69} 43}
diff --git a/src/wix/WixToolset.Core.WindowsInstaller/MspBackend.cs b/src/wix/WixToolset.Core.WindowsInstaller/MspBackend.cs
index 398fc780..38a4ab34 100644
--- a/src/wix/WixToolset.Core.WindowsInstaller/MspBackend.cs
+++ b/src/wix/WixToolset.Core.WindowsInstaller/MspBackend.cs
@@ -63,11 +63,6 @@ namespace WixToolset.Core.WindowsInstaller
63 } 63 }
64 } 64 }
65 65
66 public IDecompileResult Decompile(IDecompileContext context)
67 {
68 throw new NotImplementedException();
69 }
70
71#if TODO_PATCHING 66#if TODO_PATCHING
72 public Intermediate Unbind(IUnbindContext context) 67 public Intermediate Unbind(IUnbindContext context)
73 { 68 {
diff --git a/src/wix/WixToolset.Core/DecompileContext.cs b/src/wix/WixToolset.Core.WindowsInstaller/WindowsInstallerDecompileContext.cs
index a7ec03fd..1428a469 100644
--- a/src/wix/WixToolset.Core/DecompileContext.cs
+++ b/src/wix/WixToolset.Core.WindowsInstaller/WindowsInstallerDecompileContext.cs
@@ -1,17 +1,16 @@
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
3namespace WixToolset.Core 3namespace WixToolset.Core.WindowsInstaller
4{ 4{
5 using System; 5 using System;
6 using System.Collections.Generic; 6 using System.Collections.Generic;
7 using WixToolset.Data; 7 using WixToolset.Data;
8 using WixToolset.Extensibility; 8 using WixToolset.Extensibility;
9 using WixToolset.Extensibility.Data; 9 using WixToolset.Extensibility.Data;
10 using WixToolset.Extensibility.Services;
11 10
12 internal class DecompileContext : IDecompileContext 11 internal class WindowsInstallerDecompileContext : IWindowsInstallerDecompileContext
13 { 12 {
14 internal DecompileContext(IServiceProvider serviceProvider) 13 internal WindowsInstallerDecompileContext(IServiceProvider serviceProvider)
15 { 14 {
16 this.ServiceProvider = serviceProvider; 15 this.ServiceProvider = serviceProvider;
17 } 16 }
@@ -22,7 +21,7 @@ namespace WixToolset.Core
22 21
23 public OutputType DecompileType { get; set; } 22 public OutputType DecompileType { get; set; }
24 23
25 public IReadOnlyCollection<IDecompilerExtension> Extensions { get; set; } 24 public IReadOnlyCollection<IWindowsInstallerDecompilerExtension> Extensions { get; set; }
26 25
27 public string ExtractFolder { get; set; } 26 public string ExtractFolder { get; set; }
28 27
@@ -40,6 +39,8 @@ namespace WixToolset.Core
40 39
41 public bool SuppressDroppingEmptyTables { get; set; } 40 public bool SuppressDroppingEmptyTables { get; set; }
42 41
42 public bool SuppressRelativeActionSequencing { get; set; }
43
43 public bool SuppressExtractCabinets { get; set; } 44 public bool SuppressExtractCabinets { get; set; }
44 45
45 public bool SuppressUI { get; set; } 46 public bool SuppressUI { get; set; }
diff --git a/src/wix/WixToolset.Core/DecompileResult.cs b/src/wix/WixToolset.Core.WindowsInstaller/WindowsInstallerDecompileResult.cs
index fc24cab7..69363286 100644
--- a/src/wix/WixToolset.Core/DecompileResult.cs
+++ b/src/wix/WixToolset.Core.WindowsInstaller/WindowsInstallerDecompileResult.cs
@@ -1,13 +1,13 @@
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
3namespace WixToolset.Core 3namespace WixToolset.Core.WindowsInstaller
4{ 4{
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.Extensibility.Data; 8 using WixToolset.Extensibility.Data;
9 9
10 internal class DecompileResult : IDecompileResult 10 internal class WindowsInstallerDecompileResult : IWindowsInstallerDecompileResult
11 { 11 {
12 public XDocument Document { get; set; } 12 public XDocument Document { get; set; }
13 13
diff --git a/src/wix/WixToolset.Core/Decompiler.cs b/src/wix/WixToolset.Core.WindowsInstaller/WindowsInstallerDecompiler.cs
index 859f582b..10420010 100644
--- a/src/wix/WixToolset.Core/Decompiler.cs
+++ b/src/wix/WixToolset.Core.WindowsInstaller/WindowsInstallerDecompiler.cs
@@ -1,8 +1,9 @@
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
3namespace WixToolset.Core 3namespace WixToolset.Core.WindowsInstaller
4{ 4{
5 using System; 5 using System;
6 using WixToolset.Core.WindowsInstaller.Decompile;
6 using WixToolset.Extensibility; 7 using WixToolset.Extensibility;
7 using WixToolset.Extensibility.Data; 8 using WixToolset.Extensibility.Data;
8 using WixToolset.Extensibility.Services; 9 using WixToolset.Extensibility.Services;
@@ -10,16 +11,16 @@ namespace WixToolset.Core
10 /// <summary> 11 /// <summary>
11 /// Decompiler of the WiX toolset. 12 /// Decompiler of the WiX toolset.
12 /// </summary> 13 /// </summary>
13 internal class Decompiler : IDecompiler 14 internal class WindowsInstallerDecompiler : IWindowsInstallerDecompiler
14 { 15 {
15 internal Decompiler(IServiceProvider serviceProvider) 16 internal WindowsInstallerDecompiler(IServiceProvider serviceProvider)
16 { 17 {
17 this.ServiceProvider = serviceProvider; 18 this.ServiceProvider = serviceProvider;
18 } 19 }
19 20
20 public IServiceProvider ServiceProvider { get; } 21 public IServiceProvider ServiceProvider { get; }
21 22
22 public IDecompileResult Decompile(IDecompileContext context) 23 public IWindowsInstallerDecompileResult Decompile(IWindowsInstallerDecompileContext context)
23 { 24 {
24 // Pre-decompile. 25 // Pre-decompile.
25 // 26 //
@@ -30,7 +31,8 @@ namespace WixToolset.Core
30 31
31 // Decompile. 32 // Decompile.
32 // 33 //
33 var result = this.BackendDecompile(context); 34 var command = new DecompileMsiOrMsmCommand(context);
35 var result = command.Execute();
34 36
35 if (result != null) 37 if (result != null)
36 { 38 {
@@ -44,25 +46,5 @@ namespace WixToolset.Core
44 46
45 return result; 47 return result;
46 } 48 }
47
48 private IDecompileResult BackendDecompile(IDecompileContext context)
49 {
50 var extensionManager = context.ServiceProvider.GetService<IExtensionManager>();
51
52 var backendFactories = extensionManager.GetServices<IBackendFactory>();
53
54 foreach (var factory in backendFactories)
55 {
56 if (factory.TryCreateBackend(context.DecompileType.ToString(), context.OutputPath, out var backend))
57 {
58 var result = backend.Decompile(context);
59 return result;
60 }
61 }
62
63 // TODO: messaging that a backend could not be found to decompile the decompile type?
64
65 return null;
66 }
67 } 49 }
68} 50}
diff --git a/src/wix/WixToolset.Core.WindowsInstaller/WixToolsetCoreServiceProviderExtensions.cs b/src/wix/WixToolset.Core.WindowsInstaller/WixToolsetCoreServiceProviderExtensions.cs
index e686fa49..d064aed1 100644
--- a/src/wix/WixToolset.Core.WindowsInstaller/WixToolsetCoreServiceProviderExtensions.cs
+++ b/src/wix/WixToolset.Core.WindowsInstaller/WixToolsetCoreServiceProviderExtensions.cs
@@ -5,6 +5,7 @@ namespace WixToolset.Core.WindowsInstaller
5 using System; 5 using System;
6 using System.Collections.Generic; 6 using System.Collections.Generic;
7 using WixToolset.Core.WindowsInstaller.ExtensibilityServices; 7 using WixToolset.Core.WindowsInstaller.ExtensibilityServices;
8 using WixToolset.Extensibility.Data;
8 using WixToolset.Extensibility.Services; 9 using WixToolset.Extensibility.Services;
9 10
10 /// <summary> 11 /// <summary>
@@ -31,6 +32,11 @@ namespace WixToolset.Core.WindowsInstaller
31 { 32 {
32 // Singletons. 33 // Singletons.
33 coreProvider.AddService((provider, singletons) => AddSingleton<IWindowsInstallerBackendHelper>(singletons, new WindowsInstallerBackendHelper(provider))); 34 coreProvider.AddService((provider, singletons) => AddSingleton<IWindowsInstallerBackendHelper>(singletons, new WindowsInstallerBackendHelper(provider)));
35
36 // Transients.
37 coreProvider.AddService<IWindowsInstallerDecompiler>((provider, singletons) => new WindowsInstallerDecompiler(provider));
38 coreProvider.AddService<IWindowsInstallerDecompileContext>((provider, singletons) => new WindowsInstallerDecompileContext(provider));
39 coreProvider.AddService<IWindowsInstallerDecompileResult>((provider, singletons) => new WindowsInstallerDecompileResult());
34 } 40 }
35 41
36 private static T AddSingleton<T>(Dictionary<Type, object> singletons, T service) where T : class 42 private static T AddSingleton<T>(Dictionary<Type, object> singletons, T service) where T : class
diff --git a/src/wix/WixToolset.Core/CommandLine/CommandLine.cs b/src/wix/WixToolset.Core/CommandLine/CommandLine.cs
index cd3b2fe4..0921f9ff 100644
--- a/src/wix/WixToolset.Core/CommandLine/CommandLine.cs
+++ b/src/wix/WixToolset.Core/CommandLine/CommandLine.cs
@@ -9,14 +9,6 @@ namespace WixToolset.Core.CommandLine
9 using WixToolset.Extensibility.Data; 9 using WixToolset.Extensibility.Data;
10 using WixToolset.Extensibility.Services; 10 using WixToolset.Extensibility.Services;
11 11
12 internal enum CommandTypes
13 {
14 Unknown,
15 Build,
16 Preprocess,
17 Decompile,
18 }
19
20 internal class CommandLine : ICommandLine 12 internal class CommandLine : ICommandLine
21 { 13 {
22 public CommandLine(IServiceProvider serviceProvider) 14 public CommandLine(IServiceProvider serviceProvider)
@@ -154,18 +146,9 @@ namespace WixToolset.Core.CommandLine
154 } 146 }
155 else 147 else
156 { 148 {
157 if (Enum.TryParse(arg, true, out CommandTypes commandType)) 149 if ("build".Equals(arg, StringComparison.OrdinalIgnoreCase))
158 { 150 {
159 switch (commandType) 151 command = new BuildCommand(this.ServiceProvider);
160 {
161 case CommandTypes.Build:
162 command = new BuildCommand(this.ServiceProvider);
163 break;
164
165 case CommandTypes.Decompile:
166 command = new DecompileCommand(this.ServiceProvider);
167 break;
168 }
169 } 152 }
170 else 153 else
171 { 154 {
@@ -175,8 +158,6 @@ namespace WixToolset.Core.CommandLine
175 { 158 {
176 break; 159 break;
177 } 160 }
178
179 command = null;
180 } 161 }
181 } 162 }
182 } 163 }
diff --git a/src/wix/WixToolset.Core/CommandLine/CommandLineParser.cs b/src/wix/WixToolset.Core/CommandLine/CommandLineParser.cs
index 015d3e62..7e6e7cac 100644
--- a/src/wix/WixToolset.Core/CommandLine/CommandLineParser.cs
+++ b/src/wix/WixToolset.Core/CommandLine/CommandLineParser.cs
@@ -41,12 +41,16 @@ namespace WixToolset.Core.CommandLine
41 return argument; 41 return argument;
42 } 42 }
43 43
44 public void GetArgumentAsFilePathOrError(string argument, string fileType, IList<string> paths) 44 public bool GetArgumentAsFilePathOrError(string argument, string fileType, IList<string> paths)
45 { 45 {
46 foreach (var path in this.GetFiles(argument, fileType)) 46 var files = this.GetFiles(argument, fileType);
47
48 foreach (var path in files)
47 { 49 {
48 paths.Add(path); 50 paths.Add(path);
49 } 51 }
52
53 return files.Length > 0;
50 } 54 }
51 55
52 public string GetNextArgumentOrError(string commandLineSwitch) 56 public string GetNextArgumentOrError(string commandLineSwitch)
@@ -140,6 +144,23 @@ namespace WixToolset.Core.CommandLine
140 return false; 144 return false;
141 } 145 }
142 146
147 public string PeekNextArgument()
148 {
149 return this.TryPeekNextArgument(out var argument) ? argument : null;
150 }
151
152 public bool TryPeekNextArgument(out string argument)
153 {
154 if (this.RemainingArguments.Count > 0)
155 {
156 argument = this.RemainingArguments.Peek();
157 return true;
158 }
159
160 argument = null;
161 return false;
162 }
163
143 private bool TryGetNextNonSwitchArgumentOrError(out string arg) 164 private bool TryGetNextNonSwitchArgumentOrError(out string arg)
144 { 165 {
145 var result = this.TryGetNextSwitchOrArgument(out arg); 166 var result = this.TryGetNextSwitchOrArgument(out arg);
diff --git a/src/wix/WixToolset.Core/CommandLine/DecompileCommand.cs b/src/wix/WixToolset.Core/CommandLine/DecompileCommand.cs
deleted file mode 100644
index 22853f86..00000000
--- a/src/wix/WixToolset.Core/CommandLine/DecompileCommand.cs
+++ /dev/null
@@ -1,202 +0,0 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3namespace WixToolset.Core.CommandLine
4{
5 using System;
6 using System.IO;
7 using System.Threading;
8 using System.Threading.Tasks;
9 using System.Xml.Linq;
10 using WixToolset.Data;
11 using WixToolset.Extensibility;
12 using WixToolset.Extensibility.Data;
13 using WixToolset.Extensibility.Services;
14
15 internal class DecompileCommand : ICommandLineCommand
16 {
17 private readonly CommandLine commandLine;
18
19 public DecompileCommand(IServiceProvider serviceProvider)
20 {
21 this.ServiceProvider = serviceProvider;
22 this.Messaging = serviceProvider.GetService<IMessaging>();
23 this.commandLine = new CommandLine(this.Messaging);
24 }
25
26 public bool ShowHelp
27 {
28 get { return this.commandLine.ShowHelp; }
29 set { this.commandLine.ShowHelp = value; }
30 }
31
32 public bool ShowLogo
33 {
34 get { return this.commandLine.ShowLogo; }
35 set { this.commandLine.ShowLogo = value; }
36 }
37
38 // Stop parsing when we've decided to show help.
39 public bool StopParsing => this.commandLine.ShowHelp;
40
41 private IServiceProvider ServiceProvider { get; }
42
43 public IMessaging Messaging { get; }
44
45 public Task<int> ExecuteAsync(CancellationToken _)
46 {
47 if (this.commandLine.ShowHelp || String.IsNullOrEmpty(this.commandLine.DecompileFilePath))
48 {
49 Console.WriteLine("TODO: Show decompile command help");
50 return Task.FromResult(-1);
51 }
52
53 var context = this.ServiceProvider.GetService<IDecompileContext>();
54 context.Extensions = this.ServiceProvider.GetService<IExtensionManager>().GetServices<IDecompilerExtension>();
55 context.DecompilePath = this.commandLine.DecompileFilePath;
56 context.DecompileType = this.commandLine.CalculateDecompileType();
57 context.IntermediateFolder = this.commandLine.CalculateIntermedateFolder();
58 context.OutputPath = this.commandLine.CalculateOutputPath();
59
60 try
61 {
62 var decompiler = this.ServiceProvider.GetService<IDecompiler>();
63 var result = decompiler.Decompile(context);
64
65 if (!this.Messaging.EncounteredError)
66 {
67 Directory.CreateDirectory(Path.GetDirectoryName(Path.GetFullPath(context.OutputPath)));
68 result.Document.Save(context.OutputPath, SaveOptions.OmitDuplicateNamespaces);
69 }
70 }
71 catch (WixException e)
72 {
73 this.Messaging.Write(e.Error);
74 }
75
76 if (this.Messaging.EncounteredError)
77 {
78 return Task.FromResult(1);
79 }
80
81 return Task.FromResult(0);
82 }
83
84 public bool TryParseArgument(ICommandLineParser parser, string argument)
85 {
86 return this.commandLine.TryParseArgument(argument, parser);
87 }
88
89 private class CommandLine
90 {
91 public CommandLine(IMessaging messaging)
92 {
93 this.Messaging = messaging;
94 }
95
96 private IMessaging Messaging { get; }
97
98 public string DecompileFilePath { get; private set; }
99
100 public string DecompileType { get; private set; }
101
102 public Platform Platform { get; private set; }
103
104 public bool ShowLogo { get; set; }
105
106 public bool ShowHelp { get; set; }
107
108 public string IntermediateFolder { get; private set; }
109
110 public string OutputFile { get; private set; }
111
112 public bool TryParseArgument(string arg, ICommandLineParser parser)
113 {
114 if (parser.IsSwitch(arg))
115 {
116 var parameter = arg.Substring(1);
117 switch (parameter.ToLowerInvariant())
118 {
119 case "intermediatefolder":
120 this.IntermediateFolder = parser.GetNextArgumentAsDirectoryOrError(arg);
121 return true;
122
123 case "o":
124 case "out":
125 this.OutputFile = parser.GetNextArgumentAsFilePathOrError(arg);
126 return true;
127 }
128 }
129 else
130 {
131 if (String.IsNullOrEmpty(this.DecompileFilePath))
132 {
133 this.DecompileFilePath = parser.GetArgumentAsFilePathOrError(arg, "decompile file");
134 return true;
135 }
136 else if (String.IsNullOrEmpty(this.OutputFile))
137 {
138 this.OutputFile = parser.GetArgumentAsFilePathOrError(arg, "output file");
139 return true;
140 }
141 }
142
143 return false;
144 }
145
146 public OutputType CalculateDecompileType()
147 {
148 if (String.IsNullOrEmpty(this.DecompileType))
149 {
150 this.DecompileType = Path.GetExtension(this.DecompileFilePath);
151 }
152
153 switch (this.DecompileType.ToLowerInvariant())
154 {
155 case "bundle":
156 case ".exe":
157 return OutputType.Bundle;
158
159 case "library":
160 case ".wixlib":
161 return OutputType.Library;
162
163 case "module":
164 case ".msm":
165 return OutputType.Module;
166
167 case "patch":
168 case ".msp":
169 return OutputType.Patch;
170
171 case ".pcp":
172 return OutputType.PatchCreation;
173
174 case "product":
175 case "package":
176 case ".msi":
177 return OutputType.Product;
178
179 case "transform":
180 case ".mst":
181 return OutputType.Transform;
182
183 case "intermediatepostlink":
184 case ".wixipl":
185 return OutputType.IntermediatePostLink;
186 }
187
188 return OutputType.Unknown;
189 }
190
191 public string CalculateIntermedateFolder()
192 {
193 return String.IsNullOrEmpty(this.IntermediateFolder) ? Path.GetTempPath() : this.IntermediateFolder;
194 }
195
196 public string CalculateOutputPath()
197 {
198 return String.IsNullOrEmpty(this.OutputFile) ? Path.ChangeExtension(this.DecompileFilePath, ".wxs") : this.OutputFile;
199 }
200 }
201 }
202}
diff --git a/src/wix/WixToolset.Core/IDecompiler.cs b/src/wix/WixToolset.Core/IDecompiler.cs
deleted file mode 100644
index 74ec26de..00000000
--- a/src/wix/WixToolset.Core/IDecompiler.cs
+++ /dev/null
@@ -1,12 +0,0 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3namespace WixToolset.Core
4{
5 using WixToolset.Extensibility.Data;
6
7#pragma warning disable 1591 // TODO: add documentation
8 public interface IDecompiler
9 {
10 IDecompileResult Decompile(IDecompileContext context);
11 }
12}
diff --git a/src/wix/WixToolset.Core/WixToolsetServiceProvider.cs b/src/wix/WixToolset.Core/WixToolsetServiceProvider.cs
index 9fbf6717..525b9dd9 100644
--- a/src/wix/WixToolset.Core/WixToolsetServiceProvider.cs
+++ b/src/wix/WixToolset.Core/WixToolsetServiceProvider.cs
@@ -40,14 +40,12 @@ namespace WixToolset.Core
40 this.AddService<ILinkContext>((provider, singletons) => new LinkContext(provider)); 40 this.AddService<ILinkContext>((provider, singletons) => new LinkContext(provider));
41 this.AddService<IResolveContext>((provider, singletons) => new ResolveContext(provider)); 41 this.AddService<IResolveContext>((provider, singletons) => new ResolveContext(provider));
42 this.AddService<IBindContext>((provider, singletons) => new BindContext(provider)); 42 this.AddService<IBindContext>((provider, singletons) => new BindContext(provider));
43 this.AddService<IDecompileContext>((provider, singletons) => new DecompileContext(provider));
44 this.AddService<ILayoutContext>((provider, singletons) => new LayoutContext(provider)); 43 this.AddService<ILayoutContext>((provider, singletons) => new LayoutContext(provider));
45 44
46 this.AddService<IBindFileWithPath>((provider, singletons) => new BindFileWithPath()); 45 this.AddService<IBindFileWithPath>((provider, singletons) => new BindFileWithPath());
47 this.AddService<IBindPath>((provider, singletons) => new BindPath()); 46 this.AddService<IBindPath>((provider, singletons) => new BindPath());
48 this.AddService<IBindResult>((provider, singletons) => new BindResult()); 47 this.AddService<IBindResult>((provider, singletons) => new BindResult());
49 this.AddService<IComponentKeyPath>((provider, singletons) => new ComponentKeyPath()); 48 this.AddService<IComponentKeyPath>((provider, singletons) => new ComponentKeyPath());
50 this.AddService<IDecompileResult>((provider, singletons) => new DecompileResult());
51 this.AddService<IIncludedFile>((provider, singletons) => new IncludedFile()); 49 this.AddService<IIncludedFile>((provider, singletons) => new IncludedFile());
52 this.AddService<IPreprocessResult>((provider, singletons) => new PreprocessResult()); 50 this.AddService<IPreprocessResult>((provider, singletons) => new PreprocessResult());
53 this.AddService<IResolvedDirectory>((provider, singletons) => new ResolvedDirectory()); 51 this.AddService<IResolvedDirectory>((provider, singletons) => new ResolvedDirectory());
@@ -58,7 +56,6 @@ namespace WixToolset.Core
58 56
59 this.AddService<IBinder>((provider, singletons) => new Binder(provider)); 57 this.AddService<IBinder>((provider, singletons) => new Binder(provider));
60 this.AddService<ICompiler>((provider, singletons) => new Compiler(provider)); 58 this.AddService<ICompiler>((provider, singletons) => new Compiler(provider));
61 this.AddService<IDecompiler>((provider, singletons) => new Decompiler(provider));
62 this.AddService<ILayoutCreator>((provider, singletons) => new LayoutCreator(provider)); 59 this.AddService<ILayoutCreator>((provider, singletons) => new LayoutCreator(provider));
63 this.AddService<IPreprocessor>((provider, singletons) => new Preprocessor(provider)); 60 this.AddService<IPreprocessor>((provider, singletons) => new Preprocessor(provider));
64 this.AddService<ILibrarian>((provider, singletons) => new Librarian(provider)); 61 this.AddService<ILibrarian>((provider, singletons) => new Librarian(provider));
diff --git a/src/wix/test/WixToolsetTest.Converters/ProductPackageFixture.cs b/src/wix/test/WixToolsetTest.Converters/ProductPackageFixture.cs
index e01b9789..5ad7ef6f 100644
--- a/src/wix/test/WixToolsetTest.Converters/ProductPackageFixture.cs
+++ b/src/wix/test/WixToolsetTest.Converters/ProductPackageFixture.cs
@@ -249,7 +249,7 @@ namespace WixToolsetTest.Converters
249 var v3msiPath = Path.Combine(folder, "TypicalV3.msi"); 249 var v3msiPath = Path.Combine(folder, "TypicalV3.msi");
250 var result = WixRunner.Execute(new[] 250 var result = WixRunner.Execute(new[]
251 { 251 {
252 "decompile", v3msiPath, 252 "msi", "decompile", v3msiPath,
253 "-intermediateFolder", intermediateFolder, 253 "-intermediateFolder", intermediateFolder,
254 "-o", decompiledWxsPath 254 "-o", decompiledWxsPath
255 }); 255 });
diff --git a/src/wix/test/WixToolsetTest.CoreIntegration/CustomTableFixture.cs b/src/wix/test/WixToolsetTest.CoreIntegration/CustomTableFixture.cs
index b9a6185d..1be7d9ab 100644
--- a/src/wix/test/WixToolsetTest.CoreIntegration/CustomTableFixture.cs
+++ b/src/wix/test/WixToolsetTest.CoreIntegration/CustomTableFixture.cs
@@ -219,7 +219,7 @@ namespace WixToolsetTest.CoreIntegration
219 219
220 result = WixRunner.Execute(new[] 220 result = WixRunner.Execute(new[]
221 { 221 {
222 "decompile", msiPath, 222 "msi", "decompile", msiPath,
223 "-sw1060", 223 "-sw1060",
224 "-intermediateFolder", intermediateFolder, 224 "-intermediateFolder", intermediateFolder,
225 "-o", decompiledWxsPath 225 "-o", decompiledWxsPath
diff --git a/src/wix/test/WixToolsetTest.CoreIntegration/DecompileFixture.cs b/src/wix/test/WixToolsetTest.CoreIntegration/DecompileFixture.cs
index e87bbfee..b43d2033 100644
--- a/src/wix/test/WixToolsetTest.CoreIntegration/DecompileFixture.cs
+++ b/src/wix/test/WixToolsetTest.CoreIntegration/DecompileFixture.cs
@@ -20,7 +20,7 @@ namespace WixToolsetTest.CoreIntegration
20 20
21 var result = WixRunner.Execute(new[] 21 var result = WixRunner.Execute(new[]
22 { 22 {
23 "decompile", 23 "msi", "decompile",
24 Path.Combine(folder, msiName), 24 Path.Combine(folder, msiName),
25 "-intermediateFolder", intermediateFolder, 25 "-intermediateFolder", intermediateFolder,
26 "-o", outputPath 26 "-o", outputPath