C++: Můj první program pro Windows Seznámení s programovacím jazykem C++ pro Windows a naše první aplikace Hello World Jazyk C++ je díky objektům, ukazatelům a podobným srandičkám velice silný jazyk, a proto je požíván i pro programování ve Windows. V tomto seriálu (doufám) základů programování ve Windows se budeme věnovat neobjektovému programování, protože je to, dle mého názoru, nejjednodušší způsob, jak začít výuku základů programování pro Windows. A protože začínáme bez objektů, nebudeme používat Microsoftí VC (zajímalo by mě, kolik procent čtenářů jej má legálně) a využijeme nástroj, který je zdarma (GNU), a to Bloodshed Dev-C++. K dispozici je aktuálně verze 4 včetně patche na 4.01. Programovat budeme v konvenci ANSI, tudíž by programy, které zde naleznete, by měly být přenositelné i na jiné kompilátory pro Windows. Tento tutoriálnek předpokládá, že znáte alespoň trochu počítačovou angličtinu. Jeden podstatný rozdíl mezi programováním pro DOS a Windows je, že pro Windows použijeme namísto hlavní funkce main funkci WinMain a to nejlépe v tomto tvaru: int STDCALL WinMain( HINSTANCE hThisInstance, HINSTANCE hPrevInstance, LPSTR lpszArgument, int nFunsterStil ) Několik odlišností zde je. Např. STDCALL za deklarací vrácené hodnoty (kterou Windows nepoužívají). Tímto nadeklarujete, že funkce je volána standardním způsobem. Narozdíl od funkce main jsou zde parametry 4. hThisInstance - struktura daná typem HINSTANCE, handle (rukojeť) na tuto instance aplikace hPrevInstance - handle na instanci předchozí aplikace lpszArgument - ukazatel na řetězce argumentů příkazové řádky (ve Windows se moc nepoužívá) nFunsterStil - styl, kterým by Windows chtěly zobrazit naše okno Poznámky: Instance aplikace: Máte např. Hello World a spustíte jej víckrát, tzn. že máte několik instancí té samé aplikace. Parametry (argumenty) příkazové řádky vytvoří Windows např. když na ikonu programu přetáhnete jiný soubor. Dále programy pro Windows využívají hlavní proceduru okna o které se zmíním v příštím díle, protože nechci úvodní program příliš komplikovat. Takže se pustíme do samotné tvorby programu Hello World. Pokud už máme Dev-C++ nainstalované, vybereme z menu File položku New project a klikneme na ikonu projektu WinMain() Project. Ještě se ujistíme, že je vybrán C++ projekt a klikneme na OK. Zadáme Hello World a vybereme, kde se projekt vytvoří, nejlépe v nějaké prázdné složce. Teď již máme vytvořen projekt a editor nám automaticky otevřel okno Untitled1, které raději uložíme jako např. main.cpp do složky s projektem, a to v menu File - Save unit as... V souboru main.cpp můžete vidět standardní připojení hlavičkového souboru windows.h a hlavní funkci WinMain, která vrací nulu. Ve skutečnosti se k programu linkuje ještě jeden soubor, a to rsrc.o, který vznikne zkompilováním zdrojů, které můžeme editovat přes menu: Project - Edit resource file. Jak můžete vidět, je zde jen jedna položka, a to ikona, kterou můžete nahradit zde nebo v nastavení projektu: Project - Project options. Přistoupíme k samotnému Hello World. Úplně nejjednodušší cesta je zavolat funkci MessageBox, která je součástí Windows API a její kód je umístěn ve standardních knihovnách Windows, což nás v tuto chvíli nemusí zajímat. Klikneme do volného řádku nad příkazem return nebo tam, kam chceme kód vložit, a k zavolání můžeme použít průvodce obsaženého v Dev-C++: Vybereme z menu Edit - Insert - MessageBox a ve vybraném okně vhodně zvolíme, co potřebujeme. Do políčka dialog title napíšeme titulek dialogu např. Můj program, do editboxu napíšeme text např. Hello World a vybereme, která tlačítka budeme v dialogu chtít a které chceme mít aktivní, popř. Ikonu a modálnost okna (vysvětlím někdy v příštím díle, stačí ponechat Application modal). Pokud zaškrtnete tlačítko advanced options, můžete si zvolit některé další parametry, které však nabízí jen Win 95 a vyšší. Dialog vložíte do programu kliknutím na OK. Program by tedy měl vypadat nějak takto: #include <windows.h> int STDCALL WinMain (HINSTANCE hInst, HINSTANCE hPrev, LPSTR lpCmd, int nShow) {
MessageBox (NULL, "Hello World" , "M ůj program", 0 + MB_OKCANCEL + MB_ICONASTERISK); return 0; } Program zkompilujete stiskem kláves Ctrl+F9 nebo výběrem Compile z menu Execute. Tímto získáte velmi malý exe soubor narozdíl několika desítek kilový soubor v delphi nebo i megový soubor ve VC++. Tento soubor již můžete spustit a pochlubit se s ním komukoliv. Myslím však, že s tímto programem na dlouho spokojeni nebudete.
Základ programování v C++ (2.) Druhý díl o základech programování v jazyce C++, tentokrát o nejdůležitější části programů pro Windows smyčka zpráv Protože všechny procesy Windows komunikují zprávami, je u všech programů, které píšeme, nutné tyto zprávy zpracovávat. Každý program, který k něčemu využívá svůj hlavní proces, by měl obsahovat smyčku, která "vytáhne" zprávu z fronty zpráv daného procesu a předá ji ke zpracování a která se ukončí po obdržení ukončovací zprávy. Je zde však jeden problém: Pokud program pracuje na nějakém složitém výpočtu, měli byste jej nechat zobrazit dialog se zprávou nebo, a to je nejvýhodnější řešení, v hlavním okně vytvořit indikátor zpracování výpočtu, protože pokud program déle neodpovídá na žádnou zprávu, nebo pokud se jeho fronta rapidně prodlouží, můžete při stisku Ctrl-Alt-Delete vidět, že program neodpovídá. Nervózní uživatel by se jej mohl takovýmto způsobem pokusit zavřít a ztratit tak důležitá data. Použitím standardního Windowsovského indikátoru (modré čtverečky), tento problém odpadne, protože tento proces obsahuje vlastní smyčku zpráv a pokud výpočet není nijak extrémě náročný, stihne odpovídat i na zprávy. Teď jak taková standardní smyčka zpráv vypadá: while(GetMessage(&messages, NULL, 0, 0)) { TranslateMessage(&messages); DispatchMessage(&messages); } Tato smyčka se opakuje, dokud funkce GetMessage nevrátí nulu. GetMessage vrátí nulu v případě, že tento proces obdrží zprávu WM_QUIT, která mu oznamuje, že si Windows přejí tento program ukončit. Funkce TranslateMessage provádí to, že překládá hodnoty virtuálních kláves na zprávy, kterým už vaše procedura okna rozumí. A DispatchMessage provádí jen to, že zprávu pošle proceduře okna, kterou máte zaregistrovanou ve třídě okna (neplést třídy okna Windows s třídami v C++ !!!). Podívejme se teď na příklad z prvního dílu: #include <windows.h> int STDCALL WinMain (HINSTANCE hInst, HINSTANCE hPrev, LPSTR lpCmd, int nShow) { MessageBox (NULL, "Hello World" , "Můj program", 0 + MB_OKCANCEL + MB_ICONASTERISK); return 0; } Pokud tento prográmek spustíte, zjistíte, že ačkoliv zde žádnou smyčku zpráv nemáte, Windows proti delšímu zobrazení dialogu nijak neprotestují. Je to tím, že zde smyčka zpráv vlastně je. Nevěříte? Co udělá tento program při spuštění? Hlavní funkce zavolá MessageBox, a pokud je MessageBox ukončen, program okamžitě skončí. V podstatě Windows mají implmentovánu vlastní smyčku zpráv v každém svém procesu a ve většině funkcí. V tomto případě funkce MessageBox vytvoří dle vašeho přání okno, proceduru okna a smyčku zpráv, která je ukončena po kliknutí na kterékoliv tlačítko a toto tlačítko je vráceno touto funkcí. O tvorbě dialogových oken, procedurách a třídách oken někdy příště.
Základ programování ve C++ a Windows (3.) Třetí díl seriálu, tentokrát na téma Procedura a třída okna Každý uživatelsky obsluhovatelný program pro Windows, by měl mít své okno. Otevření nějakého okna je pro uživatele obvykle známkou toho, že s programem lze nějak pracovat. K naprogramování jednoduchého okna ve Windows není třeba žádných operací s grafikou, vždyť Windows to vše udělají za nás. Vše, co budeme potřebovat, je proměnná typu HWND pro uložení handle na naše okno, struktura MSG pro práci se zprávami našemu oknu a proceduru okna. K přesnějšímu určení vzhledu okna využijeme strukturu WNDCLASSEX, kterou po naplnění předáme Windows. Budeme postupovat přibližně takto: // připojíme vždy potřebný hlavičkový soubor windows.h #include <windows.h> // nadeklarujeme prototyp naší procedury okna LRESULT CALLBACK ProceduraOkna(HWND, UINT, WPARAM, LPARAM); // pro obecné použití při práci s třídami oken char JmenoTridy[ ] = "WindowsApp"; // Klasická WinMain (názvy proměnných jako v Dev-C++) int WINAPI WinMain(HINSTANCE hThisInstance, HINSTANCE hPrevInstance, LPSTR lpszArgument, int nFunsterStil) { HWND hwnd; /* Handle k našemu oknu */ MSG messages; /* Sem nám Windows ukládají zprávy */ WNDCLASSEX wincl; /* Datová struktura třídy okna */ // Vyplníme strukturu wincl wincl.hInstance = hThisInstance; wincl.lpszClassName = JmenoTridy; wincl.lpfnWndProc = ProceduraOkna; wincl.style = CS_DBLCLKS; wincl.cbSize = sizeof(WNDCLASSEX);
// Tato procedura je volána Windows
// Použijeme standartní ikonu a kurzor wincl.hIcon = LoadIcon(NULL, IDI_APPLICATION); wincl.hIconSm = LoadIcon(NULL, IDI_APPLICATION); wincl.hCursor = LoadCursor(NULL, IDC_ARROW); wincl.lpszMenuName = NULL; // Nemáme žádné menu wincl.cbClsExtra = 0; wincl.cbWndExtra = 0; // Pro pozadí okna použijeme světle šedou barvu (klasická Windows) wincl.hbrBackground = (HBRUSH) GetStockObject(LTGRAY_BRUSH); // A vyplněnou třídu okna zaregistrujeme // Pokud se to nepodaří, nemá smysl pokračovat if (!RegisterClassEx(&wincl)) return 0; // Teď vytvoříme vlastní okno hwnd = CreateWindowEx( 0, // Nebudeme využívat žádné rozší řené možnosti JmenoTridy, // Jméno třídy okna "Aplikace pro Windows", // Název okna WS_OVERLAPPEDWINDOW, // Typ okna CW_USEDEFAULT, // Pozici okna zvolí samo Windows CW_USEDEFAULT, 544, // Šířka okna 375, // Výška okna HWND_DESKTOP, // Naše okno má jako rodiče plochu NULL, // Žádné menu hThisInstance, // Handler na instanci programu NULL // Žádná další data ); // Konečně zobrazíme okno
ShowWindow(hwnd, nFunsterStil); // A spustíme standartní smyčku zpráv while(GetMessage(&messages, NULL, 0, 0)) { TranslateMessage(&messages); DispatchMessage(&messages); } return messages.wParam; } // Konec WinMain Další věcí, kterou budeme potřebovat, je procedura našeho okna: LRESULT CALLBACK ProceduraOkna(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { switch (message) { case WM_DESTROY: PostQuitMessage(0); // Windows zavírají okno a my ukončíme aplikaci break; default: // Pro zprávy se kterými nebudeme pracovat return DefWindowProc(hwnd, message, wParam, lParam); } return 0; } Toto je jen velmi zjednodušená smyčka zpráv, Windows posílají každému oknu spoustu různých zpráv s různými parametry. A i naše aplikace může tyto zprávy rozesílat. Na několik základních a nejpoužívanějších zpráv a jak s nimi pracovat nebo je rozesílat se podíváme příště.
Základ programování ve Windows a C++ (4.) Výběr několika základních zpráv pro správu oken V minulém díle tohoto tutoriálku jsme si ukázali jak jednoduše vytvořit proceduru okna, která donutí okno zavřít a ostatní parametry přepošle funkcí DefWindowProc standardní obsluze oken. Pokud však budete chtít naprogramovat nějakou složitější aplikaci, např: Word :-), pro komunikaci mezi okny, lištami nástrojů a spoustami dalších elementů vaší aplikace, rozhodně si se standardní obsluhou okna nevystačíte. Zprávy multitaskingu Windows jsou dvojího druhu, klasické a diagnostické. Diagnostické zprávy nemají u většiny kompilátorů definici a programátor by je měl zpracovávat až v případě nejvyšší nouze. Klasických zpráv používaných Windows je velmi mnoho. Pro základní programování vám bude stačit znát jich jen několik desítek a všechny je možné nalézt v referencích k Windows. Windows berou jako okna i jednotlivé prvky jak hlavního okna tak dialogů, z toho vyplývá, že je možné posílat zprávy i těmto objektům. K účelům interakce mezi procesy jsou často využity i parametry zpráv lParam a wParam. Zprávy můžete rozesílat použitím funkce SendMessage s parametry: handle okna, které zprávu má obdržet, zprávu, která se bude posílat, a wParam a lParam této zprávy. K odeslání zprávy prvku dialogového okna (jak s nimi pracovat, si ukážeme v některém z příštích dílů) použijte funkci SendDlgItemMessage s parametry: handle dialogového okna, které zprávu má obdržet, ID prvku dialogového okna, zprávu, která se bude posílat a wParam a lParam této zprávy, vše v uvedeném pořadí. Výběr některých základních zpráv:
Zprávy pro správu oken WM_CLOSE
Je poslána vždy, když má být okno zavřeno. Pokud je předána proceduře DefWindowProc, ta zavolá DestroyWindows.
WM_CREATE
Touto zprávou je okno informováno o otevření a o tom, že je možné provést inicializační
operace. Jako parametr je v lParam ukazatel na strukturu CREATESTRUCT, která obsahuje kopie parametrů předaných funkcí CreateWindow při tvorbě okna. WM_DESTROY
Je poslána proceduře okna poté, co bylo okno odstraněno z obrazovky.
WM_KILLFOCUS
Je posláno oknu, jakmile ztratí fokus, tzn. přestane být aktivní. wParam obsahuje handle okna, které fokus získalo (může být NULL)
WM_MENUSELECT
Oznamuje oknu, že uživatel zvolil některou položku z jeho menu. wParam obsahuje ID zvolené položky, lParam obsahuje kombinaci vlajek s informacemi o položce. Pokud lParam obsahuje -1 a wParam nulu, znamená to, že uživatel ukončil menu bez vybrání nějaké položky.
WM_MOVE
Posláno, jakmile je okno přesunuto. lParam obsahuje novou pozici okna - LOWORD = x-ová souřadnice, HIWORD = y-ová souřadnice.
WM_QUIT
Indikuje požadavek k ukončení běhu aplikace. wParam obsahuje kód vrácený Windowsům.
WM_SETFOCUS
Je posláno oknu, jakmile dostane fokus, tzn. že je nyní aktivní. wParam obsahuje handle okna, které fokus ztratilo (může být NULL)
WM_SETTEXT
Nastaví název objektu. U oken je to titulek, u např. tlačítka je to jeho název. lParam obsahuje ukazatel na textový řetězec.
WM_SIZE
Tato zpráva je poslána oknu, jakmile uživatel změní jeho velikost. lParam obsahuje v LOWORDu šířku a v HIWORDu výšku okna po změně velikosti. wParam může obsahovat SIZEFULLSCREEN pokud bylo okno maximalizováno, SIZEICONIC pokud bylo okno minimalizováno (pozůstatek WfW3.11), SIZENORMAL pokud se velikost okna změnila, ale není ani maximalizované, ani minimalizované.
Vstupní zprávy WM_CHAR
Zpráva je vyhodnocena, pokud jsou zpracovány zprávy WM_KEYUP a WM_KEYDOWN. wParam obsahuje hodnotu klávesy, lParam obsahuje v LOWORDu počet opakování klávesy, v LOBYTE HIWORDu nalezneme scan kód klávesy, bity 25-28 jsou interně využívány Windows, bit 29 obsahuje stav klávesy ALT, bit 30 je předchozí stav a bit 31 obsahuje přechodný stav
WM_COMMAND
Asi nejdůležitější zpráva !!! Je odeslána, když uživatel zvolí položku z menu, klikne na tlačítko, stiskne akcelerátor apod. wParam specifikuje ID zvoleného objektu,
WM_LBUTTONDBLCLK Zpráva se objeví, pokud uživatel dvojkliknul v rámci naší aplikace. wParam obsahuje kombinaci vlajek: MK_CONTROL, pokud je zároveň stisknuta klávesa Ctrl, MK_SHIFT pokud je stisknuta klávesa Shift, MK_LBUTTON, pokud je stisknuto levé tlačítko myši (podobné jako MK_RBUTTON a MK_MBUTTON ). lParam obsahuje v LOWORDu x-ovou souřadnici ukazatele myši a v HIWORDu y-ovou souřadnici. WM_RBUTTONDBLCLK Podobné jako viz výše. WM_MBUTTONDBLCLK Podobné jako viz výše. WM_LBUTTONDOWN
Objeví se, pokud je stisknuto levé tlačítko myši. wParam obsahuje vlajky jako u WM_LBUTTONDBLCLK mimo MK_LBUTTON. lParam obsahuje v LOWORDu x-ovou souřadnici ukazatele myši a v HIWORDu y-ovou souřadnici.
WM_RBUTTONDOWN
Podobné jako viz výše.
WM_MBUTTONDOWN
Podobné jako viz výše.
WM_HSCROLL
Je odeslána, pokud uživatel použil horizontální posuvník. wParam může být jedna z následujících hodnot: SB_BOTTOM - posun úplně vpravo nebo dolů SB_TOP - posun úplně vlevo nebo nahoru SB_LINEDOWN - jeden řádek dolů SB_LINEUP - jeden řádek nahoru SB_PAGEDOWN - stránka dolů SB_PAGEUP - stránka nahoru (předchozí čtyři platí i horizontálně) SB_THUMBPOSITION - posun na absolutní pozici, ta je v LOWORDu lParam SB_THUMBTRACK - posuvník je právě posouván, LOWORD lParamu obsahuje pozici.
WM_VSCROLL
Podobné jako WM_HSCROLL.
Základní zprávy pro správu okna a pro analýzu vstupů bychom měli probrány. Nejsou to však zdaleka všechny zprávy, které je možné pro tyto akce využít. Bližší informace o jednotlivých zprávách je možné nalézt různě na internetu a v dokumentacích ke kompilátorům. V příštím díle tohoto tutoriálku se podíváme na zprávy pro interakci s prvky dialogových oken a jejich notifikační kódy a v další části na některé ze systémových zpráv, zpráv pro práci s MDI okny.
Základ programování ve Windows a C++ (5.) Výběr několika základních zpráv (2.) - tentokrát pro práci s prvky ve formuláři (tlačítka, objekty) Každé tlačítko a každá objekt v okně aplikace by měl u většiny aplikací k něčemu sloužit. Zde navážu na minulý díl. Jako v minulé části tohoto tutoriálu, zde máte seznam nejdůležitějších zpráv pro interakci s těmito prvky. K dialogovým prkům jsou tyto zprávy pouze rozšířením k těm z minulé části. Ve většině případů lze použít zprávy pro okna pro dialogové prvky apod.
Zprávy pro práci s dialogovými prvky WM_NEXTDLGCTL
Posílá se dialogovému oknu, aby změnilo prvek, který vlastní fokus. Pokud je lParam nenulový, wParam určuje ID prvku, který má získat fokus. Je-li lParam roven nule, wParam obsahuje vlajku posunu, tzn. je-li wParam nula, získá fokus následující prvek, jeli nenulový, fokus získá předchozí prvek.
WM_SETFONT
Nastaví font pro kreslení textu. wParam obsahuje handle na font, pokud je NULL, pak je použit systémový font. Pokud je lParam nastaven na TRUE je prvek překreslen okamžitě.
Zprávy pro tlačítka (button, checkbox, radio) BM_GETCHECK
Posílá se prvku, při zjišťování stavu. Funkce SendDlgItemMessage vrátí TRUE, pokud je checkbox zakřížkován.
BM_GETSTATE
Vrátí TRUE, pokud je stisknuto
BM_SETCHECK
Označí nebo odznačí checkbox nebo radiobutton. Pokud je wParam nenulový, je označen, jinak je odznačen. Pro třístavové checkboxy: pokud je wParam roven 1 je označeno, pokud je 2 pak je checkbox zašednut.
BM_SETSTATE
Pokud je wParam nenulový je prvek vykreslen v inverzní grafice.
BM_SETSTYLE
Nastaví pro prvek nový styl. wParam obsahuje nové nastavení stylu. lParam obsahuje vlajku zda má být prvek překreslen. Styl je součet parametrů (viz některý příští díl o zdrojích)
DM_GETDEFID
Vrátí ID implicitního tlačítka
DM_SETDEFID
Dle wParam nastaví tlačítko jako implicitní.
Zprávy pro editační pole EM_GETLINECOUNT
Vrátí počet řádek.
EM_LIMITTEXT
Nastaví limit v počtu znaků, které může uživatel vepsat. wParam obsahuje max. počet znaků.
EM_REPLACESEL
Vymění aktuálně vybraný text za textový řetězec na který ukazuje lParam.
EM_SETPASSWORDCHAR Nastaví, který znak se bude zobrazovat místo písmen. Implicitně je hvězdička. WM_CLEAR
Smaže obsah editačního pole
ListBox - seznam LB_ADDSTRING
Přidá do seznamu nulou ukončený řetězec, na který ukazuje lParam
LB_DELETESTRING
Odstraní ze seznamu řetězec s indexem uvedeným v wParam
LB_DIR
Vyplní seznam seznamem souborů v aktuálním adresáři. wParam obsahuje atribut výběru (DOS 4+). lParam obsahuje hvězdičko-otazníkovou notaci výběru souborů, které mají být zobrazeny.
LB_GETCOUNT
Vrátí počet položek v seznamu.
LB_GETSEL
Vrátí index vybrané položky
LB_GETTEXT
Zkopíruje položku specifikovanou ve wParam do řetězce specifikovaného ukazatelem ve lParam
LB_GETTEXTLEN
Vrátí délku řetězce specifikovaného indexem ve wParam
LB_GETTOPINDEX
Vrátí index první viditelné položky v seznamu
LB_INSERTSTRING
Vloží do položky specifikované wParam řetězec, na který ukazuje lParam
LB_SETCOLUMNWIDTH
Nastaví šířku sloupců ve vícesloupcovém seznamu na wParam
LB_SETCURSEL
Položka wParam je vybrána. Pokud je wParam -1, pak není vybrána žádná položka.
ComboBox - roletka CB_ADDSTRING
Přidá do seznamu nulou ukončený řetězec na který ukazuje lParam
CB_DELETESTRING
Odstraní ze seznamu řetězec s indexem uvedeným v wParam
CB_SHOWDROPDOWN
Pokud je wParam TRUE, pak je roletka vysunuta jinak se schová
Pro combobox je možné využít většinu ze zpráv použitelných u listboxu, pouze se změní prefix z LB_ na CB_ Pokud uživatel něco dělá s kterýmkoliv dialogovým prvkem, vaše aplikace se to samozřejmě dozví pomocí notifikačního kódu. Vše co potřebujeme je klasická zpráva WM_COMMAND, kterou naše aplikace obdržela, wParam obsahuje ID prvku, LOWORD lParamu obsahuje handle na okno, a HIWORD lParamu obsahuje již některý z těchto notifikačních kódů:
Kódy pro tlačítka BN_CLICKED
Indikuje, že na tlačítko bylo kliknuto
BN_DOUBLECLICKED
Indikuje, že uživatel dvojkliknul na radiobutton
Kódy pro EditBoxy EN_CHANGE
Indikuje, že uživatel změnil obsah políčka
EN_ERRSPACE
Indikuje, že místo v paměti pro EditBox došlo.
EN_KILLFOCUS
Indikuje, že editbox ztratil fokus
EN_SETFOCUS
Indikuje, že editbox získal fokus
Kódy pro seznamy LBN_DBLCLK
Indikuje, že uživatel dvojkliknul na položku.
LBN_SELCHANGE
Indikuje, že uživatel změnil výběr.
LBN_KILLFOCUS
Indikuje, že seznam ztratil fokus
LBN_SETFOCUS
Indikuje, že seznam získal fokus
Kódy pro comboboxy CBN_DROPDOWN
Informuje, že combobox bude vysunut.
CBN_EDITCHANGE
Informuje, že uživatel změnil text v editačním poli.
CBN_SELCHANGE
Indikuje, že uživatel změnil výběr.
CBN_KILLFOCUS
Indikuje, že combobox ztratil fokus
CBN_SETFOCUS
Indikuje, že combobox získal fokus
Zde jsou uvedeny jen některé nejdůležitější kódy a seznam není zdaleka úplný. Jak vidíte, většina kódů je pro různé prvky podobná. V příštím díle navážu na tento díl seznamem dalších zpráv. Potom se podíváme na zdroje a ve finále vytvoříme nějakou funkční aplikaci. Pokud sledujete můj tutoriálek, máte se na co těšit. Jestliže něčemu nerozumíte, nezoufejte, v dalších dílech to snad objasním a jinak je zde board nebo můj mail.
Základ programování ve Windows a C++ (6.) Výběr několika základních zpráv (3.) Jako v předchozích dvou dílech věnovaných zprávám si ukážeme některé inicializační zprávy, některé ze systémových zpráv a výcuc ze zpráv věnovaných MDI oknům. Bohužel popsat všechny zprávy systému Windows je mimo rozsah tohoto tutoriálku, ale je možné je nalézt na internetu nebo v odborných publikacích.
Inicializační zprávy WM_INITDIALOG
Je poslána aplikaci těsně předtím, než je zobrazeno dialogové okno. wParam identifikuje prvek dialogu, který při otevření okna získá fokus. lParam obsahuje hodnotu dwInitParam, pokud je předávána funkcí, která vytváří dialog.
WM_INITMENU
Tato zpráva je požadavek k inicializaci menu. Je posílána těsně předtím, než je menu zobrazeno. wParam obsahuje handle na menu, které má být zobrazeno.
Systémové zprávy WM_SYSCOMMAND
Je poslána, pokud uživatel zvolí položku systémového menu. Pro získání některé z níže uvedených hodnot je třeba hodnotu wParam ANDnout s 0xFFF0. Tato může být: SC_CLOSE - Uživatel chce zavřít okno SC_HSCROLL - Horizontální posun SC_KEYMENU - Menu se otevřelo po stisku klávesy SC_MAXIMIZE(nebo SC_ZOOM) - Vybráno Maximalizovat SC_MINIMIZE(nebo SC_ICON) - Vybráno Minimalizovat SC_MOUSEMENU - Menu se otevřelo myší SC_MOVE - Vybráno Přesunout SC_NEXTWINDOW - Posun na další okno SC_PREVWINDOW - Posun na předchozí okno SC_RESTORE - Vybráno Obnovit SC_SIZE - Změna velikosti SC_VSCROLL - Vertikální posun Akce s tímto menu je lépe přenechat DefWindowProc.
WM_SYSCHAR
Podobné WM_CHAR, ale pracuje se systémovými klávesami.
WM_COMPACTING
Je posláno všem aplikacím, pokud Windows detekují, že dochází paměť nebo systémové prostředky. wParam specifikuje vytížení procesoru: 0x8000 znamená 50%.
WM_SYSCOLORCHANGE Informuje, že uživatel změnil jednu nebo více systémových barev. Aplikace by měla zavolat WM_PAINT pro překreslení a rekonstruovat všechny existující štětce. WM_TIMECHANGE
Je posláno všem aplikacím, pokud se změnil systémový čas.
MDI zprávy WM_MDICASCADE
Tato zpráva uspořádá všechna okna kaskádovým stylem.
WM_MDICREATE
Vytvoří se nové MDI okno. wParam obsahuje ukazatel na MDICREATESTRUCT strukturu.
WM_MDIDESTROY
Odstraní MDI okno. wParam obsahuje handle na toto okno.
WM_MDIGETACTIVE
Vrátí aktivní MDI okno. LOWORD vrácené hodnoty obsahuje handle na toto okno, HIWORD obsahuje 1 pokud je okno maximalizováno.
WM_MDIMAXIMIZE
Maximalizuje MDI okno, jehož handle je zadáno ve wParam.
WM_MDISETMENU
Změní menu nebo popup menu MDI okna nebo obojí. lParam obsahuje v LOWORDU handle na nové menu a v HIWORDU handle na nové popup menu. Pokud je některé ze slov nulové, menu se nemění.
WM_MDITILE
Okna se uspořádají stylem TILE.
Doufám, že vám tyto tři díly obsahující pouze zjednodušený popis některých zpráv posílaných Windowsy aplikaci a naopak pomohou při programování vašich aplikací. Rozhodně je ale budete potřebovat, pokud se mnou budete pokračovat v tomto tutoriálku.
Základ programování ve Windows a C++ (7.) Něco málo ke zdrojům a resource částem programům pro Windows Každý program pro Windows obsahuje tzv. zdrojovou část (resource). Ta je připojena většinou ke konci EXE souboru a může obsahovat dialogová okna, různá menu, bitmapy, ikony, fonty a různá jiná data, jež aplikace využívá ke komfortnější interakci s uživatelem. Zvláště pro programátora je tento způsob vytváření dialogů mnohem komfortnější. Pokud využijete nějaký WYSIWYG editor jako např. AppStudio (viva Microsoft), pak je tvorba dialogů úplně jednoduchá, ale ani syntaxe rc souborů není příliš složitá. Programátor určí vizáž okna, umístí tlačítka a přidělí jim identifikátory. Potom pomocí funkce Windows přidělí proceduru pro zpracovávání zpráv a jednoduše jej otevře. Práce přes zdroje je mnohem jednodušší než vytvářet HWND proměnné pro všechny tlačítka, statické objekty atd. a ty po jednom otevírat funkcí CreateWindow. Jak tedy na to? Pokud využijeme Dev-C++, ukládá se zdrojový kód ke zdrojové části implicitně v souboru rsrc.rc. Nejdůležitější je u tohoto kompilátoru připojit k souboru rsrc.rc soubor winresrc.h, který obsahuje seznam všech hlavičkových souborů nutných ke tvorbě zdrojů. Další soubor, který je třeba připojit, je váš vlastní soubor s definicemi identifikátorů, obvykle resource.h, ale je možné vkládat definice i na začátek rsrc.rc, ale znepřehlední to strukturu projektu. Syntaxe rc souborů je jednoduchá. Nejprve se napíše identifikátor, který je v podstatě číslo, ale pomocí direktiv #define je dobré si je vhodně pojmenovat. Konvence je ID, dále písmeno typu např. D jako dialog, podtržítko a popis zdroje. Výsledný identifikátor by mohl vypadat IDD_HLAVNIOKNO, ale není to podmínka. Dále je třeba specifikovat typ zdroje. Nejčastěji používané jsou dialogy (specifikace "DIALOG"), nabídky ("MENU"), ikony ("ICON"), kurzory ("CURSOR"), bitmapy ("BITMAP"), fonty ("FONT"), akcelerátory, které jsou vlastně klávesové zkratky ("ACCELERATORS"), a také tabulky řetězců ("STRINGTABLE"), které se deklarují trochu jinak, ale jsou užitečné při tvorbě vícejazyčných programů. Pak je možno vsadit některé speciální příkazy, ale ty nejsou pro začínajícího programátora zase tak důležité. Potom mohou následovat parametry a obsah položky uzavřené mezi příkazy BEGIN a END nebo { a }. Protože se každý typ deklaruje různě, bude lepší uvést příklady. Jednoduché okno může vypadat takto: IDD_DIALOG1 DIALOG DISCARDABLE 0, 0, 187, 65 STYLE DS_MODALFRAME | WS_MINIMIZEBOX | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU CAPTION "Dialogové okno" FONT 8, "MS Sans Serif" BEGIN DEFPUSHBUTTON "OK",IDOK,106,38,50,14 PUSHBUTTON "Storno",IDCANCEL,29,38,50,14 CONTROL "Tak takto vypadá jednoduché okno.",IDC_STATIC, "Static",SS_SIMPLE | WS_GROUP,34,15,119,8 END Způsobů, jak vytvořit jednoduché okno, je spousta. Také text je možné vytvořit několika způsoby, styl okna je možné měnit. Pokud za tag CAPTION uvedeme např: MENU IDM_MENU1, bude k tomuto oknu použito menu IDM_MENU1. Parametry STYLE mohou být: WS_BORDER, WS_CAPTION, WS_CHILD, WS_CHILDWINDOW, WS_CLIPCHILDREN, WS_CLIPSIBLINGS, WS_DISABLED, WS_DLGFRAME, WS_GROUP, WS_HSCROLL, WS_ICONIC, WS_MAXIMIZE WS_MAXIMIZEBOX, WS_MINIMIZE, WS_MINIMIZEBOX, WS_OVERLAPPED, WS_OVERLAPPEDWINDOW ,WS_POPUP, WS_POPUPWINDOW, WS_SIZEBOX, WS_SYSMENU, WS_TABSTOP, WS_THICKFRAME, WS_TILED, WS_TILEDWINDOW, WS_VISIBLE, WS_VSCROLL Popisovat je nebudu, snad jen že např: WS_MAXIMIZE přinutí okno být maximální, WS_MAXIMIZEBOX umožní aby se na oknu objevilo tlačítko pro maximalizaci, WS_VSCROLL nechá na okně vykreslit vertikální posuvník. Pro vytvoření standardního obvykle stačí použít WS_OVERLAPPEDWINDOW, které připojí některé parametry automaticky. Jednoduché menu IDR_MENU1 MENU DISCARDABLE BEGIN POPUP "&Soubor" BEGIN
MENUITEM "&Nový", MENUITEM "&Otevřít...", POPUP "&Seznam" BEGIN MENUITEM "1", MENUITEM "2", MENUITEM "3", MENUITEM "4", MENUITEM "5", MENUITEM "6", END MENUITEM "Nefunkční", MENUITEM SEPARATOR MENUITEM "&Konec",
ID_SOUBOR_NOVY ID_SOUBOR_OTEVRIT
ID_SOUBOR_SEZNAM_1 ID_SOUBOR_SEZNAM_2 ID_SOUBOR_SEZNAM_3 ID_SOUBOR_SEZNAM_4, MENUBARBREAK ID_SOUBOR_SEZNAM_5 ID_SOUBOR_SEZNAM_6 ID_SOUBOR_NEFUNKCNI, GRAYED ID_SOUBOR_KONEC
END END Vhodně zvolené identifikátory, zpřehlední zdrojový kód nejen ve zdrojích. Vložením znaku & před některé písmeno způsobí, že stiskem tohoto písmena na klávesnici je daná položka zvolena. Připojování kurzorů, ikon a bitmap není tak složité a lze je napsat jedním řádkem: IDC_CURSOR1 IDI_ICON1 IDB_BITMAP1
CURSOR ICON BITMAP
DISCARDABLE
"CURSOR1.CUR" "ICON1.ICO" "BITMAP1.BMP"
Uvedeme ID objektu, typ, rozšířené parametry a název souboru. Definice akcelerátorových kláves je podobná, uvedeme ID, klíčové slovo ACCELERATORS, paramtery a mezi tagy BEGIN a END zadáme jejich definice. Do uvozovek uvedeme písmeno klávesy nebo náhradní označení virtuální klávesy, dále identifikátor, a další parametry. Nejprve zvolíme typ klávesy (VIRTKEY nebo ASCII), SHIFT, ALT nebo CONTROL jsou vlajky stisku přesmykačů. Dále je možné uvést další parametry. IDR_ACCELERATOR1 ACCELERATORS DISCARDABLE BEGIN "A", ID_ACCEL40011, "B", ID_ACCEL40012, VK_F1, ID_ACCEL40013, VK_F2, ID_ACCEL40014, "X", ID_ACCEL40015, END
VIRTKEY,SHIFT, ALT, NOINVERT VIRTKEY,NOINVERT VIRTKEY,NOINVERT VIRTKEY,CONTROL, ALT, NOINVERT ASCII, ALT, NOINVERT
Syntaxe tabulek řetězců je trochu odlišná: STRINGTABLE DISCARDABLE BEGIN IDS_STRING1 IDS_STRING2 END
"Hello world" "Nazdar lidi"
Je třeba uvést klíčové slovo STRINGTABLE parametry se definují až k jednotlivých řetězcům. Nejsložitější ze zdrojů je definice dialogových oken. Tato mohou obsahovat spousty různých ovládacích i statických prvků, jejichž popis je bohužel mimo rámec tohoto tutoriálku. V dalších dílech se pokusíme vytvořit funkční a trochu zajímavější Hello World. Věřím, že předchozí a díly nebyly příliš zajímavé, ale je třeba si uvést alespoň základní prvky Windows API pokud nevlastníte žádnou publikaci nebo referenční příručku.
Základ programování ve Windows a C++ (8.) Hello World 2001 - konečně trochu použitelná aplikace Předchozí čtyři díly tohoto tutoriálku nejspíš nebyly příliš zajímavé, avšak pro ty, kteří si ještě nesehnali nějakou referenční příručku jazyka Windows C++, naprosto nezbytné. Seznam a krátký popis alespoň několika nejdůležitějších zpráv systému Windows bude velmi užitečný pro práci s okny i jinými dialogovými prvky. Základní popis zdrojů také nebyl zdaleka vyčerpávající, ale alespoň čtenář mého tutoriálku ví, že něco takového existuje. Pokud vám smysl předchozích článků trochu unikl a, stejně jako já, potřebujete vidět to všechno v akci než celý problém pochopíte, nezoufejte, nabízím vám celý, funkční a trochu lepší Hello World. Tento program demonstruje základní práci se zprávami, oknem, myší a zdroji. A protože asi nejsem jediný, kdo rád stahuje z internetu zdrojové kódy, které nefungují, snažil jsem se napsat následující program tak, aby stačilo pouze zkopírovat jej z prohlížeče do nového prázdného projektu každého kompilátoru, který dodržuje normu ANSI C++ a zkompilovat. Pokud využíváte Dev-C++, pak by měl vytvářený projekt obsahovat následující soubory: hello.dev, main.cpp, resource.h a rsrc.rc První soubor, který uvedu, je resource.h a obsahuje pouze definice všech prvků použitých v hlavním okně. #define #define #define #define #define #define #define #define #define #define #define #define #define
IDD_DIALOG1 IDR_MENU1 IDC_BUTTON1 IDC_BUTTON2 IDC_BUTTON3 IDC_BUTTON4 IDC_BUTTON5 IDC_BUTTON6 IDC_EDIT ID_UKONCIT ID_ABOUT IDC_STATIC IDC_TEXTIK
101 102 1000 1001 1002 1003 1004 1005 1006 2000 2001 -1 3000
Čísla použitých prvků jsou dle standardu Microsoftu, ale můžete si je libovolně měnit. Další soubor, který je třeba uvést je soubor obsahující zdroje, tedy rsrc.rc: #include <winresrc.h> #include "resource.h" // Budeme potřebovat náš soubor s definicemi a soubor s definicemi // stylů atd. (winresrc.h) 500 ICON MOVEABLE PURE LOADONCALL DISCARDABLE "E:/CODE/DEV-CPP/Icon/MAINICON.ICO" // Sem můžeme vložit nějakou vlastní ikonku, aby Windows nepoužila // v průzkumníkovi tu implicitní škaredou IDD_DIALOG1 DIALOG DISCARDABLE 0, 0, 384, 264 STYLE WS_MINIMIZEBOX | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU CAPTION "Hello World 2001" // Také použijeme menu, uvedené dále v tomto kódu MENU IDR_MENU1 FONT 8, "MS Sans Serif" BEGIN // Toto tlačítko je zde jen na parádu, vytváří podklad pro text PUSHBUTTON "",IDC_BUTTON1,11,17,360,107,WS_DISABLED | NOT WS_TABSTOP // IDC_TEXTIK - centrovaný text, včetně impliciního řetězce CTEXT "\n\n\n\n\nH E L L O W O R L D\n\n\n2 0 0 1" ,IDC_TEXTIK,14, 20,354,101,NOT WS_GROUP // Nějaká tlačítka, která použijeme v programu DEFPUSHBUTTON "Použít",IDC_BUTTON6,233,151,132,31 GROUPBOX "Ovládací prvky",IDC_STATIC,10,199,361,47 PUSHBUTTON "Minimalizovat",IDC_BUTTON2,18,214,72,21 PUSHBUTTON "Přepnout",IDC_BUTTON3,98,214,72,21 PUSHBUTTON "O programu",IDC_BUTTON4,207,214,72,21 PUSHBUTTON "Ukončit",IDC_BUTTON5,288,214,72,21
// Úplně nanic text, ale každý si rád dá do aplikace sv ůj copyright CONTROL "(C) 2001",IDC_STATIC,"Static",SS_SIMPLE | WS_DISABLED | WS_GROUP,351,252,29,8 // Rámeček GROUPBOX "Zpráva v okně",IDC_STATIC,10,141,361,47 CONTROL "Text zprávy:",IDC_STATIC,"Static",SS_SIMPLE | WS_GROUP,26,161,42,8 // A nakonec editační pole EDITTEXT IDC_EDIT,77,151,152,31,ES_MULTILINE | WS_VSCROLL | 0x1000 END // Také budeme v naší aplikaci používat jednoduché menu // pouze se dvěmi možnými volbami IDR_MENU1 MENU DISCARDABLE BEGIN POPUP "&Program" BEGIN MENUITEM "&Ukončit\tAlt-F4", ID_UKONCIT END POPUP "&Nápověda", HELP BEGIN MENUITEM "&O programu...", ID_ABOUT END END V tomto zdrojovém kódu jsme vytvořili okno s několika ovládacími prvky a jednoduché menu. Také bych měl uvést, že velikost a pozice jednotlivých prvků není v pixelech, ale v dialogových jednotkách, jejichž velikost se mění v závislosti na použitém fontu a nastavení Windows. A nakonec hlavní zdrojový kód programu: // Připojíme všechny potřebné knihovny #include <windows.h> #include <string.h> #include <stdio.h> #include "resource.h" // Vytvoříme prototyp funkce, která bude spravovat naše okno BOOL CALLBACK WndProc(HWND,UINT,WPARAM,LPARAM); int STDCALL WinMain (HINSTANCE hInst, HINSTANCE hPrev, LPSTR lpCmd, int nShow) { // Vše co nám stačí je otevřít okno a předat řízení na WndProc // Proč vytvářet okno přes CreateWindow a zpracovávat smyčku zpráv, // když to všechno za nás mohou udělat Windows DialogBox(hInst,MAKEINTRESOURCE(IDD_DIALOG1),NULL,WndProc); return(0); } // Toto byl pravděpodobně nejkratší main kód, který jste kdy napsali
BOOL CALLBACK WndProc(HWND hwnd,UINT Message,WPARAM wParam,LPARAM lParam) { // Zpravy, ktere nebudeme zpracovavat vraci FALSE aby Windows // samy rozpoznaly jestli je maji predat DefWindProc. switch(Message){ case WM_INITDIALOG: // Byl otevren dialog... nechci aby si s tím hrál DefWndProc return TRUE; case WM_COMMAND: switch(LOWORD(wParam)){ // Prikaz vzesel od ovl. prvku // Nebudeme testovat systemovy kod, protoze je nam jedno // jestli to bylo kliknuti nebo dvojklik. case IDC_BUTTON5: // Uživatel kliknul na tlačítko Ukončit case ID_UKONCIT: // Nebo vybral Ukončit z menu case IDCANCEL: // Nebo kliknul na X vpravo nahoře v okně {
int a; // - můj oblíbený název proměnných // MessageBox jsme probírali v prvním díle a = MessageBox (NULL, "Opravdu si přejete ukončit tento program?" , "Potvrzení ukončení", MB_YESNO | MB_DEFBUTTON2 | MB_ICONQUESTION); if (a==IDNO) break; // Pokud ne tak vyskočíme ven // brnky brnky na nervy a = MessageBox (NULL, "A víte to jistě?" , "Potvrzení ukončení", MB_YESNO | MB_DEFBUTTON2 | MB_ICONQUESTION); if (a==IDNO) break; EndDialog(hwnd,IDCANCEL); // Tato procedura zavírá dialogové okno } break; case IDC_BUTTON2: // Tlačítko minimalizovat { CloseWindow(hwnd); // Nepříliš intuitivní pojmenování funkce (pozůstatek z Win16) } break; case IDC_BUTTON3: // Standartizovane pipnuti je ve Windows taky celkem jednoduche MessageBeep(0); break; case IDC_BUTTON4: case ID_ABOUT: // Tlačítko O programu nebo z menu. MessageBox (NULL, "Hello World 2001 Ukázkový prográmek, pro demonstraci dialog ů, menu, a zpráv." , "O programu", MB_ICONASTERISK); // Predchozi tri radky vypadaji otresne, ale Mingw kompilator je nejaky divny a neumi \n break; case IDC_BUTTON6: // Tlačítko Použít { // nacteme retezec z editacniho pole a zkopirujeme do statickeho textu char *text; // Sestavime si novy text text=(char*)GlobalAlloc(GMEM_FIXED|GMEM_ZEROINIT,65536); SendDlgItemMessage(hwnd,IDC_EDIT,WM_GETTEXT,65535,(long)text); SendDlgItemMessage(hwnd,IDC_TEXTIK,WM_SETTEXT,65535,(long)text); // Toto je v podstate to same jako pouzit funkce // GetDlgItemText a SetDlgItemText GlobalFree(text); // a je to } break; } return TRUE; case WM_MOVE: // Také můžeme zpracovávat zprávu, že uživatel pohybuje oknem { char *titulek; // Sestavime si novy titulek titulek=(char*)GlobalAlloc(GMEM_FIXED|GMEM_ZEROINIT,256); sprintf(titulek,"Hello World 2001 >>> Probiha presun okna na: %4.4d; %4.4d",LOWORD((DWORD)lParam),HIWORD((DWORD)lParam)); // a posleme ho sami sobe - teda svemu oknu SendMessage(hwnd,WM_SETTEXT,0,(long)titulek); GlobalFree(titulek); } return TRUE; case WM_MOUSEMOVE: // Také můžeme testovat pohyb myši v našem okně { char *titulek; // Sestavime si novy titulek titulek=(char*)GlobalAlloc(GMEM_FIXED|GMEM_ZEROINIT,256); sprintf(titulek,"Hello World 2001 >>> Pozice mysi je: %4.4d,%4.4d" , LOWORD((DWORD)lParam), HIWORD((DWORD)lParam) ); // A hotový text nastavíme na titulek SendMessage(hwnd,WM_SETTEXT,0,(long)titulek); GlobalFree(titulek); } return TRUE; // Takto lze testovat zprávy všeho možného // Zprávy, které nebudeme zpracovávat předáme DefWndProc tak že vrátíme FALSE default: return FALSE;
} return TRUE; } Pokud odstraníte všechny komentáře, přijdete na to, že ve skutečnosti je to velmi jednoduchý prográmek a má spoustu možností rozšíření. Ovládání je také intuitivní. Stačí napsat nějaký text a kliknout na Použít. Tento prográmek je prozatím zakončením tohoto krátkého tutoriálku. V žádném případě jsem nevyčerpal všechna zákoutí Windows API, avšak úzký základ jsem popsal. Pokud vůbec netušíte o co v tomto tutoriálu šlo, bude třeba prostudovat nějaký tutoriálek DOS céčka a vytvořit několik funkčních programů. Je pravda, že programováním pro Windows se mnoho věcí zjednodušší, ale jen v případě, že to umíte udělat složitě.
Základ programování ve Windows a C++ (9.) Okna, procesy oken a jak na ně. Ve Windows rozeznáváme dva typy procesů a to thready (vlákna) a okna (windows). Zásadní rozdíl je v tom, že okno lze vidět, thread nikoliv. V tomto díle si trochu přiblížíme okna, práci s nimi a s jim určenými zprávami. Tuto tématiku jsem již v některém z předchozích dílů nakousnul, ale teď se k ní vrátím podrobněji. Pokud čtete tento tutoriálek od začátku, jistě víte jak vytvořit klasické dialogové okno pomocí předdefinované šablony ve zdrojích a pracovat s ním pomocí zpráv. Avšak mnohdy je třeba pracovat s oknem na mnohem nižší úrovni než na té, kterou nám poskytuje klasické vyvolání dialogu. Cesta, kterou bych zde chtěl přiblížit je obvykle uváděna na začátcích tutoriálků k Windows Céčku (++), ale pro vývoj jednoduchých až mírně složitých aplikací není třeba a vše lze zajistit pomocí dialogů, ať modálních či nemodálních. Při programování pro Windows je za okno považován jakýkoli prvek viditelný na obrazovce a to ať už okno jako šedý podklad s titulkovým pruhem a obtáhnuté světlejšími či tmavšími čárami k vytvoření efektu 3D, nebo prosté tlačítko, rámeček atd. Vytváříme-li toto vše pomocí šablony ze zdrojů, obstarají Windows za nás všechny potřebné akce a to jak vytvoření a odstranění těchto objektů, tak skoky tabulátoru a další minoritní akce. Pokud chceme tyto prvky nějak speciálně ovládat, musíme je spravovat ručně. Zvláště je to třeba při programování aplikací, komunikujících s uživatelem na základě dynamicky měněných oken. Jak na to? V základě budeme potřebovat spoustu proměnných typu HWND, tedy rukojetí na jednotlivé okna. Podle těchto rozpoznávají Windows jednotlivá okna. Jedno HWND pro každé okno, které budeme používat (okno, tlačítka, posuvníky, atd...). Statické okna, které nebudeme upravovat (např. rámečky) nemusí mít svou rukojeť, pokud je nebudeme měnit. Ke tvorbě a likvidaci oken využijeme funkce CreateWindow, CreateWindowEx a DestroyWindow. Dalším rozdílem je, že pro celou instanci aplikace reprezentované hlavním oknem (které je mimochodem třeba vytvářet dle třídy okna, kterou si musíme vytvořit a zaregistrovat) musí náš program zpracovávat smyčku, tj. testovat přítomnost zprávy, její přeložení a odeslání proceduře hlavního okna. Procedura okna je podobná procedurám dialogových oken. Rozdíl je, že místo vrácení TRUE nebo FALSE vracíme co chceme (skoro), tudíž pokud potřebujeme aby se pro některou zprávu provedla implicitní akce voláme funkci DefWndProc s danými parametry. Ve skutečnosti není práce s těmito okny zase tak složitá, jak by se mohlo na první pohled zdát. Vše potřebné k vytvoření jednoduchého okna dostanete k dispozici, pokud v Dev-C++ vytvoříte nový projekt podle šablony Windows Application a sem již můžete vložit vlastní kód. Pokud ovládáte angličtinu, vyčtete toho spoustu v komentářích, zde uvedu pouze tu část kódu, která zprostředkuje zobrazení textu uprostřed okna i při změně jeho velikosti. Mimo funkce vytvoříme globální proměnnou textik, která bude obsahovat ukazatel na statický objekt textu a proměnnou do které si uložíme instanci aplikace. HWND textik = NULL; HINSTANCE hInst; Samozřejmě někam na začátek funkce WinMain vložíme řádek: ::hInst = hThisInstance;
A do procedury okna, přesněji do switche, vložíme následující bloky: case WM_CREATE: // místo WM_INITDIALOG (u procedur dialog ů) zpracováváme WM_CREATE textik = CreateWindow( "STATIC", "libovolný text", SS_CENTER | WS_CHILD, // styl centrovaneho textu 0,0,0,0, // pro rozmery zatim pouzijeme 0 hwnd,NULL,hInst,NULL); ShowWindow(textik,SW_SHOW); // Okno (text) zobrazíme break; Styl SS_CENTER místo SS_LEFT jsem zvolil záměrně. Usnadní nám to programování centrování textu. case WM_PAINT: { // Zajistime provedeni standartni sluzby pro kresleni okna DefWindowProc(hwnd, message, wParam, lParam); // Potrebujeme rozmery okna RECT okno; GetWindowRect(hwnd, &okno); int sirka = okno.right - okno.left; int vyska = okno.bottom - okno.top; // Tipnem si kolik pixelu asi bude ten textik // Zbytek za nas udela SS_CENTER int tip = 200; int posy = (vyska/2) - 10; // 10= polovina vysky znaku int posx = (sirka/2) - (tip/2); MoveWindow(textik, posx, // posy, // tip, // 20, // TRUE);// } break;
pozice X Y sirka vyska vlajka prekresleni
Správa takovýchto oken a jejich prvků není jednoduchá, avšak jakmile si ji dostanete do podvědomí, budete okna ovládat bez problémů.
Základ programování ve Windows a C++ (10.) Základy kreslení ve Windows, trocha teorie a jednoduchý příklad Když jsem se učil programovat pro Windows, přišlo mi neskutečně složité kreslení na obrazovku. Vzhledem k jednoduchosti použití BGI knihoven pod BC, TC nebo TP je to opravdu pro začínajícího programátor trochu obtížné k pochopení. Nejprve si musíme uvědomit, že obrazovka už není jenom naše. Je zde spousta jiných oken, plocha a také hlavní panel s tlačítkem Start. Nejprve si vysvětlíme, jak jsou pod Windows API okna kreslená. Standardně máme pro kreslení k dispozici okno a k tomuto oknu náleží procedura, která reaguje na zprávy od systému nebo jiných oken a procesů. Když píšeme vlastní proceduru okna, voláme při obdržení zpráv, které nezpracováváme, funkci DefWndProc (popř. DefDlgProc nebo u dialogů vracíme 0) a takto zajistíme, že Windows za nás udělají zbytek práce, tj. i kreslení. Pro kreslení oken Windows posílají dvě zprávy, a to WM_PAINT a WM_NCPAINT. WM_PAINT je zpráva, kterou Windows oznamují proceduře okna, že vyžadují znovu vykreslit obsah klientské části okna. Standardně v této části okna nic k překreslení není, a tak DefWindProc neprovádí nic. WM_NCPAINT obdrží okno v případě, že je třeba překreslit neklientskou část okna. Do této části spadá rámeček, titulkový pruh a jeho obsah, popř. menu. Obvykle není třeba tuto zprávu zpracovávat. My budeme odpovídat na zprávu WM_PAINT. Začínáme jednodušším příkladem Chceme-li kreslit do okna, budeme v programu potřebovat několik dalších proměnných. Nejprve je to hDC typu HDC, tedy handle na kontext zařízení, podle něhož Windows poznají, do kterého okna kreslit, a dále struktura typu PAINTSTRUCT, ve které si naše aplikace uchová informace o kreslení.
HDC hDC; PAINTSTRUCT ps; Předtím než začneme vlastní kreslení, použijeme funkci BeginPaint, kterou zinicializujeme kreslící mechanizmy Windows vzhledem k našemu oknu. Za parametry uvedeme handle na naše okno a referenci na strukturu PAINTSTRUCT. hDC = BeginPaint( hwnd , &ps ); Pro kreslení si Windows uchovávají sadu objektů (neplést s objekty v C++), kterými se kreslí. Jsou to Bitmapa, Štětec (brush), Font, Pero (pen) a Oblast (region). Pero je používáno ke kreslení především čar. Štětci kreslíme výplně definovaných oblastí. Font užíváme k definici stylu písma. Ke kreslení můžeme vytvořit vlastní objekty nebo použít předdefinované. Aby Windows použily náš objekt, musíme jej vybrat funkcí SelectObject. V následujícím řádku nahradíme aktuální pero novým vlastním. Toto pero bude plná čára o tloušťce 1 pixel a modré barvě. SelectObject(hDC, CreatePen( PS_SOLID, 1, 0x00FF0000 ) ); A nyní již můžeme kreslit. Jako v BGI přesuneme aktuální pozici kurzoru na souřadnice [0,0], tj. do levého horního rohu našeho okna. MoveToEx( hDC, 0, 0, NULL ); A z tohoto bodu nakreslíme čáru do bodu [100,100]. LineTo( hDC, 100, 100 ); Nyní na pozici [30,10] nakreslíme červený bod. SetPixel( hDC, 30, 10, 0x000000FF ); A po dokončení kreslení zavoláme funkci EndPaint, která dokončí naše kreslící operace a zjednodušeně řečeno způsobí, že se to, co jsme kreslili, objeví v okně. EndPaint( hwnd, &ps ); Jak jednoduché. V příštím díle si povíme něco o tom, jak do okna umístit jakýkoliv text.
Základ programování ve Windows a C++ (11.) Kreslení textu do okna, nastavení fontu a vlastností textu V minulém díle jsem vám předvedl, jak jednoduše kreslit do okna čáry a body. K tomuto bych ještě mohl uvést, že standardní barvy systému, které si můžete nastavit v ovládacích panelech (panel obrazovka), lze získat funkcí GetSysColor. Výčet parametrů (prefix COLOR_) pak naleznete v souboru WINUSER.H nebo v referenci Win32. Začněme s tím, jak do okna nakreslit text. Opět bychom měli odpovídat na zprávu WM_PAINT. Funkce BeginPaint a EndPaint již nebudu uvádět, viz minulý díl. Chceme-li nakreslit text, musíme si nejprve vytvořit font. K tomuto účelu si vytvoříme proměnnou typu HFONT, třeba hf, která bude obsahovat handle na námi vytvořený font, který budeme používat ke kreslení textu. Font si tedy vytvoříme funkcí CreateFont. HFONT hf; hf = CreateFont( 16, // Výška písma. Je-li zadáno 0 pak je použita implicitní hodnota, // je-li menší než 0, pak je použita hodnota v pixelech. 0, // Logická průměrná šířka znaku ( 0 - implicitní ) 0, // Úhel sklonu písma
0, // Úhel orientace vzhledem k řádku FW_BOLD, // Řez písma 0, // Určuje, zda je písmo kurzíva 0, // Určuje, zda je písmo podtržené 0, // Určuje, zda je písmo přeškrtnuté DEFAULT_CHARSET, // Určuje typ znakové sady OUT_DEFAULT_PRECIS, // Určuje preciznost výpočtu stylu písma CLIP_DEFAULT_PRECIS,// Přesnost umístění DEFAULT_QUALITY, // Kvalita vykreslování písma FF_ROMAN, // Typ písma, který se zvolí není-li v systému vypraný font "Arial CE" // Font, kterým se bude text kreslit ); Dále tento font vybereme funkcí SelectObject. SelectObject( hDC, (HGDIOBJ) hf ); Před začátkem kreslení si ještě musíme vytvořit strukturu RECT, kterou následně předáme Windows. Náš text bude kreslen do takto vymezené oblasti. RECT rect = { 0,0,200,50 }; Nyní už Windows ví, že při dalším kreslení textu budeme používat náš font a kam chceme kreslit, takže nám nic nebrání nakreslit do našeho okna libovolný text. K tomuto můžeme použít několik funkcí. První z nich je DrawText, dále její rozšířená verze DrawTextEx, TextOut a nebo její rozšířená verze ExtTextOut. Příklady jsou zde. DrawText( hDC, "PC Svět", -1, &rect, DT_LEFT | DT_NOPREFIX | DT_SINGLELINE ); TextOut( hDC, 0, 0, "PC Svět", 7); Ať už jste použili jakoukoli funkci, měl by být text nakreslen v okně. Vzhled textu může být dále upraven API funkcemi jako SetTextColor, SetBkColor a SetBkMode třeba takto: SetTextColor( hDC, 0x0000FF00 ); SetBkColor( hDC, 0x000000FF ); SetBkMode( hDC, TRANSPARENT /*nebo OPAQUE*/ ); Tímto způsobem můžete vytvořit velmi zajímavé nadpisy a popisky, avšak z uživatelova pohledu by mohla být spousta barevných textů spíš rušivá a může znepříjemňovat práci s programem. Jako vždy platí: všeho s mírou.
Základ programování ve Windows a C++ (12.) Popis funkcí a postup pro kreslení bitmap a ikon do vašeho formuláře Nakreslení bitmapy do okna je další z důležitých znalostí autorů programů pro Windows. I v profesionálních programech se vždy najde v okně místo, kam vložit logo. Práce s bitmapami BMP je relativně složitá. Z důvodů paměťové náročnosti a především kvůli tzv. flicker free kreslení neboli kreslení bez problikávání obrazu je třeba bitmapy přenášet na obrazovku pomocí dalšího kontextu zařízení, který je umístěn v paměti. Jak tedy na to. Nejprve, nejlépe při zprávě WM_CREATE načteme náš obrázek funkcí LoadBitmap a k udržení handle na obrázek si mimo proceduru okna definujeme proměnnou HBITMAP. Předpokládejme tedy, že máme ve zdrojích umístěn obrázek, jehož identifikátor je 100: 100 BITMAP PRELOAD MOVEABLE "obr.bmp" Programujeme tedy:
HBITMAP hb; ... hb = LoadBitmap(hInstance, MAKEINTRESOURCE(100)); První parametr funkce LoadBitmap (hInstance) je handle na instanci aplikace, které obdržíme při vstupu do funkce WinMain. Jako odpověď na WM_PAINT nejprve zavoláme funkci BeginPaint (viz díl 10.). A nyní už vytvoříme kontext zařízení, který bude v paměti a do něhož nahrajeme náš obrázek. HDC hMemory = CreateCompatibleDC(hDC); A funkcí SelectObjekt zvolíme do tohoto kontextu naši bitmapu. SelectObject(hMemory, hb); Windows nám dále poskytují velmi rychlou funkci BitBlt, kterou použijeme k přenesení bitmapy z paměťového kontextu do kontextu naší aplikace. Předpokládáme, že velikost bitmapy je 123x456 pixelů. BitBlt(hDC, 0,0, 123, 456, hMemory, 0, 0, SRCCOPY); A nyní už můžeme vytvořený paměťový kontext odstranit a uvolnit tak paměť. DeleteDC(hMemory); Takto můžeme nakreslit na obrazovku libovolnou bitmapu. Potřebujeme-li nakreslit do okna jednoduchou ikonku, rozhodně nebudeme ale používat takto složitý postup. Ke kreslení ikon můžeme použít funkci DrawIcon nebo DrawIconEx, nejprve ale musíme ikonku načíst. To můžeme provést funkcí LoadIcon nebo LoadImage. Tyto funkce jsou rozdílné v tom, že funkcí LoadImage můžeme načíst i kuzor a bitmapy, definovat rozměry ikony, nastavení průhledných částí a barev, zatímco funkce LoadIcon načte ikonu s uživatelem nastavenými rozměry (SM_CXICON a SM_CYICON). Funkcí LoadImage můžeme načítat také ikony ze souborů a to tak, že název souboru uvedeme místo názvu zdroje a specifikujeme vlajku LR_LOADFROMFILE. Příklady načtení ikony: hi = LoadIcon(hInstance, MAKEINTRESOURCE(200) ); hi = LoadImage(hInstance, MAKEINTRESOURCE(200), IMAGE_ICON, 48, 48, LR_DEFAULTCOLOR ); Proměnná hi bude typu HICON a udržuje nám handle na načtenou ikonu. A do našeho okna můžeme nakreslit ikonku na pozici [10,10] třeba takto: DrawIcon( hDC, 10, 10, hi ); Myslím, že kreslení grafických prvků pod Windows je nyní dostatečně objasněno. Více informací o kreslení pod Windows hledejte především v referencích Windows API.
Základ programování pro Windows v C++ (13.) Standardní ovládací prvky - tlačítka, rámečky, zaškrtávací políčka, přepínače Jak jsme si v předchozích dílech seriálu ukázali, není problém vytvořit okno aplikace pomocí resource (zdrojů). Pro tvorbu takovýchto dialogů můžeme na internetu najít i několik kvalitních programů, např. Weditres. Pokud chceme vytvořit složitější aplikaci, mohou nám takovéto dialogy v mnohém pomoci, ale základní hlavní okno aplikace takto vytvářet určitě nebudeme. Zapomeňme nyní na chvíli na dialogy a pojďme se to vše naučit dělat ručně. Jak jste se mohli dočíst v díle 9, pro vlastní tvorbu okna použijeme funkci CreateWindow, případně CreateWindowEx. Funkce CreateWindowEx je definována takto: HWND CreateWindowEx( DWORD dwExStyle, LPCTSTR lpClassName, LPCTSTR lpWindowName, DWORD dwStyle, int x, int y,
// // // // // //
rozšířené styly okna název třídy okna jméno okna styly okna horizontální pozice okna vertikální pozice okna
int nWidth, int nHeight, HWND hWndParent, HMENU hMenu, HINSTANCE hInstance, LPVOID lpParam );
// // // // // //
šířka okna výška okna handle na rodi čovské okno handle na menu okna handle instance b ěžící aplikace další data pro tvorbu okna
Třídu okna vytvoříme a registrujeme podobně jako v příkladu v díle č.3, jméno okna specifikuje vnitřní název okna, který různé třídy oken používají jinak. Standardní okna, jak je zná uživatel, jej interpretují jako název v titulkovém pruhu. LPVOID lpParam pro další data pro tvorbu okna využívají především MDI aplikace, v jednoduchých programech není potřeba. V tomto článku se ovšem nebudeme věnovat samotnému oknu, nýbrž jeho prvkům. Jak již bylo vysvětleno v minulých dílech, jsou všechny prvky okna v podstatě také okny, jediný rozdíl je v tom, že tato okna mají přiřazen styl WS_CHILD, kterým toto okno přiřazujeme jinému (mateřskému neboli top-level) oknu. Pro všechny běžně používané prvky definují Windows vždy jednu třídu okna, kterou můžeme používat bez nutnosti vytvářet si jinou. V každém programu nalezneme tlačítko. Pro všechna tlačítka je definována třída "BUTTON". Pokud tento specifikujeme ve funkci CreateWindow a jako styl specifikujeme minimálně WS_CHILD a WS_VISIBLE, vytvoří nám Windows klasické tlačítko. Vzhled tohoto tlačítka můžeme dále specifikovat styly: • • • • • • • • •
BS_PUSHBUTTON - vytvoří klasické tlačítko - není třeba jej specifikovat BS_DEFPUSHBUTTON - vytvoří tlačítko jež je zvýrazněno a mělo by být aktivní pro vstup z klávesnice BS_GROUPBOX - nevytvoří klasické tlačítko ale jakousi drážku pro ohraničení určité oblasti okna, daný název tlačítka je použit jako nadpis tohoto rámečku BS_CHECKBOX - vytvoří místo tlačítka zaškrtávací políčko, vhodné do formulářů, kde může být zvoleno více voleb. U tohoto stylu obrdží aplikace informaci o kliknutí, ale změnu stavu již musí provést program. Text je zarovnán vpravo od tohoto políčka, není-li specifikováno jinak. BS_AUTOCHECKBOX - vytvoří zaškrtávací políčko jako BS_CHECKBOX ale na rozdíl od BS_CHECKBOX nechá na Windows správu zaškrtávání. BS_3STATE - podobně jako BS_CHECKBOX avšak políčko může mít i neurčitý stav (zašedlé). BS_AUTO3STATE - podobně jako BS_AUTOCHECKBOX avšak políčko může mít i neurčitý stav (zašedlé). BS_RADIOBUTTON - místo tlačítka je vytvořen kruhové políčko, které je určeno především do seznamů, kdy jen jedna položka může být zaškrtnuta. Funkce je podobná jako u BS_CHECKBOX. BS_AUTORADIOBUTTON - podobně jako u BS_RADIOBUTTON ale správa reakce na kliknutí je ponechána na Windows.
U radio nebo check boxů můžeme dále specifikovat tyto styly: • • • • •
BS_LEFTTEXT - zarovná text vlevo od daného prvku BS_LEFT - zarovná text zleva na pravé straně prvku (implicitní) BS_PUSHLIKE - checkbox, 3statebox nebo radiobutton získají tímto stylem vzhled jako klasické tlačítko. BS_RIGHT - zarovná text zprava na pravé straně prvku (implicitní) BS_RIGHTBUTTON - stejná funkce jako BS_LEFTTEXT
Klasická tlačítka mohou mít dále tyto styly: • • • • • • • • •
BS_BITMAP - specifikuje, že tlačítko bude zobrazovat bitmapu BS_BOTTOM - umístí text na dolní okraj tlačítka BS_CENTER - horizontálně vycentruje text v tlačítku BS_ICON - specifikuje, že tlačítko zobrazuje ikonu BS_MULTILINE - v případě potřeby bude text na tlačítku zalomen do více řádků BS_TEXT - specifikuje, že tlačítko zobrazuje text BS_TOP - umístí text k hornímu okraji tlačítka BS_VCENTER - vertikálně vycentruje text na tlačítku dále lze použít styly BS_LEFT, BS_RIGHT, v tomto případě však umisťují text k danému okraji tlačítka
Pro všechny typy tlačítek lze dále specifikovat styly: • •
BS_NOTIFY - tlačítko posílá mateřskému oknu notifikační zprávy jako BN_DBLCLK, BN_KILLFOCUS, BN_SETFOCUS a další BS_OWNERDRAW - tlačítko je vykreslováno aplikací (na ukázku ručního kreslení tlačítek se můžete těšit do některého z příštích dílů).
Některé styly je možné kombinovat a tím dosáhnout zajímavých výsledků. Zkuste si třeba přiřadit tlačítku WS_DLGFRAME. Získáte tak velice zajímavý okraj pro dané tlačítko. A ještě poslední detail. Pokud se vám nelíbí klasický styl textu na tlačítku, není žádný problém získat pomocí funkce CreateFont handle na font a přiřadit jej danému tlačítku funkcí SendMessage( hwnd, WM_SETFONT, (WPARAM)hfFont, MAKELPARAM(1, 0) ); kde hwnd je handle na dané tlačítko a hfFont je handle přiřazovaného fontu. Ono je sice hezké mít pěkné tlačítko, jenže pokud s ním neumíme komunikovat, je nám na nic. Windows definují pro komunikaci s tlačítky tyto zprávy: • • • • • • • •
BM_CLICK - parametry žádné - posláním této zprávy tlačítku můžete nasimulovat stisk tlačítka, jako by bylo stisknuto uživatelem. BM_GETCHECK - parametry žádné - vrátí stav checkboxu nebo radiobuttonu. BST_CHECKED = tlačítko je zaškrtnuto, BST_UNCHECKED = tlačítko není zaškrtnuto, BST_INDETERMINATE = tlačítko je v mezistavu, je zašedlé BM_GETIMAGE - parametry žádné - vrátí handle na ikonu nebo bitmapu použitou pro dané tlačítko BM_GETSTATE - parametry žádné - vrátí aktuální stav tlačítka. Vrácená hodnota je bitovým součtem hodnot uvedených v BM_GETCHECK a BST_FOCUS má-li tlačítko fokus, případně BST_PUSHED je-li tlačítko stisknuto. BM_SETCHECK - wParam = stav tlačítka (jako BM_GETCHECK) - nastaví nový stav tlačítka BM_SETIMAGE - lParam = handle na novou ikonu nebo bitmapu - nastaví tlačítku ikonu nebo bitmapu BM_SETSTATE - wParam = stav tlačítka (jako BM_GETSTATE) - nastaví nový stav tlačítka BM_SETSTYLE - wParam = nový styl tlačítka; lParam = MAKELPARAM(fRedraw, 0) - fRedraw je nenulový má-li být tlačítko nově překresleno - nastaví nový styl tlačítka
Dále bychom od tlačítek měli odchytávat notifikační zprávy, abychom tak získali přehled o tom, co s oknem aplikace provádí uživatel. Notifikační zprávy od tlačítek mimo BN_CLICKED obdržíme v případě, že je definován styl BS_NOTIFY. BN_CLICKED obdrží aplikace vždy. Všechny notifikační zprávy jsou posílány v kontextu zprávy WM_COMMAND, a to jako horní slovo parametru wParam (HIWORD(wParam)). V parametru lParam dostaneme vždy handle na daný prvek, a ve spodním slovu parametru wParam můžeme nalézt identifikační číslo daného prvku v okně. Notifikační zprávy od tlačítek jsou: • • • • • • •
BN_CLICKED - již zmiňovaná, mateřské okno ji obdrží pokaždé, když uživatel na některé tlačítko klikl. BN_DBLCLK - oznamuje, že uživatel provedl dvojklik nad daným tlačítkem. Je posíláno pouze radiobuttonům a aplikací kresleným tlačítkům. BN_DISABLE - oznamuje, že tlačítko bylo znefunkčněno. BN_HILITE, BN_PUSHED - oznamuje, že nad tlačítkem je kurzor myši a uživatel stiskl tlačítko myši. BN_KILLFOCUS - oznamuje, že tlačítko již nemá fokus BN_SETFOCUS - oznamuje, že tlačítko získalo fokus a reaguje nyní na příkazy z klávesnice BN_UNHILITE, BN_UNPUSHED - oznamuje, že stisk tlačítka byl uvolněn
Tlačítko je v GUI Windows asi nejpoužívanějším prvkem a žádná aplikace by se bez něj neobešla. V žádném případě však práce s ním není tak náročná, jak se může z tohoto článku zdát. Pomocí této stručné reference nyní můžete pracovat s klasickými tlačítky, zaškrtávacími políčky, přepínacími tlačítky (radiobuttony) a rámečky pro tyto objekty. V příštích dílech si ukážeme podobnou stručnou referenci pro další standardní prvky oken.
Základ programování pro Windows v C++ (14.) Standartní ovládací prvky - editační pole Mezi standardními Common controls pro Windows nalezneme dvě třídy, řešící tento problém. Třídu "EDIT" a třídu "RICHEDIT". Protože je Richedit trochu složitější, začneme klasickými editačními políčky "EDIT". Vytvořením okna za použití třídy EDIT získáme běžné editační políčko, jaké známe ze spousty programů a pomocí kterého můžeme komunikovat s uživatelem našeho programu. Do takovéhoto běžného políčka můžeme nechat uživatele vepsat libovolný text a na jeho základě se dále rozhodnout, jak bude program pokračovat. Pokud při tvorbě nespecifikujeme žádný styl, vytvoří nám Windows jen bílé pole, do kterého je možné text vepsat. Pro úpravu vzhledu políčka můžeme použít tyto styly:
• • • • • • • • • • • • • •
ES_AUTOHSCROLL - definováním tohoto stylu specifikujeme, že pokud uživatel začne vepisovat text delší než je velikost políčka, je tento text posouván směrem vlevo. V případě, že tento styl nespecifikujeme, nelze do políčka vepsat více znaků, než je možné zobrazit najednou. ES_AUTOVSCROLL - pro víceřádková editační pole. Tento styl specifikuje, že v případě, stiskne-li uživatel na konci věty ENTER a je tento konec na posledním zobrazitelném řádku, text je posunut směrem nahoru. ES_CENTER - je-li pole víceřádkové, je text v něm zarovnán na střed. ES_LEFT - zarovná text vlevo - implicitní, není třeba uvádět ES_LOWERCASE - všechna velká písmena vepsaná do pole jsou automaticky konvertována na malá ES_MULTILINE - specifikuje, že dané pole je víceřádkové. ES_NOHIDESEL - pole s tímto stylem zobrazuje vybranou část textu i v případě, že nemá fokus. ES_NUMBER - do pole s tímto stylem mohou být vepsána pouze číslice. ES_OEMCONVERT - text zadaný do tohoto pole je převeden ze znakové sady Windows do OEM znakové sady a zpět. Vhodné pro použití pro editační pole, která obsahují názvy souborů. ES_PASSWORD - definuje, že pole obsahuje heslo. V takovémto případě v poli nelze použít kopírování a text je zobrazen pomocí zástupného znaku (implicitně hvězdička) ES_READONLY - specifikuje, že dané pole je jen pro čtení. Do takovéhoto políčka nemůže uživatel nic vepsat. ES_RIGHT - v případě víceřádkového editačního pole, zarovná text vpravo. ES_UPPERCASE - všechna písmena zapsaná do pole s tímto stylem jsou automaticky převáděna na velká. ES_WANTRETURN - v případě víceřádkového editačního pole specifikuje, že do něj lze zapsat i znak nového řádku, klávesou ENTER.
Dále můžeme použít i standardní styly pro všechna okna, především ale využijeme styl WS_BORDER, který dodá políčku okraj. Pole, které nemá tento styl, je vykreslováno jako bílý obdélník bez okraje. Každý takto vytvořený prvek získá od Windows určitý paměťový prostor, do kterého si ukládá svůj obsah. Implicitně je tento prostor nastaven 32k. Obsah tohoto prostoru můžeme do vlastního bufferu zkopírovat buď pomocí standardní zprávy WM_GETTEXT a nastavit obsah pole pomocí WM_SETTEXT, anebo k tomu použijeme funkce GetDlgItemText a SetDlgItemText, které je vhodné použít především u dialogů. Standardní editační pole mají dále integrovánu jednoduchou schopnost technik Undo a Redo a pomocí schránky lze kopírovat a vkládat bloky textu. S editačním políčkem můžeme komunikovat pomocí těchto zpráv: • • • • • • • • • • • • • • • • • • • • •
EM_CANUNDO - parametry žádné - zjistí, zda je možno použít zprávu EM_UNDO. EM_CHARFROMPOS - lParam = MAKELPARAM(x, y), kde x a y jsou pozice bodu v editačním poli. - vrátí ASCII hodnotu znaku, který je v editačním poli na pozici dané x a y nebo -1 je-li bod mimo oblast editačního pole. EM_EMPTYUNDOBUFFER - parametry žádné - odstraní případný záznam o poslední úpravě bufferu. Po použití této zprávy již nelze použít funkci Undo. EM_FMTLINES - wParam = [TRUE/FALSE] - přepíná mezi soft-break a hard-break zalomeními, je-li nutno zalomit text v případě, že přesahuje řádek. Více viz MSDN. EM_GETFIRSTVISIBLELINE - parametry žádné - editační pole vrátí index prvního viditelného řádku textu. EM_GETHANDLE - parametry žádné - vrátí handle na blok paměti aktuálně alokovaný pro editační pole EM_GETLIMITTEXT - parametry žádné - vrátí počet znaků, které lze maximálně zapsat do editačního pole - viz EM_SETLIMITTEXT EM_GETLINE - wParam - index řádku; lParam - adresa bufferu pro řetězec; - do bufferu umístí text, který obsahuje daný řádek a vrátí počet zkopírovaných znaků. EM_GETLINECOUNT - parametry žádné - vrátí počet řádků ve víceřádkovém editačním políčku. EM_GETMARGINS - parametry žádné - vrátí šířku levého (LOWORD) a pravého (HIWORD) okraje editačního pole EM_GETMODIFY - parametry žádné - vrátí informaci o tom, zda bylo editační pole modifikováno EM_GETPASSWORDCHAR - parametry žádné - vrátí ASCII hodnotu znaku, který je použit pro překrytí hesla EM_GETRECT - lParam = (LPRECT) lprc - lprc je adresa struktury RECT, která obdrží informace o aktuálním formátovacím obdélníku pro dané pole. Viz EM_SETRECT EM_GETSEL - wParam = adresa DWORDu, který obdrží začátek výběru; wParam = adresa DWORDu, který obdrží konec výběru - do daných proměnných se přenese pozice aktuálního výběru EM_GETWORDBREAKPROC - parametry žádné - vrátí adresu funkce, která vyhodnocuje zalamování řádků EM_LINEFROMCHAR - wParam = číslo znaku - vrátí číslo řádku, na kterém je znak udaný wParam EM_LINEINDEX - wParam = číslo znaku, -1 znamená aktuální řádek - vrátí index znaku specifikovaný řádkem EM_LINELENGTH - wParam = číslo znaku - vrátí délku řádku specifikovaného wParam EM_LINESCROLL - wParam = počet znaků horizontálně; lParam = počet znaků vertikálně - posune obsah okna o uvedený počet znaků EM_POSFROMCHAR - wParam = číslo znaku - vrátí pozici znaku, jehož pozice v řetězci je dána wParam EM_REPLACESEL - wParam = vlajka, zda je možné tuto operaci vrátit (provést Undo); lParam = ukazatel na řetězec - vymění aktuálně vybraný text za text daný lParam
• • • • •
• • • • • • • • •
EM_SCROLL - wParam může být jedna z těchto hodnot: SB_LINEDOWN = posune o řádek dolů; SB_LINEUP = posune o řádek nahoru; SB_PAGEDOWN = posune o stránku dolů; SB_PAGEUP = posune o stránku nahoru provede vertikální posun textu v okně editačního pole podle parametru EM_SCROLLCARET - parametry žádné - umístí do okna stránku textu, ve které je právě kurzor EM_SETHANDLE - wParam = handle na blok paměti - nastaví současný buffer pro editační pole dle předaného handle na paměť EM_SETLIMITTEXT - wParam = limit pro editační pole - nastaví limitní počet znaků pro editační pole. Maximálně 65535. EM_SETMARGINS - wParam = vlajky parametrů, které se budou měnit: EC_LEFTMARGIN - levý okraj, EC_RIGHTMARGIN - pravý okraj, EC_USEFONTINFO - budou použity informace o aktuálním fontu pro editační pole. Je-li EC_USEFONTINFO specifikováno, je lParam ignorován; lParam - spodní slovo specifikuje šířku levého okraje, horní slovo specifikuje šířku pravého okraje - nastaví levý a pravý okraj editačního pole dle parametrů EM_SETMODIFY - wParam = nová vlajka - nastaví vnitřní vlajku modifikace na uvedenou hodnotu EM_SETPASSWORDCHAR - wParam = ASCII hodnota znaku - nastaví znak předaný parametrem jako znak, který se v polích pro heslo zobrazuje místo znaků. EM_SETREADONLY - wParam = nová vlajka - nastaví vnitřní vlajku dle hodnoty wParam. Tato vlajka určuje, zda je editační pole pouze pro čtení EM_SETRECT - lParam je adresa struktury RECT s novými hodnotami - nastaví formátovací okraje na hodnoty uvedené strukturou RECT EM_SETRECTNP - tato zpráva je identická se zprávou předchozí vyjma toho, že editační pole není v tomto případě překreslováno EM_SETSEL - parametry jako EM_GETSEL - nastaví aktuální výběr na pozice dané parametry EM_SETTABSTOPS - wParam = celkový počet tabstopů; lParam = ukazatel na pole obsahující definice všech tabstopů - pro víceřádková okna nastaví zarážky pro tabulátor - tabstopy. Okno není překresleno. EM_SETWORDBREAKPROC - lParam = ukazatel na proceduru - nastaví proceduru danou lParam jako proceduru, která bude vyhodnocovat zalomení řádků v daném editačním poli. EM_UNDO - parametry žádné - provede vrácení poslední změny v editačním poli
V mnoha aplikacích je potřeba využívat spousty editačních polí, a to jak pro standardní jednořádkový vstup, tak i pro zápis složitějších informací. Pomocí výše uvedených zpráv můžete provádět akce přímo s editačním polem na nejnižší úrovni, kterou nám Windows poskytují. Pro standardní editační pole lze ještě použít tyto zprávy Windows: • • • •
WM_COPY - parametry žádné - zkopíruje aktuálně vybraný text do schránky WM_CUT - parametry žádné - vyjme aktuálně vybraný blok textu a umístí jej do schránky WM_PASTE - parametry žádné - vloží text ze schránky na aktuální pozici kurzoru WM_UNDO - parametry žádné - vrátí poslední provedenou operaci s editačním polem (jako EM_UNDO)
U každého standardního ovládacího prvku nám Windows posílají v kontextu zprávy WM_COMMAND notifikační zprávy o změnách provedených v daném ovládacím prvku. Tato notifikační zpráva je dána horním slovem parametru wParam (HIWORD(wParam)), handle okna ovládacího prvku je dáno parametrem lParam. U editačních polí jsou to tyto zprávy: • • • • • • • •
EN_CHANGE - informuje o tom, že obsah editačního pole byl změněn EN_ERRSPACE - informuje o tom, že editační pole nemá dostatek paměti na provedení požadované operace EN_HSCROLL - informuje, že uživatel provedl nějakou akci vzhledem k horizontálnímu posuvníku editačního pole. Zpráva je poslána předtím, než je obsah okna překreslen. EN_KILLFOCUS - informuje, že editační pole ztratilo fokus klávesnice EN_MAXTEXT - informuje, že se uživatel pokusil zadat více znaků než připouští limit editačního pole EN_SETFOCUS - informuje o tom, že editační pole získalo fokus klávesnice a je tedy do něj možno psát EN_UPDATE - informuje o tom, že obsah editačního pole byl změněn, ale pole ještě nebylo překresleno. EN_VSCROLL - informuje, že uživatel provedl nějakou akci vzhledem k vertikálnímu posuvníku editačního pole. Zpráva je poslána předtím, než je obsah okna překreslen.
Editační pole jsou jedním z důležitých prvků pro komunikaci s uživatelem. Pro jednoduché aplikace nejspíš celou tuto referenci nevyužijete, ale určitě se vám bude hodit jako pomoc při programování složitějších programů.
Základ programování pro Windows v C++ (15.) Standartní ovládací prvky - statické prvky Přestože můžeme uživatele o různých akcích uvnitř programu informovat např. okny se zprávami, je lepší o některých akcích informovat jen popiskem. K jednoduché komunikaci s uživatelem slouží třída STATIC z knihovny běžných ovládacích prvků. Nejenže jsou zde jednoduché prvky pro popisky do okna našeho programu, ale nalezneme zde i
několik stylů určených čistě pro vylepšení designu naší aplikace. Statický dialogový prvek vytvoříme tak, že funkci CreateWindowEx specifikujeme jako třídu STATIC a jako styl vybereme jeden z následujících. Pro orámování části okna lze použít tyto styly: • • • • • •
SS_BLACKFRAME - vykreslí rámeček barvou, která je nastavena pro standardní vykreslování černých částí SS_GRAYFRAME - vykreslí rámeček barvou, která je nastavena pro standardní vykreslování tmavých částí SS_WHITEFRAME - vykreslí rámeček barvou, která je nastavena pro standardní vykreslování světlých částí SS_ETCHEDFRAME - vykreslí rámeček, jehož horní a levý okraj je vykreslen barvou pro kreslení tmavých částí a prvý a dolní okraj barvou pro kreslení světlých částí ovládacích prvků. SS_ETCHEDHORZ - v podstatě totožné (Win9x) s SS_ETCHEDFRAME. Určeno pro kreslení horizontálních oddělovačů. SS_ETCHEDVERT - v podstatě totožné (Win9x) s SS_ETCHEDFRAME. Určeno pro kreslení vertikálních oddělovačů.
Dále můžeme použít styly SS_BLACKRECT, SS_GRAYRECT, SS_WHITERECT dle prvních tří výše uvedených stylů, pokud chceme, aby takto vytvořený rámeček byl i vyplněn. Vytváříme-li statický ovládací prvek, který by měl zobrazovat text, použijeme jeden z těchto stylů: • • • • •
SS_LEFTNOWORDWRAP - vytvoří jednořádkový textový popisek zarovnaný vlevo SS_LEFT - vytvoří víceřádkový textový popisek zarovnaný vlevo SS_CENTER - vytvoří víceřádkový textový popisek zarovnaný na střed SS_RIGHT - vytvoří víceřádkový textový popisek zarovnaný vpravo SS_SIMPLE - vytvoří jednoduchý jednořádkový popisek zarovnaný vlevo
Obsah těchto popisků můžeme specifikovat jak při tvorbě okna, tak v průběhu běhu aplikace, a to funkcí SetWindowText(handle na okno, řetězec), anebo pomocí zprávy WM_SETTEXT, jež byla popsána dříve. Statické ovládací prvky lze také využít pro zobrazení grafiky, a to pomocí těchto stylů: • • •
SS_BITMAP - specifikuje, že grafika bude bitmapového typu SS_ICON - specifikuje, že grafika bude ikona SS_ENHMETAFILE - specifikuje, že grafika je metasoubor
Tyto styly pak doplňují zprávy, kterými můžeme během běhu aplikace nastavit zobrazovaný obrázek: • •
STM_SETIMAGE - wParam = typ grafiky, může být IMAGE_BITMAP, IMAGE_CURSOR, IMAGE_ENHMETAFILE, IMAGE_ICON; lParam specifikuje handle na daný obrázek STM_GETIMAGE - wParam = adresa na DWORD, který obrží typ grafiky viz výše - funkce vrátí handle na obrázek
Statický ovládací prvek můžeme také kreslit sami, specifikujeme-li styl SS_OWNERDRAW. V tomto případě mateřské okno obdrží zprávu WM_DRAWITEM pokaždé, bude-li třeba tento ovládací prvek překreslit. Více o OWNERDRAW prvcích v některém z příštích dílů. Další styly, které můžeme použít současně s některým z předchozích stylů, jsou: • • •
SS_SUNKEN - vykreslí vmáčknutý okraj kolem statického prvku SS_NOTIFY - statický prvek s tímto stylem posílá mateřskému oknu notifikační zprávy uvedené dále SS_NOPREFIX - u textových polí zabrání interpretaci znaku & jako podtržení znaku.
Se styly pro grafiku můžeme použít navíc tyto: • • •
SS_CENTERIMAGE - umístí obrázek doprostřed statického prvku SS_REALSIZEIMAGE - určí, že při kreslení bitmap bude použita vždy originální velikost obrázku, tj. velikost obrázku nebude upravována SS_RIGHTJUST - umístí obrázek k pravému okraji ovládacího prvku
Pokud pro statické ovládací prvky použijeme styl SS_NOTIFY, můžeme očekávat, že nám tyto prvky budou v kontextu WM_COMMAND posílat tyto informační zprávy: •
STN_CLICKED - informuje, že uživatel kliknul na daný statický prvek
• • •
STN_DBLCLK - informuje, že uživatel na daný statický prvek poklepal STN_DISABLE - informuje, že byl daný statický prvek deaktivován STN_ENABLE - informuje, že byl daný statický prvek aktivován
Ze statických prvků určitě využijete většinu typů. I prosté zobrazení ikonky programu může vzhled okna vylepšit. Popisky pak můžou uživateli zpříjemnit orientaci mezi jednotlivými částmi okna. Pro oddělení částí oken pak ale můžeme použít především styly SS_ETCHEDHORZ a SS_ETCHEDVERT, které programátoři tak rádi používají. Otevřete si ve Windows například: panel pro ovládání hlasitosti a uvidíte jak se SS_ETCHED? používají. Pro rychlou orientaci v okně programu jsou statické styly opravdu tím nejlepším. Pokud ale chcete informovat uživatele o základních akcích v programu profesionálnějším způsobem, použijete určitě stavový řádek. Ten si popíšeme příště.
Základ programování pro Windows v C++ (16.) Standartní ovládací prvky - stavový řádek Pokud spustíte některé programy jako třeba Přehrávač CD, určitě si všimnete stavového řádku ve spodní části okna. Vytvořit takovýto stavový řádek není vůbec nic složitého. Opět využijeme knihovnu Common controls, která je standardní součástí Windows. Poněvadž jsou funkce nejen pro tento ovládací prvek uloženy v externí knihovně, konkrétně comctl32.dll, musíme před použitím tohoto a dalších běžných prvků zavolat funkci InitCommonControls, čímž zajistíme natažení této knihovny v případě, že se o to ještě nepostarala jiná aplikace. U většiny kompilátorů je třeba ještě uvést tuto knihovnu jako parametr kompilátoru / linkeru. U Dev-C++ 5 toto provedeme z menu Projekt / Vlastnosti projektu a do pole Linker/Další knihovny, které je ve spodní části okna, uvedeme "-lcomctl32", samozřejmě bez uvozovek. A dále je třeba k souboru s projektem připojit hlavičkový soubor commctrl.h. A nyní nám již nic nebrání přidat stavový řádek k našemu oknu. To provedeme pomocí funkce CreateStatusWindow, která je definována takto: HWND CreateStatusWindow( LONG style, // styl stavového řádku LPCTSTR lpszText, // text, který se ve stavovém řádku má objevit HWND hwndParent, // okno, ke kterému bude stavový řádek připojen UINT wID // identifikační číslo, které chceme // stavovému řádku přiřadit ); Tato funkce nám vrátí handle na okno stavového řádku. Parametr styl udává styl stavového řádku tak, jak bychom jej normálně použili ve funkci CreateWindow. Styl by měl vždy obsahovat WS_CHILD a WS_VISIBLE, případně jednu z dalších hodnot definovaných pro stavové řádky: • •
CCS_TOP - specifikuje, že se stavový řádek umístí v horní části mateřského okna. Není-li tento styl specifikován, je stavový řádek umístěn implicitně v dolní části okna. SBARS_SIZEGRIP - přidá ke stavovému řádku na pravém okraji trojúhelníček, kterým je možné měnit velikost okna. Je použit například u WordPadu.
Není doporučeno použít výše zmíněné styly současně. Přestože se trojúhelníček zobrazí, není možné jej pro změnu velikosti okna použít. Velikost stavového řádku se automaticky upravuje po obdržení zprávy WM_SIZE. V podstatě stačí, když ji okno vlastnící stavový řádek po obdržení této zprávy přepošle oknu stavového řádku. Pro úpravu vzhledu a nastavení textu stavového řádku je možné použít tyto zprávy: • • •
SB_SETMINHEIGHT - wParam = minimální výška - nastaví minimální výšku stavového řádku na danou hodnotu SB_GETBORDERS - lParam = ukazatel na pole, které má tři elementy - první element pole obdrží šířku horizontálního okraje, druhý obdrží šířku vertikálního okraje a třetí šířku okraje mezi bloky. SB_SETPARTS - wParam = počet bloků; lParam = ukazatel na pole obsahující šířky jednotlivých bloků - pomocí této zprávy můžete stavový řádek rozkouskovat na bloky. Maximální počet bloků může být 255, poslední hodnota pole by měla být -1, čímž je poslední blok protažen k pravému okraji okna.
• •
• •
SB_GETPARTS - jako SB_SETPARTS - funkce vrátí aktuální počet bloků SB_SETTEXT - wParam = číslo bloku | styl zobrazení; lParam = ukazatel na textový řetězec - nastaví daný text do bloku uvedeného wParam. Styl zobrazení může být: SBT_NOBORDERS - text bude vykreslen bez okraje, SBT_OWNERDRAW - text je vykreslen rodičovským oknem, SBT_POPOUT - text je vykreslen s opačným okrajem (vymáčklý) než implicitně (vtlačený). SB_GETTEXTLENGTH - wParam = číslo bloku - ve spodním slově vrátí délku řetězce v bloku uvedeném ve wParam, v horním slově pak styl. SB_GETTEXT - jako SB_SETTEXT - vrátí styl textu, jímž vyplní lParam
Mimo funkce CreateStatusWindow můžeme stavový řádek vytvořit i více "nízko", a to funkcí CreateWindow, pokud specifikujeme třídu STATUSCLASSNAME. Pokud ale potřebujete vykreslit jednoduchý stavový řádek a nechcete do svého programu integrovat tento typ prvku, můžete při kreslení odpovědi na zprávu WM_PAINT použít funkci DrawStatusText (více v MSDN), která vykreslí informaci v podstatě úplně stejně jako klasický stavový řádek. Stavový řádek je prvkem, kterým můžete snadno a kompaktně informovat uživatele o stavu programu a který si určitě najde své místo v nejedné vaší aplikaci.
Základ programování pro Windows v C++ (17.) Standardní ovládací prvky - progress bar - ukazatel průběhu akce Progress bar je prvek, jimž informujeme uživatele programu o stavu probíhající operace. V programech pro Windows jej můžeme nalézt téměř všude. Když kopírujeme soubory, formátujeme disk nebo instalujeme program či hru. Jak vypadá jeho obvyklá forma, můžeme vidět například v aplikaci ScanDisk nebo Defragmentace disku, avšak celkové schopnosti tohoto prvku jsou o něco širší. Předtím, než začneme prvek Progress bar používat, musíme, jako u minule popsaného stavového řádku, připojit knihovnu comctl32.dll. Jak toto provést, se můžete dočíst v minulém díle. Chceme-li do našeho okna nějaký ten progress bar přidat, použijeme klasickou funkci CreateWindowEx a jako třídu okna použijeme hodnotu PROGRESS_CLASS, předdefinovanou v hlavičkovém souboru commctrl.h, a to například takto: hwndPB = CreateWindowEx(0, PROGRESS_CLASS, (LPSTR) NULL, WS_CHILD | WS_VISIBLE, x, y, sirka, vyska, hwndParent, (HMENU) 0, hThisInstance, NULL); Pokud nepoužijeme žádný další styl, bude stav operace vykreslován klasickými modrými čtverečky, jejichž velikost je relativní k výšce progress baru. Pro progress bar můžeme dále použít tyto dva styly: • •
PBS_SMOOTH - způsobí, že stav progress baru nebude vykreslován pomocí obvyklých čtverečků, ale přesnějším způsobem. Přesněji, bude plnou barvou vybarvena ta část progress baru, reprezentující stav v procentech. PBS_VERTICAL - změní orientaci progress baru o 90°, tzn. že se hodnoty budou zobrazovat zespoda nahoru, místo zleva vpravo.
Práce s progress bary má dva aspekty. Každý progress bar si uchovává hodnotu minimální a hodnotu maximální a dále pak tzv. krokovou hodnotu, která určuje, o kolik jednotek se zvýší jeho stav při obdržení zprávy PBM_STEPIT. Po vytvoření nového je minimální hodnota nastavena na 0 a maximální na 100, krok pak na 10. Pro nastavení nejen těchto hodnot a pro práci s progress bary použijte tyto zprávy: • • • • •
PBM_STEPIT - parametry žádné - provede nastavení vnitřního stavu ukazatele do hodnoty, která se rovná hodnotě předchozí + délce kroku. Progress bar je překreslen a zobrazuje nový stav. PBM_SETSTEP - wParam=délka kroku - nastaví délku kroku podle parametru. PBM_SETRANGE - lParam=MAKELPARAM(min, max) - nastaví minimální a maximální hodnotu. PBM_SETPOS - wParam=pozice - nastaví nový stav (pozici) bez ohledu na nastavenou délku kroku. PBM_DELTAPOS - wParam=posun - změní stav progress baru o hodnotu danou parametrem.
Pro nastavení grafického vzhledu je možné použít tyto zprávy: •
PBM_SETBARCOLOR - nastaví barvu popředí progress baru dle parametru
•
PBM_SETBKCOLOR - nastaví barvu pozadí progress baru dle parametru
Toto jsou schopnosti standardního prvku Progress bar. Snad ještě uvedu, že styl PBS_SMOOTH a PBS_VERTICAL je pod Windows 95 možno použít jen v případě, že máte nainstalován Internet Explorer, minimálně verzi 4.0.
Základ programování pro Windows v C++ (18.) - Menu Jak pracovat s dalším, široce využitelným, prvkem Win32 aplikací Menu neboli strukturované nabídky jsou velmi vhodným prvkem pro sdružování příkazů do souvisejících skupin. Používání těchto nabídek je pro uživatele velmi jednoduché a intuitivní, měně jednoduché je to již pro programátora. Pryč jsou ale doby, kdy si programátoři různá menu kreslili sami. Dnes to za ně udělá vše Windows API. Vytvořit menu pro váš program můžete dvěma způsoby. První z nich je použití zdrojů. Spočívá v tom, že programátor k vytvoření hierarchie příkazů použije některý z editorů zdrojů (např. freewarový Weditres nebo Microsoft AppStudio). Takto vytvořené menu je možné načíst funkcí LoadMenu, která má pouze dva parametry a to handle na instanci aplikace a název zdroje menu. Druhý způsob je složitější, ale flexibilnější, a tím je použití funkcí CreateMenu, AppendMenu, CreatePopupMenu, DestroyMenu, InsertMenu, SetMenu a InsertMenuItem. Tento způsob má také výhodu v tom, že váš program nebude možné upravovat různými editory zdrojů jako je např. Resource Hacker. Načtené nebo vytvořené menu je pak možné přichytit k oknu funkcí SetMenu nebo jej můžeme nechat vysunout na libovolné pozici funkcí TrackPopupMenu příp. TrackPopupMenuEx. Pokud specifikujeme menu při tvorbě okna funkcí CreateWindow nebo CreateWindowEx, nemusíme jej před ukončením aplikace odstraňovat funkcí DestroyWindow, poněvadž tuto akci za nás udělají Windows. Nejjednodušším postupem k vytvoření menu je udělat jej přes zdroje. Jednoduché menu může vypadat takto: 1000 MENU BEGIN POPUP "&Soubor" BEGIN MENUITEM "&Nový", 1010 MENUITEM SEPARATOR POPUP "Poslední" BEGIN MENUITEM "Jméno souboru", 1040 END MENUITEM "&Otevřít", 1020 MENUITEM "Konec", 1030, INACTIVE END POPUP "&Nápověda" BEGIN MENUITEM "&O programu", 1090 END END Jednoduchým zápisem lze jednoduše docílit pěkného menu. Mnohé editory nabízí i možnost náhledu/ukázky. Syntaxe je jednoduchá. Položku uvádí klíčové slovo MENUITEM nebo POPUP, jehož položky musí být uzavřeny mezi slovy BEGIN a END, povoleny jsou i znaky { a }. Následuje v uvozovkách název položky, ve kterém uvedením znaku & (ampersand) před libovolným písmenem docílíme aktivity dané položky na stisk kombinace klávesy Alt a daného písmena a toto písmeno je vykresleno podtržené. Potřebujeme-li použít v nabídce znak &, stačí jej zdvojit. Za názvem položky následuje identifikační číslo, které je posláno proceduře zpráv mateřského okna v rámci zprávy WM_COMMAND v případě, že je daná nabídka vybrána. Za tímto číslem může uvést stav položky uvedením jednoho z klíčových slov: • • • • •
INACTIVE - položka nabídky nereaguje na kliknutí GRAYED - položka je nereaguje a je vykreslena v neaktivním stavu CHECKED - po levé straně nabídky je u dané položky zobrazeno zaškrtnutí. MENUBREAK - od dané položky včetně, jsou další položky vypisovány v dalším sloupci MENUBARBREAK - jako MENUBREAK, sloupce jsou odděleny svislou čarou
Pokud místo názvu položky uvedete klíčové slovo SEPARATOR, získáte tím horizontální oddělovač. Použití menu
Máme-li vytvořeno menu pro naši aplikaci, můžeme jej nyní zprovoznit. Abychom mohli menu použít, budeme potřebovat proměnnou typu HMENU, do níž funkcí LoadMenu načteme dané menu. Pokud jste použili modifikaci příkladu výše, budete muset použít tento tvar: HMENU hMenu1 = LoadMenu( hThisInstance, MAKEINTRESOURCE(1000) ); V případě, že místo číslice 1000 máte použit nějaký řetězec, dejme tomu HlavniMenu, stačí použít tento tvar: HMENU hMenu1 = LoadMenu( hThisInstance, "HlavniMenu" ); Běžně se menu připojuje k oknu předáním parametrem funkce CreateWindow nebo CreateWindowEx, které nám automaticky zabezpečí také uvolnění použité paměti. K přiřazení menu oknu můžeme také použít funkci SetMenu, která má dva parametry (handle na okno a handle na menu) například takto: SetMenu( hHlavniOkno, hMenu1 ); Nyní potřebujeme na příkazy z menu reagovat. Přesuneme se tedy do té části kódu, kde máme proceduru okna. Jak jsem již výše uvedl, menu posílá svému mateřskému oknu informace v rámci zprávy WM_COMMAND. To znamená, že po obdržení zprávy WM_COMMAND jednoduše porovnáme ID prvku, který zprávu poslal (LOWORD(wParam)), se známými identifikátory položek menu a dle toho reagovat. Reakce na výše uvedené menu by tedy mohla probíhat nějak takto: switch(Message) { . . . case WM_COMMAND: . . . switch(LOWORD(wParam)) { case 1010: // Zvolena položka "Nový soubor" break; case 1020: // Položka "Otevřít" break; // a tak dále... } . . . break; . . . } Přestože jsem něco málo o nabídkách zmínil v díle 7 a stručnou ukázku použití jste mohli nalézt v části 8, nebylo to zdaleka vše, co je možné s menu provádět. Jak tvořit a upravovat menu dynamicky v průběhu běhu aplikace a jak vysouvat libovolné menu na libovolném místě aplikace, se dozvíte příště.
Základ programování pro Windows v C++ (19.) - menu podruhé Tvorba menu v kódu, editace menu a vysouvání na libovolné pozici Jak jste se mohli dočíst minule, není tvorba menu pomocí zdrojů nijak obtížná. Zdaleka to však není vše, co je možné s nabídkami provádět. Tvorba a správa menu za běhu programu Sice můžeme menu načítaná ze zdrojů upravovat a měnit dle libosti postupy popsanými dále, ale mají několik nevýhod. První z nich je ta, že prvotní vzhled, názvy položek a jejich ID jsou pevně dané a při případné změně menu s tím programátor musí počítat. Druhou nevýhodou je to, že struktura, názvy a ID menu jsou dány tzv. resource (zdrojovou) částí spustitelného PE souboru a existuje spousta programů, jimiž je možno tuto část měnit. Zmiňme např. velmi známý Resource Hacker. Prázdné menu můžeme vytvořit funkcí CreateMenu. Tato funkce nemá žádný parametr a vrací handle na právě vytvořené nové menu (HMENU). K přidání nové položky do menu použijeme funkci InsertMenu. Tato funkce má pět parametrů: BOOL InsertMenu( HMENU hMenu, UINT uPosition,
// handle na menu // identifikuje položku, za niž se nová položka umístí - závislé na uFlags
UINT uFlags, UINT uIDNewItem,
LPCTSTR lpNewItem
// vlajky položky, vid dále // specifikuje identifikátor položky nebo handle na další podnabídku, závislé na uFlags // řetězec, handle na bitmapu nebo data pro zprávu WM_DRAWITEM, závisí na uFLags
); Protože novou položkou může být jak text či obrázek, tak další podnabídka, specifikuje parametr uFlags význam ostatních parametrů. Parametr uFlags může být kombinací následujících předdefinovaných hodnot, ale vždy se může vyskytnout jen jedna hodnota z každého bloku. • • • • • • • • • • • • • •
MF_BYCOMMAND - implicitní - nová položka se zařadí za položku s identifikátorem daným parametrem uPosition MF_BYPOSITION - uPosition indikuje pozici dané položky MF_BITMAP - lpNewItem specifikuje handle na bitmapu, která bude použita pro vykreslení položky menu MF_OWNERDRAW - přes lpNewItem je předáno 32-bitové číslo, kterým je daná položka identifikována při předání zprávy WM_DRAWITEM MF_STRING - implicitní - lpNewItem obsahuje ukazatel na ASCIIZ řetězec, který bude použit u nabídky MF_CHECKED - u nově vytvořené položky je značka zaškrtnutí MF_DISABLED - nově vytvořená položka je neaktivní, tzn. nepřijímá akce od uživatele MF_ENABLED - nová položka je aktivní MF_GRAYED - nová položka je vykreslena v neaktivním stavu a nereaguje na akce uživatele MF_MENUBARBREAK - nová položka je umístěna do nového sloupce, od předchozího sloupce je oddělena svislou čarou MF_MENUBREAK - nová položka je umístěna do nového sloupce bez oddělovače MF_POPUP - uIDNewItem specifikuje handle na další submenu MF_SEPARATOR - nová položka je horizontální oddělovač a není s ním možné provádět další akce MF_UNCHECKED - u nové položky není vykresleno zaškrtnutí - implicitní
Na takto vytvořenou položku může náš program reagovat stejně jako bylo popsáno v minulém díle. Pomocí uIDNewItem jsme specifikovali unikátní ID a ten je nám ve spodním slově parametru wParam zprávy WM_COMMAND vrácen v případě, že uživatel tuto položku zvolil. Potřebujeme-li nějakou položku upravit, můžeme použít funkci ModifyMenu, která se používá v podstatě úplně stejně jako InsertMenu a dokonce i její parametry jsou stejné. Chceme-li položku menu upravit na vyšší úrovni, můžeme použít funkce SetMenuItemInfo, GetMenuItemInfo a InsertMenuItem. V tomto případě vás však musím odkázat na MSDN, poněvadž popis těchto funkcí je mimo rozsah tohoto článku a jsou použitelné jen na Windows 95 a vyšších. Odstranění položky menu se provede použitím funkce DeleteMenu: BOOL DeleteMenu( HMENU hMenu, // handle na menu UINT uPosition, // identifikátor menu nebo pozice UINT uFlags // vlajka (MF_BYCOMMAND nebo MS_BYPOSITION) ); Význam parametrů je opět stejný jako u předešlých funkcí. Je-li naše menu připojeno k nějakému oknu, je nutné jej překreslit, aby uživatel viděl změny, které byly v programu provedeny. Toto překreslení se provede použitím funkce DrawMenuBar, která má jen jeden parametr, a tím je handle na okno. Poznámka: Vytváříme-li submenu - podnabídku, je třeba místo funkce CreateMenu, použít funkci CreatePopupMenu. Menu na libovolné pozici Pravděpodobně to znáte. Uživatel v nějaké aplikaci klikne pravým tlačítkem myši a vysune se mu nabídka s akcemi. Chcete-li podobou nabídku přidat do vaší aplikace, není to problém. Při tvorbě takovéhoto menu je nutné použít funkci CreatePopupMenu a výše popsanými funkcemi jej vyplnit. Zobrazit toto menu je možné pomocí funkce TrackPopupMenu nebo její novější verze TrackPopupMenuEx. TrackPopupMenu je definována takto: BOOL TrackPopupMenu(
HMENU hMenu, // UINT uFlags, // int x, // int y, // int nReserved, // HWND hWnd, // CONST RECT *prcRect //
handle na menu vlajky vlastností menu x souřadnice pozice na obrazovce y souřadnice pozice na obrazovce rezervováno, mělo by být nula handle na mateřské okno // ukazatel na oblast obrazovky, mimo níž je menu zneaktivněno
); Protože většina parametrů je samovysvětlujících, uveďme jen to, že pokud parametr prcRect necháme NULL, získáme standardní chování menu, tj. menu je uzavřeno po kliknutí mimo. Parametr uFlags opět definuje několik vlastností této nabídky a může být jednou z těchto hodnot nebo jejich kombinací. • • •
TPM_CENTERALIGN - pozice menu je horizontálně vycentrována kolem bodu specifikovaného parametry x a y TPM_LEFTALIGN - menu je vysunuto doprava od bodu x,y TPM_RIGHTALIGN - menu je vysunuto doleva od bodu x,y
• •
TPM_LEFTBUTTON - chování menu je dáno předpokladem, že bylo vyvoláno levým tlačítkem myši TPM_RIGHTBUTTON - chování menu je dáno předpokladem, že bylo vyvoláno pravým tlačítkem myši
Menu naleznete snad v každém větším programu. Využití zde snad ani nemusím vypisovat, snad jen, že tímto způsobem můžete integrovat několik desítek příkazů pro váš program.
Základy programování pro Windows v C++ (20.) - Práce se soubory (1.) Jak využít funkce Win32API k rychlé práci se soubory. Pro práci se soubory můžeme samozřejmě využít standardizovaných funkcí knihoven C/C++, avšak v případě programování pro Windows jsou jak funkce implementující soubory jako proudy dat (fopen, fgetc,...) knihovny stdio.h, tak funkce pro práci se soubory na základě handle (open, write, ...) knihovny io.h stejně pouze bloky kódu odvolávájícími se na Win32 API. Takto je sice dosaženo vysoké přenositelnosti, ale v závislosti s tím použitím těchto funkcí získáme určité zpoždění a celkové zpomalení práce. Přenositelnost zdrojových kódů mezi různými systémy je sice věc pěkná, ale v komerční praxi pro uživatelnost a intuitivitu výsledných programů naprosto nepoužitelná. Win32API funkce podporují souborové systémy FAT, HPFS a NTFS bez nutnosti upravovat kód pro určitý typ. Pokud jsou zde rozdíly, budou popsány níže u každé funkce. Názvy souboru pro všechny výše uvedené systémy se řídí následujícími pravidly: • • • • • • • •
Je možné použít všechny znaky mimo separátorů komponent a znaků v rozsahu 0 až 31 včetně. Dále není možné použít níže uvedené znaky pro daný systém. Jako separátor komponent je možné použít znaky zpětné lomítko (\) a běžné lomítko (/). Žádný další znak není přípustný. Použití tečky (.) jako adresářové komponenty reprezentuje aktuální adresář. Použití dvou teček (..) jako adresářové komponenty reprezentuje rodičovský adresář adresáře aktuálního. Znaky <, >, :, ", /, \ a | jsou vyhrazeny pro operační systém a nemohou být použity jako název komponenty. Rezerovaná slova (aux, con, prn) nemohou být použita jako název adresářové nebo souborové komponenty. Řetězec obsahující cestu je formátu ASCIIZ (ukončen znakem p ascii hodnotě 0) a maximální délka je dána definicí MAX_PATH. Maximální délku je možné překročit v případě použití Unicode a UNC formátu) Názvy komponent mohou obsahovat velká i malá písmena, ale adresář nemůže obsahovat adresáře či soubory se stejnými nebo velikostí písmen odlišnými názvy.
Windows API, stejně jako pro téměř vše, používá pro práci se soubory handly. Jeden handle si pro zjednodušení můžeme představit jako číslo, pod kterým si Windows uvnitř nahledají potřebné informace. V případě selhání libovolné funkce je možné volat GetLastError a získat tak přesný popis nastalé chyby. První funkcí, kterou je nutno použít, je funkce CreateFile. Přestože Windows už od svých prvních verzí obsahují i funkci OpenFile není vhodné ji použít protože má mnoho omezení. Funkce CreateFile má následující prototyp. HANDLE CreateFile( LPCTSTR lpFileName, // nnázev souboru DWORD dwDesiredAccess, // požadovaný p řístup k souboru
DWORD dwShareMode, // režim sdílení // bezpečnostní deskriptor souboru LPSECURITY_ATTRIBUTES lpSecurityAttributes, DWORD dwCreationDistribution, // vlajky pro tvorbu souboru DWORD dwFlagsAndAttributes, // atributy souboru // handle na soubor, jehož atributy použít HANDLE hTemplateFile ); Parametrem dwDesiredAccess specifikujeme požadovaný přístup k souboru. Předat funkci můžeme předdefinovanou hodnotu GENERIC_READ, pokud budeme chtít ze souboru číst, nebo GENERIC_WRITE, pokud budeme chtít do souboru zapisovat. Pro současné čtení i zápis použijeme obě hodnoty, neuvedeme-li ani jednu z těchto hodnot, nezískáme žádný přístup. Parametr dwShareMode můžeme specifikovat, pokud chceme, aby k souboru měly přístup i ostatní aplikace. Aby jej bylo možné číst, specifikujeme FILE_SHARE_READ, pro zápis FILE_SHARE_WRITE. Neuvedeme-li žádnou z těchto hodnot, zamezíme jakémukoliv přístupu k souboru do doby volání CloseHandle. Pokud operační systém podporuje zabezpečení souborů, můžeme parametrem lpSecurityAttributes předat ukazatel na vlastní strukturu SECURITY_ATTRIBUTES. Uvedením NULL získá soubor implicitní vlastnosti. Největší pozornost bychom měli věnovat parametru dwCreationDistribution. Tímto definujeme základní chování funkce. Tento parametr musí být jedna z následujících hodnot: • • • • •
CREATE_NEW - Vytvoří nový soubor. V případě, že soubor již existuje, funkce vrátí INVALID_HANDLE_VALUE. CREATE_ALWAYS - Vytvoří nový soubor. V případě, že soubor již existuje, přepíše jej. OPEN_EXISTING - Otevře soubor. V případě že neexistuje, vrátí INVALID_HANDLE_VALUE. OPEN_ALWAYS - Otevře soubor. Pokud neexistuje, chová se jako kdyby bylo specifikováno CREATE_NEW. TRUNCATE_EXISTING - Otevře soubor a změní jeho délku na 0. V případě, že soubor neexistuje, vrátí INVALID_HANDLE_VALUE.
Dalším parametrem je dwFlagsAndAttributes a definuje atributy souboru. Zde definujeme, jaké atributy získá nově vytvořený soubor nebo otevřený soubor po uzavření. Definovat můžeme libovolnou kombinaci následujících hodnot: • • • • • •
FILE_ATTRIBUTE_ARCHIVE - soubor je archivní FILE_ATTRIBUTE_COMPRESSED - data uvnitř souboru jsou komprimovaná FILE_ATTRIBUTE_NORMAL - žádné atributy nejsou nastaveny (hodnota je platná pouze pokud je použita sama) FILE_ATTRIBUTE_HIDDEN - soubor je skrytý FILE_ATTRIBUTE_READONLY - soubor je pouze pro čtení FILE_ATTRIBUTE_SYSTEM - soubor je systémový
Zde je možné uvést i další parametry, pomocí nichž specifikujeme chování systému při práci se souborem: • • • • • • • •
FILE_FLAG_WRITE_THROUGH - Je doporučením operačnímu systému, aby používal cache pro zápis jen minimálně a data zapisoval na disk co nejdříve. FILE_FLAG_OVERLAPPED - povolí použití handle na soubor současně i vícekrát. Funkce pro zápis a čtení souboru musí používat strukturu OVERLAPPED. FILE_FLAG_NO_BUFFERING - cache pro čtení i zápis soubor není použita. Z těchto důvodů musí být čteno nebo zapisováno vždy násobek velikosti sektor dat. FILE_FLAG_RANDOM_ACCESS - je doporučení systému, aby použil jiný systém cachování souboru a to pro náhodný přístup. FILE_FLAG_SEQUENTIAL_SCAN - je doporučení systému, aby použil jiný systém cachování souboru a to pro čtení nebo zápis od začátku ke konci. FILE_FLAG_DELETE_ON_CLOSE - soubor bude smazán hned v okamžiku použití CloseHandle. FILE_FLAG_BACKUP_SEMANTICS - Pouze Windows NT - indikuje systému, že se souborem jsou prováděny zálohovací operace. Systém se musí ujistit, že volající proces má povolení SE_BACKUP_NAME nebo SE_RESTORE_NAME. FILE_FLAG_POSIX_SEMANTICS - specifikuje, že soubor je používán v rámci pravidel POSIX. Takto vytvořený soubor nemusí být přístupný ze systémů MS-DOS, Windows 3,11 nebo Windows NT
Předáním souboru parametrem hTemplateFile jsou nově vytvářenému soboru předány jeho rozšířené atributy. Funkce CreateFile se pro systémy Windows zdaleka neomezuje jen na práci s diskovými soubory. Specifikováním názvu souboru CONIN$ nebo CONOUT$ můžeme místo souboru použít vstup či výstup z konzole. Pod systémy NT, 2k a XP je možné použitím názvu soubor \\.\PHYSICALDRIVE0 získat přístup k první fyzické diskové jednotce v počítači a
analogicky i k dalším. Pro uzavření souborů otevřených pomocí CreateFile a uvolnění paměti pro daný handle na soubor je nutné použít funkci CloseHandle, která má jediný parametr a to handle vrácené funkcí CreateFile. Jak ze souboru načíst pomocí Win32API nebo do něj zapsat si povíme v příštím díle.
Základy programování pro Windows v C++ (21.) - Práce se soubory (2.) Jak využít funkce Win32API k rychlé práci se soubory V minulé části jsem uvedl popis funkce CreateFile, pomocí které soubory nejen vytváříme ale i otevíráme a měníme jejich atributy. Tato funkce má spoustu parametrů a vlajek, které prakticky získávají smysl, až když ze souboru čteme nebo do něj zapisujeme. Pro čtení ze souboru použijeme funkci Win32API, která se jmenuje ReadFile a její prototyp je následující: BOOL ReadFile( HANDLE hFile, // handle na čtený soubor LPVOID lpBuffer, // adresa bufferu, který obdrží data DWORD nNumberOfBytesToRead, // počet bytů ke čtení // adresa proměnné pro počet přečtených bytů LPDWORD lpNumberOfBytesRead, LPOVERLAPPED lpOverlapped // adresa struktury OVERLAPPED ); Prvním atributem hFile specifikujeme handle vrácené funkcí CreateFile. Tento parametr není třeba blíže specifikovat, jen pro úplnost, tento parametr nemusí přímo označovat diskový soubor, ale např. komunikační resource či mailslot. Handle předávaný této funkci musí mít přístupové právo GENERIC_READ. Druhý parametr musí odkazovat na incializovaný buffer, jehož velikost je minimálně počet bytů uvedených třetím parametrem nNumberOfBytesToRead. Parametr nNumberOfBytesToRead tedy specifikuje, kolik bytů se z daného souboru přečte a o kolik bytů se posune vnitřní ukazatel. Další čtecí operace bude tedy začínat prvním nepřečteným bytem. Parametr lpNumberOfBytesRead by měl odkazovat na proměnnou, která obdrží počet skutečně přečtených dat. Tuto hodnotu je dobré testovat, pokud pracujeme se souborem neznámé délky nebo s jiným handle než na soubor. Tento parametr může být NULL pokud lpOverlapped ukazuje na platnou strukturu OVERLAPPED. Je-li soubor otevřen pro asynchronní čtení a zápis, tj. je-li při otevření funkcí CreateFile použita vlajka FILE_FLAG_OVERLAPPED, je nutné tento parametr použít. Je-li přesto ponechán NULL, může se funkce chovat nepředvídatelným způsobem. Není-li FILE_FLAG_OVERLAPPED specifikováno, je tento parametr ignorován a měl by být NULL. Jednoduchým příkladem použití této funkce je následující kód: BOOL bResult = ReadFile(hFile, &inBuffer, nBytesToRead, &nBytesRead, NULL) ; // test na konec souboru if (bResult && nBytesRead == 0, ) { // jsme na konci souboru } Podobným způsobem a stejně jednoduše je možné do souboru i zapisovat. Pro zápis je určena funkce s analogickým názvem WriteFile a její prototyp je následující: BOOL WriteFile( HANDLE hFile, // handle na soubor LPCVOID lpBuffer, // ukazatel na blok dat DWORD nNumberOfBytesToWrite, // počet bytů k zápisu // adresa proměnné pro počet zapsaných bytů LPDWORD lpNumberOfBytesWritten,
// ukazatel na strukturu OVERLAPPED LPOVERLAPPED lpOverlapped ); Parametry této funkce jsou hodně podobné funkci ReadFile. Parametr hFile je handle na soubor, který byl otevřen nebo vytvořen s přístupovým právem GENERIC_WRITE. Jako u funkce ReadFile nemusí určovat soubor. Parametr lpBuffer odkazuje na začátek bloku dat v paměti, která mají být do souboru zapsána a jejich délka v bytech je dána parametrem nNumberOfBytesToWrite. Jako u funkce ReadFile obdržíme v proměnné specifikované lpNumberOfBytesWritten počet skutečně zapsaných bytů. Uvedení ukazatele na strukturu OVERLAPPED v parametru lpOverlapped je opět podmíněno stejnými podmínkami jako u funkce ReadFile. Obě tyto funkce se chovají podobně jako běžné céčkovské fread nebo fwrite. Ačkoli by se mohlo zdát, že tyto funkce pro práci se soubory úplně postačují, Win32API nabízí ještě několik dalších funkcí pro práci se soubory, které psaní programů mohou urychlit a usnadnit, a proto budu tomuto tématu věnovat ještě jeden článek.
Základy programování pro Windows v C++ (22.) - Práce se soubory (3.) Jak využít funkce Win32API k rychlé práci se soubory V předchozí části tohoto občasného minitutoriálku jsem vám slíbil popis dalších několika funkcí, které jsou použitelné pro práci se soubory. Tady jsou. SetEndOfFile( HANDLE hFile ); Potřebujete-li prostě změnit velikost souboru, použijte tuto funkci. Pouhým zavoláním této funkce definujete, že aktuální pozice vnitřního ukazatele je aktuální délka souboru. Dané handle musí mít přístup GENERIC_WRITE. DWORD SetFilePointer( HANDLE hFile, // handle na soubor LONG lDistanceToMove, // počet bytů pro posun // adresa horního dwordu počtu bytů PLONG lpDistanceToMoveHigh, DWORD dwMoveMethod // metoda posunu ); Tato funkce ve Win32API je složitou verzí jednoduché funkce seek. Parametry snad není třeba vysvětlovat, snad jen, že parametr lpDistanceToMoveHigh by měl obsahovat horní dword 64-bitové proměnné pro práci se soubory většími než 4GB. Tato proměnná také po návratu z funkce obsahuje horní dword aktuální pozice v souboru. Spodní dword je vrácen funkcí. Metodou specifikujeme odkud bude ukazatel posouván a může být FILE_BEGIN pro specifikování začátku souboru, FILE_CURRENT pro aktuální pozici a FILE_END pro konec souboru. BOOL SetFileAttributes( LPCTSTR lpFileName, // nnázev souboru DWORD dwFileAttributes // atributy k nastavení ); Pomocí této funkce je možné explicitně nastavit atributy daného existujícího souboru. Atributy je možné nalézt v referenci k funkci nebo v článku číslo 20 tohoto seriálu. SetHandleCount( UINT number ); Voláním této funkce specifikujeme maximální počet souborů, které může mít aktuální proces otevřené v jednom okamžiku. Maximální číslo je 255 a původní hodnota je touto funkcí vrácena. Pod Win9x je implicitní hodnota 20, pod Windows NT nemá volání této funkce žádný efekt a počet souborů je omezen pouze dostupnou nestránkovanou pamětí. BOOL CopyFile( LPCTSTR lpExistingFileName, // název existujícího souboru LPCTSTR lpNewFileName, // název nového souboru BOOL bFailIfExists // vlajka viz níže ); Tato funkce provádí rychlé a jednoduché rozhraní k rychlým kopírovacím mechanizmům Windows. První řetězec označuje název souboru, který má být zkopírován a druhý řetězec je soubor, který bude vytvořen jako kopie tohoto souboru. Vlajku bFailIfExists je nutné nastavit na FALSE, pokud chceme, aby případně existující cílový soubor byl přepsán, na TRUE pak pokud chceme, aby funkce v takovémto případě skončila bez kopírování. BOOL MoveFile( LPCTSTR lpExistingFileName, // název existujícího souboru LPCTSTR lpNewFileName, // název cílového souboru
); Tato funkce je podobná funkci CopyFile až na to, že zdrojový soubor je smazán. Tato funkce může být mnohokrát rychlejší než funkce výše a to především v případě, že oba řetězce specifikují název souboru na stejném disku. V takovémto případě je pouze upraven záznam v alokační tabulce souborů a data nejsou fyzicky přenášena. DeleteFile( LPCTSTR lpFileName ); Zavoláním této funkce jednoduše odstraníme požadovaný soubor z disku. CreateDirectory( LPCTSTR lpPathName, // název adresáře LPSECURITY_ATTRIBUTES lpSecurityAttributes ); Zavoláním této funkce vytvoříme adresář s názvem daným parametrem lpPathName. Druhým parametrem je možné specifikovat bezpečnostní atributy daného adresáře, není to však nutné a postačí uvést NULL. Tato funkce nepoužívá žádný rekurzivní algoritmus a tudíž je nutné aby všechny nadřazené adresáře k tomuto existovaly. RemoveDirectory( LPCTSTR lpszDir ); Odstraní uvedený adresář. Tento musí být prázdný a volající proces musí mít práva k odstraňování adresářů. GetLastError(); Přestože se tato funkce nevztahuje přímo k souborům jejím zavoláním po selhání libovolné Win32API funkce, a tudíž i funkce pro práci se soubory, získáme číslo aktuální chyby a dle toho můžeme upravit běh programu. Jak při vývoji Win32 programů poznáte, je tato funkce velmi velmi užitečná. Touto funkcí tedy uzavírám tuto část tutoriálu o programování pro Windows a v následujících částech se budu věnovat opět něčemu jinému.