Radek Chalupao programování a počítačích, vývoj software, školení ...
Domů | Články | Ukázky kódu | Tipy a triky | Aktuality | Školení a konzultace | Vývoj software | Freeware | Kontakt

Prostředky (resources) - úvod.  8.1.2002

V minulých dílech jsme se naučili různým způsobem vypisovat text, včetně zatím primitivního výpisu do neklientské oblasti. Mohli bychom nyní pokračovat na téma GDI tím, jak kreslit geometrické obrazce jako čáry, plošné útvary apod. Ale bude asi lepší, abychom si při výuce a testování takového kreslení uměli volat jednotlivé funkce přes nabídku okna. Na čas tedy opustíme GDI a budeme se věnovat dalšímu základnímu tématu Win API, a to jsou prostředky (resources). Mezi prostředky (překládané také jako "zdroje") totiž patří i zmíněné hlavní menu okna. Začneme ale tím nejjednodušším, a to jsou vlastní ikony a popř. i kurzory. Prostředky obecně jsou uloženy přímo ve spustitelném souboru ve formátu, který odpovídá typu zdroje. Tedy například data ikony jsou uložena ve spustitelném souboru zcela stejně jako v souboru .ico. Tím spustitelným souborem se zdroji bývá většinou přímo exe soubor aplikace, ale může být například i externí dll knihovna, jako je třeba knihovna shell32.dll, obsahující mnoho zdrojů, především ikon, známých uživatelům Windows. Abychom mohli prostředky používat, musíme je nějakým způsobem dostat (přilinkovat) do programu. Ve Visual C++ je to velice jednoduché. Nejprve přidáme do projektu tzv. "resource script", což je textový soubor (s koncovkou .rc) obsahující informace o zdrojích, které pak kompilátor a linker použijí při sestavení exe souboru. Tento soubor má tvar určený syntaxí jakéhosi "skriptovacího jazyka", který obsahuje odkazy na externí soubory (ikony, kurzory, bitmapy apod.) nebo úplný popis daného prostředku, jako je tomu v případě nabídek (menu) či dialogových oken. Jak jsem zde již uvedl, Visual C++ je vizuální v tom smyslu, že nám umožňuje tento skriptový soubor prostředků vygenerovat pomocí vizuálního editoru. Vzhledem k tomu, že toto čtou i programátoři používající C++ Builder, bude lépe s ohledem na ně si říci ještě trochu více v obecné rovině. Totiž kromě textových skriptů (.rc) existuje ještě formát tzv. zkompilované prostředky. Jsou to soubory s příponou res, které produkuje například ImageEditor, který je součástí C++ Builderu (a Delphi). C++ Builder bohužel neumí přímo editovat (a ukládat) soubory rc. Navíc pokud v C++ Builderu máte aplikaci s VCL, můžete hotový skript (.rc) snadno přidat do projektu, kde bude automaticky kompilován a linkován. Avšak v případě aplikace bez VCL nelze použít USERC, kterým jinak vkládáme rc skripty do projektu. Je možné použít kompilátor zdrojů brcc32.exe (volaný z příkazové řádky), který ze vstupního skriptu rc vytvoří kompilovaný soubor res. Ten pak lze přidat do zdrojového kódu pomocí
#pragma resource "jmeno_souboru.res"
Navíc tento res soubor umí Visual C++ (a jistě více editorů) přímo editovat. Image editor v C++ Builderu totiž umí pracovat pouze se zdroji typu bitmapa, ikona a kurzor, a to ještě v těch nejprimitivnějších formátech (třeba barevné kurzory vůbec nezná). Samozřejmě vždy lze upravovat skripty zdrojů (rc) textově. Například u ikon je to velice jednoduché neboť obsahují pouze identifikátor ikony a jméno souboru (ico) obsahujícího vlastní ikonu. Příklad řádku rc souboru definující ikonu:
IDR_MAINICON  ICON  DISCARDABLE "jmeno_souboru.ico"
zde identifikátor IDR_MAINICON musí být samozřejmě definován nejlépe ve vloženém hlavičkovém souboru třeba takhle:
#define IDR_MAINICON 101
Dále lze zdroje identifikovat textovými názvy. Pak na místě IDR_MAINICON bude příslušný text v uvozovkách. Vrátíme se nyní k našemu "většinovému" Visual C++. Do projektu si přidáme nový skript zdrojů (například) pomocí příkazu z nabídky "Project -> Add To Project -> New", kde na záložce "Files" vybereme "Resource Script". Nazveme si jej třeba main.rc a musíme nechat zaškrtnutou volbu (check-box) "Add To Project". Po potvrzení nám vedle záložek "ClassView" a "FileView" přibude záložka "ResourceView". Zde je jedna prázdná složka, do které si přidáme vlastní ikonu. Pokud chceme použít nějakou hotovou ikonu, kterou máme na disku, klikneme pravým tlačítkem na tuto složku a zvolíme "Import". Otevře se nám dialog, umožňující vybrat a vložit soubor s ikonou. Tuto ikonu pak můžeme dále editovat již přímo v editoru Visual C++. Přidejme si tedy do projektu ikonu a stejným způsobem třeba nějaký vlastní kurzor. Importovanou ikonu si přejmenujme z defaultního IDI_ICON1 na IDR_MAINICON. Toto přejmenování má svůj význam. Pokud totiž budeme postupně přidávat a mezitím třeba odstraňovat další ikony, jejichž identifikátory budou začínat na IDI_, editor by měl zajistit, že skutečné číslo, představující symbol IDR_MAINICON, bude nejnižší ze všech  identifikátorů ikon. Při zobrazení exe souboru třeba v průzkumníku Windows je pak vybrána ikona s nejnižším číslem. Chceme-li tedy, aby některá z ikon byla "hlavní", tedy reprezentující náš program, je nejlepší použít pro ni zmíněný identifikátor IDR_MAIN. Když nyní projekt překompilujeme, uvidíme již v průzkumníku náš exe soubor již reprezentovaný vloženou ikonou, aniž bychom museli cokoli přidávat do zdrojového kódu. Nyní se ale do zdrojového kódu opět podíváme. Budeme samozřejmě chtít, aby naše "hlavní ikona" byla zobrazena také na pruhu úloh s naším programem, a dále v systémovém menu (vlevo nahoře). Tuto ikonu určíme nastavením vlastnosti třídy, ke které okno patří. Později uvidíme,  že ji lze měnit i za běhu programu, zatím si ji určíme při registraci třídy okna. Nejprve musíme ještě vložit hlavičkový soubor "resource.h", obsahující definice identifikátorů. Ve funkci InitApp si upravíme následující 3 řádky na tento tvar:
  wc.hCursor = LoadCursor(g_hInstance, MAKEINTRESOURCE(IDC_MAIN));
  wc.hIcon = LoadIcon(g_hInstance, MAKEINTRESOURCE(IDR_MAIN));
  wc.hIconSm = LoadIcon(g_hInstance, MAKEINTRESOURCE(IDR_MAIN));
Jaký význam má použití prvku hIconSm? Máme zde možnost určit samostatnou ikonu, která bude použita v případě malé ikony zobrazované například vlevo nahoře jako místo pro rozbalení systémové nabídky okna. Pokud tento prvek nezadáme (bude mít hodnotu NULL), systém použije vhodný obrázek z ikony určené prvkem hIcon. Co mám na mysli tím vhodný obrázek? Každá ikona, ať se jedná o soubor (.ico) nebo ikonu ve zdrojích, může obsahovat (a v profesionálních programech obvykle obsahuje) více jednotlivých obrázků pro různá nastavení grafického adaptéru a systému co do barevné hloubky a pro různé požadované rozměry. Většinou vypadá lépe ikona, kterou si sami (resp. dobrý grafik) nakreslíme přímo v rozměru 16x16, než nechat systém, aby prostě zmenšil obrázek ikony 32x32, kde systém většinou jednoduše vyhodí každý druhý řádek. Když nyní takto upravený program spustíme, uvidíme již vlevo nahoře a na pruhu úloh vlastní ikonu a po najetí myší do okna se kurzor změní v zelenou šipku (kterou jsem do projektu importoval podobně jako ikonu). Dále si ukážeme, jak můžeme naši ikonu (nebo jakoukoli jinou kreslit do kontextu zařízení). Nejjednodušší funkce pro tento účel je DrawIcon:
BOOL DrawIcon(
  HDC hDC,      // handle kontextu zařízení
  int X,        // x-ová souřadnice ikony
  int Y,        // y-ová souřadnice
  HICON hIcon   // handle ikony
);
Pro získání handle ikony okna máme více možností. Mohli bychom použít funkci LoadIcon stejně, jako jsme ji použili při registraci třídy okna. Tato funkce, pokud je volána opakovaně, vrátí handle ikony, která byla již načtena. Nedochází tedy ke kumulovanému načítání stejné ikony do paměti při každém kreslení (pokud kreslíme na WM_PAINT). Alespoň podle dokumentace by nemělo. Ale člověk nikdy neví jak se tato funkce bude z tohoto hlediska chovat na různých (třeba i budoucích) verzích Windows. Je zde proto ještě jeden, podle mého názoru lepší a i o nějaký ten zlomek sekundy rychlejší způsob získání handle ikony nastavené třídě okna. Použijeme funkci GetClassLongPtr (popřípadě starší variantu GetClassLong, která není kompatibilní s 64 bitovou verzí Windows):
ULONG_PTR GetClassLongPtr(
  HWND hWnd,  // handle to window
  int nIndex  // offset of value to retrieve
);
Tato funkce nám umožňuje získat některou z hodnot, spojených s třídou okna. Kterou hodnotu chceme získat, určíme parametrem nIndex. Pro získání handle ikony použijeme hodnotu GCLP_HICON (v případě použití GetClassLong pak hodnotu GCL_HICON). Ukážeme si tedy jak v handleru WM_PAINT nakreslit ikonu okna na souřadnice 10,10 klientské oblasti:
void OnWM_PAINT()
{
  PAINTSTRUCT ps;
  HDC hdc;
  hdc = BeginPaint(g_hwndMain, &ps);
  centerText(hdc);
  DrawIcon(hdc, 10,10,
    (HICON)GetClassLongPtr(g_hwndMain, GCLP_HICON));
  EndPaint(g_hwndMain, &ps);
}
< zde je výsledek:/p>

win-api-8

K mnohem sofistikovanějším funkcím pro načítání a kreslení ikon a kurzorů se v tomto seriálu samozřejmě dostaneme, ale nyní jsme na začátku a musíme nejprve probrat ostatní základní témata. Příště se budeme věnovat nabídkám (menu) a zpracování uživatelského výběru z nabídky okna. Doprovodný projekt (Visual C++ 6) je ke stažení zde: win_api_8.zip

Máte připomínku, dotaz nebo komentář k článku a souvisejícími tématy?

Napište a odešlete ji emailem

Copyright © 2019  Radek Chalupa