Piątek 11 Kwiecień 2025r. Godz 00:00:00      
Postów: 251      

WinApi - Lekcja 1

Pewnie się zastanawiacie co to jest to WinAPI i do czego służy? :) Otóz jest to technika programowania polegająca na NIE wykorzystywaniu komponentów, czy nawet formularzy! Wszystko piszę się ręcznie. Nie kładziesz komponentów. Chcielismy podkreslic, ze mozna pisac w API w Pascalu, a nie tylko w C. Nie bedzie Ci potrzebna specjalna wiedza na temat Delphi. Wystarczy, ze orientujesz sie w Pascalu. W tym kursie Delphi bedziemy traktowali jako bardziej rozbudowanego Pascal'a. Tak wiec do tego poradnika nie bedzie potrzbna znajomosc projektowania wizualnego.

Najpierw pierwszy program w WinAPI. Jeżeli masz uruchomione Delphi to zamknij formularz, edytor kodu. Możesz także zamknąć okno Inspektora Obiektów - do niczego Ci się nie przyda. Teraz z menu "Project" wybierz "View Source" ( albo "View" -> "Project Source" w D2 ). Twoim oczom ukazał się plik główny projektu. Żeby napisać najprostszy program w API doprowadź go do takiej postaci:


program Project1;

uses
  Windows;

begin
  MessageBox(0, 'Hello World! Mój pierwszy program w WinAPI!', 'Program', MB_OK);
end.

Ale długi ten program, co? :) Na początek zadeklarowany został moduł "Windows". Jest to najważniejszy moduł jaki będziesz wykorzystywał. Następnie pomiędzy liniami "begin" i "end" wposujesz kod programu. W tym wypadku wyświetlone zostanie okienko. Teraz sprawdź ile taki program zajmuje? No? Ile? Na Delphi 5 zajmuje 16 kB! A ile w Delphi 2? U mnie w Delphi 2 zajmuje 7 kB! Czyż to nie wspaniałe :))
Podst. narzędziem podczas pisania w WinAPI jest help. Tak, tak help. To w nim szukajcie informacji na temat takiej, czy innej funkcji. Dalej: moduły. Moduły znajdują się w Twoim katalogu z Delphi. Tam możesz szukać informacji o rodzajach komunikatów mogących mieć zastosowanie w Twojej aplikacji. Najczęściej w WinAPI będziesz korzystać z komunikatów. Jeżeli nie rozumiesz istoty komunikatów nie będziesz mógł pisać w API - wierz mi :) Z drugiej jednak strony możesz się nauczyć komunikatów pisząć w WinAPI :)) Tak czy inaczej zapraszam do lektury kursu WinAPI. Tego nie znajdziesz w żadnej książce o programowaniu ( ja przynajmniej z taką się nie spotkałem :)). Jeżeli będziesz umiał programowac w WinAPI to nic nie stanie Ci na przeszkodzie. Każda bariera będzie dla Ciebie do pokonania... No, ale dość już tych rozważań. Pierwszy powazniejszy program będzie wyświetlał forme. Tylko pusta forma:


program pierwszy;

uses
  Windows,
  Messages;

const
  szAppName: PChar = 'jwPierwszy';

function FunkcjeOkna(hOkna: HWND; uMsg: UINT; wPar: WPARAM; lPar: LPARAM): LRESULT; stdcall;
begin // funkcja przechwytujaca komunikaty na oknie...
  Result := 0;
  case uMsg of
    WM_DESTROY: PostQuitMessage(0); // przy probie zamkniecia formy zamknij program
    else Result := DefWindowProc(hOkna, uMsg, wPar, lPar);
  end;
end;

var
  Wnd: TWndClass;  // klasa okna
  Msg: TMsg;

begin
  with Wnd do
  begin
    lpfnWndProc := @FunkcjeOkna; // funkcja obslugujaca operacje na oknie
    hInstance := hInstance; // uchwyt do zasobow
    lpszClassName := szAppName; // klasa
    hbrBackground := COLOR_WINDOW; // kolor tła
  end;

  RegisterClass(Wnd); // zarejestruj nowa klase

// stworz forme...
  CreateWindow(szAppName, 'Program pierwszy w WinAPI',
 WS_VISIBLE or WS_TILEDWINDOW,
        20, 20, 320, 120,
 0, 0, hInstance, NIL);

  while GetMessage(msg, 0, 0, 0) do DispatchMessage(msg);
end.

Czy to dużo kodu? Hmm, czy ja wiem. Na samym początku funkcja okienkowa. Ta funkcja to glowna czesc wykonawcza kazdego programu. Praktycznie poszczegolne programy roznia sie ta funkcja, reszta jest taka sama lub wymaga tylko niewielkich zmian. Adres tej funkcji jest podawany systemowi Windows podczas rejestracji klasy i dzieki temu system moze informowac nasz program o potencjalnie interesujacych zdarzeniach wywolujac funkcje okienkowa. ( np. naciśnięcie klawisza, ruch myszką itp. ).
ZAPAMIETAJ: funkcja okienkowa jest zwiazana z klasa okien; wszystkie okna tej samej klasy maja wspolna funkcje okienkowa. Windows wymaga aby funkcja ta byla zdefiniwana z derektywa 'stdcall'  - standardowe wywolanie. Oznacza ono, ze parametry sa przekazywane przez stos od konca (od prawej do lewej strony - jak w C - ale
zdejmowane ze stosu przez funkcje wywolywana - jak w Pascalu. Stos to obszar pamięci rezerwowany dla programu w czasie jego uruchamiania.
Parametry funkcji okienkowej:
hOkno: HWND; to uchwyt okna, dla ktorego przeznaczony jest komunikat.
uKomunikat: UINT; (Integer bez znaku) kod meldunku. Nazwy stalych reprezentujace kody meldunkow zaczynaja sie od WM_  i sa zdefiniowane w pliku "Messages.pas".
wParametr: WPARAM; nazwa pochodzi stad, ze w Win 16-bitowych byl typu Word (16-bitowy) a teraz jest 32-bitowy - zawiera dodatkowe dane zaleznie od meldunku.
lParametr: LPARAM; nazwa wskazuje, ze poprzednio byl typu Longint (32-bitowy) i teraz tez taki jest - dodatkowe rozne dane.

Typ funkcji, formalnie LRESULT (praktycznie 32-bitowy Longint).

UWAGA: Jesli funkcja okienkowa przetwarza meldunek to powinna zwrocic zero, jesli meldunku nie przetwarza, musi wywolac funkcje DefWindowProc z tymi samymi parametrami, ktore dostala i zwrocic wynik zwrocony przez DefWindowProc.

Teraz należy zarejestrować klasę okna głównego.Aby zarejestrowac w systemie Windows nowa klase nalezy wypelnic pola rekordu typu TWndClass jak to widac w przykladzie. Najwazniesze pola to:

lpfnWnddClass - adres funkcji okienkowej

hInstance - do ktorego trzeba skopiowac zawartosc globalnej zmiennej o tej samej nazwie, zawiera ona uchwyt naszego programu przydzielony przez system egzemplarza programu, bo jesli uruchomimy program ponownie to druga kopia bedzie miala w systemie inny uchwyt).

lpszClassName - nazwa klasy, na ktora bedziemy sie powolywac tworzac okno.

Pozostale pola oznaczaja:

Style - styl, moze byc kombinacja (przez OR) nastepujacych i jeszcze innych:
 CS_HREDRAW - okno jest przemalowane, gdy zmini sie jego szerokosc,
 CS_VREDRAW - -- ,, -- -- ,, --  wysokosc,
 CS_DBLCLKS - okno otrzymuje meldunek o podwojnym kliknieciu myszka;
  zwroc uwage na przedrostek CS_ (Class Style)

hIcon, hCursor, hbrBackgrund - uchwyty odpowiednio:
- ikony wyswietlanej w okienku jakie widac po nacisnieciu Alt+Tab tzn. duza
  ikona 32x32. W sytemie Win32 mozna zdefinowac jeszcze ikone mala 16x16
  uzywana z lewej strony paska tytulu i na pasku zadan. Jesli mala ikona
  nie zostanie zdefiniowana Windows uzyje odpowiednio zmniejszonej duzej.
- kursora myszki, gdy znajduje sie on w obrebie pola roboczego okna,
- pedzla (koloru i wzorku) zastosowanego do zamalowania obszru roboczego.
Mozesz korzystac ze standardowych kursorow i ikonek:

Ikony: IDI_APPLICATION, IDI_HAND (IDI_ERROR), IDI_QUESTION, IDI_EXCLAMATION
  (IDI_WARNING), IDI_ASTERISK (IDI_INNFORMATION), IDI_WINLOGO;

Kursory:IDC_ARROW,IDC_IBEAM,IDC_WAIT,IDC_CROSS,IDC_UPARROW,IDC_SIZE,IDC_ICON,
 IDC_SIZENWSE,IDC_SIZENESW,IDC_SIZEWE,IDC_SIZENS,IDC_SIZEALL,IDC_NO,
 IDC_APPSTARTING,IDC_HELP.

Windows dysponuje niewielkim skladzikiem podrecznych narzedzi. Pobiera sie z niego funkcja GetStockObject, jako parametr nalezy podac o co nam chodzi a jako wartosc funkcji dostajemy uchwyt obiektu. Co jest na tym skladziku? Niewiele. Oto wykaz mozliwych wartosci parameru funkcji GetStockObject:

Pedzle: WHITE_BRUSH, LTGRAY_BRUSH, GRAY_BRUSH, DKGRAY_BRUSH,
             BLACK_BRUSH, NULL_BRUSH (HOLLOW_BRUSH).

Piorka: WHITE_PEN, BLACK_PEN, NULL_PEN

Czcionki:        OEM_FIXED_FONT, ANSI_FIXED_FONT, ANSI_VAR_FONT, SYSTEM_FONT,
             DEVICE_DEFAULT_FONT, DEFAULT_PALETTE, SYSTEM_FIXED_FONT,
             DEFAULT_GUI_FONT

NULL_PEN, NULL_BRUSH oznacza niewidoczny (przezroczysty, brak).

Później następuje tworzenie okna głównego. Do tworzenie okien w sytemie Windows sluzy funkcja CreateWindow. Dostarcza ona, oczywiscie, uchwytu utworzonego okna. Gdyby sie to nie udalo to otrzymamy zero. Parametrow ma dosc duzo i zostaly one szczegolowo opisane w komentarzach.
Kazde okno musi powstac na podstawie zarejestrowanej klasy i pierwszym parametrem jest wlasnie nazwa klasy. Parametr: Style informuje o szczegolowych ce chach i wygladzie okna. Nazwy stalych dotyczacych stylow okna zdefiniowane w "Windows.pas" zaczynaja sie od WS_ (Window Style). Jest ich dosc duzo i moga byc ieszane przy pomocy OR. Niektore z nich:

WS_OVERLAPPED - okno glowne, stanowiace tlo dla innych okien
WS_POPUP - okno nakladane, np. dialogowe
WS_CHILD - okno potomne, ktore nie moze wyjsc poza okno rodzicielskie
WS_CAPTION - okno ma pasek tytulu
WS_SYSMENU - okno ma menu systemowe
WS_MINIMIZEBOX - okno ma przycisk zwijania do paska zadan
WS_MAXIMIZEBOX - okno ma przycisk rozwijania na caly ekran
WS_VISIBLE - okno jest widoczne
WS_HIDE - okno ukryte, niewidoczne
WS_DISABLED - okno nieaktywne, nie reaguje na nic tzn nie otrzymuje meldunkow
WS_BORDER - oko posiada ramke
WS_THICKFRAME - gruba ramka pozwalajaca myszka zmieniac rozmiary
WS_VSCROLL, WS_HSCROLL - okno posiada paski przewijania: pionowy, poziomy
itd. itd. ... o innych parametrach możesz poczytać w systemie pomocy.

Bardzo uzyteczne sa nastepujace kombinacje stylow:
WS_OVERLAPPEDWINDOW = (WS_OVERLAPPED or WS_CAPTION or WS_SYSMENU or
    WS_THICKFRAME or WS_MINIMIZEBOX or WS_MAXIMIZEBOX);
typowy dla okna glownego aplikacji.

Warto znac jeszcze style:
WS_POPUPWINDOW = (WS_POPUP or WS_BORDER or WS_SYSMENU);
WS_CHILDWINDOW = (WS_CHILD);

Po utworzeniu okna nalezy je uwidocznic funkcja ShowWindow (o ile nie bylo stylu WS_VISIBLE) i wyslac meldunek nakazujacy namalowanie funkcja UpdateWindow. Pierwszy parametr (i jedyny dla UpdateWindow) to uchwyt okna. Drugi dla ShowWindow moze byc: SW_HIDE, SW_SHOWNORMAL, SW_NORMAL, SW_SHOWMINIMIZED, SW_SHOWMAXIMIZED,SW_MAXIMIZE, SW_SHOWNOACTIVATE, SW_SHOW, SW_MINIMIZE, SW_SHOWMINNOACTIVE, SW_SHOWNA, SW_RESTORE, SW_SHOWDEFAULT, SW_MAX.
Nazwy cos tam mowia, kojarza sie z tym, co mozna wybrac z menu systemowego a te ktore sie nie kojarza poznamy jak bedzie trzeba. Najczesciej jednak do wizualizacji okna glownego stosuje sie globalna zmienna CmdShow ustawiana przez system. W Win95/98 ma ona zawsze wartosc SW_NORMAL ale Windows 3.11 przekazywal parametr uruchomienia ustawiony w Menadzerze Programow. I to tyle.... :)

Oknem w systemie Windows jest niemal wszystko co widzimy (lub nie) i zajmuje prostokatny obszar na ekranie, np. przycisk, lista rozwijana itd. Aby utworzyc okno niezbedne jest zarejestrowanie w systemie klasy okna. Klasa to taki wzorzec, "matrial genetyczny", dla tworzonych pozniej okien. Zawiera informacje o najistotniejszych wlasnosciach okna. Na podstawie zarejestrowanej klasy mozna utworzyc wiele okien. Nie zawsze nasz program musi rejestrowac klase okna. Kilka standardowyc
klas jest juz gotowych do uzytku, nazywamy je klasami predefiniowanymi.

Na samym końcu zawarte są także bardzo ważne linie. Informuja one o pętli komunikatów. Od tej pory, a wlasciwie jeszcze wczesniej (bo zanim okno sie ukaze
funkcja otrzymuje meldunek WM_CREATE), okno zasypywane jest strumieniem meldunkow tzn. wywolywana jest funkcja okienkowa z odpowiednimi kodami meldunkow i ich parametrami. Na razie obslugujemy tylko jeden, tzn. poslusznie 'niszczymy' okno gdy odbierzemy meldunek WM_DESTROY - to jest minimum co trzeba zrobic w funkcji okienkowej.  Ale zeby meldunki dotarly do funkcji okienkowej program glowny musi wejsc w tzw. petle meldunkow. Kazda aplikacja posiada tzw. kolejke meldunkow (w systemie wielozadaniowym - bo w starych Windows byla jedna kolejka systemowa). Funkcja GetMessage pobiera meldunki z kolejki i wpisuje je do rekordu 'msg' typu TMsg, ktory jest jej pierwszym parametem (pozostale parametry teraz nieistotne). Funkcja ta zwraca wynik typu logicznego BOOL (32-bitowe Boolean), ktory jest zawsze TRUE z wyjatkiem przypadku, gdy zostanie pobrany meldunek WM_QUIT. Wtedy funkcja zwroci FALSE a to oznacza wyjscie z petli WHILE i w efekcie koniec programu. A w petli jest przetwarzanie meldunku funkcja TranslateMessage (niezbedne do prawidlowego generownia meldunkow klawiaturowych) i dostarczanie (doreczanie) meldunku do funkcji okienkowej przy pomocy dyspozytora DispatchMessage. Troche to wyglada dziwnie ale gdy aplikacja ma wiele okien (jak srodowisko Delphi) to dyspozytor ma co robic.