www.codeworx.org/directx_tuts/Pre DirectX Tutorial: Teil 4 - WinProc

Pre DirectX Tutorial: Teil 4 - WinProc

Heya!

Dies wird der letzte Teil mit reinem Win API Zeugs sein. Wir werden einen Blick auf den Event Handler (oder WinProc) werfen. Ein Thema welches schon so manchen harten Programmierer in den Abgrund getrieben hat. Gute Aussichten, oder?

Fangen wir gleich richtig an. Ich sagte in einem früheren Teil das Windows "event driven" ist. Intern werde Nachrichten an Programme verschickt, mit denen das Programm über das System informiert wird: "wurde eine Taste gedrückt", "geht gerade der Bildschirmschoner los?", und so weiter. Ich sagte auch jedes Programm brauche so eine Art Schnittstelle zum System, welche diese Nachrichten verwaltet. Und eben dies macht der Event Hanlder unseres Programmes.

Der Event Handler eures Programmes wird von euch geschrieben, aber keine Sorge: ihr müsst euch nicht um alle Nachrichten kümmern, die da ankommen. Was kümmert es eure Demo, wenn ne Mail kommt? Ihr könnt also nur bestimmte Nachrichten aussondern, diese bearbeiten und den Rest Windows überlassen.

Noch mal eine Rückblende. Als ihr euer Fenster registriert habt, kam die Zeile WNDPROC lpfnWndProc; vor. Damit habt ihr eure WinProc angegeben. Die WinProc ist euer Event Handler und euer fenster nutzt von nun an die angegebene WinProc zum verabreiten von Windows Nachrichten.

In Wirklichkeit kommen diese Nachrichten nicht wild auf euer Programm zu. Es hätte dann auch gar keine Zeit mehr sich um seine eigenen Aufgaben zu kümmern. Windows legt die Nachrichten vielmehr in einer Art Zwischenspeicher ab. Dort bleiben sie dann bis sie verarbeitet werden.

Um es wirklich glasklar zu machen ein Beispiel: Windows hat eine Nachricht, die Nachricht wird auf den Stapel gelegt. Ihr schaut nach was auf dem Stapel liegt. Wenn es euch interessiert, holt es ab und bearbeitet es. Wenn nicht, dann lasst Windows sich darum kümmern.

Schauen wir uns mal eine solche WinProc an.

--------------------------------------------------------------------------------

LRESULT CALLBACK WinProc(
   HWND hwnd,
   UINT msg,
   WPARAM wparam,
   LPARAM lparam)


--------------------------------------------------------------------------------

Ein Blick auf die Parameter:

hwnd: Das ist unser Window Handle. Ist eigentlich nur wichtig, wenn wir mehrere Fenster der gleichen Klasse geöffnet haben. hwnd überprüft in dem Fall welche Nachrichten von welchem Fenster verursacht wurden.

msg: Das ist die eigentliche Nachricht, die WinProc verarbeitet. naja - in Wirklichkeit ist es nur eine ID Nummer, kommt aber nahe dran.

Wparam/ Lparam: Für weitergehende Bearbeitung der Nachrichten.

Wichtig ist LResult Callback. Nicht vergessen. So. Nun läuft die ganze Sache folgendermaßen ab: Wir holen die Nachricht ab. Schauen nach ob die Nachricht ID in msg für uns interessant ist und wenn ja, kommt ein Stück code wie wir weiter damit verfahren wollen. In msg stehen reine IDs, hier mal eine Auswahl:

WM_ACTIVATE - Ein Fenster wird aktiviert.
WM_CLOSE - Ein Fenster wird geschlossen.
WM_CREATE - Ein Fenster wird neu erzeugt.
WM_DESTROY - Ein Fenster wird zerstört.
WM_MOUSEMOVE - Die Maus wird bewegt

Das sind natürlich nur ein paar. Wie immer gilt: schnappt euch ein Windows Buch oder die Win API Hilfe Files von Microsoft um einen kompletten Überblick zu bekommen.

Um es wirklich supereinfach zu machen interessiert us erst mal nur eine Nachricht: WM_DESTROY. Die brauchen wir um festzustellen ob der User unser Fenster geschlossen hat, damit wir unser Programm daraufhin beenden. Merke: nur weil der User das Fenster zumacht wird unser Programm von Windows nicht automatisch beendet! Das ist unsr Job! Hier also unsere WinProc:

--------------------------------------------------------------------------------

LRESULT CALLBACK WinProc( HWND hwnd,
                          UINT msg,
                          WPARAM wparam,
                          LPARAM lparam)
{
   PAINTSTRUCT ps; // used in WM_PAINT
   HDC hdc; // handle to a device context
   switch(msg)
   {
      case WM_CREATE:
      {
         // - 1 -
         return(0);
      } break;
      case WM_PAINT:
      {
          // - 2 - 
          hdc = BeginPaint(hwnd,&ps);
          EndPaint(hwnd,&ps);
          return(0);
      } break;
      case WM_DESTROY:
      {
         // - 3 -
         PostQuitMessage(0);
         return(0);
      } break;
   default:break;
   }
}


--------------------------------------------------------------------------------

Boah, oder? Es sieht wirklich komplizierter aus als es ist. Diese 3 "Break Punkte" sind zum besseren Verständnis. Schritt für Schritt:

Erst eröffnen wir die WinProc mit dem üblichen Unsinn (siehe oben), dann definieren wir einmal die PAINTSTRUCT, die brauchen wir für WM_PAINT. Dann noch ein Handle zum Device Kontext: HDC. Dann fangen wir an die Nachrichten zu bearbeiten: wir schaun erst mit switch nach welche Nachricht eigentlich in msg gespeichert ist. Und dann folgt eine simple und einfache Bedingungsabfrage: Wenn die Nachricht dasunddas ist, dann mache dies, wenn sie diesunddas ist, dann mache das und so weiter.

Der erste Break Punkt wird erreicht, wenn das Fenster gerade geöffnet wurde. Dann wird einfach nur 0 zurückgeliefert und alles ist Sahne.

Der zweite Break Punkt wird im Falle einer WM_PAINT Nachricht erreicht. das bedeutet wahrscheinlich das ein User ein anderes Fenster über euers gelegt hat und der Fensterinhalt neu gezeichnet werden woll. Bei Vollbild Fenstern ist das weniger dramatisch, aber bei normalen Fenstern schon. Im Moment sagen wir Windows einfach "Jaja, wir kümmern uns drum", damit es uns nicht noch mehr Nachrichten schickt und fertig.

Zum dritten Break Punkt kommen wir wenn der User das Fenster schließt. Wie gesagt läuft unser Programm dennoch weiter. Interpretieren wir aber das schließen unseres Fensters als gewünschten Abbruch unseres Programms, so sollten wir nun evtl. Speicher aufräumen und was nicht alles. In dem Fall sagen wir wieder "Jaja, schon klar" und lassen es auf sich beruhen.

Wars das endlich? Naja...ja und nein - eure WinProc Routine ist fertig, ja. Aber ist euch aufgefallen das wir nirgendwo die Nachrichten abholen und an die WinProc weiterleiten? Das müssen wir in der WinMain, also unserer Hauptschleife erledigen. Ihr erinnert euch vom allerersten Beispiel? Die WinMain ist die Hauptprozedur unseres Programmes. Da fängts an.

Also los gehts:

--------------------------------------------------------------------------------

if (PeekMessage(&msg,NULL,0,0,PM_REMOVE))
{
   if (msg.message == WM_QUIT)
   break;
   TranslateMessage(&msg);
   DispatchMessage(&msg);
}


--------------------------------------------------------------------------------

Was tun wir da? PeekMessage schaut nach ob eine Nachricht vorhanden ist und wenn ja, dann steht sie in &msg. Wenn der User das Fenster geschlossen hat können wir uns die Arbeit auch sparen - ansonsten schau dir die Nachricht an ud bearbeite sie.

Und das ist wirklich alles. Hmm...ich sehe schon wie ihr mit Fragezeichen im Gesicht vor eurem Monitor sitzt, aber es ist wirklich einfacher als man denkt - Übung macht hier den Meister. Also - im nächsten Kurs geht es um eure Compiler, bis dahin...

Cheers,

Delax
[delax@sundancerinc.de]