aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRob Mensching <rob@firegiant.com>2022-03-30 10:35:02 -0700
committerRob Mensching <rob@firegiant.com>2022-03-30 14:12:12 -0700
commit5d08e0a4bbf4e4ba28300b8bace1089b64b198d7 (patch)
tree1cf7d1f79d45cc3acc32f19cabb1efedd7a4a3dc
parentc86a2148f6dd7bfcd6637b6e1c9e7b5a9b53a996 (diff)
downloadwix-5d08e0a4bbf4e4ba28300b8bace1089b64b198d7.tar.gz
wix-5d08e0a4bbf4e4ba28300b8bace1089b64b198d7.tar.bz2
wix-5d08e0a4bbf4e4ba28300b8bace1089b64b198d7.zip
Implement IWindowsInstallerDecompileExtensions
Update Util extension to validate extension model and fix some small issues in MSI decompiling. Fixes 6367
-rw-r--r--src/api/wix/WixToolset.Extensibility/BaseWindowsInstallerBackendBinderExtension.cs8
-rw-r--r--src/api/wix/WixToolset.Extensibility/BaseWindowsInstallerDecompilerExtension.cs77
-rw-r--r--src/api/wix/WixToolset.Extensibility/Data/IWindowsInstallerDecompileContext.cs12
-rw-r--r--src/api/wix/WixToolset.Extensibility/Data/IWindowsInstallerDecompileResult.cs15
-rw-r--r--src/api/wix/WixToolset.Extensibility/DecompilerExtension.cs61
-rw-r--r--src/api/wix/WixToolset.Extensibility/IWindowsInstallerDecompilerExtension.cs30
-rw-r--r--src/api/wix/WixToolset.Extensibility/Services/IWindowsInstallerDecompilerHelper.cs180
-rw-r--r--src/ext/Util/Util.wixext.sln4
-rw-r--r--src/ext/Util/Util.wixext.v3.ncrunchsolution6
-rw-r--r--src/ext/Util/test/WixToolsetTest.Util/UtilExtensionFixture.cs20
-rw-r--r--src/ext/Util/wixext/UtilCompiler.cs36
-rw-r--r--src/ext/Util/wixext/UtilConstants.cs21
-rw-r--r--src/ext/Util/wixext/UtilDecompiler.cs1272
-rw-r--r--src/ext/Util/wixext/UtilExtensionFactory.cs1
-rw-r--r--src/ext/Util/wixext/UtilTableDefinitions.cs2
-rw-r--r--src/internal/WixBuildTools.TestSupport/Builder.cs88
-rw-r--r--src/wix/WixToolset.Core.Burn/BurnExtensionFactory.cs2
-rw-r--r--src/wix/WixToolset.Core.WindowsInstaller/CommandLine/DecompilerSubcommand.cs7
-rw-r--r--src/wix/WixToolset.Core.WindowsInstaller/Decompile/DecompileMsiOrMsmCommand.cs91
-rw-r--r--src/wix/WixToolset.Core.WindowsInstaller/Decompile/Decompiler.cs662
-rw-r--r--src/wix/WixToolset.Core.WindowsInstaller/ExtensibilityServices/WindowsInstallerDecompilerHelper.cs133
-rw-r--r--src/wix/WixToolset.Core.WindowsInstaller/Unbind/UnbindDatabaseCommand.cs2
-rw-r--r--src/wix/WixToolset.Core.WindowsInstaller/WindowsInstallerDecompileContext.cs4
-rw-r--r--src/wix/WixToolset.Core.WindowsInstaller/WindowsInstallerDecompileResult.cs2
-rw-r--r--src/wix/WixToolset.Core.WindowsInstaller/WindowsInstallerDecompiler.cs85
-rw-r--r--src/wix/WixToolset.Core.WindowsInstaller/WixToolsetCoreServiceProviderExtensions.cs5
-rw-r--r--src/wix/test/Example.Extension/ExampleCompilerExtension.cs18
-rw-r--r--src/wix/test/Example.Extension/ExampleConstants.cs13
-rw-r--r--src/wix/test/Example.Extension/ExampleExtensionFactory.cs4
-rw-r--r--src/wix/test/Example.Extension/ExampleRow.cs8
-rw-r--r--src/wix/test/Example.Extension/ExampleSymbol.cs7
-rw-r--r--src/wix/test/Example.Extension/ExampleSymbolDefinitions.cs1
-rw-r--r--src/wix/test/Example.Extension/ExampleTableDefinitions.cs3
-rw-r--r--src/wix/test/Example.Extension/ExampleWindowsInstallerBackendExtension.cs11
-rw-r--r--src/wix/test/Example.Extension/ExampleWindowsInstallerDecompilerExtension.cs42
-rw-r--r--src/wix/test/WixToolsetTest.CoreIntegration/ExtensionFixture.cs43
-rw-r--r--src/wix/test/WixToolsetTest.CoreIntegration/TestData/AppSearch/DecompiledNestedDirSearchUnderRegSearch.wxs6
-rw-r--r--src/wix/test/WixToolsetTest.CoreIntegration/TestData/Class/DecompiledOldClassTableDef.wxs6
-rw-r--r--src/wix/test/WixToolsetTest.CoreIntegration/TestData/CustomTable/CustomTable-Expected.wxs4
-rw-r--r--src/wix/test/WixToolsetTest.CoreIntegration/TestData/DecompileNullComponent/Expected.wxs2
-rw-r--r--src/wix/test/WixToolsetTest.CoreIntegration/TestData/DecompileSingleFileCompressed/Expected.wxs2
-rw-r--r--src/wix/test/WixToolsetTest.CoreIntegration/TestData/DecompileSingleFileCompressed64/Expected.wxs2
-rw-r--r--src/wix/test/WixToolsetTest.CoreIntegration/TestData/ExampleExtension/Decompiled-Expected.xml19
-rw-r--r--src/wix/test/WixToolsetTest.CoreIntegration/TestData/SequenceTables/DecompiledSequenceTables.wxs6
-rw-r--r--src/wix/test/WixToolsetTest.CoreIntegration/TestData/Shortcut/DecompiledShortcuts.wxs6
-rw-r--r--src/wix/test/WixToolsetTest.CoreIntegration/WixlibFixture.cs5
46 files changed, 1644 insertions, 1390 deletions
diff --git a/src/api/wix/WixToolset.Extensibility/BaseWindowsInstallerBackendBinderExtension.cs b/src/api/wix/WixToolset.Extensibility/BaseWindowsInstallerBackendBinderExtension.cs
index e48579d7..a54f05fc 100644
--- a/src/api/wix/WixToolset.Extensibility/BaseWindowsInstallerBackendBinderExtension.cs
+++ b/src/api/wix/WixToolset.Extensibility/BaseWindowsInstallerBackendBinderExtension.cs
@@ -4,7 +4,6 @@ namespace WixToolset.Extensibility
4{ 4{
5 using System; 5 using System;
6 using System.Collections.Generic; 6 using System.Collections.Generic;
7 using System.Linq;
8 using WixToolset.Data; 7 using WixToolset.Data;
9 using WixToolset.Data.Symbols; 8 using WixToolset.Data.Symbols;
10 using WixToolset.Data.WindowsInstaller; 9 using WixToolset.Data.WindowsInstaller;
@@ -12,7 +11,7 @@ namespace WixToolset.Extensibility
12 using WixToolset.Extensibility.Services; 11 using WixToolset.Extensibility.Services;
13 12
14 /// <summary> 13 /// <summary>
15 /// Base class for creating a preprocessor extension. 14 /// Base class for creating a windows installer backend extension.
16 /// </summary> 15 /// </summary>
17 public abstract class BaseWindowsInstallerBackendBinderExtension : IWindowsInstallerBackendBinderExtension 16 public abstract class BaseWindowsInstallerBackendBinderExtension : IWindowsInstallerBackendBinderExtension
18 { 17 {
@@ -39,7 +38,10 @@ namespace WixToolset.Extensibility
39 /// <summary> 38 /// <summary>
40 /// Creates a resolved cabinet result. 39 /// Creates a resolved cabinet result.
41 /// </summary> 40 /// </summary>
42 protected IResolvedCabinet CreateResolvedCabinet() => this.Context.ServiceProvider.GetService<IResolvedCabinet>(); 41 protected IResolvedCabinet CreateResolvedCabinet()
42 {
43 return this.Context.ServiceProvider.GetService<IResolvedCabinet>();
44 }
43 45
44 /// <summary> 46 /// <summary>
45 /// See <see cref="IWindowsInstallerBackendBinderExtension.PreBackendBind(IBindContext)"/> 47 /// See <see cref="IWindowsInstallerBackendBinderExtension.PreBackendBind(IBindContext)"/>
diff --git a/src/api/wix/WixToolset.Extensibility/BaseWindowsInstallerDecompilerExtension.cs b/src/api/wix/WixToolset.Extensibility/BaseWindowsInstallerDecompilerExtension.cs
new file mode 100644
index 00000000..8072cd88
--- /dev/null
+++ b/src/api/wix/WixToolset.Extensibility/BaseWindowsInstallerDecompilerExtension.cs
@@ -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
3namespace 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}
diff --git a/src/api/wix/WixToolset.Extensibility/Data/IWindowsInstallerDecompileContext.cs b/src/api/wix/WixToolset.Extensibility/Data/IWindowsInstallerDecompileContext.cs
index f744121a..27d30a5a 100644
--- a/src/api/wix/WixToolset.Extensibility/Data/IWindowsInstallerDecompileContext.cs
+++ b/src/api/wix/WixToolset.Extensibility/Data/IWindowsInstallerDecompileContext.cs
@@ -7,7 +7,7 @@ namespace WixToolset.Extensibility.Data
7 using WixToolset.Data; 7 using WixToolset.Data;
8 8
9 /// <summary> 9 /// <summary>
10 /// The context used to decompile Windows Installer packages. 10 /// The context used to decompile a Windows Installer database.
11 /// </summary> 11 /// </summary>
12 public interface IWindowsInstallerDecompileContext 12 public interface IWindowsInstallerDecompileContext
13 { 13 {
@@ -32,6 +32,16 @@ namespace WixToolset.Extensibility.Data
32 IReadOnlyCollection<IWindowsInstallerDecompilerExtension> Extensions { get; set; } 32 IReadOnlyCollection<IWindowsInstallerDecompilerExtension> Extensions { get; set; }
33 33
34 /// <summary> 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>
35 /// Gets or sets the folder where content is extracted. 45 /// Gets or sets the folder where content is extracted.
36 /// </summary> 46 /// </summary>
37 string ExtractFolder { get; set; } 47 string ExtractFolder { get; set; }
diff --git a/src/api/wix/WixToolset.Extensibility/Data/IWindowsInstallerDecompileResult.cs b/src/api/wix/WixToolset.Extensibility/Data/IWindowsInstallerDecompileResult.cs
index 3b1dd815..724dd7fc 100644
--- a/src/api/wix/WixToolset.Extensibility/Data/IWindowsInstallerDecompileResult.cs
+++ b/src/api/wix/WixToolset.Extensibility/Data/IWindowsInstallerDecompileResult.cs
@@ -6,13 +6,24 @@ namespace WixToolset.Extensibility.Data
6 using System.Xml.Linq; 6 using System.Xml.Linq;
7 using WixToolset.Data; 7 using WixToolset.Data;
8 8
9#pragma warning disable 1591 // TODO: add documentation 9 /// <summary>
10 /// The result from decompiling a Windows Installer database.
11 /// </summary>
10 public interface IWindowsInstallerDecompileResult 12 public interface IWindowsInstallerDecompileResult
11 { 13 {
14 /// <summary>
15 /// Decompiled document.
16 /// </summary>
12 XDocument Document { get; set; } 17 XDocument Document { get; set; }
13 18
14 IReadOnlyCollection<string> ExtractedFilePaths { get; set; } 19 /// <summary>
20 /// Extracted paths.
21 /// </summary>
22 IList<string> ExtractedFilePaths { get; set; }
15 23
24 /// <summary>
25 /// Decompiled platform.
26 /// </summary>
16 Platform? Platform { get; set; } 27 Platform? Platform { get; set; }
17 } 28 }
18} 29}
diff --git a/src/api/wix/WixToolset.Extensibility/DecompilerExtension.cs b/src/api/wix/WixToolset.Extensibility/DecompilerExtension.cs
deleted file mode 100644
index b492cf3a..00000000
--- a/src/api/wix/WixToolset.Extensibility/DecompilerExtension.cs
+++ /dev/null
@@ -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
3namespace 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}
diff --git a/src/api/wix/WixToolset.Extensibility/IWindowsInstallerDecompilerExtension.cs b/src/api/wix/WixToolset.Extensibility/IWindowsInstallerDecompilerExtension.cs
index add5f886..f7d54799 100644
--- a/src/api/wix/WixToolset.Extensibility/IWindowsInstallerDecompilerExtension.cs
+++ b/src/api/wix/WixToolset.Extensibility/IWindowsInstallerDecompilerExtension.cs
@@ -2,21 +2,45 @@
2 2
3namespace WixToolset.Extensibility 3namespace WixToolset.Extensibility
4{ 4{
5 using WixToolset.Data; 5 using System.Collections.Generic;
6 using WixToolset.Data.WindowsInstaller; 6 using WixToolset.Data.WindowsInstaller;
7 using WixToolset.Extensibility.Data; 7 using WixToolset.Extensibility.Data;
8 8
9 /// <summary> 9 /// <summary>
10 /// Interface all binder extensions implement. 10 /// Interface all windows installer decompiler extensions implement.
11 /// </summary> 11 /// </summary>
12 public interface IWindowsInstallerDecompilerExtension 12 public interface IWindowsInstallerDecompilerExtension
13 { 13 {
14 /// <summary> 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>
15 /// Called before decompiling occurs. 21 /// Called before decompiling occurs.
16 /// </summary> 22 /// </summary>
23 /// <param name="context">Decompile context.</param>
17 void PreDecompile(IWindowsInstallerDecompileContext context); 24 void PreDecompile(IWindowsInstallerDecompileContext context);
18 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);
20 44
21 /// <summary> 45 /// <summary>
22 /// Called after all output changes occur and right before the output is bound into its final format. 46 /// Called after all output changes occur and right before the output is bound into its final format.
diff --git a/src/api/wix/WixToolset.Extensibility/Services/IWindowsInstallerDecompilerHelper.cs b/src/api/wix/WixToolset.Extensibility/Services/IWindowsInstallerDecompilerHelper.cs
new file mode 100644
index 00000000..1f5ac47a
--- /dev/null
+++ b/src/api/wix/WixToolset.Extensibility/Services/IWindowsInstallerDecompilerHelper.cs
@@ -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
3namespace 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}
diff --git a/src/ext/Util/Util.wixext.sln b/src/ext/Util/Util.wixext.sln
index 259a8164..0168dab9 100644
--- a/src/ext/Util/Util.wixext.sln
+++ b/src/ext/Util/Util.wixext.sln
@@ -1,7 +1,7 @@
1 1
2Microsoft Visual Studio Solution File, Format Version 12.00 2Microsoft Visual Studio Solution File, Format Version 12.00
3# Visual Studio Version 16 3# Visual Studio Version 17
4VisualStudioVersion = 16.0.30204.135 4VisualStudioVersion = 17.1.32228.430
5MinimumVisualStudioVersion = 15.0.26124.0 5MinimumVisualStudioVersion = 15.0.26124.0
6Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "utilbe", "be\utilbe.vcxproj", "{630C1EE7-2517-4A8C-83E3-DA1150308B58}" 6Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "utilbe", "be\utilbe.vcxproj", "{630C1EE7-2517-4A8C-83E3-DA1150308B58}"
7EndProject 7EndProject
diff --git a/src/ext/Util/Util.wixext.v3.ncrunchsolution b/src/ext/Util/Util.wixext.v3.ncrunchsolution
new file mode 100644
index 00000000..10420ac9
--- /dev/null
+++ b/src/ext/Util/Util.wixext.v3.ncrunchsolution
@@ -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
diff --git a/src/ext/Util/test/WixToolsetTest.Util/UtilExtensionFixture.cs b/src/ext/Util/test/WixToolsetTest.Util/UtilExtensionFixture.cs
index 883f9794..d2a4e34b 100644
--- a/src/ext/Util/test/WixToolsetTest.Util/UtilExtensionFixture.cs
+++ b/src/ext/Util/test/WixToolsetTest.Util/UtilExtensionFixture.cs
@@ -6,8 +6,6 @@ namespace WixToolsetTest.Util
6 using System.Linq; 6 using System.Linq;
7 using WixBuildTools.TestSupport; 7 using WixBuildTools.TestSupport;
8 using WixToolset.Core.TestPackage; 8 using WixToolset.Core.TestPackage;
9 using WixToolset.Data;
10 using WixToolset.Data.Symbols;
11 using WixToolset.Util; 9 using WixToolset.Util;
12 using Xunit; 10 using Xunit;
13 11
@@ -56,6 +54,18 @@ namespace WixToolsetTest.Util
56 } 54 }
57 55
58 [Fact] 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]
59 public void CanBuildCloseApplication() 69 public void CanBuildCloseApplication()
60 { 70 {
61 var folder = TestData.Get(@"TestData\CloseApplication"); 71 var folder = TestData.Get(@"TestData\CloseApplication");
@@ -313,5 +323,11 @@ namespace WixToolsetTest.Util
313 var result = WixRunner.Execute(newArgs.ToArray()); 323 var result = WixRunner.Execute(newArgs.ToArray());
314 result.AssertSuccess(); 324 result.AssertSuccess();
315 } 325 }
326
327 private static void Decompile(string[] args)
328 {
329 var result = WixRunner.Execute(args);
330 result.AssertSuccess();
331 }
316 } 332 }
317} 333}
diff --git a/src/ext/Util/wixext/UtilCompiler.cs b/src/ext/Util/wixext/UtilCompiler.cs
index 45079150..09a90928 100644
--- a/src/ext/Util/wixext/UtilCompiler.cs
+++ b/src/ext/Util/wixext/UtilCompiler.cs
@@ -19,7 +19,7 @@ namespace WixToolset.Util
19 /// <summary> 19 /// <summary>
20 /// The compiler for the WiX Toolset Utility Extension. 20 /// The compiler for the WiX Toolset Utility Extension.
21 /// </summary> 21 /// </summary>
22 public sealed class UtilCompiler : BaseCompilerExtension 22 internal sealed class UtilCompiler : BaseCompilerExtension
23 { 23 {
24 // user creation attributes definitions (from sca.h) 24 // user creation attributes definitions (from sca.h)
25 internal const int UserDontExpirePasswrd = 0x00000001; 25 internal const int UserDontExpirePasswrd = 0x00000001;
@@ -37,7 +37,7 @@ namespace WixToolset.Util
37 37
38 private static readonly Regex FindPropertyBrackets = new Regex(@"\[(?!\\|\])|(?<!\[\\\]|\[\\|\\\[)\]", RegexOptions.ExplicitCapture | RegexOptions.Compiled); 38 private static readonly Regex FindPropertyBrackets = new Regex(@"\[(?!\\|\])|(?<!\[\\\]|\[\\|\\\[)\]", RegexOptions.ExplicitCapture | RegexOptions.Compiled);
39 39
40 public override XNamespace Namespace => "http://wixtoolset.org/schemas/v4/wxs/util"; 40 public override XNamespace Namespace => UtilConstants.Namespace;
41 41
42 /// <summary> 42 /// <summary>
43 /// Types of Internet shortcuts. 43 /// Types of Internet shortcuts.
@@ -94,13 +94,10 @@ namespace WixToolset.Util
94 var createFolderId = context["DirectoryId"]; 94 var createFolderId = context["DirectoryId"];
95 var createFolderComponentId = context["ComponentId"]; 95 var createFolderComponentId = context["ComponentId"];
96 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
100 switch (element.Name.LocalName) 97 switch (element.Name.LocalName)
101 { 98 {
102 case "PermissionEx": 99 case "PermissionEx":
103 this.ParsePermissionExElement(intermediate, section, element, createFolderId, createFolderComponentId, createFolderWin64, "CreateFolder"); 100 this.ParsePermissionExElement(intermediate, section, element, createFolderId, createFolderComponentId, "CreateFolder");
104 break; 101 break;
105 default: 102 default:
106 this.ParseHelper.UnexpectedElement(parentElement, element); 103 this.ParseHelper.UnexpectedElement(parentElement, element);
@@ -159,16 +156,13 @@ namespace WixToolset.Util
159 var fileId = context["FileId"]; 156 var fileId = context["FileId"];
160 var fileComponentId = context["ComponentId"]; 157 var fileComponentId = context["ComponentId"];
161 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
165 switch (element.Name.LocalName) 159 switch (element.Name.LocalName)
166 { 160 {
167 case "PerfCounter": 161 case "PerfCounter":
168 this.ParsePerfCounterElement(intermediate, section, element, fileComponentId, fileId); 162 this.ParsePerfCounterElement(intermediate, section, element, fileComponentId, fileId);
169 break; 163 break;
170 case "PermissionEx": 164 case "PermissionEx":
171 this.ParsePermissionExElement(intermediate, section, element, fileId, fileComponentId, fileWin64, "File"); 165 this.ParsePermissionExElement(intermediate, section, element, fileId, fileComponentId, "File");
172 break; 166 break;
173 case "PerfCounterManifest": 167 case "PerfCounterManifest":
174 this.ParsePerfCounterManifestElement(intermediate, section, element, fileComponentId, fileId); 168 this.ParsePerfCounterManifestElement(intermediate, section, element, fileComponentId, fileId);
@@ -177,7 +171,7 @@ namespace WixToolset.Util
177 this.ParseEventManifestElement(intermediate, section, element, fileComponentId, fileId); 171 this.ParseEventManifestElement(intermediate, section, element, fileComponentId, fileId);
178 break; 172 break;
179 case "FormatFile": 173 case "FormatFile":
180 this.ParseFormatFileElement(intermediate, section, element, fileId, fileWin64); 174 this.ParseFormatFileElement(intermediate, section, element, fileId);
181 break; 175 break;
182 default: 176 default:
183 this.ParseHelper.UnexpectedElement(parentElement, element); 177 this.ParseHelper.UnexpectedElement(parentElement, element);
@@ -296,13 +290,10 @@ namespace WixToolset.Util
296 var registryId = context["RegistryId"]; 290 var registryId = context["RegistryId"];
297 var registryComponentId = context["ComponentId"]; 291 var registryComponentId = context["ComponentId"];
298 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
302 switch (element.Name.LocalName) 293 switch (element.Name.LocalName)
303 { 294 {
304 case "PermissionEx": 295 case "PermissionEx":
305 this.ParsePermissionExElement(intermediate, section, element, registryId, registryComponentId, registryWin64, "Registry"); 296 this.ParsePermissionExElement(intermediate, section, element, registryId, registryComponentId, "Registry");
306 break; 297 break;
307 default: 298 default:
308 this.ParseHelper.UnexpectedElement(parentElement, element); 299 this.ParseHelper.UnexpectedElement(parentElement, element);
@@ -314,13 +305,10 @@ namespace WixToolset.Util
314 var serviceInstallName = context["ServiceInstallName"]; 305 var serviceInstallName = context["ServiceInstallName"];
315 var serviceInstallComponentId = context["ServiceInstallComponentId"]; 306 var serviceInstallComponentId = context["ServiceInstallComponentId"];
316 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
320 switch (element.Name.LocalName) 308 switch (element.Name.LocalName)
321 { 309 {
322 case "PermissionEx": 310 case "PermissionEx":
323 this.ParsePermissionExElement(intermediate, section, element, serviceInstallId, serviceInstallComponentId, serviceInstallWin64, "ServiceInstall"); 311 this.ParsePermissionExElement(intermediate, section, element, serviceInstallId, serviceInstallComponentId, "ServiceInstall");
324 break; 312 break;
325 case "ServiceConfig": 313 case "ServiceConfig":
326 this.ParseServiceConfigElement(intermediate, section, element, serviceInstallComponentId, "ServiceInstall", serviceInstallName); 314 this.ParseServiceConfigElement(intermediate, section, element, serviceInstallComponentId, "ServiceInstall", serviceInstallName);
@@ -536,7 +524,6 @@ namespace WixToolset.Util
536 private void ParseComponentSearchRefElement(Intermediate intermediate, IntermediateSection section, XElement element) 524 private void ParseComponentSearchRefElement(Intermediate intermediate, IntermediateSection section, XElement element)
537 { 525 {
538 var sourceLineNumbers = this.ParseHelper.GetSourceLineNumbers(element); 526 var sourceLineNumbers = this.ParseHelper.GetSourceLineNumbers(element);
539 string refId = null;
540 527
541 foreach (var attrib in element.Attributes()) 528 foreach (var attrib in element.Attributes())
542 { 529 {
@@ -545,7 +532,7 @@ namespace WixToolset.Util
545 switch (attrib.Name.LocalName) 532 switch (attrib.Name.LocalName)
546 { 533 {
547 case "Id": 534 case "Id":
548 refId = this.ParseHelper.GetAttributeIdentifierValue(sourceLineNumbers, attrib); 535 var refId = this.ParseHelper.GetAttributeIdentifierValue(sourceLineNumbers, attrib);
549 this.ParseHelper.CreateSimpleReference(section, sourceLineNumbers, SymbolDefinitions.WixComponentSearch, refId); 536 this.ParseHelper.CreateSimpleReference(section, sourceLineNumbers, SymbolDefinitions.WixComponentSearch, refId);
550 break; 537 break;
551 default: 538 default:
@@ -2284,7 +2271,7 @@ namespace WixToolset.Util
2284 /// <param name="element">Element to parse.</param> 2271 /// <param name="element">Element to parse.</param>
2285 /// <param name="fileId">Identifier of referenced file.</param> 2272 /// <param name="fileId">Identifier of referenced file.</param>
2286 /// <param name="win64">Flag to determine whether the component is 64-bit.</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)
2288 { 2275 {
2289 var sourceLineNumbers = this.ParseHelper.GetSourceLineNumbers(element); 2276 var sourceLineNumbers = this.ParseHelper.GetSourceLineNumbers(element);
2290 string binaryId = null; 2277 string binaryId = null;
@@ -2415,7 +2402,6 @@ namespace WixToolset.Util
2415 ComponentRef = componentId, 2402 ComponentRef = componentId,
2416 }); 2403 });
2417 } 2404 }
2418
2419 } 2405 }
2420 2406
2421 this.ParseHelper.CreateCustomActionReference(sourceLineNumbers, section, "Wix4ConfigureEventManifestRegister", this.Context.Platform, CustomActionPlatforms.X86 | CustomActionPlatforms.X64 | CustomActionPlatforms.ARM64); 2407 this.ParseHelper.CreateCustomActionReference(sourceLineNumbers, section, "Wix4ConfigureEventManifestRegister", this.Context.Platform, CustomActionPlatforms.X86 | CustomActionPlatforms.X64 | CustomActionPlatforms.ARM64);
@@ -2435,7 +2421,7 @@ namespace WixToolset.Util
2435 /// <param name="componentId">Identifier of component, used to determine install state.</param> 2421 /// <param name="componentId">Identifier of component, used to determine install state.</param>
2436 /// <param name="win64">Flag to determine whether the component is 64-bit.</param> 2422 /// <param name="win64">Flag to determine whether the component is 64-bit.</param>
2437 /// <param name="tableName">Name of table that contains objectId.</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)
2439 { 2425 {
2440 var sourceLineNumbers = this.ParseHelper.GetSourceLineNumbers(element); 2426 var sourceLineNumbers = this.ParseHelper.GetSourceLineNumbers(element);
2441 var bits = new BitArray(32); 2427 var bits = new BitArray(32);
@@ -2623,7 +2609,7 @@ namespace WixToolset.Util
2623 2609
2624 if (null == id) 2610 if (null == id)
2625 { 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());
2627 } 2613 }
2628 2614
2629 this.ParseHelper.ParseForExtensionElements(this.Context.Extensions, intermediate, section, element); 2615 this.ParseHelper.ParseForExtensionElements(this.Context.Extensions, intermediate, section, element);
diff --git a/src/ext/Util/wixext/UtilConstants.cs b/src/ext/Util/wixext/UtilConstants.cs
index 28ff368f..93616e44 100644
--- a/src/ext/Util/wixext/UtilConstants.cs
+++ b/src/ext/Util/wixext/UtilConstants.cs
@@ -2,11 +2,32 @@
2 2
3namespace WixToolset.Util 3namespace WixToolset.Util
4{ 4{
5 using System.Xml.Linq;
6
5 /// <summary> 7 /// <summary>
6 /// Constants used by Utility Extension. 8 /// Constants used by Utility Extension.
7 /// </summary> 9 /// </summary>
8 internal static class UtilConstants 10 internal static class UtilConstants
9 { 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
10 internal static readonly string[] FilePermissions = { "Read", "Write", "Append", "ReadExtendedAttributes", "WriteExtendedAttributes", "Execute", null, "ReadAttributes", "WriteAttributes" }; 31 internal static readonly string[] FilePermissions = { "Read", "Write", "Append", "ReadExtendedAttributes", "WriteExtendedAttributes", "Execute", null, "ReadAttributes", "WriteAttributes" };
11 internal static readonly string[] FolderPermissions = { "Read", "CreateFile", "CreateChild", "ReadExtendedAttributes", "WriteExtendedAttributes", "Traverse", "DeleteChild", "ReadAttributes", "WriteAttributes" }; 32 internal static readonly string[] FolderPermissions = { "Read", "CreateFile", "CreateChild", "ReadExtendedAttributes", "WriteExtendedAttributes", "Traverse", "DeleteChild", "ReadAttributes", "WriteAttributes" };
12 internal static readonly string[] GenericPermissions = { "GenericAll", "GenericExecute", "GenericWrite", "GenericRead" }; 33 internal static readonly string[] GenericPermissions = { "GenericAll", "GenericExecute", "GenericWrite", "GenericRead" };
diff --git a/src/ext/Util/wixext/UtilDecompiler.cs b/src/ext/Util/wixext/UtilDecompiler.cs
index 9ef3390f..7d95fcef 100644
--- a/src/ext/Util/wixext/UtilDecompiler.cs
+++ b/src/ext/Util/wixext/UtilDecompiler.cs
@@ -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. 1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2 2
3namespace WixToolset.Extensions 3namespace WixToolset.Util
4{ 4{
5#if TODO_CONSIDER_DECOMPILER
6 using System; 5 using System;
7 using System.IO; 6 using System.IO;
8 using System.Text; 7 using System.Text;
9 using System.Collections; 8 using System.Collections;
10 using System.Diagnostics; 9 using System.Diagnostics;
11 using System.Globalization;
12 10
13 using Util = WixToolset.Extensions.Serialize.Util;
14 using WixToolset.Data; 11 using WixToolset.Data;
15 using WixToolset.Extensibility; 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 17
18 /// <summary> 18 /// <summary>
19 /// The decompiler for the WiX Toolset Utility Extension. 19 /// The decompiler for the WiX Toolset Utility Extension.
20 /// </summary> 20 /// </summary>
21 public sealed class UtilDecompiler : DecompilerExtension 21 internal sealed class UtilDecompiler : BaseWindowsInstallerDecompilerExtension
22 { 22 {
23 /// <summary> 23 public override IReadOnlyCollection<TableDefinition> TableDefinitions => UtilTableDefinitions.All;
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 }
40 24
41 /// <summary> 25 /// <summary>
42 /// Called at the beginning of the decompilation of a database. 26 /// Called at the beginning of the decompilation of a database.
43 /// </summary> 27 /// </summary>
44 /// <param name="tables">The collection of all tables.</param> 28 /// <param name="tables">The collection of all tables.</param>
45 public override void Initialize(TableIndexedCollection tables) 29 public override void PreDecompileTables(TableIndexedCollection tables)
46 { 30 {
47 this.CleanupSecureCustomProperties(tables); 31 this.CleanupSecureCustomProperties(tables);
48 this.CleanupInternetShortcutRemoveFileTables(tables); 32 this.CleanupInternetShortcutRemoveFileTables(tables);
@@ -61,24 +45,22 @@ namespace WixToolset.Extensions
61 /// <param name="tables">The collection of all tables.</param> 45 /// <param name="tables">The collection of all tables.</param>
62 private void CleanupSecureCustomProperties(TableIndexedCollection tables) 46 private void CleanupSecureCustomProperties(TableIndexedCollection tables)
63 { 47 {
64 Table propertyTable = tables["Property"]; 48 var propertyTable = tables["Property"];
65 49
66 if (null != propertyTable) 50 if (null != propertyTable)
67 { 51 {
68 foreach (Row row in propertyTable.Rows) 52 foreach (var row in propertyTable.Rows)
69 { 53 {
70 if ("SecureCustomProperties" == row[0].ToString()) 54 if ("SecureCustomProperties" == row[0].ToString())
71 { 55 {
72 StringBuilder remainingProperties = new StringBuilder(); 56 var remainingProperties = new StringBuilder();
73 string[] secureCustomProperties = row[1].ToString().Split(';'); 57 var secureCustomProperties = row[1].ToString().Split(';');
74 foreach (string property in secureCustomProperties) 58 foreach (var property in secureCustomProperties)
75 { 59 {
76 if (property.StartsWith("WIX_SUITE_", StringComparison.Ordinal) || property.StartsWith("WIX_DIR_", StringComparison.Ordinal) 60 if (property.StartsWith("WIX_SUITE_", StringComparison.Ordinal) || property.StartsWith("WIX_DIR_", StringComparison.Ordinal)
77 || property.StartsWith("WIX_ACCOUNT_", StringComparison.Ordinal)) 61 || property.StartsWith("WIX_ACCOUNT_", StringComparison.Ordinal))
78 { 62 {
79 Wix.PropertyRef propertyRef = new Wix.PropertyRef(); 63 this.DecompilerHelper.AddElementToRoot("PropertyRef", new XAttribute("Id", property));
80 propertyRef.Id = property;
81 this.Core.RootElement.AddChild(propertyRef);
82 } 64 }
83 else 65 else
84 { 66 {
@@ -104,21 +86,21 @@ namespace WixToolset.Extensions
104 private void CleanupInternetShortcutRemoveFileTables(TableIndexedCollection tables) 86 private void CleanupInternetShortcutRemoveFileTables(TableIndexedCollection tables)
105 { 87 {
106 // index the WixInternetShortcut table 88 // index the WixInternetShortcut table
107 Table wixInternetShortcutTable = tables["WixInternetShortcut"]; 89 var wixInternetShortcutTable = tables["WixInternetShortcut"];
108 Hashtable wixInternetShortcuts = new Hashtable(); 90 var wixInternetShortcuts = new Hashtable();
109 if (null != wixInternetShortcutTable) 91 if (null != wixInternetShortcutTable)
110 { 92 {
111 foreach (Row row in wixInternetShortcutTable.Rows) 93 foreach (var row in wixInternetShortcutTable.Rows)
112 { 94 {
113 wixInternetShortcuts.Add(row.GetPrimaryKey(DecompilerConstants.PrimaryKeyDelimiter), row); 95 wixInternetShortcuts.Add(row.GetPrimaryKey(), row);
114 } 96 }
115 } 97 }
116 98
117 // remove the RemoveFile rows with primary keys that match the WixInternetShortcut table's 99 // remove the RemoveFile rows with primary keys that match the WixInternetShortcut table's
118 Table removeFileTable = tables["RemoveFile"]; 100 var removeFileTable = tables["RemoveFile"];
119 if (null != removeFileTable) 101 if (null != removeFileTable)
120 { 102 {
121 for (int i = removeFileTable.Rows.Count - 1; 0 <= i; i--) 103 for (var i = removeFileTable.Rows.Count - 1; 0 <= i; i--)
122 { 104 {
123 if (null != wixInternetShortcuts[removeFileTable.Rows[i][0]]) 105 if (null != wixInternetShortcuts[removeFileTable.Rows[i][0]])
124 { 106 {
@@ -132,69 +114,86 @@ namespace WixToolset.Extensions
132 /// Decompiles an extension table. 114 /// Decompiles an extension table.
133 /// </summary> 115 /// </summary>
134 /// <param name="table">The table to decompile.</param> 116 /// <param name="table">The table to decompile.</param>
135 public override void DecompileTable(Table table) 117 public override bool TryDecompileTable(Table table)
136 { 118 {
137 switch (table.Name) 119 switch (table.Name)
138 { 120 {
139 case "WixCloseApplication": 121 case "WixCloseApplication":
122 case "Wix4CloseApplication":
140 this.DecompileWixCloseApplicationTable(table); 123 this.DecompileWixCloseApplicationTable(table);
141 break; 124 break;
142 case "WixRemoveFolderEx": 125 case "WixRemoveFolderEx":
126 case "Wix4RemoveFolderEx":
143 this.DecompileWixRemoveFolderExTable(table); 127 this.DecompileWixRemoveFolderExTable(table);
144 break; 128 break;
145 case "WixRestartResource": 129 case "WixRestartResource":
130 case "Wix4RestartResource":
146 this.DecompileWixRestartResourceTable(table); 131 this.DecompileWixRestartResourceTable(table);
147 break; 132 break;
148 case "FileShare": 133 case "FileShare":
134 case "Wix4FileShare":
149 this.DecompileFileShareTable(table); 135 this.DecompileFileShareTable(table);
150 break; 136 break;
151 case "FileSharePermissions": 137 case "FileSharePermissions":
138 case "Wix4FileSharePermissions":
152 this.DecompileFileSharePermissionsTable(table); 139 this.DecompileFileSharePermissionsTable(table);
153 break; 140 break;
154 case "WixInternetShortcut": 141 case "WixInternetShortcut":
142 case "Wix4InternetShortcut":
155 this.DecompileWixInternetShortcutTable(table); 143 this.DecompileWixInternetShortcutTable(table);
156 break; 144 break;
157 case "Group": 145 case "Group":
146 case "Wix4Group":
158 this.DecompileGroupTable(table); 147 this.DecompileGroupTable(table);
159 break; 148 break;
160 case "Perfmon": 149 case "Perfmon":
150 case "Wix4Perfmon":
161 this.DecompilePerfmonTable(table); 151 this.DecompilePerfmonTable(table);
162 break; 152 break;
163 case "PerfmonManifest": 153 case "PerfmonManifest":
154 case "Wix4PerfmonManifest":
164 this.DecompilePerfmonManifestTable(table); 155 this.DecompilePerfmonManifestTable(table);
165 break; 156 break;
166 case "EventManifest": 157 case "EventManifest":
158 case "Wix4EventManifest":
167 this.DecompileEventManifestTable(table); 159 this.DecompileEventManifestTable(table);
168 break; 160 break;
169 case "SecureObjects": 161 case "SecureObjects":
162 case "Wix4SecureObjects":
170 this.DecompileSecureObjectsTable(table); 163 this.DecompileSecureObjectsTable(table);
171 break; 164 break;
172 case "ServiceConfig": 165 case "ServiceConfig":
166 case "Wix4ServiceConfig":
173 this.DecompileServiceConfigTable(table); 167 this.DecompileServiceConfigTable(table);
174 break; 168 break;
175 case "User": 169 case "User":
170 case "Wix4User":
176 this.DecompileUserTable(table); 171 this.DecompileUserTable(table);
177 break; 172 break;
178 case "UserGroup": 173 case "UserGroup":
174 case "Wix4UserGroup":
179 this.DecompileUserGroupTable(table); 175 this.DecompileUserGroupTable(table);
180 break; 176 break;
181 case "XmlConfig": 177 case "XmlConfig":
178 case "Wix4XmlConfig":
182 this.DecompileXmlConfigTable(table); 179 this.DecompileXmlConfigTable(table);
183 break; 180 break;
184 case "XmlFile": 181 case "XmlFile":
182 case "Wix4XmlFile":
185 // XmlFile decompilation has been moved to FinalizeXmlFileTable function 183 // XmlFile decompilation has been moved to FinalizeXmlFileTable function
186 break; 184 break;
187 default: 185 default:
188 base.DecompileTable(table); 186 return false;
189 break;
190 } 187 }
188
189 return true;
191 } 190 }
192 191
193 /// <summary> 192 /// <summary>
194 /// Finalize decompilation. 193 /// Finalize decompilation.
195 /// </summary> 194 /// </summary>
196 /// <param name="tables">The collection of all tables.</param> 195 /// <param name="tables">The collection of all tables.</param>
197 public override void Finish(TableIndexedCollection tables) 196 public override void PostDecompileTables(TableIndexedCollection tables)
198 { 197 {
199 this.FinalizePerfmonTable(tables); 198 this.FinalizePerfmonTable(tables);
200 this.FinalizePerfmonManifestTable(tables); 199 this.FinalizePerfmonManifestTable(tables);
@@ -211,49 +210,21 @@ namespace WixToolset.Extensions
211 /// <param name="table">The table to decompile.</param> 210 /// <param name="table">The table to decompile.</param>
212 private void DecompileWixCloseApplicationTable(Table table) 211 private void DecompileWixCloseApplicationTable(Table table)
213 { 212 {
214 foreach (Row row in table.Rows) 213 foreach (var row in table.Rows)
215 { 214 {
216 Util.CloseApplication closeApplication = new Util.CloseApplication(); 215 var attribute = row.FieldAsNullableInteger(4) ?? 0x2;
217 216
218 closeApplication.Id = (string)row[0]; 217 this.DecompilerHelper.AddElementToRoot(UtilConstants.CloseApplicationName,
219 218 new XAttribute("Id", row.FieldAsString(0)),
220 closeApplication.Target = (string)row[1]; 219 new XAttribute("Target", row.FieldAsString(1)),
221 220 AttributeIfNotNull("Description", row, 2),
222 if (null != row[2]) 221 AttributeIfNotNull("Content", row, 3),
223 { 222 AttributeIfNotNull("CloseMessage", 0x1 == (attribute & 0x1)),
224 closeApplication.Description = (string)row[2]; 223 AttributeIfNotNull("RebootPrompt", 0x2 == (attribute & 0x2)),
225 } 224 AttributeIfNotNull("ElevatedCloseMessage", 0x4 == (attribute & 0x4)),
226 225 NumericAttributeIfNotNull("Sequence", row, 5),
227 if (null != row[3]) 226 NumericAttributeIfNotNull("Property", row, 6)
228 { 227 );
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);
257 } 228 }
258 } 229 }
259 230
@@ -263,43 +234,45 @@ namespace WixToolset.Extensions
263 /// <param name="table">The table to decompile.</param> 234 /// <param name="table">The table to decompile.</param>
264 private void DecompileWixRemoveFolderExTable(Table table) 235 private void DecompileWixRemoveFolderExTable(Table table)
265 { 236 {
266 foreach (Row row in table.Rows) 237 foreach (var row in table.Rows)
267 { 238 {
268 // Set the Id even if auto-generated previously. 239 var on = String.Empty;
269 Util.RemoveFolderEx removeFolder = new Util.RemoveFolderEx(); 240 var installMode = row.FieldAsInteger(3);
270 removeFolder.Id = (string)row[0]; 241 switch (installMode)
271 removeFolder.Property = (string)row[2];
272
273 int installMode = (int)row[3];
274 switch ((UtilCompiler.WixRemoveFolderExOn)installMode)
275 { 242 {
276 case UtilCompiler.WixRemoveFolderExOn.Install: 243 case (int)WixRemoveFolderExInstallMode.Install:
277 removeFolder.On = Util.RemoveFolderEx.OnType.install; 244 on = "install";
278 break; 245 break;
279 246
280 case UtilCompiler.WixRemoveFolderExOn.Uninstall: 247 case (int)WixRemoveFolderExInstallMode.Uninstall:
281 removeFolder.On = Util.RemoveFolderEx.OnType.uninstall; 248 on = "uninstall";
282 break; 249 break;
283 250
284 case UtilCompiler.WixRemoveFolderExOn.Both: 251 case (int)WixRemoveFolderExInstallMode.Both:
285 removeFolder.On = Util.RemoveFolderEx.OnType.both; 252 on = "both";
286 break; 253 break;
287 254
288 default: 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));
290 break; 257 break;
291 } 258 }
292 259
260 var removeFolder = new XElement(UtilConstants.RemoveFolderExName,
261 AttributeIfNotNull("Id", row, 0),
262 AttributeIfNotNull("Property", row, 2),
263 AttributeIfNotNull("On", on)
264 );
265
293 // Add to the appropriate Component or section element. 266 // Add to the appropriate Component or section element.
294 string componentId = (string)row[1]; 267 var componentId = row.FieldAsString(1);
295 Wix.Component component = (Wix.Component)this.Core.GetIndexedElement("Component", componentId); 268
296 if (null != component) 269 if (this.DecompilerHelper.TryGetIndexedElement("Component", componentId, out var component))
297 { 270 {
298 component.AddChild(removeFolder); 271 component.Add(removeFolder);
299 } 272 }
300 else 273 else
301 { 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"));
303 } 276 }
304 } 277 }
305 } 278 }
@@ -310,53 +283,51 @@ namespace WixToolset.Extensions
310 /// <param name="table">The table to decompile.</param> 283 /// <param name="table">The table to decompile.</param>
311 private void DecompileWixRestartResourceTable(Table table) 284 private void DecompileWixRestartResourceTable(Table table)
312 { 285 {
313 foreach (Row row in table.Rows) 286 foreach (var row in table.Rows)
314 { 287 {
315 // Set the Id even if auto-generated previously. 288 var restartResource = new XElement(UtilConstants.RestartResourceName,
316 Util.RestartResource restartResource = new Util.RestartResource(); 289 new XAttribute("Id", row.FieldAsString(0)));
317 restartResource.Id = (string)row[0];
318 290
319 // Determine the resource type and set accordingly. 291 // Determine the resource type and set accordingly.
320 string resource = (string)row[2]; 292 var resource = row.FieldAsString(2);
321 int attributes = (int)row[3]; 293 var attributes = row.FieldAsInteger(3);
322 UtilCompiler.WixRestartResourceAttributes type = (UtilCompiler.WixRestartResourceAttributes)(attributes & (int)UtilCompiler.WixRestartResourceAttributes.TypeMask); 294 var type = (WixRestartResourceAttributes)attributes;
323 295
324 switch (type) 296 switch (type)
325 { 297 {
326 case UtilCompiler.WixRestartResourceAttributes.Filename: 298 case WixRestartResourceAttributes.Filename:
327 restartResource.Path = resource; 299 restartResource.Add(new XAttribute("Path", resource));
328 break; 300 break;
329 301
330 case UtilCompiler.WixRestartResourceAttributes.ProcessName: 302 case WixRestartResourceAttributes.ProcessName:
331 restartResource.ProcessName = resource; 303 restartResource.Add(new XAttribute("ProcessName", resource));
332 break; 304 break;
333 305
334 case UtilCompiler.WixRestartResourceAttributes.ServiceName: 306 case WixRestartResourceAttributes.ServiceName:
335 restartResource.ServiceName = resource; 307 restartResource.Add(new XAttribute("ServiceName", resource));
336 break; 308 break;
337 309
338 default: 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));
340 break; 312 break;
341 } 313 }
342 314
343 // Add to the appropriate Component or section element. 315 // Add to the appropriate Component or section element.
344 string componentId = (string)row[1]; 316 var componentId = row.FieldAsString(1);
345 if (!String.IsNullOrEmpty(componentId)) 317 if (!String.IsNullOrEmpty(componentId))
346 { 318 {
347 Wix.Component component = (Wix.Component)this.Core.GetIndexedElement("Component", componentId); 319 if (this.DecompilerHelper.TryGetIndexedElement("Component", componentId, out var component))
348 if (null != component)
349 { 320 {
350 component.AddChild(restartResource); 321 component.Add(restartResource);
351 } 322 }
352 else 323 else
353 { 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"));
355 } 326 }
356 } 327 }
357 else 328 else
358 { 329 {
359 this.Core.RootElement.AddChild(restartResource); 330 this.DecompilerHelper.AddElementToRoot(restartResource);
360 } 331 }
361 } 332 }
362 } 333 }
@@ -367,33 +338,28 @@ namespace WixToolset.Extensions
367 /// <param name="table">The table to decompile.</param> 338 /// <param name="table">The table to decompile.</param>
368 private void DecompileFileShareTable(Table table) 339 private void DecompileFileShareTable(Table table)
369 { 340 {
370 foreach (Row row in table.Rows) 341 foreach (var row in table.Rows)
371 { 342 {
372 Util.FileShare fileShare = new Util.FileShare(); 343 var fileShare = new XElement(UtilConstants.FileShareName,
373 344 new XAttribute("Id", row.FieldAsString(0)),
374 fileShare.Id = (string)row[0]; 345 new XAttribute("Name", row.FieldAsString(1)),
375 346 AttributeIfNotNull("Description", row, 3)
376 fileShare.Name = (string)row[1]; 347 );
377
378 if (null != row[3])
379 {
380 fileShare.Description = (string)row[3];
381 }
382 348
383 // the Directory_ column is set by the parent Component 349 // the Directory_ column is set by the parent Component
384 350
385 // the User_ and Permissions columns are deprecated 351 // the User_ and Permissions columns are deprecated
386 352
387 Wix.Component component = (Wix.Component)this.Core.GetIndexedElement("Component", (string)row[2]); 353 if (this.DecompilerHelper.TryGetIndexedElement("Component", row.FieldAsString(2), out var component))
388 if (null != component)
389 { 354 {
390 component.AddChild(fileShare); 355 component.Add(fileShare);
391 } 356 }
392 else 357 else
393 { 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"));
395 } 360 }
396 this.Core.IndexElement(row, fileShare); 361
362 this.DecompilerHelper.IndexElement(row, fileShare);
397 } 363 }
398 } 364 }
399 365
@@ -403,111 +369,21 @@ namespace WixToolset.Extensions
403 /// <param name="table">The table to decompile.</param> 369 /// <param name="table">The table to decompile.</param>
404 private void DecompileFileSharePermissionsTable(Table table) 370 private void DecompileFileSharePermissionsTable(Table table)
405 { 371 {
406 foreach (Row row in table.Rows) 372 foreach (var row in table.Rows)
407 { 373 {
408 Util.FileSharePermission fileSharePermission = new Util.FileSharePermission(); 374 var fileSharePermission = new XElement(UtilConstants.FileSharePermissionName,
409 375 new XAttribute("User", row.FieldAsString(1)));
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;
419 376
420 if (16 > i && specialPermissions.Length > i) 377 this.AddPermissionAttributes(fileSharePermission, row, 2, UtilConstants.FolderPermissions);
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 }
502 378
503 Util.FileShare fileShare = (Util.FileShare)this.Core.GetIndexedElement("FileShare", (string)row[0]); 379 if (this.DecompilerHelper.TryGetIndexedElement("Wix4FileShare", row.FieldAsString(0), out var fileShare) ||
504 if (null != fileShare) 380 this.DecompilerHelper.TryGetIndexedElement("FileShare", row.FieldAsString(0), out fileShare))
505 { 381 {
506 fileShare.AddChild(fileSharePermission); 382 fileShare.Add(fileSharePermission);
507 } 383 }
508 else 384 else
509 { 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"));
511 } 387 }
512 } 388 }
513 } 389 }
@@ -518,25 +394,18 @@ namespace WixToolset.Extensions
518 /// <param name="table">The table to decompile.</param> 394 /// <param name="table">The table to decompile.</param>
519 private void DecompileGroupTable(Table table) 395 private void DecompileGroupTable(Table table)
520 { 396 {
521 foreach (Row row in table.Rows) 397 foreach (var row in table.Rows)
522 { 398 {
523 Util.Group group = new Util.Group();
524
525 group.Id = (string)row[0];
526
527 if (null != row[1]) 399 if (null != row[1])
528 { 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]));
530 } 402 }
531 403
532 group.Name = (string)row[2]; 404 this.DecompilerHelper.AddElementToRoot(UtilConstants.GroupName,
533 405 new XAttribute("Id", row.FieldAsString(0)),
534 if (null != row[3]) 406 new XAttribute("Name", row.FieldAsString(1)),
535 { 407 AttributeIfNotNull("Domain", row, 3)
536 group.Domain = (string)row[3]; 408 );
537 }
538
539 this.Core.RootElement.AddChild(group);
540 } 409 }
541 } 410 }
542 411
@@ -546,39 +415,41 @@ namespace WixToolset.Extensions
546 /// <param name="table">The table to decompile.</param> 415 /// <param name="table">The table to decompile.</param>
547 private void DecompileWixInternetShortcutTable(Table table) 416 private void DecompileWixInternetShortcutTable(Table table)
548 { 417 {
549 foreach (Row row in table.Rows) 418 foreach (var row in table.Rows)
550 { 419 {
551 Util.InternetShortcut internetShortcut = new Util.InternetShortcut(); 420 var type = String.Empty;
552 internetShortcut.Id = (string)row[0]; 421 var shortcutType = (UtilCompiler.InternetShortcutType)row.FieldAsInteger(5);
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];
561 switch (shortcutType) 422 switch (shortcutType)
562 { 423 {
563 case UtilCompiler.InternetShortcutType.Link: 424 case UtilCompiler.InternetShortcutType.Link:
564 internetShortcut.Type = Util.InternetShortcut.TypeType.link; 425 type = "link";
565 break; 426 break;
566 case UtilCompiler.InternetShortcutType.Url: 427 case UtilCompiler.InternetShortcutType.Url:
567 internetShortcut.Type = Util.InternetShortcut.TypeType.url; 428 type = "url";
568 break; 429 break;
569 } 430 }
570 431
571 Wix.Component component = (Wix.Component)this.Core.GetIndexedElement("Component", (string)row[1]); 432 var internetShortcut = new XElement(UtilConstants.InternetShortcutName,
572 if (null != component) 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))
573 { 444 {
574 component.AddChild(internetShortcut); 445 component.Add(internetShortcut);
575 } 446 }
576 else 447 else
577 { 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"));
579 } 450 }
580 451
581 this.Core.IndexElement(row, internetShortcut); 452 this.DecompilerHelper.IndexElement(row, internetShortcut);
582 } 453 }
583 } 454 }
584 455
@@ -588,54 +459,48 @@ namespace WixToolset.Extensions
588 /// <param name="table">The table to decompile.</param> 459 /// <param name="table">The table to decompile.</param>
589 private void DecompilePerfmonTable(Table table) 460 private void DecompilePerfmonTable(Table table)
590 { 461 {
591 foreach (Row row in table.Rows) 462 foreach (var row in table.Rows)
592 { 463 {
593 Util.PerfCounter perfCounter = new Util.PerfCounter(); 464 this.DecompilerHelper.IndexElement(row, new XElement(UtilConstants.PerfCounterName, new XAttribute("Name", row.FieldAsString(2))));
594
595 perfCounter.Name = (string)row[2];
596
597 this.Core.IndexElement(row, perfCounter);
598 } 465 }
599 } 466 }
600 467
601 /// <summary> 468 /// <summary>
602 /// Decompile the PerfmonManifest table. 469 /// Decompile the PerfmonManifest table.
603 /// </summary> 470 /// </summary>
604 /// <param name="table">The table to decompile.</param> 471 /// <param name="table">The table to decompile.</param>
605 private void DecompilePerfmonManifestTable(Table table) 472 private void DecompilePerfmonManifestTable(Table table)
606 { 473 {
607 foreach (Row row in table.Rows) 474 foreach (var row in table.Rows)
608 { 475 {
609 Util.PerfCounterManifest perfCounterManifest = new Util.PerfCounterManifest(); 476 this.DecompilerHelper.IndexElement(row, new XElement(UtilConstants.PerfCounterManifestName, new XAttribute("ResourceFileDirectory", row.FieldAsString(2))));
610
611 perfCounterManifest.ResourceFileDirectory = (string)row[2];
612
613 this.Core.IndexElement(row, perfCounterManifest);
614 } 477 }
615 } 478 }
616 479
617 /// <summary> 480 /// <summary>
618 /// Decompile the EventManifest table. 481 /// Decompile the EventManifest table.
619 /// </summary> 482 /// </summary>
620 /// <param name="table">The table to decompile.</param> 483 /// <param name="table">The table to decompile.</param>
621 private void DecompileEventManifestTable(Table table) 484 private void DecompileEventManifestTable(Table table)
622 { 485 {
623 foreach (Row row in table.Rows) 486 foreach (var row in table.Rows)
624 { 487 {
625 Util.EventManifest eventManifest = new Util.EventManifest(); 488 this.DecompilerHelper.IndexElement(row, new XElement(UtilConstants.EventManifestName));
626 this.Core.IndexElement(row, eventManifest);
627 } 489 }
628 } 490 }
629 491
630 /// <summary> 492 /// <summary>
631 /// Decompile the SecureObjects table. 493 /// Decompile the SecureObjects table.
632 /// </summary> 494 /// </summary>
633 /// <param name="table">The table to decompile.</param> 495 /// <param name="table">The table to decompile.</param>
634 private void DecompileSecureObjectsTable(Table table) 496 private void DecompileSecureObjectsTable(Table table)
635 { 497 {
636 foreach (Row row in table.Rows) 498 foreach (var row in table.Rows)
637 { 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 );
639 504
640 string[] specialPermissions; 505 string[] specialPermissions;
641 switch ((string)row[1]) 506 switch ((string)row[1])
@@ -653,155 +518,13 @@ namespace WixToolset.Extensions
653 specialPermissions = UtilConstants.ServicePermissions; 518 specialPermissions = UtilConstants.ServicePermissions;
654 break; 519 break;
655 default: 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]));
657 return; 522 return;
658 } 523 }
659 524
660 int permissionBits = (int)row[4]; 525 this.AddPermissionAttributes(permissionEx, row, 4, specialPermissions);
661 for (int i = 0; i < 32; i++)
662 {
663 if (0 != ((permissionBits >> i) & 1))
664 {
665 string name = null;
666 526
667 if (16 > i && specialPermissions.Length > i) 527 this.DecompilerHelper.IndexElement(row, permissionEx);
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);
805 } 528 }
806 } 529 }
807 530
@@ -811,90 +534,20 @@ namespace WixToolset.Extensions
811 /// <param name="table">The table to decompile.</param> 534 /// <param name="table">The table to decompile.</param>
812 private void DecompileServiceConfigTable(Table table) 535 private void DecompileServiceConfigTable(Table table)
813 { 536 {
814 foreach (Row row in table.Rows) 537 foreach (var row in table.Rows)
815 { 538 {
816 Util.ServiceConfig serviceConfig = new Util.ServiceConfig(); 539 var serviceConfig = new XElement(UtilConstants.ServiceConfigName,
817 540 new XAttribute("ServiceName", row.FieldAsString(0)),
818 serviceConfig.ServiceName = (string)row[0]; 541 AttributeIfNotNull("FirstFailureActionType", row, 3),
819 542 AttributeIfNotNull("SecondFailureActionType", row, 4),
820 switch ((string)row[3]) 543 AttributeIfNotNull("ThirdFailureActionType", row, 5),
821 { 544 NumericAttributeIfNotNull("ResetPeriodInDays", row, 6),
822 case "none": 545 NumericAttributeIfNotNull("RestartServiceDelayInSeconds", row, 7),
823 serviceConfig.FirstFailureActionType = Util.ServiceConfig.FirstFailureActionTypeType.none; 546 AttributeIfNotNull("ProgramCommandLine", row, 8),
824 break; 547 AttributeIfNotNull("RebootMessage", row, 9)
825 case "reboot": 548 );
826 serviceConfig.FirstFailureActionType = Util.ServiceConfig.FirstFailureActionTypeType.reboot; 549
827 break; 550 this.DecompilerHelper.IndexElement(row, serviceConfig);
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);
898 } 551 }
899 } 552 }
900 553
@@ -904,97 +557,58 @@ namespace WixToolset.Extensions
904 /// <param name="table">The table to decompile.</param> 557 /// <param name="table">The table to decompile.</param>
905 private void DecompileUserTable(Table table) 558 private void DecompileUserTable(Table table)
906 { 559 {
907 foreach (Row row in table.Rows) 560 foreach (var row in table.Rows)
908 { 561 {
909 Util.User user = new Util.User(); 562 var attributes = row.FieldAsNullableInteger(5) ?? 0;
910 563
911 user.Id = (string)row[0]; 564 var user = new XElement(UtilConstants.UserName,
912 565 new XAttribute("Id", row.FieldAsString(0)),
913 user.Name = (string)row[2]; 566 new XAttribute("Name", row.FieldAsString(2)),
914 567 AttributeIfNotNull("Domain", row, 3),
915 if (null != 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))
916 { 580 {
917 user.Domain = (string)row[3]; 581 user.Add(new XAttribute("RemoveOnUninstall", "no"));
918 } 582 }
919 583
920 if (null != row[4]) 584 if (UtilCompiler.UserDontCreateUser == (attributes & UtilCompiler.UserDontCreateUser))
921 { 585 {
922 user.Password = (string)row[4]; 586 user.Add(new XAttribute("CreateUser", "no"));
923 } 587 }
924 588
925 if (null != row[5]) 589 if (UtilCompiler.UserNonVital == (attributes & UtilCompiler.UserNonVital))
926 { 590 {
927 int attributes = (int)row[5]; 591 user.Add(new XAttribute("Vital", "no"));
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 }
978 } 592 }
979 593
980 if (null != row[1]) 594 var componentId = row.FieldAsString(1);
595 if (!String.IsNullOrEmpty(componentId))
981 { 596 {
982 Wix.Component component = (Wix.Component)this.Core.GetIndexedElement("Component", (string)row[1]); 597 if (this.DecompilerHelper.TryGetIndexedElement("Component", componentId, out var component))
983
984 if (null != component)
985 { 598 {
986 component.AddChild(user); 599 component.Add(user);
987 } 600 }
988 else 601 else
989 { 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"));
991 } 604 }
992 } 605 }
993 else 606 else
994 { 607 {
995 this.Core.RootElement.AddChild(user); 608 this.DecompilerHelper.AddElementToRoot(user);
996 } 609 }
997 this.Core.IndexElement(row, user); 610
611 this.DecompilerHelper.IndexElement(row, user);
998 } 612 }
999 } 613 }
1000 614
@@ -1004,21 +618,16 @@ namespace WixToolset.Extensions
1004 /// <param name="table">The table to decompile.</param> 618 /// <param name="table">The table to decompile.</param>
1005 private void DecompileUserGroupTable(Table table) 619 private void DecompileUserGroupTable(Table table)
1006 { 620 {
1007 foreach (Row row in table.Rows) 621 foreach (var row in table.Rows)
1008 { 622 {
1009 Util.User user = (Util.User)this.Core.GetIndexedElement("User", (string)row[0]); 623 var userId = row.FieldAsString(0);
1010 624 if (this.DecompilerHelper.TryGetIndexedElement("User", userId, out var user))
1011 if (null != user)
1012 { 625 {
1013 Util.GroupRef groupRef = new Util.GroupRef(); 626 user.Add(new XElement(UtilConstants.GroupRefName, new XAttribute("Id", row.FieldAsString(1))));
1014
1015 groupRef.Id = (string)row[1];
1016
1017 user.AddChild(groupRef);
1018 } 627 }
1019 else 628 else
1020 { 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"));
1022 } 631 }
1023 } 632 }
1024 } 633 }
@@ -1029,75 +638,58 @@ namespace WixToolset.Extensions
1029 /// <param name="table">The table to decompile.</param> 638 /// <param name="table">The table to decompile.</param>
1030 private void DecompileXmlConfigTable(Table table) 639 private void DecompileXmlConfigTable(Table table)
1031 { 640 {
1032 foreach (Row row in table.Rows) 641 foreach (var row in table.Rows)
1033 { 642 {
1034 Util.XmlConfig xmlConfig = new Util.XmlConfig(); 643 var flags = row.FieldAsNullableInteger(6) ?? 0;
1035 644 string node = null;
1036 xmlConfig.Id = (string)row[0]; 645 string action = null;
1037 646 string on = null;
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];
1058 647
1059 if (0x1 == (flags & 0x1)) 648 if (0x1 == (flags & 0x1))
1060 { 649 {
1061 xmlConfig.Node = Util.XmlConfig.NodeType.element; 650 node = "element";
1062 } 651 }
1063 else if (0x2 == (flags & 0x2)) 652 else if (0x2 == (flags & 0x2))
1064 { 653 {
1065 xmlConfig.Node = Util.XmlConfig.NodeType.value; 654 node = "value";
1066 } 655 }
1067 else if (0x4 == (flags & 0x4)) 656 else if (0x4 == (flags & 0x4))
1068 { 657 {
1069 xmlConfig.Node = Util.XmlConfig.NodeType.document; 658 node = "document";
1070 } 659 }
1071 660
1072 if (0x10 == (flags & 0x10)) 661 if (0x10 == (flags & 0x10))
1073 { 662 {
1074 xmlConfig.Action = Util.XmlConfig.ActionType.create; 663 action = "create";
1075 } 664 }
1076 else if (0x20 == (flags & 0x20)) 665 else if (0x20 == (flags & 0x20))
1077 { 666 {
1078 xmlConfig.Action = Util.XmlConfig.ActionType.delete; 667 action = "delete";
1079 } 668 }
1080 669
1081 if (0x100 == (flags & 0x100)) 670 if (0x100 == (flags & 0x100))
1082 { 671 {
1083 xmlConfig.On = Util.XmlConfig.OnType.install; 672 on = "install";
1084 } 673 }
1085 else if (0x200 == (flags & 0x200)) 674 else if (0x200 == (flags & 0x200))
1086 { 675 {
1087 xmlConfig.On = Util.XmlConfig.OnType.uninstall; 676 on = "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];
1098 } 677 }
1099 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);
1101 } 693 }
1102 } 694 }
1103 695
@@ -1114,34 +706,30 @@ namespace WixToolset.Extensions
1114 /// </remarks> 706 /// </remarks>
1115 private void FinalizePerfmonTable(TableIndexedCollection tables) 707 private void FinalizePerfmonTable(TableIndexedCollection tables)
1116 { 708 {
1117 Table perfmonTable = tables["Perfmon"]; 709 if (tables.TryGetTable("Perfmon", out var perfmonTable))
1118
1119 if (null != perfmonTable)
1120 { 710 {
1121 foreach (Row row in perfmonTable.Rows) 711 foreach (var row in perfmonTable.Rows)
1122 { 712 {
1123 string formattedFile = (string)row[1]; 713 var formattedFile = row.FieldAsString(1);
1124 Util.PerfCounter perfCounter = (Util.PerfCounter)this.Core.GetIndexedElement(row);
1125 714
1126 // try to "de-format" the File column's value to determine the proper parent File element 715 // try to "de-format" the File column's value to determine the proper parent File element
1127 if ((formattedFile.StartsWith("[#", StringComparison.Ordinal) || formattedFile.StartsWith("[!", StringComparison.Ordinal)) 716 if ((formattedFile.StartsWith("[#", StringComparison.Ordinal) || formattedFile.StartsWith("[!", StringComparison.Ordinal))
1128 && formattedFile.EndsWith("]", StringComparison.Ordinal)) 717 && formattedFile.EndsWith("]", StringComparison.Ordinal))
1129 { 718 {
1130 string fileId = formattedFile.Substring(2, formattedFile.Length - 3); 719 var fileId = formattedFile.Substring(2, formattedFile.Length - 3);
1131 720 if (this.DecompilerHelper.TryGetIndexedElement("File", fileId, out var file))
1132 Wix.File file = (Wix.File)this.Core.GetIndexedElement("File", fileId);
1133 if (null != file)
1134 { 721 {
1135 file.AddChild(perfCounter); 722 var perfCounter = this.DecompilerHelper.GetIndexedElement(row);
723 file.Add(perfCounter);
1136 } 724 }
1137 else 725 else
1138 { 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"));
1140 } 728 }
1141 } 729 }
1142 else 730 else
1143 { 731 {
1144 this.Core.OnMessage(UtilErrors.IllegalFileValueInPerfmonOrManifest(formattedFile, "Perfmon")); 732 this.Messaging.Write(UtilErrors.IllegalFileValueInPerfmonOrManifest(formattedFile, "Perfmon"));
1145 } 733 }
1146 } 734 }
1147 } 735 }
@@ -1153,34 +741,33 @@ namespace WixToolset.Extensions
1153 /// <param name="tables">The collection of all tables.</param> 741 /// <param name="tables">The collection of all tables.</param>
1154 private void FinalizePerfmonManifestTable(TableIndexedCollection tables) 742 private void FinalizePerfmonManifestTable(TableIndexedCollection tables)
1155 { 743 {
1156 Table perfmonManifestTable = tables["PerfmonManifest"]; 744 if (tables.TryGetTable("PerfmonManifest", out var perfmonManifestTable))
1157
1158 if (null != perfmonManifestTable)
1159 { 745 {
1160 foreach (Row row in perfmonManifestTable.Rows) 746 foreach (var row in perfmonManifestTable.Rows)
1161 { 747 {
1162 string formattedFile = (string)row[1]; 748 var formattedFile = row.FieldAsString(1);
1163 Util.PerfCounterManifest perfCounterManifest = (Util.PerfCounterManifest)this.Core.GetIndexedElement(row);
1164 749
1165 // try to "de-format" the File column's value to determine the proper parent File element 750 // try to "de-format" the File column's value to determine the proper parent File element
1166 if ((formattedFile.StartsWith("[#", StringComparison.Ordinal) || formattedFile.StartsWith("[!", StringComparison.Ordinal)) 751 if ((formattedFile.StartsWith("[#", StringComparison.Ordinal) || formattedFile.StartsWith("[!", StringComparison.Ordinal))
1167 && formattedFile.EndsWith("]", StringComparison.Ordinal)) 752 && formattedFile.EndsWith("]", StringComparison.Ordinal))
1168 { 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);
1170 756
1171 Wix.File file = (Wix.File)this.Core.GetIndexedElement("File", fileId); 757 if (this.DecompilerHelper.TryGetIndexedElement("File", fileId, out var file))
1172 if (null != file)
1173 { 758 {
1174 file.AddChild(perfCounterManifest); 759 file.Add(perfCounterManifest);
1175 } 760 }
1176 else 761 else
1177 { 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"));
1179 } 766 }
1180 } 767 }
1181 else 768 else
1182 { 769 {
1183 this.Core.OnMessage(UtilErrors.IllegalFileValueInPerfmonOrManifest(formattedFile, "PerfmonManifest")); 770 this.Messaging.Write(UtilErrors.IllegalFileValueInPerfmonOrManifest(formattedFile, "PerfmonManifest"));
1184 } 771 }
1185 } 772 }
1186 } 773 }
@@ -1196,64 +783,61 @@ namespace WixToolset.Extensions
1196 /// </remarks> 783 /// </remarks>
1197 private void FinalizeSecureObjectsTable(TableIndexedCollection tables) 784 private void FinalizeSecureObjectsTable(TableIndexedCollection tables)
1198 { 785 {
1199 Table createFolderTable = tables["CreateFolder"]; 786 var createFolderElementsByDirectoryId = new Dictionary<string, List<XElement>>();
1200 Table secureObjectsTable = tables["SecureObjects"];
1201
1202 Hashtable createFolders = new Hashtable();
1203 787
1204 // index the CreateFolder table because the foreign key to this table from the 788 // index the CreateFolder table because the foreign key to this table from the
1205 // LockPermissions table is only part of the primary key of this table 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))
1207 { 791 {
1208 foreach (Row row in createFolderTable.Rows) 792 foreach (var row in createFolderTable.Rows)
1209 { 793 {
1210 Wix.CreateFolder createFolder = (Wix.CreateFolder)this.Core.GetIndexedElement(row); 794 var directoryId = row.FieldAsString(0);
1211 string directoryId = (string)row[0];
1212 795
1213 if (!createFolders.Contains(directoryId)) 796 if (!createFolderElementsByDirectoryId.TryGetValue(directoryId, out var createFolderElements))
1214 { 797 {
1215 createFolders.Add(directoryId, new ArrayList()); 798 createFolderElements = new List<XElement>();
799 createFolderElementsByDirectoryId.Add(directoryId, createFolderElements);
1216 } 800 }
1217 ((ArrayList)createFolders[directoryId]).Add(createFolder); 801
802 var createFolder = this.DecompilerHelper.GetIndexedElement(row);
803 createFolderElements.Add(createFolder);
1218 } 804 }
1219 } 805 }
1220 806
1221 if (null != secureObjectsTable) 807 if (tables.TryGetTable("SecureObjects", out var secureObjectsTable))
1222 { 808 {
1223 foreach (Row row in secureObjectsTable.Rows) 809 foreach (var row in secureObjectsTable.Rows)
1224 { 810 {
1225 string id = (string)row[0]; 811 var id = row.FieldAsString(0);
1226 string table = (string)row[1]; 812 var table = row.FieldAsString(1);
1227 813
1228 Util.PermissionEx permissionEx = (Util.PermissionEx)this.Core.GetIndexedElement(row); 814 var permissionEx = this.DecompilerHelper.GetIndexedElement(row);
1229 815
1230 if ("CreateFolder" == table) 816 if (table == "CreateFolder")
1231 { 817 {
1232 ArrayList createFolderElements = (ArrayList)createFolders[id]; 818 if (createFolderElementsByDirectoryId.TryGetValue(id, out var createFolderElements))
1233
1234 if (null != createFolderElements)
1235 { 819 {
1236 foreach (Wix.CreateFolder createFolder in createFolderElements) 820 foreach (var createFolder in createFolderElements)
1237 { 821 {
1238 createFolder.AddChild(permissionEx); 822 createFolder.Add(permissionEx);
1239 } 823 }
1240 } 824 }
1241 else 825 else
1242 { 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));
1244 } 828 }
1245 } 829 }
1246 else 830 else
1247 { 831 {
1248 Wix.IParentElement parentElement = (Wix.IParentElement)this.Core.GetIndexedElement(table, id); 832 var parentElement = this.DecompilerHelper.GetIndexedElement(table, id);
1249 833
1250 if (null != parentElement) 834 if (parentElement != null)
1251 { 835 {
1252 parentElement.AddChild(permissionEx); 836 parentElement.Add(permissionEx);
1253 } 837 }
1254 else 838 else
1255 { 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));
1257 } 841 }
1258 } 842 }
1259 } 843 }
@@ -1270,10 +854,8 @@ namespace WixToolset.Extensions
1270 /// </remarks> 854 /// </remarks>
1271 private void FinalizeServiceConfigTable(TableIndexedCollection tables) 855 private void FinalizeServiceConfigTable(TableIndexedCollection tables)
1272 { 856 {
1273 Table serviceConfigTable = tables["ServiceConfig"]; 857 //var serviceInstalls = new Hashtable();
1274 Table serviceInstallTable = tables["ServiceInstall"]; 858 var serviceInstallElementsByName = new Dictionary<string, List<XElement>>();
1275
1276 Hashtable serviceInstalls = new Hashtable();
1277 859
1278 // index the ServiceInstall table because the foreign key used by the ServiceConfig 860 // index the ServiceInstall table because the foreign key used by the ServiceConfig
1279 // table is actually the ServiceInstall.Name, not the ServiceInstall.ServiceInstall 861 // table is actually the ServiceInstall.Name, not the ServiceInstall.ServiceInstall
@@ -1281,55 +863,54 @@ namespace WixToolset.Extensions
1281 // decompiler must assume there could be multiple matches and add the ServiceConfig to each 863 // decompiler must assume there could be multiple matches and add the ServiceConfig to each
1282 // TODO: the Component column information should be taken into acount to accurately identify 864 // TODO: the Component column information should be taken into acount to accurately identify
1283 // the correct column to use 865 // the correct column to use
1284 if (null != serviceInstallTable) 866 if (tables.TryGetTable("ServiceInstall", out var serviceInstallTable))
1285 { 867 {
1286 foreach (Row row in serviceInstallTable.Rows) 868 foreach (var row in serviceInstallTable.Rows)
1287 { 869 {
1288 string name = (string)row[1]; 870 var name = row.FieldAsString(1);
1289 Wix.ServiceInstall serviceInstall = (Wix.ServiceInstall)this.Core.GetIndexedElement(row);
1290 871
1291 if (!serviceInstalls.Contains(name)) 872 if (!serviceInstallElementsByName.TryGetValue(name, out var serviceInstallElements))
1292 { 873 {
1293 serviceInstalls.Add(name, new ArrayList()); 874 serviceInstallElements = new List<XElement>();
875 serviceInstallElementsByName.Add(name, serviceInstallElements);
1294 } 876 }
1295 877
1296 ((ArrayList)serviceInstalls[name]).Add(serviceInstall); 878 var serviceInstall = this.DecompilerHelper.GetIndexedElement(row);
879 serviceInstallElements.Add(serviceInstall);
1297 } 880 }
1298 } 881 }
1299 882
1300 if (null != serviceConfigTable) 883 if (tables.TryGetTable("ServiceConfig", out var serviceConfigTable))
1301 { 884 {
1302 foreach (Row row in serviceConfigTable.Rows) 885 foreach (var row in serviceConfigTable.Rows)
1303 { 886 {
1304 Util.ServiceConfig serviceConfig = (Util.ServiceConfig)this.Core.GetIndexedElement(row); 887 var serviceConfig = this.DecompilerHelper.GetIndexedElement(row);
1305 888
1306 if (0 == (int)row[2]) 889 if (row.FieldAsInteger(2) == 0)
1307 { 890 {
1308 Wix.Component component = (Wix.Component)this.Core.GetIndexedElement("Component", (string)row[1]); 891 var componentId = row.FieldAsString(1);
1309 892 if (this.DecompilerHelper.TryGetIndexedElement("Component", componentId, out var component))
1310 if (null != component)
1311 { 893 {
1312 component.AddChild(serviceConfig); 894 component.Add(serviceConfig);
1313 } 895 }
1314 else 896 else
1315 { 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"));
1317 } 899 }
1318 } 900 }
1319 else 901 else
1320 { 902 {
1321 ArrayList serviceInstallElements = (ArrayList)serviceInstalls[row[0]]; 903 var name = row.FieldAsString(0);
1322 904 if (serviceInstallElementsByName.TryGetValue(name, out var serviceInstallElements))
1323 if (null != serviceInstallElements)
1324 { 905 {
1325 foreach (Wix.ServiceInstall serviceInstall in serviceInstallElements) 906 foreach (var serviceInstall in serviceInstallElements)
1326 { 907 {
1327 serviceInstall.AddChild(serviceConfig); 908 serviceInstall.Add(serviceConfig);
1328 } 909 }
1329 } 910 }
1330 else 911 else
1331 { 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"));
1333 } 914 }
1334 } 915 }
1335 } 916 }
@@ -1342,38 +923,34 @@ namespace WixToolset.Extensions
1342 /// <param name="tables">Collection of all tables.</param> 923 /// <param name="tables">Collection of all tables.</param>
1343 private void FinalizeXmlConfigTable(TableIndexedCollection tables) 924 private void FinalizeXmlConfigTable(TableIndexedCollection tables)
1344 { 925 {
1345 Table xmlConfigTable = tables["XmlConfig"]; 926 if (tables.TryGetTable("XmlConfig", out var xmlConfigTable))
1346
1347 if (null != xmlConfigTable)
1348 { 927 {
1349 foreach (Row row in xmlConfigTable.Rows) 928 foreach (var row in xmlConfigTable.Rows)
1350 { 929 {
1351 Util.XmlConfig xmlConfig = (Util.XmlConfig)this.Core.GetIndexedElement(row); 930 var xmlConfig = this.DecompilerHelper.GetIndexedElement(row);
1352 931
1353 if (null == row[6] || 0 == (int)row[6]) 932 if (null == row[6] || 0 == (int)row[6])
1354 { 933 {
1355 Util.XmlConfig parentXmlConfig = (Util.XmlConfig)this.Core.GetIndexedElement("XmlConfig", (string)row[2]); 934 var id = row.FieldAsString(2);
1356 935 if (this.DecompilerHelper.TryGetIndexedElement("XmlConfig", id, out var parentXmlConfig))
1357 if (null != parentXmlConfig)
1358 { 936 {
1359 parentXmlConfig.AddChild(xmlConfig); 937 parentXmlConfig.Add(xmlConfig);
1360 } 938 }
1361 else 939 else
1362 { 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"));
1364 } 942 }
1365 } 943 }
1366 else 944 else
1367 { 945 {
1368 Wix.Component component = (Wix.Component)this.Core.GetIndexedElement("Component", (string)row[7]); 946 var componentId = row.FieldAsString(7);
1369 947 if (this.DecompilerHelper.TryGetIndexedElement("Component", componentId, out var component))
1370 if (null != component)
1371 { 948 {
1372 component.AddChild(xmlConfig); 949 component.Add(xmlConfig);
1373 } 950 }
1374 else 951 else
1375 { 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"));
1377 } 954 }
1378 } 955 }
1379 } 956 }
@@ -1391,113 +968,88 @@ namespace WixToolset.Extensions
1391 /// </remarks> 968 /// </remarks>
1392 private void FinalizeXmlFileTable(TableIndexedCollection tables) 969 private void FinalizeXmlFileTable(TableIndexedCollection tables)
1393 { 970 {
1394 Table xmlFileTable = tables["XmlFile"]; 971 if (tables.TryGetTable("XmlFile", out var xmlFileTable))
1395 Table eventManifestTable = tables["EventManifest"];
1396
1397 if (null != xmlFileTable)
1398 { 972 {
1399 foreach (Row row in xmlFileTable.Rows) 973 var eventManifestTable = tables["EventManifest"];
974
975 foreach (var row in xmlFileTable.Rows)
1400 { 976 {
1401 bool bManifestGenerated = false; 977 var manifestGenerated = false;
1402 string xmlFileConfigId = (string)row[0]; 978 var xmlFileConfigId = (string)row[0];
1403 if (null != eventManifestTable) 979 if (null != eventManifestTable)
1404 { 980 {
1405 foreach (Row emrow in eventManifestTable.Rows) 981 foreach (var emrow in eventManifestTable.Rows)
1406 { 982 {
1407 string formattedFile = (string)emrow[1]; 983 var formattedFile = (string)emrow[1];
1408 if ((formattedFile.StartsWith("[#", StringComparison.Ordinal) || formattedFile.StartsWith("[!", StringComparison.Ordinal)) 984 if ((formattedFile.StartsWith("[#", StringComparison.Ordinal) || formattedFile.StartsWith("[!", StringComparison.Ordinal))
1409 && formattedFile.EndsWith("]", StringComparison.Ordinal)) 985 && formattedFile.EndsWith("]", StringComparison.Ordinal))
1410 { 986 {
1411 string fileId = formattedFile.Substring(2, formattedFile.Length - 3); 987 var fileId = formattedFile.Substring(2, formattedFile.Length - 3);
1412 if (String.Equals(String.Concat("Config_", fileId, "ResourceFile"), xmlFileConfigId)) 988 if (String.Equals(String.Concat("Config_", fileId, "ResourceFile"), xmlFileConfigId))
1413 { 989 {
1414 Util.EventManifest eventManifest = (Util.EventManifest)this.Core.GetIndexedElement(emrow); 990 if (this.DecompilerHelper.TryGetIndexedElement(emrow, out var eventManifest))
1415 if (null != eventManifest)
1416 { 991 {
1417 eventManifest.ResourceFile = (string)row[4]; 992 eventManifest.Add(new XAttribute("ResourceFile", row.FieldAsString(4)));
1418 } 993 }
1419 bManifestGenerated = true; 994 manifestGenerated = true;
1420 } 995 }
1421 996
1422 else if (String.Equals(String.Concat("Config_", fileId, "MessageFile"), xmlFileConfigId)) 997 else if (String.Equals(String.Concat("Config_", fileId, "MessageFile"), xmlFileConfigId))
1423 { 998 {
1424 Util.EventManifest eventManifest = (Util.EventManifest)this.Core.GetIndexedElement(emrow); 999 if (this.DecompilerHelper.TryGetIndexedElement(emrow, out var eventManifest))
1425 if (null != eventManifest)
1426 { 1000 {
1427 eventManifest.MessageFile = (string)row[4]; 1001 eventManifest.Add(new XAttribute("MessageFile", row.FieldAsString(4)));
1428 } 1002 }
1429 bManifestGenerated = true; 1003 manifestGenerated = true;
1430 } 1004 }
1431 } 1005 }
1432 } 1006 }
1433 } 1007 }
1434 1008
1435 if (true == bManifestGenerated) 1009 if (manifestGenerated)
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])
1450 { 1010 {
1451 xmlFile.Value = (string)row[4]; 1011 continue;
1452 } 1012 }
1453 1013
1454 int flags = (int)row[5]; 1014 var action = "setValue";
1015 var flags = row.FieldAsInteger(5);
1455 if (0x1 == (flags & 0x1) && 0x2 == (flags & 0x2)) 1016 if (0x1 == (flags & 0x1) && 0x2 == (flags & 0x2))
1456 { 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]));
1458 } 1019 }
1459 else if (0x1 == (flags & 0x1)) 1020 else if (0x1 == (flags & 0x1))
1460 { 1021 {
1461 xmlFile.Action = Util.XmlFile.ActionType.createElement; 1022 action = "createElement";
1462 } 1023 }
1463 else if (0x2 == (flags & 0x2)) 1024 else if (0x2 == (flags & 0x2))
1464 { 1025 {
1465 xmlFile.Action = Util.XmlFile.ActionType.deleteValue; 1026 action = "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;
1480 } 1027 }
1481 1028
1482 if (0x00010000 == (flags & 0x00010000)) 1029 var selectionLanguage = (0x100 == (flags & 0x100)) ? "XPath" : null;
1483 { 1030 var preserveModifiedDate = 0x00001000 == (flags & 0x00001000);
1484 xmlFile.Permanent = Util.YesNoType.yes; 1031 var permanent = 0x00010000 == (flags & 0x00010000);
1485 }
1486 1032
1487 if (null != row[7]) 1033 if (this.DecompilerHelper.TryGetIndexedElement("Component", row.FieldAsString(6), out var component))
1488 { 1034 {
1489 xmlFile.Sequence = (int)row[7]; 1035 var xmlFile = new XElement(UtilConstants.XmlFileName,
1490 } 1036 AttributeIfNotNull("Id", row, 0),
1491 1037 AttributeIfNotNull("File", row, 1),
1492 Wix.Component component = (Wix.Component)this.Core.GetIndexedElement("Component", (string)row[6]); 1038 AttributeIfNotNull("ElementPath", row, 2),
1493 1039 AttributeIfNotNull("Name", row, 3),
1494 if (null != component) 1040 AttributeIfNotNull("Value", row, 4),
1495 { 1041 AttributeIfNotNull("Action", action),
1496 component.AddChild(xmlFile); 1042 AttributeIfNotNull("SelectionLanguage", selectionLanguage),
1043 AttributeIfTrue("PreserveModifiedDate", preserveModifiedDate),
1044 AttributeIfTrue("Permanent", permanent),
1045 NumericAttributeIfNotNull("Sequence", row, 7)
1046 );
1047
1048 component.Add(xmlFile);
1497 } 1049 }
1498 else 1050 else
1499 { 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"));
1501 } 1053 }
1502 } 1054 }
1503 } 1055 }
@@ -1510,34 +1062,122 @@ namespace WixToolset.Extensions
1510 /// <param name="tables">The collection of all tables.</param> 1062 /// <param name="tables">The collection of all tables.</param>
1511 private void FinalizeEventManifestTable(TableIndexedCollection tables) 1063 private void FinalizeEventManifestTable(TableIndexedCollection tables)
1512 { 1064 {
1513 Table eventManifestTable = tables["EventManifest"]; 1065 if (tables.TryGetTable("EventManifest", out var eventManifestTable))
1514
1515 if (null != eventManifestTable)
1516 { 1066 {
1517 foreach (Row row in eventManifestTable.Rows) 1067 foreach (var row in eventManifestTable.Rows)
1518 { 1068 {
1519 string formattedFile = (string)row[1]; 1069 var eventManifest = this.DecompilerHelper.GetIndexedElement(row);
1520 Util.EventManifest eventManifest = (Util.EventManifest)this.Core.GetIndexedElement(row); 1070 var formattedFile = row.FieldAsString(1);
1521 1071
1522 // try to "de-format" the File column's value to determine the proper parent File element 1072 // try to "de-format" the File column's value to determine the proper parent File element
1523 if ((formattedFile.StartsWith("[#", StringComparison.Ordinal) || formattedFile.StartsWith("[!", StringComparison.Ordinal)) 1073 if ((formattedFile.StartsWith("[#", StringComparison.Ordinal) || formattedFile.StartsWith("[!", StringComparison.Ordinal))
1524 && formattedFile.EndsWith("]", StringComparison.Ordinal)) 1074 && formattedFile.EndsWith("]", StringComparison.Ordinal))
1525 { 1075 {
1526 string fileId = formattedFile.Substring(2, formattedFile.Length - 3); 1076 var fileId = formattedFile.Substring(2, formattedFile.Length - 3);
1527 1077
1528 Wix.File file = (Wix.File)this.Core.GetIndexedElement("File", fileId); 1078 if (this.DecompilerHelper.TryGetIndexedElement("File", fileId, out var file))
1529 if (null != file)
1530 { 1079 {
1531 file.AddChild(eventManifest); 1080 file.Add(eventManifest);
1532 } 1081 }
1533 } 1082 }
1534 else 1083 else
1535 { 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));
1537 } 1120 }
1538 } 1121 }
1539 } 1122 }
1540 } 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 }
1541 } 1182 }
1542#endif
1543} 1183}
diff --git a/src/ext/Util/wixext/UtilExtensionFactory.cs b/src/ext/Util/wixext/UtilExtensionFactory.cs
index 08352813..d9bf575c 100644
--- a/src/ext/Util/wixext/UtilExtensionFactory.cs
+++ b/src/ext/Util/wixext/UtilExtensionFactory.cs
@@ -11,6 +11,7 @@ namespace WixToolset.Util
11 protected override IReadOnlyCollection<Type> ExtensionTypes => new[] 11 protected override IReadOnlyCollection<Type> ExtensionTypes => new[]
12 { 12 {
13 typeof(UtilCompiler), 13 typeof(UtilCompiler),
14 typeof(UtilDecompiler),
14 typeof(UtilExtensionData), 15 typeof(UtilExtensionData),
15 typeof(UtilWindowsInstallerBackendBinderExtension), 16 typeof(UtilWindowsInstallerBackendBinderExtension),
16 }; 17 };
diff --git a/src/ext/Util/wixext/UtilTableDefinitions.cs b/src/ext/Util/wixext/UtilTableDefinitions.cs
index c5ecab87..57c18b6c 100644
--- a/src/ext/Util/wixext/UtilTableDefinitions.cs
+++ b/src/ext/Util/wixext/UtilTableDefinitions.cs
@@ -85,7 +85,7 @@ namespace WixToolset.Util
85 UtilSymbolDefinitions.FileSharePermissions, 85 UtilSymbolDefinitions.FileSharePermissions,
86 new[] 86 new[]
87 { 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), 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"), 90 new ColumnDefinition("Permissions", ColumnType.Number, 4, primaryKey: false, nullable: false, ColumnCategory.Unknown, description: "Permissions int, as in EXPLICIT_ACCESS.grfAccessPermissions in MSDN"),
91 }, 91 },
diff --git a/src/internal/WixBuildTools.TestSupport/Builder.cs b/src/internal/WixBuildTools.TestSupport/Builder.cs
index ef0de8c9..31df0084 100644
--- a/src/internal/WixBuildTools.TestSupport/Builder.cs
+++ b/src/internal/WixBuildTools.TestSupport/Builder.cs
@@ -66,5 +66,93 @@ namespace WixBuildTools.TestSupport
66 return Query.QueryDatabase(outputPath, tables); 66 return Query.QueryDatabase(outputPath, tables);
67 } 67 }
68 } 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 }
69 } 157 }
70} 158}
diff --git a/src/wix/WixToolset.Core.Burn/BurnExtensionFactory.cs b/src/wix/WixToolset.Core.Burn/BurnExtensionFactory.cs
index eca94f77..6d2e97ba 100644
--- a/src/wix/WixToolset.Core.Burn/BurnExtensionFactory.cs
+++ b/src/wix/WixToolset.Core.Burn/BurnExtensionFactory.cs
@@ -22,7 +22,7 @@ namespace WixToolset.Core.Burn
22 { 22 {
23 extension = new BurnExtensionCommandLine(this.ServiceProvider); 23 extension = new BurnExtensionCommandLine(this.ServiceProvider);
24 } 24 }
25 if (extensionType == typeof(IBackendFactory)) 25 else if (extensionType == typeof(IBackendFactory))
26 { 26 {
27 extension = new BurnBackendFactory(); 27 extension = new BurnBackendFactory();
28 } 28 }
diff --git a/src/wix/WixToolset.Core.WindowsInstaller/CommandLine/DecompilerSubcommand.cs b/src/wix/WixToolset.Core.WindowsInstaller/CommandLine/DecompilerSubcommand.cs
index 19d1c738..0f806c95 100644
--- a/src/wix/WixToolset.Core.WindowsInstaller/CommandLine/DecompilerSubcommand.cs
+++ b/src/wix/WixToolset.Core.WindowsInstaller/CommandLine/DecompilerSubcommand.cs
@@ -66,11 +66,16 @@ namespace WixToolset.Core.WindowsInstaller.CommandLine
66 this.OutputPath = Path.ChangeExtension(this.InputPath, ".wxs"); 66 this.OutputPath = Path.ChangeExtension(this.InputPath, ".wxs");
67 } 67 }
68 68
69 var extensionManager = this.ServiceProvider.GetService<IExtensionManager>();
70 var creator = this.ServiceProvider.GetService<ISymbolDefinitionCreator>();
71
69 var context = this.ServiceProvider.GetService<IWindowsInstallerDecompileContext>(); 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>();
71 context.DecompilePath = this.InputPath; 75 context.DecompilePath = this.InputPath;
72 context.DecompileType = decompileType; 76 context.DecompileType = decompileType;
73 context.IntermediateFolder = this.IntermediateFolder; 77 context.IntermediateFolder = this.IntermediateFolder;
78 context.SymbolDefinitionCreator = creator;
74 context.OutputPath = this.OutputPath; 79 context.OutputPath = this.OutputPath;
75 80
76 context.ExtractFolder = this.ExportBasePath ?? this.IntermediateFolder; 81 context.ExtractFolder = this.ExportBasePath ?? this.IntermediateFolder;
diff --git a/src/wix/WixToolset.Core.WindowsInstaller/Decompile/DecompileMsiOrMsmCommand.cs b/src/wix/WixToolset.Core.WindowsInstaller/Decompile/DecompileMsiOrMsmCommand.cs
deleted file mode 100644
index 5001828d..00000000
--- a/src/wix/WixToolset.Core.WindowsInstaller/Decompile/DecompileMsiOrMsmCommand.cs
+++ /dev/null
@@ -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
3namespace 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}
diff --git a/src/wix/WixToolset.Core.WindowsInstaller/Decompile/Decompiler.cs b/src/wix/WixToolset.Core.WindowsInstaller/Decompile/Decompiler.cs
index 4ccfaaa5..0bd152f2 100644
--- a/src/wix/WixToolset.Core.WindowsInstaller/Decompile/Decompiler.cs
+++ b/src/wix/WixToolset.Core.WindowsInstaller/Decompile/Decompiler.cs
@@ -43,11 +43,14 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
43 /// <summary> 43 /// <summary>
44 /// Creates a new decompiler object with a default set of table definitions. 44 /// Creates a new decompiler object with a default set of table definitions.
45 /// </summary> 45 /// </summary>
46 public Decompiler(IMessaging messaging, IBackendHelper backendHelper, IEnumerable<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 { 47 {
48 this.Messaging = messaging; 48 this.Messaging = messaging;
49 this.BackendHelper = backendHelper; 49 this.BackendHelper = backendHelper;
50 this.DecompilerHelper = decompilerHelper;
50 this.Extensions = extensions; 51 this.Extensions = extensions;
52 this.ExtensionData = extensionData;
53 this.SymbolDefinitionCreator = creator;
51 this.BaseSourcePath = baseSourcePath ?? "SourceDir"; 54 this.BaseSourcePath = baseSourcePath ?? "SourceDir";
52 this.SuppressCustomTables = suppressCustomTables; 55 this.SuppressCustomTables = suppressCustomTables;
53 this.SuppressDroppingEmptyTables = suppressDroppingEmptyTables; 56 this.SuppressDroppingEmptyTables = suppressDroppingEmptyTables;
@@ -65,8 +68,14 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
65 68
66 private IBackendHelper BackendHelper { get; } 69 private IBackendHelper BackendHelper { get; }
67 70
71 private IWindowsInstallerDecompilerHelper DecompilerHelper { get; }
72
68 private IEnumerable<IWindowsInstallerDecompilerExtension> Extensions { get; } 73 private IEnumerable<IWindowsInstallerDecompilerExtension> Extensions { get; }
69 74
75 private IEnumerable<IExtensionData> ExtensionData { get; }
76
77 private ISymbolDefinitionCreator SymbolDefinitionCreator { get; }
78
70 private Dictionary<string, IWindowsInstallerDecompilerExtension> ExtensionsByTableName { get; } 79 private Dictionary<string, IWindowsInstallerDecompilerExtension> ExtensionsByTableName { get; }
71 80
72 private string BaseSourcePath { get; } 81 private string BaseSourcePath { get; }
@@ -87,8 +96,6 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
87 96
88 private bool Compressed { get; set; } 97 private bool Compressed { get; set; }
89 98
90 private XElement RootElement { get; set; }
91
92 private TableDefinitionCollection TableDefinitions { get; } 99 private TableDefinitionCollection TableDefinitions { get; }
93 100
94 private bool ShortNames { get; set; } 101 private bool ShortNames { get; set; }
@@ -101,8 +108,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
101 { 108 {
102 if (null == this.uiElement) 109 if (null == this.uiElement)
103 { 110 {
104 this.uiElement = new XElement(Names.UIElement); 111 this.uiElement = this.DecompilerHelper.AddElementToRoot(new XElement(Names.UIElement));
105 this.RootElement.Add(this.uiElement);
106 } 112 }
107 113
108 return this.uiElement; 114 return this.uiElement;
@@ -111,8 +117,6 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
111 117
112 private Dictionary<string, XElement> Singletons { get; } = new Dictionary<string, XElement>(); 118 private Dictionary<string, XElement> Singletons { get; } = new Dictionary<string, XElement>();
113 119
114 private Dictionary<string, XElement> IndexedElements { get; } = new Dictionary<string, XElement>();
115
116 private Dictionary<string, XElement> PatchTargetFiles { get; } = new Dictionary<string, XElement>(); 120 private Dictionary<string, XElement> PatchTargetFiles { get; } = new Dictionary<string, XElement>();
117 121
118 /// <summary> 122 /// <summary>
@@ -122,13 +126,23 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
122 /// <returns>The serialized WiX source code.</returns> 126 /// <returns>The serialized WiX source code.</returns>
123 public XDocument Decompile(WindowsInstallerData output) 127 public XDocument Decompile(WindowsInstallerData output)
124 { 128 {
125 if (null == output) 129 this.OutputType = output.Type;
130
131 switch (this.OutputType)
126 { 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.");
128 } 144 }
129 145
130 this.OutputType = output.Type;
131
132 // collect the table definitions from the output 146 // collect the table definitions from the output
133 this.TableDefinitions.Clear(); 147 this.TableDefinitions.Clear();
134 foreach (var table in output.Tables) 148 foreach (var table in output.Tables)
@@ -146,30 +160,11 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
146 } 160 }
147 161
148 // add any missing extension table definitions 162 // add any missing extension table definitions
149#if TODO_DECOMPILER_EXTENSIONS
150 foreach (var extension in this.Extensions) 163 foreach (var extension in this.Extensions)
151 { 164 {
152 this.AddExtension(extension); 165 this.AddExtensionTableDefinitions(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.");
169 } 166 }
170 167
171 var xWix = new XElement(Names.WixElement, this.RootElement);
172
173 // try to decompile the database file 168 // try to decompile the database file
174 // stop processing if an error previously occurred 169 // stop processing if an error previously occurred
175 if (this.Messaging.EncounteredError) 170 if (this.Messaging.EncounteredError)
@@ -192,16 +187,14 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
192 this.FinalizeDecompile(output.Tables); 187 this.FinalizeDecompile(output.Tables);
193 188
194 // return the XML document only if decompilation completed successfully 189 // return the XML document only if decompilation completed successfully
195 var document = new XDocument(xWix); 190 return this.Messaging.EncounteredError ? null : new XDocument(new XElement(Names.WixElement, this.DecompilerHelper.RootElement));
196 return this.Messaging.EncounteredError ? null : document;
197 } 191 }
198 192
199#if TODO_DECOMPILER_EXTENSIONS 193 private void AddExtensionTableDefinitions(IWindowsInstallerDecompilerExtension extension)
200 private void AddExtension(IWindowsInstallerBackendDecompilerExtension extension)
201 { 194 {
202 if (null != extension.TableDefinitions) 195 if (null != extension.TableDefinitions)
203 { 196 {
204 foreach (TableDefinition tableDefinition in extension.TableDefinitions) 197 foreach (var tableDefinition in extension.TableDefinitions)
205 { 198 {
206 if (!this.ExtensionsByTableName.ContainsKey(tableDefinition.Name)) 199 if (!this.ExtensionsByTableName.ContainsKey(tableDefinition.Name))
207 { 200 {
@@ -214,7 +207,6 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
214 } 207 }
215 } 208 }
216 } 209 }
217#endif
218 210
219 internal static Platform? GetPlatformFromTemplateSummaryInformation(string[] template) 211 internal static Platform? GetPlatformFromTemplateSummaryInformation(string[] template)
220 { 212 {
@@ -234,75 +226,10 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
234 return null; 226 return null;
235 } 227 }
236 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
302 private Dictionary<string, List<XElement>> IndexTableOneToMany(IEnumerable<Row> rows, int column = 0) 229 private Dictionary<string, List<XElement>> IndexTableOneToMany(IEnumerable<Row> rows, int column = 0)
303 { 230 {
304 return rows 231 return rows
305 .ToLookup(row => row.FieldAsString(column), row => this.GetIndexedElement(row)) 232 .ToLookup(row => row.FieldAsString(column), row => this.DecompilerHelper.GetIndexedElement(row))
306 .ToDictionary(lookup => lookup.Key, lookup => lookup.ToList()); 233 .ToDictionary(lookup => lookup.Key, lookup => lookup.ToList());
307 } 234 }
308 235
@@ -319,7 +246,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
319 private void AddChildToParent(string parentName, XElement xChild, Row row, int column) 246 private void AddChildToParent(string parentName, XElement xChild, Row row, int column)
320 { 247 {
321 var key = row.FieldAsString(column); 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))
323 { 250 {
324 xParent.Add(xChild); 251 xParent.Add(xChild);
325 } 252 }
@@ -419,7 +346,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
419 { 346 {
420 XElement xAction; 347 XElement xAction;
421 348
422 if (this.TryGetIndexedElement("CustomAction", out var _, actionSymbol.Action)) // custom action 349 if (this.DecompilerHelper.TryGetIndexedElement("CustomAction", actionSymbol.Action, out var _)) // custom action
423 { 350 {
424 xAction = new XElement(Names.CustomElement, 351 xAction = new XElement(Names.CustomElement,
425 new XAttribute("Action", actionSymbol.Action), 352 new XAttribute("Action", actionSymbol.Action),
@@ -455,7 +382,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
455 break; 382 break;
456 } 383 }
457 } 384 }
458 else if (this.TryGetIndexedElement("Dialog", out var _, actionSymbol.Action)) // dialog 385 else if (this.DecompilerHelper.TryGetIndexedElement("Dialog", actionSymbol.Action, out var _)) // dialog
459 { 386 {
460 xAction = new XElement(Names.CustomElement, 387 xAction = new XElement(Names.CustomElement,
461 new XAttribute("Dialog", actionSymbol.Action), 388 new XAttribute("Dialog", actionSymbol.Action),
@@ -495,7 +422,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
495 { 422 {
496 xSequence = new XElement(Names.WxsNamespace + sequenceTable); 423 xSequence = new XElement(Names.WxsNamespace + sequenceTable);
497 424
498 this.RootElement.Add(xSequence); 425 this.DecompilerHelper.AddElementToRoot(xSequence);
499 this.Singletons.Add(sequenceTable, xSequence); 426 this.Singletons.Add(sequenceTable, xSequence);
500 } 427 }
501 428
@@ -669,14 +596,12 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
669 /// <returns>The property element.</returns> 596 /// <returns>The property element.</returns>
670 private XElement EnsureProperty(string id) 597 private XElement EnsureProperty(string id)
671 { 598 {
672 XElement xProperty; 599 if (!this.DecompilerHelper.TryGetIndexedElement("Property", id, out var xProperty))
673
674 if (!this.TryGetIndexedElement("Property", out xProperty, id))
675 { 600 {
676 xProperty = new XElement(Names.PropertyElement, new XAttribute("Id", id)); 601 xProperty = new XElement(Names.PropertyElement, new XAttribute("Id", id));
677 602
678 this.RootElement.Add(xProperty); 603 this.DecompilerHelper.AddElementToRoot(xProperty);
679 this.IndexElement(xProperty, "Property", id); 604 this.DecompilerHelper.IndexElement("Property", id, xProperty);
680 } 605 }
681 606
682 return xProperty; 607 return xProperty;
@@ -713,6 +638,11 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
713 this.FinalizeSequenceTables(tables); 638 this.FinalizeSequenceTables(tables);
714 this.FinalizeVerbTable(tables); 639 this.FinalizeVerbTable(tables);
715 } 640 }
641
642 foreach (var extension in this.Extensions)
643 {
644 extension.PostDecompileTables(tables);
645 }
716 } 646 }
717 647
718 /// <summary> 648 /// <summary>
@@ -743,7 +673,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
743 { 673 {
744 foreach (var row in controlTable.Rows) 674 foreach (var row in controlTable.Rows)
745 { 675 {
746 var xControl = this.GetIndexedElement(row); 676 var xControl = this.DecompilerHelper.GetIndexedElement(row);
747 677
748 if ("CheckBox" == row.FieldAsString(2)) 678 if ("CheckBox" == row.FieldAsString(2))
749 { 679 {
@@ -791,9 +721,9 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
791 { 721 {
792 // Add the TARGETDIR StandardDirectory if a component is directly parented there. 722 // Add the TARGETDIR StandardDirectory if a component is directly parented there.
793 if (componentTable.Rows.Any(row => row.FieldAsString(2) == "TARGETDIR") 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))
795 { 725 {
796 this.RootElement.Add(xDirectory); 726 this.DecompilerHelper.AddElementToRoot(xDirectory);
797 } 727 }
798 728
799 foreach (var row in componentTable.Rows) 729 foreach (var row in componentTable.Rows)
@@ -803,12 +733,12 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
803 733
804 if (String.IsNullOrEmpty(keyPath)) 734 if (String.IsNullOrEmpty(keyPath))
805 { 735 {
806 var xComponent = this.GetIndexedElement("Component", row.FieldAsString(0)); 736 var xComponent = this.DecompilerHelper.GetIndexedElement("Component", row.FieldAsString(0));
807 xComponent.SetAttributeValue("KeyPath", "yes"); 737 xComponent.SetAttributeValue("KeyPath", "yes");
808 } 738 }
809 else if (WindowsInstallerConstants.MsidbComponentAttributesRegistryKeyPath == (attributes & WindowsInstallerConstants.MsidbComponentAttributesRegistryKeyPath)) 739 else if (WindowsInstallerConstants.MsidbComponentAttributesRegistryKeyPath == (attributes & WindowsInstallerConstants.MsidbComponentAttributesRegistryKeyPath))
810 { 740 {
811 if (this.TryGetIndexedElement("Registry", out var xRegistry, keyPath)) 741 if (this.DecompilerHelper.TryGetIndexedElement("Registry", keyPath, out var xRegistry))
812 { 742 {
813 if (xRegistry.Name.LocalName == "RegistryValue") 743 if (xRegistry.Name.LocalName == "RegistryValue")
814 { 744 {
@@ -826,7 +756,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
826 } 756 }
827 else if (WindowsInstallerConstants.MsidbComponentAttributesODBCDataSource == (attributes & WindowsInstallerConstants.MsidbComponentAttributesODBCDataSource)) 757 else if (WindowsInstallerConstants.MsidbComponentAttributesODBCDataSource == (attributes & WindowsInstallerConstants.MsidbComponentAttributesODBCDataSource))
828 { 758 {
829 if (this.TryGetIndexedElement("ODBCDataSource", out var xOdbcDataSource, keyPath)) 759 if (this.DecompilerHelper.TryGetIndexedElement("ODBCDataSource", keyPath, out var xOdbcDataSource))
830 { 760 {
831 xOdbcDataSource.SetAttributeValue("KeyPath", "yes"); 761 xOdbcDataSource.SetAttributeValue("KeyPath", "yes");
832 } 762 }
@@ -837,7 +767,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
837 } 767 }
838 else 768 else
839 { 769 {
840 if (this.TryGetIndexedElement("File", out var xFile, keyPath)) 770 if (this.DecompilerHelper.TryGetIndexedElement("File", keyPath, out var xFile))
841 { 771 {
842 xFile.SetAttributeValue("KeyPath", "yes"); 772 xFile.SetAttributeValue("KeyPath", "yes");
843 } 773 }
@@ -854,8 +784,8 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
854 { 784 {
855 foreach (FileRow fileRow in fileTable.Rows) 785 foreach (FileRow fileRow in fileTable.Rows)
856 { 786 {
857 if (this.TryGetIndexedElement("Component", out var xComponent, fileRow.Component) 787 if (this.DecompilerHelper.TryGetIndexedElement("Component", fileRow.Component, out var xComponent)
858 && this.TryGetIndexedElement(fileRow, out var xFile)) 788 && this.DecompilerHelper.TryGetIndexedElement(fileRow, out var xFile))
859 { 789 {
860 xComponent.Add(xFile); 790 xComponent.Add(xFile);
861 } 791 }
@@ -871,8 +801,8 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
871 { 801 {
872 foreach (var row in odbcDataSourceTable.Rows) 802 foreach (var row in odbcDataSourceTable.Rows)
873 { 803 {
874 if (this.TryGetIndexedElement("Component", out var xComponent, row.FieldAsString(1)) 804 if (this.DecompilerHelper.TryGetIndexedElement("Component", row.FieldAsString(1), out var xComponent)
875 && this.TryGetIndexedElement(row, out var xOdbcDataSource)) 805 && this.DecompilerHelper.TryGetIndexedElement(row, out var xOdbcDataSource))
876 { 806 {
877 xComponent.Add(xOdbcDataSource); 807 xComponent.Add(xOdbcDataSource);
878 } 808 }
@@ -888,8 +818,8 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
888 { 818 {
889 foreach (var row in registryTable.Rows) 819 foreach (var row in registryTable.Rows)
890 { 820 {
891 if (this.TryGetIndexedElement("Component", out var xComponent, row.FieldAsString(5)) 821 if (this.DecompilerHelper.TryGetIndexedElement("Component", row.FieldAsString(5), out var xComponent)
892 && this.TryGetIndexedElement(row, out var xRegistry)) 822 && this.DecompilerHelper.TryGetIndexedElement(row, out var xRegistry))
893 { 823 {
894 xComponent.Add(xRegistry); 824 xComponent.Add(xRegistry);
895 } 825 }
@@ -927,10 +857,10 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
927 { 857 {
928 foreach (var dialogRow in dialogTable.Rows) 858 foreach (var dialogRow in dialogTable.Rows)
929 { 859 {
930 var xDialog = this.GetIndexedElement(dialogRow); 860 var xDialog = this.DecompilerHelper.GetIndexedElement(dialogRow);
931 var dialogId = dialogRow.FieldAsString(0); 861 var dialogId = dialogRow.FieldAsString(0);
932 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))
934 { 864 {
935 this.Messaging.Write(WarningMessages.ExpectedForeignRow(dialogRow.SourceLineNumbers, "Dialog", dialogRow.GetPrimaryKey(DecompilerConstants.PrimaryKeyDelimiter), "Dialog", dialogId, "Control_First", dialogRow.FieldAsString(7), "Control")); 865 this.Messaging.Write(WarningMessages.ExpectedForeignRow(dialogRow.SourceLineNumbers, "Dialog", dialogRow.GetPrimaryKey(DecompilerConstants.PrimaryKeyDelimiter), "Dialog", dialogId, "Control_First", dialogRow.FieldAsString(7), "Control"));
936 } 866 }
@@ -949,7 +879,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
949 var controlNext = controlRow.FieldAsString(10); 879 var controlNext = controlRow.FieldAsString(10);
950 if (!String.IsNullOrEmpty(controlNext)) 880 if (!String.IsNullOrEmpty(controlNext))
951 { 881 {
952 if (this.TryGetIndexedElement("Control", out xControl, dialogId, controlNext)) 882 if (this.DecompilerHelper.TryGetIndexedElement("Control", dialogId, controlNext, out xControl))
953 { 883 {
954 // looped back to the first control in the dialog 884 // looped back to the first control in the dialog
955 if (addedControls.Contains(xControl)) 885 if (addedControls.Contains(xControl))
@@ -972,7 +902,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
972 var controlDefault = dialogRow.FieldAsString(8); 902 var controlDefault = dialogRow.FieldAsString(8);
973 if (!String.IsNullOrEmpty(controlDefault)) 903 if (!String.IsNullOrEmpty(controlDefault))
974 { 904 {
975 if (this.TryGetIndexedElement("Control", out var xDefaultControl, dialogId, controlDefault)) 905 if (this.DecompilerHelper.TryGetIndexedElement("Control", dialogId, controlDefault, out var xDefaultControl))
976 { 906 {
977 xDefaultControl.SetAttributeValue("Default", "yes"); 907 xDefaultControl.SetAttributeValue("Default", "yes");
978 } 908 }
@@ -986,7 +916,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
986 var controlCancel = dialogRow.FieldAsString(8); 916 var controlCancel = dialogRow.FieldAsString(8);
987 if (!String.IsNullOrEmpty(controlCancel)) 917 if (!String.IsNullOrEmpty(controlCancel))
988 { 918 {
989 if (this.TryGetIndexedElement("Control", out var xCancelControl, dialogId, controlCancel)) 919 if (this.DecompilerHelper.TryGetIndexedElement("Control", dialogId, controlCancel, out var xCancelControl))
990 { 920 {
991 xCancelControl.SetAttributeValue("Cancel", "yes"); 921 xCancelControl.SetAttributeValue("Cancel", "yes");
992 } 922 }
@@ -1004,13 +934,13 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
1004 foreach (var controlRow in controlTable.Rows) 934 foreach (var controlRow in controlTable.Rows)
1005 { 935 {
1006 var dialogId = controlRow.FieldAsString(0); 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))
1008 { 938 {
1009 this.Messaging.Write(WarningMessages.ExpectedForeignRow(controlRow.SourceLineNumbers, "Control", controlRow.GetPrimaryKey(DecompilerConstants.PrimaryKeyDelimiter), "Dialog_", dialogId, "Dialog")); 939 this.Messaging.Write(WarningMessages.ExpectedForeignRow(controlRow.SourceLineNumbers, "Control", controlRow.GetPrimaryKey(DecompilerConstants.PrimaryKeyDelimiter), "Dialog_", dialogId, "Dialog"));
1010 continue; 940 continue;
1011 } 941 }
1012 942
1013 var xControl = this.GetIndexedElement(controlRow); 943 var xControl = this.DecompilerHelper.GetIndexedElement(controlRow);
1014 if (!addedControls.Contains(xControl)) 944 if (!addedControls.Contains(xControl))
1015 { 945 {
1016 xControl.SetAttributeValue("TabSkip", "yes"); 946 xControl.SetAttributeValue("TabSkip", "yes");
@@ -1035,11 +965,11 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
1035 { 965 {
1036 foreach (var row in duplicateFileTable.Rows) 966 foreach (var row in duplicateFileTable.Rows)
1037 { 967 {
1038 var xCopyFile = this.GetIndexedElement(row); 968 var xCopyFile = this.DecompilerHelper.GetIndexedElement(row);
1039 var destination = row.FieldAsString(4); 969 var destination = row.FieldAsString(4);
1040 if (!String.IsNullOrEmpty(destination)) 970 if (!String.IsNullOrEmpty(destination))
1041 { 971 {
1042 if (this.TryGetIndexedElement("Directory", out var _, destination)) 972 if (this.DecompilerHelper.TryGetIndexedElement("Directory", destination, out var _))
1043 { 973 {
1044 xCopyFile.SetAttributeValue("DestinationDirectory", destination); 974 xCopyFile.SetAttributeValue("DestinationDirectory", destination);
1045 } 975 }
@@ -1056,11 +986,11 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
1056 { 986 {
1057 foreach (var row in moveFileTable.Rows) 987 foreach (var row in moveFileTable.Rows)
1058 { 988 {
1059 var xCopyFile = this.GetIndexedElement(row); 989 var xCopyFile = this.DecompilerHelper.GetIndexedElement(row);
1060 var source = row.FieldAsString(4); 990 var source = row.FieldAsString(4);
1061 if (!String.IsNullOrEmpty(source)) 991 if (!String.IsNullOrEmpty(source))
1062 { 992 {
1063 if (this.TryGetIndexedElement("Directory", out var _, source)) 993 if (this.DecompilerHelper.TryGetIndexedElement("Directory", source, out var _))
1064 { 994 {
1065 xCopyFile.SetAttributeValue("SourceDirectory", source); 995 xCopyFile.SetAttributeValue("SourceDirectory", source);
1066 } 996 }
@@ -1071,7 +1001,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
1071 } 1001 }
1072 1002
1073 var destination = row.FieldAsString(5); 1003 var destination = row.FieldAsString(5);
1074 if (this.TryGetIndexedElement("Directory", out var _, destination)) 1004 if (this.DecompilerHelper.TryGetIndexedElement("Directory", destination, out var _))
1075 { 1005 {
1076 xCopyFile.SetAttributeValue("DestinationDirectory", destination); 1006 xCopyFile.SetAttributeValue("DestinationDirectory", destination);
1077 } 1007 }
@@ -1134,7 +1064,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
1134 // TODO: warn about mismatch between columns 1064 // TODO: warn about mismatch between columns
1135 } 1065 }
1136 1066
1137 this.IndexElement(row, xProtectRange); 1067 this.DecompilerHelper.IndexElement(row, xProtectRange);
1138 } 1068 }
1139 } 1069 }
1140 1070
@@ -1144,8 +1074,8 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
1144 { 1074 {
1145 foreach (var row in externalFilesTable.Rows) 1075 foreach (var row in externalFilesTable.Rows)
1146 { 1076 {
1147 if (this.TryGetIndexedElement(row, out var xExternalFile) 1077 if (this.DecompilerHelper.TryGetIndexedElement(row, out var xExternalFile)
1148 && this.TryGetIndexedElement("FamilyFileRanges", out var xProtectRange, row.FieldAsString(0), row.FieldAsString(0))) 1078 && this.DecompilerHelper.TryGetIndexedElement("FamilyFileRanges", row.FieldAsString(0), row.FieldAsString(0), out var xProtectRange))
1149 { 1079 {
1150 xExternalFile.Add(xProtectRange); 1080 xExternalFile.Add(xProtectRange);
1151 usedProtectRanges.Add(xProtectRange); 1081 usedProtectRanges.Add(xProtectRange);
@@ -1178,7 +1108,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
1178 continue; 1108 continue;
1179 } 1109 }
1180 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))
1182 { 1112 {
1183 xTargetFile.Add(xProtectRange); 1113 xTargetFile.Add(xProtectRange);
1184 usedProtectRanges.Add(xProtectRange); 1114 usedProtectRanges.Add(xProtectRange);
@@ -1190,7 +1120,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
1190 { 1120 {
1191 foreach (var row in familyFileRangesTable.Rows) 1121 foreach (var row in familyFileRangesTable.Rows)
1192 { 1122 {
1193 var xProtectRange = this.GetIndexedElement(row); 1123 var xProtectRange = this.DecompilerHelper.GetIndexedElement(row);
1194 1124
1195 if (!usedProtectRanges.Contains(xProtectRange)) 1125 if (!usedProtectRanges.Contains(xProtectRange))
1196 { 1126 {
@@ -1276,7 +1206,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
1276 // set the disk identifiers and sources for files 1206 // set the disk identifiers and sources for files
1277 foreach (var fileRow in tables["File"]?.Rows.Cast<FileRow>() ?? Enumerable.Empty<FileRow>()) 1207 foreach (var fileRow in tables["File"]?.Rows.Cast<FileRow>() ?? Enumerable.Empty<FileRow>())
1278 { 1208 {
1279 var xFile = this.GetIndexedElement("File", fileRow.File); 1209 var xFile = this.DecompilerHelper.GetIndexedElement("File", fileRow.File);
1280 1210
1281 // Don't bother processing files that are orphaned (and won't show up in the output anyway) 1211 // Don't bother processing files that are orphaned (and won't show up in the output anyway)
1282 if (null != xFile.Parent) 1212 if (null != xFile.Parent)
@@ -1328,7 +1258,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
1328 // set the file assemblies and manifests 1258 // set the file assemblies and manifests
1329 foreach (var row in tables["MsiAssembly"]?.Rows ?? Enumerable.Empty<Row>()) 1259 foreach (var row in tables["MsiAssembly"]?.Rows ?? Enumerable.Empty<Row>())
1330 { 1260 {
1331 if (this.TryGetIndexedElement("Component", out var xComponent, row.FieldAsString(0))) 1261 if (this.DecompilerHelper.TryGetIndexedElement("Component", row.FieldAsString(0), out var xComponent))
1332 { 1262 {
1333 foreach (var xFile in xComponent.Elements(Names.FileElement).Where(x => x.Attribute("KeyPath")?.Value == "yes")) 1263 foreach (var xFile in xComponent.Elements(Names.FileElement).Where(x => x.Attribute("KeyPath")?.Value == "yes"))
1334 { 1264 {
@@ -1346,8 +1276,8 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
1346 // nest the TypeLib elements 1276 // nest the TypeLib elements
1347 foreach (var row in tables["TypeLib"]?.Rows ?? Enumerable.Empty<Row>()) 1277 foreach (var row in tables["TypeLib"]?.Rows ?? Enumerable.Empty<Row>())
1348 { 1278 {
1349 var xComponent = this.GetIndexedElement("Component", row.FieldAsString(2)); 1279 var xComponent = this.DecompilerHelper.GetIndexedElement("Component", row.FieldAsString(2));
1350 var xTypeLib = this.GetIndexedElement(row); 1280 var xTypeLib = this.DecompilerHelper.GetIndexedElement(row);
1351 1281
1352 foreach (var xFile in xComponent.Elements(Names.FileElement).Where(x => x.Attribute("KeyPath")?.Value == "yes")) 1282 foreach (var xFile in xComponent.Elements(Names.FileElement).Where(x => x.Attribute("KeyPath")?.Value == "yes"))
1353 { 1283 {
@@ -1374,7 +1304,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
1374 var mimeRef = row.FieldAsString(3); 1304 var mimeRef = row.FieldAsString(3);
1375 if (null != mimeRef) 1305 if (null != mimeRef)
1376 { 1306 {
1377 if (this.TryGetIndexedElement("MIME", out var xMime, mimeRef)) 1307 if (this.DecompilerHelper.TryGetIndexedElement("MIME", mimeRef, out var xMime))
1378 { 1308 {
1379 xMime.SetAttributeValue("Default", "yes"); 1309 xMime.SetAttributeValue("Default", "yes");
1380 } 1310 }
@@ -1389,7 +1319,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
1389 1319
1390 foreach (var row in tables["MIME"]?.Rows ?? Enumerable.Empty<Row>()) 1320 foreach (var row in tables["MIME"]?.Rows ?? Enumerable.Empty<Row>())
1391 { 1321 {
1392 var xMime = this.GetIndexedElement(row); 1322 var xMime = this.DecompilerHelper.GetIndexedElement(row);
1393 1323
1394 if (extensionsByExtensionId.TryGetValue(row.FieldAsString(1), out var xExtensions)) 1324 if (extensionsByExtensionId.TryGetValue(row.FieldAsString(1), out var xExtensions))
1395 { 1325 {
@@ -1427,9 +1357,9 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
1427 foreach (var row in classRows) 1357 foreach (var row in classRows)
1428 { 1358 {
1429 var clsid = row.FieldAsString(0); 1359 var clsid = row.FieldAsString(0);
1430 var xClass = this.GetIndexedElement(row); 1360 var xClass = this.DecompilerHelper.GetIndexedElement(row);
1431 1361
1432 if (this.TryGetIndexedElement("ProgId", out var xProgId, row.FieldAsString(3))) 1362 if (this.DecompilerHelper.TryGetIndexedElement("ProgId", row.FieldAsString(3), out var xProgId))
1433 { 1363 {
1434 if (addedProgIds.TryGetValue(xProgId, out var progid)) 1364 if (addedProgIds.TryGetValue(xProgId, out var progid))
1435 { 1365 {
@@ -1451,7 +1381,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
1451 foreach (var row in tables["ProgId"]?.Rows ?? Enumerable.Empty<Row>()) 1381 foreach (var row in tables["ProgId"]?.Rows ?? Enumerable.Empty<Row>())
1452 { 1382 {
1453 var clsid = row.FieldAsString(2); 1383 var clsid = row.FieldAsString(2);
1454 var xProgId = this.GetIndexedElement(row); 1384 var xProgId = this.DecompilerHelper.GetIndexedElement(row);
1455 1385
1456 if (!addedProgIds.ContainsKey(xProgId) && null != clsid && null == xProgId.Parent) 1386 if (!addedProgIds.ContainsKey(xProgId) && null != clsid && null == xProgId.Parent)
1457 { 1387 {
@@ -1475,7 +1405,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
1475 1405
1476 foreach (var row in tables["Extension"]?.Rows?.Where(row => row.FieldAsString(2) != null) ?? Enumerable.Empty<Row>()) 1406 foreach (var row in tables["Extension"]?.Rows?.Where(row => row.FieldAsString(2) != null) ?? Enumerable.Empty<Row>())
1477 { 1407 {
1478 var xProgId = this.GetIndexedElement("ProgId", row.FieldAsString(2)); 1408 var xProgId = this.DecompilerHelper.GetIndexedElement("ProgId", row.FieldAsString(2));
1479 1409
1480 // Haven't added the progId yet and it doesn't have a parent progId 1410 // Haven't added the progId yet and it doesn't have a parent progId
1481 if (!addedProgIds.ContainsKey(xProgId) && null == xProgId.Parent) 1411 if (!addedProgIds.ContainsKey(xProgId) && null == xProgId.Parent)
@@ -1510,7 +1440,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
1510 var bits = row.FieldAsInteger(1); 1440 var bits = row.FieldAsInteger(1);
1511 if (WindowsInstallerConstants.MsidbCustomActionTypeHideTarget == (bits & WindowsInstallerConstants.MsidbCustomActionTypeHideTarget) 1441 if (WindowsInstallerConstants.MsidbCustomActionTypeHideTarget == (bits & WindowsInstallerConstants.MsidbCustomActionTypeHideTarget)
1512 && WindowsInstallerConstants.MsidbCustomActionTypeInScript == (bits & WindowsInstallerConstants.MsidbCustomActionTypeInScript) 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)
1514 && String.IsNullOrEmpty(xProperty.Attribute("Value")?.Value) 1444 && String.IsNullOrEmpty(xProperty.Attribute("Value")?.Value)
1515 && xProperty.Attribute("Secure")?.Value != "yes" 1445 && xProperty.Attribute("Secure")?.Value != "yes"
1516 && xProperty.Attribute("SuppressModularization")?.Value != "yes") 1446 && xProperty.Attribute("SuppressModularization")?.Value != "yes")
@@ -1531,10 +1461,10 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
1531 { 1461 {
1532 foreach (var row in tables["RemoveFile"]?.Rows ?? Enumerable.Empty<Row>()) 1462 foreach (var row in tables["RemoveFile"]?.Rows ?? Enumerable.Empty<Row>())
1533 { 1463 {
1534 var xRemove = this.GetIndexedElement(row); 1464 var xRemove = this.DecompilerHelper.GetIndexedElement(row);
1535 var property = row.FieldAsString(3); 1465 var property = row.FieldAsString(3);
1536 1466
1537 if (this.TryGetIndexedElement("Directory", out var _, property)) 1467 if (this.DecompilerHelper.TryGetIndexedElement("Directory", property, out var _))
1538 { 1468 {
1539 xRemove.SetAttributeValue("Directory", property); 1469 xRemove.SetAttributeValue("Directory", property);
1540 } 1470 }
@@ -1562,7 +1492,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
1562 { 1492 {
1563 var id = row.FieldAsString(0); 1493 var id = row.FieldAsString(0);
1564 var table = row.FieldAsString(1); 1494 var table = row.FieldAsString(1);
1565 var xPermission = this.GetIndexedElement(row); 1495 var xPermission = this.DecompilerHelper.GetIndexedElement(row);
1566 1496
1567 if ("CreateFolder" == table) 1497 if ("CreateFolder" == table)
1568 { 1498 {
@@ -1580,7 +1510,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
1580 } 1510 }
1581 else 1511 else
1582 { 1512 {
1583 if (this.TryGetIndexedElement(table, out var xParent, id)) 1513 if (this.DecompilerHelper.TryGetIndexedElement(table, id, out var xParent))
1584 { 1514 {
1585 xParent.Add(xPermission); 1515 xParent.Add(xPermission);
1586 } 1516 }
@@ -1651,12 +1581,12 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
1651 { 1581 {
1652 var appSearches = IndexTable(tables["AppSearch"], keyColumn: 1, dataColumn: 0); 1582 var appSearches = IndexTable(tables["AppSearch"], keyColumn: 1, dataColumn: 0);
1653 var ccpSearches = IndexTable(tables["CCPSearch"], keyColumn: 0, dataColumn: null); 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);
1655 1585
1656 var xComplianceCheck = new XElement(Names.ComplianceCheckElement); 1586 var xComplianceCheck = new XElement(Names.ComplianceCheckElement);
1657 if (ccpSearches.Keys.Any(ccpSignature => !appSearches.ContainsKey(ccpSignature))) 1587 if (ccpSearches.Keys.Any(ccpSignature => !appSearches.ContainsKey(ccpSignature)))
1658 { 1588 {
1659 this.RootElement.Add(xComplianceCheck); 1589 this.DecompilerHelper.AddElementToRoot(xComplianceCheck);
1660 } 1590 }
1661 1591
1662 // index the locator tables by their signatures 1592 // index the locator tables by their signatures
@@ -1703,7 +1633,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
1703 foreach (var locatorRow in locatorRows) 1633 foreach (var locatorRow in locatorRows)
1704 { 1634 {
1705 var used = true; 1635 var used = true;
1706 var xSearch = this.GetIndexedElement(locatorRow); 1636 var xSearch = this.DecompilerHelper.GetIndexedElement(locatorRow);
1707 1637
1708 if ("Signature" == locatorRow.TableDefinition.Name && 0 < xSignatureSearches.Count) 1638 if ("Signature" == locatorRow.TableDefinition.Name && 0 < xSignatureSearches.Count)
1709 { 1639 {
@@ -1791,7 +1721,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
1791 { 1721 {
1792 if ("DrLocator" == parentLocatorRow.TableDefinition.Name) 1722 if ("DrLocator" == parentLocatorRow.TableDefinition.Name)
1793 { 1723 {
1794 var xParentSearch = this.GetIndexedElement(parentLocatorRow); 1724 var xParentSearch = this.DecompilerHelper.GetIndexedElement(parentLocatorRow);
1795 1725
1796 if (xParentSearch.HasElements) 1726 if (xParentSearch.HasElements)
1797 { 1727 {
@@ -1824,7 +1754,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
1824 } 1754 }
1825 else if ("RegLocator" == parentLocatorRow.TableDefinition.Name) 1755 else if ("RegLocator" == parentLocatorRow.TableDefinition.Name)
1826 { 1756 {
1827 var xParentSearch = this.GetIndexedElement(parentLocatorRow); 1757 var xParentSearch = this.DecompilerHelper.GetIndexedElement(parentLocatorRow);
1828 1758
1829 xParentSearch.Add(xSearch); 1759 xParentSearch.Add(xSearch);
1830 xUsedSearches.Add(xSearch); 1760 xUsedSearches.Add(xSearch);
@@ -1982,11 +1912,11 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
1982 1912
1983 foreach (var row in shortcutTable.Rows) 1913 foreach (var row in shortcutTable.Rows)
1984 { 1914 {
1985 var xShortcut = this.GetIndexedElement(row); 1915 var xShortcut = this.DecompilerHelper.GetIndexedElement(row);
1986 1916
1987 var target = row.FieldAsString(4); 1917 var target = row.FieldAsString(4);
1988 1918
1989 if (this.TryGetIndexedElement("Feature", out var _, target)) 1919 if (this.DecompilerHelper.TryGetIndexedElement("Feature", target, out var _))
1990 { 1920 {
1991 xShortcut.SetAttributeValue("Advertise", "yes"); 1921 xShortcut.SetAttributeValue("Advertise", "yes");
1992 this.SetPrimaryFeature(row, 4, 3); 1922 this.SetPrimaryFeature(row, 4, 3);
@@ -2301,7 +2231,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
2301 xMajorUpgrade.SetAttributeValue("Schedule", scheduledType); 2231 xMajorUpgrade.SetAttributeValue("Schedule", scheduledType);
2302 } 2232 }
2303 2233
2304 this.RootElement.Add(xMajorUpgrade); 2234 this.DecompilerHelper.AddElementToRoot(xMajorUpgrade);
2305 } 2235 }
2306 } 2236 }
2307 } 2237 }
@@ -2326,7 +2256,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
2326 { 2256 {
2327 if (xExtensions.TryGetValue(row.FieldAsString(0), out var xVerbExtensions)) 2257 if (xExtensions.TryGetValue(row.FieldAsString(0), out var xVerbExtensions))
2328 { 2258 {
2329 var xVerb = this.GetIndexedElement(row); 2259 var xVerb = this.DecompilerHelper.GetIndexedElement(row);
2330 2260
2331 foreach (var xVerbExtension in xVerbExtensions) 2261 foreach (var xVerbExtension in xVerbExtensions)
2332 { 2262 {
@@ -2351,8 +2281,9 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
2351 var sourcePath = new StringBuilder(); 2281 var sourcePath = new StringBuilder();
2352 2282
2353 var component = xFile.Parent; 2283 var component = xFile.Parent;
2284 var xDirectory = component.Parent;
2354 2285
2355 for (var xDirectory = component.Parent; null != xDirectory && xDirectory.Name.LocalName == "Directory"; xDirectory = xDirectory.Parent) 2286 while (xDirectory?.Name.LocalName == "Directory")
2356 { 2287 {
2357 string name; 2288 string name;
2358 2289
@@ -2387,6 +2318,14 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
2387 sourcePath.Insert(0, Path.DirectorySeparatorChar); 2318 sourcePath.Insert(0, Path.DirectorySeparatorChar);
2388 sourcePath.Insert(0, name); 2319 sourcePath.Insert(0, name);
2389 } 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);
2390 } 2329 }
2391 2330
2392 return sourcePath.ToString(); 2331 return sourcePath.ToString();
@@ -2470,13 +2409,13 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
2470 this.ShortNames = false; 2409 this.ShortNames = false;
2471 2410
2472 this.Singletons.Clear(); 2411 this.Singletons.Clear();
2473 this.IndexedElements.Clear(); 2412 //this.IndexedElements.Clear();
2474 this.PatchTargetFiles.Clear(); 2413 this.PatchTargetFiles.Clear();
2475 2414
2476 // set the codepage if its not neutral (0) 2415 // set the codepage if its not neutral (0)
2477 if (0 != codepage) 2416 if (0 != codepage)
2478 { 2417 {
2479 this.RootElement.SetAttributeValue("Codepage", codepage); 2418 this.DecompilerHelper.RootElement.SetAttributeValue("Codepage", codepage);
2480 } 2419 }
2481 2420
2482 if (this.OutputType == OutputType.Module) 2421 if (this.OutputType == OutputType.Module)
@@ -2484,44 +2423,51 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
2484 var table = tables["_SummaryInformation"]; 2423 var table = tables["_SummaryInformation"];
2485 var row = table.Rows.SingleOrDefault(r => r.FieldAsInteger(0) == 9); 2424 var row = table.Rows.SingleOrDefault(r => r.FieldAsInteger(0) == 9);
2486 this.ModularizationGuid = row?.FieldAsString(1); 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);
2488 } 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);
2489 2440
2490 // index the rows from the extension libraries 2441 // index the rows from the extension libraries
2491 var indexedExtensionTables = new Dictionary<string, HashSet<string>>(); 2442 var indexedExtensionTables = new Dictionary<string, HashSet<string>>();
2492#if TODO_DECOMPILER_EXTENSIONS 2443 foreach (var extension in this.ExtensionData)
2493 foreach (var extension in this.Extensions)
2494 { 2444 {
2495 // Get the optional library from the extension with the rows to be removed. 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);
2497 if (library != null) 2447 if (library != null)
2498 { 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))
2500 { 2450 {
2501 string primaryKey; 2451 if (this.TryGetPrimaryKeyFromSymbol(tableDefinitionBySymbolDefinitionName, symbol, out var tableName, out var primaryKey))
2502 string tableName;
2503
2504 // the Actions table needs to be handled specially
2505 if (table.Name == "WixAction")
2506 { 2452 {
2507 primaryKey = row.FieldAsString(1); 2453 //// the Actions table needs to be handled specially
2508 tableName = row.FieldAsString(0); 2454 //if (table.Name == "WixAction")
2509 2455 //{
2510 if (this.outputType == OutputType.Module) 2456 // primaryKey = symbol.FieldAsString(1);
2511 { 2457 // tableName = symbol.FieldAsString(0);
2512 tableName = "Module" + tableName; 2458
2513 } 2459 // if (this.outputType == OutputType.Module)
2514 } 2460 // {
2515 else 2461 // tableName = "Module" + tableName;
2516 { 2462 // }
2517 primaryKey = row.GetPrimaryKey(DecompilerConstants.PrimaryKeyDelimiter); 2463 //}
2518 tableName = table.Name; 2464 //else
2519 } 2465 //{
2520 2466 // primaryKey = symbol.GetPrimaryKey(DecompilerConstants.PrimaryKeyDelimiter);
2521 if (primaryKey != null) 2467 // tableName = table.Name;
2522 { 2468 //}
2523 HashSet<string> indexedExtensionRows; 2469
2524 if (!indexedExtensionTables.TryGetValue(tableName, out indexedExtensionRows)) 2470 if (!indexedExtensionTables.TryGetValue(tableName, out var indexedExtensionRows))
2525 { 2471 {
2526 indexedExtensionRows = new HashSet<string>(); 2472 indexedExtensionRows = new HashSet<string>();
2527 indexedExtensionTables.Add(tableName, indexedExtensionRows); 2473 indexedExtensionTables.Add(tableName, indexedExtensionRows);
@@ -2532,7 +2478,6 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
2532 } 2478 }
2533 } 2479 }
2534 } 2480 }
2535#endif
2536 2481
2537 // remove the rows from the extension libraries (to allow full round-tripping) 2482 // remove the rows from the extension libraries (to allow full round-tripping)
2538 foreach (var kvp in indexedExtensionTables) 2483 foreach (var kvp in indexedExtensionTables)
@@ -2540,8 +2485,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
2540 var tableName = kvp.Key; 2485 var tableName = kvp.Key;
2541 var indexedExtensionRows = kvp.Value; 2486 var indexedExtensionRows = kvp.Value;
2542 2487
2543 var table = tables[tableName]; 2488 if (tables.TryGetTable(tableName, out var table))
2544 if (null != table)
2545 { 2489 {
2546 var originalRows = new RowDictionary<Row>(table); 2490 var originalRows = new RowDictionary<Row>(table);
2547 2491
@@ -2550,7 +2494,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
2550 2494
2551 foreach (var row in originalRows.Values) 2495 foreach (var row in originalRows.Values)
2552 { 2496 {
2553 if (!indexedExtensionRows.Contains(row.GetPrimaryKey(DecompilerConstants.PrimaryKeyDelimiter))) 2497 if (!indexedExtensionRows.Contains(row.GetPrimaryKey()))
2554 { 2498 {
2555 table.Rows.Add(row); 2499 table.Rows.Add(row);
2556 } 2500 }
@@ -2559,6 +2503,55 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
2559 } 2503 }
2560 } 2504 }
2561 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
2562 /// <summary> 2555 /// <summary>
2563 /// Decompile the tables. 2556 /// Decompile the tables.
2564 /// </summary> 2557 /// </summary>
@@ -2581,7 +2574,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
2581 // empty tables may be kept with EnsureTable if the user set the proper option 2574 // empty tables may be kept with EnsureTable if the user set the proper option
2582 if (0 == table.Rows.Count && this.SuppressDroppingEmptyTables) 2575 if (0 == table.Rows.Count && this.SuppressDroppingEmptyTables)
2583 { 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)));
2585 } 2578 }
2586 2579
2587 switch (table.Name) 2580 switch (table.Name)
@@ -2901,14 +2894,11 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
2901 break; 2894 break;
2902 2895
2903 default: 2896 default:
2904#if TODO_DECOMPILER_EXTENSIONS 2897 if (this.ExtensionsByTableName.TryGetValue(table.Name, out var extension))
2905 if (this.ExtensionsByTableName.TryGetValue(table.Name, out var extension) 2898 {
2906 { 2899 extension.TryDecompileTable(table);
2907 extension.DecompileTable(table); 2900 }
2908 } 2901 else if (!this.SuppressCustomTables)
2909 else
2910#endif
2911 if (!this.SuppressCustomTables)
2912 { 2902 {
2913 this.DecompileCustomTable(table); 2903 this.DecompileCustomTable(table);
2914 } 2904 }
@@ -3039,7 +3029,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
3039 break; 3029 break;
3040 case 3: 3030 case 3:
3041 { 3031 {
3042 var productName = this.RootElement.Attribute("Name")?.Value; 3032 var productName = this.DecompilerHelper.RootElement.Attribute("Name")?.Value;
3043 if (value != productName) 3033 if (value != productName)
3044 { 3034 {
3045 xSummaryInformation.SetAttributeValue("Description", value); 3035 xSummaryInformation.SetAttributeValue("Description", value);
@@ -3048,7 +3038,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
3048 } 3038 }
3049 case 4: 3039 case 4:
3050 { 3040 {
3051 var productManufacturer = this.RootElement.Attribute("Manufacturer")?.Value; 3041 var productManufacturer = this.DecompilerHelper.RootElement.Attribute("Manufacturer")?.Value;
3052 if (value != productManufacturer) 3042 if (value != productManufacturer)
3053 { 3043 {
3054 xSummaryInformation.SetAttributeValue("Manufacturer", value); 3044 xSummaryInformation.SetAttributeValue("Manufacturer", value);
@@ -3065,7 +3055,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
3065 var template = value.Split(';'); 3055 var template = value.Split(';');
3066 if (0 < template.Length && 0 < template[template.Length - 1].Length) 3056 if (0 < template.Length && 0 < template[template.Length - 1].Length)
3067 { 3057 {
3068 this.RootElement.SetAttributeValue("Language", template[template.Length - 1]); 3058 this.DecompilerHelper.RootElement.SetAttributeValue("Language", template[template.Length - 1]);
3069 } 3059 }
3070 break; 3060 break;
3071 case 14: 3061 case 14:
@@ -3073,7 +3063,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
3073 // Default InstallerVersion. 3063 // Default InstallerVersion.
3074 if (installerVersion != 500) 3064 if (installerVersion != 500)
3075 { 3065 {
3076 this.RootElement.SetAttributeValue("InstallerVersion", installerVersion); 3066 this.DecompilerHelper.RootElement.SetAttributeValue("InstallerVersion", installerVersion);
3077 } 3067 }
3078 break; 3068 break;
3079 case 15: 3069 case 15:
@@ -3083,29 +3073,24 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
3083 this.ShortNames = true; 3073 this.ShortNames = true;
3084 if (OutputType.Product == this.OutputType) 3074 if (OutputType.Product == this.OutputType)
3085 { 3075 {
3086 this.RootElement.SetAttributeValue("ShortNames", "yes"); 3076 this.DecompilerHelper.RootElement.SetAttributeValue("ShortNames", "yes");
3087 } 3077 }
3088 } 3078 }
3089 3079
3090 if (0x2 == (wordCount & 0x2)) 3080 if (0x2 == (wordCount & 0x2))
3091 { 3081 {
3092 this.Compressed = true; 3082 this.Compressed = true;
3093
3094 if (OutputType.Product == this.OutputType)
3095 {
3096 this.RootElement.SetAttributeValue("Compressed", "yes");
3097 }
3098 } 3083 }
3099 3084
3100 if (OutputType.Product == this.OutputType) 3085 if (OutputType.Product == this.OutputType)
3101 { 3086 {
3102 if (0x8 == (wordCount & 0x8)) 3087 if (0x8 == (wordCount & 0x8))
3103 { 3088 {
3104 this.RootElement.SetAttributeValue("Scope", "perUser"); 3089 this.DecompilerHelper.RootElement.SetAttributeValue("Scope", "perUser");
3105 } 3090 }
3106 else 3091 else
3107 { 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");
3109 if (xAllUsers?.Attribute("Value")?.Value == "1") 3094 if (xAllUsers?.Attribute("Value")?.Value == "1")
3110 { 3095 {
3111 xAllUsers?.Remove(); 3096 xAllUsers?.Remove();
@@ -3118,9 +3103,14 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
3118 } 3103 }
3119 } 3104 }
3120 3105
3106 if (OutputType.Product == this.OutputType && !this.Compressed)
3107 {
3108 this.DecompilerHelper.RootElement.SetAttributeValue("Compressed", "no");
3109 }
3110
3121 if (xSummaryInformation.HasAttributes) 3111 if (xSummaryInformation.HasAttributes)
3122 { 3112 {
3123 this.RootElement.Add(xSummaryInformation); 3113 this.DecompilerHelper.AddElementToRoot(xSummaryInformation);
3124 } 3114 }
3125 } 3115 }
3126 else 3116 else
@@ -3173,7 +3163,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
3173 } 3163 }
3174 } 3164 }
3175 3165
3176 this.RootElement.Add(xPatchInformation); 3166 this.DecompilerHelper.AddElementToRoot(xPatchInformation);
3177 } 3167 }
3178 } 3168 }
3179 3169
@@ -3212,8 +3202,8 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
3212 row.IsColumnNull(5) || row.FieldAsInteger(5) != 1 ? null : new XAttribute("ActivateAtStorage", "yes"), 3202 row.IsColumnNull(5) || row.FieldAsInteger(5) != 1 ? null : new XAttribute("ActivateAtStorage", "yes"),
3213 row.IsColumnNull(6) || row.FieldAsInteger(6) != 1 ? null : new XAttribute("RunAsInteractiveUser", "yes")); 3203 row.IsColumnNull(6) || row.FieldAsInteger(6) != 1 ? null : new XAttribute("RunAsInteractiveUser", "yes"));
3214 3204
3215 this.RootElement.Add(appId); 3205 this.DecompilerHelper.AddElementToRoot(appId);
3216 this.IndexElement(row, appId); 3206 this.DecompilerHelper.IndexElement(row, appId);
3217 } 3207 }
3218 } 3208 }
3219 3209
@@ -3239,7 +3229,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
3239 SetControlAttributes(bbControlRow.Attributes, xControl); 3229 SetControlAttributes(bbControlRow.Attributes, xControl);
3240 } 3230 }
3241 3231
3242 if (this.TryGetIndexedElement("Billboard", out var xBillboard, bbControlRow.Billboard)) 3232 if (this.DecompilerHelper.TryGetIndexedElement("Billboard", bbControlRow.Billboard, out var xBillboard))
3243 { 3233 {
3244 xBillboard.Add(xControl); 3234 xBillboard.Add(xControl);
3245 } 3235 }
@@ -3264,7 +3254,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
3264 new XAttribute("Id", row.FieldAsString(0)), 3254 new XAttribute("Id", row.FieldAsString(0)),
3265 new XAttribute("Feature", row.FieldAsString(1))); 3255 new XAttribute("Feature", row.FieldAsString(1)));
3266 3256
3267 this.IndexElement(row, xBillboard); 3257 this.DecompilerHelper.IndexElement(row, xBillboard);
3268 billboards.Add(String.Format(CultureInfo.InvariantCulture, "{0}|{1:0000000000}", row[0], row[3]), row); 3258 billboards.Add(String.Format(CultureInfo.InvariantCulture, "{0}|{1:0000000000}", row[0], row[3]), row);
3269 } 3259 }
3270 3260
@@ -3272,7 +3262,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
3272 3262
3273 foreach (var row in billboards.Values) 3263 foreach (var row in billboards.Values)
3274 { 3264 {
3275 var xBillboard = this.GetIndexedElement(row); 3265 var xBillboard = this.DecompilerHelper.GetIndexedElement(row);
3276 3266
3277 if (!billboardActions.TryGetValue(row.FieldAsString(2), out var xBillboardAction)) 3267 if (!billboardActions.TryGetValue(row.FieldAsString(2), out var xBillboardAction))
3278 { 3268 {
@@ -3299,7 +3289,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
3299 new XAttribute("Id", row.FieldAsString(0)), 3289 new XAttribute("Id", row.FieldAsString(0)),
3300 new XAttribute("SourceFile", row.FieldAsString(1))); 3290 new XAttribute("SourceFile", row.FieldAsString(1)));
3301 3291
3302 this.RootElement.Add(xBinary); 3292 this.DecompilerHelper.AddElementToRoot(xBinary);
3303 } 3293 }
3304 } 3294 }
3305 3295
@@ -3311,7 +3301,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
3311 { 3301 {
3312 foreach (var row in table.Rows) 3302 foreach (var row in table.Rows)
3313 { 3303 {
3314 if (this.TryGetIndexedElement("File", out var xFile, row.FieldAsString(0))) 3304 if (this.DecompilerHelper.TryGetIndexedElement("File", row.FieldAsString(0), out var xFile))
3315 { 3305 {
3316 xFile.SetAttributeValue("BindPath", row.FieldAsString(1)); 3306 xFile.SetAttributeValue("BindPath", row.FieldAsString(1));
3317 } 3307 }
@@ -3389,7 +3379,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
3389 } 3379 }
3390 3380
3391 this.AddChildToParent("Component", xClass, row, 2); 3381 this.AddChildToParent("Component", xClass, row, 2);
3392 this.IndexElement(row, xClass); 3382 this.DecompilerHelper.IndexElement(row, xClass);
3393 } 3383 }
3394 } 3384 }
3395 3385
@@ -3656,7 +3646,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
3656 } 3646 }
3657 } 3647 }
3658 3648
3659 this.IndexElement(controlRow, xControl); 3649 this.DecompilerHelper.IndexElement(controlRow, xControl);
3660 } 3650 }
3661 } 3651 }
3662 3652
@@ -3668,7 +3658,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
3668 { 3658 {
3669 foreach (var row in table.Rows) 3659 foreach (var row in table.Rows)
3670 { 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))
3672 { 3662 {
3673 switch (row.FieldAsString(2)) 3663 switch (row.FieldAsString(2))
3674 { 3664 {
@@ -3730,14 +3720,14 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
3730 3720
3731 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); 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);
3732 3722
3733 this.IndexElement(row, xPublish); 3723 this.DecompilerHelper.IndexElement(row, xPublish);
3734 } 3724 }
3735 3725
3736 foreach (var row in controlEvents.Values) 3726 foreach (var row in controlEvents.Values)
3737 { 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))
3739 { 3729 {
3740 var xPublish = this.GetIndexedElement(row); 3730 var xPublish = this.DecompilerHelper.GetIndexedElement(row);
3741 xControl.Add(xPublish); 3731 xControl.Add(xPublish);
3742 } 3732 }
3743 else 3733 else
@@ -3928,7 +3918,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
3928 xCustomTable.Add(xRow); 3918 xCustomTable.Add(xRow);
3929 } 3919 }
3930 3920
3931 this.RootElement.Add(xCustomTable); 3921 this.DecompilerHelper.AddElementToRoot(xCustomTable);
3932 } 3922 }
3933 } 3923 }
3934 3924
@@ -3944,7 +3934,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
3944 new XAttribute("Directory", row.FieldAsString(0))); 3934 new XAttribute("Directory", row.FieldAsString(0)));
3945 3935
3946 this.AddChildToParent("Component", xCreateFolder, row, 1); 3936 this.AddChildToParent("Component", xCreateFolder, row, 1);
3947 this.IndexElement(row, xCreateFolder); 3937 this.DecompilerHelper.IndexElement(row, xCreateFolder);
3948 } 3938 }
3949 } 3939 }
3950 3940
@@ -4115,8 +4105,8 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
4115 xCustomAction.SetAttributeValue("PatchUninstall", "yes"); 4105 xCustomAction.SetAttributeValue("PatchUninstall", "yes");
4116 } 4106 }
4117 4107
4118 this.RootElement.Add(xCustomAction); 4108 this.DecompilerHelper.AddElementToRoot(xCustomAction);
4119 this.IndexElement(row, xCustomAction); 4109 this.DecompilerHelper.IndexElement(row, xCustomAction);
4120 } 4110 }
4121 } 4111 }
4122 4112
@@ -4148,7 +4138,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
4148 } 4138 }
4149 } 4139 }
4150 4140
4151 this.IndexElement(row, xComponentSearch); 4141 this.DecompilerHelper.IndexElement(row, xComponentSearch);
4152 } 4142 }
4153 } 4143 }
4154 4144
@@ -4162,7 +4152,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
4162 { 4152 {
4163 if (!row.IsColumnNull(1)) 4153 if (!row.IsColumnNull(1))
4164 { 4154 {
4165 if (this.TryGetIndexedElement("Component", out var xComponent, row.FieldAsString(0))) 4155 if (this.DecompilerHelper.TryGetIndexedElement("Component", row.FieldAsString(0), out var xComponent))
4166 { 4156 {
4167 xComponent.SetAttributeValue("ComPlusFlags", row.FieldAsInteger(1)); 4157 xComponent.SetAttributeValue("ComPlusFlags", row.FieldAsInteger(1));
4168 } 4158 }
@@ -4247,7 +4237,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
4247 } 4237 }
4248 4238
4249 this.AddChildToParent("Directory", xComponent, row, 2); 4239 this.AddChildToParent("Directory", xComponent, row, 2);
4250 this.IndexElement(row, xComponent); 4240 this.DecompilerHelper.IndexElement(row, xComponent);
4251 } 4241 }
4252 } 4242 }
4253 4243
@@ -4259,7 +4249,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
4259 { 4249 {
4260 foreach (var row in table.Rows) 4250 foreach (var row in table.Rows)
4261 { 4251 {
4262 if (this.TryGetIndexedElement("Feature", out var xFeature, row.FieldAsString(0))) 4252 if (this.DecompilerHelper.TryGetIndexedElement("Feature", row.FieldAsString(0), out var xFeature))
4263 { 4253 {
4264 var xLevel = new XElement(Names.LevelElement, 4254 var xLevel = new XElement(Names.LevelElement,
4265 row.IsColumnNull(2) ? null : new XAttribute("Condition", row.FieldAsString(2)), 4255 row.IsColumnNull(2) ? null : new XAttribute("Condition", row.FieldAsString(2)),
@@ -4303,7 +4293,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
4303 !row.IsColumnNull(6) ? new XAttribute("Title", row.FieldAsString(6)) : null); 4293 !row.IsColumnNull(6) ? new XAttribute("Title", row.FieldAsString(6)) : null);
4304 4294
4305 this.UIElement.Add(xDialog); 4295 this.UIElement.Add(xDialog);
4306 this.IndexElement(row, xDialog); 4296 this.DecompilerHelper.IndexElement(row, xDialog);
4307 } 4297 }
4308 } 4298 }
4309 4299
@@ -4367,13 +4357,13 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
4367 } 4357 }
4368 } 4358 }
4369 4359
4370 this.IndexElement(row, xDirectory); 4360 this.DecompilerHelper.IndexElement(row, xDirectory);
4371 } 4361 }
4372 4362
4373 // nest the directories 4363 // nest the directories
4374 foreach (var row in table.Rows) 4364 foreach (var row in table.Rows)
4375 { 4365 {
4376 var xDirectory = this.GetIndexedElement(row); 4366 var xDirectory = this.DecompilerHelper.GetIndexedElement(row);
4377 4367
4378 var id = row.FieldAsString(0); 4368 var id = row.FieldAsString(0);
4379 4369
@@ -4383,26 +4373,26 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
4383 } 4373 }
4384 else if (row.IsColumnNull(1) || WindowsInstallerStandard.IsStandardDirectory(id)) 4374 else if (row.IsColumnNull(1) || WindowsInstallerStandard.IsStandardDirectory(id))
4385 { 4375 {
4386 this.RootElement.Add(xDirectory); 4376 this.DecompilerHelper.AddElementToRoot(xDirectory);
4387 } 4377 }
4388 else 4378 else
4389 { 4379 {
4390 var parentDirectoryId = row.FieldAsString(1); 4380 var parentDirectoryId = row.FieldAsString(1);
4391 4381
4392 if (!this.TryGetIndexedElement("Directory", out var xParentDirectory, parentDirectoryId)) 4382 if (!this.DecompilerHelper.TryGetIndexedElement("Directory", parentDirectoryId, out var xParentDirectory))
4393 { 4383 {
4394 this.Messaging.Write(WarningMessages.ExpectedForeignRow(row.SourceLineNumbers, table.Name, row.GetPrimaryKey(DecompilerConstants.PrimaryKeyDelimiter), "Directory_Parent", row.FieldAsString(1), "Directory")); 4384 this.Messaging.Write(WarningMessages.ExpectedForeignRow(row.SourceLineNumbers, table.Name, row.GetPrimaryKey(DecompilerConstants.PrimaryKeyDelimiter), "Directory_Parent", row.FieldAsString(1), "Directory"));
4395 } 4385 }
4396 else if (xParentDirectory == xDirectory) // another way to specify a root directory 4386 else if (xParentDirectory == xDirectory) // another way to specify a root directory
4397 { 4387 {
4398 this.RootElement.Add(xDirectory); 4388 this.DecompilerHelper.AddElementToRoot(xDirectory);
4399 } 4389 }
4400 else 4390 else
4401 { 4391 {
4402 // TARGETDIR is omitted but if this directory is a first-generation descendant, add it as a root. 4392 // TARGETDIR is omitted but if this directory is a first-generation descendant, add it as a root.
4403 if (parentDirectoryId == "TARGETDIR") 4393 if (parentDirectoryId == "TARGETDIR")
4404 { 4394 {
4405 this.RootElement.Add(xDirectory); 4395 this.DecompilerHelper.AddElementToRoot(xDirectory);
4406 } 4396 }
4407 else 4397 else
4408 { 4398 {
@@ -4426,7 +4416,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
4426 XAttributeIfNotNull("Path", row, 2), 4416 XAttributeIfNotNull("Path", row, 2),
4427 XAttributeIfNotNull("Depth", row, 3)); 4417 XAttributeIfNotNull("Depth", row, 3));
4428 4418
4429 this.IndexElement(row, xDirectorySearch); 4419 this.DecompilerHelper.IndexElement(row, xDirectorySearch);
4430 } 4420 }
4431 } 4421 }
4432 4422
@@ -4459,7 +4449,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
4459 // destination directory/property is set in FinalizeDuplicateMoveFileTables 4449 // destination directory/property is set in FinalizeDuplicateMoveFileTables
4460 4450
4461 this.AddChildToParent("Component", xCopyFile, row, 1); 4451 this.AddChildToParent("Component", xCopyFile, row, 1);
4462 this.IndexElement(row, xCopyFile); 4452 this.DecompilerHelper.IndexElement(row, xCopyFile);
4463 } 4453 }
4464 } 4454 }
4465 4455
@@ -4570,7 +4560,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
4570 new XAttribute("Event", row.FieldAsString(2)), 4560 new XAttribute("Event", row.FieldAsString(2)),
4571 new XAttribute("Attribute", row.FieldAsString(3))); 4561 new XAttribute("Attribute", row.FieldAsString(3)));
4572 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))
4574 { 4564 {
4575 xControl.Add(xSubscribe); 4565 xControl.Add(xSubscribe);
4576 } 4566 }
@@ -4595,7 +4585,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
4595 4585
4596 if (!row.IsColumnNull(3)) 4586 if (!row.IsColumnNull(3))
4597 { 4587 {
4598 if (this.TryGetIndexedElement("MIME", out var xMime, row.FieldAsString(3))) 4588 if (this.DecompilerHelper.TryGetIndexedElement("MIME", row.FieldAsString(3), out var xMime))
4599 { 4589 {
4600 xMime.SetAttributeValue("Default", "yes"); 4590 xMime.SetAttributeValue("Default", "yes");
4601 } 4591 }
@@ -4614,7 +4604,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
4614 this.AddChildToParent("Component", xExtension, row, 1); 4604 this.AddChildToParent("Component", xExtension, row, 1);
4615 } 4605 }
4616 4606
4617 this.IndexElement(row, xExtension); 4607 this.DecompilerHelper.IndexElement(row, xExtension);
4618 } 4608 }
4619 } 4609 }
4620 4610
@@ -4682,7 +4672,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
4682 } 4672 }
4683 4673
4684 this.AddChildToParent("ImageFamilies", xExternalFile, row, 0); 4674 this.AddChildToParent("ImageFamilies", xExternalFile, row, 0);
4685 this.IndexElement(row, xExternalFile); 4675 this.DecompilerHelper.IndexElement(row, xExternalFile);
4686 } 4676 }
4687 } 4677 }
4688 4678
@@ -4761,7 +4751,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
4761 feature.SetAttributeValue("Absent", "disallow"); 4751 feature.SetAttributeValue("Absent", "disallow");
4762 } 4752 }
4763 4753
4764 this.IndexElement(row, feature); 4754 this.DecompilerHelper.IndexElement(row, feature);
4765 4755
4766 // sort the features by their display column (and append the identifier to ensure unique keys) 4756 // sort the features by their display column (and append the identifier to ensure unique keys)
4767 sortedFeatures.Add(String.Format(CultureInfo.InvariantCulture, "{0:00000}|{1}", row.FieldAsInteger(4), row[0]), row); 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
4770 // nest the features 4760 // nest the features
4771 foreach (var row in sortedFeatures.Values) 4761 foreach (var row in sortedFeatures.Values)
4772 { 4762 {
4773 var xFeature = this.GetIndexedElement("Feature", row.FieldAsString(0)); 4763 var xFeature = this.DecompilerHelper.GetIndexedElement("Feature", row.FieldAsString(0));
4774 4764
4775 if (row.IsColumnNull(1)) 4765 if (row.IsColumnNull(1))
4776 { 4766 {
4777 this.RootElement.Add(xFeature); 4767 this.DecompilerHelper.AddElementToRoot(xFeature);
4778 } 4768 }
4779 else 4769 else
4780 { 4770 {
4781 if (this.TryGetIndexedElement("Feature", out var xParentFeature, row.FieldAsString(1))) 4771 if (this.DecompilerHelper.TryGetIndexedElement("Feature", row.FieldAsString(1), out var xParentFeature))
4782 { 4772 {
4783 if (xParentFeature == xFeature) 4773 if (xParentFeature == xFeature)
4784 { 4774 {
@@ -4809,7 +4799,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
4809 new XAttribute("Id", row.FieldAsString(1))); 4799 new XAttribute("Id", row.FieldAsString(1)));
4810 4800
4811 this.AddChildToParent("Feature", xComponentRef, row, 0); 4801 this.AddChildToParent("Feature", xComponentRef, row, 0);
4812 this.IndexElement(row, xComponentRef); 4802 this.DecompilerHelper.IndexElement(row, xComponentRef);
4813 } 4803 }
4814 } 4804 }
4815 4805
@@ -4855,7 +4845,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
4855 xFile.SetAttributeValue("Compressed", "yes"); 4845 xFile.SetAttributeValue("Compressed", "yes");
4856 } 4846 }
4857 4847
4858 this.IndexElement(fileRow, xFile); 4848 this.DecompilerHelper.IndexElement(fileRow, xFile);
4859 } 4849 }
4860 } 4850 }
4861 4851
@@ -4882,7 +4872,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
4882 { 4872 {
4883 foreach (var row in table.Rows) 4873 foreach (var row in table.Rows)
4884 { 4874 {
4885 if (this.TryGetIndexedElement("File", out var xFile, row.FieldAsString(0))) 4875 if (this.DecompilerHelper.TryGetIndexedElement("File", row.FieldAsString(0), out var xFile))
4886 { 4876 {
4887 if (!row.IsColumnNull(1)) 4877 if (!row.IsColumnNull(1))
4888 { 4878 {
@@ -4912,7 +4902,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
4912 new XAttribute("Id", row.FieldAsString(0)), 4902 new XAttribute("Id", row.FieldAsString(0)),
4913 new XAttribute("SourceFile", row.FieldAsString(1))); 4903 new XAttribute("SourceFile", row.FieldAsString(1)));
4914 4904
4915 this.RootElement.Add(icon); 4905 this.DecompilerHelper.AddElementToRoot(icon);
4916 } 4906 }
4917 } 4907 }
4918 4908
@@ -4932,8 +4922,8 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
4932 row.IsColumnNull(4) ? null : new XAttribute("DiskPrompt", row.FieldAsString(4)), 4922 row.IsColumnNull(4) ? null : new XAttribute("DiskPrompt", row.FieldAsString(4)),
4933 row.IsColumnNull(5) ? null : new XAttribute("VolumeLabel", row.FieldAsString(5))); 4923 row.IsColumnNull(5) ? null : new XAttribute("VolumeLabel", row.FieldAsString(5)));
4934 4924
4935 this.RootElement.Add(family); 4925 this.DecompilerHelper.AddElementToRoot(family);
4936 this.IndexElement(row, family); 4926 this.DecompilerHelper.IndexElement(row, family);
4937 } 4927 }
4938 } 4928 }
4939 4929
@@ -5035,7 +5025,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
5035 } 5025 }
5036 } 5026 }
5037 5027
5038 this.IndexElement(row, xIniFileSearch); 5028 this.DecompilerHelper.IndexElement(row, xIniFileSearch);
5039 } 5029 }
5040 } 5030 }
5041 5031
@@ -5071,7 +5061,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
5071 new XAttribute("Condition", row.FieldAsString(0)), 5061 new XAttribute("Condition", row.FieldAsString(0)),
5072 new XAttribute("Message", row.FieldAsString(1))); 5062 new XAttribute("Message", row.FieldAsString(1)));
5073 5063
5074 this.RootElement.Add(condition); 5064 this.DecompilerHelper.AddElementToRoot(condition);
5075 } 5065 }
5076 } 5066 }
5077 5067
@@ -5230,7 +5220,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
5230 } 5220 }
5231 } 5221 }
5232 5222
5233 this.IndexElement(row, xPermission); 5223 this.DecompilerHelper.IndexElement(row, xPermission);
5234 } 5224 }
5235 } 5225 }
5236 5226
@@ -5260,8 +5250,8 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
5260 xMedia.SetAttributeValue("Cabinet", cabinet); 5250 xMedia.SetAttributeValue("Cabinet", cabinet);
5261 } 5251 }
5262 5252
5263 this.RootElement.Add(xMedia); 5253 this.DecompilerHelper.AddElementToRoot(xMedia);
5264 this.IndexElement(mediaRow, xMedia); 5254 this.DecompilerHelper.IndexElement(mediaRow, xMedia);
5265 } 5255 }
5266 } 5256 }
5267 5257
@@ -5277,7 +5267,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
5277 new XAttribute("ContentType", row.FieldAsString(0)), 5267 new XAttribute("ContentType", row.FieldAsString(0)),
5278 row.IsColumnNull(2) ? null : new XAttribute("Class", row.FieldAsString(2))); 5268 row.IsColumnNull(2) ? null : new XAttribute("Class", row.FieldAsString(2)));
5279 5269
5280 this.IndexElement(row, mime); 5270 this.DecompilerHelper.IndexElement(row, mime);
5281 } 5271 }
5282 } 5272 }
5283 5273
@@ -5338,7 +5328,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
5338 } 5328 }
5339 } 5329 }
5340 5330
5341 this.RootElement.Add(configuration); 5331 this.DecompilerHelper.AddElementToRoot(configuration);
5342 } 5332 }
5343 } 5333 }
5344 5334
@@ -5355,7 +5345,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
5355 new XAttribute("RequiredLanguage", row.FieldAsString(3)), 5345 new XAttribute("RequiredLanguage", row.FieldAsString(3)),
5356 XAttributeIfNotNull("RequiredVersion", row, 4)); 5346 XAttributeIfNotNull("RequiredVersion", row, 4));
5357 5347
5358 this.RootElement.Add(xDependency); 5348 this.DecompilerHelper.AddElementToRoot(xDependency);
5359 } 5349 }
5360 } 5350 }
5361 5351
@@ -5382,7 +5372,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
5382 xExclusion.SetAttributeValue("ExcludeExceptLanguage", -excludedLanguage); 5372 xExclusion.SetAttributeValue("ExcludeExceptLanguage", -excludedLanguage);
5383 } 5373 }
5384 5374
5385 this.RootElement.Add(xExclusion); 5375 this.DecompilerHelper.AddElementToRoot(xExclusion);
5386 } 5376 }
5387 } 5377 }
5388 5378
@@ -5402,7 +5392,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
5402 var xIgnoreTable = new XElement(Names.IgnoreTableElement, 5392 var xIgnoreTable = new XElement(Names.IgnoreTableElement,
5403 new XAttribute("Id", tableName)); 5393 new XAttribute("Id", tableName));
5404 5394
5405 this.RootElement.Add(xIgnoreTable); 5395 this.DecompilerHelper.AddElementToRoot(xIgnoreTable);
5406 } 5396 }
5407 } 5397 }
5408 } 5398 }
@@ -5417,10 +5407,10 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
5417 { 5407 {
5418 var row = table.Rows[0]; 5408 var row = table.Rows[0];
5419 5409
5420 this.RootElement.SetAttributeValue("Id", row.FieldAsString(0)); 5410 this.DecompilerHelper.RootElement.SetAttributeValue("Id", row.FieldAsString(0));
5421 // support Language columns that are treated as integers as well as strings (the WiX default, to support localizability) 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)); 5412 this.DecompilerHelper.RootElement.SetAttributeValue("Language", row.FieldAsString(1));
5423 this.RootElement.SetAttributeValue("Version", row.FieldAsString(2)); 5413 this.DecompilerHelper.RootElement.SetAttributeValue("Version", row.FieldAsString(2));
5424 } 5414 }
5425 else 5415 else
5426 { 5416 {
@@ -5442,7 +5432,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
5442 new XAttribute("Column", row.FieldAsString(2)), 5432 new XAttribute("Column", row.FieldAsString(2)),
5443 XAttributeIfNotNull("Value", row, 3)); 5433 XAttributeIfNotNull("Value", row, 3));
5444 5434
5445 this.RootElement.Add(xSubstitution); 5435 this.DecompilerHelper.AddElementToRoot(xSubstitution);
5446 } 5436 }
5447 } 5437 }
5448 5438
@@ -5487,7 +5477,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
5487 } 5477 }
5488 5478
5489 this.AddChildToParent("Component", xCopyFile, row, 1); 5479 this.AddChildToParent("Component", xCopyFile, row, 1);
5490 this.IndexElement(row, xCopyFile); 5480 this.DecompilerHelper.IndexElement(row, xCopyFile);
5491 } 5481 }
5492 } 5482 }
5493 5483
@@ -5503,7 +5493,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
5503 new XAttribute("Id", row.FieldAsString(0)), 5493 new XAttribute("Id", row.FieldAsString(0)),
5504 new XAttribute("SourceFile", row.FieldAsString(1))); 5494 new XAttribute("SourceFile", row.FieldAsString(1)));
5505 5495
5506 this.IndexElement(row, xDigitalCertificate); 5496 this.DecompilerHelper.IndexElement(row, xDigitalCertificate);
5507 } 5497 }
5508 } 5498 }
5509 5499
@@ -5520,7 +5510,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
5520 5510
5521 this.AddChildToParent("MsiDigitalCertificate", xDigitalSignature, row, 2); 5511 this.AddChildToParent("MsiDigitalCertificate", xDigitalSignature, row, 2);
5522 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))
5524 { 5514 {
5525 xParentElement.Add(xDigitalSignature); 5515 xParentElement.Add(xDigitalSignature);
5526 } 5516 }
@@ -5560,7 +5550,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
5560 break; 5550 break;
5561 } 5551 }
5562 5552
5563 this.RootElement.Add(xEmbeddedChainer); 5553 this.DecompilerHelper.AddElementToRoot(xEmbeddedChainer);
5564 } 5554 }
5565 } 5555 }
5566 5556
@@ -5735,7 +5725,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
5735 return; 5725 return;
5736 } 5726 }
5737 5727
5738 this.IndexElement(row, xPermissionEx); 5728 this.DecompilerHelper.IndexElement(row, xPermissionEx);
5739 } 5729 }
5740 } 5730 }
5741 5731
@@ -5748,7 +5738,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
5748 if (0 < table.Rows.Count) 5738 if (0 < table.Rows.Count)
5749 { 5739 {
5750 var xPackageCertificates = new XElement(Names.PatchCertificatesElement); 5740 var xPackageCertificates = new XElement(Names.PatchCertificatesElement);
5751 this.RootElement.Add(xPackageCertificates); 5741 this.DecompilerHelper.AddElementToRoot(xPackageCertificates);
5752 this.AddCertificates(table, xPackageCertificates); 5742 this.AddCertificates(table, xPackageCertificates);
5753 } 5743 }
5754 } 5744 }
@@ -5762,7 +5752,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
5762 if (0 < table.Rows.Count) 5752 if (0 < table.Rows.Count)
5763 { 5753 {
5764 var xPatchCertificates = new XElement(Names.PatchCertificatesElement); 5754 var xPatchCertificates = new XElement(Names.PatchCertificatesElement);
5765 this.RootElement.Add(xPatchCertificates); 5755 this.DecompilerHelper.AddElementToRoot(xPatchCertificates);
5766 this.AddCertificates(table, xPatchCertificates); 5756 this.AddCertificates(table, xPatchCertificates);
5767 } 5757 }
5768 } 5758 }
@@ -5776,7 +5766,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
5776 { 5766 {
5777 foreach (var row in table.Rows) 5767 foreach (var row in table.Rows)
5778 { 5768 {
5779 if (this.TryGetIndexedElement("MsiDigitalCertificate", out var xDigitalCertificate, row.FieldAsString(1))) 5769 if (this.DecompilerHelper.TryGetIndexedElement("MsiDigitalCertificate", row.FieldAsString(1), out var xDigitalCertificate))
5780 { 5770 {
5781 parent.Add(xDigitalCertificate); 5771 parent.Add(xDigitalCertificate);
5782 } 5772 }
@@ -5846,7 +5836,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
5846 break; 5836 break;
5847 } 5837 }
5848 5838
5849 this.IndexElement(row, xOdbcDataSource); 5839 this.DecompilerHelper.IndexElement(row, xOdbcDataSource);
5850 } 5840 }
5851 } 5841 }
5852 5842
@@ -5865,7 +5855,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
5865 XAttributeIfNotNull("SetupFile", row, 4)); 5855 XAttributeIfNotNull("SetupFile", row, 4));
5866 5856
5867 this.AddChildToParent("Component", xOdbcDriver, row, 1); 5857 this.AddChildToParent("Component", xOdbcDriver, row, 1);
5868 this.IndexElement(row, xOdbcDriver); 5858 this.DecompilerHelper.IndexElement(row, xOdbcDriver);
5869 } 5859 }
5870 } 5860 }
5871 5861
@@ -6010,7 +6000,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
6010 } 6000 }
6011 } 6001 }
6012 6002
6013 this.RootElement.Add(xPatchMetadata); 6003 this.DecompilerHelper.AddElementToRoot(xPatchMetadata);
6014 } 6004 }
6015 } 6005 }
6016 6006
@@ -6049,7 +6039,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
6049 patchSequence.SetAttributeValue("Supersede", "yes"); 6039 patchSequence.SetAttributeValue("Supersede", "yes");
6050 } 6040 }
6051 6041
6052 this.RootElement.Add(patchSequence); 6042 this.DecompilerHelper.AddElementToRoot(patchSequence);
6053 } 6043 }
6054 } 6044 }
6055 6045
@@ -6068,13 +6058,13 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
6068 XAttributeIfNotNull("Icon", row, 4), 6058 XAttributeIfNotNull("Icon", row, 4),
6069 XAttributeIfNotNull("IconIndex", row, 5)); 6059 XAttributeIfNotNull("IconIndex", row, 5));
6070 6060
6071 this.IndexElement(row, xProgId); 6061 this.DecompilerHelper.IndexElement(row, xProgId);
6072 } 6062 }
6073 6063
6074 // nest the ProgIds 6064 // nest the ProgIds
6075 foreach (var row in table.Rows) 6065 foreach (var row in table.Rows)
6076 { 6066 {
6077 var xProgId = this.GetIndexedElement(row); 6067 var xProgId = this.DecompilerHelper.GetIndexedElement(row);
6078 6068
6079 if (!row.IsColumnNull(1)) 6069 if (!row.IsColumnNull(1))
6080 { 6070 {
@@ -6107,13 +6097,13 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
6107 case "AllowProductCodeMismatches": 6097 case "AllowProductCodeMismatches":
6108 if ("1" == value) 6098 if ("1" == value)
6109 { 6099 {
6110 this.RootElement.SetAttributeValue("AllowProductCodeMismatches", "yes"); 6100 this.DecompilerHelper.RootElement.SetAttributeValue("AllowProductCodeMismatches", "yes");
6111 } 6101 }
6112 break; 6102 break;
6113 case "AllowProductVersionMajorMismatches": 6103 case "AllowProductVersionMajorMismatches":
6114 if ("1" == value) 6104 if ("1" == value)
6115 { 6105 {
6116 this.RootElement.SetAttributeValue("AllowMajorVersionMismatches", "yes"); 6106 this.DecompilerHelper.RootElement.SetAttributeValue("AllowMajorVersionMismatches", "yes");
6117 } 6107 }
6118 break; 6108 break;
6119 case "ApiPatchingSymbolFlags": 6109 case "ApiPatchingSymbolFlags":
@@ -6127,7 +6117,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
6127 value = value.Substring(2); 6117 value = value.Substring(2);
6128 } 6118 }
6129 6119
6130 this.RootElement.SetAttributeValue("SymbolFlags", Convert.ToInt32(value, 16)); 6120 this.DecompilerHelper.RootElement.SetAttributeValue("SymbolFlags", Convert.ToInt32(value, 16));
6131 } 6121 }
6132 catch 6122 catch
6133 { 6123 {
@@ -6138,13 +6128,13 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
6138 case "DontRemoveTempFolderWhenFinished": 6128 case "DontRemoveTempFolderWhenFinished":
6139 if ("1" == value) 6129 if ("1" == value)
6140 { 6130 {
6141 this.RootElement.SetAttributeValue("CleanWorkingFolder", "no"); 6131 this.DecompilerHelper.RootElement.SetAttributeValue("CleanWorkingFolder", "no");
6142 } 6132 }
6143 break; 6133 break;
6144 case "IncludeWholeFilesOnly": 6134 case "IncludeWholeFilesOnly":
6145 if ("1" == value) 6135 if ("1" == value)
6146 { 6136 {
6147 this.RootElement.SetAttributeValue("WholeFilesOnly", "yes"); 6137 this.DecompilerHelper.RootElement.SetAttributeValue("WholeFilesOnly", "yes");
6148 } 6138 }
6149 break; 6139 break;
6150 case "ListOfPatchGUIDsToReplace": 6140 case "ListOfPatchGUIDsToReplace":
@@ -6158,7 +6148,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
6158 var xReplacePatch = new XElement(Names.ReplacePatchElement, 6148 var xReplacePatch = new XElement(Names.ReplacePatchElement,
6159 new XAttribute("Id", guidMatch.Value)); 6149 new XAttribute("Id", guidMatch.Value));
6160 6150
6161 this.RootElement.Add(xReplacePatch); 6151 this.DecompilerHelper.AddElementToRoot(xReplacePatch);
6162 } 6152 }
6163 } 6153 }
6164 break; 6154 break;
@@ -6172,25 +6162,25 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
6172 var xTargetProductCode = new XElement(Names.TargetProductCodeElement, 6162 var xTargetProductCode = new XElement(Names.TargetProductCodeElement,
6173 new XAttribute("Id", targetProductCodeString)); 6163 new XAttribute("Id", targetProductCodeString));
6174 6164
6175 this.RootElement.Add(xTargetProductCode); 6165 this.DecompilerHelper.AddElementToRoot(xTargetProductCode);
6176 } 6166 }
6177 } 6167 }
6178 break; 6168 break;
6179 case "PatchGUID": 6169 case "PatchGUID":
6180 this.RootElement.SetAttributeValue("Id", value); 6170 this.DecompilerHelper.RootElement.SetAttributeValue("Id", value);
6181 break; 6171 break;
6182 case "PatchSourceList": 6172 case "PatchSourceList":
6183 this.RootElement.SetAttributeValue("SourceList", value); 6173 this.DecompilerHelper.RootElement.SetAttributeValue("SourceList", value);
6184 break; 6174 break;
6185 case "PatchOutputPath": 6175 case "PatchOutputPath":
6186 this.RootElement.SetAttributeValue("OutputPath", value); 6176 this.DecompilerHelper.RootElement.SetAttributeValue("OutputPath", value);
6187 break; 6177 break;
6188 default: 6178 default:
6189 var patchProperty = new XElement(Names.PatchPropertyElement, 6179 var patchProperty = new XElement(Names.PatchPropertyElement,
6190 new XAttribute("Name", name), 6180 new XAttribute("Name", name),
6191 new XAttribute("Value", value)); 6181 new XAttribute("Value", value));
6192 6182
6193 this.RootElement.Add(patchProperty); 6183 this.DecompilerHelper.AddElementToRoot(patchProperty);
6194 break; 6184 break;
6195 } 6185 }
6196 } 6186 }
@@ -6260,22 +6250,22 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
6260 switch (id) 6250 switch (id)
6261 { 6251 {
6262 case "Manufacturer": 6252 case "Manufacturer":
6263 this.RootElement.SetAttributeValue("Manufacturer", value); 6253 this.DecompilerHelper.RootElement.SetAttributeValue("Manufacturer", value);
6264 continue; 6254 continue;
6265 case "ProductCode": 6255 case "ProductCode":
6266 this.RootElement.SetAttributeValue("ProductCode", value.ToUpper(CultureInfo.InvariantCulture)); 6256 this.DecompilerHelper.RootElement.SetAttributeValue("ProductCode", value.ToUpper(CultureInfo.InvariantCulture));
6267 continue; 6257 continue;
6268 case "ProductLanguage": 6258 case "ProductLanguage":
6269 this.RootElement.SetAttributeValue("Language", value); 6259 this.DecompilerHelper.RootElement.SetAttributeValue("Language", value);
6270 continue; 6260 continue;
6271 case "ProductName": 6261 case "ProductName":
6272 this.RootElement.SetAttributeValue("Name", value); 6262 this.DecompilerHelper.RootElement.SetAttributeValue("Name", value);
6273 continue; 6263 continue;
6274 case "ProductVersion": 6264 case "ProductVersion":
6275 this.RootElement.SetAttributeValue("Version", value); 6265 this.DecompilerHelper.RootElement.SetAttributeValue("Version", value);
6276 continue; 6266 continue;
6277 case "UpgradeCode": 6267 case "UpgradeCode":
6278 this.RootElement.SetAttributeValue("UpgradeCode", value); 6268 this.DecompilerHelper.RootElement.SetAttributeValue("UpgradeCode", value);
6279 continue; 6269 continue;
6280 } 6270 }
6281 } 6271 }
@@ -6340,14 +6330,14 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
6340 } 6330 }
6341 } 6331 }
6342 6332
6343 this.IndexElement(row, radioButton); 6333 this.DecompilerHelper.IndexElement(row, radioButton);
6344 } 6334 }
6345 6335
6346 // nest the radio buttons 6336 // nest the radio buttons
6347 var xRadioButtonGroups = new Dictionary<string, XElement>(); 6337 var xRadioButtonGroups = new Dictionary<string, XElement>();
6348 foreach (var row in table.Rows.OrderBy(row => row.FieldAsString(0)).ThenBy(row => row.FieldAsInteger(1))) 6338 foreach (var row in table.Rows.OrderBy(row => row.FieldAsString(0)).ThenBy(row => row.FieldAsInteger(1)))
6349 { 6339 {
6350 var xRadioButton = this.GetIndexedElement(row); 6340 var xRadioButton = this.DecompilerHelper.GetIndexedElement(row);
6351 6341
6352 if (!xRadioButtonGroups.TryGetValue(row.FieldAsString(0), out var xRadioButtonGroup)) 6342 if (!xRadioButtonGroups.TryGetValue(row.FieldAsString(0), out var xRadioButtonGroup))
6353 { 6343 {
@@ -6395,7 +6385,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
6395 break; 6385 break;
6396 } 6386 }
6397 6387
6398 this.IndexElement(row, xRegistryKey); 6388 this.DecompilerHelper.IndexElement(row, xRegistryKey);
6399 } 6389 }
6400 else 6390 else
6401 { 6391 {
@@ -6480,7 +6470,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
6480 xRegistryValue.SetAttributeValue("Value", String.Empty); 6470 xRegistryValue.SetAttributeValue("Value", String.Empty);
6481 } 6471 }
6482 6472
6483 this.IndexElement(row, xRegistryValue); 6473 this.DecompilerHelper.IndexElement(row, xRegistryValue);
6484 } 6474 }
6485 } 6475 }
6486 } 6476 }
@@ -6552,7 +6542,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
6552 } 6542 }
6553 } 6543 }
6554 6544
6555 this.IndexElement(row, xRegistrySearch); 6545 this.DecompilerHelper.IndexElement(row, xRegistrySearch);
6556 } 6546 }
6557 } 6547 }
6558 6548
@@ -6588,7 +6578,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
6588 } 6578 }
6589 6579
6590 this.AddChildToParent("Component", xRemoveFolder, row, 1); 6580 this.AddChildToParent("Component", xRemoveFolder, row, 1);
6591 this.IndexElement(row, xRemoveFolder); 6581 this.DecompilerHelper.IndexElement(row, xRemoveFolder);
6592 } 6582 }
6593 else 6583 else
6594 { 6584 {
@@ -6625,7 +6615,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
6625 } 6615 }
6626 6616
6627 this.AddChildToParent("Component", xRemoveFile, row, 1); 6617 this.AddChildToParent("Component", xRemoveFile, row, 1);
6628 this.IndexElement(row, xRemoveFile); 6618 this.DecompilerHelper.IndexElement(row, xRemoveFile);
6629 } 6619 }
6630 } 6620 }
6631 } 6621 }
@@ -6738,7 +6728,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
6738 { 6728 {
6739 foreach (var row in table.Rows) 6729 foreach (var row in table.Rows)
6740 { 6730 {
6741 if (this.TryGetIndexedElement("File", out var xFile, row.FieldAsString(0))) 6731 if (this.DecompilerHelper.TryGetIndexedElement("File", row.FieldAsString(0), out var xFile))
6742 { 6732 {
6743 xFile.SetAttributeValue("SelfRegCost", row.IsColumnNull(1) ? 0 : row.FieldAsInteger(1)); 6733 xFile.SetAttributeValue("SelfRegCost", row.IsColumnNull(1) ? 0 : row.FieldAsInteger(1));
6744 } 6734 }
@@ -6927,7 +6917,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
6927 } 6917 }
6928 6918
6929 this.AddChildToParent("Component", xServiceInstall, row, 11); 6919 this.AddChildToParent("Component", xServiceInstall, row, 11);
6930 this.IndexElement(row, xServiceInstall); 6920 this.DecompilerHelper.IndexElement(row, xServiceInstall);
6931 } 6921 }
6932 } 6922 }
6933 6923
@@ -6943,17 +6933,17 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
6943 new XAttribute("Name", row.FieldAsString(0)), 6933 new XAttribute("Name", row.FieldAsString(0)),
6944 new XAttribute("SourceFile", row.FieldAsString(1))); 6934 new XAttribute("SourceFile", row.FieldAsString(1)));
6945 6935
6946 this.IndexElement(row, xSfpCatalog); 6936 this.DecompilerHelper.IndexElement(row, xSfpCatalog);
6947 } 6937 }
6948 6938
6949 // nest the SFPCatalog elements 6939 // nest the SFPCatalog elements
6950 foreach (var row in table.Rows) 6940 foreach (var row in table.Rows)
6951 { 6941 {
6952 var xSfpCatalog = this.GetIndexedElement(row); 6942 var xSfpCatalog = this.DecompilerHelper.GetIndexedElement(row);
6953 6943
6954 if (!row.IsColumnNull(2)) 6944 if (!row.IsColumnNull(2))
6955 { 6945 {
6956 if (this.TryGetIndexedElement("SFPCatalog", out var xParentSFPCatalog, row.FieldAsString(2))) 6946 if (this.DecompilerHelper.TryGetIndexedElement("SFPCatalog", row.FieldAsString(2), out var xParentSFPCatalog))
6957 { 6947 {
6958 xParentSFPCatalog.Add(xSfpCatalog); 6948 xParentSFPCatalog.Add(xSfpCatalog);
6959 } 6949 }
@@ -6961,12 +6951,12 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
6961 { 6951 {
6962 xSfpCatalog.SetAttributeValue("Dependency", row.FieldAsString(2)); 6952 xSfpCatalog.SetAttributeValue("Dependency", row.FieldAsString(2));
6963 6953
6964 this.RootElement.Add(xSfpCatalog); 6954 this.DecompilerHelper.AddElementToRoot(xSfpCatalog);
6965 } 6955 }
6966 } 6956 }
6967 else 6957 else
6968 { 6958 {
6969 this.RootElement.Add(xSfpCatalog); 6959 this.DecompilerHelper.AddElementToRoot(xSfpCatalog);
6970 } 6960 }
6971 } 6961 }
6972 } 6962 }
@@ -7044,7 +7034,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
7044 } 7034 }
7045 7035
7046 this.AddChildToParent("Component", xShortcut, row, 3); 7036 this.AddChildToParent("Component", xShortcut, row, 3);
7047 this.IndexElement(row, xShortcut); 7037 this.DecompilerHelper.IndexElement(row, xShortcut);
7048 } 7038 }
7049 } 7039 }
7050 7040
@@ -7093,7 +7083,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
7093 fileSearch.SetAttributeValue("MaxDate", ConvertIntegerToDateTime(row.FieldAsInteger(7))); 7083 fileSearch.SetAttributeValue("MaxDate", ConvertIntegerToDateTime(row.FieldAsInteger(7)));
7094 } 7084 }
7095 7085
7096 this.IndexElement(row, fileSearch); 7086 this.DecompilerHelper.IndexElement(row, fileSearch);
7097 } 7087 }
7098 } 7088 }
7099 7089
@@ -7110,7 +7100,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
7110 xPatchTargetFile = new XElement(Names.TargetFileElement, 7100 xPatchTargetFile = new XElement(Names.TargetFileElement,
7111 new XAttribute("Id", row.FieldAsString(1))); 7101 new XAttribute("Id", row.FieldAsString(1)));
7112 7102
7113 if (this.TryGetIndexedElement("TargetImages", out var xTargetImage, row.FieldAsString(0))) 7103 if (this.DecompilerHelper.TryGetIndexedElement("TargetImages", row.FieldAsString(0), out var xTargetImage))
7114 { 7104 {
7115 xTargetImage.Add(xPatchTargetFile); 7105 xTargetImage.Add(xPatchTargetFile);
7116 } 7106 }
@@ -7192,7 +7182,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
7192 } 7182 }
7193 7183
7194 this.AddChildToParent("UpgradedImages", xTargetImage, row, 3); 7184 this.AddChildToParent("UpgradedImages", xTargetImage, row, 3);
7195 this.IndexElement(row, xTargetImage); 7185 this.DecompilerHelper.IndexElement(row, xTargetImage);
7196 } 7186 }
7197 } 7187 }
7198 7188
@@ -7282,7 +7272,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
7282 } 7272 }
7283 7273
7284 // nested under the appropriate File element in FinalizeFileTable 7274 // nested under the appropriate File element in FinalizeFileTable
7285 this.IndexElement(row, xTypeLib); 7275 this.DecompilerHelper.IndexElement(row, xTypeLib);
7286 } 7276 }
7287 } 7277 }
7288 7278
@@ -7306,7 +7296,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
7306 xUpgrade = new XElement(Names.UpgradeElement, 7296 xUpgrade = new XElement(Names.UpgradeElement,
7307 new XAttribute("Id", upgradeRow.UpgradeCode)); 7297 new XAttribute("Id", upgradeRow.UpgradeCode));
7308 7298
7309 this.RootElement.Add(xUpgrade); 7299 this.DecompilerHelper.AddElementToRoot(xUpgrade);
7310 xUpgrades.Add(upgradeRow.UpgradeCode, xUpgrade); 7300 xUpgrades.Add(upgradeRow.UpgradeCode, xUpgrade);
7311 } 7301 }
7312 7302
@@ -7435,7 +7425,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
7435 AddSymbolPaths(row, 3, xUpgradeImage); 7425 AddSymbolPaths(row, 3, xUpgradeImage);
7436 7426
7437 this.AddChildToParent("ImageFamilies", xUpgradeImage, row, 4); 7427 this.AddChildToParent("ImageFamilies", xUpgradeImage, row, 4);
7438 this.IndexElement(row, xUpgradeImage); 7428 this.DecompilerHelper.IndexElement(row, xUpgradeImage);
7439 } 7429 }
7440 } 7430 }
7441 7431
@@ -7485,7 +7475,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
7485 XAttributeIfNotNull("Command", row, 3), 7475 XAttributeIfNotNull("Command", row, 3),
7486 XAttributeIfNotNull("Argument", row, 4)); 7476 XAttributeIfNotNull("Argument", row, 4));
7487 7477
7488 this.IndexElement(row, verb); 7478 this.DecompilerHelper.IndexElement(row, verb);
7489 } 7479 }
7490 } 7480 }
7491 7481
@@ -7537,7 +7527,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile
7537 var featureField = row.Fields[featureColumnIndex]; 7527 var featureField = row.Fields[featureColumnIndex];
7538 var componentField = row.Fields[componentColumnIndex]; 7528 var componentField = row.Fields[componentColumnIndex];
7539 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))
7541 { 7531 {
7542 xComponentRef.SetAttributeValue("Primary", "yes"); 7532 xComponentRef.SetAttributeValue("Primary", "yes");
7543 } 7533 }
diff --git a/src/wix/WixToolset.Core.WindowsInstaller/ExtensibilityServices/WindowsInstallerDecompilerHelper.cs b/src/wix/WixToolset.Core.WindowsInstaller/ExtensibilityServices/WindowsInstallerDecompilerHelper.cs
new file mode 100644
index 00000000..e09270e7
--- /dev/null
+++ b/src/wix/WixToolset.Core.WindowsInstaller/ExtensibilityServices/WindowsInstallerDecompilerHelper.cs
@@ -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
3namespace 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}
diff --git a/src/wix/WixToolset.Core.WindowsInstaller/Unbind/UnbindDatabaseCommand.cs b/src/wix/WixToolset.Core.WindowsInstaller/Unbind/UnbindDatabaseCommand.cs
index b510690e..c1fb7f12 100644
--- a/src/wix/WixToolset.Core.WindowsInstaller/Unbind/UnbindDatabaseCommand.cs
+++ b/src/wix/WixToolset.Core.WindowsInstaller/Unbind/UnbindDatabaseCommand.cs
@@ -171,7 +171,7 @@ namespace WixToolset.Core.WindowsInstaller.Unbind
171 } 171 }
172 break; 172 break;
173 case ColumnType.Object: 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 175
176 if (null != this.ExportBasePath) 176 if (null != this.ExportBasePath)
177 { 177 {
diff --git a/src/wix/WixToolset.Core.WindowsInstaller/WindowsInstallerDecompileContext.cs b/src/wix/WixToolset.Core.WindowsInstaller/WindowsInstallerDecompileContext.cs
index 1428a469..22745ab0 100644
--- a/src/wix/WixToolset.Core.WindowsInstaller/WindowsInstallerDecompileContext.cs
+++ b/src/wix/WixToolset.Core.WindowsInstaller/WindowsInstallerDecompileContext.cs
@@ -23,6 +23,10 @@ namespace WixToolset.Core.WindowsInstaller
23 23
24 public IReadOnlyCollection<IWindowsInstallerDecompilerExtension> Extensions { get; set; } 24 public IReadOnlyCollection<IWindowsInstallerDecompilerExtension> Extensions { get; set; }
25 25
26 public IReadOnlyCollection<IExtensionData> ExtensionData { get; set; }
27
28 public ISymbolDefinitionCreator SymbolDefinitionCreator { get; set; }
29
26 public string ExtractFolder { get; set; } 30 public string ExtractFolder { get; set; }
27 31
28 public string CabinetExtractFolder { get; set; } 32 public string CabinetExtractFolder { get; set; }
diff --git a/src/wix/WixToolset.Core.WindowsInstaller/WindowsInstallerDecompileResult.cs b/src/wix/WixToolset.Core.WindowsInstaller/WindowsInstallerDecompileResult.cs
index 69363286..272fb3c2 100644
--- a/src/wix/WixToolset.Core.WindowsInstaller/WindowsInstallerDecompileResult.cs
+++ b/src/wix/WixToolset.Core.WindowsInstaller/WindowsInstallerDecompileResult.cs
@@ -11,7 +11,7 @@ namespace WixToolset.Core.WindowsInstaller
11 { 11 {
12 public XDocument Document { get; set; } 12 public XDocument Document { get; set; }
13 13
14 public IReadOnlyCollection<string> ExtractedFilePaths { get; set; } 14 public IList<string> ExtractedFilePaths { get; set; }
15 15
16 public Platform? Platform { get; set; } 16 public Platform? Platform { get; set; }
17 } 17 }
diff --git a/src/wix/WixToolset.Core.WindowsInstaller/WindowsInstallerDecompiler.cs b/src/wix/WixToolset.Core.WindowsInstaller/WindowsInstallerDecompiler.cs
index 10420010..b35e3587 100644
--- a/src/wix/WixToolset.Core.WindowsInstaller/WindowsInstallerDecompiler.cs
+++ b/src/wix/WixToolset.Core.WindowsInstaller/WindowsInstallerDecompiler.cs
@@ -3,8 +3,15 @@
3namespace WixToolset.Core.WindowsInstaller 3namespace WixToolset.Core.WindowsInstaller
4{ 4{
5 using System; 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;
6 using WixToolset.Core.WindowsInstaller.Decompile; 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;
8 using WixToolset.Extensibility.Data; 15 using WixToolset.Extensibility.Data;
9 using WixToolset.Extensibility.Services; 16 using WixToolset.Extensibility.Services;
10 17
@@ -16,12 +23,20 @@ namespace WixToolset.Core.WindowsInstaller
16 internal WindowsInstallerDecompiler(IServiceProvider serviceProvider) 23 internal WindowsInstallerDecompiler(IServiceProvider serviceProvider)
17 { 24 {
18 this.ServiceProvider = serviceProvider; 25 this.ServiceProvider = serviceProvider;
26 this.Messaging = serviceProvider.GetService<IMessaging>();
19 } 27 }
20 28
21 public IServiceProvider ServiceProvider { get; } 29 private IServiceProvider ServiceProvider { get; }
30
31 private IMessaging Messaging { get; }
22 32
23 public IWindowsInstallerDecompileResult Decompile(IWindowsInstallerDecompileContext context) 33 public IWindowsInstallerDecompileResult Decompile(IWindowsInstallerDecompileContext context)
24 { 34 {
35 if (context.SymbolDefinitionCreator == null)
36 {
37 context.SymbolDefinitionCreator = this.ServiceProvider.GetService<ISymbolDefinitionCreator>();
38 }
39
25 // Pre-decompile. 40 // Pre-decompile.
26 // 41 //
27 foreach (var extension in context.Extensions) 42 foreach (var extension in context.Extensions)
@@ -31,8 +46,7 @@ namespace WixToolset.Core.WindowsInstaller
31 46
32 // Decompile. 47 // Decompile.
33 // 48 //
34 var command = new DecompileMsiOrMsmCommand(context); 49 var result = this.Execute(context);
35 var result = command.Execute();
36 50
37 if (result != null) 51 if (result != null)
38 { 52 {
@@ -46,5 +60,68 @@ namespace WixToolset.Core.WindowsInstaller
46 60
47 return result; 61 return result;
48 } 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 }
49 } 126 }
50} 127}
diff --git a/src/wix/WixToolset.Core.WindowsInstaller/WixToolsetCoreServiceProviderExtensions.cs b/src/wix/WixToolset.Core.WindowsInstaller/WixToolsetCoreServiceProviderExtensions.cs
index d064aed1..c085af9b 100644
--- a/src/wix/WixToolset.Core.WindowsInstaller/WixToolsetCoreServiceProviderExtensions.cs
+++ b/src/wix/WixToolset.Core.WindowsInstaller/WixToolsetCoreServiceProviderExtensions.cs
@@ -16,8 +16,8 @@ namespace WixToolset.Core.WindowsInstaller
16 /// <summary> 16 /// <summary>
17 /// Adds WindowsInstaller services. 17 /// Adds WindowsInstaller services.
18 /// </summary> 18 /// </summary>
19 /// <param name="coreProvider"></param> 19 /// <param name="coreProvider">Core service provider.</param>
20 /// <returns></returns> 20 /// <returns>The core service provider provided to this method.</returns>
21 public static IWixToolsetCoreServiceProvider AddWindowsInstallerBackend(this IWixToolsetCoreServiceProvider coreProvider) 21 public static IWixToolsetCoreServiceProvider AddWindowsInstallerBackend(this IWixToolsetCoreServiceProvider coreProvider)
22 { 22 {
23 AddServices(coreProvider); 23 AddServices(coreProvider);
@@ -32,6 +32,7 @@ namespace WixToolset.Core.WindowsInstaller
32 { 32 {
33 // Singletons. 33 // Singletons.
34 coreProvider.AddService((provider, singletons) => AddSingleton<IWindowsInstallerBackendHelper>(singletons, new WindowsInstallerBackendHelper(provider))); 34 coreProvider.AddService((provider, singletons) => AddSingleton<IWindowsInstallerBackendHelper>(singletons, new WindowsInstallerBackendHelper(provider)));
35 coreProvider.AddService((provider, singletons) => AddSingleton<IWindowsInstallerDecompilerHelper>(singletons, new WindowsInstallerDecompilerHelper(provider)));
35 36
36 // Transients. 37 // Transients.
37 coreProvider.AddService<IWindowsInstallerDecompiler>((provider, singletons) => new WindowsInstallerDecompiler(provider)); 38 coreProvider.AddService<IWindowsInstallerDecompiler>((provider, singletons) => new WindowsInstallerDecompiler(provider));
diff --git a/src/wix/test/Example.Extension/ExampleCompilerExtension.cs b/src/wix/test/Example.Extension/ExampleCompilerExtension.cs
index 5b8d4b3f..b68a87f1 100644
--- a/src/wix/test/Example.Extension/ExampleCompilerExtension.cs
+++ b/src/wix/test/Example.Extension/ExampleCompilerExtension.cs
@@ -10,8 +10,9 @@ namespace Example.Extension
10 10
11 internal class ExampleCompilerExtension : BaseCompilerExtension 11 internal class ExampleCompilerExtension : BaseCompilerExtension
12 { 12 {
13 public override XNamespace Namespace => "http://www.example.com/scheams/v1/wxs"; 13 private const string BundleExtensionId = "ExampleBundleExtension";
14 public string BundleExtensionId => "ExampleBundleExtension"; 14
15 public override XNamespace Namespace => ExampleConstants.Namespace;
15 16
16 public override void ParseElement(Intermediate intermediate, IntermediateSection section, XElement parentElement, XElement element, IDictionary<string, string> context) 17 public override void ParseElement(Intermediate intermediate, IntermediateSection section, XElement parentElement, XElement element, IDictionary<string, string> context)
17 { 18 {
@@ -38,10 +39,12 @@ namespace Example.Extension
38 } 39 }
39 break; 40 break;
40 case "Component": 41 case "Component":
42 var componentId = context["ComponentId"];
43
41 switch (element.Name.LocalName) 44 switch (element.Name.LocalName)
42 { 45 {
43 case "Example": 46 case "Example":
44 this.ParseExampleElement(intermediate, section, element); 47 this.ParseExampleElement(intermediate, section, element, componentId);
45 processed = true; 48 processed = true;
46 break; 49 break;
47 } 50 }
@@ -54,7 +57,7 @@ namespace Example.Extension
54 } 57 }
55 } 58 }
56 59
57 private void ParseExampleElement(Intermediate intermediate, IntermediateSection section, XElement element) 60 private void ParseExampleElement(Intermediate intermediate, IntermediateSection section, XElement element, string componentId)
58 { 61 {
59 var sourceLineNumbers = this.ParseHelper.GetSourceLineNumbers(element); 62 var sourceLineNumbers = this.ParseHelper.GetSourceLineNumbers(element);
60 Identifier id = null; 63 Identifier id = null;
@@ -93,7 +96,8 @@ namespace Example.Extension
93 if (!this.Messaging.EncounteredError) 96 if (!this.Messaging.EncounteredError)
94 { 97 {
95 var symbol = this.ParseHelper.CreateSymbol(section, sourceLineNumbers, "Example", id); 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);
97 } 101 }
98 } 102 }
99 103
@@ -152,12 +156,12 @@ namespace Example.Extension
152 156
153 if (!this.Messaging.EncounteredError) 157 if (!this.Messaging.EncounteredError)
154 { 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);
156 } 160 }
157 161
158 if (!this.Messaging.EncounteredError) 162 if (!this.Messaging.EncounteredError)
159 { 163 {
160 var symbol = section.AddSymbol(new ExampleSearchSymbol(sourceLineNumbers, id) 164 section.AddSymbol(new ExampleSearchSymbol(sourceLineNumbers, id)
161 { 165 {
162 SearchFor = searchFor, 166 SearchFor = searchFor,
163 }); 167 });
diff --git a/src/wix/test/Example.Extension/ExampleConstants.cs b/src/wix/test/Example.Extension/ExampleConstants.cs
new file mode 100644
index 00000000..abed40ea
--- /dev/null
+++ b/src/wix/test/Example.Extension/ExampleConstants.cs
@@ -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
3namespace 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}
diff --git a/src/wix/test/Example.Extension/ExampleExtensionFactory.cs b/src/wix/test/Example.Extension/ExampleExtensionFactory.cs
index e54561ee..0b0fb836 100644
--- a/src/wix/test/Example.Extension/ExampleExtensionFactory.cs
+++ b/src/wix/test/Example.Extension/ExampleExtensionFactory.cs
@@ -43,6 +43,10 @@ namespace Example.Extension
43 { 43 {
44 extension = new ExampleWindowsInstallerBackendExtension(); 44 extension = new ExampleWindowsInstallerBackendExtension();
45 } 45 }
46 else if (extensionType == typeof(IWindowsInstallerDecompilerExtension))
47 {
48 extension = new ExampleWindowsInstallerDecompilerExtension();
49 }
46 else 50 else
47 { 51 {
48 extension = null; 52 extension = null;
diff --git a/src/wix/test/Example.Extension/ExampleRow.cs b/src/wix/test/Example.Extension/ExampleRow.cs
index fc20c6c9..5c96035d 100644
--- a/src/wix/test/Example.Extension/ExampleRow.cs
+++ b/src/wix/test/Example.Extension/ExampleRow.cs
@@ -23,10 +23,16 @@ namespace Example.Extension
23 set { this.Fields[0].Data = value; } 23 set { this.Fields[0].Data = value; }
24 } 24 }
25 25
26 public string Value 26 public string ComponentRef
27 { 27 {
28 get { return (string)this.Fields[1].Data; } 28 get { return (string)this.Fields[1].Data; }
29 set { this.Fields[1].Data = value; } 29 set { this.Fields[1].Data = value; }
30 } 30 }
31
32 public string Value
33 {
34 get { return (string)this.Fields[2].Data; }
35 set { this.Fields[2].Data = value; }
36 }
31 } 37 }
32} 38}
diff --git a/src/wix/test/Example.Extension/ExampleSymbol.cs b/src/wix/test/Example.Extension/ExampleSymbol.cs
index 314087e9..f82d9604 100644
--- a/src/wix/test/Example.Extension/ExampleSymbol.cs
+++ b/src/wix/test/Example.Extension/ExampleSymbol.cs
@@ -6,6 +6,7 @@ namespace Example.Extension
6 6
7 public enum ExampleSymbolFields 7 public enum ExampleSymbolFields
8 { 8 {
9 ComponentRef,
9 Value, 10 Value,
10 } 11 }
11 12
@@ -21,6 +22,12 @@ namespace Example.Extension
21 22
22 public IntermediateField this[ExampleSymbolFields index] => this.Fields[(int)index]; 23 public IntermediateField this[ExampleSymbolFields index] => this.Fields[(int)index];
23 24
25 public string ComponentRef
26 {
27 get => this.Fields[(int)ExampleSymbolFields.ComponentRef]?.AsString();
28 set => this.Set((int)ExampleSymbolFields.ComponentRef, value);
29 }
30
24 public string Value 31 public string Value
25 { 32 {
26 get => this.Fields[(int)ExampleSymbolFields.Value]?.AsString(); 33 get => this.Fields[(int)ExampleSymbolFields.Value]?.AsString();
diff --git a/src/wix/test/Example.Extension/ExampleSymbolDefinitions.cs b/src/wix/test/Example.Extension/ExampleSymbolDefinitions.cs
index f13d716d..83b675c9 100644
--- a/src/wix/test/Example.Extension/ExampleSymbolDefinitions.cs
+++ b/src/wix/test/Example.Extension/ExampleSymbolDefinitions.cs
@@ -18,6 +18,7 @@ namespace Example.Extension
18 ExampleSymbolDefinitionType.Example.ToString(), 18 ExampleSymbolDefinitionType.Example.ToString(),
19 new[] 19 new[]
20 { 20 {
21 new IntermediateFieldDefinition(nameof(ExampleSymbolFields.ComponentRef), IntermediateFieldType.String),
21 new IntermediateFieldDefinition(nameof(ExampleSymbolFields.Value), IntermediateFieldType.String), 22 new IntermediateFieldDefinition(nameof(ExampleSymbolFields.Value), IntermediateFieldType.String),
22 }, 23 },
23 typeof(ExampleSymbol)); 24 typeof(ExampleSymbol));
diff --git a/src/wix/test/Example.Extension/ExampleTableDefinitions.cs b/src/wix/test/Example.Extension/ExampleTableDefinitions.cs
index a2b81698..3da30609 100644
--- a/src/wix/test/Example.Extension/ExampleTableDefinitions.cs
+++ b/src/wix/test/Example.Extension/ExampleTableDefinitions.cs
@@ -11,7 +11,8 @@ namespace Example.Extension
11 ExampleSymbolDefinitions.Example, 11 ExampleSymbolDefinitions.Example,
12 new[] 12 new[]
13 { 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),
15 new ColumnDefinition("Value", ColumnType.String, 0, false, false, ColumnCategory.Formatted), 16 new ColumnDefinition("Value", ColumnType.String, 0, false, false, ColumnCategory.Formatted),
16 }, 17 },
17 strongRowType: typeof(ExampleRow), 18 strongRowType: typeof(ExampleRow),
diff --git a/src/wix/test/Example.Extension/ExampleWindowsInstallerBackendExtension.cs b/src/wix/test/Example.Extension/ExampleWindowsInstallerBackendExtension.cs
index afccc56f..95d82875 100644
--- a/src/wix/test/Example.Extension/ExampleWindowsInstallerBackendExtension.cs
+++ b/src/wix/test/Example.Extension/ExampleWindowsInstallerBackendExtension.cs
@@ -18,12 +18,13 @@ namespace Example.Extension
18 switch (symbolType) 18 switch (symbolType)
19 { 19 {
20 case ExampleSymbolDefinitionType.Example: 20 case ExampleSymbolDefinitionType.Example:
21 { 21 {
22 var row = (ExampleRow)this.BackendHelper.CreateRow(section, symbol, output, ExampleTableDefinitions.ExampleTable); 22 var row = (ExampleRow)this.BackendHelper.CreateRow(section, symbol, output, ExampleTableDefinitions.ExampleTable);
23 row.Example = symbol.Id.Id; 23 row.Example = symbol.Id.Id;
24 row.Value = symbol[0].AsString(); 24 row.ComponentRef = symbol[0].AsString();
25 } 25 row.Value = symbol[1].AsString();
26 return true; 26 return true;
27 }
27 } 28 }
28 } 29 }
29 30
diff --git a/src/wix/test/Example.Extension/ExampleWindowsInstallerDecompilerExtension.cs b/src/wix/test/Example.Extension/ExampleWindowsInstallerDecompilerExtension.cs
new file mode 100644
index 00000000..dc671e82
--- /dev/null
+++ b/src/wix/test/Example.Extension/ExampleWindowsInstallerDecompilerExtension.cs
@@ -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
3namespace 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}
diff --git a/src/wix/test/WixToolsetTest.CoreIntegration/ExtensionFixture.cs b/src/wix/test/WixToolsetTest.CoreIntegration/ExtensionFixture.cs
index d814000c..a5155069 100644
--- a/src/wix/test/WixToolsetTest.CoreIntegration/ExtensionFixture.cs
+++ b/src/wix/test/WixToolsetTest.CoreIntegration/ExtensionFixture.cs
@@ -5,6 +5,7 @@ namespace WixToolsetTest.CoreIntegration
5 using System; 5 using System;
6 using System.IO; 6 using System.IO;
7 using System.Linq; 7 using System.Linq;
8 using System.Text.RegularExpressions;
8 using Example.Extension; 9 using Example.Extension;
9 using WixBuildTools.TestSupport; 10 using WixBuildTools.TestSupport;
10 using WixToolset.Core.TestPackage; 11 using WixToolset.Core.TestPackage;
@@ -23,11 +24,38 @@ namespace WixToolsetTest.CoreIntegration
23 var results = build.BuildAndQuery(Build, "Wix4Example"); 24 var results = build.BuildAndQuery(Build, "Wix4Example");
24 WixAssert.CompareLineByLine(new[] 25 WixAssert.CompareLineByLine(new[]
25 { 26 {
26 "Wix4Example:Foo\tBar" 27 "Wix4Example:Foo\tfilF5_pLhBuF5b4N9XEo52g_hUM5Lo\tBar"
27 }, results); 28 }, results);
28 } 29 }
29 30
30 [Fact] 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]
31 public void CanBuildWithExampleExtension() 59 public void CanBuildWithExampleExtension()
32 { 60 {
33 var folder = TestData.Get(@"TestData\ExampleExtension"); 61 var folder = TestData.Get(@"TestData\ExampleExtension");
@@ -64,7 +92,8 @@ namespace WixToolsetTest.CoreIntegration
64 92
65 var example = section.Symbols.Where(t => t.Definition.Type == SymbolDefinitionType.MustBeFromAnExtension).Single(); 93 var example = section.Symbols.Where(t => t.Definition.Type == SymbolDefinitionType.MustBeFromAnExtension).Single();
66 Assert.Equal("Foo", example.Id?.Id); 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());
68 } 97 }
69 } 98 }
70 99
@@ -192,8 +221,14 @@ namespace WixToolsetTest.CoreIntegration
192 221
193 private static void Build(string[] args) 222 private static void Build(string[] args)
194 { 223 {
195 var result = WixRunner.Execute(args) 224 var result = WixRunner.Execute(args);
196 .AssertSuccess(); 225 result.AssertSuccess();
226 }
227
228 private static void Decompile(string[] args)
229 {
230 var result = WixRunner.Execute(args);
231 result.AssertSuccess();
197 } 232 }
198 } 233 }
199} 234}
diff --git a/src/wix/test/WixToolsetTest.CoreIntegration/TestData/AppSearch/DecompiledNestedDirSearchUnderRegSearch.wxs b/src/wix/test/WixToolsetTest.CoreIntegration/TestData/AppSearch/DecompiledNestedDirSearchUnderRegSearch.wxs
index 6b9fe013..9e72390a 100644
--- a/src/wix/test/WixToolsetTest.CoreIntegration/TestData/AppSearch/DecompiledNestedDirSearchUnderRegSearch.wxs
+++ b/src/wix/test/WixToolsetTest.CoreIntegration/TestData/AppSearch/DecompiledNestedDirSearchUnderRegSearch.wxs
@@ -1,9 +1,9 @@
1<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs"> 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"> 3 <StandardDirectory Id="ProgramFilesFolder">
4 <Directory Id="INSTALLFOLDER" Name="MsiPackage" ShortName="ykd0udtb"> 4 <Directory Id="INSTALLFOLDER" Name="MsiPackage" ShortName="ykd0udtb">
5 <Component Id="test.txt" Guid="{E597A58A-03CB-50D8-93E3-DABA263F233A}" Bitness="always32"> 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> 7 </Component>
8 </Directory> 8 </Directory>
9 </StandardDirectory> 9 </StandardDirectory>
@@ -39,4 +39,4 @@
39 </RegistrySearch> 39 </RegistrySearch>
40 </Property> 40 </Property>
41 </Package> 41 </Package>
42</Wix> \ No newline at end of file 42</Wix>
diff --git a/src/wix/test/WixToolsetTest.CoreIntegration/TestData/Class/DecompiledOldClassTableDef.wxs b/src/wix/test/WixToolsetTest.CoreIntegration/TestData/Class/DecompiledOldClassTableDef.wxs
index 514f9243..0f4d0678 100644
--- a/src/wix/test/WixToolsetTest.CoreIntegration/TestData/Class/DecompiledOldClassTableDef.wxs
+++ b/src/wix/test/WixToolsetTest.CoreIntegration/TestData/Class/DecompiledOldClassTableDef.wxs
@@ -1,12 +1,12 @@
1<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs"> 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"> 3 <StandardDirectory Id="ProgramFilesFolder">
4 <Directory Id="INSTALLFOLDER" Name="MsiPackage" ShortName="oekcr5lq"> 4 <Directory Id="INSTALLFOLDER" Name="MsiPackage" ShortName="oekcr5lq">
5 <Component Id="ProgIdComp" Guid="{5B3B3FC1-533D-4C29-BFB3-0E88B51E59D8}" Bitness="always32"> 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"> 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" /> 7 <ProgId Id="73E7DF7E-EFAC-4E11-90E2-6EBAEB8DE58D" Description="FakeClassF12A" Advertise="yes" />
8 </Class> 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" /> 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" /> 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" /> 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." /> 19 <MajorUpgrade DowngradeErrorMessage="A newer version of [ProductName] is already installed." />
20 <Media Id="1" /> 20 <Media Id="1" />
21 </Package> 21 </Package>
22</Wix> \ No newline at end of file 22</Wix>
diff --git a/src/wix/test/WixToolsetTest.CoreIntegration/TestData/CustomTable/CustomTable-Expected.wxs b/src/wix/test/WixToolsetTest.CoreIntegration/TestData/CustomTable/CustomTable-Expected.wxs
index d7d86008..fdcc3438 100644
--- a/src/wix/test/WixToolsetTest.CoreIntegration/TestData/CustomTable/CustomTable-Expected.wxs
+++ b/src/wix/test/WixToolsetTest.CoreIntegration/TestData/CustomTable/CustomTable-Expected.wxs
@@ -1,5 +1,5 @@
1<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs"> 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"> 3 <CustomTable Id="CustomTable1">
4 <Column Id="Column1" PrimaryKey="yes" Type="string" Width="0" Category="text" Description="The first custom column." /> 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" /> 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"> 15 <StandardDirectory Id="ProgramFiles6432Folder">
16 <Directory Id="INSTALLFOLDER" Name="MsiPackage" ShortName="1egc1laj"> 16 <Directory Id="INSTALLFOLDER" Name="MsiPackage" ShortName="1egc1laj">
17 <Component Id="filcV1yrx0x8wJWj4qMzcH21jwkPko" Guid="{E597A58A-03CB-50D8-93E3-DABA263F233A}" Bitness="always32"> 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> 19 </Component>
20 </Directory> 20 </Directory>
21 </StandardDirectory> 21 </StandardDirectory>
diff --git a/src/wix/test/WixToolsetTest.CoreIntegration/TestData/DecompileNullComponent/Expected.wxs b/src/wix/test/WixToolsetTest.CoreIntegration/TestData/DecompileNullComponent/Expected.wxs
index 71553e2a..3d15dd3e 100644
--- a/src/wix/test/WixToolsetTest.CoreIntegration/TestData/DecompileNullComponent/Expected.wxs
+++ b/src/wix/test/WixToolsetTest.CoreIntegration/TestData/DecompileNullComponent/Expected.wxs
@@ -1,5 +1,5 @@
1<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs"> 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"> 3 <StandardDirectory Id="ProgramFilesFolder">
4 <Directory Id="INSTALLFOLDER" Name="MsiPackage" ShortName="oekcr5lq"> 4 <Directory Id="INSTALLFOLDER" Name="MsiPackage" ShortName="oekcr5lq">
5 <Component Id="filcV1yrx0x8wJWj4qMzcH21jwkPko" Guid="" Bitness="always32"> 5 <Component Id="filcV1yrx0x8wJWj4qMzcH21jwkPko" Guid="" Bitness="always32">
diff --git a/src/wix/test/WixToolsetTest.CoreIntegration/TestData/DecompileSingleFileCompressed/Expected.wxs b/src/wix/test/WixToolsetTest.CoreIntegration/TestData/DecompileSingleFileCompressed/Expected.wxs
index 246bcafc..9d0b8462 100644
--- a/src/wix/test/WixToolsetTest.CoreIntegration/TestData/DecompileSingleFileCompressed/Expected.wxs
+++ b/src/wix/test/WixToolsetTest.CoreIntegration/TestData/DecompileSingleFileCompressed/Expected.wxs
@@ -1,5 +1,5 @@
1<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs"> 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"> 3 <StandardDirectory Id="ProgramFilesFolder">
4 <Directory Id="INSTALLFOLDER" Name="MsiPackage" ShortName="oekcr5lq"> 4 <Directory Id="INSTALLFOLDER" Name="MsiPackage" ShortName="oekcr5lq">
5 <Component Id="filcV1yrx0x8wJWj4qMzcH21jwkPko" Guid="{E597A58A-03CB-50D8-93E3-DABA263F233A}" Bitness="always32"> 5 <Component Id="filcV1yrx0x8wJWj4qMzcH21jwkPko" Guid="{E597A58A-03CB-50D8-93E3-DABA263F233A}" Bitness="always32">
diff --git a/src/wix/test/WixToolsetTest.CoreIntegration/TestData/DecompileSingleFileCompressed64/Expected.wxs b/src/wix/test/WixToolsetTest.CoreIntegration/TestData/DecompileSingleFileCompressed64/Expected.wxs
index 81915759..a30d2c91 100644
--- a/src/wix/test/WixToolsetTest.CoreIntegration/TestData/DecompileSingleFileCompressed64/Expected.wxs
+++ b/src/wix/test/WixToolsetTest.CoreIntegration/TestData/DecompileSingleFileCompressed64/Expected.wxs
@@ -1,5 +1,5 @@
1<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs"> 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"> 3 <StandardDirectory Id="ProgramFiles64Folder">
4 <Directory Id="INSTALLFOLDER" Name="MsiPackage" ShortName="oekcr5lq"> 4 <Directory Id="INSTALLFOLDER" Name="MsiPackage" ShortName="oekcr5lq">
5 <Component Id="filcV1yrx0x8wJWj4qMzcH21jwkPko" Guid="{E597A58A-03CB-50D8-93E3-DABA263F233A}" Bitness="always64"> 5 <Component Id="filcV1yrx0x8wJWj4qMzcH21jwkPko" Guid="{E597A58A-03CB-50D8-93E3-DABA263F233A}" Bitness="always64">
diff --git a/src/wix/test/WixToolsetTest.CoreIntegration/TestData/ExampleExtension/Decompiled-Expected.xml b/src/wix/test/WixToolsetTest.CoreIntegration/TestData/ExampleExtension/Decompiled-Expected.xml
new file mode 100644
index 00000000..42ececb1
--- /dev/null
+++ b/src/wix/test/WixToolsetTest.CoreIntegration/TestData/ExampleExtension/Decompiled-Expected.xml
@@ -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>
diff --git a/src/wix/test/WixToolsetTest.CoreIntegration/TestData/SequenceTables/DecompiledSequenceTables.wxs b/src/wix/test/WixToolsetTest.CoreIntegration/TestData/SequenceTables/DecompiledSequenceTables.wxs
index d5379e7b..096b56e8 100644
--- a/src/wix/test/WixToolsetTest.CoreIntegration/TestData/SequenceTables/DecompiledSequenceTables.wxs
+++ b/src/wix/test/WixToolsetTest.CoreIntegration/TestData/SequenceTables/DecompiledSequenceTables.wxs
@@ -1,10 +1,10 @@
1<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs"> 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" /> 3 <CustomAction Id="CustomAction2" Property="TestAdvtExecuteSequenceProperty" Value="1" />
4 <StandardDirectory Id="ProgramFilesFolder"> 4 <StandardDirectory Id="ProgramFilesFolder">
5 <Directory Id="INSTALLFOLDER" Name="MsiPackage" ShortName="ykd0udtb"> 5 <Directory Id="INSTALLFOLDER" Name="MsiPackage" ShortName="ykd0udtb">
6 <Component Id="test.txt" Guid="{E597A58A-03CB-50D8-93E3-DABA263F233A}" Bitness="always32"> 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> 8 </Component>
9 </Directory> 9 </Directory>
10 </StandardDirectory> 10 </StandardDirectory>
@@ -29,4 +29,4 @@
29 <Custom Action="CustomAction2" After="CostInitialize" /> 29 <Custom Action="CustomAction2" After="CostInitialize" />
30 </AdvertiseExecuteSequence> 30 </AdvertiseExecuteSequence>
31 </Package> 31 </Package>
32</Wix> \ No newline at end of file 32</Wix>
diff --git a/src/wix/test/WixToolsetTest.CoreIntegration/TestData/Shortcut/DecompiledShortcuts.wxs b/src/wix/test/WixToolsetTest.CoreIntegration/TestData/Shortcut/DecompiledShortcuts.wxs
index da1e4f38..b04c635e 100644
--- a/src/wix/test/WixToolsetTest.CoreIntegration/TestData/Shortcut/DecompiledShortcuts.wxs
+++ b/src/wix/test/WixToolsetTest.CoreIntegration/TestData/Shortcut/DecompiledShortcuts.wxs
@@ -1,9 +1,9 @@
1<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs"> 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"> 3 <StandardDirectory Id="ProgramFilesFolder">
4 <Directory Id="INSTALLFOLDER" Name="MsiPackage" ShortName="ykd0udtb"> 4 <Directory Id="INSTALLFOLDER" Name="MsiPackage" ShortName="ykd0udtb">
5 <Component Id="ShortcutComp" Guid="{5B3B3FC1-533D-4C29-BFB3-0E88B51E59D8}" Bitness="always32"> 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]" /> 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" /> 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" /> 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." /> 16 <MajorUpgrade DowngradeErrorMessage="A newer version of [ProductName] is already installed." />
17 <Media Id="1" /> 17 <Media Id="1" />
18 </Package> 18 </Package>
19</Wix> \ No newline at end of file 19</Wix>
diff --git a/src/wix/test/WixToolsetTest.CoreIntegration/WixlibFixture.cs b/src/wix/test/WixToolsetTest.CoreIntegration/WixlibFixture.cs
index 01b82eb3..184b291b 100644
--- a/src/wix/test/WixToolsetTest.CoreIntegration/WixlibFixture.cs
+++ b/src/wix/test/WixToolsetTest.CoreIntegration/WixlibFixture.cs
@@ -289,7 +289,7 @@ namespace WixToolsetTest.CoreIntegration
289 289
290 var example = section.Symbols.Where(t => t.Definition.Type == SymbolDefinitionType.MustBeFromAnExtension).Single(); 290 var example = section.Symbols.Where(t => t.Definition.Type == SymbolDefinitionType.MustBeFromAnExtension).Single();
291 Assert.Equal("Foo", example.Id?.Id); 291 Assert.Equal("Foo", example.Id?.Id);
292 Assert.Equal("Bar", example[0].AsString()); 292 Assert.Equal("Bar", example[1].AsString());
293 } 293 }
294 } 294 }
295 295
@@ -352,7 +352,8 @@ namespace WixToolsetTest.CoreIntegration
352 352
353 var examples = section.Symbols.Where(t => t.Definition.Type == SymbolDefinitionType.MustBeFromAnExtension).ToArray(); 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()); 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());
356 } 357 }
357 } 358 }
358 } 359 }