Introduzione alle API Win32

Capitolo 23: un piccolo esperimento

Il lettore che abbia seguito il nostro consiglio, provando a scrivere il codice del programmino esplorativo relativo agli stili degli edit box, sulla base di quanto sin qua abbiamo esposto, potrebbe avere prodotto qualcosa del genere (supponiamo, per comodità, in un singolo file sorgente, con le solite convenzioni, e usando, ad esempio, i cracker di WindowsX):

// esper.c #define STRICT #define WIN32_LEAN_AND_MEAN #include <windows.h> #include <windowsx.h> #include "resource.h" // chiusura di un generico dialogo modale void OnDlgClose(HWND hWnd) { EndDialog(hWnd,0); } // torna lo stile di un controllo di un dialogo LONG getStyle(HWND hDlg, int idCtl) { return GetWindowLong(GetDlgItem(hDlg, idCtl), GWL_STYLE); } // cambia lo stile di un controllo di un dialogo void setStyle(HWND hDlg, int idCtl, LONG styl) { HWND hCtl = GetDlgItem(hDlg, idCtl); SetWindowLong(hCtl, GWL_STYLE, styl); } // mostra lo stile di un controllo di un dialogo, in // formato esadecimale, come testo di un altro controllo // dello stesso dialogo void showStyle(HWND hDlg, int idCtl, int idShow) { LONG styl = getStyle(hDlg, idCtl); char buf[12]; wsprintf(buf,"0x%8.8x",styl); SetDlgItemText(hDlg, idShow, buf); } // torna il bit di stile rappresentato da un bottone, // o 0 per finestre che non rappresentano tali bit LONG getStylebit(HWND hwnd, LONG refproc) { // ignora i non-button LONG winproc = GetWindowLong(hwnd, GWL_WNDPROC); if(winproc != refproc) return 0; // ottiene l'ID int id = GetDlgCtrlID(hwnd); // caso speciale di codifica if(id & 0x8000) id = (id&~0x8000) << 20; return id; } BOOL OnDlgInitDialog(HWND hwnd, HWND hwndFocus, LPARAM lParam) { // mostra lo stile dell'edit (in esadecimale) showStyle(hwnd, IDC_EDIT, IDC_STIL); // ottiene la winproc di un bottone HWND hButton = GetDlgItem(hwnd, ES_READONLY); LONG refproc = GetWindowLong(hButton, GWL_WNDPROC); // la ricorda per il futuro SetWindowLong(hwnd, DWL_USER, refproc); return TRUE; } void OnDlgCommand(HWND hwnd, int id, HWND hwndCtl, UINT codeNotify) { // gestisce solo clic su bottoni if(codeNotify != BN_CLICKED) return; // recupera la winproc dei button LONG refproc = GetWindowLong(hwnd, DWL_USER); // modifica lo stile come specificato LONG sb = getStylebit(hwndCtl,refproc); LONG styl = getStyle(hwnd,IDC_EDIT); if(IsDlgButtonChecked(hwnd,id)==BST_CHECKED) { styl |= sb; } else { styl &= ~sb; } setStyle(hwnd,IDC_EDIT,styl); // mostra nuovamente lo stile in esadecimale showStyle(hwnd, IDC_EDIT, IDC_STIL); } BOOL CALLBACK DialogProc( HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam ) { switch(uMsg) { HANDLE_MSG(hwndDlg,WM_CLOSE,OnDlgClose); HANDLE_MSG(hwndDlg,WM_INITDIALOG,OnDlgInitDialog); HANDLE_MSG(hwndDlg,WM_COMMAND,OnDlgCommand); } return FALSE; } int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { DialogBoxParam(hInstance, MAKEINTRESOURCE(IDD_DIALOG1), 0, DialogProc, 0); return 0; }

L'unica "finezza", se tale è, si accentra sulla getStylebit, che è architettata, oltre che per decodificare lo stile dall'id del controllo sulla base dell'eventuale bit 0x8000, anche per dare sempre 0 per eventuali non-bottoni; a questo scopo, verifichiamo che la window procedure del controllo in esame sia proprio quella dei bottoni (che, per comodità, registriamo una volta per tutte alla partenza come dato del dialogo -- non ci sarebbe, ovviamente, nulla di errato nel recuperarla invece ogni volta, da una qualsiasi finestra che "sappiamo" essere sicuramente null'altro che un bottone).

Eseguendo questo programma, osserviamo che lo stile iniziale dell'edit è 0x50010000 (da un esame di winuser.h, possiamo confermare che questo corrisponde ai tre bit di stile:

WS_CHILD|WS_VISIBLE|WS_TABSTOP il che, in effetti, appare abbastanza sensato), e che possiamo modificarlo come previsto facendo cambiare lo stato delle varie checkbox.

Purtroppo, osserviamo anche che quasi tutti questi cambiamenti di bit di stile non hanno effetto alcuno...! (Fra le poche eccezioni, ES_LOWERCASE ed ES_UPPERCASE, che effettivamente agiscono sul testo immesso mentre sono settati, benchè, cosa tutto sommato ragionevole, non sul testo preesistente).

Se il lettore che ha osservato questo effetto ha anche provato, per rimediarlo, a studiare la documentazione dell'SDK di Windows, è anche possibile che abbia notato una breve osservazione presente nella doc della SetWindowLong: "alcuni bit di stile sono in cache, così che i cambiamenti fatti con SetWindowLong non avranno effetto sinchè non si chiama la funzione SetWindowPos". Provando a seguire questa indicazione, l'ipotetico, diligente lettore può aver provato a rimediare al difetto con l'aggiunta di una chiamata, appunto, a detta API:

SetWindowPos(hCtl, 0,0,0,0,0, SWP_FRAMECHANGED| SWP_NOMOVE|SWP_NOSIZE|SWP_NOZORDER ); (nel codice come lo abbiamo appena presentato, questa chiamata andrebbe naturalmente entro la funzione setStyle, subito dopo la chiamata a SetWindowLong e subito prima della fine della setStyle). Questa è una chiamata a SetWindowPos che non fa nessun cambiamento (grazie ai vari bit SWP_NOxxx), ma dice alla finestra che qualcosa è cambiato (grazie all'apposito bit d'opzione SWP_FRAMECHANGED).

Qualcosa, in effetti, migliora; ad esempio, i bit di WS_HSCROLL e WS_VSCROLL adesso hanno effetto, facendo apparire, se settati, le scroll bar desiderate. Ma la maggior parte dei bit di stato resta invece desolantemente priva di effetti, per quanto noi possiamo settarli.

Purtroppo, la causa di questo "non-comportamento" è mal documentata ma sicuramente vera: non pochi dei bit di stile dei controlli di Windows sono, così ci dice l'osservazione, esaminati dal controllo soltanto in partenza; dopo che il controllo è stato creato, non tiene più conto di eventuali modifiche a questi bit. Ciò è sicuramente più efficiente, in quanto, in questo modo, la window procedure del controllo non ha bisogno di esaminare quei bit ad ogni messaggio che riceve, bensì solo alla creazione del controllo stesso... ma, come possiamo noi allora ottenere invece il "dinamismo" che ci serve per vedere in opera tutti i vari bit di stile al semplice clic su di una checkbox...?

Una soluzione c'è, benchè forse poco intuitiva; essa si basa, d'altronde, su di un'API che ancora non abbiamo visto, per quanto l'esistenza di qualcosa del genere debba poter essere considerata "implicita" in tutte le operazioni di Windows. Invitando il lettore a riflettere su quale possa essere l'API di cui stiamo parlando, promettiamo, ancora una volta, "il seguito alla prossima puntata"!


Capitolo 22: controlli: gli EDIT
Capitolo 24: creazione di finestre
Elenco dei capitoli