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):

`