// AboutDlg.cpp : Implementacja of CAboutDlg
#include "stdafx.h"
#include "AboutDlg.h"
#include <windowsx.h>
/////////////////////////////////////////////////////////////////////////////
// CAboutDlg

// Prototypy funkcji dla procedur obsługi wiadomości.

static BOOL AboutDlg_OnInitDialog (HWND hwnd, HWND hwndFocus, LPARAM lParam) ;
static void AboutDlg_OnCommand (HWND hwnd, int id, HWND hwndCtl, UINT codeNotify) ;

typedef enum { typeDefault        = 0,
               typeAdvancedServer = 1,
               typeWorkstation    = 2,
               typeServer         = 3 } NTTYPE ;

static NTTYPE GetNTVersion () ;

// Prototypy funkcji dla funkcji statycznych.

static void  DisplayExecutableVersionInfo (HWND hwnd) ;
static void  DisplayOperatingSystemVersionInfo (HWND hwnd) ;
static void  DisplayProcessorVersionInfo (HWND hwnd) ;

static DWORD FormatMessageFromString (LPCTSTR szFormat, LPTSTR  szBuffer, DWORD nSize, ...) ;

LRESULT CAboutDlg::OnInitDialog(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
    // Wypośrodkowanie okna dialogowego
    CenterWindow () ;

    // Aktualizacja elementów sterujących o informacje o wersji aplikacji
    DisplayExecutableVersionInfo (m_hWnd) ;

    // Aktualizacja elementów sterujących o informacje o wersji systemu operacyjnego
    DisplayOperatingSystemVersionInfo (m_hWnd) ;

    // Aktualizacja elementów sterujących o informacje o wersji procesora
    DisplayProcessorVersionInfo (m_hWnd) ;

    return TRUE ;   // System przejmuje ognisko
}

//
// 040904B0 oznacza US English i stronę kodową Unicode
// 040904E4 oznacza US English i stronę kodową Windows MultiLingual
static const TCHAR szValueNameBase [] = TEXT("\\StringFileInfo\\040904B0\\") ;
static const TCHAR szProductName []   = TEXT("ProductName") ;

// Liczba znaków w podstawowej części ciągu nazwy wartości
#define BASECHARS    (DIM(szValueNameBase) - 1)

//
//  void DisplayExecutableVersionInfo (HWND hwnd)
//
//  hwnd            Uchwyt okna dialogowego.
//
//  CEL:            Odczytuje informacje o wersji aplikacji
//                  i wyświetla je w odpowiednich elementach
//                  sterujących okna dialogowego.
//
//  KOMENTARZ:      Ta implentacja wykorzystuje początkową zawartość
//                  okna sterującego jako klucz informacji o wersji. Ta funkcja
//                  wyświetla wartość klucza uzyskaną z odpowiedniego elementu
//                  sterującego.
//

#define DIM(a) (sizeof(a)/sizeof(a[0]))

static void
DisplayExecutableVersionInfo (HWND hwnd)
{
	BOOL                bResult ;               // Wynik funkcji logicznej
    DWORD               dwVerInfoSize ;         // Wielkość informacji o wersji
    DWORD               dwHandle ;              // Wymagany parametr
    HMODULE             hmod ;                  // Procedura obsługi modułu aplikacji
    HWND                hwndControl ;           // Procedura obsługi okna sterującego
    LPVOID              pVerInfo ;              // Wskaźnik do informacji o wersji pliku
    LPVOID              pValue ;                // Wartość informacji o wersji
	TCHAR               szFullPath [_MAX_PATH] ;// Ścieżka aplikacji
	TCHAR               szValueName [256] ;     // Nazwa wartości do odczytania
    UINT                uLength ;               // Długość odczytanej wartości

    // Uzyskanie pełnej ścieżki do serwera
    hmod = _Module.GetModuleInstance() ;
    ::GetModuleFileName (hmod, szFullPath, DIM(szFullPath)) ;

    // Ustalenie wielkości bufora służącego do zapisania informacji o wersji:
    dwVerInfoSize = GetFileVersionInfoSize (szFullPath, &dwHandle) ;
    if (0 == dwVerInfoSize)
        return ;

    // Alokacja bufora dla bloku informacji o wersji
    pVerInfo = new char [dwVerInfoSize] ;
    ATLASSERT (NULL != pVerInfo) ;
    if (NULL == pVerInfo)
        return ;

    // Odczytanie bloku informacji o wersji do bufora
    GetFileVersionInfo (szFullPath, dwHandle, dwVerInfoSize, pVerInfo) ;

    // Utworzenie podstawowego ciągu nazwy wartości...
	lstrcpy (szValueName, szValueNameBase) ;
	
	// Utworzenie nazwy wartości \StringFileInfo\040904E4\ProductName
	lstrcpy (szValueName + BASECHARS, szProductName) ;

	// Odczytanie wartości
    bResult = ::VerQueryValue (pVerInfo, szValueName, &pValue, &uLength) ;

    // Sformatowanie informacji dla nagłówka okna dialogowego
    // Uzyskanie i dołączenie aktualnego nagłówka do wartości ProductName
    GetWindowText (hwnd, szValueName, DIM (szValueName)) ;
	lstrcat (szValueName, reinterpret_cast <LPCTSTR>(pValue)) ;

    // Zmiana nagłówka okna dialogowego - zwykle jest to "About <ProductName>"
	SetWindowText (hwnd, szValueName) ;

	// Dla każdego elementu sterującego w oknie dialogowym...
    //  pobranie nazwy informacji o wersji z początkowej zawartości okna elementu sterującego.
    //  odczytanie wartości o tej nazwie,
    //  zmiana zawartości okna elementu sterującego na uzyskaną wartość.
    // Ta technika wywodzi się z GENERIC.C.

    hwndControl = GetFirstChild (hwnd) ;
    while (NULL != hwndControl) {
        // Utworzenie podstawowego ciągu nazwy wartości...
	    lstrcpy (szValueName, szValueNameBase) ;
	
    	// Utworzenie nazwy wartości \StringFileInfo\040904E4\<ControlText>
        // Win32 API zawiera domyślnie następujące ciągi informacji o wersji:
        //     CompanyName              LegalCopyright     
        //     FileDescription          OriginalFilename   
        //     FileVersion              ProductName        
        //     InternalName             ProductVersion     
        
        // Uzyskanie zawartości elementu sterującego...
        GetWindowText (hwndControl,
                       szValueName + BASECHARS,
                       DIM(szValueName) - BASECHARS) ;

	    // Odczytanie wartości
	    bResult = VerQueryValue (pVerInfo, szValueName, &pValue, &uLength) ;

        // Jeśli informacja o wersji jest dostępna oraz istnieje nazwa tej informacji...
        if (bResult)
            // Jeśli istnieje wartości dla nazwy informacji o wersji...
            if (0 != uLength && NULL != pValue)
                // Zmiana zawartości elementu sterującego na wartość informacji o wersji
                SetWindowText (hwndControl, reinterpret_cast<LPCTSTR>(pValue)) ;

        hwndControl = GetNextSibling (hwndControl) ;
    }

    // Zwolnienie pamięci dla bloku informacji o wersji
    delete [] pVerInfo ;
}


//
//  DWORD DisplayOperatingSystemVersionInfo (HWND hwnd)
//
//  hwnd            Uchwyt okna dialogowego
//
//  CEL:            Wyświetla wersję systemu operacyjnego
//
//  KOMENTARZ:
//

static void
DisplayOperatingSystemVersionInfo (HWND hwnd)
{
    BOOL                bResult ;
    HINSTANCE           hinst ;
    NTTYPE              NtOsType ;
    TCHAR               szOSVer [256] ;
    TCHAR               szFormatString [256] ;

    // Uzyskanie informacji o wersji systemu operacyjnego
    OSVERSIONINFO       osver ;
    osver.dwOSVersionInfoSize = sizeof (osver) ;    // Konieczność inicjalizacji rozmiaru elementu!

    bResult = GetVersionEx (&osver) ;       // Odczytanie informacji o wersji
    ATLASSERT (FALSE != bResult) ;
    if (FALSE == bResult)
        return ;

    hinst = GetWindowInstance (hwnd) ;      // Uzyskanie egzemplarza LoadString

    switch (osver.dwPlatformId) {
        case VER_PLATFORM_WIN32_NT:         // Windows NT
            NtOsType = GetNTVersion () ;
            LoadString (hinst, IDS_PLATFORM_WIN32_NT + NtOsType,
                        szFormatString, DIM (szFormatString)) ;
            break ;

        case VER_PLATFORM_WIN32s:           // Win32s w Windows 3.1
            LoadString (hinst, IDS_PLATFORM_WIN32s,
                        szFormatString, DIM (szFormatString)) ;
            break ;

        case VER_PLATFORM_WIN32_WINDOWS:     // Windows 95/98
            if ((osver.dwMajorVersion > 4) ||
                (osver.dwMajorVersion == 4) && (osver.dwMinorVersion > 0)) {
                // Windows 98
                LoadString (hinst, IDS_PLATFORM_WIN32_WINDOWS98,
                            szFormatString, DIM (szFormatString)) ;
                // Windows 95 koduje dodatkowe informacje w HIWORD(dwBuildNumber)
                // Usunięcie niepotrzebnych śmieci
                osver.dwBuildNumber = LOWORD (osver.dwBuildNumber) ;
            }
            else {
                // Windows 95
                LoadString (hinst, IDS_PLATFORM_WIN32_WINDOWS95,
                            szFormatString, DIM (szFormatString)) ;
                // Windows 95 koduje dodatkowe informacje w HIWORD(dwBuildNumber)
                // Usunięcie niepotrzebnych śmieci
                osver.dwBuildNumber = LOWORD (osver.dwBuildNumber) ;
            }
            break ;

        default:                            // Nieznany system operacyjny
            LoadString (hinst, IDS_PLATFORM_UNKNOWN,
                        szFormatString, DIM (szFormatString)) ;
            break ;
    }

    wsprintf (szOSVer, szFormatString,
              osver.dwMajorVersion,
              osver.dwMinorVersion,
              osver.dwBuildNumber) ;
    SetDlgItemText (hwnd, IDC_ABOUT_OSVERSION, szOSVer) ;
}


//
//  void DisplayProcessorVersionInfo (HWND hwnd, DWORD dwPlatformId)
//
//  hwnd            Uchwyt okna dialogowego
//  dwPlatformId    Identyfikator platformy sprzętowej zwracany przez GetVersionEx
//
//  CEL:            Wyświetla wersję procesora
//
//  KOMENTARZ:
//

static void
DisplayProcessorVersionInfo (HWND hwnd)
{
    BOOL                bRecognized ;
    HINSTANCE           hinst ;
    SYSTEM_INFO         si ;
    TCHAR               szBuffer [256] ;
    TCHAR               szFormat [256] ;

    // Uzyskanie informacji o aktualnym systemie
    // Wyzerowanie struktury, ponieważ Windows 95 i starsze wersje Windows NT
    // nie inicjalizują *wszystkich* pól struktury (a mianowicie,
    // wProcessorLevel i wProcessorRevision. Niestety, dokumentacja nie
    // informuje, *które* wersje Windows NT nie pozwalają na ustawienie
    // tych pól. Z tego powodu należy utworzyć nowe pola.

    ZeroMemory (&si, sizeof (si)) ;
    GetSystemInfo (&si) ;
    
    hinst = GetWindowInstance (hwnd) ;      // Uzyskanie egzemplarza LoadString

    // Ustalenie architektury procesora
    bRecognized = TRUE ;
    switch (si.wProcessorArchitecture) {
        default:
            bRecognized = FALSE ;
            LoadString (hinst, IDS_PROCESSOR_ARCHITECTURE_UNKNOWN,
                        szBuffer, DIM (szBuffer)) ;
            break ;

        case PROCESSOR_ARCHITECTURE_INTEL:  // Intel
            switch (si.wProcessorLevel) {
                default:
                    bRecognized = FALSE ;
                    LoadString (hinst,
                                IDS_PROCESSOR_LEVEL_INTEL_UNKNOWN,
                                szBuffer, DIM (szBuffer)) ;
                    break ;

                case 3:                     // Intel 80386
                    LoadString (hinst,
                                IDS_PROCESSOR_ARCHITECTURE_INTEL_386_486,
                                szFormat, DIM (szFormat)) ;
                    FormatMessageFromString (
                        szFormat,
                        szBuffer, DIM (szBuffer),
                        TEXT ("80386"),
                        LOBYTE (si.wProcessorRevision)) ;
                    break ;

                case 4:                     // Intel 80486
                    LoadString (hinst,
                                IDS_PROCESSOR_ARCHITECTURE_INTEL_386_486,
                                szFormat, DIM (szFormat)) ;
                    FormatMessageFromString (
                        szFormat,
                        szBuffer, DIM (szBuffer),
                        TEXT ("80486"),
                        LOBYTE (si.wProcessorRevision)) ;
                    break ;

                case 5:                     // Intel Pentium
                    LoadString (hinst,
                                IDS_PROCESSOR_ARCHITECTURE_INTEL_PENTIUM,
                                szFormat, DIM (szFormat)) ;
                    switch (HIBYTE(si.wProcessorRevision)) {
                    default:
                    FormatMessageFromString (
                        szFormat,
                        szBuffer, DIM (szBuffer),
                        TEXT ("Pentium"),
                        HIBYTE (si.wProcessorRevision),
                        LOBYTE (si.wProcessorRevision)) ;
                    break ;
                    case 4:
                    FormatMessageFromString (
                        szFormat,
                        szBuffer, DIM (szBuffer),
                        TEXT ("Pentium with MMX"),
                        HIBYTE (si.wProcessorRevision),
                        LOBYTE (si.wProcessorRevision)) ;
                    break;
                    }

                case 6:                     // Intel Pentium Pro/II
                    LoadString (hinst,
                                IDS_PROCESSOR_ARCHITECTURE_INTEL_PENTIUM,
                                szFormat, DIM (szFormat)) ;
                    switch (HIBYTE(si.wProcessorRevision)) {
                    default:
                        FormatMessageFromString (
                            szFormat,
                            szBuffer, DIM (szBuffer),
                            TEXT ("Pentium Pro"),
                            HIBYTE (si.wProcessorRevision),
                            LOBYTE (si.wProcessorRevision)) ;
                    break ;
                    case 1:
                        FormatMessageFromString (
                            szFormat,
                            szBuffer, DIM (szBuffer),
                            TEXT ("Pentium Pro"),
                            HIBYTE (si.wProcessorRevision),
                            LOBYTE (si.wProcessorRevision)) ;
                    break ;
                    case 3:
                    case 5:
                        FormatMessageFromString (
                            szFormat,
                            szBuffer, DIM (szBuffer),
                            TEXT ("Pentium II"),
                            HIBYTE (si.wProcessorRevision),
                            LOBYTE (si.wProcessorRevision)) ;
                    break ;
                    }
            }
            break ;

        case PROCESSOR_ARCHITECTURE_MIPS:   // MIPS
            switch (si.wProcessorLevel) {   // 00xx - xx to numer 8-bitowej implementacji
                LoadString (hinst, IDS_PROCESSOR_ARCHITECTURE_MIPS,
                            szFormat, DIM (szFormat)) ;
                default:
                    bRecognized = FALSE ;
                    LoadString (hinst, IDS_PROCESSOR_LEVEL_MIPS_UNKNOWN,
                                szBuffer, DIM (szBuffer)) ;
                    break ;

                case 0x0004:                // MIPS R4000
                    FormatMessageFromString (
                        szFormat,
                        szBuffer, DIM (szBuffer),
                        TEXT ("R4000"),
                        LOBYTE (si.wProcessorRevision)) ;
                    break ;
            }
            break ;

        case PROCESSOR_ARCHITECTURE_ALPHA:  // Alpha
            LoadString (hinst, IDS_PROCESSOR_ARCHITECTURE_ALPHA,
                        szFormat, DIM (szFormat)) ;
            switch (si.wProcessorLevel) {   // xxxx - 16-bitowa wersja procesora
                default:
                    bRecognized = FALSE ;
                    LoadString (hinst, IDS_PROCESSOR_LEVEL_ALPHA_UNKNOWN,
                                szBuffer, DIM (szBuffer)) ;
                    break ;

                case 21064:                 // Alpha 21064
                    FormatMessageFromString (
                        szFormat,
                        szBuffer, DIM (szBuffer),
                        TEXT ("21064"),
                        (TCHAR)(HIBYTE (si.wProcessorRevision) + (TCHAR) 'A'),
                        LOBYTE (si.wProcessorRevision)) ;
                    break ;

                case 21066:                 // Alpha 21066
                    FormatMessageFromString (
                        szFormat,
                        szBuffer, DIM (szBuffer),
                        TEXT ("21066"),
                        (TCHAR)(HIBYTE (si.wProcessorRevision) + (TCHAR) 'A'),
                        LOBYTE (si.wProcessorRevision)) ;
                    break ;

                case 21164:                 // Alpha 21164
                    FormatMessageFromString (
                        szFormat,
                        szBuffer, DIM (szBuffer),
                        TEXT ("21164"),
                        (TCHAR)(HIBYTE (si.wProcessorRevision) + (TCHAR) 'A'),
                        LOBYTE (si.wProcessorRevision)) ;
                    break ;
            }
            break ;

        case PROCESSOR_ARCHITECTURE_PPC:    // Power PC
            LoadString (hinst, IDS_PROCESSOR_ARCHITECTURE_PPC,
                        szFormat, DIM (szFormat)) ;
            switch (si.wProcessorLevel) {   // xxxx - 16-bitowa wersja procesora
                default:
                    bRecognized = FALSE ;
                    LoadString (hinst, IDS_PROCESSOR_LEVEL_PPC_UNKNOWN,
                                szBuffer, DIM (szBuffer)) ;
                    break ;

                case 1:                     // Power PC 601
                    FormatMessageFromString (
                        szFormat,
                        szBuffer, DIM (szBuffer),
                        TEXT ("601"),
                        HIBYTE (si.wProcessorRevision),
                        LOBYTE (si.wProcessorRevision)) ;
                    break ;

                case 3:                     // Power PC 603
                    FormatMessageFromString (
                        szFormat,
                        szBuffer, DIM (szBuffer),
                        TEXT ("603"),
                        HIBYTE (si.wProcessorRevision),
                        LOBYTE (si.wProcessorRevision)) ;
                    break ;

                case 4:                     // Power PC 604
                    FormatMessageFromString (
                        szFormat,
                        szBuffer, DIM (szBuffer),
                        TEXT ("604"),
                        HIBYTE (si.wProcessorRevision),
                        LOBYTE (si.wProcessorRevision)) ;
                    break ;

                case 6:                     // Power PC 603+
                    FormatMessageFromString (
                        szFormat,
                        szBuffer, DIM (szBuffer),
                        TEXT ("603+"),
                        HIBYTE (si.wProcessorRevision),
                        LOBYTE (si.wProcessorRevision)) ;
                    break ;

                case 9:                     // Power PC 604+
                    FormatMessageFromString (
                        szFormat,
                        szBuffer, DIM (szBuffer),
                        TEXT ("604+"),
                        HIBYTE (si.wProcessorRevision),
                        LOBYTE (si.wProcessorRevision)) ;
                    break ;

                case 20:                    // Power PC 620
                    FormatMessageFromString (
                        szFormat,
                        szBuffer, DIM (szBuffer),
                        TEXT ("620"),
                        HIBYTE (si.wProcessorRevision),
                        LOBYTE (si.wProcessorRevision)) ;
                    break ;
            }
            break;

        case PROCESSOR_ARCHITECTURE_SHX:    // Hitachi SHx
            LoadString (hinst, IDS_PROCESSOR_ARCHITECTURE_SHX,
                        szFormat, DIM (szFormat)) ;
            break;
        case PROCESSOR_ARCHITECTURE_ARM:    // StrongArm
            LoadString (hinst, IDS_PROCESSOR_ARCHITECTURE_ARM,
                        szFormat, DIM (szFormat)) ;
            break;
        case PROCESSOR_ARCHITECTURE_IA64:   // IA64
            LoadString (hinst, IDS_PROCESSOR_ARCHITECTURE_IA64,
                        szFormat, DIM (szFormat)) ;
            break;
        case PROCESSOR_ARCHITECTURE_ALPHA64: // Alpha 64
            LoadString (hinst, IDS_PROCESSOR_ARCHITECTURE_ALPHA64,
                        szFormat, DIM (szFormat)) ;
            break ;
    }

    // Jeśli typ procesora nie został jeszcze rozpoznany, należy sprawdzić
    // pole dwProcessorType w strukturze SYSTEM_INFO i wykorzystać
    // jego wartość.
    if (!bRecognized) {
        switch (si.dwProcessorType) {
            case PROCESSOR_INTEL_386:
                LoadString (hinst, IDS_PROCESSOR_NOREV_INTEL_386,
                            szBuffer, DIM (szBuffer)) ;
                break ;

            case PROCESSOR_INTEL_486:
                LoadString (hinst, IDS_PROCESSOR_NOREV_INTEL_486,
                            szBuffer, DIM (szBuffer)) ;
                break ;

            case PROCESSOR_INTEL_PENTIUM:
                LoadString (hinst, IDS_PROCESSOR_NOREV_INTEL_PENTIUM,
                            szBuffer, DIM (szBuffer)) ;
                break ;

            case PROCESSOR_MIPS_R4000:
                LoadString (hinst, IDS_PROCESSOR_NOREV_MIPS_R4000,
                            szBuffer, DIM (szBuffer)) ;
                break ;

            case PROCESSOR_ALPHA_21064:
                LoadString (hinst, IDS_PROCESSOR_NOREV_ALPHA_21064,
                            szBuffer, DIM (szBuffer)) ;
                break ;
        }
    }

    SetDlgItemText (hwnd, IDC_ABOUT_PROCESSORVERSION, szBuffer) ;
}

//
//  DWORD FormatMessageFromString (LPCTSTR szFormat, LPTSTR  szBuffer, DWORD nSize, ...)
//
//  szFormat        Ciąg formatujący zawierający znaczniki wiadomości
//  szBuffer        Wyjściowy bufor ciągu
//  nSize           Wielkość wyjściowego bufora ciągu
//  ...             Zmienna liczba opcjonalnych parametrów
//                  
//
//  CEL:        
//                  Przydatna funkcja pomocnicza do wywoływania FormatMessage
//
//  KOMENTARZ:
//

static DWORD
FormatMessageFromString (LPCTSTR szFormat, LPTSTR  szBuffer, DWORD nSize, ...)
{
    DWORD               dwRet ;
    va_list             marker ;

    va_start (marker, nSize) ;              // Inicjalizacja argumentów zmiennej

    dwRet = FormatMessage (FORMAT_MESSAGE_FROM_STRING,
                           szFormat, 0, 0,
                           szBuffer, nSize,
                           &marker) ;

    va_end (marker) ;                       // Reset argumentów zmiennej

    return dwRet ;
}


//
//  void AboutDlg_OnCommand (HWND hwnd, int id, hwnd hwndCtl, UINT codeNotify)
//
//  hwnd            Uchwyt okna dialogowego
//  id              Definiuje identyfikator elementu menu lub akceleratora.
//  hwndCtl         Uchwyt elementu sterującego wysyłającego wiadomość, jeśli ta wiadomość
//                  pochodzi z elementu; w przeciwnym wypadku ten parametr jest pusty. 
//  codeNotify      Definiuje kod powiadomienia, jeśli wiadomość pochodzi z elementu sterującego.
//                  Ten parametr ma wartość 1, jeśli wiadomość pochodzi z akceleratora.
//                  Ten parametr ma wartość 0, jeśli wiadomość pochodzi z menu.
//                  
//
//  CEL:        
//                  Obsługa klawiatury i powiadomień z elementów sterujących.
//                  Klinięcie przycisku OK lub wciśnięcie klawisza Enter/Esc
//                  powoduje zamknięcie okna dialogowego.
//                  
//
//  KOMENTARZ:
//

static
void AboutDlg_OnCommand (HWND hwnd, int id, HWND hwndCtl, UINT codeNotify)
{
    switch (id) {
        case IDOK:                          // Przycisk OK lub klawisz Enter
        case IDCANCEL:                      // Klawisz Esc
            EndDialog (hwnd, TRUE) ;        // Zamknięcie okna dialogowego
			break ;

		default:
			break ;
	}
}

//
//  NTTYPE GetNTVersion ()
//
//  CEL:        
//                  Ustalenie konkretnego wariantu systemu Windows NT.
//
//  KOMENTARZ:
//					System Windows NT 4.0 nie jest zgodny z dokumentacją
//					w Bazie Wiedzy. Obie wersje systemu ustawiają odpowiedni
//                  klucz rejestru na "LANMANNT", a nie "SERVERNT".
//

static NTTYPE
GetNTVersion ()
{
    TCHAR               szValue [256] ;
    DWORD               dwType = 0, dwSize = sizeof (szValue) ;
    HKEY                hKey       = NULL ;
    LONG                lStatus ;

static const TCHAR szProductOptions []   = TEXT("SYSTEM\\CurrentControlSet\\Control\\ProductOptions") ;
static const TCHAR szProductType []      = TEXT("ProductType") ;
static const TCHAR szWinNT []            = TEXT("WINNT") ;      //  Windows NT Workstation
static const TCHAR szServerNT []         = TEXT("SERVERNT") ;   //  Windows NT Server (3.5 lub nowszy)
static const TCHAR szAdvancedServerNT [] = TEXT("LANMANNT") ;   //  Windows NT Advanced Server (3.1)

    lStatus = ::RegOpenKeyEx (HKEY_LOCAL_MACHINE,szProductOptions, 0, KEY_QUERY_VALUE, &hKey) ;
    if (ERROR_SUCCESS != lStatus)
        return typeDefault ;            // Windows NT

    lStatus = ::RegQueryValueEx (hKey, szProductType, NULL, &dwType, (LPBYTE) szValue, &dwSize) ; 
    ::RegCloseKey (hKey) ;
    if (ERROR_SUCCESS != lStatus)
        return typeDefault ;            // Windows NT

    if (0 == lstrcmpi (szWinNT, szValue))
        return typeWorkstation ;        // Windows NT Workstation
    else if (0 == lstrcmpi (szServerNT, szValue))
        return typeServer ;             // Windows NT Server
    else if (0 == lstrcmpi (szAdvancedServerNT, szValue))
        return typeServer ;             // Windows NT Advanced Server (3.1)

    return typeDefault ;                // Windows NT
}
