// 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.Generic; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.IO; using System.Text.RegularExpressions; using System.Xml.Linq; using WixToolset.Data; using WixToolset.Data.Tuples; using WixToolset.Extensibility; using WixToolset.Extensibility.Data; using WixToolset.Extensibility.Services; /// /// Compiler of the WiX toolset. /// internal partial class Compiler : ICompiler { private const int MinValueOfMaxCabSizeForLargeFileSplitting = 20; // 20 MB private const int MaxValueOfMaxCabSizeForLargeFileSplitting = 2 * 1024; // 2048 MB (i.e. 2 GB) private const string DefaultComponentIdPlaceholderFormat = "WixComponentIdPlaceholder{0}"; private const string DefaultComponentIdPlaceholderWixVariableFormat = "!(wix.{0})"; // If these are true you know you are building a module or product // but if they are false you cannot not be sure they will not end // up a product or module. Use these flags carefully. private bool compilingModule; private bool compilingProduct; private string activeName; private string activeLanguage; // TODO: Implement this differently to not require the VariableResolver. private VariableResolver componentIdPlaceholdersResolver; /// /// Type of RadioButton element in a group. /// private enum RadioButtonType { /// Not set, yet. NotSet, /// Text Text, /// Bitmap Bitmap, /// Icon Icon, } internal Compiler(IServiceProvider serviceProvider) { this.ServiceProvider = serviceProvider; this.Messaging = serviceProvider.GetService(); } private IServiceProvider ServiceProvider { get; } public IMessaging Messaging { get; } private ICompileContext Context { get; set; } private CompilerCore Core { get; set; } /// /// Gets or sets the platform which the compiler will use when defaulting 64-bit attributes and elements. /// /// The platform which the compiler will use when defaulting 64-bit attributes and elements. public Platform CurrentPlatform => this.Context.Platform; /// /// Gets or sets the option to show pedantic messages. /// /// The option to show pedantic messages. public bool ShowPedanticMessages { get; set; } /// /// Compiles the provided Xml document into an intermediate object /// /// Intermediate object representing compiled source document. /// This method is not thread-safe. public Intermediate Compile(ICompileContext context) { var target = new Intermediate(); if (String.IsNullOrEmpty(context.CompilationId)) { context.CompilationId = target.Id; } this.Context = context; var extensionsByNamespace = new Dictionary(); foreach (var extension in this.Context.Extensions) { if (!extensionsByNamespace.TryGetValue(extension.Namespace, out var collidingExtension)) { extensionsByNamespace.Add(extension.Namespace, extension); } else { this.Messaging.Write(ErrorMessages.DuplicateExtensionXmlSchemaNamespace(extension.GetType().ToString(), extension.Namespace.NamespaceName, collidingExtension.GetType().ToString())); } extension.PreCompile(this.Context); } // Try to compile it. try { var parseHelper = this.Context.ServiceProvider.GetService(); this.Core = new CompilerCore(target, this.Messaging, parseHelper, extensionsByNamespace); this.Core.ShowPedanticMessages = this.ShowPedanticMessages; this.componentIdPlaceholdersResolver = new VariableResolver(this.ServiceProvider); // parse the document var source = this.Context.Source; var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(source.Root); if ("Wix" == source.Root.Name.LocalName) { if (CompilerCore.WixNamespace == source.Root.Name.Namespace) { this.ParseWixElement(source.Root); } else // invalid or missing namespace { if (String.IsNullOrEmpty(source.Root.Name.NamespaceName)) { this.Core.Write(ErrorMessages.InvalidWixXmlNamespace(sourceLineNumbers, "Wix", CompilerCore.WixNamespace.ToString())); } else { this.Core.Write(ErrorMessages.InvalidWixXmlNamespace(sourceLineNumbers, "Wix", source.Root.Name.NamespaceName, CompilerCore.WixNamespace.ToString())); } } } else { this.Core.Write(ErrorMessages.InvalidDocumentElement(sourceLineNumbers, source.Root.Name.LocalName, "source", "Wix")); } // Resolve any Component Id placeholders compiled into the intermediate. this.ResolveComponentIdPlaceholders(target); } finally { foreach (var extension in this.Context.Extensions) { extension.PostCompile(target); } this.Core = null; } return this.Messaging.EncounteredError ? null : target; } /// /// Parses a Wix element. /// /// Element to parse. private void ParseWixElement(XElement node) { var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node); string requiredVersion = null; foreach (var attrib in node.Attributes()) { if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace) { switch (attrib.Name.LocalName) { case "RequiredVersion": requiredVersion = this.Core.GetAttributeVersionValue(sourceLineNumbers, attrib); break; default: this.Core.UnexpectedAttribute(node, attrib); break; } } else { this.Core.ParseExtensionAttribute(node, attrib); } } if (null != requiredVersion) { this.Core.VerifyRequiredVersion(sourceLineNumbers, requiredVersion); } foreach (var child in node.Elements()) { if (CompilerCore.WixNamespace == child.Name.Namespace) { switch (child.Name.LocalName) { case "Bundle": this.ParseBundleElement(child); break; case "Fragment": this.ParseFragmentElement(child); break; case "Module": this.ParseModuleElement(child); break; case "PatchCreation": this.ParsePatchCreationElement(child); break; case "Product": this.ParseProductElement(child); break; case "Patch": this.ParsePatchElement(child); break; default: this.Core.UnexpectedElement(node, child); break; } } else { this.Core.ParseExtensionElement(node, child); } } } private void ResolveComponentIdPlaceholders(Intermediate target) { if (0 < this.componentIdPlaceholdersResolver.VariableCount) { foreach (var section in target.Sections) { foreach (var tuple in section.Tuples) { foreach (var field in tuple.Fields) { if (field?.Type == IntermediateFieldType.String) { var data = field.AsString(); if (!String.IsNullOrEmpty(data)) { var resolved = this.componentIdPlaceholdersResolver.ResolveVariables(tuple.SourceLineNumbers, data, false, false); if (resolved.UpdatedValue) { field.Set(resolved.Value); } } } } } } } } /// /// Uppercases the first character of a string. /// /// String to uppercase first character of. /// String with first character uppercased. private static string UppercaseFirstChar(string s) { if (0 == s.Length) { return s; } return String.Concat(s.Substring(0, 1).ToUpperInvariant(), s.Substring(1)); } /// /// Lowercases the string if present. /// /// String to lowercase. /// Null if the string is null, otherwise returns the lowercase. private static string LowercaseOrNull(string s) { return s?.ToLowerInvariant(); } /// /// Given a possible short and long file name, create an msi filename value. /// /// The short file name. /// Possibly the long file name. /// The value in the msi filename data type. private string GetMsiFilenameValue(string shortName, string longName) { if (null != shortName && null != longName && !String.Equals(shortName, longName, StringComparison.OrdinalIgnoreCase)) { return String.Concat(shortName, "|", longName); } else { return this.Core.IsValidShortFilename(longName, false) ? longName : shortName; } } /// /// Adds a search property to the active section. /// /// Current source/line number of processing. /// Property to add to search. /// Signature for search. private void AddAppSearch(SourceLineNumber sourceLineNumbers, Identifier propertyId, string signature) { if (!this.Core.EncounteredError) { if (propertyId.Id != propertyId.Id.ToUpperInvariant()) { this.Core.Write(ErrorMessages.SearchPropertyNotUppercase(sourceLineNumbers, "Property", "Id", propertyId.Id)); } var tuple = new AppSearchTuple(sourceLineNumbers, new Identifier(propertyId.Access, propertyId.Id, signature)) { PropertyRef = propertyId.Id, SignatureRef = signature }; this.Core.AddTuple(tuple); } } /// /// Adds a property to the active section. /// /// Current source/line number of processing. /// Identifier of property to add. /// Value of property. /// Flag if property is an admin property. /// Flag if property is a secure property. /// Flag if property is to be hidden. /// Adds the property to a new section. private void AddProperty(SourceLineNumber sourceLineNumbers, Identifier propertyId, string value, bool admin, bool secure, bool hidden, bool fragment) { // properties without a valid identifier should not be processed any further if (null == propertyId || String.IsNullOrEmpty(propertyId.Id)) { return; } if (!String.IsNullOrEmpty(value)) { var regex = new Regex(@"\[(?[a-zA-Z_][a-zA-Z0-9_\.]*)]", RegexOptions.Compiled | RegexOptions.Singleline | RegexOptions.ExplicitCapture); var matches = regex.Matches(value); foreach (Match match in matches) { var group = match.Groups["identifier"]; if (group.Success) { this.Core.Write(WarningMessages.PropertyValueContainsPropertyReference(sourceLineNumbers, propertyId.Id, group.Value)); } } } if (!this.Core.EncounteredError) { var section = this.Core.ActiveSection; // Add the row to a separate section if requested. if (fragment) { var id = String.Concat(this.Core.ActiveSection.Id, ".", propertyId.Id); section = this.Core.CreateSection(id, SectionType.Fragment, this.Core.ActiveSection.Codepage, this.Context.CompilationId); // Reference the property in the active section. this.Core.CreateSimpleReference(sourceLineNumbers, "Property", propertyId.Id); } // Allow row to exist with no value so that PropertyRefs can be made for *Search elements // the linker will remove these rows before the final output is created. var tuple = new PropertyTuple(sourceLineNumbers, propertyId) { Value = value, }; section.Tuples.Add(tuple); if (admin || hidden || secure) { this.AddWixPropertyRow(sourceLineNumbers, propertyId, admin, secure, hidden, section); } } } private void AddWixPropertyRow(SourceLineNumber sourceLineNumbers, Identifier property, bool admin, bool secure, bool hidden, IntermediateSection section = null) { if (secure && property.Id != property.Id.ToUpperInvariant()) { this.Core.Write(ErrorMessages.SecurePropertyNotUppercase(sourceLineNumbers, "Property", "Id", property.Id)); } if (null == section) { section = this.Core.ActiveSection; this.Core.EnsureTable(sourceLineNumbers, "Property"); // Property table is always required when using WixProperty table. } var tuple = new WixPropertyTuple(sourceLineNumbers) { PropertyRef = property.Id, Admin = admin, Hidden = hidden, Secure = secure }; section.Tuples.Add(tuple); } /// /// Adds a "implemented category" registry key to active section. /// /// Current source/line number of processing. /// GUID for category. /// ClassId for to mark "implemented". /// Identifier of parent component. private void RegisterImplementedCategories(SourceLineNumber sourceLineNumbers, string categoryId, string classId, string componentId) { this.Core.CreateRegistryRow(sourceLineNumbers, RegistryRootType.ClassesRoot, String.Concat("CLSID\\", classId, "\\Implemented Categories\\", categoryId), "*", null, componentId); } /// /// Parses an application identifer element. /// /// Element to parse. /// Identifier of parent component. /// The required advertise state (set depending upon the parent). /// Optional file identifier for CLSID when not advertised. /// Optional TypeLib GUID for CLSID. /// Optional TypeLib Version for CLSID Interfaces (if any). private void ParseAppIdElement(XElement node, string componentId, YesNoType advertise, string fileServer, string typeLibId, string typeLibVersion) { var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node); string appId = null; string remoteServerName = null; string localService = null; string serviceParameters = null; string dllSurrogate = null; bool? activateAtStorage = null; var appIdAdvertise = YesNoType.NotSet; bool? runAsInteractiveUser = null; string description = null; foreach (var attrib in node.Attributes()) { if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace) { switch (attrib.Name.LocalName) { case "Id": appId = this.Core.GetAttributeGuidValue(sourceLineNumbers, attrib, false); break; case "ActivateAtStorage": activateAtStorage = YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib); break; case "Advertise": appIdAdvertise = this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib); break; case "Description": description = this.Core.GetAttributeValue(sourceLineNumbers, attrib); break; case "DllSurrogate": dllSurrogate = this.Core.GetAttributeValue(sourceLineNumbers, attrib, EmptyRule.CanBeEmpty); break; case "LocalService": localService = this.Core.GetAttributeValue(sourceLineNumbers, attrib); break; case "RemoteServerName": remoteServerName = this.Core.GetAttributeValue(sourceLineNumbers, attrib); break; case "RunAsInteractiveUser": runAsInteractiveUser = YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib); break; case "ServiceParameters": serviceParameters = this.Core.GetAttributeValue(sourceLineNumbers, attrib); break; default: this.Core.UnexpectedAttribute(node, attrib); break; } } else { this.Core.ParseExtensionAttribute(node, attrib); } } if (null == appId) { this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Id")); } if ((YesNoType.No == advertise && YesNoType.Yes == appIdAdvertise) || (YesNoType.Yes == advertise && YesNoType.No == appIdAdvertise)) { this.Core.Write(ErrorMessages.AppIdIncompatibleAdvertiseState(sourceLineNumbers, node.Name.LocalName, "Advertise", appIdAdvertise.ToString(), advertise.ToString())); } else { advertise = appIdAdvertise; } // 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 "Class": this.ParseClassElement(child, componentId, advertise, fileServer, typeLibId, typeLibVersion, appId); break; default: this.Core.UnexpectedElement(node, child); break; } } else { this.Core.ParseExtensionElement(node, child); } } if (YesNoType.Yes == advertise) { if (null != description) { this.Core.Write(ErrorMessages.IllegalAttributeWhenAdvertised(sourceLineNumbers, node.Name.LocalName, "Description")); } if (!this.Core.EncounteredError) { var tuple = new AppIdTuple(sourceLineNumbers, new Identifier(AccessModifier.Public, appId)) { AppId = appId, RemoteServerName = remoteServerName, LocalService = localService, ServiceParameters = serviceParameters, DllSurrogate = dllSurrogate, ActivateAtStorage = activateAtStorage, RunAsInteractiveUser = runAsInteractiveUser }; this.Core.AddTuple(tuple); } } else if (YesNoType.No == advertise) { if (null != description) { this.Core.CreateRegistryRow(sourceLineNumbers, RegistryRootType.ClassesRoot, String.Concat("AppID\\", appId), null, description, componentId); } else { this.Core.CreateRegistryRow(sourceLineNumbers, RegistryRootType.ClassesRoot, String.Concat("AppID\\", appId), "+", null, componentId); } if (null != remoteServerName) { this.Core.CreateRegistryRow(sourceLineNumbers, RegistryRootType.ClassesRoot, String.Concat("AppID\\", appId), "RemoteServerName", remoteServerName, componentId); } if (null != localService) { this.Core.CreateRegistryRow(sourceLineNumbers, RegistryRootType.ClassesRoot, String.Concat("AppID\\", appId), "LocalService", localService, componentId); } if (null != serviceParameters) { this.Core.CreateRegistryRow(sourceLineNumbers, RegistryRootType.ClassesRoot, String.Concat("AppID\\", appId), "ServiceParameters", serviceParameters, componentId); } if (null != dllSurrogate) { this.Core.CreateRegistryRow(sourceLineNumbers, RegistryRootType.ClassesRoot, String.Concat("AppID\\", appId), "DllSurrogate", dllSurrogate, componentId); } if (true == activateAtStorage) { this.Core.CreateRegistryRow(sourceLineNumbers, RegistryRootType.ClassesRoot, String.Concat("AppID\\", appId), "ActivateAtStorage", "Y", componentId); } if (true == runAsInteractiveUser) { this.Core.CreateRegistryRow(sourceLineNumbers, RegistryRootType.ClassesRoot, String.Concat("AppID\\", appId), "RunAs", "Interactive User", componentId); } } } /// /// Parses an AssemblyName element. /// /// File element to parse. /// Parent's component id. private void ParseAssemblyName(XElement node, string componentId) { var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node); string id = 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.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); } } if (null == id) { this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Id")); } this.Core.ParseForExtensionElements(node); if (!this.Core.EncounteredError) { var tuple = new MsiAssemblyNameTuple(sourceLineNumbers, new Identifier(AccessModifier.Private, componentId, id)) { ComponentRef = componentId, Name = id, Value = value }; this.Core.AddTuple(tuple); } } /// /// Parses a binary element. /// /// Element to parse. /// Identifier for the new row. [SuppressMessage("Microsoft.Performance", "CA1800:DoNotCastUnnecessarily")] private Identifier ParseBinaryElement(XElement node) { var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node); Identifier id = null; string sourceFile = null; var suppressModularization = 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": id = this.Core.GetAttributeIdentifier(sourceLineNumbers, attrib); break; case "SourceFile": case "src": if (null != sourceFile) { this.Core.Write(ErrorMessages.IllegalAttributeWithOtherAttribute(sourceLineNumbers, node.Name.LocalName, "SourceFile", "src")); } if ("src" == attrib.Name.LocalName) { this.Core.Write(WarningMessages.DeprecatedAttribute(sourceLineNumbers, node.Name.LocalName, attrib.Name.LocalName, "SourceFile")); } sourceFile = this.Core.GetAttributeValue(sourceLineNumbers, attrib); break; case "SuppressModularization": suppressModularization = this.Core.GetAttributeYesNoValue(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 (!String.IsNullOrEmpty(id.Id)) // only check legal values { if (55 < id.Id.Length) { this.Core.Write(ErrorMessages.StreamNameTooLong(sourceLineNumbers, node.Name.LocalName, "Id", id.Id, id.Id.Length, 55)); } else if (!this.compilingProduct) // if we're not doing a product then we can't be sure that a binary identifier will fit when modularized { if (18 < id.Id.Length) { this.Core.Write(WarningMessages.IdentifierCannotBeModularized(sourceLineNumbers, node.Name.LocalName, "Id", id.Id, id.Id.Length, 18)); } } } if (null == sourceFile) { this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "SourceFile")); } this.Core.ParseForExtensionElements(node); if (!this.Core.EncounteredError) { var tuple = new BinaryTuple(sourceLineNumbers, id) { Data = sourceFile }; this.Core.AddTuple(tuple); if (YesNoType.Yes == suppressModularization) { this.Core.AddTuple(new WixSuppressModularizationTuple(sourceLineNumbers, id)); } } return id; } /// /// Parses an icon element. /// /// Element to parse. /// Identifier for the new row. private string ParseIconElement(XElement node) { var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node); Identifier id = 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 "Id": id = this.Core.GetAttributeIdentifier(sourceLineNumbers, attrib); 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 == id) { this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Id")); id = Identifier.Invalid; } else if (!String.IsNullOrEmpty(id.Id)) // only check legal values { if (57 < id.Id.Length) { this.Core.Write(ErrorMessages.StreamNameTooLong(sourceLineNumbers, node.Name.LocalName, "Id", id.Id, id.Id.Length, 57)); } else if (!this.compilingProduct) // if we're not doing a product then we can't be sure that a binary identifier will fit when modularized { if (20 < id.Id.Length) { this.Core.Write(WarningMessages.IdentifierCannotBeModularized(sourceLineNumbers, node.Name.LocalName, "Id", id.Id, id.Id.Length, 20)); } } } if (null == sourceFile) { this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "SourceFile")); } this.Core.ParseForExtensionElements(node); if (!this.Core.EncounteredError) { this.Core.AddTuple(new IconTuple(sourceLineNumbers, id) { Data = new IntermediateFieldPathValue { Path = sourceFile } }); } return id.Id; } /// /// Parses an InstanceTransforms element. /// /// Element to parse. private void ParseInstanceTransformsElement(XElement node) { var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node); 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 "Property": property = this.Core.GetAttributeIdentifierValue(sourceLineNumbers, attrib); this.Core.CreateSimpleReference(sourceLineNumbers, "Property", property); break; default: this.Core.UnexpectedAttribute(node, attrib); break; } } else { this.Core.ParseExtensionAttribute(node, attrib); } } if (null == property) { this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Property")); } // find unexpected child elements foreach (var child in node.Elements()) { if (CompilerCore.WixNamespace == child.Name.Namespace) { switch (child.Name.LocalName) { case "Instance": this.ParseInstanceElement(child, property); break; default: this.Core.UnexpectedElement(node, child); break; } } else { this.Core.ParseExtensionElement(node, child); } } } /// /// Parses an instance element. /// /// Element to parse. /// Identifier of instance property. private void ParseInstanceElement(XElement node, string propertyId) { var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node); Identifier id = null; string productCode = null; string productName = null; string upgradeCode = 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 "ProductCode": productCode = this.Core.GetAttributeGuidValue(sourceLineNumbers, attrib, true); break; case "ProductName": productName = this.Core.GetAttributeValue(sourceLineNumbers, attrib); break; case "UpgradeCode": upgradeCode = 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")); } if (null == productCode) { this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "ProductCode")); } this.Core.ParseForExtensionElements(node); if (!this.Core.EncounteredError) { var tuple = new WixInstanceTransformsTuple(sourceLineNumbers, id) { PropertyId = propertyId, ProductCode = productCode, ProductName = productName, UpgradeCode = upgradeCode }; this.Core.AddTuple(tuple); } } /// /// Parses a category element. /// /// Element to parse. /// Identifier of parent component. private void ParseCategoryElement(XElement node, string componentId) { var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node); string id = null; string appData = null; string feature = null; string qualifier = 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; case "AppData": appData = this.Core.GetAttributeValue(sourceLineNumbers, attrib); break; case "Feature": feature = this.Core.GetAttributeIdentifierValue(sourceLineNumbers, attrib); this.Core.CreateSimpleReference(sourceLineNumbers, "Feature", feature); break; case "Qualifier": qualifier = 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 == qualifier) { this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Qualifier")); } this.Core.ParseForExtensionElements(node); if (!this.Core.EncounteredError) { var tuple = new PublishComponentTuple(sourceLineNumbers) { ComponentId = id, Qualifier = qualifier, ComponentRef = componentId, AppData = appData, FeatureRef = feature ?? Guid.Empty.ToString("B"), }; this.Core.AddTuple(tuple); } } /// /// Parses a class element. /// /// Element to parse. /// Identifier of parent component. /// Optional Advertise State for the parent AppId element (if any). /// Optional file identifier for CLSID when not advertised. /// Optional TypeLib GUID for CLSID. /// Optional TypeLib Version for CLSID Interfaces (if any). /// Optional parent AppId. private void ParseClassElement(XElement node, string componentId, YesNoType advertise, string fileServer, string typeLibId, string typeLibVersion, string parentAppId) { var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node); string appId = null; string argument = null; var class16bit = false; var class32bit = false; string classId = null; var classAdvertise = YesNoType.NotSet; var contexts = new string[0]; string formattedContextString = null; var control = false; string defaultInprocHandler = null; string defaultProgId = null; string description = null; string fileTypeMask = null; string foreignServer = null; string icon = null; var iconIndex = CompilerConstants.IntegerNotSet; string insertable = null; string localFileServer = null; var programmable = false; var relativePath = YesNoType.NotSet; var safeForInit = false; var safeForScripting = false; var shortServerPath = false; string threadingModel = null; string version = null; foreach (var attrib in node.Attributes()) { if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace) { switch (attrib.Name.LocalName) { case "Id": classId = this.Core.GetAttributeGuidValue(sourceLineNumbers, attrib, false); break; case "Advertise": classAdvertise = this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib); break; case "AppId": appId = this.Core.GetAttributeGuidValue(sourceLineNumbers, attrib, false); break; case "Argument": argument = this.Core.GetAttributeValue(sourceLineNumbers, attrib); break; case "Context": contexts = this.Core.GetAttributeValue(sourceLineNumbers, attrib).Split("\r\n\t ".ToCharArray(), StringSplitOptions.RemoveEmptyEntries); break; case "Control": control = YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib); break; case "Description": description = this.Core.GetAttributeValue(sourceLineNumbers, attrib); break; case "Handler": defaultInprocHandler = this.Core.GetAttributeValue(sourceLineNumbers, attrib); 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 "RelativePath": relativePath = this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib); break; // The following attributes result in rows always added to the Registry table rather than the Class table case "Insertable": insertable = (YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib)) ? "Insertable" : "NotInsertable"; break; case "Programmable": programmable = YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib); break; case "SafeForInitializing": safeForInit = YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib); break; case "SafeForScripting": safeForScripting = YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib); break; case "ForeignServer": foreignServer = this.Core.GetAttributeValue(sourceLineNumbers, attrib); break; case "Server": localFileServer = this.Core.GetAttributeValue(sourceLineNumbers, attrib); break; case "ShortPath": shortServerPath = YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib); break; case "ThreadingModel": threadingModel = this.Core.GetAttributeValue(sourceLineNumbers, attrib); break; case "Version": version = this.Core.GetAttributeValue(sourceLineNumbers, attrib); break; default: this.Core.UnexpectedAttribute(node, attrib); break; } } else { this.Core.ParseExtensionAttribute(node, attrib); } } if (null == classId) { this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Id")); } var uniqueContexts = new HashSet(); foreach (var context in contexts) { if (uniqueContexts.Contains(context)) { this.Core.Write(ErrorMessages.DuplicateContextValue(sourceLineNumbers, context)); } else { uniqueContexts.Add(context); } if (context.EndsWith("32", StringComparison.Ordinal)) { class32bit = true; } else { class16bit = true; } } if ((YesNoType.No == advertise && YesNoType.Yes == classAdvertise) || (YesNoType.Yes == advertise && YesNoType.No == classAdvertise)) { this.Core.Write(ErrorMessages.AdvertiseStateMustMatch(sourceLineNumbers, classAdvertise.ToString(), advertise.ToString())); } else { advertise = classAdvertise; } // If the advertise state has not been set, default to non-advertised. if (YesNoType.NotSet == advertise) { advertise = YesNoType.No; } if (YesNoType.Yes == advertise && 0 == contexts.Length) { this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Context", "Advertise", "yes")); } if (!String.IsNullOrEmpty(parentAppId) && !String.IsNullOrEmpty(appId)) { this.Core.Write(ErrorMessages.IllegalAttributeWhenNested(sourceLineNumbers, node.Name.LocalName, "AppId", node.Parent.Name.LocalName)); } if (!String.IsNullOrEmpty(localFileServer)) { this.Core.CreateSimpleReference(sourceLineNumbers, "File", localFileServer); } // Local variables used strictly for child node processing. var fileTypeMaskIndex = 0; var firstProgIdForClass = YesNoType.Yes; foreach (var child in node.Elements()) { if (CompilerCore.WixNamespace == child.Name.Namespace) { switch (child.Name.LocalName) { case "FileTypeMask": if (YesNoType.Yes == advertise) { fileTypeMask = String.Concat(fileTypeMask, null == fileTypeMask ? String.Empty : ";", this.ParseFileTypeMaskElement(child)); } else if (YesNoType.No == advertise) { var childSourceLineNumbers = Preprocessor.GetSourceLineNumbers(child); this.Core.CreateRegistryRow(childSourceLineNumbers, RegistryRootType.ClassesRoot, String.Concat("FileType\\", classId, "\\", fileTypeMaskIndex.ToString()), String.Empty, this.ParseFileTypeMaskElement(child), componentId); fileTypeMaskIndex++; } break; case "Interface": this.ParseInterfaceElement(child, componentId, class16bit ? classId : null, class32bit ? classId : null, typeLibId, typeLibVersion); break; case "ProgId": { var foundExtension = false; var progId = this.ParseProgIdElement(child, componentId, advertise, classId, description, null, ref foundExtension, firstProgIdForClass); if (null == defaultProgId) { defaultProgId = progId; } firstProgIdForClass = YesNoType.No; } break; default: this.Core.UnexpectedElement(node, child); break; } } else { this.Core.ParseExtensionElement(node, child); } } // If this Class is being advertised. if (YesNoType.Yes == advertise) { if (null != fileServer || null != localFileServer) { this.Core.Write(ErrorMessages.IllegalAttributeWithOtherAttribute(sourceLineNumbers, node.Name.LocalName, "Server", "Advertise", "yes")); } if (null != foreignServer) { this.Core.Write(ErrorMessages.IllegalAttributeWithOtherAttribute(sourceLineNumbers, node.Name.LocalName, "ForeignServer", "Advertise", "yes")); } if (null == appId && null != parentAppId) { appId = parentAppId; } // add a Class row for each context if (!this.Core.EncounteredError) { foreach (var context in contexts) { var tuple = new ClassTuple(sourceLineNumbers) { CLSID = classId, Context = context, ComponentRef = componentId, DefaultProgIdRef = defaultProgId, Description = description, FileTypeMask = fileTypeMask, DefInprocHandler = defaultInprocHandler, Argument = argument, FeatureRef = Guid.Empty.ToString("B"), RelativePath = YesNoType.Yes == relativePath, }; if (null != appId) { tuple.AppIdRef = appId; this.Core.CreateSimpleReference(sourceLineNumbers, "AppId", appId); } if (null != icon) { tuple.IconRef = icon; this.Core.CreateSimpleReference(sourceLineNumbers, "Icon", icon); } if (CompilerConstants.IntegerNotSet != iconIndex) { tuple.IconIndex = iconIndex; } this.Core.AddTuple(tuple); } } } else if (YesNoType.No == advertise) { if (null == fileServer && null == localFileServer && null == foreignServer) { this.Core.Write(ErrorMessages.ExpectedAttributes(sourceLineNumbers, node.Name.LocalName, "ForeignServer", "Server")); } if (null != fileServer && null != foreignServer) { this.Core.Write(ErrorMessages.IllegalAttributeWhenNested(sourceLineNumbers, node.Name.LocalName, "ForeignServer", "File")); } else if (null != localFileServer && null != foreignServer) { this.Core.Write(ErrorMessages.IllegalAttributeWithOtherAttribute(sourceLineNumbers, node.Name.LocalName, "ForeignServer", "Server")); } else if (null == fileServer) { fileServer = localFileServer; } if (null != appId) // need to use nesting (not a reference) for the unadvertised Class elements { this.Core.Write(ErrorMessages.IllegalAttributeWithOtherAttribute(sourceLineNumbers, node.Name.LocalName, "AppId", "Advertise", "no")); } // add the core registry keys for each context in the class foreach (var context in contexts) { if (context.StartsWith("InprocServer", StringComparison.Ordinal)) // dll server { if (null != argument) { this.Core.Write(ErrorMessages.IllegalAttributeWithOtherAttribute(sourceLineNumbers, node.Name.LocalName, "Arguments", "Context", context)); } if (null != fileServer) { formattedContextString = String.Concat("[", shortServerPath ? "!" : "#", fileServer, "]"); } else if (null != foreignServer) { formattedContextString = foreignServer; } } else if (context.StartsWith("LocalServer", StringComparison.Ordinal)) // exe server (quote the long path) { if (null != fileServer) { if (shortServerPath) { formattedContextString = String.Concat("[!", fileServer, "]"); } else { formattedContextString = String.Concat("\"[#", fileServer, "]\""); } } else if (null != foreignServer) { formattedContextString = foreignServer; } if (null != argument) { formattedContextString = String.Concat(formattedContextString, " ", argument); } } else { this.Core.Write(ErrorMessages.IllegalAttributeValue(sourceLineNumbers, node.Name.LocalName, "Context", context, "InprocServer", "InprocServer32", "LocalServer", "LocalServer32")); } this.Core.CreateRegistryRow(sourceLineNumbers, RegistryRootType.ClassesRoot, String.Concat("CLSID\\", classId, "\\", context), String.Empty, formattedContextString, componentId); // ClassId context if (null != icon) // ClassId default icon { this.Core.CreateSimpleReference(sourceLineNumbers, "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("CLSID\\", classId, "\\DefaultIcon"), String.Empty, icon, componentId); } } if (null != parentAppId) // ClassId AppId (must be specified via nesting, not with the AppId attribute) { this.Core.CreateRegistryRow(sourceLineNumbers, RegistryRootType.ClassesRoot, String.Concat("CLSID\\", classId), "AppID", parentAppId, componentId); } if (null != description) // ClassId description { this.Core.CreateRegistryRow(sourceLineNumbers, RegistryRootType.ClassesRoot, String.Concat("CLSID\\", classId), String.Empty, description, componentId); } if (null != defaultInprocHandler) { switch (defaultInprocHandler) // ClassId Default Inproc Handler { case "1": this.Core.CreateRegistryRow(sourceLineNumbers, RegistryRootType.ClassesRoot, String.Concat("CLSID\\", classId, "\\InprocHandler"), String.Empty, "ole2.dll", componentId); break; case "2": this.Core.CreateRegistryRow(sourceLineNumbers, RegistryRootType.ClassesRoot, String.Concat("CLSID\\", classId, "\\InprocHandler32"), String.Empty, "ole32.dll", componentId); break; case "3": this.Core.CreateRegistryRow(sourceLineNumbers, RegistryRootType.ClassesRoot, String.Concat("CLSID\\", classId, "\\InprocHandler"), String.Empty, "ole2.dll", componentId); this.Core.CreateRegistryRow(sourceLineNumbers, RegistryRootType.ClassesRoot, String.Concat("CLSID\\", classId, "\\InprocHandler32"), String.Empty, "ole32.dll", componentId); break; default: this.Core.CreateRegistryRow(sourceLineNumbers, RegistryRootType.ClassesRoot, String.Concat("CLSID\\", classId, "\\InprocHandler32"), String.Empty, defaultInprocHandler, componentId); break; } } if (YesNoType.NotSet != relativePath) // ClassId's RelativePath { this.Core.Write(ErrorMessages.RelativePathForRegistryElement(sourceLineNumbers)); } } if (null != threadingModel) { threadingModel = Compiler.UppercaseFirstChar(threadingModel); // add a threading model for each context in the class foreach (var context in contexts) { this.Core.CreateRegistryRow(sourceLineNumbers, RegistryRootType.ClassesRoot, String.Concat("CLSID\\", classId, "\\", context), "ThreadingModel", threadingModel, componentId); } } if (null != typeLibId) { this.Core.CreateRegistryRow(sourceLineNumbers, RegistryRootType.ClassesRoot, String.Concat("CLSID\\", classId, "\\TypeLib"), null, typeLibId, componentId); } if (null != version) { this.Core.CreateRegistryRow(sourceLineNumbers, RegistryRootType.ClassesRoot, String.Concat("CLSID\\", classId, "\\Version"), null, version, componentId); } if (null != insertable) { // Add "*" for name so that any subkeys (shouldn't be any) are removed on uninstall. this.Core.CreateRegistryRow(sourceLineNumbers, RegistryRootType.ClassesRoot, String.Concat("CLSID\\", classId, "\\", insertable), "*", null, componentId); } if (control) { // Add "*" for name so that any subkeys (shouldn't be any) are removed on uninstall. this.Core.CreateRegistryRow(sourceLineNumbers, RegistryRootType.ClassesRoot, String.Concat("CLSID\\", classId, "\\Control"), "*", null, componentId); } if (programmable) { // Add "*" for name so that any subkeys (shouldn't be any) are removed on uninstall. this.Core.CreateRegistryRow(sourceLineNumbers, RegistryRootType.ClassesRoot, String.Concat("CLSID\\", classId, "\\Programmable"), "*", null, componentId); } if (safeForInit) { this.RegisterImplementedCategories(sourceLineNumbers, "{7DD95802-9882-11CF-9FA9-00AA006C42C4}", classId, componentId); } if (safeForScripting) { this.RegisterImplementedCategories(sourceLineNumbers, "{7DD95801-9882-11CF-9FA9-00AA006C42C4}", classId, componentId); } } /// /// Parses an Interface element. /// /// Element to parse. /// Identifier of parent component. /// 16-bit proxy for interface. /// 32-bit proxy for interface. /// Optional TypeLib GUID for CLSID. /// Version of the TypeLib to which this interface belongs. Required if typeLibId is specified private void ParseInterfaceElement(XElement node, string componentId, string proxyId, string proxyId32, string typeLibId, string typelibVersion) { var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node); string baseInterface = null; string interfaceId = null; string name = null; var numMethods = CompilerConstants.IntegerNotSet; var versioned = true; foreach (var attrib in node.Attributes()) { if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace) { switch (attrib.Name.LocalName) { case "Id": interfaceId = this.Core.GetAttributeGuidValue(sourceLineNumbers, attrib, false); break; case "BaseInterface": baseInterface = this.Core.GetAttributeGuidValue(sourceLineNumbers, attrib, false); break; case "Name": name = this.Core.GetAttributeValue(sourceLineNumbers, attrib); break; case "NumMethods": numMethods = this.Core.GetAttributeIntegerValue(sourceLineNumbers, attrib, 0, Int32.MaxValue); break; case "ProxyStubClassId": proxyId = this.Core.GetAttributeGuidValue(sourceLineNumbers, attrib); break; case "ProxyStubClassId32": proxyId32 = this.Core.GetAttributeGuidValue(sourceLineNumbers, attrib, false); break; case "Versioned": versioned = YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib); break; default: this.Core.UnexpectedAttribute(node, attrib); break; } } else { this.Core.ParseExtensionAttribute(node, attrib); } } if (null == interfaceId) { this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Id")); } if (null == name) { this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Name")); } this.Core.ParseForExtensionElements(node); this.Core.CreateRegistryRow(sourceLineNumbers, RegistryRootType.ClassesRoot, String.Concat("Interface\\", interfaceId), null, name, componentId); if (null != typeLibId) { this.Core.CreateRegistryRow(sourceLineNumbers, RegistryRootType.ClassesRoot, String.Concat("Interface\\", interfaceId, "\\TypeLib"), null, typeLibId, componentId); if (versioned) { this.Core.CreateRegistryRow(sourceLineNumbers, RegistryRootType.ClassesRoot, String.Concat("Interface\\", interfaceId, "\\TypeLib"), "Version", typelibVersion, componentId); } } if (null != baseInterface) { this.Core.CreateRegistryRow(sourceLineNumbers, RegistryRootType.ClassesRoot, String.Concat("Interface\\", interfaceId, "\\BaseInterface"), null, baseInterface, componentId); } if (CompilerConstants.IntegerNotSet != numMethods) { this.Core.CreateRegistryRow(sourceLineNumbers, RegistryRootType.ClassesRoot, String.Concat("Interface\\", interfaceId, "\\NumMethods"), null, numMethods.ToString(), componentId); } if (null != proxyId) { this.Core.CreateRegistryRow(sourceLineNumbers, RegistryRootType.ClassesRoot, String.Concat("Interface\\", interfaceId, "\\ProxyStubClsid"), null, proxyId, componentId); } if (null != proxyId32) { this.Core.CreateRegistryRow(sourceLineNumbers, RegistryRootType.ClassesRoot, String.Concat("Interface\\", interfaceId, "\\ProxyStubClsid32"), null, proxyId32, componentId); } } /// /// Parses a CLSID's file type mask element. /// /// Element to parse. /// String representing the file type mask elements. private string ParseFileTypeMaskElement(XElement node) { var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node); var cb = 0; var offset = CompilerConstants.IntegerNotSet; string mask = 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 "Mask": mask = this.Core.GetAttributeValue(sourceLineNumbers, attrib); break; case "Offset": offset = this.Core.GetAttributeIntegerValue(sourceLineNumbers, attrib, 0, Int32.MaxValue); 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 == mask) { this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Mask")); } if (CompilerConstants.IntegerNotSet == offset) { this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Offset")); } if (null == value) { this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Value")); } this.Core.ParseForExtensionElements(node); if (!this.Core.EncounteredError) { if (mask.Length != value.Length) { this.Core.Write(ErrorMessages.ValueAndMaskMustBeSameLength(sourceLineNumbers)); } cb = mask.Length / 2; } return String.Concat(offset.ToString(CultureInfo.InvariantCulture.NumberFormat), ",", cb.ToString(CultureInfo.InvariantCulture.NumberFormat), ",", mask, ",", value); } /// /// Parses a product search element. /// /// Element to parse. /// Signature for search element. private void ParseProductSearchElement(XElement node, string propertyId) { var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node); string upgradeCode = null; string language = null; string maximum = null; string minimum = null; var excludeLanguages = false; var maxInclusive = false; var minInclusive = true; 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 "IncludeMaximum": maxInclusive = YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib); break; case "IncludeMinimum": minInclusive = YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib); break; case "Language": language = this.Core.GetAttributeValue(sourceLineNumbers, attrib); break; case "Minimum": minimum = this.Core.GetAttributeValue(sourceLineNumbers, attrib); break; case "Maximum": maximum = this.Core.GetAttributeValue(sourceLineNumbers, attrib); break; case "UpgradeCode": upgradeCode = this.Core.GetAttributeGuidValue(sourceLineNumbers, attrib, false); break; default: this.Core.UnexpectedAttribute(node, attrib); break; } } else { this.Core.ParseExtensionAttribute(node, attrib); } } if (null == minimum && null == maximum) { this.Core.Write(ErrorMessages.ExpectedAttributes(sourceLineNumbers, node.Name.LocalName, "Minimum", "Maximum")); } this.Core.ParseForExtensionElements(node); if (!this.Core.EncounteredError) { var tuple = new UpgradeTuple(sourceLineNumbers) { UpgradeCode = upgradeCode, VersionMin = minimum, VersionMax = maximum, Language = language, ActionProperty = propertyId, OnlyDetect = true, ExcludeLanguages = excludeLanguages, VersionMaxInclusive = maxInclusive, VersionMinInclusive = minInclusive, }; this.Core.AddTuple(tuple); } } /// /// Parses a registry search element. /// /// Element to parse. /// Signature for search element. private string ParseRegistrySearchElement(XElement node) { var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node); var explicitWin64 = false; Identifier id = null; string key = null; string name = null; string signature = null; RegistryRootType? root = null; RegLocatorType? type = null; var search64bit = 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 "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, false); break; case "Type": var typeValue = this.Core.GetAttributeValue(sourceLineNumbers, attrib); switch (typeValue) { case "directory": type = RegLocatorType.Directory; break; case "file": type = RegLocatorType.FileName; break; case "raw": type = RegLocatorType.Raw; break; case "": break; default: this.Core.Write(ErrorMessages.IllegalAttributeValue(sourceLineNumbers, node.Name.LocalName, "Type", typeValue, "directory", "file", "raw")); break; } break; case "Win64": explicitWin64 = true; search64bit = YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib); break; default: this.Core.UnexpectedAttribute(node, attrib); break; } } else { this.Core.ParseExtensionAttribute(node, attrib); } } if (!explicitWin64 && (Platform.IA64 == this.CurrentPlatform || Platform.X64 == this.CurrentPlatform)) { search64bit = true; } if (null == id) { id = this.Core.CreateIdentifier("reg", root.ToString(), key, name, type.ToString(), search64bit.ToString()); } 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")); } if (!type.HasValue) { this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Type")); } signature = id.Id; var oneChild = false; foreach (var child in node.Elements()) { if (CompilerCore.WixNamespace == child.Name.Namespace) { switch (child.Name.LocalName) { case "DirectorySearch": if (oneChild) { this.Core.Write(ErrorMessages.TooManySearchElements(sourceLineNumbers, node.Name.LocalName)); } oneChild = true; // directorysearch parentage should work like directory element, not the rest of the signature type because of the DrLocator.Parent column signature = this.ParseDirectorySearchElement(child, id.Id); break; case "DirectorySearchRef": if (oneChild) { this.Core.Write(ErrorMessages.TooManySearchElements(sourceLineNumbers, node.Name.LocalName)); } oneChild = true; signature = this.ParseDirectorySearchRefElement(child, id.Id); break; case "FileSearch": if (oneChild) { this.Core.Write(ErrorMessages.TooManySearchElements(sourceLineNumbers, node.Name.LocalName)); } oneChild = true; signature = this.ParseFileSearchElement(child, id.Id, false, CompilerConstants.IntegerNotSet); id = new Identifier(AccessModifier.Private, signature); // FileSearch signatures override parent signatures break; case "FileSearchRef": if (oneChild) { this.Core.Write(ErrorMessages.TooManySearchElements(sourceLineNumbers, node.Name.LocalName)); } oneChild = true; var newId = this.ParseSimpleRefElement(child, "Signature"); // FileSearch signatures override parent signatures id = new Identifier(AccessModifier.Private, newId); signature = null; break; default: this.Core.UnexpectedElement(node, child); break; } } else { this.Core.ParseExtensionElement(node, child); } } if (!this.Core.EncounteredError) { var tuple = new RegLocatorTuple(sourceLineNumbers, id) { Root = root.Value, Key = key, Name = name, Type = type.Value, Win64 = search64bit }; this.Core.AddTuple(tuple); } return signature; } /// /// Parses a registry search reference element. /// /// Element to parse. /// Signature of referenced search element. private string ParseRegistrySearchRefElement(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.GetAttributeIdentifierValue(sourceLineNumbers, attrib); this.Core.CreateSimpleReference(sourceLineNumbers, "RegLocator", id); 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); return id; // the id of the RegistrySearchRef element is its signature } /// /// Parses child elements for search signatures. /// /// Node whose children we are parsing. /// Returns list of string signatures. private List ParseSearchSignatures(XElement node) { var signatures = new List(); foreach (var child in node.Elements()) { string signature = null; if (CompilerCore.WixNamespace == child.Name.Namespace) { switch (child.Name.LocalName) { case "ComplianceDrive": signature = this.ParseComplianceDriveElement(child); break; case "ComponentSearch": signature = this.ParseComponentSearchElement(child); break; case "DirectorySearch": signature = this.ParseDirectorySearchElement(child, String.Empty); break; case "DirectorySearchRef": signature = this.ParseDirectorySearchRefElement(child, String.Empty); break; case "IniFileSearch": signature = this.ParseIniFileSearchElement(child); break; case "ProductSearch": // handled in ParsePropertyElement break; case "RegistrySearch": signature = this.ParseRegistrySearchElement(child); break; case "RegistrySearchRef": signature = this.ParseRegistrySearchRefElement(child); break; default: this.Core.UnexpectedElement(node, child); break; } } else { this.Core.ParseExtensionElement(node, child); } if (!String.IsNullOrEmpty(signature)) { signatures.Add(signature); } } return signatures; } /// /// Parses a compliance drive element. /// /// Element to parse. /// Signature of nested search elements. private string ParseComplianceDriveElement(XElement node) { var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node); string signature = null; var oneChild = false; foreach (var child in node.Elements()) { if (CompilerCore.WixNamespace == child.Name.Namespace) { var childSourceLineNumbers = Preprocessor.GetSourceLineNumbers(node); switch (child.Name.LocalName) { case "DirectorySearch": if (oneChild) { this.Core.Write(ErrorMessages.TooManySearchElements(childSourceLineNumbers, node.Name.LocalName)); } oneChild = true; signature = this.ParseDirectorySearchElement(child, "CCP_DRIVE"); break; case "DirectorySearchRef": if (oneChild) { this.Core.Write(ErrorMessages.TooManySearchElements(childSourceLineNumbers, node.Name.LocalName)); } oneChild = true; signature = this.ParseDirectorySearchRefElement(child, "CCP_DRIVE"); break; default: this.Core.UnexpectedElement(node, child); break; } } else { this.Core.ParseExtensionElement(node, child); } } if (null == signature) { this.Core.Write(ErrorMessages.SearchElementRequired(sourceLineNumbers, node.Name.LocalName)); } return signature; } /// /// Parses a compilance check element. /// /// Element to parse. private void ParseComplianceCheckElement(XElement node) { var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node); foreach (var attrib in node.Attributes()) { if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace) { switch (attrib.Name.LocalName) { default: this.Core.UnexpectedAttribute(node, attrib); break; } } else { this.Core.ParseExtensionAttribute(node, attrib); } } string signature = null; // see if this property is used for appSearch var signatures = this.ParseSearchSignatures(node); foreach (var sig in signatures) { // if we haven't picked a signature for this ComplianceCheck pick // this one if (null == signature) { signature = sig; } else if (signature != sig) { // all signatures under a ComplianceCheck must be the same this.Core.Write(ErrorMessages.MultipleIdentifiersFound(sourceLineNumbers, node.Name.LocalName, sig, signature)); } } if (null == signature) { this.Core.Write(ErrorMessages.SearchElementRequired(sourceLineNumbers, node.Name.LocalName)); } if (!this.Core.EncounteredError) { this.Core.AddTuple(new CCPSearchTuple(sourceLineNumbers, new Identifier(AccessModifier.Private, signature))); } } /// /// Parses a component element. /// /// Element to parse. /// Type of component's complex reference parent. Will be Uknown if there is no parent. /// Optional identifier for component's primary parent. /// Optional string for component's parent's language. /// Optional disk id inherited from parent directory. /// Optional identifier for component's directory. /// Optional source path for files up to this point. [SuppressMessage("Microsoft.Performance", "CA1800:DoNotCastUnnecessarily")] private void ParseComponentElement(XElement node, ComplexReferenceParentType parentType, string parentId, string parentLanguage, int diskId, string directoryId, string srcPath) { var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node); var comPlusBits = CompilerConstants.IntegerNotSet; string condition = null; var encounteredODBCDataSource = false; var files = 0; var guid = "*"; var componentIdPlaceholder = String.Format(Compiler.DefaultComponentIdPlaceholderFormat, this.componentIdPlaceholdersResolver.VariableCount); // placeholder id for defaulting Component/@Id to keypath id. var componentIdPlaceholderWixVariable = String.Format(Compiler.DefaultComponentIdPlaceholderWixVariableFormat, componentIdPlaceholder); var id = new Identifier(AccessModifier.Private, componentIdPlaceholderWixVariable); var keyFound = false; string keyPath = null; var shouldAddCreateFolder = false; var keyPathType = ComponentKeyPathType.Directory; var location = ComponentLocation.LocalOnly; var disableRegistryReflection = false; var neverOverwrite = false; var permanent = false; var shared = false; var sharedDllRefCount = false; var transitive = false; var uninstallWhenSuperseded = false; var explicitWin64 = false; var win64 = false; var multiInstance = false; var symbols = new List(); string feature = 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 "ComPlusFlags": comPlusBits = this.Core.GetAttributeIntegerValue(sourceLineNumbers, attrib, 0, Int16.MaxValue); break; case "DisableRegistryReflection": disableRegistryReflection = YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib); //if (YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib)) //{ // bits |= MsiInterop.MsidbComponentAttributesDisableRegistryReflection; //} break; case "Directory": directoryId = this.Core.CreateDirectoryReferenceFromInlineSyntax(sourceLineNumbers, attrib, directoryId); break; case "DiskId": diskId = this.Core.GetAttributeIntegerValue(sourceLineNumbers, attrib, 1, Int16.MaxValue); break; case "Feature": feature = this.Core.GetAttributeIdentifierValue(sourceLineNumbers, attrib); break; case "Guid": guid = this.Core.GetAttributeGuidValue(sourceLineNumbers, attrib, true, true); break; case "KeyPath": if (YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib)) { keyFound = true; keyPath = null; shouldAddCreateFolder = true; } break; case "Location": var locationValue = this.Core.GetAttributeValue(sourceLineNumbers, attrib); switch (locationValue) { case "either": location = ComponentLocation.Either; //bits |= MsiInterop.MsidbComponentAttributesOptional; break; case "local": // this is the default location = ComponentLocation.LocalOnly; break; case "source": location = ComponentLocation.SourceOnly; //bits |= MsiInterop.MsidbComponentAttributesSourceOnly; break; case "": break; default: this.Core.Write(ErrorMessages.IllegalAttributeValue(sourceLineNumbers, node.Name.LocalName, attrib.Name.LocalName, locationValue, "either", "local", "source")); break; } break; case "MultiInstance": multiInstance = YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib); break; case "NeverOverwrite": neverOverwrite = YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib); //if (YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib)) //{ // bits |= MsiInterop.MsidbComponentAttributesNeverOverwrite; //} break; case "Permanent": permanent = YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib); //if (YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib)) //{ // bits |= MsiInterop.MsidbComponentAttributesPermanent; //} break; case "Shared": shared = YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib); //if (YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib)) //{ // bits |= MsiInterop.MsidbComponentAttributesShared; //} break; case "SharedDllRefCount": sharedDllRefCount = YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib); //if (YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib)) //{ // bits |= MsiInterop.MsidbComponentAttributesSharedDllRefCount; //} break; case "Transitive": transitive = YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib); //if (YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib)) //{ // bits |= MsiInterop.MsidbComponentAttributesTransitive; //} break; case "UninstallWhenSuperseded": uninstallWhenSuperseded = YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib); //if (YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib)) //{ // bits |= MsiInterop.MsidbComponentAttributesUninstallOnSupersedence; //} break; case "Win64": explicitWin64 = true; win64 = YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib); //if (YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib)) //{ // bits |= MsiInterop.MsidbComponentAttributes64bit; // win64 = true; //} break; default: this.Core.UnexpectedAttribute(node, attrib); break; } } else { this.Core.ParseExtensionAttribute(node, attrib); } } if (!explicitWin64 && (Platform.IA64 == this.CurrentPlatform || Platform.X64 == this.CurrentPlatform)) { //bits |= MsiInterop.MsidbComponentAttributes64bit; win64 = true; } if (null == directoryId) { this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Directory")); } if (String.IsNullOrEmpty(guid) && shared /*MsiInterop.MsidbComponentAttributesShared == (bits & MsiInterop.MsidbComponentAttributesShared)*/) { this.Core.Write(ErrorMessages.IllegalAttributeValueWithOtherAttribute(sourceLineNumbers, node.Name.LocalName, "Shared", "yes", "Guid", "")); } if (String.IsNullOrEmpty(guid) && permanent /*MsiInterop.MsidbComponentAttributesPermanent == (bits & MsiInterop.MsidbComponentAttributesPermanent)*/) { this.Core.Write(ErrorMessages.IllegalAttributeValueWithOtherAttribute(sourceLineNumbers, node.Name.LocalName, "Permanent", "yes", "Guid", "")); } if (null != feature) { if (this.compilingModule) { this.Core.Write(ErrorMessages.IllegalAttributeInMergeModule(sourceLineNumbers, node.Name.LocalName, "Feature")); } else { if (ComplexReferenceParentType.Feature == parentType || ComplexReferenceParentType.FeatureGroup == parentType) { this.Core.Write(ErrorMessages.IllegalAttributeWhenNested(sourceLineNumbers, node.Name.LocalName, "Feature", node.Parent.Name.LocalName)); } else { this.Core.CreateComplexReference(sourceLineNumbers, ComplexReferenceParentType.Feature, feature, null, ComplexReferenceChildType.Component, id.Id, true); } } } foreach (var child in node.Elements()) { var keyPathSet = YesNoType.NotSet; string keyPossible = null; ComponentKeyPathType? keyBit = null; if (CompilerCore.WixNamespace == child.Name.Namespace) { switch (child.Name.LocalName) { case "AppId": this.ParseAppIdElement(child, id.Id, YesNoType.NotSet, null, null, null); break; case "Category": this.ParseCategoryElement(child, id.Id); break; case "Class": this.ParseClassElement(child, id.Id, YesNoType.NotSet, null, null, null, null); break; case "Condition": if (null != condition) { var childSourceLineNumbers = Preprocessor.GetSourceLineNumbers(node); this.Core.Write(ErrorMessages.TooManyChildren(childSourceLineNumbers, node.Name.LocalName, child.Name.LocalName)); } condition = this.ParseConditionElement(child, node.Name.LocalName, null, null); break; case "CopyFile": this.ParseCopyFileElement(child, id.Id, null); break; case "CreateFolder": var createdFolder = this.ParseCreateFolderElement(child, id.Id, directoryId, win64); if (directoryId == createdFolder) { shouldAddCreateFolder = false; } break; case "Environment": this.ParseEnvironmentElement(child, id.Id); break; case "Extension": this.ParseExtensionElement(child, id.Id, YesNoType.NotSet, null); break; case "File": keyPathSet = this.ParseFileElement(child, id.Id, directoryId, diskId, srcPath, out keyPossible, win64, guid); keyBit = ComponentKeyPathType.File; files++; break; case "IniFile": this.ParseIniFileElement(child, id.Id); break; case "Interface": this.ParseInterfaceElement(child, id.Id, null, null, null, null); break; case "IsolateComponent": this.ParseIsolateComponentElement(child, id.Id); break; case "ODBCDataSource": keyPathSet = this.ParseODBCDataSource(child, id.Id, null, out keyPossible); keyBit = ComponentKeyPathType.OdbcDataSource; encounteredODBCDataSource = true; break; case "ODBCDriver": this.ParseODBCDriverOrTranslator(child, id.Id, null, TupleDefinitionType.ODBCDriver); break; case "ODBCTranslator": this.ParseODBCDriverOrTranslator(child, id.Id, null, TupleDefinitionType.ODBCTranslator); break; case "ProgId": var foundExtension = false; this.ParseProgIdElement(child, id.Id, YesNoType.NotSet, null, null, null, ref foundExtension, YesNoType.NotSet); break; case "RegistryKey": keyPathSet = this.ParseRegistryKeyElement(child, id.Id, null, null, win64, out keyPossible); keyBit = ComponentKeyPathType.Registry; break; case "RegistryValue": keyPathSet = this.ParseRegistryValueElement(child, id.Id, null, null, win64, out keyPossible); keyBit = ComponentKeyPathType.Registry; break; case "RemoveFile": this.ParseRemoveFileElement(child, id.Id, directoryId); break; case "RemoveFolder": this.ParseRemoveFolderElement(child, id.Id, directoryId); break; case "RemoveRegistryKey": this.ParseRemoveRegistryKeyElement(child, id.Id); break; case "RemoveRegistryValue": this.ParseRemoveRegistryValueElement(child, id.Id); break; case "ReserveCost": this.ParseReserveCostElement(child, id.Id, directoryId); break; case "ServiceConfig": this.ParseServiceConfigElement(child, id.Id, null); break; case "ServiceConfigFailureActions": this.ParseServiceConfigFailureActionsElement(child, id.Id, null); break; case "ServiceControl": this.ParseServiceControlElement(child, id.Id); break; case "ServiceInstall": this.ParseServiceInstallElement(child, id.Id, win64); break; case "Shortcut": this.ParseShortcutElement(child, id.Id, node.Name.LocalName, directoryId, YesNoType.No); break; case "SymbolPath": symbols.Add(this.ParseSymbolPathElement(child)); break; case "TypeLib": this.ParseTypeLibElement(child, id.Id, null, win64); break; default: this.Core.UnexpectedElement(node, child); break; } } else { var context = new Dictionary() { { "ComponentId", id.Id }, { "DirectoryId", directoryId }, { "Win64", win64.ToString() }, }; var possibleKeyPath = this.Core.ParsePossibleKeyPathExtensionElement(node, child, context); if (null != possibleKeyPath) { if (PossibleKeyPathType.None == possibleKeyPath.Type) { keyPathSet = YesNoType.No; } else { keyPathSet = possibleKeyPath.Explicit ? YesNoType.Yes : YesNoType.NotSet; if (!String.IsNullOrEmpty(possibleKeyPath.Id)) { keyPossible = possibleKeyPath.Id; } if (PossibleKeyPathType.Registry == possibleKeyPath.Type || PossibleKeyPathType.RegistryFormatted == possibleKeyPath.Type) { keyBit = ComponentKeyPathType.Registry; //MsiInterop.MsidbComponentAttributesRegistryKeyPath; } } } } // Verify that either the key path is not set, or it is set along with a key path ID. Debug.Assert(YesNoType.Yes != keyPathSet || (YesNoType.Yes == keyPathSet && null != keyPossible)); if (keyFound && YesNoType.Yes == keyPathSet) { this.Core.Write(ErrorMessages.ComponentMultipleKeyPaths(sourceLineNumbers, node.Name.LocalName, "KeyPath", "yes", "File", "RegistryValue", "ODBCDataSource")); } // if a possible KeyPath has been found and that value was explicitly set as // the KeyPath of the component, set it now. Alternatively, if a possible // KeyPath has been found and no KeyPath has been previously set, use this // value as the default KeyPath of the component if (!String.IsNullOrEmpty(keyPossible) && (YesNoType.Yes == keyPathSet || (YesNoType.NotSet == keyPathSet && String.IsNullOrEmpty(keyPath) && !keyFound))) { keyFound = YesNoType.Yes == keyPathSet; keyPath = keyPossible; keyPathType = keyBit.Value; } } if (shouldAddCreateFolder) { var tuple = new CreateFolderTuple(sourceLineNumbers) { DirectoryRef = directoryId, ComponentRef = id.Id }; this.Core.AddTuple(tuple); } // check for conditions that exclude this component from using generated guids var isGeneratableGuidOk = "*" == guid; if (isGeneratableGuidOk) { if (encounteredODBCDataSource) { this.Core.Write(ErrorMessages.IllegalComponentWithAutoGeneratedGuid(sourceLineNumbers)); isGeneratableGuidOk = false; } if (0 < files && ComponentKeyPathType.Registry == keyPathType) { this.Core.Write(ErrorMessages.IllegalComponentWithAutoGeneratedGuid(sourceLineNumbers, true)); isGeneratableGuidOk = false; } } // check for implicit KeyPath which can easily be accidentally changed if (this.ShowPedanticMessages && !keyFound && !isGeneratableGuidOk) { this.Core.Write(ErrorMessages.ImplicitComponentKeyPath(sourceLineNumbers, id.Id)); } // if there isn't an @Id attribute value, replace the placeholder with the id of the keypath. // either an explicit KeyPath="yes" attribute must be specified or requirements for // generatable guid must be met. if (componentIdPlaceholderWixVariable == id.Id) { if (isGeneratableGuidOk || keyFound && !String.IsNullOrEmpty(keyPath)) { this.componentIdPlaceholdersResolver.AddVariable(sourceLineNumbers, componentIdPlaceholder, keyPath, false); id = new Identifier(AccessModifier.Private, keyPath); } else { this.Core.Write(ErrorMessages.CannotDefaultComponentId(sourceLineNumbers)); } } // If an id was not determined by now, we have to error. if (null == id) { this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Id")); } // finally add the Component table row if (!this.Core.EncounteredError) { var tuple = new ComponentTuple(sourceLineNumbers, id) { ComponentId = guid, DirectoryRef = directoryId, Location = location, Condition = condition, KeyPath = keyPath, KeyPathType = keyPathType, DisableRegistryReflection = disableRegistryReflection, NeverOverwrite = neverOverwrite, Permanent = permanent, SharedDllRefCount = sharedDllRefCount, Shared = shared, Transitive = transitive, UninstallWhenSuperseded = uninstallWhenSuperseded, Win64 = win64 }; this.Core.AddTuple(tuple); if (multiInstance) { this.Core.AddTuple(new WixInstanceComponentTuple(sourceLineNumbers, id) { ComponentRef = id.Id, }); } if (0 < symbols.Count) { var tupleDelaPatch = new WixDeltaPatchSymbolPathsTuple(sourceLineNumbers, new Identifier(AccessModifier.Private, SymbolPathType.Component, id.Id)) { SymbolType = SymbolPathType.Component, SymbolId = id.Id, SymbolPaths = String.Join(";", symbols) }; this.Core.AddTuple(tupleDelaPatch); } // Complus if (CompilerConstants.IntegerNotSet != comPlusBits) { var complusTuple = new ComplusTuple(sourceLineNumbers) { ComponentRef = id.Id, ExpType = comPlusBits, }; this.Core.AddTuple(complusTuple); } // if this is a module, automatically add this component to the references to ensure it gets in the ModuleComponents table if (this.compilingModule) { this.Core.CreateComplexReference(sourceLineNumbers, ComplexReferenceParentType.Module, this.activeName, this.activeLanguage, ComplexReferenceChildType.Component, id.Id, false); } else if (ComplexReferenceParentType.Unknown != parentType && null != parentId) // if parent was provided, add a complex reference to that. { // If the Component is defined directly under a feature, then mark the complex reference primary. this.Core.CreateComplexReference(sourceLineNumbers, parentType, parentId, parentLanguage, ComplexReferenceChildType.Component, id.Id, ComplexReferenceParentType.Feature == parentType); } } } /// /// Parses a component group element. /// /// Element to parse. [SuppressMessage("Microsoft.Performance", "CA1800:DoNotCastUnnecessarily")] private void ParseComponentGroupElement(XElement node, ComplexReferenceParentType parentType, string parentId) { var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node); Identifier id = null; string directoryId = null; string source = 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": // If the inline syntax is invalid it returns null. Use a static error identifier so the null // directory identifier here doesn't trickle down false errors into child elements. directoryId = this.Core.CreateDirectoryReferenceFromInlineSyntax(sourceLineNumbers, attrib, null) ?? "ErrorParsingInlineSyntax"; break; case "Source": source = 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; } if (!String.IsNullOrEmpty(source) && !source.EndsWith(Path.DirectorySeparatorChar.ToString(), StringComparison.Ordinal)) { source = String.Concat(source, Path.DirectorySeparatorChar); } foreach (var child in node.Elements()) { if (CompilerCore.WixNamespace == child.Name.Namespace) { switch (child.Name.LocalName) { case "ComponentGroupRef": this.ParseComponentGroupRefElement(child, ComplexReferenceParentType.ComponentGroup, id.Id, null); break; case "ComponentRef": this.ParseComponentRefElement(child, ComplexReferenceParentType.ComponentGroup, id.Id, null); break; case "Component": this.ParseComponentElement(child, ComplexReferenceParentType.ComponentGroup, id.Id, null, CompilerConstants.IntegerNotSet, directoryId, source); break; default: this.Core.UnexpectedElement(node, child); break; } } else { this.Core.ParseExtensionElement(node, child); } } if (!this.Core.EncounteredError) { this.Core.AddTuple(new WixComponentGroupTuple(sourceLineNumbers, id)); // Add this componentGroup and its parent in WixGroup. this.Core.CreateWixGroupRow(sourceLineNumbers, parentType, parentId, ComplexReferenceChildType.ComponentGroup, id.Id); } } /// /// Parses a component group reference element. /// /// Element to parse. /// ComplexReferenceParentType of parent element. /// Identifier of parent element (usually a Feature or Module). /// Optional language of parent (only useful for Modules). private void ParseComponentGroupRefElement(XElement node, ComplexReferenceParentType parentType, string parentId, string parentLanguage) { Debug.Assert(ComplexReferenceParentType.ComponentGroup == parentType || ComplexReferenceParentType.FeatureGroup == parentType || ComplexReferenceParentType.Feature == parentType || ComplexReferenceParentType.Module == parentType); var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node); string id = null; var primary = 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": id = this.Core.GetAttributeIdentifierValue(sourceLineNumbers, attrib); this.Core.CreateSimpleReference(sourceLineNumbers, "WixComponentGroup", id); break; case "Primary": primary = this.Core.GetAttributeYesNoValue(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); this.Core.CreateComplexReference(sourceLineNumbers, parentType, parentId, parentLanguage, ComplexReferenceChildType.ComponentGroup, id, (YesNoType.Yes == primary)); } /// /// Parses a component reference element. /// /// Element to parse. /// ComplexReferenceParentType of parent element. /// Identifier of parent element (usually a Feature or Module). /// Optional language of parent (only useful for Modules). private void ParseComponentRefElement(XElement node, ComplexReferenceParentType parentType, string parentId, string parentLanguage) { Debug.Assert(ComplexReferenceParentType.FeatureGroup == parentType || ComplexReferenceParentType.ComponentGroup == parentType || ComplexReferenceParentType.Feature == parentType || ComplexReferenceParentType.Module == parentType); var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node); string id = null; var primary = 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": id = this.Core.GetAttributeIdentifierValue(sourceLineNumbers, attrib); this.Core.CreateSimpleReference(sourceLineNumbers, "Component", id); break; case "Primary": primary = this.Core.GetAttributeYesNoValue(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); this.Core.CreateComplexReference(sourceLineNumbers, parentType, parentId, parentLanguage, ComplexReferenceChildType.Component, id, (YesNoType.Yes == primary)); } /// /// Parses a component search element. /// /// Element to parse. /// Signature for search element. private string ParseComponentSearchElement(XElement node) { var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node); Identifier id = null; string componentId = null; var type = LocatorType.Filename; 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 "Guid": componentId = this.Core.GetAttributeGuidValue(sourceLineNumbers, attrib, false); break; case "Type": var typeValue = this.Core.GetAttributeValue(sourceLineNumbers, attrib); switch (typeValue) { case "directory": type = LocatorType.Directory; break; case "file": type = LocatorType.Filename; break; case "": break; default: this.Core.Write(ErrorMessages.IllegalAttributeValue(sourceLineNumbers, node.Name.LocalName, attrib.Name.LocalName, typeValue, "directory", "file")); break; } break; default: this.Core.UnexpectedAttribute(node, attrib); break; } } else { this.Core.ParseExtensionAttribute(node, attrib); } } if (null == id) { id = this.Core.CreateIdentifier("cmp", componentId, type.ToString()); } var signature = id.Id; var oneChild = false; foreach (var child in node.Elements()) { if (CompilerCore.WixNamespace == child.Name.Namespace) { switch (child.Name.LocalName) { case "DirectorySearch": if (oneChild) { this.Core.Write(ErrorMessages.TooManySearchElements(sourceLineNumbers, node.Name.LocalName)); } oneChild = true; // directorysearch parentage should work like directory element, not the rest of the signature type because of the DrLocator.Parent column signature = this.ParseDirectorySearchElement(child, id.Id); break; case "DirectorySearchRef": if (oneChild) { this.Core.Write(ErrorMessages.TooManySearchElements(sourceLineNumbers, node.Name.LocalName)); } oneChild = true; signature = this.ParseDirectorySearchRefElement(child, id.Id); break; case "FileSearch": if (oneChild) { this.Core.Write(ErrorMessages.TooManySearchElements(sourceLineNumbers, node.Name.LocalName)); } oneChild = true; signature = this.ParseFileSearchElement(child, id.Id, false, CompilerConstants.IntegerNotSet); id = new Identifier(AccessModifier.Private, signature); // FileSearch signatures override parent signatures break; case "FileSearchRef": if (oneChild) { this.Core.Write(ErrorMessages.TooManySearchElements(sourceLineNumbers, node.Name.LocalName)); } oneChild = true; var newId = this.ParseSimpleRefElement(child, "Signature"); // FileSearch signatures override parent signatures id = new Identifier(AccessModifier.Private, newId); signature = null; break; default: this.Core.UnexpectedElement(node, child); break; } } else { this.Core.ParseExtensionElement(node, child); } } if (!this.Core.EncounteredError) { var tuple = new CompLocatorTuple(sourceLineNumbers, id) { SignatureRef = id.Id, ComponentId = componentId, Type = type, }; this.Core.AddTuple(tuple); //var row = this.Core.CreateRow(sourceLineNumbers, TupleDefinitionType.CompLocator, id); //row.Set(1, componentId); //row.Set(2, type); } return signature; } /// /// Parses a create folder element. /// /// Element to parse. /// Identifier for parent component. /// Default identifier for directory to create. /// true if the component is 64-bit. /// Identifier for the directory that will be created private string ParseCreateFolderElement(XElement node, string componentId, string directoryId, bool win64Component) { var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node); foreach (var attrib in node.Attributes()) { if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace) { switch (attrib.Name.LocalName) { case "Directory": directoryId = this.Core.CreateDirectoryReferenceFromInlineSyntax(sourceLineNumbers, attrib, directoryId); break; default: this.Core.UnexpectedAttribute(node, attrib); break; } } else { this.Core.ParseExtensionAttribute(node, attrib); } } foreach (var child in node.Elements()) { if (CompilerCore.WixNamespace == child.Name.Namespace) { switch (child.Name.LocalName) { case "Shortcut": this.ParseShortcutElement(child, componentId, node.Name.LocalName, directoryId, YesNoType.No); break; case "Permission": this.ParsePermissionElement(child, directoryId, "CreateFolder"); break; case "PermissionEx": this.ParsePermissionExElement(child, directoryId, "CreateFolder"); break; default: this.Core.UnexpectedElement(node, child); break; } } else { var context = new Dictionary() { { "DirectoryId", directoryId }, { "ComponentId", componentId }, { "Win64", win64Component.ToString() } }; this.Core.ParseExtensionElement(node, child, context); } } if (!this.Core.EncounteredError) { var tuple = new CreateFolderTuple(sourceLineNumbers) { DirectoryRef = directoryId, ComponentRef = componentId }; this.Core.AddTuple(tuple); } return directoryId; } /// /// Parses a copy file element. /// /// Element to parse. /// Identifier of parent component. /// Identifier of file to copy (null if moving the file). private void ParseCopyFileElement(XElement node, string componentId, string fileId) { var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node); Identifier id = null; var delete = false; string destinationDirectory = null; string destinationName = null; string destinationShortName = null; string destinationProperty = null; string sourceDirectory = null; string sourceFolder = null; string sourceName = null; string sourceProperty = 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 "Delete": delete = YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib); break; case "DestinationDirectory": destinationDirectory = this.Core.CreateDirectoryReferenceFromInlineSyntax(sourceLineNumbers, attrib, null); break; case "DestinationName": destinationName = this.Core.GetAttributeLongFilename(sourceLineNumbers, attrib, false); break; case "DestinationProperty": destinationProperty = this.Core.GetAttributeIdentifierValue(sourceLineNumbers, attrib); break; case "DestinationShortName": destinationShortName = this.Core.GetAttributeShortFilename(sourceLineNumbers, attrib, false); break; case "FileId": if (null != fileId) { this.Core.Write(ErrorMessages.IllegalAttributeWhenNested(sourceLineNumbers, node.Name.LocalName, attrib.Name.LocalName, node.Parent.Name.LocalName)); } fileId = this.Core.GetAttributeIdentifierValue(sourceLineNumbers, attrib); this.Core.CreateSimpleReference(sourceLineNumbers, "File", fileId); break; case "SourceDirectory": sourceDirectory = this.Core.CreateDirectoryReferenceFromInlineSyntax(sourceLineNumbers, attrib, null); break; case "SourceName": sourceName = this.Core.GetAttributeValue(sourceLineNumbers, attrib); break; case "SourceProperty": sourceProperty = this.Core.GetAttributeIdentifierValue(sourceLineNumbers, attrib); break; default: this.Core.UnexpectedAttribute(node, attrib); break; } } else { this.Core.ParseExtensionAttribute(node, attrib); } } if (null != sourceFolder && null != sourceDirectory) // SourceFolder and SourceDirectory cannot coexist { this.Core.Write(ErrorMessages.IllegalAttributeWithOtherAttribute(sourceLineNumbers, node.Name.LocalName, "SourceFolder", "SourceDirectory")); } if (null != sourceFolder && null != sourceProperty) // SourceFolder and SourceProperty cannot coexist { this.Core.Write(ErrorMessages.IllegalAttributeWithOtherAttribute(sourceLineNumbers, node.Name.LocalName, "SourceFolder", "SourceProperty")); } if (null != sourceDirectory && null != sourceProperty) // SourceDirectory and SourceProperty cannot coexist { this.Core.Write(ErrorMessages.IllegalAttributeWithOtherAttribute(sourceLineNumbers, node.Name.LocalName, "SourceProperty", "SourceDirectory")); } if (null != destinationDirectory && null != destinationProperty) // DestinationDirectory and DestinationProperty cannot coexist { this.Core.Write(ErrorMessages.IllegalAttributeWithOtherAttribute(sourceLineNumbers, node.Name.LocalName, "DestinationProperty", "DestinationDirectory")); } // generate a short file name if (null == destinationShortName && (null != destinationName && !this.Core.IsValidShortFilename(destinationName, false))) { destinationShortName = this.Core.CreateShortName(destinationName, true, false, node.Name.LocalName, componentId); } if (null == id) { id = this.Core.CreateIdentifier("cf", sourceFolder, sourceDirectory, sourceProperty, destinationDirectory, destinationProperty, destinationName); } this.Core.ParseForExtensionElements(node); if (null == fileId) { // DestinationDirectory or DestinationProperty must be specified if (null == destinationDirectory && null == destinationProperty) { this.Core.Write(ErrorMessages.ExpectedAttributesWithoutOtherAttribute(sourceLineNumbers, node.Name.LocalName, "DestinationDirectory", "DestinationProperty", "FileId")); } if (!this.Core.EncounteredError) { var tuple = new MoveFileTuple(sourceLineNumbers, id) { ComponentRef = componentId, SourceName = sourceName, DestName= String.IsNullOrEmpty(destinationShortName) && String.IsNullOrEmpty(destinationName) ? null : this.GetMsiFilenameValue(destinationShortName, destinationName), SourceFolder = sourceDirectory ?? sourceProperty, DestFolder = destinationDirectory ?? destinationProperty, Delete = delete }; this.Core.AddTuple(tuple); } } else // copy the file { if (null != sourceDirectory) { this.Core.Write(ErrorMessages.IllegalAttributeWithOtherAttribute(sourceLineNumbers, node.Name.LocalName, "SourceDirectory", "FileId")); } if (null != sourceFolder) { this.Core.Write(ErrorMessages.IllegalAttributeWithOtherAttribute(sourceLineNumbers, node.Name.LocalName, "SourceFolder", "FileId")); } if (null != sourceName) { this.Core.Write(ErrorMessages.IllegalAttributeWithOtherAttribute(sourceLineNumbers, node.Name.LocalName, "SourceName", "FileId")); } if (null != sourceProperty) { this.Core.Write(ErrorMessages.IllegalAttributeWithOtherAttribute(sourceLineNumbers, node.Name.LocalName, "SourceProperty", "FileId")); } if (delete) { this.Core.Write(ErrorMessages.IllegalAttributeWithOtherAttribute(sourceLineNumbers, node.Name.LocalName, "Delete", "FileId")); } if (null == destinationName && null == destinationDirectory && null == destinationProperty) { this.Core.Write(WarningMessages.CopyFileFileIdUseless(sourceLineNumbers)); } if (!this.Core.EncounteredError) { var tuple = new DuplicateFileTuple(sourceLineNumbers, id) { ComponentRef = componentId, FileRef = fileId, DestinationName = String.IsNullOrEmpty(destinationShortName) && String.IsNullOrEmpty(destinationName) ? null : this.GetMsiFilenameValue(destinationShortName, destinationName), DestinationFolder = destinationDirectory ?? destinationProperty }; this.Core.AddTuple(tuple); } } } /// /// Parses a CustomAction element. /// /// Element to parse. private void ParseCustomActionElement(XElement node) { var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node); Identifier id = null; var inlineScript = false; var suppressModularization = YesNoType.NotSet; string source = null; string target = null; var explicitWin64 = false; CustomActionSourceType? sourceType = null; CustomActionTargetType? targetType = null; var executionType = CustomActionExecutionType.Immediate; var hidden = false; var impersonate = true; var patchUninstall = false; var tsAware = false; var win64 = false; var async = false; var ignoreResult = 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 "BinaryKey": if (null != source) { this.Core.Write(ErrorMessages.CustomActionMultipleSources(sourceLineNumbers, node.Name.LocalName, attrib.Name.LocalName, "BinaryKey", "Directory", "FileKey", "Property", "Script")); } source = this.Core.GetAttributeIdentifierValue(sourceLineNumbers, attrib); //sourceBits = MsiInterop.MsidbCustomActionTypeBinaryData; sourceType = CustomActionSourceType.Binary; this.Core.CreateSimpleReference(sourceLineNumbers, "Binary", source); // add a reference to the appropriate Binary break; case "Directory": if (null != source) { this.Core.Write(ErrorMessages.CustomActionMultipleSources(sourceLineNumbers, node.Name.LocalName, attrib.Name.LocalName, "BinaryKey", "Directory", "FileKey", "Property", "Script")); } source = this.Core.CreateDirectoryReferenceFromInlineSyntax(sourceLineNumbers, attrib, null); //sourceBits = MsiInterop.MsidbCustomActionTypeDirectory; sourceType = CustomActionSourceType.Directory; break; case "DllEntry": if (null != target) { this.Core.Write(ErrorMessages.CustomActionMultipleTargets(sourceLineNumbers, node.Name.LocalName, attrib.Name.LocalName, "DllEntry", "Error", "ExeCommand", "JScriptCall", "Script", "Value", "VBScriptCall")); } target = this.Core.GetAttributeValue(sourceLineNumbers, attrib); //targetBits = MsiInterop.MsidbCustomActionTypeDll; targetType = CustomActionTargetType.Dll; break; case "Error": if (null != target) { this.Core.Write(ErrorMessages.CustomActionMultipleTargets(sourceLineNumbers, node.Name.LocalName, attrib.Name.LocalName, "DllEntry", "Error", "ExeCommand", "JScriptCall", "Script", "Value", "VBScriptCall")); } target = this.Core.GetAttributeValue(sourceLineNumbers, attrib); //targetBits = MsiInterop.MsidbCustomActionTypeTextData | MsiInterop.MsidbCustomActionTypeSourceFile; sourceType = CustomActionSourceType.File; targetType = CustomActionTargetType.TextData; // The target can be either a formatted error string or a literal // error number. Try to convert to error number to determine whether // to add a reference. No need to look at the value. if (Int32.TryParse(target, out var ignored)) { this.Core.CreateSimpleReference(sourceLineNumbers, "Error", target); } break; case "ExeCommand": if (null != target) { this.Core.Write(ErrorMessages.CustomActionMultipleTargets(sourceLineNumbers, node.Name.LocalName, attrib.Name.LocalName, "DllEntry", "Error", "ExeCommand", "JScriptCall", "Script", "Value", "VBScriptCall")); } target = this.Core.GetAttributeValue(sourceLineNumbers, attrib, EmptyRule.CanBeEmpty); // one of the few cases where an empty string value is valid //targetBits = MsiInterop.MsidbCustomActionTypeExe; targetType = CustomActionTargetType.Exe; break; case "Execute": var execute = this.Core.GetAttributeValue(sourceLineNumbers, attrib); switch (execute) { case "commit": //bits |= MsiInterop.MsidbCustomActionTypeInScript | MsiInterop.MsidbCustomActionTypeCommit; executionType = CustomActionExecutionType.Commit; break; case "deferred": //bits |= MsiInterop.MsidbCustomActionTypeInScript; executionType = CustomActionExecutionType.Deferred; break; case "firstSequence": //bits |= MsiInterop.MsidbCustomActionTypeFirstSequence; executionType = CustomActionExecutionType.FirstSequence; break; case "immediate": executionType = CustomActionExecutionType.Immediate; break; case "oncePerProcess": //bits |= MsiInterop.MsidbCustomActionTypeOncePerProcess; executionType = CustomActionExecutionType.OncePerProcess; break; case "rollback": //bits |= MsiInterop.MsidbCustomActionTypeInScript | MsiInterop.MsidbCustomActionTypeRollback; executionType = CustomActionExecutionType.Rollback; break; case "secondSequence": //bits |= MsiInterop.MsidbCustomActionTypeClientRepeat; executionType = CustomActionExecutionType.ClientRepeat; break; default: this.Core.Write(ErrorMessages.IllegalAttributeValue(sourceLineNumbers, node.Name.LocalName, attrib.Name.LocalName, execute, "commit", "deferred", "firstSequence", "immediate", "oncePerProcess", "rollback", "secondSequence")); break; } break; case "FileKey": if (null != source) { this.Core.Write(ErrorMessages.CustomActionMultipleSources(sourceLineNumbers, node.Name.LocalName, attrib.Name.LocalName, "BinaryKey", "Directory", "FileKey", "Property", "Script")); } source = this.Core.GetAttributeIdentifierValue(sourceLineNumbers, attrib); //sourceBits = MsiInterop.MsidbCustomActionTypeSourceFile; sourceType = CustomActionSourceType.File; this.Core.CreateSimpleReference(sourceLineNumbers, "File", source); // add a reference to the appropriate File break; case "HideTarget": hidden = YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib); //if (YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib)) //{ // bits |= MsiInterop.MsidbCustomActionTypeHideTarget; //} break; case "Impersonate": impersonate = YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib); //if (YesNoType.No == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib)) //{ // bits |= MsiInterop.MsidbCustomActionTypeNoImpersonate; //} break; case "JScriptCall": if (null != target) { this.Core.Write(ErrorMessages.CustomActionMultipleTargets(sourceLineNumbers, node.Name.LocalName, attrib.Name.LocalName, "DllEntry", "Error", "ExeCommand", "JScriptCall", "Script", "Value", "VBScriptCall")); } target = this.Core.GetAttributeValue(sourceLineNumbers, attrib, EmptyRule.CanBeEmpty); // one of the few cases where an empty string value is valid //targetBits = MsiInterop.MsidbCustomActionTypeJScript; targetType = CustomActionTargetType.JScript; break; case "PatchUninstall": patchUninstall = YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib); //if (YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib)) //{ // extendedBits |= MsiInterop.MsidbCustomActionTypePatchUninstall; //} break; case "Property": if (null != source) { this.Core.Write(ErrorMessages.CustomActionMultipleSources(sourceLineNumbers, node.Name.LocalName, attrib.Name.LocalName, "BinaryKey", "Directory", "FileKey", "Property", "Script")); } source = this.Core.GetAttributeValue(sourceLineNumbers, attrib); //sourceBits = MsiInterop.MsidbCustomActionTypeProperty; sourceType = CustomActionSourceType.Property; break; case "Return": var returnValue = this.Core.GetAttributeValue(sourceLineNumbers, attrib); switch (returnValue) { case "asyncNoWait": //bits |= MsiInterop.MsidbCustomActionTypeAsync | MsiInterop.MsidbCustomActionTypeContinue; async = true; ignoreResult = true; break; case "asyncWait": //bits |= MsiInterop.MsidbCustomActionTypeAsync; async = true; break; case "check": break; case "ignore": //bits |= MsiInterop.MsidbCustomActionTypeContinue; ignoreResult = true; break; case "": break; default: this.Core.Write(ErrorMessages.IllegalAttributeValue(sourceLineNumbers, node.Name.LocalName, attrib.Name.LocalName, returnValue, "asyncNoWait", "asyncWait", "check", "ignore")); break; } break; case "Script": if (null != source) { this.Core.Write(ErrorMessages.CustomActionMultipleSources(sourceLineNumbers, node.Name.LocalName, attrib.Name.LocalName, "BinaryKey", "Directory", "FileKey", "Property", "Script")); } if (null != target) { this.Core.Write(ErrorMessages.CustomActionMultipleTargets(sourceLineNumbers, node.Name.LocalName, attrib.Name.LocalName, "DllEntry", "Error", "ExeCommand", "JScriptCall", "Script", "Value", "VBScriptCall")); } // set the source and target to empty string for error messages when the user sets multiple sources or targets source = String.Empty; target = String.Empty; inlineScript = true; var script = this.Core.GetAttributeValue(sourceLineNumbers, attrib); switch (script) { case "jscript": //sourceBits = MsiInterop.MsidbCustomActionTypeDirectory; sourceType = CustomActionSourceType.Directory; //targetBits = MsiInterop.MsidbCustomActionTypeJScript; targetType = CustomActionTargetType.JScript; break; case "vbscript": //sourceBits = MsiInterop.MsidbCustomActionTypeDirectory; sourceType = CustomActionSourceType.Directory; //targetBits = MsiInterop.MsidbCustomActionTypeVBScript; targetType = CustomActionTargetType.VBScript; break; case "": break; default: this.Core.Write(ErrorMessages.IllegalAttributeValue(sourceLineNumbers, node.Name.LocalName, attrib.Name.LocalName, script, "jscript", "vbscript")); break; } break; case "SuppressModularization": suppressModularization = this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib); break; case "TerminalServerAware": tsAware = YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib); //if (YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib)) //{ // bits |= MsiInterop.MsidbCustomActionTypeTSAware; //} break; case "Value": if (null != target) { this.Core.Write(ErrorMessages.CustomActionMultipleTargets(sourceLineNumbers, node.Name.LocalName, attrib.Name.LocalName, "DllEntry", "Error", "ExeCommand", "JScriptCall", "Script", "Value", "VBScriptCall")); } target = this.Core.GetAttributeValue(sourceLineNumbers, attrib, EmptyRule.CanBeEmpty); // one of the few cases where an empty string value is valid //targetBits = MsiInterop.MsidbCustomActionTypeTextData; targetType = CustomActionTargetType.TextData; break; case "VBScriptCall": if (null != target) { this.Core.Write(ErrorMessages.CustomActionMultipleTargets(sourceLineNumbers, node.Name.LocalName, attrib.Name.LocalName, "DllEntry", "Error", "ExeCommand", "JScriptCall", "Script", "Value", "VBScriptCall")); } target = this.Core.GetAttributeValue(sourceLineNumbers, attrib, EmptyRule.CanBeEmpty); // one of the few cases where an empty string value is valid //targetBits = MsiInterop.MsidbCustomActionTypeVBScript; targetType = CustomActionTargetType.VBScript; break; case "Win64": explicitWin64 = true; win64 = YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib); //if (YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib)) //{ // bits |= MsiInterop.MsidbCustomActionType64BitScript; //} 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; } if (!explicitWin64 && (CustomActionTargetType.VBScript == targetType || CustomActionTargetType.JScript == targetType) && (Platform.IA64 == this.CurrentPlatform || Platform.X64 == this.CurrentPlatform)) { win64 = true; } // get the inner text if any exists var innerText = this.Core.GetTrimmedInnerText(node); // if we have an in-lined Script CustomAction ensure no source or target attributes were provided if (inlineScript) { target = innerText; } else if (CustomActionTargetType.VBScript == targetType) // non-inline vbscript { if (null == source) { this.Core.Write(ErrorMessages.IllegalAttributeWithoutOtherAttributes(sourceLineNumbers, node.Name.LocalName, "VBScriptCall", "BinaryKey", "FileKey", "Property")); } else if (CustomActionSourceType.Directory == sourceType) { this.Core.Write(ErrorMessages.IllegalAttributeWithOtherAttribute(sourceLineNumbers, node.Name.LocalName, "VBScriptCall", "Directory")); } } else if (CustomActionTargetType.JScript == targetType) // non-inline jscript { if (null == source) { this.Core.Write(ErrorMessages.IllegalAttributeWithoutOtherAttributes(sourceLineNumbers, node.Name.LocalName, "JScriptCall", "BinaryKey", "FileKey", "Property")); } else if (CustomActionSourceType.Directory == sourceType) { this.Core.Write(ErrorMessages.IllegalAttributeWithOtherAttribute(sourceLineNumbers, node.Name.LocalName, "JScriptCall", "Directory")); } } else if (CustomActionTargetType.Exe == targetType) // exe-command { if (null == source) { this.Core.Write(ErrorMessages.IllegalAttributeWithoutOtherAttributes(sourceLineNumbers, node.Name.LocalName, "ExeCommand", "BinaryKey", "Directory", "FileKey", "Property")); } } else if (CustomActionTargetType.TextData == targetType && CustomActionSourceType.Directory != sourceType && CustomActionSourceType.Property != sourceType && CustomActionSourceType.File != sourceType) { this.Core.Write(ErrorMessages.IllegalAttributeWithoutOtherAttributes(sourceLineNumbers, node.Name.LocalName, "Value", "Directory", "Property", "Error")); } else if (!String.IsNullOrEmpty(innerText)) // inner text cannot be specified with non-script CAs { this.Core.Write(ErrorMessages.CustomActionIllegalInnerText(sourceLineNumbers, node.Name.LocalName, innerText, "Script")); } if (win64 && CustomActionTargetType.VBScript != targetType && CustomActionTargetType.JScript != targetType) { this.Core.Write(ErrorMessages.IllegalAttributeWithoutOtherAttributes(sourceLineNumbers, node.Name.LocalName, "Win64", "Script", "VBScriptCall", "JScriptCall")); } if (async && ignoreResult && CustomActionTargetType.Exe != targetType) { this.Core.Write(ErrorMessages.IllegalAttributeValueWithoutOtherAttribute(sourceLineNumbers, node.Name.LocalName, "Return", "asyncNoWait", "ExeCommand")); } // TS-aware CAs are valid only when deferred. if (tsAware & CustomActionExecutionType.Deferred != executionType && CustomActionExecutionType.Rollback != executionType && CustomActionExecutionType.Commit != executionType) { this.Core.Write(ErrorMessages.IllegalTerminalServerCustomActionAttributes(sourceLineNumbers)); } // MSI doesn't support in-script property setting, so disallow it if (CustomActionSourceType.Property == sourceType && CustomActionTargetType.TextData == targetType && (CustomActionExecutionType.Deferred == executionType || CustomActionExecutionType.Rollback == executionType || CustomActionExecutionType.Commit == executionType)) { this.Core.Write(ErrorMessages.IllegalPropertyCustomActionAttributes(sourceLineNumbers)); } if (!targetType.HasValue /*0 == targetBits*/) { this.Core.Write(ErrorMessages.ExpectedAttributes(sourceLineNumbers, node.Name.LocalName, "DllEntry", "Error", "ExeCommand", "JScriptCall", "Script", "Value", "VBScriptCall")); } this.Core.ParseForExtensionElements(node); if (!this.Core.EncounteredError) { var tuple = new CustomActionTuple(sourceLineNumbers, id) { ExecutionType = executionType, Source = source, SourceType = sourceType.Value, Target = target, TargetType = targetType.Value, Async = async, IgnoreResult = ignoreResult, Impersonate = impersonate, PatchUninstall = patchUninstall, TSAware = tsAware, Win64 = win64, }; this.Core.AddTuple(tuple); if (YesNoType.Yes == suppressModularization) { this.Core.AddTuple(new WixSuppressModularizationTuple(sourceLineNumbers, id)); } // For deferred CAs that specify HideTarget we should also hide the CA data property for the action. if (hidden && (CustomActionExecutionType.Deferred == executionType || CustomActionExecutionType.Commit == executionType || CustomActionExecutionType.Rollback == executionType)) { this.AddWixPropertyRow(sourceLineNumbers, id, false, false, hidden); } } } /// /// Parses a simple reference element. /// /// Element to parse. /// Table which contains the target of the simple reference. /// Id of the referenced element. private string ParseSimpleRefElement(XElement node, string table) { 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); this.Core.CreateSimpleReference(sourceLineNumbers, table, id); 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); return id; } /// /// Parses a PatchFamilyRef element. /// /// Element to parse. /// The parent type. /// The ID of the parent. /// Id of the referenced element. private void ParsePatchFamilyRefElement(XElement node, ComplexReferenceParentType parentType, string parentId) { var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node); var primaryKeys = new string[2]; foreach (var attrib in node.Attributes()) { if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace) { switch (attrib.Name.LocalName) { case "Id": primaryKeys[0] = this.Core.GetAttributeIdentifierValue(sourceLineNumbers, attrib); break; case "ProductCode": primaryKeys[1] = this.Core.GetAttributeIdentifierValue(sourceLineNumbers, attrib); break; default: this.Core.UnexpectedAttribute(node, attrib); break; } } else { this.Core.ParseExtensionAttribute(node, attrib); } } if (null == primaryKeys[0]) { this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Id")); } this.Core.CreateSimpleReference(sourceLineNumbers, "MsiPatchSequence", primaryKeys); this.Core.ParseForExtensionElements(node); if (!this.Core.EncounteredError) { this.Core.CreateComplexReference(sourceLineNumbers, parentType, parentId, null, ComplexReferenceChildType.PatchFamily, primaryKeys[0], true); } } /// /// Parses an ensure table element. /// /// Element to parse. private void ParseEnsureTableElement(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.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")); } else if (31 < id.Length) { this.Core.Write(ErrorMessages.TableNameTooLong(sourceLineNumbers, node.Name.LocalName, "Id", id)); } this.Core.ParseForExtensionElements(node); this.Core.EnsureTable(sourceLineNumbers, id); } /// /// Parses a custom table element. /// /// Element to parse. /// not cleaned private void ParseCustomTableElement(XElement node) { var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node); string tableId = null; string categories = null; var columnCount = 0; string columnNames = null; string columnTypes = null; string descriptions = null; string keyColumns = null; string keyTables = null; string maxValues = null; string minValues = null; string modularizations = null; string primaryKeys = null; string sets = null; var bootstrapperApplicationData = false; foreach (var attrib in node.Attributes()) { if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace) { switch (attrib.Name.LocalName) { case "Id": tableId = this.Core.GetAttributeIdentifierValue(sourceLineNumbers, attrib); break; case "Unreal": bootstrapperApplicationData = YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib); break; default: this.Core.UnexpectedAttribute(node, attrib); break; } } else { this.Core.ParseExtensionAttribute(node, attrib); } } if (null == tableId) { this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Id")); } else if (31 < tableId.Length) { this.Core.Write(ErrorMessages.CustomTableNameTooLong(sourceLineNumbers, node.Name.LocalName, "Id", tableId)); } foreach (var child in node.Elements()) { if (CompilerCore.WixNamespace == child.Name.Namespace) { var childSourceLineNumbers = Preprocessor.GetSourceLineNumbers(child); switch (child.Name.LocalName) { case "Column": ++columnCount; var category = String.Empty; string columnName = null; string columnType = null; var description = String.Empty; var keyColumn = CompilerConstants.IntegerNotSet; var keyTable = String.Empty; var localizable = false; var maxValue = CompilerConstants.LongNotSet; var minValue = CompilerConstants.LongNotSet; var modularization = "None"; var nullable = false; var primaryKey = false; var setValues = String.Empty; string typeName = null; var width = 0; foreach (var childAttrib in child.Attributes()) { switch (childAttrib.Name.LocalName) { case "Id": columnName = this.Core.GetAttributeIdentifierValue(childSourceLineNumbers, childAttrib); break; case "Category": category = this.Core.GetAttributeValue(childSourceLineNumbers, childAttrib); break; case "Description": description = this.Core.GetAttributeValue(childSourceLineNumbers, childAttrib); break; case "KeyColumn": keyColumn = this.Core.GetAttributeIntegerValue(childSourceLineNumbers, childAttrib, 1, 32); break; case "KeyTable": keyTable = this.Core.GetAttributeValue(childSourceLineNumbers, childAttrib); break; case "Localizable": localizable = YesNoType.Yes == this.Core.GetAttributeYesNoValue(childSourceLineNumbers, childAttrib); break; case "MaxValue": maxValue = this.Core.GetAttributeLongValue(childSourceLineNumbers, childAttrib, Int32.MinValue + 1, Int32.MaxValue); break; case "MinValue": minValue = this.Core.GetAttributeLongValue(childSourceLineNumbers, childAttrib, Int32.MinValue + 1, Int32.MaxValue); break; case "Modularize": modularization = this.Core.GetAttributeValue(childSourceLineNumbers, childAttrib); break; case "Nullable": nullable = YesNoType.Yes == this.Core.GetAttributeYesNoValue(childSourceLineNumbers, childAttrib); break; case "PrimaryKey": primaryKey = YesNoType.Yes == this.Core.GetAttributeYesNoValue(childSourceLineNumbers, childAttrib); break; case "Set": setValues = this.Core.GetAttributeValue(childSourceLineNumbers, childAttrib); break; case "Type": var typeValue = this.Core.GetAttributeValue(childSourceLineNumbers, childAttrib); switch (typeValue) { case "binary": typeName = "OBJECT"; break; case "int": typeName = "SHORT"; break; case "string": typeName = "CHAR"; break; case "": break; default: this.Core.Write(ErrorMessages.IllegalAttributeValue(childSourceLineNumbers, child.Name.LocalName, "Type", typeValue, "binary", "int", "string")); break; } break; case "Width": width = this.Core.GetAttributeIntegerValue(childSourceLineNumbers, childAttrib, 0, Int32.MaxValue); break; default: this.Core.UnexpectedAttribute(child, childAttrib); break; } } if (null == columnName) { this.Core.Write(ErrorMessages.ExpectedAttribute(childSourceLineNumbers, child.Name.LocalName, "Id")); } if (null == typeName) { this.Core.Write(ErrorMessages.ExpectedAttribute(childSourceLineNumbers, child.Name.LocalName, "Type")); } else if ("SHORT" == typeName) { if (2 != width && 4 != width) { this.Core.Write(ErrorMessages.CustomTableIllegalColumnWidth(childSourceLineNumbers, child.Name.LocalName, "Width", width)); } columnType = String.Concat(nullable ? "I" : "i", width); } else if ("CHAR" == typeName) { var typeChar = localizable ? "l" : "s"; columnType = String.Concat(nullable ? typeChar.ToUpper(CultureInfo.InvariantCulture) : typeChar.ToLower(CultureInfo.InvariantCulture), width); } else if ("OBJECT" == typeName) { if ("Binary" != category) { this.Core.Write(ErrorMessages.ExpectedBinaryCategory(childSourceLineNumbers)); } columnType = String.Concat(nullable ? "V" : "v", width); } this.Core.ParseForExtensionElements(child); columnNames = String.Concat(columnNames, null == columnNames ? String.Empty : "\t", columnName); columnTypes = String.Concat(columnTypes, null == columnTypes ? String.Empty : "\t", columnType); if (primaryKey) { primaryKeys = String.Concat(primaryKeys, null == primaryKeys ? String.Empty : "\t", columnName); } minValues = String.Concat(minValues, null == minValues ? String.Empty : "\t", CompilerConstants.LongNotSet != minValue ? minValue.ToString(CultureInfo.InvariantCulture) : String.Empty); maxValues = String.Concat(maxValues, null == maxValues ? String.Empty : "\t", CompilerConstants.LongNotSet != maxValue ? maxValue.ToString(CultureInfo.InvariantCulture) : String.Empty); keyTables = String.Concat(keyTables, null == keyTables ? String.Empty : "\t", keyTable); keyColumns = String.Concat(keyColumns, null == keyColumns ? String.Empty : "\t", CompilerConstants.IntegerNotSet != keyColumn ? keyColumn.ToString(CultureInfo.InvariantCulture) : String.Empty); categories = String.Concat(categories, null == categories ? String.Empty : "\t", category); sets = String.Concat(sets, null == sets ? String.Empty : "\t", setValues); descriptions = String.Concat(descriptions, null == descriptions ? String.Empty : "\t", description); modularizations = String.Concat(modularizations, null == modularizations ? String.Empty : "\t", modularization); break; case "Row": string dataValue = null; foreach (var childAttrib in child.Attributes()) { this.Core.ParseExtensionAttribute(child, childAttrib); } foreach (var data in child.Elements()) { var dataSourceLineNumbers = Preprocessor.GetSourceLineNumbers(data); switch (data.Name.LocalName) { case "Data": columnName = null; foreach (var dataAttrib in data.Attributes()) { switch (dataAttrib.Name.LocalName) { case "Column": columnName = this.Core.GetAttributeValue(dataSourceLineNumbers, dataAttrib); break; default: this.Core.UnexpectedAttribute(data, dataAttrib); break; } } if (null == columnName) { this.Core.Write(ErrorMessages.ExpectedAttribute(dataSourceLineNumbers, data.Name.LocalName, "Column")); } dataValue = String.Concat(dataValue, null == dataValue ? String.Empty : WixCustomRowTuple.FieldSeparator.ToString(), columnName, ":", Common.GetInnerText(data)); break; } } this.Core.CreateSimpleReference(sourceLineNumbers, "WixCustomTable", tableId); if (!this.Core.EncounteredError) { this.Core.AddTuple(new WixCustomRowTuple(childSourceLineNumbers) { Table = tableId, FieldData = dataValue }); } break; default: this.Core.UnexpectedElement(node, child); break; } } else { this.Core.ParseExtensionElement(node, child); } } if (0 < columnCount) { if (null == primaryKeys || 0 == primaryKeys.Length) { this.Core.Write(ErrorMessages.CustomTableMissingPrimaryKey(sourceLineNumbers)); } if (!this.Core.EncounteredError) { var tuple = new WixCustomTableTuple(sourceLineNumbers, new Identifier(AccessModifier.Public, tableId)) { ColumnCount = columnCount, ColumnNames = columnNames, ColumnTypes = columnTypes, PrimaryKeys = primaryKeys, MinValues = minValues, MaxValues = maxValues, KeyTables = keyTables, KeyColumns = keyColumns, Categories = categories, Sets = sets, Descriptions = descriptions, Modularizations = modularizations, Unreal = bootstrapperApplicationData }; this.Core.AddTuple(tuple); } } } /// /// Parses a directory element. /// /// Element to parse. /// Optional identifier of parent directory. /// Disk id inherited from parent directory. /// Path to source file as of yet. [SuppressMessage("Microsoft.Performance", "CA1820:TestForEmptyStringsUsingStringLength")] private void ParseDirectoryElement(XElement node, string parentId, int diskId, string fileSource) { var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node); Identifier id = null; string componentGuidGenerationSeed = null; var fileSourceAttribSet = false; var nameHasValue = false; var name = "."; // default to parent directory. string[] inlineSyntax = null; string shortName = null; string sourceName = null; string shortSourceName = null; string symbols = 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 "ComponentGuidGenerationSeed": componentGuidGenerationSeed = this.Core.GetAttributeGuidValue(sourceLineNumbers, attrib, false); break; case "DiskId": diskId = this.Core.GetAttributeIntegerValue(sourceLineNumbers, attrib, 1, Int16.MaxValue); break; case "FileSource": fileSource = this.Core.GetAttributeValue(sourceLineNumbers, attrib); fileSourceAttribSet = true; break; case "Name": nameHasValue = true; if (attrib.Value.Equals(".")) { name = attrib.Value; } else { inlineSyntax = this.Core.GetAttributeInlineDirectorySyntax(sourceLineNumbers, attrib); } break; case "ShortName": shortName = this.Core.GetAttributeShortFilename(sourceLineNumbers, attrib, false); break; case "ShortSourceName": shortSourceName = this.Core.GetAttributeShortFilename(sourceLineNumbers, attrib, false); break; case "SourceName": if ("." == attrib.Value) { sourceName = attrib.Value; } else { sourceName = this.Core.GetAttributeLongFilename(sourceLineNumbers, attrib, false); } break; default: this.Core.UnexpectedAttribute(node, attrib); break; } } else { this.Core.ParseExtensionAttribute(node, attrib); } } // Create the directory rows for the inline. if (null != inlineSyntax) { // Special case the single entry in the inline syntax since it is the most common case // and needs no extra processing. It's just the name of the directory. if (1 == inlineSyntax.Length) { name = inlineSyntax[0]; } else { var pathStartsAt = 0; if (inlineSyntax[0].EndsWith(":")) { parentId = inlineSyntax[0].TrimEnd(':'); this.Core.CreateSimpleReference(sourceLineNumbers, "Directory", parentId); pathStartsAt = 1; } for (var i = pathStartsAt; i < inlineSyntax.Length - 1; ++i) { var inlineId = this.Core.CreateDirectoryRow(sourceLineNumbers, null, parentId, inlineSyntax[i]); parentId = inlineId.Id; } name = inlineSyntax[inlineSyntax.Length - 1]; } } if (!nameHasValue) { if (!String.IsNullOrEmpty(shortName)) { this.Core.Write(ErrorMessages.IllegalAttributeWithoutOtherAttributes(sourceLineNumbers, node.Name.LocalName, "ShortName", "Name")); } if (null == parentId) { this.Core.Write(ErrorMessages.DirectoryRootWithoutName(sourceLineNumbers, node.Name.LocalName, "Name")); } } else if (!String.IsNullOrEmpty(name)) { if (String.IsNullOrEmpty(shortName)) { if (!name.Equals(".") && !name.Equals("SourceDir") && !this.Core.IsValidShortFilename(name, false)) { shortName = this.Core.CreateShortName(name, false, false, "Directory", parentId); } } else if (name.Equals(".")) { this.Core.Write(ErrorMessages.IllegalAttributeWithOtherAttribute(sourceLineNumbers, node.Name.LocalName, "ShortName", "Name", name)); } else if (name.Equals(shortName)) { this.Core.Write(WarningMessages.DirectoryRedundantNames(sourceLineNumbers, node.Name.LocalName, "Name", "ShortName", name)); } } if (String.IsNullOrEmpty(sourceName)) { if (!String.IsNullOrEmpty(shortSourceName)) { this.Core.Write(ErrorMessages.IllegalAttributeWithoutOtherAttributes(sourceLineNumbers, node.Name.LocalName, "ShortSourceName", "SourceName")); } } else { if (String.IsNullOrEmpty(shortSourceName)) { if (!sourceName.Equals(".") && !this.Core.IsValidShortFilename(sourceName, false)) { shortSourceName = this.Core.CreateShortName(sourceName, false, false, "Directory", parentId); } } else if (sourceName.Equals(".")) { this.Core.Write(ErrorMessages.IllegalAttributeWithOtherAttribute(sourceLineNumbers, node.Name.LocalName, "ShortSourceName", "SourceName", sourceName)); } else if (sourceName.Equals(shortSourceName)) { this.Core.Write(WarningMessages.DirectoryRedundantNames(sourceLineNumbers, node.Name.LocalName, "SourceName", "ShortSourceName", sourceName)); } } // Update the file source path appropriately. if (fileSourceAttribSet) { if (!fileSource.EndsWith(Path.DirectorySeparatorChar.ToString(), StringComparison.Ordinal)) { fileSource = String.Concat(fileSource, Path.DirectorySeparatorChar); } } else // add the appropriate part of this directory element to the file source. { string append = String.IsNullOrEmpty(sourceName) ? name : sourceName; if (!String.IsNullOrEmpty(append)) { fileSource = String.Concat(fileSource, append, Path.DirectorySeparatorChar); } } if (null == id) { id = this.Core.CreateIdentifier("dir", parentId, name, shortName, sourceName, shortSourceName); } // Calculate the DefaultDir for the directory row. var defaultDir = String.IsNullOrEmpty(shortName) ? name : String.Concat(shortName, "|", name); if (!String.IsNullOrEmpty(sourceName)) { defaultDir = String.Concat(defaultDir, ":", String.IsNullOrEmpty(shortSourceName) ? sourceName : String.Concat(shortSourceName, "|", sourceName)); } if ("TARGETDIR".Equals(id.Id) && !"SourceDir".Equals(defaultDir)) { this.Core.Write(ErrorMessages.IllegalTargetDirDefaultDir(sourceLineNumbers, defaultDir)); } foreach (var child in node.Elements()) { if (CompilerCore.WixNamespace == child.Name.Namespace) { switch (child.Name.LocalName) { case "Component": this.ParseComponentElement(child, ComplexReferenceParentType.Unknown, null, null, diskId, id.Id, fileSource); break; case "Directory": this.ParseDirectoryElement(child, id.Id, diskId, fileSource); break; case "Merge": this.ParseMergeElement(child, id.Id, diskId); break; case "SymbolPath": if (null != symbols) { symbols += ";" + this.ParseSymbolPathElement(child); } else { symbols = this.ParseSymbolPathElement(child); } break; default: this.Core.UnexpectedElement(node, child); break; } } else { this.Core.ParseExtensionElement(node, child); } } if (!this.Core.EncounteredError) { var tuple = new DirectoryTuple(sourceLineNumbers, id) { ParentDirectoryRef = parentId, Name = name, ShortName = shortName, SourceName = sourceName, SourceShortName = shortSourceName, ComponentGuidGenerationSeed = componentGuidGenerationSeed }; this.Core.AddTuple(tuple); if (null != symbols) { this.Core.AddTuple(new WixDeltaPatchSymbolPathsTuple(sourceLineNumbers, id) { SymbolType = SymbolPathType.Directory, SymbolId = id.Id, SymbolPaths = symbols, }); } } } /// /// Parses a directory reference element. /// /// Element to parse. [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1800:DoNotCastUnnecessarily")] private void ParseDirectoryRefElement(XElement node) { var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node); string id = null; var diskId = CompilerConstants.IntegerNotSet; var fileSource = String.Empty; 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); this.Core.CreateSimpleReference(sourceLineNumbers, "Directory", id); break; case "DiskId": diskId = this.Core.GetAttributeIntegerValue(sourceLineNumbers, attrib, 1, Int16.MaxValue); break; case "FileSource": fileSource = 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 (!String.IsNullOrEmpty(fileSource) && !fileSource.EndsWith(Path.DirectorySeparatorChar.ToString(), StringComparison.Ordinal)) { fileSource = String.Concat(fileSource, Path.DirectorySeparatorChar); } foreach (var child in node.Elements()) { if (CompilerCore.WixNamespace == child.Name.Namespace) { switch (child.Name.LocalName) { case "Component": this.ParseComponentElement(child, ComplexReferenceParentType.Unknown, null, null, diskId, id, fileSource); break; case "Directory": this.ParseDirectoryElement(child, id, diskId, fileSource); break; case "Merge": this.ParseMergeElement(child, id, diskId); break; default: this.Core.UnexpectedElement(node, child); break; } } else { this.Core.ParseExtensionElement(node, child); } } } /// /// Parses a directory search element. /// /// Element to parse. /// Signature of parent search element. /// Signature of search element. private string ParseDirectorySearchElement(XElement node, string parentSignature) { var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node); Identifier id = null; var depth = CompilerConstants.IntegerNotSet; string path = null; var assignToProperty = false; string signature = 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 "Depth": depth = this.Core.GetAttributeIntegerValue(sourceLineNumbers, attrib, 0, Int16.MaxValue); break; case "Path": path = this.Core.GetAttributeValue(sourceLineNumbers, attrib); break; case "AssignToProperty": assignToProperty = 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.CreateIdentifier("dir", path, depth.ToString()); } signature = id.Id; var oneChild = false; var hasFileSearch = false; foreach (var child in node.Elements()) { if (CompilerCore.WixNamespace == child.Name.Namespace) { var childSourceLineNumbers = Preprocessor.GetSourceLineNumbers(child); switch (child.Name.LocalName) { case "DirectorySearch": if (oneChild) { this.Core.Write(ErrorMessages.TooManySearchElements(childSourceLineNumbers, node.Name.LocalName)); } oneChild = true; signature = this.ParseDirectorySearchElement(child, id.Id); break; case "DirectorySearchRef": if (oneChild) { this.Core.Write(ErrorMessages.TooManySearchElements(childSourceLineNumbers, node.Name.LocalName)); } oneChild = true; signature = this.ParseDirectorySearchRefElement(child, id.Id); break; case "FileSearch": if (oneChild) { this.Core.Write(ErrorMessages.TooManySearchElements(sourceLineNumbers, node.Name.LocalName)); } oneChild = true; hasFileSearch = true; signature = this.ParseFileSearchElement(child, id.Id, assignToProperty, depth); break; case "FileSearchRef": if (oneChild) { this.Core.Write(ErrorMessages.TooManySearchElements(sourceLineNumbers, node.Name.LocalName)); } oneChild = true; signature = this.ParseSimpleRefElement(child, "Signature"); break; default: this.Core.UnexpectedElement(node, child); break; } // If AssignToProperty is set, only a FileSearch // or no child element can be nested. if (assignToProperty) { if (!hasFileSearch) { this.Core.Write(ErrorMessages.IllegalParentAttributeWhenNested(sourceLineNumbers, node.Name.LocalName, "AssignToProperty", child.Name.LocalName)); } else if (!oneChild) { // This a normal directory search. assignToProperty = false; } } } else { this.Core.ParseExtensionElement(node, child); } } if (!this.Core.EncounteredError) { var access = id.Access; var rowId = id.Id; // If AssignToProperty is set, the DrLocator row created by // ParseFileSearchElement creates the directory entry to return // and the row created here is for the file search. if (assignToProperty) { access = AccessModifier.Private; rowId = signature; // The property should be set to the directory search Id. signature = id.Id; } var tuple = new DrLocatorTuple(sourceLineNumbers, new Identifier(access, rowId, parentSignature, path)) { SignatureRef = rowId, Parent = parentSignature, Path = path, }; if (CompilerConstants.IntegerNotSet != depth) { tuple.Depth = depth; } this.Core.AddTuple(tuple); } return signature; } /// /// Parses a directory search reference element. /// /// Element to parse. /// Signature of parent search element. /// Signature of search element. private string ParseDirectorySearchRefElement(XElement node, string parentSignature) { var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node); Identifier id = null; Identifier parent = null; string path = null; string signature = 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 "Parent": parent = this.Core.GetAttributeIdentifier(sourceLineNumbers, attrib); break; case "Path": path = this.Core.GetAttributeValue(sourceLineNumbers, attrib); break; default: this.Core.UnexpectedAttribute(node, attrib); break; } } else { this.Core.ParseExtensionAttribute(node, attrib); } } if (null != parent) { if (!String.IsNullOrEmpty(parentSignature)) { this.Core.Write(ErrorMessages.CanNotHaveTwoParents(sourceLineNumbers, id.Id, parent.Id, parentSignature)); } else { parentSignature = parent.Id; } } if (null == id) { id = this.Core.CreateIdentifier("dsr", parentSignature, path); } signature = id.Id; var oneChild = false; foreach (var child in node.Elements()) { if (CompilerCore.WixNamespace == child.Name.Namespace) { var childSourceLineNumbers = Preprocessor.GetSourceLineNumbers(child); switch (child.Name.LocalName) { case "DirectorySearch": if (oneChild) { this.Core.Write(ErrorMessages.TooManySearchElements(childSourceLineNumbers, node.Name.LocalName)); } oneChild = true; signature = this.ParseDirectorySearchElement(child, id.Id); break; case "DirectorySearchRef": if (oneChild) { this.Core.Write(ErrorMessages.TooManySearchElements(childSourceLineNumbers, node.Name.LocalName)); } oneChild = true; signature = this.ParseDirectorySearchRefElement(child, id.Id); break; case "FileSearch": if (oneChild) { this.Core.Write(ErrorMessages.TooManySearchElements(childSourceLineNumbers, node.Name.LocalName)); } oneChild = true; signature = this.ParseFileSearchElement(child, id.Id, false, CompilerConstants.IntegerNotSet); break; case "FileSearchRef": if (oneChild) { this.Core.Write(ErrorMessages.TooManySearchElements(sourceLineNumbers, node.Name.LocalName)); } oneChild = true; signature = this.ParseSimpleRefElement(child, "Signature"); break; default: this.Core.UnexpectedElement(node, child); break; } } else { this.Core.ParseExtensionElement(node, child); } } this.Core.CreateSimpleReference(sourceLineNumbers, "DrLocator", id.Id, parentSignature, path); return signature; } /// /// Parses a feature element. /// /// Element to parse. /// The type of parent. /// Optional identifer for parent feature. /// Display value for last feature used to get the features to display in the same order as specified /// in the source code. [SuppressMessage("Microsoft.Performance", "CA1800:DoNotCastUnnecessarily")] private void ParseFeatureElement(XElement node, ComplexReferenceParentType parentType, string parentId, ref int lastDisplay) { var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node); Identifier id = null; string configurableDirectory = null; string description = null; var displayValue = "collapse"; var level = 1; string title = null; var installDefault = FeatureInstallDefault.Local; var typicalDefault = FeatureTypicalDefault.Install; var disallowAbsent = false; var disallowAdvertise = false; var display = 0; 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 "Absent": var absentValue = this.Core.GetAttributeValue(sourceLineNumbers, attrib); switch (absentValue) { case "allow": // this is the default break; case "disallow": //bits |= MsiInterop.MsidbFeatureAttributesUIDisallowAbsent; disallowAbsent = true; break; case "": break; default: this.Core.Write(ErrorMessages.IllegalAttributeValue(sourceLineNumbers, node.Name.LocalName, attrib.Name.LocalName, absentValue, "allow", "disallow")); break; } break; case "AllowAdvertise": var advertiseValue = this.Core.GetAttributeValue(sourceLineNumbers, attrib); switch (advertiseValue) { case "disallow": case "no": //bits |= MsiInterop.MsidbFeatureAttributesDisallowAdvertise; disallowAdvertise = true; break; case "allow": case "yes": // this is the default break; case "": break; default: this.Core.Write(ErrorMessages.IllegalAttributeValue(sourceLineNumbers, node.Name.LocalName, attrib.Name.LocalName, advertiseValue, "no", "system", "yes")); break; } break; case "ConfigurableDirectory": configurableDirectory = this.Core.CreateDirectoryReferenceFromInlineSyntax(sourceLineNumbers, attrib, null); break; case "Description": description = this.Core.GetAttributeValue(sourceLineNumbers, attrib); break; case "Display": displayValue = this.Core.GetAttributeValue(sourceLineNumbers, attrib); break; case "InstallDefault": var installDefaultValue = this.Core.GetAttributeValue(sourceLineNumbers, attrib); switch (installDefaultValue) { case "followParent": if (ComplexReferenceParentType.Product == parentType) { this.Core.Write(ErrorMessages.RootFeatureCannotFollowParent(sourceLineNumbers)); } //bits = bits | MsiInterop.MsidbFeatureAttributesFollowParent; installDefault = FeatureInstallDefault.FollowParent; break; case "local": // this is the default installDefault = FeatureInstallDefault.Local; break; case "source": //bits = bits | MsiInterop.MsidbFeatureAttributesFavorSource; installDefault = FeatureInstallDefault.Source; break; case "": break; default: this.Core.Write(ErrorMessages.IllegalAttributeValue(sourceLineNumbers, node.Name.LocalName, attrib.Name.LocalName, installDefaultValue, "followParent", "local", "source")); break; } break; case "Level": level = this.Core.GetAttributeIntegerValue(sourceLineNumbers, attrib, 0, Int16.MaxValue); break; case "Title": title = this.Core.GetAttributeValue(sourceLineNumbers, attrib); if ("PUT-FEATURE-TITLE-HERE" == title) { this.Core.Write(WarningMessages.PlaceholderValue(sourceLineNumbers, node.Name.LocalName, attrib.Name.LocalName, title)); } break; case "TypicalDefault": var typicalValue = this.Core.GetAttributeValue(sourceLineNumbers, attrib); switch (typicalValue) { case "advertise": //bits |= MsiInterop.MsidbFeatureAttributesFavorAdvertise; typicalDefault = FeatureTypicalDefault.Advertise; break; case "install": // this is the default typicalDefault = FeatureTypicalDefault.Install; break; default: this.Core.Write(ErrorMessages.IllegalAttributeValue(sourceLineNumbers, node.Name.LocalName, attrib.Name.LocalName, typicalValue, "advertise", "install")); break; } 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 (38 < id.Id.Length) { this.Core.Write(ErrorMessages.FeatureNameTooLong(sourceLineNumbers, node.Name.LocalName, "Id", id.Id)); } if (null != configurableDirectory && configurableDirectory.ToUpper(CultureInfo.InvariantCulture) != configurableDirectory) { this.Core.Write(ErrorMessages.FeatureConfigurableDirectoryNotUppercase(sourceLineNumbers, node.Name.LocalName, "ConfigurableDirectory", configurableDirectory)); } if (FeatureTypicalDefault.Advertise == typicalDefault && disallowAdvertise) { this.Core.Write(ErrorMessages.FeatureCannotFavorAndDisallowAdvertise(sourceLineNumbers, node.Name.LocalName, "TypicalDefault", "advertise", "AllowAdvertise", "no")); } var childDisplay = 0; foreach (var child in node.Elements()) { if (CompilerCore.WixNamespace == child.Name.Namespace) { switch (child.Name.LocalName) { case "ComponentGroupRef": this.ParseComponentGroupRefElement(child, ComplexReferenceParentType.Feature, id.Id, null); break; case "ComponentRef": this.ParseComponentRefElement(child, ComplexReferenceParentType.Feature, id.Id, null); break; case "Component": this.ParseComponentElement(child, ComplexReferenceParentType.Feature, id.Id, null, CompilerConstants.IntegerNotSet, null, null); break; case "Condition": this.ParseConditionElement(child, node.Name.LocalName, id.Id, null); break; case "Feature": this.ParseFeatureElement(child, ComplexReferenceParentType.Feature, id.Id, ref childDisplay); break; case "FeatureGroupRef": this.ParseFeatureGroupRefElement(child, ComplexReferenceParentType.Feature, id.Id); break; case "FeatureRef": this.ParseFeatureRefElement(child, ComplexReferenceParentType.Feature, id.Id); break; case "MergeRef": this.ParseMergeRefElement(child, ComplexReferenceParentType.Feature, id.Id); break; default: this.Core.UnexpectedElement(node, child); break; } } else { this.Core.ParseExtensionElement(node, child); } } switch (displayValue) { case "collapse": lastDisplay = (lastDisplay | 1) + 1; display = lastDisplay; break; case "expand": lastDisplay = (lastDisplay + 1) | 1; display = lastDisplay; break; case "hidden": display = 0; break; default: if (!Int32.TryParse(displayValue, NumberStyles.Integer, CultureInfo.InvariantCulture, out display)) { this.Core.Write(ErrorMessages.IllegalAttributeValue(sourceLineNumbers, node.Name.LocalName, "Display", displayValue, "collapse", "expand", "hidden")); } else { // Save the display value (if its not hidden) for subsequent rows if (0 != display) { lastDisplay = display; } } break; } if (!this.Core.EncounteredError) { var tuple = new FeatureTuple(sourceLineNumbers, id) { ParentFeatureRef = null, // this field is set in the linker Title = title, Description = description, Display = display, Level = level, DirectoryRef = configurableDirectory, DisallowAbsent = disallowAbsent, DisallowAdvertise = disallowAdvertise, InstallDefault = installDefault, TypicalDefault = typicalDefault, }; this.Core.AddTuple(tuple); if (ComplexReferenceParentType.Unknown != parentType) { this.Core.CreateComplexReference(sourceLineNumbers, parentType, parentId, null, ComplexReferenceChildType.Feature, id.Id, false); } } } /// /// Parses a feature reference element. /// /// Element to parse. /// The type of parent. /// Optional identifier for parent feature. [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1800:DoNotCastUnnecessarily")] private void ParseFeatureRefElement(XElement node, ComplexReferenceParentType parentType, string parentId) { var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node); string id = null; var ignoreParent = 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": id = this.Core.GetAttributeIdentifierValue(sourceLineNumbers, attrib); this.Core.CreateSimpleReference(sourceLineNumbers, "Feature", id); break; case "IgnoreParent": ignoreParent = this.Core.GetAttributeYesNoValue(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")); } var lastDisplay = 0; foreach (var child in node.Elements()) { if (CompilerCore.WixNamespace == child.Name.Namespace) { switch (child.Name.LocalName) { case "ComponentGroupRef": this.ParseComponentGroupRefElement(child, ComplexReferenceParentType.Feature, id, null); break; case "ComponentRef": this.ParseComponentRefElement(child, ComplexReferenceParentType.Feature, id, null); break; case "Component": this.ParseComponentElement(child, ComplexReferenceParentType.Feature, id, null, CompilerConstants.IntegerNotSet, null, null); break; case "Feature": this.ParseFeatureElement(child, ComplexReferenceParentType.Feature, id, ref lastDisplay); break; case "FeatureGroup": this.ParseFeatureGroupElement(child, ComplexReferenceParentType.Feature, id); break; case "FeatureGroupRef": this.ParseFeatureGroupRefElement(child, ComplexReferenceParentType.Feature, id); break; case "FeatureRef": this.ParseFeatureRefElement(child, ComplexReferenceParentType.Feature, id); break; case "MergeRef": this.ParseMergeRefElement(child, ComplexReferenceParentType.Feature, id); break; default: this.Core.UnexpectedElement(node, child); break; } } else { this.Core.ParseExtensionElement(node, child); } } if (!this.Core.EncounteredError) { if (ComplexReferenceParentType.Unknown != parentType && YesNoType.Yes != ignoreParent) { this.Core.CreateComplexReference(sourceLineNumbers, parentType, parentId, null, ComplexReferenceChildType.Feature, id, false); } } } /// /// Parses a feature group element. /// /// Element to parse. [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1800:DoNotCastUnnecessarily")] private void ParseFeatureGroupElement(XElement node, ComplexReferenceParentType parentType, string parentId) { var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node); Identifier 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.GetAttributeIdentifier(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; } var lastDisplay = 0; foreach (var child in node.Elements()) { if (CompilerCore.WixNamespace == child.Name.Namespace) { switch (child.Name.LocalName) { case "ComponentGroupRef": this.ParseComponentGroupRefElement(child, ComplexReferenceParentType.FeatureGroup, id.Id, null); break; case "ComponentRef": this.ParseComponentRefElement(child, ComplexReferenceParentType.FeatureGroup, id.Id, null); break; case "Component": this.ParseComponentElement(child, ComplexReferenceParentType.FeatureGroup, id.Id, null, CompilerConstants.IntegerNotSet, null, null); break; case "Feature": this.ParseFeatureElement(child, ComplexReferenceParentType.FeatureGroup, id.Id, ref lastDisplay); break; case "FeatureGroupRef": this.ParseFeatureGroupRefElement(child, ComplexReferenceParentType.FeatureGroup, id.Id); break; case "FeatureRef": this.ParseFeatureRefElement(child, ComplexReferenceParentType.FeatureGroup, id.Id); break; case "MergeRef": this.ParseMergeRefElement(child, ComplexReferenceParentType.FeatureGroup, id.Id); break; default: this.Core.UnexpectedElement(node, child); break; } } else { this.Core.ParseExtensionElement(node, child); } } if (!this.Core.EncounteredError) { this.Core.AddTuple(new WixFeatureGroupTuple(sourceLineNumbers, id)); //Add this FeatureGroup and its parent in WixGroup. this.Core.CreateWixGroupRow(sourceLineNumbers, parentType, parentId, ComplexReferenceChildType.FeatureGroup, id.Id); } } /// /// Parses a feature group reference element. /// /// Element to parse. /// The type of parent. /// Identifier of parent element. private void ParseFeatureGroupRefElement(XElement node, ComplexReferenceParentType parentType, string parentId) { Debug.Assert(ComplexReferenceParentType.Feature == parentType || ComplexReferenceParentType.FeatureGroup == parentType || ComplexReferenceParentType.ComponentGroup == parentType || ComplexReferenceParentType.Product == parentType); var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node); string id = null; var ignoreParent = YesNoType.NotSet; var primary = 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": id = this.Core.GetAttributeValue(sourceLineNumbers, attrib); this.Core.CreateSimpleReference(sourceLineNumbers, "WixFeatureGroup", id); break; case "IgnoreParent": ignoreParent = this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib); break; case "Primary": primary = this.Core.GetAttributeYesNoValue(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) { if (YesNoType.Yes != ignoreParent) { this.Core.CreateComplexReference(sourceLineNumbers, parentType, parentId, null, ComplexReferenceChildType.FeatureGroup, id, (YesNoType.Yes == primary)); } } } /// /// Parses an environment element. /// /// Element to parse. /// Identifier of parent component. private void ParseEnvironmentElement(XElement node, string componentId) { var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node); Identifier id = null; string name = null; EnvironmentActionType? action = null; EnvironmentPartType? part = null; var permanent = false; var separator = ";"; // default to ';' var system = 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 "Action": var actionValue = this.Core.GetAttributeValue(sourceLineNumbers, attrib); switch (actionValue) { case "create": action = EnvironmentActionType.Create; break; case "set": action = EnvironmentActionType.Set; break; case "remove": action = EnvironmentActionType.Remove; break; default: this.Core.Write(ErrorMessages.IllegalAttributeValue(sourceLineNumbers, node.Name.LocalName, attrib.Name.LocalName, value, "create", "set", "remove")); break; } break; case "Name": name = this.Core.GetAttributeValue(sourceLineNumbers, attrib); break; case "Part": var partValue = this.Core.GetAttributeValue(sourceLineNumbers, attrib); switch (partValue) { case "all": part = EnvironmentPartType.All; break; case "first": part = EnvironmentPartType.First; break; case "last": part = EnvironmentPartType.Last; break; case "": break; default: this.Core.Write(ErrorMessages.IllegalAttributeValue(sourceLineNumbers, node.Name.LocalName, "Part", partValue, "all", "first", "last")); break; } break; case "Permanent": permanent = YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib); break; case "Separator": separator = this.Core.GetAttributeValue(sourceLineNumbers, attrib); break; case "System": system = YesNoType.Yes == 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) { id = this.Core.CreateIdentifier("env", ((int?)action)?.ToString(), name, ((int?)part)?.ToString(), system.ToString()); } if (null == name) { this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Name")); } if (!part.HasValue && action == EnvironmentActionType.Create) { this.Core.Write(ErrorMessages.IllegalAttributeWithOtherAttribute(sourceLineNumbers, node.Name.LocalName, "Part", "Action", "create")); } //if (Wix.Environment.PartType.NotSet != partType) //{ // if ("+" == action) // { // this.Core.Write(ErrorMessages.IllegalAttributeWithOtherAttribute(sourceLineNumbers, node.Name.LocalName, "Part", "Action", "create")); // } // switch (partType) // { // case Wix.Environment.PartType.all: // break; // case Wix.Environment.PartType.first: // text = String.Concat(text, separator, "[~]"); // break; // case Wix.Environment.PartType.last: // text = String.Concat("[~]", separator, text); // break; // } //} //if (permanent) //{ // uninstall = null; //} this.Core.ParseForExtensionElements(node); if (!this.Core.EncounteredError) { var tuple = new EnvironmentTuple(sourceLineNumbers, id) { Name = name, Value = value, Separator = separator, Action = action, Part = part, Permanent = permanent, System = system, ComponentRef = componentId }; this.Core.AddTuple(tuple); //var row = this.Core.CreateRow(sourceLineNumbers, TupleDefinitionType.Environment, id); //row.Set(1, String.Concat(action, uninstall, system ? "*" : String.Empty, name)); //row.Set(2, text); //row.Set(3, componentId); } } /// /// Parses an error element. /// /// Element to parse. private void ParseErrorElement(XElement node) { var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node); var id = 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.GetAttributeIntegerValue(sourceLineNumbers, attrib, 0, Int16.MaxValue); break; default: this.Core.UnexpectedAttribute(node, attrib); break; } } else { this.Core.ParseExtensionAttribute(node, attrib); } } if (CompilerConstants.IntegerNotSet == id) { this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Id")); id = CompilerConstants.IllegalInteger; } this.Core.ParseForExtensionElements(node); if (!this.Core.EncounteredError) { var tuple = new ErrorTuple(sourceLineNumbers) { Error = id, Message = Common.GetInnerText(node) }; this.Core.AddTuple(tuple); } } /// /// Parses an extension element. /// /// Element to parse. /// Identifier of parent component. /// Flag if this extension is advertised. /// ProgId for extension. private void ParseExtensionElement(XElement node, string componentId, YesNoType advertise, string progId) { var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node); string extension = null; string mime = null; foreach (var attrib in node.Attributes()) { if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace) { switch (attrib.Name.LocalName) { case "Id": extension = this.Core.GetAttributeValue(sourceLineNumbers, attrib); break; case "Advertise": var extensionAdvertise = this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib); if ((YesNoType.No == advertise && YesNoType.Yes == extensionAdvertise) || (YesNoType.Yes == advertise && YesNoType.No == extensionAdvertise)) { this.Core.Write(ErrorMessages.AdvertiseStateMustMatch(sourceLineNumbers, extensionAdvertise.ToString(), advertise.ToString())); } advertise = extensionAdvertise; break; case "ContentType": mime = this.Core.GetAttributeValue(sourceLineNumbers, attrib); break; default: this.Core.UnexpectedAttribute(node, attrib); break; } } else { var context = new Dictionary() { { "ProgId", progId }, { "ComponentId", componentId } }; this.Core.ParseExtensionAttribute(node, attrib, context); } } if (YesNoType.NotSet == advertise) { advertise = YesNoType.No; } foreach (var child in node.Elements()) { if (CompilerCore.WixNamespace == child.Name.Namespace) { switch (child.Name.LocalName) { case "Verb": this.ParseVerbElement(child, extension, progId, componentId, advertise); break; case "MIME": var newMime = this.ParseMIMEElement(child, extension, componentId, advertise); if (null != newMime && null == mime) { mime = newMime; } break; default: this.Core.UnexpectedElement(node, child); break; } } else { this.Core.ParseExtensionElement(node, child); } } if (YesNoType.Yes == advertise) { if (!this.Core.EncounteredError) { var tuple = new ExtensionTuple(sourceLineNumbers, new Identifier(AccessModifier.Public, extension, componentId)) { Extension = extension, ComponentRef = componentId, ProgIdRef = progId, MimeRef = mime, FeatureRef = Guid.Empty.ToString("B") }; this.Core.AddTuple(tuple); this.Core.EnsureTable(sourceLineNumbers, "Verb"); } } else if (YesNoType.No == advertise) { this.Core.CreateRegistryRow(sourceLineNumbers, RegistryRootType.ClassesRoot, String.Concat(".", extension), String.Empty, progId, componentId); // Extension if (null != mime) { this.Core.CreateRegistryRow(sourceLineNumbers, RegistryRootType.ClassesRoot, String.Concat(".", extension), "Content Type", mime, componentId); // Extension's MIME ContentType } } } /// /// Parses a file element. /// /// File element to parse. /// Parent's component id. /// Ancestor's directory id. /// Disk id inherited from parent component. /// Default source path of parent directory. /// This will be set with the possible keyPath for the parent component. /// true if the component is 64-bit. /// 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. [SuppressMessage("Microsoft.Performance", "CA1800:DoNotCastUnnecessarily")] private YesNoType ParseFileElement(XElement node, string componentId, string directoryId, int diskId, string sourcePath, out string possibleKeyPath, bool win64Component, string componentGuid) { var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node); Identifier id = null; var assemblyType = AssemblyType.NotAnAssembly; string assemblyApplication = null; string assemblyManifest = null; string bindPath = null; //int bits = MsiInterop.MsidbFileAttributesVital; var readOnly = false; var checksum = false; bool? compressed = null; var hidden = false; var system = false; var vital = true; // assume all files are vital. string companionFile = null; string defaultLanguage = null; var defaultSize = 0; string defaultVersion = null; string fontTitle = null; var generatedShortFileName = false; var keyPath = YesNoType.NotSet; string name = null; var patchGroup = CompilerConstants.IntegerNotSet; var patchIgnore = false; var patchIncludeWholeFile = false; var patchAllowIgnoreOnError = false; string ignoreLengths = null; string ignoreOffsets = null; string protectLengths = null; string protectOffsets = null; string symbols = null; string procArch = null; int? selfRegCost = null; string shortName = null; var source = sourcePath; // assume we'll use the parents as the source for this file var sourceSet = 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 "Assembly": var assemblyValue = this.Core.GetAttributeValue(sourceLineNumbers, attrib); switch (assemblyValue) { case ".net": assemblyType = AssemblyType.DotNetAssembly; break; case "no": assemblyType = AssemblyType.NotAnAssembly; break; case "win32": assemblyType = AssemblyType.Win32Assembly; break; default: this.Core.Write(ErrorMessages.IllegalAttributeValue(sourceLineNumbers, "File", "Assembly", assemblyValue, "no", "win32", ".net")); break; } break; case "AssemblyApplication": assemblyApplication = this.Core.GetAttributeIdentifierValue(sourceLineNumbers, attrib); this.Core.CreateSimpleReference(sourceLineNumbers, "File", assemblyApplication); break; case "AssemblyManifest": assemblyManifest = this.Core.GetAttributeIdentifierValue(sourceLineNumbers, attrib); this.Core.CreateSimpleReference(sourceLineNumbers, "File", assemblyManifest); break; case "BindPath": bindPath = this.Core.GetAttributeValue(sourceLineNumbers, attrib, EmptyRule.CanBeEmpty); break; case "Checksum": if (YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib)) { checksum = true; //bits |= MsiInterop.MsidbFileAttributesChecksum; } break; case "CompanionFile": companionFile = this.Core.GetAttributeIdentifierValue(sourceLineNumbers, attrib); this.Core.CreateSimpleReference(sourceLineNumbers, "File", companionFile); break; case "Compressed": var compressedValue = this.Core.GetAttributeYesNoDefaultValue(sourceLineNumbers, attrib); if (YesNoDefaultType.Yes == compressedValue) { compressed = true; //bits |= MsiInterop.MsidbFileAttributesCompressed; } else if (YesNoDefaultType.No == compressedValue) { compressed = false; //bits |= MsiInterop.MsidbFileAttributesNoncompressed; } break; case "DefaultLanguage": defaultLanguage = this.Core.GetAttributeValue(sourceLineNumbers, attrib); break; case "DefaultSize": defaultSize = this.Core.GetAttributeIntegerValue(sourceLineNumbers, attrib, 0, Int32.MaxValue); break; case "DefaultVersion": defaultVersion = this.Core.GetAttributeValue(sourceLineNumbers, attrib); break; case "DiskId": diskId = this.Core.GetAttributeIntegerValue(sourceLineNumbers, attrib, 1, Int16.MaxValue); break; case "FontTitle": fontTitle = this.Core.GetAttributeValue(sourceLineNumbers, attrib); break; case "Hidden": if (YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib)) { hidden = true; //bits |= MsiInterop.MsidbFileAttributesHidden; } break; case "KeyPath": keyPath = this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib); break; case "Name": name = this.Core.GetAttributeLongFilename(sourceLineNumbers, attrib, false); break; case "PatchGroup": patchGroup = this.Core.GetAttributeIntegerValue(sourceLineNumbers, attrib, 1, Int32.MaxValue); break; case "PatchIgnore": patchIgnore = YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib); break; case "PatchWholeFile": patchIncludeWholeFile = YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib); break; case "PatchAllowIgnoreOnError": patchAllowIgnoreOnError = YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib); break; case "ProcessorArchitecture": var procArchValue = this.Core.GetAttributeValue(sourceLineNumbers, attrib); switch (procArchValue) { case "msil": procArch = "MSIL"; break; case "x86": procArch = "x86"; break; case "x64": procArch = "amd64"; break; case "ia64": procArch = "ia64"; break; case "": break; default: this.Core.Write(ErrorMessages.IllegalAttributeValue(sourceLineNumbers, "File", "ProcessorArchitecture", procArchValue, "msil", "x86", "x64", "ia64")); break; } break; case "ReadOnly": if (YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib)) { readOnly = true; //bits |= MsiInterop.MsidbFileAttributesReadOnly; } break; case "SelfRegCost": selfRegCost = this.Core.GetAttributeIntegerValue(sourceLineNumbers, attrib, 0, Int16.MaxValue); break; case "ShortName": shortName = this.Core.GetAttributeShortFilename(sourceLineNumbers, attrib, false); break; case "Source": source = this.Core.GetAttributeValue(sourceLineNumbers, attrib); sourceSet = true; break; case "System": if (YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib)) { system = true; //bits |= MsiInterop.MsidbFileAttributesSystem; } break; case "TrueType": if (YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib)) { fontTitle = String.Empty; } break; case "Vital": var isVital = this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib); if (YesNoType.Yes == isVital) { vital = true; //bits |= MsiInterop.MsidbFileAttributesVital; } else if (YesNoType.No == isVital) { vital = false; //bits &= ~MsiInterop.MsidbFileAttributesVital; } break; default: this.Core.UnexpectedAttribute(node, attrib); break; } } else { this.Core.ParseExtensionAttribute(node, attrib); } } if (null != companionFile) { // the companion file cannot be the key path of a component if (YesNoType.Yes == keyPath) { this.Core.Write(ErrorMessages.IllegalAttributeWithOtherAttribute(sourceLineNumbers, node.Name.LocalName, "CompanionFile", "KeyPath", "yes")); } } if (sourceSet && !source.EndsWith(Path.DirectorySeparatorChar.ToString(), StringComparison.Ordinal) && null == name) { name = Path.GetFileName(source); if (!this.Core.IsValidLongFilename(name, false)) { this.Core.Write(ErrorMessages.IllegalLongFilename(sourceLineNumbers, node.Name.LocalName, "Source", name)); } } // generate a short file name if (null == shortName && (null != name && !this.Core.IsValidShortFilename(name, false))) { shortName = this.Core.CreateShortName(name, true, false, node.Name.LocalName, directoryId); generatedShortFileName = true; } if (null == id) { id = this.Core.CreateIdentifier("fil", directoryId, name ?? shortName); } if (!this.compilingModule && CompilerConstants.IntegerNotSet == diskId) { diskId = 1; // default to first Media } if (null != defaultVersion && null != companionFile) { this.Core.Write(ErrorMessages.IllegalAttributeWithOtherAttribute(sourceLineNumbers, node.Name.LocalName, "DefaultVersion", "CompanionFile", companionFile)); } if (AssemblyType.NotAnAssembly == assemblyType) { if (null != assemblyManifest) { this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Assembly", "AssemblyManifest")); } if (null != assemblyApplication) { this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Assembly", "AssemblyApplication")); } } else { if (AssemblyType.Win32Assembly == assemblyType && null == assemblyManifest) { this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "AssemblyManifest", "Assembly", "win32")); } // allow "*" guid components to omit explicit KeyPath as they can have only one file and therefore this file is the keypath if (YesNoType.Yes != keyPath && "*" != componentGuid) { this.Core.Write(ErrorMessages.IllegalAttributeValueWithoutOtherAttribute(sourceLineNumbers, node.Name.LocalName, "Assembly", (AssemblyType.DotNetAssembly == assemblyType ? ".net" : "win32"), "KeyPath", "yes")); } } foreach (var child in node.Elements()) { if (CompilerCore.WixNamespace == child.Name.Namespace) { switch (child.Name.LocalName) { case "AppId": this.ParseAppIdElement(child, componentId, YesNoType.NotSet, id.Id, null, null); break; case "AssemblyName": this.ParseAssemblyName(child, componentId); break; case "Class": this.ParseClassElement(child, componentId, YesNoType.NotSet, id.Id, null, null, null); break; case "CopyFile": this.ParseCopyFileElement(child, componentId, id.Id); break; case "IgnoreRange": this.ParseRangeElement(child, ref ignoreOffsets, ref ignoreLengths); break; case "ODBCDriver": this.ParseODBCDriverOrTranslator(child, componentId, id.Id, TupleDefinitionType.ODBCDriver); break; case "ODBCTranslator": this.ParseODBCDriverOrTranslator(child, componentId, id.Id, TupleDefinitionType.ODBCTranslator); break; case "Permission": this.ParsePermissionElement(child, id.Id, "File"); break; case "PermissionEx": this.ParsePermissionExElement(child, id.Id, "File"); break; case "ProtectRange": this.ParseRangeElement(child, ref protectOffsets, ref protectLengths); break; case "Shortcut": this.ParseShortcutElement(child, componentId, node.Name.LocalName, id.Id, keyPath); break; case "SymbolPath": if (null != symbols) { symbols += ";" + this.ParseSymbolPathElement(child); } else { symbols = this.ParseSymbolPathElement(child); } break; case "TypeLib": this.ParseTypeLibElement(child, componentId, id.Id, win64Component); break; default: this.Core.UnexpectedElement(node, child); break; } } else { var context = new Dictionary() { { "FileId", id.Id }, { "ComponentId", componentId }, { "DirectoryId", directoryId }, { "Win64", win64Component.ToString() } }; this.Core.ParseExtensionElement(node, child, context); } } if (!this.Core.EncounteredError) { var patchAttributes = PatchAttributeType.None; if (patchIgnore) { patchAttributes |= PatchAttributeType.Ignore; } if (patchIncludeWholeFile) { patchAttributes |= PatchAttributeType.IncludeWholeFile; } if (patchAllowIgnoreOnError) { patchAttributes |= PatchAttributeType.AllowIgnoreOnError; } if (String.IsNullOrEmpty(source)) { source = name ?? shortName; } else if (source.EndsWith(Path.DirectorySeparatorChar.ToString(), StringComparison.Ordinal)) // if source relies on parent directories, append the file name { source = null == name ? Path.Combine(source, shortName) : Path.Combine(source, name); } var attributes = FileTupleAttributes.None; attributes |= readOnly ? FileTupleAttributes.ReadOnly : 0; attributes |= hidden ? FileTupleAttributes.Hidden : 0; attributes |= system ? FileTupleAttributes.System : 0; attributes |= vital ? FileTupleAttributes.Vital : 0; attributes |= checksum ? FileTupleAttributes.Checksum : 0; attributes |= compressed.HasValue && compressed == true ? FileTupleAttributes.Compressed : 0; attributes |= compressed.HasValue && compressed == false ? FileTupleAttributes.Uncompressed : 0; attributes |= generatedShortFileName ? FileTupleAttributes.GeneratedShortFileName : 0; var tuple = new FileTuple(sourceLineNumbers, id) { ComponentRef = componentId, Name = name, ShortName = shortName, FileSize = defaultSize, Version = companionFile ?? defaultVersion, Language = defaultLanguage, Attributes = attributes, //ReadOnly = readOnly, //Hidden = hidden, //System = system, //Vital = vital, //Checksum = checksum, //Compressed = compressed, //GeneratedShortFileName = generatedShortFileName, DirectoryRef = directoryId, DiskId = (CompilerConstants.IntegerNotSet == diskId) ? null : (int?)diskId, Source = new IntermediateFieldPathValue { Path = source }, FontTitle = fontTitle, SelfRegCost = selfRegCost, BindPath = bindPath, PatchGroup = (CompilerConstants.IntegerNotSet == patchGroup) ? null : (int?)patchGroup, PatchAttributes = patchAttributes, // Delta patching information RetainLengths = protectLengths, IgnoreOffsets = ignoreOffsets, IgnoreLengths = ignoreLengths, RetainOffsets = protectOffsets, SymbolPaths = symbols }; this.Core.AddTuple(tuple); if (AssemblyType.NotAnAssembly != assemblyType) { this.Core.AddTuple(new AssemblyTuple(sourceLineNumbers, id) { ComponentRef = componentId, FeatureRef = Guid.Empty.ToString("B"), ManifestFileRef = assemblyManifest, ApplicationFileRef = assemblyApplication, Type = assemblyType, ProcessorArchitecture = procArch, }); } } this.Core.CreateSimpleReference(sourceLineNumbers, "Media", diskId.ToString(CultureInfo.InvariantCulture.NumberFormat)); // If this component does not have a companion file this file is a possible keypath. possibleKeyPath = null; if (null == companionFile) { possibleKeyPath = id.Id; } return keyPath; } /// /// Parses a file search element. /// /// Element to parse. /// Signature of parent search element. /// Whether this search element is used to search for the parent directory. /// The depth specified by the parent search element. /// Signature of search element. private string ParseFileSearchElement(XElement node, string parentSignature, bool parentDirectorySearch, int parentDepth) { var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node); Identifier id = null; string languages = null; var minDate = CompilerConstants.IntegerNotSet; var maxDate = CompilerConstants.IntegerNotSet; var maxSize = CompilerConstants.IntegerNotSet; var minSize = CompilerConstants.IntegerNotSet; string maxVersion = null; string minVersion = null; string name = 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 "Name": name = this.Core.GetAttributeLongFilename(sourceLineNumbers, attrib, false); break; case "MinVersion": minVersion = this.Core.GetAttributeValue(sourceLineNumbers, attrib); break; case "MaxVersion": maxVersion = this.Core.GetAttributeValue(sourceLineNumbers, attrib); break; case "MinSize": minSize = this.Core.GetAttributeIntegerValue(sourceLineNumbers, attrib, 0, Int32.MaxValue); break; case "MaxSize": maxSize = this.Core.GetAttributeIntegerValue(sourceLineNumbers, attrib, 0, Int32.MaxValue); break; case "MinDate": minDate = this.Core.GetAttributeDateTimeValue(sourceLineNumbers, attrib); break; case "MaxDate": maxDate = this.Core.GetAttributeDateTimeValue(sourceLineNumbers, attrib); break; case "Languages": languages = this.Core.GetAttributeValue(sourceLineNumbers, attrib); break; case "ShortName": shortName = this.Core.GetAttributeShortFilename(sourceLineNumbers, attrib, false); break; default: this.Core.UnexpectedAttribute(node, attrib); break; } } else { this.Core.ParseExtensionAttribute(node, attrib); } } // Using both ShortName and Name will not always work due to a Windows Installer bug. if (null != shortName && null != name) { this.Core.Write(WarningMessages.FileSearchFileNameIssue(sourceLineNumbers, node.Name.LocalName, "ShortName", "Name")); } else if (null == shortName && null == name) // at least one name must be specified. { this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Name")); } if (this.Core.IsValidShortFilename(name, false)) { if (null == shortName) { shortName = name; name = null; } else { this.Core.Write(ErrorMessages.IllegalAttributeValueWithOtherAttribute(sourceLineNumbers, node.Name.LocalName, "Name", name, "ShortName")); } } if (null == id) { if (String.IsNullOrEmpty(parentSignature)) { id = this.Core.CreateIdentifier("fs", name ?? shortName); } else // reuse parent signature in the Signature table { id = new Identifier(AccessModifier.Private, parentSignature); } } var isSameId = String.Equals(id.Id, parentSignature, StringComparison.Ordinal); if (parentDirectorySearch) { // If searching for the parent directory, the Id attribute // value must be specified and unique. if (isSameId) { this.Core.Write(ErrorMessages.UniqueFileSearchIdRequired(sourceLineNumbers, parentSignature, node.Name.LocalName)); } } else if (parentDepth > 1) { // Otherwise, if the depth > 1 the Id must be absent or the same // as the parent DirectorySearch if AssignToProperty is not set. if (!isSameId) { this.Core.Write(ErrorMessages.IllegalSearchIdForParentDepth(sourceLineNumbers, id.Id, parentSignature)); } } this.Core.ParseForExtensionElements(node); if (!this.Core.EncounteredError) { var tuple = new SignatureTuple(sourceLineNumbers, id) { FileName = name ?? shortName, MinVersion = minVersion, MaxVersion = maxVersion, Languages = languages }; if (CompilerConstants.IntegerNotSet != minSize) { tuple.MinSize = minSize; } if (CompilerConstants.IntegerNotSet != maxSize) { tuple.MaxSize = maxSize; } if (CompilerConstants.IntegerNotSet != minDate) { tuple.MinDate = minDate; } if (CompilerConstants.IntegerNotSet != maxDate) { tuple.MaxDate = maxDate; } this.Core.AddTuple(tuple); // Create a DrLocator row to associate the file with a directory // when a different identifier is specified for the FileSearch. if (!isSameId) { if (parentDirectorySearch) { // Creates the DrLocator row for the directory search while // the parent DirectorySearch creates the file locator row. this.Core.AddTuple(new DrLocatorTuple(sourceLineNumbers, new Identifier(AccessModifier.Public, parentSignature, id.Id, String.Empty)) { SignatureRef = parentSignature, Parent = id.Id }); } else { this.Core.AddTuple(new DrLocatorTuple(sourceLineNumbers, new Identifier(AccessModifier.Public, id.Id, parentSignature, String.Empty)) { SignatureRef = id.Id, Parent = parentSignature }); } } } return id.Id; // the id of the FileSearch element is its signature } /// /// Parses a fragment element. /// /// Element to parse. private void ParseFragmentElement(XElement node) { var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node); Identifier id = null; 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 "Id": id = this.Core.GetAttributeIdentifier(sourceLineNumbers, attrib); break; default: this.Core.UnexpectedAttribute(node, attrib); break; } } else { this.Core.ParseExtensionAttribute(node, attrib); } } // NOTE: Id is not required for Fragments, this is a departure from the normal run of the mill processing. this.Core.CreateActiveSection(id?.Id, SectionType.Fragment, 0, this.Context.CompilationId); 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 "BootstrapperApplication": this.ParseBootstrapperApplicationElement(child); break; case "BootstrapperApplicationRef": this.ParseBootstrapperApplicationRefElement(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, id?.Id); break; case "Condition": this.ParseConditionElement(child, node.Name.LocalName, null, null); break; case "Container": this.ParseContainerElement(child); break; case "CustomAction": this.ParseCustomActionElement(child); break; case "CustomActionRef": this.ParseSimpleRefElement(child, "CustomAction"); break; case "CustomTable": this.ParseCustomTableElement(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, "MsiEmbeddedChainer"); break; case "EnsureTable": this.ParseEnsureTableElement(child); break; case "Feature": this.ParseFeatureElement(child, ComplexReferenceParentType.Unknown, null, ref featureDisplay); break; case "FeatureGroup": this.ParseFeatureGroupElement(child, ComplexReferenceParentType.Unknown, id?.Id); break; case "FeatureRef": this.ParseFeatureRefElement(child, ComplexReferenceParentType.Unknown, null); break; case "Icon": this.ParseIconElement(child); break; case "IgnoreModularization": this.ParseIgnoreModularizationElement(child); break; case "Media": this.ParseMediaElement(child, null); break; case "MediaTemplate": this.ParseMediaTemplateElement(child, null); break; case "PackageGroup": this.ParsePackageGroupElement(child); break; case "PackageCertificates": case "PatchCertificates": this.ParseCertificatesElement(child); break; case "PatchFamily": this.ParsePatchFamilyElement(child, ComplexReferenceParentType.Unknown, id.Id); break; case "PatchFamilyGroup": this.ParsePatchFamilyGroupElement(child, ComplexReferenceParentType.Unknown, id.Id); break; case "PatchFamilyGroupRef": this.ParsePatchFamilyGroupRefElement(child, ComplexReferenceParentType.Unknown, id.Id); break; case "PayloadGroup": this.ParsePayloadGroupElement(child, ComplexReferenceParentType.Unknown, null); break; case "Property": this.ParsePropertyElement(child); break; case "PropertyRef": this.ParseSimpleRefElement(child, "Property"); break; case "RelatedBundle": this.ParseRelatedBundleElement(child); 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 "UI": this.ParseUIElement(child); break; case "UIRef": this.ParseSimpleRefElement(child, "WixUI"); break; case "Upgrade": this.ParseUpgradeElement(child); break; case "Variable": this.ParseVariableElement(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 && null != id) { this.Core.AddTuple(new WixFragmentTuple(sourceLineNumbers, id)); } } /// /// Parses a condition element. /// /// Element to parse. /// LocalName of the parent element. /// Id of the parent element. /// Dialog of the parent element if its a Control. /// The condition if one was found. private string ParseConditionElement(XElement node, string parentElementLocalName, string id, string dialog) { var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node); string action = null; string condition = null; var level = CompilerConstants.IntegerNotSet; string message = null; foreach (var attrib in node.Attributes()) { if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace) { switch (attrib.Name.LocalName) { case "Action": if ("Control" == parentElementLocalName) { action = this.Core.GetAttributeValue(sourceLineNumbers, attrib); switch (action) { case "default": action = "Default"; break; case "disnable": action = "Disable"; break; case "enable": action = "Enable"; break; case "hide": action = "Hide"; break; case "show": action = "Show"; break; case "": break; default: this.Core.Write(ErrorMessages.IllegalAttributeValue(sourceLineNumbers, node.Name.LocalName, attrib.Name.LocalName, action, "default", "disable", "enable", "hide", "show")); break; } } else { this.Core.UnexpectedAttribute(node, attrib); } break; case "Level": if ("Feature" == parentElementLocalName) { level = this.Core.GetAttributeIntegerValue(sourceLineNumbers, attrib, 0, Int16.MaxValue); } else { this.Core.UnexpectedAttribute(node, attrib); } break; case "Message": if ("Fragment" == parentElementLocalName || "Product" == parentElementLocalName) { message = this.Core.GetAttributeValue(sourceLineNumbers, attrib); } else { this.Core.UnexpectedAttribute(node, attrib); } break; default: this.Core.UnexpectedAttribute(node, attrib); break; } } else { this.Core.ParseExtensionAttribute(node, attrib); } } // get the condition from the inner text of the element condition = this.Core.GetConditionInnerText(node); this.Core.ParseForExtensionElements(node); // the condition should not be empty if (null == condition || 0 == condition.Length) { condition = null; this.Core.Write(ErrorMessages.ConditionExpected(sourceLineNumbers, node.Name.LocalName)); } switch (parentElementLocalName) { case "Control": if (null == action) { this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Action")); } if (!this.Core.EncounteredError) { this.Core.AddTuple(new ControlConditionTuple(sourceLineNumbers) { DialogRef = dialog, ControlRef = id, Action = action, Condition = condition, }); } break; case "Feature": if (CompilerConstants.IntegerNotSet == level) { this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Level")); level = CompilerConstants.IllegalInteger; } if (!this.Core.EncounteredError) { this.Core.AddTuple(new ConditionTuple(sourceLineNumbers) { FeatureRef = id, Level = level, Condition = condition }); } break; case "Fragment": case "Product": if (null == message) { this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Message")); } if (!this.Core.EncounteredError) { this.Core.AddTuple(new LaunchConditionTuple(sourceLineNumbers) { Condition = condition, Description = message }); } break; } return condition; } /// /// Parses a IniFile element. /// /// Element to parse. /// Identifier of the parent component. private void ParseIniFileElement(XElement node, string componentId) { var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node); Identifier id = null; InifFileActionType? action = null; string directory = null; string key = null; string name = null; string section = null; string shortName = 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 "Action": var actionValue = this.Core.GetAttributeValue(sourceLineNumbers, attrib); switch (actionValue) { case "addLine": action = InifFileActionType.AddLine; break; case "addTag": action = InifFileActionType.AddTag; break; case "removeLine": action = InifFileActionType.RemoveLine; break; case "removeTag": action = InifFileActionType.RemoveTag; break; case "": // error case handled by GetAttributeValue() break; default: this.Core.Write(ErrorMessages.IllegalAttributeValue(sourceLineNumbers, node.Name.LocalName, "Action", actionValue, "addLine", "addTag", "createLine", "removeLine", "removeTag")); break; } break; case "Directory": directory = this.Core.GetAttributeIdentifierValue(sourceLineNumbers, attrib); break; case "Key": key = this.Core.GetAttributeValue(sourceLineNumbers, attrib); break; case "Name": name = this.Core.GetAttributeLongFilename(sourceLineNumbers, attrib, false); break; case "Section": section = this.Core.GetAttributeValue(sourceLineNumbers, attrib); break; case "ShortName": shortName = this.Core.GetAttributeShortFilename(sourceLineNumbers, attrib, false); break; case "Value": value = this.Core.GetAttributeValue(sourceLineNumbers, attrib); break; default: this.Core.UnexpectedAttribute(node, attrib); break; } } else { this.Core.ParseExtensionAttribute(node, attrib); } } if (!action.HasValue) { this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Action")); } else if (InifFileActionType.AddLine == action || InifFileActionType.AddTag == action || InifFileActionType.CreateLine == action) { if (null == value) { this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Value")); } } if (null == key) { this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Key")); } if (null == name) { this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Name")); } else if (0 < name.Length) { if (this.Core.IsValidShortFilename(name, false)) { if (null == shortName) { shortName = name; name = null; } else { this.Core.Write(ErrorMessages.IllegalAttributeValueWithOtherAttribute(sourceLineNumbers, node.Name.LocalName, "Name", name, "ShortName")); } } else // generate a short file name. { if (null == shortName) { shortName = this.Core.CreateShortName(name, true, false, node.Name.LocalName, componentId); } } } if (null == section) { this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Section")); } if (null == id) { id = this.Core.CreateIdentifier("ini", directory, name ?? shortName, section, key, name); } this.Core.ParseForExtensionElements(node); if (!this.Core.EncounteredError) { var tuple = new IniFileTuple(sourceLineNumbers, id) { FileName = this.GetMsiFilenameValue(shortName, name), DirProperty = directory, Section = section, Key = key, Value = value, Action = action.Value, ComponentRef = componentId }; this.Core.AddTuple(tuple); } } /// /// Parses an IniFile search element. /// /// Element to parse. /// Signature for search element. private string ParseIniFileSearchElement(XElement node) { var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node); Identifier id = null; var field = CompilerConstants.IntegerNotSet; string key = null; string name = null; string section = null; string shortName = null; string signature = null; var type = 1; // default is file 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 "Field": field = this.Core.GetAttributeIntegerValue(sourceLineNumbers, attrib, 0, Int16.MaxValue); break; case "Key": key = this.Core.GetAttributeValue(sourceLineNumbers, attrib); break; case "Name": name = this.Core.GetAttributeLongFilename(sourceLineNumbers, attrib, false); break; case "Section": section = this.Core.GetAttributeValue(sourceLineNumbers, attrib); break; case "ShortName": shortName = this.Core.GetAttributeShortFilename(sourceLineNumbers, attrib, false); break; case "Type": var typeValue = this.Core.GetAttributeValue(sourceLineNumbers, attrib); switch (typeValue) { case "directory": type = 0; break; case "file": type = 1; break; case "raw": type = 2; break; case "": break; default: this.Core.Write(ErrorMessages.IllegalAttributeValue(sourceLineNumbers, node.Name.LocalName, "Type", typeValue, "directory", "file", "registry")); break; } break; default: this.Core.UnexpectedAttribute(node, attrib); break; } } else { this.Core.ParseExtensionAttribute(node, attrib); } } if (null == key) { this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Key")); } if (null == name) { this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Name")); } else if (0 < name.Length) { if (this.Core.IsValidShortFilename(name, false)) { if (null == shortName) { shortName = name; name = null; } else { this.Core.Write(ErrorMessages.IllegalAttributeValueWithOtherAttribute(sourceLineNumbers, node.Name.LocalName, "Name", name, "ShortName")); } } else if (null == shortName) // generate a short file name. { shortName = this.Core.CreateShortName(name, true, false, node.Name.LocalName); } } if (null == section) { this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Section")); } if (null == id) { id = this.Core.CreateIdentifier("ini", name, section, key, field.ToString(), type.ToString()); } signature = id.Id; var oneChild = false; foreach (var child in node.Elements()) { if (CompilerCore.WixNamespace == child.Name.Namespace) { var childSourceLineNumbers = Preprocessor.GetSourceLineNumbers(child); switch (child.Name.LocalName) { case "DirectorySearch": if (oneChild) { this.Core.Write(ErrorMessages.TooManySearchElements(childSourceLineNumbers, node.Name.LocalName)); } oneChild = true; // directorysearch parentage should work like directory element, not the rest of the signature type because of the DrLocator.Parent column signature = this.ParseDirectorySearchElement(child, id.Id); break; case "DirectorySearchRef": if (oneChild) { this.Core.Write(ErrorMessages.TooManySearchElements(childSourceLineNumbers, node.Name.LocalName)); } oneChild = true; signature = this.ParseDirectorySearchRefElement(child, id.Id); break; case "FileSearch": if (oneChild) { this.Core.Write(ErrorMessages.TooManySearchElements(sourceLineNumbers, node.Name.LocalName)); } oneChild = true; signature = this.ParseFileSearchElement(child, id.Id, false, CompilerConstants.IntegerNotSet); id = new Identifier(AccessModifier.Private, signature); // FileSearch signatures override parent signatures break; case "FileSearchRef": if (oneChild) { this.Core.Write(ErrorMessages.TooManySearchElements(sourceLineNumbers, node.Name.LocalName)); } oneChild = true; var newId = this.ParseSimpleRefElement(child, "Signature"); // FileSearch signatures override parent signatures id = new Identifier(AccessModifier.Private, newId); signature = null; break; default: this.Core.UnexpectedElement(node, child); break; } } else { this.Core.ParseExtensionElement(node, child); } } if (!this.Core.EncounteredError) { var tuple = new IniLocatorTuple(sourceLineNumbers, id) { SignatureRef = id.Id, FileName = this.GetMsiFilenameValue(shortName, name), Section = section, Key = key, Type = type }; if (CompilerConstants.IntegerNotSet != field) { tuple.Field = field; } this.Core.AddTuple(tuple); } return signature; } /// /// Parses an isolated component element. /// /// Element to parse. /// Identifier of parent component. private void ParseIsolateComponentElement(XElement node, string componentId) { var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node); string shared = null; foreach (var attrib in node.Attributes()) { if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace) { switch (attrib.Name.LocalName) { case "Shared": shared = this.Core.GetAttributeIdentifierValue(sourceLineNumbers, attrib); this.Core.CreateSimpleReference(sourceLineNumbers, "Component", shared); break; default: this.Core.UnexpectedAttribute(node, attrib); break; } } else { this.Core.ParseExtensionAttribute(node, attrib); } } if (null == shared) { this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Shared")); } this.Core.ParseForExtensionElements(node); if (!this.Core.EncounteredError) { this.Core.AddTuple(new IsolatedComponentTuple(sourceLineNumbers) { SharedComponentRef = shared, ApplicationComponentRef = componentId }); } } /// /// Parses a PatchCertificates or PackageCertificates element. /// /// The element to parse. private void ParseCertificatesElement(XElement node) { var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node); // no attributes are supported for this element foreach (var attrib in node.Attributes()) { if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace) { this.Core.UnexpectedAttribute(node, attrib); } else { this.Core.ParseExtensionAttribute(node, attrib); } } foreach (var child in node.Elements()) { if (CompilerCore.WixNamespace == child.Name.Namespace) { switch (child.Name.LocalName) { case "DigitalCertificate": var name = this.ParseDigitalCertificateElement(child); if (!this.Core.EncounteredError) { var tuple = this.Core.CreateTuple(sourceLineNumbers, "PatchCertificates" == node.Name.LocalName ? TupleDefinitionType.MsiPatchCertificate : TupleDefinitionType.MsiPackageCertificate); tuple.Set(0, name); tuple.Set(1, name); } break; default: this.Core.UnexpectedElement(node, child); break; } } else { this.Core.ParseExtensionElement(node, child); } } } /// /// Parses an digital certificate element. /// /// Element to parse. /// The identifier of the certificate. private string ParseDigitalCertificateElement(XElement node) { var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node); Identifier id = 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 "Id": id = this.Core.GetAttributeIdentifier(sourceLineNumbers, attrib); 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 == id) { this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Id")); id = Identifier.Invalid; } else if (40 < id.Id.Length) { this.Core.Write(ErrorMessages.StreamNameTooLong(sourceLineNumbers, node.Name.LocalName, "Id", id.Id, id.Id.Length, 40)); // No need to check for modularization problems since DigitalSignature and thus DigitalCertificate // currently have no usage in merge modules. } if (null == sourceFile) { this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "SourceFile")); } this.Core.ParseForExtensionElements(node); if (!this.Core.EncounteredError) { this.Core.AddTuple(new MsiDigitalCertificateTuple(sourceLineNumbers, id) { CertData = sourceFile }); } return id.Id; } /// /// Parses an digital signature element. /// /// Element to parse. /// Disk id inherited from parent media. private void ParseDigitalSignatureElement(XElement node, string diskId) { var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node); string certificateId = 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 "SourceFile": sourceFile = this.Core.GetAttributeValue(sourceLineNumbers, attrib); break; default: this.Core.UnexpectedAttribute(node, attrib); break; } } else { this.Core.ParseExtensionAttribute(node, attrib); } } // sanity check for debug to ensure the stream name will not be a problem if (null != sourceFile) { Debug.Assert(62 >= "MsiDigitalSignature.Media.".Length + diskId.Length); } foreach (var child in node.Elements()) { if (CompilerCore.WixNamespace == child.Name.Namespace) { switch (child.Name.LocalName) { case "DigitalCertificate": certificateId = this.ParseDigitalCertificateElement(child); break; default: this.Core.UnexpectedElement(node, child); break; } } else { this.Core.ParseExtensionElement(node, child); } } if (null == certificateId) { this.Core.Write(ErrorMessages.ExpectedElement(sourceLineNumbers, node.Name.LocalName, "DigitalCertificate")); } if (!this.Core.EncounteredError) { this.Core.AddTuple(new MsiDigitalSignatureTuple(sourceLineNumbers, new Identifier(AccessModifier.Public, "Media", diskId)) { Table = "Media", SignObject = diskId, DigitalCertificateRef = certificateId, Hash = sourceFile }); } } /// /// Parses a MajorUpgrade element. /// /// The element to parse. /// The parent element. private void ParseMajorUpgradeElement(XElement node, IDictionary contextValues) { var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node); var migrateFeatures = true; var ignoreRemoveFailure = false; var allowDowngrades = false; var allowSameVersionUpgrades = false; var blockUpgrades = false; string downgradeErrorMessage = null; string disallowUpgradeErrorMessage = null; string removeFeatures = null; string schedule = null; var upgradeCode = contextValues["UpgradeCode"]; if (String.IsNullOrEmpty(upgradeCode)) { this.Core.Write(ErrorMessages.ParentElementAttributeRequired(sourceLineNumbers, "Product", "UpgradeCode", node.Name.LocalName)); } var productVersion = contextValues["ProductVersion"]; if (String.IsNullOrEmpty(productVersion)) { this.Core.Write(ErrorMessages.ParentElementAttributeRequired(sourceLineNumbers, "Product", "Version", node.Name.LocalName)); } var productLanguage = contextValues["ProductLanguage"]; foreach (var attrib in node.Attributes()) { if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace) { switch (attrib.Name.LocalName) { case "AllowDowngrades": allowDowngrades = YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib); break; case "AllowSameVersionUpgrades": allowSameVersionUpgrades = YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib); break; case "Disallow": blockUpgrades = YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib); break; case "DowngradeErrorMessage": downgradeErrorMessage = this.Core.GetAttributeValue(sourceLineNumbers, attrib); break; case "DisallowUpgradeErrorMessage": disallowUpgradeErrorMessage = this.Core.GetAttributeValue(sourceLineNumbers, attrib); break; case "MigrateFeatures": migrateFeatures = (YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib)); break; case "IgnoreLanguage": if (YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib)) { productLanguage = null; } break; case "IgnoreRemoveFailure": ignoreRemoveFailure = (YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib)); break; case "RemoveFeatures": removeFeatures = this.Core.GetAttributeValue(sourceLineNumbers, attrib); break; case "Schedule": schedule = this.Core.GetAttributeValue(sourceLineNumbers, attrib); break; default: this.Core.UnexpectedAttribute(node, attrib); break; } } else { this.Core.ParseExtensionAttribute(node, attrib); } } this.Core.ParseForExtensionElements(node); if (!allowDowngrades && String.IsNullOrEmpty(downgradeErrorMessage)) { this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "DowngradeErrorMessage", "AllowDowngrades", "yes", true)); } if (allowDowngrades && !String.IsNullOrEmpty(downgradeErrorMessage)) { this.Core.Write(ErrorMessages.IllegalAttributeWithOtherAttribute(sourceLineNumbers, node.Name.LocalName, "DowngradeErrorMessage", "AllowDowngrades", "yes")); } if (allowDowngrades && allowSameVersionUpgrades) { this.Core.Write(ErrorMessages.IllegalAttributeWithOtherAttribute(sourceLineNumbers, node.Name.LocalName, "AllowSameVersionUpgrades", "AllowDowngrades", "yes")); } if (blockUpgrades && String.IsNullOrEmpty(disallowUpgradeErrorMessage)) { this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "DisallowUpgradeErrorMessage", "Disallow", "yes", true)); } if (!blockUpgrades && !String.IsNullOrEmpty(disallowUpgradeErrorMessage)) { this.Core.Write(ErrorMessages.IllegalAttributeWithOtherAttribute(sourceLineNumbers, node.Name.LocalName, "DisallowUpgradeErrorMessage", "Disallow", "yes")); } if (!this.Core.EncounteredError) { // create the row that performs the upgrade (or downgrade) var tuple = new UpgradeTuple(sourceLineNumbers) { UpgradeCode = upgradeCode, Remove = removeFeatures, MigrateFeatures = migrateFeatures, IgnoreRemoveFailures = ignoreRemoveFailure, ActionProperty = Common.UpgradeDetectedProperty }; if (allowDowngrades) { tuple.VersionMin = "0"; tuple.Language = productLanguage; tuple.VersionMinInclusive = true; } else { tuple.VersionMax = productVersion; tuple.Language = productLanguage; tuple.VersionMaxInclusive = allowSameVersionUpgrades; } this.Core.AddTuple(tuple); // Ensure the action property is secure. this.AddWixPropertyRow(sourceLineNumbers, new Identifier(AccessModifier.Public, Common.UpgradeDetectedProperty), false, true, false); // Add launch condition that blocks upgrades if (blockUpgrades) { var conditionTuple = new LaunchConditionTuple(sourceLineNumbers) { Condition = Common.UpgradePreventedCondition, Description = downgradeErrorMessage }; this.Core.AddTuple(conditionTuple); } // now create the Upgrade row and launch conditions to prevent downgrades (unless explicitly permitted) if (!allowDowngrades) { var upgradeTuple = new UpgradeTuple(sourceLineNumbers) { UpgradeCode = upgradeCode, VersionMin = productVersion, Language = productLanguage, OnlyDetect = true, IgnoreRemoveFailures = ignoreRemoveFailure, ActionProperty = Common.DowngradeDetectedProperty }; this.Core.AddTuple(upgradeTuple); // Ensure the action property is secure. this.AddWixPropertyRow(sourceLineNumbers, new Identifier(AccessModifier.Public, Common.DowngradeDetectedProperty), false, true, false); var conditionTuple = new LaunchConditionTuple(sourceLineNumbers) { Condition = Common.DowngradePreventedCondition, Description = downgradeErrorMessage }; this.Core.AddTuple(conditionTuple); } // finally, schedule RemoveExistingProducts string after = null; switch (schedule) { case null: case "afterInstallValidate": after = "InstallValidate"; break; case "afterInstallInitialize": after = "InstallInitialize"; break; case "afterInstallExecute": after = "InstallExecute"; break; case "afterInstallExecuteAgain": after = "InstallExecuteAgain"; break; case "afterInstallFinalize": after = "InstallFinalize"; break; } this.Core.ScheduleActionTuple(sourceLineNumbers, AccessModifier.Public, SequenceTable.InstallExecuteSequence, "RemoveExistingProducts", afterAction: after); } } /// /// Parses a media element. /// /// Element to parse. /// Set to the PatchId if parsing Patch/Media element otherwise null. private void ParseMediaElement(XElement node, string patchId) { var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node); var id = CompilerConstants.IntegerNotSet; string cabinet = null; CompressionLevel? compressionLevel = null; string diskPrompt = null; string layout = null; var patch = null != patchId; string volumeLabel = null; string source = null; string symbols = null; var embedCab = patch ? YesNoType.Yes : 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": id = this.Core.GetAttributeIntegerValue(sourceLineNumbers, attrib, 1, Int16.MaxValue); break; case "Cabinet": cabinet = this.Core.GetAttributeValue(sourceLineNumbers, attrib); break; case "CompressionLevel": compressionLevel = this.ParseCompressionLevel(sourceLineNumbers, node, attrib); break; case "DiskPrompt": diskPrompt = this.Core.GetAttributeValue(sourceLineNumbers, attrib); this.Core.CreateSimpleReference(sourceLineNumbers, "Property", "DiskPrompt"); // ensure the output has a DiskPrompt Property defined break; case "EmbedCab": embedCab = this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib); break; case "Layout": layout = this.Core.GetAttributeValue(sourceLineNumbers, attrib); break; case "VolumeLabel": volumeLabel = this.Core.GetAttributeValue(sourceLineNumbers, attrib); break; case "Source": source = this.Core.GetAttributeValue(sourceLineNumbers, attrib); break; default: this.Core.UnexpectedAttribute(node, attrib); break; } } else { this.Core.ParseExtensionAttribute(node, attrib); } } if (CompilerConstants.IntegerNotSet == id) { this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Id")); id = CompilerConstants.IllegalInteger; } if (YesNoType.IllegalValue != embedCab) { if (YesNoType.Yes == embedCab) { if (null == cabinet) { this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Cabinet", "EmbedCab", "yes")); } else { if (62 < cabinet.Length) { this.Core.Write(ErrorMessages.MediaEmbeddedCabinetNameTooLong(sourceLineNumbers, node.Name.LocalName, "Cabinet", cabinet, cabinet.Length)); } cabinet = String.Concat("#", cabinet); } } else // external cabinet file { // external cabinet files must use 8.3 filenames if (!String.IsNullOrEmpty(cabinet) && !this.Core.IsValidShortFilename(cabinet, false)) { // WiX variables in the name will trip the "not a valid 8.3 name" switch, so let them through if (!Common.WixVariableRegex.Match(cabinet).Success) { this.Core.Write(WarningMessages.MediaExternalCabinetFilenameIllegal(sourceLineNumbers, node.Name.LocalName, "Cabinet", cabinet)); } } } } if (!compressionLevel.HasValue && null == cabinet) { this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Cabinet", "CompressionLevel")); } if (patch) { // Default Source to a form of the Patch Id if none is specified. if (null == source) { source = String.Concat("_", new Guid(patchId).ToString("N", CultureInfo.InvariantCulture).ToUpper(CultureInfo.InvariantCulture)); } } foreach (var child in node.Elements()) { var childSourceLineNumbers = Preprocessor.GetSourceLineNumbers(child); if (CompilerCore.WixNamespace == child.Name.Namespace) { switch (child.Name.LocalName) { case "DigitalSignature": if (YesNoType.Yes == embedCab) { this.Core.Write(ErrorMessages.SignedEmbeddedCabinet(childSourceLineNumbers)); } else if (null == cabinet) { this.Core.Write(ErrorMessages.ExpectedSignedCabinetName(childSourceLineNumbers)); } else { this.ParseDigitalSignatureElement(child, id.ToString(CultureInfo.InvariantCulture.NumberFormat)); } break; case "PatchBaseline": if (patch) { this.ParsePatchBaselineElement(child, id); } else { this.Core.UnexpectedElement(node, child); } break; case "SymbolPath": if (null != symbols) { symbols += "" + this.ParseSymbolPathElement(child); } else { symbols = this.ParseSymbolPathElement(child); } break; default: this.Core.UnexpectedElement(node, child); break; } } else { this.Core.ParseExtensionElement(node, child); } } // add the row to the section if (!this.Core.EncounteredError) { var tuple = new MediaTuple(sourceLineNumbers, new Identifier(AccessModifier.Public, id)) { DiskId = id, DiskPrompt = diskPrompt, Cabinet = cabinet, VolumeLabel = volumeLabel, Source = source, // the Source column is only set when creating a patch CompressionLevel = compressionLevel, Layout = layout }; this.Core.AddTuple(tuple); if (null != symbols) { this.Core.AddTuple(new WixDeltaPatchSymbolPathsTuple(sourceLineNumbers, new Identifier(AccessModifier.Private, SymbolPathType.Media, id)) { SymbolType = SymbolPathType.Media, SymbolId = id.ToString(CultureInfo.InvariantCulture), SymbolPaths = symbols }); } } } /// /// Parses a media template element. /// /// Element to parse. /// Set to the PatchId if parsing Patch/Media element otherwise null. private void ParseMediaTemplateElement(XElement node, string patchId) { var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node); var cabinetTemplate = "cab{0}.cab"; string diskPrompt = null; var patch = null != patchId; string volumeLabel = null; int? maximumUncompressedMediaSize = null; int? maximumCabinetSizeForLargeFileSplitting = null; CompressionLevel? compressionLevel = null; // this defaults to mszip in Binder var embedCab = patch ? YesNoType.Yes : YesNoType.NotSet; foreach (var attrib in node.Attributes()) { if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace) { switch (attrib.Name.LocalName) { case "CabinetTemplate": var authoredCabinetTemplateValue = this.Core.GetAttributeValue(sourceLineNumbers, attrib, EmptyRule.CanBeEmpty); if (!String.IsNullOrEmpty(authoredCabinetTemplateValue)) { cabinetTemplate = authoredCabinetTemplateValue; } // Create an example cabinet name using the maximum number of cabinets supported, 999. var exampleCabinetName = String.Format(cabinetTemplate, "###"); if (!this.Core.IsValidLocIdentifier(exampleCabinetName)) { // The example name should not match the authored template since that would nullify the // reason for having multiple cabients. External cabinet files must also be valid file names. if (exampleCabinetName.Equals(authoredCabinetTemplateValue) || !this.Core.IsValidLongFilename(exampleCabinetName, false)) { this.Core.Write(ErrorMessages.InvalidCabinetTemplate(sourceLineNumbers, cabinetTemplate)); } else if (!this.Core.IsValidShortFilename(exampleCabinetName, false) && !Common.WixVariableRegex.Match(exampleCabinetName).Success) // ignore short names with wix variables because it rarely works out. { this.Core.Write(WarningMessages.MediaExternalCabinetFilenameIllegal(sourceLineNumbers, node.Name.LocalName, "CabinetTemplate", cabinetTemplate)); } } break; case "CompressionLevel": compressionLevel = this.ParseCompressionLevel(sourceLineNumbers, node, attrib); break; case "DiskPrompt": diskPrompt = this.Core.GetAttributeValue(sourceLineNumbers, attrib); this.Core.CreateSimpleReference(sourceLineNumbers, "Property", "DiskPrompt"); // ensure the output has a DiskPrompt Property defined this.Core.Write(WarningMessages.ReservedAttribute(sourceLineNumbers, node.Name.LocalName, attrib.Name.LocalName)); break; case "EmbedCab": embedCab = this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib); break; case "VolumeLabel": volumeLabel = this.Core.GetAttributeValue(sourceLineNumbers, attrib); this.Core.Write(WarningMessages.ReservedAttribute(sourceLineNumbers, node.Name.LocalName, attrib.Name.LocalName)); break; case "MaximumUncompressedMediaSize": maximumUncompressedMediaSize = this.Core.GetAttributeIntegerValue(sourceLineNumbers, attrib, 1, Int32.MaxValue); break; case "MaximumCabinetSizeForLargeFileSplitting": maximumCabinetSizeForLargeFileSplitting = this.Core.GetAttributeIntegerValue(sourceLineNumbers, attrib, Compiler.MinValueOfMaxCabSizeForLargeFileSplitting, Compiler.MaxValueOfMaxCabSizeForLargeFileSplitting); break; default: this.Core.UnexpectedAttribute(node, attrib); break; } } else { this.Core.ParseExtensionAttribute(node, attrib); } } if (YesNoType.Yes == embedCab) { cabinetTemplate = String.Concat("#", cabinetTemplate); } if (!this.Core.EncounteredError) { this.Core.AddTuple(new MediaTuple(sourceLineNumbers, new Identifier(AccessModifier.Public, 1)) { DiskId = 1 }); var tuple = new WixMediaTemplateTuple(sourceLineNumbers) { CabinetTemplate = cabinetTemplate, VolumeLabel = volumeLabel, DiskPrompt = diskPrompt, MaximumUncompressedMediaSize = maximumUncompressedMediaSize, MaximumCabinetSizeForLargeFileSplitting = maximumCabinetSizeForLargeFileSplitting, CompressionLevel = compressionLevel }; //else //{ // mediaTemplateRow.MaximumUncompressedMediaSize = CompilerCore.DefaultMaximumUncompressedMediaSize; //} //else //{ // mediaTemplateRow.MaximumCabinetSizeForLargeFileSplitting = 0; // Default value of 0 corresponds to max size of 2048 MB (i.e. 2 GB) //} this.Core.AddTuple(tuple); } } /// /// Parses a merge element. /// /// Element to parse. /// Identifier for parent directory. /// Disk id inherited from parent directory. private void ParseMergeElement(XElement node, string directoryId, int diskId) { var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node); Identifier id = null; var configData = String.Empty; FileTupleAttributes attributes = 0; string language = 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 "Id": id = this.Core.GetAttributeIdentifier(sourceLineNumbers, attrib); break; case "DiskId": diskId = this.Core.GetAttributeIntegerValue(sourceLineNumbers, attrib, 1, Int16.MaxValue); this.Core.CreateSimpleReference(sourceLineNumbers, "Media", diskId.ToString(CultureInfo.InvariantCulture.NumberFormat)); break; case "FileCompression": var compress = this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib); attributes |= compress == YesNoType.Yes ? FileTupleAttributes.Compressed : 0; attributes |= compress == YesNoType.No ? FileTupleAttributes.Uncompressed : 0; break; case "Language": language = this.Core.GetAttributeLocalizableIntegerValue(sourceLineNumbers, attrib, 0, Int16.MaxValue); 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 == id) { this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Id")); } if (null == language) { this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Language")); } if (null == sourceFile) { this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "SourceFile")); } if (CompilerConstants.IntegerNotSet == diskId) { this.Core.Write(ErrorMessages.ExpectedAttributeInElementOrParent(sourceLineNumbers, node.Name.LocalName, "DiskId", "Directory")); diskId = CompilerConstants.IllegalInteger; } foreach (var child in node.Elements()) { if (CompilerCore.WixNamespace == child.Name.Namespace) { switch (child.Name.LocalName) { case "ConfigurationData": if (0 == configData.Length) { configData = this.ParseConfigurationDataElement(child); } else { configData = String.Concat(configData, ",", this.ParseConfigurationDataElement(child)); } break; default: this.Core.UnexpectedElement(node, child); break; } } else { this.Core.ParseExtensionElement(node, child); } } if (!this.Core.EncounteredError) { var tuple = new WixMergeTuple(sourceLineNumbers, id) { DirectoryRef = directoryId, SourceFile = sourceFile, DiskId = diskId, ConfigurationData = configData, FileAttributes = attributes, FeatureRef = Guid.Empty.ToString("B") }; tuple.Set((int)WixMergeTupleFields.Language, language); this.Core.AddTuple(tuple); } } /// /// Parses a configuration data element. /// /// Element to parse. /// String in format "name=value" with '%', ',' and '=' hex encoded. private string ParseConfigurationDataElement(XElement node) { var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node); string name = 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 "Name": name = 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); } } if (null == name) { this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Name")); } else // need to hex encode these characters { name = name.Replace("%", "%25"); name = name.Replace("=", "%3D"); name = name.Replace(",", "%2C"); } if (null == value) { this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Value")); } else // need to hex encode these characters { value = value.Replace("%", "%25"); value = value.Replace("=", "%3D"); value = value.Replace(",", "%2C"); } this.Core.ParseForExtensionElements(node); return String.Concat(name, "=", value); } /// /// Parses a merge reference element. /// /// Element to parse. /// Parents complex reference type. /// Identifier for parent feature or feature group. private void ParseMergeRefElement(XElement node, ComplexReferenceParentType parentType, string parentId) { var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node); string id = null; var primary = 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": id = this.Core.GetAttributeIdentifierValue(sourceLineNumbers, attrib); this.Core.CreateSimpleReference(sourceLineNumbers, "WixMerge", id); break; case "Primary": primary = this.Core.GetAttributeYesNoValue(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); this.Core.CreateComplexReference(sourceLineNumbers, parentType, parentId, null, ComplexReferenceChildType.Module, id, (YesNoType.Yes == primary)); } /// /// Parses a mime element. /// /// Element to parse. /// Identifier for parent extension. /// Identifier for parent component. /// Flag if the parent element is advertised. /// Content type if this is the default for the MIME type. private string ParseMIMEElement(XElement node, string extension, string componentId, YesNoType parentAdvertised) { var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node); string classId = null; string contentType = null; var advertise = parentAdvertised; var returnContentType = YesNoType.NotSet; foreach (var attrib in node.Attributes()) { if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace) { switch (attrib.Name.LocalName) { case "Advertise": advertise = this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib); break; case "Class": classId = this.Core.GetAttributeGuidValue(sourceLineNumbers, attrib, false); break; case "ContentType": contentType = this.Core.GetAttributeValue(sourceLineNumbers, attrib); break; case "Default": returnContentType = this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib); break; default: this.Core.UnexpectedAttribute(node, attrib); break; } } else { this.Core.ParseExtensionAttribute(node, attrib); } } if (null == contentType) { this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "ContentType")); } // if the advertise state has not been set, default to non-advertised if (YesNoType.NotSet == advertise) { advertise = YesNoType.No; } this.Core.ParseForExtensionElements(node); if (YesNoType.Yes == advertise) { if (YesNoType.Yes != parentAdvertised) { this.Core.Write(ErrorMessages.AdvertiseStateMustMatch(sourceLineNumbers, advertise.ToString(), parentAdvertised.ToString())); } if (!this.Core.EncounteredError) { var tuple = new MIMETuple(sourceLineNumbers, new Identifier(AccessModifier.Private, contentType)) { ContentType = contentType, ExtensionRef = extension, CLSID = classId }; this.Core.AddTuple(tuple); } } else if (YesNoType.No == advertise) { if (YesNoType.Yes == returnContentType && YesNoType.Yes == parentAdvertised) { this.Core.Write(ErrorMessages.CannotDefaultMismatchedAdvertiseStates(sourceLineNumbers)); } this.Core.CreateRegistryRow(sourceLineNumbers, RegistryRootType.ClassesRoot, String.Concat("MIME\\Database\\Content Type\\", contentType), "Extension", String.Concat(".", extension), componentId); if (null != classId) { this.Core.CreateRegistryRow(sourceLineNumbers, RegistryRootType.ClassesRoot, String.Concat("MIME\\Database\\Content Type\\", contentType), "CLSID", classId, componentId); } } return YesNoType.Yes == returnContentType ? contentType : null; } /// /// Parses a patch property element. /// /// The element to parse. /// True if parsing an patch element. private void ParsePatchPropertyElement(XElement node, bool patch) { var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node); string name = null; string company = 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": case "Name": name = this.Core.GetAttributeValue(sourceLineNumbers, attrib); break; case "Company": company = 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); } } if (null == name) { this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Name")); } if (null == value) { this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Value")); } this.Core.ParseForExtensionElements(node); if (patch) { // /Patch/PatchProperty goes directly into MsiPatchMetadata table this.Core.AddTuple(new MsiPatchMetadataTuple(sourceLineNumbers) { Company = company, Property = name, Value = value }); } else { if (null != company) { this.Core.Write(ErrorMessages.UnexpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Company")); } this.AddPrivateProperty(sourceLineNumbers, name, value); } } /// /// Adds a row to the properties table. /// /// Source line numbers. /// Name of the property. /// Value of the property. private void AddPrivateProperty(SourceLineNumber sourceLineNumbers, string name, string value) { if (!this.Core.EncounteredError) { var tuple = new PropertyTuple(sourceLineNumbers, new Identifier(AccessModifier.Private, name)) { Value = value }; this.Core.AddTuple(tuple); } } /// /// Parses a TargetProductCode element. /// /// The element to parse. /// The id from the node. private string ParseTargetProductCodeElement(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.GetAttributeValue(sourceLineNumbers, attrib); if (id.Length > 0 && "*" != 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")); } this.Core.ParseForExtensionElements(node); return id; } /// /// Parses a ReplacePatch element. /// /// The element to parse. /// The id from the node. private string ParseReplacePatchElement(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")); } this.Core.ParseForExtensionElements(node); return id; } /// /// Parses a symbol path element. /// /// The element to parse. /// The path from the node. private string ParseSymbolPathElement(XElement node) { var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node); string path = null; foreach (var attrib in node.Attributes()) { if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace) { switch (attrib.Name.LocalName) { case "Path": path = this.Core.GetAttributeValue(sourceLineNumbers, attrib); break; default: this.Core.UnexpectedAttribute(node, attrib); break; } } else { this.Core.ParseExtensionAttribute(node, attrib); } } if (null == path) { this.Core.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Path")); } this.Core.ParseForExtensionElements(node); return path; } /// /// Parses the All element under a PatchFamily. /// /// The element to parse. private void ParseAllElement(XElement node) { var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node); // find unexpected attributes foreach (var attrib in node.Attributes()) { if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace) { this.Core.UnexpectedAttribute(node, attrib); } else { this.Core.ParseExtensionAttribute(node, attrib); } } this.Core.ParseForExtensionElements(node); // Always warn when using the All element. this.Core.Write(WarningMessages.AllChangesIncludedInPatch(sourceLineNumbers)); if (!this.Core.EncounteredError) { var tuple = new WixPatchRefTuple(sourceLineNumbers) { Table = "*", PrimaryKeys = "*" }; this.Core.AddTuple(tuple); } } /// /// Parses all reference elements under a PatchFamily. /// /// The element to parse. /// Table that reference was made to. private void ParsePatchChildRefElement(XElement node, string tableName) { 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) { var tuple = new WixPatchRefTuple(sourceLineNumbers) { Table = tableName, PrimaryKeys = id }; this.Core.AddTuple(tuple); } } /// /// Parses a PatchBaseline element. /// /// The element to parse. /// Media index from parent element. private void ParsePatchBaselineElement(XElement node, int diskId) { var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node); Identifier id = null; var parsedValidate = false; var validationFlags = TransformFlags.PatchTransformDefault; 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; 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 (27 < id.Id.Length) { this.Core.Write(ErrorMessages.IdentifierTooLongError(sourceLineNumbers, node.Name.LocalName, "Id", id.Id, 27)); } foreach (var child in node.Elements()) { if (CompilerCore.WixNamespace == child.Name.Namespace) { switch (child.Name.LocalName) { case "Validate": if (parsedValidate) { var childSourceLineNumbers = Preprocessor.GetSourceLineNumbers(child); this.Core.Write(ErrorMessages.TooManyChildren(childSourceLineNumbers, node.Name.LocalName, child.Name.LocalName)); } else { this.ParseValidateElement(child, ref validationFlags); parsedValidate = true; } break; default: this.Core.UnexpectedElement(node, child); break; } } else { this.Core.ParseExtensionElement(node, child); } } if (!this.Core.EncounteredError) { var tuple = new WixPatchBaselineTuple(sourceLineNumbers, id) { DiskId = diskId, ValidationFlags = validationFlags }; this.Core.AddTuple(tuple); } } /// /// Parses a Validate element. /// /// The element to parse. /// TransformValidation flags to use when creating the authoring patch transform. private void ParseValidateElement(XElement node, ref TransformFlags validationFlags) { var sourceLineNumbers = Preprocessor.GetSourceLineNumbers(node); foreach (var attrib in node.Attributes()) { if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || CompilerCore.WixNamespace == attrib.Name.Namespace) { switch (attrib.Name.LocalName) { case "ProductId": if (YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib)) { validationFlags |= TransformFlags.ValidateProduct; } else { validationFlags &= ~TransformFlags.ValidateProduct; } break; case "ProductLanguage": if (YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib)) { validationFlags |= TransformFlags.ValidateLanguage; } else { validationFlags &= ~TransformFlags.ValidateLanguage; } break; case "ProductVersion": var check = this.Core.GetAttributeValue(sourceLineNumbers, attrib); validationFlags &= ~TransformFlags.ProductVersionMask; switch (check) { case "Major": case "major": validationFlags |= TransformFlags.ValidateMajorVersion; break; case "Minor": case "minor": validationFlags |= TransformFlags.ValidateMinorVersion; break; case "Update": case "update": validationFlags |= TransformFlags.ValidateUpdateVersion; break; case "": break; default: this.Core.Write(ErrorMessages.IllegalAttributeValue(sourceLineNumbers, node.Name.LocalName, "Version", check, "Major", "Minor", "Update")); break; } break; case "ProductVersionOperator": var op = this.Core.GetAttributeValue(sourceLineNumbers, attrib); validationFlags &= ~TransformFlags.ProductVersionOperatorMask; switch (op) { case "Lesser": case "lesser": validationFlags |= TransformFlags.ValidateNewLessBaseVersion; break; case "LesserOrEqual": case "lesserOrEqual": validationFlags |= TransformFlags.ValidateNewLessEqualBaseVersion; break; case "Equal": case "equal": validationFlags |= TransformFlags.ValidateNewEqualBaseVersion; break; case "GreaterOrEqual": case "greaterOrEqual": validationFlags |= TransformFlags.ValidateNewGreaterEqualBaseVersion; break; case "Greater": case "greater": validationFlags |= TransformFlags.ValidateNewGreaterBaseVersion; break; case "": break; default: this.Core.Write(ErrorMessages.IllegalAttributeValue(sourceLineNumbers, node.Name.LocalName, "Operator", op, "Lesser", "LesserOrEqual", "Equal", "GreaterOrEqual", "Greater")); break; } break; case "UpgradeCode": if (YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib)) { validationFlags |= TransformFlags.ValidateUpgradeCode; } else { validationFlags &= ~TransformFlags.ValidateUpgradeCode; } break; case "IgnoreAddExistingRow": if (YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib)) { validationFlags |= TransformFlags.ErrorAddExistingRow; } else { validationFlags &= ~TransformFlags.ErrorAddExistingRow; } break; case "IgnoreAddExistingTable": if (YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib)) { validationFlags |= TransformFlags.ErrorAddExistingTable; } else { validationFlags &= ~TransformFlags.ErrorAddExistingTable; } break; case "IgnoreDeleteMissingRow": if (YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib)) { validationFlags |= TransformFlags.ErrorDeleteMissingRow; } else { validationFlags &= ~TransformFlags.ErrorDeleteMissingRow; } break; case "IgnoreDeleteMissingTable": if (YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib)) { validationFlags |= TransformFlags.ErrorDeleteMissingTable; } else { validationFlags &= ~TransformFlags.ErrorDeleteMissingTable; } break; case "IgnoreUpdateMissingRow": if (YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib)) { validationFlags |= TransformFlags.ErrorUpdateMissingRow; } else { validationFlags &= ~TransformFlags.ErrorUpdateMissingRow; } break; case "IgnoreChangingCodePage": if (YesNoType.Yes == this.Core.GetAttributeYesNoValue(sourceLineNumbers, attrib)) { validationFlags |= TransformFlags.ErrorChangeCodePage; } else { validationFlags &= ~TransformFlags.ErrorChangeCodePage; } break; default: this.Core.UnexpectedAttribute(node, attrib); break; } } else { this.Core.ParseExtensionAttribute(node, attrib); } } } } }