V tomto článku si řekneme něco o verzích operačního systému Windows z hlediska programátora. Ukážeme si, jak za běhu programu zjistit, na které platformě a verzi běží, a dále si ukážeme, jak pomocí podmíněného překladu zajistit, aby výsledný kód mohl běžet na požadované verzi Windows. Začněme podmíněným překladem.
Nastavení cílové platformy pro podmíněný překlad
K určení cílové platformy aplikace používáme definované hodnoty, které můžeme definovat například pomocí
#define WINVER 0x0500
nebo ve Visual C++ přímo v nastavení projektu.
Uvedu zde přehled jednotlivých cílových platforem Windows a odpovídajících kombinací definovaných hodnot:
Windows 95 a Windows NT |
WINVER = 0x0400 |
Windows 98 a Windows NT |
_WIN32_WINDOWS=0x0410 a WINVER=0x0400 |
Windows NT 4.0 |
_WIN32_WINNT=0x0400 a WINVER=0x0400 |
Windows 98 |
_WIN32_WINDOWS=0x0410 |
Windows 2000 |
_WIN32_WINNT=0x0500 a WINVER=0x0500 |
Windows Me |
_WIN32_WINDOWS=0x0490 |
Windows XP |
_WIN32_WINNT=0x0501 a WINVER=0x0501 |
Dále máme možnost specifikovat požadovanou verzi Internet Exploreru
Internet Explorer 3.0 – 3.02 |
_WIN32_IE=0x0300 |
Internet Explorer 4.0 |
_WIN32_IE=0x0400 |
Internet Explorer 4.01 |
_WIN32_IE=0x0401 |
Internet Explorer 5.0 |
_WIN32_IE=0x0500 |
Internet Explorer 5.01-5.5 |
_WIN32_IE=0x0501 |
Ve vlastním zdrojovém kódu pak budeme testovat tyto hodnoty, abychom mohli rozhodnout, zda například některé funkce, podporované pouze na určité verzi, máme zahrnout do podmíněného překladu. Jako příklad si uvedeme použití průhledných oken ve Windows 2000 (a vyšší). Funkce SetLayeredWindowAttributes je totiž podporována pouze ve Windows 2000 a vyšší a nikoli na Windows řady 95 (tedy ani např. Windows Me).
#if _WIN32_WINNT >= 0x0500
SetLayeredWindowAttributes(m_hWnd, 0, (BYTE)120, LWA_ALPHA);
#endif //_WIN32_WINNT >= 0x0500
Zjištění verze Windows za běhu programu
Nyní si ukažme, jak za běhu programu zjistit platformu a verzi Windows, na které program právě běží.
K tomuto účelu slouží funkce
BOOL GetVersionEx(
LPOSVERSIONINFO lpVersionInfo // informace o verzi
);
jejíž parametr ukazuje na strukturu
typedef struct _OSVERSIONINFO{
DWORD dwOSVersionInfoSize;
DWORD dwMajorVersion;
DWORD dwMinorVersion;
DWORD dwBuildNumber;
DWORD dwPlatformId;
TCHAR szCSDVersion[ 128 ];
} OSVERSIONINFO;
s tím, že ve Windows 2000 a vyšších navíc může parametrem být adresa rozšířené struktury
typedef struct _OSVERSIONINFOEX {
DWORD dwOSVersionInfoSize;
DWORD dwMajorVersion;
DWORD dwMinorVersion;
DWORD dwBuildNumber;
DWORD dwPlatformId;
TCHAR szCSDVersion[ 128 ];
WORD wServicePackMajor;
WORD wServicePackMinor;
WORD wSuiteMask;
BYTE wProductType;
BYTE wReserved;
} OSVERSIONINFOEX, *POSVERSIONINFOEX, *LPOSVERSIONINFOEX;
Když porovnáte tyto struktury, zjistíte, že „začátek té „rozšířené“ varianty je totožný s variantou OSVERSIONINFO. Můžeme tedy ve všech případech použít rozšířené varianty s tím, že nastavením prvku dwOSVersionInfoSize specifikujeme, zda použít plnou celou strukturu nebo její jednoduchou variantu fungující na všech verzích Windows. V prvním kroku tedy zjistíme, zda jsme ve Windows 2000 nebo vyšších, tím způsobem, že zkusíme použít plnou rozšířenou strukturu. V případě, že funkce vrátí chybu (tedy FALSE), budeme vědět, že nejsme ve Windows 2000, a zavoláme funkci znovu s tím, že předtím pouze nastavíme velikost struktury (dwOSVersionInfoSize) na sizeof(OSVERSIONINFO), tedy na jednoduchou variantu. Tento i další postup přesné specifikace verze, včetně zjištění nainstalovaného Servis Packu a čísla sestavení (ve Windows 2000) je zřejmý z následujícího opisu kódu (členská funkce dialogu ukázkové aplikace), který zjištěné informace „poskládá“ do dvou textových řetězců a zobrazí je ve zmíněném dialogu:
void CUkazkaDlg::ZobrazVerziWindows()
{
TCHAR chVerze[200];
OSVERSIONINFOEX osvi;
BOOL bOsVersionInfoEx;
ZeroMemory(&osvi, sizeof(OSVERSIONINFOEX));
osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
if( !(bOsVersionInfoEx = GetVersionEx ((OSVERSIONINFO > &osvi)) )
{
// pokud nefunguje, nejsme ve Windows 2000
osvi.dwOSVersionInfoSize = sizeof (OSVERSIONINFO);
if (! GetVersionEx ( (OSVERSIONINFO > &osvi) )
return; // někde se stala chyba!
}
switch ( osvi.dwPlatformId )
{
case VER_PLATFORM_WIN32_NT:
if ( osvi.dwMajorVersion <= 4 )
lstrcpy(chVerze, "Windows NT");
if ( osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 0)
lstrcpy(chVerze, "Windows 2000");
if ( osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 1)
lstrcpy(chVerze, "Windows XP");
if ( bOsVersionInfoEx )
{
if ( osvi.wProductType == VER_NT_WORKSTATION )
lstrcat(chVerze, " Professional");
if ( osvi.wProductType == VER_NT_SERVER )
lstrcat(chVerze, " Server");
}
else
{
HKEY hKey;
char szProductType[80];
DWORD dwBufLen;
RegOpenKeyEx( HKEY_LOCAL_MACHINE,
"SYSTEM\\CurrentControlSet\\Control\\ProductOptions",
0, KEY_QUERY_VALUE, &hKey );
RegQueryValueEx( hKey, "ProductType", NULL, NULL,
(LPBYTE) szProductType, &dwBufLen);
RegCloseKey( hKey );
if (lstrcmpi( "WINNT", szProductType) == 0 )
lstrcat(chVerze, " Workstation");
if ( lstrcmpi( "SERVERNT", szProductType) == 0 )
lstrcat(chVerze, " Server");
}
m_Platforma.SetWindowText(chVerze);
sprintf(chVerze, "verze %d.%d %s (Build %d)\n",
osvi.dwMajorVersion,
osvi.dwMinorVersion,
osvi.szCSDVersion,
osvi.dwBuildNumber & 0xFFFF);
m_Verze.SetWindowText(chVerze);
break;
case VER_PLATFORM_WIN32_WINDOWS:
if ( (osvi.dwMajorVersion == 4) && (osvi.dwMinorVersion == 0))
SendDlgItemMessage(IDC_PLATFORMA, WM_SETTEXT, 0, (LPARAM)"Microsoft Windows 95");
if ( (osvi.dwMajorVersion == 4) && (osvi.dwMinorVersion == 10))
SendDlgItemMessage(IDC_PLATFORMA, WM_SETTEXT, 0, (LPARAM)"Microsoft Windows 98");
if ( (osvi.dwMajorVersion == 4) && (osvi.dwMinorVersion == 90))
SendDlgItemMessage(IDC_PLATFORMA, WM_SETTEXT, 0, (LPARAM)"Microsoft Windows ME");
break;
case VER_PLATFORM_WIN32s:
SendDlgItemMessage(IDC_PLATFORMA, WM_SETTEXT, 0, (LPARAM)"Microsoft Win32");
break;
}
}
Zde si můžete stáhnout ukázkový projekt win_version.zip (31 kB).