// 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.Util { using System; using System.Collections; using System.Collections.Generic; using System.Globalization; using System.Linq; using System.Text; using System.Text.RegularExpressions; using System.Xml.Linq; using WixToolset.Data; using WixToolset.Data.Symbols; using WixToolset.Extensibility; using WixToolset.Extensibility.Data; using WixToolset.Util.Symbols; /// /// The compiler for the WiX Toolset Utility Extension. /// internal sealed class UtilCompiler : BaseCompilerExtension { // user creation attributes definitions (from sca.h) internal const int UserDontExpirePasswrd = 0x00000001; internal const int UserPasswdCantChange = 0x00000002; internal const int UserPasswdChangeReqdOnLogin = 0x00000004; internal const int UserDisableAccount = 0x00000008; internal const int UserFailIfExists = 0x00000010; internal const int UserUpdateIfExists = 0x00000020; internal const int UserLogonAsService = 0x00000040; internal const int UserLogonAsBatchJob = 0x00000080; internal const int UserDontRemoveOnUninstall = 0x00000100; internal const int UserDontCreateUser = 0x00000200; internal const int UserNonVital = 0x00000400; internal const int UserRemoveComment = 0x00000800; private static readonly Regex FindPropertyBrackets = new Regex(@"\[(?!\\|\])|(? UtilConstants.Namespace; /// /// Types of Internet shortcuts. /// public enum InternetShortcutType { /// Create a .lnk file. Link = 0, /// Create a .url file. Url, } /// /// Types of permission setting methods. /// private enum PermissionType { /// LockPermissions (normal) type permission setting. LockPermissions, /// FileSharePermissions type permission setting. FileSharePermissions, /// SecureObjects type permission setting. SecureObjects, } /// /// Processes an element for the Compiler. /// /// Parent element of element to process. /// Element to process. /// Extra information about the context in which this element is being parsed. public override void ParseElement(Intermediate intermediate, IntermediateSection section, XElement parentElement, XElement element, IDictionary context) { this.ParsePossibleKeyPathElement(intermediate, section, parentElement, element, context); } /// /// Processes an element for the Compiler. /// /// Source line number for the parent element. /// Parent element of element to process. /// Element to process. /// Extra information about the context in which this element is being parsed. public override IComponentKeyPath ParsePossibleKeyPathElement(Intermediate intermediate, IntermediateSection section, XElement parentElement, XElement element, IDictionary context) { IComponentKeyPath possibleKeyPath = null; switch (parentElement.Name.LocalName) { case "CreateFolder": var createFolderId = context["DirectoryId"]; var createFolderComponentId = context["ComponentId"]; switch (element.Name.LocalName) { case "PermissionEx": this.ParsePermissionExElement(intermediate, section, element, createFolderId, createFolderComponentId, "CreateFolder"); break; default: this.ParseHelper.UnexpectedElement(parentElement, element); break; } break; case "Component": var componentId = context["ComponentId"]; var directoryId = context["DirectoryId"]; var componentWin64 = Boolean.Parse(context["Win64"]); switch (element.Name.LocalName) { case "EventSource": possibleKeyPath = this.ParseEventSourceElement(intermediate, section, element, componentId); break; case "FileShare": this.ParseFileShareElement(intermediate, section, element, componentId, directoryId); break; case "InternetShortcut": this.ParseInternetShortcutElement(intermediate, section, element, componentId, directoryId); break; case "PerformanceCategory": this.ParsePerformanceCategoryElement(intermediate, section, element, componentId); break; case "RemoveFolderEx": this.ParseRemoveFolderExElement(intermediate, section, element, componentId); break; case "RemoveRegistryKey": this.ParseRemoveRegistryKeyExElement(intermediate, section, element, componentId); break; case "RestartResource": this.ParseRestartResourceElement(intermediate, section, element, componentId); break; case "ServiceConfig": this.ParseServiceConfigElement(intermediate, section, element, componentId, "Component", null); break; case "TouchFile": this.ParseTouchFileElement(intermediate, section, element, componentId, componentWin64); break; case "Group": this.ParseGroupElement(intermediate, section, element, componentId); break; case "User": this.ParseUserElement(intermediate, section, element, componentId); break; case "XmlFile": this.ParseXmlFileElement(intermediate, section, element, componentId); break; case "XmlConfig": this.ParseXmlConfigElement(intermediate, section, element, componentId, false); break; default: this.ParseHelper.UnexpectedElement(parentElement, element); break; } break; case "File": var fileId = context["FileId"]; var fileComponentId = context["ComponentId"]; switch (element.Name.LocalName) { case "PerfCounter": this.ParsePerfCounterElement(intermediate, section, element, fileComponentId, fileId); break; case "PermissionEx": this.ParsePermissionExElement(intermediate, section, element, fileId, fileComponentId, "File"); break; case "PerfCounterManifest": this.ParsePerfCounterManifestElement(intermediate, section, element, fileComponentId, fileId); break; case "EventManifest": this.ParseEventManifestElement(intermediate, section, element, fileComponentId, fileId); break; case "FormatFile": this.ParseFormatFileElement(intermediate, section, element, fileId); break; default: this.ParseHelper.UnexpectedElement(parentElement, element); break; } break; case "Bundle": case "Fragment": case "Module": case "Package": switch (element.Name.LocalName) { case "CloseApplication": this.ParseCloseApplicationElement(intermediate, section, element); break; case "Group": this.ParseGroupElement(intermediate, section, element, null); break; case "RestartResource": // Currently not supported for Bundles. if (parentElement.Name.LocalName != "Bundle") { this.ParseRestartResourceElement(intermediate, section, element, null); } else { this.ParseHelper.UnexpectedElement(parentElement, element); } break; case "User": this.ParseUserElement(intermediate, section, element, null); break; case "BroadcastEnvironmentChange": case "BroadcastSettingChange": case "CheckRebootRequired": case "ExitEarlyWithSuccess": case "FailWhenDeferred": case "QueryNativeMachine": case "QueryWindowsDirectories": case "QueryWindowsDriverInfo": case "QueryWindowsSuiteInfo": case "QueryWindowsWellKnownSIDs": case "WaitForEvent": case "WaitForEventDeferred": this.AddCustomActionReference(intermediate, section, element, parentElement); break; case "ComponentSearch": case "ComponentSearchRef": case "DirectorySearch": case "DirectorySearchRef": case "FileSearch": case "FileSearchRef": case "ProductSearch": case "ProductSearchRef": case "RegistrySearch": case "RegistrySearchRef": case "WindowsFeatureSearch": case "WindowsFeatureSearchRef": // These will eventually be supported under Module/Product, but are not yet. if (parentElement.Name.LocalName == "Bundle" || parentElement.Name.LocalName == "Fragment") { // TODO: When these are supported by all section types, move // these out of the nested switch and back into the surrounding one. switch (element.Name.LocalName) { case "ComponentSearch": this.ParseComponentSearchElement(intermediate, section, element); break; case "ComponentSearchRef": this.ParseComponentSearchRefElement(intermediate, section, element); break; case "DirectorySearch": this.ParseDirectorySearchElement(intermediate, section, element); break; case "DirectorySearchRef": this.ParseWixSearchRefElement(intermediate, section, element); break; case "FileSearch": this.ParseFileSearchElement(intermediate, section, element); break; case "FileSearchRef": this.ParseWixSearchRefElement(intermediate, section, element); break; case "ProductSearch": this.ParseProductSearchElement(intermediate, section, element); break; case "ProductSearchRef": this.ParseWixSearchRefElement(intermediate, section, element); break; case "RegistrySearch": this.ParseRegistrySearchElement(intermediate, section, element); break; case "RegistrySearchRef": this.ParseWixSearchRefElement(intermediate, section, element); break; case "WindowsFeatureSearch": this.ParseWindowsFeatureSearchElement(intermediate, section, element); break; case "WindowsFeatureSearchRef": this.ParseWindowsFeatureSearchRefElement(intermediate, section, element); break; } } else { this.ParseHelper.UnexpectedElement(parentElement, element); } break; default: this.ParseHelper.UnexpectedElement(parentElement, element); break; } break; case "Registry": case "RegistryKey": case "RegistryValue": var registryId = context["RegistryId"]; var registryComponentId = context["ComponentId"]; switch (element.Name.LocalName) { case "PermissionEx": this.ParsePermissionExElement(intermediate, section, element, registryId, registryComponentId, "Registry"); break; default: this.ParseHelper.UnexpectedElement(parentElement, element); break; } break; case "ServiceInstall": var serviceInstallId = context["ServiceInstallId"]; var serviceInstallName = context["ServiceInstallName"]; var serviceInstallComponentId = context["ServiceInstallComponentId"]; switch (element.Name.LocalName) { case "PermissionEx": this.ParsePermissionExElement(intermediate, section, element, serviceInstallId, serviceInstallComponentId, "ServiceInstall"); break; case "ServiceConfig": this.ParseServiceConfigElement(intermediate, section, element, serviceInstallComponentId, "ServiceInstall", serviceInstallName); break; default: this.ParseHelper.UnexpectedElement(parentElement, element); break; } break; case "UI": switch (element.Name.LocalName) { case "BroadcastEnvironmentChange": case "BroadcastSettingChange": case "CheckRebootRequired": case "ExitEarlyWithSuccess": case "FailWhenDeferred": case "QueryWindowsDirectories": case "QueryWindowsDriverInfo": case "QueryWindowsSuiteInfo": case "QueryWindowsWellKnownSIDs": case "WaitForEvent": case "WaitForEventDeferred": this.AddCustomActionReference(intermediate, section, element, parentElement); break; } break; default: this.ParseHelper.UnexpectedElement(parentElement, element); break; } return possibleKeyPath; } private void AddCustomActionReference(Intermediate intermediate, IntermediateSection section, XElement element, XElement parentElement) { // These elements are not supported for bundles. if (parentElement.Name.LocalName == "Bundle") { this.ParseHelper.UnexpectedElement(parentElement, element); return; } var customAction = element.Name.LocalName; switch (element.Name.LocalName) { case "BroadcastEnvironmentChange": case "BroadcastSettingChange": case "CheckRebootRequired": case "ExitEarlyWithSuccess": case "FailWhenDeferred": case "WaitForEvent": case "WaitForEventDeferred": //default: customAction = element.Name.LocalName; break; case "QueryWindowsDirectories": customAction = "QueryOsDirs"; break; case "QueryWindowsDriverInfo": customAction = "QueryOsDriverInfo"; break; case "QueryWindowsSuiteInfo": customAction = "QueryOsInfo"; break; case "QueryWindowsWellKnownSIDs": customAction = "QueryOsWellKnownSID"; break; } foreach (var attrib in element.Attributes()) { if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || this.Namespace == attrib.Name.Namespace) { // no attributes today } else { this.ParseHelper.ParseExtensionAttribute(this.Context.Extensions, intermediate, section, element, attrib); } } var sourceLineNumbers = this.ParseHelper.GetSourceLineNumbers(element); this.ParseHelper.ParseForExtensionElements(this.Context.Extensions, intermediate, section, element); this.ParseHelper.CreateCustomActionReference(sourceLineNumbers, section, "Wix4" + customAction, this.Context.Platform, CustomActionPlatforms.X86 | CustomActionPlatforms.X64 | CustomActionPlatforms.ARM64); } /// /// Parses the common search attributes shared across all searches. /// /// Source line number for the parent element. /// Attribute to parse. /// Value of the Id attribute. /// Value of the Variable attribute. /// Value of the Condition attribute. /// Value of the After attribute. private void ParseCommonSearchAttributes(SourceLineNumber sourceLineNumbers, XAttribute attrib, ref Identifier id, ref string variable, ref string condition, ref string after) { switch (attrib.Name.LocalName) { case "Id": id = this.ParseHelper.GetAttributeIdentifier(sourceLineNumbers, attrib); break; case "Variable": variable = this.ParseHelper.GetAttributeBundleVariableNameValue(sourceLineNumbers, attrib); break; case "Condition": condition = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib); break; case "After": after = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib); break; default: System.Diagnostics.Debug.Assert(false); break; } } /// /// Parses a ComponentSearch element. /// /// Element to parse. private void ParseComponentSearchElement(Intermediate intermediate, IntermediateSection section, XElement element) { var sourceLineNumbers = this.ParseHelper.GetSourceLineNumbers(element); Identifier id = null; string variable = null; string condition = null; string after = null; string guid = null; string productCode = null; var attributes = WixComponentSearchAttributes.None; var type = WixComponentSearchType.KeyPath; foreach (var attrib in element.Attributes()) { if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || this.Namespace == attrib.Name.Namespace) { switch (attrib.Name.LocalName) { case "Id": case "Variable": case "Condition": case "After": this.ParseCommonSearchAttributes(sourceLineNumbers, attrib, ref id, ref variable, ref condition, ref after); break; case "Guid": guid = this.ParseHelper.GetAttributeGuidValue(sourceLineNumbers, attrib); break; case "ProductCode": productCode = this.ParseHelper.GetAttributeGuidValue(sourceLineNumbers, attrib); break; case "Result": var result = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib); switch (result) { case "directory": type = WixComponentSearchType.WantDirectory; break; case "keyPath": type = WixComponentSearchType.KeyPath; break; case "state": type = WixComponentSearchType.State; break; default: this.Messaging.Write(ErrorMessages.IllegalAttributeValue(sourceLineNumbers, attrib.Parent.Name.LocalName, attrib.Name.LocalName, result, "directory", "keyPath", "state")); break; } break; default: this.ParseHelper.UnexpectedAttribute(element, attrib); break; } } else { this.ParseHelper.ParseExtensionAttribute(this.Context.Extensions, intermediate, section, element, attrib); } } if (null == guid) { this.Messaging.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, element.Name.LocalName, "Guid")); } if (null == id) { id = this.ParseHelper.CreateIdentifier("wcs", variable, condition, after, guid, productCode, attributes.ToString(), type.ToString()); } this.ParseHelper.ParseForExtensionElements(this.Context.Extensions, intermediate, section, element); if (!this.Messaging.EncounteredError) { this.ParseHelper.CreateWixSearchSymbol(section, sourceLineNumbers, element.Name.LocalName, id, variable, condition, after, null); section.AddSymbol(new WixComponentSearchSymbol(sourceLineNumbers, id) { Guid = guid, ProductCode = productCode, Attributes = attributes, Type = type, }); } } /// /// Parses a ComponentSearchRef element /// /// Element to parse. private void ParseComponentSearchRefElement(Intermediate intermediate, IntermediateSection section, XElement element) { var sourceLineNumbers = this.ParseHelper.GetSourceLineNumbers(element); foreach (var attrib in element.Attributes()) { if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || this.Namespace == attrib.Name.Namespace) { switch (attrib.Name.LocalName) { case "Id": var refId = this.ParseHelper.GetAttributeIdentifierValue(sourceLineNumbers, attrib); this.ParseHelper.CreateSimpleReference(section, sourceLineNumbers, SymbolDefinitions.WixComponentSearch, refId); break; default: this.ParseHelper.UnexpectedAttribute(element, attrib); break; } } else { this.ParseHelper.ParseExtensionAttribute(this.Context.Extensions, intermediate, section, element, attrib); } } this.ParseHelper.ParseForExtensionElements(this.Context.Extensions, intermediate, section, element); } /// /// Parses a WindowsFeatureSearch element. /// /// Element to parse. private void ParseWindowsFeatureSearchElement(Intermediate intermediate, IntermediateSection section, XElement element) { var sourceLineNumbers = this.ParseHelper.GetSourceLineNumbers(element); Identifier id = null; string variable = null; string condition = null; string after = null; string feature = null; foreach (var attrib in element.Attributes()) { if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || this.Namespace == attrib.Name.Namespace) { switch (attrib.Name.LocalName) { case "Id": case "Variable": case "Condition": case "After": this.ParseCommonSearchAttributes(sourceLineNumbers, attrib, ref id, ref variable, ref condition, ref after); break; case "Feature": feature = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib); switch (feature) { case "sha2CodeSigning": break; default: this.Messaging.Write(ErrorMessages.IllegalAttributeValue(sourceLineNumbers, element.Name.LocalName, "Feature", feature, "sha2CodeSigning")); break; } break; default: this.ParseHelper.UnexpectedAttribute(element, attrib); break; } } else { this.ParseHelper.ParseExtensionAttribute(this.Context.Extensions, intermediate, section, element, attrib); } } if (id == null) { id = this.ParseHelper.CreateIdentifier("wwfs", variable, condition, after); } if (feature == null) { this.Messaging.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, element.Name.LocalName, "Feature")); } this.ParseHelper.ParseForExtensionElements(this.Context.Extensions, intermediate, section, element); var bootstrapperExtensionId = this.ParseHelper.CreateIdentifierValueFromPlatform("Wix4UtilBootstrapperExtension", this.Context.Platform, BurnPlatforms.X86 | BurnPlatforms.X64 | BurnPlatforms.ARM64); if (bootstrapperExtensionId == null) { this.Messaging.Write(ErrorMessages.UnsupportedPlatformForElement(sourceLineNumbers, this.Context.Platform.ToString(), element.Name.LocalName)); } if (!this.Messaging.EncounteredError) { this.ParseHelper.CreateWixSearchSymbol(section, sourceLineNumbers, element.Name.LocalName, id, variable, condition, after, bootstrapperExtensionId); section.AddSymbol(new WixWindowsFeatureSearchSymbol(sourceLineNumbers, id) { Type = feature, }); } } /// /// Parses a WindowsFeatureSearchRef element /// /// Element to parse. private void ParseWindowsFeatureSearchRefElement(Intermediate intermediate, IntermediateSection section, XElement element) { var sourceLineNumbers = this.ParseHelper.GetSourceLineNumbers(element); foreach (var attrib in element.Attributes()) { if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || this.Namespace == attrib.Name.Namespace) { switch (attrib.Name.LocalName) { case "Id": var refId = this.ParseHelper.GetAttributeIdentifierValue(sourceLineNumbers, attrib); this.ParseHelper.CreateSimpleReference(section, sourceLineNumbers, UtilSymbolDefinitions.WixWindowsFeatureSearch, refId); break; default: this.ParseHelper.UnexpectedAttribute(element, attrib); break; } } else { this.ParseHelper.ParseExtensionAttribute(this.Context.Extensions, intermediate, section, element, attrib); } } this.ParseHelper.ParseForExtensionElements(this.Context.Extensions, intermediate, section, element); } /// /// Parses an event source element. /// /// Element to parse. /// Identifier of parent component. private IComponentKeyPath ParseEventSourceElement(Intermediate intermediate, IntermediateSection section, XElement element, string componentId) { var sourceLineNumbers = this.ParseHelper.GetSourceLineNumbers(element); string sourceName = null; string logName = null; string categoryMessageFile = null; var categoryCount = CompilerConstants.IntegerNotSet; string eventMessageFile = null; string parameterMessageFile = null; var typesSupported = 0; var isKeyPath = false; foreach (var attrib in element.Attributes()) { if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || this.Namespace == attrib.Name.Namespace) { switch (attrib.Name.LocalName) { case "CategoryCount": categoryCount = this.ParseHelper.GetAttributeIntegerValue(sourceLineNumbers, attrib, 0, Int32.MaxValue); break; case "CategoryMessageFile": categoryMessageFile = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib); break; case "EventMessageFile": eventMessageFile = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib); break; case "KeyPath": isKeyPath = YesNoType.Yes == this.ParseHelper.GetAttributeYesNoValue(sourceLineNumbers, attrib); break; case "Log": logName = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib); if ("Security" == logName) { this.Messaging.Write(ErrorMessages.IllegalAttributeValue(sourceLineNumbers, element.Name.LocalName, attrib.Name.LocalName, logName, "Application", "System", "")); } break; case "Name": sourceName = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib); break; case "ParameterMessageFile": parameterMessageFile = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib); break; case "SupportsErrors": if (YesNoType.Yes == this.ParseHelper.GetAttributeYesNoValue(sourceLineNumbers, attrib)) { typesSupported |= 0x01; // EVENTLOG_ERROR_TYPE } break; case "SupportsFailureAudits": if (YesNoType.Yes == this.ParseHelper.GetAttributeYesNoValue(sourceLineNumbers, attrib)) { typesSupported |= 0x10; // EVENTLOG_AUDIT_FAILURE } break; case "SupportsInformationals": if (YesNoType.Yes == this.ParseHelper.GetAttributeYesNoValue(sourceLineNumbers, attrib)) { typesSupported |= 0x04; // EVENTLOG_INFORMATION_TYPE } break; case "SupportsSuccessAudits": if (YesNoType.Yes == this.ParseHelper.GetAttributeYesNoValue(sourceLineNumbers, attrib)) { typesSupported |= 0x08; // EVENTLOG_AUDIT_SUCCESS } break; case "SupportsWarnings": if (YesNoType.Yes == this.ParseHelper.GetAttributeYesNoValue(sourceLineNumbers, attrib)) { typesSupported |= 0x02; // EVENTLOG_WARNING_TYPE } break; default: this.ParseHelper.UnexpectedAttribute(element, attrib); break; } } else { this.ParseHelper.ParseExtensionAttribute(this.Context.Extensions, intermediate, section, element, attrib); } } if (null == sourceName) { this.Messaging.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, element.Name.LocalName, "Name")); } if (null == logName) { this.Messaging.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, element.Name.LocalName, "EventLog")); } if (null == eventMessageFile) { this.Messaging.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, element.Name.LocalName, "EventMessageFile")); } if (null == categoryMessageFile && 0 < categoryCount) { this.Messaging.Write(ErrorMessages.IllegalAttributeWithoutOtherAttributes(sourceLineNumbers, element.Name.LocalName, "CategoryCount", "CategoryMessageFile")); } if (null != categoryMessageFile && CompilerConstants.IntegerNotSet == categoryCount) { this.Messaging.Write(ErrorMessages.IllegalAttributeWithoutOtherAttributes(sourceLineNumbers, element.Name.LocalName, "CategoryMessageFile", "CategoryCount")); } this.ParseHelper.ParseForExtensionElements(this.Context.Extensions, intermediate, section, element); string eventSourceKey = $@"SYSTEM\CurrentControlSet\Services\EventLog\{logName}\{sourceName}"; var id = this.ParseHelper.CreateRegistrySymbol(section, sourceLineNumbers, RegistryRootType.LocalMachine, eventSourceKey, "EventMessageFile", eventMessageFile, componentId, RegistryValueType.Expandable); if (null != categoryMessageFile) { this.ParseHelper.CreateRegistrySymbol(section, sourceLineNumbers, RegistryRootType.LocalMachine, eventSourceKey, "CategoryMessageFile", categoryMessageFile, componentId, RegistryValueType.Expandable); } if (CompilerConstants.IntegerNotSet != categoryCount) { this.ParseHelper.CreateRegistrySymbol(section, sourceLineNumbers, RegistryRootType.LocalMachine, eventSourceKey, "CategoryCount", categoryCount, componentId); } if (null != parameterMessageFile) { this.ParseHelper.CreateRegistrySymbol(section, sourceLineNumbers, RegistryRootType.LocalMachine, eventSourceKey, "ParameterMessageFile", parameterMessageFile, componentId, RegistryValueType.Expandable); } if (0 != typesSupported) { this.ParseHelper.CreateRegistrySymbol(section, sourceLineNumbers, RegistryRootType.LocalMachine, eventSourceKey, "TypesSupported", typesSupported, componentId); } var componentKeyPath = this.CreateComponentKeyPath(); componentKeyPath.Id = id; componentKeyPath.Explicit = isKeyPath; componentKeyPath.Type = PossibleKeyPathType.Registry; return componentKeyPath; } /// /// Parses a close application element. /// /// Element to parse. private void ParseCloseApplicationElement(Intermediate intermediate, IntermediateSection section, XElement element) { var sourceLineNumbers = this.ParseHelper.GetSourceLineNumbers(element); string condition = null; string description = null; string target = null; string property = null; Identifier id = null; int attributes = 2; // default to CLOSEAPP_ATTRIBUTE_REBOOTPROMPT enabled var sequence = CompilerConstants.IntegerNotSet; var terminateExitCode = CompilerConstants.IntegerNotSet; var timeout = CompilerConstants.IntegerNotSet; foreach (var attrib in element.Attributes()) { if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || this.Namespace == attrib.Name.Namespace) { switch (attrib.Name.LocalName) { case "Id": id = this.ParseHelper.GetAttributeIdentifier(sourceLineNumbers, attrib); break; case "Condition": condition = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib); break; case "Description": description = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib); break; case "Property": property = this.ParseHelper.GetAttributeIdentifierValue(sourceLineNumbers, attrib); break; case "Sequence": sequence = this.ParseHelper.GetAttributeIntegerValue(sourceLineNumbers, attrib, 0, Int32.MaxValue); break; case "Timeout": timeout = this.ParseHelper.GetAttributeIntegerValue(sourceLineNumbers, attrib, 0, Int32.MaxValue); break; case "Target": target = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib); break; case "CloseMessage": if (YesNoType.Yes == this.ParseHelper.GetAttributeYesNoValue(sourceLineNumbers, attrib)) { attributes |= 1; // CLOSEAPP_ATTRIBUTE_CLOSEMESSAGE } else { attributes &= ~1; // CLOSEAPP_ATTRIBUTE_CLOSEMESSAGE } break; case "EndSessionMessage": if (YesNoType.Yes == this.ParseHelper.GetAttributeYesNoValue(sourceLineNumbers, attrib)) { attributes |= 8; // CLOSEAPP_ATTRIBUTE_ENDSESSIONMESSAGE } else { attributes &= ~8; // CLOSEAPP_ATTRIBUTE_ENDSESSIONMESSAGE } break; case "PromptToContinue": if (YesNoType.Yes == this.ParseHelper.GetAttributeYesNoValue(sourceLineNumbers, attrib)) { attributes |= 0x40; // CLOSEAPP_ATTRIBUTE_PROMPTTOCONTINUE } else { attributes &= ~0x40; // CLOSEAPP_ATTRIBUTE_PROMPTTOCONTINUE } break; case "RebootPrompt": if (YesNoType.Yes == this.ParseHelper.GetAttributeYesNoValue(sourceLineNumbers, attrib)) { attributes |= 2; // CLOSEAPP_ATTRIBUTE_REBOOTPROMPT } else { attributes &= ~2; // CLOSEAPP_ATTRIBUTE_REBOOTPROMPT } break; case "ElevatedCloseMessage": if (YesNoType.Yes == this.ParseHelper.GetAttributeYesNoValue(sourceLineNumbers, attrib)) { attributes |= 4; // CLOSEAPP_ATTRIBUTE_ELEVATEDCLOSEMESSAGE } else { attributes &= ~4; // CLOSEAPP_ATTRIBUTE_ELEVATEDCLOSEMESSAGE } break; case "ElevatedEndSessionMessage": if (YesNoType.Yes == this.ParseHelper.GetAttributeYesNoValue(sourceLineNumbers, attrib)) { attributes |= 0x10; // CLOSEAPP_ATTRIBUTE_ELEVATEDENDSESSIONMESSAGE } else { attributes &= ~0x10; // CLOSEAPP_ATTRIBUTE_ELEVATEDENDSESSIONMESSAGE } break; case "TerminateProcess": terminateExitCode = this.ParseHelper.GetAttributeIntegerValue(sourceLineNumbers, attrib, 0, Int32.MaxValue); attributes |= 0x20; // CLOSEAPP_ATTRIBUTE_TERMINATEPROCESS break; default: this.ParseHelper.UnexpectedAttribute(element, attrib); break; } } else { this.ParseHelper.ParseExtensionAttribute(this.Context.Extensions, intermediate, section, element, attrib); } } if (null == target) { this.Messaging.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, element.Name.LocalName, "Target")); } else if (null == id) { id = this.ParseHelper.CreateIdentifier("ca", target); } if (String.IsNullOrEmpty(description) && 0x40 == (attributes & 0x40)) { this.Messaging.Write(ErrorMessages.IllegalAttributeValueWithoutOtherAttribute(sourceLineNumbers, element.Name.LocalName, "PromptToContinue", "yes", "Description")); } if (0x22 == (attributes & 0x22)) { this.Messaging.Write(ErrorMessages.IllegalAttributeWithOtherAttribute(sourceLineNumbers, element.Name.LocalName, "TerminateProcess", "RebootPrompt", "yes")); } this.ParseHelper.ParseForExtensionElements(this.Context.Extensions, intermediate, section, element); this.ParseHelper.CreateCustomActionReference(sourceLineNumbers, section, "Wix4CloseApplications", this.Context.Platform, CustomActionPlatforms.X86 | CustomActionPlatforms.X64 | CustomActionPlatforms.ARM64); if (!this.Messaging.EncounteredError) { var symbol = section.AddSymbol(new WixCloseApplicationSymbol(sourceLineNumbers, id) { Target = target, Description = description, Condition = condition, Attributes = attributes, Property = property, }); if (CompilerConstants.IntegerNotSet != sequence) { symbol.Sequence = sequence; } if (CompilerConstants.IntegerNotSet != terminateExitCode) { symbol.TerminateExitCode = terminateExitCode; } if (CompilerConstants.IntegerNotSet != timeout) { symbol.Timeout = timeout * 1000; // make the timeout milliseconds in the table. } } } /// /// Parses a DirectorySearch element. /// /// Element to parse. private void ParseDirectorySearchElement(Intermediate intermediate, IntermediateSection section, XElement element) { var sourceLineNumbers = this.ParseHelper.GetSourceLineNumbers(element); Identifier id = null; string variable = null; string condition = null; string after = null; string path = null; var attributes = WixFileSearchAttributes.IsDirectory; var type = WixFileSearchType.Path; foreach (var attrib in element.Attributes()) { if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || this.Namespace == attrib.Name.Namespace) { switch (attrib.Name.LocalName) { case "Id": case "Variable": case "Condition": case "After": this.ParseCommonSearchAttributes(sourceLineNumbers, attrib, ref id, ref variable, ref condition, ref after); break; case "DisableFileRedirection": if (this.ParseHelper.GetAttributeYesNoValue(sourceLineNumbers, attrib) == YesNoType.Yes) { attributes |= WixFileSearchAttributes.DisableFileRedirection; } break; case "Path": path = this.ParseHelper.GetAttributeLongFilename(sourceLineNumbers, attrib, false, true); break; case "Result": var result = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib); switch (result) { case "exists": type = WixFileSearchType.Exists; break; default: this.Messaging.Write(ErrorMessages.IllegalAttributeValue(sourceLineNumbers, attrib.Parent.Name.LocalName, attrib.Name.LocalName, result, "exists")); break; } break; default: this.ParseHelper.UnexpectedAttribute(element, attrib); break; } } else { this.ParseHelper.ParseExtensionAttribute(this.Context.Extensions, intermediate, section, element, attrib); } } if (null == path) { this.Messaging.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, element.Name.LocalName, "Path")); } if (null == id) { id = this.ParseHelper.CreateIdentifier("wds", variable, condition, after, path, attributes.ToString(), type.ToString()); } this.ParseHelper.ParseForExtensionElements(this.Context.Extensions, intermediate, section, element); if (!this.Messaging.EncounteredError) { this.ParseHelper.CreateWixSearchSymbol(section, sourceLineNumbers, element.Name.LocalName, id, variable, condition, after, null); this.CreateWixFileSearchRow(section, sourceLineNumbers, id, path, attributes, type); } } /// /// Parses a DirectorySearchRef, FileSearchRef, ProductSearchRef, and RegistrySearchRef elements /// /// Element to parse. private void ParseWixSearchRefElement(Intermediate intermediate, IntermediateSection section, XElement node) { var sourceLineNumbers = this.ParseHelper.GetSourceLineNumbers(node); foreach (XAttribute attrib in node.Attributes()) { if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || this.Namespace == attrib.Name.Namespace) { switch (attrib.Name.LocalName) { case "Id": var refId = this.ParseHelper.GetAttributeIdentifierValue(sourceLineNumbers, attrib); this.ParseHelper.CreateSimpleReference(section, sourceLineNumbers, SymbolDefinitions.WixSearch, refId); break; default: this.ParseHelper.UnexpectedAttribute(node, attrib); break; } } else { this.ParseHelper.ParseExtensionAttribute(this.Context.Extensions, intermediate, section, node, attrib); } } this.ParseHelper.ParseForExtensionElements(this.Context.Extensions, intermediate, section, node); } /// /// Parses a FileSearch element. /// /// Element to parse. private void ParseFileSearchElement(Intermediate intermediate, IntermediateSection section, XElement node) { var sourceLineNumbers = this.ParseHelper.GetSourceLineNumbers(node); Identifier id = null; string variable = null; string condition = null; string after = null; string path = null; var attributes = WixFileSearchAttributes.None; var type = WixFileSearchType.Path; foreach (var attrib in node.Attributes()) { if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || this.Namespace == attrib.Name.Namespace) { switch (attrib.Name.LocalName) { case "Id": case "Variable": case "Condition": case "After": this.ParseCommonSearchAttributes(sourceLineNumbers, attrib, ref id, ref variable, ref condition, ref after); break; case "DisableFileRedirection": if (this.ParseHelper.GetAttributeYesNoValue(sourceLineNumbers, attrib) == YesNoType.Yes) { attributes |= WixFileSearchAttributes.DisableFileRedirection; } break; case "Path": path = this.ParseHelper.GetAttributeLongFilename(sourceLineNumbers, attrib, false, true); break; case "Result": string result = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib); switch (result) { case "exists": type = WixFileSearchType.Exists; break; case "version": type = WixFileSearchType.Version; break; default: this.Messaging.Write(ErrorMessages.IllegalAttributeValue(sourceLineNumbers, attrib.Parent.Name.LocalName, attrib.Name.LocalName, result, "exists", "version")); break; } break; default: this.ParseHelper.UnexpectedAttribute(node, attrib); break; } } else { this.ParseHelper.ParseExtensionAttribute(this.Context.Extensions, intermediate, section, node, attrib); } } if (null == path) { this.Messaging.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Path")); } if (null == id) { id = this.ParseHelper.CreateIdentifier("wfs", variable, condition, after, path, attributes.ToString(), type.ToString()); } this.ParseHelper.ParseForExtensionElements(this.Context.Extensions, intermediate, section, node); if (!this.Messaging.EncounteredError) { this.ParseHelper.CreateWixSearchSymbol(section, sourceLineNumbers, node.Name.LocalName, id, variable, condition, after, null); this.CreateWixFileSearchRow(section, sourceLineNumbers, id, path, attributes, type); } } /// /// Creates a row in the WixFileSearch table. /// /// Source line number for the parent element. /// Identifier of the search (key into the WixSearch table) /// File/directory path to search for. /// /// private void CreateWixFileSearchRow(IntermediateSection section, SourceLineNumber sourceLineNumbers, Identifier id, string path, WixFileSearchAttributes attributes, WixFileSearchType type) { section.AddSymbol(new WixFileSearchSymbol(sourceLineNumbers, id) { Path = path, Attributes = attributes, Type = type, }); } /// /// Parses a file share element. /// /// Element to parse. /// Identifier of parent component. /// Identifier of referred to directory. private void ParseFileShareElement(Intermediate intermediate, IntermediateSection section, XElement element, string componentId, string directoryId) { var sourceLineNumbers = this.ParseHelper.GetSourceLineNumbers(element); string description = null; string name = null; Identifier id = null; foreach (var attrib in element.Attributes()) { if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || this.Namespace == attrib.Name.Namespace) { switch (attrib.Name.LocalName) { case "Id": id = this.ParseHelper.GetAttributeIdentifier(sourceLineNumbers, attrib); break; case "Name": name = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib); break; case "Description": description = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib); break; default: this.ParseHelper.UnexpectedAttribute(element, attrib); break; } } else { this.ParseHelper.ParseExtensionAttribute(this.Context.Extensions, intermediate, section, element, attrib); } } if (null == id) { id = this.ParseHelper.CreateIdentifier("ufs", componentId, name); } if (null == name) { this.Messaging.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, element.Name.LocalName, "Name")); } var fileSharePermissionCount = 0; foreach (var child in element.Elements()) { if (this.Namespace == child.Name.Namespace) { switch (child.Name.LocalName) { case "FileSharePermission": this.ParseFileSharePermissionElement(intermediate, section, child, id); ++fileSharePermissionCount; break; default: this.ParseHelper.UnexpectedElement(element, child); break; } } else { this.ParseHelper.ParseExtensionElement(this.Context.Extensions, intermediate, section, element, child); } } if (fileSharePermissionCount == 0) { this.Messaging.Write(ErrorMessages.ExpectedElement(sourceLineNumbers, element.Name.LocalName, "FileSharePermission")); } this.ParseHelper.CreateCustomActionReference(sourceLineNumbers, section, "Wix4ConfigureSmbInstall", this.Context.Platform, CustomActionPlatforms.X86 | CustomActionPlatforms.X64 | CustomActionPlatforms.ARM64); this.ParseHelper.CreateCustomActionReference(sourceLineNumbers, section, "Wix4ConfigureSmbUninstall", this.Context.Platform, CustomActionPlatforms.X86 | CustomActionPlatforms.X64 | CustomActionPlatforms.ARM64); if (!this.Messaging.EncounteredError) { section.AddSymbol(new FileShareSymbol(sourceLineNumbers, id) { ShareName = name, ComponentRef = componentId, Description = description, DirectoryRef = directoryId, }); } } /// /// Parses a FileSharePermission element. /// /// Element to parse. /// The identifier of the parent FileShare element. private void ParseFileSharePermissionElement(Intermediate intermediate, IntermediateSection section, XElement element, Identifier fileShareId) { var sourceLineNumbers = this.ParseHelper.GetSourceLineNumbers(element); var bits = new BitArray(32); string user = null; var validBitNames = new HashSet(UtilConstants.StandardPermissions.Concat(UtilConstants.GenericPermissions).Concat(UtilConstants.FolderPermissions)); foreach (var attrib in element.Attributes()) { if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || this.Namespace == attrib.Name.Namespace) { switch (attrib.Name.LocalName) { case "User": user = this.ParseHelper.GetAttributeIdentifierValue(sourceLineNumbers, attrib); this.ParseHelper.CreateSimpleReference(section, sourceLineNumbers, UtilSymbolDefinitions.User, user); break; default: if (validBitNames.Contains(attrib.Name.LocalName)) { var attribValue = this.ParseHelper.GetAttributeYesNoValue(sourceLineNumbers, attrib); if (this.TrySetBitFromName(UtilConstants.StandardPermissions, attrib.Name.LocalName, attribValue, bits, 16) || this.TrySetBitFromName(UtilConstants.GenericPermissions, attrib.Name.LocalName, attribValue, bits, 28) || this.TrySetBitFromName(UtilConstants.FolderPermissions, attrib.Name.LocalName, attribValue, bits, 0)) { break; } } this.ParseHelper.UnexpectedAttribute(element, attrib); break; } } else { this.ParseHelper.ParseExtensionAttribute(this.Context.Extensions, intermediate, section, element, attrib); } } var permission = this.CreateIntegerFromBitArray(bits); if (null == user) { this.Messaging.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, element.Name.LocalName, "User")); } if (Int32.MinValue == permission) // just GENERIC_READ, which is MSI_NULL { this.Messaging.Write(ErrorMessages.GenericReadNotAllowed(sourceLineNumbers)); } this.ParseHelper.ParseForExtensionElements(this.Context.Extensions, intermediate, section, element); if (!this.Messaging.EncounteredError) { section.AddSymbol(new FileSharePermissionsSymbol(sourceLineNumbers) { FileShareRef = fileShareId.Id, UserRef = user, Permissions = permission, }); } } /// /// Parses a group element. /// /// Node to be parsed. /// Component Id of the parent component of this element. private void ParseGroupElement(Intermediate intermediate, IntermediateSection section, XElement element, string componentId) { var sourceLineNumbers = this.ParseHelper.GetSourceLineNumbers(element); Identifier id = null; string domain = null; string name = null; string comment = null; Group6SymbolAttributes attributes = Group6SymbolAttributes.None; foreach (var attrib in element.Attributes()) { if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || this.Namespace == attrib.Name.Namespace) { switch (attrib.Name.LocalName) { case "Id": id = this.ParseHelper.GetAttributeIdentifier(sourceLineNumbers, attrib); break; case "Name": name = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib); break; case "Domain": domain = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib); break; case "Comment": if (null == componentId) { this.Messaging.Write(UtilErrors.IllegalAttributeWithoutComponent(sourceLineNumbers, element.Name.LocalName, attrib.Name.LocalName)); } comment = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib); break; case "CreateGroup": if (null == componentId) { this.Messaging.Write(UtilErrors.IllegalAttributeWithoutComponent(sourceLineNumbers, element.Name.LocalName, attrib.Name.LocalName)); } if (YesNoType.No == this.ParseHelper.GetAttributeYesNoValue(sourceLineNumbers, attrib)) { attributes |= Group6SymbolAttributes.DontCreateGroup; } break; case "FailIfExists": if (null == componentId) { this.Messaging.Write(UtilErrors.IllegalAttributeWithoutComponent(sourceLineNumbers, element.Name.LocalName, attrib.Name.LocalName)); } if (YesNoType.Yes == this.ParseHelper.GetAttributeYesNoValue(sourceLineNumbers, attrib)) { attributes |= Group6SymbolAttributes.FailIfExists; } break; case "UpdateIfExists": if (null == componentId) { this.Messaging.Write(UtilErrors.IllegalAttributeWithoutComponent(sourceLineNumbers, element.Name.LocalName, attrib.Name.LocalName)); } if (YesNoType.Yes == this.ParseHelper.GetAttributeYesNoValue(sourceLineNumbers, attrib)) { attributes |= Group6SymbolAttributes.UpdateIfExists; } break; case "RemoveComment": if (YesNoType.Yes == this.ParseHelper.GetAttributeYesNoValue(sourceLineNumbers, attrib)) { attributes |= Group6SymbolAttributes.RemoveComment; } break; case "RemoveOnUninstall": if (null == componentId) { this.Messaging.Write(UtilErrors.IllegalAttributeWithoutComponent(sourceLineNumbers, element.Name.LocalName, attrib.Name.LocalName)); } if (YesNoType.No == this.ParseHelper.GetAttributeYesNoValue(sourceLineNumbers, attrib)) { attributes |= Group6SymbolAttributes.DontRemoveOnUninstall; } break; case "Vital": if (null == componentId) { this.Messaging.Write(UtilErrors.IllegalAttributeWithoutComponent(sourceLineNumbers, element.Name.LocalName, attrib.Name.LocalName)); } if (YesNoType.No == this.ParseHelper.GetAttributeYesNoValue(sourceLineNumbers, attrib)) { attributes |= Group6SymbolAttributes.NonVital; } break; default: this.ParseHelper.UnexpectedAttribute(element, attrib); break; } } else { this.ParseHelper.ParseExtensionAttribute(this.Context.Extensions, intermediate, section, element, attrib); } } if (null == id) { id = this.ParseHelper.CreateIdentifier("ugr", componentId, domain, name); } if (null == name) { this.Messaging.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, element.Name.LocalName, "Name")); } if (null != comment && (Group6SymbolAttributes.RemoveComment & attributes) != 0) { this.Messaging.Write(ErrorMessages.IllegalAttributeWithOtherAttribute(sourceLineNumbers, element.Name.LocalName, "Comment", "RemoveComment")); } if (null != componentId) { this.ParseHelper.CreateCustomActionReference(sourceLineNumbers, section, "Wix6ConfigureGroups", this.Context.Platform, CustomActionPlatforms.X86 | CustomActionPlatforms.X64 | CustomActionPlatforms.ARM64); } foreach (var child in element.Elements()) { if (this.Namespace == child.Name.Namespace) { switch (child.Name.LocalName) { case "GroupRef": this.ParseGroupRefElement(intermediate, section, child, id.Id, groupType: true); break; default: this.ParseHelper.UnexpectedElement(element, child); break; } } else { this.ParseHelper.ParseExtensionElement(this.Context.Extensions, intermediate, section, element, child); } } if (!this.Messaging.EncounteredError) { section.AddSymbol(new GroupSymbol(sourceLineNumbers, id) { ComponentRef = componentId, Name = name, Domain = domain, }); section.AddSymbol(new Group6Symbol(sourceLineNumbers, id) { GroupRef = id.Id, Comment = comment, Attributes = attributes, }); } } /// /// Parses a GroupRef element /// /// Element to parse. /// Required child id to be joined to the group. /// whether the child is a group (true) or a user (false) private void ParseGroupRefElement(Intermediate intermediate, IntermediateSection section, XElement element, string childId, bool groupType) { var sourceLineNumbers = this.ParseHelper.GetSourceLineNumbers(element); string groupId = null; foreach (var attrib in element.Attributes()) { if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || this.Namespace == attrib.Name.Namespace) { switch (attrib.Name.LocalName) { case "Id": groupId = this.ParseHelper.GetAttributeIdentifierValue(sourceLineNumbers, attrib); this.ParseHelper.CreateSimpleReference(section, sourceLineNumbers, UtilSymbolDefinitions.Group, groupId); break; default: this.ParseHelper.UnexpectedAttribute(element, attrib); break; } } else { this.ParseHelper.ParseExtensionAttribute(this.Context.Extensions, intermediate, section, element, attrib); } } this.ParseHelper.ParseForExtensionElements(this.Context.Extensions, intermediate, section, element); if (!this.Messaging.EncounteredError) { if (!groupType) { section.AddSymbol(new UserGroupSymbol(sourceLineNumbers) { UserRef = childId, GroupRef = groupId, }); } else { // Add reference to bring in fragment this.ParseHelper.CreateCustomActionReference(sourceLineNumbers, section, "Wix6AddGroupMembership", this.Context.Platform, CustomActionPlatforms.X86 | CustomActionPlatforms.X64 | CustomActionPlatforms.ARM64); section.AddSymbol(new GroupGroupSymbol(sourceLineNumbers) { ChildGroupRef = childId, ParentGroupRef = groupId, }); } } } /// /// Parses an InternetShortcut element. /// /// Element to parse. /// Identifier of parent component. /// Default directory if none is specified on the InternetShortcut element. private void ParseInternetShortcutElement(Intermediate intermediate, IntermediateSection section, XElement element, string componentId, string defaultTarget) { var sourceLineNumbers = this.ParseHelper.GetSourceLineNumbers(element); Identifier id = null; string name = null; string target = null; string directoryId = null; string type = null; string iconFile = null; int iconIndex = 0; foreach (var attrib in element.Attributes()) { if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || this.Namespace == attrib.Name.Namespace) { switch (attrib.Name.LocalName) { case "Directory": directoryId = this.ParseHelper.GetAttributeIdentifierValue(sourceLineNumbers, attrib); break; case "Id": id = this.ParseHelper.GetAttributeIdentifier(sourceLineNumbers, attrib); break; case "Name": name = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib); break; case "Target": target = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib); break; case "Type": type = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib); break; case "IconFile": iconFile = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib); break; case "IconIndex": iconIndex = this.ParseHelper.GetAttributeIntegerValue(sourceLineNumbers, attrib, 0, Int32.MaxValue); break; default: this.ParseHelper.UnexpectedAttribute(element, attrib); break; } } else { this.ParseHelper.ParseExtensionAttribute(this.Context.Extensions, intermediate, section, element, attrib); } } // If there was no directoryId specified on the InternetShortcut element, default to the one on // the parent component. if (null == directoryId) { directoryId = defaultTarget; } if (null == id) { id = this.ParseHelper.CreateIdentifier("uis", componentId, directoryId, name, target); } // In theory this can never be the case, since InternetShortcut can only be under // a component element, and if the Directory wasn't specified the default will come // from the component. However, better safe than sorry, so here's a check to make sure // it didn't wind up being null after setting it to the defaultTarget. if (null == directoryId) { this.Messaging.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, element.Name.LocalName, "Directory")); } if (null == name) { this.Messaging.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, element.Name.LocalName, "Name")); } if (null == target) { this.Messaging.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, element.Name.LocalName, "Target")); } this.ParseHelper.ParseForExtensionElements(this.Context.Extensions, intermediate, section, element); var shortcutType = InternetShortcutType.Link; if (String.Equals(type, "url", StringComparison.OrdinalIgnoreCase)) { shortcutType = InternetShortcutType.Url; } if (!this.Messaging.EncounteredError) { this.CreateWixInternetShortcut(section, sourceLineNumbers, componentId, directoryId, id, name, target, shortcutType, iconFile, iconIndex); } } /// /// Creates the rows needed for WixInternetShortcut to work. /// /// The CompilerCore object used to create rows. /// Source line information about the owner element. /// Identifier of parent component. /// Identifier of directory containing shortcut. /// Identifier of shortcut. /// Name of shortcut without extension. /// Target URL of shortcut. private void CreateWixInternetShortcut(IntermediateSection section, SourceLineNumber sourceLineNumbers, string componentId, string directoryId, Identifier shortcutId, string name, string target, InternetShortcutType type, string iconFile, int iconIndex) { // add the appropriate extension based on type of shortcut name = String.Concat(name, InternetShortcutType.Url == type ? ".url" : ".lnk"); section.AddSymbol(new WixInternetShortcutSymbol(sourceLineNumbers, shortcutId) { ComponentRef = componentId, DirectoryRef = directoryId, Name = name, Target = target, Attributes = (int)type, IconFile = iconFile, IconIndex = iconIndex, }); this.ParseHelper.CreateCustomActionReference(sourceLineNumbers, section, "Wix4SchedInternetShortcuts", this.Context.Platform, CustomActionPlatforms.X86 | CustomActionPlatforms.X64 | CustomActionPlatforms.ARM64); // make sure we have a CreateFolder table so that the immediate CA can add temporary rows to handle installation and uninstallation this.ParseHelper.EnsureTable(section, sourceLineNumbers, "CreateFolder"); // use built-in MSI functionality to remove the shortcuts rather than doing so via CA section.AddSymbol(new RemoveFileSymbol(sourceLineNumbers, shortcutId) { ComponentRef = componentId, DirPropertyRef = directoryId, OnUninstall = true, FileName = name, }); } /// /// Parses a performance category element. /// /// Element to parse. /// Identifier of parent component. private void ParsePerformanceCategoryElement(Intermediate intermediate, IntermediateSection section, XElement element, string componentId) { var sourceLineNumbers = this.ParseHelper.GetSourceLineNumbers(element); Identifier id = null; string name = null; string help = null; var multiInstance = YesNoType.No; int defaultLanguage = 0x09; // default to "english" var parsedPerformanceCounters = new List(); // default to managed performance counter var library = "netfxperf.dll"; var openEntryPoint = "OpenPerformanceData"; var collectEntryPoint = "CollectPerformanceData"; var closeEntryPoint = "ClosePerformanceData"; foreach (var attrib in element.Attributes()) { if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || this.Namespace == attrib.Name.Namespace) { switch (attrib.Name.LocalName) { case "Close": closeEntryPoint = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib); break; case "Collect": collectEntryPoint = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib); break; case "DefaultLanguage": defaultLanguage = this.GetPerformanceCounterLanguage(sourceLineNumbers, attrib); break; case "Help": help = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib); break; case "Id": id = this.ParseHelper.GetAttributeIdentifier(sourceLineNumbers, attrib); break; case "Library": library = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib); break; case "MultiInstance": multiInstance = this.ParseHelper.GetAttributeYesNoValue(sourceLineNumbers, attrib); break; case "Name": name = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib); break; case "Open": openEntryPoint = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib); break; default: this.ParseHelper.UnexpectedAttribute(element, attrib); break; } } else { this.ParseHelper.ParseExtensionAttribute(this.Context.Extensions, intermediate, section, element, attrib); } } if (null == id && null == name) { this.Messaging.Write(ErrorMessages.ExpectedAttributes(sourceLineNumbers, element.Name.LocalName, "Id", "Name")); } else if (null == id) { id = this.ParseHelper.CreateIdentifier("upc", componentId, name); } else if (null == name) { name = id.Id; } // Process the child counter elements. foreach (var child in element.Elements()) { if (this.Namespace == child.Name.Namespace) { switch (child.Name.LocalName) { case "PerformanceCounter": var counter = this.ParsePerformanceCounterElement(intermediate, section, child, defaultLanguage); if (null != counter) { parsedPerformanceCounters.Add(counter); } break; default: this.ParseHelper.UnexpectedElement(element, child); break; } } else { this.ParseHelper.ParseExtensionElement(this.Context.Extensions, intermediate, section, element, child); } } if (!this.Messaging.EncounteredError) { // Calculate the ini and h file content. var objectName = "OBJECT_1"; var objectLanguage = defaultLanguage.ToString("D3", CultureInfo.InvariantCulture); var sbIniData = new StringBuilder(); sbIniData.AppendFormat("[info]\r\ndrivername={0}\r\nsymbolfile=wixperf.h\r\n\r\n[objects]\r\n{1}_{2}_NAME=\r\n\r\n[languages]\r\n{2}=LANG{2}\r\n\r\n", name, objectName, objectLanguage); sbIniData.AppendFormat("[text]\r\n{0}_{1}_NAME={2}\r\n", objectName, objectLanguage, name); if (null != help) { sbIniData.AppendFormat("{0}_{1}_HELP={2}\r\n", objectName, objectLanguage, help); } int symbolConstantsCounter = 0; var sbSymbolicConstants = new StringBuilder(); sbSymbolicConstants.AppendFormat("#define {0} {1}\r\n", objectName, symbolConstantsCounter); var sbCounterNames = new StringBuilder("[~]"); var sbCounterTypes = new StringBuilder("[~]"); for (int i = 0; i < parsedPerformanceCounters.Count; ++i) { var counter = parsedPerformanceCounters[i]; var counterName = String.Concat("DEVICE_COUNTER_", i + 1); sbIniData.AppendFormat("{0}_{1}_NAME={2}\r\n", counterName, counter.Language, counter.Name); if (null != counter.Help) { sbIniData.AppendFormat("{0}_{1}_HELP={2}\r\n", counterName, counter.Language, counter.Help); } symbolConstantsCounter += 2; sbSymbolicConstants.AppendFormat("#define {0} {1}\r\n", counterName, symbolConstantsCounter); sbCounterNames.Append(UtilCompiler.FindPropertyBrackets.Replace(counter.Name, this.EscapeProperties)); sbCounterNames.Append("[~]"); sbCounterTypes.Append(counter.Type); sbCounterTypes.Append("[~]"); } sbSymbolicConstants.AppendFormat("#define LAST_{0}_COUNTER_OFFSET {1}\r\n", objectName, symbolConstantsCounter); // Add the calculated INI and H strings to the PerformanceCategory table. section.AddSymbol(new PerformanceCategorySymbol(sourceLineNumbers, id) { ComponentRef = componentId, Name = name, IniData = sbIniData.ToString(), ConstantData = sbSymbolicConstants.ToString(), }); // Set up the application's performance key. var escapedName = UtilCompiler.FindPropertyBrackets.Replace(name, this.EscapeProperties); var linkageKey = String.Format(@"SYSTEM\CurrentControlSet\Services\{0}\Linkage", escapedName); var performanceKey = String.Format(@"SYSTEM\CurrentControlSet\Services\{0}\Performance", escapedName); this.ParseHelper.CreateRegistrySymbol(section, sourceLineNumbers, RegistryRootType.LocalMachine, linkageKey, "Export", escapedName, componentId); this.ParseHelper.CreateRegistrySymbol(section, sourceLineNumbers, RegistryRootType.LocalMachine, performanceKey, "-", null, componentId); this.ParseHelper.CreateRegistrySymbol(section, sourceLineNumbers, RegistryRootType.LocalMachine, performanceKey, "Library", library, componentId); this.ParseHelper.CreateRegistrySymbol(section, sourceLineNumbers, RegistryRootType.LocalMachine, performanceKey, "Open", openEntryPoint, componentId); this.ParseHelper.CreateRegistrySymbol(section, sourceLineNumbers, RegistryRootType.LocalMachine, performanceKey, "Collect", collectEntryPoint, componentId); this.ParseHelper.CreateRegistrySymbol(section, sourceLineNumbers, RegistryRootType.LocalMachine, performanceKey, "Close", closeEntryPoint, componentId); this.ParseHelper.CreateRegistrySymbol(section, sourceLineNumbers, RegistryRootType.LocalMachine, performanceKey, "IsMultiInstance", YesNoType.Yes == multiInstance ? 1 : 0, componentId); this.ParseHelper.CreateRegistrySymbol(section, sourceLineNumbers, RegistryRootType.LocalMachine, performanceKey, "Counter Names", sbCounterNames.ToString(), componentId, RegistryValueType.MultiString); this.ParseHelper.CreateRegistrySymbol(section, sourceLineNumbers, RegistryRootType.LocalMachine, performanceKey, "Counter Types", sbCounterTypes.ToString(), componentId, RegistryValueType.MultiString); } this.ParseHelper.CreateCustomActionReference(sourceLineNumbers, section, "Wix4InstallPerfCounterData", this.Context.Platform, CustomActionPlatforms.X86 | CustomActionPlatforms.X64 | CustomActionPlatforms.ARM64); this.ParseHelper.CreateCustomActionReference(sourceLineNumbers, section, "Wix4UninstallPerfCounterData", this.Context.Platform, CustomActionPlatforms.X86 | CustomActionPlatforms.X64 | CustomActionPlatforms.ARM64); } /// /// Gets the performance counter language as a decimal number. /// /// Source line information about the owner element. /// The attribute containing the value to get. /// Numeric representation of the language as per WinNT.h. private int GetPerformanceCounterLanguage(SourceLineNumber sourceLineNumbers, XAttribute attribute) { int language = 0; if (String.Empty == attribute.Value) { this.Messaging.Write(ErrorMessages.IllegalEmptyAttributeValue(sourceLineNumbers, attribute.Parent.Name.LocalName, attribute.Name.LocalName)); } else { switch (attribute.Value) { case "afrikaans": language = 0x36; break; case "albanian": language = 0x1c; break; case "arabic": language = 0x01; break; case "armenian": language = 0x2b; break; case "assamese": language = 0x4d; break; case "azeri": language = 0x2c; break; case "basque": language = 0x2d; break; case "belarusian": language = 0x23; break; case "bengali": language = 0x45; break; case "bulgarian": language = 0x02; break; case "catalan": language = 0x03; break; case "chinese": language = 0x04; break; case "croatian": language = 0x1a; break; case "czech": language = 0x05; break; case "danish": language = 0x06; break; case "divehi": language = 0x65; break; case "dutch": language = 0x13; break; case "piglatin": case "english": language = 0x09; break; case "estonian": language = 0x25; break; case "faeroese": language = 0x38; break; case "farsi": language = 0x29; break; case "finnish": language = 0x0b; break; case "french": language = 0x0c; break; case "galician": language = 0x56; break; case "georgian": language = 0x37; break; case "german": language = 0x07; break; case "greek": language = 0x08; break; case "gujarati": language = 0x47; break; case "hebrew": language = 0x0d; break; case "hindi": language = 0x39; break; case "hungarian": language = 0x0e; break; case "icelandic": language = 0x0f; break; case "indonesian": language = 0x21; break; case "italian": language = 0x10; break; case "japanese": language = 0x11; break; case "kannada": language = 0x4b; break; case "kashmiri": language = 0x60; break; case "kazak": language = 0x3f; break; case "konkani": language = 0x57; break; case "korean": language = 0x12; break; case "kyrgyz": language = 0x40; break; case "latvian": language = 0x26; break; case "lithuanian": language = 0x27; break; case "macedonian": language = 0x2f; break; case "malay": language = 0x3e; break; case "malayalam": language = 0x4c; break; case "manipuri": language = 0x58; break; case "marathi": language = 0x4e; break; case "mongolian": language = 0x50; break; case "nepali": language = 0x61; break; case "norwegian": language = 0x14; break; case "oriya": language = 0x48; break; case "polish": language = 0x15; break; case "portuguese": language = 0x16; break; case "punjabi": language = 0x46; break; case "romanian": language = 0x18; break; case "russian": language = 0x19; break; case "sanskrit": language = 0x4f; break; case "serbian": language = 0x1a; break; case "sindhi": language = 0x59; break; case "slovak": language = 0x1b; break; case "slovenian": language = 0x24; break; case "spanish": language = 0x0a; break; case "swahili": language = 0x41; break; case "swedish": language = 0x1d; break; case "syriac": language = 0x5a; break; case "tamil": language = 0x49; break; case "tatar": language = 0x44; break; case "telugu": language = 0x4a; break; case "thai": language = 0x1e; break; case "turkish": language = 0x1f; break; case "ukrainian": language = 0x22; break; case "urdu": language = 0x20; break; case "uzbek": language = 0x43; break; case "vietnamese": language = 0x2a; break; default: this.Messaging.Write(ErrorMessages.IllegalEmptyAttributeValue(sourceLineNumbers, attribute.Parent.Name.LocalName, attribute.Name.LocalName)); break; } } return language; } /// /// Parses a performance counter element. /// /// Element to parse. /// Default language for the performance counter. private ParsedPerformanceCounter ParsePerformanceCounterElement(Intermediate intermediate, IntermediateSection section, XElement element, int defaultLanguage) { var sourceLineNumbers = this.ParseHelper.GetSourceLineNumbers(element); ParsedPerformanceCounter parsedPerformanceCounter = null; string name = null; string help = null; var type = System.Diagnostics.PerformanceCounterType.NumberOfItems32; int language = defaultLanguage; foreach (var attrib in element.Attributes()) { if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || this.Namespace == attrib.Name.Namespace) { switch (attrib.Name.LocalName) { case "Help": help = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib); break; case "Name": name = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib); break; case "Type": type = this.GetPerformanceCounterType(sourceLineNumbers, attrib); break; case "Language": language = this.GetPerformanceCounterLanguage(sourceLineNumbers, attrib); break; default: this.ParseHelper.UnexpectedAttribute(element, attrib); break; } } else { this.ParseHelper.ParseExtensionAttribute(this.Context.Extensions, intermediate, section, element, attrib); } } if (null == name) { this.Messaging.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, element.Name.LocalName, "Name")); } if (null == help) { this.Messaging.Write(UtilWarnings.RequiredAttributeForWindowsXP(sourceLineNumbers, element.Name.LocalName, "Help")); } this.ParseHelper.ParseForExtensionElements(this.Context.Extensions, intermediate, section, element); if (!this.Messaging.EncounteredError) { parsedPerformanceCounter = new ParsedPerformanceCounter(name, help, type, language); } return parsedPerformanceCounter; } /// /// Gets the performance counter type. /// /// Source line information about the owner element. /// The attribute containing the value to get. /// Numeric representation of the language as per WinNT.h. private System.Diagnostics.PerformanceCounterType GetPerformanceCounterType(SourceLineNumber sourceLineNumbers, XAttribute attribute) { var type = System.Diagnostics.PerformanceCounterType.NumberOfItems32; if (String.Empty == attribute.Value) { this.Messaging.Write(ErrorMessages.IllegalEmptyAttributeValue(sourceLineNumbers, attribute.Parent.Name.LocalName, attribute.Name.LocalName)); } else { switch (attribute.Value) { case "averageBase": type = System.Diagnostics.PerformanceCounterType.AverageBase; break; case "averageCount64": type = System.Diagnostics.PerformanceCounterType.AverageCount64; break; case "averageTimer32": type = System.Diagnostics.PerformanceCounterType.AverageTimer32; break; case "counterDelta32": type = System.Diagnostics.PerformanceCounterType.CounterDelta32; break; case "counterTimerInverse": type = System.Diagnostics.PerformanceCounterType.CounterTimerInverse; break; case "sampleFraction": type = System.Diagnostics.PerformanceCounterType.SampleFraction; break; case "timer100Ns": type = System.Diagnostics.PerformanceCounterType.Timer100Ns; break; case "counterTimer": type = System.Diagnostics.PerformanceCounterType.CounterTimer; break; case "rawFraction": type = System.Diagnostics.PerformanceCounterType.RawFraction; break; case "timer100NsInverse": type = System.Diagnostics.PerformanceCounterType.Timer100NsInverse; break; case "counterMultiTimer": type = System.Diagnostics.PerformanceCounterType.CounterMultiTimer; break; case "counterMultiTimer100Ns": type = System.Diagnostics.PerformanceCounterType.CounterMultiTimer100Ns; break; case "counterMultiTimerInverse": type = System.Diagnostics.PerformanceCounterType.CounterMultiTimerInverse; break; case "counterMultiTimer100NsInverse": type = System.Diagnostics.PerformanceCounterType.CounterMultiTimer100NsInverse; break; case "elapsedTime": type = System.Diagnostics.PerformanceCounterType.ElapsedTime; break; case "sampleBase": type = System.Diagnostics.PerformanceCounterType.SampleBase; break; case "rawBase": type = System.Diagnostics.PerformanceCounterType.RawBase; break; case "counterMultiBase": type = System.Diagnostics.PerformanceCounterType.CounterMultiBase; break; case "rateOfCountsPerSecond64": type = System.Diagnostics.PerformanceCounterType.RateOfCountsPerSecond64; break; case "rateOfCountsPerSecond32": type = System.Diagnostics.PerformanceCounterType.RateOfCountsPerSecond32; break; case "countPerTimeInterval64": type = System.Diagnostics.PerformanceCounterType.CountPerTimeInterval64; break; case "countPerTimeInterval32": type = System.Diagnostics.PerformanceCounterType.CountPerTimeInterval32; break; case "sampleCounter": type = System.Diagnostics.PerformanceCounterType.SampleCounter; break; case "counterDelta64": type = System.Diagnostics.PerformanceCounterType.CounterDelta64; break; case "numberOfItems64": type = System.Diagnostics.PerformanceCounterType.NumberOfItems64; break; case "numberOfItems32": type = System.Diagnostics.PerformanceCounterType.NumberOfItems32; break; case "numberOfItemsHEX64": type = System.Diagnostics.PerformanceCounterType.NumberOfItemsHEX64; break; case "numberOfItemsHEX32": type = System.Diagnostics.PerformanceCounterType.NumberOfItemsHEX32; break; default: this.Messaging.Write(ErrorMessages.IllegalEmptyAttributeValue(sourceLineNumbers, attribute.Parent.Name.LocalName, attribute.Name.LocalName)); break; } } return type; } /// /// Parses a perf counter element. /// /// Element to parse. /// Identifier of parent component. /// Identifier of referenced file. private void ParsePerfCounterElement(Intermediate intermediate, IntermediateSection section, XElement element, string componentId, string fileId) { var sourceLineNumbers = this.ParseHelper.GetSourceLineNumbers(element); string name = null; this.Messaging.Write(UtilWarnings.DeprecatedPerfCounterElement(sourceLineNumbers)); foreach (var attrib in element.Attributes()) { if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || this.Namespace == attrib.Name.Namespace) { switch (attrib.Name.LocalName) { case "Name": name = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib); break; default: this.ParseHelper.UnexpectedAttribute(element, attrib); break; } } else { this.ParseHelper.ParseExtensionAttribute(this.Context.Extensions, intermediate, section, element, attrib); } } if (null == name) { this.Messaging.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, element.Name.LocalName, "Name")); } this.ParseHelper.ParseForExtensionElements(this.Context.Extensions, intermediate, section, element); if (!this.Messaging.EncounteredError) { section.AddSymbol(new PerfmonSymbol(sourceLineNumbers) { ComponentRef = componentId, File = $"[#{fileId}]", Name = name, }); } this.ParseHelper.CreateCustomActionReference(sourceLineNumbers, section, "Wix4ConfigurePerfmonInstall", this.Context.Platform, CustomActionPlatforms.X86 | CustomActionPlatforms.X64 | CustomActionPlatforms.ARM64); this.ParseHelper.CreateCustomActionReference(sourceLineNumbers, section, "Wix4ConfigurePerfmonUninstall", this.Context.Platform, CustomActionPlatforms.X86 | CustomActionPlatforms.X64 | CustomActionPlatforms.ARM64); } /// /// Parses a perf manifest element. /// /// Element to parse. /// Identifier of parent component. /// Identifier of referenced file. private void ParsePerfCounterManifestElement(Intermediate intermediate, IntermediateSection section, XElement element, string componentId, string fileId) { var sourceLineNumbers = this.ParseHelper.GetSourceLineNumbers(element); string resourceFileDirectory = null; foreach (var attrib in element.Attributes()) { if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || this.Namespace == attrib.Name.Namespace) { switch (attrib.Name.LocalName) { case "ResourceFileDirectory": resourceFileDirectory = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib); break; default: this.ParseHelper.UnexpectedAttribute(element, attrib); break; } } else { this.ParseHelper.ParseExtensionAttribute(this.Context.Extensions, intermediate, section, element, attrib); } } this.ParseHelper.ParseForExtensionElements(this.Context.Extensions, intermediate, section, element); if (!this.Messaging.EncounteredError) { section.AddSymbol(new PerfmonManifestSymbol(sourceLineNumbers) { ComponentRef = componentId, File = $"[#{fileId}]", ResourceFileDirectory = resourceFileDirectory, }); } this.ParseHelper.CreateCustomActionReference(sourceLineNumbers, section, "Wix4ConfigurePerfmonManifestRegister", this.Context.Platform, CustomActionPlatforms.X86 | CustomActionPlatforms.X64 | CustomActionPlatforms.ARM64); this.ParseHelper.CreateCustomActionReference(sourceLineNumbers, section, "Wix4ConfigurePerfmonManifestUnregister", this.Context.Platform, CustomActionPlatforms.X86 | CustomActionPlatforms.X64 | CustomActionPlatforms.ARM64); } /// /// Parses a format files element. /// /// Element to parse. /// Identifier of referenced file. /// Flag to determine whether the component is 64-bit. private void ParseFormatFileElement(Intermediate intermediate, IntermediateSection section, XElement element, string fileId) { var sourceLineNumbers = this.ParseHelper.GetSourceLineNumbers(element); string binaryId = null; foreach (var attrib in element.Attributes()) { if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || this.Namespace == attrib.Name.Namespace) { switch (attrib.Name.LocalName) { case "BinaryRef": binaryId = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib); break; default: this.ParseHelper.UnexpectedAttribute(element, attrib); break; } } else { this.ParseHelper.ParseExtensionAttribute(this.Context.Extensions, intermediate, section, element, attrib); } } this.ParseHelper.ParseForExtensionElements(this.Context.Extensions, intermediate, section, element); if (null == binaryId) { this.Messaging.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, element.Name.LocalName, "BinaryRef")); } if (!this.Messaging.EncounteredError) { this.ParseHelper.CreateCustomActionReference(sourceLineNumbers, section, "Wix4SchedFormatFiles", this.Context.Platform, CustomActionPlatforms.X86 | CustomActionPlatforms.X64 | CustomActionPlatforms.ARM64); section.AddSymbol(new WixFormatFilesSymbol(sourceLineNumbers) { BinaryRef = binaryId, FileRef = fileId, }); this.ParseHelper.CreateSimpleReference(section, sourceLineNumbers, SymbolDefinitions.Binary, binaryId); } } /// /// Parses a event manifest element. /// /// Element to parse. /// Identifier of parent component. /// Identifier of referenced file. private void ParseEventManifestElement(Intermediate intermediate, IntermediateSection section, XElement element, string componentId, string fileId) { var sourceLineNumbers = this.ParseHelper.GetSourceLineNumbers(element); string messageFile = null; string resourceFile = null; string parameterFile = null; foreach (var attrib in element.Attributes()) { if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || this.Namespace == attrib.Name.Namespace) { switch (attrib.Name.LocalName) { case "MessageFile": messageFile = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib); break; case "ResourceFile": resourceFile = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib); break; case "ParameterFile": parameterFile = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib); break; default: this.ParseHelper.UnexpectedAttribute(element, attrib); break; } } else { this.ParseHelper.ParseExtensionAttribute(this.Context.Extensions, intermediate, section, element, attrib); } } this.ParseHelper.ParseForExtensionElements(this.Context.Extensions, intermediate, section, element); if (!this.Messaging.EncounteredError) { section.AddSymbol(new EventManifestSymbol(sourceLineNumbers) { ComponentRef = componentId, File = $"[#{fileId}]", }); if (null != messageFile) { section.AddSymbol(new XmlFileSymbol(sourceLineNumbers, new Identifier(AccessModifier.Section, $"Config_{fileId}MessageFile")) { File = $"[#{fileId}]", ElementPath = "/*/*/*/*[\\[]@messageFileName[\\]]", Name = "messageFileName", Value = messageFile, Flags = 4 | 0x00001000, //bulk write | preserve modified date ComponentRef = componentId, }); } if (null != parameterFile) { section.AddSymbol(new XmlFileSymbol(sourceLineNumbers, new Identifier(AccessModifier.Section, $"Config_{fileId}ParameterFile")) { File = $"[#{fileId}]", ElementPath = "/*/*/*/*[\\[]@parameterFileName[\\]]", Name = "parameterFileName", Value = parameterFile, Flags = 4 | 0x00001000, //bulk write | preserve modified date ComponentRef = componentId, }); } if (null != resourceFile) { section.AddSymbol(new XmlFileSymbol(sourceLineNumbers, new Identifier(AccessModifier.Section, $"Config_{fileId}ResourceFile")) { File = $"[#{fileId}]", ElementPath = "/*/*/*/*[\\[]@resourceFileName[\\]]", Name = "resourceFileName", Value = resourceFile, Flags = 4 | 0x00001000, //bulk write | preserve modified date ComponentRef = componentId, }); } } this.ParseHelper.CreateCustomActionReference(sourceLineNumbers, section, "Wix4ConfigureEventManifestRegister", this.Context.Platform, CustomActionPlatforms.X86 | CustomActionPlatforms.X64 | CustomActionPlatforms.ARM64); this.ParseHelper.CreateCustomActionReference(sourceLineNumbers, section, "Wix4ConfigureEventManifestUnregister", this.Context.Platform, CustomActionPlatforms.X86 | CustomActionPlatforms.X64 | CustomActionPlatforms.ARM64); if (null != messageFile || null != parameterFile || null != resourceFile) { this.AddReferenceToSchedXmlFile(sourceLineNumbers, section); } } /// /// Parses a PermissionEx element. /// /// Element to parse. /// Identifier of object to be secured. /// Identifier of component, used to determine install state. /// Flag to determine whether the component is 64-bit. /// Name of table that contains objectId. private void ParsePermissionExElement(Intermediate intermediate, IntermediateSection section, XElement element, string objectId, string componentId, string tableName) { var sourceLineNumbers = this.ParseHelper.GetSourceLineNumbers(element); var bits = new BitArray(32); string domain = null; string[] specialPermissions = null; string user = null; var attributes = WixPermissionExAttributes.Inheritable; // default to inheritable. var permissionType = PermissionType.SecureObjects; switch (tableName) { case "CreateFolder": specialPermissions = UtilConstants.FolderPermissions; break; case "File": specialPermissions = UtilConstants.FilePermissions; break; case "Registry": specialPermissions = UtilConstants.RegistryPermissions; if (String.IsNullOrEmpty(objectId)) { this.Messaging.Write(UtilErrors.InvalidRegistryObject(sourceLineNumbers, element.Parent.Name.LocalName)); } break; case "ServiceInstall": specialPermissions = UtilConstants.ServicePermissions; permissionType = PermissionType.SecureObjects; break; default: this.ParseHelper.UnexpectedElement(element.Parent, element); break; } var validBitNames = new HashSet(UtilConstants.StandardPermissions.Concat(UtilConstants.GenericPermissions).Concat(specialPermissions)); foreach (var attrib in element.Attributes()) { if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || this.Namespace == attrib.Name.Namespace) { switch (attrib.Name.LocalName) { case "Domain": if (PermissionType.FileSharePermissions == permissionType) { this.Messaging.Write(ErrorMessages.IllegalAttributeWhenNested(sourceLineNumbers, element.Name.LocalName, attrib.Name.LocalName, element.Parent.Name.LocalName)); } domain = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib); break; case "Inheritable": if (this.ParseHelper.GetAttributeYesNoValue(sourceLineNumbers, attrib) == YesNoType.No) { attributes &= ~WixPermissionExAttributes.Inheritable; } break; case "User": user = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib); break; default: if (validBitNames.Contains(attrib.Name.LocalName)) { var attribValue = this.ParseHelper.GetAttributeYesNoValue(sourceLineNumbers, attrib); if (this.TrySetBitFromName(UtilConstants.StandardPermissions, attrib.Name.LocalName, attribValue, bits, 16) || this.TrySetBitFromName(UtilConstants.GenericPermissions, attrib.Name.LocalName, attribValue, bits, 28) || this.TrySetBitFromName(specialPermissions, attrib.Name.LocalName, attribValue, bits, 0)) { break; } } this.ParseHelper.UnexpectedAttribute(element, attrib); break; } } else { this.ParseHelper.ParseExtensionAttribute(this.Context.Extensions, intermediate, section, element, attrib); } } var permission = this.CreateIntegerFromBitArray(bits); if (null == user) { this.Messaging.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, element.Name.LocalName, "User")); } if (Int32.MinValue == permission) // just GENERIC_READ, which is MSI_NULL { this.Messaging.Write(ErrorMessages.GenericReadNotAllowed(sourceLineNumbers)); } this.ParseHelper.ParseForExtensionElements(this.Context.Extensions, intermediate, section, element); if (!this.Messaging.EncounteredError) { this.ParseHelper.CreateCustomActionReference(sourceLineNumbers, section, "Wix4SchedSecureObjects", this.Context.Platform, CustomActionPlatforms.X86 | CustomActionPlatforms.X64 | CustomActionPlatforms.ARM64); var id = this.ParseHelper.CreateIdentifier("sec", objectId, tableName, domain, user); section.AddSymbol(new SecureObjectsSymbol(sourceLineNumbers, id) { SecureObject = objectId, Table = tableName, Domain = domain, User = user, Attributes = attributes, Permission = permission, ComponentRef = componentId, }); } } /// /// Parses a ProductSearch element. /// /// Element to parse. private void ParseProductSearchElement(Intermediate intermediate, IntermediateSection section, XElement element) { var sourceLineNumbers = this.ParseHelper.GetSourceLineNumbers(element); Identifier id = null; string variable = null; string condition = null; string after = null; string productCode = null; string upgradeCode = null; var attributes = WixProductSearchAttributes.None; var type = WixProductSearchType.Version; foreach (var attrib in element.Attributes()) { if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || this.Namespace == attrib.Name.Namespace) { switch (attrib.Name.LocalName) { case "Id": case "Variable": case "Condition": case "After": this.ParseCommonSearchAttributes(sourceLineNumbers, attrib, ref id, ref variable, ref condition, ref after); break; case "ProductCode": productCode = this.ParseHelper.GetAttributeGuidValue(sourceLineNumbers, attrib, false); break; case "UpgradeCode": upgradeCode = this.ParseHelper.GetAttributeGuidValue(sourceLineNumbers, attrib, false); break; case "Result": var result = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib); switch (result) { case "version": type = WixProductSearchType.Version; break; case "language": type = WixProductSearchType.Language; break; case "state": type = WixProductSearchType.State; break; case "assignment": type = WixProductSearchType.Assignment; break; default: this.Messaging.Write(ErrorMessages.IllegalAttributeValue(sourceLineNumbers, attrib.Parent.Name.LocalName, attrib.Name.LocalName, result, "version", "language", "state", "assignment")); break; } break; default: this.ParseHelper.UnexpectedAttribute(element, attrib); break; } } else { this.ParseHelper.ParseExtensionAttribute(this.Context.Extensions, intermediate, section, element, attrib); } } if (null == upgradeCode && null == productCode) { this.Messaging.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, element.Name.LocalName, "ProductCode", "UpgradeCode", true)); } if (null != upgradeCode && null != productCode) { this.Messaging.Write(ErrorMessages.IllegalAttributeWithOtherAttribute(sourceLineNumbers, element.Name.LocalName, "UpgradeCode", "ProductCode")); } string guid; if (upgradeCode != null) { // set an additional flag if this is an upgrade code attributes |= WixProductSearchAttributes.UpgradeCode; guid = upgradeCode; } else { guid = productCode; } if (null == id) { id = this.ParseHelper.CreateIdentifier("wps", variable, condition, after, guid, attributes.ToString(), type.ToString()); } this.ParseHelper.ParseForExtensionElements(this.Context.Extensions, intermediate, section, element); if (!this.Messaging.EncounteredError) { this.ParseHelper.CreateWixSearchSymbol(section, sourceLineNumbers, element.Name.LocalName, id, variable, condition, after, null); section.AddSymbol(new WixProductSearchSymbol(sourceLineNumbers, id) { Guid = guid, Attributes = attributes, Type = type, }); } } /// /// Parses a RegistrySearch element. /// /// Element to parse. private void ParseRegistrySearchElement(Intermediate intermediate, IntermediateSection section, XElement element) { var sourceLineNumbers = this.ParseHelper.GetSourceLineNumbers(element); Identifier id = null; string variable = null; string condition = null; string after = null; RegistryRootType? root = null; string key = null; string value = null; var expand = YesNoType.NotSet; var win64 = this.Context.IsCurrentPlatform64Bit; var attributes = WixRegistrySearchAttributes.None; var type = WixRegistrySearchType.Value; foreach (var attrib in element.Attributes()) { if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || this.Namespace == attrib.Name.Namespace) { switch (attrib.Name.LocalName) { case "Id": case "Variable": case "Condition": case "After": this.ParseCommonSearchAttributes(sourceLineNumbers, attrib, ref id, ref variable, ref condition, ref after); break; case "Bitness": var bitnessValue = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib); switch (bitnessValue) { case "always32": win64 = false; break; case "always64": win64 = true; break; case "default": case "": break; default: this.Messaging.Write(ErrorMessages.IllegalAttributeValue(sourceLineNumbers, attrib.Name.LocalName, attrib.Name.LocalName, bitnessValue, "default", "always32", "always64")); break; } break; case "Root": root = this.ParseHelper.GetAttributeRegistryRootValue(sourceLineNumbers, attrib, false); break; case "Key": key = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib); break; case "Value": value = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib); break; case "ExpandEnvironmentVariables": expand = this.ParseHelper.GetAttributeYesNoValue(sourceLineNumbers, attrib); break; case "Result": var result = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib); switch (result) { case "exists": type = WixRegistrySearchType.Exists; break; case "value": type = WixRegistrySearchType.Value; break; default: this.Messaging.Write(ErrorMessages.IllegalAttributeValue(sourceLineNumbers, attrib.Parent.Name.LocalName, attrib.Name.LocalName, result, "exists", "value")); break; } break; default: this.ParseHelper.UnexpectedAttribute(element, attrib); break; } } else { this.ParseHelper.ParseExtensionAttribute(this.Context.Extensions, intermediate, section, element, attrib); } } if (!root.HasValue) { this.Messaging.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, element.Name.LocalName, "Root")); } if (null == key) { this.Messaging.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, element.Name.LocalName, "Key")); } if (expand == YesNoType.Yes) { if (type == WixRegistrySearchType.Exists) { this.Messaging.Write(ErrorMessages.IllegalAttributeValueWithOtherAttribute(sourceLineNumbers, element.Name.LocalName, "ExpandEnvironmentVariables", expand.ToString(), "Result", "exists")); } attributes |= WixRegistrySearchAttributes.ExpandEnvironmentVariables; } if (win64) { attributes |= WixRegistrySearchAttributes.Win64; } if (null == id) { id = this.ParseHelper.CreateIdentifier("wrs", variable, condition, after, root.ToString(), key, value, attributes.ToString(), type.ToString()); } this.ParseHelper.ParseForExtensionElements(this.Context.Extensions, intermediate, section, element); if (!this.Messaging.EncounteredError) { this.ParseHelper.CreateWixSearchSymbol(section, sourceLineNumbers, element.Name.LocalName, id, variable, condition, after, null); section.AddSymbol(new WixRegistrySearchSymbol(sourceLineNumbers, id) { Root = root.Value, Key = key, Value = value, Attributes = attributes, Type = type, }); } } /// /// Parses a RemoveFolderEx element. /// /// Element to parse. /// Identifier of parent component. private void ParseRemoveFolderExElement(Intermediate intermediate, IntermediateSection section, XElement element, string componentId) { var sourceLineNumbers = this.ParseHelper.GetSourceLineNumbers(element); Identifier id = null; var mode = WixRemoveFolderExInstallMode.Uninstall; string property = null; string condition = null; foreach (var attrib in element.Attributes()) { if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || this.Namespace == attrib.Name.Namespace) { switch (attrib.Name.LocalName) { case "Condition": condition = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib); break; case "Id": id = this.ParseHelper.GetAttributeIdentifier(sourceLineNumbers, attrib); break; case "On": var onValue = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib); if (onValue.Length == 0) { } else { switch (onValue) { case "install": mode = WixRemoveFolderExInstallMode.Install; break; case "uninstall": mode = WixRemoveFolderExInstallMode.Uninstall; break; case "both": mode = WixRemoveFolderExInstallMode.Both; break; default: this.Messaging.Write(ErrorMessages.IllegalAttributeValue(sourceLineNumbers, element.Name.LocalName, "On", onValue, "install", "uninstall", "both")); break; } } break; case "Property": property = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib); break; default: this.ParseHelper.UnexpectedAttribute(element, attrib); break; } } else { this.ParseHelper.ParseExtensionAttribute(this.Context.Extensions, intermediate, section, element, attrib); } } if (String.IsNullOrEmpty(property)) { this.Messaging.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, element.Name.LocalName, "Property")); } if (id == null) { id = this.ParseHelper.CreateIdentifier("wrf", componentId, property, mode.ToString()); } this.ParseHelper.ParseForExtensionElements(this.Context.Extensions, intermediate, section, element); if (!this.Messaging.EncounteredError) { this.ParseHelper.CreateCustomActionReference(sourceLineNumbers, section, "Wix4RemoveFoldersEx", this.Context.Platform, CustomActionPlatforms.X86 | CustomActionPlatforms.X64 | CustomActionPlatforms.ARM64); section.AddSymbol(new WixRemoveFolderExSymbol(sourceLineNumbers, id) { ComponentRef = componentId, Property = property, InstallMode = mode, Condition = condition }); this.ParseHelper.EnsureTable(section, sourceLineNumbers, "RemoveFile"); } } /// /// Parses a RemoveRegistryKeyEx element. /// /// Element to parse. /// Identifier of parent component. private void ParseRemoveRegistryKeyExElement(Intermediate intermediate, IntermediateSection section, XElement element, string componentId) { var sourceLineNumbers = this.ParseHelper.GetSourceLineNumbers(element); Identifier id = null; var mode = WixRemoveRegistryKeyExInstallMode.Uninstall; string condition = null; RegistryRootType? root = null; string key = null; foreach (var attrib in element.Attributes()) { if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || this.Namespace == attrib.Name.Namespace) { switch (attrib.Name.LocalName) { case "Condition": condition = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib); break; case "Id": id = this.ParseHelper.GetAttributeIdentifier(sourceLineNumbers, attrib); break; case "On": var actionValue = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib); switch (actionValue) { case "": break; case "install": mode = WixRemoveRegistryKeyExInstallMode.Install; break; case "uninstall": mode = WixRemoveRegistryKeyExInstallMode.Uninstall; break; default: this.Messaging.Write(ErrorMessages.IllegalAttributeValue(sourceLineNumbers, element.Name.LocalName, "On", actionValue, "install", "uninstall")); break; } break; case "Root": root = this.ParseHelper.GetAttributeRegistryRootValue(sourceLineNumbers, attrib, false); break; case "Key": key = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib); break; default: this.ParseHelper.UnexpectedAttribute(element, attrib); break; } } else { this.ParseHelper.ParseExtensionAttribute(this.Context.Extensions, intermediate, section, element, attrib); } } if (!root.HasValue) { this.Messaging.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, element.Name.LocalName, "Root")); } if (key == null) { this.Messaging.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, element.Name.LocalName, "Key")); } if (id == null) { id = this.ParseHelper.CreateIdentifier("rrx", componentId, condition, root.ToString(), key, mode.ToString()); } this.ParseHelper.ParseForExtensionElements(this.Context.Extensions, intermediate, section, element); if (!this.Messaging.EncounteredError) { this.ParseHelper.EnsureTable(section, sourceLineNumbers, "Registry"); this.ParseHelper.EnsureTable(section, sourceLineNumbers, "RemoveRegistry"); this.ParseHelper.CreateCustomActionReference(sourceLineNumbers, section, "Wix4RemoveRegistryKeysEx", this.Context.Platform, CustomActionPlatforms.X86 | CustomActionPlatforms.X64 | CustomActionPlatforms.ARM64); section.AddSymbol(new WixRemoveRegistryKeyExSymbol(sourceLineNumbers, id) { ComponentRef = componentId, Root = root.Value, Key = key, InstallMode = mode, Condition = condition }); } } /// /// Parses a RestartResource element. /// /// The element to parse. /// The identity of the parent component. private void ParseRestartResourceElement(Intermediate intermediate, IntermediateSection section, XElement element, string componentId) { var sourceLineNumbers = this.ParseHelper.GetSourceLineNumbers(element); Identifier id = null; string resource = null; WixRestartResourceAttributes? attributes = null; foreach (var attrib in element.Attributes()) { if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || this.Namespace == attrib.Name.Namespace) { switch (attrib.Name.LocalName) { case "Id": id = this.ParseHelper.GetAttributeIdentifier(sourceLineNumbers, attrib); break; case "Path": resource = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib); attributes = WixRestartResourceAttributes.Filename; break; case "ProcessName": resource = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib); attributes = WixRestartResourceAttributes.ProcessName; break; case "ServiceName": resource = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib); attributes = WixRestartResourceAttributes.ServiceName; break; default: this.ParseHelper.UnexpectedAttribute(element, attrib); break; } } else { this.ParseHelper.ParseExtensionAttribute(this.Context.Extensions, intermediate, section, element, attrib); } } // Validate the attribute. if (id == null) { id = this.ParseHelper.CreateIdentifier("wrr", componentId, resource, attributes.ToString()); } if (!attributes.HasValue) { this.Messaging.Write(ErrorMessages.ExpectedAttributes(sourceLineNumbers, element.Name.LocalName, "Path", "ProcessName", "ServiceName")); } this.ParseHelper.ParseForExtensionElements(this.Context.Extensions, intermediate, section, element); if (!this.Messaging.EncounteredError) { this.ParseHelper.CreateCustomActionReference(sourceLineNumbers, section, "Wix4RegisterRestartResources", this.Context.Platform, CustomActionPlatforms.X86 | CustomActionPlatforms.X64 | CustomActionPlatforms.ARM64); section.AddSymbol(new WixRestartResourceSymbol(sourceLineNumbers, id) { ComponentRef = componentId, Resource = resource, Attributes = attributes, }); } } /// /// Parses a service configuration element. /// /// Element to parse. /// Identifier of parent component. /// Name of parent element. /// Optional name of service private void ParseServiceConfigElement(Intermediate intermediate, IntermediateSection section, XElement element, string componentId, string parentTableName, string parentTableServiceName) { var sourceLineNumbers = this.ParseHelper.GetSourceLineNumbers(element); string firstFailureActionType = null; var newService = false; string programCommandLine = null; string rebootMessage = null; int? resetPeriod = null; int? restartServiceDelay = null; string secondFailureActionType = null; string serviceName = null; string thirdFailureActionType = null; foreach (var attrib in element.Attributes()) { if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || this.Namespace == attrib.Name.Namespace) { switch (attrib.Name.LocalName) { case "FirstFailureActionType": firstFailureActionType = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib); break; case "ProgramCommandLine": programCommandLine = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib); break; case "RebootMessage": rebootMessage = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib); break; case "ResetPeriodInDays": resetPeriod = this.ParseHelper.GetAttributeIntegerValue(sourceLineNumbers, attrib, 0, Int32.MaxValue); break; case "RestartServiceDelayInSeconds": restartServiceDelay = this.ParseHelper.GetAttributeIntegerValue(sourceLineNumbers, attrib, 0, Int32.MaxValue); break; case "SecondFailureActionType": secondFailureActionType = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib); break; case "ServiceName": serviceName = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib); break; case "ThirdFailureActionType": thirdFailureActionType = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib); break; default: this.ParseHelper.UnexpectedAttribute(element, attrib); break; } } else { this.ParseHelper.ParseExtensionAttribute(this.Context.Extensions, intermediate, section, element, attrib); } } // if this element is a child of ServiceInstall then ignore the service name provided. if ("ServiceInstall" == parentTableName) { if (null == serviceName || parentTableServiceName == serviceName) { serviceName = parentTableServiceName; } else { this.Messaging.Write(ErrorMessages.IllegalAttributeWhenNested(sourceLineNumbers, element.Name.LocalName, "ServiceName", parentTableName)); } newService = true; } else { // not a child of ServiceInstall, so ServiceName must have been provided if (null == serviceName) { this.Messaging.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, element.Name.LocalName, "ServiceName")); } } var context = new Dictionary() { { "ServiceConfigComponentId", componentId }, { "ServiceConfigServiceName", serviceName } }; this.ParseHelper.ParseForExtensionElements(this.Context.Extensions, intermediate, section, element, context); if (!this.Messaging.EncounteredError) { this.ParseHelper.CreateCustomActionReference(sourceLineNumbers, section, "Wix4SchedServiceConfig", this.Context.Platform, CustomActionPlatforms.X86 | CustomActionPlatforms.X64 | CustomActionPlatforms.ARM64); section.AddSymbol(new ServiceConfigSymbol(sourceLineNumbers) { ServiceName = serviceName, ComponentRef = componentId, NewService = newService ? 1 : 0, FirstFailureActionType = firstFailureActionType, SecondFailureActionType = secondFailureActionType, ThirdFailureActionType = thirdFailureActionType, ResetPeriodInDays = resetPeriod, RestartServiceDelayInSeconds = restartServiceDelay, ProgramCommandLine = programCommandLine, RebootMessage = rebootMessage, }); } } /// /// Parses a touch file element. /// /// Element to parse. /// Identifier of parent component. /// Indicates whether the path is a 64-bit path. private void ParseTouchFileElement(Intermediate intermediate, IntermediateSection section, XElement element, string componentId, bool win64) { var sourceLineNumbers = this.ParseHelper.GetSourceLineNumbers(element); Identifier id = null; string path = null; var onInstall = YesNoType.NotSet; var onReinstall = YesNoType.NotSet; var onUninstall = YesNoType.NotSet; var nonvital = YesNoType.NotSet; int attributes = 0; foreach (var attrib in element.Attributes()) { if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || this.Namespace == attrib.Name.Namespace) { switch (attrib.Name.LocalName) { case "Id": id = this.ParseHelper.GetAttributeIdentifier(sourceLineNumbers, attrib); break; case "Path": path = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib); break; case "OnInstall": onInstall = this.ParseHelper.GetAttributeYesNoValue(sourceLineNumbers, attrib); break; case "OnReinstall": onReinstall = this.ParseHelper.GetAttributeYesNoValue(sourceLineNumbers, attrib); break; case "OnUninstall": onUninstall = this.ParseHelper.GetAttributeYesNoValue(sourceLineNumbers, attrib); break; case "Nonvital": nonvital = this.ParseHelper.GetAttributeYesNoValue(sourceLineNumbers, attrib); break; default: this.ParseHelper.UnexpectedAttribute(element, attrib); break; } } else { this.ParseHelper.ParseExtensionAttribute(this.Context.Extensions, intermediate, section, element, attrib); } } if (null == path) { this.Messaging.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, element.Name.LocalName, "Path")); } // If none of the scheduling actions are set, default to touching on install and reinstall. if (YesNoType.NotSet == onInstall && YesNoType.NotSet == onReinstall && YesNoType.NotSet == onUninstall) { onInstall = YesNoType.Yes; onReinstall = YesNoType.Yes; } attributes |= YesNoType.Yes == onInstall ? 0x1 : 0; attributes |= YesNoType.Yes == onReinstall ? 0x2 : 0; attributes |= YesNoType.Yes == onUninstall ? 0x4 : 0; attributes |= win64 ? 0x10 : 0; attributes |= YesNoType.Yes == nonvital ? 0 : 0x20; if (null == id) { id = this.ParseHelper.CreateIdentifier("tf", path, attributes.ToString()); } this.ParseHelper.ParseForExtensionElements(this.Context.Extensions, intermediate, section, element); if (!this.Messaging.EncounteredError) { section.AddSymbol(new WixTouchFileSymbol(sourceLineNumbers, id) { ComponentRef = componentId, Path = path, Attributes = attributes, }); this.ParseHelper.CreateCustomActionReference(sourceLineNumbers, section, "Wix4TouchFileDuringInstall", this.Context.Platform, CustomActionPlatforms.X86 | CustomActionPlatforms.X64 | CustomActionPlatforms.ARM64); } } /// /// Parses an user element. /// /// Element to parse. /// Optional identifier of parent component. private void ParseUserElement(Intermediate intermediate, IntermediateSection section, XElement element, string componentId) { var sourceLineNumbers = this.ParseHelper.GetSourceLineNumbers(element); Identifier id = null; int attributes = 0; string domain = null; string name = null; string comment = null; string password = null; foreach (var attrib in element.Attributes()) { if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || this.Namespace == attrib.Name.Namespace) { switch (attrib.Name.LocalName) { case "Id": id = this.ParseHelper.GetAttributeIdentifier(sourceLineNumbers, attrib); break; case "CanNotChangePassword": if (null == componentId) { this.Messaging.Write(UtilErrors.IllegalAttributeWithoutComponent(sourceLineNumbers, element.Name.LocalName, attrib.Name.LocalName)); } if (YesNoType.Yes == this.ParseHelper.GetAttributeYesNoValue(sourceLineNumbers, attrib)) { attributes |= UserPasswdCantChange; } break; case "Comment": if (null == componentId) { this.Messaging.Write(UtilErrors.IllegalAttributeWithoutComponent(sourceLineNumbers, element.Name.LocalName, attrib.Name.LocalName)); } comment = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib); break; case "CreateUser": if (null == componentId) { this.Messaging.Write(UtilErrors.IllegalAttributeWithoutComponent(sourceLineNumbers, element.Name.LocalName, attrib.Name.LocalName)); } if (YesNoType.No == this.ParseHelper.GetAttributeYesNoValue(sourceLineNumbers, attrib)) { attributes |= UserDontCreateUser; } break; case "Disabled": if (null == componentId) { this.Messaging.Write(UtilErrors.IllegalAttributeWithoutComponent(sourceLineNumbers, element.Name.LocalName, attrib.Name.LocalName)); } if (YesNoType.Yes == this.ParseHelper.GetAttributeYesNoValue(sourceLineNumbers, attrib)) { attributes |= UserDisableAccount; } break; case "Domain": domain = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib); break; case "FailIfExists": if (null == componentId) { this.Messaging.Write(UtilErrors.IllegalAttributeWithoutComponent(sourceLineNumbers, element.Name.LocalName, attrib.Name.LocalName)); } if (YesNoType.Yes == this.ParseHelper.GetAttributeYesNoValue(sourceLineNumbers, attrib)) { attributes |= UserFailIfExists; } break; case "LogonAsService": if (null == componentId) { this.Messaging.Write(UtilErrors.IllegalAttributeWithoutComponent(sourceLineNumbers, element.Name.LocalName, attrib.Name.LocalName)); } if (YesNoType.Yes == this.ParseHelper.GetAttributeYesNoValue(sourceLineNumbers, attrib)) { attributes |= UserLogonAsService; } break; case "LogonAsBatchJob": if (null == componentId) { this.Messaging.Write(UtilErrors.IllegalAttributeWithoutComponent(sourceLineNumbers, element.Name.LocalName, attrib.Name.LocalName)); } if (YesNoType.Yes == this.ParseHelper.GetAttributeYesNoValue(sourceLineNumbers, attrib)) { attributes |= UserLogonAsBatchJob; } break; case "Name": name = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib); break; case "Password": password = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib); break; case "PasswordExpired": if (null == componentId) { this.Messaging.Write(UtilErrors.IllegalAttributeWithoutComponent(sourceLineNumbers, element.Name.LocalName, attrib.Name.LocalName)); } if (YesNoType.Yes == this.ParseHelper.GetAttributeYesNoValue(sourceLineNumbers, attrib)) { attributes |= UserPasswdChangeReqdOnLogin; } break; case "PasswordNeverExpires": if (null == componentId) { this.Messaging.Write(UtilErrors.IllegalAttributeWithoutComponent(sourceLineNumbers, element.Name.LocalName, attrib.Name.LocalName)); } if (YesNoType.Yes == this.ParseHelper.GetAttributeYesNoValue(sourceLineNumbers, attrib)) { attributes |= UserDontExpirePasswrd; } break; case "RemoveComment": if (YesNoType.Yes == this.ParseHelper.GetAttributeYesNoValue(sourceLineNumbers, attrib)) { attributes |= UserRemoveComment; } break; case "RemoveOnUninstall": if (null == componentId) { this.Messaging.Write(UtilErrors.IllegalAttributeWithoutComponent(sourceLineNumbers, element.Name.LocalName, attrib.Name.LocalName)); } if (YesNoType.No == this.ParseHelper.GetAttributeYesNoValue(sourceLineNumbers, attrib)) { attributes |= UserDontRemoveOnUninstall; } break; case "UpdateIfExists": if (null == componentId) { this.Messaging.Write(UtilErrors.IllegalAttributeWithoutComponent(sourceLineNumbers, element.Name.LocalName, attrib.Name.LocalName)); } if (YesNoType.Yes == this.ParseHelper.GetAttributeYesNoValue(sourceLineNumbers, attrib)) { attributes |= UserUpdateIfExists; } break; case "Vital": if (null == componentId) { this.Messaging.Write(UtilErrors.IllegalAttributeWithoutComponent(sourceLineNumbers, element.Name.LocalName, attrib.Name.LocalName)); } if (YesNoType.No == this.ParseHelper.GetAttributeYesNoValue(sourceLineNumbers, attrib)) { attributes |= UserNonVital; } break; default: this.ParseHelper.UnexpectedAttribute(element, attrib); break; } } else { this.ParseHelper.ParseExtensionAttribute(this.Context.Extensions, intermediate, section, element, attrib); } } if (null == id) { id = this.ParseHelper.CreateIdentifier("usr", componentId, name); } if (null == name) { this.Messaging.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, element.Name.LocalName, "Name")); } if (null != comment && (UserRemoveComment & attributes) != 0) { this.Messaging.Write(ErrorMessages.IllegalAttributeWithOtherAttribute(sourceLineNumbers, element.Name.LocalName, "Comment", "RemoveComment")); } foreach (var child in element.Elements()) { if (this.Namespace == child.Name.Namespace) { switch (child.Name.LocalName) { case "GroupRef": if (null == componentId) { var childSourceLineNumbers = this.ParseHelper.GetSourceLineNumbers(child); this.Messaging.Write(UtilErrors.IllegalElementWithoutComponent(childSourceLineNumbers, child.Name.LocalName)); } this.ParseGroupRefElement(intermediate, section, child, id.Id, groupType: false); break; default: this.ParseHelper.UnexpectedElement(element, child); break; } } else { this.ParseHelper.ParseExtensionElement(this.Context.Extensions, intermediate, section, element, child); } } if (null != componentId) { this.ParseHelper.CreateCustomActionReference(sourceLineNumbers, section, "Wix4ConfigureUsers", this.Context.Platform, CustomActionPlatforms.X86 | CustomActionPlatforms.X64 | CustomActionPlatforms.ARM64); } if (!this.Messaging.EncounteredError) { section.AddSymbol(new UserSymbol(sourceLineNumbers, id) { ComponentRef = componentId, Name = name, Domain = domain, Password = password, Comment = comment, Attributes = attributes, }); } } /// /// Parses a XmlFile element. /// /// Element to parse. /// Identifier of parent component. private void ParseXmlFileElement(Intermediate intermediate, IntermediateSection section, XElement element, string componentId) { var sourceLineNumbers = this.ParseHelper.GetSourceLineNumbers(element); Identifier id = null; string file = null; string elementPath = null; string name = null; string value = null; int sequence = -1; int flags = 0; foreach (var attrib in element.Attributes()) { if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || this.Namespace == attrib.Name.Namespace) { switch (attrib.Name.LocalName) { case "Action": var actionValue = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib); switch (actionValue) { case "createElement": flags |= 0x00000001; // XMLFILE_CREATE_ELEMENT break; case "deleteValue": flags |= 0x00000002; // XMLFILE_DELETE_VALUE break; case "bulkSetValue": flags |= 0x00000004; // XMLFILE_BULKWRITE_VALUE break; case "setValue": // no flag for set value since it's the default break; default: this.Messaging.Write(ErrorMessages.IllegalAttributeValue(sourceLineNumbers, element.Name.LocalName, "Action", actionValue, "createElement", "deleteValue", "setValue", "bulkSetValue")); break; } break; case "SelectionLanguage": var selectionLanguage = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib); switch (selectionLanguage) { case "XPath": flags |= 0x00000100; // XMLFILE_USE_XPATH break; case "XSLPattern": // no flag for since it's the default break; default: this.Messaging.Write(ErrorMessages.IllegalAttributeValue(sourceLineNumbers, element.Name.LocalName, "SelectionLanguage", selectionLanguage, "XPath", "XSLPattern")); break; } break; case "Id": id = this.ParseHelper.GetAttributeIdentifier(sourceLineNumbers, attrib); break; case "File": file = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib); break; case "ElementPath": elementPath = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib); break; case "Name": name = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib); break; case "Permanent": if (YesNoType.Yes == this.ParseHelper.GetAttributeYesNoValue(sourceLineNumbers, attrib)) { flags |= 0x00010000; // XMLFILE_DONT_UNINSTALL } break; case "Sequence": sequence = this.ParseHelper.GetAttributeIntegerValue(sourceLineNumbers, attrib, 1, Int16.MaxValue); break; case "Value": value = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib); break; case "PreserveModifiedDate": if (YesNoType.Yes == this.ParseHelper.GetAttributeYesNoValue(sourceLineNumbers, attrib)) { flags |= 0x00001000; // XMLFILE_PRESERVE_MODIFIED } break; default: this.ParseHelper.UnexpectedAttribute(element, attrib); break; } } else { this.ParseHelper.ParseExtensionAttribute(this.Context.Extensions, intermediate, section, element, attrib); } } if (null == id) { id = this.ParseHelper.CreateIdentifier("uxf", componentId, file, elementPath, name); } if (null == file) { this.Messaging.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, element.Name.LocalName, "File")); } if (null == elementPath) { this.Messaging.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, element.Name.LocalName, "ElementPath")); } if ((0x00000001 /*XMLFILE_CREATE_ELEMENT*/ & flags) != 0 && null == name) { this.Messaging.Write(ErrorMessages.IllegalAttributeWithoutOtherAttributes(sourceLineNumbers, element.Name.LocalName, "Action", "Name")); } this.ParseHelper.ParseForExtensionElements(this.Context.Extensions, intermediate, section, element); if (!this.Messaging.EncounteredError) { var symbol = section.AddSymbol(new XmlFileSymbol(sourceLineNumbers, id) { File = file, ElementPath = elementPath, Name = name, Value = value, Flags = flags, ComponentRef = componentId, }); if (-1 != sequence) { symbol.Sequence = sequence; } } this.AddReferenceToSchedXmlFile(sourceLineNumbers, section); } /// /// Parses a XmlConfig element. /// /// Element to parse. /// Identifier of parent component. /// Whether or not the element is nested. private void ParseXmlConfigElement(Intermediate intermediate, IntermediateSection section, XElement element, string componentId, bool nested) { var sourceLineNumbers = this.ParseHelper.GetSourceLineNumbers(element); Identifier id = null; string elementId = null; string elementPath = null; int flags = 0; string file = null; string name = null; var sequence = CompilerConstants.IntegerNotSet; string value = null; string verifyPath = null; foreach (var attrib in element.Attributes()) { if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || this.Namespace == attrib.Name.Namespace) { switch (attrib.Name.LocalName) { case "Id": id = this.ParseHelper.GetAttributeIdentifier(sourceLineNumbers, attrib); break; case "Action": if (nested) { this.Messaging.Write(ErrorMessages.IllegalAttributeWhenNested(sourceLineNumbers, element.Name.LocalName, attrib.Name.LocalName, element.Parent.Name.LocalName)); } else { var actionValue = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib); switch (actionValue) { case "create": flags |= 0x10; // XMLCONFIG_CREATE break; case "delete": flags |= 0x20; // XMLCONFIG_DELETE break; default: this.Messaging.Write(ErrorMessages.IllegalAttributeValue(sourceLineNumbers, element.Name.LocalName, attrib.Name.LocalName, actionValue, "create", "delete")); break; } } break; case "ElementId": elementId = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib); break; case "ElementPath": elementPath = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib); break; case "File": file = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib); break; case "Name": name = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib); break; case "Node": if (nested) { this.Messaging.Write(ErrorMessages.IllegalAttributeWhenNested(sourceLineNumbers, element.Name.LocalName, attrib.Name.LocalName, element.Parent.Name.LocalName)); } else { var nodeValue = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib); switch (nodeValue) { case "element": flags |= 0x1; // XMLCONFIG_ELEMENT break; case "value": flags |= 0x2; // XMLCONFIG_VALUE break; case "document": flags |= 0x4; // XMLCONFIG_DOCUMENT break; default: this.Messaging.Write(ErrorMessages.IllegalAttributeValue(sourceLineNumbers, element.Name.LocalName, attrib.Name.LocalName, nodeValue, "element", "value", "document")); break; } } break; case "On": if (nested) { this.Messaging.Write(ErrorMessages.IllegalAttributeWhenNested(sourceLineNumbers, element.Name.LocalName, attrib.Name.LocalName, element.Parent.Name.LocalName)); } else { var onValue = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib); switch (onValue) { case "install": flags |= 0x100; // XMLCONFIG_INSTALL break; case "uninstall": flags |= 0x200; // XMLCONFIG_UNINSTALL break; default: this.Messaging.Write(ErrorMessages.IllegalAttributeValue(sourceLineNumbers, element.Name.LocalName, attrib.Name.LocalName, onValue, "install", "uninstall")); break; } } break; case "PreserveModifiedDate": if (YesNoType.Yes == this.ParseHelper.GetAttributeYesNoValue(sourceLineNumbers, attrib)) { flags |= 0x00001000; // XMLCONFIG_PRESERVE_MODIFIED } break; case "Sequence": sequence = this.ParseHelper.GetAttributeIntegerValue(sourceLineNumbers, attrib, 1, Int16.MaxValue); break; case "Value": value = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib); break; case "VerifyPath": verifyPath = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib); break; default: this.ParseHelper.UnexpectedAttribute(element, attrib); break; } } else { this.ParseHelper.ParseExtensionAttribute(this.Context.Extensions, intermediate, section, element, attrib); } } if (null == id) { id = this.ParseHelper.CreateIdentifier("uxc", componentId, file, elementId, elementPath); } if (null == file) { this.Messaging.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, element.Name.LocalName, "File")); } if (null == elementId && null == elementPath) { this.Messaging.Write(ErrorMessages.ExpectedAttributes(sourceLineNumbers, element.Name.LocalName, "ElementId", "ElementPath")); } else if (null != elementId) { if (null != elementPath) { this.Messaging.Write(ErrorMessages.IllegalAttributeWithOtherAttribute(sourceLineNumbers, element.Name.LocalName, "ElementId", "ElementPath")); } if (0 != flags) { this.Messaging.Write(ErrorMessages.IllegalAttributeWithOtherAttributes(sourceLineNumbers, element.Name.LocalName, "ElementId", "Action", "Node", "On")); } this.ParseHelper.CreateSimpleReference(section, sourceLineNumbers, UtilSymbolDefinitions.XmlConfig, elementId); } // find unexpected child elements foreach (var child in element.Elements()) { if (this.Namespace == child.Name.Namespace) { switch (child.Name.LocalName) { case "XmlConfig": if (nested) { this.Messaging.Write(ErrorMessages.UnexpectedElement(sourceLineNumbers, element.Name.LocalName, child.Name.LocalName)); } else { this.ParseXmlConfigElement(intermediate, section, child, componentId, true); } break; default: this.ParseHelper.UnexpectedElement(element, child); break; } } else { this.ParseHelper.ParseExtensionElement(this.Context.Extensions, intermediate, section, element, child); } } if (!this.Messaging.EncounteredError) { var symbol = section.AddSymbol(new XmlConfigSymbol(sourceLineNumbers, id) { File = file, ElementId = elementId, ElementPath = elementPath, VerifyPath = verifyPath, Name = name, Value = value, Flags = flags, ComponentRef = componentId, }); if (CompilerConstants.IntegerNotSet != sequence) { symbol.Sequence = sequence; } } this.ParseHelper.CreateCustomActionReference(sourceLineNumbers, section, "Wix4SchedXmlConfig", this.Context.Platform, CustomActionPlatforms.X86 | CustomActionPlatforms.X64 | CustomActionPlatforms.ARM64); } /// /// Match evaluator to escape properties in a string. /// private string EscapeProperties(Match match) { string escape = null; switch (match.Value) { case "[": escape = @"[\[]"; break; case "]": escape = @"[\]]"; break; } return escape; } private int CreateIntegerFromBitArray(BitArray bits) { if (32 != bits.Length) { throw new ArgumentException(String.Format("Can only convert a bit array with 32-bits to integer. Actual number of bits in array: {0}", bits.Length), "bits"); } var intArray = new int[1]; bits.CopyTo(intArray, 0); return intArray[0]; } private bool TrySetBitFromName(string[] attributeNames, string attributeName, YesNoType attributeValue, BitArray bits, int offset) { for (var i = 0; i < attributeNames.Length; i++) { if (attributeName.Equals(attributeNames[i], StringComparison.Ordinal)) { bits.Set(i + offset, YesNoType.Yes == attributeValue); return true; } } return false; } private void AddReferenceToSchedXmlFile(SourceLineNumber sourceLineNumbers, IntermediateSection section) { this.ParseHelper.CreateCustomActionReference(sourceLineNumbers, section, "Wix4SchedXmlFile", this.Context.Platform, CustomActionPlatforms.X86 | CustomActionPlatforms.X64 | CustomActionPlatforms.ARM64); } /// /// Private class that stores the data from a parsed PerformanceCounter element. /// private class ParsedPerformanceCounter { internal ParsedPerformanceCounter(string name, string help, System.Diagnostics.PerformanceCounterType type, int language) { this.Name = name; this.Help = help; this.Type = (int)type; this.Language = language.ToString("D3", CultureInfo.InvariantCulture); } internal string Name { get; } internal string Help { get; } internal int Type { get; } internal string Language { get; } } } }