From 5d08e0a4bbf4e4ba28300b8bace1089b64b198d7 Mon Sep 17 00:00:00 2001 From: Rob Mensching Date: Wed, 30 Mar 2022 10:35:02 -0700 Subject: Implement IWindowsInstallerDecompileExtensions Update Util extension to validate extension model and fix some small issues in MSI decompiling. Fixes 6367 --- .../BaseWindowsInstallerBackendBinderExtension.cs | 8 +- .../BaseWindowsInstallerDecompilerExtension.cs | 77 ++ .../Data/IWindowsInstallerDecompileContext.cs | 12 +- .../Data/IWindowsInstallerDecompileResult.cs | 15 +- .../DecompilerExtension.cs | 61 - .../IWindowsInstallerDecompilerExtension.cs | 30 +- .../Services/IWindowsInstallerDecompilerHelper.cs | 180 +++ src/ext/Util/Util.wixext.sln | 4 +- src/ext/Util/Util.wixext.v3.ncrunchsolution | 6 + .../WixToolsetTest.Util/UtilExtensionFixture.cs | 20 +- src/ext/Util/wixext/UtilCompiler.cs | 36 +- src/ext/Util/wixext/UtilConstants.cs | 21 + src/ext/Util/wixext/UtilDecompiler.cs | 1272 +++++++------------- src/ext/Util/wixext/UtilExtensionFactory.cs | 1 + src/ext/Util/wixext/UtilTableDefinitions.cs | 2 +- src/internal/WixBuildTools.TestSupport/Builder.cs | 88 ++ .../WixToolset.Core.Burn/BurnExtensionFactory.cs | 2 +- .../CommandLine/DecompilerSubcommand.cs | 7 +- .../Decompile/DecompileMsiOrMsmCommand.cs | 91 -- .../Decompile/Decompiler.cs | 662 +++++----- .../WindowsInstallerDecompilerHelper.cs | 133 ++ .../Unbind/UnbindDatabaseCommand.cs | 2 +- .../WindowsInstallerDecompileContext.cs | 4 + .../WindowsInstallerDecompileResult.cs | 2 +- .../WindowsInstallerDecompiler.cs | 85 +- .../WixToolsetCoreServiceProviderExtensions.cs | 5 +- .../Example.Extension/ExampleCompilerExtension.cs | 18 +- src/wix/test/Example.Extension/ExampleConstants.cs | 13 + .../Example.Extension/ExampleExtensionFactory.cs | 4 + src/wix/test/Example.Extension/ExampleRow.cs | 8 +- src/wix/test/Example.Extension/ExampleSymbol.cs | 7 + .../Example.Extension/ExampleSymbolDefinitions.cs | 1 + .../Example.Extension/ExampleTableDefinitions.cs | 3 +- .../ExampleWindowsInstallerBackendExtension.cs | 11 +- .../ExampleWindowsInstallerDecompilerExtension.cs | 42 + .../ExtensionFixture.cs | 43 +- .../DecompiledNestedDirSearchUnderRegSearch.wxs | 6 +- .../TestData/Class/DecompiledOldClassTableDef.wxs | 6 +- .../TestData/CustomTable/CustomTable-Expected.wxs | 4 +- .../TestData/DecompileNullComponent/Expected.wxs | 2 +- .../DecompileSingleFileCompressed/Expected.wxs | 2 +- .../DecompileSingleFileCompressed64/Expected.wxs | 2 +- .../ExampleExtension/Decompiled-Expected.xml | 19 + .../SequenceTables/DecompiledSequenceTables.wxs | 6 +- .../TestData/Shortcut/DecompiledShortcuts.wxs | 6 +- .../WixlibFixture.cs | 5 +- 46 files changed, 1644 insertions(+), 1390 deletions(-) create mode 100644 src/api/wix/WixToolset.Extensibility/BaseWindowsInstallerDecompilerExtension.cs delete mode 100644 src/api/wix/WixToolset.Extensibility/DecompilerExtension.cs create mode 100644 src/api/wix/WixToolset.Extensibility/Services/IWindowsInstallerDecompilerHelper.cs create mode 100644 src/ext/Util/Util.wixext.v3.ncrunchsolution delete mode 100644 src/wix/WixToolset.Core.WindowsInstaller/Decompile/DecompileMsiOrMsmCommand.cs create mode 100644 src/wix/WixToolset.Core.WindowsInstaller/ExtensibilityServices/WindowsInstallerDecompilerHelper.cs create mode 100644 src/wix/test/Example.Extension/ExampleConstants.cs create mode 100644 src/wix/test/Example.Extension/ExampleWindowsInstallerDecompilerExtension.cs create mode 100644 src/wix/test/WixToolsetTest.CoreIntegration/TestData/ExampleExtension/Decompiled-Expected.xml (limited to 'src') 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 { using System; using System.Collections.Generic; - using System.Linq; using WixToolset.Data; using WixToolset.Data.Symbols; using WixToolset.Data.WindowsInstaller; @@ -12,7 +11,7 @@ namespace WixToolset.Extensibility using WixToolset.Extensibility.Services; /// - /// Base class for creating a preprocessor extension. + /// Base class for creating a windows installer backend extension. /// public abstract class BaseWindowsInstallerBackendBinderExtension : IWindowsInstallerBackendBinderExtension { @@ -39,7 +38,10 @@ namespace WixToolset.Extensibility /// /// Creates a resolved cabinet result. /// - protected IResolvedCabinet CreateResolvedCabinet() => this.Context.ServiceProvider.GetService(); + protected IResolvedCabinet CreateResolvedCabinet() + { + return this.Context.ServiceProvider.GetService(); + } /// /// See 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 @@ +// 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. + +namespace WixToolset.Extensibility +{ + using System.Collections.Generic; + using WixToolset.Data; + using WixToolset.Data.WindowsInstaller; + using WixToolset.Extensibility.Data; + using WixToolset.Extensibility.Services; + + /// + /// Base class for creating a windows installer decompiler extensions. + /// + public abstract class BaseWindowsInstallerDecompilerExtension : IWindowsInstallerDecompilerExtension + { + /// + /// Context for use by the extension. + /// + protected IWindowsInstallerDecompileContext Context { get; private set; } + + /// + /// Messaging for use by the extension. + /// + protected IMessaging Messaging { get; private set; } + + /// + /// Decompiler helper for use by the extension. + /// + protected IWindowsInstallerDecompilerHelper DecompilerHelper { get; private set; } + + /// + /// See + /// + public virtual IReadOnlyCollection TableDefinitions { get; } + + /// + /// See + /// + public virtual void PreDecompile(IWindowsInstallerDecompileContext context) + { + this.Context = context; + + this.Messaging = context.ServiceProvider.GetService(); + + this.DecompilerHelper = context.ServiceProvider.GetService(); + } + + /// + /// See + /// + public virtual void PreDecompileTables(TableIndexedCollection tables) + { + } + + /// + /// See + /// + public virtual bool TryDecompileTable(Table table) + { + return false; + } + + /// + /// See + /// + public virtual void PostDecompileTables(TableIndexedCollection tables) + { + } + + /// + /// See + /// + public virtual void PostDecompile(IWindowsInstallerDecompileResult result) + { + } + } +} 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 using WixToolset.Data; /// - /// The context used to decompile Windows Installer packages. + /// The context used to decompile a Windows Installer database. /// public interface IWindowsInstallerDecompileContext { @@ -31,6 +31,16 @@ namespace WixToolset.Extensibility.Data /// IReadOnlyCollection Extensions { get; set; } + /// + /// Collection of extension data to use during decompiling. + /// + IReadOnlyCollection ExtensionData { get; set; } + + /// + /// Symbol definition creator used to load extension data. + /// + ISymbolDefinitionCreator SymbolDefinitionCreator { get; set; } + /// /// Gets or sets the folder where content is extracted. /// 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 using System.Xml.Linq; using WixToolset.Data; -#pragma warning disable 1591 // TODO: add documentation + /// + /// The result from decompiling a Windows Installer database. + /// public interface IWindowsInstallerDecompileResult { + /// + /// Decompiled document. + /// XDocument Document { get; set; } - IReadOnlyCollection ExtractedFilePaths { get; set; } + /// + /// Extracted paths. + /// + IList ExtractedFilePaths { get; set; } + /// + /// Decompiled platform. + /// Platform? Platform { get; set; } } } 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 @@ -// 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. - -namespace WixToolset.Extensibility -{ - using WixToolset.Data; - -#if BRING_BACK_LATER - /// - /// Base class for creating a decompiler extension. - /// - public abstract class DecompilerExtension : IDecompilerExtension - { - /// - /// Gets or sets the decompiler core for the extension. - /// - /// The decompiler core for the extension. - public IDecompilerCore Core { get; set; } - - /// - /// Gets the table definitions this extension decompiles. - /// - /// Table definitions this extension decompiles. - public virtual TableDefinitionCollection TableDefinitions { get; protected set; } - - /// - /// Gets the library that this decompiler wants removed from the decomipiled output. - /// - /// The table definitions to use while loading the library. - /// The library for this extension or null if there is no library to be removed. - public virtual Library GetLibraryToRemove(TableDefinitionCollection tableDefinitions) - { - return null; - } - - /// - /// Called at the beginning of the decompilation of a database. - /// - /// The collection of all tables. - public virtual void Initialize(TableIndexedCollection tables) - { - } - - /// - /// Decompiles an extension table. - /// - /// The table to decompile. - public virtual void DecompileTable(Table table) - { - this.Core.UnexpectedTable(table); - } - - /// - /// Finalize decompilation. - /// - /// The collection of all tables. - public virtual void Finish(TableIndexedCollection tables) - { - } - } -#endif -} 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 @@ namespace WixToolset.Extensibility { - using WixToolset.Data; + using System.Collections.Generic; using WixToolset.Data.WindowsInstaller; using WixToolset.Extensibility.Data; /// - /// Interface all binder extensions implement. + /// Interface all windows installer decompiler extensions implement. /// public interface IWindowsInstallerDecompilerExtension { + /// + /// Gets the table definitions this extension decompiles. + /// + /// Table definitions this extension decompiles. + IReadOnlyCollection TableDefinitions { get; } + /// /// Called before decompiling occurs. /// + /// Decompile context. void PreDecompile(IWindowsInstallerDecompileContext context); - // TODO: Redesign this interface to be useful. + /// + /// Called before decompiling occurs. + /// + /// The collection of all tables. + void PreDecompileTables(TableIndexedCollection tables); + + /// + /// Try to decompile an extension table. + /// + /// The table to decompile. + /// True if the table was decompiled, false otherwise. + bool TryDecompileTable(Table table); + + /// + /// After decompilation tables. + /// + /// The collection of all tables. + void PostDecompileTables(TableIndexedCollection tables); /// /// 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 @@ +// 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. + +namespace WixToolset.Extensibility.Services +{ + using System.Xml.Linq; + using WixToolset.Data.WindowsInstaller; + + /// + /// Interface provided to help Windows Installer decompiler extensions. + /// + public interface IWindowsInstallerDecompilerHelper + { + /// + /// Gets or sets the root element of the decompiled output. + /// + XElement RootElement { get; set; } + + /// + /// Creates an element from the standard WiX Toolset namespace and adds it to the root document. + /// + /// Name of the element to create and add. + /// Optional content to add to the new element. + /// Element in the standard namespace. + XElement AddElementToRoot(string name, params object[] content); + + /// + /// Creates an element with the specified name and adds it to the root document. + /// + /// Name of the element to create and add. + /// Optional content to add to the new element. + /// Element in the standard namespace. + XElement AddElementToRoot(XName name, params object[] content); + + /// + /// Adds an existing element to the root document. + /// + /// Element to add. + /// Same element provided. + XElement AddElementToRoot(XElement element); + + /// + /// Creates an element from the standard WiX Toolset namespace. + /// + /// Name of the element to create. + /// Optional content to add to the new element. + /// Element in the standard namespace. + XElement CreateElement(string name, params object[] content); + + /// + /// Get an element index by a row's table and primary keys. + /// + /// Row to get element. + /// Element indexed for the row or null if not found. + XElement GetIndexedElement(Row row); + + /// + /// Get an element index by table and primary key. + /// + /// Table name for indexed element. + /// Primary key for indexed element. + /// Element indexed for the table and primary key or null if not found. + XElement GetIndexedElement(string table, string primaryKey); + + /// + /// Get an element index by table and primary keys. + /// + /// Table name for indexed element. + /// Primary key for first column indexed element. + /// Primary key for second column indexed element. + /// Element indexed for the table and primary keys or null if not found. + XElement GetIndexedElement(string table, string primaryKey1, string primaryKey2); + + /// + /// Get an element index by table and primary keys. + /// + /// Table name for indexed element. + /// Primary key for first column indexed element. + /// Primary key for second column indexed element. + /// Primary key for third column indexed element. + /// Element indexed for the table and primary keys or null if not found. + XElement GetIndexedElement(string table, string primaryKey1, string primaryKey2, string primaryKey3); + + /// + /// Get an element index by table and primary keys. + /// + /// Table name for indexed element. + /// Primary keys for indexed element. + /// Element indexed for the table and primary keys or null if not found. + XElement GetIndexedElement(string table, string[] primaryKeys); + + /// + /// Try to get an element index by a row's table and primary keys. + /// + /// Row to get element. + /// Element indexed for the row. + /// True if the element was index otherwise false. + bool TryGetIndexedElement(Row row, out XElement element); + + /// + /// Try to get an element index by table name and primary key. + /// + /// Table name for indexed element. + /// Primary key for indexed element. + /// Element indexed for the table and primary key. + /// True if the element was index otherwise false. + bool TryGetIndexedElement(string table, string primaryKey, out XElement element); + + /// + /// Try to get an element index by table name and primary keys. + /// + /// Table name for indexed element. + /// First column's primary key for indexed element. + /// Second column's primary key for indexed element. + /// Element indexed for the table and primary key. + /// True if the element was index otherwise false. + bool TryGetIndexedElement(string table, string primaryKey1, string primaryKey2, out XElement element); + + /// + /// Try to get an element index by table name and primary keys. + /// + /// Table name for indexed element. + /// First column's primary key for indexed element. + /// Second column's primary key for indexed element. + /// Third column's primary key for indexed element. + /// Element indexed for the table and primary key. + /// True if the element was index otherwise false. + bool TryGetIndexedElement(string table, string primaryKey1, string primaryKey2, string primaryKey3, out XElement element); + + /// + /// Try to get an element index by table name and primary keys. + /// + /// Table name for indexed element. + /// Primary keys for indexed element. + /// Element indexed for the table and primary key. + /// True if the element was index otherwise false. + bool TryGetIndexedElement(string table, string[] primaryKeys, out XElement element); + + /// + /// Index an element by a row's table and primary keys. + /// + /// Row to index element. + /// Element to index. + void IndexElement(Row row, XElement element); + + /// + /// Index an element by table and primary key. + /// + /// Table name to index element. + /// Primary key to index element. + /// Element to index. + void IndexElement(string table, string primaryKey, XElement element); + + /// + /// Index an element by table and primary keys. + /// + /// Table name to index element. + /// First column's primary key to index element. + /// Second column's primary key to index element. + /// Element to index. + void IndexElement(string table, string primaryKey1, string primaryKey2, XElement element); + + /// + /// Index an element by table and primary keys. + /// + /// Table name to index element. + /// First column's primary key to index element. + /// Second column's primary key to index element. + /// Third column's primary key to index element. + /// Element to index. + void IndexElement(string table, string primaryKey1, string primaryKey2, string primaryKey3, XElement element); + + /// + /// Index an element by table and primary keys. + /// + /// Table name to index element. + /// Column's primary key to index element. + /// Element to index. + void IndexElement(string table, string[] primaryKeys, XElement element); + } +} 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 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 16 -VisualStudioVersion = 16.0.30204.135 +# Visual Studio Version 17 +VisualStudioVersion = 17.1.32228.430 MinimumVisualStudioVersion = 15.0.26124.0 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "utilbe", "be\utilbe.vcxproj", "{630C1EE7-2517-4A8C-83E3-DA1150308B58}" EndProject 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 @@ + + + True + True + + \ 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 using System.Linq; using WixBuildTools.TestSupport; using WixToolset.Core.TestPackage; - using WixToolset.Data; - using WixToolset.Data.Symbols; using WixToolset.Util; using Xunit; @@ -55,6 +53,18 @@ namespace WixToolsetTest.Util }, results.OrderBy(s => s).ToArray()); } + [Fact] + public void CanRoundtripFileShare() + { + var folder = TestData.Get(@"TestData", "UsingFileShare"); + var build = new Builder(folder, typeof(UtilExtensionFactory), new[] { folder }); + var output = Path.Combine(folder, "decompile.xml"); + + build.BuildAndDecompileAndBuild(Build, Decompile, output); + File.Exists(output); + } + + [Fact] public void CanBuildCloseApplication() { @@ -313,5 +323,11 @@ namespace WixToolsetTest.Util var result = WixRunner.Execute(newArgs.ToArray()); result.AssertSuccess(); } + + private static void Decompile(string[] args) + { + var result = WixRunner.Execute(args); + result.AssertSuccess(); + } } } 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 /// /// The compiler for the WiX Toolset Utility Extension. /// - public sealed class UtilCompiler : BaseCompilerExtension + internal sealed class UtilCompiler : BaseCompilerExtension { // user creation attributes definitions (from sca.h) internal const int UserDontExpirePasswrd = 0x00000001; @@ -37,7 +37,7 @@ namespace WixToolset.Util private static readonly Regex FindPropertyBrackets = new Regex(@"\[(?!\\|\])|(? "http://wixtoolset.org/schemas/v4/wxs/util"; + public override XNamespace Namespace => UtilConstants.Namespace; /// /// Types of Internet shortcuts. @@ -94,13 +94,10 @@ namespace WixToolset.Util var createFolderId = context["DirectoryId"]; var createFolderComponentId = context["ComponentId"]; - // If this doesn't parse successfully, something really odd is going on, so let the exception get thrown - var createFolderWin64 = Boolean.Parse(context["Win64"]); - switch (element.Name.LocalName) { case "PermissionEx": - this.ParsePermissionExElement(intermediate, section, element, createFolderId, createFolderComponentId, createFolderWin64, "CreateFolder"); + this.ParsePermissionExElement(intermediate, section, element, createFolderId, createFolderComponentId, "CreateFolder"); break; default: this.ParseHelper.UnexpectedElement(parentElement, element); @@ -159,16 +156,13 @@ namespace WixToolset.Util var fileId = context["FileId"]; var fileComponentId = context["ComponentId"]; - // If this doesn't parse successfully, something really odd is going on, so let the exception get thrown - var fileWin64 = Boolean.Parse(context["Win64"]); - switch (element.Name.LocalName) { case "PerfCounter": this.ParsePerfCounterElement(intermediate, section, element, fileComponentId, fileId); break; case "PermissionEx": - this.ParsePermissionExElement(intermediate, section, element, fileId, fileComponentId, fileWin64, "File"); + this.ParsePermissionExElement(intermediate, section, element, fileId, fileComponentId, "File"); break; case "PerfCounterManifest": this.ParsePerfCounterManifestElement(intermediate, section, element, fileComponentId, fileId); @@ -177,7 +171,7 @@ namespace WixToolset.Util this.ParseEventManifestElement(intermediate, section, element, fileComponentId, fileId); break; case "FormatFile": - this.ParseFormatFileElement(intermediate, section, element, fileId, fileWin64); + this.ParseFormatFileElement(intermediate, section, element, fileId); break; default: this.ParseHelper.UnexpectedElement(parentElement, element); @@ -296,13 +290,10 @@ namespace WixToolset.Util var registryId = context["RegistryId"]; var registryComponentId = context["ComponentId"]; - // If this doesn't parse successfully, something really odd is going on, so let the exception get thrown - var registryWin64 = Boolean.Parse(context["Win64"]); - switch (element.Name.LocalName) { case "PermissionEx": - this.ParsePermissionExElement(intermediate, section, element, registryId, registryComponentId, registryWin64, "Registry"); + this.ParsePermissionExElement(intermediate, section, element, registryId, registryComponentId, "Registry"); break; default: this.ParseHelper.UnexpectedElement(parentElement, element); @@ -314,13 +305,10 @@ namespace WixToolset.Util var serviceInstallName = context["ServiceInstallName"]; var serviceInstallComponentId = context["ServiceInstallComponentId"]; - // If this doesn't parse successfully, something really odd is going on, so let the exception get thrown - var serviceInstallWin64 = Boolean.Parse(context["Win64"]); - switch (element.Name.LocalName) { case "PermissionEx": - this.ParsePermissionExElement(intermediate, section, element, serviceInstallId, serviceInstallComponentId, serviceInstallWin64, "ServiceInstall"); + this.ParsePermissionExElement(intermediate, section, element, serviceInstallId, serviceInstallComponentId, "ServiceInstall"); break; case "ServiceConfig": this.ParseServiceConfigElement(intermediate, section, element, serviceInstallComponentId, "ServiceInstall", serviceInstallName); @@ -536,7 +524,6 @@ namespace WixToolset.Util private void ParseComponentSearchRefElement(Intermediate intermediate, IntermediateSection section, XElement element) { var sourceLineNumbers = this.ParseHelper.GetSourceLineNumbers(element); - string refId = null; foreach (var attrib in element.Attributes()) { @@ -545,7 +532,7 @@ namespace WixToolset.Util switch (attrib.Name.LocalName) { case "Id": - refId = this.ParseHelper.GetAttributeIdentifierValue(sourceLineNumbers, attrib); + var refId = this.ParseHelper.GetAttributeIdentifierValue(sourceLineNumbers, attrib); this.ParseHelper.CreateSimpleReference(section, sourceLineNumbers, SymbolDefinitions.WixComponentSearch, refId); break; default: @@ -2284,7 +2271,7 @@ namespace WixToolset.Util /// Element to parse. /// Identifier of referenced file. /// Flag to determine whether the component is 64-bit. - private void ParseFormatFileElement(Intermediate intermediate, IntermediateSection section, XElement element, string fileId, bool win64) + private void ParseFormatFileElement(Intermediate intermediate, IntermediateSection section, XElement element, string fileId) { var sourceLineNumbers = this.ParseHelper.GetSourceLineNumbers(element); string binaryId = null; @@ -2415,7 +2402,6 @@ namespace WixToolset.Util ComponentRef = componentId, }); } - } this.ParseHelper.CreateCustomActionReference(sourceLineNumbers, section, "Wix4ConfigureEventManifestRegister", this.Context.Platform, CustomActionPlatforms.X86 | CustomActionPlatforms.X64 | CustomActionPlatforms.ARM64); @@ -2435,7 +2421,7 @@ namespace WixToolset.Util /// Identifier of component, used to determine install state. /// Flag to determine whether the component is 64-bit. /// Name of table that contains objectId. - private void ParsePermissionExElement(Intermediate intermediate, IntermediateSection section, XElement element, string objectId, string componentId, bool win64, string tableName) + private void ParsePermissionExElement(Intermediate intermediate, IntermediateSection section, XElement element, string objectId, string componentId, string tableName) { var sourceLineNumbers = this.ParseHelper.GetSourceLineNumbers(element); var bits = new BitArray(32); @@ -2623,7 +2609,7 @@ namespace WixToolset.Util if (null == id) { - id = this.ParseHelper.CreateIdentifier("wps", variable, condition, after, (productCode == null ? upgradeCode : productCode), attributes.ToString()); + id = this.ParseHelper.CreateIdentifier("wps", variable, condition, after, productCode ?? upgradeCode, attributes.ToString()); } 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 @@ namespace WixToolset.Util { + using System.Xml.Linq; + /// /// Constants used by Utility Extension. /// internal static class UtilConstants { + internal static readonly XNamespace Namespace = "http://wixtoolset.org/schemas/v4/wxs/util"; + + internal static readonly XName CloseApplicationName = Namespace + "CloseApplication"; + internal static readonly XName EventManifestName = Namespace + "EventManifest"; + internal static readonly XName FileShareName = Namespace + "FileShare"; + internal static readonly XName FileSharePermissionName = Namespace + "FileSharePermission"; + internal static readonly XName GroupName = Namespace + "Group"; + internal static readonly XName GroupRefName = Namespace + "GroupRef"; + internal static readonly XName InternetShortcutName = Namespace + "InternetShortcut"; + internal static readonly XName PerfCounterName = Namespace + "PerfCounter"; + internal static readonly XName PerfCounterManifestName = Namespace + "PerfCounterManifest"; + internal static readonly XName PermissionExName = Namespace + "PermissionEx"; + internal static readonly XName RemoveFolderExName = Namespace + "RemoveFolderEx"; + internal static readonly XName RestartResourceName = Namespace + "RestartResource"; + internal static readonly XName ServiceConfigName = Namespace + "ServiceConfig"; + internal static readonly XName UserName = Namespace + "User"; + internal static readonly XName XmlConfigName = Namespace + "XmlConfig"; + internal static readonly XName XmlFileName = Namespace + "XmlFile"; + internal static readonly string[] FilePermissions = { "Read", "Write", "Append", "ReadExtendedAttributes", "WriteExtendedAttributes", "Execute", null, "ReadAttributes", "WriteAttributes" }; internal static readonly string[] FolderPermissions = { "Read", "CreateFile", "CreateChild", "ReadExtendedAttributes", "WriteExtendedAttributes", "Traverse", "DeleteChild", "ReadAttributes", "WriteAttributes" }; 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 @@ // 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. -namespace WixToolset.Extensions +namespace WixToolset.Util { -#if TODO_CONSIDER_DECOMPILER using System; using System.IO; using System.Text; using System.Collections; using System.Diagnostics; - using System.Globalization; - using Util = WixToolset.Extensions.Serialize.Util; using WixToolset.Data; using WixToolset.Extensibility; - using Wix = WixToolset.Data.Serialize; + using WixToolset.Data.WindowsInstaller; + using System.Collections.Generic; + using System.Xml.Linq; + using WixToolset.Util.Symbols; /// /// The decompiler for the WiX Toolset Utility Extension. /// - public sealed class UtilDecompiler : DecompilerExtension + internal sealed class UtilDecompiler : BaseWindowsInstallerDecompilerExtension { - /// - /// Creates a decompiler for Utility Extension. - /// - public UtilDecompiler() - { - this.TableDefinitions = UtilExtensionData.GetExtensionTableDefinitions(); - } - - /// - /// Get the extensions library to be removed. - /// - /// Table definitions for library. - /// Library to remove from decompiled output. - public override Library GetLibraryToRemove(TableDefinitionCollection tableDefinitions) - { - return UtilExtensionData.GetExtensionLibrary(tableDefinitions); - } + public override IReadOnlyCollection TableDefinitions => UtilTableDefinitions.All; /// /// Called at the beginning of the decompilation of a database. /// /// The collection of all tables. - public override void Initialize(TableIndexedCollection tables) + public override void PreDecompileTables(TableIndexedCollection tables) { this.CleanupSecureCustomProperties(tables); this.CleanupInternetShortcutRemoveFileTables(tables); @@ -61,24 +45,22 @@ namespace WixToolset.Extensions /// The collection of all tables. private void CleanupSecureCustomProperties(TableIndexedCollection tables) { - Table propertyTable = tables["Property"]; + var propertyTable = tables["Property"]; if (null != propertyTable) { - foreach (Row row in propertyTable.Rows) + foreach (var row in propertyTable.Rows) { if ("SecureCustomProperties" == row[0].ToString()) { - StringBuilder remainingProperties = new StringBuilder(); - string[] secureCustomProperties = row[1].ToString().Split(';'); - foreach (string property in secureCustomProperties) + var remainingProperties = new StringBuilder(); + var secureCustomProperties = row[1].ToString().Split(';'); + foreach (var property in secureCustomProperties) { if (property.StartsWith("WIX_SUITE_", StringComparison.Ordinal) || property.StartsWith("WIX_DIR_", StringComparison.Ordinal) || property.StartsWith("WIX_ACCOUNT_", StringComparison.Ordinal)) { - Wix.PropertyRef propertyRef = new Wix.PropertyRef(); - propertyRef.Id = property; - this.Core.RootElement.AddChild(propertyRef); + this.DecompilerHelper.AddElementToRoot("PropertyRef", new XAttribute("Id", property)); } else { @@ -104,21 +86,21 @@ namespace WixToolset.Extensions private void CleanupInternetShortcutRemoveFileTables(TableIndexedCollection tables) { // index the WixInternetShortcut table - Table wixInternetShortcutTable = tables["WixInternetShortcut"]; - Hashtable wixInternetShortcuts = new Hashtable(); + var wixInternetShortcutTable = tables["WixInternetShortcut"]; + var wixInternetShortcuts = new Hashtable(); if (null != wixInternetShortcutTable) { - foreach (Row row in wixInternetShortcutTable.Rows) + foreach (var row in wixInternetShortcutTable.Rows) { - wixInternetShortcuts.Add(row.GetPrimaryKey(DecompilerConstants.PrimaryKeyDelimiter), row); + wixInternetShortcuts.Add(row.GetPrimaryKey(), row); } } // remove the RemoveFile rows with primary keys that match the WixInternetShortcut table's - Table removeFileTable = tables["RemoveFile"]; + var removeFileTable = tables["RemoveFile"]; if (null != removeFileTable) { - for (int i = removeFileTable.Rows.Count - 1; 0 <= i; i--) + for (var i = removeFileTable.Rows.Count - 1; 0 <= i; i--) { if (null != wixInternetShortcuts[removeFileTable.Rows[i][0]]) { @@ -132,69 +114,86 @@ namespace WixToolset.Extensions /// Decompiles an extension table. /// /// The table to decompile. - public override void DecompileTable(Table table) + public override bool TryDecompileTable(Table table) { switch (table.Name) { case "WixCloseApplication": + case "Wix4CloseApplication": this.DecompileWixCloseApplicationTable(table); break; case "WixRemoveFolderEx": + case "Wix4RemoveFolderEx": this.DecompileWixRemoveFolderExTable(table); break; case "WixRestartResource": + case "Wix4RestartResource": this.DecompileWixRestartResourceTable(table); break; case "FileShare": + case "Wix4FileShare": this.DecompileFileShareTable(table); break; case "FileSharePermissions": + case "Wix4FileSharePermissions": this.DecompileFileSharePermissionsTable(table); break; case "WixInternetShortcut": + case "Wix4InternetShortcut": this.DecompileWixInternetShortcutTable(table); break; case "Group": + case "Wix4Group": this.DecompileGroupTable(table); break; case "Perfmon": + case "Wix4Perfmon": this.DecompilePerfmonTable(table); break; case "PerfmonManifest": + case "Wix4PerfmonManifest": this.DecompilePerfmonManifestTable(table); break; case "EventManifest": + case "Wix4EventManifest": this.DecompileEventManifestTable(table); break; case "SecureObjects": + case "Wix4SecureObjects": this.DecompileSecureObjectsTable(table); break; case "ServiceConfig": + case "Wix4ServiceConfig": this.DecompileServiceConfigTable(table); break; case "User": + case "Wix4User": this.DecompileUserTable(table); break; case "UserGroup": + case "Wix4UserGroup": this.DecompileUserGroupTable(table); break; case "XmlConfig": + case "Wix4XmlConfig": this.DecompileXmlConfigTable(table); break; case "XmlFile": + case "Wix4XmlFile": // XmlFile decompilation has been moved to FinalizeXmlFileTable function break; default: - base.DecompileTable(table); - break; + return false; } + + return true; } /// /// Finalize decompilation. /// /// The collection of all tables. - public override void Finish(TableIndexedCollection tables) + public override void PostDecompileTables(TableIndexedCollection tables) { this.FinalizePerfmonTable(tables); this.FinalizePerfmonManifestTable(tables); @@ -211,49 +210,21 @@ namespace WixToolset.Extensions /// The table to decompile. private void DecompileWixCloseApplicationTable(Table table) { - foreach (Row row in table.Rows) + foreach (var row in table.Rows) { - Util.CloseApplication closeApplication = new Util.CloseApplication(); - - closeApplication.Id = (string)row[0]; - - closeApplication.Target = (string)row[1]; - - if (null != row[2]) - { - closeApplication.Description = (string)row[2]; - } - - if (null != row[3]) - { - closeApplication.Content = (string)row[3]; - } - - // set defaults - closeApplication.CloseMessage = Util.YesNoType.no; - closeApplication.RebootPrompt = Util.YesNoType.yes; - closeApplication.ElevatedCloseMessage = Util.YesNoType.no; - - if (null != row[4]) - { - int attribute = (int)row[4]; - - closeApplication.CloseMessage = (0x1 == (attribute & 0x1)) ? Util.YesNoType.yes : Util.YesNoType.no; - closeApplication.RebootPrompt = (0x2 == (attribute & 0x2)) ? Util.YesNoType.yes : Util.YesNoType.no; - closeApplication.ElevatedCloseMessage = (0x4 == (attribute & 0x4)) ? Util.YesNoType.yes : Util.YesNoType.no; - } - - if (null != row[5]) - { - closeApplication.Sequence = (int)row[5]; - } - - if (null != row[6]) - { - closeApplication.Property = (string)row[6]; - } - - this.Core.RootElement.AddChild(closeApplication); + var attribute = row.FieldAsNullableInteger(4) ?? 0x2; + + this.DecompilerHelper.AddElementToRoot(UtilConstants.CloseApplicationName, + new XAttribute("Id", row.FieldAsString(0)), + new XAttribute("Target", row.FieldAsString(1)), + AttributeIfNotNull("Description", row, 2), + AttributeIfNotNull("Content", row, 3), + AttributeIfNotNull("CloseMessage", 0x1 == (attribute & 0x1)), + AttributeIfNotNull("RebootPrompt", 0x2 == (attribute & 0x2)), + AttributeIfNotNull("ElevatedCloseMessage", 0x4 == (attribute & 0x4)), + NumericAttributeIfNotNull("Sequence", row, 5), + NumericAttributeIfNotNull("Property", row, 6) + ); } } @@ -263,43 +234,45 @@ namespace WixToolset.Extensions /// The table to decompile. private void DecompileWixRemoveFolderExTable(Table table) { - foreach (Row row in table.Rows) + foreach (var row in table.Rows) { - // Set the Id even if auto-generated previously. - Util.RemoveFolderEx removeFolder = new Util.RemoveFolderEx(); - removeFolder.Id = (string)row[0]; - removeFolder.Property = (string)row[2]; - - int installMode = (int)row[3]; - switch ((UtilCompiler.WixRemoveFolderExOn)installMode) + var on = String.Empty; + var installMode = row.FieldAsInteger(3); + switch (installMode) { - case UtilCompiler.WixRemoveFolderExOn.Install: - removeFolder.On = Util.RemoveFolderEx.OnType.install; + case (int)WixRemoveFolderExInstallMode.Install: + on = "install"; break; - - case UtilCompiler.WixRemoveFolderExOn.Uninstall: - removeFolder.On = Util.RemoveFolderEx.OnType.uninstall; + + case (int)WixRemoveFolderExInstallMode.Uninstall: + on = "uninstall"; break; - - case UtilCompiler.WixRemoveFolderExOn.Both: - removeFolder.On = Util.RemoveFolderEx.OnType.both; + + case (int)WixRemoveFolderExInstallMode.Both: + on = "both"; break; default: - this.Core.OnMessage(WixWarnings.UnrepresentableColumnValue(row.SourceLineNumbers, table.Name, "InstallMode", installMode)); + this.Messaging.Write(WarningMessages.UnrepresentableColumnValue(row.SourceLineNumbers, table.Name, "InstallMode", installMode)); break; } + var removeFolder = new XElement(UtilConstants.RemoveFolderExName, + AttributeIfNotNull("Id", row, 0), + AttributeIfNotNull("Property", row, 2), + AttributeIfNotNull("On", on) + ); + // Add to the appropriate Component or section element. - string componentId = (string)row[1]; - Wix.Component component = (Wix.Component)this.Core.GetIndexedElement("Component", componentId); - if (null != component) + var componentId = row.FieldAsString(1); + + if (this.DecompilerHelper.TryGetIndexedElement("Component", componentId, out var component)) { - component.AddChild(removeFolder); + component.Add(removeFolder); } else { - this.Core.OnMessage(WixWarnings.ExpectedForeignRow(row.SourceLineNumbers, table.Name, row.GetPrimaryKey(DecompilerConstants.PrimaryKeyDelimiter), "Component_", componentId, "Component")); + this.Messaging.Write(WarningMessages.ExpectedForeignRow(row.SourceLineNumbers, table.Name, row.GetPrimaryKey(), "Component_", componentId, "Component")); } } } @@ -310,53 +283,51 @@ namespace WixToolset.Extensions /// The table to decompile. private void DecompileWixRestartResourceTable(Table table) { - foreach (Row row in table.Rows) + foreach (var row in table.Rows) { - // Set the Id even if auto-generated previously. - Util.RestartResource restartResource = new Util.RestartResource(); - restartResource.Id = (string)row[0]; + var restartResource = new XElement(UtilConstants.RestartResourceName, + new XAttribute("Id", row.FieldAsString(0))); // Determine the resource type and set accordingly. - string resource = (string)row[2]; - int attributes = (int)row[3]; - UtilCompiler.WixRestartResourceAttributes type = (UtilCompiler.WixRestartResourceAttributes)(attributes & (int)UtilCompiler.WixRestartResourceAttributes.TypeMask); + var resource = row.FieldAsString(2); + var attributes = row.FieldAsInteger(3); + var type = (WixRestartResourceAttributes)attributes; switch (type) { - case UtilCompiler.WixRestartResourceAttributes.Filename: - restartResource.Path = resource; + case WixRestartResourceAttributes.Filename: + restartResource.Add(new XAttribute("Path", resource)); break; - case UtilCompiler.WixRestartResourceAttributes.ProcessName: - restartResource.ProcessName = resource; + case WixRestartResourceAttributes.ProcessName: + restartResource.Add(new XAttribute("ProcessName", resource)); break; - case UtilCompiler.WixRestartResourceAttributes.ServiceName: - restartResource.ServiceName = resource; + case WixRestartResourceAttributes.ServiceName: + restartResource.Add(new XAttribute("ServiceName", resource)); break; default: - this.Core.OnMessage(WixWarnings.UnrepresentableColumnValue(row.SourceLineNumbers, table.Name, "Attributes", attributes)); + this.Messaging.Write(WarningMessages.UnrepresentableColumnValue(row.SourceLineNumbers, table.Name, "Attributes", attributes)); break; } // Add to the appropriate Component or section element. - string componentId = (string)row[1]; + var componentId = row.FieldAsString(1); if (!String.IsNullOrEmpty(componentId)) { - Wix.Component component = (Wix.Component)this.Core.GetIndexedElement("Component", componentId); - if (null != component) + if (this.DecompilerHelper.TryGetIndexedElement("Component", componentId, out var component)) { - component.AddChild(restartResource); + component.Add(restartResource); } else { - this.Core.OnMessage(WixWarnings.ExpectedForeignRow(row.SourceLineNumbers, table.Name, row.GetPrimaryKey(DecompilerConstants.PrimaryKeyDelimiter), "Component_", componentId, "Component")); + this.Messaging.Write(WarningMessages.ExpectedForeignRow(row.SourceLineNumbers, table.Name, row.GetPrimaryKey(), "Component_", componentId, "Component")); } } else { - this.Core.RootElement.AddChild(restartResource); + this.DecompilerHelper.AddElementToRoot(restartResource); } } } @@ -367,33 +338,28 @@ namespace WixToolset.Extensions /// The table to decompile. private void DecompileFileShareTable(Table table) { - foreach (Row row in table.Rows) + foreach (var row in table.Rows) { - Util.FileShare fileShare = new Util.FileShare(); - - fileShare.Id = (string)row[0]; - - fileShare.Name = (string)row[1]; - - if (null != row[3]) - { - fileShare.Description = (string)row[3]; - } + var fileShare = new XElement(UtilConstants.FileShareName, + new XAttribute("Id", row.FieldAsString(0)), + new XAttribute("Name", row.FieldAsString(1)), + AttributeIfNotNull("Description", row, 3) + ); // the Directory_ column is set by the parent Component // the User_ and Permissions columns are deprecated - Wix.Component component = (Wix.Component)this.Core.GetIndexedElement("Component", (string)row[2]); - if (null != component) + if (this.DecompilerHelper.TryGetIndexedElement("Component", row.FieldAsString(2), out var component)) { - component.AddChild(fileShare); + component.Add(fileShare); } else { - this.Core.OnMessage(WixWarnings.ExpectedForeignRow(row.SourceLineNumbers, table.Name, row.GetPrimaryKey(DecompilerConstants.PrimaryKeyDelimiter), "Component_", (string)row[2], "Component")); + this.Messaging.Write(WarningMessages.ExpectedForeignRow(row.SourceLineNumbers, table.Name, row.GetPrimaryKey(), "Component_", (string)row[2], "Component")); } - this.Core.IndexElement(row, fileShare); + + this.DecompilerHelper.IndexElement(row, fileShare); } } @@ -403,111 +369,21 @@ namespace WixToolset.Extensions /// The table to decompile. private void DecompileFileSharePermissionsTable(Table table) { - foreach (Row row in table.Rows) + foreach (var row in table.Rows) { - Util.FileSharePermission fileSharePermission = new Util.FileSharePermission(); - - fileSharePermission.User = (string)row[1]; - - string[] specialPermissions = UtilConstants.FolderPermissions; - int permissions = (int)row[2]; - for (int i = 0; i < 32; i++) - { - if (0 != ((permissions >> i) & 1)) - { - string name = null; + var fileSharePermission = new XElement(UtilConstants.FileSharePermissionName, + new XAttribute("User", row.FieldAsString(1))); - if (16 > i && specialPermissions.Length > i) - { - name = specialPermissions[i]; - } - else if (28 > i && UtilConstants.StandardPermissions.Length > (i - 16)) - { - name = UtilConstants.StandardPermissions[i - 16]; - } - else if (0 <= (i - 28) && UtilConstants.GenericPermissions.Length > (i - 28)) - { - name = UtilConstants.GenericPermissions[i - 28]; - } - - if (null == name) - { - this.Core.OnMessage(WixWarnings.UnknownPermission(row.SourceLineNumbers, row.Table.Name, row.GetPrimaryKey(DecompilerConstants.PrimaryKeyDelimiter), i)); - } - else - { - switch (name) - { - case "ChangePermission": - fileSharePermission.ChangePermission = Util.YesNoType.yes; - break; - case "CreateChild": - fileSharePermission.CreateChild = Util.YesNoType.yes; - break; - case "CreateFile": - fileSharePermission.CreateFile = Util.YesNoType.yes; - break; - case "Delete": - fileSharePermission.Delete = Util.YesNoType.yes; - break; - case "DeleteChild": - fileSharePermission.DeleteChild = Util.YesNoType.yes; - break; - case "GenericAll": - fileSharePermission.GenericAll = Util.YesNoType.yes; - break; - case "GenericExecute": - fileSharePermission.GenericExecute = Util.YesNoType.yes; - break; - case "GenericRead": - fileSharePermission.GenericRead = Util.YesNoType.yes; - break; - case "GenericWrite": - fileSharePermission.GenericWrite = Util.YesNoType.yes; - break; - case "Read": - fileSharePermission.Read = Util.YesNoType.yes; - break; - case "ReadAttributes": - fileSharePermission.ReadAttributes = Util.YesNoType.yes; - break; - case "ReadExtendedAttributes": - fileSharePermission.ReadExtendedAttributes = Util.YesNoType.yes; - break; - case "ReadPermission": - fileSharePermission.ReadPermission = Util.YesNoType.yes; - break; - case "Synchronize": - fileSharePermission.Synchronize = Util.YesNoType.yes; - break; - case "TakeOwnership": - fileSharePermission.TakeOwnership = Util.YesNoType.yes; - break; - case "Traverse": - fileSharePermission.Traverse = Util.YesNoType.yes; - break; - case "WriteAttributes": - fileSharePermission.WriteAttributes = Util.YesNoType.yes; - break; - case "WriteExtendedAttributes": - fileSharePermission.WriteExtendedAttributes = Util.YesNoType.yes; - break; - default: - Debug.Fail(String.Format("Unknown permission '{0}'.", name)); - break; - } - } - } - } + this.AddPermissionAttributes(fileSharePermission, row, 2, UtilConstants.FolderPermissions); - Util.FileShare fileShare = (Util.FileShare)this.Core.GetIndexedElement("FileShare", (string)row[0]); - if (null != fileShare) + if (this.DecompilerHelper.TryGetIndexedElement("Wix4FileShare", row.FieldAsString(0), out var fileShare) || + this.DecompilerHelper.TryGetIndexedElement("FileShare", row.FieldAsString(0), out fileShare)) { - fileShare.AddChild(fileSharePermission); + fileShare.Add(fileSharePermission); } else { - this.Core.OnMessage(WixWarnings.ExpectedForeignRow(row.SourceLineNumbers, table.Name, row.GetPrimaryKey(DecompilerConstants.PrimaryKeyDelimiter), "FileShare_", (string)row[0], "FileShare")); + this.Messaging.Write(WarningMessages.ExpectedForeignRow(row.SourceLineNumbers, table.Name, row.GetPrimaryKey(), "FileShare_", (string)row[0], "Wix4FileShare")); } } } @@ -518,25 +394,18 @@ namespace WixToolset.Extensions /// The table to decompile. private void DecompileGroupTable(Table table) { - foreach (Row row in table.Rows) + foreach (var row in table.Rows) { - Util.Group group = new Util.Group(); - - group.Id = (string)row[0]; - if (null != row[1]) { - this.Core.OnMessage(WixWarnings.UnrepresentableColumnValue(row.SourceLineNumbers, table.Name, "Component_", (string)row[1])); + this.Messaging.Write(WarningMessages.UnrepresentableColumnValue(row.SourceLineNumbers, table.Name, "Component_", (string)row[1])); } - group.Name = (string)row[2]; - - if (null != row[3]) - { - group.Domain = (string)row[3]; - } - - this.Core.RootElement.AddChild(group); + this.DecompilerHelper.AddElementToRoot(UtilConstants.GroupName, + new XAttribute("Id", row.FieldAsString(0)), + new XAttribute("Name", row.FieldAsString(1)), + AttributeIfNotNull("Domain", row, 3) + ); } } @@ -546,39 +415,41 @@ namespace WixToolset.Extensions /// The table to decompile. private void DecompileWixInternetShortcutTable(Table table) { - foreach (Row row in table.Rows) + foreach (var row in table.Rows) { - Util.InternetShortcut internetShortcut = new Util.InternetShortcut(); - internetShortcut.Id = (string)row[0]; - internetShortcut.Directory = (string)row[2]; - // remove .lnk/.url extension because compiler extension adds it back for us - internetShortcut.Name = Path.ChangeExtension((string)row[3], null); - internetShortcut.Target = (string)row[4]; - internetShortcut.IconFile = (string)row[6]; - internetShortcut.IconIndex = (int)row[7]; - - UtilCompiler.InternetShortcutType shortcutType = (UtilCompiler.InternetShortcutType)row[5]; + var type = String.Empty; + var shortcutType = (UtilCompiler.InternetShortcutType)row.FieldAsInteger(5); switch (shortcutType) { case UtilCompiler.InternetShortcutType.Link: - internetShortcut.Type = Util.InternetShortcut.TypeType.link; + type = "link"; break; case UtilCompiler.InternetShortcutType.Url: - internetShortcut.Type = Util.InternetShortcut.TypeType.url; + type = "url"; break; } - Wix.Component component = (Wix.Component)this.Core.GetIndexedElement("Component", (string)row[1]); - if (null != component) + var internetShortcut = new XElement(UtilConstants.InternetShortcutName, + new XAttribute("Id", row.FieldAsString(0)), + new XAttribute("Directory", row.FieldAsString(2)), + new XAttribute("Name", Path.GetFileNameWithoutExtension(row.FieldAsString(3))), // remove .lnk/.url extension because compiler extension adds it back for us + new XAttribute("Type", type), + new XAttribute("Target", row.FieldAsString(4)), + new XAttribute("IconFile", row.FieldAsString(6)), + NumericAttributeIfNotNull("IconIndex", row, 7) + ); + + var componentId = row.FieldAsString(1); + if (this.DecompilerHelper.TryGetIndexedElement("Component", componentId, out var component)) { - component.AddChild(internetShortcut); + component.Add(internetShortcut); } else { - this.Core.OnMessage(WixWarnings.ExpectedForeignRow(row.SourceLineNumbers, table.Name, row.GetPrimaryKey(DecompilerConstants.PrimaryKeyDelimiter), "Component_", (string)row[1], "Component")); + this.Messaging.Write(WarningMessages.ExpectedForeignRow(row.SourceLineNumbers, table.Name, row.GetPrimaryKey(), "Component_", componentId, "Component")); } - this.Core.IndexElement(row, internetShortcut); + this.DecompilerHelper.IndexElement(row, internetShortcut); } } @@ -588,54 +459,48 @@ namespace WixToolset.Extensions /// The table to decompile. private void DecompilePerfmonTable(Table table) { - foreach (Row row in table.Rows) + foreach (var row in table.Rows) { - Util.PerfCounter perfCounter = new Util.PerfCounter(); - - perfCounter.Name = (string)row[2]; - - this.Core.IndexElement(row, perfCounter); + this.DecompilerHelper.IndexElement(row, new XElement(UtilConstants.PerfCounterName, new XAttribute("Name", row.FieldAsString(2)))); } } - + /// /// Decompile the PerfmonManifest table. /// /// The table to decompile. private void DecompilePerfmonManifestTable(Table table) { - foreach (Row row in table.Rows) + foreach (var row in table.Rows) { - Util.PerfCounterManifest perfCounterManifest = new Util.PerfCounterManifest(); - - perfCounterManifest.ResourceFileDirectory = (string)row[2]; - - this.Core.IndexElement(row, perfCounterManifest); + this.DecompilerHelper.IndexElement(row, new XElement(UtilConstants.PerfCounterManifestName, new XAttribute("ResourceFileDirectory", row.FieldAsString(2)))); } } - + /// /// Decompile the EventManifest table. /// /// The table to decompile. private void DecompileEventManifestTable(Table table) { - foreach (Row row in table.Rows) + foreach (var row in table.Rows) { - Util.EventManifest eventManifest = new Util.EventManifest(); - this.Core.IndexElement(row, eventManifest); + this.DecompilerHelper.IndexElement(row, new XElement(UtilConstants.EventManifestName)); } } - + /// /// Decompile the SecureObjects table. /// /// The table to decompile. private void DecompileSecureObjectsTable(Table table) { - foreach (Row row in table.Rows) + foreach (var row in table.Rows) { - Util.PermissionEx permissionEx = new Util.PermissionEx(); + var permissionEx = new XElement(UtilConstants.PermissionExName, + AttributeIfNotNull("Domain", row, 2), + AttributeIfNotNull("User", row, 3) + ); string[] specialPermissions; switch ((string)row[1]) @@ -653,155 +518,13 @@ namespace WixToolset.Extensions specialPermissions = UtilConstants.ServicePermissions; break; default: - this.Core.OnMessage(WixWarnings.IllegalColumnValue(row.SourceLineNumbers, row.Table.Name, row.Fields[1].Column.Name, row[1])); + this.Messaging.Write(WarningMessages.IllegalColumnValue(row.SourceLineNumbers, row.Table.Name, row.Fields[1].Column.Name, row[1])); return; } - int permissionBits = (int)row[4]; - for (int i = 0; i < 32; i++) - { - if (0 != ((permissionBits >> i) & 1)) - { - string name = null; + this.AddPermissionAttributes(permissionEx, row, 4, specialPermissions); - if (16 > i && specialPermissions.Length > i) - { - name = specialPermissions[i]; - } - else if (28 > i && UtilConstants.StandardPermissions.Length > (i - 16)) - { - name = UtilConstants.StandardPermissions[i - 16]; - } - else if (0 <= (i - 28) && UtilConstants.GenericPermissions.Length > (i - 28)) - { - name = UtilConstants.GenericPermissions[i - 28]; - } - - if (null == name) - { - this.Core.OnMessage(WixWarnings.UnknownPermission(row.SourceLineNumbers, row.Table.Name, row.GetPrimaryKey(DecompilerConstants.PrimaryKeyDelimiter), i)); - } - else - { - switch (name) - { - case "Append": - permissionEx.Append = Util.YesNoType.yes; - break; - case "ChangePermission": - permissionEx.ChangePermission = Util.YesNoType.yes; - break; - case "CreateChild": - permissionEx.CreateChild = Util.YesNoType.yes; - break; - case "CreateFile": - permissionEx.CreateFile = Util.YesNoType.yes; - break; - case "CreateLink": - permissionEx.CreateLink = Util.YesNoType.yes; - break; - case "CreateSubkeys": - permissionEx.CreateSubkeys = Util.YesNoType.yes; - break; - case "Delete": - permissionEx.Delete = Util.YesNoType.yes; - break; - case "DeleteChild": - permissionEx.DeleteChild = Util.YesNoType.yes; - break; - case "EnumerateSubkeys": - permissionEx.EnumerateSubkeys = Util.YesNoType.yes; - break; - case "Execute": - permissionEx.Execute = Util.YesNoType.yes; - break; - case "GenericAll": - permissionEx.GenericAll = Util.YesNoType.yes; - break; - case "GenericExecute": - permissionEx.GenericExecute = Util.YesNoType.yes; - break; - case "GenericRead": - permissionEx.GenericRead = Util.YesNoType.yes; - break; - case "GenericWrite": - permissionEx.GenericWrite = Util.YesNoType.yes; - break; - case "Notify": - permissionEx.Notify = Util.YesNoType.yes; - break; - case "Read": - permissionEx.Read = Util.YesNoType.yes; - break; - case "ReadAttributes": - permissionEx.ReadAttributes = Util.YesNoType.yes; - break; - case "ReadExtendedAttributes": - permissionEx.ReadExtendedAttributes = Util.YesNoType.yes; - break; - case "ReadPermission": - permissionEx.ReadPermission = Util.YesNoType.yes; - break; - case "ServiceChangeConfig": - permissionEx.ServiceChangeConfig = Util.YesNoType.yes; - break; - case "ServiceEnumerateDependents": - permissionEx.ServiceEnumerateDependents = Util.YesNoType.yes; - break; - case "ServiceInterrogate": - permissionEx.ServiceInterrogate = Util.YesNoType.yes; - break; - case "ServicePauseContinue": - permissionEx.ServicePauseContinue = Util.YesNoType.yes; - break; - case "ServiceQueryConfig": - permissionEx.ServiceQueryConfig = Util.YesNoType.yes; - break; - case "ServiceQueryStatus": - permissionEx.ServiceQueryStatus = Util.YesNoType.yes; - break; - case "ServiceStart": - permissionEx.ServiceStart = Util.YesNoType.yes; - break; - case "ServiceStop": - permissionEx.ServiceStop = Util.YesNoType.yes; - break; - case "ServiceUserDefinedControl": - permissionEx.ServiceUserDefinedControl = Util.YesNoType.yes; - break; - case "Synchronize": - permissionEx.Synchronize = Util.YesNoType.yes; - break; - case "TakeOwnership": - permissionEx.TakeOwnership = Util.YesNoType.yes; - break; - case "Traverse": - permissionEx.Traverse = Util.YesNoType.yes; - break; - case "Write": - permissionEx.Write = Util.YesNoType.yes; - break; - case "WriteAttributes": - permissionEx.WriteAttributes = Util.YesNoType.yes; - break; - case "WriteExtendedAttributes": - permissionEx.WriteExtendedAttributes = Util.YesNoType.yes; - break; - default: - throw new InvalidOperationException(String.Format("Unknown permission attribute '{0}'.", name)); - } - } - } - } - - if (null != row[2]) - { - permissionEx.Domain = (string)row[2]; - } - - permissionEx.User = (string)row[3]; - - this.Core.IndexElement(row, permissionEx); + this.DecompilerHelper.IndexElement(row, permissionEx); } } @@ -811,90 +534,20 @@ namespace WixToolset.Extensions /// The table to decompile. private void DecompileServiceConfigTable(Table table) { - foreach (Row row in table.Rows) + foreach (var row in table.Rows) { - Util.ServiceConfig serviceConfig = new Util.ServiceConfig(); - - serviceConfig.ServiceName = (string)row[0]; - - switch ((string)row[3]) - { - case "none": - serviceConfig.FirstFailureActionType = Util.ServiceConfig.FirstFailureActionTypeType.none; - break; - case "reboot": - serviceConfig.FirstFailureActionType = Util.ServiceConfig.FirstFailureActionTypeType.reboot; - break; - case "restart": - serviceConfig.FirstFailureActionType = Util.ServiceConfig.FirstFailureActionTypeType.restart; - break; - case "runCommand": - serviceConfig.FirstFailureActionType = Util.ServiceConfig.FirstFailureActionTypeType.runCommand; - break; - default: - this.Core.OnMessage(WixWarnings.IllegalColumnValue(row.SourceLineNumbers, table.Name, row.Fields[3].Column.Name, row[3])); - break; - } - - switch ((string)row[4]) - { - case "none": - serviceConfig.SecondFailureActionType = Util.ServiceConfig.SecondFailureActionTypeType.none; - break; - case "reboot": - serviceConfig.SecondFailureActionType = Util.ServiceConfig.SecondFailureActionTypeType.reboot; - break; - case "restart": - serviceConfig.SecondFailureActionType = Util.ServiceConfig.SecondFailureActionTypeType.restart; - break; - case "runCommand": - serviceConfig.SecondFailureActionType = Util.ServiceConfig.SecondFailureActionTypeType.runCommand; - break; - default: - this.Core.OnMessage(WixWarnings.IllegalColumnValue(row.SourceLineNumbers, table.Name, row.Fields[4].Column.Name, row[4])); - break; - } - - switch ((string)row[5]) - { - case "none": - serviceConfig.ThirdFailureActionType = Util.ServiceConfig.ThirdFailureActionTypeType.none; - break; - case "reboot": - serviceConfig.ThirdFailureActionType = Util.ServiceConfig.ThirdFailureActionTypeType.reboot; - break; - case "restart": - serviceConfig.ThirdFailureActionType = Util.ServiceConfig.ThirdFailureActionTypeType.restart; - break; - case "runCommand": - serviceConfig.ThirdFailureActionType = Util.ServiceConfig.ThirdFailureActionTypeType.runCommand; - break; - default: - this.Core.OnMessage(WixWarnings.IllegalColumnValue(row.SourceLineNumbers, table.Name, row.Fields[5].Column.Name, row[5])); - break; - } - - if (null != row[6]) - { - serviceConfig.ResetPeriodInDays = (int)row[6]; - } - - if (null != row[7]) - { - serviceConfig.RestartServiceDelayInSeconds = (int)row[7]; - } - - if (null != row[8]) - { - serviceConfig.ProgramCommandLine = (string)row[8]; - } - - if (null != row[9]) - { - serviceConfig.RebootMessage = (string)row[9]; - } - - this.Core.IndexElement(row, serviceConfig); + var serviceConfig = new XElement(UtilConstants.ServiceConfigName, + new XAttribute("ServiceName", row.FieldAsString(0)), + AttributeIfNotNull("FirstFailureActionType", row, 3), + AttributeIfNotNull("SecondFailureActionType", row, 4), + AttributeIfNotNull("ThirdFailureActionType", row, 5), + NumericAttributeIfNotNull("ResetPeriodInDays", row, 6), + NumericAttributeIfNotNull("RestartServiceDelayInSeconds", row, 7), + AttributeIfNotNull("ProgramCommandLine", row, 8), + AttributeIfNotNull("RebootMessage", row, 9) + ); + + this.DecompilerHelper.IndexElement(row, serviceConfig); } } @@ -904,97 +557,58 @@ namespace WixToolset.Extensions /// The table to decompile. private void DecompileUserTable(Table table) { - foreach (Row row in table.Rows) + foreach (var row in table.Rows) { - Util.User user = new Util.User(); - - user.Id = (string)row[0]; - - user.Name = (string)row[2]; - - if (null != row[3]) + var attributes = row.FieldAsNullableInteger(5) ?? 0; + + var user = new XElement(UtilConstants.UserName, + new XAttribute("Id", row.FieldAsString(0)), + new XAttribute("Name", row.FieldAsString(2)), + AttributeIfNotNull("Domain", row, 3), + AttributeIfNotNull("Password", row, 4), + AttributeIfTrue("PasswordNeverExpires", UtilCompiler.UserDontExpirePasswrd == (attributes & UtilCompiler.UserDontExpirePasswrd)), + AttributeIfTrue("CanNotChangePassword", UtilCompiler.UserPasswdCantChange == (attributes & UtilCompiler.UserPasswdCantChange)), + AttributeIfTrue("PasswordExpired", UtilCompiler.UserPasswdChangeReqdOnLogin == (attributes & UtilCompiler.UserPasswdChangeReqdOnLogin)), + AttributeIfTrue("Disabled", UtilCompiler.UserDisableAccount == (attributes & UtilCompiler.UserDisableAccount)), + AttributeIfTrue("FailIfExists", UtilCompiler.UserFailIfExists == (attributes & UtilCompiler.UserFailIfExists)), + AttributeIfTrue("UpdateIfExists", UtilCompiler.UserUpdateIfExists == (attributes & UtilCompiler.UserUpdateIfExists)), + AttributeIfTrue("LogonAsService", UtilCompiler.UserLogonAsService == (attributes & UtilCompiler.UserLogonAsService)), + AttributeIfTrue("LogonAsService", UtilCompiler.UserLogonAsService == (attributes & UtilCompiler.UserLogonAsService)) + ); + + if (UtilCompiler.UserDontRemoveOnUninstall == (attributes & UtilCompiler.UserDontRemoveOnUninstall)) { - user.Domain = (string)row[3]; + user.Add(new XAttribute("RemoveOnUninstall", "no")); } - if (null != row[4]) + if (UtilCompiler.UserDontCreateUser == (attributes & UtilCompiler.UserDontCreateUser)) { - user.Password = (string)row[4]; + user.Add(new XAttribute("CreateUser", "no")); } - if (null != row[5]) + if (UtilCompiler.UserNonVital == (attributes & UtilCompiler.UserNonVital)) { - int attributes = (int)row[5]; - - if (UtilCompiler.UserDontExpirePasswrd == (attributes & UtilCompiler.UserDontExpirePasswrd)) - { - user.PasswordNeverExpires = Util.YesNoType.yes; - } - - if (UtilCompiler.UserPasswdCantChange == (attributes & UtilCompiler.UserPasswdCantChange)) - { - user.CanNotChangePassword = Util.YesNoType.yes; - } - - if (UtilCompiler.UserPasswdChangeReqdOnLogin == (attributes & UtilCompiler.UserPasswdChangeReqdOnLogin)) - { - user.PasswordExpired = Util.YesNoType.yes; - } - - if (UtilCompiler.UserDisableAccount == (attributes & UtilCompiler.UserDisableAccount)) - { - user.Disabled = Util.YesNoType.yes; - } - - if (UtilCompiler.UserFailIfExists == (attributes & UtilCompiler.UserFailIfExists)) - { - user.FailIfExists = Util.YesNoType.yes; - } - - if (UtilCompiler.UserUpdateIfExists == (attributes & UtilCompiler.UserUpdateIfExists)) - { - user.UpdateIfExists = Util.YesNoType.yes; - } - - if (UtilCompiler.UserLogonAsService == (attributes & UtilCompiler.UserLogonAsService)) - { - user.LogonAsService = Util.YesNoType.yes; - } - - if (UtilCompiler.UserDontRemoveOnUninstall == (attributes & UtilCompiler.UserDontRemoveOnUninstall)) - { - user.RemoveOnUninstall = Util.YesNoType.no; - } - - if (UtilCompiler.UserDontCreateUser == (attributes & UtilCompiler.UserDontCreateUser)) - { - user.CreateUser = Util.YesNoType.no; - } - - if (UtilCompiler.UserNonVital == (attributes & UtilCompiler.UserNonVital)) - { - user.Vital = Util.YesNoType.no; - } + user.Add(new XAttribute("Vital", "no")); } - if (null != row[1]) + var componentId = row.FieldAsString(1); + if (!String.IsNullOrEmpty(componentId)) { - Wix.Component component = (Wix.Component)this.Core.GetIndexedElement("Component", (string)row[1]); - - if (null != component) + if (this.DecompilerHelper.TryGetIndexedElement("Component", componentId, out var component)) { - component.AddChild(user); + component.Add(user); } else { - this.Core.OnMessage(WixWarnings.ExpectedForeignRow(row.SourceLineNumbers, table.Name, row.GetPrimaryKey(DecompilerConstants.PrimaryKeyDelimiter), "Component_", (string)row[1], "Component")); + this.Messaging.Write(WarningMessages.ExpectedForeignRow(row.SourceLineNumbers, table.Name, row.GetPrimaryKey(), "Component_", componentId, "Component")); } } else { - this.Core.RootElement.AddChild(user); + this.DecompilerHelper.AddElementToRoot(user); } - this.Core.IndexElement(row, user); + + this.DecompilerHelper.IndexElement(row, user); } } @@ -1004,21 +618,16 @@ namespace WixToolset.Extensions /// The table to decompile. private void DecompileUserGroupTable(Table table) { - foreach (Row row in table.Rows) + foreach (var row in table.Rows) { - Util.User user = (Util.User)this.Core.GetIndexedElement("User", (string)row[0]); - - if (null != user) + var userId = row.FieldAsString(0); + if (this.DecompilerHelper.TryGetIndexedElement("User", userId, out var user)) { - Util.GroupRef groupRef = new Util.GroupRef(); - - groupRef.Id = (string)row[1]; - - user.AddChild(groupRef); + user.Add(new XElement(UtilConstants.GroupRefName, new XAttribute("Id", row.FieldAsString(1)))); } else { - this.Core.OnMessage(WixWarnings.ExpectedForeignRow(row.SourceLineNumbers, table.Name, row.GetPrimaryKey(DecompilerConstants.PrimaryKeyDelimiter), "Group_", (string)row[0], "Group")); + this.Messaging.Write(WarningMessages.ExpectedForeignRow(row.SourceLineNumbers, table.Name, row.GetPrimaryKey(), "Group_", userId, "Group")); } } } @@ -1029,75 +638,58 @@ namespace WixToolset.Extensions /// The table to decompile. private void DecompileXmlConfigTable(Table table) { - foreach (Row row in table.Rows) + foreach (var row in table.Rows) { - Util.XmlConfig xmlConfig = new Util.XmlConfig(); - - xmlConfig.Id = (string)row[0]; - - xmlConfig.File = (string)row[1]; - - xmlConfig.ElementPath = (string)row[2]; - - if (null != row[3]) - { - xmlConfig.VerifyPath = (string)row[3]; - } - - if (null != row[4]) - { - xmlConfig.Name = (string)row[4]; - } - - if (null != row[5]) - { - xmlConfig.Value = (string)row[5]; - } - - int flags = (int)row[6]; + var flags = row.FieldAsNullableInteger(6) ?? 0; + string node = null; + string action = null; + string on = null; if (0x1 == (flags & 0x1)) { - xmlConfig.Node = Util.XmlConfig.NodeType.element; + node = "element"; } else if (0x2 == (flags & 0x2)) { - xmlConfig.Node = Util.XmlConfig.NodeType.value; + node = "value"; } else if (0x4 == (flags & 0x4)) { - xmlConfig.Node = Util.XmlConfig.NodeType.document; + node = "document"; } if (0x10 == (flags & 0x10)) { - xmlConfig.Action = Util.XmlConfig.ActionType.create; + action = "create"; } else if (0x20 == (flags & 0x20)) { - xmlConfig.Action = Util.XmlConfig.ActionType.delete; + action = "delete"; } if (0x100 == (flags & 0x100)) { - xmlConfig.On = Util.XmlConfig.OnType.install; + on = "install"; } else if (0x200 == (flags & 0x200)) { - xmlConfig.On = Util.XmlConfig.OnType.uninstall; - } - - if (0x00001000 == (flags & 0x00001000)) - { - xmlConfig.PreserveModifiedDate = Util.YesNoType.yes; - } - - if (null != row[8]) - { - xmlConfig.Sequence = (int)row[8]; + on = "uninstall"; } - this.Core.IndexElement(row, xmlConfig); + var xmlConfig = new XElement(UtilConstants.XmlConfigName, + new XAttribute("Id", row.FieldAsString(0)), + new XAttribute("File", row.FieldAsString(1)), + new XAttribute("ElementPath", row.FieldAsString(2)), + AttributeIfNotNull("VerifyPath", row, 3), + AttributeIfNotNull("Name", row, 4), + AttributeIfNotNull("Node", node), + AttributeIfNotNull("Action", action), + AttributeIfNotNull("On", on), + AttributeIfTrue("PreserveModifiedDate", 0x00001000 == (flags & 0x00001000)), + NumericAttributeIfNotNull("Sequence", row, 8) + ); + + this.DecompilerHelper.IndexElement(row, xmlConfig); } } @@ -1114,34 +706,30 @@ namespace WixToolset.Extensions /// private void FinalizePerfmonTable(TableIndexedCollection tables) { - Table perfmonTable = tables["Perfmon"]; - - if (null != perfmonTable) + if (tables.TryGetTable("Perfmon", out var perfmonTable)) { - foreach (Row row in perfmonTable.Rows) + foreach (var row in perfmonTable.Rows) { - string formattedFile = (string)row[1]; - Util.PerfCounter perfCounter = (Util.PerfCounter)this.Core.GetIndexedElement(row); + var formattedFile = row.FieldAsString(1); // try to "de-format" the File column's value to determine the proper parent File element if ((formattedFile.StartsWith("[#", StringComparison.Ordinal) || formattedFile.StartsWith("[!", StringComparison.Ordinal)) && formattedFile.EndsWith("]", StringComparison.Ordinal)) { - string fileId = formattedFile.Substring(2, formattedFile.Length - 3); - - Wix.File file = (Wix.File)this.Core.GetIndexedElement("File", fileId); - if (null != file) + var fileId = formattedFile.Substring(2, formattedFile.Length - 3); + if (this.DecompilerHelper.TryGetIndexedElement("File", fileId, out var file)) { - file.AddChild(perfCounter); + var perfCounter = this.DecompilerHelper.GetIndexedElement(row); + file.Add(perfCounter); } else { - this.Core.OnMessage(WixWarnings.ExpectedForeignRow(row.SourceLineNumbers, perfmonTable.Name, row.GetPrimaryKey(DecompilerConstants.PrimaryKeyDelimiter), "File", formattedFile, "File")); + this.Messaging.Write(WarningMessages.ExpectedForeignRow(row.SourceLineNumbers, perfmonTable.Name, row.GetPrimaryKey(), "File", formattedFile, "File")); } } else { - this.Core.OnMessage(UtilErrors.IllegalFileValueInPerfmonOrManifest(formattedFile, "Perfmon")); + this.Messaging.Write(UtilErrors.IllegalFileValueInPerfmonOrManifest(formattedFile, "Perfmon")); } } } @@ -1153,34 +741,33 @@ namespace WixToolset.Extensions /// The collection of all tables. private void FinalizePerfmonManifestTable(TableIndexedCollection tables) { - Table perfmonManifestTable = tables["PerfmonManifest"]; - - if (null != perfmonManifestTable) + if (tables.TryGetTable("PerfmonManifest", out var perfmonManifestTable)) { - foreach (Row row in perfmonManifestTable.Rows) + foreach (var row in perfmonManifestTable.Rows) { - string formattedFile = (string)row[1]; - Util.PerfCounterManifest perfCounterManifest = (Util.PerfCounterManifest)this.Core.GetIndexedElement(row); + var formattedFile = row.FieldAsString(1); // try to "de-format" the File column's value to determine the proper parent File element if ((formattedFile.StartsWith("[#", StringComparison.Ordinal) || formattedFile.StartsWith("[!", StringComparison.Ordinal)) && formattedFile.EndsWith("]", StringComparison.Ordinal)) { - string fileId = formattedFile.Substring(2, formattedFile.Length - 3); + var perfCounterManifest = this.DecompilerHelper.GetIndexedElement(row); + var fileId = formattedFile.Substring(2, formattedFile.Length - 3); - Wix.File file = (Wix.File)this.Core.GetIndexedElement("File", fileId); - if (null != file) + if (this.DecompilerHelper.TryGetIndexedElement("File", fileId, out var file)) { - file.AddChild(perfCounterManifest); + file.Add(perfCounterManifest); } else { - this.Core.OnMessage(WixWarnings.ExpectedForeignRow(row.SourceLineNumbers, perfCounterManifest.ResourceFileDirectory, row.GetPrimaryKey(DecompilerConstants.PrimaryKeyDelimiter), "File", formattedFile, "File")); + var resourceFileDirectory = perfCounterManifest.Attribute("ResourceFileDirectory")?.Value; + + this.Messaging.Write(WarningMessages.ExpectedForeignRow(row.SourceLineNumbers, resourceFileDirectory, row.GetPrimaryKey(), "File", formattedFile, "File")); } } else { - this.Core.OnMessage(UtilErrors.IllegalFileValueInPerfmonOrManifest(formattedFile, "PerfmonManifest")); + this.Messaging.Write(UtilErrors.IllegalFileValueInPerfmonOrManifest(formattedFile, "PerfmonManifest")); } } } @@ -1196,64 +783,61 @@ namespace WixToolset.Extensions /// private void FinalizeSecureObjectsTable(TableIndexedCollection tables) { - Table createFolderTable = tables["CreateFolder"]; - Table secureObjectsTable = tables["SecureObjects"]; - - Hashtable createFolders = new Hashtable(); + var createFolderElementsByDirectoryId = new Dictionary>(); // index the CreateFolder table because the foreign key to this table from the // LockPermissions table is only part of the primary key of this table - if (null != createFolderTable) + if (tables.TryGetTable("CreateFolder", out var createFolderTable)) { - foreach (Row row in createFolderTable.Rows) + foreach (var row in createFolderTable.Rows) { - Wix.CreateFolder createFolder = (Wix.CreateFolder)this.Core.GetIndexedElement(row); - string directoryId = (string)row[0]; + var directoryId = row.FieldAsString(0); - if (!createFolders.Contains(directoryId)) + if (!createFolderElementsByDirectoryId.TryGetValue(directoryId, out var createFolderElements)) { - createFolders.Add(directoryId, new ArrayList()); + createFolderElements = new List(); + createFolderElementsByDirectoryId.Add(directoryId, createFolderElements); } - ((ArrayList)createFolders[directoryId]).Add(createFolder); + + var createFolder = this.DecompilerHelper.GetIndexedElement(row); + createFolderElements.Add(createFolder); } } - if (null != secureObjectsTable) + if (tables.TryGetTable("SecureObjects", out var secureObjectsTable)) { - foreach (Row row in secureObjectsTable.Rows) + foreach (var row in secureObjectsTable.Rows) { - string id = (string)row[0]; - string table = (string)row[1]; + var id = row.FieldAsString(0); + var table = row.FieldAsString(1); - Util.PermissionEx permissionEx = (Util.PermissionEx)this.Core.GetIndexedElement(row); + var permissionEx = this.DecompilerHelper.GetIndexedElement(row); - if ("CreateFolder" == table) + if (table == "CreateFolder") { - ArrayList createFolderElements = (ArrayList)createFolders[id]; - - if (null != createFolderElements) + if (createFolderElementsByDirectoryId.TryGetValue(id, out var createFolderElements)) { - foreach (Wix.CreateFolder createFolder in createFolderElements) + foreach (var createFolder in createFolderElements) { - createFolder.AddChild(permissionEx); + createFolder.Add(permissionEx); } } else { - this.Core.OnMessage(WixWarnings.ExpectedForeignRow(row.SourceLineNumbers, "SecureObjects", row.GetPrimaryKey(DecompilerConstants.PrimaryKeyDelimiter), "LockObject", id, table)); + this.Messaging.Write(WarningMessages.ExpectedForeignRow(row.SourceLineNumbers, "SecureObjects", row.GetPrimaryKey(), "LockObject", id, table)); } } else { - Wix.IParentElement parentElement = (Wix.IParentElement)this.Core.GetIndexedElement(table, id); + var parentElement = this.DecompilerHelper.GetIndexedElement(table, id); - if (null != parentElement) + if (parentElement != null) { - parentElement.AddChild(permissionEx); + parentElement.Add(permissionEx); } else { - this.Core.OnMessage(WixWarnings.ExpectedForeignRow(row.SourceLineNumbers, "SecureObjects", row.GetPrimaryKey(DecompilerConstants.PrimaryKeyDelimiter), "LockObject", id, table)); + this.Messaging.Write(WarningMessages.ExpectedForeignRow(row.SourceLineNumbers, "SecureObjects", row.GetPrimaryKey(), "LockObject", id, table)); } } } @@ -1270,10 +854,8 @@ namespace WixToolset.Extensions /// private void FinalizeServiceConfigTable(TableIndexedCollection tables) { - Table serviceConfigTable = tables["ServiceConfig"]; - Table serviceInstallTable = tables["ServiceInstall"]; - - Hashtable serviceInstalls = new Hashtable(); + //var serviceInstalls = new Hashtable(); + var serviceInstallElementsByName = new Dictionary>(); // index the ServiceInstall table because the foreign key used by the ServiceConfig // table is actually the ServiceInstall.Name, not the ServiceInstall.ServiceInstall @@ -1281,55 +863,54 @@ namespace WixToolset.Extensions // decompiler must assume there could be multiple matches and add the ServiceConfig to each // TODO: the Component column information should be taken into acount to accurately identify // the correct column to use - if (null != serviceInstallTable) + if (tables.TryGetTable("ServiceInstall", out var serviceInstallTable)) { - foreach (Row row in serviceInstallTable.Rows) + foreach (var row in serviceInstallTable.Rows) { - string name = (string)row[1]; - Wix.ServiceInstall serviceInstall = (Wix.ServiceInstall)this.Core.GetIndexedElement(row); + var name = row.FieldAsString(1); - if (!serviceInstalls.Contains(name)) + if (!serviceInstallElementsByName.TryGetValue(name, out var serviceInstallElements)) { - serviceInstalls.Add(name, new ArrayList()); + serviceInstallElements = new List(); + serviceInstallElementsByName.Add(name, serviceInstallElements); } - ((ArrayList)serviceInstalls[name]).Add(serviceInstall); + var serviceInstall = this.DecompilerHelper.GetIndexedElement(row); + serviceInstallElements.Add(serviceInstall); } } - if (null != serviceConfigTable) + if (tables.TryGetTable("ServiceConfig", out var serviceConfigTable)) { - foreach (Row row in serviceConfigTable.Rows) + foreach (var row in serviceConfigTable.Rows) { - Util.ServiceConfig serviceConfig = (Util.ServiceConfig)this.Core.GetIndexedElement(row); + var serviceConfig = this.DecompilerHelper.GetIndexedElement(row); - if (0 == (int)row[2]) + if (row.FieldAsInteger(2) == 0) { - Wix.Component component = (Wix.Component)this.Core.GetIndexedElement("Component", (string)row[1]); - - if (null != component) + var componentId = row.FieldAsString(1); + if (this.DecompilerHelper.TryGetIndexedElement("Component", componentId, out var component)) { - component.AddChild(serviceConfig); + component.Add(serviceConfig); } else { - this.Core.OnMessage(WixWarnings.ExpectedForeignRow(row.SourceLineNumbers, serviceConfigTable.Name, row.GetPrimaryKey(DecompilerConstants.PrimaryKeyDelimiter), "Component_", (string)row[1], "Component")); + this.Messaging.Write(WarningMessages.ExpectedForeignRow(row.SourceLineNumbers, serviceConfigTable.Name, row.GetPrimaryKey(), "Component_", componentId, "Component")); } } else { - ArrayList serviceInstallElements = (ArrayList)serviceInstalls[row[0]]; - - if (null != serviceInstallElements) + var name = row.FieldAsString(0); + if (serviceInstallElementsByName.TryGetValue(name, out var serviceInstallElements)) { - foreach (Wix.ServiceInstall serviceInstall in serviceInstallElements) + foreach (var serviceInstall in serviceInstallElements) { - serviceInstall.AddChild(serviceConfig); + serviceInstall.Add(serviceConfig); } } else { - this.Core.OnMessage(WixWarnings.ExpectedForeignRow(row.SourceLineNumbers, serviceConfigTable.Name, row.GetPrimaryKey(DecompilerConstants.PrimaryKeyDelimiter), "ServiceName", (string)row[0], "ServiceInstall")); + this.Messaging.Write(WarningMessages.ExpectedForeignRow(row.SourceLineNumbers, serviceConfigTable.Name, row.GetPrimaryKey(), "ServiceName", name, "ServiceInstall")); } } } @@ -1342,38 +923,34 @@ namespace WixToolset.Extensions /// Collection of all tables. private void FinalizeXmlConfigTable(TableIndexedCollection tables) { - Table xmlConfigTable = tables["XmlConfig"]; - - if (null != xmlConfigTable) + if (tables.TryGetTable("XmlConfig", out var xmlConfigTable)) { - foreach (Row row in xmlConfigTable.Rows) + foreach (var row in xmlConfigTable.Rows) { - Util.XmlConfig xmlConfig = (Util.XmlConfig)this.Core.GetIndexedElement(row); + var xmlConfig = this.DecompilerHelper.GetIndexedElement(row); if (null == row[6] || 0 == (int)row[6]) { - Util.XmlConfig parentXmlConfig = (Util.XmlConfig)this.Core.GetIndexedElement("XmlConfig", (string)row[2]); - - if (null != parentXmlConfig) + var id = row.FieldAsString(2); + if (this.DecompilerHelper.TryGetIndexedElement("XmlConfig", id, out var parentXmlConfig)) { - parentXmlConfig.AddChild(xmlConfig); + parentXmlConfig.Add(xmlConfig); } else { - this.Core.OnMessage(WixWarnings.ExpectedForeignRow(row.SourceLineNumbers, xmlConfigTable.Name, row.GetPrimaryKey(DecompilerConstants.PrimaryKeyDelimiter), "ElementPath", (string)row[2], "XmlConfig")); + this.Messaging.Write(WarningMessages.ExpectedForeignRow(row.SourceLineNumbers, xmlConfigTable.Name, row.GetPrimaryKey(), "ElementPath", (string)row[2], "XmlConfig")); } } else { - Wix.Component component = (Wix.Component)this.Core.GetIndexedElement("Component", (string)row[7]); - - if (null != component) + var componentId = row.FieldAsString(7); + if (this.DecompilerHelper.TryGetIndexedElement("Component", componentId, out var component)) { - component.AddChild(xmlConfig); + component.Add(xmlConfig); } else { - this.Core.OnMessage(WixWarnings.ExpectedForeignRow(row.SourceLineNumbers, xmlConfigTable.Name, row.GetPrimaryKey(DecompilerConstants.PrimaryKeyDelimiter), "Component_", (string)row[7], "Component")); + this.Messaging.Write(WarningMessages.ExpectedForeignRow(row.SourceLineNumbers, xmlConfigTable.Name, row.GetPrimaryKey(), "Component_", componentId, "Component")); } } } @@ -1391,113 +968,88 @@ namespace WixToolset.Extensions /// private void FinalizeXmlFileTable(TableIndexedCollection tables) { - Table xmlFileTable = tables["XmlFile"]; - Table eventManifestTable = tables["EventManifest"]; - - if (null != xmlFileTable) + if (tables.TryGetTable("XmlFile", out var xmlFileTable)) { - foreach (Row row in xmlFileTable.Rows) + var eventManifestTable = tables["EventManifest"]; + + foreach (var row in xmlFileTable.Rows) { - bool bManifestGenerated = false; - string xmlFileConfigId = (string)row[0]; + var manifestGenerated = false; + var xmlFileConfigId = (string)row[0]; if (null != eventManifestTable) { - foreach (Row emrow in eventManifestTable.Rows) + foreach (var emrow in eventManifestTable.Rows) { - string formattedFile = (string)emrow[1]; + var formattedFile = (string)emrow[1]; if ((formattedFile.StartsWith("[#", StringComparison.Ordinal) || formattedFile.StartsWith("[!", StringComparison.Ordinal)) && formattedFile.EndsWith("]", StringComparison.Ordinal)) { - string fileId = formattedFile.Substring(2, formattedFile.Length - 3); + var fileId = formattedFile.Substring(2, formattedFile.Length - 3); if (String.Equals(String.Concat("Config_", fileId, "ResourceFile"), xmlFileConfigId)) { - Util.EventManifest eventManifest = (Util.EventManifest)this.Core.GetIndexedElement(emrow); - if (null != eventManifest) + if (this.DecompilerHelper.TryGetIndexedElement(emrow, out var eventManifest)) { - eventManifest.ResourceFile = (string)row[4]; + eventManifest.Add(new XAttribute("ResourceFile", row.FieldAsString(4))); } - bManifestGenerated = true; + manifestGenerated = true; } - + else if (String.Equals(String.Concat("Config_", fileId, "MessageFile"), xmlFileConfigId)) { - Util.EventManifest eventManifest = (Util.EventManifest)this.Core.GetIndexedElement(emrow); - if (null != eventManifest) + if (this.DecompilerHelper.TryGetIndexedElement(emrow, out var eventManifest)) { - eventManifest.MessageFile = (string)row[4]; + eventManifest.Add(new XAttribute("MessageFile", row.FieldAsString(4))); } - bManifestGenerated = true; + manifestGenerated = true; } } } } - if (true == bManifestGenerated) - continue; - - Util.XmlFile xmlFile = new Util.XmlFile(); - - xmlFile.Id = (string)row[0]; - xmlFile.File = (string)row[1]; - xmlFile.ElementPath = (string)row[2]; - - if (null != row[3]) - { - xmlFile.Name = (string)row[3]; - } - - if (null != row[4]) + if (manifestGenerated) { - xmlFile.Value = (string)row[4]; + continue; } - int flags = (int)row[5]; + var action = "setValue"; + var flags = row.FieldAsInteger(5); if (0x1 == (flags & 0x1) && 0x2 == (flags & 0x2)) { - this.Core.OnMessage(WixWarnings.IllegalColumnValue(row.SourceLineNumbers, xmlFileTable.Name, row.Fields[5].Column.Name, row[5])); + this.Messaging.Write(WarningMessages.IllegalColumnValue(row.SourceLineNumbers, xmlFileTable.Name, row.Fields[5].Column.Name, row[5])); } else if (0x1 == (flags & 0x1)) { - xmlFile.Action = Util.XmlFile.ActionType.createElement; + action = "createElement"; } else if (0x2 == (flags & 0x2)) { - xmlFile.Action = Util.XmlFile.ActionType.deleteValue; - } - else - { - xmlFile.Action = Util.XmlFile.ActionType.setValue; - } - - if (0x100 == (flags & 0x100)) - { - xmlFile.SelectionLanguage = Util.XmlFile.SelectionLanguageType.XPath; - } - - if (0x00001000 == (flags & 0x00001000)) - { - xmlFile.PreserveModifiedDate = Util.YesNoType.yes; + action = "deleteValue"; } - if (0x00010000 == (flags & 0x00010000)) - { - xmlFile.Permanent = Util.YesNoType.yes; - } + var selectionLanguage = (0x100 == (flags & 0x100)) ? "XPath" : null; + var preserveModifiedDate = 0x00001000 == (flags & 0x00001000); + var permanent = 0x00010000 == (flags & 0x00010000); - if (null != row[7]) + if (this.DecompilerHelper.TryGetIndexedElement("Component", row.FieldAsString(6), out var component)) { - xmlFile.Sequence = (int)row[7]; - } - - Wix.Component component = (Wix.Component)this.Core.GetIndexedElement("Component", (string)row[6]); - - if (null != component) - { - component.AddChild(xmlFile); + var xmlFile = new XElement(UtilConstants.XmlFileName, + AttributeIfNotNull("Id", row, 0), + AttributeIfNotNull("File", row, 1), + AttributeIfNotNull("ElementPath", row, 2), + AttributeIfNotNull("Name", row, 3), + AttributeIfNotNull("Value", row, 4), + AttributeIfNotNull("Action", action), + AttributeIfNotNull("SelectionLanguage", selectionLanguage), + AttributeIfTrue("PreserveModifiedDate", preserveModifiedDate), + AttributeIfTrue("Permanent", permanent), + NumericAttributeIfNotNull("Sequence", row, 7) + ); + + component.Add(xmlFile); } else { - this.Core.OnMessage(WixWarnings.ExpectedForeignRow(row.SourceLineNumbers, xmlFileTable.Name, row.GetPrimaryKey(DecompilerConstants.PrimaryKeyDelimiter), "Component_", (string)row[6], "Component")); + this.Messaging.Write(WarningMessages.ExpectedForeignRow(row.SourceLineNumbers, xmlFileTable.Name, row.GetPrimaryKey(), "Component_", (string)row[6], "Component")); } } } @@ -1510,34 +1062,122 @@ namespace WixToolset.Extensions /// The collection of all tables. private void FinalizeEventManifestTable(TableIndexedCollection tables) { - Table eventManifestTable = tables["EventManifest"]; - - if (null != eventManifestTable) + if (tables.TryGetTable("EventManifest", out var eventManifestTable)) { - foreach (Row row in eventManifestTable.Rows) + foreach (var row in eventManifestTable.Rows) { - string formattedFile = (string)row[1]; - Util.EventManifest eventManifest = (Util.EventManifest)this.Core.GetIndexedElement(row); + var eventManifest = this.DecompilerHelper.GetIndexedElement(row); + var formattedFile = row.FieldAsString(1); // try to "de-format" the File column's value to determine the proper parent File element if ((formattedFile.StartsWith("[#", StringComparison.Ordinal) || formattedFile.StartsWith("[!", StringComparison.Ordinal)) && formattedFile.EndsWith("]", StringComparison.Ordinal)) { - string fileId = formattedFile.Substring(2, formattedFile.Length - 3); + var fileId = formattedFile.Substring(2, formattedFile.Length - 3); - Wix.File file = (Wix.File)this.Core.GetIndexedElement("File", fileId); - if (null != file) + if (this.DecompilerHelper.TryGetIndexedElement("File", fileId, out var file)) { - file.AddChild(eventManifest); + file.Add(eventManifest); } } else { - this.Core.OnMessage(UtilErrors.IllegalFileValueInPerfmonOrManifest(formattedFile, "EventManifest")); + this.Messaging.Write(UtilErrors.IllegalFileValueInPerfmonOrManifest(formattedFile, "EventManifest")); + } + } + } + } + + private void AddPermissionAttributes(XElement element, Row row, int column, string[] specialPermissions) + { + var permissions = row.FieldAsInteger(column); + for (var i = 0; i < 32; i++) + { + if (0 != ((permissions >> i) & 1)) + { + string name = null; + + if (16 > i && specialPermissions.Length > i) + { + name = specialPermissions[i]; + } + else if (28 > i && UtilConstants.StandardPermissions.Length > (i - 16)) + { + name = UtilConstants.StandardPermissions[i - 16]; + } + else if (0 <= (i - 28) && UtilConstants.GenericPermissions.Length > (i - 28)) + { + name = UtilConstants.GenericPermissions[i - 28]; + } + + if (!String.IsNullOrEmpty(name)) + { + element.Add(new XAttribute(name, "yes")); + } + else + { + this.Messaging.Write(WarningMessages.UnknownPermission(row.SourceLineNumbers, row.Table.Name, row.GetPrimaryKey(), i)); } } } } + + private static XAttribute AttributeIfNotNull(string name, string value) + { + return value == null ? null : new XAttribute(name, value); + } + + private static XAttribute AttributeIfNotNull(string name, bool value) + { + return new XAttribute(name, value ? "yes" : "no"); + } + + private static XAttribute AttributeIfNotNull(string name, Row row, int field) + { + if (row[field] != null) + { + return new XAttribute(name, row.FieldAsString(field)); + } + + return null; + } + + private static XAttribute NumericAttributeIfNotNull(string name, Row row, int field) + { + if (row[field] != null) + { + return new XAttribute(name, row.FieldAsInteger(field)); + } + + return null; + } + + private static XAttribute AttributeIfTrue(string name, bool value) + { + return value ? new XAttribute(name, "yes") : null; + } + } + + internal static class XElementExtensions + { + public static XElement AttributeIfNotNull(this XElement element, string name, Row row, int field) + { + if (row[field] != null) + { + element.Add(new XAttribute(name, row.FieldAsString(field))); + } + + return element; + } + + public static XElement NumericAttributeIfNotNull(this XElement element, string name, Row row, int field) + { + if (row[field] != null) + { + element.Add(new XAttribute(name, row.FieldAsInteger(field))); + } + + return element; + } } -#endif } 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 protected override IReadOnlyCollection ExtensionTypes => new[] { typeof(UtilCompiler), + typeof(UtilDecompiler), typeof(UtilExtensionData), typeof(UtilWindowsInstallerBackendBinderExtension), }; 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 UtilSymbolDefinitions.FileSharePermissions, new[] { - 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), + 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), 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), new ColumnDefinition("Permissions", ColumnType.Number, 4, primaryKey: false, nullable: false, ColumnCategory.Unknown, description: "Permissions int, as in EXPLICIT_ACCESS.grfAccessPermissions in MSDN"), }, 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 return Query.QueryDatabase(outputPath, tables); } } + + public void BuildAndDecompileAndBuild(Action buildFunc, Action decompileFunc, string decompilePath) + { + var sourceFiles = Directory.GetFiles(this.SourceFolder, "*.wxs"); + var wxlFiles = Directory.GetFiles(this.SourceFolder, "*.wxl"); + + using (var fs = new DisposableFileSystem()) + { + var intermediateFolder = fs.GetFolder(); + var outputFolder = Path.Combine(intermediateFolder, "bin"); + var decompileExtractFolder = Path.Combine(intermediateFolder, "decompiled", "extract"); + var decompileIntermediateFolder = Path.Combine(intermediateFolder, "decompiled", "obj"); + var decompileBuildFolder = Path.Combine(intermediateFolder, "decompiled", "bin"); + var outputPath = Path.Combine(outputFolder, this.OutputFile); + var decompileBuildPath = Path.Combine(decompileBuildFolder, this.OutputFile); + + // First build. + var firstBuildArgs = new List + { + "build", + "-o", outputPath, + "-intermediateFolder", intermediateFolder, + }; + + if (this.ExtensionType != null) + { + firstBuildArgs.Add("-ext"); + firstBuildArgs.Add(Path.GetFullPath(new Uri(this.ExtensionType.Assembly.CodeBase).LocalPath)); + } + + firstBuildArgs.AddRange(sourceFiles); + + foreach (var wxlFile in wxlFiles) + { + firstBuildArgs.Add("-loc"); + firstBuildArgs.Add(wxlFile); + } + + foreach (var bindPath in this.BindPaths) + { + firstBuildArgs.Add("-bindpath"); + firstBuildArgs.Add(bindPath); + } + + buildFunc(firstBuildArgs.ToArray()); + + // Decompile built output. + var decompileArgs = new List + { + "msi", "decompile", + outputPath, + "-intermediateFolder", decompileIntermediateFolder, + "-x", decompileExtractFolder, + "-o", decompilePath + }; + + if (this.ExtensionType != null) + { + decompileArgs.Add("-ext"); + decompileArgs.Add(Path.GetFullPath(new Uri(this.ExtensionType.Assembly.CodeBase).LocalPath)); + } + + decompileFunc(decompileArgs.ToArray()); + + // Build decompiled output. + var secondBuildArgs = new List + { + "build", + decompilePath, + "-o", decompileBuildPath, + "-intermediateFolder", decompileIntermediateFolder + }; + + if (this.ExtensionType != null) + { + secondBuildArgs.Add("-ext"); + secondBuildArgs.Add(Path.GetFullPath(new Uri(this.ExtensionType.Assembly.CodeBase).LocalPath)); + } + + secondBuildArgs.Add("-bindpath"); + secondBuildArgs.Add(outputFolder); + + secondBuildArgs.Add("-bindpath"); + secondBuildArgs.Add(decompileExtractFolder); + + buildFunc(secondBuildArgs.ToArray()); + } + } } } 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 { extension = new BurnExtensionCommandLine(this.ServiceProvider); } - if (extensionType == typeof(IBackendFactory)) + else if (extensionType == typeof(IBackendFactory)) { extension = new BurnBackendFactory(); } 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 this.OutputPath = Path.ChangeExtension(this.InputPath, ".wxs"); } + var extensionManager = this.ServiceProvider.GetService(); + var creator = this.ServiceProvider.GetService(); + var context = this.ServiceProvider.GetService(); - context.Extensions = this.ServiceProvider.GetService().GetServices(); + context.Extensions = extensionManager.GetServices(); + context.ExtensionData = extensionManager.GetServices(); context.DecompilePath = this.InputPath; context.DecompileType = decompileType; context.IntermediateFolder = this.IntermediateFolder; + context.SymbolDefinitionCreator = creator; context.OutputPath = this.OutputPath; 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 @@ - // 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. - -namespace WixToolset.Core.WindowsInstaller.Decompile -{ - using System; - using System.Collections.Generic; - using System.ComponentModel; - using System.IO; - using System.Linq; - using WixToolset.Core.Native.Msi; - using WixToolset.Core.WindowsInstaller.Unbind; - using WixToolset.Data; - using WixToolset.Data.WindowsInstaller; - using WixToolset.Extensibility.Data; - using WixToolset.Extensibility.Services; - - internal class DecompileMsiOrMsmCommand - { - public DecompileMsiOrMsmCommand(IWindowsInstallerDecompileContext context) - { - this.Context = context; - this.Messaging = context.ServiceProvider.GetService(); - } - - private IWindowsInstallerDecompileContext Context { get; } - - private IMessaging Messaging { get; } - - public IWindowsInstallerDecompileResult Execute() - { - var result = this.Context.ServiceProvider.GetService(); - - try - { - using (var database = new Database(this.Context.DecompilePath, OpenDatabase.ReadOnly)) - { - // Delete the directory and its files to prevent cab extraction failure due to an existing file. - if (Directory.Exists(this.Context.ExtractFolder)) - { - Directory.Delete(this.Context.ExtractFolder, true); - } - - var backendHelper = this.Context.ServiceProvider.GetService(); - - 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); - var output = unbindCommand.Execute(); - var extractedFilePaths = new List(unbindCommand.ExportedFiles); - - 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); - result.Document = decompiler.Decompile(output); - - result.Platform = GetPlatformFromOutput(output); - - // extract the files from the cabinets - if (!String.IsNullOrEmpty(this.Context.ExtractFolder) && !this.Context.SuppressExtractCabinets) - { - var fileDirectory = String.IsNullOrEmpty(this.Context.CabinetExtractFolder) ? Path.Combine(this.Context.ExtractFolder, "File") : this.Context.CabinetExtractFolder; - - var extractCommand = new ExtractCabinetsCommand(output, database, this.Context.DecompilePath, fileDirectory, this.Context.IntermediateFolder, this.Context.TreatProductAsModule); - extractCommand.Execute(); - - extractedFilePaths.AddRange(extractCommand.ExtractedFiles); - result.ExtractedFilePaths = extractedFilePaths; - } - else - { - result.ExtractedFilePaths = new string[0]; - } - } - } - catch (Win32Exception e) - { - if (0x6E == e.NativeErrorCode) // ERROR_OPEN_FAILED - { - throw new WixException(ErrorMessages.OpenDatabaseFailed(this.Context.DecompilePath)); - } - - throw; - } - - return result; - } - - private static Platform? GetPlatformFromOutput(WindowsInstallerData output) - { - var template = output.Tables["_SummaryInformation"]?.Rows.SingleOrDefault(row => row.FieldAsInteger(0) == 7)?.FieldAsString(1); - - return Decompiler.GetPlatformFromTemplateSummaryInformation(template?.Split(';')); - } - } -} 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 /// /// Creates a new decompiler object with a default set of table definitions. /// - public Decompiler(IMessaging messaging, IBackendHelper backendHelper, IEnumerable extensions, string baseSourcePath, bool suppressCustomTables, bool suppressDroppingEmptyTables, bool suppressRelativeActionSequencing, bool suppressUI, bool treatProductAsModule) + public Decompiler(IMessaging messaging, IBackendHelper backendHelper, IWindowsInstallerDecompilerHelper decompilerHelper, IEnumerable extensions, IEnumerable extensionData, ISymbolDefinitionCreator creator, string baseSourcePath, bool suppressCustomTables, bool suppressDroppingEmptyTables, bool suppressRelativeActionSequencing, bool suppressUI, bool treatProductAsModule) { this.Messaging = messaging; this.BackendHelper = backendHelper; + this.DecompilerHelper = decompilerHelper; this.Extensions = extensions; + this.ExtensionData = extensionData; + this.SymbolDefinitionCreator = creator; this.BaseSourcePath = baseSourcePath ?? "SourceDir"; this.SuppressCustomTables = suppressCustomTables; this.SuppressDroppingEmptyTables = suppressDroppingEmptyTables; @@ -65,8 +68,14 @@ namespace WixToolset.Core.WindowsInstaller.Decompile private IBackendHelper BackendHelper { get; } + private IWindowsInstallerDecompilerHelper DecompilerHelper { get; } + private IEnumerable Extensions { get; } + private IEnumerable ExtensionData { get; } + + private ISymbolDefinitionCreator SymbolDefinitionCreator { get; } + private Dictionary ExtensionsByTableName { get; } private string BaseSourcePath { get; } @@ -87,8 +96,6 @@ namespace WixToolset.Core.WindowsInstaller.Decompile private bool Compressed { get; set; } - private XElement RootElement { get; set; } - private TableDefinitionCollection TableDefinitions { get; } private bool ShortNames { get; set; } @@ -101,8 +108,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile { if (null == this.uiElement) { - this.uiElement = new XElement(Names.UIElement); - this.RootElement.Add(this.uiElement); + this.uiElement = this.DecompilerHelper.AddElementToRoot(new XElement(Names.UIElement)); } return this.uiElement; @@ -111,8 +117,6 @@ namespace WixToolset.Core.WindowsInstaller.Decompile private Dictionary Singletons { get; } = new Dictionary(); - private Dictionary IndexedElements { get; } = new Dictionary(); - private Dictionary PatchTargetFiles { get; } = new Dictionary(); /// @@ -122,13 +126,23 @@ namespace WixToolset.Core.WindowsInstaller.Decompile /// The serialized WiX source code. public XDocument Decompile(WindowsInstallerData output) { - if (null == output) + this.OutputType = output.Type; + + switch (this.OutputType) { - throw new ArgumentNullException(nameof(output)); + case OutputType.Module: + this.DecompilerHelper.RootElement = new XElement(Names.ModuleElement); + break; + case OutputType.PatchCreation: + this.DecompilerHelper.RootElement = new XElement(Names.PatchCreationElement); + break; + case OutputType.Product: + this.DecompilerHelper.RootElement = new XElement(Names.PackageElement); + break; + default: + throw new InvalidOperationException("Unknown output type."); } - this.OutputType = output.Type; - // collect the table definitions from the output this.TableDefinitions.Clear(); foreach (var table in output.Tables) @@ -146,30 +160,11 @@ namespace WixToolset.Core.WindowsInstaller.Decompile } // add any missing extension table definitions -#if TODO_DECOMPILER_EXTENSIONS foreach (var extension in this.Extensions) { - this.AddExtension(extension); - } -#endif - - switch (this.OutputType) - { - case OutputType.Module: - this.RootElement = new XElement(Names.ModuleElement); - break; - case OutputType.PatchCreation: - this.RootElement = new XElement(Names.PatchCreationElement); - break; - case OutputType.Product: - this.RootElement = new XElement(Names.PackageElement); - break; - default: - throw new InvalidOperationException("Unknown output type."); + this.AddExtensionTableDefinitions(extension); } - var xWix = new XElement(Names.WixElement, this.RootElement); - // try to decompile the database file // stop processing if an error previously occurred if (this.Messaging.EncounteredError) @@ -192,16 +187,14 @@ namespace WixToolset.Core.WindowsInstaller.Decompile this.FinalizeDecompile(output.Tables); // return the XML document only if decompilation completed successfully - var document = new XDocument(xWix); - return this.Messaging.EncounteredError ? null : document; + return this.Messaging.EncounteredError ? null : new XDocument(new XElement(Names.WixElement, this.DecompilerHelper.RootElement)); } -#if TODO_DECOMPILER_EXTENSIONS - private void AddExtension(IWindowsInstallerBackendDecompilerExtension extension) + private void AddExtensionTableDefinitions(IWindowsInstallerDecompilerExtension extension) { if (null != extension.TableDefinitions) { - foreach (TableDefinition tableDefinition in extension.TableDefinitions) + foreach (var tableDefinition in extension.TableDefinitions) { if (!this.ExtensionsByTableName.ContainsKey(tableDefinition.Name)) { @@ -214,7 +207,6 @@ namespace WixToolset.Core.WindowsInstaller.Decompile } } } -#endif internal static Platform? GetPlatformFromTemplateSummaryInformation(string[] template) { @@ -234,75 +226,10 @@ namespace WixToolset.Core.WindowsInstaller.Decompile return null; } - /// - /// Gets the element corresponding to the row it came from. - /// - /// The row corresponding to the element. - /// The indexed element. - private XElement GetIndexedElement(Row row) - { - return this.GetIndexedElement(row.TableDefinition.Name, row.GetPrimaryKey(DecompilerConstants.PrimaryKeyDelimiter)); - } - - /// - /// Gets the element corresponding to the primary key of the given table. - /// - /// The table corresponding to the element. - /// The primary key corresponding to the element. - /// The indexed element. - private XElement GetIndexedElement(string table, params string[] primaryKey) - { - return this.TryGetIndexedElement(table, out var element, primaryKey) ? element : null; - } - - /// - /// Tries to get the element corresponding to the primary key of the given table. - /// - /// The table corresponding to the element. - /// The indexed element. - /// Whether the element was found. - private bool TryGetIndexedElement(Row row, out XElement xElement) - { - return this.TryGetIndexedElement(row.TableDefinition.Name, out xElement, row.GetPrimaryKey(DecompilerConstants.PrimaryKeyDelimiter)); - } - - /// - /// Tries to get the element corresponding to the primary key of the given table. - /// - /// The table corresponding to the element. - /// The indexed element. - /// The primary key corresponding to the element. - /// Whether the element was found. - private bool TryGetIndexedElement(string table, out XElement xElement, params string[] primaryKey) - { - return this.IndexedElements.TryGetValue(String.Concat(table, ':', String.Join(DecompilerConstants.PrimaryKeyDelimiterString, primaryKey)), out xElement); - } - - /// - /// Index an element by its corresponding row. - /// - /// The row corresponding to the element. - /// The element to index. - private void IndexElement(Row row, XElement element) - { - this.IndexedElements.Add(String.Concat(row.TableDefinition.Name, ':', row.GetPrimaryKey(DecompilerConstants.PrimaryKeyDelimiter)), element); - } - - /// - /// Index an element by its corresponding row. - /// - /// The element to index. - /// - /// - private void IndexElement(XElement element, string table, params string[] primaryKey) - { - this.IndexedElements.Add(String.Concat(table, ':', String.Join(DecompilerConstants.PrimaryKeyDelimiterString, primaryKey)), element); - } - private Dictionary> IndexTableOneToMany(IEnumerable rows, int column = 0) { return rows - .ToLookup(row => row.FieldAsString(column), row => this.GetIndexedElement(row)) + .ToLookup(row => row.FieldAsString(column), row => this.DecompilerHelper.GetIndexedElement(row)) .ToDictionary(lookup => lookup.Key, lookup => lookup.ToList()); } @@ -319,7 +246,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile private void AddChildToParent(string parentName, XElement xChild, Row row, int column) { var key = row.FieldAsString(column); - if (this.TryGetIndexedElement(parentName, out var xParent, key)) + if (this.DecompilerHelper.TryGetIndexedElement(parentName, key, out var xParent)) { xParent.Add(xChild); } @@ -419,7 +346,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile { XElement xAction; - if (this.TryGetIndexedElement("CustomAction", out var _, actionSymbol.Action)) // custom action + if (this.DecompilerHelper.TryGetIndexedElement("CustomAction", actionSymbol.Action, out var _)) // custom action { xAction = new XElement(Names.CustomElement, new XAttribute("Action", actionSymbol.Action), @@ -455,7 +382,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile break; } } - else if (this.TryGetIndexedElement("Dialog", out var _, actionSymbol.Action)) // dialog + else if (this.DecompilerHelper.TryGetIndexedElement("Dialog", actionSymbol.Action, out var _)) // dialog { xAction = new XElement(Names.CustomElement, new XAttribute("Dialog", actionSymbol.Action), @@ -495,7 +422,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile { xSequence = new XElement(Names.WxsNamespace + sequenceTable); - this.RootElement.Add(xSequence); + this.DecompilerHelper.AddElementToRoot(xSequence); this.Singletons.Add(sequenceTable, xSequence); } @@ -669,14 +596,12 @@ namespace WixToolset.Core.WindowsInstaller.Decompile /// The property element. private XElement EnsureProperty(string id) { - XElement xProperty; - - if (!this.TryGetIndexedElement("Property", out xProperty, id)) + if (!this.DecompilerHelper.TryGetIndexedElement("Property", id, out var xProperty)) { xProperty = new XElement(Names.PropertyElement, new XAttribute("Id", id)); - this.RootElement.Add(xProperty); - this.IndexElement(xProperty, "Property", id); + this.DecompilerHelper.AddElementToRoot(xProperty); + this.DecompilerHelper.IndexElement("Property", id, xProperty); } return xProperty; @@ -713,6 +638,11 @@ namespace WixToolset.Core.WindowsInstaller.Decompile this.FinalizeSequenceTables(tables); this.FinalizeVerbTable(tables); } + + foreach (var extension in this.Extensions) + { + extension.PostDecompileTables(tables); + } } /// @@ -743,7 +673,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile { foreach (var row in controlTable.Rows) { - var xControl = this.GetIndexedElement(row); + var xControl = this.DecompilerHelper.GetIndexedElement(row); if ("CheckBox" == row.FieldAsString(2)) { @@ -791,9 +721,9 @@ namespace WixToolset.Core.WindowsInstaller.Decompile { // Add the TARGETDIR StandardDirectory if a component is directly parented there. if (componentTable.Rows.Any(row => row.FieldAsString(2) == "TARGETDIR") - && this.TryGetIndexedElement("Directory", out var xDirectory, "TARGETDIR")) + && this.DecompilerHelper.TryGetIndexedElement("Directory", "TARGETDIR", out var xDirectory)) { - this.RootElement.Add(xDirectory); + this.DecompilerHelper.AddElementToRoot(xDirectory); } foreach (var row in componentTable.Rows) @@ -803,12 +733,12 @@ namespace WixToolset.Core.WindowsInstaller.Decompile if (String.IsNullOrEmpty(keyPath)) { - var xComponent = this.GetIndexedElement("Component", row.FieldAsString(0)); + var xComponent = this.DecompilerHelper.GetIndexedElement("Component", row.FieldAsString(0)); xComponent.SetAttributeValue("KeyPath", "yes"); } else if (WindowsInstallerConstants.MsidbComponentAttributesRegistryKeyPath == (attributes & WindowsInstallerConstants.MsidbComponentAttributesRegistryKeyPath)) { - if (this.TryGetIndexedElement("Registry", out var xRegistry, keyPath)) + if (this.DecompilerHelper.TryGetIndexedElement("Registry", keyPath, out var xRegistry)) { if (xRegistry.Name.LocalName == "RegistryValue") { @@ -826,7 +756,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile } else if (WindowsInstallerConstants.MsidbComponentAttributesODBCDataSource == (attributes & WindowsInstallerConstants.MsidbComponentAttributesODBCDataSource)) { - if (this.TryGetIndexedElement("ODBCDataSource", out var xOdbcDataSource, keyPath)) + if (this.DecompilerHelper.TryGetIndexedElement("ODBCDataSource", keyPath, out var xOdbcDataSource)) { xOdbcDataSource.SetAttributeValue("KeyPath", "yes"); } @@ -837,7 +767,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile } else { - if (this.TryGetIndexedElement("File", out var xFile, keyPath)) + if (this.DecompilerHelper.TryGetIndexedElement("File", keyPath, out var xFile)) { xFile.SetAttributeValue("KeyPath", "yes"); } @@ -854,8 +784,8 @@ namespace WixToolset.Core.WindowsInstaller.Decompile { foreach (FileRow fileRow in fileTable.Rows) { - if (this.TryGetIndexedElement("Component", out var xComponent, fileRow.Component) - && this.TryGetIndexedElement(fileRow, out var xFile)) + if (this.DecompilerHelper.TryGetIndexedElement("Component", fileRow.Component, out var xComponent) + && this.DecompilerHelper.TryGetIndexedElement(fileRow, out var xFile)) { xComponent.Add(xFile); } @@ -871,8 +801,8 @@ namespace WixToolset.Core.WindowsInstaller.Decompile { foreach (var row in odbcDataSourceTable.Rows) { - if (this.TryGetIndexedElement("Component", out var xComponent, row.FieldAsString(1)) - && this.TryGetIndexedElement(row, out var xOdbcDataSource)) + if (this.DecompilerHelper.TryGetIndexedElement("Component", row.FieldAsString(1), out var xComponent) + && this.DecompilerHelper.TryGetIndexedElement(row, out var xOdbcDataSource)) { xComponent.Add(xOdbcDataSource); } @@ -888,8 +818,8 @@ namespace WixToolset.Core.WindowsInstaller.Decompile { foreach (var row in registryTable.Rows) { - if (this.TryGetIndexedElement("Component", out var xComponent, row.FieldAsString(5)) - && this.TryGetIndexedElement(row, out var xRegistry)) + if (this.DecompilerHelper.TryGetIndexedElement("Component", row.FieldAsString(5), out var xComponent) + && this.DecompilerHelper.TryGetIndexedElement(row, out var xRegistry)) { xComponent.Add(xRegistry); } @@ -927,10 +857,10 @@ namespace WixToolset.Core.WindowsInstaller.Decompile { foreach (var dialogRow in dialogTable.Rows) { - var xDialog = this.GetIndexedElement(dialogRow); + var xDialog = this.DecompilerHelper.GetIndexedElement(dialogRow); var dialogId = dialogRow.FieldAsString(0); - if (!this.TryGetIndexedElement("Control", out var xControl, dialogId, dialogRow.FieldAsString(7))) + if (!this.DecompilerHelper.TryGetIndexedElement("Control", dialogId, dialogRow.FieldAsString(7), out var xControl)) { this.Messaging.Write(WarningMessages.ExpectedForeignRow(dialogRow.SourceLineNumbers, "Dialog", dialogRow.GetPrimaryKey(DecompilerConstants.PrimaryKeyDelimiter), "Dialog", dialogId, "Control_First", dialogRow.FieldAsString(7), "Control")); } @@ -949,7 +879,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile var controlNext = controlRow.FieldAsString(10); if (!String.IsNullOrEmpty(controlNext)) { - if (this.TryGetIndexedElement("Control", out xControl, dialogId, controlNext)) + if (this.DecompilerHelper.TryGetIndexedElement("Control", dialogId, controlNext, out xControl)) { // looped back to the first control in the dialog if (addedControls.Contains(xControl)) @@ -972,7 +902,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile var controlDefault = dialogRow.FieldAsString(8); if (!String.IsNullOrEmpty(controlDefault)) { - if (this.TryGetIndexedElement("Control", out var xDefaultControl, dialogId, controlDefault)) + if (this.DecompilerHelper.TryGetIndexedElement("Control", dialogId, controlDefault, out var xDefaultControl)) { xDefaultControl.SetAttributeValue("Default", "yes"); } @@ -986,7 +916,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile var controlCancel = dialogRow.FieldAsString(8); if (!String.IsNullOrEmpty(controlCancel)) { - if (this.TryGetIndexedElement("Control", out var xCancelControl, dialogId, controlCancel)) + if (this.DecompilerHelper.TryGetIndexedElement("Control", dialogId, controlCancel, out var xCancelControl)) { xCancelControl.SetAttributeValue("Cancel", "yes"); } @@ -1004,13 +934,13 @@ namespace WixToolset.Core.WindowsInstaller.Decompile foreach (var controlRow in controlTable.Rows) { var dialogId = controlRow.FieldAsString(0); - if (!this.TryGetIndexedElement("Dialog", out var xDialog, dialogId)) + if (!this.DecompilerHelper.TryGetIndexedElement("Dialog", dialogId, out var xDialog)) { this.Messaging.Write(WarningMessages.ExpectedForeignRow(controlRow.SourceLineNumbers, "Control", controlRow.GetPrimaryKey(DecompilerConstants.PrimaryKeyDelimiter), "Dialog_", dialogId, "Dialog")); continue; } - var xControl = this.GetIndexedElement(controlRow); + var xControl = this.DecompilerHelper.GetIndexedElement(controlRow); if (!addedControls.Contains(xControl)) { xControl.SetAttributeValue("TabSkip", "yes"); @@ -1035,11 +965,11 @@ namespace WixToolset.Core.WindowsInstaller.Decompile { foreach (var row in duplicateFileTable.Rows) { - var xCopyFile = this.GetIndexedElement(row); + var xCopyFile = this.DecompilerHelper.GetIndexedElement(row); var destination = row.FieldAsString(4); if (!String.IsNullOrEmpty(destination)) { - if (this.TryGetIndexedElement("Directory", out var _, destination)) + if (this.DecompilerHelper.TryGetIndexedElement("Directory", destination, out var _)) { xCopyFile.SetAttributeValue("DestinationDirectory", destination); } @@ -1056,11 +986,11 @@ namespace WixToolset.Core.WindowsInstaller.Decompile { foreach (var row in moveFileTable.Rows) { - var xCopyFile = this.GetIndexedElement(row); + var xCopyFile = this.DecompilerHelper.GetIndexedElement(row); var source = row.FieldAsString(4); if (!String.IsNullOrEmpty(source)) { - if (this.TryGetIndexedElement("Directory", out var _, source)) + if (this.DecompilerHelper.TryGetIndexedElement("Directory", source, out var _)) { xCopyFile.SetAttributeValue("SourceDirectory", source); } @@ -1071,7 +1001,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile } var destination = row.FieldAsString(5); - if (this.TryGetIndexedElement("Directory", out var _, destination)) + if (this.DecompilerHelper.TryGetIndexedElement("Directory", destination, out var _)) { xCopyFile.SetAttributeValue("DestinationDirectory", destination); } @@ -1134,7 +1064,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile // TODO: warn about mismatch between columns } - this.IndexElement(row, xProtectRange); + this.DecompilerHelper.IndexElement(row, xProtectRange); } } @@ -1144,8 +1074,8 @@ namespace WixToolset.Core.WindowsInstaller.Decompile { foreach (var row in externalFilesTable.Rows) { - if (this.TryGetIndexedElement(row, out var xExternalFile) - && this.TryGetIndexedElement("FamilyFileRanges", out var xProtectRange, row.FieldAsString(0), row.FieldAsString(0))) + if (this.DecompilerHelper.TryGetIndexedElement(row, out var xExternalFile) + && this.DecompilerHelper.TryGetIndexedElement("FamilyFileRanges", row.FieldAsString(0), row.FieldAsString(0), out var xProtectRange)) { xExternalFile.Add(xProtectRange); usedProtectRanges.Add(xProtectRange); @@ -1178,7 +1108,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile continue; } - if (this.TryGetIndexedElement("FamilyFileRanges", out var xProtectRange, upgradedImagesRow.FieldAsString(4), row.FieldAsString(1))) + if (this.DecompilerHelper.TryGetIndexedElement("FamilyFileRanges", upgradedImagesRow.FieldAsString(4), row.FieldAsString(1), out var xProtectRange)) { xTargetFile.Add(xProtectRange); usedProtectRanges.Add(xProtectRange); @@ -1190,7 +1120,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile { foreach (var row in familyFileRangesTable.Rows) { - var xProtectRange = this.GetIndexedElement(row); + var xProtectRange = this.DecompilerHelper.GetIndexedElement(row); if (!usedProtectRanges.Contains(xProtectRange)) { @@ -1276,7 +1206,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile // set the disk identifiers and sources for files foreach (var fileRow in tables["File"]?.Rows.Cast() ?? Enumerable.Empty()) { - var xFile = this.GetIndexedElement("File", fileRow.File); + var xFile = this.DecompilerHelper.GetIndexedElement("File", fileRow.File); // Don't bother processing files that are orphaned (and won't show up in the output anyway) if (null != xFile.Parent) @@ -1328,7 +1258,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile // set the file assemblies and manifests foreach (var row in tables["MsiAssembly"]?.Rows ?? Enumerable.Empty()) { - if (this.TryGetIndexedElement("Component", out var xComponent, row.FieldAsString(0))) + if (this.DecompilerHelper.TryGetIndexedElement("Component", row.FieldAsString(0), out var xComponent)) { foreach (var xFile in xComponent.Elements(Names.FileElement).Where(x => x.Attribute("KeyPath")?.Value == "yes")) { @@ -1346,8 +1276,8 @@ namespace WixToolset.Core.WindowsInstaller.Decompile // nest the TypeLib elements foreach (var row in tables["TypeLib"]?.Rows ?? Enumerable.Empty()) { - var xComponent = this.GetIndexedElement("Component", row.FieldAsString(2)); - var xTypeLib = this.GetIndexedElement(row); + var xComponent = this.DecompilerHelper.GetIndexedElement("Component", row.FieldAsString(2)); + var xTypeLib = this.DecompilerHelper.GetIndexedElement(row); foreach (var xFile in xComponent.Elements(Names.FileElement).Where(x => x.Attribute("KeyPath")?.Value == "yes")) { @@ -1374,7 +1304,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile var mimeRef = row.FieldAsString(3); if (null != mimeRef) { - if (this.TryGetIndexedElement("MIME", out var xMime, mimeRef)) + if (this.DecompilerHelper.TryGetIndexedElement("MIME", mimeRef, out var xMime)) { xMime.SetAttributeValue("Default", "yes"); } @@ -1389,7 +1319,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile foreach (var row in tables["MIME"]?.Rows ?? Enumerable.Empty()) { - var xMime = this.GetIndexedElement(row); + var xMime = this.DecompilerHelper.GetIndexedElement(row); if (extensionsByExtensionId.TryGetValue(row.FieldAsString(1), out var xExtensions)) { @@ -1427,9 +1357,9 @@ namespace WixToolset.Core.WindowsInstaller.Decompile foreach (var row in classRows) { var clsid = row.FieldAsString(0); - var xClass = this.GetIndexedElement(row); + var xClass = this.DecompilerHelper.GetIndexedElement(row); - if (this.TryGetIndexedElement("ProgId", out var xProgId, row.FieldAsString(3))) + if (this.DecompilerHelper.TryGetIndexedElement("ProgId", row.FieldAsString(3), out var xProgId)) { if (addedProgIds.TryGetValue(xProgId, out var progid)) { @@ -1451,7 +1381,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile foreach (var row in tables["ProgId"]?.Rows ?? Enumerable.Empty()) { var clsid = row.FieldAsString(2); - var xProgId = this.GetIndexedElement(row); + var xProgId = this.DecompilerHelper.GetIndexedElement(row); if (!addedProgIds.ContainsKey(xProgId) && null != clsid && null == xProgId.Parent) { @@ -1475,7 +1405,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile foreach (var row in tables["Extension"]?.Rows?.Where(row => row.FieldAsString(2) != null) ?? Enumerable.Empty()) { - var xProgId = this.GetIndexedElement("ProgId", row.FieldAsString(2)); + var xProgId = this.DecompilerHelper.GetIndexedElement("ProgId", row.FieldAsString(2)); // Haven't added the progId yet and it doesn't have a parent progId if (!addedProgIds.ContainsKey(xProgId) && null == xProgId.Parent) @@ -1510,7 +1440,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile var bits = row.FieldAsInteger(1); if (WindowsInstallerConstants.MsidbCustomActionTypeHideTarget == (bits & WindowsInstallerConstants.MsidbCustomActionTypeHideTarget) && WindowsInstallerConstants.MsidbCustomActionTypeInScript == (bits & WindowsInstallerConstants.MsidbCustomActionTypeInScript) - && this.TryGetIndexedElement("Property", out var xProperty, row.FieldAsString(0)) + && this.DecompilerHelper.TryGetIndexedElement("Property", row.FieldAsString(0), out var xProperty) && String.IsNullOrEmpty(xProperty.Attribute("Value")?.Value) && xProperty.Attribute("Secure")?.Value != "yes" && xProperty.Attribute("SuppressModularization")?.Value != "yes") @@ -1531,10 +1461,10 @@ namespace WixToolset.Core.WindowsInstaller.Decompile { foreach (var row in tables["RemoveFile"]?.Rows ?? Enumerable.Empty()) { - var xRemove = this.GetIndexedElement(row); + var xRemove = this.DecompilerHelper.GetIndexedElement(row); var property = row.FieldAsString(3); - if (this.TryGetIndexedElement("Directory", out var _, property)) + if (this.DecompilerHelper.TryGetIndexedElement("Directory", property, out var _)) { xRemove.SetAttributeValue("Directory", property); } @@ -1562,7 +1492,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile { var id = row.FieldAsString(0); var table = row.FieldAsString(1); - var xPermission = this.GetIndexedElement(row); + var xPermission = this.DecompilerHelper.GetIndexedElement(row); if ("CreateFolder" == table) { @@ -1580,7 +1510,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile } else { - if (this.TryGetIndexedElement(table, out var xParent, id)) + if (this.DecompilerHelper.TryGetIndexedElement(table, id, out var xParent)) { xParent.Add(xPermission); } @@ -1651,12 +1581,12 @@ namespace WixToolset.Core.WindowsInstaller.Decompile { var appSearches = IndexTable(tables["AppSearch"], keyColumn: 1, dataColumn: 0); var ccpSearches = IndexTable(tables["CCPSearch"], keyColumn: 0, dataColumn: null); - var drLocators = tables["DrLocator"]?.Rows.ToDictionary(row => this.GetIndexedElement(row), row => row); + var drLocators = tables["DrLocator"]?.Rows.ToDictionary(row => this.DecompilerHelper.GetIndexedElement(row), row => row); var xComplianceCheck = new XElement(Names.ComplianceCheckElement); if (ccpSearches.Keys.Any(ccpSignature => !appSearches.ContainsKey(ccpSignature))) { - this.RootElement.Add(xComplianceCheck); + this.DecompilerHelper.AddElementToRoot(xComplianceCheck); } // index the locator tables by their signatures @@ -1703,7 +1633,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile foreach (var locatorRow in locatorRows) { var used = true; - var xSearch = this.GetIndexedElement(locatorRow); + var xSearch = this.DecompilerHelper.GetIndexedElement(locatorRow); if ("Signature" == locatorRow.TableDefinition.Name && 0 < xSignatureSearches.Count) { @@ -1791,7 +1721,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile { if ("DrLocator" == parentLocatorRow.TableDefinition.Name) { - var xParentSearch = this.GetIndexedElement(parentLocatorRow); + var xParentSearch = this.DecompilerHelper.GetIndexedElement(parentLocatorRow); if (xParentSearch.HasElements) { @@ -1824,7 +1754,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile } else if ("RegLocator" == parentLocatorRow.TableDefinition.Name) { - var xParentSearch = this.GetIndexedElement(parentLocatorRow); + var xParentSearch = this.DecompilerHelper.GetIndexedElement(parentLocatorRow); xParentSearch.Add(xSearch); xUsedSearches.Add(xSearch); @@ -1982,11 +1912,11 @@ namespace WixToolset.Core.WindowsInstaller.Decompile foreach (var row in shortcutTable.Rows) { - var xShortcut = this.GetIndexedElement(row); + var xShortcut = this.DecompilerHelper.GetIndexedElement(row); var target = row.FieldAsString(4); - if (this.TryGetIndexedElement("Feature", out var _, target)) + if (this.DecompilerHelper.TryGetIndexedElement("Feature", target, out var _)) { xShortcut.SetAttributeValue("Advertise", "yes"); this.SetPrimaryFeature(row, 4, 3); @@ -2301,7 +2231,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile xMajorUpgrade.SetAttributeValue("Schedule", scheduledType); } - this.RootElement.Add(xMajorUpgrade); + this.DecompilerHelper.AddElementToRoot(xMajorUpgrade); } } } @@ -2326,7 +2256,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile { if (xExtensions.TryGetValue(row.FieldAsString(0), out var xVerbExtensions)) { - var xVerb = this.GetIndexedElement(row); + var xVerb = this.DecompilerHelper.GetIndexedElement(row); foreach (var xVerbExtension in xVerbExtensions) { @@ -2351,8 +2281,9 @@ namespace WixToolset.Core.WindowsInstaller.Decompile var sourcePath = new StringBuilder(); var component = xFile.Parent; + var xDirectory = component.Parent; - for (var xDirectory = component.Parent; null != xDirectory && xDirectory.Name.LocalName == "Directory"; xDirectory = xDirectory.Parent) + while (xDirectory?.Name.LocalName == "Directory") { string name; @@ -2387,6 +2318,14 @@ namespace WixToolset.Core.WindowsInstaller.Decompile sourcePath.Insert(0, Path.DirectorySeparatorChar); sourcePath.Insert(0, name); } + + xDirectory = xDirectory.Parent; + } + + if (xDirectory?.Name.LocalName == "StandardDirectory" && WindowsInstallerStandard.TryGetStandardDirectory(xDirectory.Attribute("Id").Value, out var standardDirectory)) + { + sourcePath.Insert(0, Path.DirectorySeparatorChar); + sourcePath.Insert(0, standardDirectory.Name); } return sourcePath.ToString(); @@ -2470,13 +2409,13 @@ namespace WixToolset.Core.WindowsInstaller.Decompile this.ShortNames = false; this.Singletons.Clear(); - this.IndexedElements.Clear(); + //this.IndexedElements.Clear(); this.PatchTargetFiles.Clear(); // set the codepage if its not neutral (0) if (0 != codepage) { - this.RootElement.SetAttributeValue("Codepage", codepage); + this.DecompilerHelper.RootElement.SetAttributeValue("Codepage", codepage); } if (this.OutputType == OutputType.Module) @@ -2484,44 +2423,51 @@ namespace WixToolset.Core.WindowsInstaller.Decompile var table = tables["_SummaryInformation"]; var row = table.Rows.SingleOrDefault(r => r.FieldAsInteger(0) == 9); this.ModularizationGuid = row?.FieldAsString(1); - this.RootElement.SetAttributeValue("Guid", this.ModularizationGuid); + this.DecompilerHelper.RootElement.SetAttributeValue("Guid", this.ModularizationGuid); + } + + this.RemoveExtensionDataFromTables(tables); + + foreach (var extension in this.Extensions) + { + extension.PreDecompileTables(tables); } + } + + private void RemoveExtensionDataFromTables(TableIndexedCollection tables) + { + var tableDefinitionBySymbolDefinitionName = this.TableDefinitions.Where(t => t.SymbolDefinition != null).ToDictionary(t => t.SymbolDefinition.Name); // index the rows from the extension libraries var indexedExtensionTables = new Dictionary>(); -#if TODO_DECOMPILER_EXTENSIONS - foreach (var extension in this.Extensions) + foreach (var extension in this.ExtensionData) { // Get the optional library from the extension with the rows to be removed. - var library = extension.GetLibraryToRemove(this.tableDefinitions); + var library = extension.GetLibrary(this.SymbolDefinitionCreator); if (library != null) { - foreach (var row in library.Sections.SelectMany(s => s.Tables).SelectMany(t => t.Rows)) + foreach (var symbol in library.Sections.SelectMany(s => s.Symbols)) { - string primaryKey; - string tableName; - - // the Actions table needs to be handled specially - if (table.Name == "WixAction") + if (this.TryGetPrimaryKeyFromSymbol(tableDefinitionBySymbolDefinitionName, symbol, out var tableName, out var primaryKey)) { - primaryKey = row.FieldAsString(1); - tableName = row.FieldAsString(0); - - if (this.outputType == OutputType.Module) - { - tableName = "Module" + tableName; - } - } - else - { - primaryKey = row.GetPrimaryKey(DecompilerConstants.PrimaryKeyDelimiter); - tableName = table.Name; - } - - if (primaryKey != null) - { - HashSet indexedExtensionRows; - if (!indexedExtensionTables.TryGetValue(tableName, out indexedExtensionRows)) + //// the Actions table needs to be handled specially + //if (table.Name == "WixAction") + //{ + // primaryKey = symbol.FieldAsString(1); + // tableName = symbol.FieldAsString(0); + + // if (this.outputType == OutputType.Module) + // { + // tableName = "Module" + tableName; + // } + //} + //else + //{ + // primaryKey = symbol.GetPrimaryKey(DecompilerConstants.PrimaryKeyDelimiter); + // tableName = table.Name; + //} + + if (!indexedExtensionTables.TryGetValue(tableName, out var indexedExtensionRows)) { indexedExtensionRows = new HashSet(); indexedExtensionTables.Add(tableName, indexedExtensionRows); @@ -2532,7 +2478,6 @@ namespace WixToolset.Core.WindowsInstaller.Decompile } } } -#endif // remove the rows from the extension libraries (to allow full round-tripping) foreach (var kvp in indexedExtensionTables) @@ -2540,8 +2485,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile var tableName = kvp.Key; var indexedExtensionRows = kvp.Value; - var table = tables[tableName]; - if (null != table) + if (tables.TryGetTable(tableName, out var table)) { var originalRows = new RowDictionary(table); @@ -2550,7 +2494,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile foreach (var row in originalRows.Values) { - if (!indexedExtensionRows.Contains(row.GetPrimaryKey(DecompilerConstants.PrimaryKeyDelimiter))) + if (!indexedExtensionRows.Contains(row.GetPrimaryKey())) { table.Rows.Add(row); } @@ -2559,6 +2503,55 @@ namespace WixToolset.Core.WindowsInstaller.Decompile } } + private bool TryGetPrimaryKeyFromSymbol(Dictionary tableDefinitionBySymbolDefinitionName, IntermediateSymbol symbol, out string tableName, out string primaryKey) + { + tableName = null; + primaryKey = null; + + if (symbol is WixActionSymbol actionSymbol) + { + tableName = actionSymbol.SequenceTable.WindowsInstallerTableName(); + primaryKey = actionSymbol.Action; + return true; + } + + if (!tableDefinitionBySymbolDefinitionName.TryGetValue(symbol.Definition.Name, out var tableDefinition)) + { + return false; + } + + tableName = tableDefinition.Name; + + if (tableDefinition.SymbolIdIsPrimaryKey) + { + primaryKey = symbol.Id.Id; + } + else + { + var sb = new StringBuilder(); + + for (var i = 0; i < symbol.Fields.Length && i < tableDefinition.Columns.Length; ++i) + { + var column = tableDefinition.Columns[i]; + var field = symbol.Fields[i]; + + if (column.PrimaryKey) + { + if (sb.Length > 0) + { + sb.Append('/'); + } + + sb.Append(field.AsString()); + } + } + + primaryKey = sb.ToString(); + } + + return true; + } + /// /// Decompile the tables. /// @@ -2581,7 +2574,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile // empty tables may be kept with EnsureTable if the user set the proper option if (0 == table.Rows.Count && this.SuppressDroppingEmptyTables) { - this.RootElement.Add(new XElement(Names.EnsureTableElement, new XAttribute("Id", table.Name))); + this.DecompilerHelper.AddElementToRoot(new XElement(Names.EnsureTableElement, new XAttribute("Id", table.Name))); } switch (table.Name) @@ -2901,14 +2894,11 @@ namespace WixToolset.Core.WindowsInstaller.Decompile break; default: -#if TODO_DECOMPILER_EXTENSIONS - if (this.ExtensionsByTableName.TryGetValue(table.Name, out var extension) - { - extension.DecompileTable(table); - } - else -#endif - if (!this.SuppressCustomTables) + if (this.ExtensionsByTableName.TryGetValue(table.Name, out var extension)) + { + extension.TryDecompileTable(table); + } + else if (!this.SuppressCustomTables) { this.DecompileCustomTable(table); } @@ -3039,7 +3029,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile break; case 3: { - var productName = this.RootElement.Attribute("Name")?.Value; + var productName = this.DecompilerHelper.RootElement.Attribute("Name")?.Value; if (value != productName) { xSummaryInformation.SetAttributeValue("Description", value); @@ -3048,7 +3038,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile } case 4: { - var productManufacturer = this.RootElement.Attribute("Manufacturer")?.Value; + var productManufacturer = this.DecompilerHelper.RootElement.Attribute("Manufacturer")?.Value; if (value != productManufacturer) { xSummaryInformation.SetAttributeValue("Manufacturer", value); @@ -3065,7 +3055,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile var template = value.Split(';'); if (0 < template.Length && 0 < template[template.Length - 1].Length) { - this.RootElement.SetAttributeValue("Language", template[template.Length - 1]); + this.DecompilerHelper.RootElement.SetAttributeValue("Language", template[template.Length - 1]); } break; case 14: @@ -3073,7 +3063,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile // Default InstallerVersion. if (installerVersion != 500) { - this.RootElement.SetAttributeValue("InstallerVersion", installerVersion); + this.DecompilerHelper.RootElement.SetAttributeValue("InstallerVersion", installerVersion); } break; case 15: @@ -3083,29 +3073,24 @@ namespace WixToolset.Core.WindowsInstaller.Decompile this.ShortNames = true; if (OutputType.Product == this.OutputType) { - this.RootElement.SetAttributeValue("ShortNames", "yes"); + this.DecompilerHelper.RootElement.SetAttributeValue("ShortNames", "yes"); } } if (0x2 == (wordCount & 0x2)) { this.Compressed = true; - - if (OutputType.Product == this.OutputType) - { - this.RootElement.SetAttributeValue("Compressed", "yes"); - } } if (OutputType.Product == this.OutputType) { if (0x8 == (wordCount & 0x8)) { - this.RootElement.SetAttributeValue("Scope", "perUser"); + this.DecompilerHelper.RootElement.SetAttributeValue("Scope", "perUser"); } else { - var xAllUsers = this.RootElement.Elements(Names.PropertyElement).SingleOrDefault(p => p.Attribute("Id")?.Value == "ALLUSERS"); + var xAllUsers = this.DecompilerHelper.RootElement.Elements(Names.PropertyElement).SingleOrDefault(p => p.Attribute("Id")?.Value == "ALLUSERS"); if (xAllUsers?.Attribute("Value")?.Value == "1") { xAllUsers?.Remove(); @@ -3118,9 +3103,14 @@ namespace WixToolset.Core.WindowsInstaller.Decompile } } + if (OutputType.Product == this.OutputType && !this.Compressed) + { + this.DecompilerHelper.RootElement.SetAttributeValue("Compressed", "no"); + } + if (xSummaryInformation.HasAttributes) { - this.RootElement.Add(xSummaryInformation); + this.DecompilerHelper.AddElementToRoot(xSummaryInformation); } } else @@ -3173,7 +3163,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile } } - this.RootElement.Add(xPatchInformation); + this.DecompilerHelper.AddElementToRoot(xPatchInformation); } } @@ -3212,8 +3202,8 @@ namespace WixToolset.Core.WindowsInstaller.Decompile row.IsColumnNull(5) || row.FieldAsInteger(5) != 1 ? null : new XAttribute("ActivateAtStorage", "yes"), row.IsColumnNull(6) || row.FieldAsInteger(6) != 1 ? null : new XAttribute("RunAsInteractiveUser", "yes")); - this.RootElement.Add(appId); - this.IndexElement(row, appId); + this.DecompilerHelper.AddElementToRoot(appId); + this.DecompilerHelper.IndexElement(row, appId); } } @@ -3239,7 +3229,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile SetControlAttributes(bbControlRow.Attributes, xControl); } - if (this.TryGetIndexedElement("Billboard", out var xBillboard, bbControlRow.Billboard)) + if (this.DecompilerHelper.TryGetIndexedElement("Billboard", bbControlRow.Billboard, out var xBillboard)) { xBillboard.Add(xControl); } @@ -3264,7 +3254,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile new XAttribute("Id", row.FieldAsString(0)), new XAttribute("Feature", row.FieldAsString(1))); - this.IndexElement(row, xBillboard); + this.DecompilerHelper.IndexElement(row, xBillboard); billboards.Add(String.Format(CultureInfo.InvariantCulture, "{0}|{1:0000000000}", row[0], row[3]), row); } @@ -3272,7 +3262,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile foreach (var row in billboards.Values) { - var xBillboard = this.GetIndexedElement(row); + var xBillboard = this.DecompilerHelper.GetIndexedElement(row); if (!billboardActions.TryGetValue(row.FieldAsString(2), out var xBillboardAction)) { @@ -3299,7 +3289,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile new XAttribute("Id", row.FieldAsString(0)), new XAttribute("SourceFile", row.FieldAsString(1))); - this.RootElement.Add(xBinary); + this.DecompilerHelper.AddElementToRoot(xBinary); } } @@ -3311,7 +3301,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile { foreach (var row in table.Rows) { - if (this.TryGetIndexedElement("File", out var xFile, row.FieldAsString(0))) + if (this.DecompilerHelper.TryGetIndexedElement("File", row.FieldAsString(0), out var xFile)) { xFile.SetAttributeValue("BindPath", row.FieldAsString(1)); } @@ -3389,7 +3379,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile } this.AddChildToParent("Component", xClass, row, 2); - this.IndexElement(row, xClass); + this.DecompilerHelper.IndexElement(row, xClass); } } @@ -3656,7 +3646,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile } } - this.IndexElement(controlRow, xControl); + this.DecompilerHelper.IndexElement(controlRow, xControl); } } @@ -3668,7 +3658,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile { foreach (var row in table.Rows) { - if (this.TryGetIndexedElement("Control", out var xControl, row.FieldAsString(0), row.FieldAsString(1))) + if (this.DecompilerHelper.TryGetIndexedElement("Control", row.FieldAsString(0), row.FieldAsString(1), out var xControl)) { switch (row.FieldAsString(2)) { @@ -3730,14 +3720,14 @@ namespace WixToolset.Core.WindowsInstaller.Decompile 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); - this.IndexElement(row, xPublish); + this.DecompilerHelper.IndexElement(row, xPublish); } foreach (var row in controlEvents.Values) { - if (this.TryGetIndexedElement("Control", out var xControl, row.FieldAsString(0), row.FieldAsString(1))) + if (this.DecompilerHelper.TryGetIndexedElement("Control", row.FieldAsString(0), row.FieldAsString(1), out var xControl)) { - var xPublish = this.GetIndexedElement(row); + var xPublish = this.DecompilerHelper.GetIndexedElement(row); xControl.Add(xPublish); } else @@ -3928,7 +3918,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile xCustomTable.Add(xRow); } - this.RootElement.Add(xCustomTable); + this.DecompilerHelper.AddElementToRoot(xCustomTable); } } @@ -3944,7 +3934,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile new XAttribute("Directory", row.FieldAsString(0))); this.AddChildToParent("Component", xCreateFolder, row, 1); - this.IndexElement(row, xCreateFolder); + this.DecompilerHelper.IndexElement(row, xCreateFolder); } } @@ -4115,8 +4105,8 @@ namespace WixToolset.Core.WindowsInstaller.Decompile xCustomAction.SetAttributeValue("PatchUninstall", "yes"); } - this.RootElement.Add(xCustomAction); - this.IndexElement(row, xCustomAction); + this.DecompilerHelper.AddElementToRoot(xCustomAction); + this.DecompilerHelper.IndexElement(row, xCustomAction); } } @@ -4148,7 +4138,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile } } - this.IndexElement(row, xComponentSearch); + this.DecompilerHelper.IndexElement(row, xComponentSearch); } } @@ -4162,7 +4152,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile { if (!row.IsColumnNull(1)) { - if (this.TryGetIndexedElement("Component", out var xComponent, row.FieldAsString(0))) + if (this.DecompilerHelper.TryGetIndexedElement("Component", row.FieldAsString(0), out var xComponent)) { xComponent.SetAttributeValue("ComPlusFlags", row.FieldAsInteger(1)); } @@ -4247,7 +4237,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile } this.AddChildToParent("Directory", xComponent, row, 2); - this.IndexElement(row, xComponent); + this.DecompilerHelper.IndexElement(row, xComponent); } } @@ -4259,7 +4249,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile { foreach (var row in table.Rows) { - if (this.TryGetIndexedElement("Feature", out var xFeature, row.FieldAsString(0))) + if (this.DecompilerHelper.TryGetIndexedElement("Feature", row.FieldAsString(0), out var xFeature)) { var xLevel = new XElement(Names.LevelElement, row.IsColumnNull(2) ? null : new XAttribute("Condition", row.FieldAsString(2)), @@ -4303,7 +4293,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile !row.IsColumnNull(6) ? new XAttribute("Title", row.FieldAsString(6)) : null); this.UIElement.Add(xDialog); - this.IndexElement(row, xDialog); + this.DecompilerHelper.IndexElement(row, xDialog); } } @@ -4367,13 +4357,13 @@ namespace WixToolset.Core.WindowsInstaller.Decompile } } - this.IndexElement(row, xDirectory); + this.DecompilerHelper.IndexElement(row, xDirectory); } // nest the directories foreach (var row in table.Rows) { - var xDirectory = this.GetIndexedElement(row); + var xDirectory = this.DecompilerHelper.GetIndexedElement(row); var id = row.FieldAsString(0); @@ -4383,26 +4373,26 @@ namespace WixToolset.Core.WindowsInstaller.Decompile } else if (row.IsColumnNull(1) || WindowsInstallerStandard.IsStandardDirectory(id)) { - this.RootElement.Add(xDirectory); + this.DecompilerHelper.AddElementToRoot(xDirectory); } else { var parentDirectoryId = row.FieldAsString(1); - if (!this.TryGetIndexedElement("Directory", out var xParentDirectory, parentDirectoryId)) + if (!this.DecompilerHelper.TryGetIndexedElement("Directory", parentDirectoryId, out var xParentDirectory)) { this.Messaging.Write(WarningMessages.ExpectedForeignRow(row.SourceLineNumbers, table.Name, row.GetPrimaryKey(DecompilerConstants.PrimaryKeyDelimiter), "Directory_Parent", row.FieldAsString(1), "Directory")); } else if (xParentDirectory == xDirectory) // another way to specify a root directory { - this.RootElement.Add(xDirectory); + this.DecompilerHelper.AddElementToRoot(xDirectory); } else { // TARGETDIR is omitted but if this directory is a first-generation descendant, add it as a root. if (parentDirectoryId == "TARGETDIR") { - this.RootElement.Add(xDirectory); + this.DecompilerHelper.AddElementToRoot(xDirectory); } else { @@ -4426,7 +4416,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile XAttributeIfNotNull("Path", row, 2), XAttributeIfNotNull("Depth", row, 3)); - this.IndexElement(row, xDirectorySearch); + this.DecompilerHelper.IndexElement(row, xDirectorySearch); } } @@ -4459,7 +4449,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile // destination directory/property is set in FinalizeDuplicateMoveFileTables this.AddChildToParent("Component", xCopyFile, row, 1); - this.IndexElement(row, xCopyFile); + this.DecompilerHelper.IndexElement(row, xCopyFile); } } @@ -4570,7 +4560,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile new XAttribute("Event", row.FieldAsString(2)), new XAttribute("Attribute", row.FieldAsString(3))); - if (this.TryGetIndexedElement("Control", out var xControl, row.FieldAsString(0), row.FieldAsString(1))) + if (this.DecompilerHelper.TryGetIndexedElement("Control", row.FieldAsString(0), row.FieldAsString(1), out var xControl)) { xControl.Add(xSubscribe); } @@ -4595,7 +4585,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile if (!row.IsColumnNull(3)) { - if (this.TryGetIndexedElement("MIME", out var xMime, row.FieldAsString(3))) + if (this.DecompilerHelper.TryGetIndexedElement("MIME", row.FieldAsString(3), out var xMime)) { xMime.SetAttributeValue("Default", "yes"); } @@ -4614,7 +4604,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile this.AddChildToParent("Component", xExtension, row, 1); } - this.IndexElement(row, xExtension); + this.DecompilerHelper.IndexElement(row, xExtension); } } @@ -4682,7 +4672,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile } this.AddChildToParent("ImageFamilies", xExternalFile, row, 0); - this.IndexElement(row, xExternalFile); + this.DecompilerHelper.IndexElement(row, xExternalFile); } } @@ -4761,7 +4751,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile feature.SetAttributeValue("Absent", "disallow"); } - this.IndexElement(row, feature); + this.DecompilerHelper.IndexElement(row, feature); // sort the features by their display column (and append the identifier to ensure unique keys) sortedFeatures.Add(String.Format(CultureInfo.InvariantCulture, "{0:00000}|{1}", row.FieldAsInteger(4), row[0]), row); @@ -4770,15 +4760,15 @@ namespace WixToolset.Core.WindowsInstaller.Decompile // nest the features foreach (var row in sortedFeatures.Values) { - var xFeature = this.GetIndexedElement("Feature", row.FieldAsString(0)); + var xFeature = this.DecompilerHelper.GetIndexedElement("Feature", row.FieldAsString(0)); if (row.IsColumnNull(1)) { - this.RootElement.Add(xFeature); + this.DecompilerHelper.AddElementToRoot(xFeature); } else { - if (this.TryGetIndexedElement("Feature", out var xParentFeature, row.FieldAsString(1))) + if (this.DecompilerHelper.TryGetIndexedElement("Feature", row.FieldAsString(1), out var xParentFeature)) { if (xParentFeature == xFeature) { @@ -4809,7 +4799,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile new XAttribute("Id", row.FieldAsString(1))); this.AddChildToParent("Feature", xComponentRef, row, 0); - this.IndexElement(row, xComponentRef); + this.DecompilerHelper.IndexElement(row, xComponentRef); } } @@ -4855,7 +4845,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile xFile.SetAttributeValue("Compressed", "yes"); } - this.IndexElement(fileRow, xFile); + this.DecompilerHelper.IndexElement(fileRow, xFile); } } @@ -4882,7 +4872,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile { foreach (var row in table.Rows) { - if (this.TryGetIndexedElement("File", out var xFile, row.FieldAsString(0))) + if (this.DecompilerHelper.TryGetIndexedElement("File", row.FieldAsString(0), out var xFile)) { if (!row.IsColumnNull(1)) { @@ -4912,7 +4902,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile new XAttribute("Id", row.FieldAsString(0)), new XAttribute("SourceFile", row.FieldAsString(1))); - this.RootElement.Add(icon); + this.DecompilerHelper.AddElementToRoot(icon); } } @@ -4932,8 +4922,8 @@ namespace WixToolset.Core.WindowsInstaller.Decompile row.IsColumnNull(4) ? null : new XAttribute("DiskPrompt", row.FieldAsString(4)), row.IsColumnNull(5) ? null : new XAttribute("VolumeLabel", row.FieldAsString(5))); - this.RootElement.Add(family); - this.IndexElement(row, family); + this.DecompilerHelper.AddElementToRoot(family); + this.DecompilerHelper.IndexElement(row, family); } } @@ -5035,7 +5025,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile } } - this.IndexElement(row, xIniFileSearch); + this.DecompilerHelper.IndexElement(row, xIniFileSearch); } } @@ -5071,7 +5061,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile new XAttribute("Condition", row.FieldAsString(0)), new XAttribute("Message", row.FieldAsString(1))); - this.RootElement.Add(condition); + this.DecompilerHelper.AddElementToRoot(condition); } } @@ -5230,7 +5220,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile } } - this.IndexElement(row, xPermission); + this.DecompilerHelper.IndexElement(row, xPermission); } } @@ -5260,8 +5250,8 @@ namespace WixToolset.Core.WindowsInstaller.Decompile xMedia.SetAttributeValue("Cabinet", cabinet); } - this.RootElement.Add(xMedia); - this.IndexElement(mediaRow, xMedia); + this.DecompilerHelper.AddElementToRoot(xMedia); + this.DecompilerHelper.IndexElement(mediaRow, xMedia); } } @@ -5277,7 +5267,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile new XAttribute("ContentType", row.FieldAsString(0)), row.IsColumnNull(2) ? null : new XAttribute("Class", row.FieldAsString(2))); - this.IndexElement(row, mime); + this.DecompilerHelper.IndexElement(row, mime); } } @@ -5338,7 +5328,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile } } - this.RootElement.Add(configuration); + this.DecompilerHelper.AddElementToRoot(configuration); } } @@ -5355,7 +5345,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile new XAttribute("RequiredLanguage", row.FieldAsString(3)), XAttributeIfNotNull("RequiredVersion", row, 4)); - this.RootElement.Add(xDependency); + this.DecompilerHelper.AddElementToRoot(xDependency); } } @@ -5382,7 +5372,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile xExclusion.SetAttributeValue("ExcludeExceptLanguage", -excludedLanguage); } - this.RootElement.Add(xExclusion); + this.DecompilerHelper.AddElementToRoot(xExclusion); } } @@ -5402,7 +5392,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile var xIgnoreTable = new XElement(Names.IgnoreTableElement, new XAttribute("Id", tableName)); - this.RootElement.Add(xIgnoreTable); + this.DecompilerHelper.AddElementToRoot(xIgnoreTable); } } } @@ -5417,10 +5407,10 @@ namespace WixToolset.Core.WindowsInstaller.Decompile { var row = table.Rows[0]; - this.RootElement.SetAttributeValue("Id", row.FieldAsString(0)); + this.DecompilerHelper.RootElement.SetAttributeValue("Id", row.FieldAsString(0)); // support Language columns that are treated as integers as well as strings (the WiX default, to support localizability) - this.RootElement.SetAttributeValue("Language", row.FieldAsString(1)); - this.RootElement.SetAttributeValue("Version", row.FieldAsString(2)); + this.DecompilerHelper.RootElement.SetAttributeValue("Language", row.FieldAsString(1)); + this.DecompilerHelper.RootElement.SetAttributeValue("Version", row.FieldAsString(2)); } else { @@ -5442,7 +5432,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile new XAttribute("Column", row.FieldAsString(2)), XAttributeIfNotNull("Value", row, 3)); - this.RootElement.Add(xSubstitution); + this.DecompilerHelper.AddElementToRoot(xSubstitution); } } @@ -5487,7 +5477,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile } this.AddChildToParent("Component", xCopyFile, row, 1); - this.IndexElement(row, xCopyFile); + this.DecompilerHelper.IndexElement(row, xCopyFile); } } @@ -5503,7 +5493,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile new XAttribute("Id", row.FieldAsString(0)), new XAttribute("SourceFile", row.FieldAsString(1))); - this.IndexElement(row, xDigitalCertificate); + this.DecompilerHelper.IndexElement(row, xDigitalCertificate); } } @@ -5520,7 +5510,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile this.AddChildToParent("MsiDigitalCertificate", xDigitalSignature, row, 2); - if (this.TryGetIndexedElement(row.FieldAsString(0), out var xParentElement, row.FieldAsString(1))) + if (this.DecompilerHelper.TryGetIndexedElement(row.FieldAsString(0), row.FieldAsString(1), out var xParentElement)) { xParentElement.Add(xDigitalSignature); } @@ -5560,7 +5550,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile break; } - this.RootElement.Add(xEmbeddedChainer); + this.DecompilerHelper.AddElementToRoot(xEmbeddedChainer); } } @@ -5735,7 +5725,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile return; } - this.IndexElement(row, xPermissionEx); + this.DecompilerHelper.IndexElement(row, xPermissionEx); } } @@ -5748,7 +5738,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile if (0 < table.Rows.Count) { var xPackageCertificates = new XElement(Names.PatchCertificatesElement); - this.RootElement.Add(xPackageCertificates); + this.DecompilerHelper.AddElementToRoot(xPackageCertificates); this.AddCertificates(table, xPackageCertificates); } } @@ -5762,7 +5752,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile if (0 < table.Rows.Count) { var xPatchCertificates = new XElement(Names.PatchCertificatesElement); - this.RootElement.Add(xPatchCertificates); + this.DecompilerHelper.AddElementToRoot(xPatchCertificates); this.AddCertificates(table, xPatchCertificates); } } @@ -5776,7 +5766,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile { foreach (var row in table.Rows) { - if (this.TryGetIndexedElement("MsiDigitalCertificate", out var xDigitalCertificate, row.FieldAsString(1))) + if (this.DecompilerHelper.TryGetIndexedElement("MsiDigitalCertificate", row.FieldAsString(1), out var xDigitalCertificate)) { parent.Add(xDigitalCertificate); } @@ -5846,7 +5836,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile break; } - this.IndexElement(row, xOdbcDataSource); + this.DecompilerHelper.IndexElement(row, xOdbcDataSource); } } @@ -5865,7 +5855,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile XAttributeIfNotNull("SetupFile", row, 4)); this.AddChildToParent("Component", xOdbcDriver, row, 1); - this.IndexElement(row, xOdbcDriver); + this.DecompilerHelper.IndexElement(row, xOdbcDriver); } } @@ -6010,7 +6000,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile } } - this.RootElement.Add(xPatchMetadata); + this.DecompilerHelper.AddElementToRoot(xPatchMetadata); } } @@ -6049,7 +6039,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile patchSequence.SetAttributeValue("Supersede", "yes"); } - this.RootElement.Add(patchSequence); + this.DecompilerHelper.AddElementToRoot(patchSequence); } } @@ -6068,13 +6058,13 @@ namespace WixToolset.Core.WindowsInstaller.Decompile XAttributeIfNotNull("Icon", row, 4), XAttributeIfNotNull("IconIndex", row, 5)); - this.IndexElement(row, xProgId); + this.DecompilerHelper.IndexElement(row, xProgId); } // nest the ProgIds foreach (var row in table.Rows) { - var xProgId = this.GetIndexedElement(row); + var xProgId = this.DecompilerHelper.GetIndexedElement(row); if (!row.IsColumnNull(1)) { @@ -6107,13 +6097,13 @@ namespace WixToolset.Core.WindowsInstaller.Decompile case "AllowProductCodeMismatches": if ("1" == value) { - this.RootElement.SetAttributeValue("AllowProductCodeMismatches", "yes"); + this.DecompilerHelper.RootElement.SetAttributeValue("AllowProductCodeMismatches", "yes"); } break; case "AllowProductVersionMajorMismatches": if ("1" == value) { - this.RootElement.SetAttributeValue("AllowMajorVersionMismatches", "yes"); + this.DecompilerHelper.RootElement.SetAttributeValue("AllowMajorVersionMismatches", "yes"); } break; case "ApiPatchingSymbolFlags": @@ -6127,7 +6117,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile value = value.Substring(2); } - this.RootElement.SetAttributeValue("SymbolFlags", Convert.ToInt32(value, 16)); + this.DecompilerHelper.RootElement.SetAttributeValue("SymbolFlags", Convert.ToInt32(value, 16)); } catch { @@ -6138,13 +6128,13 @@ namespace WixToolset.Core.WindowsInstaller.Decompile case "DontRemoveTempFolderWhenFinished": if ("1" == value) { - this.RootElement.SetAttributeValue("CleanWorkingFolder", "no"); + this.DecompilerHelper.RootElement.SetAttributeValue("CleanWorkingFolder", "no"); } break; case "IncludeWholeFilesOnly": if ("1" == value) { - this.RootElement.SetAttributeValue("WholeFilesOnly", "yes"); + this.DecompilerHelper.RootElement.SetAttributeValue("WholeFilesOnly", "yes"); } break; case "ListOfPatchGUIDsToReplace": @@ -6158,7 +6148,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile var xReplacePatch = new XElement(Names.ReplacePatchElement, new XAttribute("Id", guidMatch.Value)); - this.RootElement.Add(xReplacePatch); + this.DecompilerHelper.AddElementToRoot(xReplacePatch); } } break; @@ -6172,25 +6162,25 @@ namespace WixToolset.Core.WindowsInstaller.Decompile var xTargetProductCode = new XElement(Names.TargetProductCodeElement, new XAttribute("Id", targetProductCodeString)); - this.RootElement.Add(xTargetProductCode); + this.DecompilerHelper.AddElementToRoot(xTargetProductCode); } } break; case "PatchGUID": - this.RootElement.SetAttributeValue("Id", value); + this.DecompilerHelper.RootElement.SetAttributeValue("Id", value); break; case "PatchSourceList": - this.RootElement.SetAttributeValue("SourceList", value); + this.DecompilerHelper.RootElement.SetAttributeValue("SourceList", value); break; case "PatchOutputPath": - this.RootElement.SetAttributeValue("OutputPath", value); + this.DecompilerHelper.RootElement.SetAttributeValue("OutputPath", value); break; default: var patchProperty = new XElement(Names.PatchPropertyElement, new XAttribute("Name", name), new XAttribute("Value", value)); - this.RootElement.Add(patchProperty); + this.DecompilerHelper.AddElementToRoot(patchProperty); break; } } @@ -6260,22 +6250,22 @@ namespace WixToolset.Core.WindowsInstaller.Decompile switch (id) { case "Manufacturer": - this.RootElement.SetAttributeValue("Manufacturer", value); + this.DecompilerHelper.RootElement.SetAttributeValue("Manufacturer", value); continue; case "ProductCode": - this.RootElement.SetAttributeValue("ProductCode", value.ToUpper(CultureInfo.InvariantCulture)); + this.DecompilerHelper.RootElement.SetAttributeValue("ProductCode", value.ToUpper(CultureInfo.InvariantCulture)); continue; case "ProductLanguage": - this.RootElement.SetAttributeValue("Language", value); + this.DecompilerHelper.RootElement.SetAttributeValue("Language", value); continue; case "ProductName": - this.RootElement.SetAttributeValue("Name", value); + this.DecompilerHelper.RootElement.SetAttributeValue("Name", value); continue; case "ProductVersion": - this.RootElement.SetAttributeValue("Version", value); + this.DecompilerHelper.RootElement.SetAttributeValue("Version", value); continue; case "UpgradeCode": - this.RootElement.SetAttributeValue("UpgradeCode", value); + this.DecompilerHelper.RootElement.SetAttributeValue("UpgradeCode", value); continue; } } @@ -6340,14 +6330,14 @@ namespace WixToolset.Core.WindowsInstaller.Decompile } } - this.IndexElement(row, radioButton); + this.DecompilerHelper.IndexElement(row, radioButton); } // nest the radio buttons var xRadioButtonGroups = new Dictionary(); foreach (var row in table.Rows.OrderBy(row => row.FieldAsString(0)).ThenBy(row => row.FieldAsInteger(1))) { - var xRadioButton = this.GetIndexedElement(row); + var xRadioButton = this.DecompilerHelper.GetIndexedElement(row); if (!xRadioButtonGroups.TryGetValue(row.FieldAsString(0), out var xRadioButtonGroup)) { @@ -6395,7 +6385,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile break; } - this.IndexElement(row, xRegistryKey); + this.DecompilerHelper.IndexElement(row, xRegistryKey); } else { @@ -6480,7 +6470,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile xRegistryValue.SetAttributeValue("Value", String.Empty); } - this.IndexElement(row, xRegistryValue); + this.DecompilerHelper.IndexElement(row, xRegistryValue); } } } @@ -6552,7 +6542,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile } } - this.IndexElement(row, xRegistrySearch); + this.DecompilerHelper.IndexElement(row, xRegistrySearch); } } @@ -6588,7 +6578,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile } this.AddChildToParent("Component", xRemoveFolder, row, 1); - this.IndexElement(row, xRemoveFolder); + this.DecompilerHelper.IndexElement(row, xRemoveFolder); } else { @@ -6625,7 +6615,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile } this.AddChildToParent("Component", xRemoveFile, row, 1); - this.IndexElement(row, xRemoveFile); + this.DecompilerHelper.IndexElement(row, xRemoveFile); } } } @@ -6738,7 +6728,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile { foreach (var row in table.Rows) { - if (this.TryGetIndexedElement("File", out var xFile, row.FieldAsString(0))) + if (this.DecompilerHelper.TryGetIndexedElement("File", row.FieldAsString(0), out var xFile)) { xFile.SetAttributeValue("SelfRegCost", row.IsColumnNull(1) ? 0 : row.FieldAsInteger(1)); } @@ -6927,7 +6917,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile } this.AddChildToParent("Component", xServiceInstall, row, 11); - this.IndexElement(row, xServiceInstall); + this.DecompilerHelper.IndexElement(row, xServiceInstall); } } @@ -6943,17 +6933,17 @@ namespace WixToolset.Core.WindowsInstaller.Decompile new XAttribute("Name", row.FieldAsString(0)), new XAttribute("SourceFile", row.FieldAsString(1))); - this.IndexElement(row, xSfpCatalog); + this.DecompilerHelper.IndexElement(row, xSfpCatalog); } // nest the SFPCatalog elements foreach (var row in table.Rows) { - var xSfpCatalog = this.GetIndexedElement(row); + var xSfpCatalog = this.DecompilerHelper.GetIndexedElement(row); if (!row.IsColumnNull(2)) { - if (this.TryGetIndexedElement("SFPCatalog", out var xParentSFPCatalog, row.FieldAsString(2))) + if (this.DecompilerHelper.TryGetIndexedElement("SFPCatalog", row.FieldAsString(2), out var xParentSFPCatalog)) { xParentSFPCatalog.Add(xSfpCatalog); } @@ -6961,12 +6951,12 @@ namespace WixToolset.Core.WindowsInstaller.Decompile { xSfpCatalog.SetAttributeValue("Dependency", row.FieldAsString(2)); - this.RootElement.Add(xSfpCatalog); + this.DecompilerHelper.AddElementToRoot(xSfpCatalog); } } else { - this.RootElement.Add(xSfpCatalog); + this.DecompilerHelper.AddElementToRoot(xSfpCatalog); } } } @@ -7044,7 +7034,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile } this.AddChildToParent("Component", xShortcut, row, 3); - this.IndexElement(row, xShortcut); + this.DecompilerHelper.IndexElement(row, xShortcut); } } @@ -7093,7 +7083,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile fileSearch.SetAttributeValue("MaxDate", ConvertIntegerToDateTime(row.FieldAsInteger(7))); } - this.IndexElement(row, fileSearch); + this.DecompilerHelper.IndexElement(row, fileSearch); } } @@ -7110,7 +7100,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile xPatchTargetFile = new XElement(Names.TargetFileElement, new XAttribute("Id", row.FieldAsString(1))); - if (this.TryGetIndexedElement("TargetImages", out var xTargetImage, row.FieldAsString(0))) + if (this.DecompilerHelper.TryGetIndexedElement("TargetImages", row.FieldAsString(0), out var xTargetImage)) { xTargetImage.Add(xPatchTargetFile); } @@ -7192,7 +7182,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile } this.AddChildToParent("UpgradedImages", xTargetImage, row, 3); - this.IndexElement(row, xTargetImage); + this.DecompilerHelper.IndexElement(row, xTargetImage); } } @@ -7282,7 +7272,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile } // nested under the appropriate File element in FinalizeFileTable - this.IndexElement(row, xTypeLib); + this.DecompilerHelper.IndexElement(row, xTypeLib); } } @@ -7306,7 +7296,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile xUpgrade = new XElement(Names.UpgradeElement, new XAttribute("Id", upgradeRow.UpgradeCode)); - this.RootElement.Add(xUpgrade); + this.DecompilerHelper.AddElementToRoot(xUpgrade); xUpgrades.Add(upgradeRow.UpgradeCode, xUpgrade); } @@ -7435,7 +7425,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile AddSymbolPaths(row, 3, xUpgradeImage); this.AddChildToParent("ImageFamilies", xUpgradeImage, row, 4); - this.IndexElement(row, xUpgradeImage); + this.DecompilerHelper.IndexElement(row, xUpgradeImage); } } @@ -7485,7 +7475,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile XAttributeIfNotNull("Command", row, 3), XAttributeIfNotNull("Argument", row, 4)); - this.IndexElement(row, verb); + this.DecompilerHelper.IndexElement(row, verb); } } @@ -7537,7 +7527,7 @@ namespace WixToolset.Core.WindowsInstaller.Decompile var featureField = row.Fields[featureColumnIndex]; var componentField = row.Fields[componentColumnIndex]; - if (this.TryGetIndexedElement("FeatureComponents", out var xComponentRef, Convert.ToString(featureField.Data), Convert.ToString(componentField.Data))) + if (this.DecompilerHelper.TryGetIndexedElement("FeatureComponents", featureField.AsString(), componentField.AsString(), out var xComponentRef)) { xComponentRef.SetAttributeValue("Primary", "yes"); } 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 @@ +// 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. + +namespace WixToolset.Core.WindowsInstaller.ExtensibilityServices +{ + using System; + using System.Collections.Generic; + using System.Xml.Linq; + using WixToolset.Core.WindowsInstaller.Decompile; + using WixToolset.Data.WindowsInstaller; + using WixToolset.Extensibility.Services; + + internal class WindowsInstallerDecompilerHelper : IWindowsInstallerDecompilerHelper + { + public WindowsInstallerDecompilerHelper(IServiceProvider _) + { + } + + private Dictionary IndexedElements { get; } = new Dictionary(); + + #region IWindowsInstallerDecompilerHelper interfaces + + public XElement RootElement { get; set; } + + public XElement AddElementToRoot(string name, params object[] content) + { + var element = new XElement(Names.WxsNamespace + name, content); + + this.RootElement.Add(element); + + return element; + } + + public XElement AddElementToRoot(XName name, params object[] content) + { + var element = new XElement(name, content); + + this.RootElement.Add(element); + + return element; + } + + public XElement AddElementToRoot(XElement element) + { + this.RootElement.Add(element); + + return element; + } + + public XElement CreateElement(string name, params object[] content) + { + return new XElement(Names.WxsNamespace + name, content); + } + + public XElement GetIndexedElement(Row row) + { + return this.GetIndexedElement(row.TableDefinition.Name, row.GetPrimaryKey()); + } + + public XElement GetIndexedElement(string table, string primaryKey) + { + return this.TryGetIndexedElement(table, primaryKey, out var element) ? element : null; + } + + public XElement GetIndexedElement(string table, string primaryKey1, string primaryKey2) + { + return this.TryGetIndexedElement(table, primaryKey1, primaryKey2, out var element) ? element : null; + } + + public XElement GetIndexedElement(string table, string primaryKey1, string primaryKey2, string primaryKey3) + { + return this.TryGetIndexedElement(table, primaryKey1, primaryKey2, primaryKey3, out var element) ? element : null; + } + + public XElement GetIndexedElement(string table, string[] primaryKeys) + { + return this.TryGetIndexedElement(table, primaryKeys, out var element) ? element : null; + } + + public void IndexElement(Row row, XElement element) + { + this.IndexElement(row.Table.Name, row.GetPrimaryKey(), element); + } + + public void IndexElement(string table, string primaryKey, XElement element) + { + var key = String.Concat(table, ':', primaryKey); + this.IndexedElements.Add(key, element); + } + + public void IndexElement(string table, string primaryKey1, string primaryKey2, XElement element) + { + this.IndexElement(table, String.Join("/", primaryKey1, primaryKey2), element); + } + + public void IndexElement(string table, string primaryKey1, string primaryKey2, string primaryKey3, XElement element) + { + this.IndexElement(table, String.Join("/", primaryKey1, primaryKey2, primaryKey3), element); + } + + public void IndexElement(string table, string[] primaryKeys, XElement element) + { + this.IndexElement(table, String.Join("/", primaryKeys), element); + } + + public bool TryGetIndexedElement(Row row, out XElement element) + { + return this.TryGetIndexedElement(row.TableDefinition.Name, row.GetPrimaryKey(), out element); + } + + public bool TryGetIndexedElement(string table, string primaryKey, out XElement element) + { + var key = String.Concat(table, ':', primaryKey); + return this.IndexedElements.TryGetValue(key, out element); + } + + public bool TryGetIndexedElement(string table, string primaryKey1, string primaryKey2, out XElement element) + { + return this.TryGetIndexedElement(table, String.Join("/", primaryKey1, primaryKey2), out element); + } + + public bool TryGetIndexedElement(string table, string primaryKey1, string primaryKey2, string primaryKey3, out XElement element) + { + return this.TryGetIndexedElement(table, String.Join("/", primaryKey1, primaryKey2, primaryKey3), out element); + } + + public bool TryGetIndexedElement(string table, string[] primaryKeys, out XElement element) + { + return this.TryGetIndexedElement(table, String.Join("/", primaryKeys), out element); + } + + #endregion + } +} 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 } break; case ColumnType.Object: - var sourceFile = "FILE NOT EXPORTED, USE THE dark.exe -x OPTION TO EXPORT BINARIES"; + var sourceFile = "FILE NOT EXPORTED"; if (null != this.ExportBasePath) { 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 public IReadOnlyCollection Extensions { get; set; } + public IReadOnlyCollection ExtensionData { get; set; } + + public ISymbolDefinitionCreator SymbolDefinitionCreator { get; set; } + public string ExtractFolder { get; set; } 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 { public XDocument Document { get; set; } - public IReadOnlyCollection ExtractedFilePaths { get; set; } + public IList ExtractedFilePaths { get; set; } public Platform? Platform { get; set; } } 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 @@ namespace WixToolset.Core.WindowsInstaller { using System; + using System.Collections.Generic; + using System.ComponentModel; + using System.IO; + using System.Linq; + using WixToolset.Core.Native.Msi; using WixToolset.Core.WindowsInstaller.Decompile; - using WixToolset.Extensibility; + using WixToolset.Core.WindowsInstaller.Unbind; + using WixToolset.Data; + using WixToolset.Data.WindowsInstaller; using WixToolset.Extensibility.Data; using WixToolset.Extensibility.Services; @@ -16,12 +23,20 @@ namespace WixToolset.Core.WindowsInstaller internal WindowsInstallerDecompiler(IServiceProvider serviceProvider) { this.ServiceProvider = serviceProvider; + this.Messaging = serviceProvider.GetService(); } - public IServiceProvider ServiceProvider { get; } + private IServiceProvider ServiceProvider { get; } + + private IMessaging Messaging { get; } public IWindowsInstallerDecompileResult Decompile(IWindowsInstallerDecompileContext context) { + if (context.SymbolDefinitionCreator == null) + { + context.SymbolDefinitionCreator = this.ServiceProvider.GetService(); + } + // Pre-decompile. // foreach (var extension in context.Extensions) @@ -31,8 +46,7 @@ namespace WixToolset.Core.WindowsInstaller // Decompile. // - var command = new DecompileMsiOrMsmCommand(context); - var result = command.Execute(); + var result = this.Execute(context); if (result != null) { @@ -46,5 +60,68 @@ namespace WixToolset.Core.WindowsInstaller return result; } + + private IWindowsInstallerDecompileResult Execute(IWindowsInstallerDecompileContext context) + { + var result = context.ServiceProvider.GetService(); + + try + { + using (var database = new Database(context.DecompilePath, OpenDatabase.ReadOnly)) + { + // Delete the directory and its files to prevent cab extraction failure due to an existing file. + if (Directory.Exists(context.ExtractFolder)) + { + Directory.Delete(context.ExtractFolder, true); + } + + var backendHelper = context.ServiceProvider.GetService(); + var decompilerHelper = context.ServiceProvider.GetService(); + + var unbindCommand = new UnbindDatabaseCommand(this.Messaging, backendHelper, database, context.DecompilePath, context.DecompileType, context.ExtractFolder, context.IntermediateFolder, context.IsAdminImage, suppressDemodularization: false, skipSummaryInfo: false); + var output = unbindCommand.Execute(); + var extractedFilePaths = new List(unbindCommand.ExportedFiles); + + 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); + result.Document = decompiler.Decompile(output); + + result.Platform = GetPlatformFromOutput(output); + + // extract the files from the cabinets + if (!String.IsNullOrEmpty(context.ExtractFolder) && !context.SuppressExtractCabinets) + { + var fileDirectory = String.IsNullOrEmpty(context.CabinetExtractFolder) ? Path.Combine(context.ExtractFolder, "File") : context.CabinetExtractFolder; + + var extractCommand = new ExtractCabinetsCommand(output, database, context.DecompilePath, fileDirectory, context.IntermediateFolder, context.TreatProductAsModule); + extractCommand.Execute(); + + extractedFilePaths.AddRange(extractCommand.ExtractedFiles); + result.ExtractedFilePaths = extractedFilePaths; + } + else + { + result.ExtractedFilePaths = new string[0]; + } + } + } + catch (Win32Exception e) + { + if (0x6E == e.NativeErrorCode) // ERROR_OPEN_FAILED + { + throw new WixException(ErrorMessages.OpenDatabaseFailed(context.DecompilePath)); + } + + throw; + } + + return result; + } + + private static Platform? GetPlatformFromOutput(WindowsInstallerData output) + { + var template = output.Tables["_SummaryInformation"]?.Rows.SingleOrDefault(row => row.FieldAsInteger(0) == 7)?.FieldAsString(1); + + return Decompiler.GetPlatformFromTemplateSummaryInformation(template?.Split(';')); + } } } 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 /// /// Adds WindowsInstaller services. /// - /// - /// + /// Core service provider. + /// The core service provider provided to this method. public static IWixToolsetCoreServiceProvider AddWindowsInstallerBackend(this IWixToolsetCoreServiceProvider coreProvider) { AddServices(coreProvider); @@ -32,6 +32,7 @@ namespace WixToolset.Core.WindowsInstaller { // Singletons. coreProvider.AddService((provider, singletons) => AddSingleton(singletons, new WindowsInstallerBackendHelper(provider))); + coreProvider.AddService((provider, singletons) => AddSingleton(singletons, new WindowsInstallerDecompilerHelper(provider))); // Transients. coreProvider.AddService((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 internal class ExampleCompilerExtension : BaseCompilerExtension { - public override XNamespace Namespace => "http://www.example.com/scheams/v1/wxs"; - public string BundleExtensionId => "ExampleBundleExtension"; + private const string BundleExtensionId = "ExampleBundleExtension"; + + public override XNamespace Namespace => ExampleConstants.Namespace; public override void ParseElement(Intermediate intermediate, IntermediateSection section, XElement parentElement, XElement element, IDictionary context) { @@ -38,10 +39,12 @@ namespace Example.Extension } break; case "Component": + var componentId = context["ComponentId"]; + switch (element.Name.LocalName) { case "Example": - this.ParseExampleElement(intermediate, section, element); + this.ParseExampleElement(intermediate, section, element, componentId); processed = true; break; } @@ -54,7 +57,7 @@ namespace Example.Extension } } - private void ParseExampleElement(Intermediate intermediate, IntermediateSection section, XElement element) + private void ParseExampleElement(Intermediate intermediate, IntermediateSection section, XElement element, string componentId) { var sourceLineNumbers = this.ParseHelper.GetSourceLineNumbers(element); Identifier id = null; @@ -93,7 +96,8 @@ namespace Example.Extension if (!this.Messaging.EncounteredError) { var symbol = this.ParseHelper.CreateSymbol(section, sourceLineNumbers, "Example", id); - symbol.Set(0, value); + symbol.Set(0, componentId); + symbol.Set(1, value); } } @@ -152,12 +156,12 @@ namespace Example.Extension if (!this.Messaging.EncounteredError) { - this.ParseHelper.CreateWixSearchSymbol(section, sourceLineNumbers, element.Name.LocalName, id, variable, condition, after, this.BundleExtensionId); + this.ParseHelper.CreateWixSearchSymbol(section, sourceLineNumbers, element.Name.LocalName, id, variable, condition, after, BundleExtensionId); } if (!this.Messaging.EncounteredError) { - var symbol = section.AddSymbol(new ExampleSearchSymbol(sourceLineNumbers, id) + section.AddSymbol(new ExampleSearchSymbol(sourceLineNumbers, id) { SearchFor = searchFor, }); 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 @@ +// 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. + +namespace Example.Extension +{ + using System.Xml.Linq; + + internal class ExampleConstants + { + public static readonly XNamespace Namespace = "http://www.example.com/scheams/v1/wxs"; + + public static readonly XName ExampleName = Namespace + "Example"; + } +} 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 { extension = new ExampleWindowsInstallerBackendExtension(); } + else if (extensionType == typeof(IWindowsInstallerDecompilerExtension)) + { + extension = new ExampleWindowsInstallerDecompilerExtension(); + } else { 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 set { this.Fields[0].Data = value; } } - public string Value + public string ComponentRef { get { return (string)this.Fields[1].Data; } set { this.Fields[1].Data = value; } } + + public string Value + { + get { return (string)this.Fields[2].Data; } + set { this.Fields[2].Data = value; } + } } } 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 public enum ExampleSymbolFields { + ComponentRef, Value, } @@ -21,6 +22,12 @@ namespace Example.Extension public IntermediateField this[ExampleSymbolFields index] => this.Fields[(int)index]; + public string ComponentRef + { + get => this.Fields[(int)ExampleSymbolFields.ComponentRef]?.AsString(); + set => this.Set((int)ExampleSymbolFields.ComponentRef, value); + } + public string Value { 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 ExampleSymbolDefinitionType.Example.ToString(), new[] { + new IntermediateFieldDefinition(nameof(ExampleSymbolFields.ComponentRef), IntermediateFieldType.String), new IntermediateFieldDefinition(nameof(ExampleSymbolFields.Value), IntermediateFieldType.String), }, 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 ExampleSymbolDefinitions.Example, new[] { - new ColumnDefinition("Example", ColumnType.String, 72, true, false, ColumnCategory.Identifier), + new ColumnDefinition("Example", ColumnType.String, 72, true, false, ColumnCategory.Identifier, modularizeType: ColumnModularizeType.Column), + new ColumnDefinition("Component_", ColumnType.String, 72, false, false, ColumnCategory.Identifier, keyTable: "Component", keyColumn: 1, modularizeType: ColumnModularizeType.Column), new ColumnDefinition("Value", ColumnType.String, 0, false, false, ColumnCategory.Formatted), }, 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 switch (symbolType) { case ExampleSymbolDefinitionType.Example: - { - var row = (ExampleRow)this.BackendHelper.CreateRow(section, symbol, output, ExampleTableDefinitions.ExampleTable); - row.Example = symbol.Id.Id; - row.Value = symbol[0].AsString(); - } + { + var row = (ExampleRow)this.BackendHelper.CreateRow(section, symbol, output, ExampleTableDefinitions.ExampleTable); + row.Example = symbol.Id.Id; + row.ComponentRef = symbol[0].AsString(); + row.Value = symbol[1].AsString(); return true; + } } } 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 @@ +// 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. + +namespace Example.Extension +{ + using System; + using System.Collections.Generic; + using System.Xml.Linq; + using WixToolset.Data.WindowsInstaller; + using WixToolset.Extensibility; + + internal class ExampleWindowsInstallerDecompilerExtension : BaseWindowsInstallerDecompilerExtension + { + public override IReadOnlyCollection TableDefinitions => ExampleTableDefinitions.All; + + public override bool TryDecompileTable(Table table) + { + switch (table.Name) + { + case "Wix4Example": + this.ProcessExampleTable(table); + return true; + } + + return false; + } + + private void ProcessExampleTable(Table table) + { + foreach (var row in table.Rows) + { + var componentId = row.FieldAsString(1); + if (this.DecompilerHelper.TryGetIndexedElement("Component", componentId, out var component)) + { + component.Add(new XElement(ExampleConstants.ExampleName, + new XAttribute("Id", row.FieldAsString(0)), + new XAttribute("Value", row.FieldAsString(2)) + )); + } + } + } + } +} 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 using System; using System.IO; using System.Linq; + using System.Text.RegularExpressions; using Example.Extension; using WixBuildTools.TestSupport; using WixToolset.Core.TestPackage; @@ -23,10 +24,37 @@ namespace WixToolsetTest.CoreIntegration var results = build.BuildAndQuery(Build, "Wix4Example"); WixAssert.CompareLineByLine(new[] { - "Wix4Example:Foo\tBar" + "Wix4Example:Foo\tfilF5_pLhBuF5b4N9XEo52g_hUM5Lo\tBar" }, results); } + [Fact] + public void CanRoundtripExampleExtension() + { + var folder = TestData.Get(@"TestData", "ExampleExtension"); + var expectedOutputPath = Path.Combine(folder, "Decompiled-Expected.xml"); + + var build = new Builder(folder, typeof(ExampleExtensionFactory), new[] { Path.Combine(folder, "data") }); + using (var fs = new DisposableFileSystem()) + { + var decompileFolder = fs.GetFolder(); + var actualOutputPath = Path.Combine(decompileFolder, "decompiled.xml"); + + build.BuildAndDecompileAndBuild(Build, Decompile, actualOutputPath); + + var expected = File.ReadAllLines(expectedOutputPath); + var actual = File.ReadAllLines(actualOutputPath).Select(ReplaceGuids).ToArray(); ; + WixAssert.CompareLineByLine(expected, actual); + } + } + + private static string ReplaceGuids(string value) + { + value = String.IsNullOrWhiteSpace(value) ? value : Regex.Replace(value, @" ProductCode=\""\{[a-fA-F0-9\-]+\}\""", " ProductCode=\"{GUID}\""); + return String.IsNullOrWhiteSpace(value) ? value : Regex.Replace(value, @" Guid=\""\{[a-fA-F0-9\-]+\}\""", " Guid=\"{GUID}\""); + + } + [Fact] public void CanBuildWithExampleExtension() { @@ -64,7 +92,8 @@ namespace WixToolsetTest.CoreIntegration var example = section.Symbols.Where(t => t.Definition.Type == SymbolDefinitionType.MustBeFromAnExtension).Single(); Assert.Equal("Foo", example.Id?.Id); - Assert.Equal("Bar", example[0].AsString()); + Assert.Equal("filF5_pLhBuF5b4N9XEo52g_hUM5Lo", example[0].AsString()); + Assert.Equal("Bar", example[1].AsString()); } } @@ -192,8 +221,14 @@ namespace WixToolsetTest.CoreIntegration private static void Build(string[] args) { - var result = WixRunner.Execute(args) - .AssertSuccess(); + var result = WixRunner.Execute(args); + result.AssertSuccess(); + } + + private static void Decompile(string[] args) + { + var result = WixRunner.Execute(args); + result.AssertSuccess(); } } } 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 @@ - + - + @@ -39,4 +39,4 @@ - \ No newline at end of file + 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 @@ - + - + @@ -19,4 +19,4 @@ - \ No newline at end of file + 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 @@ - + @@ -15,7 +15,7 @@ - + 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 @@ - + 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 @@ - + 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 @@ - + 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 @@ + + + + + + + + + + + + + + + + + + + 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 @@ - + - + @@ -29,4 +29,4 @@ - \ No newline at end of file + 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 @@ - + - + @@ -16,4 +16,4 @@ - \ No newline at end of file + 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 var example = section.Symbols.Where(t => t.Definition.Type == SymbolDefinitionType.MustBeFromAnExtension).Single(); Assert.Equal("Foo", example.Id?.Id); - Assert.Equal("Bar", example[0].AsString()); + Assert.Equal("Bar", example[1].AsString()); } } @@ -352,7 +352,8 @@ namespace WixToolsetTest.CoreIntegration var examples = section.Symbols.Where(t => t.Definition.Type == SymbolDefinitionType.MustBeFromAnExtension).ToArray(); WixAssert.CompareLineByLine(new string[] { "Foo", "Other" }, examples.Select(t => t.Id?.Id).ToArray()); - WixAssert.CompareLineByLine(new[] { "Bar", "Value" }, examples.Select(t => t[0].AsString()).ToArray()); + WixAssert.CompareLineByLine(new[] { "filF5_pLhBuF5b4N9XEo52g_hUM5Lo", "filvxdStJhRE_M5kbpLsTZJXbs34Sg" }, examples.Select(t => t[0].AsString()).ToArray()); + WixAssert.CompareLineByLine(new[] { "Bar", "Value" }, examples.Select(t => t[1].AsString()).ToArray()); } } } -- cgit v1.2.3-55-g6feb