Tworzenie własnego okna programu
Stronę tą wyświetlono już: 837 razy
Kod programu
Na wcześniejszej stronie omówiony został sposób tworzenia nowego projektu o nazwie HelloWorld. Tym razem utworzony projekt będzie wykorzystywał własnoręcznie utworzone okno programu w celu wyświetlenia tego samego tekstu. Rozpocząć należy oczywiście od utworzenia projektu, tym razem niech jego nazwa będzie np. HelloWorld2. Projekt musi być pusty i należy dodać do niego plik winmain.cpp w sposób wcześniej już omawiany.
Oto kod prostego programu, którego jedynym celem będzie wyświetlania napisu Witaj świecie:
- #include <windows.h>
- LRESULT CALLBACK hwndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam){
- static RECT wndSize; // zmienna statyczna przechowująca informacje o wymiarach okna
- switch(msg){
- case WM_CREATE: // ten komunikat będzie obsłużony zawsze jako pierwszy
- {
- }
- break;
- case WM_DESTROY: // ten komunikat będzie obsłużony, gdy wciśniesz przycisk zamknięcia programu
- {
- PostQuitMessage(0); // kończy program
- }
- break;
- case WM_SIZE:
- {
- SetRect(&wndSize, // wskaźnik do struktury typu RECT
- 0, // zerowa pozycja lewego górnego narożnika okna
- 0, // zerowa pozycja prawego górnego narożnika okna
- LOWORD(lParam), // makro LOWORD wyciąga pierwszych 16 bitów z parametru lParam, które opisują szerokość okna
- HIWORD(lParam) // makro HIWORD wyciąga ostatnie 16 bitów z parametru lParam, które opisują wysokość okna
- ); // ustawia strukturę wndSize tak, aby przechowywała szerokość i wysokość okna
- }
- break;
- case WM_PAINT:
- {
- PAINTSTRUCT ps; // struktora potrzebna do rysowania
- HDC hdc = BeginPaint(hWnd, &ps); // tworzenie kontekstu urządzenia HDC
- SetTextColor(hdc,RGB(255,255,255)); // ustawienie koloru tekstu
- SetBkMode(hdc, TRANSPARENT); // ustawienie przezroczystości tła tekstu
- DrawText(hdc, // kontekst urządzenia
- L"Witaj świecie", // tekst do wyświetlenia
- -1, // to powinna być długość ciągu znaków, które będą rysowane, jednakże w tym przypadku można podać -1 a program sam wyliczy tę wartość
- &wndSize, // struktura zawierająca rozmiar okna
- DT_CENTER | DT_VCENTER | DT_SINGLELINE // flagi trybów rysowania: text centralnie w poziomie; text centralnie w pionie; pojedyńcza linia tekstu (konieczne aby DT_VCENTER działało)
- ); // funkcja rysująca tekst
- EndPaint(hWnd, &ps); // kończenie rysowania
- }
- break;
- }
- return DefWindowProc(hWnd, msg, wParam, lParam);
- }
- int WINAPI WinMain(HINSTANCE hInst, HINSTANCE , LPSTR str, int ){
- WNDCLASS wnd;
- wnd.cbClsExtra = NULL; // ten parametr powinien być ustawiony na null (powinien on zawierać informacje o rozmiarze dodatkowych danych jakie będą przechowywane dla danej klasy okna)
- wnd.cbWndExtra = NULL; // ten parametr powinien być ustawiony na null (powinien on zawierać informacje o rozmiarze dodatkowych danych jakie będą przechowywane dla danego egzemplarza okna)
- wnd.hbrBackground = CreateSolidBrush(RGB(0,0,0)); // to będzie tło okna
- wnd.hCursor = LoadCursor(NULL, IDC_ARROW); // ładowanie standardowego kursora
- wnd.hIcon = LoadIcon(NULL, IDI_APPLICATION); // ładowanie standardowej ikonki aplikacji
- wnd.hInstance = hInst; // uchwyt instancji programu
- wnd.lpfnWndProc = hwndProc; // wskaźnik do funkcji będącej procedurą okna
- wnd.lpszClassName = L"Witaj świecie"; // nazwa klasy okna
- wnd.lpszMenuName = NULL; // tutaj powinno być menu, ale tym razem nie będzie żadnego menu więc NULL
- wnd.style = CS_VREDRAW|CS_HREDRAW; // opcje wyświetlania okna CS_VREDRAW i CS_HREDRAW oznaczają odświerzanie okna w poziomie i pionie
- if(!RegisterClass(&wnd)){ // jeżeli nie udało się zarejestrować klassy okna
- MessageBox(NULL,L"Wzięło się i nie zarejestrowało", L"Błąd", MB_OK); // to płacz, że coś się stało
- return 0; // i zwracaj zero
- }
- HWND hWnd = CreateWindow(L"Witaj świecie", // nazwa klasy okna
- L"Witaj świecie", // tekst belki tytułowej okna
- WS_OVERLAPPEDWINDOW, // styl okna (ten oznacza z belką tytułową, przyciskami minimalizacji, maksymalizacji i zwijania oraz z ramką
- CW_USEDEFAULT, // pozycja x (CS_USEDEFAULT oznacza domyślne)
- CW_USEDEFAULT, // pozycja y (CS_USEDEFAULT oznacza domyślne)
- CW_USEDEFAULT, // szerokość (CS_USEDEFAULT oznacza domyślne)
- CW_USEDEFAULT, // wysokość (CS_USEDEFAULT oznacza domyślne)
- NULL, // okno rodzica (tutaj nie będzie takiego więc NULL)
- NULL, // wskaźnik do menu (też nie będzie więc NULL)
- hInst, // uchwyt instancji programu
- NULL // wskaźnik do dodatkowych danych
- );
- ShowWindow(hWnd, SW_NORMAL); // wyświetlania okna i jego tryb
- UpdateWindow(hWnd); // wymuszenie pierwszego odświerzenia okna
- MSG msg; // struktura komunikatów
- // tutaj jest pętla komunikatów
- while(GetMessage(&msg, 0,0,0)){ // dopóki nie otrzymano komunikatu == 0
- TranslateMessage(&msg); // tłumacz komunikat
- DispatchMessage(&msg); // rozpakuj komunikat
- }
- return 0;
- }
W powyższym kodzie rejestracja klasy okna odbywa się za pomocą funkcji RegisterClass, która przyjmuje jako jedyny parametr wskaźnik do struktury WNDCLASS. Przeznaczenie wszystkich pól struktury WNDCLASS zostało wyjaśnione w kodzie programu. Tworzenie okna programu oraz jego uchwytu HWND odbywa się za pomocą funkcji CreateWindow, której parametry również opisałem w kodzie. Dalej są funkcje odpowiedzialne za wyświetlenie okna ShowWindow oraz pierwsze jego odświeżenie UpdateWindow. Następnie potrzebna jest struktura do przetwarzania komunikatów oraz funkcja GetMessage do ich przechwytywania. Tłumaczenie i rozpakowywanie otrzymanego komunikatu odbywa się odpowiednio za pomocą funkcji TranslateMessage i DispatchMessage.
W funkcji procedury okna hwndProc warto przyjrzeć się dwóm komunikatom WM_SIZE oraz WM_PAINT. Pierwszy z nich odpowiedzialny jest za pozyskiwanie informacji o rozmiarze okna, które z kolei przechowywane są w zmiennej statycznej typu RECT (skrót od rectangle - prostokąt). Do wypełnienia tej struktury wykorzystana została funkcja SetRect. Co ciekawe w parametrze lParam funkcji hwndProc zawarta została informacja na temat rozmiaru obszaru okna. Okazuje się bowiem, że pierwszych 16 bitów parametru lParam zawiera informację o szerokości okna natomiast następne 16 bitów zawiera informacje o wysokości okna. Do wyciągnięcia tych informacji użyto makr LOWORD (skrót od low word - niższe słowo) oraz HIWORD (skrót od hight word - wyższe słowo).
Komunikat WM_PAINT wykorzystuje w swym wnętrzu strukturę typu PAINTSTRUCT oraz dwie kluczowe funkcje BeginPaint - dzięki której pozyskiwany jest kontekst urządzenia HDC oraz na końcu EndPaint. Ważnym jest aby wiedzieć, że struktury PAINSTRUCT oraz funkcji BeginPaint i EndPaint można używać tylko i wyłącznie w komunikacie WM_PAINT i nigdzie indziej.
Makra, funkcje i struktury użyte w kodzie
Poniżej zamieszczam spis nowo użytych makr występujących w kodzie programu:
- LOWORD - wyciąga najniższych 16-bitów z zmiennej 32-bitowej;
- HIWORD - wyciąga ostatnich 16-bitów z zmiennej 32-bitowej;
- RGB - makro tworzące zmienną 24-bitową typu COLORREF opisującą kolor w systemie rgb (czerwony, zielony, niebieski).
Lista funkcji:
- BeginPaint - funkcja używana do pozyskiwania kontekstu urządzenia HDC w komunikacie WM_PAINT. Ta funkcja jest ściśle powiązana z funkcją EndPaint oraz z strukturą PAINTSTRUCT;
- CreateSolidBrush - funkcja używana do pozyskiwania uchwytu wypełnienia o określonym kolorze. Ta funkcja jest ściśle powiązana z typem COLORREF oraz makrem RGB;
- CreateWindow - funkcja używana do utworzenia nowego uchwytu okna o określonej nazwie klasy okna, która z kolei powiązana jest ściśle z funkcją RegisterClass oraz strukturą WNDCLASS;
- DefWindowProc - przekazuje nieobsłużone komunikaty dalej;
- DispatchMessage - rozpakowuje komunikaty;
- DrawText - wyświetla tekst rozmieszczając go w określonym prostokątnym obszarze;
- EndPaint - funkcja używana do zwolnienia kontekstu urządzenia HDC w komunikacie WM_PAINT. Ta funkcja jest ściśle powiązana z funkcją BeginPaint oraz z strukturą PAINTSTRUCT;
- GetMessage - przechwytuje komunikaty;
- LoadCursor - ładuje kursor (w tym projekcie domyślny);
- LoadIcon - ładuje ikonę (w tym projekcie domyślną);
- PostQuitMessage - funkcja kończąca żywot programu;
- RegisterClass - funkcja umożliwiająca rejestrowanie klasy okna. Funkcja jest ściśle powiązana z strukturą WNDCLASS;
- SetBkMode - ustawia tryb rysowania tekstu z tłem lub bez tła dla danego kontekstu urządzenia HDC;
- SetRect - funkcja ustawiająca pola struktury typu RECT;
- SetTextColor - funkcja ustawiająca kolor, jakim tekst będzie rysowany w danym kontekście urządzenia HDC;
- ShowWindow - funkcja wyświetla okno w określonym trybie (zminimalizowane, zmaksymalizowane ...);
- TranslateMessage - tłumaczy komunikaty;
- UpdateWindow - funkcja odświeża okno.
Lista struktur:
- MSG - struktura związana z pętlą komunikatów i przechwytywaniem komunikatów;
- PAINTSTRUCT - struktura związana z pozyskiwaniem kontekstu urządzenia w komunikacie WM_PAINT;
- WNDCLASS - struktura związana z rejestracją nowej klasy okna;