You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

508 lines
21 KiB

  1. /* Copyright (C) 2008-2016 Peter Palotas, Jeffrey Jangli, Alexandr Normuradov
  2. *
  3. * Permission is hereby granted, free of charge, to any person obtaining a copy
  4. * of this software and associated documentation files (the "Software"), to deal
  5. * in the Software without restriction, including without limitation the rights
  6. * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  7. * copies of the Software, and to permit persons to whom the Software is
  8. * furnished to do so, subject to the following conditions:
  9. *
  10. * The above copyright notice and this permission notice shall be included in
  11. * all copies or substantial portions of the Software.
  12. *
  13. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  14. * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  15. * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  16. * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  17. * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  18. * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  19. * THE SOFTWARE.
  20. */
  21. using System;
  22. using System.ComponentModel;
  23. using System.Diagnostics;
  24. using System.Diagnostics.CodeAnalysis;
  25. using System.Runtime.InteropServices;
  26. using System.Security;
  27. namespace Alphaleonis.Win32
  28. {
  29. /// <summary>Static class providing access to information about the operating system under which the assembly is executing.</summary>
  30. public static class OperatingSystem
  31. {
  32. #region OperatingSystem Name Enum
  33. /// <summary>A set of flags that describe the named Windows versions.</summary>
  34. /// <remarks>The values of the enumeration are ordered. A later released operating system version has a higher number, so comparisons between named versions are meaningful.</remarks>
  35. [SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "Os")]
  36. [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Os")]
  37. public enum EnumOsName
  38. {
  39. /// <summary>A Windows version earlier than Windows 2000.</summary>
  40. Earlier = -1,
  41. /// <summary>Windows 2000 (Server or Professional).</summary>
  42. Windows2000 = 0,
  43. /// <summary>Windows XP.</summary>
  44. WindowsXP = 1,
  45. /// <summary>Windows Server 2003.</summary>
  46. WindowsServer2003 = 2,
  47. /// <summary>Windows Vista.</summary>
  48. WindowsVista = 3,
  49. /// <summary>Windows Server 2008.</summary>
  50. WindowsServer2008 = 4,
  51. /// <summary>Windows 7.</summary>
  52. Windows7 = 5,
  53. /// <summary>Windows Server 2008 R2.</summary>
  54. WindowsServer2008R2 = 6,
  55. /// <summary>Windows 8.</summary>
  56. Windows8 = 7,
  57. /// <summary>Windows Server 2012.</summary>
  58. WindowsServer2012 = 8,
  59. /// <summary>Windows 8.1.</summary>
  60. Windows81 = 9,
  61. /// <summary>Windows Server 2012 R2</summary>
  62. WindowsServer2012R2 = 10,
  63. /// <summary>Windows 10</summary>
  64. Windows10 = 11,
  65. /// <summary>Windows Server</summary>
  66. WindowsServer = 12,
  67. /// <summary>A later version of Windows than currently installed.</summary>
  68. Later = 65535
  69. }
  70. #endregion // OperatingSystem Name Enum
  71. #region ProcessorArchitecture Name enum
  72. /// <summary>A set of flags to indicate the current processor architecture for which the operating system is targeted and running.</summary>
  73. [SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "Pa")]
  74. [SuppressMessage("Microsoft.Design", "CA1028:EnumStorageShouldBeInt32")]
  75. public enum EnumProcessorArchitecture
  76. {
  77. /// <summary>PROCESSOR_ARCHITECTURE_INTEL
  78. /// <para>The system is running a 32-bit version of Windows.</para>
  79. /// </summary>
  80. X86 = 0,
  81. /// <summary>PROCESSOR_ARCHITECTURE_IA64
  82. /// <para>The system is running on a Itanium processor.</para>
  83. /// </summary>
  84. [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Ia")]
  85. [SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "Ia")]
  86. IA64 = 6,
  87. /// <summary>PROCESSOR_ARCHITECTURE_AMD64
  88. /// <para>The system is running a 64-bit version of Windows.</para>
  89. /// </summary>
  90. X64 = 9,
  91. /// <summary>PROCESSOR_ARCHITECTURE_UNKNOWN
  92. /// <para>Unknown architecture.</para>
  93. /// </summary>
  94. Unknown = 65535
  95. }
  96. #endregion // ProcessorArchitecture Name enum
  97. #region Properties
  98. #region IsServer
  99. private static bool _isServer;
  100. /// <summary>Gets a value indicating whether the operating system is a server operating system.</summary>
  101. /// <value><see langword="true"/> if the current operating system is a server operating system; otherwise, <see langword="false"/>.</value>
  102. public static bool IsServer
  103. {
  104. get
  105. {
  106. if (_servicePackVersion == null)
  107. UpdateData();
  108. return _isServer;
  109. }
  110. }
  111. #endregion // IsServer
  112. #region IsWow64Process
  113. private static bool? _isWow64Process;
  114. /// <summary>Gets a value indicating whether the current process is running under WOW64.</summary>
  115. /// <value><see langword="true"/> if the current process is running under WOW64; otherwise, <see langword="false"/>.</value>
  116. public static bool IsWow64Process
  117. {
  118. get
  119. {
  120. if (_isWow64Process == null)
  121. {
  122. bool value;
  123. IntPtr processHandle = Process.GetCurrentProcess().Handle;
  124. if (!NativeMethods.IsWow64Process(processHandle, out value))
  125. Marshal.ThrowExceptionForHR(Marshal.GetLastWin32Error());
  126. // A pointer to a value that is set to TRUE if the process is running under WOW64.
  127. // If the process is running under 32-bit Windows, the value is set to FALSE.
  128. // If the process is a 64-bit application running under 64-bit Windows, the value is also set to FALSE.
  129. _isWow64Process = value;
  130. }
  131. return (bool) _isWow64Process;
  132. }
  133. }
  134. #endregion // IsWow64Process
  135. #region OSVersion
  136. private static Version _osVersion;
  137. /// <summary>Gets the numeric version of the operating system.</summary>
  138. /// <value>The numeric version of the operating system.</value>
  139. public static Version OSVersion
  140. {
  141. get
  142. {
  143. if (_osVersion == null)
  144. UpdateData();
  145. return _osVersion;
  146. }
  147. }
  148. #endregion // OSVersion
  149. #region VersionName
  150. private static EnumOsName _enumOsName = EnumOsName.Later;
  151. /// <summary>Gets the named version of the operating system.</summary>
  152. /// <value>The named version of the operating system.</value>
  153. public static EnumOsName VersionName
  154. {
  155. get
  156. {
  157. if (_servicePackVersion == null)
  158. UpdateData();
  159. return _enumOsName;
  160. }
  161. }
  162. #endregion // VersionName
  163. #region ProcessorArchitecture
  164. private static EnumProcessorArchitecture _processorArchitecture;
  165. /// <summary>Gets the processor architecture for which the operating system is targeted.</summary>
  166. /// <value>The processor architecture for which the operating system is targeted.</value>
  167. /// <remarks>If running under WOW64 this will return a 32-bit processor. Use <see cref="IsWow64Process"/> to determine if this is the case.</remarks>
  168. public static EnumProcessorArchitecture ProcessorArchitecture
  169. {
  170. get
  171. {
  172. if (_servicePackVersion == null)
  173. UpdateData();
  174. return _processorArchitecture;
  175. }
  176. }
  177. #endregion // ProcessorArchitecture
  178. #region ServicePackVersion
  179. private static Version _servicePackVersion;
  180. /// <summary>Gets the version of the service pack currently installed on the operating system.</summary>
  181. /// <value>The version of the service pack currently installed on the operating system.</value>
  182. /// <remarks>Only the <see cref="System.Version.Major"/> and <see cref="System.Version.Minor"/> fields are used.</remarks>
  183. public static Version ServicePackVersion
  184. {
  185. get
  186. {
  187. if (_servicePackVersion == null)
  188. UpdateData();
  189. return _servicePackVersion;
  190. }
  191. }
  192. #endregion // ServicePackVersion
  193. #endregion // Properties
  194. #region Methods
  195. #region IsAtLeast
  196. /// <summary>Determines whether the operating system is of the specified version or later.</summary>
  197. /// <returns><see langword="true"/> if the operating system is of the specified <paramref name="version"/> or later; otherwise, <see langword="false"/>.</returns>
  198. /// <param name="version">The lowest version for which to return true.</param>
  199. public static bool IsAtLeast(EnumOsName version)
  200. {
  201. return VersionName >= version;
  202. }
  203. /// <summary>Determines whether the operating system is of the specified version or later, allowing specification of a minimum service pack that must be installed on the lowest version.</summary>
  204. /// <returns><see langword="true"/> if the operating system matches the specified <paramref name="version"/> with the specified service pack, or if the operating system is of a later version; otherwise, <see langword="false"/>.</returns>
  205. /// <param name="version">The minimum required version.</param>
  206. /// <param name="servicePackVersion">The major version of the service pack that must be installed on the minimum required version to return true. This can be 0 to indicate that no service pack is required.</param>
  207. public static bool IsAtLeast(EnumOsName version, int servicePackVersion)
  208. {
  209. return IsAtLeast(version) && ServicePackVersion.Major >= servicePackVersion;
  210. }
  211. #endregion // IsAtLeast
  212. #endregion // Methods
  213. #region Private
  214. [SuppressMessage("Microsoft.Naming", "CA2204:Literals should be spelled correctly", MessageId = "RtlGetVersion")]
  215. private static void UpdateData()
  216. {
  217. var verInfo = new NativeMethods.RTL_OSVERSIONINFOEXW();
  218. // Needed to prevent: System.Runtime.InteropServices.COMException:
  219. // The data area passed to a system call is too small. (Exception from HRESULT: 0x8007007A)
  220. verInfo.dwOSVersionInfoSize = Marshal.SizeOf(verInfo);
  221. var sysInfo = new NativeMethods.SYSTEM_INFO();
  222. NativeMethods.GetNativeSystemInfo(ref sysInfo);
  223. // RtlGetVersion returns STATUS_SUCCESS (0).
  224. if (NativeMethods.RtlGetVersion(ref verInfo))
  225. throw new Win32Exception(Marshal.GetLastWin32Error(), "Function RtlGetVersion() failed to retrieve the operating system information.");
  226. _osVersion = new Version(verInfo.dwMajorVersion, verInfo.dwMinorVersion, verInfo.dwBuildNumber);
  227. _processorArchitecture = (EnumProcessorArchitecture) sysInfo.wProcessorArchitecture;
  228. _servicePackVersion = new Version(verInfo.wServicePackMajor, verInfo.wServicePackMinor);
  229. _isServer = verInfo.wProductType == NativeMethods.VER_NT_DOMAIN_CONTROLLER || verInfo.wProductType == NativeMethods.VER_NT_SERVER;
  230. // RtlGetVersion: https://msdn.microsoft.com/en-us/library/windows/hardware/ff561910%28v=vs.85%29.aspx
  231. // The following table summarizes the most recent operating system version numbers.
  232. // Operating system Version number Other
  233. // ================================================================================
  234. // Windows 10 10.0 OSVERSIONINFOEX.wProductType == VER_NT_WORKSTATION
  235. // Windows Server 10.0 OSVERSIONINFOEX.wProductType != VER_NT_WORKSTATION
  236. // Windows 8.1 6.3 OSVERSIONINFOEX.wProductType == VER_NT_WORKSTATION
  237. // Windows Server 2012 R2 6.3 OSVERSIONINFOEX.wProductType != VER_NT_WORKSTATION
  238. // Windows 8 6.2 OSVERSIONINFOEX.wProductType == VER_NT_WORKSTATION
  239. // Windows Server 2012 6.2 OSVERSIONINFOEX.wProductType != VER_NT_WORKSTATION
  240. // Windows 7 6.1 OSVERSIONINFOEX.wProductType == VER_NT_WORKSTATION
  241. // Windows Server 2008 R2 6.1 OSVERSIONINFOEX.wProductType != VER_NT_WORKSTATION
  242. // Windows Server 2008 6.0 OSVERSIONINFOEX.wProductType != VER_NT_WORKSTATION
  243. // Windows Vista 6.0 OSVERSIONINFOEX.wProductType == VER_NT_WORKSTATION
  244. // Windows Server 2003 R2 5.2 GetSystemMetrics(SM_SERVERR2) != 0
  245. // Windows Server 2003 5.2 GetSystemMetrics(SM_SERVERR2) == 0
  246. // Windows XP 64-Bit Edition 5.2 (OSVERSIONINFOEX.wProductType == VER_NT_WORKSTATION) && (sysInfo.PaName == PaName.X64)
  247. // Windows XP 5.1 Not applicable
  248. // Windows 2000 5.0 Not applicable
  249. // 10 == The lastest MajorVersion of Windows.
  250. if (verInfo.dwMajorVersion > 10)
  251. _enumOsName = EnumOsName.Later;
  252. else
  253. switch (verInfo.dwMajorVersion)
  254. {
  255. #region Version 10
  256. case 10:
  257. switch (verInfo.dwMinorVersion)
  258. {
  259. // Windows 10 or Windows Server
  260. case 0:
  261. _enumOsName = (verInfo.wProductType == NativeMethods.VER_NT_WORKSTATION)
  262. ? EnumOsName.Windows10
  263. : EnumOsName.WindowsServer;
  264. break;
  265. }
  266. break;
  267. #endregion // Version 10
  268. #region Version 6
  269. case 6:
  270. switch (verInfo.dwMinorVersion)
  271. {
  272. // Windows Vista or Windows Server 2008
  273. case 0:
  274. _enumOsName = (verInfo.wProductType == NativeMethods.VER_NT_WORKSTATION)
  275. ? EnumOsName.WindowsVista
  276. : EnumOsName.WindowsServer2008;
  277. break;
  278. // Windows 7 or Windows Server 2008 R2
  279. case 1:
  280. _enumOsName = (verInfo.wProductType == NativeMethods.VER_NT_WORKSTATION)
  281. ? EnumOsName.Windows7
  282. : EnumOsName.WindowsServer2008R2;
  283. break;
  284. // Windows 8 or Windows Server 2012
  285. case 2:
  286. _enumOsName = (verInfo.wProductType == NativeMethods.VER_NT_WORKSTATION)
  287. ? EnumOsName.Windows8
  288. : EnumOsName.WindowsServer2012;
  289. break;
  290. // Windows 8.1 or Windows Server 2012 R2
  291. case 3:
  292. _enumOsName = (verInfo.wProductType == NativeMethods.VER_NT_WORKSTATION)
  293. ? EnumOsName.Windows81
  294. : EnumOsName.WindowsServer2012R2;
  295. break;
  296. default:
  297. _enumOsName = EnumOsName.Later;
  298. break;
  299. }
  300. break;
  301. #endregion // Version 6
  302. #region Version 5
  303. case 5:
  304. switch (verInfo.dwMinorVersion)
  305. {
  306. case 0:
  307. _enumOsName = EnumOsName.Windows2000;
  308. break;
  309. case 1:
  310. _enumOsName = EnumOsName.WindowsXP;
  311. break;
  312. case 2:
  313. _enumOsName = (verInfo.wProductType == NativeMethods.VER_NT_WORKSTATION && _processorArchitecture == EnumProcessorArchitecture.X64)
  314. ? EnumOsName.WindowsXP
  315. : (verInfo.wProductType != NativeMethods.VER_NT_WORKSTATION) ? EnumOsName.WindowsServer2003 : EnumOsName.Later;
  316. break;
  317. default:
  318. _enumOsName = EnumOsName.Later;
  319. break;
  320. }
  321. break;
  322. #endregion // Version 5
  323. default:
  324. _enumOsName = EnumOsName.Earlier;
  325. break;
  326. }
  327. }
  328. #region P/Invoke members / NativeMethods
  329. private static class NativeMethods
  330. {
  331. internal const short VER_NT_WORKSTATION = 1;
  332. internal const short VER_NT_DOMAIN_CONTROLLER = 2;
  333. internal const short VER_NT_SERVER = 3;
  334. [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
  335. internal struct RTL_OSVERSIONINFOEXW
  336. {
  337. public int dwOSVersionInfoSize;
  338. public readonly int dwMajorVersion;
  339. public readonly int dwMinorVersion;
  340. public readonly int dwBuildNumber;
  341. public readonly int dwPlatformId;
  342. [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 128)]
  343. public readonly string szCSDVersion;
  344. public readonly ushort wServicePackMajor;
  345. public readonly ushort wServicePackMinor;
  346. public readonly ushort wSuiteMask;
  347. public readonly byte wProductType;
  348. public readonly byte wReserved;
  349. }
  350. [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
  351. internal struct SYSTEM_INFO
  352. {
  353. public readonly ushort wProcessorArchitecture;
  354. private readonly ushort wReserved;
  355. public readonly uint dwPageSize;
  356. public readonly IntPtr lpMinimumApplicationAddress;
  357. public readonly IntPtr lpMaximumApplicationAddress;
  358. public readonly IntPtr dwActiveProcessorMask;
  359. public readonly uint dwNumberOfProcessors;
  360. public readonly uint dwProcessorType;
  361. public readonly uint dwAllocationGranularity;
  362. public readonly ushort wProcessorLevel;
  363. public readonly ushort wProcessorRevision;
  364. }
  365. /// <summary>The RtlGetVersion routine returns version information about the currently running operating system.</summary>
  366. /// <returns>RtlGetVersion returns STATUS_SUCCESS.</returns>
  367. /// <remarks>Available starting with Windows 2000.</remarks>
  368. [SuppressMessage("Microsoft.Security", "CA5122:PInvokesShouldNotBeSafeCriticalFxCopRule"), DllImport("ntdll.dll", SetLastError = true, CharSet = CharSet.Unicode)]
  369. [return: MarshalAs(UnmanagedType.Bool)]
  370. internal static extern bool RtlGetVersion([MarshalAs(UnmanagedType.Struct)] ref RTL_OSVERSIONINFOEXW lpVersionInformation);
  371. /// <summary>Retrieves information about the current system to an application running under WOW64.
  372. /// If the function is called from a 64-bit application, it is equivalent to the GetSystemInfo function.
  373. /// </summary>
  374. /// <returns>This function does not return a value.</returns>
  375. /// <remarks>To determine whether a Win32-based application is running under WOW64, call the <see cref="IsWow64Process"/> function.</remarks>
  376. /// <remarks>Minimum supported client: Windows XP [desktop apps | Windows Store apps]</remarks>
  377. /// <remarks>Minimum supported server: Windows Server 2003 [desktop apps | Windows Store apps]</remarks>
  378. [SuppressMessage("Microsoft.Security", "CA5122:PInvokesShouldNotBeSafeCriticalFxCopRule")]
  379. [DllImport("kernel32.dll", SetLastError = false, CharSet = CharSet.Unicode), SuppressUnmanagedCodeSecurity]
  380. internal static extern void GetNativeSystemInfo([MarshalAs(UnmanagedType.Struct)] ref SYSTEM_INFO lpSystemInfo);
  381. /// <summary>Determines whether the specified process is running under WOW64.</summary>
  382. /// <returns>
  383. /// If the function succeeds, the return value is a nonzero value.
  384. /// If the function fails, the return value is zero. To get extended error information, call GetLastError.
  385. /// </returns>
  386. /// <remarks>Minimum supported client: Windows Vista, Windows XP with SP2 [desktop apps only]</remarks>
  387. /// <remarks>Minimum supported server: Windows Server 2008, Windows Server 2003 with SP1 [desktop apps only]</remarks>
  388. [SuppressMessage("Microsoft.Security", "CA5122:PInvokesShouldNotBeSafeCriticalFxCopRule")]
  389. [DllImport("kernel32.dll", SetLastError = false, CharSet = CharSet.Unicode), SuppressUnmanagedCodeSecurity]
  390. [return: MarshalAs(UnmanagedType.Bool)]
  391. internal static extern bool IsWow64Process([In] IntPtr hProcess, [Out, MarshalAs(UnmanagedType.Bool)] out bool lpSystemInfo);
  392. }
  393. #endregion // P/Invoke members / NativeMethods
  394. #endregion // Private
  395. }
  396. }