windows_version.cc 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433
  1. // Copyright (c) 2012 The Chromium Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be
  3. // found in the LICENSE file.
  4. #include "base/win/windows_version.h"
  5. #include <windows.h>
  6. #include <memory>
  7. #include <tuple>
  8. #include <utility>
  9. #include "base/check_op.h"
  10. #include "base/file_version_info_win.h"
  11. #include "base/files/file_path.h"
  12. #include "base/logging.h"
  13. #include "base/no_destructor.h"
  14. #include "base/notreached.h"
  15. #include "base/strings/string_util.h"
  16. #include "base/strings/utf_string_conversions.h"
  17. #include "base/win/registry.h"
  18. #include "build/build_config.h"
  19. #if !defined(__clang__) && _MSC_FULL_VER < 191125507
  20. #error VS 2017 Update 3.2 or higher is required
  21. #endif
  22. #if !defined(NTDDI_WIN10_FE)
  23. #error Windows 10.0.20348.0 SDK or higher required.
  24. #endif
  25. namespace base {
  26. namespace win {
  27. namespace {
  28. // The values under the CurrentVersion registry hive are mirrored under
  29. // the corresponding Wow6432 hive.
  30. constexpr wchar_t kRegKeyWindowsNTCurrentVersion[] =
  31. L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion";
  32. // Returns the "UBR" (Windows 10 patch number) and "DisplayVersion" (or
  33. // "ReleaseId" on earlier versions) (Windows 10 release number) from registry.
  34. // "UBR" is an undocumented value and will be 0 if the value was not found.
  35. // "ReleaseId" will be an empty string if neither new nor old values are found.
  36. std::pair<int, std::string> GetVersionData() {
  37. DWORD ubr = 0;
  38. std::wstring release_id;
  39. RegKey key;
  40. if (key.Open(HKEY_LOCAL_MACHINE, kRegKeyWindowsNTCurrentVersion,
  41. KEY_QUERY_VALUE) == ERROR_SUCCESS) {
  42. key.ReadValueDW(L"UBR", &ubr);
  43. // "DisplayVersion" has been introduced in Windows 10 2009
  44. // when naming changed to mixed letters and numbers.
  45. key.ReadValue(L"DisplayVersion", &release_id);
  46. // Use discontinued "ReleaseId" instead, if the former is unavailable.
  47. if (release_id.empty())
  48. key.ReadValue(L"ReleaseId", &release_id);
  49. }
  50. return std::make_pair(static_cast<int>(ubr), WideToUTF8(release_id));
  51. }
  52. const _SYSTEM_INFO& GetSystemInfoStorage() {
  53. static const _SYSTEM_INFO system_info = [] {
  54. _SYSTEM_INFO info = {};
  55. ::GetNativeSystemInfo(&info);
  56. return info;
  57. }();
  58. return system_info;
  59. }
  60. } // namespace
  61. // static
  62. OSInfo** OSInfo::GetInstanceStorage() {
  63. // Note: we don't use the Singleton class because it depends on AtExitManager,
  64. // and it's convenient for other modules to use this class without it.
  65. static OSInfo* info = []() {
  66. _OSVERSIONINFOEXW version_info = {sizeof(version_info)};
  67. #pragma clang diagnostic push
  68. #pragma clang diagnostic ignored "-Wdeprecated-declarations"
  69. // GetVersionEx() is deprecated, and the suggested replacement are
  70. // the IsWindows*OrGreater() functions in VersionHelpers.h. We can't
  71. // use that because:
  72. // - For Windows 10, there's IsWindows10OrGreater(), but nothing more
  73. // granular. We need to be able to detect different Windows 10 releases
  74. // since they sometimes change behavior in ways that matter.
  75. // - There is no IsWindows11OrGreater() function yet.
  76. ::GetVersionEx(reinterpret_cast<_OSVERSIONINFOW*>(&version_info));
  77. #pragma clang diagnostic pop
  78. DWORD os_type = 0;
  79. ::GetProductInfo(version_info.dwMajorVersion, version_info.dwMinorVersion,
  80. 0, 0, &os_type);
  81. return new OSInfo(version_info, GetSystemInfoStorage(), os_type);
  82. }();
  83. return &info;
  84. }
  85. // static
  86. OSInfo* OSInfo::GetInstance() {
  87. return *GetInstanceStorage();
  88. }
  89. // static
  90. OSInfo::WindowsArchitecture OSInfo::GetArchitecture() {
  91. switch (GetSystemInfoStorage().wProcessorArchitecture) {
  92. case PROCESSOR_ARCHITECTURE_INTEL:
  93. return X86_ARCHITECTURE;
  94. case PROCESSOR_ARCHITECTURE_AMD64:
  95. return X64_ARCHITECTURE;
  96. case PROCESSOR_ARCHITECTURE_IA64:
  97. return IA64_ARCHITECTURE;
  98. case PROCESSOR_ARCHITECTURE_ARM64:
  99. return ARM64_ARCHITECTURE;
  100. default:
  101. return OTHER_ARCHITECTURE;
  102. }
  103. }
  104. OSInfo::OSInfo(const _OSVERSIONINFOEXW& version_info,
  105. const _SYSTEM_INFO& system_info,
  106. DWORD os_type)
  107. : version_(Version::PRE_XP),
  108. wow_process_machine_(WowProcessMachine::kUnknown),
  109. wow_native_machine_(WowNativeMachine::kUnknown) {
  110. version_number_.major = version_info.dwMajorVersion;
  111. version_number_.minor = version_info.dwMinorVersion;
  112. version_number_.build = version_info.dwBuildNumber;
  113. std::tie(version_number_.patch, release_id_) = GetVersionData();
  114. version_ = MajorMinorBuildToVersion(
  115. version_number_.major, version_number_.minor, version_number_.build);
  116. InitializeWowStatusValuesForProcess(GetCurrentProcess());
  117. service_pack_.major = version_info.wServicePackMajor;
  118. service_pack_.minor = version_info.wServicePackMinor;
  119. service_pack_str_ = WideToUTF8(version_info.szCSDVersion);
  120. processors_ = static_cast<int>(system_info.dwNumberOfProcessors);
  121. allocation_granularity_ = system_info.dwAllocationGranularity;
  122. if (version_info.dwMajorVersion == 6 || version_info.dwMajorVersion == 10) {
  123. // Only present on Vista+.
  124. switch (os_type) {
  125. case PRODUCT_CLUSTER_SERVER:
  126. case PRODUCT_DATACENTER_SERVER:
  127. case PRODUCT_DATACENTER_SERVER_CORE:
  128. case PRODUCT_ENTERPRISE_SERVER:
  129. case PRODUCT_ENTERPRISE_SERVER_CORE:
  130. case PRODUCT_ENTERPRISE_SERVER_IA64:
  131. case PRODUCT_SMALLBUSINESS_SERVER:
  132. case PRODUCT_SMALLBUSINESS_SERVER_PREMIUM:
  133. case PRODUCT_STANDARD_SERVER:
  134. case PRODUCT_STANDARD_SERVER_CORE:
  135. case PRODUCT_WEB_SERVER:
  136. version_type_ = SUITE_SERVER;
  137. break;
  138. case PRODUCT_PROFESSIONAL:
  139. case PRODUCT_ULTIMATE:
  140. version_type_ = SUITE_PROFESSIONAL;
  141. break;
  142. case PRODUCT_ENTERPRISE:
  143. case PRODUCT_ENTERPRISE_E:
  144. case PRODUCT_ENTERPRISE_EVALUATION:
  145. case PRODUCT_ENTERPRISE_N:
  146. case PRODUCT_ENTERPRISE_N_EVALUATION:
  147. case PRODUCT_ENTERPRISE_S:
  148. case PRODUCT_ENTERPRISE_S_EVALUATION:
  149. case PRODUCT_ENTERPRISE_S_N:
  150. case PRODUCT_ENTERPRISE_S_N_EVALUATION:
  151. case PRODUCT_BUSINESS:
  152. case PRODUCT_BUSINESS_N:
  153. version_type_ = SUITE_ENTERPRISE;
  154. break;
  155. case PRODUCT_PRO_FOR_EDUCATION:
  156. case PRODUCT_PRO_FOR_EDUCATION_N:
  157. version_type_ = SUITE_EDUCATION_PRO;
  158. break;
  159. case PRODUCT_EDUCATION:
  160. case PRODUCT_EDUCATION_N:
  161. version_type_ = SUITE_EDUCATION;
  162. break;
  163. case PRODUCT_HOME_BASIC:
  164. case PRODUCT_HOME_PREMIUM:
  165. case PRODUCT_STARTER:
  166. default:
  167. version_type_ = SUITE_HOME;
  168. break;
  169. }
  170. } else if (version_info.dwMajorVersion == 5 &&
  171. version_info.dwMinorVersion == 2) {
  172. if (version_info.wProductType == VER_NT_WORKSTATION &&
  173. system_info.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64) {
  174. version_type_ = SUITE_PROFESSIONAL;
  175. } else if (version_info.wSuiteMask & VER_SUITE_WH_SERVER) {
  176. version_type_ = SUITE_HOME;
  177. } else {
  178. version_type_ = SUITE_SERVER;
  179. }
  180. } else if (version_info.dwMajorVersion == 5 &&
  181. version_info.dwMinorVersion == 1) {
  182. if (version_info.wSuiteMask & VER_SUITE_PERSONAL)
  183. version_type_ = SUITE_HOME;
  184. else
  185. version_type_ = SUITE_PROFESSIONAL;
  186. } else {
  187. // Windows is pre XP so we don't care but pick a safe default.
  188. version_type_ = SUITE_HOME;
  189. }
  190. }
  191. OSInfo::~OSInfo() = default;
  192. Version OSInfo::Kernel32Version() const {
  193. static const Version kernel32_version =
  194. MajorMinorBuildToVersion(Kernel32BaseVersion().components()[0],
  195. Kernel32BaseVersion().components()[1],
  196. Kernel32BaseVersion().components()[2]);
  197. return kernel32_version;
  198. }
  199. OSInfo::VersionNumber OSInfo::Kernel32VersionNumber() const {
  200. DCHECK_EQ(Kernel32BaseVersion().components().size(), 4u);
  201. static const VersionNumber version = {
  202. .major = Kernel32BaseVersion().components()[0],
  203. .minor = Kernel32BaseVersion().components()[1],
  204. .build = Kernel32BaseVersion().components()[2],
  205. .patch = Kernel32BaseVersion().components()[3]};
  206. return version;
  207. }
  208. // Retrieve a version from kernel32. This is useful because when running in
  209. // compatibility mode for a down-level version of the OS, the file version of
  210. // kernel32 will still be the "real" version.
  211. base::Version OSInfo::Kernel32BaseVersion() const {
  212. static const NoDestructor<base::Version> version([] {
  213. std::unique_ptr<FileVersionInfoWin> file_version_info =
  214. FileVersionInfoWin::CreateFileVersionInfoWin(
  215. FilePath(FILE_PATH_LITERAL("kernel32.dll")));
  216. if (!file_version_info) {
  217. // crbug.com/912061: on some systems it seems kernel32.dll might be
  218. // corrupted or not in a state to get version info. In this case try
  219. // kernelbase.dll as a fallback.
  220. file_version_info = FileVersionInfoWin::CreateFileVersionInfoWin(
  221. FilePath(FILE_PATH_LITERAL("kernelbase.dll")));
  222. }
  223. CHECK(file_version_info);
  224. return file_version_info->GetFileVersion();
  225. }());
  226. return *version;
  227. }
  228. bool OSInfo::IsWowDisabled() const {
  229. return (wow_process_machine_ == WowProcessMachine::kDisabled);
  230. }
  231. bool OSInfo::IsWowX86OnAMD64() const {
  232. return (wow_process_machine_ == WowProcessMachine::kX86 &&
  233. wow_native_machine_ == WowNativeMachine::kAMD64);
  234. }
  235. bool OSInfo::IsWowX86OnARM64() const {
  236. return (wow_process_machine_ == WowProcessMachine::kX86 &&
  237. wow_native_machine_ == WowNativeMachine::kARM64);
  238. }
  239. bool OSInfo::IsWowAMD64OnARM64() const {
  240. #if defined(ARCH_CPU_X86_64)
  241. // An AMD64 process running on an ARM64 device results in the incorrect
  242. // identification of the device architecture (AMD64 is reported). However,
  243. // IsWow64Process2 will return the correct device type for the native
  244. // machine, even though the OS doesn't consider an AMD64 process on an ARM64
  245. // processor a classic Windows-on-Windows setup.
  246. return (wow_process_machine_ == WowProcessMachine::kDisabled &&
  247. wow_native_machine_ == WowNativeMachine::kARM64);
  248. #else
  249. return false;
  250. #endif
  251. }
  252. bool OSInfo::IsWowX86OnOther() const {
  253. return (wow_process_machine_ == WowProcessMachine::kX86 &&
  254. wow_native_machine_ == WowNativeMachine::kOther);
  255. }
  256. std::string OSInfo::processor_model_name() {
  257. if (processor_model_name_.empty()) {
  258. const wchar_t kProcessorNameString[] =
  259. L"HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\0";
  260. RegKey key(HKEY_LOCAL_MACHINE, kProcessorNameString, KEY_READ);
  261. std::wstring value;
  262. key.ReadValue(L"ProcessorNameString", &value);
  263. processor_model_name_ = WideToUTF8(value);
  264. }
  265. return processor_model_name_;
  266. }
  267. // With the exception of Server 2003, server variants are treated the same as
  268. // the corresponding workstation release.
  269. // static
  270. Version OSInfo::MajorMinorBuildToVersion(uint32_t major,
  271. uint32_t minor,
  272. uint32_t build) {
  273. if (major == 11)
  274. return Version::WIN11;
  275. if (major == 10) {
  276. if (build >= 22000)
  277. return Version::WIN11;
  278. if (build >= 20348)
  279. return Version::SERVER_2022;
  280. if (build >= 19044)
  281. return Version::WIN10_21H2;
  282. if (build >= 19043)
  283. return Version::WIN10_21H1;
  284. if (build >= 19042)
  285. return Version::WIN10_20H2;
  286. if (build >= 19041)
  287. return Version::WIN10_20H1;
  288. if (build >= 18363)
  289. return Version::WIN10_19H2;
  290. if (build >= 18362)
  291. return Version::WIN10_19H1;
  292. if (build >= 17763)
  293. return Version::WIN10_RS5;
  294. if (build >= 17134)
  295. return Version::WIN10_RS4;
  296. if (build >= 16299)
  297. return Version::WIN10_RS3;
  298. if (build >= 15063)
  299. return Version::WIN10_RS2;
  300. if (build >= 14393)
  301. return Version::WIN10_RS1;
  302. if (build >= 10586)
  303. return Version::WIN10_TH2;
  304. return Version::WIN10;
  305. }
  306. if (major > 6) {
  307. // Hitting this likely means that it's time for a >11 block above.
  308. NOTREACHED() << major << "." << minor << "." << build;
  309. return Version::WIN_LAST;
  310. }
  311. if (major == 6) {
  312. switch (minor) {
  313. case 0:
  314. return Version::VISTA;
  315. case 1:
  316. return Version::WIN7;
  317. case 2:
  318. return Version::WIN8;
  319. default:
  320. DCHECK_EQ(minor, 3u);
  321. return Version::WIN8_1;
  322. }
  323. }
  324. if (major == 5 && minor != 0) {
  325. // Treat XP Pro x64, Home Server, and Server 2003 R2 as Server 2003.
  326. return minor == 1 ? Version::XP : Version::SERVER_2003;
  327. }
  328. // Win 2000 or older.
  329. return Version::PRE_XP;
  330. }
  331. Version GetVersion() {
  332. return OSInfo::GetInstance()->version();
  333. }
  334. OSInfo::WowProcessMachine OSInfo::GetWowProcessMachineArchitecture(
  335. const int process_machine) {
  336. switch (process_machine) {
  337. case IMAGE_FILE_MACHINE_UNKNOWN:
  338. return OSInfo::WowProcessMachine::kDisabled;
  339. case IMAGE_FILE_MACHINE_I386:
  340. return OSInfo::WowProcessMachine::kX86;
  341. case IMAGE_FILE_MACHINE_ARM:
  342. case IMAGE_FILE_MACHINE_THUMB:
  343. case IMAGE_FILE_MACHINE_ARMNT:
  344. return OSInfo::WowProcessMachine::kARM32;
  345. }
  346. return OSInfo::WowProcessMachine::kOther;
  347. }
  348. OSInfo::WowNativeMachine OSInfo::GetWowNativeMachineArchitecture(
  349. const int native_machine) {
  350. switch (native_machine) {
  351. case IMAGE_FILE_MACHINE_ARM64:
  352. return OSInfo::WowNativeMachine::kARM64;
  353. case IMAGE_FILE_MACHINE_AMD64:
  354. return OSInfo::WowNativeMachine::kAMD64;
  355. }
  356. return OSInfo::WowNativeMachine::kOther;
  357. }
  358. void OSInfo::InitializeWowStatusValuesFromLegacyApi(HANDLE process_handle) {
  359. BOOL is_wow64 = FALSE;
  360. if (!::IsWow64Process(process_handle, &is_wow64))
  361. return;
  362. if (is_wow64) {
  363. wow_process_machine_ = WowProcessMachine::kX86;
  364. wow_native_machine_ = WowNativeMachine::kAMD64;
  365. } else {
  366. wow_process_machine_ = WowProcessMachine::kDisabled;
  367. }
  368. }
  369. void OSInfo::InitializeWowStatusValuesForProcess(HANDLE process_handle) {
  370. static const auto is_wow64_process2 =
  371. reinterpret_cast<decltype(&IsWow64Process2)>(::GetProcAddress(
  372. ::GetModuleHandle(L"kernel32.dll"), "IsWow64Process2"));
  373. if (!is_wow64_process2) {
  374. InitializeWowStatusValuesFromLegacyApi(process_handle);
  375. return;
  376. }
  377. USHORT process_machine = IMAGE_FILE_MACHINE_UNKNOWN;
  378. USHORT native_machine = IMAGE_FILE_MACHINE_UNKNOWN;
  379. if (!is_wow64_process2(process_handle, &process_machine, &native_machine)) {
  380. return;
  381. }
  382. wow_process_machine_ = GetWowProcessMachineArchitecture(process_machine);
  383. wow_native_machine_ = GetWowNativeMachineArchitecture(native_machine);
  384. }
  385. } // namespace win
  386. } // namespace base