Poprzedni program nadal nie działa tak jakbyśmy chcieli, zaraz po utworzeniu okna program się kończy(return 0), więc jedyną rzeczą jaką zobaczymy to mignięcie okna. Po co nam takie programy.

Istnieje jednak wyjście z tej sytuacji, należy nawiązać komuniakację z systemem. W tym celu musimy zrobić pętlę komunikatów, która cały czas będzie wysyłała i otrzymywała komunikaty z systemu. Właściwie to każdy program okienkowy jest w rzeczywistości pętlą.

Przebieg programu wygląda w ten sposób, że zaczyna się, odrazu na początku działania powinien się zainicjować, czyli potworzyć w pamięci wszystko co niezbęde do funkcjonowania, okna itp. Następnie program wchodzi pętlę, w której komunikuje się z systemem. Dopiero, gdy program dostanie sygnał z systemu, pętla kończy się, wykouje to co jest po pętli i zamyka się. My już mamy zainicjowane, to co jest niezbędne do dziłania programu, czyli nasze okno i teraz czas na pętlę komunikatów. Składa się ona z dwóch funkcji.

No to zaczynamy, pierwszym krokiem jest zrobienie pętli, która będzie pobierać komunikaty z systemu i je wysyłać.

#include <windows.h>

INT WINAPI WinMain(HINSTANCE hInst,HINSTANCE,LPSTR lStart,INT nShow)
{
  
//czynności rozpoczynające program (tworzenie okna)

  WNDCLASSEX wc;
  wc.hInstance=hInst;
  wc.lpszClassName=
"Klasa okna"
;
  wc.lpfnWndProc=DefWindowProc;
  wc.style=CS_DBLCLKS;
  wc.cbSize=sizeof(WNDCLASSEX);
  wc.hIcon=LoadIcon(
0
,IDI_APPLICATION);
  wc.hIconSm=LoadIcon(
0
,IDI_APPLICATION);
  wc.hCursor=LoadCursor(
0
,IDC_ARROW);
  wc.lpszMenuName=
0
;
  wc.hbrBackground=(HBRUSH)GetStockObject(WHITE_BRUSH);
  wc.cbWndExtra=
0
;
  wc.cbClsExtra=
0
;
  if(RegisterClassEx(&wc)==
0
) return
0
;
  HWND Okno=CreateWindowEx(
0
,
"Klasa okna"
,
"Tytuł okna"
,WS_OVERLAPPEDWINDOW,
50
,
50
,
100
,
100
,
0
,
0
,hInst,
0
);
  ShowWindow(Okno,nShow);
  
//po utworzeniu niezbędnych rzeczy w programie, robimy tą pętlę

  MSG msgs;
//struktura na komunikaty

  while(GetMessage(&msgs,
0
,
0
,
0
))
//pętla obsługująca wymianę komunikatów

  {
    TranslateMessage(&msgs);
//funkcja tłumacząca sygnały z klawiatury na odpowiednie komunikaty systemowe

    DispatchMessage(&msgs);
//funkcja przetwarzająca komunikaty systemowe przez procedury obsługi

  }
  return msgs.wParam;
}

Działanie aplikacji w Windowsie opiera się na ciągłym wysyłaniu i odbierniau komuniaktów z i do systemu. Najpierw musimy utworzyć strukturę MSG, do przechowywania tych komunikatów. Nastpnie wchodzimy w petlę, która będzie ciągle obsługiwać wymianę komunikatów pomiędzy programem a systemem. W takiej pętli znajdują się dwie funckje. Pierwsza z nich, przechwytuje komunikaty pochodzące z klawiatury, natomiast kolejna, wywołuje odpowiednie funckje, które będą odpowiedzialne, za reagowanie naszego programu na te komunikaty.

Tak skompilowany program powinien już poprawnie działać.

Dobrze. Wiemy, że system wysyła do naszego programu komunikaty, teraz możemy napisać funckję, która będzie obsługiwała reakcje naszego programu, na każdy komunikat. Dla każdej klasy okna w naszym programie, możemy przypisać taką funkcje obsługi, która będzie zawierała odpowiednią obsługę komunikatów. Przypomnę teraz jedno pole struktury WNDCLASSEX:

...
wc.lpfnWndProc=DefWindowProc;
...

Właśnie tu przypisujemy funkcję do odpowiedniego okna. W przykładnie naszemu oknu przypisujemy, funkcję o nazwie DefWindowProc, jest to domyślna funkcja obsługująca wszystkie komunikaty w sposób domyślny. Jednak jest ona mało efektywna, dlatego napiszmy sobie sami taką funkcję.

#include <windows.h>

LRESULT CALLBACK WndProc(HWND,UINT,WPARAM,LPARAM);
//deklaracja funkcji obsługi o nazwie WndProc


INT WINAPI WinMain(HINSTANCE hInst,HINSTANCE,LPSTR lStart,INT nShow)
{
  WNDCLASSEX wc;
  wc.hInstance=hInst;
  wc.lpszClassName=
"Klasa okna"
;
  wc.lpfnWndProc=WndProc;
//nazwa naszej własnej funkcji obsługującej komunikaty

  wc.style=CS_DBLCLKS;
  wc.cbSize=sizeof(WNDCLASSEX);
  wc.hIcon=LoadIcon(
0
,IDI_APPLICATION);
  wc.hIconSm=LoadIcon(
0
,IDI_APPLICATION);
  wc.hCursor=LoadCursor(
0
,IDC_ARROW);
  wc.lpszMenuName=
0
;
  wc.hbrBackground=(HBRUSH)GetStockObject(WHITE_BRUSH);
  wc.cbWndExtra=
0
;
  wc.cbClsExtra=
0
;
  if(RegisterClassEx(&wc)==
0
) return
0
;
  HWND Okno=CreateWindowEx(
0
,
"Klasa okna"
,
"Tytuł okna"
,WS_OVERLAPPEDWINDOW,
50
,
50
,
100
,
100
,
0
,
0
,hInst,
0
);
  ShowWindow(Okno,nShow);
  MSG msgs;
  while(GetMessage(&msgs,
0
,
0
,
0
))
  {
    TranslateMessage(&msgs);
    DispatchMessage(&msgs);
  }
  return msgs.wParam;
}

LRESULT CALLBACK WndProc(HWND hwnd,UINT msg,WPARAM wPar,LPARAM lPar)
{
  switch(msg)
  {
    case WM_PAINT:
      
//polecenia dla komunikatu WM_PAINT

      break;
    case WM_CLOSE:
      
//polecenia dla komunikatu WM_CLOSE

      PostQuitMessage(
0
);
      break;
    default:
      return DefWindowProc(hwnd,msg,wPar,lPar);
//domyślna obsługa reszty komunikatów

  }
  return
0
;
}

Nasza funkcja obsługi nazywa się WndProc. Jest typu LRESULT CALLBACK i ma 4 parametry. Pierwszy z nich to uchwyt okna, z którego pochodzi komunikat. Drugi parametr to "rodzaj komunikatu", czyli np. komunikat zamknięcia okna, czy kliknięcie na oknie lub każdy inny komunikat. Trzeci i czwarty to dodatkowe informacje, są zależne od rodzaju komunikatu, przykładowo w komunikacie "zmiana rozmiaru okna", będą to nowe wymiary okna.

Komunikaty rozdziela się w instrukcji switch. Instrukcja ta sprawdza rodzaj komunikatu, czyli sprawdza nasze msg, i według tego wyszukuje odpowiednie polecenia do wykonania. Jeżeli nie zdefiniowaliśmy danego komunikatu, to instrukcja natrafi na "default", czyli domyślne polecenia, a tam powinniśmy wywołać domyślna procedurę, aby obsłużyć komunikat w domyślny sposób. Jak widać w tej funkcji, każdy komunikat obsługujemy pomiędzy "case" a "break" i tutaj powtórzę, aby nie zapominać o break, bo wykona się następny komunikat.

Wcześniej mówiłem o komunikatach w sposób opisowy, np. komunikat zmiany rozmiaru okna, ale każdy komunikat ma swoją nazwę w kodzie. Napewno każdy komunikat będzie się zaczynam na "WM_", można sie domyślać, że to skrót od Windows Message(ang. komunikat Windowsa), nastepnie jest już nazwa danego komunikatu. Ile jest takich komunikatów? Bardzo dużo, oto najpotrzebniejsze:

WM_CLOSE - komunikat ten jest wysyłany przed zamknięciem okna, są to polecenia które mają się wykonać podczas zamykania okna, włącznie z samym zamykaniem.
WM_COMMAND - ten komunikat to komenda jakiegoś innego okna, które wysłało do naszego okna komendę.
WM_CREATE - komunikat ten jest wysyłany po utworzeniu okna, zwykle są w nim funckje tworzące wszystko co jest w tym oknie.
WM_MOVE - komunikat wysyłany podczas przesunięcia okna.
WM_PAINT - komunikat powiadamiający o potrzebie odrysowania okna.
WM_SIZE - komunikat wysyłany podczas zmiany rozmiaru okna.

Na koniec tej lekcji powiem o funkcji PostQuitMessage(), wywołanie tej funkcji spowoduje zakończenie działania pętli komunikatów, ta o której była mowa na początu tej lekcji, czyli innymi słowy zatrzyma działanie programu.