Skip to content

Commit

Permalink
Fix OS detection error in UWP (#2376)
Browse files Browse the repository at this point in the history
  • Loading branch information
JamesNK authored Feb 27, 2024
1 parent 787faa6 commit 098dca8
Show file tree
Hide file tree
Showing 4 changed files with 48 additions and 9 deletions.
3 changes: 1 addition & 2 deletions global.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
{
"sdk": {
"version": "8.0.100",
"rollForward": "latestFeature"
"version": "8.0.200"
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,14 @@ namespace Grpc.Net.Client.Internal;
/// <summary>
/// Types for calling RtlGetVersion. See https://www.pinvoke.net/default.aspx/ntdll/RtlGetVersion.html
/// </summary>
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)
Expand All @@ -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<byte>());
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
{
/// <summary>
Expand Down
11 changes: 7 additions & 4 deletions src/Grpc.Net.Client/Internal/OperatingSystem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -53,9 +53,10 @@ private OperatingSystem()
// Get the value lazily so that it is only called if needed.
_isWindowsServer = new Lazy<bool>(() =>
{
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;
}

Expand All @@ -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<bool>(() => windowsServer, LazyThreadSafetyMode.ExecutionAndPublication);
}
Expand Down
4 changes: 2 additions & 2 deletions test/Grpc.Net.Client.Tests/OperatingSystemTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -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);
Expand Down

0 comments on commit 098dca8

Please sign in to comment.