API HANDLE E PROGRAMMAZIONE AVANZATA
Piccola premessa: in questo capitolo vi troverete di fronte argomenti del tutto nuovi,
tipici della programmazione C strutturata (cioè non orientata agli oggetti). Già con il
C++ molti dei concetti che seguono sono stati incapsulati in classi (MFC o OWL). Capirete
quindi che essendo il Delphi orientato alla logica RAD, i concetti di handle e API sono
stati COMPLETAMENTE sostituiti da oggetti VCL (Visual Component Library). Questo se da un
lato il programmatore è più libero, da un altro lato lo rende vincolato a classi scritte
da altri.
Questo è solo un discorso teorico, in realtà Delphi è talmente potente da poter gestire
anche le chiamate API più complesse, nonchè tutti quei concetti tipici della
programmazione Windows strutturata (funzioni callback per esempio).
Se avete quindi ancora problemi nello scrivere semplici applicazioni usando i mezzi che
Delphi vi propone non posso che consigliarvi di affinare meglio le vostre tecniche, questo
capitolo non fa per voi!
Un handle è un numero intero a 32-bit senza segno. Tutto qui!
Il Windows gestisce ogni oggetto (sia esso grafico come una finestra, sia esso hardware
come la periferica video) con un handle, ad esempio quando inseriamo un nuovo pulsante in
una form, questo ha necessariamente un handle.
E' quindi ovvio che Windows può gestire fino a 232 finestre
contemporameamente, non di più perchè questi sono gli handle che può allocare in
memoria.
Ma perchè dovremmo accedere ad un handle, quando Delphi ci mette a disposizione tutto
già pronto, ma soprattutto come si fa ad accedere ad un handle?
Riguardo alla prima domanda non c'è una risposta precisa, in questo capitolo proporrò
alcuni esempi dove per sopperire a limitazioni dell'ambiente Visuale si deve ricorrere a
queste tecniche più a basso livello (anche se è un termine improprio).
Per accedere ad un handle è invece semplicissimo, ogni oggetto ha una proprietà non
visibile da Object Inspector di nome handle!
Avete mai provato a nascondere l'icona sulla TaskBar di Win '95? Molto semplice non si
può fare in Delphi!
Per capire ciò si deve premettere che in Delphi esiste un oggetto Application che
contiene l'handle del pulsante sulla taskbar (non è propriamente così, ma per
semplificarvi le cose...;-) quindi basterebbe nascondere trovare una chiamata per
nascondere un oggetto grafico conoscendo il suo handle.
Proviamo quindi ad inserire in una form vuota un pulsante e nel codice click di questo il
seguente codice
ShowWindow(handle, SW_HIDE);
Eseguiamo il codice... magia alla pressione del tasto sparirà la finestra!
Se mi avete seguito attentamente avrete già capito che l'istruzione seguente
ShowWindow(Application.handle, SW_HIDE);
Farà sparire il pulsante sulla taskbar... provare per credere!
Ma cosè questa istruzione ShowWindow? E' per l'appunto una API che visualizza, minimizza,
massimizza o nasconde una finestra. Allora vi chiederete, il pulsante sulla taskbar è una
finestra? Non proprio, in realtà l'oggetto Application è costruito attorno ad una
finestra non visibile che serve solo ed esclusivamente per visualizzare il pulsante sotto,
nascondendo questa finestra tramite l'istruzione ShowWindow potremo fare sparire anche il
pulsante ad essa associato.
Provate, tanto per fare, anche le seguenti istruzioni:
ShowWindow(handle, SW_MINIMIZE);
ShowWindow(handle, SW_MAXIMIZE);
Che rispettivamente minimizzano e massimizzano la form. Non usate queste due istruzioni con Application.handle, vi avviso!
Non so se avete notato ma il titolo della finestra è "appiccicoso", sembra
che non esista possibilità di creare una finestra ridimensionabile senza di questo!
Ricorrendo però ad adeguate chiamate API...
Ogni finestra è costituita da due tipi di stile, lo stile e lo stile esteso (introdotto
da Windows 3.1).
Per selezionare uno stile basta usare l'API SetWindowLong passando come parametri l'handle
della finestra, GWL_STYLE nel caso dello stile, SWL_EXSTYLE nel caso dello stile esteso e
lo stile vero e proprio.
Al contrario l'API GetWindowLong può essere usata per recuperare lo stile usato
Ad esempio inseriamo tre pulsante in una form ognuno con il seguente codice nell'evento
click:
Button1 | var Stile: Integer; |
Button2 | begin |
Button3 | var Stile: Integer; |
Button4 | var Stile: Integer; |
Ora eseguito l'esempio e provate in sequenza i pulsanti. Poi riavviate il progetto e
provate i tasti in ordine sparso. Il risultato saranno delle Form con stile non
direttamente selezionabili in Delphi!
Vi potreste (giustamente) chiedere come assegnare uno stile alla finestra PRIMA che venga
creata. Per fare ciò dovremo sovrascrivere un metodo dell'oggetto TForm.
TIP |
![]() |
Se non vi è ben chiaro il concetto di sovrascrittura di un metodo vi consiglio la lettura del capitolo OOP-Object Oriented Programming. |
Per fare ciò dovremo sovrascrivere il metodo CreateParams, l'oggetto TForm1 sarà definito quindi come segue
type
TForm1 = class(TForm)
procedure CreateParams(var Params: TCreateParams); override;
private
{ Private declarations }
public
{ Public declarations }
end;
Ovviamente questo è solo in prototipo del metodo CreateParams, inserite quindi anche le seguenti righe di codice (nella sezione implementation, ma penso che avevate già capito)...
procedure TForm1.CreateParams(var Params: TCreateParams);
begin
inherited CreateParams(Params);
Params.ExStyle := Params.ExStyle or WS_EX_CLIENTEDGE;
end;
Il risultato sarà indentico alla pressione del tasto Button4, con l'unica differenza
che è stato fatto prima che la finestra fosse stata creata.
Ma cos'è questo metodo CreateParams? Altro non è che i parametri che verranno passati
alle API necessarie per creare la finestra vera e propria, quindi la sovrascrittura del
metodo stesso ci permette di manipolare quello che Delphi ci nasconde.
Quando minimizziamo una finestra di Windows questa mostra delle "ragnatele".
Purtroppo i progetti scritti in Delphi sembrano odiare questo gradevole effetto grafico.
Nella realtà l'oggetto Form che siamo soliti usare è costruito attorno all'API
CreateWindow, in altre parole ogni finestra che viene creata in Windows deve usare
quest'API. Gli stessi pulsanti, CheckBox e ListBox sono in realtà finestre che contengono
handle. Purtroppo gli sviluppatori di Delphi pr qualche criticabile ragione hanno deciso
di creare un oggetto Application che "sovrasta" tutte le Form del Delphi. Per
questo quando minimizziamo una Form nella realtà non viene minimizzata ma bensì nascosta
e quindi Win '95 non può visualizzare l'effetto grafico in questione.
Come venirne a capo? Semplicemente con qualche chiamata API ;-)
A conferma di quanto vi ho appena spiegato provate ad inserire in un pulsante la seguente
riga di codice:
ShowWindow(handle, SW_MINIMIZE);
Se avete ben capito l'uso di questa API la finestra dovrebbe venire minimizzata, e in
effetti questo avviene ma non nel modo corretto...
Il primo passo sarà quello di fare apparire nella taskbar il pulsante; per fare ciò
dovremo sovrascrivere nuovamente il metodo CreateParams (vedi sezione precedente).
Tralasciando il prototipo, il metodo vero e proprio sarà il seguente:
procedure TForm1.CreateParams(var Params: TCreateParams);
begin
inherited CreateParams(Params);
Params.WndParent := GetDeskTopWindow;
end;
In effetti apparirà un pulsante nella taskbar, ma questo certo non risolve il nostro
problema!
Per nascondere il vecchio pulsante nella taskbar basta proseguire come già spiegato nella
prima parte del capitolo, aggiungiamo quindi il seguente codice nell'evento OnFormCreate
della Form1:
procedure TForm1.FormCreate(Sender: TObject);
begin
Show;
ShowWindow(Application.handle, SW_HIDE);
end;
Siamo in dirittura di arrivo (enjoy ;-))... Adesso ci troviamo di fronte a due possibilità:
Il primo metodo è sicuramente più semplice, ma dato che noi siamo programmatori seri
procederemo con il secondo.
L'intercettazione di un messaggio in Delphi è piuttosto semplice, in effetti è molto
simile alla sovrascrittura di un metodo con l'unica eccezione che il nome della procedura
non è definito in alcun oggetto (lo possiamo quindi decidere noi stessi) e che il
parametro override andrà sostituito con message nomemessaggio.
L'oggetto TForm1 andrà quindi definito come segue:
type
TForm1 = class(TForm)
procedure CreateParams(var Params: TCreateParams); override;
procedure FormCreate(Sender: TObject);
private
{ Private declarations }
procedure WmSysCommand(var Msg: TWMSysCommand); message WM_SYSCOMMAND;
public
{ Public declarations }
end;
Se avete ben compreso la procedura WmSysCommand poteva anche venir denominata come
minimizza, non sarebbe cambiato nulla, invece è importante osservare che il nome del
messaggio che ci interessa intercettare è WM_SYSCOMMAND e quindi la procedura accetta un
parametro di tipo TWmSysCommand.
Analogamente se volessimo intercettare in altro messaggio (ad esempio WM_SIZE dovremo come
parametro il tipo previsto dall'Object Pascal (in questo caso TWmSize). Non è poi così
difficile, vero?
Unica avvertenza, dichiarate i messaggi sempre come privati!
Sarà bene presentare anche la vera procedura, altrimenti qui facciamo notte... ;-)
procedure TForm1.WmSysCommand(var Msg: TWMSysCommand);
Begin
DefaultHandler(Msg);
End;
Incredibile, funziona...
Ma dove sta la magia? Semplice, provate a sostituire DefaultHandler(Msg);
con
Inherited;
non funzionerà più!
In effetti abbiamo fatto tutto questo lavoro per sovrascrivere quel benedetto messaggio,
non per ereditare il bug.
Ma cosè quella fantomatica riga DefaultHandler? Per spiegarlo devo farvi riscrivere tutta
la procedura (spiacente)
procedure TForm1.WmSysCommand(var Msg: TWMSysCommand);
Var DefMsg: TMessage;
Begin
DefMsg := TMessage(Msg);
DefWindowProc(handle, Msg.Msg, DefMsg.wParam, DefMsg.lParam);
End;
Questa procedura funziona alla perfezione al posto dell'altra per il semplice motivo che DefaultHandler altro non è che l'incapsulazione Pascal dell'API DefWindowProc. Quest'API serve per richiamare il funzionamento standard di un messaggio Windows, quindi richiamando DefaultHandler la finestra verrà minimizzata e non nascosta.
Vi consiglio vivamente un download del progetto finale della form free minimize bug.