We take many environmental factors for granted when it comes to running our .NET applications. Information about the operating system may seem insignificant for folks deploying to rigorously maintained target environments. Still, for folks who publish desktop client software, the luxury of choosing the destination is not an option. This post will be using .NET to determine the operating system and architecture our .NET application is currently running within. We’ll also resolve the SDK version our app is utilizing. Finally, we’ll run our .NET code under three different operating systems to see the results: macOS, Windows, and Linux.

Why Does It Matter?

Most of the .NET Base Class Libraries (BCL) operate agnostically to the system they execute within. Some APIs delegate responsibility to the underlying operating system and utilize environment-specific implementations. Examples include interfaces for Linux, the FileSystemWatcher class, and cryptography. We only need to search the dotnet/runtime repository for “Linux” and see all the operating system mentions.

Understanding that behavior may be different depending on the target environment can save hours of debugging and frustration. The variance in behavior may also make us consider whether we want to try deploying to different target operating systems. We may not realize that high-level frameworks like ASP.NET Core depend on these low-level behaviors, so we can’t be entirely agnostic.

Using System.Runtime

.NET has a particular namespace that contains many helper classes for folks interested in revealing information about an app’s current environment; that namespace is System.Runtime.InteropServices. Under the System.Runtime.InteropServices namespace, we see two especially helpful classes: RuntimeInformation and RuntimeEnvironment.

Starting with RuntimeInformation, we can see information about our current operating system and machine. We have the following static properties.

string FrameworkDescription;
Architecture ProcessArchitecture;
string RuntimeIdentifier;
Architecture OSArchitecture;
string OSDescription;

We also have access to a method of IsOSPlatform, which takes an enum parameter of type OSPlatform.

RuntimeInformation.IsOSPlatform(OSPlatform.OSX)

Our next class, RunTimeEnvironment, where we have access to the following methods:

string GetRuntimeDirectory();
string GetSystemVersion();
bool FromGlobalAccessCache();
object GetRuntimeInterfaceAsObject();
IntPtr GetRuntimeInterfaceAsIntPtr();

We also have a string property of SystemConfigurationFile. The support for this property is unavailable by most newer implementations of .NET and is likely still here for legacy support.

Let’s write an example using top-level statements that will give us an idea of our runtime environment.

using System.Runtime.InteropServices;
using static System.Console;

Clear();
WriteLine(RuntimeInformation.FrameworkDescription);
WriteLine(RuntimeInformation.OSDescription);
WriteLine(RuntimeInformation.OSArchitecture);
WriteLine(RuntimeEnvironment.GetRuntimeDirectory());

Let’s run these in three different environments and see what we get!

Runtime Environments

Let’s start with macOS, my native development environment.

runtime on macOS

As we can see, the output is reflective of the host environment.

.NET 5.0.0-rc.1.20451.14
Darwin 19.6.0 Darwin Kernel Version 19.6.0: Mon Aug 31 22:12:52 PDT 2020; root:xnu-6153.141.2~1/RELEASE_X86_64
X64
/usr/local/share/dotnet/shared/Microsoft.NETCore.App/5.0.0-rc.1.20451.14/

Let’s move onto Windows.

Running the same code on Windows yields different Windows-specific results.

runtime on macOS

With the results in the console being:

.NET 5.0.0-rc.1.20451.14
Microsoft Windows 10.0.19041
X64
C:\Program Files\dotnet\shared\Microsoft.NETCore.App\5.0.0-rc.1.20451.14\

Finally, let’s run the same code under Linux.

runtime on macOS

.NET 5.0.0-rc.1.20451.14
Linux 5.4.0-42-generic #46~18.04.1-Ubuntu SMP Fri Jul 10 07:21:24 UTC 2020
X64
/usr/share/dotnet/shared/Microsoft.NETCore.App/5.0.0-rc.1.20451.14/

Conclusion

If we’re writing code that needs to ship to multiple environments, the System.Runtime.InteropServices is invaluable. An implementation may differ for each OS, and using these helper runtime features can help us branch and handle strange edge cases. I hope you found this post helpful. Please leave a comment below if you’ve had to use either RuntimeInformation or RunTimeEnvironment in your codebase.