programare windows

126
1 Introducere Scopul acestui curs este de a familiariza cititorul cu o parte din problemele cu care se confruntă cel ce doreşte să înveţe să programeze aplicaţii de tip Windows. În acest sens am considerat necesar să prezint modul de abordare al construcţiei unei aplicaţii Windows, fără a detalia funcţiile din interfaţa de programare (API) şi mai ales fără a incerca o abordare căt de căt sistematică a acestora. Recomand cu caldură cartea lui Charles Petzold – “Programare Windows”, considerată de multi cititori biblia programării sub Windows, precum si cartea lui J. Prossie – “Programming Windows with MFC – Second Edition”. Aceste cărţi precum si MSDN, stau la baza întocmirii acestor note de curs. Totuşi nu uitaţi că Internet-ul. Site-uri precum codeguru sau codeproject merită a fi vizitate. Mediul de dezvoltare folosit în elaborarea exemplelor este VC++ 6.0 şi Visual Studio .Net 2003. Exemple si alte explicatii la aceste note de curs le gasiti pe pagina http://www.infoiasi.ro/~iasimi/IDD . Tot aici gasiti si cursul complet, ce poate fi descărcat. Ce aduce nou sistemul de operare Windows? 1. Interfata grafica cu utilizatorul GUI (mediu grafic cu ferestre); toate tipurile de interfeţe grafice cu utilizatorul folosesc elemente grafice afişate într-o imagine de tip bitmap. La început ecranul era folosit numai pentru afişarea informaţiilor pe care utilizatorul le introducea de la tastatură. Intr-o interfaţă grafică, ecranul devine chiar o sursă pentru celelalte intrari ale utilizatorului. Pe ecran sunt afişate diferite obiecte grafice (butoane, bare de derulare, casete de editare, etc.). Folosind tastatura şi/sau mouse-ul, utilizatorul poate gestiona aceste obiecte. In locul unui ciclu unidirectional al informatiilor de la tastatură la program şi de la program la ecran (SO DOS), utilizatorul interacţionează direct cu obiectele de pe ecran. 2. Consecventa privind interfata cu utilizatorul. 3. Multitasking-ul. Notiunea de proces si de fir de executie. 4. Gestionarea memoriei. Windows şi mesaje Windows este referit adesea ca un sistem de operare bazat pe mesaje. Fiecare eveniment (apasarea unei taste, clic de mouse, etc.) este transformat într-un mesaj. In mod obişnuit aplicaţiile sunt construite în jurul unei bucle de mesaje care regăseşte aceste mesaje şi apelează funcţia potrivită pentru a trata mesajul. Mesajele, deşi sunt trimise aplicaţiilor, nu se adresează acestora, ci unei alte componente fundamentale a SO, fereastra (windows). O fereastra este mai mult decât o zonă dreptunghiulară afişată pe ecran; aceasta reprezintă o entitate abstractă cu ajutorul căreia utilizatorul şi aplicaţia interacţionează reciproc. Aplicaţii, fire şi ferestre O aplicaţie Win32 constă din unul sau mai multe fire (threads), care sunt căi paralele de execuţie. Gândim firele ca fiind multitasking-ul din cadrul unei aplicaţii. Observaţie: Sub Win32s, poate rula o aplicaţie cu un singur fir de execuţie. O fereastră este totdeauna “gestionată de” un fir; un fir poate fi proprietarul uneia sau mai multor ferestre sau pentru nici una. In final, ferestrele sunt într-o relaţie ierarhică; unele sunt la nivelul cel mai de sus, altele sunt subordonate părinţilor lor, sunt ferestre descendente. Exista mai multe tipuri de ferestre in Windows; cele mai obişnuite sunt asociate cu o aplicaţie. Casetele de dialog (dialog boxes) din cadrul unei ferestre sunt de asemenea ferestre. Acelaşi lucru pentru butoane, controale de editatre, list box-uri, icoane, etc.

Upload: ionut-fiorosu

Post on 22-Nov-2015

111 views

Category:

Documents


17 download

DESCRIPTION

Programare Windows

TRANSCRIPT

  • 1

    Introducere Scopul acestui curs este de a familiariza cititorul cu o parte din problemele cu care se confrunt cel ce

    dorete s nvee s programeze aplicaii de tip Windows. n acest sens am considerat necesar s prezint modul de abordare al construciei unei aplicaii Windows, fr a detalia funciile din interfaa de programare (API) i mai ales fr a incerca o abordare ct de ct sistematic a acestora. Recomand cu caldur cartea lui Charles Petzold Programare Windows, considerat de multi cititori biblia programrii sub Windows, precum si cartea lui J. Prossie Programming Windows with MFC Second Edition. Aceste cri precum si MSDN, stau la baza ntocmirii acestor note de curs. Totui nu uitai c Internet-ul. Site-uri precum codeguru sau codeproject merit a fi vizitate.

    Mediul de dezvoltare folosit n elaborarea exemplelor este VC++ 6.0 i Visual Studio .Net 2003. Exemple si alte explicatii la aceste note de curs le gasiti pe pagina http://www.infoiasi.ro/~iasimi/IDD. Tot aici gasiti si cursul complet, ce poate fi descrcat.

    Ce aduce nou sistemul de operare Windows? 1. Interfata grafica cu utilizatorul GUI (mediu grafic cu ferestre); toate tipurile de interfee grafice cu

    utilizatorul folosesc elemente grafice afiate ntr-o imagine de tip bitmap. La nceput ecranul era folosit numai pentru afiarea informaiilor pe care utilizatorul le introducea de la tastatur. Intr-o interfa grafic, ecranul devine chiar o surs pentru celelalte intrari ale utilizatorului. Pe ecran sunt afiate diferite obiecte grafice (butoane, bare de derulare, casete de editare, etc.). Folosind tastatura i/sau mouse-ul, utilizatorul poate gestiona aceste obiecte. In locul unui ciclu unidirectional al informatiilor de la tastatur la program i de la program la ecran (SO DOS), utilizatorul interacioneaz direct cu obiectele de pe ecran.

    2. Consecventa privind interfata cu utilizatorul. 3. Multitasking-ul. Notiunea de proces si de fir de executie. 4. Gestionarea memoriei.

    Windows i mesaje Windows este referit adesea ca un sistem de operare bazat pe mesaje. Fiecare eveniment (apasarea unei taste, clic de mouse, etc.) este transformat ntr-un mesaj. In mod obinuit aplicaiile sunt construite n jurul unei bucle de mesaje care regsete aceste mesaje i apeleaz funcia potrivit pentru a trata mesajul.

    Mesajele, dei sunt trimise aplicaiilor, nu se adreseaz acestora, ci unei alte componente fundamentale a SO, fereastra (windows). O fereastra este mai mult dect o zon dreptunghiular afiat pe ecran; aceasta reprezint o entitate abstract cu ajutorul creia utilizatorul i aplicaia interacioneaz reciproc.

    Aplicaii, fire i ferestre O aplicaie Win32 const din unul sau mai multe fire (threads), care sunt ci paralele de execuie. Gndim firele ca fiind multitasking-ul din cadrul unei aplicaii.

    Observaie: Sub Win32s, poate rula o aplicaie cu un singur fir de execuie. O fereastr este totdeauna gestionat de un fir; un fir poate fi proprietarul uneia sau mai multor ferestre sau pentru nici una. In final, ferestrele sunt ntr-o relaie ierarhic; unele sunt la nivelul cel mai de sus, altele sunt subordonate prinilor lor, sunt ferestre descendente. Exista mai multe tipuri de ferestre in Windows; cele mai obinuite sunt asociate cu o aplicaie. Casetele de dialog (dialog boxes) din cadrul unei ferestre sunt de asemenea ferestre. Acelai lucru pentru butoane, controale de editatre, list box-uri, icoane, etc.

  • 2

    Clasa Window Comportarea unei ferestre este definita de clasa fereastr (window class). Clasa fereastr menine informaii despre modul de afiare iniial, icoana implicit, cursor, resursele meniu i cel mai important lucru, adresa funciei ataat ferestrei procedura fereastr window procedure. Cnd o aplicaie proceseaz mesaje, aceasta se face n mod obinuit prin apelul funciei Windows DispatchMessage pentru fiecare mesaj primit; DispatchMessage la rndul ei apeleaz procedura fereastr corespunztoare, identificnd iniial crei ferestre i este trimis mesajul. n continuare procedura fereastr va trata mesajul.

    Exist mai multe clase fereastr standard furnizate de Windows. Aceste clase sistem globale

    implementeaz n general funcionalitatea controalelor comune. Orice aplicaie poate folosi aceste controale, de exemplu orice aplicaie poate implementa controale de editare, utiliznd clasa fereastra Edit. Aplicaiile pot de asemeni s-i defineasc propriile clase fereastr cu ajutorul funciei RegisterClass. Acest lucru se ntmpl n mod obinuit pentru fereastra principal a aplicaiei (icoana, resurse, etc.).

    Windows permite de asemeni subclasarea sau superclasarea unei ferestre existente. Subclasarea substituie procedura fereastr pentru o clas ferestr cu o alt procedur. Subclasarea se

    realizeaz prin schimbarea adresei procedurii fereastr cu ajutorul funciei SetWindowLong (instance subclassing) sau SetClassLong (subclasare global). Instance subclassing nseamn c se schimb numai comportarea ferestrei specificate. Global subclassing nseamn c se schimb comportarea tuturor ferestrelor de tipul specificat. Superclasarea creaz o nou clas bazat pe o clas existent, reinnd numai procedura fereastr. Pentru a superclasa o clas fereastr, o aplicaie regsete informaiile despre clasa fereastr utiliznd funcia GetClassInfo, modific structura WNDCLASS astfel recepionat i folosete structura modificat ntr-un apel al funciei RegisterClass. GetClassInfo returneaza de asemenea i adresa procedurii fereastr. Mesajele pe care noua fereastr nu le trateaz trebuie trecute acestei proceduri.

    Tipuri de mesaje Mesajele reprezint n fapt evenimente la diferite nivele ale aplicaiei. Exist o clasificare a acestor mesaje (din pcate nu prea exact): mesaje fereastr, mesaje de notificare i mesaje de comand, dar deocamdat nu ne intereseaz acest lucru.

    Mesajele windows constau din mai multe pri, descrise de structura MSG. typedef struct tagMSG { HWND hwnd; UINT message; WPARAM wParam; LPARAM lParam; DWORD time; POINT pt; } MSG; Descriere: hwnd identific n mod unic fereastra la care a fost transmis acest mesaj. Fiecare fereastr n Windows are un asemenea identificator (tipul este HWND). message reprezint identificatorul mesajului. Identificatorii mesajului sunt referii n mod obinuit cu ajutorul constantelor simbolice i nu prin valoarea lor numeric. Aceast descriere se gsete n windows.h. Urmtoarele elemente pot fi privite ca parametrii ai mesajului, care au valori specifice funcie de fiecare mesaj n parte. Marea majoritate a mesajelor ncep cu WM_. De exemplu WM_LBUTTONDOWN, WM_MOUSEMOVE, WM_LBUTTONUP, etc.

  • 3

    Aplicaiile pot s-i defineasc propriile mesaje. Cerina major este ca identificatorul mesajului s fie unic. Pentru a defini un mesaj n sistem folosim funcia RegisterWindowMessage. Mesaje i multitasking

    In Windows 3.1, bucla de mesaje are rol important n interaciunea dintre aplicaii i sistemul de operare (SQ). Funcionarea corect a unui SO Windows 3.1 depinde de cooperarea dintre aplicaii. Aplicaiile sunt cele care permit SO s preia controlul. Acest neajuns este nlturat n Windows 95/98, NT, 2000, XP i cele ce vor urma. SO este cel care realizeaz programarea aplicaiilor pentru cuantele de timp necesare pe procesor. Dei aceast planificare a execuiei este diferit pe Windows 95/98 fa de NT, rolul primordial revine SO.

    Cozi de mesaje

    n Windows pe 16 bii, SO menine o singur coad de mesaje. Cnd o aplicaie ncearc s gseasc urmtorul mesaj din coada de mesaje prin funciile GetMessage sau PeekMessage, SO poate efectua un context switch i poate activa o alt aplicaie pentru care mesajele ateapt n coad. Mesajul din vrful cozii este extras i returnat aplicaiei via structura MSG. Dac aplicaia eueaz n apelul lui GetMessage, PeekMessage sau Yield, aceasta blocheaz sistemul. Coada de mesaje poate deveni plin!

    n Win32 (95/98, NT, 2000, XP) mecanismul cozii de mesaje este mult mai complicat. O singur coad de mesaje nu mai rezolv problema. Aici sistemul de operare d controlul unei aplicaii i tot SO permite multitasking-ul. Dou sau mai multe fire pot accesa coada de mesaje n acelai timp i nu exist nici o garanie c mesajele extrase sunt ale lor. Acesta este unul motivele pentru care coada de mesaje a fost separat n cozi de mesaje individuale pentru fiecare fir din sistem. Procese i Fire

    ntr-un SO non-multifir, de exemplu UNIX, unitatea cea mai mic de execuie este task-ul sau procesul. Mecanismul de planificare (aparine SO) al task-urilor va comuta ntre acestea; multitasking-ul se realizeaz ntre dou sau mai multe procese (task-uri). Dac o aplicaie are nevoie s execute mai multe funcii simultan, atunci aceasta se va divide n mai multe task-uri. Din aceast tehnic decurg anumite neajunsuri, consum de resurse, timp de lansare a unui nou task, spaii de adrese diferite, probleme de sincronizare, etc.

    n contrast, ntr-un sistem multifir (multifilar, multithreaded) unitatea cea mai mic de execuie este firul, nu procesul. Un proces sau task poate conine mai multe fire, dintre care unul singur este firul principal, firul primar. Lansarea n execuie a unui nou fir cere mai puine resurse din partea SO, firul ruleaz n cadrul aceluiai proces, problemele de sincronizare sunt i nu sunt complicate. Oricum apar dou sincronizri: sincronizare ntre procese i sincronizare ntre fire. Fire i Mesaje

    Dup cum am mai spus proprietarul ferestrei este firul de execuie. Fiecare fir are coada proprie, privat, de mesaje n care SO depoziteaz mesajele adresate ferestrei. Aceasta nu nseamn c un fir trebuie neaprat s aib o fereastr proprie i o coad proprie de mesaje. Pot exista fire i fr fereastr i fr bucl de mesaje. n MFC, aceste fire se numesc worker threads (nu au ataat o fereastr, nu prezint interfa ctre utilizator), iar celelalte se numesc user-interface threads.

    Apeluri de funcii Windows

    Windows ofer un mare numr de funcii pentru a executa o mare varietate de task-uri, controlul proceselor, gestionarea ferestrelor, fiierelor, memoriei, servicii grafice, comunicaii, etc. Apelurile sistem pot fi organizate n trei categorii: 1. servicii nucleu (apeluri sistem pentru controlul proceselor, firelor, gestiunea memoriei, etc.);

  • 4

    2. servicii utilizator (gestiunea elementelor de interfa ale utilizatorului cum ar fi ferestre, controale, dialoguri, etc.);

    3. servicii GDI (Graphics Device Interface) (ieirea grafic independent de dispozitiv). Sistemul Windows include de asemenea funcii API pentru alte funcionaliti MAPI (Messaging API), TAPI (Telephony API) sau ODBC (Open Database Connectivity).

    Servicii Nucleu

    Serviciile nucleu cuprind de obicei: getionarea fiierelor, memoriei, proceselor, firelor, resurselor. Gestionarea fiierelor nu ar trebui s se mai fac cu funcii din bibliotecile C sau prin iostream-urile din

    C++. Aplicaiile ar trebui s utilizeze conceptul Win32 de obiect fisier file object i funciile asociate cu acesta. De exemplu exist fiiere mapate n memorie care asigura comunicarea ntre task-uri.

    Referitor la gestionarea memoriei pe lng funciile cunoscute, SO Windows ofer funcii care pot manipula spaii de adrese de sute de MB alocndu-le dar nefcnd commiting.

    Cea mai important faet a proceselor i firelor este gestiunea sincronizrii. Problema este complet nou i nu a fost ntlnit n Windows 3.1. n Win32, sincronizarea se face cu ajutorul unor obiecte de sincronizare, pe care firele le pot utiliza pentru a informa alte fire despre starea lor, de a proteja zone senzitive de cod sau de a obtine informatii despre alte fire sau starea altor obiecte.

    In Win32 multe resurse nucleu sunt reprezentate ca obiecte obiecte nucleu: fiiere, fire, procese, obiecte de sincronizare, etc. Obiectele sunt referite prin manipulatori, identificatori (handlers); exist funcii pentru manipularea generic a obiectelor, pentru manipularea obiectelor de un anumit tip. Sub NT, obiectele au ataate proprieti de securitate. De exemplu, un fir nu poate accesa un obiect fiier dac nu are drepturile necesare care s coincid cu proprietile de securitate.

    Modulul nucleu furnizezz de asemeni funcii pentru gestionarea resurselor interfa-utilizator. Aceste resurse includ icoane, cursoare, abloane de dialog, resurse string, tabele de acceleratori, bitmap-uri, etc.

    Nucleul NT furnizeaz printre altele: atribute de securitate pentru obiectele nucleu, backup, funcionalitatea aplicaiilor de tip consol care pot utiliza funcii pentru memoria virtual sau pot utiliza mai multe fire de execuie.

    Servicii utilizator

    Modulul utilizator furnizeaz apeluri sistem care gestioneaz aspecte i elemente ale interfeei utilizatorului; sunt incluse funcii care manipuleaz ferestre, dialoguri, meniuri, controale, clipboard, etc. Se exemplific tipurile de operaii pentru fiecare resurs n parte (n general creare, modificare, tergere, mutare, redimensionare, etc.).

    Modulul utilizator furnizeaz funcii pentru managementul mesajelor i cozilor de mesaje. Aplicaiile pot utiliza aceste apeluri pentru a controla coninutul cozii de mesaje proprii, a regsi i a procesa mesajele, a crea noi mesaje. Noile mesaje pot fi trimise (sent) sau plasate (posted) la orice fereastr. Un mesaj plasat pentru o fereastr funcia PostMessage - nseamn pur i simplu intrarea acestuia n coada de mesaje nu i procesarea imediat a acestuia. Trimiterea unui mesaj (sent) implic tratarea lui imediat sau mai corect spus funcia SendMessage nu-i termin execuia pn cnd mesajul nu a fost tratat.

    Servicii GDI

    Funciile din GDI sunt utilizate n mod obinuit pentru a executa operaii grafice primitive independente de dispozitiv, pe contexte de dispozitiv. Un context de dispozitiv este o interfa la un periferic grafic specific (n fapt este o structur de date pstrat n memorie). Contextul de dispozitiv poate fi utilizat pentru a obine informaii despre periferic i pentru a executa ieirile grafice pe acest periferic. Informaiile care pot fi obinute printr-un context de dispozitiv, descriu n detaliu acest periferic.

    Ieirea grafic este executat printr-un context de dispozitiv prin pasarea (trecerea) unui manipulator (identificator) al contextului de dispozitiv funciilor grafice din GDI.

  • 5

    Contextele de dispozitiv pot descrie o varietate mare de periferice. Contextele de dispozitiv obinuite includ: contexte de dispozitiv display, contexte de dispozitiv memorie (pentru ieirea unui bitmap memorat n memorie) sau contexte de dispozitiv printer.

    Un context de dispozitiv foarte special este contextul de dispozitiv metafile care permite aplicaiilor de a nregistra permanent apelurile din GDI (fiierul pstreaz o serie de primitive grafice) care sunt independente de dispozitiv. Metafiierele joac un rol crucial n reperzentarea independent de dispozitiv a obiectelor OLE nglobate.

    Desenarea ntr-un context de dispozitiv se face cu ajutorul coordonatelor logice. Coordonatele logice descriu obiectele utiliznd msurtori reale independente de dispozitiv, de exemplu, un dreptunghi poate fi descris ca fiind lat de 2 inch i nalt de 1 inch. GDI furnizeaz funcionalitatea necesar pentru maparea coordonatelor logice n coordonate fizice.

    Diferene semnificative exist n modul cum aceast mapare are loc n Win32s, Windows 95 i Windows NT.

    Win32s i Windows 95 folosesc reprezentarea coordonatelor pe 16 biti. Windows NT, XP poate manipula coordonate pe 32 bii.

    Toatre cele trei sisteme suport mapri (transformri) din coordonate logice n coordonate fizice. Aceste transformri sunt determinate (influenate) de valorile ce specific originea coordonatelor i (signed extent) extensia cu semn a spaiului logic i al celui fizic. Originea coordonatelor specific deplasarea pe orizontal i vertical, iar extensia (extent) determina orientarea i scara obiectelor dup mapare (transformare). n plus, Windows NT ofer ceea ce se numete world transformation functions. Prin aceste funcii, orice transformare liniar poate fi folosit pentru transformarea spaiului de coordonate logice n spaiul de coordonate fizice; n plus pentru translaii i scalare ieirile pot fi rotite sau sheared. Exemple de funcii grafice: Rectangle, Ellipse, Polygon, TextOut, etc. Alte funcii de interes deosebit (bit blit functions: PatBlt, BitBlt, StechBlt) sunt cele legate de desenarea i copierea bitmap-urilor. Contextele de dispozitiv pot fi create i distruse, starea lor poate fi salvat i rencrcat. Un alt grup de funcii gestioneaz transformrile de coordonate. Funcii comune tuturor platformelor pot fi utilizate pentru a seta sau regsi originea i extent-ul unei ferestre (spaiul de coordonate logic) i viewport-ului (spaiul de coordonate al perifericului destinaie). NT posed funcii specifice pentru transformri matriceale. Funciile GDI pot fi folosite de asemenea pentru gestionarea paletelor, aceasta nseamn c prin gestionarea paletei de culori, aplicaiile pot selecta o mulime de culori care se potrivesc cel mai bine cu culorile din imaginea (gif, pcx.) care trebuie afiat. Gestionarea paletei poate fi utilizat i n tehnici de animaie. O alt trstur a GDI-ului este crearea i gestionarea obiectelor GDI (pensoane, penie, fonturi, bitmap-uri, palete) precum i a regiunilor i a clipping-ului.

    Alte API-uri

    Funcii pentru controale comune; Funcii pentru dialoguri comune; MAPI, (Messaging Applications Programming Interface); MCI (Multimedia Control Interface); OLE API; TAPI (Telephony API).

  • 6

    Raportarea erorilor

    Majoritatea funciilor Windows folosesc un mecanism pentru evidenierea erorilor. Cnd apare o eroare, aceste funcii seteaz o valoare a erorii pentru firul respectiv, valoare care poate fi regsit cu funcia GetLastError. Valoarile pe 32 bii, returnate de aceast funcie sunt definite in winerror.h sau n fiierul header al bibliotecii specifice. Valoarea erorii poate fi setat i din cadrul aplicaiei cu ajutorul funciei SetLastError. Codurile de eroare trebuie s aib setat bitul 29.

    Din pcate tratarea erorilor nu a fost foarte bine standardizat in Windows. Din acest motiv trebuie s citim

    documentaia funciei pe care dorim s o folosim pentru a vedea ce tip de valoare returnez in cazul unei erori.

    Folosirea funciilor din biblioteca C/C++

    Aplicaiile Win32 pot folosi setul standard al funciilor din biblioteca C/C++ cu anumite restricii. Aplicaiile Windows nu au acces n mod normal la stream-urile stdin, stdout, stderr sau obiectele iostream din C++. Numai aplicaiile consol pot utiliza aceste stream-uri. Funciile relative la fiiere pot fi folosite, dar acestea nu suport toate facilitile oferite de SO Windows securitate, drepturi de acces. n locul funciilor din familia exec se va folosi CreateProcess.

    n ceeea ce privete gestionarea memoriei se folosesc cu succes funciile din C sau C++. Funciile din biblioteca matematic, pentru gestionarea stringurilor, a bufferelor, a caracterelor, a conversiilor de date pot fi de asemenea folosite. Aplicaiile Win32 nu trebuie s foloseasc ntreruperea 21 sau funcii IBM PC BIOS. Arhitectura unei aplicatii Windows. Forma vizual a unei aplicaii Windows

    1. Bara de titlu 2. Bara de meniu 3. Toolbar 4. Meniul sistem 5. Barele de navigare (derulare) vertical si orizontal 6. Bara de stare 7. Zona client

    Vom descrie n continuare elementele de care avem nevoie pentru a scrie prima aplicaie Windows (cu SDK). Pentru acest lucru mai avem nevoie de cteva noiuni.

  • 7

    Fereastra. Clasa fereastra. Fereastra se reprint pe ecran sub forma unui dreptunghi. Sistemul de operare pastreaz o serie de informaii despre fereastr, informaii ce sunt descrise de clasa fereastr, iar structura corespunztoare are numele de WNDCLASS(EX). Inainte de a crea fereastra cu funcia CreateWindow(Ex), trebuie s completm structura pentru clasa fereastr. Structura WNDCLASSEX - informatii despre clasa fereastra

    UINT cbSize; // sizeof(WNDCLASSEX) UINT style; // incep cu CS_ WNDPROC lpfnWndProc; // procedura fereastra int cbClsExtra; // bytes extra pt. clasa int cbWndExtra; // bytes extra pt. fereastra HANDLE hInstance; HICON hIcon; HCURSOR hCursor; HBRUSH hbrBackground; LPCTSTR lpszMenuName; LPCTSTR lpszClassName; HICON hIconSm; // numai in WNDCLASSEX;

    Clasa fereastr menine informaii despre modul de afiare iniial al ferestrei, icoana implicit, cursor, resursele meniu i cel mai important lucru, adresa funciei ataat ferestrei procedura fereastr (window procedure). In Windows avem de-a face cu clase ferestre deja definite i clase ferestre ce pot fidefinite de utilizator. Clase fereastr standard - clase sistem globale (controale commune, EDIT, BUTTON, LISTBOX, COMBOBOX, etc.). Clase fereastr definite de utilizator, de obicei fereastra principala a aplicatiei, inregistrarea clasei se face cu ajutorul functiei RegisterClass(Ex). Exemplu de completare a unei structuri WNDCLASSEX: // WNDCLASS wndClass; memset(&wndClass, 0, sizeof(wndClass)); // stiluri de fereastra wndClass.style = CS_HREDRAW | CS_VREDRAW; // procedura fereastra wndClass.lpfnWndProc = WndProc; // instanta aplicatiei wndClass.hInstance = hInstance; // resursa cursor wndClass.hCursor = LoadCursor(NULL, IDC_ARROW); // resursa penson wndClass.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1); // nume fereastra wndClass.lpszClassName = "HELLO"; iar inregistrarea clasei: RegisterClassEx(&wndClass); O fereastr n Windows este caracterizat prin dou categorii de stiluri: stiluri de clas i stiluri de fereastr. Stilurile de clas sunt prefixate de obicei cu CS_, iar cele de fereastra cu WS_. Stilurile de clas sunt reinute de data membru

  • 8

    style din WNDCLASS(EX) (mai multe stiluri de clas se pot nregistra folosind operatorul OR (|) pe bii). Stilurile ferestrei (WS_) se completeaz ca valori ale parametrilor funciei CreateWindowEx sau CreateWindow. Observaie: CreateWindow i CreateWindowEx creaz o fereastr, returneaz un HWND, dar cea de-a doua form folosete stiluri extinse pentru fereastr. In general sufixul Ex provine de la Extended. Astfel exist i funciile RegisterClass i RegsiterClassEx, prescurtat scrise sub forma RegisterClass(Ex). Stiluri de clasa (CS_) Pot fi combinate folosind operatorul OR (|) pe bii. Funcii folosite GetClassLong, SetClassLong Cele mai importante stiluri:

    Value Action

    CS_CLASSDC Aloc un context de dispozitiv ce poate fi partajat de toate ferestrele din clas.

    CS_DBLCLKS Trimite mesajul dublu clic procedurii ferestr.

    CS_OWNDC Aloc un unic context de dispozitiv pentru fiecare fereastr din calas.

    Stiluri fereastra (WS_) Pot fi combinate folosind operatorul OR (|) pe bii. Functii folosite: GetWindowLong, SetWindowLong Se furnizeaza ca parametri in functia CreateWindow(Ex). In continuare se descriu cteva stiluri de fereastr (lista complet poate consultat n MSDN).

    WS_BORDER Creaz o fereastr ce are margini. WS_CAPTION Creaz o fereastr ce are o bar de titlu. Nu poate fi utilizat cu stilul WS_DLGFRAME.

    Folosirea acestui stil implic i folosirea stilului definit anterior. WS_CHILD Creaz o fereastr descent (copil). Nu poate fi utilizat cu stilul WS_POPUP. WS_DISABLED Creaz o fereastr ce iniial este disabled. WS_DLGFRAME Creaz o fereastr cu margini duble, dar fr titlu. WS_GROUP Specific primul conttrol al unui grup de contraole. WS_HSCROLL Creaz o fereastr ce are bara de navigare orizontal. WS_MAXIMIZE Creaz o fereastr de mrime maxim. WS_MAXIMIZEBOX Creaz o fereastr ce are butonul Maximize. WS_MINIMIZE i WS_MINIMIZEBOX asemntoare cu cele dou de mai sus. WS_VISIBLE Creaz o fereastr ce iniial vizibil.

    O list complet a stilurilor de fereastr poate fi consultat n MSDN. Crearea unei ferestre. Funcia CreateWindow(Ex) n exemplul ce urmeaz se creaz o fereastra cu stiluri extinse. HWND CreateWindowEx( DWORD dwExStyle, // extended window style LPCTSTR lpClassName, // pointer to registered class name LPCTSTR lpWindowName, // pointer to window name DWORD dwStyle, // window style int x, // horizontal position of window

  • 9

    int y, // vertical position of window int nWidth, // window width int nHeight, // window height HWND hWndParent, // handle to parent or owner window HMENU hMenu, // handle to menu, or child-window //iden HINSTANCE hInstance, // handle to application instance LPVOID lpParam // pointer to window-creation data ); lpClassName = numele clasei, vine din completarea structurii WNDCLASS(EX) si apoi inregistrarea clasei cu functia RegisterClass(Ex). HWND CreateWindow( LPCTSTR lpClassName, // pointer to registered class name LPCTSTR lpWindowName, // pointer to window name DWORD dwStyle, // window style int x, // horizontal position of window int y, // vertical position of window int nWidth, // window width int nHeight, // window height HWND hWndParent, // handle to parent or owner window HMENU hMenu, // handle to menu or child-window identifier HANDLE hInstance, // handle to application instance LPVOID lpParam // pointer to window-creation data ); Returneaza un handle (tip HWND) la fereastra creat. Cu acest handle este identificat fereastra n aplicaie. Structura mesajului Mesajele reprezint n fapt evenimente la diferite nivele ale aplicaiei. Exist o clasificare a acestor mesaje : mesaje fereastr, mesaje de notificare i mesaje de comand. Mesajele windows constau din urmatoarele pri, descrise de structura MSG. typedef struct tagMSG { HWND hwnd; UINT message; WPARAM wParam; LPARAM lParam; DWORD time; POINT pt; } MSG; Explicatii. Exemplu de mesaj cu parametri: Mesaj cu doi parametri WM_LBUTTONDOWN fwKeys = wParam; // key flags xPos = LOWORD(lParam); // horizontal position of cursor yPos = HIWORD(lParam); // vertical position of cursor

    Observaie:

  • 10

    Acest mesaj este trimis cnd s-a fcut clic pe butonul stnga al mouse-ului. Parametrii acestui mesaj pstreaz coordonatele mouse-ului unde s-a fcut clic. ntr-o aplicaie SDK, wParam i lParam i gsii ca fiind parametrii numrul 3 i 4 al funciei ce trateaz mesajele pentru o caset de dialog, sau ai funciei procedur fereastr. Preferm s nu traducem n acest caz descrierea mesajelor pentru a ncerca s v obinuim cu MSDN.

    Parameters fwKeys

    Value of wParam. Indicates whether various virtual keys are down. This parameter can be any combination of the following values: Value Description

    MK_CONTROL Set if the ctrl key is down.

    MK_LBUTTON Set if the left mouse button is down.

    MK_MBUTTON Set if the middle mouse button is down.

    MK_RBUTTON Set if the right mouse button is down.

    MK_SHIFT Set if the shift key is down.

    xPos Value of the low-order word of lParam. Specifies the x-coordinate of the cursor. The coordinate is relative to the upper-left corner of the client area.

    yPos

    Value of the high-order word of lParam. Specifies the y-coordinate of the cursor. The coordinate is relative to the upper-left corner of the client area.

    Return Values If an application processes this message, it should return zero. An application can use the MAKEPOINTS macro to convert the lParam parameter to a POINTS structure. Mesaj cu un parametru WM_QUIT The WM_QUIT message indicates a request to terminate an application and is generated when the application calls the PostQuitMessage function. It causes the GetMessage function to return zero. WM_QUIT nExitCode = (int) wParam; // exit code Parameters nExitCode

    Value of wParam. Specifies the exit code given in the PostQuitMessage function. Return Values This message does not have a return value, because it causes the message loop to terminate before the message is sent to the application's window procedure. Mesaj fara parametri WM_CLOSE The WM_CLOSE message is sent as a signal that a window or an application should terminate. Parameters This message has no parameters. Return Values If an application processes this message, it should return zero. Default Action The DefWindowProc function calls the DestroyWindow function to destroy the window.

  • 11

    WM_DESTROY The WM_DESTROY message is sent when a window is being destroyed. It is sent to the window procedure of the window being destroyed after the window is removed from the screen. This message is sent first to the window being destroyed and then to the child windows (if any) as they are destroyed. During the processing of the message, it can be assumed that all child windows still exist. Parameters This message has no parameters. Return Values If an application processes this message, it should return zero. Exemplu de tratare a unui mesaj ntr-o procedur fereastr LRESULT CALLBACK WndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { switch(uMsg) { case WM_PAINT: DeseneazaCeva(hwnd); break; case WM_DESTROY: PostQuitMessage(0); break; default: return DefWindowProc(hwnd, uMsg, wParam, lParam); } return 0; // trebuie sa intoarca totdeauna 0 (zero) }

    Categorii de mesaje Exista trei categorii de mesaje: 1) Mesaje fereastr (Windows messages )

    Aceste mesaje sunt prefixate cu WM_ , excepie fcnd mesajul WM_COMMAND. Mesajele fereastra sunt tratate de ferestre si vizualizari (n fapt vizualizarile sunt tot ferestre, diferenta exist ntre fereastra principal main frame - si vizualizri). Aceste mesaje, n general, au parametri.

    Exemplu // cod in procedura fereastra case WM_LBUTTONDOWN: { // hDC = handle la Device Context

    HDC hDC; RECT clientRect; hDC = GetDC(hwnd); if (hDC != NULL) {

    GetClientRect(hwnd, &clientRect); DPtoLP(hDC, (LPPOINT)&clientRect, 2); DrawText(hDC, "Hello, World!", -1, &clientRect,

    DT_CENTER | DT_VCENTER | DT_SINGLELINE); ReleaseDC(hwnd, hDC);

    } break; }

  • 12

    2) Notificri ale controlului (Control notifications )

    Acestea includ mesajul de notificare WM_COMMAND din controale i alte ferestre descendente, ctre fereastra printe. Exemplu: Un control de editare trimite parintelui lui (in mod normal o caseta de dialog) un mesaj WM_COMMAND ce contine codul de notificare EN_CHANGE cind utilizatorul a alterat textul din cadrul controlului. Exceptie: Notificarea BN_CLICKED (un control push button a fost apasat) este tratata ca un mesaj de comanda. Cadrul de lucru (framework) ruteaza mesajele de notificare ale controalelor ca orice alt mesaj. Unele mesaje de notificare sunt trimise via mesajul WM_NOTIFY. Trebuie consultata documentatia Windows pentru a vedea notificarile suportate de controale via WM_NOTIFY (de exemplu consultati controlul list view). In SDK, codul se gaseste in procedura fereastra a casetei de dialog ce contine controlul. Exemplu: i) Creare caseta de dialog (din resurse) ce are ID-ul IDD_CURS

    // Codul se poate gasi in procedura fereastrei // principale a aplicatiei DialogBox(hInst, MAKEINTRESOURCE(IDD_CURS),

    hwndMain, (DLGPROC)DlgCurs);

    // hwndMain = HANDLE pentru fereastra principala // a aplicatiei, // obtinut ca rezultat al crearii ferestrei: // vezi functia CreateWindow // sau CreateWindowEx // DlgCurs = procedura fereastra a casetei // de dialog identificata de IDD_CURS

    ii) Prototipul pentru procedura fereastr (ataat unui dialog) este:

    BOOL CALLBACK DlgCurs(HWND hwnd, UINT msg,

    WPARAM wParam, LPARAM lParam) { ... switch(msg) { ... case WM_COMMAND:

    { switch (LOWORD(wParam)) { case IDC_EDITCURS: // ID control de editare switch(HIWORD(wParam)) { case EN_CHANGE: // tratare notificare... break; ...

    }

  • 13

    case IDC_PUSHBUTTON: // ID buton // cod pentru tratare mesaj ...

    break; } ... } case WM_NOTIFY: { UINT code; UINT idCtrl; code = ((NMHDR*)lParam)->code; idCtrl = ((NMHDR*)lParam)->idFrom; switch(wParam) // wParam par. in procedura fereastra { case IDC_LISTVIEW: { UINT code = ((NMHDR*)lParam)->code; switch(code) { case NM_CLICK: { break; } } ...

    } }

    In MFC Mesajele de notificare sunt tratate de obicei de obiectele claselor derivate din CWnd (CFrameWnd, CMDIFrame, CMDIChildWnd, CView, CDialog) precum i clasele proprii derivate din aceasta. Aceste obiecte sunt caracterizate de faptul ca au un HWND, identificator al ferestrei Windows. 3) Mesaje de comanda (Command messages)

    Acestea includ mesajele de notificare WM_COMMAND din obiectele interfeei cu utilizatorul: meniu-uri, butoane din toolbar i taste acceleratoare. Cadrul de lucru proceseaza comenzile in mod diferit fa de celelalte mesaje i acestea pot fi tratate de mai multe tipuri de obiecte (documente, abloane de documente document templates -, obiectul aplicaie, vizualizri).

    A se vedea rutarea comenzilor si ierarhia de clase MFC. Recomand exersarea acestora intr-o aplicatie tip SDI si apoi puteti trece la MDI. Intr-o aplicatie bazata pe dialog, lucrurile sunt mai usoare. Exemplu // cod scris in procedura ferestrei principale a aplicatiei switch(nCode) { // ... nCode ID-ul optiunii din meniu

  • 14

    case IDM_WINDOWTILE: SendMessage(hwnd,WM_MDITILE,0,0); break; case IDM_WINDOWCASCADE: SendMessage(hwnd,WM_MDICASCADE,0,0); break; } Coada de mesaje Windows folosete dou metode pentru a ruta mesajele la procedura fereastr:

    (1) punnd mesajele ntr-o coad de ateptare (principiul FIFO), numita coada de mesaje, care este un obiect sistem, gestionat in memorie i pastreaza mesajele; aceste mesaje se numesc queued messages.

    (2) trimind mesajele n mod direct procedurii fereastr. Acestea sunt numite nonqueued messages (ex. WM_ACTIVATE, WM_SETFOCUS, etc.). Aceste mesaje sunt trimise pentru a notifica o fereastra despre aparitia unui eveniment care o afecteaz.

    Sistemul mentine o singura coada de mesaje numita coada de mesaje sistem i un numr de cozi de mesaje pentru fiecare fir cu interfa. Driver-ul pentru mouse sau tastatur convertete intrrile (evenimentele) in mesaje i le plaseaz n coada de mesaje a sistemului. Evenimentele genereaz mesaje. SO preia cte un singur mesaj la un moment dat din coada de mesaje a sistemului, determin crei ferestre i este adresat i apoi pune mesajul n coada de mesaje a firului respectiv. O schem simplificat a acestui mecanism este Windows recepioneaz evenimentul (aciunea utilizatorului, scurgerea unui interval de timp, etc)

    Windows transform aciunea n mesaj

    Fereastra programului primete mesajul

    Programul execut o bucl care preia i distribuie mesajele.

    Firul preia mesajul (functia GetMessage) l redirecteaz ctre sistem (functia RegisterClass nregistreaz n sistem o clas fereastr o structur WNDCLASS / WNDCLASSEX deci are informaii despre adresa procedurii fereastra; CreateWindow / CreateWindowEx va folosi aceasta clasa inregistrat pentru a crea fereastra i ca atare va avea informaii despre adresa procedurii fereastr) iar acesta va apela funcia fereastr corespunztoare pentru a-l trata (funcia DispatchMessage face acest lucru, dar nu completeaz informaiile legate de timpul cnd a aprut mesajul sau de poziia cursorului mouse-ului). In cadrul procedurii fereastr exist codul pentru tratarea mesajului. Procedura fereastr este de tip CALLBACK, deci este o functie ce va fi apelata de catre sistemul de operare. Programatorul poate considera ca functia DispatchMessage apeleaza procedura fereastra furnizindu-i mesajul ce trebuie tratat (in realitate SO procedeaz un pic altfel). Exceptie: Mesajul WM_PAINT este mentinut in coada de mesaje si va fi procesat cind nu mai exista alte mesaje. Daca apar mai multe mesaje WM_PAINT, se retine ultimul mesaj de acest tip, celelalte mesaje WM_PAINT fiind

  • 15

    sterse din coada de mesaje. Acest algoritm reduce efectul neplacut datorat multiplilelor redesenari ale ferestrei precum si timpul procesor necesar redesenarii. Functii utile ce lucreaza cu mesaje (pentru o lista completa consultati MSDN) Un fir poate pune un mesaj in coada proprie de mesaje folosind funcia PostMessage. Funcia PostThreadMessage va pune un mesaj n coada de mesaje a altui fir. Un mesaj poate fi examinat, fr a fi eliminat din coada de mesaje, cu ajutorul funciei PeekMessage. Aceasta funie completeaza o structur de tip MSG cu informaii despre mesaj.

    Informatiile legate de timpul cind a aparut mesajul i pozitia cursorului mouse-ului pot fi obinute cu ajutorul funciilor GetMessageTime, respectiv GetMessagePos. Un fir poate folosi funcia WaitMessage pentru a da controlul altui fir cnd nu mai exist mesaje n coada proprie de mesaje.

    Un mesaj este insotit in sistem de dou valori date de parametrii WPARAM si LPARAM ai functiei SendMessage. Daca analizati prototipul procedurii fereastra veti observa ca aceasta contine ID-ul mesajului si doi parametri de tipul indicat mai sus. Un mesaj poate contine informatii aditionale, informatii furnizate pe o valoare pe 32 de biti (poate fi pointer la o structura). Aceste informatii se completeaza cu functia SetMessageExtraInfo si pot fi regasite cu functia GetMessageExtraInfo. Informatia ataat cozii de mesaje a firului este valid pna la urmtorul apel al functiei GetMessage sau PeekMessage. Aproape toate ferestrele din Windows pot pstra informaii suplimentare, informaii pstrate pe o valoare pe 32 biti, deci putem pastra pointeri la structuri (listbox, combo box vezi functiile SetItemData, SetItemDataPtr, GetItemData, GetItemDataPtr, etc.) Pentru trimiterea unui mesaj se pot utiliza functiile SendMessage, PostMessage, PostQuitMessage, SendDlgItemMessage, SendMessageCallback, SendNotifyMessage, PostMessage, PostThreadMessage.

    Functia SendMessage blocheaz firul apelant ateptnd tratarea mesajului. Celelalte functii, enumerate mai sus, prezint diverse variaii de la aceasta tehnica general. Observatie. Trebuie sa facem dictinctie intre coada de mesaje, harta de mesaje, bucla de mesaje. Notiunea de harta de mesaje exista in ierarhia de clase MFC. Bucla de mesaje Bucla de mesaje cea mai simpl consta din apelul uneia din functiile: GetMessage, TranslateMessage, si DispatchMessage. Exemplu: MSG msg; while( GetMessage( &msg, NULL, 0, 0 ) ) { TranslateMessage( &msg ); DispatchMessage( &msg ); } Acest cod se poate gasi in cadrul functiei WinMain. Functia GetMessage regsete un mesaj din coad i l copie n structura de tip MSG. Aceast funcie returneaz valori diferite de zero atta timp ct nu extrage din coada mesajul WM_QUIT, caz in care returneaza zero (FALSE) i bucla de mesaje se termin. Intr-o aplicatie cu un singur fir, terminarea buclei de mesaje este echivalent cu inchiderea aplicatiei. O aplicatie poate forta terminarea buclei de mesaje utilizind functia PostQuitMessage, in mod obisnuit ca rspuns la mesajul WM_DESTROY a ferestrei principale a aplicatiei. Vezi mai jos descrierea functiei GetMessage.

  • 16

    Daca specificm un handle la fereastra (al doilea parametru din GetMessage), vor fi extrase numai mesajele pentru fereastra identificata de acel handle. Ultimii doi parametri din GetMessage realizeaza o filtrare pentru mesaje (valaorea minima si valoarea maxima pentru mesaje). TranslateMessage. Daca firul primeste caractere de la tastatur, atunci trebuie folosita functia TranslateMessage. Sistemul genereaza mesajele WM_KEYDOWN si WM_KEYUP (mesaje de chei virtuale) ori de cite ori utilizatorul apas o tast. Aceste mesaje nu contin codul caracterului apasat. TranslateMessage traduce mesajul de cheie virtuala intr-un mesaj caracter (WM_CHAR) i il pune napoi n coada de mesaje (evident in procedura fereastra trebuie sa existe un handler (functie, portiune de cod) care sa trateze mesajul WM_CHAR)), iar la urmtoarea iteraie a buclei de mesaje acesta va ajunge la DispatchMessage. DispatchMessage trimite un mesaj la procedura fereastr a ferestrei identificat de handle ferestrei din mesaj. Daca handle este HWND_TOPMOST, DispatchMessage trimite mesajul procedurii ferestrei de la nivelul cel mai de sus (top level) din sistem. Dac handle este NULL, nu se intmpl nimic. TranslateAccelerator

    O aplicatie care folosete taste de accelerare trebuie s fie n stare s tanslateze (traduc) mesajele de la tastur n mesaje de comand. Pentru a realiza acest lucru, bucla de mesaje trebuie sa includ un apel la funcia TranslateAccelerator. IsDialogMessage

    Daca un fir folosete casete de dialog amodale, bucla de mesaje trebuie s includ un apel la funcia IsDialogMessage. In acest mod caseta de dialog poate primi intrri de la tastatur. GetMessage Functia GetMessage regaseste un mesaj din coada de mesaje a firului apelant i il plaseaz n structura specificat. Aceast funcie poate regsi mesaje asociate cu o anumit fereastr (identificat de HWND) i mesaje "puse" cu ajutorul funciei PostThreadMessage. Funcia regsete mesajele care se afl ntr-un anumit interval de valori. Functia GetMessage nu regsete mesaje pentru ferestre ce aparin altor fire sau aplicaii. Sintaxa este: BOOL GetMessage( LPMSG lpMsg, // address of structure with message HWND hWnd, // handle of window UINT wMsgFilterMin, // first message UINT wMsgFilterMax // last message );

    Parametrii

    lpMsg = Pointer la o structur de tip MSG, structur ce va fi completat cu informaiile despre mesajul extras din coada de mesaje a firului.

    hWnd = Handle la fereastra ale carei mesaje vor fi extrase. O valoare are o semnificaie speciala: Valoare Semnificatie

    NULL GetMessage regaseste mesajele pentru orice fereastra ce apartine firului din care este apelata si mesaje "puse" (in coada de mesaje) cu ajutorul functiei PostThreadMessage.

  • 17

    WinMain Un program sub windows ncepe cu funcia WinMain (un program sub DOS incepe cu functia main()) care are patru parametri: primii doi sunt de tip HINSTANCE, al treilea reprezinta un pointer la un ir de caractere ce reprezint linia de comand, iar ultimul este un intreg i reprezint modul de afisare al ferestrei (minimizata, maximizata, normal, etc.). Al doilea parametru este pstrat pentru compatibilitate cu Windows pe 16 biti, deci in Windows pe 32 biti nu se folosete i totdeauna va fi NULL. int WINAPI WinMain(HINSTANCE hInstance,

    HINSTANCE hPrevInstance, LPSTR lpCmdLine, UINT nCmdShow);

    Clasele fereastr care se completeaz i inregistreaz se fac n cadrul acestei funcii WinMain (direct sau indirect prin apelul altei funcii). Data membru hInstance din WNDCLASS(EX) se va completa cu hInstance din WinMain. lpfnWndProc (adresa procedurii fereastra) se va completa cu adresa funciei definit n cadrul aplicaiei. In acest mod stabilim funcia unde vom scrie codul ce va trata mesajele ce vin la aceasta fereastr. Adresa acestei funcii va fi inregistrat n sistem, iar apelul functiei DispatchMessage va identifica funcia corect care trebuie s trateze mesajul. Procedura fereastra este apelata de catre SO (nu ma apela tu, te apelez eu). (vezi tipul CALLBACK) Observatie: Trebuie facut distincie ntre clasa fereastr i o clas din POO i de asemenea ntre stil clas si stil fereastr (de altfel stilurile clasei sunt prefixate cu CS_ iar stilurile ferestrei cu WS_). Exemplu complet in SDK. #include // ---------- Apelata pe mesajul WM_PAINT void Deseneaza(HWND hwnd) {

    HDC hDC; PAINTSTRUCT paintStruct; RECT clientRect; hDC = BeginPaint(hwnd, &paintStruct); if (hDC != NULL) {

    GetClientRect(hwnd, &clientRect); DPtoLP(hDC, (LPPOINT)&clientRect, 2); DrawText(hDC, "Hello, World!", -1, &clientRect,

    DT_CENTER | DT_VCENTER | DT_SINGLELINE); EndPaint(hwnd, &paintStruct);

    } } // --------- Procedura fereastra LRESULT CALLBACK WndProc(HWND hwnd,

    UINT uMsg, WPARAM wParam, LPARAM lParam)

  • 18

    { switch(uMsg) {

    case WM_PAINT: Deseneaza(hwnd); break;

    case WM_DESTROY: PostQuitMessage(0); break;

    default: return DefWindowProc(hwnd, uMsg, wParam, lParam);

    } return 0; // trebuie sa intoarca totdeauna 0 (zero) } // --------------- Programul principal int WINAPI WinMain(HINSTANCE hInstance,

    HINSTANCE hPrevInstance, LPSTR d3, int nCmdShow)

    { MSG msg; HWND hwnd; WNDCLASS wndClass; memset(&wndClass, 0, sizeof(wndClass)); // stiluri de fereastra wndClass.style = CS_HREDRAW | CS_VREDRAW; // procedura fereastra wndClass.lpfnWndProc = WndProc; wndClass.hInstance = hInstance; // instanta aplicatiei // resursa cursor wndClass.hCursor = LoadCursor(NULL, IDC_ARROW); // resursa penson wndClass.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1); // nume fereastra wndClass.lpszClassName = "HELLO"; // inregistrare fereastra if (!RegisterClass(&wndClass)) return FALSE; hwnd = CreateWindow("HELLO", "HELLO",

    WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, hInstance, NULL);

    // stabilire atribute pentru afisarea ferestrei ShowWindow(hwnd, nCmdShow); // desenarea ferestrei se trimite mesajul WM_PAINT UpdateWindow(hwnd); // bucla de mesaje while (GetMessage(&msg, NULL, 0, 0))

    DispatchMessage(&msg); return msg.wParam;

    } Explicaii: 1. se creaz fereastra prin completarea structurii WNDCLASS;

  • 19

    2. se nregistreaz fereastra RegisterClass(&wndClass); 3. se creaz fereastra CreateWindow; 4. se stabileste modul de afiare al ferestrei ShowWindow(hwnd, nCmdShow); 5. se afieaz fereastra propriu zis UpdateWindow(hwnd); 6. urmeaz bucla de mesaje. Alte functii folositoare in gestiunea ferestrei GetWindowLong - Regsete informaii despre fereastra specificat. LONG GetWindowLong( HWND hWnd, // handle of window int nIndex // offset of value to retrieve); Adresa procedurii fereastr poate fi obinut cu funcia GetWindowLong cu parametrul GWL_WNDPROC sau DWL_DLGPROC. Valori posibile pentru parametrul nIndex

    Valoare Actiune

    GWL_EXSTYLE Regsete stilurile extinse ale ferestrei.

    GWL_STYLE Regsete stilurile ferestrei.

    GWL_WNDPROC Regsete adresa procedurii fereastr, sau un handle ce reprezint adresa procedurii fereastr. Trebuie s folosim CallWindowProc pentru a apela procedura fereastr.

    GWL_HWNDPARENT Regste handle la fereastra printe, dac exist.

    GWL_ID Regsete identificatorul ferestrei.

    Mai multe detalii n MSDN.

    Cnd hWnd identifica o caseta de dialog, pot fi folosite si urmtoarele valori:

    Valoare Actiune

    DWL_DLGPROC Regsete adresa procedurii casetei de dialog.

    DWL_MSGRESULT Regsete valoare de retur a unui mesaj procesat in procedura ataat casetei de dialog.

    SetWindowLong - modific atributele unei ferestre. Prototip LONG SetWindowLong( HWND hWnd, // handle of window int nIndex, // offset of value to set LONG dwNewLong // new value ); nIndex - Valori posibile sunt n general aceleai ca la funcia anterioar, deosebirea fiind n aceea c aceste valori se vor modifica. CallWindowProc (fr traducere)

  • 20

    Putem folosi functia CallWindowProc pentru a apela procedura fereastr. LRESULT CallWindowProc( WNDPROC lpPrevWndFunc, // pointer to previous procedure HWND hWnd, // handle to window UINT Msg, // message WPARAM wParam, // first message parameter LPARAM lParam // second message parameter ); Parameters lpPrevWndFunc

    Pointer to the previous window procedure. If this value is obtained by calling the GetWindowLong function with the nIndex parameter set to GWL_WNDPROC or DWL_DLGPROC, it is actually either the address of a window or dialog box procedure, or a handle representing that address.

    hWnd Handle to the window procedure to receive the message.

    Msg Specifies the message.

    wParam Specifies additional message-specific information. The contents of this parameter depend on the value of the Msg parameter.

    lParam Specifies additional message-specific information. The contents of this parameter depend on the value of the Msg parameter.

    Return Values The return value specifies the result of the message processing and depends on the message sent. Trimiterea unui mesaj SendMessage Trimite mesajul specificat la o fereastr sau la ferestre. Funcia asteapt terminarea execuiei codului corespunztor mesajului transmis.

    Functia PostMessage, plaseaza un mesaj in coada de mesaje a unui fir i nu asteapt terminarea prelucrarii. LRESULT SendMessage( HWND hWnd, // handle of destination window UINT Msg, // message to send WPARAM wParam, // first message parameter LPARAM lParam // second message parameter );

  • 21

    Observatie: Dac hWnd are valoarea HWND_BROADCAST, mesajul este trimis tuturor ferestrelor top-level din sistem, incluzand ferestre disabled sau invizibile, ferestre pop-up, dar nu ferestrelor copil.

    Valoarea returnata depinde de mesajul trimis. Observaie Aplicaiile ce doresc s comunice folosind HWND_BROADCAST ar trebui s foloseasc functia RegisterWindowMessage pentru a obine un mesaj unic n vederea comunicrii ntre aplicaii.

    Exemplu // int i; are o valoare corecta // HANDLE hwndCtl = handle la un control de editare // plasat intr-o caseta de dialog SendMessage(hwndctl, EM_SETSEL, (WPARAM)(INT)i, (LPARAM)(INT)(i+1)); SendMessage(hwndctl, EM_REPLACESEL, (WPARAM)(BOOL)FALSE, (LPARAM)(LPCTSTR)"");

  • 22

    n continuare sunt prezentate cteva aplicaii Windows, ce par anormale la prima vedere din cauz c nu au toate elementele descrise pn acum (bucla de mesaje, procedur fereastr, etc.). Urmrii-le! Sunt foarte interesante. Bucla de mesaje ascuns #include int WINAPI WinMain(HINSTANCE d1, HINSTANCE d2, LPSTR d3, int d4) { MessageBox(NULL, "Hello, World!", "", MB_OK); } Bucla de mesaje i procedura fereastr sunt ascunse. MessageBox afieaz o caset (box) de dialog care conine procedura fereastr i deoarece boxa de dialog este modal (nu poate fi prsit fr a se da clic pe ...) practic se cicleaz pe bucla de mesaje.

    Bucla de mesaje exist Un program windows obinuit, n timpul iniializrii, nregistreaz mai nti clasa fereastr apoi creaz fereastra principal utiliznd noua clas nregistrat. n exemplul ce urmeaz folosim deja clasa nregistrat, BUTTON.

    #include int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE d2, LPSTR d3, int d4) { MSG msg; HWND hwnd; hwnd = CreateWindow("BUTTON", "Hello, World!", WS_VISIBLE | BS_CENTER, 100, 100, 100, 80, NULL, NULL, hInstance, NULL); while (GetMessage(&msg, NULL, 0, 0)) { if (msg.message == WM_LBUTTONUP) { DestroyWindow(hwnd); PostQuitMessage(0); } DispatchMessage(&msg); } return msg.wParam; } Explicaii:

    Dup ce se creeaz fereastra, programul intr n bucla while, unde se apeleaz GetMessage. Cnd aplicaia primete un mesaj, GetMessage ntoarce acel mesaj; valoarea ntoars este FALSE numai dac mesajul primit a fost WM_QUIT.

    La tratarea mesajului WM_LBUTTONDOWN se distruge fereastra aplicaiei i apoi se pune n coda de mesaje, mesajul WM_QUIT, pentru a se realiza terminarea buclei while. Orice alt mesaj diferit de WM_LBUTTONDOWN nu este tratat de aplicaie, este preluat de DispatchMessage care va apela procedura fereastr a clasei BUTTON.

    n marea majoritate a cazurilor procedura nu execut nimic special, unul din rolurile ei fiind acela de a goli coada de mesaje a aplicaiei i de a respecta principiul n Windows nici un mesaj nu se pierde.

  • 23

    n afar de GetMessage, mai existi funcia PeekMessage care se utilizeaz de obicei cnd aplicaia dorete s execute anumite aciuni i nu are nici un mesaj de procesat.

    Proceduri fereastr #include // ---------------- Apelata pe mesajul WM_PAINT void DrawHello(HWND hwnd) { HDC hDC; PAINTSTRUCT paintStruct; RECT clientRect; hDC = BeginPaint(hwnd, &paintStruct); if (hDC != NULL) { GetClientRect(hwnd, &clientRect); DPtoLP(hDC, (LPPOINT)&clientRect, 2); DrawText(hDC, "Hello, World!", -1, &clientRect, DT_CENTER | DT_VCENTER | DT_SINGLELINE); EndPaint(hwnd, &paintStruct); } } // --------------------------- Procedura fereastra LRESULT CALLBACK WndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { switch(uMsg) { case WM_PAINT: DrawHello(hwnd); break; case WM_DESTROY: PostQuitMessage(0); break; default: return DefWindowProc(hwnd, uMsg, wParam, lParam); } return 0; // trebuie sa intoarca totdeauna 0 (zero) } // --------------- Programul principal int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR d3, int nCmdShow) { MSG msg; HWND hwnd; WNDCLASS wndClass; if (hPrevInstance == NULL) // valabil numai pentru Windows 3.1 { memset(&wndClass, 0, sizeof(wndClass));

  • 24

    // stiluri de fereastra wndClass.style = CS_HREDRAW | CS_VREDRAW; wndClass.lpfnWndProc = WndProc; // procedura fereastra wndClass.hInstance = hInstance; // instanta aplicatiei wndClass.hCursor = LoadCursor(NULL, IDC_ARROW); // resursa cursor wndClass.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1); // resursa penson wndClass.lpszClassName = "HELLO"; // nume fereastra // inregistrare fereastra if (!RegisterClass(&wndClass)) return FALSE; } // terminat if hwnd = CreateWindow("HELLO", "HELLO", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, hInstance, NULL); // Ar trebui testat daca CreateWindow s-a executat cu succes! ShowWindow(hwnd, nCmdShow); UpdateWindow(hwnd); while (GetMessage(&msg, NULL, 0, 0)) DispatchMessage(&msg); return msg.wParam; } Explicaii: 9 se creaz fereastra prin completarea structurii WNDCLASS; 9 se nregistreaz fereastra RegisterClass(&wndClass); 9 se creaz fereastra CreateWindow; 9 se stabileste modul de afiare al ferestrei ShowWindow(hwnd, nCmdShow); 9 se afieaz fereastra propriu zis UpdateWindow(hwnd); 9 urmeaz bucla de mesaje. Codul ce trebuie urmrit este cel din WndProc, procedura fereastr. Ce mesaje sunt tratate? Care sunt rspunsurile aplicaiei la aceste mesaje? WndProc trateaz dou mesaje: WM_PAINT i WM_DESTROY. Alte mesaje dect cele indicate sunt tratate de ctre DefWindowProc. La mesajul WM_DESTROY se plaseaz n coada de mesaje, mesajul WM_QUIT care are ca efect terminarea buclei de mesaje, i deci terminarea aplicaiei. La mesajul WM_PAINT se apeleaz funcia DrawHello. Dar cnd este trimis mesajul WM_PAINT i de cine? Mesajul WM_PAINT este trimis prima dat de funcia UpdateWindow, adic atunci cnd fereastra devine vizibil prima dat. ncercai opiunile Size i Move din meniul sistem. Ce se ntmpl? Trebuie reinut urmtorul lucru: dac n coada de mesaje apar mai multe mesaje WM_PAINT, sistemul va trata numai ultimul mesaj. n fapt ultima redesenare a ferestrei rmne vizibil, restul afirilor ar fi consumatoare de timp i n plus ar crea i un efect neplcut datorat rdesenrilor succesive.

  • 25

    S explicm codul din DrawHello. hDC = BeginPaint(hwnd, &paintStruct); BeginPaint ncearc s completeze variabila paintStruct i ca rspuns obine un context de dispozitiv care va trebui folosit de funciile din GDI. Ieirile grafice au nevoie de acest context de dispozitiv. GetClientRect(hwnd, &clientRect); Se obin dimensiunile zonei client, completate n clientRect. Observai parametrii funciei: hwnd va indica pentru ce fereastr se dorete acest lucru. DPtoLP(hDC, (LPPOINT)&clientRect, 2); Coordonatele fizice sunt transformate n coordonate logice. Primul parametru, hDC, indic pentru ce context de dispozitiv se face acest lucru. DrawText(hDC, "Hello, World!", -1, &clientRect, DT_CENTER | DT_VCENTER | DT_SINGLELINE); Se afieaz n zona client, Hello, World!, folosind contextul de dispozitiv obiunut de BeginPaint. EndPaint(hwnd, &paintStruct); Se termin desenarea, i se distrug informaiile din paintStruct. La terminarea funciei, hDC se distruge, fiind local. n fapt dup EndPaint hDC-ul nu mai este valid. Procedura fereastr nu este nimic altceva dect o structura mare switch.

    Mai multe bucle de mesaje i proceduri fereastr Aplicaiile pot avea cte bucle de mesaje doresc. De exemplu o aplicaie care are propria bucl de mesaje i face apel la MessageBox va avea cel puin dou bucle de mesaje.

    Pentru exemplificare vom considera cazul desenrii libere realizat cu o captur a mouse-lui. Aplicaia trebuie s fie n stare s trateze mesajele WM_LBUTTONDOWN, WM_LBUTTONUP i WM_MOUSEMOVE pentru a realiza aceast desenare. Vom avea tot timpul n minte faptul c un eveniment de mouse, n zona client, va genera un mesaj care va fi nsoit de coordonatele punctului unde acesta a avut loc. Logica aplicaiei este urmtoarea: bucla de mesaje va trata mesajul WM_LBUTTONDOWN. n cadrul funciei ce trateaz acest mesaj se va realiza capturarea mouse-lui, astfel aplicaia este informat de orice micare a mouse-lui prin tratarea mesajelor WM_MOUSEMOVE i WM_LBUTTONUP. Ieirea din cea de-a doua bucl de mesaje se face la tratarea mesajului WM_LBUTTONUP, caz n care i capturarea mouse-lui nceteaz. De reinut c n cadrul acestei a doua bucle de mesaje controlm mereu dac mouse-ul este capturat pentru zona client. Acest lucru nseamn c dac facem clic stnga n afara zonei client i inem butonul stng al mouse-lui apsat i ne micam prin zona client nu se va desena nimic. Mouse-ul nu a fost capturat de aceast fereastr. Funcii noi n acest cod. GetMessagePos() = obine coordonatele punctului unde se afl mouse-ul, coordonate relative la ecran. Coordonatele sunt obinute ntr-un DWORD, care conine n primii doi octeti valoarea lui x, iar n ultimii doi octei valoarea lui y. (Numrtoarea octeilor se face de la stnga la dreapta.) Macro-ul MAKEPOINTS transform valoarea unui DWORD ntr-o structur de tip POINTS. Cum zona client (fereastra) este plasat n cadrul ecranului, va trebui s translatm aceste coordonate n zona client.

  • 26

    ScreenToClient() = transform coordonate ecran n zona client. DPtoLP() = transform coordonatele fizice de dispozitiv n coordonate logice, necesare pentru a desena n zona client. LineTo() = deseneaz un segment de la origine (sau punctul stabilit cu MoveTo, MoveToEx) pn la punctul curent. GetCapture() = testeaz dac mouse-ul a fost capturat de fereastra aplicaiei. SetCapture(HWND ) = realizeaz capturarea mouse-ului pentru fereastra cu handler-ul specificat. ReleaseCapture() = elibereaz capturarea mouse-ului. GetDC() = obine un context de dispozitiv pentru a desena n fereastr (zona client). ReleaseDC() = elibereaz contextul de dispozitiv obinut cu GetDC. #include void AddSegmentAtMessagePos(HDC hDC, HWND hwnd, BOOL bDraw) { DWORD dwPos; POINTS points; POINT point; dwPos = GetMessagePos(); points = MAKEPOINTS(dwPos); point.x = points.x; point.y = points.y; ScreenToClient(hwnd, &point); DPtoLP(hDC, &point, 1); if (bDraw) LineTo(hDC, point.x, point.y); else MoveToEx(hDC, point.x, point.y, NULL); } void DrawHello(HWND hwnd) { HDC hDC; MSG msg; if (GetCapture() != NULL) return; hDC = GetDC(hwnd); if (hDC != NULL) { SetCapture(hwnd); AddSegmentAtMessagePos(hDC, hwnd, FALSE); while(GetMessage(&msg, NULL, 0, 0)) { if (GetCapture() != hwnd) break; switch (msg.message) { case WM_MOUSEMOVE: AddSegmentAtMessagePos(hDC, hwnd, TRUE); break; case WM_LBUTTONUP: goto ExitLoop; default: DispatchMessage(&msg); } } ExitLoop: ReleaseCapture();

  • 27

    ReleaseDC(hwnd, hDC); } } LRESULT CALLBACK WndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { switch(uMsg) { case WM_LBUTTONDOWN: DrawHello(hwnd); break; case WM_DESTROY: PostQuitMessage(0); break; default: return DefWindowProc(hwnd, uMsg, wParam, lParam); } return 0; } int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR d3, int nCmdShow) { MSG msg; HWND hwnd; WNDCLASS wndClass; if (hPrevInstance == NULL) { memset(&wndClass, 0, sizeof(wndClass)); wndClass.style = CS_HREDRAW | CS_VREDRAW; wndClass.lpfnWndProc = WndProc; wndClass.hInstance = hInstance; wndClass.hCursor = LoadCursor(NULL, IDC_ARROW); wndClass.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1); wndClass.lpszClassName = "HELLO"; if (!RegisterClass(&wndClass)) return FALSE; } hwnd = CreateWindow("HELLO", "HELLO", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, hInstance, NULL); ShowWindow(hwnd, nCmdShow); UpdateWindow(hwnd); while (GetMessage(&msg, NULL, 0, 0)) DispatchMessage(&msg); return msg.wParam; } Observaie. Mesajul WM_MOUSEMOVE poate fi tratat i n bucla de mesaje din WinMain, dar pentru a realiza aceeai funcionalitate codul i logica aplicaiei trebuiesc schimbate.

  • 28

    Concluzii 9 Fiecare aplicaie Windows este construit n jurul unei bucle de mesaje. O bucl de mesaje face apeluri

    repetate la funciile GetMessage sau PeekMessage i regsete mesajele pe care le dispecereaz procedurilor fereastr prin funcia DispatchMessage.

    9 Procedurile fereastr sunt definite pentru clasele fereastr n momemntul cnd clasa fereastr a fost nregistrat prin RegisterClass.

    9 Mesajele adresate aplicaiei sunt tratate de procedura fereastr sau sunt trimise procedurii implicite DefWindowProc sau DefDlgProc n situaia cnd nu sunt tratate de procedura fereastr.

    9 Orice mesaj windows trebuie tratat, nu trebuie pierdut. 9 Mesajele pot fi plasate sau trimise unei aplicaii. Mesajele plasate sunt depozitate n coada de unde sunt regsite

    cu GetMessage sau PeekMessage. Fa de un mesaj plasat, un mesaj trimis (SendMessage) implic imediat un apel al procedurii fereastr. Cu alte cuvinte nu se termin execuia funciei SendMessage pn cnd mesajul nu a fost tratat.

    9 O aplicaie poate avea mai multe bucle de mesaje.

  • 29

    Clase de baz in MFC

    Programarea Windows pe care ncercm s o nvm n cadrul acestui curs se bazeaz pe biblioteca de clase MFC Microsoft Foundation Classes. Fr a ntelege ns mecanismul programrii sub Windows, aceste clase nu ne ajut foarte mult. Dei exemplele ce urmeaz vor fi cu MFC, tot vom face trimeteri la SDK i n cele ce urmeaz.

    Pentru nceput vom descrie cele mai importante clase din MFC (cu care ne ntlnim in exemplele noastre) i chiar dac nu vom reine multe lucruri la prima lectur, cel puin nu ne vom speria cd vom gsi prin cod referine la aceste clase sau clase derivate.

    Observaie: O fereastr n SDK este identificat de o variabil de tip HWND. Acest lucru rmne valabil i

    n MFC, dar n general vom lucra mai mult cu pointeri la structuri ce reprezint ferestre. Data membru de tip HWND ce identific fereastra (de exemplu) va fi nglobat n obiectul ce reprezint fereastra, i de aici rezult c funciile ce aveau un parametru de tip HWND (in SDK) nu-l vor mai avea in MFC. In loc de UpdateWindow(hWnd, nCmdShow); vom ntlni un cod de forma m_pMainWnd->UpdateWindow(nCmdShow);.

    CObject #include CObject este clasa de baz principal pentru MFC. Majoritatea claselor din MFC sunt drivate din aceast clas. CObject furnizeaz urmtoarele servicii: suport de serializare; informaii despre clas n timpul execuiei; diagnosticare obiecte; compatibilitate cu clasele colecie (CArray, CList, etc.). CObject nu suporta motenirea multipl i CObject trebuie s fie cel mai din stanga ierarhiei n cazul derivrii. Dac folosim CObject atunci n clasele derivate putem beneficia de macro-urile: DECLARE_DYNAMIC i IMPLEMENT_DYNAMIC, permite accesul in timpul execuiei la numele clasei i poziia acesteia n ierarhie. DECLARE_SERIAL i IMPLEMENT_SERIAL, includ toat funcionalitatea macrourilor de mai sus, i permit unui obiect s fie serializat (scris/citit n/din arhiv). Exemplu pentru serializare class XPersoana : public CObject { pubic: // Interfata private: CString m_Nume; WORD m_Varsta; protected: virtual void Serialize(CArchive& ar); }; Implementarea funciei Serialization() pentru aceasta clas ar putea fi: void XPersoana::Serialize(CArchive& ar) { if (ar.IsStoring()) ar

  • 30

    else ar >> m_Nume >> m_Varsta; }

    Cnd avem nevoie s memorm o instan a clasei XPersoana pe disc sau s o citim din disc, vom apela funcia Serialize().

    Exemplu

    class CMyDocument : public CDocument {... XPersoana m_persoane[100]; ... }; void CMyDocument::Serialize(CArchive& ar) { for (int i=0;i XPersoana(Popescu, 20) -> XManager(Zetu,20) -> XPersoana(Zoe,12) -> etc. Deci nu mai avem un proces simplu de serializare. Adaugand macro-ul DECLARE_SERIAL in definitia clasei si IMPLEMENT_SERIAL in implementarea clasei, un pointer la o instan a clasei poate fi memorat i realocat dintr-o arhiv. In concluzie implementarea completa pentru aceasta clasa este: class XPersoana : public CObject { public:

  • 31

    // Interfata private: CString m_Nume; WORD m_Varsta; protected: virtual void Serialize(CArchive& ar); DECLARE_SERIAL(XPersoana) }; class XManager : public XPersoana { public: // Interfata private: CList m_subordonati; protected: void Serialize(CArchive& ar); DECLARE_SERIAL(XManager) }; IMPLEMENT_SERIAL(XPersoana, CObject, 1) IMPLEMENT_SERIAL(XManager, XPersoana, 1) // // Aceasta este o functie helper pentru clasa colectie // template CList si ne spune cum memoreaza obiecte // de tipul XPersoana* // void SerializeElements(CArchive& ar, XPersoana** pElemente, int nCount) { for (int i=0;i < nCount; i++) { if (ar.IsStoring()) ar > pElemente[i]; } } void XPersoana::Serialize(CArchive& ar) { if (ar.IsStoring()) ar m_Nume >> m_Varsta; } void XManager::Serialize(CArchive& ar) { XPersoana::Serialize(ar); m_subordonati.Serialize(ar); } void CMyDocument::Serialize(CArchive& ar) { m_persoane.Serialize(ar); }

  • 32

    CCmdTarget CCmdTarget este clasa de baz pentru arhitectura de tratare a mesajelor din biblioteca MFC. Dac se dorete crearea unei noi clase ce trebuie s trateze mesaje, aceasta trebuie derivat din CCmdTarget sau dintr-un descendent al acesteia. Metoda OnCmdMsg ( ) este folosit pentru rutarea, distribuirea mesajelor i tratarea acestora. n plus clasa CCmdTarget mai gestioneaz trecerea cursorului n starea de ateptare (cursor cu clepsidr) i ieirea din aceast stare folosind metodele BeginWaitCursor ( ), EndWaitCursor ( ) i RestoreWaitCursor ( ). Clase derivate din CCmdTarget: CView, CWinApp, CDocument, CWnd si CFrameWnd. Pentru a lucra cu comenzi va trebui sa completam harta de mesaje (se face corespondenta intre mesaj si functia ce-l trateaza) iar in majoritatea cazurilor acest lucru este facut de ClassWizard. Bine-nteles codul din functia ce trateaza mesajul trebuie scris de noi. In general mesajele sunt trimise ferestrei cadru principale, dar comenzile sunt rutate apoi catre alte obiecte. In mod normal o clasa ruteaza comenzile la alte obiecte pentru a le da sansa sa trateze comanda. Daca comanda nu este trata de nici un obiect atunci se cauta in harta de mesaje a clasei pentru a vedea daca mesajul are asociata o functie de tratare. In situatia cind clasa nu trateaza comanda, aceasta este rutata catre clasa de baza a clasei curente. Vom explica pe larg aceasta rutare intr-un curs viitor. Urmatorul exemplu este din MSDN. Se explica sintaxa metodei OnCmdMsg i apoi se d un exemplu. CCmdTarget::OnCmdMsg virtual BOOL OnCmdMsg( UINT nID, int nCode, void* pExtra, AFX_CMDHANDLERINFO* pHandlerInfo ); Return Value Nonzero if the message is handled; otherwise 0. Parameters nID Contains the command ID. nCode Identifies the command notification code. pExtra Used according to the value of nCode. pHandlerInfo If not NULL, OnCmdMsg fills in the pTarget and pmf members of the pHandlerInfo structure instead of dispatching the command. Typically, this parameter should be NULL. Remarks Called by the framework to route and dispatch command messages and to handle the update of command user-interface objects. This is the main implementation routine of the framework command architecture. At run time, OnCmdMsg dispatches a command to other objects or handles the command itself by calling the root class CCmdTarget::OnCmdMsg, which does the actual message-map lookup. Observatie: 1. In concluzie, aceast funcie este apelata de cadrul de lucru (framework), asta nsemnnd c nu vom gsi n cod un apel explicit la aceast funcie. Funcia este folosit pentru a ruta mesajele de comand. 2. Fiecare comand are un ID (identificator) de exemplu IDM_FILE_NEW, poate fi ID-ul pentru comanda de meniu, File->New. Example // This example illustrates extending the framework's standard command // route from the view to objects managed by the view. This example // is from an object-oriented drawing application, similar to the // DRAWCLI sample application, which draws and edits "shapes".

  • 33

    BOOL CMyView::OnCmdMsg(UINT nID, int nCode, void* pExtra, AFX_CMDHANDLERINFO* pHandlerInfo) { // Extend the framework's command route from the view to // the application-specific CMyShape that is currently selected // in the view. m_pActiveShape is NULL if no shape object // is currently selected in the view. if ((m_pActiveShape != NULL) && m_pActiveShape->OnCmdMsg(nID, nCode, pExtra, pHandlerInfo)) return TRUE; // If the object(s) in the extended command route don't handle // the command, then let the base class OnCmdMsg handle it. return CView::OnCmdMsg(nID, nCode, pExtra, pHandlerInfo); } // The command handler for ID_SHAPE_COLOR (menu command to change // the color of the currently selected shape) was added to // the message map of CMyShape (note, not CMyView) using ClassWizard. // The menu item will be automatically enabled or disabled, depending // on whether a CMyShape is currently selected in the view, that is, // depending on whether CMyView::m_pActiveView is NULL. It is not // necessary to implement an ON_UPDATE_COMMAND_UI handler to enable // or disable the menu item. BEGIN_MESSAGE_MAP(CMyShape, CCmdTarget) //{{AFX_MSG_MAP(CMyShape) ON_COMMAND(ID_SHAPE_COLOR, OnShapeColor) //}}AFX_MSG_MAP END_MESSAGE_MAP() CWinThread Un obiect din clasa CWinThread reprezint un fir de execuie dintr-o aplicaie. Firul de execuie principal este un obiect al clasei CWinApp ce este o clas derivat din CWinThread. Pe lng firul de execuie principal se mai pot folosi i alte fire de execuie, folosind obiecte CWinThread. Exista doua tipuri de fire de executie suportate de CWinThread:

    1. fire de lucru (fara interfata utilizator, deci nu au bucla de mesage); 2. fire cu interfata utilizator.

    O problema important legat de firele de execuie o constituie sincronizarea acestora (executie sincronizat).

    Pentru o documentare asupra mebrilor clasei CWinThread consultati MSDN. CWinApp Derivat din CWinThread Clasa CWinApp este clasa de baz pentru obiectul aplicaie. Clasa aplicaie din MFC ncapsuleaz iniializarea, execuia i terminarea unei aplicaii Windows.

  • 34

    Fiecare aplicaie MFC poate conine doar un singur obiect derivat din CWinApp. Acest obiect este global i este construit naintea construirii ferestrelor, fiind disponibil atunci cnd sistemul de operare Windows apeleaz funcia WinMain, care este oferit de biblioteca MFC. Cnd se deriveaz o clas aplicaie din CWinApp, se suprascrie funcia membru InitInstance ( ) pentru a se construi i iniializa noua aplicaie. Pe lng funciile membru ale lui CWinApp, MFC ofer funcii globale pentru a obine informaii despre obiectul aplicaie curent:

    AfxGetApp ( ) returneaz un pointer la obiectul aplicaie curent; AfxGetInstanceHandle ( ) handle la instana aplicaie curent; AfxGetResourceHandle ( ) handle la resursele aplicaiei; AfxGetAppName ( ) numele aplicaiei.

    Exemplu class Cmfc1App : public CWinApp { public: Cmfc1App(); // Overrides public: virtual BOOL InitInstance(); // Implementation afx_msg void OnAppAbout(); DECLARE_MESSAGE_MAP() }; iar in implementare BOOL Cmfc1App::InitInstance() { // InitCommonControls() is required on Windows XP if an application // manifest specifies use of ComCtl32.dll version 6 or later to enable // visual styles. Otherwise, any window creation will fail. InitCommonControls(); CWinApp::InitInstance(); // Initialize OLE libraries if (!AfxOleInit()) { AfxMessageBox(IDP_OLE_INIT_FAILED); return FALSE; } AfxEnableControlContainer(); // Standard initialization // If you are not using these features and wish to reduce the size // of your final executable, you should remove from the following // the specific initialization routines you do not need // Change the registry key under which our settings are stored // TODO: You should modify this string to be something appropriate

  • 35

    // such as the name of your company or organization SetRegistryKey(_T("Local AppWizard-Generated Applications")); LoadStdProfileSettings(4); // Load standard INI file options (including MRU) // Register the application's document templates. Document templates // serve as the connection between documents, frame windows and views CSingleDocTemplate* pDocTemplate; pDocTemplate = new CSingleDocTemplate( IDR_MAINFRAME, RUNTIME_CLASS(Cmfc1Doc), RUNTIME_CLASS(CMainFrame), // main SDI frame window RUNTIME_CLASS(Cmfc1View)); if (!pDocTemplate) return FALSE; AddDocTemplate(pDocTemplate); // Parse command line for standard shell commands, DDE, file open CCommandLineInfo cmdInfo; ParseCommandLine(cmdInfo); // Dispatch commands specified on the command line. Will return FALSE if // app was launched with /RegServer, /Register, /Unregserver or /Unregister. if (!ProcessShellCommand(cmdInfo)) return FALSE; // The one and only window has been initialized, so show and update it m_pMainWnd->ShowWindow(SW_SHOW); m_pMainWnd->UpdateWindow(); // call DragAcceptFiles only if there's a suffix // In an SDI app, this should occur after ProcessShellCommand return TRUE; } CWnd Clasa CWnd este clasa de baz pentru toate celelalte clase fereastr oferite de MFC. Aceast clas are o mare parte din funciile necesare n obiectele frame, view, controale, etc. Obiectul fereastr este un obiect al clasei CWnd sau derivat din clasa CWnd care este creat direct de ctre program. Fereastra, pe de alt parte este un handle ctre o structur intern din Windows care conine resursele ferestrei respective. CFrameWnd Clasa CFrameWnd conine o implementare implicit pentru urmtoarele funcii ale ferestrei principale dintr-o aplicaie Windows:

    menine ordinea cu ferestrele de vizualizare active; comenzile i mesajele de notificare le trimite ferestrei de vizualizare active; modificarea textului ce apare pe bara de titlu a ferestrei n funcie de fereastra de vizualizare activ; se ocup de poziionarea barelor de control, a ferestrelor de vizualizare i a altor ferestre copil, n

    zona client a ferestrei principale; bara de meniu; meniul sistem al aplicaiei;

  • 36

    acceleratori (tratarea tastelor speciale); starea iniial a aplicaiei (minimizat, maximizat); help senzitiv la context; bara de stare; se ocup de perechile document-view.

    Exemplu class CMainFrame : public CFrameWnd { protected: // create from serialization only CMainFrame(); DECLARE_DYNCREATE(CMainFrame) // Attributes public: // Operations public: // Overrides public: virtual BOOL PreCreateWindow(CREATESTRUCT& cs); // Implementation public: virtual ~CMainFrame(); #ifdef _DEBUG virtual void AssertValid() const; virtual void Dump(CDumpContext& dc) const; #endif protected: // control bar embedded members CStatusBar m_wndStatusBar; CToolBar m_wndToolBar; // Generated message map functions protected: afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct); DECLARE_MESSAGE_MAP() }; iar in implementare avem (cod partial pentru crearea ferestrei) int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct) { if (CFrameWnd::OnCreate(lpCreateStruct) == -1) return -1; if (!m_wndToolBar.CreateEx(this, TBSTYLE_FLAT, WS_CHILD | WS_VISIBLE | CBRS_TOP | CBRS_GRIPPER | CBRS_TOOLTIPS | CBRS_FLYBY | CBRS_SIZE_DYNAMIC) || !m_wndToolBar.LoadToolBar(IDR_MAINFRAME))

  • 37

    { TRACE0("Failed to create toolbar\n"); return -1; // fail to create } if (!m_wndStatusBar.Create(this) || !m_wndStatusBar.SetIndicators(indicators, sizeof(indicators)/sizeof(UINT))) { TRACE0("Failed to create status bar\n"); return -1; // fail to create } // TODO: Delete these three lines if you don't want // the toolbar to be dockable m_wndToolBar.EnableDocking(CBRS_ALIGN_ANY); EnableDocking(CBRS_ALIGN_ANY); DockControlBar(&m_wndToolBar); return 0; } BOOL CMainFrame::PreCreateWindow(CREATESTRUCT& cs) { if( !CFrameWnd::PreCreateWindow(cs) ) return FALSE; // TODO: Modify the Window class or styles here by modifying // the CREATESTRUCT cs return TRUE; } Structura CREATESTRUCT este asemntoare cu structura WNDCLASSEX. typedef struct tagCREATESTRUCT { LPVOID lpCreateParams; HANDLE hInstance; HMENU hMenu; HWND hwndParent; int cy; int cx; int y; int x; LONG style; LPCSTR lpszName; LPCSTR lpszClass; DWORD dwExStyle; } CREATESTRUCT;

  • 38

    Document templates - abloane de document

    Aplicaiile MFC folosesc implicit un model de programare ce separ datele programului de partea de afiare a acestor date i de majoritatea interaciunilor dintre utilizator i date. n acest model, un obiect document citete i scrie datele pe disc i mai poate oferi nite funcii de lucru cu aceste date.

    Un obiect distinct se ocup de partea de vizualizare a datelor ntr-o anumit fereastr i trateaz interaciunea utilizatorului cu acestea. Obiectul de vizualizare poate citi datele din document i le poate modifica la aciunea utilizatorului.

    Modelul document/view este aplicabil i n situaia n care exist mai multe ferestre de vizualizare pentru un document, lsnd libertatea fiecrui obiect de vizualizare s-i afieze datele, n timp ce partea de cod comun tuturor ferestrelor de vizualizare (cum ar fi partea de calcule) se poate implementa n document.

    Documentul se mai ocup i de reactualizarea ferestrelor de vizualizare dac datele au fost modificate (de ctre document sau de ctre una din ferestrele de vizualizare).

    Arhitectura MFC document/view suport o implementare uoar a ferestrelor de vizualizare multiple, tipuri de documente diferite, ferestre mprite (splitter windows), i alte tipuri de caracteristici ale interfeelor. La baza modelului document/view stau urmtoarele patru clase:

    CDocument (sau COleDocument); CView; CFrameWnd; CDocTemplate.

    Prile din MFC cele mai vizibile, att pentru utilizator ct i pentru programator, sunt documentul i ferestrele

    de vizualizare. Cea mai mare parte din munca investit ntr-un proiect const n scrierea claselor document i view. Clasa CDocument ofer funciile de baz pentru clasele document specifice aplicaiei. Un document

    reprezint un bloc de date pe care utilizatorul l poate deschide cu comanda Open, salva cu comanda Save, etc. Clasa CView st la baza claselor view ale aplicaiei. Un view este ataat unui document i funcioneaz ca

    un intermediar ntre document i utilizator: view-ul construiete n fereastr o imagine a documentului i interpreteaz aciunile utilizatorului i le transmite documentului. n figura urmtoare este prezentat relaia dintre document i view:

    Documentele, ferestrele de vizualizare asociate i ferestrele cadru care conin ferestrele de vizualizare pentru un document sunt create de un template document. Acesta este responsabil de crearea i gestionarea tuturor documentelor de acelai tip. Orice aplicaie MFC SDI creat cu AppWizard are cel puin un document template. Acest template creaz i definete relaiile dintre document, fereastra cadru i fereastra de vizualizare. Cnd este creat un nou element sau cnd este deschis un document, acest template este folosit pentru a crea cele trei elemente n urmtoarea ordine: documentul, fereastra cadru i fereastra de vizualizare. Pentru o aplicaie MDI mai apare un pas n plus fa de aplicaia SDI: crearea ferestrei cadru principale a aplicaiei naintea crerii documentului. Template-ul document mapeaz documentul, fereastra cadru i fereastra de vizualizare pe clasele proprii aplicaiei. Crearea unui template (creat implicit de AppWizard):

    CSingleDocTemplate* pDocTemplate; pDocTemplate = new CSingleDocTemplate( IDR_MAINFRAME, RUNTIME_CLASS(CSdiDoc), RUNTIME_CLASS(CMainFrame), // main SDI frame window RUNTIME_CLASS(CSdiView)); AddDocTemplate(pDocTemplate);

    Secvena de apeluri la crearea documentului:

    CMyApp::InitInstance ( ); dac aplicaia este MDI atunci se creaz i afieaz fereastra cadru principal; parcurge i proceseaz linia de comand (dac nu exist se apeleaz OnFileNew ( )); se selecteaz template-ul document;

  • 39

    se creaz un document gol prin CDocTemplate::OpenDocumentFile ( ); CDocTemplate::CreateNewDoc ( ); constructorul pentru CMyDocument; CMyDocument::OnNewDocument ( ).

    Crearea ferestrei cadru: CDocTemplate::CreateNewFrame ( ) constructorul pentru CMainFrame; CMainFrame::LoadFrame ( ); CMainFrame::PreCreateWindow ( ); CMainFrame::OnCreate ( ); CMainFrame::OnCreateClient ( );

    Crearea ferestrei de vizualizare: CMainFrame::CreateView ( ); constructorul pentru CMyView; CMyView::PreCreateWindow ( ); CMainFrame::InitialUpdateFrame ( ); CMyView::OnInitialUpdate ( ); CMyView::OnActivateFrame ( ); CMainFrame::ActivateFrame ( ); CMyView::OnActivateView ( );

    CDocument Clasa CDocument furnizeaza functionalitatea de baza in arhitectura document-view implementata in MFC

    si are ca scop gestionarea documentului aplicatiei. CDocument suporta operatii standard de creare, incarcare si salvare a documentului. O aplicatie poate suporta mai mult decat un document. Fiecare tip are asociat un document template

    (sablon). Acest sablon specifica resursele utilizate pentru acel tip de document. Utilizatorul interactioneaza cu un document prin intermediul unui obiect CView. Un obiect CView

    reprezinta o vizualizare a documentului in zona client. Un document poate avea asociate mai multe vizualizari. Un document primeste comenzi forward-ate de vizualizarea activa precum si comenzi din meniu (Open, Save).

    CView Clasa CView (obiecte instantiate direct sau indirect) furnizeaza vizualizarea documentului. O vedere

    actioneaza ca un intermediar intre document si utilizator. O vedere este o fereastra descendenta din fereastra cadru.O vedere este responsabila de tratarea mai multor tipuri de intrari: tastatura, mouse, operatii de drag & drop, comenzi din meniu, toobar sau bare de defilare (scroll bars).

    Metoda cea mai importanta din aceasta clasa este OnDraw, responsabila pentru desenarea in zona client. O alta metoda folosita este cea care face legatura intre document si vizualizare: GetDocument(), functie ce

    returneaza un pointer la obiectul de tip document. Alte clase de vizuallizare: CCtrlView, CDaoRecordView, CEditView, CFormView, CListView, CRecordView, CRichEditView, CScrollView, CTreeView.

  • 40

    Harta de mesaje

    Hrile de mesaje sunt pri ale modelului MFC de programare Windows. n loc de a scrie funcia WinMain() care trimite mesaje la procedura fereastr (funcia) WindProc() i apoi s controlm ce mesaj a fost trimis pentru a activa funcia corespunztoare, vom scrie doar funcia care trateaz mesajul i vom aduga mesajul la harta de mesaje a clasei. Cadrul de lucru va face operaiunile necesare pentru a ruta acest mesaj n mod corect. Construirea hrii de mesaje

    Hrile de mesaje se construiesc n dou etape. Declararea hrtii de mesaje (macro DECLARE_MESSAGE_MAP()) se face n fiierul .h al clasei, iar implementarea se face in fiierul .cpp al clasei (BEGIN_MESSAGE_MAP() ... END_MESSAGE_MAP()). Exemplu //{{AFX_MSG(CShowStringApp) afx_msg void OnAppAbout(); //the ClassWizard will add and remove member functions here. // DO NOT EDIT what you see in these blocks of generated code ! //}}AFX_MSG DECLARE_MESSAGE_MAP() Se declar funcia OnAppAbout() care este prefixat cu afx_msg ce constituie un comentariu pentru compilatorul de C++, dar care indic vizual c aceast funcie trateaz un mesaj. Aceast funcie o vom gsi i n cadrul macro-ului BEGIN_MESSAGE_MAP(), ca un parametru al macro-ului ON_COMMAND(). Primul parametru al acestui din urm macro este ID-ul mesajului (comenzii n acest caz), iar al doilea numele funciei ce trateaz acest mesaj. Cod in .cpp BEGIN_MESSAGE_MAP(CShowStringApp, CWinApp) //{{AFX_MSG_MAP(CShowStringApp) ON_COMMAND(ID_APP_ABOUT, OnAppAbout) //DO NOT EDIT what you see in these blocks of generated code! //}}AFX_MSG_MAP // Standard file based document commands ON_COMMAND(ID_FILE_NEW, CWinApp::OnFileNew) ON_COMMAND(ID_FILE_OPEN, CWinApp::OnFileOpen) ON_COMMAND(ID_FILE_PRINT_SETUP, CWinApp::OnFilePrintSetup) END_MESSAGE_MAP()

    Macro-ul DECLARE_MESSAGE_MAP() adaug date mebru i funcii la clasa respectiv. Practic se declar o tabel cu un numr de intrri variabil (sfritul tabelei este marcat (completat) de END_MESSAGE_MAP()) i funcii care opereaz cu acest elementele acestui tabel.

    Macro-uri pentru harta de mesaje BEGIN_MESSAGE_MAP i END_MESSAGE_MAP sunt macro-uri care ca i macro-ul DECLARE_MESSAGE_MAP din fiierul .h, declar anumite variabile membru i funcii pe care cadrul de lucru le va utiliza pentru a naviga prin hrile de mesaje ale tuturor obiectelor din sistem. Dintre macro-urile folosite n hrile de mesaje, enumerm:

    DECLARE_MESSAGE_MAPfolosit n fiierul .h pentru a declara c va exista o hartesaje in .cpp BEGIN_MESSAGE_MAPMarcheaz nceputul hrii de mesaje n fiierul surs. END_MESSAGE_MAPMarcheaz sfritul hrii de mesaje n fiierul surs. ON_COMMANDFolosit pentru a face legtura ntre comenzi i funciile care trateaz aceste comenzi.

  • 41

    ON_COMMAND_RANGEFolosit pentru a face legtura ntre un grup de comenzi i funcia care le trateaz.

    ON_CONTROLFolosit pentru a face legtura ntre un mesaj de notificare al unui control i funcia ce-l trateaz.

    ON_CONTROL_RANGEFolosit pentru a face legtura ntre un grup de mesaje de notificare al unui control i funcia corespunztoare.

    ON_MESSAGEFolosit pentru a realiza legtura ntre un mesaj definit de utilizator i funcia care-l trateaz.

    ON_REGISTERED_MESSAGEFolosit pentru a realiza legtura ntre un mesaj defint de utilizator, dar nregistrat i funcia care-l trateaz.

    ON_UPDATE_COMMAND_UIFolosit pentru a indica funcia care va face actualizarea pentru o comand specific.

    ON_COMMAND_UPDATE_UI_RANGECa mai sus, dar pentru un grup de comenzi. ON_NOTIFYFolosit pentru a indica funcia ce va aduga informaii suplimentare, pentru un mesaj de

    notificare al unui control. ON_NOTIFY_RANGECa mai sus, dar pentru un grup de mesaje de notificare al unui control.

    ON_NOTIFY_EXCa la ON_NOTIFY, dar funcia va ntoarce TRUE sau FALSE pentru a indica dac notificarea poate fi trecut altui obiect pentru tratri suplimentare.

    ON_NOTIFY_EX_RANGECa mai sus, dar se refer la un grup de comenzi de notificare. n plus la aceste macro-uri, exist peste 100 de macro-uri, unul pentru fiecare din cele mai comune mesaje. De

    exemplu macro-ul ON_CREATE pentru mesajul WM_CREATE, etc. n mod obinuit aceste macro-uri sunt adugate la clas de ctre ClassWizard.

    Cum lucreaz harta de mesaje ? Fiecare aplicaie are un obiect motenit din clasa CWinApp i o funcie membru Run(). Aceast funcie

    apeleaz funcia CWinThread::Run(), care apeleaz GetMessage(), TranslateMessage() i DispatchMessage().

    Funcia fereastr (n SDK) tie handler-ul ferestrei pentru care este trimis mesajul. Fiecare obiect fereastr folosete acelai stil al clasei Windows i aceeai funcie WindProc, numit AfxWndProc(). MFC menine ceva asemntor, numit handle map, o tabel cu handler-ii ferestrelor i pointeri la obiecte, i framework-ul folosete aceasta pentru a trimite un pointer la obiectul C++, un CWnd*. n continuare el apeleaz WindowProc(), o funcie virtual a acestui obiect. Datorit