[C#] 実行中の .NET バージョンを取得する方法
実行中のプロセスの .NET のバージョンを取得する方法です。.NET Core 以降と .NET Framework では異なる点があり、.NET Framework では注意が必要です。
環境
- .NET 8.0.8 (SDK 8.0.400)
- C# 12.0
- Visual Studio 2022 Version 17.11.3
- Windows 11 Pro 23H2 22631.4169
前提
.NET 8 から .NET Framework 4.8 まで、以下のように csproj に設定して検証しました。
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFrameworks>net8.0;net7.0;net6.0;net5.0;netcoreapp3.1;net48</TargetFrameworks>
</PropertyGroup>
</Project>
取得結果
以下のプロパティを使用して、実行中の .NET バージョンを取得した結果です。
- Environment.Version (
System.Version
) - RuntimeInformation.FrameworkDescription (
System.String
) - AppContext.TargetFrameworkName (
System.String?
)- ⚠️ Assembly.GetEntryAssembly() の結果に依存する点に注意
実行環境 | Version | FrameworkDescription | TargetFrameworkName |
---|---|---|---|
.NET 8 | 8.0.8 | .NET 8.0.8 | .NETCoreApp,Version=v8.0 |
.NET 7 | 7.0.20 | .NET 7.0.20 | .NETCoreApp,Version=v7.0 |
.NET 6 | 6.0.33 | .NET 6.0.33 | .NETCoreApp,Version=v6.0 |
.NET 5 | 5.0.17 | .NET 5.0.17 | .NETCoreApp,Version=v5.0 |
.NET Core 3.1 | 3.1.32 | .NET Core 3.1.32 | .NETCoreApp,Version=v3.1 |
.NET Framework 4.8.1 | 4.0.30319.42000 | .NET Framework 4.8.9261.0 | .NETFramework,Version=v4.8 |
⚠️ .NET Framework は上記の方法は非推奨のため、正確に取得するにはレジストリを参照する方法が良いでしょう。
https://learn.microsoft.com/ja-jp/dotnet/framework/migration-guide/how-to-determine-which-versions-are-installed
ついでに、以下のプロパティの取得結果です。
- RuntimeInformation.RuntimeIdentifier (.NET 5.0 以降)
- RuntimeInformation.ProcessArchitecture
- RuntimeInformation.OSArchitecture
- RuntimeInformation.OSDescription
- Environment.OSVersion
実行環境 | RuntimeIdentifier | ProcessArchitecture | OSArchitecture | OSDescription | OSVersion |
---|---|---|---|---|---|
.NET 8 | win-x64 | X64 | X64 | Microsoft Windows 10.0.22631 | Microsoft Windows NT 10.0.22631.0 |
.NET 7 | win10-x64 | X64 | X64 | Microsoft Windows 10.0.22631 | Microsoft Windows NT 10.0.22631.0 |
.NET 6 | win10-x64 | X64 | X64 | Microsoft Windows 10.0.22631 | Microsoft Windows NT 10.0.22631.0 |
.NET 5 | win10-x64 | X64 | X64 | Microsoft Windows 10.0.22631 | Microsoft Windows NT 10.0.22631.0 |
.NET Core 3.1 | N/A | X64 | X64 | Microsoft Windows 10.0.22631 | Microsoft Windows NT 6.2.9200.0 |
.NET Framework 4.8.1 | N/A | X64 | X64 | Microsoft Windows 10.0.22631 | Microsoft Windows NT 6.2.9200.0 |
Environment.Version, RuntimeInformation.FrameworkDescription について
.NET Core 3.1 以降では同じバージョン値が取得できています。
実際に取得するソースコードを抜粋すると、以下のようになっているようです。
.NET 5 以降のソースコード
typeof(object).Assembly.GetCustomAttribute<AssemblyInformationalVersionAttribute>()?.InformationalVersion
で取得します。Version
型で取得するか、string
の".NET x.y.z"
の形式で取得するかが異なります。
// .NET 5 以降の Environment.Version のコード抜粋
public static Version Version
{
get
{
string? versionString = typeof(object).Assembly.GetCustomAttribute<AssemblyInformationalVersionAttribute>()?.InformationalVersion;
ReadOnlySpan<char> versionSpan = versionString.AsSpan();
// Strip optional suffixes
int separatorIndex = versionSpan.IndexOfAny('-', '+', ' ');
if (separatorIndex != -1)
versionSpan = versionSpan.Slice(0, separatorIndex);
// Return zeros rather then failing if the version string fails to parse
return Version.TryParse(versionSpan, out Version? version) ? version : new Version();
}
}
// .NET 5 以降の RuntimeInformation.FrameworkDescription のコード抜粋
private const string FrameworkName = ".NET";
private static string? s_frameworkDescription;
public static string FrameworkDescription
{
get
{
if (s_frameworkDescription == null)
{
ReadOnlySpan<char> versionString = typeof(object).Assembly.GetCustomAttribute<AssemblyInformationalVersionAttribute>()?.InformationalVersion;
// Strip the git hash if there is one
int plusIndex = versionString.IndexOf('+');
if (plusIndex >= 0)
{
versionString = versionString.Slice(0, plusIndex);
}
s_frameworkDescription = !versionString.Trim().IsEmpty ? $"{FrameworkName} {versionString}" : FrameworkName;
}
return s_frameworkDescription;
}
}
.NET Core 3.1 のソースコード
typeof(object).Assembly.GetCustomAttribute<AssemblyInformationalVersionAttribute>()?.InformationalVersion
で取得します (.NET 5 以降同様)。Version
型で取得するか、string
の".NET Core x.y.z"
の形式で取得するかが異なります。- ただし、
AppContext.GetData("FX_PRODUCT_VERSION")
が設定されている場合は優先されます。
// .NET Core 3.1 の Environment.Version のコード抜粋
public static Version Version
{
get
{
// FX_PRODUCT_VERSION is expected to be set by the host
string? versionString = (string?)AppContext.GetData("FX_PRODUCT_VERSION");
if (versionString == null)
{
// Use AssemblyInformationalVersionAttribute as fallback if the exact product version is not specified by the host
versionString = typeof(object).Assembly.GetCustomAttribute<AssemblyInformationalVersionAttribute>()?.InformationalVersion;
}
ReadOnlySpan<char> versionSpan = versionString.AsSpan();
// Strip optional suffixes
int separatorIndex = versionSpan.IndexOfAny("-+ ");
if (separatorIndex != -1)
versionSpan = versionSpan.Slice(0, separatorIndex);
// Return zeros rather then failing if the version string fails to parse
return Version.TryParse(versionSpan, out Version? version) ? version : new Version();
}
}
// .NET Core 3.1 の RuntimeInformation.FrameworkDescription のコード抜粋
private const string FrameworkName = ".NET Core";
private static string s_frameworkDescription;
public static string FrameworkDescription
{
get
{
if (s_frameworkDescription == null)
{
string versionString = (string)AppContext.GetData("FX_PRODUCT_VERSION");
if (versionString == null)
{
// Use AssemblyInformationalVersionAttribute as fallback if the exact product version is not specified by the host
versionString = typeof(object).Assembly.GetCustomAttribute<AssemblyInformationalVersionAttribute>()?.InformationalVersion;
// Strip the git hash if there is one
int plusIndex = versionString.IndexOf('+');
if (plusIndex != -1)
versionString = versionString.Substring(0, plusIndex);
}
s_frameworkDescription = $"{FrameworkName} {versionString}";
}
return s_frameworkDescription;
}
}
.NET Framework 4.8.1 のソースコード
typeof(object).GetTypeInfo().Assembly.GetCustomAttribute(typeof(AssemblyFileVersionAttribute))).Version
で取得します。- ただし、
Environment.Version
は固定値です。
// .NET Framework 4.8.1 の Environment.Version のコード抜粋
public static Version Version => new Version(4, 0, 30319, 42000);
// .NET Framework 4.8.1 の RuntimeInformation.FrameworkDescription のコード抜粋
private static string s_frameworkDescription;
public static string FrameworkDescription
{
get
{
if (RuntimeInformation.s_frameworkDescription == null)
RuntimeInformation.s_frameworkDescription = ".NET Framework " + ((AssemblyFileVersionAttribute)typeof(object).GetTypeInfo().Assembly.GetCustomAttribute(typeof(AssemblyFileVersionAttribute))).Version;
return RuntimeInformation.s_frameworkDescription;
}
}
.NET Framework では取得方法が大きく異なっていることが分かります。
ちなみに typeof(object).Assembly.Location
は、以下の場所になります。
C:\Program Files\dotnet\shared\Microsoft.NETCore.App\8.0.8\System.Private.CoreLib.dll
C:\Program Files\dotnet\shared\Microsoft.NETCore.App\7.0.20\System.Private.CoreLib.dll
C:\Program Files\dotnet\shared\Microsoft.NETCore.App\6.0.33\System.Private.CoreLib.dll
C:\Program Files\dotnet\shared\Microsoft.NETCore.App\5.0.17\System.Private.CoreLib.dll
C:\Program Files\dotnet\shared\Microsoft.NETCore.App\3.1.32\System.Private.CoreLib.dll
C:\Windows\Microsoft.NET\Framework64\v4.0.30319\mscorlib.dll
AppContext.TargetFrameworkName について
他に AppDomainSetup.TargetFrameworkName でも取得できますが、結果は全く同じでした。
AppContext.TargetFrameworkName
の取得するソースコードを抜粋すると、以下のようになっているようです。
.NET 5 以降および .NET Core 3.1 のソースコード
// .NET 5 以降および .NET Core 3.1 の AppContext.TargetFrameworkName のコード抜粋
public static string? TargetFrameworkName =>
// The Target framework is not the framework that the process is actually running on.
// It is the value read from the TargetFrameworkAttribute on the .exe that started the process.
Assembly.GetEntryAssembly()?.GetCustomAttribute<TargetFrameworkAttribute>()?.FrameworkName;
.NET Framework 4.8.1 のソースコード
// .NET Framework 4.8.1 の AppContext.TargetFrameworkName のコード抜粋
public static class AppContext
{
public static string TargetFrameworkName
{
// 1. AppDomainSetup.TargetFrameworkName プロパティを参照している
get => AppDomain.CurrentDomain.SetupInformation.TargetFrameworkName;
}
}
public sealed class AppDomainSetup : IAppDomainSetup
{
[OptionalField(VersionAdded = 5)]
private string _TargetFrameworkName;
// 2. ここにセットされる値は AppDomain.GetTargetFrameworkName() で取得している
public string TargetFrameworkName
{
get => this._TargetFrameworkName;
set => this._TargetFrameworkName = value;
}
}
public sealed class AppDomain
{
private AppDomainSetup _FusionStore;
// 3. ここで実際に取得する
internal String GetTargetFrameworkName()
{
String targetFrameworkName = _FusionStore.TargetFrameworkName;
if (targetFrameworkName == null && IsDefaultAppDomain() && !_FusionStore.CheckedForTargetFrameworkName)
{
// This should only be run in the default appdomain. All other appdomains should have
// values copied from the default appdomain and/or specified by the host.
Assembly assembly = Assembly.GetEntryAssembly();
if (assembly != null)
{
TargetFrameworkAttribute[] attrs = (TargetFrameworkAttribute[])assembly.GetCustomAttributes(typeof(TargetFrameworkAttribute));
if (attrs != null && attrs.Length > 0)
{
Contract.Assert(attrs.Length == 1);
targetFrameworkName = attrs[0].FrameworkName;
_FusionStore.TargetFrameworkName = targetFrameworkName;
}
}
_FusionStore.CheckedForTargetFrameworkName = true;
}
return targetFrameworkName;
}
}
- いずれの .NET バージョンも取得方法は同じで、
Assembly.GetEntryAssembly()
のTargetFrameworkAttribute
を取得します。 Assembly.GetEntryAssembly()
は、通常は .NET プロセスが開始されたアセンブリを取得しますがが、null
になる場合もある点に注意が必要です。- 例えば、pythonnet や COM などの相互運用で呼び出された場合は
null
になります。 - また .NET 9 では Assembly.SetEntryAssembly() メソッドも追加されているため、変更される可能性があります。
感謝
関連記事
新着記事