Otázky a odpovědi

Programátorská poradna

Časová náročnost (min):

Začátečník

Pokročilý

Profesionál

Použitý operační systém : Hlavní vývojový nástroj :

Další vývojový software :

Jiný software :

Windows 2000 SP3

Visual C# .NET 2002

Žádný

Žádný

 

 

 

Chtěl bych z aplikace napsané v C# zavolat API funkci Windows, ovšem nemůžu najít způsob, jak to provést. Mohl byste mi poradit?

 

Budete-li se pokoušet volat z řízené aplikace neřízenou funkci aplikačního programovacího rozhraní (API) Windows, budete muset splnit před samotným použitím vámi požadované funkce několik podmínek:

 

1.      Deklaraci API funkce musí předcházet použití speciálního atributu DllImportAttribute, který se nachází ve jmenném prostoru System.Runtime.InteropServices. Atribut DllImportAttribute naznačuje, že bude importována API funkce s neřízeným kódem. Ve skutečnosti je atribut pouze jiným označením pro speciální třídu se stejným názvem (DllImportAttribute). Konstruktor této třídy pracuje s jedním parametrem typu string, kterým je název DLL knihovny, ve které je uložen kód API funkce, kterou chcete použít.

 

2.      Deklarační příkaz API funkce musí obsahovat klíčová slova static a extern.

 

3.      Deklarace API funkce v řízeném kódu musí přesně odpovídat prototypu funkce v neřízeném kódu. To znamená, že deklarace musí obsahovat stejné jméno, dále stejnou signaturu s příslušnými datovými typy formálních parametrů funkce a také stejnou návratovou hodnotu (pokud existuje).

 

Jestliže budou splněny tyto podmínky, bude API funkce úspěšně naimportována do kódu vaší řízené aplikace. S API funkcí můžete posléze pracovat jako s jakoukoliv jinou funkcí. Podívejme se na ukázku použití API funkce operačního systému Windows:

 

             // ... Tento programový kód se nachází uvnitř třídy Form1.

             static void Main()

             {

                    Application.Run(new Form1());

             }

 

             [System.Runtime.InteropServices.DllImport("User32")]

                    private static extern bool MoveWindow(int hWnd,

                    int X, int Y, int nWidth, int nHeight, bool bRepaint);

 

             [System.Runtime.InteropServices.DllImport("User32")]

                    private static extern bool AnimateWindow(int hWnd,

                    uint dwTime, uint dwFlags);

 

             private void button1_Click(object sender, System.EventArgs e)

             {

                    frm = new Form();

                    frm.Load += new EventHandler(frm_Load);

                    frm.Show();

             }

            

             Form frm;

 

             private void frm_Load(object sender, System.EventArgs e)

             {

                    int handle = frm.Handle.ToInt32();

                    MoveWindow(handle, 362, 234, 300, 300, true);

                    AnimateWindow(handle, 200, 0x00080000);

             }

             // ... zde pokračuje kód třídy Form1.

 

Ukázkový programový kód používá pro přesun a animaci nově vytvořené instance třídy Form dvě API funkce: MoveWindow a AnimateWindow. Před každou z funkcí se nachází atribut DllImport se jménem dynamicky linkované knihovny (DLL), ve které je uložen kód dané funkce (obě zde použité funkce jsou uloženy v souboru User32.dll). Velmi důležité je, aby byl atribut zapsán v hranatých závorkách. Za atributem se nachází samotný deklarační příkaz, který je složen z modifikátoru přístupu (private), klíčových slov static a extern, typem návratové hodnoty funkce a její signaturou (datové typy přitom musí odpovídat datovým typům použitím v neřízeném kódu API funkce). Ve výše uvedené programové ukázce je dále deklarována referenční proměnná frm s oborem třídy, které může uchovávat referenci na instance třídy Form.  V událostní proceduře button1_Click je vytvořena nová instance třídy Form, přičemž dochází také k vytvoření systémového delegáta (EventHandler) pro zpracovatele události Load nově vytvořené instance. Jakmile bude aktivována metoda Show objektu frm, delegát usměrní program tak, aby byl uskutečněn programový kód, jenž se nachází ve zpracovateli události frm_Load. Když dojde k události Load objektu frm, bude vykonán kód zpracovatele frm_Load (k události dojde po zavolání metody Show příslušné instance). Zpracovatel události frm_Load obsahuje následující řádky programového kódu:

 

private void frm_Load(object sender, System.EventArgs e)

       {

             int handle = frm.Handle.ToInt32();

             MoveWindow(handle, 362, 234, 300, 300, true);

             AnimateWindow(handle, 200, 0x00080000);

       }

 

Co proboha tento kód provádí? Tak především, je získán handle okna instance třídy Form. Handle představuje jednoznačný identifikátor, pomocí kterého Windows přistupují k vytvořené instanci (hodnota identifikátoru handle je v tomto případě převedena do podoby 32bitového celého čísla pomocí metody ToInt32). Handle okna potřebujeme získat, neboť jej využijeme v obou API funkcích. Abyste přesně věděli, co uvedené API funkce provádějí, představíme si je podrobněji:

 

1.      API funkce MoveWindow má tuto všeobecní podobu (v C/C++):

 

BOOL MoveWindow(

  HWND hWnd,      // handle okna

  int X,          // horizontální pozice okna

  int Y,          // vertikální pozice okna

  int nWidth,     // šířka okna

  int nHeight,    // výška okna

  BOOL bRepaint   // volba, která určuje, zdali bude po přemístění realizováno                                   

                  // také překreslení okna

);

 

Podoba naší funkce je následovní:

 

MoveWindow(handle, 362, 234, 300, 300, true);

 

Funkce zachovává implicitní velikost okna instance třídy Form (300x300 pixelů), ovšem mění pozici levého horního bodu okna na bod se souřadnicemi (362, 234). Poslední parametr (bRepaint) je nastaven na hodnotu true, což znamená, že ihned poté, co se okno instance přesune, bude provedeno jeho překreslení.

    

2.      API funkce AnimateWindow má tuto všeobecní podobu (opět v C/C++):

 

BOOL AnimateWindow(

  HWND hwnd,     // handle okna

  DWORD dwTime,  // doba trvání animace okna

  DWORD dwFlags  // typ animace

);

 

A opět se podívejme na naší implementaci funkce AnimateWindow:

 

AnimateWindow(handle, 200, 0x00080000);

 

Prvním parametrem funkce je handle okna, které bude animováno. Druhý parametr (dwTime) je datového typu DWORD (typ DWORD představuje 32bitové celé číslo bez znaménka, jehož maximální hodnota je 232-1). V C# se typ DWORD samozřejmě nepoužívá, ovšem jeho ekvivalentem je datový typ uint, jenž disponuje stejným rozsahem platných celočíselných hodnot. Parametr dwTime odpovídá době trvání animace, která se udává v milisekundách. V našem případě bude animace trvat 200 milisekund, což je doporučená hodnota daná způsobem práce operačního systému. Nejhrozivěji pravděpodobně vypadá třetí parametr (dwFlags), jehož hodnotou je podivně vyhlížející číslo v šestnáctkové soustavě 0x0008000. Numerická hodnota 0x0008000 odpovídá interní konstantě AW_BLEND, která se používá v případě, kdy má být okno animováno pomocí tzv. fade efektu. Fade efekt znamená plynulé animování zobrazení okna, přičemž na začátku je okno takřka průhledné a postupně se „zaplňuje“ do standardního barevného schématu a podoby.  

 

Tip

Pokud přemýšlíte nad tím, jak vyjádřit číslo 0x0008000 v desítkové soustavě, zde je návod:

 

1.      Z numerické hodnoty 0x0008000 nejprve odstraníme prefix 0x, který vizuálně připomíná, že pracujeme s číslem v šestnáctkové soustavě. Tím pádem získáváme hodnotu 0008000.

 

2.      Hodnotu 0008000 přetypujeme na nám srozumitelné číslo podle tohoto vzorce:

 

0008000 = 0*167+0*166+0*165+8*164+0*163+0*162+0*161+0*160

 

3.      Z celého vzorce nakonec získáme pouze jedinou hodnotu, kterou je číslo 524 288 (8*164=524 288).

 

Doposud jsem programoval ve Visual Basicu .NET. Myslíte, že bych se mohl naučit programovat také ve Visual C#?

 

Zcela jistě ano. Visual C# je velmi zajímavý jazyk, který vhodně kombinuje rychlý návrh aplikací, jenž je známý právě z Visual Basicu a flexibilní syntaxi, která je předností jazyka C++. Protože všechny jazyky platformy .NET využívají společné integrované prostředí, na první pohled možná ani nepostřehnete, že se nacházíte uvnitř Visual C#. Visual C# taktéž nabízí vizuální vývoj aplikací, takže grafickou podobu aplikace můžete navrhnout přesně tak, jak to děláte ve Visual Basicu .NET. Na druhé straně je pravdou, že zápis programového kódu se od Visual Basicu podstatně liší a zde vám zcela určitě pomůže předcházející zkušenost s jazykem C nebo C++ (případně s Javou). Pokud neznáte uvedené jazyky, nemusíte zoufat. Seriál Začínáme s jazykem Visual C# vám poskytne všechny potřebné informace nato, abyste mohli psát své první programy i ve Visual C#. Ve skutečnosti jsou jazyky Visual C# a Visual Basic .NET do velké míry podobné ve smyslu implementace stejných, nebo přinejmenším velmi podobných programovacích prvků. Ku příkladu, oba jazyky umožňují řídit tok programu pomocí rozhodovací konstrukce If (if v C#), ovšem zápis této konstrukce je v C# jiný než ve Visual Basicu (podobně je tomu také třeba při cyklech či prvcích OOP). Sečteno a podtrženo, jestliže dobře ovládáte Visual Basic .NET, při pilném studiu se do tajů Visual C# můžete dostat velmi rychle. Poněkud problematičtější je ovšem přechod k Visual C# pro programátory, kteří pracují s Visual Basicem verze 6. Ty totiž budou muset nejdříve zvládnout všechny inovace a modifikace, jež přináší vývojová platforma .NET.            

 

Pracuji s formulářem, který bych potřeboval vertikálně i horizontálně vycentrovat na obrazovce počítače. Vím, že v režimu návrhu mohu nastavit vlastnost StartPosition na hodnotu CenterScreen, co mám však udělat, když chci provést vystředění formuláře za běhu programu?

 

Událostní proceduru Load formuláře upravte do níže uvedené podoby:

 

       private void Form1_Load(object sender, System.EventArgs e)

             {

                    this.Top = (Screen.PrimaryScreen.Bounds.Height - this.Height) / 2;

                    this.Left = (Screen.PrimaryScreen.Bounds.Width - this.Width) / 2;

             }

 

Abyste mohli provést vystředění formuláře, musíte znát rozměry zobrazovací plochy obrazovky a rozměry samotného formuláře. Rozměry formuláře jsou známé, nakolik Visual C# implicitně vytváří formuláře s velikostí 300x300 obrazových bodů neboli pixelů. Při zrození instance formuláře jsou do vlastností Height a Width této instance uloženy hodnoty 300 pixelů. Pro zjištění rozměrů viditelné plochy obrazovky použijeme třídu Screen. Konstruktor třídy Screen ovšem není veřejný a proto není možné přímo vytvářet instance této třídy. Pokud ovšem zavoláme statickou vlastnost PrimaryScreen, podaří se nám získat instanci třídy Screen. Vrácená instance představuje hlavní zobrazovací jednotku počítačového systému (pokud systém pracuje jenom s jednou obrazovkou, bude vrácena tato, v opačném případě bude vrácena ta zobrazovací jednotka, která je nakonfigurována jako primární). Dále pokračujeme zavoláním vlastnosti Bounds vytvořené instance třídy Screen. Vlastnost Bounds vrací instanci třídy Rectangle (jde o objekt, jenž popisuje obdélníkový region hlavní zobrazovací jednotky). Máme-li instanci třídy Rectangle, můžeme konečně použít vlastnost Height (resp. Width) pro zjištění výšky (resp. šířky) viditelné plochy obrazovky. Předpokládejme, že vlastníte 17-palcový monitor a používáte rozlišení 1024x768 obrazových bodů. Za těchto podmínek vrátí příkaz Screen.PrimaryScreen.Bounds.Height hodnotu 768 a příkaz Screen.PrimaryScreen.Bounds.Width zase vrátí hodnotu 1024. Od takto získaných hodnot posléze odečteme hodnoty, které reprezentují výšku (resp. šířku) formuláře a finální výsledky vydělíme dvěma. Do vlastnosti Top formuláře bude po provedení všech nastíněných operací uložena hodnota 234, zatímco vlastnost Left bude obsahovat hodnotu 362 (viz obrázek).

 

     

 

Chtěl bych se zeptat, jak je možné ve Visual C# vytvářet zpracovatele události, a to jak v režimu návrhu, tak i za běhu aplikace. 

 

V režimu návrhu aplikace vytvoříte zpracovatele události takto:

 

1.      Na formuláři vyberte ten ovládací prvek, pro který chcete vytvořit zpracovatele události.

2.      V okně Properties Window klepněte na tlačítko Events ().

3.      Vyhledejte položku s názvem požadované události (např. Click) a poklepejte na tuto položku.

4.      Visual C# okamžitě vygeneruje programovou kostru událostní procedury a otevře okno editoru pro zápis programového kódu.

5.      Vyplňte tělo vytvořené událostní procedury vhodným programovým kódem.

 

Jestliže budete chtít spojit událost s jejím zpracovatelem za běhu programu, budete muset všechny potřebné operace provést přímo z programového kódu. Aby bylo možné vytvořit spojení mezi událostí a příslušným zpracovatelem, musíte vytvořit systémového delegáta, který bude zodpovědný za to, aby událost našla svého zpracovatele.

 

Následující fragment kódu byl vyňat ze třídy Form1. Můžete si v něm všimnout událostní proceduru button1_Click. V této proceduře dochází k vytvoření nové instance třídy Button (s názvem btnTlačítko1), dále jsou nastaveny některé klíčové vlastnosti nově vytvořené instance a instance je přidána do kolekce ovládacích prvků formuláře.

 

Spojení mezi události Click tlačítka btnTlačítko1 a zpracovatelem této události (btnTlačítko1_Click) zabezpečuje systémový delegát EventHandler. Při této programové operaci je velmi důležité správné použití operátorů += a new. Nakonec už stačí jenom napsat událostní proceduru btnTlačítko1_Click, která ovšem musí disponovat stejnými prvky, jako systémový delegát (nesmí vracet hodnotu a musí pracovat se dvěma parametry typu object a System.EventArgs). Pokud máte zkušenosti s jazykem C nebo C++, můžete si delegáta představit jako funkční ukazatel, který je ale v jazyce Visual C# zcela typově bezpečný. Delegát tak ve skutečnosti obsahuje runtime adresu funkce, v našem případě zpracovatele události, a tohoto zpracovatele aktivuje vždy, když je o to požádán (neboli vždy, když uživatel klepne na tlačítko).   

 

             private void button1_Click(object sender, System.EventArgs e)

             {

                    Button btnTlačítko1 = new Button();

                    btnTlačítko1.Text = "Tlačítko";

                    btnTlačítko1.Location = new Point(10, 100);

                    btnTlačítko1.Size = new Size(200, 100);

                    this.Controls.Add(btnTlačítko1);

                    btnTlačítko1.Click += new System.EventHandler(btnTlačítko1_Click);

             }

 

             private void btnTlačítko1_Click(object sender, EventArgs e)

             {

                    MessageBox.Show("Právě jste klepli na tlačítko.");

             }

 

Graficky můžeme vztah mezi událostí, jejím zpracovatelem a systémovým delegátem znázornit takto:

 

 

 

Ján Hanák