19. Internetovské aplikace
  1. Ovladače Internetu v C++ Builderu jsou umístěny na stránce Internet Palety komponent. Tyto ovladače je možno rozdělit do dvou skupin. Do první skupiny řadíme ovladače ActiveX poskytnuté firmou NetMasters. Stručný popis těchto ovladačů je uveden v následující tabulce.

  2.  
    Ovladač Popis
    TNMDayTime Získává datum a čas z Internetovské služby datetime.
    TNMEcho Zasílá text na Internetovskou službu echo a získává jej zpět.
    TNMFinger Získává informace o uživatelích ze služby finger.
    TNMFTP Provádí přenos souborů mezi počítači pomocí protokolu FTP.
    TNMHTTP Provádí přenos souborů pomocí protokolu HTTP (používáme k získání dokumentu, který nemá být zobrazen ve Webovském prohlížeči).
    TNMMsg Zasílá textovou zprávu pomocí TCP/IP.
    TNMMsgServ Získává zprávu zaslanou ovladačem TNMMsg.
    TNMNNTP Zasílá a přijímá zprávy pomocí protokolu NNTP.
    TNMPOP3 Získává zprávy email z poštovního serveru pomocí protokolu POP3.
    TNMUUProcessor Kóduje a dekóduje soubory.
    TNMSMTP Zasílá email prostřednictvím protokolu SMTP.
    TNMStrm Zasílá datový proud do sítě.
    TNMStrmServ Získává datový proud zaslaný ovladačem TNMStrm.
    TNMTime Získává čas z Internetu.
    TNMUDP Přenáší data mezi sítěmi pomocí protokolu UDP.
    TPowersock Zaobaluje Winsock API.
    TNMGeneralServer Používáno pro obecné servery TCP/IP.
    THTML Zobrazuje soubory HTML. Je to komponenta Webovského prohlížeče.
    TNMURL Převádí URL na řetězec a naopak.

    Druhá kategorie ovladačů zahrnuje komponenty TClientSocket a TServerSocket. Tyto komponenty zpracovávají sokety TCP/IP. Pomocí komponent obou skupin lze vytvářet kvalitní internetovské aplikace.

  3. Jednou z programovacích úloh Internetu je vytváření Webovského prohlížeče. Základ prohlížeče je tvořen komponentou HTML. Tuto komponentu umístíme na formulář a voláme metodu RequestDoc. Tím je prohlížeč vytvořen. My ale vytvoříme nepatrně složitější prohlížeč. Začneme vývoj nové aplikace. Na formulář umístíme komponentu Panel, změníme její vlastnost Align na alTop, Height na 60 a vlastnost Caption vyprázdníme. Na panel (do horního levého rohu) umístíme komponentu ComboBox. Její šířku změníme na šířku panelu a vlastnost Text na námi zvolené URL (např. http://www.spse.cz). Na formulář vložíme ještě komponentu StatusBar a nastavíme její vlastnost SimplePanel na true. Doprostřed formuláře vložíme komponentu HTML a změníme její vlastnost Align na alClient.

  4. Pro kombinovaný ovladač je nutno ještě vytvořit obsluhy události. Obsluha události OnClick je tvořena příkazem:
    if (ComboBox1->Text != "")
      HTML1->RequestDoc(ComboBox1->Text);
    a obsluha OnKeyPress příkazy:
    if (Key == VK_RETURN) {
      Key = 0;
      if (ComboBox1->Text == "") return;
      ComboBox1->Items->Insert(0, ComboBox1->Text);
      ComboBox1Click(Sender);
    }
    Nyní již můžeme program vyzkoušet. Zapíšeme URL do kombinovaného ovladače a stiskneme Enter. Pokud jsme zadali přípustné URL, pak určená stránka je zavedena do ovladače HTML.
  5. K našemu prohlížeči přidáme nyní indikátor zpracování. Na stavovém řádku se necháme informovat o stavu zavádění stránky. Vytvoříme obsluhy událostí OnUpdateRetrieval (informuje o průběhu stahování) a OnEndRetrieval (dokončení stahování) komponenty HTML. Pro výpočet procenta zavedení použijeme vlastnosti GetBytesTotal (celková velikost dokumentu) a GetBytesDone (velikost již stažené části dokumentu). Obsluha OnUpdateRetrieval je tvořena příkazy:

  6. int total = HTML1->RetrieveBytesTotal;
    int done = HTML1->RetrieveBytesDone;
    int percent;
    if (total == 0 || done == 0) percent = 0;
    else percent = ((done * 100) / total);
    char buff[80];
    wsprintf(buff, "Getting Document: %d%% of %dK", percent, total / 1024);
    StatusBar1->SimpleText = buff;
    a obsluha OnEndRetrieval příkazem:
    StatusBar1->SimpleText = "Done";
  7. V našem prohlížeči provedeme ještě nějaké změny. Na panel pod kombinovaný ovladač umístíme čtyři tlačítka. U prvního z nich změníme vlastnost Caption na Go! a obsluha jeho stisknutí bude tvořena příkazem:

  8. ComboBox1Click(0);
    U druhého tlačítka změníme Caption na Stop a obsluha jeho stisknutí je tvořena:
    HTML1->Cancel(0);
    StatusBar1->SimpleText = "Done";
    Pro třetí tlačítko změníme Caption na Reload a obsluha jeho stisku bude stejná jako pro první tlačítko. Poslední, čtvrté tlačítko bude mít Caption změněno na View Source a obsluha jeho stisku bude tvořena příkazy:
    HTML1->ViewSource = !HTML1->ViewSource;
    if (HTML1->ViewSource) Button4->Caption = "View Document";
    else Button4->Caption = "View Source";
    Ke komponentě HTML přidáme ještě obsluhu události OnDoRequestDoc. Je tvořena příkazem:
    StatusBar1->SimpleText = "Connecting to " + URL + "...";
    Další přidaná obsluha OnBeginRetrieval je tvořena:
    StatusBar1->SimpleText = "Connected...";
    ComboBox1->Items->Insert(0, ComboBox1->Text);
    Když se nyní podíváme na obsluhu události OnKeyPress kombinovaného ovladače, pak v ní vidíme stejný řádek jako poslední řádek v poslední obsluze. Tento řádek v obsluze OnKeyPress můžeme zrušit.
    Tím je vývoj aplikace Internetovského prohlížeče dokončen.
  9. Dále se budeme zabývat odesíláním pošty z aplikace. Odeslání email není těžké. Aplikace např. může být tvořena komponentou Memo a tlačítkem Send. Uživatel napíše text do komponenty Memo a stiskem tlačítka jej odešle.

  10. Ovladač NMSMTP je používán k odesílání pošty pomocí služby SMTP.SMPT je protokol, který nevyžaduje autorizované přihlášení na server. Můžeme se jednoduše připojit k nějakému poštovnímu serveru, odeslat zprávu email a odpojit se. Vlastnost Host je použita ke specifikaci jména poštovního serveru, ke kterému se chceme připojit. Obvykle zde používáme hodnotu mail (říkáme tím ovladači NMSMTP, že se chceme připojit k našemu lokálnímu mail serveru). Pokud chceme, můžeme specifikovat jméno poštovního serveru (např. mail.mujpodnik.cz), ale není to obvykle nutné. Vlastnost Port je použita ke specifikaci portu, na který se chceme připojit. Implicitní port SMTP je 25. Všechny informace poštovní zprávy jsou obsaženy ve vlastnosti PostMessage. Tato vlastnost je třída obsahující vlastnosti ToAddress, FromAddress, Subject, Body apod. Nastavíme tyto vlastnosti (ovladač NMSMTP přejmenujeme na SMTP) a můžeme odeslat zprávu. Dříve je ale nutno připojit se k SMTP serveru. To provedeme metodou Connect:
    SMTP->Host = "mail";
    SMTP->Connect();
    Po připojení lze odeslat email. Např.
    SMTP->PostMessage->FromAddress = "jmeno@spse.cz";
    SMTP->PostMessage->ToAddress->Add("jmeno1@spse.cz");
    SMTP->PostMessage->Subject = "Test";
    SMTP->PostMessage->Body->Add("Toto je test");
    SMTP->SendMail();
    Povšimněte si, že vlastnosti ToAddress a Body jsou typu TStringList. Mail lze odeslat několika adresátům a text může obsahovat několik řádek.
    Po připojení k SMTP serveru je generována událost OnConnect. Není ale vhodné odesílat mail z obsluhy této události (jsou zde jisté omezení). Vlastnosti FromAddress a ToAddress musí být zadány. Všechny ostatní vlastnosti PostMessage jsou volitelné.
    Po úspěšném odeslání zprávy se můžeme odpojit od SMTP serveru. Po odeslání zprávy je generována událost OnSuccess. Obsluhu této události může tvořit příkaz:
    SMTP->Disconnect();
    Můžeme odeslat několik zpráv během jednoho připojení. Pokud máme několik zpráv, není nutno po odeslání každé zprávy se odpojit a opětovně připojit. Zpráva může být odeslána bez problémů nebo při odeslání zprávy může vzniknout chyba. Při chybě je generována událost OnFailure, v jejíž obsluze se můžeme odpojit od serveru.
    Tím jsme se seznámili s odesíláním email. Pro příjem používáme komponentu POP3.
  11. Jak již víme, knihovna vizuálních komponent je zapsána v Object Pascalu. Nyní se budeme zabývat tím, jak jazykové služby Object Pascalu jsou implementovány v C++ Builderu. Tento popis je určen pro programátory používající objekty VCL ve svých aplikacích a pro vývojáře vytvářející nové třídy odvozené od VCL tříd.

  12. Odkazy na C++ třídy odvozené od TObject ukazují na třídy, pro které TObject je nutný, ale ne nutně bezprostřední, předek. Tyto třídy také označujeme jako "třídy stylu VCL".
    C++ a Object Pascal májí jisté rozdíly ve vytváření, inicializaci, kopírování a rušení objektů. S těmito rozdíly se seznámíme podrobněji.
    V C++ instance třídy je aktuální objekt. S objektem lze pracovat přímo nebo jej lze zpřístupnit nepřímo a to pomocí odkazu nebo ukazatele. Např. pro třídu C++ jména CPP_trida s konstruktorem bez parametrů, jsou přípustné všechny následující instance této třídy:
    CPP_trida hodnotou;                // objekt typu CPP_trida
    CPP_trida& odkaz = hodnotou;       // odkaz na objekt hodnotou
    CPP_trida* prt = new CPP_trida();  // ukazatel na objekt typu CPP_trida
    V Object Pascalu proměnná typu objekt se vždy odkazuje na objekt nepřímo. Paměť pro všechny objekty je alokována dynamicky. Např. pro třídu Object Pascalu OP_trida
    odkaz: OP_trida;
    odkaz := OP_trida.Create;
    odkaz je odkaz na objekt typu OP_trida. V C++ Builderu bychom to zapsali takto:
    OP_trida* odkaz = new OP_trida();
    Mezi odkazy v C++ a Object Pascalu jsou jisté rozdíly. Těmito rozdíly se nebudeme podrobněji zabývat. Abychom se těmto rozdílům vyhnuli, všechny identifikátory objektů VCL jsou přeloženy na ukazatele C++ v C++ Builderu.
    Na rozdíl od C++, Object Pascal nemá zabudovanou podporu pro vytváření kopií objektů. Operátor přiřazení Object Pascalu (:=) není přiřazovacím operátorem třídy. V následujícím kódu, operátor přiřazení kopíruje odkaz a ne objekt. V následujícím kódu se B a C odkazují na stejný objekt:
    B, C: TButton;
    B := TButton.Create(vlastniOvladac);
    C := B;
    Předchozí příklad je možno přeložit na následující kód v C++ Builderu:
    TButton* B = NULL;
    TButton* C = NULL;
    B = new TButton(vlastniOvladac);
    C = B;     // kopie ukazatele a ne objektu
    Třídy stylu VCL v C++ Builderu splňují jazyková pravidla Object Pascalu pro operátor přiřazení. Následující kód je tedy nepřípustný:
    TVCLStyleClass* p = new TVCLStyleClass;
    TVCLStyleClass* q = new TVCLStyleClass;
    *p = *q;    // není dovoleno pro třídy stylu VCL
    neboť se snaží kopírovat objekt a ne ukazatel.
    Pro třídy stylu VCL je stále přípustné vytváření odkazů. Např. následující kód je přípustný:
    TVCLStyleClass* ptr = new TVCLStyleClass;
    TVCLStyleClass& odk = *ptr;    // přípustné
    Object Pascal nemá kopírovací konstruktor a tedy ani třídy stylu VCL v C++ Builderu nemají kopírovací konstruktor. Následující kód se pokusí vytvořit ukazatel na TButton pomocí kopírovacího konstruktoru:
    TButton* B = new TButton(vlastniOvladac);
    TButton* C = new TButton(*B);    // není dovoleno pro třídy stylu VCL
    Pro třídy VCL nezapisujeme kód, který předpokládá použití kopírovacího konstruktoru. Pro vytvoření kopie objektu třídy stylu VCL v C++ Builderu, můžeme zapsat metodu, která kopíruje objekt. Potomci třídy TPersistent mohou také předefinovat metodu Assign pro kopírování dat z jednoho objektu do jiného. Toto se obvykle provádí u grafických tříd (např. TBitmap a TIcon), které obsahují zdroje obrázků. Způsob kopírování objektu může být určen tvůrcem komponenty, ale nesmíme zapomenout, že některé způsoby použitelné v C++ nejsou dostupné pro třídy stylu VCL.
    Je také rozdíl ve způsobu, kdy instance proměnné odkazující se na objekty v C++ a v Object Pascalu, mohou být předávány funkcím. V C++ objekty mohou být předány funkci hodnotou, odkazem nebo ukazatelem. Object Pascal umožňuje předávat objekty hodnotou nebo odkazem. Při předávání parametru hodnotou v Object Pascalu se ale také ve skutečnosti jedná o předávání odkazu na objekt. V Object Pascalu není tedy ekvivalent pro předávání objektu hodnotou v C++. Totéž platí i pro třídy stylu VCL.
  13. V C++ a Object Pascalu jsou také vytvářeny různě objekty. Ve standardním C++, pořadí vytváření je: virtuální základní třída, následované základními třídami a nakonec odvozená třída. Syntaxe C++ používá inicializační seznam konstruktorů k volání konstruktorů základních tříd. Běhový typ objektu je ta třída jejíž konstruktor byl právě volán. Vyřizování virtuálních metod se mění v průběhu vytváření.

  14. V Object Pascalu je zajištěno volání pouze konstruktoru vytvářené třídy; nicméně paměť pro základní třídy je alokována. Vytvoření každé základní třídy je provedeno voláním inherited v konstruktoru odvozené třídy. Třídy VCL používají inherited k volání (neprázdného) konstruktoru základní třídy. Není to ale vyžadováno jazykem. Běhový typ objektu je určen bezprostředně a nemění se při volání konstruktorů základních tříd. Vyřizování virtuálních metod se nemění v průběhu vytváření.
    Objekty stylu VCL jsou vytvářeny podobně jako objekty Object Pascalu, ale pomocí syntaxe C++. To znamená, že způsob a pořadí volání konstruktorů základní třídy splňuje syntaxi C++, používající seznam inicializací pro všechny základní třídy neVCL a prvního bezprostředního předka VCL. Tato základní třída VCL je první třída, která bude vytvořena. Tedy základní třídy VCL jsou vytvářeny v opačném pořadí než v C++. Běhový typ objektu a zpracování virtuálních metod jsou založeny na Object Pascalu.
  15. Virtuální metody vyvolané z těla konstruktorů základní třídy VCL, tj. tříd implementovaných v Object Pascalu, jsou zachytávány jako v C++, v závislosti na běhovém typu objektu. Protože C++ Builder kombinuje model Object Pascalu, nastavování běhového typu objektu bezprostředně s modelem C++ i vytváření základních tříd před vytvářením odvozených tříd, volání virtuálních metod z konstruktorů základních tříd ve třídách stylu VCL může mít zákeřné vedlejší efekty.

  16. V Object Pascalu, programátor může používat klíčové slovo inherited, které poskytuje flexibilitu pro volání konstruktorů základní třídy, kdekoliv v těle konstruktoru odvozené třídy. Pokud odvozená třída přepisuje nějakou virtuální metodu, která závisí na nastavení objektu nebo inicializaci datových složek, pak to lze provést před voláním konstruktoru základní třídy a tedy před vyvoláním virtuálních metod.
    Syntaxe C++ nemá klíčové slovo inherited pro volání konstruktoru základní třídy v průběhu vytváření odvozené třídy. Není to nutné, neboť běhový typ objektu je určen právě vytvářenou třídou a ne odvozenou třídou. Tedy vyvolané virtuální metody patří do současné třídy a ne do odvozené třídy. Není tedy nutno inicializovat datové složky nebo nastavovat objekt odvozené třídy dříve než tyto metody jsou volány.
    V C++ Builderu objekty stylu VCL mají běhový typ odvozené třídy i při volání konstruktoru základní třídy. Pokud konstruktor základní třídy volá virtuální metodu, pak je vyvolána metoda odvozené třídy, pokud odvozená třída ji předefinovává. Jestliže tato virtuální třída závisí na něčem v inicializačním seznamu nebo v těle konstruktoru odvozené třídy, pak metoda je volána dříve než to nastane. Virtuální metody obvykle nevoláme v konstruktoru, ale jsou vyvolány nepřímo.
    Následující příklad porovnává třídy stylu C++ a VCL, které mají překryté virtuální metody. Tento příklad ukazuje jak volat tyto virtuální metody z konstruktorů základní třídy v obou případech. Třídy MujZakl a MujOdv jsou standardní třídy C++. Třídy MujVCLZakl a MujVCLOdv jsou třídy stylu VCL, odvozené od TObject. Virtuální metoda CoJsem je v obou odvozených třídách předefinována, ale je volána pouze v konstruktorech základních tříd a ne v konstruktorech odvozených tříd.
    #include <sysdefs.h>
    #include <sysutils.hpp>
    #include <iostream.h>
    // Třídy stylu C++
    class MujZakl {
    public:
     MujZakl() { CoJsem(); }
     virtual void CoJsem() { cout << "Jsem základní" << endl; }
    };
    class MujOdv : public MujZakl {
    public:
     virtual void CoJsem() { cout << "Jsem odvozená" << endl; }
    };
    // Třídy stylu VCL
    class MujVCLZakl : public TObject {
    public:
     __fastcall MujVCLZakl() { CoJsem(); }
     virtual void __fastcall CoJsem() { cout << "Jsem základní" << endl; }
    };
    class MujVCLOdv : public MujVCLZakl {
    public:
     virtual void __fastcall CoJsem() { cout << "Jsem odvozená" << endl; }
    };
    int main(void)
    {
     MujOdv d;                         // instance třídy C++
     MujVCLOdv *pvd = new MujVCLOdv;   // instance třídy VCL
     return 0;
    }
    Výstup z tohoto příkladu je:
    Jsem základní
    Jsem odvozená
    protože je rozdíl v běhových typech MujOdv a MujOVLOdv během volání jejich konstruktorů základní třídy.
    Jelikož ve virtuálních funkcích mohou být použity datové složky, je důležité znát jak jsou inicializovány. V Object Pascalu jsou všechna neinicializovaná data vynulována. V C++ to není zajištěno. Následující typy datových složek třídy musí být inicializovány v inicializačním seznamu konstruktoru třídy: odkazy a datové složky s neimplicitním konstruktorem.
    Hodnoty datových složek inicializovaných v těle konstruktoru jsou při volání konstruktoru základní třídy nedefinovány. V C++ Builderu paměť pro třídy stylu VCL je vynulována (hodnoty jsou aktuálně nedefinované).
    Virtuální funkce používající hodnoty proměnných inicializovaných v těle konstruktoru nebo v inicializačním seznamu se chovají jako kdyby byly inicializovány nulou. To proto, že konstruktor základní třídy je volán před zpracováním inicializačního seznamu nebo provedením těla konstruktoru. Ukazuje to následující příklad:
    #include <sysdefs.h>
    #include <sysutils.hpp>
    class Base : public TObject {
    public:
     __fastcall Base() { init(); }
     virtual void __fastcall init() { }
    };
    class Derived : public Base {
    public:
     Derived(int nz) : nenula(nz) { }
     virtual void __fastcall init()
     {
      if (nenula == 0)
       throw Exception("nenula je nula!");
     }
    private:
     int nenula;
    };
    int main(void)
    {
     Derived *d42 = new Derived(42);
     return 0;
    }
    Tento příklad generuje výjimku v konstruktoru Base. Protože Base je vytvářena před Derived, nenula není inicializována na hodnotu 42, předanou konstruktoru. Nemůžeme inicializovat datové složky našich tříd stylu VCL před vyvoláním konstruktoru základní třídy.
  17. Rušení objektů se liší ve dvou věcech v C++ od Object Pascalu: destruktory volanými z důvodu výjimky vzniklé v konstruktoru a virtuálními metodami volanými z destruktorů. Předpokládejme následující příklad: třída C je odvozena od třídy B, která je odvozena od A.

  18. class A
    {
     // tělo
    };
    class B: public A
    {
     // tělo
    };
    class C: public B
    {
     // tělo
    };
    Předpokládejme, že výjimka vznikne v konstruktoru třídy B při vytváření instance C. Výsledek se liší v C++, Object Pascalu a pro třídy stylu VCL: Zpracování virtuálních metod v destruktorech se řídí stejnými pravidly jako jejich zpracování v konstruktorech. To znamená, že pro třídy stylu VCL je nejprve zrušena odvozená třída, ale běhový typ objektu zůstává nezměněn i při volání destruktorů základní třídy. Tedy, pokud virtuální metody jsou volány v destruktoru základní třídy VCL, mají být zpracovány třídou, která již byla zrušena.
    TObject zavádí dvě virtuální metody, BeforeDestruction a AfterConstruction, které umožňují programátorům zapsat kód, který je zpracován před  zrušením objektu a po vytvoření objektu. AfterConstruction je volán po vyvolání posledního konstruktoru. BeforeDestruction je volán před voláním prvního destruktoru. Tyto metody jsou veřejné a jsou volány automaticky.
    Object Pascal má koncepci virtuálních funkcí třídy. C++ má analogii v statických virtuálních funkcích, ale C++ má nepřesný vztah k tomuto typu funkcí. Tyto funkce jsou bezpečně volány z VCL, ale nikdy je nesmíme volat sami. K identifikaci těchto funkcí v hlavičkových souborech jsou předcházeny komentářem:
    /* virtual class method */
  19. Nyní si na jednoduché aplikaci ukážeme, jak měnit obsluhu nějaké události. Začneme vývoj nové aplikace a na formulář umístíme tlačítko. Jeho Caption změníme na Změň obsluhu události. Stiskem tlačítka budeme měnit obsluhu stisku tohoto tlačítka. Obsluhu stisku tlačítka tvoří příkazy:

  20. MessageBox(NULL, "Obsluha události # 1", "První", MB_OK);
    Button1->OnClick = Button1Click2;
    V této obsluze přiřazujeme jinou obsluhu (Button1Click2) obsluze stisku tohoto tlačítka. Při dalším stisku tlačítka bude vyvolána tato jiná obsluha. Tuto obsluhu musíme ještě vytvořit. Vytvoříme tedy funkci (je potřeba také doplnit deklaraci třídy formuláře v hlavičkovém souboru):
    void __fastcall TForm1::Button1Click2(TObject *Sender)
    {
        MessageBox(NULL, "Obsluha události # 2", "Druhá", MB_OK);
        Button1->OnClick = Button1Click;
    }
    V této obsluze se opět vrátíme k původní obsluze. Aplikace je hotova, můžeme ji vyzkoušet. Vidíme, že obsluhy lze snadno měnit přiřazením (musí souhlasit parametry obsluhy).
  21. Dále se opět podíváme na již hotovou aplikaci. Stáhneme si ji a vyzkoušíme. Tato aplikace má kurzory a bitové mapy nadefinované v souboru zdrojů. Podívejte se, jak tyto prvky jsou používány. Prostudujte si také používání dalších prvků (např. komponenty TrackBar v dialogovém okně voleb, změnu volby Pause v nabídce apod.).
19. Internetovské aplikace