Messaggi finestra (Introduzione a Win32 e C++)

Un'applicazione GUI deve rispondere agli eventi dell'utente e dal sistema operativo.

  • Gli eventi dell'utente includono tutti i modi in cui un utente può interagire con il programma: clic del mouse, tratti di tasti, movimenti del touchscreen e così via.
  • Gli eventi del sistema operativo includono qualsiasi elemento "esterno" del programma che può influire sul comportamento del programma. Ad esempio, l'utente potrebbe collegare un nuovo dispositivo hardware o Windows potrebbe attivare uno stato di alimentazione inferiore (sospensione o ibernazione).

Questi eventi possono verificarsi in qualsiasi momento mentre il programma è in esecuzione, in quasi qualsiasi ordine. Come si struttura un programma il cui flusso di esecuzione non può essere stimato in anticipo?

Per risolvere questo problema, Windows usa un modello di passaggio di messaggi. Il sistema operativo comunica con la finestra dell'applicazione passando messaggi. Un messaggio è semplicemente un codice numerico che designa un determinato evento. Ad esempio, se l'utente preme il pulsante sinistro del mouse, la finestra riceve un messaggio con il codice del messaggio seguente.

#define WM_LBUTTONDOWN    0x0201

Alcuni messaggi contengono dati associati. Ad esempio, il messaggio WM_LBUTTONDOWN include la coordinata x e la coordinata y del cursore del mouse.

Per passare un messaggio a una finestra, il sistema operativo chiama la routine della finestra registrata per tale finestra. E ora si sa a cosa serve la procedura della finestra.

Ciclo del messaggio

Un'applicazione riceverà migliaia di messaggi durante l'esecuzione. Si consideri che ogni pressione del tasto e clic del pulsante del mouse genera un messaggio. Inoltre, un'applicazione può avere diverse finestre, ognuna con la propria procedura di finestra. In che modo il programma riceve tutti questi messaggi e li recapita alla procedura corretta della finestra? L'applicazione richiede un ciclo per recuperare i messaggi e inviarli alle finestre corrette.

Per ogni thread che crea una finestra, il sistema operativo crea una coda per i messaggi della finestra. Questa coda contiene messaggi per tutte le finestre create in tale thread. La coda stessa è nascosta dal programma. Non è possibile modificare direttamente la coda. È tuttavia possibile eseguire il pull di un messaggio dalla coda chiamando la funzione GetMessage .

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

Questa funzione rimuove il primo messaggio dall'intestazione della coda. Se la coda è vuota, la funzione si blocca fino a quando non viene accodato un altro messaggio. Il fatto che i blocchi GetMessage non rispondano al programma. Se non sono presenti messaggi, non c'è nulla da fare per il programma. Se è necessario eseguire l'elaborazione in background, è possibile creare thread aggiuntivi che continuano a essere eseguiti mentre GetMessage attende un altro messaggio. Vedere Evitare colli di bottiglia nella procedura della finestra.

Il primo parametro di GetMessage è l'indirizzo di una struttura MSG . Se la funzione ha esito positivo, compila la struttura MSG con informazioni sul messaggio. Sono incluse la finestra di destinazione e il codice del messaggio. Gli altri tre parametri consentono di filtrare i messaggi ricevuti dalla coda. In quasi tutti i casi, questi parametri verranno impostati su zero.

Anche se la struttura MSG contiene informazioni sul messaggio, quasi mai si esaminerà direttamente questa struttura. Verrà invece passato direttamente a due altre funzioni.

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

La funzione TranslateMessage è correlata all'input della tastiera. Converte le sequenze di tasti (tasto giù, tasto su) in caratteri. Non è necessario sapere come funziona questa funzione; ricordarsi di chiamarlo prima di DispatchMessage. Se si è curiosi, il collegamento alla documentazione MSDN contiene ulteriori informazioni.

La funzione DispatchMessage indica al sistema operativo di chiamare la routine della finestra della finestra che rappresenta la destinazione del messaggio. In altre parole, il sistema operativo cerca l'handle della finestra nella tabella delle finestre, trova il puntatore a funzione associato alla finestra e richiama la funzione.

Si supponga, ad esempio, che l'utente preme il pulsante sinistro del mouse. Ciò causa una catena di eventi:

  1. Il sistema operativo inserisce un messaggio WM_LBUTTONDOWN nella coda dei messaggi.
  2. Il programma chiama la funzione GetMessage .
  3. GetMessage esegue il pull del messaggio WM_LBUTTONDOWN dalla coda e compila la struttura MSG .
  4. Il programma chiama le funzioni TranslateMessage e DispatchMessage .
  5. All'interno di DispatchMessage, il sistema operativo chiama la procedura della finestra.
  6. La procedura della finestra può rispondere al messaggio o ignorarla.

Quando la routine della finestra viene restituita, torna a DispatchMessage. Viene restituito al ciclo di messaggi per il messaggio successivo. Se il programma è in esecuzione, i messaggi continueranno ad arrivare nella coda. Pertanto, è necessario disporre di un ciclo che estrae continuamente i messaggi dalla coda e li invia. È possibile considerare il ciclo come segue:

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

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

Come scritto, naturalmente, questo ciclo non finirà mai. In questo caso viene visualizzato il valore restituito per la funzione GetMessage . In genere, GetMessage restituisce un valore diverso da zero. Quando si vuole uscire dall'applicazione e uscire dal ciclo di messaggi, chiamare la funzione PostQuitMessage .

        PostQuitMessage(0);

La funzione PostQuitMessage inserisce un messaggio WM_QUIT nella coda dei messaggi. WM_QUIT è un messaggio speciale: fa sì che GetMessage restituisca zero, segnalando la fine del ciclo di messaggi. Di seguito è riportato il ciclo di messaggi modificato.

// Correct.

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

Finché GetMessage restituisce un valore diverso da zero, l'espressione nel ciclo while restituisce true. Dopo aver chiamato PostQuitMessage, l'espressione diventa false e il programma si interrompe dal ciclo. Un risultato interessante di questo comportamento è che la routine della finestra non riceve mai un messaggio di WM_QUIT . Pertanto, non è necessario disporre di un'istruzione case per questo messaggio nella procedura della finestra.

La domanda ovvia successiva è quando chiamare PostQuitMessage. Si tornerà a questa domanda nell'argomento Chiusura della finestra, ma prima di tutto è necessario scrivere la procedura della finestra.

Messaggi pubblicati e messaggi inviati

La sezione precedente ha parlato dei messaggi che passano in una coda. In alcuni casi, il sistema operativo chiamerà direttamente una routine della finestra, ignorando la coda.

La terminologia per questa distinzione può generare confusione:

  • La pubblicazione di un messaggio indica che il messaggio passa alla coda dei messaggi e viene inviato tramite il ciclo di messaggi (GetMessage e DispatchMessage).
  • L'invio di un messaggio indica che il messaggio ignora la coda e il sistema operativo chiama direttamente la routine della finestra.

Per ora, la differenza non è molto importante. La routine della finestra gestisce tutti i messaggi. Tuttavia, alcuni messaggi ignorano la coda e passano direttamente alla procedura della finestra. Tuttavia, può fare la differenza se l'applicazione comunica tra finestre. È possibile trovare una descrizione più approfondita di questo problema nell'argomento Informazioni su messaggi e code di messaggi.

Prossima

Scrittura della routine finestra