From 5fd1b7ff82f17d55c8357fe76898a1bdc5953476 Mon Sep 17 00:00:00 2001 From: Rob Mensching Date: Fri, 26 Feb 2021 11:24:10 -0800 Subject: Absorb Dependency.wixext into Core Partly resolves wixtoolset/issues#5949 --- .../Bind/BindDatabaseCommand.cs | 12 + .../Bind/ProcessDependencyReferencesCommand.cs | 111 + src/WixToolset.Core/Compiler.cs | 13 + src/WixToolset.Core/CompilerErrors.cs | 13 + src/WixToolset.Core/CompilerWarnings.cs | 53 + src/WixToolset.Core/Compiler_2.cs | 4972 ------------------- src/WixToolset.Core/Compiler_Bundle.cs | 9 + src/WixToolset.Core/Compiler_Dependency.cs | 385 ++ src/WixToolset.Core/Compiler_Module.cs | 3 + src/WixToolset.Core/Compiler_Package.cs | 4975 ++++++++++++++++++++ .../DependencyExtensionFixture.cs | 30 + .../TestData/UsingProvides/Package.en-us.wxl | 11 + .../TestData/UsingProvides/Package.wxs | 14 + .../TestData/UsingProvides/PackageComponents.wxs | 10 + .../TestData/UsingProvides/example.txt | 1 + 15 files changed, 5640 insertions(+), 4972 deletions(-) create mode 100644 src/WixToolset.Core.WindowsInstaller/Bind/ProcessDependencyReferencesCommand.cs create mode 100644 src/WixToolset.Core/CompilerWarnings.cs delete mode 100644 src/WixToolset.Core/Compiler_2.cs create mode 100644 src/WixToolset.Core/Compiler_Dependency.cs create mode 100644 src/WixToolset.Core/Compiler_Package.cs create mode 100644 src/test/WixToolsetTest.CoreIntegration/DependencyExtensionFixture.cs create mode 100644 src/test/WixToolsetTest.CoreIntegration/TestData/UsingProvides/Package.en-us.wxl create mode 100644 src/test/WixToolsetTest.CoreIntegration/TestData/UsingProvides/Package.wxs create mode 100644 src/test/WixToolsetTest.CoreIntegration/TestData/UsingProvides/PackageComponents.wxs create mode 100644 src/test/WixToolsetTest.CoreIntegration/TestData/UsingProvides/example.txt diff --git a/src/WixToolset.Core.WindowsInstaller/Bind/BindDatabaseCommand.cs b/src/WixToolset.Core.WindowsInstaller/Bind/BindDatabaseCommand.cs index 25a093fd..a3f2da94 100644 --- a/src/WixToolset.Core.WindowsInstaller/Bind/BindDatabaseCommand.cs +++ b/src/WixToolset.Core.WindowsInstaller/Bind/BindDatabaseCommand.cs @@ -325,6 +325,18 @@ namespace WixToolset.Core.WindowsInstaller.Bind command.Execute(); } + // Process dependency references. + if (SectionType.Product == section.Type || SectionType.Module == section.Type) + { + var dependencyRefs = section.Symbols.OfType().ToList(); + + if (dependencyRefs.Any()) + { + var command = new ProcessDependencyReferencesCommand(this.WindowsInstallerBackendHelper, section, dependencyRefs); + command.Execute(); + } + } + // If there are any backend extensions, give them the opportunity to process // the section now that the fields have all be resolved. // diff --git a/src/WixToolset.Core.WindowsInstaller/Bind/ProcessDependencyReferencesCommand.cs b/src/WixToolset.Core.WindowsInstaller/Bind/ProcessDependencyReferencesCommand.cs new file mode 100644 index 00000000..899d06e1 --- /dev/null +++ b/src/WixToolset.Core.WindowsInstaller/Bind/ProcessDependencyReferencesCommand.cs @@ -0,0 +1,111 @@ +// 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.Bind +{ + using System; + using System.Collections.Generic; + using System.Linq; + using WixToolset.Data; + using WixToolset.Data.Symbols; + using WixToolset.Extensibility.Services; + + internal class ProcessDependencyReferencesCommand + { + // The root registry key for the dependency extension. We write to Software\Classes explicitly + // based on the current security context instead of HKCR. See + // http://msdn.microsoft.com/en-us/library/ms724475(VS.85).aspx for more information. + private const string DependencyRegistryRoot = @"Software\Classes\Installer\Dependencies\"; + private const string RegistryDependents = "Dependents"; + + public ProcessDependencyReferencesCommand(IWindowsInstallerBackendHelper backendHelper, IntermediateSection section, IEnumerable dependencyRefSymbols) + { + this.Section = section; + this.DependencyRefSymbols = dependencyRefSymbols; + } + + private IntermediateSection Section { get; } + + private IEnumerable DependencyRefSymbols { get; } + + public void Execute() + { + var wixDependencyRows = this.Section.Symbols.OfType().ToDictionary(d => d.Id.Id); + var wixDependencyProviderRows = this.Section.Symbols.OfType().ToDictionary(d => d.Id.Id); + + // For each relationship, get the provides and requires rows to generate registry values. + foreach (var wixDependencyRefRow in this.DependencyRefSymbols) + { + var providesId = wixDependencyRefRow.WixDependencyProviderRef; + var requiresId = wixDependencyRefRow.WixDependencyRef; + + // If we do not find both symbols, skip the registry key generation. + if (!wixDependencyRows.TryGetValue(requiresId, out var wixDependencyRow)) + { + continue; + } + + if (!wixDependencyProviderRows.TryGetValue(providesId, out var wixDependencyProviderRow)) + { + continue; + } + + // Format the root registry key using the required provider key and the current provider key. + var requiresKey = wixDependencyRow.Id.Id; + var providesKey = wixDependencyRow.ProviderKey; + var keyRequires = String.Format(@"{0}{1}\{2}\{3}", DependencyRegistryRoot, requiresKey, RegistryDependents, providesKey); + + // Get the component ID from the provider. + var componentId = wixDependencyProviderRow.ComponentRef; + + var id = Common.GenerateIdentifier("reg", providesId, requiresId, "(Default)"); + this.Section.AddSymbol(new RegistrySymbol(wixDependencyRefRow.SourceLineNumbers, new Identifier(AccessModifier.Private, id)) + { + ComponentRef = componentId, + Root = RegistryRootType.MachineUser, + Key = keyRequires, + Name = "*", + }); + + if (!String.IsNullOrEmpty(wixDependencyRow.MinVersion)) + { + id = Common.GenerateIdentifier("reg", providesId, requiresId, "MinVersion"); + this.Section.AddSymbol(new RegistrySymbol(wixDependencyRefRow.SourceLineNumbers, new Identifier(AccessModifier.Private, id)) + { + ComponentRef = componentId, + Root = RegistryRootType.MachineUser, + Key = keyRequires, + Name = "MinVersion", + Value = wixDependencyRow.MinVersion + }); + } + + string maxVersion = (string)wixDependencyRow[3]; + if (!String.IsNullOrEmpty(wixDependencyRow.MaxVersion)) + { + id = Common.GenerateIdentifier("reg", providesId, requiresId, "MaxVersion"); + this.Section.AddSymbol(new RegistrySymbol(wixDependencyRefRow.SourceLineNumbers, new Identifier(AccessModifier.Private, id)) + { + ComponentRef = componentId, + Root = RegistryRootType.MachineUser, + Key = keyRequires, + Name = "MaxVersion", + Value = wixDependencyRow.MaxVersion + }); + } + + if (wixDependencyRow.Attributes != WixDependencySymbolAttributes.None) + { + id = Common.GenerateIdentifier("reg", providesId, requiresId, "Attributes"); + this.Section.AddSymbol(new RegistrySymbol(wixDependencyRefRow.SourceLineNumbers, new Identifier(AccessModifier.Private, id)) + { + ComponentRef = componentId, + Root = RegistryRootType.MachineUser, + Key = keyRequires, + Name = "Attributes", + Value = String.Concat("#", (int)wixDependencyRow.Attributes) + }); + } + } + } + } +} diff --git a/src/WixToolset.Core/Compiler.cs b/src/WixToolset.Core/Compiler.cs index 85261cce..ac99a8a1 100644 --- a/src/WixToolset.Core/Compiler.cs +++ b/src/WixToolset.Core/Compiler.cs @@ -2366,6 +2366,16 @@ namespace WixToolset.Core var foundExtension = false; this.ParseProgIdElement(child, id.Id, YesNoType.NotSet, null, null, null, ref foundExtension, YesNoType.NotSet); break; + case "Provides": + if (win64) + { + this.Messaging.Write(CompilerWarnings.Win64Component(sourceLineNumbers, id.Id)); + } + + keyPathSet = this.ParseProvidesElement(child, null, id.Id, out keyPossible); + keyBit = ComponentKeyPathType.Registry; + break; + case "RegistryKey": keyPathSet = this.ParseRegistryKeyElement(child, id.Id, null, null, win64, out keyPossible); keyBit = ComponentKeyPathType.Registry; @@ -6290,6 +6300,9 @@ namespace WixToolset.Core case "RelatedBundle": this.ParseRelatedBundleElement(child); break; + case "Requires": + this.ParseRequiresElement(child, null, false); + break; case "SetDirectory": this.ParseSetDirectoryElement(child); break; diff --git a/src/WixToolset.Core/CompilerErrors.cs b/src/WixToolset.Core/CompilerErrors.cs index da64c376..9b3d85b9 100644 --- a/src/WixToolset.Core/CompilerErrors.cs +++ b/src/WixToolset.Core/CompilerErrors.cs @@ -6,6 +6,16 @@ namespace WixToolset.Core internal static class CompilerErrors { + public static Message IllegalCharactersInProvider(SourceLineNumber sourceLineNumbers, string attributeName, char illegalChar, string illegalChars) + { + return Message(sourceLineNumbers, Ids.IllegalCharactersInProvider, "The provider key authored into the {0} attribute contains an illegal character, '{1}'. Please author the provider key without any of the following characters: {2}", attributeName, illegalChar, illegalChars); + } + + public static Message ReservedValue(SourceLineNumber sourceLineNumbers, string elementName, string attributeName, string attributeValue) + { + return Message(sourceLineNumbers, Ids.ReservedValue, "The {0}/@{1} attribute value '{2}' is reserved and cannot be used here. Please choose a different value.", elementName, attributeName, attributeValue); + } + public static Message IllegalName(SourceLineNumber sourceLineNumbers, string parentElement, string name) { return Message(sourceLineNumbers, Ids.IllegalName, "The Tag/@Name attribute value, '{1}', contains invalid filename identifiers. The Tag/@Name may have defaulted from the {0}/@Name attrbute. If so, use the Tag/@Name attribute to provide a valid filename. Any character except for the follow may be used: \\ ? | > < : / * \".", parentElement, name); @@ -23,6 +33,9 @@ namespace WixToolset.Core public enum Ids { + IllegalCharactersInProvider = 5400, + ReservedValue = 5401, + IllegalName = 6601, ExampleRegid = 6602, } diff --git a/src/WixToolset.Core/CompilerWarnings.cs b/src/WixToolset.Core/CompilerWarnings.cs new file mode 100644 index 00000000..3b9666dd --- /dev/null +++ b/src/WixToolset.Core/CompilerWarnings.cs @@ -0,0 +1,53 @@ +// 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 +{ + using WixToolset.Data; + + internal static class CompilerWarnings + { + public static Message DiscouragedVersionAttribute(SourceLineNumber sourceLineNumbers) + { + return Message(sourceLineNumbers, Ids.DiscouragedVersionAttribute, "The Provides/@Version attribute should not be specified in an MSI package. The ProductVersion will be used by default."); + } + + public static Message DiscouragedVersionAttribute(SourceLineNumber sourceLineNumbers, string id) + { + return Message(sourceLineNumbers, Ids.DiscouragedVersionAttribute, "The Provides/@Version attribute should not be specified for MSI package {0}. The ProductVersion will be used by default.", id); + } + + public static Message PropertyRemoved(string name) + { + return Message(null, Ids.PropertyRemoved, "The property {0} was authored in the package with a value and will be removed. The property should not be authored.", name); + } + + public static Message ProvidesKeyNotFound(SourceLineNumber sourceLineNumbers, string id) + { + return Message(sourceLineNumbers, Ids.ProvidesKeyNotFound, "The provider key with identifier {0} was not found in the WixDependencyProvider table. Related registry rows will not be removed from authoring.", id); + } + + public static Message RequiresKeyNotFound(SourceLineNumber sourceLineNumbers, string id) + { + return Message(sourceLineNumbers, Ids.RequiresKeyNotFound, "The dependency key with identifier {0} was not found in the WixDependency table. Related registry rows will not be removed from authoring.", id); + } + + public static Message Win64Component(SourceLineNumber sourceLineNumbers, string componentId) + { + return Message(sourceLineNumbers, Ids.Win64Component, "The Provides element should not be authored in the 64-bit component with identifier {0}. The dependency feature may not work if installing this package on 64-bit Windows operating systems prior to Windows 7 and Windows Server 2008 R2. Set the Component/@Bitness attribute to \"always32\" to ensure the dependency feature works correctly on legacy operating systems.", componentId); + } + + private static Message Message(SourceLineNumber sourceLineNumber, Ids id, string format, params object[] args) + { + return new Message(sourceLineNumber, MessageLevel.Warning, (int)id, format, args); + } + + public enum Ids + { + ProvidesKeyNotFound = 5431, + RequiresKeyNotFound = 5432, + PropertyRemoved = 5433, + DiscouragedVersionAttribute = 5434, + Win64Component = 5435, + } + } +} diff --git a/src/WixToolset.Core/Compiler_2.cs b/src/WixToolset.Core/Compiler_2.cs deleted file mode 100644 index 295392c8..00000000 --- a/src/WixToolset.Core/Compiler_2.cs +++ /dev/null @@ -1,4972 +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 -{ - using System; - using System.Collections; - using System.Collections.Generic; - using System.Diagnostics.CodeAnalysis; - using System.Globalization; - using System.IO; - using System.Xml.Linq; - using WixToolset.Data; - using WixToolset.Data.Symbols; - using WixToolset.Data.WindowsInstaller; - using WixToolset.Extensibility; - - /// - /// Compiler of the WiX toolset. - /// - internal partial class Compiler : ICompiler - { - /// - /// Parses a product element. - /// - /// Element to parse. - private void ParsePackageElement(XElement node) - { - var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node); - var compressed = YesNoDefaultType.Default; - var sourceBits = 0; - var codepage = 65001; - var productCode = "*"; - var isPerMachine = true; - string installScope = null; - string upgradeCode = null; - string manufacturer = null; - string version = null; - string symbols = null; - var isCodepageSet = false; - var isPackageNameSet = false; - var isKeywordsSet = false; - var isPackageAuthorSet = false; - - this.GetDefaultPlatformAndInstallerVersion(out var platform, out var msiVersion); - - this.activeName = null; - this.activeLanguage = null; - - foreach (var attrib in node.Attributes()) - { - if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace) - { - switch (attrib.Name.LocalName) - { - case "Codepage": - codepage = this.Core.GetAttributeCodePageValue(sourceLineNumbers, attrib); - break; - case "Compressed": - compressed = this.Core.GetAttributeYesNoDefaultValue(sourceLineNumbers, attrib); - break; - case "InstallerVersion": - msiVersion = this.Core.GetAttributeIntegerValue(sourceLineNumbers, attrib, 0, Int32.MaxValue); - break; - case "Language": - this.activeLanguage = this.Core.GetAttributeLocalizableIntegerValue(sourceLineNumbers, attrib, 0, Int16.MaxValue); - break; - case "Manufacturer": - manufacturer = this.Core.GetAttributeValue(sourceLineNumbers, attrib, EmptyRule.MustHaveNonWhitespaceCharacters); - if ("PUT-COMPANY-NAME-HERE" == manufacturer) - { - this.Core.Write(WarningMessages.PlaceholderValue(sourceLineNumbers, node.Name.LocalName, attrib.Name.LocalName, manufacturer)); - } - break; - case "Name": - this.activeName = this.Core.GetAttributeValue(sourceLineNumbers, attrib, EmptyRule.MustHaveNonWhitespaceCharacters); - if ("PUT-PRODUCT-NAME-HERE" == this.activeName) - { - this.Core.Write(WarningMessages.PlaceholderValue(sourceLineNumbers, node.Name.LocalName, attrib.Name.LocalName, this.activeName)); - } - break; - case "ProductCode": - productCode = this.Core.GetAttributeGuidValue(sourceLineNumbers, attrib, true); - break; - case "Scope": - installScope = this.Core.GetAttributeValue(sourceLineNumbers, attrib); - switch (installScope) - { - case "perMachine": - // handled below after we create the section. - break; - case "perUser": - isPerMachine = false; - sourceBits |= 8; - break; - default: - this.Core.Write(ErrorMessages.IllegalAttributeValue(sourceLineNumbers, node.Name.LocalName, attrib.Name.LocalName, installScope, "perMachine", "perUser")); - break; - } - break; - case "ShortNames": - if (YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib)) - { - sourceBits |= 1; - } - break; - case "UpgradeCode": - upgradeCode = this.Core.GetAttributeGuidValue(sourceLineNumbers, attrib, false); - break; - case "Version": // if the attribute is valid version, use the attribute value as is (so "1.0000.01.01" would *not* get translated to "1.0.1.1"). - var verifiedVersion = this.Core.GetAttributeVersionValue(sourceLineNumbers, attrib); - if (!String.IsNullOrEmpty(verifiedVersion)) - { - version = attrib.Value; - } - break; - default: - this.Core.UnexpectedAttribute(node, attrib); - break; - } - } - else - { - this.Core.ParseExtensionAttribute(node, attrib); - } - } - - if (null == productCode) - { - this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Id")); - } - - if (null == this.activeLanguage) - { - this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Language")); - } - - if (null == manufacturer) - { - this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Manufacturer")); - } - - if (null == this.activeName) - { - this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Name")); - } - - if (null == upgradeCode) - { - this.Core.Write(WarningMessages.MissingUpgradeCode(sourceLineNumbers)); - } - - if (null == version) - { - this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Version")); - } - else if (!CompilerCore.IsValidProductVersion(version)) - { - this.Core.Write(ErrorMessages.InvalidProductVersion(sourceLineNumbers, version)); - } - - if (compressed != YesNoDefaultType.No) - { - sourceBits |= 2; - } - - if (this.Core.EncounteredError) - { - return; - } - - try - { - this.compilingProduct = true; - this.Core.CreateActiveSection(productCode, SectionType.Product, codepage, this.Context.CompilationId); - - this.AddProperty(sourceLineNumbers, new Identifier(AccessModifier.Public, "Manufacturer"), manufacturer, false, false, false, true); - this.AddProperty(sourceLineNumbers, new Identifier(AccessModifier.Public, "ProductCode"), productCode, false, false, false, true); - this.AddProperty(sourceLineNumbers, new Identifier(AccessModifier.Public, "ProductLanguage"), this.activeLanguage, false, false, false, true); - this.AddProperty(sourceLineNumbers, new Identifier(AccessModifier.Public, "ProductName"), this.activeName, false, false, false, true); - this.AddProperty(sourceLineNumbers, new Identifier(AccessModifier.Public, "ProductVersion"), version, false, false, false, true); - if (null != upgradeCode) - { - this.AddProperty(sourceLineNumbers, new Identifier(AccessModifier.Public, "UpgradeCode"), upgradeCode, false, false, false, true); - } - - if (isPerMachine) - { - this.AddProperty(sourceLineNumbers, new Identifier(AccessModifier.Public, "ALLUSERS"), "1", false, false, false, false); - } - - this.ValidateAndAddCommonSummaryInformationSymbols(sourceLineNumbers, msiVersion, platform); - - this.Core.AddSymbol(new SummaryInformationSymbol(sourceLineNumbers) - { - PropertyId = SummaryInformationType.WordCount, - Value = sourceBits.ToString(CultureInfo.InvariantCulture) - }); - - var contextValues = new Dictionary - { - ["ProductLanguage"] = this.activeLanguage, - ["ProductVersion"] = version, - ["UpgradeCode"] = upgradeCode - }; - - var featureDisplay = 0; - foreach (var child in node.Elements()) - { - if (CompilerCore.WixNamespace == child.Name.Namespace) - { - switch (child.Name.LocalName) - { - case "_locDefinition": - break; - case "AdminExecuteSequence": - this.ParseSequenceElement(child, SequenceTable.AdminExecuteSequence); - break; - case "AdminUISequence": - this.ParseSequenceElement(child, SequenceTable.AdminUISequence); - break; - case "AdvertiseExecuteSequence": - this.ParseSequenceElement(child, SequenceTable.AdvertiseExecuteSequence); - break; - case "InstallExecuteSequence": - this.ParseSequenceElement(child, SequenceTable.InstallExecuteSequence); - break; - case "InstallUISequence": - this.ParseSequenceElement(child, SequenceTable.InstallUISequence); - break; - case "AppId": - this.ParseAppIdElement(child, null, YesNoType.Yes, null, null, null); - break; - case "Binary": - this.ParseBinaryElement(child); - break; - case "ComplianceCheck": - this.ParseComplianceCheckElement(child); - break; - case "Component": - this.ParseComponentElement(child, ComplexReferenceParentType.Unknown, null, null, CompilerConstants.IntegerNotSet, null, null); - break; - case "ComponentGroup": - this.ParseComponentGroupElement(child, ComplexReferenceParentType.Unknown, null); - break; - case "CustomAction": - this.ParseCustomActionElement(child); - break; - case "CustomActionRef": - this.ParseSimpleRefElement(child, SymbolDefinitions.CustomAction); - break; - case "CustomTable": - this.ParseCustomTableElement(child); - break; - case "CustomTableRef": - this.ParseCustomTableRefElement(child); - break; - case "Directory": - this.ParseDirectoryElement(child, null, CompilerConstants.IntegerNotSet, String.Empty); - break; - case "DirectoryRef": - this.ParseDirectoryRefElement(child); - break; - case "EmbeddedChainer": - this.ParseEmbeddedChainerElement(child); - break; - case "EmbeddedChainerRef": - this.ParseSimpleRefElement(child, SymbolDefinitions.MsiEmbeddedChainer); - break; - case "EnsureTable": - this.ParseEnsureTableElement(child); - break; - case "Feature": - this.ParseFeatureElement(child, ComplexReferenceParentType.Product, productCode, ref featureDisplay); - break; - case "FeatureRef": - this.ParseFeatureRefElement(child, ComplexReferenceParentType.Product, productCode); - break; - case "FeatureGroupRef": - this.ParseFeatureGroupRefElement(child, ComplexReferenceParentType.Product, productCode); - break; - case "Icon": - this.ParseIconElement(child); - break; - case "InstanceTransforms": - this.ParseInstanceTransformsElement(child); - break; - case "Launch": - this.ParseLaunchElement(child); - break; - case "MajorUpgrade": - this.ParseMajorUpgradeElement(child, contextValues); - break; - case "Media": - this.ParseMediaElement(child, null); - break; - case "MediaTemplate": - this.ParseMediaTemplateElement(child, null); - break; - case "PackageCertificates": - case "PatchCertificates": - this.ParseCertificatesElement(child); - break; - case "Property": - this.ParsePropertyElement(child); - break; - case "PropertyRef": - this.ParseSimpleRefElement(child, SymbolDefinitions.Property); - break; - case "SetDirectory": - this.ParseSetDirectoryElement(child); - break; - case "SetProperty": - this.ParseSetPropertyElement(child); - break; - case "SFPCatalog": - string parentName = null; - this.ParseSFPCatalogElement(child, ref parentName); - break; - case "SoftwareTag": - this.ParsePackageTagElement(child); - break; - case "SummaryInformation": - this.ParseSummaryInformationElement(child, ref isCodepageSet, ref isPackageNameSet, ref isKeywordsSet, ref isPackageAuthorSet); - break; - case "SymbolPath": - if (null != symbols) - { - symbols += ";" + this.ParseSymbolPathElement(child); - } - else - { - symbols = this.ParseSymbolPathElement(child); - } - break; - case "UI": - this.ParseUIElement(child); - break; - case "UIRef": - this.ParseSimpleRefElement(child, SymbolDefinitions.WixUI); - break; - case "Upgrade": - this.ParseUpgradeElement(child); - break; - case "WixVariable": - this.ParseWixVariableElement(child); - break; - default: - this.Core.UnexpectedElement(node, child); - break; - } - } - else - { - this.Core.ParseExtensionElement(node, child); - } - } - - if (!this.Core.EncounteredError) - { - if (!isCodepageSet) - { - this.Core.AddSymbol(new SummaryInformationSymbol(sourceLineNumbers) - { - PropertyId = SummaryInformationType.Codepage, - Value = "1252" - }); - } - - if (!isPackageNameSet) - { - this.Core.AddSymbol(new SummaryInformationSymbol(sourceLineNumbers) - { - PropertyId = SummaryInformationType.Subject, - Value = this.activeName - }); - } - - if (!isPackageAuthorSet) - { - this.Core.AddSymbol(new SummaryInformationSymbol(sourceLineNumbers) - { - PropertyId = SummaryInformationType.Author, - Value = manufacturer - }); - } - - if (!isKeywordsSet) - { - this.Core.AddSymbol(new SummaryInformationSymbol(sourceLineNumbers) - { - PropertyId = SummaryInformationType.Keywords, - Value = "Installer" - }); - } - - if (null != symbols) - { - this.Core.AddSymbol(new WixDeltaPatchSymbolPathsSymbol(sourceLineNumbers) - { - SymbolId = productCode, - SymbolType = SymbolPathType.Product, - SymbolPaths = symbols, - }); - } - } - } - finally - { - this.compilingProduct = false; - } - } - - private void GetDefaultPlatformAndInstallerVersion(out string platform, out int msiVersion) - { - // Let's default to a modern version of MSI. Users can override, - // of course, subject to platform-specific limitations. - msiVersion = 500; - - switch (this.CurrentPlatform) - { - case Platform.X86: - platform = "Intel"; - break; - case Platform.X64: - platform = "x64"; - break; - case Platform.ARM64: - platform = "Arm64"; - break; - default: - throw new ArgumentException("Unknown platform enumeration '{0}' encountered.", this.CurrentPlatform.ToString()); - } - } - - private void ValidateAndAddCommonSummaryInformationSymbols(SourceLineNumber sourceLineNumbers, int msiVersion, string platform) - { - if (String.Equals(platform, "X64", StringComparison.OrdinalIgnoreCase) && 200 > msiVersion) - { - msiVersion = 200; - this.Core.Write(WarningMessages.RequiresMsi200for64bitPackage(sourceLineNumbers)); - } - - if (String.Equals(platform, "Arm64", StringComparison.OrdinalIgnoreCase) && 500 > msiVersion) - { - msiVersion = 500; - this.Core.Write(WarningMessages.RequiresMsi500forArmPackage(sourceLineNumbers)); - } - - this.Core.AddSymbol(new SummaryInformationSymbol(sourceLineNumbers) - { - PropertyId = SummaryInformationType.Comments, - Value = String.Format(CultureInfo.InvariantCulture, "This installer database contains the logic and data required to install {0}.", this.activeName) - }); - - this.Core.AddSymbol(new SummaryInformationSymbol(sourceLineNumbers) - { - PropertyId = SummaryInformationType.Title, - Value = "Installation Database" - }); - - this.Core.AddSymbol(new SummaryInformationSymbol(sourceLineNumbers) - { - PropertyId = SummaryInformationType.PlatformAndLanguage, - Value = String.Format(CultureInfo.InvariantCulture, "{0};{1}", platform, this.activeLanguage) - }); - - this.Core.AddSymbol(new SummaryInformationSymbol(sourceLineNumbers) - { - PropertyId = SummaryInformationType.WindowsInstallerVersion, - Value = msiVersion.ToString(CultureInfo.InvariantCulture) - }); - - this.Core.AddSymbol(new SummaryInformationSymbol(sourceLineNumbers) - { - PropertyId = SummaryInformationType.Security, - Value = "2" - }); - - } - - /// - /// Parses an odbc driver or translator element. - /// - /// Element to parse. - /// Identifier of parent component. - /// Default identifer for driver/translator file. - /// Symbol type we're processing for. - private void ParseODBCDriverOrTranslator(XElement node, string componentId, string fileId, SymbolDefinitionType symbolDefinitionType) - { - var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node); - Identifier id = null; - var driver = fileId; - string name = null; - var setup = fileId; - - foreach (var attrib in node.Attributes()) - { - if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace) - { - switch (attrib.Name.LocalName) - { - case "Id": - id = this.Core.GetAttributeIdentifier(sourceLineNumbers, attrib); - break; - case "File": - driver = this.Core.GetAttributeIdentifierValue(sourceLineNumbers, attrib); - this.Core.CreateSimpleReference(sourceLineNumbers, SymbolDefinitions.File, driver); - break; - case "Name": - name = this.Core.GetAttributeValue(sourceLineNumbers, attrib); - break; - case "SetupFile": - setup = this.Core.GetAttributeIdentifierValue(sourceLineNumbers, attrib); - this.Core.CreateSimpleReference(sourceLineNumbers, SymbolDefinitions.File, setup); - break; - default: - this.Core.UnexpectedAttribute(node, attrib); - break; - } - } - else - { - this.Core.ParseExtensionAttribute(node, attrib); - } - } - - if (null == name) - { - this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Name")); - } - - if (null == id) - { - id = this.Core.CreateIdentifier("odb", name, fileId, setup); - } - - // drivers have a few possible children - if (SymbolDefinitionType.ODBCDriver == symbolDefinitionType) - { - // process any data sources for the driver - foreach (var child in node.Elements()) - { - if (CompilerCore.WixNamespace == child.Name.Namespace) - { - switch (child.Name.LocalName) - { - case "ODBCDataSource": - string ignoredKeyPath = null; - this.ParseODBCDataSource(child, componentId, name, out ignoredKeyPath); - break; - case "Property": - this.ParseODBCProperty(child, id.Id, SymbolDefinitionType.ODBCAttribute); - break; - default: - this.Core.UnexpectedElement(node, child); - break; - } - } - else - { - this.Core.ParseExtensionElement(node, child); - } - } - } - else - { - this.Core.ParseForExtensionElements(node); - } - - if (!this.Core.EncounteredError) - { - switch (symbolDefinitionType) - { - case SymbolDefinitionType.ODBCDriver: - this.Core.AddSymbol(new ODBCDriverSymbol(sourceLineNumbers, id) - { - ComponentRef = componentId, - Description = name, - FileRef = driver, - SetupFileRef = setup, - }); - break; - case SymbolDefinitionType.ODBCTranslator: - this.Core.AddSymbol(new ODBCTranslatorSymbol(sourceLineNumbers, id) - { - ComponentRef = componentId, - Description = name, - FileRef = driver, - SetupFileRef = setup, - }); - break; - default: - throw new ArgumentOutOfRangeException(nameof(symbolDefinitionType)); - } - } - } - - /// - /// Parses a Property element underneath an ODBC driver or translator. - /// - /// Element to parse. - /// Identifier of parent driver or translator. - /// Name of the table to create property in. - private void ParseODBCProperty(XElement node, string parentId, SymbolDefinitionType symbolDefinitionType) - { - var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node); - string id = null; - string propertyValue = null; - - foreach (var attrib in node.Attributes()) - { - if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace) - { - switch (attrib.Name.LocalName) - { - case "Id": - id = this.Core.GetAttributeValue(sourceLineNumbers, attrib); - break; - case "Value": - propertyValue = this.Core.GetAttributeValue(sourceLineNumbers, attrib); - break; - default: - this.Core.UnexpectedAttribute(node, attrib); - break; - } - } - else - { - this.Core.ParseExtensionAttribute(node, attrib); - } - } - - if (null == id) - { - this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Id")); - } - - this.Core.ParseForExtensionElements(node); - - if (!this.Core.EncounteredError) - { - var identifier = new Identifier(AccessModifier.Private, parentId, id); - switch (symbolDefinitionType) - { - case SymbolDefinitionType.ODBCAttribute: - this.Core.AddSymbol(new ODBCAttributeSymbol(sourceLineNumbers, identifier) - { - DriverRef = parentId, - Attribute = id, - Value = propertyValue, - }); - break; - case SymbolDefinitionType.ODBCSourceAttribute: - this.Core.AddSymbol(new ODBCSourceAttributeSymbol(sourceLineNumbers, identifier) - { - DataSourceRef = parentId, - Attribute = id, - Value = propertyValue, - }); - break; - default: - throw new ArgumentOutOfRangeException(nameof(symbolDefinitionType)); - } - } - } - - /// - /// Parse an odbc data source element. - /// - /// Element to parse. - /// Identifier of parent component. - /// Default name of driver. - /// Identifier of this element in case it is a keypath. - /// Yes if this element was marked as the parent component's key path, No if explicitly marked as not being a key path, or NotSet otherwise. - private YesNoType ParseODBCDataSource(XElement node, string componentId, string driverName, out string possibleKeyPath) - { - var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node); - Identifier id = null; - var keyPath = YesNoType.NotSet; - string name = null; - var registration = CompilerConstants.IntegerNotSet; - - foreach (var attrib in node.Attributes()) - { - if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace) - { - switch (attrib.Name.LocalName) - { - case "Id": - id = this.Core.GetAttributeIdentifier(sourceLineNumbers, attrib); - break; - case "DriverName": - driverName = this.Core.GetAttributeValue(sourceLineNumbers, attrib); - break; - case "KeyPath": - keyPath = this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib); - break; - case "Name": - name = this.Core.GetAttributeValue(sourceLineNumbers, attrib); - break; - case "Registration": - var registrationValue = this.Core.GetAttributeValue(sourceLineNumbers, attrib); - switch (registrationValue) - { - case "machine": - registration = 0; - break; - case "user": - registration = 1; - break; - case "": - break; - default: - this.Core.Write(ErrorMessages.IllegalAttributeValue(sourceLineNumbers, node.Name.LocalName, "Registration", registrationValue, "machine", "user")); - break; - } - break; - default: - this.Core.UnexpectedAttribute(node, attrib); - break; - } - } - else - { - this.Core.ParseExtensionAttribute(node, attrib); - } - } - - if (CompilerConstants.IntegerNotSet == registration) - { - this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Registration")); - registration = CompilerConstants.IllegalInteger; - } - - if (null == id) - { - id = this.Core.CreateIdentifier("odc", name, driverName, registration.ToString()); - } - - foreach (var child in node.Elements()) - { - if (CompilerCore.WixNamespace == child.Name.Namespace) - { - switch (child.Name.LocalName) - { - case "Property": - this.ParseODBCProperty(child, id.Id, SymbolDefinitionType.ODBCSourceAttribute); - break; - default: - this.Core.UnexpectedElement(node, child); - break; - } - } - else - { - this.Core.ParseExtensionElement(node, child); - } - } - - if (!this.Core.EncounteredError) - { - this.Core.AddSymbol(new ODBCDataSourceSymbol(sourceLineNumbers, id) - { - ComponentRef = componentId, - Description = name, - DriverDescription = driverName, - Registration = registration - }); - } - - possibleKeyPath = id.Id; - return keyPath; - } - - /// - /// Parses a package element. - /// - /// Element to parse. - /// - /// - /// - /// - private void ParseSummaryInformationElement(XElement node, ref bool isCodepageSet, ref bool isPackageNameSet, ref bool isKeywordsSet, ref bool isPackageAuthorSet) - { - var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node); - string codepage = null; - string packageName = null; - string keywords = null; - string packageAuthor = null; - - foreach (var attrib in node.Attributes()) - { - if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace) - { - switch (attrib.Name.LocalName) - { - case "Codepage": - codepage = this.Core.GetAttributeLocalizableCodePageValue(sourceLineNumbers, attrib, true); - break; - case "Description": - packageName = this.Core.GetAttributeValue(sourceLineNumbers, attrib); - break; - case "Keywords": - keywords = this.Core.GetAttributeValue(sourceLineNumbers, attrib); - break; - case "Manufacturer": - packageAuthor = this.Core.GetAttributeValue(sourceLineNumbers, attrib); - if ("PUT-COMPANY-NAME-HERE" == packageAuthor) - { - this.Core.Write(WarningMessages.PlaceholderValue(sourceLineNumbers, node.Name.LocalName, attrib.Name.LocalName, packageAuthor)); - } - break; - default: - this.Core.UnexpectedAttribute(node, attrib); - break; - } - } - else - { - this.Core.ParseExtensionAttribute(node, attrib); - } - } - - this.Core.ParseForExtensionElements(node); - - if (!this.Core.EncounteredError) - { - if (null != codepage) - { - isCodepageSet = true; - this.Core.AddSymbol(new SummaryInformationSymbol(sourceLineNumbers) - { - PropertyId = SummaryInformationType.Codepage, - Value = codepage - }); - } - - if (null != packageName) - { - isPackageNameSet = true; - this.Core.AddSymbol(new SummaryInformationSymbol(sourceLineNumbers) - { - PropertyId = SummaryInformationType.Subject, - Value = packageName - }); - } - - if (null != packageAuthor) - { - isPackageAuthorSet = true; - this.Core.AddSymbol(new SummaryInformationSymbol(sourceLineNumbers) - { - PropertyId = SummaryInformationType.Author, - Value = packageAuthor - }); - } - - if (null != keywords) - { - isKeywordsSet = true; - this.Core.AddSymbol(new SummaryInformationSymbol(sourceLineNumbers) - { - PropertyId = SummaryInformationType.Keywords, - Value = keywords - }); - } - } - } - - /// - /// Parses a patch information element. - /// - /// Element to parse. - private void ParsePatchInformationElement(XElement node) - { - var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node); - var codepage = "1252"; - string comments = null; - var keywords = "Installer,Patching,PCP,Database"; - var msiVersion = 1; // Should always be 1 for patches - string packageAuthor = null; - var packageName = this.activeName; - var security = YesNoDefaultType.Default; - - foreach (var attrib in node.Attributes()) - { - if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace) - { - switch (attrib.Name.LocalName) - { - case "AdminImage": - this.Core.Write(WarningMessages.DeprecatedAttribute(sourceLineNumbers, node.Name.LocalName, attrib.Name.LocalName)); - break; - case "Comments": - comments = this.Core.GetAttributeValue(sourceLineNumbers, attrib); - break; - case "Compressed": - this.Core.Write(WarningMessages.DeprecatedAttribute(sourceLineNumbers, node.Name.LocalName, attrib.Name.LocalName)); - break; - case "Description": - packageName = this.Core.GetAttributeValue(sourceLineNumbers, attrib); - break; - case "Keywords": - keywords = this.Core.GetAttributeValue(sourceLineNumbers, attrib); - break; - case "Languages": - this.Core.Write(WarningMessages.DeprecatedAttribute(sourceLineNumbers, node.Name.LocalName, attrib.Name.LocalName)); - break; - case "Manufacturer": - packageAuthor = this.Core.GetAttributeValue(sourceLineNumbers, attrib); - break; - case "Platforms": - this.Core.Write(WarningMessages.DeprecatedAttribute(sourceLineNumbers, node.Name.LocalName, attrib.Name.LocalName)); - break; - case "ReadOnly": - security = this.Core.GetAttributeYesNoDefaultValue(sourceLineNumbers, attrib); - break; - case "ShortNames": - this.Core.Write(WarningMessages.DeprecatedAttribute(sourceLineNumbers, node.Name.LocalName, attrib.Name.LocalName)); - break; - case "SummaryCodepage": - codepage = this.Core.GetAttributeLocalizableCodePageValue(sourceLineNumbers, attrib); - break; - default: - this.Core.UnexpectedAttribute(node, attrib); - break; - } - } - else - { - this.Core.ParseExtensionAttribute(node, attrib); - } - } - - this.Core.ParseForExtensionElements(node); - - if (!this.Core.EncounteredError) - { - this.Core.AddSymbol(new SummaryInformationSymbol(sourceLineNumbers) - { - PropertyId = SummaryInformationType.Codepage, - Value = codepage - }); - - this.Core.AddSymbol(new SummaryInformationSymbol(sourceLineNumbers) - { - PropertyId = SummaryInformationType.Title, - Value = "Patch" - }); - - if (null != packageName) - { - this.Core.AddSymbol(new SummaryInformationSymbol(sourceLineNumbers) - { - PropertyId = SummaryInformationType.Subject, - Value = packageName - }); - } - - if (null != packageAuthor) - { - this.Core.AddSymbol(new SummaryInformationSymbol(sourceLineNumbers) - { - PropertyId = SummaryInformationType.Author, - Value = packageAuthor - }); - } - - if (null != keywords) - { - this.Core.AddSymbol(new SummaryInformationSymbol(sourceLineNumbers) - { - PropertyId = SummaryInformationType.Keywords, - Value = keywords - }); - } - - if (null != comments) - { - this.Core.AddSymbol(new SummaryInformationSymbol(sourceLineNumbers) - { - PropertyId = SummaryInformationType.Comments, - Value = comments - }); - } - - this.Core.AddSymbol(new SummaryInformationSymbol(sourceLineNumbers) - { - PropertyId = SummaryInformationType.WindowsInstallerVersion, - Value = msiVersion.ToString(CultureInfo.InvariantCulture) - }); - - this.Core.AddSymbol(new SummaryInformationSymbol(sourceLineNumbers) - { - PropertyId = SummaryInformationType.WordCount, - Value = "0" - }); - - this.Core.AddSymbol(new SummaryInformationSymbol(sourceLineNumbers) - { - PropertyId = SummaryInformationType.Security, - Value = YesNoDefaultType.No == security ? "0" : YesNoDefaultType.Yes == security ? "4" : "2" - }); - } - } - - /// - /// Parses a permission element. - /// - /// Element to parse. - /// Identifier of object to be secured. - /// Name of table that contains objectId. - private void ParsePermissionElement(XElement node, string objectId, string tableName) - { - var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node); - var bits = new BitArray(32); - string domain = null; - string[] specialPermissions = null; - string user = null; - - switch (tableName) - { - case "CreateFolder": - specialPermissions = Common.FolderPermissions; - break; - case "File": - specialPermissions = Common.FilePermissions; - break; - case "Registry": - specialPermissions = Common.RegistryPermissions; - break; - default: - this.Core.UnexpectedElement(node.Parent, node); - return; // stop processing this element since no valid permissions are available - } - - foreach (var attrib in node.Attributes()) - { - if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace) - { - switch (attrib.Name.LocalName) - { - case "Domain": - domain = this.Core.GetAttributeValue(sourceLineNumbers, attrib); - break; - case "User": - user = this.Core.GetAttributeValue(sourceLineNumbers, attrib); - break; - case "FileAllRights": - // match the WinNT.h mask FILE_ALL_ACCESS for value 0x001F01FF (aka 1 1111 0000 0001 1111 1111 or 2032127) - bits[0] = bits[1] = bits[2] = bits[3] = bits[4] = bits[5] = bits[6] = bits[7] = bits[8] = bits[16] = bits[17] = bits[18] = bits[19] = bits[20] = true; - break; - case "SpecificRightsAll": - // match the WinNT.h mask SPECIFIC_RIGHTS_ALL for value 0x0000FFFF (aka 1111 1111 1111 1111) - bits[0] = bits[1] = bits[2] = bits[3] = bits[4] = bits[5] = bits[6] = bits[7] = bits[8] = bits[9] = bits[10] = bits[11] = bits[12] = bits[13] = bits[14] = bits[15] = true; - break; - default: - var attribValue = this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib); - if (!this.Core.TrySetBitFromName(Common.StandardPermissions, attrib.Name.LocalName, attribValue, bits, 16)) - { - if (!this.Core.TrySetBitFromName(Common.GenericPermissions, attrib.Name.LocalName, attribValue, bits, 28)) - { - if (!this.Core.TrySetBitFromName(specialPermissions, attrib.Name.LocalName, attribValue, bits, 0)) - { - this.Core.UnexpectedAttribute(node, attrib); - break; - } - } - } - break; - } - } - else - { - this.Core.ParseExtensionAttribute(node, attrib); - } - } - - if (null == user) - { - this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "User")); - } - - var permission = this.Core.CreateIntegerFromBitArray(bits); - - if (Int32.MinValue == permission) // just GENERIC_READ, which is MSI_NULL - { - this.Core.Write(ErrorMessages.GenericReadNotAllowed(sourceLineNumbers)); - } - - this.Core.ParseForExtensionElements(node); - - if (!this.Core.EncounteredError) - { - this.Core.AddSymbol(new LockPermissionsSymbol(sourceLineNumbers) - { - LockObject = objectId, - Table = tableName, - Domain = domain, - User = user, - Permission = permission - }); - } - } - - /// - /// Parses an extended permission element. - /// - /// Element to parse. - /// Identifier of object to be secured. - /// Name of table that contains objectId. - private void ParsePermissionExElement(XElement node, string objectId, string tableName) - { - var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node); - string condition = null; - Identifier id = null; - string sddl = null; - - switch (tableName) - { - case "CreateFolder": - case "File": - case "Registry": - case "ServiceInstall": - break; - default: - this.Core.UnexpectedElement(node.Parent, node); - return; // stop processing this element since nothing will be valid. - } - - foreach (var attrib in node.Attributes()) - { - if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace) - { - switch (attrib.Name.LocalName) - { - case "Id": - id = this.Core.GetAttributeIdentifier(sourceLineNumbers, attrib); - break; - case "Condition": - condition = this.Core.GetAttributeValue(sourceLineNumbers, attrib); - break; - case "Sddl": - sddl = this.Core.GetAttributeValue(sourceLineNumbers, attrib); - break; - default: - this.Core.UnexpectedAttribute(node, attrib); - break; - } - } - else - { - this.Core.ParseExtensionAttribute(node, attrib); - } - } - - this.Core.VerifyNoInnerText(sourceLineNumbers, node); - - if (null == sddl) - { - this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Sddl")); - } - - if (null == id) - { - id = this.Core.CreateIdentifier("pme", objectId, tableName, sddl); - } - - this.Core.ParseForExtensionElements(node); - - if (!this.Core.EncounteredError) - { - this.Core.AddSymbol(new MsiLockPermissionsExSymbol(sourceLineNumbers, id) - { - LockObject = objectId, - Table = tableName, - SDDLText = sddl, - Condition = condition - }); - } - } - - /// - /// Parses a progid element - /// - /// Element to parse. - /// Identifier of parent component. - /// Flag if progid is advertised. - /// CLSID related to ProgId. - /// Default description of ProgId - /// Optional parent ProgId - /// Set to true if an extension is found; used for error-checking. - /// Whether or not this ProgId is the first one found in the parent class. - /// This element's Id. - private string ParseProgIdElement(XElement node, string componentId, YesNoType advertise, string classId, string description, string parent, ref bool foundExtension, YesNoType firstProgIdForClass) - { - var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node); - string icon = null; - var iconIndex = CompilerConstants.IntegerNotSet; - string noOpen = null; - string progId = null; - var progIdAdvertise = YesNoType.NotSet; - - foreach (var attrib in node.Attributes()) - { - if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace) - { - switch (attrib.Name.LocalName) - { - case "Id": - progId = this.Core.GetAttributeValue(sourceLineNumbers, attrib); - break; - case "Advertise": - progIdAdvertise = this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib); - break; - case "Description": - description = this.Core.GetAttributeValue(sourceLineNumbers, attrib, EmptyRule.CanBeEmpty); - break; - case "Icon": - icon = this.Core.GetAttributeIdentifierValue(sourceLineNumbers, attrib); - break; - case "IconIndex": - iconIndex = this.Core.GetAttributeIntegerValue(sourceLineNumbers, attrib, Int16.MinValue + 1, Int16.MaxValue); - break; - case "NoOpen": - noOpen = this.Core.GetAttributeValue(sourceLineNumbers, attrib, EmptyRule.CanBeEmpty); - break; - default: - this.Core.UnexpectedAttribute(node, attrib); - break; - } - } - else - { - this.Core.ParseExtensionAttribute(node, attrib); - } - } - - if ((YesNoType.No == advertise && YesNoType.Yes == progIdAdvertise) || (YesNoType.Yes == advertise && YesNoType.No == progIdAdvertise)) - { - this.Core.Write(ErrorMessages.AdvertiseStateMustMatch(sourceLineNumbers, advertise.ToString(), progIdAdvertise.ToString())); - } - else if (YesNoType.NotSet != progIdAdvertise) - { - advertise = progIdAdvertise; - } - - if (YesNoType.NotSet == advertise) - { - advertise = YesNoType.No; - } - - if (null != parent && (null != icon || CompilerConstants.IntegerNotSet != iconIndex)) - { - this.Core.Write(ErrorMessages.VersionIndependentProgIdsCannotHaveIcons(sourceLineNumbers)); - } - - var firstProgIdForNestedClass = YesNoType.Yes; - foreach (var child in node.Elements()) - { - if (CompilerCore.WixNamespace == child.Name.Namespace) - { - switch (child.Name.LocalName) - { - case "Extension": - this.ParseExtensionElement(child, componentId, advertise, progId); - foundExtension = true; - break; - case "ProgId": - // Only allow one nested ProgId. If we have a child, we should not have a parent. - if (null == parent) - { - if (YesNoType.Yes == advertise) - { - this.ParseProgIdElement(child, componentId, advertise, null, description, progId, ref foundExtension, firstProgIdForNestedClass); - } - else if (YesNoType.No == advertise) - { - this.ParseProgIdElement(child, componentId, advertise, classId, description, progId, ref foundExtension, firstProgIdForNestedClass); - } - - firstProgIdForNestedClass = YesNoType.No; // any ProgId after this one is definitely not the first. - } - else - { - var childSourceLineNumbers = Preprocessor.GetSourceLineNumbers(child); - this.Core.Write(ErrorMessages.ProgIdNestedTooDeep(childSourceLineNumbers)); - } - break; - default: - this.Core.UnexpectedElement(node, child); - break; - } - } - else - { - this.Core.ParseExtensionElement(node, child); - } - } - - if (YesNoType.Yes == advertise) - { - if (!this.Core.EncounteredError) - { - var symbol = this.Core.AddSymbol(new ProgIdSymbol(sourceLineNumbers, new Identifier(AccessModifier.Public, progId)) - { - ProgId = progId, - ParentProgIdRef = parent, - ClassRef = classId, - Description = description, - }); - - if (null != icon) - { - symbol.IconRef = icon; - this.Core.CreateSimpleReference(sourceLineNumbers, SymbolDefinitions.Icon, icon); - } - - if (CompilerConstants.IntegerNotSet != iconIndex) - { - symbol.IconIndex = iconIndex; - } - - this.Core.EnsureTable(sourceLineNumbers, WindowsInstallerTableDefinitions.Class); - } - } - else if (YesNoType.No == advertise) - { - this.Core.CreateRegistryRow(sourceLineNumbers, RegistryRootType.ClassesRoot, progId, String.Empty, description, componentId); - if (null != classId) - { - this.Core.CreateRegistryRow(sourceLineNumbers, RegistryRootType.ClassesRoot, String.Concat(progId, "\\CLSID"), String.Empty, classId, componentId); - if (null != parent) // if this is a version independent ProgId - { - if (YesNoType.Yes == firstProgIdForClass) - { - this.Core.CreateRegistryRow(sourceLineNumbers, RegistryRootType.ClassesRoot, String.Concat("CLSID\\", classId, "\\VersionIndependentProgID"), String.Empty, progId, componentId); - } - - this.Core.CreateRegistryRow(sourceLineNumbers, RegistryRootType.ClassesRoot, String.Concat(progId, "\\CurVer"), String.Empty, parent, componentId); - } - else - { - if (YesNoType.Yes == firstProgIdForClass) - { - this.Core.CreateRegistryRow(sourceLineNumbers, RegistryRootType.ClassesRoot, String.Concat("CLSID\\", classId, "\\ProgID"), String.Empty, progId, componentId); - } - } - } - - if (null != icon) // ProgId's Default Icon - { - this.Core.CreateSimpleReference(sourceLineNumbers, SymbolDefinitions.File, icon); - - icon = String.Format(CultureInfo.InvariantCulture, "\"[#{0}]\"", icon); - - if (CompilerConstants.IntegerNotSet != iconIndex) - { - icon = String.Concat(icon, ",", iconIndex); - } - - this.Core.CreateRegistryRow(sourceLineNumbers, RegistryRootType.ClassesRoot, String.Concat(progId, "\\DefaultIcon"), String.Empty, icon, componentId); - } - } - - if (null != noOpen) - { - this.Core.CreateRegistryRow(sourceLineNumbers, RegistryRootType.ClassesRoot, progId, "NoOpen", noOpen, componentId); // ProgId NoOpen name - } - - // raise an error for an orphaned ProgId - if (YesNoType.Yes == advertise && !foundExtension && null == parent && null == classId) - { - this.Core.Write(WarningMessages.OrphanedProgId(sourceLineNumbers, progId)); - } - - return progId; - } - - /// - /// Parses a property element. - /// - /// Element to parse. - private void ParsePropertyElement(XElement node) - { - var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node); - Identifier id = null; - var admin = false; - var complianceCheck = false; - var hidden = false; - var secure = false; - var suppressModularization = YesNoType.NotSet; - string value = null; - - foreach (var attrib in node.Attributes()) - { - if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace) - { - switch (attrib.Name.LocalName) - { - case "Id": - id = this.Core.GetAttributeIdentifier(sourceLineNumbers, attrib); - break; - case "Admin": - admin = YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib); - break; - case "ComplianceCheck": - complianceCheck = YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib); - break; - case "Hidden": - hidden = YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib); - break; - case "Secure": - secure = YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib); - break; - case "SuppressModularization": - suppressModularization = this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib); - break; - case "Value": - value = this.Core.GetAttributeValue(sourceLineNumbers, attrib); - break; - default: - this.Core.UnexpectedAttribute(node, attrib); - break; - } - } - else - { - this.Core.ParseExtensionAttribute(node, attrib); - } - } - - if (null == id) - { - this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Id")); - id = Identifier.Invalid; - } - else if ("ProductID" == id.Id) - { - this.Core.Write(WarningMessages.ProductIdAuthored(sourceLineNumbers)); - } - else if ("SecureCustomProperties" == id.Id || "AdminProperties" == id.Id || "MsiHiddenProperties" == id.Id) - { - this.Core.Write(ErrorMessages.CannotAuthorSpecialProperties(sourceLineNumbers, id.Id)); - } - - this.Core.VerifyNoInnerText(sourceLineNumbers, node); - - if ("ErrorDialog" == id.Id) - { - this.Core.CreateSimpleReference(sourceLineNumbers, SymbolDefinitions.Dialog, value); - } - - foreach (var child in node.Elements()) - { - if (CompilerCore.WixNamespace == child.Name.Namespace) - { - { - switch (child.Name.LocalName) - { - case "ProductSearch": - this.ParseProductSearchElement(child, id.Id); - secure = true; - break; - default: - // let ParseSearchSignatures handle standard AppSearch children and unknown elements - break; - } - } - } - } - - // see if this property is used for appSearch - var signatures = this.ParseSearchSignatures(node); - - // If we're doing CCP then there must be a signature. - if (complianceCheck && 0 == signatures.Count) - { - this.Core.Write(ErrorMessages.SearchElementRequiredWithAttribute(sourceLineNumbers, node.Name.LocalName, "ComplianceCheck", "yes")); - } - - foreach (var sig in signatures) - { - if (complianceCheck && !this.Core.EncounteredError) - { - this.Core.AddSymbol(new CCPSearchSymbol(sourceLineNumbers, new Identifier(AccessModifier.Private, sig))); - } - - this.AddAppSearch(sourceLineNumbers, id, sig); - } - - // If we're doing AppSearch get that setup. - if (0 < signatures.Count) - { - this.AddProperty(sourceLineNumbers, id, value, admin, secure, hidden, false); - } - else // just a normal old property. - { - // If the property value is empty and none of the flags are set, print out a warning that we're ignoring - // the element. - if (String.IsNullOrEmpty(value) && !admin && !secure && !hidden) - { - this.Core.Write(WarningMessages.PropertyUseless(sourceLineNumbers, id.Id)); - } - else // there is a value and/or a flag set, do that. - { - this.AddProperty(sourceLineNumbers, id, value, admin, secure, hidden, false); - } - } - - if (!this.Core.EncounteredError && YesNoType.Yes == suppressModularization) - { - this.Core.Write(WarningMessages.PropertyModularizationSuppressed(sourceLineNumbers)); - - this.Core.AddSymbol(new WixSuppressModularizationSymbol(sourceLineNumbers, id)); - } - } - - /// - /// Parses a RegistryKey element. - /// - /// Element to parse. - /// Identifier for parent component. - /// Root specified when element is nested under another Registry element, otherwise CompilerConstants.IntegerNotSet. - /// Parent key for this Registry element when nested. - /// true if the component is 64-bit. - /// Identifier of this registry key since it could be the component's keypath. - /// Yes if this element was marked as the parent component's key path, No if explicitly marked as not being a key path, or NotSet otherwise. - private YesNoType ParseRegistryKeyElement(XElement node, string componentId, RegistryRootType? root, string parentKey, bool win64Component, out string possibleKeyPath) - { - var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node); - Identifier id = null; - var key = parentKey; // default to parent key path - var forceCreateOnInstall = false; - var forceDeleteOnUninstall = false; - var keyPath = YesNoType.NotSet; - - possibleKeyPath = null; - - foreach (var attrib in node.Attributes()) - { - if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace) - { - switch (attrib.Name.LocalName) - { - case "Id": - id = this.Core.GetAttributeIdentifier(sourceLineNumbers, attrib); - break; - case "Action": - this.Core.Write(WarningMessages.DeprecatedRegistryKeyActionAttribute(sourceLineNumbers)); - var actionValue = this.Core.GetAttributeValue(sourceLineNumbers, attrib); - switch (actionValue) - { - case "create": - forceCreateOnInstall = true; - break; - case "createAndRemoveOnUninstall": - forceCreateOnInstall = true; - forceDeleteOnUninstall = true; - break; - case "none": - break; - case "": - break; - default: - this.Core.Write(ErrorMessages.IllegalAttributeValue(sourceLineNumbers, node.Name.LocalName, attrib.Name.LocalName, actionValue, "create", "createAndRemoveOnUninstall", "none")); - break; - } - break; - case "ForceCreateOnInstall": - forceCreateOnInstall = YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib); - break; - case "ForceDeleteOnUninstall": - forceDeleteOnUninstall = YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib); - break; - case "Key": - key = this.Core.GetAttributeValue(sourceLineNumbers, attrib); - if (null != parentKey) - { - key = Path.Combine(parentKey, key); - } - key = key?.TrimEnd('\\'); - break; - case "Root": - if (root.HasValue) - { - this.Core.Write(ErrorMessages.RegistryRootInvalid(sourceLineNumbers)); - } - - root = this.Core.GetAttributeRegistryRootValue(sourceLineNumbers, attrib, true); - break; - default: - this.Core.UnexpectedAttribute(node, attrib); - break; - } - } - else - { - this.Core.ParseExtensionAttribute(node, attrib); - } - } - - var name = forceCreateOnInstall ? (forceDeleteOnUninstall ? "*" : "+") : (forceDeleteOnUninstall ? "-" : null); - - if (forceCreateOnInstall || forceDeleteOnUninstall) // generates a Registry row, so an Id must be present - { - // generate the identifier if it wasn't provided - if (null == id) - { - id = this.Core.CreateIdentifier("reg", componentId, ((int)root).ToString(CultureInfo.InvariantCulture.NumberFormat), LowercaseOrNull(key), LowercaseOrNull(name)); - } - } - else // does not generate a Registry row, so no Id should be present - { - if (null != id) - { - this.Core.Write(ErrorMessages.IllegalAttributeWithoutOtherAttributes(sourceLineNumbers, node.Name.LocalName, "Id", "ForceCreateOnInstall", "ForceDeleteOnUninstall", "yes", true)); - } - } - - if (!root.HasValue) - { - this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Root")); - } - - if (null == key) - { - this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Key")); - key = String.Empty; // set the key to something to prevent null reference exceptions - } - - foreach (var child in node.Elements()) - { - if (CompilerCore.WixNamespace == child.Name.Namespace) - { - string possibleChildKeyPath = null; - - switch (child.Name.LocalName) - { - case "RegistryKey": - if (YesNoType.Yes == this.ParseRegistryKeyElement(child, componentId, root, key, win64Component, out possibleChildKeyPath)) - { - if (YesNoType.Yes == keyPath) - { - this.Core.Write(ErrorMessages.ComponentMultipleKeyPaths(sourceLineNumbers, child.Name.LocalName, "KeyPath", "yes", "File", "RegistryValue", "ODBCDataSource")); - } - - possibleKeyPath = possibleChildKeyPath; // the child is the key path - keyPath = YesNoType.Yes; - } - else if (null == possibleKeyPath && null != possibleChildKeyPath) - { - possibleKeyPath = possibleChildKeyPath; - } - break; - case "RegistryValue": - if (YesNoType.Yes == this.ParseRegistryValueElement(child, componentId, root, key, win64Component, out possibleChildKeyPath)) - { - if (YesNoType.Yes == keyPath) - { - this.Core.Write(ErrorMessages.ComponentMultipleKeyPaths(sourceLineNumbers, child.Name.LocalName, "KeyPath", "yes", "File", "RegistryValue", "ODBCDataSource")); - } - - possibleKeyPath = possibleChildKeyPath; // the child is the key path - keyPath = YesNoType.Yes; - } - else if (null == possibleKeyPath && null != possibleChildKeyPath) - { - possibleKeyPath = possibleChildKeyPath; - } - break; - case "Permission": - if (!forceCreateOnInstall) - { - this.Core.Write(ErrorMessages.UnexpectedElementWithAttributeValue(sourceLineNumbers, node.Name.LocalName, child.Name.LocalName, "ForceCreateOnInstall", "yes")); - } - this.ParsePermissionElement(child, id.Id, "Registry"); - break; - case "PermissionEx": - if (!forceCreateOnInstall) - { - this.Core.Write(ErrorMessages.UnexpectedElementWithAttributeValue(sourceLineNumbers, node.Name.LocalName, child.Name.LocalName, "ForceCreateOnInstall", "yes")); - } - this.ParsePermissionExElement(child, id.Id, "Registry"); - break; - default: - this.Core.UnexpectedElement(node, child); - break; - } - } - else - { - var context = new Dictionary() { { "RegistryId", id?.Id }, { "ComponentId", componentId }, { "Win64", win64Component.ToString() } }; - this.Core.ParseExtensionElement(node, child, context); - } - } - - if (!this.Core.EncounteredError && null != name) - { - this.Core.AddSymbol(new RegistrySymbol(sourceLineNumbers, id) - { - Root = root.Value, - Key = key, - Name = name, - ComponentRef = componentId, - }); - } - - return keyPath; - } - - /// - /// Parses a RegistryValue element. - /// - /// Element to parse. - /// Identifier for parent component. - /// Root specified when element is nested under a RegistryKey element, otherwise CompilerConstants.IntegerNotSet. - /// Root specified when element is nested under a RegistryKey element, otherwise CompilerConstants.IntegerNotSet. - /// true if the component is 64-bit. - /// Identifier of this registry key since it could be the component's keypath. - /// Yes if this element was marked as the parent component's key path, No if explicitly marked as not being a key path, or NotSet otherwise. - private YesNoType ParseRegistryValueElement(XElement node, string componentId, RegistryRootType? root, string parentKey, bool win64Component, out string possibleKeyPath) - { - var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node); - Identifier id = null; - var key = parentKey; // default to parent key path - string name = null; - string value = null; - string action = null; - var valueType = RegistryValueType.String; - var actionType = RegistryValueActionType.Write; - var keyPath = YesNoType.NotSet; - - possibleKeyPath = null; - - foreach (var attrib in node.Attributes()) - { - if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace) - { - switch (attrib.Name.LocalName) - { - case "Id": - id = this.Core.GetAttributeIdentifier(sourceLineNumbers, attrib); - break; - case "Action": - var actionValue = this.Core.GetAttributeValue(sourceLineNumbers, attrib); - switch (actionValue) - { - case "append": - actionType = RegistryValueActionType.Append; - break; - case "prepend": - actionType = RegistryValueActionType.Prepend; - break; - case "write": - actionType = RegistryValueActionType.Write; - break; - case "": - break; - default: - this.Core.Write(ErrorMessages.IllegalAttributeValue(sourceLineNumbers, node.Name.LocalName, attrib.Name.LocalName, actionValue, "append", "prepend", "write")); - break; - } - break; - case "Key": - key = this.Core.GetAttributeValue(sourceLineNumbers, attrib); - if (null != parentKey) - { - if (parentKey.EndsWith("\\", StringComparison.Ordinal)) - { - key = String.Concat(parentKey, key); - } - else - { - key = String.Concat(parentKey, "\\", key); - } - } - break; - case "KeyPath": - keyPath = this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib); - break; - case "Name": - name = this.Core.GetAttributeValue(sourceLineNumbers, attrib); - break; - case "Root": - if (root.HasValue) - { - this.Core.Write(ErrorMessages.RegistryRootInvalid(sourceLineNumbers)); - } - - root = this.Core.GetAttributeRegistryRootValue(sourceLineNumbers, attrib, true); - break; - case "Type": - var typeValue = this.Core.GetAttributeValue(sourceLineNumbers, attrib); - switch (typeValue) - { - case "binary": - valueType = RegistryValueType.Binary; - break; - case "expandable": - valueType = RegistryValueType.Expandable; - break; - case "integer": - valueType = RegistryValueType.Integer; - break; - case "multiString": - valueType = RegistryValueType.MultiString; - break; - case "string": - valueType = RegistryValueType.String; - break; - case "": - break; - default: - this.Core.Write(ErrorMessages.IllegalAttributeValue(sourceLineNumbers, node.Name.LocalName, attrib.Name.LocalName, typeValue, "binary", "expandable", "integer", "multiString", "string")); - break; - } - break; - case "Value": - value = this.Core.GetAttributeValue(sourceLineNumbers, attrib, EmptyRule.CanBeEmpty); - break; - default: - this.Core.UnexpectedAttribute(node, attrib); - break; - } - } - else - { - this.Core.ParseExtensionAttribute(node, attrib); - } - } - - // generate the identifier if it wasn't provided - if (null == id) - { - id = this.Core.CreateIdentifier("reg", componentId, ((int)(root ?? RegistryRootType.Unknown)).ToString(), LowercaseOrNull(key), LowercaseOrNull(name)); - } - - if (RegistryValueType.MultiString != valueType && (RegistryValueActionType.Append == actionType || RegistryValueActionType.Prepend == actionType)) - { - this.Core.Write(ErrorMessages.IllegalAttributeValueWithoutOtherAttribute(sourceLineNumbers, node.Name.LocalName, "Action", action, "Type", "multiString")); - } - - if (null == key) - { - this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Key")); - } - - if (!root.HasValue) - { - this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Root")); - } - - foreach (var child in node.Elements()) - { - if (CompilerCore.WixNamespace == child.Name.Namespace) - { - switch (child.Name.LocalName) - { - case "MultiString": - case "MultiStringValue": - if (RegistryValueType.MultiString != valueType && null != value) - { - this.Core.Write(ErrorMessages.RegistryMultipleValuesWithoutMultiString(sourceLineNumbers, node.Name.LocalName, "Value", child.Name.LocalName, "Type")); - } - else - { - value = this.ParseRegistryMultiStringElement(child, value); - } - break; - case "Permission": - this.ParsePermissionElement(child, id.Id, "Registry"); - break; - case "PermissionEx": - this.ParsePermissionExElement(child, id.Id, "Registry"); - break; - default: - this.Core.UnexpectedElement(node, child); - break; - } - } - else - { - var context = new Dictionary() { { "RegistryId", id?.Id }, { "ComponentId", componentId }, { "Win64", win64Component.ToString() } }; - this.Core.ParseExtensionElement(node, child, context); - } - } - - //switch (typeType) - //{ - //case Wix.RegistryValue.TypeType.binary: - // value = String.Concat("#x", value); - // break; - //case Wix.RegistryValue.TypeType.expandable: - // value = String.Concat("#%", value); - // break; - //case Wix.RegistryValue.TypeType.integer: - // value = String.Concat("#", value); - // break; - //case Wix.RegistryValue.TypeType.multiString: - // switch (actionType) - // { - // case Wix.RegistryValue.ActionType.append: - // value = String.Concat("[~]", value); - // break; - // case Wix.RegistryValue.ActionType.prepend: - // value = String.Concat(value, "[~]"); - // break; - // case Wix.RegistryValue.ActionType.write: - // default: - // if (null != value && -1 == value.IndexOf("[~]", StringComparison.Ordinal)) - // { - // value = String.Format(CultureInfo.InvariantCulture, "[~]{0}[~]", value); - // } - // break; - // } - // break; - //case Wix.RegistryValue.TypeType.@string: - // // escape the leading '#' character for string registry keys - // if (null != value && value.StartsWith("#", StringComparison.Ordinal)) - // { - // value = String.Concat("#", value); - // } - // break; - //} - - // value may be set by child MultiStringValue elements, so it must be checked here - if (null == value && valueType != RegistryValueType.Binary) - { - this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Value")); - } - else if (0 == value?.Length && ("+" == name || "-" == name || "*" == name)) // prevent accidental authoring of special name values - { - this.Core.Write(ErrorMessages.RegistryNameValueIncorrect(sourceLineNumbers, node.Name.LocalName, "Name", name)); - } - - if (!this.Core.EncounteredError) - { - this.Core.AddSymbol(new RegistrySymbol(sourceLineNumbers, id) - { - Root = root.Value, - Key = key, - Name = name, - Value = value, - ValueType = valueType, - ValueAction = actionType, - ComponentRef = componentId, - }); - } - - // If this was just a regular registry key (that could be the key path) - // and no child registry key set the possible key path, let's make this - // Registry/@Id a possible key path. - if (null == possibleKeyPath) - { - possibleKeyPath = id.Id; - } - - return keyPath; - } - - private string ParseRegistryMultiStringElement(XElement node, string value) - { - var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node); - string multiStringValue = null; - - foreach (var attrib in node.Attributes()) - { - if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace) - { - switch (attrib.Name.LocalName) - { - case "Value": - multiStringValue = this.Core.GetAttributeValue(sourceLineNumbers, attrib); - break; - default: - this.Core.UnexpectedAttribute(node, attrib); - break; - } - } - } - - this.Core.VerifyNoInnerText(sourceLineNumbers, node); - - this.Core.ParseForExtensionElements(node); - - return null == value ? multiStringValue ?? "[~]" : String.Concat(value, "[~]", multiStringValue); - } - - /// - /// Parses a RemoveRegistryKey element. - /// - /// The element to parse. - /// The component identifier of the parent element. - private void ParseRemoveRegistryKeyElement(XElement node, string componentId) - { - var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node); - Identifier id = null; - RemoveRegistryActionType? actionType = null; - string key = null; - var name = "-"; - RegistryRootType? root = null; - - foreach (var attrib in node.Attributes()) - { - if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace) - { - switch (attrib.Name.LocalName) - { - case "Id": - id = this.Core.GetAttributeIdentifier(sourceLineNumbers, attrib); - break; - case "Action": - var actionValue = this.Core.GetAttributeValue(sourceLineNumbers, attrib); - switch (actionValue) - { - case "removeOnInstall": - actionType = RemoveRegistryActionType.RemoveOnInstall; - break; - case "removeOnUninstall": - actionType = RemoveRegistryActionType.RemoveOnUninstall; - break; - case "": - break; - default: - this.Core.Write(ErrorMessages.IllegalAttributeValue(sourceLineNumbers, node.Name.LocalName, attrib.Name.LocalName, actionValue, "removeOnInstall", "removeOnUninstall")); - break; - } - //if (0 < action.Length) - //{ - // if (!Wix.RemoveRegistryKey.TryParseActionType(action, out actionType)) - // { - // this.Core.Write(ErrorMessages.IllegalAttributeValue(sourceLineNumbers, node.Name.LocalName, attrib.Name.LocalName, action, "removeOnInstall", "removeOnUninstall")); - // } - //} - break; - case "Key": - key = this.Core.GetAttributeValue(sourceLineNumbers, attrib); - break; - case "Root": - root = this.Core.GetAttributeRegistryRootValue(sourceLineNumbers, attrib, true); - break; - default: - this.Core.UnexpectedAttribute(node, attrib); - break; - } - } - else - { - this.Core.ParseExtensionAttribute(node, attrib); - } - } - - // generate the identifier if it wasn't provided - if (null == id) - { - id = this.Core.CreateIdentifier("reg", componentId, ((int)root).ToString(CultureInfo.InvariantCulture.NumberFormat), LowercaseOrNull(key), LowercaseOrNull(name)); - } - - if (!root.HasValue) - { - this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Root")); - } - - if (null == key) - { - this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Key")); - } - - if (!actionType.HasValue) - { - this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Action")); - } - - this.Core.ParseForExtensionElements(node); - - if (!this.Core.EncounteredError) - { - this.Core.AddSymbol(new RemoveRegistrySymbol(sourceLineNumbers, id) - { - Root = root.Value, - Key = key, - Name = name, - Action = actionType.Value, - ComponentRef = componentId, - }); - } - } - - /// - /// Parses a RemoveRegistryValue element. - /// - /// The element to parse. - /// The component identifier of the parent element. - private void ParseRemoveRegistryValueElement(XElement node, string componentId) - { - var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node); - Identifier id = null; - string key = null; - string name = null; - RegistryRootType? root = null; - - foreach (var attrib in node.Attributes()) - { - if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace) - { - switch (attrib.Name.LocalName) - { - case "Id": - id = this.Core.GetAttributeIdentifier(sourceLineNumbers, attrib); - break; - case "Key": - key = this.Core.GetAttributeValue(sourceLineNumbers, attrib); - break; - case "Name": - name = this.Core.GetAttributeValue(sourceLineNumbers, attrib); - break; - case "Root": - root = this.Core.GetAttributeRegistryRootValue(sourceLineNumbers, attrib, true); - break; - default: - this.Core.UnexpectedAttribute(node, attrib); - break; - } - } - else - { - this.Core.ParseExtensionAttribute(node, attrib); - } - } - - // generate the identifier if it wasn't provided - if (null == id) - { - id = this.Core.CreateIdentifier("reg", componentId, ((int)root).ToString(CultureInfo.InvariantCulture.NumberFormat), LowercaseOrNull(key), LowercaseOrNull(name)); - } - - if (!root.HasValue) - { - this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Root")); - } - - if (null == key) - { - this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Key")); - } - - this.Core.ParseForExtensionElements(node); - - if (!this.Core.EncounteredError) - { - this.Core.AddSymbol(new RemoveRegistrySymbol(sourceLineNumbers, id) - { - Root = root.Value, - Key = key, - Name = name, - ComponentRef = componentId - }); - } - } - - /// - /// Parses a remove file element. - /// - /// Element to parse. - /// Identifier of parent component. - /// Identifier of the parent component's directory. - private void ParseRemoveFileElement(XElement node, string componentId, string parentDirectory) - { - var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node); - Identifier id = null; - string directory = null; - string name = null; - bool? onInstall = null; - bool? onUninstall = null; - string property = null; - string shortName = null; - - foreach (var attrib in node.Attributes()) - { - if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace) - { - switch (attrib.Name.LocalName) - { - case "Id": - id = this.Core.GetAttributeIdentifier(sourceLineNumbers, attrib); - break; - case "Directory": - directory = this.Core.CreateDirectoryReferenceFromInlineSyntax(sourceLineNumbers, attrib, parentDirectory); - break; - case "Name": - name = this.Core.GetAttributeLongFilename(sourceLineNumbers, attrib, true); - break; - case "On": - var onValue = this.Core.GetAttributeValue(sourceLineNumbers, attrib); - switch (onValue) - { - case "install": - onInstall = true; - break; - case "uninstall": - onUninstall = true; - break; - case "both": - onInstall = true; - onUninstall = true; - break; - } - break; - case "Property": - property = this.Core.GetAttributeIdentifierValue(sourceLineNumbers, attrib); - break; - case "ShortName": - shortName = this.Core.GetAttributeShortFilename(sourceLineNumbers, attrib, true); - break; - default: - this.Core.UnexpectedAttribute(node, attrib); - break; - } - } - else - { - this.Core.ParseExtensionAttribute(node, attrib); - } - } - - if (null == name) - { - this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Name")); - } - - if (!onInstall.HasValue && !onUninstall.HasValue) - { - this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "On")); - } - - if (null != directory && null != property) - { - this.Core.Write(ErrorMessages.IllegalAttributeWithOtherAttribute(sourceLineNumbers, node.Name.LocalName, "Property", "Directory", directory)); - } - - if (null == id) - { - var on = (onInstall == true && onUninstall == true) ? 3 : (onUninstall == true) ? 2 : (onInstall == true) ? 1 : 0; - id = this.Core.CreateIdentifier("rmf", directory ?? property ?? parentDirectory, LowercaseOrNull(shortName), LowercaseOrNull(name), on.ToString()); - } - - this.Core.ParseForExtensionElements(node); - - if (!this.Core.EncounteredError) - { - this.Core.AddSymbol(new RemoveFileSymbol(sourceLineNumbers, id) - { - ComponentRef = componentId, - FileName = name, - ShortFileName = shortName, - DirPropertyRef = directory ?? property ?? parentDirectory, - OnInstall = onInstall, - OnUninstall = onUninstall, - }); - } - } - - /// - /// Parses a RemoveFolder element. - /// - /// Element to parse. - /// Identifier of parent component. - /// Identifier of parent component's directory. - private void ParseRemoveFolderElement(XElement node, string componentId, string parentDirectory) - { - var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node); - Identifier id = null; - string directory = null; - bool? onInstall = null; - bool? onUninstall = null; - string property = null; - - foreach (var attrib in node.Attributes()) - { - if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace) - { - switch (attrib.Name.LocalName) - { - case "Id": - id = this.Core.GetAttributeIdentifier(sourceLineNumbers, attrib); - break; - case "Directory": - directory = this.Core.CreateDirectoryReferenceFromInlineSyntax(sourceLineNumbers, attrib, parentDirectory); - break; - case "On": - var onValue = this.Core.GetAttributeValue(sourceLineNumbers, attrib); - switch (onValue) - { - case "install": - onInstall = true; - break; - case "uninstall": - onUninstall = true; - break; - case "both": - onInstall = true; - onUninstall = true; - break; - } - break; - case "Property": - property = this.Core.GetAttributeValue(sourceLineNumbers, attrib); - break; - default: - this.Core.UnexpectedAttribute(node, attrib); - break; - } - } - else - { - this.Core.ParseExtensionAttribute(node, attrib); - } - } - - if (!onInstall.HasValue && !onUninstall.HasValue) - { - this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "On")); - } - - if (null != directory && null != property) - { - this.Core.Write(ErrorMessages.IllegalAttributeWithOtherAttribute(sourceLineNumbers, node.Name.LocalName, "Property", "Directory", directory)); - } - - if (null == id) - { - var on = (onInstall == true && onUninstall == true) ? 3 : (onUninstall == true) ? 2 : (onInstall == true) ? 1 : 0; - id = this.Core.CreateIdentifier("rmf", directory ?? property ?? parentDirectory, on.ToString()); - } - - this.Core.ParseForExtensionElements(node); - - if (!this.Core.EncounteredError) - { - this.Core.AddSymbol(new RemoveFileSymbol(sourceLineNumbers, id) - { - ComponentRef = componentId, - DirPropertyRef = directory ?? property ?? parentDirectory, - OnInstall = onInstall, - OnUninstall = onUninstall - }); - } - } - - /// - /// Parses a reserve cost element. - /// - /// Element to parse. - /// Identifier of parent component. - /// Optional and default identifier of referenced directory. - private void ParseReserveCostElement(XElement node, string componentId, string directoryId) - { - var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node); - Identifier id = null; - var runFromSource = CompilerConstants.IntegerNotSet; - var runLocal = CompilerConstants.IntegerNotSet; - - foreach (var attrib in node.Attributes()) - { - if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace) - { - switch (attrib.Name.LocalName) - { - case "Id": - id = this.Core.GetAttributeIdentifier(sourceLineNumbers, attrib); - break; - case "Directory": - directoryId = this.Core.CreateDirectoryReferenceFromInlineSyntax(sourceLineNumbers, attrib, directoryId); - break; - case "RunFromSource": - runFromSource = this.Core.GetAttributeIntegerValue(sourceLineNumbers, attrib, 0, Int32.MaxValue); - break; - case "RunLocal": - runLocal = this.Core.GetAttributeIntegerValue(sourceLineNumbers, attrib, 0, Int32.MaxValue); - break; - default: - this.Core.UnexpectedAttribute(node, attrib); - break; - } - } - else - { - this.Core.ParseExtensionAttribute(node, attrib); - } - } - - if (null == id) - { - id = this.Core.CreateIdentifier("rc", componentId, directoryId); - } - - if (CompilerConstants.IntegerNotSet == runFromSource) - { - this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "RunFromSource")); - } - - if (CompilerConstants.IntegerNotSet == runLocal) - { - this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "RunLocal")); - } - - this.Core.ParseForExtensionElements(node); - - if (!this.Core.EncounteredError) - { - this.Core.AddSymbol(new ReserveCostSymbol(sourceLineNumbers, id) - { - ComponentRef = componentId, - ReserveFolder = directoryId, - ReserveLocal = runLocal, - ReserveSource = runFromSource - }); - } - } - - /// - /// Parses a sequence element. - /// - /// Element to parse. - /// Name of sequence table. - private void ParseSequenceElement(XElement node, SequenceTable sequenceTable) - { - // Parse each action in the sequence. - foreach (var child in node.Elements()) - { - var childSourceLineNumbers = Preprocessor.GetSourceLineNumbers(child); - var actionName = child.Name.LocalName; - string afterAction = null; - string beforeAction = null; - string condition = null; - var customAction = "Custom" == actionName; - var overridable = false; - var exitSequence = CompilerConstants.IntegerNotSet; - var sequence = CompilerConstants.IntegerNotSet; - var showDialog = "Show" == actionName; - var specialAction = "InstallExecute" == actionName || "InstallExecuteAgain" == actionName || "RemoveExistingProducts" == actionName || "DisableRollback" == actionName || "ScheduleReboot" == actionName || "ForceReboot" == actionName || "ResolveSource" == actionName; - var specialStandardAction = "AppSearch" == actionName || "CCPSearch" == actionName || "RMCCPSearch" == actionName || "LaunchConditions" == actionName || "FindRelatedProducts" == actionName; - var suppress = false; - - foreach (var attrib in child.Attributes()) - { - if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace) - { - switch (attrib.Name.LocalName) - { - case "Action": - if (customAction) - { - actionName = this.Core.GetAttributeIdentifierValue(childSourceLineNumbers, attrib); - this.Core.CreateSimpleReference(childSourceLineNumbers, SymbolDefinitions.CustomAction, actionName); - } - else - { - this.Core.UnexpectedAttribute(child, attrib); - } - break; - case "After": - if (customAction || showDialog || specialAction || specialStandardAction) - { - afterAction = this.Core.GetAttributeIdentifierValue(childSourceLineNumbers, attrib); - this.Core.CreateSimpleReference(childSourceLineNumbers, SymbolDefinitions.WixAction, sequenceTable.ToString(), afterAction); - } - else - { - this.Core.UnexpectedAttribute(child, attrib); - } - break; - case "Before": - if (customAction || showDialog || specialAction || specialStandardAction) - { - beforeAction = this.Core.GetAttributeIdentifierValue(childSourceLineNumbers, attrib); - this.Core.CreateSimpleReference(childSourceLineNumbers, SymbolDefinitions.WixAction, sequenceTable.ToString(), beforeAction); - } - else - { - this.Core.UnexpectedAttribute(child, attrib); - } - break; - case "Condition": - condition = this.Core.GetAttributeValue(childSourceLineNumbers, attrib); - break; - case "Dialog": - if (showDialog) - { - actionName = this.Core.GetAttributeIdentifierValue(childSourceLineNumbers, attrib); - this.Core.CreateSimpleReference(childSourceLineNumbers, SymbolDefinitions.Dialog, actionName); - } - else - { - this.Core.UnexpectedAttribute(child, attrib); - } - break; - case "OnExit": - if (customAction || showDialog || specialAction) - { - var exitValue = this.Core.GetAttributeValue(childSourceLineNumbers, attrib); - switch (exitValue) - { - case "success": - exitSequence = -1; - break; - case "cancel": - exitSequence = -2; - break; - case "error": - exitSequence = -3; - break; - case "suspend": - exitSequence = -4; - break; - } - } - else - { - this.Core.UnexpectedAttribute(child, attrib); - } - break; - case "Overridable": - overridable = YesNoType.Yes == this.Core.GetAttributeYesNoValue(childSourceLineNumbers, attrib); - break; - case "Sequence": - sequence = this.Core.GetAttributeIntegerValue(childSourceLineNumbers, attrib, 1, Int16.MaxValue); - break; - case "Suppress": - suppress = YesNoType.Yes == this.Core.GetAttributeYesNoValue(childSourceLineNumbers, attrib); - break; - default: - this.Core.UnexpectedAttribute(node, attrib); - break; - } - } - else - { - this.Core.ParseExtensionAttribute(node, attrib); - } - } - - this.Core.VerifyNoInnerText(childSourceLineNumbers, node); - - if (customAction && "Custom" == actionName) - { - this.Core.Write(ErrorMessages.ExpectedAttribute(childSourceLineNumbers, child.Name.LocalName, "Action")); - } - else if (showDialog && "Show" == actionName) - { - this.Core.Write(ErrorMessages.ExpectedAttribute(childSourceLineNumbers, child.Name.LocalName, "Dialog")); - } - - if (CompilerConstants.IntegerNotSet != sequence) - { - if (CompilerConstants.IntegerNotSet != exitSequence) - { - this.Core.Write(ErrorMessages.IllegalAttributeWithOtherAttribute(childSourceLineNumbers, child.Name.LocalName, "Sequence", "OnExit")); - } - else if (null != beforeAction || null != afterAction) - { - this.Core.Write(ErrorMessages.IllegalAttributeWithOtherAttribute(childSourceLineNumbers, child.Name.LocalName, "Sequence", "Before", "After")); - } - } - else // sequence not specified use OnExit (which may also be not set). - { - sequence = exitSequence; - } - - if (null != beforeAction && null != afterAction) - { - this.Core.Write(ErrorMessages.IllegalAttributeWithOtherAttribute(childSourceLineNumbers, child.Name.LocalName, "After", "Before")); - } - else if ((customAction || showDialog || specialAction) && !suppress && CompilerConstants.IntegerNotSet == sequence && null == beforeAction && null == afterAction) - { - this.Core.Write(ErrorMessages.NeedSequenceBeforeOrAfter(childSourceLineNumbers, child.Name.LocalName)); - } - - // action that is scheduled to occur before/after itself - if (beforeAction == actionName) - { - this.Core.Write(ErrorMessages.ActionScheduledRelativeToItself(childSourceLineNumbers, child.Name.LocalName, "Before", beforeAction)); - } - else if (afterAction == actionName) - { - this.Core.Write(ErrorMessages.ActionScheduledRelativeToItself(childSourceLineNumbers, child.Name.LocalName, "After", afterAction)); - } - - // normal standard actions cannot be set overridable by the user (since they are overridable by default) - if (overridable && WindowsInstallerStandard.IsStandardAction(actionName) && !specialAction) - { - this.Core.Write(ErrorMessages.UnexpectedAttribute(childSourceLineNumbers, child.Name.LocalName, "Overridable")); - } - - // suppress cannot be specified at the same time as Before, After, or Sequence - if (suppress && (null != afterAction || null != beforeAction || CompilerConstants.IntegerNotSet != sequence || overridable)) - { - this.Core.Write(ErrorMessages.IllegalAttributeWithOtherAttributes(childSourceLineNumbers, child.Name.LocalName, "Suppress", "Before", "After", "Sequence", "Overridable")); - } - - this.Core.ParseForExtensionElements(child); - - // add the row and any references needed - if (!this.Core.EncounteredError) - { - if (suppress) - { - this.Core.AddSymbol(new WixSuppressActionSymbol(childSourceLineNumbers, new Identifier(AccessModifier.Public, sequenceTable, actionName)) - { - SequenceTable = sequenceTable, - Action = actionName - }); - } - else - { - var symbol = this.Core.AddSymbol(new WixActionSymbol(childSourceLineNumbers, new Identifier(AccessModifier.Public, sequenceTable, actionName)) - { - SequenceTable = sequenceTable, - Action = actionName, - Condition = condition, - Before = beforeAction, - After = afterAction, - Overridable = overridable, - }); - - if (CompilerConstants.IntegerNotSet != sequence) - { - symbol.Sequence = sequence; - } - } - } - } - } - - - /// - /// Parses a service config element. - /// - /// Element to parse. - /// Identifier of parent component. - /// Optional element containing parent's service name. - private void ParseServiceConfigElement(XElement node, string componentId, string serviceName) - { - var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node); - Identifier id = null; - string delayedAutoStart = null; - string failureActionsWhen = null; - var name = serviceName; - var install = false; - var reinstall = false; - var uninstall = false; - string preShutdownDelay = null; - string requiredPrivileges = null; - string sid = null; - - this.Core.Write(WarningMessages.ServiceConfigFamilyNotSupported(sourceLineNumbers, node.Name.LocalName)); - - foreach (var attrib in node.Attributes()) - { - if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace) - { - switch (attrib.Name.LocalName) - { - case "Id": - id = this.Core.GetAttributeIdentifier(sourceLineNumbers, attrib); - break; - case "DelayedAutoStart": - delayedAutoStart = this.Core.GetAttributeValue(sourceLineNumbers, attrib); - switch (delayedAutoStart) - { - case "no": - delayedAutoStart = "0"; - break; - case "yes": - delayedAutoStart = "1"; - break; - default: - // allow everything else to pass through that are hopefully "formatted" Properties. - break; - } - break; - case "FailureActionsWhen": - failureActionsWhen = this.Core.GetAttributeValue(sourceLineNumbers, attrib); - switch (failureActionsWhen) - { - case "failedToStop": - failureActionsWhen = "0"; - break; - case "failedToStopOrReturnedError": - failureActionsWhen = "1"; - break; - default: - // allow everything else to pass through that are hopefully "formatted" Properties. - break; - } - break; - case "OnInstall": - install = YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib); - //if (YesNoType.Yes == install) - //{ - // events |= MsiInterop.MsidbServiceConfigEventInstall; - //} - break; - case "OnReinstall": - reinstall = YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib); - //if (YesNoType.Yes == reinstall) - //{ - // events |= MsiInterop.MsidbServiceConfigEventReinstall; - //} - break; - case "OnUninstall": - uninstall = YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib); - //if (YesNoType.Yes == uninstall) - //{ - // events |= MsiInterop.MsidbServiceConfigEventUninstall; - //} - break; - default: - this.Core.UnexpectedAttribute(node, attrib); - break; - case "PreShutdownDelay": - preShutdownDelay = this.Core.GetAttributeValue(sourceLineNumbers, attrib, EmptyRule.CanBeEmpty); - break; - case "ServiceName": - if (!String.IsNullOrEmpty(serviceName)) - { - this.Core.Write(ErrorMessages.IllegalAttributeWhenNested(sourceLineNumbers, node.Name.LocalName, attrib.Name.LocalName, "ServiceInstall")); - } - - name = this.Core.GetAttributeValue(sourceLineNumbers, attrib); - break; - case "ServiceSid": - sid = this.Core.GetAttributeValue(sourceLineNumbers, attrib); - switch (sid) - { - case "none": - sid = "0"; - break; - case "restricted": - sid = "3"; - break; - case "unrestricted": - sid = "1"; - break; - default: - // allow everything else to pass through that are hopefully "formatted" Properties. - break; - } - break; - } - } - else - { - this.Core.ParseExtensionAttribute(node, attrib); - } - } - - // Get the ServiceConfig required privilegs. - foreach (var child in node.Elements()) - { - if (CompilerCore.WixNamespace == child.Name.Namespace) - { - switch (child.Name.LocalName) - { - case "RequiredPrivilege": - requiredPrivileges = this.ParseRequiredPrivilege(child, requiredPrivileges); - break; - default: - this.Core.UnexpectedElement(node, child); - break; - } - } - else - { - this.Core.ParseExtensionElement(node, child); - } - } - - if (String.IsNullOrEmpty(name)) - { - this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "ServiceName")); - } - else if (null == id) - { - id = this.Core.CreateIdentifierFromFilename(name); - } - - if (!install && !reinstall && !uninstall) - { - this.Core.Write(ErrorMessages.ExpectedAttributes(sourceLineNumbers, node.Name.LocalName, "OnInstall", "OnReinstall", "OnUninstall")); - } - - if (String.IsNullOrEmpty(delayedAutoStart) && String.IsNullOrEmpty(failureActionsWhen) && String.IsNullOrEmpty(preShutdownDelay) && String.IsNullOrEmpty(requiredPrivileges) && String.IsNullOrEmpty(sid)) - { - this.Core.Write(ErrorMessages.ExpectedAttributes(sourceLineNumbers, node.Name.LocalName, "DelayedAutoStart", "FailureActionsWhen", "PreShutdownDelay", "ServiceSid", "RequiredPrivilege")); - } - - if (!this.Core.EncounteredError) - { - if (!String.IsNullOrEmpty(delayedAutoStart)) - { - this.Core.AddSymbol(new MsiServiceConfigSymbol(sourceLineNumbers, new Identifier(id.Access, String.Concat(id.Id, ".DS"))) - { - Name = name, - OnInstall = install, - OnReinstall = reinstall, - OnUninstall = uninstall, - ConfigType = MsiServiceConfigType.DelayedAutoStart, - Argument = delayedAutoStart, - ComponentRef = componentId, - }); - } - - if (!String.IsNullOrEmpty(failureActionsWhen)) - { - this.Core.AddSymbol(new MsiServiceConfigSymbol(sourceLineNumbers, new Identifier(id.Access, String.Concat(id.Id, ".FA"))) - { - Name = name, - OnInstall = install, - OnReinstall = reinstall, - OnUninstall = uninstall, - ConfigType = MsiServiceConfigType.FailureActionsFlag, - Argument = failureActionsWhen, - ComponentRef = componentId, - }); - } - - if (!String.IsNullOrEmpty(sid)) - { - this.Core.AddSymbol(new MsiServiceConfigSymbol(sourceLineNumbers, new Identifier(id.Access, String.Concat(id.Id, ".SS"))) - { - Name = name, - OnInstall = install, - OnReinstall = reinstall, - OnUninstall = uninstall, - ConfigType = MsiServiceConfigType.ServiceSidInfo, - Argument = sid, - ComponentRef = componentId, - }); - } - - if (!String.IsNullOrEmpty(requiredPrivileges)) - { - this.Core.AddSymbol(new MsiServiceConfigSymbol(sourceLineNumbers, new Identifier(id.Access, String.Concat(id.Id, ".RP"))) - { - Name = name, - OnInstall = install, - OnReinstall = reinstall, - OnUninstall = uninstall, - ConfigType = MsiServiceConfigType.RequiredPrivilegesInfo, - Argument = requiredPrivileges, - ComponentRef = componentId, - }); - } - - if (!String.IsNullOrEmpty(preShutdownDelay)) - { - this.Core.AddSymbol(new MsiServiceConfigSymbol(sourceLineNumbers, new Identifier(id.Access, String.Concat(id.Id, ".PD"))) - { - Name = name, - OnInstall = install, - OnReinstall = reinstall, - OnUninstall = uninstall, - ConfigType = MsiServiceConfigType.PreshutdownInfo, - Argument = preShutdownDelay, - ComponentRef = componentId, - }); - } - } - } - - private string ParseRequiredPrivilege(XElement node, string requiredPrivileges) - { - var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node); - string privilege = null; - - foreach (var attrib in node.Attributes()) - { - if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace) - { - switch (attrib.Name.LocalName) - { - case "Name": - privilege = this.Core.GetAttributeValue(sourceLineNumbers, attrib); - switch (privilege) - { - case "assignPrimaryToken": - privilege = "SeAssignPrimaryTokenPrivilege"; - break; - case "audit": - privilege = "SeAuditPrivilege"; - break; - case "backup": - privilege = "SeBackupPrivilege"; - break; - case "changeNotify": - privilege = "SeChangeNotifyPrivilege"; - break; - case "createGlobal": - privilege = "SeCreateGlobalPrivilege"; - break; - case "createPagefile": - privilege = "SeCreatePagefilePrivilege"; - break; - case "createPermanent": - privilege = "SeCreatePermanentPrivilege"; - break; - case "createSymbolicLink": - privilege = "SeCreateSymbolicLinkPrivilege"; - break; - case "createToken": - privilege = "SeCreateTokenPrivilege"; - break; - case "debug": - privilege = "SeDebugPrivilege"; - break; - case "enableDelegation": - privilege = "SeEnableDelegationPrivilege"; - break; - case "impersonate": - privilege = "SeImpersonatePrivilege"; - break; - case "increaseBasePriority": - privilege = "SeIncreaseBasePriorityPrivilege"; - break; - case "increaseQuota": - privilege = "SeIncreaseQuotaPrivilege"; - break; - case "increaseWorkingSet": - privilege = "SeIncreaseWorkingSetPrivilege"; - break; - case "loadDriver": - privilege = "SeLoadDriverPrivilege"; - break; - case "lockMemory": - privilege = "SeLockMemoryPrivilege"; - break; - case "machineAccount": - privilege = "SeMachineAccountPrivilege"; - break; - case "manageVolume": - privilege = "SeManageVolumePrivilege"; - break; - case "profileSingleProcess": - privilege = "SeProfileSingleProcessPrivilege"; - break; - case "relabel": - privilege = "SeRelabelPrivilege"; - break; - case "remoteShutdown": - privilege = "SeRemoteShutdownPrivilege"; - break; - case "restore": - privilege = "SeRestorePrivilege"; - break; - case "security": - privilege = "SeSecurityPrivilege"; - break; - case "shutdown": - privilege = "SeShutdownPrivilege"; - break; - case "syncAgent": - privilege = "SeSyncAgentPrivilege"; - break; - case "systemEnvironment": - privilege = "SeSystemEnvironmentPrivilege"; - break; - case "systemProfile": - privilege = "SeSystemProfilePrivilege"; - break; - case "systemTime": - case "modifySystemTime": - privilege = "SeSystemtimePrivilege"; - break; - case "takeOwnership": - privilege = "SeTakeOwnershipPrivilege"; - break; - case "tcb": - case "trustedComputerBase": - privilege = "SeTcbPrivilege"; - break; - case "timeZone": - case "modifyTimeZone": - privilege = "SeTimeZonePrivilege"; - break; - case "trustedCredManAccess": - case "trustedCredentialManagerAccess": - privilege = "SeTrustedCredManAccessPrivilege"; - break; - case "undock": - privilege = "SeUndockPrivilege"; - break; - case "unsolicitedInput": - privilege = "SeUnsolicitedInputPrivilege"; - break; - default: - // allow everything else to pass through that are hopefully "formatted" Properties. - break; - } - break; - default: - this.Core.UnexpectedAttribute(node, attrib); - break; - } - } - } - - this.Core.VerifyNoInnerText(sourceLineNumbers, node); - - if (privilege == null) - { - this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Name")); - } - - this.Core.ParseForExtensionElements(node); - - return (requiredPrivileges == null) ? privilege : String.Concat(requiredPrivileges, "[~]", privilege); - } - - /// - /// Parses a service config failure actions element. - /// - /// Element to parse. - /// Identifier of parent component. - /// Optional element containing parent's service name. - private void ParseServiceConfigFailureActionsElement(XElement node, string componentId, string serviceName) - { - var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node); - Identifier id = null; - var name = serviceName; - var install = false; - var reinstall = false; - var uninstall = false; - int? resetPeriod = null; - string rebootMessage = null; - string command = null; - string actions = null; - string actionsDelays = null; - - this.Core.Write(WarningMessages.ServiceConfigFamilyNotSupported(sourceLineNumbers, node.Name.LocalName)); - - foreach (var attrib in node.Attributes()) - { - if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace) - { - switch (attrib.Name.LocalName) - { - case "Id": - id = this.Core.GetAttributeIdentifier(sourceLineNumbers, attrib); - break; - case "Command": - command = this.Core.GetAttributeValue(sourceLineNumbers, attrib, EmptyRule.CanBeEmpty); - break; - case "OnInstall": - install = YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib); - break; - case "OnReinstall": - reinstall = YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib); - break; - case "OnUninstall": - uninstall = YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib); - break; - case "RebootMessage": - rebootMessage = this.Core.GetAttributeValue(sourceLineNumbers, attrib, EmptyRule.CanBeEmpty); - break; - case "ResetPeriod": - resetPeriod = this.Core.GetAttributeIntegerValue(sourceLineNumbers, attrib, 0, Int32.MaxValue); - break; - case "ServiceName": - if (!String.IsNullOrEmpty(serviceName)) - { - this.Core.Write(ErrorMessages.IllegalAttributeWhenNested(sourceLineNumbers, node.Name.LocalName, attrib.Name.LocalName, "ServiceInstall")); - } - - name = this.Core.GetAttributeValue(sourceLineNumbers, attrib); - break; - default: - this.Core.UnexpectedAttribute(node, attrib); - break; - } - } - else - { - this.Core.ParseExtensionAttribute(node, attrib); - } - } - - // Get the ServiceConfigFailureActions actions. - foreach (var child in node.Elements()) - { - if (CompilerCore.WixNamespace == child.Name.Namespace) - { - switch (child.Name.LocalName) - { - case "Failure": - string action = null; - string delay = null; - var childSourceLineNumbers = Preprocessor.GetSourceLineNumbers(child); - - foreach (var childAttrib in child.Attributes()) - { - if (String.IsNullOrEmpty(childAttrib.Name.NamespaceName) || CompilerCore.WixNamespace == childAttrib.Name.Namespace) - { - switch (childAttrib.Name.LocalName) - { - case "Action": - action = this.Core.GetAttributeValue(childSourceLineNumbers, childAttrib); - switch (action) - { - case "none": - action = "0"; - break; - case "restartComputer": - action = "2"; - break; - case "restartService": - action = "1"; - break; - case "runCommand": - action = "3"; - break; - default: - // allow everything else to pass through that are hopefully "formatted" Properties. - break; - } - break; - case "Delay": - delay = this.Core.GetAttributeValue(childSourceLineNumbers, childAttrib); - break; - default: - this.Core.UnexpectedAttribute(child, childAttrib); - break; - } - } - } - - if (String.IsNullOrEmpty(action)) - { - this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, child.Name.LocalName, "Action")); - } - - if (String.IsNullOrEmpty(delay)) - { - this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, child.Name.LocalName, "Delay")); - } - - if (!String.IsNullOrEmpty(actions)) - { - actions = String.Concat(actions, "[~]"); - } - actions = String.Concat(actions, action); - - if (!String.IsNullOrEmpty(actionsDelays)) - { - actionsDelays = String.Concat(actionsDelays, "[~]"); - } - actionsDelays = String.Concat(actionsDelays, delay); - break; - default: - this.Core.UnexpectedElement(node, child); - break; - } - } - else - { - this.Core.ParseExtensionElement(node, child); - } - } - - if (String.IsNullOrEmpty(name)) - { - this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "ServiceName")); - } - else if (null == id) - { - id = this.Core.CreateIdentifierFromFilename(name); - } - - if (!install && !reinstall && !uninstall) - { - this.Core.Write(ErrorMessages.ExpectedAttributes(sourceLineNumbers, node.Name.LocalName, "OnInstall", "OnReinstall", "OnUninstall")); - } - - if (!this.Core.EncounteredError) - { - this.Core.AddSymbol(new MsiServiceConfigFailureActionsSymbol(sourceLineNumbers, id) - { - Name = name, - OnInstall = install, - OnReinstall = reinstall, - OnUninstall = uninstall, - ResetPeriod = resetPeriod, - RebootMessage = rebootMessage, - Command = command, - Actions = actions, - DelayActions = actionsDelays, - ComponentRef = componentId, - }); - } - } - - /// - /// Parses a service control element. - /// - /// Element to parse. - /// Identifier of parent component. - private void ParseServiceControlElement(XElement node, string componentId) - { - var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node); - string arguments = null; - Identifier id = null; - string name = null; - var installRemove = false; - var uninstallRemove = false; - var installStart = false; - var uninstallStart = false; - var installStop = false; - var uninstallStop = false; - bool? wait = null; - - foreach (var attrib in node.Attributes()) - { - if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace) - { - switch (attrib.Name.LocalName) - { - case "Id": - id = this.Core.GetAttributeIdentifier(sourceLineNumbers, attrib); - break; - case "Name": - name = this.Core.GetAttributeValue(sourceLineNumbers, attrib); - break; - case "Remove": - var removeValue = this.Core.GetAttributeValue(sourceLineNumbers, attrib); - switch (removeValue) - { - case "install": - installRemove = true; - break; - case "uninstall": - uninstallRemove = true; - break; - case "both": - installRemove = true; - uninstallRemove = true; - break; - case "": - break; - } - break; - case "Start": - var startValue = this.Core.GetAttributeValue(sourceLineNumbers, attrib); - switch (startValue) - { - case "install": - installStart = true; - break; - case "uninstall": - uninstallStart = true; - break; - case "both": - installStart = true; - uninstallStart = true; - break; - case "": - break; - } - break; - case "Stop": - var stopValue = this.Core.GetAttributeValue(sourceLineNumbers, attrib); - switch (stopValue) - { - case "install": - installStop = true; - break; - case "uninstall": - uninstallStop = true; - break; - case "both": - installStop = true; - uninstallStop = true; - break; - case "": - break; - } - break; - case "Wait": - wait = YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib); - break; - default: - this.Core.UnexpectedAttribute(node, attrib); - break; - } - } - else - { - this.Core.ParseExtensionAttribute(node, attrib); - } - } - - if (null == id) - { - id = this.Core.CreateIdentifierFromFilename(name); - } - - if (null == name) - { - this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Name")); - } - - // get the ServiceControl arguments - foreach (var child in node.Elements()) - { - if (CompilerCore.WixNamespace == child.Name.Namespace) - { - switch (child.Name.LocalName) - { - case "ServiceArgument": - arguments = this.ParseServiceArgument(child, arguments); - break; - default: - this.Core.UnexpectedElement(node, child); - break; - } - } - else - { - this.Core.ParseExtensionElement(node, child); - } - } - - if (!this.Core.EncounteredError) - { - this.Core.AddSymbol(new ServiceControlSymbol(sourceLineNumbers, id) - { - Name = name, - InstallRemove = installRemove, - UninstallRemove = uninstallRemove, - InstallStart = installStart, - UninstallStart = uninstallStart, - InstallStop = installStop, - UninstallStop = uninstallStop, - Arguments = arguments, - Wait = wait, - ComponentRef = componentId - }); - } - } - - private string ParseServiceArgument(XElement node, string arguments) - { - var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node); - string argument = null; - - foreach (var attrib in node.Attributes()) - { - if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace) - { - switch (attrib.Name.LocalName) - { - case "Value": - argument = this.Core.GetAttributeValue(sourceLineNumbers, attrib); - break; - default: - this.Core.UnexpectedAttribute(node, attrib); - break; - } - } - } - - this.Core.VerifyNoInnerText(sourceLineNumbers, node); - - if (argument == null) - { - this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Value")); - } - - this.Core.ParseForExtensionElements(node); - - return (arguments == null) ? argument : String.Concat(arguments, "[~]", argument); - } - - /// - /// Parses a service dependency element. - /// - /// Element to parse. - /// Parsed sevice dependency name. - private string ParseServiceDependencyElement(XElement node) - { - var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node); - string dependency = null; - var group = false; - - foreach (var attrib in node.Attributes()) - { - if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace) - { - switch (attrib.Name.LocalName) - { - case "Id": - dependency = this.Core.GetAttributeValue(sourceLineNumbers, attrib); - break; - case "Group": - group = YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib); - break; - default: - this.Core.UnexpectedAttribute(node, attrib); - break; - } - } - else - { - this.Core.ParseExtensionAttribute(node, attrib); - } - } - - if (null == dependency) - { - this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Id")); - } - - this.Core.ParseForExtensionElements(node); - - return group ? String.Concat("+", dependency) : dependency; - } - - /// - /// Parses a service install element. - /// - /// Element to parse. - /// Identifier of parent component. - /// - private void ParseServiceInstallElement(XElement node, string componentId, bool win64Component) - { - var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node); - Identifier id = null; - string account = null; - string arguments = null; - string dependencies = null; - string description = null; - string displayName = null; - var eraseDescription = false; - string loadOrderGroup = null; - string name = null; - string password = null; - - var serviceType = ServiceType.OwnProcess; - var startType = ServiceStartType.Demand; - var errorControl = ServiceErrorControl.Normal; - var interactive = false; - var vital = false; - - foreach (var attrib in node.Attributes()) - { - if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace) - { - switch (attrib.Name.LocalName) - { - case "Id": - id = this.Core.GetAttributeIdentifier(sourceLineNumbers, attrib); - break; - case "Account": - account = this.Core.GetAttributeValue(sourceLineNumbers, attrib); - break; - case "Arguments": - arguments = this.Core.GetAttributeValue(sourceLineNumbers, attrib); - break; - case "Description": - description = this.Core.GetAttributeValue(sourceLineNumbers, attrib); - break; - case "DisplayName": - displayName = this.Core.GetAttributeValue(sourceLineNumbers, attrib); - break; - case "EraseDescription": - eraseDescription = YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib); - break; - case "ErrorControl": - var errorControlValue = this.Core.GetAttributeValue(sourceLineNumbers, attrib); - switch (errorControlValue) - { - case "ignore": - errorControl = ServiceErrorControl.Ignore; - break; - case "normal": - errorControl = ServiceErrorControl.Normal; - break; - case "critical": - errorControl = ServiceErrorControl.Critical; - break; - case "": // error case handled by GetAttributeValue() - break; - default: - this.Core.Write(ErrorMessages.IllegalAttributeValue(sourceLineNumbers, node.Name.LocalName, attrib.Name.LocalName, errorControlValue, "ignore", "normal", "critical")); - break; - } - break; - case "Interactive": - interactive = YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib); - break; - case "LoadOrderGroup": - loadOrderGroup = this.Core.GetAttributeValue(sourceLineNumbers, attrib); - break; - case "Name": - name = this.Core.GetAttributeValue(sourceLineNumbers, attrib); - break; - case "Password": - password = this.Core.GetAttributeValue(sourceLineNumbers, attrib); - break; - case "Start": - var startValue = this.Core.GetAttributeValue(sourceLineNumbers, attrib); - switch (startValue) - { - case "auto": - startType = ServiceStartType.Auto; - break; - case "demand": - startType = ServiceStartType.Demand; - break; - case "disabled": - startType = ServiceStartType.Disabled; - break; - case "boot": - case "system": - this.Core.Write(ErrorMessages.ValueNotSupported(sourceLineNumbers, node.Name.LocalName, attrib.Name.LocalName, startValue)); - break; - case "": - break; - default: - this.Core.Write(ErrorMessages.IllegalAttributeValue(sourceLineNumbers, node.Name.LocalName, attrib.Name.LocalName, startValue, "auto", "demand", "disabled")); - break; - } - break; - case "Type": - var typeValue = this.Core.GetAttributeValue(sourceLineNumbers, attrib); - switch (typeValue) - { - case "ownProcess": - serviceType = ServiceType.OwnProcess; - break; - case "shareProcess": - serviceType = ServiceType.ShareProcess; - break; - case "kernelDriver": - case "systemDriver": - this.Core.Write(ErrorMessages.ValueNotSupported(sourceLineNumbers, node.Name.LocalName, attrib.Name.LocalName, typeValue)); - break; - case "": - break; - default: - this.Core.Write(ErrorMessages.IllegalAttributeValue(sourceLineNumbers, node.Name.LocalName, node.Name.LocalName, typeValue, "ownProcess", "shareProcess")); - break; - } - break; - case "Vital": - vital = YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib); - break; - default: - this.Core.UnexpectedAttribute(node, attrib); - break; - } - } - else - { - this.Core.ParseExtensionAttribute(node, attrib); - } - } - - if (String.IsNullOrEmpty(name)) - { - this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Name")); - } - else if (null == id) - { - id = this.Core.CreateIdentifierFromFilename(name); - } - - if (0 == startType) - { - this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Start")); - } - - if (eraseDescription) - { - description = "[~]"; - } - - // get the ServiceInstall dependencies and config - foreach (var child in node.Elements()) - { - if (CompilerCore.WixNamespace == child.Name.Namespace) - { - switch (child.Name.LocalName) - { - case "PermissionEx": - this.ParsePermissionExElement(child, id.Id, "ServiceInstall"); - break; - case "ServiceConfig": - this.ParseServiceConfigElement(child, componentId, name); - break; - case "ServiceConfigFailureActions": - this.ParseServiceConfigFailureActionsElement(child, componentId, name); - break; - case "ServiceDependency": - dependencies = String.Concat(dependencies, this.ParseServiceDependencyElement(child), "[~]"); - break; - default: - this.Core.UnexpectedElement(node, child); - break; - } - } - else - { - var context = new Dictionary() { { "ServiceInstallId", id?.Id }, { "ServiceInstallName", name }, { "ServiceInstallComponentId", componentId }, { "Win64", win64Component.ToString() } }; - this.Core.ParseExtensionElement(node, child, context); - } - } - - if (null != dependencies) - { - dependencies = String.Concat(dependencies, "[~]"); - } - - if (!this.Core.EncounteredError) - { - this.Core.AddSymbol(new ServiceInstallSymbol(sourceLineNumbers, id) - { - Name = name, - DisplayName = displayName, - ServiceType = serviceType, - StartType = startType, - ErrorControl = errorControl, - LoadOrderGroup = loadOrderGroup, - Dependencies = dependencies, - StartName = account, - Password = password, - Arguments = arguments, - ComponentRef = componentId, - Description = description, - Interactive = interactive, - Vital = vital - }); - } - } - - /// - /// Parses a SetDirectory element. - /// - /// Element to parse. - private void ParseSetDirectoryElement(XElement node) - { - var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node); - string actionName = null; - string id = null; - string condition = null; - var executionType = CustomActionExecutionType.Immediate; - var sequences = new[] { SequenceTable.InstallUISequence, SequenceTable.InstallExecuteSequence }; // default to "both" - string value = null; - - foreach (var attrib in node.Attributes()) - { - if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace) - { - switch (attrib.Name.LocalName) - { - case "Action": - actionName = this.Core.GetAttributeIdentifierValue(sourceLineNumbers, attrib); - break; - case "Condition": - condition = this.Core.GetAttributeValue(sourceLineNumbers, attrib); - break; - case "Id": - id = this.Core.GetAttributeIdentifierValue(sourceLineNumbers, attrib); - this.Core.CreateSimpleReference(sourceLineNumbers, SymbolDefinitions.Directory, id); - break; - case "Sequence": - var sequenceValue = this.Core.GetAttributeValue(sourceLineNumbers, attrib); - switch (sequenceValue) - { - case "execute": - sequences = new[] { SequenceTable.InstallExecuteSequence }; - break; - case "first": - executionType = CustomActionExecutionType.FirstSequence; - break; - case "ui": - sequences = new[] { SequenceTable.InstallUISequence }; - break; - case "both": - break; - case "": - break; - default: - this.Core.Write(ErrorMessages.IllegalAttributeValue(sourceLineNumbers, node.Name.LocalName, attrib.Name.LocalName, sequenceValue, "execute", "ui", "both")); - break; - } - break; - case "Value": - value = this.Core.GetAttributeValue(sourceLineNumbers, attrib); - break; - default: - this.Core.UnexpectedAttribute(node, attrib); - break; - } - } - else - { - this.Core.ParseExtensionAttribute(node, attrib); - } - } - - this.Core.VerifyNoInnerText(sourceLineNumbers, node); - - if (null == id) - { - this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Id")); - } - else if (String.IsNullOrEmpty(actionName)) - { - actionName = String.Concat("Set", id); - } - - if (null == value) - { - this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Value")); - } - - this.Core.ParseForExtensionElements(node); - - if (!this.Core.EncounteredError) - { - this.Core.AddSymbol(new CustomActionSymbol(sourceLineNumbers, new Identifier(AccessModifier.Public, actionName)) - { - ExecutionType = executionType, - SourceType = CustomActionSourceType.Directory, - TargetType = CustomActionTargetType.TextData, - Source = id, - Target = value - }); - - foreach (var sequence in sequences) - { - this.Core.ScheduleActionSymbol(sourceLineNumbers, AccessModifier.Public, sequence, actionName, condition, afterAction: "CostInitialize"); - } - } - } - - /// - /// Parses a SetProperty element. - /// - /// Element to parse. - private void ParseSetPropertyElement(XElement node) - { - var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node); - string actionName = null; - string id = null; - string condition = null; - string afterAction = null; - string beforeAction = null; - var executionType = CustomActionExecutionType.Immediate; - var sequences = new[] { SequenceTable.InstallUISequence, SequenceTable.InstallExecuteSequence }; // default to "both" - string value = null; - - foreach (var attrib in node.Attributes()) - { - if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace) - { - switch (attrib.Name.LocalName) - { - case "Action": - actionName = this.Core.GetAttributeIdentifierValue(sourceLineNumbers, attrib); - break; - case "Id": - id = this.Core.GetAttributeIdentifierValue(sourceLineNumbers, attrib); - break; - case "Condition": - condition = this.Core.GetAttributeValue(sourceLineNumbers, attrib); - break; - case "After": - afterAction = this.Core.GetAttributeIdentifierValue(sourceLineNumbers, attrib); - break; - case "Before": - beforeAction = this.Core.GetAttributeIdentifierValue(sourceLineNumbers, attrib); - break; - case "Sequence": - var sequenceValue = this.Core.GetAttributeValue(sourceLineNumbers, attrib); - switch (sequenceValue) - { - case "execute": - sequences = new[] { SequenceTable.InstallExecuteSequence }; - break; - case "first": - executionType = CustomActionExecutionType.FirstSequence; - break; - case "ui": - sequences = new[] { SequenceTable.InstallUISequence }; - break; - case "both": - break; - case "": - break; - default: - this.Core.Write(ErrorMessages.IllegalAttributeValue(sourceLineNumbers, node.Name.LocalName, attrib.Name.LocalName, sequenceValue, "execute", "ui", "both")); - break; - } - break; - case "Value": - value = this.Core.GetAttributeValue(sourceLineNumbers, attrib, EmptyRule.CanBeEmpty); - break; - default: - this.Core.UnexpectedAttribute(node, attrib); - break; - } - } - else - { - this.Core.ParseExtensionAttribute(node, attrib); - } - } - - this.Core.VerifyNoInnerText(sourceLineNumbers, node); - - if (null == id) - { - this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Id")); - } - else if (String.IsNullOrEmpty(actionName)) - { - actionName = String.Concat("Set", id); - } - - if (null == value) - { - this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Value")); - } - - if (null != beforeAction && null != afterAction) - { - this.Core.Write(ErrorMessages.IllegalAttributeWithOtherAttribute(sourceLineNumbers, node.Name.LocalName, "After", "Before")); - } - else if (null == beforeAction && null == afterAction) - { - this.Core.Write(ErrorMessages.ExpectedAttributesWithOtherAttribute(sourceLineNumbers, node.Name.LocalName, "After", "Before", "Id")); - } - - this.Core.ParseForExtensionElements(node); - - // add the row and any references needed - if (!this.Core.EncounteredError) - { - // action that is scheduled to occur before/after itself - if (beforeAction == actionName) - { - this.Core.Write(ErrorMessages.ActionScheduledRelativeToItself(sourceLineNumbers, node.Name.LocalName, "Before", beforeAction)); - } - else if (afterAction == actionName) - { - this.Core.Write(ErrorMessages.ActionScheduledRelativeToItself(sourceLineNumbers, node.Name.LocalName, "After", afterAction)); - } - - this.Core.AddSymbol(new CustomActionSymbol(sourceLineNumbers, new Identifier(AccessModifier.Public, actionName)) - { - ExecutionType = executionType, - SourceType = CustomActionSourceType.Property, - TargetType = CustomActionTargetType.TextData, - Source = id, - Target = value, - }); - - foreach (var sequence in sequences) - { - this.Core.ScheduleActionSymbol(sourceLineNumbers, AccessModifier.Public, sequence, actionName, condition, beforeAction, afterAction); - } - } - } - - /// - /// Parses a SFP catalog element. - /// - /// Element to parse. - /// Parent SFPCatalog. - private void ParseSFPFileElement(XElement node, string parentSFPCatalog) - { - var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node); - string id = null; - - foreach (var attrib in node.Attributes()) - { - if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace) - { - switch (attrib.Name.LocalName) - { - case "Id": - id = this.Core.GetAttributeIdentifierValue(sourceLineNumbers, attrib); - break; - default: - this.Core.UnexpectedAttribute(node, attrib); - break; - } - } - else - { - this.Core.ParseExtensionAttribute(node, attrib); - } - } - - if (null == id) - { - this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Id")); - } - - this.Core.ParseForExtensionElements(node); - - if (!this.Core.EncounteredError) - { - this.Core.AddSymbol(new FileSFPCatalogSymbol(sourceLineNumbers) - { - FileRef = id, - SFPCatalogRef = parentSFPCatalog - }); - } - } - - /// - /// Parses a SFP catalog element. - /// - /// Element to parse. - /// Parent SFPCatalog. - private void ParseSFPCatalogElement(XElement node, ref string parentSFPCatalog) - { - var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node); - string parentName = null; - string dependency = null; - string name = null; - string sourceFile = null; - - foreach (var attrib in node.Attributes()) - { - if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace) - { - switch (attrib.Name.LocalName) - { - case "Dependency": - dependency = this.Core.GetAttributeValue(sourceLineNumbers, attrib); - break; - case "Name": - name = this.Core.GetAttributeShortFilename(sourceLineNumbers, attrib, false); - parentSFPCatalog = name; - break; - case "SourceFile": - sourceFile = this.Core.GetAttributeValue(sourceLineNumbers, attrib); - break; - default: - this.Core.UnexpectedAttribute(node, attrib); - break; - } - } - else - { - this.Core.ParseExtensionAttribute(node, attrib); - } - } - - if (null == name) - { - this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Name")); - } - - if (null == sourceFile) - { - this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "SourceFile")); - } - - foreach (var child in node.Elements()) - { - if (CompilerCore.WixNamespace == child.Name.Namespace) - { - switch (child.Name.LocalName) - { - case "SFPCatalog": - this.ParseSFPCatalogElement(child, ref parentName); - if (null != dependency && parentName == dependency) - { - this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Dependency")); - } - dependency = parentName; - break; - case "SFPFile": - this.ParseSFPFileElement(child, name); - break; - default: - this.Core.UnexpectedElement(node, child); - break; - } - } - else - { - this.Core.ParseExtensionElement(node, child); - } - } - - if (null == dependency) - { - this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Dependency")); - } - - if (!this.Core.EncounteredError) - { - this.Core.AddSymbol(new SFPCatalogSymbol(sourceLineNumbers) - { - SFPCatalog = name, - Catalog = sourceFile, - Dependency = dependency - }); - } - } - - /// - /// Parses a shortcut element. - /// - /// Element to parse. - /// Identifer for parent component. - /// Local name of parent element. - /// Default identifier of parent (which is usually the target). - /// Flag to indicate whether the parent element is the keypath of a component or not (will only be true for file parent elements). - private void ParseShortcutElement(XElement node, string componentId, string parentElementLocalName, string defaultTarget, YesNoType parentKeyPath) - { - var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node); - Identifier id = null; - var advertise = false; - string arguments = null; - string description = null; - string descriptionResourceDll = null; - int? descriptionResourceId = null; - string directory = null; - string displayResourceDll = null; - int? displayResourceId = null; - int? hotkey = null; - string icon = null; - int? iconIndex = null; - string name = null; - string shortName = null; - ShortcutShowType? show = null; - string target = null; - string workingDirectory = null; - - foreach (var attrib in node.Attributes()) - { - if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace) - { - switch (attrib.Name.LocalName) - { - case "Id": - id = this.Core.GetAttributeIdentifier(sourceLineNumbers, attrib); - break; - case "Advertise": - advertise = YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib); - break; - case "Arguments": - arguments = this.Core.GetAttributeValue(sourceLineNumbers, attrib); - break; - case "Description": - description = this.Core.GetAttributeValue(sourceLineNumbers, attrib); - break; - case "DescriptionResourceDll": - descriptionResourceDll = this.Core.GetAttributeValue(sourceLineNumbers, attrib); - break; - case "DescriptionResourceId": - descriptionResourceId = this.Core.GetAttributeIntegerValue(sourceLineNumbers, attrib, 0, Int16.MaxValue); - break; - case "Directory": - directory = this.Core.CreateDirectoryReferenceFromInlineSyntax(sourceLineNumbers, attrib, null); - break; - case "DisplayResourceDll": - displayResourceDll = this.Core.GetAttributeValue(sourceLineNumbers, attrib); - break; - case "DisplayResourceId": - displayResourceId = this.Core.GetAttributeIntegerValue(sourceLineNumbers, attrib, 0, Int16.MaxValue); - break; - case "Hotkey": - hotkey = this.Core.GetAttributeIntegerValue(sourceLineNumbers, attrib, 0, Int16.MaxValue); - break; - case "Icon": - icon = this.Core.GetAttributeIdentifierValue(sourceLineNumbers, attrib); - this.Core.CreateSimpleReference(sourceLineNumbers, SymbolDefinitions.Icon, icon); - break; - case "IconIndex": - iconIndex = this.Core.GetAttributeIntegerValue(sourceLineNumbers, attrib, Int16.MinValue + 1, Int16.MaxValue); - break; - case "Name": - name = this.Core.GetAttributeLongFilename(sourceLineNumbers, attrib, false); - break; - case "ShortName": - shortName = this.Core.GetAttributeShortFilename(sourceLineNumbers, attrib, false); - break; - case "Show": - var showValue = this.Core.GetAttributeValue(sourceLineNumbers, attrib); - switch (showValue) - { - case "normal": - show = ShortcutShowType.Normal; - break; - case "maximized": - show = ShortcutShowType.Maximized; - break; - case "minimized": - show = ShortcutShowType.Minimized; - break; - case "": - break; - default: - this.Core.Write(ErrorMessages.IllegalAttributeValue(sourceLineNumbers, node.Name.LocalName, "Show", showValue, "normal", "maximized", "minimized")); - break; - } - break; - case "Target": - target = this.Core.GetAttributeValue(sourceLineNumbers, attrib); - break; - case "WorkingDirectory": - workingDirectory = this.Core.GetAttributeIdentifierValue(sourceLineNumbers, attrib); - break; - default: - this.Core.UnexpectedAttribute(node, attrib); - break; - } - } - else - { - this.Core.ParseExtensionAttribute(node, attrib); - } - } - - if (advertise && null != target) - { - this.Core.Write(ErrorMessages.IllegalAttributeWithOtherAttribute(sourceLineNumbers, node.Name.LocalName, "Target", "Advertise", "yes")); - } - - if (null == directory) - { - if ("Component" == parentElementLocalName) - { - directory = defaultTarget; - } - else - { - this.Core.Write(ErrorMessages.ExpectedAttributeWhenElementNotUnderElement(sourceLineNumbers, node.Name.LocalName, "Directory", "Component")); - } - } - - if (null != descriptionResourceDll) - { - if (!descriptionResourceId.HasValue) - { - this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "DescriptionResourceDll", "DescriptionResourceId")); - } - } - else - { - if (descriptionResourceId.HasValue) - { - this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "DescriptionResourceId", "DescriptionResourceDll")); - } - } - - if (null != displayResourceDll) - { - if (!displayResourceId.HasValue) - { - this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "DisplayResourceDll", "DisplayResourceId")); - } - } - else - { - if (displayResourceId.HasValue) - { - this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "DisplayResourceId", "DisplayResourceDll")); - } - } - - if (null == name) - { - this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Name")); - } - - if ("Component" != parentElementLocalName && null != target) - { - this.Core.Write(ErrorMessages.IllegalAttributeWhenNested(sourceLineNumbers, node.Name.LocalName, "Target", parentElementLocalName)); - } - - if (null == id) - { - id = this.Core.CreateIdentifier("sct", directory, LowercaseOrNull(name)); - } - - foreach (var child in node.Elements()) - { - if (CompilerCore.WixNamespace == child.Name.Namespace) - { - switch (child.Name.LocalName) - { - case "Icon": - icon = this.ParseIconElement(child); - break; - case "ShortcutProperty": - this.ParseShortcutPropertyElement(child, id.Id); - break; - default: - this.Core.UnexpectedElement(node, child); - break; - } - } - else - { - this.Core.ParseExtensionElement(node, child); - } - } - - if (!this.Core.EncounteredError) - { - if (advertise) - { - if (YesNoType.Yes != parentKeyPath && "Component" != parentElementLocalName) - { - this.Core.Write(WarningMessages.UnclearShortcut(sourceLineNumbers, id.Id, componentId, defaultTarget)); - } - - target = Guid.Empty.ToString("B"); - } - else if (null != target) - { - } - else if ("Component" == parentElementLocalName || "CreateFolder" == parentElementLocalName) - { - target = "[" + defaultTarget + "]"; - } - else if ("File" == parentElementLocalName) - { - target = "[#" + defaultTarget + "]"; - } - - this.Core.AddSymbol(new ShortcutSymbol(sourceLineNumbers, id) - { - DirectoryRef = directory, - Name = name, - ShortName = shortName, - ComponentRef = componentId, - Target = target, - Arguments = arguments, - Description = description, - Hotkey = hotkey, - IconRef = icon, - IconIndex = iconIndex, - Show = show, - WorkingDirectory = workingDirectory, - DisplayResourceDll = displayResourceDll, - DisplayResourceId = displayResourceId, - DescriptionResourceDll = descriptionResourceDll, - DescriptionResourceId = descriptionResourceId, - }); - } - } - - /// - /// Parses a shortcut property element. - /// - /// Element to parse. - /// - private void ParseShortcutPropertyElement(XElement node, string shortcutId) - { - var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node); - Identifier id = null; - string key = null; - string value = null; - - foreach (var attrib in node.Attributes()) - { - if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace) - { - switch (attrib.Name.LocalName) - { - case "Id": - id = this.Core.GetAttributeIdentifier(sourceLineNumbers, attrib); - break; - case "Key": - key = this.Core.GetAttributeValue(sourceLineNumbers, attrib); - break; - case "Value": - value = this.Core.GetAttributeValue(sourceLineNumbers, attrib); - break; - default: - this.Core.UnexpectedAttribute(node, attrib); - break; - } - } - else - { - this.Core.ParseExtensionAttribute(node, attrib); - } - } - - this.Core.VerifyNoInnerText(sourceLineNumbers, node); - - if (String.IsNullOrEmpty(key)) - { - this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Key")); - } - else if (null == id) - { - id = this.Core.CreateIdentifier("scp", shortcutId, key.ToUpperInvariant()); - } - - if (String.IsNullOrEmpty(value)) - { - this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Value")); - } - - this.Core.ParseForExtensionElements(node); - - if (!this.Core.EncounteredError) - { - this.Core.AddSymbol(new MsiShortcutPropertySymbol(sourceLineNumbers, id) - { - ShortcutRef = shortcutId, - PropertyKey = key, - PropVariantValue = value - }); - } - } - - /// - /// Parses a typelib element. - /// - /// Element to parse. - /// Identifier of parent component. - /// Identifier of file that acts as typelib server. - /// true if the component is 64-bit. - private void ParseTypeLibElement(XElement node, string componentId, string fileServer, bool win64Component) - { - var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node); - string id = null; - var advertise = YesNoType.NotSet; - var cost = CompilerConstants.IntegerNotSet; - string description = null; - var flags = 0; - string helpDirectory = null; - var language = CompilerConstants.IntegerNotSet; - var majorVersion = CompilerConstants.IntegerNotSet; - var minorVersion = CompilerConstants.IntegerNotSet; - var resourceId = CompilerConstants.LongNotSet; - - foreach (var attrib in node.Attributes()) - { - if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace) - { - switch (attrib.Name.LocalName) - { - case "Id": - id = this.Core.GetAttributeGuidValue(sourceLineNumbers, attrib, false); - break; - case "Advertise": - advertise = this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib); - break; - case "Control": - if (YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib)) - { - flags |= 2; - } - break; - case "Cost": - cost = this.Core.GetAttributeIntegerValue(sourceLineNumbers, attrib, 0, Int32.MaxValue); - break; - case "Description": - description = this.Core.GetAttributeValue(sourceLineNumbers, attrib); - break; - case "HasDiskImage": - if (YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib)) - { - flags |= 8; - } - break; - case "HelpDirectory": - helpDirectory = this.Core.CreateDirectoryReferenceFromInlineSyntax(sourceLineNumbers, attrib, null); - break; - case "Hidden": - if (YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib)) - { - flags |= 4; - } - break; - case "Language": - language = this.Core.GetAttributeIntegerValue(sourceLineNumbers, attrib, 0, Int16.MaxValue); - break; - case "MajorVersion": - majorVersion = this.Core.GetAttributeIntegerValue(sourceLineNumbers, attrib, 0, UInt16.MaxValue); - break; - case "MinorVersion": - minorVersion = this.Core.GetAttributeIntegerValue(sourceLineNumbers, attrib, 0, Byte.MaxValue); - break; - case "ResourceId": - resourceId = this.Core.GetAttributeLongValue(sourceLineNumbers, attrib, Int32.MinValue, Int32.MaxValue); - break; - case "Restricted": - if (YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib)) - { - flags |= 1; - } - break; - default: - this.Core.UnexpectedAttribute(node, attrib); - break; - } - } - else - { - this.Core.ParseExtensionAttribute(node, attrib); - } - } - - if (null == id) - { - this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Id")); - } - - if (CompilerConstants.IntegerNotSet == language) - { - this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Language")); - language = CompilerConstants.IllegalInteger; - } - - // build up the typelib version string for the registry if the major or minor version was specified - string registryVersion = null; - if (CompilerConstants.IntegerNotSet != majorVersion || CompilerConstants.IntegerNotSet != minorVersion) - { - if (CompilerConstants.IntegerNotSet != majorVersion) - { - registryVersion = majorVersion.ToString("x", CultureInfo.InvariantCulture.NumberFormat); - } - else - { - registryVersion = "0"; - } - - if (CompilerConstants.IntegerNotSet != minorVersion) - { - registryVersion = String.Concat(registryVersion, ".", minorVersion.ToString("x", CultureInfo.InvariantCulture.NumberFormat)); - } - else - { - registryVersion = String.Concat(registryVersion, ".0"); - } - } - - // if the advertise state has not been set, default to non-advertised - if (YesNoType.NotSet == advertise) - { - advertise = YesNoType.No; - } - - foreach (var child in node.Elements()) - { - if (CompilerCore.WixNamespace == child.Name.Namespace) - { - switch (child.Name.LocalName) - { - case "AppId": - this.ParseAppIdElement(child, componentId, YesNoType.NotSet, fileServer, id, registryVersion); - break; - case "Class": - this.ParseClassElement(child, componentId, YesNoType.NotSet, fileServer, id, registryVersion, null); - break; - case "Interface": - this.ParseInterfaceElement(child, componentId, null, null, id, registryVersion); - break; - default: - this.Core.UnexpectedElement(node, child); - break; - } - } - else - { - this.Core.ParseExtensionElement(node, child); - } - } - - - if (YesNoType.Yes == advertise) - { - if (CompilerConstants.LongNotSet != resourceId) - { - this.Core.Write(ErrorMessages.IllegalAttributeWhenAdvertised(sourceLineNumbers, node.Name.LocalName, "ResourceId")); - } - - if (0 != flags) - { - if (0x1 == (flags & 0x1)) - { - this.Core.Write(ErrorMessages.IllegalAttributeWithOtherAttribute(sourceLineNumbers, node.Name.LocalName, "Restricted", "Advertise", "yes")); - } - - if (0x2 == (flags & 0x2)) - { - this.Core.Write(ErrorMessages.IllegalAttributeWithOtherAttribute(sourceLineNumbers, node.Name.LocalName, "Control", "Advertise", "yes")); - } - - if (0x4 == (flags & 0x4)) - { - this.Core.Write(ErrorMessages.IllegalAttributeWithOtherAttribute(sourceLineNumbers, node.Name.LocalName, "Hidden", "Advertise", "yes")); - } - - if (0x8 == (flags & 0x8)) - { - this.Core.Write(ErrorMessages.IllegalAttributeWithOtherAttribute(sourceLineNumbers, node.Name.LocalName, "HasDiskImage", "Advertise", "yes")); - } - } - - if (!this.Core.EncounteredError) - { - var symbol = this.Core.AddSymbol(new TypeLibSymbol(sourceLineNumbers) - { - LibId = id, - Language = language, - ComponentRef = componentId, - Description = description, - DirectoryRef = helpDirectory, - FeatureRef = Guid.Empty.ToString("B") - }); - - if (CompilerConstants.IntegerNotSet != majorVersion || CompilerConstants.IntegerNotSet != minorVersion) - { - symbol.Version = (CompilerConstants.IntegerNotSet != majorVersion ? majorVersion * 256 : 0) + (CompilerConstants.IntegerNotSet != minorVersion ? minorVersion : 0); - } - - if (CompilerConstants.IntegerNotSet != cost) - { - symbol.Cost = cost; - } - } - } - else if (YesNoType.No == advertise) - { - if (CompilerConstants.IntegerNotSet != cost && CompilerConstants.IllegalInteger != cost) - { - this.Core.Write(ErrorMessages.IllegalAttributeWithOtherAttribute(sourceLineNumbers, node.Name.LocalName, "Cost", "Advertise", "no")); - } - - if (null == fileServer) - { - this.Core.Write(ErrorMessages.MissingTypeLibFile(sourceLineNumbers, node.Name.LocalName, "File")); - } - - if (null == registryVersion) - { - this.Core.Write(ErrorMessages.ExpectedAttributesWithOtherAttribute(sourceLineNumbers, node.Name.LocalName, "MajorVersion", "MinorVersion", "Advertise", "no")); - } - - // HKCR\TypeLib\[ID]\[MajorVersion].[MinorVersion], (Default) = [Description] - this.Core.CreateRegistryRow(sourceLineNumbers, RegistryRootType.ClassesRoot, String.Format(CultureInfo.InvariantCulture, @"TypeLib\{0}\{1}", id, registryVersion), null, description, componentId); - - // HKCR\TypeLib\[ID]\[MajorVersion].[MinorVersion]\[Language]\[win16|win32|win64], (Default) = [TypeLibPath]\[ResourceId] - var path = String.Concat("[#", fileServer, "]"); - if (CompilerConstants.LongNotSet != resourceId) - { - path = String.Concat(path, Path.DirectorySeparatorChar, resourceId.ToString(CultureInfo.InvariantCulture.NumberFormat)); - } - this.Core.CreateRegistryRow(sourceLineNumbers, RegistryRootType.ClassesRoot, String.Format(CultureInfo.InvariantCulture, @"TypeLib\{0}\{1}\{2}\{3}", id, registryVersion, language, (win64Component ? "win64" : "win32")), null, path, componentId); - - // HKCR\TypeLib\[ID]\[MajorVersion].[MinorVersion]\FLAGS, (Default) = [TypeLibFlags] - this.Core.CreateRegistryRow(sourceLineNumbers, RegistryRootType.ClassesRoot, String.Format(CultureInfo.InvariantCulture, @"TypeLib\{0}\{1}\FLAGS", id, registryVersion), null, flags.ToString(CultureInfo.InvariantCulture.NumberFormat), componentId); - - if (null != helpDirectory) - { - // HKCR\TypeLib\[ID]\[MajorVersion].[MinorVersion]\HELPDIR, (Default) = [HelpDirectory] - this.Core.CreateRegistryRow(sourceLineNumbers, RegistryRootType.ClassesRoot, String.Format(CultureInfo.InvariantCulture, @"TypeLib\{0}\{1}\HELPDIR", id, registryVersion), null, String.Concat("[", helpDirectory, "]"), componentId); - } - } - } - - /// - /// Parses an upgrade element. - /// - /// Element to parse. - private void ParseUpgradeElement(XElement node) - { - var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node); - string id = null; - - foreach (var attrib in node.Attributes()) - { - if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace) - { - switch (attrib.Name.LocalName) - { - case "Id": - id = this.Core.GetAttributeGuidValue(sourceLineNumbers, attrib, false); - break; - default: - this.Core.UnexpectedAttribute(node, attrib); - break; - } - } - else - { - this.Core.ParseExtensionAttribute(node, attrib); - } - } - - if (null == id) - { - this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Id")); - } - - // process the UpgradeVersion children here - foreach (var child in node.Elements()) - { - if (CompilerCore.WixNamespace == child.Name.Namespace) - { - var childSourceLineNumbers = Preprocessor.GetSourceLineNumbers(child); - - switch (child.Name.LocalName) - { - case "Property": - this.ParsePropertyElement(child); - this.Core.Write(WarningMessages.DeprecatedUpgradeProperty(childSourceLineNumbers)); - break; - case "UpgradeVersion": - this.ParseUpgradeVersionElement(child, id); - break; - default: - this.Core.UnexpectedElement(node, child); - break; - } - } - else - { - this.Core.ParseExtensionElement(node, child); - } - } - - // No rows created here. All row creation is done in ParseUpgradeVersionElement. - } - - /// - /// Parse upgrade version element. - /// - /// Element to parse. - /// Upgrade code. - private void ParseUpgradeVersionElement(XElement node, string upgradeId) - { - var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node); - - string actionProperty = null; - string language = null; - string maximum = null; - string minimum = null; - var excludeLanguages = false; - var ignoreFailures = false; - var includeMax = false; - var includeMin = true; - var migrateFeatures = false; - var onlyDetect = false; - string removeFeatures = null; - - foreach (var attrib in node.Attributes()) - { - if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace) - { - switch (attrib.Name.LocalName) - { - case "ExcludeLanguages": - excludeLanguages = YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib); - break; - case "IgnoreRemoveFailure": - ignoreFailures = YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib); - break; - case "IncludeMaximum": - includeMax = YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib); - break; - case "IncludeMinimum": // this is "yes" by default - includeMin = YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib); - break; - case "Language": - language = this.Core.GetAttributeValue(sourceLineNumbers, attrib); - break; - case "Minimum": - minimum = this.Core.GetAttributeVersionValue(sourceLineNumbers, attrib); - break; - case "Maximum": - maximum = this.Core.GetAttributeVersionValue(sourceLineNumbers, attrib); - break; - case "MigrateFeatures": - migrateFeatures = YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib); - break; - case "OnlyDetect": - onlyDetect = YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib); - break; - case "Property": - actionProperty = this.Core.GetAttributeIdentifierValue(sourceLineNumbers, attrib); - break; - case "RemoveFeatures": - removeFeatures = this.Core.GetAttributeValue(sourceLineNumbers, attrib); - break; - default: - this.Core.UnexpectedAttribute(node, attrib); - break; - } - } - else - { - this.Core.ParseExtensionAttribute(node, attrib); - } - } - - if (null == actionProperty) - { - this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Property")); - } - else if (actionProperty.ToUpper(CultureInfo.InvariantCulture) != actionProperty) - { - this.Core.Write(ErrorMessages.SecurePropertyNotUppercase(sourceLineNumbers, node.Name.LocalName, "Property", actionProperty)); - } - - if (null == minimum && null == maximum) - { - this.Core.Write(ErrorMessages.ExpectedAttributes(sourceLineNumbers, node.Name.LocalName, "Minimum", "Maximum")); - } - - this.Core.ParseForExtensionElements(node); - - if (!this.Core.EncounteredError) - { - this.Core.AddSymbol(new UpgradeSymbol(sourceLineNumbers) - { - UpgradeCode = upgradeId, - VersionMin = minimum, - VersionMax = maximum, - Language = language, - ExcludeLanguages = excludeLanguages, - IgnoreRemoveFailures = ignoreFailures, - VersionMaxInclusive = includeMax, - VersionMinInclusive = includeMin, - MigrateFeatures = migrateFeatures, - OnlyDetect = onlyDetect, - Remove = removeFeatures, - ActionProperty = actionProperty - }); - - // Ensure that RemoveExistingProducts is authored in InstallExecuteSequence - // if at least one row in Upgrade table lacks the OnlyDetect attribute. - if (!onlyDetect) - { - this.Core.CreateSimpleReference(sourceLineNumbers, SymbolDefinitions.WixAction, "InstallExecuteSequence", "RemoveExistingProducts"); - } - } - } - - /// - /// Parses a verb element. - /// - /// Element to parse. - /// Extension verb is releated to. - /// Optional progId for extension. - /// Identifier for parent component. - /// Flag if verb is advertised. - private void ParseVerbElement(XElement node, string extension, string progId, string componentId, YesNoType advertise) - { - var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node); - string id = null; - string argument = null; - string command = null; - var sequence = CompilerConstants.IntegerNotSet; - string targetFile = null; - string targetProperty = null; - - foreach (var attrib in node.Attributes()) - { - if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace) - { - switch (attrib.Name.LocalName) - { - case "Id": - id = this.Core.GetAttributeValue(sourceLineNumbers, attrib); - break; - case "Argument": - argument = this.Core.GetAttributeValue(sourceLineNumbers, attrib); - break; - case "Command": - command = this.Core.GetAttributeValue(sourceLineNumbers, attrib); - break; - case "Sequence": - sequence = this.Core.GetAttributeIntegerValue(sourceLineNumbers, attrib, 1, Int16.MaxValue); - break; - case "TargetFile": - targetFile = this.Core.GetAttributeValue(sourceLineNumbers, attrib); - this.Core.CreateSimpleReference(sourceLineNumbers, SymbolDefinitions.File, targetFile); - break; - case "TargetProperty": - targetProperty = this.Core.GetAttributeValue(sourceLineNumbers, attrib); - break; - default: - this.Core.UnexpectedAttribute(node, attrib); - break; - } - } - else - { - this.Core.ParseExtensionAttribute(node, attrib); - } - } - - if (null == id) - { - this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Id")); - } - - if (null != targetFile && null != targetProperty) - { - this.Core.Write(ErrorMessages.IllegalAttributeWithOtherAttribute(sourceLineNumbers, node.Name.LocalName, "TargetFile", "TargetProperty")); - } - - this.Core.ParseForExtensionElements(node); - - if (YesNoType.Yes == advertise) - { - if (null != targetFile) - { - this.Core.Write(ErrorMessages.IllegalAttributeWhenAdvertised(sourceLineNumbers, node.Name.LocalName, "TargetFile")); - } - - if (null != targetProperty) - { - this.Core.Write(ErrorMessages.IllegalAttributeWhenAdvertised(sourceLineNumbers, node.Name.LocalName, "TargetProperty")); - } - - if (!this.Core.EncounteredError) - { - var symbol = this.Core.AddSymbol(new VerbSymbol(sourceLineNumbers) - { - ExtensionRef = extension, - Verb = id, - Command = command, - Argument = argument, - }); - - if (CompilerConstants.IntegerNotSet != sequence) - { - symbol.Sequence = sequence; - } - } - } - else if (YesNoType.No == advertise) - { - if (CompilerConstants.IntegerNotSet != sequence) - { - this.Core.Write(ErrorMessages.IllegalAttributeWithOtherAttribute(sourceLineNumbers, node.Name.LocalName, "Sequence", "Advertise", "no")); - } - - if (null == targetFile && null == targetProperty) - { - this.Core.Write(ErrorMessages.ExpectedAttributesWithOtherAttribute(sourceLineNumbers, node.Name.LocalName, "TargetFile", "TargetProperty", "Advertise", "no")); - } - - string target = null; - if (null != targetFile) - { - target = String.Concat("\"[#", targetFile, "]\""); - } - else if (null != targetProperty) - { - target = String.Concat("\"[", targetProperty, "]\""); - } - - if (null != argument) - { - target = String.Concat(target, " ", argument); - } - - var prefix = progId ?? String.Concat(".", extension); - - if (null != command) - { - this.Core.CreateRegistryRow(sourceLineNumbers, RegistryRootType.ClassesRoot, String.Concat(prefix, "\\shell\\", id), String.Empty, command, componentId); - } - - this.Core.CreateRegistryRow(sourceLineNumbers, RegistryRootType.ClassesRoot, String.Concat(prefix, "\\shell\\", id, "\\command"), String.Empty, target, componentId); - } - } - - /// - /// Parses a WixVariable element. - /// - /// Element to parse. - private void ParseWixVariableElement(XElement node) - { - var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node); - Identifier id = null; - var overridable = false; - string value = null; - - foreach (var attrib in node.Attributes()) - { - if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace) - { - switch (attrib.Name.LocalName) - { - case "Id": - id = this.Core.GetAttributeIdentifier(sourceLineNumbers, attrib); - break; - case "Overridable": - overridable = (YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib)); - break; - case "Value": - value = this.Core.GetAttributeValue(sourceLineNumbers, attrib, EmptyRule.CanBeEmpty); - break; - default: - this.Core.UnexpectedAttribute(node, attrib); - break; - } - } - else - { - this.Core.ParseExtensionAttribute(node, attrib); - } - } - - if (null == id) - { - this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Id")); - } - - if (null == value) - { - this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Value")); - } - - this.Core.ParseForExtensionElements(node); - - if (!this.Core.EncounteredError) - { - this.Core.AddSymbol(new WixVariableSymbol(sourceLineNumbers, id) - { - Value = value, - Overridable = overridable - }); - } - } - - private CompressionLevel? ParseCompressionLevel(SourceLineNumber sourceLineNumbers, XElement node, XAttribute attribute) - { - var compressionLevel = this.Core.GetAttributeValue(sourceLineNumbers, attribute); - switch (compressionLevel) - { - case "high": - return CompressionLevel.High; - case "low": - return CompressionLevel.Low; - case "medium": - return CompressionLevel.Medium; - case "mszip": - return CompressionLevel.Mszip; - case "none": - return CompressionLevel.None; - case "": - break; - default: - this.Core.Write(ErrorMessages.IllegalCompressionLevel(sourceLineNumbers, compressionLevel)); - break; - } - - return null; - } - } -} diff --git a/src/WixToolset.Core/Compiler_Bundle.cs b/src/WixToolset.Core/Compiler_Bundle.cs index 1ee09166..b8c7b7b1 100644 --- a/src/WixToolset.Core/Compiler_Bundle.cs +++ b/src/WixToolset.Core/Compiler_Bundle.cs @@ -196,6 +196,9 @@ namespace WixToolset.Core case "ParentName": parentName = this.Core.GetAttributeValue(sourceLineNumbers, attrib); break; + case "ProviderKey": + this.ParseBundleProviderKeyAttribute(sourceLineNumbers, node, attrib); + break; case "SplashScreenSourceFile": splashScreenSourceFile = this.Core.GetAttributeValue(sourceLineNumbers, attrib); break; @@ -340,6 +343,9 @@ namespace WixToolset.Core case "RelatedBundle": this.ParseRelatedBundleElement(child); break; + case "Requires": + this.ParseRequiresElement(child, null, false); + break; case "SetVariable": this.ParseSetVariableElement(child); break; @@ -2386,6 +2392,9 @@ namespace WixToolset.Core case "PayloadGroupRef": this.ParsePayloadGroupRefElement(child, ComplexReferenceParentType.Package, id, ComplexReferenceChildType.Unknown, null); break; + case "Provides": + this.ParseProvidesElement(child, packageType, id.Id, out _); + break; case "ExitCode": allowed = (packageType == WixBundlePackageType.Exe); if (allowed) diff --git a/src/WixToolset.Core/Compiler_Dependency.cs b/src/WixToolset.Core/Compiler_Dependency.cs new file mode 100644 index 00000000..74982fba --- /dev/null +++ b/src/WixToolset.Core/Compiler_Dependency.cs @@ -0,0 +1,385 @@ +// 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 +{ + using System; + using System.Xml.Linq; + using WixToolset.Data; + using WixToolset.Data.Symbols; + + /// + /// Compiler of the WiX toolset. + /// + internal partial class Compiler : ICompiler + { + // The root registry key for the dependency extension. We write to Software\Classes explicitly + // based on the current security context instead of HKCR. See + // http://msdn.microsoft.com/en-us/library/ms724475(VS.85).aspx for more information. + private const string DependencyRegistryRoot = @"Software\Classes\Installer\Dependencies\"; + + private static readonly char[] InvalidDependencyCharacters = new char[] { ' ', '\"', ';', '\\' }; + + /// + /// Processes the ProviderKey bundle attribute. + /// + /// Source line number for the parent element. + /// Parent element of attribute. + /// The XML attribute for the ProviderKey attribute. + private void ParseBundleProviderKeyAttribute(SourceLineNumber sourceLineNumbers, XElement parentElement, XAttribute attribute) + { + var providerKey = this.Core.GetAttributeValue(sourceLineNumbers, attribute); + int illegalChar; + + // Make sure the key does not contain any illegal characters or values. + if (String.IsNullOrEmpty(providerKey)) + { + this.Messaging.Write(ErrorMessages.IllegalEmptyAttributeValue(sourceLineNumbers, parentElement.Name.LocalName, attribute.Name.LocalName)); + } + else if (0 <= (illegalChar = providerKey.IndexOfAny(InvalidDependencyCharacters))) + { + this.Messaging.Write(CompilerErrors.IllegalCharactersInProvider(sourceLineNumbers, "ProviderKey", providerKey[illegalChar], String.Join(" ", InvalidDependencyCharacters))); + } + else if ("ALL" == providerKey) + { + this.Messaging.Write(CompilerErrors.ReservedValue(sourceLineNumbers, parentElement.Name.LocalName, "ProviderKey", providerKey)); + } + + if (!this.Messaging.EncounteredError) + { + // Generate the primary key for the row. + var id = this.Core.CreateIdentifier("dep", attribute.Name.LocalName, providerKey); + + // Create the provider symbol for the bundle. The Component_ field is required + // in the table definition but unused for bundles, so just set it to the valid ID. + this.Core.AddSymbol(new WixDependencyProviderSymbol(sourceLineNumbers, id) + { + ComponentRef = id.Id, + ProviderKey = providerKey, + Attributes = WixDependencyProviderAttributes.ProvidesAttributesBundle, + }); + } + } + + /// + /// Processes the Provides element. + /// + /// The XML node for the Provides element. + /// The type of the package being chained into a bundle, or null if building an MSI package. + /// The identifier of the parent component or package. + /// Possible KeyPath identifier. + /// Yes if this is the keypath. + private YesNoType ParseProvidesElement(XElement node, WixBundlePackageType? packageType, string parentId, out string possibleKeyPath) + { + possibleKeyPath = null; + + var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node); + Identifier id = null; + string key = null; + string version = null; + string displayName = null; + + foreach (var attrib in node.Attributes()) + { + if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace) + { + switch (attrib.Name.LocalName) + { + case "Id": + id = this.Core.GetAttributeIdentifier(sourceLineNumbers, attrib); + break; + case "Key": + key = this.Core.GetAttributeValue(sourceLineNumbers, attrib); + break; + case "Version": + version = this.Core.GetAttributeVersionValue(sourceLineNumbers, attrib); + break; + case "DisplayName": + displayName = this.Core.GetAttributeValue(sourceLineNumbers, attrib); + break; + default: + this.Core.UnexpectedAttribute(node, attrib); + break; + } + } + else + { + this.Core.ParseExtensionAttribute(node, attrib); + } + } + + // Make sure the key is valid. The key will default to the ProductCode for MSI packages + // and the package code for MSP packages in the binder if not specified. + if (!String.IsNullOrEmpty(key)) + { + int illegalChar; + + // Make sure the key does not contain any illegal characters or values. + if (0 <= (illegalChar = key.IndexOfAny(InvalidDependencyCharacters))) + { + this.Messaging.Write(CompilerErrors.IllegalCharactersInProvider(sourceLineNumbers, "Key", key[illegalChar], String.Join(" ", InvalidDependencyCharacters))); + } + else if ("ALL" == key) + { + this.Messaging.Write(CompilerErrors.ReservedValue(sourceLineNumbers, node.Name.LocalName, "Key", key)); + } + } + else if (!packageType.HasValue) + { + // Make sure the ProductCode is authored and set the key. + this.Core.CreateSimpleReference(sourceLineNumbers, SymbolDefinitions.Property, "ProductCode"); + key = "!(bind.property.ProductCode)"; + } + else if (WixBundlePackageType.Exe == packageType || WixBundlePackageType.Msu == packageType) + { + // Must specify the provider key when authored for a package. + this.Messaging.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Key")); + } + + // The Version attribute should not be authored in or for an MSI package. + if (!String.IsNullOrEmpty(version)) + { + switch (packageType) + { + case null: + this.Messaging.Write(CompilerWarnings.DiscouragedVersionAttribute(sourceLineNumbers)); + break; + case WixBundlePackageType.Msi: + this.Messaging.Write(CompilerWarnings.DiscouragedVersionAttribute(sourceLineNumbers, parentId)); + break; + } + } + else if (WixBundlePackageType.Msp == packageType || WixBundlePackageType.Msu == packageType) + { + // Must specify the Version when authored for packages that do not contain a version. + this.Messaging.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Version")); + } + + // Need the element ID for child element processing, so generate now if not authored. + if (null == id) + { + id = this.Core.CreateIdentifier("dep", node.Name.LocalName, parentId, key); + } + + foreach (var child in node.Elements()) + { + if (CompilerCore.WixNamespace == child.Name.Namespace) + { + switch (child.Name.LocalName) + { + case "Requires": + this.ParseRequiresElement(child, id.Id, requiresAction: !packageType.HasValue); + break; + case "RequiresRef": + this.ParseRequiresRefElement(child, id.Id, requiresAction: !packageType.HasValue); + break; + default: + this.Core.UnexpectedElement(node, child); + break; + } + } + else + { + this.Core.ParseExtensionElement(node, child); + } + } + + if (!this.Messaging.EncounteredError) + { + var symbol = this.Core.AddSymbol(new WixDependencyProviderSymbol(sourceLineNumbers, id) + { + ComponentRef = parentId, + ProviderKey = key, + }); + + if (!String.IsNullOrEmpty(version)) + { + symbol.Version = version; + } + + if (!String.IsNullOrEmpty(displayName)) + { + symbol.DisplayName = displayName; + } + + if (!packageType.HasValue) + { + // Generate registry rows for the provider using binder properties. + var keyProvides = String.Concat(DependencyRegistryRoot, key); + var root = RegistryRootType.MachineUser; + + var value = "[ProductCode]"; + this.Core.CreateRegistryRow(sourceLineNumbers, root, keyProvides, null, value, parentId); + + value = !String.IsNullOrEmpty(version) ? version : "[ProductVersion]"; + var versionRegistrySymbol = this.Core.CreateRegistryRow(sourceLineNumbers, root, keyProvides, "Version", value, parentId); + + value = !String.IsNullOrEmpty(displayName) ? displayName : "[ProductName]"; + this.Core.CreateRegistryRow(sourceLineNumbers, root, keyProvides, "DisplayName", value, parentId); + + // Use the Version registry value and use that as a potential key path. + possibleKeyPath = versionRegistrySymbol.Id; + } + } + + return YesNoType.NotSet; + } + + /// + /// Processes the Requires element. + /// + /// The XML node for the Requires element. + /// The parent provider identifier. + /// Whether the Requires custom action should be referenced. + private void ParseRequiresElement(XElement node, string providerId, bool requiresAction) + { + var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node); + Identifier id = null; + string providerKey = null; + string minVersion = null; + string maxVersion = null; + var attributes = WixDependencySymbolAttributes.None; + var illegalChar = -1; + + foreach (var attrib in node.Attributes()) + { + if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace) + { + switch (attrib.Name.LocalName) + { + case "Id": + id = this.Core.GetAttributeIdentifier(sourceLineNumbers, attrib); + break; + case "ProviderKey": + providerKey = this.Core.GetAttributeValue(sourceLineNumbers, attrib); + break; + case "Minimum": + minVersion = this.Core.GetAttributeVersionValue(sourceLineNumbers, attrib); + break; + case "Maximum": + maxVersion = this.Core.GetAttributeVersionValue(sourceLineNumbers, attrib); + break; + case "IncludeMinimum": + if (YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib)) + { + attributes |= WixDependencySymbolAttributes.RequiresAttributesMinVersionInclusive; + } + break; + case "IncludeMaximum": + if (YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib)) + { + attributes |= WixDependencySymbolAttributes.RequiresAttributesMaxVersionInclusive; + } + break; + default: + this.Core.UnexpectedAttribute(node, attrib); + break; + } + } + else + { + this.Core.ParseExtensionAttribute(node, attrib); + } + } + + this.Core.ParseForExtensionElements(node); + + if (null == id) + { + // Generate an ID only if this element is authored under a Provides element; otherwise, a RequiresRef + // element will be necessary and the Id attribute will be required. + if (!String.IsNullOrEmpty(providerId)) + { + id = this.Core.CreateIdentifier("dep", node.Name.LocalName, providerKey); + } + else + { + this.Messaging.Write(ErrorMessages.ExpectedAttributeWhenElementNotUnderElement(sourceLineNumbers, node.Name.LocalName, "Id", "Provides")); + id = Identifier.Invalid; + } + } + + if (String.IsNullOrEmpty(providerKey)) + { + this.Messaging.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "ProviderKey")); + } + // Make sure the key does not contain any illegal characters. + else if (0 <= (illegalChar = providerKey.IndexOfAny(InvalidDependencyCharacters))) + { + this.Messaging.Write(CompilerErrors.IllegalCharactersInProvider(sourceLineNumbers, "ProviderKey", providerKey[illegalChar], String.Join(" ", InvalidDependencyCharacters))); + } + + if (!this.Messaging.EncounteredError) + { + var symbol = this.Core.AddSymbol(new WixDependencySymbol(sourceLineNumbers, id) + { + ProviderKey = providerKey, + MinVersion = minVersion, + MaxVersion = maxVersion, + Attributes = attributes + }); + + // Create the relationship between this WixDependency symbol and the WixDependencyProvider symbol. + if (!String.IsNullOrEmpty(providerId)) + { + this.Core.AddSymbol(new WixDependencyRefSymbol(sourceLineNumbers) + { + WixDependencyProviderRef = providerId, + WixDependencyRef = id.Id, + }); + } + } + } + + /// + /// Processes the RequiresRef element. + /// + /// The XML node for the RequiresRef element. + /// The parent provider identifier. + /// Whether the Requires custom action should be referenced. + private void ParseRequiresRefElement(XElement node, string providerId, bool requiresAction) + { + var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node); + string id = null; + + foreach (var attrib in node.Attributes()) + { + if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace) + { + switch (attrib.Name.LocalName) + { + case "Id": + id = this.Core.GetAttributeIdentifierValue(sourceLineNumbers, attrib); + break; + default: + this.Core.UnexpectedAttribute(node, attrib); + break; + } + } + else + { + this.Core.ParseExtensionAttribute(node, attrib); + } + } + + this.Core.ParseForExtensionElements(node); + + if (String.IsNullOrEmpty(id)) + { + this.Messaging.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Id")); + } + + if (!this.Messaging.EncounteredError) + { + // Create a link dependency on the row that contains information we'll need during bind. + this.Core.CreateSimpleReference(sourceLineNumbers, SymbolDefinitions.WixDependency, id); + + // Create the relationship between the WixDependency row and the parent WixDependencyProvider row. + this.Core.AddSymbol(new WixDependencyRefSymbol(sourceLineNumbers) + { + WixDependencyProviderRef = providerId, + WixDependencyRef = id, + }); + } + } + } +} diff --git a/src/WixToolset.Core/Compiler_Module.cs b/src/WixToolset.Core/Compiler_Module.cs index d730ae5d..6953467f 100644 --- a/src/WixToolset.Core/Compiler_Module.cs +++ b/src/WixToolset.Core/Compiler_Module.cs @@ -190,6 +190,9 @@ namespace WixToolset.Core case "PropertyRef": this.ParseSimpleRefElement(child, SymbolDefinitions.Property); break; + case "Requires": + this.ParseRequiresElement(child, null, false); + break; case "SetDirectory": this.ParseSetDirectoryElement(child); break; diff --git a/src/WixToolset.Core/Compiler_Package.cs b/src/WixToolset.Core/Compiler_Package.cs new file mode 100644 index 00000000..d2728e9c --- /dev/null +++ b/src/WixToolset.Core/Compiler_Package.cs @@ -0,0 +1,4975 @@ +// 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 +{ + using System; + using System.Collections; + using System.Collections.Generic; + using System.Diagnostics.CodeAnalysis; + using System.Globalization; + using System.IO; + using System.Xml.Linq; + using WixToolset.Data; + using WixToolset.Data.Symbols; + using WixToolset.Data.WindowsInstaller; + using WixToolset.Extensibility; + + /// + /// Compiler of the WiX toolset. + /// + internal partial class Compiler : ICompiler + { + /// + /// Parses a product element. + /// + /// Element to parse. + private void ParsePackageElement(XElement node) + { + var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node); + var compressed = YesNoDefaultType.Default; + var sourceBits = 0; + var codepage = 65001; + var productCode = "*"; + var isPerMachine = true; + string installScope = null; + string upgradeCode = null; + string manufacturer = null; + string version = null; + string symbols = null; + var isCodepageSet = false; + var isPackageNameSet = false; + var isKeywordsSet = false; + var isPackageAuthorSet = false; + + this.GetDefaultPlatformAndInstallerVersion(out var platform, out var msiVersion); + + this.activeName = null; + this.activeLanguage = null; + + foreach (var attrib in node.Attributes()) + { + if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace) + { + switch (attrib.Name.LocalName) + { + case "Codepage": + codepage = this.Core.GetAttributeCodePageValue(sourceLineNumbers, attrib); + break; + case "Compressed": + compressed = this.Core.GetAttributeYesNoDefaultValue(sourceLineNumbers, attrib); + break; + case "InstallerVersion": + msiVersion = this.Core.GetAttributeIntegerValue(sourceLineNumbers, attrib, 0, Int32.MaxValue); + break; + case "Language": + this.activeLanguage = this.Core.GetAttributeLocalizableIntegerValue(sourceLineNumbers, attrib, 0, Int16.MaxValue); + break; + case "Manufacturer": + manufacturer = this.Core.GetAttributeValue(sourceLineNumbers, attrib, EmptyRule.MustHaveNonWhitespaceCharacters); + if ("PUT-COMPANY-NAME-HERE" == manufacturer) + { + this.Core.Write(WarningMessages.PlaceholderValue(sourceLineNumbers, node.Name.LocalName, attrib.Name.LocalName, manufacturer)); + } + break; + case "Name": + this.activeName = this.Core.GetAttributeValue(sourceLineNumbers, attrib, EmptyRule.MustHaveNonWhitespaceCharacters); + if ("PUT-PRODUCT-NAME-HERE" == this.activeName) + { + this.Core.Write(WarningMessages.PlaceholderValue(sourceLineNumbers, node.Name.LocalName, attrib.Name.LocalName, this.activeName)); + } + break; + case "ProductCode": + productCode = this.Core.GetAttributeGuidValue(sourceLineNumbers, attrib, true); + break; + case "Scope": + installScope = this.Core.GetAttributeValue(sourceLineNumbers, attrib); + switch (installScope) + { + case "perMachine": + // handled below after we create the section. + break; + case "perUser": + isPerMachine = false; + sourceBits |= 8; + break; + default: + this.Core.Write(ErrorMessages.IllegalAttributeValue(sourceLineNumbers, node.Name.LocalName, attrib.Name.LocalName, installScope, "perMachine", "perUser")); + break; + } + break; + case "ShortNames": + if (YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib)) + { + sourceBits |= 1; + } + break; + case "UpgradeCode": + upgradeCode = this.Core.GetAttributeGuidValue(sourceLineNumbers, attrib, false); + break; + case "Version": // if the attribute is valid version, use the attribute value as is (so "1.0000.01.01" would *not* get translated to "1.0.1.1"). + var verifiedVersion = this.Core.GetAttributeVersionValue(sourceLineNumbers, attrib); + if (!String.IsNullOrEmpty(verifiedVersion)) + { + version = attrib.Value; + } + break; + default: + this.Core.UnexpectedAttribute(node, attrib); + break; + } + } + else + { + this.Core.ParseExtensionAttribute(node, attrib); + } + } + + if (null == productCode) + { + this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Id")); + } + + if (null == this.activeLanguage) + { + this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Language")); + } + + if (null == manufacturer) + { + this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Manufacturer")); + } + + if (null == this.activeName) + { + this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Name")); + } + + if (null == upgradeCode) + { + this.Core.Write(WarningMessages.MissingUpgradeCode(sourceLineNumbers)); + } + + if (null == version) + { + this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Version")); + } + else if (!CompilerCore.IsValidProductVersion(version)) + { + this.Core.Write(ErrorMessages.InvalidProductVersion(sourceLineNumbers, version)); + } + + if (compressed != YesNoDefaultType.No) + { + sourceBits |= 2; + } + + if (this.Core.EncounteredError) + { + return; + } + + try + { + this.compilingProduct = true; + this.Core.CreateActiveSection(productCode, SectionType.Product, codepage, this.Context.CompilationId); + + this.AddProperty(sourceLineNumbers, new Identifier(AccessModifier.Public, "Manufacturer"), manufacturer, false, false, false, true); + this.AddProperty(sourceLineNumbers, new Identifier(AccessModifier.Public, "ProductCode"), productCode, false, false, false, true); + this.AddProperty(sourceLineNumbers, new Identifier(AccessModifier.Public, "ProductLanguage"), this.activeLanguage, false, false, false, true); + this.AddProperty(sourceLineNumbers, new Identifier(AccessModifier.Public, "ProductName"), this.activeName, false, false, false, true); + this.AddProperty(sourceLineNumbers, new Identifier(AccessModifier.Public, "ProductVersion"), version, false, false, false, true); + if (null != upgradeCode) + { + this.AddProperty(sourceLineNumbers, new Identifier(AccessModifier.Public, "UpgradeCode"), upgradeCode, false, false, false, true); + } + + if (isPerMachine) + { + this.AddProperty(sourceLineNumbers, new Identifier(AccessModifier.Public, "ALLUSERS"), "1", false, false, false, false); + } + + this.ValidateAndAddCommonSummaryInformationSymbols(sourceLineNumbers, msiVersion, platform); + + this.Core.AddSymbol(new SummaryInformationSymbol(sourceLineNumbers) + { + PropertyId = SummaryInformationType.WordCount, + Value = sourceBits.ToString(CultureInfo.InvariantCulture) + }); + + var contextValues = new Dictionary + { + ["ProductLanguage"] = this.activeLanguage, + ["ProductVersion"] = version, + ["UpgradeCode"] = upgradeCode + }; + + var featureDisplay = 0; + foreach (var child in node.Elements()) + { + if (CompilerCore.WixNamespace == child.Name.Namespace) + { + switch (child.Name.LocalName) + { + case "_locDefinition": + break; + case "AdminExecuteSequence": + this.ParseSequenceElement(child, SequenceTable.AdminExecuteSequence); + break; + case "AdminUISequence": + this.ParseSequenceElement(child, SequenceTable.AdminUISequence); + break; + case "AdvertiseExecuteSequence": + this.ParseSequenceElement(child, SequenceTable.AdvertiseExecuteSequence); + break; + case "InstallExecuteSequence": + this.ParseSequenceElement(child, SequenceTable.InstallExecuteSequence); + break; + case "InstallUISequence": + this.ParseSequenceElement(child, SequenceTable.InstallUISequence); + break; + case "AppId": + this.ParseAppIdElement(child, null, YesNoType.Yes, null, null, null); + break; + case "Binary": + this.ParseBinaryElement(child); + break; + case "ComplianceCheck": + this.ParseComplianceCheckElement(child); + break; + case "Component": + this.ParseComponentElement(child, ComplexReferenceParentType.Unknown, null, null, CompilerConstants.IntegerNotSet, null, null); + break; + case "ComponentGroup": + this.ParseComponentGroupElement(child, ComplexReferenceParentType.Unknown, null); + break; + case "CustomAction": + this.ParseCustomActionElement(child); + break; + case "CustomActionRef": + this.ParseSimpleRefElement(child, SymbolDefinitions.CustomAction); + break; + case "CustomTable": + this.ParseCustomTableElement(child); + break; + case "CustomTableRef": + this.ParseCustomTableRefElement(child); + break; + case "Directory": + this.ParseDirectoryElement(child, null, CompilerConstants.IntegerNotSet, String.Empty); + break; + case "DirectoryRef": + this.ParseDirectoryRefElement(child); + break; + case "EmbeddedChainer": + this.ParseEmbeddedChainerElement(child); + break; + case "EmbeddedChainerRef": + this.ParseSimpleRefElement(child, SymbolDefinitions.MsiEmbeddedChainer); + break; + case "EnsureTable": + this.ParseEnsureTableElement(child); + break; + case "Feature": + this.ParseFeatureElement(child, ComplexReferenceParentType.Product, productCode, ref featureDisplay); + break; + case "FeatureRef": + this.ParseFeatureRefElement(child, ComplexReferenceParentType.Product, productCode); + break; + case "FeatureGroupRef": + this.ParseFeatureGroupRefElement(child, ComplexReferenceParentType.Product, productCode); + break; + case "Icon": + this.ParseIconElement(child); + break; + case "InstanceTransforms": + this.ParseInstanceTransformsElement(child); + break; + case "Launch": + this.ParseLaunchElement(child); + break; + case "MajorUpgrade": + this.ParseMajorUpgradeElement(child, contextValues); + break; + case "Media": + this.ParseMediaElement(child, null); + break; + case "MediaTemplate": + this.ParseMediaTemplateElement(child, null); + break; + case "PackageCertificates": + case "PatchCertificates": + this.ParseCertificatesElement(child); + break; + case "Property": + this.ParsePropertyElement(child); + break; + case "PropertyRef": + this.ParseSimpleRefElement(child, SymbolDefinitions.Property); + break; + case "Requires": + this.ParseRequiresElement(child, null, false); + break; + case "SetDirectory": + this.ParseSetDirectoryElement(child); + break; + case "SetProperty": + this.ParseSetPropertyElement(child); + break; + case "SFPCatalog": + string parentName = null; + this.ParseSFPCatalogElement(child, ref parentName); + break; + case "SoftwareTag": + this.ParsePackageTagElement(child); + break; + case "SummaryInformation": + this.ParseSummaryInformationElement(child, ref isCodepageSet, ref isPackageNameSet, ref isKeywordsSet, ref isPackageAuthorSet); + break; + case "SymbolPath": + if (null != symbols) + { + symbols += ";" + this.ParseSymbolPathElement(child); + } + else + { + symbols = this.ParseSymbolPathElement(child); + } + break; + case "UI": + this.ParseUIElement(child); + break; + case "UIRef": + this.ParseSimpleRefElement(child, SymbolDefinitions.WixUI); + break; + case "Upgrade": + this.ParseUpgradeElement(child); + break; + case "WixVariable": + this.ParseWixVariableElement(child); + break; + default: + this.Core.UnexpectedElement(node, child); + break; + } + } + else + { + this.Core.ParseExtensionElement(node, child); + } + } + + if (!this.Core.EncounteredError) + { + if (!isCodepageSet) + { + this.Core.AddSymbol(new SummaryInformationSymbol(sourceLineNumbers) + { + PropertyId = SummaryInformationType.Codepage, + Value = "1252" + }); + } + + if (!isPackageNameSet) + { + this.Core.AddSymbol(new SummaryInformationSymbol(sourceLineNumbers) + { + PropertyId = SummaryInformationType.Subject, + Value = this.activeName + }); + } + + if (!isPackageAuthorSet) + { + this.Core.AddSymbol(new SummaryInformationSymbol(sourceLineNumbers) + { + PropertyId = SummaryInformationType.Author, + Value = manufacturer + }); + } + + if (!isKeywordsSet) + { + this.Core.AddSymbol(new SummaryInformationSymbol(sourceLineNumbers) + { + PropertyId = SummaryInformationType.Keywords, + Value = "Installer" + }); + } + + if (null != symbols) + { + this.Core.AddSymbol(new WixDeltaPatchSymbolPathsSymbol(sourceLineNumbers) + { + SymbolId = productCode, + SymbolType = SymbolPathType.Product, + SymbolPaths = symbols, + }); + } + } + } + finally + { + this.compilingProduct = false; + } + } + + private void GetDefaultPlatformAndInstallerVersion(out string platform, out int msiVersion) + { + // Let's default to a modern version of MSI. Users can override, + // of course, subject to platform-specific limitations. + msiVersion = 500; + + switch (this.CurrentPlatform) + { + case Platform.X86: + platform = "Intel"; + break; + case Platform.X64: + platform = "x64"; + break; + case Platform.ARM64: + platform = "Arm64"; + break; + default: + throw new ArgumentException("Unknown platform enumeration '{0}' encountered.", this.CurrentPlatform.ToString()); + } + } + + private void ValidateAndAddCommonSummaryInformationSymbols(SourceLineNumber sourceLineNumbers, int msiVersion, string platform) + { + if (String.Equals(platform, "X64", StringComparison.OrdinalIgnoreCase) && 200 > msiVersion) + { + msiVersion = 200; + this.Core.Write(WarningMessages.RequiresMsi200for64bitPackage(sourceLineNumbers)); + } + + if (String.Equals(platform, "Arm64", StringComparison.OrdinalIgnoreCase) && 500 > msiVersion) + { + msiVersion = 500; + this.Core.Write(WarningMessages.RequiresMsi500forArmPackage(sourceLineNumbers)); + } + + this.Core.AddSymbol(new SummaryInformationSymbol(sourceLineNumbers) + { + PropertyId = SummaryInformationType.Comments, + Value = String.Format(CultureInfo.InvariantCulture, "This installer database contains the logic and data required to install {0}.", this.activeName) + }); + + this.Core.AddSymbol(new SummaryInformationSymbol(sourceLineNumbers) + { + PropertyId = SummaryInformationType.Title, + Value = "Installation Database" + }); + + this.Core.AddSymbol(new SummaryInformationSymbol(sourceLineNumbers) + { + PropertyId = SummaryInformationType.PlatformAndLanguage, + Value = String.Format(CultureInfo.InvariantCulture, "{0};{1}", platform, this.activeLanguage) + }); + + this.Core.AddSymbol(new SummaryInformationSymbol(sourceLineNumbers) + { + PropertyId = SummaryInformationType.WindowsInstallerVersion, + Value = msiVersion.ToString(CultureInfo.InvariantCulture) + }); + + this.Core.AddSymbol(new SummaryInformationSymbol(sourceLineNumbers) + { + PropertyId = SummaryInformationType.Security, + Value = "2" + }); + + } + + /// + /// Parses an odbc driver or translator element. + /// + /// Element to parse. + /// Identifier of parent component. + /// Default identifer for driver/translator file. + /// Symbol type we're processing for. + private void ParseODBCDriverOrTranslator(XElement node, string componentId, string fileId, SymbolDefinitionType symbolDefinitionType) + { + var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node); + Identifier id = null; + var driver = fileId; + string name = null; + var setup = fileId; + + foreach (var attrib in node.Attributes()) + { + if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace) + { + switch (attrib.Name.LocalName) + { + case "Id": + id = this.Core.GetAttributeIdentifier(sourceLineNumbers, attrib); + break; + case "File": + driver = this.Core.GetAttributeIdentifierValue(sourceLineNumbers, attrib); + this.Core.CreateSimpleReference(sourceLineNumbers, SymbolDefinitions.File, driver); + break; + case "Name": + name = this.Core.GetAttributeValue(sourceLineNumbers, attrib); + break; + case "SetupFile": + setup = this.Core.GetAttributeIdentifierValue(sourceLineNumbers, attrib); + this.Core.CreateSimpleReference(sourceLineNumbers, SymbolDefinitions.File, setup); + break; + default: + this.Core.UnexpectedAttribute(node, attrib); + break; + } + } + else + { + this.Core.ParseExtensionAttribute(node, attrib); + } + } + + if (null == name) + { + this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Name")); + } + + if (null == id) + { + id = this.Core.CreateIdentifier("odb", name, fileId, setup); + } + + // drivers have a few possible children + if (SymbolDefinitionType.ODBCDriver == symbolDefinitionType) + { + // process any data sources for the driver + foreach (var child in node.Elements()) + { + if (CompilerCore.WixNamespace == child.Name.Namespace) + { + switch (child.Name.LocalName) + { + case "ODBCDataSource": + string ignoredKeyPath = null; + this.ParseODBCDataSource(child, componentId, name, out ignoredKeyPath); + break; + case "Property": + this.ParseODBCProperty(child, id.Id, SymbolDefinitionType.ODBCAttribute); + break; + default: + this.Core.UnexpectedElement(node, child); + break; + } + } + else + { + this.Core.ParseExtensionElement(node, child); + } + } + } + else + { + this.Core.ParseForExtensionElements(node); + } + + if (!this.Core.EncounteredError) + { + switch (symbolDefinitionType) + { + case SymbolDefinitionType.ODBCDriver: + this.Core.AddSymbol(new ODBCDriverSymbol(sourceLineNumbers, id) + { + ComponentRef = componentId, + Description = name, + FileRef = driver, + SetupFileRef = setup, + }); + break; + case SymbolDefinitionType.ODBCTranslator: + this.Core.AddSymbol(new ODBCTranslatorSymbol(sourceLineNumbers, id) + { + ComponentRef = componentId, + Description = name, + FileRef = driver, + SetupFileRef = setup, + }); + break; + default: + throw new ArgumentOutOfRangeException(nameof(symbolDefinitionType)); + } + } + } + + /// + /// Parses a Property element underneath an ODBC driver or translator. + /// + /// Element to parse. + /// Identifier of parent driver or translator. + /// Name of the table to create property in. + private void ParseODBCProperty(XElement node, string parentId, SymbolDefinitionType symbolDefinitionType) + { + var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node); + string id = null; + string propertyValue = null; + + foreach (var attrib in node.Attributes()) + { + if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace) + { + switch (attrib.Name.LocalName) + { + case "Id": + id = this.Core.GetAttributeValue(sourceLineNumbers, attrib); + break; + case "Value": + propertyValue = this.Core.GetAttributeValue(sourceLineNumbers, attrib); + break; + default: + this.Core.UnexpectedAttribute(node, attrib); + break; + } + } + else + { + this.Core.ParseExtensionAttribute(node, attrib); + } + } + + if (null == id) + { + this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Id")); + } + + this.Core.ParseForExtensionElements(node); + + if (!this.Core.EncounteredError) + { + var identifier = new Identifier(AccessModifier.Private, parentId, id); + switch (symbolDefinitionType) + { + case SymbolDefinitionType.ODBCAttribute: + this.Core.AddSymbol(new ODBCAttributeSymbol(sourceLineNumbers, identifier) + { + DriverRef = parentId, + Attribute = id, + Value = propertyValue, + }); + break; + case SymbolDefinitionType.ODBCSourceAttribute: + this.Core.AddSymbol(new ODBCSourceAttributeSymbol(sourceLineNumbers, identifier) + { + DataSourceRef = parentId, + Attribute = id, + Value = propertyValue, + }); + break; + default: + throw new ArgumentOutOfRangeException(nameof(symbolDefinitionType)); + } + } + } + + /// + /// Parse an odbc data source element. + /// + /// Element to parse. + /// Identifier of parent component. + /// Default name of driver. + /// Identifier of this element in case it is a keypath. + /// Yes if this element was marked as the parent component's key path, No if explicitly marked as not being a key path, or NotSet otherwise. + private YesNoType ParseODBCDataSource(XElement node, string componentId, string driverName, out string possibleKeyPath) + { + var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node); + Identifier id = null; + var keyPath = YesNoType.NotSet; + string name = null; + var registration = CompilerConstants.IntegerNotSet; + + foreach (var attrib in node.Attributes()) + { + if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace) + { + switch (attrib.Name.LocalName) + { + case "Id": + id = this.Core.GetAttributeIdentifier(sourceLineNumbers, attrib); + break; + case "DriverName": + driverName = this.Core.GetAttributeValue(sourceLineNumbers, attrib); + break; + case "KeyPath": + keyPath = this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib); + break; + case "Name": + name = this.Core.GetAttributeValue(sourceLineNumbers, attrib); + break; + case "Registration": + var registrationValue = this.Core.GetAttributeValue(sourceLineNumbers, attrib); + switch (registrationValue) + { + case "machine": + registration = 0; + break; + case "user": + registration = 1; + break; + case "": + break; + default: + this.Core.Write(ErrorMessages.IllegalAttributeValue(sourceLineNumbers, node.Name.LocalName, "Registration", registrationValue, "machine", "user")); + break; + } + break; + default: + this.Core.UnexpectedAttribute(node, attrib); + break; + } + } + else + { + this.Core.ParseExtensionAttribute(node, attrib); + } + } + + if (CompilerConstants.IntegerNotSet == registration) + { + this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Registration")); + registration = CompilerConstants.IllegalInteger; + } + + if (null == id) + { + id = this.Core.CreateIdentifier("odc", name, driverName, registration.ToString()); + } + + foreach (var child in node.Elements()) + { + if (CompilerCore.WixNamespace == child.Name.Namespace) + { + switch (child.Name.LocalName) + { + case "Property": + this.ParseODBCProperty(child, id.Id, SymbolDefinitionType.ODBCSourceAttribute); + break; + default: + this.Core.UnexpectedElement(node, child); + break; + } + } + else + { + this.Core.ParseExtensionElement(node, child); + } + } + + if (!this.Core.EncounteredError) + { + this.Core.AddSymbol(new ODBCDataSourceSymbol(sourceLineNumbers, id) + { + ComponentRef = componentId, + Description = name, + DriverDescription = driverName, + Registration = registration + }); + } + + possibleKeyPath = id.Id; + return keyPath; + } + + /// + /// Parses a package element. + /// + /// Element to parse. + /// + /// + /// + /// + private void ParseSummaryInformationElement(XElement node, ref bool isCodepageSet, ref bool isPackageNameSet, ref bool isKeywordsSet, ref bool isPackageAuthorSet) + { + var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node); + string codepage = null; + string packageName = null; + string keywords = null; + string packageAuthor = null; + + foreach (var attrib in node.Attributes()) + { + if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace) + { + switch (attrib.Name.LocalName) + { + case "Codepage": + codepage = this.Core.GetAttributeLocalizableCodePageValue(sourceLineNumbers, attrib, true); + break; + case "Description": + packageName = this.Core.GetAttributeValue(sourceLineNumbers, attrib); + break; + case "Keywords": + keywords = this.Core.GetAttributeValue(sourceLineNumbers, attrib); + break; + case "Manufacturer": + packageAuthor = this.Core.GetAttributeValue(sourceLineNumbers, attrib); + if ("PUT-COMPANY-NAME-HERE" == packageAuthor) + { + this.Core.Write(WarningMessages.PlaceholderValue(sourceLineNumbers, node.Name.LocalName, attrib.Name.LocalName, packageAuthor)); + } + break; + default: + this.Core.UnexpectedAttribute(node, attrib); + break; + } + } + else + { + this.Core.ParseExtensionAttribute(node, attrib); + } + } + + this.Core.ParseForExtensionElements(node); + + if (!this.Core.EncounteredError) + { + if (null != codepage) + { + isCodepageSet = true; + this.Core.AddSymbol(new SummaryInformationSymbol(sourceLineNumbers) + { + PropertyId = SummaryInformationType.Codepage, + Value = codepage + }); + } + + if (null != packageName) + { + isPackageNameSet = true; + this.Core.AddSymbol(new SummaryInformationSymbol(sourceLineNumbers) + { + PropertyId = SummaryInformationType.Subject, + Value = packageName + }); + } + + if (null != packageAuthor) + { + isPackageAuthorSet = true; + this.Core.AddSymbol(new SummaryInformationSymbol(sourceLineNumbers) + { + PropertyId = SummaryInformationType.Author, + Value = packageAuthor + }); + } + + if (null != keywords) + { + isKeywordsSet = true; + this.Core.AddSymbol(new SummaryInformationSymbol(sourceLineNumbers) + { + PropertyId = SummaryInformationType.Keywords, + Value = keywords + }); + } + } + } + + /// + /// Parses a patch information element. + /// + /// Element to parse. + private void ParsePatchInformationElement(XElement node) + { + var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node); + var codepage = "1252"; + string comments = null; + var keywords = "Installer,Patching,PCP,Database"; + var msiVersion = 1; // Should always be 1 for patches + string packageAuthor = null; + var packageName = this.activeName; + var security = YesNoDefaultType.Default; + + foreach (var attrib in node.Attributes()) + { + if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace) + { + switch (attrib.Name.LocalName) + { + case "AdminImage": + this.Core.Write(WarningMessages.DeprecatedAttribute(sourceLineNumbers, node.Name.LocalName, attrib.Name.LocalName)); + break; + case "Comments": + comments = this.Core.GetAttributeValue(sourceLineNumbers, attrib); + break; + case "Compressed": + this.Core.Write(WarningMessages.DeprecatedAttribute(sourceLineNumbers, node.Name.LocalName, attrib.Name.LocalName)); + break; + case "Description": + packageName = this.Core.GetAttributeValue(sourceLineNumbers, attrib); + break; + case "Keywords": + keywords = this.Core.GetAttributeValue(sourceLineNumbers, attrib); + break; + case "Languages": + this.Core.Write(WarningMessages.DeprecatedAttribute(sourceLineNumbers, node.Name.LocalName, attrib.Name.LocalName)); + break; + case "Manufacturer": + packageAuthor = this.Core.GetAttributeValue(sourceLineNumbers, attrib); + break; + case "Platforms": + this.Core.Write(WarningMessages.DeprecatedAttribute(sourceLineNumbers, node.Name.LocalName, attrib.Name.LocalName)); + break; + case "ReadOnly": + security = this.Core.GetAttributeYesNoDefaultValue(sourceLineNumbers, attrib); + break; + case "ShortNames": + this.Core.Write(WarningMessages.DeprecatedAttribute(sourceLineNumbers, node.Name.LocalName, attrib.Name.LocalName)); + break; + case "SummaryCodepage": + codepage = this.Core.GetAttributeLocalizableCodePageValue(sourceLineNumbers, attrib); + break; + default: + this.Core.UnexpectedAttribute(node, attrib); + break; + } + } + else + { + this.Core.ParseExtensionAttribute(node, attrib); + } + } + + this.Core.ParseForExtensionElements(node); + + if (!this.Core.EncounteredError) + { + this.Core.AddSymbol(new SummaryInformationSymbol(sourceLineNumbers) + { + PropertyId = SummaryInformationType.Codepage, + Value = codepage + }); + + this.Core.AddSymbol(new SummaryInformationSymbol(sourceLineNumbers) + { + PropertyId = SummaryInformationType.Title, + Value = "Patch" + }); + + if (null != packageName) + { + this.Core.AddSymbol(new SummaryInformationSymbol(sourceLineNumbers) + { + PropertyId = SummaryInformationType.Subject, + Value = packageName + }); + } + + if (null != packageAuthor) + { + this.Core.AddSymbol(new SummaryInformationSymbol(sourceLineNumbers) + { + PropertyId = SummaryInformationType.Author, + Value = packageAuthor + }); + } + + if (null != keywords) + { + this.Core.AddSymbol(new SummaryInformationSymbol(sourceLineNumbers) + { + PropertyId = SummaryInformationType.Keywords, + Value = keywords + }); + } + + if (null != comments) + { + this.Core.AddSymbol(new SummaryInformationSymbol(sourceLineNumbers) + { + PropertyId = SummaryInformationType.Comments, + Value = comments + }); + } + + this.Core.AddSymbol(new SummaryInformationSymbol(sourceLineNumbers) + { + PropertyId = SummaryInformationType.WindowsInstallerVersion, + Value = msiVersion.ToString(CultureInfo.InvariantCulture) + }); + + this.Core.AddSymbol(new SummaryInformationSymbol(sourceLineNumbers) + { + PropertyId = SummaryInformationType.WordCount, + Value = "0" + }); + + this.Core.AddSymbol(new SummaryInformationSymbol(sourceLineNumbers) + { + PropertyId = SummaryInformationType.Security, + Value = YesNoDefaultType.No == security ? "0" : YesNoDefaultType.Yes == security ? "4" : "2" + }); + } + } + + /// + /// Parses a permission element. + /// + /// Element to parse. + /// Identifier of object to be secured. + /// Name of table that contains objectId. + private void ParsePermissionElement(XElement node, string objectId, string tableName) + { + var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node); + var bits = new BitArray(32); + string domain = null; + string[] specialPermissions = null; + string user = null; + + switch (tableName) + { + case "CreateFolder": + specialPermissions = Common.FolderPermissions; + break; + case "File": + specialPermissions = Common.FilePermissions; + break; + case "Registry": + specialPermissions = Common.RegistryPermissions; + break; + default: + this.Core.UnexpectedElement(node.Parent, node); + return; // stop processing this element since no valid permissions are available + } + + foreach (var attrib in node.Attributes()) + { + if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace) + { + switch (attrib.Name.LocalName) + { + case "Domain": + domain = this.Core.GetAttributeValue(sourceLineNumbers, attrib); + break; + case "User": + user = this.Core.GetAttributeValue(sourceLineNumbers, attrib); + break; + case "FileAllRights": + // match the WinNT.h mask FILE_ALL_ACCESS for value 0x001F01FF (aka 1 1111 0000 0001 1111 1111 or 2032127) + bits[0] = bits[1] = bits[2] = bits[3] = bits[4] = bits[5] = bits[6] = bits[7] = bits[8] = bits[16] = bits[17] = bits[18] = bits[19] = bits[20] = true; + break; + case "SpecificRightsAll": + // match the WinNT.h mask SPECIFIC_RIGHTS_ALL for value 0x0000FFFF (aka 1111 1111 1111 1111) + bits[0] = bits[1] = bits[2] = bits[3] = bits[4] = bits[5] = bits[6] = bits[7] = bits[8] = bits[9] = bits[10] = bits[11] = bits[12] = bits[13] = bits[14] = bits[15] = true; + break; + default: + var attribValue = this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib); + if (!this.Core.TrySetBitFromName(Common.StandardPermissions, attrib.Name.LocalName, attribValue, bits, 16)) + { + if (!this.Core.TrySetBitFromName(Common.GenericPermissions, attrib.Name.LocalName, attribValue, bits, 28)) + { + if (!this.Core.TrySetBitFromName(specialPermissions, attrib.Name.LocalName, attribValue, bits, 0)) + { + this.Core.UnexpectedAttribute(node, attrib); + break; + } + } + } + break; + } + } + else + { + this.Core.ParseExtensionAttribute(node, attrib); + } + } + + if (null == user) + { + this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "User")); + } + + var permission = this.Core.CreateIntegerFromBitArray(bits); + + if (Int32.MinValue == permission) // just GENERIC_READ, which is MSI_NULL + { + this.Core.Write(ErrorMessages.GenericReadNotAllowed(sourceLineNumbers)); + } + + this.Core.ParseForExtensionElements(node); + + if (!this.Core.EncounteredError) + { + this.Core.AddSymbol(new LockPermissionsSymbol(sourceLineNumbers) + { + LockObject = objectId, + Table = tableName, + Domain = domain, + User = user, + Permission = permission + }); + } + } + + /// + /// Parses an extended permission element. + /// + /// Element to parse. + /// Identifier of object to be secured. + /// Name of table that contains objectId. + private void ParsePermissionExElement(XElement node, string objectId, string tableName) + { + var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node); + string condition = null; + Identifier id = null; + string sddl = null; + + switch (tableName) + { + case "CreateFolder": + case "File": + case "Registry": + case "ServiceInstall": + break; + default: + this.Core.UnexpectedElement(node.Parent, node); + return; // stop processing this element since nothing will be valid. + } + + foreach (var attrib in node.Attributes()) + { + if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace) + { + switch (attrib.Name.LocalName) + { + case "Id": + id = this.Core.GetAttributeIdentifier(sourceLineNumbers, attrib); + break; + case "Condition": + condition = this.Core.GetAttributeValue(sourceLineNumbers, attrib); + break; + case "Sddl": + sddl = this.Core.GetAttributeValue(sourceLineNumbers, attrib); + break; + default: + this.Core.UnexpectedAttribute(node, attrib); + break; + } + } + else + { + this.Core.ParseExtensionAttribute(node, attrib); + } + } + + this.Core.VerifyNoInnerText(sourceLineNumbers, node); + + if (null == sddl) + { + this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Sddl")); + } + + if (null == id) + { + id = this.Core.CreateIdentifier("pme", objectId, tableName, sddl); + } + + this.Core.ParseForExtensionElements(node); + + if (!this.Core.EncounteredError) + { + this.Core.AddSymbol(new MsiLockPermissionsExSymbol(sourceLineNumbers, id) + { + LockObject = objectId, + Table = tableName, + SDDLText = sddl, + Condition = condition + }); + } + } + + /// + /// Parses a progid element + /// + /// Element to parse. + /// Identifier of parent component. + /// Flag if progid is advertised. + /// CLSID related to ProgId. + /// Default description of ProgId + /// Optional parent ProgId + /// Set to true if an extension is found; used for error-checking. + /// Whether or not this ProgId is the first one found in the parent class. + /// This element's Id. + private string ParseProgIdElement(XElement node, string componentId, YesNoType advertise, string classId, string description, string parent, ref bool foundExtension, YesNoType firstProgIdForClass) + { + var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node); + string icon = null; + var iconIndex = CompilerConstants.IntegerNotSet; + string noOpen = null; + string progId = null; + var progIdAdvertise = YesNoType.NotSet; + + foreach (var attrib in node.Attributes()) + { + if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace) + { + switch (attrib.Name.LocalName) + { + case "Id": + progId = this.Core.GetAttributeValue(sourceLineNumbers, attrib); + break; + case "Advertise": + progIdAdvertise = this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib); + break; + case "Description": + description = this.Core.GetAttributeValue(sourceLineNumbers, attrib, EmptyRule.CanBeEmpty); + break; + case "Icon": + icon = this.Core.GetAttributeIdentifierValue(sourceLineNumbers, attrib); + break; + case "IconIndex": + iconIndex = this.Core.GetAttributeIntegerValue(sourceLineNumbers, attrib, Int16.MinValue + 1, Int16.MaxValue); + break; + case "NoOpen": + noOpen = this.Core.GetAttributeValue(sourceLineNumbers, attrib, EmptyRule.CanBeEmpty); + break; + default: + this.Core.UnexpectedAttribute(node, attrib); + break; + } + } + else + { + this.Core.ParseExtensionAttribute(node, attrib); + } + } + + if ((YesNoType.No == advertise && YesNoType.Yes == progIdAdvertise) || (YesNoType.Yes == advertise && YesNoType.No == progIdAdvertise)) + { + this.Core.Write(ErrorMessages.AdvertiseStateMustMatch(sourceLineNumbers, advertise.ToString(), progIdAdvertise.ToString())); + } + else if (YesNoType.NotSet != progIdAdvertise) + { + advertise = progIdAdvertise; + } + + if (YesNoType.NotSet == advertise) + { + advertise = YesNoType.No; + } + + if (null != parent && (null != icon || CompilerConstants.IntegerNotSet != iconIndex)) + { + this.Core.Write(ErrorMessages.VersionIndependentProgIdsCannotHaveIcons(sourceLineNumbers)); + } + + var firstProgIdForNestedClass = YesNoType.Yes; + foreach (var child in node.Elements()) + { + if (CompilerCore.WixNamespace == child.Name.Namespace) + { + switch (child.Name.LocalName) + { + case "Extension": + this.ParseExtensionElement(child, componentId, advertise, progId); + foundExtension = true; + break; + case "ProgId": + // Only allow one nested ProgId. If we have a child, we should not have a parent. + if (null == parent) + { + if (YesNoType.Yes == advertise) + { + this.ParseProgIdElement(child, componentId, advertise, null, description, progId, ref foundExtension, firstProgIdForNestedClass); + } + else if (YesNoType.No == advertise) + { + this.ParseProgIdElement(child, componentId, advertise, classId, description, progId, ref foundExtension, firstProgIdForNestedClass); + } + + firstProgIdForNestedClass = YesNoType.No; // any ProgId after this one is definitely not the first. + } + else + { + var childSourceLineNumbers = Preprocessor.GetSourceLineNumbers(child); + this.Core.Write(ErrorMessages.ProgIdNestedTooDeep(childSourceLineNumbers)); + } + break; + default: + this.Core.UnexpectedElement(node, child); + break; + } + } + else + { + this.Core.ParseExtensionElement(node, child); + } + } + + if (YesNoType.Yes == advertise) + { + if (!this.Core.EncounteredError) + { + var symbol = this.Core.AddSymbol(new ProgIdSymbol(sourceLineNumbers, new Identifier(AccessModifier.Public, progId)) + { + ProgId = progId, + ParentProgIdRef = parent, + ClassRef = classId, + Description = description, + }); + + if (null != icon) + { + symbol.IconRef = icon; + this.Core.CreateSimpleReference(sourceLineNumbers, SymbolDefinitions.Icon, icon); + } + + if (CompilerConstants.IntegerNotSet != iconIndex) + { + symbol.IconIndex = iconIndex; + } + + this.Core.EnsureTable(sourceLineNumbers, WindowsInstallerTableDefinitions.Class); + } + } + else if (YesNoType.No == advertise) + { + this.Core.CreateRegistryRow(sourceLineNumbers, RegistryRootType.ClassesRoot, progId, String.Empty, description, componentId); + if (null != classId) + { + this.Core.CreateRegistryRow(sourceLineNumbers, RegistryRootType.ClassesRoot, String.Concat(progId, "\\CLSID"), String.Empty, classId, componentId); + if (null != parent) // if this is a version independent ProgId + { + if (YesNoType.Yes == firstProgIdForClass) + { + this.Core.CreateRegistryRow(sourceLineNumbers, RegistryRootType.ClassesRoot, String.Concat("CLSID\\", classId, "\\VersionIndependentProgID"), String.Empty, progId, componentId); + } + + this.Core.CreateRegistryRow(sourceLineNumbers, RegistryRootType.ClassesRoot, String.Concat(progId, "\\CurVer"), String.Empty, parent, componentId); + } + else + { + if (YesNoType.Yes == firstProgIdForClass) + { + this.Core.CreateRegistryRow(sourceLineNumbers, RegistryRootType.ClassesRoot, String.Concat("CLSID\\", classId, "\\ProgID"), String.Empty, progId, componentId); + } + } + } + + if (null != icon) // ProgId's Default Icon + { + this.Core.CreateSimpleReference(sourceLineNumbers, SymbolDefinitions.File, icon); + + icon = String.Format(CultureInfo.InvariantCulture, "\"[#{0}]\"", icon); + + if (CompilerConstants.IntegerNotSet != iconIndex) + { + icon = String.Concat(icon, ",", iconIndex); + } + + this.Core.CreateRegistryRow(sourceLineNumbers, RegistryRootType.ClassesRoot, String.Concat(progId, "\\DefaultIcon"), String.Empty, icon, componentId); + } + } + + if (null != noOpen) + { + this.Core.CreateRegistryRow(sourceLineNumbers, RegistryRootType.ClassesRoot, progId, "NoOpen", noOpen, componentId); // ProgId NoOpen name + } + + // raise an error for an orphaned ProgId + if (YesNoType.Yes == advertise && !foundExtension && null == parent && null == classId) + { + this.Core.Write(WarningMessages.OrphanedProgId(sourceLineNumbers, progId)); + } + + return progId; + } + + /// + /// Parses a property element. + /// + /// Element to parse. + private void ParsePropertyElement(XElement node) + { + var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node); + Identifier id = null; + var admin = false; + var complianceCheck = false; + var hidden = false; + var secure = false; + var suppressModularization = YesNoType.NotSet; + string value = null; + + foreach (var attrib in node.Attributes()) + { + if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace) + { + switch (attrib.Name.LocalName) + { + case "Id": + id = this.Core.GetAttributeIdentifier(sourceLineNumbers, attrib); + break; + case "Admin": + admin = YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib); + break; + case "ComplianceCheck": + complianceCheck = YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib); + break; + case "Hidden": + hidden = YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib); + break; + case "Secure": + secure = YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib); + break; + case "SuppressModularization": + suppressModularization = this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib); + break; + case "Value": + value = this.Core.GetAttributeValue(sourceLineNumbers, attrib); + break; + default: + this.Core.UnexpectedAttribute(node, attrib); + break; + } + } + else + { + this.Core.ParseExtensionAttribute(node, attrib); + } + } + + if (null == id) + { + this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Id")); + id = Identifier.Invalid; + } + else if ("ProductID" == id.Id) + { + this.Core.Write(WarningMessages.ProductIdAuthored(sourceLineNumbers)); + } + else if ("SecureCustomProperties" == id.Id || "AdminProperties" == id.Id || "MsiHiddenProperties" == id.Id) + { + this.Core.Write(ErrorMessages.CannotAuthorSpecialProperties(sourceLineNumbers, id.Id)); + } + + this.Core.VerifyNoInnerText(sourceLineNumbers, node); + + if ("ErrorDialog" == id.Id) + { + this.Core.CreateSimpleReference(sourceLineNumbers, SymbolDefinitions.Dialog, value); + } + + foreach (var child in node.Elements()) + { + if (CompilerCore.WixNamespace == child.Name.Namespace) + { + { + switch (child.Name.LocalName) + { + case "ProductSearch": + this.ParseProductSearchElement(child, id.Id); + secure = true; + break; + default: + // let ParseSearchSignatures handle standard AppSearch children and unknown elements + break; + } + } + } + } + + // see if this property is used for appSearch + var signatures = this.ParseSearchSignatures(node); + + // If we're doing CCP then there must be a signature. + if (complianceCheck && 0 == signatures.Count) + { + this.Core.Write(ErrorMessages.SearchElementRequiredWithAttribute(sourceLineNumbers, node.Name.LocalName, "ComplianceCheck", "yes")); + } + + foreach (var sig in signatures) + { + if (complianceCheck && !this.Core.EncounteredError) + { + this.Core.AddSymbol(new CCPSearchSymbol(sourceLineNumbers, new Identifier(AccessModifier.Private, sig))); + } + + this.AddAppSearch(sourceLineNumbers, id, sig); + } + + // If we're doing AppSearch get that setup. + if (0 < signatures.Count) + { + this.AddProperty(sourceLineNumbers, id, value, admin, secure, hidden, false); + } + else // just a normal old property. + { + // If the property value is empty and none of the flags are set, print out a warning that we're ignoring + // the element. + if (String.IsNullOrEmpty(value) && !admin && !secure && !hidden) + { + this.Core.Write(WarningMessages.PropertyUseless(sourceLineNumbers, id.Id)); + } + else // there is a value and/or a flag set, do that. + { + this.AddProperty(sourceLineNumbers, id, value, admin, secure, hidden, false); + } + } + + if (!this.Core.EncounteredError && YesNoType.Yes == suppressModularization) + { + this.Core.Write(WarningMessages.PropertyModularizationSuppressed(sourceLineNumbers)); + + this.Core.AddSymbol(new WixSuppressModularizationSymbol(sourceLineNumbers, id)); + } + } + + /// + /// Parses a RegistryKey element. + /// + /// Element to parse. + /// Identifier for parent component. + /// Root specified when element is nested under another Registry element, otherwise CompilerConstants.IntegerNotSet. + /// Parent key for this Registry element when nested. + /// true if the component is 64-bit. + /// Identifier of this registry key since it could be the component's keypath. + /// Yes if this element was marked as the parent component's key path, No if explicitly marked as not being a key path, or NotSet otherwise. + private YesNoType ParseRegistryKeyElement(XElement node, string componentId, RegistryRootType? root, string parentKey, bool win64Component, out string possibleKeyPath) + { + var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node); + Identifier id = null; + var key = parentKey; // default to parent key path + var forceCreateOnInstall = false; + var forceDeleteOnUninstall = false; + var keyPath = YesNoType.NotSet; + + possibleKeyPath = null; + + foreach (var attrib in node.Attributes()) + { + if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace) + { + switch (attrib.Name.LocalName) + { + case "Id": + id = this.Core.GetAttributeIdentifier(sourceLineNumbers, attrib); + break; + case "Action": + this.Core.Write(WarningMessages.DeprecatedRegistryKeyActionAttribute(sourceLineNumbers)); + var actionValue = this.Core.GetAttributeValue(sourceLineNumbers, attrib); + switch (actionValue) + { + case "create": + forceCreateOnInstall = true; + break; + case "createAndRemoveOnUninstall": + forceCreateOnInstall = true; + forceDeleteOnUninstall = true; + break; + case "none": + break; + case "": + break; + default: + this.Core.Write(ErrorMessages.IllegalAttributeValue(sourceLineNumbers, node.Name.LocalName, attrib.Name.LocalName, actionValue, "create", "createAndRemoveOnUninstall", "none")); + break; + } + break; + case "ForceCreateOnInstall": + forceCreateOnInstall = YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib); + break; + case "ForceDeleteOnUninstall": + forceDeleteOnUninstall = YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib); + break; + case "Key": + key = this.Core.GetAttributeValue(sourceLineNumbers, attrib); + if (null != parentKey) + { + key = Path.Combine(parentKey, key); + } + key = key?.TrimEnd('\\'); + break; + case "Root": + if (root.HasValue) + { + this.Core.Write(ErrorMessages.RegistryRootInvalid(sourceLineNumbers)); + } + + root = this.Core.GetAttributeRegistryRootValue(sourceLineNumbers, attrib, true); + break; + default: + this.Core.UnexpectedAttribute(node, attrib); + break; + } + } + else + { + this.Core.ParseExtensionAttribute(node, attrib); + } + } + + var name = forceCreateOnInstall ? (forceDeleteOnUninstall ? "*" : "+") : (forceDeleteOnUninstall ? "-" : null); + + if (forceCreateOnInstall || forceDeleteOnUninstall) // generates a Registry row, so an Id must be present + { + // generate the identifier if it wasn't provided + if (null == id) + { + id = this.Core.CreateIdentifier("reg", componentId, ((int)root).ToString(CultureInfo.InvariantCulture.NumberFormat), LowercaseOrNull(key), LowercaseOrNull(name)); + } + } + else // does not generate a Registry row, so no Id should be present + { + if (null != id) + { + this.Core.Write(ErrorMessages.IllegalAttributeWithoutOtherAttributes(sourceLineNumbers, node.Name.LocalName, "Id", "ForceCreateOnInstall", "ForceDeleteOnUninstall", "yes", true)); + } + } + + if (!root.HasValue) + { + this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Root")); + } + + if (null == key) + { + this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Key")); + key = String.Empty; // set the key to something to prevent null reference exceptions + } + + foreach (var child in node.Elements()) + { + if (CompilerCore.WixNamespace == child.Name.Namespace) + { + string possibleChildKeyPath = null; + + switch (child.Name.LocalName) + { + case "RegistryKey": + if (YesNoType.Yes == this.ParseRegistryKeyElement(child, componentId, root, key, win64Component, out possibleChildKeyPath)) + { + if (YesNoType.Yes == keyPath) + { + this.Core.Write(ErrorMessages.ComponentMultipleKeyPaths(sourceLineNumbers, child.Name.LocalName, "KeyPath", "yes", "File", "RegistryValue", "ODBCDataSource")); + } + + possibleKeyPath = possibleChildKeyPath; // the child is the key path + keyPath = YesNoType.Yes; + } + else if (null == possibleKeyPath && null != possibleChildKeyPath) + { + possibleKeyPath = possibleChildKeyPath; + } + break; + case "RegistryValue": + if (YesNoType.Yes == this.ParseRegistryValueElement(child, componentId, root, key, win64Component, out possibleChildKeyPath)) + { + if (YesNoType.Yes == keyPath) + { + this.Core.Write(ErrorMessages.ComponentMultipleKeyPaths(sourceLineNumbers, child.Name.LocalName, "KeyPath", "yes", "File", "RegistryValue", "ODBCDataSource")); + } + + possibleKeyPath = possibleChildKeyPath; // the child is the key path + keyPath = YesNoType.Yes; + } + else if (null == possibleKeyPath && null != possibleChildKeyPath) + { + possibleKeyPath = possibleChildKeyPath; + } + break; + case "Permission": + if (!forceCreateOnInstall) + { + this.Core.Write(ErrorMessages.UnexpectedElementWithAttributeValue(sourceLineNumbers, node.Name.LocalName, child.Name.LocalName, "ForceCreateOnInstall", "yes")); + } + this.ParsePermissionElement(child, id.Id, "Registry"); + break; + case "PermissionEx": + if (!forceCreateOnInstall) + { + this.Core.Write(ErrorMessages.UnexpectedElementWithAttributeValue(sourceLineNumbers, node.Name.LocalName, child.Name.LocalName, "ForceCreateOnInstall", "yes")); + } + this.ParsePermissionExElement(child, id.Id, "Registry"); + break; + default: + this.Core.UnexpectedElement(node, child); + break; + } + } + else + { + var context = new Dictionary() { { "RegistryId", id?.Id }, { "ComponentId", componentId }, { "Win64", win64Component.ToString() } }; + this.Core.ParseExtensionElement(node, child, context); + } + } + + if (!this.Core.EncounteredError && null != name) + { + this.Core.AddSymbol(new RegistrySymbol(sourceLineNumbers, id) + { + Root = root.Value, + Key = key, + Name = name, + ComponentRef = componentId, + }); + } + + return keyPath; + } + + /// + /// Parses a RegistryValue element. + /// + /// Element to parse. + /// Identifier for parent component. + /// Root specified when element is nested under a RegistryKey element, otherwise CompilerConstants.IntegerNotSet. + /// Root specified when element is nested under a RegistryKey element, otherwise CompilerConstants.IntegerNotSet. + /// true if the component is 64-bit. + /// Identifier of this registry key since it could be the component's keypath. + /// Yes if this element was marked as the parent component's key path, No if explicitly marked as not being a key path, or NotSet otherwise. + private YesNoType ParseRegistryValueElement(XElement node, string componentId, RegistryRootType? root, string parentKey, bool win64Component, out string possibleKeyPath) + { + var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node); + Identifier id = null; + var key = parentKey; // default to parent key path + string name = null; + string value = null; + string action = null; + var valueType = RegistryValueType.String; + var actionType = RegistryValueActionType.Write; + var keyPath = YesNoType.NotSet; + + possibleKeyPath = null; + + foreach (var attrib in node.Attributes()) + { + if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace) + { + switch (attrib.Name.LocalName) + { + case "Id": + id = this.Core.GetAttributeIdentifier(sourceLineNumbers, attrib); + break; + case "Action": + var actionValue = this.Core.GetAttributeValue(sourceLineNumbers, attrib); + switch (actionValue) + { + case "append": + actionType = RegistryValueActionType.Append; + break; + case "prepend": + actionType = RegistryValueActionType.Prepend; + break; + case "write": + actionType = RegistryValueActionType.Write; + break; + case "": + break; + default: + this.Core.Write(ErrorMessages.IllegalAttributeValue(sourceLineNumbers, node.Name.LocalName, attrib.Name.LocalName, actionValue, "append", "prepend", "write")); + break; + } + break; + case "Key": + key = this.Core.GetAttributeValue(sourceLineNumbers, attrib); + if (null != parentKey) + { + if (parentKey.EndsWith("\\", StringComparison.Ordinal)) + { + key = String.Concat(parentKey, key); + } + else + { + key = String.Concat(parentKey, "\\", key); + } + } + break; + case "KeyPath": + keyPath = this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib); + break; + case "Name": + name = this.Core.GetAttributeValue(sourceLineNumbers, attrib); + break; + case "Root": + if (root.HasValue) + { + this.Core.Write(ErrorMessages.RegistryRootInvalid(sourceLineNumbers)); + } + + root = this.Core.GetAttributeRegistryRootValue(sourceLineNumbers, attrib, true); + break; + case "Type": + var typeValue = this.Core.GetAttributeValue(sourceLineNumbers, attrib); + switch (typeValue) + { + case "binary": + valueType = RegistryValueType.Binary; + break; + case "expandable": + valueType = RegistryValueType.Expandable; + break; + case "integer": + valueType = RegistryValueType.Integer; + break; + case "multiString": + valueType = RegistryValueType.MultiString; + break; + case "string": + valueType = RegistryValueType.String; + break; + case "": + break; + default: + this.Core.Write(ErrorMessages.IllegalAttributeValue(sourceLineNumbers, node.Name.LocalName, attrib.Name.LocalName, typeValue, "binary", "expandable", "integer", "multiString", "string")); + break; + } + break; + case "Value": + value = this.Core.GetAttributeValue(sourceLineNumbers, attrib, EmptyRule.CanBeEmpty); + break; + default: + this.Core.UnexpectedAttribute(node, attrib); + break; + } + } + else + { + this.Core.ParseExtensionAttribute(node, attrib); + } + } + + // generate the identifier if it wasn't provided + if (null == id) + { + id = this.Core.CreateIdentifier("reg", componentId, ((int)(root ?? RegistryRootType.Unknown)).ToString(), LowercaseOrNull(key), LowercaseOrNull(name)); + } + + if (RegistryValueType.MultiString != valueType && (RegistryValueActionType.Append == actionType || RegistryValueActionType.Prepend == actionType)) + { + this.Core.Write(ErrorMessages.IllegalAttributeValueWithoutOtherAttribute(sourceLineNumbers, node.Name.LocalName, "Action", action, "Type", "multiString")); + } + + if (null == key) + { + this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Key")); + } + + if (!root.HasValue) + { + this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Root")); + } + + foreach (var child in node.Elements()) + { + if (CompilerCore.WixNamespace == child.Name.Namespace) + { + switch (child.Name.LocalName) + { + case "MultiString": + case "MultiStringValue": + if (RegistryValueType.MultiString != valueType && null != value) + { + this.Core.Write(ErrorMessages.RegistryMultipleValuesWithoutMultiString(sourceLineNumbers, node.Name.LocalName, "Value", child.Name.LocalName, "Type")); + } + else + { + value = this.ParseRegistryMultiStringElement(child, value); + } + break; + case "Permission": + this.ParsePermissionElement(child, id.Id, "Registry"); + break; + case "PermissionEx": + this.ParsePermissionExElement(child, id.Id, "Registry"); + break; + default: + this.Core.UnexpectedElement(node, child); + break; + } + } + else + { + var context = new Dictionary() { { "RegistryId", id?.Id }, { "ComponentId", componentId }, { "Win64", win64Component.ToString() } }; + this.Core.ParseExtensionElement(node, child, context); + } + } + + //switch (typeType) + //{ + //case Wix.RegistryValue.TypeType.binary: + // value = String.Concat("#x", value); + // break; + //case Wix.RegistryValue.TypeType.expandable: + // value = String.Concat("#%", value); + // break; + //case Wix.RegistryValue.TypeType.integer: + // value = String.Concat("#", value); + // break; + //case Wix.RegistryValue.TypeType.multiString: + // switch (actionType) + // { + // case Wix.RegistryValue.ActionType.append: + // value = String.Concat("[~]", value); + // break; + // case Wix.RegistryValue.ActionType.prepend: + // value = String.Concat(value, "[~]"); + // break; + // case Wix.RegistryValue.ActionType.write: + // default: + // if (null != value && -1 == value.IndexOf("[~]", StringComparison.Ordinal)) + // { + // value = String.Format(CultureInfo.InvariantCulture, "[~]{0}[~]", value); + // } + // break; + // } + // break; + //case Wix.RegistryValue.TypeType.@string: + // // escape the leading '#' character for string registry keys + // if (null != value && value.StartsWith("#", StringComparison.Ordinal)) + // { + // value = String.Concat("#", value); + // } + // break; + //} + + // value may be set by child MultiStringValue elements, so it must be checked here + if (null == value && valueType != RegistryValueType.Binary) + { + this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Value")); + } + else if (0 == value?.Length && ("+" == name || "-" == name || "*" == name)) // prevent accidental authoring of special name values + { + this.Core.Write(ErrorMessages.RegistryNameValueIncorrect(sourceLineNumbers, node.Name.LocalName, "Name", name)); + } + + if (!this.Core.EncounteredError) + { + this.Core.AddSymbol(new RegistrySymbol(sourceLineNumbers, id) + { + Root = root.Value, + Key = key, + Name = name, + Value = value, + ValueType = valueType, + ValueAction = actionType, + ComponentRef = componentId, + }); + } + + // If this was just a regular registry key (that could be the key path) + // and no child registry key set the possible key path, let's make this + // Registry/@Id a possible key path. + if (null == possibleKeyPath) + { + possibleKeyPath = id.Id; + } + + return keyPath; + } + + private string ParseRegistryMultiStringElement(XElement node, string value) + { + var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node); + string multiStringValue = null; + + foreach (var attrib in node.Attributes()) + { + if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace) + { + switch (attrib.Name.LocalName) + { + case "Value": + multiStringValue = this.Core.GetAttributeValue(sourceLineNumbers, attrib); + break; + default: + this.Core.UnexpectedAttribute(node, attrib); + break; + } + } + } + + this.Core.VerifyNoInnerText(sourceLineNumbers, node); + + this.Core.ParseForExtensionElements(node); + + return null == value ? multiStringValue ?? "[~]" : String.Concat(value, "[~]", multiStringValue); + } + + /// + /// Parses a RemoveRegistryKey element. + /// + /// The element to parse. + /// The component identifier of the parent element. + private void ParseRemoveRegistryKeyElement(XElement node, string componentId) + { + var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node); + Identifier id = null; + RemoveRegistryActionType? actionType = null; + string key = null; + var name = "-"; + RegistryRootType? root = null; + + foreach (var attrib in node.Attributes()) + { + if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace) + { + switch (attrib.Name.LocalName) + { + case "Id": + id = this.Core.GetAttributeIdentifier(sourceLineNumbers, attrib); + break; + case "Action": + var actionValue = this.Core.GetAttributeValue(sourceLineNumbers, attrib); + switch (actionValue) + { + case "removeOnInstall": + actionType = RemoveRegistryActionType.RemoveOnInstall; + break; + case "removeOnUninstall": + actionType = RemoveRegistryActionType.RemoveOnUninstall; + break; + case "": + break; + default: + this.Core.Write(ErrorMessages.IllegalAttributeValue(sourceLineNumbers, node.Name.LocalName, attrib.Name.LocalName, actionValue, "removeOnInstall", "removeOnUninstall")); + break; + } + //if (0 < action.Length) + //{ + // if (!Wix.RemoveRegistryKey.TryParseActionType(action, out actionType)) + // { + // this.Core.Write(ErrorMessages.IllegalAttributeValue(sourceLineNumbers, node.Name.LocalName, attrib.Name.LocalName, action, "removeOnInstall", "removeOnUninstall")); + // } + //} + break; + case "Key": + key = this.Core.GetAttributeValue(sourceLineNumbers, attrib); + break; + case "Root": + root = this.Core.GetAttributeRegistryRootValue(sourceLineNumbers, attrib, true); + break; + default: + this.Core.UnexpectedAttribute(node, attrib); + break; + } + } + else + { + this.Core.ParseExtensionAttribute(node, attrib); + } + } + + // generate the identifier if it wasn't provided + if (null == id) + { + id = this.Core.CreateIdentifier("reg", componentId, ((int)root).ToString(CultureInfo.InvariantCulture.NumberFormat), LowercaseOrNull(key), LowercaseOrNull(name)); + } + + if (!root.HasValue) + { + this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Root")); + } + + if (null == key) + { + this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Key")); + } + + if (!actionType.HasValue) + { + this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Action")); + } + + this.Core.ParseForExtensionElements(node); + + if (!this.Core.EncounteredError) + { + this.Core.AddSymbol(new RemoveRegistrySymbol(sourceLineNumbers, id) + { + Root = root.Value, + Key = key, + Name = name, + Action = actionType.Value, + ComponentRef = componentId, + }); + } + } + + /// + /// Parses a RemoveRegistryValue element. + /// + /// The element to parse. + /// The component identifier of the parent element. + private void ParseRemoveRegistryValueElement(XElement node, string componentId) + { + var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node); + Identifier id = null; + string key = null; + string name = null; + RegistryRootType? root = null; + + foreach (var attrib in node.Attributes()) + { + if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace) + { + switch (attrib.Name.LocalName) + { + case "Id": + id = this.Core.GetAttributeIdentifier(sourceLineNumbers, attrib); + break; + case "Key": + key = this.Core.GetAttributeValue(sourceLineNumbers, attrib); + break; + case "Name": + name = this.Core.GetAttributeValue(sourceLineNumbers, attrib); + break; + case "Root": + root = this.Core.GetAttributeRegistryRootValue(sourceLineNumbers, attrib, true); + break; + default: + this.Core.UnexpectedAttribute(node, attrib); + break; + } + } + else + { + this.Core.ParseExtensionAttribute(node, attrib); + } + } + + // generate the identifier if it wasn't provided + if (null == id) + { + id = this.Core.CreateIdentifier("reg", componentId, ((int)root).ToString(CultureInfo.InvariantCulture.NumberFormat), LowercaseOrNull(key), LowercaseOrNull(name)); + } + + if (!root.HasValue) + { + this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Root")); + } + + if (null == key) + { + this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Key")); + } + + this.Core.ParseForExtensionElements(node); + + if (!this.Core.EncounteredError) + { + this.Core.AddSymbol(new RemoveRegistrySymbol(sourceLineNumbers, id) + { + Root = root.Value, + Key = key, + Name = name, + ComponentRef = componentId + }); + } + } + + /// + /// Parses a remove file element. + /// + /// Element to parse. + /// Identifier of parent component. + /// Identifier of the parent component's directory. + private void ParseRemoveFileElement(XElement node, string componentId, string parentDirectory) + { + var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node); + Identifier id = null; + string directory = null; + string name = null; + bool? onInstall = null; + bool? onUninstall = null; + string property = null; + string shortName = null; + + foreach (var attrib in node.Attributes()) + { + if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace) + { + switch (attrib.Name.LocalName) + { + case "Id": + id = this.Core.GetAttributeIdentifier(sourceLineNumbers, attrib); + break; + case "Directory": + directory = this.Core.CreateDirectoryReferenceFromInlineSyntax(sourceLineNumbers, attrib, parentDirectory); + break; + case "Name": + name = this.Core.GetAttributeLongFilename(sourceLineNumbers, attrib, true); + break; + case "On": + var onValue = this.Core.GetAttributeValue(sourceLineNumbers, attrib); + switch (onValue) + { + case "install": + onInstall = true; + break; + case "uninstall": + onUninstall = true; + break; + case "both": + onInstall = true; + onUninstall = true; + break; + } + break; + case "Property": + property = this.Core.GetAttributeIdentifierValue(sourceLineNumbers, attrib); + break; + case "ShortName": + shortName = this.Core.GetAttributeShortFilename(sourceLineNumbers, attrib, true); + break; + default: + this.Core.UnexpectedAttribute(node, attrib); + break; + } + } + else + { + this.Core.ParseExtensionAttribute(node, attrib); + } + } + + if (null == name) + { + this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Name")); + } + + if (!onInstall.HasValue && !onUninstall.HasValue) + { + this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "On")); + } + + if (null != directory && null != property) + { + this.Core.Write(ErrorMessages.IllegalAttributeWithOtherAttribute(sourceLineNumbers, node.Name.LocalName, "Property", "Directory", directory)); + } + + if (null == id) + { + var on = (onInstall == true && onUninstall == true) ? 3 : (onUninstall == true) ? 2 : (onInstall == true) ? 1 : 0; + id = this.Core.CreateIdentifier("rmf", directory ?? property ?? parentDirectory, LowercaseOrNull(shortName), LowercaseOrNull(name), on.ToString()); + } + + this.Core.ParseForExtensionElements(node); + + if (!this.Core.EncounteredError) + { + this.Core.AddSymbol(new RemoveFileSymbol(sourceLineNumbers, id) + { + ComponentRef = componentId, + FileName = name, + ShortFileName = shortName, + DirPropertyRef = directory ?? property ?? parentDirectory, + OnInstall = onInstall, + OnUninstall = onUninstall, + }); + } + } + + /// + /// Parses a RemoveFolder element. + /// + /// Element to parse. + /// Identifier of parent component. + /// Identifier of parent component's directory. + private void ParseRemoveFolderElement(XElement node, string componentId, string parentDirectory) + { + var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node); + Identifier id = null; + string directory = null; + bool? onInstall = null; + bool? onUninstall = null; + string property = null; + + foreach (var attrib in node.Attributes()) + { + if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace) + { + switch (attrib.Name.LocalName) + { + case "Id": + id = this.Core.GetAttributeIdentifier(sourceLineNumbers, attrib); + break; + case "Directory": + directory = this.Core.CreateDirectoryReferenceFromInlineSyntax(sourceLineNumbers, attrib, parentDirectory); + break; + case "On": + var onValue = this.Core.GetAttributeValue(sourceLineNumbers, attrib); + switch (onValue) + { + case "install": + onInstall = true; + break; + case "uninstall": + onUninstall = true; + break; + case "both": + onInstall = true; + onUninstall = true; + break; + } + break; + case "Property": + property = this.Core.GetAttributeValue(sourceLineNumbers, attrib); + break; + default: + this.Core.UnexpectedAttribute(node, attrib); + break; + } + } + else + { + this.Core.ParseExtensionAttribute(node, attrib); + } + } + + if (!onInstall.HasValue && !onUninstall.HasValue) + { + this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "On")); + } + + if (null != directory && null != property) + { + this.Core.Write(ErrorMessages.IllegalAttributeWithOtherAttribute(sourceLineNumbers, node.Name.LocalName, "Property", "Directory", directory)); + } + + if (null == id) + { + var on = (onInstall == true && onUninstall == true) ? 3 : (onUninstall == true) ? 2 : (onInstall == true) ? 1 : 0; + id = this.Core.CreateIdentifier("rmf", directory ?? property ?? parentDirectory, on.ToString()); + } + + this.Core.ParseForExtensionElements(node); + + if (!this.Core.EncounteredError) + { + this.Core.AddSymbol(new RemoveFileSymbol(sourceLineNumbers, id) + { + ComponentRef = componentId, + DirPropertyRef = directory ?? property ?? parentDirectory, + OnInstall = onInstall, + OnUninstall = onUninstall + }); + } + } + + /// + /// Parses a reserve cost element. + /// + /// Element to parse. + /// Identifier of parent component. + /// Optional and default identifier of referenced directory. + private void ParseReserveCostElement(XElement node, string componentId, string directoryId) + { + var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node); + Identifier id = null; + var runFromSource = CompilerConstants.IntegerNotSet; + var runLocal = CompilerConstants.IntegerNotSet; + + foreach (var attrib in node.Attributes()) + { + if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace) + { + switch (attrib.Name.LocalName) + { + case "Id": + id = this.Core.GetAttributeIdentifier(sourceLineNumbers, attrib); + break; + case "Directory": + directoryId = this.Core.CreateDirectoryReferenceFromInlineSyntax(sourceLineNumbers, attrib, directoryId); + break; + case "RunFromSource": + runFromSource = this.Core.GetAttributeIntegerValue(sourceLineNumbers, attrib, 0, Int32.MaxValue); + break; + case "RunLocal": + runLocal = this.Core.GetAttributeIntegerValue(sourceLineNumbers, attrib, 0, Int32.MaxValue); + break; + default: + this.Core.UnexpectedAttribute(node, attrib); + break; + } + } + else + { + this.Core.ParseExtensionAttribute(node, attrib); + } + } + + if (null == id) + { + id = this.Core.CreateIdentifier("rc", componentId, directoryId); + } + + if (CompilerConstants.IntegerNotSet == runFromSource) + { + this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "RunFromSource")); + } + + if (CompilerConstants.IntegerNotSet == runLocal) + { + this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "RunLocal")); + } + + this.Core.ParseForExtensionElements(node); + + if (!this.Core.EncounteredError) + { + this.Core.AddSymbol(new ReserveCostSymbol(sourceLineNumbers, id) + { + ComponentRef = componentId, + ReserveFolder = directoryId, + ReserveLocal = runLocal, + ReserveSource = runFromSource + }); + } + } + + /// + /// Parses a sequence element. + /// + /// Element to parse. + /// Name of sequence table. + private void ParseSequenceElement(XElement node, SequenceTable sequenceTable) + { + // Parse each action in the sequence. + foreach (var child in node.Elements()) + { + var childSourceLineNumbers = Preprocessor.GetSourceLineNumbers(child); + var actionName = child.Name.LocalName; + string afterAction = null; + string beforeAction = null; + string condition = null; + var customAction = "Custom" == actionName; + var overridable = false; + var exitSequence = CompilerConstants.IntegerNotSet; + var sequence = CompilerConstants.IntegerNotSet; + var showDialog = "Show" == actionName; + var specialAction = "InstallExecute" == actionName || "InstallExecuteAgain" == actionName || "RemoveExistingProducts" == actionName || "DisableRollback" == actionName || "ScheduleReboot" == actionName || "ForceReboot" == actionName || "ResolveSource" == actionName; + var specialStandardAction = "AppSearch" == actionName || "CCPSearch" == actionName || "RMCCPSearch" == actionName || "LaunchConditions" == actionName || "FindRelatedProducts" == actionName; + var suppress = false; + + foreach (var attrib in child.Attributes()) + { + if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace) + { + switch (attrib.Name.LocalName) + { + case "Action": + if (customAction) + { + actionName = this.Core.GetAttributeIdentifierValue(childSourceLineNumbers, attrib); + this.Core.CreateSimpleReference(childSourceLineNumbers, SymbolDefinitions.CustomAction, actionName); + } + else + { + this.Core.UnexpectedAttribute(child, attrib); + } + break; + case "After": + if (customAction || showDialog || specialAction || specialStandardAction) + { + afterAction = this.Core.GetAttributeIdentifierValue(childSourceLineNumbers, attrib); + this.Core.CreateSimpleReference(childSourceLineNumbers, SymbolDefinitions.WixAction, sequenceTable.ToString(), afterAction); + } + else + { + this.Core.UnexpectedAttribute(child, attrib); + } + break; + case "Before": + if (customAction || showDialog || specialAction || specialStandardAction) + { + beforeAction = this.Core.GetAttributeIdentifierValue(childSourceLineNumbers, attrib); + this.Core.CreateSimpleReference(childSourceLineNumbers, SymbolDefinitions.WixAction, sequenceTable.ToString(), beforeAction); + } + else + { + this.Core.UnexpectedAttribute(child, attrib); + } + break; + case "Condition": + condition = this.Core.GetAttributeValue(childSourceLineNumbers, attrib); + break; + case "Dialog": + if (showDialog) + { + actionName = this.Core.GetAttributeIdentifierValue(childSourceLineNumbers, attrib); + this.Core.CreateSimpleReference(childSourceLineNumbers, SymbolDefinitions.Dialog, actionName); + } + else + { + this.Core.UnexpectedAttribute(child, attrib); + } + break; + case "OnExit": + if (customAction || showDialog || specialAction) + { + var exitValue = this.Core.GetAttributeValue(childSourceLineNumbers, attrib); + switch (exitValue) + { + case "success": + exitSequence = -1; + break; + case "cancel": + exitSequence = -2; + break; + case "error": + exitSequence = -3; + break; + case "suspend": + exitSequence = -4; + break; + } + } + else + { + this.Core.UnexpectedAttribute(child, attrib); + } + break; + case "Overridable": + overridable = YesNoType.Yes == this.Core.GetAttributeYesNoValue(childSourceLineNumbers, attrib); + break; + case "Sequence": + sequence = this.Core.GetAttributeIntegerValue(childSourceLineNumbers, attrib, 1, Int16.MaxValue); + break; + case "Suppress": + suppress = YesNoType.Yes == this.Core.GetAttributeYesNoValue(childSourceLineNumbers, attrib); + break; + default: + this.Core.UnexpectedAttribute(node, attrib); + break; + } + } + else + { + this.Core.ParseExtensionAttribute(node, attrib); + } + } + + this.Core.VerifyNoInnerText(childSourceLineNumbers, node); + + if (customAction && "Custom" == actionName) + { + this.Core.Write(ErrorMessages.ExpectedAttribute(childSourceLineNumbers, child.Name.LocalName, "Action")); + } + else if (showDialog && "Show" == actionName) + { + this.Core.Write(ErrorMessages.ExpectedAttribute(childSourceLineNumbers, child.Name.LocalName, "Dialog")); + } + + if (CompilerConstants.IntegerNotSet != sequence) + { + if (CompilerConstants.IntegerNotSet != exitSequence) + { + this.Core.Write(ErrorMessages.IllegalAttributeWithOtherAttribute(childSourceLineNumbers, child.Name.LocalName, "Sequence", "OnExit")); + } + else if (null != beforeAction || null != afterAction) + { + this.Core.Write(ErrorMessages.IllegalAttributeWithOtherAttribute(childSourceLineNumbers, child.Name.LocalName, "Sequence", "Before", "After")); + } + } + else // sequence not specified use OnExit (which may also be not set). + { + sequence = exitSequence; + } + + if (null != beforeAction && null != afterAction) + { + this.Core.Write(ErrorMessages.IllegalAttributeWithOtherAttribute(childSourceLineNumbers, child.Name.LocalName, "After", "Before")); + } + else if ((customAction || showDialog || specialAction) && !suppress && CompilerConstants.IntegerNotSet == sequence && null == beforeAction && null == afterAction) + { + this.Core.Write(ErrorMessages.NeedSequenceBeforeOrAfter(childSourceLineNumbers, child.Name.LocalName)); + } + + // action that is scheduled to occur before/after itself + if (beforeAction == actionName) + { + this.Core.Write(ErrorMessages.ActionScheduledRelativeToItself(childSourceLineNumbers, child.Name.LocalName, "Before", beforeAction)); + } + else if (afterAction == actionName) + { + this.Core.Write(ErrorMessages.ActionScheduledRelativeToItself(childSourceLineNumbers, child.Name.LocalName, "After", afterAction)); + } + + // normal standard actions cannot be set overridable by the user (since they are overridable by default) + if (overridable && WindowsInstallerStandard.IsStandardAction(actionName) && !specialAction) + { + this.Core.Write(ErrorMessages.UnexpectedAttribute(childSourceLineNumbers, child.Name.LocalName, "Overridable")); + } + + // suppress cannot be specified at the same time as Before, After, or Sequence + if (suppress && (null != afterAction || null != beforeAction || CompilerConstants.IntegerNotSet != sequence || overridable)) + { + this.Core.Write(ErrorMessages.IllegalAttributeWithOtherAttributes(childSourceLineNumbers, child.Name.LocalName, "Suppress", "Before", "After", "Sequence", "Overridable")); + } + + this.Core.ParseForExtensionElements(child); + + // add the row and any references needed + if (!this.Core.EncounteredError) + { + if (suppress) + { + this.Core.AddSymbol(new WixSuppressActionSymbol(childSourceLineNumbers, new Identifier(AccessModifier.Public, sequenceTable, actionName)) + { + SequenceTable = sequenceTable, + Action = actionName + }); + } + else + { + var symbol = this.Core.AddSymbol(new WixActionSymbol(childSourceLineNumbers, new Identifier(AccessModifier.Public, sequenceTable, actionName)) + { + SequenceTable = sequenceTable, + Action = actionName, + Condition = condition, + Before = beforeAction, + After = afterAction, + Overridable = overridable, + }); + + if (CompilerConstants.IntegerNotSet != sequence) + { + symbol.Sequence = sequence; + } + } + } + } + } + + + /// + /// Parses a service config element. + /// + /// Element to parse. + /// Identifier of parent component. + /// Optional element containing parent's service name. + private void ParseServiceConfigElement(XElement node, string componentId, string serviceName) + { + var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node); + Identifier id = null; + string delayedAutoStart = null; + string failureActionsWhen = null; + var name = serviceName; + var install = false; + var reinstall = false; + var uninstall = false; + string preShutdownDelay = null; + string requiredPrivileges = null; + string sid = null; + + this.Core.Write(WarningMessages.ServiceConfigFamilyNotSupported(sourceLineNumbers, node.Name.LocalName)); + + foreach (var attrib in node.Attributes()) + { + if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace) + { + switch (attrib.Name.LocalName) + { + case "Id": + id = this.Core.GetAttributeIdentifier(sourceLineNumbers, attrib); + break; + case "DelayedAutoStart": + delayedAutoStart = this.Core.GetAttributeValue(sourceLineNumbers, attrib); + switch (delayedAutoStart) + { + case "no": + delayedAutoStart = "0"; + break; + case "yes": + delayedAutoStart = "1"; + break; + default: + // allow everything else to pass through that are hopefully "formatted" Properties. + break; + } + break; + case "FailureActionsWhen": + failureActionsWhen = this.Core.GetAttributeValue(sourceLineNumbers, attrib); + switch (failureActionsWhen) + { + case "failedToStop": + failureActionsWhen = "0"; + break; + case "failedToStopOrReturnedError": + failureActionsWhen = "1"; + break; + default: + // allow everything else to pass through that are hopefully "formatted" Properties. + break; + } + break; + case "OnInstall": + install = YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib); + //if (YesNoType.Yes == install) + //{ + // events |= MsiInterop.MsidbServiceConfigEventInstall; + //} + break; + case "OnReinstall": + reinstall = YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib); + //if (YesNoType.Yes == reinstall) + //{ + // events |= MsiInterop.MsidbServiceConfigEventReinstall; + //} + break; + case "OnUninstall": + uninstall = YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib); + //if (YesNoType.Yes == uninstall) + //{ + // events |= MsiInterop.MsidbServiceConfigEventUninstall; + //} + break; + default: + this.Core.UnexpectedAttribute(node, attrib); + break; + case "PreShutdownDelay": + preShutdownDelay = this.Core.GetAttributeValue(sourceLineNumbers, attrib, EmptyRule.CanBeEmpty); + break; + case "ServiceName": + if (!String.IsNullOrEmpty(serviceName)) + { + this.Core.Write(ErrorMessages.IllegalAttributeWhenNested(sourceLineNumbers, node.Name.LocalName, attrib.Name.LocalName, "ServiceInstall")); + } + + name = this.Core.GetAttributeValue(sourceLineNumbers, attrib); + break; + case "ServiceSid": + sid = this.Core.GetAttributeValue(sourceLineNumbers, attrib); + switch (sid) + { + case "none": + sid = "0"; + break; + case "restricted": + sid = "3"; + break; + case "unrestricted": + sid = "1"; + break; + default: + // allow everything else to pass through that are hopefully "formatted" Properties. + break; + } + break; + } + } + else + { + this.Core.ParseExtensionAttribute(node, attrib); + } + } + + // Get the ServiceConfig required privilegs. + foreach (var child in node.Elements()) + { + if (CompilerCore.WixNamespace == child.Name.Namespace) + { + switch (child.Name.LocalName) + { + case "RequiredPrivilege": + requiredPrivileges = this.ParseRequiredPrivilege(child, requiredPrivileges); + break; + default: + this.Core.UnexpectedElement(node, child); + break; + } + } + else + { + this.Core.ParseExtensionElement(node, child); + } + } + + if (String.IsNullOrEmpty(name)) + { + this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "ServiceName")); + } + else if (null == id) + { + id = this.Core.CreateIdentifierFromFilename(name); + } + + if (!install && !reinstall && !uninstall) + { + this.Core.Write(ErrorMessages.ExpectedAttributes(sourceLineNumbers, node.Name.LocalName, "OnInstall", "OnReinstall", "OnUninstall")); + } + + if (String.IsNullOrEmpty(delayedAutoStart) && String.IsNullOrEmpty(failureActionsWhen) && String.IsNullOrEmpty(preShutdownDelay) && String.IsNullOrEmpty(requiredPrivileges) && String.IsNullOrEmpty(sid)) + { + this.Core.Write(ErrorMessages.ExpectedAttributes(sourceLineNumbers, node.Name.LocalName, "DelayedAutoStart", "FailureActionsWhen", "PreShutdownDelay", "ServiceSid", "RequiredPrivilege")); + } + + if (!this.Core.EncounteredError) + { + if (!String.IsNullOrEmpty(delayedAutoStart)) + { + this.Core.AddSymbol(new MsiServiceConfigSymbol(sourceLineNumbers, new Identifier(id.Access, String.Concat(id.Id, ".DS"))) + { + Name = name, + OnInstall = install, + OnReinstall = reinstall, + OnUninstall = uninstall, + ConfigType = MsiServiceConfigType.DelayedAutoStart, + Argument = delayedAutoStart, + ComponentRef = componentId, + }); + } + + if (!String.IsNullOrEmpty(failureActionsWhen)) + { + this.Core.AddSymbol(new MsiServiceConfigSymbol(sourceLineNumbers, new Identifier(id.Access, String.Concat(id.Id, ".FA"))) + { + Name = name, + OnInstall = install, + OnReinstall = reinstall, + OnUninstall = uninstall, + ConfigType = MsiServiceConfigType.FailureActionsFlag, + Argument = failureActionsWhen, + ComponentRef = componentId, + }); + } + + if (!String.IsNullOrEmpty(sid)) + { + this.Core.AddSymbol(new MsiServiceConfigSymbol(sourceLineNumbers, new Identifier(id.Access, String.Concat(id.Id, ".SS"))) + { + Name = name, + OnInstall = install, + OnReinstall = reinstall, + OnUninstall = uninstall, + ConfigType = MsiServiceConfigType.ServiceSidInfo, + Argument = sid, + ComponentRef = componentId, + }); + } + + if (!String.IsNullOrEmpty(requiredPrivileges)) + { + this.Core.AddSymbol(new MsiServiceConfigSymbol(sourceLineNumbers, new Identifier(id.Access, String.Concat(id.Id, ".RP"))) + { + Name = name, + OnInstall = install, + OnReinstall = reinstall, + OnUninstall = uninstall, + ConfigType = MsiServiceConfigType.RequiredPrivilegesInfo, + Argument = requiredPrivileges, + ComponentRef = componentId, + }); + } + + if (!String.IsNullOrEmpty(preShutdownDelay)) + { + this.Core.AddSymbol(new MsiServiceConfigSymbol(sourceLineNumbers, new Identifier(id.Access, String.Concat(id.Id, ".PD"))) + { + Name = name, + OnInstall = install, + OnReinstall = reinstall, + OnUninstall = uninstall, + ConfigType = MsiServiceConfigType.PreshutdownInfo, + Argument = preShutdownDelay, + ComponentRef = componentId, + }); + } + } + } + + private string ParseRequiredPrivilege(XElement node, string requiredPrivileges) + { + var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node); + string privilege = null; + + foreach (var attrib in node.Attributes()) + { + if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace) + { + switch (attrib.Name.LocalName) + { + case "Name": + privilege = this.Core.GetAttributeValue(sourceLineNumbers, attrib); + switch (privilege) + { + case "assignPrimaryToken": + privilege = "SeAssignPrimaryTokenPrivilege"; + break; + case "audit": + privilege = "SeAuditPrivilege"; + break; + case "backup": + privilege = "SeBackupPrivilege"; + break; + case "changeNotify": + privilege = "SeChangeNotifyPrivilege"; + break; + case "createGlobal": + privilege = "SeCreateGlobalPrivilege"; + break; + case "createPagefile": + privilege = "SeCreatePagefilePrivilege"; + break; + case "createPermanent": + privilege = "SeCreatePermanentPrivilege"; + break; + case "createSymbolicLink": + privilege = "SeCreateSymbolicLinkPrivilege"; + break; + case "createToken": + privilege = "SeCreateTokenPrivilege"; + break; + case "debug": + privilege = "SeDebugPrivilege"; + break; + case "enableDelegation": + privilege = "SeEnableDelegationPrivilege"; + break; + case "impersonate": + privilege = "SeImpersonatePrivilege"; + break; + case "increaseBasePriority": + privilege = "SeIncreaseBasePriorityPrivilege"; + break; + case "increaseQuota": + privilege = "SeIncreaseQuotaPrivilege"; + break; + case "increaseWorkingSet": + privilege = "SeIncreaseWorkingSetPrivilege"; + break; + case "loadDriver": + privilege = "SeLoadDriverPrivilege"; + break; + case "lockMemory": + privilege = "SeLockMemoryPrivilege"; + break; + case "machineAccount": + privilege = "SeMachineAccountPrivilege"; + break; + case "manageVolume": + privilege = "SeManageVolumePrivilege"; + break; + case "profileSingleProcess": + privilege = "SeProfileSingleProcessPrivilege"; + break; + case "relabel": + privilege = "SeRelabelPrivilege"; + break; + case "remoteShutdown": + privilege = "SeRemoteShutdownPrivilege"; + break; + case "restore": + privilege = "SeRestorePrivilege"; + break; + case "security": + privilege = "SeSecurityPrivilege"; + break; + case "shutdown": + privilege = "SeShutdownPrivilege"; + break; + case "syncAgent": + privilege = "SeSyncAgentPrivilege"; + break; + case "systemEnvironment": + privilege = "SeSystemEnvironmentPrivilege"; + break; + case "systemProfile": + privilege = "SeSystemProfilePrivilege"; + break; + case "systemTime": + case "modifySystemTime": + privilege = "SeSystemtimePrivilege"; + break; + case "takeOwnership": + privilege = "SeTakeOwnershipPrivilege"; + break; + case "tcb": + case "trustedComputerBase": + privilege = "SeTcbPrivilege"; + break; + case "timeZone": + case "modifyTimeZone": + privilege = "SeTimeZonePrivilege"; + break; + case "trustedCredManAccess": + case "trustedCredentialManagerAccess": + privilege = "SeTrustedCredManAccessPrivilege"; + break; + case "undock": + privilege = "SeUndockPrivilege"; + break; + case "unsolicitedInput": + privilege = "SeUnsolicitedInputPrivilege"; + break; + default: + // allow everything else to pass through that are hopefully "formatted" Properties. + break; + } + break; + default: + this.Core.UnexpectedAttribute(node, attrib); + break; + } + } + } + + this.Core.VerifyNoInnerText(sourceLineNumbers, node); + + if (privilege == null) + { + this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Name")); + } + + this.Core.ParseForExtensionElements(node); + + return (requiredPrivileges == null) ? privilege : String.Concat(requiredPrivileges, "[~]", privilege); + } + + /// + /// Parses a service config failure actions element. + /// + /// Element to parse. + /// Identifier of parent component. + /// Optional element containing parent's service name. + private void ParseServiceConfigFailureActionsElement(XElement node, string componentId, string serviceName) + { + var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node); + Identifier id = null; + var name = serviceName; + var install = false; + var reinstall = false; + var uninstall = false; + int? resetPeriod = null; + string rebootMessage = null; + string command = null; + string actions = null; + string actionsDelays = null; + + this.Core.Write(WarningMessages.ServiceConfigFamilyNotSupported(sourceLineNumbers, node.Name.LocalName)); + + foreach (var attrib in node.Attributes()) + { + if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace) + { + switch (attrib.Name.LocalName) + { + case "Id": + id = this.Core.GetAttributeIdentifier(sourceLineNumbers, attrib); + break; + case "Command": + command = this.Core.GetAttributeValue(sourceLineNumbers, attrib, EmptyRule.CanBeEmpty); + break; + case "OnInstall": + install = YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib); + break; + case "OnReinstall": + reinstall = YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib); + break; + case "OnUninstall": + uninstall = YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib); + break; + case "RebootMessage": + rebootMessage = this.Core.GetAttributeValue(sourceLineNumbers, attrib, EmptyRule.CanBeEmpty); + break; + case "ResetPeriod": + resetPeriod = this.Core.GetAttributeIntegerValue(sourceLineNumbers, attrib, 0, Int32.MaxValue); + break; + case "ServiceName": + if (!String.IsNullOrEmpty(serviceName)) + { + this.Core.Write(ErrorMessages.IllegalAttributeWhenNested(sourceLineNumbers, node.Name.LocalName, attrib.Name.LocalName, "ServiceInstall")); + } + + name = this.Core.GetAttributeValue(sourceLineNumbers, attrib); + break; + default: + this.Core.UnexpectedAttribute(node, attrib); + break; + } + } + else + { + this.Core.ParseExtensionAttribute(node, attrib); + } + } + + // Get the ServiceConfigFailureActions actions. + foreach (var child in node.Elements()) + { + if (CompilerCore.WixNamespace == child.Name.Namespace) + { + switch (child.Name.LocalName) + { + case "Failure": + string action = null; + string delay = null; + var childSourceLineNumbers = Preprocessor.GetSourceLineNumbers(child); + + foreach (var childAttrib in child.Attributes()) + { + if (String.IsNullOrEmpty(childAttrib.Name.NamespaceName) || CompilerCore.WixNamespace == childAttrib.Name.Namespace) + { + switch (childAttrib.Name.LocalName) + { + case "Action": + action = this.Core.GetAttributeValue(childSourceLineNumbers, childAttrib); + switch (action) + { + case "none": + action = "0"; + break; + case "restartComputer": + action = "2"; + break; + case "restartService": + action = "1"; + break; + case "runCommand": + action = "3"; + break; + default: + // allow everything else to pass through that are hopefully "formatted" Properties. + break; + } + break; + case "Delay": + delay = this.Core.GetAttributeValue(childSourceLineNumbers, childAttrib); + break; + default: + this.Core.UnexpectedAttribute(child, childAttrib); + break; + } + } + } + + if (String.IsNullOrEmpty(action)) + { + this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, child.Name.LocalName, "Action")); + } + + if (String.IsNullOrEmpty(delay)) + { + this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, child.Name.LocalName, "Delay")); + } + + if (!String.IsNullOrEmpty(actions)) + { + actions = String.Concat(actions, "[~]"); + } + actions = String.Concat(actions, action); + + if (!String.IsNullOrEmpty(actionsDelays)) + { + actionsDelays = String.Concat(actionsDelays, "[~]"); + } + actionsDelays = String.Concat(actionsDelays, delay); + break; + default: + this.Core.UnexpectedElement(node, child); + break; + } + } + else + { + this.Core.ParseExtensionElement(node, child); + } + } + + if (String.IsNullOrEmpty(name)) + { + this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "ServiceName")); + } + else if (null == id) + { + id = this.Core.CreateIdentifierFromFilename(name); + } + + if (!install && !reinstall && !uninstall) + { + this.Core.Write(ErrorMessages.ExpectedAttributes(sourceLineNumbers, node.Name.LocalName, "OnInstall", "OnReinstall", "OnUninstall")); + } + + if (!this.Core.EncounteredError) + { + this.Core.AddSymbol(new MsiServiceConfigFailureActionsSymbol(sourceLineNumbers, id) + { + Name = name, + OnInstall = install, + OnReinstall = reinstall, + OnUninstall = uninstall, + ResetPeriod = resetPeriod, + RebootMessage = rebootMessage, + Command = command, + Actions = actions, + DelayActions = actionsDelays, + ComponentRef = componentId, + }); + } + } + + /// + /// Parses a service control element. + /// + /// Element to parse. + /// Identifier of parent component. + private void ParseServiceControlElement(XElement node, string componentId) + { + var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node); + string arguments = null; + Identifier id = null; + string name = null; + var installRemove = false; + var uninstallRemove = false; + var installStart = false; + var uninstallStart = false; + var installStop = false; + var uninstallStop = false; + bool? wait = null; + + foreach (var attrib in node.Attributes()) + { + if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace) + { + switch (attrib.Name.LocalName) + { + case "Id": + id = this.Core.GetAttributeIdentifier(sourceLineNumbers, attrib); + break; + case "Name": + name = this.Core.GetAttributeValue(sourceLineNumbers, attrib); + break; + case "Remove": + var removeValue = this.Core.GetAttributeValue(sourceLineNumbers, attrib); + switch (removeValue) + { + case "install": + installRemove = true; + break; + case "uninstall": + uninstallRemove = true; + break; + case "both": + installRemove = true; + uninstallRemove = true; + break; + case "": + break; + } + break; + case "Start": + var startValue = this.Core.GetAttributeValue(sourceLineNumbers, attrib); + switch (startValue) + { + case "install": + installStart = true; + break; + case "uninstall": + uninstallStart = true; + break; + case "both": + installStart = true; + uninstallStart = true; + break; + case "": + break; + } + break; + case "Stop": + var stopValue = this.Core.GetAttributeValue(sourceLineNumbers, attrib); + switch (stopValue) + { + case "install": + installStop = true; + break; + case "uninstall": + uninstallStop = true; + break; + case "both": + installStop = true; + uninstallStop = true; + break; + case "": + break; + } + break; + case "Wait": + wait = YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib); + break; + default: + this.Core.UnexpectedAttribute(node, attrib); + break; + } + } + else + { + this.Core.ParseExtensionAttribute(node, attrib); + } + } + + if (null == id) + { + id = this.Core.CreateIdentifierFromFilename(name); + } + + if (null == name) + { + this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Name")); + } + + // get the ServiceControl arguments + foreach (var child in node.Elements()) + { + if (CompilerCore.WixNamespace == child.Name.Namespace) + { + switch (child.Name.LocalName) + { + case "ServiceArgument": + arguments = this.ParseServiceArgument(child, arguments); + break; + default: + this.Core.UnexpectedElement(node, child); + break; + } + } + else + { + this.Core.ParseExtensionElement(node, child); + } + } + + if (!this.Core.EncounteredError) + { + this.Core.AddSymbol(new ServiceControlSymbol(sourceLineNumbers, id) + { + Name = name, + InstallRemove = installRemove, + UninstallRemove = uninstallRemove, + InstallStart = installStart, + UninstallStart = uninstallStart, + InstallStop = installStop, + UninstallStop = uninstallStop, + Arguments = arguments, + Wait = wait, + ComponentRef = componentId + }); + } + } + + private string ParseServiceArgument(XElement node, string arguments) + { + var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node); + string argument = null; + + foreach (var attrib in node.Attributes()) + { + if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace) + { + switch (attrib.Name.LocalName) + { + case "Value": + argument = this.Core.GetAttributeValue(sourceLineNumbers, attrib); + break; + default: + this.Core.UnexpectedAttribute(node, attrib); + break; + } + } + } + + this.Core.VerifyNoInnerText(sourceLineNumbers, node); + + if (argument == null) + { + this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Value")); + } + + this.Core.ParseForExtensionElements(node); + + return (arguments == null) ? argument : String.Concat(arguments, "[~]", argument); + } + + /// + /// Parses a service dependency element. + /// + /// Element to parse. + /// Parsed sevice dependency name. + private string ParseServiceDependencyElement(XElement node) + { + var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node); + string dependency = null; + var group = false; + + foreach (var attrib in node.Attributes()) + { + if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace) + { + switch (attrib.Name.LocalName) + { + case "Id": + dependency = this.Core.GetAttributeValue(sourceLineNumbers, attrib); + break; + case "Group": + group = YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib); + break; + default: + this.Core.UnexpectedAttribute(node, attrib); + break; + } + } + else + { + this.Core.ParseExtensionAttribute(node, attrib); + } + } + + if (null == dependency) + { + this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Id")); + } + + this.Core.ParseForExtensionElements(node); + + return group ? String.Concat("+", dependency) : dependency; + } + + /// + /// Parses a service install element. + /// + /// Element to parse. + /// Identifier of parent component. + /// + private void ParseServiceInstallElement(XElement node, string componentId, bool win64Component) + { + var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node); + Identifier id = null; + string account = null; + string arguments = null; + string dependencies = null; + string description = null; + string displayName = null; + var eraseDescription = false; + string loadOrderGroup = null; + string name = null; + string password = null; + + var serviceType = ServiceType.OwnProcess; + var startType = ServiceStartType.Demand; + var errorControl = ServiceErrorControl.Normal; + var interactive = false; + var vital = false; + + foreach (var attrib in node.Attributes()) + { + if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace) + { + switch (attrib.Name.LocalName) + { + case "Id": + id = this.Core.GetAttributeIdentifier(sourceLineNumbers, attrib); + break; + case "Account": + account = this.Core.GetAttributeValue(sourceLineNumbers, attrib); + break; + case "Arguments": + arguments = this.Core.GetAttributeValue(sourceLineNumbers, attrib); + break; + case "Description": + description = this.Core.GetAttributeValue(sourceLineNumbers, attrib); + break; + case "DisplayName": + displayName = this.Core.GetAttributeValue(sourceLineNumbers, attrib); + break; + case "EraseDescription": + eraseDescription = YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib); + break; + case "ErrorControl": + var errorControlValue = this.Core.GetAttributeValue(sourceLineNumbers, attrib); + switch (errorControlValue) + { + case "ignore": + errorControl = ServiceErrorControl.Ignore; + break; + case "normal": + errorControl = ServiceErrorControl.Normal; + break; + case "critical": + errorControl = ServiceErrorControl.Critical; + break; + case "": // error case handled by GetAttributeValue() + break; + default: + this.Core.Write(ErrorMessages.IllegalAttributeValue(sourceLineNumbers, node.Name.LocalName, attrib.Name.LocalName, errorControlValue, "ignore", "normal", "critical")); + break; + } + break; + case "Interactive": + interactive = YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib); + break; + case "LoadOrderGroup": + loadOrderGroup = this.Core.GetAttributeValue(sourceLineNumbers, attrib); + break; + case "Name": + name = this.Core.GetAttributeValue(sourceLineNumbers, attrib); + break; + case "Password": + password = this.Core.GetAttributeValue(sourceLineNumbers, attrib); + break; + case "Start": + var startValue = this.Core.GetAttributeValue(sourceLineNumbers, attrib); + switch (startValue) + { + case "auto": + startType = ServiceStartType.Auto; + break; + case "demand": + startType = ServiceStartType.Demand; + break; + case "disabled": + startType = ServiceStartType.Disabled; + break; + case "boot": + case "system": + this.Core.Write(ErrorMessages.ValueNotSupported(sourceLineNumbers, node.Name.LocalName, attrib.Name.LocalName, startValue)); + break; + case "": + break; + default: + this.Core.Write(ErrorMessages.IllegalAttributeValue(sourceLineNumbers, node.Name.LocalName, attrib.Name.LocalName, startValue, "auto", "demand", "disabled")); + break; + } + break; + case "Type": + var typeValue = this.Core.GetAttributeValue(sourceLineNumbers, attrib); + switch (typeValue) + { + case "ownProcess": + serviceType = ServiceType.OwnProcess; + break; + case "shareProcess": + serviceType = ServiceType.ShareProcess; + break; + case "kernelDriver": + case "systemDriver": + this.Core.Write(ErrorMessages.ValueNotSupported(sourceLineNumbers, node.Name.LocalName, attrib.Name.LocalName, typeValue)); + break; + case "": + break; + default: + this.Core.Write(ErrorMessages.IllegalAttributeValue(sourceLineNumbers, node.Name.LocalName, node.Name.LocalName, typeValue, "ownProcess", "shareProcess")); + break; + } + break; + case "Vital": + vital = YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib); + break; + default: + this.Core.UnexpectedAttribute(node, attrib); + break; + } + } + else + { + this.Core.ParseExtensionAttribute(node, attrib); + } + } + + if (String.IsNullOrEmpty(name)) + { + this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Name")); + } + else if (null == id) + { + id = this.Core.CreateIdentifierFromFilename(name); + } + + if (0 == startType) + { + this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Start")); + } + + if (eraseDescription) + { + description = "[~]"; + } + + // get the ServiceInstall dependencies and config + foreach (var child in node.Elements()) + { + if (CompilerCore.WixNamespace == child.Name.Namespace) + { + switch (child.Name.LocalName) + { + case "PermissionEx": + this.ParsePermissionExElement(child, id.Id, "ServiceInstall"); + break; + case "ServiceConfig": + this.ParseServiceConfigElement(child, componentId, name); + break; + case "ServiceConfigFailureActions": + this.ParseServiceConfigFailureActionsElement(child, componentId, name); + break; + case "ServiceDependency": + dependencies = String.Concat(dependencies, this.ParseServiceDependencyElement(child), "[~]"); + break; + default: + this.Core.UnexpectedElement(node, child); + break; + } + } + else + { + var context = new Dictionary() { { "ServiceInstallId", id?.Id }, { "ServiceInstallName", name }, { "ServiceInstallComponentId", componentId }, { "Win64", win64Component.ToString() } }; + this.Core.ParseExtensionElement(node, child, context); + } + } + + if (null != dependencies) + { + dependencies = String.Concat(dependencies, "[~]"); + } + + if (!this.Core.EncounteredError) + { + this.Core.AddSymbol(new ServiceInstallSymbol(sourceLineNumbers, id) + { + Name = name, + DisplayName = displayName, + ServiceType = serviceType, + StartType = startType, + ErrorControl = errorControl, + LoadOrderGroup = loadOrderGroup, + Dependencies = dependencies, + StartName = account, + Password = password, + Arguments = arguments, + ComponentRef = componentId, + Description = description, + Interactive = interactive, + Vital = vital + }); + } + } + + /// + /// Parses a SetDirectory element. + /// + /// Element to parse. + private void ParseSetDirectoryElement(XElement node) + { + var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node); + string actionName = null; + string id = null; + string condition = null; + var executionType = CustomActionExecutionType.Immediate; + var sequences = new[] { SequenceTable.InstallUISequence, SequenceTable.InstallExecuteSequence }; // default to "both" + string value = null; + + foreach (var attrib in node.Attributes()) + { + if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace) + { + switch (attrib.Name.LocalName) + { + case "Action": + actionName = this.Core.GetAttributeIdentifierValue(sourceLineNumbers, attrib); + break; + case "Condition": + condition = this.Core.GetAttributeValue(sourceLineNumbers, attrib); + break; + case "Id": + id = this.Core.GetAttributeIdentifierValue(sourceLineNumbers, attrib); + this.Core.CreateSimpleReference(sourceLineNumbers, SymbolDefinitions.Directory, id); + break; + case "Sequence": + var sequenceValue = this.Core.GetAttributeValue(sourceLineNumbers, attrib); + switch (sequenceValue) + { + case "execute": + sequences = new[] { SequenceTable.InstallExecuteSequence }; + break; + case "first": + executionType = CustomActionExecutionType.FirstSequence; + break; + case "ui": + sequences = new[] { SequenceTable.InstallUISequence }; + break; + case "both": + break; + case "": + break; + default: + this.Core.Write(ErrorMessages.IllegalAttributeValue(sourceLineNumbers, node.Name.LocalName, attrib.Name.LocalName, sequenceValue, "execute", "ui", "both")); + break; + } + break; + case "Value": + value = this.Core.GetAttributeValue(sourceLineNumbers, attrib); + break; + default: + this.Core.UnexpectedAttribute(node, attrib); + break; + } + } + else + { + this.Core.ParseExtensionAttribute(node, attrib); + } + } + + this.Core.VerifyNoInnerText(sourceLineNumbers, node); + + if (null == id) + { + this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Id")); + } + else if (String.IsNullOrEmpty(actionName)) + { + actionName = String.Concat("Set", id); + } + + if (null == value) + { + this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Value")); + } + + this.Core.ParseForExtensionElements(node); + + if (!this.Core.EncounteredError) + { + this.Core.AddSymbol(new CustomActionSymbol(sourceLineNumbers, new Identifier(AccessModifier.Public, actionName)) + { + ExecutionType = executionType, + SourceType = CustomActionSourceType.Directory, + TargetType = CustomActionTargetType.TextData, + Source = id, + Target = value + }); + + foreach (var sequence in sequences) + { + this.Core.ScheduleActionSymbol(sourceLineNumbers, AccessModifier.Public, sequence, actionName, condition, afterAction: "CostInitialize"); + } + } + } + + /// + /// Parses a SetProperty element. + /// + /// Element to parse. + private void ParseSetPropertyElement(XElement node) + { + var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node); + string actionName = null; + string id = null; + string condition = null; + string afterAction = null; + string beforeAction = null; + var executionType = CustomActionExecutionType.Immediate; + var sequences = new[] { SequenceTable.InstallUISequence, SequenceTable.InstallExecuteSequence }; // default to "both" + string value = null; + + foreach (var attrib in node.Attributes()) + { + if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace) + { + switch (attrib.Name.LocalName) + { + case "Action": + actionName = this.Core.GetAttributeIdentifierValue(sourceLineNumbers, attrib); + break; + case "Id": + id = this.Core.GetAttributeIdentifierValue(sourceLineNumbers, attrib); + break; + case "Condition": + condition = this.Core.GetAttributeValue(sourceLineNumbers, attrib); + break; + case "After": + afterAction = this.Core.GetAttributeIdentifierValue(sourceLineNumbers, attrib); + break; + case "Before": + beforeAction = this.Core.GetAttributeIdentifierValue(sourceLineNumbers, attrib); + break; + case "Sequence": + var sequenceValue = this.Core.GetAttributeValue(sourceLineNumbers, attrib); + switch (sequenceValue) + { + case "execute": + sequences = new[] { SequenceTable.InstallExecuteSequence }; + break; + case "first": + executionType = CustomActionExecutionType.FirstSequence; + break; + case "ui": + sequences = new[] { SequenceTable.InstallUISequence }; + break; + case "both": + break; + case "": + break; + default: + this.Core.Write(ErrorMessages.IllegalAttributeValue(sourceLineNumbers, node.Name.LocalName, attrib.Name.LocalName, sequenceValue, "execute", "ui", "both")); + break; + } + break; + case "Value": + value = this.Core.GetAttributeValue(sourceLineNumbers, attrib, EmptyRule.CanBeEmpty); + break; + default: + this.Core.UnexpectedAttribute(node, attrib); + break; + } + } + else + { + this.Core.ParseExtensionAttribute(node, attrib); + } + } + + this.Core.VerifyNoInnerText(sourceLineNumbers, node); + + if (null == id) + { + this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Id")); + } + else if (String.IsNullOrEmpty(actionName)) + { + actionName = String.Concat("Set", id); + } + + if (null == value) + { + this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Value")); + } + + if (null != beforeAction && null != afterAction) + { + this.Core.Write(ErrorMessages.IllegalAttributeWithOtherAttribute(sourceLineNumbers, node.Name.LocalName, "After", "Before")); + } + else if (null == beforeAction && null == afterAction) + { + this.Core.Write(ErrorMessages.ExpectedAttributesWithOtherAttribute(sourceLineNumbers, node.Name.LocalName, "After", "Before", "Id")); + } + + this.Core.ParseForExtensionElements(node); + + // add the row and any references needed + if (!this.Core.EncounteredError) + { + // action that is scheduled to occur before/after itself + if (beforeAction == actionName) + { + this.Core.Write(ErrorMessages.ActionScheduledRelativeToItself(sourceLineNumbers, node.Name.LocalName, "Before", beforeAction)); + } + else if (afterAction == actionName) + { + this.Core.Write(ErrorMessages.ActionScheduledRelativeToItself(sourceLineNumbers, node.Name.LocalName, "After", afterAction)); + } + + this.Core.AddSymbol(new CustomActionSymbol(sourceLineNumbers, new Identifier(AccessModifier.Public, actionName)) + { + ExecutionType = executionType, + SourceType = CustomActionSourceType.Property, + TargetType = CustomActionTargetType.TextData, + Source = id, + Target = value, + }); + + foreach (var sequence in sequences) + { + this.Core.ScheduleActionSymbol(sourceLineNumbers, AccessModifier.Public, sequence, actionName, condition, beforeAction, afterAction); + } + } + } + + /// + /// Parses a SFP catalog element. + /// + /// Element to parse. + /// Parent SFPCatalog. + private void ParseSFPFileElement(XElement node, string parentSFPCatalog) + { + var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node); + string id = null; + + foreach (var attrib in node.Attributes()) + { + if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace) + { + switch (attrib.Name.LocalName) + { + case "Id": + id = this.Core.GetAttributeIdentifierValue(sourceLineNumbers, attrib); + break; + default: + this.Core.UnexpectedAttribute(node, attrib); + break; + } + } + else + { + this.Core.ParseExtensionAttribute(node, attrib); + } + } + + if (null == id) + { + this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Id")); + } + + this.Core.ParseForExtensionElements(node); + + if (!this.Core.EncounteredError) + { + this.Core.AddSymbol(new FileSFPCatalogSymbol(sourceLineNumbers) + { + FileRef = id, + SFPCatalogRef = parentSFPCatalog + }); + } + } + + /// + /// Parses a SFP catalog element. + /// + /// Element to parse. + /// Parent SFPCatalog. + private void ParseSFPCatalogElement(XElement node, ref string parentSFPCatalog) + { + var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node); + string parentName = null; + string dependency = null; + string name = null; + string sourceFile = null; + + foreach (var attrib in node.Attributes()) + { + if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace) + { + switch (attrib.Name.LocalName) + { + case "Dependency": + dependency = this.Core.GetAttributeValue(sourceLineNumbers, attrib); + break; + case "Name": + name = this.Core.GetAttributeShortFilename(sourceLineNumbers, attrib, false); + parentSFPCatalog = name; + break; + case "SourceFile": + sourceFile = this.Core.GetAttributeValue(sourceLineNumbers, attrib); + break; + default: + this.Core.UnexpectedAttribute(node, attrib); + break; + } + } + else + { + this.Core.ParseExtensionAttribute(node, attrib); + } + } + + if (null == name) + { + this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Name")); + } + + if (null == sourceFile) + { + this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "SourceFile")); + } + + foreach (var child in node.Elements()) + { + if (CompilerCore.WixNamespace == child.Name.Namespace) + { + switch (child.Name.LocalName) + { + case "SFPCatalog": + this.ParseSFPCatalogElement(child, ref parentName); + if (null != dependency && parentName == dependency) + { + this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Dependency")); + } + dependency = parentName; + break; + case "SFPFile": + this.ParseSFPFileElement(child, name); + break; + default: + this.Core.UnexpectedElement(node, child); + break; + } + } + else + { + this.Core.ParseExtensionElement(node, child); + } + } + + if (null == dependency) + { + this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Dependency")); + } + + if (!this.Core.EncounteredError) + { + this.Core.AddSymbol(new SFPCatalogSymbol(sourceLineNumbers) + { + SFPCatalog = name, + Catalog = sourceFile, + Dependency = dependency + }); + } + } + + /// + /// Parses a shortcut element. + /// + /// Element to parse. + /// Identifer for parent component. + /// Local name of parent element. + /// Default identifier of parent (which is usually the target). + /// Flag to indicate whether the parent element is the keypath of a component or not (will only be true for file parent elements). + private void ParseShortcutElement(XElement node, string componentId, string parentElementLocalName, string defaultTarget, YesNoType parentKeyPath) + { + var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node); + Identifier id = null; + var advertise = false; + string arguments = null; + string description = null; + string descriptionResourceDll = null; + int? descriptionResourceId = null; + string directory = null; + string displayResourceDll = null; + int? displayResourceId = null; + int? hotkey = null; + string icon = null; + int? iconIndex = null; + string name = null; + string shortName = null; + ShortcutShowType? show = null; + string target = null; + string workingDirectory = null; + + foreach (var attrib in node.Attributes()) + { + if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace) + { + switch (attrib.Name.LocalName) + { + case "Id": + id = this.Core.GetAttributeIdentifier(sourceLineNumbers, attrib); + break; + case "Advertise": + advertise = YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib); + break; + case "Arguments": + arguments = this.Core.GetAttributeValue(sourceLineNumbers, attrib); + break; + case "Description": + description = this.Core.GetAttributeValue(sourceLineNumbers, attrib); + break; + case "DescriptionResourceDll": + descriptionResourceDll = this.Core.GetAttributeValue(sourceLineNumbers, attrib); + break; + case "DescriptionResourceId": + descriptionResourceId = this.Core.GetAttributeIntegerValue(sourceLineNumbers, attrib, 0, Int16.MaxValue); + break; + case "Directory": + directory = this.Core.CreateDirectoryReferenceFromInlineSyntax(sourceLineNumbers, attrib, null); + break; + case "DisplayResourceDll": + displayResourceDll = this.Core.GetAttributeValue(sourceLineNumbers, attrib); + break; + case "DisplayResourceId": + displayResourceId = this.Core.GetAttributeIntegerValue(sourceLineNumbers, attrib, 0, Int16.MaxValue); + break; + case "Hotkey": + hotkey = this.Core.GetAttributeIntegerValue(sourceLineNumbers, attrib, 0, Int16.MaxValue); + break; + case "Icon": + icon = this.Core.GetAttributeIdentifierValue(sourceLineNumbers, attrib); + this.Core.CreateSimpleReference(sourceLineNumbers, SymbolDefinitions.Icon, icon); + break; + case "IconIndex": + iconIndex = this.Core.GetAttributeIntegerValue(sourceLineNumbers, attrib, Int16.MinValue + 1, Int16.MaxValue); + break; + case "Name": + name = this.Core.GetAttributeLongFilename(sourceLineNumbers, attrib, false); + break; + case "ShortName": + shortName = this.Core.GetAttributeShortFilename(sourceLineNumbers, attrib, false); + break; + case "Show": + var showValue = this.Core.GetAttributeValue(sourceLineNumbers, attrib); + switch (showValue) + { + case "normal": + show = ShortcutShowType.Normal; + break; + case "maximized": + show = ShortcutShowType.Maximized; + break; + case "minimized": + show = ShortcutShowType.Minimized; + break; + case "": + break; + default: + this.Core.Write(ErrorMessages.IllegalAttributeValue(sourceLineNumbers, node.Name.LocalName, "Show", showValue, "normal", "maximized", "minimized")); + break; + } + break; + case "Target": + target = this.Core.GetAttributeValue(sourceLineNumbers, attrib); + break; + case "WorkingDirectory": + workingDirectory = this.Core.GetAttributeIdentifierValue(sourceLineNumbers, attrib); + break; + default: + this.Core.UnexpectedAttribute(node, attrib); + break; + } + } + else + { + this.Core.ParseExtensionAttribute(node, attrib); + } + } + + if (advertise && null != target) + { + this.Core.Write(ErrorMessages.IllegalAttributeWithOtherAttribute(sourceLineNumbers, node.Name.LocalName, "Target", "Advertise", "yes")); + } + + if (null == directory) + { + if ("Component" == parentElementLocalName) + { + directory = defaultTarget; + } + else + { + this.Core.Write(ErrorMessages.ExpectedAttributeWhenElementNotUnderElement(sourceLineNumbers, node.Name.LocalName, "Directory", "Component")); + } + } + + if (null != descriptionResourceDll) + { + if (!descriptionResourceId.HasValue) + { + this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "DescriptionResourceDll", "DescriptionResourceId")); + } + } + else + { + if (descriptionResourceId.HasValue) + { + this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "DescriptionResourceId", "DescriptionResourceDll")); + } + } + + if (null != displayResourceDll) + { + if (!displayResourceId.HasValue) + { + this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "DisplayResourceDll", "DisplayResourceId")); + } + } + else + { + if (displayResourceId.HasValue) + { + this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "DisplayResourceId", "DisplayResourceDll")); + } + } + + if (null == name) + { + this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Name")); + } + + if ("Component" != parentElementLocalName && null != target) + { + this.Core.Write(ErrorMessages.IllegalAttributeWhenNested(sourceLineNumbers, node.Name.LocalName, "Target", parentElementLocalName)); + } + + if (null == id) + { + id = this.Core.CreateIdentifier("sct", directory, LowercaseOrNull(name)); + } + + foreach (var child in node.Elements()) + { + if (CompilerCore.WixNamespace == child.Name.Namespace) + { + switch (child.Name.LocalName) + { + case "Icon": + icon = this.ParseIconElement(child); + break; + case "ShortcutProperty": + this.ParseShortcutPropertyElement(child, id.Id); + break; + default: + this.Core.UnexpectedElement(node, child); + break; + } + } + else + { + this.Core.ParseExtensionElement(node, child); + } + } + + if (!this.Core.EncounteredError) + { + if (advertise) + { + if (YesNoType.Yes != parentKeyPath && "Component" != parentElementLocalName) + { + this.Core.Write(WarningMessages.UnclearShortcut(sourceLineNumbers, id.Id, componentId, defaultTarget)); + } + + target = Guid.Empty.ToString("B"); + } + else if (null != target) + { + } + else if ("Component" == parentElementLocalName || "CreateFolder" == parentElementLocalName) + { + target = "[" + defaultTarget + "]"; + } + else if ("File" == parentElementLocalName) + { + target = "[#" + defaultTarget + "]"; + } + + this.Core.AddSymbol(new ShortcutSymbol(sourceLineNumbers, id) + { + DirectoryRef = directory, + Name = name, + ShortName = shortName, + ComponentRef = componentId, + Target = target, + Arguments = arguments, + Description = description, + Hotkey = hotkey, + IconRef = icon, + IconIndex = iconIndex, + Show = show, + WorkingDirectory = workingDirectory, + DisplayResourceDll = displayResourceDll, + DisplayResourceId = displayResourceId, + DescriptionResourceDll = descriptionResourceDll, + DescriptionResourceId = descriptionResourceId, + }); + } + } + + /// + /// Parses a shortcut property element. + /// + /// Element to parse. + /// + private void ParseShortcutPropertyElement(XElement node, string shortcutId) + { + var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node); + Identifier id = null; + string key = null; + string value = null; + + foreach (var attrib in node.Attributes()) + { + if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace) + { + switch (attrib.Name.LocalName) + { + case "Id": + id = this.Core.GetAttributeIdentifier(sourceLineNumbers, attrib); + break; + case "Key": + key = this.Core.GetAttributeValue(sourceLineNumbers, attrib); + break; + case "Value": + value = this.Core.GetAttributeValue(sourceLineNumbers, attrib); + break; + default: + this.Core.UnexpectedAttribute(node, attrib); + break; + } + } + else + { + this.Core.ParseExtensionAttribute(node, attrib); + } + } + + this.Core.VerifyNoInnerText(sourceLineNumbers, node); + + if (String.IsNullOrEmpty(key)) + { + this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Key")); + } + else if (null == id) + { + id = this.Core.CreateIdentifier("scp", shortcutId, key.ToUpperInvariant()); + } + + if (String.IsNullOrEmpty(value)) + { + this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Value")); + } + + this.Core.ParseForExtensionElements(node); + + if (!this.Core.EncounteredError) + { + this.Core.AddSymbol(new MsiShortcutPropertySymbol(sourceLineNumbers, id) + { + ShortcutRef = shortcutId, + PropertyKey = key, + PropVariantValue = value + }); + } + } + + /// + /// Parses a typelib element. + /// + /// Element to parse. + /// Identifier of parent component. + /// Identifier of file that acts as typelib server. + /// true if the component is 64-bit. + private void ParseTypeLibElement(XElement node, string componentId, string fileServer, bool win64Component) + { + var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node); + string id = null; + var advertise = YesNoType.NotSet; + var cost = CompilerConstants.IntegerNotSet; + string description = null; + var flags = 0; + string helpDirectory = null; + var language = CompilerConstants.IntegerNotSet; + var majorVersion = CompilerConstants.IntegerNotSet; + var minorVersion = CompilerConstants.IntegerNotSet; + var resourceId = CompilerConstants.LongNotSet; + + foreach (var attrib in node.Attributes()) + { + if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace) + { + switch (attrib.Name.LocalName) + { + case "Id": + id = this.Core.GetAttributeGuidValue(sourceLineNumbers, attrib, false); + break; + case "Advertise": + advertise = this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib); + break; + case "Control": + if (YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib)) + { + flags |= 2; + } + break; + case "Cost": + cost = this.Core.GetAttributeIntegerValue(sourceLineNumbers, attrib, 0, Int32.MaxValue); + break; + case "Description": + description = this.Core.GetAttributeValue(sourceLineNumbers, attrib); + break; + case "HasDiskImage": + if (YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib)) + { + flags |= 8; + } + break; + case "HelpDirectory": + helpDirectory = this.Core.CreateDirectoryReferenceFromInlineSyntax(sourceLineNumbers, attrib, null); + break; + case "Hidden": + if (YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib)) + { + flags |= 4; + } + break; + case "Language": + language = this.Core.GetAttributeIntegerValue(sourceLineNumbers, attrib, 0, Int16.MaxValue); + break; + case "MajorVersion": + majorVersion = this.Core.GetAttributeIntegerValue(sourceLineNumbers, attrib, 0, UInt16.MaxValue); + break; + case "MinorVersion": + minorVersion = this.Core.GetAttributeIntegerValue(sourceLineNumbers, attrib, 0, Byte.MaxValue); + break; + case "ResourceId": + resourceId = this.Core.GetAttributeLongValue(sourceLineNumbers, attrib, Int32.MinValue, Int32.MaxValue); + break; + case "Restricted": + if (YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib)) + { + flags |= 1; + } + break; + default: + this.Core.UnexpectedAttribute(node, attrib); + break; + } + } + else + { + this.Core.ParseExtensionAttribute(node, attrib); + } + } + + if (null == id) + { + this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Id")); + } + + if (CompilerConstants.IntegerNotSet == language) + { + this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Language")); + language = CompilerConstants.IllegalInteger; + } + + // build up the typelib version string for the registry if the major or minor version was specified + string registryVersion = null; + if (CompilerConstants.IntegerNotSet != majorVersion || CompilerConstants.IntegerNotSet != minorVersion) + { + if (CompilerConstants.IntegerNotSet != majorVersion) + { + registryVersion = majorVersion.ToString("x", CultureInfo.InvariantCulture.NumberFormat); + } + else + { + registryVersion = "0"; + } + + if (CompilerConstants.IntegerNotSet != minorVersion) + { + registryVersion = String.Concat(registryVersion, ".", minorVersion.ToString("x", CultureInfo.InvariantCulture.NumberFormat)); + } + else + { + registryVersion = String.Concat(registryVersion, ".0"); + } + } + + // if the advertise state has not been set, default to non-advertised + if (YesNoType.NotSet == advertise) + { + advertise = YesNoType.No; + } + + foreach (var child in node.Elements()) + { + if (CompilerCore.WixNamespace == child.Name.Namespace) + { + switch (child.Name.LocalName) + { + case "AppId": + this.ParseAppIdElement(child, componentId, YesNoType.NotSet, fileServer, id, registryVersion); + break; + case "Class": + this.ParseClassElement(child, componentId, YesNoType.NotSet, fileServer, id, registryVersion, null); + break; + case "Interface": + this.ParseInterfaceElement(child, componentId, null, null, id, registryVersion); + break; + default: + this.Core.UnexpectedElement(node, child); + break; + } + } + else + { + this.Core.ParseExtensionElement(node, child); + } + } + + + if (YesNoType.Yes == advertise) + { + if (CompilerConstants.LongNotSet != resourceId) + { + this.Core.Write(ErrorMessages.IllegalAttributeWhenAdvertised(sourceLineNumbers, node.Name.LocalName, "ResourceId")); + } + + if (0 != flags) + { + if (0x1 == (flags & 0x1)) + { + this.Core.Write(ErrorMessages.IllegalAttributeWithOtherAttribute(sourceLineNumbers, node.Name.LocalName, "Restricted", "Advertise", "yes")); + } + + if (0x2 == (flags & 0x2)) + { + this.Core.Write(ErrorMessages.IllegalAttributeWithOtherAttribute(sourceLineNumbers, node.Name.LocalName, "Control", "Advertise", "yes")); + } + + if (0x4 == (flags & 0x4)) + { + this.Core.Write(ErrorMessages.IllegalAttributeWithOtherAttribute(sourceLineNumbers, node.Name.LocalName, "Hidden", "Advertise", "yes")); + } + + if (0x8 == (flags & 0x8)) + { + this.Core.Write(ErrorMessages.IllegalAttributeWithOtherAttribute(sourceLineNumbers, node.Name.LocalName, "HasDiskImage", "Advertise", "yes")); + } + } + + if (!this.Core.EncounteredError) + { + var symbol = this.Core.AddSymbol(new TypeLibSymbol(sourceLineNumbers) + { + LibId = id, + Language = language, + ComponentRef = componentId, + Description = description, + DirectoryRef = helpDirectory, + FeatureRef = Guid.Empty.ToString("B") + }); + + if (CompilerConstants.IntegerNotSet != majorVersion || CompilerConstants.IntegerNotSet != minorVersion) + { + symbol.Version = (CompilerConstants.IntegerNotSet != majorVersion ? majorVersion * 256 : 0) + (CompilerConstants.IntegerNotSet != minorVersion ? minorVersion : 0); + } + + if (CompilerConstants.IntegerNotSet != cost) + { + symbol.Cost = cost; + } + } + } + else if (YesNoType.No == advertise) + { + if (CompilerConstants.IntegerNotSet != cost && CompilerConstants.IllegalInteger != cost) + { + this.Core.Write(ErrorMessages.IllegalAttributeWithOtherAttribute(sourceLineNumbers, node.Name.LocalName, "Cost", "Advertise", "no")); + } + + if (null == fileServer) + { + this.Core.Write(ErrorMessages.MissingTypeLibFile(sourceLineNumbers, node.Name.LocalName, "File")); + } + + if (null == registryVersion) + { + this.Core.Write(ErrorMessages.ExpectedAttributesWithOtherAttribute(sourceLineNumbers, node.Name.LocalName, "MajorVersion", "MinorVersion", "Advertise", "no")); + } + + // HKCR\TypeLib\[ID]\[MajorVersion].[MinorVersion], (Default) = [Description] + this.Core.CreateRegistryRow(sourceLineNumbers, RegistryRootType.ClassesRoot, String.Format(CultureInfo.InvariantCulture, @"TypeLib\{0}\{1}", id, registryVersion), null, description, componentId); + + // HKCR\TypeLib\[ID]\[MajorVersion].[MinorVersion]\[Language]\[win16|win32|win64], (Default) = [TypeLibPath]\[ResourceId] + var path = String.Concat("[#", fileServer, "]"); + if (CompilerConstants.LongNotSet != resourceId) + { + path = String.Concat(path, Path.DirectorySeparatorChar, resourceId.ToString(CultureInfo.InvariantCulture.NumberFormat)); + } + this.Core.CreateRegistryRow(sourceLineNumbers, RegistryRootType.ClassesRoot, String.Format(CultureInfo.InvariantCulture, @"TypeLib\{0}\{1}\{2}\{3}", id, registryVersion, language, (win64Component ? "win64" : "win32")), null, path, componentId); + + // HKCR\TypeLib\[ID]\[MajorVersion].[MinorVersion]\FLAGS, (Default) = [TypeLibFlags] + this.Core.CreateRegistryRow(sourceLineNumbers, RegistryRootType.ClassesRoot, String.Format(CultureInfo.InvariantCulture, @"TypeLib\{0}\{1}\FLAGS", id, registryVersion), null, flags.ToString(CultureInfo.InvariantCulture.NumberFormat), componentId); + + if (null != helpDirectory) + { + // HKCR\TypeLib\[ID]\[MajorVersion].[MinorVersion]\HELPDIR, (Default) = [HelpDirectory] + this.Core.CreateRegistryRow(sourceLineNumbers, RegistryRootType.ClassesRoot, String.Format(CultureInfo.InvariantCulture, @"TypeLib\{0}\{1}\HELPDIR", id, registryVersion), null, String.Concat("[", helpDirectory, "]"), componentId); + } + } + } + + /// + /// Parses an upgrade element. + /// + /// Element to parse. + private void ParseUpgradeElement(XElement node) + { + var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node); + string id = null; + + foreach (var attrib in node.Attributes()) + { + if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace) + { + switch (attrib.Name.LocalName) + { + case "Id": + id = this.Core.GetAttributeGuidValue(sourceLineNumbers, attrib, false); + break; + default: + this.Core.UnexpectedAttribute(node, attrib); + break; + } + } + else + { + this.Core.ParseExtensionAttribute(node, attrib); + } + } + + if (null == id) + { + this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Id")); + } + + // process the UpgradeVersion children here + foreach (var child in node.Elements()) + { + if (CompilerCore.WixNamespace == child.Name.Namespace) + { + var childSourceLineNumbers = Preprocessor.GetSourceLineNumbers(child); + + switch (child.Name.LocalName) + { + case "Property": + this.ParsePropertyElement(child); + this.Core.Write(WarningMessages.DeprecatedUpgradeProperty(childSourceLineNumbers)); + break; + case "UpgradeVersion": + this.ParseUpgradeVersionElement(child, id); + break; + default: + this.Core.UnexpectedElement(node, child); + break; + } + } + else + { + this.Core.ParseExtensionElement(node, child); + } + } + + // No rows created here. All row creation is done in ParseUpgradeVersionElement. + } + + /// + /// Parse upgrade version element. + /// + /// Element to parse. + /// Upgrade code. + private void ParseUpgradeVersionElement(XElement node, string upgradeId) + { + var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node); + + string actionProperty = null; + string language = null; + string maximum = null; + string minimum = null; + var excludeLanguages = false; + var ignoreFailures = false; + var includeMax = false; + var includeMin = true; + var migrateFeatures = false; + var onlyDetect = false; + string removeFeatures = null; + + foreach (var attrib in node.Attributes()) + { + if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace) + { + switch (attrib.Name.LocalName) + { + case "ExcludeLanguages": + excludeLanguages = YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib); + break; + case "IgnoreRemoveFailure": + ignoreFailures = YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib); + break; + case "IncludeMaximum": + includeMax = YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib); + break; + case "IncludeMinimum": // this is "yes" by default + includeMin = YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib); + break; + case "Language": + language = this.Core.GetAttributeValue(sourceLineNumbers, attrib); + break; + case "Minimum": + minimum = this.Core.GetAttributeVersionValue(sourceLineNumbers, attrib); + break; + case "Maximum": + maximum = this.Core.GetAttributeVersionValue(sourceLineNumbers, attrib); + break; + case "MigrateFeatures": + migrateFeatures = YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib); + break; + case "OnlyDetect": + onlyDetect = YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib); + break; + case "Property": + actionProperty = this.Core.GetAttributeIdentifierValue(sourceLineNumbers, attrib); + break; + case "RemoveFeatures": + removeFeatures = this.Core.GetAttributeValue(sourceLineNumbers, attrib); + break; + default: + this.Core.UnexpectedAttribute(node, attrib); + break; + } + } + else + { + this.Core.ParseExtensionAttribute(node, attrib); + } + } + + if (null == actionProperty) + { + this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Property")); + } + else if (actionProperty.ToUpper(CultureInfo.InvariantCulture) != actionProperty) + { + this.Core.Write(ErrorMessages.SecurePropertyNotUppercase(sourceLineNumbers, node.Name.LocalName, "Property", actionProperty)); + } + + if (null == minimum && null == maximum) + { + this.Core.Write(ErrorMessages.ExpectedAttributes(sourceLineNumbers, node.Name.LocalName, "Minimum", "Maximum")); + } + + this.Core.ParseForExtensionElements(node); + + if (!this.Core.EncounteredError) + { + this.Core.AddSymbol(new UpgradeSymbol(sourceLineNumbers) + { + UpgradeCode = upgradeId, + VersionMin = minimum, + VersionMax = maximum, + Language = language, + ExcludeLanguages = excludeLanguages, + IgnoreRemoveFailures = ignoreFailures, + VersionMaxInclusive = includeMax, + VersionMinInclusive = includeMin, + MigrateFeatures = migrateFeatures, + OnlyDetect = onlyDetect, + Remove = removeFeatures, + ActionProperty = actionProperty + }); + + // Ensure that RemoveExistingProducts is authored in InstallExecuteSequence + // if at least one row in Upgrade table lacks the OnlyDetect attribute. + if (!onlyDetect) + { + this.Core.CreateSimpleReference(sourceLineNumbers, SymbolDefinitions.WixAction, "InstallExecuteSequence", "RemoveExistingProducts"); + } + } + } + + /// + /// Parses a verb element. + /// + /// Element to parse. + /// Extension verb is releated to. + /// Optional progId for extension. + /// Identifier for parent component. + /// Flag if verb is advertised. + private void ParseVerbElement(XElement node, string extension, string progId, string componentId, YesNoType advertise) + { + var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node); + string id = null; + string argument = null; + string command = null; + var sequence = CompilerConstants.IntegerNotSet; + string targetFile = null; + string targetProperty = null; + + foreach (var attrib in node.Attributes()) + { + if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace) + { + switch (attrib.Name.LocalName) + { + case "Id": + id = this.Core.GetAttributeValue(sourceLineNumbers, attrib); + break; + case "Argument": + argument = this.Core.GetAttributeValue(sourceLineNumbers, attrib); + break; + case "Command": + command = this.Core.GetAttributeValue(sourceLineNumbers, attrib); + break; + case "Sequence": + sequence = this.Core.GetAttributeIntegerValue(sourceLineNumbers, attrib, 1, Int16.MaxValue); + break; + case "TargetFile": + targetFile = this.Core.GetAttributeValue(sourceLineNumbers, attrib); + this.Core.CreateSimpleReference(sourceLineNumbers, SymbolDefinitions.File, targetFile); + break; + case "TargetProperty": + targetProperty = this.Core.GetAttributeValue(sourceLineNumbers, attrib); + break; + default: + this.Core.UnexpectedAttribute(node, attrib); + break; + } + } + else + { + this.Core.ParseExtensionAttribute(node, attrib); + } + } + + if (null == id) + { + this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Id")); + } + + if (null != targetFile && null != targetProperty) + { + this.Core.Write(ErrorMessages.IllegalAttributeWithOtherAttribute(sourceLineNumbers, node.Name.LocalName, "TargetFile", "TargetProperty")); + } + + this.Core.ParseForExtensionElements(node); + + if (YesNoType.Yes == advertise) + { + if (null != targetFile) + { + this.Core.Write(ErrorMessages.IllegalAttributeWhenAdvertised(sourceLineNumbers, node.Name.LocalName, "TargetFile")); + } + + if (null != targetProperty) + { + this.Core.Write(ErrorMessages.IllegalAttributeWhenAdvertised(sourceLineNumbers, node.Name.LocalName, "TargetProperty")); + } + + if (!this.Core.EncounteredError) + { + var symbol = this.Core.AddSymbol(new VerbSymbol(sourceLineNumbers) + { + ExtensionRef = extension, + Verb = id, + Command = command, + Argument = argument, + }); + + if (CompilerConstants.IntegerNotSet != sequence) + { + symbol.Sequence = sequence; + } + } + } + else if (YesNoType.No == advertise) + { + if (CompilerConstants.IntegerNotSet != sequence) + { + this.Core.Write(ErrorMessages.IllegalAttributeWithOtherAttribute(sourceLineNumbers, node.Name.LocalName, "Sequence", "Advertise", "no")); + } + + if (null == targetFile && null == targetProperty) + { + this.Core.Write(ErrorMessages.ExpectedAttributesWithOtherAttribute(sourceLineNumbers, node.Name.LocalName, "TargetFile", "TargetProperty", "Advertise", "no")); + } + + string target = null; + if (null != targetFile) + { + target = String.Concat("\"[#", targetFile, "]\""); + } + else if (null != targetProperty) + { + target = String.Concat("\"[", targetProperty, "]\""); + } + + if (null != argument) + { + target = String.Concat(target, " ", argument); + } + + var prefix = progId ?? String.Concat(".", extension); + + if (null != command) + { + this.Core.CreateRegistryRow(sourceLineNumbers, RegistryRootType.ClassesRoot, String.Concat(prefix, "\\shell\\", id), String.Empty, command, componentId); + } + + this.Core.CreateRegistryRow(sourceLineNumbers, RegistryRootType.ClassesRoot, String.Concat(prefix, "\\shell\\", id, "\\command"), String.Empty, target, componentId); + } + } + + /// + /// Parses a WixVariable element. + /// + /// Element to parse. + private void ParseWixVariableElement(XElement node) + { + var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node); + Identifier id = null; + var overridable = false; + string value = null; + + foreach (var attrib in node.Attributes()) + { + if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace) + { + switch (attrib.Name.LocalName) + { + case "Id": + id = this.Core.GetAttributeIdentifier(sourceLineNumbers, attrib); + break; + case "Overridable": + overridable = (YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib)); + break; + case "Value": + value = this.Core.GetAttributeValue(sourceLineNumbers, attrib, EmptyRule.CanBeEmpty); + break; + default: + this.Core.UnexpectedAttribute(node, attrib); + break; + } + } + else + { + this.Core.ParseExtensionAttribute(node, attrib); + } + } + + if (null == id) + { + this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Id")); + } + + if (null == value) + { + this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Value")); + } + + this.Core.ParseForExtensionElements(node); + + if (!this.Core.EncounteredError) + { + this.Core.AddSymbol(new WixVariableSymbol(sourceLineNumbers, id) + { + Value = value, + Overridable = overridable + }); + } + } + + private CompressionLevel? ParseCompressionLevel(SourceLineNumber sourceLineNumbers, XElement node, XAttribute attribute) + { + var compressionLevel = this.Core.GetAttributeValue(sourceLineNumbers, attribute); + switch (compressionLevel) + { + case "high": + return CompressionLevel.High; + case "low": + return CompressionLevel.Low; + case "medium": + return CompressionLevel.Medium; + case "mszip": + return CompressionLevel.Mszip; + case "none": + return CompressionLevel.None; + case "": + break; + default: + this.Core.Write(ErrorMessages.IllegalCompressionLevel(sourceLineNumbers, compressionLevel)); + break; + } + + return null; + } + } +} diff --git a/src/test/WixToolsetTest.CoreIntegration/DependencyExtensionFixture.cs b/src/test/WixToolsetTest.CoreIntegration/DependencyExtensionFixture.cs new file mode 100644 index 00000000..14eb8ff7 --- /dev/null +++ b/src/test/WixToolsetTest.CoreIntegration/DependencyExtensionFixture.cs @@ -0,0 +1,30 @@ +// 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 WixToolsetTest.CoreIntegration +{ + using WixBuildTools.TestSupport; + using WixToolset.Core.TestPackage; + using Xunit; + + public class DependencyExtensionFixture + { + [Fact] + public void CanBuildUsingProvides() + { + var folder = TestData.Get(@"TestData\UsingProvides"); + var build = new Builder(folder, null, new[] { folder }); + + var results = build.BuildAndQuery(Build, "WixDependencyProvider"); + Assert.Equal(new[] + { + "WixDependencyProvider:dep74OfIcniaqxA7EprRGBw4Oyy3r8\tfilF5_pLhBuF5b4N9XEo52g_hUM5Lo\tUsingProvides\t\t\t", + }, results); + } + + private static void Build(string[] args) + { + var result = WixRunner.Execute(args) + .AssertSuccess(); + } + } +} diff --git a/src/test/WixToolsetTest.CoreIntegration/TestData/UsingProvides/Package.en-us.wxl b/src/test/WixToolsetTest.CoreIntegration/TestData/UsingProvides/Package.en-us.wxl new file mode 100644 index 00000000..38c12ac1 --- /dev/null +++ b/src/test/WixToolsetTest.CoreIntegration/TestData/UsingProvides/Package.en-us.wxl @@ -0,0 +1,11 @@ + + + + + + A newer version of [ProductName] is already installed. + MsiPackage + + diff --git a/src/test/WixToolsetTest.CoreIntegration/TestData/UsingProvides/Package.wxs b/src/test/WixToolsetTest.CoreIntegration/TestData/UsingProvides/Package.wxs new file mode 100644 index 00000000..07da1215 --- /dev/null +++ b/src/test/WixToolsetTest.CoreIntegration/TestData/UsingProvides/Package.wxs @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/src/test/WixToolsetTest.CoreIntegration/TestData/UsingProvides/PackageComponents.wxs b/src/test/WixToolsetTest.CoreIntegration/TestData/UsingProvides/PackageComponents.wxs new file mode 100644 index 00000000..7e459e9a --- /dev/null +++ b/src/test/WixToolsetTest.CoreIntegration/TestData/UsingProvides/PackageComponents.wxs @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/src/test/WixToolsetTest.CoreIntegration/TestData/UsingProvides/example.txt b/src/test/WixToolsetTest.CoreIntegration/TestData/UsingProvides/example.txt new file mode 100644 index 00000000..1b4ffe8a --- /dev/null +++ b/src/test/WixToolsetTest.CoreIntegration/TestData/UsingProvides/example.txt @@ -0,0 +1 @@ +This is example.txt. \ No newline at end of file -- cgit v1.2.3-55-g6feb