| File: GenerateApplicationManifest.cs | Web Access |
| Project: src\msbuild\src\Tasks\Microsoft.Build.Tasks.csproj (Microsoft.Build.Tasks.Core) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System; using System.Diagnostics; using System.IO; using System.Runtime.Versioning; using System.Xml; using Microsoft.Build.Framework; using Microsoft.Build.Tasks.Deployment.ManifestUtilities; using Constants = Microsoft.Build.Tasks.Deployment.ManifestUtilities.Constants; #nullable disable namespace Microsoft.Build.Tasks { /// <summary> /// Generates an application manifest for ClickOnce projects. /// </summary> [SupportedOSPlatform("windows")] [MSBuildMultiThreadableTask] public sealed class GenerateApplicationManifest : GenerateManifestBase { private enum _ManifestType { Native, ClickOnce, } private ITaskItem[] _dependencies; private ITaskItem[] _files; private ITaskItem[] _isolatedComReferences; private _ManifestType _manifestType = _ManifestType.ClickOnce; private ITaskItem[] _fileAssociations; private bool _useApplicationTrust; public string ClrVersion { get; set; } public ITaskItem ConfigFile { get; set; } public ITaskItem[] Dependencies { get => _dependencies; set => _dependencies = value; } public string ErrorReportUrl { get; set; } public ITaskItem[] FileAssociations { get { // File associations are only valid when targeting 3.5 or later if (Util.CompareFrameworkVersions(TargetFrameworkVersion, Constants.TargetFrameworkVersion35) < 0) { return null; } return _fileAssociations; } set => _fileAssociations = value; } public ITaskItem[] Files { get => _files; set => _files = value; } public bool HostInBrowser { get; set; } public ITaskItem IconFile { get; set; } public ITaskItem[] IsolatedComReferences { get => _isolatedComReferences; set => _isolatedComReferences = value; } public string ManifestType { get; set; } public string OSVersion { get; set; } public string Product { get; set; } public string Publisher { get; set; } public bool RequiresMinimumFramework35SP1 { get; set; } public string SuiteName { get; set; } public string SupportUrl { get; set; } public string TargetFrameworkSubset { get; set; } = String.Empty; public string TargetFrameworkProfile { get; set; } = String.Empty; public ITaskItem TrustInfoFile { get; set; } public bool UseApplicationTrust { get { // Use application trust is only used if targeting v3.5 or later if (Util.CompareFrameworkVersions(TargetFrameworkVersion, Constants.TargetFrameworkVersion35) < 0) { return false; } return _useApplicationTrust; } set => _useApplicationTrust = value; } public override bool Execute() { if (!NativeMethodsShared.IsWindows) { Log.LogErrorWithCodeFromResources("General.TaskRequiresWindows", nameof(GenerateApplicationManifest)); return false; } return base.Execute(); } protected override Type GetObjectType() { return typeof(ApplicationManifest); } protected override bool OnManifestLoaded(Manifest manifest) { return BuildApplicationManifest(manifest as ApplicationManifest); } protected override bool OnManifestResolved(Manifest manifest) { if (UseApplicationTrust) { return BuildResolvedSettings(manifest as ApplicationManifest); } return true; } private bool BuildApplicationManifest(ApplicationManifest manifest) { _dependencies = Util.SortItems(_dependencies, TaskEnvironment); _files = Util.SortItems(_files, TaskEnvironment); _isolatedComReferences = Util.SortItems(_isolatedComReferences, TaskEnvironment); if (Dependencies != null) { foreach (ITaskItem item in Dependencies) { if (LauncherBasedDeployment) { // In Launcher-based deployments, everything needs to be a regular file. AddFileFromItem(item); } else { AddAssemblyFromItem(item); } } } if (Files != null) { foreach (ITaskItem item in Files) { AddFileFromItem(item); } } // Build ClickOnce info... manifest.IsClickOnceManifest = _manifestType == _ManifestType.ClickOnce; if (manifest.IsClickOnceManifest) { if (manifest.EntryPoint == null && Util.CompareFrameworkVersions(TargetFrameworkVersion, Constants.TargetFrameworkVersion35) < 0) { Log.LogErrorWithCodeFromResources("GenerateManifest.NoEntryPoint"); return false; } if (!AddClickOnceFiles(manifest)) { return false; } if (!AddClickOnceFileAssociations(manifest)) { return false; } } if (HostInBrowser && Util.CompareFrameworkVersions(TargetFrameworkVersion, Constants.TargetFrameworkVersion30) < 0) { Log.LogErrorWithCodeFromResources("GenerateManifest.HostInBrowserInvalidFrameworkVersion"); return false; } // Build isolated COM info... if (!AddIsolatedComReferences(manifest)) { return false; } manifest.MaxTargetPath = MaxTargetPath; manifest.HostInBrowser = HostInBrowser; manifest.UseApplicationTrust = UseApplicationTrust; if (UseApplicationTrust && SupportUrl != null) { manifest.SupportUrl = SupportUrl; } if (UseApplicationTrust && SuiteName != null) { manifest.SuiteName = SuiteName; } if (UseApplicationTrust && ErrorReportUrl != null) { manifest.ErrorReportUrl = ErrorReportUrl; } return true; } private bool AddIsolatedComReferences(ApplicationManifest manifest) { int t1 = Environment.TickCount; bool success = true; if (IsolatedComReferences != null) { foreach (ITaskItem item in IsolatedComReferences) { string name = item.GetMetadata("Name"); if (String.IsNullOrEmpty(name)) { name = Path.GetFileName(item.ItemSpec); } FileReference file = AddFileFromItem(item); if (!file.ImportComComponent(TaskEnvironment.GetAbsolutePath(item.ItemSpec), manifest.OutputMessages, name)) { success = false; } } } Util.WriteLog($"GenerateApplicationManifest.AddIsolatedComReferences t={Environment.TickCount - t1}"); return success; } private bool AddClickOnceFileAssociations(ApplicationManifest manifest) { if (FileAssociations != null) { foreach (ITaskItem item in FileAssociations) { var fileAssociation = new FileAssociation { DefaultIcon = item.GetMetadata("DefaultIcon"), Description = item.GetMetadata("Description"), Extension = item.ItemSpec, ProgId = item.GetMetadata("Progid") }; manifest.FileAssociations.Add(fileAssociation); } } return true; } private bool AddClickOnceFiles(ApplicationManifest manifest) { int t1 = Environment.TickCount; if (!String.IsNullOrEmpty(ConfigFile?.ItemSpec)) { manifest.ConfigFile = FindFileFromItem(ConfigFile).TargetPath; } if (!String.IsNullOrEmpty(IconFile?.ItemSpec)) { manifest.IconFile = FindFileFromItem(IconFile).TargetPath; } if (!String.IsNullOrEmpty(TrustInfoFile?.ItemSpec)) { AbsolutePath trustInfoPath = TaskEnvironment.GetAbsolutePath(TrustInfoFile.ItemSpec); manifest.TrustInfo = new TrustInfo(); manifest.TrustInfo.Read(trustInfoPath); } if (manifest.TrustInfo == null) { manifest.TrustInfo = new TrustInfo(); } if (OSVersion != null) { manifest.OSVersion = OSVersion; } if (ClrVersion != null) { AssemblyReference clrPlatformAssembly = manifest.AssemblyReferences.Find(Constants.CLRPlatformAssemblyName); if (clrPlatformAssembly == null) { clrPlatformAssembly = new AssemblyReference { IsPrerequisite = true }; manifest.AssemblyReferences.Add(clrPlatformAssembly); } clrPlatformAssembly.AssemblyIdentity = new AssemblyIdentity(Constants.CLRPlatformAssemblyName, ClrVersion); } if (Util.CompareFrameworkVersions(TargetFrameworkVersion, Constants.TargetFrameworkVersion30) == 0) { EnsureAssemblyReferenceExists(manifest, CreateAssemblyIdentity(Constants.NET30AssemblyIdentity)); } else if (Util.CompareFrameworkVersions(TargetFrameworkVersion, Constants.TargetFrameworkVersion35) == 0) { EnsureAssemblyReferenceExists(manifest, CreateAssemblyIdentity(Constants.NET30AssemblyIdentity)); EnsureAssemblyReferenceExists(manifest, CreateAssemblyIdentity(Constants.NET35AssemblyIdentity)); if ((!String.IsNullOrEmpty(TargetFrameworkSubset) && TargetFrameworkSubset.Equals(Constants.ClientFrameworkSubset, StringComparison.OrdinalIgnoreCase)) || (!String.IsNullOrEmpty(TargetFrameworkProfile) && TargetFrameworkProfile.Equals(Constants.ClientFrameworkSubset, StringComparison.OrdinalIgnoreCase))) { EnsureAssemblyReferenceExists(manifest, CreateAssemblyIdentity(Constants.NET35ClientAssemblyIdentity)); } else if (RequiresMinimumFramework35SP1) { EnsureAssemblyReferenceExists(manifest, CreateAssemblyIdentity(Constants.NET35SP1AssemblyIdentity)); } } Util.WriteLog($"GenerateApplicationManifest.AddClickOnceFiles t={Environment.TickCount - t1}"); return true; } protected internal override bool ValidateInputs() { bool valid = base.ValidateInputs(); if (ManifestType != null) { try { _manifestType = (_ManifestType)Enum.Parse(typeof(_ManifestType), ManifestType, true); } catch (FormatException) { Log.LogErrorWithCodeFromResources("GenerateManifest.InvalidValue", "ManifestType"); valid = false; } catch (ArgumentException) { Log.LogErrorWithCodeFromResources("GenerateManifest.InvalidValue", "ManifestType"); valid = false; } if (_manifestType == _ManifestType.Native) { EntryPoint = null; // EntryPoint is ignored if ManifestType="Native" } } if (ClrVersion != null && !Util.IsValidVersion(ClrVersion, 4)) { Log.LogErrorWithCodeFromResources("GenerateManifest.InvalidValue", "ClrVersion"); valid = false; } if (OSVersion != null && !Util.IsValidVersion(OSVersion, 4)) { Log.LogErrorWithCodeFromResources("GenerateManifest.InvalidValue", "OSVersion"); valid = false; } if (!Util.IsValidFrameworkVersion(TargetFrameworkVersion) || Util.CompareFrameworkVersions(TargetFrameworkVersion, Constants.TargetFrameworkVersion20) < 0) { Log.LogErrorWithCodeFromResources("GenerateManifest.InvalidValue", "TargetFrameworkVersion"); valid = false; } if (_manifestType == _ManifestType.ClickOnce) { // ClickOnce supports asInvoker privilege only. if (GetRequestedExecutionLevel(out string requestedExecutionLevel) && String.CompareOrdinal(requestedExecutionLevel, Constants.UACAsInvoker) != 0) { Log.LogErrorWithCodeFromResources("GenerateManifest.InvalidRequestedExecutionLevel", requestedExecutionLevel); valid = false; } } return valid; } private bool BuildResolvedSettings(ApplicationManifest manifest) { // Note: if changing the logic in this function, please update the logic in // GenerateDeploymentManifest.BuildResolvedSettings as well. if (Product != null) { manifest.Product = Product; } else if (String.IsNullOrEmpty(manifest.Product)) { manifest.Product = Path.GetFileNameWithoutExtension(manifest.AssemblyIdentity.Name); } Debug.Assert(!String.IsNullOrEmpty(manifest.Product)); if (Publisher != null) { manifest.Publisher = Publisher; } else if (String.IsNullOrEmpty(manifest.Publisher)) { string org = Util.GetRegisteredOrganization(); if (!String.IsNullOrEmpty(org)) { manifest.Publisher = org; } else { manifest.Publisher = manifest.Product; } } Debug.Assert(!String.IsNullOrEmpty(manifest.Publisher)); return true; } private static AssemblyIdentity CreateAssemblyIdentity(string[] values) { if (values.Length != 5) { return null; } return new AssemblyIdentity(values[0], values[1], values[2], values[3], values[4]); } private static void EnsureAssemblyReferenceExists(ApplicationManifest manifest, AssemblyIdentity identity) { if (manifest.AssemblyReferences.Find(identity) == null) { var assembly = new AssemblyReference { IsPrerequisite = true, AssemblyIdentity = identity }; manifest.AssemblyReferences.Add(assembly); } } private bool GetRequestedExecutionLevel(out string requestedExecutionLevel) { requestedExecutionLevel = Constants.UACAsInvoker; // For backwards compatibility we assume asInvoker to begin with. if (String.IsNullOrEmpty(InputManifest?.ItemSpec) || String.CompareOrdinal(InputManifest.ItemSpec, "NoManifest") == 0) { return false; } try { AbsolutePath inputManifestPath = TaskEnvironment.GetAbsolutePath(InputManifest.ItemSpec); using (Stream s = File.Open(inputManifestPath, FileMode.Open, FileAccess.Read, FileShare.Read)) { var document = new XmlDocument(); var xrSettings = new XmlReaderSettings { DtdProcessing = DtdProcessing.Ignore }; using (XmlReader xr = XmlReader.Create(s, xrSettings)) { document.Load(xr); // Create an XmlNamespaceManager for resolving namespaces. var nsmgr = XmlNamespaces.GetNamespaceManager(document.NameTable); XmlNode node = (XmlElement)document.SelectSingleNode(XPaths.requestedExecutionLevelPath, nsmgr); if (node != null) { var attr = (XmlAttribute)(node.Attributes.GetNamedItem("level")); if (attr != null) { requestedExecutionLevel = attr.Value; } } } return true; } } catch (Exception ex) { Log.LogErrorWithCodeFromResources("GenerateManifest.ReadInputManifestFailed", InputManifest.ItemSpec, ex.Message); return false; } } } }