« Podstawy WinAPI, lekcja »
Rozdział 1. W niniejszym rozdziale dowiesz się jak zbudowana jest aplikacja okienkowa systemu Windows oparta o standardową bibliotekę WinAPI. Ponadto w rozdziale omówiono takie zagadnienia jak: funkcja MessageBox; klasa okna (WNDCLASSEX) i style okna; blokująca pętla komunikatów oraz procedura do obsługi komunikatów. (lekcja)
Panel użytkownika
Nazwa użytkownika:
Hasło:
Nie masz jeszcze konta?
Zarejestruj się!
Autor: 'Złośliwiec'
Kurs WinAPI, C++

Podstawy WinAPI

[lekcja] Rozdział 1. W niniejszym rozdziale dowiesz się jak zbudowana jest aplikacja okienkowa systemu Windows oparta o standardową bibliotekę WinAPI. Ponadto w rozdziale omówiono takie zagadnienia jak: funkcja MessageBox; klasa okna (WNDCLASSEX) i style okna; blokująca pętla komunikatów oraz procedura do obsługi komunikatów.
Naszym pierwszym wielkim wyzwaniem będzie stworzenie i narysowanie zwykłego okienka. Wielkim, bo nie jest to kwestia jednej instrukcji, jak być może sobie wyobrażasz. Trochę trzeba się będzie nagimnastykować... Najpierw musimy stworzyć i zarejestrować klasę okna, następnie stworzyć samo okno, potem wyświetlić je i wreszcie stworzyć tak zwaną pętlę komunikatów.

Zacznijmy od stworzenia nowego projektu. Zakładam, że korzystasz z Dev-C++. W starszych wersjach Deva był odrębny typ projektu - "WinMain project", obecnie jest tylko "Windows Application". Jeśli go wybierzemy, to uzyskamy gotowy szablon windowsowego programu, który będzie potrafił wszystko, co opiszemy sobie za chwilę w tej części kursu. Ale jeśli przeskoczymy ten etap teraz, to później nic a nic nie zakumamy z następnych zagadnień, więc zalecam na razie przeczytać tę część, później przeczytać jeszcze raz i jeszcze raz, aż wreszcie coś zaczniemy łapać ;-).

Tak więc wybieramy typ projektu "Empty Project" i wchodzimy sobie od razu do opcji projektu. W pierwszej zakładce ("General") szukamy ramki "Type" i wybieramy "Win32 GUI". W tej wersji Dev-C++ (kiedy piszę te słowa, aktualna wersja to 4.9.9.2) na tym kończy się ustawianie opcji i możemy od razu przejść do właściwego kodzenia.

Funkcja WinMain

WinMain jest to windowsowy odpowiednik main, jak się zapewne domyślasz. Jej budowa jest następująca:
C/C++
#include <windows.h>

int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow )
{
    return 0;
}
Plik windows.h (razem z innymi powiązanymi z nim nagłówkami) zawiera dosłownie wszystko, co będzie nam potrzebne w kursie WinAPI i będziemy go zawsze dołączać, kiedy zechcemy napisać cokolwiek pod windowsa. O funkcji WinMain trochę już mówiłem, ale wypadałoby dokładniej opisać jej argumenty. Parametr hInstance to uchwyt naszej aplikacji. Tłumacząc na ludzki język: numer jej wystąpienia (instance). Oznacza to, że jeśli uruchomimy np. Notatnik, a następnie nie zamykając go otworzymy Notatnik jeszcze raz, to pierwsze okno Notatnika będzie miało inny numer identyfikacyjny, niż drugie.
W WinAPI typy danych zaczynające się przedrostkiem H oznaczają uchwyty. Zmienne takie służą do indentyfikowania rozmaitych obiektów. Obiektem takim może być też menu (typ uchwytu - HMENU), czcionka (typ uchwytu - HFONT), okno (typ uchwytu - HWND), blok pamięci (typ uchwytu - HGLOBAL lub HLOCAL) i wiele, wiele innych.
Argument hPrevInstance powinien zawierać uchwyt poprzedniego wystąpienia aplikacji, ale od wersji systemu Windows 95 wynosi on zawsze NULL.
Specjalna wartość NULL jest po prostu równa zero. Zwykle wykorzystuje się ją do oznaczenia, że wskaźnik lub uchwyt jest pusty.
Argument lpCmdLine zawiera linię poleceń, z jakiej został uruchomiony nasz program. Jest to niestety pojedynczy string, tak więc trzeba go będzie pociachać, jeśli zechcemy wyciągnąć z niego konkretne parametry dla programu i nie ma takiego luksusu, jak w "zwykłej" main.
Typ LPSTR to po prostu synonim typu char*. Ogólnie wszystkie typy z przedrostkiem P lub LP oznaczają w WinAPI wskaźniki.
Wreszcie nCmdShow określa, jaki powinien być stan okna naszego programu. Powinniśmy podać ten argument dalej, do funkcji ShowWindow pokazującej okno - o tym później.

Cóż jeszcze może być tajemniczego w powyższym kodzie? Instrukcji return 0 chyba nie trzeba omawiać; może najwyżej dziwnie wygląda to WINAPI w deklaracji WinMain. Określa ono tzw. konwencję wywoływania funkcji, ale o tym na razie nie musisz nic wiedzieć :-).

Funkcja MessageBox

Kod powyżej oczywiście nie robi nic konkretnego, podobnie jak pusta funkcja main w programie konsolowym. Ale możemy sobie coś dodać. Okna naszej aplikacji jeszcze nie mamy, ale możemy sobie wywalić na ekran jakiś fajny komunikat. Robimy to funkcją MessageBox.

Składnia:
MessageBox( hWnd, lpText, lpCaption, uType )
ArgumentZnaczenie
hWnduchwyt okna, które jest właścicielem komunikatu. Jeśli okna akurat nie mamy, dajemy tu NULL, co oznacza bezpański komunikat
lpTextpo prostu tekst komunikatu, który wyświetlamy
lpCaptiontytuł okienka komunikatu. Jeśli damy NULL, zostanie wybrany tytuł domyślny Error
uTypekombinacja stylów okienka komunikatu

A oto i style, określające przyciski, jakie będą widoczne w okienku komunikatu:
StałaZnaczenie
MB_ABORTRETRYIGNOREprzyciski Abort, Retry i Ignore (w polskiej wersji systemu będą oczywiście miały inne nazwy)
MB_OKtylko przycisk OK
MB_OKCANCELprzyciski OK i Anuluj
MB_RETRYCANCELprzyciski Ponów i Anuluj
MB_YESNOprzyciski Tak i Nie
MB_YESNOCANCELprzyciski Tak, Nie i Anuluj

Jeśli nie wybierzesz żadnego z tych przycisków, domyślnie zostanie wybrany MB_OK. Dostępne ikonki to:
StałaZnaczenie
MB_ICONEXCLAMATION lub MB_ICONWARNINGwykrzyknik
MB_ICONINFORMATION, MB_ICONASTERISKliterka 'i' w kółeczku (informacja)
MB_ICONQUESTIONznak zapytania
MB_ICONSTOP lub MB_ICONERROR lub MB_ICONHANDbłąd krytyczny (znak stopu)

Jeśli nie wybierzesz żadnej z tych ikon, komunikat będzie bez ikony :-). Żeby wskazać, który przycisk jest domyślny (z grubą, czarną ramką dookoła), dodajesz jeden ze stylów: MB_DEFBUTTON1, MB_DEFBUTTON2, MB_DEFBUTTON3, MB_DEFBUTTON4. Domyślnie wybierany jest MB_DEFBUTTON1.

Możesz określić modalność komunikatu:
StałaZnaczenie
MB_APPLMODALużytkownik może kontynuować pracę w danej aplikacji dopiero po zareagowaniu na komunikat (czyli wciśnięciu jednego z przycisków). Jeśli tego nie uczyni, próba zrobienia czegokolwiek w tej aplikacji skończy się piknięciem (straszne, nieprawdaż? :-) ).
MB_SYSTEMMODALjak wyżej, tylko że komunikat jest wyświetlany zawsze na wierzchu WSZYSTKICH okien, jakie są otworzone w systemie. Należy stosować taki styl do zgłaszania Bardzo Poważnych Błędów, które jeśli się zignoruje, system może przestać normalnie pracować (np. dysk się pali :-) ).
MB_TASKMODALdziała tak samo, jak MB_APPLMODAL, tylko że jeśli nie podasz parametru hWnd, to i tak aplikacja przerwie pracę aż do reakcji użytkownika na komunikat. Używaj tego wtedy, gdy nie masz głównego okna w swojej aplikacji, ale musisz uniemożliwić dalszą pracę aplikacji aż do odpowiedzi użytkownika na komunikat.

Istnieje jeszcze kilka możliwych stylów, ale nie będą nam one na razie potrzebne. Zresztą połowy z wymienionych też nie będziesz pewnie zbyt często używał :-). Warto je jednak znać. Resztę doczytasz sobie w innych źródłach, jak mawiają profesorowie :-).
Funkcja MessageBox może zwrócić następujące wartości:
  • 0 – jeśli funkcja z jakichś przyczyn nawali (np. brak pamięci)
  • IDABORT – wybrano przycisk Przerwij
  • IDCANCEL – wybrano Anuluj
  • IDRETRY – wybrano Ponów
  • IDIGNORE – zgadnij :-)
  • IDNO – zgadnij :-)
  • IDYES – zgadnij :-)
  • IDOK – zgadnij :-)

Często nie musimy w ogóle sprawdzać, co MessageBox zwraca (np. gdy mamy tylko jeden przycisk :-) ). Tak też będzie w naszym przykładziku, który po prostu wyświetla wiadomość. Poniższy kod wpisujemy oczywiście wewnątrz funkcji WinMain:
C/C++
#include <windows.h>

int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow )
{
    MessageBox( NULL, "To jest wiadomość.", "Wiadomość", MB_ICONINFORMATION | MB_OKCANCEL );
    return 0;
}
Komunikat z tego przykładu będzie miał przyciski OK i Anuluj, ale nie sprawdzamy, który z nich został naciśnięty. Mogłem nie dodać żadnego przycisku, wtedy byłby tylko OK, ale chciałem pokazać, jak się łączy poszczególne style (operatorek OR, rzecz jasna, aczkolwiek zwykłe dodawanie + też może być).

Klasa okna

Jak już wspomniałem, żeby stworzyć okienko aplikacji, takie z prawdziwego zdarzenia, musimy najpierw zarejestrować jego klasę. W tym celu wypełniamy pola struktury WNDCLASSEX. Jest to dość spora struktura - moglibyśmy skorzystać z prostszej, WNDCLASS, ale tamta nie ma pola na małą ikonkę, a my bardzo chcemy mieć małą ikonkę w programie (proszę nie bić :-)). Oto co wpisujemy w kolejne pola:
PoleZnaczenie
cbSizeRozmiar struktury w bajtach. Należy tu wpisać sizeof (WINDOWCLASSEX).
styleNazwa mówi za siebie - style klasy. Dodam tylko, że NIE jest to to samo, co style okna, którymi zajmiemy się później. Tak więc wystarczy dać tu 0.
lpfnWndProcWskaźnik do procedury obsługującej okno (o tym później, na razie wpisujemy WndProc)
cbClsExtra, cbWndExtraDodatkowe bajty pamięci dla klasy (można ustawić na 0)
hInstanceIdentyfikator aplikacji, która ma być właścicielem okna (zazwyczaj pierwszy parametr naszej funkcji WinMain)
hIconIkonka okna. Dokładniej: duża ikonka, widać ją kiedy naciśniesz Alt-Tab. Ładujemy ją poleceniem LoadIcon(NULL, IDI_APPLICATION), które wybierze nam domyślną ikonkę aplikacji.
hCursorKursor myszki. Analogicznie, jak dla ikonki, korzystamy z LoadCursor(NULL, IDC_ARROW), co zaowocuje pojawieniem się naszym okienku ślicznej strzałeczki :-).
hbrBackgroundTło naszego okienka, czyli jego kolor i wzór. Wybieramy domyślne, czyli zwykle szare tło - (HBRUSH)(COLOR_WINDOW + 1). Więcej o uchwycie HBRUSH poczytasz w rozdziale poświęconym grafice w WinAPI.
lpszMenuNameNazwa identyfikująca menu naszego okna w pliku zasobów. Na razie nie mamy żadnego menu, więc możemy dać NULL.
lpszClassNameNazwa klasy, którą tworzymy. Możesz wpisać, co chcesz (prawie ;-)).
hIconSmMała ikonka naszej aplikacji. Widać ją w rogu naszego okienka oraz na pasku zadań. Dajemy tutaj to samo, co w przypadku dużej ikony.

A więc ogólnie nasze wypełnianie struktury, której nadaliśmy malowniczą nazwę wc (niby od ''window class'') będzie wyglądało jakoś tak:
C/C++
LPSTR NazwaKlasy = "Klasa Okienka";

WNDCLASSEX wc;

wc.cbSize = sizeof( WNDCLASSEX );
wc.style = 0;
wc.lpfnWndProc = WndProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = hInstance;
wc.hIcon = LoadIcon( NULL, IDI_APPLICATION );
wc.hCursor = LoadCursor( NULL, IDC_ARROW );
wc.hbrBackground =( HBRUSH )( COLOR_WINDOW + 1 );
wc.lpszMenuName = NULL;
wc.lpszClassName = NazwaKlasy;
wc.hIconSm = LoadIcon( NULL, IDI_APPLICATION );
Skoro wypełniliśmy już nasz formularz rejestracyjny, pozostaje tylko wysłać go do Wysokiej Komisji Rejestrującej Klasy :-) i modlić się o pozytywne rozpatrzenie prośby. Należy również liczyć się z możliwością odmowy rejestracji (co w praktyce się nie zdarza, Komisyja łaskawa jest, ale programistyczny savoir vivre wymaga przewidywania absolutnie każdej sytuacji) - wtedy nie pozostanie nam nic innego, jak tylko wywalić nieprzyjemny komunikat i zakończyć program:
C/C++
if( !RegisterClassEx( & wc ) )
{
    MessageBox( NULL, "Wysoka Komisja odmawia rejestracji tego okna!", "Niestety...",
    MB_ICONEXCLAMATION | MB_OK );
    return 1;
}

Jeżeli jednak komisja w postaci funkcji RegisterClassEx wyraziła zgodę, zwracając wartość niezerową, to możemy śmiało przystąpić do kroku następnego, a mianowicie budowy naszego okienka. Do tego zaś służy funkcja CreateWindowEx. Oto jakie są składniki każdego szanującego się okna:

Składnia:
CreateWindowEx( dwExStyle, lpClassName, lpWindowName, dwStyle, x, y, nWidth, nHeight, hWndParent, hMenu, hInstance, lpParam )
ArgumentZnaczenie
dwExStyleRozszerzone parametry stylu okna. Damy sobie WS_EX_WINDOWEDGE, czyli trójwymiarową ramkę
lpClassNameNazwa klasy okna, którą właśnie zarejestrowaliśmy
lpWindowNameNapis na pasku tytułowym okienka
dwStyle"Zwykłe" style okienka (szczegóły dalej)
x, yPozycja okna. Można ustawić na CW_USEDEFAULT, czyli na domyślną pozycję
nWidth, nHeightWymiary okienka. Wpisz, ile chcesz. Tylko nie chciej za dużo albo za mało :-)
hWndParentUchwyt okna rodzicielskiego (nadrzędnego). Zwykle takowe nie istnieje, więc dajemy NULL
hMenuUchwyt menu dla naszego okna. Na razie żadnego nie mamy, więc NULL
hInstanceUchwyt aplikacji, której przypisujemy okienko. Dajemy parametr hInstance, otrzymany od systemu jako argument dla WinMain
lpParamwpisz NULL i nie dociekaj, co to :-). Teoretycznie jest to wskaźnik do dodatkowych parametrów

Najczęściej używane style okienka, podawane jako dwStyle, to:
StałaZnaczenie
WS_BORDERCienka ramka
WS_CAPTIONOkreśla, że okno ma mieć pasek tytułowy (automatycznie dodaje też WS_BORDER)
WS_CHILDTworzy okienko potomne (tego na razie nie robimy), nie może być użyte z WS_POPUP
WS_DISABLEDOkienko będzie wyłączone zaraz po utworzeniu (inwalida od urodzenia? :-) )
WS_DLGFRAMEOkienko z ramką stosowaną do dialogów
WS_HSCROLLOkienko z poziomym paskiem do przesuwania
WS_MAXIMIZEOkienko po utworzeniu będzie zmaksymalizowane
WS_MAXIMIZEBOXDodaje przycisk Maksymalizuj w prawym górnym rogu (musisz połączyć z WS_SYSMENU, żeby działało)
WS_MINIMIZEOkienko startuje na pasku zadań
WS_MINIMIZEBOXDodaje przycisk Minimalizuj (musisz połączyć z WS_SYSMENU)
WS_OVERLAPPEDZwykłe okienko z paskiem tytułowym i z ramką
WS_OVERLAPPEDWINDOWPołączenie stylów WS_OVERLAPPED, WS_CAPTION, WS_SYSMENU, WS_THICKFRAME, WS_MINIMIZEBOX i WS_MAXIMIZEBOX
WS_POPUPZwykły szary prostokąt...
WS_POPUPWINDOWPołączenie stylów WS_OVERLAPPED i WS_SYSMENU, z tym że dopóki nie dodasz WS_CAPTION, menu okienka nie będzie widoczne
WS_SIZEBOXOkienko, które może zmieniać rozmiar
WS_SYSMENUTworzy menu dla okienka (ikonka w lewym górnym rogu), działa tylko z WS_CAPTION
WS_TABSTOPUżytkownik może się przełączać pomiędzy kontrolkami w okienku klawiszem TAB
WS_VISIBLESprawia, że okno jest widzialne
WS_VSCROLLDodaje pionowy pasek przesuwu do okna

Przyjrzyj się kilku przykładom zastosowania powyższych stylów:

Przykładowe style okien (Windows 98)
Przykładowe style okien (Windows 98)

Wiemy już prawie wszystko na temat budowy okienka, a więc - do dzieła. Wywołujemy CreateWindowEx, która przy odrobinie szczęścia powinna nam zwrócić uchwyt do nowego okna:
C/C++
HWND hwnd;

hwnd = CreateWindowEx( WS_EX_CLIENTEDGE, NazwaKlasy, "Oto okienko", WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT, 240, 120, NULL, NULL, hInstance, NULL );

if( hwnd == NULL )
{
    MessageBox( NULL, "Okno odmówiło przyjścia na świat!", "Ale kicha...", MB_ICONEXCLAMATION );
    return 1;
}

Okno wreszcie gotowe! Jedyne, co nam pozostało, to uczynić je widocznym. A tym zajmie się funkcja ShowWindow, oraz UpdateWindow (ta ostatnia żeby upewnić się, że okno zostało poprawnie narysowane):
C/C++
ShowWindow( hwnd, nCmdShow );
UpdateWindow( hwnd );
Jak zapewne pamiętasz, parametr nCmdShow wzięliśmy od funkcji WinMain, ta zaś otrzymała go od systemu, a konkretnie od użytkownika, który we właściwościach skrótu może określić, czy program ma być uruchamiany w okienku normalnym, zmaksymalizowanym itp. Oczywiście, możesz zamiast przypisywania tego parametru dać cokolwiek innego, wtedy użytkownik nie będzie miał po prostu wpływu na to, jak okno będzie wyglądało tuż po uruchomieniu programu.

Pętla komunikatów

Pewnie nie jesteś zachwycony, że nasz program mimo tylu zaklęć nadal nie działa jak należy (o ile miałeś odwagę go skompilować na tym etapie - ja bym nie miał ;-)). No cóż, brakuje jeszcze najważniejszego - pętli komunikatów. Jest to najzwyklejsza w świecie pętla, która ma za zadanie przechwytywać wszelkie komunikaty, jakie system wyśle do naszej aplikacji. Takim komunikatem może być kliknięcie myszą, wciśnięcie jakiegoś klawisza albo zamknięcie okna. Wszystko zależy od tego, jakie konkretnie komunikaty chcemy przechwytywać.

Najpierw musimy sobie stworzyć globalną (na zewnątrz WinMain) zmienną do przechowywania komunikatów:
C/C++
MSG Komunikat;
Następnie robimy wspomnianą pętelkę (to już wewnątrz WinMain):
C/C++
while( GetMessage( & Komunikat, NULL, 0, 0 ) )
{
    TranslateMessage( & Komunikat );
    DispatchMessage( & Komunikat );
}

return Komunikat.wParam;
Funkcja GetMessage, jak sama nazwa wskazuje, pobiera kolejne wiadomości od systemu, a dokładniej z tzw. kolejki wiadomości (''message queue''). Jeśli robimy jakąś czynność, która wiąże się z powstaniem jakiejś wiadomości, np. poruszamy myszą, wiadomość posyłana jest do tej kolejki, skąd zabiera ją GetMessage. Jeżeli kolejka w danym momencie jest pusta, to GetMessage czeka na pierwszą lepszą wiadomość, blokując dalsze wykonywanie programu. Może to brzmieć groźnie, ale w okienkowym systemie jest akurat bardzo pożądane.

Funkcja TranslateMessage wykonuje kilka drobnych operacji z wykorzystaniem naszego komunikatu - szczegóły nie są nam do niczego potrzebne. DispatchMessage wysyła komunikat do właściwego miejsca przeznaczenia - tutaj jest nim nasze okno. Przy tak prostym programie nie musisz podawać, do którego okna ma trafić komunikat - system "sam się domyśli".

Obsługa komunikatów

Wewnątrz WinMain mamy już wszystko, czego nam do szczęścia trzeba. Ale rejestrując klasę naszego okienka podaliśmy nazwę funkcji, która jeszcze nie istnieje i którą sami musimy napisać. Mowa o WndProc (nazwę wymyślasz sam, nie jest ona istotna). To właśnie ta funkcja zajmie się właściwą obsługą komunikatów systemu, czyli np. reakcją na wciskanie różnych klawiszy. Dodajemy tę funkcję zaraz za WinMain:
C/C++
LRESULT CALLBACK WndProc( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam )
{
    switch( msg )
    {
    case WM_CLOSE:
        DestroyWindow( hwnd );
        break;
       
    case WM_DESTROY:
        PostQuitMessage( 0 );
        break;
       
        default:
        return DefWindowProc( hwnd, msg, wParam, lParam );
    }
   
    return 0;
}
Jeden z argumentów funkcji WndProc to msg - jest to kod komunikatu, który w danej chwili funkcja ma obsłużyć. Żeby nasze okno w ogóle się pokazało, WndProc nie musi obsługiwać żadnego komunikatu. My chcemy jednak, aby nasze śliczne okienko dało się czasami zamknąć, dlatego dodaliśmy reakcję na komunikat WM_CLOSE. Reakcja to wywołanie funkcji niszczącej nasze okno. Przy niszczeniu okna system wysyła do aplikacji komunikat WM_DESTROY, który również obsłużymy - wysyłając z kolei do systemu komunikat, że chcemy już zakończyć działanie naszej aplikacji - PostQuitMessage (nie ma okna - nie ma aplikacji).

Pozostałe komunikaty (np. kliknięcie myszą) nas nic nie obchodzą, więc odsyłamy je dalej - do funkcji DefWindowProc, która zajmie się ich domyślną obsługą. To wszystko, WndProc powinna zwrócić jeszcze 0.

Wielki finisz!

No, nareszcie - mamy nasze okienko! Trzeba się było nieźle nagimnastykować, ale to wszystko to była w sumie najtrudniejsza część programowania w WinAPI. Od tej pory pozwolimy, aby DevC++ generował za nas ten cały szkielet programu, a my tylko ewentualnie będziemy go modyfikować do własnych, niecnych celów ;-).

Na zakończenie tej części przedstawiam (na wszelki wypadek) cały omówiony kod w jednym kawałku:

C/C++
#include <windows.h>

LPSTR NazwaKlasy = "Klasa Okienka";
MSG Komunikat;

LRESULT CALLBACK WndProc( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam );

int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow )
{
   
    // WYPEŁNIANIE STRUKTURY
    WNDCLASSEX wc;
   
    wc.cbSize = sizeof( WNDCLASSEX );
    wc.style = 0;
    wc.lpfnWndProc = WndProc;
    wc.cbClsExtra = 0;
    wc.cbWndExtra = 0;
    wc.hInstance = hInstance;
    wc.hIcon = LoadIcon( NULL, IDI_APPLICATION );
    wc.hCursor = LoadCursor( NULL, IDC_ARROW );
    wc.hbrBackground =( HBRUSH )( COLOR_WINDOW + 1 );
    wc.lpszMenuName = NULL;
    wc.lpszClassName = NazwaKlasy;
    wc.hIconSm = LoadIcon( NULL, IDI_APPLICATION );
   
    // REJESTROWANIE KLASY OKNA
    if( !RegisterClassEx( & wc ) )
    {
        MessageBox( NULL, "Wysoka Komisja odmawia rejestracji tego okna!", "Niestety...",
        MB_ICONEXCLAMATION | MB_OK );
        return 1;
    }
   
    // TWORZENIE OKNA
    HWND hwnd;
   
    hwnd = CreateWindowEx( WS_EX_CLIENTEDGE, NazwaKlasy, "Oto okienko", WS_OVERLAPPEDWINDOW,
    CW_USEDEFAULT, CW_USEDEFAULT, 240, 120, NULL, NULL, hInstance, NULL );
   
    if( hwnd == NULL )
    {
        MessageBox( NULL, "Okno odmówiło przyjścia na świat!", "Ale kicha...", MB_ICONEXCLAMATION );
        return 1;
    }
   
    ShowWindow( hwnd, nCmdShow ); // Pokaż okienko...
    UpdateWindow( hwnd );
   
    // Pętla komunikatów
    while( GetMessage( & Komunikat, NULL, 0, 0 ) )
    {
        TranslateMessage( & Komunikat );
        DispatchMessage( & Komunikat );
    }
    return Komunikat.wParam;
}

// OBSŁUGA ZDARZEŃ
LRESULT CALLBACK WndProc( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam )
{
    switch( msg )
    {
    case WM_CLOSE:
        DestroyWindow( hwnd );
        break;
       
    case WM_DESTROY:
        PostQuitMessage( 0 );
        break;
       
        default:
        return DefWindowProc( hwnd, msg, wParam, lParam );
    }
   
    return 0;
}
Poprzedni dokumentNastępny dokument
PodstawyKontrolki