Introduzione alle API Win32

Capitolo 5: messaggi

Con le finestre si comunica ricevendo e spedendo messaggi (da non confondersi con i "messaggi" mostrati all'utente da MessageBox!). Il sistema spedisce messaggi alle finestre a fronte di vari possibili eventi; il nostro codice, ricevendo questi messaggi e rispondendovi, determinerà il comportamento della nostra applicazione. Inoltre, noi stessi spediremo messaggi a varie finestre allo scopo di richiedere ad esse di compiere certe azioni, o anche per ottenere da esse delle informazioni, eccetera.

Sul flusso dei messaggi si impernia ogni normale programma windows, che dovrà anzi essere, di norma, strutturato allo scopo, "ad eventi" (in questo contesto, un evento è sinonimo di "messaggio").

Un messaggio, in certi contesti, esiste come singola struttura MSG:

typedef struct MSG { HWND hWnd; // finestra di destinazione UINT message; // codice del messaggio WPARAM wParam; // 1o parametro LPARAM lParam; // 2o parametro DWORD time; // istante di spedizione POINT pt; // coordinate del mouse } MSG;

I campi time e pt non sono normalmente usati; i campi wParam ed lParam sono arbitrari "token", di 32 bit l'uno, con un significato interamente dipendente dal contesto (cioè dal tipo di messaggio) -- la distinzione fra i loro tipi ha ragioni puramente storiche; hWnd, quando una finestra riceve un messaggio, sarà appunto la handle della finestra stessa.

Il codice message sarà normalmente una macro del tipo WM_QUALCOSA, per i normali "messaggi di Windows"; sono definiti centinaia di questi codici. Inoltre, si possono definire ulteriori codici di messaggio per i propri scopi, e le varie classi di finestre predefinite di Windows ("controlli comuni") lo fanno.

Più spesso, vedremo un messaggio in termini dei quattro valori hwnd, message, wParam, ed lParam, "spediti" o "ricevuti" separatamente, come argomenti di una funzione (API, o funzione da noi scritta).

Perchè, dunque, il nostro dialogo non termina quando l'utente clicca sulla crocetta in altro a destra? Perchè, a fronte di questo evento, il sistema non risponde in modo automatico chiudendo la finestra: quello che fa, invece, è spedire un messaggio WM_CLOSE alla finestra, *chiedendole*, in pratica, di chiudersi. Questo schema è molto più flessibile, permettendo al programma di controllare il proprio comportamento con precisione.

Ma, nel nostro esempietto, noi non avevamo usato nessuna procedura che rispondesse ai messaggi; il WM_CLOSE, dunque, arriva, ma, poichè nessuno vi risponde, esso resta senza effetto.

Un dialogo risponde ai messaggi usando una dialog procedure -- una funzione il cui indirizzo viene passato al sistema, alla creazione del dialogo, nel parametro di tipo DLGPROC della API DialogBoxParam. La dialog procedure deve avere questa forma...:

BOOL CALLBACK DialogProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) { // eventuali azioni return FALSE; }

Quando il sistema chiama la dialog procedure, essa riceve come argomenti l'HWND del dialogo, il codice di messaggio in uMsg, i parametri in wParam ed lParam; deve normalmente tornare FALSE se non ha già interamente gestito il messaggio (e vuole che il sistema "completi" la gestione stessa), e TRUE se e solo se ha già interamente gestito il messaggio in proprio.

Per far sì che il nostro dialogo vuoto si chiuda, come è normale, al "clic" dell'utente sulla crocetta in alto a destra, basta dunque avere una dialog procedure come, ad esempio:

BOOL CALLBACK unaDlgProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) { if(uMsg==WM_CLOSE) { EndDialog(hwndDlg, 0); return TRUE; } return FALSE; }

L'API EndDialog termina un dialogo (la cui HWND essa riceve come primo parametro) con un "codice di ritorno" pari al secondo parametro (qui usiamo zero, per indicare "tutto OK"); il valore passato come secondo parametro di EndDialog è anche tornato come risultato complessivo dalla chiamata DialogBoxParam. In questa dialog procedure, dunque, noi gestiamo il solo messaggio WM_CLOSE, cui rispondiamo con la terminazione del dialogo -- poi, restituiamo TRUE, per dire al sistema che abbiamo interamente gestito noi il messaggio stesso.

Con questa dialog procedure, e le stesse risorse di prima, il nostro programmino diventa dunque:

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { return DialogBoxParam(0,"MioDialogo", 0,unaDlgProc,0); }

Per trasformare questo "scheletrico" programma in uno che faccia qualcosa che sia un minimo interessante, occorre, sostanzialmente:

Sarà dunque lungo questi due "assi" che ci muoveremo nel prossimo capitolo.


Capitolo 4: dialoghi
Capitolo 6: controlli: gli STATIC
Elenco dei capitoli