Abbiamo già dedicato parecchia energia, al
Capitolo 9,
ad esaminare come "smistare" i
messaggi, portandoli tutti alla "destinazione"
di un'apposita funzione (invece di gestirli tutti,
come potrebbe venire spontaneo ma sarebbe poco
furbo fare, nella Dialog Procedure, entro un grosso
switch
o altro costrutto equivalente).
Ma ci sono alcuni
altri problemi sull'uso dei messaggi, sia in
spedizione, sia in ricezione; in questo capitolo, e
nel successivo,
accenneremo ad alcuni aiuti che gli header di
sistema di Windows ci possono offrire in merito.
Anzitutto, nei meccanismi "di fondo" e "unificanti" dei messaggi di Windows, c'è un problema di tipi. Ogni messaggio si porta dietro "due token di 32 bit ciascuno"; ciascuno può "trasportare" un puntatore, una handle, un intero, magari due, ma "intrinsecamente" non ha davvero in sè e per sè alcun tipo. In pratica, per preparare e per usare questi messaggi abbiamo quasi sempre bisogno di fare dei "cast" C (prassi già di per sè assai spiacevole!), e, non di rado, anche di impacchettare o spacchettare "parole" ("word", interi di 16 bit). Questo è un livello di accesso molto basso, cioè vicinissimo alla macchina e lontanissimo dai problemi che è nostro interesse risolvere; lo indica molto chiaramente la continua violazione del sistema dei tipi che i "cast" implicano.
Fortunatamente, c'è una semplice via d'uscita.
Accettiamo pure il fatto
che ogni messaggio viene, in
realtà, sempre "spedito" da SendMessage
[in
realtà, c'è un'importante alternativa, la API
PostMessage
, che esamineremo più avanti],
con due parametri di tipo WPARAM
e LPARAM
(ciascuno di 32 bit) e un valore di ritorno di tipo
LRESULT
(idem); accettiamo pure che esso
viene "ricevuto" da una Dialog Procedure negli
stessi termini (in realtà, una Dialog Procedure
ritorna BOOL
, cioè TRUE
o
FALSE
, non LRESULT
,
ma vedremo più avanti, al prossimo
capitolo, come essa possa
in realtà "ritornare" il suo vero risultato) -- questi
sono fra gli
aspetti più fondamentali di Windows.
Ma nessuno ci obbliga, una volta presone atto, e ben acquisita questa conoscenza, a restarne "sempre" pienamente coscienti... e a subire tutti i possibili errori che un "cast" può mascherare al nostro compilatore! Perchè non "nascondere" questa "realtà" dei messaggi, e dei tipi ad essi connessi (o meglio, della mancanza di veri e propri tipi, da parte dei "veri" parametri che un messaggio porta con sè), dietro un semplice "schermo" di costo basso o nullo...?
Per esempio, abbiamo accennato che, per cambiare
lo stato "scelto/non scelto" di un BUTTON
stile
checkbox o radiobutton, gli si spedisce il messaggio
BM_SETCHECK
, con WPARAM
pari rigorosamente a
0
(per togliere la "spuntatura", o "pallino" che sia)
o a 1
(per metterla), e
LPARAM
pari a 0
. Allora,
perchè non "incapsulare" tutto questo in macro
del tipo...:
Non potremmo scriverci una "batteria" di simili macro, compresi tutti i cast necessari, ficcarla in un qualche nostro header file, e poi "scordarci" che sono macro, e "chiamarle" come e quando occorra...?
Potremmo, ma non serve: qualcuno altro l'ha già fatto per noi! Precisamente, quel qualcuno è la Microsoft, che, nell'header file <windowsx.h> incluso nell'SDK, offre appunto, fra l'altro, una piccola batteria di macro del genere, per spedire messaggi "mirati" ai vari controlli elementari. Le macro in questione presentano al programmatore una sintassi, e in certa misura gli permettono di usare dei tipi di parametri, meno "ostici" di quanto i bassi livelli delle API Windows non richiedano.
wParam
pari
sempre a 0
o a 1
-- e
questo, per ragioni di generalità relativamente
al suo uso con i
button "3-state"), ma il vantaggio è
che tutte queste macro sono già fatte nel file
di header di sistema
<windowsx.h>, pronte per essere riusate
nei nostri programmi senza "reinventare l'acqua
calda"!
Altri simili "rivestimenti" di messaggi, con "costo"
di uso molto basso (sia pure non lo "zero assoluto"
implicito in una macro di preprocessore!) li ha fatti
sempre la Microsoft, e li offre come parte delle
API di Win32. Ad esempio,
abbiamo visto che, per cambiare il testo di una
finestra, le si invia il messaggio WM_SETTEXT
;
ebbene, c'è un'alternativa praticamente equivalente:
chiamare invece la API...:
Questo tipo di "funzioni di sottile rivestimento",
rispetto alla semplice spedizione di messaggi,
ha, come dicevamo, un minimo di costo (è pur
sempre una funzione, quindi qualche istruzione
di macchina -- qualche nanosecondo -- è speso
per chiamata-e-ritorno), ma anche un qualche
piccolo vantaggio; ad esempio, SetWindowText
"armonizza" i valori di ritorno in caso di errore
(che potrebbero essere diversi per WM_SETTEXT
spedita a diverse classi di controllo o finestra),
tornando sempre FALSE
per qualsiasi errore
(e, naturalmente, TRUE
se ha successo). La
"sicurezza dei tipi" è, inoltre, salvaguardata
meglio dall'uso di queste funzioni, rispetto a
quello di macro equivalenti.
In generale, è meglio usare questi "rivestimenti",
macro o funzioni che siano, che non ricorrere alla
pura e semplice SendMessage
(per quei messaggi,
naturalmente, per cui un tale
"rivestimento" È previsto!).
I pochi nanosecondi da "pagare" costituiscono infatti,
tutto sommato, un prezzo estremamente modesto.
A scopi didattici, quali sono appunto quelli di questo tutorial, potremo, tuttavia, continuare ad indicare i messaggi come substrato fondamentale della comunicazione con i controlli, lasciando alla diligenza del lettore la ricerca delle API direttamente equivalenti, quando esse esistano, ovvero delle relative macro di ricoprimento, rispettivamente sui manuali di riferimento delle API (ad esempio, l'MSDN) e il già nominato header file windowsx.h.
Capitolo 10: controlli: i BUTTON
Capitolo 12: ID e HWND
Elenco dei capitoli