I. Wstęp
Witaj w części IX kursu programowania w języku Pascal dla początkujących. Opiszę tutaj podstawy programowania w systemie Windows, co ma służyć jako wprowadzenie i przygotowanie do bardziej zaawansowanego programowania. Niestety, aby tworzyć profesjonalne oprogramowanie należało będzie przejść na bardziej zaawansowany język programowania. Posiadając wiedzę z tego kursu bez problemu możesz startować do nauki C, C++, C# czy Java, a jeżeli nie chcesz rozstawać się z Pascalem możesz zainteresować się środowiskiem Delphi. Wszystko zależy od tego jakie programy chcesz pisać. Delphi oparte na odmianie języka Pascal nazwaną Object Pascal daje możliwości szybkiego i wygodnego tworzenia aplikacji, jednak (zależy to jeszcze od użytych technik) programy tworzone w tym środowisku nie są najwyższej jakości, jednak jest ono wystarczające do pisania prostych programów użytkowych dla systemu Windows.
II. Pierwszy program
Tradycyjnie, najpierw program Hello World!. Oto kod programu, który zaraz opiszę:
Budowa programu jest taka jak poprzednich, jednak widzimy tutaj, że używany jest moduł o nazwie Windows. Zawiera on różne funkcje (nazywane funkcjami Windows API, w skrócie WinAPI), stałe i struktury systemu Windows. W kodzie głównym programu widać wywołanie funkcji MessageBox. Jeżeli skompilowałeś i uruchomiłeś powyższy kod zauważyłeś, że wyświetlone zostało okno informacyjne. To właśnie wykonuje ta funkcja. Wyświetla ono predefiniowane okno informacyjne o podanej przez nas treści, tytule, rodzaju przycisków i ikonie. Funkcja ta znajduje się w bibliotece systemowej (ta znajduje się dokładnie w bibliotece user32.dll) i jest gotowa do użycia. Taki gotowy do użycia zestaw różnych funkcji bardzo ułatwia programowanie w Windows.
Teraz jeszcze jedna rzecz. Funkcje jak pamiętasz, różnią się od procedur tym, że zwracają wartość po wykonaniu jakiejś czynności. Ta funkcja też zwraca wartość. Z dokumentacji można się dowiedzieć, że zwraca ona wartość wciśniętego przez użytkownika przycisku.
Spójrz na poniższy kod:
W komunikacie wyświetlanym przez powyższy program zmienił się zestaw przycisków. Jako ostatni argument podana została stała MB_YESNO, która mówi, że wyświetlony komunikat ma mieć przyciski Tak i Nie. Wartość zwracana przez funkcję przypisywana jest do zmiennej R. Następnie instrukcją porównania sprawdzane jest czy zwrócona wartość równa jest stałej o nazwie IDYES (stała ta jest zadeklarowana w module Windows). Jeżeli zwrócona wartość jest równa tej stałej, to znaczy, że użytkownik kliknął przycisk Tak. W przeciwnym wypadku kliknął Nie.
Jak już pisałem, możliwe do ustawienia przyciski i ikony opisane są w dokumentacji.
III. Okna, klasy okien, komunikaty
Teraz coś trudniejszego. Wyświetlimy okno, ale nie predefiniowane, tylko zarejestrujemy własną klasę okna. Kod poniżej może wydawać się długi i zniechęcający, ale po przeanalizowaniu tego wszystkiego, układa się to w logicznie działający mechanizm. W wielu programach okienkowych widziałeś zapewne różne przyciski, pola tekstowe, suwaki, paski postępu itp. Tak naprawdę to też są okna, tylko nam się przyjęło nazywać je tak. Dokładnie mówiąc są to okna potomne, a okno główne programu jest oknem nadrzędnym/oknem rodzica. Cała zasada działania opiera się na tym, że do okien wysyłane są wiadomości, a my w naszym programie sprawdzamy instrukcją warunkową jaka wiadomość została wysłana do okna i możemy napisać kod, który wykona się po otrzymaniu tej wiadomości. Takie wiadomości są przesyłane, również do kontrolek i analogicznie się je obsługuje.
Sprójrz na poniższy kod wyświetlający proste okno:
Na początku programu widzimy dyrektywę dla kompilatora. Nie jest to kod programu, ta dyrektywa służy tylko kompilatorowi, aby poinformować go, że program będzie programem okienkowym ({$APPTYPE GUI}) i, żeby ustawił kompatybilność ze środowiskiem Delphi ({$MODE DELPHI}). Krótko opisując, powyższy program rejestruje nową klasę okna funkcją RegisterWindow, tworzy okno funkcją CreateWindow i wyświetla je funkcją ShowWindow, po której następuje odświeżenie obszaru roboczego okna ("wnętrza okna") funkcją UpdateWindow.
W opisie powyższego kodu pojawiło się wiele nazw funkcji WinAPI. Opis wszystkich funkcji możesz znaleźć na stronie MSDN (Microsoft Software Developer's Network). Opis niektórych funkcji Windows API możesz znaleźć na tej stronie (coder.org.pl) w dziale WinAPI/Funkcje. Niestety nie ma możliwości opisania tam wszystkich funkcji, zatem musisz nauczyć się i przyzwyczaić do korzystania z dokumentacji w języku angielskim.
IV. Tworzenie kontrolek
Jak już pisałem wcześniej, że kontrolki to też okna i tworzy się je tak jak okna. Używamy do tego funkcji CreateWindowEx podając jako klasę okna, nazwę klasy kontrolki.
Oto kod programu wyświetlającego okno z przyciskiem, po naciśnięciu którego wyświetlany jest komunikat:
W powyższym programie obsłużone zostały dwie dodatkowe wiadomości: WM_CREATE i WM_COMMAND. Wiadomość WM_CREATE wysyłana jest podczas tworzenia okna, a wiadomość WM_COMMAND wysyłana jest między innymi, gdy użytkownik kliknie w jakąś kontrolkę. Gdy okno odbierze wiadomość WM_CREATE, wykonuje się wtedy kod tworzący kontrolkę przycisku. Natomiast w wiadomości WM_COMMAND sprawdzamy, czy w parametrze wParam jest numer ID przycisku, jeżeli tak to wyświetlamy komunikat.
Inne kontrolki tworzymy i obsługujemy analogicznie. Najważniejsze tutaj jest poznać zasadę działania, wtedy potrafimy sobie poradzić mając do dyspozycji tylko dokumentację.
V. Okno dialogowe z zasobów
Jeżeli nie chcemy tworzyć okna i kontrolek dynamicznie (w czasie działania programu), możemy umieścić skrypt zasobów w którym zdefiniujemy wygląd okna i kontrolek. Skrypt taki można utworzyć dowolnym edytorem zasobów.
Mamy taki skrypt zasobów:
Zapisz go jako plik z rozszerzeniem *.rc. Musimy go teraz skompilować do pliku z rozszerzeniem *.res. W katalogu FreePascala znajdź plik GoRC.exe. Jest to kompilator zasobów. Wywołaj go z Wiersza Polecenia, aby skompilować skrypt zasobów:
Lub po prostu przeciągnij plik okno.rc nad program GoRC.exe i upuść.
Oto kod programu wyświetlającego okno dialogowe z zasobów:
Widzimy, że kod tego programu jest o wiele krótszy od poprzedniego. Nie rejestrujemy tutaj własnej klasy okna, tylko korzystamy z okna predefiniowanego (okna dialogowego). Na początku programu widać dyrektywę dla kompilatora informującą go, aby dołączył do programu podany skompilowany skrypt zasobów. Mimo, że program wyświetla okno z zasobów, nie rejestruje własnej klasy okna, zasada działania jest zachowana, jest pętla przetwarzająca komunikaty, funkcja obsługująca okno itp.
VI. Zakończenie
Na tym kończy się ten kurs. Poznałeś podstawowe zasady pisania programów, poznałeś najważniejsze instrukcje, jak tworzy się procedury i funkcje, jak programować prostą grafikę, a w tej części poznałeś podstawy programowania w Windows API. Sądzę, że po przejściu tego kursu jesteś gotowy rozpocząć naukę programowania w jakimś "bardziej zaawansowanym" języku.
Pozdrawiam i życzę sukcesów w programowaniu, Dawid '0DFh' Farbaniec
Witaj w części IX kursu programowania w języku Pascal dla początkujących. Opiszę tutaj podstawy programowania w systemie Windows, co ma służyć jako wprowadzenie i przygotowanie do bardziej zaawansowanego programowania. Niestety, aby tworzyć profesjonalne oprogramowanie należało będzie przejść na bardziej zaawansowany język programowania. Posiadając wiedzę z tego kursu bez problemu możesz startować do nauki C, C++, C# czy Java, a jeżeli nie chcesz rozstawać się z Pascalem możesz zainteresować się środowiskiem Delphi. Wszystko zależy od tego jakie programy chcesz pisać. Delphi oparte na odmianie języka Pascal nazwaną Object Pascal daje możliwości szybkiego i wygodnego tworzenia aplikacji, jednak (zależy to jeszcze od użytych technik) programy tworzone w tym środowisku nie są najwyższej jakości, jednak jest ono wystarczające do pisania prostych programów użytkowych dla systemu Windows.
II. Pierwszy program
Tradycyjnie, najpierw program Hello World!. Oto kod programu, który zaraz opiszę:
Kod:
program WinHelloWorld;
uses Windows;
begin
MessageBox(0, 'Hello World!', 'Informacja', MB_OK+MB_ICONINFORMATION);
end.
Budowa programu jest taka jak poprzednich, jednak widzimy tutaj, że używany jest moduł o nazwie Windows. Zawiera on różne funkcje (nazywane funkcjami Windows API, w skrócie WinAPI), stałe i struktury systemu Windows. W kodzie głównym programu widać wywołanie funkcji MessageBox. Jeżeli skompilowałeś i uruchomiłeś powyższy kod zauważyłeś, że wyświetlone zostało okno informacyjne. To właśnie wykonuje ta funkcja. Wyświetla ono predefiniowane okno informacyjne o podanej przez nas treści, tytule, rodzaju przycisków i ikonie. Funkcja ta znajduje się w bibliotece systemowej (ta znajduje się dokładnie w bibliotece user32.dll) i jest gotowa do użycia. Taki gotowy do użycia zestaw różnych funkcji bardzo ułatwia programowanie w Windows.
Teraz jeszcze jedna rzecz. Funkcje jak pamiętasz, różnią się od procedur tym, że zwracają wartość po wykonaniu jakiejś czynności. Ta funkcja też zwraca wartość. Z dokumentacji można się dowiedzieć, że zwraca ona wartość wciśniętego przez użytkownika przycisku.
Spójrz na poniższy kod:
Kod:
program WinHelloWorld;
uses Windows;
var
R : Integer;
begin
R := MessageBox(0, 'Wybierz: Tak lub Nie.', 'Informacja', MB_YESNO);
if R = IDYES
then MessageBox(0, 'Wybrano przycisk Tak.','Informacja',0)
else MessageBox(0, 'Wybrano przycisk Nie.','Informacja',0);
end.
W komunikacie wyświetlanym przez powyższy program zmienił się zestaw przycisków. Jako ostatni argument podana została stała MB_YESNO, która mówi, że wyświetlony komunikat ma mieć przyciski Tak i Nie. Wartość zwracana przez funkcję przypisywana jest do zmiennej R. Następnie instrukcją porównania sprawdzane jest czy zwrócona wartość równa jest stałej o nazwie IDYES (stała ta jest zadeklarowana w module Windows). Jeżeli zwrócona wartość jest równa tej stałej, to znaczy, że użytkownik kliknął przycisk Tak. W przeciwnym wypadku kliknął Nie.
Jak już pisałem, możliwe do ustawienia przyciski i ikony opisane są w dokumentacji.
III. Okna, klasy okien, komunikaty
Teraz coś trudniejszego. Wyświetlimy okno, ale nie predefiniowane, tylko zarejestrujemy własną klasę okna. Kod poniżej może wydawać się długi i zniechęcający, ale po przeanalizowaniu tego wszystkiego, układa się to w logicznie działający mechanizm. W wielu programach okienkowych widziałeś zapewne różne przyciski, pola tekstowe, suwaki, paski postępu itp. Tak naprawdę to też są okna, tylko nam się przyjęło nazywać je tak. Dokładnie mówiąc są to okna potomne, a okno główne programu jest oknem nadrzędnym/oknem rodzica. Cała zasada działania opiera się na tym, że do okien wysyłane są wiadomości, a my w naszym programie sprawdzamy instrukcją warunkową jaka wiadomość została wysłana do okna i możemy napisać kod, który wykona się po otrzymaniu tej wiadomości. Takie wiadomości są przesyłane, również do kontrolek i analogicznie się je obsługuje.
Sprójrz na poniższy kod wyświetlający proste okno:
Kod:
{$APPTYPE GUI}
{$MODE DELPHI}
program AplikacjaOkienkowa;
uses
Strings, Windows;
//zmienne globalne
var
AMessage: Msg;
hWindow: HWnd;
WindowClass: WndClass;
//funkcja obsługująca wiadomości wysyłane do okna
function WindowProc(Window: HWnd; AMessage: UINT; WParam : WPARAM;
LParam: LPARAM): LRESULT; stdcall; export;
begin
WindowProc := 0;
case AMessage of
//gdy wysłano wiadomość WM_DESTROY
WM_DESTROY: begin
//zamknij okno
PostQuitMessage(0);
Exit;
end;
end;
//wszystkie nieobsługiwane wyżej wiadomości,
//prześlij do domyślnej funkcji obsługi okna
WindowProc := DefWindowProc(Window, AMessage, WParam, LParam);
end;
//program główny
begin
//wypełnienie pól struktury WndClass
WindowClass.Style := CS_HREDRAW or CS_VREDRAW;
WindowClass.lpfnWndProc := WndProc(@WindowProc);
WindowClass.cbCl***tra := 0;
WindowClass.cbWndExtra := 0;
WindowClass.hInstance := system.MainInstance;
WindowClass.hIcon := LoadIcon(0, IDI_APPLICATION);
WindowClass.hCursor := LoadCursor(0, IDC_ARROW);
WindowClass.hbrBackground := GetStockObject(WHITE_BRUSH);
WindowClass.lpszMenuName := nil;
WindowClass.lpszClassName := 'WindowClass';
//zarejestrowanie klasy okna
RegisterClass(WindowClass);
//stworzenie okna i zapisanie zwróconego uchwytu
//do zmiennej
hWindow := CreateWindow('WindowClass', 'Proste okno',
WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT,
CW_USEDEFAULT, CW_USEDEFAULT, 0, 0, system.MainInstance, nil);
//jeżeli udało się otworzyć okno (uchwyt różny od zera)
//to wyświetl okno na ekranie
if hWindow <> 0 then
begin
ShowWindow(hWindow, CmdShow);
ShowWindow(hWindow, SW_SHOW);
UpdateWindow(hWindow);
end;
//pętla przetwarzająca wiadomości wysyłane
//do okna
while GetMessage(@AMessage, 0, 0, 0) do
begin
TranslateMessage(AMessage);
DispatchMessage(AMessage);
end;
Halt(AMessage.wParam);
end.
Na początku programu widzimy dyrektywę dla kompilatora. Nie jest to kod programu, ta dyrektywa służy tylko kompilatorowi, aby poinformować go, że program będzie programem okienkowym ({$APPTYPE GUI}) i, żeby ustawił kompatybilność ze środowiskiem Delphi ({$MODE DELPHI}). Krótko opisując, powyższy program rejestruje nową klasę okna funkcją RegisterWindow, tworzy okno funkcją CreateWindow i wyświetla je funkcją ShowWindow, po której następuje odświeżenie obszaru roboczego okna ("wnętrza okna") funkcją UpdateWindow.
W opisie powyższego kodu pojawiło się wiele nazw funkcji WinAPI. Opis wszystkich funkcji możesz znaleźć na stronie MSDN (Microsoft Software Developer's Network). Opis niektórych funkcji Windows API możesz znaleźć na tej stronie (coder.org.pl) w dziale WinAPI/Funkcje. Niestety nie ma możliwości opisania tam wszystkich funkcji, zatem musisz nauczyć się i przyzwyczaić do korzystania z dokumentacji w języku angielskim.
IV. Tworzenie kontrolek
Jak już pisałem wcześniej, że kontrolki to też okna i tworzy się je tak jak okna. Używamy do tego funkcji CreateWindowEx podając jako klasę okna, nazwę klasy kontrolki.
Oto kod programu wyświetlającego okno z przyciskiem, po naciśnięciu którego wyświetlany jest komunikat:
Kod:
{$APPTYPE GUI}
{$MODE DELPHI}
program AplikacjaOkienkowa;
uses
Strings, Windows;
//zmienne globalne
var
AMessage: Msg;
hWindow: HWnd;
WindowClass: WndClass;
//funkcja obsługująca wiadomości wysyłane do okna
function WindowProc(Window: HWnd; AMessage: UINT; WParam : WPARAM;
LParam: LPARAM): LRESULT; stdcall; export;
begin
WindowProc := 0;
case AMessage of
//gdy wysłano wiadomość WM_DESTROY
WM_CREATE: begin
//tworzymy przycisk
CreateWindowEx(0, 'button', 'Witaj!', WS_CHILD or WS_VISIBLE,
50,50,100,30,Window,1001,system.MainInstance,nil);
end;
WM_COMMAND: begin
//w parametrze wParam jest numer kontrolki
//naszemu przyciskowi daliśmy numer 1001
//więc porównujemy wParam z tą wartością
if wParam = 1001 then MessageBox(0,'Hello!','Info',0);
end;
WM_DESTROY: begin
//zamknij okno
PostQuitMessage(0);
Exit;
end;
end;
//wszystkie nieobsługiwane wyżej wiadomości,
//prześlij do domyślnej funkcji obsługi okna
WindowProc := DefWindowProc(Window, AMessage, WParam, LParam);
end;
//program główny
begin
//wypełnienie pól struktury WndClass
WindowClass.Style := CS_HREDRAW or CS_VREDRAW;
WindowClass.lpfnWndProc := WndProc(@WindowProc);
WindowClass.cbCl***tra := 0;
WindowClass.cbWndExtra := 0;
WindowClass.hInstance := system.MainInstance;
WindowClass.hIcon := LoadIcon(0, IDI_APPLICATION);
WindowClass.hCursor := LoadCursor(0, IDC_ARROW);
WindowClass.hbrBackground := GetStockObject(WHITE_BRUSH);
WindowClass.lpszMenuName := nil;
WindowClass.lpszClassName := 'WindowClass';
//zarejestrowanie klasy okna
RegisterClass(WindowClass);
//stworzenie okna i zapisanie zwróconego uchwytu
//do zmiennej
hWindow := CreateWindow('WindowClass', 'Proste okno',
WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT,
CW_USEDEFAULT, CW_USEDEFAULT, 0, 0, system.MainInstance, nil);
//jeżeli udało się otworzyć okno (uchwyt różny od zera)
//to wyświetl okno na ekranie
if hWindow <> 0 then
begin
ShowWindow(hWindow, CmdShow);
ShowWindow(hWindow, SW_SHOW);
UpdateWindow(hWindow);
end;
//pętla przetwarzająca wiadomości wysyłane
//do okna
while GetMessage(@AMessage, 0, 0, 0) do
begin
TranslateMessage(AMessage);
DispatchMessage(AMessage);
end;
Halt(AMessage.wParam);
end.
W powyższym programie obsłużone zostały dwie dodatkowe wiadomości: WM_CREATE i WM_COMMAND. Wiadomość WM_CREATE wysyłana jest podczas tworzenia okna, a wiadomość WM_COMMAND wysyłana jest między innymi, gdy użytkownik kliknie w jakąś kontrolkę. Gdy okno odbierze wiadomość WM_CREATE, wykonuje się wtedy kod tworzący kontrolkę przycisku. Natomiast w wiadomości WM_COMMAND sprawdzamy, czy w parametrze wParam jest numer ID przycisku, jeżeli tak to wyświetlamy komunikat.
Inne kontrolki tworzymy i obsługujemy analogicznie. Najważniejsze tutaj jest poznać zasadę działania, wtedy potrafimy sobie poradzić mając do dyspozycji tylko dokumentację.
V. Okno dialogowe z zasobów
Jeżeli nie chcemy tworzyć okna i kontrolek dynamicznie (w czasie działania programu), możemy umieścić skrypt zasobów w którym zdefiniujemy wygląd okna i kontrolek. Skrypt taki można utworzyć dowolnym edytorem zasobów.
Mamy taki skrypt zasobów:
Kod:
#define IDD_MAIN 1000
#define IDC_BUTTON1001 1001
IDD_MAIN DIALOGEX 10,10,237,89
CAPTION "Okno dialogowe z zasobów"
FONT 9,"Verdana"
STYLE 0x90c80804
EXSTYLE 0x00000000
BEGIN
CONTROL "Hello!",IDC_BUTTON1001,"Button",0x10010000,87,34,52,15,0x00000000
END
Zapisz go jako plik z rozszerzeniem *.rc. Musimy go teraz skompilować do pliku z rozszerzeniem *.res. W katalogu FreePascala znajdź plik GoRC.exe. Jest to kompilator zasobów. Wywołaj go z Wiersza Polecenia, aby skompilować skrypt zasobów:
Kod:
cd "C:\Program Files\FPC\bin\i386-win32\"
GoRC okno.rc
Lub po prostu przeciągnij plik okno.rc nad program GoRC.exe i upuść.
Oto kod programu wyświetlającego okno dialogowe z zasobów:
Kod:
{$APPTYPE GUI}
{$MODE DELPHI}
{$R okno.RES} //dołączamy plik zasobów z oknem dialogowym
program OknoDialogowe;
uses Windows;
var
AMessage : MSG;
function WindowProc(Window: HWnd; AMessage: UINT; WParam : WPARAM;
LParam: LPARAM): LRESULT; stdcall; export;
begin
WindowProc := 0;
case AMessage of
WM_COMMAND: begin
case WParam of
1001 : MessageBox(0,'Hello!','Info',0);
end;
end;
WM_CLOSE: begin
ExitProcess(0);
end;
end;
WindowProc := 0;
end;
begin
//Wyświetlenie okna dialogowego z zasobów
DialogBoxParam(GetModuleHandle(0),1000,0,@WindowProc,0);
//kolejka komunikatów przesyłanych do okna
while GetMessage(@AMessage, 0, 0, 0) do
begin
TranslateMessage(AMessage);
DispatchMessage(AMessage);
end;
end.
Widzimy, że kod tego programu jest o wiele krótszy od poprzedniego. Nie rejestrujemy tutaj własnej klasy okna, tylko korzystamy z okna predefiniowanego (okna dialogowego). Na początku programu widać dyrektywę dla kompilatora informującą go, aby dołączył do programu podany skompilowany skrypt zasobów. Mimo, że program wyświetla okno z zasobów, nie rejestruje własnej klasy okna, zasada działania jest zachowana, jest pętla przetwarzająca komunikaty, funkcja obsługująca okno itp.
VI. Zakończenie
Na tym kończy się ten kurs. Poznałeś podstawowe zasady pisania programów, poznałeś najważniejsze instrukcje, jak tworzy się procedury i funkcje, jak programować prostą grafikę, a w tej części poznałeś podstawy programowania w Windows API. Sądzę, że po przejściu tego kursu jesteś gotowy rozpocząć naukę programowania w jakimś "bardziej zaawansowanym" języku.
Pozdrawiam i życzę sukcesów w programowaniu, Dawid '0DFh' Farbaniec