From f1aada8cc6419c3c9934fa880c8f6e5c59e23385 Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Mon, 13 Mar 2023 13:08:19 +0200 Subject: Windows --- libbutl/host-os-release.cxx | 85 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 85 insertions(+) (limited to 'libbutl/host-os-release.cxx') diff --git a/libbutl/host-os-release.cxx b/libbutl/host-os-release.cxx index fff05f7..f13f62c 100644 --- a/libbutl/host-os-release.cxx +++ b/libbutl/host-os-release.cxx @@ -8,11 +8,16 @@ #include #include +#include #include #include #include // file_exists() #include // parse_quoted() +#ifdef _WIN32 +# include +#endif + using namespace std; namespace butl @@ -201,6 +206,83 @@ namespace butl } } + static os_release + host_os_release_windows () + { +#ifdef _WIN32 + // The straightforward way to get the version would be the GetVersionEx() + // Win32 function. However, if the application is built with a certain + // assembly manifest, this function will return the version the + // application was built for rather than what's actually running. + // + // The other plausible options are to call the `ver` program and parse it + // output (of questionable regularity) or to call RtlGetVersion(). The + // latter combined with GetProcAddress() seems to be a widely-used + // approach, so we are going with that (seeing that we employ a similar + // technique in quite a few places). + // + HMODULE nh (GetModuleHandle ("ntdll.dll")); + if (nh == nullptr) + throw runtime_error ("unable to get handle to ntdll.dll"); + + using RtlGetVersion = LONG /*NTSTATUS*/ (WINAPI*)(PRTL_OSVERSIONINFOW); + + RtlGetVersion gv ( + function_cast ( + GetProcAddress (nh, "RtlGetVersion"))); + + // RtlGetVersion() is available from Windows 2000 which is way before + // anything we might possibly care about (e.g., XP or 7). + // + if (gv == nullptr) + throw runtime_error ("unable to get address of RtlGetVersion()"); + + RTL_OSVERSIONINFOW vi; + vi.dwOSVersionInfoSize = sizeof (vi); + gv (&vi); // Always succeeds, according to documentation. + + // Ok, the real mess starts here. Here is how the commonly known Windows + // versions correspond to the major/minor/build numbers and how we will + // map them (note that there are also Server versions in the mix; see the + // OSVERSIONINFOEXW struct documentation for the complete picture): + // + // major minor build mapped + // Windows 11 10 0 >=22000 11 + // Windows 10 10 0 <22000 10 + // Windows 8.1 6 3 8.1 + // Windows 8 6 2 8 + // Windows 7 6 1 7 + // Windows Vista 6 0 6 + // Windows XP Pro/64-bit 5 2 5.2 + // Windows XP 5 1 5.1 + // Windows 2000 5 0 5 + // + // Based on this it's probably not wise to try to map any future versions + // automatically. + // + string v; + if (vi.dwMajorVersion == 10 && vi.dwMinorVersion == 0) + { + v = vi.dwBuildNumber >= 22000 ? "11" : "10"; + } + else if (vi.dwMajorVersion == 6 && vi.dwMinorVersion == 3) v = "8.1"; + else if (vi.dwMajorVersion == 6 && vi.dwMinorVersion == 2) v = "8"; + else if (vi.dwMajorVersion == 6 && vi.dwMinorVersion == 1) v = "7"; + else if (vi.dwMajorVersion == 6 && vi.dwMinorVersion == 0) v = "6"; + else if (vi.dwMajorVersion == 5 && vi.dwMinorVersion == 2) v = "5.2"; + else if (vi.dwMajorVersion == 5 && vi.dwMinorVersion == 1) v = "5.1"; + else if (vi.dwMajorVersion == 5 && vi.dwMinorVersion == 0) v = "5"; + else throw ("unknown windows version " + + std::to_string (vi.dwMajorVersion) + '.' + + std::to_string (vi.dwMinorVersion) + '.' + + std::to_string (vi.dwBuildNumber)); + + return os_release {"windows", {}, move (v), "", "Windows", "", ""}; +#else + throw runtime_error ("unexpected host operating system"); +#endif + } + optional host_os_release (const target_triplet& h) { @@ -213,6 +295,9 @@ namespace butl if (c == "macos") return host_os_release_macos (); + if (c == "windows") + return host_os_release_windows (); + if (c == "bsd") { // @@ TODO: ideally we would want to run uname and obtain the actual -- cgit v1.1