Sunteți pe pagina 1din 28

Capitolul 5.

Tastatura
Ca orice program interactiv rulat pe un calculator personal, aplicaiile Windows 95 se bazeaz foarte mult pe tastatur pentru intrrile utilizatorului. Dei Windows accept i mouse-ul ca dispozitiv de intrare, tastatur deine nc poziia principal. Deoarece utilizatorii mai vechi ai calculatoarelor personale prefer s foloseasc tastatura n locul mouse-ului, recomand dezvoltatorilor de programe s ncerce implementarea complet a funciilor programului de la tastatur. (Desigur, n unele situaii - este cazul utilizrii unui program de desenare sau a unui program de editare a publicaiilor (DTP), tastatura se dovedete a fi ineficient, motiv pentru care mouse-ul este indispensabil). Tastatura nu poate fi tratat ca un dispozitiv de intrare independent de celelalte funcii ale programului. De exemplu, programele rspund deseori la intrrile de la tastatur afind caracterele introduse n zona client a ferestrei. Astfel, manipularea intrrilor de la tastatur i afiarea textului trebuie s fie tratate mpreun. Dac adaptarea programului pentru limbi sau piee strine este important pentru dumneavoastr, atunci va trebui s tii cteva lucruri i despre suportul asigurat de Windows 95 pentru setul extins de caractere ASCII (codurile de la 128 n sus), pentru setul de caractere pe doi octei (double-byte character set - DBCS) i despre suportul asigurat de Windows NT pentru codificarea pe 16 bii a caracterelor de la tastatur (cunoscut sub numele de Unicode). Tastatura elemente de baz Aa cum probabil v dai seama, arhitectura bazat pe mesaje a sistemului de operare Windows este ideal pentru lucrul cu tastatur. Programul afl" despre apsarea unor taste prin intermediul mesajelor care ajung la procedura de fereastr. De fapt, lucrurile sunt puin mai complicate: atunci cnd utilizatorul apas i elibereaz tastele, driverul de tastatur transmite sistemului de operare informaiile legate de aciunile asupra tastelor. Windows salveaz aceste aciuni (sub form de mesaje) n coada de ateptare a sistemului. Mesajele de la tastatur sunt apoi transferate, unul cte unul, n coada de mesaje a programului cruia i aparine fereastra ce deine cursorul de intrare" (despre care vom discuta imediat). Programul distribuie mesajele procedurii de fereastr corespunztoare. Motivul acestui proces n dou etape - stocarea mesajelor mai nti n coada de mesaje a sistemului i apoi transferarea acestora n coada de mesaje a aplicaiilor - este legat de sincronizare. Atunci cnd utilizatorul apas pe taste ntr-un ritm mai rapid dect cel n care programul prelucreaz mesajele primite, Windows stocheaz aciunile neprelucrate n coada de ateptare a sistemului, deoarece una dintre aciunile asupra tastaturii poate avea ca efect comutarea cursorului de intrare (input focus) ctre un alt program. n consecin, urmtoarele taste trebuie transmise celui de-al doilea program. Windows trimite programelor opt tipuri de mesaje prin care indic diferite evenimente de tastatur. S-ar putea s par numeroase, dar programul poate s ignore multe dintre acestea, fr probleme. De asemenea, n majoritatea cazurilor, aceste mesaje conin mai multe informaii despre tastatur dect avei nevoie n program. O parte a sarcinii de manipulare a tastaturii const n a ti ce mesaje sunt importante. Ignorarea tastaturii Dei tastatura este principala surs de intrri de la utilizatori a unui program pentru Windows, programele nu trebuie s reacioneze la toate mesajele pe care le primete de la tastatur. Multe funcii ale tastaturii sunt tratate chiar de Windows. De exemplu, putei s ignorai apsrile de taste legate de funcii sistem. Acestea sunt, n general, combinaii cu tasta Alt. Programul nu este obligat s monitorizeze aceste taste, deoarece Windows i comunic efectul acestora. (Totui, 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. (Despre meniuri vom discuta n Capitolul 10.) Unele programe pentru Windows folosesc acceleratori" (sau taste de accelerare") pentru opiunile de meniu folosite mai frecvent. Acceleratorii sunt, de obicei, combinaii de taste funcionale - sau alte taste corespunztoare unor caractere - cu tasta Ctrl. Tastele de accelerare sunt definite n fiierul de resurse al programului, (n Capitolul 10 vom vedea cum convertete Windows acceleratorii n mesaje pentru comenzile din meniu. Nu trebuie s facei dumneavoastr aceast conversie.) Casetele de dialog (despre care vom discuta n Capitolul 11) au i ele o interfa cu tastatura, dar programele, n general, nu trebuie s monitorizeze tastatura ct timp este activ o caset de dialog. Interfaa cu tastatura este manipulat de Windows i acesta trimite programului mesaje prin care i comunic efectele tastelor apsate. Casetele de dialog pot conine controale de editare pentru introducerea textului. Acestea sunt mici casete, n care utilizatorul poate s scrie un ir de caractere. Windows asigur logica de funcionare a controlului de editare i furnizeaz programului coninutul final al controlului, dup ce utilizatorul a terminat. Chiar i n fereastra principal putei s definii ferestre descendent, care s funcioneze ca i controalele de editare. Un astfel de exemplu este programul POPPAD din Capitolul 8. Acesta este ceva mai mult dect un control de editare cu mai multe linii, care se bazeaz pe sistemul de operare pentru partea mai grea a operaiilor.

Cursorul, cursorul, cine a luat cursorul de intrare? Tastatura trebuie s fie partajat de toate aplicaiile rulate simultan sub Windows. Unele aplicaii pot avea mai multe ferestre, iar tastatura trebuie s fie partajat de toate ferestrele din cadrul aceleiai aplicaii. Atunci cnd este apsat o tast, o singur fereastr trebuie s primeasc mesajul privind apsarea tastei respective. Fereastra care primete acest mesaj este fereastra care deine cursorul de intrare" (input focus"). Conceptul cursorului de intrare este strns legat de conceptul de fereastr activ". Fereastra care deine cursorul de intrare este fie fereastra activ, fie o fereastr descendent a ferestrei active. De obicei, fereastra activ este uor de identificat. Dac fereastra activ are o bar de titlu, Windows evideniaz bara de titlu a acesteia. Dac fereastra activ are un cadru de dialog (o form des ntlnit n casetele de dialog) n locul unei bare de titlu, Windows evideniaz acest cadru. Dac fereastra activ a fost redus la o pictogram (minimizat), Windows evideniaz textul afiat sub pictogram. Ferestrele descendent mai des ntlnite sunt controale, precum butoanele de apsare, butoanele radio, casetele de validare, barele de derulare, casetele de editare i casetele list, care, de obicei, apar n casete de dialog. Ferestrele descendent nu sunt niciodat ferestre active. Dac o fereastr descendent deine cursorul de intrare, atunci fereastra activ este fereastra printe. Controalele de tip fereastr descendent indic faptul c dein cursorul de intrare prin afiarea unui cursor care clipete sau a unui cursor de editare. Dac fereastra activ a fost redus la o pictogram, atunci nici o fereastr nu deine cursorul de intrare. Windows continu s trimit programului mesaje de la tastatur, dar acestea sunt trimise ntr-o alt form dect mesajele trimise unei ferestre active normale. O procedur de fereastr poate s afle cnd are cursorul de intrare prin interceptarea mesajelor WM_SETFOCUS i WM_KILLFOCUS. Mesajul WM_SETFOCUS indic faptul c fereastra primete cursorul de intrare (input focus), iar mesajul WM_KILLFOCUS indic faptul c fereastra pierde cursorul de intrare. Acionri de taste i caractere Mesajele privind tastatura pe care un program le recepioneaz de la sistemul de operare fac diferena ntre acionrile de taste" (keystrokes") i caractere". Aceste noiuni sunt legate de cele dou moduri n care putei s privii tastatura. n primul rnd, tastatura este o colecie de taste. Tastatura are o singur tast A; apsarea tastei A este o acionare de tast, iar eliberarea tastei A este tot o acionare de tast. Tastatura este, ns, n acelai timp, i un dispozitiv de intrare care genereaz caractere afiabile. Tasta A poate s genereze mai multe caractere, n funcie de starea tastelor Ctrl, Shift i Caps Lock. n mod normal, caracterul generat este a. Dac tasta Shift este apsat, sau tasta Caps Lock este activ, caracterul generat este A. Dac tasta Ctrl este apsat, caracterul generat este Ctrl+A. Dac se folosete un driver de tastatur pentru o limb strin, apsarea tastei A poate s fie precedat de un caracter mort" (dead-character key") sau de tastele Shift, Ctrl sau Alt n diferite combinaii. Combinaiile pot s genereze un caracter a sau A cu un accent. Pentru acionrile de taste care genereaz caractere afiabile, Windows trimite programului att mesaje pentru acionarea de taste, ct i mesaje pentru caractere. Unele taste nu genereaz caractere. Astfel de taste sunt Shift, tastele funcionale, tastele de deplasare i tastele speciale, precum Insert i Delete. n cazul acestor taste, Windows genereaz numai mesaje pentru acionari de taste. Mesaje pentru acionri de taste Atunci cnd apsai o tast, Windows insereaz n coada de ateptare a ferestrei care deine cursorul de intrare un mesaj WM_KEYDOWN sau un mesaj WM_SYSKEYDOWN. Atunci cnd eliberai fasta, Windows insereaz n coada de ateptare a ferestrei un mesaj WM_KEYUP sau un mesaj WM_SYSKEYUP.
Tasta a fost apsat Tast obinuit Tast de sistem WM_KEYDOWN WM_SYSKEYDOWN Tasta a fost eliberat WM_KEYUP WM_SYSKEYUP

De obicei, mesajele de apsare i de eliberare a tastei sunt trimise n pereche. Totui, dac inei apsat o tast pan cnd 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 ateptare, mesajele pentru acionri de taste conin informaii de timp. Putei s obinei momentul relativ n care a fost apsat sau eliberat o tast apelnd funcia GetMessageTime. Taste obinuite i taste de sistem Particula SYS" din mesajele WM_SYSKEYDOWN i WM_SYSKEYUP provin de la cuvntul system" i se refer la acionrile de taste care prezint mai mult importan pentru Windows dect pentru aplicaiile Windows. Mesajele WM_SYSKEYDOWN i WM_SYSKEYUP sunt generate, de obicei, pentru taste apsate n combinaie cu tasta Alt. Aceste acionari de taste apeleaz opiuni din meniul programului ori din meniul sistem, sunt folosite pentru

funcii ale sistemului, cum ar fi comutarea ferestrei active (Alt+Tab sau Alt+Esc) sau sunt folosite pentru acceleratori de meniu (Alt n combinaie cu o tast funcional). De obicei, programul ignor mesajele WM_SYSKEYDOWN i WM_SYSKEYUP i le retransmite funciei DefWindowProc. Deoarece Windows manipuleaz combinaiile Alt+tast, nu este nevoie s interceptai aceste mesaje. Procedura de fereastr va primi alte mesaje, legate de rezultatul acestor acionri de taste (cum ar fi selectarea unei opiuni de meniu). Chiar dac dorii s includei n program codul pentru interceptarea acestor mesaje (aa cum vom face n programul KEYLOCK din acest capitol), retransmitei mesajele funciei DefWindowProc dup ce le prelucrai, astfel nct Windows s le poat folosi n scopurile obinuite. Dar s ne gndim puin la aceste lucruri. Aproape toate mesajele care afecteaz fereastra programului trec mai nti prin procedura de fereastr. Windows prelucreaz aceste mesaje numai dac le retransmitei funciei DefWindowProc. De exemplu, dac n procedura de fereastr adugai urmtoarele linii:
case WM_SYSKEYDOWN : case WM_SYSKEYUP : case WM_SYSCHAR : return 0 ;

dezactivai toate operaiile legate de combinaiile Alt+tast (comenzi de meniu, Alt+Tab, Alt+Esc i aa mai departe) atunci cnd programul deine cursorul de intrare. Mesajele WM_KEYDOWN i WM_KEYUP sunt generate, de obicei, pentru tastele apsate i eliberate fr tasta Alt. Programul poate s foloseasc sau s ignore aceste mesaje. Sistemul de operare le ignor. Variabila lParam Pentru toate mesajele legate de acionrile de taste variabila lParam (pe 32 de bii) transmis procedurii de fereastr este mprit n ase cmpuri: contorul de repetare, codul de scanare OEM, indicatorul flag pentru taste extinse, codul de context, starea anterioar a tastei i starea de tranziie (vezi Figura 5-1). Indicator flag pentru taste extinse

Figura 5-1. Cele sase cmpuri de bii ale variabilei IParam din mesajele pentru acionari de taste. Contorul de repetare Contorul de repetare (Repeat Count) specific numrul de acionari de taste reprezentat de un mesaj. n majoritatea cazurilor, contorul de repetare are valoarea 1. Totui, dac procedura de fereastr nu reuete s prelucreze mesajele de apsare 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 corespunztor contorul de repetare. Contorul de repetare are ntotdeauna valoarea 1 pentru mesajele WM_KEYUP i WM_SYSKEYUP. Deoarece o valoare mai mare dect 1 a contorului de repetare indic faptul c rata de autorepetare a tastelor depete posibilitile de prelucrare ale programului, exist situaii n care putei s ignorai contorul de repetare atunci cnd prelucrai mesajele de la tastatur. Aproape toat lumea a avut ocazia s vad cum un procesor de texte sau un program de calcul tabelar deruleaz ecranul mai mult dect trebuie datorit comenzilor suplimentare stocate n bufferul de tastatur. Ignorarea contorului de repetare va reduce foarte mult posibilitatea apariiei unei asemenea situaii. n alte cazuri ns este bine s folosii contorul de repetare. Putei s ncercai ambele variante i s vedei pe care o preferai pentru programul dumneavoastr. Codul de scanare OEM Codul de scanare OEM (OEM Scan Code) este codul de scanare al tastaturii, generat de componentele hardware. (Dac ai scris programe n limbaj de asamblare, acest cod este identic cu cel transmis n registrul AH, n timpul ntreruperii apelului BIOS 16H.) n general, aplicaiile Windows ignor acest cod, deoarece exist metode mai bune de decodificare a informaiilor de la tastatur. Indicatorul flag pentru taste extinse 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 funcionale 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 pentru tasta Num Lock. n general, programele Windows ignor acest indicator. Codul de context Codul de context (Context Code) are valoarea 1 dac este apsat 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 excepii: O fereastr activ redus la o pictogram nu deine cursorul de intrare. Toate acionrile de taste genereaz mesaje WM_SYSKEYUP i WM_SYSKEYDOWN. Dac tasta Alt nu este apsat, bitul pentru codul de context are valoarea 0. (Windows folosete aceste mesaje astfel nct fereastra activ redus la o pictogram s nu prelucreze mesajele de la tastatur.) n cazul folosirii unui driver de tastatur pentru alte limbi dect limba englez, unele caractere sunt generate prin combinarea tastelor Shift, Ctrl sau Alt cu alte taste. n aceast situaie, bitul pentru codul de context din variabila IParam care nsoete mesajele WM_KEYUP i WM_KEYDOWN are valoarea 1, dar mesajele nu reprezint acionri de taste de sistem. Starea anterioar a tastei Starea anterioar a tastei (Previous Key State) are valoarea 0 dac tasta nu a fost anterior apsat, i valoarea 1 dac tasta a fost apsat. 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 tranziie Starea de tranziie (Transition State) are valoarea 0 dac tasta este apsat i valoarea 1 dac tasta este eliberat. Acest bit are valoarea 1 pentru mesajele WM_KEYUP si WM_SYSKEYUP si valoarea 0 pentru mesajele WM_KEYDOWN i WM_SYSKEYDOWN. Coduri virtuale de taste Dei unele informaii din parametrul lParam pot fi utile pentru prelucrarea mesajelor WM_KEYUP, WM_SYSKEYUP, WM_KEYDOWN i WM_SYSKEYDOWN, parametrul wParam este mult mai important. Acest parametru conine codul virtual care identific tasta apsat sau eliberat. Dezvoltatorii sistemului de operare Windows au ncercat s defineasc tastele virtuale ntr-o manier independent de dispozitiv. Din acest motiv, unele coduri virtuale de taste nu pot fi generate pe calculatoarele IBM PC i compatibile cu acestea, dar pot fi ntlnite la tastaturile aparinnd altor productori. Codurile virtuale pe care le vei folosi cel mai des au nume definite n fiierele antet din Windows. Tabelul de mai jos prezint aceste nume, mpreun cu codurile numerice i tastele de pe tastatura IBM care corespund tastelor virtuale. Dei toate tastele genereaz mesaje de acionare (keystroke messages), tabelul nu include tastele pentru simboluri (cum ar fi / i ?). Acestor taste le corespund coduri virtuale de la 128 n sus i de cele mai multe ori sunt definite diferit pentru tastaturile internaionale. Putei s determinai aceste coduri virtuale folosind programul KEYLOOK, prezentat n acest capitol, dar n general nu este necesar s prelucrai mesajele de acionare ale acestor taste. Coduri virtuale de taste
Zecimal Hexa Identificator WINDOWS.H Necesar Tastatur IBM

1 2 3 4 8 9 12 13 16 17 18

01 02 03 04 08 09 0C 0D 10 11 12

VK_BUTTON VK_RBUTTON VK_CANCEL VK_MBUTTON VK_BACK VK_TAB VK_CLEAR VK_RETURN VK_SHIFT VK_CONTROL VK_MENU

Ctrl-Break Backspace Tab Tasta numeric 5 cu tasta Num Lock inactiv Enter Shift Ctrl Alt (continuare)

Zecimal

Hexa

Identificator WINDOWS.H

Necesar

Tastatura IBM

19 13 VK_PAUSE Pause 20 14 VK_CAPITAL Caps Lock 27 1B VK_ESCAPE Esc 32 20 VK_SPACE Bara de spaiu 33 21 VK_PRIOR Page Up 34 22 VK_NEXT Page Down 35 23 VK_END End 36 24 VK_HOME Home 37 25 VK_LEFT Sgeat stnga 38 26 VK_UP Sgeat n sus 39 27 VK_RIGHT Sgeat n dreapta 40 28 VK_DOWN Sgeat n jos 41 29 VK_SELECT 42 2A VK_PRINT 43 2B VK_EXECUTE 44 2C VK_SNAPSHOT Print Screen 45 2D VK_INSERT Insert 46 2E VK_DELETE Delete 47 2F VK_HELP 48-57 30-39 De la 0 la 9 pe blocul principal de taste 65-90 41-5A De la A la Z 96 60 VK_NUMPAD0 Tasta numeric 0 cu tasta Num Lock activ 97 61 VK_NUMPAD1 Tasta numeric 1 cu tasta Num Lock activ 98 62 VK_NUMPAD2 Tasta numeric 2 cu tasta Num Lock activ 99 63 VK_NUMPAD3 Tasta numeric 3 cu tasta Num Lock activ 100 64 VK_NUMPAD4 Tasta numeric 4 cu tasta Num Lock activ 101 65 VK_NUMPAD5 Tasta numeric 5 cu tasta Num Lock activ 102 66 VK_NUMPAD6 Tasta numeric 6 cu tasta Num Lock activ 103 67 VK_NUMPAD7 Tasta numeric 7 cu tasta Num Lock activ 104 68 VK_NUMPAD8 Tasta numeric 8 cu tasta Num Lock activ 105 69 VK_NUMPAD9 Tasta numeric 9 cu tasta Num Lock activ 106 6A VK_MULTIPLY Tasta numeric * 107 6B VK_ADD Tasta numeric + 108 6C VK_SEPARATOR 109 6D VK_SUBTRACT Tasta numeric 110 6E VK_DECIMAL Tasta numeric . 111 6F VK_DIVIDE Tasta numeric / 112 70 VK_F1 Tasta funcional F1 113 71 VK_F2 Tasta funcional F2 114 72 VK_F3 Tasta funcional F3 115 73 VK_P4 Tasta funcional F4 116 74 VK_F5 Tasta funcional F5 117 75 VK_F6 Tasta funcional F6 118 76 VK_F7 Tasta funcionat F7 119 77 VK_F8 Tasta funcional F8 120 78 VK_F9 Tasta funcional F9 121 79 VK_F10 Tasta funcional F10 122 7A VK_F11 Tasta funcional F11 (tastatura extins) 123 7B VK_F12 . Tasta funcional F12 (tastatura extins) 124 7C VK_F13 125 7D VK_F14 126 7E VK_F15 127 7F VK_F16 144 90 VK_NUMLOCK Num Lock 145 91 VK_SCROLL Scroll Lock Semnul de validare din coloana Necesar" indic faptul c tasta respectiv este obligatorie pentru orice implementare Windows. De asemenea, sistemul de operare cere ca tastatura i driverul de tastatur s permit combinarea tastelor Ctrl, Shift sau a ambelor cu orice liter, orice tast de deplasare i orice tast funcional.

VK_LBUTTON, VK_MBUTTON i VK_RBUTTON sunt codurile virtuale corespunztoare buloanelor din stnga, din mijloc i din dreapta ale mouse-ului. Cu toate acestea, nu vei primi niciodat mesaje pentru care parametrul wParam s conin aceste valori. Mouse-ul genereaz mesaje proprii, aa cum vom vedea n capitolul urmtor. Starea tastelor de modificare Parametrii lParam i wParam care nsoesc mesajele WM_KEYUP, WM_SYSKEYUP, WM_KEYDOWN i WM_SYSKEYDOWN nu spun nimic programului despre starea tastelor de modificare. Putei s obinei starea oricrei taste virtuale folosind funcia GetKeyState. Aceast funcie este folosit, de obicei, pentru obinerea strii 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 numr n care primul bit are valoarea 1) dac tasta Shift este apsat. Valoarea returnat de apelul:
GetKeyState (VK_CAPITAL) ;

are un 1 n bitul cel mai puin semnificativ dac tasta Caps Lock este activ. De asemenea, putei s obinei starea butoanelor mouse-ului folosind codurile virtuale VK_LBUTTON, VK_MBUTTON i VK_RBUTTON. Totui, majoritatea programelor pentru Windows care trebuie s monitorizeze combinaiile ntre taste i butoanele mouse-ului utilizeaz calea invers - verificnd starea tastelor atunci cnd primesc un mesaj de la mouse. De fapt, starea tastelor de modificare este inclus n mesajele primite de la mouse (aa cum vei vedea n capitolul urmtor). Avei grij cum folosii funcia GetKeyState. Aceasta nu verific starea tastaturii n timp real. GetKeyState returneaz starea tastaturii pn n momentul mesajului curent. Funcia GetKeyState nu v permite s obinei informaii despre starea tastaturii independent de mesajele obinuite. De exemplu, s presupunem c vrei s ntrerupei prelucrarea n procedura de fereastr pn cnd utilizatorul apas tasta
while (GetKeyState (VK_F1) >= 0) ; // Greit!

Nu facei acest lucru! Programul trebuie s preia mesajul de la tastatur din coada de ateptare, nainte ca funcia GetKeyState s poat obine starea tastelor. De fapt, aceast sincronizare este n folosul dumneavoastr, deoarece putei s obinei starea corect a tastelor de modificare n timpul generrii unui anumit mesaj de la tastatur, chiar dac prelucrai mesajul (i apelai funcia GetKeyState) dup ce tasta de modificare respectiv a fost eliberat. Dac ntr-adevr avei nevoie de starea curent a unei taste, putei s folosii funcia GetAsyncKeyState. Utilizarea mesajelor de acionare a tastelor Ideea unui program care s obin informaii despre toate aciunile exercitate asupra tastelor este drgu. Cu toate acestea, majoritatea programelor Windows ignor cea mai mare parte a acestor mesaje. Mesajele WM_SYSKEYUP i WM_SYSKEYDOWN sunt folosite pentru funciile sistemului, aa c nu este nevoie s le interceptai. De asemenea, dac prelucrai mesajele WM_KEYDOWN, de obicei putei s ignorai mesajele WM_KEYUP. n general, programele pentru Windows folosesc mesajele WM_KEYDOWN pentru tastele care nu genereaz caractere. Dei s-ar putea s v vin ideea s folosii mesajele pentru acionri de taste n combinaie cu informaiile despre tastele de modificare (furnizate de funcia GetKeyState) ca s transformai mesajele pentru acionri de taste n mesaje pentru caractere, nu facei acest lucru. Vei avea probleme datorit diferenelor ntre tastaturile internaionale. De exemplu, dac primii un mesaj WM_KEYDOWN pentru care parametrul wParam are valoarea 33H, tii c utilizatorul a apsat tasta 3. Pn aici totul este bine. Dac apelai funcia GetKeyState vei afla c tasta Shift este apsat i s-ar putea s presupunei c utilizatorul a introdus un caracter diez (#), lucru care poate s nu fie adevrat. Un utilizator britanic ar fi introdus caracterul . Mesajele WM_KEYDOWN sunt utile pentru tastele de deplasare, tastele funcionale i tastele speciale, precum Insert, Delete. Uneori tastele Insert, Delete si tastele funcionale sunt folosite ca acceleratori pentru comenzi de meniu. Deoarece Windows transform acceleratorii n comenzi de meniu, nu este nevoie s prelucrai n program mesajele pentru acionarea tastelor de accelerare. Unele programe nonWindows folosesc foarte des tastele funcionale n combinaie cu tastele Alt, Ctrl i Shift. Putei face acest lucru i n programele pentru Windows, dar nu este recomandat. Dac vrei s folosii tastele funcionale, 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 reuit s eliminm aproape totul, cu excepia unui singur lucru: de cele mai multe ori vei prelucra mesajele WM_KEYDOWN numai pentru tastele de deplasare a cursorului. Atunci cnd prelucrai mesajele trimise de tastele de deplasare putei s verificai i starea tastelor Shift i Ctrl, folosind funcia GetKeyState. Unele funcii Windows folosesc de multe ori tastele de deplasare n combinaie cu tasta Shift pentru extinderea unei selecii - de exemplu ntrun procesor de texte. Tasta Ctrl este folosit deseori pentru a schimba semnificaia unei taste de deplasare. De exemplu, tasta Ctrl n combinaie cu sgeata spre dreapta mut cursorul cu un cuvnt Ia dreapta. Una dintre cele mai bune metode de a v hotr cum s folosii tastatura este s studiai modul de utilizare a acesteia n programele Windows existente. Dac nu v place cum este folosit, putei s facei ceva diferit. Dar nu

uitai c orice noutate nseamn creterea perioadei de care are nevoie un utilizator pentru a se obinui cu programul dumneavoastr. MBUNTIREA PROGRAMULUI SYSMETS: ADUGAREA INTERFEEI CU TASTATURA Atunci cnd ai scris cele trei versiuni ale programului SYSMETS prezentate n Capitolul 3 nu tiai nc nimic despre tastatur. Puteai s derulai textul numai folosind mouse-ul pe barele de derulare. Acum tim s prelucrm mesajele de la tastatur, aa c este timpul s adugm programului SYSMETS o interfa cu tastatur. Aceasta este, evident, sarcina tastelor de deplasare. Cea mai mare parte a tastelor de deplasare (Home, End, Page Up, Page Down, sgeile n sus i n jos) vor fi folosite pentru derularea pe vertical. Sgeile spre dreapta i spre stnga vor fi folosite pentru derularea pe orizontal, facilitate care n acest program este mai puin important. Adugarea logicii de tratare a mesajului WM_KEYDOWN O metod evident de creare a unei interfee este adugarea codului necesar pentru tratarea mesajului WM_KEYDOWN, dublnd astfel logica de tratare a mesajelor WM_VSCROLL i WM_HSCROLL:
case WM_KEYDOWN : iVscrollInc = iHscrollInc = 0 ; switch (wParam) { case VK_HOME: // la fel ca HM_VSCROLL, SB_TOP iVscrollInc = -iVscrollPos ; break ; case VK_END : // la fel ca HM_VSCROLL, SB_BOTTOM: iVscrollInc = iVscrollMax - iVscrollPos ; break ; case VK_UP : // la fel ca HM_VSCROLL, SB_LINEUP : iVscrollInc = -1 ; break ; case VK_DOWN : // la fel ca WM_VSCROLL, SB_LINEDOWN iVscrollInc = 1 ; break ; case VK_PRIOR : // la fel ca WM_VSCROLL, SB_PAGEUP iVscrollInc = min (-1, -cyClient / cyChar) ; break ; case VK_NEXT : // la fel ca WH_VSCROLL, SB_PAGEDOWN : iVscrollInc = max (1, cyClient / cyChar) ; break ; case VK_LEFT : // la fel ca WM_HSCROLL, SB_PAGEUP iHscrollInc = -8 ; break ; case VK_RIGHT : // la fel ca WM_HSCROLL, SB_PAGEDOWN iHscrollInc = 8 ; break ; default : break ; } if (iVscrollInc = max (-iVscrollPos, min(iVscrollInc, iVscrollMax-iVscrollPos))) { iVscrollPos += iVscrollInc ; ScrollWindow (hwnd, 0, -cyChar * iVscrollInc, NULL, NULL); SetScrollPos (hwnd, SB_VERT, iVscrollPos, TRUE); UpdateWindow (hwnd) ; } if (iHscrollInc = max (-iHscrollPos,min(iHscrollInc, iHscrollMax - iHscrollPos))) { iHscrollPos += iHscrollInc; ScrollWindow (hwnd, -cxChar*iHscrollInc, 0, NULL, NULL) ; SetScrollPos (hwnd, SB_ HORZ, iHscrollPos, TRUE) ; } return 0 ;

V displace acest cod la fel de mult ca i mie? Dublarea codului pentru tratarea mesajelor de la barele de derulare este o soluie de-a dreptul proast, deoarece dac vom dori vreodat s modificm logica de funcionare a barelor de derulare va trebui s facem aceleai modificri i n codul de tratare a mesajului WM_KEYDOWN. Trebuie s existe o soluie mai elegant. i chiar exist. Transmiterea mesajelor Nu ar fi mai bine s transformm mesajele WM_KEYDOWN n mesaje echivalente WM_VSCROLL i WM_HSCROLL i s facem cumva funcia WndProc s cread c a primit mesajele WM_VSCROLL i

WM_HSCROLL, poate chiar transmind procedurii de fereastr aceste mesaje contrafcute? Windows v permite s facei acest lucru. Funcia necesar este SendMessage i primete aceiai parametri ca i procedura de fereastr:
SendMessage (hwnd, message, wParam, lParam) ;

Atunci cnd apelai funcia SendMessage, Windows apeleaz procedura ferestrei identificat de parametrul hwnd i i transmite cei patru parametri. Dup ce procedura de fereastr ncheie prelucrarea mesajului, Windows reia execuia de la urmtoarea linie dup apelul funciei SendMessage. Procedura de fereastr creia i trimitei mesajul poate fi aceeai procedur de fereastr, o alt procedur de fereastr din acelai program sau chiar o procedur de fereastr dintr-o alt aplicaie. Iat cum putem folosi funcia SendMessage pentru prelucrarea mesajelor WM_KEYDOWN n programul SYSMETS:
case WH_KEYDOWN : switch (wParam) case VK_HOME : SendMessage (hwnd, WM_VSCROLL, SB_TOP, break; 0L);

case VK_END: SendMessage (hwnd, WM_VSCROLL, SB_BOTTOM, 0L); break ; case VK_PRIOR : SendMessage (hwnd, WH_VSCROLL, SB_PAGEUP, 0L); break ;

[alte linii de program] OK, ai prins ideea. Scopul nostru era s adugm o interfa cu tastatura pentru barele de derulare i exact acest lucru l-am fcut. Am permis folosirea tastelor de deplasare pentru a dubla logica barelor de derulare trimind procedurii de fereastr mesajele specifice barelor de derulare. Acum vedei de ce am inclus n programul SYSMETS3 din Capitolul 3 secvena de tratare a codurilor SB_TOP i SB_BOTTOM din mesajele WM_VSCROLL. n momentul respectiv acestea nu erau folosite, dar acum permit tratarea tastelor Home i End. Programul SYSMETS, prezentat n Figura 52, include cele trei modificri discutate. Pentru compilarea programului avei nevoie i de fiierul SYSMETS.H, prezentat n Figura 3-4 din Capitolul 3.
#----------------------# SYSMETS.MAK make file #----------------------sysmets.exe : sysmets.obj $(LINKER) $(GUIFLAGS) -OUT:sysmets.exe sysmets.obj $(GUILIBS) sysmets.obj : sysmets.c sysmets.h $(CC) $(CFLAGS) sysmets.c /*----------------------------------------------------SYSMETS.C -- System Metrics Display Program (Final) (c) Charles Petzold, 1996 -----------------------------------------------------*/ #include <windows.h> #include <string.h> #include "sysmets.h" LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM) ; int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow) { static char szAppName[] = "SysMets" ; HWND hwnd ; MSG msg ; WNDCLASSEX wndclass ; wndclass.cbSize = sizeof (wndclass) ; wndclass.style = CS_HREDRAW | CS_VREDRAW ; wndclass.lpfnWndProc = WndProc ; wndclass.cbClsExtra = 0 ; wndclass.cbWndExtra = 0 ; wndclass.hInstance = hInstance ; wndclass.hIcon = LoadIcon (NULL, IDI_APPLICATION) ; wndclass.hCursor = LoadCursor (NULL, IDC_ARROW) ;

wndclass.hbrBackground = (HBRUSH) GetStockObject (WHITE_BRUSH) ; wndclass.lpszMenuName = NULL ; wndclass.lpszClassName = szAppName ; wndclass.hIconSm = LoadIcon (NULL, IDI_APPLICATION) ; RegisterClassEx (&wndclass) ; hwnd = CreateWindow (szAppName, "System Metrics", WS_OVERLAPPEDWINDOW | WS_VSCROLL | WS_HSCROLL, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hInstance, NULL) ; ShowWindow (hwnd, iCmdShow) ; UpdateWindow (hwnd) ; while (GetMessage (&msg, NULL, 0, 0)) { TranslateMessage (&msg) ; DispatchMessage (&msg) ; } return msg.wParam ; } LRESULT CALLBACK WndProc (HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam) { static int cxChar, cxCaps, cyChar, cxClient, cyClient, iMaxWidth, iVscrollPos, iVscrollMax, iHscrollPos, iHscrollMax ; char szBuffer[10] ; HDC hdc ; int i, x, y, iPaintBeg, iPaintEnd, iVscrollInc, iHscrollInc ; PAINTSTRUCT ps ; TEXTMETRIC tm ; switch (iMsg) { case WM_CREATE : hdc = GetDC (hwnd) ; GetTextMetrics (hdc, &tm) ; cxChar = tm.tmAveCharWidth ; cxCaps = (tm.tmPitchAndFamily & 1 ? 3 : 2) * cxChar / 2 ; cyChar = tm.tmHeight + tm.tmExternalLeading ; ReleaseDC (hwnd, hdc) ; iMaxWidth = 40 * cxChar + 22 * cxCaps ; return 0 ; case WM_SIZE : cxClient = LOWORD (lParam) ; cyClient = HIWORD (lParam) ; iVscrollMax = max (0, NUMLINES + 2 - cyClient / cyChar) ; iVscrollPos = min (iVscrollPos, iVscrollMax) ; SetScrollRange (hwnd, SB_VERT, 0, iVscrollMax, FALSE) ; SetScrollPos (hwnd, SB_VERT, iVscrollPos, TRUE) ; iHscrollMax = max (0, 2 + (iMaxWidth - cxClient) / cxChar) ; iHscrollPos = min (iHscrollPos, iHscrollMax) ; SetScrollRange (hwnd, SB_HORZ, 0, iHscrollMax, FALSE) ; SetScrollPos (hwnd, SB_HORZ, iHscrollPos, TRUE) ; return 0 ; case WM_VSCROLL : switch (LOWORD (wParam)) { case SB_TOP : iVscrollInc = -iVscrollPos ; break ; case SB_BOTTOM : iVscrollInc = iVscrollMax - iVscrollPos ;

10

break ; case SB_LINEUP : iVscrollInc = -1 ; break ; case SB_LINEDOWN : iVscrollInc = 1 ; break ; case SB_PAGEUP : iVscrollInc = min (-1, -cyClient / cyChar) ; break ; case SB_PAGEDOWN : iVscrollInc = max (1, cyClient / cyChar) ; break ; case SB_THUMBTRACK : iVscrollInc = HIWORD (wParam) - iVscrollPos ; break ; default : iVscrollInc = 0 ; } iVscrollInc = max (-iVscrollPos, min (iVscrollInc, iVscrollMax - iVscrollPos)) ; if (iVscrollInc != 0) { iVscrollPos += iVscrollInc ; ScrollWindow (hwnd, 0, -cyChar * iVscrollInc, NULL, NULL) ; SetScrollPos (hwnd, SB_VERT, iVscrollPos, TRUE) ; UpdateWindow (hwnd) ; } return 0 ; case WM_HSCROLL : switch (LOWORD (wParam)) { case SB_LINEUP : iHscrollInc = -1 ; break ; case SB_LINEDOWN : iHscrollInc = 1 ; break ; case SB_PAGEUP : iHscrollInc = -8 ; break ; case SB_PAGEDOWN : iHscrollInc = 8 ; break ; case SB_THUMBPOSITION : iHscrollInc = HIWORD (wParam) - iHscrollPos ; break ; default : iHscrollInc = 0 ; } iHscrollInc = max (-iHscrollPos, min (iHscrollInc, iHscrollMax - iHscrollPos)) ; if (iHscrollInc != 0) { iHscrollPos += iHscrollInc ; ScrollWindow (hwnd, -cxChar * iHscrollInc, 0, NULL, NULL) ; SetScrollPos (hwnd, SB_HORZ, iHscrollPos, TRUE) ; } return 0 ; case WM_KEYDOWN :

11

switch (wParam) { case VK_HOME : SendMessage (hwnd, WM_VSCROLL, SB_TOP, 0L) ; break ; case VK_END : SendMessage (hwnd, WM_VSCROLL, SB_BOTTOM, 0L) ; break ; case VK_PRIOR : SendMessage (hwnd, WM_VSCROLL, SB_PAGEUP, 0L) ; break ; case VK_NEXT : SendMessage (hwnd, WM_VSCROLL, SB_PAGEDOWN, 0L) ; break ; case VK_UP : SendMessage (hwnd, WM_VSCROLL, SB_LINEUP, 0L) ; break ; case VK_DOWN : SendMessage (hwnd, WM_VSCROLL, SB_LINEDOWN, 0L) ; break ; case VK_LEFT : SendMessage (hwnd, WM_HSCROLL, SB_PAGEUP, 0L) ; break ; case VK_RIGHT : SendMessage (hwnd, WM_HSCROLL, SB_PAGEDOWN, 0L) ; break ; } return 0 ; case WM_PAINT : hdc = BeginPaint (hwnd, &ps) ; iPaintBeg = max (0, iVscrollPos + ps.rcPaint.top / cyChar - 1) ; iPaintEnd = min (NUMLINES, iVscrollPos + ps.rcPaint.bottom / cyChar) ; for (i = iPaintBeg ; i < iPaintEnd ; i++) { x = cxChar * (1 - iHscrollPos) ; y = cyChar * (1 - iVscrollPos + i) ; TextOut (hdc, x, y, sysmetrics[i].szLabel, strlen (sysmetrics[i].szLabel)) ; TextOut (hdc, x + 22 * cxCaps, y, sysmetrics[i].szDesc, strlen (sysmetrics[i].szDesc)) ; SetTextAlign (hdc, TA_RIGHT | TA_TOP) ; TextOut (hdc, x + 22 * cxCaps + 40 * cxChar, y, szBuffer, wsprintf (szBuffer, "%5d", GetSystemMetrics (sysmetrics[i].iIndex))) ; SetTextAlign (hdc, TA_LEFT | TA_TOP) ; } EndPaint (hwnd, &ps) ; return 0 ; case WM_DESTROY : PostQuitMessage (0) ; return 0 ; } return DefWindowProc (hwnd, iMsg, wParam, lParam) ;

12

Figura 5-2. Programul SYSMETS (final). MESAJE CARACTER Am discutat mai devreme despre ideea transformrii mesajelor generate de aciona rea tastelor n mesaje caracter innd cont de starea tastelor de modificare i v-am avertizat c acest lucru nu este suficient: trebuie s inei seama i de configuraia diferit a tastaturii de la o ar la alta. Din acest motiv, nu trebuie s ncercai s transformai dumneavoastr mesajele generate de acionarea tastelor n mesaje caracter. Windows o poate face n locul dumneavoastr. Ai mai vzut aceast secven de cod:
while (GetMessage (&msg, NULL, 0, 0)) { TranslateMessage (&msg); DispatchMessage (&msg); }

Acesta este un ciclu tipic de tratare a mesajelor din funcia WinMain. Funcia GetMessage preia urmtorul mesaj din coada de ateptare i completeaz cmpurile structurii msg. Funcia DispatchMessage transmite mesajul procedurii de fereastr corespunztoare. ntre cele dou funcii este apelat funcia TranslateMessage, care transform mesajele generate de acionarea tastelor n mesaje caracter. Dac mesajul este WM_KEYDOWN sau WM_SYSKEYDOWN i dac tasta apsat, n funcie de starea tastelor de modificare, genereaz un caracter, atunci funcia TranslateMessage insereaz un mesaj caracter n coada de ateptare. Acesta va fi urmtorul mesaj pe care l va prelua funcia GetMessage dup mesajul generat de acionarea tastei. Exist patru mesaje caracter: Caractere WM_CHAR WM_SYSCHAR Caractere moarte" WM_DEADCHAR WM_SYSDEADCHAR

Caractere non-sistem Caractere sistem

Mesajele WM_CHAR i WM_DEADCHAR sunt obinute din mesaje WM_KEYDOWN. Mesajele WM_SYSCHAR i WM_SYSDEADCHAR sunt obinute 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 acelai coninut ca i parametrul lParam al mesajului generat de acionarea tastei din care a fost obinut mesajul caracter. Parametrul wParam conine codul ASCII al caracterului (da, chiar btrnul ASCII!). Mesajele caracter sunt transmise procedurii de fereastr ntre mesajele generate de acionarea tastelor. De exemplu, dac tasta Caps Lock nu este activ i apsai i eliberai tasta A, procedura de fereastr primete urmtoarele trei mesaje: Mesaj WM_KEYDOWN WM_CHAR WM_KEYUP Tast sau cod Tasta virtual A Codul ASCII al caracterului a Tasta virtual A

Dac introducei un caracter A apsnd tasta Shift, apoi tasta A, elibernd tasta A i apoi elibernd tasta Shift, procedura de fereastr primete cinci mesaje: Mesaj Tast sau cod WM_KEYDOWN Tasta virtual VK_SHIFT WM_KEYDOWN Tasta virtual A WM_CHAR WM_KEYUP Codul ASCII al caracterului a WM_KEYUP Tasta virtual A Tasta virtual VK_SHIFT Tasta Shift nu genereaz un mesaj caracter. Dac inei tasta A apsat pn cnd intr n aciune autorepetarea, vei primi un mesaj caracter pentru fiecare caracter WM_KEYDOWN: Mesaj Tast sau cod WM_KEYDOWN WM_CHAR WM_KEYDOWN WM_CHAR WM_KEYDOWN WM_CHAR WM_KEYDOWN Tasta virtual A Codul ASCII al caracterului a Tasta virtual A Codul ASCII al caracterului a Tasta virtual A Codul ASCII al caracterului a Tasta virtual A

13

WM_CHAR WM_KEYUP

Codul ASCII al caracterului a Tasta virtual A

Dac unul dintre mesajele WM_KEYDOWN are contorul de repetare mai mare dect 1, mesajul WM_CHAR va avea acelai contor de repetare. Tasta Ctrl n combinaie cu o liter genereaz codurile de control ASCII de la 01H (Ctrl+A) la 1AH (Ctrl+Z). Putei s folosii i alte taste pentru generarea acestor coduri de control. Tabelul urmtor prezint valorile parametrului wParam dintr-un mesaj WM_CHAR pentru tastele care genereaz coduri de control: Tast Cod ASCII Dublat de Backspace Tab Ctrl+Enter Enter Esc 08H 09H 0AH 0DH 1BH Ctrl+H Ctrl+I Ctrl+J Ctrl+M Ctrl+[

Programele Windows folosesc uneori combinaiile tastei Ctrl cu litere ca acceleratori pentru meniuri, caz n care literele respective nu sunt transformate n mesaje caracter. Mesajul WM_CHAR Atunci cnd trebuie s prelucreze caracterele introduse de la tastatur (de exemplu, ntr-un procesor de texte sau ntr-un program de comunicaii) programul prelucreaz mesajele WM_CHAR. Probabil dorii s prelucrati ntr-un mod mai special tastele Backspace, Tab i Enter (eventual si tasta de salt la linie nou) dar toate celelalte caractere sunt tratate la fel:
case WH_CHAR : switch (wParam) { case '\b' :

// backspace [alte linii de program] break ; case '\f' : // tab [alte linii de program] break ; case '\n' : // salt la linie nou [alte linii de program] break ; case '\r' : // retur de car [alte linii de program] break ; default : // cod de caractere [alte linii de program] break ; }

return 0 ;

Acest fragment de program este asemntor cu secvenele de cod pentru tratarea caracterelor dintr-un program MS-DOS obinuit. Mesaje pentru caractere moarte" De obicei, programele pot s ignore mesajele WM_DEADCHAR si WM_SYSDEADCHAR. Pe unele tastaturi, n afar de tastatura de tip american, o serie de taste sunt folosite pentru adugarea semnelor diacritice la o liter. Acestea se numesc taste moarte" (dead keys") deoarece nu pot crea singure caractere. De exemplu, pe o tastatura german, n locul tastei +/= de pe tastatura american se afl o tast moart pentru accentul ascuit ('') dac nu este apsat tasta Shift, si pentru accentul grav (`) dac este apsat tasta Shift. Atunci cnd utilizatorul apas aceast tast moart, procedura de fereastr pri mete un mesaj WM_DEADCHAR pentru care parametrul wParam conine codul ASCII al semnului diacritic. Dac utilizatorul apas apoi o liter (de exemplu, tasta A), procedura de fereastr primete un mesaj WM_CHAR pentru care parametrul wParam conine codul ASCII al literei a" cu semnul diacritic respectiv. Ca urmare, programul nu trebuie s prelucreze mesajul WM_DEADCHAR, deoarece mesajul WM_CHAR urmtor i furnizeaz toate informaiile necesare. Codul Windows se ocup chiar i de tratarea erorilor: dac tasta moart este urmat de o liter care nu poate avea semnul diacritic respectiv (cum ar fi litera s"), procedura de fereastr primete dou mesaje WM_CHAR -

14

pentru primul parametrul wParam conine codul ASCII al semnului diacritic, iar pentru al doilea parametrul wParam conine codul ASCII al literei s". Examinarea mesagelor de la tastatur Dac dorii s vedei cum trimite sistemul de operare mesajele de la tastatur ctre o aplicaie, programul KEYLOOK (prezentat n figura 5.3) v poate ajuta. Acest program afieaz n zona client toate informaiile pe care Windows le trimite procedurii de fereastr pentru opt mesaje diferite de la tastatur.
#----------------------# KEYLOOK.MAK make file #----------------------keylook.exe : keylook.obj $(LINKER) $(GUIFLAGS) -OUT:keylook.exe keylook.obj $(GUILIBS) keylook.obj : keylook.c $(CC) $(CFLAGS) keylook.c /*------------------------------------------------------KEYLOOK.C -- Displays Keyboard and Character Messages (c) Charles Petzold, 1996 -------------------------------------------------------*/ #include <windows.h> #include <stdio.h> LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM) ; RECT rect ; int cxChar, cyChar ; int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow) { static char szAppName[] = "KeyLook" ; HWND hwnd ; MSG msg ; WNDCLASSEX wndclass ; wndclass.cbSize = sizeof (wndclass) ; wndclass.style = CS_HREDRAW | CS_VREDRAW ; wndclass.lpfnWndProc = WndProc ; wndclass.cbClsExtra = 0 ; wndclass.cbWndExtra = 0 ; wndclass.hInstance = hInstance ; wndclass.hIcon = LoadIcon (NULL, IDI_APPLICATION) ; wndclass.hCursor = LoadCursor (NULL, IDC_ARROW) ; wndclass.hbrBackground = (HBRUSH) GetStockObject (WHITE_BRUSH) ; wndclass.lpszMenuName = NULL ; wndclass.lpszClassName = szAppName ; wndclass.hIconSm = LoadIcon (NULL, IDI_APPLICATION) ; RegisterClassEx (&wndclass) ; hwnd = CreateWindow (szAppName, "Keyboard Message Looker", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hInstance, NULL) ; ShowWindow (hwnd, iCmdShow) ; UpdateWindow (hwnd) ; while (GetMessage (&msg, NULL, 0, 0)) { TranslateMessage (&msg) ; DispatchMessage (&msg) ; } return msg.wParam ; } void ShowKey (HWND hwnd, int iType, char *szMessage, WPARAM wParam, LPARAM lParam) { static char *szFormat[2] = { "%-14s %3d %c %6u %4d %3s %3s %4s %4s",

15

char HDC

"%-14s szBuffer[80] ; hdc ;

%3d %c %6u %4d %3s %3s %4s %4s" } ;

ScrollWindow (hwnd, 0, -cyChar, &rect, &rect) ; hdc = GetDC (hwnd) ; SelectObject (hdc, GetStockObject (SYSTEM_FIXED_FONT)) ; TextOut (hdc, cxChar, rect.bottom - cyChar, szBuffer, wsprintf (szBuffer, szFormat [iType], szMessage, wParam, (BYTE) (iType ? wParam : ' '), LOWORD (lParam), HIWORD (lParam) & 0xFF, (PSTR) (0x01000000 & lParam ? "Yes" : "No"), (PSTR) (0x20000000 & lParam ? "Yes" : "No"), (PSTR) (0x40000000 & lParam ? "Down" : "Up"), (PSTR) (0x80000000 & lParam ? "Up" : "Down"))) ; ReleaseDC (hwnd, hdc) ; ValidateRect (hwnd, NULL) ; } LRESULT CALLBACK WndProc (HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam) { static char szTop[] = "Message Key Char Repeat Scan Ext ALT Prev Tran"; static char szUnd[] = "_______ ___ ____ ______ ____ ___ ___ ____ ____"; HDC hdc ; PAINTSTRUCT ps ; TEXTMETRIC tm ; switch (iMsg) { case WM_CREATE : hdc = GetDC (hwnd) ; SelectObject (hdc, GetStockObject (SYSTEM_FIXED_FONT)) ; GetTextMetrics (hdc, &tm) ; cxChar = tm.tmAveCharWidth ; cyChar = tm.tmHeight ; ReleaseDC (hwnd, hdc) ; rect.top = 3 * cyChar / 2 ; return 0 ; case WM_SIZE : rect.right = LOWORD (lParam) ; rect.bottom = HIWORD (lParam) ; UpdateWindow (hwnd) ; return 0 ; case WM_PAINT : InvalidateRect (hwnd, NULL, TRUE) ; hdc = BeginPaint (hwnd, &ps) ; SelectObject (hdc, GetStockObject (SYSTEM_FIXED_FONT)) ; SetBkMode (hdc, TRANSPARENT) ; TextOut (hdc, cxChar, cyChar / 2, szTop, (sizeof szTop) - 1) ; TextOut (hdc, cxChar, cyChar / 2, szUnd, (sizeof szUnd) - 1) ; EndPaint (hwnd, &ps) ; return 0 ; case WM_KEYDOWN : ShowKey (hwnd, 0, "WM_KEYDOWN", wParam, lParam) ; return 0 ; case WM_KEYUP : ShowKey (hwnd, 0, "WM_KEYUP", wParam, lParam) ; return 0 ;

16

case WM_CHAR : ShowKey (hwnd, 1, "WM_CHAR", wParam, lParam) ; return 0 ; case WM_DEADCHAR : ShowKey (hwnd, 1, "WM_DEADCHAR", wParam, lParam) ; return 0 ; case WM_SYSKEYDOWN : ShowKey (hwnd, 0, "WM_SYSKEYDOWN", wParam, lParam) ; break ; // ie, call DefWindowProc case WM_SYSKEYUP : ShowKey (hwnd, 0, "WM_SYSKEYUP", wParam, lParam) ; break ; // ie, call DefWindowProc case WM_SYSCHAR : ShowKey (hwnd, 1, "WM_SYSCHAR", wParam, lParam) ; break ; // ie, call DefWindowProc case WM_SYSDEADCHAR : ShowKey (hwnd, 1, "WM_SYSDEADCHAR", wParam, lParam) ; break ; // ie, call DefWindowProc case WM_DESTROY : PostQuitMessage (0) ; return 0 ; } return DefWindowProc (hwnd, iMsg, wParam, lParam) ; }

Figura 5.3. Programul KEYLOOK Programul KEYLOOK folosete ecranul dup modelul teleimprimatorului. Atunci cnd primete un mesaj generat de acionarea unei taste, programul KEYLOOK apeleaz funcia ScrollWindow, care deruleaz n sus coninutul ntregii zone client cu nlimea unui caracter. Funcia TextOut este folosit pentru afiarea unei noi linii de informaii, ncepnd afiarea ntr-un punct aflat la o distan egal cu nlimea unui caracter, fa de marginea de jos a ecranului. Este la fel de simplu ca i n cazul unui teleimprimator. Figura 5-4 prezint fereastra afiat de programul KEYLOOK atunci cnd tastai cuvntul Windows". Prima coloan conine mesajele de Ia tastatur; a doua coloan conine codurile de taste virtuale pentru mesajele generate de acionarea fastelor; a treia coloan conine codul caracterului (i caracterul); celelalte ase coloane prezint starea celor ase cmpuri din parametrul lParam al mesajului.

Figura 5-4. Fereastra afiat de programul KEYLOOK.

n cea mai mare parte, programul KEYLOOK folosete caracteristici Windows despre care deja am discutat n programele SYSMETS prezentate pn acum, dar sunt folosite i cteva funcii noi. Remarcai, totui, c formatarea coloanelor n programul KEYLOOK ar fi fost destul de dificil dac s-ar fi folosit fontul prestabilit proporional. Codul pentru afiarea fiecrei linii ar trebui mprit n nou seciuni pentru a pstra coloanele aliniate. Pentru

17

rezolvarea unei astfel de situaii o soluie mai simpl este s folosii un font cu dimensiune fix. Aa cum am artat n capitolul anterior, pentru aceasta este nevoie de dou funcii, pe care le-am combinat ntr-o singur instruciune:
SelectObject (hdc, GetStockObject (SYSTEM_FIXED_FONT)) ;

Programul KEYLOOK apeleaz aceste funcii de fiecare dat cnd obine un context de dispozitiv. Acest lucru se ntmpl n trei locuri din program: n funcia ShowKey, n timpul prelucrrii mesajului WM_CREATE din funcia WndProc i n timpul prelucrrii mesajului WM_PAINT. Funcia GetStockObject obine o variabil handle a unui obiect grafic de stoc - care n acest caz este fontul de dimensiune fix folosit n versiunile mai vechi Windows 3.0. Funcia SelectObject selecteaz obiectul n contextul de dispozitiv. Dup acest apel, orice text va fi afiat cu fontul de dimensiune fix selectat. Putei s v ntoarcei la fontul proporional prestabilit, cu urmtoarea instruciune:
SelectObject (hdc, GetStockObject (SYSTEM_FONT)) ;

Funcia ShowKey apeleaz funcia ScrollWindow, care deruleaz liniile anterioare naintea afirii unei noi linii. n mod normal, aceast operaie ar duce la invalidarea parial a ferestrei i ar gener un mesaj WM_PAINT. Pentru a mpiedica acest lucru, nainte de terminare, funcia ShowKey apeleaz funcia ValidateRect. Programul KEYLOOK nu salveaz mesajele pe care le primete, aa c la primirea unui mesaj WM_PAINT nu poate s re-creeze fereastra. Din acest motiv, la primirea mesajului WM_PAINT programul KEYLOOK afieaz n partea superioar a ferestrei capetele celor nou coloane. naintea apelrii funciei BeginPaint, programul KEYLOOK invalideaz ntreaga fereastr. n acest fel, este tears toat suprafaa ferestrei, nu numai dreptunghiul invalid. (Faptul c programul KEYLOOK nu salveaz mesajele pe care le primete i deci la primirea unui mesaj WM_PAINT nu poate s re-creeze fereastra este un neajuns al programului. Programul TYPER, prezentat puin mai trziu, corecteaz aceast eroare.) Programul KEYLOOK afieaz n partea superioar a zonei client un antet pentru identificarea celor nou coloane. Dei este posibil crearea unui font subliniat, n acest program am ales o abordare puin diferit. Am definit dou iruri de caractere, numite szTop (care conine textul antetului) i szUnd (care conine liniuele de subliniere) i le-am afiat n aceeai poziie n partea de sus a ecranului, n timpul prelucrrii mesajului WM_PAINT. n mod normal, Windows afieaz textul n modul opac" (OPAQUE), ceea ce nseamn c terge zona de fond din spatele caracterelor afiate. Din aceast cauz, al doilea ir de caractere (szUnd) ar terge primul ir de caractere (szTop). Pentru a mpiedica acest lucru, am schimbat modul de afiare n contextul de dispozitiv din opac" n transparent":
SetBkMode (hdc, TRANSPARENT) ;

CURSORUL DE EDITARE (CARET) Atunci cnd introducei text ntr-un program, poziia n care va aprea urmtorul caracter este indicat de o liniu de subliniere sau de un mic dreptunghi, n Windows pentru acest semn se folosete termenul cursor de editare". Funcii pentru cursorul de editare Exist cinci funcii principale pentru cursorul de editare: CreateCaret - creeaz un cursor de editare asociat unei ferestre. SetCaretPos - stabilete poziia cursorului de editare n fereastr. ShowCaret - afieaz cursorul de editare. HideCaret - mascheaz cursorul de editare. DestroyCaret - distruge cursorul de editare creat. Mai exist i alte funcii pentru obinerea poziiei cursorului de editare (GetCaretPos) i pentru stabilirea i obinerea intervalelor de licrire a acestuia (SetCaretBlinkTime i GetCaretBlinkTime). Cursorul de editare este, de obicei, o linie ori un bloc orizontal de dimensiunea unui caracter, sau o linie vertical. Linia vertical este recomandat n cazul folosirii unui font proportional, cum ar fi fontul sistem prestabilit din Windows. Deoarece caracterele din fonturile proporionale nu au aceeai lime, linia sau blocul orizontal nu poate avea limea exact a unui caracter. Cursorul de editare nu poate fi creat pur i simplu n timpul prelucrrii mesajului WM_CREATE i nici distrus n timpul prelucrrii mesajului WM_DESTROY. El este ceea ce se numete o resurs de sistem". Aceasta nseamn c n sistem exist un singur cursor de editare. De fapt, atunci cnd un program trebuie s afieze un cursor de editare n fereastra proprie, el mprumut" acest semn de la sistem. Pare prea restrictiv? Ei bine, n realitate nu este. Gndii-v puin: afiarea unui cursor de editare ntr-o fereastr are sens numai dac fereastra respectiv deine cursorul de intrare (input focus). Cursorul de editare indic utilizatorului faptul c poate introduce text n program. La un moment dat, o singur fereastr poate deine cursorul de intrare, aa c existena unui singur cursor de editare este logic. Un program poate determina dac deine cursorul de intrare prin prelucrarea mesajelor WM_SETFOCUS i WM_KILLFOCUS. O procedur de fereastr recepioneaz un mesaj WM_SETFOCUS atunci cnd primete cursorul de intrare i un mesaj WM_KILLFOCUS atunci cnd pierde cursorul de intrare. Aceste mesaje sunt transmise n pereche. O procedur de fereastr primete ntotdeauna un mesaj WM_SETFOCUS nainte de a primi un

18

mesaj WM_KILLFOCUS i ntotdeauna va primi un numr egal de mesaje WM_SETFOCUS i WM_KILLFOCUS pan la distrugerea ferestrei. Principala regul de folosire a cursorului de editare este simpl. O procedur de fereastr apeleaz funcia CreateCaret n timpul prelucrrii mesajului WM_SETFOCUS i funcia DestroyCaret n timpul prelucrrii mesajului WMJKILLFOCUS. Exist i alte cteva reguli: la creare, cursorul de editare nu este afiat. Dup apelarea funciei CreateCaret, programul trebuie s apeleze funcia ShowCaret pentru a face vizibil cursorul de editare. n plus, procedura de fereastr trebuie s-l mascheze, apelnd funcia HideCaret, ori de cte ori deseneaz ceva pe ecran n timpul prelucrrii unui alt mesaj dect WM_PAINT. Dup terminarea operaiei de desenare, programul apeleaz funcia ShowCaret ca s afieze din nou cursorul de editare. Efectul funciei HideCaret este aditiv: dac apelai funcia HideCaret de mai multe ori fr s apelai funcia ShowCaret, atunci cnd dorii ca acest cursor de editare s devin din nou vizibil, trebuie s apelai funcia ShowCaret de tot attea ori de cate ori ai apelat i funcia HideCaret. Programul TYPER Programul TYPER, prezentat n Figura 5-5, combin cea mai mare parte a lucrurilor nvate n acest capitol. Putei s v gndii la programul TYPER ca la un editor de text mai rudimentar. Putei s introducei text n fereastr, s mutai cursorul de editare cu ajutorul tastelor de deplasare i s tergei coninutul ferestrei apsnd tasta Esc. Coninutul ferestrei este ters, de asemenea, i atunci cnd fereastra este redmensionat. Nu exist posibilitatea de derulare, de cutare sau de nlocuire, de salvare a fiierelor sau de verificare ortografic, dar putem spune c este un nceput! Pentru a face lucrurile mai uoare pentru mine, TYPER folosete un font cu dimensiune fix. Aa cum v putei imagina, scrierea unui editor pentru un font proportional este mult mai dificil. Programul obine un context de dispozitiv n timpul prelucrrii mai multor mesaje: WM_CREATE, WM_KEYDOWN, WM_CHAR si WM_PAINT. De fiecare dat, prin apelarea funciilor GetStockObject i SelectObject, este selectat n contextul de dispozitiv un font cu dimensiune fix. n timpul prelucrrii mesajului WM_SIZE, TYPER calculeaz limea i nlimea n caractere a ferestrei i salveaz aceste valori n variabilele cxBuffer i cyBuffer. Apoi apeleaz funcia malloc pentru alocarea unei zone de memorie suficient de mare pentru a conine toate caracterele care pot fi introduse ntr-o fereastr. Variabilele xCaret i yCaret sunt folosite pentru stocarea poziiei cursorului de editare. n timpul prelucrrii mesajului WM_SETFOCUS, TYPER apeleaz funcia CreateCaret pentru crearea unui semn caret cu dimensiunile unui caracter, apoi funcia SetCaretPos pentru stabilirea poziiei cursorului de editare i funcia ShowCaret, pentru a-l face vizibil. n timpul prelucrrii mesajului WM_KILLFOCUS, TYPER apeleaz funciile HideCaret i DestroyCaret.
#--------------------# TYPER.MAK make file #--------------------typer.exe : typer.obj $(LINKER) $(GUIFLAGS) -OUT:typer.exe typer.obj $(GUILIBS) typer.obj : typer.c $(CC) $(CFLAGS) typer.c /*-------------------------------------TYPER.C -- Typing Program (c) Charles Petzold, 1996 --------------------------------------*/ #include <windows.h> #include <stdlib.h> #define BUFFER(x,y) *(pBuffer + y * cxBuffer + x) LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM) ; int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow) { static char szAppName[] = "Typer" ; HWND hwnd ; MSG msg ; WNDCLASSEX wndclass ; wndclass.cbSize = sizeof (wndclass) ; wndclass.style = CS_HREDRAW | CS_VREDRAW ; wndclass.lpfnWndProc = WndProc ;

19

wndclass.cbClsExtra = 0 ; wndclass.cbWndExtra = 0 ; wndclass.hInstance = hInstance ; wndclass.hIcon = LoadIcon (NULL, IDI_APPLICATION) ; wndclass.hCursor = LoadCursor (NULL, IDC_ARROW) ; wndclass.hbrBackground = (HBRUSH) GetStockObject (WHITE_BRUSH) ; wndclass.lpszMenuName = NULL ; wndclass.lpszClassName = szAppName ; wndclass.hIconSm = LoadIcon (NULL, IDI_APPLICATION) ; RegisterClassEx (&wndclass) ; hwnd = CreateWindow (szAppName, "Typing Program", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hInstance, NULL) ; ShowWindow (hwnd, iCmdShow) ; UpdateWindow (hwnd) ; while (GetMessage (&msg, NULL, 0, 0)) { TranslateMessage (&msg) ; DispatchMessage (&msg) ; } return msg.wParam ; } LRESULT CALLBACK WndProc (HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam) { static char *pBuffer = NULL ; static int cxChar, cyChar, cxClient, cyClient, cxBuffer, cyBuffer, xCaret, yCaret ; HDC hdc ; int x, y, i ; PAINTSTRUCT ps ; TEXTMETRIC tm ; switch (iMsg) { case WM_CREATE : hdc = GetDC (hwnd) ; SelectObject (hdc, GetStockObject (SYSTEM_FIXED_FONT)) ; GetTextMetrics (hdc, &tm) ; cxChar = tm.tmAveCharWidth ; cyChar = tm.tmHeight ; ReleaseDC (hwnd, hdc) ; return 0 ; case WM_SIZE : // obtain window size in pixels cxClient = LOWORD (lParam) ; cyClient = HIWORD (lParam) ; // calculate window size in characters cxBuffer = max (1, cxClient / cxChar) ; cyBuffer = max (1, cyClient / cyChar) ; // allocate memory for buffer and clear it if (pBuffer != NULL) free (pBuffer) ; if ((pBuffer = (char *) malloc (cxBuffer * cyBuffer)) == NULL) MessageBox (hwnd, "Window too large. Cannot " "allocate enough memory.", "Typer", MB_ICONEXCLAMATION | MB_OK) ; else for (y = 0 ; y < cyBuffer ; y++) for (x = 0 ; x < cxBuffer ; x++)

20

BUFFER(x,y) = ' ' ; // set caret to upper left corner xCaret = 0 ; yCaret = 0 ; if (hwnd == GetFocus ()) SetCaretPos (xCaret * cxChar, yCaret * cyChar) ; return 0 ; case WM_SETFOCUS : // create and show the caret CreateCaret (hwnd, NULL, cxChar, cyChar) ; SetCaretPos (xCaret * cxChar, yCaret * cyChar) ; ShowCaret (hwnd) ; return 0 ; case WM_KILLFOCUS : // hide and destroy the caret HideCaret (hwnd) ; DestroyCaret () ; return 0 ; case WM_KEYDOWN : switch (wParam) { case VK_HOME : xCaret = 0 ; break ; case VK_END : xCaret = cxBuffer - 1 ; break ; case VK_PRIOR : yCaret = 0 ; break ; case VK_NEXT : yCaret = cyBuffer - 1 ; break ; case VK_LEFT : xCaret = max (xCaret - 1, 0) ; break ; case VK_RIGHT : xCaret = min (xCaret + 1, cxBuffer - 1) ; break ; case VK_UP : yCaret = max (yCaret - 1, 0) ; break ; case VK_DOWN : yCaret = min (yCaret + 1, cyBuffer - 1) ; break ; case VK_DELETE : for (x = xCaret ; x < cxBuffer - 1 ; x++) BUFFER (x, yCaret) = BUFFER (x + 1, yCaret) ; BUFFER (cxBuffer - 1, yCaret) = ' ' ; HideCaret (hwnd) ; hdc = GetDC (hwnd) ; SelectObject (hdc, GetStockObject (SYSTEM_FIXED_FONT)) ; TextOut (hdc, xCaret * cxChar, yCaret * cyChar, & BUFFER (xCaret, yCaret), cxBuffer - xCaret) ;

21

ShowCaret (hwnd) ; ReleaseDC (hwnd, hdc) ; break ; } SetCaretPos (xCaret * cxChar, yCaret * cyChar) ; return 0 ; case WM_CHAR : for (i = 0 ; i < (int) LOWORD (lParam) ; i++) { switch (wParam) { case '\b' : // backspace if (xCaret > 0) { xCaret-- ; SendMessage (hwnd, WM_KEYDOWN, VK_DELETE, 1L) ; } break ; case '\t' : // tab do { SendMessage (hwnd, WM_CHAR, ' ', 1L) ; } while (xCaret % 8 != 0) ; break ; case '\n' : // line feed if (++yCaret == cyBuffer) yCaret = 0 ; break ; case '\r' : xCaret = 0 ; // carriage return

if (++yCaret == cyBuffer) yCaret = 0 ; break ; case '\x1B' : // escape for (y = 0 ; y < cyBuffer ; y++) for (x = 0 ; x < cxBuffer ; x++) BUFFER (x, y) = ' ' ; xCaret = 0 ; yCaret = 0 ; InvalidateRect (hwnd, NULL, FALSE) ; break ; default : // character codes BUFFER (xCaret, yCaret) = (char) wParam ; HideCaret (hwnd) ; hdc = GetDC (hwnd) ; SelectObject (hdc, GetStockObject (SYSTEM_FIXED_FONT)) ; TextOut (hdc, xCaret * cxChar, yCaret * cyChar, & BUFFER (xCaret, yCaret), 1) ; ShowCaret (hwnd) ; ReleaseDC (hwnd, hdc) ; if (++xCaret == cxBuffer) { xCaret = 0 ; if (++yCaret == cyBuffer) yCaret = 0 ;

22

} break ; } } SetCaretPos (xCaret * cxChar, yCaret * cyChar) ; return 0 ; case WM_PAINT : hdc = BeginPaint (hwnd, &ps) ; SelectObject (hdc, GetStockObject (SYSTEM_FIXED_FONT)) ; for (y = 0 ; y < cyBuffer ; y++) TextOut (hdc, 0, y * cyChar, & BUFFER(0,y), cxBuffer) ; EndPaint (hwnd, &ps) ; return 0 ; case WM_DESTROY : PostQuitMessage (0) ; return 0 ; } return DefWindowProc (hwnd, iMsg, wParam, lParam) ; }

Figura 5.5. Programul TYPER Prelucrarea mesajelor WM_KEYDOWN i WM_CHAR este ceva mai complicat. Prelucrarea mesajului WM_KEYDOWN implic, n cea mai mare parte, tastele de deplasare a cursorului. Tasta Home duce cursorul de editare la nceputul liniei, tasta End l duce la sfritul liniei, iar tastele Page Up i Page Down l mut la nceputul, respectiv la sfritul ferestrei. Tastele cu sgei funcioneaz normal, ca i n alte programe. n cazul apsrii tastei Delete, programul TYPER trebuie s mute spre stnga cu o poziie tot coninutul bufferului de la urmtorul caracter, pn la sfritul liniei i apoi s afieze un spaiu la sfritul liniei. n timpul prelucrrii mesajului WM_CHAR sunt tratate tastele Backspace, Tab, Linefeed (Ctrl+Enter), Enter, Esc i caracterele. Remarcai c am folosit contorul de repetare (Repet Count) din parametrul IParam pentru prelucrarea mesajelor WM_CHAR (pornind de la ideea c toate caracterele introduse de utilizator sunt importante) dar nu i pentru prelucrarea mesajelor WM_KEYDOWN (pentru a evita derularea nedorit a ecranului). Tratarea tastelor Backspace i Tab este simplificat prin folosirea funciei SendMessage. Tasta Backspace este emulat prin logica de tratare a tastei Delete iar tasta Tab este emulat prin mai multe spaii. Aa cum am menionat anterior, este recomandat s mascai cursorul de editare atunci cnd efectuai operaii de desenare n timpul prelucrrii altor mesaje dect WM_PAINT. Programul face acest lucru n timpul prelucrrii mesajului WM_KEYDOWN pentru tasta Delete i n timpul prelucrrii mesajului WM_CHAR pentru caractere. n ambele cazuri, programul TYPER schimb coninutul bufferului i apoi afieaz noile caractere n fereastr. Aa cum se poate vedea n Figura 5.6, am folosit programul TYPER atunci cnd am lucrat la discursuri.

Figura 5.6. Fereastra afiat de programul TYPER. SETUL DE CARACTERE WINDOWS

23

Am menionat anterior c apsarea unei taste moarte naintea tastei corespunztoare unei litere precedat de o tasta moart genereaz un mesaj WM_CHAR pentru care parametrul wParam conine codul ASCII al caracterului cu semnul diacritic. S-ar putea ca aceast afirmaie s nu fie prea clar, deoarece setul de caractere ASCII nu conine nici un cod pentru caractere cu semne diacritice. Atunci, ce conine parametrul wParam? Pentru a rspunde la aceast ntrebare trebuie s discutm despre seturile de caractere, un subiect care, dei la prima vedere pare a-i avea mai degrab locul ntr-o discuie despre fonturi, are importana lui i pentru manipularea tastaturii. Setul de caractere ASCII standard pe 7 bii definete codurile de la 0 la 31 (0x1F) i codul 127 (0x7F) pentru caracterele de control i codurile de la 32 (0x20) la 126 (0x7E) pentru caracterele afiabile. Nici unul dintre aceste caractere nu are semne diacritice. Deoarece calculatoarele personale folosesc octei de cte opt bii, productorii de calculatoare definesc un set de caractere care conine 256 de coduri n loc de 128. Codurile suplimentare pot conine caractere cu semne diacritice. Setul de caractere extins" rezultat include i setul de caractere ASCII cu coduri de la 0 la 127. Dac Windows ar accepta un asemenea set de caractere extinse, afiarea caracterelor cu diacritice ar fi foarte simpl, dar Windows nu accept un set de caractere extins, ci dou. Din nefericire, ns, prezena celor dou seturi de caractere nu face lucrurile de dou ori mai uoare. Setul de caractere OEM Mai nti, haidei s revedem componentele hardware cu care lucreaz sistemul de operare Windows calculatoarele IBM PC i compatibile. La nceputul anilor '80, dezvoltatorii calculatoarelor IBM PC au decis s extind setul de caractere ASCII, aa cum se poate vedea n Figura 5.7. Codurile de la 0x20 la 0x7E sunt caracterele afiabile din setul de caractere ASCII. Celelalte sunt nestandard - sau cel puin aa erau n momentul respectiv. Acest set de caractere nu poate fi ignorat. Este codificat hardware n cipurile ROM ale plcilor grafice, imprimantelor i circuitelor BIOS ale plcilor de baz IBM. A fost copiat de numeroii productori de calculatoare i periferice compatibile IBM. Acest set de caractere face parte din ceea ce se numete standardul IBM". Multe programe non-Windows n mod caracter scrise pentru calculatoarele IBM PC au nevoie de acest set de caractere, deoarece folosesc pentru afiare caracterele de desenare a blocurilor i a liniilor (codurile de la B0H la DFH). Singura problem este faptul c setul extins de caractere definit de IBM nu este potrivit pentru Windows. n primul rnd, caracterele de desenare a blocurilor i a liniilor folosite de programele n mod text nu sunt necesare sub Windows, deoarece Windows lucreaz n mod grafic. Dac vrei s desenai o linie orizontal, n Windows este mai uor s apelai o funcie de desenare dect s afiai un ir de caractere 0xC4. n al doilea rnd, alfabetul grecesc i simbolurile matematice sunt mai puin importante n Windows dect literele accentuate, folosite de majoritatea limbilor europene. Un program care trebuie s afieze simboluri matematice o poate face mai uor folosind funcii grafice. Pe scurt, sistemul de operare Windows accept setul de caractere IBM, dar i acord o importan mai mic - n principal pentru aplicaii mai vechi, rulate n ferestre. n mod normal, aplicaiile Windows nu folosesc setul de caractere IBM. n documentaia Windows, setul de caractere IBM este numit setul de caractere OEM". Setul de caractere OEM este mai precis definit ca setul de caractere nativ pe calculatorul pe care ruleaz sistemul de operare Windows.

Asigurarea suportului internaional sub MS-DOS


Exist mai multe variante ale setului de caractere IBM PC, variante numite pagini de cod ". Varianta folosit n Statele Unite i n majoritatea rilor europene este pagina 437. Calculatoarele vndute n Norvegia, Danemarca, Portugalia i alte cteva ri europene folosesc alte pagini, care conin multe dintre caracterele speciale folosite n rile respective. n ultimul timp, o parte dintre aceste ri au nceput s folosesc pagina 850, care conine mai puine caractere grafice i mai multe litere cu accent i alte caractere speciale. Windows asigur suportul pentru paginile de coduri prin instalarea fonturilor OEM (folosite pentru rularea aplicaiilor MS-DOS n ferestre i pentru afiarea memoriei clipboard) corespunztoare paginii de coduri a sistemului i prin instalarea tabelelor de conversie corespunztoare pentru funciile CharToOem i OemToChar (despre care vom discuta ceva mai trziu). Programul de instalarea Windows Setup va selecta paginile de cod corespunztoare, n funcie de atributele regionale ale configuraiei curente a sistemului.

Setul de caractere ANSI


Setul extins de caractere folosit de obicei de Windows i de programele Windows se numete setul de caractere ANSI" i este, de fapt, un standard ISO. Atunci cnd programul dumneavoastr primete un mesaj WM_CHAR, parametrul wParam con ine codul ANSI al caracterului. Setul de caractere ANSI este prezentat n Figura 5.8. Aa cum se poate vedea, codurile de la 0x20 la 0x7E reprezint aceleai caractere ca i n setul de caractere OEM i n setul de caractere ASCII. Caracterele afiate sub forma unor blocuri nu sunt definite. Acestea pot aprea diferit pentru

24

Figura 5.7. Setul.IBM de caractere extinse, aranjate dup codul caracterelor.

Figura 5.8. Setul ANSI de caractere extinse, aranjate dup codul caracterelor. alte dispozitive de ieire (cum ar fi imprimanta). Fonturile TrueType definesc unele caractere suplimentare pentru codurile ANSI de la 0x80 la 0x9F.

25

OEM, ANSI i fonturile Windows conine diferite fonturi pentru afiarea seturilor de caractere ANSI i OEM. Atunci cnd obinei pentru prima dat o variabil handle a unui context de dispozitiv, unul dintre atributele acestuia este fontul selectat, n mod prestabilit, acesta este SYSTEM_FONT (fontul sistem") care folosete setul de caractere ANSI. Dac vrei s afiai caractere din setul OEM, putei s selectai fontul OEM_FIXED_FONT (numit i font de terminal") n contextul de dispozitiv, folosind urmtoarea instruciune:
SelectObject (hdc, GetStockObject (OEM_FIXED_FONT)) ;

PROBLEME LEGATE DE INTERNAIONALIZARE Am vzut c atunci cnd un utilizator Windows cu o tastatur diferit de cea definit pentru Statele Unite introduce un caracter cu semn diacritic, parametrul wPavam al mesajului WM_CHAR conine codul ANSI al caracterului respectiv. Ca urmare, dac trebuie s afiai pe ecran caracterul respectiv, este bine s folosii un font pentru setul de caractere ANSI (cum ar fi SYSTEM_FONT sau SYSTEM_FIXED_FONT). Dac folosii fontul OEM_FIXED_FONT, caracterul afiat pe ecran va fi incorect i l va surprinde pe utilizator. Alte cteva reguli simple v vor ajuta s pstrai nemodificat codul de manipulare a tastaturii atunci cnd scriei un program pentru piaa european. Folosirea seturilor de caractere Atunci cnd primii un mesaj WM_CHAR, inei seama de faptul c parametrul wParam poate avea i valori mai mari de 128. Nu pornii de la ideea c orice cod mai mare de 128 reprezint un caracter invalid. S-ar putea s avei uneori nevoie de un caracter scris cu majuscul. Nu folosii un algoritm propriu, cum ar fi: if (ch = 'a' && ch <= 'z') ch -=32 ; // Greit!!! Aceasta este o metod greit n programarea sub Windows. Dar nu folosii nici funciile standard de conversie din C: ch = toupper (ch) ; // Greit!!! Rezultatele acestor funcii sunt corecte numai pentru prima jumtate a setului de caractere. Funciile standard din C nu vor converti codul 0xE0 n 0xC0. n schimb, putei s folosii funciile Windows CharUpper i CharLower. Dac pString este un ir de caractere terminat cu zero, putei s l convertii n litere mari folosind funcia CharUpper:
CharUpper (pString) ;

Dac irul de caractere nu este terminat cu zero folosii funcia CharUpperBuff:


CharUpperBuff (pString, nLength) ;

Putei s folosii funcia CharUpper i pentru un singur caracter, dar sunt necesare unele conversii forate de tip, deoarece cuvntul mai semnificativ al parametrului trebuie s aib valoarea 0:
ch = CharUpper ((PSTR) (LONG) (BYTE) ch) ;

Dac ch este definit ca un caracter fr semn (unsigned character) prima conversie forat (BYTE) nu este necesar. Windows include i funciile CharLower i CharLowerBuff pentru convertirea irurilor de caractere n litere mici. Dac v gndii cu seriozitate la scrierea unor programe Windows care s poat fi modificate cu uurin pentru alte limbi, ar trebui s examinai i funciile CharNext i CharPrev. Aceste funcii v ajut s manipulai seturile de caractere pe mai muli octei, deseori folosite n rile orientale. Aceste seturi conin mai mult de 256 de caractere, dintre care unele folosesc doi octei. Dac v bazai pe aritmetica de pointeri normal din C pentru parcurgerea unui ir de caractere (de exemplu pentru cutarea unui caracter backslash ntr-o cale de acces la un director) s-ar putea s credei c ai gsit caracterul cutat i, de fapt, s folosii al doilea caracter al unui cod. Funciile CharNext i CharPrev primesc ca parametru un pointer de tip far la un ir de caractere i returneaz un pointer de tip far corect incrementat sau decrementat pentru codurile de caractere formate din doi octei. Comunicarea cu MS-DOS Dac Windows ar fi singurul sistem de operare rulat pe calculator, ai putea s uitai de setul de caractere OEM i s folosii numai setul de caractere ANSI. Dar utilizatorii pot s creeze fiiere n MS-DOS i s le foloseasc n Windows, sau s creeze fiiere n Windows i s le foloseasc n MS-DOS. Din pcate, sistemul de operare MS-DOS folosete setul de caractere OEM. Iat o posibil problem de comunicare. S presupunem c utilizatorul unui calculator personal, vorbitor de limb german, creeaz un fiier numit UBUNGEN.TXT (exerciii practice") ntr-un program MS-DOS, cum ar fi EDIT. Pe un calculator IBM PC caracterul U face parte din setul de caractere IBM (adic OEM) i are codul 154 sau 0x9A. (Folosind tastatura de tip american sub MS-DOS pe un calculator IBM PC putei s creai aceast liter introducnd codul Alt+154 folosind cifrele din blocul de taste numerice.) MS-DOS folosete acest cod de caracter pentru numele fiierului din director.

26

Dac un program Windows apeleaz o funcie MS-DOS ca s obin fiierele dintr-un director i afieaz numele fiierelor pe ecran folosind un font pentru setul de caractere ANSi, prima liter din numele fiierului UBUNGEN.TXT va fi afiat ca un bloc negru, deoarece codul 154 este unul dintre caracterele nedefinite din setul de caractere ANSI. Programul Windows trebuie s converteasc codul extins IBM 154 (0x9A) n codul ANSI 220 (0xDC), care reprezint litera U n setul de caractere ANSI. Acest lucru este fcut de funcia Windows OemToChar. Funcia OemToChar primete ca parametri doi pointeri de tip far ctre dou iruri de caractere. Caracterele OEM din primul ir sunt convertite n caractere ANSI i sunt stocate n cel de-al doilea ir:
OemToChar (lpszOemStr, lpszAnsiStr) ;

Acum s vedem acelai exemplu, dar n sens invers. Vorbitorul de limb german dorete ca programul Windows s creeze un fiier numit UBUNGEN.TXT. Primul caracter al numelui de fiier introdus de utilizator are codul 220 (0xDC). Dac folosii o funcie MS-DOS pentru deschiderea fiierului, MS-DOS folosete caracterul respectiv n numele fiierului. Dac ulterior utilizatorul vrea s vad numele fiierului sub MS-DOS, primul caracter va fi afiat sub forma unui bloc. naintea apelrii funciei MS-DOS trebuie s convertii numele fiierului n caractere OEM:
CharToOem (IpszAnsiStr, IpszOemStr) ;

Funcia CharToOem convertete caracterul 220 (0xDC) n caracterul 154 (0x9A). n Windows exist dou funcii cu acelai rezultat, CharToOemBuff i OemToCharBuff, pentru care, ns, irurile de caractere nu trebuie s se termine cu 0. Conversiile de mai sus sunt fcute i de funcia Windows OpenFile. Dac folosii funcia OpenFile nu apelai i funcia CharToOem. Dac folosii funcii MS-DOS ca s obinei lista de fiiere (aa cum face programul Windows File Manager), atunci numele fiierelor trebuie convertite cu funcia OemToChar, nainte de a fi afiate. Convertirea coninutului fiierelor este o alt problem care apare atunci cnd fiierele sunt folosite att sub Windows, ct i sub MS-DOS. Dac programul Windows folosete fiiere despre care tii cu siguran c au fost create ntr-un program MS-DOS, atunci coninutul acestora ar trebui s fie convertit cu ajutorul funciei OemToChar. La fel, dac programul dumneavoastr creeaz fiiere care vor fi folosite de un program MS-DOS, trebuie s folosii funcia CharToOem ca s convertii coninutul acestora. Funciile OemToChar i CharToOem sunt stocate n driverul de tastatur i conin tabele de cutare foarte simple. Funcia OemToChar convertete codurile OEM de Ia 0x80 la 0xFF ntr-un cod ANSI reprezentnd caracterul care seamn cel mai mult cu caracterul OEM corespunztor, n unele cazuri aceast conversie este aproximativ. De exemplu, majoritatea caracterelor de desenare din setul de caractere IBM sunt convertite n semne plus, liniue de desprire i linii verticale. Majoritatea codurilor OEM de la 0x00 la 0x1F nu sunt convertite n coduri ANSI. Funcia CharToOem convertete codurile ANSI de Ia 0xA0 la 0xFF n coduri din setul OEM. Literele accentuate din setul de caractere ANSI care nu apar n setul de caractere OEM sunt convertite n caracterul ASCII corespunztor, fr semnele diacritice. Folosirea blocului de taste numerice Dup cum probabil tii, tastatura IBM PC i sistemul BIOS v permit s introducei coduri pentru caracterele din setul extins IBM; astfel, apsai tasta Alt, introducei cu ajutorul tastelor numerice codul din trei cifre al caracterului OEM, apoi eliberai tasta Alt. n Windows, aceast posibilitate este dublat n dou feluri. n primul rnd, atunci cnd introducei codul Alt+[cod OEM] de la tastele numerice, Windows returneaz (prin parametrul wParam al mesajului WM_CHAR) codul caracterului ANSI care seamn cel mai mult cu caracterul al crui cod OEM l-ai introdus. Aceasta nseamn c Windows trece codul prin funcia OemToChar nainte de generarea mesajului WM_CHAR. Aceast operaie se face n folosul utilizatorului. Dac nu avei o tastatur pentru limba strin pe care dorii s o folosii i suntei obinuit s introducei caracterul U folosind combinaia de taste Alt+154, putei s facei acelai lucru i n Windows. Nu este nevoie s nvai i codurile ANSI. n al doilea rnd, dac vrei s generai un cod ANSI extins folosind tastatura de tip american, introducei codul Alt+0[cod ANSI] de la blocul de taste numerice. Parametrul wParam al mesajului WM_CHAR va conine codul ANSI respectiv. Ca urmare, Alt+0220 este tot U. Putei s ncercai acest lucru n programele KEYLOCK sauTYPER. Soluia Unicode pentru Windows NT Dezvoltatorii de programe implicai n crearea unor aplicaii pentru piaa internaional au fost nevoii s inventeze diferite soluii pentru rezolvarea deficienelor codului ASCII pe 7 bii, cum ar fi paginile de coduri sau seturile de caractere pe doi octei. Este nevoie de o soluie mai bun, i aceasta ar putea fi Unicode. Unicode este un standard de codificare a caracterelor, care folosete un cod uniform pe 16 bii pentru fiecare caracter. Acest cod permite reprezentarea tuturor caracterelor, din toate limbajele scrise, care pot fi folosite n comunicaiile prin calculator, inclusiv simbolurile chinezeti, japoneze i coreene. Unicode a fost dezvoltat de un consoriu de companii din industria calculatoarelor (inclusiv cele mai mari dintre acestea). Din nefericire, sistemul de operare Windows 95 asigur doar un suport rudimentar pentru standardul Unicode i nu poate furniza caractere Unicode prin driverul de tastatur. Aceasta este una dintre diferenele fa de Windows NT, care include suportul pentru standardul Unicode.

27

Evident, adaptarea programelor (i a modului de gndire al programatorilor) la ideea folosirii codurilor pe 16 bii este o sarcin laborioas, dar merit dac n acest fel vom putea afia pe ecran i vom putea tipri la imprimant texte n toate limbajele scrise existente.

28

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