Documente Academic
Documente Profesional
Documente Cultură
PROGRAMAREA de SISTEM Indrumarpentru
PROGRAMAREA de SISTEM Indrumarpentru
Noţiuni teoretice
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.
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.
Î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
Noţiuni teoretice
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ă.
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ă.
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.
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.
Î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
Noţiuni teoretice
Î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
Noţiuni teoretice
32
când eliberaţi fasta, Windows inserează în coada de aşteptare a
ferestrei un mesaj WM_KEYUP sau un mesaj WM_SYSKEYUP.
Caractere Caractere
„moarte"
Caractere non- WM_CHAR WM_DEADCHAR
sistem
Caractere sistem WM_SYSCHAR WM_SYSDEADCHAR
Întrebări de control:
39