Bucla de mesaje
"ascunsa"
#include <windows.h>
int WINAPI WinMain(HINSTANCE d1, HINSTANCE d2, LPSTR d3, int d4)
Bucla de mesaje si procedura fereastra sunt ascunse. MessageBox afiseaza o boxa de dialog care contine procedura fereastra si deoarece boxa de dialog este modala (nu poate fi parasita fara a se da clic pe ...) practic se cicleaza pe bucla de mesaje.
Bucla de mesaje exista
Un program windows obisnui 141c22b t, în timpul initializarii, înregistreaza mai întâi clasa fereastra apoi creaza fereastra principala utilizând noua clasa înregistrata. În exemplul ce urmeaza folosim deja clasa înregistrata, BUTTON.
#include <windows.h>
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE d2, LPSTR d3, int d4)
DispatchMessage(&msg);
}
return msg.wParam;
}
Explicatii: Dupa ce se creeaza fereastra, programul intra în bucla while, unde se apeleaza GetMessage. Când aplicatia primeste un mesaj, GetMessage întoarce acel mesaj; valoarea întoarsa este FALSE numai daca mesajul primit a fost WM_QUIT.
La tratarea mesajului WM_LBUTTONDOWN se distruge fereastra aplicatiei si 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 aplicatie, este preluat de DispatchMessage care va apela procedura fereastra a clasei BUTTON. În marea majoritate a cazurilor procedura nu executa nimic special, unul din rolurile ei fiind acela de a goli coada de mesaje a aplicatiei si de a respecta principiul "în Windows nici un mesaj nu se pierde".
În afara de GetMessage, mai existasi functia PeekMessage care se utilizeaza de obicei când aplicatia doreste sa execute anumite actiuni si nu are nici un mesaj de procesat.
Proceduri fereastra
#include <windows.h>
// ----- ----- ------ Apelata pe mesajul WM_PAINT
void DrawHello(HWND hwnd)
}
// ----- ----- ----------------- Procedura fereastra
LRESULT CALLBACK WndProc(HWND hwnd, UINT uMsg,
WPARAM wParam, LPARAM lParam)
return 0; // trebuie sa intoarca totdeauna 0 (zero)
}
// ----- ----- ----- Programul principal
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPSTR d3, int nCmdShow)
// 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;
}
Explicatii:
se creaza fereastra prin completarea structurii WNDCLASS;
se înregistreaza fereastra RegisterClass(&wndClass);
se creaza fereastra CreateWindow;
se stabileste modul de afisare al ferestrei ShowWindow(hwnd, nCmdShow);
se afiseaza fereastra propriu zisa UpdateWindow(hwnd);
urmeaza bucla de mesaje.
Codul ce trebuie urmarit este cel din WndProc, procedura fereastra.
Ce mesaje sunt tratate? Care sunt raspunsurile aplicatiei la aceste mesaje?
WndProc trateaza doua mesaje: WM_PAINT si WM_DESTROY. Alte mesaje decât cele indicate sunt tratate de catre DefWindowProc.
La mesajul WM_DESTROY se plaseaza în coada de mesaje, mesajul WM_QUIT care are ca efect terminarea buclei de mesaje, si deci terminarea aplicatiei.
La mesajul WM_PAINT se apeleaza functia DrawHello.
Dar când este trimis mesajul WM_PAINT si de cine?
Mesajul WM_PAINT este trimis prima data de functia UpdateWindow, adica atunci când fereastra devine vizibila prima data. Încercati optiunile Size si Move din meniul sistem. Ce se întâmpla? Trebuie retinut urmatorul lucru: daca în coada de mesaje apar mai multe mesaje WM_PAINT, sistemul va trata numai ultimul mesaj. În fapt ultima redesenare a ferestrei ramâne vizibila, restul afisarilor ar fi consumatoare de timp si în plus ar crea si un efect neplacut datorat rdesenarilor succesive.
Sa explicam codul din DrawHello.
hDC = BeginPaint(hwnd, &paintStruct);
BeginPaint încearca sa completeze variabila paintStruct si ca raspuns obtine un context de dispozitiv care va trebui folosit de functiile din GDI. Iesirile grafice au nevoie de acest context de dispozitiv.
GetClientRect(hwnd, &clientRect);
Se obtin dimensiunile zonei client, completate în clientRect Observati parametrii functiei: hwnd va indica pentru ce fereastra se doreste acest lucru.
DPtoLP(hDC, (LPPOINT)&clientRect, 2);
Coordonatele fizice sunt transformate în coordonate logice. Primul parametru, hDC, indica pentru ce context de dispozitiv se face acest lucru.
DrawText(hDC, "Hello, World!", -1, &clientRect,
DT_CENTER | DT_VCENTER | DT_SINGLELINE);
Se afiseaza în zona client, "Hello, World!", folosind contextul de dispozitiv obtiunut de BeginPaint
EndPaint(hwnd, &paintStruct);
Se termina desenarea, si se distrug informatiile din paintStruct
La terminarea functiei, hDC se distruge, fiind local. În fapt dupa EndPaint hDC-ul nu mai este valid.
Procedura fereastra nu este nimic altceva decât o structura mare switch.
Mai multe bucle de mesaje
si proceduri fereastra
Aplicatiile pot avea câte bucle de mesaje doresc. De exemplu o aplicatie care are propria bucla de mesaje si face apel la MessageBox va avea cel putin doua bucle de mesaje.
Pentru exemplificare vom considera cazul desenarii libere realizat cu o captura a mouse-lui. Aplicatia trebuie sa fie în stare sa trateze mesajele WM_LBUTTONDOWN, WM_LBUTTONUP si WM_MOUSEMOVE pentru a realiza aceasta desenare. Vom avea tot timpul în minte faptul ca un eveniment de mouse, în zona client va genera un mesaj care va fi însotit de coordonatele punctului unde acesta a avut loc.
Logica aplicatiei este urmatoarea: bucla de mesaje va trata mesajul WM_LBUTTONDOWN. În cadrul functiei ce trateaza acest mesaj se va realiza capturarea mouse-lui, astfel aplicatia este informata de orice miscare a mouse-lui prin tratarea mesajelor WM_MOUSEMOVE si WM_LBUTTONUP. Iesirea din cea de-a doua bucla de mesaje se face la tratarea mesajului WM_LBUTTONUP, caz în care si capturarea mouse-lui înceteaza. De retinut ca în cadrul acestei a doua bucle de mesaje controlam mereu daca mouse-ul este capturat pentru zona client. Acest lucru înseamna ca daca facem clic stânga în afara zonei client si tinem butonul stâng al mouse-lui apasat si ne miscam prin zona client nu se va desena nimic. Mouse-ul nu a fost capturat de aceasta fereastra.
Functii noi în acest cod.
GetMessagePos() = obtine coordonatele punctului unde se afla mouse-ul, coordonate relative la ecran. Coordonatele sunt obtinute într-un DWORD, care contine în primii doi octeti valoarea lui x, iar în ultimii doi octeti valoarea lui y. (Numaratoarea octetilor se face de la stânga la dreapta.)
Macro-ul MAKEPOINTS transforma valoarea unui DWORD într-o structura de tip POINTS.
Cum zona client (fereastra) este plasata în cadrul ecranului, va trebui sa translatam aceste coordonate în zona client.
ScreenToClient() = transforma coordonate ecran în zona client.
DPtoLP() = transforma coordonatele fizice de dispozitiv în coordonate logice, necesare pentru a desena în zona client.
LineTo() = deseneaza un segment de la origine (sau punctul stabilit cu MoveTo, MoveToEx) pâna la punctul curent.
GetCapture() = testeaza daca mouse-ul a fost capturat de fereastra aplicatiei.
SetCapture(HWND ) = realizeaza capturarea mouse-ului pentru fereastra cu handler-ul specificat
ReleaseCapture() = elibereaza capturarea mouse-ului.
GetDC() = obtine un context de dispozitiv pentru a desena în fereastra (zona client).
ReleaseDC() = elibereaza contextul de dispozitiv obtinut cu GetDC.
#include <windows.h>
void AddSegmentAtMessagePos(HDC hDC, HWND hwnd, BOOL bDraw)
void DrawHello(HWND hwnd)
}
ExitLoop:
ReleaseCapture();
ReleaseDC(hwnd, hDC);
}
}
LRESULT CALLBACK WndProc(HWND hwnd, UINT uMsg,
WPARAM wParam, LPARAM lParam)
return 0;
}
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPSTR d3, int nCmdShow)
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;
}
Observatie. Mesajul WM_MOUSEMOVE poate fi tratat si în bucla de mesaje din WinMain, dar pentru a realiza aceeasi functionalitate codul si logica aplicatiei trebuiesc schimbate.
Concluzii
Fiecare aplicatie Windows este construita în jurul unei bucle de mesaje. O bucla de mesaje face apeluri repetate la functiile GetMessage sau PeekMessage si regaseste mesajele pe care le dispecereaza procedurilor fereastra prin functia DispatchMessage.
Procedurile fereastra sunt definite pentru clasele fereastra în momemntul când clasa fereastra a fost înregistrata prin RegisterClass.
Mesajele adresate aplicatiei sunt tratate de procedura fereastra sau sunt trimise procedurii implicite DefWindowProc sau DefDlgProc în situatia când nu sunt tratate de procedura fereastra.
Orice mesaj windows trebuie tratat, nu trebuie pierdut.
Mesajele pot fi plasate sau trimise unei aplicatii. Mesajele plasate sunt depozitate în coada de unde sunt regasite cu GetMessage sau PeekMessage. Fata de un mesaj plasat, un mesaj trimis (SendMessage) implica imediat un apel al procedurii fereastra. Cu alte cuvinte nu se termina executia functiei SendMessage pâna când mesajul nu a fost tratat.
O aplicatie poate avea mai multe bucle de mesaje.
|