Jump to content
Sign in to follow this  
wraithdu

Compare OS Version

Recommended Posts

wraithdu

This should be a future proof OS version compare function. I've been burned a few times with old scripts using @OSVersion to do version detection when a new OS (like Windows 7) comes out, since @OSVersion tests are usually done explicitly (If @OSVersion = "WIN_VISTA"...).

I've had this laying around for a bit but didn't release it due to a bug in AutoIt < 3.3.1.0. But then it meant it would only work with >= 3.3.1.0, and older versions of AutoIt would get incorrect results, but no errors. Many people still run old versions of AutoIt so that's not cool. I came up with a suitable workaround today so here it is. It can easily be extended to test all kinds of OS properties: Build Number, Platform ID, Product Type, Suite Name. But this is solely aimed at testing version info: major and minor version, service pack major and minor version. In fact according to MSDN, all four of these must be tested at once, not individually.

Usage is pretty straight forward, decide on a test and which version numbers you are interested in. If not @error, 0 means the comparison is false, 1 means the comparison is true. If the function returns @error, check the return: 1 means check @Extended for the source of the error, 2 means GetLastError failed which means the outcome of the function is unknowable. Value of @extended: 1 means the error is from DllCall, 2 means the error is from GetLastError.

#include-once

#cs
    Windows 7               6.1
    Windows Server 2008 R2  6.1
    Windows Server 2008     6.0
    Windows Vista           6.0
    Windows Server 2003 R2  5.2
    Windows Server 2003     5.2
    Windows XP              5.1
    Windows 2000            5.0
#ce

;*** Constants
; dwTypeBitMask
Global Const $VER_BUILDNUMBER = 0x0000004
Global Const $VER_MAJORVERSION = 0x0000002
Global Const $VER_MINORVERSION = 0x0000001
Global Const $VER_PLATFORMID = 0x0000008
Global Const $VER_PRODUCT_TYPE = 0x0000080
Global Const $VER_SERVICEPACKMAJOR = 0x0000020
Global Const $VER_SERVICEPACKMINOR = 0x0000010
Global Const $VER_SUITENAME = 0x0000040
; dwConditionMask
Global Const $VER_EQUAL = 1
Global Const $VER_GREATER = 2
Global Const $VER_GREATER_EQUAL = 3
Global Const $VER_LESS = 4
Global Const $VER_LESS_EQUAL = 5
; if dwTypeBitMask is VER_SUITENAME
Global Const $VER_AND = 6
Global Const $VER_OR = 7

; #FUNCTION# ;===============================================================================
;
; Name...........: _OsVersionTest
; Description ...: Compares OS Version Info
; Syntax.........: _OsVersionTest($iTest, $osMajor, $osMinor = 0, $spMajor = 0, $spMinor = 0)
; Parameters ....: $iTest       - type of test to perform
;                  $osMajor     - OS major version number
;                  $osMinor     - OS minor version number
;                  $spMajor     - service pack major version number
;                  $spMinor     - service pack minor version number
; Return values .: Success      - Returns nonzero value if the comparison is true, 0 if it is false
;                  Failure      - Sets @Error
;                  Return values:
;                  | 1          - Check @Extended
;                  | 2          - GetLastError failed, we have no way to know the outcome of the function
;                  @Extended holds the source of @Error:
;                  | 1          - DllCall error
;                  | 2          - Function error from GetLastError
; Author ........: Erik Pilsits
; Modified.......:
; Remarks .......:
; Related .......:
; Link ..........;
; Example .......;
;
; ;==========================================================================================
Func _OsVersionTest($iTest, $osMajor, $osMinor = 0, $spMajor = 0, $spMinor = 0)
    Local Const $OSVERSIONINFOEXW = "dword dwOSVersionInfoSize;dword dwMajorVersion;dword dwMinorVersion;dword dwBuildNumber;dword dwPlatformId;" & _
                                    "wchar szCSDVersion[128];ushort wServicePackMajor;ushort wServicePackMinor;ushort wSuiteMask;byte wProductType;byte wReserved"
    Local $dwlConditionalMask = 0
    ; initialize structure
    Local $OSVI = DllStructCreate($OSVERSIONINFOEXW)
    DllStructSetData($OSVI, "dwOSVersionInfoSize", DllStructGetSize($OSVI))
    ; set data we want to compare
    DllStructSetData($OSVI, "dwMajorVersion", $osMajor)
    DllStructSetData($OSVI, "dwMinorVersion", $osMinor)
    DllStructSetData($OSVI, "wServicePackMajor", $spMajor)
    DllStructSetData($OSVI, "wServicePackMinor", $spMinor)
    ; check AutoIt version
    ; -1 = version 2 is greater...this is bad, DllCall() int64 return was fixed in 3.3.1.0
    Local $IsBadAutoIt = (__VersionCompare(@AutoItVersion, "3.3.1.0") = -1)
    ; initialize and set the mask
    VerSetConditionMask($VER_MAJORVERSION, $iTest, $dwlConditionalMask, $IsBadAutoIt)
    VerSetConditionMask($VER_MINORVERSION, $iTest, $dwlConditionalMask, $IsBadAutoIt)
    VerSetConditionMask($VER_SERVICEPACKMAJOR, $iTest, $dwlConditionalMask, $IsBadAutoIt)
    VerSetConditionMask($VER_SERVICEPACKMINOR, $iTest, $dwlConditionalMask, $IsBadAutoIt)
    ; perform test
    Return VerifyVersionInfo(DllStructGetPtr($OSVI), BitOR($VER_MAJORVERSION, $VER_MINORVERSION, $VER_SERVICEPACKMAJOR, $VER_SERVICEPACKMINOR), $dwlConditionalMask)
EndFunc

;;;INTERNAL;;;
Func VerSetConditionMask($dwTypeBitMask, $dwConditionMask, ByRef $dwlConditionalMask, $IsBadAutoIt)
    Local $ret = DllCall("kernel32.dll", "uint64", "VerSetConditionMask", "uint64", $dwlConditionalMask, "dword", $dwTypeBitMask, "byte", $dwConditionMask)
    If Not @error Then
        If $IsBadAutoIt Then
            ; fix for bad DllCall() int64 return value
            $dwlConditionalMask = _ReOrderULONGLONG($ret[0])
        Else
            $dwlConditionalMask = $ret[0]
        EndIf
    EndIf
EndFunc

Func VerifyVersionInfo($lpVersionInfo, $dwTypeMask, $dwlConditionalMask)
    Local Const $ERROR_OLD_WIN_VERSION = 1150
    ; dwTypeMask is a BitOR'd combination of the conditions we want to test
    Local $ret = DllCall("kernel32.dll", "int", "VerifyVersionInfoW", "ptr", $lpVersionInfo, "dword", $dwTypeMask, "uint64", $dwlConditionalMask)
    If Not @error Then
        ; test for function error
        If $ret[0] Then
            Return $ret[0] ; comparison is true
        Else
            ; function returned 0, we have to check GetLastError to see if there was an error
            $ret = DllCall("kernel32.dll", "dword", "GetLastError")
            If Not @error Then
                If $ret[0] = $ERROR_OLD_WIN_VERSION Then
                    Return 0 ; no error, the version comparison was false
                Else
                    Return SetError($ret[0], 2, 1) ; we have a real error
                EndIf
            Else
                Return SetError(1, 0, 2) ; this would suck, but shouldn't happen
            EndIf
        EndIf
    Else
        Return SetError(@error, 1, 1) ; DllCall error
    EndIf
EndFunc

Func _ReOrderULONGLONG($UINT64)
    Local $s_uint64 = DllStructCreate("uint64")
    Local $s_ulonglong = DllStructCreate("ulong;ulong", DllStructGetPtr($s_uint64))
    DllStructSetData($s_uint64, 1, $UINT64)
    Local $val = DllStructGetData($s_ulonglong, 1)
    DllStructSetData($s_ulonglong, 1, DllStructGetData($s_ulonglong, 2))
    DllStructSetData($s_ulonglong, 2, $val)
    Return DllStructGetData($s_uint64, 1)
EndFunc

; #FUNCTION# ====================================================================================================================
; Name...........: _VersionCompare
; Description ...: Compares two file versions for equality
; Syntax.........: _VersionCompare($sVersion1, $sVersion2)
; Parameters ....: $sVersion1   - IN - The first version
;                  $sVersion2   - IN - The second version
; Return values .: Success      - Following Values:
;                  | 0          - Both versions equal
;                  | 1          - Version 1 greater
;                  |-1          - Version 2 greater
;                  Failure      - @error will be set in the event of a catasrophic error
; Author ........: Valik
; Modified.......:
; Remarks .......: This will try to use a numerical comparison but fall back on a lexicographical comparison.
;                  See @extended for details about which type was performed.
; Related .......:
; Link ..........:
; Example .......:
; ===============================================================================================================================
Func __VersionCompare($sVersion1, $sVersion2)
    If $sVersion1 = $sVersion2 Then Return 0
    Local $sep = "."
    If StringInStr($sVersion1, $sep) = 0 Then $sep = ","
    Local $aVersion1 = StringSplit($sVersion1, $sep)
    Local $aVersion2 = StringSplit($sVersion2, $sep)
    If UBound($aVersion1) <> UBound($aVersion2) Or UBound($aVersion1) = 0 Then
        ; Compare as strings
        SetExtended(1)
        If $sVersion1 > $sVersion2 Then
            Return 1
        ElseIf $sVersion1 < $sVersion2 Then
            Return -1
        EndIf
    Else
        For $i = 1 To UBound($aVersion1) - 1
            ; Compare this segment as numbers
            If StringIsDigit($aVersion1[$i]) And StringIsDigit($aVersion2[$i]) Then
                If Number($aVersion1[$i]) > Number($aVersion2[$i]) Then
                    Return 1
                ElseIf Number($aVersion1[$i]) < Number($aVersion2[$i]) Then
                    Return -1
                EndIf
            Else ; Compare the segment as strings
                SetExtended(1)
                If $aVersion1[$i] > $aVersion2[$i] Then
                    Return 1
                ElseIf $aVersion1[$i] < $aVersion2[$i] Then
                    Return -1
                EndIf
            EndIf
        Next
    EndIf
    ; This point should never be reached
    Return SetError(2, 0, 0)
EndFunc   ;==>_VersionCompare

Example:

ConsoleWrite("Is Vista or Greater:  " & _OsVersionTest($VER_GREATER_EQUAL, 6) & @CRLF)
ConsoleWrite("Is Win7:  " & _OsVersionTest($VER_EQUAL, 6, 1) & @CRLF)
ConsoleWrite("Is Less that XP SP2:  " & _OsVersionTest($VER_LESS, 5, 1, 2) & @CRLF)
  • Like 1

Share this post


Link to post
Share on other sites
supersonic

Greate function, good job! :)

That's what I was look for...

Share this post


Link to post
Share on other sites
KaFu
KaFu

A little derivate ;), can even be further enhance with the link provided... will do when I'm bored :)...

#cs ----------------------------------------------------------------------------

    AutoIt Version: 3.3.0.0
    Author:         KaFu

    Script Function:
    _GetOSVersionEx

#ce ----------------------------------------------------------------------------

; http://www.codeguru.com/cpp/w-p/system/systeminformation/article.php/c8973

#include <array.au3>

$aRes = _GetOSVersionEx()

_ArrayDisplay($aRes)

Func _GetOSVersionEx()

    Local Const $OSVERSIONINFOEXW = "dword dwOSVersionInfoSize;dword dwMajorVersion;dword dwMinorVersion;dword dwBuildNumber;dword dwPlatformId;" & _
            "wchar szCSDVersion[128];ushort wServicePackMajor;ushort wServicePackMinor;ushort wSuiteMask;byte wProductType;byte wReserved"

    Local $OSVI = DllStructCreate($OSVERSIONINFOEXW)

    DllStructSetData($OSVI, "dwOSVersionInfoSize", DllStructGetSize($OSVI))

    Local $ret = DllCall("kernel32.dll", "int", "GetVersionExW", "ptr", DllStructGetPtr($OSVI))

    Local $aReturn[12][2]

    $aReturn[1][0] = "dwOSVersionInfoSize"
    $aReturn[2][0] = "dwMajorVersion"
    $aReturn[3][0] = "dwMinorVersion"
    $aReturn[4][0] = "dwBuildNumber"
    $aReturn[5][0] = "dwPlatformId"
    $aReturn[6][0] = "szCSDVersion"
    $aReturn[7][0] = "wServicePackMajor"
    $aReturn[8][0] = "wServicePackMinor"
    $aReturn[9][0] = "wSuiteMask"
    $aReturn[10][0] = "wProductType"
    $aReturn[11][0] = "wReserved"

    If Not @error Then

        For $i = 1 To 11
            $aReturn[$i][1] = DllStructGetData($OSVI, $i)
        Next

        $aReturn[0][0] = $aReturn[2][1] & "." & $aReturn[3][1] & "." & $aReturn[4][1]
    EndIf

    Return $aReturn

EndFunc   ;==>_GetOSVersionEx

Share this post


Link to post
Share on other sites
supersonic

KaFu,

I like your UDF a lot and I use it over years...

Since AutoIt V3.3.8.0 if I call the UDF with '_OsVersionTest($VER_EQUAL, 6, 1)' it returns 0 - although I'm using MS-Windows 7 (32-Bit, Service Pack 1).

Calling the UDF with '_OSVersionTest($VER_GREATER_EQUAL, 6)' returns 1 - just as expected...

Please, could you help me?

Greets,

-supersonic.

Share this post


Link to post
Share on other sites
KaFu

Thanks for the courtesy, but it's wraithdu's UDF :)... but I'll take a look in the evening, don't have Win7 at hand here, only XP.

Share this post


Link to post
Share on other sites
supersonic

Ohh... :)

Of course, the UDF is done by wraithdu - my fault, I'm sorry for that.

So, if wraithdu may also would take a look into it... ;)

Share this post


Link to post
Share on other sites
wraithdu

Does this forum have a head smack smiley? This'll work... :)

Please read the function header, I've even provided the currently released OS version info. Win7 is version major 6, version minor 1. You, admittedly, are running Service Pack 1. There's a parameter for that.

_OsVersionTest($VER_EQUAL, 6, 1, 1)

Share this post


Link to post
Share on other sites
supersonic

Damn... You're right! :)

Thanks for the enlightenment - next time I read more carefully.

Edited by supersonic

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
Sign in to follow this  

×