You are on page 1of 708

Delphi 7.

Kompendium programisty

Spis treci

Wstp Cz I o Rozdzia 1. Podstawy Delphi o Rozdzia 2. Jzyk Object Pascal o Rozdzia 3. Programowanie Obiektowe o Rozdzia 4. IDE Delphi1 | S t r o n a Podsumowanie czci I Cz II o Rozdzia 5. Obsuga komunikatw o Rozdzia 6. Rejestry i pliki INI o Rozdzia 7. Obsuga plikw o Rozdzia 8. Aplikacje wielowtkowe o Rozdzia 9. Multimedia o Rozdzia 10. Biblioteki DLL o Rozdzia 11. Aplikacje sieciowe o Rozdzia 12. Win API o Rozdzia 13. COM i ActiveX Podsumowanie czci II Cz III o Rozdzia 14. Komponenty VCL i CLX o Rozdzia 15. Tworzenie komponentw Podsumowanie czci III Cz IV o Rozdzia 16. Bazy danych BDE o Rozdzia 17. Bazy danych dbExpress Podsumowanie czci IV Cz V o Rozdzia 18. Delphi a Internet o Rozdzia 19. IntraWeb Podsumowanie czci V Zakoczenie Dodatek A. Zasady pisania kodu

1|Strona

Wstp
Witam Ci, Czytelniku! Ju za chwil rozpoczniesz swoj zabaw z programowaniem w Delphi. By moe czytasz t ksik z przymusu, bo takie byo polecenie Twojego szefa, a by moe jeste po prostu mody, dny wiedzy i chcesz nauczy si programowania, gdy Ci to fascynuje. Bez wzgldu na to, kim jeste, postaram si, aby lektura tej ksiki daa Ci satysfakcj, aby jak najprzyjemniej spdzi czas powicony na jej czytanie, a co najwaniejsze ? aby nauczy si pisa wasne programy. Co prawda nigdy tak si nie zdarza, aby zadowoleni byli wszyscy, lecz mam nadzieje, e tym razem uda mi si usatysfakcjonowa wikszo Czytelnikw. Na pewno masz jakie uwagi dotyczce ksiki; chciaby co w niej zmieni lub co do niej doda. Na Twoje opinie czekam pod adresem e-mail: adam@boduch.net. Zapraszam take do odwiedzenia strony http://boduch.net/ksiazki/ ? moesz tam zamieci wasne komentarze dotyczce tej ksiki. Postaram si uwzgldni Twoje uwagi w kolejnych wydaniach oraz innych publikacjach. Pytania zwizane z programowaniem w Delphi prosz zadawa wycznie na forum programistycznym http://forum.4programmers.net. Przykro mi, ale nie jestem w stanie odpowiada na Wasze e-maile z pytaniami dotyczcymi programowania! Teraz nie pozostao mi ju nic innego, jak yczy przyjemnej lektury!

2|Strona

Cz I
Pierwsza cz ksiki powicona bdzie podstawom szybkiego projektowania aplikacji. Nauczysz si korzysta z Delphi i poznasz zasad dziaania samego jzyka programowania. Cz I moe okaza si dla Ciebie najtrudniejsza! Nie chodzi mi o to, aby ju na wstpie zniechci Ci, drogi Czytelniku, do dalszej lektury ? Chc tylko zapewni, e dalej pjdzie ju z grki. Wystarczy tylko, aby zrozumia podstawowe zasady budowy i dziaania Delphi. Poznawanie kolejnych polece bdzie ju tylko kwesti czasu, a ja postaram si korygowa Twoje ewentualne bdy przez cay czas, od pierwszego do ostatniego rozdziau tej ksiki. W pierwszym rozdziale zapoznasz si ze rodowiskiem Delphi. Poznasz jego podstawowe elementy oraz zasady funkcjonowania. Kolejny rozdzia powicony bdzie jzykowi Object Pascal. Poznasz takie podstawowe pojcia, jak instrukcja warunkowa czy ptla. Cay ten rozdzia wzbogacony bdzie o przykady ilustrujce dziaanie poszczeglnych skadnikw ? po ich przeanalizowaniu powiniene zrozumie zasady dziaania tych elementw. W rozdziale trzecim zawarto omwienie samej biblioteki VCL. Ten fragment ksiki powinien by dla Ciebie o wiele ciekawszy ni poprzednie rozdziay Poznasz bardziej zaawansowane aspekty programowania wizualnego. Rozdzia koczcy t cz to rozdzia czwarty, w ktrym mowa bdzie wycznie o rodowisku Delphi. Omwi najwaniejsze opcje zwizane z Delphi oraz narzdzia, ktre przydadz Ci si podczas dalszej pracy w tym wspaniaym rodowisku.

Rozdzia 1
Podstawy Delphi
Rozdzia ten stanowi wstp do programowania. Jeeli jeste absolutnym laikiem w kwestii tworzenia programw, powiniene przeczyta poniszy fragment tekstu. Nie obawiaj si - postaram si poprowadzi Ci, drogi Czytelniku, przez t ksik krok po kroku, przechodzc w atwy do zrozumienia sposb od spraw najprostszych do skomplikowanych.

Czym jest programowanie?


Zwykemu, "szaremu" czowiekowi sowo "programowanie" kojarzy si z niezwykle trudn czynnoci projektowania programw komputerowych. W niektrych przypadkach tak jest rzeczywicie - jest to trudna operacja, ale tylko dla ludzi, ktrzy nie maj duej stycznoci z komputerami. Du rol w procesie programowania odgrywa jzyk. Programowanie nie opiera si na jednym, uniwersalnym jzyku, ktry jest zrozumiay dla kadego programisty - jzykw tych jest wiele, a eby dobrze pozna wikszo z nich, naley powici wiele czasu na ich nauk. 3|Strona

Pisanie wasnych programw opiera si na wprowadzaniu szeregu polece, dziki ktrym uruchomiony program wykona zaprogramowane czynnoci. Aby wszystko mogo zadziaa, polecenia te musz stanowi spjn cao. Najmniejszy nawet szczeg moe zdecydowa o tym, e program nie uruchomi si w ogle lub nie bdzie dziaa zgodnie z naszymi oczekiwaniami. Przy tej okazji warto, aby zapamita jedn myl: program zawsze dziaa dobrze! To, e nie dziaa zgodnie z naszymi oczekiwaniami, jest win samego projektanta. Denerwujc si i przeklinajc Delphi, miej na uwadze, e to Ty popenie bd! W takiej sytuacji przeanalizuj kod raz jeszcze w poszukiwaniu przyczyny Twojego niepowodzenia.

Przegld jzykw programowania


Wiele lat temu programista nie mia duego wyboru jzykw programowania. Wybiera mona byo midzy jzykami mao efektownymi (jak np. BASIC), lecz w miar prostymi w obsudze, a jzykami bardzo zaawansowanymi, lecz zarazem trudnymi do nauczenia si, jak np. Asembler. Kod pisany w Asemblerze jest bardzo trudny do odczytania, lecz za jego pomoc programista moe wykorzystywa dowolne instrukcje procesora - daje to bardzo wiele moliwoci. Zalet tego jzyka programowania jest niezwyka szybko; programy w nim napisane dziaaj wyjtkowo szybko, a rozmiar aplikacji wykonywalnej jest bardzo may. Rewolucj w programowaniu stanowio pojawienie si jzyka C. Jzyk ten, wraz z jego "modszym bratem" - jzykiem C++ - jest po dzi dzie najpopularniejszym jzykiem programowania. Programujc w tym jzyku, projektant ma do due pole manewru - jzyk ten jest do efektywny. Ma jednak pewn wad - jest do skomplikowany (co prawda nie w takim stopniu, jak Asembler, ale jego nauka nie jest zalecana dla osb, ktre w ogle nie miay stycznoci z programowaniem). Drugim bardzo wanym jzykiem programowania, ktry zdoby nie mniejsz popularno ni C++, jest jzyk Pascal. Stao si to za spraw Andersa Hejlsberga, ktry w 1984 roku napisa pierwsz wersj Turbo Pascala. Jzyk ten charakteryzowa si przede wszystkim prostot poczon z efektywnoci pracy; programista piszcy w Pascalu mg wykorzystywa du ilo polece, ktre usprawniay prac, a szybko dziaajcy kompilator sprawnie wskazywa popeniane przez projektanta bdy. Kod rdowy to zwyky plik tekstowy zawierajcy polecenia jzyka, ktre s interpretowane przez kompilator. Kompilator to program, ktry analizuje kod rdowy, sprawdza, czy nie zawiera on bdw, oraz ewentualnie tworzy na jego podstawie aplikacj wykonywaln. Wspczesne kompilatory s niezwykle zaawansowane i zawieraj po kilka milionw wierszy kodu rdowego - wszystko po to, aby generowana aplikacja .exe bya jak najbardziej efektywna. Obecnie jzyki C/C++ i Pascal ciesz si najwiksz popularnoci na wiecie. Nie tak dawno powstay 4|Strona

nowe jzyki - np. jzyk PHP, w ktrym mona tworzy efektywne i dynamiczne strony WWW. Jzyk PHP nie jest jzykiem kompilowanym - oznacza to, e finalna wersja programu nie jest przeksztacana w plik .exe. Plik rdowy (tzw. skrypt) jest umieszczany na serwerze; gdy uytkownik prbuje go odczyta, tzw. interpreter, zainstalowany na serwerze analizuje plik i generuje kod HTML, zrozumiay dla przegldarki internetowej.

Czym jest Delphi?


Mona powiedzie, e Delphi jest kontynuacj Turbo Pascala przeznaczon dla rodowiska Windows. W Delphi wykorzystywany jest wanie w jzyk Pascal, obecnie nazywany Object Pascalem. Naley jednak poczyni pewne zastrzeenie. Ot moe istnie wiele kompilatorw jednego jzyka programowania - przykadowo w jzyku C++ nie ograniczono si tylko do jednego kompilatora. Jzyk pozostaje taki sam, ale narzdzie generujce aplikacj wykonywaln moe by rne. Istnieje wiele kompilatorw C++ - poczwszy od darmowych, dziaajcych w systemie DOS (DJGPP), a skoczywszy na potnych, komercyjnych programach przeznaczonych dla rodowiska Windows, jak np. Microsoft Visual C++ czy Borland C++ Builder. Przypadek Delphi jest inny - Delphi to jedynie rodowisko do tworzenia aplikacji, w ktrym wykorzystywany jest jzyk Object Pascal; a zatem Delphi jest kompilatorem Pascala. Mwi o tym dlatego, e wielu programistw pyta mnie, czy istniej darmowe kompilatory Delphi. Odpowied jest jedna - nie! Gdy kto zapyta Ci, w jakim jzyku programujesz, powiniene odpowiedzie, e korzystasz z jzyka Object Pascal. Takie stwierdzenie jest prawidowe, lecz obecnie przywyko si ju mwi o "programowaniu w Delphi". Nawet sam Borland " twrca Delphi " nazywa swj produkt jzykiem Delphi (ang. Delphi language). Jeli zastanawiasz si nad wykorzystaniem Delphi jako rodowiska tworzenia aplikacji, nie zawiedziesz si! Delphi jest narzdziem typu RAD, czyli programem sucym do szybkiego tworzenia aplikacji. Projektowanie w Delphi odbywa si w prosty, przyjemny sposb, ale prostota nie oznacza, e rodowisko to nie daje duych moliwoci - przekonasz si o tym, czytajc niniejsz ksik.

Co naley umie?
Waciwie nauka Delphi nie wymaga od pocztkujcego programisty wielu umiejtnoci. Czytajc t ksik, moesz by kompletnym laikiem w dziedzinie programowania - postaram si poprowadzi Ci, drogi Czytelniku, przez proces nauki w sposb przyjemny i "bezbolesny". Jedyne, co warto zna, to jzyk angielski. Wikszo polece Delphi skada si ze sw angielskich dziki temu ich przyswojenie i zapamitanie nie powinno by trudne. Bo c dla osoby znajcej angielski oznacza polecenie CloseFile() ? - Oczywicie zamknicie pliku! Z tego typu poleceniami moesz spotka si podczas nauki Delphi - zatem jeli znasz angielski, nie powiniene mie z nimi problemw. Nazewnictwo funkcji jest jedn z wielu zalet tego rodowiska. W jzyku C/C++ postawiono nacisk na szybko pisania kodu - wiele funkcji opatrzonych jest skrtami, ktre na 5|Strona

pocztku trudno zapamita. W Delphi jest inaczej - pomimo tego, e nazwy polece s dusze, ich znaczenia atwiej si domyle.

Historia Delphi
Pierwsza wersja Delphi, przeznaczona dla platformy Windows 3.1, pojawia si na rynku w roku 1994. By to niewtpliwy przeom w dziedzinie tworzenia aplikacji. Uytkownicy otrzymali zaawansowane (jak na owe czasy) rodowisko, ktre byo przede wszystkim szybkie i efektywne. To wanie dziki Delphi wylansowano okrelenie RAD (Rapid Application Development), oznaczajce byskawiczne tworzenie aplikacji. Jak bardzo byskawiczne - Przekonasz si o tym w dalszej czci ksiki, kiedy przejdziemy ju do pisania wasnych aplikacji. Rok pniej firma Borland wydaa kolejn wersj swojego produktu, tym razem przeznaczon dla nowej, 32-bitowej platformy Windows 95. W kolejnych wersjach Delphi wprowadzono wiele usprawnie i udoskonale. Niewtpliwie istotn zmian w Delphi 6 byo przeamanie bariery dotyczcej platformy systemowej - od wersji 6 moliwe jest tworzenie aplikacji dziaajcych take w systemie Linux. Jakie zmiany czekaj uytkownikw wersji 7 ?- Mona si tego dowiedzie z czwartego rozdziau niniejszej ksiki.

Proces instalacji
Proces instalacji Delphi jest nieco kopotliwy - naley bowiem zarejestrowa si na internetowej stronie firmy Borland. Dotyczy to tylko wersji Trial, ktra dziaa jedynie przez 30 dni od daty instalacji. Borland Delphi 7 Studio Enterprise w wersji Trial moesz znale na pycie CD-ROM doczonej do tej ksiki lub na stronie producenta: http://www.borland.pl. Instalacja wymaga - jak ju napisaem - rejestracji na stronie firmy Borland. Naley wypeni ankiet i poda poprawny adres e-mail, na ktry przesany zostanie klucz rejestracyjny umoliwiajcy uruchomienie programu. Klucz zawarty bdzie w pliku przesanym jako zacznik wiadomoci e-mail plik ten naley umieci w katalogu C:\Windows. Po instalacji w menu Start powinien znajdowa si skrt do programu Delphi, umoliwiajcy uruchomienie aplikacji.

Korzystanie z polskich liter


Domylnie Delphi nie umoliwia korzystania z polskich liter - jest to do uciliwe, jeeli chcemy tworzy programy przeznaczone dla polskich odbiorcw. Na szczcie problem ten mona w do 6|Strona

prosty sposb rozwiza - wystarczy nieco zmodyfikowa rejestr Windows. Poniej przedstawiem czynnoci, ktrych wykonanie umoliwi korzystanie z polskich liter w rodowisku Delphi. 1. Z menu Start wybierz pozycj Uruchom. 2. W oknie Uruchom wpisz regedit - spowoduje to otwarcie Edytora Rejestru Windows. 3. W Edytorze Rejestru odszukaj klucz HKEY_CURRENT_USER\SOFTWARE\Borland\Delphi\7.0\Editor\Options. 4. Z menu Edycja wybierz Nowy/Warto cigu. 5. Po prawej stronie pojawi si nowa warto - nadaj jej nazw NoCtrlAltKeys. 6. Kliknij dwukrotnie nazw tej nowej wartoci i wpisz w oknie cyfr 1.

Po dokonaniu tych czynnoci moesz zamkn Edytor Rejestru i swobodnie korzysta z polskich liter. Czynnoci przedstawione powyej zatwierdzone zostan dopiero po ponownym uruchomieniu Delphi. Jeeli ustawie w rejestrze dokonywae w trakcie pracy programu Delphi, zamknij go i uruchom ponownie.

Wygld Delphi
Na cay wygld Delphi skada si kilka okien i kontrolek. Nieraz moesz spotka si z okreleniem zintegrowane rodowisko IDE. Par najbliszych podpunktw zostao powiconych omwieniu podstawowych elementw IDE. Szczegy IDE zostay omwione w rozdziale czwartym. Na rysunku 1.1 przedstawiony jest wygld Delphi zaraz po uruchomieniu programu. Jak widzisz, dostpne jest wiele okien, przyciskw i paskw menu, ktre mog wywoa szok u osoby, ktra po raz pierwszy widzi Delphi.

7|Strona

Rysunek 1.1. Podstawowe elementy zintegrowanego rodowiska Delphi 7

Okno gwne
Okno gwne przedstawione zostao na rysunku 1.2.

Rysunek 1.2. Okno gwne Delphi Jest to swoiste centrum dowodzenia - znajduj si w nim najwaniejsze paski narzdziowe oraz typowe dla aplikacji Windows paski menu.

Menu Jak prawie kada aplikacja Windows, take Delphi posiada menu gwne. W menu tym znajduj si podstawowe opcje, jak otwarcie, zapis i zamknicie projektu. Oprcz takich typowych opcji znajduj si tu take polecenia zwizane wycznie z Delphi, czyli kompilacja projektu, narzdzia manipulowania nim i wiele innych przyciskw.

8|Strona

Paleta komponentw Niezwykle wan rzecz w Delphi s komponenty, obsugiwane za pomoc palety komponentw. Komponenty to elementy, dziki ktrym termin RAD nabiera rzeczywistego znaczenia. S to "klocki", za pomoc ktrych ju w pocztkowej fazie projektu moemy stworzy interfejs aplikacji. Komponenty s tym, co przycigno do Delphi wielu programistw. Programowanie w Delphi nie opiera si tylko na wpisywaniu polece - wykonujc proste czynnoci, moemy w naszym programie umieci przyciski, kontrolki edycyjne, menu i inne typowe obiekty Windows. Wszystko to bez wprowadzania nawet jednego wiersza kodu! Paleta komponentw (rysunek 1.3) to okno podzielone na szereg zakadek, uporzdkowanych tematycznie. Zapewne wielu z tych komponentw nie bdziesz nigdy uywa, lecz podczas przedstawiania przykadowych programw postaram si zaprezentowa Ci zastosowanie wikszoci z nich.

Rysunek 1.3. Okno palety komponentw

Paski narzdziowe W oknie gwnym Delphi znajduje si sze paskw narzdziowych (jeden pasek jest ukryty), ktre swobodnie mona przeciga po ekranie (rysunek 1.4).

Rysunek 1.4. Wszystkie paski narzdziowe

9|Strona

Wszystkie dostpne na tych paskach przyciski s skrtami do polece zawartych w menu Delphi. Dziki temu atwiej jest do nich dotrze - wystarczy jedno kliknicie zamiast przedzierania si przez gszcz pozycji menu. Ze wszystkimi pozycjami w paskach skojarzone s podpowiedzi (ang. tooltips). Wystarczy umieci kursor myszy nad ikon, aby ujrze okienko z informacj o danej funkcji oraz przypisanym jej skrcie klawiaturowym. Projektant moe decydowa o wywietleniu konkretnych paskw narzdziowych oraz o "dymkach" podpowiedzi. Wystarczy w tym celu wybra z menu View pozycje Toolbars/Customize.

Projektant formularzy
Centralnie pooone okno nazywane jest Projektantem formularzy. To szare okienko z naoon siatk jest w rzeczywistoci podgldem naszego programu. Taka jest zasada projektowania w Delphi: podczas pracy moemy cay czas obserwowa, jak nasza aplikacja bdzie wygldaa podczas dziaania. Po uruchomieniu aplikacji z Projektanta formularzy zniknie siateczka, ktra znajduje si tam tylko po to, aby uatwi ustalanie pooenia komponentw. W dalszej czci tej ksiki okno Projektanta formularzy bd nazywa po prostu formularzem, lub jak to si zwyko mwi potocznie - form.

Inspektor obiektw
Inspektor obiektw zosta przedstawiony na rysunku 1.5.

Rysunek 1.5. Inspektor obiektw 10 | S t r o n a

Gwny czon Inspektora obiektw stanowi lista rozwijalna - jest to lista wszystkich obiektw (komponentw) umieszczonych w obszarze formularza. Po wybraniu elementu z tej listy w oknie Inspektora obiektw pojawi si rne pozycje, okrelajce zachowanie oraz wygld danego komponentu lub nawet caego formularza! Inspektor obiektw dzieli si na dwie zakadki - Properties (ang. waciwoci) oraz Events (ang. zdarzenia).

Zakadka Properties Po lewej stronie zakadki Properties znajduj si nazwy tzw. waciwoci, ktrym sami moemy nada warto, wpisujc j po prawej stronie. Waciwo okrela sposb zachowania si oraz wygldu komponentu. Przykadowo waciwo Color okrela kolor danego obiektu. Przeprowadmy mae wiczenie. Na rysunku 1.5 przedstawiony jest Inspektor obiektw, a na nim zostaa zaznaczona waciwo Caption. Zaznaczenie danej waciwoci odbywa si poprzez umieszczenie kursora myszy nad nazw oraz pojedyncze kliknicie. Wwczas pole nazwy danej waciwoci zmieni wygld na "wklsy", natomiast prawa cz waciwoci stanie si aktywna. Zamiast standardowego napisu Form1 wpisz w tym miejscu Moja pierwsza aplikacja. Zwr uwag, e zmiana wartoci zostaje odzwierciedlona take w wygldzie formularza - napis na pasku tytuowym formularza zmieni si zgodnie z tym, co wpisalimy w Inspektorze obiektw (rysunek 1.6)!

Rysunek 1.6. Napis na pasku tytuowym formularza W tym momencie poznae znaczenie jednej waciwoci - Caption. Od tej pory bdziesz ju wiedzia, e ta waciwo okrela tytu danej kontrolki. Zakadka Events ~~~~~~~~~~~~ Zdarzenia to cakiem inna sprawa. Po zaznaczeniu tej zakadki rwnie w Inspektorze obiektw pojawi si lista pl - te pola to zdarzenia. Zdarzenia okrelaj sposb reakcji poszczeglnych komponentw na zaistniae warunki (kliknicie danego obiektu, poruszanie kursorem myszy). Zdarzeniami zajmiemy si w dalszej czci tej ksiki, gdy wymagaj one wpisywania kodu rdowego, a nie chc, aby teraz zawraca sobie tym gow. 11 | S t r o n a

Drzewo obiektw
Angielska nazwa Drzewa obiektw to Object TreeView. Okno to przedstawione jest na rysunku 1.7, a na stae wpisao si w "krajobraz" Delphi dopiero w wersji 6.

Rysunek 1.7. Drzewo obiektw Drzewo obiektw pozwala na szybkie przeczanie si midzy obiektami znajdujcymi si w formularzu. Obiekty te przedstawione s w formie graficznej - po ich zaznaczeniu w Inspektorze obiektw pojawiaj si zdarzenia i waciwoci danej pozycji. Dla mnie okno drzewa obiektw jest nieprzydatne i zazwyczaj je wyczam. Zamiast tego rozcigam Inspektora obiektw na ca wysoko okna, tak aby lista wszystkich waciwoci widoczna bya bez koniecznoci przewijania okna. Ukrycie drzewa obiektw jest moliwe poprzez zamknicie tego okna w taki sposb, w jaki zamyka si kade okno systemu Windows. Moliwe jest take kliknicie prawym przyciskiem myszy w obszarze tego okna i wybranie pozycji Hide.

Edytor kodu
Z menu View wybierz Toggle Form/Unit. Dziki temu poleceniu mona przecza si midzy projektantem formularzy a Edytorem kodu (rysunek 1.8).

12 | S t r o n a

Rysunek 1.8. Edytor kodu Edytor kodu jest miejscem, w ktrym dokonuje si faktycznego wpisywania szeregu polece, ktre maj na celu dziaanie programu zgodnie z naszymi oczekiwaniami. Cz kodu zostanie wstawiona do edytora automatycznie - cz bdziesz musia wpisa rcznie sam. Delphi to wspaniae rodowisko, lecz programowanie nie opiera si jedynie na budowaniu aplikacji. Schemat kolorowania skadni, jaki oferuje Delphi, nie jest jedyn dostpn moliwoci. Ustawienie wasnego schematu wymaga wybrania polecenia Tools/Editor Options i zaznaczenia zakadki Color. Staraj si zapamitywa skrty klawiaturowe! Dziki temu moesz zaoszczdzi wiele czasu. Powiniene zna przynajmniej te podstawowe, jak np. F12 - powoduje przeczenie si pomidzy Edytorem kodu a Projektantem formularzy. Eksplorator kodu ~~~~~~~~~~~~ Po lewej stronie edytora kodu znajduje si okienko o nazwie Eksplorator kodu. Okno to przedstawia w sposb graficzny obiekty i moduy wstawione do naszej aplikacji. Na razie nie zastanawiaj si nad tym - zrozumienie obiektw wymaga poznania przynajmniej podstaw Object Pascala. Tak samo, jak w przypadku okna Drzewa obiektw, okno Eksploratora kodu mam zawsze ukryte. Dziki temu dysponuj lepszym podgldem kodu zawartego w Edytorze kodu.

13 | S t r o n a

Diagramy W Edytorze kodu na samym dole znajduj si dwie zakadki - Code oraz Diagram. Przeczenie si na zakadk Diagram daje moliwo tworzenia diagramw - graficznego przedstawienia zwizkw pomidzy dwoma obiektami. Szczegowo zakadk Diagram przedstawi podczas omawiania IDE Delphi w rozdziale czwartym.

Okno komunikatw Domylnie okno komunikatw pozostaje zawsze ukryte. Podczas prby kompilacji programu okno to moe zawiera informacj o bdach i ostrzeeniach (jeeli takowe wystpi). Okno komunikatw moesz wywoa "rcznie", klikajc prawym przyciskiem myszy w obszarze Edytora kodu i wybierajc pozycj Message View. Okno komunikatw przedstawione jest na rysunku 1.9.

Rysunek 1.9. Okno komunikatw W wypadku wystpienia bdu w kodzie dwukrotne kliknicie danego bdu spowoduje przejcie do wiersza kodu, w ktrym zosta znaleziony bd. Od wersji 7 okno Message View moe by podzielone na kilka zakadek.

Pierwszy projekt
Chyba nadszed ju czas na stworzenie jakiego projektu w Delphi. Tradycj stao si to, e kady pierwszy program napisany w danym jzyku programowania wywietla napis Hello World! i nic wicej! Ja take nie zamierzam odbiega od tej tradycji - myl, e to dobre wiczenie jak na pierwszy raz.

Tworzenie projektu
Czynnoci zwizane z tworzeniem projektu opisz krok po kroku - nie powiniene mie trudnoci ze stworzeniem takiej aplikacji. 1. Z menu File wybierz pozycje New/New Application - spowoduje to otwarcie nowego, "czystego" projektu. 2. Projektant formularzy moe by dowolnie przemieszczany lub rozcigany, tak jak standardowe okno systemu Windows. Zmniejsz wic jego rozmiar, chwytajc mysz krawdzie okna i przecigajc je (rysunek 1.10). 14 | S t r o n a

Rysunek 1.10. Zmniejszanie rozmiarw okna formularza 1. Kolejnym krokiem bdzie umieszczenie na formularzu obiektu (komponentu). Odszukaj na zakadce Standard palety komponentw komponent Label (etykieta). Ikona tego komponentu oznaczona jest literk A (rysunek 1.11).

Rysunek 1.11. Ikona oznaczajca komponent Label 1. Kliknij t ikon - przycisk zostanie "wcinity". 2. Umie kursor myszy nad dowolnym miejscem projektanta formularza i kliknij. W tym momencie na formularzu powinien zosta umieszczony komponent Label.

Kady komponent Delphi moesz dowolnie przemieszcza lub rozciga. Realizuje si to w prosty sposb - poprzez chwytanie komponentu i przeciganie go po formularzu. Majc zaznaczony komponent Label, odszukaj w Inspektorze obiektw waciwo Caption i zmie jej warto na Hello World (omawialimy to ju w podpunkcie "Zakadka Properties"). Po tej zmianie tekst w etykiecie powinien zosta zmieniony. To waciwie wszystko - projekt jest ju ukoczony. Myl, e na pierwsz lekcj to wystarczy.

Uruchamianie programu
Jak ju wspominaem wczeniej w tym rozdziale, podczas projektowania aplikacji moemy podglda jej ostateczn posta, jak bdzie miaa podczas dziaania. Spjrz na Projektanta formularzy - tak 15 | S t r o n a

bdzie wyglda Twj program zaraz po uruchomieniu! Jedyn rnic jest to, e zniknie pomocnicza siateczka. Uruchomienie projektu nastpuje po wybraniu z menu Run polecenia Run lub po naciniciu przycisku na pasku narzdziowym. Ja radz Ci jednak zapamita skrt klawiaturowy - F9. Nacinicie tego przycisku spowoduje skompilowanie, a nastpnie uruchomienie danego projektu; podczas dziaania programu Inspektor obiektw zostaje schowany.

Zamykanie projektu
Jeeli ju napatrzye si na swoj pierwsz aplikacj, moesz j zamkn. Zamkn program moesz tak samo, jak kad aplikacj Windows - poprzez kliknicie ikony krzyyka w prawym grnym rogu okna. Innym sposobem na zamknicie programu jest wybranie z menu Run polecenia Program Reset (Ctrl+F2). Polecenie Program Reset przydaje si bardzo czsto w przypadku, gdy program nie odpowiada albo "si zawiesi" (wykonuje stale te same czynnoci, co powoduje, e nigdy nie zostanie zakoczony taka operacja nazywa si zaptleniem).

Zapisywanie projektu ------------------------Moja rada jest taka: zapisuj kady projekt w osobnym katalogu. Delphi nie generuje jednego pliku projektu - jest ich zawsze kilka. Gdyby przechowywa kilka projektw w jednym katalogu, atwo mona by si pogubi. Proponuj stworzenie na dysku C: katalogu Projekty, w ktrym bdziesz dodawa w miar potrzeby kolejne podkatalogi. Z menu File wybierz polecenie Save All. Dziki temu Delphi zapisze cay projekt. Na samym pocztku bdziesz musia poda nazw pliku formularza - wpisz MainFrm (Delphi automatycznie doda rozszerzenie *.pas). Nastpnie musisz poda nazw projektu - wpisz np. HelloWorld. Nazwa projektu nie moe zawiera spacji lub rozpoczyna si od cyfry. Ta sama uwaga dotyczy nazwy dla pliku formularza.

Otwieranie projektu
Musisz rozrni pewne pojcia, jak nazwa projektu oraz nazwa formularza. Plik projektu Delphi zawsze oznaczony jest rozszerzeniem *.dpr; std chcc otworzy cay projekt w odpowiednim oknie, musisz wybra wanie plik DPR. Moesz take otworzy formularz (rozszerzenie *.pas), ale w tym wypadku bdziesz go mg jedynie 16 | S t r o n a

edytowa - Delphi nie pozwoli Ci na uruchomienie projektu, do ktrego naley ten formularz. Przeprowadmy mae wiczenie. Najpierw z menu File wybierz polecenie Close All. Spowoduje ono zamknicie caego projektu; zamknite zostanie rwnie okno Edytora kodu. Jeeli Twj projekt nie zosta wczeniej zapisany, Delphi zapyta Ci, czy nie chcesz go zapisa. Teraz chcc ponownie otworzy projekt, musisz z menu File wybra polecenie Open Project lub klikn odpowiedni ikon na pasku narzdziowym. Wybierz nazw pliku do otwarcia HelloWorld.dpr - i kliknij przycisk Otwrz (rysunek 1.12).

Rysunek 1.12. Otwieranie zapisanego projektu Rezultatem tej operacji bdzie otwarcie projektu, a wraz z nim otwarcie formularza, nad ktrym pracowalimy wczeniej.

Kompilacja projektu
Aby sprawdzi, czy zmiany wanie wprowadzone nie zawieraj bdw, nie trzeba uruchamia projektu - atwiej go po prostu skompilowa. Delphi wwczas sprawdzi kod rdowy i - w przypadku, gdyby zawiera on bdy - wywietli je w oknie komunikatw. Kompilacja projektu nastpuje poprzez wybranie z menu Project polecenia Compile (Ctrl+F9).

17 | S t r o n a

Pliki projektu
Po kompilacji otwartego programu moesz przekona si, e w katalogu, w ktrym umieszczony jest projekt, znajduje si cakiem sporo plikw. Nie wszystkie te pliki s potrzebne. Oznacza to, e moesz je usun, a i tak nic si nie stanie - Delphi odtworzy je przy kolejnej prbie kompilacji projektu. Poniej przedstawiam pliki, ktre mog znale si w katalogu z aplikacj.

.pas - pliki rdowe; w nich znajduj si kody rdowe formularzy i moduw (o moduach opowiem w rozdziale drugim). .dfm - jest to plik formularza; zawiera informacje dotyczce komponentw umieszczonych na formularzu (dane o ich pooeniu, nazwie itp.). .dcu - skompilowany plik *.pas; plik ten nie jest potrzebny - po kolejnej prbie kompilacji Delphi odtworzy go na podstawie kodw rdowych. .dpr - gwny plik formularza. .cfg - konkretne ustawienia kompilatora; mog by rne dla kadego projektu, std w przypadku, gdy niemoliwe bdzie odnalezienie tego pliku, zastosowane zostan ustawienia domylne. .dof - dodatkowe opcje projektu (plik zawiera np. informacje o wersji programu itp.). .res - tzw. zasoby. O zasobach bdzie mowa w dalszej czci ksiki. Plik ten zawiera np. ikon, ktra symbolizuje plik *.exe. .ddp - plik diagramu - zawiera informacje o stworzonych przez nas diagramach.

W domylnych ustawieniach Delphi okrelone jest automatyczne tworzenie kopii zapasowych. Pliki takie maj tak sam zawarto jak ich pierwowzory - jedyn rnic jest obecno znaku tyldy (~) w rozszerzeniu. Jeeli chcesz wyczy opcj tworzenia kopii zapasowych, z menu Tools wybierz polecenie Editor Options. Nastpnie kliknij zakadk Display i usu zaznaczenie pozycji Create Backup File.

Podsumowanie
W rozdziale tym omwiem podstawy Delphi - jeeli jeste kompletnym laikiem w dziedzinie programowania, przeczytanie go jest konieczna do zrozumienia zasad dziaania Delphi. Nauczye si, jak umieszcza na formularzu komponenty, poznae take znaczenie waciwoci Caption. Od nastpnego rozdziau czeka Ci wiele pracy - przedstawi w nim elementy jzyka Object Pascal. S to podstawowe informacje, ktre powiniene pozna, aby mc programowa w Delphi. Zaczniki:

Listingi_1.zip (1.71 kB)

18 | S t r o n a

Rozdzia 2
Jzyk Object Pascal
Rozdzia ten stanowi wstp do programowania w Delphi. Poznanie zasad dziaania jzyka Object Pascal jest niezbdnym warunkiem kontynuowania nauki rodowiska Delphi. Wszystkie informacje postaram si przekaza dokadnie, przedstawiajc je krok po kroku. Zaczn oczywicie od spraw podstawowych.

Plik rdowy projektu


Z menu File wybierz polecenie New/Application, co spowoduje utworzenie nowego projektu powiniene pamita to z poprzedniego rozdziau. W rozdziale tym porzucimy na chwil projektowanie obiektowe (wizualne), nie bdzie tu wic przykadw wykorzystania komponentw tym zajmiemy si w rozdziale trzecim. Z menu File wybierz polecenie Close zostaniesz zapytany, czy nie chcesz zapisa zmian w pliku Unit1.pas. Kliknij przycisk No nie chcemy zapisywa pliku formularza. W tym momencie Edytor kodu powinien zosta zamknity. Z menu Project wybierz View Source. Polecenie to spowoduje wywietlenie w Edytorze kodu zawartoci pliku gwnego *.dpr. Zapisz projekt pod nazw dprFile. Zauwa, e tym razem nie zostae zapytany o nazw formularza, gdy wczeniej zamknlimy go. Zawarto pliku DPR przedstawiona jest w listingu 2.1. Listing 2.1. Zawarto pliku DPR program dprFile; uses Forms; {$R *.res} begin Application.Initialize; Application.Run; end. Kod przedstawiony powyej jest generowany automatycznie przez Delphi. Nie przejmuj si nim na razie nie wszystko bdzie nam potrzebne.

19 | S t r o n a

Najprostszy program
Rozoymy zawarto pliku gwnego na czci, dziki czemu bdziesz mia moliwo dowiedzenia si, jakie funkcje penione s przez konkretne elementy kodu rdowego. Kod rdowy najprostszego do napisania programu przedstawiony jest poniej: end.

To nie art! Najprostszy program skada si wanie z instrukcji end, z kropk na kocu. Moesz to sprawdzi nacinij klawisz F9, uruchamiajc w ten sposb program. Oczywicie adne polecenia oprcz end nie s wpisane, dlatego program zaraz po uruchomieniu zostanie zamknity.

Podstawowa skadnia
Kod rdowy musi skada si z okrelonych polece, zakoczonych okrelonymi znakami. Nie mona pozostawi w kodzie adnego baaganu nawet pominicie jednego znaku czy zwyka literwka mog spowodowa niemoliwo uruchomienia programu. Na szczcie Delphi dysponuje na tyle dobrym kompilatorem, e miejsce bdu zostanie wskazane, a problem opisany. Przykadowo brak sowa kluczowego end przy prbie kompilacji spowoduje wskazanie bdu: [Error] dprMin.dpr(3): Declaration expected but end of file found. Zapamitaj pierwsz zasad: program musi by zawsze zakoczony poleceniem end. (kropka na kocu!).

Wielko liter
W jzyku Object Pascal w odrnieniu od C++ czy Javy wielko liter nie jest istotna. Dla kompilatora nie jest istotne, czy nazwa funkcji bdzie pisana w taki czy inny sposb. Na przykad polecenie ShowMessage bdzie znaczyo to samo, co showmessage. Mona je take zapisywa w inny sposb:
showMessage showMEssaGe itd.... Nie jest zalecane pisanie kodu z wykorzystaniem jedynie maych liter - np. showmessage. Wielu pocztkujcych programistw, zafascynowanych nauk Delphi, nie pamita o sposobie pisania kodu projektanci ci nie stosuj wci w kodzie, a wszystkie polecenia pisz maymi literami. Uwierz mi, e wanie po sposobie pisania kodu mona rozpozna pocztkujcego programist ci bardziej zaawansowani przyjli takie zasady tworzenia kodu, aby by on bardziej przejrzysty. Z moj propozycj pisania kodu moesz zapozna si w dodatku A.

20 | S t r o n a

Pamitaj o redniku!
Zapamitaj, e kada instrukcja w Delphi musi by zakoczona znakiem rednika (;). Jest to informacja dla kompilatora, e w tym miejscu koczy si jedna instrukcja. Znak rednika jako symbol zakoczenia instrukcji obowizuje w wikszoci jzykw programowania (Java, C/C++, Delphi, PHP). Oczywicie istniej pewne wyjtki od tej reguy. Na samym pocztku tego rozdziau zaprezentowaem Ci najprostszy program, ktry zakoczony by znakiem kropki, a nie rednika!

Bloki begin i end


Waciwy kod programu zawsze wpisywany jest pomidzy instrukcje begin i end. Sowo end moe oznacza zarwno zakoczenie jakiego bloku instrukcji, jak i zakoczenie programu. W pierwszym przypadku na kocu tego sowa stawiamy znak rednika, a w drugim przypadku znak kropki. Program podczas uruchamiania zawsze czyta instrukcje rozpoczynajce si od sowa begin jest to jakby pocztek programu i waciwych polece, ktre maj by wykonane po starcie aplikacji. Pamitaj, aby ilo instrukcji begin bya rwna iloci instrukcji end w przeciwnym razie kompilator wywietli bd: [Error] dprMin.dpr(9): 'END' expected but end of file found. Taki kod jest jak najbardziej prawidowy: begin begin

begin end; end; end. Natomiast brak jednego ze sw end spowoduje wyej wspomniany bd. Jeeli natomiast zabraknie jednego ze sw begin, Delphi wywietli bd: [Error] dprMin.dpr(10): '.' expected but ';' found.

21 | S t r o n a

Dyrektywa program
Typowy program powinien skada si z gwnej dyrektywy program oraz sw begin i end. Co prawda najprostszy program to jedynie sowo end, ale w prawidowo zaprojektowanej aplikacji powinien znajdowa si take nagwek program, identyfikujcy jej nazw. Po utworzeniu projektu dyrektywa program zawiera jego nazw. Przykadowo jeeli plik gwny projektu nosi nazw project.dpr, to pierwszy wiersz owego pliku wyglda tak: program project;

Dyrektywa powinna by oczywicie zakoczona znakiem rednika.

Komentarze
Komentarze s chyba najprostszym elementem kadego jzyka programowania. Jest to blok tekstu, ktry nie jest interpretowany przez kompilator. W komentarzach moesz zawrze swoje przemylenia oraz uwagi dotyczce kodu rdowego. program project; begin { to jest komentarz } end.

Typowy komentarz Delphi zawarty jest pomidzy znakami {}. W edytorze kodu komentarz jest wyrniony kursyw i kolorem ciemnoniebieskim. Istnieje kilka typw komentarzy np. komentarz jednowierszowy: // to jest komentarz jednowierszowy

Wiele osb ten rodzaj komentarzy nazywa komentarzami w stylu C, gdy zosta on zapoyczony z jzyka C. Jak sama nazwa wskazuje, komentarzem jest tylko jeden wiersz kodu rdowego: program project; begin // tu jest komentarz a tu ju nie ma komentarza end. 22 | S t r o n a

Drugi wiersz w bloku begin nie jest komentarzem podczas kompilacji Delphi wskae bd. Istnieje jeszcze jeden typ komentarzy najrzadziej uywany przez programistw: program project; begin (* komentowany kod *) end.

Zalet tego typu komentarza jest to, e moe on w sobie zawiera rwnie znaki {. program project; begin (* komentarz... { jaki tekst } *) end.

Jak widzisz, taki sposb pozwala na umieszczanie komentarzy w komentarzu, ale zapewne rzadko bdziesz z niego korzysta.

Zmienne
Czym byby program, ktry nie mgby zapisywa danych do pamici komputera Praktycznie kady program podczas dziaania korzysta z pamici, aby przechowa rne dane, potrzebne do dalszego jego dziaania. Zmienne to obszar w pamici komputera, ktry suy do przechowywania danych tymczasowych (obecnych w pamici do czasu wyczenia programu), majcych posta liczb, tekstu itp.

23 | S t r o n a

Deklaracja zmiennych
Przed przydzieleniem danych do pamici zmienn naley zadeklarowa w kodzie programu. Deklaracja zmiennej powinna by umieszczona przed blokiem begin. Przykadowa deklaracja moe wyglda tak: program varApp; var Zmienna : String; begin end.

W razie potrzeby zadeklarowania zmiennej konieczne jest zastosowanie sowa kluczowego var (skrt od sowa variable zmienna). Stanowi to informacj dla kompilatora, e po tym sowie kluczowym zostanie umieszczona deklaracja zmiennych. Zmienna zawsze musi mie nazw! Dziki tej nazwie moemy atwo odwoa si do poszczeglnych danych zapisanych w pamici. Pierwszym czonem deklaracji zmiennej musi by unikalna nazwa (nie mog istnie dwie takie same zmienne w programie). Po znaku dwukropka naley wpisa typ zmiennej (o typach zmiennych powiem pniej). Z punktu widzenia kompilatora nie ma znaczenia, w jaki sposb zapiszesz (zadeklarujesz) zmienn moe wic odby si to tak: var zmienna:string;

lub tak: var zmienna: string;

Dla zachowania przejrzystoci kodu zalecane jest jednak stosowanie deklaracji w formie przedstawionej w pierwszym przykadzie.

24 | S t r o n a

Typy zmiennych
Typy zmiennych okrelaj rodzaj danych, ktry bdzie zapisywany w pamici. W poprzednim podpunkcie podczas deklarowania zmiennej skorzystaem z typu String. Ten typ danych suy do przechowywania tekstu i tylko tekstu! Tak wic chcc w pamici komputera umieci np. liczb, naley skorzysta z innego typu zmiennej. Typy zmiennych przedstawiem w tabeli 2.1. Oczywicie tych typw jest wicej, ale nie musisz zna ich wszystkich wystarcz te podstawowe. Tabela 2.1. Typy zmiennych w Object Pascalu Nazwa Integer Int64 SmallInt ShortInt Byte Word Opis -2 147 483 648 2 147 483 647 -263 263 - 1 -32 768 32 767 -128 127 0 255 0 65 535

LongWord 0 4 294 967 295 Char Boolean pojedynczy znak TRUE lub FALSE

ShortString 255 znakw AnsiString 231 znakw String 231 znakw

Extended 3,6 10-4951 1,1 104932 Double Single Currency 5,0 10-324 1,7 10308 1,5 10-45 3,4 1038 -922 337 203 685 477,5808 922 337 203 685 477,5807

Niektre z tych typw su do przechowywania tekstu, inne do przechowywania liczb. Rni je pojemno. Przykadowo chcc zapisa w pamici jak du liczb, nie skorzystasz z typu Byte, poniewa do tego typu mog by przypisywane jedynie wartoci z zakresu od 0 do 255. Moesz za to skorzysta z typu Int64. 25 | S t r o n a

Oprcz, jak to wczeniej nazwaem, pojemnoci powysze typy danych rni take ilo zajmowanej pamici operacyjnej. Przykadowo typ Byte zera jedynie 1 bajt pamici, a typ Int64 8 bajtw. Moesz pomyle, e to niedua rnica, ale jeli zmiennych 8 bajtowych jest kilkadziesit (kilkaset?) Jest to zwyke marnowanie pamici! Podczas czytania tej ksiki oraz podczas przegldania rnych kodw rdowych moesz zauway, e dla typw liczbowych programici czsto stosuj zmienn Integer. Jest to jakby uniwersalny typ zmiennej liczbowej, gdy jej zakres jest w miar duy, a nie wykorzystuje ona a tak duo pamici.

Deklaracja kilku zmiennych


Po wpisaniu sowa kluczowego var moesz zadeklarowa tyle zmiennych, ile bdzie Ci potrzebne nie musisz za kadym razem uywa dyrektywy var. program varApp; var Zmienna1 : String; Zmienna2 : String; Zmienna3 : String; begin end.

W powyszym przypadku zadeklarowae trzy zmienne typu String. Od tej pory dla kompilatora sowa Zmienna1, Zmienna2, Zmienna3 nie s ju konstrukcjami nieznanymi wiadome bdzie, e w tym przypadku chodzi o zmienne. Podczas deklaracji kilku zmiennych tego samego typu mona wpisa wszystkie zmienne razem, oddzielajc ich nazwy przecinkami: program varApp; var Zmienna1, Zmienna2, Zmienna3 : String; begin end. Z punktu widzenia kompilatora w tym wypadku rwnie nastpuje deklaracja trzech zmiennych typu String. Chcc jeszcze zadeklarowa zmienne innego typu, naley to zrobi w ten sposb:

26 | S t r o n a

program varApp; var Zmienna1, Zmienna2, Zmienna3 : String; Liczba1, Liczba2 : Integer; begin end.

Przydzia danych
Przydzielenie danych dla zmiennej musi odby si w bloku begin. Istnieje jednak moliwo przydzielenia danych w trakcie pisania programu. Przydzia statyczny ~~~~~~~~~~~~ W celu okrelenia wartoci dla konkretnej zmiennej naley to zrobi podczas jej deklarowania, uywajc w tym celu znaku rwnoci (=). program varApp; var Zmienna1 : String = 'Oto zmienna nr 1'; begin end.

Taki kod spowoduje, e na samym starcie programu zmienna Zmienna1 bdzie miaa warto Oto zmienna nr 1. Kady tekst zadeklarowany w ramach zmiennej musi by ujty w znaki apostrofw. Podczas pisania programu nie moesz przydzieli wartoci kilku zmiennym naraz: program varApp; var Zmienna1, Zmienna2 : String = 'Oto zmienna nr 1'; begin end.

27 | S t r o n a

Prba uruchomienia takiego programu spowoduje wywietlenie bdu: [Error] varApp.dpr(4): Cannot initialize multiple variables. program varApp; var Zmienna1 : String = 'Oto zmienna nr 1'; Zmienna2 : String = 'Oto zmienna nr 2'; begin end.

Natomiast kod przedstawiony powyej bdzie ju cakiem prawidowy. Przydzia wartoci dla zmiennej podczas pisania kodu czsto nazywany jest przydziaem domylnym. Jeeli sprbujesz uruchomi program, a kompilator znajdzie zmienn, ktrej nie przypisae adnej wartoci, zostanie wywietlone ostrzeenie: [Hint] varApp.dpr(4): Variable 'Zmienna1' is declared but never used in 'varApp'.

Przydzia dynamiczny Moliwa jest take zmiana zawartoci danej zmiennej podczas pracy programu. Jest to czynno stosunkowo prosta polega ona na zastosowaniu znaku :=, tzw. operatora przydziau. Oto przykad: program varApp; var Zmienna1 : String; Zmienna2 : String; begin Zmienna1 := 'Oto jest zmienna nr 1'; Zmienna2 := 'Oto jest zmienna nr 2'; end.

Oczywicie nic si nie stanie jeeli ponownie zmienisz warto ju raz deklarowanej zmiennej.

28 | S t r o n a

Stae
Podobnie jak zmienne, stae rwnie su do przechowywania jakich danych podczas dziaania aplikacji. Jest jednak pomidzy nimi jedna rnica stae, jak sama nazwa wskazuje, nie mog podlega modyfikacji podczas dziaania programu. Czyli warto staych jest okrelana ju podczas pisania programu: program varConst; const Stala1 = 'Oto jest staa...'; begin end.

Stae, w odrnieniu od zmiennych, deklarujemy z uyciem sowa kluczowego const. Jak widzisz, nie deklarujemy take typu zmiennej typ jest okrelany automatycznie na podstawie wartoci.

Domylne typy staych


Jeeli przykadowo przypisujesz staej jak liczb: const Stala2 = 12;

Delphi uzna, e staa jest sta typu Integer (jest to domylny typ staych). Programista moe to w do prosty sposb zmieni: program varConst; const Stala1 = 'Oto jest staa...'; Stala2 : Byte = 12; begin end.

A zatem w tym przypadku Stala2 bdzie sta typu Byte o wartoci 12.

29 | S t r o n a

Jeeli sprbujesz przypisa jak warto staej przykadowo: begin Stala1 := 'Inna warto'; end.

Delphi uzna to za bd i wywietli podpowied dla Ciebie: [Error] varConst.dpr(8): Left side cannot be assigned to.

Uywanie staych i zmiennych w programie


Jeeli potrafisz ju deklarowa stae i zmienne, naley z nich wreszcie skorzysta. Przy tej okazji poznasz pierwsze polecenie ShowMessage. Jego uycie spowoduje wywietlenie okienka informacyjnego. Z polecenia tego korzysta si w nastpujcy sposb: program varConst; uses Dialogs; // wczanie moduu do programu tym zajmiemy si pniej begin ShowMessage('To jest tekst w okienku!'); end.

Program rozpoczynamy pewnym sowem kluczowym uses. Nie zawracaj sobie jednak tym gowy zajmiemy si t kwesti w dalszej czci rozdziau. Najwaniejsze jest polecenie ShowMessage. W nawiasie oraz w apostrofach wpisujemy tekst, ktry ma by wywietlony w oknie. Uruchom teraz program, naciskajc klawisz F9 rezultat dziaania przedstawiony jest na rysunku 2.1.

Rysunek 2.1. Rezultat dziaania programu Zamiast tekstu w apostrofach moesz wpisa nazw zmiennej tym sposobem program podczas dziaania podstawi na to miejsce zawarto zmiennej. Tak samo ma si sprawa ze staymi oto przykad:

30 | S t r o n a

program varConst; uses Dialogs; // wczanie moduu do programu tym zajmiemy si pniej const Stala1 = 'Oto jest staa...'; var Zmienna1 : String; begin Zmienna1 := 'Tekst umieszczony w okienku!'; ShowMessage(Zmienna1); ShowMessage(Stala1); end.

Na samym pocztku w bloku begin nastpuje przypisanie danych zmiennej, a kolejnym krokiem jest wywietlenie jej zawartoci; nastpne okienko wywietli natomiast zawarto staej.

Tablice danych
Wyobra sobie przypadek, gdy w programie musisz uy wielu, naprawd wielu zmiennych. Czy wygodne jest w takim przypadku deklarowanie tylu zmiennych, z inn nazw dla kadej Do tego wanie su tablice. Tablice deklarowane s za pomoc sowa kluczowego array. program arrayApp; uses Dialogs; var Tablica : array[0..1] of String; begin end. Konstrukcja tablic jest do specyficzna. Po sowie kluczowym array w nawiasach kwadratowych naley wpisa ilo elementw, z ktrych skada si bdzie tablica. Nazwa_Tablicy : array[Najmniejszy_Indeks..Najwikszy_Indeks] of Typ_danych; W powyszym przypadku najmniejszym indeksem jest 0, a najwikszym 1. Z tego powodu tablica skada si bdzie z dwch elementw (zero jest take liczone jako jeden element).

31 | S t r o n a

Przydzia danych odbywa si take z zastosowaniem nawiasw kwadratowych: program arrayApp;

var Tablica : array[0..1] of String; begin Tablica[0] := 'Pierwszy element tablicy'; Tablica[1] := 'Drugi element tablicy'; end.

Podsumujmy: z tablic korzysta si tak samo jak ze zmiennych. Jedyn rnic jest to, e naley zawsze podawa numer indeksu, do ktrego chce si zapisa lub odczyta dane.

Tablice jako stae


Moliwe jest deklarowanie tablic jako staych. Tak, jak w przypadku zwykych staych, dane take naley przypisa tablicy podczas projektowania aplikacji: program arrayConst; const Tablica : array[0..1] of String = ( ('Pierwszy element'), ('Drugi element') ); begin end.

Take w tym przykadzie tablica skada si z dwch elementw. Dodatkowe nawiasy zostay wprowadzone jedynie po to, aby zwikszy czytelno rwnie dobrze mona by zapisa program w ten sposb: program arrayConst; const Tablica : array[0..1] of String = ( 'Pierwszy element', 'Drugi element'); begin end.

32 | S t r o n a

Obowizkowy jest jedynie jeden nawias, w ktrym wypisujemy elementy tablicy, oddzielajc je przecinkami. Naley uwaa na przydzia danych zgodnie z iloci elementw, jakie zostay zadeklarowane w kodzie. Przykadowy kod: program arrayConst; const Tablica : array[0..2] of String = ( 'Pierwszy element', 'Drugi element'); begin end.

nie bdzie mg zosta skompilowany zadeklarowano trzy elementy, a dane przydzielono jedynie do dwch. Delphi wywietli bd: [Error] arrayConst.dpr(5): Number of elements differs from declaration.

Tablice wielowymiarowe
Object Pascal umoliwia take deklarowanie tablic tzw. wielowymiarowych. Po zadeklarowaniu takich tablic do konkretnego elementu moemy odwoa si w nastpujcy sposb: Tablica[0][0] := 'Przypisanie danych';

W powyszym przypadku skorzystaem jedynie z tablic dwuwymiarowych, ktrych deklaracja wyglda tak: var Tablica : array[0..1, 0..1] of String;

Deklaracja jest take specyficzna polega bowiem na wypisywaniu iloci elementw w nawiasie kwadratowym, przy czym poszczeglne elementy s oddzielone przecinkami. W przedstawionej wyej deklaracji mamy a 4 elementy! Przydzia danych odbywa si w nastpujcy sposb: program arrayarray; var Tablica : array[0..1, 0..1] of String; begin Tablica[0][0] Tablica[0][1] Tablica[1][0] Tablica[1][1] end.

:= := := :=

'Element 'Element 'Element 'Element

1'; 2'; 3'; 4';

33 | S t r o n a

Istot dziaania tablic dwuwymiarowych lepiej zrozumiesz, przegldajc listing 2.2. Listing 2.2. Program deklarujcy dwuwymiarowe tablice program arrayarray; var Tablica : array[0..1, 0..2] of String; begin Tablica[0][0] := 'Fiat'; { marka samochodu } Tablica[0][1] := 'Uno'; Tablica[0][2] := 'Punto'; { modele samochodu } Tablica[1][0] := 'Audi'; Tablica[1][1] := 'A4'; Tablica[1][2] := 'A8'; end.

W tym przypadku nastpia deklaracja tablicy 2x3. Dwa gwne elementy to element Fiat oraz element Audi. Kolejne dwa podpola okrelaj modele samochodw. Przedstawiajc tablice wielowymiarowe, mwiem tylko o dwch wymiarach. Istnieje jednak moliwo zadeklarowania tablic, ktre bd miay wiele wymiarw. program arrayx3; var Tablica : array[0..1, 0..1, 0..1] of String; begin Tablica[0][0][0] := 'Warto'; { itd. } end.

W tym przykadzie nasza tablica to tablica 3x2 typu String. W jaki sposb dane s przydzielane do tej tablicy Przykad znajduje si w powyszym kodzie rdowym.

34 | S t r o n a

Tablice dynamiczne
Nieraz podczas pracy z Delphi w programie wymagane bdzie zadeklarowania tablicy o niewiadomej liczbie elementw. Znaczy to, e programista w trakcie pisania programu nie jest w stanie okreli, ile elementw tablicy bdzie mu potrzebne. W tym celu w Delphi 4 zaimplementowano moliwo tworzenia tablic dynamicznych. Tablice dynamiczne deklaruje si bez podania iloci elementw: program dynArray; var Tablica : array of String; begin end.

Przy tej okazji poznasz nowe polecenie SetLength. Suy ono do okrelenia iloci elementw tablicy podczas dziaania programu. Pierwszym parametrem tego polecenia jest nazwa tablicy dynamicznej drugi parametr to ilo elementw, z ktrych tablica ma si skada. Parametry przekazywane do polecenia musz by oddzielane przecinkami: program dynArray; var Tablica : array of String; begin SetLength(Tablica, 2); end.

Od tej pory po uruchomieniu programu tablica skada si bdzie z dwch elementw. Wypenianie elementw danymi odbywa si tak samo jak w przypadku zwykych tablic: program dynArray; var Tablica : array of String; begin SetLength(Tablica, 2); Tablica[0] := 'Warto 1'; Tablica[1] := 'Warto 2'; end.

35 | S t r o n a

Na poziomie tworzenia programu nie jest moliwe okrelenie przez kompilator, z ilu elementw ostatecznie bdzie skada si tablica. Std prba odczytu elementu spoza tablicy moe skoczy si komunikatem o bdzie!

Polecenia Low i High Oba polecenia Low i High mog by uyte jedynie w poczeniu z tablicami. Warto je zna, gdy czasem mog si przyda. Zwracaj one liczb rwn najmniejszemu (Low) oraz najwikszemu (High) indeksowi tablicy. Deklaracja tablicy moe na przykad wyglda w ten sposb: Tablica : array[10..100] of Integer;

Wywoanie polecenia Low(Tablica) spowoduje, e funkcja zwrci warto 10. Natomiast funkcja High zwrci warto 100.

Operatory
W jzyku Object Pascal istnieje wiele operatorw. Dwa z nich zastosowae ju wczeniej. S to operatory przypisania (:=) i porwnania (=). Operator porwnania, jak zapewne zauwaye, jest take uywany do przydzielania zmiennym i staym domylnych wartoci. Najprociej mwic, operatory to elementy (znaki) jzyka suce do manipulowania danymi. Istniej operatory porwnania, arytmetyczne i przypisania. Wikszo z nich zaprezentowaem w tabeli 2.2. Operator = := <> > > >= <= Znaczenie Porwnanie sprawdza, czy obie wartoci s sobie rwne Przypisanie danych jeden z najwaniejszych operatorw Rne od sprawdza, czy obie wartoci s od siebie rne Wiksze od sprawdza, czy jedna warto z podanych zmiennych jest wiksza od drugiej Mniejsze od sprawdza, czy jedna warto jest mniejsza od drugiej Wiksze lub rwne Mniejsze lub rwne

36 | S t r o n a

+ * / div mod and or not xor shl shr

Dodawanie Odejmowanie Mnoenie Dzielenie Dzielenie z obciciem reszty Dzielenie z zachowaniem reszty z dzielenia Porwnywanie typw logiczne i Porwnywanie typw logiczne lub Zaprzeczenie (stosowane podczas porwnywania typw) Operator bitowy dysjunkcja Operator bitowy przesunicie w lewo Operator bitowy przesunicie w prawo

Zastosowanie wikszoci z tych operatorw poznasz w kolejnym podpunkcie Instrukcje warunkowe. Oprcz wyej podanych operatorw istniej take funkcje Inc oraz Dec. Funkcje te su odpowiednio do zwikszania i zmniejszania wartoci parametru. S one rwnowane nastpujcym instrukcjom: X := X + 1; // to samo co Inc(x) Y := Y 1; // to samo co Dec(Y)

Ponadto funkcje Inc i Dec posiadaj opcjonalny parametr dodatkowy. Domylnie bowiem warto podanej zmiennej jest zwikszana lub zmniejszana o jeden. Drugi parametr decyduje o tym, czy ma to by warto wiksza ni jeden np. var I : Integer; begin I := 1; // przydzielenie wartoci pocztkowej Inc(I, 2); // w tym momencie zmienna I posiada warto 3 end.

37 | S t r o n a

Aplikacje konsolowe
W dalszej czci tego rozdziau korzysta bdziemy z tzw. aplikacji konsolowych. Oczywicie bdziemy si tym zajmowa tylko w tym rozdziale podczas czytania kolejnych korzysta ju bdziesz z gwnych funkcji, jakie oferuje Delphi, czyli komponentw i formularzy. Na razie jednak, aby wszystko lepiej zrozumie, proponuj Ci wykorzystanie w przykadach aplikacji konsolowych. Inaczej mwic, aplikacje konsolowe to programy, ktrych rezultat wywietlany jest w oknie trybu MS-DOS. Aby program mg dziaa w trybie konsolowym, nie trzeba wykonywa adnych skomplikowanych czynnoci wystarczy doda do projektu tak oto dyrektyw:
{$APPTYPE CONSOLE}

Dyrektywa ta zawarta jest jednak w nawiasie klamrowym, czyli teoretycznie powinna by traktowana przez kompilator jako komentarz. Tak jednak nie jest, a to za spraw znaku $ na samym pocztku. Przykadowy program moe zatem wyglda nastpujco: program appconsole; {$APPTYPE CONSOLE} begin Readln; end.

Instrukcja Readln w bloku programu nakazuje czekanie na nacinicie przez uytkownika klawisza Enter. Dopiero po wykonaniu tej czynnoci program zostanie zamknity. Wpisywanie tekstu do okna konsoli odbywa si poprzez zastosowanie polecenia Writeln lub Write. W tym pierwszym na kocu dodawany zostaje znak nowej linii, natomiast w drugim nie. program appconsole; {$APPTYPE CONSOLE} begin Write('Linia, ktra nie bdzie zakoczona znakiem nowej linii'); Writeln('To linia, ktra bdzie zakoczona znakiem nowej linii'); Write('A ta znowu nie bdzie...'); Readln; end.

Uruchom program przedstawiony w powyszym listingu, aby sprawdzi rezultat dziaania.

38 | S t r o n a

Instrukcje warunkowe
Instrukcje warunkowe s elementem programowania obecnym w kadym jzyku. S bardzo czsto wykorzystywane, wic niezbdne jest poznanie ich i zrozumienie zasad ich dziaania. Instrukcje te su bowiem do porwnywania danych i wykonywania na nich rnych operacji. Wykorzystujc instrukcje warunkowe, jeste w stanie odpowiednio zareagowa na dan sytuacj.

Instrukcja if..then
Podstawow instrukcj warunkow jest instrukcja if..then. Jej budowa jest nastpujca: if { tu nastpuje sprawdzanie warunku } then { wykonaj pewn operacj }

Po sowie if musi si znale pewien warunek do sprawdzenia przykadowo if 4 > 3 then

Taki warunek zostanie zawsze speniony, gdy wiadomo, e cyfra 4 jest wiksza od 3. Tak wic za kadym razem zostanie wykonany kod umieszczony po sowie then. program if_then; {$APPTYPE CONSOLE} begin if 4 > 3 then Writeln('Tak... warunek zosta speniony!'); Readln; end.

Po uruchomieniu powyszego programu za kadym razem wywietlony zostanie tekst Tak... Warunek zosta speniony!.

39 | S t r o n a

Pobieranie tekstu z konsoli Przeprowadmy mae wiczenie. Ot po uruchomieniu programu zostaniemy poproszeni o wpisanie pewnego tekstu imienia. Jeli wpisane zostanie imi Adam, zostan wykonane pewne czynnoci; w przeciwnym wypadku zostanie wywietlony tekst powitalny. Kod programu realizujcego to zadanie ma nastpujc posta: program if_then; {$APPTYPE CONSOLE} var Imie : String; begin Writeln('Wpisz swoje imi...'); Readln(Imie); // pobranie wpisanej wartoci if Imie = 'Adam' then Writeln('Super! Ja te mam na imi Adam!'); if Imie <> 'Adam' then Writeln('Cze ' + Imie); Readln; end.

Na samym pocztku programu pobierane s dane wpisane przez uytkownika programu. Realizuje to procedura Readln. Za jej pomoc dane wpisane przez uytkownika wdruj do zmiennej Imie. Cel naszego wiczenia to sprawdzenie, czy wpisany tekst to Adam. Zwr take uwag na ten wiersz kodu: Writeln('Cze ' + Imie);

Do wywietlanego napisu Cze doczana jest take zawarto zmiennej Imie. Jak widzisz, operator (+) ma take takie zadanie czenie dwch tekstw.

Kilka instrukcji po sowie then

Istnieje pewna zasada, ktr naley zapamita i ktra bdzie wykorzystywana w dalszej czci tego rozdziau. Ot wiele instrukcji po sowie then musi by umieszczone dodatkowo w bloku tekstu midzy begin a end (przykad: listing 2.3.) 40 | S t r o n a

Listing 2.3. Pobieranie danych z konsoli i porwnywanie ich za pomoc operatora if program if_then; {$APPTYPE CONSOLE} var Imie : String; begin Writeln('Wpisz swoje imi...'); Readln(Imie); // pobranie wpisanej wartoci if Imie = 'Adam' then begin // dodatkowa instrukcja begin Writeln('Super! Ja te mam na imi Adam!'); Writeln('Hahhahaa...'); end; Readln; end.

W caym powyszym listingu interesuje nas jedynie ten fragment kodu: if Imie = 'Adam' then begin // dodatkowa instrukcja begin Writeln('Super! Ja te mam na imi Adam!'); Writeln('Hahhahaa....'); end;

Oznacza on, e gdy warto zmiennej jest rwna Adam, wykonane bd w wyniku tego dwie instrukcje Writeln. Taki wypadek zmusza nas do umieszczenia dodatkowo sw kluczowych begin oraz end. Jeeli blok begin..end nie znalazby si w kodzie, to druga instrukcja Writeln wykonana zostaaby niezalenie od tego, czy zmienna Imie posiadaaby warto Adam, czy te nie.

Kilka warunkw do spenienia To, czy dany kod zostanie wykonany, moe zalee od wielu czynnikw. Niekoniecznie musi by to jeden warunek do spenienia moe by ich wiele. Jednak konieczne jest umieszczenie wszystkich tych warunkw w nawiasie (listing 2.4.). Listing 2.4. Kilka warunkw w instrukcji if 41 | S t r o n a

program if_then; {$APPTYPE CONSOLE} var Imie : String; begin Writeln('Wpisz swoje imi...'); Readln(Imie); // pobranie wpisanej wartoci Randomize; // procedura losujca if (Imie = 'Adam') and (Random(10) = 5) then Writeln('Super! Ja te mam na imi Adam!'); Readln; end.

Kod ten mona przetumaczy nastpujco: jeeli zmienna Imie zawiera warto Adam i rezultat losowania spord 10 liczb wynosi 5, wywietl tekst na konsoli. Jak widzisz, aby warunek zosta speniony pomylnie, musz zosta wykonane dwie czynnoci. Wielu pocztkujcych programistw czsto orientuje si, e zapomnieli zastosowa nawiasw podczas sprawdzania kilku warunkw. Spowoduje to w tym wypadku wywietlenie bdu: [Error] if..then.dpr(14): Incompatible types: 'String' and 'Integer'. Poznae przy tej okazji znaczenie kolejnej procedury, a mianowicie Random. Polecenie to realizuje proces losowania spord liczb, z ktrych najwysza podana jest w nawiasie. A zatem wywoanie procedury w ten sposb Random(100) spowoduje wylosowanie liczby z zakresu od 0 do 99. Tak! Nie pomyliem si! Moliwe jest, e wyniku losowania zwrcona zostanie warto 0. Pamitaj, aby zawsze przed wywoaniem procedury Random umieszcza w kodzie instrukcj Randomize. Powoduje ona zainicjowanie procesu losowania.

Instrukcja case..of
Drug instrukcj warunkow jest case..of. Instrukcja ta rwnie realizuje proces porwnywania danych. Czsto stosowana jest w przypadku, gdy mamy do sprawdzenia wiele warunkw instrukcja if..then nie zdaje wwczas egzaminu, gdy naleaoby porwna wiele wartoci ze sob. Idealnie za to nadaje si do tego instrukcja case..of; jej skadnia jest nastpujca: case Nazwa_Zmiennej of 0: { instrukcje wykonywane w przypadku, gdy zmienna ma warto 0 } 1: { instrukcje wykonywane w przypadku, gdy zmienna ma warto 1 } end;

42 | S t r o n a

Jak wida, schemat instrukcji jest raczej prosty. Nie zapomnij o sowie end;, koczcym instrukcj warunkow. Przykadowy program zaprezentowaem w listingu 2.5. Listing 2.5. Program wykorzystujcy instrukcj case..of program case_of; {$APPTYPE CONSOLE} var Mandat : Integer; begin Mandat := 50; case Mandat of 10: Writeln('E, 10 z. Moe jeszcze zapac?'); 20: Writeln('20 z nie bd mia na chleb!'); 50: Writeln('50 z Panie wadzo... moe dogadamy si w inny sposb?'); end; Readln; end.

Po uruchomieniu programu wykonany zostanie warunek ostatni, gdy zmiennej Mandat nadaem na sztywno warto 50; chciaem jednak tylko zaprezentowa sposb uycia instrukcji case..of.

Zakresy Du zalet instrukcji case..of jest moliwo sprawdzania wartoci od-do. Przykadowo jeeli warto zmiennej Mandat wynosi midzy 10 a 15, wykonany zostanie konkretny warunek. Zmodyfikujemy poprzedni program do takiej postaci, aby warto zmiennej bya losowana: program case_of; {$APPTYPE CONSOLE} var Mandat : Integer; begin Randomize; Mandat := Random(50)+1; // dodajemy 1, aby nie zostao wylosowane 0 case Mandat of 1..10: Writeln('E, 10 z. Moe jeszcze zapac?'); 43 | S t r o n a

11..20: Writeln('20 z ? nie bd mia na chleb!'); 21..50: Writeln('50 z ? Panie wadzo... moe dogadamy si w inny sposb?'); end; Readln; end.

Dziki zastosowaniu zakresw pierwsza instrukcja zostanie zrealizowana w przypadku, gdy zmienna Mandat bdzie miaa warto od 1 do 10.

Brak moliwoci korzystania z acuchw Wad instrukcji case..of jest brak moliwoci porwnywania danych tekstowych. Najlepiej sprawdzi to na przykadzie spjrz na poniszy kod: program case_strings; {$APPTYPE CONSOLE} var S : String; begin S := 'H'; case S of 'H': Writeln('???'); end; Readln; end.

Prbujemy tu sprawdzi, czy zmienna S ma warto H. Niestety prba kompilacji takiego programu zakoczy si bdem: [Error] case_strings.dpr(11): Ordinal type required.

Kilka instrukcji Tak samo, jak w przypadku instrukcji warunkowej if..then, instrukcja case..of wymaga umieszczenia kodu w bloku begin..end w przypadku, gdy kod ten zawiera wicej ni jedn instrukcj.

44 | S t r o n a

program case_of; {$APPTYPE CONSOLE} var Mandat : Integer; begin Randomize; Mandat := Random(50)+1; // dodajemy 1, aby nie zostao wylosowane 0 case Mandat of 1..10: Writeln('E, 10 z. moe jeszcze zapac'); 11..20: Writeln('20 z. nie bd mia na chleb'); 21..50: begin Writeln('50 z. Panie wadzo... moe dogadamy si w inny sposb?'); Writeln('Nieeeee...'); end; end; Readln; end.

Sam widzisz jeeli wylosowana zostanie warto pomidzy 21 a 50, wykonane zostan dwie instrukcje. W takim przypadku naley umieci kod w dodatkowym bloku begin..end.

Instrukcja else
Angielskie sowo else mona w tym kontekcie przetumaczy jako w przeciwnym wypadku. Konstrukcja else jest zawsze stosowana w poczeniu z instrukcj if..then oraz case..of. Za jej pomoc mona wykona takie operacje, ktre nie mieszcz si w ramach wczeniej wykorzystywanych instrukcji. Oczywicie wszystko najlepiej zrozumie na przykadach napisz zatem przykadowy program. Pamitasz pierwszy program, jaki zaprezentowaem podczas omawiania instrukcji if Zmodyfikuj go do takiej postaci: program if_then_else; {$APPTYPE CONSOLE} var Imie : String; begin 45 | S t r o n a

Writeln('Wpisz swoje imi...'); Readln(Imie); // pobranie wpisanej wartoci if Imie = 'Adam' then Writeln('Super! Ja te mam na imi Adam!') // < rednika! else Writeln('Cze ' + Imie); Readln; end.

uwaga! Brak

W poprzedniej wersji tego programu to, czy zmienna Imie nie zawiera wartoci Adam, sprawdzane byo w kolejnym warunku if. Teraz w przypadku, gdy zmienna nie bdzie zawiera tej wartoci, wywietlony zostanie tekst powitalny. Wszystko moliwe jest za spraw instrukcji else. Mam nadziej, e ten przykad pomg Ci zrozumie, jak dziaa instrukcja else. Zauwa, e w wierszu nad instrukcj else nie ma rednika na kocu! To jest wanie wyjtkowa sytuacja, kiedy nie stawiamy rednika!

Kiedy stosowa rednik, a kiedy nie ? Poprzedni przykad z uyciem instrukcji if i else nie zawiera rednika. Kiedy stosowa ten rednik, a kiedy nie Spjrz taki fragment kodu ju wymaga postawienie rednika przed else: if Imie = 'Adam' then begin { jaka inna instrukcja } Writeln('Super! Ja te mam na imi Adam!'); end else Writeln('Cze ' + Imie);

Take w przypadku, gdy po sowie then stosujemy blok begin..end, rednik musi si w kodzie znale! Ale zasada pozostaje taka sama nie stosujemy go przed else! end else Writeln('Cze ' + Imie);

Sprawa pocztkowo moe wyda si nieco skomplikowana, ale po dokadniejszym przejrzeniu kodu mona rozrni, kiedy naley stosowa rednik na kocu, a kiedy nie.

46 | S t r o n a

Kilka instrukcji if i else Moliwe jest poczenie kilku instrukcji if oraz else. Taka konstrukcja ma posta else if {
warunek }

Jeeli jeden warunek nie zostanie speniony, nastpi analiza drugiego. Jeeli take drugi nie zostanie speniony analizowane bd kolejne warunki. Spjrz na poniszy kod: program if_else; {$APPTYPE CONSOLE} var I : Integer; begin Randomize; I := Random(50); if i = 10 then Writeln('I = 10') else if i = 20 then Writeln('I = 20') else if i = 30 then Writeln('I = 30') { kod, w razie, gdy aden warunek nie zostanie speniony } else Writeln('adna warto nie jest odpowiednia!'); Readln; end.

Na samym pocztku nastpuje sprawdzenie, czy wylosowana liczba to 10; w innym wypadku nastpuje sprawdzenie kolejno liczb 20 i 30, co tworzy nastpne warunki if. Jeeli aden z poprzednich warunkw nie zostanie zrealizowany, wykonany zostanie kod umieszczony po sowie else. Kilka instrukcji po sowie begin Jak mwiem wczeniej, czsto bdzie sprawdza si zasada mwica o tym, e gdy po jednym sowie kluczowym (np. else lub then) wystpi wicej ni jedna instrukcja, cay fragment kodu naley umieci w dodatkowym bloku begin..end. if i = 10 then Writeln('I = 10') else if i = 20 then Writeln('I = 20') else if i = 30 then Writeln('I = 30') { kod wykonywany w razie, gdy aden warunek nie zostanie speniony } else begin Writeln('adna warto nie jest odpowiednia!'); Writeln('Sprbuj jeszcze raz'); end;

Jak wida w powyszym przykadzie, rednik nie zosta wstawiony ani po sowie else, ani przed nim.

47 | S t r o n a

Instrukcja else w case..of Moliwe jest take zastosowanie sowa kluczowego else w instrukcji case..of. Jeeli aden ze sprawdzanych warunkw case nie zostanie speniony, mona odpowiednio na to zareagowa. Posu si jednym z poprzednich przykadw (listing 2.6). Listing 2.6. Instrukcja else zagniedona w case..of program case_of_else; {$APPTYPE CONSOLE} var Mandat : Integer; begin Randomize; Mandat := Random(100)+1; // dodajemy 1, aby nie zostao wylosowane 0 case Mandat of 1..10: Writeln('E, 10 z. Moe jeszcze zapac?'); 11..20: Writeln('20 z nie bd mia na chleb!'); 21..50: begin Writeln('50 z Panie wadzo... moe dogadamy si w inny sposb?'); Writeln('Nieeeee...'); end; { dodajemy else } else Writeln('Jaki inny mandacik?'); end; Readln; end.

Tym razem losowana jest liczba z zakresu od 1 do 100. Dziki temu instrukcja case moe nie uwzgldnia tej liczby wykonany zostanie blok kodu umieszczony po sowie kluczowym else.

48 | S t r o n a

Procedury i funkcje
Podczas czytania tego rozdziau moge zauway, e nieraz posugiwaem si sowem procedura lub funkcja w odniesieniu do polece jzyka Object Pascal. Moge odnie wraenie, e wszystkie te sowa s synonimami jednak sprawa wyglda nieco inaczej.

Procedury
Procedura to wydzielony blok kodu, realizujcy okrelone zadanie. Procedur, z ktrej korzystae ju na samym pocztku tego rozdziau, jest ShowMessage. Jak pamitasz, procedura ta realizowaa wywietlanie okienka informacyjnego. Wyobra sobie, e w danym programie musisz czsto wykonywa ten sam blok kodu np. wywietlanie kilku tekstw w konsoli. W takim przypadku za kadym razem naleaoby wpisywa po kolei instrukcje Writeln. Dziki zastosowaniu procedur moesz wpisa te instrukcje tylko raz pniej pozostaje ju tylko umieszczenie w odpowiednim miejscu nazwy procedury, aby wykona kod w niej zawarty. Przykadowa deklaracja procedury wyglda tak: procedure Nazwa_Procedury; begin { kod procedury } end;

Jak widzisz, procedur deklaruje si z uyciem sowa kluczowego procedure, po ktrym nastpuje nazwa. Nazwa procedury musi by unikalna dla kadego programu nie moe si powtarza. Od teraz za kadym razem, gdy gdzie w kodzie wpiszesz sowo Nazwa_Procedury, wykonany zostanie kod umieszczony w jej wntrzu. Przykadowy program z wykorzystaniem procedur przedstawiam poniej. program ProcApp; {$APPTYPE CONSOLE} procedure Quit; begin Writeln('Wyjcie z programu! Nacinij Enter, aby zakoczy!'); Readln; end; var Key : Char; begin Writeln('Nacinij liter Q, aby zakoczy!'); Readln(Key); if Key = 'Q' then Quit else Writeln('Fajnie, e jeste z nami'); end. 49 | S t r o n a

Pamitaj o tym, e procedury deklaruje si poza blokiem begin..end! W programie tym zadeklarowaem procedur Quit. Od tej pory za kadym razem, gdy wpiszesz w programie sowo Quit, program wywietli informacj i po naciniciu klawisza Enter zakoczy swe dziaanie. W programie tym zadeklarowaem nowy typ zmiennej, jakiego do tej pory nie uywaem. Typ Char, bo to o nim mowa, umoliwia zapisywanie w pamici danych o postaci jedynie jednego znaku.

Funkcje
Funkcje s w swoim dziaaniu bardzo podobne do procedur. Waciwie w innych jzykach programowania, jak C/C++ czy Java, procedury w ogle nie istniej dostpne s jedynie funkcje. Podstawowa rnica pomidzy procedurami a funkcjami polega na tym, e te drugie zwracaj jak warto. Deklaracja funkcji jest bardzo specyficzna, nastpuje bowiem poprzez uycie sowa kluczowego function. Oprcz tego funkcja, jak ju mwiem, musi zwraca jak warto po znaku dwukropka naley wpisa typ zwracanej wartoci: function GetName : String; begin end;

By moe w dalszym cigu nie rozumiesz, w czym funkcje rni si od procedur najatwiej napisa przykadowy program spjrz na listing 2.7 Listing 2.7. Przykadowy program wykorzystujcy funkcj program FuncApp; {$APPTYPE CONSOLE} function GetName : String; begin Result := 'Adam Boduch'; end; begin Writeln('Nazywam si ' + GetName); Readln; end.

50 | S t r o n a

Po uruchomieniu takiego programu na konsoli wywietlony zostanie napis Nazywam si Adam Boduch. To wszystko stanie si za spraw wiersza: Result := 'Adam Boduch';

Sowo Result jest jakby ukryt zmienn po przypisaniu jej wartoci zostanie ona zwrcona przez funkcj. Sprbuj to samo zrobi z procedurami nie uda Ci si to, poniewa procedura nie moe zwrci wartoci; program nie zostanie zatem uruchomiony.

Zmienne lokalne
Tak, jak w kodzie programu mona zadeklarowa zmienne i stae, tak mona je rwnie zadeklarowa w ciele procedury lub funkcji. Takie zmienne nazywa si zmiennymi lokalnymi o okrelonym czasie ycia. function GetValue : String; var S : String; begin S := 'Khem...'; Result := S; end;

Zasada deklarowania zmiennych jest taka sama deklaracja umieszczana jest przed blokiem begin. Zmienne lub stae deklarowane w ciele procedury nie s widoczne poza ow procedur. A zatem w przypadku, gdy sprbujesz odwoa si do zmiennej umieszczonej w procedurze, Delphi wywietli komunikat o bdzie. Dlaczego takie zmienne nazywane s zmiennymi o okrelonym czasie ycia Przyczyn tego jest fakt, e pami dla nich alokowana jest w momencie wywoania procedury, a zwalniania w momencie zakoczenia dziaania. Jeeli mamy program, ktry korzysta z wielu procedur, zalecane jest uywanie o ile to moliwe zmiennych lokalnych. Dziki temu oszczdzamy pami. W dalszej czci tej ksiki moesz spotka si z okreleniem zmienne globalne. Takie zmienne to po prostu zwyke zmienne, widoczne dla caego programu. To, e zmienne zawarte w ciele procedury nie s widoczne na zewntrz, nie oznacza, e procedura nie moe uywa zmiennych globalnych. Ta zasada dziaa tylko w jedn stron!

51 | S t r o n a

Parametry procedur i funkcji


Moliwe jest, wraz z wywoaniem danej procedury lub funkcji, przekazanie do niej danych tzw. parametrw. Objani to na przykadzie wspomnianej wczeniej procedury ShowMessage. Podczas jej wywoywania w nawiasie naleao wpisa wartoci, a konkretnie tekst. Ten tekst mg by przez procedur wykorzystany do wywietlania okna informacyjnego. Deklaracja procedury lub funkcji zawierajcej parametry wyglda nastpujco: procedure SetName(Name : String); begin { kod procedury } end;

Od tej pory chcc wywoa procedur SetName, naley poda warto parametru; parametr musi by typu String. Przykadowy program wywietli na konsoli za pomoc procedury SetName tekst z parametru Name: program ProcParam; {$APPTYPE CONSOLE} procedure SetName(Name : String); begin Writeln('Nazywam si ' + Name); end; begin SetName('Adam Boduch'); Readln; end.

Procedura SetName odczytuje warto przekazanego do niej parametru i wywietla go wraz z innym tekstem na konsoli.

Kilka parametrw procedur lub funkcji Moliwe jest przekazanie do procedury lub funkcji kilku parametrw tego samego i innego typu. W takim przypadku wszystkie parametry naley oddzieli znakiem rednika: function SetName(FName : String; SName : String; Age : Byte); 52 | S t r o n a

Aby program zosta prawidowo skompilowany, naley przekaza funkcji a trzy parametry. Jeeli jednak wystpuje kilka parametrw tego samego typu, mona oddzieli je przecinkami: function SetName(FName, SName : String; Age : Byte);

Dziki temu zapis funkcji jest nieco krtszy. Naturalnie z punktu widzenia kompilatora nie ma znaczenia, jak zapisana jest deklaracja moliwe jest wic zapisanie tego w taki, czytelny dla nas sposb: function SetName(FName, SName : String; Age : Byte ) : String; begin end;

Parametry domylne
Pamitasz, jak wspominaem o funkcjach Inc oraz Dec Pierwszy ich parametr musia by nazw zmiennej, a drugi by opcjonalny. Delphi oferuje przydatn moliwo deklarowania parametrw domylnych. Oznacza to, ze podczas wywoywania procedury lub funkcji parametr moe, ale nie musi zosta wpisany w takim wypadku Delphi zastosuje domyln warto, ustalon przez programist. Oto przykad deklaracji takiej procedury: procedure SetName(FName : String; SName : String = 'Nieznany'; Age : Byte = 0 ); begin end;

Parametry domylne wpisujemy po znaku rwnoci. W przypadku powyszej procedury moliwe jest albo wywoanie jej w ten sposb: SetName('Moje imi');

albo wpisanie wszystkich parametrw: SetName('Moje imi', 'Moje nazwisko', 100);

53 | S t r o n a

Oba sposoby s prawidowe jeeli nie wpiszesz dwch ostatnich parametrw, Delphi za domylne wartoci uzna te, ktre zostay wpisane w deklaracji procedury; a zatem dla parametru SName bdzie to warto Nieznany, a dla Age 0. Parametry tego samego typu a wartoci domylne

Niestety nie jest moliwe deklarowanie kilku parametrw tego samego typu i jednoczesne przypisanie wartoci jednemu z nich: procedure SetName(FName, SName = 'Nieznany' : String; Age : Byte = 0 );

Powysza instrukcja spowoduje bd zawsze podczas okrelenia wartoci domylnych naley wpisa typ parametru.

Kolejno wartoci domylnych Naley jeszcze poczyni jedno istotne zastrzeenie dotyczce deklaracji wartoci domylnych. Ot bdna jest deklaracja wygldajca w ten sposb: function Funkcja(X : Integer = 0; Y : Integer) : Integer; begin end;

Prbujemy w ten sposb przypisa warto domyln pierwszemu parametrowi, a drugiemu nie. Delphi zaprotestuje i wywietli bd: [Error] OverProc.dpr(18): Default value required for 'Y'. Jedynym wyjciem jest umieszczenie domylnych parametrw na samym kocu lub deklaracja wartoci domylnych dla kadego parametru. Taki kod bdzie ju jak najbardziej prawidowy: function Funkcja(Y : Integer; X : Integer = 0) : Integer; begin end;

54 | S t r o n a

Przecianie funkcji i procedur


Stosunkowo now technologi w Delphi jest moliwo przeciania procedur i funkcji. Przecianie polega na opatrzeniu funkcji i procedury specjaln klauzul. Dziki temu kompilator nie bdzie protestowa, gdy zadeklarujemy kilka funkcji lub procedur o tej samej nazwie! Warunkiem jest jednak to, e parametry musz by rne, ale nazwa moe pozosta ta sama. Napiszmy przykadowy program, ktry bdzie wykorzystywa funkcje mnoenia przykadowa taka funkcja moe wyglda tak: function Mnozenie(X, Y : Integer) : Integer; begin Result := X * Y; end;

Zasada dziaania jest prosta. Funkcja mnoy dwa parametry X i Y, a nastpnie zwraca rezultat tego dziaania. Jednak podane w funkcji parametry mog by tylko typu Integer, czyli mog by wycznie liczbami cakowitymi. W przypadku, gdy chcemy do parametrw przekaza wartoci zmiennoprzecinkowe, kompilator zasygnalizuje bd. Mona oczywicie zamiast typu Integer zastosowa chociaby typ Currency. Mona take wykorzysta dwie funkcje Mnozenie o rnych parametrach: function Mnozenie(X, Y : Integer) : Integer; overload; begin Result := X * Y; end; function Mnozenie(X, Y : Currency) : Currency; overload; begin Result := X * Y; end;

eby wszystko zostao skompilowane dobrze, obie funkcje naley oznaczy klauzul overload. Od tej chwili podczas wywoywania funkcji Mnozenie kompilator sam na podstawie parametrw ustali, jak funkcj chcemy wywoa: Mnozenie(2, 2); Mnozenie(2.5, 2.5);

55 | S t r o n a

Typy parametrw przekazywanych do procedur i funkcji


To, co powiedziaem wczeniej na temat prostego przekazywania parametrw do funkcji i procedur, to nie wszystko. Istnieje moliwo przekazywania parametrw przez referencj lub przez sta. Przekazywanie parametrw poprzez sta Umieszczenie w deklaracji parametrw sowa kluczowego const spowoduje przekazywanie parametrw jako staych. procedure Show(const Message : String); begin Writeln(Message); end;

Co to oznacza Procedura nie moe w aden sposb wpywa na zawarto parametru. Prba nadania przez procedur jakiej wartoci spowoduje bd: [Error] ConstParam.dpr(7): Left side cannot be assigned to. Przekazujc parametry przez sta pozwalasz kompilatorowi na maksymaln optymalizacj kodu. Przekazywanie parametrw przez referencj Przekazywanie parametrw przez referencj polega na umieszczeniu przed parametrami sowa kluczowego var. Dziki temu kod znajdujcy si wewntrz procedury moe zmieni warto parametru. Aby lepiej to zrozumie, przeprowadmy mae wiczenie. Spjrz na poniszy listing 2.8. Listing 2.8. Wartoci przekazywane przez referencj program VarParam; {$APPTYPE CONSOLE} procedure SetValue(Message : String); begin { prba nadania nowej wartoci dla parametru } Message := 'Hello there!'; end; var S : String; begin S := 'Hello World'; SetValue(S); // przekazanie zmiennej Writeln(S); // odczyt wartoci zmiennej Readln; end. 56 | S t r o n a

Teraz zagadka: jak warto bdzie miaa zmienna S Uruchom program i sprawd! Na ekranie konsoli zostanie wywietlony napis Hello World, co oznacza, e procedurze nie udao si zmieni wartoci parametru. Przedefiniuj teraz nieco deklaracj procedury SetValue: procedure SetValue(var Message : String); begin { prba nadania nowej wartoci dla parametru } Message := 'Hello there!'; end;

Dziki dodaniu sowa var i ponownemu uruchomieniu programu moesz przekona si, e zmienna S bdzie miaa warto Hello there. Oznacza to, e naszej procedurze udao si zmieni warto tego parametru. Dyrektywa out Zamiast sowa kluczowego var w deklaracji funkcji lub procedury moesz umieci take sowo out. Zasada dziaania dyrektywy out jest bardzo podobna do zasady dziaania dyrektywy var. Rnica polega na tym, e do parametru zadeklarowanego jako out nie mona przekaza adnej wartoci. Zrozumiesz to na przykadzie poprzedniego programu zmodyfikuj go do takiej postaci: program OutParam; {$APPTYPE CONSOLE} procedure SetValue(out Message : String); begin { Message nie zawiera wartoci! } Message := 'Hello there!'; end; var S : String; begin S := 'Hello World'; SetValue(S); // przekazanie zmiennej Writeln(S); // odczyt wartoci zmiennej Readln; end.

W kodzie programu zapisana jest prba przekazania wartoci do procedury SetValue. Jednak za spraw dyrektywy out warto nie dociera do procedury. Inaczej mwic, parametr Message jest pusty. Sprawd dziaanie programu i porwnaj, jak program zachowuje si w przypadku, gdy w kodzie zamiast sowa out znajduje si instrukcja var.

57 | S t r o n a

Procedury zagniedone
Nic nie stoi na przeszkodzie, aby dan procedur lub funkcj umieci w innej procedurze lub funkcji. Wyglda to mniej wicej tak: procedure A; procedure B; begin end; begin end;

Z powyszego zapisu wynika, e procedura lub funkcja zagniedona (w tym wypadku procedura B) musi zosta umieszczona przed blokiem begin.

Wasne typy danych


Object Pascal umoliwia deklarowanie wasnych typw danych, ktre nastpnie mona wykorzysta w programie. Wasny typ danych mona zadeklarowa za porednictwem sowa kluczowego type. Przykadowa deklaracja wasnego typu mogaby wyglda w ten sposb: type TMediumValue = 0..20;

W wiatku programistw Delphi przyjo si ju, e kady nowy typ danych poprzedzony jest du liter T ja nie zamierzam od tej reguy odstpowa. Od tej pory moemy w swoim programie uywa wasnego typu danych TMediumValue, ktry to typ moe przybra wartoci liczbowe od 0 do 20. Wykorzystanie takiego typu danych (stworzenie zmiennej) jest ju proste: var MediumValue : TMediumValue;

Mona ju korzysta z naszej zmiennej, tak jak z kadej innej: begin MediumValue := 10; end. 58 | S t r o n a

Moliwe jest take zadeklarowanie swojego wasnego typu wygldajcego tak: TSamochody = (tsFiat, tsMercedes, tsOpel);

Po utworzeniu zmiennej wskazujcej na ten typ bdzie ona moga zawiera jedn z podanych w nawiasie wartoci.

Tablice jako nowy typ


Moliwe jest przekazywanie caych tablic jako parametrw do funkcji lub procedury. Jednak nie da si tego uczyni bezporednio naley w tym celu utworzy nowy typ danych, np. taki: type TMyArray = array[0..20] of String;

Nastpnie mona dopiero przekaza go jako parametr, tak jak to przedstawiono w poniszym przykadzie: program TypeArray; {$APPTYPE CONSOLE} type TMyArray = array[0..20] of String; procedure GetArray(MyArray : TMyArray); begin { odebranie tablicy } end; var MyArray : TMyArray; begin MyArray[0] := 'Element 1'; { ... } end.

Taki program powinien zosta skompilowany i zadziaa bez adnych problemw.

59 | S t r o n a

Aliasy typw
Aliasy su do tworzenia nowego typu, ktry w rzeczywistoci wskazuje na inny i jest mu rwnowany: Type TMojTyp = Integer;

Od tego momentu TMojTyp bdzie rwnowany typowi Integer. Z aliasw moesz korzysta, jei chcesz zwikszy czytelno kodu swojego programu. Oglnie rzecz biorc, nie jest to czsto uywana funkcja.

Rekordy
Rekordy to zorganizowana struktura danych, poczona w jedn cao. Jest to jakby paczuszka zawierajca okrelone elementy. Rekordy rwnie moesz przekazywa jako paczuszk do funkcji czy procedur w formie parametru. Nowe rekordy deklarowa mona jako nowy typ danych lub jako zmienn, z uyciem sowa kluczowego record. type TMyRecord = record X : Integer; Y : Integer; end;

Budowa, jak widzisz, jest specyficzna najpierw naley wpisa nazw rekordu, a dalej po znaku rwnoci sowo kluczowe record (uwaga, brak rednika na kocu!). Nastpnie wypisujemy elementy, z ktrych nasz rekord ma si skada. Jako e zadeklarowaem rekord jako nowy typ danych, naley utworzy dodatkowo zmienn wskazujc na ten typ. Przy tej okazji poznasz nowy operator Object Pascala kropk (.). Do poszczeglnych pl rekordu odwoujemy si w ten sposb:
MyRecord.X := 1;

Po uprzednim utworzeniu zmiennej wskazujcej na rekord.

60 | S t r o n a

Przekazywanie rekordw jako parametr procedury


Napiszmy prosty program, ktrzy pobierze dwie liczby wpisane przez uytkownika i przekae do procedury cay rekord. Samo przekazanie rekordu do funkcji przebiega w sposb do prosty: type TMyRecord = record X : Integer; Y : Integer; end; function Dzielenie(MyRecord : TMyRecord) : Integer; begin Result := MyRecord.X div MyRecord.Y; end; Delphi wymaga, aby do funkcji Dzielenie przekazany zosta rekord TMyRecord. Kolejno nastpuje podzielenie elementu X przez element Y rekordu i zwrcenie wartoci dzielenia. Cay program wyglda tak, jak na listingu 2.9. Listing 2.9. Rekord przekazany jako parametr procedury program Recordapp; {$APPTYPE CONSOLE} uses SysUtils; type TMyRecord = record X : Integer; Y : Integer; end; function Dzielenie(MyRecord : TMyRecord) : Integer; begin Result := MyRecord.X div MyRecord.Y; end; var MyRecord : TMyRecord; Result : Integer; begin Writeln('Podaj pierwsz liczb'); Readln(MyRecord.X);

61 | S t r o n a

Writeln('Podaje drug liczb'); Readln(MyRecord.Y); Result := Dzielenie(MyRecord); Writeln('Rezultat dzielenia ' + IntToStr(Result)); Readln; end.

Pobranie danych realizuje polecenie Readln, ale o tym mwilimy ju na pocztku tego rozdziau. W powyszym listingu skorzystaem z funkcji konwersji IntToStr. O konwersji bdzie mowa w podpunkcie Konwersja typw.

Deklaracja rekordu jako zmienna


Nie jest konieczne tworzenie nowego typu dla rekordu. Oznacza to, e zamiast deklarowa kolejny rekord jako nowy typ (type), mona zadeklarowa go jako zmienn: var Rec : record X, Y : Integer; end;

Wwczas nie jest konieczne tworzenie nowej zmiennej od razu mona zabra si za przypisywanie danych do elementw rekordu.

Instrukcja packed
W celu zapewnienia szybszego dziaania rozmiary rekordw s zaokrglane do wartoci 8 bajtw. Oznacza to, e po zsumowaniu wszystkich elemenw rekordu i okreleniu, ile miejsca zajmie on w pamici cao zaokrglana jest dodatkowo do 8 bajtw. Umieszczenie instrukcji packed podczas deklaracji rekordu powoduje, e zostanie on skompresowany. Minusem takiej kompresji jest wolniejsze dziaanie. type TMyRec = packed record X, Y : Integer; end;

62 | S t r o n a

Instrukcja wica with


Instrukcja with jest przewanie uywana wraz z rekordami lub obiektami (o obiektach bdzie mowa w nastpnym rozdziale). Nie peni ona adnej znaczcej roli uwalnia za to programist od pisania zbdnego kodu. Zamy, e program zawiera nastpujcy rekord: var Rec : packed record X, Y : Integer; Imie : String[20]; Nazwisko : String[20]; Wiek : Byte; end;

Prawidowe wypenienie rekordu przedstawione jest poniej: Rec.X := 12; Rec.Y := 24; Rec.Imie := 'Jan'; Rec.Nazwisko := 'Kowalski'; Rec.Wiek := 20;

Dziki zastosowaniu instrukcji wicej with kod ten mona skrci do takiej postaci: with Rec do begin X := 12; Y := 24; Imie := 'Jan'; Nazwisko := 'Kowalski'; Wiek := 20; end;

Moliwe jest okrelenie dugoci zmiennej String, co przedstawiem w powyszym przykadzie. Wystarczy w deklaracji wpisa w nawiasach kwadratowych maksymaln dugo, jak moe posiada zmienna.

63 | S t r o n a

Moduy
Modu (ang. unit) jest plikiem tekstowym, zawierajcym polecenia interpretowane przez kompilator. Podzia programu na moduy pojawi si po raz pierwszy z jednej z wczeniejszych wersji Turbo Pascala. Zastosowanie moduw pozwala na podzia kodu na osobne pliki. Przypomnij sobie przez chwil poprzedni rozdzia. Podczas tworzenia pierwszego programu i zapisywania pierwszego projektu na dysku zosta zapisany plik *.pas. Jest to wanie plik moduu. Kademu formularzowi odpowiada jeden modu, ale z kolei modu nie musi by formularzem. Wyobra sobie wic, e w swoim programie masz kilka formularzy i w zwizku z tym wiele plikw; ponadto czsto korzystasz z jednej procedury. W takim wypadku w kadym z tych formularzy musiaby umieszcza ow procedur. Zastosowanie w programie moduw zmienia t sytuacj, o czym za chwil si przekonasz.

Tworzenie nowego moduu


1. Utwrz nowy projekt. 2. Zamknij Projektanta formularzy oraz Edytor kodu i doprowad gwny plik DPR do takiej postaci: program UnitApp; begin end.

3. Z menu File wybierz polecenie New/Unit. Spowoduje to stworzenie w Edytorze kodu nowej zakadki (nowego moduu). 4. Z menu File wybierz polecenie Save As plik zapisz pod nazw MainUnit.

Budowa moduu
Zaraz po stworzeniu nowego moduu Delphi generuje potrzebne instrukcje, ktre s niezbdnym elementem tego moduu. unit MainUnit; interface implementation end.

64 | S t r o n a

Nazwa Pierwszy wiersz zawiera instrukcj unit, ktra okrela nazw moduu. Nie naley jej zmienia Delphi generuje t nazw automatycznie, w momencie zapisywania pliku na dysku. Podczas prby zmiany nazwy moduu wystpi bd: [Error] MainUnit.pas(1): Unit identifier 'MainUnitx' does not match file name.

Sekcja Interface Jest to tzw. cz publiczna moduu. Tutaj naley umieszcza deklaracje procedur i funkcji, ktre maj by widoczne na zewntrz, dla innych moduw. W tym momencie naley rozrni pewne pojcia, takie jak deklaracja oraz definicja. Deklaracja jest udostpnieniem jedynie nagwka funkcji lub procedury. Dziki temu procedury i funkcje s widoczne dla innych moduw. Np. deklaracja procedury ProcMain moe wyglda tak: procedure ProcMain(Param1, Param2 : Integer);

Definicja to cay kod procedury lub funkcji, zawarty w sekcji Implementation.

Sekcja Implementation Sekcja Implementation stanowi cz prywatn moduu. Kod w niej zawarty w (procedury, funkcje, zmienne, tablice czy stae) nie jest widoczny dla innych moduw z punktu widzenia kompilatora dane zawarte w tej sekcji nie istniej. W sekcji Implementation naley take umieszcza definicj procedur i funkcji. Przykad prawidowego moduu (deklaracji i definicji): unit MainUnit; interface procedure ProcMain(Param1, Param2 : Integer); implementation procedure ProcMain(Param1, Param2 : Integer); begin end; end.

65 | S t r o n a

Pamitaj o tym, e rwnie modu musi by zakoczony instrukcj end. (kropka na kocu!).

Wczanie moduu
Podczas poprzedniego wiczenia, ktrego celem byo utworzenie nowego moduu, Delphi zwolni nas z obowizku wczenia tego moduu do gwnego pliku *.dpr. Wczenie moduu do programu (lub do innych moduw) realizowane jest za pomoc dyrektywy uses. uses MainUnit in 'MainUnit.pas';

Od tej pory wszystkie deklaracje z moduu MainUnit bd widoczne take w obrbie programu gwnego. Taka, wygenerowana przez Delphi, konstrukcja nie jest jedyn poprawn konstrukcj. Wystarczy, jeli napiszesz tylko: uses MainUnit;

Jeeli bdziesz chcia wczy do programu kilka moduw, wypisz je jeden po drugim, oddzielajc ich nazwy przecinkami.

Sekcja Initialization oraz Finalization


Dwie wspomniane sekcje Initialization oraz Finalization s opcjonalnymi sekcjami moduu. Umieszczenie kodu bezporednio za sekcj Initialization powoduje wykonanie go zaraz po uruchomieniu programu. Natomiast kod znajdujcy si za sekcj Finalization wykonany zostanie po zakoczeniu pracy z moduem. Przykad przedstawiono w listingu 2.10. Listing 2.10. Wykorzystanie sekcji Initialization oraz Finalization unit MainUnit; interface uses Dialogs; // wczamy modu Dialogs procedure ProcMain(Param1, Param2 : Integer); implementation

66 | S t r o n a

procedure ProcMain(Param1, Param2 : Integer); begin end; initialization ShowMessage('Rozpoczynamy prac z moduem...'); finalization ShowMessage('Koczymy prac z moduem...');

end.

Po uruchomieniu programu zostanie wywietlone okienko dialogowe. Tak samo stanie si po zamkniciu naszej aplikacji. Najczciej sekcje Initialization oraz Finalization uywane s do przydzielania lub zwalniania danych (pamici), przydzielanych w ramach konkretnego moduu. Jeeli w programie korzystasz z sekcji Finalization, to koniecznym staje si umieszczenie take sekcji Initialization. W przeciwnym wypadku prba uruchomienia programu zakoczy si bdem: [Error] MainUnit.pas(17): Declaration expected but 'FINALIZATION' found. Sekcja Initialization moe nie zawiera adnego kodu istotna jest tylko jej obecno w kodzie.

Konwersja typw
rodowisko Delphi zostao tak zaprojektowane, aby nie dopuszcza do sytuacji, w ktrej zmienna typu Integer jest przekazywana np. do procedury ShowMessage, ktra jako parametru wymaga danych typu String. Jednak tu z pomoc przychodz nam funkcje konwersji, ktre pozwalaj przekonwertowa dane na inny typ. W rezultacie aby przekaza do procedury ShowMessage zmienn typu Integer, wystarczy umieci w kodzie taki zapis: ShowMessage(IntToStr(Zmienna_Integer));

Polecenie IntToStr powoduje konwersj danych w postaci Integer na format String. Funkcje konwersji, przedstawione w tabeli 2.3, zadeklarowane s w module SysUtils koniecznym staje si wic wczenie nazwy tego moduu do listy uses.

67 | S t r o n a

Nazwa IntToStr StrToInt CurrToStr StrToCurr DateTimeToStr StrToDateTime DateToStr StrToDate TimeToStr

Opis Konwertuje typ Integer na String Konwertuje typ String na Integer Konwertuje typ Currency na String Konwertuje typ String na Currency Konwertuje typ TDateTime na String Konwertuje typ String na TDateTime Konwertuje typ TDate na String Konwertuje typ String na TDate Konwertuje typ TTime na String

TimeStrToTimeToStr Konwertuje typ String na TTime FloatToStr StroToFloat IntToHex StrPas String PChar StrToBool StrToInt64 Konwertuje typ Extended na String Konwertuje typ String na Extended. Konwertuje typ Integer do postaci heksydemalnej. Konwertuje typ String na PChar. Konwertuje typ PChar na String. Konwertuje typ String na PChar. Konwertuje typ String na Boolean. Konwertuje typ String na Int64.

Przyznasz, e tych funkcji jest sporo. Nie musisz ich wszystkich pamita zawsze moesz sign do tej ksiki. Jednak wikszo z tych nazw jest intuicyjna i stanowi tylko skrt do konwertowanych typw.

68 | S t r o n a

Rzutowanie
Przy rzutowaniu naley zachowa szczegln ostrono. Jest to bowiem sposb na oszukanie kompilatora. Jeeli nie jeste pewien, co robisz, moesz w konsekwencji doprowadzi do wystpienia powanych bdw podczas dziaania programu. Najlepiej omwi to na przykadzie. Oto prosty kod rdowy, ktry na pewno nie zostanie prawidowo skompilowany: var VarC : Char; VarB : Byte; begin VarC := 'A'; VarB := VarC; end.

Dane w postaci Char (pojedynczy znak) prbujemy tu przypisa do zmiennej VarB, ktra jest zmienn typu Byte. Oczywicie kompilator wskae bd: [Error] typcast.dpr(12): Incompatible types: 'Byte' and 'Char'. Po drobnej modyfikacji cay program zostanie prawidowo skompilowany i zadziaa bez problemu: var VarC : Char; VarB : Byte; begin VarC := 'A'; VarB := Byte(VarC); // < rzutowanie end.

Rzutowaniem jest wanie przypisanie danych w ten sposb: Byte(VarC). W takim wypadku rzutujemy typ Char na Byte, w wyniku czego zmienna VarB bdzie posiada warto 65 (kod ASCII litery A). Kolejny przykad tym razem nieco praktyczniejszy, z ktrego pewnie nieraz skorzystasz. W module Windows zadeklarowana jest bardzo przydatna funkcja MessageBox. Podobnie jak polecenie ShowMessage, wywietla ona okienka informacyjne. Rnica polega na tym, e w przypadku tej funkcji mamy wiksz kontrol nad wygldem okienka. Nie to jest jednak najwaniejsze. Jako parametry tej funkcji naley poda dwie zmienne typu PChar. Typ PChar jest rwnie typem zmiennych, ktry umoliwia przechowywanie tekstu, lecz z punktu widzenia kompilatora s to osobne rodzaje danych. Jednak w tym wypadku rzutowanie typw nie przyniesie adnych niechcianych efektw: 69 | S t r o n a

program PCharToStr; uses Windows; var lpMessage, lpCaption : String; begin lpMessage := 'Okienko informacyjne'; lpCaption := 'Zamknicie programu'; MessageBox(0, PChar(lpMessage), PChar(lpCaption), MB_OK); end . Pierwszy parametr funkcji MessageBox to tzw. uchwyt okna macierzystego. Na razie si nim nie przejmuj moesz w tym miejscu wpisa cyfr 0. Kolejny parametr to tekst znajdujcy si w okienku informacyjnym. Jak widzisz, konieczne jest rzutowanie na typ PChar. Trzecim parametrem jest tytu okienka (napis na pasku tytuowym), a ostatni parametr okrela przyciski znajdujce si w oknie. Wicej na temat funkcji MessageBox moesz dowiedzie si z pliku pomocy Delphi. Ja chciaem wspomnie tylko o tym, e ostatnie parametry mog by ze sob czone za pomoc operatora +. Na przykad takie wywoanie: MessageBox(0, PChar(lpMessage), PChar(lpCaption), MB_YESNOCANCEL + MB_ICONWARNING );

spowoduje pojawienie si przyciskw Yes, No, Cancel oraz ikonki ostrzeenia (ang. warning).

Ptle
W wiatku programistw pod sowem ptla kryje si pojcie oznaczajce cige wykonywanie tych samych czynnoci. Jest to bardzo wany element kadego jzyka programowania, dlatego konieczne jest zrozumienie istoty dziaania tego elementu. Wyobra sobie sytuacj, w ktrej musisz kilka razy wykona t sam czynno, na przykad wywietlenie kilku linii tekstu. Zamiast kilkakrotnie pisa instrukcj Writeln, mona skorzysta z ptli.

70 | S t r o n a

Ptla for..do
Jest to chyba najprostsza z moliwych ptli. Uywaj jej zawsze wtedy, gdy wiesz dokadnie, ile wykona (iteracji) danej czynnoci chcesz zastosowa. Podczas korzystania z ptli for musisz zadeklarowa zmienn, ktra za kadym wykonaniem danej czynnoci bdzie przybiera warto aktualnej iteracji. eby to lepiej zrozumie, spjrz na przykadow budow ptli: for Zmienna := 1 to 10 do { instrukcje }

Pierwsz instrukcj musi by sowo for. Po nim nastpuje nazwa zmiennej, ktra musi przybra warto pocztkow. Nastpnie kolejne sowo kluczowe to, po ktrym nastpuje warto kocowa ptli. Powysza konstrukcja spowoduje 10-krotne wykonanie polece umieszczonych po sowie do. Podsumowujc, budowa ptli przedstawia si nastpujco: for Zmienna := {Warto pocztkowa} to {Warto kocowa} do {instrukcje }

Przykadowy program wywietla na ekranie konsoli nastpujcy tekst:


Odliczanie 1 Odliczanie 2 ...

W celu zrealizowania tego zadania bez korzystania z ptli naleaoby 10 razy przepisa instrukcj Writeln, co jest po prostu strat czasu. program ForLoop; {$APPTYPE CONSOLE} uses SysUtils; var I : Integer; // deklaracja zmiennej begin for I := 1 to 10 do Writeln('Odlicznie... ' + IntToStr(i)); Readln; end.

Uruchom program i sprawd jego dziaanie. Pozmieniaj warto pocztkow i kocow, aby sprawdzi, jakie bdzie zachowanie programu. 71 | S t r o n a

Odliczanie od gry do dou Ptla, jak przedstawiem wyej, realizuje odliczanie od dou (wartoci mniejszej) do gry (warto wysza). Istnieje moliwo odliczania odwrotnego, czyli od wartoci wyszej do mniejszej. W tym celu sowo kluczowe to naley zastpi sowem downto. Wystarczy zatem drobna zmiana ptli: for I := 10 downto 1 do Writeln('Odlicznie... ' + IntToStr(i));

Teraz program wykona ptl z wartoci pocztkow rwn 10. Z ptl for wie si jedno ostrzeenie kompilatora. Ot w przypadku, gdy ptla for znajduje si wewntrz procedury lub funkcji, zmienna pomocnicza musi by zmienn lokaln. Przykadowo prba skompilowania takiego kodu: var I: Integer; // deklaracja zmiennej procedure Loop; begin for I := 0 to 100 do { instrukcje } end;

wie si z wystpieniem ostrzeenia: [Warning] LoopBreak.dpr(10): For loop control variable must be simple local variable. Kompilator prbuje Ci ostrzec, i zmienna (w tym przypadku I) moe by modyfikowana przez inne procedury, a to nie jest zalecane. Umieszczenie deklaracji zmiennej wewntrz procedury Loop pozwoli na prawidow kompilacj programu.

Ptla while..do
Niekiedy nie jeste w stanie okreli, ile iteracji bdzie wymagaa ptla; moe nie bdzie wymagana adna iteracja, a moe potrzebne bd ich setki W takim przypadku naley skorzysta z ptli while. Budowa takiej ptli jest nastpujca: while {Warunek do spenienia} do { instrukcje }

Ptla bdzie wykonywana, dopki warunek zapisany pomidzy sowami kluczowymi while i do nie zostanie speniony. Napiszmy prosty program, polegajcy na pobieraniu hasa dostpu. Jeeli haso bdzie bdne, ptla zostanie wykonana po raz drugi; jeeli haso bdzie poprawne nastpi zakoczenie programu.

72 | S t r o n a

program WhileLoop; {$APPTYPE CONSOLE} uses SysUtils; var Password : String; // deklaracja zmiennej begin while Password <> 'delphi7' do begin Writeln('Podaj haso...'); Readln(Password); end; Writeln('Haso poprawne!'); Readln; end.

Omwi pokrtce program. Na samym pocztku umieszczamy warunek: while Password <> 'delphi7' do

Za jego pomoc sprawdzamy, czy zmienna Password jest rna od delphi7 jeeli tak jest, nastpuje wykonanie instrukcji ptli znajdujcej si w bloku begin..end. Jeeli wpisane haso jest niepoprawne, wykonywana zostaje kolejna iteracja; w przeciwnym wypadku ptla koczy swoje dziaanie.

Ptla repeat..until
Efekt zastosowania ptli repeat jest bardzo podobny do dziaania ptli while ptla ta take moe by wykorzystywana w nieskoczono. Jedyna rnica polega na tym, e w ptli repeat warunek zakoczenia sprawdzany jest dopiero na kocu wykonania. Oznacza, to, e ptla repeat zawsze bdzie wykonana co najmniej raz. Dopiero po tej iteracji program sprawdzi, czy mona wyj z ptli. W przypadku ptli while warunek sprawdzany jest bezporednio przed jej wykonaniem, co w rezultacie moe spowodowa, e taka ptla nigdy nie zostanie wykonana. Budowa ptli repeat jest nastpujca:

73 | S t r o n a

repeat { instrukcje do wykonania } until { warunek zakoczenia }

Ptla repeat, uyta w poprzednim przykadzie, wygldaaby nastpujco: repeat Writeln('Podaj haso...'); Readln(Password); until Password = 'delphi7';

Rezultat dziaania byby identyczny. Z ptlami wie si niebezpieczestwo zaptlenia. Naley uwaa, aby program nie wykonywa stale tych samych czynnoci moe do tego doj, jeeli warunek zakoczenia nigdy nie zostanie speniony.

Procedura Continue
Polecenie Continue moe by uywane tylko wraz z ptlami. Powoduje ono przejcie do nastpnego wywoania ptli bez wykonywania dalszych instrukcji. Oczywicie najlepiej istot dziaania procedury Continue poznasz na przykadzie. Zamy, e mamy ptl for, ktra zostanie wykonana 10 razy. Za kadym razem program losuje jak liczb z przedziau od 1 do 3 i na podstawie tej wylosowanej liczby wywietla jaki tekst. Dziki poleceniu Continue moemy sprawi, aby w przypadku, gdy wylosowan liczb bdzie 1, omin wywietlenie tekstu i przej do nastpnej iteracji (listing 2.11.). Listing 2.11. Zastosowanie instrukcji Continue w ptli for program LoopContinue; {$APPTYPE CONSOLE} var I, Number : Integer; // deklaracja zmiennej begin Randomize; for I := 1 to 10 do begin Number := Random(3)+1; 74 | S t r o n a

if Number = 1 then Continue; case 1: 2: 3: end; end; Number of Writeln('Uuu, wylosowae 1'); Writeln('No, dwa... jeszcze moe by'); Writeln('Dobrze');

Readln; end.

Interesujcy nas warunek znajduje si w tym miejscu: if Number = 1 then Continue;. Kompilator odczytuje to tak: jeeli zmienna Number zawiera warto 1, pomi wykonywanie dalszych instrukcji i przejd od razu do kolejnej iteracji.

Procedura Break
Polecenie Break rwnie moe by wykonane tylko w poczeniu z ptlami. W odrnieniu od procedury Continue umoliwia ono wyjcie z ptli (opuszczenie jej). Po napotkaniu instrukcji Break dalsze wykonywanie ptli zostaje wstrzymane, a program wykonuje polecenia umieszczone po ptli. Zmodyfikujmy ostatni przykad, zastpujc procedur Continue poleceniem Break: program LoopBreak; {$APPTYPE CONSOLE} var I, Number : Integer; // deklaracja zmiennej begin Randomize;

for I := 1 to 10 do begin Number := Random(3)+1; if Number = 1 then begin Writeln('Wylosowano 1 opuszczamy ptle...'); Break; end;

75 | S t r o n a

case 1: 2: 3: end; end;

Number of Writeln('Uuu, wylosowae 1'); Writeln('No, dwa... jeszcze moe by'); Writeln('Dobrze');

Readln; end.

Jeeli program wylosuje cyfr 1, program wywietli stosown informacj i zakoczy dziaanie ptli. aden kod umieszczony poniej instrukcji Break nie zostanie wykonany.

Zbiory
Zbiory s kolekcj danych tego samego typu. To zdanie zapewne niezbyt wyjania funkcj zbiorw, spjrz wic na ten kod: type TCar = (tcFiat, tcSkoda, tcOpel, tcFerrari, tcPorshe, tcPeugeot); TCarSet = set of TCar;

W drugim wierszu znajduje si deklaracja nowego typu danych TCar. Zmienna korzystajca z tego typu moe zawiera jedn z wartoci podanych w nawiasie. Natomiast typ TCarSet jest zbiorem danych TCar. Nowy zbir deklaruje si za pomoc konstrukcji set of. Jak ju mwiem, zbiory s konstrukcj mogc zawiera elementy okrelonych danych. Znaczy to, e zmienna korzystajca z typu TCarSet moe zawiera np. wszystkie elementy lub tylko kilka spord nich. Przy wykorzystaniu zwykych zmiennych nie jest to moliwe, gdy do takiej zmiennej mona przypisa tylko jeden element TCar. Moliwa jest take deklaracja bezporednia, czyli deklaracja bez tworzenia dodatkowego typu TCar: type TCarSet = set of (tcFiat, tcSkoda, tcOpel, tcFerrari, tcPorshe, tcPeugeot);

Zbiory mog by rwnie zbiorami liczbowymi lub zawierajcymi pojedyncze znaki: program Sets; {$APPTYPE CONSOLE} type 76 | S t r o n a

TCarSet = set of (tcFiat, tcSkoda, tcOpel, tcFerrari, tcPorshe, tcPeugeot); TNumberSet = set of 0..9; TCharSet = set of 'A'..'Z';

Przypisywanie elementw zbioru


Chcc przypisa elementy zbioru do danej zmiennej, trzeba skorzysta z nawiasw kwadratowych. var CarSet : TCarSet; begin CarSet := [tcSkoda, tcOpel];

Powyszy przykad powoduje przypisanie do zbioru dwch elementw tcSkoda i tcOpel. Moliwe jest oczywicie stworzenie kilku zmiennych korzystajcych z danego zbioru: var Tanie, Srednie, Drogie : TCarSet; begin Tanie := []; Srednie := [tcFiat, tcSkoda, tcOpel, tcPeugeot]; Drogie := [tcPorshe, tcFerrari]; end.

Do zmiennej Tanie przypisujemy zbir pusty po prostu nie zawiera on elementw, symbolizuj go wic jedynie dwa nawiasy.

Odczytywanie elementw ze zbioru


Wraz ze zbiorami czsto uywany jest operator in. Suy on do sprawdzania, czy dany element naley do okrelonego zbioru. Przykadowo: if (tcFiat in Cars) then { czynnoci }

Zwr uwag na konstrukcj. Na pocztku naley wpisa nazw elementu, a dopiero pniej zmienn wskazujc na zbir. Jeeli dany element naley do zbioru, wykonany zostanie kod znajdujcy si po sowie then.

77 | S t r o n a

Zaprzeczanie Moesz zapyta: co stanie si, gdy chcemy sprawdzi, czy dany element nie naley do zbioru ??. W takim wypadku zamiast nie moemy operatora in wpisa out, ale moliwe jest zastosowanie operatora not, ktry jest zaprzeczeniem (o operatorach pisaem nieco wyej). if not (tcFiat in Cars) then { czynnoci }

Jeeli element tcFiat nie naley do zbioru Cars, warunek zostanie speniony.

Przekazywanie zbioru jako parametru procedury Czsto podczas programowania w Object Pascalu natkniesz si na konstrukcj, ktra wymaga przekazania zbioru jako parametru procedury. Jeeli wic podczas kompilacji wywietlony zostanie bd: [Error] Sets.dpr(20): Incompatible types: 'TCarSet' and 'Enumeration', bdzie to oznaczao, e podany parametr musi by zbiorem, czyli musi by przekazany w nawiasach kwadratowych. Oto przykad takiego programu: program Sets; {$APPTYPE CONSOLE} type TCarSet = set of (tcFiat, tcSkoda, tcOpel, tcFerrari, tcPorsche, tcPeugeot); procedure CoKupujemy(Cars : TCarSet); begin if (tcFiat in Cars) then Writeln('Kupujemy Fiata!'); if (tcSkoda in Cars) then Writeln('Kupujemy Skode!'); if (tcOpel in Cars) then Writeln('Kupujemy Opla!'); if (tcFerrari in Cars) then Writeln('Kupujemy Ferrari!'); if (tcPorsche in Cars) then Writeln('Kupujemy Porsche!'); if (tcPeugeot in Cars) then Writeln('Kupujemy Peugeota!'); end; begin CoKupujemy([tcPorsche, tcFerrari]); Readln; end.

78 | S t r o n a

Dodawanie i odejmowanie elementw zbioru


W celu dodania do zbioru lub odjcia z niego jakiego elementu mona skorzysta z operatorw + i . Trzeba to jednak zapisa w specyficzny sposb: CarSet := CarSet + [tcFiat];

Za pomoc tego polecenia dodajemy do zbioru element tcFiat. Taka konstrukcja jest wymagana, gdy gdybymy napisali tak: CarSet := [tcFiat];

spowodowaoby to wymazanie elementw poprzednio znajdujcych si w zbiorze i dodanie jedynie tcFiat.

Include i Exclude Zalecan metod dodawania oraz odejmowania elementw zbioru s funkcje Include oraz Exclude. Pierwsza z nich wcza element do zbioru, a druga odejmuje. Ich uycie jest zalecane ze wzgldu na to, e dziaaj o wiele szybciej ni operacje z zastosowaniem znakw + i . Przykad uycia: Include(CarSet, tcFiat); // dodawanie Exclude(CarSet, tcPorshe); // odejmowanie

Wskaniki
Wskaniki to najtrudniejsza do opanowania czynno programistyczna zarwno w Object Pascalu, jak i w innych jzykach programowania. Zazwyczaj podczas przypisywania danych do zmiennej Delphi rezerwuje obszar w komrkach pamici i tam umieszcza dane zmiennej. Gdy chcemy w programie odwoa si do zmiennej, Delphi na podstawie jej nazwy odszukuje komrk pamici, w ktrej umieszczone s dane. Wskaniki to zmienne, ktre wskazuj na inn zmienn. Zapewne powysza wskazwka niewiele Ci wyjania. Wskaniki to specjalny typ danych w pamici nie s przechowywane dane jako takich, lecz jedynie odpowiadajce im adresy komrki pamici.

79 | S t r o n a

Tworzenie wskanika
Zadeklarowania wskanika dokonuje si za pomoc operatora (^). var P : ^String;

Od tego momentu w programie moemy korzysta ze wskanika P, wskazujcego typ String. We wszelkich operacjach dokonywanych na wskanikach musz by wykorzystywane dwa operatory specyficzne jedynie dla typw wskanikowych s to operatory ^ oraz @. Ich znaczenie poznasz w dalszej czci tego rozdziau.

Przydzia danych do wskanikw


Na samym pocztku przeprowadmy pewien test. Sprbuj uruchomi taki program: program Pointers; var P : ^String; begin P^ := 'Delphi 7'; end.

Program prbuje przypisa okrelone dane do wskanika w postaci acucha tekstowego; musi to si odby z wykorzystaniem operatora ^, w przeciwnym wypadku Delphi zasygnalizuje bd: [Error] Pointers.dpr(12): Incompatible types: 'String' and 'Pointer'. Prba uruchomienia takiego programu zakoczy si jednak bdem typu Runtime (patrz rysunek 2.2).

Rysunek 2.2. Komunikat o bdzie wywietlony po uruchomieniu programu Przyczyn bdu jest fakt, e do typu wskanikowego nie mona przypisa w normalny sposb wartoci. Wskaniki musz uprzednio wskazywa na inn, zwyk zmienn. Przydzielaniem danych bezporednio do wskanika zajmiemy si w dalszej czci tego rozdziau. 80 | S t r o n a

Poniszy program zostanie skompilowany i, co najwaniejsze, zadziaa bez problemu: program Pointers; var S : String; P : ^String; begin S := 'Delphi'; // przypisanie danych do zwykej zmiennej P := @S; // uzyskanie adresu zmiennej P^ := 'Borland Delphi 7 Studio'; // modyfikacja danych end.

Uzyskiwanie adresw zmiennej Jak wida w powyszym przykadzie, po zadeklarowaniu zmiennej S (typu String) wskanik musi uzyska jej adres. Realizuje to operator @. Od tego momentu wskanik P wskazuje na zmienn S. Ponisza instrukcja: P^ := 'Borland Delphi 7 Studio';

w rzeczywistoci spowoduje zmian wartoci zmiennej S! Dzieje si tak dlatego, e wskanik P wskazuje na zmienn S. A zatem zmieniajc warto wskanika, w rzeczywistoci zmieniamy warto zmiennej! Moesz to sprawdzi, dodajc na kocu programu jeszcze jedn instrukcj: MessageBox(0, PChar(S), '', 0);

W okienku informacyjnym bdzie widnia napis Borland Delphi 7 Studio.

Do czego to suy?
To jest dobre pytanie! Mona si zastanowi, do czego su wskaniki? . Zaoenie jest takie, e podczas tworzenia jakich struktur zarwno tablic, jak i rekordw nie jest konieczne manipulowanie wielkimi blokami pamici. Wystarczy tylko stworzy wskanik tego rekordu i ewentualnie modyfikowa w ten sposb dane, zamiast tworzy kolejn instancj (kopi) rekordu.

81 | S t r o n a

Tworzenie wskanikw na rekordy Wanie teraz przedstawi Ci przykad tego, o czym mwiem wczeniej. Podczas tworzenia jakiego rekordu wskazane jest utworzenie nowego typu wskanikowego, wskazujcego na ten rekord. Po wypenieniu danych do procedury jest przekazywany jedynie wskanik tego rekordu: program PRecApp; uses Dialogs; type TInfoRec = packed record FName : String[30]; SName : String[30]; Age : Byte; Pesel : Int64; Nip : String[60] end; PInfoRec = ^TInfoRec; // utworzenie wskanika procedure SomeProc(InfoRec : PInfoRec); begin ShowMessage('Dotychczasowa warto InfoRec.FName to ' + InfoRec.FName + '. Zmieniam na Adam'); InfoRec.FName := 'Adam'; // zmiana danych end; var InfoRec: TInfoRec; begin InfoRec.FName := 'Jan'; InfoRec.SName := 'Kowalski'; InfoRec.Age := 41; InfoRec.Pesel := 55012010013; InfoRec.Nip := '342342343223423'; SomeProc(@InfoRec); ShowMessage(InfoRec.FName); // wywietlenie zmienionej wartoci end.

Wskanik odczytuje dane rekordu InfoRec z pamici moliwa jest take zamiana tych danych, co zaprezentowaem w powyszym listingu.

82 | S t r o n a

Przydzia i zwalnianie pamici


Na samym pocztku omawiania wskanikw zaprezentowaem przykad, w ktrym prbowalimy przydzieli dane do wskanika. Uruchomienie tamtego programu skoczyo si bdem, dlatego e nie zaalokowalimy pamici dla tych wskanikw. Pami mona zaalokowa (przydzieli) za pomoc funkcji New. Stos to cay obszar pamici rezerwowany dla aplikacji w trakcie jej uruchamiania. Sterta to caa dostpna pami komputera oraz ilo wolnego miejsca na dysku komputera. Po wywoaniu funkcji New program automatycznie alokuje w sposb dynamiczny pami dla rekordu. Po skoczeniu pracy z rekordem pami naley zwolni za pomoc polecenia Dispose (listing 2.12.) Listing 2.12. Przydzia pamici dla rekordu program NewPointer; uses Dialogs; type TInfoRec = packed record FName : String[30]; SName : String[30]; Age : Byte; Pesel : Int64; Nip : String[60] end; PInfoRec = ^TInfoRec; // utworzenie wskanika

var InfoRec: PInfoRec; begin New(InfoRec); InfoRec^.FName := 'Jan'; InfoRec^.SName := 'Kowalski'; InfoRec^.Age := 41; InfoRec^.Pesel := 55012010013; InfoRec^.Nip := '342342343223423'; ShowMessage(InfoRec^.FName); // wywietlenie zmienionej wartoci

83 | S t r o n a

Dispose(InfoRec); end.

W celu zaalokowania pamici mona posuy si take procedurami GetMem i FreeMem. Funkcja GetMem wymaga wpisania dodatkowego parametru, jakim jest ilo bajtw przeznaczonych do alokacji. Dane te uzyskujemy, wywoujc funkcj SizeOf np.: GetMem(InfoRec, SizeOf(InfoRec));

Zalecane jest jednak uycie funkcji New i Dispose.

Warto pusta
Nieraz podczas programowania spotkasz si z instrukcj nil. Instrukcja ta uywana jest wraz ze wskanikami i oznacza warto pust. Wskaznik := nil;

Taki zapis spowoduje, e do wskanika nie bd aktualnie przypisane adne wartoci.

Pliki doczane
Idea plikw doczanych jest bardzo prosta polega na wczeniu w odpowiednim miejscu moduu pliku tekstowego, ktry jest traktowany jak integralna jego cz. Plik doczany to nic innego, jak zwyky plik tekstowy. Z menu File wybierz pozycje New/Other. W oknie dialogowym kliknij ikon Text. W Edytorze kodu pojawi si nowa zakadka zapisz ten plik pod nazw SHOW.INC, ale uprzednio wpisz w Edytorze kodu prost instrukcj: ShowMessage('Hello World!');

Plik gwny DPR powinien wyglda tak: program IncludeInc; uses Dialogs; begin 84 | S t r o n a

{$I SHOW.INC} end.

Dziki dyektywie {$I} mona wczy plik do programu; bdzie to rwnowane wstawieniu w tym miejscu zawartoci owego pliku SHOW.INC.

Etykiety
Chocia wielu programistw sprzeciwia si uywaniu etykiet, ja omwi tutaj t technologi, gdy nieraz moesz by zmuszony do skorzystania z niej. Etykiety, mwic prosto, to miejsce w kodzie (zakadka), do ktrego mona zawsze przej (przeskoczy). Najpierw jednak tak etykiet naley zadeklarowa np. tak, jak zmienne, tyle e za pomoc sowa kluczowego label. label Moja_Etykieta;

Dziki takiej deklaracji kompilator wie, e ma do czynienia z etykiet. Przejcie do takiej etykiety ogranicza si do wywoania sowa kluczowego goto Nazwa_Etykiety; program Labels; {$APPTYPE CONSOLE} uses SysUtils; label Moja_Etykieta; var I : Integer; begin Randomize; Moja_Etykieta: I := Random(10); Writeln('Wylosowaem ' + IntToStr(I)); if I = 5 then goto Moja_Etykieta; // jeeli komputer wylosuje 5 ponw losowanie

85 | S t r o n a

Readln; end.

Sam zakadk ustawia si, wpisujc jej nazw, a po dwukropku dalsz cz kodu. Powyszy program realizuje losowanie liczby; jeeli wylosowana zostanie liczba 5, program przechodzi do ustawionej wczeniej etykiety.

Podsumowanie
Przyznam szczerze, e dla Ciebie mg to by trudny, a zarazem niezbyt ciekawy rozdzia tej ksiki. C, przed przystpieniem do tworzenia powaniejszych aplikacji w Delphi naleao pozna podstawow skadni. Teraz masz ju solidne podstawy do dalszej nauki. Nie martw si, jeeli nie zrozumiae wszystkiego; jest to naturalne. Wiadomo, e nie jeste w stanie w tak krtkim czasie zapamita wszystkiego naraz. Powracaj do tego rozdziau w razie, gdy czego zapomnisz. Zaczniki:

Listingi_2.zip (21.20 kB)

86 | S t r o n a

Rozdzia 3
Programowanie obiektowe
Z pewnoci ten rozdzia niniejszej ksiki bdzie dla Ciebie bardziej interesujcy. Nauczysz si bowiem korzysta z dobrodziejstw, jakie oferuje Delphi, czyli komponentw i klas, dziki ktrym programowanie daje lepsze, a co najwaniejsze szybsze efekty.

VCL
Skrt VCL pochodzi od sw Visual Component Library, co mona przetumaczy jako wizualn bibliotek komponentw. Rewolucyjno Delphi polegaa wanie na zastosowaniu biblioteki VCL, ktra znacznie uatwiaa programistom szybkie tworzenie aplikacji. VCL znacznie upraszcza tworzenie programw, udostpniajc funkcje, dziki ktrym za pomoc np. jednego wiersza kodu wykona mona skomplikowane na pozr czynnoci. Przykadowo zaadowanie i wywietlenie obrazka realizowane jest za pomoc jednego wiersza: Image.Picture.LoadFromFile('C:\obrazek.bmp');

Programowanie w Delphi opiera si wanie w duym stopniu na zastosowaniu komponentw (obiektw) i odwoaniu si do ich metod lub waciwoci. Metoda to procedura lub funkcja danej klasy, ktra wykonuje jakie czynnoci. Szerzej omwi tej temat w dalszej czci rozdziau.

Podstawowy kod moduu


Stwrz nowy projekt, wybierajc z menu File polecenie New/Application. Nacinij klawisz F12, co spowoduje przeczenie si do Edytora kodu, ktrego zawarto widnieje w listingu 3.1. Listing 3.1. Podstawowy kod formularza, wygenerowany przez Delphi unit Unit1; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, 87 | S t r o n a

Controls, Forms, Dialogs; type TForm1 = class(TForm) private { Private declarations } public { Public declarations } end; var Form1: TForm1; implementation {$R *.dfm} end.

Modu ten skada si z podstawowych instrukcji, jakie powinny si w nim znajdowa nie powinny by one ju dla Ciebie obce. To, co moe przyku Twoj uwag, to dua ilo moduw wczona w ramach nowego projektu oraz nietypowe instrukcje, zadeklarowane jako nowy typ. Przedstawiony poniej kod nazywamy klas. type TForm1 = class(TForm) private { Private declarations } public { Public declarations } end;

Klasa (nie myli z komponentem!) moe zawiera metody (procedury i funkcje) stale ze sob wsppracujce w celu wykonania pewnych czynnoci. O klasach bdzie mowa w dalszej czci rozdziau na razie nie zaprztaj sobie tym gowy.

88 | S t r o n a

Plik DFM formularza


Kolejna niespotykana wczeniej instrukcja to dyrektywa: {$R *.dfm}

Nakazuje ona wczenie do moduu pliku formularza, ktry zawiera informacje dotyczce zarwno samego formularza, jak i komponentw w nim si znajdujcych. Podgld formularza uzyskuje si poprzez kliknicie prawym przyciskiem myszy w obszarze formularza i wybranie z menu podrcznego polecenia View as Text. Wwczas w edytorze kodu pojawi si kod, taki jak w listingu 3.2. Listing 3.2. Kod formularza object Form1: TForm1 Left = 192 Top = 107 Width = 696 Height = 480 Caption = 'Form1' Color = clBtnFace Font.Charset = DEFAULT_CHARSET Font.Color = clWindowText Font.Height = -11 Font.Name = 'MS Sans Serif' Font.Style = [] OldCreateOrder = False PixelsPerInch = 96 TextHeight = 13 end

Na razie plik ten zawiera tylko informacje o formularzu, gdy nie umiecilimy jeszcze w projekcie adnych komponentw. Nie jest zalecane bezporednie modyfikowanie pliku *.dfm. Lepiej dokona zmian jedynie w Inspektorze Obiektw, a Delphi automatycznie uaktualni wwczas plik *.dfm. Ponowne przeczenie si na podgld formularza realizowane jest poprzez kliknicie prawym przyciskiem myszy w obszarze Edytora kodu i wybranie z menu podrcznego polecenia View As Form.

89 | S t r o n a

Umieszczanie komponentw na formularzu


Komponenty dziel si na wizualne i niewidoczne. Komponenty wizualne to takie komponenty, ktre pomagaj ulepszy interfejs (wygld) naszego programu s to rne przyciski, kontrolki edycyjne czy listy rozwijalne. Natomiast komponenty niewidoczne realizuj swoje, okrelone zadania, lecz po uruchomieniu programu nie s widoczne na formularzu.

Umieszczanie komponentw
O umieszczeniu komponentw na formularzu wspominaem ju w rozdziale 1., ale kilka sw przypomnienia nie zaszkodzi. Na zakadce Standard palety komponentw odszukaj przycisk TButton; komponent ten jest oznaczony ikonk symbolizujc przycisk. Kliknij t ikon, a nastpnie przemie kursor nad formularz ponowne kliknicie spowoduje umieszczenie obiektu w danym punkcie. Komponent umieszczony na formularzu mona swobodnie przemieszcza, rozciga i zwa wszystko to mona czyni za pomoc myszy, ale rwnie dobrze mona zmienia odpowiednie waciwoci w Inspektorze obiektw. O pooeniu komponentu decyduj waciwoci Left (pooenie w poziomie), Top (pooenie w pionie), Width (szeroko komponentu) i Height (wysoko komponentu); wszystkie wartoci podawane s w pikselach. Waciwoci te obecne s w kadym komponencie wizualnym.

Zmiana waciwoci komponentu


W naszym programie najpierw musimy dokona pewnych zmian we waciwociach samego formularza. Aby to uczyni, musisz zaznaczy formularz wystarczy jedno kliknicie w obszarze projektanta formularzy. Moesz take z listy rozwijalnej Inspektora obiektw wybra pozycj Form1. Kady komponent posiada waciwo Name; okrela ona nazw obiektu czy to formularza, czy komponentu. Nazwa ta musi by unikalna za jej pomoc moemy si w kodzie odwoywa zarwno do metod obiektu, jak i do waciwoci. Na samym pocztku naleaoby dokona zmiany nazwy gwnego formularza projektu. W tym celu odszukaj w Inspektorze obiektw pozycj Name i zaznacz j jednym klikniciem (rysunek 3.1).

90 | S t r o n a

Rysunek 3.1. Zaznaczona waciwo Name Co prawda nic si nie stanie, jeeli zachowamy tak nazw, jaka jest dotychczas, czyli Form1 jest to nazwa domylna lecz regu stao si, aby nazywa formularze, powiedzmy bardziej opisowo. Jeeli Twoja aplikacja jest bardzo skomplikowana i posiada wiele formularzy (moe tak by), to nazywajc je kolejno Form1, Form2 itd. atwo si pogubi. Dlatego te zazwyczaj nazwy formularzy okrela si wedug funkcji, jakie peni np. MainForm, AboutForm. Pamitaj, aby zawsze doda w nazwie kocwk Form (ang. formularz)! Zmie wic waciwo Name, wpisujc sowo MainForm i akceptujc je poprzez nacinicie klawisza Enter. Od tej pory formularz bdzie nosi nazw MainForm (formularz gwny).

Szeroko i wysoko Majc w dalszym cigu zaznaczony formularz, zmie jego waciwoci Width i Height na, odpowiednio, 400 (Width) i 125 (Height). Rozmiar formularza zostanie zmieniony wraz ze zmian waciwoci w Inspektorze obiektw.

Zdarzenia komponentw
W otwartym projekcie umie komponent TLabel, nadaj jego waciwoci Caption warto Przykad uycia komponentu TLabel, natomiast waciwoci Name nadaj warto lblMain. Waciwo Name komponentu TButton, uprzednio umieszczonego na formularzu, zmie na btnMain, natomiast Caption na Nacinij mnie!. Jak ju napisaem w rozdziale pierwszym, zdarzenia su do tego, aby odpowiednio reagowa na sytuacje stwarzane w wyniku dziaania programu. Podczas pracy z projektem kliknij dwukrotnie przycisk; Delphi przeniesie Ci do Edytora kodu i automatycznie wygeneruje procedur zdarzeniow. procedure TMainForm.btnMainClick(Sender: TObject); begin end;

91 | S t r o n a

Procedura ta jest nieco inna ni te, ktrymi zajmowalimy si w poprzednim rozdziale. Moesz w niej wpisa kod, ktry bdzie wykonywany po naciniciu przycisku przez uytkownika. Doprowad t procedur (metode) do takiej postaci: procedure TMainForm.btnMainClick(Sender: TObject); begin lblMain.Caption := 'Napis zmieniono'; end;

Uruchomienie programu i nacinicie przycisku spowoduje zmian napisu w etykiecie. Widzisz wic, e poszczeglne waciwoci komponentw mona zmienia rwnie w kodzie, take podczas dziaania aplikacji. Umoliwia to operator znak kropki (.):
Nazwa_Obiektu.Nazwa_Waciwoci := 'Nowa warto';

Dostp do poszczeglnych elementw odbywa si mniej wicej tak, jak to wida powyej.

Lista wygenerowanych zdarze w Inspektorze obiektw Zaznacz ponownie komponent TButton i kliknij zakadk Events w Inspektorze obiektw. Okno Inspektora obiektw powinno wyglda tak, jak na rysunku 3.2.

92 | S t r o n a

Rysunek 3.2. Lista zdarze Inspektora obiektw Zwr uwag, e jedna pozycja (OnClick) jest ju zajta oznacza to, e zdarzenie OnClick komponentu btnMain jest ju wygenerowane. Zostao to wykonane automatycznie po tym, jak dwukrotnie klikne przycisk.

Generowanie pozostaych zdarze Korzysta moesz ze wszystkich zdarze, jakie oferuje Ci Delphi w obrbie danego komponentu. Przykadowo zdarzenie OnMouseMove umoliwia Ci oprogramowanie zdarzenia przesuwania kursora nad obiektem. Innymi sowy, moesz odpowiednio na tak sytuacj zareagowa. Jak odbywa si generowanie zdarze z poziomu Inspektora obiektw ? Zaznacz zdarzenie, klikajc je niech to bdzie OnMouseMove. Po prawej stronie pojawi si lista rozwijalna, ktra na razie jest

93 | S t r o n a

pusta. Przesu kursor nad biae pole powinien zmieni swj ksztat. W tym momencie kliknij dwukrotnie spowoduje to wygenerowanie zdarzenia OnMouseMove. procedure TMainForm.btnMainMouseMove(Sender: TObject; Shift: TShiftState; X, Y: Integer); begin end;

W niniejszej ksice dla okrelenia nazw komponentw bd posugiwa si prawidowym okreleniem z liter T na pocztku np. TButton, TLabel. Jest to prawidowa nazwa dla komponentw VCL, chocia wiele osb nadaje komponentom nazwy pozbawione tej litery na pocztku Button, Label. Jeeli wygenerowane zdarzenie pozostanie puste, tj. nie bdzie zawierao adnego kodu, zostanie usunite podczas kompilacji lub zapisu projektu.

Kod generowany automatycznie


Zwr uwag na to, e podczas gdy umieszczasz komponenty na formularzu i dokonujesz rnych zmian, Delphi jednoczenie pracuje w tle, modyfikujc odpowiednio kod rdowy programu. Po umieszczeniu przez Ciebie na formularzu dwch kontrolek i wygenerowaniu zdarzenia cay kod programu przedstawia si tak, jak na listingu 3.3. Listing 3.3. Kod formularza po dokonaniu zmian unit MainFrm; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls; type TMainForm = class(TForm) btnMain: TButton; Label1: TLabel; procedure btnMainClick(Sender: TObject); private { Private declarations } public 94 | S t r o n a

{ Public declarations } end; var MainForm: TMainForm; implementation {$R *.dfm} procedure TMainForm.btnMainClick(Sender: TObject); begin lblMain.Caption := 'Napis zmieniono'; end; end.

Dla nas istotnym elementem powyszego listingu jest ten fragment: type TMainForm = class(TForm) btnMain: TButton; Label1: TLabel; procedure btnMainClick(Sender: TObject); private { Private declarations } public { Public declarations } end;

Jest to kod klasy formularza. Zauwa, e Delphi automatycznie po umieszczeniu przez nas komponentu na formularzu dodaje odpowiedni instrukcj do klasy. Wyglda to tak, jak deklaracja zmiennej lewa strona to nazwa obiektu, a po przecinku okrelony jest jego typ. Podczas np. zmiany nazwy komponentu Delphi automatycznie zastpi star nazw w kodzie programu.

Klasy
Klasa jest pewn struktur metod oraz waciwoci i pl, poczan w jedn cao. Omwienie tematu, jakim s klasy, wydaje si trudnym zadaniem szczeglnie na tym etapie znajomoci Delphi, na ktrym jeste. Postaram si wyjani to jak najdokadniej jeeli wszystko zrozumiesz, nie 95 | S t r o n a

powiniene mie ju problemw ze zrozumieniem zasad, jakimi rzdzi si VCL.

Czym waciwie s klasy?


Cay VCL opiera si na klasach; upraszcza to tworzenie nowych komponentw i obiektw wszystko jest ze sob cile powizane. Zamy, e tworzysz duy projekt, zawierajcy wiele formularzy. Dla tak duego i skomplikowanego projektu bardzo wane jest stworzenie tzw. engine. Sowo to okreli mona mianem silnika (mechanizmu) albo serca aplikacji. S to procedury lub funkcje (lub po prostu klasa), wykonujce najwaniejsze czynnoci programowe, zwizane z prawidowym dziaaniem samej aplikacji. Podczas omawiania klas przedstawi program, ktry bdzie korzysta z klas, a cae jego serce znajdzie si w module Engine.pas. Zadaniem programu bdzie tworzenie stron WWW z szablonw. W odpowiednie miejsce programu wstawiane bd polecenia, ktre program automatycznie umieci w pliku HTML (szablonie).

Tworzenie klas
Klasy mona zadeklarowa albo w sekcji Interface moduu, albo w sekcji Implementation. Klasy zawsze musz by deklarowane jako nowy typ danych podstawowa konstrukcja wyglda tak: type TEngine = class end;

Sowem kluczowym jest sowo class, informujce kompilator, e ma do czynienia z klas. Definicja klasy jak kadego obiektu zakoczona musi by sowem end;. Klasy mog zawiera jedynie deklaracj funkcji i procedur (ktre nazywamy metodami) oraz deklaracj zmiennych (wwczas nie naley uywa sowa var).

Definicja metod
Kompilator musi wiedzie, e dana metoda naley do okrelonej klasy. Rozwamy przykad nastpujcej klasy: type TEngine = class { brak rednika! } procedure Parse; end;

96 | S t r o n a

Procedura Parse naley do danej klasy, ale jest to jedynie jej deklaracja konieczne jest take umieszczenie kodu owej procedury w sekcji Implementation: procedure TEngine.Parse; begin end;

Zwr uwag na specyficzny zapis nagwka procedury. Aby owa procedura bya utosamiana z klas TEngine, najpierw naley wpisa nazw klasy, a dopiero po operatorze kropki nazw procedury. W Delphi wymagane jest, aby zadeklarowana w klasie metoda posiadaa definicj, czyli krtko mwic aby w kodzie rdowym znalaz si kod owej metody. W przeciwnym wypadku zostanie wywietlony bd: [Error] Engine.pas(20): Unsatisfied forward or external declaration: 'TEngine.xxx'. Zalecane jest podczas tworzenia klas stosowanie specjalnego nazewnictwa; nazwa klasy powinna zaczyna si od litery T.

Tworzenie klasy
Oprcz zwykej deklaracji nowej klasy naley stworzy zmienn, ktra wskazywa bdzie na nowy typ. Nie jest moliwe proste odwoanie si do metod danej klasy wczeniej naley nowy obiekt utworzy, co pozwoli na okrelenie przydziau pamici. Dopiero wtedy mona uzyska peny dostp do funkcji, jakie oferuje nam dana klasa. Odbywa si to nastpujco: procedure TMainForm.btnGenerateClick(Sender: TObject); var Template : TEngine; begin Template := TEngine.Create; end;

Na samym pocztku konieczne jest stworzenie zmiennej wskazujcej dany obiekt. Przydzia pamici odbywa si za porednictwem metody Create, obecnej w kadej klasie:

Template := TEngine.Create;

Metoda Create alokuje pami dla konkretnego obiektu, ale co ze zwalnianiem tej pamici? Kada klasa posiada take ukryt metod Free (lub Destroy), ktra zwalnia pami. Pamitaj o tym, aby zawsze po zakoczeniu korzystania z klasy zwalnia pami dla niej przydzielon. Template.Free; // zwolnienie pamici!

97 | S t r o n a

Do zwalniania pamici su dwie metody Free oraz Destroy. Zdecydowanie bardziej zalecan metod jest Free, dlatego e ponowne jej wywoanie w przypadku, gdy klasa zostaa ju zwolniona, nie powoduje bdu. Korzystanie z naszej klasy TEngine powinno odbywa si takimi etapami: procedure TMainForm.btnGenerateClick(Sender: TObject); var Template : TEngine; // deklaracja zmiennej wskazujcej klas begin Template := TEngine.Create; // utworzenie klasy { wywoanie metod klasy } Template.Parse; Template.Free; // zwolnienie pamici end;

Poziomy dostpu do klasy


Posumy si poprzednim przykadem, w ktrym mwiem o tworzeniu engine i duego projektu opartego na gwnej klasie. Zamy, e pracujesz w zespole, a Tobie zostao przydzielone zadanie stworzenia engine. Niektre elementy wcale nie musz by udostpniane na zewntrz klasy. Bo i w jakim celu? Klasa moe zawiera elementy, ktre nie powinny by ujawniane innym klasom lub innym moduom. Elementy te s tworzone tylko na potrzeby tylko tej klasy; niewaciwe ich wykorzystanie przez uytkownikw moe spowodowa niepodane skutki. Delphi udostpnia trzy poziomy dostpu do klasy private (prywatne), protected (chronione), public (publiczne). W zalenoci od sekcji, w ktrej te metody bd umieszczone, bd one inaczej interpretowane przez kompilator. Przykadowa deklaracja klasy z uyciem sekcji moe wyglda tak: TEngine = class private FFileName : String; FFileLines : TStringList; protected procedure Execute(Path : String); public Pattern : TTemplate; Replace : TTemplate; procedure Parse; constructor Create(FileName : String); destructor Destroy; override; end;

98 | S t r o n a

Jak widzisz, wystarczy, e wpiszesz odpowiednie sowo kluczowe w klasie i poniej jego bdziesz wpisywa metody.

Sekcja private Metody umieszczone w sekcji private s okrelane jako prywatne. Oznacza to, e nie bd widoczne na zewntrz moduu, w ktrym znajduje si dana klasa. A zatem po prbie odwoania si do metody umieszczonej w sekcji private kompilator zasygnalizuje bd nazwa owej metody nie bdzie moga by przez niego rozpoznana. Metoda z sekcji private nie jest widoczna tylko w przypadku, gdy prbujesz si do niej odwoa z innego moduu.

Sekcja protected Metoda umieszczona w sekcji protected jest widoczna zarwno dla moduu, w ktrym znajduje si klasa, jak i dla caej klasy. Jest to jakby drugi poziom ochrony, gdy metody z sekcji protected s widoczne dla innych klas, ktre dziedzicz po naszej klasie! Aby to zrozumie, naley wiedzie, czym jest dziedziczenie zajmiemy si tym w dalszej czci rozdziau.

Sekcja public Metody umieszczone w sekcji public s widoczne dla wszystkich innych klas i moduw. Istnieje jeszcze jedna sekcja published, ale dokadnie omwi j w czci czwartej niniejszej ksiki.

Dziedziczenie
Tym, co zapewnia szybki i dynamiczny rozwj VCL, jest dziedziczenie. Polega to na budowaniu nowych klas na bazie klas ju istniejcych. Dziki temu wiele wanych metod zawartych moe by jedynie w klasie bazowej inne klasy po niej dziedziczce nie musz ponownie zawiera tych samych funkcji. Po utworzeniu nowego projektu klasa znajdujca si w module wyglda nastpujco: type TForm1 = class(TForm)

Oznacza to, e w tym momencie tworzony jest nowy typ danych TForm1, ktry dziedziczy po klasie TForm. Nazw dziedziczonej klasy naley wpisa w nawiasie przy sowie kluczowym class. Dziki temu nasz formularz posiada takie waciwoci, jak Height, Width czy Caption, ktre s obecne w klasie TForm nasza klasa jedynie je dziedziczy. 99 | S t r o n a

type TEngine = class private FFileName : String; FFileLines : TStringList; protected procedure Execute(Path : String); public Pattern : TTemplate; Replace : TTemplate; procedure Parse; constructor Create(FileName : String); destructor Destroy; override; end; TEngine2 = class(TEngine) { dziedziczymy z klasy TEngine } end;

Spjrz na powyszy fragment kodu. Po uruchomieniu programu klasa TEngine2 zawiera bdzie wszelkie metody z klasy TEngine; nie ma jedynie dostpu do metod i zmiennych z sekcji private. Dziki temu wykorzystujc pierwotn wersj silnika, czyli klasy TEngine, moemy j unowoczeni, dodajc nowe elementy do klasy TEngine2 i jednoczenie nie tracc starych. Na tym opiera si idea dziedziczenia.

Klasa domylna Zastanawiasz si, z jakiej klasy dziedziczymy, jeeli podczas tworzenia deklaracji nie wpiszemy w nawiasie adnej nazwy W tym wypadku Delphi automatycznie za klas bazow uzna TObject. Klasa TObject jest podstawow klas dla caego VCL zawiera metody sterujce alokacj pamici dla klasy, zwalnianiem pamici itp.

Typy metod
Domylnie wszystkie metody deklarowane przez nas w ramach danej klasy to metody statyczne, nie opatrzone adn klauzul. Moliwe jest jednak tworzenie metod wirtualnych oraz dynamicznych. Wie si to z opatrzeniem danej metody klauzul virtual lub dynamic. TEngine2 = class(TEngine) procedure A; // statyczna procedure B; virtual; // wirtualna

100 | S t r o n a

procedure C; dynamic; // dynamiczna end;

Wspominam o tym, gdy z typami metod wie si jeszcze jedno pojcie, a mianowicie przedefiniowanie metod o tym bdzie mowa w kolejnym punkcie.

Metody wirtualne kontra metody dynamiczne W dziaaniu metody dynamiczne i wirtualne s praktycznie takie same. Jedyne, co ich rni, to sposb wykonywania. Ot w metody wirtualne wiksza jest szybko wykonania procedury, natomiast metody dynamiczne umoliwiaj lepsz optymalizacj kodu.

Konstruktory i destruktory
Ju wczeniej w tym rozdziale zapoznae si z metodami Create i Destroy. Te dwie metody to w rzeczywistoci konstruktor (Create) oraz destruktor (Destroy). We wasnej klasie moesz doda dwie metody, ktre bd wykonywane na samym starcie (podczas tworzenia klasy) oraz po zakoczeniu korzystania z klasy. S to specjalne typy metod oznaczamy je sowami kluczowymi constructor i destructor. constructor Create(FileName : String); destructor Destroy; override;

Poniewa w klasie TObject istnieje ju destruktor Destroy, opatrzony klauzul virtual, w naszej klasie naley przedefiniowa ten typ, dodajc na kocu definicji sowo override. W przeciwnym wypadku Delphi wywietli ostrzeenie: [Warning] Engine.pas(21): Method 'Destroy' hides virtual method of base type 'TObject'. W konstruktorach przewanie umieszcza si kod, ktry ma by wykonany przed rozpoczciem rzeczywistego korzystania z klasy. Moe to by np. tworzenie innych klas lub alokacja pamici. Pami po skoczeniu pracy z klas naley zwolni; jest to przewanie dokonywane w destruktorze klasy. Przykad uycia moesz znale w kolejnym podrozdziale, Przykad uycia klas. Konstruktory i destruktory (a moe by ich wiele) zawsze musz znajdowa si w sekcji public klasy.

101 | S t r o n a

Przykad uycia klas


Jak dotd, wiedza na temat klas bya do sucha i teoretyczna. Wiadome jest, e czowiek najlepiej uczy si poprzez praktyk std ten punkt, w ktrym opisaem tworzenie przykadowej aplikacji opartej na klasach. Aplikacja bdzie do prosta w zaoeniu, lecz przy okazji jej tworzenia poznasz zastosowanie paru ciekawych funkcji w Delphi oraz utrwalisz sw wiedz na temat klas.

Oglne zaoenia
Zadanie polega na wygenerowaniu dokumentu HTML, ktry zawiera bdzie informacje wpisane w programie. Wyglda to tak, e uytkownik wpisuje tytu strony HTML, nagwek oraz tre. Po naciniciu przycisku na dane z programu zostan podstawie odpowiedniego szablonu wtopione w ten szablon.

Tworzenie moduu Engine


Z menu File wybierz polecenie New/Other. W oknie wska ikon Unit utworzony zostanie nowy modu. Do prawidowego dziaania konieczne bdzie wczenie do listy uses plikw Windows.pas, SysUtils.pas i Classes.pas. uses Windows, Classes, SysUtils;

Modu Windows jest podstawowym moduem dla wszystkich innych moduw i aplikacji bez niego praktycznie nie moe istnie aden program. W module Classes umiejscowione s klasy, z ktrych czsto korzysta si podczas programowania przy uyciu VCL. Modu SysUtils take jest czsto uywanym moduem zawiera wiele przydatnych procedur oraz funkcji konwertowania czy operacji na acuchach.

Szablon Podstaw dziaania naszego przykadowego programu jest szablon. Zawiera on szkielet pliku HTML (patrz listing 3.4). Listing 3.4. Szablon programu <html> <head> <meta httpequiv="Content-Type" content="text/html; charset=iso102 | S t r o n a

8859-2"> <meta httpequiv="Content-Language" content="pl"> <title><!--PAGE_TITLE--></title> </head> <body> <h1>Witaj <!--USER_NAME--></h1> <hr> <!--HELLO_INFO-->

</body> </html>

Zwr uwag na komentarze HTML, umieszczone midzy znakami <!-- i -->; komentarze te bd zastpione odpowiednimi wartociami, przekazanymi klasie przez interfejs programu.

Wygld klasy Klasa, z ktrej bdziemy korzysta w naszej aplikacji, nie jest zbyt skomplikowana. type TTemplate = array of String; TEngine = class private FFileName : String; FFileLines : TStringList; protected procedure Execute(Path : String); virtual; public Pattern : TTemplate; Replace : TTemplate; procedure Parse; constructor Create(FileName : String); destructor Destroy; override; end;

Nowy typ TTemplate to tablica dynamiczna typu String; myl, e na ten temat wystarczajco wiele napisaem ju w poprzednim rozdziale.

103 | S t r o n a

Sekcja private posiada dwie zmienne FFileName zawiera ciek szablonu, z ktrego bdziemy korzystali. FFileLines to wskazanie klasy typu TStringList suy ona do wykonywania operacji na tekcie (adowanie, zapis do pliku, tworzenie nowego wiersza), gdzie wszystko odbywa si w pamici komputera. W sekcji protected znajduje si tylko jedna procedura Execute, ktra suy do otwierania strony internetowej wskazanej w parametrze Path. W sekcji public bardzo wan rol odgrywaj dwie zmienne, wskazujce na typ TTemplate. Za pomoc procedury Parse dokonywana jest zamiana odpowiednich wartoci w szablonie.

Kod rdowy moduu

Konstruktor i destruktor

Konstruktor i destruktor naszej klasy su do zaadowania szablonu do pamici, a wczeniej do utworzenia klasy TStringList: constructor TEngine.Create(FileName : String); begin FFileName := FileName; // przypisanie wartoci parametru do zmiennej w sekcji private FFileLines := TStringList.Create; // utworzenie typu TStringList FFileLines.LoadFromFile(FileName); // zaadowanie zawartoci zmiennej do pliku end; destructor TEngine.Destroy; begin FFileLines.Free; // zwolnienie typu { zwolnienie tablic } Pattern := nil; Replace := nil; DeleteFile('temporary.html'); // wykasowanie pliku tymczasowego inherited; // wywoanie destruktora klasy bazowej end;

Zaadowanie szablonu do pamici nastpuje za pomoc procedury LoadFromFile z klasy TStringList. Na tym polega wanie potga VCL w jednym wierszu kodu realizowanych jest wiele na pozr skomplikowanych czynnoci. W destruktorze naley zwolni zmienn FFileLines oraz tablice typu TTemplate. Oprcz tego wykonanie procedury DeleteFile powoduje wykasowanie pliku tymczasowego temporary.html, ktry uyty zostanie do wywietlenia rezultatw w przegldarce. 104 | S t r o n a

Dokonywanie zmian w szablonie

Kluczow rol odgrywa procedura Parse. Zmienne, przekazane do klasy w postaci tablicy typu TTemplate, musz zawiera pola do suce do zastpienia wartoci w szablonie. To w zasadzie gwne zadanie realizowane jest przez funkcj Parse: procedure TEngine.Parse; var i : Integer; begin for I := Low(Pattern) to High(Pattern) do { zastpienie okrelonych wartoci w FFileLines } FFileLines.Text := StringReplace(FFileLines.Text, Pattern[i], Replace[i], [rfReplaceAll]); FFileLines.SaveToFile('temporary.html'); Execute('temporary.html'); end;

Waciwo Text z klasy TStringList zwraca tre zaadowanego pliku, w tym wypadku szablonu. Procedura ta realizuje polecenie StringReplace, w wyniku ktrego zamieniane s odpowiednie wartoci w zmiennej. Tak zmodyfikowany plik jest zapisywany ponownie na dysku pod nazw temporary.html. W listingu 3.5. przedstawiony jest kod rdowy moduu Engine Listing 3.5. Kod rdowy moduu Engine.pas unit Engine; interface uses Windows, Classes, SysUtils; type TTemplate = array of String; TEngine = class private FFileName : String; FFileLines : TStringList; protected procedure Execute(Path : String); virtual; public Pattern : TTemplate; Replace : TTemplate; 105 | S t r o n a

procedure Parse; constructor Create(FileName : String); destructor Destroy; override; end;

implementation { TEngine } uses ShellAPI; // wczenie moduu ShellAPI constructor TEngine.Create(FileName : String); begin FFileName := FileName; // przypisanie wartoci parametru do zmiennej w sekcji private FFileLines := TStringList.Create; // utworzenie typu TStringList FFileLines.LoadFromFile(FileName); // zaadowanie zawartoci zmiennej do pliku end; destructor TEngine.Destroy; begin FFileLines.Free; // zwolnienie typu { zwolnienie tablic } Pattern := nil; Replace := nil; DeleteFile('temporary.html'); // wykasowanie pliku tymczasowego inherited; // wywoanie destruktora klasy bazowej end; procedure TEngine.Execute(Path: String); begin // otwarcie pliku w przegldarce Internetowej ShellExecute(0, 'open', PChar(Path), nil, nil, SW_SHOW); end; procedure TEngine.Parse; var i : Integer; begin for I := Low(Pattern) to High(Pattern) do { zastpienie okrelonych wartoci w FFileLines } FFileLines.Text := StringReplace(FFileLines.Text, Pattern[i], Replace[i], [rfReplaceAll]); FFileLines.SaveToFile('temporary.html'); 106 | S t r o n a

Execute('temporary.html'); end; end.

Ciekaw funkcj przedstawion w tym module jest ShellExecute, ktra zawarta jest w module ShellApi.pas. Funkcja ta powoduje uruchomienie pliku okrelonego w trzecim parametrze. Trzecim parametrem mog by dodatkowe parametry, z jakimi ma zosta uruchomiony program, a parametr czwarty okrela ciek do pliku. Ostatni parametr to tzw. flaga, okrelajca sposb, w jaki uruchomiony zostanie program moe to by SW_SHOW (poka), SW_HIDE (ukryj na starcie), SW_SHOWMAXIMIZED (poka zmaksymalizowany) lub SW_SHOWMINIMIZED (poka zminimalizowany).

Interfejs programu
Interfejsem nazywamy oglny wygld programu kontrolki, ktre komunikuj si z wntrzem aplikacji, przekazujc polecenia wydane przez uytkownika. Nasz przykadowy program nie bdzie zawiera wielu bajerw. Wystarczy par kontrolek, dziki ktrym uytkownik bdzie mg okreli tytu strony, tre oraz nagwek. Moja propozycja interfejsu aplikacji zostaa przedstawiona na rysunku 3.3.

Rysunek 3.3. Interfejs programu W skad komponentw umieszczonych w formularzu wchodzi:


komponent TGroupBox, w ktrym znajduj si inne komponenty; trzy komponenty typu TLabel (etykiety); dwa pola TEdit dwie kontrolki, przeznaczone do wpisania tytuu i nagwka; TMemo kontrolka tekstowa, wielowierszowa; TButton przycisk realizujcy cae zadanie. 107 | S t r o n a

Komponent TGroupBox nie odgrywa adnej znaczcej roli suy jedynie do tworzenia ozdobnej ramki. Jest take tzw. rodzicem dla komponentw. Oznacza to, e umieszczone w nim komponenty s jakby grupowane w jedn cao. Sprbuj przesun komponent TGroupBox zauwaysz, e wraz z przesuniciem obiektu przemieszczone zostan take inne komponenty w nim umieszczone.

Kod rdowy formularza gwnego


Na samym pocztku do listy uses formularza gwnego naley wczy nasz engine modu Engine.pas. Nastpnie cay proces zwizany z wykonaniem zadania powierzany jest klasie my j tylko inicjujemy. Kod rdowy formularza gwnego znajduje si w listingu 3.6. Listing 3.6. Kod rdowy formularza gwnego unit MainFrm; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls; type TMainForm = class(TForm) btnGenerate: TButton; GroupBox1: TGroupBox; lblTitle: TLabel; edtTitle: TEdit; lblHeader: TLabel; edtHeader: TEdit; Label1: TLabel; memWelcome: TMemo; procedure btnGenerateClick(Sender: TObject); private { Private declarations } public { Public declarations } end; var MainForm: TMainForm; implementation {$R *.dfm} uses Engine; 108 | S t r o n a

procedure TMainForm.btnGenerateClick(Sender: TObject); var Template : TEngine; begin { wywoaj konstruktor klasy z parametrem nazwa pliku szablonu } Template := TEngine.Create('index.tpl'); { okrel rozmiary tablicy } SetLength(Template.Pattern, 3); SetLength(Template.Replace, 3); { przypisz do tablicy element do zastpienia } Template.Pattern[0] := '<!--PAGE_TITLE-->'; { przypisz do tablicy element, ktry zastpi komentarz } Template.Replace[0] := edtTitle.Text; Template.Pattern[1] := '<!--USER_NAME-->'; Template.Replace[1] := edtHeader.Text; Template.Pattern[2] := '<!--HELLO_INFO-->'; Template.Replace[2] := memWelcome.Lines.Text; Template.Parse; Template.Free; end;

end.

Uruchamianie programu
Sprbuj skompilowa i uruchomi program. Jeeli zawiera bdy, popraw je - zgodnie z listingiem, ktry znajdziesz na doczonej do ksiki pycie CD-ROM. Rysunek 3.4 przedstawia program w trakcie dziaania, a rysunek 3.5 rezultat wykonania zadania stron HTML, wygenerowan przez program.

109 | S t r o n a

Rysunek 3.4. Program w trakcie dziaania

Rysunek 3.5. Strona wygenerowana przez aplikacj

110 | S t r o n a

Parametr Sender procedury zdarzeniowej


By moe zawaye, e podczas generowania nowego zdarzenia procedura zdarzeniowa zawsze posiada parametr Sender. Przykadowo po wygenerowaniu zdarzenia OnKeyPress formularza procedura zdarzeniowa wyglda nastpujco: procedure TForm1.FormKeyPress(Sender: TObject; var Key: Char); begin end;

Zdarzenie OnKeyPress odpowiada za przechwytywanie informacji dotyczcych klawisza nacinitego podczas dziaania programu. Posiada ono dwa parametry Sender i Key. Parametr Key zawiera informacje o klawiszu, ktry zosta nacinity podczas dziaania aplikacji. Parametr Sender jest jakby wskanikiem dziki niemu moemy dowiedzie si, z jakiego komponentu pochodzi zdarzenie, co jest wane w przypadku, gdy jedna procedura obsuguje kilka zdarze jednego typu. Aby lepiej to zilustrowa, napiszmy odpowiedni program.

Przechwytywanie informacji o nacinitym klawiszu


Przy okazji tego wiczenia zaprezentuj, w jaki sposb mona skorzysta z parametru Sender w przypadku, gdy jedna procedura zdarzeniowa uywana jest przez kilka komponentw. 1. W formularzu umie trzy przykadowe komponenty TMemo (kontrolka edycyjna wieloliniowa), TEdit (kontrolka jednoliniowa) i TCheckBox (zaznaczenie opcji). 2. Zmie waciwo Enabled komponentu TMemo na False. Spowoduje to, e komponent TMemo podczas dziaania programu bdzie nieaktywny, tj. nie bdzie mona wpisywa w nim adnego tekstu.

Zaznacz nastpnie formularz, tak aby w Inspektorze obiektw pojawiy si waciwoci i zdarzenia formularza. Wybierz zakadk Events z Inspektora obiektw i odszukaj trzy zdarzenia OnKeyDown, OnKeyPress i OnKeyUp. Wszystkie trzy s zwizane z przechwytywaniem procesu nacinicia klawisza. Zdarzenie OnKeyDown wystpuje w momencie nacinicia klawisza przez uytkownika. Zdarzenie to bdzie wystpowao, dopki uytkownik nie puci tego klawisza. Umoliwia ono take przechwytywanie naciskania takich klawiszy, jak F1F12, Home, End itd. Zdarzenie OnKeyPress wystpuje w trakcie naciskania klawisza na klawiaturze dostarcza 111 | S t r o n a

uytkownikowi parametr Key typu Char, czyli przekazuje nacinity znak. W przypadku, gdy uytkownik nacinie taki klawisz jak Alt lub Ctrl, zdarzenie to nie wystpuje. Zdarzenie OnKeyUp natomiast generowane jest w momencie puszczenia nacinitego uprzednio klawisza klawiatury. Ponadto zdarzenia OnKeyDown i OnKeyUp dostarczaj informacj o tym, czy w danym momencie wcinity jest take klawisz Ctrl lub Alt czy moe nacinity jest lewy przycisk myszki. Wygeneruj teraz wszystkie trzy zdarzenia OnKeyDown, OnKeyPress oraz OnKeyUp: procedure TMainForm.FormKeyPress(Sender: TObject; var Key: Char); begin memKeyInfo.Lines.Add('Nacinicie klawisza ' + Key); end; procedure TMainForm.FormKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState); begin memKeyInfo.Lines.Add('Wcinito klawisz #' + IntToStr(Key)); end; procedure TMainForm.FormKeyUp(Sender: TObject; var Key: Word; Shift: TShiftState); begin memKeyInfo.Lines.Add('Puszczono klawisz #' + IntToStr(Key)); end;

Podczas uruchomienia programu w komponencie TMemo dodawane s nowe wiersze, zawierajce informacje o naciniciu klawisza oraz jego kodzie ASCII. Dodanie nowej linii do komponentu realizowane jest za pomoc instrukcji: memKeyInfo.Lines.Add('tekst do dodania');

W rzeczywistoci waciwo Lines komponentu typu TMemo wskazuje na typ TStringList (korzystalimy z niego podczas omawiania klas) widzisz wic, jak wszystkie klasy VCL s ze sob poczone.

Obsugiwanie zdarze przez inne komponenty W przypadku komponentw TEdit oraz TCheckBox nie bdziemy pisali nowych procedur obsugi zdarze skorzystamy z tych, ktre ju mamy. Zaznacz komponent TEdit i przejd do zakadki Events z Inspektora Obiektw; odszukaj zdarzenie 112 | S t r o n a

OnKeyDown, zaznacz je, a nastpnie nacinij klawisz strzaki, co spowoduje rozwinicie listy zdarze tego typu, dostpnych w aplikacji (rysunek 3.6).

Rysunek 3.6. Lista zdarze moliwych do zastosowania Wybierz z tej listy zdarzenie OnKeyDown od tej pory wystpienie tego zdarzenia w komponencie TEdit bdzie realizowane przez procedur FormKeyDown. Tak samo postp ze zdarzeniami OnKeyPress i OnKeyUp, a take ze zdarzeniami komponentu TCheckBox. W takim przypadku wszystkie zdarzenia z tych trzech komponentw obsugiwane bd przez jedn procedur.

Obsuga parametru Sender


Moesz uruchomi program i sprawdzi jego dziaanie. Niewane, czy aktywny jest komponent TEdit, czy TCheckBox wszystkie nacinicia klawiszy s przechwytywane (rysunek 3.7).

Rysunek 3.7. Monitorowanie naciskania klawiszy Dziki parametrowi Sender, ktry obecny jest w kadej procedurze zdarzeniowej, moemy dowiedzie si, z ktrego komponentu zostao przesane zdarzenie. Zmodyfikuj w kodzie procedur FormKeyPress: procedure TMainForm.FormKeyPress(Sender: TObject; var Key: Char); begin memKeyInfo.Lines.Add('Nacinicie klawisza ' + Key + ' z klasy ' + Sender.ClassName); end;

113 | S t r o n a

Do treci komponentu TMemo bdzie take doczona informacja, z ktrej klasy pochodzi to zdarzenie. Realizuje to waciwo ClassName z klasy TObject. Ponownie uruchom aplikacj i sprawd teraz jej dziaanie; moesz aktywowa komponenty TCheckBox oraz TEdit klikniciem myszki i dopiero wwczas naciska klawisze. Daje to efekt przedstawiony na rysunku 3.8.

Rysunek 3.8. Informacja o naciskanych klawiszach W przypadku, gdy ilo wierszy znajdujcych si w komponencie TMemo jest na tyle dua, e nie mieszcz si one w oknie, nie mamy moliwoci przewinicia zawartoci komponentu. Naley zmieni odpowiednie ustawienie we waciwoci ScrollBars komponentu TMemo. Z listy rozwijalnej moesz wybra ssNone (brak paskw przewijania), ssHorizontal (poziomy pasek przewijania), ssVertical (pionowy pasek przewijania) lub ssBoth (obydwa paski przewijania).

Operatory is i as
Dwa operatory, is i as, s stosowane w poczeniu z klasami. Pewnie rzadko bdziesz z nich korzysta, jednak warto powici im nieco uwagi. Pierwszy z nich operator is suy do sprawdzania, czy np. aktywna kontrolka nie jest typu TEdit. To jest tylko przykad, gdy operator ten zazwyczaj realizuje porwnanie typw klas zobacz: if Sender is TEdit then memKeyInfo.Lines.Add('Zdarzenie pochodzi z klasy TEdit');

W przypadku, gdy zdarzenie pochodzi z komponentu typu TEdit, instrukcja if zostaje speniona. Operator is dziaa podobnie jak porwnanie za pomoc =. W niektrych jednak przypadkach nie mona uy operatora =: if Sender = TEdit then { kod }

114 | S t r o n a

Spowoduje to wywietlenie komunikatu o bdzie: [Error] MainFrm.pas(34): Incompatible types, gdy parametr Sender pochodzcy z klasy TObject oraz klasa TEdit to dwie oddzielne klasy. Operator as natomiast suy do tzw. konwersji. Nie jest to jednak typowa konwersja, jak omawiaem w poprzednim rozdziale. Zamy, e masz kilka kontrolek typu TEdit zdarzenie OnKeyPress dla kadej z nich jest obsugiwane przez jedn procedur zdarzeniow. Chciaby zmieni jak waciwo jednego komponentu typu TEdit, a to jest moliwe dziki operatorowi as. procedure TMainForm.FormKeyPress(Sender: TObject; var Key: Char); begin if Sender is TEdit then (Sender as TEdit).Text := ''; memKeyInfo.Lines.Add('Nacinicie klawisza ' + Key + ' z klasy ' + Sender.ClassName); end;

Po uruchomieniu programu i naciniciu klawisza w momencie, gdy komponent TEdit jest aktywny, wywoane zostanie zdarzenie OnKeyPress wwczas waciwo Text (ktra okrela tekst wpisany w kontrolce) zostanie wyczyszczona. Formularz posiada waciwo ActiveControl, ktra ustawia wybran kontrolk aktywn zaraz po uruchomieniu programu.

Parametr Self
Sowo kluczowe Self jest czsto nazywane wskanikiem klasy. W Delphi jest ono ukrywane, lecz stanowi wskazanie danej klasy oto przykadowy kod: procedure TForm1.FormCreate(Sender: TObject); begin Caption := 'Pole Caption'; end;

W takim wypadku Delphi domyla si, e chodzi tutaj o odwoanie do waciwoci Caption klasy TForm1 i kod jest wykonywany prawidowo. Rwnie dobrze mona by napisa: Self.Caption := 'Pole Caption';

Z punktu widzenia kompilatora wyglda to tak, jak przedstawiono powyej (wykorzystano wskanik Self danej klasy); kod taki rwnie zostanie skompilowany prawidowo. 115 | S t r o n a

Kolejny przykad ilustruje dynamiczne tworzenie przycisku. Tworzenie jakiegokolwiek komponentu w sposb dynamiczny wyglda tak samo, jak tworzenie instancji zwykej klasy. Jest jednak maa rnica w konstruktorze musisz poda tzw. rodzica, czyli, krtko mwic, okreli, w jakim komponencie ma zosta umieszczony komponent wanie tworzony. procedure TMainForm.btnCreateClick(Sender: TObject); var Button : TButton; begin Button := TButton.Create(Self); Button.Parent := Self; Button.Caption := 'Nowy...'; Button.Left := 150; end;

W konstruktorze wpisaem sowo Self stanowi to informacj dla kompilatora, e rodzicem komponentu ma by wanie formularz, czyli TMainForm. Dociekliwy Czytelnik moe zapyta, dlaczego po zakoczeniu korzystania z klasy, jak jest TButton, nie zwolniem pamici. Delphi uczyni to za mnie automatycznie, poniewa rodzicem dla komponentu jest formularz po jego zamkniciu zostan uprzednio zwolnione wszelkie obiekty w nim si znajdujce.

acuchy tekstowe
acuchami (ang. strings) nazywamy cig znakw o jakiej dugoci. W Delphi, w przeciwiestwie do innych jzykw, istnieje wiele zmiennych okrelajcych acuch. Dotd stosowaem jedynie zmienne typu String oraz (czasami) PChar. Np. w C++ nie istnieje pojcie acuch w tym jzyku acuchem jest w istocie tablica o okrelonej liczbie elementw.

ShortString
Typ ShortString to podstawowy typ acuchowy Delphi 1 o dugoci ograniczonej do 255 znakw. Z tej przyczyny gwn zalet wykorzystania tego typu acucha jest szybko. Zmienn uywajc acuch ShortString mona zadeklarowa na dwa sposoby: var S1 : ShortString; // dugo - 255 znakw S2 : String[255]; // dugo - 255 znakw

116 | S t r o n a

Obie zmienne bd w tej sytuacji zmiennymi typu ShortString. W przypadku zmiennej S2 moesz rwnie dobrze zadeklarowa zmienn o mniejszej dugoci, wpisujc odpowiedni warto w nawiasie. Dugo acucha ShortString umieszczona jest w pierwszym bajcie atwo wic mona odczyta rzeczywist dugo tekstu: var S : ShortString; Len : Integer; begin S := 'Hello World!'; Len := Ord(S[0]);

Zmienna Len zawiera bdzie warto 12. Funkcja Ord suy do zamiany (konwersji) znaku typu Char do wartoci liczbowej Integer. Odwrotn funkcj (zamiana wartoci Integer do Char) realizuje funkcja Chr.

AnsiString
Typ AnsiString pojawi si po raz pierwszy w Delphi 2 nie ma w nim ograniczenia dugoci, przez co typ ten staje si bardzo uniwersalny. Domylne ustawienia Delphi nakazuj traktowa typ String tak samo jak typ AnsiString. Delphi automatycznie zarzdza pamici dla zmiennych typu AnsiString Ty nie musisz si niczym przejmowa. Wad tego acucha jest odrobin wolniejsze dziaanie ni w przypadku ShortString, ale ze wzgldu na brak limitw dugoci acucha jego uycie jest zalecane. Odczyt dugoci acucha nie moe tutaj odby si z uyciem znakw [], jak ma to miejsce w acuchu ShortString; w tym wypadku mona skorzysta z funkcji Length. var S : AnsiString; Len : Integer; begin S := 'Hello World!'; Len := Length(S);

117 | S t r o n a

WideString
Ten typ jest bardzo podobny do AnsiString take nie posiada limitowanej dugoci. Jest przewanie uywany przez funkcje API korzystajce ze znakw Unicode.

acuchy z zerowym ogranicznikiem


Pod t nazw kryj si w rzeczywistoci zmienne typu PChar lub tablice Char. Nazwa pochodzi std, e acuch reprezentowany przez typ PChar jest zakoczony znakiem o kodzie 0. W jzyku C wszystkie acuchy to w rzeczywistoci tablice Char np.: var S : array[0..255] of char; begin S := 'Hello World!';

Po deklaracji tablicy 255-elementowej typu Char moemy przypisywa do niej dane jak do zwykego acucha. Warto zmiennej S jest zakoczona znakiem #0 informujcym o kocu acucha. Typ PChar jest w rzeczywistoci typem wskanikowych (pointers), ktry wskazuje na tablic znakw. Prawdopodobnie nie bdziesz czsto uywa typu PChar, jednak ze wzgldu na to, e system Windows by pisany w jzyku C, w wikszoci procedur Win API wymagane jest podanie jako parametrw zmiennych typu PChar.

Operacje na acuchach
Delphi posiada bardzo wygodne funkcje umoliwiajce operowanie na acuchach, czyli ich edycj, wycinanie czci, znajdowanie fragmentw itp. By moe Delphi nie posiada a tylu uytecznych funkcji co np. PHP, ale na nasze potrzeby na razie wystarcz. Wszystkie funkcje prezentowane w tym punkcie s zawarte w module SysUtils.pas lub StrUtils.pas.

czenie acuchw
czenie dwch typw acuchowych moe odbywa si za pomoc operatora +, ale take dziki funkcji Concat.

118 | S t r o n a

procedure TForm1.Button1Click(Sender: TObject); var S1, S2 : AnsiString; begin S1 := 'Adam'; S2 := ' Boduch'; ShowMessage( Concat(S1, S2) ); end;

W rezultacie wykonania tej procedury w okienku wywietlony zostanie napis Adam Boduch. T sam funkcj peni operator +, ktry jest ponadto szybszy. Dlatego te najprawdopodobniej nie bdziesz mia okazji wiele razy stosowa funkcji Concat. Ja na przykad jeszcze nigdy z niej nie korzystaem.

Wycinanie acucha
Przez wycinanie acucha rozumiem kasowanie jego czci. Uzyskiwanie jego fragmentw omwiem w punkcie kolejnym. Usunicie czci danych z acucha realizuje funkcja Delete. Naley w niej poda, od ktrego znaku ma si rozpocz wycinanie i ile znakw ma zosta wycitych. procedure TForm1.Button1Click(Sender: TObject); var S1 : AnsiString; begin S1 := 'Borland Delphi 7 Studio'; Delete(S1, 1, 8); ShowMessage(S1); end;

W wyniku wykonania tej operacji w oknie wywietlony zostanie jedynie napis Delphi 7 Studio, a sowo Borland zostanie wycite.

Uzyskiwanie fragmentw acucha


W tym zakresie Delphi oferuje nam do sporo przydatnych funkcji. S to funkcje kopiujce okrelon ilo bajtw z lewej lub z prawej strony cigu, a take funkcja Copy, ktra kopiuje z okrelonego miejsca podan ilo znakw. W Delphi 7 zostay wprowadzone nowe funkcje z moduu StrUtils: LeftBStr, RightBStr oraz MidBStr. Su one do uzyskiwania czci acucha z lewej lub prawej strony oraz z wybranego 119 | S t r o n a

miejsca. Maj one poprawi obsug na poziomie pojedynczych bajtw. Nowa wersja Delphi zostaa zaopatrzona take w przecione (overloaded) funkcje LeftStr, RightStr i MidStr, ktre umoliwiaj teraz dziaanie take na zmiennych WideString. Przykady uycia: uses StrUtils; procedure TForm1.Button1Click(Sender: TObject); var S1 : AnsiString; begin S1 := 'Borland Delphi 7 Studio'; ShowMessage( LeftBStr(S1, 8) ); { zwrci napis "Borland" } end; (**********************************************) uses StrUtils; procedure TForm1.Button1Click(Sender: TObject); var S1 : AnsiString; begin S1 := 'Borland Delphi 7 Studio'; ShowMessage( RightBStr(S1, 6) ); { zwrci napis "Studio" } end; (************************************************) uses StrUtils; procedure TForm1.Button1Click(Sender: TObject); var S1 : AnsiString; begin S1 := 'Borland Delphi 7 Studio'; ShowMessage( MidBStr(S1, 8, 7) ); { zwrci napis "Delphi" } end;

120 | S t r o n a

W identyczny sposb jak funkcja MidStr dziaa take funkcja Copy. Funkcja ta jest funkcj wbudowan, a zatem nie jest konieczne doczanie jakiegokolwiek moduu do prawidowego dziaania owej funkcji.

Wstawianie danych do acucha


Wstawianie nowych danych do ju istniejcego acucha realizuje wbudowana funkcja Insert. Pierwszym parametrem tej funkcji musi by tekst, ktry ma zosta wstawiony do acucha, a kolejny parametr to nazwa zmiennej, na ktrej bdziemy operowa; parametr ostatni to pozycja, na ktrej zostanie wstawiony tekst. Przykad: procedure TForm1.Button1Click(Sender: TObject); var S1 : AnsiString; begin S1 := 'Borland Delphi 7 Studio'; Insert(' Enterprise', S1, Length(S1) + 1); ShowMessage(S1); end;

Po uruchomieniu programu w okienku pojawi si tekst Borland Delphi 7 Studio Enterprise. W ostatnim parametrze procedury Insert do dugoci acucha (ktr to dugo uzyskujemy za pomoc funkcji Length) dodawana jest cyfra 1, aby zachowa przerw midzy wyrazami.

Wyszukiwanie danych w acuchu


Nowoci w Delphi 7 jest funkcja PosEx, dziki ktrej mona jeszcze lepiej zrealizowa operacj wyszukiwania danych w acuchu. Funkcja ta znajduje si w module StrUtils, a jej starsza siostra funkcja Pos jest funkcj wbudowan, take suc do znajdowania danych w zmiennej typu String. Funkcja Pos zwraca pozycj w zmiennej typu String, gdzie znaleziony zosta szukany tekst; jeeli tekst nie zostanie znaleziony, funkcja zwraca warto 0. procedure TForm1.Button1Click(Sender: TObject); var S1 : AnsiString; begin S1 := 'Borland Delphi 7 Studio'; if Pos('Studio', S1) > 0 then ShowMessage('Znaleziono napis Studio!'); end;

121 | S t r o n a

Nowa funkcja PosEx posiada dodatkowo parametr opcjonalny, ktry moe oznacza miejsce, od ktrego rozpocznie si wyszukiwanie.

Pozostae funkcje
Pragn przedstawi Ci par funkcji, ktre by moe przydadz Ci si podczas programowania w Delphi. Zaznaczam, e s to tylko wybrane funkcje wicej na ich temat moesz dowiedzie si z pomocy Delphi.

AnsiMatchStr Realizuje wyszukiwanie wartoci okrelonej w pierwszym parametrze tablicy, ktra musi by przekazana w drugim parametrze tej funkcji. Nagwek funkcji przedstawia si nastpujco: function AnsiMatchStr(const AText: string; const AValues: array of string): Integer;

AnsiReverseString Funkcja realizuje algorytm odwracania liter. Przykadowo jeeli wywoasz t polecenie z takim parametrem: AnsiReverseString('ABC');, otrzymasz acuch CBA.

DupeString Funkcja dubluje przekazany jako pierwszy parametr tekst okrelon w drugim parametrze ilo razy. Np.: S := DupeString('Delphi', 2);

W wyniku takiej operacji otrzymamy warto DelphiDelphi.

SearchBuf Za pomoc tego polecenia moesz wyszuka tekst znajdujcy si w buforze. Pojcie bufor w tym wypadku oznacza warto (tekst lub zmienna), ktra bdzie przedmiotem wyszukiwania. Nagwek tej funkcji: function SearchBuf(Buf: PChar; BufLen: Integer; SelStart, SelLength: Integer; SearchString: String; Options: TStringSearchOptions = [soDown]): PChar;

122 | S t r o n a

Pierwszym parametrem jest tekst, w ktrym odbdzie si szukanie; parametr kolejny to dugo tekstu (moemy j okreli poprzez SizeOf). Parametr trzeci to pozycja, od ktrej rozpocznie si szukanie. Parametr SelLength okrela ilo znakw, ktre zostan przeanalizowane od miejsca okrelanego jako SelStart. Kolejny parametr SearchString okrela tekst do znalezienia, a ostatni opcje szukania (tabela 3.1). Tabela 3.1. Moliwe wartoci typu TStringSearchOptions Warto soDown Krtki opis Szukanie odbdzie si w d

soMatchCase Podczas szukania rozrniane bd wielkie i mae litery soWholeWord Pod uwag bd brane nie tylko cae okrelenia, ale fragmenty.

Przykadowo szukajc sowa ABC, program wemie pod uwag take ABCDEEF

LowerCase Funkcja powoduje zamian wszystkich znakw okrelonych w pierwszym parametrze na mae litery. S := LowerCase(S); // wyraz DELPHI zostanie zamieniony na delphi

Zalecane jest korzystanie z funkcji AnsiLowerCase, ktra take zmienia znaki na mae litery, ale uwzgldnia np. polskie znaki diakrytyczne.

UpperCase W odrnieniu od funkcji LowerCase zamienia wszystkie znaki na due litery: S := UpperCase(S); // wyraz Delphi zostanie zamieniony na DELPHI

Zalecane jest korzystanie z funkcji AnsiUpperCase, ktra take zmienia znaki na due litery, ale uwzgldnia np. polskie znaki diakrytyczne.

Trim Funkcja Trim obcina spacje z pocztku i koca acucha.

123 | S t r o n a

procedure TForm1.Button1Click(Sender: TObject); var S1 : AnsiString; begin S1 := 'Borland Delphi 7 Studio '; ShowMessage(Trim(S1)); end;

Jak wida, do funkcji Trim przekazany zosta napis zawierajcy na kocu wiele spacji po konwersji spacje te zostan obcite. Istniej take funkcje TrimLeft oraz TrimRight, ktre obcinaj spacje odpowiednio z lewej oraz prawej strony tekstu.

WrapText Funkcja WrapText przydaje si w przypadku, gdy mamy do czynienia z dugim acuchem. Powoduje ona zawinicie wierszy poprzez wstawienie znaku nowego wiersza lub innego, przez nas okrelonego. function WrapText(const Line, BreakStr: string; nBreakChars: TSysCharSet; MaxCol: Integer):string; overload;

Pierwszy parametr okrela numer wiersza, a drugi tekst, ktry wstawiony bdzie pomidzy amane wiersze. Parametr nBreakChars jest zbiorem (o zbiorach mwilimy w poprzednim rozdziale) znakw, po ktrych nastpi amanie wiersza. Ostatni parametr okrela maksymaln dugo wiersza.

Typ wariantowe
Typy wariantowe nie pojawiy si po raz pierwszy w Delphi, znane byy ju programistom jzyka Clipper. Obecnie w jzykach PHP czy Perl podczas przydzielania danych do zmiennych typ zmiennej jest ustalany przez kompilator. To samo jest moliwe w Delphi po zadeklarowaniu jednej zmiennej typu Variant mona do niej przydziela rne dane, takie jak acuchy, liczby itp. procedure TForm1.Button1Click(Sender: TObject); var V : Variant; begin { przydzielenie acucha } V := 'Adam Boduch'; 124 | S t r o n a

{ przydzielenie liczb } V := 123; { przydzielenie liczb zmiennoprzecinkowych } V := 1.23; { przydzielenie wartoci typu Boolean } V := TRUE; end;

Taki kod zostanie bezproblemowo skompilowany przez Delphi. Do jednej zmiennej mona przypisa wszystkie rodzaje danych, bez obsugiwania konwersji. Rwnie taki kod zostanie skompilowany, a program prawidowo wykonany: procedure TForm1.Button1Click(Sender: TObject); var V : Variant; begin V := 123; ShowMessage(V); end;

Nie wykorzystano tu adnych funkcji konwersji, a mimo to liczba zostaa wywietlona jak typ String. Nie mwiem wczeniej o typie Boolean. Zmienna korzystajca z tego typu moe przybra jedynie dwie wartoci True (prawda) lub False (fasz). Czsto bdziesz korzysta z tego typu dla okrelenia waciwoci, ktra moe by albo wykonana (True), albo nie (False).

Waciwoci
Par najbliszych stron powic omwieniu podstawowych waciwoci VCL, jakie napotka moesz podczas pracy z Delphi. Nie bd to naturalnie wszystkie waciwoci komponentw dostpne w Delphi przedstawi tylko te podstawowe waciwoci, dotyczce wikszoci obiektw biblioteki VCL.

Align Waciwo Align suy do okrelenia pooenia komponentu w formularzu; waciwo ta dotyczy jedynie komponentw wizualnych. Warto waciwoci wybieramy z listy rozwijalnej Inspektora obiektw; moe ona zawiera wartoci takie, jak w tabeli 3.2. Tabela 3.2. Moliwe wartoci waciwoci Align

125 | S t r o n a

Warto

Opis

alBottom Komponent pooony bdzie u dou formularza, niezalenie od jego wielkoci alClient Obszar komponentu wypeni cay obszar formularza

alCustom Pooenie jest okrelane wzgldem komponentu (formularza) macierzystego alLeft alNone alRight alTop Obiekt pooony bdzie zawsze przy lewej krawdzi formularza lub komponentu macierzystego Pooenie nieokrelone (swobodne) Obiekt pooony bdzie zawsze przy prawej krawdzi formularza lub komponentu macierzystego Komponent bdzie pooony u gry formularza

Waciwo Align moe okrela pooenie komponentu wzgldem formularza lub wzgldem innego komponentu macierzystego. Takim komponentem jest TPanel, ktry jest rodzicem dla komponentu. Komponent TPanel, tak jak i wszystkie komponenty na nim umieszczone, stanowi jedn cao.

Anchors Waciwo Anchors mona rozwin, klikajc ikonk znajdujc si obok nazwy tej waciwoci (rysunek 3.9).

126 | S t r o n a

Rysunek 3.9. Rozwinita waciwo Anchors Waciwo ta okrela pooenie komponentu wzgldem komponentu-rodzica. Np. w przypadku, gdy waciwo akLeft gazi Anchors ma warto True, pooenie komponentu po lewej stronie jest jakby -blokowane. Podczas uruchomienia programu i rozcigania formularza komponent na nim umieszczony bdzie zawsze pooony w tym samym miejscu. Sprawd to! Zmie wszystkie waciwoci gazi Anchors na False. Teraz uruchom program i sprbuj rozcign lub zwa formularz. Zauwaysz, e komponent (np. TButton) bdzie dopasowywa swe pooenie do rozmiarw formularza.

127 | S t r o n a

Constraints Po rozwiniciu tej gazi pojawi si waciwoci MaxHeight, MinHeight, MaxWidth i MinWidth. Okrelaj one kolejno: maksymaln szeroko, minimaln szeroko, maksymaln wysoko oraz minimaln wysoko komponentu. Domylnie wszystkie te waciwoci posiadaj warto 0, co oznacza brak limitw. Jeeli chcesz zablokowa rozmiary komponentu, pamitaj wwczas o gazi Constraints. Cursor Kady komponent wizualny moe posiada osobny kursor. Oznacza to, e po naprowadzeniu kursora myszki nad dany obiekt kursor zostanie zmieniony wedug waciwoci Cursor danego obiektu. Po rozwiniciu listy rozwijalnej obok nazwy kadego kursora pojawi si jego podgld (rysunek 3.10).

Rysunek 3.10. Lista kursorw waciwoci Cursor DragCursor, DragKind, DragMode Wszystkie te trzy waciwoci zwizane s z technik Drag and Drop (ang. przecignij i upu). Delphi umoliwia konstruowanie aplikacji, ktra obsuguje przeciganie komponentw i umieszczanie ich w innych miejscach formularza. DragCursor okrela kursor, ktry okrela bdzie stan przecigania. DragKind okrela, czy dany obiekt bdzie mg zosta przecigany po formularzu czy te bdzie to miejsce tzw. dokowania, czyli miejsce, gdzie moe by umieszczony inny obiekt. DragMode okrela, czy moliwe bdzie przeciganie danego komponentu. Ustawienie waciwoci na dmManual wycza t opcj; z kolei ustawienie dmAutomatic wcza tak moliwo.

128 | S t r o n a

Font Waciwo Font dotyczy tylko komponentw wizualnych i okrela czcionk przez nie uywan. Ga Font mona rozwin, a nastpnie zdefiniowa szczegowe elementy, takie jak kolor, nazwa czcionki, wysoko czy styl (pogrubienie, kursywa, podkrelenie). Klas TFont i zwizan z ni waciwoci Font szczegowo zajmiemy si w rozdziale na temat grafiki.

HelpContex, HelpKeyword, HelpType Waciwoci te zwizane s z plikiem pomocy. Wikszo starannie zaprojektowanych aplikacji w systemie Windows posiada plik pomocy Delphi natomiast zawiera mechanizmy pozwalajce na zintegrowanie pliku pomocy z aplikacj. HelpContex okrela numer ID strony pomocy, ktrej dotyczy bdzie dana kontrolka. HelpKeyword moe zawiera sowo kluczowe okrelajce dan kontrolk. czy si to z ostatni waciwoci HelpType. Szukanie moe si bowiem odbywa wedug ID (htContext) lub wedug sw kluczowych (htKeyword).

Hint, ShowHint Waciwoci typu Hint s zwizane z tzw. dymkami podpowiedzi (ang. hint). Za ich pomoc moesz ustawi tekst podpowiedzi, ktry bdzie wywietlany po tym, jak uytkownik umieci kursor nad obiektem. Aby podpowied bya wywietlana, waciwo ShowHint musi by ustawiona na True. Z dymkami podpowiedzi wie si kilka dodatkowych waciwoci klasy TApplication. Klasy TApplication nie trzeba tworzy jest to wykonywane automatycznie; wystarczy odwoa si do konkretnej pozycji: Application.HintColor := clBlue;

Waciwo HintColor pozwala na okrelenie koloru ta podpowiedzi. Kolejna waciwo HintHidePause okrela czas w milisekundach (1 sek. = 1 000 milisekund), po ktrym podpowied zostanie ukryta. HintPause okrela czas, po ktrym podpowied zostanie wywietlona. Domylna warto to 500 milisekund. HintShortCuts to waciwo typu Boolean. Po zmianie tej waciwoci na True wraz z podpowiedzi bdzie wywietlony skrt klawiaturowy wywoujcy dan funkcj np. Wycina tekst do schowka (Ctrl+X).

129 | S t r o n a

Domylna warto kolejnej waciwoci HintShortPause to 50 milisekund. Waciwo ta okrela, po jakim czasie wywietlona zostanie podpowied kolejnej kontrolki, jeeli przemiecimy kursor znad jednego komponentu na drugi (np. wdrujc po pozycjach menu lub przyciskach paskw narzdziowych). Podpowied bdzie wywietlana tylko wwczas, gdy waciwo ShowHint danego obiektu bdzie ustawiona na True.

Visible Waciwo Visible dotyczy jedynie komponentw wizualnych. Jeeli jej warto to True (warto domylna), wwczas komponent bdzie wywietlany; jeeli False komponent podczas dziaania programu bdzie ukryty.

Tag Czsto moesz napotka si na waciwo Tag, gdy jest ona obecna w kadym komponencie. Nie peni ona adnej funkcji jest przeznaczona jedynie dla programisty do dodatkowego uycia. Moesz w niej przechowywa rne wartoci liczbowe (waciwo Tag jest typu Integer).

Zdarzenia
Par najbliszych stron powic omwieniu podstawowych zdarze VCL, jakie napotka moesz podczas pracy z Delphi. Nie bd to naturalnie wszystkie zdarzenia komponentw dostpne w Delphi, gdy to jest akurat specyficzn spraw dla kadego komponentu.

OnClick Zdarzenie OnClick wystpuje podczas kliknicia klawiszem myszy w obszarze danej kontrolki jest to chyba najczciej uywane zdarzenie VCL, dlatego nie bd go szerzej opisywa. Mam nadziej, e podczas czytania tej ksiki zorientujesz si, do czego suy ta waciwo.

OnContextPopup Delphi umoliwia tworzenie menu, w tym menu podrcznego (tzw. popup menu), rozwijalnego za pomoc kliknicia prawym przyciskiem myszki. To zdarzenie jest generowane wanie wwczas, gdy popup menu zostaje rozwinite. Wraz z tym zdarzeniem programicie dostarczana jest informacja dotyczca pooenia kursora myszki 130 | S t r o n a

(parametr MousePos) oraz tzw. uchwytu (o tym przy innej okazji). Parametr MousePos jest typu TPoint, a to nic innego jak zwyky rekord, zawierajcy dwie pozycje X i Y. A zatem jeeli chcemy odczyta pooenie kursora myszki w poziomie, wystarczy odczyta je poprzez MousePos.X;

OnDblClick Zdarzenie jest generowane podczas dwukrotnego kliknicia danego obiektu. Obsugiwane jest tak samo jak zdarzenie OnClick wraz ze zdarzeniem nie s dostarczane adne dodatkowe parametry.

OnActivate, OnDeactivate Te dwa zdarzenia zwizane s jedynie z oknami (formularzami). Wystpuj w momencie, gdy okno stanie si aktywne (OnActivate) lub zostanie dezaktywowane (OnDeactivate).

OnClose, OnCloseQuery Te dwa zdarzenia zwizane s rwnie z formularzem, a konkretnie z jego zamykaniem. Dziki zdarzeniu OnClose moesz zareagowa podczas prby zamknicia okna. Wraz ze zdarzeniem dostarczany jest parametr Action, ktry okrela akcj do wykonania. Moemy nada temu parametrowi wartoci przedstawione w tabeli 3.3. Tabela 3.3. Waciwoci klasy TCloseAction Warto caNone caHide Opis Nic si nie dzieje mona zamkn okno Okno nie jest zamykane, a jedynie ukrywane

caMinimize Okno jest minimalizowane zamiast zamykania caFree Okno zostaje zwolnione, co w efekcie powoduje zamknicie

Zdarzenia OnCloseQuery moesz uy, aby zapyta uytkownika, czy rzeczywicie chce zamkn okno. Zdarzenia posiada parametr CanClose; jeeli nastpi jego zmiana na False, okno nie zostanie zamknite.

131 | S t r o n a

OnPaint Zdarzenie OnPaint wystpuje zawsze wtedy, gdy okno jest wywietlane i umieszczane na pierwszym planie. W zdarzeniu tym bdziesz umieszcza kod, ktrego zadaniem bdzie malowanie w obszarze formularza.

OnResize Zdarzenie OnResize wystpuje tylko wtedy, gdy uytkownik zmienia rozmiary formularza. Moesz dziki temu zdarzeniu odpowiednio zareagowa na zmiany lub nie dopuci do nich.

OnShow, OnHide Jak atwo si domyle, te dwa zdarzenia informuj o tym, czy aplikacja jest ukrywana czy pokazywana. Pokazanie lub ukrycie formularza dokonywane jest za pomoc metody Show lub Hide klasy TForm.

OnMouseDown, OnMouseMove, OnMouseUp, OnMouseWheel, OnMouseWheelDown, OnMouseWheelUp Wszystkie wymienione zdarzenia zwizane s z obsug myszy s to kolejno: kliknicie w obszarze kontrolki, przesunicie kursora nad kontrolk, puszczenie klawisza myszy, uycie rolki myszki, przesunicie rolki w gr lub w d. Wraz z tymi zdarzeniami do aplikacji moe by dostarczana informacja o pooeniu kursora myszy oraz o przycisku myszy, ktry zosta nacinity (lewy, rodkowy, prawy). Informacje te zawiera parametr Button klasy TMouseButton (tabela 3.4). Tabela 3.4. Moliwe wartoci klasy TMouseButton Warto mbLeft Opis Nacinito lewy przycisk myszki

mbMiddle Nacinito rodkowy przycisk myszki mbRight Nacinito prawy przycisk myszki.

Wraz ze zdarzeniami obsugi myszy moe by dostarczany rwnie parametr Shift, ktry jest obecny take w zdarzeniach klawiaturowych (OnKeyUp, OnKeyDown). Wartoci, jakie moe posiada parametr Shfit, przedstawione s w tabeli 3.5.

132 | S t r o n a

Tabela 3.5. Moliwe wartoci klasy TShiftState Warto ssShift ssAlt ssCtrl ssLeft ssRight Opis Klawisz Shift jest przytrzymany w momencie wystpienia zdarzenia Klawisz Alt jest przytrzymany w momencie wystpienia zdarzenia Klawisz Ctrl jest przytrzymany w momencie wystpienia zdarzenia Przytrzymany jest rwnie lewy przycisk myszki Przytrzymany jest take prawy przycisk myszki

ssMiddle Przytrzymany jest rodkowy przycisk myszy ssDouble Nastpio dwukrotne kliknicie

Zdarzenia zwizane z dokowaniem Wspominaem ju wczeniej o moliwoci dokowania obiektw metod przecignij i upu. Zwizane jest z tym par zdarze, ktre czsto moesz napotka, przegldajc list z zakadki Events z Inspektora Obiektw.

OnDockDrop

Zdarzenie OnDockDrop generowane jest w momencie, gdy uytkownik prbuje osadzi jak inn kontrolk w obrbie naszego obiektu.

OnDockOver

Zdarzenie to wystpuje w momencie, gdy jaka inna kontrolka jest przecigana nad naszym obiektem.

OnStartDock

Zdarzenie wystpuje w momencie, gdy rozpoczynasz przeciganie jakiego obiektu. Warunkiem wystpienia tego zdarzenia jest ustawienie waciwoci DragKind na warto dkDock.

OnStartDrag

Zdarzenie wystpuje tylko wwczas, gdy waciwo DragKind komponentu jest ustawiona na dkDrag. Wykorzystaj to zdarzenie w momencie, kiedy chcesz zareagowa na przeciganie obiektu. 133 | S t r o n a

OnEndDrag, OnEndDock

Pierwsze ze zdarze wykorzystaj w przypadku, gdy chcesz zareagowa na zakoczenie procesu przecigania; drugie natomiast wystpuje w przypadku zakoczenia procesu przecignij i upu.

OnDragDrop

Zdarzenie to generowane jest w momencie, gdy w danym komponencie nastpuje spuszczenie danych przeciganych metod drag nad drop.

OnDragOver

Zdarzenie to generowane jest w monecie, gdy nad danym komponentem uytkownik przeciga kursor z przeciganymi danymi.

Przykadowy program

Zainteresowanych metod wymiany danych pomidzy dwoma obiektami odsyam do przykadowego programu znajdujcego si na pycie CD-ROM, doczonej do ksiki. Program umieszczony jest w katalogu ../listingi/3/DragnDrop, a jego dziaanie prezentuje rysunek 3.11.

Rysunek 3.11. Dziaanie programu wykorzystujcego metod Drag and Drop Program umoliwia wymian danych metod przecigania pomidzy komponentami TListBox; moliwe jest take dowolne przemieszczanie komponentw np. TButton, TLabel oraz umieszczanie ich w panelu (TPanel). Aby przemieszczanie danych pomidzy komponentami TListBox mogo doj do skutku, waciwo DragMode musi by ustawiona na dmAutomatic. Rwnie dobrze mona wywoa procedur DragBegin komponentu TListBox w celu uruchomienia procesu przecigania.

134 | S t r o n a

Wyjtki
aden program nie jest pozbawiony bdw jest to zupenie naturalne, gdy nawet najwiksze firmy, zatrudniajce wielu programistw, nie s w stanie zlikwidowa w swoich produktach wszystkich niedocigni (dotyczy to zwaszcza duych projektw). Programujc w Delphi, mamy moliwo przynajmniej do pewnego stopnia zapanowania nad tymi bdami. Bd moe bowiem wynika z wykonania pewnej operacji, ktrej my, projektanci, si nie spodziewalimy; moe te wystpi wwczas, gdy uytkownik wykona czynnoci nieprawidowe dla programu np. poda z warto itp. W takim wypadku program generuje tzw. wyjtki, czyli komunikaty o bdach. My moemy jedynie odpowiednio zareagowa na zaistniay wyjtek, poprzez np. wywietlenie stosownego komunikatu czy chociaby wykonanie pewnej czynnoci.

Sowa kluczowe try..except


Objcie danego kodu kontrol odbywa si poprzez umieszczenie go w bloku try..except. Wyglda to tak: try { instrukcje do wykonania } except { instrukcje do wykonania w razie wystpienia bdu } end;

Jeeli kod znajdujcy si po sowie try spowoduje wystpienie bdu, program automatycznie wykona instrukcje umieszczone po sowie except. Jeeli uruchamiasz program bezporednio z Delphi (naciskajc klawisz F9), wyjtki mog nie zadziaa. Zwizane jest to z tym, e Delphi automatycznie kontroluje wykonywanie aplikacji i w razie bdu wywietla stosowny komunikat (rysunek 3.12) oraz zatrzymuje prac programu. eby temu zapobiec, musisz wyczy odpowiedni opcj. W tym celu przejd do menu Tools/Debugger Options, kliknij zakadk Language Exceptions i usu zaznaczenie pozycji Stop on Delphi Exception.

Rysunek 3.12. Okno wywietlane przez Delphi w przypadku wystpienia bdu Przykad: musisz pobra od uytkownika pewne dane np. liczb. Dziki wyjtkom moesz sprawdzi, czy podane w polu TEdit wartoci s wartociami liczbowymi.

135 | S t r o n a

procedure TMainForm.btnConvertClick(Sender: TObject); begin try StrToInt(edtValue.Text); // prba konwersji tekstu na liczb Application.MessageBox('Konwersja powioda si!', 'OK', MB_ICONINFORMATION); except Application.MessageBox('Bd! Musisz wpisa liczb!', 'Bd', MB_ICONERROR) end; end;

Na samym pocztku w bloku try nastpuje prba konwersji tekstu na liczb (StrToInt). Jeeli wszystko odbdzie si zgodnie z planem, to okienko informacyjne zawiera bdzie odpowiedni tekst. Jeeli natomiast podana warto nie bdzie liczb, wykonany zostanie wyjtek z bloku except. Funkcja MessageBox z klasy TApplication ma takie same dziaanie jak funkcja MessageBox z moduu Windows.pas.

Sowa kluczowe try..finally


Kolejn instrukcj wyjtkw s sowa kluczowe try oraz finally. W odrnieniu od bloku except kod znajdujcy si po sowie finally bdzie wykonywany zawsze, niezalenie od tego, czy wystpi wyjtek. Konstrukcji tej uywa si np. w wypadku, gdy konieczne jest zwolnienie pamici, a nie jestemy pewni, czy podczas operacji nie wystpi aden bd. { rezerwujemy pami } try { operacje mogce sta si rdem wyjtku } finally { zwolnienie pamici } end;

Instrukcje try oraz finally s czsto uywane przez programistw podczas tworzenia nowych klas i zwalniania danych oto przykad: MojaKlasa := TMojaKlasa.Create; try { jakie operacje } finally MojaKlasa.Free; end; 136 | S t r o n a

Dziki temu niezalenie od tego, czy wystpi wyjtek, czy te nie, pami zostanie zwolniona! Z tak konstrukcj moesz spotka si bardzo czsto, przegldajc kody rdowe innych programistw. Moliwe jest rwnie poczenie blokw try oraz except z blokiem try..finally: MojaKlasa := TMojaKlasa.Create; try try { operacje mogce sta si rdem wyjtkw } except { komunikat informujcy o wystpieniu bdu } end; finally MojaKlasa.Free; // zwolnienie pamici end;

Sowo kluczowe raise


Sowo kluczowe raise suy do tworzenia klasy wyjtku. Brzmi to troch skomplikowanie, ale w rzeczywistoci takie nie jest. Spjrz na poniszy kod: if Length(Edit1.Text) = 0 then raise Exception.Create('Wpisz jaki tekst w polu Edit!');

W przypadku, gdy uytkownik nie wpisze nic, w polu Edit wygenerowany zostanie wyjtek. Wyjtki generowane s za pomoc klasy Exception, ale o tym napisz nieco pniej. Na razie powiniene wiedzie, e sowo raise umoliwia generowanie wyjtkw poza blokiem try..except. Pozostawienie sowa raise samego, jak w poniszym przypadku, spowoduje wywietlenie domylnego komunikatu o bdzie: try { jakie funkcje } except raise; end;

Jeeli w tym wypadku w bloku try znajd si instrukcje, ktre doprowadz do wystpienia bdu, to sowo kluczowe raise spowoduje wywietlenie domylnego komunikatu o bdzie dla tego wyjtku. Nie moesz jednak uywa sowa raise poza blokiem try..except w takim wypadku zostanie wywietlony komunikat o bdzie: [Error] Unit1.pas(29): Re-raising an exception only allowed in exception handler.

137 | S t r o n a

Klasa Exception
W module SysUtils zadeklarowana jest klasa Exception (wyjtkowo bez litery T na pocztku), ktra jest klas bazow dla wszystkich wyjtkw. W Delphi istnieje kilkadziesit klas wyjtkw, a kada klasa odpowiada za inny wyjtek. Przykadowo bd EConvertError wystpuje podczas bdw konwersji, a EDivByZero podczas prby dzielenia liczb przez 0. Wszystko to zwizane jest z tzw. selektywn obsug wyjtkw, o czym bdziemy mwili za chwil. W kadym razie moliwe jest zadeklarowanie w programie wasnego typu wyjtku. type ELowError = class(Exception); EMediumError = class(Exception); EHighError = class(Exception);

Przyjo si ju, e nazwy wyjtkw rozpoczynane s od litery E Tobie take zalecam stosowanie takiego nazewnictwa. Od mementu zadeklarowania nowego typu moesz generowa takie wyjtki: raise EHighError.Create('Co strasznego! Zakocz aplikacj!');

Obiekt EHighError jest zwyk klas, dziedziczon z Exception, wic naley take wywoa jej konstruktor. Tekst wpisany w apostrofy wywietlony zostanie w okienku komunikatu o bdzie (rysunek 3.13).

Rysunek 3.13. Komunikat o bdzie wygenerowany za pomoc klasy EHighError

Selektywna obsuga wyjtkw


Selektywna obsuga wyjtkw polega na wykryciu rodzaju bdu i wywietleniu stosownej informacji (lub wykonaniu jakiej innej czynnoci). try { instrukcje mogce spowodowa bd } except on ELowError do { jaki komunikat } on EHighError do { jaki komunikat } end; 138 | S t r o n a

Wanie poznae zastosowanie kolejnego operatora jzyka Object Pascal on. Jak widzisz, dziki niemu moemy okreli, jakiego typu jest wyjtek i odpowiednio na zareagowa. W module SysUtils zadeklarowanych jest kilkadziesit klas wyjtkw, jak np. EAccessViolation (bdy zwizane z nieprawidowym dostpem do pamici), EInvalidCast (zwizany z nieprawidowym rzutowaniem) czy EInvalidPointer (zwizany z nieprawidowymi operacjami na wskanikach). Wicej moesz dowiedzie si z systemu pomocy Delphi.

Zdarzenie OnException
Na prno szuka zdarzenia OnException na licie zakadki Events z Inspektora obiektw. Zdarzenie OnException jest zwizane z ca aplikacj, a nie jedynie formularzem std znajduje si w klasie TApplication. Dziki temu zdarzeniu moemy przechwyci wszystkie komunikaty o bdach wystpujce w naszej aplikacji; jest to jednak odmienna forma zdarzenia, ktrej nie generujemy z poziomu Inspektora obiektw. Musimy w programie napisa now procedur, ktra bdzie obsugiwa zdarzenie OnException. Deklaracja takiej procedury musi wyglda tak: procedure MyAppException(Sender: TObject; E : Exception);

Drugi parametr E zawiera wyjtek, ktry wystpi w programie. Zapytasz, czemu wanie taka deklaracja ? Podczas gdy generowae zdarzenia z poziomu Inspektora obiektw np. OnMouseMove zawieray one specyficzne parametry dotyczce okrelonej sytuacji (w przypadku OnMouseMove byy to wsprzdne myszki oraz parametr Shift). Delphi nie dopuci do uruchomienia programu w przypadku, gdy procedura zdarzeniowa OnException nie bdzie zawieraa parametru E. Aby rzeczywicie mc przechwytywa wyjtki zaistniae w programie, naley wykona jeszcze jedn czynno: procedure TMainForm.FormCreate(Sender: TObject); begin Application.OnException := MyAppException; end;

Nakazujemy programowi, aby wszelkie wyjtki zaistniae w programie byy obsugiwane przez procedur MyAppException.

139 | S t r o n a

Obsuga wyjtkw Mamy ju procedur, ktra bdzie obsugiwaa zdarzenie OnException, ale to jeszcze nie koniec. Musimy jeszcze nasz procedur MyAppException jako oprogramowa i nakaza jej wykonywanie jakich czynnoci zwizanych z wyjtkami. procedure TMainForm.MyAppException(Sender: TObject; E: Exception); begin { wywietlenie komunikatw wyjtkw } Application.ShowException(E); if E is EHighError then // jeeli wyjtek to EHighError... begin if Application.MessageBox('Dalsze dziaanie programu grozi zawieszeniem systemu. Czy chcesz kontynuowa?', 'Bd', MB_YESNO + MB_ICONWARNING) = Id_No then Application.Terminate; end; end;

Pierwszy wiersz procedury to wykonanie polecenia ShowException z klasy Application. Polecenie to powoduje wywietlenie komunikatu zwizanego z danym wyjtkiem (rysunek 3.14.). Kolejne instrukcje stanowi ju tylko przykad, jak mona zareagowa w sytuacji wystpienia jakiego konkretnego bdu (listing 3.7.) Listing 3.7. Kod moduu MainForm unit MainFrm; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls, ExtCtrls, ComCtrls; type TMainForm = class(TForm) rgExceptions: TRadioGroup; btnGenerate: TButton; StatusBar: TStatusBar; procedure FormCreate(Sender: TObject); procedure btnGenerateClick(Sender: TObject); private procedure MyAppException(Sender: TObject; E : Exception); public 140 | S t r o n a

{ Public declarations } end; ELowError = class(Exception); EMediumError = class(Exception); EHighError = class(Exception); var MainForm: TMainForm; implementation {$R *.dfm} { TMainForm } procedure TMainForm.MyAppException(Sender: TObject; E: Exception); begin { wywietlenie komunikatw wyjtkw } Application.ShowException(E); if E is EHighError then // jeeli wyjtek to EHighError... begin if Application.MessageBox('Dalsze dziaanie programu grozi zawieszeniem systemu. Czy chcesz kontynuowa?', 'Bd', MB_YESNO + MB_ICONWARNING) = Id_No then Application.Terminate; end; end; procedure TMainForm.FormCreate(Sender: TObject); begin { przypisanie zdarzeniu OnException procedury MyAppException } Application.OnException := MyAppException; end; procedure TMainForm.btnGenerateClick(Sender: TObject); begin { odczytanie pozycji z komponentu TRadioGroup } case rgExceptions.ItemIndex of 0: raise ELowError.Create('Niegrony bd!'); 1: raise EMediumError.Create('Niebezpieczny bd!'); 2: raise EHighError.Create('Bardzo niebezpieczny bd!'); end; end; end.

141 | S t r o n a

Zamiast standardowego wywietlenia opisu bdu w komunikacie informacyjnym (co w listingu 3.7 jest efektem polecenia ShowException) moliwe jest wywietlenie komunikatu, np. w komponencie aplikacji. Wystarczy, e zmodyfikujesz listing 3.7 i w zdarzeniu MyAppException napiszesz: StatusBar.SimpleText := E.Message;

Rysunek 3.14. Program podczas dziaania

Klasa TApplication
Program wykorzystujcy formularze posiada ukryt zmienn Application, ktra wskazuje klas TApplication. Klasa ta odpowiada za dziaanie aplikacji, jej uruchamianie i zamykanie, obsug wyjtkw itp. Niestety waciwoci oraz zdarzenia tej klasy nie s widoczne w Inspektorze obiektw, wic operacji na klasie TApplication naley dokonywa bezporednio w kodzie programu. Oto zawarto gwnego pliku DPR zaraz po utworzeniu nowego projektu: program Project1; uses Forms, Unit1 in 'Unit1.pas' {Form1}; {$R *.res} begin Application.Initialize; Application.CreateForm(TForm1, Form1); Application.Run; end.

Wszystkie metody wywoywane w bloku begin..end znajduj si w klasie TApplication to moe wiadczy o tym, jak wana z punktu widzenia VCL jest ta klasa. 142 | S t r o n a

Pierwszy wiersz, czyli instrukcja Initialize, powoduje zainicjowanie procesu dziaania aplikacji. Kolejna instrukcja CreateForm powoduje utworzenie formularza, a ostatnia Run uruchomienie aplikacji. W dalszych punktach przedstawi najwaniejsze waciwoci, metody i zdarzenia klasy TApplication. Nie chc jednak przekracza pewnych ram i mwi o rzeczach, o ktrych dowiesz si w dalszej czci ksiki omwi zatem teraz tylko podstawowe waciwoci, zdarzenia i metody.

Waciwoci klasy TApplication


Waciwoci klasy TApplication s cile zwizane z dziaaniem aplikacji i obsug niektrych jej aspektw. Oto najwaniejsze z nich

Active Waciwo Active jest waciwoci tylko do odczytu. Oznacza to, e nie mona jej modyfikowa, a jedynie odczyta jej warto. Waciwo ta zwraca warto True, jeeli aplikacja jest aplikacj pierwszoplanow.

ExeName ExeName jest take waciwoci tylko do odczytu. Okrela ona ciek do aplikacji wykonywalnej EXE. Label1.Caption := Application.ExeName;

Powyszy kod spowoduje wywietlenie w etykiecie cieki do programu. Peny kod rdowy programu wywietlajcego ciek do aplikacji znajduje si na pycie CD-ROM w katalogu ../listingi/3/ExeName. ShowMainForm Waciwo ShowMainForm domylnie posiada warto True, co oznacza, e formularz gwny zostanie wywietlony. Nadajc tej waciwoci warto False, blokujemy wywietlenie formularza gwnego: begin Application.Initialize; Application.ShowMainForm := False; // nie wywietlaj! Application.CreateForm(TMainForm, MainForm); Application.Run; end; 143 | S t r o n a

Title Waciwo Title okrela tekst, ktry jest wywietlony na pasku stanu obok ikony w czasie, gdy aplikacja jest zminimalizowana. Application.Title := 'Nazwa programu';

Metody klasy TApplication


Oto par opisw wybranych metod z klasy TApplication. CreateForm Metoda CreateForm jest uywana do tworzenia nowego formularza. Peny przykad uycia tej procedury moesz znale w kolejnym rozdziale. Minimize Wywoanie metody Minimize spowoduje zminimalizowanie aplikacji do paska zada. Wywoanie procedury jest proste: Application.Minimize; // minimalizuj

Terminate Wywoanie metody Terminate spowoduje natychmiastowe zamknicie aplikacji. Inn funkcj zamykajc jest Close, ale zamyka ona jedynie formularz, a nie ca aplikacj, dlatego zalecane jest uywanie zamiast niej funkcji Terminate. MessageBox Metoda MessageBox powoduje wywietlenie okienka informacyjnego; jest zatem jakby rozbudowan funkcj ShowMessage, gdy umoliwia ustalenie wikszej iloci parametrw. procedure TForm1.FormCreate(Sender: TObject); begin if Application.MessageBox('Uruchomiony program?', 'Tak/Nie', MB_YESNO + MB_ICONINFORMATION) = id_No then Application.Terminate; end; Na podstawie powyszego kodu rdowego na starcie programu zostanie wywietlone okienko z pytaniem. Jeeli uytkownik nacinie przycisk Nie, program zostanie zamknity.

144 | S t r o n a

ProcessMeessages Piszc programy w Delphi, pewnie nieraz skorzystasz jeszcze z funkcji ProcessMessages. Owa metoda jest stosowana w trakcie wykonywania dugich i czasochonnych oblicze (np. wykonanie duej ptli), dziki czemu nie powoduje zablokowania programu na czas wykonywania owych oblicze. Zamy, e w programie wykorzystujesz du ptl for, ktra wykona, powiedzmy, milion iteracji. Do czasu, a ptla nie zakoczy swego dziaania, nasz program bdzie zablokowany. Oznacza to, e uytkownik nie bdzie mia adnych moliwoci zamknicia programu czy zmiany pooenia jego okna do czasu zakoczenia dziaania ptli. W takim wypadku naley zastosowa funkcj ProcessMessages: for I := 0 to 1000000 do begin Application.ProcessMessages; { wykonywanie instrukcji } end;

Powyszy kod sprawia, ze wykonywanie ptli nie spowoduje zawieszenia programu. Moje wyjanienie dotyczce zasady dziaania metody ProcessMessages nie byo cakiem profesjonalne, gdy wymaga zrozumienia mechanizmu zwanego komunikatami (bdziemy o tym mwi w rozdziale 5.). Funkcja ProcessMessage powoduje bowiem przepuszczenie wszystkich komunikatw z kolejki, a dopiero pniej zwrcenie sterowania do aplikacji dziki temu program nie sprawia wraenia zawieszonego. Jak pisaem, ten opis moe nie mwi Ci zbyt wiele najpierw przeczytaj rozdzia 5., a dopiero potem powr do tego opisu. Restore Wywoanie metody Restore spowoduje powrt aplikacji do normalnego stanu (jeeli jest np. zminimalizowana). Application.Restore; // przywr normalne okno

Zdarzenia klasy TApplication


Ju raz podczas czytania niniejszej ksiki miae okazj zapozna si z dziaaniem zdarzenia o nazwie OnException, z klasy TApplication. Powiniene wic wiedzie, jak wyglda obsuga zdarze klasy TApplication z poziomu Delphi. Tabela 3.6 przedstawia opis najwaniejszych zdarze. Tabela 3.6. Zdarzenia klasy TApplication

145 | S t r o n a

Zdarzenie OnActivate

Krtki opis Zdarzenie wystpuje w momencie, gdy aplikacja staje si aktywna

OnDeactivate Kiedy aplikacja przestaje by aktywna, generowane jest zdarzenie OnException O tym zdarzeniu bya ju mowa we wczeniejszych fragmentach rozdziau. Powoduje ono przechwycenie wszystkich wyjtkw zaistniaych w programie Wystpuje w momencie, gdy aplikacja przestaje by aktywna nie wykonuje adnych czynnoci

OnIdle

OnMinimize Zdarzenie jest generowane w momencie, gdy aplikacja jest minimalizowana OnRestore Kiedy aplikacja jest przywracana do normalnego stanu metod Restore, generowane jest to zdarzenie W momencie nacinicia przez uytkownika skrtu klawiaturowego generowane jest zdarzenie OnShortCut (wystpuje przed zdarzeniem OnKeyDown) W momencie pojawienia si dymka podpowiedzi generowane jest zdarzenie OnShowHint

OnShortCut

OnShowHint

Podsumowanie
Niniejszy rozdzia by powicony w caoci bibliotece VCL. Staraem si umieci w nim informacje cile zwizane z projektowaniem wizualnym mam nadziej, e wszystko jest na tym etapie zrozumiae. To, co mogo sprawi trudnoci, to zrozumienie idei klas, ale jeeli tego wci nie rozumiesz, nie przejmuj si to przyjdzie z czasem! Uwierz mi! Kiedy powrcisz do tego rozdziau i stwierdzisz, e wszystko jest takie atwe!. Zaczniki:

Listingi_3.zip (222.17 kB)

146 | S t r o n a

Rozdzia 4
IDE Delphi

Pierwsz spraw, na ktr zwraca si uwag po uruchomieniu programu, jest jego wygld, paski narzdziowe oraz menu oglnie mwic, opcje dostpne w owym programie. Ten rozdzia powicony bdzie wanie samemu rodowisku Delphi oraz funkcjom przez nie udostpnianym. Czym jest wobec tego IDE? Na IDE Delphi skadaj si:

menu gwne, paski narzdziowe, paleta komponentw, Inspektor obiektw, Projektant formularzy, Edytor kodu, Eksplorator kodu.

Niektre spord tych elementw Delphi poznae ju w rozdziale pierwszym, lecz wwczas staraem si omwi je raczej pobienie. Teraz masz okazj zapozna si dogbnie z funkcjami dostpnymi w Delphi; bd take zaznacza, jakie elementy s charakterystyczne dla nowej wersji Delphi.

Paski narzdziowe
W kadej aplikacji Windows za pomoc paskw narzdziowych mona uzyska szybki dostp do okrelonych funkcji programu. W wikszoci przypadkw paski s jedynie skrtami do rzeczywistych polece umieszczonych w menu programu; najczciej paski zawieraj tylko te najbardziej uyteczne funkcje tak te jest w rodowisku Delphi. W Delphi 7 wprowadzono nowy styl (styl XP) paskw narzdziowych oraz menu, upodobniony do wygldu paskw z systemu Windows XP.

147 | S t r o n a

Pasek Standard
Ju z sama nazwa Standard (ang. standardowy) wskazuje na to, e w pasek narzdziowy zawiera podstawowe opcje Delphi. Tak jest w istocie spjrz na rysunek 4.1.

Rysunek 4.1. Pasek Standard Na rysunku 4.1 opisaem take przeznaczenie kadego przycisku tego paska narzdziowego. Przycisk Otwrz umoliwia otwieranie tylko pojedynczych moduw lub caych projektw, natomiast za pomoc przycisku Otwrz projekt (ang. Open Project) mona otwiera tylko gwne projekty DPR . Dwa ostatnie przyciski Add file to Project (Dodaj plik do projektu) i Remove file from Project (Usu plik z projektu) umoliwiaj dodanie pliku do projektu lub usunicie go z projektu. Po klikniciu jednego z tych przyciskw Delphi doda lub usunie odpowiedni deklaracj w sekcji uses moduu.

Pasek View
Pasek View przedstawiony zosta na rysunku 4.2.

Rysunek 4.2. Pasek View Za pomoc tego paska mona wczy podgld (View) czy to moduu, czy formularza. Pierwsze dwa przyciski powoduj wywietlenie okien, w ktrych moesz wybra podgld moduu albo formularza. Za pomoc trzeciego od lewej przycisku mona przemieszcza si pomidzy formularzem a moduem; przycisk ten peni tak sam funkcj jak klawisz F12. Ostatni przycisk umoliwia tworzenie nowego formularza w ramach obecnego projektu tworzeniem aplikacji zawierajcych kilka formularzy zajmiemy si w dalszej czci tego rozdziau. Funkcje paska View s raczej rzadko uywane, nie bdziesz mia wic okazji intensywnie z niego korzysta czciej za to uywa si skrtw klawiaturowych wywoujcych te same funkcje, co wspomniane przyciski.

148 | S t r o n a

Pasek Debug
Pasek Debug zwizany jest z uruchamianiem programu oraz jego krokowym wykonywaniem. w pasek narzdziowy zosta przedstawiony na rysunku 4.3.

Rysunek 4.3. Pasek narzdziowy Debug Pierwsza ikona suy do kompilacji i uruchamiania projektu peni tak sam funkcj co polecenie Run z menu Run. Druga ikona zwizana jest z czasowym zatrzymaniem wykonywania programu jest to odpowiednik polecenia Program Pause z menu Run. Ostatnie dwa przyciski su do pracy krokowej, powoduj bowiem wykonanie programu instrukcja po instrukcji. Oznacza to, e kada operacja programu jest monitorowana przez kompilator, a odpowiedni wiersz w kodzie rdowym podwietlany. Jedyna rnica pomidzy tymi dwoma przyciskami polega na tym, e kliknicie przycisku Trace Into wcza szczegowe przeszukiwanie kodu procedur, natomiast opcja Step Over oznacza krokowe wykonanie bez wgldu w procedury.

Pasek Desktop
Rysunek 4.4. przedstawia wygld paska Desktop.

Rysunek 4.4. Pasek narzdziowy Desktop Wbrew pozorom funkcje z paska Desktop s bardzo przydatne przynajmniej ja czsto z niego korzystam. Dziki niemu mona atwo zapisa dotychczasowe ustawienia projektu oznacza to, e moesz np. ukry okno drzewa obiektw, rozcign formularz i takie ustawienia zapisa. Po kolejnym uruchomieniu Delphi okno drzewa obiektw nie bdzie ju widniao w krajobrazie Delphi. Proste wiczenie: 1. 2. 3. 4. Zamknij okno drzewa obiektw, jak tak zamykasz kade okno Windows. Rozcignij Inspektora obiektw na ca wysoko ekranu. Rozcignij take Edytor kodu na ca szeroko oraz wysoko okna. Teraz kliknij pierwszy przycisk paska Desktop pojawi si okno, w ktrym musisz poda nazw schematu ustawie. Nacinij OK.

Od tej pory takie ustawienia bd ustawieniami domylnymi.

149 | S t r o n a

Pasek Custom
Pasek narzdziowy Custom posiada tylko jeden przycisk nie ma zatem wiele do opisywania. Przycisk ten powoduje otwarcie systemu pomocy Delphi.

Pasek Internet
Pasek Internet jest domylnie ukryty moesz go wywoa, wybierajc z menu polecenia View/Toolbars/Internet. Funkcje tego paska s zwizane z bardziej zaawansowanym aspektem, a mianowicie WebSnap. Technologi WebSnap opisz w dalszej czci niniejszej ksiki.

Repozytorium
Repozytorium to okno, ktre pojawia si po wybraniu z menu File pozycji New/Other (rysunek 4.5).

Rysunek 4.5. Okno dialogowe Repozytorium Okno Repozytorium umoliwia tworzenie nowych projektw, formularzy, bibliotek DLL i innego rodzaju specjalistycznych przedsiwzi, w ktrych wykorzystywane jest Delphi. Jego obsuga nie jest trudna wystarczy spord wielu zakadek wybra interesujcy nas projekt, a nastpnie klikn przycisk OK. Zostanie utworzony nowy projekt.

150 | S t r o n a

Dodawanie projektu do Repozytorium


Moliwe jest dodanie naszego projektu do okna Repozytorium. Nie jest to trudne, a po dokonaniu tego zabiegu mona bdzie tworzy kolejne projekty w bardzo prosty sposb. Dodawanie projektw do Repozytorium jest dobrym pomysem w przypadku, gdy nasza aplikacja ma by szablonem. Utwrz jaki przykadowy projekt. Niech nie bdzie to nic skomplikowanego wystarczy zwyky formularz z jedn etykiet umieszczon na rodku. Nastpnie z menu Project wybierz polecenie Add to Repository. Wywietlone zostanie wwczas okno przedstawione na rysunku 4.6.

Rysunek 4.6. Okno Add to Repository Przed dodaniem naszego formularza do Repozytorium naley poda par informacji dotyczcych projektu. I tak w pierwszym polu Title naley wpisa tytu, jaki okrela bdzie nasz formularz w oknie Repozytorium; wpisz np. My1stProject. Drugie pole Description suy do umieszczenia krtkiego opisu; wpisz np. Projekt Hello World. Lista rozwijalna Page suy do wyboru zakadki, w jakiej umieszczona zostanie nowa ikona naszego formularza wybierz zakadk Project. W polu Author wpisz swoje imi i nazwisko. Moesz take ewentualnie wybra ikon, ktra okrela bdzie now pozycj w Repozytorium. Po naciniciu przycisku OK nowa ikona zostanie dodana do zakadki Project. Jeeli bdziesz chcia w przyszoci otworzy w formularz, zostaniesz poproszony o wskazanie miejsca, w ktrym maj by zapisywane odpowiednie pliki. Opcja Add to Project znajduje si rwnie w menu podrcznym, ktre dostpne jest po klikniciu prawym przyciskiem myszy w obszarze formularza.

Ustawienia Repozytorium
Usunicie pozycji z Repozytorium lub jej zmiana moe by dokonana za porednictwem okna Object Repository, ktre mona wywoa poprzez menu Tools/Repository. Istnieje w nim moliwo usunicia lub dodania danej zakadki oraz usunicia lub dodania konkretnej pozycji z danej zakadki.

151 | S t r o n a

Praca z palet komponentw


Okno palety komponentw (rysunek 4.7) moe by tak, jak paski narzdzi dowolnie przemieszczane.

Rysunek 4.7. Okno palety komponentw Zawiera szereg zakadek tematycznych na kadej zakadce znajduj si komponenty danej kategorii.

Umieszczanie kilku obiektw naraz


Do tej pory umieszczae dany obiekt w jednym tylko egzemplarzu. Oznacza to, e po umieszczeniu komponentu na formularzu naleao znowu klikn okrelon ikonk, aby jeszcze raz umieci kontrolk na formularzu. Tymczasem moliwe jest umieszczanie kilka razy tego samego komponentu bez koniecznoci wykonywania a tylu klikni. W tym celu podczas klikania konkretnej ikony komponentu naley nacisn przycisk Shift. Wwczas wok ikony pojawi si niebieska obwdka przeprowadmy may test: 1. Przytrzymujc klawisz Shift, kliknij ikon komponentu TLabel. Komponent zostanie oznaczony niebiesk obwdk (rysunek 4.8). 2. Umie kursor nad formularzem kade kliknicie lewego przycisku myszy w obrbie formularza powodowa bdzie umieszczenie w nim komponentu TLabel.

Rysunek 4.8. Oznaczenie ikony komponentu TLabel Na kadej zakadce palety komponentw znajduje si przycisk z ikonk kursora kliknicie go spowoduje dezaktywacj opcji i w konsekwencji zniknicie niebieskiej obwdki.

Menu palety komponentw


Paleta komponentw posiada swoje menu podrczne po klikniciu prawym przyciskiem myszy w obszarze palety pojawi si menu z list opcji. Pozycja Tabs powoduje rozwinicie kolejnej listy z nazwami zakadek. Po klikniciu ktrej z nich Delphi przejdzie do danej zakadki.

152 | S t r o n a

Pole Show Hints wcza lub wycza dymki podpowiedzi, pojawiajce si po przytrzymaniu kursora nad danym komponentem. Domylnie ta opcja jest wczona. Pozycja Hide suy do ukrycia palety komponentw. W celu ponownego wywietlenia naley klikn polecenie menu: View/Toolbars/Component Palette. Wybranie polecenia Help spowoduje wywietlenie odpowiedniej strony pomocy Delphi. Na owej stronie wywietlona zostanie pomoc dotyczca palety komponentw. Ostatnia pozycja to Properties jej wybranie spowoduje wywietlenie okna waciwoci palety komponentw (rysunek 4.9).

Rysunek 4.9. Okno Palette Properties

Waciwoci palety komponentw


Okno waciwoci palety umoliwia dostosowanie ustawie konkretnej zakadki. Za pomoc pierwszego przycisku Add mona doda now zakadk. Przycisk Delete spowoduje usunicie zaznaczonej zakadki (zakadka jednak musi by pusta przed jej usuniciem), a Rename zmian nazwy zakadki. Po zaznaczeniu konkretnego komponentu w oknie po prawej stronie uaktywniane s tylko trzy przyciski. Dwa z nich MoveUp i MoveDown su do zmiany pozycji wywietlania danego komponentu; przycisk Delete natomiast zmienia si w przycisk Hide, ktry umoliwia ukrycie danego przycisku. Przemieszczanie komponentw na inne zakadki jest moliwe za pomoc metody przecignij i upu. Wystarczy zaznaczy ktry z komponentw i przecign go na zakadki.

153 | S t r o n a

Projektant formularzy
By moe mylisz, e poznae ju wszystko, co oferuje Ci Projektant formularzy, lecz jest jeszcze par funkcji menu, o ktrych powiniene wiedzie. Jak ju zapewne zdye zauway, Inspektor obiektw jest pokryty pomocnicz siatk, ktra uatwia dopasowanie pooenia komponentu. Dokadne pooenie danego obiektu moesz wyznacza dziki klawiszom strzaek przytrzymujc klawisz Ctrl i naciskajc te klawisze moesz sterowa pooeniem aktualnie zaznaczonego komponentu. W takim wypadku pozycja moe by okrelona z dokadnoci do piksela. Inn funkcj peni klawisz Shift. Po jego przytrzymaniu mona zmienia rozmiary komponentw (take z dokadnoci do piksela). Moliwe jest zaznaczenie kilku (kilkudziesiciu lub kilkuset) komponentw naraz i przemieszanie ich wszystkich razem. W trakcie obrysowywania wszystkich obiektw zostan one zaznaczone (rysunek 4.10).

Rysunek 4.10. Trzy obiekty zaznaczone na formularzu Gdy masz zaznaczone te kilka obiektw, moesz je przemieszcza, zmienia ich rozmiary lub edytowa wsplne dla nich waciwoci (tylko takie waciwoci bd wywietlane w Inspektorze obiektw). Przeprowad mae dowiadczenie. Obrysuj wszystkie kontrolki, ktre umiecie na formularzu wwczas w Inspektorze obiektw pojawi si waciwoci wsplne dla tych wszystkich obiektw. Jeeli wszystkie zaznaczone obiekty s komponentami wizualnymi, w Inspektorze obiektw powiniene znale ga Font. Rozwi j i zmie warto waciwoci Size na 12 czcionka wszystkich komponentw powinna zosta zmieniona na rozmiar 12 punktw.

154 | S t r o n a

Menu projektanta formularzy


Kliknicie prawym przyciskiem myszy w obszarze formularza spowoduje rozwinicie menu podrcznego; opcje te dostpne s take w menu Edit. Dziki nim moesz sterowa obiektami umieszczonymi na formularzu okrela automatycznie ich pooenie itp.

Edycja obiektw Po zaznaczeniu ktrego z komponentw moesz klikn go prawym przyciskiem myszy i rozwin polecenie Edit. W podmenu tym znajduj si typowe opcje dotyczce zaznaczonego obiektu, jak np. Cut (wycinanie), Copy (kopiowanie obiektu), Paste (wklejanie obiektu ze schowka), Delete (usuwanie zaznaczonego obiektu), Select All (zaznaczenie wszystkich obiektw na formularzu) czy Undo (cofnicie ostatniej operacji). Operacje te wykona moesz take za pomoc skrtw klawiaturach, jak np. Ctrl+X (wycinanie), Ctrl+C (kopiowanie), Ctrl+V (wklejanie) i Ctrl+A (zaznaczenie wszystkich obiektw). Przeprowad mae dowiadczenie: zaznacz dwa komponenty TLabel, ktre uprzednio umiecie na formularzu. Nastpnie z menu Edit wybierz Copy; w tym momencie do schowka powdroway dwa obiekty typu TLabel. Kliknij w obszarze formularza spowoduje to usunicie zaznaczenia wszystkich komponentw. Teraz z menu Edit wybierz Paste do formularza dodane zostan dwa komponenty ze schowka. Zwr uwag na to, e nie dopuszczono do sytuacji, w ktrej na formularzu znalazyby si dwa komponenty o tej samej nazwie pola Name obiektw, ktre uprzednio byy w schowku, zostay zmienione.

Pooenie obiektw Podrczne menu (lub, jak kto woli, menu Edit) oferuje take opcje suce do kontroli pooenia obiektu. S to pozycje Send to Back oraz Bring to Front. Obie s uywane w sytuacji, gdy dwa obiekty nakadaj si na siebie. Spjrz na rysunek 4.11. Widniej na nim dwa obiekty: jeden (TButton) jest umieszczony nad drugim (TMemo).

155 | S t r o n a

Rysunek 4.11. Dwa obiekty naoone na siebie Opcje Send to Back oraz Bring to Front umoliwiaj wanie ustawienie, ktra z tych kontrolek bdzie znajdowa si na wierzchu, a ktra pod spodem. Pierwsza nich (Send to Back) spowoduje schowanie zaznaczonego obiektu pod obiekt, na ktrym si znajduje. Druga natomiast (Bring to Front) wysuwa zaznaczon kontrolk na wierzch. Pozycja komponentw ~~~~~~~~~~~~~ Po wywietleniu menu podrcznego i wybraniu polecenia Position zostanie rozwinite podmenu polece zwizanych z ustawieniami pooenia kontrolek. Za pomoc pierwszego z nich Align to Grid moliwe jest umiejscowienie komponentw zgodnie z siatk formularza. Inaczej mwic, opcja ta umoliwia wyrwnanie komponentw. Wybranie kolejnego polecenia Align spowoduje wywietlenie okienka Alignment (rysunek 4.12).

Rysunek 4.12. Okno Alignment Okno Alignment umoliwia ustawienie pooenia komponentw wzgldem siebie. Przykadowo moesz zaznaczy dwa komponenty jednoczenie TMemo oraz TButton. Po wywietleniu okna Alignment i wybraniu pozycji Center zarwno w ramce Horizontal, jak i Vertical komponenty zostan ustawione porodku wzgldem siebie (rysunek 4.13).

Rysunek 4.13. Komponenty wyrodkowane wzgldem siebie 156 | S t r o n a

Nastpna pozycja w menu Position to Size. Umoliwia ona dopasowanie rozmiarw komponentw wzgldem siebie, czyli np. dokadne dopasowanie wielkoci komponentw czy te dopasowanie do wikszego z zaznaczonych obiektw. Pozycja Scale powoduje zmian rozmiarw obiektw w skali procentowej. Wystarczy w okienku, ktre pojawi si po wybraniu opcji Scale, wpisa liczb pomidzy 25 a 100. Nastpna pozycja to Tab Order. Po wybraniu tego polecenia wywietlone zostanie okno, w ktrym istnieje moliwo ustawienia kolejnoci, w jakiej po naciniciu przycisku Tab zaznaczane bd komponenty. Istnieje bowiem moliwo przemieszczania si po komponentach umieszczonych na formularzu za pomoc przycisku Taba. Nie jest to jednak zbyt popularna opcja zapewne nie bdziesz z niej czsto korzysta. Kolejna opcja Creating Order umoliwia ustawienie kolejnoci, w jakiej podczas uruchamiania programu bd tworzone komponenty niewidoczne. Zazwyczaj w takiej sytuacji komponenty s tworzone w kolejnoci umieszczenia ich na formularzu moesz to jednak zmieni za pomoc okna Creating Order. Kolejne dwie pozycje menu View as Form oraz Add to Repository powinny by Ci ju znane; miae okazj sprawdzi, jak dziaaj. Ostatnia pozycja Text DFM pojawia si po raz pierwszy w Delphi 5. Domylnie jest ona zaznaczona, co powoduje, e pliki *.dfm projektu s zapisywane w postaci tekstowej; usunicie jej zaznaczenia spowoduje zapisywanie plikw w postaci binarnej.

Drzewo obiektw
Drzewo obiektw suy do graficznego przedstawienia wzajemnej relacji pomidzy obiektami graficznymi i niewidocznymi. Okno drzewa obiektw zostao zaprezentowane na rysunku 4.14.

Rysunek 4.14. Drzewo obiektw Po zaznaczeniu konkretnej pozycji w Inspektorze obiektw pojawiaj si waciwoci konkretnego obiektw, a sam obiekt w formularzu zostaje zaznaczony. Jak wida na rysunku 4.14, istnieje moliwo zaznaczenia wielu obiektw naraz, co daje efekt identyczny z obrysowaniem lub pojedynczym zaznaczaniem obiektw na formularzu.

157 | S t r o n a

Inspektor obiektw
Mogoby si wydawa, e znasz ju wszystkie istotne informacje dotyczce Inspektora obiektw uywae go w kocu czsto podczas dotychczasowej pracy z Delphi. Postaram si jednak teraz omwi te opcje Inspektora Obiektw, z ktrymi nie miae wczeniej do czynienia. Poczwszy od Delphi 6, Inspektor obiektw umoliwia wywietlanie waciwoci kilku obiektw, ktre s ze sob powizane (rysunek 4.15).

Rysunek 4.15. Powizane obiekty przedstawione w Inspektorze obiektw Kady formularz posiada waciwo ActiveControl, okrelajc, ktra z kontrolek bdzie aktywna zaraz po uruchomieniu programu. Po wybraniu z listy rozwijalnej nazwy ktrej z kontrolek (np. Button1) istnieje moliwo rozwinicia listy wszystkich waciwoci owego komponentu Button1.

Menu podrczne Inspektora obiektw


Po klikniciu prawym przyciskiem myszy w obszarze Inspektora obiektw pojawi si menu podrczne. Ciekaw opcj menu jest opcja Arrange, ktra umoliwia uporzdkowanie waciwoci wedug nazwy (by Name) lub kategorii (by Category). Uporzdkowanie waciwoci na kategorie da taki efekt, jak przedstawiony na rysunku 4.16.

158 | S t r o n a

Rysunek 4.16. Waciwoci Inspektora obiektw uporzdkowane wedug kategorii Tak samo jest z zakadk Events tam rwnie zdarzenia mog by sortowane wedug nazwy lub kategorii.

Waciwoci Inspektora obiektw


W menu podrcznym znajduje si opcja Properties po jej klikniciu mamy moliwo ustawienia rnych opcji dotyczcych Inspektora obiektw, w tym kolorw uywanych przez to okno (rysunek 4.17).

159 | S t r o n a

Rysunek 4.17. Okno ustawie Inspektora obiektw Po lewej stronie tego okna znajduj si opcje dotyczce kolorystyki Inspektora obiektw moemy np. ustawi kolor wywietlania waciwoci tylko do odczytu lub kolor ta. Prawa strona to ju opcje cile zwizane z ustawieniami Inspektora obiektw. Krtkie omwienie tych opcji znajduje si w tabeli 4.1 oraz 4.2. Tabela 4.1. Opcje ramki Options Opcja Show instance list Opis opcji Jeeli pozycja jest zaznaczona, to Inspektor obiektw bdzie posiada u gry list rozwijaln z obiektami znajdujcymi si na formularzu

Show classname in Grna lista Inspektora obiektw zawiera bdzie take nazw klasy, a nie instance list jedynie nazw kontrolki. Show Status bar Jeeli opcja jest zaznaczona, na dole Inspektora obiektw wywietlany bdzie pasek stanu

Render background Dziki tej opcji kada z waciwoci oddzielona jest od pozostaych poziom lini grid Integral height Opcja ta dotyczy rozcigania Inspektora obiektw w pionie. Jeeli opcja jest 160 | S t r o n a

zaznaczona, wysoko bdzie dopasowana do ostatniego wiersza Inspektora obiektw Show read only properties Bold non default values Domylnie niezaznaczona. Pokazuje take waciwoci Inspektora obiektw, ktre s jednie do odczytu Wywietla pogrubionym krojem te wartoci, ktre nie s domylne

Tabela 4.2. Opcje ramki References Opcja Opis opcji

Opcja dotyczy tego, o czym mwiem na pocztku, czyli pokazywania waciwoci Expand inline obiektu poczonego. Jeeli opcja jest wyczona, takie waciwoci nie bd si pojawiay Show on Jeeli opcja nie jest zaznaczona, w zakadce Events nie pojawi si waciwoci events page powizanego komponentu

Eksplorator kodu
Eksplorator kodu to okienko, ktre domylnie jest zadokowane w edytorze kodu. Przedstawia ono (rysunek 4.18) moduy oraz komponenty uyte w danym module.

161 | S t r o n a

Rysunek 4.18. Klasy i moduy uyte w pliku Unit1.pas Po klikniciu danej pozycji w Eksploratorze kodu Delphi ustawi kursor na jej deklaracji. Jeeli zatem klikniesz np. pozycj Classes/Form1/Published/Memo1, to kursor zostanie ustawiony w miejscu deklaracji obiektu Memo1. Okno Eksploratora kodu take posiada swoje menu podrczne. Ciekawym poleceniem jest New, dziki ktremu moemy przykadowo doda modu do listy uses czy utworzy now zmienn Delphi automatycznie doda odpowiedni deklaracj do kodu. Przejd do gazi Variables/Constans (zmienne i stae), kliknij j prawym przyciskiem myszy i z menu wybierz New. W Eksploratorze kodu dodana zostanie nowa pozycja; po wpisaniu np. S : String w Edytorze kodu zostanie dodana nowa zmienna globalna S. Istnieje take moliwo ustawienia bardziej szczegowych opcji dotyczcych Eksploratora kodu. Wystarczy z menu podrcznego wybra pozycj Properties.

162 | S t r o n a

Przegldarka projektu
Przegldarka projektu to okno wywoywane poprzez menu View/Browser (rysunek 4.19).

Rysunek 4.19. Przegldarka projektu Okno przegldarki projektu suy do wywietlania informacji dotyczcych projektu: klas, moduw, zawartoci klas itp. Wszystko uporzdkowane jest w hierarchicznej kolejnoci obiektw. Oto lista zakadek:

Globals lista klas, waciwoci, typw oraz zmiennych. Classes lista klas VCL, przedstawiona w hierarchicznej kolejnoci. Units lista moduw i deklaracji z kadego moduu.

Oglnie rzecz biorc, okno Przegldarki obiektu wydaje si troch zapominane; ja sam przyznam szczerze, e prawie w ogle z niego nie korzystam, podobnie jak wielu programistw. Moe Ty bdziesz zaglda do niego czciej?

163 | S t r o n a

Lista To-Do
To do w dosownym tumaczeniu oznacza do zrobienia. Podczas prac nad duym projektem, w ktrych bierze udzia wiele osb, bardzo przydaje si taka lista zada do wykonania i spraw ju gotowych. W tym celu Borland udostpni bardzo wygodne narzdzie, ktre mona wywoa za porednictwem menu View/To-Do List (rysunek 4.20).

Rysunek 4.20. Lista spraw do zrobienia Menu podrczne zawiera polecenie Add, dziki ktremu moesz doda now pozycj. W stosownym oknie powiniene wpisa potrzebne informacje:

Text informacja (zadanie) do wykonania. Priority priorytet wykonania operacji. Owner osoba dodajca notk. Categroy kategoria.

Jak widzisz, w oknie To-Do znajduje si jeszcze jedna kolumna Module ale o tym powiem nieco pniej. Pierwszy haczyk umoliwia usunicie zaznaczenia opcji jako wykonanej (done). Jeeli opcja nie jest zaznaczona, stanowi to informacj dla projektantw, e okrelone zadanie naley wykona.

Znaczniki to-do w kodzie


Istnieje take moliwo oznaczenia jako komentarz pewnego fragmentu kodu, ktry pniej pojawi si w oknie To-Do. Jest to bardzo wygodne, gdy informacja znajduje si zarwno w kodzie programu, jak i w oknie To-Do. 164 | S t r o n a

procedure TForm1.Button1Click(Sender: TObject); begin StrToInt(edtValue.Text); {TODO 8 oAdam cWane: Tutaj dodaj wyjtek!} end;

Spjrz na powyszy fragment, a dokadniej mwic, na komentarz zaczynajcy si od sowa TODO. W oknie To-Do ten fragment jest identyfikowany podczas analizy kodu. Wybierz teraz ponownie menu View/To-Do list, a w oknie widoczna bdzie wpisana w kodzie informacja (rysunek 4.21).

Rysunek 4.21. Nowa pozycja do zrobienia Konstrukcja komentarza, ktry kwalifikuje si do To-Do, powinna wyglda nastpujco: {TODO 8 o<osoba dodajca notk> c<kategoria>: <informacja>}

Istnieje take moliwo zastpienia sowa TODO sowem DONE. Wtedy dana notka bdzie kwalifikowana jako zrobione. procedure TForm1.Button1Click(Sender: TObject); begin try StrToInt(edtValue.Text); except raise; end; {DONE 8 oAdam cWane: Tutaj dodaj wyjtek!} end;

Nacinicie Ctrl+Shift+T spowoduje wywietlenie okna sucego do dodania pozycji do okna To-Do List.

165 | S t r o n a

Diagramy
Diagramy su do graficznego przedstawienia zalenoci midzy komponentami; korzystamy z nich za porednictwem zakadki Diagram w edytorze kodu. Uywane s w poczeniu z oknem Object TreeView (drzewo obiektw), tak wic musisz wywietli drzewo obiektw, aby mc korzysta z diagramw. Przecigajc elementy z drzewa obiektw nad diagram (metod przecignij i upu), spowodujesz umieszczenie elementu w diagramie. Uywajc tego okna, moesz take doda notki i komentarze przeznaczone dla innych twrcw oprogramowania (rysunek 4.22).

Rysunek 4.22. Diagram Okno Diagram posiada w lewym, grnym rogu list rozwijaln. Za jej pomoc mona utworzy kilka diagramw. Nazw konkretnego diagramu ustala si w polu tekstowym Name; moesz doda take opis w polu Description. Jak ju pisaem, poprzez przeciganie obiektw z okna Object TreeView umieszczasz odpowiedni ikon w diagramie. Strzak (graficzne pooenie) moesz doda za pomoc przycisku Allude connector. Komentarz mona umieci w dowolnym miejscu, a realizuje to przycisk Comment block; umieszczenie komentarza jest podobne do umieszczania komponentu. Korzystanie z diagramw jest raczej do intuicyjne jeeli chcesz zasign wicej informacji na ten temat, odsyam Ci do pomocy Delphi.

166 | S t r o n a

Code Insight
Tematem kolejnego punktu tego rozdziau jest Code Insight narzdzie niezwykle pomocne w programowaniu w Delphi. Trudno jest zapamita deklaracj kadej z procedur czy np. list metod w danej klasie. Code Insight jest jakby pomocnikiem, ktry uatwia wpisywanie parametrw do funkcji. Podstawow funkcj Code Insight jest pokazywanie deklaracji funkcji, ktr wpisujemy. Wygeneruj zdarzenie OnClick komponentu TButton i wpisz nazw polecenia MessageBox (

Po postawieniu nawiasu oznaczajcego rozpoczcie wpisywanie parametrw Delphi wywietli podgld parametrw, ktre maj znale si w funkcji (rysunek 4.23).

Rysunek 4.23. Code Insight w trakcie dziaania Pogrubion czcionk jest zaznaczony parametr, ktry aktualnie wpisujemy. Skrtem klawiaturowym, ktry powoduje wywietlenie podpowiedzi, jest <i>Ctrl+Shift+spacja</i>.

Code Completion
Inn usug technologii Code Insight jest Code Completion. Usuga ta umoliwia podgld wszystkich moliwych w danym momencie do zastosowania procedur, funkcji czy waciwoci. Skrtem klawiaturowym Code Completion jest Ctrl+spacja; samo okno przedstawione jest na rysunku 4.24. 167 | S t r o n a

Rysunek 4.24. Code Completion W trakcie pisania kodu po postawieniu operatora kropka (.) take wywietlona zostanie lista wszystkich metod danego obiektu (klasy). Lista Code Completion jest dopasowywana do tekstu, ktry aktualnie wpisujesz. Oznacza to, e gdy wpisujesz np. liter B, wwczas na licie Code Completion pojawi si jedynie metody o nazwach zaczynajcych si na liter B. Po licie Code Completion moesz przemieszcza si za pomoc klawiszy strzaek na klawiaturze; nacinicie przycisku Enter spowoduje wstawienie do kodu zaznaczonej pozycji.

Code Completion zmiany w Delphi 7 Wedug zapewnie twrcw Code Insight jest teraz szybszy ni poprzednio oraz daje moliwo przejcia do deklaracji danej funkcji lub procedury. Jest to moliwe poprzez nacinicie klawisza Ctrl i umieszczenie kursora nad konkretn pozycj Code Insight; po wybraniu konkretnego wiersza Delphi przeniesie nas do deklaracji owej procedury lub funkcji. W wersjach Professional oraz Enterprise istnieje moliwo wykorzystania Code Completion take w plikach HTML. Poleceniem File/Open moesz otworzy jaki plik HTML. Wwczas Code Completion (Ctrl+spacja) bdzie zawiera list moliwych do zastosowania znacznikw HTML.

168 | S t r o n a

Ustawienia Code Insight W Delphi 7 wprowadzono moliwo edycji ustawie Code Insight poprzez okno Tools/Editor Options (zakadka Code Insight). Przede wszystkim lista Code Completion posiada rne oznaczenia kolorystyczne funkcji, procedur i zmiennych. Ustawienie te moesz zmieni w ramce CodeInsight Colors. Za pomoc suwaka Delay moesz zmieni czas, po ktrym ujrzysz Code Insight (przypominam, e moesz go wywoa w kadej chwili za pomoc skrtu Ctrl+spacja).

Projekty
Na pocztku omwi spraw moe dla Ciebie banaln, czyli kompilowanie i uruchamianie aplikacji. Przecie robie to ju wiele razy podczas projektowania programw w Delphi, lecz tym razem trzeba dokadniej omwi pewne pojcia, a mianowicie kompilowanie, budowanie i sprawdzanie bdw. W menu Project znajduj si trzy polecenia, ktre nas interesuj: Compile, Build, Syntax check.

Syntax Check jest najszybsz metod sprawdzania bdw w aplikacji. Sprawdzanie nastpuje tylko w tych moduach, w ktrych nastpia jaka zmiana od czasu ostatniej kontroli poprawnoci. Za pomoc tego polecenia nie dokonuje si kompilacji kodu (moduw i aplikacji wykonywalnej) sprawdzane jest jedynie, czy kod nie zawiera bdw. Compile powoduje kompilacj moduw (plikw), ktre nie zostay jeszcze skompilowane lub w ktrych nastpia jaka zmiana. Najpierw dokonywane jest sprawdzenie bdw opcja ta nie jest zatem tak szybka, jak Syntax Check. Build jest opcj dziaajc najwolniej. Powoduje ona kompilacj wszystkich moduw oraz caego projektu niezalenie od tego, czy w danych plikach nastpiy jakie zmiany czy te nie.

W menu Project znajduj si take dwie inne opcje Compile All Projects oraz Build All Projects. Te dwie pozycje s wykorzystywane podczas pracy z menederem projektw (meneder projektw zostanie omwiony pniej).

Opcje projektu
Istnieje moliwo ustawiania opcji specyficznych dla kadego projektu. Ustawienia te s nastpnie przechowywane w plikach *.dof i *.cfg, a ich kontroli mona dokona w menu Project/Options (rysunek 4.25).

169 | S t r o n a

Rysunek 4.25. Okno opcji projektu Okno opcji projektu podzielone jest na kilka zakadek omwienie ich zajmie najbliszych par stron. U dou okna znajduje si pozycja Default. Po jej zaznaczeniu wszystkie ustawienia dokonane przez Ciebie zostan ustawieniami domylnymi.

Zakadka Forms W zakadce Forms (rysunek 4.25) znajduj si dwie listy, ktre zawieraj nazwy wszystkich formularzy obecnych w programie. Pierwsza lista okrela formularze, ktre bd tworzone automatycznie podczas uruchamiania programu, a druga zawiera te formularze, ktre naley utworzy w sposb dynamiczny. Tworzeniem kilku formularzy w projekcie bardziej szczegowo zajm si w dalszej czci tego rozdziau.

Zakadka Application Zakadka Application podzielona jest na dwie ramki Application Settings oraz Output Settings (rysunek 4.26).

170 | S t r o n a

Rysunek 4.26. Okno opcji projektu zakadka Application Pierwsza ramka zawiera pole Title, w ktrym mona okreli nazw aplikacji (pojawiajc si na pasku zada). Wpisanie danych w tym polu nie jest obowizkowe (jeli pozostawimy je puste, nazw bdzie nazwa projektu). Kolejna pozycja Help file identyfikuje nazw pliku pomocy. Moliwe jest bowiem zintegrowanie pliku pomocy (z rozszerzeniem *.hlp) z aplikacj Delphi (w poczeniu z niektrymi waciwociami komponentw patrz rozdzia 3.). Wan spraw jest odpowiedni dobr ikony dla aplikacji wykonywalnej. Za pomoc przycisku Load Icon moesz okreli ikon, ktra ozdabia bdzie nasz program. Ikona dla aplikacji wykonywalnej przechowywana jest w pliku *.res, dlatego nie usu go przez przypadek!

Ramka Output settings zawiera jedno pole Target file extension, ktre suy do okrelenia domylnego rozszerzenia dla programu (domylnym rozszerzeniem jest *.exe). Moesz wpisa w to pole np. rozszerzenie .ocx, a nastpnie skompilowa program. Zobaczysz, e w katalogu z projektem znajdzie si plik z rozszerzeniem OCX.

171 | S t r o n a

Zakadka Compiler To, co rzuca si w oczy na samym pocztku, to mnogo opcji; wszystkie one zwizane s z kompilatorem Delphi. Wygld tej zakadki przedstawiono na rysunku 4.27. natomiast opis poszczeglnych opcji tej zakadki, znajduje si w tabelach 4.3. 4.5.

Rysunek 4.27. Okno opcji projektu zakadka Compiler Tabela 4.3. Ramka Code generation Pozycja Optimization Stack frames Pentium-safe FDIV Record field alignment Opis Jeeli opcja jest zaznaczona (domylne ustawienie), kompilator wygeneruje moliwie jak najefektywniej dziaajcy kod Wyczenie tej opcji powoduje, e czas kompilacji bdzie moliwie najkrtszy Dotyczy starszych procesorw Pentium. Kompilator jest w stanie poradzi sobie z bdem dzielenia zmiennoprzecinkowego Okrela wyrwnanie rekordw w pamici (w bajtach). Moesz wybra liczb z listy rozwijalnej

172 | S t r o n a

Tabela 4.4. Ramka Runtime Error Pozycja Range checking I/O checking Overflow checking Opis Wczenie tej opcji powoduje, i Delphi sprawdza, czy nie przekroczone zostay granice tablic lub zmiennych String Sprawdzenie bdw wejcia-wyjcia Sprawdzanie, czy liczba Integer mieci si w odpowiednim zakresie

Tabela 4.5. Ramka Syntax Options Pozycja Strict var-strings Opis Opcja zwizana z kompatybilnoci dugich acuchw

Complete boolean Opcja zwizana z operacjami porwnania za pomoc operatorw or i and eval Extended syntax Opcja wcza przekazywanie wartoci zwrotnych przez funkcje

Typed @ operator Zwizane z operatorem @ i typami wskanikowymi Open parameters Huge strings Assignable typed constants Wcza przekazywanie otwartych acuchw w deklaracji funkcji i procedur. Jeeli opcja jest wyczona, typ String jest identyfikowany z ShortString. W przeciwnym wypadku przedmiotem identyfikacji jest AnsiString Kompatybilno z Delphi 1. Jeeli opcja jest wczona, to stae mog by modyfikowane (tak jak to miao miejsce w Delphi 1)

Zakadka Compiler posiada take ramk Debugging, zwizan z zachowywaniem informacji potrzebnych debugerowi.

Zakadka Compiler Message Jedn z nowoci w Delphi 7 jest nowa zakadka Compiler Message znajdujca si w oknie opcji projektu (rysunek 4.28).

173 | S t r o n a

Rysunek 4.28. Zakadka Compiler Message Opcje tej zakadki pozwalaj na wyczenie wywietlania przez kompilator Delphi okrelonych ostrzee. Na samej grze znajduj si dwie pozycje Show hints i Show warnings. Okrelaj one, czy Delphi ma wywietla odpowiednio podpowiedzi i ostrzeenia. Konkretne ostrzeenia moesz wyczy na licie Warnings.

Zakadka Linker Zakadka Linker (rysunek 4.29) zawiera nieco bardziej zaawansowane ustawienia Delphi.

174 | S t r o n a

Rysunek 4.29. Zakadka Linker Pierwsza sekcja, Map file, suy do tworzenia tzw. map pliku (projektu) i zarazem okrela szczegowo tych map. Delphi podczas kompilacji programu automatycznie tworzy plik *.map zawierajcy bardziej szczegowe informacje, takie jak adres startowy, ostrzeenia i bdy. Kolejna sekcja EXE and DLL options zawiera opcje dotyczce tworzenia pliku wynikowego. Pierwsza opcja okrela, czy Delphi ma tworzy aplikacje konsolowe; kolejne wymuszaj na Delphi doczenie do pliku *.exe informacji na temat zewntrznego debugera. Ciekawe opcje zawiera kolejna sekcja Linker Output. Umoliwia ona bowiem generowanie zamiast standardowych plikw *.dcu plikw *.obj, ktre obsuy moe C++ Builder. W sekcji Memory sizes znajduj si informacje na temat bazowego adresu moduu. Ta opcja jest przeznaczona dla najbardziej zaawansowanych uytkownikw moesz pozostawi tutaj wartoci domylne.

Zakadka Directories/Conditionals W zakadce Directories/Conditionals znajduj si kontrolki suce do okrelania rnych cieek do plikw wymaganych przez Delphi. Moesz przykadowo okreli ciek, w ktrej Delphi umieci pliki wynikowe programu lub ciek do moduw Delphi.

175 | S t r o n a

Zakadk Directories/Conditionals zaprezentowano na rysunku 4.30.

Rysunek 4.30. Zakadka Directories/Conditionals Pola edycyjne mog zawiera kilka cieek, oddzielonych od siebie znakiem rednika. Aby umili uytkownikowi prac, obok kadej takiej kontrolki znajduj si przyciski ze znakiem wielokropka (...). Po klikniciu przycisku wywietlone zostanie okno suce do wyboru katalogu.

Zakadka Version Info Wiele programw dziaajcych w systemie Windows posiada doczone informacje na temat wersji aplikacji. Po otworzeniu okna waciwoci danego pliku informacje te mona znale na zakadce Wersja. Zakadka Version Info umoliwia doczanie do programu takich wanie informacji (rysunek 4.31).

176 | S t r o n a

Rysunek 4.31. Zakadka Version Info Domylnie opcje dodawania informacji do pliku EXE lub DLL s wyczone aby skorzysta z tej zakadki, naley wczy funkcj poprzez zaznaczenie opcji Include version information in project. Gwna sekcja Module version number umoliwia ustawienie wersji aplikacji wykonywalnej. Numer wersji okrelaj cztery numery Major Version, Minor Version, Release i Build. Po zaznaczeniu pozycji Auto-incremenent build number warto pola wersji Build bdzie automatycznie zwikszana przy kadym zbudowaniu projektu. Na samym dole zakadki znajduje si lista podzielona na dwie kolumny. Tutaj moesz wpisa nieco wicej informacji dotyczcych swojego projektu.

Zakadka Packages Zakadka Packages (rysunek 4.32) zwizana jest z tzw. pakietami.

177 | S t r o n a

Rysunek 4.32. Zakadka Packages Ciekaw opcj zawiera sekcja Runtime Packages. Pozycja Build with runtime packages okrela, czy pakiety zostan doczone do programu, czy te bd zawarte w postaci oddzielnych plikw. Opcja Build with runtime packages ma bardzo wane znaczenie. Pozostawienie jej wyczonej powoduje bowiem zmniejszenie rozmiarw aplikacji wykonywalnej o wielko plikw koniecznych do prawidowego dziaania programu. Majc pewno, e na maszynie, na ktrej ma by uruchomiony dany program, znajduje si Delphi 7, moesz t opcj wyczy, co znacznie zmniejszy rozmiar pliku EXE.

Pliki projektu
Po zapisaniu projektu na dysku w katalogu pojawi si szereg plikw zwizanych z danym projektem. Niektre z tych plikw generowane s po procesie kompilacji i przeznaczone s jedynie na potrzeby Delphi. Niektre z nich mog by zreszt automatycznie odtworzone po kolejnej prbie kompilacji programu. Przykadowo pliki *.dcu to skompilowane pliki moduw Delphi, natomiast pliki z rozszerzeniem *.exe to gotowe do uruchomienia programy, generowane w wyniku procesu kompilacji. Pliki z tymi rozszerzeniami mog by bez wahania usunite. Kolejny rodzaj plikw to dane zwizane z konkretnym ustawieniem opcji projektu, czyli ustawienia kompilatora, cieki do moduw itp., przechowywanew plikach *.cfg i *.dof. Owe pliki take mog zosta usunite gdy otworzysz projekt, a Delphi nie bdzie mg odnale danych zwizanych z opcjami projektu, zastosuje ustawienia domylne. Istnieje kilka plikw, bez ktrych ponowna kompilacja projektu nie jest moliwa. Do takich plikw 178 | S t r o n a

nale: *.pas (kody rdowe moduw), *.dpr (plik gwny projektu) oraz *.dfm (ustawienia formularza, pozycje i waciwoci komponentw). A zatem podsumujmy:

.pas pliki rdowe; w nich znajduj si kody rdowe formularzy i moduw. .dfm jest to plik formularza; zawiera informacje dotyczce komponentw umieszczonych na formularzu (ich pooeniu, nazwie itp.). W przypadku zastosowania biblioteki CLX w miejsce VCL rozszerzeniem tego pliku bdzie *.xmf, lecz o tym powiemy dopiero w czwartej czci ksiki). .dcu skompilowany plik *.pas; nie jest potrzebny po kolejnej prbie kompilacji Delphi odbuduje plik *.dcu na podstawie kodw rdowych. .dpr gwny plik formularza. .cfg konkretne ustawienia kompilatora; mog by rne dla kadego projektu, std w przypadku, gdy Delphi nie bdzie mg znale tego pliku, zastosuje ustawienia domylne. .dof dodatkowe opcje projektu; plik zawiera np. informacje o wersji programu itp. .res tzw. zasoby. O zasobach bdzie mowa w dalszej czci ksiki. Plik ten zawiera np. ikon, ktra ozdabia plik *.exe. Usunicie tego pliku moe mie znaczcy wpyw na dalsze dziaanie programu, lecz gdy wiesz, co robisz i jeste pewien, e brak tego pliku nie wpynie negatywnie na dziaanie aplikacji, moesz go usun. .ddp plik diagramu, zawiera informacje o stworzonych przez nas diagramach; take nie jest wymagany podczas kolejnej kompilacji programu.

Domylne ustawienia Delphi nakazuj automatyczne tworzenie kopii zapasowych. Pliki takie maj tak sam zawarto, jak ich pierwowzory jedyn rnic jest obecno znaku tyldy (~) w rozszerzeniu. Jeeli chcesz wyczy opcj tworzenia kopii zapasowych, z menu Tools wybierz polecenie Editor Options. Kliknij zakadk Display i usu zaznaczenie pozycji Create Backup File.

Meneder projektu
Wiksze programy wymagaj wielu wierszy kodu, czasem take wielu plikw pomocniczych, jak np. biblioteki DLL czy rne aplikacje EXE. Jak wiesz, Delphi umoliwia prac w tym samym czasie nad jednym projektem. Jeeli chcesz edytowa projekt biblioteki DLL czy pomocniczej aplikacji EXE, to za kadym razem musisz go otwiera. Dziki menederowi projektu przeczanie si pomidzy rnymi projektami staje si znacznie prostsze. Okno menedera projektu mona wywietli wybierajc pozycj menu: View/Project manager. Samo okno menedera projektw przedstawione zostao na rysunku 4.33.

179 | S t r o n a

Rysunek 4.33. Okno menedera projektu Meneder projektu suy take do graficznego przedstawiania (w formie gazi) formularzy i moduw wczonych do danej aplikacji. atwo mona take doda lub usun dany plik lub nawet cay projekt.

Pasek narzdziowy
Dziki paskowi narzdziowemu moemy w oknie menedera projektu doda lub usun jaki element. Lista rozwijalna suy do przeczania si pomidzy dwoma projektami. Przycisk New powoduje otworzenie Repozytorium, a za pomoc przycisku Remove moemy usun konkretny plik z projektu lub cay projekt. Ostatni przycisk Activate suy do aktywacji konkretnego projektu. Nazwa aktywnego projektu jest wywietlana pogrubion czcionk. Nacinicie klawisza F9 spowoduje uruchomienie aktywnego projektu. Wykonaj teraz pewne wiczenie, majce na celu stworzenie nowego projektu i dodanie go do okna Project manager. Nacinij przycisk New, a z okna Repozytorium wybierz ikon Application. Spowoduje to utworzenie nowego projektu Delphi, a w centralnym oknie Project manager pojaw si nowa ga Project1.exe. Aby okno menedera projektu nie chowao si gdzie w gbi, radz wczy opcj Stay On Top (ang. pozosta na wierzchu). Kliknij prawym przyciskiem myszy gdzie w obszarze okna Project manager i z menu podrcznego wybierz polecenie Stay On Top.

Praca z menederem projektu


Kolejnym krokiem podczas pracy z menederem projektu bdzie utworzenie kolejnego projektu. Ponownie nacinij przycisk New i wybierz z Repozytorium ikon Application. W tym momencie okno menedera powinno wyglda podobnie do tego z rysunku 4.34. 180 | S t r o n a

Rysunek 4.34. Okno menedera projektw z dwoma otwartymi projektami Po klikniciu konkretnej pozycji na licie w plik zostanie otwarty w Edytorze kodu. Jak widzisz na rysunku 4.34, jeden z projektw jest wywietlany pogrubion czcionk. Pogrubienie oznacza aktywny projekt. Po naciniciu klawisza F9 Delphi uruchomi aktywny projekt. Innym sposobem jest uycie przycisku Run z paska narzdziowego Debug. Obok tego przycisku znajduje si inny przycisk, po ktrego rozwiniciu pojawi si lista rozwijalna, zawierajca aktywne projekty (rysunek 4.35).

Rysunek 4.35. Projekty otwarte w oknie Project Manager

Menu podrczne menedera projektw


Zdecydowaem si opisa menu podrczne menedera projektw z tego wzgldu, i jest ono do obszerne i zalene od tego, jak pozycj klikniemy. Przykadowo inne menu pojawi si w przypadku, gdy klikniemy prawym przyciskiem w obrbie pola MyGroup, a inne, gdy klikniemy w obszarze AppProject.exe.

181 | S t r o n a

Menu grupy projektw Piszc o grupach projektw mam na myli najwysz ga, na rysunku 4.34 oznaczon napisem MyGroup. Menu podrczne moe zawiera nastpujce elementy:

Add New Project


Moesz wybra t pozycj menu w celu utworzenia nowego projektu. Jej wybranie spowoduje wywietlenie Repozytorium, po czym wybrany projekt zostanie dodany do listy menedera projektw. Opcja dziaa tak samo jak przycisk New na pasku narzdziowym okna Project Manager.

Add Existing Project


Caa lista projektw z okna Project Manager moe by zapisana w jednym pliku *.bpg. Polecenie Add Existing Project spowoduje otwarcie okna, w ktrym mona wybra plik *.bpg lub *.dpr, ktry nastpnie zostanie dodany do listy menedera projektw.

Save Project Group, Save Project Group As


Te dwa polecenie su do zapisywania listy menedera projektw w jednym pliku *.bpg. Pierwsze z nich powoduje zapisanie projektu, natomiast po wybraniu drugiego polecenia bdziesz mia moliwo zapisu listy projektw pod now nazw.

View Project Group Source


Wybranie tej pozycji spowoduje wywietlenie w Edytorze kodu zawartoci pliku *.bpg. Plik taki moe wyglda np. tak, jak na listingu 4.1, lecz jego edycja nie jest zalecana. Najlepiej dokonywa zmian z poziomu okien dialogowych Delphi, a zawarto pliku *.bpg pozostawia bez zmian (plik jest uywany przez Delphi). Listing 4.1. Zawarto pliku *.bpg #----------------------------------------------------------------------------VERSION = BWS.01 #----------------------------------------------------------------------------!ifndef ROOT ROOT = $(MAKEDIR)\.. !endif #------------------------------------------------------------------182 | S t r o n a

----------MAKE = $(ROOT)\bin\make.exe -$(MAKEFLAGS) -f$** DCC = $(ROOT)\bin\dcc32.exe $** BRCC = $(ROOT)\bin\brcc32.exe $** #----------------------------------------------------------------------------PROJECTS = AppProject.exe #----------------------------------------------------------------------------default: $(PROJECTS) #----------------------------------------------------------------------------AppProject.exe: AppProject.dpr $(DCC)

Toolbar
Opcja Toolbar moe by albo zaznaczona, albo nie. Okrela ona, czy w oknie menedera projektu pasek narzdziowy ma by widoczny.

StatusBar
Podobnie jak w przypadku opcji Toolbar, pozycja ta moe by zaznaczona lub nie. Jeeli opcja jest zaznaczona, to w oknie menedera wywietlony zostanie pasek statusu.

Stay on Top
Jeeli opcja jest zaznaczona, to okno Project Manager bdzie wywietlone zawsze nad innymi, pozostaymi oknami.

Dockable
Pozycja Dockable umoliwia dokowanie okna Project Manager w innych oknach np. w Inspektorze obiektw.

Menu Projektu Po klikniciu prawym przyciskiem myszy pozycji okrelajcej projekt wywietlone zostanie menu rne od tego, ktre przedstawiem powyej. Zawiera ono opcje zwizane z danym projektem.

183 | S t r o n a

Add
Moesz skorzysta z opcji Add w celu dodania do projektu nowego pliku, zapisanego ju na dysku. Moe to by np. plik *.pas lub *.rc.

Remove File
Wybranie opcji spowoduje usunicie danego pliku z projektu. Dany plik nie jest fizycznie kasowany z dysku, a jedynie nie bdzie kompilowany razem z programem.

Save
Opcja powoduje zapisanie wszystkich plikw z konkretnego projektu. Dziaa tak samo jak wybranie polecenia Save All z menu File.

Options
Wywietlenie ustawie dotyczcych projektu moliwe jest take po wybraniu tego polecenia. Opcje projektu byy omawiane w poprzednim podpunkcie Projekty.

Activate
Opcja Activate powoduje uaktywnienie zaznaczonego projektu. Dziaa tak samo jak przycisk Activate na pasku narzdziowym.

Compile
Po wybraniu opcji Compile zostan skompilowane wszystkie pliki z danego projektu, ktre ulegy zmianie od czasu ostatniej kompilacji. Opcja dziaa tak samo jak polecenie Compile z menu Project.

Build
Opcja Build dziaa tak samo jak polecenie Build z menu Project, czyli powoduje zbudowanie (ponowne skompilowanie) caego projektu niezalenie od tego, czy nastpiy w nim zmiany od czasu ostatniej kompilacji.

View Source
Opcja ta powoduje wywietlenie w Edytorze kodu zawartoci pliku *.dpr; dziaa tak samo, jak polecenie View Source z menu Project.

184 | S t r o n a

Close
Opcja powoduje zamknicie zaznaczonego projektu. Jeli bdzie to konieczne, przed zamkniciem zostaniesz zapytany, czy chcesz zapisa zmiany w projekcie.

Remove Project
Usunicie caego projektu z menedera projektw jest moliwe dziki poleceniu Remove Project.

Build Sooner, Build Later


Te dwie opcje powoduj zamian miejsc w oknie menedera projektw. Po wybraniu polecenia Build Sooner zaznaczony projekt jest przesuwany o jedn pozycj wyej na licie. Dziki temu mamy moliwo ustalenia kolejnoci, w jakiej dane projekty bd kompilowane.

Compile All from Here, Build All from Here


Te dwie opcje s jedn z nowoci w Delphi 7. Umoliwiaj one kolejno kompilacj oraz ponowne zbudowanie projektw, poczwszy od miejsca zaznaczenia. Jeeli mamy w menederze wiele projektw, po wybraniu tych opcji zbudowany zostanie zaznaczony projekt i inne projekty, znajdujce si poniej. Nie naley zapomina o dwch opcjach znajdujcych si w menu Project: Compile All oraz Build All. Powoduj one kompilacj oraz zbudowanie wszystkich projektw otwartych w oknie menedera.

Kilka wersji jzykowych projektu


Tworzenie kilku wersji jzykowych projektu stao si bardzo proste dziki przydatnemu narzdziu firmy Borland Translation Manager. Dane odpowiadajce za wywietlenie programu w innej wersji jzykowej s przechowywane w jednym pliku, wic w razie potrzeby wystarczy w katalogu umieci taki plik, aby cay program zacz dziaa w innej wersji jzykowej.

Tworzymy angielsk wersj jzykow


Utwrz jaki zwyky, prosty projekt i umie w nim jeden przycisk oraz jedn etykiet (TLabel). Zmie waciwo Caption obydwu komponentw na: Przycisk 1 oraz Komponent TLabel. Projekt zapisz pod jakkolwiek nazw. Pierwszym krokiem w tworzeniu drugiej wersji jzykowej jest uruchomienie specjalnego kreatora. Z menu File wybierz Other otwarte zostanie okno Repozytorium. Wybierz pozycj Resource DLL Wizard, ktra spowoduje otwarcie kreatora (rysunek 4.36). 185 | S t r o n a

Rysunek 4.36. Kreator zasobw Wczeniej moesz zosta zapytany o to, czy chcesz zapisa dany projekt (jeeli jeszcze tego nie zrobie). Pierwszym krokiem jest kliknicie przycisku Next; w kolejnej zakadce bdziesz musia okreli pliki, ktre maj zosta przetumaczone. Domyln pozycj jest katalog z naszym programem, wic nie bdziesz musia tu nic zmienia. Nacinij ponownie Next. Kolejne okno zawiera spis jzykw. Tutaj bdziemy musieli okreli, na jaki jzyk chcemy przetumaczy aplikacj. Zaznacz pozycj Angielski (Stany Zjednoczone) i nacinij przycisk Next. W dalszych zakadkach naciskaj ju tylko klawisze Next nie bdziemy wprowadzali tutaj adnych zmian. Ostatnia zakadka bdzie zawieraa przycisk Finish po jego naciniciu zostaniesz zapytany, czy chcesz przekompilowa projekt. Odpowiedz twierdzco. Delphi nastpnie przekompiluje projekt i wywietli podsumowanie (rysunek 4.37).

Rysunek 4.37. Podsumowanie procesu kompilacji projektu Oprcz wywietlenia podsumowania w Edytorze kodu wywietlona zostanie zawarto nowego pliku TransApp.dpr (nazwa projektu nie ma znaczenia), taka jak na listingu 4.2. 186 | S t r o n a

Po naciniciu przycisku OK Delphi zaproponuje Ci zapisanie pliku *.bpg, czyli menedera projektu. Zapisz go pod dowoln nazw. Listing 4.2. Zawarto nowego pliku DPR // Do not edit. This file is machine generated by the Resource DLL Wizard. library TransApp; {ITE} {LCID:00000415:00000409} {} {ITE} {DFMFileType} {MainFrm.dfm} {ITE} {RCFileType} {TransApp_DRC.rc} {$R 'MainFrm.dfm' MainForm:TForm} {$R 'TransApp_DRC.res' 'TransApp_DRC.rc'} {$E enu} begin end.

Zawarto tego pliku nie jest przeznaczona do edycji, take nie bdziesz musia dokonywa tutaj jakichkolwiek zmian. Spjrz teraz na katalog, w ktrym umiecie projekt. Moesz zauway, e utworzony zosta nowy folder enu. Folder ten zawiera kopi plikw *.dfm oraz *.dpr. Ich tumaczeniem zajmiemy si za chwil.

Tumaczenie projektu
Jeeli podczas zakoczenia pracy z kreatorem zasobw zapisae plik *.bpg, zosta otwarty program Translation Manager, ktrym bdziemy posugiwa si podczas procesu tumaczenia zasobw. Program dzieli si na trzy zakadki; nas bdzie interesowa ostatnia z nich Workspace Program Translation Manager mona uruchomi take poprzez menu View/Translation Manager. Zakadka Workspace podzielona jest na dwie czci. Po lewej stronie znajduj si gazie z list formularzy projektu oraz tzw. resource scripts. S to wbudowane w plik EXE komunikaty o bdach, nazwy miesicy itp. Tumaczenie caoci jednak moe troch potrwa, dlatego ja podejm si przetumaczenia jedynie nazw komponentw. Interesuje nas ga Forms/MainForm. W prawym oknie mamy podgld waciwoci formularza. W kolumnie Angielski naley wpisa angielskie tumaczenie okrelonej waciwoci. Uwaga! Tekst musi by wpisany w apostrofy! Po skoczeniu pracy naley zapisa projekt (Ctrl+S).

187 | S t r o n a

Kompilacja projektu
Po zakoczeniu procesu tumaczenia moesz zamkn program Translation Manager. Z menu Project wybierz pozycj Build All, co spowoduje zbudowanie obydwu projektw (projektu waciwego oraz przetumaczonych zasobw). Po tej operacji w katalogu z programem pojawi si nowy plik z rozszerzeniem *.enu TransApp.enu. Zawiera on skompilowany i przetumaczony formularz. Od tej pory po uruchomieniu aplikacji w jej katalogu bdzie wyszukiwany plik *.enu; jeeli zostanie znaleziony, zostanie wczona angielska wersja jzykowa (rysunek 4.39).

Rysunek 4.39. Angielska wersja programu Jeeli chcesz ponownie przywrci polsk wersj jzykow, musisz zmieni nazw pliku *.enu, usun go albo przenie do innego katalogu. Kod rdowy prezentowanego tu programu znajduje si na pycie CD-ROM w katalogu ../listingi/4/TransApp.

Kilka formularzy w jednym projekcie


Z czasem, gdy Twoje umiejtnoci programowania wzrosn, zaczniesz tworzy skomplikowane programy zawierajce po kilka (kilkanacie?) formularzy. Najpierw jednak musisz nauczy si tworzy owe formularze. Nie jest to takie trudne, gdy wiele czynnoci Delphi wykona za Ciebie (jak np. dodanie deklaracji do listy uses). Stworzenie nowego formularza opiera si jedynie na wybraniu z menu pozycji File/New/Form, co spowoduje utworzenie nowej zakadki w Edytorze kodu oraz nowego formularza. Jednak utworzenie formularza i jego wywietlenie to ju dwie zupenie rne sprawy.

Wywietlenie drugiego formularza


Zapisz cay projekt, w tym dwa formularze. Jeden z nich (gwny) nazwij MainFrm.pas, a drugi AboutFrm.pas.

188 | S t r o n a

Na formularzu gwnym umie przycisk i wygeneruj jego zdarzenie OnClick. procedure TMainForm.btnShowMeClick(Sender: TObject); begin { AboutForm to nazwa drugiego formularza, znajdujcego si w pliku AboutFrm.pas } AboutForm.ShowModal; end;

Pamitasz, jak w poprzednim rozdziale podczas omawiania metod klasy TApplication wspomniaem o CreateForm? Teraz masz okazj zapozna si z przykadem dziaania owej funkcji. Pierwszym parametrem jest nazwa klasy, ktra zostanie utworzona, a drugim zmienna, ktra reprezentowa bdzie formularz. Zmiennej nie musimy nigdzie deklarowa, gdy w pliku AboutFrm.pas jest ona zadeklarowana automatycznie. Druga funkcja ShowModal powoduje wywietlenie formularza w sposb modalny. Istniej dwie funkcje suce do wywietlania formularza Show oraz ShowModal. Funkcja ShowModal powoduje, i drugi formularz praktycznie blokuje dziaanie formularza gwnego. Dopiero po zamkniciu drugiego formularza (AboutForm) moliwe jest korzystanie z pierwszego. Funkcja Show dziaa w inny sposb i umoliwia korzystanie z dwch formularzy naraz. Delphi jest na tyle sprytny, e podczas uruchamiania programu upomni si o to, e nie dodalimy deklaracji odpowiedniego moduu do listy uses (rysunek 4.40).

Rysunek 4.40. Informacja o tym, e modu nie jest dodany Nacinij przycisk Yes, aby doda nazw moduu do listy uses, a nastpnie uruchom program. Po naciniciu przycisku zostanie wywietlony drugi formularz. Kod rdowy powyszego programu znajduje si na pycie CD-ROM w katalogu ../4/FewForms.

Dynamiczne tworzenie formularza


Przykad zamieszczony w poprzednim podpunkcie dotyczy jedynie wywietlenia formularza. W rzeczywistoci formularz zostaje utworzony na samym starcie aplikacji, a my jedynie go wywietlamy. Jeli nie korzystamy z owego formularza, niepotrzebne staje si utworzenie formularza, a co za tym idzie, take zarezerwowanie dla pamici. 189 | S t r o n a

Z menu Project wybierz polecenie Options i kliknij zakadk Forms. Zakadka ta dotyczy formularzy, z ktrych korzystamy w programie. Po lewej stronie znajduj si formularze tworzone automatycznie na starcie programu. Zaznacz pozycj AboutForm i kliknij znaczek >. Spowoduje to przemieszczenie zaznaczonej pozycji do prawej listy. Kliknicie przycisku OK pozwoli na akceptacj zmian od tej pory po uruchomieniu programu formularz nie bdzie tworzony automatycznie. Spowodowao to zmian zawartoci pliku gwnego dpr (listing 4.3). Listing 4.3. Zawarto gwnego pliku po wprowadzeniu zmian program FewApp; uses Forms, MainFrm in 'MainFrm.pas' {MainForm}, AboutFrm in 'AboutFrm.pas' {AboutForm}; {$R *.res} begin Application.Initialize; Application.CreateForm(TMainForm, MainForm); Application.Run; end.

W kodzie programu nie ma ju instrukcji CreateForm, odpowiadajcej za utworzenie danego formularza na starcie. Co naley teraz zrobi, chcc wywietli formularz? Naley go przed wywietleniem utworzy, wywoujc jego konstruktor: procedure TMainForm.btnShowMeClick(Sender: TObject); begin AboutForm := TAboutForm.Create(Application); AboutForm.ShowModal; AboutForm.Free; end;

Nie zapominaj o tym, e TAboutFrom to nowa klasa formularza drugiego, ktra take posiada swj wasny konstruktor. W parametrze konstruktora naley poda nazw okna-rodzica w tym miejscu mona wpisa albo nazw wskanika Self, albo Application. Po wywietleniu formularza naley go zwolni! Dociekliwy Czytelnik moe zastanowi si przez chwil w momencie przygldania si powyszym instrukcjom. Przecie zwolnienie klasy (metoda Free) nastpuje zaraz po wywietleniu formularza 190 | S t r o n a

(metoda ShowModal). Jak to? Zaraz po wywietlaniu formularz zostanie zwolniony? moesz zapyta. Wykonywanie tego moduu gwnego zostanie zawieszone do momentu zakoczenia prac z drugim formularzem AboutForm. Oznacza to, e instrukcja Free zostanie wykonana dopiero po zamkniciu okna przez uytkownika.

Aplikacje MDI
Generalnie aplikacje dziel si na dwa rodzaje: SDI i MDI. W aplikacjach MDI osadzone jest wiele okien wywietlanych w tym samym czasie. Wyglda to tak: uytkownik w programie moe otworzy wiele dokumentw; kady z nich jest wywietlany w osobnym oknie. Moliwe jest przeczanie si midzy oknami, ich minimalizowanie czy zamykanie. Poprowadz Ci krok po kroku w procesie tworzenia aplikacji MDI bdzie to prosty edytor tekstu. Przy tej okazji poznasz zastosowanie paru interesujcych komponentw oraz nauczysz si tworzy wasne menu oraz paski narzdziowe.

Projektowanie interfejsu
Nasz program bdzie si skada z dwch formularzy: gwnego (centrum dowodzenia) oraz formularza sucego do wywietlania zawartoci pliku tekstowego. Efekt kocowy, jaki chcemy osign, przedstawiony zosta na rysunku 4.41.

191 | S t r o n a

Rysunek 4.41. Kocowy efekt, jaki chcemy osign, tworzc program Na gwn cz formularza skada si menu (komponent TMainMenu), pasek narzdziowy (TToolBar) oraz pasek stanu (TStatusBar), penicy funkcj estetyczn oraz sucy do wywietlania podpowiedzi.

Pasek narzdziowy Umie na formularzu komponent TToolBar powinien on zosta umieszczony na samej grze formularza. Nastpnie odszukaj w Inspektorze obiektw ga EdgeBorders i ustaw wszystkie znajdujce si tam waciwoci na True. Spowoduje to zaznaczenie obramowa komponentu ze wszystkich stron. Nastpnie bdziesz musia utworzy na pasku kilka przyciskw. Wystarczy, e klikniesz pasek prawym przyciskiem myszy spowoduje to rozwinicie menu podrcznego. Po wybraniu polecenia New Button na pasku stworzony zostanie nowy przycisk. Utwrz w ten sposb sze przyciskw, a po trzech pierwszych wstaw separator (polecenie New Separator w menu podrcznym). Dodawanie ikon Goy pasek narzdziowy bez adnych ikon nie wyglda ciekawie. Naley go czym przyozdobi. W tym celu umie na formularzu komponent TImageList, ktry suy do gromadzenia ikon. Kliknij dwukrotnie ten komponent spowoduje to wywietlenie okna przedstawionego na rysunku 4.42.

Rysunek 4.42. Dodawanie ikon Poprzez przycisk Add moesz zaadowa z dysku jak ikon lub plik *.bmp. Kiedy dodasz ju swoje ikonki, ostatecznym krokiem bdzie ustawienie pewnej waciwoci w komponencie TToolBar. Z waciwoci Images komponentu TToolBar musisz wybra zbir ikon majcych ozdabia pasek narzdziowy wybierz nazw odpowiadajc komponentowi TImageList.

192 | S t r o n a

Projektowanie menu Z menu w Delphi zwizane s dwa komponenty: TMainMenu oraz TPopupMenu. Pierwszy z nich suy do tworzenia menu znajdujcego si u gry okna. Drugi komponent TPopupMenu umoliwia stworzenie menu podrcznego wywoywanego po klikniciu prawym przyciskiem myszy. W naszej aplikacji skorzystamy z komponentu TMainMenu. Jego edycja odbywa si po dwukrotnym klikniciu w jego obszarze. Pierwszym krokiem bdzie stworzenie gwnej pozycji Plik. Wpisz wic w tym momencie we waciwoci Caption sowo Plik; spowoduje to utworzenie pozycji menu. Na rysunku 4.43 przedstawione zostao menu, jakie uyte bdzie w naszym programie.

Rysunek 4.43. Menu uyte w programie Kada pozycja zawiera jedno puste pole z szarym obramowaniem. Po zaznaczeniu tego pola moemy stworzy now pozycj wystarczy wpisa now warto w polu Caption i nacisn klawisz Enter. W celu utworzenia poziomej linii naley w polu Caption wpisa znak . Caa operacja jest o tyle wygodna, e podczas projektowania aplikacji dysponujemy podgldem tworzonego menu. Jak widzisz, na rysunku 4.43 znajduje si menu Okno, zawierajce polecenie Otwarte okna, ktre posiada puste podmenu. W naszym programie w tym miejscu znajdzie si lista wszystkich otwartych w programie okien. Podmenu moesz stworzy, klikajc konkretn pozycj (prawym przyciskiem) i wybierajc z menu polecenie Create submenu. Przyozdabianie menu Ten sam komponent ImageList, ktry uyty zosta do stworzenia ikon przyciskw paska narzdziowego, moe posuy do ozdobienia naszego menu gwnego. Wystarczy, e po zaznaczeniu obiektu TMainMenu wybierzesz z waciwoci Images pozycj imlToolMain (w moim przypadku komponent TImagList nosi akurat tak nazw). Kada pozycja w menu posiada ponadto waciwo ImageIndex, ktra pozwala na wybr konkretnej ikony dla danej pozycji menu (rysunek 4.44).

193 | S t r o n a

Rysunek 4.44. Lista moliwych do przypisania ikon Przypisz niektrym pozycjom ikony z ImageList. Pozostae komponenty Pozostae komponenty, z jakich skorzystamy, to OpenDialog oraz SaveDialog. Oba z nich powoduj wywietlanie standardowych okien dialogowych Windows Otwrz plik i Zapisz plik. Naley tylko ustawi we waciwociach tych komponentw odpowiedni filtr. Filtr powoduje wywietlenie w oknie Otwrz lub Zapisz plikw z odpowiednim rozszerzeniem. Pozwala to uytkownikowi zawzi obszar poszukiwa do konkretnych plikw. W tym celu posuymy si waciwoci Filter. Po jej zaznaczeniu obok pojawi si przycisk ze znakiem wielokropka. Po jego naciniciu wywietlone zostanie okno edycji filtrw (rysunek 4.45).

Rysunek 4.45. Okno edycji filtrw W kolumnie po lewej stronie znajduje si opis filtru (to, co uytkownik bdzie widzia na licie rozwijalnej), a po prawej rozszerzenie plikw wywietlanych po wybraniu danego filtra. Jeeli chcesz, moesz w komponencie SaveDialog zmieni take waciwo DefaultExt. Dziki temu w przypadku, gdy uytkownik nie wpisze rozszerzenia, zostanie ono do pliku automatycznie doczone. To rozszerzenie naley wanie wpisa we waciwoci DefaultExt.

194 | S t r o n a

Kod rdowy przykadowej aplikacji


Kod rdowy na pocztku moe wyda Ci si skomplikowany, lecz gdy ju lepiej poznasz Delphi, okae si bardzo prosty. A moe w przyszoci rozbudujesz edytor, ktry tu prezentuj? Wiele instrukcji tu podawanych poczone jest z waciwoci ActiveMDIChild, ktra okrela aktywny formularz w aplikacji MDI. Np. metoda kopiujca tekst w naszym programie bdzie wyglda tak: procedure TMainForm.btnCopyClick(Sender: TObject); begin { kopiuj } (ActiveMDIChild as TMDIForm).reDoc.CopyToClipboard; end;

W powyszym fragmencie zawarte jest znane Ci ju rzutowanie. Waciwo ActiveMDIChild rzutujemy na formularz potomny TMDIForm. Dziki takiemu zapisowi moemy odwoa si do metody CopyToClipboard komponentu TRichEdit (w tym kodzie wystpujcemu pod nazw reDoc).

Czy warto?
Zapoznae si ju nieco z Delphi i pewnie masz wyrobion opini na jego temat. By moe jeste zachwycony tym rodowiskiem, ale moe zdye si ju zniechci, bo uwaasz, e nauczenie si go jest bardzo trudne. Niezalenie od Twego zdania zapewne zastanawiasz si, czy warto uczy si tego jzyka. Powiem tak: programowanie wcale nie jest proste (ale nie zamierzam tutaj nikogo odstrasza!), trzeba je jednak po prostu lubi! Object Pascal naley do jzykw programowania najprostszych do nauczenia si, a jednoczenie zapewniajcych du funkcjonalno. Wiele osb sdzi, e nauka Delphi nie ma sensu, gdy w wiecie programistw liczy si jedynie C++. Nie mam zamiaru uczestniczy w witej wojnie zwolennikw Delphi i C++. Powiem tylko, e Object Pascal (Delphi) w niewielkim stopniu ustpuje C++, a dziki VCL i wszelkim innym udogodnieniom, jakie oferuje, jest rodowiskiem godnym polecenia. Jeeli wic na tym etapie nie jeste w stanie zrozumie wielu rzeczy, to radz Ci, drogi Czytelniku, aby opanowa te podstawy, gdy jest to niezbdne do dalszej nauki. Sprbuj przeprowadzi samodzielne wiczenia, jeszcze raz dokadnie przeczyta poprzednie rozdziay, wyszuka dodatkowe informacje w Internecie czy w pomocy Delphi. Jeeli mimo wszystko nadal masz jakie wtpliwoci, nie przejmuj si nimi! Uwierz mi, e wszystko wyjani si z czasem a umiejtnoci przyjd wraz z dowiadczeniem.

195 | S t r o n a

Delphi a C++ Builder


Firma Borland jest take autorem rodowiska C++ Builder, ktre suy do tworzenia aplikacji opartych o jzyk C++. Najciekawsze jest to, e oba rodowiska s udzco do siebie podobne! Jedyn rnic jest fundament: Delphi oparte jest na Object Pascalu, a C++ Builder na jzyku C++. Co prawda tematem tej ksiki nie jest C++, ale pozwoliem sobie na mae porwnanie tych obydwu rodowisk, aby zda sobie spraw ze stopnia wtajemniczenia, jakim musi charakteryzowa si programista.

Rozmiar aplikacji wykonywalnej i czas kompilacji


Programy napisane w rodowisku C++ Builder kompiluj si duej, a rozmiar pliku wykonywalnego jest wikszy. Mnie zaraz po uruchomieniu C++ Buildera i prbie kompilacji irytowa niezwykle dugi czas, jaki potrzebny by na sprawdzenie bdw i kompilacj programu. Moe i nic w tym dziwnego jzyk C++ jest bardziej rozbudowany od Object Pascala lecz ja wci nie mog si do tego przyzwyczai.

Pliki
Delphi oraz C++ Builder generuj rne pliki dla projektw rnice dotycz przede wszystkim rozszerze, cho nie tylko. Np. plik formularza jest w obu przypadkach taki sam. W tabeli 4.6 przedstawione s rozszerzenia plikw Delphi i C++ Builder. Tabela 4.6. Rozszerzenia plikw Delphi i C++ Builder Plik Gwny plik projektu Pliki kodu rdowego Plik nagwkowy Delphi *.dpr *.pas brak C++ Builder *.bgr *.cpp *.h lub *.hpp *.obj *.dfm

Skompilowany plik rdowy *.dcu Plik formularza Pliki zasobw Skompilowany pakiet Plik rdowy pakietu *.dfm

*.res lub *.dcr *.res *.bpl *.dpk *.bpl *.bpk

Poczwszy od Delphi 4, kompilator jest w stanie kompilowa pliki *.dcu jako *.obj. Daje to moliwo wykorzystania moduw Delphi w aplikacjach C++ Builder. 196 | S t r o n a

Skadnia
Tego nie da si ukry skadnia C++ jest znacznie trudniejsza ni skadnia Object Pascala i to wanie dlatego ten drugi jzyk jest zalecany dla pocztkujcych programistw. Listing 4.4. Programu napisany w C++ Builder zawarto gwnego pliku *.cpp, //-------------------------------------------------------------------------#include <vcl.h> #pragma hdrstop //-------------------------------------------------------------------------WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int) { try { Application->Initialize(); Application->Run(); } catch (Exception &exception) { Application->ShowException(&exception); } catch (...) { try { throw Exception(""); } catch (Exception &exception) { Application->ShowException(&exception); } } return 0; } //--------------------------------------------------------------------------

Aby zrozumie powyszy zapis, naley zna zasady skadni samego jzyka C++, co nie jest tematem tego podrcznika. Osobie znajcej jzyk C++ ten listing moe wyda si prosty i przejrzysty, ale jeeli pierwszym poznanym przez Ciebie jzykiem programowania by Turbo Pascal, kod C++ moe wyda Ci si odstraszajcy. 197 | S t r o n a

Cechy wsplne
Delphi i C++ Builder posiadaj bardzo wan cech wspln VCL. Jeeli poznasz cho troch t wizualn bibliotek w Delphi, nie bdziesz mia problemw z wykorzystaniem jej w C++ w obu tych rodowiskach jest ona jednakowa. Bdziesz tylko musia pozna podstawow skadni jzyka C++, znacznie rnic si od Object Pascala. Jeeli natomiast znasz troch PHP lub Jav, bdzie to dla Ciebie dobra wiadomo: oba te jzyki posiadaj skadni bardzo podobn do C++. Druga sprawa to interfejs. Delphi i C++ Builder maj praktycznie identyczny interfejs, takie same narzdzia itp.

Rnice atwiej jest jednak mwi o rnicach ni o cechach wsplnych obydwu tych rodowisk. Praktycznie rzecz ujmujc, jest mao identycznych elementw skadni. Najwaniejsze rnice przedstawione zostay w tabeli 4.7. Tabela 4.7. Wybrane elementy rodowisk Delphi i C++ Builder Opis Pocztek bloku Koniec bloku Operator przypisania Operator porwnania Operator nierwnoci Operator odwoania acuch tekstowy Delphi C++ begin { end := = <> . ' } = == != > " /* */ switch this NULL

Komentarz wielowierszowy { } Instrukcja case Sowo kluczowe Self Sowo kluczowe nil case Self nil

198 | S t r o n a

Rozwamy przykad ptli. Ich nazwy s podobne, lecz konstrukcja troch inna. Np. ptla for napisana w C++ Builder wyglda tak: for (i=0; i<100; i++) { }

Jak widzisz, budowa owej ptli jest cakowicie inna ni w Delphi. W jzyku C++ brakuje rwnie sowa kluczowego then.

Co nowego w Delphi 7?
Kada kolejna wersja Delphi obfituje w nowe narzdzia i komponenty, ktre maj jeszcze bardziej uatwi i przyspieszy projektowania aplikacji. W najnowszej wersji Delphi projektanci skupili si wyranie na unowoczenieniu istniejcych komponentw i dodaniu nowych, sucych do projektowania aplikacji sieciowych. Oto najwaniejsze zmiany, jakie mona napotka w nowym rodowisku Delphi 7.

Zmiany w IDE
Elementem widocznym od razu po uruchomieniu Delphi jest cakowicie zmieniony wygld menu gwnego (rysunek 4.46), wzorowanego obecnie na stylu Windows XP.

Rysunek 4.46. Nowe menu w Delphi 7

199 | S t r o n a

Menu Component list Jeeli wemiemy pod uwag nowe elementy w menu, nie dostrzeemy zbyt wielu zmian. Jedn z najbardziej istotnych innowacji jest pojawienie si nowego menu View/Component List. Wybranie tego polecenia powoduje wywietlenie okna z list wszystkich moliwych do dodania komponentw (rysunek 4.47).

Rysunek 4.47. Nowe okno Components W oknie Components istnieje moliwo zaznaczenia kilku komponentw, ktre nastpnie zostan umieszczoen na formularzu. Zaznacza komponenty naley, przytrzymujc klawisz Shift, a nastpnie trzeba klikn przycisk Add to form. Okno Message Hints Ciekaw opcj wprowadzon w Delphi 7 jest okno Message Hints, wywoywane za porednictwem menu View/Additional Message Info (rysunek 4.48).

Rysunek 4.48. Okno Message Hints Okno to suy do wywietlania dodatkowych wskazwek i podpowiedzi dotyczcych komunikatw kompilatora. Tre takich wskazwek moe by na bieco aktualizowana poprzez cignicie odpowiedniego pliku z serwera firmy Borland.

200 | S t r o n a

Po naciniciu pierwszego przycisku na pasku narzdziowym masz moliwo edycji komunikatu w przypadku pojawienia si jakiego bdu.

Pozostae zmiany Oprcz zmian wymienionych powyej s te inne troch mniej istotne; nie s na tyle wane, aby rozpisywa si na ich temat, ale warto o nich wspomnie.

Zakadka Color w oknie Tools/Editor Options zawiera teraz dwie opcje Foreground Color oraz Background Color. W oknie Message View znajduje si kilka zakadek sucych do wywietlania rnych typw zakadek. Kombinacje klawiszy Alt+Page Down oraz Alt+Page Up powoduj przemieszczanie si pomidzy zakadkami tych okien, ktre je posiadaj np. Message View czy Edytor kodu. Nowa zakadka Compiler Message w oknie Project/Options/Compiler Message. Pozwala ona na okrelenie, ktre komunikaty maj by wywietlane przez kompilator.

Nowe komponenty
Borland wprowadzi take par zmian dotyczcych komponentw. Usprawni nam to troch prac i sprawi, e nasze aplikacje bd wyglday adniej.

Elementy wizualne w stylu Windows XP Chyba jedn z waniejszych zmian dotyczcych komponentw jest moliwo tworzenia aplikacji, ktre wizualnie bd dostosowane do systemu Windows XP. Na palecie Additional znajduj si komponenty, dziki ktrym moesz utworzy menu i paski narzdziowe takie, jak w Windows XP. Przykad ilustrujcy wykorzystanie nowych komponentw w Delphi 7 moesz znale na pycie CDROM w katalogu ../listingi/4/WinXP.

Manifest XP
Na palecie Win32 znajduje si komponent TXPManifest. Na pierwszy rzut oka nie wydaje si, aby wykonywa on jakie specjalne czynnoci tak te jest. Za to dziki niemu biblioteka VCL moe wykorzystywa kontrolki Windows XP, czyli bibliotek Windows Common Controls 6. Aby wszystkie kontrolki byy w stylu XP, w katalogu z programem musi si znale plik manifest zawierajcy instrukcje XML. Umie na formularzu komponent TXPManifest (jego nazw moesz zmieni na XPManifest). Skompiluj program. Od tej pory w zasobach aplikacji wykonywalnej powinny znale si instrukcje:

201 | S t r o n a

<?xml version="1.0" encoding="UTF8" standalone="yes"?> <assembly xmlns="urn:schemasmicrosoftcom:asm.v1" manifestVersion="1.0"> <assemblyIdentity type="win32" name="DelphiApplication" version="1.0.0.0" processorArchitecture="*"/> <dependency> <dependentAssembly> <assemblyIdentity type="win32" name="Microsoft.Windows.CommonControls" version="6.0.0.0" publicKeyToken="6595b64144ccf1df" language="*" processorArchitecture="*"/> </dependentAssembly> </dependency> </assembly>

Kod rdowy programu wykorzystujcego komponent TXPManifest znajduje si w katalogu ../4/Manifest.

Komponenty Indy W nowej wersji Delphi znalaza si kolejna ju wersja popularnego pakietu Indy, sucego do tworzenia aplikacji sieciowych. Autorem pakietu Indy nie jest firma Borland, ale jego dua popularno sprawia, e zosta doczony do rodowiska Delphi. Wykorzystaniem komponentw Indy zajmiemy si w rozdziale 11. Delphi 7 zawiera komponenty Indy w wersji 9.0. Ze strony www.nevrona.com/Indy/ moesz pobra najnowsz wersj tego pakietu. Pakiet Indy nie jest jedynym pakietem tego typu. Bardzo popularny i rwnie dobry (jeeli nie lepszy), cho troch trudniejszy, jest pakiet ICS. Komponenty ICS zostay napisane przez belgijskiego programist i s dostpne na stronie www.overbyte.be. Pozostae komponenty

Na stronie Dialogs palety komponentw znajduje si nowy komponent TPageSsetupDialog. Dziki temu obiektowi moemy wywietli standardowe okienko Windows Ustawienia strony. Zakadka dbExpress zawiera komponent TSimpleDataSet, ktry zastpuje TSQLClientDataSet. Technologi dbExpress zajmiemy si szczegowo w rozdziale 5. 202 | S t r o n a

Komponenty zmodyfikowane

Nowe waciwoci klasy TCustomForm ScreenSnap oraz SnapBuffer. Kontroluj one, czy formularz dochodzi do skraju ekranu podczas jego przesuwania. Nowa waciwo klasy TCustomComboBoxEx AutoCompleteOptions pozwala reagowa na naciskanie klawiszy przez uytkownika. Komponenty CXL TOpenDialog i TSaveDialog zostay zmodyfikowane tak, aby obsugiway moliwo podgldu pliku.

Bazy danych
Borland wprowadzi take udogodnienia dotyczce baz danych. Przede wszystkim zaktualizowane zostay sterowniki dbExpress do baz Informix SE, Oracle 9i, DB2 7.2, Interbase 6.5., MySQL 3.23.49. Wprowadzony zosta take nowy sterownik MS SQL Server 2000. Wicej o dbExpress poczyta moesz w rozdziale 5.

Pozostae zmiany:

W aplikacjach DataSnap zastosowanie IAppServer zostao zastpione przez IAppServerSOAP. DataSnap nie bdzie ju obsugiwa pocze zgodnych ze standardem COBRA.

.NET
Pewnie nieraz syszae ju o platformie .NET. Jest to strategia firmy Microsoft, polegajca oglnie mwic na stworzeniu pewnego standardu tworzenia oprogramowania. Microsoft dy do zaprojektowania wielu usug, ktre byyby ze sob zintegrowane, co oznaczaoby uproszczenie wielu z nich. .NET to caa seria produktw, ktre bd ze sob zintegrowane, a ich kluczowym elementem bdzie wykorzystanie Internetu. Nazwa .NET zastpuje wczeniejsz robocz nazw Next Generation Windows Services. Microsoft planuje stworzenie caej rodziny produktw .NET systemu operacyjnego Windows .NET oraz systemu oprogramowania Visual Studio. Borland postanowi przyczy si do tej strategii i przystosowa swoje produkty do .NET. Najwaniejsz chyba zmian w Delphi 7 jest przystosowanie kompilatora do platformy .NET.

203 | S t r o n a

Modyfikacje kompilatora
Poczwszy od wersji 7, kompilator Delphi zawiera trzy nowe ostrzeenia zgodne z kompilatorem .NET, zwane unsafe. Ostrzeenia te pojawiaj si w czasie, gdy kompilator nie moe przewidzie skutkw dziaania pewnych instrukcji. Domylnie ostrzeenia unsafe s wyczone mona je wczy w opcjach projektu (Project/Options). Poniej przedstawiam instrukcje, przy ktrych kompilator moe wywietli ostrzenia unsafe. Niebezpieczne typy:
PChar, PWideChar, PAnsiChar. Wskaniki. Zmienne file of. Real48. Zmienne Variant.

Niebezpieczne funkcje:
Addr, Hi, Lo, Swap. BlockRead, BlockWrite. GetMem, FreeMem.

Operator @.

Oprcz tego ostrzeenia typu unsafe mog by spowodowane rzutowaniem na rekordy lub inne typy.

Podsumowanie
To by do dugi i by moe czasami nucy rozdzia, powicony IDE Delphi, kwestii, ktra zapewne nie interesuje w tak duym stopniu pocztkujcego programisty. Sam pamitam, e gdy uczyem si Object Pascala, poznawanie IDE nie byo dla mnie zbyt interesujce pochonity nauk Object Pascala pragnem jak najszybciej nauczy si jak najwicej interesujcych rzeczy dotyczcych tego jzyka. Ty take by moe czytasz ten rozdzia ju po zapoznaniu si z nastpnymi czciami tej ksiki. Jeeli wic kiedy zapomnisz, gdzie mona znale opcje suce do zmiany kolorowania skadni powrcisz do tego rozdziau

204 | S t r o n a

Podsumowanie czci I
Wydaje mi si, e cz I niniejszej ksiki bya najtrudniejszym etapem nauki Delphi ? w kocu pocztki zawsze s najtrudniejsze, nieprawda? Kolejne czci bd z pewnoci atwiejsze, gdy bdziesz zna ju zasady, jakimi rzdzi si Object Pascal oraz cae Delphi. W pierwszym rozdziale opisywaem absolutne podstawy Delphi ? wszystko to, co jest konieczne, aby rozpocz dalsz nauk programowania w tym rodowisku. Poznae mniej wicej wygld Delphi, podstawowe funkcje menu i zasady obowizujce w jzyku Object Pascal. Poprzedni rozdzia by powicony przede wszystkim skadni jzyka Object Pascal ? czyli podstawowej kwestii potrzebnej do napisania wasnego programu. Ten rozdzia mg by troch trudny, jeeli nie miae wczeniej stycznoci z adnym jzykiem programowania. Jeeli wszystko zrozumiae, skadam Ci serdeczne gratulacje! Powiem Ci szczerze, e nie wszyscy pocztkujcy programici potrafi poj wszystko od razu ? nie jest atwo przyswoi sobie tak du parti materiau. Trzeci rozdzia by powicony wycznie tym sprawom, ktre dotycz Delphi, czyli bibliotece VCL, klasom i programowaniu obiektowemu. Ptle czy instrukcje warunkowe obecne s praktycznie w kadym jzyku programowania ? rny jest tylko ich zapis. Natomiast bibliotek VCL naprawd naley pozna. Bd o niej jeszcze mwi w III czci niniejszej ksiki. Pierwsz cz koczy rozdzia 4., ktry powala Ci zapozna si z IDE Delphi, najwaniejszymi jego funkcjami, oknami itp. Mam nadziej, e dotychczas nie poczue si zniechcony ? podczas dalszej lektury ksiki czeka Ci jeszcze wicej wrae.

205 | S t r o n a

Cz II
Rozpoczynamy kolejny etap nauki Delphi. Poprzednia cz stanowia jedynie wprowadzenie do tego rodowiska. Nauczye si w niej tworzy aplikacje w Delphi. Cz II nie bdzie powicona jednemu tematowi, lecz kilku zagadnieniem dotyczcym bardziej zaawansowanych technik programowania. Rozdzia 5. bdzie powicony obsudze komunikatw w systemie Windows. Nauczysz si wysya i odbiera wasne komunikaty. W rozdziale 6. zaprojektujemy aplikacje umoliwiajce komunikowanie si z rejestrem Windows oraz plikami INI, w ktrych mona przechowywa rne informacje, mogce by wykorzystane przez programy. Mam nadziej, e zaciekawi Ci rozdzia 7., w ktrym nauczysz si obsugiwa pliki, kopiowa je, kasowa i przechowywa w nich informacje. Czym jest wtek? Tego dowiesz si w rozdziale 8., w ktrym zaprojektujemy wielowtkow wyszukiwark. W rozdziale 9. czekaj Ci ciekawe dowiadczenia ? tworzenie aplikacji multimedialnych, wykorzystywanie grafiki i dwiku oraz odtwarzanie filmw. W kolejnym, 10. rozdziale przedstawi temat tworzenia oraz wykorzystania bibliotek DLL. Za ich pomoc moemy podzieli nasz aplikacj na wiele czci. Interesujcy jest take rozdzia 11., w ktrym nauczysz si tworzy aplikacje sieciowe wykorzystujce Internet. Dowiesz si, w jaki sposb wysya poczt e-mail czy obsugiwa protok HTTP. Z kolei w rozdziale 12. zostanie poruszony trudniejszy aspekt programowania w systemie Windows: tworzenie maych, szybko dziaajcych aplikacji, wykorzystujcych mechanizmy WinAPI. Ostatni, 13. rozdzia tej ksiki bdzie dotyczy kwestii tworzenia programw opartych o obiekty COM i kontrolki ActiveX. Brzmi troch gronie? Nie martw si. Czytajc t ksik, krok po kroku zrozumiesz, na czym to wszystko polega.

206 | S t r o n a

Rozdzia 5
Komunikaty, bo to one bd tematem niniejszego rozdziau, nie s technologi zbyt czsto uywan przez programistw Delphi, a to ze wzgldu na moliwo zastpienia ich przez zdarzenia. Jest to jednak troch bardziej zaawansowany element programowania, z ktrego nieraz moesz skorzysta w swoich aplikacjach.

Czym s komunikaty?
Sowo komunikat jest czsto stosowane w jzyku uytkownikw komputerw. Tematem tego rozdziau nie bd jednak ? jak pocztkowo mogoby si wydawa ? komunikaty w postaci okienek systemu Windows. Wszystko to, co dzieje si w systemie (mwimy tu na razie o systemie Windows) jest wynikiem komunikatw ? czy to nacinicie klawisza, czy ruch myszki lub otwarcie jakiego programu. Jak zapewne zauwaye, w Delphi odzwierciedleniem komunikatw s zdarzenia ? moemy na przykad wykorzysta w naszym programie zdarzenie OnClose, ktre jest wynikiem zamknicia okna. W rzeczywistoci jest to komunikat WM_CLOSE. Nie wszystko da si zrealizowa za pomoc zdarze Delphi ? dlatego wanie nieraz bdziesz zmuszony skorzysta z komunikatw. Oto kolejny przykad: uytkownik klika lewym klawiszem myszy w obszarze okna aplikacji. W tym momencie system wysya do tego okna komunikat WM_LBUTTONDOWN, w ktrym zawarte s pewne informacje, takie jak np. wsprzdne myszki. Zadaniem aplikacji jest odpowiednie zareagowanie na ten komunikat. Jeeli tylko bdziesz mia okazj, stosuj zdarzenia zamiast korzysta z komunikatw. Jest to pewniejszy oraz szybszy sposb reagowania na okrelone sytuacje.

Rodzaje komunikatw
W rzeczywistoci komunikaty s jedynie zwykymi staymi o okrelonych wartociach, ktre s jakby identyfikatorem danego komunikatu. Komunikaty moliwe do wykorzystania w Delphi zdefiniowane s w module Message.pas.

Komunikaty okienkowe
Podstawowym typem komunikatw s tzw. komunikaty okienkowe, ktre posiadaj przedrostek WM_ (ang. Windows Message). Ten typ komunikatw uywany jest do komunikacji pomidzy oknami ? informuj one np. o naciniciu klawisza lub o przesuniciu kursora myszy w obszarze danego okna. Najpopularniejsze (najczciej stosowane) komunikaty przedstawione s w tabeli 5.1.

207 | S t r o n a

Nazwa WM_CREATE WM_DESTROY WM_MOVE

Opis Komunikat jest wysyany w momencie tworzenia okna Komunikat jest wysyany w momencie zamykania okna Komunikat stanowi informacj dla aplikacji, e okno jest wanie przemieszczane Komunikat stanowi informacj dla aplikacji, e zmieniany jest wanie rozmiar okna Nastpia aktywacja okna Komunikat jest wysyany w momencie, gdy okno powinno zosta zamknite Program zostaje zakoczony Nacinicie klawisza. Odpowiednik zdarzenia OnKeyPress Wcinicie klawisza. Odpowiednik zdarzenia OnKeyDown Puszczenie klawisza. Odpowiednik zdarzenia OnKeyUp Nastpio przesunicie kursora myszy

WM_SIZE WM_ACTIVATE WM_CLOSE WM_QUIT WM_CHAR WM_KEYDOWN WM_KEYUP WM_MOUSEMOVE

WM_LBUTTONDOWN Lewy przycisk myszy zosta nacinity WM_LBUTTONUP Lewy przycisk myszy zosta puszczony

Naturalnie jest to tylko cz z moliwych do zastosowania komunikatw. W rzeczywistoci s ich setki! Wystarczy zajrze do moduu Message.pas. Nie musisz pamita ich wszystkich ? wystarczy, e w razie potrzeby zajrzysz do systemu pomocy Delphi lub do tego rozdziau.

Komunikaty powiadamiajce
Kada kontrolka Windows posiada wasne typy komunikatw, ktre przesyane s z okna macierzystego (formularz Delphi) do danej kontrolki. Komunikaty takie mog zawiera informacje o tym, e nastpio przesunicie suwaka, nacinito przycisk albo np. zostaa wywietlona lista rozwijalna. Jak ju pisaem, kada kontrolka ma swj zestaw komunikatw ? z kad z tych kontrolek zwizany jest inny przedrostek.

208 | S t r o n a

Tabela 5.2. Najczciej uywane komunikaty zwizane z kontrolkami Windows Nazwa Przycisk BN_CLICKED BN_PAINT BN_DISABLE Kliknito przycisk. Obraz przycisku musi zosta odwieony Przycisk zosta zablokowany Opis

BN_DOUBLECLICKED Nastpio podwjne kliknicie w obszarze komponentu BN_SETFOCUS BN_KILLFOCUS Obiekt sta si aktywny Obiekt przestaje by aktywny Lista wyboru (ListBox) LBN_SELCHANGE Komunikat pojawia si w momencie, gdy uytkownik zamierza zmieni zawarto zaznaczonej pozycji Komunikat pojawia si w momencie, gdy uytkownik rezygnuje z zaznaczenia pozycji Kontrolka rozwijalna CBN_SELCHANGE CBN_DBLCLK CBN_EDITCHANGE CBN_DROPDOWN CBN_CLOSEUP CBN_SELENDOK Komunikat pojawia si w momencie, gdy uytkownik zaznaczy pozycj i prbuje j zmieni Nastpio podwjne kliknicie w obszarze kontrolki Wystpuje w przypadku, gdy uytkownik zmieni tre kontrolki Nastpio rozwinicie listy rozwijalnej Lista rozwijalna zostaa zamknita Komunikat wystpuje po wybraniu jakiego elementu z listy Kontrolka edycyjna EN_CHANGE EN_UPDATE Zmieniony zosta edytowany tekst Komunikat zawiera informacj o odwieeniu tekstu

LBN_SELCANCEL

209 | S t r o n a

EN_ERRSPACE EN_MAXTEXT EN_HSCROLL EN_VSCROLL

Nie mona zadeklarowa wystarczajcej iloci pamici! Tekst wykracza poza kontrolk edycyjn Kliknito poziomy pasek przewijania Kliknito pionowy pasek przewijania

W opisach z tabeli 5.2 staraem si unika powtarzania komunikatw. Dla kadej bowiem kontrolki wystpuje np. komunikat _SETFOCUS. Wystarczy tylko zmieni prefiks (czyli doda odpowiednie EN, CBN), aby komunikat by prawidowo wykryty przez system. W tabeli 5.2 zamieciem jedynie komunikaty powiadamiajce (notification messages), ktre zawieraj informacje o zdarzeniach zwizanych z kontrolkami. Istnieje wiele komunikatw sucych do bezporedniego operowania na danych (np. do zamiany wpisanego tekstu itp.). Komunikaty te s uywane podczas tworzenia programw dziaajcych jako WinAPI, ale jeeli jeste ciekawy ich dziaania, zajrzyj do projektu ChildMsg.dpr umieszczonego na pycie CD-ROM. WinAPI to skrt od sw Windows Application Programming Interface. Jest to metoda programowania polegajca na wykorzystywaniu jedynie narzdzi dostpnych w systemie ? bez korzystania z VCL. Jeeli chcesz si dowiedzie czego wicej na ten temat, zajrzyj do rozdziau 12. Czsto moesz spotka si z okreleniem Win32. Oznacza ono po prostu system Windows dziaajcy w rodowisku 32-bitowym (czyli Windows 95 i nowsze wersje).

Komunikaty definiowane przez uytkownika


Nieraz niezbdna okae si komunikacja pomidzy dwiema aplikacjami. Mona j zrealizowa w do prosty i przejrzysty sposb ? za pomoc komunikatw! Nie musimy korzysta wycznie z tych komunikatw, ktre zadeklarowane s ju w module Message.pas ? moliwe jest take deklarowanie wasnych. W gruncie rzeczy sprowadza si ono jedynie do zadeklarowania staej. Istnieje tylko jeden warunek: wartoci komunikatw nie mog si powtarza, aby ze sob nie kolidoway. Komunikatami definiowanymi przez uytkownika zajmiemy si w dalszej czci tego rozdziau.

Jak dziaaj komunikaty?


Dla nas ? programistw ? istotn spraw jest obsuga samego komunikatu. To, jak komunikat dziaa, moe wyda si ju mniej interesujce. Mimo to warto dowiedzie si czego o zasadach funkcjonowania takiego systemu. Ot pierwszym krokiem podejmowanym po wystpieniu okrelonego zdarzenia (np. kliknicia mysz) jest zakwalifikowanie rodzaju zdarzenia przez system. Nastpnie system umieszcza komunikat w tzw. kolejce komunikatw (system Windows przechowuje dla kadej aplikacji oddzieln kolejk). Kolejnym krokiem jest wykonywanie ptli, ktra przekazuje kolejno wszystkie komunikaty do konkretnego okna. Tutaj za obsuenie komunikatu odpowiedzialna jest sama aplikacja.

210 | S t r o n a

Obsuga komunikatw
Obsuga komunikatw jest o tyle trudniejsza od zdarze, e trzeba samodzielnie ?rozgryza? wartoci parametrw. W zdarzeniach wszystko podane jest jak naley ? w postaci parametrw i procedur zdarzeniowych.

Przechwytywanie komunikatw
Zadaniem Twojego pierwszego programu zwizanego z komunikatami bdzie przechwycenie komunikatu WM_LBUTTONDOWN, wystpujcego w momencie nacinicia przez uytkownika lewego przycisku myszy! W sekcji private klasy formularza dodaj nastpujcy wiersz: procedure WmLButtonDown(var Msg : TMessage); message WM_LBUTTONDOWN;

Jest to deklaracja procedury, ktra obsugiwa bdzie zdarzenie. Zasada konstruowania procedur obsugujcych komunikaty polega na dodaniu na kocu deklaracji sowa kluczowego message. Po sowie tym naley wpisa nazw komunikatu ? w naszym przypadku WM_LBUTTONDOWN. Przyjo si specyficzne nazewnictwo procedur obsugujcych komunikaty, ktre polega na nadawaniu procedurze takiej samej nazwy jak nazwa komunikatu, tyle e bez znaku podkrelenia (_). Nie przejmuj si na razie parametrem Msg powyszej procedury ? omwi go nieco pniej. Definicja procedury WmLButtonDown powinna zatem wyglda tak: procedure TMainForm.WmLButtonDown(var Msg: TMessage); begin lblInfo.Caption := 'Uytkownik wcisn lewy klawisz myszy!'; end;

Moesz ju uruchomi program. Po klikniciu lewym przyciskiem myszy w obszarze formularza wykonana zostanie procedura WmLButtonDown, a co za tym idzie, wywietlona zostanie take stosowna informacja w etykiecie (TLabel). Cay kod rdowy moduu znajduje si w listingu 5.1. Zwr uwag, e w definicji procedury nie stosujemy klauzuli message ? tak, jak ma to miejsce w deklaracji. Listing 5.1. Kod moduu przechwytujcego komunikat

211 | S t r o n a

unit MainFrm; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls; type TMainForm = class(TForm) lblInfo: TLabel; private procedure WmLButtonDown(var Msg : TMessage); message WM_LBUTTONDOWN; public { Public declarations } end; var MainForm: TMainForm; implementation {$R *.dfm} { TMainForm } procedure TMainForm.WmLButtonDown(var Msg: TMessage); begin lblInfo.Caption := 'Uytkownik wcisn lewy klawisz myszy!'; end; end.

Struktura TMsg
W poprzednim przykadzie procedura posiadaa parametr Msg, ktry by typu TMessage. Ta struktura identyfikuje wanie komunikat i jego zawarto (parametry). VCL (typ TMessage naley do bibliotek VCL) zwalnia nas w wikszej czci od programowania bardziej zaawansowanych aspektw zwizanych z komunikatami, jak pobieranie komunikatu z ptli itp. W rodowisku WinAPI obowizuje inny rekord, ktry jest pierwowzorem dla TMessage:

212 | S t r o n a

type TMsg = packed record hwnd: HWND; message: UINT; wParam: WPARAM; lParam: LPARAM; time: DWORD; pt: TPoint; end;

Rekord ten zadeklarowany jest w module Windows.pas, i zawiera informacje dotyczce samego komunikatu.
hwnd ? uchwyt okna, do ktrego komunikat jest kierowany. message ? identyfikator komunikatu. wParam ? 32-bitowa liczba, okrelajca dodatkowe parametry zwizane z komunikatem. lParam ? kolejne 32-bitowe pole, okrelajce dodatkowy parametr przekazywany wraz z

komunikatem. time ? czas wysania komunikatu. pt ? wsprzdne kursora myszy.

Przy okazji omawiania tej struktury naley si par sw objanienia. Pierwszym parametrem jest tzw. uchwyt okna. Jest to 32-bitowa liczba, okrelajc dane okno w systemie Windows. Kade okno posiada swj unikalny numer ? dziki temu moemy ?porozumiewa? si z danym programem np. wanie dziki komunikatom. Komunikat moe zawiera w sobie pewne dane, jak np. wsprzdne kursora myszy czy informacja okrelajca, jaki klawisz jest w tej chwili dodatkowo wcinity. Jednak s te komunikaty, ktre nie posiadaj adnych dodatkowych parametrw.

Struktura TMessage
W Delphi nie bdziemy zajmowali si struktur TMsg, lecz skorzystamy z dobrodziejstw VCL i rekordu TMessage, ktry zadeklarowany jest w module Message.pas. type TMessage = packed record Msg: Cardinal; case Integer of 0: ( WParam: Longint; LParam: Longint; Result: Longint); 1: ( 213 | S t r o n a

WParamLo: WParamHi: LParamLo: LParamHi: ResultLo: ResultHi: end;

Word; Word; Word; Word; Word; Word);

Na pozr struktura TMessage przedstawia si bardzo skomplikowanie, lecz nie zawiera tylu elementw, co TMsg. Mamy tu jedynie informacj przekazywan w komunikacie, zawierajc parametry lParam i wParam. Rekord TMessage zawiera dodatkowo parametr Result. Czasami system oczekuje od komunikatu uzyskania informacji zwrotnej o jego przebiegu. Wwczas mamy moliwo nadania okrelonej wartoci polu Result: Msg.Result := 1;

Pozostao jeszcze objanienie kwestii specyficznej budowy rekordu, a mianowicie dodanie instrukcji case. Dla pola LParam zadeklarowane s jeszcze dwie wartoci: LParamLo i LParamHi; tak samo ma si sprawa z pozostaymi parametrami ? WParam i Result. Aby lepiej to zrozumie, polecam wykonanie pewnego wiczenia. Podczas odbierania komunikatu WM_LBUTTONDOWN parametr lParam zawiera wsprzdne kursora myszy, ktre s jakby ?zakodowane? w postaci 32-bitowej liczby. Sam moesz si o tym przekona: procedure TMainForm.WmLButtonDown(var Msg: TMessage); begin lblInfo.Caption := 'Wsprzdne myszy: ' + IntToStr(Msg.LParam); end;

W takim wypadku podczas pobierania komunikatu w etykiecie wywietlona zostanie jaka ?kosmiczna? liczba ? np. 2490472. eby uzyska z tej liczby rzeczywiste pooenie kursora, naley skorzysta z funkcji LOWORD i HIWORD. Funkcje LOWORD i HIWORD Funkcje LOWORD i ksu do uzyskiwania tzw. wyszego oraz niszego wyrazu liczby 32-bitowej. Za pomoc tych funkcji moemy ?odkodowa? parametr, ktry przykadowo okrela wsprzdne kursora myszy: LOWORD(2490472); { da liczb 104 } HIWORD(2490472); { da liczb 38 } W powyszy sposb uzyskujemy rzeczywiste pooenie kursora myszy ? 104 (w poziomie) i 38 (w pionie). 214 | S t r o n a

Struktura TMessage zwalnia nas od takich zada dziki parametrom LParamLo i LParamHi, ktre zawieraj ju ?odkodowane? wartoci. Listing 5.2 przedstawia program z listingu 5.1 ju po dokonaniu modyfikacji, a sam program w trakcie dziaania, przedstawiony zosta na rysunku 5.1 Listing 5.2. Uzyskiwanie wsprzdnych kursora myszy z komunikatu unit MainFrm; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls; type TMainForm = class(TForm) lblInfo: TLabel; private procedure WmLButtonDown(var Msg : TMessage); message WM_LBUTTONDOWN; public { Public declarations } end; var MainForm: TMainForm; implementation {$R *.dfm} { TMainForm } procedure TMainForm.WmLButtonDown(var Msg: TMessage); begin lblInfo.Caption := 'Wsprzdne myszy: ' + IntToStr(Msg.LParamLo) + ' i ' + IntToStr(Msg.LParamHi); end; end.

Rysunek 5.1. Program w trakcie dziaania

215 | S t r o n a

Specyficzne struktury typw


Nie jest konieczne mozolne rozszyfrowanie parametrw lParam i wParam za kadym razem. Delphi udostpnia specyficzne dla niektrych komunikatw struktury danych, dziki ktrym moemy uzyska informacj na temat parametrw. Przykadowo zamiast uywa ? jak w poprzednim przykadzie ? rekordu TMessage, wystarczyo skorzysta z TWMMouse: type TWMMouse = packed record Msg: Cardinal; Keys: Longint; case Integer of 0: ( XPos: Smallint; YPos: Smallint); 1: ( Pos: TSmallPoint; Result: Longint); end;

Osobny rekord jest zadeklarowany praktycznie dla wikszoci standardowych komunikatw Windows. Nie musisz pamita typw waciwych dla nich wszystkich ? wystarczy zna nazw komunikatu, a nazw struktury mona utworzy, dodajc na samym pocztku liter T i usuwajc znak _. Przykadowo dla komunikatu WM_LBUTTONDOWN odpowiedni struktur bdzie TWmLButtonDown. Nic nie stoi oczywicie na przeszkodzie, aby korzysta ze struktury TMessage, ktra pasuje do kadego rodzaju komunikatu.

Zdarzenie OnMessage
Klasa TApplication zawiera zdarzenie OnMessage, dziki ktremu my, projektanci moemy kontrolowa nadsyane do aplikacji komunikaty. Tak samo, jak w przypadku zdarzenia OnException , prno szuka go w Inspektorze Obiektw ? jest to zdarzenie ukryte, ktre moemy przypisa w nastpujcy sposb: Application.OnMessage := Procedura_Obslugi;

Procedura obsugujca zdarzenie OnMessage musi mie specjaln budow: procedure MyOnMessage(var Msg : TMsg; var Handled: Boolean); Pierwszym parametrem jest wskazanie struktury TMsg, ktr omwiem nieco wczeniej. Drugi parametr informuje o tym, czy komunikat zosta poprawnie i w caoci obsuony. Zdarzenie OnMessage stanowi wygodny w uyciu proces kontroli przepywu komunikatw z kolejki do okna obsugi. Moemy dziki niemu odpowiednio zareagowa na wystpienie okrelonych komunikatw, co zaraz poka. Naley jednak by ostronym w obsudze zdarzenia OnMessage. 216 | S t r o n a

System kieruje do okna naprawd wiele komunikatw, wic naley tak zaprogramowa funkcj, aby nie spowalniaa dziaania programu ani nie spowodowaa jego zawieszenia. Przykad takiego programu znajduje si w listingu 5.3. Listing 5.3. Przechwytywanie komunikatw unit MainFrm; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls, Grids, ValEdit; type TMainForm = class(TForm) GroupBox1: TGroupBox; vleMsg: TValueListEditor; procedure FormCreate(Sender: TObject); procedure btnClearClick(Sender: TObject); private ID : Integer; // licznik komunikatw procedure MyOnMessage(var Msg : TMsg; var Handled: Boolean); public { Public declarations } end; var MainForm: TMainForm; implementation {$R *.dfm} { TForm1 } procedure TMainForm.MyOnMessage(var Msg: TMsg; var Handled: Boolean); begin Inc(ID); // zwiksz licznik { dodaj nowy rekord do komponentu TValueListEditor } vleMsg.InsertRow(IntToStr(ID), IntToHex(Msg.message, 4), False); end; procedure TMainForm.FormCreate(Sender: TObject); begin { wszystkie komunikaty kieruj do procedury MyOnMessage } Application.OnMessage := MyOnMessage; end;

end.

217 | S t r o n a

Kluczow spraw jest przydzielenie na starcie odpowiedniej procedury do zdarzenia OnMessage: Application.OnMessage := MyOnMessage;

W procedurze MyOnMessage nastpuje zwikszenie licznika ID, a nastpnie dodanie nowego rekordu do komponentu TValueListEditor. Komponent ten znajduje si na zakadce Additional i suy do przedstawiania wartoci w formie dwch kolumn (rysunek 5.2). W drugiej kolumnie warto komunikatu zaprezentowana jest w postaci heksadecymalnej (szesnastkowej), a to za spraw funkcji IntToHex.

Rysunek 5.2. Komunikaty przekazane do zdarzenia OnMessage

Wysyanie komunikatw
Czyme by bya moliwo odbierania komunikatw bez moliwoci ich wysyania? Wysyanie komunikatw jest operacj wzgldnie prost, realizowan za pomoc trzech funkcji: SendMessage, PostMessage, Perform. Dwie z nich zwizane s z WinAPI, a ostatnia z VCL.

Perform
Kada kontrolka VCL udostpnia metod, dziki ktrej mona przekaza jej jaki komunikat. Ta metoda nosi nazw Perform. Deklaracja odpowiedniej funkcji przedstawia si tak: function Perform(Msg: Cardinal; WParam, LParam: Longint): Longint;

Pierwszym parametrem jest nazwa komunikatu przesyanego do danej kontrolki. Kolejne dwa parametry to wartoci przekazywane wraz z parametrami. Poniszy kod powoduje wstawienie do kontrolki tekstowej napisu Nowy tekst... 218 | S t r o n a

procedure TMainForm.btnSetTextClick(Sender: TObject); begin { ustaw tekst na kontrolce } edtValue.Perform(WM_SETTEXT, 0, Longint(PChar('Nowy tekst...'))); end;

Jak widzisz, wysyamy do komponentu edtValue komunikat WM_SETTEXT, powodujcy wstawienie do okna tekstu z parametru lParam. Parametr wParam moe by pusty (mie warto 0). Zastosowaem tutaj podwjne rzutowanie, gdy parametr lParam musi by liczb: Longint(PChar('Nowy tekst...')));

Jeli nie uylibymy rzutowania PChar(), kompilator wywietliby bd: [Error] MainFrm.pas(34): Invalid typecast. Typ Longint z punktu widzenia kompilatora jest rwny typowi Integer. Rwnie dobrze mgby zamiast Longint wpisa Integer, a kod i tak zostaby skompilowany prawidowo. Teraz moesz zobaczy, jak proste jest VCL w porwnaniu z operowaniem na komunikatach. Tak sam czynno jak w powyszym kodzie moemy zrealizowa za pomoc jednego wiersza kodu: edtValue.Text := 'Nowy tekst...'; // wstawianie tekstu

Rwnie dobrze wysyajc komunikat WM_SETTEXT moesz zamiast rzutowanej wartoci przekaza po prostu adres bufora zawierajcego dany tekst ? oto przykad: procedure TMainForm.btnSetTextClick(Sender: TObject); var Buffer : array[0..255] of char; // tablica 255-elementowa begin { ustaw tekst na kontrolce } Buffer := 'Nowy tekst...'; edtValue.Perform(WM_SETTEXT, 0, Longint(@Buffer)); // przekazujemy wskanik jako parametr lParam end;

219 | S t r o n a

Odczyt tekstu z kontrolki Aby odczyta tekst z kontrolki, moemy skorzysta z komunikatu WM_GETTEXT. Podczas wysyania komunikatu pierwszy parametr ? wParam ? musi zawiera maksymaln ilo znakw do pobrania. Drugi parametr ? lParam ? musi zawiera wskazanie buforu, czyli zmiennej, ktrej komunikat przypisze odczytane wartoci. procedure TMainForm.btnGetTextClick(Sender: TObject); var Buffer : array[0..255] of char; begin { odczytujemy tekst wpisany w kontrolce i przypisujemy do bufora ? Buffer } edtValue.Perform(WM_GETTEXT, SizeOf(Buffer), Integer(@Buffer)); Application.MessageBox(PChar('Tekst wpisany w kontrolce "' + Buffer + '"'), '', MB_OK + MB_ICONINFORMATION); end;

Na samym pocztku zadeklarowaem tablic 255-elementow, ktra po wysaniu komunikatu bdzie zawiera tekst znajdujcy si w kontrolce. Podczas wysyania komunikatu parametr wParam zawiera ilo elementw tablicy (funkcja SizeOf), a drugi parametr jest wskanikiem tablicy Buffer. Kompletny kod programu zaprezentowany w tym podpunkcie znajduje si na pycie CD-ROM doczonej do niniejszej ksiki. Projekt znajduje si w katalogu ..listingi/5/Perform. Podczas operowania na komunikatach przydatna staje si pomoc Delphi, ktra zawiera opis komunikatw oraz wymaganych parametrw.

Funkcje SendMessage i PostMessage


Chcc pokona barier naszej aplikacji i wysa komunikat do innego okna, naley skorzysta z funkcji WinAPI ? SendMessage lub PostMessage. Deklaracje tych funkcji (deklaracja znajduje si w pliku Windows.pas) przedstawiem poniej: function LPARAM): function LPARAM): SendMessage(hWnd: HWND; Msg: UINT; wParam: WPARAM; lParam: LRESULT; stdcall; PostMessage(hWnd: HWND; Msg: UINT; wParam: WPARAM; lParam: BOOL; stdcall;

Dziaanie obydwu z nich jest bardzo podobne ? rnice s nike, ale o tym opowiem za chwil. Obydwie funkcje zostay zadeklarowane z uyciem klauzuli stdcall. Szczegowo opowiem o tym w rozdziale 9. Budowa samych funkcji SendMessage i PostMessage jest podobna do Perform, tyle e ta ostatnia nie posiada parametru hWnd, ktry okrela uchwyt okna, do ktrego kierujemy komunikat. 220 | S t r o n a

Pobieranie uchwytu okna Do pobierania uchwytu okna suy funkcja API ? FindWindow. Funkcja ta jest raczej prosta w uyciu: function FindWindow(lpClassName, lpWindowName: PChar): HWND; stdcall;

Wymaga podania dwch parametrw, ktre umoliwi znalezienie uchwytu danego okna. Pierwszy z nich okrela nazw klasy, a drugi okrela tekst (tytu) okna. Przykadowo jeli chcemy pobra uchwyt do okna Inspektora obiektw, moemy napisa: Uchwyt := FindWindow(nil, 'Object Inspector');

Jeeli nie znasz klasy okna lub jego tytuu, moesz wpisa w tym miejscu warto nil (pusty). Na doczonej do ksiki pycie CD-ROM znajduje si kod rdowy programu mojego autorstwa, ktry przedstawia graficznie wszystkie okna otwarte w danym momencie w systemie. Projekt umieszczony jest w katalogu ..listingi/5/EnumWnd; w kodzie zawarte jest duo komentarzy, wic nie powiniene mie problemw z jego zrozumieniem. Program przedstawiony zosta na rysunku 5.3.

Rysunek 5.3. Rozkad okien otwartych w systemie Istnieje moliwo rozwinicia danej gazi, co spowoduje pojawienie si okien pochodnych. Przed znakiem dwukropka (:) wpisany jest tytu okna, a po dwukropku klasa danego okna

221 | S t r o n a

Rnice pomidzy SendMessage i PostMessage Wczeniej powiedziaem, e dwie funkcje ? SendMessage oraz PostMessage ? s niemal identyczne. Podkrelam sowo niemal, gdy rni si one w sposobie przekazania komunikatu. Funkcja PostMessage kieruje komunikat bezporednio do kolejki komunikatw i natychmiast zwraca sterowanie do aplikacji, niezalenie od tego, co dalej stanie si z komunikatem. Funkcja moe zwrci warto True (komunikat zosta zakolejkowany) lub False (komunikat nie dotar do kolejki). Funkcja SendMessage natomiast kieruje komunikat bezporednio do danego okna i czeka na jego wykonanie. Moe zwrci jak konkretn warto, przekazan przez dany komunikat.

Przykad uycia

Jeeli chcemy przykadowo zamkn dan aplikacj, moemy wysa do niej komunikat WM_QUIT: procedure TForm1.Button1Click(Sender: TObject); var H : HWND; begin H := FindWindow(nil, 'Bitmapy'); PostMessage(H, WM_QUIT, 0, 0); end;

Utworzony wedug powyszego kodu program odnajdzie uchwyt programu, ktrego tytu to Bitmapy, a nastpnie wyle do niego komunikat WM_QUIT.

Komunikaty rozgaszajce
VCL udostpnia funkcj Broadcast, ktra jest uywana do rozsyania tego samego komunikatu do wszystkich kontrolek potomnych (child), czyli takich, ktre s umieszczone na danym obiekcie. Poniszy przykad prezentuje rozsyanie komunikatu WM_CHAR do wszystkich kontrolek umieszczonych na formularzu: procedure TForm1.Button1Click(Sender: TObject); var Message : TMessage; begin Message.Msg := WM_CHAR; Message.WParam := 65; Message.LParam := 0; Message.Result := 0; Broadcast(Message); end; Funkcja Broadcast jest dostpna dla wszystkich obiektw wizualnych VCL 222 | S t r o n a

Deklarowanie wasnych komunikatw

Istnieje moliwo deklaracji wasnych komunikatw oraz procesu komunikacji midzy dwiema aplikacjami wykorzystujcymi ten komunikat. Pierwszym krokiem jest deklaracja takiego samego komunikatu (o tej samej wartoci ? identyfikatorze). const WM_ERROR = WM_USER + 1001;

Moe to wyglda tak, jak powyej; kluczow spraw jest okrelenie takiego identyfikatora, ktry nie bdzie kolidowa z innymi, zarezerwowanymi ju komunikatami. Do staej WM_USER naley doda jak warto, ktrej grna granica wynosi 31 734.

Obsuga komunikatu
Po zadeklarowaniu komunikatu jego obsuga jest taka sama, jak w przypadku innych komunikatw. Po pierwsze w aplikacji naley zadeklarowa taki nagwek: procedure WmError(var Msg : TMessage); message WM_ERROR;

Od tej pory procedura WmError bdzie odpowiedzialna za odebranie naszego komunikatu. Listingi 5.4 oraz 5.5 przedstawiaj dwa moduy ? jeden odpowiedzialny jest za wysyanie komunikatw, a drugi za ich odbieranie. Listing 5.4. Odebranie komunikatu WM_ERROR unit RcvMainFrm; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls; const WM_ERROR = WM_USER + 1001; ErrorArray : array[0..2] of String[25] = ('Bd niskiego stopnia', 'Bd powany', 'Bd krytyczny!'); type 223 | S t r o n a

TMainForm = class(TForm) edtText: TEdit; Label1: TLabel; private procedure WmError(var Msg : TMessage); message WM_ERROR; end; var MainForm: TMainForm; implementation {$R *.dfm} { TMainForm } procedure TMainForm.WmError(var Msg: TMessage); begin edtText.Text := ErrorArray[Msg.wParam] + ' (' + IntToStr(Msg.LParam) + ')'; Msg.Result := 1; // warto zwrotna end; end.

Listing 5.5. Wysanie komunikatu WM_ERROR unit SndMainFrm; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls; const WM_ERROR = WM_USER + 1001; type TMainForm = class(TForm) btnSend: TButton; procedure btnSendClick(Sender: TObject); end; var MainForm: TMainForm; 224 | S t r o n a

implementation {$R *.dfm}

procedure TMainForm.btnSendClick(Sender: TObject); var iHandle : HWND; begin iHandle := FindWindow('TMainForm', 'Odebranie komunikatu'); if SendMessage(iHandle, WM_ERROR, 2, 20) = 1 then ShowMessage('Komunikat przekazany!') else ShowMessage('Bd. Komunikat nie zosta przekazany!'); end; end.

Kompletne kody dwch programw znajduj si w katalogu ..listingi/5/OwnMsg. Przy okazji analizy dwch kodw rdowych z listingw 5.4 i 5.5 moesz zaobserwowa wyraniejsz rnic pomidzy funkcjami SendMessage oraz PostMessage. Po wysaniu komunikatu program sprawdza, czy dotar do miejsca przeznaczenia: if SendMessage(iHandle, WM_ERROR, 2, 20) = 1 then { co }

Jeeli komunikat zosta odebrany, to zwrcon wartoci powinna by liczba 1, a to za spraw moduu RcvMainFrm.pas: procedure TMainForm.WmError(var Msg: TMessage); begin edtText.Text := ErrorArray[Msg.wParam] + ' (' + IntToStr(Msg.LParam) + ')'; Msg.Result := 1; // warto zwrotna end;

Do pola Result struktury TMessage przypisana zostaa warto zwrotna ? 1.

225 | S t r o n a

Funkcje operujce na uchwytach


Istnieje wiele funkcji, ktre pozwalaj nam ? jeli znamy uchwyt danego okna ? manipulowa nim, czyli przesuwa je, minimalizowa, maksymalizowa, zmienia pooenie itp. Nie jest w takim przypadku konieczne korzystanie z komunikatw. Moe w tym momencie odbiegam troch od tematu tego rozdziau, ale podam nazwy tych najwaniejszych funkcji API, bo mog one Ci si przyda w przyszoci.

CloseWindow
function CloseWindow(hWnd: HWND): BOOL; stdcall;

Wbrew pozorom ta funkcja powoduje jedynie zminimalizowanie okna okrelonego w parametrze, a nie ? jak mogoby si wydawa na pocztku ? zamykanie go. procedure TForm1.Button1Click(Sender: TObject); var hW : HWND; begin hW := FindWindow(nil, 'Unit1.pas'); // okrel uchwyt CloseWindow(hW); // minimalizuj end;

EnableWidow
function EnableWindow(hWnd: HWND; bEnable: BOOL): BOOL; stdcall;

Funkcja powoduje dezaktywacj okna okrelonego w pierwszym parametrze ? hWnd. Drugi parametr ? bEnable ? okrela, czy okno ma zosta uaktywnione (True) czy zablokowane (False). Dezaktywacja powoduje, e nie mona z danym oknem wykona adnych czynnoci, jak np. przemieszczenie okienka, minimalizacja itp.

226 | S t r o n a

GetClientRect
function GetClientRect(hWnd: HWND; var lpRect: TRect): BOOL; stdcall;

Funkcja podaje szeroko i wysoko okna okrelonego w pierwszym parametrze. Drugi parametr musi zawiera nazw zmiennej typu TRect, do ktrej przypisane zostan odczytane dane. TRect to nic innego jak zwyky rekord: TRect = record case Integer of 0: (Left, Top, Right, Bottom: Integer); 1: (TopLeft, BottomRight: TPoint); end;

Posiada on parametry okrelajce jego pooenie z lewej strony, od gry, od prawej i od dou. Przykad uycia funkcji GetClientRect: procedure TForm1.Button1Click(Sender: TObject); var hW : HWND; R : TRect; begin hW := FindWindow(nil, 'Unit1.pas'); // okrel uchwyt Windows.GetClientRect(hW, R); end;

Wywoujc funkcj, naley poda take nazw moduu, czyli w naszym przypadku Windows. Jest to specyficzne wywoanie, ktrego bdziesz uywa wtedy, gdy np. nazwa danej funkcji bdzie zarezerwowana przez Delphi (w takiej sytuacji kompilator ?nie wie?, do ktrej funkcji programista chce si odwoa ? naley zatem poda nazw moduu). Funkcja GetClientRect dostarcza informacji jedynie o szerokoci danego okna (R.Right) oraz o jego wysokoci (R.Bottom). Jeeli chcesz uzyska informacj o pooeniu okna w poziomie (odlego od lewej krawdzi ekranu), skorzystaj z funkcji GetWindowRect, ktra posiada takie same parametry jak wspomniana ju funkcja GetClientRect.

227 | S t r o n a

GetDesktopWindow
function GetDesktopWindow: HWND; stdcall;

228 | S t r o n a potrzebowa np. namalowa co na pulpicie. Wwczas koniecznym staje si pobranie uchwytu do pulpitu. O funkcjach graficznych Delphi bdzie mowa w rozdziale 10.

GetForegroundWindow
function GetForegroundWindow: HWND; stdcall;

Dziki tej funkcji moesz pobra uchwyt do okna, ktre aktualnie znajduje si ?na wierzchu?, nad innymi oknami.

GetParent
function GetParent(hWnd: HWND): HWND; stdcall;

Dziki funkcji GetParent moesz uzyska uchwyt do okna macierzystego. Zamy, e masz ju uchwyt do jakiego okna ? komponentu umieszczonego na formularzu. Chcesz jednak uzyska uchwyt owego formularza. Wwczas moesz skorzysta z funkcji GetParent.

GetWindowText
function GetWindowText(hWnd: HWND; lpString: PChar; nMaxCount: Integer): Integer; stdcall;

Dziaanie funkcji GetWindowText mona porwna do komunikatu WM_GETTEXT. Mona si domyle znaczenia tej funkcji ? powoduje ona odczytanie tekstu (tytuu) okna podanego w parametrze pierwszym. procedure TForm1.Button1Click(Sender: TObject); var hW : HWND; Buffer : array[0..255] of char; begin hW := FindWindow(nil, 'Unit1.pas'); // okrel uchwyt GetWindowText(hW, Buffer, SizeOf(Buffer)); { Buffer = Unit1.pas } end;

228 | S t r o n a

Z funkcji mona skorzysta np. wtedy, gdy znamy klas danego okna, a nie znamy jego tytuu. Istnieje take funkcja GetWindowTextLength, ktra podaje dugo tekstu (tytuu) danego okna w postaci iloci znakw.

IsIconic
function IsIconic(hWnd: HWND): BOOL; stdcall;

Funkcja zwraca True w przypadku, gdy okno podane w parametrze hWnd jest zminimalizowane.

IsWindow
function IsWindow(hWnd: HWND): BOOL; stdcall;

Funkcja zwraca True, jeeli uchwyt podany w parametrze hWnd jest oknem.

IswindowVisible
function IsWindowVisible(hWnd: HWND): BOOL; stdcall;

Jeeli okno podane w parametrze pierwszym jest oknem widocznym, funkcja zwraca True, a w przeciwnym wypadku ? False.

MoveWindow
function MoveWindow(hWnd: HWND; X, Y, nWidth, nHeight: Integer; bRepaint: BOOL): BOOL; stdcall;

229 | S t r o n a

Funkcja MoveWindow suy do okrelenia pozycji danego okna. Moesz przemieci dane okno lub zmieni jego rozmiar. Znaczenie poszczeglnych parametrw jest nastpujce:

hWnd ? uchwyt okna, ktrego bdzie dotyczy operacja. X, Y ? nowe pozycje X (poziom) i Y (pion) dla okna. nWidth, nHeight ? szeroko i wysoko okna. bRepaint ? okrela, czy okno po zakoczeniu operacji ma zosta odwieone (True) czy te nie (False). O odwieaniu opowiem w rozdziale powiconym grafice.

procedure TForm1.Button1Click(Sender: TObject); var hW : HWND; begin hW := FindWindow(nil, 'Unit1.pas'); // okrel uchwyt MoveWindow(hW, 1, 1, 600, 200, False); end;

OpenIcon
function OpenIcon(hWnd: HWND): BOOL; stdcall;

Dziki funkcji OpenIcon moesz przywrci zminimalizowane okno do normalnego stanu. procedure TForm1.Button1Click(Sender: TObject); var hW : HWND; begin hW := FindWindow(nil, 'Unit1.pas'); // okrel uchwyt OpenIcon(hw); end;

230 | S t r o n a

SetForegroundWindow
function SetForegroundWindow(hWnd: HWND): BOOL; stdcall;

Funkcja SetForegroundWindow powoduje ustawienie aktywnego okna. Aktywne okno znajduje si na samym wierzchu. Jeeli operacja si powiedzie, funkcja zwrci warto True. procedure TForm1.Button1Click(Sender: TObject); var hW : HWND; begin hW := FindWindow(nil, 'Unit1.pas'); // okrel uchwyt SetForegroundWindow(hW); end;

Jak zapewne zauwaye, znaczenie funkcji SetForegroundWindow jest odwrotne do GetForegroundWindow. Ta druga funkcja pobiera aktywne okno, a pierwsza je ustawia. Podobnie jest z innymi funkcjami prezentowanymi w tym podpunkcie.

SetWindowText
function SetWindowText(hWnd: HWND; lpString: PChar): BOOL; stdcall;

Funkcja powoduje ustawienie nowej wartoci tekstowej dla okna okrelonego w pierwszym parametrze. procedure TForm1.Button1Click(Sender: TObject); var hW : HWND; begin hW := FindWindow(nil, 'Unit1.pas'); // okrel uchwyt SetWindowText(hW, 'Nowy tekst'); end;

231 | S t r o n a

ShowWindow
function ShowWindow(hWnd: HWND; nCmdShow: Integer): BOOL; stdcall;

Dziki funkcji ShowWindow moliwe jest nadanie okrelonego statusu dla danego okna. Piszc ?status? mam na myli pozycj okna, ktre okrela drugi parametr (tabela 5.3). Flaga SW_HIDE SW_MAXIMIZE SW_MINIMIZE SW_RESTORE SW_SHOW SW_SHOWDEFAULT Powoduje ukrycie okna Maksymalizacja danego okna Minimalizacja okna Przywrcenie okna do domylnego pooenia (jeeli okno jest zminimalizowane lub zmaksymalizowane) Wywietlenie ukrytego okna Przywrcenie okna z domylnymi wartociami (szeroko, wysoko) Opis

SW_SHOWMAXIMIZED Wywietlenie okna i zmaksymalizowanie go SW_SHOWMINIMIZED Wywietlenie okna i zminimalizowanie go

procedure TForm1.Button1Click(Sender: TObject); var hW : HWND; begin hW := FindWindow(nil, ?Unit1.pas?); // okrel uchwyt ShowWindow(hW, SW_MINIMIZE); end;

232 | S t r o n a

?Zahaczanie? okien
Piszc ?zahaczanie? pozwoliem sobie na mae spolszczenie. W rodowisku programistycznym takie ?zahaczanie? jest nazywane ?zakadaniem hooka? 1. Jest to mechanizm umoliwiajcy monitorowanie komunikatw przepywajcych przez system. Jako przykad zaprezentuj Ci w tym rozdziale aplikacj, ktra przechwytywa bdzie dane o wszystkich naciskanych w systemie klawiszach. Aplikacje tego typu okrelane s jako KeySpy (w dosownym tumaczeniu szpieg klawiszy).

Zakadanie globalnego hooka


Do zakadania hooka systemowego suy funkcja API ? SetWindowsHookEx. Najlepiej jest wywoywa t funkcj podczas startu aplikacji (zdarzenie OnCreate). Po zakoczeniu dziaania programu hooka naley zwolni ? czyni to funkcja UnhookWindowsHookEx. Deklaracje obu funkcji przedstawiaj si nastpujco: function SetWindowsHookEx(idHook: Integer; lpfn: TFNHookProc; hmod: HINST; dwThreadId: DWORD): HHOOK; stdcall; function UnhookWindowsHookEx(hhk: HHOOK): BOOL; stdcall;

Funkcja SetWindowsHookEx zwraca wskazanie zmiennej typu HHOOK. Nazw tej zmiennej naley poda w parametrze UnhookWindowsHookEx, aby prawidowo zwolni wszystkie zasoby. Znaczenie poszczeglnych parametrw polecenia SetWindowsHookEx jest nastpujce:
idHook ? okrela typ hooka, czyli rodzaj komunikatw, ktre maj by przechwytywane. W naszym przykadzie w tym polu podamy warto WH_JOURNALRECORD, ktra okrela wszelkie

komunikaty kierowane do systemowej kolejki komunikatw (ang. message queue). lpfn ? parametr ten musi okrela nazw procedury, ktra obsugiwa bdzie ?nadlatujce? komunikaty. Tym zajmiemy si nieco pniej. hMod ? parametr ten suy do okrelenia moduu, w ktrym znajduje si procedura majca obsuy nasz hook. Procedura moe by bowiem umieszczona w bibliotece DLL, dlatego jeli pisz ?modu?, mam na myli albo nasz aplikacj, albo bibliotek DLL. dwThreadId ? wtek, ktrego dotyczy hook. Na razie nie zawracaj sobie tym gowy ? wtkami zajmiemy si w rozdziale 8.

Zaoenie i zwolnienie hooka moe wyglda np. tak: procedure TMainForm.FormCreate(Sender: TObject); begin 233 | S t r o n a

{ za hooka } MainHook := SetWindowsHookEx(WH_JOURNALRECORD, KeyboardHook, hInstance, 0); if (MainHook = NULL) then raise Exception.Create('Bd! Hook nie zosta zaoony!'); end; procedure TMainForm.FormDestroy(Sender: TObject); begin UnhookWindowsHookEx(MainHook); end;

Drugi parametr funkcji SetWindowsHookEx okrela nazw procedury obsugujcej nasz hook ? procedur t stworzymy za chwil. Trzeci parametr to hInstance ? ta staa informuje, e operacje dotycz naszej aplikacji. Pamitasz, jak mwiem wczeniej o tym, e hook moe by umieszczony take w bibliotece DLL? W takim wypadku w tym miejscu naleaoby poda uchwyt biblioteki DLL, lecz gdy procedura obsugujca hook znajduje si w naszym programie, wystarczy wpisa sowo hInstance. Jeeli zakadanie hooka powiedzie si, zmienna MainHook bdzie zawieraa wskazanie na niego. W przeciwnym wypadku bdzie to warto NULL. Warto NULL podobnie jak nil oznacza warto pust.

Funkcja obsugujca hooka


Nasza funkcja obsugujca hooka bdzie nosia nazw HookKeyboard. Musi ona mie specyficzn budow, czyli skada si z okrelonej liczby elementw o okrelonym typie. function KeyboardHook(Code: Integer; wParam : WPARAM; lParam : LPARAM): Longint; stdcall;

Pierwszy parametr okrela typ hooka, a dwa pozostae zawieraj dodatkowe parametry. Jeeli chcesz si dowiedzie czego wicej na ten temat, odsyam Ci do pomocy Delphi. Parametr lParam standardowo zawiera wskanik struktury TEventMsg. Tak wic na samym starcie naley odczyta ten wskanik oraz dane struktury. Rekord TEventMsg wyglda nastpujco: TEventMsg = packed record message: UINT; paramL: UINT; paramH: UINT; time: DWORD; hwnd: HWND; end; 234 | S t r o n a

Poszczeglne pola rekordu maj praktycznie takie same znaczenie, jak omawiany na pocztku tego rozdziau rekord TMsg. Kolejnym krokiem jest ?odfiltrowanie? komunikatw i pozostawienie tylko tych, ktre s komunikatem WM_KEYDOWN. Parametr paramL struktury TEventMsg jest kodem ASCII nacinitego klawisza. Oto, jak bdzie wygldaa caa funkcja KeyboardHook ? przypatrz si jej dokadnie; pniej omwi poszczeglne jej elementy. var MainHook : HHOOK; // wskazanie na hooka lpWnd : PChar; // nazwa okna, w ktrym uytkownik naciska klawisz function KeyboardHook(Code: Integer; wParam : WPARAM; lParam : LPARAM): Longint; stdcall; var Buffer : TEventMsg; // deklaracja struktury Wnd : array[0..255] of char; procedure TranslateKey(Key : Byte); begin with MainForm do begin case Key of 13: Memo.Lines.Add(''); 8: Memo.Text := Memo.Text + '[backspace]'; 27: Memo.Text := Memo.Text + '[esc]'; else Memo.Text := Memo.Text + Chr(Key); end; end; end; begin Result := 0; // warto zwracana przez procedur Buffer := PEventMsg(lParam)^; // uzyskanie danych poprzez odczytanie wskanika if Buffer.Message = WM_KEYDOWN then { dotyczy tylko komunikatu WM_KEYDOWN } begin GetWindowText(Buffer.hwnd, Wnd, SizeOf(Wnd)); // pobierz tekst aktywnego okna { jeeli uytkownik zapisuje dane w innym oknie } if Wnd <> lpWnd then begin lpWnd := Wnd; MainForm.Memo.Clear; // wyczy zawarto komponentu end; TranslateKey(Buffer.paramL); end; end; 235 | S t r o n a

Funkcja ta zawiera w sobie procedur TranslateKey, ktra ma na celu analiz kodw ASCII. Jeeli, przykadowo kod ASCII jest rwny 13, oznacza to, e uytkownik wcisn klawisz Enter (liczba 13 jest kodem ASCII klawisza Enter) i naley na to odpowiednio zareagowa (doda nowy wiersz w komponencie TMemo). Odczytanie wskanika zawartego w parametrze lParam wyglda tak: Buffer := PEventMsg(lParam)^; // uzyskanie danych poprzez odczytanie wskanika

Dziki temu od tej pory rekord Buffer bdzie zawiera szczegowe informacje na temat komunikatu (kod ASCII klawisza, nazw komunikatu itp.). W listingu 5.6 zaprezentowany zosta cay kod rdowy moduu. Listing 5.6. Modu odpowiedzialny za tworzenie globalnego hooka unit MainFrm; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls, ComCtrls; type TMainForm = class(TForm) Memo: TMemo; procedure FormCreate(Sender: TObject); procedure FormDestroy(Sender: TObject); end; var MainForm: TMainForm; implementation {$R *.dfm} var MainHook : HHOOK; // wskazanie na hooka lpWnd : PChar; // nazwa okna, w ktrym uytkownik naciska klawisz function KeyboardHook(Code: Integer; wParam : WPARAM; lParam : LPARAM): Longint; stdcall; var Buffer : TEventMsg; // deklaracja struktury Wnd : array[0..255] of char; procedure TranslateKey(Key : Byte); 236 | S t r o n a

begin with MainForm do begin case Key of 13: Memo.Lines.Add(''); 8: Memo.Text := Memo.Text + '[backspace]'; 27: Memo.Text := Memo.Text + '[esc]'; else Memo.Text := Memo.Text + Chr(Key); end; end; end; begin Result := 0; // warto zwracana przez procedur Buffer := PEventMsg(lParam)^; // uzyskanie danych poprzez odczytanie wskanika if Buffer.Message = WM_KEYDOWN then { dotyczy tylko komunikatu WM_KEYDOWN } begin GetWindowText(Buffer.hwnd, Wnd, SizeOf(Wnd)); // pobierz tekst aktywnego okna { jeeli uytkownik zapisuje dane w innym oknie } if Wnd <> lpWnd then begin lpWnd := Wnd; MainForm.Memo.Clear; // wyczy zawarto komponentu end; TranslateKey(Buffer.paramL); end; end; procedure TMainForm.FormCreate(Sender: TObject); begin { za hooka } MainHook := SetWindowsHookEx(WH_JOURNALRECORD, KeyboardHook, hInstance, 0); if (MainHook = NULL) then raise Exception.Create('Bd! Hook nie zosta zaoony!'); end; procedure TMainForm.FormDestroy(Sender: TObject); begin UnhookWindowsHookEx(MainHook); end; end.

Moesz skompilowa i sprawdzi dziaanie programu. Rysunek 5.4 prezentuje program w trakcie dziaania. Jeli piszemy ten tekst w edytorze Microsoft Word, tekst zostanie rwnie dodany do komponentu TMemo. 237 | S t r o n a

Rysunek 5.4. Przechwytywanie naciskanych klawiszy Podczas krtkiego zapoznania z programem moesz zauway, e litery z polskimi znakami diakrytycznymi s wpisywane do TMemo jako ?krzaczki?. Twoje dodatkowe zadanie to ulepszenie programu. Spjrz na procedur TranslateKey, umieszczon w funkcji HookKeyboard. Procedura ta ma za zadanie zastpowa niektre kody ASCII odpowiednim tekstem. Tak samo naley postpi z ?krzaczkami? ? naley po prostu je zastpi. Aby uatwi Ci zadanie, w katalogu ..listingi/5/ASCII na pycie CD-ROM umieciem program, ktry pokazuje kody ASCII naciskanych klawiszy.

Podsumowanie
By moe komunikaty nie bd zbyt czsto przez Ciebie stosowane, ale warto je zna. Jest to ju ten aspekt programowania, ktry mona zaliczy do ?bardziej zaawansowanych umiejtnoci? ? nieraz moesz z tej wiedzy skorzysta. Pod koniec tego rozdziau podjem si, troch ryzykownie, omwienia tematu hookw, ktry jest nieco trudniejszy ni sama obsuga komunikatw w VCL, wic moe na tym etapie by dla Ciebie troch niezrozumiay. Poznae ju pierwsze funkcji API, ktre ? nie ma co ukrywa ? s trudniejsze w wykorzystaniu ni zwyke klasy VCL, ale wiedza o uyciu narzdzi WinAPI pozwoli Ci zosta o wiele lepszym programist. Zaczniki:

Listingi_5.zip (436.88 kB)

238 | S t r o n a

Rozdzia 6
Rejestry i pliki INI

Czy nie zastanawiao Ci nigdy, gdzie tak naprawd te wszystkie programy przechowuj informacje o uytkowniku oraz opcje i inne ustawienia? W dziewidziesiciu procentach takich przypadkw autorzy tyche programw musieli skorzysta z Rejestru Windows lub plikw INI. By moe nie wiesz dokadnie, czym jest Rejestr lub pliki INI, wic na samym pocztku tego rozdziau powic par stron na omwienie budowy zarwno Rejestru, jak i plikw INI.

Czym jest Rejestr Windows?


Rejestr Windows to gszcz informacji na temat ustawie systemowych oraz ustawie programw dziaajcych na komputerze. Uszkodzenie rejestru moe spowodowa problemy z uruchomieniem systemu, naley zatem obchodzi si z nim do delikatnie. Chocia Microsoft moe temu zaprzecza, wiadomo, e Rejestr Windows jest jednym z najwaniejszych elementw systemu; zawiera wiele kluczy oraz wartoci niezwykle istotnych dla dziaania systemu. Rejestr Windows, a waciwie Edytor Rejestru, zosta przedstawiony na rysunku 6.1.

Rysunek 6.1. Okno Edytora Rejestru Program przedstawiony na rysunku 6.1 mona uruchomi, wybierajc z menu Start/Uruchom i wpisujc w okienku regedit.

239 | S t r o n a

Jako e bez zachowania naleytej ostronoci mona bardzo atwo ?zepsu? Rejestr, zalecane jest utworzenie jego kopii zapasowej w postaci pliku *.reg. Mona tego dokona, wybierajc z menu Rejestr pozycj Eksportuj plik rejestru. W oknie musisz wpisa nazw pliku oraz zaznaczy opcj Wszystko. Po zakoczeniu operacji w podanej lokalizacji utworzony zostanie plik, po ktrego uruchomieniu Rejestr zostanie uaktualniony wedug wartoci zawartych w kopii.

Podstawowe klucze
Rejestr Windows dzieli si na kilka podstawowych kluczy gwnych: HKEY_CLASSES_ROOT, HKEY_CURRENT_USER, HKEY_LOCAL_MACHINE, HKEY_USERS, HKEY_CURRENT_CONFIG i HKEY_DYN_DATA. Tylko w tych kluczach gwnych moesz tworzy nowe klucze oraz wartoci. Waciwie powiniene napisa swj program tak, aby tworzy nowe klucze w gazi HKEY_CURRENT_USER, gdy to ona jest przeznaczona dla programw zewntrznych, ale oczywicie nic nie stoi na przeszkodzie, aby wpisywa dane do innych gazi.

Podstawowe pojcia
Rejestr Windows dzieli si na klucze oraz wartoci. W celu zrozumienia zasady dziaania Rejestru oraz dalszej terminologii uywanej przeze mnie w tym rozdziale naley wyjani te pojcia. Mona przyj, e kluczami s gazie (foldery) przedstawione w Edytorze Rejestru. Takie foldery (klucze) mog zawiera bardzo wiele wartoci, ktre s danymi. W kadym kluczu znajduje si tzw. warto domylna, ktra nie zawiera adnych danych. Istnieje moliwo tworzenia kilku typw wartoci: tekstowa, binarna lub DWORD. Warto tekstowa suy do przechowywania tekstu. Warto DWORD to najzwyklejsze dane w postaci liczb, natomiast warto binarna umoliwia przechowywanie wikszej iloci danych.

Pliki INI
Pliki INI to zwyke pliki tekstowe z rozszerzeniem *.ini, o specyficznej budowie opartej na jednolitym standardzie. Pliki INI, podobnie jak Rejestr, su do przechowywania ustawie aplikacji. Dane te s jednak przechowywane w plikach, co daje w efekcie mniejszy poziom bezpieczestwa (kady moe otworzy sobie taki plik w Notatniku Windows) ni Rejestr.

Budowa
Podstawowym kryterium podziau plikw INI s sekcje. W kadej sekcji mog znajdowa si rne klucze i wartoci. Przykadowa zawarto pliku INI (w tym przypadku jest to plik win.ini) wyglda tak:
[Desktop] Wallpaper=(None) TileWallpaper=1 WallpaperStyle=0

240 | S t r o n a

W tym przypadku sekcj jest wiersz [Desktop]. Wymagane jest wpisywanie nazwy nowej sekcji w nawiasach kwadratowych, dziki czemu przy odwoaniu do sekcji w programie system ?wie?, ktre wiersze bd nas interesoway. W sekcji mog znajdowa si klucze i wartoci, oddzielone znakiem rwnoci.
Klucz=warto

Do wartoci mog by przypisywane rne dane, takie jak liczby czy tekst. Kada warto musi by oddzielona od klucza znakiem nowego wiersza. Oznacza to, e kady klucz musi by zapisany jeden pod drugim. Istnieje moliwo stosowania w plikach INI komentarzy. Komentarze, tak jak w jzyku programowania, nie s interpretowane przez system. W plikach INI istniej jedynie komentarze jednowierszowe, ktre musz rozpoczyna si od znaku rednika (;).

Rejestr kontra plik INI


Czasem podczas projektowania aplikacji bdziesz musia zastanowi si, czy zastosowa pliki INI czy moe Rejestr Windows. Jest to wbrew pozorom wana decyzja, ktra podjta musi zosta po przemyleniu paru spraw. Rejestr Windows daje wiksze bezpieczestwo, gdy uytkownik chcc zmieni pewne wartoci, bdzie musia wyszukiwa je w caym gszczu rnych kluczy. Zawarto pliku INI moe by zmieniona praktycznie przez kadego, nawet pocztkujcego. Naley wic wzi pod uwag to, e uytkownik nieumylnie moe spowodowa zmian zawarto pliku INI, a tym samym doprowadzi do nieprawidowego funkcjonowania programu. Z drugiej jednak strony mona skorzysta z plikw INI, aby da moliwo zmiany pewnych ustawie w programie. Tak konstrukcj zastosowali projektanci interpretera PHP. Uytkownik chcc zmieni pewne ustawienia, musi ?pogrzeba? troch w pliku INI. Jest to jednak opcja przeznaczona dla bardziej zaawansowanych programistw. Ostatni aspekt. Podczas usunicia systemu skasowany zostanie take rejestr Windows, a tym samym ustawienia naszego programu. Plik INI moe by umieszczony w katalogu z programem, tak wic reinstalacja systemu nie oznacza utracenia zapisanych w owym pliku danych.

Klasa TRegistry
VCL zwalnia nas w duym stopniu z mozolnego korzystania z funkcji API w celu operowania na Rejestrze. Udostpnia bowiem klas TRegistry, ktra znajduje si w module Registry. Pierwsz rzecz, ktr musisz wykona, jest dodanie do listy uses moduu Registry.

241 | S t r o n a

Tworzenie nowych kluczy


Podstawow operacj na Rejestrze jest otwarcie konkretnego klucza, zamknicie go po zakoczeniu operacji lub stworzenie nowego. Stworzenie nowego klucza realizuje metoda CreateKey, w ktrej naley poda jedynie nazw klucza. procedure TMainForm.btnCreateClick(Sender: TObject); var Reg : TRegistry; begin Reg := TRegistry.Create; Reg.CreateKey('Moja aplikacja 1.0'); Reg.Free; end;

Klasa TRegistry jest zwyk klas VCL, naley wic utworzy j przed rozpoczciem korzystania z Rejestru, a po zakoczeniu ? zwolni za pomoc metody Free. Domylnie wszystkie operacje na rejestrze odbywaj si na kluczu gwnym HKEY_CURRENT_USER. Jeeli chcesz dokona zmian, w innym kluczu gwnym naley przypisa jego nazw do zmiennej RootKey z klasy TRegistry. Powyszy kod spowoduje tworzenie nowego klucza Moja Aplikacja 1.0 w kluczu gwnym HKEY_CURRENT_USER (rysunek 6.2).

Rysunek 6.2. Zaznaczony nowo utworzony klucz w Rejestrze Windows

242 | S t r o n a

Otwieranie istniejcych kluczy


Metoda OpenKey z klasy TRegistry realizuje otwarcie istniejcego klucza lub utworzenie nowego. Tak naprawd do tworzenia nowych kluczy rzadko stosowana jest funkcja CreateKey ? znacznie czciej programici korzystaj z metody OpenKey, ktra moe zarwno otwiera klucze, jak i tworzy nowe! Oto przykad otwarcia istniejcego klucza: procedure TMainForm.btnOpenClick(Sender: TObject); var Reg : TRegistry; begin Reg := TRegistry.Create; if Reg.OpenKey('Moja aplikacja 1.0', False) then ShowMessage('Klucz otwarty!') else ShowMessage('Bd!'); Reg.Free; end;

Funkcja OpenKey zwraca True, jeeli klucz zosta otwarty, lub False, jeeli nie udao si otworzy klucza. Drugi parametr owej funkcji okrela, czy klucz ma zosta utworzony w przypadku, gdy nie istnieje (True), lub czy funkcja po prostu ma zwrci warto False (nie udao si utworzy ani otworzy klucza). Po zakoczeniu korzystania z kluczy naley klucz zamkn poleceniem CloseFile, aczkolwiek nie jest to wymagane, jeeli jednoczenie koczysz uywanie klasy ? wwczas naley wywoa metod Free, ktra zamknie klucz oraz zwolni sam klas. Metoda CloseFile jest uywana w przypadku, gdy operujesz na wielu kluczach Rejestru. Przykadowo: otwierasz jeden, wprowadzasz stosowne zmiany, zamykasz klucz i otwierasz inny.

Usuwanie kluczy
Chcc usun klucz, naley wywoa metod DeleteKey. Jej uycie jest proste, poniewa posiada tylko jeden parametr, ktry jest nazw klucza. Funkcja zwraca True, jeeli operacja si powiedzie, lub ? w przeciwnym wypadku ? False.
... Reg.DeleteKey('Moja Aplikacja 1.0'); ...

Poczymy jeszcze jedno zastrzeenie, eby unikn nieporozumie! Parametr w funkcjach OpenKey, CreateKey i DeleteKey to w rzeczywistoci nie nazwa klucza, lecz cieka do niego. Nasz klucz moe by umieszczony w sporym ?gszczu? innych kluczy, std naley poda pen ciek: Reg.DeleteKey('Klucze\Inny klucz\Moja Aplikacja 1.0');

243 | S t r o n a

Dodawanie wartoci
Bardzo atwo zapamita metody powodujce zapis danych do Rejestru, gdy nazywane s one wedug wzorca WriteXXX, gdzie w miejsce XXX naley poda typ danych przeznaczonych do dodania. Czyli np. chcc zapisa w Rejestrze dane typu String, stosujemy funkcj WriteString. Kada z funkcji zapisujcych dane posiada dwa parametry. Pierwszy z nich to nazwa wartoci w kluczu, a drugi parametr to dane do zapisania. Przykadowo jeli chcesz zapisa w Rejestrze dane typu String, musisz skorzysta z kodu: Reg.WriteString('City', edtCity.Text);

Spowoduje to utworzenie lub nadpisanie wartoci City z danymi zapisanymi w komponencie edtCity. Oto przykad zapisania kilku wartoci do klucza: procedure TMainForm.btnSaveClick(Sender: TObject); var Reg : TRegistry; begin Reg := TRegistry.Create; if Reg.OpenKey('Moja aplikacja 1.0', False) then ShowMessage('Klucz otwarty!') else ShowMessage('Bd!'); Reg.WriteString('FName', edtFName.Text); Reg.WriteString('SName', edtSName.Text); Reg.WriteString('City', edtCity.Text); Reg.WriteString('Country', edtCountry.Text); Reg.WriteInteger('Pesel', StrToInt(edtPesel.Text)); Reg.Free; end;

Na rysunku 6.3 przedstawiony jest klucz Rejestru, ktry przedstawia zapisane i wprowadzone przez program wartoci.

244 | S t r o n a

Rysunek 6.3. Zapisane przez program wartoci

Pozostae funkcje suce do zapisu danych Zapoznae si ju z dwiema funkcjami sucymi do zapisu danych ? WriteInteger oraz WriteString. Oczywicie nie s to jedyne funkcje ? wszystkie pozostae opisuj i przedstawiam poniej:

WriteTime

procedure WriteTime(const Name: String; Value: TDateTime); Funkcja WriteTime suy do zapisu czasu. Dziki niej moesz np. zapisa czas ostatniego uruchomienia programu. ... Reg.WriteTime('Czas', Now); ...

Funkcja Now zwraca aktualny czas. Chcc zapisa take dat, zastosuj funkcj WriteDate lub WriteDateTime.

245 | S t r o n a

WriteFloat

procedure WriteFloat(const Name: String; Value: Double); Funkcja WriteFloat spowoduje stworzenie w Rejestrze nowego klucza z wartoci zmiennoprzecinkow typu Double.

WriteDate, WriteDateTime

procedure WriteDate(const Name: String; Value: TDateTime); procedure WriteDateTime(const Name: String; Value: TDateTime); Wywoanie pierwszej funkcji spowoduje utworzenie w Rejestrze klucza zawierajcego jedynie dat, a drugiej (WriteDateTime) ? zapisanie daty oraz czasu.

WriteCurrency

procedure WriteCurrency(const Name: String; Value: Currency); Procedura WriteCurrency powoduje zapisanie w Rejestrze wartoci zmiennoprzecinkowej typu Currency.

WriteBool

procedure WriteBool(const Name: String; Value: Boolean); Jeeli chcesz zapisa w Rejestrze warto typu Boolean (True lub False), skorzystaj z procedury
WriteBool.

WriteBinaryData

procedure WriteBinaryData(const Name: String; var Buffer; BufSize: Integer); Procedura WriteBinaryData umoliwia zapisanie w Rejestrze wartoci nieokrelonej. Drugi parametr musi zawiera wskazanie zmiennej (bufor), a ostatni parametr okrela rozmiar bufora. Maksymalna wielko danych, jakie mog by zapisane w Rejestrze, to 2 048 bajtw. Przykad uycia: var Buffer : array[0..255] of char; ... Buffer := 'Jaki tekst moe by lub co innego'; Reg.WriteBinaryData(?Nazwa?, Buffer, SizeOf(Buffer)); ... 246 | S t r o n a

Odczyt danych
Wiadomo, e zapisujemy dane rwnie czsto, jak je odczytujemy. Odczytywanie i zapisywanie danych jest bardzo podobne ? rni si jedynie nazwy polece. W trakcie odczytywania danych stosujemy polecenia ReadXXX, gdzie XXX jest nazw typu do odczytania. Kolejna rnica jest taka, e polecenia z rodziny ReadXXX to funkcje, a nie procedury ? jak to miao miejsce podczas zapisywania danych. Posumy si przykadem z poprzedniego podpunktu. Tam program zapisywa w Rejestrze par informacji, takich jak imi, nazwisko itp. Poniszy przykad ilustruje sposb odczytu tych danych: procedure TMainForm.FormCreate(Sender: TObject); var Reg : TRegistry; begin Reg := TRegistry.Create; if Reg.KeyExists('Moja aplikacja 1.0') then begin Reg.OpenKey('Moja aplikacja 1.0', False); edtFName.Text := Reg.ReadString('FName'); edtSName.Text := Reg.ReadString('SName'); edtCity.Text := Reg.ReadString('City'); edtCountry.Text := Reg.ReadString('Country'); edtPesel.Text := IntToStr(Reg.ReadInteger('Pesel')); end; Reg.Free; end;

Kod jest umieszczony w zdarzeniu OnCreate, co spowoduje odczytanie danych zaraz po uruchomieniu aplikacji. Zauwa, e przed samym procesem odczytu nastpuje sprawdzanie, czy taki klucz istnieje (funkcja KeyExists). Moe si bowiem zdarzy tak, e program jest uruchamiany po raz pierwszy i taki klucz nie istnieje. Listing 6.1 przedstawia cay kod programu, a na rysunku 6.4 zaprezentowano program w trakcie dziaania. Listing 6.1. Kod rdowy moduu unit MainFrm; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, Registry, StdCtrls; 247 | S t r o n a

type TMainForm = class(TForm) btnSave: TButton; gbInfo: TGroupBox; lblFName: TLabel; lblSName: TLabel; lblCity: TLabel; lblCountry: TLabel; lblPesel: TLabel; edtFName: TEdit; edtSName: TEdit; edtCity: TEdit; edtCountry: TEdit; edtPesel: TEdit; procedure btnSaveClick(Sender: TObject); procedure FormCreate(Sender: TObject); private { Private declarations } public { Public declarations } end; var MainForm: TMainForm; implementation {$R *.dfm} procedure TMainForm.btnSaveClick(Sender: TObject); var Reg : TRegistry; begin Reg := TRegistry.Create; if Reg.OpenKey('Moja aplikacja 1.0', True) then ShowMessage('Klucz otwarty!') else ShowMessage('Bd!'); Reg.WriteString('FName', edtFName.Text); Reg.WriteString('SName', edtSName.Text); Reg.WriteString('City', edtCity.Text); Reg.WriteString('Country', edtCountry.Text); Reg.WriteInteger('Pesel', StrToInt(edtPesel.Text)); Reg.Free; end; procedure TMainForm.FormCreate(Sender: TObject); var 248 | S t r o n a

Reg : TRegistry; begin Reg := TRegistry.Create; if Reg.KeyExists('Moja aplikacja 1.0') then begin Reg.OpenKey('Moja aplikacja 1.0', False); edtFName.Text := Reg.ReadString('FName'); edtSName.Text := Reg.ReadString('SName'); edtCity.Text := Reg.ReadString('City'); edtCountry.Text := Reg.ReadString('Country'); edtPesel.Text := IntToStr(Reg.ReadInteger('Pesel')); end; Reg.Free; end; end.

Rysunek 6.4. Program w trakcie dziaania Nie bd marnowa ju papieru na opis wszystkich funkcji sucych do odczytu danych z Rejestru, gdy ich zastosowanie i nazwy mona odkry intuicyjne. W poprzednim podpunkcie przedstawiem wszystkie procedury do zapisu danych ? jeeli chcesz odczyta dane, wystarczy w nazwach tych procedur zamieni fragment Write na Read.

249 | S t r o n a

Inne funkcje operujce na Rejestrze


Przedstawione dotychczas w tym rozdziale funkcje nie s jedynymi, ktre mona zastosowa wraz z Rejestrem. Poniej przedstawiam opis i deklaracje pozostaych.

DeleteValue function DeleteValue(const Name: String): Boolean;

Do kasowania caych kluczy mona uy funkcji DeleteKey, natomiast aby usun poszczeglne wartoci z tych kluczy, naley zastosowa funkcj DeleteValue. Parametr Name jest nazw wartoci, ktra ma zosta skasowana. Funkcja zwraca True, jeeli operacja si powiedzie.

GetDataInfo function GetDataInfo(const ValueName: String; var Value: TRegDataInfo): Boolean;

Funkcja GetDataInfo dostarcza informacji na temat danych umieszczonych w rejestrze. Pierwszym parametrem musi by nazwa wartoci, natomiast drugi parametr musi by wskazaniem na struktur TRegDataInfo. type TRegDataInfo = record RegData: TRegDataType; DataSize: Integer; end;

Po wywoaniu funkcji GetDataInfo rekord TRegDataInfo zawiera bdzie informacje na temat rodzaju wartoci (parametr RegData) oraz rozmiaru wartoci (parametr DataSize). W tabeli 6.1 przedstawiono moliwe wartoci parametru RegData. Tabela 6.1. Moliwa zawarto typu TRegDataType Nazwa rdUnknown rdString Opis Warto rejestru nie moe zosta zidentyfikowana Warto tekstowa

rdExpandString Warto tekstowa z zerowym ogranicznikiem rdInteger rdBinary 32-bitowa warto Integer Warto binarna

250 | S t r o n a

Przykad uycia: uses Registry; procedure TForm1.FormCreate(Sender: TObject); var Reg : TRegistry; RegInfo : TRegDataInfo; begin Reg := TRegistry.Create; try Reg.OpenKey('SOFTWARE\b4p', False); Reg.GetDataInfo('App Dir', RegInfo); case RegInfo.RegData of rdUnknown: ShowMessage('Niezidentyfikowana warto'); rdString: ShowMessage('Warto tekstowa!'); rdExpandString: ShowMessage('Warto tekstowa z zerowym ogranicznikiem'); rdInteger: ShowMessage('Warto Integer'); rdBinary: ShowMessage('Warto binarna'); end; finally Reg.Free; end; end;

GetDataSize function GetDataSize(const ValueName: String): Integer;

Funkcja posiada tylko cz moliwoci polecenia GetDataType. Zwraca bowiem rozmiar konkretnej wartoci Rejestru, podawany w bajtach.

GetDataType function GetDataType(const ValueName: String): TRegDataType;

Rezultat wykonania tej funkcji zawiera rodzaj wartoci Rejestru. Funkcja moe zwrci takie dane, jakie zostay przedstawione w tabeli 6.1.

GetKeyInfo function GetKeyInfo(var Value: TRegKeyInfo): Boolean;

Dziki tej funkcji moesz dowiedzie si czego wicej o konkretnym kluczu. Wszystko to za spraw rekordu TRegKeyInfo.

251 | S t r o n a

type TRegKeyInfo = record NumSubKeys: Integer; MaxSubKeyLen: Integer; NumValues: Integer; MaxValueLen: Integer; MaxDataLen: Integer; FileTime: TFileTime; end;

Rekord dostarcza nastpujcych informacji:


NumSubKeys ? ilo podkluczy w danym kluczu. Wiadomo, e okrelony klucz Rejestru moe posiada wiele innych kluczy. Ten parametr dostarcza informacji na ten temat. NumSubKeyLen ? dany klucz moe zawiera wiele podkluczy o okrelonych dugociach. NumSubKeyLen to dugo (w znakach) najduszego z podkluczy. NumValues ? ilo wartoci w danym kluczu. MaxValueLen ? dugo (w znakach) najduszej wartoci z danego klucza. MaxDataLen ? maksymalna wielko konkretnej wartoci w kluczu (w bajtach). FileTime ? czas ostatniego dostpu do klucza.

GetKeyNames procedure GetKeyNames(Strings: TStrings);

Dziki procedurze GetKeyNames moemy pozna nazwy wszystkich kluczy znajdujcych si w danym, otwartym przez nas kluczu. Wszystkie nazwy zostan umieszczone w zmiennej typu TStrings. Miae ju okazj zapozna si z klas TStrings w rozdziale 2. Klasa ta umoliwia do prosty dostp do plikw tekstowych i, oglnie, do tekstu. Oto przykad: procedure TForm1.FormCreate(Sender: TObject); var Reg : TRegistry; KeyNames : TStringList; begin Reg := TRegistry.Create; KeyNames := TStringList.Create; try Reg.OpenKey('Software', False); Reg.GetKeyNames(KeyNames); ShowMessage(KeyNames[0]); finally Reg.Free; KeyNames.Free; // nie zapomnij o zwolnieniu! end; end; 252 | S t r o n a

Po uruchomieniu programu zmienna KeyNames zawiera nazwy wszystkich kluczy z klucza gwnego ? Software. Ich nazwy mona pozna za pomoc nawiasw kwadratowych: KeyNames[0]; // odczytaj pierwszy element

GetValueNames procedure GetValueNames(Strings: TStrings);

Z funkcji GetValueNames korzysta si podobnie jak z GetKeyNames. Jedyna rnica to rezultat wykonania owej funkcji. W poprzednim przykadzie uycie procedury GetKeyNames spowodowao odczyt wszystkich nazw kluczy. Procedura GetValueNames powoduje natomiast odczytanie nazw wszystkich wartoci znajdujcych si w danym kluczu.

HasSubKeys function HasSubKeys: Boolean;

Funkcja HasSubKeys zwraca True, jeeli dany klucz posiada podklucze.

KeyExists function KeyExists(const Key: String): Boolean;

Funkcja KeyExists sprawdza, czy dany klucz, okrelony w parametrze Key, istnieje. Jeeli nie, funkcja zwraca warto False.

LoadKey function LoadKey(const Key, FileName: String): Boolean;

Pamitasz, jak na samym pocztku rozdziau mwiem o moliwoci wyeksportowania zawartoci Rejestru do pliku *.reg? Dziki funkcji LoadKey istnieje moliwo wczytania zawartoci pliku (okrelanego poprzez parametr FileName) i dodania go do klucza okrelonego poprzez parametr Key.

SaveKey function SaveKey(const Key, FileName: String): Boolean;

SaveKey zapisuje zawarto konkretnego klucza Rejestru w pliku okrelonym parametrem FileName.

253 | S t r o n a

MoveKey procedure MoveKey(const OldName, NewName: String; Delete: Boolean);

W celu przeniesienia danego klucza do innego moesz skorzysta z funkcji MoveKey. Pierwszy parametr stanowi dotychczasow ciek klucza; parametr NewName to nowa cieka, a ostatni parametr stanowi informacj o tym, czy klucz ma zosta jedynie przeniesiony (True) czy skopiowany (False).

RootKey

Waciwo RootKey (jest to waciwo, a nie metoda!) umoliwia przypisanie wartoci klucza gwnego, ktrego bd dotyczy operacje: ... Reg.RootKey := HKEY_CLASSES_ROOT; ...

Wykonanie powyszego kodu spowoduje, e wszelkie operacje na Rejestrze bd zwizane z kluczem gwnym ? HKEY_CLASSES_ROOT.

Praktyczny przykad
Aby utrwali wiadomoci dotyczce Rejestru, proponuj wykonanie maego wiczenia. Program bdzie dodawa odpowiedni klucz do Rejestru Windows, dziki czemu za kadym uruchomieniem systemu uruchamiany bdzie take nasz program. W tym celu naley doda odpowiedni warto do klucza HKEY_CURRENT_USER\SOFTWARE\Microsoft\Windows\CurrentVersion\Run. W kluczu tym musi si znale jaka warto okrelajca ciek do naszej aplikacji. Przypominam, e ciek do aplikacji zwraca funkcja ExeName z klasy TApplication. Nazwa klucza moe by dowolna, byleby warto wskazywaa na ciek. 1. 2. 3. 4. Utwrz nowy projekt. Dodaj na formularzu przycisk i zmie jego waciwo Name na btnDelete. Wygeneruj zdarzenie OnClick przycisku. Wygeneruj zdarzenie OnCreate formularza.

Na formularzu znajdzie si przycisk, po ktrego naciniciu odpowiedni klucz zostanie usunity z Rejestru (jeeli np. uytkownik nie chce ju, aby program by uruchamiany po starcie systemu). Kod rdowy programu znajduje si w listingu 6.2. 254 | S t r o n a

Listing 6.2. Kod rdowy moduu unit MainFrm; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls, Registry; type TMainForm = class(TForm) btnDelete: TButton; procedure FormCreate(Sender: TObject); procedure btnDeleteClick(Sender: TObject); private { Private declarations } public { Public declarations } end; var MainForm: TMainForm; implementation {$R *.dfm} procedure TMainForm.FormCreate(Sender: TObject); var Reg : TRegistry; begin Reg := TRegistry.Create; try { otwarcie klucza } Reg.RootKey := HKEY_CURRENT_USER; Reg.OpenKey('SOFTWARE\Microsoft\Windows\CurrentVersion\Run', False); if not Reg.ValueExists('MyApp') then begin Reg.WriteString('MyApp', Application.ExeName); Application.MessageBox('Od tej pory program bdzie uruchamiany po starcie systemu!', ':-)', MB_OK + MB_ICONINFORMATION); end; finally Reg.Free; 255 | S t r o n a

end; end; procedure TMainForm.btnDeleteClick(Sender: TObject); var Reg : TRegistry; begin Reg := TRegistry.Create; try { otwarcie klucza } Reg.RootKey := HKEY_CURRENT_USER; Reg.OpenKey('SOFTWARE\Microsoft\Windows\CurrentVersion\Run', False); Reg.DeleteValue('MyApp'); finally Reg.Free; end; end; end.

Peny kod rdowy powyszego programu znajduje si na pycie CD-ROM w katalogu ..listingi/6/Autostart.

Klasa TINIFile
W module IniFiles.pas znajduje si klasa TINIFile, z ktrej bdziemy korzysta podczas wykonywania operacji na plikach INI. Na szczcie nie musisz od nowa uczy si nazw polece, gdy w wikszoci s one takie same, jak w przypadku klasy TRegistry. Z tego te powodu nie bd rozpisywa si zbytnio na temat funkcji klasy TINIFile.

Tworzenie nowego pliku INI


Rozpoczcie pracy z plikami INI musi wiza si z zainicjowaniem klasy TINIFile (wywoaniem konstruktora). Konstruktor tej klasy wymaga jednego parametru ? cieki do pliku INI. Nie musisz przejmowa si tym, e np. w plik INI nie istnieje ? w razie potrzeby zostanie stworzony przez Delphi. uses INIFiles; procedure TForm1.Button1Click(Sender: TObject); var INI : TIniFile; 256 | S t r o n a

begin INI := TINIFile.Create('C:\moj.ini'); try finally INI.Free; end; end;

Powyszy kod spowoduje zainicjowanie klasy TINIFile. W parametrze podaem ciek do nieistniejcego pliku C:\moj.ini. Wykonanie takiego kodu nie spowoduje jednak utworzenia nowego pliku, gdy nie zostan znalezione funkcje zapisujce co w pliku INI. Podczas wywoywania konstruktora klasy TINIFile naley poda pen ciek do pliku. W przeciwnym wypadku system utworzy plik w katalogu Windows.

Zapisywanie danych
Jak mwiem wczeniej, uycie metod z klasy TINIFile jest bardzo podobne do zasad obowizujcych w klasie TRegistry, a nazwy metod s wrcz identyczne. Rnica dotyczy za to liczby parametrw. Przypominam, e pliki INI dodatkowo skadaj si z sekcji, tak wic podczas kadej operacji na tych plikach (zapis, odczyt) naley poda take nazw sekcji (w pierwszym parametrze). uses INIFiles; procedure TForm1.Button1Click(Sender: TObject); var INI : TIniFile; begin INI := TINIFile.Create('C:\moj.ini'); try INI.WriteString('Main', 'Klucz', 'Warto'); finally INI.Free; end; end;

Wykonanie powyszego kodu spowoduje utworzenie nowej sekcji ? Main (jeeli oczywicie dana sekcja nie istnieje), a w niej klucza o nazwie Klucz z wartoci Warto. Teraz jeeli plik C:\moj.ini nie istnieje, zostanie utworzony, a jego zawarto bdzie si przedstawia tak:
[Main] Klucz=Warto

257 | S t r o n a

Odczyt danych
Funkcje odczytujce dane z pliku INI take zawieraj po trzy parametry ? przykadowa deklaracja funkcji ReadString wyglda tak: function ReadString(const Section, Ident, Default: String): String; override;

Pierwszy parametr stanowi nazw sekcji, drugi ? nazw klucza, a trzeci ? dla odmiany ? warto domyln. Warto domylna stosowana jest na wypadek, gdyby dany plik INI nie istnia lub dany klucz (lub sekcja) nie znajdowa si w tym pliku. Wwczas funkcja ReadString zwraca warto okrelon w trzecim parametrze. uses INIFiles; procedure TForm1.Button1Click(Sender: TObject); var INI : TIniFile; begin INI := TINIFile.Create('C:\moj.ini'); try ShowMessage( INI.ReadString('Main', 'Klucz', 'Warto') ); finally INI.Free; end; end;

Funkcje zwizane z operacjami na sekcjach


Jak wspominaem na pocztku, pliki INI s podzielone na sekcje. W tym podpunkcie zajmiemy si omawianiem funkcji, ktrych brakuje w klasie TRegistry ? czyli funkcji, ktre operuj na sekcjach plikw INI. Podstawowe funkcje to: ReadSection, ReadSections, ReadSectionValues oraz EraseSection.

ReadSection procedure ReadSection (const Section: String; Strings: TStrings); override;

Procedura ReadSection odczytuje wszystkie klucze znajdujce si w danej sekcji. Pierwszy parametr musi by nazw sekcji, a kolejny ? wskazaniem na zmienn typu TStrings. 258 | S t r o n a

procedure TForm1.Button1Click(Sender: TObject); var INI : TINIFile; Keys : TStringList; begin INI := TINIFile.Create('C:\moj.ini'); Keys := TStringList.Create; try INI.ReadSection('Main', Keys); { Zmienna Keys zawiera teraz nazwy wszystkich kluczy w sekcji } finally Keys.Free; INI.Free; end; end;

ReadSections procedure ReadSections(Strings: TStrings); virtual; abstract;

Czasem zachodzi potrzeba odczytu wszystkich sekcji znajdujcych si w pliku INI. Wwczas z pomoc przychodzi procedura ReadSections. Procedura ta wczytuje do parametru Strings wszystkie nazwy sekcji z danego pliku INI. Korzystanie z niej jest podobne do przypadku ReadSection.

ReadSectionValues procedure ReadSectionValues(const Section: String; Strings: TStrings); override;

Chcc odczyta wartoci znajdujce si w danej sekcji, naley skorzysta z procedury


ReadSectionValues. Pierwszy parametr musi by nazw sekcji, a drugi wskazaniem na zmienn typu TStrings. Po uruchomieniu programu do parametru Strings przypisane zostan nazwy

wszystkich kluczy oraz wartoci z danej sekcji. Klucze s poczone z wartociami znakiem =, wic pozostaje jeszcze sprawa oddzielenia tych dwch pozycji. Klasa TStrings posiada przydatne waciwoci, ktre pomog Ci rozdzieli klucz od wartoci. Przede wszystkim jest to waciwo NameValueSeparator. Nadanie jej wartoci ?=? spowoduje oddzielenie klucza od wartoci i przypisanie ich do odpowiednich waciwoci: Names oraz Values. Do konkretnej wartoci naley odwoa si za pomoc nawiasw kwadratowych. EraseSection procedure EraseSection(const Section: String); override;

Skoro istniej funkcje suce do odczytywania zawartoci sekcji, to musi istnie take procedura usuwajca cae sekcje. Do takich funkcji naley EraseSection. Wystarczy w parametrze Section poda nazw sekcji, aby wszystkie klucze w niej si znajdujce zostay skasowane.

259 | S t r o n a

Przykadowy program
Aby mg utrwali nieco wiadomoci na temat plikw INI, pragn zaprezentowa Ci prost aplikacj dziaajc na plikach INI. Program bdzie realizowa logowanie. Po uruchomieniu zostanie wywietlone mae okienko, w ktrym uytkownik powinien wpisa swj pseudonim. Po kolejnym uruchomieniu programu uytkownik zostanie wykryty (rysunek 6.5).

Rysunek 6.5. Etykieta powitalna Cay program skada si praktycznie z jednej tylko procedury (zdarzenia) OnCreate: procedure TMainForm.FormCreate(Sender: TObject); var INI : TINIFile; Login : String; // login wczytany z pliku INI begin { na samym pocztku naley sprawdzi, czy plik jest utworzony } if not FileExists(ExtractFilePath(Application.ExeName) + 'setup.ini') then begin { jeeli nie mona znale pliku, wywietl okienko z prob o wpisanie swojego loginu } Login := InputBox('Rejestracja...', 'Podaj login', ''); if Login <> '' then // sprawd, czy login nie jest pusty begin { jeeli tak nie jest ? utwrz plik } INI := TINIFile.Create(ExtractFilePath(Application.ExeName) + 'setup.ini'); try INI.WriteString('Main', 'Login', Login); // zapisz do pliku login lblMain.Caption := 'Witaj ' + Login; finally INI.Free; end; Exit; // nie rb ju nic... end else Application.Terminate; // jeeli uytkownik nie wpisa loginu ? zakocz aplikacje end; { ten kod zostanie wykonany tylko wtedy, gdy plik INI zosta znaleziony } 260 | S t r o n a

INI := TINIFile.Create(ExtractFilePath(Application.ExeName) + 'setup.ini'); try Login := INI.ReadString('Main', 'Login', 'Adam Boduch'); // nastpuje odczyt loginu lblMain.Caption := 'Witaj ' + Login; // nastpnie wywietl login na komponencie finally INI.Free; end; end;

Na samym pocztku program sprawdza, czy w katalogu znajduje si plik setup.ini. Jeeli tak, sprawa jest prosta: naley odczyta warto klucza z pliku INI i wywietli j w etykiecie. Jeeli natomiast plik nie istnieje, naley wywietli okienko, w ktrym uytkownik bdzie mg poda swj pseudonim. Korzystamy w tym momencie z funkcji InputBox, ktra znajduje si w module Dialogs.pas (rysunek 6.6). Przy wywoywaniu polecenia InputBox naley poda trzy parametry typu String. Pierwszy to napis na pasku tytuowym okienka, drugi to tekst etykiety, natomiast ostatni parametr to domylna warto w polu typu TEdit.

Rysunek 6.6. Okienko wywoywane za porednictwem funkcji InputBox

Podsumowanie
Jestem przekonany, e tworzc bardziej zaawansowane programy, bdziesz zmuszony do korzystania z plikw INI lub z Rejestru. Nie jest to w sumie takie trudne, a pozwala na efektywne przechowywanie (zachowanie) ustawie programu. Po przeczytaniu tego rozdziau powiniene mie ju jak wiedz na temat Rejestru czy plikw INI ? w razie czego zawsze moesz sign po ten podrcznik i odwiey sobie pami. Zaczniki:

Listingi_6.zip (212.51 kB)

261 | S t r o n a

Rozdzia 7
Obsuga plikw

Czym jest plik? Tego chyba nie trzeba wyjania adnemu uytkownikowi komputera. Istnieje kilka rodzajw plikw: tekstowe, binarne, typowane itp. Pliki s wykorzystywane przez programy do pobierania lub przechowywania informacji; mog te zawiera binarne fragmenty programu. Ten rozdzia bdzie powicony plikom i ich obsudze w Delphi. Zaczniemy od rzeczy najprostszych, przechodzc do coraz bardziej zaawansowanych aspektw. Nie bdzie to jednak co, co przysporzy Ci blu gowy ? obsuga plikw nie jest trudna. Wystarczy tylko zna kilka podstawowych polece.

Pliki tekstowe
Podstawowym rodzajem plikw s pliki tekstowe, ktrych budowa jest bardzo prosta. Pliki tekstowe zawieraj wycznie tekst, ktrego kolejne wiersze s oddzielone znakami nowego wiersza.

Inicjalizacja
Zanim stworzymy nowy plik lub otworzymy ju istniejcy, wymagana jest jego inicjalizacja. W rzeczywistoci jest to przypisanie konkretnego pliku do jakiej zmiennej, a realizuje to polecenie AssignFile. Wyglda to tak: var TF : TextFile; begin AssignFile(TF, ?C:\plik.txt?); { dalsze operacje } end;

Wywoanie procedury wbudowanej AssignFile powoduje skojarzenie zmiennej tekstowej ? TX z plikiem C:\plik.txt. Taka konstrukcja jest wymagana, aby rozpocz dalsz prac z plikiem. Pierwszy parametr musi by zmienn typu TextFile, a kolejny to cieka pliku. Funkcja AssignFile, jak i inne tego typu nale do funkcji wbudowanych. Oznacza to, e ich deklaracja znajduje si w module System, ktry jest automatycznie wczany do kadego projektu.

262 | S t r o n a

Tworzenie nowego pliku


Za tworzenie nieistniejcego wczeniej pliku odpowiada funkcja Rewrite. Jest to rwnie funkcja systemowa, ktrej nagwek przedstawia si nastpujco: procedure Rewrite(var F: File [; Recsize: Word ] );

Pierwszym parametrem musi by nazwa zmiennej plikowej ? w naszym przypadku bdzie to TextFile. Drugim parametrem na razie si nie przejmuj, gdy jest on opcjonalny. Tak si skada, e jedna funkcja (w tym wypadku ? Rewrite) moe by wykorzystywana do otwierania kilku rodzajw plikw ? w takiej wanie sytuacji jest wykorzystywany ten drugi parametr. Oto przykad utworzenia nowego pliku tekstowego: procedure TMainForm.btnRewriteClick(Sender: TObject); var TF : TextFile; begin AssignFile(TF, 'C:\plik.txt'); try Rewrite(TF); finally CloseFile(TF); end; end;

Na wszelki wypadek cay kod umieciem w bloku try..finally. Dziki temu zawsze ? bez wzgldu na to, czy podczas tworzenia pliku wystpi jaki bd ? wykonana zostanie instrukcja CloseFile, zwalniajca zmienn TF. Listing powyszego programu moesz znale na doczonej do ksiki pycie CD-ROM w katalogu ..listingi/7/Create File.

Otwieranie istniejcego pliku


Jeeli jaki plik tekstowy ju istnieje, to w celu dalszej edycji naley go otworzy poleceniem Reset. procedure Reset(var F [: File; RecSize: Word ] );

Pierwszy parametr to ? tak samo jak w przypadku polecenia Rewrite ? nazwa zmiennej typu TextFile. Drugi parametr jest parametrem opcjonalnym, identycznie jak w przypadku funkcji Rewrite.

263 | S t r o n a

procedure TMainForm.btnResetClick(Sender: TObject); var TF : TextFile; begin AssignFile(TF, 'C:\plik2.txt'); try Reset(TF); finally CloseFile(TF); end; end;

Naley uwaa na to, czy plik, ktry prbujemy otworzy, istnieje. W przeciwnym wypadku program wygeneruje wyjtek EFileNotFound, co zazwyczaj skoczy si wywietleniem komunikatu o bdzie. W takiej sytuacji przydaje si funkcja FileExists, ktra sprawdza istnienie podanego w parametrze pliku. if FileExists('C:\plik.txt') Reset(TF) else Rewrite(TF);

Jeeli plik istnieje, zostanie otwarty, a w przeciwnym wypadku ? utworzony.

Odczyt plikw tekstowych


Pamitasz, jak we wczeniejszych fragmentach ksiki (a dokadnie w rozdziale drugim) wspominaem o funkcjach Read i Readln? Te dwie funkcje su take do odczytywania informacji z plikw tekstowych. Spjrz na poniszy fragment kodu: procedure TMainForm.btnResetClick(Sender: TObject); var TF : TextFile; S : String; // zmienna tymczasowa begin AssignFile(TF, 'C:\plik.txt'); try Reset(TF); { ptla odczytuje kolejne wiersze pliku tekstowego } while not Eof(TF) do begin Readln(TF, S); // odczytanie wierszy i przypisanie zawartoci do zmiennej S memFile.Lines.Add(S); end; finally CloseFile(TF); end; end; end. 264 | S t r o n a

Oprcz uycia standardowych procedur Read i Readln skorzystaem take z funkcji Eof. Funkcja Eof informuje o napotkaniu koca pliku podczas odczytywania jego zawartoci. A zatem ptla w powyszym kodzie bdzie wykonywana, dopki nie zostan odczytane wszystkie wiersze pliku tekstowego. Pierwszym parametrem procedury Readln musi by nazwa zmiennej typu TextFile. Drugi parametr to zmienna typu String, do ktrej przypisana zostanie zawarto wiersza. Po uruchomieniu programu zostanie wywietlone okno przedstawione na rysunku 7.1.

Rysunek 7.1. Zawarto pliku wczytana do komponentu TMemo Prawdopodobnie rzadko bdziesz uywa takiego zapisu, gdy VCL posiada wasne funkcje suce do przetwarzania plikw tekstowych. Zamiast stosowa ten ? do skomplikowany ? zapis mona wykorzysta taki sposb: Memo.Lines.LoadFromFile('C:\plik.txt');

Okazuje si, e jednym wierszem kodu mona zastpi szereg wbudowanych instrukcji jzyka Object Pascal.

Zapis nowych danych w pliku


Gdy chcemy zapisa nowe dane w pliku, naley skorzysta z funkcji Writeln i Write. Wspominaem o nich ju w rozdziale drugim, ale tym razem ich uycie ? cho podobne ? nieznacznie si rni. Pierwszy parametr musi by wskazaniem na zmienn typu TextFile, natomiast parametr drugi to zmienna typu String. Writeln(TF, S);

265 | S t r o n a

Oto przykad na udoskonalenie programu, ktry zaprezentowany zosta w poprzednim podpunkcie. Tym razem procedura umoliwia zapis danych z komponentu TMemo: procedure TMainForm.btnSaveClick(Sender: TObject); var TF : TextFile; i : Integer; begin AssignFile(TF, 'C:\plik.txt'); try Rewrite(TF); for I := 0 to memFile.Lines.Count ?1 do Writeln(TF, memFile.Lines[i]); finally CloseFile(TF); end; end;

Powyszy kod powoduje zapisanie wszystkich wierszy z komponentu TMemo do pliku tekstowego. Waciwo Count klasy TStrings (waciwo Lines komponentu TMemo jest typu TStrings! Tak, cae VCL jest ze sob poczone!) zwraca ilo wierszy znajdujcych si w komponencie. Zamiast instrukcji Writeln moesz zastosowa take Write. Rnica pomidzy tymi dwoma poleceniami polega na tym, e to drugie polecenie nie dodaje na kocu tekstu znaku nowego wiersza. W konsekwencji tekst nie bdzie podzielony na wiersze, ale wszystko zostanie zapisane w jednym cigu.

Zapis danych na kocu pliku


Niekiedy przytrafia si sytuacja, w ktrej chcemy zapisa jakie dane do pliku, ale umieszczajc je tylko na jego kocu. W takim wypadku przydatna staje si funkcja Append. Za jej pomoc moliwe jest otwarcie pliku i ustawienie punktu zapisu na jego kocu. W praktyce wyglda to tak: procedure TMainForm.btnAppendClick(Sender: TObject); var TF : TextFile; begin AssignFile(TF, 'C:\plik.txt'); try Append(TF); Writeln(TF, edtText.Text); finally CloseFile(TF); end; end; Polecenie Append jednoczenie powoduje otwarcie pliku ? nie jest wic konieczne wczeniejsze zastosowanie procedury Reset. Nie istniej funkcje umoliwiajce przemieszczanie si po plikach tekstowych w przd lub w ty. 266 | S t r o n a

Pliki amorficzne
W tym rozdziale plikami amorficznymi bd nazywa pliki o nieregularnej budowie, czyli pliki binarne. Napisaem nieregularnej, gdy pliki amorficzne nie maj okrelonej budowy, a ich poszczeglne wiersze nie s zakoczone znakiem nowego wiersza czy jakim innym specyficznym znakiem. Obsuga plikw binarnych moe si przyda do odczytu fragmentw danych, ich zapisu lub skopiowania do innego pliku. Przykadem moe by odczyt czci danych z pliku mp3 (tzw. tag ? informacja o wykonawcy, tytule piosenki itp.). Obsuga tych operacji z poziomu Delphi jest raczej prosta; nazwy funkcji s takie same, jak w przypadku plikw testowych, cho istniej polecenia specyficzne wanie dla plikw amorficznych.

Otwieranie i zamykanie plikw


Otwieranie oraz zamykanie pliku amorficznego realizowane jest take przez funkcje AssignFile, Reset, CloseFile lub Rewrite. Polecenie Append, prezentowane w poprzednim podpunkcie, moe by uywane jedynie w kontekcie plikw tekstowych. Oto przykadowe otwarcie i zamknicie wybranego przez uytkownika pliku: procedure TMainForm.btnOpenClick(Sender: TObject); var F : File; begin { jeeli otwarte zostanie okno i wybrany plik } if OpenDialog.Execute then begin AssignFile(F, OpenDialog.FileName); try try Reset(F, 1); // otwrz plik ShowMessage('Plik zosta otwarty'); except { jeeli wystpi bd ? wywietl wyjtek } raise; end; finally CloseFile(F); // zamknij plik end; end; end;

267 | S t r o n a

W odniesieniu do plikw amorficznych naley skorzysta ze zmiennej typu File, a nie ? jak w poprzednich przykadach ? TextFile. Procedura Reset z poprzedniego przykadu posiada drugi parametr (opcjonalny), ktry okrela wielko rekordu uywanego podczas operacji na plikach. Drugi parametr ? Reset ? ma znaczenie jedynie w kontekcie plikw amorficznych. W przypadku, gdy parametr ten jest pusty, Delphi za domyln warto uzna 128, co moe spowodowa nieprawidowe dziaanie programu. Dlatego te jako bezpieczn warto naley w tym miejscu podawa cyfr 1.

Tryb otwarcia pliku


Jzyk Object Pascal zawiera zmienn globaln FileMode, ktra definiuje sposb zapisu lub odczytu danych. Mwic innymi sowami, okrela ona prawa dostpu do pliku. Domylna warto tej zmiennej to fmOpenReadWrite, czyli moliwo zarwno odczytu, jak i zapisu danych w pliku. Warto pozna t zmienn ju teraz, gdy wiedza ta przyda si w dalszej czci rozdziau, gdy bdziemy mwili o strumieniach. Zmienna FileMode okrela sposb dostpu do plikw amorficznych ? nie tekstowych! Tabela 7.1 zawiera wartoci, ktre mog by przypisane do zmiennej FileMode lub ktre bd wykorzystywane podczas omawiania strumieni. Tabela 7.1. Wartoci okrelajce dostp do pliku Warto fmCreate fmOpenRead fmOpenWrite Opis Jeeli plik nie istnieje ? zostanie utworzony; w przeciwnym wypadku zostanie otwarty Plik zostanie otwarty jedynie do odczytu Plik zostanie otwarty do zapisu

fmOpenReadWrite Plik zostanie otwarty zarwno do zapisu, jak i do odczytu fmShareExclusive Dostp do pliku jest niemoliwy z poziomu innych programw fmShareDenyWrite Zapis z poziomu innych programw jest zabroniony fmShareDenyRead Odczyt z poziomu innych programw jest zabroniony fmShareDenyNone Peny dostp do pliku dla innych aplikacji

268 | S t r o n a

Zapis i odczyt danych


W celu odczytania jakiej iloci danych lub zapisania okrelonej porcji informacji naley skorzysta z funkcji BlockWrite i BlockRead. Obie funkcje wymagaj podania bufora, czyli danych, ktre planujemy zapisa w pliku. Spjrz na poniszy fragment kodu: procedure TForm1.Button1Click(Sender: TObject); var F : File; Buffer : array[0..255] of char; begin AssignFile(F, 'C:\dane.txt'); try Rewrite(F, 1); Buffer := 'Jakie dane...'; BlockWrite(F, Buffer, SizeOf(Buffer)); finally CloseFile(F); end; end;

W procedurze zadeklarowane zostay dwie zmienne: F, ktra okrela plik amorficzny, oraz tablica Buffer (255 elementowa). Utworzenie pliku przebiega standardowo (Rewrite). Nastpnie do tablicy Buffer (czyli naszego bufora) zostaj przypisane dane. Kolejnym krokiem jest zapisanie danych w pliku za pomoc procedury BlockWrite. Pierwszym parametrem owej procedury musi by nazwa zmiennej typu File. Kolejny parametr to nazwa bufora, a trzeci to rozmiar danych zapisywanych w pliku. Funkcja SizeOf zwraca rzeczywisty rozmiar, w tym wypadku ? tablicy. Po uruchomieniu programu na dysku powstanie plik ? na pozr ? tekstowy. Sprawd jednak jego rozmiar. Okae si, e niepozorny plik tekstowy zajmuje a 256 bajtw! Jest to spowodowane tym, e tablica Buffer zajmuje 256 bajtw (pamitaj ? 0 te jest elementem tablicy).

Przykad dziaania ? kopiowanie plikw


Kopiowanie w gruncie rzeczy opiera si na pobieraniu jakiego fragmentu pliku i dodawaniu tego fragmentu to pliku drugiego. Procedury BlockWrite oraz BlockRead posiadaj jeszcze jeden, opcjonalny parametr, ktry okrela, ile rzeczywicie bajtw zostao odpowiednio odczytanych lub zapisanych. Ten parametr przyda nam si podczas projektowania procedury kopiujcej plik. 269 | S t r o n a

Wygld przykadowego programu sucego do kopiowania przedstawiony zosta na rysunku 7.2.

Rysunek 7.2. Wygld programu Po naciniciu przycisku otwarte zostanie okno, w ktrym uytkownik bdzie musia wskaza ciek do pliku, z ktrego zostanie utworzona kopia (na dysku C:\). Przebieg procesu kopiowania pliku zostanie przedstawiony na pasku postpu (komponent TProgressBar). Kod rdowy programu przedstawiony jest na listingu 7.1. Po zapoznaniu si z nim przeczytaj zamieszczone poniej omwienie. Listing 7.1. Kopiowanie plikw unit MainFrm; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls, ComCtrls; type TMainForm = class(TForm) OpenDialog: TOpenDialog; GroupBox1: TGroupBox; lblFile: TLabel; pbCopy: TProgressBar; btnCopy: TButton; procedure btnCopyClick(Sender: TObject); private { Private declarations } public { Public declarations } end; var MainForm: TMainForm; 270 | S t r o n a

implementation {$R *.dfm} procedure TMainForm.btnCopyClick(Sender: TObject); var SrcFile, DstFile : File; { plik rdowy i plik przeznaczenia } FSize : Integer; { rozmiar kopiowanego pliku } Bytes : Integer; { ilo odczytanych danych } Buffer : array[0..255] of byte; { bufor przechowujcy dane } TotalSize : Integer; { ilo skopiowanych ju bajtw } begin if OpenDialog.Execute then begin { wywietl na etykiecie ciek kopiowanego pliku } lblFile.Caption := 'Plik ' + OpenDialog.FileName; AssignFile(SrcFile, OpenDialog.FileName); try Reset(SrcFile, 1); { otwrz plik } FSize := FileSize(SrcFile); { odczytaj rozmiar pliku } pbCopy.Max := (FSize div 1000); { maksymalna pozycja na pasku postpu } AssignFile(DstFile, 'C:\' + ExtractFileName(OpenDialog.FileName) + '~'); try { utwrz plik } Rewrite(DstFile, 1); repeat Application.ProcessMessages; { odczytaj dane } BlockRead(SrcFile, Buffer, SizeOf(Buffer), Bytes); if Bytes > 0 then { jeeli liczba odczytanych bajtw jest wiksza od 0 } begin { przypisz odczytane dane do pliku } BlockWrite(DstFile, Buffer, Bytes); TotalSize := TotalSize + Bytes; end; { pozycja na pasku postpu } pbCopy.Position := (TotalSize div 1000); until Bytes = 0; finally 271 | S t r o n a

CloseFile(DstFile); end; finally CloseFile(SrcFile); end; end; end; end.

Kod moe wyda si nieco skomplikowany. W rzeczywistoci polega po prostu na odczytywaniu w ptli kolejnych porcji danych. Na samym pocztku nastpuje otwarcie pliku do skopiowania i utworzenie jego kopii. Zauwa, e wykorzystaem funkcj ExtractFileName. Suy ona do pobrania samej nazwy pliku z penej cieki. Wczeniej zastosowaem nie omawiana jeszcze funkcj FileSize, ktra suy do pobierania rozmiaru otwartego pliku. W programie do kopiowania danych uyem ptli repeat..until, poniewa wymagane jest co najmniej jednokrotne wykonanie ptli (co gwarantuje wanie ta ptla). Fragment danych przypisywany jest do zmiennej Buffer, a ilo rzeczywicie odczytanych danych ? do zmiennej Bytes. Musiaem skorzysta z takiej konstrukcji z jednego powodu. Ilo odczytanych danych nie zawsze musi wynosi 255 bajtw (rozmiar tablicy) ? moe by to mniejsza warto, np. w przypadku, gdy rozmiar pliku nie jest zaokrglony do 255 bajtw (co zdarza si bardzo rzadko i jest kwesti przypadku). Zapisanie w ten sposb odczytanej ?paczuszki? jest moliwe dziki nastpujcemu wierszowi kodu: BlockWrite(DstFile, Buffer, Bytes);

Drugi parametr (Buffer) jest wskazaniem bufora, a trzeci parametr to rozmiar, czyli ilo odczytanych danych ? zmienna Bytes. Ptla wykonywana jest dopty, dopki rozmiar odczytanych danych nie wynosi 0, co oznaczaoby, e caa zawarto pliku zostaa odczytana. Peny kod rdowy powyszego programu moesz znale na pycie CD-ROM w katalogu ..listingi/7/Copy File. W rezultacie powyszy przykad by jedynie zaprezentowaniem moliwoci procedur BlockWrite i BlockRead, gdy do kopiowania rwnie dobrze mona uy funkcji CopyFile ? o tym powiemy jednak w dalszej czci rozdziau.

272 | S t r o n a

Inne funkcje operujce na plikach


Naley wspomnie o kilku funkcjach, z ktrych nieraz moesz skorzysta podczas operowania na plikach.

FilePos function FilePos(var F): Longint;

Funkcja FilePos zwraca rezultat w postaci pozycji, ktrej wanie tycz si operacje. W parametrze naley poda oczywicie nazw zmiennej typu File.

FileSize function FileSize(var F): Integer;

Funkcja podaje rozmiar (w bajtach) otwartego pliku: var F : File; begin AssignFile(F); Reset(F, 1); Label.Caption := 'Rozmiar pliku: ' + IntToStr(FileSize(F)); CloseFile(F); end;

Seek procedure Seek(var F; N: Longint);

Procedura Seek (jak zreszt wszystkie powysze funkcje) dziaa jedynie w odniesieniu do plikw amorficznych i suy do przechodzenia do okrelonego miejsca pliku (definiowanego poprzez parametr N).

Truncate procedure Truncate(var F);

Procedura Truncate suy do przycinania pliku. Podany w parametrze F plik zostaje obcity od konkretnego miejsca do koca.

273 | S t r o n a

procedure TForm1.Button1Click(Sender: TObject); var F : File; begin AssignFile(F, 'C:\dane.txt'); try Reset(F, 1); Seek(F, FileSize(F) div 2); // przejcie na rodek pliku Truncate(F); finally CloseFile(F); end; end;

Powyszy kod powoduje skrcenie pliku dane.txt o poow. Najpierw po otwarciu przechodzimy do rodka pliku (wiem, e to brzmi troch abstrakcyjnie), by pniej usun wszystkie dane znajdujce si poniej.

Rename procedure Rename(var F; Newname: string); procedure Rename(var F; Newname: PChar);

atwo mona si domyle, e te dwie procedury (przeciane) su do zmiany nazwy pliku. Pierwszym parametrem musi by zmienna typu File, a drugi parametr to nazwa nowego pliku.

RemoveFile procedure RemoveFile(const AFileName: string);

W celu skasowania pojedynczego pliku mona wywoa procedur RemoveFile. Parametr AFileName powinien zawiera ciek do pliku, ktry chcemy skasowa.

Operacje na ciekach plikw W module SysUtils znajduje si kilka bardzo uytecznych funkcji, pozwalajcych na manipulowanie ciek pliku. Piszc ?manipulowanie? mam na myli uzyskiwanie ze cieki nazwy pliku, rozszerzenia czy katalogu, w ktrym plik si znajduje. Funkcje te opisaem w tabeli 7.2.

274 | S t r o n a

Tabela 7.2. Funkcje do uzyskiwania danych ze zmiennej String Funkcja ExtractFileDir Opis Z kompletnej cieki pliku pobiera jedynie nazw katalogu, w ktrym znajduje si plik ? np.: C:\Windows\System Funkcja ze cieki zwraca jedynie liter dysku, na ktrym znajduje si dany plik Funkcja zwraca rozszerzenie pliku Z podanej w parametrze cieki zwracana jest jedynie nazwa pliku Funkcja dziaa podobnie jak ExtractFileDir, z t rnic, e zwraca nazw katalogu ze znakiem \ na kocu: C:\Windows\System\

ExtractFileDrive ExtractFileExt ExtractFileName ExtractFilePath

ExtractShortPathName Zwraca skrcon ciek ? np. C:\Progra~1\MyComp~1\MyApp\MyApp.exe

Funkcje operujce na katalogach


W Delphi w prosty sposb moemy operowa na katalogach, korzystajc z poniszych funkcji. Co prawda nie ma ich duo, ale do podstawowych operacji cakowicie wystarcz.

mkDir procedure MkDir(const S: string); overload; procedure MkDir(P: PChar); overload;

Procedura mkDir powinna by znana osobom, ktre wczeniej programoway w Turbo Pascalu. Umoliwia utworzenie katalogu okrelonego w parametrze S lub P (funkcja przeciona). procedure TForm1.Button1Click(Sender: TObject); begin mkDir('C:\folder'); end;

275 | S t r o n a

rmDir procedure RmDir(const S: string); overload; procedure RmDir(P: PChar); overload;

Nazwa funkcji jest podobna do powyszej z t rnic, e owa funkcja ? rmDir (skrt od Remove Directory) ? powoduje usunicie katalogu. procedure TForm1.Button1Click(Sender: TObject); begin rmDir('C:\folder'); end;

RemoveDirectory function RemoveDir(const Dir: string): Boolean;

Funkcja RemoveDir take suy do usunicia podanego w parametrze katalogu. W odrnieniu od polecenia rmDir funkcja ta jest zawarta w module SysUtils (natomiast rmDir jest funkcj systemow) i zwraca False w przypadku, gdy nie uda si usun katalogu. procedure TForm1.Button1Click(Sender: TObject); begin RemoveDir('C:\folder'); end;

Funkcja nie zwraca komunikatu o bdzie w razie nieudanej prby usunicia, tak jak to ma miejsce w przypadku rmDir. Oba polecenia ? RemoveDir oraz rmDir ? nie usun katalogu, w ktrym znajduj si pliki lub inne foldery. W takim przypadku naley usun pojedynczo wszystkie pliki znajdujce si w owym folderze, a dopiero pniej sam katalog. Wiedz potrzebn do wykonania tego zadania zdobdziesz w dalszej czci rozdziau.

Pliki typowane
Pliki typowane s kolejnym rodzajem plikw; mog okaza si bardzo przydatne w pisaniu programw. Dotd poznae dwa rodzaje plikw: tekstowe (dajce si podzieli na wiersze) oraz binarne ? o nieregularnej budowie. Pliki typowane mog zawiera dane o regularnym ukadzie ? np. cae rekordy danych. Rekordy moesz dowolnie odczytywa lub zapisywa. W ten sposb moesz stworzy nawet prost baz danych.

276 | S t r o n a

Deklaracja
Deklaracja plikw typowanych przebiega niestandardowo ? np. tak: var MyFile : File of TRecord ; Od tej pory zmienna MyFile definiuje nowy typ plikw, ktre skada si bd z rekordw TRecord. Aby dopeni procesu deklaracji nowego typu, naley okreli jeszcze struktur rekordu TRecord. type TRecord = packed record Imie : String[20]; Nazwisko : String[20]; Wiek : Byte; end; procedure TForm1.Button1Click(Sender: TObject); var MyFile : file of TRecord; begin end; end.

Od tej pory w plikach bdzie mona zamieszcza, a nastpnie odczytywa cae rekordy TRecord. Umoliwia nam to atwe gromadzenie danych potrzebnych np. w trakcie dziaania programu.

Tworzenie pliku i dodawanie danych


Oto pierwsza zasada: wraz z plikami typowanymi nie moe zosta uyte polecenie Writeln, a jedynie Write. W przeciwnym wypadku kompilator wywietli komunikat o bdzie: [Error] MainFrm.pas(44): Illegal type in Write/Writeln statement. Utworzenie pliku typowanego i dodanie do niego danych (rekordu) moe wyglda tak: type { deklaracja rekordu } TRecord = packed record FName : String[30]; SName : String[30]; Age : Byte; end;

277 | S t r o n a

procedure TMainForm.btnCreateClick(Sender: TObject); var F: file of TRecord; Rec : TRecord; begin { wypenienie rekordu danymi } Rec.FName := 'Piotr'; Rec.SName := 'Nowak'; Rec.Age := 89; AssignFile(F, 'dane.dat'); try Rewrite(F); // utworzenie pliku Write(F, Rec); // dodanie rekordu finally CloseFile(F); end; end;

Tym razem plik zostanie zapisany w katalogu, w ktrym umieszczony jest program. Po uruchomieniu programu i wykonaniu powyszej procedury do pliku zostanie dodany rekord.

Odczyt rekordu z pliku


Zapisane rekordy mona odczyta z pliku za pomoc procedury Read ? w podobny sposb jak przy odczytywaniu danych z innych rodzajw plikw. Oto przykad: procedure TMainForm.Button1Click(Sender: TObject); var F: file of TRecord; Rec : TRecord; begin AssignFile(F, 'dane.dat'); try Reset(F); Read(F, Rec); { rekord Rec zawiera informacje wczytane z pliku } finally CloseFile(F); end; end;

278 | S t r o n a

Przykad dziaania ? ksika adresowa


Aby mg lepiej utrwali sobie wiadomoci dotyczce plikw typowanych, w tym punkcie przedstawi wiczenie, ktrego celem bdzie stworzenie prostej ksiki adresowej, opartej na plikach typowanych. Nasz program bdzie zawiera proste funkcje, takie jak dodanie kontaktu oraz usunicie go z pliku. Program przedstawiony jest na rysunku 7.3.

Rysunek 7.3. Program ? ksika adresowa

Projektowanie interfejsu Nasz program skada si bdzie z dwch formularzy: jeden przedstawiony jest na rysunku 7.3, drugi natomiast to formularz sucy do dodawania nowego pola w komponencie typu TListView. Komponent TListView jest podzielony na kolumny, a to za spraw waciwoci ViewStyle, ktrej nadano warto vsReport. Tworzenie kolumn odbywa si za porednictwem waciwoci Columns. Obiekt TToolBar spenia rol paska narzdziowego ? na nim znajduj si dwa przyciski, tworzone za pomoc polecenia New Button z menu podrcznego owego komponentu. Warto ustawi warto waciwoci AutoSize komponentu TToolBar na True, co pozwoli dopasowa rozmiar paska narzdziowego do przyciskw na nim si znajdujcych. 279 | S t r o n a

Wywietlanie etykiet tekstowych na przyciskach paska narzdziowego jest moliwe za porednictwem waciwoci ShowCaptions.

Rysunek 7.4 przedstawia drugi formularz programu, wywietlany podczas prby dodania nowego rekordu do naszej bazy.

Rysunek 7.4. Formularz sucy do dodawania rekordw do bazy Formularz zawiera kilka kontrolek typu TEdit, w ktrych musz si znale dane do zapisania w bazie ? nie stanowi to nic nadzwyczajnego.

Zaoenia programu Program ma by prost baz danych, skadajc si z paru rekordw. Po kadym uruchomieniu programu wywoywana jest procedura ReadFile, ktra ma na celu odczytanie rekordw znajdujcych si w pliku. Po kadorazowym dodaniu rekordu zawarto pliku jest ponownie wywietlana w komponencie TListView ? znowu nastpuje wywoanie procedury ReadFile.

Procedura ReadFile Zadaniem procedury ReadFile jest odczytanie z pliku wszystkich rekordw, a nastpnie dodanie ich kolejno do komponentu TListView: procedure TMainForm.ReadFile; var F : TAddressFile; i : Integer; ListItem : TListItem; Rec : TAddress; 280 | S t r o n a

begin lvAddress.Clear; { anuluj, jeeli plik z danymi nie istnieje } if not FileExists('file.dat') then Exit; AssignFile(F, 'file.dat'); try Reset(F); // utwrz plik for I := 0 to FileSize(F) ?1 do begin Read(F, Rec); // w ptli otwrz kolejne rekordy ListItem := lvAddress.Items.Add; // dodaj rekord do komponentu ListItem.Caption := Rec.Name; ListItem.SubItems.Add(IntToStr(Rec.Tel1)); ListItem.SubItems.Add(IntToStr(Rec.Tel2)); ListItem.SubItems.Add(Rec.Mail); end; finally CloseFile(F); end; end;

Jeeli jest to pierwsze uruchomienie programu lub plik z danymi nie istnieje, kod procedury zostaje pominity (metoda Exit ? anulowanie wykonywania dalszej czci kodu). W dalszej czci programu po otwarciu pliku nastpuje otwieranie w ptli kolejnych rekordw. W tym przypadku funkcja FileSize zwraca ilo rekordw znajdujcych si w pliku, tak wic w kadej iteracji wykonywane jest odczytanie zawartoci do rekordu TAddress. Wwczas nie pozostaje ju nic innego, jak doda zawarto owego rekordu do komponentu TListView. Dodawanie elementu do komponentu odbywa si za porednictwem zmiennej typu TListItem. Do tego rekordu naley przypisa dane, ktre maj zosta dodane do komponentu TListView.

Kasowanie elementu Object Pascal nie posiada procedury umoliwiajcej kasowanie konkretnego rekordu z pliku ? naley t funkcj zaprogramowa samemu. Jak to wyglda w praktyce? Naley w miejsce pliku z baz danych utworzy pusty plik i do niego dodawa kolejne rekordy, odczytane z komponentu TListView, pomijajc rekord, ktry zosta zaznaczony do usunicia. procedure TMainForm.btnRemoveClick(Sender: TObject); var F : TAddressFile; Rec : TAddress; 281 | S t r o n a

i : Integer; begin AssignFile(F, 'file.dat'); try Rewrite(F); // utworzenie pliku i skasowanie poprzedniej zawartoci for I := 0 to lvAddress.Items.Count ?1 do begin { jeeli wykonywana iteracja nie ma numeru takiego samego, jak zaznaczony element } if I <> lvAddress.Selected.Index then begin { dodaj zawarto kolejnego elementu do pliku } Rec.Name := lvAddress.Items[i].Caption; Rec.Tel1 := StrToInt(lvAddress.Items[i].SubItems[0]); Rec.Tel2 := StrToInt(lvAddress.Items[i].SubItems[1]); Rec.Mail := lvAddress.Items[i].SubItems[2]; Write(F, Rec); end; end; finally CloseFile(F); ReadFile; // odwie zawarto komponentu end; end;

Budowa tej procedury jest w gruncie rzeczy prosta ? jest to dodawanie poszczeglnych elementw z komponentu TListView do pliku, z pominiciem elementu zaznaczonego. W listingu 7.2 przedstawiony zosta kod rdowy caego moduu MainFrm.pas, a listing 7.3 zawiera kod rdowy moduu drugiego, sucego do dodawania nowych pl. Listing 7.2. Kod rdowy formularza MainFrm.pas unit MainFrm; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, ComCtrls, ToolWin, ImgList; type TMainForm = class(TForm) ToolBar1: TToolBar; 282 | S t r o n a

btnAdd: TToolButton; btnRemove: TToolButton; ImageList1: TImageList; StatusBar: TStatusBar; lvAddress: TListView; procedure FormCreate(Sender: TObject); procedure btnAddClick(Sender: TObject); procedure btnRemoveClick(Sender: TObject); private { Private declarations } public procedure ReadFile; end; TAddress Name : Tel1 : Tel2 : Mail : end; = packed record String[30]; Integer; Integer; String[30];

TAddressFile = file of TAddress; var MainForm: TMainForm; implementation uses AddFrm; {$R *.dfm} { TMainForm } procedure TMainForm.ReadFile; var F : TAddressFile; i : Integer; ListItem : TListItem; Rec : TAddress; begin lvAddress.Clear; { anuluj, jeeli plik z danymi nie istnieje } if not FileExists('file.dat') then Exit; AssignFile(F, 'file.dat'); try Reset(F); // utwrz plik 283 | S t r o n a

for I := 0 to FileSize(F) ?1 do begin Read(F, Rec); // w ptli otwrz kolejne rekordy ListItem := lvAddress.Items.Add; // dodaj rekord do komponentu ListItem.Caption := Rec.Name; ListItem.SubItems.Add(IntToStr(Rec.Tel1)); ListItem.SubItems.Add(IntToStr(Rec.Tel2)); ListItem.SubItems.Add(Rec.Mail); end; finally CloseFile(F); end; end; procedure TMainForm.FormCreate(Sender: TObject); begin { podczas otwierania programu wywoaj procedur } ReadFile; end; procedure TMainForm.btnAddClick(Sender: TObject); begin AddForm := TAddForm.Create(Application); AddForm.ShowModal; AddForm.Free; end; procedure TMainForm.btnRemoveClick(Sender: TObject); var F : TAddressFile; Rec : TAddress; i : Integer; begin AssignFile(F, 'file.dat'); try Rewrite(F); // utworzenie pliku i skasowanie poprzedniej zawartoci for I := 0 to lvAddress.Items.Count ?1 do begin { jeeli wykonywana iteracja nie ma numeru takiego samego, jak zaznaczony element } if I <> lvAddress.Selected.Index then begin { dodaj zawarto z kolejnego elementu do pliku } Rec.Name := lvAddress.Items[i].Caption; 284 | S t r o n a

Rec.Tel1 := StrToInt(lvAddress.Items[i].SubItems[0]); Rec.Tel2 := StrToInt(lvAddress.Items[i].SubItems[1]); Rec.Mail := lvAddress.Items[i].SubItems[2]; Write(F, Rec); end; end; finally CloseFile(F); ReadFile; // odwie zawarto komponentu end; end; end.

Listing 7.3. Kod rdowy formularza AddFrm.pas unit AddFrm; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls, Buttons; type TAddForm = class(TForm) GroupBox1: TGroupBox; btnAdd: TBitBtn; Label1: TLabel; Label2: TLabel; Label3: TLabel; Label4: TLabel; edtName: TEdit; edtTel1: TEdit; edtTel2: TEdit; edtAddress: TEdit; procedure btnAddClick(Sender: TObject); private { Private declarations } public { Public declarations } end; var AddForm: TAddForm;

285 | S t r o n a

implementation {$R *.dfm} uses MainFrm; procedure TAddForm.btnAddClick(Sender: TObject); var Rec : TAddress; F : TAddressFile; begin AssignFile(F, 'file.dat'); try { jeeli plik istnieje, otwrz go; w przeciwnym wypadku ? utwrz } if FileExists('file.dat') then Reset(F) else Rewrite(F); Seek(F, FileSize(F)); // przesu na koniec pliku Rec.Name Rec.Tel1 Rec.Tel2 Rec.Mail Write(F, := edtName.Text; := StrToInt(edtTel1.Text); := StrToInt(edtTel2.Text); := edtAddress.Text; Rec); // dodaj zawarto

finally CloseFile(F); MainForm.lvAddress.Clear; MainForm.ReadFile; end; end; end.

Kopiowanie i przenoszenie plikw


Podczas omawiania plikw amorficznych przedstawiem przykad kopiowania dwch plikw. Chciaem wtedy jedynie zaprezentowa zasad kopiowania danych, lecz stanowio to troch wywaanie otwartych drzwi. Istniej bowiem funkcje, dziki ktrym skopiowanie pliku jest jedynie kwesti jednego wiersza kodu.

Kopiowanie
Kopiowanie pliku (o kopiowaniu caych katalogw wspomn pniej) moe by zrealizowane za pomoc jednej funkcji API ? CopyFile. 286 | S t r o n a

Deklaracja owej funkcji w module Windows.pas przedstawia si nastpujco: function CopyFile(lpExistingFileName, lpNewFileName: PChar; bFailIfExists: BOOL): BOOL; stdcall;

Pierwszym parametrem musi by cieka do kopiowanego pliku. Drugi parametr definiuje ciek do nowego pliku, a ostatni (typu Bool) okrela, czy w przypadku istnienia pliku o takiej nazwie program ma go zastpi czy te nie. Przykad uycia: CopyFile('C:\plik.exe', 'D:\plik.exe', True);

Wad takiego kopiowania jest maa moliwo manipulowania ca operacj. Nie moemy np. na komponencie TProgressBar pokaza postpu procesu kopiowania pliku.

Przenoszenie pliku
Przenoszenie pliku (inaczej mwic, jego wycinanie) jest rwnie proste co kopiowanie. Tu rwnie wchodzi w gr jedna instrukcja ? MoveFile. function MoveFile(lpExistingFileName, lpNewFileName: PChar): BOOL; stdcall;

Pierwszym parametrem musi by poprzednia cieka dostpu do pliku, a drugi parametr (lpNewFileName) musi by now ciek: MoveFile('C:\plik.exe', 'D:\plike.exe');

W tym momencie plik plik.exe z dysku C: zostanie przeniesiony na dysk D:

Struktura TSHFileOpStruct
W module ShellAPI znajduje si cakiem przydatny rekord ? TSHFileOpStruct ? ktry mona wykorzysta do kopiowania lub przenoszenia plikw. Oczywicie rekord jest uywany jedynie w poczeniu z odpowiednimi funkcjami; prezentuje si w ten sposb: TShFileOpStruct = packed record Wnd: HWND; wFunc: UINT; 287 | S t r o n a

pFrom: PWideChar; pTo: PWideChar; fFlags: FILEOP_FLAGS; fAnyOperationsAborted: BOOL; hNameMappings: Pointer; lpszProgressTitle: PWideChar; end;

Znaczenie poszczeglnych parametrw jest nastpujce:


Wnd ? uchwyt okna dialogowego uywanego do pokazania statusu operacji. wFunc ? funkcja, jaka ma zosta wykonana ? patrz tabela 7.3. pFrom ? cieka pliku przeznaczonego do skopiowania, przeniesienia lub do innej operacji. pTo ? cieka pliku docelowego. fFlags ? flagi uywane w poczeniu z operacj ? patrz tabela 7.4. fAnyOperationsAborted ? jeeli uytkownik przerwie operacje przed jej zakoczeniem, parametr ten bdzie zawiera warto True. hNameMappings ? parametr jest brany pod uwag jedynie wtedy, gdy parametr fFlags zawiera warto FOF_WANTMAPPINGHANDLE. Dotyczy to troch bardziej zaawansowanego tematu, a mianowicie plikw odwzorowanych. lpszProgressTitle ? tekst, ktry pojawi si w oknie kopiowania (standardowe okno systemu Windows). Parametr uywany jedynie w przypadku, gdy fFlags to FOF_SIMPLEPROGRESS.

Tabela 7.3. Moliwe wartoci parametru wFunc Parametr FO_COPY Opis Kopiowanie plikw. Uywane s wwczas parametry wFrom i wTo

FO_DELETE Usuwanie plikw. Parametr wFrom okrela ciek do usunicia. wTo jest ignorowany FO_MOVE Przeniesienie pliku. Uywane s wwczas parametry wFrom i wTo

FO_RENAME Zmienia nazw pliku okrelonego parametrem wFrom. wTo zawiera now nazw

Tabela 7.4. Moliwe wartoci parametru fFlags Parametr FOF_ALLOWUNDO FOF_FILESONLY FOF_NOCONFIRMATION Opis Umoliwia cofnicie operacji w razie koniecznoci Zezwala na wykonywanie operacji jedynie na plikach Nie wywietla przycisku Tak na wszystkie, jeeli jest to konieczne

288 | S t r o n a

FOF_NOCONFIRMMKDIR FOF_RENAMEONCOLLISION FOF_SILENT FOF_SIMPLEPROGRESS FOF_WANTMAPPINGHANDLE

Nie wywietla zapytania o utworzenie katalogu, jeeli jest to konieczne (tworzy go automatycznie) Jeeli plik istnieje, zmienia jego nazw Nie wywietla okienka z paskiem postpu operacji Wywietla okienko postpu, ale nie pokazuje nazwy pliku Bardziej zaawansowany parametr, uywany w poczeniu z plikami odwzorowanymi

To tyle, jeeli chodzi o teori. Sprawdmy dziaanie owego rekordu w praktyce. Aby cao moga zadziaa, naley na kocu wywoa funkcj SHFileOperation, ktrej parametrem bdzie wskazanie struktury TShFileOpStruct. Oto przykad skopiowania programu do innego katalogu: uses ShellAPI; procedure TMainForm.Button1Click(Sender: TObject); var Sh : TShFileOpStruct; begin Sh.Wnd := Handle; Sh.wFunc := FO_COPY; Sh.pFrom := PChar(Application.ExeName); Sh.pTo := 'C:\kopia\kopia.exe'; Sh.fFlags := FOF_ALLOWUNDO + FOF_NOCONFIRMATION; Sh.lpszProgressTitle := 'Trwa kopiowanie...'; SHFileOperation(Sh); end;

Po wykonaniu programu system najpierw zapyta Ci, czy chcesz utworzy katalog C:\kopia (jeeli nie istnieje), a dopiero pniej skopiuje dane. Peny kod rdowy owego programu znajduje si na pycie CD-ROM w katalogu ../listingi/7/ShFileOpStruct.

289 | S t r o n a

Strumienie
Strumienie s specjaln form wymiany i transportu danych, obsugiwan przez klas TStream. To okrelenie moe nie jest zbyt precyzyjne, ale zaraz postaram si przedstawi Ci szczegowe objanienia. Dziki strumieniom moesz w prosty sposb operowa na danych znajdujcych si w pamici komputera, w plikach itp. Poprzednie przykady (pliki typowane i amorficzne) opieray si na wykorzystaniu funkcji WinAPI. Klasa TStream jest natomiast klas VCL umieszczon w module Classes, std obsuga samych plikw, jak i transport danych, mog by atwiejsze.

Podzia strumieni
Klasa TStream jest jedynie klas bazow dla innych klas pochodnych ? strumieni operujcych na innym typie danych. Przykadowo do operowania na plikach uyjemy klasy TFileStream, a do operowania na blokach pamici ? TMemoryStream. Kada z takich klas charakteryzuje si odmiennymi waciwociami i metodami, std przed ich uyciem naley si zastanowi, ktra z nich bdzie nam potrzebna. Klasa TStream jest wic klas bazow dla kilku klas pochodnych:

TFileStream ? umoliwia dostp do plikw. TStringStream ? suy do manipulowania danymi typu String (acuchy tekstowe). TMemoryStream ? klasa suy do operowania na blokach pamici. TBlobStream ? klasa strumieniowa, zwizana z bazami danych. O bazach danych bd mwi w trzeciej czci niniejszej ksiki. TWinSocketStream ? klasa suca do obsugi tzw. gniazd. Bd mwi o tym w dalszej czci ksiki. TResourceStream ? klasa uywana w poczeniu z zasobami. O zasobach bdzie mowa w rozdziale 10.

Prosty przykad na pocztek


Pierwszy przykad to zapis danych ? waciwoci danego komponentu. Wystarczy w tym wypadku skorzysta z metody WriteComponent klasy TStream. Jeeli klasa TStream posiada ow metod, to znaczy, e posiadaj j take klasy pochodne ? w tym TFileStream. Oto przykad:

290 | S t r o n a

procedure TMainForm.btnSaveClick(Sender: TObject); var S : TFileStream; begin { zapisz plik } S := TFileStream.Create('dane', fmCreate); S.WriteComponent(edtValue); // zapisz dane S.Free; end;

Przykad jest prosty i krtki, ale bardzo funkcjonalny. Na samym pocztku naley wywoa konstruktora klasy TFileStream ? pierwszy parametr to nazwa pliku, a drugi to tzw. flaga (opisz to pniej). Konstruktor klasy jest specyficzny dla kadej klasy strumieniowej. Oznacza to, e kada klasa moe posiada inne parametry albo nie posiada ich w ogle. Kolejny wiersz to wywoanie metody WriteComponent ? naley w niej poda nazw komponentu, ktrego waciwoci zostan zapisane w pliku. Odczyt danych to take kwestia paru wierszy: S := TFileStream.Create('dane', fmOpenRead); S.ReadComponent(edtValue); S.Free;

Przypisanie zapisanych w pliku waciwoci realizowane jest przez procedur ReadComponent. W parametrze musi znale si nazwa komponentu, do ktrego program przypisze dane z pliku (listing 7.4.) Listing 7.4. Zapis i odczyt waciwoci komponentu TEdit unit MainFrm; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls; type TMainForm = class(TForm) btnSave: TButton; edtValue: TEdit; 291 | S t r o n a

procedure btnSaveClick(Sender: TObject); procedure FormCreate(Sender: TObject); private { Private declarations } public { Public declarations } end; var MainForm: TMainForm; implementation {$R *.dfm} procedure TMainForm.btnSaveClick(Sender: TObject); var S : TFileStream; begin { zapisz plik } S := TFileStream.Create('dane', fmCreate); S.WriteComponent(edtValue); // zapisz dane S.Free; end; procedure TMainForm.FormCreate(Sender: TObject); var S : TFileStream; begin if not FileExists('dane') then Exit; // anuluj, jeeli plik nie istnieje S := TFileStream.Create('dane', fmOpenRead); S.ReadComponent(edtValue); S.Free; end; end.

Konstruktor klasy TFileStream


Praktycznie rzecz biorc, jedynie klasa TFileStream wymaga podania parametrw otwarcia pliku. Drugi parametr konstruktora okrela bowiem, w jaki sposb plik zostanie otwarty. A moe ma on zosta utworzony? Moliwe do wykorzystania flagi przedstawiono w tabeli 7.5.

292 | S t r o n a

Tabela 7.5. Moliwe tryby otwarcia pliku Tryb fmCreate fmOpenRead fmOpenWrite Opis Plik ma zosta utworzony. Jeeli istnieje, zostanie otwarty do zapisu Plik zostanie otwarty do odczytu Plik zostanie otwarty do zapisu

fmOpenReadWrite Plik zostanie otwarty zarwno do zapisu, jak i do odczytu fmShareExclusive Inne aplikacje nie bd mogy otworzy pliku, podczas gdy nasza aplikacja korzysta z owego pliku

fmShareDenyWrite Inne aplikacje mog otwiera plik, ale tylko do odczytu fmShareDenyRead Inne aplikacje mog otwiera plik jedynie do zapisu fmShareDenyNone Brak zabezpiecze ? inne aplikacje mog odczytywa i zapisywa dane w pliku

Parametry (flagi) znajdujce si w powyszej tabeli mog by ze sob czone za pomoc operatora +. Pamitaj o tym, aby po skoczeniu pracy z zasobem zwolni go, wywoujc metod Free.

Pozostae metody i waciwoci klasy TStream


W niniejszej sekcji omwi jedynie waciwoci i metody, ktre s obecne w klasie TStream, tak wic s obecne take i w klasach pochodnych. Jednak niektre klasy pochodne posiadaj inne, dodatkowe metody i waciwoci, charakterystyczne dla danego strumienia. Na razie przedstawi jedynie deklaracj, owych metod i waciwoci; ich wykorzystanie praktyczne zaprezentuj nieco dalej.

293 | S t r o n a

Waciwoci

Position

property Position: Int64;

Waciwo Position okrela pooenie w danym strumieniu. Zwracana przez ni warto okrela ilo bajtw, jakie zostay ju odczytane. Przykadowo jeeli odczytywany przez nas plik ma wielko 1 000 bajtw, moemy przej do okrelonego miejsca tego pliku. Suy do tego metoda Seek. Jeeli przesuniemy si np. na 300. bajt, waciwo Position zwrci warto 300 (pooenie w pliku).

Size

property Size: Int64;

Waciwo Size zwraca wielko odczytywanego pliku ? strumienia. Warto jest podawana w bajtach.

Metody

CopyFrom

function CopyFrom(Source: TStream; Count: Int64): Int64;

Funkcja CopyFrom jest uywana wtedy, gdy chcemy skopiowa dane z jakiego strumienia do innego strumienia. Pierwszy parametr musi by nazw klasy typu TStream lub pochodn. Natomiast drugi parametr to ilo bajtw, ktre maj by przypisane drugiemu strumieniowi.

Read, ReadBuffer

function Read(var Buffer; Count: Longint): Longint; virtual; abstract; procedure ReadBuffer(var Buffer; Count: Longint);

Oba polecenia s do siebie bardzo podobne, a ich dziaanie jest wrcz identyczne. Jedyna rnica polega na tym, e Read jest funkcj, a ReadBuffer ? procedur. Tak wic funkcja Read zwraca ilo bajtw, ktre zostay dotychczas odczytane.

294 | S t r o n a

Seek

function Seek(Offset: Longint; Origin: Word): Longint; overload; virtual; function Seek(const Offset: Int64; Origin: TSeekOrigin): Int64; overload; virtual;

Funkcja Seek suy do przesuwania si w pliku i ustawiania znacznika odczytu na konkretnej pozycji. Funkcja ta dziaa tak samo jak funkcja systemowa Seek, ktra omawiana bya w trakcie opisywania plikw amorficznych i typowanych. Jak widzisz, dostpne s dwie funkcje ? w zalenoci od rodzaju parametrw system wybierze jedn z nich. Pierwszym parametrem musi by ilo bajtw, o jak zostanie wykonany ?skok?. Natomiast kolejny parametr okrela, jak bdzie interpretowana warto Offset (wartoci moliwe do uycia w tym parametrze znajduj si w tabeli 7.6.). Tabela 7.6. Moliwe wartoci parametru Origin Warto Opis

soFromBeginning Przesunicie odbdzie si od pocztku zasobu soFromCurrent soFromEnd Warto Offset okrela ilo bajtw, o jak nastpi przesunicie ? poczwszy od dotychczasowej pozycji Przesunicie odbdzie si od tyu

Write, WriteBuffe

function Write(const Buffer; Count: Longint): Longint; virtual; abstract; procedure WriteBuffer(const Buffer; Count: Longint);

Zasada jest taka, jak w przypadku funkcji Read i ReadBuffer. Tutaj take dwie funkcje odgrywaj prawie identyczn rol (zapis danych do strumienia). Pierwszy parametr musi okrela dane, ktre zostan zapisane, a drugi ? ilo bajtw, ktra ma zosta doczona do strumienia. Jedna rnica dzielca te oba polecenia polega na tym, e Write zwraca ilo bajtw zapisanych w strumieniu.

295 | S t r o n a

Praktyczny przykad
Jak dotd wicej o strumieniach pisaem teoretycznie, nie podajc praktycznych przykadw wykorzystania. Zaprezentuj wobec tego teraz przykad ? dzielenie plikw. Nasz program, wykorzystujc strumienie, podzieli wybrany plik na mniejsze fragmenty. Uytkownik oczywicie bdzie mg z powrotem poczy wszystko w jedn cao.

Rzut okiem na interfejs programu Gwny formularz programu przedstawiony zosta na rysunku 7.5.

Rysunek 7.5. Wygld programu sucego do dzielenia plikw Gwny czon programu stanowi komponenty z zakadki Win 3.1 palety komponentw. Dziki owym komponentom mamy moliwo wywietlenia struktury katalogw na danej partycji oraz zawarto zaznaczonego folderu. Do ustalenia rozmiaru pojedynczego pliku skorzystaem z komponentu TTrackBar z zakadki Win32. Domylnie jeden podzielony fragment pliku bdzie mia rozmiar rwny 500 bajtw.

Na samym dole znajduje si pasek postpu (TProgressBar), ktry okrela stopie wykonania dzielenia plikw.

296 | S t r o n a

Kod rdowy programu Podstawowymi funkcjami w naszym programie bd dwie procedury, ktre zadeklarowaem w sekcji private klasy: private { procedura procedure { procedura procedure end;

dzielenia plikw } DivFile(const FileName: String); czenia plikw } ConnectFile(const Dir : String);

Pierwsza procedura bdzie dzielia plik okrelony parametrem FileName; natomiast procedura druga spowoduje poczenie wszystkich plikw, znajdujcych si w okrelonym katalogu, w tym przypadku okrelonym parametrem Dir. Najpierw przyjrzyj si procedurze DivFile, a ja pniej postaram si j omwi: procedure TMainForm.DivFile(const FileName: String); var Input : TFileStream; Output : TFileStream; i : Integer; DirPath : String; BuffSize : Integer; begin BuffSize := BufferTrack.Position; // pobierz rozmiar bufora ( rozmiar jednego pliku ) DirPath := FileName + '.temp'; // dodaj rozszerzenie mkDir(DirPath); // utwrz folder Input := TFileStream.Create(FileName, fmOpenRead); try ProgressBar.Max := (Input.Size div BuffSize); { po podzieleniu rozmiaru pliku przez bufor otrzymamy ilo "kawakw", z ktrych bdzie si skada podzielony plik } for I := 0 to (Input.Size div BuffSize) do begin Application.ProcessMessages; ProgressBar.Position := i; { w kadej iteracji ptli przesu si w zawartoci pliku o rozmiar bufora } Input.Seek(i * BuffSize, soFromBeginning); { utwrz w nowoutworzonym folderze plik odpowiadajcy 297 | S t r o n a

fragmentowi dzielonego pliku } Output := TFileStream.Create((DirPath + '\' + ExtractFileName(FileName) + IntToStr(i) + '.temp'), fmCreate); try { nastpnie za pomoc funkcji CopyFrom ze strumienia okrelona ilo bajtw zostaje przekopiowana (bufor) do strumienia Output. Jeeli pozostaa do skopiowania cz jest mniejsza od bufora, to trzeba skopiowa tylko t cz, ktra pozostaa do skopiowania... :)) } if (Input.Size ? (i * BuffSize)) < BuffSize then Output.CopyFrom(Input, (Input.Size ? (i * BuffSize))) else Output.CopyFrom(Input, BuffSize); finally Output.Free; end; end; finally Input.Free; end; end;

Na samym pocztku kod ten moe wyda si niezwykle skomplikowany ? aby uatwi jego odczytanie, umieciem w nim sporo komentarzy. Na samym pocztku nastpuje wczytanie pliku do strumienia ? zmiennej Input. Kolejnym krokiem jest okrelenie iloci iteracji ptli for. Ilo t uzyskujemy, dzielc rozmiar strumienia przez bufor : for I := 0 to (Input.Size div BuffSize) do

Kolejnym krokiem jest skorzystanie z funkcji Seek w celu przemieszczenia si do okrelonego miejsca w pliku: Input.Seek(i * BuffSize, soFromBeginning);

Dziki temu w kadej iteracji ptli nastpuje przesunicie o np. 500, 1 000, 1 500 bajtw itd. Podczas tej iteracji tworzony jest nowy strumie (plik), a do niego dodawane jest kolejne 500 bajtw danych. czenie plikw jest spraw o tyle skomplikowan, e niektre instrukcje nie s Ci znane. Poznasz je dopiero w dalszej czci tego rozdziau. Mam na myli instrukcje suce do wyszukiwania plikw w danym katalogu.

298 | S t r o n a

Oto procedura czca: procedure TMainForm.ConnectFile(const Dir: String); var SR : TSearchRec; Found : Integer; I : Integer; Input : TFileStream; Output : TfileStream; NumberOfFiles : Integer; begin NumberOfFiles := 0; { te instrukcje maj na celu uzyskanie iloci plikw .temp znajdujcych si w okrelonej lokalizacji ? ilo plikw oznacza zmienna NumberOfFile. } Found := FindFirst(Dir + '\*.temp', faAnyFile, SR); while Found = 0 do begin Inc(NumberOfFiles); Found := FindNext(SR); end; FindClose(SR); { te instrukcje odpowiadaj za stworzenie pliku ? to do niego zostanie wczona zawarto plikw-kawakw... } if not FileExists(ExtractFileDir(Dir) + ChangeFileExt(ExtractFileName(Dir), '')) then Output := TFileStream.Create(ExtractFileDir(Dir) + ChangeFileExt(ExtractFileName(Dir), ''), fmCreate) else Output := TFileStream.Create(ExtractFileDir(Dir) + ChangeFileExt(ExtractFileName(Dir), ''), fmOpenWrite); ProgressBar.Max := NumberOfFiles; try for I := 0 to NumberOfFiles ?1 do begin Application.ProcessMessages; ProgressBar.Position := i; { tutaj nastpuje otwarcie pliku-kawaka do skopiowania } Input := TFileStream.Create(Dir + '\' + ExtractFileName(ChangeFileExt(DirListBox.Directory, '')) + 299 | S t r o n a

IntToStr(i) + '.temp', fmOpenRead); try { tutaj do pliku czonego kopiujemy zawarto maego pliku (czci) } Output.CopyFrom(Input, Input.Size); finally Input.Free; end; end; finally Output.Free; end; end;

Na samym pocztku odbywa si wyszukanie wszystkich plikw znajdujcych si w danym katalogu. Nastpnie, po pobraniu iloci plikw (zajmiemy si tym nieco dalej), nastpuje wykonanie ptli, ktra odczytywa bdzie zawarto tych wszystkich plikw i kolejno dodawa dane do jednego strumienia (listing 7.5.). Listing 7.5. Listing programu do dzielenia plikw unit MainFrm; interface uses Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, ComCtrls, StdCtrls, FileCtrl; type TMainForm = class(TForm) BufferTrack: TTrackBar; lblBuffor: TLabel; DirListBox: TDirectoryListBox; FileListBox: TFileListBox; DriveCombo: TDriveComboBox; btnDivFile: TButton; ProgressBar: TProgressBar; btnConnectFile: TButton; procedure BufferTrackChange(Sender: TObject); procedure btnDivFileClick(Sender: TObject); procedure btnConnectFileClick(Sender: TObject); private { procedura dzielenia plikw } procedure DivFile(const FileName: String); { procedura czenia plikw } procedure ConnectFile(const Dir : String); end;

300 | S t r o n a

var MainForm: TMainForm; implementation {$R *.DFM} { TMainForm } procedure TMainForm.DivFile(const FileName: String); var Input : TFileStream; Output : TFileStream; i : Integer; DirPath : String; BuffSize : Integer; begin BuffSize := BufferTrack.Position; // pobierz rozmiar bufora ( rozmiar jednego pliku ) DirPath := FileName + '.temp'; // dodaj rozszerzenie mkDir(DirPath); // utwrz folder Input := TFileStream.Create(FileName, fmOpenRead); try ProgressBar.Max := (Input.Size div BuffSize); { po podzieleniu rozmiaru pliku przez bufor otrzymamy ilo kawakw, z ktrych skada si bdzie podzielony plik } for I := 0 to (Input.Size div BuffSize) do begin Application.ProcessMessages; ProgressBar.Position := i; { w kadej iteracji ptli przesu si w zawartoci pliku o rozmiar bufora } Input.Seek(i * BuffSize, soFromBeginning); { utwrz w nowoutworzonym folderze plik odpowiadajcy fragmentowi dzielonego pliku } Output := TFileStream.Create((DirPath + '\' + ExtractFileName(FileName) + IntToStr(i) + '.temp'), fmCreate); try { nastpnie, za pomoc funkcji CopyFrom, ze strumienia okrelona ilo bajtw (bufor) zostaje przekopiowana do strumienia Output. Jeeli pozostaa do skopiowania cz jest mniejsza od bufora, to trzeba skopiowa tylko t cze, ktra pozostaa do skopiowania... :)) } if (Input.Size ? (i * BuffSize)) < BuffSize then Output.CopyFrom(Input, (Input.Size ? (i * BuffSize))) else Output.CopyFrom(Input, BuffSize); finally Output.Free; 301 | S t r o n a

end; end; finally Input.Free; end; end; procedure TMainForm.BufferTrackChange(Sender: TObject); begin lblBuffor.Caption := 'Bufor: ' + IntToStr(BufferTrack.Position); end; procedure TMainForm.btnDivFileClick(Sender: TObject); begin DivFile(FileListBox.FileName); end; procedure TMainForm.ConnectFile(const Dir: String); var SR : TSearchRec; Found : Integer; I : Integer; Input : TFileStream; Output : TfileStream; NumberOfFiles : Integer; begin NumberOfFiles := 0; { Te instrukcje maj na celu uzyskanie iloci plikw .temp znajdujcych si w okrelonej lokalizacji ? ilo plikw oznacza zmienna NumberOfFile. } Found := FindFirst(Dir + '\*.temp', faAnyFile, SR); while Found = 0 do begin Inc(NumberOfFiles); Found := FindNext(SR); end; FindClose(SR); { Te instrukcje odpowiadaj za stworzenie pliku ? to do niego zostanie wczona zawartoci plikw-kawakw... } if not FileExists(ExtractFileDir(Dir) + ChangeFileExt(ExtractFileName(Dir), '')) then Output := TFileStream.Create(ExtractFileDir(Dir) + ChangeFileExt(ExtractFileName(Dir), ''), fmCreate) else Output := TFileStream.Create(ExtractFileDir(Dir) + ChangeFileExt(ExtractFileName(Dir), ''), fmOpenWrite); ProgressBar.Max := NumberOfFiles; try 302 | S t r o n a

for I := 0 to NumberOfFiles ?1 do begin Application.ProcessMessages; ProgressBar.Position := i; { tutaj nastpuje otwarcie pliku ? kawaka do skopiowania } Input := TFileStream.Create(Dir + '\' + ExtractFileName(ChangeFileExt(DirListBox.Directory, '')) + IntToStr(i) + '.temp', fmOpenRead); try { tutaj do pliku czonego kopiujemy zawarto maego pliku (czci) } Output.CopyFrom(Input, Input.Size); finally Input.Free; end; end; finally Output.Free; end; end; procedure TMainForm.btnConnectFileClick(Sender: TObject); begin ConnectFile(DirListBox.Directory); end; end.

Na doczonej pycie CD-ROM znajduje si inny przykad zastosowania strumieni ? odczytywanie tzw. tagw z plikw mp3. Kompletny kod rdowy moesz znale w katalogu ..listingi/7/mp3Tag.

303 | S t r o n a

Wyszukiwanie
W poprzednim podpunkcie podczas omawiania strumieni zamieciem przykad, w ktrym wykorzystane zostay nieznane Ci dotychczas funkcje. Miay one na celu wyszukanie plikw znajdujcych si w danym katalogu. Teraz zajmiemy si wanie procedurami umoliwiajcymi znalezienie konkretnego pliku w okrelonym miejscu lub stworzenie listy wszystkich plikw.

Rekord TSearchRec
Podczas korzystania z funkcji wyszukiwania bdziesz zmuszony pozna struktur rekordu TSearchRec. Po wykonaniu odpowiednich instrukcji w owym rekordzie zapisane zostan informacje na temat znalezionego pliku. type TSearchRec = record Time: Integer; Size: Integer; Attr: Integer; Name: TFileName; ExcludeAttr: Integer; FindHandle: THandle; FindData: TWin32FindData; end;

Do wyszukiwania bdziemy uywa instrukcji FindFirst oraz FindNext. Wwczas naley w parametrze poda zmienn wskazujc rekord TSearchRec. w rekord dostarcza nam informacji o rozmiarze pliku, czasie jego utworzenia oraz atrybutach.

Jak zrealizowa wyszukiwanie?


Zrealizowanie procedury przeszukania dysku wcale nie jest takie trudne. Posugiwa si bdziemy trzema podstawowymi funkcjami z moduu SysUtils, ktre w poczeniu z rekordem TSearchRec dadz zamierzony efekt. Te funkcje to FindFirst, FindNext i FindClose. FindFirst function FindFirst(const Path: string; Attr: Integer; var F: TSearchRec): Integer;

Na samym pocztku caego procesu naley skorzysta z funkcji FindFirst, ktra inicjuje proces wyszukiwania. Pierwszym parametrem musi by katalog (cieka), w ktrym program bdzie przeszukiwa pliki. Drugi parametr to atrybuty plikw, ktre maj by brane pod uwag (patrz tabela 7.7). Parametr ostatni ? F ? to wskazanie na rekord TSearchRec. 304 | S t r o n a

Tabela 7.7. Atrybuty plikw Nazwa atrybutu faReadOnly faHidden faSysFile faDirectory faArchive faAnyFile Opis Pliki tylko do odczytu Pliki ukryte Pliki systemowe Katalogi Archiwa</td Dowolne pliki

Atrybuty plikw mog by ze sob poczone za pomoc operatora +. Przykadowo jeeli chcesz, aby program wyszuka pliki ukryte oraz systemowe, to w drugim parametrze naley poda kombinacj: faHidden + faSysFile. Jeeli operacja wyszukania powiedzie si, funkcja zwrci 0. W przeciwnym wypadku zwrcony zostanie numer bdu. FindFirst('C:\Windows\*.*', faAnyFile, SR);

Powyszy przykad spowoduje znalezienie dowolnych plikw z katalogu C:\Windows. Podajc ciek do katalogu w pierwszym parametrze, wpisaem na kocu znaki *.*. Jest to tzw. maska, oznaczajca rozszerzenia plikw. W takim wypadku pod uwag bd brane pliki z dowolnym rozszerzeniem.

FindNext function FindNext(var F: TSearchRec): Integer;

Funkcja FindNext jest stosowana w poczeniu z wyej omwion funkcj ? FindFirst. Mona powiedzie, e obie te funkcje uzupeniaj si wzajemnie i razem realizuj proces przeszukiwania.
FindFirst jedynie inicjuje proces wyszukiwania, a po znalezieniu pierwszego pliku zwraca warto

0. Aby program szuka dalej, naley wywoa funkcj FindNext, w ktrej podany musi zosta parametr typu TSearchRec. 305 | S t r o n a

FindClose procedure FindClose(var F: TSearchRec);

Po zakoczeniu caego procesu zwizanego z wyszukiwaniem naley zwolni odpowiednie zasoby i pami zarezerwowan przez funkcj FindFirst. Wykorzystanie tej procedury jest proste ? w parametrze wystarczy jedynie poda nazw zmiennej wskazujcej na rekord TSearchRec.

Rekurencja
Podczas swojej dalszej przygody z programowaniem czsto moesz spotka si z terminem rekurencji. Najprociej mwic, jest to procedura, ktra wywouje sam siebie. Moe brzmi to nieco dziwnie, ale tak jest w rzeczywistoci! W pewnym momencie, podczas wykonywania algorytmu (okrelonej operacji), nastpuje ponowne odwoanie si do wykonywanej funkcji. Rekurencja zostanie wykonana przez nas podczas realizacji zadania, jakim jest wyszukiwanie. Wyglda to tak: uytkownik wykonuje okrelone czynnoci, ktre maj za zadanie wyszukanie wszystkich plikw, przykadowo w katalogu C:\Moje dokumenty. I tutaj nastpuje wywoanie procedury ? np. Search('C:\Moje dokumenty'). Program oprcz pobrania listy wszystkich plikw pobiera rwnie list katalogw. Wykonywana zostaje wwczas kluczowa operacja (rekurencja), czyli ponowne wywoanie procedury, ale ze zmienionym parametrem ? np.
Search('C:\Moje dokumenty\www').

Praktyczny przykad
Jak dotd zaprezentowaem jedynie teoretyczn wiedz na temat wyszukiwania. Nie moe by tak, aby Twoje przysze dowiadczenia opieray si jedynie na teorii. Dlatego te na pycie CD-ROM w katalogu ../listingi/7/Searching umieciem peny kod programu realizujcego wyszukiwanie. Program w trakcie dziaania przedstawiem na rysunku 7.6.

306 | S t r o n a

Rysunek 7.6. Wyszukiwanie plikw z rozszerzeniem *.pas

Wyszukiwanie plikw
Wyszukiwanie plikw realizowane jest praktycznie za pomoc jednej procedury rekurencyjnej ? Search: { procedura rekurencyjna } procedure TMainForm.Search(const StartDir : String; Ext : String); var SR, DR : TSearchRec; Found, FoundFile : Integer; ListItem : TListItem; Icon : TIcon; ExtNo : Word; { ta procedura sprawdza, czy na kocu zmiennej znajduje si znak \ ? jeeli tak, nic nie jest wykonywane; jeeli tego znaku brak, zostaje on dodany... } function IsDir(Value : String) : String; begin if Value[Length(Value)] <> '\' then // jeeli na kocu znajdziesz znak Result := Value + '\' else Result := Value; // dodaj go... w przeciwnym wypadku nie wykonuj nic end; begin Icon := TIcon.Create; 307 | S t r o n a

Found := FindFirst(IsDir(StartDir) + '*.*', faDirectory, DR); // nastpuje pobieranie katalogw z podanej lokalizacji while Found = 0 do // ptelka begin Application.ProcessMessages; // nie blokuj programu if ((DR.Attr and faDirectory) = faDirectory) and // sprawdza, czy pozycja jest katalogiem ((DR.Name <> '.') and (DR.Name <> '..')) then begin { jeeli jest to katalog, nastpuje pobranie plikw w nim si znajdujcych } FoundFile := FindFirst(IsDir(StartDir) + DR.Name + '\' + Ext, faAnyFile, SR); while FoundFile = 0 do begin Application.ProcessMessages; if ((SR.Name <> '.') and (SR.Name <> '..')) then begin Icon.Handle := ExtractAssociatedIcon(hInstance, PCHar(IsDir(StartDir) + DR.Name + '\' + SR.Name), ExtNO); { dodanie cieki pliku do listy plikw } ListItem := lbFileBox.Items.Add; ListItem.ImageIndex := ImageList.AddIcon(Icon); ListItem.Caption := IsDir(StartDir) + DR.Name + '\' + SR.Name; ListItem.SubItems.Add(DateTimeToStr(FileDateToDateTime(SR.Time))); ListItem.SubItems.Add(IntToStr(SR.Size) + ' B'); end; FoundFile := FindNext(SR); // kontynuuj przeszukiwanie end; FindClose(SR); // zkocz Search(IsDir(StartDir) + DR.Name, Ext); // tutaj nastpuje rekurencja end; Found := FindNext(DR); // kontynuuj end; FindClose(DR); Icon.Free; end;

Kod ten moe wyda si zagmatwany i niezrozumiay, a w przekonaniu tym moe nas utwierdzi obecno duej iloci komentarzy, ktre powoduj pewien zamt.

308 | S t r o n a

Przejdmy jednak do omwienia samego kodu. Jak mona zauway, w procedurze Search znajduje si inna procedura ? IsDir. Sprawdza ona, czy na kocu podanej w parametrze cieki znajduje si znak \. Jeeli go nie ma, zostaje on dodany. Na samym pocztku nastpuje pobranie listy wszystkich katalogw znajdujcych si w katalogu startowym, okrelonym w parametrze StartDir. Pobranie odbywa si za pomoc ptli while, w ktrej odczytujemy zawarto rekordu TSearchRec. Aby szuka dalej, naley wywoa funkcj FindNext. Kolejnym etapem jest wywoanie kolejnego polecenia FindFirst, ktre ma tym razem pobra list wszystkich plikw o okrelonym rozszerzeniu z aktualnego katalogu. Nastpnie program dodaje nazw kadego pliku do listy komponentu TListView. Uyta tu zostaa funkcja ExtractAssociatedIcon, ktra ma za zadanie pobra ikon okrelajc dany plik. Jak zapewne zauwaye, zawsze sprawdzam, czy nazwa znalezionego pliku nie jest znakiem . (kropka) lub .. (dwie kropki). W ten sposb system zaznacza, e istnieje katalog nadrzdny. Peny kod rdowy programu znajduje si w listingu 7.6. Listing 7.6. Kod rdowy programu (wyszukiwanie) { Copyright (c) 2002 ? Adam Boduch }

unit MainFrm; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls, FileCtrl, ComCtrls, ImgList, ShellAPI; type TMainForm = class(TForm) DirBox: TDirectoryListBox; edtMask: TEdit; lblMask: TLabel; btnFind: TButton; lblFoundResults: TLabel; lbFileBox: TListView; ImageList: TImageList; procedure btnFindClick(Sender: TObject); procedure lbFileBoxDblClick(Sender: TObject); private procedure Search(const StartDir : String; Ext : String); end;

309 | S t r o n a

var MainForm: TMainForm; implementation {$R *.dfm} { procedura rekurencyjna } procedure TMainForm.Search(const StartDir : String; Ext : String); var SR, DR : TSearchRec; Found, FoundFile : Integer; ListItem : TListItem; Icon : TIcon; ExtNo : Word; { ta procedura sprawdza, czy na kocu zmiennej znajduje si znak \ ? jeeli tak, nic nie jest wykonywane; jeeli tego znaku brak, zostaje on dodany... } function IsDir(Value : String) : String; begin if Value[Length(Value)] <> '\' then // jeeli na kocu znajdziesz znak Result := Value + '\' else Result := Value; // dodaj go... w przeciwnym wypadku nie wykonuj nic end; begin Icon := TIcon.Create; Found := FindFirst(IsDir(StartDir) + '*.*', faDirectory, DR); // nastpuje pobieranie katalogw z podanej lokalizacji while Found = 0 do // ptelka begin Application.ProcessMessages; // nie blokuj programu if ((DR.Attr and faDirectory) = faDirectory) and // sprawdza, czy pozycja jest katalogiem ((DR.Name <> '.') and (DR.Name <> '..')) then begin { jeeli jest to katalog, nastpuje pobranie plikw w nim si znajdujcych } FoundFile := FindFirst(IsDir(StartDir) + DR.Name + '\' + Ext, faAnyFile, SR); while FoundFile = 0 do begin Application.ProcessMessages; if ((SR.Name <> '.') and (SR.Name <> '..')) then begin Icon.Handle := ExtractAssociatedIcon(hInstance, PCHar(IsDir(StartDir) + DR.Name + '\' + SR.Name), ExtNO); { dodanie cieki pliku do listy plikw } ListItem := lbFileBox.Items.Add; ListItem.ImageIndex := ImageList.AddIcon(Icon); ListItem.Caption := IsDir(StartDir) + DR.Name + '\' + SR.Name; 310 | S t r o n a

ListItem.SubItems.Add(DateTimeToStr(FileDateToDateTime(SR.Time))); ListItem.SubItems.Add(IntToStr(SR.Size) + ' B'); end; FoundFile := FindNext(SR); // kontynuuj przeszukiwanie end; FindClose(SR); // zkocz Search(IsDir(StartDir) + DR.Name, Ext); // tutaj nastpuje rekurencja end; Found := FindNext(DR); // kontynuuj end; FindClose(DR); Icon.Free; end; procedure TMainForm.btnFindClick(Sender: TObject); begin lbFileBox.Clear; // wyczy kontrolk ImageList.Clear; lbFileBox.Items.BeginUpdate; btnFind.Enabled := False; // dezaktywuj komponent Search(DirBox.Directory, edtMask.Text); // wyszukaj btnFind.Enabled := True; // aktywuj komponent lblFoundResults.Caption := 'Rezultaty poszukiwa: ' + IntToStr(lbFileBox.Items.Count) + ' znalezionych plikw...'; lblFoundResults.Visible := True; // pokazanie komponentu lbFileBox.Items.EndUpdate; end; procedure TMainForm.lbFileBoxDblClick(Sender: TObject); begin ShellExecute(Handle, 'open', PCHar(lbFileBox.Selected.Caption), nil, nil, SW_SHOW); end; end.

311 | S t r o n a

Informacja o dyskach
W tym podpunkcie zajmiemy si uzyskiwaniem podstawowych informacji o dyskach znajdujcych si w systemie oraz danych dokadniejszych, takich jak pojemno partycji, ilo wolnego miejsca itp.

Pobieranie listy napdw


Dyski (napdy) w systemie Windows mog mie rne litery ? od A do Z. Aby pobra list wszystkich dyskw, naley wykona ptle: for I := Ord('A') to Ord('Z') do { ... }

Przypominam, e funkcja Ord ma za zadanie zamian znaku podanego w parametrze (typu Char) na kod ASCII. W kadej iteracji ptli musimy pobra informacj na temat konkretnego dysku. Realizowane jest to poprzez funkcj GetDriveType, ktra na podstawie litery dysku jest w stanie poda jego typ (CDROM, stacja dyskietek itp.). Tabela 7.8 prezentuje wartoci, ktre mog zosta zwrcone przez funkcj GetDriveType. Tabela 7.8. Wartoci, jakie moe zwrci funkcja GetDriveType Warto 0 1 Opis Typ dysku nie jest moliwy do okrelenia Napd o tym oznaczeniu nie istnieje

DRIVE_REMOVABLE Dyskietka lub napd wymienny DRIVE_FIXED DRIVE_REMOTE DRIVE_CDROM DRIVE_RAMDISK Napd niewymienny Napd sieciowy CD-ROM RAM-dysk, czyli dysk wirtualny

W parametrze funkcji naley poda nazw (liter) dysku: GetDriveType('C:\'); 312 | S t r o n a

Wwczas system zwrci informacj na temat dysku C:

Pobieranie rozmiaru dysku


Pobranie iloci wolnego miejsca oraz pojemnoci konkretnego dysku jest bardzo proste dziki dwm funkcjom: DiskSize oraz DiskFree. W obu naley poda parametr w postaci numeru dysku ? np. Ord('C'), a zwrcona warto bdzie zawieraa ilo wolnych bajtw oraz czn ilo bajtw. { dodanie do komponentu danych (w kB) } ListItem.SubItems.Add(IntToStr(DiskSize(DriveByte) div 1024) + ' kB'); ListItem.SubItems.Add(IntToStr(DiskFree(DriveByte) div 1024) + ' kB');

Powyszy przykad prezentuje dodanie do komponentu TListView informacji o pojemnoci oraz iloci wolnego miejsca wyraonej w kilobajtach.

Pobieranie dodatkowych informacji


W celu pobrania dodatkowych informacji na temat dysku (takich, jak ilo klastrw, sektorw itp.) naley skorzysta z funkcji API ? GetDiskFreeSpace: function GetDiskFreeSpace(lpRootPathName: PChar; var lpSectorsPerCluster, lpBytesPerSector, lpNumberOfFreeClusters, lpTotalNumberOfClusters: DWORD): BOOL; stdcall;

Podczas wywoywania funkcji wszystkie parametry z wyjtkiem pierwszego musz by wskazaniem na zmienn typu DWORD. Znaczenie poszczeglnych parametrw jest nastpujce:

lpRootPathName ? cieka (litera dysku), z ktrego bd pobieranie informacje. Moe to by np. C:\ lpSectorsPerCluster ? ilo sektorw przydajcych na klaster. lpBytesPerSector ? liczba bajtw przypadajcych na jeden sektor. lpNumberOfFreeClusters ? liczba wolnych klastrw. lpTotalNumberOfClusters ? czna ilo klastrw.

313 | S t r o n a

Zastosowanie funkcji GetDiskFreeSpace moe wyglda tak: { pobieranie dodatkowych informacji do dyskw } GetDiskFreeSpace(PChar(Chr(i) + ':\'), SectorsPerClusters, BytesPerSector, FreeClusters, TotalClusters);

Od tego momentu w zmiennych podanych w parametrach pojawi si odpowiednie wartoci. To, co teraz naley zrobi, to przedstawi je uytkownikowi w odpowiedniej formie. Cay kod rdowy programu znajduje si w listingu 7.7. Listing 7.7. Kod rdowy moduu unit MainFrm; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, ImgList, ComCtrls; type TMainForm = class(TForm) imgIcons: TImageList; ListView: TListView; procedure FormCreate(Sender: TObject); private { Private declarations } public { Public declarations } end; var MainForm: TMainForm; implementation {$R *.dfm} procedure TMainForm.FormCreate(Sender: TObject); var i : Integer; Drive_Type : Integer; ListItem : TListItem; Volume : array[0..255] of char; MaxComponentLength, Flag : DWORD; SectorsPerClusters, BytesPerSector, FreeClusters, TotalClusters : DWORD; DriveByte : Byte; begin 314 | S t r o n a

{ ptla po wszystkich literach } for I := Ord('A') to Ord('Z') do begin // uzyskanie litery dysku Drive_Type := GetDriveType(PChar(Chr(i) + ':\')); if (Drive_Type <> 0) and (Drive_Type <> 1) then begin { pobranie etykiety } GetVolumeInformation(PChar(Chr(i) + ':\'), Volume, SizeOf(Volume), nil, MaxComponentLength, Flag, nil, 0); ListItem := ListView.Items.Add; if Volume = '' then Volume := 'BRAK'; ListItem.Caption := Chr(i) + ':\ (' + Volume + ')'; wywietlenie litery i etykiety dysku

//

{ pobieranie dodatkowych informacji do dyskw } GetDiskFreeSpace(PChar(Chr(i) + ':\'), SectorsPerClusters, BytesPerSector, FreeClusters, TotalClusters); ListItem.SubItems.Add(IntToStr(SectorsPerClusters)); ListItem.SubItems.Add(IntToStr(BytesPerSector)); ListItem.SubItems.Add(IntToStr(FreeClusters)); ListItem.SubItems.Add(IntToStr(TotalClusters)); { numer dysku } DriveByte := (i ? Ord('A') + 1); { dodanie do komponentu danych (w kB) } ListItem.SubItems.Add(IntToStr(DiskSize(DriveByte) div 1024) + ' kB'); ListItem.SubItems.Add(IntToStr(DiskFree(DriveByte) div 1024) + ' kB');

case Drive_Type of { w zalenoci od rodzaju dysku wywietl ikon } DRIVE_CDROM: ListItem.ImageIndex := 0; DRIVE_FIXED: ListItem.ImageIndex := 1; else ListItem.ImageIndex := ?1; end; end; end; end;

end.

Praktycznie cay kod wykonywany jest w jednym zdarzeniu ? OnCreate. Rysunek 7.7 przedstawia program w trakcie dziaania.

315 | S t r o n a

Rysunek 7.7. Dyski zainstalowane w systemie Podczas wykonywania ptli na starcie programu sprawdzane jest, czy dysk o danej literze istnieje ? jeeli tak, pobierana jest jego etykieta oraz dodatkowe informacje. Nastpnie wszystko zostaje adnie przedstawione na komponencie TListView w poczeniu z obiektem TImageList. Ostatnia instrukcja (case) sprawdza typ dysku i w zalenoci od niego wywietla okrelon ikon: case Drive_Type of { w zalenoci od rodzaju dysku wywietl ikon } DRIVE_CDROM: ListItem.ImageIndex := 0; DRIVE_FIXED: ListItem.ImageIndex := 1; else ListItem.ImageIndex := ?1; end;

Podsumowanie
Mam nadziej, e w tym rozdziale chocia troch zdoaem wzbudzi Twoj ciekawo i ch tworzenia programw przeznaczonych dla Windows. Wiedza na temat plikw na pewno przyda Ci si w przyszoci i bdziesz z niej czsto korzysta, tym bardziej, e obsuga plikw w Delphi nie jest przesadnie trudna.

316 | S t r o n a

Rozdzia 8
Aplikacje wielowtkowe

Sowo wtek moe mie rne znaczenie. W wiecie programistw moe oznacza moliwo wykonywania wielu czynnoci naraz. Przykadowo w systemie Windows moemy uruchamia kilka programw dziaajcych jednoczenie ? kady program jest osobnym wtkiem. W tym rozdziale zajmiemy si tworzeniem kilku wtkw w ramach jednego procesu. Procesem mona nazwa kad aplikacj, uruchomion w danym momencie. Tak te terminologi bd stosowa w dalszej czci tego rozdziau. Zatem przyjmijmy, e proces to egzemplarz aplikacji uruchomiony w systemie.

Czym tak naprawd s wtki?


Zacznijmy od wyjanienia, czym tak naprawd s wtki. Kada aplikacja (proces) dziaajca w systemie Windows posiada tzw. wtek gwny (ang. primary thread), ktry moe uruchamia inne wtki poboczne (ang. secondary threads). W tym samym czasie moe dziaa kilka wtkw pobocznych, ktre wykonuj rne lub te same operacje. Spjrz na rysunek 8.1. Program przedstawiony na tym rysunku dokonuje wyszukiwania wielowtkowego, analizujc jednoczenie wszystkie dyski znajdujce si w systemie.

Rysunek 8.1. Wyszukiwanie wielowtkowe

317 | S t r o n a

W tym wypadku zadaniem kadego wtku jest wyszukanie plikw na osobnym dysku. W rezultacie jeden wtek przypada na kady dysk, dziki czemu wyszukiwanie trwa naprawd szybko. Peny kod rdowy programu Wyszukiwanie wielowtkowe moesz znale na pycie CD-ROM w katalogu ../listingi/8/Wyszukiwarka. By moe to, co napisaem do tej pory przybliyo Ci troch zasad funkcjonowania wtkw. Wyobra sobie moliwo wykonywania innych czynnoci w tle aplikacji ? bez jej jednoczesnego blokowania. Dajesz uytkownikowi moliwo dokonywania zmian w programie, a w tle moe dziaa inny wtek, ktry wykonywa bdzie pozostae operacje.

Klasa TThread
Podczas tworzenia aplikacji wielowtkowych bdziemy korzystali z klasy VCL ? TThread. Istnieje oczywicie moliwo tworzenia wtkw przy wykorzystaniu mechanizmw WinAPI, lecz klasa TThread w duym stopniu zwalnia nas z mozolnego kodowania ? jest po prostu atwiejsza w obsudze. Klasa TThread znajduje si w module Classes.pas.

Deklaracja klasy TThread


Deklaracja klasy TThread znajduje si w pliku Classes.pas i przedstawia si w nastpujcy sposb: TThread = class private FHandle: THandle; FThreadID: THandle; FTerminated: Boolean; FSuspended: Boolean; FFreeOnTerminate: Boolean; FFinished: Boolean; FReturnValue: Integer; FOnTerminate: TNotifyEvent; FMethod: TThreadMethod; FSynchronizeException: TObject; procedure CallOnTerminate; function GetPriority: TThreadPriority; procedure SetPriority(Value: TThreadPriority); procedure SetSuspended(Value: Boolean); protected procedure DoTerminate; virtual; procedure Execute; virtual; abstract; procedure Synchronize(Method: TThreadMethod); property ReturnValue: Integer read FReturnValue write 318 | S t r o n a

FReturnValue; property Terminated: Boolean read FTerminated; public constructor Create(CreateSuspended: Boolean); destructor Destroy; override; procedure Resume; procedure Suspend; procedure Terminate; function WaitFor: LongWord; property FreeOnTerminate: Boolean read FFreeOnTerminate write FFreeOnTerminate; property Handle: THandle read FHandle; property Priority: TThreadPriority read GetPriority write SetPriority; property Suspended: Boolean read FSuspended write SetSuspended; property ThreadID: THandle read FThreadID; property OnTerminate: TNotifyEvent read FOnTerminate write FOnTerminate; end;

Dziaanie wtku mona wstrzyma lub wznowi dziki metodom Suspend i Resume. Rozpoczcie wtku jest jednak realizowane za pomoc metody Execute.

Tworzenie nowej klasy wtku


Jeeli chcemy utworzy nowy wtek, jedynym rozwizaniem jest zadeklarowanie w kodzie programu nowej klasy, dziedziczcej po TThread. Klas t moemy samodzielnie wpisa bezporednio w kod programu lub skorzysta z kreatora Delphi. Z menu File wybierz New/Other, co spowoduje otwarcie Repozytorium (o Repozytorium pisaem w rozdziale 4.). Wystarczy na zakadce New wybra pozycj Thread Object (rysunek 8.2).

319 | S t r o n a

Rysunek 8.2. Okno Repozytorium Po naciniciu przycisku OK zostaniesz poproszony o wpisanie nazwy klasy w odpowiednim oknie. Wpisz np. TMojWatek. Wwczas stworzony zostanie nowy modu, a w nim deklaracja nowej klasy (patrz listing 8.1). Listing 8.1. Kod rdowy nowego moduu wygenerowanego przez Delphi unit Unit2; interface uses Classes; type TMojWatek = class(TThread) private { Private declarations } protected procedure Execute; override; end; implementation { Important: Methods and properties of objects in visual components can only be used in a method called using Synchronize, for example, Synchronize(UpdateCaption); and UpdateCaption could look like, 320 | S t r o n a

procedure TMojWatek.UpdateCaption; begin Form1.Caption := 'Updated in a thread'; end; } { TMojWatek } procedure TMojWatek.Execute; begin { Place thread code here } end; end.

Nowy modu zawiera klas TMojWatek, w ktrej umieszczona jest jedna metoda (w sekcji protected). To wanie w metodzie Execute naley umieci waciwy kod wtku. Ponadto w module znajduje si ciekawy komentarz, ktry zostanie przeze mnie omwiony w dalszej czci rozdziau. W kadym bd razie nie jest konieczne tworzenie nowego moduu dla klasy wtku. Nie jest take konieczne tworzenie samej klasy w taki sposb, w jaki to przedstawiem. Rwnie dobrze mona zadeklarowa klas samodzielnie. Podczas samodzielnego deklarowania klasy dziedziczcej po TThread nie wolno zapomina o deklaracji metody Execute. Metoda Execute musi by umieszczona w sekcji protected i opatrzona dyrektyw override.

Kilka instancji wtku


W kadej klasie wtku mog by oczywicie deklarowane metody i waciwoci ? zupenie tak samo, jakby to bya zwyka klasa. Istnieje take moliwo uruchamiania kilku klas wtku jednoczenie! Powoduje to stworzenie dla kadej klasy osobnej instancji zmiennej i zarezerwowanie osobnego bloku pamici. Tworzenie wtku przedstawia si nastpujco: TMojWatek.Create(False);

Po wywoaniu konstruktora klasy uruchamiany jest cay proces (metoda Execute), a to za spraw parametru typu Boolean zawartego w konstruktorze. Jeeli warto tego parametru to True, uruchomienie wtku nastpi dopiero po wywoaniu metody Resume. Nie zaleca si uruchamiania w tym samym czasie duej iloci wtkw w ramach tego samego procesu. Zalecana ilo to 16 wtkw w ramach jednego procesu.

321 | S t r o n a

Tworzenie klasy
Przedstawi Ci teraz przykadowy program tworzcy trzy wtki pochodne, ktre bd dziaa jednoczenie. Ich dziaanie nie spowoduje zablokowania programu ? uytkownik bdzie mg przeciga okno programu, minimalizowa go itp. Przykadowy program bdzie banalny i raczej niepraktyczny. Wtek wylosuje jak liczb z zakresu 0?999 i wykona ptle for od liczby 1 do tej wylosowanej wartoci. Ptla bdzie wykonywana tylko przez jaki czas ?dziki spowalnianiu (funkcja Sleep). Przerwa midzy kolejnymi iteracjami to 100 milisekund. Program przedstawiony zosta na rysunku 8.3.

Rysunek 8.3. Dziaanie trzech wtkw naraz Postp wykonywania ptli przedstawiony jest za pomoc komponentw TProgressBar.

Kod klasy
Deklaracja klasy jest do prosta ? wykorzystujemy jedn metod, konstruktor oraz dwie waciwoci: type TGoThread = class(TThread) private FV : Integer; // wylosowana liczba FCounter : Integer; // numer wtku protected procedure Execute; override; public constructor Create(Counter : Integer); end;

Deklarowanie konstruktora przez programist nie jest konieczne, lecz ja stworzyem go ze wzgldu na konieczno przekazania do klasy pewnego parametru, jakim jest numer wtku:

322 | S t r o n a

constructor TGoThread.Create(Counter: Integer); begin inherited Create(False); // wywoanie wtku FCounter := Counter; // przypisanie wartoci do zmiennej end;

Na pocztku w konstruktorze wywoujemy konstruktor klasy bazowej. Nastpnie zmiennej (polu) FCounter przypisujemy warto, ktra zostaa podana wraz z parametrem konstruktora. Oto, jak wyglda gwna procedura ? Execute: procedure TGoThread.Execute; var i : Integer; begin FreeOnTerminate := True; // zwolnij po zakoczeniu wtku Randomize; FV := Random(1000); { odnalezienie komponentu na formularzu } TProgressBar(MainForm.FindComponent('ProgressBar' + InttoStr(FCounter))).Max := FV; for i := 0 to FV do begin Sleep(10); TProgressBar(MainForm.FindComponent('ProgressBar' + IntToStr(FCounter))).Position := i; end; end;

Zwr uwag na przypisanie do waciwoci FreeOnTerminate wartoci True. Spowoduje to zwolnienie klasy po zakoczeniu dziaania wtku. Kolejne instrukcje s ju cile zwizane z dziaaniem owego wtku. Ciekaw konstrukcj jest: TProgressBar(MainForm.FindComponent('ProgressBar' + InttoStr(FCounter))).Max := FV;

Taki zapis umoliwia znalezienie na formularzu komponentu bez znajomoci jego nazwy. Wystarczy jedynie poda nazw komponentu w parametrze funkcji FindComponent. Kompletny kod rdowy moduu znajduje si w listingu 8.2. Listing 8.2. Kod rdowy moduu 323 | S t r o n a

unit MainFrm; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls, ComCtrls; type TMainForm = class(TForm) gbHome: TGroupBox; ProgressBar1: TProgressBar; ProgressBar2: TProgressBar; ProgressBar3: TProgressBar; Label1: TLabel; Label2: TLabel; Label3: TLabel; btnGo: TButton; procedure btnGoClick(Sender: TObject); private { Private declarations } public { Public declarations } end; TGoThread = class(TThread) private FV : Integer; // wylosowana liczba FCounter : Integer; // numer wtku protected procedure Execute; override; public constructor Create(Counter : Integer); end; var MainForm: TMainForm; implementation {$R *.dfm} procedure TMainForm.btnGoClick(Sender: TObject); begin { utworzenie trzech wtkw } TGoThread.Create(1); TGoThread.Create(2); TGoThread.Create(3); end; { TGoThread } constructor TGoThread.Create(Counter: Integer); begin inherited Create(False); // wywoanie wtku 324 | S t r o n a

FCounter := Counter; // przypisanie wartoci do zmiennej end; procedure TGoThread.Execute; var i : Integer; begin FreeOnTerminate := True; // zwolnij po zakoczeniu wtku Randomize; FV := Random(1000); { odnalezienie komponentu na formularzu } TProgressBar(MainForm.FindComponent('ProgressBar' + InttoStr(FCounter))).Max := FV; for i := 0 to FV do begin Sleep(10); TProgressBar(MainForm.FindComponent('ProgressBar' + IntToStr(FCounter))).Position := i; end; end; end.

Wznawianie i wstrzymywanie wtkw


Klasa TThread posiada metody, dziki ktrym moemy wznowi lub zatrzyma wykonywanie danego wtku. Zadanie wstrzymywania i wznawiania wykonywania danego wtku realizuje metoda Suspend i Resume. var MojWatek : TMojWatek; begin MojWatek := TMojWatek.Create(True); MojWatek.Resume; // uruchomienie wtku MojWatek.Suspend; // wstrzymanie end;

O tym, czy wtek jest w danym momencie uruchomiony, informuje waciwo Suspended. Przyjmuje ona warto True, jeeli wtek jest wstrzymany, natomiast w przeciwnym wypadku ? False.

325 | S t r o n a

Priorytet wtku
Wtkom mona nadawa rne priorytety, zalenie od ?wanoci? zadania, jakie dany wtek wykonuje. Nadajc operacji wyszy priorytet uzyskujesz pewno, e procesor przydzieli czas wykonania wanie naszemu wtkowi. Priorytet nadaje si wtkom poprzez waciwo Priority, wykorzystujc takie oto wartoci: tpIdle, tpLowest, tpLower, tpNormal, tpHigher, tpHighest, tpTimeCritical. Najniszym priorytetem jest tpIdle ? taki wtek jest wykonywany wtedy, gdy aden inny proces nie wymaga uycia procesora (np. wygaszacze ekranu). Natomiast priorytet tpTimeCritical otrzymuj procesy, ktre wymagaj uycia procesora w trybie natychmiastowym. MojWatek.Priority := lpHigher; // nadanie wyszego priorytetu

Nie naley zbytnio przesadza z nadawaniem wtkom priorytetw. Zalecane jest zachowanie priorytetu normalnego (tpNormal). Nadanie wtkowi zbyt wysokiego priorytetu moe spowodowa nieprawidowe dziaanie pozostaych programw uruchomionych w tym samym czasie.

Synchronizacja
Naley rozway jeszcze jedn sytuacj, a mianowicie uruchamianie kilku wtkw w tym samym czasie. Jeeli owe wtki modyfikuj waciwoci lub dokonuj jakichkolwiek innych zmian w bibliotece VCL, moe doj do kolizji. Dotyczy to np. przypadku, gdy owe wtki musz pobiera jakie wartoci z komponentw i jednoczenie je modyfikowa. W tym celu zalecane jest uycie metody Synchronize klasy TThread. procedure TGoThread.Execute; begin Synchronize(SetProprties); end;

W ten sposb wtek wywouje metod Synchronize, w ktrej podana zostaa nazwa procedury do wykonania ? SetProprties. Dziki temu masz pewno, e spord kilku uruchomionych w danym momencie funkcji tylko jedna bdzie wykonywana w danym czasie i tylko ona bdzie moga dokonywa zmian w bibliotece VCL.

326 | S t r o n a

Tre komentarza
Na pocztku rozdziau chcc stworzy nowy wtek, uye Repozytorium. W module, ktry zosta utworzony przez Delphi, widnia taki komentarz: { Important: Methods and properties of objects in visual components can only be used in a method called using Synchronize, for example, Synchronize(UpdateCaption); and UpdateCaption could look like, procedure TMojWatek.UpdateCaption; begin Form1.Caption := 'Updated in a thread'; end; }

Oto jego tumaczenie: Wane! Metody i waciwoci obiektw VCL mog by uyte jedynie w metodzie wywoywanej za pomoc Synchronize. Pamitasz jeszcze program, ktry prezentowaem Ci kilka stron wczeniej (trzy wtki modyfikujce waciwo Position komponentu TProgressBar)? W listingu 8.3 zaprezentowany jest program wykorzystujcy metody Synchronize, dziki ktrej w jednym momencie dostp do komponentw VCL ma tylko jeden wtek. Listing 8.3. Dostp do VCL ma tylko jeden wtek unit MainFrm; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls, ComCtrls; type TMainForm = class(TForm) gbHome: TGroupBox; ProgressBar1: TProgressBar; ProgressBar2: TProgressBar; ProgressBar3: TProgressBar; Label1: TLabel; Label2: TLabel; Label3: TLabel; btnGo: TButton; procedure btnGoClick(Sender: TObject); private { Private declarations } public { Public declarations } end; 327 | S t r o n a

TGoThread = class(TThread) private FV : Integer; // wylosowana liczba pozycja : Integer; FCounter : Integer; // numer wtku procedure SetProprties; protected procedure Execute; override; public constructor Create(Counter : Integer); end; var MainForm: TMainForm; implementation {$R *.dfm} procedure TMainForm.btnGoClick(Sender: TObject); begin { utworzenie trzech wtkw } TGoThread.Create(1); TGoThread.Create(2); TGoThread.Create(3); end; { TGoThread } constructor TGoThread.Create(Counter: Integer); begin inherited Create(False); // wywoanie wtku FCounter := Counter; // przypisanie wartoci do zmiennej end; procedure TGoThread.Execute; var i : Integer; begin FreeOnTerminate := True; // zwolnij po zakoczeniu wtku Randomize; FV := Random(1000); { odnalezienie komponentu na formularzu } TProgressBar(MainForm.FindComponent('ProgressBar' + InttoStr(FCounter))).Max := FV; for i := 0 to FV do begin Sleep(10); pozycja = i; Synchronize(SetProprties); end; end;

328 | S t r o n a

procedure TGoThread.SetProprties; begin TProgressBar(MainForm.FindComponent('ProgressBar' + IntToStr(FCounter))).Position := pozycja; end; end.

Zdarzenia klasy TThread


Chciaem w tym miejscu wspomnie jeszcze o zdarzeniach, a waciwie o jednym zdarzeniu, znajdujcym si w klasie TThread. To zdarzenie to OnTerminate, ktre moe si przyda, jeeli chcemy przechwyci zakoczenie dziaania wtku. Najlepiej przypisa do zdarzenia odpowiedni procedur w momencie utworzenia klasy, czyli w konstruktorze Create. constructor TMainThread.Create; begin inherited Create(False); OnTerminate := MyTerminate; // przypisanie procedury do zdarzenia end; procedure TMainThread.Execute; begin { kod wtku } end; procedure TMainThread.MyTerminate(Sender: TObject); begin { kod zdarzenia } end;

Uycie metody DoTerminate klasy TThread powoduje wywoanie zdarzenia OnTerminate.

329 | S t r o n a

Przykad: wyszukiwanie wielowtkowe


Na rysunku 8.1 przedstawione zostao dziaanie wielowtkowej wyszukiwarki. Co prawda cay kod rdowy umieszczony jest na doczonej do ksiki pycie CD-ROM, lecz warto omwi jego dziaanie. Zaprezentuj wic proces tworzenia takiego programu krok po kroku.

Jak to dziaa?
Sam proces wyszukiwania opisany zosta w poprzednim rozdziale. Nasz program bdzie si rni tym, e zadaniem kadego wtku bdzie wyszukanie plikw na innej partycji, co w konsekwencji potrwa krcej ni w sytuacji, gdyby miaoby to by realizowane w ramach jednego wtku.

Wyszukiwanie
Procedura wyszukiwania jest podobna do tej, ktr prezentowaem w poprzednim rozdziale. Poniej przedstawiona procedura jest rekurencyjna, czyli ? jak zapewne pamitasz ? realizuje przeszukiwanie rwnie w podkatalogach. procedure Search(StartDir : String); var SR, DR : TSearchRec; Found, FoundFile : Integer; { ta procedura sprawdza, czy na kocu zmiennej znajduje si znak \ ? jeeli tak, nic nie jest wykonywane; jeeli tego znaku brak, zostaje on dodany... } function IsDir(Value : String) : String; begin if Value[Length(Value)] <> '\' then // jeeli na kocu znajdziesz znak Result := Value + '\' else Result := Value; // dodaj go... w przeciwnym wypadku nie wykonuj nic end; begin Found := FindFirst(IsDir(StartDir) + '*.*', faDirectory, DR); // nastpuje pobieranie katalogw z podanej lokalizacji while Found = 0 do // ptelka begin if ((DR.Attr and faDirectory) = faDirectory) and // sprawdza, czy pozycja jest katalogiem ((DR.Name <> '.') and (DR.Name <> '..')) then begin MainForm.StatusBar.SimpleText := IsDir(StartDir) + DR.Name + '\*.*'; // na komponencie wywietl aktualnie przeszukiwany katalog if Pos(FFileName, DR.Name) > 0 then // sprawd, czy w nazwie jest szukany cig znakw MainForm.lbResults.Items.Add(IsDir(StartDir) + DR.Name); { pobierz na razie wszystkie pliki z danego katalogu ? potem je 330 | S t r o n a

przeanalizujemy } FoundFile := FindFirst(IsDir(StartDir) + DR.Name + '\*.*', faAnyFile, SR); while FoundFile = 0 do begin if ((SR.Name <> '.') and (SR.Name <> '..')) then // if Pos(FFileName, SR.Name) > 0 then // nastpuje sprawdzenie, czy plik nie zawiera czci szukanego cigu MainForm.lbResults.Items.Add(IsDir(StartDir) + DR.Name + '\' + SR.Name); FoundFile := FindNext(SR); // kontynuuj przeszukiwanie end; FindClose(SR); // zakocz Search(IsDir(StartDir) + DR.Name); // tutaj nastpuje rekurencja end; Found := FindNext(DR); // kontynuuj end; FindClose(DR); end;

W powyszej procedurze zagniedona jest kolejna ? IsDir. Sprawdza ona, czy na kocu cieki znajduje si znak backslash (\). Jeeli go nie ma, dodaje ten znak, gdy wymagany jest on do prawidowego dziaania funkcji rekurencyjnej. Znalezienie konkretnego pliku jest kwalifikowane za pomoc funkcji Pos. Jeeli dany plik lub katalog zawiera szukany cig znakw (a sprawdza to funkcja Pos), nastpuje wywietlenie cieki w komponencie TListBox.

Obliczanie czasu przeszukiwania


Do obliczenia czasu potrzebnego na przeszukanie konkretnej partycji skorzystamy z funkcji GetTickCount. Funkcja ta zwraca ilo milisekund, jakie upyny od czasu uruchomienia systemu. Wystarczy wic pobra warto pocztkow przed wywoaniem wtku oraz warto kocow po zakoczeniu wykonywania operacji ? np. przy zakoczeniu wtku: destructor TSearchThread.Destroy; begin Stop := GetTickCount; // pobierz czas zakoczenia Total := Stop ? Start; // odejmij czas startu od czasu zakoczenia Total := Total / 1000; // podziel przez 1000, aby uzyska liczb sekund { wywietl na komponencie czas wyszukiwania na danym dysku } MainForm.lbEnd.Items.Add(FDrive + ':\ ? ' + CurrToStr(Total) + ' sek.'); inherited; end; Zmienna Start jest uprzednio pobran wartoci, okrelajc czas rozpoczcia wtku. Finaln warto Total naley podzieli przez 1 000, aby uzyska liczb sekund. 331 | S t r o n a

Kod rdowy moduu


Peny kod rdowy moduu znajduje si w listingu 8.4. Listing 8.4. Kod rdowy moduu { Copyright (c) 2002 by Adam Boduch } unit MainFrm; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls, ExtCtrls, Buttons, ComCtrls; type TMainForm = class(TForm) leFileName: TLabeledEdit; btnFind: TBitBtn; Bevel1: TBevel; lbResults: TListBox; StatusBar: TStatusBar; lbEnd: TListBox; Label1: TLabel; ProgressBar: TProgressBar; procedure btnFindClick(Sender: TObject); end; TSearchThread = class(TThread) private Start, Stop : Integer; // wartoci te przechowuj czas rozpoczcia i zakoczenia dziaania wtku Total : Currency; // warto cakowitego czasu przeszukania FFileName : String; // nazwa pliku do odnalezienia FDrive : Char; // dysk, na ktrym odbdzie si szukanie procedure MyOnTerminate(Sender: TObject); // obsuga zdarzenia OnTerminate public constructor Create(const FileName : String; Drive : Char); // konstruktor dla klasy destructor Destroy; override; // destruktor dla klasy procedure SearchInDrive; // procedura poszukiwawcza protected procedure Execute; override; end; var MainForm: TMainForm; SearchThread : TSearchThread; implementation 332 | S t r o n a

{$R *.dfm} constructor TSearchThread.Create(Const FileName : String; Drive : Char); begin inherited Create(False); // wywoanie konstruktora klasy bazowej FreeOnTerminate := True; // zwolnij przy zakoczeniu OnTerminate := MyOnTerminate; // przypisz procedur zdarzenia FFileName := FileName; // nazwa pliku do znalezienia FDrive := Drive; // dysk Start := GetTickCount; // pobierz czas startu (w milisekundach) end; destructor TSearchThread.Destroy; begin Stop := GetTickCount; // pobierz czas zakoczenia Total := Stop ? Start; // odejmij czas startu od czasu zakoczenia Total := Total / 1000; // podziel przez 1000, aby uzyska liczb sekund { wywietl na komponencie czas wyszukiwania na danym dysku } MainForm.lbEnd.Items.Add(FDrive + ':\ ? ' + CurrToStr(Total) + ' sek.'); inherited; end; procedure TSearchThread.SearchInDrive; procedure Search(StartDir : String); var SR, DR : TSearchRec; Found, FoundFile : Integer; { ta procedura sprawdza, czy na kocu zmiennej znajduje si znak \ ? jeeli tak, nic nie jest wykonywane; jeeli tego znaku brak, zostaje on dodany... } function IsDir(Value : String) : String; begin if Value[Length(Value)] <> '\' then // jeeli na kocu znajdziesz znak Result := Value + '\' else Result := Value; // dodaj go... w przeciwnym wypadku nie wykonuj nic end; begin Found := FindFirst(IsDir(StartDir) + '*.*', faDirectory, DR); // nastpuje pobieranie katalogw z podanej lokalizacji while Found = 0 do // ptelka begin if ((DR.Attr and faDirectory) = faDirectory) and // sprawdza, czy pozycja jest katalogiem ((DR.Name <> '.') and (DR.Name <> '..')) then begin 333 | S t r o n a

MainForm.StatusBar.SimpleText := IsDir(StartDir) + DR.Name + '\*.*'; // na komponencie wywietl aktualnie przeszukiwany katalog if Pos(FFileName, DR.Name) > 0 then // sprawd, czy w nazwie jest szukany cig znakw MainForm.lbResults.Items.Add(IsDir(StartDir) + DR.Name); { pobierz na razie wszystkie pliki z danego katalogu ? potem je przeanalizujemy } FoundFile := FindFirst(IsDir(StartDir) + DR.Name + '\*.*', faAnyFile, SR); while FoundFile = 0 do begin if ((SR.Name <> '.') and (SR.Name <> '..')) then // if Pos(FFileName, SR.Name) > 0 then // nastpuje sprawdzenie, czy plik nie zawiera czci szukanego cigu MainForm.lbResults.Items.Add(IsDir(StartDir) + DR.Name + '\' + SR.Name); FoundFile := FindNext(SR); // kontynuuj przeszukiwanie end; FindClose(SR); // zakocz Search(IsDir(StartDir) + DR.Name); // tutaj nastpuje rekurencja end; Found := FindNext(DR); // kontynuuj end; FindClose(DR); end; begin Search(FDrive + ':\'); end;

// rozpocznij wyszukiwanie na danym dysku

procedure TSearchThread.Execute; begin (SearchInDrive); // wywoaj procedur... end; procedure TSearchThread.MyOnTerminate(Sender: TObject); begin { podczas koczenia wyszukiwania wywietl na komponencie ilo odnalezionych pozycji } MainForm.ProgressBar.Position := MainForm.ProgressBar.Position + 1; MainForm.StatusBar.SimpleText := 'Znaleziono: ' + IntToStr(MainForm.lbResults.Items.Count) + ' plikw...'; end;

procedure TMainForm.btnFindClick(Sender: TObject); var i : Integer; DriveType : Integer; begin lbResults.Clear; // wyczy komponent 334 | S t r o n a

lbEnd.Clear; // wyczy komponent ProgressBar.Max := 0; // ustaw warto maksymaln na 0 ProgressBar.Position := 0; // pozycja na 0 for I := Ord('A') to Ord('Z') do // ptla po wszystkich dyskach begin DriveType := GetDriveType(PChar(chr(i) + ':\')); // pobierz informacje o dysku if not (DriveType = 0) and not (DriveType = 1) then // jeeli dysk istnieje begin ProgressBar.Max := ProgressBar.Max + 1; // zwiksz waciwo maks. o jeden SearchThread := TSearchThread.Create(leFileName.Text, Chr(i)); // wywoaj wtek z liter dysku jako pocztkowy parametr end; end; end; end.

Podsumowanie
W tym rozdziale przedstawiem Ci zasad dziaania wtkw. Myl, e po dokadniejszym zapoznaniu si z tym zagadnieniem nie wyglda ona tak strasznie, tym bardziej, e nie jestemy zmuszeni do korzystania z funkcji WinAPI, ale uywamy wygodnej klasy VCL. Na pewno nieraz bdziesz w swojej aplikacji wykorzystywa wtki? Zaczniki:

Listingi_8.zip (7.95 kB)

335 | S t r o n a

Rozdzia 9
Multimedia
Na samym pocztku wypadaoby wyjani, co kryje si pod pojciem multimedia. Najprociej rzecz ujmujc, jest to nazwa okrelajca techniki komputerowe suce do przekazywania informacji, w tym rne rodki przekazu. Mam na myli dwik, obraz, film, animacj itp.

Rozdzia 9. bdzie wanie powicony tworzeniu grafiki, odtwarzaniu dwikw, wywietlaniu filmw oraz tworzeniu animacji. Nie oczekuj, e od razu bdziesz projektowa wietne animacje ? do stworzenia takiej grafiki, jak moemy zobaczy w grach, potrzebna jest znajomo programowania OpenGL lub DirectX, a to wykracza poza ramy tej ksiki.

Z czego bdziemy korzysta?


W Delphi nie mamy zbyt duego wyboru narzdzi, jakimi moglibymy si posugiwa. Malowanie po formularzu oglnie polega na zastosowaniu rnych funkcji WinAPI, lecz zostao ono nieco uproszczone dziki klasie TCanvas (ptno). Moim skromnym zdaniem Delphi nie nadaje si do tworzenia zaawansowanej grafiki, mimo e posiada wspomaganie narzdzi typu OpenGL czy DirectX. Jednak jest to tylko moje zdanie; wiele osb uwaa, e jest wrcz przeciwnie ? w Delphi mona tworzy wspaniae programy z grafik.

OpenGL
OpenGL, czyli Open Graphics Library, to biblioteka suca do tworzenia aplikacji graficznych. W rzeczywistoci jest to may plik DLL, ktry uatwia tworzenie rnych aplikacji wykorzystujcych skomplikowane efekty graficzne (3D, 2D, obroty, wiata itp.). Wedug niektrych obsuga tej biblioteki jest ?czarn magi?, a dla innych z kolei jest bardzo prosta. Jedno jest pewne: aby programowa przy uyciu OpenGL, potrzebna jest dobra znajomo matematyki oraz fizyki. Listing 9.1 przedstawia bardzo prosty program napisany przy uyciu biblioteki OpenGL, wywietlajcy prosty trjkt. Listing 9.1. Prosty program napisany przy uyciu OpenGL unit MainFrm; interface uses Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, OpenGL; type TMainForm = class(TForm) procedure FormCreate(Sender: TObject); procedure FormDestroy(Sender: TObject); 336 | S t r o n a

procedure FormResize(Sender: TObject); private RC : HGLRC; // rendering DC : HDC; // modu procedure glDraw; procedure Idle(Sender: TObject; var Done: Boolean); end; var MainForm: TMainForm; implementation {$R *.DFM} procedure TMainForm.glDraw(); begin glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT); // czyszczenie glLoadIdentity(); // take czyszczenie // procedura tworzca wierzchoki w przestrzeni (tutaj trjkty) glTranslate(0, -1, -2); // nasza pozycja lub raczej przesunicie o wektor wierzchokw // przypominam, e w OpenGL wystpuje system x,y,z glRotatef(10, 20, -10, 0); // obrcenie widoku o dany kt // procedura rysujca trjkt glBegin(GL_TRIANGLES); glVertex3f(0, 0, 0); glVertex3f(0.5, 0.5, 0); glVertex3f(1, 0, 0); glEnd(); // procedura rysujca zwroty x,y,z, gdzie: // x - czerwony, // y - zielony, // z - niebieski. glColor3f(1, 0, 0); glBegin(GL_LINES); glVertex3f(0, 0, 0); glVertex3f(2, 0, 0); glColor3f(0, 1, 0); glVertex3f(0, 0, 0); glVertex3f(0, 2, 0); glColor3f(0, 0, 1); glVertex3f(0, 0, 0); glVertex3f(0, 0, 2); glEnd(); glColor3f(1, 1, 1); end; procedure glInit(); begin glClearColor(0.0, 0.0, 0.0, 0.0); // kolor wypenienia: r - czerwony, g - zielony, b - niebieski, a - przezroczysto glShadeModel(GL_SMOOTH); glClearDepth(1.0); glEnable(GL_DEPTH_TEST); 337 | S t r o n a

glDepthFunc(GL_LESS); glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST); end; procedure TMainForm.FormCreate(Sender: TObject); var pFd : TPIXELFORMATDESCRIPTOR; pF : Integer; begin DC := GetDC(Handle); // uchwyt moduu OpenGL pFd.nSize := SizeOf(pFd); pFd.nVersion := 1; pFd.dwFlags := PFD_DRAW_TO_WINDOW or PFD_SUPPORT_OPENGL or PFD_DOUBLEBUFFER or 0; pFd.iPixelType := PFD_TYPE_RGBA; pFd.cColorBits := 32; pF := ChoosePixelFormat(DC, @pFd); SetPixelFormat(DC, pF, @pFd); RC := wglCreateContext(DC); wglMakeCurrent(DC, RC); Resize; Application.OnIdle := Idle; renderowania glInit; end;

// nasza scenka

//ustalenie wartoci cigego

procedure TMainForm.FormDestroy(Sender: TObject); begin { Usuniecie uchwytw z pamici } wglMakeCurrent(0,0); wglDeleteContext(rc); end; procedure TMainForm.Idle(Sender: TObject; var Done: Boolean); begin glDraw(); // renderowanie sceny SwapBuffers(DC); // przeniesienie z bufora na ekran end; procedure TMainForm.FormResize(Sender: TObject); begin glViewport(0, 0, Width, Height); // okienko (rozmiary) glMatrixMode(GL_PROJECTION); glLoadIdentity(); gluPerspective(110.0, Width / Height, 0.01, 100.0); { pierwsza warto (110.0) to kt widzenia, domylnie 90, druga to kalkulacja perspektywy - zawsze szeroko/wysoko, trzecia - z jakiej najbliszej odlegoci nie rysowa sceny, czwarta - z jakiej najdalszej odlegoci nie rysowa sceny; } glMatrixMode(GL_MODELVIEW); end; end. 338 | S t r o n a

Jak mwiem, z OpenGL wie si sporo dziaa matematycznych, lecz przed ich uyciem naley zainicjowa bibliotek. Delphi zawiera modu OpenGL.pas, ktry importuje wszystkie potrzebne procedury z pliku OpenGL.dll i uatwia korzystanie z dobrodziejstw oferowanych nam przez ow bibliotek. Wicej na temat biblioteki OpenGL moesz dowiedzie si z ksiek Wydawnictwa Helion: OpenGL. Ksiga eksperta oraz Programowanie gier w OpenGL. Informacje na temat tej biblioteki moesz znale take pod adresami: http://www.opengl.org ? strona powicona OpenGL; http://www.eecs.tulane.edu/www/Terry/OpenGL/Introduction.html ? kurs OpenGL.

DirectX
Odpowiedzi Microsoftu na powstanie biblioteki OpenGL bya biblioteka DirectX. DirectX obejmuje wikszy zakres multimediw, jak np. obsug dwiku lub urzdze typu dojstik. Wad DirectX jest to, e obsuguje go jedynie system Windows ? w odrnieniu od OpenGL, ktry dostpny jest praktycznie dla kadej platformy. Jeli chcemy napisa program w oparciu o DirectX, sprawa jest nieco prostsza, a to dziki komponentom z serii DelphiX. Naley je pobra z Internetu i zainstalowa (instalacj komponentw zajmiemy si w III czci ksiki) ? wwczas na palecie komponentw znajdzie si kilka kontrolek, dziki ktrym tworzenie aplikacji w oparciu o DirectX stanie si o wiele atwiejsze.

Tworzenie bitmap
Na pocztek zajmiemy si rzecz najprostsz ? adowaniem, wywietlaniem i zapisywaniem plikw typu *.bmp, czyli inaczej mwic ? bitmap. VCL jak zawsze o wiele bardziej uatwia spraw ? dziki tej bibliotece wywietlenie obrazka staje si bardzo proste. Moemy wykorzysta chociaby komponent TImage.

Korzystanie z komponentu TImage


Komponent TImage jest bardzo przydatny, jeli chodzi o wywietlanie obrazkw, a jednoczenie bardzo prosty w uyciu. Aby zaadowa obrazek do komponentu jeszcze w trakcie projektowania programu, wystarczy skorzysta z waciwoci Picture (rysunek 9.1).

Rysunek 9.1. Zaznaczona waciwo Picture

339 | S t r o n a

Kliknicie przycisku wielokropka spowoduje wywietlenie edytora obrazkw (Picture Editor) ? patrz rysunek 9.2.

Rysunek 9.2. Okno Picture Editor Nacinicie przycisku Load spowoduje wywietlenie okna sucego do wyboru pliku graficznego. Nastpnie taki plik graficzny zostanie umiejscowiony w komponencie TImage i skompilowany wraz z programem (wczony do wynikowego pliku EXE). Usunicie owego pliku nastpi po naciniciu przycisku Clear. adowanie obrazkw w trakcie dziaania programu 1. 2. 3. 4. Umie na formularzu komponent TImage Waciwoci Align komponentu TImage nadaj warto alClient. Zmie waciwo Stretch komponentu TImage na True. Wygeneruj zdarzenie OnCreate formularza i umie w nim taki kod:

procedure TMainForm.FormCreate(Sender: TObject); begin Image.Picture.Bitmap.LoadFromFile('helion.bmp'); end;

Na samym pocztku po umieszczeniu komponentu na formularzu i rozcigniciu go na ca szeroko nadae waciwoci Stretch warto True. Spowoduje to, e kady obrazek zaadowany do komponentu zostanie rozcignity lub zwony tak, aby dostosowa si do rozmiarw komponentu (rysunek 9.3).

340 | S t r o n a

Rysunek 9.3. Program w trakcie dziaania Powrmy do kodu, ktry powoduje zaadowanie bitmapy. Teraz wanie masz okazj doceni zalety biblioteki VCL ? wystarczy jeden wiersz kodu! Najpierw za pomoc operatora kropki (.) odwoujemy si do obiektu TPicture, a nastpnie do obiektu TBitmap. Klasa TBitmap zawiera natomiast procedur LoadFromFile, ktra umoliwia zaadowanie obrazka z pliku. Podczas adowania obrazka naley zwrci uwag, czy jest on rzeczywicie zapisany w formacie BMP. W przeciwnym wypadku wykonany zostanie wyjtek EInvalidType.

Klasa TBitmap
Do operowania na samych plikach *.bmp suy klasa TBitmap. Dziki niej moliwe jest dokonywanie operacji pamiciowych, tzn. operacji na bitmapie odbywajcych si w pamici komputera i nie dajcych rezultatw dla uytkownika. Przed wykorzystaniem metod klasy TBitmap naley j utworzy: var Bitmap : TBitmap; begin Bitmap := TBitmap.Create; try { operacje na bitmapie } finally Bitmap.Free; // zwolnienie klasy end; end;

Tworzenie oraz niszczenie obiektu odbywa si identycznie jak w przypadku innych klas VCL. W tabelach 9.1 oraz 9.2 przedstawiam najwaniejsze waciwoci oraz metody klasy TBitmap.

341 | S t r o n a

Tabela 9.1. Najwaniejsze waciwoci klasy TBitmap Waciwo Canvas Empty Height Opis Wskazanie na klas TCanvas ? zajmiemy si tym nieco pniej Waciwo przybiera warto True, jeli bitmapa nie jest zaadowana Wysoko bitmapy (w pikselach) Moesz przydzieli tej waciwoci warto True, jeli szybko adowania jest priorytetowa ? wwczas wywietlana bitmapa bdzie posiadaa jedynie 255 kolorw Okrela format pikseli: pf1bit (1 bit na piksel ? bitmapa czarno-biaa), pf4bit, pf8bit, pf15bit, pf16bit, pf24bit, pf32bit, pfCustom (nieokrelone). Zwraca kolor pierwszego piksela w bitmapie (jeeli TransparentMode jest ustawione na tmAuto)

IgnorePalette

PixelFormat

TransparentColor

Okrela rodzaj przezroczystoci. Jeeli waciwo ma ustawion warto tmAuto, to kolor przezroczystoci jest okrelany na podstawie lewego dolnego piksela. TransparentMode Jeeli waciwo ma ustawion warto tmFixed, oznacza to, e kolor przezroczystoci ma by odczytany z obiektu Transparent Width Modified Waciwo okrela sposb malowania bitmapy. Po ustawieniu wartoci True bitmapa bdzie przezroczysta Okrela szeroko bitmapy w pikselach Waciwo okrela, czy bitmapa zostaa zmodyfikowana

Tabela 9.2. Najwaniejsze metody klasy TBitmap Metoda LoadFromClipboardFormat Opis Procedura powoduje zaadowanie obrazka, ktry znajduje si obecnie w schowku aduje bitmap z zasobw (o tym w dalszej czci rozdziau) na podstawie ID

LoadFromResourceID

LoadFromResourceName aduj bitmap z zasobw na podstawie nazwy zasobu

342 | S t r o n a

LoadFromStream LoadFromFile SaveToFile SaveToStream SaveToClipboardFormat FreeImage

aduje bitmap, ktra jest zapisana w strumieniu (TStream) aduje bitmap z pliku Zapisuje bitmap do pliku Zapisuje bitmap do strumienia (TStream) Kopiuj bitmap do schowka Zwalnia obrazek i jednoczenie take pami

Odczytywanie obrazka ze schowka


Moe zboczymy w tym momencie troch z tematu grafiki, albowiem zamierzam opisa sposoby wykorzystania schowka w operowaniu grafik, a dokadnie zapisywanie do schowka oraz odczytywanie. Schowek jest specjalnym mechanizmem systemu Windows, umoliwiajcym zapisywanie i przechowywanie dowolnych informacji (tekst, grafika) na potrzeby jednego lub kilku programw. Zapis oraz odczyt danych ze schowka umoliwiaj dwie funkcje klasy TBitmap: LoadFromClipboardFormat oraz SaveToClipboardFormat. procedure SaveToClipboardFormat(var AFormat: Word; var AData: THandle; var APalette: HPALETTE); override; procedure LoadFromClipboardFormat(AFormat: Word; AData: THandle; APalette: HPALETTE); override;

Oglnie do wykorzystania schowka suy modu Clipbrd, wic na samym pocztku bdziesz musia doda go do listy uses. Operowanie klas TClipBoard (znajduje si ona w module Clipbrd) jest cakiem proste. Zapisywanie i odczytywanie danych moe odbywa si za pomoc procedur GetAsHandle oraz SetAsHandle. Oglnie w schowku moe by kilka rodzajw danych (patrz tabela 9.3), lecz my chcemy odczyta jedynie dane w postaci bitmapy (CF_BITMAP).

343 | S t r o n a

Tabela 9.3. Rodzaje danych mogcych znale si w schowku Flaga CF_TEXT CF_BITMAP Tekst Grafika w postaci bitmapy Rodzaj danych

CF_METAFILEPICT Plik metafile CF_PICTURE Zdjcie (obiekt typu TPicture)

CF_COMPONENT Dowolny inny obiekt

Odczyt danych

Image.Picture.Bitmap.LoadFromClipboardFormat(CF_BITMAP, ClipBoard.GetAsHandle(CF_BITMAP), 0);

W pierwszym parametrze podajemy rodzaj danych do odczytania; drugi parametr to ju wywoanie funkcji GetAsHandle z klasy TClipBoard. Funkcja ta ma za zadanie odczyta dane i zwraca do nich uchwyt (THandle). Wczeniej wypadaoby sprawdzi, czy w schowku rzeczywicie znajduj si dane w postaci bitmapy ? inaczej Delphi wywoa wyjtek: if ClipBoard.HasFormat(CF_BITMAP) then { tutaj kod }

Funkcja HasFormat zwraca True, jeeli w schowku znajduj si dane okrelone w parametrze (w tym wypadku CF_BITMAP).

Zapisywanie danych Zapisywanie danych jest nieco trudniejsze, gdy w funkcji SaveToClipboardFormat parametry musz by zmiennymi. Image.Picture.Bitmap.SaveToClipboardFormat(wFormat, AHandle, APalette); ClipBoard.SetAsHandle(wFormat, AHandle);

344 | S t r o n a

Funkcja SaveToClipboardFormat powoduje przypisanie do zmiennej wFormat nowej wartoci ? tak samo jest w wypadku zmiennej AHandle. Dopiero teraz mona wywoa polecenie SetAsHandle, zapisujce dane do schowka. Program w trakcie dziaania przedstawiony zosta na rysunku 9.4., a jego kod rdowy znajduje si w listingu 9.2.

Rysunek 9.4. Program w trakcie dziaania Listing 9.2. Kod rdowy programu unit MainFrm; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls, ExtCtrls; type TMainForm = class(TForm) gbImage: TGroupBox; Image: TImage; rgSelect: TRadioGroup; btnDoIt: TButton; btnClear: TButton; procedure btnDoItClick(Sender: TObject); 345 | S t r o n a

procedure btnClearClick(Sender: TObject); private { Private declarations } public { Public declarations } end; var MainForm: TMainForm; implementation {$R *.dfm} uses Clipbrd; procedure TMainForm.btnDoItClick(Sender: TObject); var AHandle : THandle; wFormat : WORD; APalette : HPALETTE; begin case rgSelect.ItemIndex of 0: Image.Picture.Bitmap.LoadFromClipboardFormat(CF_BITMAP, ClipBoard.GetAsHandle(CF_BITMAP), 0); 1: begin Image.Picture.Bitmap.SaveToClipboardFormat(wFormat, AHandle, APalette); ClipBoard.SetAsHandle(wFormat, AHandle); end; 2: Image.Picture.Bitmap.LoadFromFile('helion.bmp'); end; end; procedure TMainForm.btnClearClick(Sender: TObject); begin { zwolnij zasoby } Image.Picture.Bitmap.FreeImage; Image.Picture.Assign(nil); end; end.

346 | S t r o n a

Pliki JPEG
Pliki JPEG (albo inaczej ? JPG) s bardzo popularnym formatem zapisu obrazkw. Wszystko dziki znakomitej kompresji, pozwalajcej na due zmniejszenie wielkoci pliku bez duej utraty jakoci obrazu.

Klasa TJPEGImage
Do operowania na plikach JPG suy klasa TJPEGImage, znajdujca si w module JPEG. adowanie i zapisywanie plikw wyglda praktycznie tak samo jak w przypadku bitmap. Jedyna rnica polega na tym, e klasa TJPEGImage przy zapisywaniu stosuje ju kompresj. Obrazek helion.bmp, ktrym posugiwaem si jako przykadem, zosta zmniejszony z 44 KB do 8 KB. Najwaniejsze waciwoci klasy TJPEGImage przedstawione zostay w tabeli 9.4, a metody ? w tabeli 9.5 Tabela 9.4. Najwaniejsze waciwoci klasy TJPEGImage Waciwo Opis

CompressionQuality Okrela jako kompresji ? od 1 do 100 Grayscale Performance Okrela, czy obrazek ma by czarno-biay czy kolorowy Sposb kompresji: jpBestQuality (lepsza jako, wikszy plik), jpBestSpeed (optymalizacja pod wzgldem szybkoci ? mniejsza jako) Sposb wywietlania obrazka: jsFullSize (peny obrazek), jsHalf (poowa), jsQuarter (wiartka), jsEighth (1/8)

Scale

Klasa TJPEGImage posiada take te same metody i waciwoci co TBitmap ? np. Width, Height i Empty. Tabela 9.5. Najwaniejsze metody klasy TJPEGImage Metoda Compress Assign Opis Kompresuje na podstawie ustawie waciwoci takich jak CompressionQuality czy Performance Powoduje ?skopiowanie? danych z innej klasy

347 | S t r o n a

DIBNeeded Dekompresja pliku JPEG do postaci bitmapy

Tak samo, jak w przypadku waciwoci klasa TJPEGImage posiada metody klasy TBitmap (aczkolwiek nie wszystkie), takie jak SaveToFile, LoadFromFile, SaveToStream itp.

Wywietlanie obrazkw w komponencie TImage


Warto wspomnie o jeszcze jednej kwestii dotyczcej wywietlenia obrazkw w komponencie TImage. Jak dotd uywalimy tego komponentu jedynie do wywietlania bitmap. Oczywicie nadaje si on rwnie do wywietlania takich plikw, jak np. JPEG, lecz wczeniej konieczne jest dodanie do listy moduw (uses) pliku JPEG. Image.Picture.LoadFromFile('helion.jpg');

Pamitaj, aby nie odwoywa si w przypadku bitmap do klasy TBitmap, ale bezporednio do
TPicture

Przykad dziaania ? kompresja plikw


Na pycie CD-ROM doczonej do ksiki znajduje si katalog ..listingi/9/JPEG/, a w nim projekt JpegApp.dpr. w program umoliwia kompresowanie bitmap do postaci plikw *.jpg. Sama kompresja opiera si wycznie na zastosowaniu jednej procedury: uses jpeg; procedure TMainForm.btnConvertClick(Sender: TObject); var Bitmap : TBitmap; JPG : TJPEGImage; begin Bitmap := TBitmap.Create; try { w przypadku, gdy uytkownik wybierze w oknie plik BMP } if OpenDialog.Execute then begin { zaaduj do klasy } Bitmap.LoadFromFile(OpenDialog.FileName); { miniaturk wywietl rwnie w komponencie TImage } Image.Picture.Bitmap.Assign(Bitmap); { utwrz klas } JPG := TJPEGImage.Create; try { przypisz obrazek z klasy TBitmap } JPG.Assign(Bitmap); if SaveDialog.Execute then { zapisanie ju skompresowanego obrazka } 348 | S t r o n a

JPG.SaveToFile(SaveDialog.FileName); finally JPG.Free; end; end; finally Bitmap.Free; end; end; Oprcz przycisku TButton na formularzu znajduj si take komponenty TImage oraz TOpenDialog i TSaveDialog. Te dwa ostatnie su do wywietlania standardowych ?windowsowych? okien Otwrz plik oraz Zapisz plik. Jak dziaa program? Najpierw, po naciniciu przycisku, uytkownik musi wskaza plik *.bmp, ktry ma zosta skompresowany. W tym momencie zostaje utworzona klasa TBitmap, a podgld obrazka zostaje wywietlony w TImage. Nastpnie uytkownik musi poda now nazw pliku, w ktrym zapisane zostan skompresowane dane. W tym momencie wystarczy pobra dane z klasy TBitmap (za pomoc metody Assign), a nastpnie wywoa metod SaveToFile.

Pliki GIF
Niestety Delphi nie posiada obecnie adnego moduu (komponentu) wspomagajcego wywietlanie plikw (animacji) GIF. Jedynym rozwizaniem jest pobranie z Internetu komponentu o nazwie TGIFImage i zainstalowanie go w Delphi. Instalacj komponentw zajmiemy si w czci III tej ksiki, a sam komponent moesz znale choby na stronie http://4programmers.net.

Zasoby
Sowo zasoby (ang. resources) moe mie wiele znacze, lecz w tym wypadku oznacza dane dodawane i kompilowane wraz z plikiem EXE. Chcc wywietli w trakcie dziaania programu jaki obrazek, musielibymy docza te wszystkie pliki wraz z programem. Moe to si jednak wyda nieco niewygodne ? nie lepiej mie wszystko w jednej paczuszce (pliku)? Zasoby w rzeczywistoci s plikiem o rozszerzeniu *.res lub *.rc, ktry moe by wczany do programu za pomoc dyrektywy: {$R ZASOBY.RES}

Powysza instrukcja moe wydawa si komentarzem, ale w rzeczywistoci jest poleceniem wczenia pliku ZASOBY.RES do skompilowanej wersji programu (plik *.exe). 349 | S t r o n a

Tworzenie zasobw
Najprostszym rodkiem umoliwiajcym stworzenie zasobw jest skorzystanie z edytora (program Image Editor). w edytor jest standardowo doczany do Delphi, lecz w rzeczywistoci stanowi osobn aplikacj; mona go uruchomi, wybierajc z menu Tools polecenie Image Editor (rysunek 9.5).

Rysunek 9.5. Program Image Editor Program Image Editor jest w rzeczywistoci prostym edytorem graficznym, lecz rwnie nadaje si do tworzenia zasobw. Aby utworzy nowy zasb, z menu File naley wybra New/Resource File. W tym momencie zostanie otwarte nowe okno (rysunek 9.6), ktre moe zawiera rne gazie zasobw (bitmapy, ikony, kursory).

Rysunek 9.6. Okienko do tworzenia nowych zasobw 350 | S t r o n a

Doczanie bitmapy Nie zamierzamy tworzy nowej bitmapy, lecz wstawi do zasobw ju istniejc. W tym celu z menu File wybierz Open i znajd na dysku jaki obrazek BMP. Image Editor jest do prostym programem i nie umoliwia wywietlania obrazkw o wikszej liczbie kolorw ni 255. Ominiciem tego problemu zajmiemy si nieco pniej. Najpierw dotychczasowy obrazek musisz skopiowa do schowka, naciskajc kolejno Ctrl+A (zaznaczenie) i Ctrl+C (skopiowanie). Po tym zabiegu moesz ju zamkn okno z bitmap. Przejdmy do naszych zasobw ? kliknij w obszarze tego okna prawym przyciskiem myszy i z menu New wybierz Bitmap. Bdziesz musia wpisa rozmiary nowej bitmapy oraz poda liczb kolorw (rysunek 9.7).

Rysunek 9.7. Tworzenie nowej bitmapy Po tym zabiegu na licie pojawia si nowa ga Bitmap, a w niej nasza bitmapa. Po jej otwarciu zobaczymy puste okno ? tutaj naley wklei skopiowan uprzednio bitmap.

Pozostae zasoby Tworzenie kolejnych zasobw (kursory, ikony) jest bardzo podobne. W wyniku tych dziaa nasze okno z zasobami wzbogaci si o nowe gazie (rysunek 9.8). Teraz nie pozostaje nam ju nic innego, jak zapisa plik (File/Save As) pod nazw resource.res.

Rysunek 9.8. Bitmapa, kursor oraz ikona wczona do pliku zasobw 351 | S t r o n a

Wykorzystanie zasobw
W poprzednim punkcie utworzye plik resource.res, do ktrego wczony by kursor, bitmapa i ikona. Plik ten moesz znale na pycie CD-ROM w katalogu ..listingi/9/Rest. Pierwszym krokiem jest doczenie tego pliku do projektu: {$R RESOURCE.RES}

adowanie bitmapy Podczas omawiania klasy TBitmap wspominaem o funkcji LoadFromResourceName, dziki ktrej w do prosty sposb mona zaadowa obrazek bezporednio z doczonych zasobw: procedure TMainForm.btnLoadBitmapClick(Sender: TObject); begin imgBitmap.Picture.Bitmap.LoadFromResourceName(hInstance, '1st_bitmap'); end;

W pierwszym parametrze tej procedury naley poda uchwyt do instancji programu. Tak si skada, e zasoby mog by take adowane z biblioteki DLL (o tym bdzie mowa w kolejnym rozdziale) ? w takim wypadku naleaoby poda w tym miejscu uchwyt do biblioteki. Moesz jednak przyj, e w wikszoci sytuacji wystarczy sowo kluczowe hInstance. Natomiast drugi parametr procedury LoadFromResourceName to nazwa bitmapy umieszczonej w zasobach. adowanie ikony Ikony w Delphi s reprezentowane przez klas TIcon. Obsuga tej klasy jest niezwykle podobna do TBitmap oraz TJPEGImage ? wikszo waciwoci i metod powtarza si, std postanowiem nie omawia jej dokadniej. adowanie i wywietlanie ikony moe wyglda tak: procedure TMainForm.btnLoadIconClick(Sender: TObject); var Icon : TIcon; begin Icon := TIcon.Create; Icon.Handle := LoadIcon(hInstance, '1st_ico'); // zaaduj ikon z zasobw imgIcon.Picture.Icon := Icon; Icon.Free; end; Niestety klasa TIcon pozbawiona jest funkcji LoadFromResourceName (dostpna jest tylko LoadFromFile), wic aby ?wycign? ikon z zasobw, naley zastosowa metody zastpcze ? w tym wypadku funkcj LoadIcon. Uycie tej funkcji jest podobne do funkcji VCL, lecz zwracany rezultat ma posta typu HICON (typ WinAPI).

352 | S t r o n a

adowanie kursora W Delphi obowizuje tzw. tablica kursorw. Oznacza to, e kady kursor ma swj numer i uycie go wie si z przypisaniem do odpowiedniej waciwoci odpowiedniego numeru ? np. tak: Screen.Cursor := 1;

W takim wypadku caa aplikacja bdzie korzystaa z kursora o numerze 1, lecz wczeniej trzeba taki kursor zaadowa: procedure TMainForm.FormCreate(Sender: TObject); begin { zaaduj kursor do tablicy zasobw } Screen.Cursors[1] := LoadCursor(hInstance, '1st_cur'); { wywietl kursor } Screen.Cursor := 1; end;

Tutaj take korzystamy z funkcji API ? LoadCursor ? i przydzielamy kursor do tablicy Cursors pod numerem 1. W listingu 9.3. znajduje si przykad adowania kursora, ikony oraz bitmapy. Listing 9.3. Kod rdowy programu unit MainFrm; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls, ExtCtrls; {$R RESOURCE.RES} type TMainForm = class(TForm) btnLoadIcon: TButton; imgIcon: TImage; imgBitmap: TImage; btnLoadBitmap: TButton; procedure FormCreate(Sender: TObject); procedure btnLoadIconClick(Sender: TObject); procedure btnLoadBitmapClick(Sender: TObject); private { Private declarations } public { Public declarations } end; var 353 | S t r o n a

MainForm: TMainForm; implementation {$R *.dfm} procedure TMainForm.FormCreate(Sender: TObject); begin { zaaduj kursor do tablicy zasobw } Screen.Cursors[1] := LoadCursor(hInstance, '1st_cur'); { wywietl kursor } Screen.Cursor := 1; end; procedure TMainForm.btnLoadIconClick(Sender: TObject); var Icon : TIcon; begin Icon := TIcon.Create; Icon.Handle := LoadIcon(hInstance, '1st_ico'); // zaaduj ikon z zasobw imgIcon.Picture.Icon := Icon; Icon.Free; end; procedure TMainForm.btnLoadBitmapClick(Sender: TObject); begin imgBitmap.Picture.Bitmap.LoadFromResourceName(hInstance, '1st_bitmap'); end; end.

Rczne tworzenie zasobw


Program Image Editor nie daje nam zbyt duych moliwoci tworzenia bardziej zaawansowanych zasobw, wic naley posuy si nieco bardziej skomplikowanymi metodami ? pisaniem kodu zasobw. Tworzc swoje zasoby rcznie, mamy moliwo wikszego manipulowania danymi, ktre maj si tam znale. Moemy umieci tam dosownie wszystko ? poczwszy od plikw JPG, a skoczywszy na innych programach. Cao opiera si na pisaniu skryptw o rozszerzeniu *.rc. Takie skrypty to w rzeczywistoci zwyke pliki tekstowe zawierajce odpowiednie polecenia. Nastpnie ? za pomoc doczonego do Delphi programu (brcc32.exe) ? s one kompilowane do postaci pliku *.res. Program jest uruchamiany w oknie MS-DOS, a jego rozmiary s bardzo mae. Od razu zalecam skopiowanie go do katalogu, w ktrym ma si odby tworzenie zasobw, gdy w przeciwnym wypadku wykorzystywanie polece systemu MS-DOS bdzie do niewygodne.

354 | S t r o n a

Dodawanie plikw JPEG Pierwszym krokiem jest stworzenie w katalogu z programem pliku files.rc. Plik taki moesz otworzy w kadym edytorze tekstowym i umieci w nim nastpujcy wiersz: PIC JPEGFILE "sfp.jpg" Pierwszy czon tej linii do identyfikator ? sowo, jakie bdzie identyfikowa wanie ten obrazek. Kolejny czon to typ pliku. Podawana tu warto nie jest specjalnie wana; istotna jest jedynie podczas pisania programu adujcego zasoby. Wreszcie ostatni czon ? sowo umieszczone w cudzysowach ? to nazwa pliku przeznaczonego do skompilowania do postaci zasobw. Teraz, majc ju plik przeznaczony do skompilowania, musisz uruchomi w oknie MS-DOS program brcc32.exe z parametrem okrelajcym nazw zasobw ? np.: brcc32.exe files.rc

W wyniku tego zostanie stworzony plik files.res, ktry zawiera ju obrazek JPG. adowanie pliku JPG Poniewa klasa TJPEGImage nie zawiera procedury umoliwiajcej adowanie obrazkw z zasobu, trzeba napisa wasn klas, opart czciowo na strumieniach. Pierwszym krokiem jest deklaracja nowej klasy opartej na TJPEGImage: { klasa dziedziczca po TJPEGImage, ktra posiada jedn dodatkow funkcj adowania z zasobw } TJPEGRes = class(TJPEGImage) public procedure LoadFromResource(const ResID: PChar); virtual; end;

Dziki takiemu zabiegowi nasza nowa klasa ? TJPEGRes ? zawiera wszystkie metody z klasy TJPEGImage, a dodatkowo jeszcze procedur LoadFromResource: procedure TJPEGRes.LoadFromResource(const ResID: PChar); var Res : TResourceStream; // utwrz zmienn begin { aduj obrazek z zasobw } Res := TResourceStream.Create(hInstance, ResID, 'JPEGFILE'); try LoadFromStream(Res); // aduj obrazek do strumienia ze zmiennej Res finally Res.Free; // zwolnij pami end; end; 355 | S t r o n a

Mimo e klasa TJPEGImage jest pozbawiona funkcji LoadFromResourceName, to znajduje si w niej polecenie LoadFromStream, ktre teraz okazao si dla nas bardzo przydatne. Najpierw naleao utworzy klas TResourceStream, ktra posiada bardzo przydatny konstruktor, umoliwiajcy zaadowanie dowolnego pliku wprost z zasobw. Zwr uwag na ostatni parametr konstruktora ? jest to typ zasobw, ktry musi by zgodny z typem, ktry zadeklarowalimy w pliku files.rc. Peny kod rdowy tego programu znajduje si w listingu 9.4. Listing 9.4. adowanie pliku JPG z zasobw unit MainFrm; interface { tutaj przechowywane s zasoby z JPEG } {$R FILES.RES}

uses Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls, ExtCtrls, jpeg; type TMainForm = class(TForm) btnLoadRes: TButton; Image: TImage; procedure btnLoadResClick(Sender: TObject); end; { klasa dziedziczca po TJPEGImage, ktra posiada jedn dodatkow funkcj adowania z zasobw } TJPEGRes = class(TJPEGImage) public procedure LoadFromResource(const ResID: PChar); virtual; end; var MainForm: TMainForm; implementation {$R *.DFM} { TJPEGRes } procedure TJPEGRes.LoadFromResource(const ResID: PChar); var Res : TResourceStream; // utwrz zmienn begin { aduj obrazek z zasobw } Res := TResourceStream.Create(hInstance, ResID, 'JPEGFILE'); try 356 | S t r o n a

LoadFromStream(Res); // aduj obrazek do strumienia ze zmiennej Res finally Res.Free; // zwolnij pami end; end; procedure TMainForm.btnLoadResClick(Sender: TObject); var JPG : TJPEGRes; begin JPG := TJPEGRes.Create; // tworzenie nowej klasy try JPG.LoadFromResource('PIC'); // zaadowanie odpowiedniego zasobu Image.Picture.Assign(JPG); finally JPG.Free; end; end; end.

Doklejanie plikw EXE


Powyszej przedstawiem sposb na adowanie obrazkw z zasobu wykorzystujc klas TResourceStream. Chcc ?wycign? z zasobw inne pliki ? np. aplikacje ? take naley skorzysta z tego sposobu. Do pliku exe_file.rc dodaem tak lini: ASCII EXEFILE "ascii.exe"

W tym wypadku identyfikatorem zasobu jest ASCII, a typ pliku to EXEFILE. ?Wycignicie? zasobu i zapisanie go gdzie na dysku moe wyglda tak: procedure TForm1.btnExtractClick(Sender: TObject); var Res : TResourceStream; begin if SaveDialog.Execute then // jeeli okno zostanie wywietlone begin { wywoaj konstruktor klasy } Res := TResourceStream.Create(Hinstance, 'ASCII', 'EXEFILE'); try Res.SaveToFile(SaveDialog.FileName); // zapisz zasb do pliku finally Res.Free; end; end; end; 357 | S t r o n a

Klasa TCanvas
W Delphi dostpna jest niezwykle wygodna i prosta w uyciu klasa TCanvas, ktra uwalnia nas od nieco mudnego wykorzystywania funkcji API, sucych do wykonywania prostych czynnoci graficznych (wywietlenie obrazku, namalowanie tekstu itp.). Klasy TCanvas nie trzeba inicjowa ani zwalnia ? wszystko dziki temu, e zadanie to wykonuje formularz, ktry posiada waciwo Canvas. Prosty przykad: 1. Wygeneruj zdarzenie OnPaint formularza. 2. W procedurze zdarzeniowej wpisz taki kod:

Canvas.TextOut(10, 10, 'Witaj!');

Instrukcja TextOut z klasy TCanvas powoduje wywietlenie napisu okrelonego w trzecim parametrze tej procedury. Dwa pierwsze parametry okrelaj pozycje X i Y wywietlanego napisu (rysunek 9.9).

Rysunek 9.9. Zastosowanie procedury TextOut Zadajmy sobie pytanie: dlaczego kod musi by umieszczony w zdarzeniu OnPaint? System Windows dziaa na zasadzie wielowtkowej. W danym momencie moe by uruchomionych wiele programw; mog one wykonywa swoje, ustalone czynnoci. Okna te jednak s minimalizowane, a ich pozycje s zmieniane ? system nie jest w stanie ?zapamita? obrazu kadego okna, gdy pochonoby to wiele, wiele pamici. Std w momencie, gdy jakie okno jest wywietlane i znajduje si na pierwszym planie, system wysya do niego komunikat WM_PAINT, stanowicy informacj dla programu, e znajduje si on na pierwszym planie. Reakcja an ten komunikat zaley od aplikacji. Std obecno zdarzenia OnPaint, dziki ktremu jestemy w stanie narysowa cokolwiek na formularzu.

358 | S t r o n a

Pira i pdzle
Przed rysowaniem mamy moliwo ustawienia pewnych parametrw. Mam na myli style okrelajce grubo linii, styl wypeniania figur, wszelkie kolory itp. Ponadto klasa TCanvas udostpnia dwie waciwoci ? Pen (klasa TPen) oraz Brush (TBrush).

Klasa TPen
Waciwo Pen jest uywana do okrelenia rodzaju linii, koloru linii i innych ksztatw w czasie korzystania z klasy TCanvas. Wszystkie waciwoci zgromadziem w tabeli 9.6. Tabela 9.6. Waciwoci klasy TPen Waciwoci Color Mode Style Width Opis waciwoci Okrela kolor rysowanych linii i ksztatw (krawdzi) Definiuje tryb rysowanych linii i krawdzi (patrz tabela 9.7) Styl rysowanych linii (patrz tabela 9.8) Szeroko rysowanej linii (warto podawana w pikselach)

Waciwo Mode Dziki waciwoci Mode masz dostp do wikszej iloci kombinacji definiujcych tryb rysowanych linii. Waciwo Mode wskazuje na typ TPenMode: type TPenMode = (pmBlack, pmWhite, pmNop, pmNot, pmCopy, pmNotCopy, pmMergePenNot, pmMaskPenNot, pmMergeNotPen, pmMaskNotPen, pmMerge, pmNotMerge, pmMask, pmNotMask, pmXor, pmNotXor); Sam widzisz, e istnieje stosunkowo sporo rodzajw rysowania ? patrz tabela 9.7. Tabela 9.7. Wartoci typu TPenMode Warto pmBlack pmWhite pmNop Opis Zawsze czarny Zawsze biay Niezmienny

359 | S t r o n a

pmNot pmCopy pmNotCopy

Odwrotno koloru ta klasy TCanvas Kolor pira Inwersja koloru pira

pmMergePenNot Kombinacja koloru pira i odwrotnoci koloru ta pmMergeNotPen Kombinacja koloru ta i inwersja koloru pira pmMerge pmNotMerge Kombinacja koloru ta i pira. Odwrotno flagi pmMerge

Wicej kombinacji moesz znale w systemie pomocy Delphi.

Waciwo Style Inna ciekawa waciwo ? Style ? okrela sposb rysowania linii (cigy, przerywany itp.). W rzeczywistoci waciwo ta wskazuje na typ TPenStyle: type TPenStyle = (psSolid, psDash, psDot, psDashDot, psDashDotDot, psClear, psInsideFrame);

W tabeli 9.8 przedstawiem tumaczenia powyszych wartoci. Tabela 9.8. Wartoci typu TPenStyle Warto psSolid psDash psDot psDashDot Opis Linia ciga Linia przerywana Linia kropkowana Na przemian: kropki i kreski

psDashDotDot Na przemian: kreska i dwie kropki psClear Brak obrysowania

360 | S t r o n a

May przykad: Canvas.Pen.Color := clGreen; Canvas.Pen.Style := psDashDotDot; Canvas.Rectangle(80, 10, 250, 150); // narysowanie kwadratu

W powyszym przykadzie zmienilimy kolor linii na zielony, a styl rysowania na psDashDotDot. Nastpnie z zastosowaniem tych ustawie narysowalimy kwadrat (polecenie Rectangle). Na pycie CD-ROM w katalogu ..listingi/9/TPenStyle Demo/Demo.dpr znajduje si przykad ilustrujcy praktyczne zastosowanie wszystkich stylw. Jak dotd nic nie mwiem na temat kolorw uywanych w Delphi. Wszystkie kolory to w rzeczywistoci typ TColor. Wszystkie wartoci typu TColor posiadaj przedrostek cl, tak wic jeli chcesz skorzysta np. z koloru czarnego i znasz odpowiednik sowa ?czarny? w jzyku angielskim, moesz si atwo domyle, e warto odpowiadajca temu kolorowi to clBlack.

Klasa TBrush
Dziki klasie TBrush mamy moliwo ustawienia opcji wypeniania figur (kolor, bitmapa majca wypeni figur, styl). Waciwoci owej klasy przedstawione zostay w tabeli 9.9. Tabela 9.9. Waciwoci klasy TBrush Warto Opis Bitmap Wskazuje na klas TBitmap; okrela bitmap, jaka ma wypenia figury Color Style Okrela kolor wypenienia Waciwo definiuje styl wypenienia figur (tabela 9.10)

Waciwo Style Podobnie jak w przypadku klasy TPen, tutaj take waciwo Style okrela styl, tyle e dotyczy on wypenienia figur, a nie obrysowywania. type TBrushStyle = (bsSolid, bsClear, bsHorizontal, bsVertical, bsFDiagonal, bsBDiagonal, bsCross, bsDiagCross);

Moliwe do zastosowania style opisaem w tabeli 9.10. 361 | S t r o n a

Tabela 9.10. Moliwe wartoci klasy TBrushStyle Warto bsSolid bsCross bsClear Opis Pene wypenienie To bdzie siateczk To przezroczyste

bsDiagCross Siatka przecinajca si pod ktem prostym bsHorizontal Linie poziome bsVertical Linie pionowe

bsBDiagonal Ukonie z lewego dolnego naronika do grnego bsFDiagonal Ukonie z lewego grnego naronika do dolnego

Teraz ? znajc waciwoci klasy TBrush ? moemy wyprbowywa nie tylko rne ustawienia pira, ale rwnie pdzla: Canvas.Pen.Color := clGreen; Canvas.Pen.Style := psDashDotDot; Canvas.Brush.Color := clYellow; // kolor ta ? ty Canvas.Brush.Style := bsVertical; Canvas.Rectangle(80, 10, 250, 150); // narysowanie kwadratu

Czcionki
Czcionki w Delphi reprezentowane s przez klas TFont, dajc moliwo nie tylko zmiany kroju, ale take zmiany stylu wywietlanego tekstu. Klasa TCanvas rwnie pozwala na dostp do TFont: Canvas.Font.Name := 'Courier New'; Canvas.TextOut(100, 10, 'Hello World! ');

W powyszym wypadku zmienilimy czcionk na Courier New, a dopiero pniej narysowalimy tekst. Na rysunku 9.10 przedstawiony zosta przykadowy program, umoliwiajcy przetestowanie zmiany wygldu czcionki. 362 | S t r o n a

Rysunek 9.10. Testowanie czcionek Kod rdowy tego programu jest oczywicie dostpny na pycie CD-ROM.

Waciwoci klasy TFont


W tabeli 9.11 znajduj si waciwoci klasy TFont wraz z opisami. Tabela 9.11. Waciwoci klasy TFont Waciwo Warto Charset Color Height Name Pitch Size Style Kodowanie znakw Kolor uywanej czcionki Wysoko czcionki (w pikselach) Nazwa czcionki (musi by podawana w apostrofach) Moe przybra trzy wartoci okrelajce czcionk: fpDefault (domylna warto), fpFixed (wszystkie znaki maj rwn szeroko), fpVariable (znaki maj rne szerokoci) Rozmiar czcionki (w punktach) Styl czcionki: fsBold (pogrubiony), fsItalic (pochylony), fsUnderline (pokrelenie), fsStrikeOut (przekrelenie)

Tak si skada, e prawie w kadym komponencie wizualnym znajduje si waciwo Font typu TFont ? wiedza na jej temat moe Ci si zatem nieraz przyda. 363 | S t r o n a

Tu jednak musz poczyni mae zastrzeenie. Jeeli chcesz, aby tekst zosta pogrubiony, moesz napisa tak: Font.Style := [fsBold];

W takim jednak wypadku tekst zostanie tylko pogrubiony. Czyli jeeli wczeniej by pisany kursyw, to teraz kursywa zostanie usunita, a tekst bdzie tylko pogrubiony. Nam nie o to chodzi, gdy chcemy, aby tekst by pogrubiony, ale przy zachowaniu wczeniejszych styli. Trzeba wic napisa to tak: Font.Style := Font.Style + [fsBold];

Tutaj zastosowaem operator +. Jeeli jednak chciaby, aby od stylu tekstu odjty zosta styl pogrubienia, moesz napisa tak: Font.Style := Font.Style ? [fsBold];

Jeeli chcesz zresetowa wszystkie style (wszystko zostanie odznaczone), wpisz co takiego: Font.Style := [];

Metody klasy TCanvas


Do narysowania rnych ksztatw, figur geometrycznych, linii oraz tekstu mona skorzysta z metod klasy TCanvas, ktre s bardzo proste w uyciu. W wikszoci przypadkw naley poda jedynie parametry okrelajce pooenie danej figury i jej rozmiar. W tym podpunkcie przedstawi najwaniejsze metody klasy TCanvas. Cz opisz troch dokadniej, a inne zaprezentuj w postaci jednego punktu, przedstawiajc przykadowy program reprezentujcy rysowanie rnych figur geometrycznych.

Draw
procedure Draw(X, Y: Integer; Graphic: TGraphic);

Dziki funkcji moesz wywietli na formularzu jak grafik (np. bitmap) w miejscu oznaczonym przez parametry X oraz Y.

364 | S t r o n a

var Bitmap : TBitmap; begin Bitmap := TBitmap.Create; try Bitmap.LoadFromFile(?helion.bmp?); // adowanie pliku Canvas.Draw(10, 10, Bitmap); // wywietlenie obrazka w punkcie 10, 10 finally Bitmap.Free; end; end;

Dziki metodzie Draw mona si oby bez komponentw typu TImage. Pamitaj jednak, aby powyszy kod umieci w zdarzeniu OnPaint albo w jakiej procedurze zdarzeniowej (np. OnClick komponentu TButton).

FillRect
procedure FillRect(const Rect: TRect);

Procedura FillRect moe posuy do wypeniania jakiego obszaru (okrelonego parametrem Rect) z zastosowaniem dotychczasowych ustawie pira i pdzla. Canvas.Brush.Color := clWhite; Canvas.FillRect(Rect(10, 10, 100, 200));

Powyszy kod spowoduje wypenienie okrelonego obszaru biaym kwadratem. Zwr uwag, e w parametrze procedury FillRect znajduje si nieznany dotd typ ? TRect. W rzeczywistoci jest to rekord, ktrego deklaracja znajduje si w pliku Windows.pas: TRect = record case Integer of 0: (Left, Top, Right, Bottom: Integer); 1: (TopLeft, BottomRight: TPoint); end;

Nie wykonuje on nic nadzwyczajnego ? chcc zastpi ten typ, naleaoby zadeklarowa w procedurze cztery zmienne typu Integer, ktre okrelayby pooenie w pionie i w poziomie oraz szeroko i wysoko. Z zastosowaniem rekordu przekazujemy do procedury tylko jeden parametr. Zwr uwag, e w tym przypadku wcale nie jest konieczna deklaracja nowej zmiennej ? wystarczy przekaza parametry w ten sposb: Canvas.FillRect(Rect(10, 10, 100, 200));

365 | S t r o n a

StretchDraw
procedure StretchDraw(const Rect: TRect; Graphic: TGraphic);

Pamitasz, jak na pocztku tego rozdziau zmieniae waciwo Stretch komponentu TImage? Jakie to wwczas dawao efekty? Wtedy osigalimy dopasowanie caego obrazka do wielkoci komponentu. Procedura StretchDraw jest jakby rozszerzeniem polecenia Draw. Oprcz zwykego malowania grafiki umoliwia dopasowanie jej rozmiarw ? nie tylko pooenia X i Y, ale take szerokoci i wysokoci. var Bitmap : TBitmap; begin Bitmap := TBitmap.Create; try Bitmap.LoadFromFile('helion.bmp'); // adowanie pliku Canvas.StretchDraw(Rect(10, 10, 100, 100), Bitmap); finally Bitmap.Free; end; end;

W tym wypadku narysowany obraz bdzie mia rozmiary 100100.

TextOut
procedure TextOut(X, Y: Integer; const Text: string);

Z procedur TextOut zetkne si ju wczeniej. Realizuje ona proste rysowanie po formularzu. Pierwsze dwa parametry s pozycjami reprezentujcymi pooenie narysowanego tekstu, a ostatni ? typu String ? to tekst, ktry zostanie wywietlony. Oto przykad wywietlenia bitmapy oraz ? dodatkowo ? rysowania na niej tekstu: procedure TForm1.Button1Click(Sender: TObject); var Bitmap : TBitmap; begin Bitmap := TBitmap.Create; try Bitmap.LoadFromFile('C:\helion.bmp'); Canvas.Draw(50, 50, Bitmap); Canvas.Brush.Style := bsClear; // to rysowanego tekstu ? przezroczyste Canvas.TextOut(60, 60, 'http://helion.pl'); finally Bitmap.Free; end; end; 366 | S t r o n a

Przed narysowaniem tekstu styl pdzla zostaje ustawiony na bsClear, co gwarantuje, e tekst bdzie mia przezroczyste to (rysunek 9.11).

Rysunek 9.11. Efekt wywietlania bitmapy i rysowania tekstu

TextRect
procedure TextRect(Rect: TRect; X, Y: Integer; const Text: string);

Oprcz rysowania zwykego prostokta procedura TextRect umoliwia zdefiniowanie prostokta, wewntrz ktrego zostanie narysowany tekst (parametr Rect). Kolejne dwa parametry X i Y okrelaj poziome i pionowe pooenie tego tekstu: Canvas.Brush.Color := clWhite; Canvas.TextRect(Rect(10, 10, 100, 50), 20, 20, 'Helion');

Aby bardziej uwidoczni istnienie tego prostokta, na pocztku zmieniem kolor pdzla na biay. Efekt zastosowania takiego kodu wida na rysunku 9.12.

Rysunek 9.12. Efekt zastosowania funkcji TextRect

TextWidth, TextHeight
function TextWidth(const Text: string): Integer; function TextHeight(const Text: string): Integer;

Przypominam, e do obliczania dugoci acucha (w znakach) suy funkcja Length. W przypadku, gdy chcemy obliczy wysoko i szeroko tekstu w pikselach, musimy skorzysta z funkcji TextWidth, TextHeight. W parametrach tych funkcji naley wpisa jedynie tekst, ktry ma zosta zmierzony. Szeroko tekstu zaley rwnie od czcionki, jak zastosowano. Np. w czcionce Courier New zawarte s znaki o tej samej szerokoci, w odrnieniu od np. czcionki Arial. 367 | S t r o n a

TextExtent
function TextExtent(const Text: string): TSize;

Funkcja TextExtent podaje zarwno wysoko tekstu, jak i jego szeroko. Zwracana warto ma posta rekordu TSize: type TSize = packed record cx: Longint; cy: Longint; end;

MoveTo
procedure MoveTo(X, Y: Integer);

Procedura MoveTo suy do przeniesienia punktu startowego przed rysowaniem np. linii. Przykadowo rysowana linia ma mie swj pocztek w prawym, grnym rogu formularza. Wwczas aby ustawi punkt startowy, naley skorzysta z funkcji MoveTo: Canvas.MoveTo(500, 1); // ustawienie punktu startowego

Teraz jeli chcemy narysowa lini, wystarczy wywoa funkcj LineTo.

LineTo
procedure LineTo(X, Y: Integer);

W owym poleceniu naley poda parametry X i Y rysowanej linii. procedure TForm1.FormPaint(Sender: TObject); begin Canvas.Pen.Width := 5; Canvas.MoveTo(500, 20); Canvas.LineTo(10, 200); end;

Aby lepiej pokaza rysowan lini, jej szeroko ustawiem na 5 pikseli (rysunek 9.13).

368 | S t r o n a

Rysunek 9.13. Rysowanie linii

Inne funkcje suce do rysowania ksztatw


Oprcz funkcji przedstawionych wczeniej istnieje szereg polece, dziki ktrym moesz narysowa wiele rnych figur geometrycznych (tabela 9.12). Tabela 9.12. Nazwy funkcji sucych do rysowania figur geometrycznych Funkcja Arc Chord Ellipse Pie Polygon Polyline Opis Rysowanie uku Zamknita figura (wielokt) Rysowanie elipsy Wycinek koa Figura rysowana podstawie tablicy punktw Linia amana

Rectangle Prostokt

369 | S t r o n a

Przykadowy program
Na doczonej do ksiki pycie CD-ROM znajduje si projekt (..listingi/9/PaintTest/PaintTest.dpr), ktry prezentuje sposb rysowania rnych figur geometrycznych (rysunek 9.14). Uytkownik ma moliwo wyboru rodzaju pdzla oraz pira ? stylw i kolorw.

Rysunek 9.14. Prezentacja rysowania figur geometrycznych W listingu 9.5 znajduje si kod rdowy moduu MainFrm.pas, bdcego czci projektu PaintTest.dpr. Listing 9.5. Kod rdowy moduu MainFrm.pas unit MainFrm; interface uses Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls, ExtCtrls, Spin; { Stae oznaczajce odpowiednio style wypenienia (TBrushStyle) oraz styl pdzla. } const BrushStyle : array[0..6] of TBrushStyle = (bsSolid, bsCross, bsDiagCross, bsHorizontal, bsVertical, bsFDiagonal, bsBDiagonal); 370 | S t r o n a

PenStyle : array[0..5] of TPenStyle = (psDash, psDashDot, psDashDotDot, psDot, psInsideFrame, psSolid);

type TMainForm = class(TForm) Panel1: TPanel; Shape: TRadioGroup; Setup: TGroupBox; Label1: TLabel; Label2: TLabel; Label3: TLabel; Label4: TLabel; LineSize: TSpinEdit; Style: TComboBox; PColorLine: TPanel; PBrushColor: TPanel; LineColor: TColorDialog; BrushColor: TColorDialog; Label5: TLabel; PStyle: TComboBox; procedure ShapeClick(Sender: TObject); procedure PColorLineClick(Sender: TObject); procedure PBrushColorClick(Sender: TObject); private { Procedura ustawiajca waciwoci pdzla oraz wypenienia } procedure Ustaw(Sender: TObject); end; var MainForm: TMainForm; implementation {$R *.DFM} procedure TMainForm.ShapeClick(Sender: TObject); { Staa ta zawiera wsprzdne punktw, wymagane do wywietlenia wycinka koa } const Wielokat: array[0..3] of TPoint = ((X: 0; Y:0), (X: 150; Y:50), (X:230; Y:130), (X:40; Y:120)); var R : TRect; begin R := ClientRect; //obszar formy { do obszaru odejmij szeroko Panelu } R := Rect(R.Left, R.Top, R.Right ? Panel1.Width, R.Bottom); { W zalenoci od wybranej opcji na komponencie "TRadioButton" wykonywane s odpowiednie funkcje. } case Shape.ItemIndex of 0: begin Repaint; // odswie obraz 371 | S t r o n a

Ustaw(Sender); // wykonaj procedur Canvas.Rectangle(20, 20, 220, 220); // prostokt end; 1: begin Repaint; Ustaw(Sender); Canvas.Ellipse(R); // elipsa end; 2: begin Repaint; Ustaw(Sender); // wycinek koa Canvas.Pie(0, 0, ClientWidth ? Panel1.Width, ClientHeight, 90, 0, 300, 10); end; 3: begin Repaint; Ustaw(Sender); with ClientRect do // amana [ dziaa tylko wtedy, gdy Pen.Style = psSolid ] Canvas.Arc(Left, Top, Right ? Panel1.Width, Bottom, Right, Top, Left, Top); end; 4: begin Repaint; Ustaw(Sender); Canvas.RoundRect(20, 20, 220, 220, 30, 30); // prostokt z zaokrglonymi naronikami end; 5: begin Repaint; Ustaw(Sender); Canvas.Polygon(Wielokat); // wielokt end; 6: begin Repaint; Ustaw(Sender); with ClientRect do // odcinek Canvas.Chord(Left, Top, Right ? Panel1.Width, Bottom, Right, Top, Left, Top); end; end; end; procedure TMainForm.Ustaw(Sender: TObject); begin // Ustaw styl wypenienia w zalenoci od wybranego stylu w kontrolce Canvas.Brush.Style := BrushStyle[Style.ItemIndex]; // kolor wypeniania w zalenoci od koloru Panelu Canvas.Brush.Color := PBrushColor.Color; // Ustaw styl pdzla w zalenoci od opcji wybranej w kontrolce Canvas.Pen.Style := PenStyle[PStyle.itemIndex]; // kolor pdzla w zalenoci od koloru panelu Canvas.Pen.Color := PColorLine.Color; // Ustaw szeroko pdzla w zalenoci od wybranej opcji w kontrolce Canvas.Pen.Width := LineSize.Value; 372 | S t r o n a

end; procedure TMainForm.PColorLineClick(Sender: TObject); begin // wywietla komponent i ustawia kolor Panelu w zalenoci // od wybranego koloru w komponencie if LineColor.Execute then PColorLine.Color := LineColor.Color; end; procedure TMainForm.PBrushColorClick(Sender: TObject); begin // j/w if BrushColor.Execute then PBrushColor.Color := BrushColor.Color; end; { END } end.

Waciwie wszystko odbywa si tutaj z pomoc instrukcji warunkowej case. Gdy uytkownik wybierze odpowiedni ksztat, program pobiera ustawienia kolorw, style pdzla i pira, a nastpnie wywietla dany ksztat.

Proste animacje tekstowe


Piszc animacje tekstowe, mam na myli wizualizacj tekstu ? tj. jego przemieszczenie i rysowanie 3D. Do napisania programu, ktry wykonywaby jakie bardziej skomplikowane animacje (obroty figur 3D), najlepiej jest wykorzysta biblioteki OpenGL albo DirectX.

Tekst trjwymiarowy (3D)


W gruncie rzeczy narysowanie tekstu trjwymiarowego jest bardzo proste. Polega jedynie na podwjnym namalowaniu tego samego napisu, z minimalnym przesuniciem i innym kolorem. Oto przykad: Canvas.Font.Name := 'Courier New'; // czcionka Canvas.Font.Size := 20; // rozmiar czcionki Canvas.Font.Style := Font.Style + [fsBold]; // pogrubienie Canvas.Brush.Style := bsClear; // to przezroczyste Canvas.Font.Color := clWhite; // kolor czcionki Canvas.TextOut(20, 20, 'WWW.4PROGRAMMERS.NET'); Canvas.Brush.Style := bsClear; // to przezroczyste Canvas.Font.Color := clBlack; // kolor czcionki Canvas.TextOut(19, 19, 'WWW.4PROGRAMMERS.NET');

373 | S t r o n a

Na samym pocztku naley ustawi odpowiedni czcionk. Nastpnie naley okreli to tekstu jako przezroczyste (bsClear). Ustawiamy bia czcionk i teraz nastpuje narysowanie biaego tekstu w punkcie 20, 20. Pniej tekst zostanie narysowany czarn czcionk, tyle e z przesuniciem 1 piksela. Efektem takiego kodu bdzie napis wygldajcy tak, jak na rysunku 9.15.

Rysunek 9.15. Wywietlenie napisu 3D

Efekt maszyny do pisania


Kolejnym efektem graficznym, jaki pragn zaprezentowa, jest efekt, ktry nazwaem maszyn do pisania. W tej animacji litery wywietlane s jedna po drugiej (rysunek 9.16).

Rysunek 9.16. Wywietlanie jednej litery po drugiej Cay efekt opiera si na wywietlaniu kolejnych liter w czasowych odstpach ? np. 100 milisekund. Na samym pocztku konieczne staje si pobranie dugoci napisu, ktry chcemy przedstawi: TextLength := Length(DText); // pobierz dugo tekstu

Skorzystaem tutaj z funkcji Length, gdy naszym celem jest pobranie iloci znakw. Wane jest rwnie to, aby uyta czcionka posiadaa wszystkie znaki o tej samej dugoci: const DText = 'Delphi...'; procedure TMainForm.KrokPoKroku(X, Y: Integer); var TextLength, I: Integer; begin TextLength := Length(DText); // pobierz dugo tekstu

374 | S t r o n a

with Canvas do begin for I := 1 to TextLength do // ptelka... begin Application.ProcessMessages; Sleep(100); // czekaj 100 milisekund Brush.Style := bsClear; // styl na przezroczysty Font.Name := 'Courier New'; // czcionka Font.Color := clWhite; // kolor czcionki ? biay Font.Size := 16; // rozmiar { Wywietlaj tekst jedna litera po drugiej z przesuniciem } TextOut((X + i * 16), Y, DText[i]); Brush.Style := bsClear; Font.Color := clBlack; TextOut((X + i * 16) ?2, Y ?2, DText[i]); // wywietl ten sam tekst w innym pooeniu ? efekt cienia end; end; end;

Chyba najtrudniejsz rzecz w tej procedurze jest wytyczenie odstpw pomidzy kolejnymi znakami. Ja wykorzystaem metod prb i bdw i okazao si, e najoptymalniejszym rozwizaniem jest mnoenie zmiennej I przez liczb 16. Wtedy pierwszy znak zostanie umieszczony w punkcie 16, drugi w punkcie 32 itd. Przed wywoaniem tej procedury dobrze jest odwiey obraz znajdujcy si na formularzu (metoda Repaint): procedure TMainForm.btnRunClick(Sender: TObject); begin Repaint; // odwie Canvas KrokPoKroku(100, 100); // wywoaj procedur end;

Animacja na belce tytuowej


Kolejny efekt, jaki chc zaprezentowa, to przemieszczanie si tekstu po pasku tytuowym programu (rysunek 9.17).

Rysunek 9.17. Tekst wywietlany na pasku tytuowym

375 | S t r o n a

Na samym pocztku bdziesz musia zadeklarowa zmienn globaln, ktra bdzie okrela, czy animacja jest wci uruchomiona: var Running : Boolean = TRUE;

Kolejny krokiem jest deklaracja w sekcji private procedury Go: procedure Go(const PText : TStrings);

Owa procedura jako parametr przyjmuje dane w postaci typu TStrings. Kady wiersz tekstu znajdujcy si w tym typie bdzie osobno pokazywany na pasku tytuowym. Gdy zostan ju pokazane wszystkie wiersze, animacja rozpocznie swe dziaanie od pocztku: procedure TMainForm.Go(const PText: TStrings); var PTextLong : Integer; // dugo tekstu I : Integer; LinesCount : Integer; // ilo wierszy begin while (Running) do // dopki zmienna bdzie miaa warto True begin Application.ProcessMessages; if not Running then Break; // sprawdzaj, czy przypadkiem nic si nie zmienio for LinesCount := 0 to PText.Count ?1 do // zaczynaj od pierwszego wiersza begin if not Running then Break; // znw sprawd... PTextLong := Length(PText[LinesCount]); // pobierz dugo wiersza Sleep(500); // odczekaj p sekundy Caption := ''; // wyma Caption for I := 0 to PTextLong do // wykonuj ptl literka po literce begin Application.ProcessMessages; Sleep(100); // z przerw 100 milisekund if not Running then Break; // znw sprawd! Caption := Caption + PText.Strings[LinesCount][i]; // wywietl po kolei wszystkie litery end; end; end; end;

376 | S t r o n a

Kod tej procedury moe si wyda nieco odstraszajcy, lecz warto zapozna si z komentarzami ? wwczas wiele spraw okae si atwiejszymi. Aby wszystko dobrze dziaao, naleao umieci tu a trzy ptle. Pierwsza ? while ? suy do kontrolowania, czy uytkownik nie chce przypadkiem zamkn aplikacji. Jeeli nie ? powtarza cay proces od pocztku. Kolejna ptla pobiera kolejne wiersze ze zmiennej typu TStrings; zagniedona w niej jest kolejna ptla for, wywietlajca napis litera po literze. W tym wypadku sprawa jest znacznie prostsza, ni w poprzednim przykadzie, gdzie rysowanie kolejnych liter musiao by dokadnie mierzone. W listingu 9.6. znajduje si kod rdowy moduu. Listing 9.6. Kod rdowy programu unit MainFrm; interface uses Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, StdCtrls; type TMainForm = class(TForm) btnGo: TButton; btnStop: TButton; procedure btnGoClick(Sender: TObject); procedure FormClose(Sender: TObject; var Action: TCloseAction); procedure btnStopClick(Sender: TObject); private procedure Go(const PText : TStrings); public end; var MainForm: TMainForm; implementation {$R *.DFM} var Running : Boolean = TRUE; by uruchomiona // zmienna okrela, czy animacja ma

procedure TMainForm.Go(const PText: TStrings); var PTextLong : Integer; // dugo tekstu I : Integer; LinesCount : Integer; // ilo wierszy begin while (Running) do // dopki zmienna bdzie miaa warto True begin Application.ProcessMessages; if not Running then Break; // sprawdzaj, czy przypadkiem nic si nie zmienio for LinesCount := 0 to PText.Count ?1 do // zaczynaj od 377 | S t r o n a

pierwszego wiersza begin if not Running then Break; // znw sprawd... PTextLong := Length(PText[LinesCount]); // pobierz dlugo wiersza Sleep(500); // odczekaj p sekundy Caption := ''; // wyma Caption for I := 0 to PTextLong do // wykonuj ptl literka po literce begin Application.ProcessMessages; Sleep(100); // z przerw 100 milisekund if not Running then Break; // znw sprawd! Caption := Caption + PText.Strings[LinesCount][i]; // wywietl po kolei wszystkie litery end; end; end; end; procedure TMainForm.btnGoClick(Sender: TObject); var sText : TStrings; begin btnStop.Visible := True; // pokazanie przycisku umoliwiajcego zatrzymanie sText := TStringList.Create; try with sText do begin { dodanie kolejnych wierszy, ktre po kolei bd wywietlane } Add('Delphi 7.0 ...'); Add('...jest doskonaym narzdziem typu RAD do szybkiego tworzenia aplikacji...'); Add('...moesz si o tym przekona sam. Ten program zajmuje 92 linie.'); Add('Adam Boduch'); end; Go(sText); // wywoaj procedur z parametrem finally sText.Free; end; end; procedure TMainForm.FormClose(Sender: TObject; var Action: TCloseAction); begin Running := False; // przy prbie zamknicia zmie warto end; procedure TMainForm.btnStopClick(Sender: TObject); begin Running := False; // zmie warto, jeeli chcesz zatrzyma end; end. 378 | S t r o n a

Inne pynne animacje


W Delphi istnieje oczywicie moliwo tworzenia innych, pynnych animacji ? niekoniecznie tekstowych. Na rysunku 9.18 przedstawiony jest program, ktrego dziaanie polega na przemieszczaniu rysunku po caym oknie programu. Cay problem tkwi w tym, aby rysunek ?odbija si? od krawdzi okna i wdrowa w inne, losowe kierunki okna.

Rysunek 9.18. Rysunek przemieszczajcy si po oknie programu Kod rdowy tego programu zamieszczam na pycie CD-ROM ? zachcam do samodzielnego przetestowania go (katalog ../listingi/9/ImgPaint/ImgPaint.dpr). Przemieszczanie obrazkw po formularzu opiera si, oglnie rzecz biorc, na kadorazowym rysowaniu obrazka w innej pozycji. W przypadku programu przedstawionego na rysunku 9.18 za przemieszczanie obrazka odpowiada osobny wtek (o wtkach bya mowa w poprzednim rozdziale). Jeeli jednak chcemy za kadym razem przerysowywa obrazek, naley uprzednio odwiey obraz (metoda Repaint). W wielu przypadkach moe to powodowa migotanie okna, dajce nieprzyjemny dla oka efekt. W takich przypadkach dobrze jest stosowa tzw. podwjne buforowanie. DoubleBuffered := True;

W przypadku, gdy waciwo (nie jest to waciwo klasy TCanvas, ale klasy TWinControl ? patrz rozdzia 14.) ma warto False, rysowanie odbywa si bezporednio na formularzu, co moe rzeczywicie powodowa migotanie obrazu. Zmiana wartoci na True moe czciowo poprawi efekt wizualny. W przykadowym programie z rysunku 9.18 rozwizaem ten problem nieco inaczej, umieszczajc na formularzu komponent TPaintBox i rysujc bezporednio na nim.

379 | S t r o n a

Odtwarzanie dwikw
Obecnie istnieje wiele formatw dwikowych ? od tych najprostszych (jak WAV) po najpopularniejsze, typu mp3. Na razie zajmiemy si odtwarzaniem tych najmniej skomplikowanych plikw dwikowych ? WAV (fala ? ang. wave). Moe nie jest to bardzo popularny format (pliki tego rodzaju czsto maj due rozmiary), lecz znakomicie nadaje si do odtwarzania prostych dwikw.

Funkcja PlaySound
W module MMSystem zadeklarowana jest funkcja API PlaySound, dziki ktrej odtwarzanie prostych dwikw jest czynnoci raczej nieskomplikowan ? oto jej deklaracja: function PlaySound(pszSound: PChar; hmod: HMODULE; fdwSound: DWORD): BOOL; stdcall;

Pierwszy parametr musi zawiera ciek do odtwarzanego dwiku; drugi to wskazanie moduu ? w naszym przypadku w tym miejscu wystarczy wstawi cyfr 0. Ostatni parametr to flaga. Najprostsze odtworzenie dwiku *.wav moe wyglda tak: PlaySound('C:\plik.wav' 0, SND_FILENAME);

W tabeli 9.13 umieciem moliwe do zastosowania flagi. Tabela 9.13. Flagi polecenia PlaySound Flaga SND_ALIAS SND_FILENAME SND_NOWAIT SND_NOSTOP Opis Odtwarzanie dwiku systemowego Odtwarzany plik znajduje si na dysku Flaga nakazuje wstrzymanie odtwarzania, jeeli jest ju odtwarzany jaki inny dwik Odtwarzanie nastpi w przypadku, gdy nie jest odtwarzany inny utwr

SND_RESOURCE Plik muzyczny bdzie odtwarzany z zasobw SND_LOOP SND_ASYNC Powoduje odtwarzanie utworu w ptli. Tylko w zastosowaniu z SND_ASYNC Odtwarzanie bdzie odbywa si w tle

SND_NODEFAULT Jeeli plik do odtworzenia nie istnieje, nie jest generowany dwik ostrzegawczy SND_PURGE Zatrzymanie odtwarzania

380 | S t r o n a

Istnieje moliwo poczenia kilku flag za pomoc operatora or. Przy omawianiu parametru SND_ALIAS wspomniaem o moliwoci odtwarzania pliku systemowego. Takie dwiki s zadeklarowane w rejestrze Windows i generowane w wyniku zajcia jaki sytuacji ? np. uruchomienia systemu, zamknicia go, kliknicia mysz itp. PlaySound('MailBeep', 0, SND_ALIAS or SND_NODEFAULT);

W tym przypadku odegrany zostanie dwik MailBeep, zapisany w rejestrze.

Uycie komponentu TMediaPlayer


Zabawa funkcjami WinAPI nie ma zbytniego sensu, gdy mamy pod rk komponent TMediaPlayer, ktrego uycie wie si tylko z wywoywaniem kolejnych metod. Komponent ten znajduje si w palecie komponentw na zakadce System ? umie go na formularzu. Wygld kontrolki nie przedstawia si zbyt zachcajco ? ja zawsze ukrywam widoczn palet sterowania, zmieniajc waciwo Visible na False. Oto przykad rozpoczcia odtwarzania pliku mp3: with Player do begin FileName := 'C:\Live set at home.mp3'; // okrelenie cieki do pliku Open; // otwarcie pliku Play; // rozpoczcie odtwarzania end;

Na samym pocztku konieczne staje si przydzielenie cieki pliku do waciwoci FileName komponentu. Jeeli wiemy ju, jaki plik chcemy odtwarza, wystarczy wybra metod Open, a nastpnie Play. Waniejsze metody komponentu TMediaPlayer przedstawione zostay w tabeli 9.14. Metoda Opis Back Close Open Pause Play Cofa odtwarzanie o okrelon we waciwoci Frames liczb klatek Wycza urzdzenie, co wie si ze zatrzymaniem odtwarzania Wcza urzdzenie i przygotowuje do odtwarzania Wstrzymuje odtwarzanie Rozpoczyna odtwarzanie

Resume Wznawia odtwarzanie

381 | S t r o n a

Stop

Zatrzymuje odtwarzanie

Odtwarzanie filmw
Za pomoc komponentu TMediaPlayer mona rwnie dobrze odtwarza take filmy. Na doczonej do ksiki pycie CD-ROM (../lisingi/9/Player/Player.dpr) znajduje si program (odtwarzacz), ktry napisaem do dawno, bo 3 lata temu, lecz jest wci aktualny. Program w trakcie dziaania przedstawiony jest na rysunku 9.19, a jego kod znajduje si w listingu 9.7.

Rysunek 9.19. Odtwarzacz multimedialny Listing 9.7. Kod rdowy odtwarzacza unit MainFrmU; interface uses Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, MPlayer, StdCtrls, ExtCtrls, ComCtrls, Menus, Buttons, MMSystem, Registry, ToolWin, ImgList; 382 | S t r o n a

type TMainFrm = class(TForm) HomeMenu: TMainMenu; File1: TMenuItem; FileOpen: TMenuItem; N1: TMenuItem; StatusB: TStatusBar; Open: TOpenDialog; PP: TPanel; TextPanel: TPanel; Scroll: TScrollBar; Player: TMediaPlayer; Timer: TTimer; Sound: TTrackBar; Image1: TImage; Exit: TMenuItem; Close: TMenuItem; Widok1: TMenuItem; FullS: TMenuItem; N2: TMenuItem; OnTop: TMenuItem; Bar: TToolBar; Play: TToolButton; Pause: TToolButton; ImageList: TImageList; Stop: TToolButton; About1: TMenuItem; procedure FileOpenClick(Sender: TObject); procedure ScrollScroll(Sender: TObject; ScrollCode: TScrollCode; var ScrollPos: Integer); procedure TimerTimer(Sender: TObject); procedure PlayerNotify(Sender: TObject); procedure SoundChange(Sender: TObject); procedure FormCreate(Sender: TObject); procedure Image1Click(Sender: TObject); procedure PPClick(Sender: TObject); procedure CloseClick(Sender: TObject); procedure FullSClick(Sender: TObject); procedure OnTopClick(Sender: TObject); procedure FormDestroy(Sender: TObject); procedure PauseClick(Sender: TObject); procedure PlayClick(Sender: TObject); procedure StopClick(Sender: TObject); procedure About1Click(Sender: TObject); private Button: TMPBtnType; procedure ShowText(Text: String); 383 | S t r o n a

public FName : String; Tx : String; end; var MainFrm: TMainFrm; implementation uses BigFrmU; {$R *.DFM}

procedure TMainFrm.ShowText(Text: String); begin { Procedura, ktra wywietla w Panelu tekst. } TextPanel.Caption := Text; end; procedure TMainFrm.FileOpenClick(Sender: TObject); begin try if Open.Execute then

FName := Open.FileName;

//Przypisanie cieki zmiennej

Player.FileName := FName; //Przypisanie odtwarzaczowi zmiennej Player.Open; // Otwarcie filmu Player.Display := PP; { Przypisanie odtwarzaczowi obszaru wywietlania jako Panel } Player.DisplayRect := PP.ClientRect; { Dopasowanie rozmiarw filmu do rozmiarw Panelu } Scroll.Position := 0; //Wskanik na 0 Tx := ExtractFileName(Player.FileName); //Odczenie nazwy pliku od cieki ShowText(Tx); // i wywietlenie jej w Panelu "TextPanel" Caption := Tx; 384 | S t r o n a

except on EMCIDeviceError do raise Exception.Create(Format( 'Nie mog otworzy pliku o rozszerzeniu %s. Sprawd, czy plik nie '+ 'jest uszkodzony lub czy prawidowy jest jego format danych.', [ExtractFileExt(Player.FileName)])); end; end; procedure TMainFrm.ScrollScroll(Sender: TObject; ScrollCode: TScrollCode; var ScrollPos: Integer); begin { Pozycja odtwarzacza rwna si bdzie pozycji Scrolla. Jeeli zmieni si pozycja Scrolla, film zostanie "przewinity" } Player.Position := Scroll.Position; end; procedure TMainFrm.TimerTimer(Sender: TObject); begin { j/w } Scroll.Position := Player.Position; end; procedure TMainFrm.PlayerNotify(Sender: TObject); begin // jeeli film si skoczy if Player.NotifyValue = nvSuccessful then // Jeeli operacja si zakoczy begin Timer.Enabled := False; // wyczenie Timera Scroll.Position := Scroll.Position; //Scroll na tej samej pozycji Player.Stop; StopClick(Sender); end; end; procedure TMainFrm.SoundChange(Sender: TObject); begin //Ustawienie gonoci... case Sound.Position of 1: 385 | S t r o n a

WaveOutSetVolume(0, 2: WaveOutSetVolume(0, 3: WaveOutSetVolume(0, 4: WaveOutSetVolume(0, 5: WaveOutSetVolume(0, end; end;

$20002000); {2000} $60006000); {6000} $80008000); {8000} $90009000); {9000} $FFFFFFFF); {maksymalna gono}

procedure TMainFrm.FormCreate(Sender: TObject); var Reg : TRegistry; Key : Boolean; I : Integer; S : String; begin // wczytaj wartoci z rejestru Reg := TRegistry.Create; try Key := Reg.OpenKey( 'Software\Player', False); if Key then begin Top := Reg.ReadInteger('Top'); Left := Reg.ReadInteger('Left'); OnTop.Checked := Reg.ReadBool('StayOnTop'); end else OnTop.Checked := False; finally Reg.Free; end; end; procedure TMainFrm.Image1Click(Sender: TObject); begin Sound.Position := 1; // Wyciszenie end; procedure TMainFrm.PPClick(Sender: TObject); begin try Player.Pause; ShowText('Wcinito pauz...'); except 386 | S t r o n a

ShowText('Brak filmu...'); end; end; procedure TMainFrm.CloseClick(Sender: TObject); begin Player.Close; // Zamknicie filmu ShowText('Film zosta zamknity.'); // Wywietlenie tekstu Caption := 'Player'; end; procedure TMainFrm.FullSClick(Sender: TObject); begin FullForm.BorderStyle := bsNone; // ukryj pasek FullForm.WindowState := wsMaximized; // maksymalizacja okna Player.Display := FullForm.FullPanel;// obraz na Panelu with FullForm.FullPanel do // rozmiar panelu dopasowany do rozmiaru filmu Player.DisplayRect := Rect(0, 0, Width, Height); OnTop.Checked := False; FullForm.ShowModal; // Wywietlenie okna 2 end; procedure TMainFrm.OnTopClick(Sender: TObject); begin { Zawrze na wierzchu } OnTop.Checked := not OnTop.Checked; if OnTop.Checked = True then FormStyle := fsStayOnTop; end; procedure TMainFrm.FormDestroy(Sender: TObject); var Reg : TRegistry; begin Reg := TRegistry.Create; try Reg.OpenKey( 'Software\Player', True); // zapisz pozycj okna Reg.WriteInteger('Top', Top); Reg.WriteInteger('Left', Left); // zapisz pozycj "StayOnTop" Reg.WriteBool('StayOnTop',OnTop.Checked); finally 387 | S t r o n a

Reg.Free; end; end; procedure TMainFrm.PauseClick(Sender: TObject); begin // pauza try Player.Pause; // zastopuj odtwarzanie filmu Play.Down := False; // przyciski "odcinite" Stop.Down := False; Pause.Down := True; ShowText('Wcinito pauz...'); // zmie tekst except // wyjtek Pause.Down := False; raise Exception.Create( 'Film nie jest odtwarzany!'); end; end; procedure TMainFrm.PlayClick(Sender: TObject); begin try Scroll.Max := Player.Length; //Przypisanie maksymalnej wartoci do dugoci filmu Timer.Enabled := True; // Wczenie Timera ShowText('Trwa odtwarzanie...'); Player.Play; Play.Down := True; Stop.Down := False; Pause.Down := False; except Play.Down := False; raise Exception.Create( 'Najpierw musisz wybra film do otwarcia!'); end; end; procedure TMainFrm.StopClick(Sender: TObject); begin try Player.Stop; Play.Down := False; Pause.Down := False; Stop.Down := True; ShowText('Zatrzymano film...'); except Stop.Down := False; raise Exception.Create( 388 | S t r o n a

'Nie mona wyczy filmu, poniewa nie jest on odtwarzany!'); end; end; procedure TMainFrm.About1Click(Sender: TObject); begin MessageDlg( ' Player v. BETA '+#13+#13+ 'Autor: Adam Boduch '+#13+ 'E-mail: adam@boduch.net '+ #13+ ' http://4programmers.net',mtInformation, [mbOK], 0); end; end.

Przy okazji analizowania kodu moesz sobie przypomnie funkcje zwizane z zapisem danych do rejestru ? program bowiem zapisuje w nim aktualne ustawienia oraz pooenie okna.

Odtwarzanie filmu Odtwarzanie filmu przebiega w podobny sposb, jak odtwarzanie dwiku ? na pocztku naley przypisa warto zmiennej FileName, pniej otworzy film (Open), a na kocu rozpocz odtwarzanie (Play). Jedyna rnica jest taka, e odtwarzany film bdzie wywietlany na komponencie TPanel ? odpowiada za to taki kod: Player.Display := PP; Player.DisplayRect := PP.ClientRect;

Oprcz okrelenia obszaru na ktrym zostanie wywietlony film, nastpuje tu take dopasowanie rozmiarw wywietlanego filmu do rozmiarw panelu (waciwo DisplayRect).

Pozycja odtwarzanego filmu Jak pewnie zauwaye, oprcz zwykego odtwarzania pasek (komponent TScrollBar) pokazuje pozycj odtwarzanego filmu. Mona to zrealizowa za pomoc komponentu TTimer. Zadaniem komponentu TTimer jest wykonywanie jakiej czynnoci w pewnych odstpach czasu (w naszym wypadku ? co 1 sekund). procedure TMainFrm.TimerTimer(Sender: TObject); begin 389 | S t r o n a

Scroll.Position := Player.Position; end;

Co 1 sekund pozycja scrolla (paska przewijania) jest uaktualniania w stosunku do pozycji odtwarzanego filmu. Prba przewinicia tego paska spowoduje ustawienie nowej pozycji dla odtwarzania filmu: procedure TMainFrm.ScrollScroll(Sender: TObject; ScrollCode: TScrollCode; var ScrollPos: Integer); begin Player.Position := Scroll.Position; end;

Ustawianie gonoci Program posiada take opcje ustawiania gonoci filmu. Wszystko to dziki funkcji API ? WaveOutSetVolume: WaveOutSetVolume(0, $90009000);

W drugim parametrze naley poda wartoci dla prawego oraz lewego gonika ? w tym wypadku warto wynosi $9000. Niektre odtwarzacze filmw umoliwiaj rwnie wywietlanie napisw w przypadku, gdy film nie jest w polskiej wersji jzykowej. Jak to zrobi? Poka to w 13. rozdziale ksiki.

Kontrolka Flash
Format plikw Flash zyska sobie du popularno dziki prostocie tworzenia animacji oraz duej efektywnoci, jak zapewnia. W Delphi istnieje moliwo odtwarzania animacji *.swf tworzonych za pomoc programu Flash, lecz bdzie do tego potrzebna kontrolka ActiveX doczona do tego programu. ActiveX jest to plik OCX, ktry mona nazwa komponentem. Jest to standard opracowany przez Microsoft. O kontrolkach ActiveX bdzie mowa w rozdziale 13. niniejszej ksiki. Plik SWFLASH.OCX znajdziesz w katalogu C:\Windows\System\Macromed\Flash (przynajmniej tak jest 390 | S t r o n a

w moim przypadku).

Instalacja kontrolki
Z menu Component wybierz polecenie Import ActiveX Control. Okno to przedstawiono na rysunku 9.20.

Rysunek 9.20. Importowanie nowej kontrolki Nacinij przycisk Add i znajd wrd katalogw szukan kontrolk ActiveX. Pniej na licie kliknij pozycj Shockwave Flash. Nacinij przycisk Install, aby zainstalowa kontrolk. Zostanie wywietlone okno, w ktrym powiniene jeszcze raz nacisn OK. Zobaczysz wwczas takie okno, jakie przedstawiono na rysunku 9.21.

391 | S t r o n a

Rysunek 9.21. Instalacja kontrolki ActiveX Zostaniesz zapytany, czy kontynuowa instalacj kontrolki w palecie komponentw. Nacinij Yes. Nastpnie powiniene ujrze informacj o prawidowym zainstalowaniu kontrolki. Nacinij Ctrl+S, aby zapisa wszystko. Teraz moesz ju otworzy nowy projekt. Nowy komponent do wywietlania animacji Flash znajduje si na palecie ActiveX.

Wykorzystanie komponentu
Przede wszystkim musisz dysponowa jak animacj w formacie Flash! Jeeli ju takow masz, to na formularzu umie komponent TShockwaveFlash z palety ActiveX. Wygeneruj zdarzenie OnCreate formularza ? do wywietlenia filmu bd potrzebne tylko dwa wiersze kodu. procedure TMainForm.FormCreate(Sender: TObject); begin Flash.Movie := ExtractFilePath(Application.ExeName) + 'banner.swf'; Flash.Play; end;

W komponencie tym wymagane jest podanie dokadnej cieki do pliku SWF. Musimy posuy si funkcj Aplication.ExeName, ktra zwraca pen ciek dostpu do naszej aplikacji. Zakadajc, e film znajduje si w katalogu z programem, musimy jeszcze uy funkcji ExtractFilePath. Funkcja ta z parametru wyodrbnia jedynie ciek. Przykadowo jeli wywoamy funkcj w ten sposb: ExtractFilePath('C:\Moje dokumenty\app\mj.exe');

392 | S t r o n a

zostanie zwrcona warto: C:\Moje dokumenty\app\ Dziaanie programu zaprezentowano na rysunku 9.22.

Rysunek 9.22. Wykorzystanie kontrolki TShockwaveFlash

Podsumowanie
By moe nie wyczerpaem do koca tematu i co pominem, ale staraem si zaprezentowa gwne aspekty wykorzystania grafiki oraz operowania dwikiem. Mam nadziej, e przedstawione tu informacje przydadz Ci si w przyszoci?! A moe napiszesz odtwarzacz filmw lepszy ni ten, ktry tu zaprezentowaem? Zaczniki:

Listingi_9.zip (1423.79 kB)

Rozdzia 10

Edytuj Historia Przenie 393 | S t r o n a

Obserwuj

Biblioteki DLL
Pewnie nieraz spotkae si z pojciem biblioteka DLL. By moe wiesz ju, czym s te biblioteki i jak ich uywa. Jeeli jeszcze nie jeste tego wiadomy, nie przejmuj si ? objanieniem tego pojcia zajm si na samym pocztku niniejszego rozdziau. Nauczysz si tworzy i wykorzystywa stworzone przez siebie biblioteki w programach dziaajcych w systemie Windows.

Spis treci 1 Czym jest biblioteka DLL? 2 Do czego mog si przyda biblioteki DLL? 2.1 Zalety 2.2 Wady 3 Tworzenie bibliotek DLL 3.1 Budowa biblioteki 3.2 Rozmiar biblioteki 4 Eksportowanie procedur i funkcji 4.1 Eksportowanie przez nazw 4.2 Eksport przez indeks 5 adowanie bibliotek DLL 5.1 adowanie statyczne 5.2 adowanie dynamiczne 6 Konwersje wywoania 7 Formularze w bibliotekach DLL 7.1 Tworzenie formularza 7.2 Eksportowanie formularza 8 Przekazywanie rekordw do bibliotek 8.1 Budowa pliku mp3 8.2 Odczyt tagu z pliku mp3 8.3 Demo 9 acuchy w bibliotekach DLL 10 Zasoby w bibliotece DLL 10.1 Przygotowanie zasobw 10.2 adowanie zasobw z biblioteki DLL 11 Procedura inicjujco-koczca 11.1 Blok begin biblioteki DLL 11.2 DLLProc 11.3 Kod biblioteki 11.4 Program wykorzystujcy bibliotek 12 Podsumowanie 394 | S t r o n a

W tym rozdziale:

dowiesz si, czym s biblioteki DLL; nauczysz si tworzy i wykorzystywa biblioteki DLL; zaprojektujesz bibliotek odczytujc informacje z plikw mp3.

Czym jest biblioteka DLL?


DLL to skrt od sw Dynamic Link Library, czyli biblioteka dynamicznie czona. W skrcie mona powiedzie, e biblioteka DLL to plik o rozszerzeniu *.dll, zawierajcy procedury i funkcje, ktre mog by nastpnie wykorzystywane w programie. Jednak biblioteka DLL i plik wykonywalny programu to dwa rne pliki, ktre jednak s w stanie ze sob wsppracowa. Biblioteka DLL moe zawiera funkcje i procedury, ktre uruchamia moe jedynie aplikacja wykonywalna. Ten rozdzia bdzie powicony tworzeniu bibliotek DLL oraz wykorzystywaniu ich w swoich programach. Nie jest to takie trudne, gdy budowanie bibliotek jest bardzo podobne do tworzenia zwykych aplikacji wykonywalnych. Istnieje jednak par zasad, ktrych poznanie jest wymagane do bezproblemowej wsppracy pomidzy bibliotekami DLL a aplikacjami je wykorzystujcymi.

Do czego mog si przyda biblioteki DLL?


Pewnie zadajesz sobie w tym momencie pytanie: ?do czego waciwie s potrzebne te biblioteki DLL??. Umieszczanie czci kodu w bibliotekach DLL ma wiele zalet, lecz istniej take wady tego rozwizania.

Zalety
Do zalet mona zaliczy przede wszystkim podzia kodu. W przypadku, gdy program podzielony jest na biblioteki DLL, a konieczne jest poprawienie jakiego bdu, wystarczy wymieni tylko jeden plik DLL, a nie cay program. W bibliotekach DLL mona przechowywa take zasoby programu: kursory, ikony, bitmapy, inne 395 | S t r o n a

programy itp. Z poziomu programu mamy peny dostp do takich zasobw. Wykorzystujc biblioteki DLL, mona stworzy program dziaajcy w wielu wersjach jzykowych. Raz napisana biblioteka DLL moe by uyta wiele razy przez wiele programw. Ogromn zalet jest moliwo obsugi bibliotek DLL przez rne jzyki programowania. Przykadowo biblioteka napisana w C++ Builder moe by wykorzystana zarwno w Delphi, jak i we wszystkich rodowiskach programistycznych dziaajcych w systemie Windows. Praktycznie wszystkie funkcje API zawarte s w bibliotekach DLL. Podczas dotychczasowej pracy z Delphi nieraz miae okazj wykorzysta funkcje z moduu Windows.pas. W rzeczywistoci funkcje i procedury z tego moduu s jedynie importowane z bibliotek DLL systemu Windows. Cay system Windows jest oparty na bibliotekach DLL! Funkcje zawarte w jednym tylko pliku mog by uywane przez wiele programw (wanie z tych moliwoci korzystamy teraz w Delphi). Kolejny przykad to biblioteka umoliwiajca odtwarzanie plikw mp3. Osoba projektujca taki program zawara jego kod w bibliotece DLL. Dziki temu kady programista, ktry chce wykorzysta w swojej aplikacji moliwo odtwarzania plikw mp3, uyje owego pliku DLL (tutaj ujawnia si kolejna zaleta ? niezaleno od jzyka programowania). w programista nie musi zatem od pocztku tworzy funkcji obsugujcych odtwarzanie plikw mp3 ? s one od razu dostpne.

Wady
Jeeli decydujemy si na podzia aplikacji pomidzy biblioteki DLL i plik wykonywalny, musimy liczy si ze zwikszeniem rozmiarw caej aplikacji ? pliki DLL maj zazwyczaj taki sam rozmiar, jak aplikacje wykonywalne, wic czny rozmiar programu bdzie do duy. Jedna biblioteka DLL moe by czsto odpowiedzialna za prawidowe dziaanie wielu programw. Brak takiego jednego pliku (usunitego na przykad przy odinstalowywaniu jakiej aplikacji) moe spowodowa nieprawidowe dziaanie aplikacji lub niemono uruchomienia takiego programu.

Tworzenie bibliotek DLL


Do stworzenia wasnej biblioteki DLL nie bdzie nam potrzebny aden zewntrzny program. Wystarczy otworzy Repozytorium (File/New/Other) i wybra ikon DLL Wizard (rysunek 10.1). Wwczas w Delphi zostanie utworzony nowy projekt, a w Edytorze kodu bdzie znajdowa si kod przedstawiony w listingu 10.1.

396 | S t r o n a

Rysunek 10.1. Okno Repozytorium z zaznaczonym elementem DLL Wizard. Listing 10.1. Startowy kod rdowy biblioteki DLL library Project2; { Important note about DLL memory management: ShareMem must be the first unit in your library's USES clause AND your project's (select Project?View Source) USES clause if your DLL exports any procedures or functions that pass strings as parameters or function results. This applies to all strings passed to and from your DLL??even those that are nested in records and classes. ShareMem is the interface unit to the BORLNDMM.DLL shared memory manager, which must be deployed along with your DLL. To avoid using BORLNDMM.DLL, pass string information using PChar or ShortString parameters. } uses SysUtils, Classes; {$R *.res}

397 | S t r o n a

begin end.

Budowa biblioteki
W przeciwiestwie do ?zwykych? programw pierwszy wiersz kodu rdowego zawiera sowo kluczowe library (zamiast standardowego program), po ktrym nastpuje nazwa biblioteki. Charakterystyczn cech biblioteki, ktra od razu rzuca si w oczy, jest do duy komentarz. Jego treci zajmiemy si nieco pniej ? teraz moesz go zwyczajnie usun. Reszta kodu rdowego jest ju standardowa i nie powiniene mie problemw z jej zrozumieniem. Oglnie mona przyj, e budowa biblioteki jest bardzo podobna do standardowego pliku projektu (*.dpr) Delphi.

Rozmiar biblioteki
Niestety rozmiar biblioteki w adnym stopniu nie bdzie mniejszy od rozmiaru aplikacji wykonywalnej. Jest to spowodowane uyciem takich moduw, jak SysUtils czy Classes. Oglnie rzecz biorc, rozmiar biblioteki jest zaleny od zasobw, ktre maj by do niej wczone, oraz od uytych moduw. Pewnym rozwizaniem jest stosowanie jedynie moduu Windows oraz funkcji API ? pozwoli to na tworzenie bibliotek DLL o rozmiarach rzdu 13 KB. Tworzeniem aplikacji API zajmiemy si w rozdziale 12.

Eksportowanie procedur i funkcji


Nie wystarczy jedynie stworzenie procedury czy funkcji, aby moga ona by wykorzystywana przez inne aplikacje. Konieczne jest take wyeksportowanie takiej funkcji, aby bya ona ?widoczna? na zewntrz. Do tego suy sowo kluczowe exports. Wystarczy, e przed blokiem begin..end wstawisz taki kod: exports About, SomeFunc;

Powyszy przypadek spowoduje wyeksportowanie dwch procedur (funkcji): About i SomeFunc. 398 | S t r o n a

Eksportowanie przez nazw


Eksportowa funkcje oraz procedury mona na dwa sposoby: przez nazw oraz przez indeks. Spjrz na listing 10.2. Znajduje si tam kod rdowy prostej biblioteki, w ktrej nastpuje eksport procedury About poprzez nazw. Listing 10.2. Eksport procedury About library SimpleDLL; uses Windows; procedure About; stdcall; begin MessageBox(0, 'Hello World!', 'Hello', MB_OK + MB_ICONINFORMATION); end; exports About name 'About'; begin end.

Wyeksportowanie procedury czy te funkcji poprzez nazw polega jedynie na uyciu dyrektywy name. W cudzysowach naley umieci nazw eksportowanej procedury, ktra moe by rna od rzeczywistej nazwy. A zatem jeeli procedura nazywa si About, nic nie stoi na przeszkodzie, aby wyeksportowa j pod nazw MyDLL (pod tak te nazw ow procedur bd ?widzie? inne programy).

Eksport przez indeks


Istnieje inny sposb eksportu procedur lub funkcji ? przez indeks. Moim zdaniem jest to rzadziej stosowany sposb eksportu. Wynika to z tego, e zamiast nazw uywamy indeksw ? np.: exports About index 1, SomeFunc index 2;

Jeli w takim wypadku chcemy wykorzysta owe funkcje, naleaoby w naszej aplikacji zapamita 399 | S t r o n a

indeksy, z jakimi s one eksportowane z biblioteki DLL. atwiej jest jednak zapamita nazwy ni cyfry, std eksportowanie przez indeks jest uywane do rzadko.

adowanie bibliotek DLL


Oglnie rzecz biorc, wykorzystanie procedur i funkcji zawartych w bibliotekach DLL nie jest czym nadzwyczajnym. Jest to do prosta czynno, chocia by moe to moja subiektywna opinia. adowanie bibliotek DLL moe by statyczne lub dynamiczne. Te pierwsze jest raczej proste, za to adowanie dynamiczne moe sprawi nieco problemw.

adowanie statyczne
Za przykad niech posuy prosty kod rdowy z katalogu ../listingi/10/SimpleDLL, znajdujcy si na doczonej do ksiki pycie CD-ROM. adowanie statyczne polega na pobraniu procedury z biblioteki DLL w momencie uruchomienia aplikacji. Pamitaj, e jeli plik DLL nie istnieje lub nie zawiera danej procedury, program nie zostanie uruchomiony. Utwrz nowy projekt, a nastpnie dodaj poniszy wiersz w sekcji interface: procedure About; stdcall external 'SimpleDLL.dll' name 'About';

Deklaracja powyszej procedury musi mie specyficzn budow ? od tej pory podczas wywoania procedury About zaadowana zostanie procedura z biblioteki DLL. Nie bdziemy teraz wnika w znaczenie sowa stdcall, zajmiemy si za to sowem external, ktre spowoduje zaadowanie biblioteki DLL. W pierwszym rzdzie naley wpisa w cudzysowie nazw biblioteki DLL, a pniej nazw procedury (funkcji), ktr chcemy importowa. Gdybymy importowali procedur przez indeks, deklaracja procedury wygldaaby tak: procedure About; stdcall external 'SimpleDLL.dll' index 1;

Zamiast sowa kluczowego name wystarczy po prostu wpisa index, a nastpnie cyfr, jak opatrzylimy ow procedur w bibliotece DLL.

400 | S t r o n a

Uycie procedury znajdujcej si w bibliotece wyglda tak: procedure TMainForm.btnLoadClick(Sender: TObject); begin About; end;

Nic nie stoi na przeszkodzie, aby zadeklarowana w programie procedura miaa inn nazw ni ta, ktra jest rzeczywicie adowana z biblioteki DLL.

adowanie dynamiczne
adowanie dynamiczne procedury lub funkcji moe by troch trudniejsze, gdy polega na zaadowaniu biblioteki i jej zwolnieniu w okrelonym momencie. Zalet adowania dynamicznego jest wiksza kontrola nad wykorzystan bibliotek oraz moliwo gospodarowania pamici. Pami na potrzeby biblioteki jest przydzielana nie podczas uruchamiania programu, ale w okrelonym przez nas momencie. Oto przykad zaadowania procedury w sposb dynamiczny: procedure TMainForm.btnDynamicLoadClick(Sender: TObject); var DLL : THandle; About : procedure; begin DLL := LoadLibrary('SimpleDLL.dll'); // zaadowanie pliku try @About := GetProcAddress(DLL, 'About'); // pobranie wskanika do procedury if @About = nil then raise Exception.Create('Nie mona zaadowa procedury'); About; // wykonanie procedury finally FreeLibrary(DLL); end; end;

Przyznasz, e w porwnaniu z jednym wierszem kodu w wypadku adowania statycznego istnieje jednak spora rnica. Pierwszym zaskoczeniem moe by taki sposb zadeklarowania zmiennej: var DLL : THandle; About : procedure; 401 | S t r o n a

Zmienna About jest typu procedure?! Taka kombinacja take jest moliwa ?About jest od tej pory wskazaniem procedury. Kolejnym krokiem jest zaadowanie biblioteki DLL za pomoc polecenia LoadLibrary: DLL := LoadLibrary('SimpleDLL.dll'); // zaadowanie pliku

W parametrze owej funkcji naley poda jedynie nazw biblioteki, ktra ma zosta zaadowana. Jeeli adowanie powiedzie si, funkcja zwrci uchwyt do biblioteki DLL. Nastpnie konieczne jest pobranie wskanika do procedury lub funkcji umieszczonej w bibliotece DLL. @About := GetProcAddress(DLL, 'About'); procedury // pobranie wskanika do

W pierwszym parametrze funkcji GetProcAddress naley poda uchwyt biblioteki DLL, a w drugim ? nazw procedury lub funkcji. Jeeli adowanie nie powiedzie si ? zmienna About bdzie miaa warto nil. Przykad adowania biblioteki w sposb dynamiczny oraz statyczny, znajduje si w listingu 10.3. Listing 10.3. adowanie biblioteki DLL w sposb statyczny oraz dynamiczny unit MainFrm; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls; type TMainForm = class(TForm) btnStaticLoad: TButton; btnDynamicLoad: TButton; procedure btnStaticLoadClick(Sender: TObject); procedure btnDynamicLoadClick(Sender: TObject); private { Private declarations } public { Public declarations } end; 402 | S t r o n a

var MainForm: TMainForm; { adowanie statyczne } procedure About; stdcall external 'SimpleDLL.dll' name 'About'; implementation {$R *.dfm} procedure TMainForm.btnStaticLoadClick(Sender: TObject); begin About; // wywoanie procedury end; procedure TMainForm.btnDynamicLoadClick(Sender: TObject); var DLL : THandle; About : procedure; begin DLL := LoadLibrary('SimpleDLL.dll'); // zaadowanie pliku try @About := GetProcAddress(DLL, 'About'); // pobranie wskanika do procedury if @About = nil then raise Exception.Create('Nie mona zaadowa procedury'); About; // wykonanie procedury finally FreeLibrary(DLL); end; end; end.

Konwersje wywoania
Jak dotd moge zauway, procedury prezentowane w tej ksice opatrzone s klauzul stdcall. Podczas deklarowania procedury lub funkcji moesz opatrzy j jedn z klauzul: register, pascal, cdecl, stdcall, safecall, ktre okrelaj sposb przekazywania parametrw do stosu. Domyln klauzul jest register, ktra zapewnia najwiksz efektywno poprzez wysyanie 403 | S t r o n a

parametrw do stosu zgodnie z kolejnoci deklaracji w procedurze ? od lewej do prawej (tak, jak pascal). Natomiast dyrektywy cdecl, stdcall i safecall powoduj wysyanie parametrw do stosu od strony prawej do lewej. Z kolei wszystkie klauzule z wyjtkiem cdecl powoduj zwolnienie parametrw, przez procedur wykonujc. Dyrektywa cdecl jest uyteczna w przypadku, gdy uywane funkcje, znajdujce si w bibliotece DLL, s napisane w C lub C++. Najbardziej uniwersaln klauzul jest stdcall, ktra jest jakby poczeniem dwch: pascal oraz cdecl. Naley uywa dyrektywy stdcall w wypadku, gdy nie jestemy pewni, w jakim jzyku zostaa napisana biblioteka DLL. Spowoduje to moliwie jak najbezpieczniejsze uycie funkcji.

Formularze w bibliotekach DLL


Jak ju wspomniaem wczeniej, rdowy plik biblioteki funkcjonuje na takich samych zasadach, co plik gwny *.dpr. Moliwe jest tworzenie formularzy w projekcie biblioteki DLL. Po otwarciu nowego projektu biblioteki DLL z menu File wybierz New/Form. Spowoduje to utworzenie w ramach projektu (biblioteki DLL) nowego formularza. Formularza moemy uywa tak samo, jak w zwykym projekcie Delphi. To, co nas interesuje, to wyeksportowanie go z biblioteki DLL.

Tworzenie formularza
Niech nasz przykadowy formularz w ramach biblioteki DLL bdzie oknem O programie?. Okno nie musi posiada zbyt wiele ?bajerw? ? wystarczy par pl informacyjnych z notk o autorze itp. Moja propozycja to okno z rysunku 10.2.

Rysunek 10.2. Okno O programie

404 | S t r o n a

Eksportowanie formularza
W rzeczywistoci problemem jest wyeksportowanie formularza z biblioteki DLL tak, aby program by w stanie skorzysta z niego. Jeeli nie modyfikowae kodu, gwny plik *.dpr biblioteki powinien wyglda tak, jak w listingu 10.4. Listing 10.4. Kod rdowy biblioteki library FormDLL; uses SysUtils, Classes, DLLFrm in 'DLLFrm.pas' {DLLForm}; {$R *.res} begin end.

Delphi automatycznie wczy nazw moduu, z formularzem do listy uses. Wczeniej mwiem o eksporcie formularza. W rzeczywistoci nie musimy eksportowa caego formularza, a jedynie funkcj, ktra spowoduje jego wywietlenie (listing 10.5). Listing 10.5. Gwny plik biblioteki po modyfikacji library FormDLL; uses Forms, DLLFrm in 'DLLFrm.pas' {DLLForm}; procedure ShowAboutForm; stdcall; begin DLLForm := TDLLForm.Create(Application); // utworzenie formularza DLLForm.ShowModal; // wywietlenie DLLForm.Free; // zwolnienie end; exports { eksport procedury } ShowAboutForm name 'ShowAboutForm'; begin end. 405 | S t r o n a

Do wywietlenia formularza uyjemy procedury ShowAboutForm. Procedura ta ma na celu utworzenie formularza (alokacja pamici) i jego wywietlenie. Z takim lub podobnym kodem moge si ju spotka w rozdziale 4. To waciwie wszystko! Teraz wystarczy w jakim miejscu programu wykorzystujcego ow bibliotek zadeklarowa nastpujc procedur: procedure About; stdcall external 'FormDLL.dll' name 'ShowAboutForm';

Po wywoaniu procedury About zaadowany zostanie formularz z pliku DLL. Peny kod rdowy tego programu moesz znale na pycie CD-ROM w katalogu ../listingi/10/FormDLL.

Przekazywanie rekordw do bibliotek


Za chwil przedstawi przykad, w ktrym aplikacja bdzie wsppracowa z bibliotek DLL, przekazujc cae rekordy danych. Bdzie to nieco praktyczniejsze zaprezentowanie funkcjonalnoci bibliotek. Mianowicie funkcja zawarta w pliku DLL bdzie odpowiadaa za odczytanie z pliku mp3 tzw. tagu (ID3v1). Ci, ktrzy posiadaj na swoim dysku pliki mp3, wiedz, o czym mwi. Program bdzie wyglda tak, jak na rysunku 10.3.

Rysunek 10.3. Program w dziaaniu

406 | S t r o n a

Budowa pliku mp3


Aby umoliwi odczyt informacji z pliku mp3, naley zna jego budow. Poniewa konstrukcja pliku mp3 jest oglnie dostpna, zatem wiemy, e zawarto interesujcego nas tagu znajduje si w ostatnich 128 bajtach pliku mp3. Opis budowy pliku mp3 moesz cign m.in. ze strony http://4programmers.net. Naszym zadaniem jest odczytanie tych 128 kocowych bajtw, a nastpnie rozdzielenie ich na fragmenty ? np. tytu, wykonawc itp. Rekord, ktrym posugiwaa si bdzie nasza aplikacja z plikiem DLL, wyglda tak: type { rekord, ktry bdzie eksportowany do aplikacji } TMp3 = packed record ID: String[3]; // czy Tag istnieje? Title : String[30]; // tytu Artist : String[30]; // wykonawca Album : String[30]; // album Year : String[4]; // rok wydania Comment : String[30]; // komentarz Genre : String[30]; // typ ? np. POP, Techno, Jazz itp. end; PMp3 = ^TMp3;

Odczyt tagu z pliku mp3


Pobranie informacji z pliku mp3 nie powinno by problemem, jeeli znamy jego budow oraz jeli potrafimy wykorzysta dostpne w Delphi mechanizmy. O obsudze plikw bya mowa w rozdziale 7. Ja posuyem si mechanizmem plikw amorficznych (rwnie dobrze mogem wykorzysta strumienie), gdy do prawidowego funkcjonowania biblioteki nie jest potrzebny aden modu ? dziki temu rozmiar biblioteki jest mniejszy: procedure LoadTag(const lpFileName : PChar; Tag : PMp3); stdcall; var F : File; Buffer : array[1..128] of char; begin AssignFile(F, String(lpFileName)); try Reset(F, 1); { przesunicie pozycji na 128. bajt od koca } Seek(F, FileSize(F) ? 128); BlockRead(F, Buffer, 128); // odczytanie zawartoci bufora 407 | S t r o n a

{ do rekordu przypisz informacje odczytane z pliku mp3 } with Tag^ do begin ID := Copy(Buffer, 1, 3); Title := Copy(Buffer, 4, 30); Artist := Copy(Buffer, 34, 30); Album := Copy(Buffer, 64, 30); Year := Copy(Buffer, 94, 4); Comment := Copy(Buffer, 98, 30); Genre := GenreArray[Ord(Buffer[128])]; end; finally CloseFile(F); end; end;

Powyej przedstawiem procedur zawart w pliku DLL. W pierwszej kolejnoci nastpuje otwarcie pliku i przesunicie si w odpowiednie jego miejsce. Drugi etap to odczyt kocowych 128 bajtw pliku (BlockRead). Nastpnie nie pozostaje nic innego, jak rozdzieli dane na informacje dotyczce tytuu, wykonawcy itp. (funkcja Copy). Cay kod rdowy biblioteki prezentuje listing 10.6. Listing 10.6. Kod rdowy biblioteki mp3DLL.dll. { Copyright (c) 2003 by Adam Boduch } library mp3DLL;

type { rekord, ktry bdzie eksportowany do aplikacji } TMp3 = packed record ID: String[3]; // czy Tag istnieje? Title : String[30]; // tytu Artist : String[30]; // wykonawca Album : String[30]; // album Year : String[4]; // rok wydania Comment : String[30]; // komentarz Genre : String[30]; // typ ? np. POP, Techno, Jazz itp. end; PMp3 = ^TMp3;

const 408 | S t r o n a

oto tablica zawierajca typy utworw } GenreArray : array[0..79] of ShortString = ( ('Blues'), ('Classic Rock'), ('Country'), ('Dance'), ('Disco'), ('Funk'), ('Grunge'), ('Hip?Hop'), ('Jazz'), ('Metal'), ('New Age'), ('Oldies'), ('Other'), ('Pop'), ('R&B'), ('Rap'), ('Reggae'), ('Rock'), ('Techno'), ('Industrial'), ('Alternative'), ('Ska'), ('Death Metal'), ('Pranks'), ('Soundtrack'), ('Euro?Techno'), ('Ambient'), ('Trip?Hop'), ('Vocal'), ('Jazz+Funk'), ('Fusion'), ('Trance'), ('Classical'), ('Instrumental'), ('Acid' ), ('House'), ('Game'), ('Sound Clip'), ('Gospel'), ('Noise'), ('AlternRock'), ('Bass'), ('Soul'), ('Punk'), ('Space'), ('Meditative'), ('Instrumental Pop'), ('Instrumental Rock'), ('Ethnic'), ('Gothic'), ('Darkwave'), ('Techno?Industrial'), ('Electronic'), ('Pop?Folk'), ('Eurodance'), ('Dream'), ('Southern Rock'), ('Comedy'), ('Cult'), ('Gangsta'), ('Top 40'), ('Christian Rap'), ('Pop/Funk'), ('Jungle'), ('Native American'), ('Cabaret'), ('New Wave'), ('Psychadelic'), ('Rave'), ('Showtunes'), ('Trailer'), ('Lo?Fi'), ('Tribal'), ('Acid Punk'), ('Acid Jazz'), ('Polka'), ('Retro'), ('Musical'), ('Rock & Roll'), ('Hard Rock') ); procedure LoadTag(const lpFileName : PChar; Tag : PMp3); stdcall; var F : File; Buffer : array[1..128] of char; begin AssignFile(F, String(lpFileName)); try Reset(F, 1); { przesunicie pozycji na 128 bajt od koca } Seek(F, FileSize(F) ? 128); BlockRead(F, Buffer, 128); // odczytanie zawartoci bufora { do rekordu przypisz informacje odczytane z pliku mp3 } with Tag^ do begin ID := Copy(Buffer, 1, 3); Title := Copy(Buffer, 4, 30); Artist := Copy(Buffer, 34, 30); Album := Copy(Buffer, 64, 30); Year := Copy(Buffer, 94, 4); Comment := Copy(Buffer, 98, 30); Genre := GenreArray[Ord(Buffer[128])]; 409 | S t r o n a

end; finally CloseFile(F); end; end; exports LoadTag name 'LoadTag'; begin end.

Zwr uwag na to, e do stworzenia tej biblioteki nie by potrzebny aden modu. Ostateczny rozmiar biblioteki to 36 KB.

Demo
Skoro mamy ju bibliotek, ktra realizuje dane zadanie, to napisanie programu j wykorzystujcego jest jedynie formalnoci.(listing 10.7.). Listing 10.7. Program korzystajcy z biblioteki DLL { Copyright (c) 2003 by Adam Boduch } unit MainFrm; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls; type TMainForm = class(TForm) GroupBox1: TGroupBox; OpenDialog: TOpenDialog; lblTitle: TLabel; lblArtist: TLabel; lblAlbum: TLabel; lblYear: TLabel; lblComments: TLabel; 410 | S t r o n a

lblGenre: TLabel; edtTitle: TEdit; edtArtist: TEdit; edtAlbum: TEdit; edtYear: TEdit; edtComments: TEdit; edtGenre: TEdit; btnLoad: TButton; procedure btnLoadClick(Sender: TObject); private { Private declarations } public { Public declarations } end; PMp3 = ^TMp3; TMp3 = packed record ID: String[3]; // czy Tag istnieje? Title : String[30]; // tytu Artist : String[30]; // wykonawca Album : String[30]; // album Year : String[4]; // rok wydania Comment : String[30]; // komentarz Genre : String[30]; // typ ? np. POP, Techno, Jazz itp. end; var MainForm : TMainForm; implementation {$R *.dfm} procedure LoadTag(const lpFileName : PChar; Tag : PMp3); stdcall external 'mp3DLL.dll' name 'LoadTag'; procedure TMainForm.btnLoadClick(Sender: TObject); var Tag : TMp3; begin if OpenDialog.Execute then begin LoadTag(PChar(OpenDialog.FileName), @Tag); // wywoaj funkcje z biblioteki DLL if Tag.ID <> 'TAG' then Exit; // jeeli tag nie istnieje ? anuluj dalsze dziaania with Tag do begin 411 | S t r o n a

{ wartoci z rekordu przypisz do komponentw } edtTitle.Text := Tag.Title; edtArtist.Text := Tag.Artist; edtAlbum.Text := Tag.Album; edtYear.Text := Tag.Year; edtComments.Text := Tag.Comment; edtGenre.Text := Tag.Genre; end; end; end; end.

Po zaadowaniu procedury z biblioteki rekord Tag powinien zawiera informacje dotyczce tagu z pliku mp3. Jeeli plik mp3 jest pozbawiony tej informacji, to element ID rekordu Tag nie bdzie zawiera wartoci TAG. Na kocu pozostaje jedynie przedstawienie wartoci w komponentach TEdit.

acuchy w bibliotekach DLL


Ten podpunkt jest zwizany z uyciem acuchw w bibliotekach DLL. Ma to zwizek z komentarzem, ktry znajduje si w kodzie biblioteki zaraz po utworzeniu jej poprzez Repozytorium. Twrcy przestrzegaj w owym komentarzu przed uywaniem w bibliotekach acuchw typu String. Taki typ danych nie moe znale si w parametrach eksportowanych procedur; nie moe te wystpowa jako zwracany przeze element. Zamiast tego naley uywa acuchw typu PChar lub ShortString. Uywanie dugich acuchw nie jest wykluczone, lecz zarwno na licie uses w bibliotece, jak i w aplikacji musi si znale modu ShareMem. Uwaga! Musi on si znale na pierwszym miejscu spord wszystkich moduw na licie uses: uses ShareMem, Windows, Classes; // itd....

Zasoby w bibliotece DLL


412 | S t r o n a

Dziki bibliotekom DLL mona w do prosty sposb stworzy aplikacj z wielojzycznym interfejsem. Wszystko dziki zasobom, o ktrych mowa bya w poprzednim rozdziale. Tworzenie zasobw nie powinno ju dla Ciebie stanowi problemu. Wczenie ich do biblioteki DLL wyglda identycznie, jak w przypadku zwykego projektu:
{$R ZASOBY.RES}

Od tego momentu cay plik ZASOBY.RES zostanie wczony do projektu. W poprzednim rozdziale podawaem przykad, w jaki sposb mona odczyta zasoby umieszczone w aplikacji. W przypadku bibliotek DLL adowanie jest podobne.

Przygotowanie zasobw
W moim przykadzie umieszcz w zasobach biblioteki DLL acuchy tekstowe. Zasb w postaci pliku *.rc zaprezentowany zosta w listingu 10.8. Listing 10.8. Tre skryptu zasobw
STRINGTABLE BEGIN 101, "Cze" 102, "Hello" END

Po skompilowaniu tego pliku w katalogu z programem powinien znajdowa si plik TEKST.RES. Kod biblioteki jest krtki: library ResDLL;

{$R TEKST.res} begin end.

W kodzie znajduje si jedynie dyrektywa powodujca wczenie zasobw do programu.

adowanie zasobw z biblioteki DLL


W poprzednim rozdziale zaprezentowaem Ci, drogi Czytelniku, funkcj LoadString, ktra 413 | S t r o n a

umoliwiaa pobranie tekstu z zasobw. Uycie tej procedury w tym momencie jest bardzo podobne, tyle e pierwszy parametr musi by wskazaniem biblioteki DLL. Oto kod: procedure TMainForm.FormCreate(Sender: TObject); var DLL : THandle; Buffer : array[0..255] of char; // bufor przechowujcy tekst begin DLL := LoadLibrary('ResDLL.dll'); try { zaadowanie tekstu } LoadString(DLL, 101, Buffer, SizeOf(Buffer)); lblLabel1.Caption := Buffer; LoadString(DLL, 102, Buffer, SizeOf(Buffer)); lblLabel2.Caption := Buffer; finally FreeLibrary(DLL); end; end;

Funkcja LoadLibrary zwraca uchwyt do zaadowanej biblioteki (THandle). Ten uchwyt jest nastpnie wykorzystywany podczas adowania tekstu. Na doczonej do ksiki pycie CD-ROM znajduj si inne przykady wykorzystania zasobw w bibliotekach DLL. W katalogu ../listingi/10/ResDLL umieszczony jest przykad prezentujcy adowanie tekstu oraz bitmap ? wszystkie te procesy dziaaj w wtku. Kolejny przykad (../listingi/10/MusicDLL) naleaoby umieci w trzech dziaach niniejszej ksiki. Prezentuje on moliwo odtwarzania dwikw przy wykorzystaniu mechanizmw WinAPI (bdzie o tym mowa w kolejnym rozdziale). Wszystkie funkcje s jednak umieszczone w bibliotece DLL. Wreszcie ostatni przykad z katalogu ../listingi/10/DLL & Image prezentuje wspdziaanie biblioteki oraz aplikacji w zakresie operowania grafik.

Procedura inicjujco-koczca
Korzystajc z moduu, moemy w nim umieci sekcje initialization oraz finalization, w ktrych kod bdzie wykonywany w momencie przyczenia moduu do pamici lub jego odczenia (zaprzestania korzystania). W kodzie biblioteki DLL nie moemy stosowa tych sekcji, ale dysponujemy czym w rodzaju ich odpowiednikw ? instrukcj inicjujco-koczc.

414 | S t r o n a

Blok begin biblioteki DLL


Dotd podczas omawiania bibliotek DLL nie umieszczaem instrukcji w bloku begin i end. Kod znajdujcy si w tym bloku zostanie uruchomiony na samym pocztku ?w czasie, gdy biblioteka bdzie adowana do pamici. library DLL; uses Windows; { jakie procedury } begin MessageBox(0, 'Witaj! Wanie korzystasz z mojej biblioteki!', ':?)', MB_OK); end.

Po zaadowaniu biblioteki za pomoc LoadLibrary najpierw na ekranie zostanie wywietlony tekst w okienku.

DLLProc
Poprzedni przykad pokazywa, jak przechwytywa moment adowania biblioteki do pamici, lecz co w przypadku jej zwalniania? W bloku begin..end do ukrytej zmiennej DLLProc moemy przypisa procedur inicjujco-koczc, dziki ktrej bdziemy mogli przechwyci zakoczenie korzystania z biblioteki (tabela 10.1). Nazwa Opis

DLL_PROCESS_ATTACH Zaadowanie biblioteki przez program j wykorzystujcy DLL_PROCESS_DETACH Zwalnianie biblioteki przez program j wykorzystujcy DLL_THREAD_ATTACH Proces korzystajcy z danej biblioteki uruchomi wtek DLL_THREAD_DETACH Proces korzystajcy z danej biblioteki zakoczy wtek

415 | S t r o n a

Aby przechwyci zwalnianie biblioteki lub wtku, naley utworzy pewn procedur ? nazwijmy j DLL_Proc. procedure DLL_Proc(Reason : Integer); begin end;

Procedura musi mie cile okrelon budow ? w tym wypadku istotny jest parametr Reason. Oto, jak powinna wyglda cao: library DLLPro;

procedure DLL_Proc(Reason : Integer); begin end; begin DLLProc := @DLL_Proc; end.

Teraz, stosujc instrukcj case w procedurze DLL_Proc, moesz wpisa kod, ktry bdzie wykonywany zalenie od zdarzenia. Uwaaj, aby nie zapisa projektu biblioteki DLL pod nazw DLLProc, czyli tak sam, jak nazwa zmiennej. Zauwa, e w bloku begin do przypisania wartoci do DLLProc uyem znaku @. Wszystko dlatego, ze DLLProc jest zmienn typu Pointer.

Kod biblioteki
Zamy, e chcemy, aby biblioteka przechwytywaa moment zaadowania jej do pamici, jej zwalniania, a take moment uruchamiania wtku przez program wykorzystujcy w plik DLL. Kod biblioteki mgby wyglda tak, jak na listingu 10.9. Listing 10.9. Kod biblioteki wykorzystujcej procedur inicjujco-koczc library DLLPro; uses Windows; 416 | S t r o n a

procedure DLL_Proc(Reason : Integer); begin case Reason of DLL_PROCESS_DETACH: MessageBox(0, 'Zwalnianie biblioteki...', '', MB_OK); DLL_THREAD_ATTACH: MessageBox(0, 'Tworzenie wtku...', '', MB_OK); DLL_THREAD_DETACH: MessageBox(0, 'Zamknicie wtku...', '', MB_OK); end; end; begin DLLProc := @DLL_Proc; MessageBox(0, 'adowanie biblioteki...', '', MB_OK); end.

Od tego momentu, w zalenoci od zaistniaego zdarzenia, wywietlony zostanie komunikat informacyjny. Zwr uwag, e w procedurze DLL_Proc nie uyem DLL_PROCESS_ATTACH. Ta staa znajduje si w Delphi jedynie z powodw zachowania kompatybilnoci z poprzednimi wersjami. W rzeczywistoci nie jest wykorzystywana, a kod, ktry ma zosta wykonany na starcie (po zaadowaniu biblioteki), powinien zosta umieszczony w bloku begin..end.

Program wykorzystujcy bibliotek


Wykorzystanie biblioteki, ktra korzysta z procedury inicjujco-koczcej, nie jest niczym nadzwyczajnym. Nie potrzeba adnych specjalnych instrukcji ? zaadowanie i zwolnienie biblioteki moe wyglda np. tak: var DLL : THandle; procedure TMainForm.FormCreate(Sender: TObject); begin DLL := LoadLibrary('DLLPro.dll'); // zaaduj bibliotek end; procedure TMainForm.Button1Click(Sender: TObject); begin FreeLibrary(DLL); // zwolnij zasoby Application.Terminate; end; 417 | S t r o n a

Dobrze, ale co z wtkami? Na pycie CD-ROM umieciem bardziej rozbudowany przykad dziaania procedury inicjujco-koczcej. Znajduje si on w katalogu ../listingi/10/Entry Proc. Program wykorzystujcy bibliotek DLL moe uruchomi wtek, co spowoduje przechwycenie tego faktu przez nasz bibliotek. Wysya ona do naszej aplikacji odpowiedni komunikat (przy okazji moemy sobie utrwali wiedz dotyczc komunikatw). Wykorzystanie procedury inicjujco-koczcej jest dobrym sposobem na tworzenie bibliotek DLL typu shareware . W kodzie begin mona umieci kod (komunikat informacyjny), ktry moe informowa programist o tym, e korzysta z niepenej funkcjonalnoci biblioteki i naley za ni zapaci.

Podsumowanie
Jeeli poznasz ju zasady projektowania bibliotek DLL, oka si, e nie jest to takie trudne. Tworzenie bibliotek DLL jest kolejnym krokiem do projektowania bardziej zaawansowanych programw, w ktrych podzia okazuje si bardziej ?ekonomiczny?. Zaczniki:

Listingi_10.zip (416.63 kB)

Rozdzia 11

Edytuj Historia Przenie Obserwuj

Aplikacje sieciowe
Czy nie zauwaye, drogi Czytelniku, e programy, z ktrych korzystamy na co dzie, posiadaj coraz wicej opcji umoliwiajcych integracj z serwerami producenta? Programy sprawdzaj, czy w Internecie jest ju dostpna nowa wersja , umoliwiaj aktualizacj moduw, a nawet ?szpiegowanie? Twojego komputera. W tym rozdziale zajmiemy si wykorzystaniem dostpnych w Delphi komponentw, aby cho troch usprawni dziaanie naszych programw. 418 | S t r o n a

Spis treci 1 Z czego bdziemy korzysta? 2 Odrobin teorii 2.1 IP 2.2 TCP 2.3 Porty 2.4 HTTP 2.5 HTTPS 2.6 FTP 2.7 SMTP 3 Biblioteka WinInet.dll 3.1 Ustanawianie poczenia 3.2 Otwieranie konkretnego adresu URL 3.3 Odczyt pliku 3.4 Pobieranie rozmiaru pliku 4 Sprawdzanie poczenia 5 Sprawdzanie IP 5.1 Zainicjowanie biblioteki 5.2 Pobieranie adresu IP 6 czenie przy uyciu gniazd 6.1 Czego uy? 6.2 czenie pomidzy komputerami 6.3 Wymiana danych 6.3.1 Tworzenie komend 6.4 Jak dziaaj ?konie trojaskie?? 7 Pingi 7.1 Wysyanie sygnau ping 7.2 Odpowiedzi z serwera 8 Kontrolka TWebBrowser 8.1 adowanie strony 8.2 Odwieanie 8.3 Nastpna i poprzednia strona 8.4 Pozostae kody 8.4.1 Wywietlanie adresu zaadowanej strony 8.4.2 Wywietlanie tytuu strony 8.4.3 Sprawdzenie, czy strona jest chroniona (SSL) 8.4.4 Ustawienie koloru ta strony 9 Protok SMTP 9.1 Interfejs programu 9.2 Dziaanie programu 9.3 Zdarzenia komponentu 9.3.1 Kod rdowy 10 Protok HTTP 419 | S t r o n a

10.1 czenie z serwerem 10.2 Wymiana danych 10.2.1 Metoda POST 10.2.2 Metoda GET 10.3 Pobieranie kodu strony WWW 10.4 Wysyanie danych przez skrypt PHP 10.4.1 Wysyanie danych do skryptu PHP 11 Praktyczne przykady wykorzystania HTTP 11.1 Sprawdzenie nowej wersji programu 11.1.1 Jak to dziaa? 11.1.2 Budowa programu 11.2 Korzystanie z zewntrznej wyszukiwarki 11.2.1 Jak to dziaa? 11.2.2 Struktura kodu HTML 11.2.3 Analiza kodu HTML 11.2.4 Kod rdowy programu 12 Protok FTP 13 Podsumowanie

W tym rozdziale:

omwi zasady dziaania najpopularniejszych protokow sieciowych; dowiesz si, jak wykrywa, czy istnieje poczenie internetowe; napiszesz program sucy do pobierania plikw z Internetu; nauczysz si obsugiwa protok SMTP i wysya e-maile; dowiesz si, jak ? korzystajc z protokou HTTP ? wysya dane do skryptw lub odczytywa zawarto strony WWW.

Z czego bdziemy korzysta?


Windows posiada bibliotek DLL o nazwie WinSock.dll, ktra realizuje bardzo wiele czynnoci zwizanych z dostpem do Internetu. Wykorzystanie funkcji z owej biblioteki moe by troch trudne, dlatego nie warto wywaa otwartych drzwi ? mona skorzysta z komponentw oferowanych nam przez firm Borland. W zakadce Internet znajduj si np. takie komponenty, jak TTcpClient i TTcpServer, przeznaczone do czenia si ze sob za pomoc protokou TCP/IP. Na tej samej zakadce znajduje si 420 | S t r o n a

take komponent TudpSocket do czenia si za pomoc protokou UDP/IP. Zrezygnowano natomiast z komponentw, do ktrych wiele ludzi mogo si ju przyzwyczai ? TClientSocket oraz TServerSocket. Teraz ich funkcj maj peni wanie komponenty TTcpClient i TTcpServer. Do Delphi doczony jest bardzo popularny pakiet komponentw Indy, uatwiajcych wykorzystanie rnych protokow sieciowych. Do obecnej wersji Delphi doczono Indy 9, w efekcie czego powstao pi nowych zakadek: Indy Clients, Indy Servers, Indy Intercepts, Indy I/O Handlers i Indy Misc. Komponenty te udostpniane s na zasadzie Open Source. Najnowsz wersj moesz znale na stronie www.nevrona.com/Indy/. Nie musimy korzysta wycznie z pakietu Indy ? ze strony www.overbyte.be moemy cign rwnie popularny pakiet ICS, stworzony przez belgijskiego programist Franois Piette. Zdaniem wielu programistw pakiet ICS jest lepszy od Indy, lecz nieco trudniejszy w uytkowaniu.

Odrobin teorii
Internet jest peen fachowych okrele i niezbyt zrozumiaych skrtw. Dla zwykego, ?szarego? uytkownika nie jest istotne, co oznacza i jak dziaa IP czy protok TCP. Rozpoczynajc pisanie aplikacji korzystajcych z Internetu, powiniene by zorientowany przynajmniej w podstawach funkcjonowania protokow internetowych. Dlatego te par najbliszych stron powicimy omwieniu podstawowych zagadnie z tym zwizanych. Jeeli Ci to nie interesuje lub znasz zasad dziaania protokow internetowych, to moesz miao omin te strony.

IP
Z pojciem adres IP zetkne si pewnie nieraz. Jest to skrt od angielskiego sowa Internet Protocol. Najprociej mwic, adres IP jest to adres komputera w sieci. Internet to ?pajczyna?, w ktrej poczonych jest wiele komputerw. IP jest 32-bitow liczb, informujc, do jakiej sieci wczony jest dany komputer. Liczba ta zapisywana jest w postaci czterech liczb, oddzielonych od siebie kropk. Przykadowy adres IP wyglda np. tak: 223.125.100.155. Adresy IP podzielone s na klasy ? patrz tabela 11.1. Tabela 11.1. Klasy IP Klasa Najmniejszy adres Najwikszy adres

421 | S t r o n a

A B C D E

0.1.0.0 128.0.0.0 192.0.1.0 224.0.0.0 240.0.0.0

126.0.0.0 191.255.0.0 223.255.255.0 239.255.255.255 247.255.255.255

Tabela 11.1 nie zawiera jednak m.in. adresw IP od 127.0.0.0 do 127.255.255.255. Powd jest prosty ? s to adresy lokalne, ktre nie istniej w Internecie. W celu zapewnienia zgodnoci adresy s przydzielane przez jedn organizacj o nazwie Internet Network Information Center. Organizacja przydziela adresy wielkim sieciom, ktre z kolei rozdzielaj przyznane adresy midzy swoich uytkownikw. Ilo komputerw w sieci stale ronie. Aby kademu z nich zagwarantowa stay, niepowtarzalny numer, trzeba byo rozbudowa protok IP ? nowa wersja nosi nazw IPv6. Wykorzystywane w niej s liczby 128-bitowe, a nie ? jak dotychczas ? 32-bitowe.

TCP
Protok TCP (ang. Transmission Control Protocol) jest drug co do wanoci usug w sieci (po IP). Polega ona na przesyaniu danych (pakietw) pomidzy dwoma komputerami. Poczenie z dwoma komputerami odbywa si poprzez podanie adresu IP. Wtedy jeden z komputerw na podstawie adresu IP oraz portu (o portach czytaj dalej) czy si z drugim komputerem. Protok TCP jest czsto nazywany niezawodn usug przesyania danych ? dlatego, e po wysaniu pakietu komputer czeka na otrzymanie potwierdzenia, tzw. ACK (patrz rysunek 11.1).

422 | S t r o n a

Rysunek 11.1. Zasada wysyania pakietw TCP/IP Jeeli komputer nie otrzyma potwierdzenia ACK, czeka okrelon ilo czasu, po czym uznaje, i pakiet nie dotar ? nastpuje przesyanie po raz kolejny. Na TCP opiera si wiele innych protokow ? np. FTP czy SMTP. Take sieci lokalne LAN korzystaj z tego protokou.

Porty
Komputer, ktry prbuje si poczy z innym komputerem, to klient. Natomiast komputer po drugiej stronie, ktry oczekuje na przyczenie klienta, jest nazywany serwerem. Jednak na jednym komputerze moe by uruchomionych wiele aplikacji ? skd mamy wiedzie, do ktrej klient chce si poczy? Do tego su porty, ktre stanowi swoiste ?furtki?. Jest to liczba z zakresu od 1 do 65 000; gdy chcemy si poczy z innym komputerem, wraz z adresem IP musimy poda numer portu. Tak wic port aplikacji klienta oraz aplikacji serwera musi by taki sam, aby doszo do poczenia.

HTTP
Niezwykle popularny protok HTTP (Hyper-Text Transfer Protocol) suy do transmisji stron internetowych WWW z serwera do przegldarki. Na podstawie wpisanego w przegldarce adresu nastpuje lokalizacja adresu IP serwera (adres IP jest ustalany na podstawie domeny) ? stamtd przesyane s informacje do przegldarki uytkownika. Moliwe jest zwrcenie przez serwer komunikatu o bdzie ? np. bd o numerze 404 to brak strony, ktr prbujemy zaadowa. W tabeli 11.2 przedstawiem najczstsze numery bdw. Tabela 11.2. Numery bdw HTTP 423 | S t r o n a

Numer bdu Opis 200 OK 206 Czciowa zawarto 301 Przeniesiono na stae 302 Przeniesiono tymczasowo 304 Niezmodyfikowany 400 Bdne danie 403 Zakazane (brak dostpu) 404 Brak strony 405 Nieuznawania metoda 412 Warunki niespenione 500 Bd serwera

HTTPS
Protok HTTP moe nie zapewnia wystarczajcego poziomu bezpieczestwa dla szczeglnie wanych operacji internetowych ? np. zakupw, operowania pienidzmi w bankach itp. HTTPS jest rozbudowan wersj protokou HTTP, umoliwiajc szyfrowanie danych przepywajcych z komputera klienta do serwera WWW i odwrotnie. Takie szyfrowanie nosi nazw SSL (Secure Socket Layer). To, czy strona jest szyfrowana, czy te nie, okrela adres internetowy ? adresy stron wykorzystujcych SSL zaczynaj si od https://.

FTP
FTP to skrt od angielskiego sowa File Transfer Protocol. Jest to niezwykle stary protok, obecny praktycznie od pocztku istnienia Internetu. Polega on na poczeniu dwch komputerw na zasadzie klient-serwer. Poczenie zazwyczaj odbywa si poprzez port 21. (popularne usugi, np. FTP, zawsze maj taki sam port, aby wystarczyo zna tylko sam adres). Tak wic klient czy si z danym komputerem w wyniku podania jego adresu IP. Po poczeniu moe odbywa si dwustronna komunikacja na zasadzie przesyania plikw.

SMTP
Jeeli korzystasz z Internetu, to pewnie nieraz syszae o tym protokole. Suy on do wysyania listw elektronicznych. Komunikacja odbywa si na zasadzie klient-serwer ? klient czy si z okrelonym adresem IP przez okrelony port (zazwyczaj 25.). Wysya do serwera komend MAIL, po czym, gdy otrzyma odpowied, nastpuje transmisja listu do serwera (najpierw nagwek, a potem tre wiadomoci). Niektre serwery wymagaj tzw. autoryzacji. Zabezpieczaj si w ten sposb przed wysyaniem spamu. Przy zakadaniu konta pocztowego ? niezalenie od tego, na ktrym serwerze ? musimy poda nazw uytkownika (login) oraz haso. Podczas konfiguracji programu pocztowego 424 | S t r o n a

naley wpisa w odpowiednie pola obie te wartoci. Podczas poczenia te dane przesyane s do serwera; serwer sprawdza, czy s prawdziwe (tj. czy taka skrzynka e-mail jest rzeczywicie zaoona na tym serwerze) i, jeeli wszystko si powiedzie, odbywa si transmisja.

Biblioteka WinInet.dll
Na samym pocztku tego rozdziau zaczniemy od korzystania z biblioteki WinInet.dll, a nie z komponentw. To moe by trudny pocztek, gdy korzystanie z biblioteki WinInet.dll wcale nie jest takie proste. To dlatego tak popularne s pakiety komponentw w stylu Indy, gdy uatwiaj znacznie prac z programem. Chciabym jednak pokaza, na czym opiera si tworzenie programw z uyciem tej biblioteki, gdy oglnie dostpne informacje na ten temat s raczej skpe. Przy okazji czytania tego rozdziau napiszemy program umoliwiajcy pobieranie plikw z Internetu (rysunek 11.2).

Rysunek 11.2. Program pobierajcy plik z Internetu Do skorzystania z funkcji biblioteki WinInet.dll bdzie Ci potrzebny modu WinInet.pas ? dodaj jego nazw do listy moduw uses.

Ustanawianie poczenia
W celu zainicjowania biblioteki Wininet.dll bdziemy musieli skorzysta z funkcji InternetOpen: function InternetOpen(lpszAgent: PChar; dwAccessType: DWORD; lpszProxy, lpszProxyBypass: PChar; dwFlags: DWORD): HINTERNET; stdcall;

Znaczenie poszczeglnych parametrw jest nastpujce:


lpszAgent ? nazwa programu (przegldarki), jaka bdzie identyfikowana przez serwer. dwAccessType ? dostp do sieci Internet ? patrz tabela 11.3.

425 | S t r o n a

lpszProxy ? serwer poredniczcy (proxy), z ktrym bdziemy si czyli. lpszProxyBypass ? lista adresw IP, ktre z jakiego powodu nie powinny mie dostpu

do serwera proxy.
dwFlags ? dodatkowe flagi, uywane wraz z poczeniem.

Tabela 11.3. Tryby dostpu do sieci Internet Tryb INTERNET_OPEN_TYPE_DIRECT INTERNET_OPEN_TYPE_PRECONFIG Opis Poczenie lokalne Typ poczenia oraz ewentualny adres serwera proxy jest pobierany z rejestru Poczenie jest odczytywane z plikw systemowych Windows Poczenie poprzez serwer proxy

INTERNET_OPEN_TYPE_PRECONFIG_WITH_NO_AUTOPROXY INTERNET_OPEN_TYPE_PROXY

W naszym przypadku inicjowanie biblioteki moe wyglda w sposb nastpujcy: hSession := InternetOpen('Fast Download', INTERNET_OPEN_TYPE_PRECONFIG, nil, nil, 0);

Funkcja InternetOpen zwraca uchwyt sesji w postaci zmiennej hInternet ? w uchwyt bdzie nam potrzebny w kolejnych operacjach. W ostatnim parametrze moesz doda take flag INTERNET_FLAG_FROM_CACHE, dziki ktrej plik ju raz pobrany bdzie odczytywany z pamici podrcznej ? tzw. cache?u. Kada zmienna typu hInternet musi zosta zwolniona po zakoczeniu korzystania. Realizuje to funkcja InternetCloseHandle.

Otwieranie konkretnego adresu URL


Do odczytania konkretnego pliku bdziemy musieli skorzysta z funkcji InternetOpenURL: function InternetOpenUrl(hInet: HINTERNET; lpszUrl: PChar; lpszHeaders: PChar; dwHeadersLength: DWORD; dwFlags: DWORD; dwContext: DWORD): HINTERNET; stdcall;

426 | S t r o n a

W pierwszym parametrze bdziemy musieli poda uchwyt uzyskany dziki funkcji InternetOpen. Kolejny parametr okrela adres strony, ktr prbujemy otworzy. Kolejne dwa parametry s zwizane z nagwkami, ktre moemy wysa. W naszym przykadzie nie bdziemy wysya adnych nagwkw, wic pozostawimy te wartoci puste (nil i 0). Kolejny parametr moe okrela dodatkowe parametry poczenia ? np. poczenie za pomoc trybu bezpiecznego (HTTPS) czy np. autoryzacj. Przykad wywoania tej funkcji: hURL := InternetOpenURL(hSession, PChar(URL), nil, 0, 0, 0);

Funkcja InternetOpenURL take zwraca uchwyt w postaci zmiennej hInternet, ktra bdzie nam potrzebna w czasie pobierania pliku.

Odczyt pliku
Wreszcie nadchodzi kolej na odczytanie pliku, tj. pobranie jego czci i zapisanie na dysku. W tym celu skorzystamy z funkcji InternetReadFile: function InternetReadFile(hFile: HINTERNET; lpBuffer: Pointer; dwNumberOfBytesToRead: DWORD; var lpdwNumberOfBytesRead: DWORD): BOOL; stdcall;

Pobieranie pliku bdzie nastpowa partiami, po 1 kB kada. Pierwszy parametr musi zawiera wskazanie (uchwyt) zmiennej typu hInternet. Warto t uzyskalimy w czasie wywoywania funkcji InternetOpenURL. Kolejne dwa parametry s zwizane z buforem, ktry bdzie przechowywa pobrane fragmenty pliku. Parametr lpBuffer musi zawiera wskazanie bufora, a dwNumberOfBytesToRead rozmiar owego bufora (ilo danych, ktre maj zosta pobrane). Ostatni parametr przekazuje ilo rzeczywicie odczytanych bajtw. Pobieranie pliku z Internetu moe wyglda nastpujco: AssignFile(F, FileName); try Rewrite(F, 1); repeat if Broken then Break; { pobieranie kolejnych fragmentw pliku } InternetReadFile(hURL, @Buffer, SizeOf(Buffer), dwRead); BlockWrite(F, Buffer, dwRead); // zapisanie buforu w pliku TotalRead := TotalRead + dwRead; Application.ProcessMessages; 427 | S t r o n a

{ wywietlenie postpu } lblProgress.Caption := 'cigam ' + IntToStr(TotalRead div 1024) + ' kB z ' + IntToStr(dwSize div 1024) + ' kB'; ProgressBar.Position := TotalRead div 1024; until dwRead = 0; finally CloseFile(F); end;

Cz z tych instrukcji na pewno jest Ci znana, chociaby z poprzednich rozdziaw dotyczcych obsugi plikw. W skrcie mona powiedzie, e w powyszym kodzie w ptli nastpuje cykliczne odczytywanie fragmentw pliku i przypisywanie ich zmiennej Buffor. Nastpnie zawarto tej zmiennej zostaje wstawiona do pliku. Zakoczenie operacji sygnalizuje zmienna dwSize, ktra przybiera warto 0 w przypadku, gdy pobieranie zostao zakoczone.

Pobieranie rozmiaru pliku


W gruncie rzeczy z fragmentw kodw, ktre przedstawiem wyej, mona by byo skonstruowa program do cigania plikw. eby wszystko wygldao lepiej, a program by bardziej uyteczny, bdziemy musieli take dowiedzie si, jaki jest rozmiar pliku, ktry prbujemy pobra. Pozwoli to nam ukaza na komponencie TProgressBar postp w ciganiu pliku. Skorzystamy z funkcji HTTPQueryInfo, ktra umoliwia uzyskanie wielu informacji na temat pliku (nazwa serwera, rozmiar, rodzaj pliku itp.): function HttpQueryInfo(hRequest: HINTERNET; dwInfoLevel: DWORD; lpvBuffer: Pointer; var lpdwBufferLength: DWORD; var lpdwReserved: DWORD): BOOL; stdcall;

W pierwszym parametrze musi si znale uchwyt uzyskany w wyniku dziaania funkcji InternetOpenURL. Drugi parametr musi zawiera flag informujc o typie informacji, jak chcemy uzyska. My w naszym programie musimy wpisa tutaj warto: HTTP_QUERY_CONTENT_LENGTH. Uzyskana informacja jest zwracana w formie wskanika: dwBufLen := 1024; dwIndex := 0; GetMem(pBuf, dwBufLen); { pobranie informacji na temat wielkoci pliku } HttpQueryInfo(hURL, HTTP_QUERY_CONTENT_LENGTH, pBuf, dwBufLen, dwIndex); dwSize := StrToInt(StrPas(pBuf)); ProgressBar.Max := (dwSize div 1024);

428 | S t r o n a

FreeMem(pBuf, dwBufLen);

Wane jest to, e parametry podawane w funkcji HTTPQueryInfo musz by zmiennymi. Dlatego te zadeklarowaem zmienne dwIndex i dwBufLen. Na pocztku naleao przeznaczy 1 kB pamici na poczet wskanika, ktry bdzie zawiera informacj o rozmiarze pliku. Po wykonaniu funkcji zmienna pBuf zawieraa informacje, ktre s nam potrzebne ? nie pozostao nic innego, jak przedstawi je w formie liczby (warto Integer) i zwolni pami. W listingu 11.1 znajduje si cay kod rdowy programu. Listing 11.1. Pobieranie pliku za pomoc biblioteki Wininet.dll unit MainFrm; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls, ComCtrls; type TMainForm = class(TForm) ProgressBar: TProgressBar; btnDownload: TButton; GroupBox1: TGroupBox; Label1: TLabel; Label2: TLabel; edtURL: TEdit; edtFile: TEdit; lblProgress: TLabel; procedure btnDownloadClick(Sender: TObject); procedure FormClose(Sender: TObject; var Action: TCloseAction); private Broken : Boolean; procedure Download(const URL : String; FileName : String); public { Public declarations } end; var MainForm: TMainForm; implementation {$R *.dfm} 429 | S t r o n a

{ TMainForm } uses WinInet; procedure TMainForm.Download(const URL: String; FileName: String); var Buffer : array[1..1024] of Byte; // bufor zawierajcy pobrany fragment pliku hSession, hURL : HINTERNET; dwRead : DWORD; // ilo odczytanych danych dwSize : DWORD; // rozmiar pliku F : File; pBuf : Pointer; dwBufLen : DWORD; dwIndex : DWORD; TotalRead : Integer; begin { otwieranie poczenia } hSession := InternetOpen('Fast Download', INTERNET_OPEN_TYPE_PRECONFIG, nil, nil, 0); Application.ProcessMessages; lblProgress.Caption := 'czenie z serwerem...'; btnDownload.Enabled := False; try { otwarcie podanego adresu URL } hURL := InternetOpenURL(hSession, PChar(URL), nil, 0, 0, 0); Application.ProcessMessages; lblProgress.Caption := 'Czekanie na odpowied...'; dwBufLen := 1024; dwIndex := 0; GetMem(pBuf, dwBufLen); { pobranie informacji na temat wielkoci pliku } HttpQueryInfo(hURL, HTTP_QUERY_CONTENT_LENGTH, pBuf, dwBufLen, dwIndex); dwSize := StrToInt(StrPas(pBuf)); ProgressBar.Max := (dwSize div 1024); FreeMem(pBuf, dwBufLen); try AssignFile(F, FileName); try Rewrite(F, 1); 430 | S t r o n a

repeat if Broken then Break; { pobieranie kolejnych fragmentw pliku } InternetReadFile(hURL, @Buffer, SizeOf(Buffer), dwRead); BlockWrite(F, Buffer, dwRead); // zapisanie buforu w pliku TotalRead := TotalRead + dwRead; Application.ProcessMessages; { wywietlenie postpu } lblProgress.Caption := 'cigam ' + IntToStr(TotalRead div 1024) + ' kB z ' + IntToStr(dwSize div 1024) + ' kB'; ProgressBar.Position := TotalRead div 1024; until dwRead = 0; finally CloseFile(F); end; finally InternetCloseHandle(hSession); end; finally InternetCloseHandle(hURL); btnDownload.Enabled := False; end; lblProgress.Caption := 'Pobrano'; end; procedure TMainForm.btnDownloadClick(Sender: TObject); begin Download(edtURL.Text, edtFile.Text); end; procedure TMainForm.FormClose(Sender: TObject; var Action: TCloseAction); begin Broken := True; end; end.

By moe cay kod wyglda nieco odstraszajco, lecz po opanowaniu podstawowych funkcji moduu WinInet nie powiniene mie problemw z jego odczytaniem. Rwnie dobrze do realizacji tego zadania mgby skorzysta z gotowego komponentu, ktrych jest wiele w Internecie. Czy nie lepiej jednak samemu podj takie wyzwanie? Moim zdaniem jest to dobry sposb nauczenia si, jak przebiega cay ten proces zwizany z pobraniem pliku. 431 | S t r o n a

Wicej informacji na temat budowy funkcji przedstawionych w tym podpunkcie moesz znale na stronie http://msdn.microsoft.com.

Sprawdzanie poczenia
?Jak sprawdzi, czy komputer jest w danym momencie podczony do sieci??. To pytanie pojawia si bardzo czsto. Wiele jest rozwiza tego problemu. Mona np. sprawdza IP (patrz punkt ?Sprawdzanie IP?) i ? jeeli nie rwna si ono 127.0.0.1 lub 0.0.0.0 ? mona stwierdzi, e istnieje poczenie z Internetem. Mona take sprbowa poczenia z jakim serwerem ? jeeli ta prba si powiedzie, oznacza to, e komputer jest podczony do sieci. Takie rozwizanie jest jednak zawodne, gdy serwer moe w danym momencie nie dziaa (awarie si zdarzaj), a to wcale nie musi oznacza, e komputer uytkownika nie jest podczony do Internetu. Mwic o prbie poczenia z danym serwerem miaem na myli wysyanie do niego pakietw ICMP za pomoc programu ping ? moesz o tym przeczyta w dalszej czci tego rozdziau. Ja natomiast chciaem Ci przedstawi rozwizanie z uyciem biblioteki WinInet.dll i funkcji InternetGetConnectedState. Sprawdzanie, czy komputer jest poczony z Internetem, moe wyglda w ten sposb: uses WinInet; procedure TMainForm.btnCheckClick(Sender: TObject); var dwConnection : DWORD; begin { flagi } dwConnection := INTERNET_CONNECTION_MODEM + INTERNET_CONNECTION_LAN + INTERNET_CONNECTION_PROXY; { sprawd, czy jest poczenie } if not InternetGetConnectedState(@dwConnection, 0) then lblResult.Caption := 'Brak poczenia' else lblResult.Caption := 'Jest poczenie'; end;

Zmienna dwConnection zawiera flagi informujce o typie poczenia, jakie ma by uwzgldniane przy wywoaniu funkcji InternetGetConnectedState. Kompletny kod rdowy tego programu moesz znale na doczonej do ksiki pycie CD-ROM w katalogu ../listingi/11/GetINetState/GetINet.dpr. 432 | S t r o n a

Sprawdzanie IP
Chcc dowiedzie si o obecnym adresie IP komputera, na ktrym jest uruchomiony program, moesz oczywicie skorzysta z gotowych komponentw. Ja poka, jak zrobi to w sposb ?programowy?, korzystajc z biblioteki Windows: WinSock.dll. Na samym pocztku dodaj wic do listy uses modu WinSock. Oglnie rzecz biorc, do pobierania adresu IP suy funkcja iNet_ntoa, w ktrej jako parametr naley poda wskanik struktury InAddr.

Zainicjowanie biblioteki
Zanim skorzystamy z biblioteki WinSock.dll, naley j zainicjowa. Suy do tego polecenie WSAStartup. Po zakoczeniu korzystania z biblioteki naley j zwolni ? poleceniem WSACleanup. Inicjowanie i zakoczenie moe wyglda tak: procedure TMainForm.GetIPAndName(var IPAddress, ACompName: PCHar); var VER : WORD; Data : TWSAData; begin // adujemy bibliotek Winsock VER := MAKEWORD(1, 0); WSAStartup(VER, Data); try finally WSACleanup; // zwolnij bibliotek Winsock end; end;

Parametry podawane funkcji WSAStartup musz by przekazywane poprzez zmienne, czyli uprzednio naley zadeklarowa zmienn typu WORD oraz TWSAData. Pierwszy parametr funkcji WSAStartup musi zawiera numer wersji biblioteki WinSock.dll ? utworzony przy uyciu MAKEWORD (funkcja na podstawie dwch liczb tworzy liczb typu Word ? dziaa odwrotnie ni funkcje HiWord oraz LoWord, przedstawione w rozdziale 5.).

433 | S t r o n a

Pobieranie adresu IP
Caa procedura pobrania nazwy zalogowanego uytkownika oraz adresu IP przedstawiona jest poniej: procedure TMainForm.GetIPAndName(var IPAddress, ACompName: PCHar); var Host : PHostEnt; CompName : array[0..MAX_PATH] of char; IP : PChar; // adres IP komputera VER : WORD; Data : TWSAData; begin // adujemy bibliotek Winsock VER := MAKEWORD(1, 0); WSAStartup(VER, Data); try // Pobieramy nazw komputera i przypisujemy j zmiennej "CompName" GetHostName(@CompName, MAX_PATH); Host := GetHostByName(@CompName); // uzyskanie nazwy uytkownika ACompName := Host^.h_name;// przypisanie zmiennej "ACompName" nazwy uytkownika // Pobieramy jego adres IP ( uyte tu zostao rzutowanie ) IP := iNet_ntoa(PInAddr(Host^.h_addr_list^)^); IPAddress := IP; // przypisanie zmiennej "IPAddress" nazwy IP finally WSACleanup; // zwolnij bibliotek Winsock end; end;

Funkcja eksportuje dwie zmienne: IPAddress oraz ACompName. Pierwsza z nich zawiera bdzie adres IP aktualnego poczenia, a druga ? w przypadku poczenia modemowego np. z sieci TPSA ? nazw uytkownika, czyli PPP. Pierwsze funkcje ? GetHostName i GetHostByName ? maj na celu pobranie nazwy sieciowej lokalnego komputera, ktra od tej pory jest zawarta w strukturze PHostEnt. Samo pobranie adresu IP nastpuje za spraw iNet_ntoa. W kodzie umieciem tak warto: MAX_PATH. W rzeczywistoci jest to staa, oznaczajca maksymaln warto (dugo) tablicy. Czsto programici zamiast rozmiaru tablicy podaj wanie sta MAX_PATH, gdy nie s w stanie okreli, jaka bdzie wielko tablicy w trakcie dziaania programu. 434 | S t r o n a

Peen kod rdowy programu moesz znale na doczonej do ksiki pycie CD-ROM w katalogu ../listingi/11/GetIp/GetIp.dpr.

czenie przy uyciu gniazd


Poczenie pomidzy dwoma komputerami w sieci jest moliwe dziki zastosowaniu tzw. gniazd (ang. sockets). Z punktu widzenia systemu gniazdo jest zwykym uchwytem. Poczenie moe nastpi wwczas, gdy obydwa programy s uruchomione na rnych maszynach i dziaaj na tym samym porcie. Jak napisaem na pocztku tego rozdziau, port jest ?furtk?, dziki ktrej oba komputery mog przesya informacje. Poczenie moe by inicjowane na dwa sposoby. Pierwszy sposb to oczekiwanie na poczenie. Program jest uruchamiany, port jest otwarty i nastpuje oczekiwanie na poczenie z zewntrz ? program peni w ten sposb funkcj serwera. Druga metoda to podanie adresu IP (wraz z numerem portu), gdzie oczekuje aplikacja-serwer. Tak wic jeeli mamy te same programy znajdujce si na dwch rnych maszynach, to nasza aplikacja moe by albo klientem, albo serwerem.

Czego uy?
Do zrealizowania prostego zadania, jakim jest poczenie i wymiana informacji tekstowych, mona uy komponentw Indy: TidTCPClient oraz TidTCPServer (zakadki Indy Clients i Indy Servers). Dziki tym dwm kontrolkom bdziemy mogli zrealizowa poczenie TCP/IP midzy dwoma komputerami. Za pomoc tych komponentw napiszemy prost aplikacj suc do komunikacji (rysunek 11.3).

Rysunek 11.3. Interfejs programu

435 | S t r o n a

czenie pomidzy komputerami


Po uruchomieniu programu bdzie on dziaa jako klient. Dopiero po zaznaczeniu opcji Oczekuj na poczenie uaktywni si jako serwer i bdzie mg przyczy klienta. Natomiast klient po wpisaniu adresu IP i naciniciu przycisku wywoa zdarzenie OnClick: procedure TMainForm.btnConnectClick(Sender: TObject); begin if Client.Connected then // w przypadku, gdy zestawione jest poczenie... begin Client.Disconnect; //...rozcz z serwerem btnConnect.Caption := 'Pocz z komputerem'; end else begin //...w przeciwnym wypadku... Server.Active := False; //...dezaktywuj serwer... Client.Host := edtIP.Text; //...pobierz IP z kontrolki Client.Connect; //...pocz btnConnect.Caption := 'Rozcz'; end; end;

Przycisk btnConnect suy zarwno do czenia, jak i rozczania z serwerem. Pierwsza instrukcja if sprawdza, czy program jest poczony (a cilej mwic, czy poczony jest komponent Client) ? jeeli tak, rozcza go.

Wymiana danych
Oczywicie pomidzy komponentami TidTCPClient oraz TidTCPServer istnieje moliwo wymiany danych tekstowych ? jest to proces podobny do dziaania komunikatora. My jednak zajmiemy si tworzeniem polece. Komponenty Indy umoliwiaj tworzenie polece, ktre su wymianie informacji. Przykadowo klient moe wysa do aplikacji-serwera polecenie HELLO, a serwer moe odpowiednio zareagowa i ewentualnie wysa odpowied.

Tworzenie komend 1.Zaznacz komponent TidTCPServer i odszukaj waciwo CommandHandlers. Zostanie wywietlone okno Editiing Server.CommandHandlers. (rysunek 11.4). 436 | S t r o n a

Rysunek 11.4. Tworzenie nowego polecenia serwera 2.Nacinij przycisk Insert ? zostanie utworzony nowy rekord. 3.We waciwoci Command w Inspektorze Obiektw wpisz sowo HELLO. 4.We waciwoci Name wpisz HelloCmd. 5.Rozwi waciwo ReplyNormal. 6.Waciwociom NumericCode oraz TextCode nadaj warto 200. 7.Waciwoci Text nadaj warto Witam! Polecenie przesane prawidowo! Od tej pory nowym poleceniem bdzie HELLO. W wyniku odebrania owego polecenia serwer wyle klientowi odpowied Witam! Polecenie przesane prawidowo! Waciwoci NumericCode oraz TextCode mog zawiera numer odpowiedzi zwracanej przez serwer. Teraz kliknij zakadk Events w Inspektorze Obiektw i wygeneruj zdarzenie OnCommand. Zdarzenie to bdzie wystpowa w przypadku, gdy polecenie zostanie przez serwer odebrane. Procedura zdarzeniowa moe wyglda w sposb nastpujcy: procedure TMainForm.ServerHelloCmdCommand(ASender: TIdCommand); begin ASender.SendReply; // wylij odpowied Memo.Lines.Add('Otrzymaem polecenie "HELLO"'); // w Memo wywietl informacje o otrzymaniu komendy end;

W pierwszym wierszu procedury nastpuje wywoanie metody SendReply ? w tym momencie klientowi zostaje wysyana odpowied z komunikatem oraz numerem odpowiedzi. Wysyanie polecenia HELLO przez aplikacj-klienta moe wyglda w ten sposb: procedure TMainForm.CmdBoxClick(Sender: TObject); begin Client.SendCmd(CmdBox.Items[CmdBox.ItemIndex]); // wysanie 437 | S t r o n a

polecenia { wywietlenie rezultatu w komponencie Memo } Memo.Lines.Add(Client.LastCmdResult.TextCode + '?' + Client.LastCmdResult.Text.Text); end;

Wysyanie komendy jest realizowane przez SendCmd. W procedurze tej naley poda nazw komendy, ktra ma zosta przesana. Kolejny wiersz to wywietlenie w komponencie Memo odpowiedzi, jak otrzymalimy z serwera. Wszystko to dziki waciwoci LastCmdResult, ktra zwraca informacje otrzymane od serwera. Na rysunku 11.5 przedstawiony jest program w trakcie dziaania. Cay kod programu znajduje si w listingu 11.2.

Rysunek 11.5. Dziaanie programu Listing 11.2. Kod rdowy programu { Copyright (c) 2002 by Adam Boduch <adam@4programmers.net> } unit MainFrm; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, Sockets, StdCtrls, IdTCPConnection, IdTCPClient, IdBaseComponent, IdComponent, IdTCPServer, IdIOHandler, IdIOHandlerSocket; type TMainForm = class(TForm) 438 | S t r o n a

btnConnect: TButton; edtIP: TEdit; lblIP: TLabel; GroupBox1: TGroupBox; Memo: TMemo; ServerChecked: TCheckBox; Client: TIdTCPClient; Server: TIdTCPServer; CmdBox: TListBox; procedure ServerConnect(AThread: TIdPeerThread); procedure ServerDisconnect(AThread: TIdPeerThread); procedure btnConnectClick(Sender: TObject); procedure ServerCheckedClick(Sender: TObject); procedure ClientConnect(Sender: TObject); procedure ClientDisconnect(Sender: TObject); procedure ServerHelloCmdCommand(ASender: TIdCommand); procedure CmdBoxClick(Sender: TObject); private public { Public declarations } end; var MainForm: TMainForm;

implementation {$R *.dfm} procedure TMainForm.ServerConnect(AThread: TIdPeerThread); begin Memo.Lines.Add('Klient poczony...'); end; procedure TMainForm.ServerDisconnect(AThread: TIdPeerThread); begin Memo.Lines.Add('Klient rozczony...'); end; procedure TMainForm.btnConnectClick(Sender: TObject); begin if Client.Connected then // w przypadku, gdy zawarte jest poczenie... begin Client.Disconnect; //...rozcz z serwerem btnConnect.Caption := 'Pocz z komputerem'; 439 | S t r o n a

end else begin //...w przeciwnym wypadku... Server.Active := False; //...dezaktywuj serwer... Client.Host := edtIP.Text; //...pobierz IP z kontrolki Client.Connect; //...pocz btnConnect.Caption := 'Rozcz'; end; end; procedure TMainForm.ServerCheckedClick(Sender: TObject); begin // aktywuj serwer w razie, gdy pozycja jest zaznaczona Server.Active := ServerChecked.Checked; end; procedure TMainForm.ClientConnect(Sender: TObject); begin Memo.Lines.Add('Poczony...'); // informacja w komponencie Memo end; procedure TMainForm.ClientDisconnect(Sender: TObject); begin Memo.Lines.Add('Rozczony...'); // informacja w komponencie Memo end; procedure TMainForm.ServerHelloCmdCommand(ASender: TIdCommand); begin ASender.SendReply; // wylij odpowied Memo.Lines.Add('Otrzymaem polecenie "HELLO"'); // w Memo wywietl informacje o otrzymaniu polecenia end; procedure TMainForm.CmdBoxClick(Sender: TObject); begin Client.SendCmd(CmdBox.Items[CmdBox.ItemIndex]); // wysanie polecenia { wywietlenie rezultatu w komponencie Memo } Memo.Lines.Add(Client.LastCmdResult.TextCode + '?' + Client.LastCmdResult.Text.Text); end; end.

440 | S t r o n a

Jak dziaaj ?konie trojaskie??


?Konie trojaskie?, inaczej zwane backdoors, dziaaj na tej samej zasadzie. Nie zamierzam tutaj podawa przepisu na stworzenie ?konia trojaskiego?, ale prawd jest, e programy te komunikuj si za pomoc protokou TCP/IP. Aplikacja-serwer jest zazwyczaj doczana do innego programu, po czym zagnieda si gdzie w systemie i jest uruchomiona przez cay czas. ?Konie trojaskie? bardzo czsto maj take w sobie funkcje zwan KeySpy. KeySpy dosownie mona przetumaczy na polski jako szpieg klawiszy. Jest to aplikacja, ktra monitoruje naciskane przez Ciebie klawisze, a nastpnie cay pisany za pomoc klawiatury tekst zapisuje gdzie w pliku tekstowym. Podczas poczenia z Internetem moe np. wysa autorowi takiego programu na jego adres e-mail wszystko to, co pisae na swoim komputerze. Po poczeniu z Internetem aplikacja-klient wydaje polecenia serwerowi, ktry je interpretuje i zgodnie z nimi dokonuje rnych ?modyfikacji? w naszym komputerze. W powyszym przykadzie zaprezentowaem przykad wymiany danych pomidzy aplikacjami. Taka wymiana bya raczej niegrona, lecz ?konie trojaskie? przesyaj midzy sob inne komunikaty, w wyniku ktrych projektant takiego ?konia? moe otrzyma inne informacje na temat Twojego komputera ? jak np. cieka do katalogu Windows (patrz rysunek 11.6).

Rysunek 11.6. Program uzyskuje ciek do katalogu Windows W ten sposb autor takiego ?konia trojaskiego? uzyskuje informacj o katalogu, w ktrym znajduje si system operacyjny. Oczywicie to by tylko przykad, gdy inne polecenia mog powodowa inne zachowania aplikacji-serwera. Wspczesne ?konie trojaskie? potrafi przesya pomidzy sob pliki (oczywicie bez wiedzy uytkownika), sterowa kursorem myszki, wpisywa na ekranie rne teksty, wysuwa szuflad napdu CD-ROM lub nawet wycza komputer. Niestety wci wiele jest uytkownikw Internetu, ktrych pasjonuje ?wamywanie? si do czyjego komputera. Jak si przed tym zabezpieczy? Najprostsz metod jest zastosowanie tzw. firewalla (ang. ciana ogniowa), czyli programu, ktry nie dopuci do poczenia bezporednio z jakim adresem IP bez Twojej wiedzy. 441 | S t r o n a

Kod programu wysyajcego ciek do katalogu Windows znajduje si na pycie CD-ROM w katalogu ..listingi/11/WndCommand/25.dpr.

Pingi
Sowo ping jest zwizane z wysyaniem zapytania do konkretnego komputera w sieci. Jest to sposb sprawdzenia, czy dany komputer o danym adresie IP istnieje w sieci. Wysyanie sygnaw ping jest take sposobem na okrelenie czasu, jaki jest potrzebny na uzyskanie odpowiedzi. Gdy serwer otrzyma sygna ping, natychmiast wysya do nas odpowied. Komponentem sucym do wysyania sygnaw ping jest TIdICMPClient z palety Indy Clients. Za jego pomoc skonstruujemy program wygldajcy tak, jak na rysunku 11.7.

Rysunek 11.7. Odebrany ping

Wysyanie sygnau ping


Wysyanie sygnau ping z uyciem komponentu TidICMPClient jest bardzo proste. Jedyna rzecz, jak musimy zrobi, to przypisa adres hosta do zmiennej Host, a nastpnie wywoa metod Ping: Ping.Host := edtIP.Text; // przypisujemy hosta Ping.Ping; // wysyamy sygna ping

Wczeniej w Inspektorze Obiektw we waciwoci Port wpisaem numer 80.

442 | S t r o n a

Odpowiedzi z serwera
Uzyskiwanie odpowiedzi jest realizowane przez zdarzenie OnReply komponentu TidICMPClient. procedure TMainForm.PingReply(ASender: TComponent; const AReplyStatus: TReplyStatus); begin // odpowied btnPing.Enabled := True; { sprawdzamy, czy wysanie sygnau ping nie zakoczyo si niepowodzeniem } if AReplyStatus.BytesReceived = 0 then memReply.Lines.Add('Time Out') else memReply.Lines.Add(Format('%d bajtw odebranych z %s w %d ms', [AReplyStatus.BytesReceived, AReplyStatus.FromIpAddress, AReplyStatus.MsRoundTripTime])); end;

Odpowied z serwera jest dostarczana z parametrem AReplyStatus. Wraz z tym rekordem s dostarczane rne informacje, m.in. czas, w jakim uzyskalimy odpowied, ilo odebranych bajtw czy np. adres IP. Jeeli odebrany rekord jest wielkoci 0 kB, oznacza to, i przekroczony zosta czas odpowiedzi z serwera. Takie zdarzenie moe wystpi w momencie, gdy wysyamy sygna ping do wolnego serwera lub korzystamy z wolnego cza. Kod rdowy programu znajduje si w listingu 11.3. Listing 11.3. Wysyanie sygnau ping do serwera unit MainFrm; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, IdBaseComponent, IdComponent, IdRawBase, IdRawClient, IdIcmpClient, StdCtrls; type TMainForm = class(TForm) Ping: TIdIcmpClient; GroupBox1: TGroupBox; Label1: TLabel; edtIP: TEdit; btnPing: TButton; memReply: TMemo; procedure btnPingClick(Sender: TObject); 443 | S t r o n a

procedure PingReply(ASender: TComponent; const AReplyStatus: TReplyStatus); private { Private declarations } public { Public declarations } end; var MainForm: TMainForm; implementation {$R *.dfm} procedure TMainForm.btnPingClick(Sender: TObject); begin btnPing.Enabled := False; // dezaktywujemy przycisk Ping.Host := edtIP.Text; // przypisujemy hosta Ping.Ping; // wysyamy sygna ping end; procedure TMainForm.PingReply(ASender: TComponent; const AReplyStatus: TReplyStatus); begin // odpowied btnPing.Enabled := True; { sprawdzamy, czy wysanie sygnau ping nie zakoczyo si niepowodzeniem } if AReplyStatus.BytesReceived = 0 then memReply.Lines.Add('Time Out') else memReply.Lines.Add(Format('%d bajtw odebranych z %s w %d ms', [AReplyStatus.BytesReceived, AReplyStatus.FromIpAddress, AReplyStatus.MsRoundTripTime])); end; end.

Kontrolka TWebBrowser
Niekiedy zachodzi potrzeba przedstawienia w naszym programie jakiego tekstu w formie strony WWW. Wwczas naley skorzysta z jakiego komponentu, ktry interpretuje kod HTML. 444 | S t r o n a

Rozwizanie jest blisko Ciebie ? to komponent TWebBrowser. W rzeczywistoci jest to kontrolka ActiveX przegldarki Internet Explorer, tak wic korzystajc z niej, korzystasz w pewnym sensie z ?silnika? firmy Microsoft. Jeeli uytkownik naszego programu nie bdzie mia zainstalowanego programu Internet Explorer, to niestety nie bdziemy mogli uy take komponentu TWebBrowser. Nie ma jednak co narzeka ? dugo szukajc w Internecie, nie napotkasz drugiego tak dobrego komponentu sucego do wywietlania stron WWW. Kontrolki ActiveX maj zazwyczaj rozszerzenie *.ocx i s skompilowanym kodem, jakby dodatkowym komponentem. O kontrolkach ActiveX bdziemy mwi w rozdziale 13. tej ksiki. Na rysunku 11.8 znajduje si przykad programu wykorzystujcego komponent TwebBrowser, a jego kod rdowy moesz znale w listingu 11.4

Rysunek 11.8. Program wykorzystujcy komponent TWebBrowser

adowanie strony
Wywietlenie strony w komponencie TWebBrowser jest rzecz dziecinnie prost. Umoliwia to metoda Navigate, w ktrej podaje si jedynie jeden parametr, jakim jest adres strony do zaadowania. procedure TMainForm.btnGoClick(Sender: TObject); begin { zaaduj podany URL } 445 | S t r o n a

WebBrowser.Navigate(edtURL.Text); end;

Odwieanie
Nacinicie przycisku Odwie spowoduje ponowne zaadowanie strony w naszym programie. Metoda realizujca to zadanie nazywa si Refresh: procedure TMainForm.btnRefreshClick(Sender: TObject); begin { odwie } WebBrowser.Refresh; end;

Nastpna i poprzednia strona


Przechodzc po kolei po rnych stronach WWW, nasza kontrolka rejestruje ich przebieg i zapamituje adresy. Nasz program jest w stanie cofn si do poprzedniej strony albo przej do kolejnej. Dane w takim wypadku zostan odczytane z dysku (strona nie zostanie przeadowana): procedure TMainForm.btnBackClick(Sender: TObject); begin { cofnij } WebBrowser.GoBack; end; procedure TMainForm.btnFowardClick(Sender: TObject); begin { nastpna strona } WebBrowser.GoForward; end;

Pozostae kody
Peen kod rdowy programu jest przedstawiony w listingu 11.4, lecz nie wykorzystalimy w nim peni moliwoci komponentu TWebBrowser. Poniej przedstawiam dodatkowe instrukcje, z jakich moe w przyszoci skorzystasz.

446 | S t r o n a

Wywietlanie adresu zaadowanej strony

ShowMessage(WebBrowser.OleObject.Document.URL);

Wywietlanie tytuu strony

ShowMessage(WebBrowser.OleObject.Document.Title);

Sprawdzenie, czy strona jest chroniona (SSL)

if WebBrowser.OleObject.Document.Location.Protocol = 'https:' then ShowMessage('Strona chroniona');

Ustawienie koloru ta strony

WebBrowser.OleObject.Document.bgColor := '#000000';

Listing 11.4. Kod rdowy przegldarki unit MainFrm; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, ToolWin, ComCtrls, OleCtrls, SHDocVw, StdCtrls, Buttons, ImgList; type 447 | S t r o n a

TMainForm = class(TForm) WebBrowser: TWebBrowser; StatusBar: TStatusBar; CoolBar: TCoolBar; ToolBar1: TToolBar; edtURL: TEdit; btnGo: TButton; ToolBar2: TToolBar; SpeedButton1: TSpeedButton; ImageList1: TImageList; btnBack: TToolButton; btnFoward: TToolButton; btnStop: TToolButton; btnRefersh: TToolButton; procedure btnGoClick(Sender: TObject); procedure btnStopClick(Sender: TObject); procedure btnBackClick(Sender: TObject); procedure btnFowardClick(Sender: TObject); procedure btnRefershClick(Sender: TObject); procedure WebBrowserStatusTextChange(Sender: TObject; const Text: WideString); private { Private declarations } public { Public declarations } end; var MainForm: TMainForm; implementation {$R *.dfm} procedure TMainForm.btnGoClick(Sender: TObject); begin { zaaduj podany URL } WebBrowser.Navigate(edtURL.Text); end; procedure TMainForm.btnStopClick(Sender: TObject); begin { wstrzymaj adowanie strony } WebBrowser.Stop; end; procedure TMainForm.btnBackClick(Sender: TObject); begin 448 | S t r o n a

{ cofnij } WebBrowser.GoBack; end; procedure TMainForm.btnFowardClick(Sender: TObject); begin { nastpna strona } WebBrowser.GoForward; end; procedure TMainForm.btnRefershClick(Sender: TObject); begin { odwie } WebBrowser.Refresh; end; procedure TMainForm.WebBrowserStatusTextChange(Sender: TObject; const Text: WideString); begin { wywietl postp w adowaniu strony } StatusBar.SimpleText := Text; end; end.

Protok SMTP
Chcc obsuy protok SMTP, naley skorzysta z komponentu TIdSMTP. Jeeli chodzi o samo wysanie listu e-mail, mona to zrobi nawet przy uyciu komponentu TTcpSocket. W takim przypadku wysyamy do serwera odpowiednie polecenia ? jeeli uzyskamy odpowied, wysyany jest nagwek wiadomoci, a nastpnie tre. Jednak nie ma co wywaa otwartych drzwi ? pakiet Indy oferuje nam komponent o nazwie IdMessage, dziki ktremu w prosty sposb bdziemy mogli wysa list elektroniczny.

Interfejs programu
Program nie bdzie skomplikowany. Gwny czon aplikacji stanowi dwa komponenty TGroupBox, na ktrych znajduj si kontrolki TEdit, suce do wpisania nazwy serwera, portu oraz np. adresu nadawcy czy treci wiadomoci (rysunek 11.9).

449 | S t r o n a

Rysunek 11.9. Interfejs programu do wysyania e-maili

Dziaanie programu
Na samym pocztku program odczytuje informacj na temat adresu serwera, portu i nazwy uytkownika skrzynki e-mail. Niektre serwery wymagaj tzw. autoryzacji, zapobiegajc w ten sposb wysyaniu niechcianych e-mali za ich porednictwem. Zazwyczaj istnieje moliwo wysania wiadomoci przy wykorzystaniu dowolnego serwera obsugujcego SMTP. Jednake w niektrych przypadkach konieczne jest podanie loginu i hasa, jakie wpisalimy podczas zakadania skrzynki email. Kolejnym krokiem jest odczytanie informacji z drugiego komponentu TGroupBox i przypisanie ich do TidMessage. Kontrolki TidMessage oraz TidSMTP musz dziaa razem ? wysyanie e-maila wyglda w ten sposb: SMTP.Send(Message); // wysyanie e-maila

W parametrze metody Send naley poda nazw komponentu TidMessage.

450 | S t r o n a

Zdarzenia komponentu
Komponent TidSMTP (o nazwie SMTP) korzysta z trzech zdarze: OnConnected, OnDisconnected, OnStatus. Pierwsze i drugie wystpuje w momencie poczenia i rozczenia si z serwerem. Ostatnie natomiast suy do informowania uytkownika o postpie dziaania oraz ewentualnej reakcji. procedure TMainForm.SMTPConnected(Sender: TObject); begin GroupBox1.Enabled := False; GroupBox2.Enabled := False; end; procedure TMainForm.SMTPDisconnected(Sender: TObject); begin GroupBox1.Enabled := True; GroupBox2.Enabled := True; end; procedure TMainForm.SMTPStatus(ASender: TObject; const AStatus: TIdStatus; const AStatusText: String); begin case AStatus of hsResolving: StatusBar.SimpleText := 'Wyszukiwanie hosta...'; hsConnecting: StatusBar.SimpleText := 'czenie z serwerem ' + SMTP.Host; hsConnected: StatusBar.SimpleText := 'Poczony z serwerem'; hsDisconnecting: StatusBar.SimpleText := 'Trwa rozczanie...'; hsDisconnected: StatusBar.SimpleText := 'Rozczono'; end; end;

Pierwsza dwie procedury maja na celu jedynie dezaktywacj lub aktywacj komponentw typu TGroupBox, co blokuje take dostp do pozostaych komponentw. Natomiast ostatnie zdarzenie, OnStatus, ma na celu poinformowanie uytkownika o obecnym stanie obiektu. Informacja jest dostarczana wraz z parametrem AStatus, ktry moe przybiera wartoci takie, jak w tabeli 11.4. Status hsResolving Opis Wyszukiwanie adresu IP komputera poprzez podany adres 451 | S t r o n a

hsConnecting hsConnected

Trwa czenie z uzyskanym adresem IP Trwa prba rozczenia si z serwerem

hsDisconnecting Trwa prba rozczenia si z serwerem hsDisconnected Poczenie zostao zakoczone

Kod rdowy Cay kod rdowy programu znajduje si w listingu 11.5. Listing 11.5. Kod rdowy programu do wysyania e-maili { Copyright (c) 2002 by Adam Boduch <adam@4programmers.net> } unit MainFrm; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, ComCtrls, IdMessage, IdBaseComponent, IdComponent, IdTCPConnection, IdTCPClient, IdMessageClient, IdSMTP, StdCtrls, Buttons; type TMainForm = class(TForm) SMTP: TIdSMTP; Message: TIdMessage; StatusBar: TStatusBar; GroupBox1: TGroupBox; lblFrom: TLabel; edtFrom: TEdit; lblTo: TLabel; edtTo: TEdit; lblSubject: TLabel; edtSubject: TEdit; lblBody: TLabel; memBody: TMemo; btnSend: TSpeedButton; GroupBox2: TGroupBox; 452 | S t r o n a

lblHost: TLabel; edtHost: TEdit; lblPort: TLabel; edtPort: TEdit; lblLogin: TLabel; lblPassword: TLabel; edtLogin: TEdit; edtPassword: TEdit; procedure SMTPConnected(Sender: TObject); procedure SMTPDisconnected(Sender: TObject); procedure SMTPStatus(ASender: TObject; const AStatus: TIdStatus; const AStatusText: String); procedure btnSendClick(Sender: TObject); private { Private declarations } public { Public declarations } end; var MainForm: TMainForm; implementation {$R *.dfm} procedure TMainForm.SMTPConnected(Sender: TObject); begin GroupBox1.Enabled := False; GroupBox2.Enabled := False; end; procedure TMainForm.SMTPDisconnected(Sender: TObject); begin GroupBox1.Enabled := True; GroupBox2.Enabled := True; end; procedure TMainForm.SMTPStatus(ASender: TObject; const AStatus: TIdStatus; const AStatusText: String); begin case AStatus of hsResolving: StatusBar.SimpleText := 'Wyszukiwanie hosta...'; hsConnecting: StatusBar.SimpleText := 'czenie z serwerem ' + SMTP.Host; hsConnected: StatusBar.SimpleText := 'Poczony z serwerem'; hsDisconnecting: StatusBar.SimpleText := 'Trwa rozczanie...'; 453 | S t r o n a

hsDisconnected: StatusBar.SimpleText := 'Rozczono'; end; end; procedure TMainForm.btnSendClick(Sender: TObject); begin if Length(edtLogin.Text) > 0 then // Jeeli uytkownik wpisa login... begin SMTP.AuthenticationType := atLogin; //...znaczy to, e serwer wymaga autoryzacji { przypisanie waciwoci Username (uytkownik) oraz Password (haso) } SMTP.Username := edtLogin.Text; SMTP.Password := edtPassword.Text; end; SMTP.Host := edtHost.Text; // przypisanie adresu SMTP.Port := StrToIntDef(edtPort.Text, 25); // przypisanie IP try try SMTP.Connect; // prba poczenia si z serwerem btnSend.Enabled := False; // dezaktywacja przycisku Message.Subject := edtSubject.Text; // temat wiadomoci Message.From.Address := edtFrom.Text; // adres nadawcy Message.From.Text := edtFrom.Text; Message.From.Name := edtFrom.Text;

Message.Recipients.Add; Message.Recipients.Items[0].Address := edtTo.Text; // adres odbiorcy Message.Recipients.Items[0].Text := edtTo.Text; Message.Recipients.Items[0].Name := edtTo.Text; Message.Body.Assign(memBody.Lines); // pobieranie treci wiadomoci SMTP.Send(Message); // wysyanie e-maila except { w razie wystpienia bdu ? wywietl komunikat } raise Exception.Create('Bd! Nie mona poczy si z serwerem!'); end; finally 454 | S t r o n a

{ te instrukcje bd wykonywane ZAWSZE bez wzgldu na ew. wystpienie bdu } btnSend.Enabled := True; SMTP.Disconnect; end; end; end.

Na samym pocztku procedury wysyania wiadomoci nastpuje sprawdzenie, czy uytkownik wpisa rwnie login i haso do swojego konta. Jeeli tak, oznacza to, e serwer wymaga autoryzacji ? waciwo AuthenticationType jest ustawiana na atLogin. W programie skorzystaem z funkcji konwersji IntToStrDef, ktra konwertuje liczb do tekstu, ale oprcz tego posiada drugi, opcjonalny parametr ? warto domyln. Jeeli konwersja nie powiedzie si, do zmiennej zostanie przypisany parametr z parametru drugiego.

Protok HTTP
Chcc obsuy protok HTTP, moemy skorzysta z komponentu TIdHTTP. Komponent ten znajduje si na zakadce Indy Clients. Niektre waciwoci s identyczne, jak w przypadku komponentu TIdSMTP, ale w porwnaniu z tym komponentem jest ich wicej. Dotyczy to take zdarze ? w komponencie TIdHTTP jest ich wicej, a to ze wzgldu na wiksz funkcjonalno protokou HTTP.

czenie z serwerem
Oglnie rzecz biorc, poczenie si z danym serwerem HTTP polega na wpisaniu we waciwoci Host komponentu TidHTTP odpowiedniego adresu, a nastpnie wywoaniu metody Connect. Ot, caa filozofia! Portem odpowiadajcym usudze HTTP jest port 80. procedure TMainForm.btnConnectClick(Sender: TObject); begin HTTP.Host := edtIP.Text; // przypisanie nazwy hosta HTTP.Connect; // poczenie HTTP.Disconnect; // rozczenie end;

W pierwszym wierszu program przypisuje do waciwoci Host zawarto komponentu edtIP. 455 | S t r o n a

Nastpnie czy si z serwerem i natychmiast rozcza. W przypadku udanego poczenia program wywouje zdarzenie OnConnected komponentu TidHTTP, a w przypadku rozczenia ? OnDisconnected.

Wymiana danych
Przegldajc strony WWW, klikasz odnoniki, ktre prowadz do plikw umieszczonych na serwerze. Niekiedy jednak korzystanie z serwisu wymaga wypenienia formularza, ktrego zawarto wdruje najczciej do odpowiedniego skryptu PHP, ASP lub CGI. Odwiedzasz take strony tworzone dynamicznie (tzn. rwnie przy uyciu jzykw skryptowych), a do adresu doczane s jakie parametry. Te dwa przykady wysyania informacji nazywane s metodami GET i POST.

Metoda POST Zamy, ze przegldasz stron internetow. Na tej stronie znajduje si formularz, po wypenieniu ktrego i naciniciu przycisku Wylij informacje wdruj do autora strony. Najczciej za taki proces odpowiada skrypt wykonywany po stronie serwera. Skrypt to plik skadajcy si z okrelonych polece, zapisanych w danym jzyku (np. PHP). Polecenia te s interpretowane przez odpowiednie oprogramowanie znajdujce si na serwerze, a rezultat jest zwracany jest postaci dokumentu HTML. Przegldarka przesya dane z formularza jako nagwek HTTP do owego skryptu. Dane te s przedstawione w takiej postaci:
nazwa_pola=wartosc_pola&nazwa_pola2=wartosc_pola2

Poszczeglne elementy formularza s oddzielone od siebie znakiem &. Natomiast nazwa danego pola jest oddzielona od wartoci znakiem =. W ten sposb poczone dane wdruj wraz z nagwkiem do skryptu, ktry ju je odpowiednio interpretuje. Istot metody POST jest wanie to, e dane s przekazywane w nagwku HTTP do przegldarki.

Metoda GET Metoda GET rni si zasadniczo od metody POST. Tzn. dane, ktre s przekazywane s zbudowane tak samo ? poszczeglne pola s oddzielone znakiem &. Zasadnicz jednak rnic jest to, e te dane s doczane do adresu strony. Czyli wyglda to mniej wicej tak: http://www.4programmers.net/sk[...]e1=wartosc1&pole2=wartosc2 456 | S t r o n a

Metod GET dane mona przekazywa niewielkie iloci danych. Nie zaleca si przekazywania t metod np. zawartoci duych p edycyjnych itp.

Pobieranie kodu strony WWW


Aby pobra kod HTML wybranej strony WWW, wystarczy wywoa metod Get. Komponent TidHTTP zawiera kilka przecionych metod Get, w tym jedn, ktra zwraca zawarto podanej w parametrze strony. HTML := HTTP.Get(edtURL.Text); // pobranie kodu HTML

Funkcja Get zwraca w powyszym przypadku kod strony WWW w formie acucha znakw ? String. Program w trakcie dziaania przedstawiony jest na rysunku 11.10.

Rysunek 11.10. Kod strony wstawiony do komponentu TMemo Kod procedury pobierajcej ow zawarto witryny wyglda mniej wicej tak: procedure TMainForm.btnGetClick(Sender: TObject); var HTML : String; begin { wczeniej we waciwoci Host komponentu TidHTTP konieczne jest przypisanie wartoci "4programmers.net" jako nazwy hosta } 457 | S t r o n a

HTTP.Connect; // prba poczenia try try HTML := HTTP.Get(edtURL.Text); // pobranie kodu HTML memHTML.Lines.Text := HTML; // przypisanie treci strony do komponentu TMemo except raise Exception.Create('Nie mona poczy si z serwerem!'); end; finally HTTP.Disconnect; // rozczenie end; end;

Przed poczeniem si z danym serwerem konieczne jest wpisanie we waciwoci Host nazwy serwera, z ktrym prbujemy si czy. Czyli w przypadku, gdy prbujemy odczyta kod strony http://4programmers.net/programy.php, we waciwoci Host naley wpisa 4programmers.net. Istnieje jednak na to recepta ? wystarczy sama metoda Get, aby komponent sam, na podstawie podanego adresu, poczy si z danym serwerem: procedure TMainForm.btnGetClick(Sender: TObject); var HTML : String; begin HTML := HTTP.Get(edtURL.Text); // pobranie kodu HTML memHTML.Lines.Text := HTML; // przypisanie treci strony do komponentu TMemo end;

Wysyanie danych przez skrypt PHP


Mam tu na myli wysyanie e-maila przez skrypt PHP umieszczony na serwerze. Nasz program, korzystajc z metody Post, przele do skryptu odpowiednie informacje, a ten z kolei wyle do mnie emaila z wiadomoci wpisan przez uytkownika. Jest to rwnie dobry sposb na wysyanie poczty, lecz naley pamita, e wysanie jest zalene od tego, czy serwer w danym momencie dziaa, czy te nie. Jestemy jednak w stanie w pewien sposb kontrolowa przesyanie tego e-maila ? moemy wywietli odpowiedni tekst lub np. policzy, ile osb korzystao z naszego skryptu. Nie mam zamiaru wyjania tutaj podstawowych poj zwizanych z PHP. Jeeli t ksik czyta kto, kto wczeniej mia styczno z tym jzykiem, to zrozumienie skryptu z listingu 11.6 nie bdzie 458 | S t r o n a

stanowio dla niego problemu. Listing 11.6. Budowa skryptu mail.php <? /* Copyright (c) 2002 by Adam Boduch <adam@4programmers.net> */ mail("adam@4programmers.net", "Opinia dotyczca ksiki", $message, "From: $from"); echo "E-mail zosta wysany prawidowo. Dzikuje! Odpowied uzyskasz na adres $from"; ?>

Polecenie mail w PHP powoduje wysanie skryptu z tematem Opinia dotyczca ksiki na adres adam@4programmers.net. To chyba powinno wystarczy.

Wysyanie danych do skryptu PHP Interfejs programu przedstawiony zosta na rysunku 11.11. Posiada dwie kontrolki ? jedn suc do wpisania adresu zwrotnego, a drug do wpisania treci wiadomoci.

Rysunek 11.11. Program w trakcie dziaania Po naciniciu przycisku dane z formularza s przesyane do do pliku http://4programmers.net/mail.php:

459 | S t r o n a

procedure TMainForm.btnSendMsgClick(Sender: TObject); var StreamIn, StreamOut : TStringStream; begin { utworzenie strumieni } HTTP.Host := '4programmers.net'; StreamIn := TStringStream.Create(''); StreamOut := TStringStream.Create(''); try StreamIn.WriteString(Format('message=%s&from=%s', [memMsg.Text, edtFrom.Text])); HTTP.Post('http://4programmers.net/mail.php', StreamIn, StreamOut); // wysanie zawartoci do skryptu { wywietlenie odpowiedzi, jaka zostaa zwrcona przez skrypt } MessageBox(Handle, PChar(StreamOut.DataString), 'Wiadomo wysana :?)', MB_ICONINFORMATION); finally { zwolnienie zasobw } StreamIn.Free; StreamOut.Free; end; end;

Jak widzisz, procedura ta korzysta ze strumieni typu TStringStream. Zawarto pierwszego z nich ? StremIn ? bdzie przesyana do skryptu, a zawarto drugiego ? StreamOut ? bdzie zawieraa tekst zwrcony przez w skrypt mail.php. Naley pamita o tym, e dane przesyane do skryptu musz mie specyficzn budow, ktr skrypt bdzie w stanie zanalizowa. Przede wszystkim wszystkie parametry (tj. zwrotny adres e-mail i tre wiadomoci) musz by oddzielone znakiem &. StreamIn.WriteString(Format('message=%s&from=%s', [memMsg.Text, edtFrom.Text]));

Dodatkowo warto jest oddzielona od nazwy parametru znakiem =. Tutaj naley Ci si par sw wyjanienia. Nazwa parametru ? np. message ? to inaczej identyfikator. Za pomoc tej nazwy skrypt PHP bdzie odwoywa si do treci przesanej mu w formularzu. Skrypt mail.php rzeczywicie umieciem na serwerze http://4programmers.net. Jeeli wic kiedykolwiek chciaby wysa mi opini dotyczc tej ksiki, moesz skorzysta z tego programu.

Praktyczne przykady wykorzystania HTTP


460 | S t r o n a

Poniej przedstawiam dwa inne przykady wykorzystania protokou HTTP oraz komponentu TidHTTP. Wydaje mi si, e prezentuj one rozwizanie popularnych problemw, z jakimi spotyka si pocztkujcy programista.

Sprawdzenie nowej wersji programu


Uytkujc rne programy, moesz zauway, e dua ich ilo oferuje sprawdzenie, czy w danej chwili nie zostaa udostpniona nowa wersja. Przedstawi tutaj rozwizanie tego problemu. Cay kod bdzie zrealizowany z uyciem komponentw dostpnych w Delphi 7.

Jak to dziaa? Zasada dziaania bdzie nastpujca: na swoim serwerze musisz umieci plik tekstowy, ktry zawiera bdzie numer wersji programu. Po uruchomieniu programu i naciniciu przycisku program odczyta wersj z pliku tekstowego, a nastpnie porwna j ze swoim numerem wersji. Jeeli numer z serwera bdzie wikszy, oznacza to bdzie, e istnieje nowa wersja i naley poda do niej URL.

Budowa programu Gwny formularz programu przedstawiony zosta na rysunku 11.12.

Rysunek 11.12. Gwny formularz programu Dwie etykiety, ktre wida na rysunku, s na pocztku niewidoczne (ich waciwoci Visible s ustawione na False) ? zostan pokazane w przypadku znalezienia nowej wersji programu. Etykieta lblURL ma ustawiony kolor niebieski, a waciwo Cursor ma warto crHandPoint. Etykieta bdzie zawieraa adres nowej wersji pliku ? po jej klikniciu otwarta zostanie przegldarka i rozpocznie si pobieranie pliku. 461 | S t r o n a

Procedura sprawdzajca istnienie nowej wersji wyglda tak: procedure TMainForm.Button1Click(Sender: TObject); var NewVersion : String[5]; begin HTTP.Host := '127.0.0.1'; NewVersion := HTTP.Get(URL); // pobranie numeru aktualnej wersji

if NewVersion > Version then begin { w przypadku, gdy znaleziono now wersje } ShowMessage('Znaleziono now wersj programu ? ' + NewVersion); // istnieje nowa wersja programu Delete(NewVersion, Pos('.', NewVersion), 1); // usunie kropki z numeru wersji Label1.Visible := True; lblURL.Caption := 'http://127.0.0.1/' + AppName + NewVersion + '.exe'; lblURL.Visible := True; end; end;

Na samym pocztku po pobraniu numeru wersji i porwnaniu jej z dotychczasow wartoci moemy stwierdzi, czy istnieje uaktualnienie programu. URL do nowej wersji pliku nie moe zawiera kropek, wic przed podaniem uytkownikowi adresu naley je wszystkie usun. Mona to zrealizowa za pomoc funkcji Pos oraz Copy. Peen kod rdowy programu przedstawiony zosta w listingu 11.7. Listing 11.7. Kod rdowy moduu MainFrm.pas unit MainFrm; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls, IdBaseComponent, IdComponent, IdTCPConnection, IdTCPClient, IdHTTP, { dodany modul ??> } ShellAPI; type TMainForm = class(TForm) HTTP: TIdHTTP; Button1: TButton; Label1: TLabel; 462 | S t r o n a

lblURL: TLabel; procedure FormCreate(Sender: TObject); procedure Button1Click(Sender: TObject); procedure lblURLClick(Sender: TObject); private { Private declarations } public { Public declarations } end; const Version = '1.0'; URL = 'http://127.0.0.1/version.txt'; AppName = 'moj_program'; var MainForm: TMainForm; implementation {$R *.dfm} procedure TMainForm.FormCreate(Sender: TObject); begin Caption := AppName + ' ? ' + Version; end; procedure TMainForm.Button1Click(Sender: TObject); var NewVersion : String[5]; begin HTTP.Host := '127.0.0.1'; NewVersion := HTTP.Get(URL); // pobranie numeru aktualnej wersji

if NewVersion > Version then begin { w przypadku, gdy znaleziono now wersje } ShowMessage('Znaleziono now wersj programu ? ' + NewVersion); // istnieje nowa wersja programu Delete(NewVersion, Pos('.', NewVersion), 1); // usunie kropki z numeru wersji Label1.Visible := True; lblURL.Caption := 'http://127.0.0.1/' + AppName + NewVersion + '.exe'; lblURL.Visible := True; end; end; 463 | S t r o n a

procedure TMainForm.lblURLClick(Sender: TObject); begin { pobranie nowej wersji programu } ShellExecute(Handle, 'open', PChar(TLabel(Sender as TObject).Caption), nil, nil, SW_SHOWNORMAL); end; end.

Korzystanie z zewntrznej wyszukiwarki


Tym razem napiszemy program, ktry bdzie korzysta z wyszukiwarki serwisu 4programmers.net. Jest to serwis powicony programowaniu ? m.in. w Delphi. wiczenie bdzie polega na pobraniu, a nastpnie analizie kodu HTML strony i przedstawieniu wynikw wyszukiwania w naszej aplikacji.

Jak to dziaa? Skorzystanie z zewntrznej wyszukiwarki umieszczonej na jakim serwerze wymaga najpierw zadania okrelonego zapytania. Zapytanie konstruujemy w formie okrelonego adresu strony ? np. http://4programmers.net/search.php?Q=delphi To zapytanie przekazujemy do skryptu search.php metod GET. Parametr Q, ?doklejony? do adresu, zawiera sowo kluczowe do wyszukania. Jedyna trudno to interpretacja kodu rdowego strony WWW i wyodrbnienie z niego interesujcych nas elementw.

Struktura kodu HTML Nim zabierzemy si za pisanie aplikacji, naley pozna struktur, czyli sposb, w jaki wyszukiwarka 4programmers.net generuje kod HTML. Wejd na stron www.4programmers.net. Na gwnej stronie znajduje si wyszukiwarka. Wpisz w odpowiednie pole sowo do wyszukania ? np. hook. W rezultacie tego zapytania wyszukiwarka znajdzie jedn stron odpowiadajc zadanemu pytaniu. Kliknij prawym przyciskiem w obrbie strony i wybierz pozycj Poka rdo. Nas interesuje jedynie ten fragment HTML: <!??TITLE??>Jak zaoy globalnego Hooka na klawiatur?<!--/TITLE-></a></b> (2002-10-02 16:54:16)<br><!--BODY-->Oto kod ukazujcy, jak 464 | S t r o n a

zaoy funkcj przechwytujca na klawiatur. W interface: var MainHook : HHOOK; function KeyHook(Code: Integer; wParam : WPARAM; lParam : LPARAM): Longint; stdcall; A w Implementation: function KeyHook(Code: Integer; w...<!--/BODY--><br>URL: <a href="http://www.4programmers.net/view_faq.php?id=181"> <!--URL-->http://www.4programmers.net/view_faq.php?id=181<!--/URL-->

Za pomoc komentarzy HTML opisane s fragmenty strony. Np. <!--TITLE-->Jak zaoy globalnego Hooka na klawiatur?<!--/TITLE-->

Tytu znalezionej strony mieci si pomidzy znacznikami i . Tak wic naszym zadaniem bdzie ?wycignicie? tekstu znajdujcego si pomidzy tymi znacznikami. Adres strony mieci si pomidzy znacznikami i , a krtki opis ? pomidzy znacznikami i . To znacznie uatwi nam wyodrbnienie z kodu HTML interesujcych nas rzeczy. Wszystkie te dane przedstawimy w naszym programie.

Analiza kodu HTML Najtrudniejsz rzecz do zrobienia jest wyodrbnienie tytuu strony, jej adresu oraz opisu. Gdybymy korzystali z PHP, nie byoby problemu ? istnieje tam wygodna funkcja ereg. Jednak w Delphi nie dysponujemy odpowiednikiem tej funkcji, trzeba zatem napisa j samemu. Nagwek takiej funkcji moe wyglda np. tak: function Ereg(var Body : String; Pattern : String) : TMatch;

Pierwszy parametr jest treci strony (kod HTML), a drugi parametr to warto do znalezienia. Przykadowo funkcj bdzie mona wywoa w ten sposb: Ereg(Body, '<!?-TITLE-->|<!--/TITLE-->');

Tym sposobem funkcja pobierze wszystko, co znajduje si pomidzy znacznikami i . Oto cay kod procedury Ereg: function TMainForm.Ereg(var Body: String; Pattern: String): TMatch; var Offset : Integer; 465 | S t r o n a

Counter : Integer; APattern : array[0..1] of String; BeginPos, EndPos : Integer; begin Offset := 1; // pozycja ostatnio znalezionej wartoci Counter := 1; { oddzielenie dwch elementw na podstawie znaku | } APattern[0] := Copy(Pattern, 0, Pos('|', Pattern) ?1); APattern[1] := Copy(Pattern, Pos('|', Pattern)+1, Length(Pattern)); while (PosEx(APattern[0], Body, Offset) > 0) do // szukamy pierwszego czonu begin SetLength(Result, Counter); // okrelamy wielko tablicy { okrelenie pocztkowej pozycji szukanego okrelenia } BeginPos := PosEx(APattern[0], Body, Offset) + Length(APattern[0]); { okrelenie kocowej pozycji szukanego okrelenia } EndPos := PosEx(APattern[1], Body, Offset); Offset := EndPos+1; // do zmiennej przypisywana jest pozycja ostatnio znalezionej wartoci // wycignicie treci pomidzy np. znacznikami <!--TITLE--> i <!--/TITLE--> Result[Counter-1] := Copy(Body, BeginPos, EndPos - BeginPos); Inc(Counter); end; end; // zwikszenie licznika

Zrealizowanie tego zdania polega przede wszystkim na zastosowaniu funkcji operujcych na acuchach takich, jak PosEx i Copy. Funkcja PosEx jest now funkcj w Delphi 7. Znajduje si w module StrUtils ? dodaj wic ten modu do listy moduw uses. Na potrzeby naszej funkcji Ereg naleao take zadeklarowa nowy typ danych: TMatch = array of String;

Taki zabieg by konieczny z tego wzgldu, i w Delphi funkcje nie mog zwraca danych w postaci tablic. Aby to omin, naley zadeklarowa nowy typ tablic dynamicznych ? w tym wypadku TMatch.

466 | S t r o n a

Kod rdowy programu Kompletny kod rdowy umieszczony zosta w listingu 11.8, a program w trakcie dziaania prezentuje rysunek 11.13. Listing 11.8. Kod rdowy moduu MainFrm.pas { Copyright (c) 2002 by Adam Boduch <adam@4programmers.net> } unit MainFrm; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls, Buttons, ComCtrls, StrUtils, IdBaseComponent, IdComponent, IdTCPConnection, IdTCPClient, IdHTTP, ImgList, ShellAPI, IdAntiFreezeBase, IdAntiFreeze; type TMatch = array of String; TMainForm = class(TForm) StatusBar: TStatusBar; GroupBox1: TGroupBox; Label1: TLabel; edtQ: TEdit; btnSearch: TBitBtn; GroupBox2: TGroupBox; ListView: TListView; HTTP: TIdHTTP; ImageList1: TImageList; IdAntiFreeze: TIdAntiFreeze; procedure HTTPConnected(Sender: TObject); procedure HTTPDisconnected(Sender: TObject); procedure btnSearchClick(Sender: TObject); procedure ListViewInfoTip(Sender: TObject; Item: TListItem; var InfoTip: String); procedure edtQKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState); procedure edtQClick(Sender: TObject); private 467 | S t r o n a

anTitle : TMatch; anURL : TMatch; anBody : TMatch; function Ereg(var Body : String; Pattern : String) : TMatch; public { Public declarations } end; var MainForm: TMainForm; implementation {$R *.dfm} { TMainForm } function TMainForm.Ereg(var Body: String; Pattern: String): TMatch; var Offset : Integer; Counter : Integer; APattern : array[0..1] of String; BeginPos, EndPos : Integer; begin Offset := 1; // pozycja ostatnio znalezionej wartoci Counter := 1; { oddzielenie dwch elementw na podstawie znaku | } APattern[0] := Copy(Pattern, 0, Pos('|', Pattern) ?1); APattern[1] := Copy(Pattern, Pos('|', Pattern)+1, Length(Pattern)); while (PosEx(APattern[0], Body, Offset) > 0) do // szukamy pierwszego czonu begin SetLength(Result, Counter); // okrelamy wielko tablicy { okrelenie pocztkowej pozycji szukanego okrelenia } BeginPos := PosEx(APattern[0], Body, Offset) + Length(APattern[0]); { okrelenie kocowej pozycji szukanego okrelenia } EndPos := PosEx(APattern[1], Body, Offset); Offset := EndPos+1; // do zmiennej przypisywana jest pozycja ostatnio znalezionej wartoci // wycignicie treci pomidzy np. znakami <!--TITLE--> i <!-/TITLE--> Result[Counter?1] := Copy(Body, BeginPos, EndPos ? BeginPos); 468 | S t r o n a

Inc(Counter); end; end;

// zwikszenie licznika

procedure TMainForm.HTTPConnected(Sender: TObject); begin btnSearch.Enabled := False; StatusBar.SimpleText := 'Poczono z serwerem 4programmers.net...'; end; procedure TMainForm.HTTPDisconnected(Sender: TObject); begin btnSearch.Enabled := True; StatusBar.SimpleText := 'Rozczono z serwerem 4programmers.net...'; end; function Convert(Src: String): String; begin Src := StringReplace(Src,Chr(182),Chr(156), [rfReplaceAll]); Src := StringReplace(Src,Chr(177),Chr(185), [rfReplaceAll]); Src := StringReplace(Src,Chr(188),Chr(159), [rfReplaceAll]); Src := StringReplace(Src,'<span style="background?color: #C0C0C0">', '', [rfReplaceAll]); Src := StringReplace(Src,'</span>', '', [rfReplaceAll]); Src := StringReplace(Src,'&lt','<', [rfReplaceAll]); Src := StringReplace(Src,'&gt','>', [rfReplaceAll]); Result := Src; end; procedure TMainForm.btnSearchClick(Sender: TObject); var HTML : String; I : Integer; TitleItem : TListItem; begin HTTP.Host := '4programmers.net'; HTTP.Connect; // czenie... try try { wyczyszczenie tablic } anTitle := nil; anURL := nil; anBody := nil; ListView.Clear; // wyczyszczenie Listview'a

469 | S t r o n a

StatusBar.SimpleText := 'Trwa wyszukiwanie...'; { pobieranie rezultatu poszukiwa } HTML := HTTP.Get('http://4programmers.net/search.php?Q=' + edtQ.Text); anTitle := Ereg(HTML, '<!--TITLE-->|<!--/TITLE-->'); // wycignicie tytuw anURL := Ereg(HTML, '<!--URL-->|<!--/URL-->'); anBody := Ereg(HTML, '<!--BODY-->|<!--/BODY-->'); if High(anTitle) = ?1 then MessageBox(Handle, 'Niestety nie znaleziono adnej strony odpowiadajcej Twojemu zapytaniu!', ':?(', MB_ICONINFORMATION); { ptla po wszystkich znalezionych stronach } for I := 0 to High(anTitle) do begin TitleItem := ListView.Items.Add; // dodanie do komponentu TitleItem.ImageIndex := 0; // numer ikony ozdabiajcej dan pozycj TitleItem.Caption := Convert(anTitle[i]); // dodanie tytuu strony (po konwersji) TitleItem.SubItems.Add(Convert(anBody[i])); // opis strony do drugiej kolumny end; except raise; end; finally HTTP.Disconnect; end; end; procedure TMainForm.ListViewInfoTip(Sender: TObject; Item: TListItem; var InfoTip: String); begin // dymek podpowiedzi... InfoTip := Convert(anBody[Item.Index]); end; procedure TMainForm.edtQKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState); begin // po naciniciu klawisza Enter rozpocznij wyszukiwanie if Key = vk_Return then btnSearchClick(Sender); end;

470 | S t r o n a

procedure TMainForm.edtQClick(Sender: TObject); begin // otwrz stron internetow ShellExecute(Handle, 'open', PChar(anURL[ListView.Selected.Index]), nil, nil, Sw_Show); end; end.

Rysunek 11.13. Rezultat poszukiwa Nie twierdz, e program jest prosty i opiera si na prostych zasadach. Nie mona jednak pozostawa na tym samym etapie, lecz naley wyznacza sobie zadania i konsekwentnie je realizowa. Du cz kodu stanowi komentarze ? ich przeanalizowanie pozwoli zrozumie zasad dziaania programu. Po poczeniu si z serwerem przychodzi czas na zadanie zapytania i uzyskanie danej odpowiedzi: HTML := HTTP.Get('http://4programmers.net/search.php?Q=' + edtQ.Text);

W tym momencie skrypt umieszczony na serwerze wykonana za nas ca prac i zwrci odpowiedni kod rdowy w postaci znacznikw HTML. Kolejny etap to analiza tego kodu za pomoc stworzonej wczeniej funkcji Ereg: anTitle := Ereg(HTML, '<!--TITLE-->|<!--/TITLE-->');

471 | S t r o n a

Funkcja ta wykona spor cz caego zadania. Od tej pory zmienna anTitle bdzie tablic i zawiera bdzie tytuy wszystkich zwrconych przez wyszukiwark stron. Teraz nie pozostao ju nic innego, jak wywietli rezultaty w komponencie TListView. Nim to jednak nastpi, naley dokona konwersji polskich znakw, za co odpowiada zamieszczona w kodzie funkcja Convert. Wszystko dlatego, e strona WWW zapisana jest w standardzie kodowania: iso-8859-2, czyli po przedstawieniu fragmentw HTML w komponencie TListView zamiast polskich znakw pojawiyby si ?kraczki?. Peen kod rdowy tego programu moesz znale na pycie CD-ROM w katalogu ../listingi/11/Search/p224.dpr.

Protok FTP
atwo operowania komponentami Indy sprawia, i obsuga protokou FTP jest rwnie prosta, co innych komponentw z rodziny Indy. czenie, odbieranie listy katalogw czy przesyanie lub ciganie plikw jest realizowane za pomoc pojedynczych procedur. Przykady wykorzystania komponentw Indy moesz znale na stronie http://www.nevrona.com/Indy/

Podsumowanie
W tym rozdziale zaprezentowaem podstawowe techniki wykorzystania obsugi Internetu w swoich aplikacjach. Mam nadziej, e opisane tutaj oraz zamieszczone na pycie CD-ROM przykady pomog Ci w rozszerzeniu swoich programw o dodatkowe opcje. Co prawda wykorzystanie biblioteki WinSock.dll oraz WinInet.dll sprawia nieco kopotw, ale obecnie wikszo programistw korzysta z gotowych komponentw upraszczajcych wszystkie czynnoci zwizane z dostpem do sieci Internet ? Tobie te zalecam ich stosowanie. Zaczniki:

Listingi_11.zip (287.65 kB)

472 | S t r o n a

Rozdzia 12

Edytuj Historia Przenie Obserwuj

WinAPI
Nie sposb byo nie wspomnie w tej ksice o WinAPI. Z tym terminem zetkne si ju wielokrotnie podczas lektury niniejszej publikacji. Teraz chciabym omwi ten temat nieco dogbniej. Nie jest moliwe bowiem zawarcie wszystkich informacji dotyczcych WinAPI w jednym rozdziale ? temat ten jest na tyle rozlegy, e mona by mu powici osobn ksik. Wicej o WinAPI moesz dowiedzie si ze strony http://msdn.microsoft.com lub z systemu pomocy Delphi (znajdziesz tam dokadny opis funkcji i procedur).

Spis treci 1 Czym tak naprawd jest WinAPI? 1.1 Zasady tworzenia programw za pomoc WinAPI 1.2 Brak zdarze 1.3 Brak komponentw 1.4 Zalety wykorzystania WinAPI 2 Pierwszy program 3 Funkcja okienkowa 4 Rejestracja klasy 5 Tworzenie formularza 6 Komunikaty i uchwyty 7 acuchy 7.1 Konwersja acuchw 7.2 Funkcje operujce na acuchach 7.2.1 CharLower, CharUpper 7.2.2 lstrlen 7.2.3 lstrcpyn 8 Tworzenie kontrolek 8.1 Umieszczanie kontrolek przy starcie programu 8.2 Flagi kontrolek 9 Obsuga zdarze 10 Uchwyty do kontrolek 11 Tworzenie bardziej zaawansowanych kontrolek 473 | S t r o n a

11.1 Pozostae kontrolki 12 Wywietlanie grafiki 12.1 Rysowanie w WinAPI 12.2 Kontekst urzdzenia graficznego 12.3 Obsuga WM_PAINT 12.3.1 Zmiana koloru ta 12.4 adowanie i wywietlanie bitmapy 13 adowanie zasobw 13.1 Skompilowane zasoby 13.2 Wykorzystanie zasobw 13.2.1 Wywietlenie formularza 13.2.2 Ustawianie wartoci komponentw formularza 13.3 LockResource, LoadResource, FindResource 13.4 Zapisywanie plikw na dysku 14 Podsumowanie

Mona powiedzie, e ten rozdzia przeznaczony jest dla ?maniakw? Delphi w pozytywnym tego sowa znaczeniu. Programowanie w WinAPI nie jest bowiem atwe i wygodne, ale umoliwia zachowanie wikszej kontroli nad programem.

Czym tak naprawd jest WinAPI?


Pena nazwa tego skrtu to Windows Application Programming Interface . Dla osb, ktre wczeniej tworzyy swoje programy w Turbo Pascalu, biblioteka wizualna i klasy mog wyda si do niezrozumiae. Z kolei dla niektrych prostsze moe okaza si rozpoczcie pisania programw metod API. Ty jednak jeste ju zapewne przyzwyczajony do stosowania klas i komponentw, a WinAPI moe Ci si wyda trudne lub po prostu nieciekawe. Problem, ktrego rozwizanie przy uyciu komponentw wymagao jednego wiersza kodu, przy wykorzystaniu WinAPI moe oznacza konieczno napisania nawet kilkudziesiciu wierszy! Dlatego nie zdziwi si, jeeli ominiesz ten rozdzia i przejdziesz od razu do kolejnego.

Zasady tworzenia programw za pomoc WinAPI


Wyobra sobie pisanie programw bez wykorzystania formularzy, komponentw i wszystkich innych udogodnie oferowanych przez Delphi. Nasze programy bd oparte jedynie na podstawowych moduach Windows.pas i Messages.pas. Wszystkie funkcje, z ktrych bdziemy korzysta, zawarte s 474 | S t r o n a

w bibliotekach DLL systemu Windows. Ich zaadowanie do programu odbywa si w module Windows.pas. Nam, uytkownikom tych bibliotek, potrzebna jest wiedza o ich budowie ? liczbie i typie parametrw, wartociach zwracanych przez funkcj itp. Std miej na uwadze perspektyw czstego zagldania do pomocy Delphi. Przy tej okazji warto wspomnie o moliwoci cho czciowego nauczenia si jzyka C. System Windows by pisany w tym jzyku, std opisy wszystkich funkcji API (deklaracje) s rwnie w nim przedstawione. Jest to pewna okazja do poznania choby w maym stopniu budowy jzyka C. Gdy kiedy zaczniesz pisa programy w jzyku C++, znajomo WinAPI bardzo Ci si przyda! Nazwy funkcji s takie same ? jedynie skadnia nieco si rni.

Brak zdarze
Podczas pisania programw w ?czystym? API bdziemy pozbawieni wygodnego mechanizmu, jakim s zdarzenia. Jak pamitasz z rozdziau 5., mechanizm zdarze mona zastpi poprzez komunikaty Windows. Ten moment jest wic dobr okazj, aby cofn si do rozdziau 5. i przypomnie sobie zasad funkcjonowania komunikatw.

Brak komponentw
W API bdziemy musieli oby si bez komponentw. Te ?klocki?, jakimi s komponenty, w duym stopniu odciay nas od mozolnego operowania komunikatami czy pamici. Nie bdziemy jednak pozbawieni typowych kontrolek Windows, jak przycisk czy lista rozwijalna ? bdziemy je tworzy w kodzie programu za pomoc funkcji CreateWindow.

Zalety wykorzystania WinAPI


Zastanawiasz si moe, co takiego zyskasz, uywajc mechanizmw WinAPI? Powiem szczerze: niewiele. Duym plusem jest szybko dziaania aplikacji oraz rozmiar. Programy tworzone w Delphi i wykorzystujce VCL maj due rozmiary. Nawet prosty program w postaci ?czystego? formularza potrafi zajmowa grubo ponad 300 kB. Aplikacje wykorzystujce jedynie WinAPI mog zajmowa nawet 16 kB i s wykonywane znacznie szybciej. Rnica jest znaczna, nieprawda? Czytajc ten rozdzia, masz moliwo zaznajomienia si z dotd nieznanymi funkcjami, z ktrych by moe bdziesz musia skorzysta w swoich programach, gdy VCL okae si niewystarczajcy i zbytnio bdzie Ci ogranicza. Gwnymi zaletami Delphi s przecie biblioteka VCL, klasy oraz formularze, dziki ktrym tworzenie 475 | S t r o n a

programw trwa znacznie krcej. Jeli porzucisz te udogodnienia, pisanie aplikacji moe zaj wicej czasu, a nie po to chyba tworzono Delphi, prawda? Podsumowujc: WinAPI jest okazj do gbszego zaznajomienia si z tematyk programowania w systemie Windows, lecz nie nadaje si do pisania duych projektw.

Pierwszy program
Przypomnij sobie rozdzia 2. Wwczas poznawae dopiero jzyk Object Pascal, ale tworzone przez Ciebie programy take nie zawieray adnych komponentw czy formularzy. Te programy po skompilowaniu take miay rozmiar kilkunastu kilobajtw ? mona zatem powiedzie, e ju wtedy pisae programy WinAPI! Zamknij formularz i Edytor kodu. Nastpnie z menu Project wybierz polecenie View Source. Kod rdowy projektu (pliku *.dpr) doprowad do takiej postaci: program Project1; uses Windows; begin end.

Na razie nie potrzebujemy pliku zasobw, wic usunem take dyrektyw {$R}. Ikon naszego programu moemy doda w nastpnej kolejnoci. Tak powstay kod rdowy zapisz gdzie na dysku. Nastpnie z menu Project wybierz polecenie Build, co spowoduje skompilowanie aplikacji i utworzenie pliku *.exe. Spjrz teraz na rozmiar aplikacji ? u mnie jest to 14 kB! Na razie co prawda program jest ?pusty?, ale ju wkrtce co nieco do niego dodamy.

Funkcja okienkowa
Dotd nasz program koczy prac zaraz po uruchomieniu go ? w bloku begin nie ma przecie adnej instrukcji. Naszym celem jest napisanie takiego programu w WinAPI, ktry zakoczyby prac po interwencji uytkownika ? zamkniciu okna. Musimy wic napisa kod, ktry spowodowaby wywietlenie formularza. Jednym z etapw tworzenia takiego formularza jest napisanie funkcji okienkowej. Funkcja taka bdzie odpowiedzialna za odbieranie wszystkich komunikatw, ktre

476 | S t r o n a

docieraj do okna i ewentualn reakcj na dany komunikat. Zadeklaruj wic w programie tak funkcj: function WndProc(Wnd: HWND; uMsg: UINT; wPar: WPARAM; lPar: LPARAM): LRESULT; stdcall; begin end;

Znaczenie parametrw tej funkcji jest nastpujce:


Wnd ? uchwyt do okna. uMsg ? komunikat. wPar ? pierwsza warto komunikatu. lPar ? druga warto komunikatu.

Taka budowa jest nieprzypadkowa ? aby cay program zosta prawidowo skompilowany, funkcja okienkowa musi wyglda tak, jak to przedstawiem powyej. Pierwszym komunikatem, jaki bdzie obsugiwany przez funkcj okienkow, jest WM_DESTROY. Program musi odpowiednio zareagowa na prb zamknicia programu. function WndProc(Wnd: HWND; uMsg: UINT; wPar: WPARAM; lPar: LPARAM): LRESULT; stdcall; begin { na pocztek zwracamy warto 0 ? meldunek jest przetwarzany } Result := 0; case uMsg of { w tym miejscu naley obsuy nalene komunikaty } { w funkcji DefWindowProc przekazujemy takie same parametry, jak w funkcji okienkowej } WM_DESTROY: PostQuitMessage(0); else Result := DefWindowProc(Wnd, uMsg, wPar, lPar); end; end;

Jak widzisz, instrukcja case sprawdza, jaki komunikat zosta odebrany przez funkcj okienkow. W przypadku odebrania komunikatu WM_DESTROY program koczy prac ? PostQuitMessage. Na samym jednak pocztku przypisujemy funkcji warto zwrotn ? cyfr 0. W przeciwnym wypadku ? jeeli nadesany komunikat ?nas interesuje? ? przekazujemy go dalej, do domylnego okna. Realizuje to funkcja DefWindowProc; parametry musz by identyczne z parametrami funkcji okienkowej. Aby program zosta prawidowo skompilowany, na licie uses musi znale si modu Messages.pas.

477 | S t r o n a

Rejestracja klasy
Aby cay formularz mg zosta stworzony, uprzednio naley zarejestrowa klas. Rejestracja klasy nastpuje poprzez wywoanie funkcji RegisterClass z moduu Windows.pas. function RegisterClass(const lpWndClass: TWndClass): ATOM; stdcall;

W parametrze owej funkcji naley poda zmienn wskazujc rekord TWndClass:

TWndClass = packed record style: UINT; lpfnWndProc: TFNWndProc; cbClsExtra: Integer; cbWndExtra: Integer; hInstance: HINST; hIcon: HICON; hCursor: HCURSOR; hbrBackground: HBRUSH; lpszMenuName: PAnsiChar; lpszClassName: PAnsiChar; end;

Powyszy rekord okrela wygld formularza, kolor ta, styl i kursor. Znacznie parametrw jest nastpujce:

style ? parametr w okrela styl wywietlanego okna. Moliwe jest mieszanie stylw za pomoc operatora or. lpfnWndProc ? jest to wskazanie na funkcj okienkow. cbClsExtra ? liczba dodatkowych bajtw alokowanych wraz z rekordem. cbWndExtra ? liczba dodatkowych bajtw alokowanych wraz z instancj okna. hInstance ? uchwyt do zasobw. hIcon ? identyfikacja formularza. hCursor ? kursor uywany w czasie wywietlania formularza. hbrBackground ? to formularza. Moliwe jest zastosowanie jednej z poniszych wartoci: COLOR_ACTIVEBORDER, COLOR_ACTIVECAPTION, COLOR_APPWORKSPACE, COLOR_BACKGROUND, COLOR_BTNFACE, COLOR_BTNSHADOW, COLOR_BTNTEXT, COLOR_CAPTIONTEXT, COLOR_GRAYTEXT, COLOR_HIGHLIGHT, COLOR_HIGHLIGHTTEXT, COLOR_INACTIVEBORDER, COLOR_INACTIVECAPTION, COLOR_MENU, COLOR_MENUTEXT, COLOR_SCROLLBAR, COLOR_WINDOW, COLOR_WINDOWFRAME, COLOR_WINDOWTEXT. lpszMenuName ? wskazanie na acuch okrelajcy menu uywane w programie. lpszClassName ? wskazanie na nazw klasy (warto typu PChar).

478 | S t r o n a

Rejestracja nowej klasy moe by wykonana w poniszy sposb: var Wnd: TWndClass;

// klasa okna

begin with Wnd do begin lpfnWndProc := @WndProc; // funkcja okienkowa hInstance := hInstance; // uchwyt do zasobw lpszClassName := 'My1stApp'; // klasa hbrBackground := COLOR_WINDOW; // kolor ta end; RegisterClass(Wnd); // zarejestruj now klas end;

W moim przypadku nie byo konieczne wypenianie wszystkich pl rekordu TWndClass. Przypisaem jedynie te pola, ktre wydaway si konieczne do uzyskania przynajmniej podstawowego wygldu formularza.

Tworzenie formularza
Na szczcie tworzenie samego formularza nie jest czynnoci zbytnio skomplikowan. Realizuje to bowiem jedna instrukcja ? CreateWindow: function CreateWindow(lpClassName: PChar; lpWindowName: PChar; dwStyle: DWORD; X, Y, nWidth, nHeight: Integer; hWndParent: HWND; hMenu: HMENU; hInstance: HINST; lpParam: Pointer): HWND;

Przyznasz, e ilo parametrw jest spora:


lpClassName ? nazwa klasy (warto PChar). Warto ta musi si rwna wartoci wpisanej w rekordzie TWndClass. lpWindowName ? acuch okrelajcy tekst, ktry bdzie wywietlany na formularzu. Znaczenie parametru mona porwnywa do waciwoci Caption komponentw. dwStyle ? styl okna (tabela 12.1). x ? pooenie formularza w poziomie. Wstawienie w to miejsce staej CW_USEDEFAULT powoduje automatyczne dopasowanie pooenia przez system (nowe okno bdzie przesunite lekko w lew stron). 479 | S t r o n a

y ? pooenie formularza w pionie. Wstawienie w to miejsce staej CW_USEDEFAULT powoduje automatyczne dopasowanie pooenia przez system. nWidth ? szeroko formularza. Tutaj take staa CW_USEDEFAULT powoduje automatyczne dopasowanie szerokoci. nHeight ? wysoko formularza. Staa CW_USEDEFAULT powoduje dopasowanie wysokoci formularza. hWndParent ? uchwyt do okna rodzica. hMenu ? wskazanie do menu, ktre ma by uyte w programie. hInstance ? okrela instancj moduu, ktry ma by kojarzony z programem.

Na podstawie tych danych utworzenie formularza moe wyglda tak: CreateWindow('My1stApp', 'Pierwszy program w WinAPI', WS_VISIBLE or WS_TILEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, 0, 0, hInstance, NIL);

Tabela 12.1. Najczstsze wartoci okrelajce styl okna Warto Opis

WS_OVERLAPPED Okno posiada pasek tytuowy oraz obramowanie WS_CHILD WS_POPUP WS_CAPTION WS_SYSMENU Potomne okno, ktre nie moe ?wyj? poza okno rodzicielskie Okno dialogowe Okno ma pasek tytuu Okno ma menu systemowe

WS_MINIMIZEBOX Okno ma przycisk minimalizacji WS_MAXIMIZEBOX Okno ma przycisk maksymalizacji WS_VISIBLE WS_HIDE WS_DISABLED WS_BORDER Okno jest widoczne Okno jest ukryte Nieaktywne okno ? nie reaguje na zdarzenia Okno posiada ramk

Listing 12.1. Pierwszy program napisany w WinAPI

480 | S t r o n a

{ Copyright (c) 2002 by Adam Boduch } program WndApp; uses Windows, Messages;

function WndProc(Wnd: HWND; uMsg: UINT; wPar: WPARAM; lPar: LPARAM): LRESULT; stdcall; begin { na pocztek zwracamy warto 0 ? meldunek jest przetwarzany } Result := 0; case uMsg of { w tym miejscu naley obsuy nalene komunikaty } { w funkcji DefWindowProc przekazujemy takie same parametry, jak w funkcji okienkowej } WM_DESTROY: PostQuitMessage(0); else Result := DefWindowProc(Wnd, uMsg, wPar, lPar); end; end; var Wnd: TWndClass; Msg: TMsg;

// klasa okna

begin with Wnd do begin lpfnWndProc := @WndProc; // funkcja okienkowa hInstance := hInstance; // uchwyt do zasobw lpszClassName := 'My1stApp'; // klasa hbrBackground := COLOR_WINDOW; // kolor ta end; RegisterClass(Wnd); // zarejestruj now klas CreateWindow('My1stApp', 'Pierwszy program w WinAPI', WS_VISIBLE or WS_TILEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, 0, 0, hInstance, NIL); while GetMessage(msg, 0, 0, 0) do DispatchMessage(msg); end.

481 | S t r o n a

W listingu 12.1 zaprezentowano cay kod rdowy programu. Nie omawiaem jeszcze ostatnich instrukcji tego listingu. S one bardzo wane ? bez nich program nie bdzie mg zosta uruchomiony. Operacje te musz by wykonane, aby funkcja okienkowa otrzymaa potrzebne meldunki. Podczas uruchamiania program musi wej w tzw. faz meldunkw. Funkcja GetMessage pobiera kolejno meldunki, wpisujc je do struktury TMsg (parametry s nieistotne), a nastpnie przekazuje funkcji DispatchMessage, ktra z kolei przekazuje meldunek funkcji okienkowej.

Komunikaty i uchwyty
Piszc programy w WinAPI, bdziemy posugiwali si wycznie komunikatami jako form zastpujc zdarzenia (funkcje SendMessage i PostMessage). Warto wic przypomnie sobie informacje na temat wysyania komunikatw. Ich odbieranie bdzie nastpowao tylko w funkcji okienkowej. Odwiemy zatem pami ? komunikaty mona podzieli na nastpujce kategorie:

komunikaty klawiaturowe (uytkownik nacisn lub zwolni jaki klawisz), komunikaty myszy (uytkownik wykona jak czynno mysz), komunikaty zegara, oznaczajce upyw okrelonego odcinka czasu, komunikaty systemu ? tworzenie okna, zmiana jego rozmiaru i pooenia, zwijanie i rozwijanie okna, zmiana kolorw systemowych itp., komunikaty wewntrzne ? wysyane przez inne okna utworzone w naszym programie.

W kadym komunikacie naley poda uchwyt okna docelowego (lub kontrolki docelowej). Uchwyt jest liczb 32-bitow, ktra identyfikuje kontrolk w systemie Windows. To wanie Windows przydziela uchwyty rnym kontrolkom. Nazwa typu reprezentujcego uchwyt zaczyna si od litery H, czyli np. HWND, HBRUSH, HFONT czy HBITMAP. Dziki temu atwo jest rozpozna, czy dana zmienna jest uchwytem.

acuchy
Podczas pisania programw w WinAPI bdziemy uywali jedynie acuchw typu PChar lub acuchw w formie tablicy. Stosowanie typu String powoduje spowolnienie dziaania programu i zuywanie wikszej iloci pamici. Typ PChar ma jeszcze jedn zalet ? mona dokonywa na nim takich operacji:

482 | S t r o n a

P2 := 'To jest Delphi'; P1 := P2 + 8;

Pozornie wyglda to tak, jakby do zmiennej P2 dodawana bya cyfra 8. W rzeczywistoci do typu P1 przypisujemy warto zmiennej P2, tyle e bez pierwszych 8 znakw. Podczas lektury dalszej czci ksiki moesz spotka si take z deklaracjami zmiennych w postaci tablicy: Variable : array[0..255] of char;

Do takiej zmiennej mona nastpnie przypisa dane w zwyky sposb, tyle e ich wielko bdzie ograniczona do 255 znakw. Variable := 'Adam Boduch';

Konwersja acuchw
W VCL ten problem nie istnia ? modu SysUtils posiada odpowiednie funkcje, umoliwiajce konwersj typw. Piszc programy API, nie bdziemy mogli z nich skorzysta ? pozostaje nam uycie funkcji zastpczych, np. wvsprintf. Oto moliwy sposb wykonania funkcji zastpczej: function IntToStr(Value : Integer) : String; var Buffer : array[0..255] of char; // bufor, w ktrym przechowywa bdziemy dane begin wvsprintf(Buffer, '%d', @Value); // tu nastpuje funkcja konwersji Result := Buffer; // zwracamy rezultat end;

Funkcja wvsprintf suy do konwertowania tekstu. Pierwszym parametrem musi by wskazanie zwracanego przez funkcj cigu. Ja zadeklarowaem Buffer ? 256-elementow tablic typu Char. Drugi parametr to tzw. maska. Jeli wstawimy w to miejsce znak %d, zostanie on zastpiony liczb typu Integer. Owa liczba to zmienna Value, przekazywana jako trzeci parametr. Przykadowy program prezentujcy dziaanie acuchw, zamieciem w listingu 12.2. W powyszej funkcji IntToStr zadeklarowaem tablic 256-elementow (standardowo), chocia tak naprawd a tak dua warto nie jest konieczna. Wyjtkowo w powyszej funkcji jako zwracanego rezultatu uyem typu String. Zrobiem to tylko po 483 | S t r o n a

to, aby upodobni budow funkcji do rzeczywistego wygldu funkcji IntToStr z moduu SysUtils. Listing 12.2. Peny kod rdowy programu { Copyright (c) 2002 by Adam Boduch } uses Windows; function IntToStr(Value : Integer) : String; var Buffer : array[0..255] of char; // bufor, w ktrym przechowywa bdziemy dane begin wvsprintf(Buffer, '%d', @Value); // tu nastpuje funkcja konwersji Result := Buffer; // zwracamy rezultat end; begin MessageBox(0, PChar('Witaj w ' + IntToStr(12) + ' czci ksiki!'), 'Witaj!', MB_OK); // wywietl warto zmiennej end.

Inny przykad wykorzystania funkcji wvsprintf:

Buffer : array[0..255] of char; Format : packed record // deklaracja rekordu danych do konwersji Int : Integer; Fl : String; end; begin { wypenienie danych do konwersji } Format.Int := 11; Format.Fl := 'Adam Boduch'; wvsprintf(Buffer, 'Witaj w %d czci kursu, ja nazywam si %s!', @Format); MessageBox(0, Buffer, '', 0); end.

W tym wypadku w drugim parametrze w acuchu znajduj si dwa znaki ? %s i %d. Zostan one po konwersji zastpione danymi w postaci liczby Integer oraz acucha String. 484 | S t r o n a

Funkcje operujce na acuchach


Poniej przedstawiam kilka funkcji WinAPI operujcych na acuchach. Mog one okaza si przydatne podczas pisania aplikacji w API.

CharLower, CharUpper

function CharLower(lpsz: PChar): PChar; stdcall; function CharUpper(lpsz: PChar): PChar; stdcall;

Obie funkcje powoduj zamian znakw odpowiednio na mae lub wielkie litery. Pierwsza z nich (CharLower) zamienia litery z wielkich na mae, a CharUpper ? z maych na wielkie. program main; uses Windows;

begin MessageBox(0, CharLower('TO JEST PROGRAM W WINAPI'), '', MB_OK); MessageBox(0, CharUpper('to jest program w winapi'), '', MB_OK); end.

Warto si zainteresowa take funkcj CharLowerBuff i CharUpperBuff. Obie take powoduj zamian znakw, lecz posiadaj take dodatkowy parametr, ktry okrela liczb znakw, ktre maj zosta zamienione.

lstrlen

function lstrlen(lpString: PChar): Integer; stdcall;

Funkcja lstrlen podaje dugo acucha okrelonego w parametrze lpString. Dugo podawana jest w znakach. Writeln(lstrlen('Adam')); 485 | S t r o n a

Powysza instrukcja wywietli na ekranie liczb 4.

lstrcpyn

function lstrcpyn(lpString1, lpString2: PChar; iMaxLength: Integer): PChar; stdcall;

Funkcja suy do kopiowania czci acucha do drugiej zmiennej. Pierwszy parametr musi by wskazaniem acucha, do ktrego zostan skopiowane dane. Drugi parametr ? lpString2 ? to miejsce, z ktrego dane zostan pobrane. Ostatni parametr ? iMaxLength ? okrela liczb znakw do skopiowania: program main; uses Windows; {$APPTYPE CONSOLE} var P1 : array[0..50] of char; begin lstrcpyn(P1, 'Delphi jest narzdziem typu RAD', 7); Writeln(P1); Readln; end.

Powyszy kod rdowy spowoduje wywietlenie na ekranie napisu Delphi.

Tworzenie kontrolek
Zarwno tworzenie komponentw, jak i rnych kontrolek odbywa si za porednictwem funkcji CreateWindow. W przypadku komponentw nie bdzie konieczna rejestracja nowych klas itp. elementw. Do stworzenia nowego komponentu wystarczy napisanie jednego wiersza kodu. Komponent naley utworzy z flag WS_CHILD oraz WS_VISIBLE. Stworzenie przycisku bdzie wic wygldao nastpujco: 486 | S t r o n a

CreateWindow('BUTTON', 'Przycisk', WS_CHILD or WS_VISIBLE, 100, 100, 120, 25, Wnd, 0, hInstance, nil);

Jedyn charakterystyczn cech jest pierwszy parametr tej funkcji. Jeeli chcesz stworzy przycisk, musisz w to miejsce wpisa sowo BUTTON. Drugi parametr to tekst, ktry bdzie widnia na przycisku. Trzeci parametr to flagi komponentu. Dalsza cz jest ju taka sama, jak w przypadku tworzenia formularza. W tabeli 12.2 umieciem wartoci, jakie moe przyjmowa pierwszy parametr funkcji CreateWindow. Tabela 12.2. Moliwe wartoci pierwszego parametru funkcji CreateWindow Warto BUTTON Opis Przycisk ? odpowiednik komponentu TButton

COMBOBOX Lista rozwijalna ? odpowiednik TComboBox EDIT LISTBOX Kontrolka edycyjna ? jedno liniowa. Odpowiednik komponentu TEdit Kontrolka wielowierszowa. Odpowiednik TListBox

MDICLIENT Okno potomne ? MDI SCROLLBAR Pasek przewijania ? inaczej TScrollBar STATIC Etykieta tekstowa. Odpowiednik TLabel

Taka kontrolka bdzie wic ?dzieckiem? w stosunku do formularza (WS_CHILD) i bdzie take widoczna (WS_VISIBLE). Jeeli chcesz, aby kontrolka na starcie bya niewidoczna, uyj flagi WS_HIDE. Pamitaj, aby podczas tworzenia nowej kontrolki w parametrze hWndParent (czwarty od koca) poda uchwyt okna gwnego. Parametr w okrela uchwyt okna ?rodzica? ? w tym wypadku formularza.

Umieszczanie kontrolek przy starcie programu


Jeeli chcemy, aby kontrolki byy tworzone na starcie programu, kod naley umieci w funkcji okienkowej. Konieczne jest take obsuenie komunikatu WM_CREATE. Oto kod:

487 | S t r o n a

function WndProc(Wnd: HWND; uMsg: UINT; wPar: WPARAM; lPar: LPARAM): LRESULT; stdcall; begin { na pocztek zwracamy warto 0 ? meldunek jest przetwarzany } Result := 0; case uMsg of WM_CREATE: CreateWindow('BUTTON', 'Przycisk', WS_CHILD or WS_VISIBLE, 100, 100, 120, 25, Wnd, 0, hInstance, nil); WM_DESTROY: PostQuitMessage(0); else Result := DefWindowProc(Wnd, uMsg, wPar, lPar); end; end;

W przypadku zastosowania takiej funkcji okienkowej, jak przedstawiono powyej, na formularzu w punkcie 100,100 zostanie umieszczony przycisk. W listingu 12.3 znajduje si kod rdowy programu, ktrego efektem jest umieszczenie 5 przyciskw na raz. Listing 12.3. Umieszczanie kilku przyciskw { Copyright (c) 2002 by Adam Boduch } program ChildApp; uses Windows, Messages; function IntToStr(Value : Integer) : String; var Buffer : array[0..255] of char; // bufor, w ktrym przechowywa bdziemy dane begin wvsprintf(Buffer, '%d', @Value); // tu nastpuje funkcja konwersji Result := Buffer; // zwracamy rezultat end; function WndProc(Wnd: HWND; uMsg: UINT; wPar: WPARAM; lPar: LPARAM): LRESULT; stdcall; var i : Integer; begin { na pocztek zwracamy warto 0 ? meldunek jest przetwarzany } Result := 0; case uMsg of 488 | S t r o n a

WM_CREATE: begin for I := 1 to 5 do CreateWindow('BUTTON', PCHar('Przycisk nr: ' + IntToStr(i)), WS_CHILD or WS_VISIBLE, 100, 100 + i * 30, 120, 25, Wnd, 0, hInstance, nil); end; WM_DESTROY: PostQuitMessage(0); else Result := DefWindowProc(Wnd, uMsg, wPar, lPar); end; end; var Wnd: TWndClass; Msg: TMsg;

// klasa okna

begin with Wnd do begin lpfnWndProc := @WndProc; // funkcja okienkowa hInstance := hInstance; lpszClassName := 'My1stApp'; // klasa hbrBackground := COLOR_WINDOW; // kolor ta end; RegisterClass(Wnd); // zarejestruj now klas CreateWindow('My1stApp', 'Pierwszy program w WinAPI', WS_VISIBLE or WS_TILEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, 0, 0, hInstance, NIL); while GetMessage(msg, 0, 0, 0) do DispatchMessage(msg); end.

W kodzie wykorzystalimy wczeniej napisan funkcj IntToStr. Umieszczenie 5 kontrolek nastpuje w ptli, dlatego te za kad iteracj naley zmienia pooenie przycisku w pionie, wykonujc takie dziaanie: 100 + I * 30; Powoduje to dodanie do liczby 100 wartoci z mnoenia ? np. 30, 60, 90 itd. Dziaanie programu prezentuje rysunek 12.1.

489 | S t r o n a

Rysunek 12.1. Dziaanie programu

Flagi kontrolek
Poszczeglne kontrolki umieszczone na formularzu mog posiada dodatkowe flagi, okrelajce zachowanie lub wygld komponentu. Owe flagi mona podawa jako trzeci parametr polecenia CreateWindow, czc je operatorem or. W tabelach 12.3 ? 12.6 przedstawiam najczciej uywane flagi. Tabela 12.3. Flagi uywane z kontrolk BUTTON Flaga BS_3STATE BS_AUTO3STATE Krtki opis Kontrolka (przycisk) stanie si komponentem ? la TCheckBox Flaga podobna do BS_3STATE, tyle e komponent moe przybiera warto ?zaznaczony? Kontrolka (przycisk) stanie si komponentem ? la TRadioButton (rysunek 12.2) Powoduje, e przycisk zostanie wywietlony z czarn, pogrubion obwdk Komponent zostanie wywietlony z obwdk (rysunek 12.3)

BS_AUTORADIOBUTTON BS_DEFPUSHBUTTON BS_GROUPBOX

490 | S t r o n a

BS_BITMAP BS_BOTTOM BS_CENTER BS_ICON BS_LEFT BS_MULTILINE BS_RIGHT BS_TOP BS_VCENTER

Umoliwia wywietlanie bitmapy na kontrolce Ustawia tekst na samym dole komponentu Centruje tekst w poziomie Umoliwia wywietlanie ikony na komponencie Tekst bdzie wyrwnany do lewej strony Flaga umoliwia wywietlanie kilku wierszy tekstu Tekst bdzie wyrwnany do prawej strony Tekst zostanie umieszczony u gry kontrolki Tekst zostanie wyrodkowany w pionie

Rysunek 12.2. Przyciski w formie komponentu TRadioButton

491 | S t r o n a

Rysunek 12.3. Przyciski w formie kontrolek TGroupBox

Tabela 12.4. Flagi uywane z kontrolk COMBOBOX Flaga Krtki opis

CBS_DISABLENOSCROLL Pasek przewijania zostanie zablokowany CBS_DROPDOWN CBS_DROPDOWNLIST CBS_LOWERCASE CBS_UPPERCASE CBS_SORT Lista rozwijalna zostanie aktywna Nie bdzie moliwe edytowanie listy rozwijalnej (zaznaczona pozycja nie bdzie moga by zmieniana) Konwertuje tekst wpisany w kontrolce na mae litery Konwertuje tekst wpisany w kontrolce na wielkie litery Automatyczne sortowanie danych wpisanych w kontrolce

Tabela 12.5. Flagi uywane z kontrolk EDIT Flaga Krtki opis

ES_AUTOHSCROLL Automatycznie przewi tekst w kontrolce w poziomie, jeeli uytkownik wpisa

492 | S t r o n a

wicej znakw ni moe by w niej wywietlone ES_CENTER ES_LEFT ES_LOWERCASE ES_MULTILINE ES_NUMBER ES_PASSWORD ES_READONLY ES_RIGHT ES_UPPERCASE ES_WANTRETURN Wyrodkuj tekst, jeeli kontrolka zawiera wiele wierszy Wyrwnaj tekst do lewej strony Konwertuj wpisany tekst na mae litery Flaga umoliwia wpisywanie w kontrolce wielu wierszy tekstu Zezwalaj na wpisywanie jedynie liczb Tekst wpisany w kontrolce zostanie zastpiony znakami * Tekst wpisany w kontrolce bdzie przeznaczony jedynie do odczytu Tekst zostanie wyrwnany do prawej strony, jeeli kontrolka zostaa stworzona z flag ES_MULTILINE Wpisane w kontrolce litery konwertuj na wielkie Dotyczy kontrolek wielowierszowych. Po zastosowaniu tej flagi nacinicie klawisza Enter przenosi kursor do kolejnego wiersza

Tabela 12.6. Flagi uywane z kontrolk LISTBOX Flaga Krtki opis

LBS_DISABLENOSCROLL Wywietla nieaktywny, pionowy pasek przewijania LBS_EXTENDEDSEL LBS_MULTICOLUMN LBS_SORT Zezwala na zaznaczenie wielu wierszy z uyciem klawisza Shift Zezwala na wywietlanie w komponencie kilku kolumn Automatyczne sortowanie kolumn

Wicej informacji na temat flag znajdziesz w pomocy WinAPI pod hasem CreateWindow.

Obsuga zdarze
493 | S t r o n a

Umiemy ju tworzy formularze i umieszcza na nich kontrolki WinAPI. Kolejnym krokiem jest obsuga zdarze (np. kliknicia obiektu). Pierwszym krokiem bdzie nadanie kontrolce jakiego unikalnego identyfikatora. CreateWindow('BUTTON', PCHar('Przycisk nr: ' + IntToStr(i)), WS_CHILD or WS_VISIBLE, 100, 100 + i * 30, 120, 25, Wnd, 100, hInstance, nil);

W tym wypadku nadalimy kontrolce identyfikator nr 100. Od tego momentu podczas kliknicia przycisku bdziemy musieli odbiera komunikat WM_COMMAND i ? zalenie od numeru ID ? odpowiednio reagowa: WM_COMMAND: if wPar = 100 then MessageBox(Wnd, 'Nacisne!', '', MB_OK);

Na takiej samej zasadzie moesz kontrolowa nacinicie wszystkich przyciskw ? wane jest tylko, aby numery ID rniy si. W powyszym przykadzie skorzystaem z instrukcji if, lecz przy wikszej liczbie instrukcji wygodniej bdzie zastosowa case. Oto zmodyfikowana procedura okienkowa z poprzedniego programu: function WndProc(Wnd: HWND; uMsg: UINT; wPar: WPARAM; lPar: LPARAM): LRESULT; stdcall; var i : Integer; begin { na pocztku zwracamy warto 0 ? meldunek jest przetwarzany } Result := 0; case uMsg of WM_CREATE: begin { w ptli umieszczamy kilka przyciskw, kademu z nich nadajc kolejny identyfikator - poczynajc od 100 } for I := 1 to 5 do CreateWindow('BUTTON', PCHar('Przycisk nr: ' + IntToStr(i)), WS_CHILD or WS_VISIBLE, 100, 100 + i * 30, 120, 25, Wnd, 100 + i, hInstance, nil); end; WM_COMMAND: // obsuga kliknicia przycisku begin case wPar of // sprawd, czy w Par jest od 101 do 105 101..105: MessageBox(Wnd, PChar('Witaj!, nacisne przycisk nr ' + IntToStr(wPar ? 100) + '!'), ':?)', 494 | S t r o n a

MB_OK + MB_ICONINFORMATION); end; end; WM_DESTROY: PostQuitMessage(0); else Result := DefWindowProc(Wnd, uMsg, wPar, lPar); end; end;

Jak wida, za kad iteracj ptli nowa kontrolka zostaje utworzona ze zmienionym numerem ID. Po uruchomieniu aplikacji i naciniciu przez uytkownika przycisku do programu zostaje wysany komunikat WM_COMMAND z parametrem wPar, ktry zawiera numer ID przycisku. Na tej podstawie moemy odpowiednio zareagowa ? w tym wypadku poprzez wywietlenie komunikatu. Peen kod rdowy powyszego programu moesz znale na pycie CD-ROM w katalogu ..listingi/12/Wm_Command.

Uchwyty do kontrolek
Po prawidowym utworzeniu kontrolki funkcja CreateWindow zwraca jej uchwyt w postaci typu HWND. Np.: Edit := CreateWindow('EDIT', '', WS_CHILD or WS_VISIBLE or WS_BORDER, 10, 10, 100, 25, Wnd, 0, hInstance, nil);

Teraz majc uchwyt takiej kontrolki, moemy wysya do niej komunikaty. Przykadowo chcc pobra tekst z kontrolki EDIT, musimy skorzysta z funkcji GetWindowText: GetWindowText(Edit, Buffer, SizeOf(Buffer)); // pobierz tekst z edita

Pierwszym parametrem tej funkcji musi by uchwyt kontrolki, z ktrej chcemy pobra tekst. Drugi parametr ? Buffer ? to np. acuch o takiej postaci: var Buffer : array[0..128] of char;

Peny kod programu znajduje si w listingu 12.4.

495 | S t r o n a

Listing 12.4. Peny kod programu { Copyright (c) 2002 by Adam Boduch } program PMsg; uses Windows, Messages; var Edit : THandle; function WndProc(Wnd: HWND; uMsg: UINT; wPar: WPARAM; lPar: LPARAM): LRESULT; stdcall; var Buffer : array[0..128] of char; begin { na pocztku zwracamy warto 0 ? meldunek jest przetwarzany } Result := 0; case uMsg of WM_CREATE: begin Edit := CreateWindow('EDIT', '', WS_CHILD or WS_VISIBLE or WS_BORDER, 10, 10, 100, 25, Wnd, 0, hInstance, nil); CreateWindow('BUTTON', 'OK', WS_CHILD or WS_VISIBLE, 150, 10, 120, 25, Wnd, 101, hInstance, nil); end; WM_COMMAND: if wPar = 101 then begin GetWindowText(Edit, Buffer, SizeOf(Buffer)); // pobierz tekst z parametru Edit MessageBox(Wnd, Buffer, 'EDIT', MB_OK); // wywietl w okienku SendMessage(Wnd, WM_SETTEXT, 0, Longint(@Buffer)); // ustaw now warto Caption end; WM_DESTROY: PostQuitMessage(0); else Result := DefWindowProc(Wnd, uMsg, wPar, lPar); end; end; var Wnd: TWndClass;

// klasa okna 496 | S t r o n a

Msg: TMsg; begin with Wnd do begin lpfnWndProc := @WndProc; // funkcja okienkowa hInstance := hInstance; lpszClassName := 'My1stApp'; // klasa hbrBackground := COLOR_WINDOW; // kolor ta end; RegisterClass(Wnd); // zarejestruj now klas CreateWindow('My1stApp', 'Server App', WS_VISIBLE or WS_TILEDWINDOW, 300, 300, 300, 70, 0, 0, hInstance, NIL); while GetMessage(msg, 0, 0, 0) do begin TranslateMessage(msg); DispatchMessage(msg); end; end.

W pierwszej kolejnoci po naciniciu przycisku pobierana zostaje warto wpisana w kontrolce EDIT. Teraz wystarczy ju tylko wywietli zawarto zmiennej Buffer. Nastpnie program ustawia now warto dla okna formularza (mona powiedzie, e to jest waciwo Caption): SendMessage(Wnd, WM_SETTEXT, 0, Longint(@Buffer));

Chciaem przy okazji zaprezentowa sposb wysyania komunikatw do kontrolek. W tym celu do okna naley przekaza komunikat WM_SETTEXT. Drugi parametr natomiast musi by wskazaniem tekstu, ktry ma zosta umieszczony w oknie.

Tworzenie bardziej zaawansowanych kontrolek


Aby moliwe byo tworzenie bardziej zaawansowanych kontrolek (takich, jak komponenty typu TProgressBar czy TListView), naley do listy uses doda modu CommCtrl. Operowa tymi 497 | S t r o n a

komponentami moemy tylko poprzez komunikaty. Ich spis moesz znale w pliku CommCtrl.pas. Znaczenie poszczeglnych komunikatw jest bardzo intuicyjne. C bowiem wykonuje komunikat PBM_SETPOST? Mona si domyle, e ustawia now pozycj w komponencie. Jeeli uruchomisz program z uyciem biblioteki CommCtrl, a na ekranie nadal widnie bdzie ?czysty? formularz (tzn. komponent nie zostanie utworzony), to wwczas konieczne bdzie wywoanie procedury InitCommonControls. Procedura ta inicjuje odpowiedni bibliotek DLL. Najlepiej t procedur wywoa tu po utworzeniu nowej klasy w sekcji begin..end. Przykadowo ? utworzenie nowej kontrolki ? la TProgressBar wyglda nastpujco: CreateWindow('msctls_progress32', '', WS_CHILD or WS_VISIBLE, 100, 10, 350, 20, Wnd, 0, hInstance, nil);

Decydujce znaczenie ma tutaj parametr msctls_progress32. Spis wszystkich parametrw kluczowych dla utworzenia komponentu moesz znale w pliku CommCtrl.pas. Po utworzeniu komponentu mona wysya do niego komunikaty ? np. dotyczce zmiany pozycji: for i := 0 to 100 do begin Sleep(50); SendMessage(ProgressBar, PBM_SETPOS, i, 0); end;

Jest to zatem ptla od jednego do stu z przerwami pomidzy kolejnymi iteracjami, wynoszcymi 50 milisekund. Podczas kadorazowego wykonania ptli do komponentu jest wysyany komunikat PBM_SETPOS. Parametr lParam funkcji SendMessage zawiera now warto (pozycj) paska postpu. Cay ten kod umiecimy w programie obsugi komunikatu WM_PAINT (listing 12.5.) Listing 12.5. Peny kod programu { Copyright (c) 2002 by Adam Boduch } program Ctrl; uses Windows, CommCtrl, Messages; var ProgressBar : HWND; function WndProc(Wnd: HWND; uMsg: UINT; wPar: WPARAM; lPar: LPARAM): 498 | S t r o n a

LRESULT; stdcall; var i : Integer; begin { na pocztku zwracamy warto 0 ? meldunek jest przetwarzany } Result := 0; case uMsg of WM_CREATE: begin // umie komponent ProgressBar i zwr uchwyt ProgressBar := CreateWindow('msctls_progress32', '', WS_CHILD or WS_VISIBLE, 100, 10, 350, 20, Wnd, 0, hInstance, nil); end; WM_PAINT: // obsuga komunikatu WM_PAINT begin for i := 0 to 100 do begin Sleep(50); // odczekaj 50 milisekund SendMessage(ProgressBar, PBM_SETPOS, i, 0); // wylij komunikat do komponentu end; Halt(1); // zamknij program end; WM_DESTROY: PostQuitMessage(0); else Result := DefWindowProc(Wnd, uMsg, wPar, lPar); end; end; var Wnd: TWndClass; Msg: TMsg;

// klasa okna

begin with Wnd do begin lpfnWndProc := @WndProc; // funkcja okienkowa hInstance := hInstance; lpszClassName := 'My1stApp'; // klasa hbrBackground := COLOR_WINDOW; // kolor ta hIcon := LoadIcon(0, IDI_APPLICATION); // domylna ikona hCursor := LoadCursor(0, IDC_ARROW); // domylny kursor end; RegisterClass(Wnd); // zarejestruj now klas InitCommonControls; // stwrz formularz... CreateWindow('My1stApp', 'Aplikacja z wykorzystaniem moduu 499 | S t r o n a

CommCtrl.pas', WS_VISIBLE or WS_TILEDWINDOW, 300, 300, 500, 300, 0, 0, hInstance, NIL); while GetMessage(msg, 0, 0, 0) do begin TranslateMessage(msg); DispatchMessage(msg); end; end.

Pozostae kontrolki
Nazwy pozostaych kontrolek, z jakich moesz skorzysta w swoich programach, zawarte s w pliku CommCtrl.pas. Tam rwnie moesz znale list komunikatw zwizanych z konkretnym komponentem. W tabeli 12.7 prezentuj komponenty, ktrych moesz uy w swoich programach w API, wraz z ich odpowiednikami w VCL. Tabela 12.7. Kontrolki z biblioteki CommCtrl.dll Identyfikator kontrolki ToolbarWindow32 msctls_statusbar32 msctls_trackbar32 msctls_updown32 msctls_progress32 SysListView32 SysTreeView32 ComboBoxEx32 SysTabControl32 SysAnimate32 Nazwa kontrolki Pasek narzdziowy Pasek aplikacji Pasek przewijania Pasek gra-d Pasek postpu Lista pozycji Drzewo obiektw Kontrolka Combo System zakadek Animacje systemowe Odpowiednik VCL TToolBar TStatusBar TTrackBar TUpDown TProgressBar TListView TTreeView TComboBoxEx TTabControl TAnimate

500 | S t r o n a

SysMonthCal32 SysDateTimePick32 SysIPAddress32 SysPager

Kalendarz Prezentuje dat i czas

TMonthCalendar TDateTimePicker

Podaje adres IP komputera. brak odpowiednika System stron TPageControl

Wywietlanie grafiki
We wczeniejszych rozdziaach miae okazj zapozna si z funkcjami operujcymi na grafice czy te wywietlajcymi tekst. Poznae take klas TCanvas, ktra owe zadanie znacznie upraszczaa. Funkcje API umoliwiajce rysowanie lub wywietlanie grafiki s bardzo podobne do funkcji z klasy TCanvas. Rnica polega jedynie na liczbie parametrw.

Rysowanie w WinAPI
Przypominam, e rysowanie czegokolwiek powinno odby si po wywoaniu komunikatu WM_PAINT. Wiadomo, e okno w Windows moe podlega rnym zdarzeniom, takim jak: minimalizacja, moliwo zasonicia przez inne okno itp. System nie przechowuje obrazu ekranu w pamici, lecz odpowiedzialna jest za to sama aplikacja. Po odsoniciu okna i przywrceniu go na pierwszy plan do aplikacji wysyany jest komunikat WM_PAINT ? do nas naley obsuenie tego komunikatu.

Kontekst urzdzenia graficznego


Wszystkie funkcje operujce na WinAPI wymagaj podania pierwszego parametru, ktry jest tzw. kontekstem urzdzenia (Device Context). Jest to pewna struktura danych, opisujca rne parametry rysowania ? czcionk, grubo i kolor linii itp. Takie ustawienia s rne dla kadego programu uruchomionego w systemie, a my w swoich aplikacjach bdziemy musieli pobiera uchwyt do owego kontekstu: DCHandle := GetDC(Wnd);

501 | S t r o n a

Od tej pory mamy ju uchwyt, ktry bdzie trzeba podawa przy kadej funkcji operujcej na grafice. Po zakoczeniu malowania naley ten uchwyt zwolni, korzystajc z polecenia ReleaseDC: ReleaseDC(Wnd, DCHandle);

Za pomoc funkcji GetDC uzyskujemy uchwyt, dziki ktremu moemy malowa po caym obszarze roboczym programu. Nie mamy natomiast moliwoci rysowania na pasku tytuowym okna ? do tego bdzie nam potrzebna funkcja GetWindowDC.

Obsuga WM_PAINT
Istnieje lepsza metoda dostarczania kontekstu urzdzenia ? rekord TPaintStruct. Rekord TPaintStruct dostarcza oprcz kontekstu urzdzenia take informacje, ktre mona wykorzysta podczas obsugi komunikatu WM_PAINT. Namalowanie czego na formularzu opiera si na wywoaniu metody BeginPaint oraz ? po zakoczeniu ? EndPaint: WM_PAINT: begin DC := BeginPaint(Wnd, PS); TextOut(DC, 10, 10, 'Delphi 7', Length('Delphi 7')); EndPaint(Wnd, PS); end;

Wczeniej jednak naley zadeklarowa zmienne PS oraz DC: var PS : TPaintStruct; DC : HDC; // uchwyt

Funkcja TextOut realizuje ? podobnie jak funkcja o tej samej nazwie z klasy TCanvas ? rysowanie tekstu. Pierwszym parametrem musi by uchwyt do kontekstu urzdzenia. Kolejne dwa parametry to wsprzdne rysowanego tekstu. Trzeci parametr to tekst, ktry zostanie narysowany na formularzu, a ostatni ? dugo tekstu. Rysunek 12.4 prezentuje program po uruchomieniu.

502 | S t r o n a

Rysunek 12.4. Tekst narysowany metod TextOut

Zmiana koloru ta Na rysunku 12.4 widzisz, e dotychczasowy efekt dziaania funkcji TextOut nie jest zbyt interesujcy. Napis jest wywietlany na biaym tle. Aby to zmieni, mona ustawi przezroczyste to, uywajc w tym celu funkcji SetBkMode: SetBkMode(DC, TRANSPARENT);

Pierwszy parametr musi by kontekstem urzdzenia, a drugi to flaga informujca o tym, e to bdzie przezroczyste (TRANSPARENT). W do prosty sposb mona zmieni rwnie kolor wywietlanego tekstu. Wystarczy zastosowa funkcj SetTextColor. Pierwszym parametrem tej funkcji musi by oczywicie kontekst urzdzenia. Drugi parametr musi okrela kolor tekstu w postaci RGB (Reed Green Blue), czyli kombinacji trzech kolorw: czerwonego, zielonego i niebieskiego ? np.: SetTextColor(DC, RGB(0, 100, 150));

adowanie i wywietlanie bitmapy


Zamy, e w zasobach programu umieszczona jest bitmapa o nazwie ID_BITMAP. Przy wykorzystaniu VCL zaadowanie bitmapy do komponentu TImage zajoby chwil ? wystarczyby jeden wiersz kodu: Image.Picture.Bitmap.LoadFromResourceName(hInstance, 'ID_BITMAP');

Chcc skorzysta z funkcji API, musimy powici na to nieco wicej czasu i napisa wicej kodu: WM_PAINT: begin DC := BeginPaint(Wnd, PS); Bitmap := LoadBitmap(hInstance, 'ID_BITMAP'); _Bitmap := CreateCompatibleDC(DC); SelectObject(_Bitmap, Bitmap); BitBlt(dc, 10, 10, 14, 14, _Bitmap, 0, 0, SRCCOPY); DeleteDC(_Bitmap);

503 | S t r o n a

EndPaint(Wnd, PS); end;

Samo zaadowanie jest proste, gdy uywamy tutaj funkcji LoadBitmap, ktra zwraca bitmap w postaci zmiennej HBITMAP (odpowiednik klasy TBitmap). Nastpnym krokiem jest stworzenie pamiciowego kontekstu urzdzenia (CreateCompatibleDC), na ktrym zostanie narysowana bitmapa. Kolejny krok to wybranie waciwego kontekstu za pomoc funkcji SelectObject. Wreszcie samo narysowanie bitmapy nastpuje poprzez funkcj BitBlt. Pierwszy parametr owej funkcji to uchwyt kontekstu, na ktrym zostanie narysowana bitmapa. Kolejne dwa parametry to X i Y, wsprzdne miejsca wywietlania obrazka. Rozmiar rysowanej bitmapy okrelaj dwa kolejne parametry. Jeszcze inne dwa parametry okrelaj pozycj X i Y lewego grnego rogu obrazka oraz stopie jego wywietlania. Ostatni parametr okrela sposb przedstawienia bitmapy ? w tym wypadku kopiowanie ze rda do miejsca przeznaczenia. Najlepszym sposobem sprawdzenia dziaania owych parametrw jest przetestowanie ich w praktyce. Nim skompilujesz swj program, zadeklaruj w nim nastpujce zmienne: var Bitmap : HBITMAP; _Bitmap : HDC;

adowanie zasobw
W rozdziale 10. bya mowa o wykorzystaniu zasobw do przechowywania w pliku wykonywalnym rnych danych ? poczwszy od grafiki, a na innych plikach wykonywalnych skoczywszy. W tym podpunkcie poka, w jaki sposb skorzysta z tych zasobw, nie uywajc przy tym klasy TResourceStream. Korzystajc z zasobw, mona w WinAPI napisa swj wasny instalator, ktry bdzie ?przechowywa? w sobie pliki instalacyjne. Taki przykadowy instalator moesz znale na pycie CDROM w katalogu ../listingi/12/Install. W katalogu z projektem znajdziesz rwnie skompilowan wersj projektu ? plik Install.exe (rysunek 12.5). Po uruchomieniu programu instalacyjnego na dysku zostanie zainstalowany program, ktry kiedy napisaem (lecz to jest w tej chwili nie istotne).

504 | S t r o n a

Rysunek 12.5. Instalator wykonany w WinAPI

Skompilowane zasoby
Tworzenie zasobw za pomoc programu brcc32.exe wyglda tak samo, jak to przedstawiaem w rozdziale 10. ? np. w przypadku mojego instalatora plik files.rc wyglda tak: MAILBOXES RCDATA "Mailboxes.exe" SETUP RCDATA "setup.dll" MAILCNT RCDATA "Mailboxes.cnt" MAILHLP RCDATA "Mailboxes.hlp" README RCDATA "Readme.html" SAMPLE RCDATA "sample.txt" DEFAULT RCDATA "default.mbx" UNINSTALL RCDATA "Odinstaluj.exe"

Tak skonstruowany plik *.rc pozwoli na wczenie do gotowego zasobu (*.res) powyej przedstawionych plikw. W rozdziale 10. nie wspomniaem o jednej kwestii ? mianowicie o moliwoci tworzenia bardziej zaawansowanych zasobw, np. formularza: LICENCJA DIALOGEX 42, 8, 271, 133 STYLE DS_MODALFRAME | DS_CENTER | DS_3DLOOK | DS_SETFOREGROUND | WS_POPUP | 505 | S t r o n a

WS_VISIBLE | WS_CAPTION | WS_SYSMENU EXSTYLE WS_EX_CLIENTEDGE CAPTION "Instalacja programu" FONT 8, "MS Sans Serif" BEGIN ICON "a", a, 11, 9, 21, 20 LTEXT "Najnowsz wersj programu MailBoxes moesz zawsze znale na stronie www.4programmers.net", a, 37, 11, 221, 20 DEFPUSHBUTTON "Instaluj", 102, 101, 100, 101, 14 PUSHBUTTON "Wyjcie", 103, 208, 100, 47, 14 GROUPBOX "Gdzie zainstalowa program?", a, 10, 59, 247, 32 EDITTEXT 106, 16, 72, 233, 14, ES_AUTOHSCROLL CONTROL "BAR", BAR, "msctls_progress32", 0x0 | WS_CLIPSIBLINGS, 14, 42, 241, 11 END

Pisanie tego rcznie raczej nie ma sensu ? ja uywaem pakietu Microsoft Visual Studio, ktry oferuje tworzenie zasobw. Ty jednak moesz skorzysta z darmowych narzdzi dostpnych w Internecie.

Wykorzystanie zasobw
Aeby skorzysta z naszych zasobw, bdziemy zmuszeni zastosowa funkcje WinAPI, ktrych do tej pory nie uywalimy ? np. LoadResource i FindRecource. Najpierw jednak naley zaj si przedstawieniem formularza instalacyjnego.

Wywietlenie formularza Za wywietlenie formularza znajdujcego si w zasobach odpowiada funkcja DialogBox: function DialogBox(hInstance: HINST; lpTemplate: PChar; hWndParent: HWND; lpDialogFunc: TFNDlgProc): Integer;

Pierwszy parametr musi by wskazaniem moduu, w ktrym znajduj si zasoby ? my w tym miejscu podajemy warto hInstance. Drugi parametr musi by nazw szablonu, ktry chcemy wywietli. Parametr trzeci (hWndParent) stanowi uchwyt do okna macierzystego; jako e nasze okno bdzie oknem macierzystym, wpisujemy w tym miejscu cyfr 0. Ostatni parametr to wskazanie procedury, ktra bdzie obsugiwa zdarzenia naszego formularza: DialogBox(hInstance, 'LICENCJA', 0, @DlgWindowProc); 506 | S t r o n a

Funkcja DlgWindowProc, ktr podaem w ostatnim parametrze, jest zwyk funkcj okienkow ? musi odpowiada na przychodzce do aplikacji komunikaty. Zamknicie okna (zakoczenie dziaania aplikacji) zrealizujemy za pomoc polecenia EndDialog: EndDialog(wnd,0);

Ustawianie wartoci komponentw formularza Mimo e formularz wykorzystany w programie jest jedynie skryptem, posiada on take komponenty. Podczas ustawiania tych komponentw przydzieliem kademu z nich osobny identyfikator (ID), do ktrego bdziemy si odwoywa podczas wykonywania funkcji: SetDlgItemText(wnd, 106, 'C:\Mailboxes 1.53');

Przykadowo funkcja SetDlgItemText powoduje ustawienie nowej wartoci kontrolki. W pierwszym parametrze wpisujemy uchwyt okna, a w drugim ID kontrolki. Parametr ostatni to tekst, ktry ma zosta umieszczony w obiekcie.

LockResource, LoadResource, FindResource


Zaadowanie zasobw (tekstu licencji) do kontrolki moe si wyda troch skomplikowane, gdy trzeba si posuy a trzema funkcjami naraz: LockResource, LoadResource i FindResource. SetDlgItemText(wnd, 101, LockResource(LoadResource(hinstance,FindResource(hinstance,'LICENCJA ','TXT'))));

Pierwsza funkcja ? LockResource ? zwraca wskanik do pierwszego bajtu zasobw, jednak wymaga podania uchwytu do zasobw. Ten globalny uchwyt jest natomiast zwracany przez funkcj LoadResource, ktra w drugim parametrze wymaga podania wskazania adowanego zasobu. Tutaj pomoe nam funkcja FindResource, w ktrej parametrach wystarczy poda typ oraz nazw zasobu.

507 | S t r o n a

Zapisywanie plikw na dysku


Chcc zastpi klas TResourceStream, jestemy zmuszeni skorzysta z funkcji LoadResource oraz FindResource. Najpierw w swoim instalatorze zadeklaruj tablic plikw, ktre s instalowane: { oto elementy umieszczone w zasobach } const Tablica : array[0..7] of _DATA = ((Files: 'Mailboxes.exe'; FName: 'MAILBOXES'), (Files: 'setup.dll'; FName: 'SETUP'), (Files: 'Mailboxes.cnt'; FName: 'MAILCNT'), (Files: 'Mailboxes.hlp'; FName: 'MAILHLP'), (Files: 'Readme.html'; FName: 'README'), (Files: 'Sample.txt'; FName: 'SAMPLE'), (Files: 'default.mbx'; FName: 'DEFAULT'), (Files: 'Odinstaluj.exe'; FName: 'UNINSTALL'));

Pierwszy element tej tablicy to nazwa pliku, ktry ma zosta utworzony na dysku; drugi to wskazanie nazwy zasobu. Instalacja (wyodrbnianie) poszczeglnych elementw moe wyglda tak: For I:= Low(Tablica) to High(Tablica) do begin AssignFile(Ouff, PC + Tablica[i].Files); // stwrz plik... Rewrite(Ouff, 1); { odnajd w zasobach zasb i przypisz go zmiennej Fres } Fres := FindResource(hInstance, Tablica[i].FName, RT_RCDATA); { do pliku zapisz dane z wycignitych zasobw } BlockWrite(Ouff, LockResource(LoadResource(hInstance, Fres))^, SizeofResource(hinstance, Fres)); Closefile(ouff); // zamknij plik end;

Posuyem si tutaj funkcjami operujcymi na plikach, ktre szczegowo omwiem w rozdziale 7. Po zlokalizowaniu konkretnego zasobu (FindResource) nastpuje jego zapisanie do pliku (BlockWrite). Peny kod rdowy znajduje si w listingu 12.6. Listing 12.6. Kod rdowy instalatora { Copyright (c) 2001 by Adam Boduch [http://programowanie.of.pl] } program setup; uses Windows, Messages; 508 | S t r o n a

{$R FILES.RES} // <--- pliki, ktre zostan zainstalowane {$R RESOURCE.RES} // <---- bitmapa ( dodatkowe pliki ) type { rekord zawiera dwa elementy. Pierwszym jest nazwa pliku umieszczonego w zasobach ? np. SFP, a drugim elementem jest nazwa pliku, ktry zostanie zapisany na dysku ? np: sfp.jpg } _DATA = packed record Files: String; FName: PChar; end;

{ oto elementy umieszczone w zasobach } const Tablica : array[0..7] of _DATA = ((Files: 'Mailboxes.exe'; FName: 'MAILBOXES'), (Files: 'setup.dll'; FName: 'SETUP'), (Files: 'Mailboxes.cnt'; FName: 'MAILCNT'), (Files: 'Mailboxes.hlp'; FName: 'MAILHLP'), (Files: 'Readme.html'; FName: 'README'), (Files: 'Sample.txt'; FName: 'SAMPLE'), (Files: 'default.mbx'; FName: 'DEFAULT'), (Files: 'Odinstaluj.exe'; FName: 'UNINSTALL'));

var KeyHandle : HKEY; Uninstall : PChar; const Name : PChar = 'Mailboxes v. 1.5.3';

(******************************************************************* ******)

function DlgWindowProc (Wnd: hWnd; Msg: UINT; DlgWParam: WPARAM; DlgLParam: LPARAM): boolean; stdcall; var Fres: Integer; Ouff: File; Buff: array[0..254] of char; PC : String; I : Integer; 509 | S t r o n a

label Next; begin result := true; case Msg of WM_INITDIALOG: begin SetDlgItemText(wnd, 101, LockResource(LoadResource(hinstance,FindResource(hinstance,'LICENCJA ','TXT')))); SetDlgItemText(wnd, 106, 'C:\Mailboxes 1.53'); end; WM_CLOSE: EndDialog(wnd,0); wm_activate: SendDlgItemMessage(wnd, 101, EM_SETSEL, ?1, 0); WM_COMMAND: begin if LOWORD(DlgWParam) = 103 then EndDialog(wnd,0); if LOWORD(DlgWParam) = 102 then begin { pobranie cieki, w ktrej ma zosta zainstalowany program } GetDlgItemText(wnd, 106, Buff, SizeOf(Buff)); PC := Buff; { sprawdzenie, czy na kocu znajduje si znak \ } if PC[Length(PC)] <> '\' then PC := PC + '\' else PC := PC; Uninstall := PChar(PC + 'Odinstaluj.exe'); { otwarcie rejestru i klucza } RegOpenKeyEx(HKEY_LOCAL_MACHINE, PChar('Software\Microsoft\Windows\CurrentVersion\Uninstall'), 0, KEY_ALL_ACCESS, KeyHandle); { stworzenie nowej wartoci } RegCreateKey(KeyHandle, 'Mailboxes', KeyHandle); RegSetValueEx(KeyHandle, 'DisplayName', 0, REG_SZ, Name, SizeOf(Name)); RegSetValueEx(KeyHandle, 'UninstallString', 0, REG_SZ, Uninstall, SizeOf(Uninstall));

{ sprawdzenie, czy uytkownik nie wpisa tylko litery partycji ? jeeli nie, naley utworzy dodatkowo katalog } if Length(PC) <= 3 then goto Next else CreateDirectory(PChar(PC), nil); Next:

{ instaluj poszczeglne elementy } 510 | S t r o n a

For I:= Low(Tablica) to High(Tablica) do begin AssignFile(Ouff, PC + Tablica[i].Files); plik...

// stworz

Rewrite(Ouff, 1); odnajd w zasobach zasb i przypisz go do zmiennej Fres } Fres := FindResource(hInstance, Tablica[i].FName, RT_RCDATA); { do pliku zapisz dane z "wycignitych" zasobw } BlockWrite(Ouff, LockResource(LoadResource(hInstance, Fres))^, SizeofResource(hinstance, Fres)); Closefile(ouff); // zamknij plik end; MessageBox(0, 'Instalacja przebiega pomylnie!', ':)', MB_OK + MB_ICONINFORMATION); PostQuitMessage(0); // zakocz program... end; end; else result:=false; end; end; {

begin if MessageBox(0, 'Za chwil odbdzie si instalacja programu Mailboxes v. 1.53. Czy chcesz kontynuowa?', 'Mailboxes ? instalacja', MB_YESNO + MB_ICONINFORMATION) = ID_Yes then begin DialogBox(hInstance, 'LICENCJA', 0, @DlgWindowProc); end; end.

Podsumowanie
Programowanie w czystym WinAPI moe by niewygodne, a nawet do trudne, ale nie da si ukry, e zapewnia wiksz kontrol nad programem i daje wiksze moliwoci, ktrych niekiedy brak w bibliotece VCL. Zaczniki: 511 | S t r o n a

Listingi_12.zip (599.11 kB)

Rozdzia 13

Edytuj Historia Przenie Obserwuj

COM i ActiveX
W tym rozdziale zajmiemy si do specyficznym aspektem programowania, stworzonym przez firm Microsoft i nadal z ni kojarzonym ? projektowaniem kontrolek COM i ActiveX. Dowiesz si, do czego su kontrolki ActiveX, jak je tworzy i jak ich uywa.

Spis treci 1 Czym jest COM? 1.1 Kontrolka w rozumieniu COM 1.2 Odrobin historii 2 Tworzenie obiektw COM 3 Metody i waciwoci 3.1 Dodawanie metod 3.1.1 Parametry nowej metody 3.1.2 Warto zwrotna funkcji 3.2 Dodawanie waciwoci 4 Kod rdowy kontrolki 5 Budowa i rejestracja kontrolki 6 Wykorzystanie obiektu COM 7 Interfejsy 7.1 GUID 8 ActiveX 9 Import kontrolek ActiveX 9.1 Wykorzystanie kontrolki TVText 10 Tworzenie kontrolek ActiveX 11 Przykad: wywietlanie napisw do filmu 11.1 Tworzenie interfejsu COM 11.2 Tworzenie kontrolki ActiveX 512 | S t r o n a

11.2.1 Strona wizualna 11.2.2 Kod rdowy kontrolki 11.3 Budowa i rejestracja 12 ActiveX w Internecie 12.1 Wzgldy bezpieczestwa 12.2 Przykadowa kontrolka 12.2.1 Tworzenie kontrolki ActiveX 12.2.2 Publikowanie kontrolki 13 Podsumowanie

Czym jest COM?


Rozwiniciem angielskiego skrtu COM jest nazwa Component Object Model (obiektowy model komponentw). Jest to specyfikacja firmy Microsoft, ktra w zaoeniu dotyczy tworzenia obiektw wielokrotnego uytku, niezalenie od jzyka programowania. Aby zrozumie ActiveX, musisz pozna COM ? postaram si zwile Ci to wytumaczy. Ot firma Microsoft stworzya model obiektw, ktre wykorzystywane mog by w kadym rodowisku programistycznym Win32. Wynikiem powstania obiektu COM jest kontrolka ? plik z rozszerzeniem .ocx. Kontrolka taka moe by wykorzystana zarwno w Delphi, jak i Visual C++, C++ Builder czy Visual Basic. Na razie obiekty COM s jedynie obiektami dziaajcymi w rnych rodowiskach Windows ? niemoliwe jest wykorzystanie ich poza tym systemem.

Kontrolka w rozumieniu COM


W tym rozdziale bd uywa sowa ?kontrolka? w znaczeniu obiektu COM. Do tej pory to sowo kojarzyo Ci si zapewne z komponentem Delphi. Mona powiedzie, e obiekty COM s takim uniwersalnym komponentem, podobnym do biblioteki DLL. Raz utworzona kontrolka moe by wykorzystywana wiele razy, przez wielu programistw oraz w rnych rodowiskach programowania. Jeeli kto ju napisa kontrolk speniajc dan funkcj, to po co wywaa otwarte drzwi i tworzy jeszcze raz to samo? Przykadem moe by przegldarka WWW. Napisanie programu analizujcego kod HTML jest niezwykle czasochonnym i mudnym zadaniem. Niekiedy jednak w naszym programie konieczne staje si wywietlenie jakiego dokumentu w formie strony WWW. Dziki technologii COM i ActiveX (o ActiveX powiemy nieco pniej) moemy zaimportowa udostpnione przez twrcw 513 | S t r o n a

przegldarki obiekty COM i wykorzysta je w programie jako dodatkowy komponent.

Odrobin historii
COM jest technologi stosunkowo now, bo powsta kilka lat temu. Wprowadzenie jej miao na celu stworzenie jednolitego standardu komunikacji, tak aby np. (by jeszcze raz posuy si powyszym przykadem) programici mogli korzysta z moliwoci przegldania stron WWW w swoich aplikacjach. Firma Microsoft wysza naprzeciw temu zadaniu i utworzya modu obiektw (COM), ktry umoliwia udostpnianie innym aplikacjom swoich metod. W przypadku Delphi zaimportowana kontrolka COM staje si jakby komponentem, umieszczonym na palecie komponentw. Wwczas w do prosty sposb mona skorzysta z zalet takiego obiektu, wywoujc zawarte w nim metody.

Tworzenie obiektw COM


1. Z menu File wybierz New/New/Other. Pojawi si Repozytorium. Zaznacz zakadk ActiveX (rysunek 13.1).

Rysunek 13.1. Zakadka ActiveX Repozytoriumv 2. Zaznacz w tym oknie ikon ActiveX Library i nacinij OK. W tym momencie zostanie utworzony pusty projekt. 514 | S t r o n a

3. Z menu File wybierz polecenie Save. Wska miejsce, gdzie Delphi ma zapisa plik. Utworzylimy wanie pusty projekt, ale na razie do niczego nam to nie suy. Utworzenie waciwego obiektu COM te jest proste ? polega na wybraniu ikony COM Object. Ponownie wybierz polecenie File/New/Other, a w zakadce ActiveX tym razem wybierz pozycj COM Object. Delphi wywietli okno kreatora obiektw COM, widoczne na rysunku 13.2.

Rysunek 13.2. Kreator obiektw COM W polu Class Name (nazwa kontrolki) wpisz XorCode. Pole Description suy do wstawienia krtkiego opisu obiektu. Moesz wpisa np. Kodowanie metod XOR. Klikajc przycisk OK, zamknij okno. Obiekt COM zosta utworzony. Z menu File wybierz Save All i wpisz nazw moduu. Na pierwszym planie znajduje si okno edytora biblioteki typu (rysunek 13.3). Za pomoc tego edytora sterujemy obiektem COM. Wszystkie zmiany dokonane w tym edytorze znajd odzwierciedlenie w module, lecz tym zajmiemy si nieco pniej.

515 | S t r o n a

Rysunek 13.3. Okno edytora biblioteki typu

Metody i waciwoci
Tak, jak zwyky komponent posiada rne metody i waciwoci, za pomoc ktrych moemy ?sterowa? ich zachowaniem, tak metody s obecne rwnie w kontrolce COM.

Dodawanie metod
Dodawanie metod oraz waciwoci jest czynnoci w miar prost. Zadanie opiera si na umiejtnym wykorzystaniu edytora (rysunek 13.4). W gazi po lewej znajduje si pozycja IXorCode ? kliknij j prawym klawiszem myszy i z menu wybierz New/Method.

516 | S t r o n a

Rysunek 13.4. Dodawanie nowej metody Po wybraniu pozycji Method bdziesz musia poda nazw naszej metody ? wpisz sowo Code, co spowoduje pojawienie si nowej gazi. Operowanie na nowej metodzie odbywa si poprzez zakadki, ktre pojawi si po prawej stronie (rysunek 13.5).

Rysunek 13.5. Zakadki suce do zmiany opcji metody

517 | S t r o n a

Parametry nowej metody Ustalenia parametrw naszej funkcji oraz typu wartoci przez ni zwracanej mona dokona w zakadce Parameters. Nasza kontrolka bdzie suya do kodowania tekstu metod XOR (metod t opisz nieco pniej) ? a zatem bdzie nam potrzebna tylko jedna metoda, ktra nie posiada parametrw. Warto jednak wiedzie, jak si tworzy parametry. Parametry zaznaczonej metody prezentowane s w zakadce Parameters w ramce Parameters (rysunek 13.6).

Rysunek 13.6. Ustawianie parametru dla metody W pierwszej kolumnie naley wpisa nazw parametru, a w kolejnej jego typ; natomiast w trzeciej moemy okreli, czy parametr ma by wejciowy czy te wyjciowy. Klikajc przycisk Add, moemy doda kolejny parametr.

Warto zwrotna funkcji W naszym przykadzie nie interesuj nas adne parametry funkcji, gdy domylnie nie s nam potrzebne. Jedyna wana kwestia to warto zwrotna funkcji, ktr moemy ustawi za pomoc listy rozwijalnej Return Type. Rozwi ow list i wybierz lpstr, co oznacza warto typu PChar. Zapisz projekt, aby zmiany dokonane w edytorze zostay odzwierciedlone w kodzie. System Windows by pisany w jzyku C, std na licie rozwijalnej nie mona dostrzec adnych typw charakterystycznych dla Object Pascala. W jzyku C nie ma takiego typu jak PChar ? mona 518 | S t r o n a

korzysta jedynie z lpstr.

Dodawanie waciwoci
W naszej przykadowej kontrolce skorzystamy przy pisaniu procedury kodujcej z dwch zmiennych (waciwoci) ? kodowanego acucha oraz hasa. Musimy mie wic dwie waciwoci. Kliknij prawym przyciskiem myszy pozycj IXorCode i (podobnie jak w przypadku tworzenia waciwoci) wybierz waciwo New/Properties. Stwrz w ten sposb dwie waciwoci ? lpString oraz lpPassword. Nie przejmuj si, e zostay stworzone tym samym po dwie gazie dla kadej waciwoci. Domylnie zakada si bowiem, e jedna z nich potrzebna jest do zapisywania danych do waciwoci, a druga do ich odczytu. My jednak potrzebujemy waciwoci ?tylko do zapisu?, wic jedn moemy bez obaw usun. Po zaznaczeniu konkretnej pozycji na zakadce Attributes znajdzie si pole Invoke Kind, a w nim warto Property Put albo Property Get. Moesz usun klucze, w ktrych jest Property Get, klikajc prawym przyciskiem myszy element, ktry chcesz skasowa, a nastpnie wybierajc polecenie Delete. Kolejnym krokiem jest ustawienie odpowiednich wartoci. 1. 2. 3. 4. Zaznacz pozycj lpString. Kliknij zakadk Parameters. Odszukaj pozycj Return Type i z listy wybierz void. Nastpnie ustaw parametr ? nadaj mu typ LPSTR.

To samo zrb z kluczem lpPassword. Dziki temu w kontrolce mamy dwie waciwoci, ktre suy bd tylko do zapisu. Napisaem ?waciwoci?, ale w rzeczywistoci w kodzie programu widniej dwie procedury: procedure TXorCode.Set_lpString(Value: PChar); begin end; procedure TXorCode.Set_lpPassword(Value: PChar); begin end;

C, taka jest zasada tworzenia kontrolek COM. Uytkownik wykorzystujcy ow kontrolk, chcc nada waciwoci now warto, w rzeczywistoci wywouje np. procedur Set_lpPassword, przekazujc warto w parametrze Value.

519 | S t r o n a

Kod rdowy kontrolki


Zmian dokonujemy za porednictwem edytora, ktry w tle generuje kod rdowy dla kontrolki. Oprcz standardowego kodu rdowego kontrolki Delphi tworzy take plik z tzw. interfejsami. Plik taki mona atwo rozpozna po kocwce _TLB, dodanej do nazwy pliku. Zajrzyj do katalogu, w ktrym zapisae projekt ? na pewno znajduje si tam plik XorCode_TLB.pas lub inny (jeeli nadae inn nazw). W listingu 13.1 zaprezentowaem kod tego pliku. Listing 13.1. Kod rdowy biblioteki unit XorCode_TLB; // ******************************************************************** **** // // WARNING // ------// The types declared in this file were generated from data read from a // Type Library. If this type library is explicitly or indirectly (via // another type library referring to this type library) re-imported, or the // 'Refresh' command of the Type Library Editor activated while editing the // Type Library, the contents of this file will be regenerated and all // manual modifications will be lost. // ******************************************************************** **** // // PASTLWTR : 1.2 // File generated on 03-02-04 15:22:45 from Type Library described below. // ******************************************************************** **** // // Type Lib: C:\Moje dokumenty\Pliki textowe\delphi_kompendium_programisty\listingi\13\XorCode\XorCode.tl b (1) // LIBID: {356B1880-384C-11D7-A2DC-00E04CE92EC6} 520 | S t r o n a

// LCID: 0 // Helpfile: // HelpString: XorCode Library // DepndLst: // (1) v2.0 stdole, (C:\WINDOWS\SYSTEM\STDOLE2.TLB) // ******************************************************************** **** // {$TYPEDADDRESS OFF} // Unit must be compiled without type-checked pointers. {$WARN SYMBOL_PLATFORM OFF} {$WRITEABLECONST ON} {$VARPROPSETTER ON} interface uses Windows, ActiveX, Classes, Graphics, StdVCL, Variants;

// ******************************************************************** *// // GUIDS declared in the TypeLibrary. Following prefixes are used: // // // // Type Libraries CoClasses DISPInterfaces : LIBID_xxxx : CLASS_xxxx : DIID_xxxx

Non-DISP interfaces: IID_xxxx

// ******************************************************************** *// const // TypeLibrary Major and minor versions XorCodeMajorVersion = 1; XorCodeMinorVersion = 0; LIBID_XorCode: TGUID = '{356B1880-384C-11D7-A2DC-00E04CE92EC6}'; IID_IXorCode: TGUID = '{356B1881-384C-11D7-A2DC-00E04CE92EC6}'; CLASS_XorCode: TGUID = '{356B1883-384C-11D7-A2DC-00E04CE92EC6}'; type // ******************************************************************** *// 521 | S t r o n a

// Forward declaration of types defined in TypeLibrary // ******************************************************************** *// IXorCode = interface; // ******************************************************************** *// // Declaration of CoClasses defined in Type Library // (NOTE: Here we map each CoClass to its Default Interface) // ******************************************************************** *// XorCode = IXorCode;

// ******************************************************************** *// // Interface: IXorCode // Flags: (256) OleAutomation // GUID: {356B1881-384C-11D7-A2DC-00E04CE92EC6} // ******************************************************************** *// IXorCode = interface(IUnknown) ['{356B1881-384C-11D7-A2DC-00E04CE92EC6}'] function Code: PChar; stdcall; procedure Set_lpString(Value: PChar); stdcall; procedure Set_lpPassword(Value: PChar); stdcall; end; // ******************************************************************** *// // The Class CoXorCode provides a Create and CreateRemote method to // create instances of the default interface IXorCode exposed by // the CoClass XorCode. The functions are intended to be used by // clients wishing to automate the CoClass objects exposed by the // server of this typelibrary. 522 | S t r o n a

// ******************************************************************** *// CoXorCode = class class function Create: IXorCode; class function CreateRemote(const MachineName: string): IXorCode; end; implementation uses ComObj; class function CoXorCode.Create: IXorCode; begin Result := CreateComObject(CLASS_XorCode) as IXorCode; end; class function CoXorCode.CreateRemote(const MachineName: string): IXorCode; begin Result := CreateRemoteComObject(MachineName, CLASS_XorCode) as IXorCode; end; end.

Jeli przyjrzae si tym instrukcjom, kod zapewne wyda Ci si bardzo ?zagmatwany? i niezrozumiay. Przykadowo taki fragment: IXorCode = interface(IUnknown) ['{356B1881-384C-11D7-A2DC-00E04CE92EC6}'] function Code: PChar; stdcall; procedure Set_lpString(Value: PChar); stdcall; procedure Set_lpPassword(Value: PChar); stdcall; end;

jest deklaracj interfejsu IXorCode. O interfejsach powiem nieco pniej. Tymczasem przyjrzyj si listingowi 13.2., w ktrym znajduje si kod rdowy kontrolki. Nazwy interfejsw COM zaczynaj si od liter I, podobnie jak nazwy klas VCL rozpoczynaj si liter T. Listing 13.3. Kod rdowy kontrolki

523 | S t r o n a

unit MainFrm; {$WARN SYMBOL_PLATFORM OFF} interface uses Windows, ActiveX, Classes, ComObj, XorCode_TLB, StdVcl; type TXorCode = class(TTypedComObject, IXorCode) private FlpString : PChar; FlpPassword : PChar; protected function Code: PChar; stdcall; procedure Set_lpString(Value: PChar); stdcall; procedure Set_lpPassword(Value: PChar); stdcall; {Declare IXorCode methods here} end; implementation uses ComServ; function TXorCode.Code: PChar; var I : Integer; PassCount : Integer; begin PassCount := 0; Result := FlpString; // przypisz warto pocztkow for I := 1 to Length(FlpString) do // wykonuj dla kadej litery osobno begin { Kady znak zamieniaj na warto liczbow, a nastpnie "XOR-uj" z kad liter hasa ? powstaje wwczas unikalna kombinacja. } Result[i] := Chr(Ord(FlpString[i]) xor Ord(FlpPassword[PassCount])); Inc(PassCount); // zwiksz licznik ? kolejna litera hasa { Jeeli licznik przekroczy dugo hasa ? wyzeruj go } if PassCount > Length(FlpPassword) then PassCount := 0; end;

524 | S t r o n a

end; procedure TXorCode.Set_lpString(Value: PChar); begin { przydzielenie do zmiennej wartoci podanej w parametrze Value } FlpString := Value; end; procedure TXorCode.Set_lpPassword(Value: PChar); begin { przydzielenie do zmiennej wartoci podanej w parametrze Value } FlpPassword := Value; end;

initialization TTypedComObjectFactory.Create(ComServer, TXorCode, Class_XorCode, ciMultiInstance, tmApartment); end.

Budowa i rejestracja kontrolki


Jeeli cay potrzebny kod jest ju gotowy, moemy przystpi do kolejnego kroku, jakim jest skompilowanie kontrolki do postaci pliku DLL. Z menu Project wybierz pozycj Build XorCode. Cay kod zostanie skompilowany do postaci pliku *.dll ? jeeli nie ma adnych bdw, mona przystpi do rejestracji kontrolki. Przed uyciem obiektu COM naley go zarejestrowa w systemie. Jest to prosta czynno, polegajca na wybraniu z menu Project polecenia Register ActiveX Server. W wyniku tej operacji zobaczysz informacj potwierdzajc (rysunek 13.7).

Rysunek 13.7. Okno informujce o pomylnej rejestracji kontrolki Rejestracja kontrolki w systemie moe odby si take za porednictwem funkcji RegisterComServer. W parametrze procedury naley poda jedynie ciek do pliku DLL. 525 | S t r o n a

Wykorzystanie obiektu COM


Aby nasz program prawidowo wykorzysta kontrolk COM, nie jest wcale konieczne umieszczenie pliku *.dll w katalogu z programem. Wystarczyo uprzednie zarejestrowanie kontrolki, aby informacja o ciece zostaa dodana do rejestru. Do prawidowego uytkowania obiektu COM potrzebny nam bdzie jednak modu XorCode_TLB.pas, ktry znajduje si w tym samym katalogu, co skompilowany obiekt COM. Skopiuj wic ten plik do katalogu, gdzie znajduje si projekt wykorzystujcy kontrolk. Dodaj do listy uses programu moduy: uses XorCode_TLB, ComObj;

Peny kod rdowy programu ? aplikacji demonstrujcej uycie operacji XOR ? znajduje si w listingu 13.4. Listing 13.4. Kod rdowy programu wykorzystujcego kontrolk COM unit MainFrm; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls; type TMainForm = class(TForm) btnCode: TButton; edtValue: TEdit; procedure btnCodeClick(Sender: TObject); private { Private declarations } public { Public declarations } end; var MainForm: TMainForm; implementation

526 | S t r o n a

{$R *.dfm} uses XorCode_TLB, ComObj; procedure TMainForm.btnCodeClick(Sender: TObject); var XorCode : IXorCode; lpPassword : String; begin { pobierz haso } lpPassword := InputBox('Podaj haso', 'Wpisz haso', ''); XorCode := CreateCOMObject(CLASS_XORCODE) as IXorCode; if Assigned(XorCode) then begin { ustaw wartoci } XorCode.Set_lpString(PChar(edtValue.Text)); XorCode.Set_lpPassword(PChar(lpPassword)); edtValue.Text := XorCode.Code; // zakoduj end; end; end.

Zanim skorzystamy z dobrodziejstw obiektu COM, naley go utworzy. Umoliwia to funkcja CreateCOMObject. W tym momencie moemy ju wywoywa funkcj ze skompilowanej biblioteki DLL, przekazujc jej potrzebne parametry (tekst do zakodowania oraz haso). Haso musi poda sam uytkownik programu w okienku wywoywanym za pomoc funkcji InputBox. Uruchomiony program jest przedstawiony na rysunku 13.8. Na samym pocztku po naciniciu przycisku zostaniemy poproszeni o wpisanie hasa ? ja wpisaem 123. Nastpnie wedug tego hasa tekst znajdujcy si w kontrolce zostanie zakodowany. Jeli bdziesz chcia zdekodowa tekst, a nie podasz hasa (123), nie uzyskasz dostpu do tekstu.

Rysunek 13.8. Zakodowany tekst Funkcja InputBox jest zadeklarowana w module Dialogs. Dziki niej w prosty sposb mona wywietli okno zawierajce pole edycyjne (TEdit), etykiet (TLabel) oraz dwa przyciski: OK i Anuluj. Pierwszym parametrem tej funkcji jest tekst, ktry ma zosta wywietlony na pasku tytuowym okna; drugi parametr to tekst majcy znale si w etykiecie, a ostatni ? domylny tekst wpisany w polu 527 | S t r o n a

TEdit. Warto zwracana przez ow funkcj to tekst, ktry uytkownik wpisa w polu edycyjnym.

Interfejsy
Z punktu widzenia Object Pascala interfejs obiektu COM jest zwyk klas. Mona by powiedzie, e interfejsy umoliwiaj manipulacj obiektem ? daj wgld w funkcje obiektu COM i umoliwiaj kontaktowanie si z kontrolk. W powyszym przykadzie interfejs wyglda tak: IXorCode = interface(IUnknown) ['{356B1881-384C-11D7-A2DC-00E04CE92EC6}'] function Code: PChar; stdcall; procedure Set_lpString(Value: PChar); stdcall; procedure Set_lpPassword(Value: PChar); stdcall; end;

Charakterystyczn jego cech jest nazwa, rozpoczynajca si od litery I (a nie od T, jak w przypadku klas Object Pascala) oraz sowo kluczowe interface. Podobnie jak TObject stanowi podstawow klas caego VCL, tak wszystkie interfejsy wywodz si z klasy IUknown.

GUID
Ciekawym elementem jest do dugi numer wpisany w nawiasie kwadratowym. Jest to tzw. GUID (Globally Unique ID). Jest to 128-bitowa liczba, okrelajca dany interfejs. Numer ten ma posta losow i skada si z czynnikw, ktre mog by w danym momencie unikalne ? np. aktualna data, numery okrelajce komputer itp. Z punktu widzenia programisty ten numer nie jest tak istotny ? nie naley si nim zbytnio przejmowa.

ActiveX
ActiveX to technologia oparta na COM. Pozwala na tworzenie kontrolek .ocx lub .dll. Tak naprawd ActiveX to obiekt COM, tyle e posiadajcy wasny interfejs dostpny na poziomie projektowania. Wyglda to w ten sposb, e tworzony jest zwyky formularz VCL, bdcy w rzeczywistoci kontrolk ActiveX. Mona korzysta ze wszystkich komponentw i, oglnie rzecz biorc, projektowanie jest atwiejsze ni w przypadku zwykych obiektw COM. 528 | S t r o n a

Dodatkowo ActiveX pozwala na wygenerowanie kodu pozwalajcego na umieszczenie aplikacji na stronie WWW.

Import kontrolek ActiveX


Korzystajc z Delphi, moesz nawet nie wiedzie, e w rzeczywistoci korzystasz z kontrolki ActiveX. Po zaimportowaniu do Delphi taka kontrolka jest przedstawiana jak zwyky komponent i znajduje si na palecie komponentw. Przykad? Komponent TWebBrowser (paleta Internet). Komponent ten suy do wywietlania stron WWW, ale w rzeczywistoci jest to kontrolka ActiveX przegldarki Internet Explorer. A zatem majc zainstalowan przegldark, posiadasz rwnie kontrolk ActiveX, ktr z kolei moesz uy w Delphi. Przewaga kontrolek ActiveX nad obiektami COM polega midzy innymi na tym, e podczas projektowania kontrolek mona uy komponentw VCL oraz umieszcza je na palecie komponentw. Na doczonej do ksiki pycie CD-ROM znajdziesz kontrolk ActiveX o nazwie VTextProj.ocx. Sprbujmy zaimportowa j do palety komponentw. 1. Z menu Component wybierz Import ActiveX Control (rysunek 13.9).

529 | S t r o n a

Rysunek 13.9. Importowanie kontrolki ActiveX 2. Nacinij przycisk Add; wska w oknie kontrolk ActiveX, ktr chcesz importowa. 3. Na licie pojawi si nowa pozycja. Nacinij przycisk Install. 4. Zostaniesz poproszony o wskazanie pakietu, w ktrym zostanie umieszczony ?komponent? (rysunek 13.10). Wybierz OK, akceptujc wprowadzone dane.

Rysunek 13.10. Wskazanie pakietu 5. Nastpnie zostaniesz zapytany, czy chcesz skompilowa pakiet (rysunek 13.11). Zatwierd przyciskiem OK.

530 | S t r o n a

Rysunek 13.11. Kontynuacja instalacji Po tym zabiegu kontrolka powinna zosta zainstalowana w palecie komponentw. Zapisz zmiany i utwrz nowy projekt; komponent znajduje si ju na palecie ActiveX.

Wykorzystanie kontrolki TVText


Nie powiedziaem jeszcze, do czego suy kontrolka, ktr wanie importowae. Ot umoliwia ona wywietlanie napisw do filmw. Jeeli lubisz filmy, to zapewne wiesz, e wiele odtwarzaczy umoliwia wywietlanie napisw w przypadku, gdy film jest w innej wersji jzykowej (rysunek 13.12). Tworzeniem takiej kontrolki krok po kroku zajmiemy si w dalszej czci tego rozdziau, a tymczasem moesz przyjrze si listingowi 13.5. w ktrym znajduje si program korzystajcy z tej kontrolki

Rysunek 13.12. Kontrolka w trakcie dziaania! Listing 13.5. Kod rdowy programu wykorzystujcego kontrolk TVText

531 | S t r o n a

{ Copyright (c) 2002 by Adam Boduch <adam@4programmes.net> } unit MainFrm; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, OleCtrls, VTextProj_TLB, StdCtrls; type TMainForm = class(TForm) VText: TVText; btnLoad: TButton; OpenDialog: TOpenDialog; btnStop: TButton; procedure btnLoadClick(Sender: TObject); procedure btnStopClick(Sender: TObject); procedure FormClose(Sender: TObject; var Action: TCloseAction); private { Private declarations } public { Public declarations } end; var MainForm: TMainForm; implementation {$R *.dfm} procedure TMainForm.btnLoadClick(Sender: TObject); begin if OpenDialog.Execute then begin VText.Stop; // zatrzymanie, jeeli teraz co jest odtwarzane... btnStop.Enabled := True; VText.Start(PChar(OpenDialog.FileName)); // wywoanie procedury end; end; procedure TMainForm.btnStopClick(Sender: TObject); begin VText.Stop; 532 | S t r o n a

btnStop.Enabled := False; end; procedure TMainForm.FormClose(Sender: TObject; var Action: TCloseAction); begin VText.Stop; // zatrzymanie, jeeli uytkownik chce zamkn program end; end.

Wykorzystanie takiego ?komponentu? w programie jest niezwykle proste. Dziaanie tego obiektu jest przed nami ukryte ? o to nie musimy si martwi. Jedyne, co nas interesuje, to dwie metody: Start i Stop. W pierwszej naley poda ciek do pliku tekstowego z napisami, co spowoduje rozpoczcie dziaania. Druga metoda ? Stop ? zatrzyma dziaanie obiektu.

Tworzenie kontrolek ActiveX


Tworzenie obiektw ActiveX jest rwnie proste jak tworzenie obiektw COM. Jak ju mwiem, podczas projektowania kontrolek ActiveX moemy uywa wizualnej biblioteki komponentw (VCL). Proces ten jest podobny do tworzenia obiektw COM. Z menu File wybierz polecenie New, a nastpnie Other. Kliknij zakadk ActiveX, wybierz ikon ActiveX Form. Na rysunku 13.13 przedstawione jest okno, ktre zostanie wywietlone w wyniku tej operacji.

Rysunek 13.13. Tworzenie nowej kontrolki ActiveX 533 | S t r o n a

W polu New ActiveX Name wpisz ActiveXTest. Pole Implementation Unit niech zawiera warto ActiveXTestFrm.pas, a Project Name: ActiveXTestProj.dpr. Naciij OK, a Delphi utworzy kontrolk ActiveX i wywietli formularz. Z menu File wybierz polecenie Save All. Podczas zapisywania domyln nazw pliku, jak zaproponuje Delphi, bdzie ta, ktr wpisae w oknie tworzenia kontrolki ActiveX. Katalog, ktry wybrae, wzbogaci si o par nowych plikw. Oprcz standardowych plikw znajduje si tam rwnie plik ActiveXTestProj_TLB.pas. Plik ten zawiera interfejsy COM.

Przykad: wywietlanie napisw do filmu


Napiszmy przykadow kontrolk ActiveX, ktra pozwoli Ci zrozumie istot dziaania tych obiektw. Nasza przykadowa kontrolka bdzie umoliwia wywietlanie napisw do filmw. Na samym pocztku procesu tworzenia kontrolki musimy, tak jak w przypadku kontrolek COM, stworzy dwie metody ? Start i Stop.

Tworzenie interfejsu COM


Stwrz now kontrolk ActiveX. Postpuj tak, jak w przypadku wiczenia poprzedniego ? w polu New ActiveX Name kreatora wpisz VText. W polu Implementation Unit wpisz VTextFrm.pas, a w polu Project Name ? VTextProj.dpr. Zapisz cay projekt. W katalogu z kontrolk znajduje si plik VTextProj_TLB.pas. Domylnie ten plik nie jest otwarty w projekcie. Otwrz go zatem ? w edytorze kodu utworzona zostanie nowa zakadka, a na pierwszy plan wysunie si edytor biblioteki. Kliknij prawym przyciskiem myszy pozycj IVText i z menu podrcznego wybierz New/Method. Metod t nazwij Start. Kliknij zakadk Parameters. Bdziesz musia doda nowy parametr dla funkcji. Doprowad parametr do takiej postaci, jak przedstawiono na rysunku 13.14.

534 | S t r o n a

Rysunek 13.14. Ustawianie parametrw metody Stwrz teraz metod Stop i powtrz cay proces. Nasza procedura nie bdzie zawieraa adnych parametrw ? nie musisz zatem robi nic wicej. Zapisz cay projekt.

Tworzenie kontrolki ActiveX


Jak ju mwiem, nasza kontrolka wywietla bdzie napisy do filmw. Wiele odtwarzaczy multimedialnych oferuje moliwo zaadowania napisw do filmu, ktry akurat ogldamy w innej wersji jzykowej. Nasza kontrolka bdzie uwzgldniaa plik z napisami, ktrego poszczeglne wiersze zapisane s w ten sposb: <tt.... 00:37:46:Prosz. 00:37:53:Pan musi by naprawd wany... ... Przed poszczeglnymi kwestiami aktorw umieszczony jest czas od rozpoczcia filmu, w jakim powinien zosta wywietlony napis. Kontrolka w momencie wywoania metody Start zaczyna dziaa w ptli while. Zreszt zaraz przekonasz si, jak to bdzie wyglda?

Strona wizualna

535 | S t r o n a

1. Przede wszystkim otwrz plik VTextFrm (jeeli jeszcze nie jest otwarty w projekcie). 2. Naciskajc klawisz F12, przecz si do formularza. Zmniejsz ten formularz, dostosowujc go do wasnych wymaga. 3. Umie na formularzu komponent TPanel, a jego waciwoci Align nadaj warto alClient. 4. Waciwo BevelInner zmie na bvLowered, a sam komponent nazwij pnlMessage. 5. Na komponencie pnlMessage umie obiekt TLabel i zmie warto waciwoci Align na alBottom; komponent nazwij lblCurrent.

Oto cay proces projektowania kontrolki od strony wizualnej! Kolejnym krokiem jest tworzenie samego kodu kontrolki.

Kod rdowy kontrolki Przecz si do kodu pliku VTextFrm. Odszukaj sekcj private i dodaj nastpujce wiersze kodu:

FLines : TStringList; // zmienna przechowuje napisy FTime, FText : TArray; // tablica ? czas oraz napis FBroken : WordBool; // okrela, czy proces jest uruchomiony procedure PrepareText; // przygotuj (przeanalizuj) plik tekstowy

Skorzystaem tutaj z nowego typu danych ? tablicy dynamicznej TArray. Dodaj deklaracje tego typu w sekcji type: TArray = array of String;

Procedura PrepareText, ktr zadeklarowalimy w sekcji private, suy do przygotowania pliku tekstowego. Zaraz po wywoaniu metody Startdo pola FLines zostaje odczytany plik z napisami. Procedura PrepareText ma oddzieli z kadego wiersza czas, w ktrym napis ma by wywietlony, oraz sam tre napisu. procedure TVText.PrepareText; var i : Integer; begin { okrel wielko tablicy na podstawie iloci wierszy } SetLength(FTime, FLines.Count); SetLength(FText, FLines.Count); { ptla po wszystkich wierszach... } for I := 0 to FLines.Count ?1 do 536 | S t r o n a

begin { do tego elementu tablicy przypisz czas, w ktrym powinien zosta wywietlony napis } FTime[i] := (Copy(FLines[i], 1, 8)); { tutaj przypisz sam tre } FText[i] := (Copy(FLines[i], 10, Length(FLines[i]) ? 8)); end; end;

Dziki temu mamy gotowe do uytku tablice FTime oraz FText. Teraz jedyny problem stanowi wywietlenie odpowiedniego elementu tablicy w odpowiednim czasie. Odpowiada za to metoda Start. procedure TVText.Start(FileName: PChar); var Counter : Integer; // licznik ? ile ju napisw zostao wywietlonych FPause : Integer; // czas wywietlania napisu CurrentTime : TTime; // czas odtwarzania filmu wHour, wMin, wSec : Integer; begin FLines := TStringList.Create; FLines.LoadFromFile(FileName); // zaaduj plik tekstowy PrepareText; // przygotuj dwie tablice FBroken := False; Counter := ?1; FPause := 0; wHour := 0; wMin := 0; wSec := 0; { ptla wywietlana co 1000 milisekund, dopki zmienna FBroken nie ma wartoci False } while (not FBroken) or (not Application.Terminated) do begin Application.ProcessMessages; Sleep(1000); // odczekaj 1 sek. if FBroken then Break; // jeeli zmienna = TRUE, przerwij dziaanie Inc(wSec); // zwiksz liczb sekund if wSec >= 60 then // jeeli liczba sekund jest wiksza od 60... begin Inc(wMin); // zwiksz liczb minut 537 | S t r o n a

wSec := 0; // wyzeruj zmienn end; if wMin > 60 then // jeeli liczba minut jest wiksza do 60 begin Inc(wHour); // zwiksz liczb godzin wMin := 0; // wyzeruj minuty end; // na podstawie danych utwrz zmienn TTime CurrentTime := EncodeTime(wHour, wMin, wSec, 0); lblCurrent.Caption := TimeToStr(CurrentTime);

if AnsiMatchStr(TimeToStr(CurrentTime), FTime) then begin Inc(Counter); pnlMessage.Caption := FText[Counter]; FPause := 0; end else begin if Length(pnlMessage.Caption) > 0 then begin Inc(FPause); if FPause = 5 then begin FPause := 0; pnlMessage.Caption := ''; end; end; end; end; end;

Po zaadowaniu napisw i wywoaniu procedury PrepareText mamy gotowe tablice. Ptla, ktra jest wykonywana w odstpie 1 sekundy, za kadym razem zwiksza liczb sekund, a nastpnie minut (jeeli liczba sekund osignie 60) itd. Nastpnie ? za pomoc funkcji EncodeTime i dziki zmiennym wHour, wMin, wSec ? moemy skonstruowa typ TTime. Funkcja AnsiMatchStr sprawdza, czy dany warto CurrentTime znajduje si w tablicy FTime. Funkcja AnsiMatchStr znajduje si w module StrUtils.pas. Aby wszystko mogo dziaa, musisz ten modu doda do listy uses. Nastpi wwczas wywietlenie tekstu z tablicy FText. Jednym problem jest rozrnienie, ktry 538 | S t r o n a

element tablicy powinien by w tym momencie wywietlony. Aby go rozwiza, naley wprowadzi zmienn Counter, ktra zwikszy si o jeden za kadym razem, gdy napis zostanie wywietlony. W dokumentacji Delphi znajduje si bd ? funkcja AnsiMatchStr wcale nie zwraca liczby w postaci Integer, jak zostao to napisane. W rzeczywistoci zwraca True, jeeli element zosta znaleziony, lub ? w przeciwnym wypadku ? False. Pozostao jeszcze napisanie metody Stop. Procedura ta bdzie suy do wstrzymywania caego procesu. Jej kod jest prosty: procedure TVText.Stop; begin FBroken := True; end;

Zmiana wartoci zmiennej FBroken na True powoduje zatrzymanie dziaania ptli while. Peny kod rdowy moduu przestawiony jest w listingu 13.6. Listing 13.6. Kod rdowy kontrolki { Copyright (c) 2002 by Adam Boduch <adam@4programmers.net> } unit VTextFrm; {$WARN SYMBOL_PLATFORM OFF} interface uses Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, ActiveX, AxCtrls, VTextProj_TLB, StdVcl, StdCtrls, ExtCtrls, StrUtils; type TArray = array of String; TVText = class(TActiveForm, IVText) pnlMessage: TPanel; lblCurrent: TLabel; private { Private declarations } FEvents: IVTextEvents; FLines : TStringList; // zmienna przechowuje napisy 539 | S t r o n a

FTime, FText : TArray; // tablica ? czas oraz napis FBroken : WordBool; // okrela, czy proces jest uruchomiony procedure PrepareText; // przygotuj (przeanalizuj) plik tekstowy procedure ActivateEvent(Sender: TObject); procedure ClickEvent(Sender: TObject); procedure CreateEvent(Sender: TObject); procedure DblClickEvent(Sender: TObject); procedure DeactivateEvent(Sender: TObject); procedure DestroyEvent(Sender: TObject); procedure KeyPressEvent(Sender: TObject; var Key: Char); procedure PaintEvent(Sender: TObject); protected { Protected declarations } procedure DefinePropertyPages(DefinePropertyPage: TDefinePropertyPage); override; procedure EventSinkChanged(const EventSink: IUnknown); override; function Get_Active: WordBool; safecall; function Get_AlignDisabled: WordBool; safecall; function Get_AutoScroll: WordBool; safecall; function Get_AutoSize: WordBool; safecall; function Get_AxBorderStyle: TxActiveFormBorderStyle; safecall; function Get_Caption: WideString; safecall; function Get_Color: OLE_COLOR; safecall; function Get_DoubleBuffered: WordBool; safecall; function Get_DropTarget: WordBool; safecall; function Get_Enabled: WordBool; safecall; function Get_Font: IFontDisp; safecall; function Get_HelpFile: WideString; safecall; function Get_KeyPreview: WordBool; safecall; function Get_PixelsPerInch: Integer; safecall; function Get_PrintScale: TxPrintScale; safecall; function Get_Scaled: WordBool; safecall; function Get_ScreenSnap: WordBool; safecall; function Get_SnapBuffer: Integer; safecall; function Get_Visible: WordBool; safecall; function Get_VisibleDockClientCount: Integer; safecall; procedure _Set_Font(var Value: IFontDisp); safecall; procedure Set_AutoScroll(Value: WordBool); safecall; procedure Set_AutoSize(Value: WordBool); safecall; procedure Set_AxBorderStyle(Value: TxActiveFormBorderStyle); safecall; procedure Set_Caption(const Value: WideString); safecall; procedure Set_Color(Value: OLE_COLOR); safecall; procedure Set_DoubleBuffered(Value: WordBool); safecall; procedure Set_DropTarget(Value: WordBool); safecall; procedure Set_Enabled(Value: WordBool); safecall; procedure Set_Font(const Value: IFontDisp); safecall; 540 | S t r o n a

procedure Set_HelpFile(const Value: WideString); safecall; procedure Set_KeyPreview(Value: WordBool); safecall; procedure Set_PixelsPerInch(Value: Integer); safecall; procedure Set_PrintScale(Value: TxPrintScale); safecall; procedure Set_Scaled(Value: WordBool); safecall; procedure Set_ScreenSnap(Value: WordBool); safecall; procedure Set_SnapBuffer(Value: Integer); safecall; procedure Set_Visible(Value: WordBool); safecall; procedure Start(FileName: PChar); safecall; procedure Stop; safecall; public { Public declarations } procedure Initialize; override; end; implementation uses ComObj, ComServ; {$R *.DFM} { TVText } procedure TVText.DefinePropertyPages(DefinePropertyPage: TDefinePropertyPage); begin { Define property pages here. Property pages are defined by calling DefinePropertyPage with the class id of the page. For example, DefinePropertyPage(Class_VTextPage); } end; procedure TVText.EventSinkChanged(const EventSink: IUnknown); begin FEvents := EventSink as IVTextEvents; inherited EventSinkChanged(EventSink); end; procedure TVText.Initialize; begin inherited Initialize; OnActivate := ActivateEvent; OnClick := ClickEvent; OnCreate := CreateEvent; OnDblClick := DblClickEvent; OnDeactivate := DeactivateEvent; OnDestroy := DestroyEvent; OnKeyPress := KeyPressEvent; 541 | S t r o n a

OnPaint := PaintEvent; end; function TVText.Get_Active: WordBool; begin Result := Active; end; function TVText.Get_AlignDisabled: WordBool; begin Result := AlignDisabled; end; function TVText.Get_AutoScroll: WordBool; begin Result := AutoScroll; end; function TVText.Get_AutoSize: WordBool; begin Result := AutoSize; end; function TVText.Get_AxBorderStyle: TxActiveFormBorderStyle; begin Result := Ord(AxBorderStyle); end; function TVText.Get_Caption: WideString; begin Result := WideString(Caption); end; function TVText.Get_Color: OLE_COLOR; begin Result := OLE_COLOR(Color); end; function TVText.Get_DoubleBuffered: WordBool; begin Result := DoubleBuffered; end; function TVText.Get_DropTarget: WordBool; begin Result := DropTarget; end;

542 | S t r o n a

function TVText.Get_Enabled: WordBool; begin Result := Enabled; end; function TVText.Get_Font: IFontDisp; begin GetOleFont(Font, Result); end; function TVText.Get_HelpFile: WideString; begin Result := WideString(HelpFile); end; function TVText.Get_KeyPreview: WordBool; begin Result := KeyPreview; end; function TVText.Get_PixelsPerInch: Integer; begin Result := PixelsPerInch; end; function TVText.Get_PrintScale: TxPrintScale; begin Result := Ord(PrintScale); end; function TVText.Get_Scaled: WordBool; begin Result := Scaled; end; function TVText.Get_ScreenSnap: WordBool; begin Result := ScreenSnap; end; function TVText.Get_SnapBuffer: Integer; begin Result := SnapBuffer; end; function TVText.Get_Visible: WordBool; begin Result := Visible; 543 | S t r o n a

end; function TVText.Get_VisibleDockClientCount: Integer; begin Result := VisibleDockClientCount; end; procedure TVText._Set_Font(var Value: IFontDisp); begin SetOleFont(Font, Value); end; procedure TVText.ActivateEvent(Sender: TObject); begin if FEvents <> nil then FEvents.OnActivate; end; procedure TVText.ClickEvent(Sender: TObject); begin if FEvents <> nil then FEvents.OnClick; end; procedure TVText.CreateEvent(Sender: TObject); begin if FEvents <> nil then FEvents.OnCreate; end; procedure TVText.DblClickEvent(Sender: TObject); begin if FEvents <> nil then FEvents.OnDblClick; end; procedure TVText.DeactivateEvent(Sender: TObject); begin if FEvents <> nil then FEvents.OnDeactivate; end; procedure TVText.DestroyEvent(Sender: TObject); begin if FEvents <> nil then FEvents.OnDestroy; end; procedure TVText.KeyPressEvent(Sender: TObject; var Key: Char); var TempKey: Smallint; begin TempKey := Smallint(Key); if FEvents <> nil then FEvents.OnKeyPress(TempKey); 544 | S t r o n a

Key := Char(TempKey); end; procedure TVText.PaintEvent(Sender: TObject); begin if FEvents <> nil then FEvents.OnPaint; end; procedure TVText.Set_AutoScroll(Value: WordBool); begin AutoScroll := Value; end; procedure TVText.Set_AutoSize(Value: WordBool); begin AutoSize := Value; end; procedure TVText.Set_AxBorderStyle(Value: TxActiveFormBorderStyle); begin AxBorderStyle := TActiveFormBorderStyle(Value); end; procedure TVText.Set_Caption(const Value: WideString); begin Caption := TCaption(Value); end; procedure TVText.Set_Color(Value: OLE_COLOR); begin Color := TColor(Value); end; procedure TVText.Set_DoubleBuffered(Value: WordBool); begin DoubleBuffered := Value; end; procedure TVText.Set_DropTarget(Value: WordBool); begin DropTarget := Value; end; procedure TVText.Set_Enabled(Value: WordBool); begin Enabled := Value; end;

545 | S t r o n a

procedure TVText.Set_Font(const Value: IFontDisp); begin SetOleFont(Font, Value); end; procedure TVText.Set_HelpFile(const Value: WideString); begin HelpFile := String(Value); end; procedure TVText.Set_KeyPreview(Value: WordBool); begin KeyPreview := Value; end; procedure TVText.Set_PixelsPerInch(Value: Integer); begin PixelsPerInch := Value; end; procedure TVText.Set_PrintScale(Value: TxPrintScale); begin PrintScale := TPrintScale(Value); end; procedure TVText.Set_Scaled(Value: WordBool); begin Scaled := Value; end; procedure TVText.Set_ScreenSnap(Value: WordBool); begin ScreenSnap := Value; end; procedure TVText.Set_SnapBuffer(Value: Integer); begin SnapBuffer := Value; end; procedure TVText.Set_Visible(Value: WordBool); begin Visible := Value; end; procedure TVText.Start(FileName: PChar); var Counter : Integer; // licznik ? ile ju napisw zostao 546 | S t r o n a

wywietlonych FPause : Integer; // czas wywietlania napisu CurrentTime : TTime; // czas odtwarzania filmu wHour, wMin, wSec : Integer; begin FLines := TStringList.Create; FLines.LoadFromFile(FileName); // zaaduj plik tekstowy PrepareText; // przygotuj dwie tablice FBroken := False; Counter := ?1; FPause := 0; wHour := 0; wMin := 0; wSec := 0; { ptla wywietlana co 1000 milisekund dopki zmienna FBroken nie ma wartoci False } while (not FBroken) or (not Application.Terminated) do begin Application.ProcessMessages; Sleep(1000); // odczekaj 1 sek. if FBroken then Break; // jeeli zmienna = TRUE, przerwij dziaanie Inc(wSec); // zwiksz liczb sekund if wSec >= 60 then // jeeli liczba sekund jest wiksza od 60... begin Inc(wMin); // zwiksz liczb minut wSec := 0; // wyzeruj zmienn end; if wMin > 60 then // jeeli liczba minut jest wiksza od 60 begin Inc(wHour); // zwiksz liczb godzin wMin := 0; // wyzeruj minuty end; // na podstawie danych utwrz zmienn TTime CurrentTime := EncodeTime(wHour, wMin, wSec, 0); lblCurrent.Caption := TimeToStr(CurrentTime);

if AnsiMatchStr(TimeToStr(CurrentTime), FTime) then begin 547 | S t r o n a

Inc(Counter); pnlMessage.Caption := FText[Counter]; FPause := 0; end else begin if Length(pnlMessage.Caption) > 0 then begin Inc(FPause); if FPause = 5 then begin FPause := 0; pnlMessage.Caption := ''; end; end; end; end; end; procedure TVText.Stop; begin FBroken := True; end; procedure TVText.PrepareText; var i : Integer; begin { okrel wielko tablicy na podstawie iloci wierszy } SetLength(FTime, FLines.Count); SetLength(FText, FLines.Count); { ptla po wszystkich wierszach... } for I := 0 to FLines.Count ?1 do begin { do tego elementu tablicy przypisz czas, w ktrym powinien zosta wywietlony napis } FTime[i] := (Copy(FLines[i], 1, 8)); { tutaj przypisz sam tre } FText[i] := (Copy(FLines[i], 10, Length(FLines[i]) ? 8)); end; end; initialization TActiveFormFactory.Create( ComServer, TActiveFormControl, TVText, 548 | S t r o n a

Class_VText, 1, '', OLEMISC_SIMPLEFRAME or OLEMISC_ACTSLIKELABEL, tmApartment); end.

W odrnieniu od obiektw COM kontrolki ActiveX posiadaj dodatkowe metody, takie jak zwyke formularze (generowane automatycznie przez Delphi). Dziki temu po umieszczeniu kontrolki ActiveX na formularzu nasz nowy komponent bdzie posiada standardowe metody oraz zdarzenia zwykego formularza ? std wynika obszerno kodu rdowego.

Budowa i rejestracja
Nasza kontrolka jest ju gotowa. Po wybraniu polecenia Build z menu Project kod rdowy zostanie skompilowany i przeksztacony w plik .ocx. Jeeli projekt zawiera jakie bdy, informajce o tym znajdziesz w oknie wiadomoci (ang. Message View). Po wybraniu polecenia Register ActiveX Server z menu Run kontrolka zostanie zarejestrowana w systemie. Instalacj kontrolek ActiveX omwiem ju w podpunkcie ?Import kontrolek ActiveX?.

ActiveX w Internecie
Istnieje moliwo publikowania tworzonych przez siebie kontrolek w Internecie na stronie WWW. Niepotrzebna jest nawet do tego wiksza znajomo jzyka HTML. Delphi wygeneruje nawet potrzebn stron WWW, ktr wystarczy tylko umieci gdzie na serwerze.

Wzgldy bezpieczestwa
Uywanie kontrolek ActiveX w Internecie nie jest zbyt popularne. Wiele uytkownikw ze wzgldu na niebezpieczestwo wynikajce z korzystania ActiveX ma wyczon opcj ich adowania (zdaje si, e jest to domylne ustawienie). Z tego wzgldu przy prbie zaadowania kontrolki zobacz tylko taki komunikat, jak na rysunku 13.15.

549 | S t r o n a

Rysunek 13.15. Komunikat informujcy o niemonoci obsuenia ActiveX

Przykadowa kontrolka
W tym punkcie zaprezentuj moliwoci publikowania wasnych kontrolek. Z tego te wzgldu obiekt, ktry teraz stworzymy, bdzie bardzo prosty.

Tworzenie kontrolki ActiveX Utwrz now kontrolk i nazwij j ActiveWWW. W polu Implementation Unit wpisz ActiveWWWFrm.pas, a w polu Project Name ? ActiveWWWProj.dpr. Nasza kontrolka bdzie w ptli wywietla napis z wykorzystaniem efektu maszyny do pisania, czyli literka po literce. Na formularzu umie komponent TGroupBox, a na nim TLabel. Rozcignij etykiet na ca szeroko TGroupBox i zmie waciwo AutoSize na False. Umie dodatkowo dwa przyciski. Jeden bdzie suy do rozpoczcia animacji, a drugi do jej zatrzymania. var FBroken : Boolean; procedure TActiveWWW.btnGoClick(Sender: TObject); const ExMsg = 'To jest przykadowa kontrolka :?)'; var i : Integer; begin FBroken := False; { ptla while wykonywana przez cay czas trwania programu } while (not Application.Terminated) or (not FBroken) do begin lblMessage.Caption := ''; { ptla for powoduje wywietlenie na etykiecie kolejnych liter } for i := 1 to Length(ExMsg) do begin Application.ProcessMessages; if FBroken then Break; Sleep(100); 550 | S t r o n a

lblMessage.Caption := lblMessage.Caption + ExMsg[i]; end; end; end; procedure TActiveWWW.btnStopClick(Sender: TObject); begin FBroken := True; end;

Wykorzystalimy efekt maszyny do pisania, polegajcy na wywietlaniu przykadowego napisu litera po literze.

Publikowanie kontrolki Do opublikowania kontrolki w sieci posuymy si dwoma poleceniami z menu Project: Web Deployment Options oraz Web Deploy. Na samym pocztku wybierz pierwsze polecenie, aby okreli opcje publikacji (rysunek 13.16).

551 | S t r o n a

Rysunek 13.16. Okno Web Deployment Options W oknie tym musimy poda par informacji, ktre s potrzebne do zbudowania kontrolki. Zamy, e bdzie ona uruchamiana na lokalnym serwerze Apache. Pierwsze pole Target dir okna Web Deployment Options musi zawiera ciek do katalogu, w ktrym kontrolka zostanie umieszczona po zbudowaniu. W kolejnym polu (Target URL) wpisz adres URL, ktry bdzie prowadzi do odpowiedniej strony ? ja wpisaem http://localhost. Ostatnie pole (HTML Dir) okrela ciek, w ktrej wygenerowany zostanie odpowiedni plik HTML. Ja wpisaem t sam warto, co w pozycji Target Dir. To waciwie wszystko. Zamknij okno przyciskiem OK. Wybierz z menu Project pozycj Web Deploy. Kontrolka powinna zosta skompilowana i zapisana w wybranym przez Ciebie katalogu. W oknie Web Deployment Options moesz zaznaczy opcj Use CAB file compression. Dziki temu skompilowana kontrolka ActiveX zostanie skompresowana w pliku *.cab. Po tym zabiegu plik ActiveWWWProj.htm zawiera tre przedstawion w listingu 13.7. Listing 13.7. Kod HTML strony wygenerowanej przez Delphi <HTML> <H1> Delphi 7 ActiveX Test Page </H1><p> You should see your Delphi 7 forms or controls embedded in the form below. <HR><center><P> <OBJECT classid="clsid:6013039B-7D1B-4DAE-98D2-232733529810" codebase="http://localhost/ActiveWWWProj.ocx#version=1,0,0,0" width=350 height=250 align=center hspace=0 vspace=0 > </OBJECT> </HTML>

Aby kontrolka ActiveX bya lepiej wywietlana, nadaem szerokoci i wysokoci obiektu takie wartoci:
width=400 height=150

Rezultat dziaania programu moesz zaobserwowa na rysunku 13.17. 552 | S t r o n a

Rysunek 13.17. Kontrolka ActiveX w dziaaniu Peen kod rdowy powyszej kontrolki moesz znale na pycie CD-ROM w katalogu ../listingi/13/WebActiveX/ActiveWWWProj.dpr

Podsumowanie
W tym rozdziale pokazaem, w jaki sposb mona tworzy obiekty COM oraz kontrolki ActiveX. Naley to moe do bardziej zaawansowanych czynnoci programistycznych, ale moe si przyda ? chociaby do dynamicznego tworzenia stron WWW wzbogaconych o kontrolki ActiveX. Zaczniki:

Listingi_13.zip (536.79 kB)

Podsumowanie czci II

Edytuj Historia Przenie


553 | S t r o n a

Obserwuj

Podsumowanie czci II
W tym momencie zakoczony zosta pewien etap nauki. Cz ta miaa za zadanie podejmowa bardziej zaawansowane aspekty tworzenia programw, lecz nie pomijajc tych, ktre byy konieczne do zrozumienia dalsz cz. Na pewno niektre spord zaprezentowanych rozdziaw byy dla Ciebie ciekawsze, a inne ? nudniejsze. By moe nie przeczytae wszystkiego ? nie jest to konieczne. Jeeli dany temat nie interesuje Ci teraz, to moe zaciekawi Ci nieco pniej ? nie ma popiechu! W tej czci kady rozdzia powicony by osobnemu tematowi. Kolejne czci tej ksiki bd ju cakiem inne ? omwi w nich takie tematy, ktre wymagaj powicenia nieco wicej czasu i nie mieszcz si w ramach jednego rozdziau.

554 | S t r o n a

Cz III

Edytuj Historia Przenie Obserwuj

Cz III
Do tej pory poszczeglne czci ksiki nie byy jako specjalnie uporzdkowane. W poprzedniej, II czci, omawiaem wiele rnorodnych aspektw pisania programw. Od tej pory si to zmieni ? kolejne czci tej ksiki bd ju uporzdkowane tematycznie. W tej czci przedstawi tworzenie wasnych komponentw. Nie jest to taka prosta sztuka, lecz aby pisa wasne komponenty, trzeba opanowa przynajmniej podstawy VCL. Dlatego te rozdzia 14. bdzie powicony cakowicie bibliotekom VCL i CLX ? omwieniu hierarchii klas oraz podstaw rodowiska wizualnego, jakim jest Delphi. W rozdziale 14. znajdzie si przede wszystkim teoria ? mniej bdzie praktyki. Za to po przeczytaniu rozdziau 15. nauczysz si samodzielnie pisa komponenty. Zaczniemy od tych najprostszych, ?niewidzialnych?, a potem przejdziemy do bardziej rozbudowanych komponentw graficznych. Wiksz cz tego rozdziau bd stanowi informacje praktyczne.

Rozdzia 14

Edytuj Historia Przenie Obserwuj

Komponenty VCL i CLX


Zanim przejdziemy do waciwego procesu tworzenia komponentw, konieczne bdzie dokadne 555 | S t r o n a

zapoznanie si z zasadami ich dziaania. Co prawda zarwno o klasach, jak i o VCL mwiem ju w rozdziale 3., lecz tamta wiedza bya konieczna do dalszego projektowania aplikacji w Delphi. Tym razem poznasz architektur komponentw VCL oraz CLX ze strony projektanta ? dowiesz si, jak to wszystko dziaa i jak jest ze sob poczone.

Spis treci 1 Czym jest komponent? 2 VCL 3 CLX 3.1 Tworzenie aplikacji opartych na CLX 3.2 CLX a VCL 3.3 Architektura CLX 4 Windows a Linux 4.1 Kompilacja warunkowa 5 Rodzaje komponentw 5.1 Komponenty wizualne 5.2 Komponenty niewizualne 5.3 Komponenty graficzne 6 Hierarchia komponentw 6.1 TObject 6.1.1 Metody klasy TObject 6.1.1.1 ClassName 6.1.1.2 ClassNameIs 6.1.1.3 ClassInfo 6.1.1.4 ClassParent 6.1.1.5 ClassType 6.1.1.6 FieldAddress 6.1.1.7 MethodAddress 6.2 TPersistent 6.2.1 Metoda Assign 6.2.2 Metoda AssignTo 6.2.3 Metoda DefineProperties 6.3 TComponent 6.3.1 Waciwoci klasy TComponent 6.3.1.1 ComponentCount 6.3.1.2 ComponentIndex 6.3.1.3 Components 6.3.1.4 ComponentState 6.3.1.5 Name 6.3.1.6 Owner 6.3.2 Metody klasy TComponent 6.4 TControl 6.4.1 Waciwoci klasy TControl 556 | S t r o n a

6.5 TWinControl i TWidgetControl 6.5.1 Waciwoci klas TWinControl i TWidgetControl 6.5.2 Zdarzenia 6.6 Klasy TCustom 6.7 TGraphicControl 7 Budowa komponentu 7.1 Waciwoci 7.1.1 Rodzaje waciwoci 7.2 Zdarzenia 7.3 Metody 8 RTTI 8.1 Waciwoci obiektu 8.2 Dokadniejsze informacje o obiekcie 9 Podsumowanie

W tym rozdziale:

dowiesz si, jak funkcjonuje klasa VCL; dowiesz si, czym jest biblioteka CLX; poznasz elementarne metody i waciwoci podstawowych klas.

Czym jest komponent?


Delphi jako rodowisko wizualne udostpnia rne kontrolki ? tzw. komponenty, suce do szybkiego projektowania aplikacji. Komponenty s ?klockami?, ktrymi posuguje si programista w procesie budowania aplikacji. Patrzc na komponenty ze strony projektanta aplikacji, mamy do czynienia z wygodnymi i atwymi w obsudze narzdziami. Nie interesuje nas, jak one dziaaj i jaki kod znajduje si ?w rodku?. Wane s zdarzenia, metody, waciwoci i zadania, jakie speniaj. W rzeczywistoci wiele komponentw jest ze sob powizanych i korzysta z tych samych moduw, klas i bibliotek. Kluczem do poznania VCL oraz CLX jest zrozumienie zasady dziaania rnych typw danych, poznanie hierarchii klas itp.

557 | S t r o n a

VCL
VCL to Visual Component Library (wizualna biblioteka komponentw). Programowanie w Delphi nie byoby takie atwe dla pocztkujcych, gdyby nie VCL. Poprzednikiem VCL bya biblioteka OWL (Object Windows Library), znajdujca si w Turbo Pascalu. Dla uytkownika wywoanie konstruktora Create to tylko jeden wiersz kodu, powodujcy utworzenie komponentu (obiektu). W rzeczywistoci na podstawie kodu VCL wykonywany jest szereg innych procedur, ktre ostatecznie daj efekt w postaci utworzenia komponentu.

CLX
Opublikowanie Delphi 6 byo czym niezwykym ze wzgldu na wprowadzenie biblioteki CLX (Component Library for Cross-Platform), czyli midzyplatformowej biblioteki komponentw. Pojawienie si CLX wizane jest z wydaniem nowego rodowiska programistycznego Kylix, przeznaczonego dla systemu operacyjnego Linux. Programowanie w Kyliksie jest niezwykle podobne do programowania w Delphi ? nawet interfejs jest ten sam. Kylix rwnie ? tak jak Delphi ? wykorzystuje jzyk Object Pascal, co mogo troch dziwi w momencie edycji produktu. W kocu Linux jest platform, na ktrej krluje C++. W kadym razie w zwizku z pojawieniem si Kyliksa oraz biblioteki CLX przekroczona zostaa pewna bariera ? od tej pory moliwe jest tworzenie aplikacji zgodnych zarwno z systemem Windows, jak i Linuks.

Tworzenie aplikacji opartych na CLX


Aby stworzy aplikacj dziaajc w oparciu o CLX, wystarczy z menu File wybra polecenie New/CLX Application. Wwczas zmieniona zostanie zawarto palety komponentw oraz edytora kodu.

CLX a VCL
Sam akronim VCL moe dawa do zrozumienia, e mamy do czynienia z cakowicie wizualn bibliotek. W rzeczywistoci tak nie jest, bo VCL zawiera rwnie komponenty, ktre nie s widoczne, ale jednak odpowiadaj za wykonywanie jakich czynnoci. Piszc programy dziaajce w systemie Windows, korzystamy ze standardowych kontrolek tego systemu (chociaby kontrolka TButton), zawartych w bibliotece User32.dll lub CommCtrl32.dll. Natomiast w przypadku CLX komponenty z tej biblioteki s oparte na tzw. widgetach, zawartych w bibliotece Qt. Owa biblioteka zawiera niezalene 558 | S t r o n a

klasy, ktre swoim wygldem oraz funkcjonalnoci przypominaj standardowe kontrolki Windows. Sowo widget jest skrtem sw Window Gadget.

Architektura CLX
Architektura CLX jest nieco bardziej zoona ni VCL. Mamy tu bowiem kilka grup komponentw. Oprcz grupy wizualnej ? VisualCLX ? istniej jeszcze komponenty BaseCLX (moduy wsplne dla Delphi i Kyliksa), DataCLX (komponenty zapewniajce dostp do technologii dbExpress ? bdziemy o tym mwi w kolejnej czci ksiki) oraz NetCLX (technologie internetowe). VCL jest zbudowany na bazie kontrolek WinAPI, zawartych m.in. w bibliotekach User32.dll oraz ComCtrl32.dll. W przypadku CLX grupa komponentw VisualCLX jest oparta na bibliotece Qt. Hierarchia klas jest ona bardzo podobna do hierarchii komponentw VCL. Dodano par nowych klas, inne zamieniono miejscami, lecz starano si zachowa maksymaln kompatybilno pomidzy CLX i VCL ? tak, aby przenoszenie kodu odbywao si z jak najmniejszym nakadem pracy.

Windows a Linux
Mona by przypuszcza, e dla programisty tworzcego swoje aplikacje w jzyku Object Pascal nie ma znaczenia, na jakiej platformie bdzie uruchamiany w program. Oczywicie naley jednak pamita o pewnych elementach charakterystycznych dla danego systemu. Przykadem moe by konieczno zwracania uwagi na nazewnictwo plikw w Linuksie ? w systemie tym rozrniane s np. nazwy moduw na licie uses.

W systemie Linux uywany jest inny znak separatora katalogw. W Windows jest to znak backslash (\), a w Linuksie ? slash (/). Waciwy dla konkretnej platformy znak mona odczyta ze staej PathSeparator. Pamitasz, jak podczas omawiania bibliotek DLL wspominaem o konwencjach wywoania stdcall, safecall i cdecl? W Kyliksie naley zmieni t klauzul na cdecl, gdy tylko taka forma jest prawidowa. Tworzc programy w Kyliksie, naley unika moduw oraz funkcji charakterystycznych dla Windows ? np. moduw Windows, ActiveX, ComServ, ComObj itp. ? gdy nie odnajdziemy tam takich technologii jak BDE, COM czy ActiveX. Bardzo wane jest zapamitanie, e w Linuksie nie moemy korzysta z komunikatw. Niedopuszczalne jest zatem wysyanie komunikatw czy ich przechwytywanie ? np. WM_CHAR itp. Wikszo klas nazywa si tak samo. Pamitaj jednak, e klasa TWinControl w CLX nazywa si TWidgetControl. Ze wzgldw kompatybilnoci zachowano jednak rwnie klas TWinControl, ktra jest rwnowana klasie TWidgetControl. Unikaj stosowania funkcji typowych dla Windows: Win32Check, RaiseLastWin32Error. 559 | S t r o n a

W systemie Linux nie ma Rejestru ? nie moesz wic korzysta z klasy TRegistry. Nazwy moduw CLX rozpoczynaj si od litery Q ? np. QControls. System Linux nie uywa liter do okrelania dyskw, tak jak ma to miejsce w Windows. Wszystko opiera si na charakterystycznym systemie plikw, gdzie istniej tylko katalogi gwne ? np. /usr

Kompilacja warunkowa
Zarwno w Delphi, jak i w Kyliksie istnieje moliwo wprowadzenia charakterystycznego dla danej platformy kodu. Wszystko dziki tzw. symbolom kompilacji warunkowej. {$IFDEF LINUX} // kod dla Linuksa {$ENDIF}

Dla systemu Linux takim symbolem jest LINUX, a dla Windows moe to by WIN32 i MSWINDOWS: {$IFDEF WIN32} // kod dla Windows {$ENDIF}

Dziki takiemu prostemu rozwizaniu mona wprowadza charakterystyczne dla danej platformy elementy kodu.

Rodzaje komponentw
Komponenty w Delphi dziel si na wizualne oraz niewizualne ? bynajmniej nie chodzi tutaj o waciwo Visible, ktrej zmiana na True powoduje ukrycie komponentu.

Komponenty wizualne
Nieprzypadkowo dla okrelenia komponentu wizualnego czsto uywam w tej ksice sowa kontrolka (control). Naley zda sobie spraw z tego, e kontrolka to obiekt wizualny, bdcy w stanie odbiera komunikaty od uytkownika. Kontrolki maj waciwoci mogce okreli ich pooenie wzgldem projektanta formularzy. Mog by take aktywowane (focus). Komponenty wizualne 560 | S t r o n a

wywodz si z klasy TControl. Przykadami kontrolek wizualnych s TButton, TLabel, TListView, TComboBox itp.

Komponenty niewizualne
Komponentem jest kady obiekt, mogcy znale si w palecie komponentw. Niektre z tych obiektw s widoczne po uruchomieniu programu, niektre jednak nie. Komponenty niewidoczne wywodz si z klasy TComponent, lecz mimo tego, e ich nie wida, peni inn funkcj, czsto o wiele waniejsz ni rola obiektw wizualnych. Przykadem komponentw niewidocznych jest komponent TTimer, TOpenDialog (oglnie wszystkie komponenty z palety Dialogs) oraz komponenty z palety Indy.

Komponenty graficzne
Niektre komponenty Delphi mimo swego charakteru wizualnego nie posiadaj zdolnoci interakcji z uytkownikiem ? nie mog wic sta si aktywne. Nie posiadaj take uchwytu, a ich klas bazow jest TGraphicControl. Przykadem takich komponentw s TPaintBox i TImage.

Hierarchia komponentw
Na rysunku 14.1 przedstawiona zostaa hierarchia komponentw VCL. Naturalnie jest to tylko fragment ogromnej pajczyny klas.

Rysunek 14.1. Hierarchia komponentw VCL W CLX ze wzgldw kompatybilnoci starano si zachowa identyczne nazewnictwo klas. Rnica pojawia si w klasie TWinControl, ktra w CLX nosi nazw TWidgetControl. Pamitaj o tym, e w CLX nie ma klasy TRegistry, lecz moesz korzysta z klasy TRegINIFile. 561 | S t r o n a

TObject
Klasa TObject jest podstawowym fundamentem caej struktury VCL oraz CLX. Zawiera wiele podstawowych metod, obecnych we wszystkich klasach. Wszystko dziki dziedziczeniu ? w czasie, gdy pozostae klasy dziedzicz po klasie TObject, przejmuj jej wszystkie metody. Klasa ta odpowiada za tak podstawowe czynnoci, jak:

tworzenie i usuwanie instancji komponentu, alokacj i zwalnianie potrzebnej pamici, obsug komunikatw, zwracanie informacji na temat rodzaju klasy, nazwy itp.

Metody klasy TObject Klasa TObject definiuje jedynie metody, ktre mog by pniej uywane przez wszystkie inne kontrolki VCL. Spis najwaniejszych metod klasy TObject znajduje si poniej.

ClassName

class function ClassName: ShortString;

Owa funkcja zwraca nazw klasy ? przykad: ShowMessage(Button1.ClassName);

Powyszy kod spowoduje wywietlenie rzeczywistej nazwy klasy, czyli TButton.

ClassNameIs

class function ClassNameIs(const Name: string): Boolean;

562 | S t r o n a

Funkcja sprawdza, czy podana w parametrze klasa odpowiada tej, z ktrej jest wywoywana funkcja. Najlepiej objani to na przykadzie: Button1.ClassNameIs(?TButton?); // funkcja zwrci True Button1.ClassNameIs(?TObject?); // funkcja zwrci False

ClassInfo

class function ClassInfo: Pointer;

Funkcja zwraca wskanik do tablicy zawierajcej informacje takie jak typ obiektu, waciwoci itp. Wrcimy do tego nieco pniej.

ClassParent

class function ClassParent: TClass;

Informacja o klasie bazowej dla naszego komponentu jest zwracana przez funkcj ClassParent. Dziki owej funkcji moemy odwoa si do innych waciwoci klasy bazowej, nie znajc jej podczas tworzenia programu.

ClassType

function ClassType: TClass;

Funkcja dziaa podobnie jak ClassParent. Jedyna rnica polega na tym, e zwraca ona informacje na temat klasy, z ktrej jest wywoywana ? np.: ShowMessage(Button1.ClassType.ClassName); 563 | S t r o n a

Po wykonaniu takiego kodu w okienku wywietlony zostanie napis TButton. Inna sprawa, e taki sam efekt uzyskamy, korzystajc z funkcji ClassName.

FieldAddress

function FieldAddress(const Name: ShortString): Pointer;

Funkcja FieldAddress zwraca wskanik do waciwoci znajdujcej si w pamici. Nazwa waciwoci musi zosta podana w parametrze.

MethodAddress

class function MethodAddress(const Name: ShortString): Pointer;

Funkcja dziaa podobnie jak FieldAddress, tyle e ta zwraca wskanik do metody umieszczonej w pamici ? nie za waciwoci. Wicej opisw metod klasy TObject moesz znale w systemie pomocy Delphi lub (w jzyku polskim) pod adresem http://4programmers.net/Delphi

TPersistent
Klas drug po TObject w caej hierarchii jest TPersistent. Oczywicie wikszo metod dziedziczy po TObject, ale wprowadza take swoje metody. Klasy TPersistent moesz uy jako klasy bazowej w momencie, gdy deklarujesz klasy, ktre nie maj by komponentem. Jaka jest wic rnica midzy TObject a TPersistent? Ot klasa TPersistent moe by przodkiem dla wszystkich klas mogcych zapisywa wartoci do waciwoci, ktre nastpnie mog by przechowywane w pliku *.dfm (lub *.xfm ? dla Kyliksa).

564 | S t r o n a

Metoda Assign

procedure Assign(Source: TPersistent); virtual;

Metoda Assign, obecna w klasie TPersistent, powoduje skopiowanie danych (waciwoci) z jednego obiektu do drugiego.

Metoda AssignTo

procedure AssignTo(Dest: TPersistent); virtual;

AssignTo dziaa odwrotnie ni procedura Assign. Powoduje skopiowanie danych do innego

obiektu, okrelonego w parametrze Dest.

Metoda DefineProperties

procedure DefineProperties(Filer: TFiler); virtual;

Procedura DefineProperties umoliwia przechowanie w pliku *.dfm dodatkowych danych ? tym pojciem zajmiemy si kolejnym rozdziale.

TComponent
Klasa TComponent, jak mona wywnioskowa z nazwy, jest przodkiem wszystkich komponentw. Komponenty dziedziczce po TComponent mog:

by zintegrowane z IDE Delphi, w tym mog by umieszczone na palecie komponentw; jeden obiekt typu TComponent moe posiada inny obiekt (by jego rodzicem); komponenty mog by konwertowane na obiekty COM lub kontrolki ActiveX.

Obiekty COM s wynalazkiem firmy Microsoft, std obecne s jedynie na platformie Windows. Klasa TComponent wprowadza szereg nowych waciwoci oraz metod, obecnych pniej we 565 | S t r o n a

wszystkich komponentach (w kocu wszystkie komponenty pochodz od klasy TComponent).

Waciwoci klasy TComponent Klasa TComponent zawiera wiele ciekawych waciwoci, ktre przedstawiem poniej.

ComponentCount

property ComponentCount: Integer;

Waciwo ComponentCount zwraca liczb obiektw umieszczonych na danym komponencie (rysunek 14.2.): procedure TMainForm.Button1Click(Sender: TObject); begin Application.MessageBox(PChar('Na formularzu znajduj si ' + IntToStr(ComponentCount) + ' komponenty), 'Tak', MB_OK) end;

Rysunek 14.2. Program demonstrujcy znaczenie waciwoci ComponentCount

ComponentIndex

property ComponentIndex: Integer;

Inna waciwo klasy TComponent ? Components[] to tablica komponentw. Do konkretnego 566 | S t r o n a

obiektu mona si odwoa, podajc jego indeks. Taki indeks natomiast jest zwracany przez waciwo ComponentIndex. ShowMessage(IntToStr( Button1.ComponentIndex));

W powyszym przypadku w okienku pojawi si cyfra 1 jako numer identyfikacyjny dla komponentu Button1.

Components

property Components[Index: Integer]: TComponent;

Jak wspomniaem powyej, jest to tablica wszystkich elementw (komponentw) umieszczonych na danym obiekcie. Do konkretnej kontrolki mona si odwoa, podajc w nawiasie kwadratowym jej indeks: Components[1].Free;

Powyszy kod spowoduje zniszczenie obiektu okrelonego numerem 1.

ComponentState

property ComponentState: TComponentState;

Sygnalizuje stan komponentu. Moe zwrci wartoci takie, jak przedstawione w tabeli 14.1. Tabela 14.1. Moliwe elementy typu TComponentState Element Opis

csDesigning Komponent jest w trakcie projektowania, np. jego waciwoci s zmieniane csDestroying Komponent za chwil zostanie zniszczony

567 | S t r o n a

csFixups csLoading csReading csWriting

Komponent jest podczony do innego komponentu znajdujcego si na innym formularzu Komponent jest wanie adowany (tworzony) Komponent odczytuje swoje waciwoci ze strumienia (pliku *.dfm) Komponent zapisuje waciwoci do strumienia

Name

property Name: TComponentName;

Definiuje unikatow w ramach formularza nazw dla komponentu. Warto jest przeznaczona zarwno do zapisu, jak i do odczytu.

Owner

property Owner: TComponent;

Waciwo Owner jest wskazaniem obiektu-rodzica. Jeeli przykadowo komponent kkjest umieszczony na formularzu Form1, to dziki takiej konstrukcji: Button1.Owner...

mona odwoa si do formularza Form1.

Metody klasy TComponent Nowych metod wprowadzonych wraz z klas TComponent jest bardzo wiele. Wikszo z nich by moe nigdy nie zostanie przez Ciebie uyta, gdy su np. do okrelenia pewnego stanu, w jakim ma znajdowa si komponent.

568 | S t r o n a

Na pewno najwaniejsz metod klasy TComponent jest konstruktor, ktry w odrnieniu od klasy TObject posiada dodatkowy parametr ? rodzica, czyli obiekt, na ktrym ma zosta utworzony nowy komponent. constructor Create(AOwner: TComponent); virtual;

Utworzony w ten sposb konstruktor powinien zosta zwolniony metod Free. Do zwalniania komponentu nigdy nie uywaj metody Destroy. Procedura Free przed rzeczywistym zwolnieniem sprawdza, czy obiekt nie zosta ju wczeniej zwolniony, co zapobiega pojawieniu si bdu typu Access Violation. Oto przykad utworzenia komponentu typu TButton w sposb dynamiczny: procedure TForm1.btnMakeClick(Sender: TObject); var MyButton : TButton; begin MyButton := TButton.Create(Form1); MyButton.Parent := Form1; // okrelenie rodzica MyButton.Caption := 'Przycisk 1'; end;

Rodzicem komponentu ustanawiamy formularz Form1. Jeeli nie okrelilimy dokadniejszych danych ? np. pooenia komponentu ? zostanie on umieszczony w lewym, grnym rogu formularza. Zwr uwag, e w powyszym kodzie nie ma instrukcji zwalniajcej komponent ? Free. Wszystko dziki temu, e rodzicem nowego obiektu zosta formularz ? Form1. Przy zwalnianiu (zamykaniu) okna program najpierw zwolni obiekty znajdujce si na nim, czyli m.in. nasz przycisk. Ciekaw metod klasy TComponent jest procedura FindComponent. Wspominam tu o niej, gdy by moe czsto bdziesz korzysta z jej zalet. Umoliwia ona bowiem odnalezienie jakiego komponentu na formularzu i odwoanie si do konkretnych jego metod ? oto przykad: procedure TMainForm.btnMakeClick(Sender: TObject); begin Randomize; TEdit(FindComponent('Edit' + IntToStr(Random(3)+1))).Text := 'Nowa warto'; end;

Powyszy kod spowoduje losowe odwoanie si do ktrej z trzech umieszczonych na formularzu kontrolek typu TEdit ki zmian jej waciwoci Text (rysunek 14.3).

569 | S t r o n a

Rysunek 14.3. Losowe odwoanie si do ktrej z kontrolek TEdit

TControl
Kolejn klas w hierarchii obiektw VCL jest klasa TControl. Reprezentuje ona komponenty wizualne (jest podstawow klas dla tego rodzaju komponentw). Kady obiekt typu TControl posiada dodatkowe waciwoci okrelajce pooenie komponentu: Top (pooenie w pionie), Left (pooenie w poziomie), Width (szeroko) i Height (wysoko).

Waciwoci klasy TControl Najwaniejsze z waciwoci (ktrych nazwy moesz ju pamita, gdy s one wywietlane w Inspektorze Obiektw) przedstawiem w tabeli 14.2. Tabela 14.2. Najwaniejsze waciwoci klasy TControl Waciwo Align AutoSize Caption Opis Okrela wyrwnanie komponentu wzgldem obiektu-rodzica Okrela wyrwnanie komponentu wzgldem obiektu-rodzica Tytu komponentu (tekst wywietlany na obiekcie)

ClientHeight Rozmiar obszaru roboczego (wysoko) ClientWidth Rozmiar obszaru roboczego (szeroko) Color Cursor Enabled Font Hint Kolor ta obiektu Kursor wywietlany po umieszczeniu wskanika myszy nad obiektem Okrela, czy obiekt jest aktywny czy te nie Czcionka uywana przez komponent Wskazwka (etykietka podpowiedzi), pokazywana po umieszczeniu kursora nad

570 | S t r o n a

obiektem ShowHint Visible Waciwo okrela, czy podpowiedzi maj by wywietlane Waciwo okrela, czy komponent ma by widoczny podczas dziaania programu

Procedury i funkcje klasy TControl maj na celu przede wszystkim ustawienie danej wartoci komponentu lub odczytanie jej. Owa klasa posiada take wiele metod nakazujcych w konsekwencji wystpienie pewnego zdarzenia (klasa TControl wprowadza zdarzenia, dziki ktrym jestemy w stanie zareagowa na konkretne sytuacje).

TWinControl i TWidgetControl
Klasy TWinControl (VCL) oraz TWidgetControl (CLX) s bazowymi klasami dla kontrolek takich, jak przycisk (TButton) i lista rozwijalna (TComboBox). Owe klasy s klasami bazowymi dla bardzo wielu kontrolek w Delphi. Charakteryzuj si nastpujcymi waciwociami:

Umoliwiaj wywietlanie tekstu (np. TMemo). Umoliwiaj odbieranie od uytkownika zdarze, reagowanie np. na nacinicie przycisku czy wpisywanie znakw za pomoc klawiatury. Kontrolki mog by rodzicem innych obiektw. Kontrolki pochodzce z tych klas posiadaj uchwyt (VCL) lub widget (CLX).

Waciwoci klas TWinControl i TWidgetControl Obie klasy definiuj wiele waciwoci, majcych na celu zmodyfikowanie wygldu danej kontrolki. Za przykad moe tu posuy waciwo BevelKind, ktra okrela styl rysowania ramki dla komponentu. Do podobnych waciwoci tego typu nale take: BevelEdges, BevelInner, BevelOuter, BevelWidth i BorderWidth. Ciekaw waciwoci jest take Brush, ktra okrela kolor i styl wypeniajcego wntrze komponentu.

Zdarzenia Zdarzenia klasy TWinControl s zwizane z uzyskiwaniem dostpu do obiektu (aktywacja) czy te wprowadzaniem tekstu z klawiatury. S to zdarzenia: OnDockDrop, OnDockOver, OnEnter, 571 | S t r o n a

OnExit, OnKeyDown, OnKeyPress i OnKeyUp.

Klasy TCustom
Wiele klas VCL ? np. TMemo ? posiada odpowiedniki w postaci klas bazowych, ktre rozpoczynaj si przedrostkiem Custom, np. TCustomMemo. Takie klasy peni wycznie rol klas bazowych dla nowych komponentw i nie s w praktyce wykorzystywane. Np. komponent TDBMemo take wywodzi si z klasy TCustomMemo ? dziki temu rozwizaniu programici oszczdzaj wiele wierszy kodu, dziedziczc w zwyky sposb potrzebne metody i waciwoci.

TGraphicControl
Jest to raczej specyficzny rodzaj kontrolek, a mianowicie komponent graficzny. Komponenty takie s oczywicie komponentami wizualnymi, ale nie mog by aktywne. Nie mog take peni roli rodzica innych komponentw. miao mona powiedzie, e kontrolki tego typu s komponentami nieaktywnymi. Natomiast kada kontrolka typu TGrpaphicControl ma przyporzdkowane ptno (Canvas), ktrego odwieenie nastpuje za pomoc wywoania metody Repaint.

Budowa komponentu
Komponent w rzeczywistoci jest klas. Jest jednak klas do specyficzn, posiadajc okrelon budow, bez ktrej taka klasa nie byaby po prostu komponentem.

Waciwoci
W klasie waciwoci su do przechowywania w pamici pewnej wartoci. W przypadku komponentw wartoci standardowo wprowadzone w Inspektorze Obiektw s zapisywane w pliku *.dfm. Jednake aby waciwoci byy widoczne w Inspektorze Obiektw, naley uy specjalnej sekcji ? published. O klasach mwiem ju w rozdziale 3., lecz o sekcji published jedynie wwczas wspomniaem. Jeli tworzysz komponenty i chcesz doda do nich waciwoci, kod w sekcji published musi wyglda tak: TMojKomponent = class(TLabel) private 572 | S t r o n a

FText : String; procedure DoIt(Value : String); published properties Text : String read FText write DoIt; end;

Waciwoci w klasie musimy poprzedza sowem kluczowym properties. Powyszy kod oznacza, e waciwo Text bdzie typu String, a jej odczytanie bdzie rwnowane z odczytaniem danych ze zmiennej FText. Wszystko dziki klauzuli read, po ktrej nastpuje nazwa zmiennej umieszczonej w sekcji private (nie jest konieczne umieszczenie zmiennej FText w sekcji public). Natomiast w przypadku, gdy uytkownik zechce przypisa jak warto waciwoci Text, wywoaprocedur DoIt. Nowa warto zostanie przekazana do procedury DoIt w parametrze Value. Warto, aby przyzwyczai si do takiej konstrukcji w przypadku komponentw, gdy w przyszoci czsto bdziemy j stosowali. Chcc uczyni dan waciwo przeznaczon tylko do zapisu lub tylko do odczytu, wystarczy pomin jedn z klauzul ? albo read, albo write. Istnieje moliwo dodania na samym kocu sowa kluczowego default, ktre powoduje ustawienie domylnej wartoci waciwoci.

Rodzaje waciwoci Pracujc ju jaki czas z Delphi, zapewne zauwaye, e nie wszystkie waciwoci w Inspektorze Obiektw s przedstawione tak samo. Niektre proste waciwoci su jedynie do wpisywania nowej wartoci i odczytywania jej. Z kolei inne waciwoci s przedstawione w postaci listy rozwijalnej. Tabela 14.3 przedstawia rne typy waciwoci. Tabela 14.3. Typy waciwoci Typy waciwoci Opis

Wyliczeniowa

Waciwo wyliczeniowa prezentowana jest w Inspektorze Obiektw w formie listy rozwijalnej (czenie z typem Boolean) ? rysunek 14.4 Waciwo zbiorowa (set) jest prezentowana w formie listy rozwijalnej. Wartoci w zbiorze mona zmienia na True lub False, w zalenoci od tego, czy waciwo przynaley do zbioru czy te nie (rysunek 14.5) Czasami zdarza si, e waciwo danego komponentu jest konkretnym obiektem (klas). Tak jest np. z waciwoci Lines komponentu TMemo, ktra jest waciwoci 573 | S t r o n a

Zbiorowa

Obiektowa

typu TStringList. Wwczas przy prbie edycji otwierany zostaje nowy edytor

Rysunek 14.4. Waciwo wyliczeniowa

Rysunek 14.5. Waciwo zbiorowa

Zdarzenia
Zdarzenia su do zaprogramowania okrelonej reakcji w momencie zajcia jakiego wydarzenia (np. kliknicia mysz). Podczas projektowania wasnych komponentw do czsto bdziesz zmuszony do przypisywania w kodzie programu jakiego zdarzenia, tzw. procedury zdarzeniowej. Procedura zdarzeniowa musi mie okrelone parametry, zalene od rodzaju zdarzenia. Przykadowo procedura zdarzeniowa dla zdarzenia OnClick musi mie parametr Sender typu TObject. Zatem deklaracja takiej procedury bdzie wyglda tak: private procedure MyOnClick(Sedner: TObject); end; ... procedure TForm1.Button1Click(Sender: TObject); var MyButton : TButton; begin MyButton := TButton.Create(Form1); 574 | S t r o n a

MyButton.Parent := Form1; MyButton.OnClick := MyOnClick; // przypisanie procedury zdarzeniowej end; ...

W przypadku, gdyby procedura MyOnClick nie posiadaa parametru Sender, prba kompilacji programu zakoczyaby si komunikatem o bdzie: [Error] Unit1.pas(32): Incompatible types: 'Parameter lists differ'.

Metody
Przypominam, e metody to inaczej procedury i funkcje zawarte w klasie. Aby owe metody byy (w przypadku komponentw) widoczne dla uytkownika komponentu, naley umieci je w sekcji public.

RTTI
Zajmiemy si teraz pojciem zwanym RTTI (Run Time Type Information), ktre umoliwia nam dostp do informacji na temat waciwoci, zdarze poszczeglnych komponentw ? wszystko podczas dziaania skompilowanej aplikacji.

Waciwoci obiektu
Funkcje, z ktrych bdziemy korzysta, znajduj si w pliku TypInfo.pas ? dodaj wic do listy uses modu TypInfo. W gruncie rzeczy pobieranie listy waciwoci danego obiektu jest proste ? wystarczy skorzysta z funkcji GetPropList. W rzeczywistoci okazuje si, e czeka nas wiele pracy z wskanikami. Funkcja GetPropList w module TypInfo jest zadeklarowana nastpujco: function GetPropList(TypeInfo: PTypeInfo; TypeKinds: TTypeKinds; PropList: PPropList): Integer;

Pierwszy parametr tej funkcji moe zawiera wskazanie komponentu, ktrego bdzie dotyczy operacja ? w to miejsce moemy np. wstawi instrukcj Button1.ClassInfo. Drugi parametr jest

575 | S t r o n a

pewnego rodzaju filtrem. Okrela, jakie waciwoci chcemy wywietli. Jest to waciwo typu zbiorowego, zadeklarowana nastpujco: type TTypeKind = (tkUnknown, tkInteger, tkChar, tkEnumeration, tkFloat, tkString, tkSet, tkClass, tkMethod, tkWChar, tkLString, tkWString, tkVariant, tkArray, tkRecord, tkInterface, tkInt64, tkDynArray); TTypeKinds = set of TTypeKind;

Moemy poda, jakie waciwoci chcemy wywietli. Najlepszym rozwizaniem jest wstawienie jako drugiego parametru sowa tkProperties, ktre obejmuje wszystkie waciwoci. Podsumowujc, wywoanie funkcji GetPropList moe wyglda tak: GetPropList(btnGet.ClassInfo, tkProperties, PropList);

Zakadamy, e ostatni parametr ? PropList ? jest typu PPropList. Z kolei typ PPropList jest zadeklarowany nastpujco: PPropList = ^TPropList; TPropList = array[0..16379] of PPropInfo;

A zatem okazuje si, e PPropList jest wskazaniem typu TPropList, ktry z kolei jest tablic PPropInfo: PPropInfo = ^TPropInfo; TPropInfo = packed record PropType: PPTypeInfo; GetProc: Pointer; SetProc: Pointer; StoredProc: Pointer; Index: Integer; Default: Longint; NameIndex: SmallInt; Name: ShortString; end;

Wiem, wiem, e to wszystko jest zagmatwane ? jeden typ jest wskazaniem drugiego typu itp., ale musisz si przyzwyczai, e cae VCL w ten wanie sposb jest zbudowane. Peny kod programu zosta przedstawiony w listingu 14.1, a program w trakcie dziaania ? na rysunku 14.6. Listing 14.1. Peny kod rdowy moduu

576 | S t r o n a

unit MainFrm; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls, Grids, ValEdit; type TMainForm = class(TForm) btnGet: TButton; vleValue: TValueListEditor; procedure btnGetClick(Sender: TObject); private { Private declarations } public { Public declarations } end; var MainForm: TMainForm; implementation {$R *.dfm} uses TypInfo; procedure TMainForm.btnGetClick(Sender: TObject); var PropList : PPropList; i : Integer; begin GetMem(PropList, SizeOf(PropList^)); // zarezerwuj pami try // pobierz list waciwoci GetPropList(btnGet.ClassInfo, tkProperties, PropList); for I := 0 to High(PropList^) ?1 do begin if (PropList^[i] <> nil) then vleValue.InsertRow(PropList^[i].Name, PropList^[i].PropType^.Name, True) end; finally FreeMem(PropList); end; end; 577 | S t r o n a

end.

Rysunek 14.6. Waciwoci komponentu TButton Wywoujc funkcj GetPropList w ten sposb: GetPropList(btnGet.ClassInfo, tkProperties + [tkMethod], PropList);

spowodujemy, e pod uwag zostan wzite take metody danego obiektu.

Dokadniejsze informacje o obiekcie


W poprzednim podpunkcie omwiem zagadnienie zwizane z pobieraniem informacji na temat waciwoci danego obiektu. Tym razem zajmiemy si pobraniem informacji na temat samego obiektu (nazwa klasy, nazwa moduu itp.). Moemy to zrobi za pomoc funkcji GetTypeData, ktra zdefiniowana jest nastpujco: function GetTypeData(TypeInfo: PTypeInfo): PTypeData;

Funkcj mona wywoa np. w ten sposb: 578 | S t r o n a

ClassInfo := GetTypeData(btnGet.ClassInfo);

Dziki temu rekord ClassInfo bdzie zawiera informacje na temat komponentu btnGet (typu TButton). Rekord ClassInfo jest rekordem typu PTypeData, wygldajcym nastpujco: PTypeData = ^TTypeData; TTypeData = packed record case TTypeKind of tkUnknown, tkLString, tkWString, tkVariant: (); tkInteger, tkChar, tkEnumeration, tkSet, tkWChar: ( OrdType: TOrdType; case TTypeKind of tkInteger, tkChar, tkEnumeration, tkWChar: ( MinValue: Longint; MaxValue: Longint; case TTypeKind of tkInteger, tkChar, tkWChar: (); tkEnumeration: ( BaseType: PPTypeInfo; NameList: ShortStringBase)); tkSet: ( CompType: PPTypeInfo)); tkFloat: ( FloatType: TFloatType); tkString: ( MaxLength: Byte); tkClass: ( ClassType: TClass; ParentInfo: PPTypeInfo; PropCount: SmallInt; UnitName: ShortStringBase; {PropData: TPropData}); tkMethod: ( MethodKind: TMethodKind; ParamCount: Byte; ParamList: array[0..1023] of Char {ParamList: array[1..ParamCount] of record Flags: TParamFlags; ParamName: ShortString; TypeName: ShortString; end; ResultType: ShortString}); tkInterface: ( IntfParent : PPTypeInfo; { ancestor } IntfFlags : TIntfFlagsBase; Guid : TGUID; 579 | S t r o n a

IntfUnit : ShortStringBase; {PropData: TPropData}); tkInt64: ( MinInt64Value, MaxInt64Value: Int64); end;

Program wykorzystujcy powyszy rekord przedstawiony jest na rysunku 14.7. Sam kod rdowy znajduje si w listingu 14.2.

Rysunek 14.7. Informacje na temat obiektu TButton Listing 14.2. Kod rdowy moduu unit MainFrm; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls, Grids, ValEdit; 580 | S t r o n a

type TMainForm = class(TForm) btnGet: TButton; vleValues: TValueListEditor; procedure btnGetClick(Sender: TObject); private { Private declarations } public { Public declarations } end; var MainForm: TMainForm; implementation {$R *.dfm} uses TypInfo; procedure TMainForm.btnGetClick(Sender: TObject); var ClassInfo : PTypeData; begin ClassInfo := GetTypeData(btnGet.ClassInfo); vleValues.InsertRow('Nazwa klasy', ClassInfo.ClassType.ClassName, True); vleValues.InsertRow('Klasa bazowa', ClassInfo.ClassType.ClassParent.ClassName, True); vleValues.InsertRow('Modu', ClassInfo.UnitName, True); vleValues.InsertRow('Liczba waciwoci', IntToStr(ClassInfo.PropCount), True); vleValues.InsertRow('Liczba parametrw', IntToStr(ClassInfo.ParamCount), True); vleValues.InsertRow('Rozmiar', IntToStr(btnGet.InstanceSize), True); end; end.

Podsumowanie
581 | S t r o n a

Niniejszy rozdzia powicony by bardziej teoretycznym zagadnieniom, zwizanym z komponentami i z bibliotek VCL. Przybliyem w nim budow samej biblioteki VCL, wspomniaem te o podstawowych klasach, na ktrych opiera si owa biblioteka. Nie zapomniaem take o CLX. Praktycznym tworzeniem komponentw w Delphi zajmiemy si w kolejnym rozdziale. Zaczniki:

Listingi_14.zip (7.88 kB)

Rozdzia 15

Edytuj Historia Przenie Obserwuj

Tworzenie komponentw
Poprzedni rozdzia stanowi wprowadzenie do technik tworzenia komponentw. Omwiem w nim rol VCL i CLX w programowaniu w Delphi oraz hierarchi klas. Przyznam, mogo to by nieco nudne ze wzgldu na du dawk wiedzy teoretycznej. Tym razem zajmiemy si ju programowaniem wasnych projektw i zastosowaniem ich w praktyce.

Spis treci 1 Tworzenie nowego komponentu 2 Edycja kodu 3 Konstruktory i destruktory 4 Waciwoci 4.1 Klauzula default 4.2 Klauzula stored 4.3 Klauzula nodefault 4.4 Waciwo wyliczeniowa 4.5 Waciwo zbiorowa 582 | S t r o n a

5 Zdarzenia 5.1 Definiowanie wasnych zdarze 6 Ikona dla komponentw 7 Przykadowy komponent 7.1 Oglny zarys klasy 7.2 Komunikaty 7.3 Kod rdowy komponentu 8 Instalacja komponentw 9 Demonstracja moliwoci komponentu TURLabel 10 Zachowanie komponentu 11 Komponenty graficzne 11.1 Oglny zarys klasy komponentu 11.2 Kod rdowy komponentu 12 Pakiety komponentw 13 Podsumowanie

W tym rozdziale:

wykorzystasz w sposb praktyczny wiedz na temat komponentw; zaprojektujesz swj pierwszy komponent ? TURLabel; nauczysz si instalowa komponenty; zaprojektujesz komponent graficzny.

Tworzenie nowego komponentu


Ju duszy czas pracujesz z Delphi, powiniene wic zauway istnienie menu Component. Nas na tym etapie zainteresuj jedynie dwie pozycje tego menu ? New Component i Install Component. Pierwsze polecenie suy do stworzenia nowego projektu komponentu, natomiast za pomoc drugiego moemy zainstalowa ju istniejcy komponent. Po wybraniu tej opcji na palecie komponentw zostanie utworzona nowa ikonka ? wwczas komponent bdzie dziaa jak zwyky komponent VCL. Zajmijmy si jednak tworzeniem nowego komponentu. Z menu Component wybierz New Component. Na ekranie zobaczysz wwczas takie okienko, jakie zostao przedstawione na rysunku 15.1.

583 | S t r o n a

Rysunek 15.1. Tworzenie nowego komponentu W pierwszym polu z listy rozwijalnej naley wybra komponent, ktry bdzie stanowi klas bazow dla naszego nowego obiektu. Ja z tej listy wybraem klase TLabel. Jeeli chcesz tworzy komponent od pocztku, wybierz pozycj TComponent. Jest to bazowa klasa dla wszystkich komponentw, zawierajca par potrzebnych czasem procedur i funkcji. W kolejnym polu ? Class Name ? musisz wpisa nazw nowego komponentu. Pamitaj, e wedle obowizujcej zasady naley poprzedzi nazw liter T. Pole Palette Page okrela, na jakiej palecie zostanie zainstalowany komponent. Unit file name to cieka do katalogu, w ktrym zostan zapisane nowe pliki z komponentem. Zawarto tego pola pozostawiem niezmienion. I wreszcie ostatnie pole okrela ciek do katalogu, gdzie znajdowa si bd potrzebne do kompilacji pliki komponentu. ${DELPHI} oznacza domyln ciek do pliku programu Delphi. Po naciniciu przycisku Install komponent zostanie zainstalowany na wybranej palecie, natomiast po naciniciu przycisku OK w edytorze kodu zostanie jedynie wywietlona zawarto kodu komponentu.

Edycja kodu
Po wykonaniu operacji opisanych w poprzednim punkcie w edytorze kodu powinien zosta wywietlony taki kod, jak w listingu 15.1. Listing 15.1. Podstawowy kod komponentu TURLabel unit URLabel; interface 584 | S t r o n a

uses Windows, Messages, SysUtils, Classes, Controls, StdCtrls; type TURLabel = class(TLabel) private { Private declarations } protected { Protected declarations } public { Public declarations } published { Published declarations } end; procedure Register; implementation procedure Register; begin RegisterComponents('Samples', [TURLabel]); end; end.

Kluczowe znaczenie ma procedura Register. Nastpuje w niej zarejestrowanie komponentu realizowane przez polecenie RegisterComponents. Pierwszy parametr oznacza nazw palety. Drugi parametr to nazwy komponentw do zainstalowania. Musz one by wpisane w nawiasie kwadratowym, gdy w tym wypadku wystpuj jako elementy tablicy. Taki zapis jest konieczny, gdy jeden modu ? w tym wypadku URLabel ? moe zawiera kilka klas (komponentw). Podczas wpisywania zakadki dla tworzonego komponentu nie musisz podawa nazwy zakadki ju istniejcej ? jeeli wpiszesz nazw nieistniejcej zakadki, zostanie ona utworzona przez Delphi. Zwr uwag, e w klasie moesz uywa nowej sekcji ? published. Ta sekcja moe zawiera metody, ktre bd wywietlone w Inspektorze Obiektw.

Konstruktory i destruktory
Komponenty, podobnie jak zwyke klasy, posiadaj konstruktory oraz destruktory. Nie s to co 585 | S t r o n a

prawda obowizkowe elementy klasy, gdy nawet jeli ich nie zadeklarujemy, to i tak komponent bdzie posiada domylny konstruktor z klasy bazowej (najwyej ? TObject). W przypadku komponentu konstruktor winien mie parametr AOwner (typu TComponent), ktry zawieraby wskazanie komponentu (lub formularza) rodzica:

public constructor Create(AOwner : TComponent); override; destructor Destroy; override;

Jak widzisz, zarwno konstruktor, jak i destruktor s opatrzone klauzul override, co znaczy, e s przedefiniowane (bya o tym mowa w 3. rozdziale ksiki) ? moemy dla nich wpisa kod: constructor TURLabel.Create(AOwner : TComponent); begin inherited Create(AOwner); { podczas wywoywania konstruktora dla waciwoci przypisz domylny tekst } FURL := 'http://4programmers.net'; end; destructor TURLabel.Destroy; begin inherited; end;

W tym miejscu oprcz typowego utworzenia obiektu (dziaanie konstruktora) nastpuje rwnie przydzielenie wartoci dla waciwoci FURL.

Waciwoci
Waciwoci komponentw su do przechowywania wartoci rnych typw. Jednak jeli chcemy, aby waciwoci byy widoczne w Inspektorze Obiektw, naley uy kolejnej sekcji w klasie ? sekcji published: property URL : String read FURL write FURL;

Waciwo naley oznaczy sowem kluczowym property. W tym wypadku waciwo URL bdzie typu String. Zwyko si take definiowa zmienne pomocnicze, ktrych deklaracje umieszcza si w sekcji private. Nazwy owych zmiennych umieszczane s po sowach kluczowych read i write. 586 | S t r o n a

W ten sposb atwo utworzy jak waciwo tylko do odczytu (pozostawiajc jedynie klauzul read) lub tylko do zapisu (klauzula write), albo te zarwno do zapisu, jak i do odczytu (zarwno read, jak i write). Regu stao si ju specjalne nazewnictwo zmiennych pomocniczych, polegajce na dodawaniu przed nazw litery F.

Klauzula default
Podczas pisania samego komponentu istnieje moliwo okrelania domylnej wartoci za pomoc klauzuli default. property Count : Integer read FCount write FCount default 1;

W takim wypadku domylna warto waciwoci Count to 1. Klauzula default obejmuje jedynie typy liczbowe i zbiory (set) ? nie obsuguje natomiast acuchw String.

Klauzula stored
W przypadku typw typu Boolean uywa si klauzuli stored, a nie default ? np.: property DoIt : Boolean read FDoIt write FDoIt stored False;

Jeli nie skorzystamy z klauzuli stored, program za domyln warto dla waciwoci przyjmie True.

Klauzula nodefault
Klauzula nodefault jest przeciwiestwem defulat ? oznacza, e waciwo nie bdzie miaa wartoci domylnej. Stosowana jest jedynie w niektrych przypadkach, gdy klasa bazowa, z ktrej korzysta nasz komponent, posiada waciwo domyln. TBaseClass = class private FProp : Integer; published property Prop : Integer read FProp write FProp default 1; 587 | S t r o n a

end; TMyClass = class(TBaseClass) private FProp : Integer; published property Prop : Integer read FProp write FProp nodefault; end;

W takim wypadku klasa TMyClass take bdzie posiada waciwo Prop, tyle e nie bdzie ju ona zawieraa wartoci domylnej.

Waciwo wyliczeniowa
O waciwociach wyliczeniowych wspominaem w poprzednim rozdziale, lecz tym razem zajmiemy si ich deklaracj. Przypominam, e waciwo wyliczeniowa to lista rozwijalna z moliwymi wartociami (rysunek 15.2).

Rysunek 15.2. Waciwo wyliczeniowa W jzyku Object Pascal waciwoci wyliczeniowe to w rzeczywistoci deklaracje typu set of. type TSetCarOption = (coFiat, coOpel, coPorshe); TSetCarOptions = set of TSetCarOption; TMyClass = class(TComponent) private FCar : TSetCarOptions; protected { Protected declarations } 588 | S t r o n a

public { Public declarations } published property Car : TSetCarOptions read FCar write FCar default [coFiat]; end;

Zadeklarowalimy wanie waciwo wyliczeniow typu TSetCarOptions. W tym wypadku waciwo Car bdzie posiadaa domyln warto ? coFiat.

Waciwo zbiorowa
Przykad waciwoci zbiorowej przedstawiony zosta na rysunku 15.3.

Rysunek 15.3. Waciwo zbiorowa Deklarowanie waciwoci zbiorowej w rzeczywistoci wie si z zadeklarowaniem nowego typu danych (klasy). type TCarClass = class(TPersistent) private FFuel : Integer; FCarName : String; published property CarName : String read FCarName write FCarName; property Fuel : Integer read FFuel write FFuel; end; TMyClass = class(TComponent) private FCar : TCarClass; 589 | S t r o n a

protected { Protected declarations } public constructor Create(AOwner : TComponent); override; destructor Destroy; override; published property Car : TCarClass read FCar write FCar; end;

Nasz komponent bdzie posiada waciwo zbiorow Car. Po jej rozwiniciu w Inspektorze Obiektw pojawi si waciwoci z klasy TCarClass. Mamy do czynienia z now klas TCarClass i przed skorzystaniem z jej waciwoci naley j utworzy (wywoa konstruktor): constructor TMyClass.Create(AOwner: TComponent); begin inherited Create(AOwner); FCar := TCarClass.Create; // utworzenie klasy end; destructor TMyClass.Destroy; begin FCar.Free; // zwolnienie inherited; end;

Zdarzenia
Zdarzenia, podobnie jak waciwoci naszego komponentu, musz by deklarowane w sekcji published ? tak, aby byy widoczne w Inspektorze Obiektw ? np. w ten sposb: property OnMouseEnter : TNotifyEvent read FOnMouseEnter write FOnMouseEnter; property OnMouseLeave : TNotifyEvent read FOnMouseLeave write FOnMouseLeave;

Zapis ten jest do dziwny, gdy sprawia wraenie, jakbymy deklarowali zwyke waciwoci, tyle e typu TNotifyEvent. Typ TNotifyEvent jest zadeklarowany w module Classes: type TNotifyEvent = procedure (Sender: TObject) of object;

590 | S t r o n a

Mona powiedzie, e okrela on procedur zdarzeniow, ktra miaaby tylko jeden parametr ? Sender. Ju nieraz podczas lektury tej ksiki miae okazj zapozna si z moj opini o tym, e dana procedura zdarzeniowa musi mie parametr Sender. Teraz ju wiesz, e zdarzenie OnClick jest typu TNotifyEvent i std musi posiada parametr Sender.

Definiowanie wasnych zdarze


Czasem moe przytrafi si sytuacja, gdy w naszym komponencie bdzie wymagane zdarzenie zawierajce wicej ni jeden parametr ? po prostu bdzie umoliwiao uytkownikowi przekazanie jaki funkcji. W takim wypadku konieczne stanie si zadeklarowanie nowego typu zdarzenia: type TMyEvent = procedure(Sender: TObject; X : Integer) of object;

Taka skadnia ? tj. umieszczenie frazy of object na kocu ? jest obowizkowa. Teraz oprcz zwykego parametru Sender zdarzenie bdzie posiada take parametr X. Co z nim zrobimy? To ju zaley od programisty. Gdy chcesz, aby jakie dziaanie komponentu spowodowao wystpienie zdarzenia, moesz zastosowa po prostu taki kod: OnURLClick(Self, 1);

A zatem pierwszym parametrem Self zastpujemy wymagany parametr typu TObject, natomiast drugi parametr przekazany wraz ze zdarzeniem to cyfra 1.

Ikona dla komponentw


Po zainstalowaniu komponentu Delphi przydzieli dla niego swoj wasn ikon. W kadym wypadku mona to zmieni, przypisujc temu komponentowi osobn ikon. Wystarczy skorzysta z programu do tworzenia bitmap. Moesz uy np. programu Image Editor, ktrego ju wczeniej uywalimy przy okazji tworzenia rnych zasobw. W przypadku, gdy tworzysz zasoby dla komponentu, tworzysz plik .DCR, a nie .RES! Uwaaj na to! Take sowo ikona komponentu jest tylko terminem umownym, gdy w zasobie musisz stworzy NIE ikon, ale BITMAP o rozmiarach 2424 piksele. Stwrz wic nowy plik .DCR, a w nim bitmap 2424 piksele, na ktrej narysuj jaki obrazek. Cay 591 | S t r o n a

zasb nastpnie wcz do kodu komponentu za pomoc dyrektywy {$R ZASOBY.DCR}. Naley pamita o jeszcze jednej kwestii zwizanej z nazewnictwem bitmapy ? musi si ona nazywa tak samo, jak klasa (komponent), ktra wykorzystuje t ikon. A zatem jeeli komponent nazywa si TURLabel, bitmapa musi rwnie nosi nazw TURLABEL.

Przykadowy komponent
Waciwie moesz ju przystpi do napisania swojego pierwszego komponentu. Nie bdzie to nic nadzwyczajnego ? po prostu etykieta, ktra jest w rzeczywistoci odnonikiem do strony WWW. Jej kliknicie spowoduje otwarcie danego adresu.

Oglny zarys klasy


W przypadku, gdy masz ju otwarty nowy projekt, powiniene mie w edytorze ?czysty? kod tworzonego wanie komponentu. Kod naszej nowej klasy TURLabel powinien przedstawia si w nastpujcy sposb: TURLabel = class(TLabel) private FURL : String; FParametr : String; FOnMouseEnter, FOnMouseLeave : TNotifyEvent; FOnURLClick : TClickEvent; FDefaultFontColor : TColor; FDefaultFontStyle : TFontStyles; protected procedure CmMouseEnter(var Msg : TMessage); message CM_MOUSEENTER; procedure CmMouseLeave(var Msg : TMessage); message CM_MOUSELEAVE; procedure WMLButtonDown(var Msg : TMessage); message WM_LBUTTONDOWN; public constructor Create(AOwner : TComponent); override; destructor Destroy; override; published property URL : String read FURL write FURL; // URL do otwarcia property Parametr : String read FParametr write FParametr; // dodatkowy parametr property OnMouseEnter : TNotifyEvent read FOnMouseEnter write FOnMouseEnter; 592 | S t r o n a

property OnMouseLeave : TNotifyEvent read FOnMouseLeave write FOnMouseLeave; property OnURLClick : TClickEvent read FOnURLClick write FOnURLClick; end;

Komponent bdzie posiada standardow waciwo URL, okrelajc adres strony, ktra ma zosta otwarta. Zdarzenia OnMouseEnter oraz OnMouseLeave bd wystpoway w momencie umieszczenia kursora myszy nad obiektem lub przemieszczenia kursora znad obiektu. Dodatkowe zdarzenie OnURLClick wystpi, gdy uytkownik kliknie odnonik. Bdzie ono przekazywao do programu wsprzdne pozycji kursora X i Y, pobrane w momencie nacinicia przycisku myszy.

Komunikaty
Do przechwycenia momentu umieszczenia kursora nad obiektem niezbdne bdzie skorzystanie z komunikatw, a cilej mwic z CM_MOUSEENTER oraz CM_MOUSELEAVE. procedure TURLabel.CMMouseEnter(var Msg : TMessage); begin { jeeli wykorzystane jest zdarzenie FOnMouseEnter ? wywoaj je } if Assigned(FOnMouseEnter) then OnMouseEnter(Self); FDefaultFontColor := Font.Color; // pobierz do zmiennej kolor czcionki FDefaultFontStyle := Font.Style; // pobierz styl czcionki (pogrubiony, podkrelony) Cursor := crHandPoint; // zmie kursor Font.Color := clBlue; // zmie kolor czcionki Font.Style := Font.Style + [fsUnderline]; // dodaj podkrelenie end; procedure TURLabel.CmMouseLeave(var Msg : TMessage); begin { jeeli wykorzystane jest zdarzenie FOnMousLeave ? wywoaj je } if Assigned(FOnMouseLeave) then OnMouseLeave(Self); { przywr zapisane w zmiennej dane } Font.Color := FDefaultFontColor; Font.Style := FDefaultFontStyle; end;

Na samym pocztku po wykryciu wystpienia komunikatu generowane s zdarzenia OnMouseEnter oraz OnMouseLeave: 593 | S t r o n a

if Assigned(FOnMouseEnter) then OnMouseEnter(Self);

Od tego momentu obsug zdarzenia musi si zaj uytkownik komponentu. Oprcz tego umieszczenie kursora myszy w obszarze komponentu spowoduje zmian koloru czcionki na niebieski oraz dodanie podkrelenia. Do wykonania tego zadania niezbdne byo wczeniejsze zadeklarowanie pl FDefaultFontColor oraz FDefaultFontStyle. Po odsuniciu kursora znad obiektu przywrcone zostan domylne ustawienia koloru oraz kroju czcionki. Za pomoc metody Assigned na pocztku dokonuje si sprawdzenia, czy uytkownik komponentu wygenerowa owe zdarzenie; jeeli tak si stao, jest ono generowane.

Kod rdowy komponentu


Peny kod rdowy komponentu TURLabel zosta przedstawiony w listingu 15.2. Zwr uwag na obecno dyrektywy {$R ZASOBY.DCR}, za pomoc ktrej wczane s zasoby, co w konsekwencji prowadzi do zmiany ikony komponentu. Listing 15.2. Kod rdowy komponentu TURLabel { Copyright (c) 2002 by Adam Boduch 2002 } unit URLabel; interface uses Windows, Messages, SysUtils, Graphics, Classes, Dialogs, Controls, StdCtrls; {$R ZASOBY.DCR} type TClickEvent = procedure(Sender: TObject; X, Y : Integer) of object; TURLabel = class(TLabel) private FURL : String; FParametr : String; FOnMouseEnter, FOnMouseLeave : TNotifyEvent; FOnURLClick : TClickEvent; FDefaultFontColor : TColor; FDefaultFontStyle : TFontStyles; 594 | S t r o n a

protected procedure CmMouseEnter(var Msg : TMessage); message CM_MOUSEENTER; procedure CmMouseLeave(var Msg : TMessage); message CM_MOUSELEAVE; procedure WMLButtonDown(var Msg : TMessage); message WM_LBUTTONDOWN; public constructor Create(AOwner : TComponent); override; destructor Destroy; override; published property URL : String read FURL write FURL; // URL do otwarcia property Parametr : String read FParametr write FParametr; // dodatkowy parametr property OnMouseEnter : TNotifyEvent read FOnMouseEnter write FOnMouseEnter; property OnMouseLeave : TNotifyEvent read FOnMouseLeave write FOnMouseLeave; property OnURLClick : TClickEvent read FOnURLClick write FOnURLClick; end; procedure Register; implementation uses ShellAPI; constructor TURLabel.Create(AOwner : TComponent); begin inherited Create(AOwner); { podczas wywoywania konstruktora dla waciwoci przypisz domylny tekst } FURL := 'http://4programmers.net'; end; destructor TURLabel.Destroy; begin inherited; end; procedure TURLabel.WMLButtonDown(var Msg : TMessage); var P : TPoint; begin { w razie kliknicia komponentu otwrz program lub stron WWW, ktrej cieka 595 | S t r o n a

jest wpisana we waciwoci FURL } GetCursorPos(P); // pobranie wsprzdnych kursora myszy ShellExecute(0, 'open', PChar(FURL), PChar(FParametr), nil, SW_SHOW); if Assigned(FOnURLClick) then OnURLClick(Self, P.X, P.Y); // generowanie zdarzenia end; procedure TURLabel.CMMouseEnter(var Msg : TMessage); begin { jeeli wykorzystane jest zdarzenie FOnMouseEnter ? wywoaj je } if Assigned(FOnMouseEnter) then OnMouseEnter(Self); FDefaultFontColor := Font.Color; // pobierz do zmiennej kolor czcionki FDefaultFontStyle := Font.Style; // pobierz styl czcionki (pogrubiony, podkrelony) Cursor := crHandPoint; // zmie kursor Font.Color := clBlue; // zmie kolor czcionki Font.Style := Font.Style + [fsUnderline]; // dodaj podkrelenie end; procedure TURLabel.CmMouseLeave(var Msg : TMessage); begin { jeeli wykorzystane jest zdarzenie FOnMousLeave ? wywoaj je } if Assigned(FOnMouseLeave) then OnMouseLeave(Self); { przywr zapisane w zmiennej dane } Font.Color := FDefaultFontColor; Font.Style := FDefaultFontStyle; end; procedure Register; begin RegisterComponents('Samples', [TURLabel]); end; end.

Instalacja komponentw
Gdy mamy ju gotowy komponent, moemy go zainstalowa. wybierajc z menu Component polecenie Install Component. 596 | S t r o n a

Po wykonaniu tej czynnoci powiniene na ekranie ujrze okienko przedstawione na rysunku 15.4. Nas w tej chwili interesuje tylko pierwsza zakadka. Pierwsze pole musi zawiera ciek do pliku rdowego komponentu. Po naciniciu przycisku Browse moesz wskaza ten plik. To waciwie wszystko.

Rysunek 15.4. Okno wyboru komponentu Pozycja Package file name zawiera nazw pakietu, do ktrego zostanie dodany nasz komponent. W tym miejscu powinna znajdowa si cieka domylnego pakietu Borlanda. Na tym etapie nie musisz nic zmienia ? tworzeniem wasnych pakietw zajmiemy si nieco pniej. Nacinij przycisk OK w celu kontynuowania instalacji. Wywietlona zostanie zawarto pakietu dclusr.dpk Okno powinno wyglda tak, jak na rysunku 15.5.

Rysunek 15.5. Instalacja komponentu w pakiecie dclusr.dpk W oknie tym znajduje si lista komponentw nalecych do tego pakietu. Za pomoc przycisku Add moesz do tego pakietu doda kolejny komponent. Przyciskiem Remove moesz natomiast usun komponent z pakietu i tym samym go odinstalowa. Aby sfinalizowa proces instalacji, nacinij przycisk Compile. Po jego naciniciu Delphi sprbuje skompilowa modu zawierajcy komponent do postaci .dcu i w kocu doda ten komponent do palety komponentw na wybranej zakadce. Jeeli wszystko pjdzie dobrze, zobaczysz okienko informacyjne z komunikatem dotyczcym prawidowej instalacji komponentu. W razie wystpienia bdw kompilator otworzy zawarto moduu i wskae, gdzie nastpi bd podczas kompilacji. Jeeli tworzysz komponenty na potrzeby wielu uytkownikw, czyli rozpowszechniasz dany komponent np. w Internecie, nie musisz docza pliku rdowego komponentu ? wystarczy jedynie 597 | S t r o n a

forma skompilowana (plik .dcu). Ale uwaga! Jeeli Ty uywasz do tworzenia komponentu wersji 7 Delphi i nie udostpniasz jego kodu rdowego, a jedynie form skompilowan, to osoba chcca zainstalowa ten komponent na swoim komputerze take bdzie musiaa posiada wersj 7. W przeciwnym wypadku proces instalacji si nie powiedzie.

Demonstracja moliwoci komponentu TURLabel


Stworzymy teraz program demonstrujcy funkcjonalno komponentu TURLabel. Piszc komponenty i udostpniajc je szerszej publicznoci, powiniene zawsze docza wersj prbn swojego komponentu, tak aby uytkownik jak najszybciej mg sprawdzi jego dziaanie. Moja propozycja programu prezentacyjnego jest przedstawiona na rysunku 15.6.

Rysunek 15.6. Program prezentujcy dziaanie komponentu TURLabel Po instalacji komponent powinien zosta umieszczony na palecie Samples. Umie nasz now kontrolk na formularzu. Spjrz na zakadk Events z Inspektora Obiektw ? znajduj si tam zadeklarowane przez nas zdarzenia (rysunek 15.7).

Rysunek 15.7. Zdarzenie OnURLClick Po wygenerowaniu zdarzenia OnURLClick zauwaysz, e procedura zdarzeniowa posiada parametry X i Y: 598 | S t r o n a

procedure TDemoForm.URLabel1URLClick(Sender: TObject; X, Y: Integer); begin lblTip.Caption := 'Podpowied: odnonik zosta kliknity w punkcie ' + IntToStr(X) + ' i ' + IntToStr(Y); end;

Teraz majc informacje o wsprzdnych X i Y (reprezentujcych miejsce kliknicia odnonika), moesz w prosty sposb przedstawi je uytkownikowi.

Zachowanie komponentu
Podczas umieszczania komponentu na formularzu take zostaje wywoywany konstruktor Create. Z tego te powodu istnieje moliwo kontroli zachowania tego komponentu dziki waciwoci ComponentState (wspominaem o niej w poprzednim rozdziale). Moliwe wartoci tego komponentu przedstawiem w tabeli 15.1. Warto Opis

csDesigning Aplikacja wykorzystujca komponent znajduje si na etapie projektowania csLoading Ze strumienia do komponentu odczytywane s dane. Warto przydzielana jest w momencie pierwszego tworzenia komponentu

csUpdating Komponent jest aktualizowany csWriting csReading Zapisywanie danych z komponentu do strumienia Odczytywanie danych ze strumienia

csDestroying Za chwil komponent zostanie zniszczony

Komponenty graficzne
Zajmijmy si przez moment innymi typami komponentw, ktre nie s oparte na adnej sprecyzowanej klasie typu TLabel czy TMemo. Stworzymy komponent praktycznie od pocztku. Za 599 | S t r o n a

klas bazow posuy nam TCustomControl. Wybraem j dlatego, gdy posiada zdarzenie OnPaint i klas Canvas. Zaprojektujemy bowiem komponent graficzny, ktry ? po wprowadzeniu odpowiednich danych ? bdzie na swojej powierzchni malowa napis, przesuwajcy si i odbijajcy od cianek komponentu. Dziaanie tego komponentu zostao przedstawione na rysunku 15.8.

Rysunek 15.8. Przykad dziaania komponentu Tekst bdzie trjwymiarowy, a ramka, ktr widzisz, to krawdzie komponentu. Najtrudniejsze bdzie zaprogramowanie procedury odbijania od cianek i zapewnienie samego efektu losowoci w odbijaniu tekstu. Cao dziaa mniej wicej tak: na samym pocztku losowane s dwie wartoci, ktre reprezentowa bd przesunicie animacji z pozycji X i Y: iX := Random(4)+1; iY := Random(4)+1;

Do tej wartoci dodawana jest jeszcze cyfra 1 (w celu zabezpieczenia si przed wylosowaniem zera). W komponencie tym wykorzystamy obiekt (komponent) TTimer, umoliwiajcy wykonywanie co jaki czas okrelonych instrukcji. Stworzymy go w naszym komponencie w sposb dynamiczny. Obiekt TTimer bdzie zatem wykonywa osobne zadanie ? dodawa do wsprzdnych pozycji tekstu X i Y wylosowane wartoci iX oraz iY. Po kadej takiej operacji trzeba bdzie sprawdza, czy przypadkiem napis nie dotkn ciany komponentu ? wtedy ponownie dokonujemy operacji losowania i napis zostaje przesunity w innym kierunku. 600 | S t r o n a

Oglny zarys klasy komponentu


Ten komponent bdzie nieco trudniejszy od poprzedniego, ktry projektowalimy. Kod klasy przedstawia si nastpujco: type TFly = class(TCustomControl) private FCaption : String; FActive : Boolean; FX, FY : Integer; Timer : TTimer; FInterval : Integer; F3D : TColor; FOnStart, FOnStop : TNotifyEvent; procedure SetActive(Value : Boolean); procedure SetInterval(Value : Integer); procedure SetCaption(ACaption : String); procedure SetFont(AFont : TFont); protected procedure OnTimer(Sender: TObject); // procedura obsugi Timera public constructor Create(AOwner : TComponent); override; // konstruktor destructor Destroy; override; // destruktor procedure Paint; override; // procedura OnPaint published property Interval : Integer read FInterval write SetInterval; // przerwa pomidzy skokami property Active : Boolean read FActive write SetActive; // animacja aktywna czy nie? property Caption : String read FCaption write SetCaption; // wywietlany tekst property T3D : TColor read F3D write F3D; // drugi kolor, dajcy efekt 3D { standardowe zdarzenia } property Font write SetFont; property OnClick; property OnDblClick; property OnMouseDown; property OnMouseUp; property OnMouseMove; { dwa zdarzenia, ktre bd wystpowa podczas zatrzymania lub uruchomienia animacji } property OnStart : TNotifyEvent read FOnStart write FOnStart; 601 | S t r o n a

property OnStop : TNotifyEvent read FOnStop write FOnStop; end;

Chyba nie musz objania, jak stworzy szablon dla nowego komponentu ? prezentowalimy to ju ostatnio. Omwmy jednak pobienie budow samej klasy. W sekcji private znajduj si cztery procedury. Su one do ustawienia pewnych wartoci. Spjrz na sekcj published. Kilka metod odczytuje wartoci ze zmiennej, a po sowie kluczowym write umieszczona jest nazwa procedury. Dziki temu oprcz przypisania wartoci do metody mona w kodzie procedury zawrze jakie dodatkowe czynnoci, czyli ? inaczej mwic ? okreli, co program ma wykona po przypisaniu danych do waciwoci. Kluczow waciwoci jest Active. Definiuje ona bowiem, czy animacja ma by uruchomiona. Jeeli nie, napis nie bdzie si porusza ? zostanie jedynie wywoana metoda Paint, ale TTimer bdzie wyczony (waciwo Timer.Enabled). Jeeli Active = TRUE, TTimer zostanie wczony. Zwr jeszcze uwag na jedn rzecz, a mianowicie zdarzenia takie, jak OnMoueDown, OnMoueUp itd. Wpisanie ich nazw w zupenoci wystarczy, aby te zdarzenia pojawiy si w Inspektorze Obiektw.

Kod rdowy komponentu


Zacznijmy moe od konstruktora. Jego zadaniem jest przypisanie paru metodom odpowiednich wartoci: constructor TFly.Create(AOwner : TComponent); begin inherited Create(AOwner); { utwrz obiekt TTimer } Timer := TTimer.Create(Self); Timer.Enabled := False; Interval := 500; // przypisz waciwo Interval Timer.Interval := Interval; Timer.OnTimer := OnTimer; // obsuga zdarzenia Height := 350; Width := 300; { ustawienia domylne dla czcionki } Font.Style := [fsBold]; Font.Size := 14; Font.Color := clBlack; F3D := clWhite; Randomize; { losuj pocztkow pozycj pooenia tekstu } 602 | S t r o n a

FX := Random(Width); FY := Random(Height); { wylosuj ilo pikseli o ktre animacja bdzie si posuwa } iX := Random(4)+1; iY := Random(4)+1; FCaption := '4programmers.net'; DoubleBuffered := True; end;

Na samym jednak pocztku tworzony jest komponent TTimer i ustawiane s jego waciwoci oraz procedura obsugi zdarzenia OnTimer. Nastpnie ustawiona zostaje domylna czcionka, jaka bdzie uywana zaraz po umieszczeniu komponentu na formularzu. Zwr uwag na ostatni wiersz tego konstruktora. Zmieniam warto zmiennej DoubleBuffered na True. Zmienna ta okrela, czy podczas rysowania uywane bdzie tzw. podwjne buforowanie. Podczas gdy jest ona ustawiona na False, co jest wartoci domyln, rysowanie odbywa si bezporednio na komponencie, co moe spowodowa bardzo nielubiane przez programistw migotanie tekstu. Natomiast gdy zmienna ma warto True, wszelkie zmiany dotyczce rysowania odbywaj si w pamici komputera, a dopiero pniej s przedstawiane na formularzu. Wie si to ze zwikszeniem zapotrzebowania na pami. W konstruktorze obiektu nie zapomnij o zwolnieniu obiektu typu TTimer: destructor TFly.Destroy; begin Timer.Free; // zwolnij obiekt inherited; end;

Podstaw dla komponentu ju mamy ? teraz zajmijmy si spraw rysowania. Ju podczas umieszczania komponentu na formularzu bdzie mona zmieni waciwo Active na True i obserwowa proces animacji. Jeeli animacja bdzie zatrzymana, tekst nie bdzie si porusza. Trzeba zatroszczy si o to, aby uytkownik np. po zmodyfikowaniu czcionki podczas projektowania programu od razu zobaczy efekty? Trzeba zatem umoliwi podgld zmian. Po kadorazowej zmianie ? czy to czcionki, czy czego innego ? tekst zostanie odwieony, czyli ponownie zostanie wywoana metoda Paint: procedure TFly.Paint; begin Canvas.Font := Font; // ustaw czcionk Canvas.Brush.Style := bsClear; // tekst przezroczysty Canvas.Font.Color := F3D; // drugi kolor, dajcy efekt 3D Canvas.TextOut(Fx, Fy, FCaption); // narysuj najpierw drugim kolorem 603 | S t r o n a

Canvas.Font.Color := Font.Color; // ustaw teraz prawidowy Canvas.TextOut(FX+1, FY+1, FCaption); // z minimalnym przesuniciem narysuj drug warstw end;

Na samym pocztku tej procedury (w pierwszym wierszu) do klasy Canvas zostaje przypisana taka czcionka, jaka ustawiona jest w Inspektorze Obiektw. Pniej wedug ustawie Canvas narysowany zostanie tekst 3D. Nie jest to nic trudnego. Po prostu rysujemy ten sam tekst z minimalnym przesuniciem (np. 1 punktu) i ze zmienionym kolorem czcionki. Pozostao nam jeszcze wprowadzanie animacji w ruch. Zrealizujemy to za pomoc komponentu TTimer i jego zdarzenia OnTimer: procedure TFLy.OnTimer(Sender: TObject); begin { do wartoci zmiennych dodaj wylosowane pozycje iX oraz iY } FX := FX + iX; FY := FY + iY; Repaint; // przerysuj Canvas.Font.Color := F3D; Canvas.TextOut(Fx, Fy, FCaption); Canvas.Font.Color := Font.Color; Canvas.TextOut(FX+1, FY+1, FCaption); { tutaj nastpuje sprawdzenie, czy animacja nie wychodzi poza brzegi komponentu. Jeeli tak si stanie, trzeba dla zmiennych iX oraz iY wylosowa nowe wartoci, o ktre bdzie si przesuwa animacja. } Randomize; if FX < ?1 then iX := Random(4); if FY < ?1 then iY := Random(4); if FX >= (Width ? Canvas.TextWidth(FCaption)) then iX := ?Random(4); if FY >= (Height ? Canvas.TextHeight(FCaption)) then iY := ?Random(4); end;

Za kadym wystpieniem tego zdarzenia do obecnych wartoci FX i FY zostaj dodane wartoci iX i iY, czyli wylosowane na pocztku wartoci oznaczajce, o ile punktw tekst bdzie przewijany w pionie i poziomie. Animacja ta polega po prostu na kadorazowym przerysowaniu tekstu, tyle e w zmienionej pozycji i z uprzednim odwieeniem. W ostatnich instrukcjach if tej metody sprawdzane 604 | S t r o n a

jest, czy nie wystpia kolizja, tj. czy tekst nie dotkn krawdzi komponentu. Wtedy bowiem trzeba wylosowa nowe wartoci FX i FY, czyli nowe kierunki, w ktrych przesuwany bdzie tekst. Peny kod komponentu przedstawiony jest w listingu 15.3. Listing 15.3. Kod rdowy komponentu { Copyright (c) 2002 by Adam Boduch } unit Fly; interface uses Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, ExtCtrls; type TFly = class(TCustomControl) private FCaption : String; FActive : Boolean; FX, FY : Integer; Timer : TTimer; FInterval : Integer; F3D : TColor; FOnStart, FOnStop : TNotifyEvent; procedure SetActive(Value : Boolean); procedure SetInterval(Value : Integer); procedure SetCaption(ACaption : String); procedure SetFont(AFont : TFont); protected procedure OnTimer(Sender: TObject); // procedura obsugi dla Timera public constructor Create(AOwner : TComponent); override; // konstruktor destructor Destroy; override; // destruktor procedure Paint; override; // procedura OnPaint published property Interval : Integer read FInterval write SetInterval; // przerwa pomidzy skokami property Active : Boolean read FActive write SetActive; // animacja aktywna czy nie? property Caption : String read FCaption write SetCaption; // wywietlany tekst 605 | S t r o n a

property T3D : TColor read F3D write F3D; // drugi kolor, dajcy efekt 3D { standardowe zdarzenia } property Font write SetFont; property OnClick; property OnDblClick; property OnMouseDown; property OnMouseUp; property OnMouseMove; { dwa zdarzenia, ktre bd wystpowa podczas zatrzymania lub uruchomienia animacji } property OnStart : TNotifyEvent read FOnStart write FOnStart; property OnStop : TNotifyEvent read FOnStop write FOnStop; end;

procedure Register; implementation var iX, iY : Integer; procedure TFly.SetInterval(Value : Integer); begin FInterval := Value; Timer.Interval := FInterval; end; procedure TFly.SetCaption(ACaption : String); begin FCaption := ACaption; Paint; end; procedure TFly.SetActive(Value : Boolean); begin FActive := Value; Paint; Timer.Enabled := FActive; { tutaj nastpuje sprawdzenie, czy animacja zostaa zatrzymana czy dopiero rozpoczta; nastpnie wywoywane zostaje odpowiednie zdarzenie } if Value = True then if Assigned(FOnStart) then OnStart(Self); if Value = False then if Assigned(FOnStop) then OnStop(Self); end; procedure TFly.SetFont(AFont : TFont); begin 606 | S t r o n a

Font := AFont; Paint; end; procedure TFLy.OnTimer(Sender: TObject); begin { do wartoci zmiennych dodaj wylosowane pozycje iX oraz iY } FX := FX + iX; FY := FY + iY; Repaint; // przerysuj Canvas.Font.Color := F3D; Canvas.TextOut(Fx, Fy, FCaption); Canvas.Font.Color := Font.Color; Canvas.TextOut(FX+1, FY+1, FCaption); { tutaj nastpuje sprawdzenie, czy animacja nie wychodzi poza brzegi komponentu. Jeeli tak si stanie, trzeba dla zmiennych iX oraz iY wylosowa nowe wartoci, o ktre bdzie si przesuwa animacja. } Randomize; if FX < ?1 then iX := Random(4); if FY < ?1 then iY := Random(4); if FX >= (Width ? Canvas.TextWidth(FCaption)) then iX := ?Random(4); if FY >= (Height ? Canvas.TextHeight(FCaption)) then iY := ?Random(4); end;

procedure TFly.Paint; begin Canvas.Font := Font; // ustaw czcionk Canvas.Brush.Style := bsClear; // tekst przezroczysty Canvas.Font.Color := F3D; // drugi kolor, dajcy efekt 3D Canvas.TextOut(Fx, Fy, FCaption); // narysuj najpierw drugim kolorem Canvas.Font.Color := Font.Color; // ustaw teraz prawidowy kolor Canvas.TextOut(FX+1, FY+1, FCaption); // z minimalnym przesuniciem narysuj drug warstw end; constructor TFly.Create(AOwner : TComponent); begin 607 | S t r o n a

inherited Create(AOwner); { utwrz obiekt TTimer } Timer := TTimer.Create(Self); Timer.Enabled := False; Interval := 500; // przypisz waciwo Interval Timer.Interval := Interval; Timer.OnTimer := OnTimer; // obsuga zdarzenia Height := 350; Width := 300; { domylne ustawienia czcionki } Font.Style := [fsBold]; Font.Size := 14; Font.Color := clBlack; F3D := clWhite; Randomize; { losuj pocztkow pozycj dla pooenia tekstu } FX := Random(Width); FY := Random(Height); { wylosuj ilo pikseli, o ktre animacja bdzie si przesuwa } iX := Random(4)+1; iY := Random(4)+1; FCaption := '4programmers.net'; DoubleBuffered := True; end; destructor TFly.Destroy; begin Timer.Free; // zwolnij obiekt inherited; end; procedure Register; begin RegisterComponents('Samples', [TFly]); end;

end.

Tak przedstawia si cay kod tego komponentu. Po zainstalowaniu moesz spokojnie z niego korzysta. Do tej ksiki doczony jest take program demonstrujcy korzystanie z tego 608 | S t r o n a

komponentu.

Pakiety komponentw
Czasem moe zaj potrzeba zainstalowania kilku (kilkunastu?) komponentw Twojego autorstwa. Co wtedy? Przecie nie bdziesz instalowa kadego komponentu z osobna. Dobrym rozwizaniem jest w tym wypadku zastosowanie pakietw komponentu. Dlatego te dla przykadu napisaem dwa proste komponenty ? TGetUser, ktry podaje nazw zalogowanego uytkownika systemu oraz TGetWindows, ktry to komponent podaje ciek do katalogu, w ktrym zainstalowany jest system Windows. Oba kody rdowe przedstawione s w listingach 15.4 i 15.5. Listing 15.4. Kod rdowy komponentu TGetUser { Copyright (c) 2002 by Adam Boduch } unit GetUser; interface uses Windows, Messages, SysUtils, Classes, Controls; type TGetUser = class(TComponent) public function GetUser : String; // funkcja zwraca nazw zalogowanego uytkownika end; procedure Register; implementation function TGetUser.GetUser : String; var Buffer : array[0..128] of char; Size : DWORD; begin Size := 128; GetUserName(Buffer, Size); // wywoaj procedur GetUserName Result := Buffer; end; 609 | S t r o n a

procedure Register; begin RegisterComponents('Samples', [TGetUser]); end; end.

Listing 15.5. Kod rdowy komponentu TGetWindows { Copyright (c) 2002 by Adam Boduch } unit GetWindows; interface uses Windows, Messages, SysUtils, Classes; type TGetWindows = class(TComponent) public function GetWDirectory : String; end; procedure Register; implementation function TGetWindows.GetWDirectory : String; var Buffer : array[0..255] of char; begin GetWindowsDirectory(Buffer, SizeOf(Buffer)); // podaje ciek do katalogu, w ktrym zainstalowany jest system Result := Buffer; end; procedure Register; begin RegisterComponents('Samples', [TGetWindows]); end; end.

610 | S t r o n a

Komponenty te obsuguje si bardzo atwo, lecz moesz stworzy pakiet, ktry posuy do jednoczesnego ich zainstalowania. W tym celu z menu File wybierz polecenie Other. Nastpnie kliknij dwukrotnie ikon Package. Na ekranie pojawi si takie okienko, jak na rysunku 15.9.

Rysunek 15.9. Tworzenie nowego pakietu Przycisk Add suy do dodawania nowych moduw do pakietu. Moduy te zostan dodane do gazi Contains. Po naciniciu tego przycisku zostanie wywietlone okno ? wystarczy wwczas poda nazw moduu, w ktrym znajduje si komponent. Nastpnie moesz taki pakiet zapisa, wybierajc z menu File polecenie Save. Teraz wystarczy, e po otwarciu takiego pakietu naciniesz przycisk Install, a Delphi zainstaluje wszystkie komponenty zawarte w pakiecie. Po naciniciu przycisku Compile moduy zawarte w tym pakiecie zostan skompilowane do postaci plikw DCU. Jak ju mwiem wczeniej, jeeli komponent zosta skompilowany w Delphi 7, to do zainstalowania go na innym komputerze, gdzie take jest zainstalowane Delphi 7, nie potrzeba kodw rdowych.

Podsumowanie
Nie da si ukry, e projektowanie komponentw jest kolejnym stopniem wtajemniczenia w nauce Delphi. Ty t sztuk wanie opanowae ? uwierz mi, e ta nauka nie pjdzie na marne. By moe bdziesz w przyszoci pisa komercyjne komponenty, ktre pniej bdziesz sprzedawa? Tak, tak ? taki rodzaj zarobkowania jest czsto spotykany w sieci. Zaczniki:

Listingi_15.zip (414.04 kB)

611 | S t r o n a

Podsumowanie czci III


Edytuj Historia Przenie Obserwuj

Podsumowanie czci III


Sam nie wiem, czy umiejtno programowania komponentw przyda Ci si w przyszoci, lecz nie sposb byo pomin to zagadnienie. Rozdzia 14. by powicony w wikszoci teoretycznej budowie biblioteki VCL. Wiedza ta moe Ci si przyda wanie przy projektowaniu nowych ?klockw? i przy wyborze klas bazowych dla nowych kontrolek. W rozdziale 15. wspomniaem co nieco o projektowaniu wasnych kontrolek. Przedstawiem par przykadw ilustrujcych proces tworzenia wasnych obiektw. Mam nadziej, e wiedza zdobyta w tym rozdziale przyda Ci si w przyszoci.

Cz IV

Edytuj Historia Przenie Obserwuj

Cz IV
Przyczyn tak duej popularnoci Delphi jest po czci moliwo wygodnego korzystania z baz danych. W innym, rwnie popularnym i cakiem dobrym rodowisku ? w Turbo Pascalu ? nie istniay adne biblioteki umoliwiajce tworzenie aplikacji bazodanowych. Zarwno w Delphi, jak i w C++ Builder dostpne s odpowiednie narzdzia, ktre wspomagaj tworzenie aplikacji z wykorzystaniem baz danych. W tej czci omwimy wanie tworzenie takich programw, ktre czasem wymagaj wykorzystania baz danych. W rozdziale 16. zajmiemy si istniejc ju od jakiego czasu technologi BDE, umoliwiajc tworzenie aplikacji z wykorzystaniem takich baz, jak Paradox czy dBase.
612 | S t r o n a

Kolejny rozdzia tej czci powicimy opisowi tworzenia aplikacji klient-serwer; poczymy si z serwerem baz danych MySQL i napiszemy program korzystajcy z zalet tego bardzo popularnego systemu.

Rozdzia 16

Edytuj Historia Przenie Obserwuj

Bazy danych BDE


W tym rozdziale skupimy nasz uwag na tworzeniu aplikacji bazodanowych. Na pocztek dokonamy krtkiego przegldu baz danych, a nastpnie zajmiemy si tworzeniem aplikacji opartych o technologi BDE (Borland Database Engine).

Spis treci 1 Czym jest baza danych? 2 Typy baz danych 2.1 Lokalne bazy danych 2.2 Bazy danych typu klient-serwer 3 Wielowarstwowo baz danych 4 Bazy danych w Delphi 5 Borland Database Engine 5.1 Sterowniki baz danych 6 Przykadowa baza danych 7 Komponenty bazodanowe 8 Komponent TTable 8.1 TDataSource 8.2 TDataSet 8.3 Komponent TQuery 9 BDE Administrator 9.1 Tworzenie nowej bazy danych 10 Tworzenie bazy w kodzie programu 11 Tworzenie tabel 12 Tworzenie rekordw 613 | S t r o n a

13 Odczytywanie wartoci z tabeli 14 Przykadowy program korzystajcy z naszej bazy danych 15 Podsumowanie

W tym rozdziale:

poznasz, na czym polega budowa baz danych; poznasz znaczenie terminw lokalna baza danych i aplikacje klient-sewer; nauczysz si wykorzystywa technologi BDE; napiszesz prosty program korzystajcy z baz danych.

Czym jest baza danych?


Wiele firm wykorzystuje bazy danych do gromadzenia informacji, gdy bezwzgldnie jest to najlepszy i najefektywniejszy sposb przechowywania danych ? nie wymagajcy plikw tekstowych, rejestrw czy plikw INI. Sam nie byem kiedy skonny do korzystania z baz danych, lecz gdy poznaem ich funkcje i dziaanie, nie potrafiem ju wyobrazi sobie projektowania aplikacji bez ich uycia. Powiedzmy sobie szczerze: jeeli nie jeste zaznajomiony z tematyk baz danych, to sowo to kojarzy Ci si zapewne ze zbiorem rnych danych ? setkami informacji na temat klientw danej firmy, pac pracownikw, ich adresw itp. Nie jeste daleki od prawdy; postaram si przybliy Ci funkcjonowanie baz danych w Delphi. Uyjemy do tego mechanizmu firmy Borland ? BDE.

Typy baz danych


Pocztkujcemu uytkownikowi wydaje si, e baza danych to zwyczajny program, ktry przechowuje gdzie w pliku potrzebne informacje i? tyle! W rzeczywistoci istniej bazy lokalne oraz bazy typu klient-serwer.

614 | S t r o n a

Lokalne bazy danych


Lokalne bazy danych umieszczone s na jednym komputerze, czyli na tej samej maszynie, na ktrej uruchomiony jest nasz program. Su do prostej komunikacji pomidzy aplikacj a zbiorem danych; uywane s tylko przez nasz aplikacj. Przykadem jest program, ktry przechowuje adresy oraz inne dane pracownikw firmy.

Bazy danych typu klient-serwer


Innym systemem ? nieco bardziej skomplikowanym, lecz bardziej popularnym ? jest architektura baz typu klient-serwer. Na serwerze znajduje si program, ktrego zadaniem jest przechowywanie danych, zarzdzanie nimi, a take obsug aplikacji-klientw. Aplikacja-klient to program, ktry jest udostpniamy w kilku egzemplarzach (lub nawet setkach kopii) i suy do komunikacji z serwerem. Klient zadaje serwerowi tzw. zapytania, ktre ten interpretuje i zwraca klientowi. Istnieje wiele zalet takiego rozwizania ? dostp do bazy danych ma wiksza liczba osb, a same dane s bezpieczniejsze (pod wzgldem moliwoci utraty). Zarazem jednak wicej osb ma do nich wgld. Tworzeniem takich aplikacji zajmiemy si w kolejnym rozdziale.

Wielowarstwowo baz danych


By moe bdziesz mia okazj zetkn si kiedy z pojciem jednowarstwowa baza danych lub podobnym, dlatego te wyjani je szczegowo. Lokalne bazy danych s nazywane zazwyczaj jednowarstwowymi ? z tego powodu, i wszelkie operacje s wykonywane bezporednio (program posiada bezporednie poczenie z baz danych). Dwuwarstwowa baza danych to poczenie z baz danych za pomoc tzw. sterownikw (baza klientserwer). Aplikacja uywa sterownikw, aby poczy si z serwerem, a ten bierze na siebie odpowiedzialno za zarzdzanie danymi. Wielowarstwowo to poczenie kilku serwerw. Aplikacje-klienci cz si z serwerami, ktre z kolei odpowiadaj za przekazanie danych do serwera gwnego.

Bazy danych w Delphi


615 | S t r o n a

Wykorzystujc tak wspaniae rodowisko, jakim jest Delphi, masz moliwo skorzystania z kilku rodzajw baz danych:

BDE ? do skomplikowany mechanizm Borlanda, pozwalajcy na czenie si z bazami danych typu dBase czy Pardox. ADO ? jest to standard firmy Microsoft, polegajcy na czeniu si z bazami danych przy wykorzystaniu mechanizmu ADO (ActiveX Data Object). dbExpress ? stosunkowo nowa technologia, pozwalajca na czenie si z rnymi typami baz danych. Du zalet jest uniwersalno tego typu baz danych, dostpnych take dla Linuksa. InterBase ? komponenty tej grupy umoliwiaj poczenie si z serwerem bazodanowym firmy Borland ? InterBase.

W III czci tej ksiki zajmiemy si dwiema najpopularniejszymi metodami dostpu do baz danych ? BDE oraz dbExpress.

Borland Database Engine


BDE, czyli Borland Database Engine, to zbir bibliotek DLL i funkcji API, umoliwiajcych w do prosty sposb komunikowanie si z systemami baz danych. Co prawda BDE jest doczany do Delphi, lecz stanowi zbir osobnych bibliotek, umieszczonych w katalogu Borland Shared/BDE.

Sterowniki baz danych


Ju nieraz wspomniaem w tym rozdziale o tzw. sterownikach. W rzeczywistoci sterowniki s funkcjami API dokonujcymi operacji na bazach danych. Rne bazy danych, takie jak dBase czy Paradox, posiadaj rn budow ? sterowniki dokonuj ?tumaczenia? funkcji pisanych w Delphi na ?jzyk? zrozumiay dla bazy danych.

Przykadowa baza danych


Mechanizm BDE udostpnia kilka przykadowych baz danych, aby uytkownik mg sprawdzi w jak najkrtszym czasie dziaanie komponentw. Przeprowadzimy mae wiczenie, prezentujce wykorzystanie tych ?gotowcw?. 616 | S t r o n a

Najpierw udaj si do katalogu, w ktrym zainstalowane jest BDE (w moim przypadku jest to F:\Common Files\Borland Shared\Data) i odszukaj plik country.db. To jest wanie przykadowa baza danych, z ktrej za chwil skorzystamy (rysunek 16.1).

Rysunek 16.1. Pliki bazy danych country Na rysunku oprcz pliku z rozszerzeniem *.db znajduj si take pliki *.cds oraz *.px, wykorzystywane na potrzeby samego BDE. 1. Przejd do zakadki BDE w palecie komponentw i odszukaj komponent TTable (pierwszy z lewej); umie go na formularzu. 2. Przejd do zakadki BDE i umie na formularzu komponent TDataSource (rwnie pierwszy z lewej). 3. Z listy rozwijalnej komponentu TDataSource wybierz pozycj Table, ktra okrela umieszczony na formularzu komponent TTable. 4. Przejd do zakadki Data Controls i umie na formularzu komponent TDBGrid; jego rozmiar dopasuj wedug wasnych upodoba. 5. Majc dalej zaznaczony komponent TDBGrid, z listy waciwoci kwybierz DataSource (wskazanie komponentu TDataSource).

Po tych czynnociach mamy ju gotowy interfejs programu. Teraz nadszed czas na poczenie si z baz, ale uprzednio musimy wybra odpowiedni tabel. Ponownie zaznacz komponent TTable; bdziemy musieli wybra baz danych, z ktrej bdziemy korzysta. Z listy rozwijalnej waciwoci DatabaseName wybierz DBDEMOS; nastpnie z listy waciwoci TableName wybierz country.db. Nadszed teraz decydujcy moment ? warto waciwoci Active zmie na True. W tym momencie komponent powinien poczy si z baz danych, a na komponencie TDBGrid powinny pojawi si wartoci odczytane z owej bazy. Program w trakcie dziaania przedstawiony zosta na rysunku 16.2.

617 | S t r o n a

Rysunek 16.2. Wartoci z bazy danych przedstawione w komponencie TDBGrid Naturalnie jest to tylko przykad ? w dalszej czci rozdziau zajmiemy si tworzeniem wasnej bazy danych i samodzielnym dodawaniem rekordw.

Komponenty bazodanowe
Mimo e nie stworzylimy jeszcze prawdziwej aplikacji opartej na bazach danych, tj. nie napisalimy ani jednego wiersza kodu, to wykorzystae ju w poprzednim przykadzie kilka komponentw. Co prawda przykad by do prosty, ale wymaga uycia a trzech komponentw. Wikszo operacji na bazach danych odbywa si dziki komponentom niewizualnym, aczkolwiek przedstawienie wyniku tej operacji (dodanie nowego rekordu, odczytanie zawartoci tabeli) musi by przedstawione w postaci wizualnej kontrolki. Owe kontrolki suce do przedstawienia zawartoci baz danych s zamieszczone na palecie Data Controls, a wyrniaj si tym, e ich nazwa poprzedzona jest literami DB. W poprzednim przykadzie uylimy komponentu TDBGrid, ktry reprezentuje zbir kolumn i wierszy. Przed uruchomieniem programu konieczne jest jednak przydzielenie odpowiedniej wartoci do waciwoci DataSource (rysunek 16.3).

618 | S t r o n a

Rysunek 16.3. Inspektor Obiektw z zaznaczon waciwoci DataSource Owa waciwo DataSource okrela zbir danych, ktry ma zosta przedstawiony w komponencie. Inny przykad znajduje si na rysunku 16.4. Tam bowiem zastosowaem komponenty TDBImage oraz TDBNavigator.

619 | S t r o n a

Rysunek 16.4. Graficzne przedstawienie wartoci z tabeli W tym przykadzie odczytywana jest tabela animals.db, ktra posiada kolumn BMP, zawierajc zdjcie wybranego zwierzaka. Graficzne przedstawienie tego zdjcia wie si z wybraniem z waciwoci DataField pozycji BMP jako nazwy kolumny do zaprezentowania. Komponent TDBNavigate suy do przesuwania si midzy kolejnymi rekordami tabeli; tutaj take konieczne jest wybranie odpowiedniej wartoci z waciwoci DataSource.

Komponent TTable
Komponent TTable jest najprostszym rodkiem umoliwiajcym dostp do konkretnej bazy danych oraz tabeli. Tabela jest uporzdkowanym zbiorem kolumn i wierszy. Gwne waciwoci oraz metody tego komponentu zostay przedstawione w tabelach 16.1 oraz 16.2. Tabela 16.1. Gwne waciwoci komponentu TTable Waciwo TableType TableName Opis Typ tabeli (Paradox, dBase, ASCII, FoxPro lub warto domylna) Z lity rozwijalnej moesz wybra tabel nalec do okrelonej bazy danych

620 | S t r o n a

ReadOnly

Okrela, czy tabela ma by tylko do odczytu

DatabaseName Z listy rozwijalnej moesz wybra baz danych, z ktrej bdziemy korzysta Exclusive Umoliwia zablokowanie danej tabeli wycznie dla naszej aplikacji

Tabela 16.2. Gwne metody komponentu TTable Waciwo Opis

CreateTable Tworzy tabele na podstawie wczeniej podanych informacji EmptyTable Usuwa wszystkie rekordy z tabeli

DeleteTable Usuwa tabel cakowicie RenameTable Zmienia nazw tabeli LockTable Blokuje tabel, tak aby inne aplikacje nie miay do niej dostpu

UnlockTable Odblokowuje tabel GotoKey Przechodzi do wybranego rekordu

TDataSource
W poprzednim przykadzie rwnie korzystalimy z komponentu TDataSource. Nie peni on adnej znaczcej funkcji poza tym, e jest porednikiem pomidzy komponentami typu TTable czy TQuery a kontrolkami wizualnymi typu TDBGrid.

TDataSet
Klasa TDataSet jest klas bazow dla komponentw typu TQuery czy TTable, std posiada ona wikszo metod i waciwoci klasy TTable. Waciwoci tej klasy przedstawiem w tabeli 16.3, a jej gwne metody ? w tabeli 16.4. Tabela 16.3. Gwne waciwoci klasy TDataSet Metoda Opis

621 | S t r o n a

Active Bof Eof Fields FieldValue Filter Filtered Modified RecNo

Ustawienie wartoci na True powoduje poczenie z baz danych i odczytanie zbioru danych Waciwo zwraca True, jeli kursor znajduje si na pierwszym rekordzie Warto zwraca True, jeeli kursor znajduje si na ostatnim rekordzie Wskazanie typu TFields, zwracajcego informacj na temat pl Zwraca w postaci typu Variant warto okrelonego pola Okrela kryterium filtrowania rekordw Okrela, czy zastosowa filtrowanie Okrela, czy biecy rekord zosta zmodyfikowany Biecy numer rekordu

RecordCount Oglna ilo rekordw

Piszc sowo kursor w powyszej tabeli, mam na myli aktualnie zaznaczon pozycj. Tabela 16.4. Gwne metody klasy TDataSet Metoda Append Cancel Opis Po wywoaniu tej metody stworzony zostaje nowy rekord Anuluje zmiany dokonane w biecym rekordzie

ClearFields Czyci zawarto wszystkich pl rekordu Delete Edit FetchAll FindFirst FindNext FindLast Usuwa biecy rekord Daje moliwo edycji rekordu Pobiera wszystkie rekordy, poczwszy od zaznaczonego Rozpoczyna wyszukiwanie Znajduje kolejny rekord Znajduje ostatni rekord

622 | S t r o n a

Refersh

Odwiea zbir informacji o bazie danych

Komponent TQuery
W dziaaniu komponent TQuery (angielskie sowo query oznacza zapytanie) przypomina kontrolk TTable. Jedyna znaczca rnica, jak mona dostrzec, to operowanie na bazie danych za pomoc jzyka SQL. SQL to skrt od angielskich sw Structured Query Language. Jest to jzyk oparty na specjalnych zapytaniach kierowanych do bazy. Posugujc si komponentem TQuery, moesz uzyska dostp do takich baz danych, jak Sybase, Oracle, Informix, DB2 czy InterBase oraz lokalnych: Paradox, dBASE, Access i FoxPro. Komponent TQuery nie posiada waciwoci TableName, lecz dziki zapytaniom SQL mamy moliwo uzyskania dostpu jednoczenie do kilku tabel. Wicej informacji o samym jzyku SQL znajduje si w rozdziale 17.

BDE Administrator
BDE Administrator (rysunek 16.5) to narzdzie suce do manipulowania bazami danych.

623 | S t r o n a

Rysunek 16.5. Program BDE Administrator Za jego pomoc moemy usun, zapisa lub zmodyfikowa dowoln baz danych.

Tworzenie nowej bazy danych


Utworzenie nowej bazy danych moe odby si zarwno poprzez aplikacj BDE Administrator, jak i poprzez odpowiednie funkcje w kodzie programu. Najpierw zajmijmy si tym pierwszym przypadkiem. 1. Po otwarciu programu BDE Administrator z menu Object wybierz New. 2. Zaakceptuj domyln pozycj w oknie, ktre pojawi si w wyniku tej operacji. 3. Na licie po lewej stronie pojawi si nowa pozycja, przygotowana do wpisania nowej nazwy ? wpisz MyDatabase.

W tym momencie baza danych zostaa utworzona (rysunek 16.6). Pozostao jeszcze wpisanie cieki do katalogu, w ktrym przechowywane bd pliki bazy danych. Na dysku C: utwrz katalog MyDatabase, a w programie BDE po zaznaczeniu pozycji MyDatabase w polu PATH wpisz C:\MyDatabase.

624 | S t r o n a

Rysunek 16.6. Nowa baza danych Zaakceptuj zmiany skrtem Ctrl+A.

Tworzenie bazy w kodzie programu


Z utworzeniem nowej bazy danych z poziomu aplikacji wie si uycie komponentu TSession. Umie ten komponent na formularzu i zmie waciwo AutoSessionName na True. Kod tworzcy now baz wyglda tak: procedure TMainForm.btnMakeClick(Sender: TObject); begin if not DirectoryExists('C:\MyDatabase') then begin CreateDir('C:\MyDatabase'); Session.AddStandardAlias('MyDatabase', 'C:\MyDatabase', ''); end; end;

A zatem ? uoglniajc ? za tworzenie bazy odpowiada polecenie AddStandardAlias z komponentu TSession. 625 | S t r o n a

Tworzenie tabel
Samo utworzenie tabeli jest realizowane poprzez metod CreateTable, lecz wczeniej naley ustali odpowiednie parametry dla kolumn, ktre maj znajdowa si w tabeli. procedure TForm1.Button1Click(Sender: TObject); begin Table.DatabaseName := 'MojaBaza'; Table.TableType := ttParadox; Table.TableName := 'MainTable'; if not Table.Exists then begin with Table.FieldDefs do begin with AddFieldDef do begin Name := 'ID'; // nazwa parametru ? ID DataType := ftInteger; // typ parametru ? Integer Required := True; // pole jest wymagane end; end; { utwrz tabele } Table.CreateTable; end; end;

Powyszy kod powoduje stworzenie tabeli o nazwie MainTable, ktra bdzie posiada tylko jedn kolumn ? ID. Okrelenie elementu do utworzenia odbywa si za porednictwem rekordu AddFieldDef. Ch utworzenia kolejnej tabeli wie si z przypisaniem kolejnych danych do rekordu: with AddFieldDef do begin Name := 'Towar'; // nazwa parametru ? ID DataType := ftString; // typ parametru ? Integer Required := False; // pole nie jest wymagane end;

Podczas tworzenia nowego elementu, konieczne byo podanie typu kolumny ? w tym wypadku: fsString (kolumna tekstowa). Inne moliwe typy kolumny znajduj si w tabeli 16.5.

626 | S t r o n a

Tabela 16.5. Typy pl bazy danych Typ pola ftUknown ftString ftInteger ftWord ftSmallInt ftFloat ftBoolean ftCurrency ftDateTime ftGraphic ftFmtMemo Opis Nieokrelony typ pola acuch tekstowy 32-bitowa liczba cakowita typu Integer 16-bitowa liczba typu Word 16-bitowa warto typu SmallInt Warto zmiennoprzecinkowa True lub False Warto zmiennoprzecinkowa Data i czas Bitmapa Pole Memo

ftTypedBinary Pole binarne (typowane) ftBlob Due pole binarne

W moim przypadku kod powodujcy utworzenie tabeli wykorzystywanej na potrzeby tego rozdziau wyglda tak: procedure TMainForm.Button1Click(Sender: TObject); begin Table.DatabaseName := 'MojaBaza'; Table.TableType := ttParadox; Table.TableName := 'MainTable'; if not Table.Exists then begin with Table.FieldDefs do begin with AddFieldDef do begin Name := 'ID'; // nazwa parametru ? ID DataType := ftInteger; // typ parametru ? Integer 627 | S t r o n a

Required := True; // pole jest wymagane end; with AddFieldDef do begin Name := 'Towar'; // nazwa parametru ? towar DataType := ftString; // typ parametru ? String Required := False; // pole nie jest wymagane end; with AddFieldDef do begin Name := 'Cena'; // nazwa parametru ? cena DataType := ftCurrency; // typ parametru ? Currency Required := True; // pole jest wymagane end; with AddFieldDef do begin Name := 'Data'; // nazwa parametru ? cena DataType := ftDateTime; // typ parametru ? DataTime Required := True; // pole jest wymagane end; end; { utwrz tabele } Table.CreateTable; end; end;

Peny kod rdowy zawierajcy procedury tworzenia tabel znajduje si na pycie CD-ROM w katalogu ../istingi/16/mTable/mTable.dpr.

Tworzenie rekordw
Dodanie nowego rekordu mona podzieli na kilka etapw: 1. Wywoanie metody Append, ktre spowoduje wstawienie rekordu na kocu tabeli. 2. Przypisanie do poszczeglnych kolumn nowych wartoci. 3. Wywoanie metody Post, ktra zatwierdzi zmiany i przekae t informacj do bazy.

Jeeli mamy ju tabel, dodanie do niej nowego rekordu moe przebiega w sposb nastpujcy: procedure TMainForm.btnSaveClick(Sender: TObject); begin Table.Append; 628 | S t r o n a

Table.FieldValues['ID'] := 34; Table.FieldValues['Towar'] := 'Proszek do prania'; Table.FieldValues['Cena'] := 2.10; Table.FieldValues['Data'] := Now; Table.Post; end;

Zatem na pocztku wywoujemy metod Append, a dopiero pniej przydzielamy konkretne wartoci do poszczeglnych kolumn. Przydzielanie wartoci odbywa si za porednictwem waciwoci FieldValues. Na samym kocu wysyamy wszystko do bazy danych. Jeeli pod tabel ?podpity? jest komponent TDBGrid, mamy moliwo podgldu wszelkich operacji dokonywanych w tabeli (rysunek 16.7).

Rysunek 16.7. Utworzenie nowego rekordu

Odczytywanie wartoci z tabeli


Jeeli kontrolka TDBGrid pozwala na podgld zawartoci tabeli, nie musimy si martwi o adowanie i wywietlanie poszczeglnych rekordw. Nie zawsze jednak chcemy, aby pozycje zostay wywietlone w owym komponencie. Prostym rozwizaniem tego problemu jest pobieranie zawartoci tabeli w ptli przy uyciu polecenia Next, ktre nakazuje odczytanie kolejnego rekordu: procedure TMainForm.FormCreate(Sender: TObject); begin Table.Active := True; while not Table.Eof do begin Memo1.Lines.Add(Table.FieldValues['Towar']); Table.Next; end; end;

629 | S t r o n a

Ptla jest wykonywana, dopki nie zostanie napotkany koniec rekordw (Eof). Dla przykadu prezentuj jedynie odczytywanie kolumny Towar z naszej tabeli. Zwr uwag na wywoanie (jeszcze w ptli) polecenia Next. Nie zapomnij o konwersji! Jeli odczytujemy wartoci z poszczeglnych kolumn za pomoc FieldValuesk, otrzymujemy rezultat w postaci zmiennej typu Variant. Jeeli jednak nie zastosujemy w tym wypadku konwersji, a warto w kolumnie bdzie typu Integer, program wywietli wyjtek, gdy prbujemy przypisa do typu String warto typu Integer. Zwr na to uwag, gdy w przypadku zmiennych Variant kompilator nie wskae bdu na etapie projektowania aplikacji.

Przykadowy program korzystajcy z naszej bazy danych


Jeszcze niedawno utworzye wasn baz danych, a pniej tabel. Jeeli dojdziesz ju do tego etapu, to wykorzystanie tej prostej bazy danych bdzie raczej nieskomplikowane. Powiedzmy sobie szczerze: czego potrzebujesz wicej? Delphi jest na tyle ?intuicyjnym? narzdziem, e dziki kontrolce TDBGrid mamy zapewniony rwnie tryb edycji rnych pl (po zakoczeniu edycji naley wprowadzi uaktualnienie do bazy danych ? metoda Post). Przykad takiego programu znajduje si w listingu 16.1. Listing 16.1. Prosta baza danych unit MainFrm; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls, DB, DBTables, Grids, DBGrids; type TMainForm = class(TForm) btnAdd: TButton; Table: TTable; DBGrid: TDBGrid; DataSource: TDataSource; btnRemove: TButton; btnSave: TButton; procedure btnAddClick(Sender: TObject); procedure FormCreate(Sender: TObject); 630 | S t r o n a

procedure FormDestroy(Sender: TObject); procedure btnSaveClick(Sender: TObject); procedure btnRemoveClick(Sender: TObject); private { Private declarations } public { Public declarations } end; var MainForm: TMainForm; implementation {$R *.dfm} procedure TMainForm.btnAddClick(Sender: TObject); begin { dodawanie nowego rekordu } Table.Append; end; procedure TMainForm.FormCreate(Sender: TObject); begin Table.Active := True; end; procedure TMainForm.FormDestroy(Sender: TObject); begin Table.Active := False; end; procedure TMainForm.btnSaveClick(Sender: TObject); begin { akceptacja zmian } Table.Post; end; procedure TMainForm.btnRemoveClick(Sender: TObject); begin { usuwanie zaznaczonej pozycji } Table.Delete; end; end.

Dziaanie tego programu jest widoczne na rysunku 16.8. Zmiany takie, jak dodawanie nowego 631 | S t r o n a

rekordu czy jego modyfikacja, s dokonywany w sposb wizualny za pomoc kontrolki TDBGrid.

Rysunek 16.8. Aplikacja prezentujca przykadowe uycie bazy danych

Podsumowanie
W tym rozdziale nauczye si tworzy lokalne aplikacje bazodanowe wykorzystujce mechanizm BDE. We wszystkich zaprezentowanych tu przykadach wykorzystano baz danych Paradox. W kolejnym rozdziale zaprezentuj sposb tworzenia aplikacji korzystajcych z baz danych bez uywania BDE. Zaczniki:

Listingi_16.zip (9.46 kB)

Rozdzia 17

Edytuj Historia Przenie Obserwuj 632 | S t r o n a

Bazy danych dbExpress


W poprzednim rozdziale omawiaem korzystanie z baz danych przy uyciu komponentw BDE. Inn ? moim zdaniem ciekaw ? form dostpu do baz danych jest technologia dbExpress. Dziki komponentom z zakadki dbExpress moesz w prosty sposb uzyska dostp do najpopularniejszych systemw baz danych, takich jak MS SQL, MySQL, Interbase i Oracle.

Spis treci 1 Aplikacje klient-serwer 1.1 Narzdzia 1.2 Komponenty 2 czenie z serwerem 2.1 Kontrola procesu logowania 2.2 Zdarzenia AfterConnect i AfterDisconnect 3 Jak dziaa MySQL? 3.1 Tabele 3.2 Zapytania 4 Tworzenie tabel 5 Dodawanie rekordw 6 Kasowanie rekordw 6.1 Procedura kasujca 7 Odczytywanie rekordw 7.1 Przykadowy program 8 Zmiana wartoci rekordw 8.1 Przykadowy program: spis sprzedanych towarw 9 Inne komponenty dbExpress 10 Programy oparte o dbExpress 11 Podsumowanie

Podstaw korzystania z rnych baz danych s tzw. sterowniki, ktre umoliwiaj dostp do systemw baz i peni rol porednikw. Wanym czynnikiem wpywajcym na jako technologii dbExpress jest to, e s to komponenty midzyplatformowe, zapewniajce zgodno z Delphi oraz ze rodowiskiem Kylix. W tym rozdziale:

633 | S t r o n a

dowiesz si, na czym polega tworzenie aplikacji typu klient-serwer; nauczysz si komunikowa z baz danych MySQL; zaprojektujesz prost aplikacj baz danych, czc si z serwerem.

Aplikacje klient-serwer
Architektura baz danych typu klient-serwer polega na czeniu aplikacji-klienta z serwerem, na ktrym znajduje si centralna baza danych. Zasada ta jest podobna do omawianej w rozdziale 11. komunikacji za pomoc gniazd. Rnica polega na tym, e na serwerze jest zainstalowany jaki system baz danych, a my moemy tylko si z nim poczy. SQL to skrt od angielskich sw Structured Query Language. Jest to jzyk oparty na specjalnych zapytaniach kierowanych do bazy.

Narzdzia
Podczas pisania tego rozdziau i przygotowywania przykadowych programw posu si darmowymi narzdziami, ktre moe atwo zdoby kadego, kto ma dostp do Internetu. Jako serwer wykorzystamy serwer lokalny Apache 1.3. Baza danych oparta bdzie na MySQL. Po pierwsze, baza danych MySQL jest do atwa w uyciu, bardzo popularna, darmowa i stosunkowo szybka. Serwer Apache take jest darmowy i niezwykle popularny. Oba te programy dostpne s zarwno w wersjach dla Linuksa, jak i dla Windows. Najnowsza wersja serwera Apache znajduje si na stronie www.apache.org, natomiast serwer baz danych MySQL moesz pobra ze strony www.mysql.com. Sposb instalacji Apache oraz MySQL moesz znale m.in. na stronie www.4programmers.net. Ze wzgldu na do znaczne rozmiary tych aplikacji postanowiem ? z myl o Czytelnikach, ktrzy posiadaj modemy ? umieci te aplikacje na doczonej do ksiki pycie CD-ROM.

Komponenty
W przykadach z tego rozdziau wykorzystamy komponenty bazodanowe z zakadki dbExpress. Owe komponenty s raczej atwe w uyciu, jeeli pozna si zasad ich dziaania. Na pocztku poczenie si z serwerem moe sprawi niewielkie problemy, lecz pniej wszystko powinno pj gadko.

634 | S t r o n a

czenie z serwerem
Do poczenia z serwerem MySQL bdziemy musieli uy komponentu TSQLConnection z zakadki dbExpress. Pierwsza rzecz, jak musisz zrobi, to skopiowanie do katalogu z programem biblioteki libmysql.dll, ktra jest wymagana do poczenia. w plik moesz znale w pakiecie MySQL jak rwnie na doczonej do ksiki pycie CD-ROM, w katalogu ../listingi/17/Connect. Umie wic komponent TSQLConnection na formularzu i zmie jego nazw na MySQL. Nastpnie bdziesz musia zmieni warto waciwoci ConnectionName na MySQLConnection (rysunek 17.1); spowoduje to automatyczne dopasowanie kilku pozostaych waciwoci.

Rysunek 17.1. Lista moliwych pocze Komponent odczytuje list sterownikw z plikw dbxconnections.ini oraz dbxdrivers.ini (u mnie te pliki znajduj si w katalogu E:\Borland Shared\DBExpress). Pliki INI zawieraj take parametry, ktre s wczytywane do waciwoci Params w Inspektorze Obiektw. Aby mc nawiza poczenie, naley zmodyfikowa jeszcze odpowiednie wartoci we waciwoci Params. Wybierz t waciwo ? zostanie wywietlone okno edycji parametrw, takie jak na rysunku 17.2.

635 | S t r o n a

Rysunek 17.2. Okno edycji parametrw W tym momencie powiniene uruchomi serwer MySQL i stworzy w nim jak przykadow baz danych ? np. delphi. W oknie edycji parametrw w pozycji Host Name wpisz adres serwera ? w moim przypadku jest to 127.0.0.1. Pozycja Database okrela baz danych, na jakiej odbd si operacje ? wpisz tutaj nazw utworzonej bazy danych, czyli delphi. Pozostao jeszcze okrelenie nazwy uytkownika bazy oraz haso (pola User_Name oraz Password). Wpisz tutaj nazw uytkownika i haso do swojej bazy danych. W moim przypadku ani haso, ani nazwa uytkownika nie s wymagane, mog wic usun te pola z edytora. Poczenie ustanowi mona poprzez waciwo Connected komponentu TSQLConnection. Waciwo Connected jest typu Boolean ? przypisanie jej wartoci True spowoduje prb poczenia z serwerem: procedure TMainForm.btnConnectClick(Sender: TObject); begin if MySQL.Connected then MySQL.Connected := False else MySQL.Connected := True; end;

Wykonanie powyszego kodu spowoduje albo rozczenie, albo poczenie z baz danych ? w zalenoci od aktualnego stanu. W przypadku zmiany waciwoci LoginPromt na False podczas czenia nie zostanie wywietlone okno logowania. Program pobierze nazw uytkownika oraz haso z parametrw User_Name oraz Password. Jeeli dostp do Twojej bazy nie wymaga nazwy uytkownika ani hasa, usu klucze User_Name oraz 636 | S t r o n a

Password z okna edytora waciwoci (rysunek 17.2).

Kontrola procesu logowania


Nie kady chce, aby przed zalogowaniem komponent wywietla okienko, w ktrym trzeba poda nazw uytkownika i haso. Lepszym rozwizaniem jest umieszczenie na formularzu dwch etykiet, w ktrych uytkownik bdzie mg wprowadzi swoje haso oraz nazw uytkownika. To zadanie mona zrealizowa, korzystajc ze zdarzenia OnLogin: procedure TMainForm.MySQLLogin(Database: TSQLConnection; LoginParams: TStrings); begin LoginParams.Values['User_Name'] := edtLogin.Text; LoginParams.Values['password'] := edtPassword.Text; end;

Zdarzenie to wystpuje zawsze przed zalogowaniem si do systemu. Powoduje ono przypisanie nazwy uytkownika oraz hasa z etykiet tekstowych.

Zdarzenia AfterConnect i AfterDisconnect


Aby sprawdzi, czy program zosta ju poczony z baz danych, wystarczy wygenerowa dwa zdarzenia: AfterConnect oraz AfterDisconnect. Oba wystpuj po zawarciu poczenia oraz po rozczeniu z serwerem (rysunek 17.3): procedure TMainForm.MySQLAfterConnect(Sender: TObject); begin StatusBar.SimpleText := 'Poczony ...'; end; procedure TMainForm.MySQLAfterDisconnect(Sender: TObject); begin StatusBar.SimpleText := 'Rozczony...'; end;

637 | S t r o n a

Rysunek 17.3. Tekst informujcy o nawizaniu poczenia

Jak dziaa MySQL?


Przed przystpieniem do dalszych dziaa musisz opanowa podstawy oraz zasady dziaania bazy danych MySQL. MySQL (www.mysql.com) jest darmow, opart na licencji GNU platform baz danych. Swoj popularno zawdzicza gwnie temu, e jest darmowa ? rwnie w zastosowaniach komercyjnych. Stay rozwj tego programu zapewniaj setki programistw, wsppracujcych przy tworzeniu oraz poprawianiu tej bazy danych.

Tabele
Baza danych jest pojciem o wielu znaczeniach. W systemie MySQL baza danych jest zbiorem tabel. Natomiast tabela jest uporzdkowanym zbiorem kolumn i wierszy, niczym tabela w programie Excel albo Word (rysunek 17.4).

Rysunek 17.4. Zasada dziaania tabel Na powyszym rysunku przedstawiona jest tabela skadajca si z trzech kolumn i zawierajca dwa rekordy. Taka sama idea zastosowana jest w bazach danych (w tym MySQL). Myl, e to pojcie jest dla Ciebie ju w miar jasne, chociaby po lekturze poprzedniego rozdziau o aplikacjach BDE.

Zapytania
Z wydawaniem ?rozkazw? bazie danych wie si pojcie zapyta. Zapytania przekazywane do bazy danych nakazuj jej wykonanie pewnych czynnoci: utworzenia tabeli, dodania kolumny, usunicia kolumny, wstawienia nowego rekordu (wiersza) czy usunicia bazy danych. Aby zapytanie zostao prawidowo zinterpretowane przez baz danych, musi by skonstruowane wedug odpowiedniej skadni. Przykadowo zapytanie nakazujce stworzenie nowej tabeli wyglda tak: CREATE TABLE users ( id int(11) NOT NULL AUTO_INCREMENT, name varchar(128) NOT NULL DEFAULT '', mail varchar(128) NOT NULL DEFAULT '', 638 | S t r o n a

UNIQUE KEY id (id) )

Tworzona tabela bdzie si nazywa users oraz bdzie posiada trzy kolumny: id (identyfikator rekordu), name i mail. Przeznaczeniem tej tabeli jest przechowywanie informacji na temat zarejestrowanych uytkownikw, a zatem kolumna name moe okrela nazw uytkownika (pseudonim, imi lub nazwisko), a mail adres e-mail wpisujcej si osoby. Numer id jest zwikszany za kadym dodaniem rekordu (auto_increment). Maksymalna liczba znakw, jak moe zawiera dana kolumna, to 128 (varchar(128)) ? adna z kolumn nie moe by pusta (NOT NULL), a domylna warto to cig pusty (default ''). Kolumna id bdzie unikatowa (UNIQUE). MySQL nie rozrnia maych i wielkich liter w zapytaniach. Oznacza to, e zapytania CREATE TABLE i create table zostan zinterpretowane tak samo.

Tworzenie tabel
Do utworzenia tabeli przez nasz program bdziemy potrzebowali komponentu TSQLDataSet; umie go na formularzu i nazwij SQL. Kolejnym krokiem jest wybranie z listy SQLConnection komponentu MySQL. Zapytanie mona ustawi we waciwoci CommandText, zaznaczajc ow waciwo, a nastpnie naciskajc przycisk wielokropka, co spowoduje otwarcie edytora takiego, jak na rysunku 17.5.

639 | S t r o n a

Rysunek 17.5. Edytor zapyta W polu SQL wpisz takie oto zapytanie: CREATE TABLE users ( id int(11) NOT NULL AUTO_INCREMENT, name varchar(128) NOT NULL DEFAULT '', mail varchar(128) NOT NULL DEFAULT '', UNIQUE KEY id (id) )

Moesz ju zamkn edytor; wpisane zapytanie spowoduje utworzenie tabeli users. eby wysa zapytanie, naley uy funkcji ExecDir z naszego komponentu SQL. Utworzenie tabeli (wysanie zapytania) realizowane jest przez ten kod: procedure TMainForm.btnCreateTableClick(Sender: TObject); begin if SQL.ExecSQL(True) = 0 then MessageDlg('Tabela utworzona!', mtInformation, [mbOK], 0); end;

Funkcja ExecSQL zwraca cyfr 0, jeeli operacja si powioda.

640 | S t r o n a

Dodawanie rekordw
Wstawienie rekordu do istniejcej ju tabeli odbywa si za porednictwem zapytania INSERT. Jeeli znamy dane, ktre chcemy umieci w tabeli, to sformuowanie zapytania nie powinno przysporzy problemu: INSERT users SET name='Adam Boduch',mail='adam@boduch.net'

Zwr uwag, e nie musimy podawa pierwszego parametru ? id. Baza MySQL automatycznie nada warto kolumnie id, gdy przypisalimy jej parametr auto_increment. Na formularzu moesz umieci dwie etykiety tekstowe, w ktrych uytkownik bdzie mg poda swoje imi oraz adres e-mail (rysunek 17.6). Kod programu prezentuje listing 17.1.

Rysunek 17.6. Dodawanie nowego rekordu do bazy Listing 17.1. Kod rdowy programu { Copyright (c) 2002 by Adam Boduch <adam@4programmers.net> } unit MainFrm; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, DBXpress, DB, SqlExpr, StdCtrls, FMTBcd; type TMainForm = class(TForm) MySQL: TSQLConnection; btnConnect: TButton; 641 | S t r o n a

SQL: TSQLDataSet; GroupBox1: TGroupBox; Label1: TLabel; Label2: TLabel; edtLogin: TEdit; edtMail: TEdit; btnAdd: TButton; procedure btnConnectClick(Sender: TObject); procedure MySQLAfterConnect(Sender: TObject); procedure MySQLAfterDisconnect(Sender: TObject); procedure btnAddClick(Sender: TObject); private Connected : Boolean; public { Public declarations } end; var MainForm: TMainForm; implementation {$R *.dfm} procedure TMainForm.btnConnectClick(Sender: TObject); begin if Connected then Connected := False else Connected := True; MySQL.Connected := Connected; end; procedure TMainForm.MySQLAfterConnect(Sender: TObject); begin btnConnect.Caption := 'Rozcz'; end; procedure TMainForm.MySQLAfterDisconnect(Sender: TObject); begin btnConnect.Caption := 'Pocz'; end; procedure TMainForm.btnAddClick(Sender: TObject); var SQLQuery : String; begin SQLQuery := Format('INSERT INTO users SET name="%s", mail="%s"', [edtLogin.Text, edtMail.Text]); SQL.CommandText := SQLQuery; if SQL.ExecSQL(True) = 1 then MessageDlg('Rekord zosta dodany!', 642 | S t r o n a

mtInformation, [mbOK], 0); end; end.

Na samym pocztku konieczne staje si odpowiednie sformuowanie zapytania (czyli acucha), ktrego warto przydzielimy waciwoci CommandText komponentu TSQLDataSet. Rekord powinien zosta wstawiony do bazy po wykonaniu funkcji ExecSQL. Jeli wstawienie zostanie wykonane pomylnie, funkcja ExecSQL zwrci warto rn od 0.

Kasowanie rekordw
Jeeli uznamy, e przy wstawianiu nowej pozycji pomylilimy si lub nie jest ona nam ju potrzebna ? moemy j z atwoci usun. Do tego suy zapytanie DELETE. Formuujc zapytanie, naley poda dane, wedug ktrych baza usunie rekord (np. numer ID): DELETE FROM users WHERE id='1'

Takie zapytanie moe by tworzone na rny sposb ? tj. rne warunki mog spowodowa usunicie danych. Kryterium moe by nazwa uytkownika: DELETE FROM users WHERE name='Adam Boduch'

W powyszym przypadku usunite zostan wszystkie rekordy, w ktrych kolumna name ma warto Adam Boduch. Aby zaostrzy kryterium usuwania danych, mona zastosowa operatory AND i OR (tak samo, jak w Delphi) DELETE FROM users SET name='Adam' AND mail='adam@boduch.net'

W kocu moe istnie wiele rekordw, w ktrych kolumna name ma warto Adam, prawda? eby nie usun niepotrzebnie danych, zastosujemy operator AND w celu podania rwnie wartoci kolumny mail.

643 | S t r o n a

Procedura kasujca
W naszym przypadku najlepiej usuwa rekord, biorc pod uwag kolumn id, ktra w adnym wypadku nie bdzie si powtarza: procedure TMainForm.btnDeleteClick(Sender: TObject); begin SQL.CommandText := 'DELETE FROM users where id="1"'; SQL.ExecSQL(True); end;

Odczytywanie rekordw
Chyba najtrudniejszym zadaniem w przypadku dbExpress jest odczytywanie rekordw. Samo dbExpress jest w gruncie rzeczy proste do opanowania, lecz odczyt danych wymaga powicenia wikszej iloci czasu i wierszy kodu. Sam odczyt jest realizowany przez zapytanie SELECT: SELECT * FROM users

Taka instrukcja pobiera wszystkie rekordy znajdujce si w bazie danych. Moliwe jest zaostrzenie tego kryterium i odczytanie tylko wybranych fragmentw: SELECT * FROM users WHERE name='Adam'

Powysze zapytanie wywietli jedynie rekordy zawierajce w kolumnie name warto Adam. Po wysaniu zapytania odczyt konkretnych elementw z tabeli odbywa si za pomoc waciwoci FieldValues: SQL.FieldValues['name']

Nazw kolumny, ktr chcemy odczyta, naley wstawi w nawiasie kwadratowym. Warto zwracana przez ow waciwo jest typu Variant, wic znika problem konwersji pomidzy typami (w przypadku, gdyby np. kolumna id bya typu Integer). Jeli mamy odczyta wiele rekordw, naley to uczyni w ptli for:

644 | S t r o n a

for I := 1 to SQL.RecordCount do begin { dodaj kolejne wartoci } ListItem := ListView.Items.Add; ListItem.Caption := IntToStr(SQL.FieldValues['id']); ListItem.SubItems.Add(SQL.FieldValues['name']); ListItem.SubItems.Add(SQL.FieldValues['mail']); SQL.Next; end;

Waciwo RecordCount zawiera ilo rekordw, ktre zostay zwrcone przez baz ? jeli znamy t liczb, moemy rozwiza problem odczytania wszystkich wierszy. Zwr uwag na przedostatni wiersz ? wywoanie instrukcji Next z komponentu TSQLDataSet. Bez tej instrukcji odczytanie kolejnego rekordu nie byoby moliwe ? w wyniku jej wykonania komponent przechodzi do nastpnego elementu.

Przykadowy program
Przed czytaniem wszystkich elementw zwrconych przez baz naley wywoa metod Open komponentu TSQLDataSet, a po zakoczeniu operacji ? procedur Close. Peny kod programu zaprezentowano w listingu 17.2. Listing 17.2. Odczytywanie oraz kasowanie rekordw z bazy danych { Copyright (c) 2002 by Adam Boduch <adam@4programmers.net> } unit MainFrm; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, DBXpress, DB, SqlExpr, StdCtrls, FMTBcd, Grids, ValEdit, ComCtrls; type TMainForm = class(TForm) MySQL: TSQLConnection; btnConnect: TButton; SQL: TSQLDataSet; ListView: TListView; 645 | S t r o n a

btnDelete: TButton; procedure btnConnectClick(Sender: TObject); procedure MySQLAfterConnect(Sender: TObject); procedure MySQLAfterDisconnect(Sender: TObject); procedure FormClose(Sender: TObject; var Action: TCloseAction); procedure btnDeleteClick(Sender: TObject); private Connected : Boolean; procedure LoadTable; public { Public declarations } end; var MainForm: TMainForm; implementation {$R *.dfm} procedure TMainForm.btnConnectClick(Sender: TObject); begin if Connected then Connected := False else Connected := True; MySQL.Connected := Connected; end; procedure TMainForm.MySQLAfterConnect(Sender: TObject); begin btnConnect.Caption := 'Rozcz'; btnDelete.Enabled := True; LoadTable; end; procedure TMainForm.MySQLAfterDisconnect(Sender: TObject); begin btnConnect.Caption := 'Pocz'; btnDelete.Enabled := False; end; procedure TMainForm.FormClose(Sender: TObject; var Action: TCloseAction); begin // podczas zamknicia programu ? rozczenie z serwerem MySQL.Connected := False; end; procedure TMainForm.LoadTable; var 646 | S t r o n a

i : Integer; ListItem : TListItem; begin ListView.Items.Clear; SQL.CommandText := 'SELECT * FROM users'; // zapytanie SQL.Open; // odczytaj dane for I := 1 to SQL.RecordCount do begin { dodaj kolejne wartoci } ListItem := ListView.Items.Add; ListItem.Caption := IntToStr(SQL.FieldValues['id']); ListItem.SubItems.Add(SQL.FieldValues['name']); ListItem.SubItems.Add(SQL.FieldValues['mail']); SQL.Next; end; SQL.Close; end; procedure TMainForm.btnDeleteClick(Sender: TObject); begin SQL.CommandText := Format('DELETE FROM users where id="%s"', [ListView.Selected.Caption]); SQL.ExecSQL(True); LoadTable; // po wykonaniu zapytania ? wywoaj procedur end; end.

Zwr uwag, e program oprcz odczytania elementw z bazy danych do komponentu TListViewk potrafi take te elementy usuwa ? suy do tego przycisk btnDelete. Sam program w dziaaniu jest przedstawiony na rysunku 17.7.

647 | S t r o n a

Rysunek 17.7. Dane odczytane z serwera MySQL

Zmiana wartoci rekordw


W celu zmiany wartoci rekordw wystarczy wysa do bazy zapytanie SQL UPDATE. UPDATE users SET name='adam@boduch.net'

Powysze zapytanie uaktualni warto kolumny name ? nada jej warto adam@boduch.net. Problem w tym, e uaktualnienie obejmie wszystkie wiersze danej tabeli. Aby zmieni warto jednego tylko rekordu, naley doda warunek WHERE: UPDATE users SET name='adam@boduch.net' WHERE ID='1'

W powyszym przypadku uaktualnienie dotyczy bdzie jedynie rekordu o wartoci ID rwnej 1. SQL.CommandText := 'UPDATE users SET name="Jan Kowalski" WHERE name="Adam Boduch"'; SQL.ExecSQL(True);

Powyszy fragment kodu spowoduje uaktualnienie wszystkich rekordw, w ktrych kolumnie name znajduje si acuch Adam Boduch. Nowa warto kolumny name to od tego momentu Jan Kowalski.

648 | S t r o n a

Przykadowy program: spis sprzedanych towarw


Na doczonej do ksiki pycie CD-ROM znajduje si program wykorzystujcy baz MySQL do umieszczania na serwerze listy sprzedanych towarw. Baza danych przechowuje informacj na temat nazwy towaru, iloci sprzedanych sztuk, ceny oraz daty wykonania transakcji. Zapytanie SQL, realizujce stworzenie odpowiedniej tabeli, wyglda tak: CREATE TABLE towary ( id int(11) NOT NULL AUTO_INCREMENT, towar char(255) NOT NULL DEFAULT '', sztuk smallint(2) NOT NULL DEFAULT '0', cena float NOT NULL DEFAULT '0', DATA datetime NOT NULL DEFAULT '0000-00-00 00:00:00', UNIQUE KEY id (id) ) TYPE=MyISAM;

Kolumna cena bdzie typu zmiennoprzecinkowego, a data ? typu datetime, ktry umoliwia przechowywanie dat i czasu. Program bdzie si skada z dwch formularzy: jeden posuy do odczytywania danych i przedstawienia ich w komponencie TListView, a drugi bdzie suy do dodawania nowych rekordw. W praktyce jest to podsumowanie tego wszystkiego, co zaprezentowaem w poprzednich podpunktach rozdziau. Kody rdowe obu formularzy przedstawione s w listingach 17.3 i 17.4. Listing 17.3. Kod rdowy moduu MainFrm.pas unit MainFrm; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls, ComCtrls, DBXpress, FMTBcd, DB, SqlExpr; type TMainForm = class(TForm) ListView: TListView; GroupBox1: TGroupBox; btnConnect: TButton; btnInsert: TButton; btnRemove: TButton; SQL: TSQLConnection; Query: TSQLDataSet; procedure btnInsertClick(Sender: TObject); procedure btnConnectClick(Sender: TObject); 649 | S t r o n a

procedure btnRemoveClick(Sender: TObject); private public procedure LoadTable; end; var MainForm: TMainForm; implementation uses InsertFrm; {$R *.dfm} procedure TMainForm.btnInsertClick(Sender: TObject); begin { wywietl formularz sucy do dodawania kolejnych rekordw } InsertForm := TInsertForm.Create(Application); InsertForm.ShowModal; InsertForm.Free; end; procedure TMainForm.LoadTable; var i : Integer; ListItem : TListItem; begin ListView.Items.Clear; Query.CommandText := 'SELECT * FROM towary'; // odczytanie danych z tabeli Query.Open; { przedstaw informacje w komponencie TListView } for I := 0 to Query.RecordCount do begin ListItem := ListView.Items.Add; ListItem.Caption := Query.FieldValues['id']; ListItem.SubItems.Add(Query.FieldValues['towar']); ListItem.SubItems.Add(Query.FieldValues['sztuk']); ListItem.SubItems.Add(Query.FieldValues['cena'] + ' z'); ListItem.SubItems.Add(Query.FieldValues['data']); Query.Next; end; Query.Close; 650 | S t r o n a

end; procedure TMainForm.btnConnectClick(Sender: TObject); begin SQL.Connected := True; // pocz LoadTable; SQL.Connected := False; end; procedure TMainForm.btnRemoveClick(Sender: TObject); begin SQL.Connected := True; // pocz Query.CommandText := Format('DELETE FROM users where id="%s"', [ListView.Selected.Caption]); Query.ExecSQL(True); LoadTable; // po wykonaniu zapytania ? wywoaj procedur SQL.Connected := False; // rozcz z serwerem end; end.

Listing 17.4. Kod rdowy moduu InsertFrm.pas unit InsertFrm; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls; type TInsertForm = class(TForm) GroupBox1: TGroupBox; Label1: TLabel; Label2: TLabel; Label3: TLabel; btnInsert: TButton; edtThi: TEdit; edtCount: TEdit; edtPrice: TEdit; procedure btnInsertClick(Sender: TObject); private { Private declarations } public { Public declarations } 651 | S t r o n a

end; var InsertForm: TInsertForm; implementation uses MainFrm; {$R *.dfm} procedure TInsertForm.btnInsertClick(Sender: TObject); begin with MainForm do begin SQL.Connected := True; // pocz z serwerem // wylij zapytanie Query.CommandText := Format('INSERT INTO towary SET towar="%s",sztuk="%s",cena="%s",data=NOW()', [edtThi.Text, edtCount.Text, edtPrice.Text]); Query.ExecSQL(True); LoadTable; // ponownie wywietl zawarto tabeli SQL.Connected := False; Close; end; end; end.

W przypadku, gdy masz na dysku dwie wersje Delphi ? 6 oraz 7 ? mog pojawi si problemy w korzystaniu z dbExpress, np. bd Operation Not Supported.

Inne komponenty dbExpress


W zakadce dbExpress ? oprcz komponentw TSQLConnction oraz TSQLDataSet ? znajduj si rwnie komponenty TSQLQuery, TSQLStoredProc, TSQLTable, TSQLMonitor i TSQLSimpleDataSet. Ciekawym komponentem jest TSQLMonitor. Umoliwia on monitorowanie komend przesyanych pomidzy komputerami za pomoc komponentu TSQLConnection. Umie na formularzu komponent TSQLMonitor i z listy rozwijalnej waciwoci SQLConnection wybierz komponent typu TSQLConnection. Zdarzenie OnLogTrace komponentu SQLMonitor wystpuje w trakcie komunikacji midzy komputerami. 652 | S t r o n a

procedure TMainForm.SQLMonitorLogTrace(Sender: TObject; CBInfo: pSQLTRACEDesc); begin Memo.Lines := SQLMonitor.TraceList; end;

Jak widzisz, w prosty sposb mona list wiadomoci wywietli w komponencie Memo. SQLMonitor posiada bowiem waciwo TraceList typu TStringList, ktra zawiera wszystkie wiadomoci. Aby komponent SQLMonitor zacz dziaa, naley zmieni waciwo Active na True. W zdarzeniu OnAfterConnect komponentu TSQLConnection wpisz przykadowy kod, ktry wykona jakie zapytanie SQL.

SQL.CommandText := 'SELECT * FROM users; SQL.ExecSQL(True);

Zapytanie SELECT nic nie wnosi do programu, gdy nie mona uywa metody ExecSQL ? ma ona za zadanie wykonanie pewnych czynnoci, ktre bdziemy obserwowa podczas dziaania programu w komponencie Memo. Rysunek 17.8 przedstawia program podczas dziaania.

Rysunek 17.8. Monitorowanie SQL Komponent TSQLMonitor posiada przydatn waciwo FileName. Dziki niej monitorowane wiadomoci mog by automatycznie dodawane do pliku okrelonego wanie we waciwoci FileName. Pozostae komponenty z zakadki dbExpress nie oferuj nic ponad to, co komponent TSQLDataSet; funkcjonuj podobnie jak ich odpowiedniki w BDE. 653 | S t r o n a

Programy oparte o dbExpress


Sterowniki baz danych pakietu dbExpress s zawarte w oddzielnych bibliotekach DLL. W celu przeniesienia caej aplikacji na inny komputer naley doczy pewne pliki *.dll. Jakie? O tym informuje tabela 17.1. Tabela 17.1. Biblioteki DLL wymagane w poczeniu z rnymi bazami danych Biblioteka dbexpdb2.dll Przeznaczenie Bazy danych DB

dbexpmysql.dll Bazy danych MySQL dbexpmss.dll dbexpora.dll dbexpinf.dll dbexpint.dll Bazy danych MS SQL Bazy danych Oracle Sterowniki Informix Bazy danych Interbase

Podsumowanie
Technologia dbExpress jest bardzo efektywna i do atwa w uyciu ? zapewnia dostp do wielu baz danych. Pewnie jeszcze wielokrotnie skorzystasz z dbEpxress i nieraz usyszysz o tej technologii. Zaczniki:

Listingi_17.zip (140.20 kB)

Podsumowanie czci IV
654 | S t r o n a

Edytuj Historia Przenie Obserwuj

Podsumowanie czci IV
Caa IV cz tej ksiki zostaa powicona tworzeniu aplikacji z wykorzystaniem baz danych. Mam nadziej, e wiedza ta jeszcze nieraz przyda Ci si przy okazji tworzenia programw w Delphi. Rozdzia 16. zawiera informacje dotyczce programowania baz danych z uyciem technologii BDE. Napisae program wykorzystujcy baz danych Paradox oraz dowiedziae si, jak funkcjonuj bazy danych i czym waciwie s. W rozdziale 17. nasz uwag skupilimy na technologii dbExpress, dziki ktrej istniej moliwo poczenia si z tak popularnym systemem baz danych, jak MySQL. Pokazaem, w jaki sposb mona tworzy aplikacje czce si z serwerem i pobierajce z niego informacje.

655 | S t r o n a

Cz V

Edytuj Historia Przenie Obserwuj

Cz V
Ostatnia cz tej ksiki bdzie powicona tworzeniu aplikacji internetowych z wykorzystaniem Delphi. Piszc sowo internetowych, nie mam na myli programw wykorzystujcych sie Internet, tylko aplikacji dziaajcych jako strona WWW ? tak, aby kady uytkownik posiadajcy przegldark internetow mg skorzysta z naszego programu bez koniecznoci instalowania go na swoim dysku. Program bdzie wykonywany na serwerze. Na pocztek w rozdziale 18. zajmiemy si tworzeniem bibliotek ISAPI, wykorzystujc technologi obecn w Delphi od dawna. Zaprezentuj, w jaki sposb mona wykorzysta mechanizmy CGI i ISAPI do tworzenia dynamicznych stron WWW ? czyli programu dziaajcego w oknie przegldarki. Kolejny, ostatni rozdzia powicony bdzie projektowaniu aplikacji przy uyciu IntraWeb ? nowej technologii, jak pojawia si dopiero w Delphi 7. Za jej pomoc mona tworzy dynamiczne strony WWW, wykorzystujc standardowe kontrolki Delphi (przyciski, listy rozwijalne itp.).

Rozdzia 18

Edytuj Historia Przenie Obserwuj

Delphi a Internet
Tak, tak, o Internecie bya mowa ju w rozdziale 11., lecz wwczas podjem jedynie temat programowania sieciowego z uyciem rnych protokow internetowych. Tym razem sprawa wyglda nieco inaczej, bowiem mowa bdzie o internetowych zastosowaniach Delphi. 656 | S t r o n a

W dziedzinie informatyki postp jest nieunikniony. Powstaj nowe usugi, a jzyki programowania wci s rozwijane. Firma Borland postanowia udoskonali swj produkt w zakresie internetowych zastosowa, umoliwiajc uytkownikom tworzenie programw dziaajcych w sieci Internet, tzw. weplikacji (zlepek sw Web Application). Tworzenie aplikacji internetowych nie jest nowoci wprowadzon dopiero w Delphi 7 ? istniao ju w poprzednich wersjach tego produktu. Owe technologie umoliwiaj tworzenie dynamicznych stron WWW, nie wymagajc od uytkownika znajomoci jzyka HTML, nie mwic ju o jzykach programowania takich jak PHP, CGI, Java czy JavaScript.

Spis treci 1 Z czego bdziemy korzystali? 1.1 Serwer Personal Web Server 2 CGI, ISAPI, NSAPI 3 Tworzenie rozszerze serwera 4 Akcje serwera 5 Uruchamianie biblioteki 6 Kod rdowy biblioteki ISAPI 7 TWebRequest i TWebResponse 8 Wykorzystanie szablonw 8.1 Tworzenie nowego szablonu 8.2 Szablony dynamiczne 8.2.1 Zdarzenie OnHTMLTag 8.3 Przykadowy program 8.4 Dodatkowe parametry 9 Wysyanie i odbieranie cookies 9.1 Ustawianie pliku cookies 9.2 Odczyt cookies 10 Wysyanie strumieni 11 Korzystanie z baz danych 12 WebSnap 13 Podsumowanie

W tym rozdziale:

poznasz znaczenie terminw ISAPI, NSAPI i CGI; dowiesz si, w jaki sposb mona tworzy dynamiczne serwisy WWW; nasze przykady bd opiera si o tworzenie aplikacji z wykorzystaniem technologii ISAPI.

657 | S t r o n a

Z czego bdziemy korzystali?


Do zrealizowania niektrzy zada bdziemy potrzebowa serwera WWW obsugujcego standard ISAPI . Co prawda niektre technologie w Delphi udostpniaj wasny serwer na potrzeby dziaania aplikacji, lecz chcc zaprezentowa w peni dziaanie programw, bd potrzebowa ?prawdziwego? serwera. Ze wzgldu na atwo obsugi i dostpno na potrzeby tego rozdziau wybraem serwer firmy Microsoft ? Personal Web Server, ale Ty moesz skorzysta z dowolnego serwera obsugujcego standard ISAPI ? np. IIS lub Apache.

Serwer Personal Web Server


Program Personal Web Serwer (PWS) jest dostarczany wraz z systemem operacyjnym Windows. W moim przypadku (Windows 98) jest dostpny na pycie CD-ROM w katalogu add-one. Jego instalacja jest prosta ? wystarczy postpowa zgodnie ze wskazwkami wywietlanymi na ekranie. Po instalacji na pulpicie zostanie utworzony skrt do owego serwera, a na dysku C: katalog Inetpub. Program w trakcie dziaania przedstawiony jest na rysunku 18.1.

Rysunek 18.1. Program Personal Web Server 658 | S t r o n a

Waciwe uruchomienie serwera nastpi po naciniciu przycisku Uruchom. Od tego momentu po wpisaniu w przegldarce internetowej adresu http://127.0.0.1 wczytana zostanie strona z naszego serwera. Program Personal Web Server mona take pobra z Internetu ? wystarczy skorzysta z jakie popularnej wyszukiwarki, np. www.google.pl.

CGI, ISAPI, NSAPI


wiat Internetu jest peen niezrozumiaych poj, jak chociaby CGI czy ISAPI. Za chwil postaram si objani, o co waciwie w tym wszystkim chodzi. Kiedy powsta Internet, a wraz z nim strony WWW, ich wywietlanie ograniczao si jedynie do statycznej prezentacji obrazu lub tekstu (czyli stron zapisanych w jzyku HTML ? ang. HyperText Markup Language). Z czasem, wobec wci powikszajcej si ?pajczyny? stron WWW, statyczne strony przestay wystarcza. Serwisy staway si coraz bardziej rozbudowane, a kada np. grafika wymagaa dokonania modyfikacji w kadej podstronie. Zaczto szuka rozwizania polegajcego na tworzeniu dynamicznych stron WWW, czyli takich, ktrych wygld zaley od okrelonej czynnoci. Pierwsz specyfikacj tego typu byo CGI (ang. Common Gataway Interface), ktre umoliwiao zapisywanie specjalnych skryptw (programw CGI), ktre generoway dynamiczne strony WWW. Obecnie skrypty CGI s pisanie przewanie w jzyku Perl, ktry jest zwykym jzykiem programowania ? dziki niemu mona w atwy sposb stworzy ksig goci, system nowoci oraz inne elementy interaktywnych stron WWW. W tamtym okresie na rynku serwerw WWW dominoway dwie firmy ? Microsoft oraz Netscape. Obie doceniy znaczenie tworzenia dynamicznych stron WWW i utworzyy podobne do siebie standardy ? ISAPI (Microsoft) oraz NSAPI (Netscape). Zarwno ISAPI, jak i NSAPI to w rzeczywistoci biblioteki DLL, umoliwiajce dynamiczne generowanie stron internetowych; nazywane s czsto rozszerzeniami serwerw WWW. Wiksz popularno zyskaa technologia ISAPI i to przede wszystkim ni zajmiemy si w tym rozdziale. Jednak ISAPI potrzebuje do dziaania serwera WWW ? std potrzebny nam by chociaby najprostszy serwer, jakim jest Personal Web Server. Obecnie dominujc technologi tworzenia stron WWW jest PHP, lecz niektre firmy (rzadziej prywatne osoby) wci stosuj ISAPI w celu zaprojektowania dynamicznych stron i dlatego zajmiemy si teraz ich tworzeniem. W tym miejscu naley si jeszcze jedna, maa uwaga. W momencie wpisania w przegldarce np. adresu http://127.0.0.1/SCRIPTS/ISAPI.dll zaadowana zostanie biblioteka DLL, ktra (niestety) bdzie przebywa w przestrzeni adresowej serwera a do jego zamknicia. Inaczej mwic, Windows nie pozwoli na usunicie takiej biblioteki ani jej zmodyfikowanie przed zakoczeniem pracy serwera. To 659 | S t r o n a

rozwizanie ma jednak zalety w postaci wikszej wydajnoci, w przeciwiestwie do programw CGI, ktre musz by uruchamiane za kadym wywoaniem. Nie wszystkie serwery stosuj takie praktyki ? w niektrych istnieje moliwo podmiany pliku w trakcie dziaania serwera.

Tworzenie rozszerze serwera


Tworzenie biblioteki ISAPI zaczynamy jak zwykle w Repozytorium (rysunek 18.2). Po zaznaczeniu ikony Web Server Application i klikniciu OK Delphi otworzy okno, w ktrym bdziemy musieli wybra rodzaj rozszerzenia serwera WWW ? patrz rysunek 18.3.

Rysunek 18.2. Repozytorium z zaznaczon ikon Web Server Application

660 | S t r o n a

Rysunek 18.3. Nowa aplikacja serwera Pierwsze domylnie zaznaczone pole to projekt biblioteki ISAPI lub NSAPI (tym si zajmiemy); kolejna opcja suy do tworzenia rozszerzenia typu CGI, ktre przebiega bardzo podobnie. Kolejne dwie pozycje zwizane s z tworzeniem moduw najpopularniejszego serwera WWW ? Apache. Mamy do wyboru tworzenie moduu do wersji 1.x tego serwera lub do wersji 2.x. Ostatnia pozycja zwizana jest z tworzeniem rozszerzenia wykorzystujcego specjalny debuger, ktry stanowi take serwer dla tworzonego projektu. W poprzednich wersjach Delphi moliwe byo take tworzenie aplikacji rozszerze Win-CGI (Common Gataway Interface for Windows), lecz obecnie Borland wycofa si z tej strategii, uznajc j za przestarza. Korzystanie z ISAPI (Internet Server API) jest moliwe jedynie na platformie Windows. Po wybraniu typu rozszerzenia Delphi utworzy formularz oparty o klas TWebModule, ktra stanowi jedynie ?pojemnik? na umieszczane komponenty. W rzeczywistoci nie pozwala na umieszczanie komponentw wizualnych, gdy nie ma to w tym wypadku najmniejszego sensu. Jedyne komponenty mogce znale si na formularzu to komponenty niewidoczne ? w szczeglnoci komponenty z zakadki Internet oraz komponenty do obsugi baz danych.

Akcje serwera
Klasa TWebModule implementuje system obsugi protokou HTTP, jednak wykorzystanie ISAPI opiera si na tzw. akcjach, w ramach ktrych program wykonuje pewne zadania. Sama ?akcja? doczana jest do adresu w przegldarce ? np.:

661 | S t r o n a

http://127.0.0.1/isapi.dll/test W tym wypadku akcj stanowi fragment /test. Tworzenie akcji odbywa si poprzez waciwo Action klasy TWebModule (rysunek 18.4).

Rysunek 18.4. Edytowanie akcji moduu aplikacji Cao odbywa si za porednictwem okna WebModule.Actions (rysunek 18.4). Okno to podzielone jest na poszczeglne kolumny: PathInfo jest wanie fragmentem wstawianym na koniec adresu i identyfikujcym dan akcj. Pierwsza kolumna ? Name ? okrela nazw. Utworzenie nowej akcji odbywa si poprzez nacinicie pierwszego przycisku z lewej lub naciniciu klawisza Insert. Kod rdowy, ktry ma zosta wykonany w wyniku wywoania takiego adresu, generowany jest za porednictwem zdarzenia OnAction: procedure TWebModule1.WebModule1WebActionItem1Action(Sender: TObject; Request: TWebRequest; Response: TWebResponse; var Handled: Boolean); begin end;

Informacja zwrotna musi zosta zawarta w parametrze Response typu TWebResponse. W parametrze k zawarte s informacje na temat dania HTTP, co niesie ze sob ciekawe informacje, jak nazwa protokou i inne nagwki HTTP. W naszym przykadzie kod caego moduu prezentuje si tak, jak w listingu 18.1. Listing 18.1. Kod rdowy moduu unit MainFrm; interface uses 662 | S t r o n a

SysUtils, Classes, HTTPApp; type TWebModule1 = class(TWebModule) procedure WebModule1WebActionItem1Action(Sender: TObject; Request: TWebRequest; Response: TWebResponse; var Handled: Boolean); private { Private declarations } public { Public declarations } end; var WebModule1: TWebModule1; implementation {$R *.dfm} procedure TWebModule1.WebModule1WebActionItem1Action(Sender: TObject; Request: TWebRequest; Response: TWebResponse; var Handled: Boolean); var HTML : String; begin HTML := '<html>' + '<head>' + '<title>Przykad wykorzystania ISAPI</title>' + '</head>'+ '<body>'+ '<h1>Witaj, uytkowniku!</h1>'+ '<hr>'+ '<i>Copyright (c) 2003 by Adam Boduch</i>'+ '</body>'+ '</html>'; Response.Content := HTML; end; end.

Na samym pocztku nastpuje formuowanie zawartoci strony WWW zwracanej w wyniku wywoania biblioteki. Kod HTML przypisywany jest do zmiennej HTML; zwracanie zawartoci tej zmiennej nastpuje w momencie przypisania jej do waciwoci Content parametru Response.

663 | S t r o n a

Uruchamianie biblioteki
Jako e nasza biblioteka nie moe dziaa samodzielnie, naley umieci j w odpowiednim katalogu serwera. Zbuduj wic bibliotek (Project/Build), w wyniku czego w katalogu z projektem utworzony zostanie plik Isapi1.dll (w moim przypadku). w plik naley umieci gdzie w katalogu serwera ? niech bdzie to C:\Inetpub\scripts. Nastpnie uruchom przegldark WWW i wpisz nastpujcy adres: http://127.0.0.1/scripts/isapi1.dll/default. Rezultat dziaania takiej biblioteki przedstawiony jest na rysunku 18.5.

Rysunek 18.5. Rezultat dziaania biblioteki ISAPI Nie zapominaj o ?doklejeniu? na kocu adresu WWW fragmentu /default.

Kod rdowy biblioteki ISAPI


Spjrz na kod rdowy gwnego projektu *.dpr biblioteki (Project/View Source): library isapi1;

664 | S t r o n a

uses ActiveX, ComObj, WebBroker, ISAPIThreadPool, ISAPIApp, MainFrm in 'MainFrm.pas' {WebModule1: TWebModule}; {$R *.res} exports GetExtensionVersion, HttpExtensionProc, TerminateExtension; begin CoInitFlags := COINIT_MULTITHREADED; Application.Initialize; Application.CreateForm(TWebModule1, WebModule1); Application.Run; end.

Na pierwszy rzut oka gwny plik *.dpr jest bardzo podobny do gwnego pliku zwykej aplikacji VCL. Zwr uwag na trzy procedury eksportowane przez nasz bibliotek (GetExtensionVersion, HttpExtensionProc, TerminateExtension). Pierwsza z nich zwraca serwerowi numer wersji rozszerzenia; kolejne polecenie eksportuje rozszerzenia zwizane z obsug protokou HTTP. Ostatnia procedura jest zwizana z prawidow obsug procesu zakoczenia dziaania programu i zwolnienia biblioteki.

TWebRequest i TWebResponse
Obie klasy organizuj komunikacj pomidzy serwerem WWW a rozszerzeniem, czyli bibliotek DLL. W parametrze Request znajduj si dania klienta, czyli take nagwki HTTP, natomiast Response okrela zwracane przez bibliotek wartoci. Klasa TWebRequest, a konkretnie jej waciwoci, dostarczaj wielu ciekawych informacji na temat naszego rozszerzenia oraz na temat uytkownika ? np. dane o przegldarce (nazwa i wersja), systemie operacyjnym, metodzie wywoania strony, adresie itp. Najciekawsze informacje zgromadziem w tabeli 18.1. Tabela 18.1. Waciwoci klasy TWebRequest

665 | S t r o n a

Nazwa
Method

Opis Metoda wywoania strony (GET, POST)

ProtocolVersion Wersja protokou HTTP UserAgent URL ServerPort ScriptName RemoteAddr RemoteHost Referer

Uywana przegldarka Adres wywoywanego programu w katalogu serwera Numer portu serwera Nazwa wywoywanego skryptu (np. /skrpty/isapi.dll) Adres IP uytkownika korzystajcego z programu Nazwa hosta uytkownika (np. ppp.tpnet.pl). Strona, z ktrej uytkownik trafi na nasz program Dodatkowe parametry przekazane wraz z programem (np. imie=adam&nazwisko=boduch) Rozdzielone ju parametry, doczane do programu (w postaci typu TStringList) Czon okrelajcy akcje do wykonania, np. w przypadku adresu http://127.0.0.1/default.dll/get waciwo zwrci /get Host, czyli adres strony ? np. http://127.0.0.1 Dugo nagwka HTTP

Query

QueryFields

PathInfo

Host ContentLength

ContentEncoding Mechanizm kodowania nagwka ContentFields

Zawiera parametry przekazane metod POST do skryptu

Teraz przykad prezentujcy korzystanie z tej wiedzy w praktyce ? oto, jak moe wyglda zdarzenie OnAction: procedure TWebModule1.WebModule1WebActionItem1Action(Sender: TObject; Request: TWebRequest; Response: TWebResponse; var Handled: Boolean); var HTML : TStringList; 666 | S t r o n a

begin { konstruowanie treci zwracanej przez program } HTML := TStringList.Create; try HTML.Add('<html>'); HTML.Add('<head>'); HTML.Add('<title>Informacje dotyczce HTTP</title>'); HTML.Add('</head>'); HTML.Add('<body>'); HTML.Add('<h1>HTTP</h1>'); HTML.Add('Metoda: <b>' + Request.Method + '</b><br>'); HTML.Add('URL: <b>' + Request.URL + '</b><br>'); HTML.Add('Przegldarka: <b>' + Request.UserAgent + '</b><br>');

HTML.Add('</body>'); HTML.Add('</html>'); Response.Content := HTML.Text; finally HTML.Free; end; Handled := True; end;

Do skonstruowania strony HTML uyem typu TStringList, gdy jest to chyba najprostszy sposb, lepszy ni czenie wszystkiego w jeden dugi acuch String. Ze wzgldu na dugo kodu w przykadzie (rysunek 18.6) zaprezentowaem wykorzystanie jedynie trzech waciwoci, lecz Ty ? jeeli chcesz ? moesz napisa kod wywietlajcy wiksz ilo danych.

667 | S t r o n a

Rysunek 18.6. Przykad wykorzystania danych z nagwka HTTP

Wykorzystanie szablonw
Takie wpisywanie treci strony bezporednio w kodzie rdowym programu moe by troch niepraktyczne. Mona jednak ten problem omin, wykorzystujc element szablonw. Polega to, oglnie mwic, na oddzieleniu rzeczywistego kodu programu od treci HTML, ktra ma by wynikiem dziaania skryptu. Funkcje tak umoliwia komponent TPageProducer, mieszczcy si w zakadce Internet. Aby z niego skorzysta, naley podczas tworzenia akcji rozwin w Inspektorze Obiektw waciwo Producer i wybra komponent typu TPageProducer (rysunek 18.7).

668 | S t r o n a

Rysunek 18.7. Waciwo Producer Od tej pory przy kadorazowym wywoaniu tej akcji wywietlony zostanie kod z waciwoci HTMLDoc komponentu TPageProducer.

Tworzenie nowego szablonu


Aby nasz szablon w ogle zadziaa, konieczne jest wpisanie jakiej treci HTML we waciwoci HTMLDoc ? np. takiej: <html> <head> <title>Strona generowana z uyciem komponentu TPageProducer</title> </head> 669 | S t r o n a

<body> Witam! Oto przykadowa strona generowana z uyciem szablonw! </body> </html>

Waciwo HTMLDoc jest typu TStrings, wic jej edycja moe nastpi zarwno z poziomu programu, jak i podczas jego projektowania (rysunek 18.8).

Rysunek 18.8. Edycja waciwoci HTMLDoc Waciwie nie jest konieczne wpisywanie adnego kodu ? nasz program jest ju gotowy do kompilacji i uruchomienia. Zamiast waciwoci HTMLDoc mona zastosowa take waciwo HTMLFile. Wwczas tre strony HTML bdzie odczytywana z pliku.

Szablony dynamiczne
Rozszerzenia serwerw s w kocu uywane po to, aby umoliwi uytkownikowi generowanie dynamicznych stron w zalenoci od okrelonego zdarzenia. Szablony w pewien sposb uniemoliwiaj zachowanie dynamicznoci, gdy zawarto dokumentu ? odczytywana z waciwoci HTMLDoc ? jest staa. Problem ten mona czciowo rozwiza, stosujc specjalnie znaczniki w treci kodu HTML. Wwczas moemy w trakcie programu zmienia zawarto owych znacznikw wedle wasnego uznania. Przykadowo jeeli w treci strony HTML znajduje si znacznik <#your_name>, to zastpienie tego

670 | S t r o n a

znacznika okrelon treci ogranicza si jedynie do wywoania metody OnHTMLTag komponentu TPageProducer: procedure TWebModule1.HTMLPageHTMLTag(Sender: TObject; Tag: TTag; const TagString: String; TagParams: TStrings; var ReplaceText: String); begin if Tag = tgCustom then begin if TagString = 'your_name' then ReplaceText := strName else if TagString = 'country' then ReplaceText := strCountry; end; end;

Wana jest jednak specjalna konstrukcja znacznikw HTML ? musz by zawarte w nawiasach < i >, a pierwszym znakiem nazwy znacznika musi by #. Wwczas komponent analizuje tre HTML z waciwoci HTMLDoc i przy kadorazowym napotkaniu znacznika wywouje zdarzenie OnHTMLTag.

Zdarzenie OnHTMLTag Znaczenie poszczeglnych parametrw zdarzenia OnHTMLTag jest nastpujce:


Tag ? wskazanie na typ TTag, ktry identyfikuje rodzaj znacznika (patrz tabela 18.2). TagString ? acuch (String) identyfikujcy okrelony znacznik. Nazwa znacznika jest

pozbawiona pocztkowych znakw <# oraz >. TagParams ? dodatkowe parametry znacznika. Do okrelonego znacznika mog by doczone okrelone parametry, ale o tym powiem za chwil. ReplaceText ? tekst, ktry ma zastpi identyfikowany znacznik.

Tabela 18.2. Znaczenie poszczeglnych pozycji typu TTag Parametr


tgLink

Opis Odnonik do innej strony WWW w postaci <#LINK Dest=adres_www>. Inaczej mwic, jest to znacznik <A> zapisany w HTML Znacznik do obrazka w postaci <#IMAGE Ref=adres_obrazka>. Inaczej jest to odnonik <IMG> z HTML Tabela ? inaczej znacznik <TABLE> w HTML. Budowa znacznika: <#TABLE
Param1=waro>

tgImage

tgTable

tgImageMap Mapa obrazka ? inaczej znacznik z HTML. Budowa: <#IMAGEMAP Param1=warto>

671 | S t r o n a

tgObject

Inaczej znacznik <kbd><OBJECT></kbd>, identyfikujcy wstawiany obiekt ? np. kontrolk ActiveX. Budowa: <#OBJECT Control=adres_do_konstrolki> Nieokrelony znacznik

tgCustom

W naszym przykadzie najpierw sprawdzamy, czy znacznik nie jest okrelony (tgCustom), a nastpnie podmieniamy wartoci: ... if TagString = 'your_name' then ReplaceText := strName; ...

strName to w tym wypadku zmienna globalna, ktra zawiera warto przechwycon jako parametr

wywoywanego programu.

Przykadowy program
Pierwszym krokiem jest stworzenie przykadowego dokumentu HTML, ktry bdzie formularzem, w ktrym uytkownik wpisze dane przesyane nastpnie do programu. Kod takiego formularza prezentuje listing 18.2. Listing 18.2. Kod rdowy przykadowej strony WWW <html> <head> <title>Podaj swoje imi</title> </head> <body> <h1>Witaj!</h1> <p>Prosz wpisa w poniszym formularzu swoje imi i miejsce zamieszkania</p> <form action="templates2.dll/get" method="GET"> Imi: <input type="text" name="your_name"><br> Kraj: <input type="text" name="your_country"><br> <input type="Submit" value="Wylij"> </form> </body> </html>

672 | S t r o n a

Tak stworzony dokument naley zapisa np. pod nazw formularz.html i umieci w katalogu serwera ? np. scripts. Tematem tej ksiki nie jest jzyk HTML, wic nie zamierzam tutaj szczegowo objania poszczeglnych znacznikw. Powiem jedynie, e powyszy kod powoduje wywietlenie formularza z dwoma polami, w ktrych uytkownik musi poda swoje imi i kraj, z ktrego pochodzi (rysunek 18.9).

Rysunek 18.9. Formularz strony WWW Gdy uytkownik nacinie przycisk, w przegldarce zostanie zaadowana strona: http://127.0.0.1/scripts/templ[...]e=Adam&your_country=Polska Jeeli przyjrzysz si odnonikowi, to stwierdzisz, e oprcz odwoania do biblioteki ISAPI zawiera on parametry wpisane w formularzu. Oznacza to, e do naszej biblioteki DLL naley odczytanie tych parametrw i przedstawienie ich w odpowiedniej formie (szablonie). Przypominam, e za odczytaniem konkretnego elementu stoi konstrukcja: Request.QueryFields.Values['your_name'];

W powyszej instrukcji nastpuje odczytanie wartoci parametru your_name. Peny kod programu znajduje si w listingu 18.3. Listing 18.3. Kod rdowy programu unit MainFrm; interface uses SysUtils, Classes, HTTPApp, HTTPProd; type 673 | S t r o n a

TWebModule1 = class(TWebModule) HTMLPage: TPageProducer; procedure WebModuleBeforeDispatch(Sender: TObject; Request: TWebRequest; Response: TWebResponse; var Handled: Boolean); procedure HTMLPageHTMLTag(Sender: TObject; Tag: TTag; const TagString: String; TagParams: TStrings; var ReplaceText: String); private { Private declarations } public { Public declarations } end; var WebModule1: TWebModule1; implementation {$R *.dfm} var strName, strCountry : String; procedure TWebModule1.WebModuleBeforeDispatch(Sender: TObject; Request: TWebRequest; Response: TWebResponse; var Handled: Boolean); begin strName := Request.QueryFields.Values['your_name']; strCountry := Request.QueryFields.Values['your_country']; end; procedure TWebModule1.HTMLPageHTMLTag(Sender: TObject; Tag: TTag; const TagString: String; TagParams: TStrings; var ReplaceText: String); begin if Tag = tgCustom then begin if TagString = 'your_name' then ReplaceText := strName else if TagString = 'country' then ReplaceText := strCountry; end; end; end.

Powyszy listing powoduje odczytanie parametrw przekazanych do biblioteki i przedstawienie ich w

674 | S t r o n a

szablonie. Rezultat dziaania programu znajduje si na rysunku 18.10.

Rysunek 18.10. Dziaanie biblioteki

Dodatkowe parametry
Tworzc szablon strony WWW, czyli ? inaczej mwic ? wpisujc zawarto we waciwoci HTMLDoc, mona w trakcie pisania znacznikw stosowa rne parametry, okrelajce dalsze zachowanie programu. Przykadowo niech zawarto szablonu wyglda tak: <html> <head> <title>Strona generowana z uyciem komponentu TPageProducer</title> </head> <body> <#IMAGE ID=helion> </body> </html>

Jak wida, oprcz znacznika <#IMAGE> umieszczony jest w kodzie parametr ID o zawartoci helion. Jako e w kodzie moe wystpowa kilka znacznikw <#IMAGE>, trzeba je jako rozrni ? np. wanie za pomoc parametrw. Oto przykad, jak moe wyglda zdarzenie OnHTMLTag komponentu TPageProducer: procedure TWebModule1.HTMLPageHTMLTag(Sender: TObject; Tag: TTag; const TagString: String; TagParams: TStrings; var ReplaceText: 675 | S t r o n a

String); begin if Tag = tgImage then begin if TagParams.Values['ID'] = 'helion' then ReplaceText := '<img src=http://127.0.0.1/helion.bmp>'; end; end;

Parametr TagParams okrela wanie parametry znacznika. Pierwszy warunek if sprawdza, czy parametr id ma warto helion ? jeeli tak, przydziela do parametru ReplaceText kod HTML majcy wywietli obrazek.

Wysyanie i odbieranie cookies


Cookie w jzyku ang. oznacza ciasteczko. Jest to mechanizm przechowywania pewnych danych na komputerze klienta (odwiedzajcego stron WWW). Moliwe jest wwczas stwierdzenie, czy dany uytkownik by ju na danej stronie WWW, czy jest tam po raz pierwszy. Takie ciasteczka mog zawiera rwnie inne informacje, jak np. dat zaadowania strony itp. Ustawienie oraz odczytanie pliku cookie odbywa si w zdarzeniu OnAction; za odczytywanie odpowiada metoda klasy TWebRequest, a za zapisanie ? TWebResponse.

Ustawianie pliku cookies


Plik cookie ustawiany jest za pomoc metody SetCookieField z klasy TWebResponse: procedure SetCookieField(Values: TStrings; const ADomain, APath: string; AExpires: TDateTime; ASecure: Boolean);

Pierwszym parametrem w formie klasy TStrings musz by dane, ktre maj zosta umieszczone w ciastku. Drugi parametr okrela domen, w ramach ktrej ustawiony zostanie plik (cookies s odczytywane i ?widziane? na podstawie tego wanie parametru); trzeci podobnie okrela ciek (adres), pod ktr widoczne bdzie ciastko. Parametr AExpires okrela dat i czas wyganicia cookie, a ostatni parametr zawiera informacj, czy ciastko korzysta z protokou zabezpiecze (poczenie bezpieczne SSL). Przede wszystkim utwrz dwie akcje ? /set oraz /get. Jak nietrudno si domyle, pierwsza bdzie powodowaa ustawienie ciastka, a druga jego odebranie.

676 | S t r o n a

procedure TWebModule1.WebModule1setAction(Sender: TObject; Request: TWebRequest; Response: TWebResponse; var Handled: Boolean); var S : TStringList; begin S := TStringList.Create; try S.Add('Browser=' + Request.UserAgent); // dodanie informacji o przegldarce S.Add('Visit=' + FormatDateTime('dd:mm:yyyy', Now)); // informacja o dacie wizyty Response.SetCookieField(S, '', '', 17-02-2004, False); Response.Content := 'Ciasteczko zostao ustawione...'; finally S.Free; end; end;

Poszczeglne parametry ciastka znajduj si w kolejnych wierszach zmiennej typu TStringList. Uwaaj! Nazwy ustawianych parametrw musz by oddzielone od wartoci znakiem =. Po ustawieniu parametrw, ktre maj widnie w cookie, naley okreli czas wywoania konkretnej funkcji ? SetCookieField. W naszym wypadku ciasteczko straci warto 17 lutego 2004 roku.

Odczyt cookies
Wraz z pozostaymi danymi HTTP (nagwkiem) do naszego programu dostarczane s informacje na temat cookies. Owe informacje zapisane s w zmiennej typu TStringList ? CookieFields. Oto przykad wykorzystania: procedure TWebModule1.WebModule1getAction(Sender: TObject; Request: TWebRequest; Response: TWebResponse; var Handled: Boolean); begin Response.Content := 'Bye tutaj ' + Request.CookieFields.Values['Visit'] + ' i korzystae z ' + Request.CookieFields.Values['Browser']; end;

Rysunek 18.11. przedstawia program w trakcie dziaania (odczytywanie cookies).

677 | S t r o n a

Rysunek 18.11. Odczytanie pliku cookies Tak naprawd pomimo tego, e cookies mog wydawa si doskona technologi, nie s przeznaczone dla bardziej zaawansowanych rozwiza, np. wymagajcych przechowywania danych binarnych. Rozmiar pojedynczego ciastka wynosi 4 KB, a jedna domena moe ustawi najwyej 20 plikw cookies. W systemach Windows pliki cookies przechowywane s w katalogu C:\Windows\Cookies (Windows 9.x.).

Wysyanie strumieni
Oprcz zwykego tekstu moliwe jest wysyanie do przegldarki strumieni w postaci np. obrazkw. Oczywicie takie strumienie musz by identyfikowane przez przegldark ? nie mog to by dowolne dane binarne. Oglnie polega to na ustawieniu odpowiedniego typu danych wyjciowych ? w tym wypadku: image/pjpeg, identyfikujcego obrazki typu JPEG. Przydzielenie danych wyjciowych odbywa si za pomoc waciwoci ContentType: Response.ContentType := 'image/pjpeg';

Poniszy kod rdowy prezentuje adowanie obrazku z zasobw, a nastpnie wysanie caoci danych do przegldarki:

678 | S t r o n a

procedure TWebModule1.WebModule1sendAction(Sender: TObject; Request: TWebRequest; Response: TWebResponse; var Handled: Boolean); var JPG : TResourceStream; begin { zaaduj obrazek z rejestru } JPG := TResourceStream.Create(hInstance, 'PIC', 'JPEGFILE'); try // typ danych wyjciowych ? obrazek JPEG Response.ContentType := 'image/pjpeg'; Response.ContentStream := JPG; // wysyane dane Response.SendResponse; // wylij Handled := True; finally JPG.Free; end; end;

Ostateczne wysanie obrazka dokonywane jest za pomoc metody SendResponse. Nie zapomnij o doczeniu odpowiedniej dyrektywy, powodujcej doczenie do pliku ISAPI pliku z zasobami: {$R FILES.res}

Korzystanie z baz danych


Modu TWebModule zezwala na umieszczanie na naszym formularzu niewidocznych komponentw sucych do obsugi baz danych. Obsuga takiej bazy danych jest identyczna ze zwyczajnym obsugiwaniem komponentw BDE. Rnica polega na tym, e konieczne jest samodzielne napisanie funkcji odczytujcych rekordy z bazy danych. Nastpnie takie rekordy musz by zapisane w formie tabeli HTML. Na formularzu musisz wic umieci jedynie komponent TTable ? to wystarczy. Przed kompilacj projektu z waciwoci DatabaseName wybierz nasz baz danych ? MojaBaza. Z listy waciwoci TableName wybierz MainTablek. Kod rdowy moduu ISAPI przedstawiony zosta w listingu 18.4. Listing 18.4. Kod rdowy programu korzystajcego z baz danych unit MainFrm; interface

679 | S t r o n a

uses SysUtils, Classes, HTTPApp, HTTPProd, DB, DBTables; type TWebModule1 = class(TWebModule) Table: TTable; procedure WebModule1WebActionItem1Action(Sender: TObject; Request: TWebRequest; Response: TWebResponse; var Handled: Boolean); private { Private declarations } public { Public declarations } end; var WebModule1: TWebModule1; implementation {$R *.dfm} procedure TWebModule1.WebModule1WebActionItem1Action(Sender: TObject; Request: TWebRequest; Response: TWebResponse; var Handled: Boolean); var HTML : TStringList; begin HTML := TStringList.Create; try { tworzenie pocztku szablonu } HTML.Add('<html>'); HTML.Add('<head>'); HTML.Add('<title>Odczyt z baz danych</title>'); HTML.Add('</head>'); HTML.Add('<body>'); HTML.Add('<table width="60%" size="1" border="1">'); // tworzenie tabeli HTML.Add('<tr><td width="5%">ID</td><td width="60%">Towar</td><td width="15%">Cena</td><td width="20%">Data</td></tr>'); Table.Active := True; { kolejne odczytywanie nastpnych rekordw i dodawanie ich do rezultatu HTML } while not Table.Eof do 680 | S t r o n a

begin HTML.Add('<tr>'); HTML.Add('<td width="5%">' + IntToStr(Table.FieldValues['id']) + '</td>'); HTML.Add('<td widrh="60%">' + Table.FieldValues['Towar'] + '</td>'); HTML.Add('<td width="15%">' + FloatToStr(Table.FieldValues['Cena']) + '</td>'); HTML.Add('<td width="20%">' + DateTimeToStr(Table.FieldValues['Data']) + '</td>'); HTML.Add('</tr>'); Table.Next; end; HTML.Add('</table>'); Table.Active := False; HTML.Add('</body>'); HTML.Add('</html>'); Response.Content := HTML.Text; Handled := True; finally HTML.Free; end; end; end.

Po kompilacji i umieszczeniu pliku DLL na serwerze oraz wpisaniu odpowiedniego adresu przegldarka wywietli rezultat widoczny na rysunku 18.12. Przed odczytaniem rekordw z bazy danych musisz zmieni warto waciwoci Active komponentu TTable na True. Rwnowane z t instrukcj jest wywoanie metody Open i Close komponentu.

681 | S t r o n a

Rysunek 18.12. Odczyt rekordw z bazy danych Nie zapomnij o konwersji! Odczytujc wartoci z poszczeglnych kolumn za pomoc FieldValues, otrzymujemy rezultat w postaci zmiennej typu Variant. Jeeli jednak nie zastosujemy w tym wypadku konwersji, a warto w kolumnie bdzie typu Integer ? program (biblioteka ISAPI) nie zostanie wykonany, a serwer zwrci stron z komunikatem o bdzie numer 500 (oznaczajcym bd serwera).

WebSnap
Dotd mwilimy o tworzeniu programw (bibliotek) ISAPI. W Delphi 6 wprowadzono now technologi, zwan WebSnap. Pozwala ona na tworzenie wydajnych aplikacji internetowych z wykorzystaniem najnowszych technologii (projektowanie odbywa si w rodowisku RAD). Postanowiem jednak opuci ten temat ze wzgldu na obecno rozdziau 19., w ktrym opisuje nowo, jaka pojawia si w Delphi 7 ? IntraWeb, ktra moim zdaniem ma ogromn przewag nad WebSnap nie tylko pod wzgldem prostoty tworzenia aplikacji. Pozwalam sobie nawet stwierdzi, e IntraWeb po prostu w przyszoci wyprze WebSnap, gdy zapewnia naprawd wielk prostot tworzenia aplikacji ? ale o tym w kolejnym rozdziale?

Podsumowanie
682 | S t r o n a

W tym rozdziale miae okazj zapozna si z procesem tworzenia bibliotek ISAPI i ? oglnie ? tworzeniem dynamicznych stron WWW. Kto wie, by moe technologia przyda Ci si w czasie projektowania wasnych stron internetowych? Tak czy inaczej w kolejnym rozdziale zaprezentuj lepszy (nowszy) sposb na tworzenie dynamicznych witryn WWW. Zaczniki:

Listingi_18.zip (406.59 kB)

Rozdzia 19

Edytuj Historia Przenie Obserwuj

IntraWeb
Jedn z nowoci w Delphi 7 jest pakiet IntraWeb, dostarczony przez firm AtoZed Software. Rezultatem doczenia tego pakietu jest pojawienie si na palecie komponentw czterech nowych zakadek ? IWStandard, IWData, IW Client Side i IW Control. Komponenty te su do tworzenia aplikacji ? serwerw i interaktywnych stron WWW, zawierajcych zaawansowane kontrolki. Poniewa najlepiej wytumaczy to na przykadach, najbliszych par stron powiconych bdzie wanie technologii IntraWeb.

Spis treci 1 Czym waciwie jest IntraWeb? 2 Tworzenie projektu IntraWeb 2.1 Umieszczanie komponentw 2.2 Podgld wygldu formularza 3 Uruchamianie projektu 3.1 Obsuga aplikacji serwera 3.1.1 Menu File 3.1.2 Menu Run 4 Generowanie zdarze 683 | S t r o n a

4.1 Zdarzenia zastpcze 5 Kilka formularzy w jednym projekcie 5.1 Funkcja ShowMessage w IntraWeb 6 Elementy HTML i JavaScript 7 Wysyanie plikw 8 IntraWeb jako DLL 8.1 Konwertowanie aplikacji do ISAPI 9 IntraWeb kontra tradycyjne metody 10 Podsumowanie

Niestety Delphi 7 Professional Edition zawiera tylko cz pakietu IntraWeb ? peny pakiet dostpny jest w wersji Enterprise. Delphi 7 Personal jest w ogle pozbawiony tego narzdzia. W tym rozdziale:

dowiesz si, czym waciwie jest ten IntraWeb; nauczysz si wykorzystywa IntraWeb; zaprojektujesz wasne aplikacje z wykorzystaniem tego narzdzia.

Czym waciwie jest IntraWeb?


Wedug wielu IntraWeb jest rewolucyjn technologi, umoliwiajc tworzenie interaktywnych stron WWW bez korzystania z jzyka HTML i JavaScript. Cao tworzona jest identycznie jak zwyke aplikacje Delphi ? z wykorzystaniem komponentw, zdarze i formularzy. Wszystko to jest wywietlane w przegldarce w postaci strony WWW. Do napisania takiego programu wystarczy znajomo Object Pascal ? w adnym stopniu nie jest konieczna umiejtno projektowania stron WWW, ani tym bardziej znajomo jzykw DHTML czy JavaScript.

Tworzenie projektu IntraWeb


Tworzenie nowego projektu aplikacji wykorzystujcej IntraWeb wymaga otwarcia Repozytorium (rysunek 19.1) i wybrania zakadki IntraWeb.

684 | S t r o n a

Rysunek 19.1. Repozytorium z zaznaczon zakadk IntraWeb Aby utworzy nowy projekt, zaznacz ikon Stand Alone Application Application. Po naciniciu przycisku OK zostaniesz poproszony o wskazanie katalogu, w ktrym zostan zapisane pliki projektu (rysunek 19.2).

Rysunek 19.2. Wskazanie pliku docelowego Na samym pocztku w katalogu, ktry wskazae, utworzone zostanie sze plikw; w edytorze kodu zostanie otwarty ten podstawowy ? IWProject.dpr. Podsumowujc, w folderze powinny znajdowa si nastpujce pliki:

685 | S t r o n a

IWProject1.dpr ? gwny plik projektu; IWUnit1.pas, IWUni1.dfm ? gwne pliki formularza gwnego; ServerController.pas, ServerController.dfm ? pliki kontrolujce zachowanie serwera wbudowanego w nasz projekt.

W tym samym katalogu powiniene znale take plik IWUnit1.pas ? otwrz go, uywajc polecenia Open z menu File. Twoim oczom ukae si ?czysty?, pusty formularz. Na nim bdziemy mogli umieszcza komponenty, ktre pniej bd widoczne w formie strony WWW.

Umieszczanie komponentw
Na formularzu umieszczaj tylko komponenty z zakadek rozpoczynajcych si literami IW. Ikony tych komponentw s zaznaczone charakterystyczn bkitn obwdk z napisem IW. W przypadku prby umieszczenia komponentw nie nalecych do IntraWeb zostanie wywietlone ostrzeenie (rysunek 19.3).

Rysunek 19.3. Komunikat ostrzegawczy

Podgld wygldu formularza


Jeszcze przed uruchomieniem projektu istnieje moliwo sprawdzenia, jak bdzie wyglda nasz formularz. Wystarczy klikn formularz prawym przyciskiem myszy i wybra z menu podrcznego pozycj Preview. Postpuj w ten sposb: na formularzu umie komponenty: IWButton i IWEdit z zakadki IW Standard. Teraz kliknij formularz prawym przyciskiem myszy i z menu podrcznego wybierz pozycj Preview. W tym momencie zostanie wywietlone okno podgldu (rysunek 19.4).

686 | S t r o n a

Rysunek 19.4. Widok okna podgldu Okno podzielone jest na dwie zakadki ? WYSIWYG oraz Source. Na pierwszej z nich widzimy, jak formularz bdzie wyglda po uruchomieniu; zakadka Source daje nam wgld w rdo strony HTML. WYSIWYG to skrt od sw What You See Is What You Get, czyli To, co widzisz, jest tym, co masz. Okrelenie to jest stosowane najczciej w stosunku do edytorw HTML typu Microsoft FrontPage, w ktorych projektowanie stron WWW moe odbywa si nawet bez znajomoci jzyka HTML ? poprzez projektowanie stron i tabel tak, jak w Delphi (metod przecigania komponentw).

Uruchamianie projektu
Do prawidowego dziaania nasz program IntraWeb potrzebuje serwera. Nie ma si czym martwi ? nasz skompilowany program jednoczenie bdzie serwerem. Uruchamianie projektu wyglda tak, jak w zwykym projekcie Delphi ? wystarczy nacinicie klawisza F9. W tym momencie Delphi uruchomi serwer (rysunek 19.5).

687 | S t r o n a

Rysunek 19.5. Aplikacja-serwer Jest to jedynie serwer ? ponowne nacinicie klawisza F9 spowoduje otwarcie przegldarki internetowej z adresem (w moim przypadku): http://127.0.0.1:1979/EXEC/0/B445C800AE8348EC0F64E240 i wywietlenie zawartoci formularza w formie strony WWW.

Obsuga aplikacji serwera


Program My IntraWeb Application Server posiada kilka opcji menu, o ktrych warto wspomnie.

Menu File Ciekaw opcj jest pozycja Show Debug Information, ktra umoliwia wywietlenie w polu tekstowym informacji na temat aktualnie uruchomionej sesji ? mog one wyglda tak: New session: 9C13C800386744191064E240 Execute: 9C13C800386744191064E240 Javascript: IWCommon.js_5.0.43 Javascript: IWCL.js_5.0.43 Javascript: IWCSData.js_5.0.43 Javascript: IWExplorer.js_5.0.43

Wyczyszczenie wszystkich sesji umoliwia pozycja Clear Sessions, natomiast skopiowanie adresu strony (http://127.0.0.1:2029/EXEC/0/C40DC800405CFD291064E240) do schowka umoliwia polecenie Copy URL to Clipboard.

688 | S t r o n a

Menu Run Pozycja Run w menu gwnym serwera suy do uruchamiania okna przegldarki, w ktrym znajdzie si pakiet IntraWeb. Konkretnie czyni to pozycja Execute, lecz serwer daje nam take moliwo wyboru, w jakiej przegldarce chcemy otworzy projekt (menu Select browser). Do uruchamiania serwera w trybie bezpiecznym (SSL) suy pozycja Use SSL. Istnieje take moliwo wpisania dodatkowych parametrw, ktre maj zosta przekazane do strony WWW (opcja Parameters).

Generowanie zdarze
W odrnieniu od zwykej biblioteki VCL komponenty IntraWeb nie posiadaj wszystkich zdarze typu OnMouseMove czy OnKeyDown. Posiadaj chyba jednak te najwaniejsze ? m.in. OnClick, za pomoc ktrego jestemy w stanie kontrolowa nacinicie przycisku. 1. Umie na formularzu komponent IWButton. 2. Zmie jego nazw na IWSetBgColor, a warto waciwoci Caption na Zmie to. 3. Kliknij dwukrotnie przycisk, co spowoduje utworzenie zdarzenia OnClick:

procedure TformMain.IWSetBgColorClick(Sender: TObject); begin Randomize; SetBackgroundColor(Random(255)); end;

Moesz teraz uruchomi projekt. Kadorazowe nacinicie przycisku bdzie powodowao ustawienie losowego koloru ta o odcieniu czerwonym.

Zdarzenia zastpcze
Na samym pocztku rozdziau napisaem, e do tworzenia aplikacji typu IntraWeb nie jest potrzebna znajomo JavaScript. Istotnie mona si bez niej obej, lecz moe okaza si przydatna, o czym za chwil si przekonasz. Umie na formularzu komponent TIWLink i zmie warto jego waciwoci 689 | S t r o n a

Caption na http://4programmers.net. Komponent w tworzy w rzeczywistoci cze do jakie strony

WWW. Na zakadce Events z Inspektora Obiektw dostpne jest jedynie zdarzenie OnClick. JavaScript jest jzykiem umoliwiajcym tworzenie tzw. skryptw, pozwalajcych na tworzenie dynamicznych stron WWW, reagujcych na zdarzenia wywoywane przez uytkownika. Do skorzystania ze zdarze JavaScript mona uy waciwoci ScriptEvents (rysunek 19.6). Wybranie w Inspektorze Obiektw tej waciwoci spowoduje otwarcie okna IntraWeb Event Script Editor, ktre widoczne jest na rysunku 19.6.

Rysunek 19.6. Edytor skryptw Z rozwijalnego menu mona wybra zdarzenie, ktre chcemy obsuy ? kod tego zdarzenia naley wpisa w polu Memo, znajdujcym si poniej tej listy. Wybierz zdarzenie onMouseOver i wpisz jego kod obsugi: document.write("Haha! To by art! ");

Taki kod JavaScript spowoduje wpisanie w dokumencie HTML tekstu Haha! To by art! Zdarzenie onMouseOver wystpuje w momencie, gdy uytkownik umieci kursor myszy nad komponentem. Uruchom program i sprawd jego dziaanie w praktyce. W tabeli 19.1 znajduje si lista zdarze wraz z objanieniami. Tabela 19.1. Zdarzenia JavaScript Zdarzenie onAbort Przerwanie Objanienie

690 | S t r o n a

onChange onClick onDblClick onDragDrop onError onFucus onKeyDown onKeyPress onKeyUp onLoad

Wystpuje w momencie zmiany zawartoci obiektu Kliknicie obiektu Podwjne kliknicie Wystpuje w momencie prby przesunicia obiektu Wystpienie bdu Obiekt staje si aktywny Nastpio wcinicie klawisza w obrbie obiektu Wcinicie klawisza Klawisz zosta puszczony adowanie strony

onMouseDown Nacinicie przycisku myszy w obrbie obiektu onMouseMove Przesunicie kursora nad obiektem onMouseUp Puszczenie przycisku myszy

onMouseOver Kursor myszy jest przesuwany nad obiektem onMouseOut onMove onResize onUnload onSubmit Kursor myszy znalaz si poza polem obiektu Obiekt jest przesuwany Rozmiar obiektu jest zmieniany Uytkownik opuszcza stron Formularz, na ktrym znajduj si obiekty, jest przesyany do skryptu

Kilka formularzy w jednym projekcie


Tworzenie nowego formularza w przypadku aplikacji IntraWeb nie odbywa si tak samo, jak w VCL. Tutaj konieczne jest ponowne zajrzenie do Repozytorium. Tam z zakadki IntraWeb naley wybra 691 | S t r o n a

pozycj Application Form ? w edytorze kodu pojawi si kolejna zakadka, a wraz z ni nastpny formularz. Na drugim formularzu moesz umieci jedynie zwyk etykiet ? komponent TIWLabel. Na formularzu gwnym umie dwie etykiety TIWEdit oraz przycisk TIWButton. W naszym przykadowym programie dostp do drugiego formularza bdzie moliwy po wpisaniu odpowiedniej nazwy uytkownika i hasa. Bezporedni dostp do kontrolek jest identyczny, jak w przypadku zwykego VCL ? np. kontrolka TIWEdit posiada (podobnie jak w VCL) waciwo Text. procedure TformMain.IWBtnLoginClick(Sender: TObject); begin if (IWEdtLogin.Text = 'test') and (IWEdtPassword.Text = 'test') then LoginForm.Show else WebApplication.ShowMessage('Nieprawidowy login lub haso!'); end;

W przypadku, gdy nazwa uytkownika (login) i haso zgadzaj si, zostanie wywietlony drugi formularz (LoginForm.Show). Gdy haso lub login jest niepoprawny ? program informuje o tym, wywietlajc komunikat (ShowMessage). Listing 19.1 przedstawia peny kod moduu gwnego. Listing 19.1. Formularz gwny unit IWUnit1; {PUBDIST} interface uses IWAppForm, IWApplication, IWTypes, IWCompEdit, Classes, Controls, IWControl, IWCompLabel, IWCompButton, IWLogin; type TformMain = class(TIWAppForm) IWLogin: TIWLabel; IWPassword: TIWLabel; IWEdtLogin: TIWEdit; IWEdtPassword: TIWEdit; IWBtnLogin: TIWButton; procedure IWBtnLoginClick(Sender: TObject); procedure IWAppFormCreate(Sender: TObject); public end; implementation 692 | S t r o n a

{$R *.dfm} uses ServerController, IWForm; procedure TformMain.IWBtnLoginClick(Sender: TObject); begin if (IWEdtLogin.Text = 'test') and (IWEdtPassword.Text = 'test') then LoginForm.Show else WebApplication.ShowMessage('Nieprawidowy login lub haso!'); end; procedure TformMain.IWAppFormCreate(Sender: TObject); begin LoginForm := TLoginForm.Create(WebApplication); end; end.

Oprcz zdarzenia OnClick komponentu IWBtnLogin w formularzu zostao wygenerowane rwnie zdarzenie OnCreate. W owym zdarzeniu nastpuje wywoanie konstruktora formularza LoginForm. Destruktor tutaj nie jest konieczny, gdy zwolnienie pamici zapewni sama aplikacja. W module formularza LoginForm powiniene take utworzy zmienn wskazujc klas TLoginForm: var LoginForm : TLoginForm;

Pamitaj jednak, aby powysza zmienna bya zadeklarowana w sekcji Interface!

Funkcja ShowMessage w IntraWeb


W poprzednim przykadzie zaprezentowaem uycie funkcji ShowMessage. W IntraWeb ma ona inn budow ni w VCL. Ot w poleceniu ShowMessage IntraWeb mona okreli styl wywietlenia okienka informacyjnego ? drugi parametr moe bowiem zawiera jedn z podanych w tabeli 19.2 wartoci. Tabela 19.2. Parametr AType polecenia ShowMessage Warto Opis

693 | S t r o n a

smAlert smNewWindow smSameWindow

Zwyke okienko informacyjne Komunikat wywietlany jest w nowym oknie Komunikat wywietlany jest w tym samym oknie; tak, jak zwykle zaadowana zostaje nowa tre w oknie przegldarki Parametr dziaa tak jak w przypadku powyszej flagi ? z t rnic, e komunikat umieszczony jest w ramce

smSameWindowFrame

Jeeli nie wpiszesz adnego z podanych w tabeli 19.2 parametrw jako domylnego, zostanie zastosowany pierwszy ? smAlert. Na doczonej do ksiki pycie CD-ROM (w katalogu ../listingi/19/ShowMessage Demo/) znajduje si projekt prezentujcy dziaanie rnych parametrw funkcji ShowMessage (rysunek 19.7).

Rysunek 19.7. Formularz gwny programu

Elementy HTML i JavaScript


Formularze IntraWeb posiadaj waciwoci, ktrych zmiana powoduje ustawienie odpowiedniego znacznika HTML. Przykadem moe tu by waciwo BackgroundColor, ktra powoduje zmian koloru ta formularza. W rzeczywistoci jednak modyfikowany jest znacznik <body> jzyka HTML, ktrego zmiana daje efekt w postaci zmiany ta. W tabeli 19.3 umieciem te elementy, ktre mog by dla nas interesujce z punktu widzenia wygldu aplikacji. Tabela 19.3. Waciwoci formularzy IntraWeb

694 | S t r o n a

Waciwo Background

Opis Opcje ustawiania ta dla strony (URL, wskazanie konkretnego pliku)

BackgroundColor Waciwo daje Ci moliwo wyboru ta strony JavaScript LinkColor StyleSheet Kod JavaScript, ktry ma zosta umieszczony wraz z formularzem Kolor cza Waciwo reprezentuje tzw. arkusze stylw, czyli pliki definiujce czcionki oraz style wywietlanego tekstu

SupportedBrowser Zmienna okrela, jakie przegldarki maj obsugiwa nasz program TextColor Title VLinkColor Kolor tekstu wywietlanego na formularzu Tytu strony WWW (znacznik <title> w HTML) Kolor cza do odwiedzonej strony

Wysyanie plikw
Komponent TIWFile umoliwia przesanie pliku na serwer, ktry tworzy nasza aplikacja IntraWeb. Na formularzu umie komponent TIWFile oraz TIWButton ? tylko one dwa wystarcz, aby zaadowa plik. Dziki obiektowi TIWFile po uruchomieniu programu oprcz etykiety tekstowej pojawi si rwnie przycisk (rysunek 19.8).

Rysunek 19.8. Formularz do adowania plikw Przycisk Przegldaj jest w rzeczywistoci kodem HTML:

695 | S t r o n a

<input type="FILE" name="EDTFILE" size="34">

Przegldarka interpretuje go, wywietlajc etykiet tekstow oraz przycisk ? po jego naciniciu mona wybra plik do zaadowania. Kod przesyania pliku na serwer jest prosty. Proces ten realizuje metoda SaveToFile komponentu TIWFile. uses ServerController, SysUtils, Forms;

procedure TformMain.btnUploadClick(Sender: TObject); begin edtFile.SaveToFile(ExtractFilePath(Application.ExeName) + edtFile.FileName); WebApplication.ShowMessage('Plik zosta zaadowany na serwer!', smNewWindow); end;

Nazw pliku, ktry ma zosta przesany na serwer, okrela waciwo FileName.

IntraWeb jako DLL


Do tej pory nasze przykadowe aplikacje posiaday wasny serwer, ale problem pojawia si w momencie, gdy chcemy nasz program umieci na rzeczywistym serwerze, tak aby dziaa jak zwyka strona WWW. Problem polega na innym sposobie utworzenia aplikacji. W Repozytorium zamiast ikony Stand Alone Application naley wybra ISAPI Application. Wwczas Delphi utworzy nowy projekt biblioteki ISAPI; korzystanie z takiego projektu odbywa si w identyczny sposb, jak w przypadku zwykej aplikacji. Rnic jest plik wynikowy ? w tym wypadku bdzie to biblioteka DLL. Po skompilowaniu programu plik DLL naley umieci w odpowiednim katalogu serwera, tak jak to robilimy w poprzednim rozdziale. Rysunek 19.9 prezentuje dziaanie aplikacji IntraWeb na serwerze opartym o program PWS.

696 | S t r o n a

Rysunek 19.9. Program dziaajcy w oparciu o IntraWeb Dziaanie programu jest raczej symboliczne ? po naciniciu przycisku w etykiecie wywietlany jest tekst. Odpowiednia procedura wyglda tak: procedure TformMain.IWButtonClick(Sender: TObject); begin IWLabel.Caption := 'Cze! To jest biblioteka z aplikacj IntraWeb!'; end;

Konwertowanie aplikacji do ISAPI


Poprzednie nasze aplikacje wykorzystyway wbudowany serwer. Teraz chcc program wywietli w postaci ISAPI, naley troszk ?pokombinowa? ? na szczcie niezbyt duo. W nowym projekcie wystarczy usun plik *.dpr i utworzy w jego miejsce nowy plik *.dpr, generowany po utworzeniu nowego projektu ISAPI Application. Spjrz na listingi 19.2 i 19.3, zawierajce odpowiednio plik *.dpr z projektu gwnego oraz z projektu aplikacji ISAPI. Listing 19.2. Plik *.dpr z projektu IntraWeb program p45; {PUBDIST} uses IWInitStandAlone, 697 | S t r o n a

ServerController in 'ServerController.pas' {IWServerController: TDataModule}, IWUnit1 in 'IWUnit1.pas' {formMain: TIWForm1}; {$R *.res} begin IWRun(TFormMain, TIWServerController); end. Listing 19.3. Plik *.dpr z projektu ISAPI IntraWeb library IWISAPIProject; uses IWInitISAPI, ServerController in 'ServerController.pas' {IWServerController: TIWServerControllerBase}, IWUnit1 in 'IWUnit1.pas' {formMain: TIWForm1}; {$R *.RES} begin IWRun(TFormMain, TIWServerController); end.

Sam moesz zaobserwowa, e rnica polega przede wszystkim na uyciu sowa kluczowego library w miejsce sowa kluczowego program.

IntraWeb kontra tradycyjne metody


Wielu ludzi jest zachwyconych dziaaniem technologii IntraWeb, jej prostoty, braku koniecznoci poznawania HTML ? wszystko odbywa si przecie na poziomie IDE Delphi. Wiele osb jest take przekonanych, e technologia nowej generacji IntraWeb wyprze jzyki skryptowe (takie jak CGI) albo nawet w ogle zlikwiduje konieczno znajomoci HTML przy projektowaniu wasnych witryn. Ja nie bybym o tym tak silnie przekonany ? moim zdaniem HTML sam w sobie nie zniknie. Nie znikn przecie rzesze ludzi, ktrzy pisz strony HTML, nie korzystajc z adnych wyspecjalizowanych edytorw, gdy wwczas maj pen kontrol nad kodem i mog go maksymalnie zoptymalizowa, czego nie mona powiedzie o IntraWeb, bdcym jedynie programem, ktry nigdy nie zastpi czowieka. Nie wiem, jak inni, ale ja raczej nie bd a takim zwolennikiem IntraWeb ? wol stary, dobry HTML oraz PHP z wykorzystaniem MySQL. Znam zarwno PHP, jak i Delphi, wic mog z czystym sumieniem 698 | S t r o n a

powiedzie, e w ogle nie chciabym tworzy stron dynamicznych z wykorzystaniem IntraWeb ? po prostu wol PHP, ktre daje mi wiksze moliwoci. Lecz jest to jedynie moje zdanie?

Podsumowanie
Wanie miae okazj przekona si, e tworzenie aplikacji IntraWeb nie odbiega zbytnio od tworzenia zwykych programw w Delphi. Tutaj take mamy do czynienia z bibliotek wizualn, ze zdarzeniami i waciwociami. Dziki IntraWeb moemy po prostu tworzy aplikacje Delphi dziaajce na serwerach. Mam nadzieje, e owa technologia z czasem zyska popularno, gdy jest tego warta. Zaczniki:

Listingi_19.zip (379.18 kB)

Podsumowanie czci V

Edytuj Historia Przenie Obserwuj

Podsumowanie czci V
W ostatniej czci tej ksiki miae okazj zapozna si z procesem tworzenia aplikacji internetowych za pomoc Delphi. Sam musisz oceni, czy jest to dobre rozwizanie problemu projektowania caych witryn internetowych i czy jest w stanie zastpi wspczesne rozwizania, takie jak PHP lub ASP. Rozdzia 18. by powicony przede wszystkim tworzeniu bibliotek ISAPI oraz krtkim omwieniu pozostaych rozszerze serwera (NSAPI i CGI). Pokazaem, jak mona tworzy dynamiczne strony WWW, wykorzystujc do tego komponenty Delphi. Peni moliwoci tworzenia aplikacji internetowych zaprezentowaem w rozdziale 19., gdzie moge zapozna si z now w Delphi 7 technologi ? IntraWeb. Umoliwia ona wykorzystanie standardowych komponentw Delphi, co czyni j zapewne bardziej przyjazn i atrakcyjn dla projektanta.

699 | S t r o n a

Zakoczenie

Edytuj Historia Przenie Obserwuj

Zakoczenie
To ju koniec ksiki tu koczy si take moja rola "przewodnika". Zakoczenie ksiki nie oznacza zakoczenia Twojej nauki Delphi. Moe to zabrzmie dziwnie, lecz to by jedynie wstp do prawdziwego programowania. Zaprezentowaem tylko podstawy operowania narzdziem, jakim jest Delphi pokazaem, w jaki sposb podj rne tematy, takie jak wykorzystywanie grafiki, plikw INI, rejestru i baz danych. Reszta zaley ju tylko od Ciebie. Mam nadziej, e choby w maym stopniu moja praca nie posza na marne i jeste z tej ksiki zadowolony. Prosz o przesyanie konstruktywnej krytyki na mj adres e-mail: adam@boduch.net. Nie moesz spocz na laurach powiniene dalej si rozwija, poznawa nowe tematy, uczestniczy w dyskusjach programistycznych (np. na forum http://forum.4programmers.net), pisa programy, analizowa kody rdowe i pomoc Delphi. Wraz z innymi zapalecami tworzymy polsk encyklopedi Delphi, ktra moe Ci pomc w lepszym poznaniu i zrozumieniu tego rodowiska; jej sieciowy adres to http://4programmers.net/Delphi. Zapraszamy do przyczenia si i wsplnego tworzenia polskiego systemu pomocy Delphi!

Dodatek A. Zasady pisania kodu


Edytuj Historia Przenie Obserwuj

Zasady pisania kodu


Istnieje jeszcze jedna kwestia, z pozoru niezbyt istotna zasady pisania kodu rdowego. Dla kompilatora nie jest istotne, jak wyglda kod czy jest zapisany duymi, czy maymi
700 | S t r o n a

literami. Jednak dla innych programistw, ktrzy czytaj Twj kod, jego wygld moe by bogosawiestwem lub przeklestwem. Czsto mona si natkn na zapisywanie kodu rdowego tak, jakby stanowi zwyky zbir polece. Taki zapis jest nieczytelny dla innych programistw (w przypadku, gdy udostpniasz go innym osobom) i wrcz niedopuszczalny, jeeli pracujesz w zespole. Dlatego te powsta standard kodowania Object Pascala, ktry wyznaczyli programici firmy Borland, piszc kod VCL. Ja sam staram si stosowa te reguy i pisa jak najczytelniejszy kod to samo zalecam Tobie, drogi Czytelniku. Spjrz na poniszy kod: procedure costam; var i:integer; begin for i:=0 to 100 do begin if i=50 then memo1.lines.add('jest ju polowa...'); end; end;

Uwierz mi, e taki kod jest mao czytelny, niepraktyczny i niezbyt przejrzysty. Teraz porwnaj to z kodem zapisanym poniej: procedure CosTam; var I : Integer; begin for I := 0 to 100 do begin if i=50 then Memo1.Lines.Add('jest ju polowa...'); end; end;

Teraz odpowiedz sobie sam na pytanie: jaki zapis wolisz?

Stosowanie wci
Przyjo si stosowanie wci wielkoci dwch spacji. Naturalnie nie stosuje si wci w kadym wersie mona powiedzie, e wcicia na pewno powinny si znale w bloku begin..end: begin ShowMessage('Dwie spacje'); end;
701 | S t r o n a

Jak widzisz, polecenie ShowMessage zostao zapisane z uyciem dwch spacji. Kolejny przykad: begin if X < 10 then begin if Y > 100 then begin end; end; end;

Zwr uwag: kady blok begin to kolejne wcicia, natomiast sowo end jest na tej samej wysokoci co odpowiadajce mu sowo begin.

Instrukcje begin i end


Nie pisz w ten sposb: if X = 10 then begin

Zasad jest, aby sowo begin znajdowao si pod spodem, a nie w tym samym wierszu, razem ze sowem then. Sowo end natomiast powinno by w tej samej kolumnie co sowo begin (tak, jak wspominaem w powyszym punkcie): if X = 10 then begin { jakie instrukcje } if Zamien = TRUE then begin { jakie instrukcje } end; end;

Styl wielbdzi w nazwach procedur


Jeeli nazwa procedury lub funkcji jest dusza, bo zawiera np. kilka wyrazw ze sob
702 | S t r o n a

poczonych, to warto j pisa tzw. stylem wielbdzim (kady wyraz skadowy zaczynajc wielk liter) np: procedure MojaPierwszaProceduraNapisanaStylemWielbladzim;

Wyglda to lepiej ni: procedure mojapierwszaproceduranapisanastylemwielbladzim;

To samo tyczy si nazw funkcji oraz zmiennych.

Stosuj wielkie litery


Tak, jak w jzyku polskim, rwnie w Pascalu wyraz nastpujcy po kropce pisze si wielk liter spjrz na ponisz instrukcj: Memo1.Lines.Add('to jest tekst?');

Nie uwaasz, e tak zapisane instrukcje wygldaj lepiej ni ponisze: memo1.lines.add('to jest tekst? ');

O wiele lepiej wyglda, prawda? Bardziej przejrzycie. To samo tyczy si nazw zmiennych: var Imie Nazwisko Kod : : : String; String; Integer;

Prawda, e wyglda lepiej? Szczeglnie z dwukropkiem umieszczonym w tej samej kolumnie. Wyjtkiem przy stosowaniu wielkich liter s sowa kluczowe, ktre Delphi pogrubia ? powinny by one zapisywane maymi literami.

Parametry procedur
Parametry o identycznym typie powinny by zgrupowane w pojedynczej deklaracji zamiast wic pisa tak:

703 | S t r o n a

procedure Moja(Param1: String; Param2: String; Pararm3: String);

moesz napisa tak: procedure Moja(Param1, Param2, Param3 : String);

Instrukcja if
Tak, jak ju wczeniej napisaem, sowo begin naley umieszcza zaraz poniej if. Ale co wtedy, gdy trzeba doda jeszcze sowo else? Moe to wyglda tak: if X = 10 then begin { co tam } end else { jeszcze co }

Jeeli zechcesz po sowie else doda jeszcze begin, moe to wyglda tak: if X = 10 then begin { co tam } end else begin { jeszcze co } end; lub: if X = 10 then begin { co tam } end else begin { jeszcze co } end;

Ja wol stosowa pierwszy wariant ze sowem begin umieszczonym niej.

Instrukcja case
704 | S t r o n a

Oto propozycja pisania instrukcji case: case i of 1: begin end; 2: begin end; end;

Obsuga wyjtkw
Zaleca si podczas tworzenia jakiego obiektu uwzgldni wystpienie wyjtku i odpowiednio na zareagowa, np.: Reg := TRegistry.Create; try { niezbdne operacje } finally Reg.Free; // zwolnienie obiektu, nawet gdy wystapi wyjtek end;

Zwr rwnie uwag na styl zapisywania i uycie wci.

Klasy
Kad klas (nazw) naley poprzedzi liter T. Jest to bardzo wana regua. Np. deklaracja nazwy powinna wyglda tak: type TKlasa = class(TObject);

A zmienna wskazujca t klas powinna wyglda tak: var Klasa : TKlasa;

705 | S t r o n a

Zawsze zapisuje si to, pomijajc pierwsz liter T.

Komentarze
Wierz mi, e komentarze s bardzo wanym elementem jzyka Object Pascal. By moe po napisaniu jakiego programu i powrceniu do niego po np. dwch miesicach nie bdziesz pamita, jak doszede do tej, czy innej funkcji, jak jest wykonywana jaka procedura itp. Warto wic komentowa kod. Na samym pocztku kadego pliku rdowego warto te umieci informacje o jego nazwie oraz prawach autorskich np.: (********************************************************) (* Moja aplikacja v. 1.0 *) (* Copyright 2003 by Adam Boduch *) (* http://boduch.net *) (********************************************************)

Pliki i nazwy formularzy


Tak, jak wikszo programistw, do nazwy formularza dodaj kocwk Form. Np. gwny formularz w programie powinien mie nazw MainForm, a zapisany plik formularza ? nazw MainFrm.pas (z kocwk Frm). Jeeli wic stworzysz formularz O programie, jego nazw moe by AboutForm, a zapisany plik moe by nazwany AboutFrm.pas. Unikaj nazewnictwa Unit1.pas, Unit2.pas jest to przejaw amatorszczyzny Rwnie gwny plik projektu (z rozszerzeniem .DPR ) powinien mie jak umown nazw, np. skrt od nazwy programu lub sam nazw programu, np. PerlEditor.

Notacja wgierska
Notacja wgierska jest technik nazewnictwa komponentw oraz zmiennych. Przyznam szczerze, e nie zawsze j stosuj, ale jest to take przydany sposb zwikszenia
706 | S t r o n a

przejrzystoci kodu. Polega na stosowaniu prefiksw przykadowo przed zmienn, ktra jest typu Integer, dodajemy prefiks i (np. iCounter). Zalecane prefiksy przedstawiam w tabelach A.1 i A.2. Tabela A.1. Prefiksy stosowane w stosunku do zmiennych Prefiks Zmienna i Integer i Cardinal i Longint w Word dw DWORD s, str String c, ch Char pc PChar b Boolean

Tabela A.2. Prefiksy popularnych komponentw Prefiks Nawa komponentu mm TMainMenu mmi TMainMenuItem pm TPopupMenu pmi TPopupMenuItem lbl TLabel btn TButton edt TEdit mem TMemo cb TCheckBox rb TRadioButton lb TListBox cb TComboBox pnl TPanel

Czy warto?
Pewnie powiesz: nie chce mi si stosowa takiego stylu. Pewnie wydaje Ci si, e stosowanie tych zasad w praktyce spowoduje, e pisanie kodu zajmie Ci wicej czasu, lecz to

707 | S t r o n a

tylko pozory. Jeeli duo piszesz na klawiaturze, to stosowanie duych liter i stylu wielbdziego tylko w maym stopniu zwiksza czas pisania.

708 | S t r o n a

You might also like