Gastposting von Oliver Scheer, Developer Evangelist - Windows, Silverlight und UI-Technologien bei der Microsoft Deutschland GmbH.
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:
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):
#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:
Das folgende Beispiel demonstriert diesen Mechanismus:
// 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: