Parlando di spedizione di messaggi, è il
caso di evidenziare un'altra particolarità. I messaggi
(almeno per quanto abbiamo visto sino ad ora!) si
spediscono sempre ad una finestra, e richiedono,
quindi, l'HWND
di quella finestra. Tutto bene, dunque,
se l'HWND
della finestra ci viene resa disponibile nel
contesto in cui vogliamo interagire con essa -- ad
esempio, come abbiamo visto nel capitolo 6, quando,
a fronte di un WM_COMMAND
, vogliamo agire sulla
finestra (controllo) che ce l'ha spedito, poichè la
HWND
di quel controllo è l'lParam
del messaggio stesso.
Ma spesso, naturalmente, a fronte di eventi su di un
certo controllo X, noi vorremo agire su di un diverso
controllo Y; ad esempio, cambiare la scritta su di uno
STATIC
, a fronte di clic, non sullo static stesso, ma
su di un certo BUTTON
con stile pushbutton. Come si fa,
allora, a recuperare l'HWND
dello STATIC
, quando
nel messaggio WM_COMMAND
ci arriva invece
quella del BUTTON
...?
Dello STATIC, e in generale dei nostri controlli, noi
conosciamo a tempo di compilazione l'ID -- quel
numerello che (attraverso, come abbiamo spiegato,
un #define
in resource.h) abbiamo associato al
controllo. L'HWND
, invece, cambia ad ogni diversa
esecuzione del programma -- non c'è modo di
determinarla a priori, una volta per tutte, mentre il
programma viene compilato!
Windows, naturalmente, ci fornisce la funzionalità
richiesta: risalire dall'ID
di un controllo alla
sua HWND
(e viceversa, avendo una HWND
,
ottenere l'ID
del controllo cui essa corrisponde).
Le API fondamentali per questo compito sono:
HWND
desiderata, o 0
se non esiste in
quel dialogo un controllo con quell'ID
;
ID
conoscendo l'HWND
del controllo (non è
valido chiamare questa API su di una HWND
che
non sia un controllo; in questo caso l'API dovrebbe
tornare 0 -- purtroppo, questa segnalazione di
errore non è garantita su tutte le versioni di
Windows nè in tutti i casi).
HWND
passatale come argomento).
Grazie alla presenza di queste API, non abbiamo
bisogno di "registrarci" da nessuna parte queste
fondamentali relazioni
ID<->HWND<->
genitore:
possiamo sempre recuperarle dinamicamente a
seconda delle esigenze. Nulla ci impedisce in
effetti di "salvarci" da qualche parte una copia di
queste info, ma non è strategia particolarmente
consigliabile: il "costo" in termini di tempo per
recuperarle è molto modesto, da bilanciare contro
il "costo" in termini di spazio di salvarle da qualche
parte (e in un sistema a memoria virtuale, dobbiamo
ricordarlo, sprecare spazio significa alla fin fine
sprecare tempo!); e soprattutto, per principio generale, è
meglio non tenere copie duplicate e ridondanti
di nessun tipo di informazioni, onde evitare errori
di "disallineamento" fra le diverse "versioni" delle
informazioni stesse.
(I lettori attenti di questo tutorial noteranno che tendo a dare una certa enfasi a questo principio della "non duplicazione delle informazioni" [la mia strategia consiste nel presentare ripetutamente questo principio, con una presentazione appena un poco diversa ogni volta -- non si tratta qui di una "duplicazione" quale, appunto, deploro, bensì di una applicazione del ben noto principio "repetita iuvant"!-]. Questa enfasi sul principio di "non duplicare" la pongo proprio perchè, come per tanti altri buoni principi generali di programmazione, è facile trovarsi a trascurarlo, per ignoranza, pigrizia, o distrazione, ma, quando lo si trascura, alla fine il prezzo si paga, eccome!, anche se non è sempre ovvio che lo si sta pagando.)
Ecco, dunque, un esempio di gestore di WM_COMMAND
che interagisce con molteplici controlli: se viene
cliccato il pushbutton di identificatore IDC_PRIMO
,
il gestore
scrive, nello STATIC
di identificatore IDC_SCRITTA
,
il testo
"Primo" -- se invece viene cliccato quello di identificatore
IDC_SECONDO
, scrive, nello stesso STATIC
, il testo
"Secondo"
(è lasciata come esercizio al lettore la preparazione del
file .RC appropriato, e l'"incastro" nella dialog procedure
del codice per determinare che questo è il gestore da
eseguire). Come abbiamo accennato, usiamo due
piccole comodità
del C++: commenti con //, e dichiarazione delle variabili
solo quando servono; per usare C puro, cambiare tutti i
commenti alla forma /* ... */, e spostare tutte le dichiarazioni
all'inizio della funzione.
Abbiamo qui usato la suaccennata funzione SetWindowText
,
invece che spedire un messaggio WM_SETTEXT
, se non
altro per la comodità di averne già un ritorno TRUE
, o
FALSE
, direttamente usabile come risultato da ritornare
dal nostro gestore.
Windows offre ulteriori funzioni API che possono tornar comode,
benchè non siano certo indispensabili, per interagire
con i controlli di un dialogo. Ad esempio, invece di trovare
l'HWND
con GetDlgItem
, poi settare il testo con la
SetWindowText
, si può direttamente chiamare l'API:
Il vantaggio è puramente uno di comodità, ma può valere la pena; le ultime tre istruzioni del nostro gestore diventerebbero dunque l'unica:
Similmente abbiamo altre "API di comodità", come la
GetDlgItemText
, SendDlgItemMessage
, eccetera; sono
comodità, certo piccole, ma non del tutto trascurabili,
quindi potremo farne uso.
Abbiamo anche "API di comodità" specializzate per certi tipi di controllo, come, per esempio:
GetDlgItem
seguito dalla SendMessage
di un BM_SETCHECK
,
BM_GETCHECK
, e l'API,
che incapsula funzionalità assai maggiori:
Quest'ultima equivale ad un ciclo sull'intero gruppo di radio-button, resettando tutti (senza pallino) con l'eccezione dell'unico scelto (cui il pallino viene invece messo).
L'ordine dei bottoni, nei cui termini determiniamo il
"primo" e l'"ultimo" del gruppo, è, lo ricordiamo,
l'ordine di creazione dei controlli entro il dialogo;
usando il file .RC, esso coincide con l'ordine in
cui ivi elenchiamo i bottoni fra le righe BEGIN
e
END
del dialogo (per le funzionalità "autoradio",
avremo normalmente anche il bit di stile WS_GROUP
nel primo radio del gruppo, e, se questi radio button
non sono gli ultimi del dialogo, sul primo del gruppo
ad essi successivo).
Per "navigare", dal nostro
codice, su questi "ordinamenti" e "raggruppamenti"
(cosa che non occorre certo fare di frequente, ma solo
per certe funzionalità avanzate), le API, di uso non
del tutto semplice, sono GetNextDlgGroupItem
e GetNextDlgTabItem
-- non le
approfondiremo
ulteriormente, in quanto inappropriate al contesto
di un semplice "tutorial" sulle API di Windows.
Capitolo 11: macro e API "di comodo"
Capitolo 13: message crackers
Elenco dei capitoli