diff --git a/Doc/library/sys.rst b/Doc/library/sys.rst
index 6ee6c490dfd..55b744e19d6 100644
--- a/Doc/library/sys.rst
+++ b/Doc/library/sys.rst
@@ -552,26 +552,15 @@ always available.
Return a named tuple describing the Windows version
currently running. The named elements are *major*, *minor*,
*build*, *platform*, *service_pack*, *service_pack_minor*,
- *service_pack_major*, *suite_mask*, and *product_type*.
- *service_pack* contains a string while all other values are
+ *service_pack_major*, *suite_mask*, *product_type* and
+ *platform_version*. *service_pack* contains a string,
+ *platform_version* a 3-tuple and all other values are
integers. The components can also be accessed by name, so
``sys.getwindowsversion()[0]`` is equivalent to
``sys.getwindowsversion().major``. For compatibility with prior
versions, only the first 5 elements are retrievable by indexing.
- *platform* may be one of the following values:
-
- +-----------------------------------------+-------------------------+
- | Constant | Platform |
- +=========================================+=========================+
- | :const:`0 (VER_PLATFORM_WIN32s)` | Win32s on Windows 3.1 |
- +-----------------------------------------+-------------------------+
- | :const:`1 (VER_PLATFORM_WIN32_WINDOWS)` | Windows 95/98/ME |
- +-----------------------------------------+-------------------------+
- | :const:`2 (VER_PLATFORM_WIN32_NT)` | Windows NT/2000/XP/x64 |
- +-----------------------------------------+-------------------------+
- | :const:`3 (VER_PLATFORM_WIN32_CE)` | Windows CE |
- +-----------------------------------------+-------------------------+
+ *platform* will be :const:`2 (VER_PLATFORM_WIN32_NT)`.
*product_type* may be one of the following values:
@@ -587,17 +576,23 @@ always available.
| | a domain controller. |
+---------------------------------------+---------------------------------+
-
This function wraps the Win32 :c:func:`GetVersionEx` function; see the
Microsoft documentation on :c:func:`OSVERSIONINFOEX` for more information
about these fields.
+ *platform_version* returns the accurate major version, minor version and
+ build number of the current operating system, rather than the version that
+ is being emulated for the process. It is intended for use in logging rather
+ than for feature detection.
+
Availability: Windows.
.. versionchanged:: 3.2
Changed to a named tuple and added *service_pack_minor*,
*service_pack_major*, *suite_mask*, and *product_type*.
+ .. versionchanged:: 3.6
+ Added *platform_version*
.. function:: get_coroutine_wrapper()
diff --git a/Lib/platform.py b/Lib/platform.py
index ee315fa3197..e48ad0b6e7b 100755
--- a/Lib/platform.py
+++ b/Lib/platform.py
@@ -497,65 +497,6 @@ _WIN32_SERVER_RELEASES = {
(6, None): "post2012ServerR2",
}
-def _get_real_winver(maj, min, build):
- if maj < 6 or (maj == 6 and min < 2):
- return maj, min, build
-
- from ctypes import (c_buffer, POINTER, byref, create_unicode_buffer,
- Structure, WinDLL)
- from ctypes.wintypes import DWORD, HANDLE
-
- class VS_FIXEDFILEINFO(Structure):
- _fields_ = [
- ("dwSignature", DWORD),
- ("dwStrucVersion", DWORD),
- ("dwFileVersionMS", DWORD),
- ("dwFileVersionLS", DWORD),
- ("dwProductVersionMS", DWORD),
- ("dwProductVersionLS", DWORD),
- ("dwFileFlagsMask", DWORD),
- ("dwFileFlags", DWORD),
- ("dwFileOS", DWORD),
- ("dwFileType", DWORD),
- ("dwFileSubtype", DWORD),
- ("dwFileDateMS", DWORD),
- ("dwFileDateLS", DWORD),
- ]
-
- kernel32 = WinDLL('kernel32')
- version = WinDLL('version')
-
- # We will immediately double the length up to MAX_PATH, but the
- # path may be longer, so we retry until the returned string is
- # shorter than our buffer.
- name_len = actual_len = 130
- while actual_len == name_len:
- name_len *= 2
- name = create_unicode_buffer(name_len)
- actual_len = kernel32.GetModuleFileNameW(HANDLE(kernel32._handle),
- name, len(name))
- if not actual_len:
- return maj, min, build
-
- size = version.GetFileVersionInfoSizeW(name, None)
- if not size:
- return maj, min, build
-
- ver_block = c_buffer(size)
- if (not version.GetFileVersionInfoW(name, None, size, ver_block) or
- not ver_block):
- return maj, min, build
-
- pvi = POINTER(VS_FIXEDFILEINFO)()
- if not version.VerQueryValueW(ver_block, "", byref(pvi), byref(DWORD())):
- return maj, min, build
-
- maj = pvi.contents.dwProductVersionMS >> 16
- min = pvi.contents.dwProductVersionMS & 0xFFFF
- build = pvi.contents.dwProductVersionLS >> 16
-
- return maj, min, build
-
def win32_ver(release='', version='', csd='', ptype=''):
try:
from sys import getwindowsversion
@@ -567,7 +508,7 @@ def win32_ver(release='', version='', csd='', ptype=''):
from _winreg import OpenKeyEx, QueryValueEx, CloseKey, HKEY_LOCAL_MACHINE
winver = getwindowsversion()
- maj, min, build = _get_real_winver(*winver[:3])
+ maj, min, build = winver.platform_version or winver[:3]
version = '{0}.{1}.{2}'.format(maj, min, build)
release = (_WIN32_CLIENT_RELEASES.get((maj, min)) or
diff --git a/Misc/NEWS b/Misc/NEWS
index b7fa96933df..15a7e0e66ad 100644
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -29,6 +29,8 @@ Core and Builtins
Library
-------
+- Issue #27932: Prevent memory leak in win32_ver().
+
- Fix UnboundLocalError in socket._sendfile_use_sendfile.
- Issue #28075: Check for ERROR_ACCESS_DENIED in Windows implementation of
diff --git a/PCbuild/pythoncore.vcxproj b/PCbuild/pythoncore.vcxproj
index 5d957de7c85..769c6436572 100644
--- a/PCbuild/pythoncore.vcxproj
+++ b/PCbuild/pythoncore.vcxproj
@@ -69,7 +69,7 @@
_USRDLL;Py_BUILD_CORE;Py_ENABLE_SHARED;MS_DLL_ID="$(SysWinVer)";%(PreprocessorDefinitions)
- shlwapi.lib;ws2_32.lib;%(AdditionalDependencies)
+ version.lib;shlwapi.lib;ws2_32.lib;%(AdditionalDependencies)0x1e000000
diff --git a/Python/sysmodule.c b/Python/sysmodule.c
index b7afe93ca5f..5dd0d108343 100644
--- a/Python/sysmodule.c
+++ b/Python/sysmodule.c
@@ -894,10 +894,11 @@ Return information about the running version of Windows as a named tuple.\n\
The members are named: major, minor, build, platform, service_pack,\n\
service_pack_major, service_pack_minor, suite_mask, and product_type. For\n\
backward compatibility, only the first 5 items are available by indexing.\n\
-All elements are numbers, except service_pack which is a string. Platform\n\
-may be 0 for win32s, 1 for Windows 9x/ME, 2 for Windows NT/2000/XP/Vista/7,\n\
-3 for Windows CE. Product_type may be 1 for a workstation, 2 for a domain\n\
-controller, 3 for a server."
+All elements are numbers, except service_pack and platform_type which are\n\
+strings, and platform_version which is a 3-tuple. Platform is always 2.\n\
+Product_type may be 1 for a workstation, 2 for a domain controller, 3 for a\n\
+server. Platform_version is a 3-tuple containing a version number that is\n\
+intended for identifying the OS rather than feature detection."
);
static PyTypeObject WindowsVersionType = {0, 0, 0, 0, 0, 0};
@@ -912,6 +913,7 @@ static PyStructSequence_Field windows_version_fields[] = {
{"service_pack_minor", "Service Pack minor version number"},
{"suite_mask", "Bit mask identifying available product suites"},
{"product_type", "System product type"},
+ {"platform_version", "Diagnostic version number"},
{0}
};
@@ -936,6 +938,12 @@ sys_getwindowsversion(PyObject *self)
PyObject *version;
int pos = 0;
OSVERSIONINFOEX ver;
+ DWORD realMajor, realMinor, realBuild;
+ HANDLE hKernel32;
+ wchar_t kernel32_path[MAX_PATH];
+ LPVOID verblock;
+ DWORD verblock_size;
+
ver.dwOSVersionInfoSize = sizeof(ver);
if (!GetVersionEx((OSVERSIONINFO*) &ver))
return PyErr_SetFromWindowsErr(0);
@@ -954,10 +962,40 @@ sys_getwindowsversion(PyObject *self)
PyStructSequence_SET_ITEM(version, pos++, PyLong_FromLong(ver.wSuiteMask));
PyStructSequence_SET_ITEM(version, pos++, PyLong_FromLong(ver.wProductType));
+ realMajor = ver.dwMajorVersion;
+ realMinor = ver.dwMinorVersion;
+ realBuild = ver.dwBuildNumber;
+
+ // GetVersion will lie if we are running in a compatibility mode.
+ // We need to read the version info from a system file resource
+ // to accurately identify the OS version. If we fail for any reason,
+ // just return whatever GetVersion said.
+ hKernel32 = GetModuleHandleW(L"kernel32.dll");
+ if (hKernel32 && GetModuleFileNameW(hKernel32, kernel32_path, MAX_PATH) &&
+ (verblock_size = GetFileVersionInfoSizeW(kernel32_path, NULL)) &&
+ (verblock = PyMem_RawMalloc(verblock_size))) {
+ VS_FIXEDFILEINFO *ffi;
+ UINT ffi_len;
+
+ if (GetFileVersionInfoW(kernel32_path, 0, verblock_size, verblock) &&
+ VerQueryValueW(verblock, L"", (LPVOID)&ffi, &ffi_len)) {
+ realMajor = HIWORD(ffi->dwProductVersionMS);
+ realMinor = LOWORD(ffi->dwProductVersionMS);
+ realBuild = HIWORD(ffi->dwProductVersionLS);
+ }
+ PyMem_RawFree(verblock);
+ }
+ PyStructSequence_SET_ITEM(version, pos++, PyTuple_Pack(3,
+ PyLong_FromLong(realMajor),
+ PyLong_FromLong(realMinor),
+ PyLong_FromLong(realBuild)
+ ));
+
if (PyErr_Occurred()) {
Py_DECREF(version);
return NULL;
}
+
return version;
}