T-13: Versionsprüfung

Gastposting von Oliver Scheer , Developer Evangelist - Windows, Silverlight und UI-Technologien bei der Microsoft Deutschland GmbH.

13_2_thumb[1]Nur noch 13 Tage bis zum Windows 7 Launch.

Was ist eigentlich die größte Herausforderung beim Wechsel von Windows XP zu Windows 7? Die eigentliche Versionsprüfung. Viele Anwendungen prüfen intern, auf welchem Betriebssystem sie eigentlich ausgeführt werden. Je nach Version des Betriebssystems werden dann bestimmte Funktionen aktiviert bzw. deaktiviert.

Leider ist die Art und Weise der Versionsprüfung oft ein Grund, der die Ausführung der Anwendung auf Windows Vista oder Windows 7 verhindert.

Folgende Regeln sollte man beachten, wenn man unbedingt eine Versionsprüfung durchführen muss:

  1. Ist eine Versionsprüfung wirklich nötig? Wenn nein, Prüfung entfernen.
  2. Wenn geprüft wird, dann über Größer-Gleich-Operator (>=) und nicht mit Gleich-Operator (==).
  3. Falls die Prüfung negativ verläuft, die Anwendung nicht blockieren, sondern den Benutzer warnen und diesen entscheiden lassen, ob die Anwendung weiter ausgeführt werden soll.
  4. Wenn zwingend Features benötigt werden, dann bitte detailliert auf diese Features prüfen, d.h. prüfen, ob bestimmte Komponenten registriert sind.

Mit diesem Vorgehen lassen sich bereits sehr viele Anwendungen auf Windows 7 portieren. Warum geht das so einfach? Weil Windows 7 wie auch schon Windows Vista viele Mechanismen für die Kompatibilität mitbringen und diese auch sehr gut funktionieren. Leider können diese Mechanismen oft nicht aktiv werden, da Entwicklerdies durch harte Prüfungen aushebeln.

Tipp: Version nur im äußersten Notfall prüfen, besser ist auf Features zu prüfen:

C++

#include <windows.h>
#include <stdio.h>
void main()
{
OSVERSIONINFO osvi;
BOOL bIsWindowsXPorLater;
ZeroMemory(&osvi, sizeof(OSVERSIONINFO));
osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
GetVersionEx(&osvi);
bIsWindowsXPorLater = ( (osvi.dwMajorVersion > 5) || (osvi.dwMajorVersion == 5) && (osvi.dwMinorVersion >= 1) ));
if(bIsWindowsXPorLater)
printf("The system meets the requirements.\n");
else printf("The system does not meet the requirements.\n");
}

Das folgende Codebeispiel demonstriert VerifyVersionInfo zum Prüfen des Betriebssystems auf eine bestimmte erforderliche Minimalversion (XP SP2):

C++

#include <windows.h>
BOOL Is_WinXP_SP2_or_Later ()
{
OSVERSIONINFOEX osvi;
DWORDLONG dwlConditionMask = 0;
int op=VER_GREATER_EQUAL;

ZeroMemory(&osvi, sizeof(OSVERSIONINFOEX));
osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
osvi.dwMajorVersion = 5;
osvi.dwMinorVersion = 1;
osvi.wServicePackMajor = 2;
osvi.wServicePackMinor = 0;

VER_SET_CONDITION( dwlConditionMask, VER_MAJORVERSION, op );
VER_SET_CONDITION( dwlConditionMask, VER_MINORVERSION, op );
VER_SET_CONDITION( dwlConditionMask, VER_SERVICEPACKMAJOR, op );
VER_SET_CONDITION( dwlConditionMask, VER_SERVICEPACKMINOR, op );

return VerifyVersionInfo(&osvi, VER_MAJORVERSION | VER_MINORVERSION | VER_SERVICEPACKMAJOR | VER_SERVICEPACKMINOR, dwlConditionMask);
}

.NET Framework-Entwickler können die Operatoren ==, !=, <=, <, >, >= auf dem Objekt Environment.OSVersion.Version verwenden:

C#

if (Environment.OSVersion.Version < new Version(5, 1))
{
MessageBox.Show("Windows XP or later required.", "Incompatible Operating System", MessageBoxButtons.OK, MessageBoxIcon.Error);
return;
}

Prüfen auf Features anstelle auf die Version

Wie bereits erwähnt, ist es besser auf Funktionen des Systems als hart auf eine Versionsnummer zu prüfen. Das liegt schlichtweg daran, dass man durch die Versionsnummer nicht auf den Funktionsumfang eines Betriebssystems schließen kann. Es können z.B. Features deaktiviert oder nachinstalliert worden sein. Auch können Services Packs oder verteilbare DLLs nachgeliefert worden sein, die nicht an eine bestimmte Versionsnummer gebunden sind. Das wird z.B. in Zukunft bei Vista der Fall sein, dort werden einige Features bereit gestellt, die erst mit Windows 7 entwickelt wurden, wie z.B. die Ribbon.

Für C++-Entwickler bedeutet dies, besser auf GetVersion() zu verzichten und lieber auf die Funktion zu prüfen. Ist eine benötigte Funktion nicht vorhanden, sollte die Anwendung dennoch weiter ausgeführt werden, ggf. mit reduziertem Umfang.

Für Win32 sollte folgender Mechanismus verwendet werden:

  • Die Methode LoadLibrary() zum Laden einer Bibliothek, die bisher noch nicht in die Anwendung geladen wurde. Falls man eine neue Funktion aus einer bereits geladenen DLL (z.B. kernel32.dll) benutzen möchte, ist es über die Funktion GetModuleHandle() möglich, den Handle dafür zu erhalten. Falls diese NULL zurückgibt, existiert die Funktion nicht.
  • Die Methode GetProcAddress() liefert einen Funktionszeiger zurück. Falls GetProcAddress()ebenfalls NULL zurückliefert, existiert diese Methode auch nicht.

Das folgende Beispiel demonstriert diesen Mechanismus:

C++

// define function pointer type
typedef BOOL (WINAPI *SetWaitableTimerExProc)(
__in HANDLE hTimer,
__in const LARGE_INTEGER *lpDueTime,
__in LONG lPeriod,
__in PTIMERAPCROUTINE pfnCompletionRoutine,
__in LPVOID lpArgToCompletionRoutine,
__in PREASON_CONTEXT WakeContext,
__in ULONG TolerableDelay
);

LARGE_INTEGER liDueTime;
liDueTime.QuadPart = 0;
nt period = 1000;
unsigned int tolerance = 1000;
HANDLE hTimer = // Get timer handle
REASON_CONTEXT reasonContext = {0};
reasonContext.Version = 0;
reasonContext.Flags = POWER_REQUEST_CONTEXT_SIMPLE_STRING;
reasonContext.Reason.SimpleReasonString = L"MyTimer";

// Get module handle to a module which is already loaded
HMODULE hKernel32Module = GetModuleHandle(_T("kernel32.dll"));
if (hKernel32Module == NULL)
return FALSE;

// Get Address of function
SetWaitableTimerExProc pFnSetWaitableTimerEx = (SetWaitableTimerExProc) ::GetProcAddress(hKernel32Module, "SetWaitableTimerEx");

// Check if the function exists
if (pFnSetWaitableTimerEx == NULL)
return FALSE;

// Call function
if (!pFnSetWaitableTimerEx(hTimer, &liDueTime, period, NULL, NULL, &reasonContext, tolerance)
{ // handle error }

Anwendungen, die in Managed Code geschrieben sind, haben es da etwas leichter. Stehen Funktionen nicht zur Verfügung, die auf Win32 APIs mittels P/Invoke zugreifen, gibt es entweder eine EntryPointNotFoundException oder DllNotFoundException.

Das Video des Tages

Weiterführende Links:

  • Das Video des Tages

Get Microsoft Silverlight