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);