Logowanie | Rejestracja | Forum | Pomoc | Reklama | Szukaj
Strona główna :: Delphi :: Kompendium
Edytuj
Historia
Rozdział 12
WinAPI
Nie sposób było nie wspomnieć w tej książce o WinAPI. Z tym terminem zetknąłeś się już wielokrotnie podczas
lektury niniejszej publikacji. Teraz chciałbym omówić ten temat nieco dogłębniej. Nie jest możliwe bowiem
zawarcie wszystkich informacji dotyczących WinAPI w jednym rozdziale ? temat ten jest na tyle rozległy, że
można by mu poświęcić osobną książkę. Więcej o WinAPI możesz dowiedzieć się ze strony
http://msdn.microsoft.com
lub z systemu pomocy Delphi (znajdziesz tam dokładny opis funkcji i procedur).
Spis treści
1 Czym tak naprawdę jest WinAPI?
1.1 Zasady tworzenia programów za pomocą WinAPI
1.2 Brak zdarzeń
1.3 Brak komponentów
1.4 Zalety wykorzystania WinAPI
2 Pierwszy program
3 Funkcja okienkowa
4 Rejestracja klasy
5 Tworzenie formularza
6 Komunikaty i uchwyty
7 Łań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 Ładowanie i wyświetlanie bitmapy
13 Ładowanie 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
Wyobraź 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ń
Delphi
Artykuły
Kompendium
Gotowce
FAQ
.NET
Turbo Pascal
FAQ
PHP
FAQ
Java
FAQ
C/C++
Artykuły
FAQ
C#
Wprowadzenie
Assembler
FAQ
(X)HTML
CSS
JavaScript
Z pogranicza
Algorytmy
WIĘCEJ
»
Delphi
C/C++
Turbo Pascal
Assembler
PHP
Programy
Dokumentacja
Kursy
Komponenty
WIĘCEJ
»
Delphi :: Kompendium :: Rozdział 12 - 4programmers.net
http://4programmers.net/Delphi/Kompendium/Rozdzia%C5%82_12
1 z 18
2009-03-14 15:43
Podczas pisania programów w ?czystym? API będziemy pozbawieni wygodnego mechanizmu, jakim są
zdarzenia. Jak pamiętasz z rozdziału 5., mechanizm zdarzeń można zastąpić poprzez komunikaty Windows.
Ten moment jest więc dobrą okazją, aby cofnąć się do rozdziału 5. i przypomnieć sobie zasadę funkcjonowania
komunikatów.
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 źródłowy
projektu (pliku *.dpr) doprowadź 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 źró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.
RSS | Forum | Pastebin |
Regulamin | Pomoc | Usuń
cookies | Prawa autorskie |
Kontakt | Reklama
Copyright © 2000-2006 by Coyote Group 0.9.3-pre3
Czas generowania strony: 1.6790 sek. (zapytań SQL:
12)
Delphi :: Kompendium :: Rozdział 12 - 4programmers.net
http://4programmers.net/Delphi/Kompendium/Rozdzia%C5%82_12
2 z 18
2009-03-14 15:43
Aby program został prawidłowo skompilowany, na liście uses musi znaleźć 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:
Delphi :: Kompendium :: Rozdział 12 - 4programmers.net
http://4programmers.net/Delphi/Kompendium/Rozdzia%C5%82_12
3 z 18
2009-03-14 15:43
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 źró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
Delphi :: Kompendium :: Rozdział 12 - 4programmers.net
http://4programmers.net/Delphi/Kompendium/Rozdzia%C5%82_12
4 z 18
2009-03-14 15:43
HBITMAP
. Dzięki temu łatwo jest rozpoznać, czy dana zmienna jest uchwytem.
Łań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 źró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.
Delphi :: Kompendium :: Rozdział 12 - 4programmers.net
http://4programmers.net/Delphi/Kompendium/Rozdzia%C5%82_12
5 z 18
2009-03-14 15:43
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 źró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
Delphi :: Kompendium :: Rozdział 12 - 4programmers.net
http://4programmers.net/Delphi/Kompendium/Rozdzia%C5%82_12
6 z 18
2009-03-14 15:43
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 źró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.
Delphi :: Kompendium :: Rozdział 12 - 4programmers.net
http://4programmers.net/Delphi/Kompendium/Rozdzia%C5%82_12
7 z 18
2009-03-14 15:43
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
BS_AUTO3STATE
Flaga podobna do BS_3STATE, tyle że komponent może przybierać wartość
?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
Delphi :: Kompendium :: Rozdział 12 - 4programmers.net
http://4programmers.net/Delphi/Kompendium/Rozdzia%C5%82_12
8 z 18
2009-03-14 15:43
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
CBS_DROPDOWNLIST
Nie będzie możliwe edytowanie listy rozwijalnej (zaznaczona pozycja nie będzie
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
ES_AUTOHSCROLL
Automatycznie przewiń tekst w kontrolce w poziomie, jeżeli użytkownik wpisał więcej
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
ES_RIGHT
Tekst zostanie wyrównany do prawej strony, jeżeli kontrolka została stworzona z flagą
ES_MULTILINE
ES_UPPERCASE
Wpisane w kontrolce litery konwertuj na wielkie
Delphi :: Kompendium :: Rozdział 12 - 4programmers.net
http://4programmers.net/Delphi/Kompendium/Rozdzia%C5%82_12
9 z 18
2009-03-14 15:43
ES_WANTRETURN
Dotyczy kontrolek wielowierszowych. Po zastosowaniu tej flagi naciśnięcie klawisza Enter
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 // sprawdź, 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 źródłowy powyższego programu możesz znaleźć 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
Delphi :: Kompendium :: Rozdział 12 - 4programmers.net
http://4programmers.net/Delphi/Kompendium/Rozdzia%C5%82_12
10 z 18
2009-03-14 15:43
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 znaleźć 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.
Delphi :: Kompendium :: Rozdział 12 - 4programmers.net
http://4programmers.net/Delphi/Kompendium/Rozdzia%C5%82_12
11 z 18
2009-03-14 15:43
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 znaleźć 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 znaleźć 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.
Delphi :: Kompendium :: Rozdział 12 - 4programmers.net
http://4programmers.net/Delphi/Kompendium/Rozdzia%C5%82_12
12 z 18
2009-03-14 15:43
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.
Delphi :: Kompendium :: Rozdział 12 - 4programmers.net
http://4programmers.net/Delphi/Kompendium/Rozdzia%C5%82_12
13 z 18
2009-03-14 15:43
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
))
;
Ładowanie 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 źró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;
Ładowanie 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 znaleźć 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).
Delphi :: Kompendium :: Rozdział 12 - 4programmers.net
http://4programmers.net/Delphi/Kompendium/Rozdzia%C5%82_12
14 z 18
2009-03-14 15:43
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 znaleźć 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:
Delphi :: Kompendium :: Rozdział 12 - 4programmers.net
http://4programmers.net/Delphi/Kompendium/Rozdzia%C5%82_12
15 z 18
2009-03-14 15:43
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 wskaźnik 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
)
;
{ odnajdź 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
źródłowy znajduje się w listingu 12.6.
Listing 12.6. Kod źró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'
)
,
Delphi :: Kompendium :: Rozdział 12 - 4programmers.net
http://4programmers.net/Delphi/Kompendium/Rozdzia%C5%82_12
16 z 18
2009-03-14 15:43
(
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
)
;
{ odnajdź 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:
Delphi :: Kompendium :: Rozdział 12 - 4programmers.net
http://4programmers.net/Delphi/Kompendium/Rozdzia%C5%82_12
17 z 18
2009-03-14 15:43
« Aplikacje sieciowe
Spis treści
COM i ActiveX »
Listingi_12.zip
(599.11 kB)
Więcej informacji
Delphi 2005. Kompendium
programisty
Adam Boduch
Format: B5, stron: 1048
oprawa twarda
Zawiera CD-ROM
©
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
Delphi :: Kompendium :: Rozdział 12 - 4programmers.net
http://4programmers.net/Delphi/Kompendium/Rozdzia%C5%82_12
18 z 18
2009-03-14 15:43