Sunteți pe pagina 1din 7

Bucla de mesaje ascuns

#include <windows.h> 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 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 <windows.h> 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. 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 <windows.h> // ---------------- 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)); // 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); ShowWindow(hwnd, nCmdShow); UpdateWindow(hwnd); while (GetMessage(&msg, NULL, 0, 0)) DispatchMessage(&msg); return msg.wParam;

} Explicaii: se creaz fereastra prin completarea structurii WNDCLASS; se nregistreaz fereastra RegisterClass(&wndClass); se creaz fereastra CreateWindow; se stabileste modul de afiare al ferestrei ShowWindow(hwnd, nCmdShow); se afieaz fereastra propriu zis UpdateWindow(hwnd); 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. 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. 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 <windows.h> 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(); 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.

Concluzii

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. Procedurile fereastr sunt definite pentru clasele fereastr n momemntul cnd clasa fereastr a fost nregistrat prin RegisterClass. 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. Orice mesaj windows trebuie tratat, nu trebuie pierdut. 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. O aplicaie poate avea mai multe bucle de mesaje.