From 2f792f6fb663edbdd6517565daf0ea8e0e11296b Mon Sep 17 00:00:00 2001 From: Jeremy Pritts <49847914+ds5678@users.noreply.github.com> Date: Wed, 22 Oct 2025 23:24:09 -0700 Subject: [PATCH] Improve Framework Id detection (#3581) * Support detecting .NET Core 1.0 and 1.1 * Formatting * Fix version number * Add support for using System.Private.CoreLib in version detection * Move mscorlib for consistency and readability * Ensure that netstandard is always checked before System.Runtime * Ensure that System.Runtime is always checked before netstandard * Formatting --- .../DotNetCorePathFinderExtensions.cs | 81 ++++++++++++------- 1 file changed, 54 insertions(+), 27 deletions(-) diff --git a/ICSharpCode.Decompiler/Metadata/DotNetCorePathFinderExtensions.cs b/ICSharpCode.Decompiler/Metadata/DotNetCorePathFinderExtensions.cs index fd620116e..7c8312c21 100644 --- a/ICSharpCode.Decompiler/Metadata/DotNetCorePathFinderExtensions.cs +++ b/ICSharpCode.Decompiler/Metadata/DotNetCorePathFinderExtensions.cs @@ -79,6 +79,19 @@ namespace ICSharpCode.Decompiler.Metadata return $".NETFramework,Version=v{assemblyDefinition.Version.ToString(2)}"; case "netstandard": return $".NETStandard,Version=v{assemblyDefinition.Version.ToString(2)}"; + case "System.Runtime": + case "System.Private.CoreLib": + { + string version = GetDotNetCoreVersion(assemblyDefinition.Version); + if (version != null) + { + return $".NETCoreApp,Version=v{version}"; + } + else + { + break; + } + } } } @@ -92,42 +105,20 @@ namespace ICSharpCode.Decompiler.Metadata string version; switch (metadata.GetString(r.Name)) { - case "netstandard": + case "mscorlib": version = r.Version.ToString(2); - return $".NETStandard,Version=v{version}"; + return $".NETFramework,Version=v{version}"; case "System.Runtime": - // System.Runtime.dll uses the following scheme: - // 4.2.0 => .NET Core 2.0 - // 4.2.1 => .NET Core 2.1 / 3.0 - // 4.2.2 => .NET Core 3.1 - // 5.0.0+ => .NET 5+ - if (r.Version >= new Version(4, 2, 0)) + case "System.Private.CoreLib": + version = GetDotNetCoreVersion(r.Version); + if (version != null) { - if (r.Version.Major >= 5) - { - version = r.Version.ToString(2); - } - else if (r.Version.Major == 4 && r.Version.Minor == 2) - { - version = r.Version.Build switch { - <= 0 => "2.0", - 1 => "3.0", - _ => "3.1" - }; - } - else - { - version = "2.0"; - } return $".NETCoreApp,Version=v{version}"; } else { continue; } - case "mscorlib": - version = r.Version.ToString(2); - return $".NETFramework,Version=v{version}"; } } catch (BadImageFormatException) @@ -136,6 +127,24 @@ namespace ICSharpCode.Decompiler.Metadata } } + // We check for netstandard separately because .NET Core/Framework assemblies can reference it. + foreach (var h in metadata.AssemblyReferences) + { + try + { + var r = metadata.GetAssemblyReference(h); + if (r.PublicKeyOrToken.IsNil || metadata.GetString(r.Name) is not "netstandard") + continue; + + string version = r.Version.ToString(2); + return $".NETStandard,Version=v{version}"; + } + catch (BadImageFormatException) + { + // ignore malformed references + } + } + // Optionally try to detect target version through assembly path as a fallback (use case: reference assemblies) if (assemblyPath != null) { @@ -185,6 +194,24 @@ namespace ICSharpCode.Decompiler.Metadata return string.Empty; } + static string GetDotNetCoreVersion(Version assemblyVersion) + { + // System.Runtime.dll and System.Private.CoreLib.dll use the following scheme: + // 4.1.0 => .NET Core 1.0 / 1.1 + // 4.2.0 => .NET Core 2.0 + // 4.2.1 => .NET Core 2.1 / 3.0 + // 4.2.2 => .NET Core 3.1 + // 5.0.0+ => .NET 5+ + return (assemblyVersion.Major, assemblyVersion.Minor, assemblyVersion.Build) switch { + (4, 1, 0) => "1.1", + (4, 2, 0) => "2.0", + (4, 2, 1) => "3.0", + (4, 2, 2) => "3.1", + ( >= 5, _, _) => assemblyVersion.ToString(2), + _ => null + }; + } + public static bool IsReferenceAssembly(this MetadataFile assembly) { return IsReferenceAssembly(assembly.Metadata, assembly.FileName);