Tartalmi kivonat
Windows programozás alapjai Előzetes verzió – 2001-04-20 A jegyzetet kiadja: BME Automatizálási és Alkalmazott Informatikai Tanszék Készítők A jegyzetet készítette: Dr. Charaf Hassan A jegyzetet átdolgozta: Benedek Zoltán Témakörök Bevezetés Eseményvezérelt programozás alapjai Fontosabb üzenetek (egér, billentyűzet, időzítő) Windows program felépítése, készítése, fordítása. Vezérlőelemek (Control-ok) Erőforrások (Menük, ikonok, kurzorok, bittérképek, sztringtáblák, stb.) Dialógus ablakok, közös dialógus ablakok (FileOpen, FileSave, Color, Font, Print setup, Find, Replace, stb.) GDI (Graphic Device Interface) Memória és fájlkezelés DLL-ek készítése. Appikációk közötti kommunikáció Help készítése Irodalom 1. MSDN (Microsoft Developer Network) Library, ezen belül a Platform SDK – ez a Microsoft Visual Studio-hoz, illetve ennek részkomponenseihez (pl. Visual C++)
tartozó online help (súgó) 2. Windows NT 4 Programming From the Ground Up, illetve ennek újabb kiadása, a Windows 2000 Programming From the Ground Up Windows programozás VC++ környezetben (Automatizálási Tanszék) 2001 1. Bevezetés 1.1 Cél A jegyzet bemutatja ma talán legelterjedtebb 32 bites operációs rendszer család, a Microsoft Windows - alapvető működési elvét - ismerteti programozásához szükséges fogalmakat, eszközöket - bemutatja a Windows programok elkészítésének módját A mai Windows verziók, mint például a Windows NT, Windows 2000 a korábbi verziókkal ellentétben teljesen önálló, rendkívül összetett operációs rendszerek. Szolgáltatások szinte végtelen sorát nyújtják, melyek mind elérhetőek a Windows programok írásakor. A jegyzetnek nem célja és nem is lehet célja, hogy ezeket mind ismertesse, inkább alapvető szinten ismerteti a fontosabb fogalmakat és szolgáltatásokat, valamint bemutatja a Windows programok
elkészítésének általános módját. Ezek ismeretében és természetesen sok-sok gyakorlás után a megfelelő dokumentációk (pl. MSDN Library) birtokában a Windows programozásának bármilyen területén képesek lehetünk programok elkészítésére. 1.2 Egy kis történelem A grafikus felhasználói felületek GUI (Graphical User Interface) a 70-es években láttak napvilágot. Az első GUI rendszert a Xerox’s Palo Alto Research Center (PARC) dolgozta ki. Ez a GUI volt a felülete az objektum orientált Smalltalk fejlesztési rendszernek. A GUI fejlesztői az objektum orientált programozás ideális eszközeinek tekintik a GUI rendszereket. A PARC rendszer után számos grafikus felület jelent meg, például: “Apple Lisa”, “OS/2 presentation”, “XWindows “, stb. A MICROSOFT Windows is egy ilyen GUI felületet biztosító operációs rendszer. Amikor elkezdték a Windows fejlesztését az objektum orientált programozás még nem volt eléggé átgondolt és
még nem volt annyira ismert, ezért a Windows-t procedurális nyelven írták meg (C nyelven ). Az első windows verzió 1985-ben jelent meg, azóta számos változáson ment át. Év 1985 1987 1988 1990 1992 1993 1993 1994 2000 Verzió Megjegyzés 1.01 320 KB RAM 2.0 Hasonlított az OS/2 Presentation managerre 2.1 286 és 386 3.0 Real, Standard, Enhanced mód támogatása 3.1 Standard, Enhanced mód támogatása Windows for (Peer to Peer) hálózat támogatás WorkGroups 3.1 Microsoft a Windows Igazi 32 bites operációs rendszer NT 3.5 3.11 (3.1 továbbfejlesztett változata) Windows95 32 bites operációs rendszer 16 bites részekkel Windows NT 4.0 Az NT 3.5 továbbfejlesztett változata Window95 Workstation; Windows felhasználói felülettel. NT 4.0 Server Windows98 Window 2000 A Windows NT architektúrájára épül annak stabilitását és a Windows98 kényelmét nyújtva. Előzetes verzió - 2001.0420 1 Windows programozás VC++ környezetben (Automatizálási
Tanszék) 2001 Az első verziók nem rendelkeztek saját fájl rendszerrel és mind 16 bitesek voltak. 1993-ban adta ki a Microsoft a Windows NT 3.5-t és utána az NT 351-t, ami igazi 32 bites multitaszkos operációs rendszer Az NT-nek létezett ALPHA, MIPS és INTEL változata, azonban a PC világban sokáig nem tudta helyettesíteni a “sima” Windows-t a Hardver igénye miatt. 1995-ben megjelent a Windows 95, ami igazi 32 bites operációs rendszer. A Microsoft 1996–ban jelenet meg a Windows NT 40 A Windows 95 (ill Windows 98) és a Windows NT (illetve Windows 2000) közötti különbségek így összegezhetők: Windows 95/ Windows 98 Windows NT/ Windows 2000 kissebb erőforrás igényű, ugyanazon a hardver konfiguráción gyorsabban fut kevésbé stabil, 16 bites kódrészeket tartalmaz stabil, robosztus, igazi 32 bites operációs rendszer nem támogatja a biztonságot (felhasználó támogatja a biztonságot (felhasználó azonosítás, azonosítás, védelem,
jogosultság ellenőrzése) védelem, jogosultság ellenőrzése) más architektúra Az újabb verziók már beépített támogatást nyújtanak a ma irányadónak tekinthető elosztott komponens alapú programok, rendszerek elkészítéséhez, amihez a Microsoft saját technológiát fejlesztett ki. Ennek a neve COM (Component Object Model), ill. DCOM (Distributed COM), valamint Windows 2000 alatt a COM+. 1.3 Mi a Windows A Windows NT és a Windows 2000 már nyújtják mindazokat a szolgáltatásokat, melyeket egy modern operációs rendszertől ma elvárunk: - - alapvető OS funkciók, mint pl. I/O (fájlkezelés, memória menedzsment) grafikus felhasználói felület multitaszking, vagyis egyszerre több programot képes futtatni virtuális memóriakezelés o az egyes taszkok védelme egymással szemben és az operációs rendszer védelme a taszkok hibás működésétől o a fizikai memóriánál nagyobb memória áll a programok rendelkezésére biztonsági funkciók
beépített hálózat kezelés hardver eszközök széleskörű támogatása hardver független programok készítésének támogatása eseményvezérelt 2. Fogalmak A fejezet áttekint néhány olyan fogalmat, valamint bemutatja a Windows működésének néhány olyan jellemzőjét, alapvető koncepcióját melyek ismerete nélkülözhetetlen a továbbiak megértéséhez. Processz Definíció A processz szót és az applikáció (program) szót gyakran egymás szinonimájaként használjuk. Valójában lényeges különbség van a két fogalom között. Egy applikáció nem más mint statikus szekvenciális utasítások gyűjteménye. Egy processz akkor keletkezik, amikor egy applikációt indítunk Előzetes verzió - 2001.0420 2 Windows programozás VC++ környezetben (Automatizálási Tanszék) 2001 Ilyenkor a processz a futó programon kívül a program futtatásához szükséges rendszer erőforrásokat is tartalmazza. Ez a megkülönböztetés fontos akkor, amikor a
Windows 32 bites architektúráját tárgyaljuk Egy processz a következőket tartalmazza: Egy futtatható program Egy saját cím tartomány (private address space) Rendszer erőforrásokat Legalább egy thread (szál) fut A virtuális memória Egy olyan operációs rendszerben, ahol több processz is lehet egyszerre, nagyon fontos az, hogy minden processz memóriatartománya védve legyen a többi processztől, sem akaratlagosan, sem véletlenül ne ronthassák el egymás működését. A Win32 operációs rendszerek tudják biztosítani ezt a fajta védelmet Két fajta memória van egy Win32 operációs rendszerben: Fizikai memória: A mérete megegyezik a gépben levő RAM nagyságával Virtuális memória: 4GB cím, azaz 232 byte címezhető memória. Vigyázat, ez nem 4GB fizikai memória. Az operációs rendszer 2GB címet foglal a saját használatra és minden applikáció számára 2GB az elérhető virtuális címtartomány. Amikor egy programot indítunk
egy Win32 operációs rendszer alatt, akkor az operációs rendszer egy új processz -t hoz létre. Minden processz használhat 2GB virtuális címet, aminek kezelése (leképezése fizikai memóriacímekké, swap fájlba kiírás illetve onnan betöltés) az operációs rendszer feladata. A rendszerben minden processz úgy érzékeli, mintha 2GB szabad memóriával rendelkezne. Tehát az applikáció soha sem fér hozzá a fizikai memóriához. Minden memória hozzáférést a memória menedzser végez a virtuális cím alapján. Amikor egy programot indítunk egy Win32 operációs rendszer alatt a következő események történnek: 1. Az operációs rendszer egy új processz-t hoz létre Minden processz használhat 2GB virtuális cím-et (nem memóriát) 2. A virtuális memória menedzser leképezi az applikáció kódját az applikáció virtuális címének valamelyik részére és a futáshoz szükséges kódot tölti be a fizikai memóriába. (a kód elhelyezkedése a
virtuális cím tartományban kevésbé vagy egyáltalán nem függ a fizikai memóriabeli helytől. Előzetes verzió - 2001.0420 3 Windows programozás VC++ környezetben (Automatizálási Tanszék) 2001 3. Ha az applikáció DLL (Dynamic Link Library) -eket használ, akkor a használt DLL-ek betöltődnek a processz címtartományába 4. Az adatoknak és a stack-nek a fizikai memóriában foglal helyet az operációs rendszer, ezeknek szintén lesz címük a virtuális címtartományban is. Az applikáció futni kezd. Futás közben minden applikáció a saját virtuális címeit használja A virtuális memória menedzser gondoskodik arról, hogy az applikáció memória hozzáférési kéréseit eljuttassa a fizikai memóriához. Threads (Szálak) A processz felfogható úgy, mint egy olyan feladat, amelyet az operációs rendszernek kell teljesítenie. Ennek a feladatnak több része lehet. Például legyen a program egy megjelenítő rendszer, ami a soros vonalon kapja
meg az adatokat. Ennek a feladatnak legalább két része van Az egyik rész az adatok gyűjtésével foglalkozik, a másik pedig kirajzolja a gyűjtött adatokat. Az operációs rendszer által nyújtott multithread lehetőséget kihasználva ebben a programban két szálat (thread) indítunk, az egyes szálak gondoskodnak az egyes részfeladatokról. Tehát a thread egy processzen belül külön feladatként (task) is felfogható. Egy processz lehet egy vagy több szálú (multithreaded). Egy multithreaded processz akkor hasznos igazán, amikor a feladat végrehajtása időigényes. Amíg az egyik szál egy időigényes feladatot hajt végre, addig egy másik szál is futhat külön, és valami más feladatot végezhet. A thread-ek a futását lehet időzíteni. Például amikor az egyik thread befejezi a számítást, akkor induljon a megjelenítő thread és így tovább. Ha a multithreaded processz a Windows NT/2000 alatt fut és a gép multiprocesszoros, akkor minden processzor
más-más thread-et futtathat. Egy thread a szülő processz cím tartományában fut, és használhatja a processz által foglalt erőforrásokat. Alapvető különbség a processz és a thread között, hogy a processzek egymástól védettek, közvetlenül nem érhetik el egymás erőforrásait (memóriaterület, stb.) Az egy adott processzen belül futó szálak ugyanazt a memória címtartományt látják, így az egyes szálak közötti kommunikációt lényegesen egyszerűbb megvalósítani, mint a processzek közöttit (természetesen ez csak az egy adott processzen belüli szálakra igaz). Fontos tudatosítani magunkban, hogy amikor egy processz elindul, akkor mindig létrejön és elindul egy szál – programunk fő futási egysége. A szálakat két csoportba szokták sorolni: a, Worker thread – olyan szál, amelyik valamilyen háttérben végrehajtandó feladatot végez és nem kommunikál a felhasználóval. Pl nyomtatás, soros vonali kommunikáció, időigényes
algoritmus futtatása a háttérben, stb. b, GUI thread – mind neve is mutatja, ide azok a szálak tartoznak, melyek grafikus felületen (ablakokon) keresztül kommunikálnak a felhasználóval. Multitaszk A Windows (Win32) több programot tud futtatni egyszerre, tehát “multitaszkos”. Egy processzor egyszerre csak egy thread-et képes futtatni. A multitaszk a Windows-ban azt jelenti, hogy a CPU idejét több thread között osztjuk meg. Ezek a thread-ek más-más processzekhez tartozhatnak A futási egységek (jelen esetben a szálak) ütemezése lehet preemptív vagy nem-preemptív. Nem-preemptív ütemezés esetében egy task csak akkor kaphat processzor időt, ha az éppen futó task önként lemond a futási jogáról. Preemptív taszk ütemezés esetében az operációs rendszer megszakíthatja a thread futását, ha letelt a rá szabott idő, vagy egy nagyobb prioritású thread kérte a futást. Ez az ütemezés kizárja, hogy egy applikáció monopolizálja a
processzort. Előzetes verzió - 2001.0420 4 Windows programozás VC++ környezetben (Automatizálási Tanszék) 2001 A Windows 3.x nem-preemptív, a Win32 operációs rendszerek (különösen a Windows NT és a Windows 2000) preemptív ütemezésűek. Az operációs rendszer a következőképpen valósítja meg a multitaszkos működést: 1. Egy thread addig fut, amíg nem szakítják meg más thread-ek, vagy amíg várnia nem kell egy erőforrásra, hogy az szabad legyen. 2. Az aktuális thread környezetének (beállításai, változói) elmentése 3. A futásra kerülő thread környezetének a betöltése a rendszerbe 4. Ezt a szekvenciát ismétli az operációs rendszer, amíg vannak futásra váró thread-ek, 2 ábra 2. ábra A threadek közötti átkapcsolás Időzítés Az időzítés alapvető feladat egy multitaszkos rendszerben. Időzítés nem más mint annak eldöntése, hogy melyik thread-nek kell futnia. A thread létrehozásakor kap egy futási prioritást A
thread-ek különböző állapotokban lehetnek: inaktív, várnak egy esemény bekövetkezésére, készen állnak a futásra. Általában az a legmagasabb prioritású thread, amely készen áll a futásra Ilyenkor azt futási állapotba teszi a rendszer, addig fut amíg be nem fejezi a működését, vagy meg nem változik az állapota (pl.: lejár az időszelete) DLL –dinamikusan linkelt könyvtár A Windows programok általában egy futtatható exe fájlból és számos DLL (Dynamic Link Library)ből állnak. A DLL-ek olyan adat-, erőforrás- és függvény-gyűjtemények, amelyek dinamikusan töltődnek be a memóriába, ha valamelyik applikáció hivatkozik rájuk, így nem linkelődnek statikusan a programhoz. A futtatható fájl információt tartalmaz arról, hogy melyik DLL melyik függvényét fogja meghívni. A DLL tehát nem más, mint egy könyvtár (library) modul betöltése és linkelése futási időben (a library modul egy bináris file, amely futtatható
rutinokat tartalmaz). A DLL fájlok általában *.dll kiterjesztésűek, pl USER32.dll A DLL -ek előnyei Támogatják a kód újra használhatóságát (code reuse) HDD-n helyet takarítanak meg, mert a DLL-eket több program is használhatja. Pl “Commondlgdll” RAM takarékosak, mert amikor a DLL a memóriába kerül, akkor több applikáció is használhatja a rutinjait. Megkönnyítik a programok felújítását azzal, hogy elég a DLL fájl kicserélni, nincs szükség a DLL-t használó programok újrafordítására. Ha azt akarjuk, hogy a programunk több nyelvű legyen, akkor egyik jó módszer erre az, hogy különböző nyelvű DLL-eket írunk hozzá , a DLL-t használó program pedig ugyanaz. Előzetes verzió - 2001.0420 5 Windows programozás VC++ környezetben (Automatizálási Tanszék) 2001 A Windows-ban kiemelkedő fontosságú a következő három DLL: KERNEL32.dll USER32.dll GDI32.dll - ez a könyvtár főleg a memória
menedzselési funkciókat tartalmazza - a felhasználói felületet kezelését biztosítja (menük, dialógus ablakok, stb.) - a rajzolási rutinokat és az ezekkel kapcsolatos funkciókat tartalmazza. Ablakok A Windows lehetőséget biztosít arra, hogy több ablakot jelenítsünk meg egyszerre. Ez a fejezet azt mutatja be, hogy hogyan viszonyulhatnak az ablakok egymáshoz. Z order A felhasználó egyszerre több ablakot is láthat a képernyőn. Ha egyik ablakból egy vagy több másik ablakot is létrehozunk és megjelenítünk, akkor ezek az ablakok több réteget alkotnak. A rétegek menedzselése az operációs rendszer dolga. Ezt a rétegezést Z order-nek hívjuk Ablakok közötti kapcsolat A Windows-ban két fajta kapcsolat van az ablakok között: szülő-gyermek (parent-child) és ownerowned (birtoklási viszony). Szülő és gyermek ablakok: Egy applikáció hozhat létre olyan ablakot, amelyik gyereke egy másik ablaknak. A gyermek ablak a következőképpen viszonyul a
szülői ablakhoz: A gyermek ablak mindig a szülő ablak fölött van a Z order-ben. A gyermek ablak a szülő ablak kliens területének a határai között van korlátozva. Ezen kívül leső részei nem láthatók. Amennyiben a szülő ablakot bezárják, mozgatják, elrejtik, stb. akkor ezek a műveletek befolyásolják a gyermek ablak helyzetét. A Windows gyermek ablakokat a szülő ablakokon keresztül indirekt módon menedzseli Owner and Owned Windows A birtoklási reláció kevésbé szigorú, mint a szülő-gyermek viszony. Az owned ablakok bárhol lehetnek a képernyőn. Egy owned ablak a következőképpen viszonyul a tulajdonosához: Az owned ablak mindig az owner ablak fölött van a Z order-ben. Automatikusan megsemmisül, amikor az owner megsemmisül. Automatikusan elrejtődik, amikor az owner-t elrejtik. Előzetes verzió - 2001.0420 6 Windows programozás VC++ környezetben (Automatizálási Tanszék) 2001 Az ablakok viszonya
egymáshoz Ablak kliens területe Az ablak kliens területe az ablaknak az a része, ahová az alkalmazás rajzolni, szöveget megjeleníteni szokott. Nem-kliens területek Kliens terület (client area) Ablak érvénytelen területe (invalid region) Nagyon fontos fogalom. Az ablaknak az a része, amelyik korábban takarásban volt, de most láthatóvá vált a felhasználó számára. Érvénytelen terület keletkezik pl, ha: - egy korábban minimized applikáció restore vagy maximize módban jelenik meg - az ablak legfelülre kerül a Z order-ben és voltak más ablakok által takart részei - az ablak mérete megváltozik és új részek válnak láthatóvá ezáltal - az applikáció meghívja az InvalidateRect API függvényt egy adott ablakra Az applikációnak gondoskodnia kell az érvénytelen területek újrarajzolásáról. Ha érvénytelen területek keletkeznek, akkor a megfelelő ablak kap egy WM PAINT üzenetet. Előzetes verzió - 2001.0420 7 Windows
programozás VC++ környezetben (Automatizálási Tanszék) 2001 Ikon, bitmap, menu, toolbar Előzetes verzió - 2001.0420 8 Windows programozás VC++ környezetben (Automatizálási Tanszék) 2001 3. API (Application Programming Interface) API alatt a Windows alatt használt és a fent említett DLL-ekben definiált főleg függvények, (változótípusok és konstansok) összességét értjük. Ezek az elemek nem nyelvfüggőek Bármilyen Windows alatt használt programozási nyelvben alkalmazhatók (C, C++, MS-ACCESS, VISUAL BASIC, FOXPRO, Delphi, stb). 3.1 Változótípusok Természetesen minden nyelvnek vannak saját változótípusai. Ezeken a “hagyományos” típusokon kívül vannak olyan típusok, amelyek Windows specifikusak. A C nyelv esetén ezek a <windowsh> fájlban vannak definiálva. Ezek közül néhány: LONG, WORD, DWORD, LPSTR, HWND, HANDLE, LRESULT, stb. Fontos megértenünk, hogy ezek a típusok egy adott nyelv esetében mindig az adott nyelv
egy típusára képződnek le. Pl a C illetve C++ nyelv esetében: typedef typedef typedef typedef unsigned int UINT; // insigned int a C nyelv beépített típusa long LONG; // long a C nyelv beépített típusa UINT WPARAM; LONG LPARAM; Számos struktúra típus definiált az Windows API-ban, ezek közül néhányat említünk: typedef struct tagPOINT{ int x; int y;}POINT typedef struct tagMSG { HWND hwnd; UINT message; WPARAM wParam; LPARAM lParam; DWORD time; POINT pt;}MSG; typedef struct tagRECT {int left; int top; int right; int bottom;}RECT, stb. Magyar jelölési rendszer A Windows programozás során a változó neveket - Simonyi Károly nyomán - úgy szokás felvenni, hogy a név kezdőbetűi utaljanak a változó típusára. Pl: b : char p : 16 bites pointer lp : 32 bites pointer n :int l :long Ezt a konvenciót nem kötelező követni, de célszerű ismerni, mert a WinAPI dokumentációja is ezt használja. 3.2 Konstansok, attribútumok Több 1000 előre definiált
konstans van az API-ban. Ezeknek az a jellegzetessége, hogy az elnevezésük prefixumokkal (2, 3 nagybetű) kezdődik. A prefixum utal a konstans szerepkörére A prefixum után egy aláhúzás jel következik és utána jön maga a konstans nagybetűvel. Néhány példa illusztrációként: CS : (Class Style) ablakosztály stílus CS HREDRAW IDI : (IDentifier Icon) ikon azonosító IDI APPLICATION IDC : (IDentifier Cursor) Kurzor azonosító IDC WAIT Előzetes verzió - 2001.0420 9 Windows programozás VC++ környezetben (Automatizálási Tanszék) 2001 WS : (Window Style) ablak típus WS ICONIC CW : (Create Window) ablak létrehozás CW DEFAULT WM : (Windows Message) Windows üzenet WM PAINT DT : (Display Text) szöveg megjelenítés DT CENTER BM : (Box Message) Check- vagy RadioBox üzenete BM GETCHECK CB : (Combo Box) kombó doboz CB GETCURSEL LB : (List Box) lista doboz LB SETCURSEL EM : (Editor Message) editor üzenet EM LIMITTEXT CF : (Clipboard
Format) vágólap formátuma CF BITMAP ERR : (ERRor) hibakódok, stb. ERR CREATEDLG Ezek a konstansok általában egy adott számot jelölnek. 3.3 Függvények A teljes Win32 API sok ezer API függvényt tartalmaz. A függvények nevei általában több angol szóból állnak. A szavak első betűje mindig nagybetű pl SetWindowText() Természetesen a saját függvényeinket úgy nevezzük, ahogy akarjuk. Figyelembe kell venni, hogy a C nyelv különbséget tesz a nagy és a kis betű között (case sensitive). Ha ugyanazt az API függvényt akarjuk meghívni egy másik programozási nyelven pl. a BORLAND PASCAL-ban vagy VISUAL BASIC-ben vagy MS-ACCESS-ben, akkor nem kell különbséget tenni a kis és a nagy betű között, de érdemes (esztétikailag) úgy írni a függvény nevét, ahogy a C-ben írjuk és ahogy szerepelt a dokumentációban. Az API függvények WINAPI (régebben FAR PASCAL) „típusúak”. Ez a függvények hívási konvencióját határozza meg: a
függvényhívás a Pascal nyelv konvenciója szerint történik. Mint ismeretes a Pascal konvenció azt jelenti, hogy a függvény paraméterei a deklarálási sorrend alapján jobbról balra kerülnek a stack-be és a stack kiürítése (takarítása) a hívott függvény feladata. Vannak olyan kitüntetett szerepű függvények, amelyeket mi írunk meg, viszont az operációs rendszer hív meg (jelen esetben a Windows!). Ezeket a függvényeket CALLBACK függvényeknek nevezzük A CALLBACK típusú függvények hívása is a Pascal hívási konvenció szerint történik. A WINAPI és a CALLBACK szavak a fordítónak (compiler) szólnak és a függvény neve elé kell őket írni, pl: LONG CALLBACK WndProc (HWND, UINT, UINT, LONG); 3.4 Objektumok és leírók (HANDLE-k) Amikor a Windows-ban létrehozunk valamilyen objektumot (itt most ne az objektum-orientált nyelvek objektumaira gondoljunk), akkor általában azt egy leíró (handle) fogja azonosítani. Például amikor
létrehozunk egy ablakot, akkor az ablakot létrehozó függvény az új ablaknak a leírójával (HWND) tér vissza. Ezt a leíró azonosítja az újonnan létrehozott ablakot, ezt a leírót kell az egyéb ablakkezelő függvényeknek átadni. Egy leíró általában egy 32 bites érték, típusa pedig HANDLE vagy valamilyen más H betűvel kezdődő típus. Ilyen objektumok például a következők: Objektum ablak processz szál Előzetes verzió - 2001.0420 Leíró típus HWND HANDLE HANDLE 10 Windows programozás VC++ környezetben (Automatizálási Tanszék) 2001 eszközkapcsolat rajzoláshoz és HDC nyomtatáshoz betűtípus HFONT fájl HANDLE 3.5 Windows verziók és a Win32 API Az egyes Windows verziók – pl. Win95 és Win2000 - architektúrája, belső működése alapjaiban különböző. Ennek ellenére mivel ugyanazon a Win32 API-n keresztül programozzuk őket, a két platformon futó programok elkészítésében általában nincs különbség. Ennek ellenére
előfordulhat, hogy bizonyok API függvények csak az egyik illetve másik platformon értelmezettek, vagy működésük a két platformon némiképp eltér. Ezen felül ugyanaz - a már lefordított program a legtöbb esetben egyaránt futtatható mindkét platformon. 3.6 Windows programok készítésének kétféle módja Windows programot alapvetően kétféleképpen lehet írni. a, A Win32 API közvetlen használatával b, Egy osztálykönyvtár (vagy framework ) használatával Komolyabb programokat lényegesen egyszerűbben, gyorsabban és hatékonyabban készíthetünk el egy jól megtervezett framework segítségével, amely a leggyakrabban használt funkciók implementálásának a terhét leveszi a hátunkról, ráadásul azonnal egy objektum orientált környezetbe helyezi a programozót. Kérdés tehát, hogy miért választjuk kezdetben a nehezebb utat. Az ok az, hogy ezek az osztály-könyvtárak elrejtik a Windows programok működésének számos lényeges vonását,
melyek megértése hosszú távon nélkülözhetetlen. Ezen felül ezek az osztály-könyvtárak a teljes API által nyújtott szolgáltatásoknak általában csak egy részét fedik le, így gyakran úgyis rákényszerülünk az API függvények használatára. Az osztály-könyvtárak maguk is az API-ra épülnek, saját magasabb szintű szolgáltatásaikat API függvények hívásával valósítják meg. Ilyen osztály-könyvtár, illetve framework az MFC (Microsoft Foundation Classes), a Borland Delphi és a Visual Basic osztály illetve komponens könyvtárai. 4. Eseményvezérelt programozás 4.1 Eseményvezérelt programok A hagyományos programozás során mindig a program hívja az operációs rendszert (pl. billentyűzet vizsgálata, olvasás a standard inputról/írás a standard outputra). A Windows programok működése a “hagyományos” programokétól alapvetően eltér. Windows alatt futó programok esetében ennek pont a fordítottja történik: a programunk
várakozik, az operációs rendszer hívja a programunkat (illetve annak egy függvényét), ha valamilyen esemény bekövetkezik a rendszerben. Milyen események lehetségesek: a felhasználó lenyomott egy billentyűt, kattintott az egérrel, kiválasztott egy menüelemet, stb. Így a Windows alatt futó programok futási menetét a felhasználói események határozzák meg, program ezekre az eseményekre reagál. Így programunk működése némiképp az interrupt vezérelt programokéra hasonlítható. Ennek megfelelően a Windows rendszer üzenet alapú. Amikor egy esemény történik pl: egér mozgatás, gomb kattintás, billentyűzeten lenyomtak egy gombot, akkor egy üzenetet generál a Windows, bár maga az applikáció is generálhat üzeneteket. Az applikáció reagálása ezekre az eseményekre – vagyis Előzetes verzió - 2001.0420 11 Windows programozás VC++ környezetben (Automatizálási Tanszék) 2001 az üzenetek kezelése - határozza meg a program
menetét illetve viselkedését. Az applikációhoz érkező üzenetek számos információval rendelkeznek az eseményről: típusa, keletkezési ideje, és melyik ablaknak szól az üzenet. 4.2 Üzenetek A felhasználói eseményeket (egér, billentyűzet, időzítés, menü stb.) a Windows saját egységes üzenet formátummá konvertálja. Az üzenet a következőket tartalmazza: a címzett ablak leírója (HWND), az üzenet tárgya/tartalma (UINT típusú szám), az üzenet paraméterei (WORD wParam, LONG lParam), az üzenet keltének időpontja és az üzenet keltésekor érvényes kurzor pozíció. Az üzenetek MSG típusú struktúrában tárolhatók: typedef struct tagMSG { HWND hwnd; UINT message; WPARAM wParam; LPARAM lParam; DWORD time; POINT pt; }MSG; Az üzenet címzettje - hwnd Mint látható, minden üzenetnek van egy címzett vagy cél ablaka. Egyszerre több programot lehet indítani a Windows alatt. Aktív applikáció alatt azt az applikációt értjük, amely
éppen elfogadja a felhasználói eseményeket. Egy applikáción belül aktív ablakról is beszélhetünk Az egér és billentyűzet üzenetek címzettje az az ablak, amelyik az esemény bekövetkezésekor aktív volt (vagyis amelyik ablakon a felhasználó kattintott az egérrel illetve amelyik ablakba a felhasználó „gépelt”). Az üzenetet végső soron a címzett ablak (illetve annak ablakkezelő függvénye – lásd később) kapja meg. Az üzenet típusa - message Egy adott üzenet típust egy egyedi szám azonosít. A könnyebb kezelhetőség érdekében az egyes üzenet típusok a windows.h -ban konstansként vannak definiálva, így egy semmitmondó szám helyett egy sokkal beszédesebb névvel is tudunk hivatkozni rájuk (pl. 0x0100 helyett WM KEYDOWN) Például billentyű lenyomását illetve a bal egérgomb lenyomását jelentő számazonosítóra a konstansok definiálása: #define WM KEYDOWN #define WM LBUTTONDOWN 0x0100 0x0201 Azt szoktuk mondani, hogy egy
ablak kap egy WM KEYDOWN vagy egy WM LBUTTONDOWN típusú üzenetet. Az üzenet paraméterei – wParam és lParam Minden üzenethez tartozik két, az üzenet típusától függő paraméter: egy WPARAM típusú és egy LPARAM típusú. Valójában minkét típus egy 32 bites egész értéket (integer-t) jelent Például egy WM KEYDOWN típusú üzenetnél ezek a paraméterek tartalmazzák a lenyomott billentyű kódját és más jellemzőit. A wParam és az lParam 32 bites értékek, több üzenet típus esetében összetett információt tartalmaznak. Ilyenkor segíthet az alábbi két makró: LOWORD ( . ) HIWORD ( . ) - a paraméterben megadott 32 bites érték alsó szavát (alsó 16 bit) adja vissza - a paraméterben megadott 32 bites érték felső szavát (felső 16 bit) adja vissza Előzetes verzió - 2001.0420 12 Windows programozás VC++ környezetben (Automatizálási Tanszék) 2001 4.3 Fontosabb üzenettípusok Egér események – egér
mozgatásakor, egérrel való kattintáskor Billentyűzet események – billentyű lenyomásakor Időzítő események Az operációs rendszer működésével kapcsolatos események. Pl: WM CREATE – az ablak létrejöttekor kapja meg az adott ablak WM DESTROY – az ablak megszüntetésekor kapja meg az adott ablak WM SIZE – ha az ablak mérete megváltozott, mert pl. a felhasználó átméretezte az ablakot WM MOVE – az ablak pozíciója megváltozott WM PAINT – az ablakon érvénytelen területek keletkeztek, az ablak újra kell rajzolja magát WM VSCROLL és WM HSCROLL – a görgetősávval kapcsolatos esemény következett be Parancs (command) üzenetek: WM COMMAND. Ez egy sokcélú üzenet típus, pl akkor keletkezik, ha: o a felhasználó kiválaszt egy menüelemet o a felhasználó kattint egy toolbar gombon, o a felhasználó megnyom egy gyorsítóbillentyűt, o ha egy gyerekablakkal történik valami, akkor általában WM COMMAND-ot küld a szülő ablaknak és az
üzenet paramétereiben küldi el az esemény leírását Természetesen a WPARAM és LPARAM értelmezése az egyes esetekben más és más. A felhasználó által definiált üzenetek (WM USER + N és WM APP + N, ahol N egy szám) 4.4 Ablakkezelő függvény (Window Procedure) A Windows-ban szinte minden olyan vizuális elem ablak (window), amelyik valamilyen interakcióra képes a felhasználóval. Így a közönséges ablak mellett ablakok a nyomó- és kiválasztógombok, szövegmezők, dialógusablakok, stb. Az egyes ablakok csak azért viselkednek másként (vagyis egy nyomógomb azért más mint egy szövegmező), mert ugyanazokra az üzenetekre másképpen reagálnak. A Windows-ban minden ablakhoz tartozik egy ablakkezelő függvény (CALLBACK típusú függvény), amely gondoskodik az ablakhoz érkező események kezeléséről. Mint már volt róla szó a Windows értesíti a programunkat arról, ha valamilyen esemény következik be, erre az alkalmazás valamilyen módon
reagálhat (ettől eseményvezérelt program). Most már azt is tudjuk milyen módon értesíti a programunkat: meghívja a célablak ablakkezelő függvényét és paraméterként átadja neki az üzenetet. Ennélfogva ezen függvényen keresztül kommunikál a Windows az alkalmazásunkkal. Egy programhoz több ablak tartozhat és legalább egy mindig tartozik. Ebből adódik, hogy egy Windows programban legalább egy ablakkezelő függvény van. 4.5 Üzenetek kézbesítése a megfelelő ablakkezelő függvényhez Üzeneteknek a célablakhoz (illetve annak ablakkezelő függvényéhez) való eljuttatásának kétféle módja lehet: a, Queued: az üzenet bekerül az üzenetsorba és onnan továbbítódik az ablakkezelő függvénynek. Ezt a fajta üzenet kézbesítést üzenet feladásnak (post a message) nevezzük b, Not-queued: az üzenetsor kikerülésével közvetlenül az ablakkezelő függvény kapja meg az üzenetet. Ezt a fajta üzenet kézbesítést üzenet küldésnek (send
a message) nevezzük Előzetes verzió - 2001.0420 13 Windows programozás VC++ környezetben (Automatizálási Tanszék) 2001 a, Queued üzenetek és üzenet sorok Minden thread rendelkezik egy üzenet sorral. Ebbe a sorba kerülnek a szálhoz érkező üzenetek és onnan továbbítódnak a megfelelő ablakkezelő függvényhez. Ezeknek az üzeneteknek a feldolgozása aszinkron módon történik. Az üzenetsor a postaládához hasonlít: a küldő nem vár az üzenet elolvasására, csak küldi a levelet és abban bízik, hogy a fogadó valamikor el fogja olvasni azt. Minden szál maga dolgozza fel a saját üzeneteit – vagyis továbbítja azokat a megfelelő ablak ablakkezelő függvényének. Az üzenet feldolgozás FIFO módon történik. Az billentyűzet és egér üzenetei mindig aszinkron módon dolgozódnak fel. Ezeket az eseményeket az üzenetté való konvertálása után a rendszer egy rendszer várakozási sorba (system queue) helyezi el. Minden szálhoz (GUI
thread) tartozik egy üzenet sor (message queue). A system queue -ba érkező üzeneteket az operációs rendszer továbbítja az applikációk szálainak üzenet soraiba. Megjegyzés Amikor az applikáció elindul, vagyis processz lesz belőle, mindig elindul egy szál (ettől fog a processz „futni”). Az egyszerűbb applikációk általában egy szálon futnak, így azt is szokták mondani, hogy üzenetsora applikációnak van. Ez természetesen csak addig igaz, amíg nem indítunk más szálakat Megjegyzés Üzenetsora valójában nincs minden szálnak, hanem csak akkor rendelődik üzenetsor egy adott szálhoz, amikor az a szál az első grafikus vagy ablakkezelő műveletet végrehatja. Ez az erőforrásokkal való takarékosság miatt van így, hiszen lehet, hogy egy szál (worker thread) soha nem hoz létre ablakot és így nem kell fogadjon üzeneteket. Ebben az esetben az üzenetsor létrehozásának nincs értelme b, Not-queued üzenetek Nem minden üzenet kerül be a
sorba. Lehetőség van arra, hogy szinkron módon is küldjünk üzenetet egy ablaknak. Ebben az esetben az üzenet nem kerül be a szál üzenetsorába, hanem közvetlenül az ablak ablakkezelő függvénye hívódik meg. Ebben az esetben, a küldő addig vár, amíg nem tér vissza a függvény, vagyis az üzenetet a célablak fel nem dolgozza. Előzetes verzió - 2001.0420 14 Windows programozás VC++ környezetben (Automatizálási Tanszék) 2001 Üzenetkezelő ciklus (Message Loop) Az applikáció (szál) maga gondoskodik az üzenet kiszedéséről és feldolgozásáról: ezt végzi az üzenetkezelő ciklus (message loop). Minden applikáció rendelkezik legalább egy üzenetsorral (mivel legalább egy szál tartozik hozzá). Kérdés, hogyan jutnak el az üzenetek az üzenetsorból a megfelelő ablakkezelő függvényhez. Azt gondolnánk, hogy az operációs rendszer ezt teljesen elrejti és a programozó feladata pusztán az eseményekre való reagálás azáltal, hogy
megírja ablak(ok) ablakkezelő függvényét. Ez azonban nem így van. Minden applikációnak (minden GUI thread-nek) tartalmaznia kell egy üzenetkezelő ciklust, ami kiszedi az üzeneteket az üzenetsorából és azt továbbítja a megfelelő ablakkezelő függvénynek. Üzenet küldés Üzeneteket mi is küldhetünk ablakoknak, mégpedig kétféle módon: a várakozási soron keresztül (queued) vagy ennek elkerülésével (not-queued). Queued üzenetet a PostMessage függvénnyel küldhetünk, nonqueued-ot a SendMessage függvény segítségével. PostMessage(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) SendMessage(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) A hWnd a célablak leírója, a message az üzenet típusa, wParam és lParam pedig az üzenet paraméterei. A PostMessage beteszi az üzenetet a célablakot kezelő szál üzenet sorába és azonnal visszatér. A SendMessage közvetlen a célablak ablakkezelő függvényét hívja meg és addig nem tér
vissza, amíg a célablak az üzenetet fel nem dolgozta. Az üzenetek nem hasonlítanak a megszakításokhoz Az egyik üzenet feldolgozását egy másik üzenet nem szakíthatja meg. Ennek elkerülésére használhatjuk a SendMessage-et úgy, hogy az üzenet feldolgozása közben egy másik üzenetet küldünk. Előzetes verzió - 2001.0420 15 Windows programozás VC++ környezetben (Automatizálási Tanszék) 2001 Üzenetek alapértelmezett feldolgozása Az ablakkezelő függvényben vannak olyan üzenetek, amelyeket a program kezel és vannak olyanok, amelyeket az alapértelmezett (default) ablakkezelő függvénynek juttat el. A DefWindowProc függvény szolgál az üzenet default ablakkezelő függvényhez való továbbítására. Olyan üzenetek szokás továbbítani, melyek a program szempontjából érdektelenek és az alapértelmezett módon akarjuk lekezelni. Ilyen pl az ablak minimalizálás, maximalizálás, a menük megjelenítése, stb. 5. A program szerkezete
Már volt róla szó, hogy minden üzenetnek van egy címzett ablaka. Amikor egy windows applikáció indul, a program egy adott pontban kezdi el a futását: ez a belépési pont (entry point) a WinMain függvény. Az applikáció létrehoz egy vagy több ablakot Minden ablak tartalmaz egy ablakkezelő függvényt, amely gondoskodik az ablak üzeneteinek feldolgozásáról. A program tartalmaz egy üzenetkezelő ciklust. Ez a ciklus kiolvassa az üzeneteket és visszaadja a Windows-nak, hogy meghívja az ablakkezelő függvényt, ami egy CALLBACK függvény, tehát nem a mi programunk hívja, hanem maga a Windows. Egy tipikus Windows program a következő részekből áll: 1. Inicializálás: ebben a részben történik az ablak osztály regisztrálása a rendszerben és a változók inicializálása. 2. A főablak létrehozása 3. Az üzenetkezelő ciklus (kiolvassa a várakozási sorból az üzeneteket és továbbítja a Windowsnak, hogy ezekkel a paraméterekkel hívja meg az
ablakkezelő függvényt. 4. A főablak kezelő függvénye (Window procedure): az üzenetekre való reagálás 5. A kilépés előtti takarítás, kilépés Egy tipikus program váza: #include <windows.h> #include <másfájlok.h> #include “myfiles.h” // globális változók deklarálás WinMain(.) { //Lokális változók deklarálása InitProgram(); // ablakosztaly regisztalasa WNDCLASSEX wcex; wcex.lpfnWndProc = WndProc; RegisterClassEx(&wcex); // ablak létrehozása hWnd=CreateWindow(.) // ablak megjelenítése (maximized, minimized, default) ShowWindow(.); UpdateWindow(hWnd); Előzetes verzió - 2001.0420 16 Windows programozás VC++ környezetben (Automatizálási Tanszék) 2001 // Üzenetkezelo ciklus While(GetMessage(&msg, NULL, NULL, NULL)) { TranslateMessage(&msg); DispatchMessage(&msg); } return msg.wParam; } // Ablakkezelő függvény LONG CALLBACK export WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { // lokális
változók // egy switch – case szerkezetben megvizsgáljuk milyen üzenetet kapott a // a függvény paraméterként és az üzenettől függően csinálunk valamit Switch (msg) { case WM CREATE: . return 0; case WM PAINT: RajzoljValamit(); return 0; case WM COMMAND: switch (wParam) { case IDM FILENEW: // ez pl. lehet egy menü elem azonosító CsinaljValamit(); break; } return 0; case WM DESTROY: memóriafelszabaditas() // pl. memória felszabadítás PostQuitMessage(0); return 0; } return DefWindowProc(hwnd, msg, wParam, lParam); } Mint tudjuk a C nyelvben a main() függvény a belépési pontja a programnak. A Windows alatt a main függvény helyet a WinMain-t kell használni. Az API elemek a windowsh-ban vannak deklarálva ezért a program elején be kell “inkludolni” a “windows.h” fájlt 5.1 Példa: “Hello World” applikáció Írjunk egy Windows programot, amely kiírja a képernyőn a “Hello World” szöveget. A szöveg az ablak közepén jelenjen meg (a
teljes példa megtalálható a példák között a HelloWorld könyvtárban.): A futó alkalmazás: Előzetes verzió - 2001.0420 17 Windows programozás VC++ környezetben (Automatizálási Tanszék) 2001 A program forrása: #include <windows.h> // ezt hasznald Visual C++ kornyezetben es ne a Windows.h-t // #include "stdafx.h" // peldany leiro HINSTANCE hInst; // ablakkezelo fuggveny deklaracioja LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); int APIENTRY WinMain(HINSTANCE HINSTANCE LPSTR int { hInstance, hPrevInstance, lpCmdLine, nCmdShow) HWND hWnd; MSG msg; char* szAppName = "Hello Word"; hInst = hInstance; // ------------ ablakosztaly regisztralasa ------------- // wcex egy WNDCLASSEX tipusu struktura lesz WNDCLASSEX wcex; wcex.cbSize = sizeof(WNDCLASSEX); wcex.lpszClassName = szAppName; // ablakosztaly neve wcex.style = CS HREDRAW | CS VREDRAW; wcex.lpfnWndProc = WndProc; // ablakkezelo fuggveny cime wcex.hInstance = hInstance;
wcex.hIcon = LoadIcon(hInstance, (LPCTSTR)IDI APPLICATION); wcex.hCursor = LoadCursor(NULL, IDC ARROW); wcex.hbrBackground = (HBRUSH)GetStockObject(WHITE BRUSH); wcex.lpszMenuName = NULL; Előzetes verzió - 2001.0420 18 Windows programozás VC++ környezetben (Automatizálási Tanszék) wcex.hIconSm wcex.cbClsExtra wcex.cbWndExtra 2001 = LoadIcon(hInstance, (LPCTSTR)IDI WINLOGO); = 0; = 0; RegisterClassEx(&wcex); // // --------------- foablak letrehozasa ------------------- hWnd = CreateWindow( szAppName, // ablakosztaly neve "Hello Word", // ablak cime WS OVERLAPPEDWINDOW,// ablak stilusa CW USEDEFAULT, // x pozicio CW USEDEFAULT, // y pozicio CW USEDEFAULT, // x meret CW USEDEFAULT, // w meret NULL, // szulo ablak leiro NULL, // menu leiro hInstance, // peldany leiro NULL); // lertehozasi parameter if (!hWnd) return FALSE; // --------------- foablak megjelenitese -----------------ShowWindow(hWnd, nCmdShow); UpdateWindow(hWnd); // ---------------
uzenetkezelo ciklus -----------------while (GetMessage(&msg, NULL, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); } return msg.wParam; } LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { PAINTSTRUCT ps; HDC hdc; char* szHello = "Hello Word"; switch (message) { case WM PAINT: hdc = BeginPaint(hWnd, &ps); RECT rt; // rt-be az ablak kliens teruletenek koordinatai GetClientRect(hWnd, &rt); DrawText(hdc, szHello, strlen(szHello), &rt, DT CENTER DT VCENTER | DT SINGLELINE ); EndPaint(hWnd, &ps); break; case WM DESTROY: PostQuitMessage(0); break; Előzetes verzió - 2001.0420 | 19 Windows programozás VC++ környezetben (Automatizálási Tanszék) 2001 default: return DefWindowProc(hWnd, message, wParam, lParam); } return 0; } 5.2 Ablak osztályok – ablakosztály regisztrálása A Windows-ban minden ablak valamilyen ablakosztályba tartozik. Mielőtt létrehoznánk egy ablakot regisztrálni kell az
ablakosztályt, amihez az új ablak tartozni fog. Ez az osztály arra szolgál, hogy az ablakhoz ablakkezelő függvényt, ikont, kurzort, háttérszínt, stb. definiáljunk Ennek megfelelően egy adott ablakosztályba tartozó ablakoknak ugyanaz lesz az ablakkezelő függvényük, következésképpen ugyanúgy reagálnak az egyes üzenetekre: vagyis hasonlóképpen fognak megjelenni és viselkedni. Azt, hogy egy ablak melyik ablakosztályba tartozik az ablak létrehozásakor (a CreateWindow vagy CreateWindowEx API függvények) kell megadni. Ablakosztály regisztrálása a következő függvénnyel lehetséges: RegisterClassEx( CONST WNDCLASSEX *lpwcx) Paraméterként át kell adni egy már előzetesen felparaméterezett WNDCLASSEX struktúrára mutató pointert. Az ablakosztályban meg lehet adni stílusjellemzőket, melyek az ablakosztályból létrehozott ablakok tulajdonságait befolyásolják. Néhány fontosabb stílus, melyek tetszés szerint kombinálhatók: CS NOCLOSE:
letiltja a rendszermenüben a Close-t CS DBLCLKS: a dupla egérkattintásnál kapjon az ablak WM LBUTTONDBLCLK ill. WM RBUTTONDBLCLK üzeneteket (lásd később) CS VREDRAW és CS HREDRAW: ha átméreteződik az ablak akkor a teljes ablakot újrafesti. Ablakosztály regisztrálása megtalálható a a „Hello World” példa alkalmazásban is. 5.3 Ablak létrehozása Ablak létrehozására a CreateWindow illetve a CreateWindowEx függvény használható. Mindkét függvénynek meg kell adni paraméterként az ablakosztályt, ami alapján az ablakot létre kell hozni, az ablak fejlécét, az ablak stílusjellemzőit (a CreateWindowEx-nél a kiterjesztett stílusjellemzőit is), az ablak koordinátáit és méretét, az applikáció leíróját (hInstance), az ablak menüjét (csak ha mást szeretnénk, mint ami az ablakosztályban adott). Néhány stílusjellemző (kb. 20-30 létezik, a teljes lista megtalálható az MDSN Library-ben), melyek a stílusjellemzőtől függően
kombinálhatók illetve nem kombinálhatók egymással: WS OVERLAPPEDWINDOW: overlapped ablakot hoz létre, az alkalmazás főablaka szokott ilyen lenni (az ablaknak lesz fejléce, rendszermenüje, kerete, stb) WM POPUP: popup ablakot hoz létre, ilyen például egy dialógusablak WS CHILD: gyerekablakot hoz létre WS HSCROLL: az ablaknak lesz vízszintes görgetősávja (scrollbar) WS VSCROLL: az ablaknak lesz függőleges görgetősávja (scrollbar) A CreateWindow függvény alkalmazása megtekinthető a „Hello World” példa alkalmazásban. Előzetes verzió - 2001.0420 20 Windows programozás VC++ környezetben (Automatizálási Tanszék) 2001 5.4 Az üzenetkezelő ciklus és az üzenetvezérlés Mint látható a fenti példában a üzenetkezelő ciklus 3 részből áll (természetesen más is lehet benne). Az első az üzenet kiolvasása a sorból, ezután az üzenet konvertálása és az üzenet továbbítása a Windowsnak, hogy ezekkel a paraméterekkel hívja meg az
ablakkezelő függvényt. Olvasás a sorból Az üzenet kiolvasására két API függvény létezik az egyik a GetMessage, a másik a PeekMessage. BOOL GetMessage(MSG *lpMsg,HWND hWnd,WORD wMinFilter,WORD wMaxFilter) Ha a függvény talál a sorban üzenetet, akkor azt kiveszi, ha nem, akkor vár és visszaadja a vezérlést a Windows-nak. A függvény első paramétere egy MSG struktúrára mutató pointer, ide kerül az üzenet a várakozó sorból. A másik három paraméter szűrési célokra használható A hWnd jelzi, hogy melyik ablak üzeneteire vagyunk kíváncsiak. A wMinFilter azt az értéket adja meg, amelynél csak nagyobb értékű üzenetekre vagyunk kíváncsiak. Hasonló célt szolgál a wMaxFilter paraméter, de a maximumra Az utóbbi paraméter hasznos lehet az egér és billentyűzet események esetén, hiszen ha valaki folyamatosan nyom egy billentyűt, előfordulhat, hogy ennyi idő alatt nem kerülnek feldolgozásra ezek az üzenetek, ilyenkor ha ennek a
paraméternek (WM KEYFIRST, WM MOUSEFIRST, WM KEYLAST, WM MOUSELAST) értéket adunk, akkor csak az első vagy az utolsó eseményt veszi figyelembe. Ha a függvény WM QUIT (kilépési) üzenetet talál a sorban, akkor FALSE-al tér vissza különben TRUE értékkel. A PeekMessage hasonló funkciót lát el mint a GetMessage azzal a különbséggel, hogy nem vár az üzenetre a visszatérés előtt. Ennek a függvénynek újabb UINT típusú paramétere is van Ez a paraméter az üzenetek kezelésére szogálhat (maradjon az üzenet a sorban a feldolgozás után vagy sem). A függvény visszatérési értéke attól függ, hogy talált-e üzenetet vagy a sorban. Az üzenet konvertálása Az applikációhoz érkező üzenetek átalakíthatók. Erre általában billentyűzet események esetében van szükség. Erre a célra a TranslateMessage függvényt lehet használni A függvény a WM KEYDOWN és WM KEYUP közé beszúr egy WM CHAR üzenetet. A WM CHAR üzenetre akkor van
szükségünk, ha a lenyomott billentyűhöz tartozó karakterre vagyunk kíváncsiak. BOOL TranslateMessage(const MSG* lpMsg) Az üzenet továbbítása az ablak kezelő függvénynek Miután kiolvastuk az üzenetet a sorból, ezt az üzenetet el kell juttatni az ablakkezelő függvénynek. Mivel az ablakkezelő függvény CALLBACK függvény, ezért a Windowsnak kell továbbítani az üzenetet és a Windows hívja meg ezt a függvényt a megfelelő paraméterekkel. Az üzenet továbbítására a DispatchMessage szolgál. LONG DispatchMessage(const MSG* lpMsg) Amennyiben az üzenet WM TIMER és a timer létrehozásakor egy timerkezelő függvényt adtunk, akkor az üzenet hatására, ez a függvény hívódik meg és ez az üzenet nem továbbítódik az ablakkezelő függvénynek. Előzetes verzió - 2001.0420 21 Windows programozás VC++ környezetben (Automatizálási Tanszék) 2001 5.5 Az ablakkezelő függvény A függvény deklarációja: LONG CALLBACK WndProc(HWND hwnd,
UINT msg, WPARAM wParam, LPARAM lParam) Az ablakkezelő függvény akkor fog meghívódni, amikor az főablakkal valamilyen esemény történik. Az esemény üzenet formájában jut el az ablakkezelő függvényhez: a függvény a paramétereiben kapja meg az ablak leíróját (hwnd), az üzenet típusát (message) és az üzenet paramétereit (wParam és lParam). Az ablakkezelő függvény neve tetszőleges lehet, a lényeg az, hogy az ablakosztály regisztrálása előtt a WNDCLASSEX struktúrában adjuk meg az ablakkezelő függvény címét (lásd a példában): wcex.lpfnWndProc = WndProc; A függvényen belül egy switch - case szerkezetben megvizsgáljuk milyen üzenet érkezett és az érkezett üzenettől függően csinálunk valamit (pl. rajzolunk, stb) Ismét kihangsúlyozandó, hogy egy adott üzenet feldolgozását egy másik üzenet nem szakíthatja meg (egy szálon belül). Ez azt jelenti, hogy amíg az ablakkezelő függvényből nem léptünk ki, addig egy újabb
üzenet feldolgozása nem kezdődhet meg. Ha egy hosszú műveletet végzünk az ablakkezelő függvényben vagy netalántán a programunk itt egy végtelen ciklusba kevereditt, akkor az applikáció „befagy”, nem reagál a billentyű lenyomására illetve az egér eseményekre és nem is rajzolja ki magát rendesen. Mindez pontosan azért van, mert az ablakkezelő függvény nem tért vissza, így nem történik meg a billentyűzet, az egér események lekezelése és az ablak újrarajzolása (a rajzolásról később lesz szó). 5.6 A példa rövid magyarázata A WinMain függvény szinte valamennyi részéről volt már szó. Amikor új applikációt készítünk a WinMain függvényt a vágólap segítségével (copy-paste) szoktuk az új applikációba egy korábbiból átvenni, hiszen az úgyis alig vagy egyáltalán nem különbözik. Az alkalmazásnak egyetlen ablaka van: a főalak. A főablak ablakkezelő függvénye a WndProc, aminek a lényegi része a WM PAINT üzenet
kezelésében van: itt történik a Hello World kirajzolása az ablak közepére. Szöveg megjelenítéséről és a rajzolásról részletesen a GDI tárgyalásánál lesz szó: Egyelőre a következőket érdemes tudni: 1.Bármilyen szöveg/rajz megjelenítéséhez létre kell hozni egy ún eszközkapcsolatot (Device Context, röviden DC). A szövegmegjelenítés és rajzolás az eszközkapcsolatra történik A rajzolás után meg kell szüntetni az eszközkapcsolatot. A példában az eszközkapcsolatot a BeginPaint-al hozzuk létre, melynek visszatérési értéke az eszközkapcsolat leíró: HDC. A szövegmegjelenítést a DrawText végzi: rajzolás a hdc eszközkapcsolatra, az szHello sztringet jeleníti meg, rt struktúra által megadott téglalapon belülre, a téglalap közepére igazítva (DT CENTER | DT VCENTER). Az rt struktúrát a GetClientRect API függvény tölti ki, mégpedig az ablak kliens területének koordinátáival. A végén az EndPaint szünteti meg az
eszközkapcsolatot. 2. A Windows a már elkészült szöveget/rajzot nem tárolja el Amikor rajzolunk, a rajz kikerül a képernyőre, de ha a rajzot vagy annak részét egy másik ablak eltakarja (mert fölé került a Z order-ben), akkor a rajz elveszik, vagyis ha újból a mi ablakunk kerül felülre, akkor a korábban takart részen a rajz nem lesz látható. Az applikációnak a rajzot – de legalább a korábban takarásban levő részt újból ki kell rajzolnia. Egzaktul fogalmazva: az applikációnak gondoskodnia kell az érvénytelen területek újrarajzolásáról. Kérdés, honnan tudja az applikáció, hogy az ablakát újra kell rajzolnia: onnan, hogy a Windows küld egy WM PAINT üzenetet. Ennek a működési mechanizmusnak az ismeretében már értjük, hogy miért pont a WM PAINT üzenet kezelésében írja ki a példaprogram a “Hello World” szöveget. Előzetes verzió - 2001.0420 22 Windows programozás VC++ környezetben (Automatizálási Tanszék) 2001
5.7 Áttekintés Windows Alkalmazás Az alkalmazás várakozási sora WM COMMAND WM KEYDOWN WM CHAR WM KEYUP Az üzenet továbbítása a windowsnak, hogy ezekkel a paraméterekkel hívja meg az abalkkezelõ függvényt. Az ablakkezelő függvény ki akarja írni a lenyomott billentyűhöz tartozó karaktert az alkalmazás aktív ablakára. A karakter kiírásához meghív egy megfelelő WinAPI függvényt, így a karakter tényleges kirajzolását a Windows hajtja végre. GetMessage(&msg,NULL,0,0) TranslateMessage(&msg) DispatchMessage(&msg) Ablak kezelõ függvény Az alkalmazás aktív ablaka A 6. Windows program készítése Általában egy Windows program a következő fájlokat tartalmazza: forrásfájlok (.C, CPP, H) egy erőforrás fájl (.rc) egy definiciós(.def) fájl Előzetes verzió - 2001.0420 23 Windows programozás VC++ környezetben (Automatizálási Tanszék) 2001 Text Editor Létrehozás módosítás .C, H, CPP fájlok C/CPP
Compiler .DEF fájl Linker .LIB fájl My Project setting Windows Applikáció Resource Compiler .RC, ICO, BMP, fájlok, Létrehozás módosítás Resource Editor A programozási nyelvek új változatai szinte kivétel nélkül rendelkeznek saját fejlesztési környezettel. Pl. a Borland C++ esetén az IDE környezet, ami rendelkezik, editorral, debugerrel, stb és resource editorként a WorkShop használható. A Visual C++ esetén szintén hasonló a helyzet más elnevezésekkel Erőforrás fájl Az erőforrás fájl (.rc) az erőforrás editorral beállított adatokat tartalmazza Tulajdonképpen ez egy szövegfájl, amely bármilyen szövegszerkesztővel is szerkeszthető csak vigyázni kell a szintaktikájára. Általában ikonokat, kurzorokat, bittérképeket, sztringtáblákat, menüket, dialógus ablakokat tartalmaz. Ezeknek az erőforrásoknak a kezeléséről a program maga gondoskodik. Minden erőforrás elemhez tartozik egy azonosító, amire hivatkozunk a programban.
Definiciós fájl A definíciós fájl (.def) beállításokat tartalmaz a létrehozandó programról A Windows 31-től kezdődően nincs különösebb szerepe, az itt megadandó beállítások a Win32-ben jórészt értelmüket vesztették. A definíciós fájloknak ma már lényegében csak a DLL-ek létrehozásakor használjuk, erről később lesz szó. Előzetes verzió - 2001.0420 24 Windows programozás VC++ környezetben (Automatizálási Tanszék) 2001 Projekt létrehozása Visual C++ 6 környezetben Visual C++ környezetben a forrásfájlok mellett egyéb fájlok is keletkeznek a projekt létrehozásakor, melyek többek között a projektre vonatkozó beállításokat tartalmaznak: .dsw – Developer Studio Workspace, ezt kell megnyitni a fejlesztőkörnyezetből .dsp – Developer Studio Project egy workpace-en belül több projekt is lehetséges .opt, ncb – egyéb projekt fájlok 7. Információ megjelenítése A Windows-ban nem létezik a standard input és
output. Így viszonylag körülményes az információ bekérése a felhasználótól és az információ megjelenítése a felhasználó számára. A standard output hiányában nem tudjuk használni pl. a printf függvényt Annak érdekében, hogy a továbbiakban egyszerűen tudjunk információt megjeleníteni ismerkedjünk meg az üzenet ablakokkal (message box). A következő ábra egy üzenetablakot mutat be: Üzenetablak megjelenítésére a következő függvény használható: int MessageBox( HWND nWnd, LPCTSTR lpText, LPCTSTR lpCaption, UINT uType); hWnd - az owner ablak leírója (NULL, ha nem akarunk ablakot megadni) lpText - az üzenetablak szövege lpCation - az üzenetablak fejléce Előzetes verzió - 2001.0420 25 Windows programozás VC++ környezetben (Automatizálási Tanszék) 2001 uType - az üzenetablak tulajdonságai, a következő konstansok kombinációja adható meg: MB OK MB YESNO stb. MB ICONQUESTION MB ICONSTOP stb. MB DEFBUTTON1 MB DEFBUTTON2
stb. – egy OK gomb legyen az ablakon – egy Yes és egy No gomb legyen az ablakon – egy kérdőjel ikon jelenik meg az ablakban – egy stoptábla jelenik meg az ablakban – az első gomb az alapértelmezett – a második gomb az alapértelmezett A MessageBox visszatérési értéke adja meg, hogy melyik gombot nyomta meg a felhasználó. A visszateresi érték lehet: IDOK, IDYES, IDNO, IDCANCEL, stb Pl: case WM CLOSE: int nRes; nRes = MessageBox( hWnd, "Biztosan ki akar lépni?", "TimerDemo", MB YESNO | MB ICONQUESTION | MB DEFBUTTON2); if (nRes == IDYES) DestroyWindow( hWnd ); break; Annak érdekében, hogy ne csak szöveget, hanem számokat is tudjunk megjeleníteni elevenítsük fel C programozási ismereteinkből az sprintf standard C függvényt: char szText[100]; sprintf( szText, “Ez egy szám: %d, ez egy szöveg: %s, ez egy lebegőpontos szám: %f, egy egy karakter: %c”, 24, “szöveg vagyok”, 3.14, ‘c’); MessageBox( hWnd, szText,
"Demo", MB OK ); 8. Események generálása Eseményt több egység válthat ki. A leggyakrabban előforduló események az egér, a billentyűzet, az időzítő, görgető sáv, stb. eseményei A Windows drivereken keresztül kommunikál a fizikai eszközökkel A driverekkel a Windows rendszer DLL-jei kommunikálnak és a programunk ezekkel a DLL-ekkel áll kapcsolatban. Az események az alábbi főbb kategóriákba sorolhatók: - Egér események – egér mozgatásakor, egérrel való kattintáskor Billentyűzet események – billentyű lenyomásakor Időzítő események Az operációs rendszer működésével kapcsolatos események. Pl: WM CREATE – az ablak létrejöttekor kapja meg az adott ablak WM DESTROY – az ablak megszüntetésekor kapja meg az adott ablak WM SIZE – ha az ablak mérete megváltozott, mert pl. a felhasználó átméretezte az ablakot WM MOVE – az ablak pozíciója megváltozott WM PAINT – az ablakon érvénytelen
területek keletkeztek, az ablak újra kell rajzolja magát WM VSCROLL és WM HSCROLL – a görgetősávval kapcsolatos esemény következett be Előzetes verzió - 2001.0420 26 Windows programozás VC++ környezetben (Automatizálási Tanszék) 2001 Parancs (command) üzenetek: WM COMMAND. Ez egy sokcélú üzenet típus, pl akkor keletkezik, ha: o a felhasználó kiválaszt egy menüelemet o a felhasználó kattint egy toolbar gombon o a felhasználó megnyom egy gyorsítóbillentyűt o ha egy gyerekablakkal történik valami, akkor általában WM COMMAND-ot küld a szülő ablaknak és az üzenet paramétereiben küldi el az esemény leírását Természetesen a WPARAM és LPARAM értelmezése az egyes esetekben más és más. A felhasználó által definiált üzenetek (WM USER + N és WM APP + N, ahol N egy szám) 8.1 Egér események Az egér esetén az esemény lehet az egér mozgatása, bal-, jobb-, illetve középső- gomb lenyomása, felengedése, dupla
kattintás stb. A következő táblázat mutatja be az egér eseményeket: Message WM MOUSEMOVE WM LBUTTONDOWN Esemény egér mozgatása bal egérgomb lenyomása WM LBUTTONUP bal egérgomb felengedése WM LBUTTONDBLCLK dupla kattintás a bal egérgombbal wParam az esemény bekövetkezése kor a shift vagy control billentyű le volt-e nyomva lParam az alsó szavában (LOWORD(lParam)) az kurzor x koordinátája a felső szavában (HIWORD(lParam)) a kurzor y koordinátája az esemény bekövetkezésekor Ugyanezek az üzenetek léteznek a jobb illetve a középső egérgombokra csak R (mint Right) illetve M (mint Middle)-t kell írni az aláhúzás utáni L helyett. A dupla kattintást csak akkor értelmezi a Windows annak, ha a beállított időn belül (default 500 ms) nyomtuk meg a gombot kétszer. A duplakattintási idő beállítására szolgál a SetDoubleClickTime(UINT) függvény. Szeretném felhívni a figyelmet arra, hogy általában (természetesen van kivétel) a minden
Set-tel kezdődő (beállító) függvény mellett létezik egy Get-tel kezdődő (lekérdező) ugyanilyen függvény. Az egér eseményt az az ablak kapja meg, amelyik felett volt a kurzor az esemény bekövetkezésekor (vagyis amelyik ablakon a felhasználó kattintott illetve amelyik felett mozgatta a kurzort). Ha az egér esemény nem az ablak kliens területe felett következett be, akkor az üzenet neve kiegészül egy NC-vel (pl. WM NCLBUTTONDOWN) Ha azt akarjuk, hogy mindig egy adott ablak kapja meg az egér eseményt a kurzor helyzetétől függetlenül, akkor ki kell adni a HWND SetCapture(hWnd) függvényt, amelynek a visszatérési értéke az ablak amely előtte birtokolta az egér üzeneteket. Amikor vissza akarjuk állítani az eredeti helyzetet, akkor a ReleaseCapture(void). függvényt kell meghívni. Előzetes verzió - 2001.0420 27 Windows programozás VC++ környezetben (Automatizálási Tanszék) 2001 8.2 Billentyűzet események A billentyűzet eseményt
az az ablak fogadja, amely birtokolja az input fókuszt. Az input fókusz mindig az aktív ablak valamelyik gyermek ablakánál van. Pl a 4 ábrán a Run ablak az aktív ablak és a fókusz ennek az ablaknak a Open nevű Edit ablakánál van. 4. ábra WM KEYDOWN billentyű üzenet akkor keletkezik, ha felhasználó lenyomott, WM KEYUP pedig akkor, ha felhasználó felengedett egy billentyűt. Mindkét esetben a wParam tartalmazza a lenyomott billentyű eszköz-független virtuális billentyűkódját. Néhány fontosabb billentyű virtuális billenyűkódja: VK DELETE Del billentyű VK SHIFT Shift billentyű VK CONTROL Control billentyű VK F1 F1 billentyű VK F2 F2 billentyű VK A Az ’A’ billentyű virtuális kódja . WM CHAR üzenetet a WM KEYDOWN és a WM KEYUP közé szúr be a Windows, mégpedig akkor, amikor a programunk az üzenetkezelő ciklusban meghívja a TranslateMessage API függvényt. A WM CHAR üzenet esetében a WPARAM a billentyű ASCII karakterkódját
tartalmazza. Természetesen csak azon billentyűk lenyomásakor keletkezik WM CHAR üzenet, melyeknek létezik ASCII kódja (pl. a DEL billentyűnek, a funkció billentyűknek nincsen). Ha a billentyű lenyomásakor az ALT billentyű le volt nyomva, akkor nem a fenti, hanem a WM SYSKEYUP, WM SYSKEYDOWN, WM SYSCHAR üzenetek keletkeznek. 8.3 Timer (Időzítő) eseményei A Windows a PC-ben található Timer IC (Intel 8254) használja az időzítések megvalósítására. Ez az IC 54.925 msec-onként aktivizálja 08H megszakítást Tehát a Windows időzítőjének az érzékenysége 54925 msec. A Timer beállítására a következő függvény használatos Timer = SetTimer (HWND hWnd, UINT idTimer, UINT msec, TIMERPROC lpMyTimerProc) A paraméterek értelmezése a következő: hWnd – annak az ablaknak a leírója, amelyikhez a timer tartozik idTimerA – az időzítő számazonosítója msec – időintervallum milliszekundumban lpMyTimerProc – időzítésre reagáló függvény
címe Előzetes verzió - 2001.0420 28 Windows programozás VC++ környezetben (Automatizálási Tanszék) 2001 A visszatérési érték jelzi, hogy sikerült a beállítás vagy sem. Ha a hWnd paraméternek nem NULL-t adunk meg, akkor az időzítő beállítása után (vagyis miután meghívtuk a SetTimer függvényt), a, ha a lpMyTimerProc paraméternek NULL-t adtunk meg, akkor a hWnd paraméterben megadott ablak (ablakkezelő függvénye) msec időközönként fog kapni egy WM TIMER üzenetet b, ha a lpMyTimerProc paraméternek nem NULL-t adtunk meg, hanem egy TIMERPROC típusú függvénycímet, akkor a lpMyTimerProc paraméterben megadott függvény hívódik meg msec időközönként. A lpMyTimerProc függvényt a következőképpen kell deklaráni: VOID CALLBACK TimerProc( HWND hwnd, UINT uMsg, dwTime ); UINT PTR idEvent, DWORD ,ennek a nevét és törzsét természetesen mi adjuk meg. A SetTimer függvény idTimer paraméterben egy számot kell megadni, ami az
ablakon belül egyértelműen kell azonosítsa a timer-t (vagyis ha egy adott ablakon belül több timer hozunk létre ez különböző szám kell legyen az egyes timer-ekre). Az időzítő mindaddig generálja az eseményeket, amíg az időzítőt a következő API függvénnyel le nem állítjuk: BOOL KillTimer(HWND hWnd, UINT idTimer) Ha a hWnd paraméter NULL, akkor a SetTimer és a KillTimer némiképpen másképp működik, a pontos működés leírása erre az esetre megtalálható a Win32 API dokumentációban. Annyit érdemes még tudni az időzítőről, hogy a b, eset, vagyis amikor függvénycímet adunk meg némiképp pontosabb működést eredményez, mint az a, eset, mert az a, esetben a WM TIMER az esemény bekövetkezésekor csak a szál várakozási sorába kerül, de még eltelhet egy kis idő, amíg innen elkerül az ablak ablakkezelő függvényéhez. Az újabb Windows verziók a közönséges timer mellett más timer típusokat is támogatnak: multimédia timer,
high resolution timer, stb. Ezek egyrészt jobb felbontást, másrészt pontosabb működést biztosítanak Időzítő kezelésére teljes példa található a példák között a TimerDemo könyvtárban. 8.4 Menü események Amikor egy menüelemet kiválasztunk, akkor egy WM COMMAND üzenet kerül a várakozási sorba. Az üzenet wParam paraméterének alsó szava (LOWORD(wParam)) tartalmazza a kiválasztott menüelem azonosítóját. Ha a felhasználó a rendszermenüből választ ki, akkor WM SYSCOMMAND üzenetet generál a Windows. A menükezeléssel részletesebben fogunk foglalkozni az erőforrásoknál Pl: case WM COMMAND: switch( LOWORD(wParam) ) { case IDM OPEN: MessageBox( hWnd, "File open command selected", "Menudemo", MB OK ); break; case IDM CLOSE: MessageBox( hWnd, "File close command selected", "Menudemo", MB OK ); break; break; Előzetes verzió - 2001.0420 29 Windows programozás VC++ környezetben (Automatizálási
Tanszék) 2001 . 9. Erőforrások (Resources) A Windows rendszerben az erőforrás olyan bináris adat, ami a futtatható (.exe) fájlban tárolható és futás közben igény szerint tölthető be a memóriába. Az erőforrásoknak két csoportja létezik: standard és egyedi (custom). A standard erőforrások a következők: - ikonok (icon) - bittérképek (bitmap) - kurzorok (cursor) - menük (menu) - sztringtáblák (string table) - dialógus ablakok (dialog) - gyorsítóbillentyűk (accelerator) - verzió információ (version) - betűtípus (font) – ritkán használt Az erőforrások esetében be lehet állítani, hogy mikor töltődjenek be a memóriába. Általában a program indításakor a Windows betölti a programot a memóriába, de az erőforrásokat nem. Ezek akkor kerülnek a memóriába, ha szükség van rájuk. Az erőforrásokat (a bittérképek kivételével) megosztva használják a példányok. A felszabadítás akkor történik, ha a programnak az utolsó
példánya is kilépett Az erőforrások készítéséhez számos eszköz áll rendelkezésre, a Developer Studio (Visual C++) is tartalmaz integrált erőforrás szerkesztőt (Resource Editor). Ezeknek az eszközöknek a kimenete az erőforrás fájl, ami egy szöveges RC kiterjesztésű fájl. Ebben megfelelő szintaktika szerint vannak definiálva vagy “importálva” az egyes erőforrás elemek. Ezt az RC szövegfájlt az erőforrás fordító (resource compiler) lefordítja egy bináris formátumú, RES kiterjesztésű fájllá. Ezt a RES fájl csatolja a linker a lefordított kódhoz, létrehozva a végleges exe állományt. Az RC formátumú erőforrás fájlt tartsuk meg (ne a RES-t), ez a kedvezőbb, hiszen ehhez a fájlhoz bármikor egy tetszőleges szövegszerkesztővel tudunk hozzáadni elemeket, természetesen a szintaktika betartása mellett. Szinte minden programban használunk erőforrás elemeket. Minden elemet el kell látnunk egy ‘ ’ terminált sztring
névazonosítóval vagy egy számazonosítóval (resource identifier), amellyel tudunk rájuk hivatkozni. Hogy melyiket választjuk rajtunk múlik, bár gyakoribb a számazonosító használata (a Developer Studio is ezzel dolgozik). Pl. az ikon esetében az RC fájlban ez a két eset következőképpen néz ki: Ikon1 vagy IDI IKON1 ICON “Myicon1.ico” ICON “Myicon1.ico” ,ahol az Ikon1 a név, illetve a számazonosító az IDI IKON1. Kérdés, hogy az IDI IKON1-ből hogyan lesz számazonosító: úgy, hogy egy megfelelő header fájlban a #define utasítással rendeljük hozzá a számot. Ezt a header fájlt be kell majd inkludolni (#include) minden olyan fájlba, ami használja az azonosítót. Példa a define-ra: myapplication.h fájlban: #define IDI ICON1 119 Előzetes verzió - 2001.0420 30 Windows programozás VC++ környezetben (Automatizálási Tanszék) 2001 Az azonosítónak egy ablakon vagy alkalmazáson (hogy melyik, az a típustól függ) belül
egyedinek kell lennie. A számazonosítók definiálásánál érdemes betartani azt a szabályt, hogy az azonosítókat nagy betűvel írjuk egy prefixummal. Pl a prefixumok lehetnek ID (általános) azonosító, IDI (ikon azonosító), IDC (kurzor), IDM (menü), IDS (string), stb. Példa: egy RC fájl a következőképpen néz ki: /*----------------------------MENUDEMO.RC resource script -----------------------------*/ #include "menudemo.h" IDI IKON1 IDB BITMAP1 IDC CURSOR1 ICON BITMAP BITMAP “Myicon1.ico” “MyBitmap1.bmp” “MyCursor1.cur” MenuDemo MENU { POPUP "&File" { MENUITEM "&New", IDM NEW, [Opció] MENUITEM "&Open.", IDM OPEN MENUITEM "&Save", IDM SAVE MENUITEM "Save &As.", IDM SAVEAS MENUITEM SEPARATOR MENUITEM "E&xit", IDM EXIT } POPUP "&Edit" { MENUITEM "&Undo", IDM UNDO MENUITEM SEPARATOR MENUITEM "Cu&t", IDM CUT MENUITEM
"&Copy", IDM COPY MENUITEM "&Paste", IDM PASTE MENUITEM "De&lete", IDM DEL } POPUP "&Background" { MENUITEM "&White", IDM WHITE, CHECKED MENUITEM "&Lt Gray", IDM LTGRAY MENUITEM "&Gray", IDM GRAY MENUITEM "&Dk Gray", IDM DKGRAY MENUITEM "&Black", IDM BLACK } POPUP "&Timer" { MENUITEM "&Start" IDM START MENUITEM "S&top" IDM STOP, GRAYED } POPUP "&Help" { MENUITEM "&Help.", IDM HELP MENUITEM "&About MenuDemo.",IDM ABOUT } } Előzetes verzió - 2001.0420 31 Windows programozás VC++ környezetben (Automatizálási Tanszék) DIALOGUS ABLAK DIALOG 18, 18, 165, 104 STYLE WS OVERLAPPED | WS VISIBLE | WS CAPTION | WS SYSMENU CAPTION "GYERMEK ABLAK" BEGIN EDITTEXT ID MEZO, 13, 27, 122, 12, ES LEFT|WS CHILD| WS VISIBLE |WS BORDER|WS TABSTOP CONTROL "",ID
COMBOBOX,"COMBOBOX",CBS DROPDOWN|WS CHILD| WS VISIBLE | WS VSCROLL | WS TABSTOP, 13, 43, 120, 33 CHECKBOX "Check box", ID CHECKBOX, 111, 56, 50, 12, WS CHILD | WS VISIBLE | WS TABSTOP RADIOBUTTON "Radio button", ID RADIOBUTTON, 111, 71, 51, WS CHILD | WS VISIBLE | WS TABSTOP CONTROL "OK", ID OK, "BorBtn", BS PUSHBUTTON | WS CHILD | WS VISIBLE | WS TABSTOP, 14, 75, 32, 20 CONTROL "Cancel", ID CANCEL, "BorBtn", BS PUSHBUTTON | WS CHILD | WS VISIBLE | WS TABSTOP, 52, 75, 32, 20 LTEXT "Írja ide a szöveget :", -1, 11, 15, 126, 8 END STRINGTABLE BEGIN IDS STRING1, IDS STRING2, IDS STRING3, IDS STRING4, IDS STRING5, END 2001 12, "Elso elem" "Második elem" "Harmadik elem" "Negyedik elem" "Ötödik elem" MyAccell ACCELERATORS BEGIN “^C”, IDM COPY, NOINVERT “^X”, IDM CUT, NOINVERT VK DELETE,IDM CUT, NOINVERT, VIRTKEY,SHIFT “^V”, IDM PASTE, NOINVERT
VK INSERT,IDM PASTE,NOINVERT, VIRTKEY,SHIFT END Az ehhez az erőforrás fájlhoz tartozó header fájl a következőképpen néz ki: /*-----------------------MENUDEMO.H header file ------------------------*/ #define IDI IKON1 #define IDB BITMAP1 201 202 #define #define #define #define #define IDS STRING1 IDS STRING2 IDS STRING3 IDS STRING4 IDS STRING5 0 1 2 3 4 #define #define #define #define #define IDM NEW IDM OPEN IDM SAVE IDM SAVEAS IDM EXIT #define IDM UNDO #define IDM CUT #define IDM COPY Előzetes verzió - 2001.0420 101 102 103 104 105 110 111 112 32 Windows programozás VC++ környezetben (Automatizálási Tanszék) #define IDM PASTE #define IDM DEL 113 114 #define #define #define #define #define 120 121 122 123 124 IDM WHITE IDM LTGRAY IDM GRAY IDM DKGRAY IDM BLACK #define IDM START #define IDM STOP 130 131 #define IDM HELP #define IDM ABOUT 140 141 #define #define #define #define #define #define 101 102 103 104 105 106 ID MEZO ID CANCEL ID OK ID
CHECKBOX ID RADIOBUTTON ID COMBOBOX #define DIALOGUS ABLAK 2001 1 Minden erőforrásra való hivatkozás előtt be kell tölteni az erőforrást és egy annak az erőforrásnak megfelelő típusú leíróban kell tárolni: HXXXX LoadXxxxxx(HINSTANCE hInstance, LPCTSTR lpszName) , ahol az X,x -ek megfelelő karakterrel helyettesítendők (icon, cursor, bitmap, stb.) A hInstance az applikáció példányleíró, az lpszName pedig az erőforrás neve. Mint volt róla szó az erőforrásokat nemcsak névvel, hanem számmal is azonosíthatjuk. Az utóbbi esetben használni kell a MAKEINTRESOURCE makrót a második paraméternél, ami a számazonosítóból nevet generál: LoadXxxxxx( hInst, MAKEINTRESOURCE(IDX XXXXX)) Megjegyzés - nyelv megadása Az egyes erőforrásoknál nyelvet is meg lehet adni. Így ugyanannak az előforrásnak lehet pl magyar és angol nyelvű verziója is a programunkban. Az erőforrás betöltésekor az a nyelvű verzió fog betöltődni, amelyik nyelv a
operációs rendszerünkben a meg van adva: ezt nevezik Locale-nak (Control Panel/Regional Options/Locale-ban lehet megadni). Megjegyzés - egyéb erőforrás betöltése Tetszőleges erőforrást be lehet tölteni a következőképpen. A FindResource API függvénnyel egy adott modulból, adott nevű és adott típusú erőforrást (a FindResourceEx –nek ezen felül a nyelvet is meg lehet adni). Ha az adott erőforrást a rendszer megtalálta, akkor az a LoadResource függvénnyel tölthető be Ahhoz, hogy a betöltött erőforrást használni tudjuk, meg kell hívni a LockResource függvényt, aminek a visszatérési értéke egy pointer a betöltött erőforrásra. A standard erőforrásokat sokkal egyszerűbben, egy lépésben be tudjuk tölteni: LoadAccelerators Gyorsítótábla betöltése LoadBitmap Bitmap betöltése Előzetes verzió - 2001.0420 33 Windows programozás VC++ környezetben (Automatizálási Tanszék) LoadCursor Kurzor betöltése LoadIcon Ikon
betöltése LoadMenu Menü betöltése LoadString Sztringtábla elem betöltése 2001 Erőforrások felszabadítása Az erőforrások egy részét nem kell felszabadítani, amikor az applikációnk befejezi a futását, az általa használt erőforrásokat az operációs rendszer felszabadítja. A bitmap, kurzor, ikon, menü típusú erőforrások manuális felszabadítására lehetőség (bizonyos esetekben szükség) van. Ennek az az oka, hogy pl. a bitmap egy olyan erőforrás, ami viszonylag sok memóriát használ A programunkból a bitmap által foglalt memóriát fel tudjuk szabadítani ha nincs már rá szükség a továbbiakban: meg kell hívni a DeleteObject(HBITMAP hBitmap) függvényt. 9.1 Ikonok (icon) Az ikont kétféleképpen lehet definiálni az erőforrásfájlban (.RC fájl): 1. Ikon1 2. IDI IKON1 ICON ICON “Myicon1.ico” “Myicon1.ico” Az első esetben névazonosítót használunk (Ikon1) a másodikban pedig számazonosítót (IDI IKON1). Az
azonosító után kell megadni az ICON kulcsszót, ami után pedig a fájl nevét, ami az ikon rajzát tartalmazza. Az ikon rajzát ico kiterjesztésű fájlokban szoktuk megadni A ikont erőforrás szerkesztővel lehet szerkeszteni: Előzetes verzió - 2001.0420 34 Windows programozás VC++ környezetben (Automatizálási Tanszék) 2001 Egy ablak ikonját az ablakosztály regisztrálása előtt tudjuk beállítani: WNDCLASSEX wcex; wcex.hIcon=LoadIcon(hInst,“ikon1”) vagy wcex.hIcon=LoadIcon(hInst,MAKEINTRESOURCE(IDI IKON1) RegisterClassEx(&wcl); //az 1.esetre //a 2. esetre Ha az ablaknak az ikonját utólag akarjuk módosítani, akkor SetClassLong (hWnd, GCL HICON, LoadIcon(hInstance,”ikon1”)) Előzetes verzió - 2001.0420 35 Windows programozás VC++ környezetben (Automatizálási Tanszék) 2001 Vannak előre definiált ikonok, amiket esetleg fel lehet használni, amennyiben nem akarunk sajátot rajzolni: IDI APPLICATION, IDI HAND 9.2 Kurzorok
(cursor) A kurzorok kezelése hasonlít az ikonokéhoz azzal az eltéréssel, hogy az ICON helyett CURSOR kulcsszót kell írni. A definíció az erőforrás fájlban (RC fájl): IDI CURSOR1 CURSOR “Mycursor1.cur” ,ahol IDI CURSOR1 a kurzor számazonosítója, a CURSOR a kulcsszó, az utolsó elem pedig a fájl neve, ami a kurzor rajzát tartalmazza. A kurzort meg lehet jeleníteni és el lehet rejteni a következő függvénnyel: int ShowCursor(BOOL fShow) Más jellegű felhasználásra példa a homokóra esete. Ha hosszú műveletet indítottunk a programunkban és tudatni akarjuk a felhasználóval, hogy várnia kell, akkor a kurzort homokórára állítjuk, és amikor befejeztük a műveletet visszaállítjuk az eredeti mutatóra. A kurzor állítására a SetCursor API függvény használatos, melynek visszatérési értéke a beállítás előtt érvényben levő kurzor: HCURSOR SetCursor( HCURSOR hNewCursor) Pl: HCURSOR hCursor = LoadCursor(hInstance, MAKEINTRSOURCE(IDC
CURSOR1); SetCursor( hCursor ); Léteznek előre definiált kurzor típusok. pl IDC WAIT (homokóra), IDC ARROW (standard nyíl), IDC CROSS (kereszt), stb. Ezeknél nem kell használni a MAKEINTRESOURCE makrót és a hInstance paraméternek NULL-t kell megadni. Pl: hCursor = LoadCursor( NULL, IDC WAIT ); SetCursor( hCursor ); Megjegyzés Az egér mozgatásakor a kurzort a Windows visszaállítja az ablakosztályban megadott kurzorra. Ezt úgy lehet kikerülni, hogy az ablakkezelő függvényben lekezeljük a WM SETCURSOR üzenetet és az üzenet kezelésében a kurzort mindig visszaállítjuk az általunk kívántra. Az ablakkezelő függvényben: case WM SETCURSOR: SetCursor( hCursor ); break; ,ahol hCursor a már korábban betöltött kurzor leírója. WM SETCURSOR üzenetet akkor küld a Windows, amikor az egeret elmozdítottuk az ablakon. 9.3 Stringtáblák (string tables) Definícióra példa az erőforrás (.RC) fájlban: Előzetes verzió - 2001.0420 36 Windows
programozás VC++ környezetben (Automatizálási Tanszék) 2001 STRINGTABLE BEGIN IDS STRING1, "Elso elem" IDS STRING2, "Második elem" END ,ahol a STRINGTABLE kulcsszó, a BEGIN-END között kell megadni az egyes sztringeket. Minden sztringnél meg kell adni a sztring azonosítóját (pl. IDS STRING1) és magát a szöveget Amikor szükségünk van egy sztringre, akkor be kell tölteni: int LoadString(HINSTANCE hInst, UINT ResourceID, LPSTR, lpszBuffer, in cbBuffer) Ez a függvény betölti a ResourceID azonosítójú sztringet az lpszBuffer bufferbe. A cbBuffer azt jelzi, hogy hány karakter kerülhet maximum a bufferbe (itt a buffer méretét szokás magadni). A függvény a buffer végére egy ‘ ’ karaktert is hozzáfűz. A visszatérési érték a ténylegesen átmásolt karakterek száma A stringtáblák használatának az egyik előnye az, hogy ha a felhasználónak megjelenített szöveget nem a kódba, hanem sztringtáblába tesszük, akkor a
programunk egy másik nyelvre (pl. magyarról angolra) való átültetésekor nem kell a forráskódban kikeresgélni a szövegeket, hanem azok egy helyen, a stringtáblában kell módosítani. Egy másik előny a memóriával való takarékosság, hiszen a sztringek igény szerint tölthetők be a memóriába. 9.4 Menük (menu) A menüket szintén az erőforrás-editor segítségével szerkeszthetjük. Minden menüelemhez egy azonosítót rendelünk hozzá. Be lehet állítani a menüelem állapotát is (CHECKED, GRAYED, Stb) A menü használatára a fenti említett RC fájl mutat példát. főmenü legördülő menü menü elem Előzetes verzió - 2001.0420 37 Windows programozás VC++ környezetben (Automatizálási Tanszék) 2001 A fenti ábra a Paint menüjét mutatja. Az File menü kiválasztásakor egy legördülő (PopUp) menü jelenik meg, amely több menüelemet tartalmaz. A jobb oldalon látható billentyű kombinációk a gyorsítók Ez azt jelenti, hogy ezekkel a
gyorsítókkal ugyanazt a hatást tudjuk elérni, mint a menü elemkiválasztásakor. A “” jelölés azt jelenti, hogy amennyiben ezt a menüelemet választjuk, akkor egy dialógusablak jelenik meg. a jel azt mutatja, hogy újabb legördülő menü fog megjelenni Egy menüelemet lehet szürkíteni, vagyis tiltani (pl a Set As Wallpaper az ábrán) . rendszer menü (system menu) A menü használatához az ablakosztály regisztrálása előtt érdemes beállítani a megfelelő menüt: WNDCLASSEX wcex; wcex.lpszMenuName=“MenuDemo” RegisterClassEx(&wcl); Utólag is megtehetjük ugyanazt a következőképpen: hMenu=LoadMenu(hInstance,”MenuDemo”) SetMenu(hWnd,hMenu) DrawMenuBar(hWnd) Legördülő menüt a programban is tudunk létrehozni a CreateMenu függvénnyel. Ehhez a létrehozott menühöz bármikor tudunk hozzáadni elemeket illetve törölni az AppendMenu, InsertMenu, RemoveMenu függvényekkel. A menü módosítása után a DrawMenuBar függvénnyel tudjuk
a módosításokat megjeleníteni. Le lehet kérdezni a menüelem állapotát, azonosítóját, szövegét a GetMenuState, GetMenuItemID, GetMenuString függvényekkel. Lehetőség van arra, hogy a “hagyományos” menükön kívül egy adott pontban egy legördülő menüt jelenítsünk meg a TrackPopupMenu függvénnyel. Ez általában az egér jobb gombjának a lenyomásához szokás kötni. Menü üzenetek Amikor a Menüből egy adott legördülő menüt választunk, akkor egy WM INITMENUPOPUP üzenet keletkezik. Ebben az esetben a wParam értéke a legördülő menü leírója, az lParam alsó szava a legördülő menü indexe, a felső szava pedig azt jelzi , hogy az üzenet rendszermenü üzenet vagy sem (1 ha rendszermenü, 0 ha nem). Ennél sokkal gyakrabban használt: a menüelem kiválasztásakor egy WM COMMAND üzenet keletkezik. Ilyenkor a LOWORD(wParam) értéke a menüelem azonosítója Az ablakkezelő függvényben a WM COMMAND üzenet kezelésén belül egy
switch-case szerkezettel szoktuk megvizsgálni, hogy melyik menüelem lett kiválasztva és a kiválasztott menüelemnek megfelelő utasításokat hajtjuk végre. Előzetes verzió - 2001.0420 38 Windows programozás VC++ környezetben (Automatizálási Tanszék) 2001 Amennyiben az üzenet rendszermenü üzenet, akkor az üzenet WM SYSCOMMAND és az lParam a kurzor pozícióját tartalmazza. Az alábbi példa a főablak ablakkezelő függvényéből mutat kódrészletet, az IDM OPEN, IDM CLOSE azonosítójú menüelemek kiválasztásakor egy üzenetablakot jelenít meg ami kiírja, hogy melyik menüelem lett kiválasztva, az IDM QUIT menüelem kiválasztásakor pedig bezárja az alkalmazást (természetesen a megfelelő menüelemeket az erőforrás fájlban kézzel, vagy az erőforrás szerkesztővel vizuálisan létre kell hozzuk) : case WM COMMAND: switch( LOWORD(wParam) ) { case IDM OPEN: MessageBox( hWnd, "File "Menudemo", MB OK break; case IDM CLOSE:
MessageBox( hWnd, "File "Menudemo", MB OK break; case IDM QUIT: DestroyWindow( hWnd ); break; } open command selected", ); close command selected", ); Menükezelésre teljes példa található a példák között a MenuDemo könyvtárban. 9.5 Bittérképek (Bitmaps) Ez az erőforrás annyiban más mint a többi, hogy ez egy GDI (Graphical Device Interface) elem (később kerül részletezésre). A bittérkép nem kerül megosztásra a példányok között és nem törlődik automatikusan a memóriából a program kilépésekor, ezért nekünk kell gondoskodnunk a törléséről a programból való kilépéskor. Definiálása az erőforrás fájlban: IDB BITMAP1 BITMAP “Mybmp.bmp” , ahol a IDB BITMAP1 a bitmap számazonosítója (a resource.h-ban definiálni kell), a BITMAP kulcsszó, a “Mybmp.bmp” pedig a fájl neve, ami a tényleges bitmintát tartalmazza Mint látható, az ikonokhoz és a kurzorokhoz hasonlóan az RC fájl nem tartalmazza a
tényleges bitmintát, csak hivatkozik arra a fájlra, amiben a bitminta tárolva van (Mybmp.bmp) A bitmapek használatáról a GDI fejezetben lesz szó részletesen. 9.6 105 Gyorsítóbillentyűk (Accelerators) A gyorsítóbillentyűk általában egy menüelem gyors elérésére használjuk. A definiálásuk hasonlít a többi erőforráshoz. A NOINVERT jelzővel azt érjük el, hogy amennyiben a gyorsítóbillentyű egy menüelemnek felel meg, ne látszódjon a menü villanása az adott billentyű kombináció kiválasztása esetén. Az üzenet kezelő ciklus a gyorsítóbillentyűk esetén a következőképpen néz ki: Előzetes verzió - 2001.0420 39 Windows programozás VC++ környezetben (Automatizálási Tanszék) 2001 hAccel=LoadAccelerators(hInstance, “MyAccel”) while (GetMessage (&msg, NULL, 0, 0)) { if(!TranslateAccelerator(hwnd,hAccel, &msg) { TranslateMessage (&msg) ; DispatchMessage (&msg) ; } } A TranslateAccelerator függvény megvizsgálja
a GetMessage által feltöltött msg üzenetet, hogy billentyűzetről származik-e. Ha igen, akkor ha az üzenet rendszermenü üzenet, akkor WM SYSCOMMAND üzenetet küld az ablakkezelő függvénynek, különben WM COMMAND-t. A függvény visszatérési értéke nem nulla, ha sikerült elküldenie az üzenetet az ablakkezelő függvénynek, különben 0. Ha sikerült az üzenet küldése, akkor abban az esetben a TranslateMessage és DispatchMessage-re nem kerül a vezérlés. Érdemes megemlíteni, hogy a TranslateMessage függvény felülírja az msg-ben tárolt hwnd-t az ő hWnd argumentumával, tehát a gyorsítóbillentyű üzenetei ugyanannak az ablaknak mennek akkor is ha másik ablaknál van az input fókusz. A TranslateMessage nem hajtja végre a billentyűzet üzeneteket, ha az inputfókusz egy dialógusablaknál vagy üzenetablaknál van. 10. Vezérlő elemek (control-ok) Már láttuk, hogy egy ablak létrehozása előtt regisztrálni kell az ablakosztályt, amihez
tartozni fog az adott ablak. A vezérlő elemek olyan előre definiált ablakosztályok, melyek vezérlési és megjelenítési funkciókat látnak el. Lehetőséget biztosítanak a felhasználónak, hogy szöveget adjon meg (edit ablak), opciók közül válasszon (radio button), valamilyen funkciót vagy műveletet aktiváljon (push button), stb. 10.1 Vezérlőelem típusok Szerkeszthető szövegablak (edit) Arra használjuk, hogy szöveget adhasson meg a felhasználó illetve szöveget jelenítsünk meg. Az a jellegzetessége, hogy nem kell nekünk gondoskodnunk a karakterek megjelenítéséről. Amikor ennél az elemnél van az input fókusz és gépelünk valamit, akkor a karakterek automatikusan jelennek meg az ablakban (ha a Read Only stílus nincsen beállítva). Be lehet állítani a görgetési opciókat, pl automatikusan görgessen függőlegesen és vízszintesen. Statikus ablak (static) Ez az ablak statikus szöveg, esetleg kép megjelenítésére alkalmas. A szöveget
tájékoztatási célra lehet használni. pl egy edit ablak mellett lehet jól használni Általában tájékoztatja a felhasználót arról, hogy milyen adatokat kell beírni az adott szöveg, illetve kombinált ablakba. (az ábrán a kombináltdoboz feletti szöveg a statikus ablak) Statikus ablak Előzetes verzió - 2001.0420 40 Windows programozás VC++ környezetben (Automatizálási Tanszék) 2001 Gomb (button) a, Nyomógomb (push button) Ezt a vezérlőelemet vezérlési funkciókra használhatjuk, pl. valamilyen funkció végrehajtására Tetszőleges képet (bitmap, ikon) vagy szöveget tehetünk rá. b, Kiválasztógomb (check button vagy checkbox) Egy adott opció kiválasztott vagy ki nem választott (esetleg tiltott) állapotának megadására szolgálhat. Egy kiválasztógomb vagy ki van választva, vagy nincs kiválasztva (esetleg tiltva van). A gombnak megadható egy felirat, ami a gomb mellett jelenik meg. c, Rádiógomb (radió button) Általában
kizárásos kiválasztási célokra használják, egy csoporton belül csak egy lehet beállított állapotban. A gombnak megadható egy felirat, ami a gomb mellett jelenik meg Listaablak (listbox) Ezen ablaknak a segítségével tudunk lehetőséget biztosítani a felhasználónak, hogy meglévő elemekből kiválaszthasson ki egy elemet. Kombinált ablak (combobox) Ez az ablak hasonlít a lista ablakhoz, azzal a különbséggel, hogy ebbe az ablakba beírni is lehet. Előzetes verzió - 2001.0420 41 Windows programozás VC++ környezetben (Automatizálási Tanszék) 2001 Görgetősáv (scrollbar) Az adatok görgetésére, lapozására szolgál. Két típus létezik a függőleges és a vízszintes Formázható szövegablak (rich edit) Olyan szövegablak, ami rendelkezik a közönséges többsoros szövegablakok tulajdonságaival, ezen felül a szöveg formázható és OLE objektumokat is lehet beszúrni. 10.2 A vezérlőelemek közös tulajdonságai Ismerkedjünk meg a
vezérlőelemek közös jellemzőivel programozói szempontból is. A vezérlő elemeket általában dialógus ablakokon helyezzük el, így egyszerűen, vizuálisan elhelyezhetők az adott dialógusablakon az erőforrás szerkesztő segítségével (dialógusablakokról a következő nagyobb fejezetben lesz szó). Az erőforrás szerkesztő arra is lehetőséget biztosít, hogy az egyes vezérlő elemek stílusjellemzőit beállítsuk. A vezérlő elemek nem csak dialógusablakon helyezhetők el, hanem a már megismert CreateWindow függvénnyel is létrehozhatók. A CreateWindow-nak azt az ablakosztály nevet kell megadni, amilyen vezérlőelemet létre szeretnénk hozni (pl.: “edit”, “button”, stb) Az általános stílusjellemzők mellett megadhatók típusfüggő stílusjellemzők, pl. az edit típus esetén az ES MULTILINE azt jelenti, hogy az adott edit ablak többsoros lesz. Mindig gyerekablakok, vagyis van egy szülőablakuk és létrehozásukkor meg kell adni a WS
CHILD stílusjellemzőt (window style). Egy adott ablakon belül egyedi számazonosítóval kell rendelkezzenek. Ha dialógusablakon használjuk a vezérlőelemet, akkor az azonosítónak resource.h fájlban egy konstans nevet szoktunk #define-al megadni. Pl: #define IDC EDIT VEZETEKNEV 12 A Developer Studio fejlesztőkörnyezet automatikusan kezeli a számazonosítókat: ha új vezérlőelemet helyezünk el egy dialógusablakon, akkor automatikusan lefoglalja a következő még nem használt számot és a mi feladatunk csak a név megadása. A név megadása után a #define bejegyzést automatikusan beszúrja a resource.h fájlba Megjegyzés: ha a vezérlőelemet a CreateWindow-val hozzuk létre, akkor a vezérlőelem azonosítóját a CreateWindow függvény hMenu paraméterében kell megadni. Ez ugyan nem logikus, de így kell csinálni Előzetes verzió - 2001.0420 42 Windows programozás VC++ környezetben (Automatizálási Tanszék) 2001 A vezérlőelemek állapotának
állítása és lekérdezése Minden vezérlőelemnek vannak olyan állapotjellemzői, melyeket a programunkból lehet állítani illetve lekérdezni. Pl egy edit ablaknál az ablak szövege, egy kiválasztógombnál a gomb állapota (ki van-e választva vagy sem), listaablaknál a listaablak elemei, illetve a kiválasztott elem indexe, stb. Ezeket az állapotjellemzőket általában úgy tudjuk állítani illetve lekérdezni, hogy az adott vezérlőelemnek egy megfelelő üzenetet küldünk a következő API függvények valamelyikével: SendMessage(HWND, UINT, WPARAM, LPARAM) ,ami az ablakleírójával adott vezérlőelemnek küldi el az adott üzenetet SendDlgItemMessage(HWND hDlg, UINT nID, UINT lParam) iMes, WPARAM wParam, LPARAM ,ami akkor használható, ha a vezérlőelemet dialógusablakon helyeztük el: a hDlg dialógusablakon levő, nID azonosítójú vezérlőelemnek küldi el az adott üzenetet. Például a SendMessage(hWndgomb, BM SETCHECK,1,0L) beállítja a hWndgomb
leírójú kiválasztógomb állapotát kiválasztottra (az 1 miatt). A következő utasítással tudjuk lekérdezni a gomb állapotát: allapot = SendMessage(hWndgomb, BM GETCHECK,0,0L) A vezérlőelemek állapotváltozásainak kezelése Ha valamilyen változás következett be az állapotukban, akkor üzenetet küldenek a szülőablakuknak. Ezeket a speciális üzeneteket notification (értesítő) üzeneteknek nevezzük. Az üzenet tartalmazza a vezérlőelem azonosítóját és a notification üzenet kódját, ami jellemzi a változást. Pl egy edit ablak EN CHANGE notification üzenetet küld a szülőjének, ha megváltozik az ablak tartalma (mert pl a felhasználó gépelt bele), vagy egy gomb BN CLICKED üzenetet küld, ha a felhasználó kattintott rajta. Nyilvánvalóan vezérlőelem típusonként más és más notification üzeneteknek léteznek, hiszen más és más értesítő jellegű üzeneteknek van értelme. A notification üzenetek felépítése a következő:
message = WM COMMAND: Az üzenet, amit a szülő kap WM COMMAND típusú (kivéve a scrollbart, ahol a szülő WM HSCROLL és WM VSCROLL üzeneteket kap). LOWORD(wParam): A vezérlőelem azonosítója (pl. IDC EDIT NEV, ) Ha egy ablaknak több vezérlőelem gyerekablaka van, akkor az ez alapján tudja eldönteni, hogy melyiktől jött az üzenet HIWORD(wParam): Az értesítő üzenet típusa (pl. BN CLICKED, EN CHANGE), vagyis ez mondja meg, hogy mi történt az adott vezérlőelemmel. lParam: A vezérlőelem ablakleíróját tartalmazza. 10.3 Vezérlőelem típusok programozói szemszögből Ez a fejezet az egyes vezérlőelem típusokra a következőket adja meg: a, Ablakosztály neve, a CreateWindow-nak kell megadni, ha esetleg nem erőforrásként akarjuk létrehozni az adott vezérlőelemet. b, Fontosabb megadható stílusjellemzők. A CreateWindow-nak kell megadni, illetve az erőforrás fájlban (vagy az erőforrás szerkesztővel) attól függően, hogy hogyan hozzuk létre
az adott vezérlőelemet Előzetes verzió - 2001.0420 43 Windows programozás VC++ környezetben (Automatizálási Tanszék) 2001 c, Fontosabb üzenetek, melyek küldhetők a vezérlőelemnek. Lásd fent a „A vezérlőelemek közös tulajdonságai” / „A vezérlőelemek állapotának állítása és lekérdezése” fejezetet. d, Fontosabb notification üzenetek. Lásd fent a „A vezérlőelemek közös tulajdonságai” / „A vezérlőelemek állapotváltozásainak kezelése” fejezetet. Az egyes vezérlőelemek által támogatott stílusjellemzők, üzenetek és notification üzenetek teljes leírása megtalálható az MSND Library-ben. Szerkeszthető szövegablak (edit) a, Ablakosztály neve: “EDIT” b, Fontosabb megadható stílusjellemzők ES MULTILINE: többsoros szövegablakot hoz létre ES LEFT, ES RIGHT, ES CENTER: szöveg igazítása a megadott módon ES READONLY: a felhasználó számára csak olvasható a szövegablak ES NUMBER: csak számot lehessen
beírni ES AUTOVSCROLL: ha az utolsó sorban a felhasználó megnyomja az ENTER billentyűt, akkor automatikusan feljebb görgeti a szöveget c, Fontosabb üzenetek, melyek küldhetők a vezérlőelemnek Editablak szövegének beállítása: massage = WM SETTEXT wParam = 0 lParam = a ‘ ’ terminált szövegre mutató pointer Példa: Írjuk ki a “Hello Windows” szöveget az edit ablakba. strcpy(szText,”Hello Windows”) SendMessage(EdithWnd,WM SETTEXT,0,(LPARAM)(LPSTR)szText) Megjegyzés: a WM SETTEXT nemcsak a szövegablaknál, hanem bármilyen ablaknál használható. Ha nem vezérlőelemnek küldjük, hanem egy közönséges ablaknak, akkor az ablak fejlécét állítja be. A maximálisan beírható szöveghossz megadása: message = EM LIMITTEXT wParam = a maximálisan beíható karakterek száma lParam = 0 d, Fontosabb notification üzenetek EN CHANGE: az ablak tartalma megváltozott EN KILLFOCUS: a fókusz lekerült a vezérlőelemről EN SETFOCUS: a fókusz rákerült a
vezérlőelemre Statikus ablak (static) Előzetes verzió - 2001.0420 44 Windows programozás VC++ környezetben (Automatizálási Tanszék) 2001 a, Ablakosztály neve: “STATIC” b, Fontosabb megadható stílusjellemzők SS BITMAP: nem szöveget, hanem egy bitmap-et jelenít meg SS ICON: nem szöveget, hanem egy ikont jelenít meg SS LEFT, SS CENTER, SS RIGHT: szöveg igazítása az adott módon c, Fontosabb üzenetek, melyek küldhetők a vezérlőelemnek A WM SETTEXT használható a szöveg beállítására. d, Fontosabb notification üzenetek . Gomb (button) a, Ablakosztály neve: “button” b, Fontosabb megadható stílusjellemzők BS PUSHBUTTON: nyomógombot hoz létre BS CHECKBOX: kiválasztógombot hoz létre BS AUTOCHECKBOX: olyan kiválasztógombot hoz létre, ami automatikusan változtatja állapotát, ha a felhasználó kattint rajra BS RADIOBUTTON: rádiógombot hoz létre BS AUTORADIOBUTTON: olyan rádiógombot hoz létre, ami automatikusan változtatja
állapotát, ha a felhasználó kattint rajra c, Fontosabb üzenetek, melyek küldhetők a vezérlőelemnek Kiválasztó és rádiógomb állapotának beállítása message = BM SETCHECK wParam = BST CHECKED vagy 1: kiválasztott állapot BST UNCHECKED vagy 0: nem kiválasztott állapot lParam = 0; d, Fontosabb notification üzenetek (amiket a vezérlőelem küld) BN CLICKED: a felhasználó kattintott az adott gombon BN KILLFOCUS: a fókusz lekerült az vezérlőelemről BN SETFOCUS: a fókusz rákerült az vezérlőelemre Listaablak (listbox) a, Ablakosztály neve: “BUTTON” b, Fontosabb megadható stílusjellemzők c, Fontosabb üzenetek, melyek küldhetők a vezérlőelemnek Előzetes verzió - 2001.0420 45 Windows programozás VC++ környezetben (Automatizálási Tanszék) 2001 A listablak feltöltése: message = LB ADDSTRING wParam = 0 lParam = a ‘ ’ terminált szövegre mutató pointer, amit hozzá akarunk adni a listaablakhoz Az aktuálisan kiválasztott elem
lekérdezése: message = LB GETCURSEL wParam = 0 lParam = 0 A SendMessage ill. a SendDlgItemMessage visszatérési értéke: az aktuálisan kiválasztott elem indexe Példa: töltsük fel egy listaablakot. char *sString[5]={ {"Single-Column"}, {"Tabulator"}, {"Graph"}, {"Main/Subform"}, {"AutoForm"}, }; for (i=0;i<5;i++) SendMessage(ListhWnd,LB ADDSTRING,0,(LPARAM)(LPSTR)sString[i]); Kombinált ablak (combobox) a, Ablakosztály neve: “COMBOBOX” b, Fontosabb megadható stílusjellemzők c, Fontosabb üzenetek, melyek küldhetők a vezérlőelemnek A feltöltése ugyanúgy történik, mint a listaablaknál, csak a LB ADDSTRING helyett az CB ADDSTRING üzenetet kell használni. Görgerősáv (scrollbar) a, Ablakosztály neve: “SCROLLBAR” A görgetősávok kezelése némiképp eltér a többi vezérlőelemétől. A tulajdonságait a SetScrollInfo függvénnyel tudjuk állítani: állítható a görgetési tartomány hossza,
minimuma, maximuma, az aktuális pozíció, stb. A többi vezérlőelemmel ellentétben nem WM COMMAND üzenetet küld, ha valamilyen változás következett be (pl. a felhasználó görgette), hanem WM HSCROLL és WM VSCROLL üzeneteket, ahol a wParam paraméterben van benne, hogy ténylegesen mi is történt. Ekkor nekünk kell a görgetősáv megváltozott pozíciójának megfelelően kirajzolni az ablak tartalmát. 10.4 Ablakokat kezelő néhány hasznos függvény A fent említett összes ablak esetén le tudjuk kérdezni az ablak szülőjét a HWND GetParent(HWND) függvénnyel. Minden ablakot lehet tiltani (disable) vagy engedélyezni (enable) a következő függvénnyel. BOOL EnableWindow(HWND, BOOL) Előzetes verzió - 2001.0420 46 Windows programozás VC++ környezetben (Automatizálási Tanszék) 2001 Ablak látható-e: BOOL IsWindowVisible(HWND) Az ablak engedélyezve van-e: BOOL IsWIndowEnabled(HWND) Közönséges ablaknál az ablak fejlécének,
vezérlőelemnél a vezérlőelem szövegének a lekérdezése: int GetWindowText (HWND, LPSTR, int) Közönséges ablaknál az ablak fejlécének, vezérlőelemnél a vezérlőelem szövegének a beállítása (ugyanaz, mintha WM SETTEXT üzenetet küldenénk): void SetWindowText(HWND,LPSTR) Közönséges ablaknál az ablak fejlécének, vezérlőelemnél a vezérlőelem szöveghosszának lekérdezése: int GetWindowTextLength(HWND) 11. Dialógusablakok A Windows-ban a dialógusablakok fontos szerepet játszanak. Ezeken keresztül történik a párbeszéd a felhasználó és a program között. A dialógusablak egyszerre erőforrás és ablak, így rendelkezik mindkettő tulajdonságaival. A dialógusablak definíciója, beállításai az erőforrás fájlban szerepelnek Pl: DIALOGUS ABLAK DIALOG 18, 18, 165, 104 STYLE WS OVERLAPPED | WS VISIBLE | WS CAPTION | WS SYSMENU CAPTION "GYERMEK ABLAK" BEGIN EDITTEXT ID MEZO, 13, 27, 122, 12, ES LEFT|WS CHILD| WS VISIBLE |WS
BORDER|WS TABSTOP CONTROL "",ID COMBOBOX,"COMBOBOX",CBS DROPDOWN|WS CHILD| WS VISIBLE | WS VSCROLL | WS TABSTOP, 13, 43, 120, 33 CHECKBOX "Check box", ID CHECKBOX, 111, 56, 50, 12, WS CHILD | WS VISIBLE | WS TABSTOP RADIOBUTTON "Radio button", ID RADIOBUTTON, 111, 71, 51, WS CHILD | WS VISIBLE | WS TABSTOP CONTROL "OK", ID OK, "BorBtn", BS PUSHBUTTON | WS CHILD | WS VISIBLE | WS TABSTOP, 14, 75, 32, 20 CONTROL "Cancel", ID CANCEL, "BorBtn", BS PUSHBUTTON | WS CHILD | WS VISIBLE | WS TABSTOP, 52, 75, 32, 20 LTEXT "Írja ide a szöveget :", -1, 11, 15, 126, 8 END 12, A dialógusablakon vezérlőelemeket (controls) szoktunk elhelyezni, ez jól látható a fenti részletben. Ezek az elemek a dialógusablak gyerekablakai, illetve a dialógusablak ezeknek a vezérlőelemeknek a szülőablaka. A dialógusablakok megadása az erőforrásfájlban szövegszerkesztővel nehézkes, nem is kell emlékezni
rá. Ehelyett az erőforrás szerkesztőt (Resource Editor) használjuk, aminek segítségével vizuálisan tudjuk a dialógusablakot megszerkeszteni, a vezérlőelemeket elhelyezni és tulajdonságaikat (stílusjellemzők) beállítani. Természetesen a Visual C++ (Developer Studio) is rendelkezik erőforrás szerkesztővel: Vezérlőelem paletta, innen drag-and-drop módon tudjuk elhelyezni a vezérlőelemeket a dialógus ablakon Előzetes verzió - 2001.0420 47 Windows programozás VC++ környezetben (Automatizálási Tanszék) Vezérlőelem ill. erőforrás azonosító 2001 Stílusjellemzők megadása A szerkesztett dialógus ablakokhoz tartozó események kezeléséről nekünk kell gondoskodnunk a programból. Mivel a dialógusablak ablak, tartozik hozzá egy ablakkezelő függvény, ami gondoskodik az ablak üzeneteinek kezeléséről. Ezt az ablakkezelő függvényt nekünk kell megírnunk A dialógusablakon elhelyezett vezérlőelemek ún. értesítő üzeneteket
(notification messages) küldenek a dialógusablaknak, ha valamilyen speciális esemény történik velük. Pl egy gomb küld egy BN CLICKED típusú értesítő üzenetet a dialógusablaknak (vagyis az szülőjének), ha a felhasználó kattintott rajta. A dialógusablakon belül minden vezérlőelem egy egyedi azonosítóval van ellátva. Ennek az azonosítónak a segítségével tudjuk megkülönböztetni az elemeket egymástól, hogy az éppen feldolgozás alatt levő üzenet kitől érkezett. Két dialógusablak típus létezik: modális és nem-modális. A modális ablak esetén addig nem válthatunk át egy másik ablakra, amíg a dialógusablak nem fejezi be a működését. Mint említettem a menüknél, a “.” jelű menük arra utalnak, hogy kiválasztásukkor egy dialógusablak jelenik meg Előzetes verzió - 2001.0420 48 Windows programozás VC++ környezetben (Automatizálási Tanszék) 11.1 2001 Modális dialógusablakok A dialógusablak megjelenítése és
megszüntetése A modális dialógusablak megjelenítésére a DialogBox (vagy a DialogBoxParam) függvény használható: int DialogBox( HINSTANCE hInstance, DLGPROC lpDialogFunc ); LPCTSTR lpTemplate, HWND hWndOwner, A hInstance az alkalmazás leírója. A lpTemplate a dialógusablak név vagy számazonosítója (ha számazonosítót használunk, akkor használni kell a MAKEINTRESOURCE makrót). A hWndOwner a dialógusablak owner ablakának leírója, ami általában az alkalmazásunk főablaka. A lpDialogFunc paraméterben kell megadni a dialógusablak ablakkezelő függvényének a címét. A DialogBox függvény mindaddig nem tér vissza, amíg a dialógusablakot be nem zárjuk. A dialógusablak bezárására az EndDialog függvény szolgál, amit a dialógusablak ablakkezelő függvényéből szoktunk meghívni pl. akkor, ha a felhasználó kattintott egy megfelelő (pl OK, Cancel) gombon. A DialogBox visszatérési értéke megegyezik az EndDialog függvény második
argumentumával Példa Az IDM SHOWDEMODIALOG azonosítójú menüelem IDD DIALOG DEMO azonosítójú dialógusablakot modálisan: kiválasztásakor jelenítsük meg a // a főablak ablakkezelő függvényében . case WM COMMAND: switch( LOWORD( wParam ) ) { case IDM SHOWDEMODIALOG: nRes = DialogBox( hInst, MAKEINTRESOURCE(IDD DIALOG DEMO), hWnd, DlgProc); break; break; } . A dialógusablak eseményeinek kezelése A dialógusablak üzeneteit, valamint a dialógusablakon levő gyerekablakok (control-ok) notification üzeneteit a dialógusablak ablakkezelő függvénye kapja meg. Az ablakkezelő függvényből TRUE-val (1el) kell visszatérni, ha az adott üzenetet feldolgoztuk, és FALSE-al (0-val), ha nem a) Példa Az IDOK azonosítójú gomb megnyomásakor zárjuk be a dialógusablakot. // a dialógusablak ablakkezelő függvénye BOOL CALLBACK DlgProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) { switch (message) { case WM COMMAND: Előzetes verzió - 2001.0420 49
Windows programozás VC++ környezetben (Automatizálási Tanszék) 2001 switch( LOWORD(wParam) ) { case IDOK: // ide akkor kerul a futas, ha az ok gombra // kattintottak EndDialog( hDlg, 0 ); // dialogusablak bezarasa return TRUE; } } return FALSE; } Dialógusablak inicializálása A dialógusablak kap egy WM INITDIALOG üzenetet még mielőtt megjelenne a képernyőn. Ennek az üzenetnek a kezelésében szoktuk a dialógusablakon levő vezérlőelemeket inicializálni: szövegablakok tartalmát beállítani, kiválasztó és rádió gombok kiválasztott állapotát beállítani, lista és kombinált ablakot feltölteni, stb. Példa A teljes példa megtalálható a példák között a DlgDemo könyvtárban, a main.cpp, resourceh és a dlgdemo.rc fájlt kell megnézni Mivel az alkalmazás egy főablakból és egy dialógusablakból áll, a következő részeket kell megíni: Erőforrás és control azonosítók a resource.h fájlban Erőforrások definiálása az RC fájlban A
cpp forrásban: - WinMain függvény – nagyjából ugyanaz, mint a Hello World példánál, csak itt menüt is adunk meg az ablakosztály regisztrálásánál - Főablak ablakkezelő függvénye - Dialógusablak ablakkezelő függvénye – ez a lényeg Előzetes verzió - 2001.0420 50 Windows programozás VC++ környezetben (Automatizálási Tanszék) 2001 A resource.h fájl #define IDR MENU1 101 #define IDD DIALOG DEMO 102 #define #define #define #define #define #define #define #define -1 1000 1001 1002 1003 1004 1005 1007 IDC STATIC IDC EDIT TEXT IDC EDIT NUMBER IDC CHECK1 IDC RADIO1 IDC RADIO2 IDC BUTTON TEST IDC EDIT LOG #define IDM SHOWDEMODIALOG 40002 DLGDemo.RC fájl #include "windows.h" #include "resource.h" // Menu MAINMENU MENU BEGIN POPUP "&Demo" BEGIN MENUITEM "&Dialog", Előzetes verzió - 2001.0420 IDM SHOWDEMODIALOG 51 Windows programozás VC++ környezetben (Automatizálási Tanszék) 2001 END
END // Dialog IDD DIALOG DEMO DIALOGEX 0, 0, 207, 122 STYLE DS MODALFRAME | WS POPUP | WS VISIBLE | WS CAPTION | WS SYSMENU EXSTYLE WS EX STATICEDGE CAPTION "Dialog" FONT 8, "MS Sans Serif" BEGIN DEFPUSHBUTTON "OK",IDOK,150,7,50,14 PUSHBUTTON "Cancel",IDCANCEL,150,24,50,14 LTEXT "Edit Text",IDC STATIC,7,7,31,8 EDITTEXT IDC EDIT TEXT,7,17,40,14,ES AUTOHSCROLL LTEXT "Edit Number",IDC STATIC,7,39,42,8 EDITTEXT IDC EDIT NUMBER,7,49,40,14,ES AUTOHSCROLL | ES NUMBER CONTROL "Check1",IDC CHECK1,"Button",BS AUTOCHECKBOX | WS TABSTOP,7,79,41,10 CONTROL "Radio1",IDC RADIO1,"Button",BS AUTORADIOBUTTON | WS GROUP,68,7,39,10 CONTROL "Radio2",IDC RADIO2,"Button",BS AUTORADIOBUTTON,67,17,39, 10 PUSHBUTTON "Show state",IDC BUTTON TEST,7,101,53,14,BS NOTIFY EDITTEXT IDC EDIT LOG,69,44,131,65,ES MULTILINE | ES AUTOHSCROLL | ES READONLY | WS VSCROLL END DlgDemo.cpp fájl
#include <windows.h> #include <stdio.h> #include "resource.h" // ezt hasznald Visual C++ kornyezetben es ne a Windows.h-t // #include "stdafx.h" // peldany leiro HINSTANCE hInst; // ablakkezelo fuggvenyek deklaracioja LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); BOOL CALLBACK DlgProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam); // egyeb sajat fuggvenyesk deklaracioi void LogText( HWND hDlg, char* pText ); int APIENTRY WinMain(HINSTANCE HINSTANCE LPSTR int { hInstance, hPrevInstance, lpCmdLine, nCmdShow) HWND hWnd; Előzetes verzió - 2001.0420 52 Windows programozás VC++ környezetben (Automatizálási Tanszék) 2001 MSG msg; char* szAppName = "Hello Word"; hInst = hInstance; // ------------ ablakosztaly regisztralasa ------------- WNDCLASSEX wcex; wcex.cbSize = sizeof(WNDCLASSEX); wcex.lpszClassName = szAppName; // ablakosztaly neve wcex.style = CS HREDRAW | CS VREDRAW; wcex.lpfnWndProc = WndProc; //
ablakkezelo fuggveny cime wcex.hInstance = hInstance; wcex.hIcon = LoadIcon(hInstance, (LPCTSTR)IDI APPLICATION); wcex.hCursor = LoadCursor(NULL, IDC ARROW); wcex.hbrBackground = (HBRUSH)GetStockObject(WHITE BRUSH); wcex.lpszMenuName = "MAINMENU"; // a menu nevenek megadasa wcex.hIconSm = LoadIcon(hInstance, (LPCTSTR)IDI WINLOGO); wcex.cbClsExtra = 0; wcex.cbWndExtra = 0; RegisterClassEx(&wcex); // --------------- foablak letrehozasa ------------------- hWnd = CreateWindow( szAppName, // ablakosztaly neve "Hello Word", // ablak cime WS OVERLAPPEDWINDOW,// ablak stilusa CW USEDEFAULT, // x pozicio CW USEDEFAULT, // y pozicio CW USEDEFAULT, // x meret CW USEDEFAULT, // w meret NULL, // szulo ablak leiro NULL, // menu leiro hInstance, // peldany leiro NULL); // lertehozasi parameter if (!hWnd) return FALSE; // --------------- foablak megjelenitese -----------------ShowWindow(hWnd, nCmdShow); UpdateWindow(hWnd); // --------------- uzenetkezelo ciklus
-----------------while (GetMessage(&msg, NULL, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); } return msg.wParam; } // főablak ablakkezelő függvénye Előzetes verzió - 2001.0420 53 Windows programozás VC++ környezetben (Automatizálási Tanszék) 2001 LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { UINT nRes; switch (message) { case WM COMMAND: switch( LOWORD( wParam ) ) { // ha az IDM SHOWDEMODIALOG menuelemet valasztottak ki case IDM SHOWDEMODIALOG: nRes = DialogBox( hInst, MAKEINTRESOURCE(IDD DIALOG DEMO), hWnd, DlgProc); if ( nRes == 1 ) MessageBox( hWnd, "DialogBox returned: OK clicked", "DlgDemo", MB OK); else if ( nRes == 0 ) MessageBox( hWnd, "DialogBox returned: Cancel clicked", "DlgDemo", MB OK); break; } break; case WM DESTROY: PostQuitMessage(0); break; default: return DefWindowProc(hWnd, message, wParam, lParam); } return 0; } // Egy saját függvény, a DlgProc
függvény használja char LogBuff[20000]; // a pText parameterben megkapott szoveget hozzafuzi // a hDlg leiroju dialogusablak IDC EDIT LOG szovegablakahoz // egy uj sorba, a sorok elejere beirja a sor szamat is void LogText( HWND hDlg, char* pText ) { static int nLineNo = 0; nLineNo++; char numBuff[10]; sprintf( numBuff, "%d. ", nLineNo ); // a sor elejere a szamot strcat( LogBuff, numBuff); // a sor lenyegi tartalma strcat( LogBuff, pText ); strcat( LogBuff, " " ); SendDlgItemMessage( hDlg, IDC EDIT LOG, WM SETTEXT, 0, (WPARAM)LogBuff ); SendDlgItemMessage( hDlg, IDC EDIT LOG, WM VSCROLL, SB BOTTOM, 0); } Előzetes verzió - 2001.0420 54 Windows programozás VC++ környezetben (Automatizálási Tanszék) 2001 // a dialógusablak ablakkezelő függvénye BOOL CALLBACK DlgProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) { switch (message) { // inicializalas a dialogusablak megjelenitese elott case WM INITDIALOG: LogBuff[0] = ; // az ID EDIT
TEXT szovegablak tartalma legyen "szoveg" SetDlgItemText( hDlg, IDC EDIT TEXT, "szoveg" ); // maximum 100 karaktert lehessen beirni SendDlgItemMessage( hDlg, IDC EDIT TEXT, EM LIMITTEXT, 100, 0 ); // az ID EDIT TEXT szovegablak tartalma legyen "999" SetDlgItemInt( hDlg, IDC EDIT NUMBER, 999, TRUE); // maximum 6 karaktert lehessen beirni SendDlgItemMessage( hDlg, IDC EDIT NUMBER, EM LIMITTEXT, 6, 0 ); // az ID CHECK1 kivalasztogomb legyen kipipalva SendDlgItemMessage( hDlg, IDC CHECK1, BM SETCHECK, 1, 0 ); return TRUE; break; case WM COMMAND: switch( LOWORD(wParam) ) case IDOK: // ide akkor kerul // kattintottak EndDialog( hDlg, 1 return TRUE; case IDCANCEL: // ide akkor kerul // kattintottak EndDialog( hDlg, 0 return TRUE; case IDC BUTTON TEST: { a futas, ha az ok gombra ); // dialogusablak bezarasa a futas, ha a cancel gombra ); // dialogusablak bezarasa // esemeny az IDC BUTTON TEST //gombtol if ( HIWORD(wParam) == BN CLICKED ) { BOOL fRes; char
szCheckRes[4]; // info IDC CHECK1-rol, a visszateresi ertek: ki //van-e pipalva fRes = SendDlgItemMessage( hDlg, IDC CHECK1, BM GETCHECK, 0, 0); if ( fRes ) strcpy( szCheckRes, "yes" ); else strcpy( szCheckRes, "no" ); char szRes[101]; GetDlgItemText( hDlg, IDC EDIT TEXT, szRes, 100 ); int nRes; nRes = GetDlgItemInt( hDlg, IDC EDIT NUMBER, Előzetes verzió - 2001.0420 55 Windows programozás VC++ környezetben (Automatizálási Tanszék) 2001 NULL, FALSE ); char buff[200]; sprintf( buff, "Check1 checked: %s; Edit Num: %d; Edit Text: %s", szCheckRes, nRes, szRes ); MessageBox( hDlg, buff, "DlgDemo", MB OK); } return TRUE; case IDC EDIT TEXT: if ( HIWORD(wParam) == EN CHANGE ) LogText( hDlg, "Edit text has changed" ); if ( HIWORD(wParam) == EN KILLFOCUS ) LogText( hDlg, "Edit text lost focus" ); return TRUE; case IDC EDIT NUMBER: if ( HIWORD(wParam) == EN CHANGE ) LogText( hDlg, "Edit num has changed" ); if (
HIWORD(wParam) == EN KILLFOCUS ) LogText( hDlg, "Edit num lost focus" ); return TRUE; return TRUE; break; } } return FALSE; } A dialógusablakon levő gyermekablakok SendDlgItemMessage-t használjuk. kezelésére gyakran a SendMessage helyett a SendDlgItemMessage(HWND hDlg, UINT nID, UINT iMes, WPARAM wParam, LPARAM lParam) ,ami a hDlg dialógusablakon levő nID azonosítójú vezérlőelemnek elküldi az iMes üzenetet, az üzenet paraméterei wParam és lParam lesznek. Egy dialógusablakon levő vezérlőelem (pl. edit) szövegének beállítása: SetDlgItemText(HWND hDlg, UINT nID, LPSTR lpszSoveg ) Egy dialógusablakon levő vezérlőelem (pl. edit) szövegének lekérdezése: GetDlgItemText(HWND hDlg, UINT nID, LPSTR lpszBuffer, UINT cbBuf) ,ahol az lpszBuffer-be másolja be az ablak tartalmát, cbBuff-ban a buffer méretét adjuk meg bájtokban. Ha a vezérlőelem egész számot tartalmaz, kényelmesebb az alábbi függvények használata: SetDlgItemInt(HWND
hDlg, UINT nID, int n, BOOL bSigned) ,ami az n paraméterben megadott számot írja be a vezérlőelembe. A bSigned paraméterrel azt tudjuk megadni, hogy a számot előjelesen kell-e értelmezni UINT GetDlgItemInt(HWND hDlg, UINT nID, BOOL *lpTranslated, BOOL bSigned) ,aminek visszatérési értéke a szövegablak tartalma számmá konvertálva. A bSigned paraméterrel azt tudjuk megadni, hogy a számot előjelesen kell-e értelmezni, az lpTranslated pedig arra vonatkozólag ad információt, hogy sikerült-e az ablak tartalmát szöveggé konvertálni vagy nem (ez tehát egy kimenő paraméter), ha ez nem érdekel minket adjunk meg ennek a paraméternek NULL értéket. Előzetes verzió - 2001.0420 56 Windows programozás VC++ környezetben (Automatizálási Tanszék) 2001 Egy dialógusablakon levő vezérlőelem ablakleírójának lekérdezése: HWND GetDlgItem(HWND hDlg, UINT nID) A fókusszal rendelkező ablak leírójának lekérdezése: HWND GetFocus() A szülőablak
ablak leírójának lekérdezése: HWND GetParent(HWND hDlg) 11.2 Nem-modális dialógusablakok A modális dialógusablakokkal ellentétben a nem-modális ablakok esetén lehetőség van arra, hogy a felhasználó egy másik ablakra váltson át. A nem-modális ablakok létrehozására nem a DialogBox-ot használjuk, hanem a CreateDialog (vagy a CreateDialogParam) függvényt. A paraméterek értelmezése ugyanaz, mint a DialogBox esetében. A visszatérési érték ebben az esetben a létrehozott ablak leírója. Általában ez a leíró egy globális változó szokott lenni, hiszen több helyen lehet szükségünk rá. Lehetőség van arra, hogy az üzenetkezelő ciklust módosítsuk annak érdekében, hogy amennyiben az üzenet a nem-modális ablaknak szól, akkor azt közvetlenül küldjük neki: hDlgModeless=CreateDialog(hInstance, “DIALOG 2”, hwnd, hAccel=LoadAccelerators(hInstance, “MyAccel”); while (GetMessage (&msg, NULL, 0, 0)) { MyDlgProc );
if(hDlgModeless==0 ||!IsDialogMessage(hDlgModeless,&msg)) { if(!TranslateAccelerator(hwnd,hAccel,&msg) { TranslateMessage (&msg) ; DispatchMessage (&msg) ; } } } Ha az üzenet a nem-modális ablakhoz érkezik, akkor az IsDialogMessage függvény ennek az ablaknak a kezelő függvényének küldi, és ilyenkor nem kerül sor a TranslateMessage és DispatchMessage meghívására. A nem-modális ablak bezárása előtt gondoskodni kell az ablak megsemmisítéséről a következőképpen: DestroyWindow(hDlgModeless); hDlgModeless=0; 11.3 Közös dialógusablakok (Common Dialogs) használata A Windows rendszerben léteznek előre definiált, ún. közös dialógusablakok Ezek a Find, Replace, FileOpen, FileSave, Print, PageSetup, Font, Color ablakok. Pl a File Open: Előzetes verzió - 2001.0420 57 Windows programozás VC++ környezetben (Automatizálási Tanszék) 2001 Lehetőség van arra, hogy ezeket saját programunkból használjuk. Az ablak megjelenítése
előtt fel kell tölteni egy megfelelő struktúrát, amiben a dialógusablakot kell inicializálni, az adott dialógusablakhoz tartozó információkat lehet megadni. Ennek a struktúrának a címét kell átadni annak a függvénynek, ami az ablakot megjeleníti. Az egyes dialógusablakokat megjelenítő függvények: HWND HWND BOOL BOOL BOOL BOOL BOOL BOOL FindText(FINDREPLACE *) ReplaceText(FINDREPLACE *) ChooseColor(CHOOSECOLOR *) PrintDlg(PRINTDLG *) PageSetupDlg (PAGESETUPDLG* ) GetOpenFileName(OPENNFILENAME *) GetSaveFileName(OPENNFILENAME *) ChooseFont(CHOOSEFONT *) A dialógusablakok használatának az a célja, hogy valamilyen információt kérjen be a felhasználótól. Ez pl. a FileOpen esetében a fájl neve, a ChooseFont esetében egy betűtípus, stb Ezt az információt a Windows a paraméterként átadott struktúra megfelelő tagjaiba írja bele, amikor a felhasználó bezárja a dialógusablakot. Pl a FileOpen esetében a lpstrFileTitle tagban lesz benne a fájl
neve, az lpstrFile tagban pedig a teljes útvonal. A dialógusablakokat megjelenítő függvények visszatérési értéke TRUE, ha a felhasználó az ablakot az OK gombbal zárta be és FALSE egyébként. Ezek megvalósításához szükségünk van arra, hogy lefoglaljuk a megfelelő méretű memóriát, inicializáljuk a struktúrát és meghívjuk a megfelelő függvényt. Az említett struktúráknak a felépítése megtalálható az MDSN Library-ben. A továbbiakban olyan példát mutatunk, amely illusztrálja a GetOpenFileName és GetSaveFileName működését. #include #include #include #include <windows.h> <commdlg.h> <stdlib.h> "filedemo.h" extern char *szAppName; Előzetes verzió - 2001.0420 58 Windows programozás VC++ környezetben (Automatizálási Tanszék) 2001 #define UNTITLED "(untitled)" static OPENFILENAME ofn ; void PopFileInitialize (HWND hwnd) { static char *szFilter[]={ "Special Files
",".dat;*.sim", "All Files (*.*)", ".*","" } ; ofn.lStructSize ofn.hwndOwner ofn.hInstance ofn.lpstrFilter ofn.lpstrCustomFilter ofn.nMaxCustFilter ofn.nFilterIndex ofn.lpstrFile ofn.nMaxFile ofn.lpstrFileTitle ofn.nMaxFileTitle ofn.lpstrInitialDir ofn.lpstrTitle ofn.Flags ofn.nFileOffset ofn.nFileExtension ofn.lpstrDefExt ofn.lCustData ofn.lpfnHook ofn.lpTemplateName } = = = = = = = = = = = = = = = = = = = = sizeof (OPENFILENAME) ; hwnd ; NULL ; szFilter[0]; NULL ; 0 ; 0 ; NULL;//Set in Open and Close func. MAX PATH ; NULL;//Set in Open and Close func. MAX FNAME + MAX EXT ; NULL ; NULL ; 0;// Set in Open and Close func. 0 ; 0 ; "Sim" ; 0L ; NULL ; NULL ; BOOL PopFileOpenDlg (HWND hwnd, LPSTR lpstrFileName, LPSTR lpstrTitleName) { ofn.hwndOwner = hwnd ; ofn.lpstrFile = lpstrFileName ; ofn.lpstrFileTitle = lpstrTitleName ; ofn.Flags = OFN CREATEPROMPT ; return GetOpenFileName (&ofn) ; } BOOL PopFileSaveDlg (HWND hwnd,
LPSTR lpstrFileName, LPSTR lpstrTitleName) { ofn.hwndOwner = hwnd ; ofn.lpstrFile = lpstrFileName ; ofn.lpstrFileTitle = lpstrTitleName ; ofn.Flags = OFN OVERWRITEPROMPT ; return GetSaveFileName (&ofn) ; } static long PopFileLength (int hFile) { long lCurrentPos = llseek (hFile, 0L, 1) ; long lFileLength = llseek (hFile, 0L, 2) ; llseek (hFile, lCurrentPos, 0) ; return lFileLength ; } void DoCaption (HWND hwnd, char *szTitleName) { char szCaption [64 + MAX FNAME + MAX EXT] ; Előzetes verzió - 2001.0420 59 Windows programozás VC++ környezetben (Automatizálási Tanszék) 2001 wsprintf (szCaption, "%s - %s", (LPSTR) szAppName, (LPSTR) (szTitleName [0] ? szTitleName : UNTITLED)) ; SetWindowText (hwnd, szCaption) ; } void OkMessage (HWND hwnd, char *szMessage, char szTitleName) { char szBuffer [64 + MAX FNAME + MAX EXT] ; wsprintf (szBuffer, szMessage,(LPSTR) (szTitleName [0] ? szTitleName : UNTITLED)) ; MessageBox (hwnd,szBuffer,szAppName,MB OK| MB
ICONEXCLAMATION) ; } short AskAboutSave (HWND hwnd, char *szTitleName) { char szBuffer [64 + MAX FNAME + MAX EXT] ; short nReturn ; wsprintf (szBuffer, "Save current changes in %s?", (LPSTR) (szTitleName [0] ? szTitleName : UNTITLED)); nReturn = MessageBox (hwnd, szBuffer, szAppName, MB YESNOCANCEL | MB ICONQUESTION) ; if (nReturn == IDYES) if (!SendMessage (hwnd, WM COMMAND, IDM SAVE, 0L)) nReturn = IDCANCEL ; return nReturn ; } Az ofn.lpfnHook paraméter egy úgynevezett Hook függvénynek a címét tartalmazza Ez egy olyan Callback függvény, amely a közös ablak üzeneteit az ablakkezelő függvény előtt kapja meg. Előzetes verzió - 2001.0420 60