@joebigelow / wix-1 / commits / 5d08e0a4

Implement IWindowsInstallerDecompileExtensions

Update Util extension to validate extension model and fix some small issues in MSI decompiling. Fixes 6367

Rob Mensching committed Mar 30, 2022 at 10:35 UTC 5d08e0a4bbf4e4ba28300b8bace1089b64b198d7
46 files changed +1644 -1390
src/api/wix/WixToolset.Extensibility/BaseWindowsInstallerBackendBinderExtension.cs
+5 -3
@@ -4,7 +4,6 @@ namespace WixToolset.Extensibility
4 {
5 using System;
6 using System.Collections.Generic;
7 - using System.Linq;
7 using WixToolset.Data;
8 using WixToolset.Data.Symbols;
9 using WixToolset.Data.WindowsInstaller;
@@ -12,7 +11,7 @@ namespace WixToolset.Extensibility
11 using WixToolset.Extensibility.Services;
12
13 /// <summary>
15 - /// Base class for creating a preprocessor extension.
14 + /// Base class for creating a windows installer backend extension.
15 /// </summary>
16 public abstract class BaseWindowsInstallerBackendBinderExtension : IWindowsInstallerBackendBinderExtension
17 {
@@ -39,7 +38,10 @@ namespace WixToolset.Extensibility
38 /// <summary>
39 /// Creates a resolved cabinet result.
40 /// </summary>
42 - protected IResolvedCabinet CreateResolvedCabinet() => this.Context.ServiceProvider.GetService<IResolvedCabinet>();
41 + protected IResolvedCabinet CreateResolvedCabinet()
42 + {
43 + return this.Context.ServiceProvider.GetService<IResolvedCabinet>();
44 + }
45
46 /// <summary>
47 /// See <see cref="IWindowsInstallerBackendBinderExtension.PreBackendBind(IBindContext)"/>
src/api/wix/WixToolset.Extensibility/BaseWindowsInstallerDecompilerExtension.cs new
+77
@@ -0,0 +1,77 @@
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 System.Collections.Generic;
6 + using WixToolset.Data;
7 + using WixToolset.Data.WindowsInstaller;
8 + using WixToolset.Extensibility.Data;
9 + using WixToolset.Extensibility.Services;
10 +
11 + /// <summary>
12 + /// Base class for creating a windows installer decompiler extensions.
13 + /// </summary>
14 + public abstract class BaseWindowsInstallerDecompilerExtension : IWindowsInstallerDecompilerExtension
15 + {
16 + /// <summary>
17 + /// Context for use by the extension.
18 + /// </summary>
19 + protected IWindowsInstallerDecompileContext Context { get; private set; }
20 +
21 + /// <summary>
22 + /// Messaging for use by the extension.
23 + /// </summary>
24 + protected IMessaging Messaging { get; private set; }
25 +
26 + /// <summary>
27 + /// Decompiler helper for use by the extension.
28 + /// </summary>
29 + protected IWindowsInstallerDecompilerHelper DecompilerHelper { get; private set; }
30 +
31 + /// <summary>
32 + /// See <see cref="IWindowsInstallerDecompilerExtension.TableDefinitions"/>
33 + /// </summary>
34 + public virtual IReadOnlyCollection<TableDefinition> TableDefinitions { get; }
35 +
36 + /// <summary>
37 + /// See <see cref="IWindowsInstallerDecompilerExtension.PostDecompile(IWindowsInstallerDecompileResult)"/>
38 + /// </summary>
39 + public virtual void PreDecompile(IWindowsInstallerDecompileContext context)
40 + {
41 + this.Context = context;
42 +
43 + this.Messaging = context.ServiceProvider.GetService<IMessaging>();
44 +
45 + this.DecompilerHelper = context.ServiceProvider.GetService<IWindowsInstallerDecompilerHelper>();
46 + }
47 +
48 + /// <summary>
49 + /// See <see cref="IWindowsInstallerDecompilerExtension.PreDecompileTables(TableIndexedCollection)"/>
50 + /// </summary>
51 + public virtual void PreDecompileTables(TableIndexedCollection tables)
52 + {
53 + }
54 +
55 + /// <summary>
56 + /// See <see cref="IWindowsInstallerDecompilerExtension.TryDecompileTable(Table)"/>
57 + /// </summary>
58 + public virtual bool TryDecompileTable(Table table)
59 + {
60 + return false;
61 + }
62 +
63 + /// <summary>
64 + /// See <see cref="IWindowsInstallerDecompilerExtension.PostDecompileTables(TableIndexedCollection)"/>
65 + /// </summary>
66 + public virtual void PostDecompileTables(TableIndexedCollection tables)
67 + {
68 + }
69 +
70 + /// <summary>
71 + /// See <see cref="IWindowsInstallerDecompilerExtension.PostDecompile(IWindowsInstallerDecompileResult)"/>
72 + /// </summary>
73 + public virtual void PostDecompile(IWindowsInstallerDecompileResult result)
74 + {
75 + }
76 + }
77 +}
src/api/wix/WixToolset.Extensibility/Data/IWindowsInstallerDecompileContext.cs
+11 -1
@@ -7,7 +7,7 @@ namespace WixToolset.Extensibility.Data
7 using WixToolset.Data;
8
9 /// <summary>
10 - /// The context used to decompile Windows Installer packages.
10 + /// The context used to decompile a Windows Installer database.
11 /// </summary>
12 public interface IWindowsInstallerDecompileContext
13 {
@@ -31,6 +31,16 @@ namespace WixToolset.Extensibility.Data
31 /// </summary>
32 IReadOnlyCollection<IWindowsInstallerDecompilerExtension> Extensions { get; set; }
33
34 + /// <summary>
35 + /// Collection of extension data to use during decompiling.
36 + /// </summary>
37 + IReadOnlyCollection<IExtensionData> ExtensionData { get; set; }
38 +
39 + /// <summary>
40 + /// Symbol definition creator used to load extension data.
41 + /// </summary>
42 + ISymbolDefinitionCreator SymbolDefinitionCreator { get; set; }
43 +
44 /// <summary>
45 /// Gets or sets the folder where content is extracted.
46 /// </summary>
src/api/wix/WixToolset.Extensibility/Data/IWindowsInstallerDecompileResult.cs
+13 -2
@@ -6,13 +6,24 @@ namespace WixToolset.Extensibility.Data
6 using System.Xml.Linq;
7 using WixToolset.Data;
8
9 -#pragma warning disable 1591 // TODO: add documentation
9 + /// <summary>
10 + /// The result from decompiling a Windows Installer database.
11 + /// </summary>
12 public interface IWindowsInstallerDecompileResult
13 {
14 + /// <summary>
15 + /// Decompiled document.
16 + /// </summary>
17 XDocument Document { get; set; }
18
14 - IReadOnlyCollection<string> ExtractedFilePaths { get; set; }
19 + /// <summary>
20 + /// Extracted paths.
21 + /// </summary>
22 + IList<string> ExtractedFilePaths { get; set; }
23
24 + /// <summary>
25 + /// Decompiled platform.
26 + /// </summary>
27 Platform? Platform { get; set; }
28 }
29 }
src/api/wix/WixToolset.Extensibility/DecompilerExtension.cs deleted
-61
@@ -1,61 +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.Data;
6 -
7 -#if BRING_BACK_LATER
8 - /// <summary>
9 - /// Base class for creating a decompiler extension.
10 - /// </summary>
11 - public abstract class DecompilerExtension : IDecompilerExtension
12 - {
13 - /// <summary>
14 - /// Gets or sets the decompiler core for the extension.
15 - /// </summary>
16 - /// <value>The decompiler core for the extension.</value>
17 - public IDecompilerCore Core { get; set; }
18 -
19 - /// <summary>
20 - /// Gets the table definitions this extension decompiles.
21 - /// </summary>
22 - /// <value>Table definitions this extension decompiles.</value>
23 - public virtual TableDefinitionCollection TableDefinitions { get; protected set; }
24 -
25 - /// <summary>
26 - /// Gets the library that this decompiler wants removed from the decomipiled output.
27 - /// </summary>
28 - /// <param name="tableDefinitions">The table definitions to use while loading the library.</param>
29 - /// <returns>The library for this extension or null if there is no library to be removed.</returns>
30 - public virtual Library GetLibraryToRemove(TableDefinitionCollection tableDefinitions)
31 - {
32 - return null;
33 - }
34 -
35 - /// <summary>
36 - /// Called at the beginning of the decompilation of a database.
37 - /// </summary>
38 - /// <param name="tables">The collection of all tables.</param>
39 - public virtual void Initialize(TableIndexedCollection tables)
40 - {
41 - }
42 -
43 - /// <summary>
44 - /// Decompiles an extension table.
45 - /// </summary>
46 - /// <param name="table">The table to decompile.</param>
47 - public virtual void DecompileTable(Table table)
48 - {
49 - this.Core.UnexpectedTable(table);
50 - }
51 -
52 - /// <summary>
53 - /// Finalize decompilation.
54 - /// </summary>
55 - /// <param name="tables">The collection of all tables.</param>
56 - public virtual void Finish(TableIndexedCollection tables)
57 - {
58 - }
59 - }
60 -#endif
61 -}
src/api/wix/WixToolset.Extensibility/IWindowsInstallerDecompilerExtension.cs
+27 -3
@@ -2,21 +2,45 @@
2
3 namespace WixToolset.Extensibility
4 {
5 - using WixToolset.Data;
5 + using System.Collections.Generic;
6 using WixToolset.Data.WindowsInstaller;
7 using WixToolset.Extensibility.Data;
8
9 /// <summary>
10 - /// Interface all binder extensions implement.
10 + /// Interface all windows installer decompiler extensions implement.
11 /// </summary>
12 public interface IWindowsInstallerDecompilerExtension
13 {
14 + /// <summary>
15 + /// Gets the table definitions this extension decompiles.
16 + /// </summary>
17 + /// <value>Table definitions this extension decompiles.</value>
18 + IReadOnlyCollection<TableDefinition> TableDefinitions { get; }
19 +
20 /// <summary>
21 /// Called before decompiling occurs.
22 /// </summary>
23 + /// <param name="context">Decompile context.</param>
24 void PreDecompile(IWindowsInstallerDecompileContext context);
25
19 - // TODO: Redesign this interface to be useful.
26 + /// <summary>
27 + /// Called before decompiling occurs.
28 + /// </summary>
29 + /// <param name="tables">The collection of all tables.</param>
30 + void PreDecompileTables(TableIndexedCollection tables);
31 +
32 + /// <summary>
33 + /// Try to decompile an extension table.
34 + /// </summary>
35 + /// <param name="table">The table to decompile.</param>
36 + /// <returns>True if the table was decompiled, false otherwise.</returns>
37 + bool TryDecompileTable(Table table);
38 +
39 + /// <summary>
40 + /// After decompilation tables.
41 + /// </summary>
42 + /// <param name="tables">The collection of all tables.</param>
43 + void PostDecompileTables(TableIndexedCollection tables);
44
45 /// <summary>
46 /// Called after all output changes occur and right before the output is bound into its final format.
src/api/wix/WixToolset.Extensibility/Services/IWindowsInstallerDecompilerHelper.cs new
+180
@@ -0,0 +1,180 @@
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.Services
4 +{
5 + using System.Xml.Linq;
6 + using WixToolset.Data.WindowsInstaller;
7 +
8 + /// <summary>
9 + /// Interface provided to help Windows Installer decompiler extensions.
10 + /// </summary>
11 + public interface IWindowsInstallerDecompilerHelper
12 + {
13 + /// <summary>
14 + /// Gets or sets the root element of the decompiled output.
15 + /// </summary>
16 + XElement RootElement { get; set; }
17 +
18 + /// <summary>
19 + /// Creates an element from the standard WiX Toolset namespace and adds it to the root document.
20 + /// </summary>
21 + /// <param name="name">Name of the element to create and add.</param>
22 + /// <param name="content">Optional content to add to the new element.</param>
23 + /// <returns>Element in the standard namespace.</returns>
24 + XElement AddElementToRoot(string name, params object[] content);
25 +
26 + /// <summary>
27 + /// Creates an element with the specified name and adds it to the root document.
28 + /// </summary>
29 + /// <param name="name">Name of the element to create and add.</param>
30 + /// <param name="content">Optional content to add to the new element.</param>
31 + /// <returns>Element in the standard namespace.</returns>
32 + XElement AddElementToRoot(XName name, params object[] content);
33 +
34 + /// <summary>
35 + /// Adds an existing element to the root document.
36 + /// </summary>
37 + /// <param name="element">Element to add.</param>
38 + /// <returns>Same element provided.</returns>
39 + XElement AddElementToRoot(XElement element);
40 +
41 + /// <summary>
42 + /// Creates an element from the standard WiX Toolset namespace.
43 + /// </summary>
44 + /// <param name="name">Name of the element to create.</param>
45 + /// <param name="content">Optional content to add to the new element.</param>
46 + /// <returns>Element in the standard namespace.</returns>
47 + XElement CreateElement(string name, params object[] content);
48 +
49 + /// <summary>
50 + /// Get an element index by a row's table and primary keys.
51 + /// </summary>
52 + /// <param name="row">Row to get element.</param>
53 + /// <returns>Element indexed for the row or null if not found.</returns>
54 + XElement GetIndexedElement(Row row);
55 +
56 + /// <summary>
57 + /// Get an element index by table and primary key.
58 + /// </summary>
59 + /// <param name="table">Table name for indexed element.</param>
60 + /// <param name="primaryKey">Primary key for indexed element.</param>
61 + /// <returns>Element indexed for the table and primary key or null if not found.</returns>
62 + XElement GetIndexedElement(string table, string primaryKey);
63 +
64 + /// <summary>
65 + /// Get an element index by table and primary keys.
66 + /// </summary>
67 + /// <param name="table">Table name for indexed element.</param>
68 + /// <param name="primaryKey1">Primary key for first column indexed element.</param>
69 + /// <param name="primaryKey2">Primary key for second column indexed element.</param>
70 + /// <returns>Element indexed for the table and primary keys or null if not found.</returns>
71 + XElement GetIndexedElement(string table, string primaryKey1, string primaryKey2);
72 +
73 + /// <summary>
74 + /// Get an element index by table and primary keys.
75 + /// </summary>
76 + /// <param name="table">Table name for indexed element.</param>
77 + /// <param name="primaryKey1">Primary key for first column indexed element.</param>
78 + /// <param name="primaryKey2">Primary key for second column indexed element.</param>
79 + /// <param name="primaryKey3">Primary key for third column indexed element.</param>
80 + /// <returns>Element indexed for the table and primary keys or null if not found.</returns>
81 + XElement GetIndexedElement(string table, string primaryKey1, string primaryKey2, string primaryKey3);
82 +
83 + /// <summary>
84 + /// Get an element index by table and primary keys.
85 + /// </summary>
86 + /// <param name="table">Table name for indexed element.</param>
87 + /// <param name="primaryKeys">Primary keys for indexed element.</param>
88 + /// <returns>Element indexed for the table and primary keys or null if not found.</returns>
89 + XElement GetIndexedElement(string table, string[] primaryKeys);
90 +
91 + /// <summary>
92 + /// Try to get an element index by a row's table and primary keys.
93 + /// </summary>
94 + /// <param name="row">Row to get element.</param>
95 + /// <param name="element">Element indexed for the row.</param>
96 + /// <returns>True if the element was index otherwise false.</returns>
97 + bool TryGetIndexedElement(Row row, out XElement element);
98 +
99 + /// <summary>
100 + /// Try to get an element index by table name and primary key.
101 + /// </summary>
102 + /// <param name="table">Table name for indexed element.</param>
103 + /// <param name="primaryKey">Primary key for indexed element.</param>
104 + /// <param name="element">Element indexed for the table and primary key.</param>
105 + /// <returns>True if the element was index otherwise false.</returns>
106 + bool TryGetIndexedElement(string table, string primaryKey, out XElement element);
107 +
108 + /// <summary>
109 + /// Try to get an element index by table name and primary keys.
110 + /// </summary>
111 + /// <param name="table">Table name for indexed element.</param>
112 + /// <param name="primaryKey1">First column's primary key for indexed element.</param>
113 + /// <param name="primaryKey2">Second column's primary key for indexed element.</param>
114 + /// <param name="element">Element indexed for the table and primary key.</param>
115 + /// <returns>True if the element was index otherwise false.</returns>
116 + bool TryGetIndexedElement(string table, string primaryKey1, string primaryKey2, out XElement element);
117 +
118 + /// <summary>
119 + /// Try to get an element index by table name and primary keys.
120 + /// </summary>
121 + /// <param name="table">Table name for indexed element.</param>
122 + /// <param name="primaryKey1">First column's primary key for indexed element.</param>
123 + /// <param name="primaryKey2">Second column's primary key for indexed element.</param>
124 + /// <param name="primaryKey3">Third column's primary key for indexed element.</param>
125 + /// <param name="element">Element indexed for the table and primary key.</param>
126 + /// <returns>True if the element was index otherwise false.</returns>
127 + bool TryGetIndexedElement(string table, string primaryKey1, string primaryKey2, string primaryKey3, out XElement element);
128 +
129 + /// <summary>
130 + /// Try to get an element index by table name and primary keys.
131 + /// </summary>
132 + /// <param name="table">Table name for indexed element.</param>
133 + /// <param name="primaryKeys">Primary keys for indexed element.</param>
134 + /// <param name="element">Element indexed for the table and primary key.</param>
135 + /// <returns>True if the element was index otherwise false.</returns>
136 + bool TryGetIndexedElement(string table, string[] primaryKeys, out XElement element);
137 +
138 + /// <summary>
139 + /// Index an element by a row's table and primary keys.
140 + /// </summary>
141 + /// <param name="row">Row to index element.</param>
142 + /// <param name="element">Element to index.</param>
143 + void IndexElement(Row row, XElement element);
144 +
145 + /// <summary>
146 + /// Index an element by table and primary key.
147 + /// </summary>
148 + /// <param name="table">Table name to index element.</param>
149 + /// <param name="primaryKey">Primary key to index element.</param>
150 + /// <param name="element">Element to index.</param>
151 + void IndexElement(string table, string primaryKey, XElement element);
152 +
153 + /// <summary>
154 + /// Index an element by table and primary keys.
155 + /// </summary>
156 + /// <param name="table">Table name to index element.</param>
157 + /// <param name="primaryKey1">First column's primary key to index element.</param>
158 + /// <param name="primaryKey2">Second column's primary key to index element.</param>
159 + /// <param name="element">Element to index.</param>
160 + void IndexElement(string table, string primaryKey1, string primaryKey2, XElement element);
161 +
162 + /// <summary>
163 + /// Index an element by table and primary keys.
164 + /// </summary>
165 + /// <param name="table">Table name to index element.</param>
166 + /// <param name="primaryKey1">First column's primary key to index element.</param>
167 + /// <param name="primaryKey2">Second column's primary key to index element.</param>
168 + /// <param name="primaryKey3">Third column's primary key to index element.</param>
169 + /// <param name="element">Element to index.</param>
170 + void IndexElement(string table, string primaryKey1, string primaryKey2, string primaryKey3, XElement element);
171 +
172 + /// <summary>
173 + /// Index an element by table and primary keys.
174 + /// </summary>
175 + /// <param name="table">Table name to index element.</param>
176 + /// <param name="primaryKeys">Column's primary key to index element.</param>
177 + /// <param name="element">Element to index.</param>
178 + void IndexElement(string table, string[] primaryKeys, XElement element);
179 + }
180 +}
src/ext/Util/Util.wixext.sln
+2 -2
@@ -1,7 +1,7 @@
1 
2 Microsoft Visual Studio Solution File, Format Version 12.00
3 -# Visual Studio Version 16
4 -VisualStudioVersion = 16.0.30204.135
3 +# Visual Studio Version 17
4 +VisualStudioVersion = 17.1.32228.430
5 MinimumVisualStudioVersion = 15.0.26124.0
6 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "utilbe", "be\utilbe.vcxproj", "{630C1EE7-2517-4A8C-83E3-DA1150308B58}"
7 EndProject
src/ext/Util/Util.wixext.v3.ncrunchsolution new
+6
@@ -0,0 +1,6 @@
1 +<SolutionConfiguration>
2 + <Settings>
3 + <AllowParallelTestExecution>True</AllowParallelTestExecution>
4 + <SolutionConfigured>True</SolutionConfigured>
5 + </Settings>
6 +</SolutionConfiguration>
\ No newline at end of file
src/ext/Util/test/WixToolsetTest.Util/UtilExtensionFixture.cs
+18 -2
@@ -6,8 +6,6 @@ namespace WixToolsetTest.Util
6 using System.Linq;
7 using WixBuildTools.TestSupport;
8 using WixToolset.Core.TestPackage;
9 - using WixToolset.Data;
10 - using WixToolset.Data.Symbols;
9 using WixToolset.Util;
10 using Xunit;
11
@@ -55,6 +53,18 @@ namespace WixToolsetTest.Util
53 }, results.OrderBy(s => s).ToArray());
54 }
55
56 + [Fact]
57 + public void CanRoundtripFileShare()
58 + {
59 + var folder = TestData.Get(@"TestData", "UsingFileShare");
60 + var build = new Builder(folder, typeof(UtilExtensionFactory), new[] { folder });
61 + var output = Path.Combine(folder, "decompile.xml");
62 +
63 + build.BuildAndDecompileAndBuild(Build, Decompile, output);
64 + File.Exists(output);
65 + }
66 +
67 +
68 [Fact]
69 public void CanBuildCloseApplication()
70 {
@@ -313,5 +323,11 @@ namespace WixToolsetTest.Util
323 var result = WixRunner.Execute(newArgs.ToArray());
324 result.AssertSuccess();
325 }
326 +
327 + private static void Decompile(string[] args)
328 + {
329 + var result = WixRunner.Execute(args);
330 + result.AssertSuccess();
331 + }
332 }
333 }
src/ext/Util/wixext/UtilCompiler.cs
+11 -25
@@ -19,7 +19,7 @@ namespace WixToolset.Util
19 /// <summary>
20 /// The compiler for the WiX Toolset Utility Extension.
21 /// </summary>
22 - public sealed class UtilCompiler : BaseCompilerExtension
22 + internal sealed class UtilCompiler : BaseCompilerExtension
23 {
24 // user creation attributes definitions (from sca.h)
25 internal const int UserDontExpirePasswrd = 0x00000001;
@@ -37,7 +37,7 @@ namespace WixToolset.Util
37
38 private static readonly Regex FindPropertyBrackets = new Regex(@"\[(?!\\|\])|(?<!\[\\\]|\[\\|\\\[)\]", RegexOptions.ExplicitCapture | RegexOptions.Compiled);
39
40 - public override XNamespace Namespace => "http://wixtoolset.org/schemas/v4/wxs/util";
40 + public override XNamespace Namespace => UtilConstants.Namespace;
41
42 /// <summary>
43 /// Types of Internet shortcuts.
@@ -94,13 +94,10 @@ namespace WixToolset.Util
94 var createFolderId = context["DirectoryId"];
95 var createFolderComponentId = context["ComponentId"];
96
97 - // If this doesn't parse successfully, something really odd is going on, so let the exception get thrown
98 - var createFolderWin64 = Boolean.Parse(context["Win64"]);
99 -
97 switch (element.Name.LocalName)
98 {
99 case "PermissionEx":
103 - this.ParsePermissionExElement(intermediate, section, element, createFolderId, createFolderComponentId, createFolderWin64, "CreateFolder");
100 + this.ParsePermissionExElement(intermediate, section, element, createFolderId, createFolderComponentId, "CreateFolder");
101 break;
102 default:
103 this.ParseHelper.UnexpectedElement(parentElement, element);
@@ -159,16 +156,13 @@ namespace WixToolset.Util
156 var fileId = context["FileId"];
157 var fileComponentId = context["ComponentId"];
158
162 - // If this doesn't parse successfully, something really odd is going on, so let the exception get thrown
163 - var fileWin64 = Boolean.Parse(context["Win64"]);
164 -
159 switch (element.Name.LocalName)
160 {
161 case "PerfCounter":
162 this.ParsePerfCounterElement(intermediate, section, element, fileComponentId, fileId);
163 break;
164 case "PermissionEx":
171 - this.ParsePermissionExElement(intermediate, section, element, fileId, fileComponentId, fileWin64, "File");
165 + this.ParsePermissionExElement(intermediate, section, element, fileId, fileComponentId, "File");
166 break;
167 case "PerfCounterManifest":
168 this.ParsePerfCounterManifestElement(intermediate, section, element, fileComponentId, fileId);
@@ -177,7 +171,7 @@ namespace WixToolset.Util
171 this.ParseEventManifestElement(intermediate, section, element, fileComponentId, fileId);
172 break;
173 case "FormatFile":
180 - this.ParseFormatFileElement(intermediate, section, element, fileId, fileWin64);
174 + this.ParseFormatFileElement(intermediate, section, element, fileId);
175 break;
176 default:
177 this.ParseHelper.UnexpectedElement(parentElement, element);
@@ -296,13 +290,10 @@ namespace WixToolset.Util
290 var registryId = context["RegistryId"];
291 var registryComponentId = context["ComponentId"];
292
299 - // If this doesn't parse successfully, something really odd is going on, so let the exception get thrown
300 - var registryWin64 = Boolean.Parse(context["Win64"]);
301 -
293 switch (element.Name.LocalName)
294 {
295 case "PermissionEx":
305 - this.ParsePermissionExElement(intermediate, section, element, registryId, registryComponentId, registryWin64, "Registry");
296 + this.ParsePermissionExElement(intermediate, section, element, registryId, registryComponentId, "Registry");
297 break;
298 default:
299 this.ParseHelper.UnexpectedElement(parentElement, element);
@@ -314,13 +305,10 @@ namespace WixToolset.Util
305 var serviceInstallName = context["ServiceInstallName"];
306 var serviceInstallComponentId = context["ServiceInstallComponentId"];
307
317 - // If this doesn't parse successfully, something really odd is going on, so let the exception get thrown
318 - var serviceInstallWin64 = Boolean.Parse(context["Win64"]);
319 -
308 switch (element.Name.LocalName)
309 {
310 case "PermissionEx":
323 - this.ParsePermissionExElement(intermediate, section, element, serviceInstallId, serviceInstallComponentId, serviceInstallWin64, "ServiceInstall");
311 + this.ParsePermissionExElement(intermediate, section, element, serviceInstallId, serviceInstallComponentId, "ServiceInstall");
312 break;
313 case "ServiceConfig":
314 this.ParseServiceConfigElement(intermediate, section, element, serviceInstallComponentId, "ServiceInstall", serviceInstallName);
@@ -536,7 +524,6 @@ namespace WixToolset.Util
524 private void ParseComponentSearchRefElement(Intermediate intermediate, IntermediateSection section, XElement element)
525 {
526 var sourceLineNumbers = this.ParseHelper.GetSourceLineNumbers(element);
539 - string refId = null;
527
528 foreach (var attrib in element.Attributes())
529 {
@@ -545,7 +532,7 @@ namespace WixToolset.Util
532 switch (attrib.Name.LocalName)
533 {
534 case "Id":
548 - refId = this.ParseHelper.GetAttributeIdentifierValue(sourceLineNumbers, attrib);
535 + var refId = this.ParseHelper.GetAttributeIdentifierValue(sourceLineNumbers, attrib);
536 this.ParseHelper.CreateSimpleReference(section, sourceLineNumbers, SymbolDefinitions.WixComponentSearch, refId);
537 break;
538 default:
@@ -2284,7 +2271,7 @@ namespace WixToolset.Util
2271 /// <param name="element">Element to parse.</param>
2272 /// <param name="fileId">Identifier of referenced file.</param>
2273 /// <param name="win64">Flag to determine whether the component is 64-bit.</param>
2287 - private void ParseFormatFileElement(Intermediate intermediate, IntermediateSection section, XElement element, string fileId, bool win64)
2274 + private void ParseFormatFileElement(Intermediate intermediate, IntermediateSection section, XElement element, string fileId)
2275 {
2276 var sourceLineNumbers = this.ParseHelper.GetSourceLineNumbers(element);
2277 string binaryId = null;
@@ -2415,7 +2402,6 @@ namespace WixToolset.Util
2402 ComponentRef = componentId,
2403 });
2404 }
2418 -
2405 }
2406
2407 this.ParseHelper.CreateCustomActionReference(sourceLineNumbers, section, "Wix4ConfigureEventManifestRegister", this.Context.Platform, CustomActionPlatforms.X86 | CustomActionPlatforms.X64 | CustomActionPlatforms.ARM64);
@@ -2435,7 +2421,7 @@ namespace WixToolset.Util
2421 /// <param name="componentId">Identifier of component, used to determine install state.</param>
2422 /// <param name="win64">Flag to determine whether the component is 64-bit.</param>
2423 /// <param name="tableName">Name of table that contains objectId.</param>
2438 - private void ParsePermissionExElement(Intermediate intermediate, IntermediateSection section, XElement element, string objectId, string componentId, bool win64, string tableName)
2424 + private void ParsePermissionExElement(Intermediate intermediate, IntermediateSection section, XElement element, string objectId, string componentId, string tableName)
2425 {
2426 var sourceLineNumbers = this.ParseHelper.GetSourceLineNumbers(element);
2427 var bits = new BitArray(32);
@@ -2623,7 +2609,7 @@ namespace WixToolset.Util
2609
2610 if (null == id)
2611 {
2626 - id = this.ParseHelper.CreateIdentifier("wps", variable, condition, after, (productCode == null ? upgradeCode : productCode), attributes.ToString());
2612 + id = this.ParseHelper.CreateIdentifier("wps", variable, condition, after, productCode ?? upgradeCode, attributes.ToString());
2613 }
2614
2615 this.ParseHelper.ParseForExtensionElements(this.Context.Extensions, intermediate, section, element);
src/ext/Util/wixext/UtilConstants.cs
+21
@@ -2,11 +2,32 @@
2
3 namespace WixToolset.Util
4 {
5 + using System.Xml.Linq;
6 +
7 /// <summary>
8 /// Constants used by Utility Extension.
9 /// </summary>
10 internal static class UtilConstants
11 {
12 + internal static readonly XNamespace Namespace = "http://wixtoolset.org/schemas/v4/wxs/util";
13 +
14 + internal static readonly XName CloseApplicationName = Namespace + "CloseApplication";
15 + internal static readonly XName EventManifestName = Namespace + "EventManifest";
16 + internal static readonly XName FileShareName = Namespace + "FileShare";
17 + internal static readonly XName FileSharePermissionName = Namespace + "FileSharePermission";
18 + internal static readonly XName GroupName = Namespace + "Group";
19 + internal static readonly XName GroupRefName = Namespace + "GroupRef";
20 + internal static readonly XName InternetShortcutName = Namespace + "InternetShortcut";
21 + internal static readonly XName PerfCounterName = Namespace + "PerfCounter";
22 + internal static readonly XName PerfCounterManifestName = Namespace + "PerfCounterManifest";
23 + internal static readonly XName PermissionExName = Namespace + "PermissionEx";
24 + internal static readonly XName RemoveFolderExName = Namespace + "RemoveFolderEx";
25 + internal static readonly XName RestartResourceName = Namespace + "RestartResource";
26 + internal static readonly XName ServiceConfigName = Namespace + "ServiceConfig";
27 + internal static readonly XName UserName = Namespace + "User";
28 + internal static readonly XName XmlConfigName = Namespace + "XmlConfig";
29 + internal static readonly XName XmlFileName = Namespace + "XmlFile";
30 +
31 internal static readonly string[] FilePermissions = { "Read", "Write", "Append", "ReadExtendedAttributes", "WriteExtendedAttributes", "Execute", null, "ReadAttributes", "WriteAttributes" };
32 internal static readonly string[] FolderPermissions = { "Read", "CreateFile", "CreateChild", "ReadExtendedAttributes", "WriteExtendedAttributes", "Traverse", "DeleteChild", "ReadAttributes", "WriteAttributes" };
33 internal static readonly string[] GenericPermissions = { "GenericAll", "GenericExecute", "GenericWrite", "GenericRead" };
src/ext/Util/wixext/UtilDecompiler.cs
+456 -816
@@ -1,48 +1,32 @@
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.Extensions
3 +namespace WixToolset.Util
4 {
5 -#if TODO_CONSIDER_DECOMPILER
5 using System;
6 using System.IO;
7 using System.Text;
8 using System.Collections;
9 using System.Diagnostics;
11 - using System.Globalization;
10
13 - using Util = WixToolset.Extensions.Serialize.Util;
11 using WixToolset.Data;
12 using WixToolset.Extensibility;
16 - using Wix = WixToolset.Data.Serialize;
13 + using WixToolset.Data.WindowsInstaller;
14 + using System.Collections.Generic;
15 + using System.Xml.Linq;
16 + using WixToolset.Util.Symbols;
17
18 /// <summary>
19 /// The decompiler for the WiX Toolset Utility Extension.
20 /// </summary>
21 - public sealed class UtilDecompiler : DecompilerExtension
21 + internal sealed class UtilDecompiler : BaseWindowsInstallerDecompilerExtension
22 {
23 - /// <summary>
24 - /// Creates a decompiler for Utility Extension.
25 - /// </summary>
26 - public UtilDecompiler()
27 - {
28 - this.TableDefinitions = UtilExtensionData.GetExtensionTableDefinitions();
29 - }
30 -
31 - /// <summary>
32 - /// Get the extensions library to be removed.
33 - /// </summary>
34 - /// <param name="tableDefinitions">Table definitions for library.</param>
35 - /// <returns>Library to remove from decompiled output.</returns>
36 - public override Library GetLibraryToRemove(TableDefinitionCollection tableDefinitions)
37 - {
38 - return UtilExtensionData.GetExtensionLibrary(tableDefinitions);
39 - }
23 + public override IReadOnlyCollection<TableDefinition> TableDefinitions => UtilTableDefinitions.All;
24
25 /// <summary>
26 /// Called at the beginning of the decompilation of a database.
27 /// </summary>
28 /// <param name="tables">The collection of all tables.</param>
45 - public override void Initialize(TableIndexedCollection tables)
29 + public override void PreDecompileTables(TableIndexedCollection tables)
30 {
31 this.CleanupSecureCustomProperties(tables);
32 this.CleanupInternetShortcutRemoveFileTables(tables);
@@ -61,24 +45,22 @@ namespace WixToolset.Extensions
45 /// <param name="tables">The collection of all tables.</param>
46 private void CleanupSecureCustomProperties(TableIndexedCollection tables)
47 {
64 - Table propertyTable = tables["Property"];
48 + var propertyTable = tables["Property"];
49
50 if (null != propertyTable)
51 {
68 - foreach (Row row in propertyTable.Rows)
52 + foreach (var row in propertyTable.Rows)
53 {
54 if ("SecureCustomProperties" == row[0].ToString())
55 {
72 - StringBuilder remainingProperties = new StringBuilder();
73 - string[] secureCustomProperties = row[1].ToString().Split(';');
74 - foreach (string property in secureCustomProperties)
56 + var remainingProperties = new StringBuilder();
57 + var secureCustomProperties = row[1].ToString().Split(';');
58 + foreach (var property in secureCustomProperties)
59 {
60 if (property.StartsWith("WIX_SUITE_", StringComparison.Ordinal) || property.StartsWith("WIX_DIR_", StringComparison.Ordinal)
61 || property.StartsWith("WIX_ACCOUNT_", StringComparison.Ordinal))
62 {
79 - Wix.PropertyRef propertyRef = new Wix.PropertyRef();
80 - propertyRef.Id = property;
81 - this.Core.RootElement.AddChild(propertyRef);
63 + this.DecompilerHelper.AddElementToRoot("PropertyRef", new XAttribute("Id", property));
64 }
65 else
66 {
@@ -104,21 +86,21 @@ namespace WixToolset.Extensions
86 private void CleanupInternetShortcutRemoveFileTables(TableIndexedCollection tables)
87 {
88 // index the WixInternetShortcut table
107 - Table wixInternetShortcutTable = tables["WixInternetShortcut"];
108 - Hashtable wixInternetShortcuts = new Hashtable();
89 + var wixInternetShortcutTable = tables["WixInternetShortcut"];
90 + var wixInternetShortcuts = new Hashtable();
91 if (null != wixInternetShortcutTable)
92 {
111 - foreach (Row row in wixInternetShortcutTable.Rows)
93 + foreach (var row in wixInternetShortcutTable.Rows)
94 {
113 - wixInternetShortcuts.Add(row.GetPrimaryKey(DecompilerConstants.PrimaryKeyDelimiter), row);
95 + wixInternetShortcuts.Add(row.GetPrimaryKey(), row);
96 }
97 }
98
99 // remove the RemoveFile rows with primary keys that match the WixInternetShortcut table's
118 - Table removeFileTable = tables["RemoveFile"];
100 + var removeFileTable = tables["RemoveFile"];
101 if (null != removeFileTable)
102 {
121 - for (int i = removeFileTable.Rows.Count - 1; 0 <= i; i--)
103 + for (var i = removeFileTable.Rows.Count - 1; 0 <= i; i--)
104 {
105 if (null != wixInternetShortcuts[removeFileTable.Rows[i][0]])
106 {
@@ -132,69 +114,86 @@ namespace WixToolset.Extensions
114 /// Decompiles an extension table.
115 /// </summary>
116 /// <param name="table">The table to decompile.</param>
135 - public override void DecompileTable(Table table)
117 + public override bool TryDecompileTable(Table table)
118 {
119 switch (table.Name)
120 {
121 case "WixCloseApplication":
122 + case "Wix4CloseApplication":
123 this.DecompileWixCloseApplicationTable(table);
124 break;
125 case "WixRemoveFolderEx":
126 + case "Wix4RemoveFolderEx":
127 this.DecompileWixRemoveFolderExTable(table);
128 break;
129 case "WixRestartResource":
130 + case "Wix4RestartResource":
131 this.DecompileWixRestartResourceTable(table);
132 break;
133 case "FileShare":
134 + case "Wix4FileShare":
135 this.DecompileFileShareTable(table);
136 break;
137 case "FileSharePermissions":
138 + case "Wix4FileSharePermissions":
139 this.DecompileFileSharePermissionsTable(table);
140 break;
141 case "WixInternetShortcut":
142 + case "Wix4InternetShortcut":
143 this.DecompileWixInternetShortcutTable(table);
144 break;
145 case "Group":
146 + case "Wix4Group":
147 this.DecompileGroupTable(table);
148 break;
149 case "Perfmon":
150 + case "Wix4Perfmon":
151 this.DecompilePerfmonTable(table);
152 break;
153 case "PerfmonManifest":
154 + case "Wix4PerfmonManifest":
155 this.DecompilePerfmonManifestTable(table);
156 break;
157 case "EventManifest":
158 + case "Wix4EventManifest":
159 this.DecompileEventManifestTable(table);
160 break;
161 case "SecureObjects":
162 + case "Wix4SecureObjects":
163 this.DecompileSecureObjectsTable(table);
164 break;
165 case "ServiceConfig":
166 + case "Wix4ServiceConfig":
167 this.DecompileServiceConfigTable(table);
168 break;
169 case "User":
170 + case "Wix4User":
171 this.DecompileUserTable(table);
172 break;
173 case "UserGroup":
174 + case "Wix4UserGroup":
175 this.DecompileUserGroupTable(table);
176 break;
177 case "XmlConfig":
178 + case "Wix4XmlConfig":
179 this.DecompileXmlConfigTable(table);
180 break;
181 case "XmlFile":
182 + case "Wix4XmlFile":
183 // XmlFile decompilation has been moved to FinalizeXmlFileTable function
184 break;
185 default:
188 - base.DecompileTable(table);
189 - break;
186 + return false;
187 }
188 +
189 + return true;
190 }
191
192 /// <summary>
193 /// Finalize decompilation.
194 /// </summary>
195 /// <param name="tables">The collection of all tables.</param>
197 - public override void Finish(TableIndexedCollection tables)
196 + public override void PostDecompileTables(TableIndexedCollection tables)
197 {
198 this.FinalizePerfmonTable(tables);
199 this.FinalizePerfmonManifestTable(tables);
@@ -211,49 +210,21 @@ namespace WixToolset.Extensions
210 /// <param name="table">The table to decompile.</param>
211 private void DecompileWixCloseApplicationTable(Table table)
212 {
214 - foreach (Row row in table.Rows)
213 + foreach (var row in table.Rows)
214 {
216 - Util.CloseApplication closeApplication = new Util.CloseApplication();
217 -
218 - closeApplication.Id = (string)row[0];
219 -
220 - closeApplication.Target = (string)row[1];
221 -
222 - if (null != row[2])
223 - {
224 - closeApplication.Description = (string)row[2];
225 - }
226 -
227 - if (null != row[3])
228 - {
229 - closeApplication.Content = (string)row[3];
230 - }
231 -
232 - // set defaults
233 - closeApplication.CloseMessage = Util.YesNoType.no;
234 - closeApplication.RebootPrompt = Util.YesNoType.yes;
235 - closeApplication.ElevatedCloseMessage = Util.YesNoType.no;
236 -
237 - if (null != row[4])
238 - {
239 - int attribute = (int)row[4];
240 -
241 - closeApplication.CloseMessage = (0x1 == (attribute & 0x1)) ? Util.YesNoType.yes : Util.YesNoType.no;
242 - closeApplication.RebootPrompt = (0x2 == (attribute & 0x2)) ? Util.YesNoType.yes : Util.YesNoType.no;
243 - closeApplication.ElevatedCloseMessage = (0x4 == (attribute & 0x4)) ? Util.YesNoType.yes : Util.YesNoType.no;
244 - }
245 -
246 - if (null != row[5])
247 - {
248 - closeApplication.Sequence = (int)row[5];
249 - }
250 -
251 - if (null != row[6])
252 - {
253 - closeApplication.Property = (string)row[6];
254 - }
255 -
256 - this.Core.RootElement.AddChild(closeApplication);
215 + var attribute = row.FieldAsNullableInteger(4) ?? 0x2;
216 +
217 + this.DecompilerHelper.AddElementToRoot(UtilConstants.CloseApplicationName,
218 + new XAttribute("Id", row.FieldAsString(0)),
219 + new XAttribute("Target", row.FieldAsString(1)),
220 + AttributeIfNotNull("Description", row, 2),
221 + AttributeIfNotNull("Content", row, 3),
222 + AttributeIfNotNull("CloseMessage", 0x1 == (attribute & 0x1)),
223 + AttributeIfNotNull("RebootPrompt", 0x2 == (attribute & 0x2)),
224 + AttributeIfNotNull("ElevatedCloseMessage", 0x4 == (attribute & 0x4)),
225 + NumericAttributeIfNotNull("Sequence", row, 5),
226 + NumericAttributeIfNotNull("Property", row, 6)
227 + );
228 }
229 }
230
@@ -263,43 +234,45 @@ namespace WixToolset.Extensions
234 /// <param name="table">The table to decompile.</param>
235 private void DecompileWixRemoveFolderExTable(Table table)
236 {
266 - foreach (Row row in table.Rows)
237 + foreach (var row in table.Rows)
238 {
268 - // Set the Id even if auto-generated previously.
269 - Util.RemoveFolderEx removeFolder = new Util.RemoveFolderEx();
270 - removeFolder.Id = (string)row[0];
271 - removeFolder.Property = (string)row[2];
272 -
273 - int installMode = (int)row[3];
274 - switch ((UtilCompiler.WixRemoveFolderExOn)installMode)
239 + var on = String.Empty;
240 + var installMode = row.FieldAsInteger(3);
241 + switch (installMode)
242 {
276 - case UtilCompiler.WixRemoveFolderExOn.Install:
277 - removeFolder.On = Util.RemoveFolderEx.OnType.install;
243 + case (int)WixRemoveFolderExInstallMode.Install:
244 + on = "install";
245 break;
279 -
280 - case UtilCompiler.WixRemoveFolderExOn.Uninstall:
281 - removeFolder.On = Util.RemoveFolderEx.OnType.uninstall;
246 +
247 + case (int)WixRemoveFolderExInstallMode.Uninstall:
248 + on = "uninstall";
249 break;
283 -
284 - case UtilCompiler.WixRemoveFolderExOn.Both:
285 - removeFolder.On = Util.RemoveFolderEx.OnType.both;
250 +
251 + case (int)WixRemoveFolderExInstallMode.Both:
252 + on = "both";
253 break;
254
255 default:
289 - this.Core.OnMessage(WixWarnings.UnrepresentableColumnValue(row.SourceLineNumbers, table.Name, "InstallMode", installMode));
256 + this.Messaging.Write(WarningMessages.UnrepresentableColumnValue(row.SourceLineNumbers, table.Name, "InstallMode", installMode));
257 break;
258 }
259
260 + var removeFolder = new XElement(UtilConstants.RemoveFolderExName,
261 + AttributeIfNotNull("Id", row, 0),
262 + AttributeIfNotNull("Property", row, 2),
263 + AttributeIfNotNull("On", on)
264 + );
265 +
266 // Add to the appropriate Component or section element.
294 - string componentId = (string)row[1];
295 - Wix.Component component = (Wix.Component)this.Core.GetIndexedElement("Component", componentId);
296 - if (null != component)
267 + var componentId = row.FieldAsString(1);
268 +
269 + if (this.DecompilerHelper.TryGetIndexedElement("Component", componentId, out var component))
270 {
298 - component.AddChild(removeFolder);
271 + component.Add(removeFolder);
272 }
273 else
274 {
302 - this.Core.OnMessage(WixWarnings.ExpectedForeignRow(row.SourceLineNumbers, table.Name, row.GetPrimaryKey(DecompilerConstants.PrimaryKeyDelimiter), "Component_", componentId, "Component"));
275 + this.Messaging.Write(WarningMessages.ExpectedForeignRow(row.SourceLineNumbers, table.Name, row.GetPrimaryKey(), "Component_", componentId, "Component"));
276 }
277 }
278 }
@@ -310,53 +283,51 @@ namespace WixToolset.Extensions
283 /// <param name="table">The table to decompile.</param>
284 private void DecompileWixRestartResourceTable(Table table)
285 {
313 - foreach (Row row in table.Rows)
286 + foreach (var row in table.Rows)
287 {
315 - // Set the Id even if auto-generated previously.
316 - Util.RestartResource restartResource = new Util.RestartResource();
317 - restartResource.Id = (string)row[0];
288 + var restartResource = new XElement(UtilConstants.RestartResourceName,
289 + new XAttribute("Id", row.FieldAsString(0)));
290
291 // Determine the resource type and set accordingly.
320 - string resource = (string)row[2];
321 - int attributes = (int)row[3];
322 - UtilCompiler.WixRestartResourceAttributes type = (UtilCompiler.WixRestartResourceAttributes)(attributes & (int)UtilCompiler.WixRestartResourceAttributes.TypeMask);
292 + var resource = row.FieldAsString(2);
293 + var attributes = row.FieldAsInteger(3);
294 + var type = (WixRestartResourceAttributes)attributes;
295
296 switch (type)
297 {
326 - case UtilCompiler.WixRestartResourceAttributes.Filename:
327 - restartResource.Path = resource;
298 + case WixRestartResourceAttributes.Filename:
299 + restartResource.Add(new XAttribute("Path", resource));
300 break;
301
330 - case UtilCompiler.WixRestartResourceAttributes.ProcessName:
331 - restartResource.ProcessName = resource;
302 + case WixRestartResourceAttributes.ProcessName:
303 + restartResource.Add(new XAttribute("ProcessName", resource));
304 break;
305
334 - case UtilCompiler.WixRestartResourceAttributes.ServiceName:
335 - restartResource.ServiceName = resource;
306 + case WixRestartResourceAttributes.ServiceName:
307 + restartResource.Add(new XAttribute("ServiceName", resource));
308 break;
309
310 default:
339 - this.Core.OnMessage(WixWarnings.UnrepresentableColumnValue(row.SourceLineNumbers, table.Name, "Attributes", attributes));
311 + this.Messaging.Write(WarningMessages.UnrepresentableColumnValue(row.SourceLineNumbers, table.Name, "Attributes", attributes));
312 break;
313 }
314
315 // Add to the appropriate Component or section element.
344 - string componentId = (string)row[1];
316 + var componentId = row.FieldAsString(1);
317 if (!String.IsNullOrEmpty(componentId))
318 {
347 - Wix.Component component = (Wix.Component)this.Core.GetIndexedElement("Component", componentId);
348 - if (null != component)
319 + if (this.DecompilerHelper.TryGetIndexedElement("Component", componentId, out var component))
320 {
350 - component.AddChild(restartResource);
321 + component.Add(restartResource);
322 }
323 else
324 {
354 - this.Core.OnMessage(WixWarnings.ExpectedForeignRow(row.SourceLineNumbers, table.Name, row.GetPrimaryKey(DecompilerConstants.PrimaryKeyDelimiter), "Component_", componentId, "Component"));
325 + this.Messaging.Write(WarningMessages.ExpectedForeignRow(row.SourceLineNumbers, table.Name, row.GetPrimaryKey(), "Component_", componentId, "Component"));
326 }
327 }
328 else
329 {
359 - this.Core.RootElement.AddChild(restartResource);
330 + this.DecompilerHelper.AddElementToRoot(restartResource);
331 }
332 }
333 }
@@ -367,33 +338,28 @@ namespace WixToolset.Extensions
338 /// <param name="table">The table to decompile.</param>
339 private void DecompileFileShareTable(Table table)
340 {
370 - foreach (Row row in table.Rows)
341 + foreach (var row in table.Rows)
342 {
372 - Util.FileShare fileShare = new Util.FileShare();
373 -
374 - fileShare.Id = (string)row[0];
375 -
376 - fileShare.Name = (string)row[1];
377 -
378 - if (null != row[3])
379 - {
380 - fileShare.Description = (string)row[3];
381 - }
343 + var fileShare = new XElement(UtilConstants.FileShareName,
344 + new XAttribute("Id", row.FieldAsString(0)),
345 + new XAttribute("Name", row.FieldAsString(1)),
346 + AttributeIfNotNull("Description", row, 3)
347 + );
348
349 // the Directory_ column is set by the parent Component
350
351 // the User_ and Permissions columns are deprecated
352
387 - Wix.Component component = (Wix.Component)this.Core.GetIndexedElement("Component", (string)row[2]);
388 - if (null != component)
353 + if (this.DecompilerHelper.TryGetIndexedElement("Component", row.FieldAsString(2), out var component))
354 {
390 - component.AddChild(fileShare);
355 + component.Add(fileShare);
356 }
357 else
358 {
394 - this.Core.OnMessage(WixWarnings.ExpectedForeignRow(row.SourceLineNumbers, table.Name, row.GetPrimaryKey(DecompilerConstants.PrimaryKeyDelimiter), "Component_", (string)row[2], "Component"));
359 + this.Messaging.Write(WarningMessages.ExpectedForeignRow(row.SourceLineNumbers, table.Name, row.GetPrimaryKey(), "Component_", (string)row[2], "Component"));
360 }
396 - this.Core.IndexElement(row, fileShare);
361 +
362 + this.DecompilerHelper.IndexElement(row, fileShare);
363 }
364 }
365
@@ -403,111 +369,21 @@ namespace WixToolset.Extensions
369 /// <param name="table">The table to decompile.</param>
370 private void DecompileFileSharePermissionsTable(Table table)
371 {
406 - foreach (Row row in table.Rows)
372 + foreach (var row in table.Rows)
373 {
408 - Util.FileSharePermission fileSharePermission = new Util.FileSharePermission();
409 -
410 - fileSharePermission.User = (string)row[1];
411 -
412 - string[] specialPermissions = UtilConstants.FolderPermissions;
413 - int permissions = (int)row[2];
414 - for (int i = 0; i < 32; i++)
415 - {
416 - if (0 != ((permissions >> i) & 1))
417 - {
418 - string name = null;
374 + var fileSharePermission = new XElement(UtilConstants.FileSharePermissionName,
375 + new XAttribute("User", row.FieldAsString(1)));
376
420 - if (16 > i && specialPermissions.Length > i)
421 - {
422 - name = specialPermissions[i];
423 - }
424 - else if (28 > i && UtilConstants.StandardPermissions.Length > (i - 16))
425 - {
426 - name = UtilConstants.StandardPermissions[i - 16];
427 - }
428 - else if (0 <= (i - 28) && UtilConstants.GenericPermissions.Length > (i - 28))
429 - {
430 - name = UtilConstants.GenericPermissions[i - 28];
431 - }
432 -
433 - if (null == name)
434 - {
435 - this.Core.OnMessage(WixWarnings.UnknownPermission(row.SourceLineNumbers, row.Table.Name, row.GetPrimaryKey(DecompilerConstants.PrimaryKeyDelimiter), i));
436 - }
437 - else
438 - {
439 - switch (name)
440 - {
441 - case "ChangePermission":
442 - fileSharePermission.ChangePermission = Util.YesNoType.yes;
443 - break;
444 - case "CreateChild":
445 - fileSharePermission.CreateChild = Util.YesNoType.yes;
446 - break;
447 - case "CreateFile":
448 - fileSharePermission.CreateFile = Util.YesNoType.yes;
449 - break;
450 - case "Delete":
451 - fileSharePermission.Delete = Util.YesNoType.yes;
452 - break;
453 - case "DeleteChild":
454 - fileSharePermission.DeleteChild = Util.YesNoType.yes;
455 - break;
456 - case "GenericAll":
457 - fileSharePermission.GenericAll = Util.YesNoType.yes;
458 - break;
459 - case "GenericExecute":
460 - fileSharePermission.GenericExecute = Util.YesNoType.yes;
461 - break;
462 - case "GenericRead":
463 - fileSharePermission.GenericRead = Util.YesNoType.yes;
464 - break;
465 - case "GenericWrite":
466 - fileSharePermission.GenericWrite = Util.YesNoType.yes;
467 - break;
468 - case "Read":
469 - fileSharePermission.Read = Util.YesNoType.yes;
470 - break;
471 - case "ReadAttributes":
472 - fileSharePermission.ReadAttributes = Util.YesNoType.yes;
473 - break;
474 - case "ReadExtendedAttributes":
475 - fileSharePermission.ReadExtendedAttributes = Util.YesNoType.yes;
476 - break;
477 - case "ReadPermission":
478 - fileSharePermission.ReadPermission = Util.YesNoType.yes;
479 - break;
480 - case "Synchronize":
481 - fileSharePermission.Synchronize = Util.YesNoType.yes;
482 - break;
483 - case "TakeOwnership":
484 - fileSharePermission.TakeOwnership = Util.YesNoType.yes;
485 - break;
486 - case "Traverse":
487 - fileSharePermission.Traverse = Util.YesNoType.yes;
488 - break;
489 - case "WriteAttributes":
490 - fileSharePermission.WriteAttributes = Util.YesNoType.yes;
491 - break;
492 - case "WriteExtendedAttributes":
493 - fileSharePermission.WriteExtendedAttributes = Util.YesNoType.yes;
494 - break;
495 - default:
496 - Debug.Fail(String.Format("Unknown permission '{0}'.", name));
497 - break;
498 - }
499 - }
500 - }
501 - }
377 + this.AddPermissionAttributes(fileSharePermission, row, 2, UtilConstants.FolderPermissions);
378
503 - Util.FileShare fileShare = (Util.FileShare)this.Core.GetIndexedElement("FileShare", (string)row[0]);
504 - if (null != fileShare)
379 + if (this.DecompilerHelper.TryGetIndexedElement("Wix4FileShare", row.FieldAsString(0), out var fileShare) ||
380 + this.DecompilerHelper.TryGetIndexedElement("FileShare", row.FieldAsString(0), out fileShare))
381 {
506 - fileShare.AddChild(fileSharePermission);
382 + fileShare.Add(fileSharePermission);
383 }
384 else
385 {
510 - this.Core.OnMessage(WixWarnings.ExpectedForeignRow(row.SourceLineNumbers, table.Name, row.GetPrimaryKey(DecompilerConstants.PrimaryKeyDelimiter), "FileShare_", (string)row[0], "FileShare"));
386 + this.Messaging.Write(WarningMessages.ExpectedForeignRow(row.SourceLineNumbers, table.Name, row.GetPrimaryKey(), "FileShare_", (string)row[0], "Wix4FileShare"));
387 }
388 }
389 }
@@ -518,25 +394,18 @@ namespace WixToolset.Extensions
394 /// <param name="table">The table to decompile.</param>
395 private void DecompileGroupTable(Table table)
396 {
521 - foreach (Row row in table.Rows)
397 + foreach (var row in table.Rows)
398 {
523 - Util.Group group = new Util.Group();
524 -
525 - group.Id = (string)row[0];
526 -
399 if (null != row[1])
400 {
529 - this.Core.OnMessage(WixWarnings.UnrepresentableColumnValue(row.SourceLineNumbers, table.Name, "Component_", (string)row[1]));
401 + this.Messaging.Write(WarningMessages.UnrepresentableColumnValue(row.SourceLineNumbers, table.Name, "Component_", (string)row[1]));
402 }
403
532 - group.Name = (string)row[2];
533 -
534 - if (null != row[3])
535 - {
536 - group.Domain = (string)row[3];
537 - }
538 -
539 - this.Core.RootElement.AddChild(group);
404 + this.DecompilerHelper.AddElementToRoot(UtilConstants.GroupName,
405 + new XAttribute("Id", row.FieldAsString(0)),
406 + new XAttribute("Name", row.FieldAsString(1)),
407 + AttributeIfNotNull("Domain", row, 3)
408 + );
409 }
410 }
411
@@ -546,39 +415,41 @@ namespace WixToolset.Extensions
415 /// <param name="table">The table to decompile.</param>
416 private void DecompileWixInternetShortcutTable(Table table)
417 {
549 - foreach (Row row in table.Rows)
418 + foreach (var row in table.Rows)
419 {
551 - Util.InternetShortcut internetShortcut = new Util.InternetShortcut();
552 - internetShortcut.Id = (string)row[0];
553 - internetShortcut.Directory = (string)row[2];
554 - // remove .lnk/.url extension because compiler extension adds it back for us
555 - internetShortcut.Name = Path.ChangeExtension((string)row[3], null);
556 - internetShortcut.Target = (string)row[4];
557 - internetShortcut.IconFile = (string)row[6];
558 - internetShortcut.IconIndex = (int)row[7];
559 -
560 - UtilCompiler.InternetShortcutType shortcutType = (UtilCompiler.InternetShortcutType)row[5];
420 + var type = String.Empty;
421 + var shortcutType = (UtilCompiler.InternetShortcutType)row.FieldAsInteger(5);
422 switch (shortcutType)
423 {
424 case UtilCompiler.InternetShortcutType.Link:
564 - internetShortcut.Type = Util.InternetShortcut.TypeType.link;
425 + type = "link";
426 break;
427 case UtilCompiler.InternetShortcutType.Url:
567 - internetShortcut.Type = Util.InternetShortcut.TypeType.url;
428 + type = "url";
429 break;
430 }
431
571 - Wix.Component component = (Wix.Component)this.Core.GetIndexedElement("Component", (string)row[1]);
572 - if (null != component)
432 + var internetShortcut = new XElement(UtilConstants.InternetShortcutName,
433 + new XAttribute("Id", row.FieldAsString(0)),
434 + new XAttribute("Directory", row.FieldAsString(2)),
435 + new XAttribute("Name", Path.GetFileNameWithoutExtension(row.FieldAsString(3))), // remove .lnk/.url extension because compiler extension adds it back for us
436 + new XAttribute("Type", type),
437 + new XAttribute("Target", row.FieldAsString(4)),
438 + new XAttribute("IconFile", row.FieldAsString(6)),
439 + NumericAttributeIfNotNull("IconIndex", row, 7)
440 + );
441 +
442 + var componentId = row.FieldAsString(1);
443 + if (this.DecompilerHelper.TryGetIndexedElement("Component", componentId, out var component))
444 {
574 - component.AddChild(internetShortcut);
445 + component.Add(internetShortcut);
446 }
447 else
448 {
578 - this.Core.OnMessage(WixWarnings.ExpectedForeignRow(row.SourceLineNumbers, table.Name, row.GetPrimaryKey(DecompilerConstants.PrimaryKeyDelimiter), "Component_", (string)row[1], "Component"));
449 + this.Messaging.Write(WarningMessages.ExpectedForeignRow(row.SourceLineNumbers, table.Name, row.GetPrimaryKey(), "Component_", componentId, "Component"));
450 }
451
581 - this.Core.IndexElement(row, internetShortcut);
452 + this.DecompilerHelper.IndexElement(row, internetShortcut);
453 }
454 }
455
@@ -588,54 +459,48 @@ namespace WixToolset.Extensions
459 /// <param name="table">The table to decompile.</param>
460 private void DecompilePerfmonTable(Table table)
461 {
591 - foreach (Row row in table.Rows)
462 + foreach (var row in table.Rows)
463 {
593 - Util.PerfCounter perfCounter = new Util.PerfCounter();
594 -
595 - perfCounter.Name = (string)row[2];
596 -
597 - this.Core.IndexElement(row, perfCounter);
464 + this.DecompilerHelper.IndexElement(row, new XElement(UtilConstants.PerfCounterName, new XAttribute("Name", row.FieldAsString(2))));
465 }
466 }
600 -
467 +
468 /// <summary>
469 /// Decompile the PerfmonManifest table.
470 /// </summary>
471 /// <param name="table">The table to decompile.</param>
472 private void DecompilePerfmonManifestTable(Table table)
473 {
607 - foreach (Row row in table.Rows)
474 + foreach (var row in table.Rows)
475 {
609 - Util.PerfCounterManifest perfCounterManifest = new Util.PerfCounterManifest();
610 -
611 - perfCounterManifest.ResourceFileDirectory = (string)row[2];
612 -
613 - this.Core.IndexElement(row, perfCounterManifest);
476 + this.DecompilerHelper.IndexElement(row, new XElement(UtilConstants.PerfCounterManifestName, new XAttribute("ResourceFileDirectory", row.FieldAsString(2))));
477 }
478 }
616 -
479 +
480 /// <summary>
481 /// Decompile the EventManifest table.
482 /// </summary>
483 /// <param name="table">The table to decompile.</param>
484 private void DecompileEventManifestTable(Table table)
485 {
623 - foreach (Row row in table.Rows)
486 + foreach (var row in table.Rows)
487 {
625 - Util.EventManifest eventManifest = new Util.EventManifest();
626 - this.Core.IndexElement(row, eventManifest);
488 + this.DecompilerHelper.IndexElement(row, new XElement(UtilConstants.EventManifestName));
489 }
490 }
629 -
491 +
492 /// <summary>
493 /// Decompile the SecureObjects table.
494 /// </summary>
495 /// <param name="table">The table to decompile.</param>
496 private void DecompileSecureObjectsTable(Table table)
497 {
636 - foreach (Row row in table.Rows)
498 + foreach (var row in table.Rows)
499 {
638 - Util.PermissionEx permissionEx = new Util.PermissionEx();
500 + var permissionEx = new XElement(UtilConstants.PermissionExName,
501 + AttributeIfNotNull("Domain", row, 2),
502 + AttributeIfNotNull("User", row, 3)
503 + );
504
505 string[] specialPermissions;
506 switch ((string)row[1])
@@ -653,155 +518,13 @@ namespace WixToolset.Extensions
518 specialPermissions = UtilConstants.ServicePermissions;
519 break;
520 default:
656 - this.Core.OnMessage(WixWarnings.IllegalColumnValue(row.SourceLineNumbers, row.Table.Name, row.Fields[1].Column.Name, row[1]));
521 + this.Messaging.Write(WarningMessages.IllegalColumnValue(row.SourceLineNumbers, row.Table.Name, row.Fields[1].Column.Name, row[1]));
522 return;
523 }
524
660 - int permissionBits = (int)row[4];
661 - for (int i = 0; i < 32; i++)
662 - {
663 - if (0 != ((permissionBits >> i) & 1))
664 - {
665 - string name = null;
525 + this.AddPermissionAttributes(permissionEx, row, 4, specialPermissions);
526
667 - if (16 > i && specialPermissions.Length > i)
668 - {
669 - name = specialPermissions[i];
670 - }
671 - else if (28 > i && UtilConstants.StandardPermissions.Length > (i - 16))
672 - {
673 - name = UtilConstants.StandardPermissions[i - 16];
674 - }
675 - else if (0 <= (i - 28) && UtilConstants.GenericPermissions.Length > (i - 28))
676 - {
677 - name = UtilConstants.GenericPermissions[i - 28];
678 - }
679 -
680 - if (null == name)
681 - {
682 - this.Core.OnMessage(WixWarnings.UnknownPermission(row.SourceLineNumbers, row.Table.Name, row.GetPrimaryKey(DecompilerConstants.PrimaryKeyDelimiter), i));
683 - }
684 - else
685 - {
686 - switch (name)
687 - {
688 - case "Append":
689 - permissionEx.Append = Util.YesNoType.yes;
690 - break;
691 - case "ChangePermission":
692 - permissionEx.ChangePermission = Util.YesNoType.yes;
693 - break;
694 - case "CreateChild":
695 - permissionEx.CreateChild = Util.YesNoType.yes;
696 - break;
697 - case "CreateFile":
698 - permissionEx.CreateFile = Util.YesNoType.yes;
699 - break;
700 - case "CreateLink":
701 - permissionEx.CreateLink = Util.YesNoType.yes;
702 - break;
703 - case "CreateSubkeys":
704 - permissionEx.CreateSubkeys = Util.YesNoType.yes;
705 - break;
706 - case "Delete":
707 - permissionEx.Delete = Util.YesNoType.yes;
708 - break;
709 - case "DeleteChild":
710 - permissionEx.DeleteChild = Util.YesNoType.yes;
711 - break;
712 - case "EnumerateSubkeys":
713 - permissionEx.EnumerateSubkeys = Util.YesNoType.yes;
714 - break;
715 - case "Execute":
716 - permissionEx.Execute = Util.YesNoType.yes;
717 - break;
718 - case "GenericAll":
719 - permissionEx.GenericAll = Util.YesNoType.yes;
720 - break;
721 - case "GenericExecute":
722 - permissionEx.GenericExecute = Util.YesNoType.yes;
723 - break;
724 - case "GenericRead":
725 - permissionEx.GenericRead = Util.YesNoType.yes;
726 - break;
727 - case "GenericWrite":
728 - permissionEx.GenericWrite = Util.YesNoType.yes;
729 - break;
730 - case "Notify":
731 - permissionEx.Notify = Util.YesNoType.yes;
732 - break;
733 - case "Read":
734 - permissionEx.Read = Util.YesNoType.yes;
735 - break;
736 - case "ReadAttributes":
737 - permissionEx.ReadAttributes = Util.YesNoType.yes;
738 - break;
739 - case "ReadExtendedAttributes":
740 - permissionEx.ReadExtendedAttributes = Util.YesNoType.yes;
741 - break;
742 - case "ReadPermission":
743 - permissionEx.ReadPermission = Util.YesNoType.yes;
744 - break;
745 - case "ServiceChangeConfig":
746 - permissionEx.ServiceChangeConfig = Util.YesNoType.yes;
747 - break;
748 - case "ServiceEnumerateDependents":
749 - permissionEx.ServiceEnumerateDependents = Util.YesNoType.yes;
750 - break;
751 - case "ServiceInterrogate":
752 - permissionEx.ServiceInterrogate = Util.YesNoType.yes;
753 - break;
754 - case "ServicePauseContinue":
755 - permissionEx.ServicePauseContinue = Util.YesNoType.yes;
756 - break;
757 - case "ServiceQueryConfig":
758 - permissionEx.ServiceQueryConfig = Util.YesNoType.yes;
759 - break;
760 - case "ServiceQueryStatus":
761 - permissionEx.ServiceQueryStatus = Util.YesNoType.yes;
762 - break;
763 - case "ServiceStart":
764 - permissionEx.ServiceStart = Util.YesNoType.yes;
765 - break;
766 - case "ServiceStop":
767 - permissionEx.ServiceStop = Util.YesNoType.yes;
768 - break;
769 - case "ServiceUserDefinedControl":
770 - permissionEx.ServiceUserDefinedControl = Util.YesNoType.yes;
771 - break;
772 - case "Synchronize":
773 - permissionEx.Synchronize = Util.YesNoType.yes;
774 - break;
775 - case "TakeOwnership":
776 - permissionEx.TakeOwnership = Util.YesNoType.yes;
777 - break;
778 - case "Traverse":
779 - permissionEx.Traverse = Util.YesNoType.yes;
780 - break;
781 - case "Write":
782 - permissionEx.Write = Util.YesNoType.yes;
783 - break;
784 - case "WriteAttributes":
785 - permissionEx.WriteAttributes = Util.YesNoType.yes;
786 - break;
787 - case "WriteExtendedAttributes":
788 - permissionEx.WriteExtendedAttributes = Util.YesNoType.yes;
789 - break;
790 - default:
791 - throw new InvalidOperationException(String.Format("Unknown permission attribute '{0}'.", name));
792 - }
793 - }
794 - }
795 - }
796 -
797 - if (null != row[2])
798 - {
799 - permissionEx.Domain = (string)row[2];
800 - }
801 -
802 - permissionEx.User = (string)row[3];
803 -
804 - this.Core.IndexElement(row, permissionEx);
527 + this.DecompilerHelper.IndexElement(row, permissionEx);
528 }
529 }
530
@@ -811,90 +534,20 @@ namespace WixToolset.Extensions
534 /// <param name="table">The table to decompile.</param>
535 private void DecompileServiceConfigTable(Table table)
536 {
814 - foreach (Row row in table.Rows)
537 + foreach (var row in table.Rows)
538 {
816 - Util.ServiceConfig serviceConfig = new Util.ServiceConfig();
817 -
818 - serviceConfig.ServiceName = (string)row[0];
819 -
820 - switch ((string)row[3])
821 - {
822 - case "none":
823 - serviceConfig.FirstFailureActionType = Util.ServiceConfig.FirstFailureActionTypeType.none;
824 - break;
825 - case "reboot":
826 - serviceConfig.FirstFailureActionType = Util.ServiceConfig.FirstFailureActionTypeType.reboot;
827 - break;
828 - case "restart":
829 - serviceConfig.FirstFailureActionType = Util.ServiceConfig.FirstFailureActionTypeType.restart;
830 - break;
831 - case "runCommand":
832 - serviceConfig.FirstFailureActionType = Util.ServiceConfig.FirstFailureActionTypeType.runCommand;
833 - break;
834 - default:
835 - this.Core.OnMessage(WixWarnings.IllegalColumnValue(row.SourceLineNumbers, table.Name, row.Fields[3].Column.Name, row[3]));
836 - break;
837 - }
838 -
839 - switch ((string)row[4])
840 - {
841 - case "none":
842 - serviceConfig.SecondFailureActionType = Util.ServiceConfig.SecondFailureActionTypeType.none;
843 - break;
844 - case "reboot":
845 - serviceConfig.SecondFailureActionType = Util.ServiceConfig.SecondFailureActionTypeType.reboot;
846 - break;
847 - case "restart":
848 - serviceConfig.SecondFailureActionType = Util.ServiceConfig.SecondFailureActionTypeType.restart;
849 - break;
850 - case "runCommand":
851 - serviceConfig.SecondFailureActionType = Util.ServiceConfig.SecondFailureActionTypeType.runCommand;
852 - break;
853 - default:
854 - this.Core.OnMessage(WixWarnings.IllegalColumnValue(row.SourceLineNumbers, table.Name, row.Fields[4].Column.Name, row[4]));
855 - break;
856 - }
857 -
858 - switch ((string)row[5])
859 - {
860 - case "none":
861 - serviceConfig.ThirdFailureActionType = Util.ServiceConfig.ThirdFailureActionTypeType.none;
862 - break;
863 - case "reboot":
864 - serviceConfig.ThirdFailureActionType = Util.ServiceConfig.ThirdFailureActionTypeType.reboot;
865 - break;
866 - case "restart":
867 - serviceConfig.ThirdFailureActionType = Util.ServiceConfig.ThirdFailureActionTypeType.restart;
868 - break;
869 - case "runCommand":
870 - serviceConfig.ThirdFailureActionType = Util.ServiceConfig.ThirdFailureActionTypeType.runCommand;
871 - break;
872 - default:
873 - this.Core.OnMessage(WixWarnings.IllegalColumnValue(row.SourceLineNumbers, table.Name, row.Fields[5].Column.Name, row[5]));
874 - break;
875 - }
876 -
877 - if (null != row[6])
878 - {
879 - serviceConfig.ResetPeriodInDays = (int)row[6];
880 - }
881 -
882 - if (null != row[7])
883 - {
884 - serviceConfig.RestartServiceDelayInSeconds = (int)row[7];
885 - }
886 -
887 - if (null != row[8])
888 - {
889 - serviceConfig.ProgramCommandLine = (string)row[8];
890 - }
891 -
892 - if (null != row[9])
893 - {
894 - serviceConfig.RebootMessage = (string)row[9];
895 - }
896 -
897 - this.Core.IndexElement(row, serviceConfig);
539 + var serviceConfig = new XElement(UtilConstants.ServiceConfigName,
540 + new XAttribute("ServiceName", row.FieldAsString(0)),
541 + AttributeIfNotNull("FirstFailureActionType", row, 3),
542 + AttributeIfNotNull("SecondFailureActionType", row, 4),
543 + AttributeIfNotNull("ThirdFailureActionType", row, 5),
544 + NumericAttributeIfNotNull("ResetPeriodInDays", row, 6),
545 + NumericAttributeIfNotNull("RestartServiceDelayInSeconds", row, 7),
546 + AttributeIfNotNull("ProgramCommandLine", row, 8),
547 + AttributeIfNotNull("RebootMessage", row, 9)
548 + );
549 +
550 + this.DecompilerHelper.IndexElement(row, serviceConfig);
551 }
552 }
553
@@ -904,97 +557,58 @@ namespace WixToolset.Extensions
557 /// <param name="table">The table to decompile.</param>
558 private void DecompileUserTable(Table table)
559 {
907 - foreach (Row row in table.Rows)
560 + foreach (var row in table.Rows)
561 {
909 - Util.User user = new Util.User();
910 -
911 - user.Id = (string)row[0];
912 -
913 - user.Name = (string)row[2];
914 -
915 - if (null != row[3])
562 + var attributes = row.FieldAsNullableInteger(5) ?? 0;
563 +
564 + var user = new XElement(UtilConstants.UserName,
565 + new XAttribute("Id", row.FieldAsString(0)),
566 + new XAttribute("Name", row.FieldAsString(2)),
567 + AttributeIfNotNull("Domain", row, 3),
568 + AttributeIfNotNull("Password", row, 4),
569 + AttributeIfTrue("PasswordNeverExpires", UtilCompiler.UserDontExpirePasswrd == (attributes & UtilCompiler.UserDontExpirePasswrd)),
570 + AttributeIfTrue("CanNotChangePassword", UtilCompiler.UserPasswdCantChange == (attributes & UtilCompiler.UserPasswdCantChange)),
571 + AttributeIfTrue("PasswordExpired", UtilCompiler.UserPasswdChangeReqdOnLogin == (attributes & UtilCompiler.UserPasswdChangeReqdOnLogin)),
572 + AttributeIfTrue("Disabled", UtilCompiler.UserDisableAccount == (attributes & UtilCompiler.UserDisableAccount)),
573 + AttributeIfTrue("FailIfExists", UtilCompiler.UserFailIfExists == (attributes & UtilCompiler.UserFailIfExists)),
574 + AttributeIfTrue("UpdateIfExists", UtilCompiler.UserUpdateIfExists == (attributes & UtilCompiler.UserUpdateIfExists)),
575 + AttributeIfTrue("LogonAsService", UtilCompiler.UserLogonAsService == (attributes & UtilCompiler.UserLogonAsService)),
576 + AttributeIfTrue("LogonAsService", UtilCompiler.UserLogonAsService == (attributes & UtilCompiler.UserLogonAsService))
577 + );
578 +
579 + if (UtilCompiler.UserDontRemoveOnUninstall == (attributes & UtilCompiler.UserDontRemoveOnUninstall))
580 {
917 - user.Domain = (string)row[3];
581 + user.Add(new XAttribute("RemoveOnUninstall", "no"));
582 }
583
920 - if (null != row[4])
584 + if (UtilCompiler.UserDontCreateUser == (attributes & UtilCompiler.UserDontCreateUser))
585 {
922 - user.Password = (string)row[4];
586 + user.Add(new XAttribute("CreateUser", "no"));
587 }
588
925 - if (null != row[5])
589 + if (UtilCompiler.UserNonVital == (attributes & UtilCompiler.UserNonVital))
590 {
927 - int attributes = (int)row[5];
928 -
929 - if (UtilCompiler.UserDontExpirePasswrd == (attributes & UtilCompiler.UserDontExpirePasswrd))
930 - {
931 - user.PasswordNeverExpires = Util.YesNoType.yes;
932 - }
933 -
934 - if (UtilCompiler.UserPasswdCantChange == (attributes & UtilCompiler.UserPasswdCantChange))
935 - {
936 - user.CanNotChangePassword = Util.YesNoType.yes;
937 - }
938 -
939 - if (UtilCompiler.UserPasswdChangeReqdOnLogin == (attributes & UtilCompiler.UserPasswdChangeReqdOnLogin))
940 - {
941 - user.PasswordExpired = Util.YesNoType.yes;
942 - }
943 -
944 - if (UtilCompiler.UserDisableAccount == (attributes & UtilCompiler.UserDisableAccount))
945 - {
946 - user.Disabled = Util.YesNoType.yes;
947 - }
948 -
949 - if (UtilCompiler.UserFailIfExists == (attributes & UtilCompiler.UserFailIfExists))
950 - {
951 - user.FailIfExists = Util.YesNoType.yes;
952 - }
953 -
954 - if (UtilCompiler.UserUpdateIfExists == (attributes & UtilCompiler.UserUpdateIfExists))
955 - {
956 - user.UpdateIfExists = Util.YesNoType.yes;
957 - }
958 -
959 - if (UtilCompiler.UserLogonAsService == (attributes & UtilCompiler.UserLogonAsService))
960 - {
961 - user.LogonAsService = Util.YesNoType.yes;
962 - }
963 -
964 - if (UtilCompiler.UserDontRemoveOnUninstall == (attributes & UtilCompiler.UserDontRemoveOnUninstall))
965 - {
966 - user.RemoveOnUninstall = Util.YesNoType.no;
967 - }
968 -
969 - if (UtilCompiler.UserDontCreateUser == (attributes & UtilCompiler.UserDontCreateUser))
970 - {
971 - user.CreateUser = Util.YesNoType.no;
972 - }
973 -
974 - if (UtilCompiler.UserNonVital == (attributes & UtilCompiler.UserNonVital))
975 - {
976 - user.Vital = Util.YesNoType.no;
977 - }
591 + user.Add(new XAttribute("Vital", "no"));
592 }
593
980 - if (null != row[1])
594 + var componentId = row.FieldAsString(1);
595 + if (!String.IsNullOrEmpty(componentId))
596 {
982 - Wix.Component component = (Wix.Component)this.Core.GetIndexedElement("Component", (string)row[1]);
983 -
984 - if (null != component)
597 + if (this.DecompilerHelper.TryGetIndexedElement("Component", componentId, out var component))
598 {
986 - component.AddChild(user);
599 + component.Add(user);
600 }
601 else
602 {
990 - this.Core.OnMessage(WixWarnings.ExpectedForeignRow(row.SourceLineNumbers, table.Name, row.GetPrimaryKey(DecompilerConstants.PrimaryKeyDelimiter), "Component_", (string)row[1], "Component"));
603 + this.Messaging.Write(WarningMessages.ExpectedForeignRow(row.SourceLineNumbers, table.Name, row.GetPrimaryKey(), "Component_", componentId, "Component"));
604 }
605 }
606 else
607 {
995 - this.Core.RootElement.AddChild(user);
608 + this.DecompilerHelper.AddElementToRoot(user);
609 }
997 - this.Core.IndexElement(row, user);
610 +
611 + this.DecompilerHelper.IndexElement(row, user);
612 }
613 }
614
@@ -1004,21 +618,16 @@ namespace WixToolset.Extensions
618 /// <param name="table">The table to decompile.</param>
619 private void DecompileUserGroupTable(Table table)
620 {
1007 - foreach (Row row in table.Rows)
621 + foreach (var row in table.Rows)
622 {
1009 - Util.User user = (Util.User)this.Core.GetIndexedElement("User", (string)row[0]);
1010 -
1011 - if (null != user)
623 + var userId = row.FieldAsString(0);
624 + if (this.DecompilerHelper.TryGetIndexedElement("User", userId, out var user))
625 {
1013 - Util.GroupRef groupRef = new Util.GroupRef();
1014 -
1015 - groupRef.Id = (string)row[1];
1016 -
1017 - user.AddChild(groupRef);
626 + user.Add(new XElement(UtilConstants.GroupRefName, new XAttribute("Id", row.FieldAsString(1))));
627 }
628 else
629 {
1021 - this.Core.OnMessage(WixWarnings.ExpectedForeignRow(row.SourceLineNumbers, table.Name, row.GetPrimaryKey(DecompilerConstants.PrimaryKeyDelimiter), "Group_", (string)row[0], "Group"));
630 + this.Messaging.Write(WarningMessages.ExpectedForeignRow(row.SourceLineNumbers, table.Name, row.GetPrimaryKey(), "Group_", userId, "Group"));
631 }
632 }
633 }
@@ -1029,75 +638,58 @@ namespace WixToolset.Extensions
638 /// <param name="table">The table to decompile.</param>
639 private void DecompileXmlConfigTable(Table table)
640 {
1032 - foreach (Row row in table.Rows)
641 + foreach (var row in table.Rows)
642 {
1034 - Util.XmlConfig xmlConfig = new Util.XmlConfig();
1035 -
1036 - xmlConfig.Id = (string)row[0];
1037 -
1038 - xmlConfig.File = (string)row[1];
1039 -
1040 - xmlConfig.ElementPath = (string)row[2];
1041 -
1042 - if (null != row[3])
1043 - {
1044 - xmlConfig.VerifyPath = (string)row[3];
1045 - }
1046 -
1047 - if (null != row[4])
1048 - {
1049 - xmlConfig.Name = (string)row[4];
1050 - }
1051 -
1052 - if (null != row[5])
1053 - {
1054 - xmlConfig.Value = (string)row[5];
1055 - }
1056 -
1057 - int flags = (int)row[6];
643 + var flags = row.FieldAsNullableInteger(6) ?? 0;
644 + string node = null;
645 + string action = null;
646 + string on = null;
647
648 if (0x1 == (flags & 0x1))
649 {
1061 - xmlConfig.Node = Util.XmlConfig.NodeType.element;
650 + node = "element";
651 }
652 else if (0x2 == (flags & 0x2))
653 {
1065 - xmlConfig.Node = Util.XmlConfig.NodeType.value;
654 + node = "value";
655 }
656 else if (0x4 == (flags & 0x4))
657 {
1069 - xmlConfig.Node = Util.XmlConfig.NodeType.document;
658 + node = "document";
659 }
660
661 if (0x10 == (flags & 0x10))
662 {
1074 - xmlConfig.Action = Util.XmlConfig.ActionType.create;
663 + action = "create";
664 }
665 else if (0x20 == (flags & 0x20))
666 {
1078 - xmlConfig.Action = Util.XmlConfig.ActionType.delete;
667 + action = "delete";
668 }
669
670 if (0x100 == (flags & 0x100))
671 {
1083 - xmlConfig.On = Util.XmlConfig.OnType.install;
672 + on = "install";
673 }
674 else if (0x200 == (flags & 0x200))
675 {
1087 - xmlConfig.On = Util.XmlConfig.OnType.uninstall;
1088 - }
1089 -
1090 - if (0x00001000 == (flags & 0x00001000))
1091 - {
1092 - xmlConfig.PreserveModifiedDate = Util.YesNoType.yes;
1093 - }
1094 -
1095 - if (null != row[8])
1096 - {
1097 - xmlConfig.Sequence = (int)row[8];
676 + on = "uninstall";
677 }
678
1100 - this.Core.IndexElement(row, xmlConfig);
679 + var xmlConfig = new XElement(UtilConstants.XmlConfigName,
680 + new XAttribute("Id", row.FieldAsString(0)),
681 + new XAttribute("File", row.FieldAsString(1)),
682 + new XAttribute("ElementPath", row.FieldAsString(2)),
683 + AttributeIfNotNull("VerifyPath", row, 3),
684 + AttributeIfNotNull("Name", row, 4),
685 + AttributeIfNotNull("Node", node),
686 + AttributeIfNotNull("Action", action),
687 + AttributeIfNotNull("On", on),
688 + AttributeIfTrue("PreserveModifiedDate", 0x00001000 == (flags & 0x00001000)),
689 + NumericAttributeIfNotNull("Sequence", row, 8)
690 + );
691 +
692 + this.DecompilerHelper.IndexElement(row, xmlConfig);
693 }
694 }
695
@@ -1114,34 +706,30 @@ namespace WixToolset.Extensions
706 /// </remarks>
707 private void FinalizePerfmonTable(TableIndexedCollection tables)
708 {
1117 - Table perfmonTable = tables["Perfmon"];
1118 -
1119 - if (null != perfmonTable)
709 + if (tables.TryGetTable("Perfmon", out var perfmonTable))
710 {
1121 - foreach (Row row in perfmonTable.Rows)
711 + foreach (var row in perfmonTable.Rows)
712 {
1123 - string formattedFile = (string)row[1];
1124 - Util.PerfCounter perfCounter = (Util.PerfCounter)this.Core.GetIndexedElement(row);
713 + var formattedFile = row.FieldAsString(1);
714
715 // try to "de-format" the File column's value to determine the proper parent File element
716 if ((formattedFile.StartsWith("[#", StringComparison.Ordinal) || formattedFile.StartsWith("[!", StringComparison.Ordinal))
717 && formattedFile.EndsWith("]", StringComparison.Ordinal))
718 {
1130 - string fileId = formattedFile.Substring(2, formattedFile.Length - 3);
1131 -
1132 - Wix.File file = (Wix.File)this.Core.GetIndexedElement("File", fileId);
1133 - if (null != file)
719 + var fileId = formattedFile.Substring(2, formattedFile.Length - 3);
720 + if (this.DecompilerHelper.TryGetIndexedElement("File", fileId, out var file))
721 {
1135 - file.AddChild(perfCounter);
722 + var perfCounter = this.DecompilerHelper.GetIndexedElement(row);
723 + file.Add(perfCounter);
724 }
725 else
726 {
1139 - this.Core.OnMessage(WixWarnings.ExpectedForeignRow(row.SourceLineNumbers, perfmonTable.Name, row.GetPrimaryKey(DecompilerConstants.PrimaryKeyDelimiter), "File", formattedFile, "File"));
727 + this.Messaging.Write(WarningMessages.ExpectedForeignRow(row.SourceLineNumbers, perfmonTable.Name, row.GetPrimaryKey(), "File", formattedFile, "File"));
728 }
729 }
730 else
731 {
1144 - this.Core.OnMessage(UtilErrors.IllegalFileValueInPerfmonOrManifest(formattedFile, "Perfmon"));
732 + this.Messaging.Write(UtilErrors.IllegalFileValueInPerfmonOrManifest(formattedFile, "Perfmon"));
733 }
734 }
735 }
@@ -1153,34 +741,33 @@ namespace WixToolset.Extensions
741 /// <param name="tables">The collection of all tables.</param>
742 private void FinalizePerfmonManifestTable(TableIndexedCollection tables)
743 {
1156 - Table perfmonManifestTable = tables["PerfmonManifest"];
1157 -
1158 - if (null != perfmonManifestTable)
744 + if (tables.TryGetTable("PerfmonManifest", out var perfmonManifestTable))
745 {
1160 - foreach (Row row in perfmonManifestTable.Rows)
746 + foreach (var row in perfmonManifestTable.Rows)
747 {
1162 - string formattedFile = (string)row[1];
1163 - Util.PerfCounterManifest perfCounterManifest = (Util.PerfCounterManifest)this.Core.GetIndexedElement(row);
748 + var formattedFile = row.FieldAsString(1);
749
750 // try to "de-format" the File column's value to determine the proper parent File element
751 if ((formattedFile.StartsWith("[#", StringComparison.Ordinal) || formattedFile.StartsWith("[!", StringComparison.Ordinal))
752 && formattedFile.EndsWith("]", StringComparison.Ordinal))
753 {
1169 - string fileId = formattedFile.Substring(2, formattedFile.Length - 3);
754 + var perfCounterManifest = this.DecompilerHelper.GetIndexedElement(row);
755 + var fileId = formattedFile.Substring(2, formattedFile.Length - 3);
756
1171 - Wix.File file = (Wix.File)this.Core.GetIndexedElement("File", fileId);
1172 - if (null != file)
757 + if (this.DecompilerHelper.TryGetIndexedElement("File", fileId, out var file))
758 {
1174 - file.AddChild(perfCounterManifest);
759 + file.Add(perfCounterManifest);
760 }
761 else
762 {
1178 - this.Core.OnMessage(WixWarnings.ExpectedForeignRow(row.SourceLineNumbers, perfCounterManifest.ResourceFileDirectory, row.GetPrimaryKey(DecompilerConstants.PrimaryKeyDelimiter), "File", formattedFile, "File"));
763 + var resourceFileDirectory = perfCounterManifest.Attribute("ResourceFileDirectory")?.Value;
764 +
765 + this.Messaging.Write(WarningMessages.ExpectedForeignRow(row.SourceLineNumbers, resourceFileDirectory, row.GetPrimaryKey(), "File", formattedFile, "File"));
766 }
767 }
768 else
769 {
1183 - this.Core.OnMessage(UtilErrors.IllegalFileValueInPerfmonOrManifest(formattedFile, "PerfmonManifest"));
770 + this.Messaging.Write(UtilErrors.IllegalFileValueInPerfmonOrManifest(formattedFile, "PerfmonManifest"));
771 }
772 }
773 }
@@ -1196,64 +783,61 @@ namespace WixToolset.Extensions
783 /// </remarks>
784 private void FinalizeSecureObjectsTable(TableIndexedCollection tables)
785 {
1199 - Table createFolderTable = tables["CreateFolder"];
1200 - Table secureObjectsTable = tables["SecureObjects"];
1201 -
1202 - Hashtable createFolders = new Hashtable();
786 + var createFolderElementsByDirectoryId = new Dictionary<string, List<XElement>>();
787
788 // index the CreateFolder table because the foreign key to this table from the
789 // LockPermissions table is only part of the primary key of this table
1206 - if (null != createFolderTable)
790 + if (tables.TryGetTable("CreateFolder", out var createFolderTable))
791 {
1208 - foreach (Row row in createFolderTable.Rows)
792 + foreach (var row in createFolderTable.Rows)
793 {
1210 - Wix.CreateFolder createFolder = (Wix.CreateFolder)this.Core.GetIndexedElement(row);
1211 - string directoryId = (string)row[0];
794 + var directoryId = row.FieldAsString(0);
795
1213 - if (!createFolders.Contains(directoryId))
796 + if (!createFolderElementsByDirectoryId.TryGetValue(directoryId, out var createFolderElements))
797 {
1215 - createFolders.Add(directoryId, new ArrayList());
798 + createFolderElements = new List<XElement>();
799 + createFolderElementsByDirectoryId.Add(directoryId, createFolderElements);
800 }
1217 - ((ArrayList)createFolders[directoryId]).Add(createFolder);
801 +
802 + var createFolder = this.DecompilerHelper.GetIndexedElement(row);
803 + createFolderElements.Add(createFolder);
804 }
805 }
806
1221 - if (null != secureObjectsTable)
807 + if (tables.TryGetTable("SecureObjects", out var secureObjectsTable))
808 {
1223 - foreach (Row row in secureObjectsTable.Rows)
809 + foreach (var row in secureObjectsTable.Rows)
810 {
1225 - string id = (string)row[0];
1226 - string table = (string)row[1];
811 + var id = row.FieldAsString(0);
812 + var table = row.FieldAsString(1);
813
1228 - Util.PermissionEx permissionEx = (Util.PermissionEx)this.Core.GetIndexedElement(row);
814 + var permissionEx = this.DecompilerHelper.GetIndexedElement(row);
815
1230 - if ("CreateFolder" == table)
816 + if (table == "CreateFolder")
817 {
1232 - ArrayList createFolderElements = (ArrayList)createFolders[id];
1233 -
1234 - if (null != createFolderElements)
818 + if (createFolderElementsByDirectoryId.TryGetValue(id, out var createFolderElements))
819 {
1236 - foreach (Wix.CreateFolder createFolder in createFolderElements)
820 + foreach (var createFolder in createFolderElements)
821 {
1238 - createFolder.AddChild(permissionEx);
822 + createFolder.Add(permissionEx);
823 }
824 }
825 else
826 {
1243 - this.Core.OnMessage(WixWarnings.ExpectedForeignRow(row.SourceLineNumbers, "SecureObjects", row.GetPrimaryKey(DecompilerConstants.PrimaryKeyDelimiter), "LockObject", id, table));
827 + this.Messaging.Write(WarningMessages.ExpectedForeignRow(row.SourceLineNumbers, "SecureObjects", row.GetPrimaryKey(), "LockObject", id, table));
828 }
829 }
830 else
831 {
1248 - Wix.IParentElement parentElement = (Wix.IParentElement)this.Core.GetIndexedElement(table, id);
832 + var parentElement = this.DecompilerHelper.GetIndexedElement(table, id);
833
1250 - if (null != parentElement)
834 + if (parentElement != null)
835 {
1252 - parentElement.AddChild(permissionEx);
836 + parentElement.Add(permissionEx);
837 }
838 else
839 {
1256 - this.Core.OnMessage(WixWarnings.ExpectedForeignRow(row.SourceLineNumbers, "SecureObjects", row.GetPrimaryKey(DecompilerConstants.PrimaryKeyDelimiter), "LockObject", id, table));
840 + this.Messaging.Write(WarningMessages.ExpectedForeignRow(row.SourceLineNumbers, "SecureObjects", row.GetPrimaryKey(), "LockObject", id, table));
841 }
842 }
843 }
@@ -1270,10 +854,8 @@ namespace WixToolset.Extensions
854 /// </remarks>
855 private void FinalizeServiceConfigTable(TableIndexedCollection tables)
856 {
1273 - Table serviceConfigTable = tables["ServiceConfig"];
1274 - Table serviceInstallTable = tables["ServiceInstall"];
1275 -
1276 - Hashtable serviceInstalls = new Hashtable();
857 + //var serviceInstalls = new Hashtable();
858 + var serviceInstallElementsByName = new Dictionary<string, List<XElement>>();
859
860 // index the ServiceInstall table because the foreign key used by the ServiceConfig
861 // table is actually the ServiceInstall.Name, not the ServiceInstall.ServiceInstall
@@ -1281,55 +863,54 @@ namespace WixToolset.Extensions
863 // decompiler must assume there could be multiple matches and add the ServiceConfig to each
864 // TODO: the Component column information should be taken into acount to accurately identify
865 // the correct column to use
1284 - if (null != serviceInstallTable)
866 + if (tables.TryGetTable("ServiceInstall", out var serviceInstallTable))
867 {
1286 - foreach (Row row in serviceInstallTable.Rows)
868 + foreach (var row in serviceInstallTable.Rows)
869 {
1288 - string name = (string)row[1];
1289 - Wix.ServiceInstall serviceInstall = (Wix.ServiceInstall)this.Core.GetIndexedElement(row);
870 + var name = row.FieldAsString(1);
871
1291 - if (!serviceInstalls.Contains(name))
872 + if (!serviceInstallElementsByName.TryGetValue(name, out var serviceInstallElements))
873 {
1293 - serviceInstalls.Add(name, new ArrayList());
874 + serviceInstallElements = new List<XElement>();
875 + serviceInstallElementsByName.Add(name, serviceInstallElements);
876 }
877
1296 - ((ArrayList)serviceInstalls[name]).Add(serviceInstall);
878 + var serviceInstall = this.DecompilerHelper.GetIndexedElement(row);
879 + serviceInstallElements.Add(serviceInstall);
880 }
881 }
882
1300 - if (null != serviceConfigTable)
883 + if (tables.TryGetTable("ServiceConfig", out var serviceConfigTable))
884 {
1302 - foreach (Row row in serviceConfigTable.Rows)
885 + foreach (var row in serviceConfigTable.Rows)
886 {
1304 - Util.ServiceConfig serviceConfig = (Util.ServiceConfig)this.Core.GetIndexedElement(row);
887 + var serviceConfig = this.DecompilerHelper.GetIndexedElement(row);
888
1306 - if (0 == (int)row[2])
889 + if (row.FieldAsInteger(2) == 0)
890 {
1308 - Wix.Component component = (Wix.Component)this.Core.GetIndexedElement("Component", (string)row[1]);
1309 -
1310 - if (null != component)
891 + var componentId = row.FieldAsString(1);
892 + if (this.DecompilerHelper.TryGetIndexedElement("Component", componentId, out var component))
893 {
1312 - component.AddChild(serviceConfig);
894 + component.Add(serviceConfig);
895 }
896 else
897 {
1316 - this.Core.OnMessage(WixWarnings.ExpectedForeignRow(row.SourceLineNumbers, serviceConfigTable.Name, row.GetPrimaryKey(DecompilerConstants.PrimaryKeyDelimiter), "Component_", (string)row[1], "Component"));
898 + this.Messaging.Write(WarningMessages.ExpectedForeignRow(row.SourceLineNumbers, serviceConfigTable.Name, row.GetPrimaryKey(), "Component_", componentId, "Component"));
899 }
900 }
901 else
902 {
1321 - ArrayList serviceInstallElements = (ArrayList)serviceInstalls[row[0]];
1322 -
1323 - if (null != serviceInstallElements)
903 + var name = row.FieldAsString(0);
904 + if (serviceInstallElementsByName.TryGetValue(name, out var serviceInstallElements))
905 {
1325 - foreach (Wix.ServiceInstall serviceInstall in serviceInstallElements)
906 + foreach (var serviceInstall in serviceInstallElements)
907 {
1327 - serviceInstall.AddChild(serviceConfig);
908 + serviceInstall.Add(serviceConfig);
909 }
910 }
911 else
912 {
1332 - this.Core.OnMessage(WixWarnings.ExpectedForeignRow(row.SourceLineNumbers, serviceConfigTable.Name, row.GetPrimaryKey(DecompilerConstants.PrimaryKeyDelimiter), "ServiceName", (string)row[0], "ServiceInstall"));
913 + this.Messaging.Write(WarningMessages.ExpectedForeignRow(row.SourceLineNumbers, serviceConfigTable.Name, row.GetPrimaryKey(), "ServiceName", name, "ServiceInstall"));
914 }
915 }
916 }
@@ -1342,38 +923,34 @@ namespace WixToolset.Extensions
923 /// <param name="tables">Collection of all tables.</param>
924 private void FinalizeXmlConfigTable(TableIndexedCollection tables)
925 {
1345 - Table xmlConfigTable = tables["XmlConfig"];
1346 -
1347 - if (null != xmlConfigTable)
926 + if (tables.TryGetTable("XmlConfig", out var xmlConfigTable))
927 {
1349 - foreach (Row row in xmlConfigTable.Rows)
928 + foreach (var row in xmlConfigTable.Rows)
929 {
1351 - Util.XmlConfig xmlConfig = (Util.XmlConfig)this.Core.GetIndexedElement(row);
930 + var xmlConfig = this.DecompilerHelper.GetIndexedElement(row);
931
932 if (null == row[6] || 0 == (int)row[6])
933 {
1355 - Util.XmlConfig parentXmlConfig = (Util.XmlConfig)this.Core.GetIndexedElement("XmlConfig", (string)row[2]);
1356 -
1357 - if (null != parentXmlConfig)
934 + var id = row.FieldAsString(2);
935 + if (this.DecompilerHelper.TryGetIndexedElement("XmlConfig", id, out var parentXmlConfig))
936 {
1359 - parentXmlConfig.AddChild(xmlConfig);
937 + parentXmlConfig.Add(xmlConfig);
938 }
939 else
940 {
1363 - this.Core.OnMessage(WixWarnings.ExpectedForeignRow(row.SourceLineNumbers, xmlConfigTable.Name, row.GetPrimaryKey(DecompilerConstants.PrimaryKeyDelimiter), "ElementPath", (string)row[2], "XmlConfig"));
941 + this.Messaging.Write(WarningMessages.ExpectedForeignRow(row.SourceLineNumbers, xmlConfigTable.Name, row.GetPrimaryKey(), "ElementPath", (string)row[2], "XmlConfig"));
942 }
943 }
944 else
945 {
1368 - Wix.Component component = (Wix.Component)this.Core.GetIndexedElement("Component", (string)row[7]);
1369 -
1370 - if (null != component)
946 + var componentId = row.FieldAsString(7);
947 + if (this.DecompilerHelper.TryGetIndexedElement("Component", componentId, out var component))
948 {
1372 - component.AddChild(xmlConfig);
949 + component.Add(xmlConfig);
950 }
951 else
952 {
1376 - this.Core.OnMessage(WixWarnings.ExpectedForeignRow(row.SourceLineNumbers, xmlConfigTable.Name, row.GetPrimaryKey(DecompilerConstants.PrimaryKeyDelimiter), "Component_", (string)row[7], "Component"));
953 + this.Messaging.Write(WarningMessages.ExpectedForeignRow(row.SourceLineNumbers, xmlConfigTable.Name, row.GetPrimaryKey(), "Component_", componentId, "Component"));
954 }
955 }
956 }
@@ -1391,113 +968,88 @@ namespace WixToolset.Extensions
968 /// </remarks>
969 private void FinalizeXmlFileTable(TableIndexedCollection tables)
970 {
1394 - Table xmlFileTable = tables["XmlFile"];
1395 - Table eventManifestTable = tables["EventManifest"];
1396 -
1397 - if (null != xmlFileTable)
971 + if (tables.TryGetTable("XmlFile", out var xmlFileTable))
972 {
1399 - foreach (Row row in xmlFileTable.Rows)
973 + var eventManifestTable = tables["EventManifest"];
974 +
975 + foreach (var row in xmlFileTable.Rows)
976 {
1401 - bool bManifestGenerated = false;
1402 - string xmlFileConfigId = (string)row[0];
977 + var manifestGenerated = false;
978 + var xmlFileConfigId = (string)row[0];
979 if (null != eventManifestTable)
980 {
1405 - foreach (Row emrow in eventManifestTable.Rows)
981 + foreach (var emrow in eventManifestTable.Rows)
982 {
1407 - string formattedFile = (string)emrow[1];
983 + var formattedFile = (string)emrow[1];
984 if ((formattedFile.StartsWith("[#", StringComparison.Ordinal) || formattedFile.StartsWith("[!", StringComparison.Ordinal))
985 && formattedFile.EndsWith("]", StringComparison.Ordinal))
986 {
1411 - string fileId = formattedFile.Substring(2, formattedFile.Length - 3);
987 + var fileId = formattedFile.Substring(2, formattedFile.Length - 3);
988 if (String.Equals(String.Concat("Config_", fileId, "ResourceFile"), xmlFileConfigId))
989 {
1414 - Util.EventManifest eventManifest = (Util.EventManifest)this.Core.GetIndexedElement(emrow);
1415 - if (null != eventManifest)
990 + if (this.DecompilerHelper.TryGetIndexedElement(emrow, out var eventManifest))
991 {
1417 - eventManifest.ResourceFile = (string)row[4];
992 + eventManifest.Add(new XAttribute("ResourceFile", row.FieldAsString(4)));
993 }
1419 - bManifestGenerated = true;
994 + manifestGenerated = true;
995 }
1421 -
996 +
997 else if (String.Equals(String.Concat("Config_", fileId, "MessageFile"), xmlFileConfigId))
998 {
1424 - Util.EventManifest eventManifest = (Util.EventManifest)this.Core.GetIndexedElement(emrow);
1425 - if (null != eventManifest)
999 + if (this.DecompilerHelper.TryGetIndexedElement(emrow, out var eventManifest))
1000 {
1427 - eventManifest.MessageFile = (string)row[4];
1001 + eventManifest.Add(new XAttribute("MessageFile", row.FieldAsString(4)));
1002 }
1429 - bManifestGenerated = true;
1003 + manifestGenerated = true;
1004 }
1005 }
1006 }
1007 }
1008
1435 - if (true == bManifestGenerated)
1436 - continue;
1437 -
1438 - Util.XmlFile xmlFile = new Util.XmlFile();
1439 -
1440 - xmlFile.Id = (string)row[0];
1441 - xmlFile.File = (string)row[1];
1442 - xmlFile.ElementPath = (string)row[2];
1443 -
1444 - if (null != row[3])
1445 - {
1446 - xmlFile.Name = (string)row[3];
1447 - }
1448 -
1449 - if (null != row[4])
1009 + if (manifestGenerated)
1010 {
1451 - xmlFile.Value = (string)row[4];
1011 + continue;
1012 }
1013
1454 - int flags = (int)row[5];
1014 + var action = "setValue";
1015 + var flags = row.FieldAsInteger(5);
1016 if (0x1 == (flags & 0x1) && 0x2 == (flags & 0x2))
1017 {
1457 - this.Core.OnMessage(WixWarnings.IllegalColumnValue(row.SourceLineNumbers, xmlFileTable.Name, row.Fields[5].Column.Name, row[5]));
1018 + this.Messaging.Write(WarningMessages.IllegalColumnValue(row.SourceLineNumbers, xmlFileTable.Name, row.Fields[5].Column.Name, row[5]));
1019 }
1020 else if (0x1 == (flags & 0x1))
1021 {
1461 - xmlFile.Action = Util.XmlFile.ActionType.createElement;
1022 + action = "createElement";
1023 }
1024 else if (0x2 == (flags & 0x2))
1025 {
1465 - xmlFile.Action = Util.XmlFile.ActionType.deleteValue;
1466 - }
1467 - else
1468 - {
1469 - xmlFile.Action = Util.XmlFile.ActionType.setValue;
1470 - }
1471 -
1472 - if (0x100 == (flags & 0x100))
1473 - {
1474 - xmlFile.SelectionLanguage = Util.XmlFile.SelectionLanguageType.XPath;
1475 - }
1476 -
1477 - if (0x00001000 == (flags & 0x00001000))
1478 - {
1479 - xmlFile.PreserveModifiedDate = Util.YesNoType.yes;
1026 + action = "deleteValue";
1027 }
1028
1482 - if (0x00010000 == (flags & 0x00010000))
1483 - {
1484 - xmlFile.Permanent = Util.YesNoType.yes;
1485 - }
1029 + var selectionLanguage = (0x100 == (flags & 0x100)) ? "XPath" : null;
1030 + var preserveModifiedDate = 0x00001000 == (flags & 0x00001000);
1031 + var permanent = 0x00010000 == (flags & 0x00010000);
1032
1487 - if (null != row[7])
1033 + if (this.DecompilerHelper.TryGetIndexedElement("Component", row.FieldAsString(6), out var component))
1034 {
1489 - xmlFile.Sequence = (int)row[7];
1490 - }
1491 -
1492 - Wix.Component component = (Wix.Component)this.Core.GetIndexedElement("Component", (string)row[6]);
1493 -
1494 - if (null != component)
1495 - {
1496 - component.AddChild(xmlFile);
1035 + var xmlFile = new XElement(UtilConstants.XmlFileName,
1036 + AttributeIfNotNull("Id", row, 0),
1037 + AttributeIfNotNull("File", row, 1),
1038 + AttributeIfNotNull("ElementPath", row, 2),
1039 + AttributeIfNotNull("Name", row, 3),
1040 + AttributeIfNotNull("Value", row, 4),
1041 + AttributeIfNotNull("Action", action),
1042 + AttributeIfNotNull("SelectionLanguage", selectionLanguage),
1043 + AttributeIfTrue("PreserveModifiedDate", preserveModifiedDate),
1044 + AttributeIfTrue("Permanent", permanent),
1045 + NumericAttributeIfNotNull("Sequence", row, 7)
1046 + );
1047 +
1048 + component.Add(xmlFile);
1049 }
1050 else
1051 {
1500 - this.Core.OnMessage(WixWarnings.ExpectedForeignRow(row.SourceLineNumbers, xmlFileTable.Name, row.GetPrimaryKey(DecompilerConstants.PrimaryKeyDelimiter), "Component_", (string)row[6], "Component"));
1052 + this.Messaging.Write(WarningMessages.ExpectedForeignRow(row.SourceLineNumbers, xmlFileTable.Name, row.GetPrimaryKey(), "Component_", (string)row[6], "Component"));
1053 }
1054 }
1055 }
@@ -1510,34 +1062,122 @@ namespace WixToolset.Extensions
1062 /// <param name="tables">The collection of all tables.</param>
1063 private void FinalizeEventManifestTable(TableIndexedCollection tables)
1064 {
1513 - Table eventManifestTable = tables["EventManifest"];
1514 -
1515 - if (null != eventManifestTable)
1065 + if (tables.TryGetTable("EventManifest", out var eventManifestTable))
1066 {
1517 - foreach (Row row in eventManifestTable.Rows)
1067 + foreach (var row in eventManifestTable.Rows)
1068 {
1519 - string formattedFile = (string)row[1];
1520 - Util.EventManifest eventManifest = (Util.EventManifest)this.Core.GetIndexedElement(row);
1069 + var eventManifest = this.DecompilerHelper.GetIndexedElement(row);
1070 + var formattedFile = row.FieldAsString(1);
1071
1072 // try to "de-format" the File column's value to determine the proper parent File element
1073 if ((formattedFile.StartsWith("[#", StringComparison.Ordinal) || formattedFile.StartsWith("[!", StringComparison.Ordinal))
1074 && formattedFile.EndsWith("]", StringComparison.Ordinal))
1075 {
1526 - string fileId = formattedFile.Substring(2, formattedFile.Length - 3);
1076 + var fileId = formattedFile.Substring(2, formattedFile.Length - 3);
1077
1528 - Wix.File file = (Wix.File)this.Core.GetIndexedElement("File", fileId);
1529 - if (null != file)
1078 + if (this.DecompilerHelper.TryGetIndexedElement("File", fileId, out var file))
1079 {
1531 - file.AddChild(eventManifest);
1080 + file.Add(eventManifest);
1081 }
1082 }
1083 else
1084 {
1536 - this.Core.OnMessage(UtilErrors.IllegalFileValueInPerfmonOrManifest(formattedFile, "EventManifest"));
1085 + this.Messaging.Write(UtilErrors.IllegalFileValueInPerfmonOrManifest(formattedFile, "EventManifest"));
1086 + }
1087 + }
1088 + }
1089 + }
1090 +
1091 + private void AddPermissionAttributes(XElement element, Row row, int column, string[] specialPermissions)
1092 + {
1093 + var permissions = row.FieldAsInteger(column);
1094 + for (var i = 0; i < 32; i++)
1095 + {
1096 + if (0 != ((permissions >> i) & 1))
1097 + {
1098 + string name = null;
1099 +
1100 + if (16 > i && specialPermissions.Length > i)
1101 + {
1102 + name = specialPermissions[i];
1103 + }
1104 + else if (28 > i && UtilConstants.StandardPermissions.Length > (i - 16))
1105 + {
1106 + name = UtilConstants.StandardPermissions[i - 16];
1107 + }
1108 + else if (0 <= (i - 28) && UtilConstants.GenericPermissions.Length > (i - 28))
1109 + {
1110 + name = UtilConstants.GenericPermissions[i - 28];
1111 + }
1112 +
1113 + if (!String.IsNullOrEmpty(name))
1114 + {
1115 + element.Add(new XAttribute(name, "yes"));
1116 + }
1117 + else
1118 + {
1119 + this.Messaging.Write(WarningMessages.UnknownPermission(row.SourceLineNumbers, row.Table.Name, row.GetPrimaryKey(), i));
1120 }
1121 }
1122 }
1123 }
1124 +
1125 + private static XAttribute AttributeIfNotNull(string name, string value)
1126 + {
1127 + return value == null ? null : new XAttribute(name, value);
1128 + }
1129 +
1130 + private static XAttribute AttributeIfNotNull(string name, bool value)
1131 + {
1132 + return new XAttribute(name, value ? "yes" : "no");
1133 + }
1134 +
1135 + private static XAttribute AttributeIfNotNull(string name, Row row, int field)
1136 + {
1137 + if (row[field] != null)
1138 + {
1139 + return new XAttribute(name, row.FieldAsString(field));
1140 + }
1141 +
1142 + return null;
1143 + }
1144 +
1145 + private static XAttribute NumericAttributeIfNotNull(string name, Row row, int field)
1146 + {
1147 + if (row[field] != null)
1148 + {
1149 + return new XAttribute(name, row.FieldAsInteger(field));
1150 + }
1151 +
1152 + return null;
1153 + }
1154 +
1155 + private static XAttribute AttributeIfTrue(string name, bool value)
1156 + {
1157 + return value ? new XAttribute(name, "yes") : null;
1158 + }
1159 + }
1160 +
1161 + internal static class XElementExtensions
1162 + {
1163 + public static XElement AttributeIfNotNull(this XElement element, string name, Row row, int field)
1164 + {
1165 + if (row[field] != null)
1166 + {
1167 + element.Add(new XAttribute(name, row.FieldAsString(field)));
1168 + }
1169 +
1170 + return element;
1171 + }
1172 +
1173 + public static XElement NumericAttributeIfNotNull(this XElement element, string name, Row row, int field)
1174 + {
1175 + if (row[field] != null)
1176 + {
1177 + element.Add(new XAttribute(name, row.FieldAsInteger(field)));
1178 + }
1179 +
1180 + return element;
1181 + }
1182 }
1542 -#endif
1183 }
src/ext/Util/wixext/UtilExtensionFactory.cs
+1
@@ -11,6 +11,7 @@ namespace WixToolset.Util
11 protected override IReadOnlyCollection<Type> ExtensionTypes => new[]
12 {
13 typeof(UtilCompiler),
14 + typeof(UtilDecompiler),
15 typeof(UtilExtensionData),
16 typeof(UtilWindowsInstallerBackendBinderExtension),
17 };
src/ext/Util/wixext/UtilTableDefinitions.cs
+1 -1
@@ -85,7 +85,7 @@ namespace WixToolset.Util
85 UtilSymbolDefinitions.FileSharePermissions,
86 new[]
87 {
88 - new ColumnDefinition("FileShare_", ColumnType.String, 72, primaryKey: true, nullable: false, ColumnCategory.Identifier, keyTable: "FileShare", keyColumn: 1, description: "FileShare that these premissions are to be applied to.", modularizeType: ColumnModularizeType.Column),
88 + new ColumnDefinition("FileShare_", ColumnType.String, 72, primaryKey: true, nullable: false, ColumnCategory.Identifier, keyTable: "Wix4FileShare", keyColumn: 1, description: "FileShare that these premissions are to be applied to.", modularizeType: ColumnModularizeType.Column),
89 new ColumnDefinition("User_", ColumnType.String, 72, primaryKey: true, nullable: false, ColumnCategory.Identifier, keyTable: "Wix4User", description: "User that these premissions are to apply to.", modularizeType: ColumnModularizeType.Column),
90 new ColumnDefinition("Permissions", ColumnType.Number, 4, primaryKey: false, nullable: false, ColumnCategory.Unknown, description: "Permissions int, as in EXPLICIT_ACCESS.grfAccessPermissions in MSDN"),
91 },
src/internal/WixBuildTools.TestSupport/Builder.cs
+88
@@ -66,5 +66,93 @@ namespace WixBuildTools.TestSupport
66 return Query.QueryDatabase(outputPath, tables);
67 }
68 }
69 +
70 + public void BuildAndDecompileAndBuild(Action<string[]> buildFunc, Action<string[]> decompileFunc, string decompilePath)
71 + {
72 + var sourceFiles = Directory.GetFiles(this.SourceFolder, "*.wxs");
73 + var wxlFiles = Directory.GetFiles(this.SourceFolder, "*.wxl");
74 +
75 + using (var fs = new DisposableFileSystem())
76 + {
77 + var intermediateFolder = fs.GetFolder();
78 + var outputFolder = Path.Combine(intermediateFolder, "bin");
79 + var decompileExtractFolder = Path.Combine(intermediateFolder, "decompiled", "extract");
80 + var decompileIntermediateFolder = Path.Combine(intermediateFolder, "decompiled", "obj");
81 + var decompileBuildFolder = Path.Combine(intermediateFolder, "decompiled", "bin");
82 + var outputPath = Path.Combine(outputFolder, this.OutputFile);
83 + var decompileBuildPath = Path.Combine(decompileBuildFolder, this.OutputFile);
84 +
85 + // First build.
86 + var firstBuildArgs = new List<string>
87 + {
88 + "build",
89 + "-o", outputPath,
90 + "-intermediateFolder", intermediateFolder,
91 + };
92 +
93 + if (this.ExtensionType != null)
94 + {
95 + firstBuildArgs.Add("-ext");
96 + firstBuildArgs.Add(Path.GetFullPath(new Uri(this.ExtensionType.Assembly.CodeBase).LocalPath));
97 + }
98 +
99 + firstBuildArgs.AddRange(sourceFiles);
100 +
101 + foreach (var wxlFile in wxlFiles)
102 + {
103 + firstBuildArgs.Add("-loc");
104 + firstBuildArgs.Add(wxlFile);
105 + }
106 +
107 + foreach (var bindPath in this.BindPaths)
108 + {
109 + firstBuildArgs.Add("-bindpath");
110 + firstBuildArgs.Add(bindPath);
111 + }
112 +
113 + buildFunc(firstBuildArgs.ToArray());
114 +
115 + // Decompile built output.
116 + var decompileArgs = new List<string>
117 + {
118 + "msi", "decompile",
119 + outputPath,
120 + "-intermediateFolder", decompileIntermediateFolder,
121 + "-x", decompileExtractFolder,
122 + "-o", decompilePath
123 + };
124 +
125 + if (this.ExtensionType != null)
126 + {
127 + decompileArgs.Add("-ext");
128 + decompileArgs.Add(Path.GetFullPath(new Uri(this.ExtensionType.Assembly.CodeBase).LocalPath));
129 + }
130 +
131 + decompileFunc(decompileArgs.ToArray());
132 +
133 + // Build decompiled output.
134 + var secondBuildArgs = new List<string>
135 + {
136 + "build",
137 + decompilePath,
138 + "-o", decompileBuildPath,
139 + "-intermediateFolder", decompileIntermediateFolder
140 + };
141 +
142 + if (this.ExtensionType != null)
143 + {
144 + secondBuildArgs.Add("-ext");
145 + secondBuildArgs.Add(Path.GetFullPath(new Uri(this.ExtensionType.Assembly.CodeBase).LocalPath));
146 + }
147 +
148 + secondBuildArgs.Add("-bindpath");
149 + secondBuildArgs.Add(outputFolder);
150 +
151 + secondBuildArgs.Add("-bindpath");
152 + secondBuildArgs.Add(decompileExtractFolder);
153 +
154 + buildFunc(secondBuildArgs.ToArray());
155 + }
156 + }
157 }
158 }
src/wix/WixToolset.Core.Burn/BurnExtensionFactory.cs
+1 -1
@@ -22,7 +22,7 @@ namespace WixToolset.Core.Burn
22 {
23 extension = new BurnExtensionCommandLine(this.ServiceProvider);
24 }
25 - if (extensionType == typeof(IBackendFactory))
25 + else if (extensionType == typeof(IBackendFactory))
26 {
27 extension = new BurnBackendFactory();
28 }
src/wix/WixToolset.Core.WindowsInstaller/CommandLine/DecompilerSubcommand.cs
+6 -1
@@ -66,11 +66,16 @@ namespace WixToolset.Core.WindowsInstaller.CommandLine
66 this.OutputPath = Path.ChangeExtension(this.InputPath, ".wxs");
67 }
68
69 + var extensionManager = this.ServiceProvider.GetService<IExtensionManager>();
70 + var creator = this.ServiceProvider.GetService<ISymbolDefinitionCreator>();
71 +
72 var context = this.ServiceProvider.GetService<IWindowsInstallerDecompileContext>();
70 - context.Extensions = this.ServiceProvider.GetService<IExtensionManager>().GetServices<IWindowsInstallerDecompilerExtension>();
73 + context.Extensions = extensionManager.GetServices<IWindowsInstallerDecompilerExtension>();
74 + context.ExtensionData = extensionManager.GetServices<IExtensionData>();
75 context.DecompilePath = this.InputPath;
76 context.DecompileType = decompileType;
77 context.IntermediateFolder = this.IntermediateFolder;
78 + context.SymbolDefinitionCreator = creator;
79 context.OutputPath = this.OutputPath;
80
81 context.ExtractFolder = this.ExportBasePath ?? this.IntermediateFolder;
src/wix/WixToolset.Core.WindowsInstaller/Decompile/DecompileMsiOrMsmCommand.cs deleted
-91
@@ -1,91 +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.WindowsInstaller.Decompile
4 -{
5 - using System;
6 - using System.Collections.Generic;
7 - using System.ComponentModel;
8 - using System.IO;
9 - using System.Linq;
10 - using WixToolset.Core.Native.Msi;
11 - using WixToolset.Core.WindowsInstaller.Unbind;
12 - using WixToolset.Data;
13 - using WixToolset.Data.WindowsInstaller;
14 - using WixToolset.Extensibility.Data;
15 - using WixToolset.Extensibility.Services;
16 -
17 - internal class DecompileMsiOrMsmCommand
18 - {
19 - public DecompileMsiOrMsmCommand(IWindowsInstallerDecompileContext context)
20 - {
21 - this.Context = context;
22 - this.Messaging = context.ServiceProvider.GetService<IMessaging>();
23 - }
24 -
25 - private IWindowsInstallerDecompileContext Context { get; }
26 -
27 - private IMessaging Messaging { get; }
28 -
29 - public IWindowsInstallerDecompileResult Execute()
30 - {
31 - var result = this.Context.ServiceProvider.GetService<IWindowsInstallerDecompileResult>();
32 -
33 - try
34 - {
35 - using (var database = new Database(this.Context.DecompilePath, OpenDatabase.ReadOnly))
36 - {
37 - // Delete the directory and its files to prevent cab extraction failure due to an existing file.
38 - if (Directory.Exists(this.Context.ExtractFolder))
39 - {
40 - Directory.Delete(this.Context.ExtractFolder, true);
41 - }
42 -
43 - var backendHelper = this.Context.ServiceProvider.GetService<IBackendHelper>();
44 -
45 - var unbindCommand = new UnbindDatabaseCommand(this.Messaging, backendHelper, database, this.Context.DecompilePath, this.Context.DecompileType, this.Context.ExtractFolder, this.Context.IntermediateFolder, this.Context.IsAdminImage, suppressDemodularization: false, skipSummaryInfo: false);
46 - var output = unbindCommand.Execute();
47 - var extractedFilePaths = new List<string>(unbindCommand.ExportedFiles);
48 -
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);
53 -
54 - // extract the files from the cabinets
55 - if (!String.IsNullOrEmpty(this.Context.ExtractFolder) && !this.Context.SuppressExtractCabinets)
56 - {
57 - var fileDirectory = String.IsNullOrEmpty(this.Context.CabinetExtractFolder) ? Path.Combine(this.Context.ExtractFolder, "File") : this.Context.CabinetExtractFolder;
58 -
59 - var extractCommand = new ExtractCabinetsCommand(output, database, this.Context.DecompilePath, fileDirectory, this.Context.IntermediateFolder, this.Context.TreatProductAsModule);
60 - extractCommand.Execute();
61 -
62 - extractedFilePaths.AddRange(extractCommand.ExtractedFiles);
63 - result.ExtractedFilePaths = extractedFilePaths;
64 - }
65 - else
66 - {
67 - result.ExtractedFilePaths = new string[0];
68 - }
69 - }
70 - }
71 - catch (Win32Exception e)
72 - {
73 - if (0x6E == e.NativeErrorCode) // ERROR_OPEN_FAILED
74 - {
75 - throw new WixException(ErrorMessages.OpenDatabaseFailed(this.Context.DecompilePath));
76 - }
77 -
78 - throw;
79 - }
80 -
81 - return result;
82 - }
83 -
84 - private static Platform? GetPlatformFromOutput(WindowsInstallerData output)
85 - {
86 - var template = output.Tables["_SummaryInformation"]?.Rows.SingleOrDefault(row => row.FieldAsInteger(0) == 7)?.FieldAsString(1);
87 -
88 - return Decompiler.GetPlatformFromTemplateSummaryInformation(template?.Split(';'));
89 - }
90 - }
91 -}
src/wix/WixToolset.Core.WindowsInstaller/Decompile/Decompiler.cs
+326 -336
@@ -43,11 +43,14 @@ 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<IWindowsInstallerDecompilerExtension> extensions, string baseSourcePath, bool suppressCustomTables, bool suppressDroppingEmptyTables, bool suppressRelativeActionSequencing, bool suppressUI, bool treatProductAsModule)
46 + public Decompiler(IMessaging messaging, IBackendHelper backendHelper, IWindowsInstallerDecompilerHelper decompilerHelper, IEnumerable<IWindowsInstallerDecompilerExtension> extensions, IEnumerable<IExtensionData> extensionData, ISymbolDefinitionCreator creator, string baseSourcePath, bool suppressCustomTables, bool suppressDroppingEmptyTables, bool suppressRelativeActionSequencing, bool suppressUI, bool treatProductAsModule)
47 {
48 this.Messaging = messaging;
49 this.BackendHelper = backendHelper;
50 + this.DecompilerHelper = decompilerHelper;
51 this.Extensions = extensions;
52 + this.ExtensionData = extensionData;
53 + this.SymbolDefinitionCreator = creator;
54 this.BaseSourcePath = baseSourcePath ?? "SourceDir";
55 this.SuppressCustomTables = suppressCustomTables;
56 this.SuppressDroppingEmptyTables = suppressDroppingEmptyTables;
@@ -65,8 +68,14 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
68
69 private IBackendHelper BackendHelper { get; }
70
71 + private IWindowsInstallerDecompilerHelper DecompilerHelper { get; }
72 +
73 private IEnumerable<IWindowsInstallerDecompilerExtension> Extensions { get; }
74
75 + private IEnumerable<IExtensionData> ExtensionData { get; }
76 +
77 + private ISymbolDefinitionCreator SymbolDefinitionCreator { get; }
78 +
79 private Dictionary<string, IWindowsInstallerDecompilerExtension> ExtensionsByTableName { get; }
80
81 private string BaseSourcePath { get; }
@@ -87,8 +96,6 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
96
97 private bool Compressed { get; set; }
98
90 - private XElement RootElement { get; set; }
91 -
99 private TableDefinitionCollection TableDefinitions { get; }
100
101 private bool ShortNames { get; set; }
@@ -101,8 +108,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
108 {
109 if (null == this.uiElement)
110 {
104 - this.uiElement = new XElement(Names.UIElement);
105 - this.RootElement.Add(this.uiElement);
111 + this.uiElement = this.DecompilerHelper.AddElementToRoot(new XElement(Names.UIElement));
112 }
113
114 return this.uiElement;
@@ -111,8 +117,6 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
117
118 private Dictionary<string, XElement> Singletons { get; } = new Dictionary<string, XElement>();
119
114 - private Dictionary<string, XElement> IndexedElements { get; } = new Dictionary<string, XElement>();
115 -
120 private Dictionary<string, XElement> PatchTargetFiles { get; } = new Dictionary<string, XElement>();
121
122 /// <summary>
@@ -122,13 +126,23 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
126 /// <returns>The serialized WiX source code.</returns>
127 public XDocument Decompile(WindowsInstallerData output)
128 {
125 - if (null == output)
129 + this.OutputType = output.Type;
130 +
131 + switch (this.OutputType)
132 {
127 - throw new ArgumentNullException(nameof(output));
133 + case OutputType.Module:
134 + this.DecompilerHelper.RootElement = new XElement(Names.ModuleElement);
135 + break;
136 + case OutputType.PatchCreation:
137 + this.DecompilerHelper.RootElement = new XElement(Names.PatchCreationElement);
138 + break;
139 + case OutputType.Product:
140 + this.DecompilerHelper.RootElement = new XElement(Names.PackageElement);
141 + break;
142 + default:
143 + throw new InvalidOperationException("Unknown output type.");
144 }
145
130 - this.OutputType = output.Type;
131 -
146 // collect the table definitions from the output
147 this.TableDefinitions.Clear();
148 foreach (var table in output.Tables)
@@ -146,30 +160,11 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
160 }
161
162 // add any missing extension table definitions
149 -#if TODO_DECOMPILER_EXTENSIONS
163 foreach (var extension in this.Extensions)
164 {
152 - this.AddExtension(extension);
153 - }
154 -#endif
155 -
156 - switch (this.OutputType)
157 - {
158 - case OutputType.Module:
159 - this.RootElement = new XElement(Names.ModuleElement);
160 - break;
161 - case OutputType.PatchCreation:
162 - this.RootElement = new XElement(Names.PatchCreationElement);
163 - break;
164 - case OutputType.Product:
165 - this.RootElement = new XElement(Names.PackageElement);
166 - break;
167 - default:
168 - throw new InvalidOperationException("Unknown output type.");
165 + this.AddExtensionTableDefinitions(extension);
166 }
167
171 - var xWix = new XElement(Names.WixElement, this.RootElement);
172 -
168 // try to decompile the database file
169 // stop processing if an error previously occurred
170 if (this.Messaging.EncounteredError)
@@ -192,16 +187,14 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
187 this.FinalizeDecompile(output.Tables);
188
189 // return the XML document only if decompilation completed successfully
195 - var document = new XDocument(xWix);
196 - return this.Messaging.EncounteredError ? null : document;
190 + return this.Messaging.EncounteredError ? null : new XDocument(new XElement(Names.WixElement, this.DecompilerHelper.RootElement));
191 }
192
199 -#if TODO_DECOMPILER_EXTENSIONS
200 - private void AddExtension(IWindowsInstallerBackendDecompilerExtension extension)
193 + private void AddExtensionTableDefinitions(IWindowsInstallerDecompilerExtension extension)
194 {
195 if (null != extension.TableDefinitions)
196 {
204 - foreach (TableDefinition tableDefinition in extension.TableDefinitions)
197 + foreach (var tableDefinition in extension.TableDefinitions)
198 {
199 if (!this.ExtensionsByTableName.ContainsKey(tableDefinition.Name))
200 {
@@ -214,7 +207,6 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
207 }
208 }
209 }
217 -#endif
210
211 internal static Platform? GetPlatformFromTemplateSummaryInformation(string[] template)
212 {
@@ -234,75 +226,10 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
226 return null;
227 }
228
237 - /// <summary>
238 - /// Gets the element corresponding to the row it came from.
239 - /// </summary>
240 - /// <param name="row">The row corresponding to the element.</param>
241 - /// <returns>The indexed element.</returns>
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.
249 - /// </summary>
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>
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.
260 - /// </summary>
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>
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.
271 - /// </summary>
272 - /// <param name="table">The table corresponding to the element.</param>
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>
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>
286 - private void IndexElement(Row row, XElement element)
287 - {
288 - this.IndexedElements.Add(String.Concat(row.TableDefinition.Name, ':', row.GetPrimaryKey(DecompilerConstants.PrimaryKeyDelimiter)), element);
289 - }
290 -
291 - /// <summary>
292 - /// Index an element by its corresponding row.
293 - /// </summary>
294 - /// <param name="element">The element to index.</param>
295 - /// <param name="table"></param>
296 - /// <param name="primaryKey"></param>
297 - private void IndexElement(XElement element, string table, params string[] primaryKey)
298 - {
299 - this.IndexedElements.Add(String.Concat(table, ':', String.Join(DecompilerConstants.PrimaryKeyDelimiterString, primaryKey)), element);
300 - }
301 -
229 private Dictionary<string, List<XElement>> IndexTableOneToMany(IEnumerable<Row> rows, int column = 0)
230 {
231 return rows
305 - .ToLookup(row => row.FieldAsString(column), row => this.GetIndexedElement(row))
232 + .ToLookup(row => row.FieldAsString(column), row => this.DecompilerHelper.GetIndexedElement(row))
233 .ToDictionary(lookup => lookup.Key, lookup => lookup.ToList());
234 }
235
@@ -319,7 +246,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
246 private void AddChildToParent(string parentName, XElement xChild, Row row, int column)
247 {
248 var key = row.FieldAsString(column);
322 - if (this.TryGetIndexedElement(parentName, out var xParent, key))
249 + if (this.DecompilerHelper.TryGetIndexedElement(parentName, key, out var xParent))
250 {
251 xParent.Add(xChild);
252 }
@@ -419,7 +346,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
346 {
347 XElement xAction;
348
422 - if (this.TryGetIndexedElement("CustomAction", out var _, actionSymbol.Action)) // custom action
349 + if (this.DecompilerHelper.TryGetIndexedElement("CustomAction", actionSymbol.Action, out var _)) // custom action
350 {
351 xAction = new XElement(Names.CustomElement,
352 new XAttribute("Action", actionSymbol.Action),
@@ -455,7 +382,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
382 break;
383 }
384 }
458 - else if (this.TryGetIndexedElement("Dialog", out var _, actionSymbol.Action)) // dialog
385 + else if (this.DecompilerHelper.TryGetIndexedElement("Dialog", actionSymbol.Action, out var _)) // dialog
386 {
387 xAction = new XElement(Names.CustomElement,
388 new XAttribute("Dialog", actionSymbol.Action),
@@ -495,7 +422,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
422 {
423 xSequence = new XElement(Names.WxsNamespace + sequenceTable);
424
498 - this.RootElement.Add(xSequence);
425 + this.DecompilerHelper.AddElementToRoot(xSequence);
426 this.Singletons.Add(sequenceTable, xSequence);
427 }
428
@@ -669,14 +596,12 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
596 /// <returns>The property element.</returns>
597 private XElement EnsureProperty(string id)
598 {
672 - XElement xProperty;
673 -
674 - if (!this.TryGetIndexedElement("Property", out xProperty, id))
599 + if (!this.DecompilerHelper.TryGetIndexedElement("Property", id, out var xProperty))
600 {
601 xProperty = new XElement(Names.PropertyElement, new XAttribute("Id", id));
602
678 - this.RootElement.Add(xProperty);
679 - this.IndexElement(xProperty, "Property", id);
603 + this.DecompilerHelper.AddElementToRoot(xProperty);
604 + this.DecompilerHelper.IndexElement("Property", id, xProperty);
605 }
606
607 return xProperty;
@@ -713,6 +638,11 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
638 this.FinalizeSequenceTables(tables);
639 this.FinalizeVerbTable(tables);
640 }
641 +
642 + foreach (var extension in this.Extensions)
643 + {
644 + extension.PostDecompileTables(tables);
645 + }
646 }
647
648 /// <summary>
@@ -743,7 +673,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
673 {
674 foreach (var row in controlTable.Rows)
675 {
746 - var xControl = this.GetIndexedElement(row);
676 + var xControl = this.DecompilerHelper.GetIndexedElement(row);
677
678 if ("CheckBox" == row.FieldAsString(2))
679 {
@@ -791,9 +721,9 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
721 {
722 // Add the TARGETDIR StandardDirectory if a component is directly parented there.
723 if (componentTable.Rows.Any(row => row.FieldAsString(2) == "TARGETDIR")
794 - && this.TryGetIndexedElement("Directory", out var xDirectory, "TARGETDIR"))
724 + && this.DecompilerHelper.TryGetIndexedElement("Directory", "TARGETDIR", out var xDirectory))
725 {
796 - this.RootElement.Add(xDirectory);
726 + this.DecompilerHelper.AddElementToRoot(xDirectory);
727 }
728
729 foreach (var row in componentTable.Rows)
@@ -803,12 +733,12 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
733
734 if (String.IsNullOrEmpty(keyPath))
735 {
806 - var xComponent = this.GetIndexedElement("Component", row.FieldAsString(0));
736 + var xComponent = this.DecompilerHelper.GetIndexedElement("Component", row.FieldAsString(0));
737 xComponent.SetAttributeValue("KeyPath", "yes");
738 }
739 else if (WindowsInstallerConstants.MsidbComponentAttributesRegistryKeyPath == (attributes & WindowsInstallerConstants.MsidbComponentAttributesRegistryKeyPath))
740 {
811 - if (this.TryGetIndexedElement("Registry", out var xRegistry, keyPath))
741 + if (this.DecompilerHelper.TryGetIndexedElement("Registry", keyPath, out var xRegistry))
742 {
743 if (xRegistry.Name.LocalName == "RegistryValue")
744 {
@@ -826,7 +756,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
756 }
757 else if (WindowsInstallerConstants.MsidbComponentAttributesODBCDataSource == (attributes & WindowsInstallerConstants.MsidbComponentAttributesODBCDataSource))
758 {
829 - if (this.TryGetIndexedElement("ODBCDataSource", out var xOdbcDataSource, keyPath))
759 + if (this.DecompilerHelper.TryGetIndexedElement("ODBCDataSource", keyPath, out var xOdbcDataSource))
760 {
761 xOdbcDataSource.SetAttributeValue("KeyPath", "yes");
762 }
@@ -837,7 +767,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
767 }
768 else
769 {
840 - if (this.TryGetIndexedElement("File", out var xFile, keyPath))
770 + if (this.DecompilerHelper.TryGetIndexedElement("File", keyPath, out var xFile))
771 {
772 xFile.SetAttributeValue("KeyPath", "yes");
773 }
@@ -854,8 +784,8 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
784 {
785 foreach (FileRow fileRow in fileTable.Rows)
786 {
857 - if (this.TryGetIndexedElement("Component", out var xComponent, fileRow.Component)
858 - && this.TryGetIndexedElement(fileRow, out var xFile))
787 + if (this.DecompilerHelper.TryGetIndexedElement("Component", fileRow.Component, out var xComponent)
788 + && this.DecompilerHelper.TryGetIndexedElement(fileRow, out var xFile))
789 {
790 xComponent.Add(xFile);
791 }
@@ -871,8 +801,8 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
801 {
802 foreach (var row in odbcDataSourceTable.Rows)
803 {
874 - if (this.TryGetIndexedElement("Component", out var xComponent, row.FieldAsString(1))
875 - && this.TryGetIndexedElement(row, out var xOdbcDataSource))
804 + if (this.DecompilerHelper.TryGetIndexedElement("Component", row.FieldAsString(1), out var xComponent)
805 + && this.DecompilerHelper.TryGetIndexedElement(row, out var xOdbcDataSource))
806 {
807 xComponent.Add(xOdbcDataSource);
808 }
@@ -888,8 +818,8 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
818 {
819 foreach (var row in registryTable.Rows)
820 {
891 - if (this.TryGetIndexedElement("Component", out var xComponent, row.FieldAsString(5))
892 - && this.TryGetIndexedElement(row, out var xRegistry))
821 + if (this.DecompilerHelper.TryGetIndexedElement("Component", row.FieldAsString(5), out var xComponent)
822 + && this.DecompilerHelper.TryGetIndexedElement(row, out var xRegistry))
823 {
824 xComponent.Add(xRegistry);
825 }
@@ -927,10 +857,10 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
857 {
858 foreach (var dialogRow in dialogTable.Rows)
859 {
930 - var xDialog = this.GetIndexedElement(dialogRow);
860 + var xDialog = this.DecompilerHelper.GetIndexedElement(dialogRow);
861 var dialogId = dialogRow.FieldAsString(0);
862
933 - if (!this.TryGetIndexedElement("Control", out var xControl, dialogId, dialogRow.FieldAsString(7)))
863 + if (!this.DecompilerHelper.TryGetIndexedElement("Control", dialogId, dialogRow.FieldAsString(7), out var xControl))
864 {
865 this.Messaging.Write(WarningMessages.ExpectedForeignRow(dialogRow.SourceLineNumbers, "Dialog", dialogRow.GetPrimaryKey(DecompilerConstants.PrimaryKeyDelimiter), "Dialog", dialogId, "Control_First", dialogRow.FieldAsString(7), "Control"));
866 }
@@ -949,7 +879,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
879 var controlNext = controlRow.FieldAsString(10);
880 if (!String.IsNullOrEmpty(controlNext))
881 {
952 - if (this.TryGetIndexedElement("Control", out xControl, dialogId, controlNext))
882 + if (this.DecompilerHelper.TryGetIndexedElement("Control", dialogId, controlNext, out xControl))
883 {
884 // looped back to the first control in the dialog
885 if (addedControls.Contains(xControl))
@@ -972,7 +902,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
902 var controlDefault = dialogRow.FieldAsString(8);
903 if (!String.IsNullOrEmpty(controlDefault))
904 {
975 - if (this.TryGetIndexedElement("Control", out var xDefaultControl, dialogId, controlDefault))
905 + if (this.DecompilerHelper.TryGetIndexedElement("Control", dialogId, controlDefault, out var xDefaultControl))
906 {
907 xDefaultControl.SetAttributeValue("Default", "yes");
908 }
@@ -986,7 +916,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
916 var controlCancel = dialogRow.FieldAsString(8);
917 if (!String.IsNullOrEmpty(controlCancel))
918 {
989 - if (this.TryGetIndexedElement("Control", out var xCancelControl, dialogId, controlCancel))
919 + if (this.DecompilerHelper.TryGetIndexedElement("Control", dialogId, controlCancel, out var xCancelControl))
920 {
921 xCancelControl.SetAttributeValue("Cancel", "yes");
922 }
@@ -1004,13 +934,13 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
934 foreach (var controlRow in controlTable.Rows)
935 {
936 var dialogId = controlRow.FieldAsString(0);
1007 - if (!this.TryGetIndexedElement("Dialog", out var xDialog, dialogId))
937 + if (!this.DecompilerHelper.TryGetIndexedElement("Dialog", dialogId, out var xDialog))
938 {
939 this.Messaging.Write(WarningMessages.ExpectedForeignRow(controlRow.SourceLineNumbers, "Control", controlRow.GetPrimaryKey(DecompilerConstants.PrimaryKeyDelimiter), "Dialog_", dialogId, "Dialog"));
940 continue;
941 }
942
1013 - var xControl = this.GetIndexedElement(controlRow);
943 + var xControl = this.DecompilerHelper.GetIndexedElement(controlRow);
944 if (!addedControls.Contains(xControl))
945 {
946 xControl.SetAttributeValue("TabSkip", "yes");
@@ -1035,11 +965,11 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
965 {
966 foreach (var row in duplicateFileTable.Rows)
967 {
1038 - var xCopyFile = this.GetIndexedElement(row);
968 + var xCopyFile = this.DecompilerHelper.GetIndexedElement(row);
969 var destination = row.FieldAsString(4);
970 if (!String.IsNullOrEmpty(destination))
971 {
1042 - if (this.TryGetIndexedElement("Directory", out var _, destination))
972 + if (this.DecompilerHelper.TryGetIndexedElement("Directory", destination, out var _))
973 {
974 xCopyFile.SetAttributeValue("DestinationDirectory", destination);
975 }
@@ -1056,11 +986,11 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
986 {
987 foreach (var row in moveFileTable.Rows)
988 {
1059 - var xCopyFile = this.GetIndexedElement(row);
989 + var xCopyFile = this.DecompilerHelper.GetIndexedElement(row);
990 var source = row.FieldAsString(4);
991 if (!String.IsNullOrEmpty(source))
992 {
1063 - if (this.TryGetIndexedElement("Directory", out var _, source))
993 + if (this.DecompilerHelper.TryGetIndexedElement("Directory", source, out var _))
994 {
995 xCopyFile.SetAttributeValue("SourceDirectory", source);
996 }
@@ -1071,7 +1001,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
1001 }
1002
1003 var destination = row.FieldAsString(5);
1074 - if (this.TryGetIndexedElement("Directory", out var _, destination))
1004 + if (this.DecompilerHelper.TryGetIndexedElement("Directory", destination, out var _))
1005 {
1006 xCopyFile.SetAttributeValue("DestinationDirectory", destination);
1007 }
@@ -1134,7 +1064,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
1064 // TODO: warn about mismatch between columns
1065 }
1066
1137 - this.IndexElement(row, xProtectRange);
1067 + this.DecompilerHelper.IndexElement(row, xProtectRange);
1068 }
1069 }
1070
@@ -1144,8 +1074,8 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
1074 {
1075 foreach (var row in externalFilesTable.Rows)
1076 {
1147 - if (this.TryGetIndexedElement(row, out var xExternalFile)
1148 - && this.TryGetIndexedElement("FamilyFileRanges", out var xProtectRange, row.FieldAsString(0), row.FieldAsString(0)))
1077 + if (this.DecompilerHelper.TryGetIndexedElement(row, out var xExternalFile)
1078 + && this.DecompilerHelper.TryGetIndexedElement("FamilyFileRanges", row.FieldAsString(0), row.FieldAsString(0), out var xProtectRange))
1079 {
1080 xExternalFile.Add(xProtectRange);
1081 usedProtectRanges.Add(xProtectRange);
@@ -1178,7 +1108,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
1108 continue;
1109 }
1110
1181 - if (this.TryGetIndexedElement("FamilyFileRanges", out var xProtectRange, upgradedImagesRow.FieldAsString(4), row.FieldAsString(1)))
1111 + if (this.DecompilerHelper.TryGetIndexedElement("FamilyFileRanges", upgradedImagesRow.FieldAsString(4), row.FieldAsString(1), out var xProtectRange))
1112 {
1113 xTargetFile.Add(xProtectRange);
1114 usedProtectRanges.Add(xProtectRange);
@@ -1190,7 +1120,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
1120 {
1121 foreach (var row in familyFileRangesTable.Rows)
1122 {
1193 - var xProtectRange = this.GetIndexedElement(row);
1123 + var xProtectRange = this.DecompilerHelper.GetIndexedElement(row);
1124
1125 if (!usedProtectRanges.Contains(xProtectRange))
1126 {
@@ -1276,7 +1206,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
1206 // set the disk identifiers and sources for files
1207 foreach (var fileRow in tables["File"]?.Rows.Cast<FileRow>() ?? Enumerable.Empty<FileRow>())
1208 {
1279 - var xFile = this.GetIndexedElement("File", fileRow.File);
1209 + var xFile = this.DecompilerHelper.GetIndexedElement("File", fileRow.File);
1210
1211 // Don't bother processing files that are orphaned (and won't show up in the output anyway)
1212 if (null != xFile.Parent)
@@ -1328,7 +1258,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
1258 // set the file assemblies and manifests
1259 foreach (var row in tables["MsiAssembly"]?.Rows ?? Enumerable.Empty<Row>())
1260 {
1331 - if (this.TryGetIndexedElement("Component", out var xComponent, row.FieldAsString(0)))
1261 + if (this.DecompilerHelper.TryGetIndexedElement("Component", row.FieldAsString(0), out var xComponent))
1262 {
1263 foreach (var xFile in xComponent.Elements(Names.FileElement).Where(x => x.Attribute("KeyPath")?.Value == "yes"))
1264 {
@@ -1346,8 +1276,8 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
1276 // nest the TypeLib elements
1277 foreach (var row in tables["TypeLib"]?.Rows ?? Enumerable.Empty<Row>())
1278 {
1349 - var xComponent = this.GetIndexedElement("Component", row.FieldAsString(2));
1350 - var xTypeLib = this.GetIndexedElement(row);
1279 + var xComponent = this.DecompilerHelper.GetIndexedElement("Component", row.FieldAsString(2));
1280 + var xTypeLib = this.DecompilerHelper.GetIndexedElement(row);
1281
1282 foreach (var xFile in xComponent.Elements(Names.FileElement).Where(x => x.Attribute("KeyPath")?.Value == "yes"))
1283 {
@@ -1374,7 +1304,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
1304 var mimeRef = row.FieldAsString(3);
1305 if (null != mimeRef)
1306 {
1377 - if (this.TryGetIndexedElement("MIME", out var xMime, mimeRef))
1307 + if (this.DecompilerHelper.TryGetIndexedElement("MIME", mimeRef, out var xMime))
1308 {
1309 xMime.SetAttributeValue("Default", "yes");
1310 }
@@ -1389,7 +1319,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
1319
1320 foreach (var row in tables["MIME"]?.Rows ?? Enumerable.Empty<Row>())
1321 {
1392 - var xMime = this.GetIndexedElement(row);
1322 + var xMime = this.DecompilerHelper.GetIndexedElement(row);
1323
1324 if (extensionsByExtensionId.TryGetValue(row.FieldAsString(1), out var xExtensions))
1325 {
@@ -1427,9 +1357,9 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
1357 foreach (var row in classRows)
1358 {
1359 var clsid = row.FieldAsString(0);
1430 - var xClass = this.GetIndexedElement(row);
1360 + var xClass = this.DecompilerHelper.GetIndexedElement(row);
1361
1432 - if (this.TryGetIndexedElement("ProgId", out var xProgId, row.FieldAsString(3)))
1362 + if (this.DecompilerHelper.TryGetIndexedElement("ProgId", row.FieldAsString(3), out var xProgId))
1363 {
1364 if (addedProgIds.TryGetValue(xProgId, out var progid))
1365 {
@@ -1451,7 +1381,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
1381 foreach (var row in tables["ProgId"]?.Rows ?? Enumerable.Empty<Row>())
1382 {
1383 var clsid = row.FieldAsString(2);
1454 - var xProgId = this.GetIndexedElement(row);
1384 + var xProgId = this.DecompilerHelper.GetIndexedElement(row);
1385
1386 if (!addedProgIds.ContainsKey(xProgId) && null != clsid && null == xProgId.Parent)
1387 {
@@ -1475,7 +1405,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
1405
1406 foreach (var row in tables["Extension"]?.Rows?.Where(row => row.FieldAsString(2) != null) ?? Enumerable.Empty<Row>())
1407 {
1478 - var xProgId = this.GetIndexedElement("ProgId", row.FieldAsString(2));
1408 + var xProgId = this.DecompilerHelper.GetIndexedElement("ProgId", row.FieldAsString(2));
1409
1410 // Haven't added the progId yet and it doesn't have a parent progId
1411 if (!addedProgIds.ContainsKey(xProgId) && null == xProgId.Parent)
@@ -1510,7 +1440,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
1440 var bits = row.FieldAsInteger(1);
1441 if (WindowsInstallerConstants.MsidbCustomActionTypeHideTarget == (bits & WindowsInstallerConstants.MsidbCustomActionTypeHideTarget)
1442 && WindowsInstallerConstants.MsidbCustomActionTypeInScript == (bits & WindowsInstallerConstants.MsidbCustomActionTypeInScript)
1513 - && this.TryGetIndexedElement("Property", out var xProperty, row.FieldAsString(0))
1443 + && this.DecompilerHelper.TryGetIndexedElement("Property", row.FieldAsString(0), out var xProperty)
1444 && String.IsNullOrEmpty(xProperty.Attribute("Value")?.Value)
1445 && xProperty.Attribute("Secure")?.Value != "yes"
1446 && xProperty.Attribute("SuppressModularization")?.Value != "yes")
@@ -1531,10 +1461,10 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
1461 {
1462 foreach (var row in tables["RemoveFile"]?.Rows ?? Enumerable.Empty<Row>())
1463 {
1534 - var xRemove = this.GetIndexedElement(row);
1464 + var xRemove = this.DecompilerHelper.GetIndexedElement(row);
1465 var property = row.FieldAsString(3);
1466
1537 - if (this.TryGetIndexedElement("Directory", out var _, property))
1467 + if (this.DecompilerHelper.TryGetIndexedElement("Directory", property, out var _))
1468 {
1469 xRemove.SetAttributeValue("Directory", property);
1470 }
@@ -1562,7 +1492,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
1492 {
1493 var id = row.FieldAsString(0);
1494 var table = row.FieldAsString(1);
1565 - var xPermission = this.GetIndexedElement(row);
1495 + var xPermission = this.DecompilerHelper.GetIndexedElement(row);
1496
1497 if ("CreateFolder" == table)
1498 {
@@ -1580,7 +1510,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
1510 }
1511 else
1512 {
1583 - if (this.TryGetIndexedElement(table, out var xParent, id))
1513 + if (this.DecompilerHelper.TryGetIndexedElement(table, id, out var xParent))
1514 {
1515 xParent.Add(xPermission);
1516 }
@@ -1651,12 +1581,12 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
1581 {
1582 var appSearches = IndexTable(tables["AppSearch"], keyColumn: 1, dataColumn: 0);
1583 var ccpSearches = IndexTable(tables["CCPSearch"], keyColumn: 0, dataColumn: null);
1654 - var drLocators = tables["DrLocator"]?.Rows.ToDictionary(row => this.GetIndexedElement(row), row => row);
1584 + var drLocators = tables["DrLocator"]?.Rows.ToDictionary(row => this.DecompilerHelper.GetIndexedElement(row), row => row);
1585
1586 var xComplianceCheck = new XElement(Names.ComplianceCheckElement);
1587 if (ccpSearches.Keys.Any(ccpSignature => !appSearches.ContainsKey(ccpSignature)))
1588 {
1659 - this.RootElement.Add(xComplianceCheck);
1589 + this.DecompilerHelper.AddElementToRoot(xComplianceCheck);
1590 }
1591
1592 // index the locator tables by their signatures
@@ -1703,7 +1633,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
1633 foreach (var locatorRow in locatorRows)
1634 {
1635 var used = true;
1706 - var xSearch = this.GetIndexedElement(locatorRow);
1636 + var xSearch = this.DecompilerHelper.GetIndexedElement(locatorRow);
1637
1638 if ("Signature" == locatorRow.TableDefinition.Name && 0 < xSignatureSearches.Count)
1639 {
@@ -1791,7 +1721,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
1721 {
1722 if ("DrLocator" == parentLocatorRow.TableDefinition.Name)
1723 {
1794 - var xParentSearch = this.GetIndexedElement(parentLocatorRow);
1724 + var xParentSearch = this.DecompilerHelper.GetIndexedElement(parentLocatorRow);
1725
1726 if (xParentSearch.HasElements)
1727 {
@@ -1824,7 +1754,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
1754 }
1755 else if ("RegLocator" == parentLocatorRow.TableDefinition.Name)
1756 {
1827 - var xParentSearch = this.GetIndexedElement(parentLocatorRow);
1757 + var xParentSearch = this.DecompilerHelper.GetIndexedElement(parentLocatorRow);
1758
1759 xParentSearch.Add(xSearch);
1760 xUsedSearches.Add(xSearch);
@@ -1982,11 +1912,11 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
1912
1913 foreach (var row in shortcutTable.Rows)
1914 {
1985 - var xShortcut = this.GetIndexedElement(row);
1915 + var xShortcut = this.DecompilerHelper.GetIndexedElement(row);
1916
1917 var target = row.FieldAsString(4);
1918
1989 - if (this.TryGetIndexedElement("Feature", out var _, target))
1919 + if (this.DecompilerHelper.TryGetIndexedElement("Feature", target, out var _))
1920 {
1921 xShortcut.SetAttributeValue("Advertise", "yes");
1922 this.SetPrimaryFeature(row, 4, 3);
@@ -2301,7 +2231,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
2231 xMajorUpgrade.SetAttributeValue("Schedule", scheduledType);
2232 }
2233
2304 - this.RootElement.Add(xMajorUpgrade);
2234 + this.DecompilerHelper.AddElementToRoot(xMajorUpgrade);
2235 }
2236 }
2237 }
@@ -2326,7 +2256,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
2256 {
2257 if (xExtensions.TryGetValue(row.FieldAsString(0), out var xVerbExtensions))
2258 {
2329 - var xVerb = this.GetIndexedElement(row);
2259 + var xVerb = this.DecompilerHelper.GetIndexedElement(row);
2260
2261 foreach (var xVerbExtension in xVerbExtensions)
2262 {
@@ -2351,8 +2281,9 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
2281 var sourcePath = new StringBuilder();
2282
2283 var component = xFile.Parent;
2284 + var xDirectory = component.Parent;
2285
2355 - for (var xDirectory = component.Parent; null != xDirectory && xDirectory.Name.LocalName == "Directory"; xDirectory = xDirectory.Parent)
2286 + while (xDirectory?.Name.LocalName == "Directory")
2287 {
2288 string name;
2289
@@ -2387,6 +2318,14 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
2318 sourcePath.Insert(0, Path.DirectorySeparatorChar);
2319 sourcePath.Insert(0, name);
2320 }
2321 +
2322 + xDirectory = xDirectory.Parent;
2323 + }
2324 +
2325 + if (xDirectory?.Name.LocalName == "StandardDirectory" && WindowsInstallerStandard.TryGetStandardDirectory(xDirectory.Attribute("Id").Value, out var standardDirectory))
2326 + {
2327 + sourcePath.Insert(0, Path.DirectorySeparatorChar);
2328 + sourcePath.Insert(0, standardDirectory.Name);
2329 }
2330
2331 return sourcePath.ToString();
@@ -2470,13 +2409,13 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
2409 this.ShortNames = false;
2410
2411 this.Singletons.Clear();
2473 - this.IndexedElements.Clear();
2412 + //this.IndexedElements.Clear();
2413 this.PatchTargetFiles.Clear();
2414
2415 // set the codepage if its not neutral (0)
2416 if (0 != codepage)
2417 {
2479 - this.RootElement.SetAttributeValue("Codepage", codepage);
2418 + this.DecompilerHelper.RootElement.SetAttributeValue("Codepage", codepage);
2419 }
2420
2421 if (this.OutputType == OutputType.Module)
@@ -2484,44 +2423,51 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
2423 var table = tables["_SummaryInformation"];
2424 var row = table.Rows.SingleOrDefault(r => r.FieldAsInteger(0) == 9);
2425 this.ModularizationGuid = row?.FieldAsString(1);
2487 - this.RootElement.SetAttributeValue("Guid", this.ModularizationGuid);
2426 + this.DecompilerHelper.RootElement.SetAttributeValue("Guid", this.ModularizationGuid);
2427 + }
2428 +
2429 + this.RemoveExtensionDataFromTables(tables);
2430 +
2431 + foreach (var extension in this.Extensions)
2432 + {
2433 + extension.PreDecompileTables(tables);
2434 }
2435 + }
2436 +
2437 + private void RemoveExtensionDataFromTables(TableIndexedCollection tables)
2438 + {
2439 + var tableDefinitionBySymbolDefinitionName = this.TableDefinitions.Where(t => t.SymbolDefinition != null).ToDictionary(t => t.SymbolDefinition.Name);
2440
2441 // index the rows from the extension libraries
2442 var indexedExtensionTables = new Dictionary<string, HashSet<string>>();
2492 -#if TODO_DECOMPILER_EXTENSIONS
2493 - foreach (var extension in this.Extensions)
2443 + foreach (var extension in this.ExtensionData)
2444 {
2445 // Get the optional library from the extension with the rows to be removed.
2496 - var library = extension.GetLibraryToRemove(this.tableDefinitions);
2446 + var library = extension.GetLibrary(this.SymbolDefinitionCreator);
2447 if (library != null)
2448 {
2499 - foreach (var row in library.Sections.SelectMany(s => s.Tables).SelectMany(t => t.Rows))
2449 + foreach (var symbol in library.Sections.SelectMany(s => s.Symbols))
2450 {
2501 - string primaryKey;
2502 - string tableName;
2503 -
2504 - // the Actions table needs to be handled specially
2505 - if (table.Name == "WixAction")
2451 + if (this.TryGetPrimaryKeyFromSymbol(tableDefinitionBySymbolDefinitionName, symbol, out var tableName, out var primaryKey))
2452 {
2507 - primaryKey = row.FieldAsString(1);
2508 - tableName = row.FieldAsString(0);
2509 -
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 -
2521 - if (primaryKey != null)
2522 - {
2523 - HashSet<string> indexedExtensionRows;
2524 - if (!indexedExtensionTables.TryGetValue(tableName, out indexedExtensionRows))
2453 + //// the Actions table needs to be handled specially
2454 + //if (table.Name == "WixAction")
2455 + //{
2456 + // primaryKey = symbol.FieldAsString(1);
2457 + // tableName = symbol.FieldAsString(0);
2458 +
2459 + // if (this.outputType == OutputType.Module)
2460 + // {
2461 + // tableName = "Module" + tableName;
2462 + // }
2463 + //}
2464 + //else
2465 + //{
2466 + // primaryKey = symbol.GetPrimaryKey(DecompilerConstants.PrimaryKeyDelimiter);
2467 + // tableName = table.Name;
2468 + //}
2469 +
2470 + if (!indexedExtensionTables.TryGetValue(tableName, out var indexedExtensionRows))
2471 {
2472 indexedExtensionRows = new HashSet<string>();
2473 indexedExtensionTables.Add(tableName, indexedExtensionRows);
@@ -2532,7 +2478,6 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
2478 }
2479 }
2480 }
2535 -#endif
2481
2482 // remove the rows from the extension libraries (to allow full round-tripping)
2483 foreach (var kvp in indexedExtensionTables)
@@ -2540,8 +2485,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
2485 var tableName = kvp.Key;
2486 var indexedExtensionRows = kvp.Value;
2487
2543 - var table = tables[tableName];
2544 - if (null != table)
2488 + if (tables.TryGetTable(tableName, out var table))
2489 {
2490 var originalRows = new RowDictionary<Row>(table);
2491
@@ -2550,7 +2494,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
2494
2495 foreach (var row in originalRows.Values)
2496 {
2553 - if (!indexedExtensionRows.Contains(row.GetPrimaryKey(DecompilerConstants.PrimaryKeyDelimiter)))
2497 + if (!indexedExtensionRows.Contains(row.GetPrimaryKey()))
2498 {
2499 table.Rows.Add(row);
2500 }
@@ -2559,6 +2503,55 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
2503 }
2504 }
2505
2506 + private bool TryGetPrimaryKeyFromSymbol(Dictionary<string, TableDefinition> tableDefinitionBySymbolDefinitionName, IntermediateSymbol symbol, out string tableName, out string primaryKey)
2507 + {
2508 + tableName = null;
2509 + primaryKey = null;
2510 +
2511 + if (symbol is WixActionSymbol actionSymbol)
2512 + {
2513 + tableName = actionSymbol.SequenceTable.WindowsInstallerTableName();
2514 + primaryKey = actionSymbol.Action;
2515 + return true;
2516 + }
2517 +
2518 + if (!tableDefinitionBySymbolDefinitionName.TryGetValue(symbol.Definition.Name, out var tableDefinition))
2519 + {
2520 + return false;
2521 + }
2522 +
2523 + tableName = tableDefinition.Name;
2524 +
2525 + if (tableDefinition.SymbolIdIsPrimaryKey)
2526 + {
2527 + primaryKey = symbol.Id.Id;
2528 + }
2529 + else
2530 + {
2531 + var sb = new StringBuilder();
2532 +
2533 + for (var i = 0; i < symbol.Fields.Length && i < tableDefinition.Columns.Length; ++i)
2534 + {
2535 + var column = tableDefinition.Columns[i];
2536 + var field = symbol.Fields[i];
2537 +
2538 + if (column.PrimaryKey)
2539 + {
2540 + if (sb.Length > 0)
2541 + {
2542 + sb.Append('/');
2543 + }
2544 +
2545 + sb.Append(field.AsString());
2546 + }
2547 + }
2548 +
2549 + primaryKey = sb.ToString();
2550 + }
2551 +
2552 + return true;
2553 + }
2554 +
2555 /// <summary>
2556 /// Decompile the tables.
2557 /// </summary>
@@ -2581,7 +2574,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
2574 // empty tables may be kept with EnsureTable if the user set the proper option
2575 if (0 == table.Rows.Count && this.SuppressDroppingEmptyTables)
2576 {
2584 - this.RootElement.Add(new XElement(Names.EnsureTableElement, new XAttribute("Id", table.Name)));
2577 + this.DecompilerHelper.AddElementToRoot(new XElement(Names.EnsureTableElement, new XAttribute("Id", table.Name)));
2578 }
2579
2580 switch (table.Name)
@@ -2901,14 +2894,11 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
2894 break;
2895
2896 default:
2904 -#if TODO_DECOMPILER_EXTENSIONS
2905 - if (this.ExtensionsByTableName.TryGetValue(table.Name, out var extension)
2906 - {
2907 - extension.DecompileTable(table);
2908 - }
2909 - else
2910 -#endif
2911 - if (!this.SuppressCustomTables)
2897 + if (this.ExtensionsByTableName.TryGetValue(table.Name, out var extension))
2898 + {
2899 + extension.TryDecompileTable(table);
2900 + }
2901 + else if (!this.SuppressCustomTables)
2902 {
2903 this.DecompileCustomTable(table);
2904 }
@@ -3039,7 +3029,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
3029 break;
3030 case 3:
3031 {
3042 - var productName = this.RootElement.Attribute("Name")?.Value;
3032 + var productName = this.DecompilerHelper.RootElement.Attribute("Name")?.Value;
3033 if (value != productName)
3034 {
3035 xSummaryInformation.SetAttributeValue("Description", value);
@@ -3048,7 +3038,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
3038 }
3039 case 4:
3040 {
3051 - var productManufacturer = this.RootElement.Attribute("Manufacturer")?.Value;
3041 + var productManufacturer = this.DecompilerHelper.RootElement.Attribute("Manufacturer")?.Value;
3042 if (value != productManufacturer)
3043 {
3044 xSummaryInformation.SetAttributeValue("Manufacturer", value);
@@ -3065,7 +3055,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
3055 var template = value.Split(';');
3056 if (0 < template.Length && 0 < template[template.Length - 1].Length)
3057 {
3068 - this.RootElement.SetAttributeValue("Language", template[template.Length - 1]);
3058 + this.DecompilerHelper.RootElement.SetAttributeValue("Language", template[template.Length - 1]);
3059 }
3060 break;
3061 case 14:
@@ -3073,7 +3063,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
3063 // Default InstallerVersion.
3064 if (installerVersion != 500)
3065 {
3076 - this.RootElement.SetAttributeValue("InstallerVersion", installerVersion);
3066 + this.DecompilerHelper.RootElement.SetAttributeValue("InstallerVersion", installerVersion);
3067 }
3068 break;
3069 case 15:
@@ -3083,29 +3073,24 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
3073 this.ShortNames = true;
3074 if (OutputType.Product == this.OutputType)
3075 {
3086 - this.RootElement.SetAttributeValue("ShortNames", "yes");
3076 + this.DecompilerHelper.RootElement.SetAttributeValue("ShortNames", "yes");
3077 }
3078 }
3079
3080 if (0x2 == (wordCount & 0x2))
3081 {
3082 this.Compressed = true;
3093 -
3094 - if (OutputType.Product == this.OutputType)
3095 - {
3096 - this.RootElement.SetAttributeValue("Compressed", "yes");
3097 - }
3083 }
3084
3085 if (OutputType.Product == this.OutputType)
3086 {
3087 if (0x8 == (wordCount & 0x8))
3088 {
3104 - this.RootElement.SetAttributeValue("Scope", "perUser");
3089 + this.DecompilerHelper.RootElement.SetAttributeValue("Scope", "perUser");
3090 }
3091 else
3092 {
3108 - var xAllUsers = this.RootElement.Elements(Names.PropertyElement).SingleOrDefault(p => p.Attribute("Id")?.Value == "ALLUSERS");
3093 + var xAllUsers = this.DecompilerHelper.RootElement.Elements(Names.PropertyElement).SingleOrDefault(p => p.Attribute("Id")?.Value == "ALLUSERS");
3094 if (xAllUsers?.Attribute("Value")?.Value == "1")
3095 {
3096 xAllUsers?.Remove();
@@ -3118,9 +3103,14 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
3103 }
3104 }
3105
3106 + if (OutputType.Product == this.OutputType && !this.Compressed)
3107 + {
3108 + this.DecompilerHelper.RootElement.SetAttributeValue("Compressed", "no");
3109 + }
3110 +
3111 if (xSummaryInformation.HasAttributes)
3112 {
3123 - this.RootElement.Add(xSummaryInformation);
3113 + this.DecompilerHelper.AddElementToRoot(xSummaryInformation);
3114 }
3115 }
3116 else
@@ -3173,7 +3163,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
3163 }
3164 }
3165
3176 - this.RootElement.Add(xPatchInformation);
3166 + this.DecompilerHelper.AddElementToRoot(xPatchInformation);
3167 }
3168 }
3169
@@ -3212,8 +3202,8 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
3202 row.IsColumnNull(5) || row.FieldAsInteger(5) != 1 ? null : new XAttribute("ActivateAtStorage", "yes"),
3203 row.IsColumnNull(6) || row.FieldAsInteger(6) != 1 ? null : new XAttribute("RunAsInteractiveUser", "yes"));
3204
3215 - this.RootElement.Add(appId);
3216 - this.IndexElement(row, appId);
3205 + this.DecompilerHelper.AddElementToRoot(appId);
3206 + this.DecompilerHelper.IndexElement(row, appId);
3207 }
3208 }
3209
@@ -3239,7 +3229,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
3229 SetControlAttributes(bbControlRow.Attributes, xControl);
3230 }
3231
3242 - if (this.TryGetIndexedElement("Billboard", out var xBillboard, bbControlRow.Billboard))
3232 + if (this.DecompilerHelper.TryGetIndexedElement("Billboard", bbControlRow.Billboard, out var xBillboard))
3233 {
3234 xBillboard.Add(xControl);
3235 }
@@ -3264,7 +3254,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
3254 new XAttribute("Id", row.FieldAsString(0)),
3255 new XAttribute("Feature", row.FieldAsString(1)));
3256
3267 - this.IndexElement(row, xBillboard);
3257 + this.DecompilerHelper.IndexElement(row, xBillboard);
3258 billboards.Add(String.Format(CultureInfo.InvariantCulture, "{0}|{1:0000000000}", row[0], row[3]), row);
3259 }
3260
@@ -3272,7 +3262,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
3262
3263 foreach (var row in billboards.Values)
3264 {
3275 - var xBillboard = this.GetIndexedElement(row);
3265 + var xBillboard = this.DecompilerHelper.GetIndexedElement(row);
3266
3267 if (!billboardActions.TryGetValue(row.FieldAsString(2), out var xBillboardAction))
3268 {
@@ -3299,7 +3289,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
3289 new XAttribute("Id", row.FieldAsString(0)),
3290 new XAttribute("SourceFile", row.FieldAsString(1)));
3291
3302 - this.RootElement.Add(xBinary);
3292 + this.DecompilerHelper.AddElementToRoot(xBinary);
3293 }
3294 }
3295
@@ -3311,7 +3301,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
3301 {
3302 foreach (var row in table.Rows)
3303 {
3314 - if (this.TryGetIndexedElement("File", out var xFile, row.FieldAsString(0)))
3304 + if (this.DecompilerHelper.TryGetIndexedElement("File", row.FieldAsString(0), out var xFile))
3305 {
3306 xFile.SetAttributeValue("BindPath", row.FieldAsString(1));
3307 }
@@ -3389,7 +3379,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
3379 }
3380
3381 this.AddChildToParent("Component", xClass, row, 2);
3392 - this.IndexElement(row, xClass);
3382 + this.DecompilerHelper.IndexElement(row, xClass);
3383 }
3384 }
3385
@@ -3656,7 +3646,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
3646 }
3647 }
3648
3659 - this.IndexElement(controlRow, xControl);
3649 + this.DecompilerHelper.IndexElement(controlRow, xControl);
3650 }
3651 }
3652
@@ -3668,7 +3658,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
3658 {
3659 foreach (var row in table.Rows)
3660 {
3671 - if (this.TryGetIndexedElement("Control", out var xControl, row.FieldAsString(0), row.FieldAsString(1)))
3661 + if (this.DecompilerHelper.TryGetIndexedElement("Control", row.FieldAsString(0), row.FieldAsString(1), out var xControl))
3662 {
3663 switch (row.FieldAsString(2))
3664 {
@@ -3730,14 +3720,14 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
3720
3721 controlEvents.Add(String.Format(CultureInfo.InvariantCulture, "{0}|{1}|{2:0000000000}|{3}|{4}|{5}", row.FieldAsString(0), row.FieldAsString(1), row.FieldAsNullableInteger(5) ?? 0, row.FieldAsString(2), row.FieldAsString(3), row.FieldAsString(4)), row);
3722
3733 - this.IndexElement(row, xPublish);
3723 + this.DecompilerHelper.IndexElement(row, xPublish);
3724 }
3725
3726 foreach (var row in controlEvents.Values)
3727 {
3738 - if (this.TryGetIndexedElement("Control", out var xControl, row.FieldAsString(0), row.FieldAsString(1)))
3728 + if (this.DecompilerHelper.TryGetIndexedElement("Control", row.FieldAsString(0), row.FieldAsString(1), out var xControl))
3729 {
3740 - var xPublish = this.GetIndexedElement(row);
3730 + var xPublish = this.DecompilerHelper.GetIndexedElement(row);
3731 xControl.Add(xPublish);
3732 }
3733 else
@@ -3928,7 +3918,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
3918 xCustomTable.Add(xRow);
3919 }
3920
3931 - this.RootElement.Add(xCustomTable);
3921 + this.DecompilerHelper.AddElementToRoot(xCustomTable);
3922 }
3923 }
3924
@@ -3944,7 +3934,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
3934 new XAttribute("Directory", row.FieldAsString(0)));
3935
3936 this.AddChildToParent("Component", xCreateFolder, row, 1);
3947 - this.IndexElement(row, xCreateFolder);
3937 + this.DecompilerHelper.IndexElement(row, xCreateFolder);
3938 }
3939 }
3940
@@ -4115,8 +4105,8 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
4105 xCustomAction.SetAttributeValue("PatchUninstall", "yes");
4106 }
4107
4118 - this.RootElement.Add(xCustomAction);
4119 - this.IndexElement(row, xCustomAction);
4108 + this.DecompilerHelper.AddElementToRoot(xCustomAction);
4109 + this.DecompilerHelper.IndexElement(row, xCustomAction);
4110 }
4111 }
4112
@@ -4148,7 +4138,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
4138 }
4139 }
4140
4151 - this.IndexElement(row, xComponentSearch);
4141 + this.DecompilerHelper.IndexElement(row, xComponentSearch);
4142 }
4143 }
4144
@@ -4162,7 +4152,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
4152 {
4153 if (!row.IsColumnNull(1))
4154 {
4165 - if (this.TryGetIndexedElement("Component", out var xComponent, row.FieldAsString(0)))
4155 + if (this.DecompilerHelper.TryGetIndexedElement("Component", row.FieldAsString(0), out var xComponent))
4156 {
4157 xComponent.SetAttributeValue("ComPlusFlags", row.FieldAsInteger(1));
4158 }
@@ -4247,7 +4237,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
4237 }
4238
4239 this.AddChildToParent("Directory", xComponent, row, 2);
4250 - this.IndexElement(row, xComponent);
4240 + this.DecompilerHelper.IndexElement(row, xComponent);
4241 }
4242 }
4243
@@ -4259,7 +4249,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
4249 {
4250 foreach (var row in table.Rows)
4251 {
4262 - if (this.TryGetIndexedElement("Feature", out var xFeature, row.FieldAsString(0)))
4252 + if (this.DecompilerHelper.TryGetIndexedElement("Feature", row.FieldAsString(0), out var xFeature))
4253 {
4254 var xLevel = new XElement(Names.LevelElement,
4255 row.IsColumnNull(2) ? null : new XAttribute("Condition", row.FieldAsString(2)),
@@ -4303,7 +4293,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
4293 !row.IsColumnNull(6) ? new XAttribute("Title", row.FieldAsString(6)) : null);
4294
4295 this.UIElement.Add(xDialog);
4306 - this.IndexElement(row, xDialog);
4296 + this.DecompilerHelper.IndexElement(row, xDialog);
4297 }
4298 }
4299
@@ -4367,13 +4357,13 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
4357 }
4358 }
4359
4370 - this.IndexElement(row, xDirectory);
4360 + this.DecompilerHelper.IndexElement(row, xDirectory);
4361 }
4362
4363 // nest the directories
4364 foreach (var row in table.Rows)
4365 {
4376 - var xDirectory = this.GetIndexedElement(row);
4366 + var xDirectory = this.DecompilerHelper.GetIndexedElement(row);
4367
4368 var id = row.FieldAsString(0);
4369
@@ -4383,26 +4373,26 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
4373 }
4374 else if (row.IsColumnNull(1) || WindowsInstallerStandard.IsStandardDirectory(id))
4375 {
4386 - this.RootElement.Add(xDirectory);
4376 + this.DecompilerHelper.AddElementToRoot(xDirectory);
4377 }
4378 else
4379 {
4380 var parentDirectoryId = row.FieldAsString(1);
4381
4392 - if (!this.TryGetIndexedElement("Directory", out var xParentDirectory, parentDirectoryId))
4382 + if (!this.DecompilerHelper.TryGetIndexedElement("Directory", parentDirectoryId, out var xParentDirectory))
4383 {
4384 this.Messaging.Write(WarningMessages.ExpectedForeignRow(row.SourceLineNumbers, table.Name, row.GetPrimaryKey(DecompilerConstants.PrimaryKeyDelimiter), "Directory_Parent", row.FieldAsString(1), "Directory"));
4385 }
4386 else if (xParentDirectory == xDirectory) // another way to specify a root directory
4387 {
4398 - this.RootElement.Add(xDirectory);
4388 + this.DecompilerHelper.AddElementToRoot(xDirectory);
4389 }
4390 else
4391 {
4392 // TARGETDIR is omitted but if this directory is a first-generation descendant, add it as a root.
4393 if (parentDirectoryId == "TARGETDIR")
4394 {
4405 - this.RootElement.Add(xDirectory);
4395 + this.DecompilerHelper.AddElementToRoot(xDirectory);
4396 }
4397 else
4398 {
@@ -4426,7 +4416,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
4416 XAttributeIfNotNull("Path", row, 2),
4417 XAttributeIfNotNull("Depth", row, 3));
4418
4429 - this.IndexElement(row, xDirectorySearch);
4419 + this.DecompilerHelper.IndexElement(row, xDirectorySearch);
4420 }
4421 }
4422
@@ -4459,7 +4449,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
4449 // destination directory/property is set in FinalizeDuplicateMoveFileTables
4450
4451 this.AddChildToParent("Component", xCopyFile, row, 1);
4462 - this.IndexElement(row, xCopyFile);
4452 + this.DecompilerHelper.IndexElement(row, xCopyFile);
4453 }
4454 }
4455
@@ -4570,7 +4560,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
4560 new XAttribute("Event", row.FieldAsString(2)),
4561 new XAttribute("Attribute", row.FieldAsString(3)));
4562
4573 - if (this.TryGetIndexedElement("Control", out var xControl, row.FieldAsString(0), row.FieldAsString(1)))
4563 + if (this.DecompilerHelper.TryGetIndexedElement("Control", row.FieldAsString(0), row.FieldAsString(1), out var xControl))
4564 {
4565 xControl.Add(xSubscribe);
4566 }
@@ -4595,7 +4585,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
4585
4586 if (!row.IsColumnNull(3))
4587 {
4598 - if (this.TryGetIndexedElement("MIME", out var xMime, row.FieldAsString(3)))
4588 + if (this.DecompilerHelper.TryGetIndexedElement("MIME", row.FieldAsString(3), out var xMime))
4589 {
4590 xMime.SetAttributeValue("Default", "yes");
4591 }
@@ -4614,7 +4604,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
4604 this.AddChildToParent("Component", xExtension, row, 1);
4605 }
4606
4617 - this.IndexElement(row, xExtension);
4607 + this.DecompilerHelper.IndexElement(row, xExtension);
4608 }
4609 }
4610
@@ -4682,7 +4672,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
4672 }
4673
4674 this.AddChildToParent("ImageFamilies", xExternalFile, row, 0);
4685 - this.IndexElement(row, xExternalFile);
4675 + this.DecompilerHelper.IndexElement(row, xExternalFile);
4676 }
4677 }
4678
@@ -4761,7 +4751,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
4751 feature.SetAttributeValue("Absent", "disallow");
4752 }
4753
4764 - this.IndexElement(row, feature);
4754 + this.DecompilerHelper.IndexElement(row, feature);
4755
4756 // sort the features by their display column (and append the identifier to ensure unique keys)
4757 sortedFeatures.Add(String.Format(CultureInfo.InvariantCulture, "{0:00000}|{1}", row.FieldAsInteger(4), row[0]), row);
@@ -4770,15 +4760,15 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
4760 // nest the features
4761 foreach (var row in sortedFeatures.Values)
4762 {
4773 - var xFeature = this.GetIndexedElement("Feature", row.FieldAsString(0));
4763 + var xFeature = this.DecompilerHelper.GetIndexedElement("Feature", row.FieldAsString(0));
4764
4765 if (row.IsColumnNull(1))
4766 {
4777 - this.RootElement.Add(xFeature);
4767 + this.DecompilerHelper.AddElementToRoot(xFeature);
4768 }
4769 else
4770 {
4781 - if (this.TryGetIndexedElement("Feature", out var xParentFeature, row.FieldAsString(1)))
4771 + if (this.DecompilerHelper.TryGetIndexedElement("Feature", row.FieldAsString(1), out var xParentFeature))
4772 {
4773 if (xParentFeature == xFeature)
4774 {
@@ -4809,7 +4799,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
4799 new XAttribute("Id", row.FieldAsString(1)));
4800
4801 this.AddChildToParent("Feature", xComponentRef, row, 0);
4812 - this.IndexElement(row, xComponentRef);
4802 + this.DecompilerHelper.IndexElement(row, xComponentRef);
4803 }
4804 }
4805
@@ -4855,7 +4845,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
4845 xFile.SetAttributeValue("Compressed", "yes");
4846 }
4847
4858 - this.IndexElement(fileRow, xFile);
4848 + this.DecompilerHelper.IndexElement(fileRow, xFile);
4849 }
4850 }
4851
@@ -4882,7 +4872,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
4872 {
4873 foreach (var row in table.Rows)
4874 {
4885 - if (this.TryGetIndexedElement("File", out var xFile, row.FieldAsString(0)))
4875 + if (this.DecompilerHelper.TryGetIndexedElement("File", row.FieldAsString(0), out var xFile))
4876 {
4877 if (!row.IsColumnNull(1))
4878 {
@@ -4912,7 +4902,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
4902 new XAttribute("Id", row.FieldAsString(0)),
4903 new XAttribute("SourceFile", row.FieldAsString(1)));
4904
4915 - this.RootElement.Add(icon);
4905 + this.DecompilerHelper.AddElementToRoot(icon);
4906 }
4907 }
4908
@@ -4932,8 +4922,8 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
4922 row.IsColumnNull(4) ? null : new XAttribute("DiskPrompt", row.FieldAsString(4)),
4923 row.IsColumnNull(5) ? null : new XAttribute("VolumeLabel", row.FieldAsString(5)));
4924
4935 - this.RootElement.Add(family);
4936 - this.IndexElement(row, family);
4925 + this.DecompilerHelper.AddElementToRoot(family);
4926 + this.DecompilerHelper.IndexElement(row, family);
4927 }
4928 }
4929
@@ -5035,7 +5025,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
5025 }
5026 }
5027
5038 - this.IndexElement(row, xIniFileSearch);
5028 + this.DecompilerHelper.IndexElement(row, xIniFileSearch);
5029 }
5030 }
5031
@@ -5071,7 +5061,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
5061 new XAttribute("Condition", row.FieldAsString(0)),
5062 new XAttribute("Message", row.FieldAsString(1)));
5063
5074 - this.RootElement.Add(condition);
5064 + this.DecompilerHelper.AddElementToRoot(condition);
5065 }
5066 }
5067
@@ -5230,7 +5220,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
5220 }
5221 }
5222
5233 - this.IndexElement(row, xPermission);
5223 + this.DecompilerHelper.IndexElement(row, xPermission);
5224 }
5225 }
5226
@@ -5260,8 +5250,8 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
5250 xMedia.SetAttributeValue("Cabinet", cabinet);
5251 }
5252
5263 - this.RootElement.Add(xMedia);
5264 - this.IndexElement(mediaRow, xMedia);
5253 + this.DecompilerHelper.AddElementToRoot(xMedia);
5254 + this.DecompilerHelper.IndexElement(mediaRow, xMedia);
5255 }
5256 }
5257
@@ -5277,7 +5267,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
5267 new XAttribute("ContentType", row.FieldAsString(0)),
5268 row.IsColumnNull(2) ? null : new XAttribute("Class", row.FieldAsString(2)));
5269
5280 - this.IndexElement(row, mime);
5270 + this.DecompilerHelper.IndexElement(row, mime);
5271 }
5272 }
5273
@@ -5338,7 +5328,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
5328 }
5329 }
5330
5341 - this.RootElement.Add(configuration);
5331 + this.DecompilerHelper.AddElementToRoot(configuration);
5332 }
5333 }
5334
@@ -5355,7 +5345,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
5345 new XAttribute("RequiredLanguage", row.FieldAsString(3)),
5346 XAttributeIfNotNull("RequiredVersion", row, 4));
5347
5358 - this.RootElement.Add(xDependency);
5348 + this.DecompilerHelper.AddElementToRoot(xDependency);
5349 }
5350 }
5351
@@ -5382,7 +5372,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
5372 xExclusion.SetAttributeValue("ExcludeExceptLanguage", -excludedLanguage);
5373 }
5374
5385 - this.RootElement.Add(xExclusion);
5375 + this.DecompilerHelper.AddElementToRoot(xExclusion);
5376 }
5377 }
5378
@@ -5402,7 +5392,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
5392 var xIgnoreTable = new XElement(Names.IgnoreTableElement,
5393 new XAttribute("Id", tableName));
5394
5405 - this.RootElement.Add(xIgnoreTable);
5395 + this.DecompilerHelper.AddElementToRoot(xIgnoreTable);
5396 }
5397 }
5398 }
@@ -5417,10 +5407,10 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
5407 {
5408 var row = table.Rows[0];
5409
5420 - this.RootElement.SetAttributeValue("Id", row.FieldAsString(0));
5410 + this.DecompilerHelper.RootElement.SetAttributeValue("Id", row.FieldAsString(0));
5411 // support Language columns that are treated as integers as well as strings (the WiX default, to support localizability)
5422 - this.RootElement.SetAttributeValue("Language", row.FieldAsString(1));
5423 - this.RootElement.SetAttributeValue("Version", row.FieldAsString(2));
5412 + this.DecompilerHelper.RootElement.SetAttributeValue("Language", row.FieldAsString(1));
5413 + this.DecompilerHelper.RootElement.SetAttributeValue("Version", row.FieldAsString(2));
5414 }
5415 else
5416 {
@@ -5442,7 +5432,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
5432 new XAttribute("Column", row.FieldAsString(2)),
5433 XAttributeIfNotNull("Value", row, 3));
5434
5445 - this.RootElement.Add(xSubstitution);
5435 + this.DecompilerHelper.AddElementToRoot(xSubstitution);
5436 }
5437 }
5438
@@ -5487,7 +5477,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
5477 }
5478
5479 this.AddChildToParent("Component", xCopyFile, row, 1);
5490 - this.IndexElement(row, xCopyFile);
5480 + this.DecompilerHelper.IndexElement(row, xCopyFile);
5481 }
5482 }
5483
@@ -5503,7 +5493,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
5493 new XAttribute("Id", row.FieldAsString(0)),
5494 new XAttribute("SourceFile", row.FieldAsString(1)));
5495
5506 - this.IndexElement(row, xDigitalCertificate);
5496 + this.DecompilerHelper.IndexElement(row, xDigitalCertificate);
5497 }
5498 }
5499
@@ -5520,7 +5510,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
5510
5511 this.AddChildToParent("MsiDigitalCertificate", xDigitalSignature, row, 2);
5512
5523 - if (this.TryGetIndexedElement(row.FieldAsString(0), out var xParentElement, row.FieldAsString(1)))
5513 + if (this.DecompilerHelper.TryGetIndexedElement(row.FieldAsString(0), row.FieldAsString(1), out var xParentElement))
5514 {
5515 xParentElement.Add(xDigitalSignature);
5516 }
@@ -5560,7 +5550,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
5550 break;
5551 }
5552
5563 - this.RootElement.Add(xEmbeddedChainer);
5553 + this.DecompilerHelper.AddElementToRoot(xEmbeddedChainer);
5554 }
5555 }
5556
@@ -5735,7 +5725,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
5725 return;
5726 }
5727
5738 - this.IndexElement(row, xPermissionEx);
5728 + this.DecompilerHelper.IndexElement(row, xPermissionEx);
5729 }
5730 }
5731
@@ -5748,7 +5738,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
5738 if (0 < table.Rows.Count)
5739 {
5740 var xPackageCertificates = new XElement(Names.PatchCertificatesElement);
5751 - this.RootElement.Add(xPackageCertificates);
5741 + this.DecompilerHelper.AddElementToRoot(xPackageCertificates);
5742 this.AddCertificates(table, xPackageCertificates);
5743 }
5744 }
@@ -5762,7 +5752,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
5752 if (0 < table.Rows.Count)
5753 {
5754 var xPatchCertificates = new XElement(Names.PatchCertificatesElement);
5765 - this.RootElement.Add(xPatchCertificates);
5755 + this.DecompilerHelper.AddElementToRoot(xPatchCertificates);
5756 this.AddCertificates(table, xPatchCertificates);
5757 }
5758 }
@@ -5776,7 +5766,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
5766 {
5767 foreach (var row in table.Rows)
5768 {
5779 - if (this.TryGetIndexedElement("MsiDigitalCertificate", out var xDigitalCertificate, row.FieldAsString(1)))
5769 + if (this.DecompilerHelper.TryGetIndexedElement("MsiDigitalCertificate", row.FieldAsString(1), out var xDigitalCertificate))
5770 {
5771 parent.Add(xDigitalCertificate);
5772 }
@@ -5846,7 +5836,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
5836 break;
5837 }
5838
5849 - this.IndexElement(row, xOdbcDataSource);
5839 + this.DecompilerHelper.IndexElement(row, xOdbcDataSource);
5840 }
5841 }
5842
@@ -5865,7 +5855,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
5855 XAttributeIfNotNull("SetupFile", row, 4));
5856
5857 this.AddChildToParent("Component", xOdbcDriver, row, 1);
5868 - this.IndexElement(row, xOdbcDriver);
5858 + this.DecompilerHelper.IndexElement(row, xOdbcDriver);
5859 }
5860 }
5861
@@ -6010,7 +6000,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
6000 }
6001 }
6002
6013 - this.RootElement.Add(xPatchMetadata);
6003 + this.DecompilerHelper.AddElementToRoot(xPatchMetadata);
6004 }
6005 }
6006
@@ -6049,7 +6039,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
6039 patchSequence.SetAttributeValue("Supersede", "yes");
6040 }
6041
6052 - this.RootElement.Add(patchSequence);
6042 + this.DecompilerHelper.AddElementToRoot(patchSequence);
6043 }
6044 }
6045
@@ -6068,13 +6058,13 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
6058 XAttributeIfNotNull("Icon", row, 4),
6059 XAttributeIfNotNull("IconIndex", row, 5));
6060
6071 - this.IndexElement(row, xProgId);
6061 + this.DecompilerHelper.IndexElement(row, xProgId);
6062 }
6063
6064 // nest the ProgIds
6065 foreach (var row in table.Rows)
6066 {
6077 - var xProgId = this.GetIndexedElement(row);
6067 + var xProgId = this.DecompilerHelper.GetIndexedElement(row);
6068
6069 if (!row.IsColumnNull(1))
6070 {
@@ -6107,13 +6097,13 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
6097 case "AllowProductCodeMismatches":
6098 if ("1" == value)
6099 {
6110 - this.RootElement.SetAttributeValue("AllowProductCodeMismatches", "yes");
6100 + this.DecompilerHelper.RootElement.SetAttributeValue("AllowProductCodeMismatches", "yes");
6101 }
6102 break;
6103 case "AllowProductVersionMajorMismatches":
6104 if ("1" == value)
6105 {
6116 - this.RootElement.SetAttributeValue("AllowMajorVersionMismatches", "yes");
6106 + this.DecompilerHelper.RootElement.SetAttributeValue("AllowMajorVersionMismatches", "yes");
6107 }
6108 break;
6109 case "ApiPatchingSymbolFlags":
@@ -6127,7 +6117,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
6117 value = value.Substring(2);
6118 }
6119
6130 - this.RootElement.SetAttributeValue("SymbolFlags", Convert.ToInt32(value, 16));
6120 + this.DecompilerHelper.RootElement.SetAttributeValue("SymbolFlags", Convert.ToInt32(value, 16));
6121 }
6122 catch
6123 {
@@ -6138,13 +6128,13 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
6128 case "DontRemoveTempFolderWhenFinished":
6129 if ("1" == value)
6130 {
6141 - this.RootElement.SetAttributeValue("CleanWorkingFolder", "no");
6131 + this.DecompilerHelper.RootElement.SetAttributeValue("CleanWorkingFolder", "no");
6132 }
6133 break;
6134 case "IncludeWholeFilesOnly":
6135 if ("1" == value)
6136 {
6147 - this.RootElement.SetAttributeValue("WholeFilesOnly", "yes");
6137 + this.DecompilerHelper.RootElement.SetAttributeValue("WholeFilesOnly", "yes");
6138 }
6139 break;
6140 case "ListOfPatchGUIDsToReplace":
@@ -6158,7 +6148,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
6148 var xReplacePatch = new XElement(Names.ReplacePatchElement,
6149 new XAttribute("Id", guidMatch.Value));
6150
6161 - this.RootElement.Add(xReplacePatch);
6151 + this.DecompilerHelper.AddElementToRoot(xReplacePatch);
6152 }
6153 }
6154 break;
@@ -6172,25 +6162,25 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
6162 var xTargetProductCode = new XElement(Names.TargetProductCodeElement,
6163 new XAttribute("Id", targetProductCodeString));
6164
6175 - this.RootElement.Add(xTargetProductCode);
6165 + this.DecompilerHelper.AddElementToRoot(xTargetProductCode);
6166 }
6167 }
6168 break;
6169 case "PatchGUID":
6180 - this.RootElement.SetAttributeValue("Id", value);
6170 + this.DecompilerHelper.RootElement.SetAttributeValue("Id", value);
6171 break;
6172 case "PatchSourceList":
6183 - this.RootElement.SetAttributeValue("SourceList", value);
6173 + this.DecompilerHelper.RootElement.SetAttributeValue("SourceList", value);
6174 break;
6175 case "PatchOutputPath":
6186 - this.RootElement.SetAttributeValue("OutputPath", value);
6176 + this.DecompilerHelper.RootElement.SetAttributeValue("OutputPath", value);
6177 break;
6178 default:
6179 var patchProperty = new XElement(Names.PatchPropertyElement,
6180 new XAttribute("Name", name),
6181 new XAttribute("Value", value));
6182
6193 - this.RootElement.Add(patchProperty);
6183 + this.DecompilerHelper.AddElementToRoot(patchProperty);
6184 break;
6185 }
6186 }
@@ -6260,22 +6250,22 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
6250 switch (id)
6251 {
6252 case "Manufacturer":
6263 - this.RootElement.SetAttributeValue("Manufacturer", value);
6253 + this.DecompilerHelper.RootElement.SetAttributeValue("Manufacturer", value);
6254 continue;
6255 case "ProductCode":
6266 - this.RootElement.SetAttributeValue("ProductCode", value.ToUpper(CultureInfo.InvariantCulture));
6256 + this.DecompilerHelper.RootElement.SetAttributeValue("ProductCode", value.ToUpper(CultureInfo.InvariantCulture));
6257 continue;
6258 case "ProductLanguage":
6269 - this.RootElement.SetAttributeValue("Language", value);
6259 + this.DecompilerHelper.RootElement.SetAttributeValue("Language", value);
6260 continue;
6261 case "ProductName":
6272 - this.RootElement.SetAttributeValue("Name", value);
6262 + this.DecompilerHelper.RootElement.SetAttributeValue("Name", value);
6263 continue;
6264 case "ProductVersion":
6275 - this.RootElement.SetAttributeValue("Version", value);
6265 + this.DecompilerHelper.RootElement.SetAttributeValue("Version", value);
6266 continue;
6267 case "UpgradeCode":
6278 - this.RootElement.SetAttributeValue("UpgradeCode", value);
6268 + this.DecompilerHelper.RootElement.SetAttributeValue("UpgradeCode", value);
6269 continue;
6270 }
6271 }
@@ -6340,14 +6330,14 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
6330 }
6331 }
6332
6343 - this.IndexElement(row, radioButton);
6333 + this.DecompilerHelper.IndexElement(row, radioButton);
6334 }
6335
6336 // nest the radio buttons
6337 var xRadioButtonGroups = new Dictionary<string, XElement>();
6338 foreach (var row in table.Rows.OrderBy(row => row.FieldAsString(0)).ThenBy(row => row.FieldAsInteger(1)))
6339 {
6350 - var xRadioButton = this.GetIndexedElement(row);
6340 + var xRadioButton = this.DecompilerHelper.GetIndexedElement(row);
6341
6342 if (!xRadioButtonGroups.TryGetValue(row.FieldAsString(0), out var xRadioButtonGroup))
6343 {
@@ -6395,7 +6385,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
6385 break;
6386 }
6387
6398 - this.IndexElement(row, xRegistryKey);
6388 + this.DecompilerHelper.IndexElement(row, xRegistryKey);
6389 }
6390 else
6391 {
@@ -6480,7 +6470,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
6470 xRegistryValue.SetAttributeValue("Value", String.Empty);
6471 }
6472
6483 - this.IndexElement(row, xRegistryValue);
6473 + this.DecompilerHelper.IndexElement(row, xRegistryValue);
6474 }
6475 }
6476 }
@@ -6552,7 +6542,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
6542 }
6543 }
6544
6555 - this.IndexElement(row, xRegistrySearch);
6545 + this.DecompilerHelper.IndexElement(row, xRegistrySearch);
6546 }
6547 }
6548
@@ -6588,7 +6578,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
6578 }
6579
6580 this.AddChildToParent("Component", xRemoveFolder, row, 1);
6591 - this.IndexElement(row, xRemoveFolder);
6581 + this.DecompilerHelper.IndexElement(row, xRemoveFolder);
6582 }
6583 else
6584 {
@@ -6625,7 +6615,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
6615 }
6616
6617 this.AddChildToParent("Component", xRemoveFile, row, 1);
6628 - this.IndexElement(row, xRemoveFile);
6618 + this.DecompilerHelper.IndexElement(row, xRemoveFile);
6619 }
6620 }
6621 }
@@ -6738,7 +6728,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
6728 {
6729 foreach (var row in table.Rows)
6730 {
6741 - if (this.TryGetIndexedElement("File", out var xFile, row.FieldAsString(0)))
6731 + if (this.DecompilerHelper.TryGetIndexedElement("File", row.FieldAsString(0), out var xFile))
6732 {
6733 xFile.SetAttributeValue("SelfRegCost", row.IsColumnNull(1) ? 0 : row.FieldAsInteger(1));
6734 }
@@ -6927,7 +6917,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
6917 }
6918
6919 this.AddChildToParent("Component", xServiceInstall, row, 11);
6930 - this.IndexElement(row, xServiceInstall);
6920 + this.DecompilerHelper.IndexElement(row, xServiceInstall);
6921 }
6922 }
6923
@@ -6943,17 +6933,17 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
6933 new XAttribute("Name", row.FieldAsString(0)),
6934 new XAttribute("SourceFile", row.FieldAsString(1)));
6935
6946 - this.IndexElement(row, xSfpCatalog);
6936 + this.DecompilerHelper.IndexElement(row, xSfpCatalog);
6937 }
6938
6939 // nest the SFPCatalog elements
6940 foreach (var row in table.Rows)
6941 {
6952 - var xSfpCatalog = this.GetIndexedElement(row);
6942 + var xSfpCatalog = this.DecompilerHelper.GetIndexedElement(row);
6943
6944 if (!row.IsColumnNull(2))
6945 {
6956 - if (this.TryGetIndexedElement("SFPCatalog", out var xParentSFPCatalog, row.FieldAsString(2)))
6946 + if (this.DecompilerHelper.TryGetIndexedElement("SFPCatalog", row.FieldAsString(2), out var xParentSFPCatalog))
6947 {
6948 xParentSFPCatalog.Add(xSfpCatalog);
6949 }
@@ -6961,12 +6951,12 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
6951 {
6952 xSfpCatalog.SetAttributeValue("Dependency", row.FieldAsString(2));
6953
6964 - this.RootElement.Add(xSfpCatalog);
6954 + this.DecompilerHelper.AddElementToRoot(xSfpCatalog);
6955 }
6956 }
6957 else
6958 {
6969 - this.RootElement.Add(xSfpCatalog);
6959 + this.DecompilerHelper.AddElementToRoot(xSfpCatalog);
6960 }
6961 }
6962 }
@@ -7044,7 +7034,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
7034 }
7035
7036 this.AddChildToParent("Component", xShortcut, row, 3);
7047 - this.IndexElement(row, xShortcut);
7037 + this.DecompilerHelper.IndexElement(row, xShortcut);
7038 }
7039 }
7040
@@ -7093,7 +7083,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
7083 fileSearch.SetAttributeValue("MaxDate", ConvertIntegerToDateTime(row.FieldAsInteger(7)));
7084 }
7085
7096 - this.IndexElement(row, fileSearch);
7086 + this.DecompilerHelper.IndexElement(row, fileSearch);
7087 }
7088 }
7089
@@ -7110,7 +7100,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
7100 xPatchTargetFile = new XElement(Names.TargetFileElement,
7101 new XAttribute("Id", row.FieldAsString(1)));
7102
7113 - if (this.TryGetIndexedElement("TargetImages", out var xTargetImage, row.FieldAsString(0)))
7103 + if (this.DecompilerHelper.TryGetIndexedElement("TargetImages", row.FieldAsString(0), out var xTargetImage))
7104 {
7105 xTargetImage.Add(xPatchTargetFile);
7106 }
@@ -7192,7 +7182,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
7182 }
7183
7184 this.AddChildToParent("UpgradedImages", xTargetImage, row, 3);
7195 - this.IndexElement(row, xTargetImage);
7185 + this.DecompilerHelper.IndexElement(row, xTargetImage);
7186 }
7187 }
7188
@@ -7282,7 +7272,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
7272 }
7273
7274 // nested under the appropriate File element in FinalizeFileTable
7285 - this.IndexElement(row, xTypeLib);
7275 + this.DecompilerHelper.IndexElement(row, xTypeLib);
7276 }
7277 }
7278
@@ -7306,7 +7296,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
7296 xUpgrade = new XElement(Names.UpgradeElement,
7297 new XAttribute("Id", upgradeRow.UpgradeCode));
7298
7309 - this.RootElement.Add(xUpgrade);
7299 + this.DecompilerHelper.AddElementToRoot(xUpgrade);
7300 xUpgrades.Add(upgradeRow.UpgradeCode, xUpgrade);
7301 }
7302
@@ -7435,7 +7425,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
7425 AddSymbolPaths(row, 3, xUpgradeImage);
7426
7427 this.AddChildToParent("ImageFamilies", xUpgradeImage, row, 4);
7438 - this.IndexElement(row, xUpgradeImage);
7428 + this.DecompilerHelper.IndexElement(row, xUpgradeImage);
7429 }
7430 }
7431
@@ -7485,7 +7475,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
7475 XAttributeIfNotNull("Command", row, 3),
7476 XAttributeIfNotNull("Argument", row, 4));
7477
7488 - this.IndexElement(row, verb);
7478 + this.DecompilerHelper.IndexElement(row, verb);
7479 }
7480 }
7481
@@ -7537,7 +7527,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
7527 var featureField = row.Fields[featureColumnIndex];
7528 var componentField = row.Fields[componentColumnIndex];
7529
7540 - if (this.TryGetIndexedElement("FeatureComponents", out var xComponentRef, Convert.ToString(featureField.Data), Convert.ToString(componentField.Data)))
7530 + if (this.DecompilerHelper.TryGetIndexedElement("FeatureComponents", featureField.AsString(), componentField.AsString(), out var xComponentRef))
7531 {
7532 xComponentRef.SetAttributeValue("Primary", "yes");
7533 }
src/wix/WixToolset.Core.WindowsInstaller/ExtensibilityServices/WindowsInstallerDecompilerHelper.cs new
+133
@@ -0,0 +1,133 @@
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.ExtensibilityServices
4 +{
5 + using System;
6 + using System.Collections.Generic;
7 + using System.Xml.Linq;
8 + using WixToolset.Core.WindowsInstaller.Decompile;
9 + using WixToolset.Data.WindowsInstaller;
10 + using WixToolset.Extensibility.Services;
11 +
12 + internal class WindowsInstallerDecompilerHelper : IWindowsInstallerDecompilerHelper
13 + {
14 + public WindowsInstallerDecompilerHelper(IServiceProvider _)
15 + {
16 + }
17 +
18 + private Dictionary<string, XElement> IndexedElements { get; } = new Dictionary<string, XElement>();
19 +
20 + #region IWindowsInstallerDecompilerHelper interfaces
21 +
22 + public XElement RootElement { get; set; }
23 +
24 + public XElement AddElementToRoot(string name, params object[] content)
25 + {
26 + var element = new XElement(Names.WxsNamespace + name, content);
27 +
28 + this.RootElement.Add(element);
29 +
30 + return element;
31 + }
32 +
33 + public XElement AddElementToRoot(XName name, params object[] content)
34 + {
35 + var element = new XElement(name, content);
36 +
37 + this.RootElement.Add(element);
38 +
39 + return element;
40 + }
41 +
42 + public XElement AddElementToRoot(XElement element)
43 + {
44 + this.RootElement.Add(element);
45 +
46 + return element;
47 + }
48 +
49 + public XElement CreateElement(string name, params object[] content)
50 + {
51 + return new XElement(Names.WxsNamespace + name, content);
52 + }
53 +
54 + public XElement GetIndexedElement(Row row)
55 + {
56 + return this.GetIndexedElement(row.TableDefinition.Name, row.GetPrimaryKey());
57 + }
58 +
59 + public XElement GetIndexedElement(string table, string primaryKey)
60 + {
61 + return this.TryGetIndexedElement(table, primaryKey, out var element) ? element : null;
62 + }
63 +
64 + public XElement GetIndexedElement(string table, string primaryKey1, string primaryKey2)
65 + {
66 + return this.TryGetIndexedElement(table, primaryKey1, primaryKey2, out var element) ? element : null;
67 + }
68 +
69 + public XElement GetIndexedElement(string table, string primaryKey1, string primaryKey2, string primaryKey3)
70 + {
71 + return this.TryGetIndexedElement(table, primaryKey1, primaryKey2, primaryKey3, out var element) ? element : null;
72 + }
73 +
74 + public XElement GetIndexedElement(string table, string[] primaryKeys)
75 + {
76 + return this.TryGetIndexedElement(table, primaryKeys, out var element) ? element : null;
77 + }
78 +
79 + public void IndexElement(Row row, XElement element)
80 + {
81 + this.IndexElement(row.Table.Name, row.GetPrimaryKey(), element);
82 + }
83 +
84 + public void IndexElement(string table, string primaryKey, XElement element)
85 + {
86 + var key = String.Concat(table, ':', primaryKey);
87 + this.IndexedElements.Add(key, element);
88 + }
89 +
90 + public void IndexElement(string table, string primaryKey1, string primaryKey2, XElement element)
91 + {
92 + this.IndexElement(table, String.Join("/", primaryKey1, primaryKey2), element);
93 + }
94 +
95 + public void IndexElement(string table, string primaryKey1, string primaryKey2, string primaryKey3, XElement element)
96 + {
97 + this.IndexElement(table, String.Join("/", primaryKey1, primaryKey2, primaryKey3), element);
98 + }
99 +
100 + public void IndexElement(string table, string[] primaryKeys, XElement element)
101 + {
102 + this.IndexElement(table, String.Join("/", primaryKeys), element);
103 + }
104 +
105 + public bool TryGetIndexedElement(Row row, out XElement element)
106 + {
107 + return this.TryGetIndexedElement(row.TableDefinition.Name, row.GetPrimaryKey(), out element);
108 + }
109 +
110 + public bool TryGetIndexedElement(string table, string primaryKey, out XElement element)
111 + {
112 + var key = String.Concat(table, ':', primaryKey);
113 + return this.IndexedElements.TryGetValue(key, out element);
114 + }
115 +
116 + public bool TryGetIndexedElement(string table, string primaryKey1, string primaryKey2, out XElement element)
117 + {
118 + return this.TryGetIndexedElement(table, String.Join("/", primaryKey1, primaryKey2), out element);
119 + }
120 +
121 + public bool TryGetIndexedElement(string table, string primaryKey1, string primaryKey2, string primaryKey3, out XElement element)
122 + {
123 + return this.TryGetIndexedElement(table, String.Join("/", primaryKey1, primaryKey2, primaryKey3), out element);
124 + }
125 +
126 + public bool TryGetIndexedElement(string table, string[] primaryKeys, out XElement element)
127 + {
128 + return this.TryGetIndexedElement(table, String.Join("/", primaryKeys), out element);
129 + }
130 +
131 + #endregion
132 + }
133 +}
src/wix/WixToolset.Core.WindowsInstaller/Unbind/UnbindDatabaseCommand.cs
+1 -1
@@ -171,7 +171,7 @@ namespace WixToolset.Core.WindowsInstaller.Unbind
171 }
172 break;
173 case ColumnType.Object:
174 - var sourceFile = "FILE NOT EXPORTED, USE THE dark.exe -x OPTION TO EXPORT BINARIES";
174 + var sourceFile = "FILE NOT EXPORTED";
175
176 if (null != this.ExportBasePath)
177 {
src/wix/WixToolset.Core.WindowsInstaller/WindowsInstallerDecompileContext.cs
+4
@@ -23,6 +23,10 @@ namespace WixToolset.Core.WindowsInstaller
23
24 public IReadOnlyCollection<IWindowsInstallerDecompilerExtension> Extensions { get; set; }
25
26 + public IReadOnlyCollection<IExtensionData> ExtensionData { get; set; }
27 +
28 + public ISymbolDefinitionCreator SymbolDefinitionCreator { get; set; }
29 +
30 public string ExtractFolder { get; set; }
31
32 public string CabinetExtractFolder { get; set; }
src/wix/WixToolset.Core.WindowsInstaller/WindowsInstallerDecompileResult.cs
+1 -1
@@ -11,7 +11,7 @@ namespace WixToolset.Core.WindowsInstaller
11 {
12 public XDocument Document { get; set; }
13
14 - public IReadOnlyCollection<string> ExtractedFilePaths { get; set; }
14 + public IList<string> ExtractedFilePaths { get; set; }
15
16 public Platform? Platform { get; set; }
17 }
src/wix/WixToolset.Core.WindowsInstaller/WindowsInstallerDecompiler.cs
+81 -4
@@ -3,8 +3,15 @@
3 namespace WixToolset.Core.WindowsInstaller
4 {
5 using System;
6 + using System.Collections.Generic;
7 + using System.ComponentModel;
8 + using System.IO;
9 + using System.Linq;
10 + using WixToolset.Core.Native.Msi;
11 using WixToolset.Core.WindowsInstaller.Decompile;
7 - using WixToolset.Extensibility;
12 + using WixToolset.Core.WindowsInstaller.Unbind;
13 + using WixToolset.Data;
14 + using WixToolset.Data.WindowsInstaller;
15 using WixToolset.Extensibility.Data;
16 using WixToolset.Extensibility.Services;
17
@@ -16,12 +23,20 @@ namespace WixToolset.Core.WindowsInstaller
23 internal WindowsInstallerDecompiler(IServiceProvider serviceProvider)
24 {
25 this.ServiceProvider = serviceProvider;
26 + this.Messaging = serviceProvider.GetService<IMessaging>();
27 }
28
21 - public IServiceProvider ServiceProvider { get; }
29 + private IServiceProvider ServiceProvider { get; }
30 +
31 + private IMessaging Messaging { get; }
32
33 public IWindowsInstallerDecompileResult Decompile(IWindowsInstallerDecompileContext context)
34 {
35 + if (context.SymbolDefinitionCreator == null)
36 + {
37 + context.SymbolDefinitionCreator = this.ServiceProvider.GetService<ISymbolDefinitionCreator>();
38 + }
39 +
40 // Pre-decompile.
41 //
42 foreach (var extension in context.Extensions)
@@ -31,8 +46,7 @@ namespace WixToolset.Core.WindowsInstaller
46
47 // Decompile.
48 //
34 - var command = new DecompileMsiOrMsmCommand(context);
35 - var result = command.Execute();
49 + var result = this.Execute(context);
50
51 if (result != null)
52 {
@@ -46,5 +60,68 @@ namespace WixToolset.Core.WindowsInstaller
60
61 return result;
62 }
63 +
64 + private IWindowsInstallerDecompileResult Execute(IWindowsInstallerDecompileContext context)
65 + {
66 + var result = context.ServiceProvider.GetService<IWindowsInstallerDecompileResult>();
67 +
68 + try
69 + {
70 + using (var database = new Database(context.DecompilePath, OpenDatabase.ReadOnly))
71 + {
72 + // Delete the directory and its files to prevent cab extraction failure due to an existing file.
73 + if (Directory.Exists(context.ExtractFolder))
74 + {
75 + Directory.Delete(context.ExtractFolder, true);
76 + }
77 +
78 + var backendHelper = context.ServiceProvider.GetService<IWindowsInstallerBackendHelper>();
79 + var decompilerHelper = context.ServiceProvider.GetService<IWindowsInstallerDecompilerHelper>();
80 +
81 + var unbindCommand = new UnbindDatabaseCommand(this.Messaging, backendHelper, database, context.DecompilePath, context.DecompileType, context.ExtractFolder, context.IntermediateFolder, context.IsAdminImage, suppressDemodularization: false, skipSummaryInfo: false);
82 + var output = unbindCommand.Execute();
83 + var extractedFilePaths = new List<string>(unbindCommand.ExportedFiles);
84 +
85 + var decompiler = new Decompiler(this.Messaging, backendHelper, decompilerHelper, context.Extensions, context.ExtensionData, context.SymbolDefinitionCreator, context.BaseSourcePath, context.SuppressCustomTables, context.SuppressDroppingEmptyTables, context.SuppressRelativeActionSequencing, context.SuppressUI, context.TreatProductAsModule);
86 + result.Document = decompiler.Decompile(output);
87 +
88 + result.Platform = GetPlatformFromOutput(output);
89 +
90 + // extract the files from the cabinets
91 + if (!String.IsNullOrEmpty(context.ExtractFolder) && !context.SuppressExtractCabinets)
92 + {
93 + var fileDirectory = String.IsNullOrEmpty(context.CabinetExtractFolder) ? Path.Combine(context.ExtractFolder, "File") : context.CabinetExtractFolder;
94 +
95 + var extractCommand = new ExtractCabinetsCommand(output, database, context.DecompilePath, fileDirectory, context.IntermediateFolder, context.TreatProductAsModule);
96 + extractCommand.Execute();
97 +
98 + extractedFilePaths.AddRange(extractCommand.ExtractedFiles);
99 + result.ExtractedFilePaths = extractedFilePaths;
100 + }
101 + else
102 + {
103 + result.ExtractedFilePaths = new string[0];
104 + }
105 + }
106 + }
107 + catch (Win32Exception e)
108 + {
109 + if (0x6E == e.NativeErrorCode) // ERROR_OPEN_FAILED
110 + {
111 + throw new WixException(ErrorMessages.OpenDatabaseFailed(context.DecompilePath));
112 + }
113 +
114 + throw;
115 + }
116 +
117 + return result;
118 + }
119 +
120 + private static Platform? GetPlatformFromOutput(WindowsInstallerData output)
121 + {
122 + var template = output.Tables["_SummaryInformation"]?.Rows.SingleOrDefault(row => row.FieldAsInteger(0) == 7)?.FieldAsString(1);
123 +
124 + return Decompiler.GetPlatformFromTemplateSummaryInformation(template?.Split(';'));
125 + }
126 }
127 }
src/wix/WixToolset.Core.WindowsInstaller/WixToolsetCoreServiceProviderExtensions.cs
+3 -2
@@ -16,8 +16,8 @@ namespace WixToolset.Core.WindowsInstaller
16 /// <summary>
17 /// Adds WindowsInstaller services.
18 /// </summary>
19 - /// <param name="coreProvider"></param>
20 - /// <returns></returns>
19 + /// <param name="coreProvider">Core service provider.</param>
20 + /// <returns>The core service provider provided to this method.</returns>
21 public static IWixToolsetCoreServiceProvider AddWindowsInstallerBackend(this IWixToolsetCoreServiceProvider coreProvider)
22 {
23 AddServices(coreProvider);
@@ -32,6 +32,7 @@ namespace WixToolset.Core.WindowsInstaller
32 {
33 // Singletons.
34 coreProvider.AddService((provider, singletons) => AddSingleton<IWindowsInstallerBackendHelper>(singletons, new WindowsInstallerBackendHelper(provider)));
35 + coreProvider.AddService((provider, singletons) => AddSingleton<IWindowsInstallerDecompilerHelper>(singletons, new WindowsInstallerDecompilerHelper(provider)));
36
37 // Transients.
38 coreProvider.AddService<IWindowsInstallerDecompiler>((provider, singletons) => new WindowsInstallerDecompiler(provider));
src/wix/test/Example.Extension/ExampleCompilerExtension.cs
+11 -7
@@ -10,8 +10,9 @@ namespace Example.Extension
10
11 internal class ExampleCompilerExtension : BaseCompilerExtension
12 {
13 - public override XNamespace Namespace => "http://www.example.com/scheams/v1/wxs";
14 - public string BundleExtensionId => "ExampleBundleExtension";
13 + private const string BundleExtensionId = "ExampleBundleExtension";
14 +
15 + public override XNamespace Namespace => ExampleConstants.Namespace;
16
17 public override void ParseElement(Intermediate intermediate, IntermediateSection section, XElement parentElement, XElement element, IDictionary<string, string> context)
18 {
@@ -38,10 +39,12 @@ namespace Example.Extension
39 }
40 break;
41 case "Component":
42 + var componentId = context["ComponentId"];
43 +
44 switch (element.Name.LocalName)
45 {
46 case "Example":
44 - this.ParseExampleElement(intermediate, section, element);
47 + this.ParseExampleElement(intermediate, section, element, componentId);
48 processed = true;
49 break;
50 }
@@ -54,7 +57,7 @@ namespace Example.Extension
57 }
58 }
59
57 - private void ParseExampleElement(Intermediate intermediate, IntermediateSection section, XElement element)
60 + private void ParseExampleElement(Intermediate intermediate, IntermediateSection section, XElement element, string componentId)
61 {
62 var sourceLineNumbers = this.ParseHelper.GetSourceLineNumbers(element);
63 Identifier id = null;
@@ -93,7 +96,8 @@ namespace Example.Extension
96 if (!this.Messaging.EncounteredError)
97 {
98 var symbol = this.ParseHelper.CreateSymbol(section, sourceLineNumbers, "Example", id);
96 - symbol.Set(0, value);
99 + symbol.Set(0, componentId);
100 + symbol.Set(1, value);
101 }
102 }
103
@@ -152,12 +156,12 @@ namespace Example.Extension
156
157 if (!this.Messaging.EncounteredError)
158 {
155 - this.ParseHelper.CreateWixSearchSymbol(section, sourceLineNumbers, element.Name.LocalName, id, variable, condition, after, this.BundleExtensionId);
159 + this.ParseHelper.CreateWixSearchSymbol(section, sourceLineNumbers, element.Name.LocalName, id, variable, condition, after, BundleExtensionId);
160 }
161
162 if (!this.Messaging.EncounteredError)
163 {
160 - var symbol = section.AddSymbol(new ExampleSearchSymbol(sourceLineNumbers, id)
164 + section.AddSymbol(new ExampleSearchSymbol(sourceLineNumbers, id)
165 {
166 SearchFor = searchFor,
167 });
src/wix/test/Example.Extension/ExampleConstants.cs new
+13
@@ -0,0 +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 Example.Extension
4 +{
5 + using System.Xml.Linq;
6 +
7 + internal class ExampleConstants
8 + {
9 + public static readonly XNamespace Namespace = "http://www.example.com/scheams/v1/wxs";
10 +
11 + public static readonly XName ExampleName = Namespace + "Example";
12 + }
13 +}
src/wix/test/Example.Extension/ExampleExtensionFactory.cs
+4
@@ -43,6 +43,10 @@ namespace Example.Extension
43 {
44 extension = new ExampleWindowsInstallerBackendExtension();
45 }
46 + else if (extensionType == typeof(IWindowsInstallerDecompilerExtension))
47 + {
48 + extension = new ExampleWindowsInstallerDecompilerExtension();
49 + }
50 else
51 {
52 extension = null;
src/wix/test/Example.Extension/ExampleRow.cs
+7 -1
@@ -23,10 +23,16 @@ namespace Example.Extension
23 set { this.Fields[0].Data = value; }
24 }
25
26 - public string Value
26 + public string ComponentRef
27 {
28 get { return (string)this.Fields[1].Data; }
29 set { this.Fields[1].Data = value; }
30 }
31 +
32 + public string Value
33 + {
34 + get { return (string)this.Fields[2].Data; }
35 + set { this.Fields[2].Data = value; }
36 + }
37 }
38 }
src/wix/test/Example.Extension/ExampleSymbol.cs
+7
@@ -6,6 +6,7 @@ namespace Example.Extension
6
7 public enum ExampleSymbolFields
8 {
9 + ComponentRef,
10 Value,
11 }
12
@@ -21,6 +22,12 @@ namespace Example.Extension
22
23 public IntermediateField this[ExampleSymbolFields index] => this.Fields[(int)index];
24
25 + public string ComponentRef
26 + {
27 + get => this.Fields[(int)ExampleSymbolFields.ComponentRef]?.AsString();
28 + set => this.Set((int)ExampleSymbolFields.ComponentRef, value);
29 + }
30 +
31 public string Value
32 {
33 get => this.Fields[(int)ExampleSymbolFields.Value]?.AsString();
src/wix/test/Example.Extension/ExampleSymbolDefinitions.cs
+1
@@ -18,6 +18,7 @@ namespace Example.Extension
18 ExampleSymbolDefinitionType.Example.ToString(),
19 new[]
20 {
21 + new IntermediateFieldDefinition(nameof(ExampleSymbolFields.ComponentRef), IntermediateFieldType.String),
22 new IntermediateFieldDefinition(nameof(ExampleSymbolFields.Value), IntermediateFieldType.String),
23 },
24 typeof(ExampleSymbol));
src/wix/test/Example.Extension/ExampleTableDefinitions.cs
+2 -1
@@ -11,7 +11,8 @@ namespace Example.Extension
11 ExampleSymbolDefinitions.Example,
12 new[]
13 {
14 - new ColumnDefinition("Example", ColumnType.String, 72, true, false, ColumnCategory.Identifier),
14 + new ColumnDefinition("Example", ColumnType.String, 72, true, false, ColumnCategory.Identifier, modularizeType: ColumnModularizeType.Column),
15 + new ColumnDefinition("Component_", ColumnType.String, 72, false, false, ColumnCategory.Identifier, keyTable: "Component", keyColumn: 1, modularizeType: ColumnModularizeType.Column),
16 new ColumnDefinition("Value", ColumnType.String, 0, false, false, ColumnCategory.Formatted),
17 },
18 strongRowType: typeof(ExampleRow),
src/wix/test/Example.Extension/ExampleWindowsInstallerBackendExtension.cs
+6 -5
@@ -18,12 +18,13 @@ namespace Example.Extension
18 switch (symbolType)
19 {
20 case ExampleSymbolDefinitionType.Example:
21 - {
22 - var row = (ExampleRow)this.BackendHelper.CreateRow(section, symbol, output, ExampleTableDefinitions.ExampleTable);
23 - row.Example = symbol.Id.Id;
24 - row.Value = symbol[0].AsString();
25 - }
21 + {
22 + var row = (ExampleRow)this.BackendHelper.CreateRow(section, symbol, output, ExampleTableDefinitions.ExampleTable);
23 + row.Example = symbol.Id.Id;
24 + row.ComponentRef = symbol[0].AsString();
25 + row.Value = symbol[1].AsString();
26 return true;
27 + }
28 }
29 }
30
src/wix/test/Example.Extension/ExampleWindowsInstallerDecompilerExtension.cs new
+42
@@ -0,0 +1,42 @@
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 Example.Extension
4 +{
5 + using System;
6 + using System.Collections.Generic;
7 + using System.Xml.Linq;
8 + using WixToolset.Data.WindowsInstaller;
9 + using WixToolset.Extensibility;
10 +
11 + internal class ExampleWindowsInstallerDecompilerExtension : BaseWindowsInstallerDecompilerExtension
12 + {
13 + public override IReadOnlyCollection<TableDefinition> TableDefinitions => ExampleTableDefinitions.All;
14 +
15 + public override bool TryDecompileTable(Table table)
16 + {
17 + switch (table.Name)
18 + {
19 + case "Wix4Example":
20 + this.ProcessExampleTable(table);
21 + return true;
22 + }
23 +
24 + return false;
25 + }
26 +
27 + private void ProcessExampleTable(Table table)
28 + {
29 + foreach (var row in table.Rows)
30 + {
31 + var componentId = row.FieldAsString(1);
32 + if (this.DecompilerHelper.TryGetIndexedElement("Component", componentId, out var component))
33 + {
34 + component.Add(new XElement(ExampleConstants.ExampleName,
35 + new XAttribute("Id", row.FieldAsString(0)),
36 + new XAttribute("Value", row.FieldAsString(2))
37 + ));
38 + }
39 + }
40 + }
41 + }
42 +}
src/wix/test/WixToolsetTest.CoreIntegration/ExtensionFixture.cs
+39 -4
@@ -5,6 +5,7 @@ namespace WixToolsetTest.CoreIntegration
5 using System;
6 using System.IO;
7 using System.Linq;
8 + using System.Text.RegularExpressions;
9 using Example.Extension;
10 using WixBuildTools.TestSupport;
11 using WixToolset.Core.TestPackage;
@@ -23,10 +24,37 @@ namespace WixToolsetTest.CoreIntegration
24 var results = build.BuildAndQuery(Build, "Wix4Example");
25 WixAssert.CompareLineByLine(new[]
26 {
26 - "Wix4Example:Foo\tBar"
27 + "Wix4Example:Foo\tfilF5_pLhBuF5b4N9XEo52g_hUM5Lo\tBar"
28 }, results);
29 }
30
31 + [Fact]
32 + public void CanRoundtripExampleExtension()
33 + {
34 + var folder = TestData.Get(@"TestData", "ExampleExtension");
35 + var expectedOutputPath = Path.Combine(folder, "Decompiled-Expected.xml");
36 +
37 + var build = new Builder(folder, typeof(ExampleExtensionFactory), new[] { Path.Combine(folder, "data") });
38 + using (var fs = new DisposableFileSystem())
39 + {
40 + var decompileFolder = fs.GetFolder();
41 + var actualOutputPath = Path.Combine(decompileFolder, "decompiled.xml");
42 +
43 + build.BuildAndDecompileAndBuild(Build, Decompile, actualOutputPath);
44 +
45 + var expected = File.ReadAllLines(expectedOutputPath);
46 + var actual = File.ReadAllLines(actualOutputPath).Select(ReplaceGuids).ToArray(); ;
47 + WixAssert.CompareLineByLine(expected, actual);
48 + }
49 + }
50 +
51 + private static string ReplaceGuids(string value)
52 + {
53 + value = String.IsNullOrWhiteSpace(value) ? value : Regex.Replace(value, @" ProductCode=\""\{[a-fA-F0-9\-]+\}\""", " ProductCode=\"{GUID}\"");
54 + return String.IsNullOrWhiteSpace(value) ? value : Regex.Replace(value, @" Guid=\""\{[a-fA-F0-9\-]+\}\""", " Guid=\"{GUID}\"");
55 +
56 + }
57 +
58 [Fact]
59 public void CanBuildWithExampleExtension()
60 {
@@ -64,7 +92,8 @@ namespace WixToolsetTest.CoreIntegration
92
93 var example = section.Symbols.Where(t => t.Definition.Type == SymbolDefinitionType.MustBeFromAnExtension).Single();
94 Assert.Equal("Foo", example.Id?.Id);
67 - Assert.Equal("Bar", example[0].AsString());
95 + Assert.Equal("filF5_pLhBuF5b4N9XEo52g_hUM5Lo", example[0].AsString());
96 + Assert.Equal("Bar", example[1].AsString());
97 }
98 }
99
@@ -192,8 +221,14 @@ namespace WixToolsetTest.CoreIntegration
221
222 private static void Build(string[] args)
223 {
195 - var result = WixRunner.Execute(args)
196 - .AssertSuccess();
224 + var result = WixRunner.Execute(args);
225 + result.AssertSuccess();
226 + }
227 +
228 + private static void Decompile(string[] args)
229 + {
230 + var result = WixRunner.Execute(args);
231 + result.AssertSuccess();
232 }
233 }
234 }
src/wix/test/WixToolsetTest.CoreIntegration/TestData/AppSearch/DecompiledNestedDirSearchUnderRegSearch.wxs
+3 -3
@@ -1,9 +1,9 @@
1 <Wix xmlns="http://wixtoolset.org/schemas/v4/wxs">
2 - <Package Codepage="1252" Language="1033" Manufacturer="Example Corporation" Name="MsiPackage" UpgradeCode="{12E4699F-E774-4D05-8A01-5BDD41BBA127}" Version="1.0.0.0" ProductCode="{33C58183-7333-4257-AEFD-6705DA66E617}">
2 + <Package Codepage="1252" Language="1033" Manufacturer="Example Corporation" Name="MsiPackage" UpgradeCode="{12E4699F-E774-4D05-8A01-5BDD41BBA127}" Version="1.0.0.0" ProductCode="{33C58183-7333-4257-AEFD-6705DA66E617}" Compressed="no">
3 <StandardDirectory Id="ProgramFilesFolder">
4 <Directory Id="INSTALLFOLDER" Name="MsiPackage" ShortName="ykd0udtb">
5 <Component Id="test.txt" Guid="{E597A58A-03CB-50D8-93E3-DABA263F233A}" Bitness="always32">
6 - <File Id="test.txt" Name="test.txt" KeyPath="yes" Source="MsiPackage\test.txt" />
6 + <File Id="test.txt" Name="test.txt" KeyPath="yes" Source="PFiles\MsiPackage\test.txt" />
7 </Component>
8 </Directory>
9 </StandardDirectory>
@@ -39,4 +39,4 @@
39 </RegistrySearch>
40 </Property>
41 </Package>
42 -</Wix>
\ No newline at end of file
42 +</Wix>
src/wix/test/WixToolsetTest.CoreIntegration/TestData/Class/DecompiledOldClassTableDef.wxs
+3 -3
@@ -1,12 +1,12 @@
1 <Wix xmlns="http://wixtoolset.org/schemas/v4/wxs">
2 - <Package Codepage="1252" Language="1033" Manufacturer="Example Corporation" Name="MsiPackage" UpgradeCode="{12E4699F-E774-4D05-8A01-5BDD41BBA127}" Version="1.0.0.0" ProductCode="{FE17A505-11A9-44D2-8D94-EB6BEAB8FF93}">
2 + <Package Codepage="1252" Language="1033" Manufacturer="Example Corporation" Name="MsiPackage" UpgradeCode="{12E4699F-E774-4D05-8A01-5BDD41BBA127}" Version="1.0.0.0" ProductCode="{FE17A505-11A9-44D2-8D94-EB6BEAB8FF93}" Compressed="no">
3 <StandardDirectory Id="ProgramFilesFolder">
4 <Directory Id="INSTALLFOLDER" Name="MsiPackage" ShortName="oekcr5lq">
5 <Component Id="ProgIdComp" Guid="{5B3B3FC1-533D-4C29-BFB3-0E88B51E59D8}" Bitness="always32">
6 <Class Id="{F12A6F69-117F-471F-AE73-F8E74218F498}" Context="LocalServer32" Description="FakeClassF12A" Advertise="yes">
7 <ProgId Id="73E7DF7E-EFAC-4E11-90E2-6EBAEB8DE58D" Description="FakeClassF12A" Advertise="yes" />
8 </Class>
9 - <File Id="filTki4JQ2gSapF7wK4K1vd.4mDSFQ" Name="ProgIdComp.txt" KeyPath="yes" ShortName="bnvvntsc.txt" Source="MsiPackage\ProgIdComp.txt" />
9 + <File Id="filTki4JQ2gSapF7wK4K1vd.4mDSFQ" Name="ProgIdComp.txt" KeyPath="yes" ShortName="bnvvntsc.txt" Source="PFiles\MsiPackage\ProgIdComp.txt" />
10 <RegistryValue Id="regUIIK326nDZpkWHuexeF58EikQvA" Root="HKCR" Key="73E7DF7E-EFAC-4E11-90E2-6EBAEB8DE58D" Name="NoOpen" Value="NoOpen73E7" Type="string" />
11 <RegistryValue Id="regY1F4E2lvu_Up6gV6c3jeN5ukn8s" Root="HKCR" Key="CLSID\{F12A6F69-117F-471F-AE73-F8E74218F498}\LocalServer32" Name="ThreadingModel" Value="Apartment" Type="string" />
12 <RegistryValue Id="regvrhMurMp98anbQJkpgA8yJCefdM" Root="HKCR" Key="CLSID\{F12A6F69-117F-471F-AE73-F8E74218F498}\Version" Value="0.0.0.1" Type="string" />
@@ -19,4 +19,4 @@
19 <MajorUpgrade DowngradeErrorMessage="A newer version of [ProductName] is already installed." />
20 <Media Id="1" />
21 </Package>
22 -</Wix>
\ No newline at end of file
22 +</Wix>
src/wix/test/WixToolsetTest.CoreIntegration/TestData/CustomTable/CustomTable-Expected.wxs
+2 -2
@@ -1,5 +1,5 @@
1 <Wix xmlns="http://wixtoolset.org/schemas/v4/wxs">
2 - <Package Codepage="1252" Language="1033" Manufacturer="Example Corporation" Name="MsiPackage" UpgradeCode="{12E4699F-E774-4D05-8A01-5BDD41BBA127}" Version="1.0.0.0" ProductCode="{83F9C623-26FE-42AB-951E-170022117F54}">
2 + <Package Codepage="1252" Language="1033" Manufacturer="Example Corporation" Name="MsiPackage" UpgradeCode="{12E4699F-E774-4D05-8A01-5BDD41BBA127}" Version="1.0.0.0" ProductCode="{83F9C623-26FE-42AB-951E-170022117F54}" Compressed="no">
3 <CustomTable Id="CustomTable1">
4 <Column Id="Column1" PrimaryKey="yes" Type="string" Width="0" Category="text" Description="The first custom column." />
5 <Column Id="Component_" Type="string" Width="72" KeyTable="Component" KeyColumn="1" Description="The custom table's Component reference" />
@@ -15,7 +15,7 @@
15 <StandardDirectory Id="ProgramFiles6432Folder">
16 <Directory Id="INSTALLFOLDER" Name="MsiPackage" ShortName="1egc1laj">
17 <Component Id="filcV1yrx0x8wJWj4qMzcH21jwkPko" Guid="{E597A58A-03CB-50D8-93E3-DABA263F233A}" Bitness="always32">
18 - <File Id="filcV1yrx0x8wJWj4qMzcH21jwkPko" Name="test.txt" KeyPath="yes" Source="MsiPackage\test.txt" />
18 + <File Id="filcV1yrx0x8wJWj4qMzcH21jwkPko" Name="test.txt" KeyPath="yes" Source=".\MsiPackage\test.txt" />
19 </Component>
20 </Directory>
21 </StandardDirectory>
src/wix/test/WixToolsetTest.CoreIntegration/TestData/DecompileNullComponent/Expected.wxs
+1 -1
@@ -1,5 +1,5 @@
1 <Wix xmlns="http://wixtoolset.org/schemas/v4/wxs">
2 - <Package Codepage="65001" Language="1033" Manufacturer="Example Corporation" Name="MsiPackage" UpgradeCode="{047730A5-30FE-4A62-A520-DA9381B8226A}" Version="1.0.0.0" Compressed="yes" InstallerVersion="200" ProductCode="{6F9B5694-F0F1-437C-919B-0D2DAF2D9DEA}">
2 + <Package Codepage="65001" Language="1033" Manufacturer="Example Corporation" Name="MsiPackage" UpgradeCode="{047730A5-30FE-4A62-A520-DA9381B8226A}" Version="1.0.0.0" InstallerVersion="200" ProductCode="{6F9B5694-F0F1-437C-919B-0D2DAF2D9DEA}">
3 <StandardDirectory Id="ProgramFilesFolder">
4 <Directory Id="INSTALLFOLDER" Name="MsiPackage" ShortName="oekcr5lq">
5 <Component Id="filcV1yrx0x8wJWj4qMzcH21jwkPko" Guid="" Bitness="always32">
src/wix/test/WixToolsetTest.CoreIntegration/TestData/DecompileSingleFileCompressed/Expected.wxs
+1 -1
@@ -1,5 +1,5 @@
1 <Wix xmlns="http://wixtoolset.org/schemas/v4/wxs">
2 - <Package Codepage="65001" Language="1033" Manufacturer="Example Corporation" Name="MsiPackage" UpgradeCode="{047730A5-30FE-4A62-A520-DA9381B8226A}" Version="1.0.0.0" Compressed="yes" InstallerVersion="200" ProductCode="{6F9B5694-F0F1-437C-919B-0D2DAF2D9DEA}">
2 + <Package Codepage="65001" Language="1033" Manufacturer="Example Corporation" Name="MsiPackage" UpgradeCode="{047730A5-30FE-4A62-A520-DA9381B8226A}" Version="1.0.0.0" InstallerVersion="200" ProductCode="{6F9B5694-F0F1-437C-919B-0D2DAF2D9DEA}">
3 <StandardDirectory Id="ProgramFilesFolder">
4 <Directory Id="INSTALLFOLDER" Name="MsiPackage" ShortName="oekcr5lq">
5 <Component Id="filcV1yrx0x8wJWj4qMzcH21jwkPko" Guid="{E597A58A-03CB-50D8-93E3-DABA263F233A}" Bitness="always32">
src/wix/test/WixToolsetTest.CoreIntegration/TestData/DecompileSingleFileCompressed64/Expected.wxs
+1 -1
@@ -1,5 +1,5 @@
1 <Wix xmlns="http://wixtoolset.org/schemas/v4/wxs">
2 - <Package Codepage="65001" Language="1033" Manufacturer="Example Corporation" Name="MsiPackage" UpgradeCode="{047730A5-30FE-4A62-A520-DA9381B8226A}" Version="1.0.0.0" Compressed="yes" ProductCode="{C51B773A-B3BE-4F29-A8A9-549AAF7FF6EC}">
2 + <Package Codepage="65001" Language="1033" Manufacturer="Example Corporation" Name="MsiPackage" UpgradeCode="{047730A5-30FE-4A62-A520-DA9381B8226A}" Version="1.0.0.0" ProductCode="{C51B773A-B3BE-4F29-A8A9-549AAF7FF6EC}">
3 <StandardDirectory Id="ProgramFiles64Folder">
4 <Directory Id="INSTALLFOLDER" Name="MsiPackage" ShortName="oekcr5lq">
5 <Component Id="filcV1yrx0x8wJWj4qMzcH21jwkPko" Guid="{E597A58A-03CB-50D8-93E3-DABA263F233A}" Bitness="always64">
src/wix/test/WixToolsetTest.CoreIntegration/TestData/ExampleExtension/Decompiled-Expected.xml new
+19
@@ -0,0 +1,19 @@
1 +<?xml version="1.0" encoding="utf-8"?>
2 +<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs">
3 + <Package Codepage="1252" Manufacturer="Example Corporation" ProductCode="{GUID}" Language="1033" Name="MsiPackage" Version="1.0.0.0" UpgradeCode="{047730A5-30FE-4A62-A520-DA9381B8226A}" InstallerVersion="200" Compressed="no">
4 + <Property Id="ExampleProperty" Value="(null)" />
5 + <StandardDirectory Id="ProgramFilesFolder">
6 + <Directory Id="INSTALLFOLDER" ShortName="oekcr5lq" Name="MsiPackage">
7 + <Component Id="filF5_pLhBuF5b4N9XEo52g_hUM5Lo" Guid="{GUID}" Bitness="always32">
8 + <Example Id="Foo" Value="Bar" xmlns="http://www.example.com/scheams/v1/wxs" />
9 + <File Id="filF5_pLhBuF5b4N9XEo52g_hUM5Lo" Name="example.txt" KeyPath="yes" Source="PFiles\MsiPackage\example.txt" />
10 + </Component>
11 + </Directory>
12 + </StandardDirectory>
13 + <Feature Id="ProductFeature" Title="MsiPackage" Level="1">
14 + <ComponentRef Id="filF5_pLhBuF5b4N9XEo52g_hUM5Lo" />
15 + </Feature>
16 + <Media Id="1" />
17 + <MajorUpgrade DowngradeErrorMessage="A newer version of [ProductName] is already installed." />
18 + </Package>
19 +</Wix>
src/wix/test/WixToolsetTest.CoreIntegration/TestData/SequenceTables/DecompiledSequenceTables.wxs
+3 -3
@@ -1,10 +1,10 @@
1 <Wix xmlns="http://wixtoolset.org/schemas/v4/wxs">
2 - <Package Codepage="1252" Language="1033" Manufacturer="Example Corporation" Name="MsiPackage" UpgradeCode="{12E4699F-E774-4D05-8A01-5BDD41BBA127}" Version="1.0.0.0" ProductCode="{74C29381-1915-4948-B8B4-5646806A0BD4}">
2 + <Package Codepage="1252" Language="1033" Manufacturer="Example Corporation" Name="MsiPackage" UpgradeCode="{12E4699F-E774-4D05-8A01-5BDD41BBA127}" Version="1.0.0.0" ProductCode="{74C29381-1915-4948-B8B4-5646806A0BD4}" Compressed="no">
3 <CustomAction Id="CustomAction2" Property="TestAdvtExecuteSequenceProperty" Value="1" />
4 <StandardDirectory Id="ProgramFilesFolder">
5 <Directory Id="INSTALLFOLDER" Name="MsiPackage" ShortName="ykd0udtb">
6 <Component Id="test.txt" Guid="{E597A58A-03CB-50D8-93E3-DABA263F233A}" Bitness="always32">
7 - <File Id="test.txt" Name="test.txt" KeyPath="yes" Source="MsiPackage\test.txt" />
7 + <File Id="test.txt" Name="test.txt" KeyPath="yes" Source="PFiles\MsiPackage\test.txt" />
8 </Component>
9 </Directory>
10 </StandardDirectory>
@@ -29,4 +29,4 @@
29 <Custom Action="CustomAction2" After="CostInitialize" />
30 </AdvertiseExecuteSequence>
31 </Package>
32 -</Wix>
\ No newline at end of file
32 +</Wix>
src/wix/test/WixToolsetTest.CoreIntegration/TestData/Shortcut/DecompiledShortcuts.wxs
+3 -3
@@ -1,9 +1,9 @@
1 <Wix xmlns="http://wixtoolset.org/schemas/v4/wxs">
2 - <Package Codepage="1252" Language="1033" Manufacturer="Example Corporation" Name="MsiPackage" UpgradeCode="{12E4699F-E774-4D05-8A01-5BDD41BBA127}" Version="1.0.0.0" ProductCode="{6CA94D1D-B568-4ED6-9EBC-3534C85970BB}">
2 + <Package Codepage="1252" Language="1033" Manufacturer="Example Corporation" Name="MsiPackage" UpgradeCode="{12E4699F-E774-4D05-8A01-5BDD41BBA127}" Version="1.0.0.0" ProductCode="{6CA94D1D-B568-4ED6-9EBC-3534C85970BB}" Compressed="no">
3 <StandardDirectory Id="ProgramFilesFolder">
4 <Directory Id="INSTALLFOLDER" Name="MsiPackage" ShortName="ykd0udtb">
5 <Component Id="ShortcutComp" Guid="{5B3B3FC1-533D-4C29-BFB3-0E88B51E59D8}" Bitness="always32">
6 - <File Id="test.txt" Name="test.txt" KeyPath="yes" Source="MsiPackage\test.txt" />
6 + <File Id="test.txt" Name="test.txt" KeyPath="yes" Source="PFiles\MsiPackage\test.txt" />
7 <Shortcut Id="FileTargetShortcut" Directory="INSTALLFOLDER" Name="FileTargetShortcut" ShortName="lm2tdtqp" Target="[#test.txt]" />
8 <Shortcut Id="CustomTargetShortcut" Directory="INSTALLFOLDER" Name="Planner" ShortName="PLANNER" Target="[INSTALLFOLDER]custom.target" />
9 <Shortcut Id="AdvtShortcut" Directory="INSTALLFOLDER" Name="AdvtShortcut" ShortName="mdbqel9r" Advertise="yes" />
@@ -16,4 +16,4 @@
16 <MajorUpgrade DowngradeErrorMessage="A newer version of [ProductName] is already installed." />
17 <Media Id="1" />
18 </Package>
19 -</Wix>
\ No newline at end of file
19 +</Wix>
src/wix/test/WixToolsetTest.CoreIntegration/WixlibFixture.cs
+3 -2
@@ -289,7 +289,7 @@ namespace WixToolsetTest.CoreIntegration
289
290 var example = section.Symbols.Where(t => t.Definition.Type == SymbolDefinitionType.MustBeFromAnExtension).Single();
291 Assert.Equal("Foo", example.Id?.Id);
292 - Assert.Equal("Bar", example[0].AsString());
292 + Assert.Equal("Bar", example[1].AsString());
293 }
294 }
295
@@ -352,7 +352,8 @@ namespace WixToolsetTest.CoreIntegration
352
353 var examples = section.Symbols.Where(t => t.Definition.Type == SymbolDefinitionType.MustBeFromAnExtension).ToArray();
354 WixAssert.CompareLineByLine(new string[] { "Foo", "Other" }, examples.Select(t => t.Id?.Id).ToArray());
355 - WixAssert.CompareLineByLine(new[] { "Bar", "Value" }, examples.Select(t => t[0].AsString()).ToArray());
355 + WixAssert.CompareLineByLine(new[] { "filF5_pLhBuF5b4N9XEo52g_hUM5Lo", "filvxdStJhRE_M5kbpLsTZJXbs34Sg" }, examples.Select(t => t[0].AsString()).ToArray());
356 + WixAssert.CompareLineByLine(new[] { "Bar", "Value" }, examples.Select(t => t[1].AsString()).ToArray());
357 }
358 }
359 }