Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Improve :func:`sys.getwindowsversion`, use RtlGetVersion() and allow build
for Universal Windows Platform.
60 changes: 27 additions & 33 deletions Python/sysmodule.c
Original file line number Diff line number Diff line change
Expand Up @@ -1645,50 +1645,40 @@ static PyStructSequence_Desc windows_version_desc = {
via indexing, the rest are name only */
};

static PyObject *
_sys_getwindowsversion_from_kernel32(void)
static PyObject*
_sys_getwindowsversion_from_RtlGetVersion(void)
{
#ifndef MS_WINDOWS_DESKTOP
PyErr_SetString(PyExc_OSError, "cannot read version info on this platform");
return NULL;
#else
HANDLE hKernel32;
wchar_t kernel32_path[MAX_PATH];
LPVOID verblock;
DWORD verblock_size;
VS_FIXEDFILEINFO *ffi;
UINT ffi_len;
DWORD realMajor, realMinor, realBuild;
HMODULE hMod;
typedef LONG NTSTATUS, * PNTSTATUS;
typedef NTSTATUS(WINAPI* RtlGetVersionPtr)(PRTL_OSVERSIONINFOW);
RtlGetVersionPtr RtlGetVer;

RTL_OSVERSIONINFOW osVerInfo;
ZeroMemory(&osVerInfo, sizeof(osVerInfo));
osVerInfo.dwOSVersionInfoSize = sizeof(osVerInfo);

Py_BEGIN_ALLOW_THREADS
hKernel32 = GetModuleHandleW(L"kernel32.dll");
hMod = GetModuleHandleW(L"ntdll.dll");

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No thanks, we don't use ntdll from CPython.

We should be changing this function to return the version number controlled by the compatibility shims, because it will match the other compatibility behaviours that are associated with that version.

The platform module exists to get the "real" version.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we went down the "read kernel32" path because this function used to be used by platform, but that module should be fully relying on WMI now, which is more accurate (if slower, but checking the version number shouldn't be a common task, and certainly not one that's used in tight loops).

@MatejKafka MatejKafka Jul 23, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not use ntdll? RtlGetVersion is now documented and used by many other projects.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Documented due to a government order isn't really a compelling reason to treat it as the intended API, nor is "everyone else does it".

ntdll/ntoskrnl are the wrong layer. Just as we don't appreciate/encourage people to use our internal-level APIs, we should not be doing it to others.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But the more fundamental reason is that the point of this API is to find out what OS features are available, and if you're under compatibility shims, they may not be. In that case, getting the "real" version is going to be a lie.

So the right answer to "what version should I assume is running" is the one that GetVersion returns.

Py_END_ALLOW_THREADS
if (!hKernel32 || !GetModuleFileNameW(hKernel32, kernel32_path, MAX_PATH)) {
if (!hMod) {
PyErr_SetFromWindowsErr(0);
return NULL;
}
verblock_size = GetFileVersionInfoSizeW(kernel32_path, NULL);
if (!verblock_size) {

Py_BEGIN_ALLOW_THREADS
RtlGetVer = (RtlGetVersionPtr)GetProcAddress(hMod, "RtlGetVersion");
Py_END_ALLOW_THREADS
if (!RtlGetVer) {
PyErr_SetFromWindowsErr(0);
return NULL;
}
verblock = PyMem_RawMalloc(verblock_size);
if (!verblock ||
!GetFileVersionInfoW(kernel32_path, 0, verblock_size, verblock) ||
!VerQueryValueW(verblock, L"", (LPVOID)&ffi, &ffi_len)) {

if (0 != RtlGetVer(&osVerInfo)) {
PyErr_SetFromWindowsErr(0);
if (verblock) {
PyMem_RawFree(verblock);
}
return NULL;
}

realMajor = HIWORD(ffi->dwProductVersionMS);
realMinor = LOWORD(ffi->dwProductVersionMS);
realBuild = HIWORD(ffi->dwProductVersionLS);
PyMem_RawFree(verblock);
return Py_BuildValue("(kkk)", realMajor, realMinor, realBuild);
#endif /* !MS_WINDOWS_DESKTOP */
return Py_BuildValue("(kkk)", osVerInfo.dwMajorVersion, osVerInfo.dwMinorVersion, osVerInfo.dwBuildNumber);
}

/* Disable deprecation warnings about GetVersionEx as the result is
Expand Down Expand Up @@ -1719,7 +1709,6 @@ sys_getwindowsversion_impl(PyObject *module)
{
PyObject *version;
int pos = 0;
OSVERSIONINFOEXW ver;

if (PyObject_GetOptionalAttrString(module, "_cached_windows_version", &version) < 0) {
return NULL;
Expand All @@ -1729,6 +1718,8 @@ sys_getwindowsversion_impl(PyObject *module)
}
Py_XDECREF(version);

OSVERSIONINFOEXW ver;
ZeroMemory(&ver, sizeof(ver));
ver.dwOSVersionInfoSize = sizeof(ver);
if (!GetVersionExW((OSVERSIONINFOW*) &ver))
return PyErr_SetFromWindowsErr(0);
Expand Down Expand Up @@ -1756,11 +1747,13 @@ sys_getwindowsversion_impl(PyObject *module)
SET_VERSION_INFO(PyLong_FromLong(ver.wSuiteMask));
SET_VERSION_INFO(PyLong_FromLong(ver.wProductType));

#ifdef MS_WINDOWS_DESKTOP
// GetVersion will lie if we are running in a compatibility mode.
// We need to read the version info from a system file resource
// We need to read the version info from kernel with RtlGetVersion
// to accurately identify the OS version. If we fail for any reason,
// just return whatever GetVersion said.
PyObject *realVersion = _sys_getwindowsversion_from_kernel32();
// UWP return correct version from GetVersionExW, this is not necessary.
PyObject *realVersion = _sys_getwindowsversion_from_RtlGetVersion();
if (!realVersion) {
if (!PyErr_ExceptionMatches(PyExc_WindowsError)) {
goto error;
Expand All @@ -1775,6 +1768,7 @@ sys_getwindowsversion_impl(PyObject *module)
}

SET_VERSION_INFO(realVersion);
#endif

#undef SET_VERSION_INFO

Expand Down
Loading