Introduzione alle API Win32

Capitolo 19: l'esempio rivisitato (1)

Nei prossimi capitoli, riorganizzeremo tutti i sorgenti del nostro programmino di esempio, risolvendo, inoltre, il problema della "variabile globale" che ci rimaneva (e un paio di cosette che negli ultimi capitoli abbiamo lasciato come "esercizi per il lettore").

Nessun problema dovrebbe presentarsi a questo punto, spero bene, per capire l'header file delle risorse, resource.h:

// resource.h: definizioni degli identificatori // delle risorse del nostro programma #ifndef _RESOURCE_H #define _RESOURCE_H // dialoghi #define IDD_DIALOG1 101 // controlli #define IDC_BUTTON1 1000 #define IDC_BUTTON2 1001 #define IDC_BUTTON3 1002 #define IDC_BUTTON4 1003 #define IDC_STICON 1004 #define IDC_LIMITS 1005 #endif e neppure per il nostro file di risorse, esem1.rc: // esem1.rc: risorse (dialogo e suoi controlli) per il programma di // esempio n. 1 del tutorial API Win -- (C) alex@magenta.com 1998-1999 #include <windows.h> #include "resource.h" IDD_DIALOG1 DIALOG 100, 100, 180, 180 STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU CAPTION "Grafica a go-go!" FONT 8, "MS Sans Serif" BEGIN AUTOCHECKBOX "Movimento limitato?",IDC_LIMITS,0,0,70,15,WS_CLIPSIBLINGS PUSHBUTTON "1",IDC_BUTTON1,140,20,20,20,BS_BITMAP|WS_CLIPSIBLINGS PUSHBUTTON "2",IDC_BUTTON2,160,20,20,20,BS_BITMAP|WS_CLIPSIBLINGS PUSHBUTTON "3",IDC_BUTTON3,150,0,20,20,BS_BITMAP|WS_CLIPSIBLINGS PUSHBUTTON "4",IDC_BUTTON4,150,40,20,20,BS_BITMAP|WS_CLIPSIBLINGS ICON "",IDC_STICON,80,80,20,20,WS_CLIPSIBLINGS END

Veniamo al codice C vero e proprio (con il solito discorso di usare due comodità del C++: i commenti con //, e la possibilità di fare la dichiarazione di una variabile nel punto in cui serve -- per tradurre in C puro, cambiare i commenti in /* */ e spostare tutte le varie dichiarazioni di variabile all'inizio di ciascun blocco).

Qui, l'approccio fondamentale deve chiaramente essere:

Il codice principale (WinMain) è abbastanza riusabile, almeno fra tutti i programmi Windows che non fanno altro che mostrare un dialogo e gestire l'interazione con esso dell'utente; per semplicità, supporremo qui che l'ID del dialogo sia sempre IDD_DIALOG1, ma potrebbe essere interessante anche "impacchettare" il dialogo (comprese le sue risorse) in una DLL, avere un WinMain che riceve il nome di quella DLL in linea comandi, la carica, eccetera. Comunque, per ora, ecco il file sorgente del nostro WinMain, esem1.c:

#define STRICT #define WIN32_LEAN_AND_MEAN #include <windows.h> #include "resource.h" #include "esemdlg.h" // // programma Windows "dialog-based": mostra un // dialogo modale (risorsa IDD_DIALOG1, dialog // procedure DialogProc), poi termina // int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { DialogBoxParam(hInstance, MAKEINTRESOURCE(IDD_DIALOG1), 0, DialogProc, 0); return 0; }

Il riuso sarà possibile, se non altro, con "copia e incolla"; abbiamo comunque isolato la dichiarazione della dialog procedure nel file di header esemdlg.h:

// generica dialog-procedure BOOL CALLBACK DialogProc( HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam ); giusto per seguire il normale uso volto a garantire l'identità di prototipo fra dichiarazione e definizione di una funzione (includeremo, naturalmente, questo stesso file di header anche nel file sorgente che implementa questa dialog procedure).

Alcune delle funzioni che abbiamo già scritto sono chiaramente riusabili, e indipendenti fra di loro, e le impacchetteremo dunque ciascuna nel proprio file sorgente, con un file di header per ciascuna, che dichiara la funzione.

// dlgclose.h // generica chiusura di dialogo modale void OnDlgClose(HWND hWnd);

// dlgclose.c #define STRICT #define WIN32_LEAN_AND_MEAN #include <windows.h> #include "dlgclose.h" // generica chiusura di dialogo modale void OnDlgClose(HWND hWnd) { EndDialog(hWnd,0); }

Funzionalità banale, certo, ma, avendola messa nella nostra "libreria", non dovremo comunque più riscriverla!

Forse un pochino meno banale:

// windmove.h // spostamento relativo, senza deformazioni, di una // finestra; facoltativamente, la mantiene entro un certo // rettangolo (se pRectLimite==0, cio` non si applica) BOOL WindMove(HWND hwnd, int dx, int dy, LPRECT pRectLimite); // windmove.c #define STRICT #define WIN32_LEAN_AND_MEAN #include <windows.h> #include "windmove.h" // // spostamento relativo, senza deformazioni, di una // finestra; facoltativamente la mantiene entro un certo // rettangolo (se pRectLimite==0, cio` non si applica) // BOOL WindMove(HWND hwnd, int dx, int dy, LPRECT pRectLimite) { // scopre la posizione attuale WINDOWPLACEMENT wp; ZeroMemory(&wp, sizeof(wp)); wp.length = sizeof(wp); if(!GetWindowPlacement(hwnd, &wp)) return FALSE; // applica lo spostamento relativo wp.rcNormalPosition.left += dx; wp.rcNormalPosition.right += dx; wp.rcNormalPosition.top += dy; wp.rcNormalPosition.bottom += dy; if(pRectLimite) { // c'e` un rettangolo-limite, verifichiamolo: int dd = wp.rcNormalPosition.left - pRectLimite->left; if(dd<0) { // la finestra sporge a sinistra wp.rcNormalPosition.left -= dd; wp.rcNormalPosition.right -= dd; } else { dd = wp.rcNormalPosition.right - pRectLimite->right; if(dd>0) { // la finestra sporge a destra wp.rcNormalPosition.left -= dd; wp.rcNormalPosition.right -= dd; } } dd = wp.rcNormalPosition.top - pRectLimite->top; if(dd<0) { // la finestra sporge "dall'alto" wp.rcNormalPosition.top -= dd; wp.rcNormalPosition.bottom -= dd; } else { // la finestra sporge "dal basso" dd = wp.rcNormalPosition.bottom - pRectLimite->bottom; if(dd>0) { wp.rcNormalPosition.top -= dd; wp.rcNormalPosition.bottom -= dd; } } } // stabilisce la nuova posizione della finestra return SetWindowPlacement(hwnd, &wp); }

Codice chiaramente riusabile è anche quello che abbiamo usato per associare bitmap ai bottoni, e icona allo static. Altrettanto chiaramente, entrambi i tipi di immagini e di controlli ci possono servire, quindi ci ritroviamo con quattro combinazioni (e si può cominciare a ragionare su come "fonderle" per evitare questa moltiplicazione...!):

// ctlimmag.h void Mostra_Icona_static(const char* ic_cod, HWND hDlg, int sta_cod); void Mostra_Icona_bottone(const char* ic_cod, HWND hDlg, int sta_cod); void Mostra_BM_static(int bm_cod, HWND hDlg, int but_cod); void Mostra_BM_bottone(int bm_cod, HWND hDlg, int but_cod); // ctlimmag.c #define STRICT #define WIN32_LEAN_AND_MEAN #include <windows.h> #include "ctlimmag.h" void Mostra_Icona_static(const char* ic_cod, HWND hDlg, int sta_cod) { HWND hSta = GetDlgItem(hDlg, sta_cod); HICON hi = LoadIcon(0,ic_cod); SendMessage(hSta,STM_SETICON,(WPARAM)hi,0); } void Mostra_Icona_bottone(const char* ic_cod, HWND hDlg, int but_cod) { HWND hBut = GetDlgItem(hDlg, but_cod); HICON hi = LoadIcon(0,ic_cod); SendMessage(hBut,BM_SETIMAGE,IMAGE_ICON,(LPARAM)hi); } void Mostra_BM_static(int bm_cod, HWND hDlg, int sta_cod) { HWND hSta = GetDlgItem(hDlg, sta_cod); HBITMAP hi = LoadBitmap(0,MAKEINTRESOURCE(bm_cod)); SendMessage(hSta,STM_SETIMAGE,IMAGE_BITMAP,(LPARAM)hi); } void Mostra_BM_bottone(int bm_cod, HWND hDlg, int but_cod) { HWND hBut = GetDlgItem(hDlg, but_cod); HBITMAP hi = LoadBitmap(0,MAKEINTRESOURCE(bm_cod)); SendMessage(hBut,BM_SETIMAGE,IMAGE_BITMAP,(LPARAM)hi); }

Ci restano da vedere il codice vero e proprio del dialogo, e la "piccola" (ma, importante!) questione del subclassing senza uso di variabili globali -- di nuovo, dunque, "il seguito alla prossima puntata"!-)


Capitolo 18: timer e subclassing
Capitolo 20: un subclassing migliore
Elenco dei capitoli