API HANDLE E PROGRAMMAZIONE AVANZATA
Prima di procedere in questo capitolo rilassatevi, bevetevi pure una Coca Cola e se necessario fatevi un giro. Tutte le tecniche RAD e orientate agli oggetti andranno riposte nel dimenticatoio al fine di una completa comprensione...
Fin dalle prime versioni Windows la programmazione per questo ambiente è sempre stata ostica, solo per visualizzare una semplice finestra erano necessarie decine di righe di codice. Fortunatamente le case costruttrici dei più noti compilatori prepararono un set di librerie orientate agli oggetti che semplificavano la programmazione per questo o.s. Tra le più note ricordiamo le Microsoft Foundation Class e le Object Windows Library rispettivamente Microsoft e Borland. Ovviamente essendo queste librerie orientate all'OOP erano utilizzabili solo dal C++ tra l'altro la Microsoft impose le sue MFC come standard per qualsiasi applicazione scritta nel suo ambiente Visual C++ tanto che alcuni seguaci di compilatori Borland le ribattezzarono Microsoft Fritten Chicken (pollo fritto alla Microsoft).
Fortunatamente anche il Pascal, ribattezzato Delphi, si evolse in questo senso, anzi la Borland fece un ulteriore passo avanti predisponendo un ambiente Visuale dove le sue librerie VCL (Visual Component Library) vengono aggiunte e rimosse tramite semplici tecniche Drag & Drop (tipiche degli ambienti RAD) con un sistema molto simile al Visual Basic.
La differenza sostanziale tra Visual Basic e Delphi è però che quest'ultimo è un linguaggio realmente compilato e quindi ci da la possibilità di creare eseguibili anche nella vecchia maniera (tramite cioè la programmazione strutturata).
Vi chiederete ora il perchè dobbiate seguire questo capitolo quando potete farne bellamente a meno... La risposta è abbastanza semplice, in alcuni casi è consigliabile sapere cosa Delphi prepara "dietro le quinte" e l'unico modo per farlo è imparare la stesura di applicazioni strutturate.
LA WINDOWCLASS
Il primo passo per la creazione di una finestra in Windows è la definizione di una sua WindowClass, una WindowClass altro non è che un record di tipo TWndClassEx contenente le informazioni di base di una finestra.
Definizione del record TWndClassEx in C++ typedef struct _WNDCLASSEX {
// wc |
La classe andrà poi registrata tramite RegisterClassEx(NomeClasse).
LA WINDOW PROCEDURE
Come avrete notato nel campo lpfnWndProc si deve specificare un puntatore ad una
fantomatica Window Procedure. Ma cos'è? Molto semplice una funzione CallBack!
Una funzione CallBack è una funzione che viene chiamata direttamente dal sistema
operativo, ad esempio quando ridimensioniamo una finestra Windows spedisce un messaggio
WM_SIZE alla Window Procedure della finestra ridimensionata e all'interno di questa
funzione il programma svolgerà le operazioni necessarie al ridimensionamento.
Nella nostra applicazione la Window Procedure sarà così definita
function WindowProc(Window: HWnd; Message: Cardinal; WParam: Word; LParam:
Longint): LongInt; stdcall;
begin
Result:= DefWindowProc(Window, Message, WParam, LParam);
end;
Come già spiegato nella prima parte del capitolo DefWindowProc serve per richiamare le funzionalità standard della finestra. In effetti l'assegnamento di Result è del tutto inutile, se volete potete tranquillamente rimuoverlo...
LA CODA DEI MESSAGGI
Come già accennato Windows è un sistema opertativo basato sulle funzioni callback,
quando l'utente esegue un'azione su di una finestra Windows manda un messaggio alla sua
window procedure. In Pascal i messaggi sono definiti nella unit Messages che dovrà per
tanto essere inclusa in tutti i progetti Delphi e sono, come da convenzione Microsoft,
preceduti da WM_ (Window Message). Il programmatore Delphi che si scontra con i messaggi
tende a confonderli con gli eventi previsti dall'ambiente, in realtà gli eventi Delphi
sono costruiti attorno ai messaggi Windows.
In questa tabella potete vedere alcuni tra i più diffusi messaggi con il loro
corrispettivo (quando presente) evento Delphi.
Messaggio Windows | Evento Delphi |
WM_PAINT | TForm.OnPaint |
WM_COMMAND | TMenuItem.OnClick |
WM_SYSCOMMAND | |
WM_KEYDOWN | TForm.OnKeyDown |
WM_KEYUP | TForm.OnKeyUp |
Come è quindi evidente è importante conoscere come gestire i messaggi in quanto non
sempre un messaggio che intendiamo sovrascrivere è accessibile da un evento Delphi.
Generalmente il programmatore Delphi che intende intercettare un messaggio lo può fare in
quattro diversi modi:
Tramite l'evento Delphi corrispondente |
Sovrascrivendo la Window Procedure dell'oggetto TForm |
Includendo la direttiva Message nella definizione di un metodo |
Tramite le tecniche di subclassing |
L'ultimo metodo è generalmente più complesso da gestire e consuma più risorse
Windows, tuttavia è indispensabile per creare componenti VCL discendenti da TComponent
che devono ricevere messaggi dell'oggetto Parent, quindi per completezza è stato incluso.
Si è invece tralasciata la sovrascrittura di DefaultHandler, questo metodo è in genere
sconsigliato, personalmente consiglio ai programmatori di propendere verso la gestione
tramite direttiva Message.
Per completare la conoscenza dei messaggi Windows consiglio chi volesse saperne di più di
informarsi pure sulle API SendMessage, PostMessage e RegisterWindowMessage. Comunque le
conoscenze acquisite in questo contesto sono già sufficienti per la creazione di diverse
applicazioni basate su messaggi non incapsulati da eventi Delphi.
CREATEWINDOWEX
Qui le cose si fanno serie ;-)
Per creare "fisicamente" una finestra si deve usare l'API CreateWindowEx, per la
verità se non si utilizzano gli stile estesi si potrebbe usare anche l'API CreateWindow
ma è meglio familiarizzare fin da subito con quest'API.
Definizione CreateWindowEx in C++ HWND CreateWindowEx( |
Se la funzione "succede" verrà ritornato l'handle della finestra creata.
Dopo tanta teoria altro non resta che provare il seguente esempio...
program WinClk;
uses
Windows,
Messages;
const
AppName = 'WinClk';
btnText = 144;
function WindowProc(Window: HWnd; Message: Cardinal; WParam: Word; LParam: Longint): LongInt; stdcall;
begin
Case Message of
WM_CLOSE : Halt;
WM_COMMAND:
Case wParam of
btnText:
MessageBox(Window, 'This is only a text', AppName, 0);
end;
end;
Result:= DefWindowProc(Window, Message, WParam, LParam);
end;
procedure WinMain;
var
Window: HWnd;
Message: TMsg;
const
WindowClass: TWndClassEx = (
style: 0;
lpfnWndProc: nil;
cbClsExtra: 0;
cbWndExtra: 0;
hInstance: 0;
hIcon: 0;
hCursor: 0;
hbrBackground: 0;
lpszMenuName: 'MyFirst';
lpszClassName: AppName);
begin
WindowClass.cbSize:= SizeOF(WindowClass);
WindowClass.lpfnWndProc:= TFnWndProc(@WindowProc);
if HPrevInst = 0 then begin
WindowClass.hInstance:= HInstance;
WindowClass.hIcon := LoadIcon(0, IDI_APPLICATION);
WindowClass.hCursor := LoadCursor(0, idc_Arrow);
WindowClass.hbrBackground:= COLOR_WINDOW;
if RegisterClassEx(WindowClass) = 0 then
Halt(255);
end;
Window:= CreateWindowEx(0,
AppName, {Class name}
'Windoze Clock', {Window name}
ws_OverlappedWindow, {style}
cw_UseDefault, {X}
cw_UseDefault, {Y}
cw_UseDefault, {Width}
cw_UseDefault, {Height}
0, {WndParent}
0, {Menu}
HInstance, {Instance}
nil); {structure creation parameter}
CreateWindow('button', 'bla bla', WS_CHILD or WS_VISIBLE or BS_PUSHBUTTON, 30, 30, 70, 70, Window, btnText, HInstance, nil);
If Window = 0 Then Halt;
ShowWindow(Window, CmdShow);
UpdateWindow(Window);
while GetMessage(Message, 0, 0, 0) do
begin
{This translates virtual-key messages into character messages.}
TranslateMessage(Message);
DispatchMessage(Message); {The translated message is now 'mailed' out.}
end;
Halt(Message.wParam);
end; {WinMain}
{****************************}
begin
WinMain; {By this time it is simple <G>}
end.
La fatica è stata ricompensata: se la compilazione andrà a buon fine avrete un eseguibile di appena 15-16Kb!
Da oggi se un programmatore C ve la mena che il Delphi non può creare applicazioni compatte snocciolategli questo progetto ;-)
Vi consiglio un download del seguente progetto interamente creato dal sottoscritto.
La complessità è di livello medio, ma nel seguito del capitolo verranno spiegati tutti i
concetti fondamentali per una corretta compresione dell'applicazione.
ATTENZIONE, CAPITOLO NON ANCORA COMPLETATO, ATTENDETE, ATTENDETE ;-)