Source code for platformids.dist.windows

# -*- coding: utf-8 -*-
"""MS-Windows releases for the *WindowsNT* family based on the registry information.
This includes the IoT products, e.g. *Windows-NT-10.0-IoT-core* on the *RaspberryPi*.
"""
from __future__ import absolute_import
from __future__ import print_function

import os
import re

from platformids import _debug, _verbose
from platformids import RTE_NT, RTE_WIN, RTE_WINDOWS, \
    rte2num, num2rte, num2pretty, \
    decode_version_str_to_segments, \
    PlatformIDsError, PlatformIDsFileCheck

__author__ = 'Arno-Can Uestuensoez'
__license__ = "Artistic-License-2.0 + Forced-Fairplay-Constraints"
__copyright__ = "Copyright (C) 2010-2018 Arno-Can Uestuensoez" \
                " @Ingenieurbuero Arno-Can Uestuensoez"
__version__ = '0.1.29'
__uuid__ = "7add5ded-c39b-4b6e-8c87-1b3a1c150ee9"

__docformat__ = "restructuredtext en"

if __debug__:

    # this is for unittests only
    class PlatformIDsUnittestTriggered(PlatformIDsError):
        pass

#
# The handled versions are of the "nt' technology, historic pre-versions and others
# are not supported.
#
# set: win32
# currently no sub-categories, some are contained in
# the 'set: generic': drives, shares
#
#
# RTE_DOS = RTE_WIN32 + 1  #: MS-DOS - frozen

#
# dist - the base versions of major and minor numbers
#
RTE_NT35 = RTE_NT + 0x001d0000  # : Windows NT-3.5
RTE_NT40 = RTE_NT + 0x00200000  # : Windows NT-4.0
RTE_NT50 = RTE_NT + 0x00280000  # : Windows NT-5.0 - W2000
RTE_NT51 = RTE_NT + 0x00290000  # : Windows NT-5.1 - WXP - 32bit
RTE_NT52 = RTE_NT + 0x002a0000  # : Windows NT-5.2 - W2003 / 2003 / 2003R2
RTE_NT60 = RTE_NT + 0x00300000  # : Windows NT-6.0 - vista / 2008
RTE_NT61 = RTE_NT + 0x00310000  # : Windows NT-6.1 - win7 / 2008R2
RTE_NT62 = RTE_NT + 0x00320000  # : Windows NT-6.2 - win8 / 2012
RTE_NT63 = RTE_NT + 0x00330000  # : Windows NT-6.3 - win8.1 / 2012R2
RTE_NT100 = RTE_NT + 0x00500000  # : Windows NT-10.0 - win10 / 2016

#
# distrel - some predefined constant values of the complete versions
#
# the values could be easily calculated dynamically by
#  old-scheme: adding the build number
#  new-scheme: adding the date based product version
#
# frozen history
#
RTE_WINNT35 = RTE_NT35 + 807  # : WindowsNT-4.0 Workstation - 32bit
RTE_WINNT40 = RTE_NT40 + 1381  # : WindowsNT-4.0 Workstation - 32bit
RTE_WIN2000 = RTE_NT50 + 2195  # : Windows2000

RTE_WINXP = RTE_NT51 + 2600  # : WindowsXP - 32bit

RTE_WINXP64 = RTE_NT52 + 3790  # : WindowsXP - 64bit
RTE_WIN2003 = RTE_NT52 + 3790  # : Windows2003
RTE_WIN2003R2 = RTE_NT52 + 3790  # : Windows2003R2 - exception: same build as 2003

RTE_WIN2008 = RTE_NT60 + 6001  # : Windows2008 RTM
RTE_WIN2008SP2 = RTE_NT60 + 6002  # : Windows2008 SP1

RTE_WIN7 = RTE_NT61 + 7600  # : Windows7
RTE_WIN2008R2 = RTE_NT61 + 7600  # : Windows2008R2

RTE_WIN7SP1 = RTE_NT61 + 7601  # : Windows7 SP1
RTE_WIN2008R2SP1 = RTE_NT61 + 7601  # : Windows2008R2 SP1

RTE_WIN8 = RTE_NT62 + 9200  # : Windows8
RTE_WIN2012 = RTE_NT62 + 9200  # : Windows2012

RTE_WIN81 = RTE_NT63 + 9600  # : Windows81
RTE_WIN2012R2 = RTE_NT63 + 9600  # : Windows2012R2

#
# for generic NT-10.0 releases
# simply add the numeric version to the base value RTE_NT100
#
# RTE_WINNT100_1703 =  RTE_NT100   + 1703           #: NT-10.0 - 2017.03
# RTE_WINNT100_1803 =  RTE_NT100   + 1803           #: NT-10.0 - 2018.03
# RTE_WINNT100_1809 =  RTE_NT100   + 1809           #: NT-10.0 - 2018.09

# : mapping of the rte string and numeric representation to the numeric value
rte2num.update(
    {
        '7 Server': RTE_WIN2008R2,
        '7': RTE_WIN7,
        '8 Server': RTE_WIN2012,
        '8': RTE_WIN8,
        'Blackcomb': RTE_WIN7,
        'Blue Server': RTE_WIN2012R2,
        'Blue': RTE_WIN81,
        'Longhorn Server' : RTE_NT61 + 6002,  # W2008 SP2
        'Threshold 1': RTE_NT100 + 1507,
        'Threshold 2': RTE_NT100 + 1511,
        'Redstone 1': RTE_NT100 + 1607,
        'Redstone 2': RTE_NT100 + 1703,
        'Redstone 3': RTE_NT100 + 1709,
        'Redstone 4': RTE_NT100 + 1803,
        'Redstone 5': RTE_NT100 + 1809,
        'Vienna': RTE_WIN7,
        'winxp': RTE_WINXP,
        'win2000': RTE_WIN2000,
        'winnt40': RTE_WINNT40,
        'win10': RTE_NT100,
        'win7': RTE_WIN7,
        'win8': RTE_WIN8,
        'win81': RTE_WIN81,
        'win2008': RTE_WIN2008,
        'win2008r2': RTE_WIN2008R2,
        'win2012': RTE_WIN2012,
        'win2012r2': RTE_WIN2012R2,
        'win2016': RTE_NT100,
        'win2019': RTE_NT100,
        'win2019se': RTE_NT100,
        RTE_WIN2000: RTE_WIN2000,
        RTE_WIN2008: RTE_WIN2008,
        RTE_WIN2008R2: RTE_WIN2008R2,
        RTE_WIN2012: RTE_WIN2012,
        RTE_WIN2012R2: RTE_WIN2012R2,
        RTE_WIN7: RTE_WIN7,
        RTE_WIN81: RTE_WIN81,
        RTE_WIN8: RTE_WIN8,
        RTE_WINNT40: RTE_WINNT40,
        RTE_WINXP: RTE_WINXP,

        'nt40': RTE_NT40,

        'nt50': RTE_NT50,
        'nt51': RTE_NT51,
        'nt52': RTE_NT52,

        'nt60': RTE_NT60,
        'nt61': RTE_NT61,
        'nt62': RTE_NT62,
        'nt63': RTE_NT63,
        'nt100': RTE_NT100,

        RTE_NT40: RTE_NT40,

        RTE_NT50: RTE_NT50,
        RTE_NT51: RTE_NT51,
        RTE_NT52: RTE_NT52,

        RTE_NT60: RTE_NT60,
        RTE_NT61: RTE_NT61,
        RTE_NT62: RTE_NT62,
        RTE_NT63: RTE_NT63,
        RTE_NT100: RTE_NT100,

    }
)

# : mapping of the rte numeric representation to the string value
num2rte.update(
    {
#         RTE_WINNT40: 'winnt40',
#         RTE_WIN2000: 'win2000',
#         RTE_WINXP: 'winxp',
#         RTE_WIN7: 'win7',
#         RTE_WIN81: 'win81',
#         RTE_WIN8: 'win8',
#         RTE_WIN2008: 'win2008',
#         RTE_WIN2008R2: 'win2008r2',  # the release is actually a different NT
#         RTE_WIN2012: 'win2012',
#         RTE_WIN2012R2: 'win2012r2',

        RTE_WINNT40: 'nt401381',
        RTE_WIN2000: 'nt502195',
        RTE_WINXP: 'nt512600',
        RTE_WIN7: 'nt617601',
        RTE_WIN81: 'nt639600',
        RTE_WIN8: 'nt629200',
        RTE_WIN2008: 'nt606002',
        RTE_WIN2008R2: 'nt617601',  # the release is actually a different NT
        RTE_WIN2012: 'nt629200',
        RTE_WIN2012R2: 'nt639600',

        RTE_NT40: 'nt40',
        RTE_NT50: 'nt50',
        RTE_NT51: 'nt51',
        RTE_NT52: 'nt52',
        RTE_NT60: 'nt60',
        RTE_NT61: 'nt61',
        RTE_NT62: 'nt62',
        RTE_NT63: 'nt63',
        RTE_NT100: 'nt100',

    }
)

# : mapping of the rte numeric representation to the pretty string value
num2pretty.update(
    {
        RTE_WIN: 'Windows',
        RTE_WINDOWS: 'Windows',

        RTE_WIN2000: 'Windows 2000',
        RTE_WIN2008: 'Windows Server 2008',
        RTE_WIN2008R2: 'Windows Server 2008R2',
        RTE_WIN2012: 'Windows Server 2012',
        RTE_WIN2012R2: 'Windows Server 2012R2',
        RTE_WIN7: 'Windows 7',
        RTE_WIN81: 'Windows 8.1',
        RTE_WIN8: 'Windows 8',
        RTE_WINNT40: 'WindowsNT-4.0',
        RTE_WINXP: 'Windows XP',

        RTE_NT40: 'NT-4.0',

        RTE_NT50: 'NT-5.0',
        RTE_NT51: 'NT-5.1',
        RTE_NT52: 'NT-5.2',

        RTE_NT60: 'NT-6.0',
        RTE_NT61: 'NT-6.1',
        RTE_NT62: 'NT-6.2',
        RTE_NT63: 'NT-6.3',
        RTE_NT100: 'NT-10.0',

    }
)

versions = {
    RTE_WINNT40: '4.0.1381',
    RTE_WIN2000: '5.0.2195',
    RTE_WINXP: '5.1.2600',
    RTE_WIN7: '6.1.7601',
    RTE_WIN81: '6.3.9600',
    RTE_WIN8: '6.2.9200',

    RTE_WIN2008: '6.0.6002',
    RTE_WIN2008R2: '6.1.7601',
    RTE_WIN2012: '6.2.9200',
    RTE_WIN2012R2: '6.3.9600',

    RTE_NT40: '4.0',

    RTE_NT50: '5.0',
    RTE_NT51: '5.1',
    RTE_NT52: '5.2',

    RTE_NT60: '6.0',
    RTE_NT61: '6.1',
    RTE_NT62: '6.2',
    RTE_NT63: '6.3',
    RTE_NT100: '10.0',

}

#
# Microsoft: OSVERSIONINFOEXA.wProductType
#
VER_NT_GENERIC = 0x0000000  # : non standard enum

VER_NT_WORKSTATION = 0x0000001  # : see wProductType
VER_NT_DOMAIN_CONTROLLER = 0x0000002  # : see wProductType
VER_NT_SERVER = 0x0000003  # : see wProductType

VER_NT_IOT = 0x0000004  # : non standard enum


[docs]class WinVersion(object):
[docs] def __init__(self, *args, **kargs): self.ntmajor = self.ntminor = self.release = self.build = 0 self.editionid = self.installationtype = self.sourcepath = self.productname = '' self.productcategory = VER_NT_GENERIC # 0x0000000 #: non standard enum self.sp = self.ubr = None self.raw = {} self.fetch_current_version_raw() pass # 4debugging
[docs] def fetch_current_version_raw(self, **kargs): use_nt_native = kargs.get('usetest', 255) # : for unittests only if os.name == 'java': # it is Jython - use Java for access to native system data try: if __debug__: # this is for unittests only if (use_nt_native & 1) == 0: raise PlatformIDsUnittestTriggered('use_nt_native_1(%d)' % (use_nt_native) ) # # 1. try JNA if _verbose: print("VERB:platformids:scan:try:JNA:jy.platformids.dist.nt.Kernel32GetProductInfo") from jy.platformids.dist.nt import Kernel32GetProductInfo # @UnresolvedImport if _verbose: print("VERB:platformids:scan:try:JNA:jy.platformids.dist.nt.Advapi32GetCurrentVersion") from jy.platformids.dist.nt import Advapi32GetCurrentVersion # @UnresolvedImport if _verbose: print("VERB:platformids:scan:JNA:LOADED") currentversion = Advapi32GetCurrentVersion().getCurrentVersionValues() for k, v in currentversion.items(): self.raw[k] = v except Exception as e: if _debug: print("VERB:platformids:scan:JNA:failed with:" + str(e)) try: if __debug__: # this is for unittests only if (use_nt_native & 2) == 0: raise PlatformIDsUnittestTriggered('use_nt_native_2(%d)' % (use_nt_native) ) # # 2. try java.util.WindowsPreferences... # if _verbose: print("VERB:platformids:scan:try:WindowsPreferences:jy.platformids.dist.nt.ReadCurrentVersionWinPrefs") from jy.platformids.dist.nt import ReadCurrentVersionWinPrefs # @UnresolvedImport if _verbose: print("VERB:platformids:scan:WindowsPreferences:LOADED") currentversion = ReadCurrentVersionWinPrefs() for k, v in currentversion: self.raw[k] = v except Exception as e: if _debug: print("VERB:platformids:scan:WindowsPreferences:failed with:" + str(e)) # # 4. try JNI next... coming soon # try: if __debug__: # this is for unittests only if (use_nt_native & 8) == 0: raise PlatformIDsUnittestTriggered('use_nt_native_8(%d)' % (use_nt_native) ) # # 8. try REG.EXE next... # if _verbose: print("VERB:platformids:scan:try:platformids.dist.nt.RegistryByExe") from platformids.dist.nt.windows_subprocess_reg_exe import RegistryByExe # @UnresolvedImport if _verbose: print("VERB:platformids:scan:RegistryByExe:LOADED") currentversion = RegistryByExe().read_CurrentVersion() for k, v in currentversion.items(): self.raw[k] = v except: if __debug__: # this is for unittests only if (use_nt_native & 16) == 0: raise PlatformIDsUnittestTriggered('use_nt_native_16(%d)' % (use_nt_native) ) # # 16. try min-info # import platform self.raw['ProductName'] = platform.System.getProperty('os.name').lower() # @UndefinedVariable # seems 2.0+ self.raw['CurrentMajorVersionNumber'], self.raw['CurrentMajorVersionNumber'], self.raw['CurrentMajorVersionNumber'] = decode_version_str_to_segments( platform.System.getProperty('os.version').lower()) # @UndefinedVariable elif os.name == 'posix': # for document generation by introspection on posix platform # avoids exception with generator error for sphinx-apidoc pass else: # # read registry # # use standard libs _winreg / winreg try: import winreg # @UnresolvedImport @UnusedImport validxError = WindowsError # @UndefinedVariable except: import _winreg as winreg # @UnresolvedImport @Reimport validxError = OSError hklm = winreg.ConnectRegistry(None, winreg.HKEY_LOCAL_MACHINE) # @UndefinedVariable - win32 only # rnumerate CurrentVersion attribute values curvers_key = winreg.OpenKey(hklm, r"SOFTWARE\Microsoft\Windows NT\CurrentVersion") # @UndefinedVariable - win32 only for i in range(1024): try: n, v, t = winreg.EnumValue(curvers_key, i) # @UndefinedVariable - win32 only @UnusedVariable self.raw[n] = v except validxError: break winreg.CloseKey(curvers_key) # @UndefinedVariable - win32 only # # fetch product category # try: # # use API # # post winxp supports simple access to product type from platformids.dist.nt.windows_kernel32dll import get_win32_OSProductInfo, get_win32_OSVersionInfoExa from platformids.dist.nt.windows_products import prod_ext, prod_type_categories osver = get_win32_OSVersionInfoExa() pinfo = get_win32_OSProductInfo() self.productcategory = pinfo.pdwReturnedProductType # TODO: check it if self.raw['InstallationType'].startswith('IoT'): # superposes others if defined as IoT self.productcategory = VER_NT_IOT except: # # estimate by data - default is GENERIC # try: if self.raw['InstallationType'].startswith('Client'): self.productcategory = VER_NT_WORKSTATION elif self.raw['InstallationType'].startswith('Server'): self.productcategory = VER_NT_SERVER elif self.raw['InstallationType'].startswith('IoT'): self.productcategory = VER_NT_IOT elif re.match(r'.*omain.*', self.raw['InstallationType'], flags=re.MULTILINE): # @UndefinedVariable self.productcategory = VER_NT_DOMAIN_CONTROLLER else: # matches for almost all systems, which in nowadays # could be used in multiple roles - without variation self.productcategory = VER_NT_GENERIC except: self.productcategory = VER_NT_GENERIC # should already be from init self.raw['productcategory'] = self.productcategory return self.raw
[docs] def readout_versioninfo_ext(self): # # Major-Minor-Version # try: # >= Win10 self.ntmajor = int(self.raw['CurrentMajorVersionNumber']) # REG_SZ 17763 self.ntminor = int(self.raw['CurrentMinorVersionNumber']) # REG_DWORD 0xa self.release = int(self.raw['ReleaseId']) # REG_SZ 1511 self.build = self.raw['CurrentBuildNumber'] # REG_DWORD 0x0 except KeyError: # < Win10 try: self.ntmajor, self.ntminor = self.raw['CurrentVersion'].split('.') # REG_SZ 6.3 self.ntmajor = int(self.ntmajor) self.ntminor = int(self.ntminor) except KeyError: # ooops... pass try: self.release = self.build = int(self.raw['CurrentBuildNumber']) # REG_DWORD 0x0 except: pass try: # WinXP < x < Win10 self.sp = re.sub(r'^[^0-9]*', '', self.raw['CSDVersion']) # REG_SZ self.sp = int(self.sp) except KeyError: pass try: self.ubr = int(self.raw['UBR']) # REG_DWORD 0x6b except KeyError: pass try: self.editionid = self.raw['EditionID'] # REG_SZ IoTUAP except KeyError: self.editionid = '' try: self.installationtype = self.raw['InstallationType'] # REG_SZ IoTUAP except KeyError: self.installationtype = '' try: # # a weak criteria as the last resort, # the installation source path with some encoding # into the path name, e.g.: # # D:\GERMAN\WIN2000\SERVER\I386 # D:\GERMAN\WINXP\PRO\I386 # self.sourcepath = self.raw['SourcePath'] # REG_SZ D:\GERMAN\WINXP\PRO\I386 self.sourcepath = re.sub(r'', r'', self.sourcepath) except KeyError: self.sourcepath = '' try: self.productname = self.raw['ProductName'] # REG_SZ IoTUAP except: pass ret = { "ntmajor": self.ntmajor, "ntminor": self.ntminor, "release": self.release, "build": self.build, "productcategory": self.productcategory, "InstallationType": self.installationtype, "ProductName": self.productname, "EditionID": self.editionid } if self.sp != None: ret["sp"] = self.sp if self.ubr != None: ret["ubr"] = self.ubr return ret
def readout_distribution(self): self.readout_versioninfo_ext() return [ 'nt%d%d%d' % (self.ntmajor, self.ntminor, self.release), '%d.%d.%d' % (self.ntmajor, self.ntminor, self.release), 'NT-%d.%d.%d' % (self.ntmajor, self.ntminor, self.release), 'NT', (self.ntmajor, self.ntminor, self.release), 'nt' ]
[docs] def __str__(self): try: res = '' res += "ntmajor = %s\n" % (str(self.ntmajor)) res += "ntminor = %s\n" % (str(self.ntminor)) res += "release = %s\n" % (str(self.release)) res += "build = %s\n" % (str(self.build)) res += "productcategory = %s\n" % (str(self.productcategory)) try: res += "sp = %s\n" % (str(self.sp)) except AttributeError: res += "ubr = %s\n" % (str(self.ubr)) res += "InstallationType = %s\n" % (str(self.installationtype)) res += "EditionID = %s\n" % (str(self.editionid)) res += "ProductName = %s\n" % (str(self.productname)) return res except: return
[docs] def __repr__(self): try: res = "{" res += "ntmajor: %s, " % (str(self.ntmajor)) res += "ntminor: %s, " % (str(self.ntminor)) res += "release: %s, " % (str(self.release)) res += "build: %s, " % (str(self.build)) res += "productcategory: %s, " % (str(self.productcategory)) try: res += "sp: %s, " % (str(self.sp)) except AttributeError: res += "ubr: %s, " % (str(self.ubr)) res += "InstallationType: %s, " % (str(self.installationtype)) res += "EditionID: %s, " % (str(self.editionid)) res += "ProductName: %s, " % (str(self.productname)) res += "}" return res except: return
dist = ['', '', '', 'NT', '', ''] try: WINVERSION = WinVersion() WINVERSION.fetch_current_version_raw() dist = WINVERSION.readout_distribution() except PlatformIDsFileCheck: # not on MS-Windows platform, so scan will fail pass if dist[5] != 'nt': # does not actually match MS-Windows dist = ['nt', '0.0.0', 'NT-0.0.0', 'NT', (0, 0, 0,), 'nt'] if __name__ == "__main__": winv = WinVersion() vinfo = winv.readout_versioninfo_ext() print(vinfo)