Ermittlung der Windows-Version

Die Betriebssystemversion ist in vielen Fällen ein Kriterium, das das weitere Vorgehen bestimmt. So kann von der Versionsnummer die Funktionsfähigkeit eines kompletten Programmteils oder das Vorhandensein bestimmter API-Funktionen, Funktionsparameter oder Strukturen abhängen.

Microsoft empfiehlt allerdings, dass die Versionsnummer nicht das alleinige Entscheidungsmerkmal sein sollte, da Funktionen durch Erweiterungs-DLLs hinzu gekommen sein könnten.

Vielmehr sollte, wenn möglich, auf das Vorhandensein eines bestimmten Features geprüft werden. Wenn bekannt ist, in welcher DLL die Funktion stecken müsste, dann kann diese DLL mittels LoadLibrary() geladen werden. Danach kann mit GetProcAddress() geprüft werden, ob die Funktion in dieser DLL enthalten ist. Allerdings bedeutet das Vorhandensein einer Funktion noch nicht, dass sie auch vollständig implementiert ist.

Andere Features können z. B. mittels GetSystemMetrics() abgeprüft werden. Für bestimmte Shell-Funktionen gibt es separate Möglichkeiten der Versionsprüfung.

Windows stellt durch die Funktion GetVersionEx() die Möglichkeit der Versionsabfrage bereit. Es gibt auch noch GetVersion(), deren Verwendung aber nicht empfohlen wird, zumal sie nur Werte für Windows 95, 98 und NT liefert. Die Funktion VerifyVersionInfo() liefert die Möglichkeit, die Version gegen bestimmte Mindestkriterien abzugleichen. Im folgenden soll es um GetVersionEx() gehen.

GetVersionEx() erwartet als einzigen Parameter die Adresse einer OSVERSIONINFO Struktur, in deren Membern die Versionsinformationen abgelegt werden. Ab Windows 2000 kann auch eine OSVERSIONINFOEX-Struktur verwendet werden. Diese stimmt mit OSVERSIONINFO in den ersten sechs Membern überein, erweitert sie aber um einige nützliche Informationen.

Die OSVERSIONINFO-Struktur
Name Bedeutung
dwVersionInfoSize Die Größe der OSVERSIONINFO-Struktur bzw. OSVERSIONINFOEX-Struktur. Dieses Feld muss vor dem Aufruf von GetVersionEx() gefüllt werden.
dwMajorVersion Die Hauptversionsnummer.
Windows (NT) 3.x hat die Hauptversionsnummer 3, Windows 95/98/Me und NT 4.0 haben die Hauptversionsnummer 4, Windows 2000, XP und 2003 haben die Nummer 5 und Windows Vista die Nummer 6
dwMinorVersion Die Unterversionsnummer.
Windows NT 3.51 hat bspw. die Unterversion 51, Windows 95/98/Me haben die Unterversionen 0/10/90, NT 4.0/2000/XP/2003 die Unterversionen 0/0/1/2
dwBuildNumber Gibt die Nummer des Betriebssystembuilds an
dwPlatformId Kann die Werte VER_PLATFORM_WIN32s (Windows 3.x), VER_PLATFORM_WIN32_WINDOWS (Windows 95/98/Me) und VER_PLATFORM_WIN32_NT (Windows NT, 2000, XP, 2003 etc.) annehmen
szCSDVersion Ein nullterminierter String, der beliebige Angaben zur Betriebssystemversion enthalten kann

Die ersten 5 Member sind DWords, szCSDVersion ist ein Feld mit 128 Bytes.

Für die im folgenden vorgestellte Funktion reichen diese Informationen aus. Die Funktion kann die Werte 0-8 zurückliefern mit folgender Bedeutung:
0 = unbekannt, 1/2/3 = Win 95/98/Me, 4/5/6/7 = NT 4.0/2000/XP/2003, 8 = Vista

Dabei wird über dwPlatformId entschieden, ob es sich um ein Consumer-Windows (Win 95/98/Me) oder einen NT-Nachfolger handelt. Hauptversionsnummern unter 4 werden nicht ausgewertet, in diesen Fällen wird das Ergebnis 0 (unbekannt) geliefert. Beim Consumer-Windows kann die Hauptversion nur die 4 sein, die Unterscheidung wird durch die Unterversion getroffen.

Windows NT 4.0 hat die Hauptversion 4, unterscheidet sich aber durch dwPlatformId vom Consumer-Windows. Bei Windows 2000/XP/2003 muss wieder die Unterversion herangezogen werden. Windows Vista hat die Hauptversion 6.

In einem Programm sieht es dann folgendermaßen aus.

;cmdlnarg - Ermittlung der Windows-Version
.586
.MODEL flat,stdcall
OPTION casemap:none

include c:\masm32\include\windows.inc
include c:\masm32\include\user32.inc
include c:\masm32\include\kernel32.inc
includelib c:\masm32\lib\user32.lib
includelib c:\masm32\lib\kernel32.lib

.DATA
cVersionTitle db "Version",0
cVersion db "0",0

.DATA?			;uninitialisierte Daten
hInstance HINSTANCE ?

.CODE
start:
  call mymain
  invoke ExitProcess,0


mymain PROC
  invoke GetModuleHandle,NULL
  mov hInstance,eax
  call getWindowsVersionShort
  or ax,30h
  mov cVersion,al
  invoke MessageBox,0,ADDR cVersion,ADDR cVersionTitle,MB_OK
  ret
mymain ENDP

;liefert die Betriebssystemversion
;0 = unbekannt, 1/2/3 = Win 95/98/Me, 4/5/6/7 = NT 4.0/2000/XP/2003, 8 = Vista
getWindowsVersionShort PROC
  LOCAL osver:OSVERSIONINFO
  mov osver.dwOSVersionInfoSize,SIZEOF OSVERSIONINFO
  invoke GetVersionEx,ADDR osver
  @@testConsumerWindows:
  cmp osver.dwPlatformId,VER_PLATFORM_WIN32_WINDOWS
  jne @@testBusinessWindows
    cmp osver.dwMajorVersion,4
    jne @@unknown
    @@test95:
      cmp osver.dwMinorVersion,0
      jne @@test98
      mov eax,1
      ret
    @@test98:
      cmp osver.dwMinorVersion,10
      jne @@testMe
      mov eax,2
      jmp short @@procend
    @@testMe:
      cmp osver.dwMinorVersion,90
      jne @@unknown
      mov eax,3
      jmp short @@procend
  
  @@testBusinessWindows:
    cmp osver.dwPlatformId,VER_PLATFORM_WIN32_NT
    jne @@unknown
    @@testNT:
      cmp osver.dwMajorVersion,4  ;NT 4
      jne @@test2000
      mov eax,4
      jmp short @@procend
    @@test2000:
      cmp osver.dwMajorVersion,5  ;Win 2000, XP, 2003
      jne @@testVista
    @@test2k:
      or osver.dwMinorVersion,0
      jnz @@testXP
      mov eax,5
      jmp short @@procend
    @@testXP:
      cmp osver.dwMinorVersion,1
      jne @@test2003
      mov eax,6
      jmp short @@procend
    @@test2003:
      cmp osver.dwMinorVersion,2
      jne @@unknown
      mov eax,7
      jmp short @@procend
    @@testVista:
    cmp osver.dwMajorVersion,6  ;Vista
    jne @@unknown
    mov eax,8
    jmp short @@procend
  @@unknown:
    xor eax,eax
  @@procend:
  ret
getWindowsVersionShort ENDP
END start

Die Prozedur mymain macht sich die Ausgabe der Versionsnummer leicht, indem einfach 30h zum Funktionsergebnis addiert wird und dieses ASCII-Zeichen als Messagebox ausgegeben wird.

Natürlich haben sie die Möglichkeit, noch wesentlich mehr Informationen zu ermitteln, sowohl mit der OSVERSIONINFOEX-Struktur als auch durch die Registry. Ein Beispiel dazu finden sie im Platform-SDK bei der Funktion GetVersionEx().