diff --git a/src/app/SharpRaven/Data/Context/Runtime.cs b/src/app/SharpRaven/Data/Context/Runtime.cs
index 0136b12b..f34ef68b 100644
--- a/src/app/SharpRaven/Data/Context/Runtime.cs
+++ b/src/app/SharpRaven/Data/Context/Runtime.cs
@@ -62,6 +62,12 @@ public class Runtime
///
[JsonProperty(PropertyName = "raw_description", NullValueHandling = NullValueHandling.Ignore)]
public string RawDescription { get; set; }
+ ///
+ /// An optional build number
+ ///
+ ///
+ [JsonProperty(PropertyName = "build", NullValueHandling = NullValueHandling.Ignore)]
+ public string Build { get; set; }
///
/// Clones this instance
@@ -73,6 +79,7 @@ internal Runtime Clone()
{
Name = this.Name,
Version = this.Version,
+ Build = this.Build,
RawDescription = this.RawDescription
};
}
@@ -85,12 +92,7 @@ public static Runtime Create()
{
try
{
- var runtime = new Runtime
- {
- RawDescription = RuntimeInfoHelper.GetRuntimeVersion()
- };
-
- return runtime;
+ return RuntimeInfoHelper.GetRuntime();
}
catch (Exception e)
{
diff --git a/src/app/SharpRaven/Data/Context/RuntimeExtensions.cs b/src/app/SharpRaven/Data/Context/RuntimeExtensions.cs
new file mode 100644
index 00000000..598b8561
--- /dev/null
+++ b/src/app/SharpRaven/Data/Context/RuntimeExtensions.cs
@@ -0,0 +1,14 @@
+namespace SharpRaven.Data.Context
+{
+ internal static class RuntimeExtensions
+ {
+ public static bool IsNetFx(this Runtime runtime) => runtime.IsRuntime(".NET Framework");
+ public static bool IsNetCore(this Runtime runtime) => runtime.IsRuntime(".NET Core");
+
+ private static bool IsRuntime(this Runtime runtime, string runtimeName)
+ {
+ return runtime?.Name?.StartsWith(runtimeName) == true
+ || runtime?.RawDescription?.StartsWith(runtimeName) == true;
+ }
+ }
+}
diff --git a/src/app/SharpRaven/SharpRaven.csproj b/src/app/SharpRaven/SharpRaven.csproj
index cb276b7c..e988e48a 100644
--- a/src/app/SharpRaven/SharpRaven.csproj
+++ b/src/app/SharpRaven/SharpRaven.csproj
@@ -21,26 +21,37 @@
-
+
+
-
-
-
-
-
-
+
+ NETFX;$(AdditionalConstants)
+
+
+
+
+
+
-
+
+ NETFX;$(AdditionalConstants)
+
+
+
+
+
+
+
- HAS_RUNTIME_INFORMATION;$(AdditionalConstants)
+ NETFX;HAS_RUNTIME_INFORMATION;NET45PLUS_REGISTRY_VERSION;$(AdditionalConstants)
@@ -51,7 +62,7 @@
- HAS_RUNTIME_INFORMATION;$(AdditionalConstants)
+ NETFX;HAS_RUNTIME_INFORMATION;NET45PLUS_REGISTRY_VERSION;$(AdditionalConstants)
diff --git a/src/app/SharpRaven/Utilities/RuntimeInfoHelper.cs b/src/app/SharpRaven/Utilities/RuntimeInfoHelper.cs
index 468f7887..eb4ddeaf 100644
--- a/src/app/SharpRaven/Utilities/RuntimeInfoHelper.cs
+++ b/src/app/SharpRaven/Utilities/RuntimeInfoHelper.cs
@@ -1,46 +1,98 @@
using System;
using System.Reflection;
+
+using SharpRaven.Data.Context;
#if HAS_RUNTIME_INFORMATION
using System.Runtime.InteropServices;
#endif
+#if NET45PLUS_REGISTRY_VERSION
+using Microsoft.Win32;
+#endif
namespace SharpRaven.Utilities
{
internal static class RuntimeInfoHelper
{
- public static string GetRuntimeVersion()
+ public static Runtime GetRuntime()
{
-#if HAS_RUNTIME_INFORMATION
+#if HAS_RUNTIME_INFORMATION // .NET Core 2+, .NET Framework 4.5+
// Prefered API: netstandard2.0 and vNext
// https://github.com/dotnet/corefx/blob/master/src/System.Runtime.InteropServices.RuntimeInformation/src/System/Runtime/InteropServices/RuntimeInformation/RuntimeInformation.cs
// e.g: .NET Framework 4.7.2633.0, .NET Native, WebAssembly
- var version = RuntimeInformation.FrameworkDescription;
+ var runtime = new Runtime
+ {
+ RawDescription = RuntimeInformation.FrameworkDescription
+ };
#else
- var mono = GetFromMono();
- var version = mono
+ var runtime = GetFromMono();
+ runtime = runtime
// Environment.Version: NET Framework 4, 4.5, 4.5.1, 4.5.2 = 4.0.30319.xxxxx
// .NET Framework 4.6, 4.6.1, 4.6.2, 4.7, 4.7.1 = 4.0.30319.42000
// Not recommended on NET45+
- ?? $".NET Framework {Environment.Version}";
+ ?? new Runtime
+ {
+ Name = ".NET Framework",
+ Version = Environment.Version.ToString()
+ };
+#endif
+
+#if NET45PLUS_REGISTRY_VERSION // .NET Framework 4.5 and later
+ if (runtime.IsNetFx())
+ {
+ runtime.Build = Get45PlusLatestInstallationFromRegistry()?.ToString();
+ }
+#endif
+
+#if !NETFX // Non .NET Framework (i.e: netstandard, netcoreapp)
+ if (runtime.IsNetCore())
+ {
+ // RuntimeInformation.FrameworkDescription returns 4.6 on .NET Core 2.0 which is counter intuitive
+ var assembly = typeof(System.Runtime.GCSettings).GetTypeInfo().Assembly;
+ var assemblyPath = assembly.CodeBase.Split(new[] { '/', '\\' }, StringSplitOptions.RemoveEmptyEntries);
+ var netCoreAppIndex = Array.IndexOf(assemblyPath, "Microsoft.NETCore.App");
+ if (netCoreAppIndex > 0 && netCoreAppIndex < assemblyPath.Length - 2)
+ {
+ runtime.Name = ".NET Core";
+ runtime.Version = assemblyPath[netCoreAppIndex + 1];
+ }
+ }
#endif
- return version;
+ return runtime;
}
- private static string GetFromMono()
+ private static Runtime GetFromMono()
{
// The implementation of Mono to RuntimeInformation:
// https://github.com/mono/mono/blob/90b49aa3aebb594e0409341f9dca63b74f9df52e/mcs/class/corlib/System.Runtime.InteropServices.RuntimeInformation/RuntimeInformation.cs
- // e.g; Mono 5.10.0 (Visual Studio built mono)
- var monoVersion = Type.GetType("Mono.Runtime", false)
- ?.GetMethod("GetDisplayName", BindingFlags.NonPublic | BindingFlags.Static)
- ?.Invoke(null, null) as string;
-
- if (monoVersion != null)
+ if (Type.GetType("Mono.Runtime", false)
+ ?.GetMethod("GetDisplayName", BindingFlags.NonPublic | BindingFlags.Static)
+ ?.Invoke(null, null) is string monoVersion)
{
- monoVersion = "Mono " + monoVersion;
+ return new Runtime
+ {
+ // Send complete (raw) and let Sentry parse it. UI can display short version but details are not lost
+ // e.g; Mono 5.10.0 (Visual Studio built mono)
+ // e.g: Mono 5.10.1.47 (tarball Tue Apr 17 09:23:16 UTC 2018)
+ RawDescription = "Mono " + monoVersion
+ };
}
- return monoVersion;
+ return null;
+ }
+
+#if NET45PLUS_REGISTRY_VERSION
+ // https://docs.microsoft.com/en-us/dotnet/framework/migration-guide/how-to-determine-which-versions-are-installed#to-find-net-framework-versions-by-querying-the-registry-in-code-net-framework-45-and-later
+ private static int? Get45PlusLatestInstallationFromRegistry()
+ {
+ const string subkey = @"SOFTWARE\Microsoft\NET Framework Setup\NDP\v4\Full\";
+
+ using (var ndpKey = RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, RegistryView.Registry32).OpenSubKey(subkey))
+ {
+ return int.TryParse(ndpKey?.GetValue("Release")?.ToString(), out var releaseId)
+ ? releaseId
+ : (int?)null;
+ }
}
+#endif
}
}
diff --git a/src/tests/SharpRaven.UnitTests/Data/Context/RuntimeTests.cs b/src/tests/SharpRaven.UnitTests/Data/Context/RuntimeTests.cs
index 3ee2c76a..ebc6a9bb 100644
--- a/src/tests/SharpRaven.UnitTests/Data/Context/RuntimeTests.cs
+++ b/src/tests/SharpRaven.UnitTests/Data/Context/RuntimeTests.cs
@@ -10,13 +10,16 @@ namespace SharpRaven.UnitTests.Data.Context
public class RuntimeTests
{
[Test]
- public void Create_RawDescription_NotNullAndAsHelper()
+ public void Create_Runtime_NotNullAndAsHelper()
{
- var runtime = Runtime.Create();
+ var actual = Runtime.Create();
- var expected = RuntimeInfoHelper.GetRuntimeVersion();
- Assert.NotNull(runtime.RawDescription);
- Assert.AreEqual(expected, runtime.RawDescription);
+ var expected = RuntimeInfoHelper.GetRuntime();
+ Assert.NotNull(actual);
+ Assert.AreEqual(expected.Build, actual.Build);
+ Assert.AreEqual(expected.Version, actual.Version);
+ Assert.AreEqual(expected.Name, actual.Name);
+ Assert.AreEqual(expected.RawDescription, actual.RawDescription);
}
[Test]
diff --git a/src/tests/SharpRaven.UnitTests/SharpRaven.UnitTests.csproj b/src/tests/SharpRaven.UnitTests/SharpRaven.UnitTests.csproj
index bc7dcadd..8d933f9a 100644
--- a/src/tests/SharpRaven.UnitTests/SharpRaven.UnitTests.csproj
+++ b/src/tests/SharpRaven.UnitTests/SharpRaven.UnitTests.csproj
@@ -12,35 +12,44 @@
+
+
+
+
+
+
+
+ HAS_RUNTIME_INFORMATION;$(AdditionalConstants)
+
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
+
HAS_RUNTIME_INFORMATION;$(AdditionalConstants)
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/tests/SharpRaven.UnitTests/Utilities/RuntimeInfoHelperTests.cs b/src/tests/SharpRaven.UnitTests/Utilities/RuntimeInfoHelperTests.cs
new file mode 100644
index 00000000..12d08c96
--- /dev/null
+++ b/src/tests/SharpRaven.UnitTests/Utilities/RuntimeInfoHelperTests.cs
@@ -0,0 +1,17 @@
+using System;
+
+using NUnit.Framework;
+using SharpRaven.Utilities;
+
+namespace SharpRaven.UnitTests.Utilities
+{
+ public class RuntimeInfoHelperTests
+ {
+ [Test]
+ public void GetRuntime_NotNull()
+ {
+ var runtime = RuntimeInfoHelper.GetRuntime();
+ Assert.That(runtime, Is.Not.Null);
+ }
+ }
+}