PROGRAMAREA de SISTEM Indrumar Pentru Lucrari de Laborator

65
LUCRARE DE LABORATOR NR. 1 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

description

Programarea in Windows

Transcript of PROGRAMAREA de SISTEM Indrumar Pentru Lucrari de Laborator

Page 1: PROGRAMAREA de SISTEM Indrumar Pentru Lucrari de Laborator

LUCRARE DE LABORATOR NR. 1

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ţiiWindows 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

Page 2: PROGRAMAREA de SISTEM Indrumar Pentru Lucrari de Laborator

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 în Windows este un tip de programare orientată pe obiecte (OOP -object-oriented programming). Acest lucru devine evident dacă vă 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ă".

2

Page 3: PROGRAMAREA de SISTEM Indrumar Pentru Lucrari de Laborator

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 să se creeze mai multe ferestre care, ca urmare, folosesc aceeaşi procedură de fereastră. De exemplu, toate butoanele din toate programele pentru Windows

3

Page 4: PROGRAMAREA de SISTEM Indrumar Pentru Lucrari de Laborator

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 sus, prefixul lpfn înseamnă, în notaţia ungară, „pointer de tip long la o funcţie".

Următoarele două instrucţiuni:

4

Page 5: PROGRAMAREA de SISTEM Indrumar Pentru Lucrari de Laborator

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 pensule „de stoc"). Funcţia GetStockObject returnează o variabilă handle a unei pensule albe:wndclass.hbrBackground = (HBRUSH) GetStockObject (WHITE_BRUSH);

5

Page 6: PROGRAMAREA de SISTEM Indrumar Pentru Lucrari de Laborator

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 terestreiAtunci 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 ferestreiWS_OVERLAPPEDWINDOW, // stilul ferestreiCW_USEDEFAULT, // poziţia iniţiala pe axa xCW_USEDEFAULT, // poziţia iniţiala pe axa yCW_USE DEFAULT, // dimensiunea iniţiala pe axa xCW_USEDEFAULT, // dimensiunea iniţiala pe axa yNULL, // variabila handle a ferestrei

părinteNULL, // variabila handle a meniuluihlnstance, // variabila handle a instanţei

programuluiNULL) ; // parametri de creare

6

Page 7: PROGRAMAREA de SISTEM Indrumar Pentru Lucrari de Laborator

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 (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

7

Page 8: PROGRAMAREA de SISTEM Indrumar Pentru Lucrari de Laborator

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 ferestreiDupă 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 mesajeDupă apelarea funcţiei UpdateWindow, fereastra devine vizibilă pe ecran.

Programul trebuie să fie acum pregătit să citească intrările de la mouse şi de la tastatură. Windows formează o „coadă de mesaje" pentru fiecare program rulat concurenţial. Atunci când apare un eveniment exterior, Windows converteşte acest eveniment într-un mesaj pe care îl plasează în coada de aşteptare.

Un program preia mesajele din coada de aşteptare prin executarea unei secvenţe de cod numită „ciclu de mesaje" („message loop"):

while (GetMessage (&msg, NULL, 0, 0)){

TranslateMessage (&msg) ;DispatchMessage (&msg) ;

} return msg.wParam;

Variabila msg este o structură de tip MSG.Funcţia GetMessage apelată la începutul ciclului de mesaje preia un

mesaj din coada de aşteptare:

8

Page 9: PROGRAMAREA de SISTEM Indrumar Pentru Lucrari de Laborator

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 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

9

Page 10: PROGRAMAREA de SISTEM Indrumar Pentru Lucrari de Laborator

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_PAINTUnul din mesaje prelucrat de funcţia WndProc este WM_PAINT. Acest

mesaj este foarte important în programarea în 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 creată 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 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

10

Page 11: PROGRAMAREA de SISTEM Indrumar Pentru Lucrari de Laborator

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_DESTROYUn 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 a 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?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.

11

Page 12: PROGRAMAREA de SISTEM Indrumar Pentru Lucrari de Laborator

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:

12

Page 13: PROGRAMAREA de SISTEM Indrumar Pentru Lucrari de Laborator

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

13

Page 14: PROGRAMAREA de SISTEM Indrumar Pentru Lucrari de Laborator

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

14

Page 15: PROGRAMAREA de SISTEM Indrumar Pentru Lucrari de Laborator

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 dispozitivAtunci 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 dispozitivSistemul 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]

15

Page 16: PROGRAMAREA de SISTEM Indrumar Pentru Lucrari de Laborator

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 culoriFuncţ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:

16

Page 17: PROGRAMAREA de SISTEM Indrumar Pentru Lucrari de Laborator

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, valoareaRGB (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.

17

Page 18: PROGRAMAREA de SISTEM Indrumar Pentru Lucrari de Laborator

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 liniilorInterfaţ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

18

Page 19: PROGRAMAREA de SISTEM Indrumar Pentru Lucrari de Laborator

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

19

Page 20: PROGRAMAREA de SISTEM Indrumar Pentru Lucrari de Laborator

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.

20

Page 21: PROGRAMAREA de SISTEM Indrumar Pentru Lucrari de Laborator

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

21

Page 22: PROGRAMAREA de SISTEM Indrumar Pentru Lucrari de Laborator

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));

22

Page 23: PROGRAMAREA de SISTEM Indrumar Pentru Lucrari de Laborator

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 plineCele şapte funcţii Windows pentru desenarea figurilor sunt

prezentate în tabelul următor:

Funcţia FiguraRectangle Dreptunghi cu colţuri drepteEllipse ElipsăRoundRect Dreptunghi cu colţuri rotunjiteChord 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ă.

23

Page 24: PROGRAMAREA de SISTEM Indrumar Pentru Lucrari de Laborator

Polygon Figură geometrică având mai multe laturiPolyPolygon Mai multe figuri geometrice cu mai

multe laturiWindows 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.

24

Page 25: PROGRAMAREA de SISTEM Indrumar Pentru Lucrari de Laborator

Sistemele de coordonate ale dispozitivuluiWindows 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

25

Page 26: PROGRAMAREA de SISTEM Indrumar Pentru Lucrari de Laborator

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?

26

Page 27: PROGRAMAREA de SISTEM Indrumar Pentru Lucrari de Laborator

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:

27

Page 28: PROGRAMAREA de SISTEM Indrumar Pentru Lucrari de Laborator

Î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.

28

Page 29: PROGRAMAREA de SISTEM Indrumar Pentru Lucrari de Laborator

Î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?

29

Page 30: PROGRAMAREA de SISTEM Indrumar Pentru Lucrari de Laborator

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

30

Page 31: PROGRAMAREA de SISTEM Indrumar Pentru Lucrari de Laborator

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

31

Page 32: PROGRAMAREA de SISTEM Indrumar Pentru Lucrari de Laborator

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_KEYUPTastă 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 sistemParticula „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

32

Page 33: PROGRAMAREA de SISTEM Indrumar Pentru Lucrari de Laborator

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

33

Page 34: PROGRAMAREA de SISTEM Indrumar Pentru Lucrari de Laborator

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 tasteDeşi unele informaţii din parametrul lParam pot fi utile pentru

prelucrarea mesajelor WM_KEYUP, WM_SYSKEYUP, WM_KEYDOWN şi WM_SYSKEYDOWN, parametrul wParam

34

Page 35: PROGRAMAREA de SISTEM Indrumar Pentru Lucrari de Laborator

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 tastelorIdeea 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

35

Page 36: PROGRAMAREA de SISTEM Indrumar Pentru Lucrari de Laborator

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

36

Page 37: PROGRAMAREA de SISTEM Indrumar Pentru Lucrari de Laborator

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-sistem WM_CHAR WM_DEADCHAR

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:

...case WM_KEYDOWN:

37

Page 38: PROGRAMAREA de SISTEM Indrumar Pentru Lucrari de Laborator

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:

38