DotNetDetector #1 .NET Frameworkのバージョンの判別

C#などで作成されたマネージドアプリの実行中のモジュールから、.NET Frameworkや.NET Coreのどのバージョンで動作しているか確認する方法はあるのでしょうか?

アプリのビルド時に使用するターゲットフレームワークは、SDKスタイル プロジェクトであれば、プロジェクトファイル内(.csprojなど)のTargetFrameworkもしくはTargetFrameworksで必要とするバージョンを指定します。

SDKスタイル プロジェクト ファイルのTarget Framework

Target Frameworkの情報は、マイクロソフトのサイト「Target frameworks in SDK-style projects (英語)」に情報があります。そこからいくつか情報を抜粋します。

Target Framework Moniker

TargetFrameworkもしくはTargetFrameworksに指定できるターゲット値は、2019年12月時点では以下の通りです。ここでは、フレームワークとして.NET Standard / .NET Core / .NET Frameworkのみ列挙しています。

Target FrameworkValue recognized by Target
Framework Moniker (TFM)
.NET Standardnetstandard1.0
netstandard1.1
netstandard1.2
netstandard1.3
netstandard1.4
netstandard1.5
netstandard1.6
netstandard2.0
netstandard2.1
.NET Corenetcoreapp1.0
netcoreapp1.1
netcoreapp2.0
netcoreapp2.1
netcoreapp2.2
netcoreapp3.0
netcoreapp3.1
.NET Frameworknet11
net20
net35
net40
net403
net45
net451
net452
net46
net461
net462
net47
net471
net472
net48
TargetFramworkとして指定できる値の一覧

ターゲットの値は、最終的にTarget Framework Moniker (TFM)によって、参照されるので、TFMが認識できる値が、設定できる値となります。

プリプロセッサー シンボル

これらのターゲットを設定すると、ビルド時には対応したシンボルが定義されます。ターゲットの値に対応した以下のシンボルが定義されます(2019年12月時点の情報)。

Target FrameworksSymbols
.NET FrameworkNETFRAMEWORK
NET20NET35NET40
NET45NET451NET452
NET46NET461NET462
NET47NET471NET472
NET48
.NET StandardNETSTANDARD
NETSTANDARD1_0NETSTANDARD1_1NETSTANDARD1_2, NETSTANDARD1_3NETSTANDARD1_4NETSTANDARD1_5
NETSTANDARD1_6
NETSTANDARD2_0NETSTANDARD2_1
.NET CoreNETCOREAPP
NETCOREAPP1_0NETCOREAPP1_1
NETCOREAPP2_0NETCOREAPP2_1NETCOREAPP2_2
NETCOREAPP3_0NETCOREAPP3_1
コンパイル時に自動的に設定されるプリプロセッサー マクロ (2019年12月時点の情報)

NETFRAMEWORK/NETSTANDARD/NETCOREAPPのシンボルはバージョンよらず、プラットフォームの種類で設定されます。また、指定した各バージョンごとのシンボルも定義されます。

そのため、アプリ内でフレームワークの種類やバージョンによって挙動を変えたい場合は、ソースコード上で#if/#endifを使って処理を変えることができます。

ライブラリー内での判定

次にアプリ側でなく、アプリ側から使われているライブラリーを考えてみます。アプリを実行しているフレームワークのバージョンによって、ライブラリー内で処理を変更したい場合はあまり多くはないと思います。

ここで、あえて利用したい状況を捻出します。たとえば、.NET Frameworkは、バージョンによってWPFやWinFormsの高DPI対応状況が異なります。高DPIの対応するようなライブラリーでは、アプリがターゲットしている.NET Frameworkのバージョンによって、処理を変更したいことがあるかもしれません。

このように、あえて、フレームワークのバージョンによって挙動を変更したい場合にどうすればよいのでしょうか?

方法は二つあります。

  • ライブラリーモジュールをフラットフォームのすべてのバージョンに対して個別に用意し、挙動を変える
  • ライブラリー内で動的にフレームワークのバージョンを判定し、挙動を変える

前者は、ほとんど同じ内容のライブラリーモジュールを多く用意することになります。リリースするモジュールが増えるため、あまりやりたくありません。

後者の場合は、実行中にフレームワークのバージョンを容易に判定できるのかという問題があります。

既存のAPIによるフレームワークのバージョン判定

既存のAPIでフレームワークのバージョンは判定できるのでしょうか?

試しに、三つのAPIを試してみます。

// build
var clrVersionBuildtime = System.Reflection.Assembly.GetEntryAssembly().ImageRuntimeVersion;
Console.WriteLine("ImageRuntimeVersion: " + clrVersionBuildtime);

// runtime
var clrVersionRuntime = System.Runtime.InteropServices.RuntimeEnvironment.GetSystemVersion();
Console.WriteLine("RuntimeEnvironment: " + clrVersionRuntime);

Console.WriteLine("Environment.Version: " + Environment.Version.ToString());

このコードを、.NET Framework 4.7.2 (net472)をターゲットに設定したコンソールアプリから実行すると以下の結果になります。

ImageRuntimeVersion: v4.0.30319
RuntimeEnvironment: v4.0.30319
Environment.Version: 4.0.30319.42000

バージョンはすべて4.0.30319と表示されており、アプリが.NET Framework 4.7.2 (net472)のモードで実行されていることがわかりません。プラットフォームに用意されているAPIでは正しく判断できないことになります。

.NET のバージョンを判定するライブラリー

そこで、実行している.NET Framework / .NET Coreのバージョンを判定するライブラリーを作成します。名前は安易にDotNetDetectorとします。

DotNetDetector

ライブラリーを作成したら、最終的にnugetで公開します。そのため、nugetでこのIDが既に使われていないか確認が必要です。実際に確認したところ、残念ながら使われていました。

DotNetDetector:
DotNetDetector detects installed .NET versions.
Version 1.0.4638.30947 (2012/09/12)

どうやら、システムにインストールされている.NET Framewrokのバージョンを判定するライブラリーのようです。今回、実現したい機能とは異なります。また、最終更新日は2012年です。

nuget上では、新たなライブラリーを公開するときは、既存のIDは使えず、ユニークなIDを付与する必要がります。そのため、IDとしては、NishySoftwareのPrefixを追加してNishySoftware.DotNetDetectorとします。他の人がNishySoftwareのプリフィックスを使用することはあり得ないので、今後も、かぶることはないと思います。

NishySoftware.DotNetDetector

NishySoftware.DotNetDetectorを公開しました。

ライブラリーモジュール、ソースコード、ビルド済みサンプルアプリは以下の場所に公開しました。

まずは、プレリリース版としての公開です。テストを重ねたうえで、正式版としてリリースしたいと思います。

実際に実行してみます。テストコードとして以下を用意します。

// build
var clrVersionBuildtime = System.Reflection.Assembly.GetEntryAssembly().ImageRuntimeVersion;
Console.WriteLine("ImageRuntimeVersion: " + clrVersionBuildtime);

// runtime
var clrVersionRuntime = System.Runtime.InteropServices.RuntimeEnvironment.GetSystemVersion();
Console.WriteLine("RuntimeEnvironment: " + clrVersionRuntime);
Console.WriteLine("Environment.Version: " + Environment.Version.ToString());

// DotNetDetector
var targetFramework = DotNetDetector.DetectAppTargetNetType();
Console.WriteLine("App target framework type: " + targetFramework.ToString());

var targetVersion = DotNetDetector.DetectAppTargetNetVersion();
Console.WriteLine("App target framework version: " + targetVersion?.ToString());

var runtimeVesion = DotNetDetector.DetectAppRuntimeNetVersion();
Console.WriteLine("App runtime framework version: " + runtimeVesion?.ToString());

var installedDotNetFramework = DotNetDetector.DetectInstalledNetFrameworkVersion();
Console.WriteLine("Installed .NET framework version: " + installedDotNetFramework?.ToString());

.NET Frameworkアプリの実行結果

このコードを、.NET Framework 4.7.2 (net472)をターゲットに設定したコンソールアプリから実行すると以下の結果になります。

ImageRuntimeVersion: v4.0.30319
RuntimeEnvironment: v4.0.30319
Environment.Version: 4.0.30319.42000
App target framework type: DotNetFramework
App target framework version: 4.7.2
App runtime framework version: 4.8.4220.0
Installed .NET framework version: 4.8

前者3行は、プラットフォームのAPIを使って表示した結果なので、先の結果と同じ内容です。後者4行がこのライブラリーを使った結果です。正しくバージョンが判定できています。

.NET Coreアプリの実行結果

同様に、.NET Core 3.1 (netcoreapp2.2)をターゲットに設定したコンソールアプリから実行すると、以下の結果になります。

ImageRuntimeVersion: v4.0.30319
RuntimeEnvironment: v4.0.30319
Environment.Version: 4.0.30319.42000
App target framework type: DotNet
App target framework version: 2.2
App runtime framework version: 2.2.8
Installed .NET framework version:

前者3行は、プラットフォームのAPIを使って表示した結果です。.NET Framework 4.7.2 (net472)をターゲットにした時と同じ値が表示されています。

後者4行がこのライブラリーを使った結果です。正しくバージョンが判定できていると思います。

なお、システムにインストールされている.NET Frameworkのバージョンが表示されていません。これは、.NET Coreアプリから呼び出されたときは、このバージョンを検出しないのがこのライブラリーの仕様としたからです。この判定をするためにはシステムのレジストリーを読み出す必要があります。レジストリーの読み書きをしようとすると、参照するライブラリーが増えてしまいます。必要とする参照を減らすためにこの仕様としました。そもそも、.NET Coreアプリなのに、.NET Coreのバージョンではなく、.NET Framewrokのバージョンが必要となる状況が想像できなかったと理由もあります。


今回は、実行中のアプリ内から.NET Framework / .NET Coreのバージョンを検出するライブラリーの投稿でした。

“DotNetDetector #1 .NET Frameworkのバージョンの判別” への1件の返信

コメントを残す