Sunteți pe pagina 1din 18

Prelegeri (6 ore – 3 întâlniri)

Tema 2-1.......................................................................................................................................................................2
Programele "HelloWorld" şi "HelloWin".................................................................................................................2
Funcțiile „WinMain” şi „WndProc”.........................................................................................................................2
Apelurile de funcţii...................................................................................................................................................3
Identificatori cu majuscule........................................................................................................................................3
Noi tipuri de date......................................................................................................................................................4
Variabilele HANDLE...............................................................................................................................................5
Notaţia ungară...........................................................................................................................................................5
Tema 2-2.......................................................................................................................................................................7
Punctul de intrare în program...................................................................................................................................7
Înregistrarea clasei de fereastră.................................................................................................................................8
Crearea ferestrei......................................................................................................................................................10
Afişarea ferestrei.....................................................................................................................................................12
Redesenarea zonei client.........................................................................................................................................12
Ciclul de mesaje. Structura de tip MSG. Tipul de date POINT..............................................................................12
Funcţiile GetMessage, TranslateMessage, DispatchMessage................................................................................13
Tema 2-3.....................................................................................................................................................................14
Procedura de fereastră - funcţia WndProc..............................................................................................................14
Prelucrarea mesajelor..............................................................................................................................................14
Mesajul WM_CREATE. Redarea unui fişier de sunet...........................................................................................15
Mesajul WM_PAINT.............................................................................................................................................15
Mesajul WM_DESTROY.......................................................................................................................................17
Tema 2-1.
Programele "HelloWorld" şi "HelloWin".
HelloWorld.c
#include <stdio.h>
int main()
{
printf("Hello world!");
}

---------------------------------------------------------------

HelloWin.c
#include <windows.h>

LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);

int APIENTRY wWinMain(HINSTANCE hInstance,


HINSTANCE hPrevInstance,
LPWSTR lpCmdLine,
int nShowCmd)
{

// Initialize global strings


WNDCLASS wc = { };

wc.lpfnWndProc = WndProc;
wc.hInstance = hInstance;
wc.lpszClassName = L"Class";

RegisterClass(&wc);

// Perform application initialization:


HWND hWnd = CreateWindowW(L"ClassName", L"Title",
0,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
NULL,
NULL,
hInstance,
NULL);
ShowWindow(hWnd, nShowCmd);

// system("pause");
return 0;
}

LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
return DefWindowProc(hWnd, message, wParam, lParam);
}
Funcțiile „WinMain” şi „WndProc”.
Fişierul conţine numai două funcţii: WinMain şi WndProc. Funcţia WinMain reprezintă punctul de intrare în
program. Aceasta este echivalentul funcţiei main din programele scrise în limbajul C.

2
Orice program pentru Windows trebuie să aibă o funcţie WinMain.
WndProc este „procedura de fereastră" a ferestrei create de programul HELLOWIN. Orice fereastră -
indiferent dacă este fereastra principală a aplicaţiei sau fereastra unui mic buton de apăsare - are o procedură de
fereastră. Procedura de fereastră este un mod de încapsulare a codului care răspunde intrărilor (în general de la
tastatură şi de la mouse) şi afişează elementele grafice pe ecran. Aşa cum veţi vedea, procedura de fereastră face
acest lucru prin prelucrarea „mesajelor" trimise către fereastră.
Nici o instrucţiune din fişierul HELLOWIN.C nu apelează direct funcţia WndProc: WndProc este apelată de
sistemul de operare Windows. Totuşi, în funcţia WinMain apare o referire la funcţia WndProc, acesta fiind motivul
pentru care WndProc este declarată în partea de început a programului, înainte de WinMain.
Apelurile de funcţii.
HELLOWIN apelează nu mai puţin de 17 funcţii Windows. Aceste funcţii, împreună cu o scurtă descriere,
sunt prezentate în continuare, în ordinea apariţiei în programul HELLOWIN:
· LoadIcon - încarcă o pictogramă care urmează să fie folosită de un program.
· LoadCursor - încarcă un indicator pentru mouse, care urmează să fie folosit de un program.
· GetStockObject - obţine un obiect grafic (în acest caz o pensulă folosită pentru desenarea fondului
ferestrei).
· RegisterClassEx - înregistrează o clasă de fereastră pentru fereastra programului.
· CreateWindow - creează o fereastră pe baza unei clase de fereastră.
· ShowWindow - afişează o fereastră pe ecran.
· UpdateWindow - cere unei ferestre să se redeseneze.
· GetMessage - preia un mesaj din coada de mesaje.
· TranslateMessage - converteşte unele dintre mesajele de la tastatură.
· DispatchMessage - trimite un mesaj către o procedură de fereastră.
· PlaySound - redă un fişier de sunet.
· BeginPaint - iniţiază o operaţie de desenare a ferestrei.
· GetClientRect - obţine dimensiunile zonei client a ferestrei.
· DrawText - afişează un text.
· EndPaint - încheie o operaţie de desenare.
· PostQuitMessage - inserează un mesaj de încheiere în coada de aşteptare.
· DefWindowProc - execută operaţiile prestabilite de prelucrare a mesajelor.
Aceste funcţii sunt documentate în cărţi şi în sursele incluse de compilator şi sunt declarate în diferite fişiere
antet incluse în fişierul WINDOWS.H.
Identificatori cu majuscule.
În fişierul HELLOWIN.C sunt folosiţi câţiva identificatori scrişi cu majuscule. Aceşti identificatori sunt
definiţi în fişierele antet din Windows. Câţiva dintre ei conţin un prefix de două sau trei litere, urmat de o liniuţă de
subliniere:
CS_HREDRAW DT_SINGLELINE WS_OVERLAPPEDWINDOW
CS_VREDRAW IDC_ARROW
CW_USEDEFAULT IDI_APPLICATION WM_CREATE
DT_CENTER SND_ASYNC WM_DESTROY
DT_VCENTER SND_FILENAME WM_PAINT
3
Acestea sunt simple constante numerice. Prefixul indică o categorie generală căreia îi aparţine constanta
respectivă, aşa cum se arată în tabelul următor:
Prefix Categorie
CS - Opţiune pentru stilul clasei
IDI - Număr de identificare pentru o pictogramă
IDC - Număr de identificare pentru un cursor
WS - Stil de fereastră
CW - Opţiune de creare a unei ferestre
WM - Mesaj de fereastră
SND - Opţiune pentru sunete
DT - Opţiune de desenare a textului
În mod normal nu este nevoie să reţineţi nici o constantă numerică pentru scrierea unui program pentru
Windows. De fapt, toate constantele numerice folosite în Windows au un identificator definit în fişierele antet.

Noi tipuri de date.


Alţi identificatori folosiţi în fişierul HELLOWIN.C sunt noi tipuri de date, definite în fişierele antet cu ajutorul
instrucţiunii typedef sau #define. Aceste definiţii au fost stabilite iniţial pentru a simplifica tranziţia programelor
Windows originale de la sistemele pe 16 biţi originale la viitoarele sisteme pe 32 de biţi, sau la cele bazate pe o altă
tehnologie. Această tranziţie nu s-a făcut chiar atât de uşor şi de transparent cum ar fi crezut unii la momentul
respectiv, dar conceptul este destul de bun.
Uneori aceste noi tipuri de date sunt doar abrevieri mai uşor de folosit. De exemplu, tipul de date UINT folosit
pentru al doilea parametru al funcţiei WndProc este prescurtarea de ia unsigned int, ceea ce în Windows 95
înseamnă o valoare întreagă pe 32 de biţi. Tipul de date PSTR folosit pentru al treilea parametru al funcţiei
WinMain este un pointer la un şir de caractere, adică înlocuieşte tipul char*.
Alte tipuri de date nu sunt la fel de evidente. De exemplu, al treilea şi al patrulea parametri ai funcţiei
WndProc sunt definiţi cu tipurile de date WPARAM si LPARAM. Originea acestor nume ţine oarecum de istoria
sistemului Windows: pe când Windows era un sistem pe 16 biţi, al treilea parametru al funcţiei WndProc era
definit ca WORD, adică un număr întreg scurt, fără semn, pe 16 biţi (unsigned short) iar al patrulea parametru era
definit ca LONG, adică un număr întreg pe 32 de biţi (long) - de aici provenind prefixele „W" şi „L" ale cuvântului
„PARAM". În Windows 95, WPARAM este definit ca UINT iar LPARAM este definit ca LONG (care este chiar
tipul long din C), aşa că ambii parametri ai procedurii de fereastră sunt valori pe 32 de biţi. Deoarece tipul de date
WORD este definit în Windows 95 ca un număr întreg fără semn pe 16 biţi (unsigned short), prefixul „W" din
WPARAM este oarecum impropriu şi poate crea confuzii.
Funcţia WndProc returnează o valoare LRESULT, definit ca un număr de tip LONG. Funcţia WinMain este de
tipul WINAPI, iar funcţia WndProc este de tipul CALLBACK. Ambii identificatori sunt definiţi ca __stdcall, care
defineşte o secvenţă specială de apelare pentru apelurile de funcţii dintre Windows şi aplicaţii.

4
Programul HELLOWIN foloseşte patru structuri de date (despre care vom discuta mai târziu în acest capitol)
definite în fişierele antet din Windows. Aceste structuri de date sunt:
MSG- Structura mesajului
WNDCLASSEX - Structura clasei de fereastră
PAINTSTRUCT - Structură pentru desenare
RECT – Dreptunghi
Primele două structuri sunt folosite în funcţia WinMain pentru definirea a două structuri numite msg şi
wndclass. Celelalte două sunt folosite în funcţia WndProc pentru definirea altor două structuri, numite ps şi rect.
Variabilele HANDLE.
În program sunt folosiţi trei identificatori cu majuscule pentru diferite tipuri de variabile handle:
HINSTANCE - Variabilă handle a unei „instanţe" - programul însuşi
HWND - Variabilă handle a unei ferestre
HDC - Variabilă handle a unui context de dispozitiv
Variabilele handle sunt folosite destul de des în Windows. În acest capitol veţi face cunoştinţă cu variabila
handle a unei pictograme (HICON), variabila handle a unui cursor (HCURSOR) şi cea a unei pensule (HBRUSH).
O variabilă handle este pur şi simplu un număr (de obicei pe 32 de biţi) care face trimitere la un obiect.
Variabilele handle din Windows sunt asemănătoare cu cele folosite pentru fişiere (file handles) în programele
convenţionale C sau MS-DOS. Un program obţine aproape întotdeauna o variabilă apelând o funcţie Windows.
Programul foloseşte apoi variabila handle obţinută pentru trimiterea la obiect în alte funcţii. Valoarea reală a
variabilei handle nu este importantă pentru program, dar modulul Windows care o furnizează programului ştie cum
să îl manipuleze pentru trimiterea la obiect.
Notaţia ungară.
S-ar putea să fi observat că unele dintre variabilele folosite în programul HELLOWIN.C au nume ciudate. Un
astfel de exemplu este szCmdLine, transmis ca parametru funcţiei WinMain.
Mulţi programatori Windows folosesc „notaţia ungară", o convenţie de denumire a variabilelor intitulată astfel
în onoarea legendarului programator de la Microsoft, Charles Simonyi. Convenţia este foarte simplă - fiecare nume
de variabilă începe cu una sau mai multe litere mici care specific tipul de date al variabilei. De exemplu, prefixul sz
al variabilei szCmdLine semnifică „şir de caractere terminat cu zero". Prefixul h al variabilelor hInstance şi
PrevInstance înseamnă „variabilă handle"; prefixul i al variabilei iCmdShow înseamnă „întreg". Şi ultimii doi
parametri ai funcţiei WndProc respectă notaţia ungară, deşi, aşa cum am arătat mai devreme, wParam ar fi trebuit
să se numească uiParam (de la „unsigned integer"). Totuşi, deoarece aceşti doi parametri se defines folosind
tipurile de date WPARAM şi LPARAM, am păstrat denumirile originale.
Atunci când denumiţi variabilele de tip structură, puteţi să folosiţi numele de tip al structurii (sau o abreviere a
acestuia) ca prefix al numelui variabilei sau chiar ca nume al variabilei. De exemplu, în funcţia WinMain din
programul HELLOWIN.C, variabila msg este o structură de tip MSG iar wndclass este o variabilă de tip
WNDCLASSEX. În funcţia WndProc, ps este o structură de tip PAINTSTRUCT iar rect este o structură de tip
RECT.
Notaţia ungară vă ajută să descoperiţi erorile înainte ca acestea să ajungă în programele finale. Deoarece
numele unei variabile descrie atât modul de folosire a acesteia, cât şi tipul de date, este mai puţin probabil să faceţi
erori de programare care implică amestecarea unor tipuri de date incompatibile.

5
Prefixele folosite pentru variabilele din această carte sunt prezentate în tabelul următor:
Prefix - Tip de date
c - char
by - BYTE (unsigned char)
n - short
i - int
x, y - int (folosit pentru coordonate)
cx, cy - int (folosit pentru dimensiuni pe axele x si y, c vine de la „contor")
b sau f - BOOL (int); f vine de la „flag" (indicator)
w - WORD (unsigned short)
l - LONG (long)
dw - DWORD (unsigned long)
fn - funcţie
s - şir de caractere
sz - sir de caractere terminat cu zero
h - variabilă handle
p - pointer

6
Tema 2-2.
Punctul de intrare în program.
După ce v-aţi format o idee generală despre programul HELLOWIN.C, putem să disecăm programul linie cu
linie. Codul începe cu o instrucţiune #include pentru includerea fişierului antet WINDOWS.H:
#include <windows.h>

Fişierul WINDOWS.H include la rândul lui mai multe fişiere antet care conţin declaraţiile funcţiilor,
structurilor, noilor tipuri de date şi constantelor numerice din Windows.
Instrucţiunea #include este urmată de declaraţia avansată a funcţiei WndProc*:
LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM) ;

Declararea în avans a acestei funcţii este necesară deoarece WndProc este referită în cadrul funcţiei WinMain.
Punctul de intrare al unui program C scris pentru un mediu convenţional este o funcţie numită main. De la
această funcţie începe execuţia programului. (De fapt, funcţia main este punctul de intrare la acea parte a
programului scrisă de programator. De obicei, compilatorul C inserează în fişierul executabil unele secvenţe de cod
pentru lansarea în execuţie. Funcţia main este apoi apelată de acest cod de lansare). Punctul de intrare într-un
program Windows este o funcţie numită WinMain.
Funcţia WinMain este întotdeauna definită astfel:
int WINAPI WinMain (HINSTANCE hinstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow)

Această funcţie foloseşte secvenţa de apelare WINAPI şi la terminare returnează sistemului de operare o
valoare întreagă. Numele funcţiei trebuie să fie WinMain. Această funcţie are patru parametri:
Parametrul hlnstance este numit „variabilă handle a instanţei" („instance handle"). Acesta este un număr care
identifică în mod unic toate programele rulate în Windows. Utilizatorul poate rula simultan mai multe copii ale
aceluiaşi program. Aceste copii se numesc „instanţe" şi fiecare are o valoare diferită pentru parametrul hlnstance.
Variabila handle a instanţei este asemănătoare cu „identificatorul de operaţie" („task ID") sau „identificatorul de
proces" („process ID") din alte sisteme de operare multitasking.
hPrevInstance („previous instance" - instanţa anterioară) este un parametru învechit. În versiunile Windows
anterioare acest parametru conţinea variabila handle a celei mai recente instanţe încă activă a aceluiaşi program.
Dacă nu erau încărcate alte instanţe ale programului, hPrevInstance avea valoarea 0 sau NULL. În Windows 95,
parametrul hPrevInstance are întotdeauna valoarea NULL.
Parametrul szCmdLine este un pointer la un şir de caractere terminat cu zero care conţine eventualii parametri
transmişi programului în linia de comandă. Puteţi să rulaţi un program Windows cu parametri incluzând parametrii
respectivi după numele programului în linia de comandă MS-DOS sau specificându-i în caseta de dialog Run
apelată din meniul Start.
Parametrul iCmdShow este un număr care indică modul iniţial de afişare a ferestrei în Windows. Acest număr
este atribuit de programul care lansează în execuţie programul aflat în discuţie.
Programele verifică rareori valoarea acestui parametru, dar o pot face dacă este nevoie. În majoritatea
cazurilor, iCmdShow are valoarea 1 sau 7. Dar cel mai bine este să nu vă gândiţi la aceste valori numerice. Mai
sugestivi sunt identificatorii SW_SHOWNORMAL (definit în Windows ca 1) şi SW_SHOWMINNOACTIVE
(definit cu valoarea 7). Prefixul SW vine de la „show window" (afişare fereastră). Acest parametru specifică dacă
fereastra programului este afişată normal sau dacă este afişată iniţial doar ca o pictogramă.
7
Înregistrarea clasei de fereastră.
O fereastră este întotdeauna creată pe baza unei clase de fereastră. Aceasta identifică procedura de fereastră
care prelucrează toate mesajele trimise către fereastră.
Pe baza aceleiaşi clase pot fi create mai multe ferestre. De exemplu, toate butoanele din Windows sunt create
pe baza unei singure clase de fereastră. Aceasta defineşte procedura de fereastră şi alte caracteristici ale ferestrei
create pe baza clasei respective. Atunci când creaţi o fereastră, definiţi şi atributele suplimentare ale acesteia, care
sunt unice pentru fereastra respectivă.
Înainte de a crea fereastra programului trebuie să înregistraţi o clasă de fereastră, apelând funcţia
RegisterClassEx. Aceasta este o versiune extinsă (de aici sufixul „Ex") a funcţiei RegisterClass din versiunile
anterioare ale sistemului de operare Windows. Totuşi, funcţia RegisterClass poate fi încă folosită în Windows 95.
Funcţia RegisterClassEx acceptă un singur parametru: un pointer la o structură de tipul WNDCLASSEX.
Structura WNDCLASSEX este definită în fişierele antet din Windows astfel:
typedef struct tagWNDCLASSEX
{
UINT cbSize ;
UINT style ;
WNDPROC lpfnWndProc ;
int cbClsExtra ;
int cbWnExtra ;
HINSTANCE hinstance ;
HICON hicon ;
HCURSOR hCursor ;
HBRUSH hbrBackground ;
LPCSTR lpszMenuName ;
LPCSTR lpszClassName ;
HICON hIconSm ;
}
WNDCLASSEX ;

Sunt necesare câteva observaţii privind tipurile de date şi notaţia ungară folosită în această structură: prefixele
LP şi lp sunt prescurtări pentru „long pointer" şi sunt „rămăşiţe" din versiunile Windows pe 16 biţi, în cazul cărora
programatorii trebuie să facă diferenţa între pointerii de tip short (sau near) pe 16 biţi şi pointerii de tip long (sau
far) pe 32 de biţi. În Windows 95 toţi pointerii au valori pe 32 de biţi. Am încercat să elimin toate prefixele l ale
tipurilor de pointeri din exemplele de programe pe care le-am ales pentru această carte, dar cu siguranţă că le veţi
mai întâlni în alte programe.
Remarcaţi şi alte moduri de folosire a notaţiei ungare: lpfn vine de la „long pointer to a function" („pointer de
tip long la o funcţie"). Prefixul cb provine de la „count of bytes" („contor de octeţi"). Prefixul hbr vine de la
„handle to a brush" („variabilă handle a unei pensule").
În funcţia WinMain trebuie să definiţi o structură de tipul WNDCLASSEX, cum ar fi:
WNDCLASSEX wndclass ;

Apoi definiţi cele 12 câmpuri ale structurii şi apelaţi funcţia RegisterClassEx:


RegisterClassEx (&wndclass) ;

Cele mai importante câmpuri ale structurii sunt al treilea şi penultimul. Penultimul câmp conţine numele clasei
de fereastră (şi în programele care creează o singură fereastră are, de obicei, acelaşi nume ca şi programul). Al
treilea câmp (lpfnWndProc) este adresa procedurii de fereastră folosită pentru toate ferestrele create pe baza acestei

8
clase (care este funcţia WndProc din programul HELLOWIN.C). Celelalte câmpuri descriu caracteristicile tuturor
ferestrelor create pe baza acestei clase.
Câmpul cbSize reprezintă dimensiunea structurii. Instrucţiunea: wndclass.style = CS_HREDRAW|
CS_VREDRAW; combină doi identificatori pentru „stilul de clasă" („class style") folosind operatorul SAU
orientat pe biţi din limbajul C. În fişierele antet din Windows sunt definiţi mai mulţi identificatori cu prefixul CS_.
Acestea sunt constante pe 32 de biţi în care un singur bit are valoarea
1. De exemplu, identificatorul CS_VREDRAW este definit ca 0x0001 iar CS_HREDRAW este definit ca
0x0002. Identificatorii definiţi în acest fel sunt numiţi uneori „identificatori pe biţi". Aceştia pot fi combinaţi cu
ajutorul operatorului SAU orientat pe biţi din limbajul C.
Cei doi identificatori pentru stilul clasei indică faptul că toate ferestrele create pe baza acestei clase sunt
redesenate complet, ori de câte ori se modifică dimensiunea pe orizontală (CS_HREDRAW) sau cea pe verticală
(CS_VREDRAW) a ferestrei. Dacă redimensionaţi fereastra programului HELLOWIN veţi vedea că textul este
redesenat, astfel încât să apară în centrul noii ferestre. Acest lucru este asigurat de cei doi identificatori de stil. Vom
vedea imediat cum este informată procedura de fereastră privind modificarea dimensiunii ferestrei.
Al treilea câmp al structurii WNDCLASSEX este iniţializat prin instrucţiunea:
wndclass.lpfnWndProc = WndProc ;

Această instrucţiune stabileşte ca procedură de fereastră funcţia WndProc, adică a doua funcţie definită în
fişierul HELLOWIN.C. Această procedură va prelucra toate mesajele trimise către toate ferestrele create pe baza
acestei clase de fereastră. Aşa cum am arătat mai sus, prefixul lpfn înseamnă, în notaţia ungară, „pointer de tip long
la o funcţie".
Următoarele două instrucţiuni:
wndclass.cbClsExtra = 0 ;
wndclass.cbWndExtra = 0 ;

rezervă un spaţiu suplimentar în structura clasei şi în structura ferestrei, păstrată în interiorul sistemului de
Windows. Un program poate să utilizeze spaţiul suplimentar în scopuri proprii.
Programul HELLOWIN nu foloseşte această posibilitate, aşa că nu se rezervă nici un spaţiu suplimentar. În
alte situaţii, aşa cum indică şi notaţia ungară, câmpurile vor avea rolul de „contor de octeţi".
Următorul câmp este variabila handle a instanţei (care este chiar unul dintre parametrii funcţiei WinMain):
wndclass.hInstance = hinstance ;

Instrucţiunea:
wndclass.hIcon = LoadIcon (NULL, IDI_APPLICATION) ;

şi instrucţiunea:
wndclass.hIconSm = LoadIcon (NULL, IDI_APPLICATION) ;

definesc o pictogramă pentru ferestrele create pe baza acestei clase. Pictograma este o mică imagine de tip
bitmap care apare în bara de taskuri a sistemului de operare şi în bara de titlu a ferestrei. Mai târziu veţi învăţa cum
să creaţi pictograme proprii pentru programele Windows. Pentru moment, vom aborda o metodă mai simplă şi vom
folosi o pictogramă predefinită.

9
Pentru obţinerea unei variabile handle a unei pictograme predefinite apelaţi funcţia LoadIcon cu primul
parametru având valoarea NULL. (Atunci când încărcaţi o pictogramă proprie, acest parametru va conţine variabila
handle a instanţei programului.) Al doilea parametru este un identificator cu prefixul IDI_ definit în fişierele antet
din Windows. Pictograma IDI_APPLICATION este o mică imagine a unei ferestre. Funcţia LoadIcon returnează o
variabilă handle a acestei pictograme. Nu ne interesează valoarea reală a acestei variabile, ci doar o stocăm în
câmpurile hIcon şi hIconSm. Aceste câmpuri sunt definite în structura WNDCLASSEX de tipul HICON („handle
to an icon").
Instrucţiunea:
wndclass.hCursor = LoadCursor (NULL, IDC_ ARROW) ;

este foarte asemănătoare cu cele două instrucţiuni anterioare. Funcţia LoadCursor încarcă un cursor predefinit
pentru mouse, numit IDC_ARROW, şi returnează o variabilă handle a acestui cursor. Atunci când este deplasat
deasupra zonei client a ferestrei create pe baza acestei clase, indicatorul mouse-ului este afişat sub forma unei
săgeţi.
Următorul câmp precizează culoarea fondului zonei client a ferestrelor create pe baza acestei clase. Prefixul
hbr al numelui hbrBackground vine de la „handle to a brush" („variabilă handle a unei pensule"). O pensulă este un
termen grafic care se referă la un model colorat de pixeli folosit pentru umplerea unei suprafeţe. Windows are mai
multe pensule standard (sau pensule „de stoc"). Funcţia GetStockObject returnează o variabilă handle a unei
pensule albe:
wndclass.hbrBackground = (HBRUSH) GetStockObject (WHITE_BRUSH);

Aceasta înseamnă că fondul zonei client a ferestrei va fi colorat cu alb, ceea ce este un lucru obişnuit.
Următorul câmp specifică meniul ferestrei. Programul HELLOWIN nu are nici un meniu, aşa că acest câmp
are valoarea NULL:
wndclass.lpszMenuName = NULL ;

În sfârşit, clasei trebuie să i se dea un nume. Pentru programele mai mici, acesta poate fi chiar numele
programului, deci în variabila szAppName este stocat şirul de caractere „HelloWin":
wndclass.IpszClassName = szAppName ;

După iniţializarea celor 12 câmpuri ale structurii, HELLOWIN înregistrează clasa de ferestre prin apelarea
funcţiei RegisterClassEx. Singurul parametru al funcţiei este un pointer către structura
WNDCLASSEX:
RegisterClassEx (&wndclass) ;

Crearea ferestrei.
Clasa de fereastră defineşte caracteristicile generale ale unei ferestre, permiţând astfel folosirea aceleiaşi clase
pentru crearea mai multor ferestre. Atunci când creaţi o fereastră apelând funcţia Create Window, puteţi să
specificaţi mai multe detalii despre fereastra respectivă.
Programatorii Windows mai noi sunt uneori derutaţi de diferenţele între o clasă de fereastră şi o fereastră, şi nu
înţeleg de ce nu pot să specifice toate caracteristicile unei ferestre printr-o singură operaţie. De fapt, împărţirea
informaţiilor în acest fel este foarte convenabilă. De exemplu, toate butoanele de apăsare sunt create pe baza unei
singure clase de fereastră. Procedura de fereastră asociată acestor butoane este localizată chiar în Windows. Clasa
de fereastră are ca sarcini prelucrarea tuturor mesajelor de la tastatură şi de la mouse trimise către butonul de
apăsare şi definirea aspectului vizual al butonului pe ecran. Din acest punct de vedere, toate butoanele de apăsare

10
funcţionează în acelaşi mod. Acestea însă pot avea diferite dimensiuni, diferite poziţii pe ecran şi diferite etichete.
Aceste caracteristici fac parte din definiţia ferestrei.
În loc să folosească o structură de date, aşa cum face funcţia RegisterClassEx, funcţia CreateWindow cere ca
toate informaţiile să fie transmise ca parametri. Iată cum este apelată funcţia CreateWindow în programul
HELLOWIN.C:
hwnd =CreateWindow (szAppName, // numele clasei de fereastra
"The Hello Program", // titlul ferestrei
WS_OVERLAPPEDWINDOW, // stilul ferestrei
CW_USEDEFAULT, // poziţia iniţiala pe axa x
CW_USEDEFAULT, // poziţia iniţiala pe axa y
CW_USE DEFAULT, // dimensiunea iniţiala pe axa x
CW_USEDEFAULT, // dimensiunea iniţiala pe axa y
NULL, // variabila handle a ferestrei părinte
NULL, // variabila handle a meniului
hlnstance, // variabila handle a instanţei programului
NULL) ; // parametri de creare

Pentru o citire mai uşoară, am folosit simbolul // pentru notarea comentariilor pe o singură linie care descriu
parametrii funcţiei Create Window.
Parametrul notat cu „numele clasei de fereastră" este szAppName, care conţine şirul de caractere „HelloWin" -
numele clasei de fereastră pe care tocmai am înregistrat-o. În acest fel, fereastra este asociată unei clase de
fereastră.
Fereastra creată de acest program este o fereastră normală suprapusă, cu o bară de titlu; în partea stângă a barei
de titlu se află caseta cu meniul sistem; în partea dreaptă se află butoanele de mărire, de micşorare şi de închidere;
fereastra are un chenar îngroşat, care permite redimensionarea. Acesta este stilul standard al ferestrelor, numit
WS_OVERLAPPEDWINDOW; în funcţia CreateWindow îi corespunde comentariul „stilul ferestrei". „Titlul
ferestrei" este textul afişat în bara de titlu.
Parametrii notaţi cu „poziţia iniţială pe axa x" şi „poziţia iniţială pe axa y" specifică poziţia iniţială a colţului
din stânga-sus al ferestrei, relativ la colţul din stânga-sus al ecranului. Prin folosirea identificatorului
CW_USEDEFAULT pentru aceşti parametri indicăm sistemului de operare să folosească valorile prestabilite
pentru o fereastră suprapusă. (CW_USEDEFAULT este definit ca 0x80000000.) În mod prestabilit, Windows
poziţionează mai multe ferestre suprapuse succesive la o distanţă crescătoare pe verticală şi pe orizontală faţă de
colţul din stânga-sus al ecranului. La fel, parametrii „dimensiunea iniţială pe axa x" şi „dimensiunea iniţială pe axa
y" specifică dimensiunile iniţiale ale ferestrei. Identificatorul CW_USEDEFAULT indică sistemului de operare să
folosească valorile prestabilite.
Parametrul indicat ca „variabilă handle a ferestrei părinte" are valoarea NULL, deoarece această fereastră nu
are nici o fereastră părinte. Atunci când între două ferestre există o relaţie părintedescendent, fereastra descendent
este afişată întotdeauna pe suprafaţa ferestrei părinte. Parametrul indicat ca „variabilă handle a meniului" are tot
valoarea NULL, deoarece fereastra nu are meniu. Parametrul indicat ca „variabilă handle a instanţei programului"
are ca valoare variabila handle transmisă programului ca parametru la apelarea funcţiei WinMain. În sfârşit,
„parametrul de creare" are valoarea NULL. Puteţi să folosiţi acest parametru pentru adresarea unor date folosite
ulterior în program.
Funcţia Create Window returnează o variabilă handle a ferestrei create. Aceasta este salvată în variabila hwnd,
definită ca fiind de tipul HWND (variabilă handle a unei ferestre). Orice fereastră din Windows are o variabilă
handle. Programul foloseşte variabila handle pentru indicarea ferestrei. Multe funcţii Windows au un parametru
hwnd, care specifică fereastra la care se referă funcţia respectivă. Dacă un program creează mai multe ferestre,
11
fiecare are o variabilă handle diferită. Variabila handle a unei ferestre este una dintre cele mai importante variabile
folosite în Windows.
Afişarea ferestrei.
După executarea funcţiei CreateWindow, fereastra a fost creată de Windows, dar încă nu este afişată pe ecran.
Pentru aceasta mai sunt necesare încă două apeluri de funcţii. Primul este:
ShowWindow (hwnd, iCmdShow) ;

Primul parametru este o variabilă handle a ferestrei create de funcţia CreateWindow. Al doilea parametru este
variabila iCmdShow, transmisă funcţiei WinMain. Dacă iCmdShow este SW_SHOWNORMAL (egal cu 1),
fereastra este afişată normal. Dacă iCmdShow este SW_SHOWMINNOACTIVE (egal cu 7), atunci fereastra nu
este afişată, dar numele şi pictograma acesteia apar pe bara de taskuri.
Redesenarea zonei client.
În programul HELLOWIN funcţia ShowWindow afişează fereastra pe ecran. Dacă al doilea parametru al
funcţiei este SW_SHOWNORMAL, Windows şterge zona client a ferestrei folosind pensula specificată în clasa
ferestrei. Apelul:
UpdateWindow (hwnd) ;

determină redesenarea zonei client. Acest lucru se face prin trimiterea către procedura de fereastră (funcţia
WndProc din HELLOWIN.C) a unui mesaj WM_PAINT. Vom vedea imediat cum tratează funcţia WndProc
aceste mesaje.
Ciclul de mesaje. Structura de tip MSG. Tipul de date POINT.
După apelarea funcţiei UpdateWindow, fereastra devine vizibilă pe ecran. Programul trebuie să fie acum
pregătit să citească intrările de la mouse şi de la tastatură. Windows formează o „coadă de mesaje" pentru fiecare
program rulat concurenţial. Atunci când apare un eveniment exterior, Windows converteşte acest eveniment într-un
mesaj pe care îl plasează în coada de aşteptare.
Un program preia mesajele din coada de aşteptare prin executarea unei secvenţe de cod numită „ciclu de
mesaje" („message loop"):
while (GetMessage (&msg, NULL, 0, 0))
{
TranslateMessage (&msg) ;
DispatchMessage (&msg) ;
}
return msg.wParam ;

Variabila msg este o structură de tip MSG, definită în fişierele antet din Windows astfel:
typedef struct tagMSG
{
HWND hwnd ;
UINT message ;
WPARAM wParam ;
LPARAM lParam ;
DWORD time ;
POINT pt ;
}
MSG ,

12
Tipul de date POINT este tot o structură, definită astfel:
typedef struct tagPOINT
{
LONG x ;
LONG y ;
}
POINT ;

Funcţiile GetMessage, TranslateMessage, DispatchMessage.


Funcţia GetMessage apelată la începutul ciclului de mesaje preia un mesaj din coada de aşteptare:
GetMessage (&msg, NULL, 0, 0)

Acest apel transmite sistemului de operare un pointer, numit msg, la o structură de tip MSG. Al doilea, al
treilea şi al patrulea parametru au valoarea NULL sau 0, ceea ce indică faptul că programul vrea să preia toate
mesajele, pentru toate ferestrele create de program. Windows completează câmpurile structurii de mesaje cu
următorul mesaj din coada de aşteptare. Câmpurile acestei structuri sunt:
· hwnd - variabila handle a ferestrei căreia îi este destinat mesajul. În programul HELLOWIN, aceasta este
aceeaşi cu valoarea hwnd returnată de funcţia CreateWindow, deoarece aceasta este singura fereastră a
programului.
· message - identificatorul mesajului. Acesta este un număr folosit pentru identificarea mesajului. Pentru fiecare
mesaj în fişierele antet din Windows este definit un identificator care începe cu prefixul WM_ („window
message"). De exemplu, dacă poziţionaţi indicatorul mouse-ului în zona client a programului HELLOWIN şi
apăsaţi butonul din stânga, Windows va insera în coada de aşteptare un mesaj pentru care câmpul message conţine
identificatorul WM_LBUTTONDOWN, adică valoarea 0x0201.
· wParam - un parametru pe 32 de biţi a cărui valoare depinde de mesajul trimis.
· lParam - un alt parametru pe 32 de biţi dependent de mesaj.
· time - momentul inserării mesajului în coada de mesaje.
· pt - coordonatele poziţiei mouse-ului în momentul inserării mesajului în coada de mesaje.
Dacă în câmpul message este transmisă orice altă valoare decât WM_QUIT (egală cu 0x0012), funcţia
GetMessage returnează o valoare diferită de zero. Mesajul WM_QUIT determină ieşirea din ciclul de mesaje.
Programul se încheie, returnând valoarea parametrului wParam al structurii msg.
Instrucţiunea:
TranslateMessage (&msg) ;

retransmite structura msg sistemului de operare, pentru convertirea unor mesaje de la tastatură. (Vom discuta
mai multe despre aceasta în Capitolul 5.)
Instrucţiunea:
DispatchMessage (&msg) ;

ca şi funcţia TranslateMessage, retransmite structura msg sistemului de operare. Windows trimite apoi mesajul
către procedura de fereastră corespunzătoare, în vederea prelucrării - cu alte cuvinte, Windows apelează procedura
de fereastră. În programul HELLOWIN, procedura de fereastră este WndProc. După ce prelucrează mesajul,
funcţia WndProc predă controlul sistemului de operare, care încă elaborează răspunsul la apelul DispatchMessage.
Atunci când Windows returnează controlul programului HELLOWIN, după executarea apelului DispatchMessage,
ciclul de tratare a mesajelor continuă cu următorul apel al funcţiei GetMessage.

13
14
Tema 2-3.
Procedura de fereastră - funcţia WndProc.
Tot codul descris până în acest moment este cod de întreţinere: a fost înregistrată clasa de fereastră, a fost
creată fereastra, care apoi a fost afişată pe ecran şi programul a intrat în ciclul de tratare a mesajelor în vederea
preluării mesajelor din coada de aşteptare.
Operaţiile reale au loc însă în procedura de fereastră. Procedura de fereastră arată ce afişează fereastra în zona
client şi cum răspunde fereastra la intrările utilizatorului.
În programul HELLOWIN procedura de fereastră este funcţia WndProc. O procedură de fereastră poate avea
orice nume (cu condiţia ca numele respectiv să nu existe deja). Un program pentru Windows poate conţine mai
multe proceduri de fereastră. O procedură de fereastră este întotdeauna asociată unei clase de fereastră, înregistrată
cu ajutorul funcţiei RegisterClassEx. Funcţia CreateWindow creează o fereastră pe baza unei anumite clase. Pe
baza aceleiaşi clase pot fi create mai multe ferestre.
Procedura de fereastră este definită întotdeauna astfel:
LRESULT CALLBACK WndProc (HWND hwnd, UINT iMsg, WPARAM wParam. LPARAM lParam)

Remarcaţi faptul că cei patru parametri ai procedurii de fereastră sunt identici cu primele patru câmpuri ale
structurii MSG.
Primul parametru este hwnd, variabila handle a ferestrei care recepţionează mesajul. Aceasta este aceeaşi
variabilă cu cea returnată de funcţia CreateWindow. Pentru un program care creează o singură fereastră, precum
HELLOWIN, aceasta este singura variabilă handle cunoscută de program. Dacă programul creează mai multe
ferestre pe baza aceleiaşi clase (şi deci foloseşte aceeaşi procedură de fereastră), atunci hwnd identifică fereastra
care primeşte mesajul.
Al doilea parametru este un număr (mai precis, un întreg fără semn - UINT - pe 32 de biţi) care identifică
mesajul. Ultimii doi parametri (wParam, de tipul WPARAM si lParam, de tipul LPARAM) furnizează mai multe
informaţii despre mesaj. Aceştia sunt numiţi „parametri de mesaj". Conţinutul acestor parametri este specific
fiecărui tip de mesaj.
Prelucrarea mesajelor.
Fiecare mesaj recepţionat de o procedură de fereastră este identificat printr-un număr, acesta fiind parametrul
iMsg al procedurii de fereastră. În fişierele antet din Windows sunt definiţi identificatori, cu prefixul WM
(„window message"), pentru fiecare parametru de mesaje.
În general, programatorii Windows folosesc o construcţie switch sau case ca să determine ce mesaje a primit
fereastra şi să stabilească modul de prelucrare a fiecărui mesaj. Atunci când prelucrează un mesaj, procedura de
fereastră trebuie să returneze valoarea 0. Orice mesaj pe care procedura de fereastră nu-l prelucrează este transmis
unei funcţii Windows numită DefWindowProc. Valoarea returnată de funcţia DefWindowProc trebuie să fie
returnată şi de procedura de fereastră.
În programul HELLOWIN, procedura de fereastră (WndProc) prelucrează numai trei mesaje: WM_CREATE,
WM_PAINT şi WM_DESTROY. Procedura de fereastră este structurată astfel:
switch (iMsg)
{
case WM_CREATE :
[prelucrează mesajul WM_CREATE]
return 0 ;
15
case WM_PAINT :
[prelucrează mesajul WM_PAINT]
return 0 ;
case WM_DESTROY :
[prelucrează mesajul WM_DESTROY]
return 0 ;
}
return DefWindowProc(hwnd, iMsg, wParam, lParam) ;

Este esenţial să apelaţi funcţia DefWindowProc pentru prelucrarea tuturor mesajelor ignorate de procedura de
fereastră a programului.
Mesajul WM_CREATE. Redarea unui fişier de sunet.
Primul mesaj pe care îl primeşte o procedură de fereastră - şi primul mesaj prelucrat de funcţia WndProc - este
WM_CREATE. WndProc recepţionează acest mesaj în timp ce Windows execute funcţia CreateWindow din
WinMain. Aceasta înseamnă că atunci când HELLOWIN apelează funcţia CreateWindow, Windows face ce are de
făcut şi apelează funcţia WndProc, transmiţându-I variabila handle a ferestrei şi mesajul WM_CREATE. WndProc
prelucrează mesajul WM_CREATE şi returnează controlul sistemului de operare. Windows poate apoi să încheie
execuţia funcţiei CreateWindow şi să se întoarcă la programul HELLOWIN pentru alte operaţii din funcţia
WinMain.
Deseori, procedurile de fereastră fac toate operaţiile de iniţializare a ferestrei în timpul prelucrării mesajului
WM_CREATE. În timpul prelucrării acestui mesaj, programul HELLOWIN redă un fişier de sunet, numit
HELLOWIN.WAV. Acest lucru se face prin apelarea funcţiei PlaySound. Primul parametru al funcţiei este numele
fişierului. Acesta ar putea să fie şi un alias (nume de înlocuire) definit în secţiunea Sounds a panoului de control
(Control Panel) sau o resursă de program. Al doilea parametru este folosit numai dacă fişierul de sunet este o
resursă. Al treilea parametru specifică un set de opţiuni. În acest caz am indicat faptul că primul parametru este un
nume de fişier şi că sunetul trebuie să fie redat asincron, adică funcţia PlaySound trebuie să returneze controlul
imediat după începerea operaţiei de redare, fără să aştepte terminarea acesteia.
WndProc încheie prelucrarea mesajului WM_CREATE cu returnarea valorii zero din procedura de fereastră.
Mesajul WM_PAINT
Al doilea mesaj prelucrat de funcţia WndProc este WM_PAINT. Acest mesaj este foarte important în
programarea sub Windows, deoarece informează fereastra privind faptul că o parte sau întreaga zonă client a
acesteia este „invalidă" şi trebuie să fie redesenată.
Cum poate să devină invalidă o zonă client? Atunci când fereastra este creată, întreaga zonă client a ferestrei
este invalidă, deoarece programul nu a desenat încă nimic în fereastră. Primul mesaj WM_PAINT (care apare, în
mod normal, atunci când programul apelează funcţia UpdateWindow din WinMain) cere procedurii de fereastră să
deseneze ceva în zona client.
Zona client devine invalidă şi atunci când redimensionaţi fereastra programului HELLOWIN. Vă amintiţi că
parametrul de stil al structurii wndclass din HELLOWIN conţine identificatorii CS_VREDRAW şi
CS_HREDRAW. Aceşti identificatori determină sistemul de operare Windows să invalideze întreaga fereastră
atunci când aceasta îşi schimbă dimensiunea şi apoi să trimită un mesaj WM_PAINT către procedura de fereastră.
Dacă micşoraţi la o pictogramă fereastra programului HELLOWIN şi apoi îi refaceţi dimensiunea iniţială.
Windows nu salvează conţinutul zonei client. Într-un mediu grafic ar fi mult prea multe date de salvat. De aceea,
Windows invalidează fereastra. Procedura de fereastră primeşte mesajul WM_PAINT şi reface conţinutul acesteia.
Atunci când mutaţi ferestrele astfel încât să se suprapună. Windows nu salvează porţiunea unei ferestre care a
fost acoperită de o altă fereastră. În schimb, atunci când fereastra respectivă este readusă la suprafaţă, porţiunea
16
anterior acoperită este invalidată. Procedura de fereastră primeşte un mesaj WM_PAINT pentru redesenarea
porţiunii respective.
Aproape întotdeauna, prelucrarea mesajului WM_PAINT începe prin apelarea funcţiei BeginPaint:
hdc = BeginPaint (hwnd, &ps) ;

şi se termină cu apelarea funcţiei EndPaint:


EndPaint (hwnd, &ps) ;

În ambele situaţii, primul parametru este variabila handle a ferestrei programului, iar al doilea parametru este
un pointer la o structură de tip PAINTSTRUCT. Structura PAINTSTRUCT conţine unele informaţii pe care
programul le poate folosi pentru redesenarea zonei client. (Vom discuta despre acest câmp al structurii în următorul
capitol.)
În timpul apelării funcţiei BeginPaint, Windows şterge fondul zonei client, dacă acesta nu a fost deja şters.
Fondul este şters folosindu-se pensula specificată în câmpul hbrBackground al structurii WNDCLASSEX, folosită
pentru înregistrarea clasei de fereastră, în cazul programului HELLOWIN, aceasta este o pensulă de stoc albă, ceea
ce înseamnă că Windows şterge fondul ferestrei colorându-l, în acelaşi timp, cu alb. Funcţia BeginPaint validează
întreaga zonă client şi returnează o „variabilă handle a contextului de dispozitiv". Un context de dispozitiv se referă
la un dispozitiv fizic de ieşire (cum ar fi un monitor video) împreună cu driverul care controlează acest dispozitiv.
Aveţi nevoie de o variabilă handle a contextului de dispozitiv ca să afişaţi text şi elemente grafice în zona client a
ferestrei. Folosind variabila handle a contextului de dispozitiv returnată de funcţia BeginPaint nu puteţi să desenaţi
în afara zonei client a ferestrei, chiar dacă încercaţi. Funcţia EndPaint eliberează variabila handle a contextului de
dispozitiv, astfel încât aceasta nu mai este validă.
Dacă procedura de fereastră nu prelucrează mesajele WM_PAINT - ceea ce se întâmplă foarte rar - acestea
trebuie să fie transmise funcţiei DefWindowProc. Funcţia DefWindowProc apelează funcţiile BeginPaint şi
EndPaint, astfel încât zona client a ferestrei să fie din nou validată.
După funcţia BeginPaint, WndProc apelează funcţia GetClientRect:
GetClientRect (hwnd, &rect) ;

Primul parametru al funcţiei GetClientRect este variabila handle a ferestrei programului. Al doilea parametru
al funcţiei este un pointer la o variabilă numită rect, de tipul RECT, definită în WndProc.
RECT este o structură pentru un „dreptunghi" definit în fişierele antet din Windows. Structura conţine patru
câmpuri de tip LONG, numite left, top, right şi bottom. Funcţia GetClientRect atribuie acestor câmpuri valorile
corespunzătoare dimensiunilor zonei client a ferestrei. Câmpurile left şi top au întotdeauna valoarea 0. Câmpurile
right şi bottom conţin lăţimea şi înălţimea zonei client, în pixeli.
WndProc transmite funcţiei DrawText un pointer către structura RECT, prin cel de-al patrulea parametru al
funcţiei:
DrawText (hdc, "Hello, Windows 95!", -1, &rect, DT_SINGLELINE : DT_CENTER ! DT_VCENTER) ;

Funcţia DrawText, aşa cum arată şi numele, este folosită pentru „desenarea" unui text. Deoarece funcţia
desenează ceva, primul parametru este o variabilă handle a contextului de dispozitiv, returnatâ de funcţia
BeginPaint. Al doilea parametru este textul care urmează să fie afişat, iar al treilea parametru are valoarea -1, ceea
ce indică faptul că textul de afişat se termină cu un octet 0.
Ultimul parametru al funcţiei este o combinaţie de indicatori flag pe biţi, definiţi în fişierele antet din
Windows. Indicatorii flag folosiţi determină afişarea textului pe o singură linie, centrată orizontal şi vertical relativ
17
la dreptunghiul specificat prin al patrulea parametru. Ca urmare, funcţia DrawText afişează textul „Hello, Windows
95!" în centrul zonei client a ferestrei.
De fiecare dată când zona client devine invalidă (aşa cum se întâmplă atunci când modificaţi dimensiunea
ferestrei), funcţia WndProc recepţionează un mesaj WM_PAINT. WndProc obţine noua dimensiune a ferestrei şi
afişează din nou textul în centrul acesteia.
Mesajul WM_DESTROY
Un alt mesaj important este WM_DESTROY. Acest mesaj indică faptul că sistemul de operare desfăşoară un
proces de distrugere a ferestrei pe baza unei comenzi de la utilizator. Mesajul este trimis atunci când utilizatorul
execută clic pe butonul Close, selectează opţiunea Close din meniul sistem sau apasă fastele Alt+F4.
Programul HELLOWIN răspunde la acest mesaj printr-o metodă standard, apelând funcţia PostQuitMessage:
PostQuitMessage(0);

Această funcţie inserează în coada de aşteptare a programului un mesaj WM_QUIT. Am spus anterior că
funcţia GetMessage returnează o valoare diferită de zero în cazul preluării oricărui mesaj în afară de WM_QUIT.
Atunci când preia din coada de aşteptare un mesaj WM_QUIT, funcţia GetMessage returnează valoarea 0, ceea ce
determină ieşirea din ciclul de tratare a mesajelor din funcţia WinMain şi închiderea programului.

18

S-ar putea să vă placă și