Issue #19143: platform module now reads Windows version from kernel32… · python/cpython@b9f4fea (original) (raw)
`@@ -26,12 +26,14 @@
`
26
26
`# Betancourt, Randall Hopper, Karl Putland, John Farrell, Greg
`
27
27
`# Andruk, Just van Rossum, Thomas Heller, Mark R. Levinson, Mark
`
28
28
`# Hammond, Bill Tutt, Hans Nowak, Uwe Zessin (OpenVMS support),
`
29
``
`-
Colin Kong, Trent Mick, Guido van Rossum, Anthony Baxter
`
``
29
`+
Colin Kong, Trent Mick, Guido van Rossum, Anthony Baxter, Steve
`
``
30
`+
Dower
`
30
31
`#
`
31
32
`# History:
`
32
33
`#
`
33
34
`#
`
34
35
`#
`
``
36
`+
1.0.8 - changed Windows support to read version from kernel32.dll
`
35
37
`# 1.0.7 - added DEV_NULL
`
36
38
`# 1.0.6 - added linux_distribution()
`
37
39
`# 1.0.5 - fixed Java support to allow running the module on Jython
`
`@@ -469,189 +471,139 @@ def _syscmd_ver(system='', release='', version='',
`
469
471
`version = _norm_version(version)
`
470
472
`return system, release, version
`
471
473
``
472
``
`-
def _win32_getvalue(key, name, default=''):
`
``
474
`+
_WIN32_CLIENT_RELEASES = {
`
``
475
`+
(5, 0): "2000",
`
``
476
`+
(5, 1): "XP",
`
``
477
`+
Strictly, 5.2 client is XP 64-bit, but platform.py historically
`
``
478
`+
has always called it 2003 Server
`
``
479
`+
(5, 2): "2003Server",
`
``
480
`+
(5, None): "post2003",
`
``
481
+
``
482
`+
(6, 0): "Vista",
`
``
483
`+
(6, 1): "7",
`
``
484
`+
(6, 2): "8",
`
``
485
`+
(6, 3): "8.1",
`
``
486
`+
(6, None): "post8.1",
`
``
487
+
``
488
`+
(10, 0): "10",
`
``
489
`+
(10, None): "post10",
`
``
490
`+
}
`
473
491
``
474
``
`-
""" Read a value for name from the registry key.
`
``
492
`+
Server release name lookup will default to client names if necessary
`
``
493
`+
_WIN32_SERVER_RELEASES = {
`
``
494
`+
(5, 2): "2003Server",
`
475
495
``
476
``
`-
In case this fails, default is returned.
`
``
496
`+
(6, 0): "2008Server",
`
``
497
`+
(6, 1): "2008ServerR2",
`
``
498
`+
(6, 2): "2012Server",
`
``
499
`+
(6, 3): "2012ServerR2",
`
``
500
`+
(6, None): "post2012ServerR2",
`
``
501
`+
}
`
477
502
``
478
``
`-
"""
`
479
``
`-
try:
`
480
``
`-
Use win32api if available
`
481
``
`-
from win32api import RegQueryValueEx
`
482
``
`-
except ImportError:
`
483
``
`-
On Python 2.0 and later, emulate using winreg
`
484
``
`-
import winreg
`
485
``
`-
RegQueryValueEx = winreg.QueryValueEx
`
486
``
`-
try:
`
487
``
`-
return RegQueryValueEx(key, name)
`
488
``
`-
except:
`
489
``
`-
return default
`
``
503
`+
def _get_real_winver(maj, min, build):
`
``
504
`+
if maj < 6 or (maj == 6 and min < 2):
`
``
505
`+
return maj, min, build
`
``
506
+
``
507
`+
from ctypes import (c_buffer, POINTER, byref, create_unicode_buffer,
`
``
508
`+
Structure, WinDLL)
`
``
509
`+
from ctypes.wintypes import DWORD, HANDLE
`
``
510
+
``
511
`+
class VS_FIXEDFILEINFO(Structure):
`
``
512
`+
fields = [
`
``
513
`+
("dwSignature", DWORD),
`
``
514
`+
("dwStrucVersion", DWORD),
`
``
515
`+
("dwFileVersionMS", DWORD),
`
``
516
`+
("dwFileVersionLS", DWORD),
`
``
517
`+
("dwProductVersionMS", DWORD),
`
``
518
`+
("dwProductVersionLS", DWORD),
`
``
519
`+
("dwFileFlagsMask", DWORD),
`
``
520
`+
("dwFileFlags", DWORD),
`
``
521
`+
("dwFileOS", DWORD),
`
``
522
`+
("dwFileType", DWORD),
`
``
523
`+
("dwFileSubtype", DWORD),
`
``
524
`+
("dwFileDateMS", DWORD),
`
``
525
`+
("dwFileDateLS", DWORD),
`
``
526
`+
]
`
``
527
+
``
528
`+
kernel32 = WinDLL('kernel32')
`
``
529
`+
version = WinDLL('version')
`
``
530
+
``
531
`+
We will immediately double the length up to MAX_PATH, but the
`
``
532
`+
path may be longer, so we retry until the returned string is
`
``
533
`+
shorter than our buffer.
`
``
534
`+
name_len = actual_len = 130
`
``
535
`+
while actual_len == name_len:
`
``
536
`+
name_len *= 2
`
``
537
`+
name = create_unicode_buffer(name_len)
`
``
538
`+
actual_len = kernel32.GetModuleFileNameW(HANDLE(kernel32._handle),
`
``
539
`+
name, len(name))
`
``
540
`+
if not actual_len:
`
``
541
`+
return maj, min, build
`
``
542
+
``
543
`+
size = version.GetFileVersionInfoSizeW(name, None)
`
``
544
`+
if not size:
`
``
545
`+
return maj, min, build
`
``
546
+
``
547
`+
ver_block = c_buffer(size)
`
``
548
`+
if (not version.GetFileVersionInfoW(name, None, size, ver_block) or
`
``
549
`+
not ver_block):
`
``
550
`+
return maj, min, build
`
``
551
+
``
552
`+
pvi = POINTER(VS_FIXEDFILEINFO)()
`
``
553
`+
if not version.VerQueryValueW(ver_block, "", byref(pvi), byref(DWORD())):
`
``
554
`+
return maj, min, build
`
``
555
+
``
556
`+
maj = pvi.contents.dwProductVersionMS >> 16
`
``
557
`+
min = pvi.contents.dwProductVersionMS & 0xFFFF
`
``
558
`+
build = pvi.contents.dwProductVersionLS >> 16
`
``
559
+
``
560
`+
return maj, min, build
`
490
561
``
491
562
`def win32_ver(release='', version='', csd='', ptype=''):
`
``
563
`+
from sys import getwindowsversion
`
``
564
`+
try:
`
``
565
`+
from winreg import OpenKeyEx, QueryValueEx, CloseKey, HKEY_LOCAL_MACHINE
`
``
566
`+
except ImportError:
`
``
567
`+
from _winreg import OpenKeyEx, QueryValueEx, CloseKey, HKEY_LOCAL_MACHINE
`
492
568
``
493
``
`-
""" Get additional version information from the Windows Registry
`
494
``
`-
and return a tuple (version, csd, ptype) referring to version
`
495
``
`-
number, CSD level (service pack), and OS type (multi/single
`
496
``
`-
processor).
`
497
``
-
498
``
`-
As a hint: ptype returns 'Uniprocessor Free' on single
`
499
``
`-
processor NT machines and 'Multiprocessor Free' on multi
`
500
``
`-
processor machines. The 'Free' refers to the OS version being
`
501
``
`-
free of debugging code. It could also state 'Checked' which
`
502
``
`-
means the OS version uses debugging code, i.e. code that
`
503
``
`-
checks arguments, ranges, etc. (Thomas Heller).
`
``
569
`+
winver = getwindowsversion()
`
``
570
`+
maj, min, build = _get_real_winver(*winver[:3])
`
``
571
`+
version = '{0}.{1}.{2}'.format(maj, min, build)
`
504
572
``
505
``
`-
Note: this function works best with Mark Hammond's win32
`
506
``
`-
package installed, but also on Python 2.3 and later. It
`
507
``
`-
obviously only runs on Win32 compatible platforms.
`
``
573
`+
release = (_WIN32_CLIENT_RELEASES.get((maj, min)) or
`
``
574
`+
_WIN32_CLIENT_RELEASES.get((maj, None)) or
`
``
575
`+
release)
`
508
576
``
509
``
`-
"""
`
510
``
`-
XXX Is there any way to find out the processor type on WinXX ?
`
511
``
`-
XXX Is win32 available on Windows CE ?
`
512
``
`-
`
513
``
`-
Adapted from code posted by Karl Putland to comp.lang.python.
`
514
``
`-
`
515
``
`-
The mappings between reg. values and release names can be found
`
516
``
`-
here: http://msdn.microsoft.com/library/en-us/sysinfo/base/osversioninfo_str.asp
`
517
``
-
518
``
`-
Import the needed APIs
`
519
``
`-
try:
`
520
``
`-
from win32api import RegQueryValueEx, RegOpenKeyEx, \
`
521
``
`-
RegCloseKey, GetVersionEx
`
522
``
`-
from win32con import HKEY_LOCAL_MACHINE, VER_PLATFORM_WIN32_NT, \
`
523
``
`-
VER_PLATFORM_WIN32_WINDOWS, VER_NT_WORKSTATION
`
524
``
`-
except ImportError:
`
525
``
`-
Emulate the win32api module using Python APIs
`
``
577
`+
getwindowsversion() reflect the compatibility mode Python is
`
``
578
`+
running under, and so the service pack value is only going to be
`
``
579
`+
valid if the versions match.
`
``
580
`+
if winver[:2] == (maj, min):
`
526
581
`try:
`
527
``
`-
sys.getwindowsversion
`
``
582
`+
csd = 'SP{}'.format(winver.service_pack_major)
`
528
583
`except AttributeError:
`
529
``
`-
No emulation possible, so return the defaults...
`
530
``
`-
return release, version, csd, ptype
`
531
``
`-
else:
`
532
``
`-
Emulation using winreg (added in Python 2.0) and
`
533
``
`-
sys.getwindowsversion() (added in Python 2.3)
`
534
``
`-
import winreg
`
535
``
`-
GetVersionEx = sys.getwindowsversion
`
536
``
`-
RegQueryValueEx = winreg.QueryValueEx
`
537
``
`-
RegOpenKeyEx = winreg.OpenKeyEx
`
538
``
`-
RegCloseKey = winreg.CloseKey
`
539
``
`-
HKEY_LOCAL_MACHINE = winreg.HKEY_LOCAL_MACHINE
`
540
``
`-
VER_PLATFORM_WIN32_WINDOWS = 1
`
541
``
`-
VER_PLATFORM_WIN32_NT = 2
`
542
``
`-
VER_NT_WORKSTATION = 1
`
543
``
`-
VER_NT_SERVER = 3
`
544
``
`-
REG_SZ = 1
`
545
``
-
546
``
`-
Find out the registry key and some general version infos
`
547
``
`-
winver = GetVersionEx()
`
548
``
`-
maj, min, buildno, plat, csd = winver
`
549
``
`-
version = '%i.%i.%i' % (maj, min, buildno & 0xFFFF)
`
550
``
`-
if hasattr(winver, "service_pack"):
`
551
``
`-
if winver.service_pack != "":
`
552
``
`-
csd = 'SP%s' % winver.service_pack_major
`
553
``
`-
else:
`
554
``
`-
if csd[:13] == 'Service Pack ':
`
555
``
`-
csd = 'SP' + csd[13:]
`
556
``
-
557
``
`-
if plat == VER_PLATFORM_WIN32_WINDOWS:
`
558
``
`-
regkey = 'SOFTWARE\Microsoft\Windows\CurrentVersion'
`
559
``
`-
Try to guess the release name
`
560
``
`-
if maj == 4:
`
561
``
`-
if min == 0:
`
562
``
`-
release = '95'
`
563
``
`-
elif min == 10:
`
564
``
`-
release = '98'
`
565
``
`-
elif min == 90:
`
566
``
`-
release = 'Me'
`
567
``
`-
else:
`
568
``
`-
release = 'postMe'
`
569
``
`-
elif maj == 5:
`
570
``
`-
release = '2000'
`
571
``
-
572
``
`-
elif plat == VER_PLATFORM_WIN32_NT:
`
573
``
`-
regkey = 'SOFTWARE\Microsoft\Windows NT\CurrentVersion'
`
574
``
`-
if maj <= 4:
`
575
``
`-
release = 'NT'
`
576
``
`-
elif maj == 5:
`
577
``
`-
if min == 0:
`
578
``
`-
release = '2000'
`
579
``
`-
elif min == 1:
`
580
``
`-
release = 'XP'
`
581
``
`-
elif min == 2:
`
582
``
`-
release = '2003Server'
`
583
``
`-
else:
`
584
``
`-
release = 'post2003'
`
585
``
`-
elif maj == 6:
`
586
``
`-
if hasattr(winver, "product_type"):
`
587
``
`-
product_type = winver.product_type
`
588
``
`-
else:
`
589
``
`-
product_type = VER_NT_WORKSTATION
`
590
``
`-
Without an OSVERSIONINFOEX capable sys.getwindowsversion(),
`
591
``
`-
or help from the registry, we cannot properly identify
`
592
``
`-
non-workstation versions.
`
593
``
`-
try:
`
594
``
`-
key = RegOpenKeyEx(HKEY_LOCAL_MACHINE, regkey)
`
595
``
`-
name, type = RegQueryValueEx(key, "ProductName")
`
596
``
`-
Discard any type that isn't REG_SZ
`
597
``
`-
if type == REG_SZ and name.find("Server") != -1:
`
598
``
`-
product_type = VER_NT_SERVER
`
599
``
`-
except OSError:
`
600
``
`-
Use default of VER_NT_WORKSTATION
`
601
``
`-
pass
`
602
``
-
603
``
`-
if min == 0:
`
604
``
`-
if product_type == VER_NT_WORKSTATION:
`
605
``
`-
release = 'Vista'
`
606
``
`-
else:
`
607
``
`-
release = '2008Server'
`
608
``
`-
elif min == 1:
`
609
``
`-
if product_type == VER_NT_WORKSTATION:
`
610
``
`-
release = '7'
`
611
``
`-
else:
`
612
``
`-
release = '2008ServerR2'
`
613
``
`-
elif min == 2:
`
614
``
`-
if product_type == VER_NT_WORKSTATION:
`
615
``
`-
release = '8'
`
616
``
`-
else:
`
617
``
`-
release = '2012Server'
`
618
``
`-
else:
`
619
``
`-
release = 'post2012Server'
`
``
584
`+
if csd[:13] == 'Service Pack ':
`
``
585
`+
csd = 'SP' + csd[13:]
`
620
586
``
621
``
`-
else:
`
622
``
`-
if not release:
`
623
``
`-
E.g. Win3.1 with win32s
`
624
``
`-
release = '%i.%i' % (maj, min)
`
625
``
`-
return release, version, csd, ptype
`
``
587
`+
VER_NT_SERVER = 3
`
``
588
`+
if getattr(winver, 'product_type', None) == 3:
`
``
589
`+
release = (_WIN32_SERVER_RELEASES.get((maj, min)) or
`
``
590
`+
_WIN32_SERVER_RELEASES.get((maj, None)) or
`
``
591
`+
release)
`
626
592
``
627
``
`-
Open the registry key
`
``
593
`+
key = None
`
628
594
`try:
`
629
``
`-
keyCurVer = RegOpenKeyEx(HKEY_LOCAL_MACHINE, regkey)
`
630
``
`-
Get a value to make sure the key exists...
`
631
``
`-
RegQueryValueEx(keyCurVer, 'SystemRoot')
`
``
595
`+
key = OpenKeyEx(HKEY_LOCAL_MACHINE,
`
``
596
`+
r'SOFTWARE\Microsoft\Windows NT\CurrentVersion')
`
``
597
`+
ptype = QueryValueEx(key, 'CurrentType')[0]
`
632
598
`except:
`
633
``
`-
return release, version, csd, ptype
`
634
``
-
635
``
`-
Parse values
`
636
``
`-
#subversion = _win32_getvalue(keyCurVer,
`
637
``
`-
'SubVersionNumber',
`
638
``
`-
('',1))[0]
`
639
``
`-
#if subversion:
`
640
``
`-
release = release + subversion # 95a, 95b, etc.
`
641
``
`-
build = _win32_getvalue(keyCurVer,
`
642
``
`-
'CurrentBuildNumber',
`
643
``
`-
('', 1))[0]
`
644
``
`-
ptype = _win32_getvalue(keyCurVer,
`
645
``
`-
'CurrentType',
`
646
``
`-
(ptype, 1))[0]
`
647
``
-
648
``
`-
Normalize version
`
649
``
`-
version = _norm_version(version, build)
`
650
``
-
651
``
`-
Close key
`
652
``
`-
RegCloseKey(keyCurVer)
`
``
599
`+
pass
`
``
600
`+
finally:
`
``
601
`+
if key:
`
``
602
`+
CloseKey(key)
`
``
603
+
653
604
`return release, version, csd, ptype
`
654
605
``
``
606
+
655
607
`def _mac_ver_xml():
`
656
608
`fn = '/System/Library/CoreServices/SystemVersion.plist'
`
657
609
`if not os.path.exists(fn):
`