Sunteți pe pagina 1din 39

LUCRARE DE LABORATOR NR.

TEMA: Aplicaţii Windows

Scopul lucrării: Studierea bazelor şi principiilor de creare a aplicaţiilor


Windows

Noţiuni teoretice

Windows este o interfaţă grafică cu utilizatorul (GUI - graphical user


interface), numită uneori şi „interfaţă vizuală" sau „mediu grafic cu ferestre"
(„graphical windowing environment"). Toate tipurile de interfeţe grafice cu
utilizatorul folosesc elemente grafice afişate într-o imagine de tip bitmap.
Elementele grafice asigură o utilizare mai eficientă a spaţiului de afişare, un
mediu bogat din punct de vedere vizual pentru transferul de informaţii şi
posibilitatea de afişare a rezultatelor aşa cum vor arăta în realitate pe hârtie
(WYSIWYG - what you see is what you get).
Toate programele pentru Windows au un aspect asemănător şi se
comportă fundamental la fel. Programele ocupă o fereastră - o suprafaţă
dreptunghiulară de pe ecran. Fereastra poate fi identificată datorită unei bare
de titlu. Majoritatea funcţiilor oferite de program sunt apelate cu ajutorul
unui meniu. Informaţiile afişate care nu încap pe un singur ecran pot fi
vizualizate cu ajutorul barelor de derulare. Unele articole de meniu apelează
casete de dialog în care utilizatorul introduce informaţii suplimentare. În
majoritatea programelor mai mari pentru Windows există o casetă de dialog
care deschide un fişier. Această casetă de dialog arată la fel (sau foarte
asemănător) în mai multe programe Windows şi este apelată aproape
întotdeauna cu aceeaşi opţiune de meniu.

Apelurile de funcţii
Windows asigură suportul pentru mai mult de o mie de apeluri de funcţii
pe care le pot folosi aplicaţiile. Fiecare funcţie Windows are un nume
sugestiv, scris atât cu majuscule, cât şi cu minuscule, cum ar fi
CreateWindow. Această funcţie, aşa cum probabil v-aţi dat seama, creează o
fereastră.
Toate funcţiile Windows importante sunt declarate în fişiere antet.
Principalul fişier antet se numeşte WINDOWS.H şi include multe alte fişiere
antet. Aceste fişiere antet sunt furnizate de orice mediu de programare pentru
Windows, aşa cum este, de exemplu, C. Fişierele antet sunt o parte
importantă a documentaţiei Windows.
În programele pentru Windows folosiţi apelurile de funcţii la fel cum
folosiţi funcţiile de bibiotecă C, cum ar fi strlen. Principala diferenţă este
faptul că în cazul funcţiilor C codul funcţiilor este legat direct de codul
programului, pe când codul funcţiilor Windows este stocat în fişiere din afara
programului, numite biblioteci cu legături dinamice (DLL - dynamic link
libraries).Mai jos sunt prezentate careva funcţii Windows pentru a simplifica
crearea unei aplicaţii Windows:
 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.
Programarea sub Windows este un tip de programare orientată pe obiecte
(OOP -object-oriented programming). Acest lucru devine evident dacă vă

2
gândiţi la obiectul pe care îl veţi folosi cel mai mult în Windows - obiectul
care dă numele sistemului de operare, obsedanta „fereastră".
Aşa cum am menţionat mai devreme, ferestrele sunt nişte suprafeţe
dreptunghiulare de pe ecran. O fereastră primeşte intrări de la tastatură sau de
la mouse şi afişează rezultate în zona proprie de pe suprafaţa ecranului.
Fereastra unei aplicaţii conţine, de obicei, bara de titlu a programului,
meniul, chenarul de dimensionare şi, eventual, barele de derulare. Casetele
de dialog sunt ferestre adiţionale. Mai mult, suprafaţa unei casete de dialog
conţine întotdeauna câteva ferestre suplimentare, numite „ferestre
descendent" („child windows"). Ferestrele descendent apar sub forma
butoanelor de apăsare, a butoanelor radio, a casetelor de validare, a
câmpurilor pentru introducerea textului, casetelor-listă şi a barelor de
derulare.
Utilizatorul vede ferestrele ca obiecte pe ecran şi poate acţiona direct
asupra lor, apăsând un buton sau manipulând o bară de derulare. Fereastra
recepţionează intenţiile utilizatorului sub forma unor „mesaje". De asemenea,
ferestrele folosesc mesaje pentru comunicarea cu alte ferestre.
Atunci când se spune că „sistemul de operare trimite programului un
mesaj" se are în vedere că Windows apelează o funcţie din program.
Parametrii acestei funcţii descriu mesajul respectiv. Această funcţie din
programul Windows este cunoscută sub numele de „procedură de fereastră"
(„window procedure").
Orice fereastră creată de un program are asociată o procedură de
fereastră. Procedura de fereastră este e funcţie care se poate afla chiar în
program sau într-o bibliotecă cu legături dinamice (DLL). Windows trimite
un mesaj către o fereastră prin apelarea procedurii de fereastră. Procedura de
fereastră prelucrează anumite informaţii pe baza mesajului primit, apoi
returnează controlul sistemului de operare.
Atunci când un program este lansat în execuţie, Windows creează o
„coadă de mesaje" („message queue") pentru programul respectiv, în această
coadă de mesaje sunt păstrate mesajele trimise către toate ferestrele pe care le
creează programul. Programul conţine o mică secvenţă de cod numită „ciclu
de mesaje" („message loop") care preia mesajele din coada de aşteptare şi le
distribuie procedurilor de fereastră corespunzătoare. Alte mesaje sunt trimise
direct procedurii de fereastră, fără să mai fie plasate în coada de mesaje.
O fereastră este creată întotdeauna pe baza unei „clase de fereastră".
Clasa specifică procedura de fereastră care prelucrează mesajele trimise către
fereastră. Folosirea unei clase de fereastră permite ca pe baza aceleiaşi clase

3
să se creeze mai multe ferestre care, ca urmare, folosesc aceeaşi procedură de
fereastră. De exemplu, toate butoanele din toate programele pentru Windows
sunt create pe baza aceleiaşi clase de fereastră. Această clasă are o procedură
de fereastră (aflată într-una dintre bibliotecile cu legături dinamice ale
sistemului de operare) care prelucrează mesajele trimise către ferestrele
butoanelor.

Înregistrarea clasei de fereastră


Clasa de fereastră identifică procedura de fereastră care prelucrează toate
mesajele trimise către fereastră. Atunci când creaţi o fereastră, definiţi şi
atributele suplimentare ale acesteia, care sunt unice pentru fereastra
respectivă.
Cele mai importante câmpuri ale clasei de fereastră 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 clase. 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. 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.
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. 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

4
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.
Următorul câmp este variabila handle a instanţei (care este chiar unul
dintre parametrii funcţiei WinMain):
wndclass.hInstance = hinstance;
Instrucţiunile:
wndclass.hIcon = LoadIcon (NULL, IDI_APPLICATION);
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. 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

5
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. Dacă programul nu are nici
un meniu, valoarea acestui câmp este 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;
Înainte de a crea fereastra programului trebuie să înregistraţi o clasă de
fereastră, apelând funcţia RegisterClassEx. 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 şi
declarată în program în felul următor:
WNDCLASSEX wndclass ;
După ce aţi definit cele 12 câmpuri ale structurii WNDCLASSEX apelaţi
funcţia RegisterClassEx:
RegisterClassEx (&wndclass) ;

Crearea ferestrei
Atunci când creaţi o fereastră apelând funcţia Create Window, puteţi să
specificaţi mai multe detalii despre fereastra respectivă. Î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ă
un exemplu de apel al funcţiei CreateWindow:
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

6
hlnstance, // variabila handle a instanţei
programului
NULL) ; // parametri de creare
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 ferestre lor, 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 para metri indicăm sistemului de operare
să folosească valorile prestabilite pentru o fereastră suprapusă. Î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ărinte-descendent, 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

7
(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,
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.
Apelul:
UpdateWindow (hwnd);
determină redesenarea zonei client. Acest lucru se face prin trimiterea
către procedura de fereastră a unui mesaj WM_PAINT.

Ciclul de mesaje
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;

8
Variabila msg este o structură de tip MSG.
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.
Instrucţiunea:
TranslateMessage (&msg);
retransmite structura msg sistemului de operare, pentru convertirea unor
mesaje de la tastatură.
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ă.

Procedura de fereastră
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. Procedura de fereastră este definită întotdeauna astfel:
LRESULT CALLBACK WndProc (HWND hwnd, UINT iMsg, WPARAM
wParam. LPARAM lParam)
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ă
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

9
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ă.
Mai jos sunt descrise două mesaje cele mai des întâlnite în programele
scrise pentru Windows.

Mesajul WM_PAINT
Unul din mesaje 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 creatp
de program. Vă amintiţi că parametrul de stil al structurii wndclass 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 ş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

10
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
anterior acoperită este invalidată. Procedura de fereastră primeşte un mesaj
WM_PAINT pentru redesenarea porţiunii respective.
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 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.

Sarcina lucrării: Creaţi o aplicaţie Windows, în centrul zonei client al căreia


este afişat un mesaj ”Lucrarea de laborator al studentului ...”. La
redimensionarea ferestrei, mesajul trebuie sa fie afişat în centrul zonei client.

Întrebări de control:
1. Care este sarcina principală a procedurii de fereastră?
2. Pe baza la ce se creează o fereastră?
3. Descrieţi câmpurile clasei de fereastră.
4. Ce este o variabilă handle?

11
5. Ce se întâmplă cu mesajele nedescrise în procedura de fereastră, dar
plasate în coada de mesaje?
6. Descrieţi funcţiile utilizate în program.

12
LUCRAREA DE LABORATOR NR. 2

TEMA: Interfaţa GDI

Scopul lucrării: de a studia primitivele oferite de interfaţa GDI

Noţiuni teoretice

Interfaţa pentru dispozitive grafice (GDI - Graphics Device


Interface) este o componentă a sistemului de operare Windows şi
are ca sarcină afişarea elementelor grafice (inclusiv a textului) pe
ecran şi la imprimantă.
De asemenea, trebuie să ştiţi că interfaţă Windows GDI îşi are
limitele ei. Cel puţin în acest moment, interfaţă GDI nu poate să
facă tot ce v-aţi putea dori de la o interfaţă grafică. Deşi puteţi să
mutaţi pe ecran obiecte grafice, GDI este, în general, un sistem de
afişare static, ce permite numai animaţii limitate. Aşa cum este
implementată în Windows 95, interfaţă GDI nu asigură un suport
direct pentru afişarea tridimensională sau pentru rotirea obiectelor.
De exemplu, atunci când desenaţi o elipsă, axele acesteia trebuie
să fie paralele cu axele sistemului de coordonate. Deşi unele
limbaje grafice folosesc numere în virgulă mobilă pentru
coordonatele virtuale. Windows 95 - din motive legate de
performanţă - foloseşte numai numere întregi pe 16 biţi aceasta
este una dintre deficienţele sistemului de operare Windows 95.
Windows NT permite folosirea coordonatelor pe 32 de biţi.
Din punctul de vedere al programatorului, interfaţa GDI este
formată din câteva sute de apeluri de funcţii şi unele tipuri de date,
macroinstrucţiuni şi structuri asociate acestor funcţii, înainte de a
studia în detaliu câteva dintre aceste funcţii, haideţi să vedem care
este structura generală a interfeţei GDI.

Tipuri de apeluri de funcţii


În general, apelurile de funcţii GDI pot fi clasificate în mai
multe categorii. Chiar dacă nu sunt foarte stricte şi există unele
suprapuneri, aceste categorii pot fi enunţate astfel:

13
 Funcţii care obţin (sau creează) şi eliberează (sau distrug) un
context de dispozitiv.
 Funcţii care obţin informaţii despre contextul de dispozitiv.
 Funcţii care desenează ceva.
 Funcţii care stabilesc sau obţin atribute ale contextului de
dispozitiv.
 Funcţii care lucrează cu obiecte GDI.
Elementele grafice pe care le afişaţi pe ecran sau le tipăriţi la
imprimantă pot fi împărţite în mai multe categorii, numite
„primitive". Iată care sunt aceste categorii:
 Linii şi curbe. Liniile reprezintă baza oricărui sistem de
desenare vectorial. GDI permite folosirea liniilor drepte, a
dreptunghiurilor, a elipselor (inclusiv subsetul de elipse
cunoscute sub numele de cercuri), a arcelor - care sunt curbe
reprezentând porţiuni din circumferinţa unei elipse sau a
curbelor Bezier. Liniile sunt desenate folosind peniţa curentă
selectată în contextul de dispozitiv.
 Suprafeţe pline. Dacă o serie de linii sau de curbe închid o
suprafaţă, aceasta poate fi „umplută" folosind pensula GDI
curentă. Această pensulă poate fi o culoare compactă, un
model (haşuri orizontale, verticale sau pe diagonală) sau o
imagine bitmap repetată pe verticală sau pe orizontală.
 Imagini bitmap. Imaginile bitmap sunt matrice dreptunghiulare
de biţi, care corespund pixelilor unui dispozitiv de afişare. În
general, acestea sunt folosite pentru afişarea imaginilor
complexe (deseori preluate din lumea reală) pe ecran sau
pentru tipărirea acestora la imprimantă. De asemenea,
imaginile bitmap sunt folosite pentru afişarea unor mici
imagini (cum ar fi pictograme, indicatoare de mouse şi
butoane de pe barele cu instrumente de lucru ale aplicaţiilor)
care trebuie afişate foarte rapid.
 Text. Textul este mai puţin „matematic" decât alte aspecte ale
graficii pe calculator. Structurile create pentru definirea
fonturilor şi pentru obţinerea informaţiilor despre fonturi sunt
printre cele mai mari din Windows. Începând cu versiunea
Windows 3.1, interfaţa GDI acceptă fonturile TrueType,
bazate pe contururi umplute, care pot fi manipulate de alte
14
funcţii GDI. Windows 95 acceptă în continuare şi fonturile
mai vechi, de tip bitmap (cum este fontul sistem prestabilit)
pentru compatibilitate şi pentru economisirea spaţiului de
memorie.
Alte aspecte ale interfeţei GDI nu sunt la fel de uşor de
clasificat. Printre acestea se numără:
 Moduri de mapare şi transformări. Modurile de mapare GDI
vă permit să desenaţi folosind ca unitate de măsură inci (sau
fracţiuni de inci), milimetri sau orice altă unitate de măsură.
De asemenea. Windows 95 asigură suportul pentru o
„transformare reală" exprimată printr-o matrice 3x3. Această
transformare permite deformarea şi rotirea obiectelor grafice.
Din păcate, această transformare nu este acceptată sub
Windows 95.
 Metafişiere (metafiles). Un metafişier este o colecţie de
comenzi GDI stocate într-o formă binară. Metafişierele sunt
folosite pentru transferarea reprezentărilor unor elemente
grafice vectoriale prin intermediul memoriei temporare
(clipboard).
 Regiuni (regions). O regiune este o suprafaţă complexă de
orice formă, definită ca o combinaţie booleană de regiuni mai
simple. În general, regiunile sunt stocate intern de GDI ca o
serie de linii de scanare, diferite de combinaţia de linii folosită
iniţial pentru definirea regiunii. Puteţi să folosiţi regiunile
pentru contururi, pentru umplere sau pentru decupare.
 Căi (paths). O cale este o colecţie de linii drepte şi curbe
stocate intern de GDI. Căile pot fi folosite pentru desenare,
pentru umplere sau pentru decupare. De asemenea, căile pot fi
transformate în regiuni.
 Decupare (clipping). Desenarea poate fi limitată la o anumită
secţiune a zonei client, numită zonă de decupare, care poate fi
dreptunghiulară sau poate avea o altă formă, definită printr-o
serie de linii. Zona de decupare este definită, în general, de o
cale sau de o regiune.
 Palete (palettes). Folosirea paletelor proprii este limitată, în
general, numai la monitoarele care pot reda 256 de culori.
Windows rezervă 20 dintre aceste culori pentru sistemul de
15
operare. Celelalte 236 de culori pot fi modificate pentru
afişarea corespunzătoare a imaginilor din lumea reală, stocate
ca imagini bitmap.
 Tipărire (printing). Deşi discuţiile din acest capitol sunt
limitate doar la afişarea pe ecran, tot ceea ce învăţaţi în acest
capitol poate fi aplicat şi operaţiilor de tipărire. (Vezi Capitolul
15 pentru alte informaţii despre tipărire.)

Contextul de dispozitiv
Atunci când vreţi să desenaţi la un dispozitiv de ieşire grafic
(cum ar fi ecranul sau imprimanta) trebuie să obţineţi mai întâi o
variabilă handle a contextului de dispozitiv (DC - device context).
Variabila handle este apoi inclusă ca parametru în apelul unei
funcţii GDI, identificând dispozitivul Ia care vreţi să desenaţi.
Contextul de dispozitiv conţine mai multe atribute curente, care
specifică modul de lucru al funcţiilor GDI pentru dispozitivul
respectiv. Aceste atribute permit ca la apelarea funcţiilor GDI să
fie specificate numai coordonatele de început sau dimensiunea, nu
şi toate celelalte informaţii de care sistemul de operare are nevoie
pentru desenarea obiectelor pe dispozitivul folosit. Atunci când
doriţi să modificaţi unul dintre aceste atribute ale contextului de
dispozitiv, apelaţi o funcţie specializată.

Obţinerea variabilei handle a contextului de dispozitiv


Sistemul de operare Windows vă pune la dispoziţie mai multe
metode pentru obţinerea variabilei handle a contextului de
dispozitiv. Dacă obţineţi o variabilă handle a contextului de
dispozitiv în timpul prelucrării unui mesaj, ar trebui să ştergeţi
această variabilă înainte de ieşirea din procedura de fereastră.
După ce este ştearsă, variabila handle nu mai poate fi folosită (nu
mai este validă).
Cea mai cunoscută metodă de obţinere şi de ştergere a
variabilei handle a contextului de dispozitiv implică folosirea
funcţiilor BeginPaint şi EndPaint în timpul prelucrării mesajului
WM_PAINT:
hdc = BeginPaint (hwnd, &ps);
[alte linii de program]
16
EndPaint (hwnd, &ps);
Variabila ps este o structură de tip PAINTSTRUCT. Câmpul
hdc al acestei structuri conţine variabila handle a contextului de
dispozitiv. Folosind variabila handle a contextului de dispozitiv,
obţinută prin apelarea funcţiei BeginPaint, nu puteţi să desenaţi
decât în regiunea invalidă a ferestrei. Funcţia BeginPaint validează
regiunea invalidă.
Programele Windows pot să obţină variabila handle a
contextului de dispozitiv şi în timpul prelucrării altor mesaje decât
WM_PAINT:
hdc = GetDC (hwnd);
(alte linii de program]
ReleaseDC (hwnd, hdc);
Acest context de dispozitiv se aplică zonei client a ferestrei
care are variabila handle hwnd. Principala diferenţă între apelul de
mai sus şi metoda folosirii funcţiilor BeginPaint şi EndPaint este
că variabila handle returnată de funcţia GetDC vă permite să
desenaţi în toată zona client a ferestrei. În plus, funcţiile GetDC şi
ReleaseDC nu validează eventualele regiuni invalide ale zonei
client.
Un program Windows poate să obţină şi o variabilă handle a
unui context de dispozitiv care se aplică întregii ferestre, nu numai
zonei client a ferestrei:
hdc = GetWindowDC (hwnd);
[alte linii de program]
ReleaseDC (hwnd, hdc);
[alte linii de program]
DeleteDC (hdc);

Obţinerea informaţiilor despre culori


Funcţia GetDeviceCaps vă permite să determinaţi modul de
organizare a memoriei în adaptoarele video şi numărul de culori
care pot fi reprezentate. Apelul de mai jos returnează numărul de
planuri de culoare:
iPlanes = GetDeviceCaps (hdc, PLANES);
Apelul următor returnează numărul de biţi de culoare folosiţi
pentru fiecare pixel:
17
iBitsPixel = GetDeviceCaps (hdc, BITSPIXEL);
Majoritatea adaptoarelor video care pot afişa culori folosesc
fie mai multe planuri de culoare, fie mai mulţi biţi de culoare
pentru fiecare pixel, dar nu pe amândouă; cu alte cuvinte, unul
dintre cele două apeluri de mai sus va returna valoarea 1. Numărul
de culori care pot fi redate de o placă video se poate calcula cu
formula următoare:
iColors = 1<<(iPlanes * iBitsPixel);
Această valoare poate să nu fie identică cu numărul de culori
obţinut prin apelarea funcţiei GetDeviceCaps cu parametrul
NUMCOLORS:
iColors = GetDeviceCaps (hdc, NUMCOLORS);
Windows foloseşte pentru reprezentarea culorilor o valoare
întreagă fără semn, pe 32 de biţi. Tipul de date folosit pentru
culori se numeşte COLORREF. Ultimii trei octeţi ai numărului
(cei mai puţin semnificativi) specifică valorile pentru culorile roşu,
verde şi albastru, de la O la 255, aşa cum se poate vedea în Figura
4-3. Rezultă o paletă potenţială de 224 culori (aproximativ 16
milioane de culori).

Valoarea pe 32 de biţi de mai sus e numită deseori „culoare


RGB". În fisierele antet din Windows sunt definite mai multe
macroinstrucţiuni pentru lucrul cu valorile RGB.
Macroinstructiunea RGB acceptă trei argumente, care reprezintă
valorile pentru culorile roşu, verde şi albastru şi le combină într-o
valoarea întreagă pe 32 de biţi, fără semn.
Astfel, valoarea
RGB (255, 0, 255)
este de fapt 0x00FF00FF, valoarea RGB pentru magenta. Dacă
toate cele trei argumente au valoarea 0, se obţine negrul, iar dacă
au valoarea 255, se obţine albul. Macroinstrucţiunile GetRValue,
GetGValue şi GetBValue extrag valorile pentru culorile primare,
sub forma unor octeţi fără semn, din valoarea RGB a culorii.
Aceste macroinstructiuni sunt utile atunci când apelaţi funcţii
Windows care returnează culori RGB.
18
Salvarea contextului de dispozitiv
În mod normal, Windows creează un nou context de dispozitiv
cu valori prestabilite atunci când apelaţi una dintre funcţiile
GetDC sau BeginPaint. Toate modificările făcute în atributele
contextului de dispozitiv se pierd atunci când contextul de
dispozitiv este şters din memorie prin apelarea funcţiei ReleaseDC
sau a funcţiei EndPaint. Dacă programul trebuie să folosească un
atribut cu o valoarea diferită de cea prestabilită va trebui să
iniţializaţi contextul de dispozitiv de fiecare dată când obţineţi o
variabilă handle:
caseWM_Paint:
hdc = BeginPaint (hwnd, &ps);
[iniţializează atributele contextului de dispozitiv]
[desenează zona client a ferestrei]
EndPaint (hwnd, &ps);
return 0;
Deşi această abordare este în general satisfăcătoare, s-ar putea
să preferaţi să salvaţi modificările făcute asupra contextului de
dispozitiv ia distrugerea acestuia, astfel încât valorile salvate să
redevină active la apelarea funcţiilor GetDC sau BeginPaint.

Desenarea liniilor
Interfaţa Windows GDI conţine şi funcţiile SetPixel şi
GetPixel. Deşi vom folosi funcţia SetPixel în programul
CONNECT din Capitolul 7, în programele de grafică propriu-zisă
aceste funcţii sunt rareori folosite. În majoritatea cazurilor, cea
mai simplă primitivă grafică vectorială trebuie să fie considerată
linia.
Windows poate să deseneze linii drepte, linii eliptice (linii
curbe, pe circumferinţa unei elipse) şi curbe Bezier. Cele şapte
funcţii acceptate de Windows 95 pentru desenarea liniilor sunt
LineTo (linii drepte), Polyline şi PolylineTo (o serie de linii drepte
conectate), PolyPolyline (mai multe linii poligonale), Arc (linii
eliptice), PolyBezier şi PolyBezierTo. (Interfaţa GDI din Windows
NT acceptă încă trei funcţii pentru desenarea liniilor: ArcTo,
AngleArc şi PolyDraw. Aceste funcţii nu sunt însă acceptate în
19
Windows 95.) Aspectul liniilor desenate cu aceste funcţii poate fi
influenţat de cinci atribute ale contextului de dispozitiv: poziţia
curentă a peniţei (numai pentru funcţiile LineTo, PolylineTo şi
PolyBezierTo), peniţa selectată, modul de afişare a fondului
(numai pentru peniţele care nu desenează cu o culoare compactă),
culoarea fondului (pentru modul OPAQUE de afişare a fondului)
şi modul de desenare.
Funcţia LineTo este una dintre puţinele funcţii GDI care nu are
nevoie de dimensiunile complete ale obiectului ce urmează să fie
desenat. Funcţia LineTo desenează o linie de la „poziţia curentă"
definită în contextul de dispozitiv pană la punctul specificat la
apelarea funcţiei (exclusiv acest punct). Poziţia curentă este
folosită ca punct de plecare şi de alte funcţii GDI. În contextul de
dispozitiv prestabilit, poziţia curentă are iniţial valoarea (0,0).
Dacă apelaţi funcţia LineTo fără să stabiliţi mai întâi poziţia
curentă, funcţia desenează o linie pornind din colţul din stânga-sus
al zonei client.
Dacă doriţi să desenaţi o linie din punctul (xStart, yStart) pană
în punctul (xEnd, yEnd) trebuie să apelaţi mai întâi funcţia
MoveToEx ca să stabiliţi ca poziţie curentă punctul (xStart,
ySfart):
MoveToEx (hdc, xStart, yStart, &pt) ;
unde pt este o structură de tip POINT, definită în fişierele antet.
Funcţia MoveToEx nu desenează nimic, ci doar schimbă
poziţia curentă. Poziţia curentă anterioară este stocată în structura
de tip POINT. Puteţi apoi să folosiţi funcţia LineTo ca să desenaţi
linia:
LineTo (hdc, xEnd, yEnd);
Apelul de mai sus desenează o linie până în punctul (xEnd,
yEnd), exclusiv acest punct. După apelul funcţiei LineTo, poziţia
curentă devine (xEnd, yEnd).
Puteţi să obţineţi poziţia curentă a peniţei apelând funcţia
GetCurrentPosition astfel:
GetCurrentPositionEx (hdc, &pt) ;
Atunci când aveţi o matrice de puncte pe care vreţi să le
conectaţi prin linii, puteţi să desenaţi mai uşor liniile folosind

20
funcţia Polyline. Instrucţiunea de mai jos desenează acelaşi
dreptunghi ca şi secvenţa de cod din exemplul anterior:
Polyline (hdc, pt, 5);
Ultimul parametru reprezintă numărul de puncte. Această
valoare putea fi reprezentată şi prin expresia (sizeof(pt) /
sizeof(POINT)). Funcţia Polyline are acelaşi efect ca şi o funcţie
MoveToEx urmată de mai multe funcţii LineTo, exceptând faptul
că funcţia Polyline nu schimbă poziţia curentă. Funcţia PolylineTo
este puţin diferită. Aceasta foloseşte ca punct de plecare poziţia
curentă şi stabileşte ca poziţie curentă sfârşitul ultimei linii
desenate. Codul de mai jos desenează acelaşi dreptunghi folosit ca
exemplu şi mai sus:
MoveToEx (hdc, pt[0].x, pt[0].y, NULL) ;
PolylineTo (hdc, pt + 1,4) ;
Deşi puteţi să folosiţi funcţiile Polyline şi PolylineTo şi pentru un
număr mic de funcţii, acestea sunt mai utile atunci când desenaţi o
curbă complexă formată din sute sau chiar mii de linii.
În continuare aş vrea să discutăm despre funcţia Arc, funcţie
care desenează o curbă eliptică. Dar funcţia Arc nu prea are sens
dacă nu discutăm mai întâi despre funcţia Ellipse, care, la rândul
ei, nu are sens fără o discuţie despre funcţia Rectangle. Si dacă
discutăm despre funcţiile Rectangle şi Ellipse, putem la fel de bine
să discutăm şi despre funcţiile RoundRect, Chord şi Pie.
Problema este că funcţiile Rectangle, Ellipse, RoundRect,
Chord şi Pie nu sunt tocmai nişte funcţii de desenare a liniilor.
Desigur, ele desenează şi linii, dar şi colorează zona închisă, cu
pensula curentă de colorare a suprafeţelor. În mod prestabilit,
această pensulă are culoarea albă, aşa că s-ar putea ca operaţia să
nu fie atât de evidentă atunci când încercaţi pentru prima dată să
utilizaţi aceste funcţii. Deşi, în sens strict, funcţiile aparţin unei
alte secţiuni din capitolul de faţă, „Desenarea suprafeţelor pline",
vom discuta acum despre fiecare.
Funcţiile de mai sus sunt asemănătoare prin faptul că sunt
construite pe baza unui „dreptunghi de încadrare" („bounding
box"). Dumneavoastră definiţi o casetă care încadrează obiectul -
dreptunghiul de încadrare - şi Windows desenează obiectul în
acest dreptunghi.
21
Cea mai simplă dintre aceste funcţii desenează un dreptunghi:
Rectangle (hdc, xLeft, yTop, xRight, yBottom) ;
Punctu cu coordonatele (xLeft, yTop) este punctul din stânga
sus, iar punctul cu coordonatele (xRight, yBottom) este punctul din
dreapta jos.
După ce aţi aflat cum se desenează un dreptunghi, folosind
aceiaşi parametri, veţi putea, la fel de simplu, să desenaţi şi o
elipsă:
Ellipse (hdc, xLeft, yTop, xRight, yBottom) ;
Funcţia pentru desenarea unui dreptunghi cu colţurile rotunjite
foloseşte acelaşi dreptunghi de încadrare ca şi funcţiile Rectangle
şi Ellipse, dar are încă doi parametri:
RoundRect (hdc, xLeft, yTop, xRight, yBottom,
xCornerEllipse, yCornerEllipse);
Pentru desenarea colţurilor rotunjite, Windows foloseşte o
mică elipsă. Lăţimea elipsei este xCornerEllipse iar înălţimea
acesteia este yCornerEllipse. Colţurile dreptunghiului sunt cu atât
mai rotunjite cu cât valorile parametrilor xCornerEllipse şi
yCornerEllipse sunt mai mari. Dacă xCornerEllipse este egal cu
jumătate din diferenţa dintre xLeft şi xRight iar yCornerEllipse
este egal cu jumătate din diferenţa dintre yTop şi yBottom, atunci
funcţia RoundRect va desena o elipsă.
Funcţiile Arc, Chord şi Pie primesc aceiaşi parametri:
Arc (hdc, xLeft, yTop, xRight, yBottom, xStart, yStart, xEnd,
yEnd);
Chord (hdc, xLeft, yTop, xRight, yBottom, xStart, yStart, xEnd,
yEnd);
Pie (hdc, xLeft, yTop, xRight, yBottom, xStart, yStart, xEnd,
yEnd);
În cazul funcţiei Arc, Windows şi-a terminat treaba, deoarece
arcul este o linie eliptică, nu o suprafaţă plină. În cazul funcţiei
Chord, Windows uneşte capetele arcului, iar în cazul funcţiei Pie,
uneşte capetele arcului cu centrul elipsei. Interioarele suprafeţelor
închise obţinute astfel sunt colorate cu pensula curentă.

Folosirea peniţelor de stoc

22
Atunci când apelaţi oricare dintre funcţiile de desenare a
liniilor, Windows foloseşte pentru desenarea liniilor „peniţa" (pen)
curentă selectată în contextul de dispozitiv. Peniţa selectată
determină culoarea, lăţimea şi tipul de linie (acestea pot fi
continue, punctate sau întrerupte). Peniţa din contextul prestabilit
de dispozitiv se numeşte BLACK_PEN. Aceasta desenează o linie
compactă, de culoare neagră, cu grosimea de un pixel, indiferent
de modul de mapare. BLACK_PEN este una dintre cele trei peniţe
„de stoc" (stock pen) furnizate de Windows. Celelalte două peniţe
de stoc sunt WHITE_PEN şi NULL_PEN. NULL_PEN este o
peniţă care nu desenează nimic. În plus, puteţi să creaţi peniţe
proprii.
Peniţe sunt determinate de propriile variabile handle. În
fişierele antet din Windows este definit tipul de date HPEN, care
reprezintă o variabilă handle a unei peniţe. Puteţi să definiţi o
variabilă (de exemplu, hPen) folosind această definiţie de tip:
HPEN hPen ;
Puteţi să obţineţi variabila handle a uneia dintre peniţele de
stoc apelând funcţia GetStockObject. De exemplu, să presupunem
că vreţi să folosiţi peniţa de stoc numită WHITE_PEN. Obţineţi
variabila handle a acesteia astfel:
hPen = GetStockObject (WHITE_PEN);
Apoi trebuie să selectaţi în contextul de dispozitiv peniţa a
cărei variabilă aţi obţinut-o, apelând funcţia SelectObject:
SelectObject (hdc, hPen) ;
După acest apel toate liniile pe care le desenaţi vor folosi
peniţa WHITE_PEN, până când selectaţi o altă peniţă în contextul
de dispozitiv sau ştergeţi contextul de dispozitiv.
În loc să definiţi explicit variabila hPen, puteţi să combinaţi
funcţiile GetStockObject şi SelectObject într-o singură
instrucţiune:
SelectObject (hdc, GetStockObject (WHITE_PEN)) ;
Dacă apoi vreţi să reveniţi la peniţa BLACK_PEN, puteţi să
obţineţi o variabilă handle a acesteia şi să o selectaţi în contextul
de dispozitiv tot cu o singură instrucţiune:
SelectObject (hdc, GetStockObject (WHITE_PEN));

23
Funcţia SelectObject returnează variabila handle a peniţei
selectate anterior în contextul de dispozitiv. Dacă deschideţi un
nou context de dispozitiv şi executaţi instrucţiunea:
hPen = SelectObject (hdc, GetStockObject
(WHITE_PEN)) ;
atunci peniţa curentă selectată în contextul de dispozitiv va fi
WHITE_PEN şi hPen va fi variabila handle a peniţei
BLACK_PEN. Puteţi apoi să selectaţi peniţa BLACK_PEN în
contextul de dispozitiv cu următoarea instrucţiune:
SelectObject (hdc, hPen) ;
Pentru a crea o peniţă proprie apelaţi funcţia:
hPen = CreatePen (iPenStyle, iWidth, rgbColor) ;
Parametrul iPenStyle precizează dacă se desenează o linie
continuă, o linie punctată sau una întreruptă. Parametrul iPenStyle
poate fi unul dintre identificatorii definiţi în fişierele antet din
Windows.
Parametrul rgbColor din funcţia CreatePen este un număr
întreg fără semn reprezentând culoarea peniţei. Pentru toate
stilurile de peniţe, exceptând PS_INSIDEFRAME, atunci când
selectaţi peniţa în contextul de dispozitiv, Windows converteşte
acest parametru la cea mai apropiată culoare pură pe care o poate
reprezenta dispozitivul de afişare. PS_INSIDEFRAME este
singurul stil care poate folosi culori amestecate, dar numai pentru
grosimi mai mari de un pixel.

Desenarea suprafeţelor pline


Cele şapte funcţii Windows pentru desenarea figurilor sunt
prezentate în tabelul următor:
Funcţia Figura
Rectangle Dreptunghi cu colţuri drepte
Ellipse Elipsă
RoundRect Dreptunghi cu colţuri rotunjite
Chord Arc pe circumferinţa unei elipse, având
capetele unite printr-o coardă
Pie Suprafaţă de forma unei felii de plăcintă,
reprezentând un segment de elipsă.

24
Polygon Figură geometrică având mai multe laturi
PolyPolygon Mai multe figuri geometrice cu mai
multe laturi
Windows desenează conturul figurilor folosind peniţa curentă
selectată în contextul de dispozitiv. Pentru acest contur sunt
folosite atributele stabilite pentru modul de desenare a fondului,
culoarea fondului şi modul de desenare, ca şi în cazul desenării
unei linii simple. Tot ceea ce aţi învăţat despre linii se aplică şi în
cazul contururilor.
Figurile sunt umplute folosind pensula selectată în contextul
de dispozitiv. În mod prestabilit, aceasta este pensula de stoc
WHITE_BRUSH, ceea ce înseamnă că interiorul figurilor va fi
umplut cu alb. Windows defineşte şase pensule de stoc: WHITE-
_BRUSH, LTGRAY_BRUSH, GRAY_BRUSH,
DKGRAY_BRUSH, BLACK_BRUSH şi NULL_BRUSH (sau
HOLLOW_BRUSH).
Puteţi să selectaţi una dintre aceste pensule în contextul de
dispozitiv la fel cum selectaţi o peniţă de stoc. Windows defineşte
tipul HBRUSH ca variabilă handle a unei pensule, aşa că puteţi să
definiţi mai întâi o variabilă de acest tip:
HBRUSH hBrush ;
Puteţi să obţineţi variabila handle a pensulei de stoc
GRAY_BRUSH apelând funcţia GetStockObject:.
hBrush = Get&toCkObject (GBAY_BRUSH) ;
Puteţi să o selectaţi apoi în contextul de dispozitiv folosind
funcţia SelectObject:
SelectObject (hdc, hBrush) ;
Din acest moment, figurile desenate vor avea interiorul gri.
Dacă vreţi să desenaţi o figură fără contur, selectaţi în
contextul de dispozitiv peniţa NULL_PEN:
SelectObject (hdc, GetStockObject (NULL_PEN)) ;
Dacă vreţi să desenaţi o figură fără să îi umpleţi interiorul,
selectaţi în contextul de dispozitiv pensula NULL_BRUSH:
SelectObject (hdc, GetStockObject (NULL_BRUSH)) ;
Aşa cum puteţi să creaţi peniţe proprii, puteţi să creaţi şi
pensule proprii.

25
Sistemele de coordonate ale dispozitivului
Windows mapează coordonatele logice specificate în funcţiile
GDI la coordonatele dispozitivului. Înainte de a discuta despre
sistemele de coordonate logice folosite de diferitele moduri de
mapare, vom discuta despre diferitele sisteme de coordonate de
dispozitiv definite în Windows pentru zona de afişare. Deşi în
general am lucrat doar în zona client a ferestrei, în anumite situaţii
Windows foloseşte alte două sisteme de coordonate de dispozitiv.
În toate sistemele de coordonate de dispozitiv sunt folosiţi pixelii
ca unitate de măsură. Valorile de pe axa orizontală (x) cresc de la
stânga la dreapta iar valorile de pe axa verticală (y) cresc de sus în
jos.
Atunci când folosim întregul ecran, spunem că lucrăm în
„coordonate ecran". Colţul din stânga-sus este punctul de
coordonate (0, 0). Coordonatele ecran sunt folosite în mesajul
WM_MOVE (pentru alte ferestre decât ferestrele descendent) şi în
următoarele funcţii Windows: CreateWinriow şi MoveWindow
(ambele pentru alte ferestre decât ferestrele descendent),
GetMessagePos, GetCursorPos, SetCursorPos, GetWindowRect,
WindowFromPoint şi SetBrushOrgEx. Acestea sunt funcţii care fie
nu au o fereastră asociată (cum ar fi cele două funcţii pentru
cursor), fie trebuie să mute (sau să găsească) o fereastră pe baza
unui punct de pe ecran. Dacă folosiţi funcţia CreateDC cu
parametrul DISPLAY ca să obţineţi un context de dispozitiv
pentru întregul ecran, atunci coordonatele logice specificate la
apelarea funcţiilor GDI vor fi mapate la coordonatele ecranului.
„Coordonatele de fereastră" se referă la întreaga fereastră a
ecranului, inclusiv bara de titlu, meniu, barele de derulare şi
chenarul ferestrei. Pentru o fereastră normală, punctul (0, 0) este
colţul din stânga-sus al chenarului de redimensionare.
Coordonatele de fereastră sunt folosite mai rar în Windows, dar
dacă obţineţi un context de dispozitiv cu ajutorul funcţiei
GetWindowDC, atunci coordonatele logice specificate la apelarea
funcţiilor GDI vor fi mapate la coordonatele ferestrei.
Al treilea sistem de coordonate de dispozitiv - cu care vom
lucra cel mai des -foloseşte „coordonatele zonei client". Punctul
(0,0) este colţul din stânga-sus al zonei client. Dacă obţineţi un
26
context de dispozitiv cu ajutorul funcţiei GetDC sau al funcţiei
BeginPaint, atunci coordonatele logice specificate Ia apelarea
funcţiilor GDI vor fi mapate la coordonatele zonei client.
Puteţi să transformaţi coordonatele zonei client în coordonatele
ecranului şi invers folosind funcţiile ClientToScreen şi
ScreenToClient. De asemenea, puteţi şă obţineţi poziţia şi
dimensiunea întregii ferestre în coordonate ecran folosind funcţia
GetWindowRect.

Sarcina lucrării: Scrieţi un program care afişează în zona client


un desen animat, utilizând toate primitivele GDI.

Întrebări de control:
1. Descrieţi principalele primitive ale interfeţei grafice.
2. Enumeraţi metodele de obţinere a variabilei handle a
dispozitivului de context.
3. Ce sisteme de coordonate ale dispozitivului de context
cunoaşteţi?

27
LUCRARE DE LABORATOR NR. 3

Tema: Curbe Bezier

Scopul lucrării: De a studia primitivele grafice ce permit afişarea


curbelor Bezier şi afişarea acestor curbe cu ajutorul formulelor.

Noţiuni teoretice

Termenul „spline" se referea odată la o riglă flexibilă din


lemn, metal sau cauciuc (florar), folosită pentru desenarea
curbelor. De exemplu, dacă aveaţi un număr de puncte aparţinând
unui grafic şi doreaţi să desenaţi o curbă între acestea (prin
interpolare sau extrapolare) trebuia să marcaţi mai întâi punctele
pe hârtie. Ancorând apoi rigla în punctele respective, nu vă
rămânea decât să trasaţi curba de-a lungul riglei. În prezent,
„spline" se referă la o funcţie matematică. Aceste funcţii au
diferite forme. Curbele Bezier sunt unele dintre cele mai
cunoscute pentru programarea grafică. În anii '60, compania de
automobile Renault trecea de la proiectarea manuală a caroseriilor
(care implica folosirea argilei) la o proiectare asistată de
calculator. Erau necesare instrumente matematice, iar Pierre
Bezier a pus la punct un set de formule care s-au dovedit foarte
utile pentru rezolvarea acestei probleme.
De atunci, datorită formei bidimensionale, curba Bezier s-a
dovedit a fi cea mai utilă curbă pentru grafica pe calculator. De
exemplu, în limbajul PostScript funcţiile Bezier sunt folosite
pentru toate curbele - elipsele sunt aproximare prin curbe Bezier
De asemenea, funcţiile Bezier sunt folosite pentru definirea
contururilor caracterelor din fonturile PostScript. (Fonturile
TrueType folosesc o formă simplificată, mai rapida, a curbelor.)
O curbă Bezier este definită prin patru puncte - două capete şi
două puncte de control. Capetele curbei sunt ancorate în cele două
puncte finale. Punctele de control acţionează ca nişte „magneţi"
care deformează linia dreaptă dintre cele două puncte finale.
Funcţiile Bezier sunt considerate utile pentru proiectarea
asistată de calculator, datorită câtorva caracteristici:
28
 În primul rând, cu puţină practică, de obicei puteţi să
manipulaţi curba până ajunge la o formă apropiată de cea
dorită.
 În al doilea rând, curbele Bezier sunt foarte uşor de controlat.
În unele variante ale funcţiilor spline, curba nu trece prin nici
unul din punctele care o definesc. Curbele Bezier, însă, sunt
întotdeauna ancorate în cele două puncte finale. (Aceasta este
una dintre ipotezele de la care porneşte calculul formulei
Bezier.) De asemenea, unele forme ale funcţiilor spline au
puncte de singularitate, din care curba se întinde la infinit. În
proiectarea asistată de calculator acest lucru este de cele mai
multe ori evitat. Ca urmare, curbele Bezier sunt întotdeauna
limitate de un patrulater (numit „carcasă convexă" - „convex
hull") format prin unirea punctelor finale şi a punctelor de
control.
 În al treilea rând, o altă caracteristică a curbelor Bezier
implică relaţiile dintre punctele finale şi punctele de control.
Curba este întotdeauna tangentă la linia trasată de la primul
punct final la primul punct de control şi are întotdeauna
aceeaşi direcţie cu această linie. (Această caracteristică este
ilustrată vizual de programul BEZIER.) De asemenea, curba
este întotdeauna tangentă la linia trasată de la al doilea punct
final la al doilea punct de control şi are întotdeauna aceeaşi
direcţie cu această linie. (Acestea sunt alte două ipoteze de la
care este derivată formula Bezier.)
 În al patrulea rând, curbele Bezier sunt satisfăcătoare şi din
punct de vedere estetic. Ştiu că acesta este un criteriu
subiectiv, dar nu este numai părerea mea.
Înainte de apariţia sistemului de operare Windows 95, trebuia
să creaţi propriile curbe Bezier folosind funcţia Polyline. De
asemenea, trebuia să cunoaşteţi următoarele ecuaţii parametrice
ale curbelor Bezier:
x(t) = (1-t)3x0 + 3t(1-t)2x1 + 3t2(1-t)x2 + t3x3
y(t) = (1-t)3y0 + 3t(1-t)2y1 + 3t2(1-t)y2 + t3y3
unde (x0, y0) este punctul de început al curbei, (x3, y3) este punctul
de sfârşit al curbei, iar cele două puncte de control sunt (x1, y1) şi
(x2, y2). Curba este trasată pentru t având valori de la 0 la 1.
29
În Windows 95 nu mai este nevoie să ştiţi aceste formule.
Pentru trasarea uneia sau a mai multor curbe Bezier conexe, puteţi
să folosiţi instrucţiunea:
PolyBezier (hdc, pt, iCount) ;
sau instrucţiunea:
PolyBezierTo (hide, pt, iCount) ;
În ambele cazuri, pt este o matrice de structuri POINT. Pentru
funcţia PolyBezier primele patru puncte specifică (în această
ordine) punctul de început, primul punct de control, al doilea
punct de control şi punctul final al curbei Bezier. Următoarele
curbe Bezier au nevoie doar de trei puncte, deoarece punctul de
început al următoarei curbe Bezier este punctul de sfârşit al primei
curbe, şi aşa mai departe. Parametrul iCount reprezintă numărul de
puncte din matrice, adică unu plus de trei ori numărul curbelor pe
care vreţi să le desenaţi.
Funcţia PolyBezierTo foloseşte poziţia curentă ca punct de
început pentru prima curbă Bezier. Fiecare curbă desenată are
nevoie doar de trei puncte. La returnarea funcţiei, poziţia curentă
este punctul final al ultimei curbe desenate.
Atenţie: atunci când trasaţi o serie de curbe Bezier conectate
între ele, legătura va fi lină numai dacă al doilea punct de control
al primei curbe, punctul de sfârşit al primei curbe (care este şi
punctul de început al celei de-a doua curbe) şi primul punct de
control al celei de-a doua curbe sunt coliniare, adică se află pe
aceeaşi linie dreaptă.

Sarcina lucrării: Scrieţi un programi care afişază curba Bezier,


utilizând funcţiile GDI standarde şi o curbă Bezier, afişată după
calcule prin formule.

Îngrebări de control:
1. Pentru ce se utilizează curbele Bezier?
2. Câte puncte sunt necesare pentru a afişa o curbă Bezier?
3. Care sunt metodele de afişare a curbelor Bezier?
4. Care sunt caracteristicile curbelor Bezier?

30
LUCRAREA DE LABORATOR NR. 4

TEMA: Principiile de lucru cu tastatura

Scopul lucrării: de a studia metodele şi principiile de lucru cu


tastatura. Însuşirea modului de prelucrare a mesajelor parvenite de
la tastatura.

Noţiuni teoretice

Arhitectura bazată pe mesaje a sistemului de operare Windows


este ideală pentru lucrul cu tastatură. Programul „află" despre
apăsarea unor taste prin intermediul mesajelor care ajung la
procedura de fereastră.
De fapt, lucrurile sunt puţin mai complicate: atunci când
utilizatorul apasă şi eliberează tastele, driverul de tastatură
transmite sistemului de operare informaţiile legate de acţiunile
asupra tastelor. Windows salvează aceste acţiuni (sub formă de
mesaje) în coada de aşteptare a sistemului. Mesajele de la tastatură
sunt apoi transferate, unul câte unul, în coada de mesaje a
programului căruia îi aparţine fereastra ce deţine „cursorul de
intrare" (despre care vom discuta imediat). Programul distribuie
mesajele procedurii de fereastră corespunzătoare.
Motivul acestui proces în două etape - stocarea mesajelor mai
întâi în coada de mesaje a sistemului şi apoi transferarea acestora
în coada de mesaje a aplicaţiilor - este legat de sincronizare.
Atunci când utilizatorul apasă pe taste într-un ritm mai rapid decât
cel în care programul prelucrează mesajele primite, Windows
stochează acţiunile neprelucrate în coada de aşteptare a sistemului,
deoarece una dintre acţiunile asupra tastaturii poate avea ca efect
comutarea cursorului de intrare (input focus) către un alt program.
În consecinţă, următoarele taste trebuie transmise celui de-al
doilea program.
Deşi tastatura este principala sursă de intrări de la utilizatori a
unui program pentru Windows, programele nu trebuie să
reacţioneze la toate mesajele pe care le primeşte de la tastatură.
Multe funcţii ale tastaturii sunt tratate chiar de Windows. De
31
exemplu, puteţi să ignoraţi apăsările de taste legate de funcţii
sistem. Acestea sunt, în general, combinaţii cu tasta Alt.
Programul nu este obligat să monitorizeze aceste taste,
deoarece Windows îi comunică efectul acestora. (Totuşi, dacă este
nevoie, programul poate face şi acest lucru.) De exemplu, dacă
utilizatorul selectează un articol dintr-un meniu cu ajutorul
tastaturii. Windows trimite programului un mesaj prin care îi
comunică articolul de meniu selectat, indiferent dacă utilizatorul a
folosit mouse-ul sau tastatura.
Unele programe pentru Windows folosesc „acceleratori" (sau
„taste de accelerare") pentru opţiunile de meniu folosite mai
frecvent. Acceleratorii sunt, de obicei, combinaţii de taste
funcţionale - sau alte taste corespunzătoare unor caractere - cu
tasta Ctrl. Tastele de accelerare sunt definite în fişierul de resurse
al programului.
Tastatura trebuie să fie partajată de toate aplicaţiile rulate
simultan sub Windows. Unele aplicaţii pot avea mai multe
ferestre, iar tastatura trebuie să fie partajată de toate ferestrele din
cadrul aceleiaşi aplicaţii. Atunci când este apăsată o tastă, o
singură fereastră trebuie să primească mesajul privind apăsarea
tastei respective. Fereastra care primeşte acest mesaj este fereastra
care deţine „cursorul de intrare" („input focus").
Dacă fereastra activă a fost redusă la o pictogramă, atunci nici
o fereastră nu deţine cursorul de intrare. Windows continuă să
trimită programului mesaje de la tastatură, dar acestea sunt trimise
într-o altă formă decât mesajele trimise unei ferestre active
normale.
O procedură de fereastră poate să afle când are cursorul de
intrare prin interceptarea mesajelor WM_SETFOCUS şi
WM_KILLFOCUS. Mesajul WM_SETFOCUS indică faptul că
fereastra primeşte cursorul de intrare (input focus), iar mesajul
WM_KILLFOCUS indică faptul că fereastra pierde cursorul de
intrare.
Atunci când apăsaţi o tastă, Windows inserează în coada de
aşteptare a ferestrei care deţine cursorul de intrare un mesaj
WM_KEYDOWN sau un mesaj WM_SYSKEYDOWN. Atunci

32
când eliberaţi fasta, Windows inserează în coada de aşteptare a
ferestrei un mesaj WM_KEYUP sau un mesaj WM_SYSKEYUP.

Tasta a fost apăsată Tasta a fost eliberată


Tastă obişnuită WM_KEYDOWN WM_KEYUP
Tastă de sistem WM_SYSKEYDOWN WM_SYSKEYUP

De obicei, mesajele de apăsare şi de eliberare a tastei sunt


trimise în pereche. Totuşi, dacă ţineţi apăsată o tastă pană când
aceasta se autorepetă, Windows trimite procedurii de fereastră o
serie de mesaje WM_KEYDOWN (sau WM_SYSKEYDOWN) şi
un singur mesaj WM_KEYUP sau (WM_SYSKEYUP) după
eliberarea tastei. Ca toate mesajele trimise prin coada de aşteptare,
mesajele pentru acţionări de taste conţin informaţii de timp. Puteţi
să obţineţi momentul relativ în care a fost apăsată sau eliberată o
tastă apelând funcţia GetMessageTime.

Taste obişnuite şi taste de sistem


Particula „SYS" din mesajele WM_SYSKEYDOWN şi
WM_SYSKEYUP provin de la cuvântul „system" şi se referă la
acţionările de taste care prezintă mai multă importanţă pentru
Windows decât pentru aplicaţiile Windows. Mesajele
WM_SYSKEYDOWN şi WM_SYSKEYUP sunt generate, de
obicei, pentru taste apăsate în combinaţie cu tasta Alt. Aceste
acţionari de taste apelează opţiuni din meniul programului ori din
meniul sistem, sunt folosite pentru funcţii ale sistemului, cum ar fi
comutarea ferestrei active (Alt+Tab sau Alt+Esc) sau sunt folosite
pentru acceleratori de meniu (Alt în combinaţie cu o tastă
funcţională). De obicei, programul ignoră mesajele
WM_SYSKEYDOWN şi WM_SYSKEYUP şi le retransmite
funcţiei DefWindowProc. Deoarece Windows manipulează
combinaţiile Alt+tastă, nu este nevoie să interceptaţi aceste
mesaje. Procedura de fereastră va primi alte mesaje, legate de
rezultatul acestor acţionări de taste (cum ar fi selectarea unei
opţiuni de meniu). Chiar dacă doriţi să includeţi în program codul
pentru interceptarea acestor mesaje (aşa cum vom face în
programul KEYLOCK din acest capitol), retransmiteţi mesajele
33
funcţiei DefWindowProc după ce le prelucraţi, astfel încât
Windows să le poată folosi în scopurile obişnuite.
Mesajele WM_KEYDOWN şi WM_KEYUP sunt generate, de
obicei, pentru tastele apăsate şi eliberate fără tasta Alt. Programul
poate să folosească sau să ignore aceste mesaje. Sistemul de
operare le ignoră.
Pentru toate mesajele legate de acţionările de taste variabila
lParam (pe 32 de biţi) transmisă procedurii de fereastră este
împărţită în şase câmpuri: contorul de repetare, codul de scanare
OEM, indicatorul flag pentru taste extinse, codul de context, starea
anterioară a tastei şi starea de tranziţie.
Contorul de repetare (Repeat Count) specifică numărul de
acţionari de taste reprezentat de un mesaj. În majoritatea cazurilor,
contorul de repetare are valoarea 1. Totuşi, dacă procedura de
fereastră nu reuşeşte să prelucreze mesajele de apăsare a unei taste
în ritmul de autorepetare (în mod prestabilit aproximativ 10
caractere pe secundă). Windows combină mai multe mesaje
WM_KEYDOWN sau WM_SYSKEYDOWN într-un singur
mesaj şi incrementează corespunzător contorul de repetare.
Contorul de repetare are întotdeauna valoarea 1 pentru mesajele
WM_KEYUP şi WM_SYSKEYUP.
Codul de scanare OEM (OEM Scan Code) este codul de
scanare al tastaturii, generat de componentele hardware. (Dacă aţi
scris programe în limbaj de asamblare, acest cod este identic cu
cel transmis în registrul AH, în timpul întreruperii apelului BIOS
16H.) În general, aplicaţiile Windows ignoră acest cod, deoarece
există metode mai bune de decodificare a informaţiilor de la
tastatură.
Indicatorul flag pentru taste extinse (Extended Key Flag) are
valoarea 1 dacă mesajul este generat de una dintre tastele
suplimentare de pe tastatura IBM extinsă. (Tastatura IBM extinsă
are tastele funcţionale în partea de sus şi un bloc separat sau
combinat de taste pentru tastele de deplasare şi tastele numerice.)
Acest indicator are valoarea 1 pentru tastele Alt şi Ctrl din partea
dreaptă a tastaturii, pentru tastele de deplasare (inclusiv tastele
Insert şi Delete) care nu fac parte din blocul de taste numerice,
pentru tastele Slash (/) şi Enter din blocul de taste numerice şi
34
pentru tasta Num Lock. În general, programele Windows ignoră
acest indicator.
Codul de context (Context Code) are valoarea 1 dacă este
apăsată tasta Alt. Acest bit va avea întotdeauna valoarea 1 pentru
mesajele WM_SYSKEYUP şi WM_SYSKEYDOWN şi valoarea
0 pentru mesajele WM_KEYUP si WM_KEYDOWN, cu două
excepţii:
1. O fereastră activă redusă la o pictogramă nu deţine
cursorul de intrare. Toate acţionările de taste generează
mesaje WM_SYSKEYUP şi WM_SYSKEYDOWN. Dacă
tasta Alt nu este apăsată, bitul pentru codul de context are
valoarea 0. (Windows foloseşte aceste mesaje astfel încât
fereastra activă redusă la o pictogramă să nu prelucreze
mesajele de la tastatură.)
2. În cazul folosirii unui driver de tastatură pentru alte limbi
decât limba engleză, unele caractere sunt generate prin
combinarea tastelor Shift, Ctrl sau Alt cu alte taste. În
această situaţie, bitul pentru codul de context din variabila
IParam care însoţeşte mesajele WM_KEYUP şi
WM_KEYDOWN are valoarea 1, dar mesajele nu
reprezintă acţionări de taste de sistem.
Starea anterioară a tastei (Previous Key State) are valoarea 0
dacă tasta nu a fost anterior apăsată, şi valoarea 1 dacă tasta a fost
apăsată. Are întotdeauna valoarea 1 pentru mesajele WM_KEYUP
şi WM_SYSKEYUP, dar poate fi 0 sau 1 pentru mesajele
WM_KEYDOWN şi WM_SYSKEYDOWN. Valoarea 1 indică
faptul că s-a primit un mesaj generat de autorepetarea unei taste.
Starea de tranziţie (Transition State) are valoarea 0 dacă tasta
este apăsată şi valoarea 1 dacă tasta este eliberată. Acest bit are
valoarea 1 pentru mesajele WM_KEYUP si WM_SYSKEYUP si
valoarea 0 pentru mesajele WM_KEYDOWN şi
WM_SYSKEYDOWN.

Coduri virtuale de taste


Deşi unele informaţii din parametrul lParam pot fi utile pentru
prelucrarea mesajelor WM_KEYUP, WM_SYSKEYUP,
WM_KEYDOWN şi WM_SYSKEYDOWN, parametrul wParam
35
este mult mai important. Acest parametru conţine codul virtual
care identifică tasta apăsată sau eliberată. Codurile virtuale pe care
le veţi folosi cel mai des au nume definite în fişierele antet din
Windows.
Parametrii lParam şi wParam care însoţesc mesajele
WM_KEYUP, WM_SYSKEYUP, WM_KEYDOWN şi
WM_SYSKEYDOWN nu spun nimic programului despre starea
tastelor de modificare. Puteţi să obţineţi starea oricărei taste
virtuale folosind funcţia GetKeyState. Această funcţie este
folosită, de obicei, pentru obţinerea stării tastelor de modificare
(Shift, Alt şi Ctrl) şi a tastelor de comutare (Caps Lock, Num Lock
şi Scroll Lock). De exemplu:
GetKeyState (VK_SHIFT);
returnează o valoare negativă (adică un număr în care primul bit
are valoarea 1) dacă tasta Shift este apăsată. Valoarea returnată de
apelul:
GetKeyState (VK_CAPITAL);
are un 1 în bitul cel mai puţin semnificativ dacă tasta Caps Lock
este activă.
Dacă într-adevăr aveţi nevoie de starea curentă a unei taste,
puteţi să folosiţi funcţia GetAsyncKeyState.

Utilizarea mesajelor de acţionare a tastelor


Ideea unui program care să obţină informaţii despre toate
acţiunile exercitate asupra tastelor este bună. Cu toate acestea,
majoritatea programelor Windows ignoră cea mai mare parte a
acestor mesaje. Mesajele WM_SYSKEYUP şi
WM_SYSKEYDOWN sunt folosite pentru funcţiile sistemului,
aşa că nu este nevoie să le interceptaţi. De asemenea, dacă
prelucraţi mesajele WM_KEYDOWN, de obicei puteţi să ignoraţi
mesajele WM_KEYUP.
În general, programele pentru Windows folosesc mesajele
WM_KEYDOWN pentru tastele care nu generează caractere. Deşi
s-ar putea să vă vină ideea să folosiţi mesajele pentru acţionări de
taste în combinaţie cu informaţiile despre tastele de modificare
(furnizate de funcţia GetKeyState) ca să transformaţi mesajele
pentru acţionări de taste în mesaje pentru caractere, nu faceţi acest
lucru. Veţi avea probleme datorită diferenţelor între tastaturile
36
internaţionale. De exemplu, dacă primiţi un mesaj
WM_KEYDOWN pentru care parametrul wParam are valoarea
33H, ştiţi că utilizatorul a apăsat tasta 3. Până aici totul este bine.
Dacă apelaţi funcţia GetKeyState veţi afla că tasta Shift este
apăsată şi s-ar putea să presupuneţi că utilizatorul a introdus un
caracter diez (#), lucru care poate să nu fie adevărat. Un utilizator
britanic ar fi introdus caracterul £. Mesajele WM_KEYDOWN
sunt utile pentru tastele de deplasare, tastele funcţionale şi tastele
speciale, precum Insert, Delete. Uneori tastele Insert, Delete si
tastele funcţionale sunt folosite ca acceleratori pentru comenzi de
meniu. Deoarece Windows transformă acceleratorii în comenzi de
meniu, nu este nevoie să prelucraţi în program mesajele pentru
acţionarea tastelor de accelerare. Unele programe non-Windows
folosesc foarte des tastele funcţionale în combinaţie cu tastele Alt,
Ctrl şi Shift. Puteţi face acest lucru şi în programele pentru
Windows, dar nu este recomandat. Dacă vreţi să folosiţi tastele
funcţionale, acestea trebuie să dubleze comenzi din meniuri. Unul
dintre obiectivele sistemului de operare Windows este să nu oblige
utilizatorii să memoreze sau să folosească liste complicate de
comenzi.
Am reuşit să eliminăm aproape totul, cu excepţia unui singur
lucru: de cele mai multe ori veţi prelucra mesajele
WM_KEYDOWN numai pentru tastele de deplasare a cursorului.
Atunci când prelucraţi mesajele trimise de tastele de deplasare
puteţi să verificaţi şi starea tastelor Shift şi Ctrl, folosind funcţia
GetKeyState. Unele funcţii Windows folosesc de multe ori tastele
de deplasare în combinaţie cu tasta Shift pentru extinderea unei
selecţii - de exemplu într-un procesor de texte. Tasta Ctrl este
folosită deseori pentru a schimba semnificaţia unei taste de
deplasare. De exemplu, tasta Ctrl în combinaţie cu săgeata spre
dreapta mută cursorul cu un cuvânt Ia dreapta.
Am discutat mai devreme despre ideea transformării mesajelor
generate de acţiona rea tastelor în mesaje caracter ţinând cont de
starea tastelor de modificare şi v-am avertizat că acest lucru nu
este suficient: trebuie să ţineţi seama şi de configuraţia diferită a
tastaturii de la o ţară la alta. Din acest motiv, nu trebuie să
încercaţi să transformaţi dumneavoastră mesajele generate de
37
acţionarea tastelor în mesaje caracter. Windows o poate face în
locul dumneavoastră. Aţi mai văzut această secvenţă de cod:
while (GetMessage (&msg, NULL, 0, 0))
{ TranslateMessage (&msg);
DispatchMessage (&msg);
}
Acesta este un ciclu tipic de tratare a mesajelor din funcţia
WinMain. Funcţia GetMessage preia următorul mesaj din coada de
aşteptare şi completează câmpurile structurii msg. Funcţia
DispatchMessage transmite mesajul procedurii de fereastră
corespunzătoare. Între cele două funcţii este apelată funcţia
TranslateMessage, care transformă mesajele generate de
acţionarea tastelor în mesaje caracter. Dacă mesajul este
WM_KEYDOWN sau WM_SYSKEYDOWN şi dacă tasta
apăsată, în funcţie de starea tastelor de modificare, generează un
caracter, atunci funcţia TranslateMessage inserează un mesaj
caracter în coada de aşteptare. Acesta va fi următorul mesaj pe
care îl va prelua funcţia GetMessage după mesajul generat de
acţionarea tastei. Există patru mesaje caracter:

Caractere Caractere
„moarte"
Caractere non- WM_CHAR WM_DEADCHAR
sistem
Caractere sistem WM_SYSCHAR WM_SYSDEADCHAR

Mesajele WM_CHAR şi WM_DEADCHAR sunt obţinute din


mesaje WM_KEYDOWN. Mesajele WM_SYSCHAR şi
WM_SYSDEADCHAR sunt obţinute din mesaje
WM_SYSKEYDOWN. În majoritatea cazurilor programul poate
să ignore toate celelalte mesaje în afară de WM_CHAR.
Parametrul lParam transmis procedurii de fereastră are acelaşi
conţinut ca şi parametrul lParam al mesajului generat de
acţionarea tastei din care a fost obţinut mesajul caracter.
Parametrul wParam conţine codul ASCII al caracterului.
O metodă cea mai simplă de a crea o interfaţă a tastaturii este
utilizarea logicii prelucrării mesajelui WM_KEYDOWN în procedura
de fereastră. De exemplu:
38
...
case WM_KEYDOWN:
switch(wParam)
{
case ‘x’: // При нажатии клавиши ‘x’ включается
// обработчик данного сообщения
[ Необходимые операторы обработки ]
break;
...
}
return 0;
...
Acest fragment de program este asemănător cu secvenţele de
cod pentru tratarea caracterelor dintr-un program MS-DOS
obişnuit.

Sarcina lucrării: de scris un program care în zona client afişază


un desen, care poate fi mişcat cu ajutorul tastelor ← (la stânga),
↑(în sus), →(la dreapta), ↓(în jos). De asemenea, trebuie să fie
prevăzute taste pentru rotirea figurii.

Întrebări de control:

39

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