/* Copyright (C) 2014-2019 de4dot@gmail.com This file is part of dnSpy dnSpy is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. dnSpy is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with dnSpy. If not, see . */ using System; using System.Collections.Generic; using System.Diagnostics; using dnlib.DotNet; namespace dnSpy.Decompiler { readonly struct TargetFrameworkInfo { /// /// true if is .NET Framework /// public bool IsDotNetFramework => Framework == ".NETFramework"; /// /// Framework, eg. ".NETFramework". This is stored in a TargetFrameworkIdentifier tag /// in the project file. /// public string Framework { get; } /// /// Version, eg. "4.5". This is stored in a TargetFrameworkVersion tag /// in the project file. /// public string Version { get; } /// /// Profile eg. "Client" or null. This is stored in a TargetFrameworkProfile tag /// in the project file. /// public string? Profile { get; } /// /// true if the info is from /// public bool FromAttribute { get; } TargetFrameworkInfo(string framework, string version, string? profile, bool fromAttribute) { Framework = framework ?? throw new ArgumentNullException(nameof(framework)); Version = version ?? throw new ArgumentNullException(nameof(version)); Profile = profile; FromAttribute = fromAttribute; } public static TargetFrameworkInfo Create(ModuleDef module) { var asm = module.Assembly; if (asm is not null && module.IsManifestModule) { var info = TryGetTargetFrameworkInfoInternal(asm); if (info is not null) return info.Value; } const string framework = ".NETFramework"; if (module.IsClr10) return new TargetFrameworkInfo(framework, "1.0", null, false); if (module.IsClr11) return new TargetFrameworkInfo(framework, "1.1", null, false); if (module.IsClr20) return new TargetFrameworkInfo(framework, GetDotNetVersion2035(module), null, false); if (module.IsClr40) return new TargetFrameworkInfo(framework, "4.0", null, false); return new TargetFrameworkInfo(framework, "4.0", null, false); } static TargetFrameworkInfo? TryGetTargetFrameworkInfoInternal(AssemblyDef asm) { var ca = asm.CustomAttributes.Find("System.Runtime.Versioning.TargetFrameworkAttribute"); if (ca is null) return null; if (ca.ConstructorArguments.Count != 1) return null; var arg = ca.ConstructorArguments[0]; if (arg.Type.GetElementType() != ElementType.String) return null; var s = arg.Value as UTF8String; if (UTF8String.IsNullOrEmpty(s)) return null; return TryCreateFromAttributeString(s); } static TargetFrameworkInfo? TryCreateFromAttributeString(string attrString) { // See corclr/src/mscorlib/src/System/Runtime/Versioning/BinaryCompatibility.cs var values = attrString.Split(new char[] { ',' }); if (values.Length < 2 || values.Length > 3) return null; var framework = values[0].Trim(); if (framework.Length == 0) return null; string? versionStr = null; string? profile = null; for (int i = 1; i < values.Length; i++) { var kvp = values[i].Split('='); if (kvp.Length != 2) return null; var key = kvp[0].Trim(); var value = kvp[1].Trim(); if (key.Equals("Version", StringComparison.OrdinalIgnoreCase)) { if (value.StartsWith("v", StringComparison.OrdinalIgnoreCase)) value = value.Substring(1); versionStr = value; if (!System.Version.TryParse(value, out var version)) return null; } else if (key.Equals("Profile", StringComparison.OrdinalIgnoreCase)) { if (!string.IsNullOrEmpty(value)) profile = value; } } if (versionStr is null || versionStr.Length == 0) return null; return new TargetFrameworkInfo(framework, versionStr, profile, true); } static HashSet dotNet30Asms = new HashSet(StringComparer.OrdinalIgnoreCase) { "ComSvcConfig, Version=3.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", "infocard, Version=3.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", "Microsoft.Transactions.Bridge, Version=3.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", "Microsoft.Transactions.Bridge.Dtc, Version=3.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", "PresentationBuildTasks, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", "PresentationCFFRasterizer, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", "PresentationCore, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", "PresentationFramework, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", "PresentationFramework.Aero, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", "PresentationFramework.Classic, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", "PresentationFramework.Luna, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", "PresentationFramework.Royale, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", "PresentationUI, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", "ReachFramework, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", "ServiceModelReg, Version=3.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", "SMSvcHost, Version=3.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", "System.IdentityModel, Version=3.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", "System.IdentityModel.Selectors, Version=3.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", "System.IO.Log, Version=3.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", "System.Printing, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", "System.Runtime.Serialization, Version=3.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", "System.ServiceModel, Version=3.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", "System.ServiceModel.Install, Version=3.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", "System.ServiceModel.WasHosting, Version=3.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", "System.Speech, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", "System.Workflow.Activities, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", "System.Workflow.ComponentModel, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", "System.Workflow.Runtime, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", "UIAutomationClient, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", "UIAutomationClientsideProviders, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", "UIAutomationProvider, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", "UIAutomationTypes, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", "WindowsBase, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", "WindowsFormsIntegration, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", "WsatConfig, Version=3.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", }; static HashSet dotNet35Asms = new HashSet(StringComparer.OrdinalIgnoreCase) { "AddInProcess, Version=3.5.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", "AddInProcess32, Version=3.5.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", "AddInUtil, Version=3.5.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", "DataSvcUtil, Version=3.5.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", "EdmGen, Version=3.5.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", "Microsoft.Build.Conversion.v3.5, Version=3.5.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", "Microsoft.Build.Engine, Version=3.5.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", "Microsoft.Build.Framework, Version=3.5.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", "Microsoft.Build.Tasks.v3.5, Version=3.5.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", "Microsoft.Build.Utilities.v3.5, Version=3.5.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", "Microsoft.Data.Entity.Build.Tasks, Version=3.5.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", "Microsoft.VisualC.STLCLR, Version=1.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", "MSBuild, Version=3.5.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", "Sentinel.v3.5Client, Version=3.5.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", "System.AddIn, Version=3.5.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", "System.AddIn.Contract, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", "System.ComponentModel.DataAnnotations, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", "System.Core, Version=3.5.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", "System.Data.DataSetExtensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", "System.Data.Entity, Version=3.5.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", "System.Data.Entity.Design, Version=3.5.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", "System.Data.Linq, Version=3.5.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", "System.Data.Services, Version=3.5.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", "System.Data.Services.Client, Version=3.5.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", "System.Data.Services.Design, Version=3.5.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", "System.DirectoryServices.AccountManagement, Version=3.5.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", "System.Management.Instrumentation, Version=3.5.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", "System.Net, Version=3.5.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", "System.ServiceModel.Web, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", "System.Web.Abstractions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", "System.Web.DynamicData, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", "System.Web.DynamicData.Design, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", "System.Web.Entity, Version=3.5.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", "System.Web.Entity.Design, Version=3.5.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", "System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", "System.Web.Extensions.Design, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", "System.Web.Routing, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", "System.Windows.Presentation, Version=3.5.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", "System.WorkflowServices, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35", "System.Xml.Linq, Version=3.5.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", }; // If input module is V35, use it, else check all its assembly references for version and // use highest version found. It doesn't try to check the asm refs of the asm refs, this // should be enough. static string GetDotNetVersion2035(ModuleDef module) { Dnr2035Version ver = Dnr2035Version.V20; foreach (var m in GetModules(module)) { ver = Max(ver, GetDotNetVersion2035Internal(m)); if (ver == Dnr2035Version.V35) return ToString(ver); } return ToString(ver); } static IEnumerable GetModules(ModuleDef module) { yield return module; foreach (var asmRef in module.GetAssemblyRefs()) { var asm = module.Context.AssemblyResolver.Resolve(asmRef, module); if (asm is not null) yield return asm.ManifestModule; } } enum Dnr2035Version { V20, V30, V35, } static Dnr2035Version Max(Dnr2035Version a, Dnr2035Version b) => a > b ? a : b; static string ToString(Dnr2035Version v) { switch (v) { case Dnr2035Version.V20: return "2.0"; case Dnr2035Version.V30: return "3.0"; case Dnr2035Version.V35: return "3.5"; default: throw new InvalidOperationException(); } } static Dnr2035Version GetDotNetVersion2035Internal(ModuleDef module) { var ver = Dnr2035Version.V20; foreach (var r in module.GetAssemblyRefs()) { if (dotNet35Asms.Contains(r.FullName)) return Dnr2035Version.V35; if (dotNet30Asms.Contains(r.FullName)) ver = Dnr2035Version.V30; } var asm = module.Assembly; if (asm is not null && module.IsManifestModule) { if (dotNet35Asms.Contains(asm.FullName)) return Dnr2035Version.V35; if (dotNet30Asms.Contains(asm.FullName)) ver = Dnr2035Version.V30; } return ver; } string? GetDisplayName() { if (Framework is null) return null; var name = GetFrameworkDisplayName(); if (name is null) return null; if (!string.IsNullOrEmpty(Profile)) name = name + " (" + Profile + ")"; return name; } string? GetFrameworkDisplayName() { switch (Framework) { case ".NETFramework": string v = Version; if (v == "4.0") v = "4"; return ".NET Framework " + v; case ".NETPortable": return ".NET Portable " + Version; case ".NETCore": return "Windows Universal " + Version; case ".NETCoreApp": if (Version.StartsWith("1.") || Version.StartsWith("2.") || Version.StartsWith("3.")) { // .NET Core 1.0-3.x return ".NET Core " + Version; } // .NET 5.0+ return ".NET " + Version; case ".NETPlatform": return ".NET Platform " + Version; case ".NETStandard": return ".NET Standard " + Version; case ".NETStandardApp": return ".NET Standard App " + Version; case "DNX": return "DNX " + Version; case "DNXCore": return "DNX Core " + Version; case "WindowsPhone": return "Windows Phone " + Version; case "WindowsPhoneApp": return "Windows Phone App " + Version; case "UAP": return "Universal App " + Version; case "Silverlight": return "Silverlight " + Version; case ".NETMicroFramework": return ".NET Micro Framework " + Version; case "WinRT": return "WinRT " + Version; case "Windows": return "Windows " + Version; case "CoreCLR": return "Core CLR " + Version; case "ASP.Net": case "ASP.NET": return "ASP.NET " + Version; case "ASP.NetCore": case "ASP.NETCore": return "ASP.NET Core " + Version; case "native": return "native " + Version; case "MonoAndroid": return "Mono Android " + Version; case "MonoTouch": return "Mono Touch " + Version; case "MonoMac": return "Mono Mac " + Version; case "Xamarin.iOS": return "Xamarin iOS " + Version; case "Xamarin.Mac": return "Xamarin Mac " + Version; case "Xamarin.PlayStation3": return "Xamarin PlayStation 3 " + Version; case "Xamarin.PlayStation4": return "Xamarin PlayStation 4 " + Version; case "Xamarin.PlayStationVita": return "Xamarin PlayStation Vita " + Version; case "Xamarin.Xbox360": return "Xamarin Xbox 360 " + Version; case "Xamarin.XboxOne": return "Xamarin Xbox One " + Version; case "Xamarin.TVOS": return "Xamarin TVOS " + Version; case "Xamarin.WatchOS": return "Xamarin WatchOS " + Version; default: Debug.Fail("Unknown target framework: " + Framework); if (Framework.Length > 20) return null; return Framework + " " + Version; } } public override string? ToString() => GetDisplayName(); } }