- Mire jó a Windows API?
- Részletes információk a számítógépünkről
- Másik program elindítása, a sajátunkból
1, Mire jó a Windows API?
Mivel fognak többet tudni a programjaink, hogyha a Windows API-t használjuk?
Bizonyára mindennapi felhasználóként megismertük a Windows képességeit, pl. böngészés, filmnézés, zenehallgatás, nyomtatás, képszerkesztés, szövegszerkesztés, és még lehetne sorolni mennyi egyéb téren - amennyiben programjainkat ilyen képességekkel szeretnénk felvértezni - szükségünk lesz a Windows API-ra, vagy egy olyan rendszerre, ami ezt elérhetővé teszi számunkra (pl. C++ Builder esetén a VCL keretrendszer ilyen).
Ebben a fejezetben a nyers Windows API-val fogok foglalkozni, és ezen cikkben azon belül is azzal, hogy miként tudhatunk meg több információt a számítógépünkről mint eddig.
Megjegyzés: API = Advanced Programming Interface
1.1, A Windows függvények működése
Amint elkészítjük majd az első programunkat, azt vesszük észre hogy nem több mint párszáz kilobyte - annak ellenére hogy elvileg az összes Windows függvény használatra készen áll a repertoárjában.
Az oka ennek az, hogy az új függvények ezúttal nem a programunkban lesznek tárolva egészen meglepő módon. Ez úgy történhet, hogy a Microsoft, a rendszer tervezésekor bevezette az ún.: "dinamikus csatolású függvénytár", vagy röviden DLL fogalmát. Ennek az a lényege hogy a függvények, egy külön fájlban lesznek tárolva (ezek *.dll fájlok), amiket a program futás közben dinamikusan tud megnyitni, használhatja a benne lévő függvényeket, és mikor már nincs rá szüksége akkor bezárhatja.
Az eddig megszokott séma szerint statikus csatolásról beszélhettünk, mivel a .h fájlokhoz mindig tartozott .c fájl, és ha pl. a math.h fájlt belefordítottuk a programba az ennek megfelelően megnövelte a méretét. A kérdés lehet helyénvaló - most már nincs szükség .h fájlokra?
Az az igazság hogy a dinamikus csatolás jó dolog, de lényegesebben komplikáltabb mint egy .h fájlt include-olni, ezáltal a statikus csatolás lehetőségét továbbra is megtartották - ilyenkor a program automatikusan nyitja meg a .dll fájlt, az összes függvényhez csinál elérést amire hivatkozunk egy .h fájlban, és bezáráskor a .dll fájlt is zárja. Így lehetővé válik az is, hogy a továbbiakban a dinamikus csatolás helyett, a klasszikus módon férjünk ezekhez a függvényekhez:
#include <windows.h>
Természetesen ezúttal is dinamikus csatolásról beszélhetünk, de ezt a programunk végzi majd a mi segítségünk nélkül indításkor, és leálláskor.
1.2, A Windows rendszer felépítése
A Windows alapvetően egy többrétegű rendszer, az egyes rétegek pedig pont ilyen .dll fájlokban vannak kidolgozva, és sokszor szoros kapcsolatban/kommunikációban állnak egymással (az ábrán a Windows NT felépítése látható):
A Windows egyik nagy különbsége a korábbi operációs rendszerekhez képest, hogy képes több programot is egyszerre futtatni, azaz ez egy multitasking rendszer. Mivel a Microsoft igyekezett a kompatibilitást megtartani a régebbi rendszerekkel, így sok MS-DOS jellegű dolog is megmaradt, mint ezt később látni fogjuk - de alapvetően elmondható, hogy a multitasking kivitelt úgy éri el a rendszer, hogy minden alkalmazás külön címterületet kap a memóriában, ami programozási szempontból azt jelenti hogy az alkalmazások nem férnek hozzá közvetlenül egymás adataihoz - így ha két, vagy több alkalmazás összehangolására kerülne a sor (interprocess communcation, IPC), az mindig elég bonyolult lesz.
Az is elmondható hogy a harver, és a programok között a rendszermag (kernel) fog összhangot biztosítani. A kernel módú oldalon lévő dolgok egy átlagos felhasználónak nem mindig látszanak, ilyen feladatok pl.:
-
a processzoridő elosztása, hiszen pl. egy processzor, egy időben három programot képtelen futtatni, ezért az idejét el kell osztani
-
az egyes erőforrásokhoz való hozzáférés korlátozása - ne állhasson neki két program egyszerre nyomtatni, a második várja meg az elsőt
-
az imént említett memóriaszervezés
-
a hardverek kezelése - pl. a billentyűleütések, egérmozdulatok továbbítása az illetékes programoknak, stb...
-
stb...
Természetesen amint az ábrán is látható a kernel módú oldal is több alegységből áll ezek közül mi főként kettővel fogunk foglalkozni a programozás során - az felhasználói felület kezelőjével (user32.dll), és a grafikai eszközinterfésszel (gdi32.dll).
Azért lesz ezekre szükségünk, mert felhasználói módú alkalmazásokat fogunk írni, amik programozása során a rendszermaggal közvetlen kapcsolatba nem kell lépni, ezen két modullal is csak azért, mivel hát mégis csak a grafikával/ablakokkal kapcsolatos.
1.2, A Windows illesztőprogram rendszere
Hogyan lehetséges az, hogy beteszünk egy új videókártyát a gépbe - aminek az előzőtől teljesen más a felépítése, és egy internetről letöltött / CD-n mellékelt telepítőcsomag után már is játszhatunk a legújabb játékokkal?
A korábbiakban, pl. a Borland C/C++ grafikai képességeit elemezve láthattuk, hogy MS-DOS alatt minden programnak, minden hardverhez egyedi kezelőprogramokat kellett magával hoznia (nálunk ezek a .BGI fájlok voltak), - és ha a hardverünk ritka volt mint a fehér holló, akkor elég kevés program volt ami képes volt kiaknázni a képességeit.
A Windows alatt ez egy kicsit máshogy történik. MS-DOS alatt, az operációs rendszer közvetlen hozzáférést adott a hardverhez, így azt lehetett csinálni amit akartunk, nyugodtan betölthető volt tetszőleges kezelőprogram pl. egy játékhoz - néha ez hibát is okozott. Korábban már említésre került, hogy a Windows egy védett módú rendszer, azaz a hardverekhez való hozzáférés is korlátozva van, bármikor beavatkozhat - ezt viszont csak egy módon teheti meg - ha rajta keresztül végezzük a különféle műveleteket.
Ez egy nagyon nagy dolog, mert ez azt jelenti hogy bármilyen hardver is van a gépben, ugyanazokkal a függvényhívásokkal, ugyanazokat az eredményeket kapjuk. Ezt úgy éri el a rendszer, hogy szabványosított illesztőprogramokat írhatnak a gyártók a saját termékükhöz, amelynek mint egy adott fogaskeréknek egy gépben, illeszkednie kell a Windows rendszerbe. Ezek az illesztőprogramok már közvetlen hozzáféréssel, elvégezhetik azokat az utasításokat (pl. egy vonal rajzolását), amit az adott program kér a rendszeren keresztül.
Manapság ez az eszköztár egészen széleskörűvé duzzadt pl. egy videókártya esetén (nagyságrendben nagyobb mint amit a Borland C/C++ grafikában tudtunk csinálni), illetve megjelentek olyan szabványok is (mint pl. a VGA) ami szerint egy általános illesztőprogramnak megfelel az adott hardver, azzal működik (még akkor is ha a képességeinek a töredékét használja ki).
Ezek az illesztőprogramok teszik lehetővé azt, hogy a Windows rendszer lényegében képes legyen bármilyen hardverösszeállítás mellett elfutni - hogy ennek mik a korlátai, az csak a gyártókra van bízva.
Megjegyzés: Ahhoz hogy egy gyártó piacra dobhasson egy illesztőprogramot, melyet mindenki könnyedén telepíthet a gépére, több szempontnak is meg kell felelnie:
-
az illesztőprogramnak digitális aláírással kell rendelkeznie
-
az illesztőprogramnak át kell esnie a WHQL szigorú tesztelésén, melyen nem omolhat össze
Ezek a szigorú feltételek garantálják azt, hogy a Windows ne fulladjon kékhalálba minden második program/játék elindításakor - így megfelelő illesztőprogramok esetén, a kékhalál legfeljebb konfigurációs/hardveres hiba miatt állhat elő.
Megjegyzés: A kékhalál (blue screen of death BSoD) akkor jön elő, amikor a kernel módú oldalon valamilyen hiba történik. Amikor egy alkalmazás hibát okoz, legrosszabb esetben morgunk egyet a piros X jelű ablak miatt, és az OK gomb hatására eltűnik. A BSoD esetén, a hiba hardverhez közeli, rendszerszintű, pl. egy adott hardveren végzett érvénytelen művelet miatt - ezért általában ilyenkor a rendszer futását meg kell szakítani, pl. azzal hogy újraindul a gép.
Megjegyzés: A képen látható eszközkezelő, mindenkinek ott van a számítógépén - több helyen is fellelhető:
-
Vezérlőpult -> Rendszer tulajdonságai -> Eszközkezelő
-
Sajátgép/Számítógép/Ez a gép -> jobbklikk az ikonjára, majd Tulajdonságok -> Eszközkezelő
Amik ezen ablakban látható hardverek, azokhoz mind vagy Microsoft által készített, vagy külső gyártó által előállított illesztőprogram van telepítve.
1.3, Néhány ismeretes Windows modul/technológia
Csak néhány (akár általunk is a későbbiekben használt) technológia/modul, a teljesség igénye nélkül:
-
GDI (Graphics Device Interface): különféle rajzolási műveletek megvalósítása, mint a Borland C/C++ grafikai képességeinél - de jelen esetben ez történhet nyomtatóra, .bmp fájlba, stb...
-
GDI+: a GDI továbbfejlesztése, már részleges videókártyás gyorsítással bír, kezeli a tényleges átlátszóságot, a transzformációkat, stb...
-
DWM (Desktop Window Manager): Windows Vista óta van jelen, ez rajzolja ki azokat a látványos effekteket az ablakok lecsukásakor, stb.., illetve az átlátszó ablakkereteket
-
DDE: a programok közötti kommunikációt hivatott megkönnyíteni (IPC), elég elavult technológia, de a mai napig működőképes - a Windows 3.1 programkezelőjével pl. DDE-n keresztül lehetett kommunikálni
-
OLE (Object Linking and Embbedding): alkalmazások közötti együttműködést biztosít, pl. mikor egy WordPad dokumentumba, beszúrunk egy Paint képet - manapság jelentősége kisebb, de a Word 2017-ben is van mind a mai napig Objektum beszúrása... gomb
-
COM (Component Object Model): az OLE kiterjesztése, általa objektumok használhatóak sok programon keresztül is (IPC), pl. egy Word példányt 5 program irányít - hálózati kiterjesztése a DCOM, helyi kiterjesztése a COM+
-
MCI (Media Control Interface): a legelső technológiája a Windowsnak, hang/videó felvételre, lejátszásra, stb... - ennek már van sokkal modernebb változata is a DirectX-en keresztül, amit a Windows Media Player is használ, de annak a programozása is bonyolultabb
-
DirectX: egy nagyon komplex csomag, amelyet a játékok is használnak mind a mai napig - videókártyásan gyorsított médialejátszás, rajzolás, vagy 3D mozgókép előállítása történhet meg általa - több almodulból is áll mint a Direct2D, Direct3D, DirectPlay, DirectShow, ...
Ezen felül a rendszer rengeteg alrészt tartalmaz még, a teljes lista itt található meg:
Mellesleg, a Windows API-val kapcsolatban bármilyen kérdésünk merül fel, azt 90% hogy az MSDN-en (Microsoft Developer Network) megtaláljuk. A jó hír, hogy mivel maga a Windows is C++ nyelven íródott (eredetileg), ezért a példaprogramok, az adatszerkezetek, stb... cuccok leírásait is C++ nyelven fogjuk megtalálni.
1.4, A Windows API elnevezésrendeszere
A Windows API egy saját elnevezésrendszerrel dolgozik, amelyet nem baj ha értünk - azt szolgálná hogy megkönnyítse a dolgunkat. Az utasítások általában nagyon sok paraméterrel dolgoznak majd, sok lesz a struktúra, és a halmaz is - de ettől még nem kell megijedni teljesen. Kezdésként nézzük meg az CreateProcess függvény MSDN-es leírását (másik program indítható el vele):
BOOL WINAPI CreateProcess( _In_opt_ LPCTSTR lpApplicationName, _Inout_opt_ LPTSTR lpCommandLine, _In_opt_ LPSECURITY_ATTRIBUTES lpProcessAttributes, _In_opt_ LPSECURITY_ATTRIBUTES lpThreadAttributes, _In_ BOOL bInheritHandles, _In_ DWORD dwCreationFlags, _In_opt_ LPVOID lpEnvironment, _In_opt_ LPCTSTR lpCurrentDirectory, _In_ LPSTARTUPINFO lpStartupInfo, _Out_ LPPROCESS_INFORMATION lpProcessInformation );
Igen, ez elsőre sokkolónak tűnhet, de ha egy kicsit átnézzük akkor lényegében nem az.
A visszatérési érték az első sorból látható hogy BOOL WINAPI. Ez nem jelent többet, mint hogy a visszatérési értéke logikai típus, azaz használható egy if-else szerkezetben - a WINAPI jelző pedig magára a függvényhívásra jellemző (*.dll készítése esetén ajánlott használni, így a legtöbb programnyelv fogja tudni használni a függvényünket). A legtöbb esetben ilyen típusú visszatérési érték mellett az oldalon ezt találjuk - néhol kiegészítésekkel:
Return Value
If the function succeeds, the return value is nonzero.
If the function fails, the return value is zero. To get extended error
information, call GetLastError.
Ez a továbbiakban a legtöbb BOOL típusú függvényre igaz lesz - de sose baj ha elolvassuk a Return Value részt.
A paraméterek ezúttal jelzőt is kaptak a típus és név mellett, méghozzá hogy milyen jellegű paraméterről beszélünk:
-
_In_: kötelező bemenő paraméter, innen csak olvasni fog - lehet pl. const int i = 5; jellegű változó is
-
_Out_: kötelező kimenő paraméter, ide csak írni fog, ezért olyan változó címét kell megadni, amibe írhat is
-
_Inout_: kötelező kétirányú paraméter, innen írni/olvasni is fog, ezért olyan változó címét kell megadni, amibe írhat is
-
_In_opt_: nem kötelező bemenő paraméter, ha nem akarjuk megadni az értéke lehet NULL
-
_Out_opt_: nem kötelező kimenő paraméter, ha megadjuk egy változó címét amibe írhat is, akkor plusz információkkal leszünk gazdagabbak, de ha ezekre nincs szükségünk lehet az értéke NULL is
-
_Inout_opt_: nem kötelező kétirányú paraméter, megadható változó címe amibe írhat, vagy NULL
A paraméterek típusa egy kicsit érdekesebb - néhány típusunk a BOOL módjára új nevet kapott, néhány pedig struktúrára utal. Sokszor a nevük is elárulja hogy micsodák is azok:
-
LP*STR: ezek valamilyen szöveges típusú adatok, lényegében char*, vagy wchar_t*, vagy egyéb variációk - az LP a local pointer rövidítése (helyi mutató), azt jelenti hogy a saját programban kell legyen a változó, illetve az STR a string, vagyis karakterlánc rövidítése, a köztes betűk adják meg hogy milyen típusú karakterekből álljon a szöveg
-
LPSECURITY_ATTRIBUTES: biztonsági tulajdonságok struktúrája, helyi mutató (LP), tehát ha van is ilyen struktúránk, cím szerint kell átadni - még szerencse hogy opcionális paraméter
-
DWORD: unsigned long int lényegében
-
LPVOID: void* lényegében
-
LPSTARTUPINFO: az indítási adatok struktúrája, helyi mutató (LP), tehát cím szerint kell átadni
-
LPPROCESS_INFORMATION: ebbe fogja elmenteni az elindított program adatait, helyi mutató (LP), tehát cím szerint kell átadni
A paraméterek elnevezése is segít, az első pár betű mindig a típusra utal - ez hasznos ha automata kiegészítéssel dolgozunk. Pl.: bInheritHandles típusa BOOL, dwCreationFlags típusa DWORD - itt a flag szó utal arra hogy halmazról van szó, lejjebb le szokták írni, milyen értékek használhatóak itt.
1.5, Minek ennyi paraméter?
Ugyebár az elmúlt szakaszban éltettem egy kicsit, hogy milyen sokoldalú a rendszer. Mivelhogy ilyen sokoldalú, nem célszerű ugyanarra a feladatra 195 függvényt megírni, elég egy is - de az minden elképzelésre legyen jó. Ennek az a hátránya, hogy ilyen bonyolult paraméterlistákat kapunk, ahol átlag programozóként a paraméterek 60-70%-ka NULL lesz úgyis.
De hát valamit valamiért....
2, Részletes információk a számítógépünkről
Természetesen nem lehet egyből a húrok közé csapni, ez még a C++ nyelven belül is egy külön állatfajta, így egyelőre maradunk a konzol alkalmazásoknál - de most már Windows API függvényekkel is fogunk dolgozni. Az első cikkben, még csak adatot gyűjtünk a saját gépünkről - lényegesen többet mint a beépített C++ függvényekkel tudnánk.
2.1, A mai dátum, és idő
Természetesen erre van beépített C függvény is, de a Windows API-val való barátkozás jegyében nem baj ha megnézünk egy ilyet is, a használt függvény a GetSystemTime lesz:
#include <stdio.h> #include <windows.h> const char* aszHetNapjai[] = {"vasarnap", "hetfo", "kedd", "szerda", "csutortok", "pentek", "szombat"}; int main() { SYSTEMTIME ido; GetSystemTime(&ido); printf("A pillanatnyi datum/ido: %04d.%02d.%02d, %s, %02d:%02d:%02d.%04d", ido.wYear, ido.wMonth, ido.wDay, aszHetNapjai[ido.wDayOfWeek], ido.wHour, ido.wMinute, ido.wSecond, ido.wMilliseconds); getchar(); return 0; }
A jó hír hogy a Windows API-ban megírt kódok lényegében minden programnyelvben ugyanúgy néznek ki - íme ugyanez Pascal nyelvben (csak a kiíratás, stb... kapcsolatos parancsok térnek el szinte):
program prjDatumIdo; uses SysUtils, Windows; const aszHetNapjai: array [0..6] of string = ('vasárnap', 'hétfő', 'kedd', 'szerda', 'csütörtök', 'péntek', 'szombat'); var ido: SYSTEMTIME; begin GetSystemTime(ido); WriteLn(format('A pillanatnyi datum/ido: %04d.%02d.%02d, %s, %02d:%02d:%02d.%04d', [ ido.wYear, ido.wMonth, ido.wDay, aszHetNapjai[ido.wDayOfWeek], ido.wHour, ido.wMinute, ido.wSecond, ido.wMilliseconds])); ReadLn; end.
Tehát eddig végül is nem volt sokkal bonyolultabb, mint egy beépített C/C++ függvényt használni erre a célra - az újdonság a .h fájlban, és a típusokban van most még - nagyjából a következő példákra is igaz lesz ez.
2.2, A számítógép fontos elérési útjai/szöveges adatai
A teljesség igénye nélkül, íme hogy tudhatjuk meg pl. hova helyezhetünk el ideiglenes fájlokat, vagy hova van telepítve a Windows rendszer. Habár az utasítások LPSTR típust kérnek, jól látható hogy ez a megszokott char*-al egyenérékű. Mellesleg az általunk használt utasítások a GetWindowsDirectory, a GetTempPath, és a GetSystemDirectory - de még jópár ilyet lehet találni a rendszerben.
#include <stdio.h> #include <windows.h> int main() { char szWinDir[4096] = {0}, szTempPath[4096] = {0}, szSystemRoot[4096] = {0}; GetWindowsDirectory(szWinDir, sizeof(szWinDir)); GetTempPath(sizeof(szTempPath), szTempPath); GetSystemDirectory(szSystemRoot, sizeof(szSystemRoot)); printf("A Windows telepitesi mappaja: \"%s\"", szWinDir); printf("\nAz ideiglenes fajlok mappaja: \"%s\"", szTempPath); printf("\nA rendszermappa helye: \"%s\"", szSystemRoot); getchar(); return 0; }
Jelen esetben a fordítási beállítások miatt a program végig ANSI karakterkódolással dolgozik, így pl. egy orosz/kínai Windows alatt nem futna le helyesen. Ilyenkor wchar_t típust kell használni és az utasítások W jelű változatát, lásd GetWindowsDirectoryW, .... - ha pedig mindenféleképpen ki szeretnénk követelni az ANSI kódolást akkor az utasítások A jelű változata, ezt garantálni fogja pl. GetWindowsDirectoryA. Ez a jelölés csak akkor áll fenn, ha az utasítás szövegekkel is dolgozik.
Sok esetben az ilyen szövegekkel kapcsolatos utasítások képesek arra is hogy megmondják, hogy mennyi helyet kell lefoglalnunk a szövegnek. Erre lesz a következő példa, a PATH környezeti változó lekérése, hibakezeléssel - a használt a utasítás, a GetEnvironmentVariable függvény lesz:
#include <stdio.h> #include <windows.h> int main() { char* szPath; unsigned long int dwRetVal; dwRetVal = GetEnvironmentVariable("PATH", NULL, 0); szPath = new char[dwRetVal]; if (!GetEnvironmentVariable("PATH", szPath, dwRetVal + 1)) { fprintf(stderr, "Hiba tortent a lekerdezes soran, hibakod: %d", GetLastError()); delete [] szPath; return 1; } else { printf("A PATH kornyezeti valtozo erteke: %s", szPath); } getchar(); delete [] szPath; return 0; }
Zárásként pedig érdemes megnézni azt is, hogy egyéb szöveges adatok miként kérhetőek le a rendszertől - mint pl. a számítógépnév (GetComputerName), illetve az aktuálisan bejelentkezett felhasználó neve (GetUserName):
#include <stdio.h> #include <windows.h> int main() { char szComputerName[4096] = {0}, szUserName[4096] = {0}; unsigned long int dwBufferLen = 4095; GetComputerName(szComputerName, &dwBufferLen); dwBufferLen = 4095; GetUserName(szUserName, &dwBufferLen); printf("A szamitogep neve: %s\nA jelenlegi felhasznalo neve: %s", szComputerName, szUserName); getchar(); return 0; }
Ami itt érdekes, hogy a karaktertömb méretét, változóba kellett menteni - mivel cím szerinti átadást kellett végezni. Ez azért van így, mert a függvények visszaírnak a dwBufferLen változóba, méghozzá azt hogy hány karaktert írt bele a tömbünkbe (jelenleg a zárókarakter nélkül).
2.3, Információ a számítógép hardvereiről
Nézzük meg pl. az alábbiakat:
-
Van-e csatlakoztatva egér a géphez?
-
Mekkora a jelenlegi képernyőfelbontás?
-
Mennyi a gép fizikai memóriája, és ebből mennyi elérhető?
Ezek a kérdések már egy vacak szintű játék készítésekor is érdekesek lehetnek - pl. hogy betöltheti-e a 300MB-os zenét a memóriába, vagy nem, esetleg hogy hova helyezze a képernyőn az ablakot, stb...
Íme hát a példaprogram - a használt függvényeink a GetSystemMetrics, és a GlobalMemoryStatusEx:
#include <stdio.h> #include <windows.h> int main() { if (GetSystemMetrics(SM_MOUSEPRESENT)) printf("A szamitogephez van eger csatlakoztatva."); else printf("A szamitogephez nincs eger csatlakoztatva."); printf("\nA kepernyo jelenlegi felbontasa: %dx%d", GetSystemMetrics(SM_CXSCREEN), GetSystemMetrics(SM_CYSCREEN)); MEMORYSTATUSEX memoria = {0}; memoria.dwLength = sizeof(memoria); GlobalMemoryStatusEx(&memoria); printf("\nA teljes fizikai memoria: %.2f GB", memoria.ullTotalPhys / 1024. / 1024. / 1024.); printf("\nAz elerheto fizikai memoria: %.2f GB", memoria.ullAvailPhys / 1024. / 1024. / 1024.); getchar(); return 0; }
Itt megint két érdekességgel lehet találkozni. Az első az az, hogy a GetSystemMetrics rengeteg mindent tud - nem csak ez a 3db SM_ kezdetű konstans található, az MSDN oldalon látható hogy elképesztő mennyiségű van - vagyis megint rengeteg hasonló dolog össze lett gyűjtve egy utasítás alá.
A másik érdekesség a memória lekérésénél a struktúra inicializálása. Erre akkor van szükség, ha a struktúra cbLength, dwSize, stb... mezőkkel indít, ilyenkor:
-
fel kell tölteni nullákkal, erre alkalmas a példában látható megoldás, illetve a memset(), vagy ZeroMemory()utasítás
-
a következő pedig hogy az első mezőt, ami a méretre vonatkozik ki kell tölteni
Erre azért van szükség, mert a Windows fejlesztése során ezeket a struktúrákat tovább bővíthetik új mezőkkel, miközben a régieket meghagyják. Ilyenkor ha egy régebbi typedef szerint programozunk, az így kapott struktúra mérete kisebb lesz, mint az újé. Ebből fogja tudni a Windows, hogy meddig írhat bele a struktúrába, mivel az első mezeje garantáltan a struktúra méretét tartalmazza. Az ilyen húzások garantálják hogy a régebbi programok is futni fognak a gépen egy-egy rendszerfrissítés után.
2.4, Az operációs rendszer verzió-információi
Mi van akkor ha meg akarjuk tudni pl. hogy Windows XP, 7, vagy 10 alól futtatjuk a programot? Természetesen erre is van lehetőség, a megoldást a GetVersionEx függvény nyújtja:
#include <stdio.h> #include <windows.h> int main() { OSVERSIONINFOA verzio = {0}; verzio.dwOSVersionInfoSize = sizeof(verzio); if (GetVersionEx(&verzio)) { printf("A rendszer verzioszama: %d.%d", verzio.dwMajorVersion, verzio.dwMinorVersion); switch (verzio.dwPlatformId) { case VER_PLATFORM_WIN32s: printf("\nA program Win32s alol fut."); break; case VER_PLATFORM_WIN32_WINDOWS: printf("\nA program Windows 9x alapu rendszer alol fut."); break; case VER_PLATFORM_WIN32_NT: printf("\nA program Windows NT alapu rendszer alol fut."); break; } } getchar(); return 0; }
Nézzük meg az eredmény mit is jelent, az alábbi táblázatban összefoglaltam néhány lehetséges kombinációt:
Verziószám | Termékcsalád | Megnevezés |
3.0 | Win32s | Windows 3.0 |
3.1 | Windows 3.1 | |
3.1 | Windows NT | Windows NT 3.1 |
3.51 | Windows NT 3.51 | |
4.0 | Windows NT 4 | |
4.0 | Windows 9x | Windows 95 |
4.1 | Windows 98 | |
4.2 | Windows Millennium Edition | |
5.0 | Windows NT | Windows 2000 |
5.1 | Windows XP | |
6.0 | Windows Vista | |
6.1 | Windows 7 | |
6.2 | Windows 8 | |
6.3* | Windows 8.1 | |
10.0* | Windows 10 |
A csillaggal jelölt verziószámoknál a rendszer attól függően, hogy mennyire régi az alkalmazás dönthet úgy is hogy 6.2-őt ad vissza, és Windows 8-nak tetteti magát.
3, Másik program elindítása, a sajátunkból
Már korábban említettem, hogy ezt meg lehet oldani a CreateProcess segítségével - de az már alapból komplikáltnak tűnt - az is. Nézzünk pár példát arra, milyen egyéb lehetőségeink vannak. A cél minden példában a Jegyzettömb megnyitása lesz.
Igazából ez a témakör egy külön fejezetet is megérne, itt csak úgy nagjából írom le a dolgokat.
3.1, A system utasítás
A parancssornak kiadja a parancsot és megvárja annak lefutását. Beépített utasítás, nem a Windows része.
#include <stdio.h> int main() { system("notepad"); getchar(); return 0; }
3.2, A WinExec utasítás
Amit a Futtatás ablakba (Win+R) beírnánk, ugyanazt kell megadni, és lefuttatja, de nem várja meg. Nem a legtestreszabhatóbb, és nagyon régi.
#include <windows.h> int main() { WinExec("notepad", SW_SHOWNORMAL); return 0; }
A Microsoft helyette a CreateProcess utasítást javasolja.
3.3, A ShellExecute utasítás
Egy remek alternatívája a WinExec-nek, modern, és nem csak programok, hanem fájlok, weboldalak megnyitására, nyomtatására, stb... is alkalmas - nem várja meg a másik program bezárását.
#include <windows.h> #include <shellapi.h> int main() { ShellExecute(0, "open", "notepad", NULL, NULL, SW_SHOWNORMAL); return 0; }
Ez a sokminden azért lehetséges, mivel az indítás a rendszerhéjon (shell) keresztül történik, így szükséges az ezzel kapcsolatos .h fájl használata is:
#include <shellapi.h>
3.4, A ShellExecuteEx utasítás
A ShellExecute kiterjesztése, itt már pl. eldönthetjük hogy megvárjuk-e a másik programot.
#include <windows.h> #include <shellapi.h> int main() { SHELLEXECUTEINFO inditas = {0}; inditas.cbSize = sizeof(inditas); inditas.fMask = SEE_MASK_NOCLOSEPROCESS; inditas.lpVerb = "open"; inditas.lpFile = "notepad"; inditas.nShow = SW_SHOWNORMAL; ShellExecuteEx(&inditas); WaitForSingleObject(inditas.hProcess,INFINITE); CloseHandle(inditas.hProcess); return 0; }
3.5, A CreateProcess utasítás
Íme a rettenet maga, mivel ez a legteljeskörűbb, szintén eldönthető a várakozás. Ez a legegyszerűbb használata a parancsnak:
#include <windows.h> int main() { STARTUPINFO inditas = {0}; inditas.cb = sizeof(inditas); PROCESS_INFORMATION proc_info = {0}; if (CreateProcess(NULL, "notepad", NULL, NULL, true, NULL, NULL, NULL, &inditas, &proc_info)) { WaitForSingleObject(proc_info.hProcess, INFINITE); CloseHandle(proc_info.hProcess); CloseHandle(proc_info.hThread); } return 0; }
Előnye, hogy nem muszáj az alkalmazásnak ".exe" kiterjesztésűnek lennie, pl. ".tmp" kiterjesztéssel is futtatható az ideiglenes mappából vele állomány, ha az egyébként futtatható. Erre egyik sem képes a fentiek közül.