diff --git a/global.json b/global.json index 391ba3c2a..338c55541 100644 --- a/global.json +++ b/global.json @@ -1,6 +1,5 @@ { "sdk": { - "version": "8.0.100", - "rollForward": "latestFeature" + "version": "8.0.200" } } diff --git a/src/Grpc.Net.Client/Internal/NtDll.cs b/src/Grpc.Net.Client/Internal/Native.cs similarity index 62% rename from src/Grpc.Net.Client/Internal/NtDll.cs rename to src/Grpc.Net.Client/Internal/Native.cs index 3f5c6a1df..348d4086f 100644 --- a/src/Grpc.Net.Client/Internal/NtDll.cs +++ b/src/Grpc.Net.Client/Internal/Native.cs @@ -23,11 +23,14 @@ namespace Grpc.Net.Client.Internal; /// /// Types for calling RtlGetVersion. See https://www.pinvoke.net/default.aspx/ntdll/RtlGetVersion.html /// -internal static class NtDll +internal static class Native { #pragma warning disable SYSLIB1054 // Use 'LibraryImportAttribute' instead of 'DllImportAttribute' to generate P/Invoke marshalling code at compile time [DllImport("ntdll.dll", SetLastError = true, CharSet = CharSet.Unicode)] internal static extern NTSTATUS RtlGetVersion(ref OSVERSIONINFOEX versionInfo); + + [DllImport("kernel32.dll", ExactSpelling = true)] + private static extern int GetCurrentApplicationUserModelId(ref uint applicationUserModelIdLength, byte[] applicationUserModelId); #pragma warning restore SYSLIB1054 // Use 'LibraryImportAttribute' instead of 'DllImportAttribute' to generate P/Invoke marshalling code at compile time internal static void DetectWindowsVersion(out Version version, out bool isWindowsServer) @@ -46,6 +49,40 @@ internal static void DetectWindowsVersion(out Version version, out bool isWindow isWindowsServer = osVersionInfo.ProductType == VER_NT_SERVER; } + internal static bool IsUwp(string frameworkDescription, Version version) + { + if (frameworkDescription.StartsWith(".NET Native", StringComparison.OrdinalIgnoreCase)) + { + return true; + } + + // From https://github.com/dotnet/runtime/blob/d752f9a19f2d4bc4559e0e303e9374e4042a916e/src/libraries/Common/tests/TestUtilities/System/PlatformDetection.Windows.cs#L190 + const int Windows8Build = 9200; + if (version.Build < Windows8Build) + { + return false; + } + else + { + var bufferSize = 0U; + var result = GetCurrentApplicationUserModelId(ref bufferSize, Array.Empty()); + switch (result) + { + case 15703: // APPMODEL_ERROR_NO_APPLICATION + return false; + case 0: // ERROR_SUCCESS + case 122: // ERROR_INSUFFICIENT_BUFFER + // Success is actually insufficient buffer as we're really only looking for + // not NO_APPLICATION and we're not actually giving a buffer here. The + // API will always return NO_APPLICATION if we're not running under a + // WinRT process, no matter what size the buffer is. + return true; + default: + throw new InvalidOperationException($"Failed to get AppModelId, result was {result}."); + } + } + } + internal enum NTSTATUS : uint { /// diff --git a/src/Grpc.Net.Client/Internal/OperatingSystem.cs b/src/Grpc.Net.Client/Internal/OperatingSystem.cs index 1ce1c6a5a..d0ea27308 100644 --- a/src/Grpc.Net.Client/Internal/OperatingSystem.cs +++ b/src/Grpc.Net.Client/Internal/OperatingSystem.cs @@ -53,9 +53,10 @@ private OperatingSystem() // Get the value lazily so that it is only called if needed. _isWindowsServer = new Lazy(() => { - if (IsWindows) + // RtlGetVersion is not available on UWP. Check it first. + if (IsWindows && !Native.IsUwp(RuntimeInformation.FrameworkDescription, Environment.OSVersion.Version)) { - NtDll.DetectWindowsVersion(out _, out var isWindowsServer); + Native.DetectWindowsVersion(out _, out var isWindowsServer); return isWindowsServer; } @@ -72,9 +73,11 @@ private OperatingSystem() // // Get correct Windows version directly from Windows by calling RtlGetVersion. // https://www.pinvoke.net/default.aspx/ntdll/RtlGetVersion.html - if (IsWindows) + // + // RtlGetVersion is not available on UWP. Check it first. + if (IsWindows && !Native.IsUwp(RuntimeInformation.FrameworkDescription, Environment.OSVersion.Version)) { - NtDll.DetectWindowsVersion(out var windowsVersion, out var windowsServer); + Native.DetectWindowsVersion(out var windowsVersion, out var windowsServer); OSVersion = windowsVersion; _isWindowsServer = new Lazy(() => windowsServer, LazyThreadSafetyMode.ExecutionAndPublication); } diff --git a/test/Grpc.Net.Client.Tests/OperatingSystemTests.cs b/test/Grpc.Net.Client.Tests/OperatingSystemTests.cs index cc2425dd5..8e50438a7 100644 --- a/test/Grpc.Net.Client.Tests/OperatingSystemTests.cs +++ b/test/Grpc.Net.Client.Tests/OperatingSystemTests.cs @@ -29,7 +29,7 @@ public class OperatingSystemTests [Platform("Win", Reason = "Only runs on Windows where ntdll.dll is present.")] public void DetectWindowsVersion_Windows_MatchesEnvironment() { - NtDll.DetectWindowsVersion(out var version, out _); + Native.DetectWindowsVersion(out var version, out _); // It is safe to compare Environment.OSVersion.Version on netfx because tests have no compatibility setting. Assert.AreEqual(Environment.OSVersion.Version, version); @@ -39,7 +39,7 @@ public void DetectWindowsVersion_Windows_MatchesEnvironment() [Platform("Win", Reason = "Only runs on Windows where ntdll.dll is present.")] public void InstanceAndIsWindowsServer_Windows_MatchesEnvironment() { - NtDll.DetectWindowsVersion(out var version, out var isWindowsServer); + Native.DetectWindowsVersion(out var version, out var isWindowsServer); Assert.AreEqual(true, OperatingSystem.Instance.IsWindows); Assert.AreEqual(version, OperatingSystem.Instance.OSVersion);