Delphi Kompendium Roz12


Delphi :: Kompendium :: Rozdział 12 - 4programmers.net http://4programmers.net/Delphi/Kompendium/Rozdzia%C5%82_12
Logowanie | Rejestracja | Forum | Pomoc | Reklama | Szukaj
Strona główna :: Delphi :: Kompendium
Rozdział 12
Delphi C/C++
Edytuj Historia
Artykuły Artykuły
Kompendium FAQ
Gotowce C#
Wprowadzenie
WinAPI FAQ
Assembler
.NET
FAQ
Turbo Pascal
(X)HTML
Nie sposób było nie wspomnieć w tej książce o WinAPI. Z tym terminem zetknąłeś się już wielokrotnie podczas FAQ
CSS
lektury niniejszej publikacji. Teraz chciałbym omówić ten temat nieco dogłębniej. Nie jest możliwe bowiem
PHP
JavaScript
zawarcie wszystkich informacji dotyczących WinAPI w jednym rozdziale ? temat ten jest na tyle rozległy, że
FAQ
Z pogranicza
można by mu poświęcić osobną książkę. Więcej o WinAPI możesz dowiedzieć się ze strony
Java
Algorytmy
http://msdn.microsoft.com lub z systemu pomocy Delphi (znajdziesz tam dokładny opis funkcji i procedur).
FAQ
WICEJ
Spis treści
1 Czym tak naprawdÄ™ jest WinAPI?
Delphi Programy
1.1 Zasady tworzenia programów za pomocą WinAPI
C/C++ Dokumentacja
1.2 Brak zdarzeń
Turbo Pascal Kursy
1.3 Brak komponentów
Assembler Komponenty
1.4 Zalety wykorzystania WinAPI
PHP WICEJ
2 Pierwszy program
3 Funkcja okienkowa
4 Rejestracja klasy
5 Tworzenie formularza
6 Komunikaty i uchwyty
7 Aańcuchy
7.1 Konwersja łańcuchów
7.2 Funkcje operujące na łańcuchach
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 Obsługa zdarzeń
10 Uchwyty do kontrolek
11 Tworzenie bardziej zaawansowanych kontrolek
11.1 Pozostałe kontrolki
12 Wyświetlanie grafiki
12.1 Rysowanie w WinAPI
12.2 Kontekst urzÄ…dzenia graficznego
12.3 Obsługa WM_PAINT
12.3.1 Zmiana koloru tła
12.4 Aadowanie i wyświetlanie bitmapy
13 Aadowanie zasobów
13.1 Skompilowane zasoby
13.2 Wykorzystanie zasobów
13.2.1 Wyświetlenie formularza
13.2.2 Ustawianie wartości komponentów formularza
13.3 LockResource, LoadResource, FindResource
13.4 Zapisywanie plików na dysku
14 Podsumowanie
Można powiedzieć, że ten rozdział przeznaczony jest dla ?maniaków? Delphi w pozytywnym tego słowa
znaczeniu. Programowanie w WinAPI nie jest bowiem łatwe i wygodne, ale umożliwia zachowanie większej
kontroli nad programem.
Czym tak naprawdÄ™ jest WinAPI?
Pełna nazwa tego skrótu to Windows Application Programming Interface . Dla osób, które wcześniej tworzyły
swoje programy w Turbo Pascalu, biblioteka wizualna i klasy mogą wydać się dość niezrozumiałe. Z kolei dla
niektórych prostsze może okazać się rozpoczęcie pisania programów metodą API. Ty jednak jesteś już zapewne
przyzwyczajony do stosowania klas i komponentów, a WinAPI może Ci się wydać trudne lub po prostu
nieciekawe. Problem, którego rozwiązanie przy użyciu komponentów wymagało jednego wiersza kodu, przy
wykorzystaniu WinAPI może oznaczać konieczność napisania nawet kilkudziesięciu wierszy! Dlatego nie
zdziwię się, jeżeli ominiesz ten rozdział i przejdziesz od razu do kolejnego.
Zasady tworzenia programów za pomocą WinAPI
Wyobraz sobie pisanie programów bez wykorzystania formularzy, komponentów i wszystkich innych
udogodnień oferowanych przez Delphi. Nasze programy będą oparte jedynie na podstawowych modułach
Windows.pas i Messages.pas. Wszystkie funkcje, z których będziemy korzystać, zawarte są w bibliotekach DLL
systemu Windows. Ich załadowanie do programu odbywa się w module Windows.pas.
Nam, użytkownikom tych bibliotek, potrzebna jest wiedza o ich budowie ? liczbie i typie parametrów,
wartościach zwracanych przez funkcję itp. Stąd miej na uwadze perspektywę częstego zaglądania do pomocy
Delphi.
Przy tej okazji warto wspomnieć o możliwości choć częściowego nauczenia się języka C. System Windows był
pisany w tym języku, stąd opisy wszystkich funkcji API (deklaracje) są również w nim przedstawione. Jest to
pewna okazja do poznania choćby w małym stopniu budowy języka C.
Gdy kiedyś zaczniesz pisać programy w języku C++, znajomość WinAPI bardzo Ci się przyda! Nazwy funkcji są
takie same ? jedynie składnia nieco się różni.
Brak zdarzeń
1 z 18 2009-03-14 15:43
Delphi :: Kompendium :: Rozdział 12 - 4programmers.net http://4programmers.net/Delphi/Kompendium/Rozdzia%C5%82_12
RSS | Forum | Pastebin |
Podczas pisania programów w ?czystym? API będziemy pozbawieni wygodnego mechanizmu, jakim są
Regulamin | Pomoc | Usuń
zdarzenia. Jak pamiętasz z rozdziału 5., mechanizm zdarzeń można zastąpić poprzez komunikaty Windows.
Copyright © 2000-2006 by Coyote Group 0.9.3-pre3
cookies | Prawa autorskie |
Ten moment jest więc dobrą okazją, aby cofnąć się do rozdziału 5. i przypomnieć sobie zasadę funkcjonowania
Czas generowania strony: 1.6790 sek. (zapytań SQL:
Kontakt | Reklama)
komunikatów.
12
Brak komponentów
W API będziemy musieli obyć się bez komponentów. Te ?klocki?, jakimi są komponenty, w dużym stopniu
odciążały nas od mozolnego operowania komunikatami czy pamięcią. Nie będziemy jednak pozbawieni
typowych kontrolek Windows, jak przycisk czy lista rozwijalna ? będziemy je tworzyć w kodzie programu za
pomocÄ… funkcji CreateWindow.
Zalety wykorzystania WinAPI
Zastanawiasz się może, co takiego zyskasz, używając mechanizmów WinAPI? Powiem szczerze: niewiele.
Dużym plusem jest szybkość działania aplikacji oraz rozmiar. Programy tworzone w Delphi i wykorzystujące
VCL mają duże rozmiary. Nawet prosty program w postaci ?czystego? formularza potrafi zajmować grubo
ponad 300 kB. Aplikacje wykorzystujące jedynie WinAPI mogą zajmować nawet 16 kB i są wykonywane
znacznie szybciej. Różnica jest znaczna, nieprawdaż?
Czytając ten rozdział, masz możliwość zaznajomienia się z dotąd nieznanymi funkcjami, z których być może
będziesz musiał skorzystać w swoich programach, gdy VCL okaże się niewystarczający i zbytnio będzie Cię
ograniczał.
Głównymi zaletami Delphi są przecież biblioteka VCL, klasy oraz formularze, dzięki którym tworzenie
programów trwa znacznie krócej. Jeśli porzucisz te udogodnienia, pisanie aplikacji może zająć więcej czasu, a
nie po to chyba tworzono Delphi, prawda?
Podsumowując: WinAPI jest okazją do głębszego zaznajomienia się z tematyką programowania w systemie
Windows, lecz nie nadaje się do pisania dużych projektów.
Pierwszy program
Przypomnij sobie rozdział 2. Wówczas poznawałeś dopiero język Object Pascal, ale tworzone przez Ciebie
programy także nie zawierały żadnych komponentów czy formularzy. Te programy po skompilowaniu także
miały rozmiar kilkunastu kilobajtów ? można zatem powiedzieć, że już wtedy pisałeś programy WinAPI!
Zamknij formularz i Edytor kodu. Następnie z menu Project wybierz polecenie View Source. Kod zródłowy
projektu (pliku *.dpr) doprowadz do takiej postaci:
program Project1;
uses
Windows;
begin
end.
Na razie nie potrzebujemy pliku zasobów, więc usunąłem także dyrektywę {$R}. Ikonę naszego programu
możemy dodać w następnej kolejności. Tak powstały kod zródłowy zapisz gdzieś na dysku. Następnie z menu
Project wybierz polecenie Build, co spowoduje skompilowanie aplikacji i utworzenie pliku *.exe. Spójrz teraz na
rozmiar aplikacji ? u mnie jest to 14 kB! Na razie co prawda program jest ?pusty?, ale już wkrótce co nieco do
niego dodamy.
Funkcja okienkowa
Dotąd nasz program kończył pracę zaraz po uruchomieniu go ? w bloku begin nie ma przecież żadnej
instrukcji. Naszym celem jest napisanie takiego programu w WinAPI, który zakończyłby pracę po interwencji
użytkownika ? zamknięciu okna. Musimy więc napisać kod, który spowodowałby wyświetlenie formularza.
Jednym z etapów tworzenia takiego formularza jest napisanie funkcji okienkowej. Funkcja taka będzie
odpowiedzialna za odbieranie wszystkich komunikatów, które docierają do okna i ewentualną reakcję na dany
komunikat. Zadeklaruj więc w programie taką funkcję:
function WndProc(Wnd: HWND; uMsg: UINT; wPar: WPARAM; lPar: LPARAM): LRESULT; stdcall;
begin
end;
Znaczenie parametrów tej funkcji jest następujące:
Wnd ? uchwyt do okna.
uMsg ? komunikat.
wPar ? pierwsza wartość komunikatu.
lPar ? druga wartość komunikatu.
Taka budowa jest nieprzypadkowa ? aby cały program został prawidłowo skompilowany, funkcja okienkowa
musi wyglądać tak, jak to przedstawiłem powyżej. Pierwszym komunikatem, jaki będzie obsługiwany przez
funkcję okienkową, jest WM_DESTROY. Program musi odpowiednio zareagować na próbę zamknięcia programu.
function WndProc(Wnd: HWND; uMsg: UINT; wPar: WPARAM; lPar: LPARAM): LRESULT; stdcall;
begin
{ na początek zwracamy wartość 0 ? meldunek jest przetwarzany }
Result := 0;
case uMsg of
{ w tym miejscu należy obsłużyć należne 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 kończy pracę ? PostQuitMessage. Na samym jednak początku
przypisujemy funkcji wartość zwrotną ? cyfrę 0. W przeciwnym wypadku ? jeżeli nadesłany komunikat ?nas
interesuje? ? przekazujemy go dalej, do domyślnego okna. Realizuje to funkcja DefWindowProc; parametry
muszą być identyczne z parametrami funkcji okienkowej.
2 z 18 2009-03-14 15:43
Delphi :: Kompendium :: Rozdział 12 - 4programmers.net http://4programmers.net/Delphi/Kompendium/Rozdzia%C5%82_12
Aby program został prawidłowo skompilowany, na liście uses musi znalezć się moduł Messages.pas.
Rejestracja klasy
Aby cały formularz mógł zostać stworzony, uprzednio należy zarejestrować klasę. Rejestracja klasy następuje
poprzez wywołanie funkcji RegisterClass z modułu Windows.pas.
function RegisterClass(const lpWndClass: TWndClass): ATOM; stdcall;
W parametrze owej funkcji należy podać zmienną wskazującą 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;
Powyższy rekord określa wygląd formularza, kolor tła, styl i kursor. Znacznie parametrów jest następujące:
style ? parametr ów określa styl wyświetlanego okna. Możliwe jest mieszanie stylów za pomocą
operatora or.
lpfnWndProc ? jest to wskazanie na funkcjÄ™ okienkowÄ….
cbClsExtra ? liczba dodatkowych bajtów alokowanych wraz z rekordem.
cbWndExtra ? liczba dodatkowych bajtów alokowanych wraz z instancją okna.
hInstance ? uchwyt do zasobów.
hIcon ? identyfikacja formularza.
hCursor ? kursor używany w czasie wyświetlania formularza.
hbrBackground ? tło formularza. Możliwe jest zastosowanie jednej z poniższych wartości:
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 łańcuch określający menu używane w programie.
lpszClassName ? wskazanie na nazwę klasy (wartość typu PChar).
Rejestracja nowej klasy może być wykonana w poniższy sposób:
var
Wnd: TWndClass; // klasa okna
begin
with Wnd do
begin
lpfnWndProc := @WndProc; // funkcja okienkowa
hInstance := hInstance; // uchwyt do zasobów
lpszClassName := 'My1stApp'; // klasa
hbrBackground := COLOR_WINDOW; // kolor tła
end;
RegisterClass(Wnd); // zarejestruj nowÄ… klasÄ™
end;
W moim przypadku nie było konieczne wypełnianie wszystkich pól rekordu TWndClass. Przypisałem jedynie te
pola, które wydawały się konieczne do uzyskania przynajmniej podstawowego wyglądu formularza.
Tworzenie formularza
Na szczęście tworzenie samego formularza nie jest czynnością 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ść parametrów jest spora:
lpClassName ? nazwa klasy (wartość PChar). Wartość ta musi się równać wartości wpisanej w rekordzie
TWndClass.
lpWindowName ? łańcuch określający tekst, który będzie wyświetlany na formularzu. Znaczenie
parametru można porównywać do właściwości Caption komponentów.
dwStyle ? styl okna (tabela 12.1).
x ? położenie formularza w poziomie. Wstawienie w to miejsce stałej CW_USEDEFAULT powoduje
automatyczne dopasowanie położenia przez system (nowe okno będzie przesunięte lekko w lewą stronę).
y ? położenie formularza w pionie. Wstawienie w to miejsce stałej CW_USEDEFAULT powoduje
automatyczne dopasowanie położenia przez system.
nWidth ? szerokość formularza. Tutaj także stała CW_USEDEFAULT powoduje automatyczne
dopasowanie szerokości.
nHeight ? wysokość formularza. Stała CW_USEDEFAULT powoduje dopasowanie wysokości formularza.
hWndParent ? uchwyt do okna rodzica.
hMenu ? wskazanie do menu, które ma być użyte w programie.
hInstance ? określa instancję modułu, który ma być kojarzony z programem.
Na podstawie tych danych utworzenie formularza może wyglądać tak:
3 z 18 2009-03-14 15:43
Delphi :: Kompendium :: Rozdział 12 - 4programmers.net http://4programmers.net/Delphi/Kompendium/Rozdzia%C5%82_12
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. Najczęstsze wartości określające styl okna
Wartość Opis
WS_OVERLAPPED Okno posiada pasek tytułowy oraz obramowanie
WS_CHILD Potomne okno, które nie może ?wyjść? poza okno rodzicielskie
WS_POPUP Okno dialogowe
WS_CAPTION Okno ma pasek tytułu
WS_SYSMENU Okno ma menu systemowe
WS_MINIMIZEBOX Okno ma przycisk minimalizacji
WS_MAXIMIZEBOX Okno ma przycisk maksymalizacji
WS_VISIBLE Okno jest widoczne
WS_HIDE Okno jest ukryte
WS_DISABLED Nieaktywne okno ? nie reaguje na zdarzenia
WS_BORDER Okno posiada ramkÄ™
Listing 12.1. Pierwszy program napisany w WinAPI
{
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 początek zwracamy wartość 0 ? meldunek jest przetwarzany }
Result := 0;
case uMsg of
{ w tym miejscu należy obsłużyć należne 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; // klasa okna
Msg: TMsg;
begin
with Wnd do
begin
lpfnWndProc := @WndProc; // funkcja okienkowa
hInstance := hInstance; // uchwyt do zasobów
lpszClassName := 'My1stApp'; // klasa
hbrBackground := COLOR_WINDOW; // kolor tła
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 listingu 12.1 zaprezentowano cały kod zródłowy programu. Nie omawiałem jeszcze ostatnich instrukcji tego
listingu. Są one bardzo ważne ? bez nich program nie będzie mógł zostać uruchomiony. Operacje te muszą być
wykonane, aby funkcja okienkowa otrzymała potrzebne meldunki. Podczas uruchamiania program musi wejść w
tzw. fazę meldunków. Funkcja GetMessage pobiera kolejno meldunki, wpisując je do struktury TMsg (parametry
są nieistotne), a następnie przekazuje funkcji DispatchMessage, która z kolei przekazuje meldunek funkcji
okienkowej.
Komunikaty i uchwyty
Pisząc programy w WinAPI, będziemy posługiwali się wyłącznie komunikatami jako formą zastępującą zdarzenia
(funkcje SendMessage i PostMessage). Warto więc przypomnieć sobie informacje na temat wysyłania
komunikatów. Ich odbieranie będzie następowało tylko w funkcji okienkowej.
Odświeżmy zatem pamięć ? komunikaty można podzielić na następujące kategorie:
komunikaty klawiaturowe (użytkownik nacisnął lub zwolnił jakiś klawisz),
komunikaty myszy (użytkownik wykonał jakąś czynność myszą),
komunikaty zegara, oznaczające upływ określonego odcinka czasu,
komunikaty systemu ? tworzenie okna, zmiana jego rozmiaru i położenia, zwijanie i rozwijanie okna,
zmiana kolorów systemowych itp.,
komunikaty wewnętrzne ? wysyłane przez inne okna utworzone w naszym programie.
W każdym komunikacie należy podać uchwyt okna docelowego (lub kontrolki docelowej). Uchwyt jest liczbą
32-bitową, która identyfikuje kontrolkę w systemie Windows. To właśnie Windows przydziela uchwyty różnym
kontrolkom. Nazwa typu reprezentujÄ…cego uchwyt zaczyna siÄ™ od litery H, czyli np. HWND, HBRUSH, HFONT czy
4 z 18 2009-03-14 15:43
Delphi :: Kompendium :: Rozdział 12 - 4programmers.net http://4programmers.net/Delphi/Kompendium/Rozdzia%C5%82_12
HBITMAP. Dzięki temu łatwo jest rozpoznać, czy dana zmienna jest uchwytem.
Aańcuchy
Podczas pisania programów w WinAPI będziemy używali jedynie łańcuchów typu PChar lub łańcuchów w formie
tablicy. Stosowanie typu String powoduje spowolnienie działania programu i zużywanie większej ilości pamięci.
Typ PChar ma jeszcze jedną zaletę ? można dokonywać na nim takich operacji:
P2 := 'To jest Delphi';
P1 := P2 + 8;
Pozornie wygląda to tak, jakby do zmiennej P2 dodawana była cyfra 8. W rzeczywistości do typu P1
przypisujemy wartość zmiennej P2, tyle że bez pierwszych 8 znaków. Podczas lektury dalszej części książki
możesz spotkać się także z deklaracjami zmiennych w postaci tablicy:
Variable : array[0..255] of char;
Do takiej zmiennej można następnie przypisać dane w zwykły sposób, tyle że ich wielkość będzie ograniczona
do 255 znaków.
Variable := 'Adam Boduch';
Konwersja łańcuchów
W VCL ten problem nie istniał ? moduł SysUtils posiadał odpowiednie funkcje, umożliwiające konwersję typów.
Pisząc programy API, nie będziemy mogli z nich skorzystać ? pozostaje nam użycie funkcji zastępczych, np.
wvsprintf.
Oto możliwy sposób wykonania funkcji zastępczej:
function IntToStr(Value : Integer) : String;
var
Buffer : array[0..255] of char; // bufor, w którym przechowywać będziemy dane
begin
wvsprintf(Buffer, '%d', @Value); // tu następuje funkcja konwersji
Result := Buffer; // zwracamy rezultat
end;
Funkcja wvsprintf służy do konwertowania tekstu. Pierwszym parametrem musi być wskazanie zwracanego
przez funkcję ciągu. Ja zadeklarowałem Buffer ? 256-elementową tablicę typu Char. Drugi parametr to tzw.
maska. Jeśli wstawimy w to miejsce znak %d, zostanie on zastąpiony liczbą typu Integer. Owa liczba to
zmienna Value, przekazywana jako trzeci parametr. Przykładowy program prezentujący działanie łańcuchów,
zamieściłem w listingu 12.2.
W powyższej funkcji IntToStr zadeklarowałem tablicę 256-elementową (standardowo),
chociaż tak naprawdę aż tak duża wartość nie jest konieczna.
Wyjątkowo w powyższej funkcji jako zwracanego rezultatu użyłem typu String. Zrobiłem to
tylko po to, aby upodobnić budowę funkcji do rzeczywistego wyglądu funkcji IntToStr z
modułu SysUtils.
Listing 12.2. Pełny kod zródłowy programu
{
Copyright (c) 2002 by Adam Boduch
}
uses Windows;
function IntToStr(Value : Integer) : String;
var
Buffer : array[0..255] of char; // bufor, w którym przechowywać będziemy dane
begin
wvsprintf(Buffer, '%d', @Value); // tu następuje funkcja konwersji
Result := Buffer; // zwracamy rezultat
end;
begin
MessageBox(0, PChar('Witaj w ' + IntToStr(12) + ' części książki!'), 'Witaj!', MB_OK); //
wyświetl wartość zmiennej
end.
Inny przykład wykorzystania funkcji wvsprintf:
Buffer : array[0..255] of char;
Format : packed record // deklaracja rekordu danych do konwersji
Int : Integer;
Fl : String;
end;
begin
{ wypełnienie danych do konwersji }
Format.Int := 11;
Format.Fl := 'Adam Boduch';
wvsprintf(Buffer, 'Witaj w %d części kursu, ja nazywam się %s!', @Format);
MessageBox(0, Buffer, '', 0);
end.
5 z 18 2009-03-14 15:43
Delphi :: Kompendium :: Rozdział 12 - 4programmers.net http://4programmers.net/Delphi/Kompendium/Rozdzia%C5%82_12
W tym wypadku w drugim parametrze w łańcuchu znajdują się dwa znaki ? %s i %d. Zostaną one po konwersji
zastąpione danymi w postaci liczby Integer oraz łańcucha String.
Funkcje operujące na łańcuchach
Poniżej przedstawiam kilka funkcji WinAPI operujących na łańcuchach. 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ę znaków odpowiednio na małe lub wielkie litery. Pierwsza z nich (CharLower)
zamienia litery z wielkich na małe, a CharUpper ? z małych 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ć także funkcją CharLowerBuff i CharUpperBuff. Obie także powodują zamianę znaków,
lecz posiadają także dodatkowy parametr, który określa liczbę znaków, które mają zostać zamienione.
lstrlen
function lstrlen(lpString: PChar): Integer; stdcall;
Funkcja lstrlen podaje długość łańcucha określonego w parametrze lpString. Długość podawana jest w
znakach.
Writeln(lstrlen('Adam'));
Powyższa instrukcja wyświetli na ekranie liczbę 4.
lstrcpyn
function lstrcpyn(lpString1, lpString2: PChar; iMaxLength: Integer): PChar; stdcall;
Funkcja służy do kopiowania części łańcucha do drugiej zmiennej. Pierwszy parametr musi być wskazaniem
łańcucha, do którego zostaną skopiowane dane. Drugi parametr ? lpString2 ? to miejsce, z którego dane
zostaną pobrane. Ostatni parametr ? iMaxLength ? określa liczbę znaków do skopiowania:
program main;
uses Windows;
{$APPTYPE CONSOLE}
var
P1 : array[0..50] of char;
begin
lstrcpyn(P1, 'Delphi jest narzędziem typu RAD', 7);
Writeln(P1);
Readln;
end.
Powyższy kod zródłowy spowoduje wyświetlenie na ekranie napisu Delphi.
Tworzenie kontrolek
Zarówno tworzenie komponentów, jak i różnych kontrolek odbywa się za pośrednictwem funkcji CreateWindow.
W przypadku komponentów nie będzie konieczna rejestracja nowych klas itp. elementów. Do stworzenia
nowego komponentu wystarczy napisanie jednego wiersza kodu. Komponent należy utworzyć z flagą WS_CHILD
oraz WS_VISIBLE. Stworzenie przycisku będzie więc wyglądało następująco:
CreateWindow('BUTTON', 'Przycisk', WS_CHILD or WS_VISIBLE, 100, 100, 120, 25, Wnd, 0,
hInstance, nil);
Jedyną charakterystyczną cechą jest pierwszy parametr tej funkcji. Jeżeli chcesz stworzyć przycisk, musisz w to
miejsce wpisać słowo BUTTON. Drugi parametr to tekst, który będzie widniał na przycisku. Trzeci parametr to
flagi komponentu. Dalsza część jest już taka sama, jak w przypadku tworzenia formularza. W tabeli 12.2
umieściłem wartości, jakie może przyjmować pierwszy parametr funkcji CreateWindow.
Tabela 12.2. Możliwe wartości pierwszego parametru funkcji CreateWindow
Wartość Opis
BUTTON Przycisk ? odpowiednik komponentu TButton
COMBOBOX Lista rozwijalna ? odpowiednik TComboBox
EDIT Kontrolka edycyjna ? jedno liniowa. Odpowiednik komponentu TEdit
LISTBOX Kontrolka wielowierszowa. Odpowiednik TListBox
6 z 18 2009-03-14 15:43
Delphi :: Kompendium :: Rozdział 12 - 4programmers.net http://4programmers.net/Delphi/Kompendium/Rozdzia%C5%82_12
MDICLIENT Okno potomne ? MDI
SCROLLBAR Pasek przewijania ? inaczej TScrollBar
STATIC Etykieta tekstowa. Odpowiednik TLabel
Taka kontrolka będzie więc ?dzieckiem? w stosunku do formularza (WS_CHILD) i będzie także widoczna
(WS_VISIBLE).
Jeżeli chcesz, aby kontrolka na starcie była niewidoczna, użyj flagi WS_HIDE.
Pamiętaj, aby podczas tworzenia nowej kontrolki w parametrze hWndParent (czwarty od końca) podać uchwyt
okna głównego. Parametr ów określa uchwyt okna ?rodzica? ? w tym wypadku formularza.
Umieszczanie kontrolek przy starcie programu
Jeżeli chcemy, aby kontrolki były tworzone na starcie programu, kod należy umieścić w funkcji okienkowej.
Konieczne jest także obsłużenie komunikatu WM_CREATE. Oto kod:
function WndProc(Wnd: HWND; uMsg: UINT; wPar: WPARAM; lPar: LPARAM): LRESULT; stdcall;
begin
{ na początek 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 powyżej, na formularzu w punkcie
100,100 zostanie umieszczony przycisk. W listingu 12.3 znajduje się kod zródłowy programu, którego efektem
jest umieszczenie 5 przycisków na raz.
Listing 12.3. Umieszczanie kilku przycisków
{
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 którym przechowywać będziemy dane
begin
wvsprintf(Buffer, '%d', @Value); // tu następuje funkcja konwersji
Result := Buffer; // zwracamy rezultat
end;
function WndProc(Wnd: HWND; uMsg: UINT; wPar: WPARAM; lPar: LPARAM): LRESULT; stdcall;
var
i : Integer;
begin
{ na początek zwracamy wartość 0 ? meldunek jest przetwarzany }
Result := 0;
case uMsg of
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; // klasa okna
Msg: TMsg;
begin
with Wnd do
begin
lpfnWndProc := @WndProc; // funkcja okienkowa
hInstance := hInstance;
lpszClassName := 'My1stApp'; // klasa
hbrBackground := COLOR_WINDOW; // kolor tła
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.
7 z 18 2009-03-14 15:43
Delphi :: Kompendium :: Rozdział 12 - 4programmers.net http://4programmers.net/Delphi/Kompendium/Rozdzia%C5%82_12
W kodzie wykorzystaliśmy wcześniej napisaną funkcję IntToStr. Umieszczenie 5 kontrolek następuje w pętli,
dlatego też za każdą iteracją należy zmieniać położenie przycisku w pionie, wykonując takie działanie:
100 + I * 30;
Powoduje to dodanie do liczby 100 wartości z mnożenia ? np. 30, 60, 90 itd. Działanie programu prezentuje
rysunek 12.1.
Rysunek 12.1. Działanie programu
Flagi kontrolek
Poszczególne kontrolki umieszczone na formularzu mogą posiadać dodatkowe flagi, określające zachowanie lub
wygląd komponentu. Owe flagi można podawać jako trzeci parametr polecenia CreateWindow, łącząc je
operatorem or. W tabelach 12.3 ? 12.6 przedstawiam najczęściej używane flagi.
Tabela 12.3. Flagi używane z kontrolką BUTTON
Flaga Krótki opis
BS_3STATE Kontrolka (przycisk) stanie siÄ™ komponentem ? la TCheckBox
Flaga podobna do BS_3STATE, tyle że komponent może przybierać wartość
BS_AUTO3STATE
?zaznaczony?
BS_AUTORADIOBUTTON Kontrolka (przycisk) stanie siÄ™ komponentem ? la TRadioButton (rysunek 12.2)
BS_DEFPUSHBUTTON Powoduje, że przycisk zostanie wyświetlony z czarną, pogrubioną obwódką
BS_GROUPBOX Komponent zostanie wyświetlony z obwódką (rysunek 12.3)
BS_BITMAP Umożliwia wyświetlanie bitmapy na kontrolce
BS_BOTTOM Ustawia tekst na samym dole komponentu
BS_CENTER Centruje tekst w poziomie
BS_ICON Umożliwia wyświetlanie ikony na komponencie
BS_LEFT Tekst będzie wyrównany do lewej strony
BS_MULTILINE Flaga umożliwia wyświetlanie kilku wierszy tekstu
BS_RIGHT Tekst będzie wyrównany do prawej strony
BS_TOP Tekst zostanie umieszczony u góry kontrolki
BS_VCENTER Tekst zostanie wyśrodkowany w pionie
8 z 18 2009-03-14 15:43
Delphi :: Kompendium :: Rozdział 12 - 4programmers.net http://4programmers.net/Delphi/Kompendium/Rozdzia%C5%82_12
Rysunek 12.2. Przyciski w formie komponentu TRadioButton
Rysunek 12.3. Przyciski w formie kontrolek TGroupBox
Tabela 12.4. Flagi używane z kontrolką COMBOBOX
Flaga Krótki opis
CBS_DISABLENOSCROLL Pasek przewijania zostanie zablokowany
CBS_DROPDOWN Lista rozwijalna zostanie aktywna
Nie będzie możliwe edytowanie listy rozwijalnej (zaznaczona pozycja nie będzie
CBS_DROPDOWNLIST
mogła być zmieniana)
CBS_LOWERCASE Konwertuje tekst wpisany w kontrolce na małe litery
CBS_UPPERCASE Konwertuje tekst wpisany w kontrolce na wielkie litery
CBS_SORT Automatyczne sortowanie danych wpisanych w kontrolce
Tabela 12.5. Flagi używane z kontrolką EDIT
Flaga Krótki opis
Automatycznie przewiń tekst w kontrolce w poziomie, jeżeli użytkownik wpisał więcej
ES_AUTOHSCROLL
znaków niż może być w niej wyświetlone
ES_CENTER Wyśrodkuj tekst, jeżeli kontrolka zawiera wiele wierszy
ES_LEFT Wyrównaj tekst do lewej strony
ES_LOWERCASE Konwertuj wpisany tekst na małe litery
ES_MULTILINE Flaga umożliwia wpisywanie w kontrolce wielu wierszy tekstu
ES_NUMBER Zezwalaj na wpisywanie jedynie liczb
ES_PASSWORD Tekst wpisany w kontrolce zostanie zastÄ…piony znakami *
ES_READONLY Tekst wpisany w kontrolce będzie przeznaczony jedynie do odczytu
Tekst zostanie wyrównany do prawej strony, jeżeli kontrolka została stworzona z flagą
ES_RIGHT
ES_MULTILINE
ES_UPPERCASE Wpisane w kontrolce litery konwertuj na wielkie
9 z 18 2009-03-14 15:43
Delphi :: Kompendium :: Rozdział 12 - 4programmers.net http://4programmers.net/Delphi/Kompendium/Rozdzia%C5%82_12
Dotyczy kontrolek wielowierszowych. Po zastosowaniu tej flagi naciśnięcie klawisza Enter
ES_WANTRETURN
przenosi kursor do kolejnego wiersza
Tabela 12.6. Flagi używane z kontrolką LISTBOX
Flaga Krótki opis
LBS_DISABLENOSCROLL Wyświetla nieaktywny, pionowy pasek przewijania
LBS_EXTENDEDSEL Zezwala na zaznaczenie wielu wierszy z użyciem klawisza Shift
LBS_MULTICOLUMN Zezwala na wyświetlanie w komponencie kilku kolumn
LBS_SORT Automatyczne sortowanie kolumn
Więcej informacji na temat flag znajdziesz w pomocy WinAPI pod hasłem CreateWindow.
Obsługa zdarzeń
Umiemy już tworzyć formularze i umieszczać na nich kontrolki WinAPI. Kolejnym krokiem jest obsługa zdarzeń
(np. kliknięcia obiektu). Pierwszym krokiem będzie 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 nadaliśmy kontrolce identyfikator nr 100. Od tego momentu podczas kliknięcia przycisku
będziemy musieli odbierać komunikat WM_COMMAND i ? zależnie od numeru ID ? odpowiednio reagować:
WM_COMMAND:
if wPar = 100 then MessageBox(Wnd, 'NacisnÄ…Å‚eÅ›!', '', MB_OK);
Na takiej samej zasadzie możesz kontrolować naciśnięcie wszystkich przycisków ? ważne jest tylko, aby numery
ID różniły się.
W powyższym przykładzie skorzystałem z instrukcji if, lecz przy większej liczbie instrukcji wygodniej będzie
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 początku zwracamy wartość 0 ? meldunek jest przetwarzany }
Result := 0;
case uMsg of
WM_CREATE:
begin
{ w pętli umieszczamy kilka przycisków, każdemu z nich nadając kolejny identyfikator -
poczynajÄ…c 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: // obsługa kliknięcia przycisku
begin
case wPar of // sprawdz, czy w Par jest od 101 do 105
101..105: MessageBox(Wnd, PChar('Witaj!, nacisnÄ…Å‚eÅ› przycisk nr ' + IntToStr(wPar ? 100)
+ '!'), ':?)',
MB_OK + MB_ICONINFORMATION);
end;
end;
WM_DESTROY: PostQuitMessage(0);
else Result := DefWindowProc(Wnd, uMsg, wPar, lPar);
end;
end;
Jak widać, za każdą iteracją pętli nowa kontrolka zostaje utworzona ze zmienionym numerem ID. Po
uruchomieniu aplikacji i naciśnięciu przez użytkownika przycisku do programu zostaje wysłany komunikat
WM_COMMAND z parametrem wPar, który zawiera numer ID przycisku. Na tej podstawie możemy odpowiednio
zareagować ? w tym wypadku poprzez wyświetlenie komunikatu.
Pełen kod zródłowy powyższego programu możesz znalezć na płycie CD-ROM w katalogu ..listingi/12
/Wm_Command.
Uchwyty do kontrolek
Po prawidłowym 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 mając uchwyt takiej kontrolki, możemy wysyłać do niej komunikaty. Przykładowo chcąc pobrać tekst z
kontrolki EDIT, musimy skorzystać z funkcji GetWindowText:
GetWindowText(Edit, Buffer, SizeOf(Buffer)); // pobierz tekst z edita
10 z 18 2009-03-14 15:43
Delphi :: Kompendium :: Rozdział 12 - 4programmers.net http://4programmers.net/Delphi/Kompendium/Rozdzia%C5%82_12
Pierwszym parametrem tej funkcji musi być uchwyt kontrolki, z której chcemy pobrać tekst. Drugi parametr ?
Buffer ? to np. łańcuch o takiej postaci:
var
Buffer : array[0..128] of char;
Pełny kod programu znajduje się w listingu 12.4.
Listing 12.4. Pełny 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 początku 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); // wyświetl 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
Msg: TMsg;
begin
with Wnd do
begin
lpfnWndProc := @WndProc; // funkcja okienkowa
hInstance := hInstance;
lpszClassName := 'My1stApp'; // klasa
hbrBackground := COLOR_WINDOW; // kolor tła
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 kolejności po naciśnięciu przycisku pobierana zostaje wartość wpisana w kontrolce EDIT. Teraz
wystarczy już tylko wyświetlić zawartość zmiennej Buffer. Następnie program ustawia nową wartość dla okna
formularza (można powiedzieć, że to jest właściwość Caption):
SendMessage(Wnd, WM_SETTEXT, 0, Longint(@Buffer));
Chciałem przy okazji zaprezentować sposób wysyłania komunikatów do kontrolek. W tym celu do okna należy
przekazać komunikat WM_SETTEXT. Drugi parametr natomiast musi być wskazaniem tekstu, który ma zostać
umieszczony w oknie.
Tworzenie bardziej zaawansowanych kontrolek
Aby możliwe było tworzenie bardziej zaawansowanych kontrolek (takich, jak komponenty typu TProgressBar
czy TListView), należy do listy uses dodać moduł CommCtrl. Operować tymi komponentami możemy tylko
poprzez komunikaty. Ich spis możesz znalezć w pliku CommCtrl.pas. Znaczenie poszczególnych komunikatów
jest bardzo intuicyjne. Cóż bowiem wykonuje komunikat PBM_SETPOST? Można się domyśleć, że ustawia nową
pozycjÄ™ w komponencie.
Jeżeli uruchomisz program z użyciem biblioteki CommCtrl, a na ekranie nadal widnieć będzie
?czysty? formularz (tzn. komponent nie zostanie utworzony), to wówczas konieczne będzie
wywołanie procedury InitCommonControls. Procedura ta inicjuje odpowiednią bibliotekę DLL.
Najlepiej tę procedurę wywołać tuż po utworzeniu nowej klasy w sekcji begin..end.
11 z 18 2009-03-14 15:43
Delphi :: Kompendium :: Rozdział 12 - 4programmers.net http://4programmers.net/Delphi/Kompendium/Rozdzia%C5%82_12
Przykładowo ? utworzenie nowej kontrolki ? la TProgressBar wygląda następująco:
CreateWindow('msctls_progress32', '', WS_CHILD or WS_VISIBLE, 100, 10, 350, 20, Wnd, 0,
hInstance, nil);
Decydujące znaczenie ma tutaj parametr msctls_progress32. Spis wszystkich parametrów kluczowych dla
utworzenia komponentu możesz znalezć w pliku CommCtrl.pas. Po utworzeniu komponentu można wysyłać do
niego komunikaty ? np. dotyczÄ…ce zmiany pozycji:
for i := 0 to 100 do
begin
Sleep(50);
SendMessage(ProgressBar, PBM_SETPOS, i, 0);
end;
Jest to zatem pętla od jednego do stu z przerwami pomiędzy kolejnymi iteracjami, wynoszącymi 50 milisekund.
Podczas każdorazowego wykonania pętli do komponentu jest wysyłany komunikat PBM_SETPOS. Parametr lParam
funkcji SendMessage zawiera nową wartość (pozycję) paska postępu. Cały ten kod umieścimy w programie
obsługi komunikatu WM_PAINT (listing 12.5.)
Listing 12.5. Pełny 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): LRESULT; stdcall;
var
i : Integer;
begin
{ na początku 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: // obsługa komunikatu WM_PAINT
begin
for i := 0 to 100 do
begin
Sleep(50); // odczekaj 50 milisekund
SendMessage(ProgressBar, PBM_SETPOS, i, 0); // wyślij 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; // klasa okna
Msg: TMsg;
begin
with Wnd do
begin
lpfnWndProc := @WndProc; // funkcja okienkowa
hInstance := hInstance; lpszClassName := 'My1stApp'; // klasa
hbrBackground := COLOR_WINDOW; // kolor tła
hIcon := LoadIcon(0, IDI_APPLICATION); // domyślna ikona
hCursor := LoadCursor(0, IDC_ARROW); // domyślny kursor
end;
RegisterClass(Wnd); // zarejestruj nowÄ… klasÄ™
InitCommonControls;
// stwórz formularz...
CreateWindow('My1stApp', 'Aplikacja z wykorzystaniem modułu 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.
Pozostałe kontrolki
Nazwy pozostałych kontrolek, z jakich możesz skorzystać w swoich programach, zawarte są w pliku
CommCtrl.pas. Tam również możesz znalezć listę komunikatów związanych z konkretnym komponentem. W
tabeli 12.7 prezentuję komponenty, których możesz użyć w swoich programach w API, wraz z ich
odpowiednikami w VCL.
12 z 18 2009-03-14 15:43
Delphi :: Kompendium :: Rozdział 12 - 4programmers.net http://4programmers.net/Delphi/Kompendium/Rozdzia%C5%82_12
Tabela 12.7. Kontrolki z biblioteki CommCtrl.dll
Identyfikator kontrolki Nazwa kontrolki Odpowiednik VCL
ToolbarWindow32 Pasek narzędziowy TToolBar
msctls_statusbar32 Pasek aplikacji TStatusBar
msctls_trackbar32 Pasek przewijania TTrackBar
msctls_updown32 Pasek góra-dół TUpDown
msctls_progress32 Pasek postępu TProgressBar
SysListView32 Lista pozycji TListView
SysTreeView32 Drzewo obiektów TTreeView
ComboBoxEx32 Kontrolka Combo TComboBoxEx
SysTabControl32 System zakładek TTabControl
SysAnimate32 Animacje systemowe TAnimate
SysMonthCal32 Kalendarz TMonthCalendar
SysDateTimePick32 Prezentuje datÄ™ i czas TDateTimePicker
SysIPAddress32 Podaje adres IP komputera. brak odpowiednika
SysPager System stron TPageControl
Wyświetlanie grafiki
We wcześniejszych rozdziałach miałeś okazję zapoznać się z funkcjami operującymi na grafice czy też
wyświetlającymi tekst. Poznałeś także klasę TCanvas, która owe zadanie znacznie upraszczała. Funkcje API
umożliwiające rysowanie lub wyświetlanie grafiki są bardzo podobne do funkcji z klasy TCanvas. Różnica polega
jedynie na liczbie parametrów.
Rysowanie w WinAPI
Przypominam, że rysowanie czegokolwiek powinno odbyć się po wywołaniu komunikatu WM_PAINT. Wiadomo, że
okno w Windows może podlegać różnym zdarzeniom, takim jak: minimalizacja, możliwość zasłonięcia przez
inne okno itp. System nie przechowuje obrazu ekranu w pamięci, lecz odpowiedzialna jest za to sama aplikacja.
Po odsłonięciu okna i przywróceniu go na pierwszy plan do aplikacji wysyłany jest komunikat WM_PAINT ? do nas
należy obsłużenie tego komunikatu.
Kontekst urzÄ…dzenia graficznego
Wszystkie funkcje operujące na WinAPI wymagają podania pierwszego parametru, który jest tzw. kontekstem
urządzenia (Device Context). Jest to pewna struktura danych, opisująca różne parametry rysowania ? czcionkę,
grubość i kolor linii itp. Takie ustawienia są różne dla każdego programu uruchomionego w systemie, a my w
swoich aplikacjach będziemy musieli pobierać uchwyt do owego kontekstu:
DCHandle := GetDC(Wnd);
Od tej pory mamy już uchwyt, który będzie trzeba podawać przy każdej funkcji operującej na grafice. Po
zakończeniu malowania należy ten uchwyt zwolnić, korzystając z polecenia ReleaseDC:
ReleaseDC(Wnd, DCHandle);
Za pomocą funkcji GetDC uzyskujemy uchwyt, dzięki któremu możemy malować po całym
obszarze roboczym programu. Nie mamy natomiast możliwości rysowania na pasku
tytułowym okna ? do tego będzie nam potrzebna funkcja GetWindowDC.
Obsługa WM_PAINT
Istnieje lepsza metoda dostarczania kontekstu urzÄ…dzenia ? rekord TPaintStruct. Rekord TPaintStruct
dostarcza oprócz kontekstu urządzenia także informacje, które można wykorzystać podczas obsługi komunikatu
WM_PAINT. Namalowanie czegoś na formularzu opiera się na wywołaniu metody BeginPaint oraz ? po
zakończeniu ? EndPaint:
WM_PAINT:
begin
DC := BeginPaint(Wnd, PS);
TextOut(DC, 10, 10, 'Delphi 7', Length('Delphi 7'));
EndPaint(Wnd, PS);
end;
Wcześniej jednak należy 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 urządzenia. Kolejne dwa parametry to współrzędne
rysowanego tekstu. Trzeci parametr to tekst, który zostanie narysowany na formularzu, a ostatni ? długość
tekstu. Rysunek 12.4 prezentuje program po uruchomieniu.
13 z 18 2009-03-14 15:43
Delphi :: Kompendium :: Rozdział 12 - 4programmers.net http://4programmers.net/Delphi/Kompendium/Rozdzia%C5%82_12
Rysunek 12.4. Tekst narysowany metodÄ… TextOut
Zmiana koloru tła
Na rysunku 12.4 widzisz, że dotychczasowy efekt działania funkcji TextOut nie jest zbyt interesujący. Napis
jest wyświetlany na białym tle. Aby to zmienić, można ustawić przezroczyste tło, używając w tym celu funkcji
SetBkMode:
SetBkMode(DC, TRANSPARENT);
Pierwszy parametr musi być kontekstem urządzenia, a drugi to flaga informująca o tym, że tło będzie
przezroczyste (TRANSPARENT).
W dość prosty sposób można zmienić również kolor wyświetlanego tekstu. Wystarczy zastosować funkcję
SetTextColor. Pierwszym parametrem tej funkcji musi być oczywiście kontekst urządzenia. Drugi parametr
musi określać kolor tekstu w postaci RGB (Reed Green Blue), czyli kombinacji trzech kolorów: czerwonego,
zielonego i niebieskiego ? np.:
SetTextColor(DC, RGB(0, 100, 150));
Aadowanie i wyświetlanie bitmapy
Załóżmy, że w zasobach programu umieszczona jest bitmapa o nazwie ID_BITMAP. Przy wykorzystaniu VCL
załadowanie bitmapy do komponentu TImage zajęłoby chwilę ? wystarczyłby jeden wiersz kodu:
Image.Picture.Bitmap.LoadFromResourceName(hInstance, 'ID_BITMAP');
Chcąc skorzystać z funkcji API, musimy poświęcić na to nieco więcej czasu i napisać więcej 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);
EndPaint(Wnd, PS);
end;
Samo załadowanie jest proste, gdyż używamy tutaj funkcji LoadBitmap, która zwraca bitmapę w postaci
zmiennej HBITMAP (odpowiednik klasy TBitmap). Następnym krokiem jest stworzenie pamięciowego kontekstu
urządzenia (CreateCompatibleDC), na którym zostanie narysowana bitmapa. Kolejny krok to wybranie
właściwego kontekstu za pomocą funkcji SelectObject. Wreszcie samo narysowanie bitmapy następuje poprzez
funkcję BitBlt. Pierwszy parametr owej funkcji to uchwyt kontekstu, na którym zostanie narysowana bitmapa.
Kolejne dwa parametry to X i Y, współrzędne miejsca wyświetlania obrazka. Rozmiar rysowanej bitmapy
określają dwa kolejne parametry. Jeszcze inne dwa parametry określają pozycję X i Y lewego górnego rogu
obrazka oraz stopień jego wyświetlania. Ostatni parametr określa sposób przedstawienia bitmapy ? w tym
wypadku kopiowanie ze zródła do miejsca przeznaczenia.
Najlepszym sposobem sprawdzenia działania owych parametrów jest przetestowanie ich w praktyce.
Nim skompilujesz swój program, zadeklaruj w nim następujące zmienne:
var
Bitmap : HBITMAP;
_Bitmap : HDC;
Aadowanie zasobów
W rozdziale 10. była mowa o wykorzystaniu zasobów do przechowywania w pliku wykonywalnym różnych
danych ? począwszy od grafiki, a na innych plikach wykonywalnych skończywszy. W tym podpunkcie pokażę, w
jaki sposób skorzystać z tych zasobów, nie używając przy tym klasy TResourceStream.
Korzystając z zasobów, można w WinAPI napisać swój własny instalator, który będzie ?przechowywał? w sobie
pliki instalacyjne. Taki przykładowy instalator możesz znalezć na płycie CD-ROM w katalogu ../listingi
/12/Install. W katalogu z projektem znajdziesz również skompilowaną wersję projektu ? plik Install.exe
(rysunek 12.5). Po uruchomieniu programu instalacyjnego na dysku zostanie zainstalowany program, który
kiedyś napisałem (lecz to jest w tej chwili nie istotne).
14 z 18 2009-03-14 15:43
Delphi :: Kompendium :: Rozdział 12 - 4programmers.net http://4programmers.net/Delphi/Kompendium/Rozdzia%C5%82_12
Rysunek 12.5. Instalator wykonany w WinAPI
Skompilowane zasoby
Tworzenie zasobów za pomocą programu brcc32.exe wygląda tak samo, jak to przedstawiałem w rozdziale 10. ?
np. w przypadku mojego instalatora plik files.rc wyglÄ…da 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 włączenie do gotowego zasobu (*.res) powyżej przedstawionych plików.
W rozdziale 10. nie wspomniałem o jednej kwestii ? mianowicie o możliwości tworzenia bardziej
zaawansowanych zasobów, np. formularza:
LICENCJA DIALOGEX 42, 8, 271, 133
STYLE DS_MODALFRAME | DS_CENTER | DS_3DLOOK | DS_SETFOREGROUND | WS_POPUP |
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 możesz zawsze znalezć na stronie
www.4programmers.net",
a, 37, 11, 221, 20
DEFPUSHBUTTON "Instaluj", 102, 101, 100, 101, 14
PUSHBUTTON "Wyjście", 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 ręcznie raczej nie ma sensu ? ja używałem pakietu Microsoft Visual Studio, który oferuje tworzenie
zasobów. Ty jednak możesz skorzystać z darmowych narzędzi dostępnych w Internecie.
Wykorzystanie zasobów
Ażeby skorzystać z naszych zasobów, będziemy zmuszeni zastosować funkcje WinAPI, których do tej pory nie
używaliśmy ? np. LoadResource i FindRecource. Najpierw jednak należy zająć się przedstawieniem formularza
instalacyjnego.
Wyświetlenie formularza
Za wyświetlenie formularza znajdującego się w zasobach odpowiada funkcja DialogBox:
function DialogBox(hInstance: HINST; lpTemplate: PChar;
hWndParent: HWND; lpDialogFunc: TFNDlgProc): Integer;
Pierwszy parametr musi być wskazaniem modułu, w którym znajdują się zasoby ? my w tym miejscu podajemy
wartość hInstance. Drugi parametr musi być nazwą szablonu, który chcemy wyświetlić. Parametr trzeci
(hWndParent) stanowi uchwyt do okna macierzystego; jako że nasze okno będzie oknem macierzystym,
wpisujemy w tym miejscu cyfrę 0. Ostatni parametr to wskazanie procedury, która będzie obsługiwać zdarzenia
naszego formularza:
DialogBox(hInstance, 'LICENCJA', 0, @DlgWindowProc);
Funkcja DlgWindowProc, którą podałem w ostatnim parametrze, jest zwykłą funkcją okienkową ? musi
odpowiadać na przychodzące do aplikacji komunikaty.
Zamknięcie okna (zakończenie działania aplikacji) zrealizujemy za pomocą polecenia EndDialog:
15 z 18 2009-03-14 15:43
Delphi :: Kompendium :: Rozdział 12 - 4programmers.net http://4programmers.net/Delphi/Kompendium/Rozdzia%C5%82_12
EndDialog(wnd,0);
Ustawianie wartości komponentów formularza
Mimo że formularz wykorzystany w programie jest jedynie skryptem, posiada on także komponenty. Podczas
ustawiania tych komponentów przydzieliłem każdemu z nich osobny identyfikator (ID), do którego będziemy
się odwoływać podczas wykonywania funkcji:
SetDlgItemText(wnd, 106, 'C:\Mailboxes 1.53');
Przykładowo funkcja SetDlgItemText powoduje ustawienie nowej wartości kontrolki. W pierwszym parametrze
wpisujemy uchwyt okna, a w drugim ID kontrolki. Parametr ostatni to tekst, który ma zostać umieszczony w
obiekcie.
LockResource, LoadResource, FindResource
Załadowanie zasobów (tekstu licencji) do kontrolki może się wydać trochę skomplikowane, gdyż trzeba się
posłużyć aż trzema funkcjami naraz: LockResource, LoadResource i FindResource.
SetDlgItemText(wnd, 101,
LockResource(LoadResource(hinstance,FindResource(hinstance,'LICENCJA','TXT'))));
Pierwsza funkcja ? LockResource ? zwraca wskaznik do pierwszego bajtu zasobów, jednak wymaga podania
uchwytu do zasobów. Ten globalny uchwyt jest natomiast zwracany przez funkcję LoadResource, która w
drugim parametrze wymaga podania wskazania ładowanego zasobu. Tutaj pomoże nam funkcja FindResource,
w której parametrach wystarczy podać typ oraz nazwę zasobu.
Zapisywanie plików na dysku
Chcąc zastąpić klasę TResourceStream, jesteśmy zmuszeni skorzystać z funkcji LoadResource oraz
FindResource. Najpierw w swoim instalatorze zadeklaruj tablicę plików, które 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, który ma zostać utworzony na dysku; drugi to wskazanie nazwy
zasobu. Instalacja (wyodrębnianie) poszczególnych elementów może wyglądać tak:
For I:= Low(Tablica) to High(Tablica) do
begin
AssignFile(Ouff, PC + Tablica[i].Files); // stwórz plik...
Rewrite(Ouff, 1);
{ odnajdz w zasobach zasób i przypisz go zmiennej Fres }
Fres := FindResource(hInstance, Tablica[i].FName, RT_RCDATA);
{ do pliku zapisz dane z wyciągniętych zasobów }
BlockWrite(Ouff, LockResource(LoadResource(hInstance, Fres))^, SizeofResource(hinstance,
Fres));
Closefile(ouff); // zamknij plik
end;
Posłużyłem się tutaj funkcjami operującymi na plikach, które szczegółowo omówiłem w rozdziale 7. Po
zlokalizowaniu konkretnego zasobu (FindResource) następuje jego zapisanie do pliku (BlockWrite). Pełny kod
zródłowy znajduje się w listingu 12.6.
Listing 12.6. Kod zródłowy instalatora
{
Copyright (c) 2001 by Adam Boduch [http://programowanie.of.pl]
}
program setup;
uses
Windows,
Messages;
{$R FILES.RES} // <--- pliki, które 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, który 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'),
16 z 18 2009-03-14 15:43
Delphi :: Kompendium :: Rozdział 12 - 4programmers.net http://4programmers.net/Delphi/Kompendium/Rozdzia%C5%82_12
(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;
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 ścieżki, w której ma zostać zainstalowany program }
GetDlgItemText(wnd, 106, Buff, SizeOf(Buff));
PC := Buff;
{ sprawdzenie, czy na końcu 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 wartości }
RegCreateKey(KeyHandle, 'Mailboxes', KeyHandle);
RegSetValueEx(KeyHandle, 'DisplayName', 0, REG_SZ, Name, SizeOf(Name));
RegSetValueEx(KeyHandle, 'UninstallString', 0, REG_SZ, Uninstall, SizeOf(Uninstall));
{ sprawdzenie, czy użytkownik nie wpisał tylko litery partycji ? jeżeli nie, należy
utworzyć dodatkowo katalog }
if Length(PC) <= 3 then goto Next else CreateDirectory(PChar(PC), nil);
Next:
{ instaluj poszczególne elementy }
For I:= Low(Tablica) to High(Tablica) do
begin
AssignFile(Ouff, PC + Tablica[i].Files); // stworz plik...
Rewrite(Ouff, 1);
{ odnajdz w zasobach zasób i przypisz go do zmiennej Fres }
Fres := FindResource(hInstance, Tablica[i].FName, RT_RCDATA);
{ do pliku zapisz dane z "wyciągniętych" zasobów }
BlockWrite(Ouff, LockResource(LoadResource(hInstance, Fres))^,
SizeofResource(hinstance, Fres));
Closefile(ouff); // zamknij plik
end;
MessageBox(0, 'Instalacja przebiegła pomyślnie!', ':)', MB_OK + MB_ICONINFORMATION);
PostQuitMessage(0); // zakończ program...
end;
end;
else result:=false;
end;
end;
begin
if MessageBox(0, 'Za chwilę odbędzie 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 może być niewygodne, a nawet dość trudne, ale nie da się ukryć, że
zapewnia większą kontrolę nad programem i daje większe możliwości, których niekiedy brak w bibliotece VCL.
Załączniki:
17 z 18 2009-03-14 15:43
Delphi :: Kompendium :: Rozdział 12 - 4programmers.net http://4programmers.net/Delphi/Kompendium/Rozdzia%C5%82_12
Listingi_12.zip (599.11 kB)
Więcej informacji
Delphi 2005. Kompendium
programisty
Adam Boduch
Format: B5, stron: 1048
oprawa twarda
Zawiera CD-ROM
« Aplikacje sieciowe Spis treÅ›ci COM i ActiveX
© Helion 2003. Autor: Adam Boduch. Zabrania siÄ™ rozpowszechniania tego tekstu bez zgody autora.
Kategoria: Kompendium
Ostatnia modyfikacja 20-06-2006 11:36 Ostatni autor Coldpeer
Ilość wyświetleń 17378 Wersja 2
Dodaj komentarz
18 z 18 2009-03-14 15:43


Wyszukiwarka

Podobne podstrony:
Delphi Kompendium Roz2
Delphi Kompendium Roz10
Delphi Kompendium Roz14
Delphi Kompendium Roz5
Delphi Kompendium Roz8
Delphi Kompendium Roz6
Delphi 2005 Kompendium programisty
Kompendium cz 2
Delphi i PHP
Delphi Leksykon kieszonkowy?lplk
kompendium
Toczenie Kompensacja zaokrÄ…glenia ostrza
kompendium

więcej podobnych podstron