Canvas not supported
Canvas not supported
Suchen
Bookmark
Canvas not supported
Shine Effekt
Direkt zum Seiteninhalt

Hauptmenü:

Windows "Fenster" API Tutorial

 

Autor:

Oscar Kohler

Version:

0.11

Level:

Einfach

Sprache:

C/C++


 
Haftungsausschluss / Disclaimer
Kontakt / Contact
Drucken / Print


Eine Grafische Oberfläche ohne Fenster ... kann man sich kaum vorstellen. Fenster ermöglichen es, Anwendungen intuitiv mit Maus und Tastatur zu bedienen. Das Sichtfeld der Anwendung zu verschieben, verkleinern, vergrössern, die Anwendung mit einem Mausklick zu schliessen oder auch zwischen verschiedenen Fenstern einer Anwendung zu wechseln. Fenster lassen sich beliebig anordnen, übereinander stappeln und bei Gebrauch aktivieren. Ständig bedienen wir die Fenster unserer Programme ohne uns grosse Gedanken darüber zu machen, was da im Hintergrund rumwerkelt. Es funktioniert halt einfach ...

Fenster API hinter die Kullissen ...


Grundsätzlich ist ein Fenster eine Art grafischer Container, der durch Windows verwaltet wird.


Fenster APIAussehen und Interaktionsmöglichkeiten sind grundlegend vorgegeben und verkörpern das Windows "Look and Feel".
Fenster können wir verwenden um beispielsweise darin zu malen, Texte auszugeben, Steuerelemente reinzulegen oder auf bestimmte Ereignisse zu reagieren.
Das zeigt, dass sich mit Fenster einiges anstellen lässt.


Nun gleich eine Überraschung ! Bei Windows ist nicht nur das "eigentliche" Fenster ein Window, sondern auch Steuerelemente (Controls) sind Fenster (im Fenster) und werden daher Childwindows genannt.

Sprung ins kalte Wasser !


Bisher klingt das Alles noch sehr vage. Am besten wir kreiren mal ein Andwendungfenster damit wir eine Vorstellung davon bekommen, wie Fenster technisch funktionieren.

Window Classes / Fenster Klassen


Fenster werden in Klassen unterteilt. Klassen definieren sozusagen die Art und Funktion eines Fensters.


Unterschieden wird zwischen:

  • System Klassen:

Steuerelemente wie Button, Listbox, Combobox, ...


  • Applikations Klassen (global, lokal):

Durch die Anwendung definierte Klassen.


Die Definition der Klasse erfolgt in der Struktur WNDCLASSEX.
Hier können wir verschiedenen Attribute setzen:

typedef struct tagWNDCLASSEX {
 UINT      cbSize;
 UINT      style;
 WNDPROC   lpfnWndProc;
 int       cbClsExtra;
 int       cbWndExtra;
 HINSTANCE hInstance;
 HICON     hIcon;
 HCURSOR   hCursor;
 HBRUSH    hbrBackground;
 LPCTSTR   lpszMenuName;
 LPCTSTR   lpszClassName;
 HICON     hIconSm;
} WNDCLASSEX, *PWNDCLASSEX;



MSDN: WNDCLASSEX Structure


Die Attribute sind eigentlich mehr oder weniger selbsterklärend. Interessant ist hingegen aber speziell ein Attribut:
WNDPROC lpfnWndProc -> Die Fenster Prozedur. Hier müssen wir nun aber etwas weiter ausholen ...

Kurzes Intermezzo:



Wie gelangt Windows an die Koordinaten der Hardware Maus ?
Bewegen wir die Maus, wird das x / y / (z) Bewegungs-Delta periodisch durch den Maus Hardwaretreiber, z.B.: mittels Hardware Interrupt, ermittelt und umgerechnet. Die Bewegungsdaten werden dann vom Treiber in standardisierter Form an Windows übergeben und anschliessend in der Sytem-Queue eingereiht.

Erfassung Mauskoordinaten

Windows Messages


Im Windows Betrieb ist es notwendig, dass Windows mit den verschiedenen Fenstern auf dem Desktop in irgendeiner Form kommuniziert. Da unter anderem auch Eingabe Geräte wie Tastatur und Maus durch Windows verwaltet wird, muss Windows den Fenstern mitteilen, was der Benutzer gerade unternimmt.


Kurvt der Benutzer mit der Maus in unserem Fenster, klickt er gerade auf ein Steuerelement, oder möchte er das Fenster schliessen, weil er nun keine Lust mehr hat ... all diese Aktionen, und noch ganz viele mehr, teilt uns Windows mit.

Windows ist ein sogenanntes "Event-Driven-System" also ein "Ereignis gesteuertes System". Das heisst wir müssen (und sollen) nicht darüber nachgrübeln ob der Benutzer nun gerade unser Fenster anklickt, oder auf einen Button drückt. Wenn dem so ist, werden wir automatisch durch Windows mit einer Meldung "Button XY wurde gedrückt" informiert.

WNDPROC


Die Art der Mitteilung wurde über ein Message System realisiert. Nun kommt unsere Fenster Prozedur ins Spiel !

Sobald ein interessantes Ereignis eintritt, teilt Windows dies allen Fenstern einer Klasse mit. Daher müssen wir Windows diese Prozedur als Callback bekannt geben.

z.B.: durch CALLBACK WndProc.

LRESULT CALLBACK WindowProc (
 __in  HWND hwnd,
 __in  UINT uMsg,
 __in  WPARAM wParam,
 __in  LPARAM lParam
);




^ MSDN: WindowProc Callback Function

Zurück zur Klassen Definition. Nachdem wir die Struktur WNDCLASSEX befüllt haben, registrieren wir die neue Fenster-Klasse in Windows. Dies geschieht durch die Windows Funktion: RegisterClassEx().

Nun könnten wir beliebig viele Windows dieser Klasse aufmachen aber wir beschränken uns erstmal auf Eines.
Kreirt wird ein Fenster mit der Funktion: CreateWindowEx().
Eigenschaften des Fensters geben wir anhand Funktions Parameter mit:

HWND WINAPI CreateWindowEx (
 __in      DWORD dwExStyle,
 __in_opt  LPCTSTR lpClassName,
 __in_opt  LPCTSTR lpWindowName,
 __in      DWORD dwStyle,
 __in      int x,
 __in      int y,
 __in      int nWidth,
 __in      int nHeight,
 __in_opt  HWND hWndParent,
 __in_opt  HMENU hMenu,
 __in_opt  HINSTANCE hInstance,
 __in_opt  LPVOID lpParam
);


^ MSDN: CreateWindowEx Function

Wenn nun alles funktioniert hat, dann öffnet Windows für uns das Fenster und gibt einen Handle HWND auf dieses Fenster zurück.
Dieses Handle ist sehr wichtig und müssen wir daher unbedingt abspeichern. Mit diesem Handle können wir das Fenster ändern, bearbeiten und grafische Funktionen in diesem Fenster ausführen.

Simple WindowNun haben wir ein Fenster, das auf dem Desktop herumlungert und auf überhaupt Nichts reagiert. Windows will uns zwar informieren, aber wir hören einfach nicht hin. Nach kurzer Zeit erscheint dann in der Titelleiste die bekannte und beunruhigende Meldung: "Keine Rückmeldung". Diese Meldung deutet gewöhnlich darauf hin, dass ein Programm mächtig ins Strudeln kam und daher nicht mehr vernüftigt reagiert.

Irgendwie müssen wir also auf die Ereignisse reagieren, bis jemand das Fenster schliessen möchte und wir dann das Programm beenden können.
Windows ist eben ein "Ereignis gesteuertes System" das verlangt auf Ereignisse geduldig zu warten und erst dann entsprechend darauf zu reagieren.

Windows Message Loop


Technisch wird dies durch den Eintritt in die Message Loop realisiert.

Message LoopWir haben weiter oben kurz über das Windows Nachrichtensystem diskutiert. Windows informiert alle betreffenden Fenster über wichtige Ereignisse.
Da wir eigentlich nur darauf warten wollen, bis wir das Fenster wieder schliessen können, interessiert uns auch nur die "Fenster-Schliessen" Nachricht.

Windows legt unsere Nachrichten in eine (Thread) Nachrichten Queue, damit wir die Nachrichten abholen können, sobald wir Zeit dafür haben.
Allerdings ist diese Zeit nicht ewig. Wenn wir einige Zeit (Sekunden) nicht darauf reagieren, erhält der Benutzer die erwähnte "Keine Rückmeldung" Information in der Titelleiste.

Die Nachrichten können wir mit der Funktion: GetMessage() von der Thread-Nachrichten-Queue abholen.
Ist gerade nichts für uns dabei wartet die Funktion solange bis Windows wieder eine Nachricht für uns abgibt (synchron).
Eine ähnliche Funktion wird mit: PeekMessage() angeboten, die jedoch asynchron arbeitet und daher eher in Demo- und Game-Loops zu finden ist.

^ MSDN: Messages and Message Queues

Sobald etwas für uns in der Queue liegt, sehen wir nach, ob unsere "Fenster schliessen" Nachricht dabei ist und wir das Fenster nun schliessen können. Wenn nicht, geben wir die Nachricht mit der Funktion: DispatchMessage() zur Weiterverarbeitung an die Fenster Prozedur ab und "loopen" fröhlich weiter.
Wichtig ist hierbei, dass in der Message Queue eher die "zeitunkritischen" Nachrichten abgelegt werden. "Zeitkritischere" Nachrichten schickt Windows direkt (nonqueued) zur Fenster Prozedur.

Eine "typische" Message-Loop zeigt das folgende Beispiel:

MSG msg;

while (GetMessage(&msg, NULL, 0, 0)) {
  TranslateMessage(&msg);
  DispatchMessage(&msg);
}
return (int) msg.wParam;

Fenster Prozedur / Windows Procedur


Die Fenster Prozedur haben wir bereits weiter oben angesprochen. Damit Windows uns mit wichtigen Nachrichten versorgen kann, haben wir diese Funktion als Callback bereits in der Fensterklasse für Windows bekannt gegeben.

Fenster Prozedur / Windows ProcedurBei jeder Nachricht die für unsere Fensterklasse möglicherweise interessant sein könnte, ruft Windows die von uns bekanntgegebene Fenster-Prozedur unqueued, also direkt und ohne Umwege über eine Queue, auf.

Die Nachricht MSG erfolgt in einem definierten Format.

typedef struct tagMSG {
 HWND   hwnd;
 UINT   message;
 WPARAM wParam;
 LPARAM lParam;
 DWORD  time;
 POINT  pt;
} MSG, *PMSG, *LPMSG;

Die Nachrichtentypen sind sehr zahlreich und informieren uns über die unterschiedlichsten Ereignisse.
Informiert werden wir sowohl über Ereignisse die unser Fenster betreffen wie z.B.: das wir unser Fenster neu zeichnen müssen, aber auch über Systemereignisse wie z.B.: dass der Computer heruntergefahren wird.

Das Prefix beginnt mit WM_, darauf folgt der Titel des Ereignis.


Anbei eine kleine Auswahl:

...
WM_PAINT
WM_CLOSE
WM_QUERYENDSESSION
WM_QUIT
WM_QUERYOPEN
WM_ERASEBKGND
WM_SYSCOLORCHANGE
WM_ENDSESSION
WM_SYSTEMERROR
WM_SHOWWINDOW
WM_CTLCOLOR
WM_WININICHANGE
WM_SETTINGCHANGE
WM_DEVMODECHANGE
...

Wenn wir uns für eine Nachricht interessieren, können wir sie aus dem Nachrichten Schwarm herausfischen und für unsere Zwecke behandeln.
Die für uns weniger interessanten geben wir einfach mit der Funktion: DefWindowProc() zur Standart Verarbeitung an Windows weiter.

Dies könnte Beispielsweise so aussehen:

LRESULT CALLBACK WndProc( HWND hWnd,                       // Fenster Handle
                         UINT msg,                        // Nachrichten Typ
                         WPARAM wParam, LPARAM lParam ) { // Parameter

  switch (msg) {                                          // Nachrichten Typ prüfen

     //***

     case WM_LBUTTONDOWN:                                 // Linke Maustaste ?
        Linke_Maus_Taste();                               // -> Action
        break;

     //***

     case WM_DESTROY:                                     // Fenster schliessen ?
        PostQuitMessage(0);                               // -> WM_QUIT
        break;

     //***

     default:                                             // -> Windows default
        return DefWindowProc(hWnd, msg, wParam, lParam);
        break;
  }

  return 0;
}


Interessant ist hierbei die Priorisierung mancher Ereignisse. Beispielsweise erhalten wir einige Nachrichten wie z.B.: WM_QUIT oder WM_TIMER erst dann, wenn die Nachrichten-Queue komplett leergeräumt ist. Nun wird vermutlich einigen bewusst, warum solche Timer nicht besonders präzise sind.

MSDN: Da wird einem geholfen ...

Microsoft hat auf der MSDN ein komplettes WinApi "Hello World !" Beispiel in C++ veröffentlicht.
Hier wird Schritt für Schritt der Aufbau der Applikation erklärt !

^ MSDN: Creating Win32-Based Applications (C++)


 
Kein Kommentar
 
Letzte Änderung: 03.02.2015
button Canvas not supported button
Zurück zum Seiteninhalt | Zurück zum Hauptmenü