// 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.Dependency { #if TODO_CONSIDER_DECOMPILER using System; using System.Collections.Generic; using System.Collections.ObjectModel; using WixToolset; using WixToolset.Data; using WixToolset.Extensibility; using WixToolset.Extensions.Serialize.Dependency; using Dependency = WixToolset.Extensions.Serialize.Dependency; using Wix = WixToolset.Data.Serialize; /// /// The decompiler for the WiX toolset dependency extension. /// public sealed class DependencyDecompiler : DecompilerExtension { private RegistryKeyValueCollection registryValues; private Dictionary keyCache; /// /// Creates a new instance of the class. /// public DependencyDecompiler() { this.registryValues = new RegistryKeyValueCollection(); this.keyCache = new Dictionary(); this.TableDefinitions = DependencyExtensionData.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 DependencyExtensionData.GetExtensionLibrary(tableDefinitions); } /// /// Decompiles an extension table. /// /// The table to decompile. public override void DecompileTable(Table table) { switch (table.Name) { case "WixDependencyProvider": this.DecompileWixDependencyProviderTable(table); break; case "WixDependency": this.DecompileWixDependencyTable(table); break; case "WixDependencyRef": this.DecompileWixDependencyRefTable(table); break; default: base.DecompileTable(table); break; } } /// /// Finalize decompilation by removing registry values that the compiler writes. /// /// The collection of all tables. public override void Finish(TableIndexedCollection tables) { // Remove generated registry rows. this.FinalizeRegistryTable(tables); // Remove extension properties. this.FinalizeProperties(); } /// /// Decompiles the WixDependencyProvider table. /// /// The table to decompile. private void DecompileWixDependencyProviderTable(Table table) { foreach (Row row in table.Rows) { Provides provides = new Provides(); provides.Id = (string)row[0]; provides.Key = (string)row[2]; if (null != row[3]) { provides.Version = (string)row[3]; } if (null != row[4]) { provides.DisplayName = (string)row[4]; } // Nothing to parse for attributes currently. Wix.Component component = (Wix.Component)this.Core.GetIndexedElement("Component", (string)row[1]); if (null != component) { component.AddChild(provides); } else { this.Core.OnMessage(WixWarnings.ExpectedForeignRow(row.SourceLineNumbers, table.Name, row.GetPrimaryKey(DecompilerConstants.PrimaryKeyDelimiter), "Component_", (string)row[1], "Component")); } // Index the provider to parent the RequiresRef elements. this.Core.IndexElement(row, provides); // Add the provider-specific registry keys to be removed during finalization. // Only remove specific keys that the compiler writes. string keyProvides = String.Concat(DependencyCommon.RegistryRoot, provides.Key); this.registryValues.Add(keyProvides, null); this.registryValues.Add(keyProvides, "Version"); this.registryValues.Add(keyProvides, "DisplayName"); this.registryValues.Add(keyProvides, "Attributes"); // Cache the provider key. this.keyCache[provides.Id] = provides.Key; } } /// /// Decompiles the WixDependency table. /// /// The table to decompile. private void DecompileWixDependencyTable(Table table) { foreach (Row row in table.Rows) { Requires requires = new Requires(); requires.Id = (string)row[0]; requires.ProviderKey = (string)row[1]; if (null != row[2]) { requires.Minimum = (string)row[2]; } if (null != row[3]) { requires.Maximum = (string)row[3]; } if (null != row[4]) { int attributes = (int)row[4]; if (0 != (attributes & DependencyCommon.RequiresAttributesMinVersionInclusive)) { requires.IncludeMinimum = Dependency.YesNoType.yes; } if (0 != (attributes & DependencyCommon.RequiresAttributesMaxVersionInclusive)) { requires.IncludeMaximum = Dependency.YesNoType.yes; } } this.Core.RootElement.AddChild(requires); // Cache the requires key. this.keyCache[requires.Id] = requires.ProviderKey; } } /// /// Decompiles the WixDependencyRef table. /// /// The table to decompile. private void DecompileWixDependencyRefTable(Table table) { foreach (Row row in table.Rows) { RequiresRef requiresRef = new RequiresRef(); requiresRef.Id = (string)row[1]; Provides provides = (Provides)this.Core.GetIndexedElement("WixDependencyProvider", (string)row[0]); if (null != provides) { provides.AddChild(requiresRef); } else { this.Core.OnMessage(WixWarnings.ExpectedForeignRow(row.SourceLineNumbers, table.Name, row.GetPrimaryKey(DecompilerConstants.PrimaryKeyDelimiter), "WixDependencyProvider_", (string)row[0], "WixDependencyProvider")); } // Get the cached keys for the provider and dependency IDs and generate registry rows. string providesKey = null; string requiresKey = null; if (null != provides && this.keyCache.ContainsKey(provides.Id)) { providesKey = this.keyCache[provides.Id]; } else { this.Core.OnMessage(DependencyWarnings.ProvidesKeyNotFound(row.SourceLineNumbers, provides.Id)); } if (this.keyCache.ContainsKey(requiresRef.Id)) { requiresKey = this.keyCache[requiresRef.Id]; } else { this.Core.OnMessage(DependencyWarnings.RequiresKeyNotFound(row.SourceLineNumbers, requiresRef.Id)); } if (!this.Core.EncounteredError) { // Add the dependency-specific registry keys to be removed during finalization. // Only remove specific keys that the compiler writes. string keyRequires = String.Format(@"{0}{1}\{2}\{3}", DependencyCommon.RegistryRoot, requiresKey, DependencyCommon.RegistryDependents, providesKey); this.registryValues.Add(keyRequires, "*"); this.registryValues.Add(keyRequires, "MinVersion"); this.registryValues.Add(keyRequires, "MaxVersion"); this.registryValues.Add(keyRequires, "Attributes"); } } } /// /// Removes rows from the Registry table that are generated by this extension. /// /// The collection of tables. private void FinalizeRegistryTable(TableIndexedCollection tables) { Table registryTable = tables["Registry"]; if (null != registryTable) { foreach (Row registryRow in registryTable.Rows) { // Check if the compiler writes this registry value; if so, it should be removed. if (this.registryValues.Contains(registryRow)) { Wix.ISchemaElement elem = this.Core.GetIndexedElement(registryRow); // If the registry row was found, remove it from its parent. if (null != elem && null != elem.ParentElement) { Wix.IParentElement elemParent = elem.ParentElement as Wix.IParentElement; if (null != elemParent) { elemParent.RemoveChild(elem); } } } } } } /// /// Removes properties defined by this extension. /// /// The collection of tables. private void FinalizeProperties() { string[] properties = new string[] { "DISABLEDEPENDENCYCHECK", "IGNOREDEPENDENCIES" }; foreach (string property in properties) { Wix.Property elem = this.Core.GetIndexedElement("Property", property) as Wix.Property; if (null != elem) { // If a value is defined, log a warning we're removing it. if (!String.IsNullOrEmpty(elem.Value)) { this.Core.OnMessage(DependencyWarnings.PropertyRemoved(elem.Id)); } // If the property row was found, remove it from its parent. if (null != elem.ParentElement) { Wix.IParentElement elemParent = elem.ParentElement as Wix.IParentElement; if (null != elemParent) { elemParent.RemoveChild(elem); } } } } } /// /// Provides an O(1) lookup for registry key and value name pairs for use in the decompiler. /// private sealed class RegistryKeyValueCollection : KeyedCollection> { /// /// Adds the registry key and value name pair to the collection if it doesn't already exist. /// /// The registry key to add. /// The registry value name to add. internal void Add(string key, string name) { KeyValuePair pair = new KeyValuePair(key, name); if (!this.Contains(pair)) { this.Add(pair); } } /// /// Returns whether the collection contains the registry key and value name pair from the . /// /// The registry to search for. /// True if the collection contains the registry key and value name pair from the ; otherwise, false. internal bool Contains(Row row) { if (null == row) { return false; } KeyValuePair pair = new KeyValuePair((string)row[2], (string)row[3]); return this.Contains(pair); } /// /// Return the hash code of the key and value pair concatenated with a colon as a delimiter. /// /// The registry key and value name pair. /// protected override int GetKeyForItem(KeyValuePair pair) { return String.Concat(pair.Key, ":", pair.Value).GetHashCode(); } } } #endif }