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
Rob Mensching committed
Mar 23, 2022 at 10:25 UTC
c86a2148f6dd7bfcd6637b6e1c9e7b5a9b53a996
28 files changed
+468
-445
src/api/wix/WixToolset.Extensibility/Data/IWindowsInstallerDecompileContext.cs
renamed
+40
-4
@@ -5,21 +5,40 @@ namespace WixToolset.Extensibility.Data
5
using System;
6
using System.Collections.Generic;
7
using WixToolset.Data;
8
- using WixToolset.Extensibility.Services;
8
10
-#pragma warning disable 1591 // TODO: add documentation
11
- public interface IDecompileContext
9
+ /// <summary>
10
+ /// The context used to decompile Windows Installer packages.
11
+ /// </summary>
12
+ public interface IWindowsInstallerDecompileContext
13
{
14
+ /// <summary>
15
+ /// Gets or sets the service provider.
16
+ /// </summary>
17
IServiceProvider ServiceProvider { get; }
18
19
+ /// <summary>
20
+ /// Gets or sets the path to the file to decompile.
21
+ /// </summary>
22
string DecompilePath { get; set; }
23
24
+ /// <summary>
25
+ /// Gets or sets the type to decompile.
26
+ /// </summary>
27
OutputType DecompileType { get; set; }
28
19
- IReadOnlyCollection<IDecompilerExtension> Extensions { get; set; }
29
+ /// <summary>
30
+ /// Gets or sets the decompiler extensions.
31
+ /// </summary>
32
+ IReadOnlyCollection<IWindowsInstallerDecompilerExtension> Extensions { get; set; }
33
34
+ /// <summary>
35
+ /// Gets or sets the folder where content is extracted.
36
+ /// </summary>
37
string ExtractFolder { get; set; }
38
39
+ /// <summary>
40
+ /// Gets or sets the folder where files are extracted.
41
+ /// </summary>
42
string CabinetExtractFolder { get; set; }
43
44
/// <summary>
@@ -28,10 +47,19 @@ namespace WixToolset.Extensibility.Data
47
/// <remarks>Default value is "SourceDir" to enable use of BindPaths.</remarks>
48
string BaseSourcePath { get; set; }
49
50
+ /// <summary>
51
+ /// Gets or sets the intermediate folder.
52
+ /// </summary>
53
string IntermediateFolder { get; set; }
54
55
+ /// <summary>
56
+ /// Gets or sets whether the decompiler admin image.
57
+ /// </summary>
58
bool IsAdminImage { get; set; }
59
60
+ /// <summary>
61
+ /// Gets or sets where to output the result.
62
+ /// </summary>
63
string OutputPath { get; set; }
64
65
/// <summary>
@@ -44,8 +72,16 @@ namespace WixToolset.Extensibility.Data
72
/// </summary>
73
bool SuppressDroppingEmptyTables { get; set; }
74
75
+ /// <summary>
76
+ /// Gets or sets whether to prevent extract cabinets.
77
+ /// </summary>
78
bool SuppressExtractCabinets { get; set; }
79
80
+ /// <summary>
81
+ /// Gets or sets whether to suppress relative action sequencing.
82
+ /// </summary>
83
+ bool SuppressRelativeActionSequencing { get; set; }
84
+
85
/// <summary>
86
/// Gets or sets the option to suppress decompiling UI-related tables.
87
/// </summary>
src/api/wix/WixToolset.Extensibility/Data/IWindowsInstallerDecompileResult.cs
renamed
+1
-1
@@ -7,7 +7,7 @@ namespace WixToolset.Extensibility.Data
7
using WixToolset.Data;
8
9
#pragma warning disable 1591 // TODO: add documentation
10
- public interface IDecompileResult
10
+ public interface IWindowsInstallerDecompileResult
11
{
12
XDocument Document { get; set; }
13
src/api/wix/WixToolset.Extensibility/IBackend.cs
-3
@@ -15,8 +15,5 @@ namespace WixToolset.Extensibility
15
/// <param name="context">Bind context.</param>
16
/// <returns>Result of the bind operation.</returns>
17
IBindResult Bind(IBindContext context);
18
-
19
-#pragma warning disable 1591 // TODO: add documentation
20
- IDecompileResult Decompile(IDecompileContext context);
18
}
19
}
src/api/wix/WixToolset.Extensibility/IBackendFactory.cs
+3
-3
@@ -3,7 +3,7 @@
3
namespace WixToolset.Extensibility
4
{
5
/// <summary>
6
- /// Implemented by extensions to create backends.
6
+ /// Implemented by extensions that are backends.
7
/// </summary>
8
public interface IBackendFactory
9
{
@@ -12,8 +12,8 @@ namespace WixToolset.Extensibility
12
/// </summary>
13
/// <param name="outputType">Type of output being created.</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>
17
- bool TryCreateBackend(string outputType, string outputPath, out IBackend backend);
17
+ bool TryCreateBackend(string outputType, string outputPath, out IBackend binder);
18
}
19
}
src/api/wix/WixToolset.Extensibility/IDecompilerExtension.cs
deleted
-22
@@ -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
-
3
-namespace 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
-}
src/api/wix/WixToolset.Extensibility/IWindowsInstallerDecompilerExtension.cs
renamed
+3
-3
@@ -9,18 +9,18 @@ namespace WixToolset.Extensibility
9
/// <summary>
10
/// Interface all binder extensions implement.
11
/// </summary>
12
- public interface IWindowsInstallerBackendDecompilerExtension
12
+ public interface IWindowsInstallerDecompilerExtension
13
{
14
/// <summary>
15
/// Called before decompiling occurs.
16
/// </summary>
17
- void PreBackendDecompile(IDecompileContext context);
17
+ void PreDecompile(IWindowsInstallerDecompileContext context);
18
19
// TODO: Redesign this interface to be useful.
20
21
/// <summary>
22
/// Called after all output changes occur and right before the output is bound into its final format.
23
/// </summary>
24
- void PostBackendDecompile(IDecompileResult result);
24
+ void PostDecompile(IWindowsInstallerDecompileResult result);
25
}
26
}
src/api/wix/WixToolset.Extensibility/Services/ICommandLineParser.cs
+86
-11
@@ -5,36 +5,111 @@ namespace WixToolset.Extensibility.Services
5
using System.Collections.Generic;
6
using WixToolset.Data;
7
8
-#pragma warning disable 1591 // TODO: add documentation
8
+ /// <summary>
9
+ /// Provides the command-line arguments.
10
+ /// </summary>
11
public interface ICommandLineParser
12
{
13
+ /// <summary>
14
+ /// Gets the argument that caused the error.
15
+ /// </summary>
16
string ErrorArgument { get; }
17
18
/// <summary>
19
/// Validates that a valid switch (starts with "/" or "-"), and returns a bool indicating its validity
20
/// </summary>
16
- /// <param name="arg">The string check.</param>
21
+ /// <param name="argument">The string check.</param>
22
/// <returns>True if a valid switch, otherwise false.</returns>
18
- bool IsSwitch(string arg);
23
+ bool IsSwitch(string argument);
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>
31
string GetArgumentAsFilePathOrError(string argument, string fileType);
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);
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);
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);
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);
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);
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);
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);
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>
93
void ReportErrorArgument(string argument, Message message = null);
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);
114
}
115
}
src/wix/WixToolset.Core.Burn/BundleBackend.cs
-6
@@ -2,7 +2,6 @@
2
3
namespace WixToolset.Core.Burn
4
{
5
- using System;
5
using WixToolset.Extensibility;
6
using WixToolset.Extensibility.Data;
7
using WixToolset.Extensibility.Services;
@@ -35,10 +34,5 @@ namespace WixToolset.Core.Burn
34
35
return result;
36
}
38
-
39
- public IDecompileResult Decompile(IDecompileContext context)
40
- {
41
- throw new NotImplementedException();
42
- }
37
}
38
}
src/wix/WixToolset.Core.WindowsInstaller/CommandLine/DecompilerSubcommand.cs
new
+185
@@ -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
+
3
+namespace 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
+}
src/wix/WixToolset.Core.WindowsInstaller/CommandLine/WindowsInstallerCommand.cs
+4
@@ -45,6 +45,10 @@ namespace WixToolset.Core.WindowsInstaller.CommandLine
45
{
46
switch (argument.ToLowerInvariant())
47
{
48
+ case "decompile":
49
+ this.Subcommand = new DecompilerSubcommand(this.ServiceProvider);
50
+ return true;
51
+
52
case "inscribe":
53
this.Subcommand = new InscribeSubcommand(this.ServiceProvider);
54
return true;
src/wix/WixToolset.Core.WindowsInstaller/Decompile/DecompileMsiOrMsmCommand.cs
+6
-11
@@ -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
3
namespace WixToolset.Core.WindowsInstaller.Decompile
4
{
@@ -11,28 +11,24 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
11
using WixToolset.Core.WindowsInstaller.Unbind;
12
using WixToolset.Data;
13
using WixToolset.Data.WindowsInstaller;
14
- using WixToolset.Extensibility;
14
using WixToolset.Extensibility.Data;
15
using WixToolset.Extensibility.Services;
16
17
internal class DecompileMsiOrMsmCommand
18
{
20
- public DecompileMsiOrMsmCommand(IDecompileContext context, IEnumerable<IWindowsInstallerBackendDecompilerExtension> backendExtensions)
19
+ public DecompileMsiOrMsmCommand(IWindowsInstallerDecompileContext context)
20
{
21
this.Context = context;
23
- this.Extensions = backendExtensions;
22
this.Messaging = context.ServiceProvider.GetService<IMessaging>();
23
}
24
27
- private IDecompileContext Context { get; }
28
-
29
- private IEnumerable<IWindowsInstallerBackendDecompilerExtension> Extensions { get; }
25
+ private IWindowsInstallerDecompileContext Context { get; }
26
27
private IMessaging Messaging { get; }
28
33
- public IDecompileResult Execute()
29
+ public IWindowsInstallerDecompileResult Execute()
30
{
35
- var result = this.Context.ServiceProvider.GetService<IDecompileResult>();
31
+ var result = this.Context.ServiceProvider.GetService<IWindowsInstallerDecompileResult>();
32
33
try
34
{
@@ -50,7 +46,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
46
var output = unbindCommand.Execute();
47
var extractedFilePaths = new List<string>(unbindCommand.ExportedFiles);
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);
50
result.Document = decompiler.Decompile(output);
51
52
result.Platform = GetPlatformFromOutput(output);
@@ -90,7 +86,6 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
86
var template = output.Tables["_SummaryInformation"]?.Rows.SingleOrDefault(row => row.FieldAsInteger(0) == 7)?.FieldAsString(1);
87
88
return Decompiler.GetPlatformFromTemplateSummaryInformation(template?.Split(';'));
93
-
89
}
90
}
91
}
src/wix/WixToolset.Core.WindowsInstaller/Decompile/Decompiler.cs
+72
-53
@@ -43,7 +43,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
43
/// <summary>
44
/// Creates a new decompiler object with a default set of table definitions.
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
{
48
this.Messaging = messaging;
49
this.BackendHelper = backendHelper;
@@ -51,10 +51,11 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
51
this.BaseSourcePath = baseSourcePath ?? "SourceDir";
52
this.SuppressCustomTables = suppressCustomTables;
53
this.SuppressDroppingEmptyTables = suppressDroppingEmptyTables;
54
+ this.SuppressRelativeActionSequencing = suppressRelativeActionSequencing;
55
this.SuppressUI = suppressUI;
56
this.TreatProductAsModule = treatProductAsModule;
57
57
- this.ExtensionsByTableName = new Dictionary<string, IWindowsInstallerBackendDecompilerExtension>();
58
+ this.ExtensionsByTableName = new Dictionary<string, IWindowsInstallerDecompilerExtension>();
59
this.StandardActions = WindowsInstallerStandard.StandardActions().ToDictionary(a => a.Id.Id);
60
61
this.TableDefinitions = new TableDefinitionCollection();
@@ -64,9 +65,9 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
65
66
private IBackendHelper BackendHelper { get; }
67
67
- private IEnumerable<IWindowsInstallerBackendDecompilerExtension> Extensions { get; }
68
+ private IEnumerable<IWindowsInstallerDecompilerExtension> Extensions { get; }
69
69
- private Dictionary<string, IWindowsInstallerBackendDecompilerExtension> ExtensionsByTableName { get; }
70
+ private Dictionary<string, IWindowsInstallerDecompilerExtension> ExtensionsByTableName { get; }
71
72
private string BaseSourcePath { get; }
73
@@ -238,7 +239,10 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
239
/// </summary>
240
/// <param name="row">The row corresponding to the element.</param>
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
+ }
246
247
/// <summary>
248
/// Gets the element corresponding to the primary key of the given table.
@@ -246,7 +250,10 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
250
/// <param name="table">The table corresponding to the element.</param>
251
/// <param name="primaryKey">The primary key corresponding to the element.</param>
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
+ }
257
258
/// <summary>
259
/// Tries to get the element corresponding to the primary key of the given table.
@@ -254,7 +261,10 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
261
/// <param name="row">The table corresponding to the element.</param>
262
/// <param name="xElement">The indexed element.</param>
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
+ }
268
269
/// <summary>
270
/// Tries to get the element corresponding to the primary key of the given table.
@@ -263,14 +273,17 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
273
/// <param name="xElement">The indexed element.</param>
274
/// <param name="primaryKey">The primary key corresponding to the element.</param>
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
+ }
280
281
/// <summary>
282
/// Index an element by its corresponding row.
283
/// </summary>
284
/// <param name="row">The row corresponding to the element.</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)
287
{
288
this.IndexedElements.Add(String.Concat(row.TableDefinition.Name, ':', row.GetPrimaryKey(DecompilerConstants.PrimaryKeyDelimiter)), element);
289
}
@@ -293,9 +306,15 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
306
.ToDictionary(lookup => lookup.Key, lookup => lookup.ToList());
307
}
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
+ }
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
+ }
318
319
private void AddChildToParent(string parentName, XElement xChild, Row row, int column)
320
{
@@ -310,7 +329,10 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
329
}
330
}
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
+ }
336
337
private static void SetAttributeIfNotNull(XElement xElement, string attributeName, string value)
338
{
@@ -1578,7 +1600,10 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
1600
/// Nests the Permission elements below their parent elements. There are no declared foreign
1601
/// keys for the parents of the LockPermissions table.
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
+ }
1607
1608
/// <summary>
1609
/// Finalize the MsiLockPermissionsEx table.
@@ -1588,7 +1613,10 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
1613
/// Nests the PermissionEx elements below their parent elements. There are no declared foreign
1614
/// keys for the parents of the MsiLockPermissionsEx table.
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
+ }
1620
1621
private static Dictionary<string, List<string>> IndexTable(Table table, int keyColumn, int? dataColumn)
1622
{
@@ -2462,53 +2490,44 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
2490
// index the rows from the extension libraries
2491
var indexedExtensionTables = new Dictionary<string, HashSet<string>>();
2492
#if TODO_DECOMPILER_EXTENSIONS
2465
- foreach (IDecompilerExtension extension in this.extensions)
2493
+ foreach (var extension in this.Extensions)
2494
{
2495
// Get the optional library from the extension with the rows to be removed.
2468
- Library library = extension.GetLibraryToRemove(this.tableDefinitions);
2469
- if (null != library)
2496
+ var library = extension.GetLibraryToRemove(this.tableDefinitions);
2497
+ if (library != null)
2498
{
2471
- foreach (var section in library.Sections)
2499
+ foreach (var row in library.Sections.SelectMany(s => s.Tables).SelectMany(t => t.Rows))
2500
{
2473
- foreach (Table table in section.Tables)
2474
- {
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);
2501
+ string primaryKey;
2502
+ string tableName;
2503
2485
- if (OutputType.Module == this.outputType)
2486
- {
2487
- tableName = String.Concat("Module", row.FieldAsString(0));
2488
- }
2489
- else
2490
- {
2491
- tableName = row.FieldAsString(0);
2492
- }
2493
- }
2494
- else
2495
- {
2496
- primaryKey = row.GetPrimaryKey(DecompilerConstants.PrimaryKeyDelimiter);
2497
- tableName = table.Name;
2498
- }
2504
+ // the Actions table needs to be handled specially
2505
+ if (table.Name == "WixAction")
2506
+ {
2507
+ primaryKey = row.FieldAsString(1);
2508
+ tableName = row.FieldAsString(0);
2509
2500
- if (null != primaryKey)
2501
- {
2502
- HashSet<string> indexedExtensionRows;
2503
- if (!indexedExtensionTables.TryGetValue(tableName, out indexedExtensionRows))
2504
- {
2505
- indexedExtensionRows = new HashSet<string>();
2506
- indexedExtensionTables.Add(tableName, indexedExtensionRows);
2507
- }
2510
+ if (this.outputType == OutputType.Module)
2511
+ {
2512
+ tableName = "Module" + tableName;
2513
+ }
2514
+ }
2515
+ else
2516
+ {
2517
+ primaryKey = row.GetPrimaryKey(DecompilerConstants.PrimaryKeyDelimiter);
2518
+ tableName = table.Name;
2519
+ }
2520
2509
- indexedExtensionRows.Add(primaryKey);
2510
- }
2521
+ if (primaryKey != null)
2522
+ {
2523
+ HashSet<string> indexedExtensionRows;
2524
+ if (!indexedExtensionTables.TryGetValue(tableName, out indexedExtensionRows))
2525
+ {
2526
+ indexedExtensionRows = new HashSet<string>();
2527
+ indexedExtensionTables.Add(tableName, indexedExtensionRows);
2528
}
2529
+
2530
+ indexedExtensionRows.Add(primaryKey);
2531
}
2532
}
2533
}
src/wix/WixToolset.Core.WindowsInstaller/IWindowsInstallerDecompiler.cs
new
+19
@@ -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
+
3
+namespace 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
+}
src/wix/WixToolset.Core.WindowsInstaller/MsiBackend.cs
-22
@@ -47,27 +47,5 @@ namespace WixToolset.Core.WindowsInstaller
47
}
48
}
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
- }
50
}
51
}
src/wix/WixToolset.Core.WindowsInstaller/MsmBackend.cs
-26
@@ -2,11 +2,7 @@
2
3
namespace WixToolset.Core.WindowsInstaller
4
{
5
- using System;
5
using WixToolset.Core.WindowsInstaller.Bind;
7
- using WixToolset.Core.WindowsInstaller.Decompile;
8
- using WixToolset.Core.WindowsInstaller.Unbind;
9
- using WixToolset.Data;
6
using WixToolset.Extensibility;
7
using WixToolset.Extensibility.Data;
8
using WixToolset.Extensibility.Services;
@@ -43,27 +39,5 @@ namespace WixToolset.Core.WindowsInstaller
39
throw;
40
}
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
- }
42
}
43
}
src/wix/WixToolset.Core.WindowsInstaller/MspBackend.cs
-5
@@ -63,11 +63,6 @@ namespace WixToolset.Core.WindowsInstaller
63
}
64
}
65
66
- public IDecompileResult Decompile(IDecompileContext context)
67
- {
68
- throw new NotImplementedException();
69
- }
70
-
66
#if TODO_PATCHING
67
public Intermediate Unbind(IUnbindContext context)
68
{
src/wix/WixToolset.Core.WindowsInstaller/WindowsInstallerDecompileContext.cs
renamed
+6
-5
@@ -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.
2
3
-namespace WixToolset.Core
3
+namespace WixToolset.Core.WindowsInstaller
4
{
5
using System;
6
using System.Collections.Generic;
7
using WixToolset.Data;
8
using WixToolset.Extensibility;
9
using WixToolset.Extensibility.Data;
10
- using WixToolset.Extensibility.Services;
10
12
- internal class DecompileContext : IDecompileContext
11
+ internal class WindowsInstallerDecompileContext : IWindowsInstallerDecompileContext
12
{
14
- internal DecompileContext(IServiceProvider serviceProvider)
13
+ internal WindowsInstallerDecompileContext(IServiceProvider serviceProvider)
14
{
15
this.ServiceProvider = serviceProvider;
16
}
@@ -22,7 +21,7 @@ namespace WixToolset.Core
21
22
public OutputType DecompileType { get; set; }
23
25
- public IReadOnlyCollection<IDecompilerExtension> Extensions { get; set; }
24
+ public IReadOnlyCollection<IWindowsInstallerDecompilerExtension> Extensions { get; set; }
25
26
public string ExtractFolder { get; set; }
27
@@ -40,6 +39,8 @@ namespace WixToolset.Core
39
40
public bool SuppressDroppingEmptyTables { get; set; }
41
42
+ public bool SuppressRelativeActionSequencing { get; set; }
43
+
44
public bool SuppressExtractCabinets { get; set; }
45
46
public bool SuppressUI { get; set; }
src/wix/WixToolset.Core.WindowsInstaller/WindowsInstallerDecompileResult.cs
renamed
+2
-2
@@ -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.
2
3
-namespace WixToolset.Core
3
+namespace WixToolset.Core.WindowsInstaller
4
{
5
using System.Collections.Generic;
6
using System.Xml.Linq;
7
using WixToolset.Data;
8
using WixToolset.Extensibility.Data;
9
10
- internal class DecompileResult : IDecompileResult
10
+ internal class WindowsInstallerDecompileResult : IWindowsInstallerDecompileResult
11
{
12
public XDocument Document { get; set; }
13
src/wix/WixToolset.Core.WindowsInstaller/WindowsInstallerDecompiler.cs
renamed
+7
-25
@@ -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.
2
3
-namespace WixToolset.Core
3
+namespace WixToolset.Core.WindowsInstaller
4
{
5
using System;
6
+ using WixToolset.Core.WindowsInstaller.Decompile;
7
using WixToolset.Extensibility;
8
using WixToolset.Extensibility.Data;
9
using WixToolset.Extensibility.Services;
@@ -10,16 +11,16 @@ namespace WixToolset.Core
11
/// <summary>
12
/// Decompiler of the WiX toolset.
13
/// </summary>
13
- internal class Decompiler : IDecompiler
14
+ internal class WindowsInstallerDecompiler : IWindowsInstallerDecompiler
15
{
15
- internal Decompiler(IServiceProvider serviceProvider)
16
+ internal WindowsInstallerDecompiler(IServiceProvider serviceProvider)
17
{
18
this.ServiceProvider = serviceProvider;
19
}
20
21
public IServiceProvider ServiceProvider { get; }
22
22
- public IDecompileResult Decompile(IDecompileContext context)
23
+ public IWindowsInstallerDecompileResult Decompile(IWindowsInstallerDecompileContext context)
24
{
25
// Pre-decompile.
26
//
@@ -30,7 +31,8 @@ namespace WixToolset.Core
31
32
// Decompile.
33
//
33
- var result = this.BackendDecompile(context);
34
+ var command = new DecompileMsiOrMsmCommand(context);
35
+ var result = command.Execute();
36
37
if (result != null)
38
{
@@ -44,25 +46,5 @@ namespace WixToolset.Core
46
47
return result;
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
- }
49
}
50
}
src/wix/WixToolset.Core.WindowsInstaller/WixToolsetCoreServiceProviderExtensions.cs
+6
@@ -5,6 +5,7 @@ namespace WixToolset.Core.WindowsInstaller
5
using System;
6
using System.Collections.Generic;
7
using WixToolset.Core.WindowsInstaller.ExtensibilityServices;
8
+ using WixToolset.Extensibility.Data;
9
using WixToolset.Extensibility.Services;
10
11
/// <summary>
@@ -31,6 +32,11 @@ namespace WixToolset.Core.WindowsInstaller
32
{
33
// Singletons.
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());
40
}
41
42
private static T AddSingleton<T>(Dictionary<Type, object> singletons, T service) where T : class
src/wix/WixToolset.Core/CommandLine/CommandLine.cs
+2
-21
@@ -9,14 +9,6 @@ namespace WixToolset.Core.CommandLine
9
using WixToolset.Extensibility.Data;
10
using WixToolset.Extensibility.Services;
11
12
- internal enum CommandTypes
13
- {
14
- Unknown,
15
- Build,
16
- Preprocess,
17
- Decompile,
18
- }
19
-
12
internal class CommandLine : ICommandLine
13
{
14
public CommandLine(IServiceProvider serviceProvider)
@@ -154,18 +146,9 @@ namespace WixToolset.Core.CommandLine
146
}
147
else
148
{
157
- if (Enum.TryParse(arg, true, out CommandTypes commandType))
149
+ if ("build".Equals(arg, StringComparison.OrdinalIgnoreCase))
150
{
159
- switch (commandType)
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
- }
151
+ command = new BuildCommand(this.ServiceProvider);
152
}
153
else
154
{
@@ -175,8 +158,6 @@ namespace WixToolset.Core.CommandLine
158
{
159
break;
160
}
178
-
179
- command = null;
161
}
162
}
163
}
src/wix/WixToolset.Core/CommandLine/CommandLineParser.cs
+23
-2
@@ -41,12 +41,16 @@ namespace WixToolset.Core.CommandLine
41
return argument;
42
}
43
44
- public void GetArgumentAsFilePathOrError(string argument, string fileType, IList<string> paths)
44
+ public bool GetArgumentAsFilePathOrError(string argument, string fileType, IList<string> paths)
45
{
46
- foreach (var path in this.GetFiles(argument, fileType))
46
+ var files = this.GetFiles(argument, fileType);
47
+
48
+ foreach (var path in files)
49
{
50
paths.Add(path);
51
}
52
+
53
+ return files.Length > 0;
54
}
55
56
public string GetNextArgumentOrError(string commandLineSwitch)
@@ -140,6 +144,23 @@ namespace WixToolset.Core.CommandLine
144
return false;
145
}
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
+
164
private bool TryGetNextNonSwitchArgumentOrError(out string arg)
165
{
166
var result = this.TryGetNextSwitchOrArgument(out arg);
src/wix/WixToolset.Core/CommandLine/DecompileCommand.cs
deleted
-202
@@ -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
-
3
-namespace 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
-}
src/wix/WixToolset.Core/IDecompiler.cs
deleted
-12
@@ -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
-
3
-namespace 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
-}
src/wix/WixToolset.Core/WixToolsetServiceProvider.cs
-3
@@ -40,14 +40,12 @@ namespace WixToolset.Core
40
this.AddService<ILinkContext>((provider, singletons) => new LinkContext(provider));
41
this.AddService<IResolveContext>((provider, singletons) => new ResolveContext(provider));
42
this.AddService<IBindContext>((provider, singletons) => new BindContext(provider));
43
- this.AddService<IDecompileContext>((provider, singletons) => new DecompileContext(provider));
43
this.AddService<ILayoutContext>((provider, singletons) => new LayoutContext(provider));
44
45
this.AddService<IBindFileWithPath>((provider, singletons) => new BindFileWithPath());
46
this.AddService<IBindPath>((provider, singletons) => new BindPath());
47
this.AddService<IBindResult>((provider, singletons) => new BindResult());
48
this.AddService<IComponentKeyPath>((provider, singletons) => new ComponentKeyPath());
50
- this.AddService<IDecompileResult>((provider, singletons) => new DecompileResult());
49
this.AddService<IIncludedFile>((provider, singletons) => new IncludedFile());
50
this.AddService<IPreprocessResult>((provider, singletons) => new PreprocessResult());
51
this.AddService<IResolvedDirectory>((provider, singletons) => new ResolvedDirectory());
@@ -58,7 +56,6 @@ namespace WixToolset.Core
56
57
this.AddService<IBinder>((provider, singletons) => new Binder(provider));
58
this.AddService<ICompiler>((provider, singletons) => new Compiler(provider));
61
- this.AddService<IDecompiler>((provider, singletons) => new Decompiler(provider));
59
this.AddService<ILayoutCreator>((provider, singletons) => new LayoutCreator(provider));
60
this.AddService<IPreprocessor>((provider, singletons) => new Preprocessor(provider));
61
this.AddService<ILibrarian>((provider, singletons) => new Librarian(provider));
src/wix/test/WixToolsetTest.Converters/ProductPackageFixture.cs
+1
-1
@@ -249,7 +249,7 @@ namespace WixToolsetTest.Converters
249
var v3msiPath = Path.Combine(folder, "TypicalV3.msi");
250
var result = WixRunner.Execute(new[]
251
{
252
- "decompile", v3msiPath,
252
+ "msi", "decompile", v3msiPath,
253
"-intermediateFolder", intermediateFolder,
254
"-o", decompiledWxsPath
255
});
src/wix/test/WixToolsetTest.CoreIntegration/CustomTableFixture.cs
+1
-1
@@ -219,7 +219,7 @@ namespace WixToolsetTest.CoreIntegration
219
220
result = WixRunner.Execute(new[]
221
{
222
- "decompile", msiPath,
222
+ "msi", "decompile", msiPath,
223
"-sw1060",
224
"-intermediateFolder", intermediateFolder,
225
"-o", decompiledWxsPath
src/wix/test/WixToolsetTest.CoreIntegration/DecompileFixture.cs
+1
-1
@@ -20,7 +20,7 @@ namespace WixToolsetTest.CoreIntegration
20
21
var result = WixRunner.Execute(new[]
22
{
23
- "decompile",
23
+ "msi", "decompile",
24
Path.Combine(folder, msiName),
25
"-intermediateFolder", intermediateFolder,
26
"-o", outputPath