20. fejezet - Winapi forevah


Valószínüleg a legtöbb programozót a hányinger kerülgeti ha a windows API-ról van szó, különösen mert a .NET kényelmének árnyékában miért nyúlna az ember ehhez az elavult, ronda, logikátlan, idióta, szerencsétlen, <egyéb featureök> felülethez? Mondjuk mert a cég ahol dolgozol ebben írta meg a programot, és bármennyire is toporzékolsz, nem fogják átírni az egészet csak azért mert neked herótod van a windowstól.

WinAPI, na!

Mindenki elolvassa a wikipédián, hogy mikböl is áll a winapi. A két legfontosabb dolog a GDI (rajzolgatás a grafkártyával) és az UI (ablakok, controlok, stb.). Amit még használni szokás, az a shell modul, például processz indításhoz illetve a base/advanced services ha mondjuk fel akarod sorolni, hogy milyen fájlok vannak egy directoryban.

Én itt most csak létre fogok hozni egy üres ablakot, hogy mindenki lássa mennyire...khm...fantasztikus a windows. API. Amit rögtön elfelejthetsz, az a main függvény. Na jó, nem muszáj elfelejteni, de akkor ott fog virítani a konzolablak is a progi mellett. Visual Studio-ban ugyanúgy console application-t kell létrehozni, de ha nem akarod látni a konzolt a console application-ben, akkor a Linker -> System ablakban a Subsystem-et állítsd át Windows-ra.

Nadehát akkor, hol fog belépni a program? Természetesen a WinMain-ben!

CODE
INT WINAPI WinMain(HINSTANCE hInst, HINSTANCE hPrevInst, LPSTR lpCmdLine, INT nShowCmd) { return 0; }

Atyaúristen...a windowsban instance-nak nevezik valamilyen modulnak egy példányát, ami lehet például EXE, vagy egy DLL, a lényeg, hogy van egy belépési pontja. Ahhoz, hogy csinálj mondjuk egy ablakot, meg kell adnod az instance-ot, ami használni akarja és a winapi osztály nevét, ami azonositja az...ablakot (a winapiban ugyanis a controlok is ablakok, söt úgy is hozod öket létre, mint az ablakot). A legtöbb esetben úgy indul a program, hogy csinálsz magadnak egy saját window class-t, ami a föablakodat jelenti:

CODE
WNDCLASSEX wc = { sizeof(WNDCLASSEX), CS_CLASSDC, (WNDPROC)WndProc, 0L, 0L, hInst, NULL, NULL, NULL, NULL, "TestClass", NULL }; RegisterClassEx(&wc); // ... UnregisterClass("TestClass", wc.hInstance); return 0;

Valamit azért elárul a felület minöségéröl, hogy mindenki vécének deklarálja ezt az objektumot. A WndProc nevü változó egy függvény lesz, nevezetesen az ablak-osztály üzenetkezelöje. Azaz minden ilyen osztályú ablak a programon belül ugyanazt a függvényt használja üzenetkezelésre. Ez most még nem baj, de amikor majd gombokat akarsz csinálni, akkor bizony elég nagy baj lesz. Szerencsére az ablakleírót mindig megkapod, így ügyes programozással mindig el lehet dönteni, hogy ki küldte az üzenetet.

CODE
LRESULT WINAPI WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) { return DefWindowProc(hWnd, msg, wParam, lParam); }

Na akkor most csináljunk egy ilyen ablakot. A CreateWindow függvény paraméterei annyira nem bonyolultak, az ablak feature-jeit például összevagyolt flagekkel lehet megadni.

CODE
int w = GetSystemMetrics(0); int h = GetSystemMetrics(1); DWORD style = WS_CLIPCHILDREN|WS_CLIPSIBLINGS|WS_SYSMENU|WS_BORDER|WS_CAPTION; hwnd = CreateWindowA("TestClass", "Winapi1", style, (w - 800) / 2, (h - 600) / 2, 800, 600, NULL, NULL, wc.hInstance, NULL);

Nem kell tudnod mi mit jelent, ha érdekel megnézed guglin. Ami még hátravan az fö üzenethurok leimplementálása. Ez egy elég idióta név, angol nyelvén message hook/loop; ugyanolyan hülyén hangzik. Közös megegyezés alapján (amiben csak én vehetek részt) nevezzük el addigfussamigquitnemjön ciklusnak. Máris mennyivel jobb nem?

CODE
MSG msg; ZeroMemory(&msg, sizeof(msg)); while( msg.message != WM_QUIT ) { while( PeekMessage(&msg, NULL, 0U, 0U, PM_REMOVE) ) { TranslateMessage(&msg); DispatchMessage(&msg); } }

Az üzenetek mindig egy FIFO sorba kerülnek be (first in, first out), a PeekMessage kiveszi az elsöt és ideadja neked. A TranslateMessage winapiról lefordítja winapira. Ismeritek azt a viccet, hogy:

Két windows beszélget:
- Te, mi kompatibilisek vagyunk egymással?
- Mit mondtál?


Ez mindent megmagyaráz szerintem. A DispatchMessage pedig elküldi az üzenetkezelönek (wndproc). Végezetül még csináljuk meg azt, hogy az Esc billentyüre kilépjen a program:

CODE
LRESULT WINAPI WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) { switch( msg ) { case WM_CLOSE: ShowWindow(hWnd, SW_HIDE); DestroyWindow(hWnd); break; case WM_DESTROY: PostQuitMessage(0); return 0; case WM_KEYUP: switch(wParam) { case VK_ESCAPE: SendMessage(hWnd, WM_CLOSE, 0, 0); break; } break; default: break; } return DefWindowProc(hWnd, msg, wParam, lParam); }

Elsöre legyen elég ennyi. Vagy inkább örökre? Kód itt.


Höfö:
  • Telepíts Linuxot!

back to homepage

Valid HTML 4.01 Transitional Valid CSS!