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
#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:
- identificare le funzionalità riusabili, e impacchettarle
separatamente -- l'inizio della nostra personale
"libreria di comode funzioni per usare la Win32 API"!
- tenere invece in file specifici quelle funzioni che sono
inestricabilmente legate a una singola applicazione
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
#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
#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
#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
#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