110110
111111"""
112112
113- __version__ = '1.0.9 '
113+ __version__ = '1.0.10 '
114114
115115import collections
116116import os
@@ -384,6 +384,49 @@ def win32_edition():
384384
385385 return None
386386
387+ def _format_sp_and_product_type (spmajor , spminor , product_type ):
388+ csd = f"SP{ spmajor } .{ spminor } " if spminor else f"SP{ spmajor } "
389+ is_client = (int (product_type ) == 1 )
390+ return csd , is_client
391+
392+ def _get_version_using_rtlgetversion ():
393+ import ctypes
394+ class OSVERSIONINFOEXW (ctypes .Structure ):
395+ _fields_ = [
396+ ("dwOSVersionInfoSize" , ctypes .c_uint32 ),
397+ ("dwMajorVersion" , ctypes .c_uint32 ),
398+ ("dwMinorVersion" , ctypes .c_uint32 ),
399+ ("dwBuildNumber" , ctypes .c_uint32 ),
400+ ("dwPlatformId" , ctypes .c_uint32 ),
401+ ("szCSDVersion" , ctypes .c_wchar * 128 ), # Service Pack name
402+ ("wServicePackMajor" , ctypes .c_uint16 ), # Major Service Pack version
403+ ("wServicePackMinor" , ctypes .c_uint16 ), # Minor Service Pack version
404+ ("wSuiteMask" , ctypes .c_uint16 ),
405+ ("wProductType" , ctypes .c_uint8 ), # Type of product (Workstation, etc.)
406+ ("wReserved" , ctypes .c_uint8 ) # Reserved
407+ ]
408+
409+ # Initialize OSVERSIONINFOEXW structure
410+ os_version = OSVERSIONINFOEXW ()
411+ os_version .dwOSVersionInfoSize = ctypes .sizeof (OSVERSIONINFOEXW )
412+
413+ # Load ntdll.dll and call RtlGetVersion
414+ ntdll = ctypes .WinDLL ("ntdll" )
415+ rtl_get_version = ntdll .RtlGetVersion
416+ rtl_get_version (ctypes .byref (os_version ))
417+
418+ # Extract the version details
419+ major = os_version .dwMajorVersion
420+ minor = os_version .dwMinorVersion
421+ build = os_version .dwBuildNumber
422+ spmajor = os_version .wServicePackMajor
423+ spminor = os_version .wServicePackMinor
424+ product_type = os_version .wProductType
425+
426+ version = f"{ major } .{ minor } .{ build } "
427+ csd , is_client = _format_sp_and_product_type (spmajor , spminor , product_type )
428+ return version , csd , is_client
429+
387430def _win32_ver (version , csd , ptype ):
388431 # Try using WMI first, as this is the canonical source of data
389432 try :
@@ -395,40 +438,39 @@ def _win32_ver(version, csd, ptype):
395438 'ServicePackMajorVersion' ,
396439 'ServicePackMinorVersion' ,
397440 )
398- is_client = (int (product_type ) == 1 )
399- if spminor and spminor != '0' :
400- csd = f'SP{ spmajor } .{ spminor } '
401- else :
402- csd = f'SP{ spmajor } '
441+ csd , is_client = _format_sp_and_product_type (spmajor , spminor , product_type )
403442 return version , csd , ptype , is_client
404443 except OSError :
405444 pass
406445
407- # Fall back to a combination of sys.getwindowsversion and "ver"
408- try :
409- from sys import getwindowsversion
410- except ImportError :
411- return version , csd , ptype , True
412-
413- winver = getwindowsversion ()
414- is_client = (getattr (winver , 'product_type' , 1 ) == 1 )
446+ # Fall back to RtlGetVersion using ntdll
415447 try :
416- version = _syscmd_ver ()[2 ]
417- major , minor , build = map (int , version .split ('.' ))
418- except ValueError :
419- major , minor , build = winver .platform_version or winver [:3 ]
420- version = '{0}.{1}.{2}' .format (major , minor , build )
421-
422- # getwindowsversion() reflect the compatibility mode Python is
423- # running under, and so the service pack value is only going to be
424- # valid if the versions match.
425- if winver [:2 ] == (major , minor ):
448+ version , csd , is_client = _get_version_using_rtlgetversion ()
449+ except Exception :
450+ # Fall back to a combination of sys.getwindowsversion and "ver"
426451 try :
427- csd = 'SP{}' .format (winver .service_pack_major )
428- except AttributeError :
429- if csd [:13 ] == 'Service Pack ' :
430- csd = 'SP' + csd [13 :]
452+ from sys import getwindowsversion
453+ except ImportError :
454+ return version , csd , ptype , True
431455
456+ winver = getwindowsversion ()
457+ is_client = (getattr (winver , 'product_type' , 1 ) == 1 )
458+ try :
459+ version = _syscmd_ver ()[2 ]
460+ major , minor , build = map (int , version .split ('.' ))
461+ except ValueError :
462+ major , minor , build = winver .platform_version or winver [:3 ]
463+ version = '{0}.{1}.{2}' .format (major , minor , build )
464+
465+ # getwindowsversion() reflect the compatibility mode Python is
466+ # running under, and so the service pack value is only going to be
467+ # valid if the versions match.
468+ if winver [:2 ] == (major , minor ):
469+ try :
470+ csd = 'SP{}' .format (winver .service_pack_major )
471+ except AttributeError :
472+ if csd [:13 ] == 'Service Pack ' :
473+ csd = 'SP' + csd [13 :]
432474 try :
433475 import winreg
434476 except ImportError :
0 commit comments