Add support for tests to detect Windows Nano at runtime.
authorAaron Robinson <arobins@microsoft.com>
Thu, 25 Oct 2018 20:01:40 +0000 (13:01 -0700)
committerAaron Robinson <arobins@microsoft.com>
Thu, 25 Oct 2018 20:01:40 +0000 (13:01 -0700)
tests/src/Common/CoreCLRTestLibrary/Utilities.cs

index a9beaa9..5b36bd2 100644 (file)
@@ -8,6 +8,7 @@ using System.Collections.Generic;
 using System.Globalization;
 using System.IO;
 using System.Reflection;
+using System.Runtime.CompilerServices;
 using System.Runtime.InteropServices;
 using System.Security;
 using System.Text;
@@ -56,23 +57,27 @@ namespace TestLibrary
             }
         }
 
-        public static bool IsWindows
+        public static bool IsWindows => (Path.DirectorySeparatorChar == '\\');
+
+        public static bool IsWindowsNanoServer => (!IsWindowsIoTCore && GetInstallationType().Equals("Nano Server", StringComparison.OrdinalIgnoreCase));
+
+        public static bool IsWindowsIoTCore
         {
-            [SecuritySafeCritical]
             get
             {
-                return Path.DirectorySeparatorChar == '\\';
+                if (IsWindows)
+                {
+                    int productType = GetWindowsProductType();
+                    return productType == Kernel32.PRODUCT_IOTUAPCOMMERCIAL
+                        || productType == Kernel32.PRODUCT_IOTUAP;
+                }
+
+                return false;
             }
         }
 
         // return whether or not the OS is a 64 bit OS
-        public static bool Is64
-        {
-            get
-            {
-                return (IntPtr.Size == 8);
-            }
-        }
+        public static bool Is64 => (IntPtr.Size == 8);
 
         public static string ByteArrayToString(byte[] bytes)
         {
@@ -145,5 +150,153 @@ namespace TestLibrary
                 System.Globalization.CultureInfo.DefaultThreadCurrentCulture = value;
             }
         }
+
+        [MethodImpl(MethodImplOptions.NoInlining)]
+        private static int GetWindowsProductType()
+        {
+            if (!Kernel32.GetProductInfo(Environment.OSVersion.Version.Major, Environment.OSVersion.Version.Minor, 0, 0, out int productType))
+            {
+                return Kernel32.PRODUCT_UNDEFINED;
+            }
+
+            return productType;
+        }
+
+        private static string GetInstallationType()
+        {
+            if (IsWindows)
+            {
+                return GetInstallationTypeForWindows();
+            }
+
+            return string.Empty;
+        }
+
+        private static string GetInstallationTypeForWindows()
+        {
+            try
+            {
+                string key = @"SOFTWARE\Microsoft\Windows NT\CurrentVersion";
+                string value = "InstallationType";
+                return GetRegistryValueString(key, value);
+            }
+            catch (Exception e) when (e is SecurityException || e is InvalidCastException || e is PlatformNotSupportedException /* UAP */)
+            {
+                return string.Empty;
+            }
+        }
+
+        [MethodImpl(MethodImplOptions.NoInlining)]
+        private static string GetRegistryValueString(string key, string value)
+        {
+            int dataSize = 0;
+            Advapi32.RType type;
+            int result = Advapi32.RegGetValueW(
+                Advapi32.HKEY_LOCAL_MACHINE,
+                key,
+                value,
+                Advapi32.RFlags.RRF_RT_REG_SZ,
+                out type,
+                IntPtr.Zero,
+                ref dataSize);
+            if (result != 0 || type != Advapi32.RType.RegSz)
+            {
+                throw new Exception($"Invalid {nameof(Advapi32.RegGetValueW)} result: 0x{result:x} type: {type}");
+            }
+
+            IntPtr data = Marshal.AllocCoTaskMem(dataSize + 1);
+            result = Advapi32.RegGetValueW(
+                Advapi32.HKEY_LOCAL_MACHINE,
+                key,
+                value,
+                Advapi32.RFlags.RRF_RT_REG_SZ,
+                out type,
+                data,
+                ref dataSize);
+            if (result != 0 || type != Advapi32.RType.RegSz)
+            {
+                throw new Exception($"Invalid {nameof(Advapi32.RegGetValueW)} result: 0x{result:x} type: {type}");
+            }
+
+            string stringValue = Marshal.PtrToStringUni(data);
+            Marshal.FreeCoTaskMem(data);
+
+            return stringValue;
+        }
+
+        private sealed class Kernel32
+        {
+            public const int PRODUCT_UNDEFINED = 0;
+            public const int PRODUCT_IOTUAP = 0x0000007B;
+            public const int PRODUCT_IOTUAPCOMMERCIAL = 0x00000083;
+            public const int PRODUCT_CORE = 0x00000065;
+            public const int PRODUCT_CORE_COUNTRYSPECIFIC = 0x00000063;
+            public const int PRODUCT_CORE_N = 0x00000062;
+            public const int PRODUCT_CORE_SINGLELANGUAGE = 0x00000064;
+            public const int PRODUCT_HOME_BASIC = 0x00000002;
+            public const int PRODUCT_HOME_BASIC_N = 0x00000005;
+            public const int PRODUCT_HOME_PREMIUM = 0x00000003;
+            public const int PRODUCT_HOME_PREMIUM_N = 0x0000001A;
+
+            /// <summary>
+            /// https://docs.microsoft.com/en-us/windows/desktop/api/sysinfoapi/nf-sysinfoapi-getproductinfo
+            /// </summary>
+            [DllImport(nameof(Kernel32), SetLastError = false)]
+            public static extern bool GetProductInfo(
+                int dwOSMajorVersion,
+                int dwOSMinorVersion,
+                int dwSpMajorVersion,
+                int dwSpMinorVersion,
+                out int pdwReturnedProductType);
+        }
+
+        private sealed class Advapi32
+        {
+            /// <summary>
+            /// http://msdn.microsoft.com/en-us/library/windows/desktop/ms724884(v=vs.85).aspx
+            /// </summary>
+            public enum RFlags
+            {
+                /// <summary>
+                /// Any
+                /// </summary>
+                Any = 0xffff,
+
+                /// <summary>
+                /// A null-terminated string.
+                /// This will be either a Unicode or an ANSI string, depending on whether you use the Unicode or ANSI function.
+                /// </summary>
+                RRF_RT_REG_SZ = 2,
+            }
+
+            /// <summary>
+            /// http://msdn.microsoft.com/en-us/library/windows/desktop/ms724884(v=vs.85).aspx
+            /// </summary>
+            public enum RType
+            {
+                /// <summary>
+                /// No defined value type
+                /// </summary>
+                RegNone = 0,
+
+                /// <summary>
+                /// A null-terminated string.
+                /// This will be either a Unicode or an ANSI string, depending on whether you use the Unicode or ANSI function.
+                /// </summary>
+                RegSz = 1,
+            }
+
+            [DllImport(nameof(Advapi32), CharSet = CharSet.Unicode, SetLastError = true)]
+            public static extern int RegGetValueW(
+                IntPtr hkey,
+                string lpSubKey,
+                string lpValue,
+                RFlags dwFlags,
+                out RType pdwType,
+                IntPtr pvData,
+                ref int pcbData);
+
+            public static IntPtr HKEY_LOCAL_MACHINE => new IntPtr(unchecked((int)0x80000002));
+        }
     }
 }