Tworzenie aplikacji dla Windows. Od prostych programów do gier komputerowych



Podobne dokumenty
IFiZR Laboratorium 5 Info

PERSON Kraków

Programowanie współbieżne. Tworzenie i obsługa semaforów oraz wątków przy użyciu funkcji Windows API.

VinCent Office. Moduł Drukarki Fiskalnej

Systemy mikroprocesorowe - projekt

Zarządzanie Zasobami by CTI. Instrukcja

ECDL Advanced Moduł AM3 Przetwarzanie tekstu Syllabus, wersja 2.0

1. Podstawy budowania wyra e regularnych (Regex)

Komunikacja w sieci Industrial Ethernet z wykorzystaniem Protokołu S7 oraz funkcji PUT/GET

INTERAKTYWNA APLIKACJA MAPOWA MIASTA RYBNIKA INSTRUKCJA OBSŁUGI

SZABLONY KOMUNIKATÓW SPIS TREŚCI

WYKŁAD 8. Postacie obrazów na różnych etapach procesu przetwarzania

Wtedy wystarczy wybrać właściwego Taga z listy.

Temat: Funkcje. Własności ogólne. A n n a R a j f u r a, M a t e m a t y k a s e m e s t r 1, W S Z i M w S o c h a c z e w i e 1

System Informatyczny CELAB. Przygotowanie programu do pracy - Ewidencja Czasu Pracy

Projektowanie i programowanie aplikacji biznesowych. Wykład 2

KALENDARZE. Ćwiczenie 1 Tworzenie nowego, edycja kalendarza. 1. Uruchom nowy projekt. 2. W menu Narzędzia kliknij polecenie Zmień czas pracy

Instrukcja programu PControl Powiadowmienia.

Opis zmian funkcjonalności platformy E-GIODO wprowadzonych w związku z wprowadzeniem możliwości wysyłania wniosków bez podpisu elektronicznego

Konfiguracja historii plików

REJESTRATOR RES800 INSTRUKCJA OBSŁUGI

API transakcyjne BitMarket.pl

Instalacja. Zawartość. Wyszukiwarka. Instalacja Konfiguracja Uruchomienie i praca z raportem Metody wyszukiwania...

Harmonogramowanie projektów Zarządzanie czasem

Kancelaris - Zmiany w wersji 2.50

Dziedziczenie : Dziedziczenie to nic innego jak definiowanie nowych klas w oparciu o już istniejące.

Jak korzystać z Group Tracks w programie Cubase na przykładzie EWQLSO Platinum (Pro)

Logowanie do mobilnego systemu CUI i autoryzacja kodami SMS

Rozdział 6. Pakowanie plecaka. 6.1 Postawienie problemu

Warszawa, r.

Zmiany w programie C GEO v. 6.5

2.Prawo zachowania masy

Opis obsługi systemu Ognivo2 w aplikacji Komornik SQL-VAT

Spring MVC Andrzej Klusiewicz 1/18

PODSTAWY METROLOGII ĆWICZENIE 4 PRZETWORNIKI AC/CA Międzywydziałowa Szkoła Inżynierii Biomedycznej 2009/2010 SEMESTR 3

Metody opracowywania dokumentów wielostronicowych. Technologia Informacyjna Lekcja 28

INSTRUKCJA DO PROGRAMU LICZARKA 2000 v 2.56

Pracownia internetowa w każdej szkole. Opiekun pracowni internetowej SBS 2003 PING

PROGRAM NAUCZANIA INFORMATYKA

Zainstalowana po raz pierwszy aplikacja wymaga aktualizacji bazy danych obsługiwanych sterowników.

Procedura okna: LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam);

Instrukcja obsługi Norton Commander (NC) wersja 4.0. Autor: mgr inż. Tomasz Staniszewski

Zmiany w wersji 1.18 programu VinCent Office.

Ćwiczenie 6.5. Otwory i śruby. Skrzynia V

Microsoft Management Console

Akademickie Centrum Informatyki PS. Wydział Informatyki PS

Co nowego w systemie Kancelaris 3.31 STD/3.41 PLUS

VLAN Ethernet. być konfigurowane w dowolnym systemie operacyjnym do ćwiczenia nr 6. Od ćwiczenia 7 należy pracować ć w systemie Linux.

Zintegrowane Systemy Zarządzania Biblioteką SOWA1 i SOWA2 SKONTRUM

Bazy danych. Andrzej Łachwa, UJ, /15

Specyfikacja techniczna banerów Flash

I. Zakładanie nowego konta użytkownika.

PROE wykład 7 kontenery tablicowe, listy. dr inż. Jacek Naruniec

SKRÓCONA INSTRUKCJA OBSŁUGI ELEKTRONICZNEGO BIURA OBSŁUGI UCZESTNIKA BADANIA BIEGŁOŚCI

REGULAMIN X GMINNEGO KONKURSU INFORMATYCZNEGO

Instrukcja obsługi platformy zakupowej e-osaa (klient podstawowy)

OŚWIETLENIE PRZESZKLONEJ KLATKI SCHODOWEJ

PRAWA ZACHOWANIA. Podstawowe terminy. Cia a tworz ce uk ad mechaniczny oddzia ywuj mi dzy sob i z cia ami nie nale cymi do uk adu za pomoc

Instrukcja Obsługi STRONA PODMIOTOWA BIP

Instrukcja wprowadzania ocen do systemu USOSweb

Laboratorium nr 2. Edytory tekstu.

JADWIGA SKIMINA PUBLIKACJA NA TEMAT: NAUKA MS. WORD 2000 W KLASIE IV

Archiwum Prac Dyplomowych

Tekst ozdobny i akapitowy

DJCONTROL INSTINCT I DJUCED PIERWSZE KROKI

INSTRUKCJA KORZYSTANIA Z ELEKTRONICZNEJ ŚCIEŻKI WYKAZÓW

Instrukcja obsługi zamka. bibi-z50. (zamek autonomiczny z czytnikiem identyfikatora Mifare)

Wdrożenie modułu płatności eservice dla systemu Virtuemart 2.0.x

Elementy typografii. Technologia Informacyjna Lekcja 22

Politechnika Warszawska Wydział Matematyki i Nauk Informacyjnych ul. Koszykowa 75, Warszawa

Instrukcja procesu aktywacji oraz obsługi systemu Banku Internetowego dla BS Mikołajki

Wartości domyślne, szablony funkcji i klas

GEO-SYSTEM Sp. z o.o. GEO-RCiWN Rejestr Cen i Wartości Nieruchomości Podręcznik dla uŝytkowników modułu wyszukiwania danych Warszawa 2007

PAKIET MathCad - Część III

Oprogramowanie klawiatury matrycowej i alfanumerycznego wyświetlacza LCD

STRONA GŁÓWNA SPIS TREŚCI. Zarządzanie zawartością stron... 2 Tworzenie nowej strony... 4 Zakładka... 4 Prawa kolumna... 9

OPIS PRZEDMIOTU ZAMÓWIENIA:

Parowanie urządzeń Bluetooth. Instrukcja obsługi

Budowa systemów komputerowych

Technologie internetowe Internet technologies Forma studiów: Stacjonarne Poziom kwalifikacji: I stopnia. Liczba godzin/tydzień: 2W, 2L

Temat: Co to jest optymalizacja? Maksymalizacja objętości naczynia prostopadłościennego za pomocą arkusza kalkulacyjngo.

Opis programu do wizualizacji algorytmów z zakresu arytmetyki komputerowej

INSTRUKCJA PROGRAMU BHM SPIS TREŚCI

Ogólna charakterystyka kontraktów terminowych

Instrukcja obsługi panelu operacyjnego XV100 w SZR-MAX-1SX

1. Korzyści z zakupu nowej wersji Poprawiono Zmiany w słowniku Stawki VAT Zmiana stawki VAT w kartotece Towary...

emszmal 3: Automatyczne księgowanie przelewów w menedżerze sprzedaży BaseLinker (plugin dostępny w wersji ecommerce)

Elementy animacji sterowanie manipulatorem

Tomasz Greszata - Koszalin

Użytkowanie elektronicznego dziennika UONET PLUS.

Rejestr Windows - cz. II

Wewnątrzszkolny system kształcenia PLAN WYNIKOWY

Wyznaczanie współczynnika sprężystości sprężyn i ich układów

Nowe funkcjonalności

Konfiguracja programu Outlook 2007 do pracy z nowym serwerem poczty (Exchange)

Zdalne odnawianie certyfikatów do SWI

7. OPRACOWYWANIE DANYCH I PROWADZENIE OBLICZEŃ powtórka

Warszawska Giełda Towarowa S.A.

INSTRUKCJA DO INTERNETOWEGO ROZKŁADU JAZDY

Transkrypt:

Tworzenie aplikacji dla Windows. Od prostych programów do gier komputerowych Autor: Pawe³ Borkowski ISBN: 978-83-246-1881-1 Format: 158x235, stron: 456 Poznaj tajniki tworzenia aplikacji dla Windows Jak okreœliæ po³o enie, rozmiar i styl okna? Jak tworzyæ w¹tki aplikacji za pomoc¹ funkcji CreateThread? Jak definiowaæ biblioteki? Dev-C++ to zintegrowane œrodowisko programistyczne, którego niew¹tpliwym atutem s¹ tzw. DevPaki, czyli rozszerzenia programu, pozwalaj¹ce korzystaæ z ró nych bibliotek, szablonów i narzêdzi. Œrodowisko Dev-C++ wspomaga tak e pracê nad nowym projektem Windows gotowym kodem tworz¹cym okno z obs³ug¹ podstawowych komunikatów. Wszystko to sprawia, e mamy do czynienia z wygodnym i funkcjonalnym œrodowiskiem, zarówno dla pocz¹tkuj¹cych, jak i zaawansowanych programistów. Z ksi¹ ki Tworzenie aplikacji dla Windows. Od prostych programów do gier komputerowych mo e skorzystaæ ka dy, kto chce nauczyæ siê programowania: zarówno studenci kierunków informatycznych, jak i osoby, które nie maj¹ takiego przygotowania. Podrêcznik kolejno ods³ania poszczególne elementy wiedzy programistycznej od najprostszych po najbardziej zaawansowane. Dowiesz siê wiêc, jak wprowadzaæ niewielkie zmiany w kodzie, jak projektowaæ aplikacje wielow¹tkowe i definiowaæ biblioteki, jak budowaæ du e, sk³adaj¹ce siê z kilku plików projekty, aby na koniec samodzielnie stworzyæ grê komputerow¹. Instalacja œrodowiska Dev-C++ Tworzenie narzêdzia pióro Obs³uga map bitowych Obs³uga komunikatów myszy i klawiatury Obiekty steruj¹ce w oknie Menu i plik zasobów Projektowanie aplikacji wielow¹tkowych Biblioteki statyczne i dynamiczne Multimedia Programowanie gier Naucz siê programowania i twórz w³asne gry!

Spis treści Wstęp... 7 Część I Dla początkujących... 9 Rozdział 1. Instalacja środowiska Dev-C++... 11 Rozdział 2. Pierwszy program... 13 2.1. Przygotowanie edytora... 13 2.2. Kod wygenerowany przez Dev-C++... 15 2.3. Określanie tytułu okna... 19 2.4. Określanie położenia i rozmiaru okna... 19 2.5. Określanie stylu okna... 21 2.6. Ćwiczenia... 22 Rozdział 3. Rysowanie w oknie... 23 3.1. Obsługa komunikatu WM_PAINT... 23 3.2. Zestawienie funkcji graficznych. Program z użyciem funkcji Ellipse i LineTo... 25 3.3. Wyświetlanie tekstu w obszarze roboczym okna... 28 3.4. Pierwszy program z użyciem funkcji SetPixel wykres funkcji sinus... 34 3.5. Tworzenie pióra... 39 3.6. Drugi program z użyciem funkcji SetPixel zbiór Mandelbrota... 42 3.7. Trzeci program z użyciem funkcji SetPixel prosta obsługa bitmap... 48 3.8. Ćwiczenia... 55 Rozdział 4. Obsługa komunikatów myszy... 57 4.1. Program z obsługą komunikatu WM_MOUSEMOVE... 57 4.2. Obsługa komunikatów WM_LBUTTONDOWN i WM_RBUTTONDOWN zbiór Mandelbrota po raz drugi... 62 4.3. Obsługa komunikatów WM_MOUSEMOVE, WM_LBUTTONDOWN i WM_RBUTTONDOWN zbiór Mandelbrota a zbiory Julii... 66 4.4. Tajemnica przycisków okna... 75 4.5. Ćwiczenia... 86 Rozdział 5. Obsługa komunikatów klawiatury... 87 5.1. Komunikaty klawiatury. Obsługa komunikatu WM_CHAR... 87 5.2. Obsługa komunikatu WM_KEYDOWN. Diagram Feigenbauma... 90 5.3. Ćwiczenia... 101

4 Tworzenie aplikacji dla Windows. Od prostych programów do gier komputerowych Rozdział 6. Obiekty sterujące w oknie... 103 6.1. Obiekt klasy BUTTON... 103 6.2. Obsługa grafiki za pomocą obiektów klasy BUTTON... 109 6.3. Obiekt klasy edycji. Automaty komórkowe... 116 6.4. Ćwiczenia... 128 Rozdział 7. Menu i plik zasobów... 131 7.1. Dodanie menu do okna... 131 7.2. Obsługa bitmapy z pliku zasobów... 138 7.3. Odczytywanie danych z pliku. Edytor bitmap... 143 7.4. Kopiarka wielokrotnie redukująca... 154 7.5. Ćwiczenia... 169 Podsumowanie części I... 169 Część II Dla średniozaawansowanych... 171 Rozdział 8. Projektowanie aplikacji wielowątkowych... 173 8.1. Tworzenie wątków za pomocą funkcji CreateThread... 173 8.2. Czy praca z wątkami przyspiesza działanie aplikacji? Sekcja krytyczna i priorytety wątków... 178 8.3. Wstrzymywanie pracy i usuwanie wątków... 190 8.4. Ćwiczenia... 199 Rozdział 9. Definiowanie bibliotek... 201 9.1. Biblioteki statyczne... 201 9.2. Biblioteki dynamiczne podejście strukturalne... 207 9.3. Biblioteki dynamiczne podejście obiektowe... 220 9.4. Własny temat okna... 225 9.5. Ćwiczenia... 254 Rozdział 10. Multimedia... 255 10.1. Odtwarzanie plików wav funkcja sndplaysound... 255 10.2. Odtwarzanie plików mp3 biblioteka FMOD... 258 10.3. Ćwiczenia... 269 Podsumowanie części II... 270 Część III Dla zaawansowanych... 273 Rozdział 11. Programowanie gier z użyciem biblioteki OpenGL... 275 11.1. Podstawy obsługi biblioteki OpenGL... 275 11.2. Prymitywy OpenGL... 284 11.3. Bufor głębokości, perspektywa wzorzec kodu do programowania gier (pierwsze starcie)... 298 11.4. Animacja... 309 11.5. Poruszanie się po scenie, funkcja glulookat i wzorzec kodu do programowania gier (starcie drugie, decydujące)... 320 11.6. Tekstury i mipmapy... 334 11.7. Listy wyświetlania i optymalizacja kodu... 349 11.8. Detekcja kolizji... 361 11.9. Światło, mgła, przezroczystość i inne efekty specjalne... 368 Uruchamianie gry w trybie pełnoekranowym... 368 Mgła... 371 Przezroczystość... 374 Światło... 384

Spis treści 5 11.10. Jak ustawić stałą prędkość działania programu?... 390 11.11. Gotowe obiekty OpenGL... 398 11.12. Fonty... 406 11.13. Dźwięk na scenie... 416 11.14. Budujemy grę!... 419 Wprowadzenie i charakter gry... 419 Dane gracza... 420 Strzał z broni... 423 Pozostali bohaterowie gry... 425 Odnawianie zasobów gracza... 428 Zakończenie programu (gry)... 430 Podsumowanie części III... 433 Dodatki... 435 Dodatek A... 437 Dodatek B... 439 Skorowidz... 441

Rozdzia 8. Projektowanie aplikacji wielow tkowych 173 Rozdzia 8. Projektowanie aplikacji wielow tkowych 8.1. Tworzenie w tków za pomoc funkcji CreateThread Jeste my przyzwyczajeni do tego, e w systemie Windows mo emy pracowa na wielu oknach jednocze nie. Jak du e jest to udogodnienie, nie trzeba nikogo przekonywa. T cech systemu nazywamy wielozadaniowo ci. Jej najpo yteczniejszym zastosowaniem jest mo liwo ukrycia przed pracodawc pod stosem otwartych okien jednego okienka z uruchomion ulubion gr. System Windows pozwala tak e na co wi cej na wielow tkowo, czyli wykonywanie wielu zada równocze nie w ramach dzia ania jednego programu. Oczywi cie, poj cia pracy równoczesnej nie nale y traktowa zbyt dos ownie. System dzieli czas pracy procesora i przyznaje po kolei pewn jego cz w celu obs ugi kolejnego w tku. Poniewa wspomniany proces wykonywany jest bardzo szybko, powstaje iluzja równoleg ego dzia ania w tków. W tek tworzymy za pomoc funkcji CreateThread o nast puj cej sk adni: HANDLE CreateThread(LPSECURITY_ATTRIBUTES atrybuty_watku, DWORD rozmiar_stosu, LPTHREAD_START_ROUTINE adres_funkcji_watku, LPVOID parametr, DWORD flaga_watku, LPDWORD identyfikator ); Pierwszy argument atrybuty_watku mia zastosowanie w systemie Windows NT. Od tego czasu nie wykorzystuje si go i mo emy w jego miejsce wpisa NULL. Drugi argument okre la rozmiar stosu w bajtach. Je eli nie mamy pomys u dotycz cego jego wielko ci, wpiszmy zero, co oznacza zadeklarowanie stosu standardowego rozmiaru. W parametrze

174 Cz II Dla redniozaawansowanych adres_funkcji_watku powinien znajdowa si, zreszt zgodnie z pomys owo nadan nazw, adres funkcji, która zawiera kod obs ugi w tku. Deklaracja funkcji musi mie nast puj c posta : DWORD NazwaFunkcji(LPVOID); Parametr, który mo emy przekaza do funkcji w tku, przekazujemy jako czwarty argument funkcji CreateThread. Argument flaga_watku okre la chwil uruchomienia w tku. Mo emy u y jednej z dwóch warto ci: 0 w tek uruchamiany jest w chwili utworzenia, CREATE_SUSPENDED uruchomienie w tku jest wstrzymane a do wywo ania funkcji ResumeThread. Ostatni parametr powinien zawiera wska nik do zmiennej, w której zostanie umieszczony identyfikator w tku. Je eli uruchomienie w tku powiedzie si, funkcja Create Thread zwraca uchwyt utworzonego w tku, w przeciwnym przypadku zwracana jest warto NULL. Wszystko, co w a nie przeczytali cie, jest niezwykle ciekawe, ale warto ju przyst pi do budowania programu. B dzie to aplikacja z dziedziny helmintologii, czyli nauki o robakach (niestety, paso ytniczych). Wyobra my sobie obszar roboczy naszego okna. Klikni cie lewego przycisku myszy powinno wywo a nowy w tek robaka, którego pocz tek niszczycielskiej dzia alno ci b dzie si znajdowa w miejscu, w jakim aktualnie znajduje si kursor myszy. Tworzymy nowy projekt Windows o nazwie Program20.dev. Spójrzmy na prosty program realizuj cy wspomniane zadanie (plik Program20_main.cpp), a nast pnie omówi kluczowe punkty programu. #include <windows.h> /* Sta e rozmiaru obszaru roboczego okna */ const int MaxX = 640; const int MaxY = 480; /* Zmienne globalne dla obs ugi w tków */ int x, y; /* Funkcja w tku */ DWORD Robak(LPVOID param); LRESULT CALLBACK WindowProcedure (HWND, UINT, WPARAM, LPARAM); char szclassname[ ] = "WindowsApp"; int WINAPI WinMain (HINSTANCE hthisinstance, HINSTANCE hprevinstance, LPSTR lpszargument, int nfunsterstil) HWND hwnd; MSG messages; WNDCLASSEX wincl;

Rozdzia 8. Projektowanie aplikacji wielow tkowych 175 wincl.hinstance = hthisinstance; wincl.lpszclassname = szclassname; wincl.lpfnwndproc = WindowProcedure; wincl.style = CS_DBLCLKS; wincl.cbsize = sizeof (WNDCLASSEX); wincl.hicon = LoadIcon (NULL, IDI_APPLICATION); wincl.hiconsm = LoadIcon (NULL, IDI_APPLICATION); wincl.hcursor = LoadCursor (NULL, IDC_ARROW); wincl.lpszmenuname = NULL; wincl.cbclsextra = 0; wincl.cbwndextra = 0; wincl.hbrbackground = (HBRUSH) COLOR_BACKGROUND; if (!RegisterClassEx (&wincl)) return 0; hwnd = CreateWindowEx ( 0, szclassname, "Program20 - aplikacja wielow tkowa", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, MaxX+8, MaxY+34, HWND_DESKTOP, NULL, hthisinstance, NULL ); ShowWindow (hwnd, nfunsterstil); while (GetMessage (&messages, NULL, 0, 0)) TranslateMessage(&messages); DispatchMessage(&messages); return messages.wparam; LRESULT CALLBACK WindowProcedure (HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam) static HDC hdc; PAINTSTRUCT ps; DWORD pointer; switch (message) case WM_PAINT: hdc = BeginPaint(hwnd, &ps); if(x>=5&&y>=5&&x<maxx-5&&y<maxy-5) Ellipse(hdc, x-5, y-5, x+5, y+5); EndPaint(hwnd, &ps); case WM_LBUTTONDOWN: x = LOWORD(lParam);

176 Cz II Dla redniozaawansowanych y = HIWORD(lParam); CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)Robak, hwnd, 0, &pointer); case WM_DESTROY: PostQuitMessage (0); default: return DefWindowProc (hwnd, message, wparam, lparam); return 0; DWORD Robak(LPVOID param) int wx = x, wy = y; //uruchomienie liczb losowych srand(gettickcount()); //p tla niesko czona while(true) //wylosuj ruch robaka wx += (rand()%5) - 2; wy += (rand()%5) - 2; //sprawd, czy robak znajduje si w dozwolonym obszarze if(wx<5) wx = 5; if(wx>maxx-5) wx = MaxX-5; if(wy<5) wy = 5; if(wy>maxy-5) wy = MaxY-5; //za aduj nowe wspó rz dne do zmiennej globalnej x = wx; y = wy; //narysuj robaka w nowym po o eniu InvalidateRect((HWND)param, NULL, FALSE); //zaczekaj 1/10 sekundy Sleep(100); return 0; Program po uruchomieniu czeka na naci ni cie lewego przycisku myszy. case WM_LBUTTONDOWN: x = LOWORD(lParam); y = HIWORD(lParam); CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)Robak, hwnd, 0, &pointer); Je eli procedura okna otrzyma komunikat o zaj ciu tego zdarzenia, tworzy w tek, którego obs ug powierza funkcji Robak. Wspó rz dne kursora myszy s adowane do zmiennych globalnych x i y w celu przekazania ich do funkcji obs ugi w tku. Aplikacja kontynuuje prac, a równolegle z ni zaczyna dzia a nowy w tek. Spójrzmy na kod funkcji Robak.

Rozdzia 8. Projektowanie aplikacji wielow tkowych 177 DWORD Robak(LPVOID param) int wx = x, wy = y; //uruchomienie liczb losowych srand(gettickcount()); while(true) //wylosuj ruch robaka wx += (rand()%5) - 2; wy += (rand()%5) - 2; //sprawd, czy robak znajduje si w dozwolonym obszarze if(wx<5) wx = 5; if(wx>maxx-5) wx = MaxX-5; if(wy<5) wy = 5; if(wy>maxy-5) wy = MaxY-5; //za aduj nowe wspó rz dne do zmiennej globalnej x = wx; y = wy; //narysuj robaka w nowym po o eniu InvalidateRect((HWND)param, NULL, FALSE); //zaczekaj 1/10 sekundy Sleep(100); return 0; Pocz tkowe wspó rz dne obiektu zosta y przekazane przy u yciu zmiennych globalnych x i y. int wx = x, wy = y; Pozosta a cz kodu umieszczona jest w p tli niesko czonej, co oznacza, e w tek zostanie zniszczony dopiero w chwili zamkni cia aplikacji. Bardzo wa n cech robaka jest jego losowy ruch realizowany za pomoc uaktualniania wspó rz dnych. wx += (rand()%5) - 2; wy += (rand()%5) - 2; Poniewa w tym zadaniu korzystamy z funkcji rand, która zwraca liczby pseudolosowe, wa ne jest, by warto inicjalizuj ca tej funkcji ró ni a si dla ka dego w tku. Liczb pocz tkow dla oblicze funkcji rand przekazujemy za pomoc srand, której argumentem jest u nas funkcja GetTickCount podaj ca liczb milisekund liczon od chwili uruchomienia systemu Windows. srand(gettickcount()); Po obliczeniu nowej wspó rz dnej po o enia robaka i sprawdzeniu jej poprawno ci wymuszamy odmalowanie obszaru roboczego okna. InvalidateRect((HWND)param, NULL, FALSE);

178 Cz II Dla redniozaawansowanych W ten sposób w tek komunikuje si z aplikacj g ówn i zmusza do zainteresowania si kodem obs ugi komunikatu WM_PAINT. case WM_PAINT: hdc = BeginPaint(hwnd, &ps); if(x>=5&&y>=5&&x<maxx-5&&y<maxy-5) Ellipse(hdc, x-5, y-5, x+5, y+5); EndPaint(hwnd, &ps); Wizualn cz ci dzia ania w tku jest narysowana w nowym po o eniu elipsa. Przyk adowy efekt dzia ania programu z uruchomionymi jednocze nie kilkudziesi cioma w tkami przedstawiam na rysunku 8.1. Rysunek 8.1. Okno dzia aj cej aplikacji Program20 8.2. Czy praca z w tkami przyspiesza dzia anie aplikacji? Sekcja krytyczna i priorytety w tków Odpowied na pytanie postawione w tytule podrozdzia u nie jest prosta. Wszystko zale y od rodzaju czynno ci realizowanych przez w tek. Pami tajmy, e ka dy utworzony w tek korzysta z cz ci czasu pracy procesora. Dlatego b dna jest my l, e skomplikowane obliczenie roz o one na wiele w tków wykona si szybciej ni to samo

Rozdzia 8. Projektowanie aplikacji wielow tkowych 179 obliczenie w jednow tkowej aplikacji. Natomiast istniej sytuacje, w których procesor czeka bezczynnie lub jest tylko nieznacznie zaj ty, wtedy oczywi cie stosowanie w tków jest zasadne. Operowanie w tkami ma przede wszystkim u atwi prac nam, czyli programistom. Ilustracj do postawionego pytania b dzie aplikacja, w której na dwóch cz ciach obszaru roboczego okna wy wietlimy zbiór Mandelbrota i jeden ze zbiorów Julii. Wykonanie ka dego z tych zada bardzo mocno obci a procesor i kart graficzn. Nasza aplikacja w pierwszej kolejno ci b dzie rysowa a oddzielnie zbiór Mandelbrota i zbiór Julii, a nast pnie obydwa zadania zostan wykonane równoleg e w dwóch w tkach. Policzymy czas wykonania zada w wersji bez u ycia w tków i z ich u yciem. Tworzymy nowy projekt Program21.dev. Poniewa przewidujemy du ilo operacji graficznych, odpowiedni kod umie cimy w pliku Program21.h. Wygenerowany przez Dev-C++ kod (plik Program21_main.cpp) modyfikujemy tylko w czterech miejscach. 1. Dodajemy plik nag ówkowy. #include "Program21.h" 2. Zmieniamy parametry funkcji CreateWindowEx. hwnd = CreateWindowEx ( 0, szclassname, "Program21", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, 648, 340, HWND_DESKTOP, NULL, hthisinstance, NULL ); 3. Dodajemy kod obs ugi komunikatu WM_PAINT. case WM_PAINT: hdc = BeginPaint(hwnd, &ps); MojaProcedura(hdc); EndPaint(hwnd, &ps); 4. Dodajemy kod obs ugi komunikatu WM_LBUTTONDOWN. Dzi ki temu mo liwe b dzie atwe od wie anie obszaru roboczego okna i tym samym wielokrotne powtórzenie do wiadczenia. case WM_LBUTTONDOWN: InvalidateRect(hwnd, NULL, TRUE); Plik Program21_main.cpp w ca o ci wygl da nast puj co (jak zwykle, pomini te zosta y generowane przez Dev-C++ komentarze):

180 Cz II Dla redniozaawansowanych #include <windows.h> #include "Program21.h" LRESULT CALLBACK WindowProcedure (HWND, UINT, WPARAM, LPARAM); char szclassname[ ] = "WindowsApp"; int WINAPI WinMain (HINSTANCE hthisinstance, HINSTANCE hprevinstance, LPSTR lpszargument, int nfunsterstil) HWND hwnd; MSG messages; WNDCLASSEX wincl; wincl.hinstance = hthisinstance; wincl.lpszclassname = szclassname; wincl.lpfnwndproc = WindowProcedure; wincl.style = CS_DBLCLKS; wincl.cbsize = sizeof (WNDCLASSEX); wincl.hicon = LoadIcon (NULL, IDI_APPLICATION); wincl.hiconsm = LoadIcon (NULL, IDI_APPLICATION); wincl.hcursor = LoadCursor (NULL, IDC_ARROW); wincl.lpszmenuname = NULL; wincl.cbclsextra = 0; wincl.cbwndextra = 0; wincl.hbrbackground = (HBRUSH) COLOR_BACKGROUND; if (!RegisterClassEx (&wincl)) return 0; hwnd = CreateWindowEx ( 0, szclassname, "Program21", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, 648, 340, HWND_DESKTOP, NULL, hthisinstance, NULL ); ShowWindow (hwnd, nfunsterstil); while (GetMessage (&messages, NULL, 0, 0)) TranslateMessage(&messages); DispatchMessage(&messages); return messages.wparam;

Rozdzia 8. Projektowanie aplikacji wielow tkowych 181 LRESULT CALLBACK WindowProcedure (HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam) HDC hdc; PAINTSTRUCT ps; switch (message) case WM_PAINT: hdc = BeginPaint(hwnd, &ps); MojaProcedura(hdc); EndPaint(hwnd, &ps); case WM_LBUTTONDOWN: InvalidateRect(hwnd, NULL, TRUE); case WM_DESTROY: PostQuitMessage (0); default: return DefWindowProc (hwnd, message, wparam, lparam); return 0; Kluczow cz programu, wraz z wywo ywan w kodzie obs ugi komunikatu WM_PAINT procedur MojaProcedura, umie cimy w pliku Program21.h. Zawarto procedury b d stanowi : 1. Obliczenie czasu potrzebnego do narysowania zbioru Mandelbrota i Julii za pomoc funkcji wywo ywanych w sposób sekwencyjny. a) Pobranie liczby milisekund, które up yn y od chwili uruchomienia systemu Windows i umieszczenie tej wielko ci w zmiennej m1. b) Wywo anie funkcji rysuj cej zbiór Mandelbrota. c) Wywo anie funkcji rysuj cej zbiór Julii. d) Pobranie liczby milisekund, które up yn y od chwili uruchomienia systemu Windows i umieszczenie tej wielko ci w zmiennej m2. e) Obliczenie i wy wietlenie ró nicy m2-m1. Wynik oznacza czas wykonania zada opisanych w punktach b i c. 2. Obliczenie czasu potrzebnego do narysowania zbioru Mandelbrota i Julii za pomoc funkcji wywo anych w równolegle pracuj cych w tkach. a) Pobranie liczby milisekund, które up yn y od chwili uruchomienia systemu Windows, i umieszczenie tej wielko ci w zmiennej m1. b) Utworzenie w tku wywo uj cego funkcj rysuj c zbiór Mandelbrota. c) Utworzenie w tku wywo uj cego funkcj rysuj c zbiór Julii. d) Oczekiwanie na zako czenie pracy obu w tków.

182 Cz II Dla redniozaawansowanych e) Pobranie liczby milisekund, które up yn y od chwili uruchomienia systemu Windows, i umieszczenie tej wielko ci w zmiennej m2. f) Obliczenie i wy wietlenie ró nicy m2-m1. Wynik oznacza czas wykonania zada opisanych w punktach b i c. Popatrzmy na pierwsz wersj procedury MojaProcedura. void MojaProcedura(HDC hdc) char tab[32]; DWORD pointer; DWORD m, m1, m2; //pobierz ilo milisekund od chwili uruchomienia systemu Windows //(czas startu zadania) m1 = GetTickCount(); //wykonaj zadanie numer 1 Zbior_Mandelbrota(hdc); //wykonaj zadanie numer 2 Zbior_Julii(hdc); //pobierz ilo milisekund od chwili uruchomienia systemu Windows //(czas zako czenia zadania) m2 = GetTickCount(); //wy wietl okres czasu potrzebny do wykonania zadania m = m2 - m1; TextOut(hdc, 10, 250, "Czas sekwencyjnego wykonania zada :", 35); itoa(m, tab, 10); TextOut(hdc, 300, 250, tab, strlen(tab)); //wyczy fragment okna Rectangle(hdc, 0, 0, 640, 240); //wyzeruj zmienn globaln dla w tków zmienna_stop = 0; //pobierz ilo milisekund od chwili uruchomienia systemu Windows //(czas startu zadania) m1 = GetTickCount(); //wywo aj w tek wykonuj cy zadanie numer 1 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)Z_M, hdc, 0, &pointer); //wywo aj w tek wykonuj cy zadanie numer 2 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)Z_J, hdc, 0, &pointer); //czekaj na zako czenie pracy w tków dowhile(zmienna_stop<2); //pobierz ilo milisekund od chwili uruchomienia systemu Windows //(czas zako czenia zadania) m2 = GetTickCount(); //wy wietl okres czasu potrzebny do wykonania zadania m = m2 - m1; TextOut(hdc, 10, 270, "Czas równoleg ego wykonania zada :", 34); itoa(m, tab, 10); TextOut(hdc, 300, 270, tab, strlen(tab)); Procedura realizuje kolejno przedstawione w punktach zadania. Do obliczenia czasu dzia ania cz ci programu u ywamy znanej ju funkcji GetTickCount. Taki sposób liczenia przedzia ów czasu jest ca kiem bezpieczny, gdy zwracana przez funkcj wielko

Rozdzia 8. Projektowanie aplikacji wielow tkowych 183 Rysunek 8.2. Okno aplikacji Program21 w dzia aniu (wersja pierwsza, niepoprawna) jest 32-bitowa, co oznacza, e mo emy spodziewa si zrestartowania licznika dopiero po 49 dniach nieprzerwanej pracy systemu Windows. Funkcje tworzonych w procedurze w tków maj nast puj c posta : DWORD Z_M(LPVOID param) Zbior_Mandelbrota((HDC)param); //zwi ksz wska nik zako czenia pracy zmienna_stop++; return 0; DWORD Z_J(LPVOID param) Zbior_Julii((HDC)param); //zwi ksz wska nik zako czenia pracy zmienna_stop++; return 0; Kody funkcji Zbior_Mandelbrota i Zbior_Julii na razie w naszych rozwa aniach nie s istotne. Nale y zwróci uwag na sposób zako czenia funkcji w tków, polegaj cy na inkrementacji zmiennej globalnej. zmienna_stop++; Dzi ki temu mo liwe staje si sprawdzenie zako czenia dzia ania obu w tków za pomoc p tli: dowhile(zmienna_stop<2); Mo emy przyj, e mamy pierwsz podstawow wersj aplikacji. Nie jest to jeszcze wersja optymalna. Popatrzmy na rysunek 8.2, na którym przedstawiam rezultat dzia ania programu. Na pocz tek nale y zauwa y, e narysowanie zbiorów Mandelbrota i Julii za pomoc w tków wykona o si szybciej ni zadanie analogiczne, zrealizowane za pomoc sekwencyjnego wywo ania funkcji. Dzieje si tak dlatego, gdy okresy oblicze wykorzystuj ce procesor s przerywane d ugimi okresami obci ania karty graficznej, podczas

184 Cz II Dla redniozaawansowanych których wy wietlany jest piksel o danej wspó rz dnej w odpowiednim kolorze. Istniej wi c krótkie przedzia y czasowe ma ego obci enia procesora, które mo e wykorzysta dla w asnych oblicze drugi w tek. Jednak niebezpiecze stwo pracy z w tkami polega na tym, e system mo e zarz dzi wstrzymanie pracy w tku i przekazanie sterowania drugiemu w najmniej spodziewanym momencie. Je eli obydwa w tki korzystaj w tym czasie ze wspólnych zasobów systemowych, a tak jest w przypadku operacji graficznych korzystaj cych z obiektów GDI, dane mog ulec zatraceniu, czego wynikiem s niezamalowane obszary, pokazane na rysunku 8.2. Na szcz cie, potrafimy przeciwdzia a przerwaniu pracy w tku w chwili dla nas niepo- danej (co za ulga!). Do tego celu s u y sekcja krytyczna, a dok adniej obiekty sekcji krytycznej. W celu przybli enia sensu istnienia tego rewelacyjnego rozwi zania programistycznego pos u si przyk adem. Wyobra my sobie bibliotek, w której znajduje si jeden egzemplarz starego manuskryptu. Wyobra my sobie tak e, e setka bibliofilów chce w danym momencie ów egzemplarz przeczyta. Mo e to robi na raz tylko jeden fanatyk starego pi miennictwa, natomiast reszta, obgryzaj c ze zdenerwowania palce, mo e tylko czeka, a szcz liwy chwilowy posiadacz obiektu zako czy prac. Podobn rol w pracy w tków odgrywa obiekt sekcji krytycznej: w jego posiadanie mo e w danej chwili wej tylko jeden w tek, natomiast pozosta e w tki zawieszaj prac do czasu przej cia obiektu sekcji krytycznej. Oczywi cie, czekaj tylko te w tki, w których zaznaczono konieczno wej cia w posiadanie obiektu sekcji krytycznej w celu wykonania fragmentu kodu. Praca z obiektami sekcji krytycznej jest niezwykle prosta i sk ada si z kilku punktów. Oto one. 1. Deklaracja obiektu sekcji krytycznej. Nazwa obiektu jest dowolna, mo emy zadeklarowa dowoln liczb obiektów. CRITICAL_SECTION critical_section; 2. Inicjalizacja obiektu sekcji krytycznej. InitializeCriticalSection(&critical_section); 3. Nast pne dwa podpunkty umieszczamy w funkcji w tków, s to: a) przej cie obiektu sekcji krytycznej: EnterCriticalSection(&critical_section); b) zwolnienie obiektu sekcji krytycznej: LeaveCriticalSection(&critical_section); 4. Zako czenie pracy z obiektem sekcji krytycznej powinno by zwi zane z jego usuni ciem. DeleteCriticalSection(&critical_section); Sposób u ycia obiektów sekcji krytycznej zobaczymy na przyk adzie kodu poprawionej procedury MojaProcedura i jednej z funkcji w tków Z_M. Rezultat dzia ania tak poprawionego programu przedstawiam na rysunku 8.3.

Rozdzia 8. Projektowanie aplikacji wielow tkowych 185 Rysunek 8.3. Okno aplikacji Program21 dzia aj cej z wykorzystaniem obiektu sekcji krytycznej /* Obiekt sekcji krytycznej */ CRITICAL_SECTION critical_section; void MojaProcedura(HDC hdc) char tab[32]; DWORD pointer, m, m1, m2; //pobierz ilo milisekund od chwili uruchomienia systemu Windows //(czas startu zadania) m1 = GetTickCount(); //wykonaj zadanie numer 1 Zbior_Mandelbrota(hdc); //wykonaj zadanie numer 2 Zbior_Julii(hdc); //pobierz ilo milisekund od chwili uruchomienia systemu Windows //(czas zako czenia zadania) m2 = GetTickCount(); //wy wietl okres czasu potrzebny do wykonania zadania m = m2 - m1; TextOut(hdc, 10, 250, "Czas sekwencyjnego wykonania zada :", 35); itoa(m, tab, 10); TextOut(hdc, 300, 250, tab, strlen(tab)); //wyczy fragment okna Rectangle(hdc, 0, 0, 640, 240); //wyzeruj zmienn globaln dla w tków zmienna_stop = 0; //zainicjalizuj obiekt sekcji krytycznej InitializeCriticalSection(&critical_section); //pobierz ilo milisekund od chwili uruchomienia systemu Windows //(czas startu zadania) m1 = GetTickCount(); //wywo aj w tek wykonuj cy zadanie numer 1 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)Z_M, hdc, 0, &pointer); //wywo aj w tek wykonuj cy zadanie numer 2 CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)Z_J, hdc, 0, &pointer); //czekaj na zako czenie pracy w tków dowhile(zmienna_stop<2); //pobierz ilo milisekund od chwili uruchomienia systemu Windows //(czas zako czenia zadania) m2 = GetTickCount();

186 Cz II Dla redniozaawansowanych //usu obiekt sekcji krytycznej DeleteCriticalSection(&critical_section); //wy wietl okres czasu potrzebny do wykonania zadania m = m2 - m1; TextOut(hdc, 10, 270, "Czas równoleg ego wykonania zada :", 34); itoa(m, tab, 10); TextOut(hdc, 300, 270, tab, strlen(tab)); DWORD Z_M(LPVOID param) double a_lewy = -2.0, a_prawy = 2.0; double b_gora = 1.5, b_dol = -1.5; double krokx = (a_prawy - a_lewy)/320.0; double kroky = (b_gora - b_dol)/240.0; int kolor; for(int y=0; y<240; y++) for(int x=0; x<320; x++) kolor = Kolor(a_lewy+double(x)*krokX, b_gora-double(y)*kroky); //wejd w sekcj krytyczn EnterCriticalSection(&critical_section); //ustaw piksel SetPixel((HDC)param, x, y, kolor); //opu sekcj krytyczn LeaveCriticalSection(&critical_section); //zwi ksz wska nik zako czenia pracy zmienna_stop++; return 0; Poniewa funkcje graficzne zosta y w aplikacji zamkni te w sekcje krytyczne, nie ma na wykresie obszarów b dnych. Wy wietlane przez program liczby mog si ró ni od zaprezentowanych na ilustracji, co jest wynikiem ró nej mocy obliczeniowej komputera, a nawet ilo ci procesów korzystaj cych w danej chwili z procesora. Ostatnie udoskonalenie programu b dzie zwi zane ze zmian priorytetu wykonywanych w tków. Uruchomione w aplikacji w tki musz wspó dzieli czas pracy procesora nie tylko mi dzy siebie, ale te z wywo uj c je aplikacj g ówn i innymi uruchomionymi w systemie w tkami. To wyd u a czas dzia ania w tków. Zmiana tej sytuacji jest mo liwa za pomoc zmiany priorytetu uruchomionych w tków. Do tego zadania s u y funkcja SetThreadPriority o nast puj cej deklaracji: BOOL SetThreadPriority(HANDLE uchwyt_watku, int priorytet); Parametr uchwyt_watku powinien zawiera uchwyt w tku, którego priorytet chcemy zmieni, a argumentem priorytet okre lamy warto priorytetu w tku. Zestawienie mo liwych do wyboru warto ci priorytetu przedstawiam w tabeli 8.1. W naszej aplikacji u yjemy najwy szego priorytetu THREAD_PRIORITY_HIGHEST, czego wynikiem b dzie prawie dwukrotne przyspieszenie czasu rysowania wykresów zbioru Mandelbrota i Julii. Oto gotowy plik Program21.h.

Rozdzia 8. Projektowanie aplikacji wielow tkowych 187 Tabela 8.1. Priorytety uruchamianych w tków Sta a priorytetu THREAD_PRIORITY_HIGHEST +2 THREAD_PRIORITY_ABOVE_NORMAL +1 THREAD_PRIORITY_NORMAL 0 THREAD_PRIORITY_BELOW_NORMAL -1 THREAD_PRIORITY_LOWEST -2 #include <math.h> Warto priorytetu //zmienna globalna do sprawdzenia stanu w tków int zmienna_stop; /* Deklaracja procedur */ void MojaProcedura(HDC); void Zbior_Mandelbrota(HDC); void Zbior_Julii(HDC); /* Funkcje w tków */ DWORD Z_M(LPVOID); DWORD Z_J(LPVOID); /* Obiekt sekcji krytycznej */ CRITICAL_SECTION critical_section; void MojaProcedura(HDC hdc) char tab[32]; DWORD pointer, m, m1, m2; HANDLE h; //pobierz ilo milisekund od chwili uruchomienia systemu Windows //(czas startu zadania) m1 = GetTickCount(); //wykonaj zadanie numer 1 Zbior_Mandelbrota(hdc); //wykonaj zadanie numer 2 Zbior_Julii(hdc); //pobierz ilo milisekund od chwili uruchomienia systemu Windows //(czas zako czenia zadania) m2 = GetTickCount(); //wy wietl okres czasu potrzebny do wykonania zadania m = m2 - m1; TextOut(hdc, 10, 250, "Czas sekwencyjnego wykonania zada :", 35); itoa(m, tab, 10); TextOut(hdc, 300, 250, tab, strlen(tab)); //wyczy fragment okna Rectangle(hdc, 0, 0, 640, 240); //wyzeruj zmienn globaln dla w tków zmienna_stop = 0; //zainicjalizuj obiekt sekcji krytycznej InitializeCriticalSection(&critical_section);

188 Cz II Dla redniozaawansowanych //pobierz ilo milisekund od chwili uruchomienia systemu Windows //(czas startu zadania) m1 = GetTickCount(); //wywo aj w tek wykonuj cy zadanie numer 1 h = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)Z_M, hdc, 0, &pointer); SetThreadPriority(h, THREAD_PRIORITY_HIGHEST); //wywo aj w tek wykonuj cy zadanie numer 2 h = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)Z_J, hdc, 0, &pointer); SetThreadPriority(h, THREAD_PRIORITY_HIGHEST); //czekaj na zako czenie pracy w tków dowhile(zmienna_stop<2); //pobierz ilo milisekund od chwili uruchomienia systemu Windows //(czas zako czenia zadania) m2 = GetTickCount(); //usu obiekt sekcji krytycznej DeleteCriticalSection(&critical_section); //wy wietl okres czasu potrzebny do wykonania zadania m = m2 - m1; TextOut(hdc, 10, 270, "Czas równoleg ego wykonania zada :", 34); itoa(m, tab, 10); TextOut(hdc, 300, 270, tab, strlen(tab)); int Kolor(double a0, double b0) double an, a = a0, b = b0; int k = 0; while((sqrt(a*a+b*b)<2)&&(k<256)) an = a*a - b*b + a0; b = 2.0*a*b + b0; a = an; k++; return 0xF8*k; void Zbior_Mandelbrota(HDC hdc) double a_lewy = -2.0, a_prawy = 2.0; double b_gora = 1.5, b_dol = -1.5; double krokx = (a_prawy - a_lewy)/320.0; double kroky = (b_gora - b_dol)/240.0; for(int y=0; y<240; y++) for(int x=0; x<320; x++) SetPixel(hdc, x, y, Kolor(a_lewy+double(x)*krokX, b_gora-double(y)*kroky)); int jkolor(double a0, double b0) double an, a = a0, b = b0; int k = 0; while((sqrt(a*a+b*b)<2)&&(k<256)) an = a*a - b*b + 0.373; b = 2.0*a*b + 0.133;

Rozdzia 8. Projektowanie aplikacji wielow tkowych 189 a = an; k++; return 0xF8*k; void Zbior_Julii(HDC hdc) double a_lewy = -2.0, a_prawy = 2.0; double b_gora = 1.5, b_dol = -1.5; double krokx = (a_prawy - a_lewy)/320.0; double kroky = (b_gora - b_dol)/240.0; for(int y=0; y<240; y++) for(int x=0; x<320; x++) SetPixel(hdc, 320+x, y, jkolor(a_lewy+double(x)*krokx, b_gora-double(y)*kroky)); DWORD Z_M(LPVOID param) double a_lewy = -2.0, a_prawy = 2.0; double b_gora = 1.5, b_dol = -1.5; double krokx = (a_prawy - a_lewy)/320.0; double kroky = (b_gora - b_dol)/240.0; int kolor; for(int y=0; y<240; y++) for(int x=0; x<320; x++) kolor = Kolor(a_lewy+double(x)*krokX, b_gora-double(y)*kroky); //wejd w sekcj krytyczn EnterCriticalSection(&critical_section); //zapal piksel SetPixel((HDC)param, x, y, kolor); //opu sekcj krytyczn LeaveCriticalSection(&critical_section); //zwi ksz wska nik zako czenia pracy zmienna_stop++; return 0; DWORD Z_J(LPVOID param) double a_lewy = -2.0, a_prawy = 2.0; double b_gora = 1.5, b_dol = -1.5; double krokx = (a_prawy - a_lewy)/320.0; double kroky = (b_gora - b_dol)/240.0; int kolor; for(int y=0; y<240; y++) for(int x=0; x<320; x++)

190 Cz II Dla redniozaawansowanych kolor = jkolor(a_lewy+double(x)*krokx, b_gora-double(y)*kroky); //wejd w sekcj krytyczn EnterCriticalSection(&critical_section); //ustaw piksel SetPixel((HDC)param, 320+x, y, kolor); //opu sekcj krytyczn LeaveCriticalSection(&critical_section); //zwi ksz wska nik zako czenia pracy zmienna_stop++; return 0; Rysunek 8.4. Okno aplikacji Program21 dzia aj cej z wykorzystaniem obiektu sekcji krytycznej i ustawionym najwy szym priorytetem pracy w tków Rezultat dzia ania programu przedstawiam na rysunku 8.4. 8.3. Wstrzymywanie pracy i usuwanie w tków Zapewne przynajmniej raz spotkali cie si z irytuj cym edytorem tekstowym, który doskonale wiedz c, co chcemy napisa z zadziwiaj cym uporem poprawia posta wpisywanego tekstu. Oto nadszed czas zemsty i w niniejszym podrozdziale zajmiemy si konstruowaniem podobnego, tylko jeszcze bardziej z o liwego edytora. Niezwykle pomocna przy tym b dzie umiej tno tworzenia w tków, które niczym duch b d ledzi y wpisywany przez u ytkownika tekst, dokonuj c jego modyfikacji. Znamy ju trzy czynno ci zwi zane z w tkami. 1. Utworzenie w tku. Do tego zadania s u y funkcja CreateThread. 2. Obs uga obiektów sekcji krytycznej. Wykorzystywane do tego celu funkcje to: InitializeCriticalSection, EnterCriticalSection, LeaveCriticalSection i DeleteCriticalSection. 3. Zmiana priorytetu w tku. Do tego zadania s u y funkcja SetThreadPriority.

Rozdzia 8. Projektowanie aplikacji wielow tkowych 191 Je eli chcemy chwilowo zawiesi dzia anie w tku, pos u ymy si funkcj Suspend Thread o nast puj cej sk adni: DWORD SuspendThread(HANDLE uchwyt_watku); Jedynym parametrem funkcji jest uchwyt wstrzymywanego w tku. Wznowienie pracy w tku jest mo liwe za pomoc funkcji ResumeThread: DWORD ResumeThread(HANDLE uchwyt_watku); Gdy dzia alno w tku zupe nie nam zbrzyd a, mo emy go usun za pomoc funkcji TerminateThread: BOOL TerminateThread(HANDLE uchwyt_watku, DWORD kod_wyjscia); Argument uchwyt_watku to oczywi cie uchwyt usuwanego w tku. Pos uguj c si parametrem kod_wyjscia, mo emy wys a kod zako czenia pracy w tku. Ten parametr najcz ciej nie b dzie nas interesowa i w jego miejsce b dziemy wpisywa liczb zero. Przyst pujemy do budowania aplikacji uwzgl dniaj cej nowo poznane funkcje: Sus pendthread i ResumeThread. Otwieramy nowy projekt o nazwie Program22.dev. Obszar roboczy okna podzielimy na dwie cz ci: w cz ci pierwszej umie cimy okno potomne klasy edycji, w cz ci drugiej przyciski typu BS_AUTORADIOBUTTON. Okno potomne tworzymy za pomoc funkcji CreateWindow. Okno klasy edycji wykreujemy przy u yciu nast puj cego kodu: static HWND hwndedytora = CreateWindowEx( 0, "EDIT", NULL, WS_VISIBLE WS_CHILD WS_BORDER ES_MULTILINE WS_VSCROLL WS_HSCROLL, 0, 0, 400, 200, hwnd, NULL, hinst, NULL); Nazw klasy okna potomnego podajemy jako drugi parametr funkcji CreateWindow. Natomiast styl okna zdefiniowany za pomoc sta ych: WS_VISIBLE WS_CHILD WS_BORDER ES_MULTILINE WS_VSCROLL WS_HSCROLL utworzy edytor wielowierszowy o poziomym i pionowym pasku przewijania. Podstawowe w asno ci okna klasy edycji to mo liwo edycji tekstu, jego pobierania i wstawiania tekstu nowego. Za ó my, e chcemy pobra wpisany w oknie tekst. T niezwyk ch mo emy zaspokoi na dwa sposoby: wykorzystuj c funkcj GetWindowText lub wysy aj c komunikat WM_GETTEXT. Kod programu, w którym zastosowali my funkcj GetWindowText, b dzie wygl da nast puj co: const int MaxT = 0xFFFF; char tekst[maxt]; GetWindowText(hwndEdytora, tekst, MaxT); Analogiczny efekt uzyskamy, wysy aj c komunikat WM_GETTEXT: const int MaxT = 0xFFFF; char tekst[maxt]; SendMessage(hwndEdytora, WM_GETTEXT, MaxT, (LPARAM)tekst);

192 Cz II Dla redniozaawansowanych Parametrem MaxT oznaczamy maksymaln liczb pobieranych znaków. Wys anie tekstu do okna edycji równie mo emy zrealizowa na dwa sposoby. Pierwszym z nich jest u ycie funkcji SetWindowText: SetWindowText(hwndEdytora, tekst); Drugim sposobem jest wys anie komunikatu WM_SETTEXT: SendMessage(hwndEdytora, WM_SETTEXT, 0, (LPARAM)(LPCTSTR)tekst); Do k opotliw w asno ci klasy edycji jest resetowanie pozycji karetki po ka dorazowym wys aniu tekstu do okna edycji. Do ustawienia pozycji karetki s u y funkcja SetCaretPos, jednak zgodnie z opisem Pomocy Microsoft Windows funkcja dzia a tylko dla karetek utworzonych przez okno, czyli nie dzia a dla obiektów klasy edycji. Pos u ymy si wybiegiem, a dok adniej wys aniem komunikatu EM_SETSEL. SendMessage(hwndEdytora, EM_SETSEL, poczatek, koniec); Komunikat EM_SETSEL jest wysy any w celu zaznaczenia bloku tekstu, pocz wszy od litery o indeksie poczatek i ko cu oznaczonym indeksem koniec. Po wykonaniu tego zadania karetka jest ustawiana w punkcie oznaczonym indeksem koniec. U ycie komunikatu w nast puj cy sposób: SendMessage(hwndEdytora, EM_SETSEL, x, x); jest atwym sposobem przemieszczania pozycji karetki do pozycji x w oknie edycji. Przejd my do omawiania kluczowej cz ci programu, czyli do obs ugi w tków. W chwili uruchomienia aplikacji tworzone s dwa w tki, z których aktywny mo e by tylko jeden lub aden. //uruchom pierwszy w tek sprawdzania pisowni watek1 = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)PisowniaPierwszaDuza, hwndedytora, 0, &p); //uruchom drugi w tek sprawdzania pisowni watek2 = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)PisowniaWszystkieDuze, hwndedytora, 0, &p); //wstrzymaj dzia anie w tku drugiego SuspendThread(watek2); //ustaw wska nik uruchomionego w tku flaga = 1; Zmienna globalna flaga s u y do zaznaczenia aktywno ci w tku. Prze czanie aktywno ci w tków, zgodnie z konfiguracj przycisków klasy BS_AUTORADIOBUTTON, zosta o zaimplementowane w kodzie obs ugi komunikatu WM_COMMAND. case WM_COMMAND: switch(loword(wparam)) case 1001: switch(flaga) case 2: //wstrzymaj dzia anie w tku drugiego SuspendThread(watek2);

Rozdzia 8. Projektowanie aplikacji wielow tkowych 193 case 3: //pocz tek nowego formatowania ustawiany jest //na ostatni wprowadzony znak GetWindowText(hwndEdytora, tekst, MaxT); pocz_format = strlen(tekst); //ponownie uruchom w tek pierwszy ResumeThread(watek1); flaga = 1; case 1002: switch(flaga) case 1: //wstrzymaj dzia anie w tku pierwszego SuspendThread(watek1); case 3: //pocz tek nowego formatowania ustawiany jest na ostatni wprowadzony znak GetWindowText(hwndEdytora, tekst, MaxT); pocz_format = strlen(tekst); //ponownie uruchom w tek drugi ResumeThread(watek2); flaga = 2; case 1003: switch(flaga) case 1: //wstrzymaj dzia anie w tku pierwszego SuspendThread(watek1); case 2: //wstrzymaj dzia anie w tku drugiego SuspendThread(watek2); flaga = 3; case 1010: SendMessage(hwnd, WM_DESTROY, 0, 0); Dzi ki uwzgl dnieniu zmiennej pocz_poz modyfikacje tekstu nie wywo uj zmian w tek cie wpisanym przed wybraniem kolejnego sposobu formatowania. Sta e parametru wparam odpowiadaj identyfikatorom okien potomnych klasy BS_AUTORADIOBUTTON. Aktywny w tek co sekund sprawdza wpisany tekst, generuj c zmiany zgodne z wybranym sposobem formatowania. Dzi ki zastosowanym w tkom kod pliku Program22_ main.cpp nie jest skomplikowany, w ca o ci wygl da nast puj co:

194 Cz II Dla redniozaawansowanych #include <windows.h> const int MaxT = 0xFFFF; char tekst[maxt]; /* znacznik uruchomionego w tku*/ int flaga; /* Uchwyty w tków*/ HANDLE watek1, watek2; /* wska nik pocz tku formatowania tekstu */ int pocz_format; /* Deklaracja funkcji w tku */ DWORD PisowniaPierwszaDuza(LPVOID parametr); DWORD PisowniaWszystkieDuze(LPVOID parametr); LRESULT CALLBACK WindowProcedure (HWND, UINT, WPARAM, LPARAM); char szclassname[ ] = "WindowsApp"; int WINAPI WinMain (HINSTANCE hthisinstance, HINSTANCE hprevinstance, LPSTR lpszargument, int nfunsterstil) HWND hwnd; MSG messages; WNDCLASSEX wincl; wincl.hinstance = hthisinstance; wincl.lpszclassname = szclassname; wincl.lpfnwndproc = WindowProcedure; wincl.style = CS_DBLCLKS; wincl.cbsize = sizeof (WNDCLASSEX); wincl.hicon = LoadIcon (NULL, IDI_APPLICATION); wincl.hiconsm = LoadIcon (NULL, IDI_APPLICATION); wincl.hcursor = LoadCursor (NULL, IDC_ARROW); wincl.lpszmenuname = NULL; wincl.cbclsextra = 0; wincl.cbwndextra = 0; wincl.hbrbackground = (HBRUSH) COLOR_BTNSHADOW; if (!RegisterClassEx (&wincl)) return 0; hwnd = CreateWindowEx ( 0, szclassname, "Program22", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, 410, 375, HWND_DESKTOP, NULL, hthisinstance,

Rozdzia 8. Projektowanie aplikacji wielow tkowych 195 NULL ); ShowWindow (hwnd, nfunsterstil); while (GetMessage (&messages, NULL, 0, 0)) TranslateMessage(&messages); DispatchMessage(&messages); return messages.wparam; LRESULT CALLBACK WindowProcedure (HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam) DWORD p; switch (message) case WM_CREATE: static HINSTANCE hinst = (HINSTANCE)GetWindowLong(hwnd, GWL_HINSTANCE); //utwórz okno edycji static HWND hwndedytora = CreateWindowEx( 0, "EDIT", NULL, WS_VISIBLE WS_CHILD WS_BORDER ES_MULTILINE WS_VSCROLL WS_HSCROLL, 0, 0, 400, 200, hwnd, NULL, hinst, NULL); CreateWindowEx(0, "BUTTON", "Opcje formatowania tekstu", WS_VISIBLE WS_CHILD BS_GROUPBOX, 5, 210, 220, 110, hwnd, NULL, hinst, NULL); static HWND hwndradio1 = CreateWindowEx( 0, "BUTTON", "Du e pierwsze litery", WS_VISIBLE WS_CHILD WS_GROUP BS_AUTORADIOBUTTON, 10, 240, 170, 20, hwnd, (HMENU)1001, hinst, NULL); static HWND hwndradio2 = CreateWindowEx( 0, "BUTTON", "Du e wszystkie litery", WS_VISIBLE WS_CHILD BS_AUTORADIOBUTTON, 10, 260, 170, 20, hwnd, (HMENU)1002, hinst, NULL); static HWND hwndradio3 = CreateWindowEx( 0, "BUTTON", "Bez modyfikacji", WS_VISIBLE WS_CHILD BS_AUTORADIOBUTTON, 10, 280, 170, 20, hwnd, (HMENU)1003, hinst, NULL); CreateWindowEx(0, "BUTTON", "Koniec", WS_VISIBLE WS_CHILD, 260, 230, 110, 80, hwnd, (HMENU)1010, hinst, NULL); //uruchom pierwszy w tek sprawdzania pisowni watek1 = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)PisowniaPierwszaDuza, hwndedytora, 0, &p);

196 Cz II Dla redniozaawansowanych //uruchom drugi w tek sprawdzania pisowni watek2 = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)PisowniaWszystkieDuze, hwndedytora, 0, &p); //wstrzymaj dzia anie w tku drugiego SuspendThread(watek2); //ustaw wska nik uruchomionego w tku flaga = 1; //ustaw domy ln opcj SendMessage(hwndRadio1, BM_SETCHECK, 1001, 0); //ustaw zakres zmian tekstu pocz_format = 0; case WM_COMMAND: switch(loword(wparam)) case 1001: switch(flaga) case 2: //wstrzymaj dzia anie w tku drugiego SuspendThread(watek2); case 3: //pocz tek nowego formatowania ustawiany jest na ostatni //wprowadzony znak GetWindowText(hwndEdytora, tekst, MaxT); pocz_format = strlen(tekst); //ponownie uruchom w tek pierwszy ResumeThread(watek1); flaga = 1; case 1002: switch(flaga) case 1: //wstrzymaj dzia anie w tku pierwszego SuspendThread(watek1); case 3: //pocz tek nowego formatowania ustawiany jest na ostatni //wprowadzony znak GetWindowText(hwndEdytora, tekst, MaxT); pocz_format = strlen(tekst); //ponownie uruchom w tek drugi ResumeThread(watek2); flaga = 2; case 1003: switch(flaga) case 1: //wstrzymaj dzia anie w tku pierwszego SuspendThread(watek1);

Rozdzia 8. Projektowanie aplikacji wielow tkowych 197 case 2: //wstrzymaj dzia anie w tku drugiego SuspendThread(watek2); flaga = 3; case 1010: SendMessage(hwnd, WM_DESTROY, 0, 0); case WM_DESTROY: PostQuitMessage (0); default: return DefWindowProc (hwnd, message, wparam, lparam); return 0; DWORD PisowniaPierwszaDuza(LPVOID parametr) int i, pozycja; //dzia aj a do odwo ania while(true) //pobierz tekst okna GetWindowText((HWND)parametr, tekst, MaxT); //popraw tekst i = pocz_format; while(tekst[i]) //postaw du liter, gdy jest to pierwszy znak if((i==pocz_format)&&(tekst[i]>=97&&tekst[i]<=122)) tekst[i] -= 32; //lub jest to znak po spacji, lub znaku nowej linii if(tekst[i]==32 tekst[i]=='\n') if(tekst[i+1]>=97&&tekst[i+1]<=122) tekst[i+1] -= 32; //sprawd polskie znaki if(tekst[i+1]==' ') tekst[i+1] = ' '; if(tekst[i+1]==' ') tekst[i+1] = ' '; if(tekst[i+1]==' ') tekst[i+1] = ' '; if(tekst[i+1]==' ') tekst[i+1] = ' '; if(tekst[i+1]==' ') tekst[i+1] = ' '; if(tekst[i+1]=='ó') tekst[i+1] = 'Ó'; if(tekst[i+1]==' ') tekst[i+1] = ' '; if(tekst[i+1]==' ') tekst[i+1] = ' '; if(tekst[i+1]==' ') tekst[i+1] = ' '; //zwi ksz licznik i++;

198 Cz II Dla redniozaawansowanych //wy lij poprawiony tekst do okna edycji SetWindowText((HWND)parametr, tekst); //ustaw karetk na koniec tekstu SendMessage((HWND)parametr, EM_SETSEL, i, i); //zaczekaj Sleep(1000); return 0; DWORD PisowniaWszystkieDuze(LPVOID parametr) int i, pozycja; //dzia aj a do odwo ania while(true) //pobierz tekst okna GetWindowText((HWND)parametr, tekst, MaxT); //popraw tekst i = pocz_format; while(tekst[i]) //postaw du liter w miejsce ma ej if(tekst[i]>=97&&tekst[i]<=122) tekst[i] -= 32; //sprawd polskie znaki if(tekst[i]==' ') tekst[i] = ' '; if(tekst[i]==' ') tekst[i] = ' '; if(tekst[i]==' ') tekst[i] = ' '; if(tekst[i]==' ') tekst[i] = ' '; if(tekst[i]==' ') tekst[i] = ' '; if(tekst[i]=='ó') tekst[i] = 'Ó'; if(tekst[i]==' ') tekst[i] = ' '; if(tekst[i]==' ') tekst[i] = ' '; if(tekst[i]==' ') tekst[i] = ' '; //zwi ksz licznik i++; //wy lij poprawiony tekst do okna edycji SetWindowText((HWND)parametr, tekst); //ustaw karetk na koniec tekstu SendMessage((HWND)parametr, EM_SETSEL, i, i); //zaczekaj Sleep(1000); return 0;

Rozdzia 8. Projektowanie aplikacji wielow tkowych 199 Okno g ówne dzia aj cej aplikacji prezentuj na rysunku 8.5. W celu zilustrowania dzia- ania programu wykorzysta em fragment wspania ej powie ci Arkadija i Borysa Strugackich Poniedzia ek zaczyna si w sobot 1. Rysunek 8.5. Okno g ówne aplikacji Program22 8.4. wiczenia wiczenie 13. Zaprojektuj aplikacj, w której w obszarze roboczym okna wy wietlany b dzie czas systemowy. W lewej cz ci obszaru okna czas b dzie wy wietlany w postaci cyfrowej, praw cz okna zajmie tarcza zegara o klasycznej postaci (patrz rysunek 8.6). Do obs ugi zegarów u yj dwóch w tków. Rysunek 8.6. Okno g ówne aplikacji Cwiczenie13 wiczenie 14. Uruchomione w aplikacji Program22 w tki s u y y do automatycznego poprawiania pisowni w edytorze tekstowym. Zaprojektuj podobn aplikacj dzia aj c w edytorze graficznym, w której uruchomiony w tek b dzie zamienia narysowane linie krzywe w linie proste (sposób dzia ania programu zilustrowano na rysunku 8.7). 1 Arkadij Strugacki, Borys Strugacki, Poniedzia ek zaczyna si w sobot, t um. Irena Piotrowska, Warszawa 1970, s. 17.

200 Cz II Dla redniozaawansowanych Rysunek 8.7. Ilustracja dzia ania programu Cwiczenie14