R03-04.DOC

(281 KB) Pobierz
Szablon dla tlumaczy

Rozdział 3.

Obsługa komunikatów Windows

Pomimo iż Delphi w dużym stopniu uwalnia programistę od operowania komunikatami Windows — na rzecz bardziej wygodnego mechanizmu zdarzeń — to jednak warto po­święcić komunikatom nieco uwagi, zwłaszcza w kontekście ich związku ze zdarzeniami Delphi. Dla projektanta nowych komponentów gruntowna znajomość natury komunikatów i zasad za­rządzania nimi jest wręcz niezbędna; dla projektantów posługujących się go­towymi komponentami może być przydatna, z tego względu, iż bez­pośrednie operowanie komunikatami bywa niekiedy koniecznością, bo nie wszyst­ko da się wykonać za pomocą standardowych mechanizmów Delphi. W niniejszym rozdziale opiszemy pokrótce system komunikatów Win32 i zasady ich obsługi w Object Pascalu, zajmiemy się także ich związkiem ze zdarzeniami Delphi.

 

Wskazówka

Komunikaty są mechanizmem specyficznym dla Windows i nie znajdują zastosowania w aplikacjach międzyplatformowych (CLX). Szczegółowe informacje na temat aplikacji międzyplatformowych zawarte są w rozdziale 13.

 

Natura komunikatów

Komunikaty Windows stanowią odzwierciedlenie — w postaci odpowiednich struktur danych i procedur — pewnych szczególnych sytuacji: naciśnię­cia klawisza, kliknięcia, przesunięcia myszy, upłynięcia odcinka czasu itp.

Strukturą danych ucieleśniającą komunikat jest rekord zawierający informację o rodzaju zdarzenia oraz dane niosące informację dodatkową; rzeczownik „zdarzenie” należy tu rozumieć w znaczeniu potocznym, nie w sensie zdarzeń Delphi czy zdarzeń Win32. W kategoriach Object Pascala rekord ten, w najbardziej podstawowej formie, ma następującą strukturę:

Type

  TMsg = packed record

 

    // uchwyt okna, do którego komunikat jest adresowany

    hwnd: HWND;   

 

    // identyfikator komunikatu

    message: UINT; 

 

    // dwa 32­–bitowe pola zawierające dodatkową informację 

    wParam: WPARAM;

    lParam: LPARAM;

   

    // czas utworzenia komunikatu

    time: DWORD;

 

    // pozycja kursora myszy w momencie utworzenia komunikatu

    pt: TPoint;

 

  end;

Powyższa definicja znajduje się w module Windows.pas, a znaczenie poszczególnych jej pól jest następujące:

·         hwnd — 32-bitowy uchwyt okna, do którego komunikat jest adresowany; z każdą okienkową kontrolką Delphi związane jest okno, którego uchwyt przechowywany jest pod właściwością Handle.

·         message — stała symboliczna klasyfikująca komunikat; oprócz stałych predefiniowanych w module Windows.pas możliwe jest definiowanie własnych komunikatów.

·         wParam — zawartością tego pola jest najczęściej stała stowarzyszona z komunikatem; może ono również zawierać uchwyt okna źródłowego lub numer identyfikacyjny kontrolki związanej z treścią komunikatu.

·         lParam — pole to zawiera najczęściej dodatkowe dane o zdarzeniu, bądź indeks, czy wskaźnik do określonej struktury danych w pamięci. Jako że pola wParam i lParam są 32-bitowe, możliwe jest ich rzutowanie na dowolny typ wskaźnikowy.

·         time — zawiera czas utworzenia rekordu związanego z komunikatem.

·         pt — zawiera położenie kursora myszy (we współrzędnych ekranowych) w momencie, gdy utworzono rekord związany z komunikatem.

 

Po zapoznaniu się z ogólną strukturą komunikatu, zobaczmy teraz, jakie typy komunikatów można napotkać w Win32.

Typy komunikatów

Dla każdego standardowego komunikatu Windows, Delphi (w module Messages.pas) defi­niuje charakterystyczną stałą symboliczną, określającą jedną z wartości pola message rekordu TMsg. Każda z tych stałychsymbolicznych rozpoczyna się od liter WM_ (Win­dows Message). Zestawienie najczęściej spotykanych komunikatów zawiera tabela 3.1.

 

Tabela 3.1. Najczęściej występujące komunikaty Windows

 

Identyfikator komunikatu

Wartość

Znaczenie

WM_ACTIVATE

$0006

Okno docelowe staje się aktywne lub przestaje być aktywne.

WM_CHAR

$0102

Naciśnięto i zwolniono klawisz; komunikat ten występuje łącznie z parą komunikatów WM_KEYDOWNWM_KEYUP .

WM_CLOSE

$0010

Okno docelowe powinno zostać zamknięte.

WM_KEYDOWN

$0100

Naciśnięto klawisz.

WM_KEYUP

$0101

Zwolniono klawisz.

WM_LBUTTONDOWN

$0201

Naciśnięto lewy przycisk myszy.

WM_MOUSEMOVE

$0200

Przesunięto kursor myszy.

WM_PAINT

$000F

Okno docelowe powinno odświeżyć
swój obszar klienta (client area).

WM_TIMER

$0113

Wystąpiło zdarzenie zegarowe.

WM_QUIT

$0012

Należy zakończyć aplikację.

 

 

Jak funkcjonuje system komunikatów Windows?

 

Z obsługą komunikatów Windows związane są trzy kluczowe elementy:

·         Kolejka komunikatów (message queue) system Windows utrzymuje oddzielną kolejkę ko­munikatów dla każdej aplikacji; za pobieranie komunikatów z tej kolejki i ich obsługę odpowiedzialna jest aplikacja.

·         Pętla obsługi komunikatów (message loop) centralną częścią każdej aplikacji Windows jest pętla pobierająca cyklicznie komunikaty z kolejki aplikacji i kierująca je do właściwych okien.

·         Procedura okienkowa (window procedure) — jest to procedura związana z konkretnym oknem, zajmująca się obsługą komunikatów kierowanych do tego okna; jest wywoływana w trybie odwołania zwrotnego (callback), a rezultatem jej działania jest najczęściej zapisanie informacji zwrotnej w otrzymanym rekordzie komunikatu.

Notatka

Odwołanie zwrotne polega na asynchronicznym wywołaniu (przez system operacyjny) procedury lub funkcji stanowiącej część aplikacji; dokładniej zajmiemy się tym zagadnieniem w rozdziale 6.

„Koleje życia” typowego komunikatu przedstawiają się więc następująco:

  1. W systemie występuje określone zdarzenie.
  2. System dokonuje klasyfikacji zdarzenia, tworzy reprezentującą je strukturę danych i umieszcza ją w kolejce związanej z aplikacją, której zdarzenie dotyczy.
  3. Aplikacja odczytuje wspomnianą strukturę z kolejki i formuje na jej podstawie rekord reprezentujący komunikat.
  4. Aplikacja przekazuje rekord komunikatu do właściwej procedury okienkowej
  5. Procedura okienkowa wykonuje działania specyficzne dla otrzymanego komunikatu.

 

Powyższy scenariusz jest przedstawiony schematycznie na rysunku 3.1. Kroki 3. i 4. realizują to, co przed chwilą nazwaliśmy pętlą ob­sługi komunikatu. Pętla taka jest charakterystyczna dla każdego programu Windows, gdyż cała jego praca sprowadza się do właściwego reagowania na zdarzenia zewnętrzne, czyli w konse­kwencji — na komunikaty Windows. Może się oczywiście zdarzyć tak, że kolejka komunika­tów jest pusta i działanie programu zostaje zawieszone w punkcie 3., aż do otrzymania jakiegoś komunikatu przez aplikację.

Rysunek 3.1. Schemat funkcjonowania komunikatów Windows

Obsługa komunikatów w kategoriach Delphi

 

Biblioteka VCL w znacznym stopniu odciąża programistę od obsługi komunikatów, realizując chociażby wspomnianą pętlę pobierającą komunikaty (jest ona zaimplementowana w module Forms.pas). Delphi definiuje ponadto (w module Messages.pas) własną strukturę reprezentującą informację zawartą w komunikacie:

 

    TMessage = packed record

    Msg: Cardinal;

    case Integer of

      0: (

        WParam: Longint;

        LParam: Longint;

        Result: Longint);

      1: (

        WParamLo: Word;

        WParamHi: Word;

        LParamLo: Word;

        LParamHi: Word;

        ResultLo: Word;

        ResultHi: Word);

  end;

 

Struktura ta zawiera nieco mniej informacji niż jej pierwowzór TMsg, z którego przejmuje jedynie identyfikator komunikatu oraz parametry lParam i wParam; pozostałe pola są wykorzystywane wewnętrznie przez Delphi.

Pole Result, nie mające odpowiednika w strukturze TMsg, przeznaczone jest do prze­kazania informacji zwrotnej — jak napisaliśmy przed chwilą, procedura okien­kowa musi informować system (w ściśle określonych kategoriach) o wyniku obsługi każdego komunikatu. Po zakończeniu obsługi komunikatu przez aplikację Delphi prze­tworzy go do postaci zgodnej ze strukturą TMsg, pobierając informację zwrotną z tego właśnie pola. Powrócimy do tej kwestii w dalszym ciągu niniejszego rozdziału.

Struktury specyficzne dla różnych typów komunikatów

Udogodnienia oferowane przez Delphi nie kończą się na poziomie struktury TMessage. By uwolnić programistę od mozolnego „odcyfrowywania” informacji zawartej w po­lach lParam i wParam, Delphi oferuje rekordy o strukturze specyficznej dla określonych typów komunikatów. Oto rekord charakterystyczny dla większości komunikatów związanych z myszą:

TWMMouse = record

  Msg: Cardinal;

  Keys: Longint;

  case Integer of

 

    0: (

      XPos: Smallint;

      YPos: Smallint);

 

    1: (

      Pos: TSmallPoint;

      Result: Longint);

end;

 

Ten rekord jest dostępny także pod innymi nazwami synonimicznymi, co podkreśla jego związek z poszczególnymi komunikatami:

 

  TWMLButtonDblClk = TWMMouse;

  TWMLButtonDown   = TWMMouse;

  TWMLButtonUp     = TWMMouse;

  TWMMButtonDblClk = TWMMouse;

  TWMMButtonDown   = TWMMouse;

  TWMMButtonUp     = TWMMouse;

 

Podobne struktury zdefiniowane są dla niemal każdego standardowego komunikatu, zgodnie z jednolitą konwencją nazewniczą: po przedrostku „T” następuje nazwa komunikatu pozbawiona podkreślenia i przekształcona na charakterystyczną dla Pascala notację „wielbłądzią” — na przykład komunikatowi WM_SETFONT odpowiada struktura o nazwie TWMSetFont.

Nic oczywiście nie stoi na przeszkodzie, by zrezygnować z tych udogodnień i posługiwać się uniwersalnym rekordem TMessage, przydatnym dla każdego komunikatu.

Przetwarzanie komunikatów

Jak przed chwilą stwierdziliśmy, przetworzenie komunikatu przez aplikację polega na skie­rowaniu go do właściwej procedury okienkowej; ta dokonuje jego mozolnej kla­syfikacji, interpretacji zawartej w nim informacji, po czym następuje jego właściwa ob­sługa i zwrotne przekazanie wyniku tej obsługi. Również w tym przypadku Delphi stwarza niebagatelne udogodnienie, g...

Zgłoś jeśli naruszono regulamin