Fenstermeldungen (Erste Schritte mit Win32 und C++)

Eine GUI-Anwendung muss auf Ereignisse vom Benutzer und vom Betriebssystem reagieren.

  • Ereignisse vom Benutzer umfassen alle Möglichkeiten, wie jemand mit Ihrem Programm interagieren kann: Mausklicks, Tastenstriche, Touchscreengesten usw.
  • Ereignisse aus dem Betriebssystem enthalten alle "außerhalb" des Programms, die sich auf das Verhalten des Programms auswirken können. Beispielsweise kann der Benutzer ein neues Hardwaregerät anschließen, oder Windows kann in einen Zustand mit geringerer Leistung (Ruhemodus oder Ruhezustand) wechseln.

Diese Ereignisse können jederzeit auftreten, während das Programm ausgeführt wird, in fast beliebiger Reihenfolge. Wie strukturieren Sie ein Programm, dessen Ausführungsablauf nicht vorhergesagt werden kann?

Um dieses Problem zu beheben, verwendet Windows ein Nachrichtenübergabemodell. Das Betriebssystem kommuniziert mit Ihrem Anwendungsfenster, indem es Nachrichten an das Fenster übergibt. Eine Nachricht ist einfach ein numerischer Code, der ein bestimmtes Ereignis angibt. Wenn der Benutzer beispielsweise die linke Maustaste drückt, erhält das Fenster eine Nachricht mit dem folgenden Nachrichtencode.

#define WM_LBUTTONDOWN    0x0201

Einigen Nachrichten sind Daten zugeordnet. Die WM_LBUTTONDOWN Nachricht enthält beispielsweise die x-Koordinate und die y-Koordinate des Mauscursors.

Um eine Nachricht an ein Fenster zu übergeben, ruft das Betriebssystem die für dieses Fenster registrierte Fensterprozedur auf. (Und jetzt wissen Sie, wozu die Fensterprozedur dient.)

Nachrichtenschleife

Eine Anwendung empfängt Tausende von Nachrichten, während sie ausgeführt wird. (Bedenken Sie, dass jeder Tastendruck und jeder Mausklick eine Nachricht generiert.) Darüber hinaus kann eine Anwendung über mehrere Fenster verfügen, die jeweils über eine eigene Fensterprozedur verfügen. Wie empfängt das Programm all diese Nachrichten und übermittelt sie an die richtige Fensterprozedur? Die Anwendung benötigt eine Schleife, um die Nachrichten abzurufen und an die richtigen Fenster zu senden.

Für jeden Thread, der ein Fenster erstellt, erstellt das Betriebssystem eine Warteschlange für Fensternachrichten. Diese Warteschlange enthält Nachrichten für alle Fenster, die in diesem Thread erstellt werden. Die Warteschlange selbst ist in Ihrem Programm ausgeblendet. Sie können die Warteschlange nicht direkt bearbeiten. Sie können jedoch eine Nachricht aus der Warteschlange abrufen, indem Sie die GetMessage-Funktion aufrufen.

MSG msg;
GetMessage(&msg, NULL, 0, 0);

Diese Funktion entfernt die erste Nachricht aus dem Kopf der Warteschlange. Wenn die Warteschlange leer ist, blockiert die Funktion, bis eine andere Nachricht in die Warteschlange eingereiht wird. Die Tatsache, dass GetMessage-Blöcke ihr Programm nicht reagiert. Wenn keine Nachrichten vorhanden sind, kann das Programm nichts tun. Wenn Sie eine Hintergrundverarbeitung durchführen müssen, können Sie zusätzliche Threads erstellen, die weiterhin ausgeführt werden, während GetMessage auf eine andere Nachricht wartet. (Siehe Vermeiden von Engpässen in Ihrer Fensterprozedur.)

Der erste Parameter von GetMessage ist die Adresse einer MSG-Struktur . Wenn die Funktion erfolgreich ist, füllt sie die MSG-Struktur mit Informationen zur Nachricht aus. Dies schließt das Zielfenster und den Nachrichtencode ein. Mit den anderen drei Parametern können Sie filtern, welche Nachrichten Sie aus der Warteschlange erhalten. In fast allen Fällen legen Sie diese Parameter auf Null fest.

Obwohl die MSG-Struktur Informationen über die Nachricht enthält, werden Sie diese Struktur fast nie direkt untersuchen. Stattdessen übergeben Sie es direkt an zwei andere Funktionen.

TranslateMessage(&msg); 
DispatchMessage(&msg);

Die TranslateMessage-Funktion bezieht sich auf Tastatureingaben. Es übersetzt Tastenanschläge (Nach unten, Taste nach oben) in Zeichen. Sie müssen nicht wirklich wissen, wie diese Funktion funktioniert; Denken Sie daran, sie vor DispatchMessage aufzurufen. Der Link zur MSDN-Dokumentation enthält weitere Informationen, wenn Sie neugierig sind.

Die DispatchMessage-Funktion weist das Betriebssystem an, die Fensterprozedur des Fensters aufzurufen, das das Ziel der Nachricht ist. Anders ausgedrückt: Das Betriebssystem sucht das Fensterhandle in seiner Fenstertabelle, findet den dem Fenster zugeordneten Funktionszeiger und ruft die Funktion auf.

Angenommen, der Benutzer drückt die linke Maustaste. Dies führt zu einer Kette von Ereignissen:

  1. Das Betriebssystem platziert eine WM_LBUTTONDOWN Nachricht in der Nachrichtenwarteschlange.
  2. Ihr Programm ruft die GetMessage-Funktion auf.
  3. GetMessage ruft die WM_LBUTTONDOWN Nachricht aus der Warteschlange ab und füllt die MSG-Struktur aus.
  4. Ihr Programm ruft die Funktionen TranslateMessage und DispatchMessage auf .
  5. In DispatchMessage ruft das Betriebssystem Ihre Fensterprozedur auf.
  6. Ihre Fensterprozedur kann entweder auf die Nachricht reagieren oder sie ignorieren.

Wenn die Fensterprozedur zurückgibt, kehrt sie zu DispatchMessage zurück. Dadurch wird die Nachrichtenschleife für die nächste Nachricht zurückgegeben. Solange Ihr Programm ausgeführt wird, werden nachrichten weiterhin in der Warteschlange eintreffen. Daher müssen Sie über eine Schleife verfügen, die kontinuierlich Nachrichten aus der Warteschlange abruft und diese sendet. Sie können sich die Schleife wie folgt vorstellen:

// WARNING: Don't actually write your loop this way.

while (1)      
{
    GetMessage(&msg, NULL, 0,  0);
    TranslateMessage(&msg); 
    DispatchMessage(&msg);
}

Wie geschrieben, würde diese Schleife natürlich nie enden. Hier kommt der Rückgabewert für die GetMessage-Funktion ins Feld. Normalerweise gibt GetMessage einen nichtzero-Wert zurück. Wenn Sie die Anwendung beenden und aus der Nachrichtenschleife ausbrechen möchten, rufen Sie die PostQuitMessage-Funktion auf.

        PostQuitMessage(0);

Die PostQuitMessage-Funktion platziert eine WM_QUIT Nachricht in der Nachrichtenwarteschlange. WM_QUIT ist eine besondere Nachricht: Sie bewirkt, dass GetMessage null zurückgibt, was das Ende der Nachrichtenschleife signalisiert. Hier ist die überarbeitete Nachrichtenschleife.

// Correct.

MSG msg = { };
while (GetMessage(&msg, NULL, 0, 0) > 0)
{
    TranslateMessage(&msg);
    DispatchMessage(&msg);
}

Solange GetMessage einen nonzero-Wert zurückgibt, wird der Ausdruck in der while-Schleife als true ausgewertet. Nachdem Sie PostQuitMessage aufgerufen haben, wird der Ausdruck false, und das Programm bricht aus der Schleife aus. (Ein interessantes Ergebnis dieses Verhaltens ist, dass Ihre Fensterprozedur nie eine WM_QUIT Nachricht empfängt. Daher müssen Sie in Ihrer Fensterprozedur keine Fallhinweise für diese Nachricht haben.)

Die nächste offensichtliche Frage ist, wann PostQuitMessage aufgerufen werden soll. Wir kehren zu dieser Frage im Thema Fenster schließen zurück, aber zuerst müssen wir unsere Fensterprozedur schreiben.

Gesendete Nachrichten im Vergleich zu gesendeten Nachrichten

Im vorherigen Abschnitt wurden Nachrichten behandelt, die in eine Warteschlange eingereiht werden. Manchmal ruft das Betriebssystem eine Fensterprozedur direkt auf, wobei die Warteschlange umgangen wird.

Die Terminologie für diese Unterscheidung kann verwirrend sein:

  • Das Posten einer Nachricht bedeutet, dass die Nachricht in die Nachrichtenwarteschlange wechselt und über die Nachrichtenschleife (GetMessage und DispatchMessage) gesendet wird.
  • Das Senden einer Nachricht bedeutet, dass die Nachricht die Warteschlange überspringt, und das Betriebssystem ruft die Fensterprozedur direkt auf.

Im Moment ist der Unterschied nicht sehr wichtig. Die Fensterprozedur verarbeitet alle Nachrichten. Einige Nachrichten umgehen jedoch die Warteschlange und wechseln direkt zu Ihrer Fensterprozedur. Es kann jedoch einen Unterschied machen, ob Ihre Anwendung zwischen Fenstern kommuniziert. Eine ausführlichere Erläuterung dieses Problems finden Sie im Thema Informationen zu Nachrichten und Nachrichtenwarteschlangen.

Nächste

Schreiben der Fensterprozedur