How can I find the version of .NET run-time programmatically? - c#

I need a solution to give me the .NET run-time version of both the full framework as well as the .NET Core.
On a machine with the following .NET versions:
Full: 4.7.2
Core: 2.1.104
Running:
RuntimeInformation.FrameworkDescription
Gives me:
Full: .NET Framework 4.7.2558.0
Core: .NET Core 4.6.26212.01
Running:
Environment.Version
Gives me:
Full: 4.0.30319.42000
Core: 4.0.30319.42000
How can I accurately get the run-time versions across different platforms knowing that the registry option is not available outside Windows.

There isn't a unified way to do this yet, although there is an open request for this here that you can track. If you click through the various issues that reference that discussion and the issues referenced further downstream, you'll see there are also some bugs in some implementations right now, but there is active work (one of the issues had a related check-in just 8 hours ago).
For .NET Framework:
using System;
...
string ver = AppDomain.CurrentDomain.SetupInformation.TargetFrameworkName;
and for .NET Core:
using System.Reflection;
using System.Runtime.Versioning;
...
string ver = Assembly.GetEntryAssembly()?.GetCustomAttribute<TargetFrameworkAttribute>()?.FrameworkName;
Output:
.NETFramework,Version=v4.5.1
.NETCoreApp,Version=v2.0
Obviously these are a bit of a hassle to use programmatically, hence the requests for a better API (including this open issue from Microsoft discussing a new API specifically focused on testing for a minimum target framework).
The other piece of the puzzle that will probably always be impossible is that a given application can reference many targets. Under the hood you might be pulling in .NET Standard 2.x and .NET Standard 1.x libraries, for example. I doubt there will ever be a good way to get a complete picture of all the targets behind a given collection of executing assemblies...

If you want to know which version of .NET Framework is installed on a machine you should use the documented 'Release Key' Registry Key. That key is registered in the system when .NET Framework is installed.
This is publicly documented here:
https://learn.microsoft.com/en-us/dotnet/framework/migration-guide/versions-and-dependencies
You have to write the code that will read that registry key and map it to an actual .NET Framework version. Here is code that does that for .NET Framework versions betwee 4.5 and 4.7.1. You can further customize that as you need to. (from https://github.com/dotnet/corefx/blob/master/src/CoreFx.Private.TestUtilities/src/System/PlatformDetection.NetFx.cs#L33)
private static Version GetFrameworkVersion()
{
using (RegistryKey ndpKey = Registry.LocalMachine.OpenSubKey(#"SOFTWARE\Microsoft\NET Framework Setup\NDP\v4\Full"))
{
if (ndpKey != null)
{
int value = (int)(ndpKey.GetValue("Release") ?? 0);
if (value >= 528040)
return new Version(4, 8, 0);
if (value >= 461808)
return new Version(4, 7, 2);
if (value >= 461308)
return new Version(4, 7, 1);
if (value >= 460798)
return new Version(4, 7, 0);
if (value >= 394802)
return new Version(4, 6, 2);
if (value >= 394254)
return new Version(4, 6, 1);
if (value >= 393295)
return new Version(4, 6, 0);
if (value >= 379893)
return new Version(4, 5, 2);
if (value >= 378675)
return new Version(4, 5, 1);
if (value >= 378389)
return new Version(4, 5, 0);
throw new NotSupportedException($"No 4.5 or later framework version detected, framework key value: {value}");
}
throw new NotSupportedException(#"No registry key found under 'SOFTWARE\Microsoft\NET Framework Setup\NDP\v4\Full' to determine running framework version");
}
}
I know you said that you don't want to use the registry, however I recommend you use the registry path if you are running on .NET Framework (you can detect that by looking at the path where assembly containing System.Object is loaded from) as that is the recommended and supported way (we use the same inside the .NET Runtime when we need to check what version is installed on a machine).
For .NET Core there isn't a registry key you can check. However, you can use the location of the assembly that contains System.Object to identify the version on which your code is running on.
public static Version GetVersion()
{
string runtimePath = System.IO.Path.GetDirectoryName(typeof(object).Assembly.Location);
// Making the assumption that the path looks like this
// C:\Program Files\dotnet\shared\Microsoft.NETCore.App\2.0.6
string version = runtimePath.Substring(runtimePath.LastIndexOf('\\') + 1);
return new Version(version);
}

I faced a similar problem, for Inno Setup, I needed to find out if NET Core was installed and if so which version? Using the registry is not the way. For this reason, I reading console outputs:
/// <summary>
/// Detect installed NET Core version (major).
/// The method for .NET Framework applications and library when you need to know are the NETCore application running is possible
/// For NETCore application simple use Environment.Version Property
/// </summary>
/// <returns>
/// NET Core version or error code:
/// -1: some exception
/// -2: not installed or wrong dotnet prompt
/// -3: access file system problems
/// </returns>
[DllExport("GetNETCoreVersion", CallingConvention = CallingConvention.StdCall)]
public static int GetNETCoreVersion()
{
try
{
//we need create and run .cmd file to make console output redirection to file with using ">" operator (OLD GOOD MS DOS)
string cmdFileName = Path.GetTempFileName() + ".cmd";
string versionFileName = Path.GetTempFileName();
//like: cmd dotnet --version with console output redirection to file "versionFileName"
//full command id: cmd dotnet --version >> "versionFileName"
File.WriteAllText(cmdFileName, "dotnet --version > " + versionFileName);
System.Diagnostics.ProcessStartInfo startInfo = new System.Diagnostics.ProcessStartInfo
{
WindowStyle = System.Diagnostics.ProcessWindowStyle.Hidden,
WorkingDirectory = Path.GetDirectoryName(cmdFileName),
FileName = cmdFileName
};
System.Diagnostics.Process process = new System.Diagnostics.Process
{
StartInfo = startInfo
};
process.Start();
process.WaitForExit();
//after success dotnet --version >> "tempFileName", the "tempFileName" file must be exists and contains NET Core version
if (File.Exists(versionFileName))
{
string versionsinfo = File.ReadAllText(versionFileName);
File.Delete(versionsinfo);
//only major version is interested
versionsinfo = versionsinfo.Substring(0, versionsinfo.IndexOf("."));
int version = -2;
int.TryParse(versionsinfo, out version);
return version;
}
return -3;
}
catch
{
return -1;
}
}

Related

Get Module Address in C# .NET 3.5?

I am creating a directx overlay for an esp hack in a game. (I have made several in C++ but wanted to try c#) This method requires me to use a version of directx which is only compatible with .net framework 3.5 or older. I have been using this code to obtain the base address of a specific module:
static int GetModuleAddress(string moduleName)
{
try
{
Process[] p = Process.GetProcessesByName(game.process);
if (p.Length > 0)
{
foreach (ProcessModule m in p[0].Modules)
{
if (m.ModuleName == moduleName)
{
//temp = (int)m.BaseAddress;
//return temp;
return (int)m.BaseAddress;
}
}
return -1;
}
else
{
return -1;
}
}
catch (Exception ex)
{
return -1;
}
}
This code works fine in framework 4.5.2 (it cycles through nearly 100 different modules and returns the base address of whichever module i'm looking for). But when using the code in 3.5 it only cycles through about 5 modules (none of which are the ones i need) and returns -1.
Why is this caused? Is there another way to obtain a base address of a module?
The reason you're only seeing 5 modules is because you're using an x86 program to parse the modules of an x64 process. If you're making a program that interacts with the memory of an external process you should always compile it for the target processes's architecture. This will save you lots of headache.
Use IntPtr for addresses and offsets and compile both x86 and x64 versions, then use the correct version for each type of target process, this will ensure the minimal amount of problems.
In addition, make sure you always run as administrator.

GetSystemMetrics() returns different results for .NET 4.5 & .NET 4.0

During a .NET 4.0 -> .NET 4.5 application migration process I've discovered an extremely strange behavior. I've been able to track this issue down to this short code snippet:
class Program
{
[System.Runtime.InteropServices.DllImport("user32.dll")]
public static extern int GetSystemMetrics(int nIndex);
static void Main(string[] args)
{
const int CXFRAME = 0x20;
const int CYFRAME = 0x21;
var dx = GetSystemMetrics(CXFRAME);
var dy = GetSystemMetrics(CYFRAME);
Console.WriteLine("{0}x{1}", dx, dy);
Console.ReadKey();
}
}
When compiled with Target Framework = 4.0 (and also 2.0, 3.0, 3.5), it outputs 8x8
When compiled with Target Framework = 4.5, it outputs 4x4
Running this sample with MSVS2012 debugger also always outputs 4x4 (with any target framework).
Other options (target framework profile, target platform, enabling/disabling Aero) do not affect the result, the only things that change the output are target framework and running with debugger. I've been able to reproduce this issue on 3 computers, unfortunately they are almost identical in terms of installed software:
Windows 7 Ultmate SP1 (russian, all updates installed) with MSVS2012 (english/rusian) Update 1
Windows 8 (on virtual machine)
Currently I'm thinking about patching some .NET classes (SystemParameters for example), which call GetSystemMetrics() using reflection, but I'm not sure how to get correct metric values.
Questions:
Am I missing something? How can GetSystemMetrics() be affected by target framework?
Is there any way to call GetSystemMetrics() from .NET 4.5 app and get correct results?
I'm open to any suggestions on fixing this issue. Also, if you cannot reproduce the issue, please leave a short system description in comments.
So, it's actually a by-design behavior, and if someone has similar issues, here is the code which always outputs the same result:
const int CXFRAME = 0x20;
const int CYFRAME = 0x21;
const int CXPADDEDBORDER = 92;
var dx = GetSystemMetrics(CXFRAME);
var dy = GetSystemMetrics(CYFRAME);
var d = GetSystemMetrics(CXPADDEDBORDER);
dx += d;
dy += d;
Console.WriteLine("{0}x{1}", dx, dy);
Console.ReadKey();
Also note that RibbonWindow WPF control, which uses WindowChrome and now comes as a part of .NET 4.5 does not know about this changes and displays messy window borders (fortunately, I think it can be fixed using modified styles).
According to Microsoft, this is by-design.
See here for full details:
The SystemParameters.WindowResizeBorderThickness seems to return incorrect value - Microsoft Connect (no archived version available)
Regression: ::GetSystemMetrics delivers different values - Microsoft Connect (archived)
Despite MS saying it's "by design", I still think it's a bug!

How can you get the FrameworkName of the current running environment? [duplicate]

How can I get the current CLR Runtime version in a running .NET program ?
Check out the System.Environment.Version property.
https://learn.microsoft.com/en-us/dotnet/api/system.environment.version
Since .NET 4.5 you could just use System.Environment.Version (it would only return 4.0.{something}, allowing you to verify that you're "at least" on 4.0 but not telling you which actual version is available unless you can map a full list of build numbers in).
In .NET Core, and starting in .NET 4.7.1 of Framework, you can check System.Runtime.InteropServices.RuntimeInformation.FrameworkDescription
It returns a string with either: ".NET Core", ".NET Framework", or ".NET Native" before the version number -- so you have a little parsing to do if you just want the number.
Try Environment.Version to get that info. Also you may need to call ToString().
That works for me:
public static String GetRunningFrameworkVersion()
{
String netVer = Environment.Version;
Assembly assObj = typeof( Object ).GetTypeInfo().Assembly;
if ( assObj != null )
{
AssemblyFileVersionAttribute attr;
attr = (AssemblyFileVersionAttribute)assObj.GetCustomAttribute( typeof(AssemblyFileVersionAttribute) );
if ( attr != null )
{
netVer = attr.Version;
}
}
return netVer;
}
I compiled my .NET program for .NET 4.5 and it returns for running under .NET 4.8:
"4.8.4150.0"
If you just want to find out the version and don't need it to be parsed or of type Version, just do this:
Console.WriteLine(System.Runtime.InteropServices.RuntimeInformation.FrameworkDescription);
If your app is running via .Net Framework 4.7.2, it returns something like .NET Framework 4.7.3875.0.
If your app is running via .Net 6, it returns something like .NET 6.0.0-rtm.21522.10.

Different Target .Net Framework on each C# Project

is it ok if i have this X project using .net2.0, and that X project is calling Y project which is using .net3.5.. i got customized buttons in Y project and im using that button in X project also, there's a method in Y project that has LINQ and X project is calling that method... i cant test it because i installed the latest .net framework.. :)
this is my code in project that has the .net3.5
[DllImport("kernel32.dll", CharSet = CharSet.Auto)]
public static extern int GetDriveType(string lpRootPathName);
public enum DriveType : int
{
Unknown = 0,
NoRoot = 1,
Removable = 2,
Localdisk = 3,
Network = 4,
CD = 5,
RAMDrive = 6
}
var selectedDrives = from s in Environment.GetLogicalDrives() where Enum.GetName(typeof(DriveType), GetDriveType(s)).Equals(DriveType.Removable) select s;
foreach (String drives in selectedDrives)
{
MessageBox.Show(drives);
}
correct also the LINQ statement if i did it wrong.. :)
If the 3.5 framework is not installed on the machine that executes this, it will fail as System.Linq.dll won't exist. You can use LINQBridge with .NET 2.0 and C# 3.0 (which will give you access to a re-implementation of LINQ-to-Objects) but in reality it may be easier to get the client to upgrade. 2.0 is pretty old now.
Alternatively... if all you need is a where, there are easier routes. For example:
foreach (String drives in Environment.GetLogicalDrives())
{
if(!Enum.GetName(typeof(DriveType), GetDriveType(s))
.Equals(DriveType.Removable))
{
continue;
}
MessageBox.Show(drives);
}
A .NET 2.0 project cannot call a method in a .NET 3.5 project.

Getting the .NET Framework directory path

How can I obtain the .NET Framework directory path inside my C# application?
The folder that I refer is "C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727"
The path to the installation directory of the CLR active for the current .NET application can be obtained by using the following method:
System.Runtime.InteropServices.RuntimeEnvironment.GetRuntimeDirectory()
I would strongly advice against reading the registry directly. For example, when a .NET application is running in 64bit systems, the CLR can either be loaded from "C:\Windows\Microsoft.NET\Framework64\v2.0.50727" (AnyCPU, x64 compilation targets) or from "C:\Windows\Microsoft.NET\Framework\v2.0.50727" (x86 compilation target). Reading registry will not tell you which one of the two directories was used by the current CLR.
Another important fact is that "the current CLR" will be "2.0" for .NET 2.0, .NET 3.0 and .NET 3.5 applications. This means that the GetRuntimeDirectory() call will return 2.0 directory even within .NET 3.5 applications (that load some of their assemblies from 3.5 directory). Depending on your interpretation of the term ".NET Framework directory path", GetRuntimeDirectory might not be the information you are looking for ("CLR directory" versus "directory from which 3.5 assemblies are coming from").
An easier way is to include the Microsoft.Build.Utilities assembly and use
using Microsoft.Build.Utilities;
ToolLocationHelper.GetPathToDotNetFramework(
TargetDotNetFrameworkVersion.VersionLatest);
You can grab it from the Windows Registry:
using System;
using Microsoft.Win32;
// ...
public static string GetFrameworkDirectory()
{
// This is the location of the .Net Framework Registry Key
string framworkRegPath = #"Software\Microsoft\.NetFramework";
// Get a non-writable key from the registry
RegistryKey netFramework = Registry.LocalMachine.OpenSubKey(framworkRegPath, false);
// Retrieve the install root path for the framework
string installRoot = netFramework.GetValue("InstallRoot").ToString();
// Retrieve the version of the framework executing this program
string version = string.Format(#"v{0}.{1}.{2}\",
Environment.Version.Major,
Environment.Version.Minor,
Environment.Version.Build);
// Return the path of the framework
return System.IO.Path.Combine(installRoot, version);
}
Source
For .NET Framework versions >= 4.5, an official way from MSDN:
internal static class DotNetFrameworkLocator
{
public static string GetInstallationLocation()
{
const string subkey = #"SOFTWARE\Microsoft\NET Framework Setup\NDP\v4\Full\";
using (var ndpKey = RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, RegistryView.Registry32).OpenSubKey(subkey))
{
if (ndpKey == null)
throw new Exception();
var value = ndpKey.GetValue("InstallPath") as string;
if (value != null)
return value;
else
throw new Exception();
}
}
}
Read value of the [HKLM]\Software\Microsoft.NetFramework\InstallRoot key - you will get "C:\WINDOWS\Microsoft.NET\Framework". Then append with desired framework version.

Categories