Skip to content
Merged
68 changes: 54 additions & 14 deletions lib/pal/desktop/WindowsDesktopSystemInformationImpl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@

#include "WindowsEnvironmentInfo.hpp"

#include <cstdint>
#include <string>

// This define is only available for TH1+
Expand Down Expand Up @@ -139,17 +140,44 @@ namespace PAL_NS_BEGIN {
std::to_string(static_cast<int>(LOWORD(pffi->dwProductVersionLS)));
}

/**
* Get OS BuildLabEx string
*/
std::string getOsBuildLabEx()
const PCSTR c_currentVersion_Key = "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion";

bool getCurrentVersionDwordValue(PCSTR valueName, uint32_t& value)
{
char buff[MAX_PATH] = { 0 };
const PCSTR c_currentVersion_Key = "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion";
const PCSTR c_buildLabEx_ValueName = "BuildLabEx";
DWORD size = sizeof(buff);
RegGetValueA(HKEY_LOCAL_MACHINE, c_currentVersion_Key, c_buildLabEx_ValueName, RRF_RT_REG_SZ | RRF_SUBKEY_WOW6464KEY, NULL, (char*)buff, &size);
return buff;
DWORD regValue = 0;
DWORD size = sizeof(regValue);
if (RegGetValueA(
HKEY_LOCAL_MACHINE,
c_currentVersion_Key,
valueName,
RRF_RT_REG_DWORD | RRF_SUBKEY_WOW6464KEY,
NULL,
&regValue,
&size) != ERROR_SUCCESS)
{
return false;
}

value = regValue;
return true;
}

std::string formatWindowsOsFullVersion(
unsigned long majorVersion,
unsigned long minorVersion,
unsigned long buildNumber,
uint32_t updateBuildRevision,
bool hasUpdateBuildRevision)
{
std::string version = std::to_string(majorVersion) + "." +
std::to_string(minorVersion) + "." +
std::to_string(static_cast<long long>(buildNumber));
if (hasUpdateBuildRevision)
{
version += "." + std::to_string(updateBuildRevision);
}

return version;
}

/**
Expand All @@ -175,8 +203,6 @@ namespace PAL_NS_BEGIN {
*/
void getOsVersion(std::string& osMajorVersion, std::string& osFullVersion)
{
std::string buildLabEx = getOsBuildLabEx();

HMODULE hNtDll = ::GetModuleHandle(TEXT("ntdll.dll"));
typedef HRESULT NTSTATUS;
typedef NTSTATUS(__stdcall * RtlGetVersion_t)(PRTL_OSVERSIONINFOW);
Expand All @@ -186,8 +212,22 @@ namespace PAL_NS_BEGIN {
if (pRtlGetVersion && SUCCEEDED(pRtlGetVersion(&rtlOsvi)))
{
osMajorVersion = std::to_string((long long)rtlOsvi.dwMajorVersion) + "." + std::to_string((long long)rtlOsvi.dwMinorVersion);
osFullVersion = osMajorVersion + "." + std::to_string((long long)rtlOsvi.dwBuildNumber);
osFullVersion = osMajorVersion + "." + buildLabEx;

// Use the kernel's authoritative dwBuildNumber from RtlGetVersion
// (see Windows team guidance on issue #1407). The registry
// CurrentBuildNumber / CurrentBuild values are only useful for
// offline reads of another Windows installation; for the running
// OS they are always in sync with the kernel build, so prefer the
// API. UBR is registry-only and is always safe to combine with the
// kernel build number.
uint32_t updateBuildRevision = 0;
bool hasUpdateBuildRevision = getCurrentVersionDwordValue("UBR", updateBuildRevision);
osFullVersion = formatWindowsOsFullVersion(
rtlOsvi.dwMajorVersion,
rtlOsvi.dwMinorVersion,
rtlOsvi.dwBuildNumber,
updateBuildRevision,
hasUpdateBuildRevision);
}
}

Expand Down
37 changes: 37 additions & 0 deletions tests/unittests/PalTests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@
#include "pal/PseudoRandomGenerator.hpp"
#include "Version.hpp"

#include <cstdint>
#include <string>

#ifdef HAVE_MAT_LOGGING
#include "pal/PAL.hpp"
#include <gtest/gtest.h>
Expand All @@ -24,6 +27,17 @@ using namespace PAL::detail;

using namespace testing;

#if defined(_WIN32) || defined(_WIN64)
namespace PAL_NS_BEGIN {
std::string formatWindowsOsFullVersion(
unsigned long majorVersion,
unsigned long minorVersion,
unsigned long buildNumber,
uint32_t updateBuildRevision,
bool hasUpdateBuildRevision);
} PAL_NS_END
#endif

class PalTests : public Test {};

TEST_F(PalTests, UuidGeneration)
Expand Down Expand Up @@ -95,6 +109,29 @@ TEST_F(PalTests, FormatUtcTimestampMsAsISO8601)
EXPECT_THAT(PAL::formatUtcTimestampMsAsISO8601(2147483647999ll), Eq("2038-01-19T03:14:07.999Z"));
}

#if defined(_WIN32) || defined(_WIN64)
TEST_F(PalTests, WindowsOsFullVersionIncludesUbrWhenPresent)
{
EXPECT_THAT(
PAL::formatWindowsOsFullVersion(10, 0, 26200, 1234, true),
Eq("10.0.26200.1234"));
}

TEST_F(PalTests, WindowsOsFullVersionOmitsUbrWhenMissing)
{
EXPECT_THAT(
PAL::formatWindowsOsFullVersion(10, 0, 26200, 0, false),
Eq("10.0.26200"));
}

TEST_F(PalTests, WindowsOsFullVersionIncludesZeroUbrWhenPresent)
{
EXPECT_THAT(
PAL::formatWindowsOsFullVersion(10, 0, 26200, 0, true),
Eq("10.0.26200.0"));
}
#endif

TEST_F(PalTests, MonotonicTime)
{
int64_t t0 = PAL::getMonotonicTimeMs();
Expand Down
Loading