Introduzione alle API Win32

Capitolo 13: message crackers

Se le varie "funzioni di comodo" (e relative macro) ci risparmiano un po' di lavoro, e facilitano l'uso dei tipi, per spedire messaggi, non si potrebbe considerare l'uso di qualcosa di analogo anche per il compito di ricevere i messaggi stessi...?

La risposta è "sì", e lo stesso header file windowsx.h, già visto per le macro di spedizione, ne fornisce una versione: i "message crackers".

Ad esempio, la macro HANDLE_WM_COMMAND, definita in windowsx.h, accetta quattro parametri -- i tre classici hwnd, wParam, lParam di un messaggio di command, e, come quarto, il nome di una funzione che deve essere simile, nomi a parte, a:

void Gestisci_Comando(HWND hDialogo, int idControllo, // id del controllo HWND hwndControllo, // hwnd del controllo UINT uCodiceNotifica // sottocodice di comando );

HANDLE_WM_COMMAND "spezza" il messaggio, nel senso dei vari cast, LOWORD, HIWORD, ecc, ecc, e chiama l'apposita funzione (suo quarto argomento).

Questo ci permette di riscrivere la TreControlli vista al capitolo precedente nella seguente forma:

void On_Comando(HWND hDialogo, int idCliccato, HWND hCliccato, UINT codice) { // i soliti controlli if(codice==BN_CLICKED && (idCliccato==IDC_PRIMO || idCliccato==IDC_SECONDO)) // l'operazione, se i controlli sono OK SetDlgItemText(hDialogo, IDC_SCRITTA, idCliccato==IDC_PRIMO?"Primo":"Secondo"); }

Questa forma non permette di tornare un codice d'errore (cioè di messaggio non gestito), ma, quando applicabile, è sicuramente più comoda e chiara, permettendo di ragionare al livello semantico un poco più alto di id e codici invece di doverseli ricavare ogni volta a colpi di cast, HIWORD, ecc.

"Liberandoci" dai dettagli dell'esatta codifica dei campi semanticamente significativi (idCliccato, ecc) nei campi-dati "di trasporto" (wParam, ecc), i message cracker migliorano inoltre la nostra portabilità: originalmente pensati per facilitare la migrazione/coesistenza fra versioni Windows a 16 e 32 bit, potranno, un domani, tornar magari comodi anche nella migrazione 32->64.

windowsx.h comprende anche una macro che facilita la scrittura dell'intera dialog procedure, con l'approccio "semi-ingenuo" accennato nel capitolo 9 (mappa messaggio->gestore che è codificata rigidamente nel codice eseguibile, ma gestione di ciascun messaggio in una sua funzione apposita):

#define HANDLE_MSG(hwnd, message, fn) \ case (message): \ return HANDLE_##message((hwnd), (wParam), (lParam), (fn))

L'idea è che si può scrivere direttamente, proprio come "corpo" della dialog procedure:

switch(uMessage) { HANDLE_MSG(hDialog,WM_CLOSE,OnClose) HANDLE_MSG(hDialog,WM_COMMAND,OnCommand) } lasciando implicito il test (case) sul messaggio, ed il passaggio dei parametri wParam ed lParam, al cracker più opportuno. Questa soluzione non è particolarmente convincente, per quanto abbiamo già detto sull'approccio "semi-ingenuo", anche se è, ovviamente, molto superiore all'approccio "totalmente ingenuo" (gestire ogni messaggio nel corpo della dialog procedure!), e forse accettabile per casi particolarmente semplici, come questo che abbiamo testè esemplificato.

Se mai, potrebbe essere meglio definirci noi stessi una macro analoga, che metta la corrispondenza messaggio->funzione in una struttura dati, e un'altra che, "frugando" la struttura dati alla ricerca dello specifico messaggio, usi, quando lo trova, il cracker adatto; la dialog procedure userebbe quest'ultima, mentre la struttura verrebbe costruita, ad esempio nel WinMain, come parte dell'inizializzazione del sistema.

 

windowsx.h contiene parecchie altre macro "di comodo", che si possono usare o meno a seconda dei gusti. Ad esempio, chi non ami il nome poco pronunciabile della importante API GetDlgCtrlID, può usare al suo posto il nome GetWindowID, definito in windowsx.h; normalmente, "rinominare" in questo modo importanti funzioni di sistema per pure considerazioni "estetiche" sarebbe una prassi di valore molto dubbio, e certamente da sconsigliare, ma, in questo caso, essendo il #define a sua volta in un file di sistema, si può ammettere di considerare questo come quasi un "sinonimo" ufficiale.

Più interessante, comunque, la macro di windowsx.h:

SetDlgMsgResult(hwnd, msg, result) da chiamarsi, di solito, nello specifico contesto: return SetDlgMsgResult(hDialogo, WM_QUALCOSA, unRisultato); per uscire dalla dialog procedure con un "risultato", che non sia solo TRUE o FALSE, a fronte di un certo messaggio WM_QUALCOSA. I meccanismi che questa macro utilizza internamente, esaminabili, chiaramente, dalla lettura di windowsx.h, suggeriamo, per il momento, di considerarli "pura magia nera" (e non si andrà troppo lontani dalla realtà...!-); li esamineremo, tuttavia, più avanti nel corso di questo tutorial.

Capitolo 12: ID e HWND
Capitolo 14: un esempio (1)
Elenco dei capitoli