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 Framework | Value recognized by Target Framework Moniker (TFM) |
---|---|
.NET Standard | netstandard1.0 netstandard1.1 netstandard1.2 netstandard1.3 netstandard1.4 netstandard1.5 netstandard1.6 netstandard2.0 netstandard2.1 |
.NET Core | netcoreapp1.0 netcoreapp1.1 netcoreapp2.0 netcoreapp2.1 netcoreapp2.2 netcoreapp3.0 netcoreapp3.1 |
.NET Framework | net11 net20 net35 net40 net403 net45 net451 net452 net46 net461 net462 net47 net471 net472 net48 |
ターゲットの値は、最終的にTarget Framework Moniker (TFM)によって、参照されるので、TFMが認識できる値が、設定できる値となります。
プリプロセッサー シンボル
これらのターゲットを設定すると、ビルド時には対応したシンボルが定義されます。ターゲットの値に対応した以下のシンボルが定義されます(2019年12月時点の情報)。
Target Frameworks | Symbols |
---|---|
.NET Framework | NETFRAMEWORK , NET20 , NET35 , NET40 , NET45 , NET451 , NET452 , NET46 , NET461 , NET462 , NET47 , NET471 , NET472 , NET48 |
.NET Standard | NETSTANDARD , NETSTANDARD1_0 , NETSTANDARD1_1 , NETSTANDARD1_2 , NETSTANDARD1_3 , NETSTANDARD1_4 , NETSTANDARD1_5 , NETSTANDARD1_6 , NETSTANDARD2_0 , NETSTANDARD2_1 |
.NET Core | NETCOREAPP , NETCOREAPP1_0 , NETCOREAPP1_1 , NETCOREAPP2_0 , NETCOREAPP2_1 , NETCOREAPP2_2 , NETCOREAPP3_0 , NETCOREAPP3_1 |
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を公開しました。
ライブラリーモジュール、ソースコード、ビルド済みサンプルアプリは以下の場所に公開しました。
- モジュールの公開先(nuget.org): NishySoftware.DotNetDetector
- ソースコードの公開先(github.com): DotNetDetector
- サンプルアプリの公開先(github.com): DotNetDetector – Release (1.0.0-alpha00)
まずは、プレリリース版としての公開です。テストを重ねたうえで、正式版としてリリースしたいと思います。
実際に実行してみます。テストコードとして以下を用意します。
// 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件の返信