20. Pou₧φvßnφ DLL
  1. Dynamicky sestavitelnΘ knihovny (DLL) umo₧≥ujφ modularizovat aplikace a tak usnad≥ujφ aktualizovßnφ a op∞tovnΘ pou₧φvßnφ jejich funkΦnosti. TakΘ pomßhajφ redukovat pam∞¥ovΘ nßroky, pokud n∞kolik aplikacφ pou₧φvß ve stejnou dobu stejnΘ funkce, nebo¥ ka₧dß aplikace zφskß svou vlastnφ kopii dat, ale k≤d je sdφlen. Ve Windows, DLL jsou moduly, kterΘ obsahujφ funkce a data. DLL jsou zavedeny za b∞hu volajφcφm modulem. Kdy₧ DLL je zavedena, je mapovßna do adresovΘho prostoru volajφcφho procesu.

  2. DLL mohou definovat dva typy funkcφ: exportovanΘ a internφ. ExportovanΘ funkce mohou b²t volßny ostatnφmi moduly. Internφ funkce mohou b²t volßny pouze uvnit° DLL, ve kterΘ jsou definovßny. Vytvß°enφ a pou₧φvßnφ DLL je podrobn∞ popsßno v SDK Win32.
    DLL Windows mohou b²t pou₧ity v aplikacφch C++ Builderu stejn∞ jako v libovoln²ch jin²ch aplikacφch C++. Ke statickΘmu zavedenφ DLL p°i zavßd∞nφ naÜφ aplikace C++ Builderu, sestavφme importnφ knihovnφ soubor pro tuto DLL do naÜφ aplikace C++ Builderu b∞hem sestavovßnφ. Pro p°idßnφ importnφ knihovny k aplikaci C++ Builderu, otev°eme vytvß°ecφ soubor (BPR) pro aplikaci a p°idßme jmΘno importnφ knihovny do seznamu knihovnφch soubor∙ p°i°azen²ch k prom∞nnΘ ALLLIB. Je-li to nutnΘ, p°idßme cestu k importnφ knihovn∞ k cestßm uveden²m ve volb∞ -L prom∞nnΘ LFLAGS.
    ExportovanΘ funkce z DLL potom budou dostupnΘ pro pou₧itφ naÜφ aplikacφ. Prototypy funkcφ DLL naÜe aplikace pou₧φvß s modifikßtorem __declspec(dllimport):
    extern "C" __declspec(dllimport) nßvratov²_typ jmΘno_importovanΘ_funkce(parametry);
    K dynamickΘmu zavßd∞nφ DLL za b∞hu aplikace C++ Builderu, pou₧ijeme funkci Windows API LoadLibrary k zavedenφ DLL a potom pou₧φvßme funkci API GetProcAddress k zφskßnφ ukazatele na funkci, kterou chceme pou₧φt. Pozor: na zaΦßtek identifikßtor∙ jazyk C p°idßvß znak podtr₧enφ. DalÜφ informace m∙₧eme nalΘzt v SDK Win32.
    Vytvß°enφ DLL v C++ Builderu je stejnΘ jako ve standardnφm C++. ImportovanΘ funkce v k≤du musφ b²t identifikovßny modifikßtorem __declspec(dllimport) stejn∞ jako musφ b²t oznaΦeny i v jin²ch p°ekladaΦφch. Nap°. nßsledujφcφ k≤d je p°φpustn² v C++ Builderu a v ostatnφch p°ekladaΦφch C++:
    double dblValue(double);
    double halfValue(double);
    extern "C" __declspec(dllexport) double changeValue(double, bool);
    double dblValue(double value)
    {
     return value * value;
    };
    double halfValue(double value)
    {
     return value / 2.0;
    }
    double changeValue(double value, bool whichOp)
    {
     return whichOp ? dblValue(value) : halfValue(value);
    }
    Ve v²Üe uvedenΘm k≤du, funkce changeValue je exportovßna a tedy je dostupnß z volajφcφch aplikacφ. Funkce dblValue a halfValue jsou internφ a nemohou b²t volßny z vn∞jÜku DLL.
    V IDE C++ Builderu, m∙₧eme vytvo°it projekt novΘ DLL volbou File | New a na strßnce New vybereme ikonu DLL. Je otev°eno editaΦnφ okno a projektovΘ volby jsou nastaveny na vytvß°enφ DLL (namφsto EXE). PovÜimn∞te si, ₧e do zdrojovΘho souboru DLL je vlo₧en hlaviΦkov² soubor VCL.H. Pokud naÜe DLL nebude pou₧φvat komponenty VCL, pak tento °ßdek m∙₧eme odstranit.
  3. Jednou z v²hod DLL je to, ₧e DLL vytvo°enß v jednom v²vojovΘm prost°edφ m∙₧e b²t nabφdnuta k pou₧φvßnφ aplikacφm vytvo°en²m v jin²ch v²vojov²ch nßstrojφch. Kdy₧ naÜe DLL obsahuje komponenty VCL pou₧φvanΘ volajφcφ aplikacφ, pak je nutno poskytnout exportovanΘ rozhranφ, kterΘ pou₧φvß standardnφ volacφ konvence a nevy₧aduje aby volajφcφ aplikace po₧adovala pro svou prßci knihovnu VCL. K vytvo°enφ komponent VCL, kterΘ mohou b²t exportovßny pou₧φvßme b∞hovΘ balφΦky.

  4. Nap°. p°edpoklßdejme, ₧e chceme vytvo°it DLL zobrazujφcφ nßsledujφcφ jednoduchΘ dialogovΘ okno:

    HlaviΦkov² soubor tohoto formulß°e je:
    // DLLMAIN.H
    //---------------------------------------------------------------
    #ifndef dllMainH
    #define dllMainH
    //---------------------------------------------------------------
    #include <vcl\Classes.hpp>
    #include <vcl\Controls.hpp>
    #include <vcl\StdCtrls.hpp>
    #include <vcl\Forms.hpp>
    //---------------------------------------------------------------
    class TYesNoDialog : public TForm
    {
    __published:    // IDE-managed Components
        TLabel *LabelText;
        TButton *YesButton;
        TButton *NoButton;
        void __fastcall YesButtonClick(TObject *Sender);
        void __fastcall NoButtonClick(TObject *Sender);
    private:        // User declarations
        bool returnValue;
    public:         // User declarations
        virtual __fastcall TYesNoDialog(TComponent* Owner);
        bool __fastcall GetReturnValue();
    };
    // exportovanß funkce rozhranφ
    extern "C" __declspec(dllexport) bool InvokeYesNoDialog();
    //---------------------------------------------------------------
      extern TYesNoDialog *YesNoDialog;
    //---------------------------------------------------------------
    #endif
    Nßsleduje v²pis zdrojovΘho souboru dialogovΘho okna:
    // DLLMAIN.CPP
    //----------------------------------------------------------------
    #include <vcl\vcl.h>
    #pragma hdrstop
    #include "dllMain.h"
    //----------------------------------------------------------------
    #pragma resource "*.dfm"
    TYesNoDialog *YesNoDialog;
    //----------------------------------------------------------------
    __fastcall TYesNoDialog::TYesNoDialog(TComponent* Owner)
      : TForm(Owner)
    {
       returnValue = false;
    }
    //---------------------------------------------------------------
    void __fastcall TYesNoDialog::YesButtonClick(TObject *Sender)
    {
        returnValue = true;
        Close();
    }
    //---------------------------------------------------------------
    void __fastcall TYesNoDialog::NoButtonClick(TObject *Sender)
    {
        returnValue = false;
        Close();
    }
    //---------------------------------------------------------------
    bool __fastcall TYesNoDialog::GetReturnValue()
    {
        return returnValue;
    }
    //---------------------------------------------------------------
    // exportovanß funkce rozhranφ, kterß volß VCL
    bool InvokeYesNoDialog()
    {
      bool returnValue;
      TYesNoDialog *YesNoDialog = new TYesNoDialog(NULL);
      YesNoDialog->ShowModal();
      returnValue = YesNoDialog->GetReturnValue();
      delete YesNoDialog;
      return returnValue;
    }
    K≤d v tomto p°φklad∞ zobrazφ dialogovΘ okno a v soukromΘ slo₧ce returnValue ulo₧φ, kter²m tlaΦφtkem okno bylo uzav°eno. Ve°ejnß funkce GetReturnValue vracφ souΦasnou hodnotu returnValue.
    K vyvolßnφ dialogu a urΦenφ, kterΘ tlaΦφtko bylo stisknuto, volajφcφ aplikace volß exportovanou funkci InvokeYesNoDialog. Tato funkce je deklarovßna v hlaviΦkovΘm souboru jako exportovanß funkce pou₧φvajφcφ sestavenφ C (k zabrßn∞nφ komolenφ jmen a pou₧φvßnφ standardnφch volacφch konvencφ C). Funkce je definovßna ve zdrojovΘm souboru. Tφm dosßhneme to, ₧e DLL m∙₧e b²t volßna z libovolnΘ aplikace (nemusφ b²t vytvo°ena v C++ Builderu). FunkΦnost VCL vy₧adovanß DLL je sestavena do samotnΘ DLL a volajφcφ aplikace o VCL nepot°ebuje nic znßt. Kdy₧ vytvß°φme DLL, kterΘ pou₧φvajφ VCL, pak vy₧adovanΘ komponenty VCL jsou sestaveny do DLL. To zv∞tÜuje DLL a je tedy vhodnΘ do jednΘ DLL ulo₧it vφce komponent. Vytvo°te DLL s dialogov²m oknem popsan²m v tomto zadßnφ.
  5. M∙₧eme nastavit volby sestavovacφho programu pro naÜi DLL na strßnce Linker dialogovΘho okna Project Options. Na tΘto strßnce je takΘ znaΦkou mo₧no urΦit, zda bude pro naÜi DLL vytvß°ena importnφ knihovna. Pokud p°eklßdßme z p°φkazovΘho °ßdku, pak sestavovacφ program vyvolßvßme s volbou -Tpd. Nap°.

  6. ilink32 /c /aa /Tpd c0d32.obj mydll.obj, mydll.dll, mdll.map,
      import32.lib cw32mt.lib
    Pokud importnφ knihovnu nemßme, pak pou₧ijeme takΘ volbu -Gi k jejφmu vygenerovßnφ.
    Importnφ knihovnu lze takΘ vytvo°it programem IMPLIB. Nap°.
    implib mydll.lib mydll.dll
    Vφce informacφ o r∙zn²ch volbßch pro sestavovßnφ DLL a jejich pou₧φvßnφ s r∙zn²mi moduly, kterΘ jsou staticky nebo dynamicky sestavovßny s knihovnou b∞hu programu nalezneme v nßpov∞d∞.
  7. Vytvo°te aplikaci, kterß pou₧ije DLL vytvo°enou v zadßnφ 2. DLL sestavte staticky.
  8. Vytvo°te aplikaci, kterß pou₧ije DLL vytvo°enou v zadßnφ 2. Tentokrßt DLL zavßd∞jte dynamicky za b∞hu aplikace.
  9. Pro podporu VCL, C++ Builder implementuje, p°eklßdß nebo p°episuje mapovßnφ mnoha datov²ch typ∙ Object Pascalu, konstrukcφ a jazykov²ch koncepcφ do jazyka C++. Mnoho zajφmav²ch datov²ch typ∙ Object Pascalu je implementovßno v C++ Builderu pomocφ typedef na p°irozen² typ C++. Tyto definice nalezneme v sysdefs.h. Je ale vhodn∞jÜφ pou₧φvat p°irozen² typ C++ namφsto typu Object Pascalu (nap°. je sice mo₧no pou₧φvat datov² typ Integer, ale je vhodn∞jÜφ pou₧φvat int).

  10. N∞kterΘ datovΘ typy Object Pascalu a jazykovΘ konstrukce, kterΘ nejsou v C++, jsou implementovßny jako t°φdy nebo struktury. N∞kdy jsou takΘ pou₧ity Üablony t°φd (nap°. Üablona set pro implementaci datovΘho typu mno₧ina). Tyto deklarace nalezneme v hlaviΦkov²ch souborech: dstring.h, sysdefs.h, variant.hpp a wstring.h.
    Parametry volanΘ odkazem a netypovΘ parametry Object Pascalu nejsou p°irozenΘ v C++. C++ i Object Pascal majφ koncepci p°edßvßnφ parametru odkazem. Jsou to modifikovatelnΘ parametry. Syntaxe v Object Pascalu je:
    procedure myFunc(var x : integer);
    V C++ lze p°edßvat tento typ parametr∙ odkazem:
    void myFunc(int& x);
    Parametry p°edßvanΘ odkazem a ukazatelem v C++ mohou b²t pou₧ity k modifikaci objektu. Odkaz C++ neodpovφdß p°esn∞ parametru p°edßvanΘmu odkazem Object Pascalu, ale oboje umo₧≥uje m∞nit hodnotu odkazu.
    Object Pascal mß takΘ parametry nespecifikovanΘho typu. Tyto parametry jsou p°edßny funkci s nedefinovan²m typem. Funkce musφ p°etypovat parametr na znßm² typ p°ed pou₧itφm parametru. C++ Builder interpretuje netypovΘ parametry jako ukazatel na void. Funkce musφ p°etypovat ukazatel na void na ukazatel na po₧adovan² typ. Nap°.
    int myfunc(void* MyName)
    {
     // P°etypovßnφ ukazatele na po₧adovan² typ; potom jeho dereference
     int* pi = static_cast<int*>(MyName);
     return 1 + *pi;
    }
  11. Object Pascal mß konstrukci "otev°enΘ pole", kterß umo₧≥uje p°edßvat funkcφm pole nespecifikovanΘ velikosti. V C++ toto m∙₧e b²t vy°eÜeno p°edßnφm ukazatele na prvnφ prvek pole a hodnotou poslednφho indexu pole (poΦet prvk∙ pole mφnus jedna). Nap°. funkce Mean v math.hpp mß v Object Pascalu deklaraci:

  12. function Mean(Data: array of Double): Extended;
    Deklarace C++ je:
    Extended __fastcall Mean(const double * Data, const int Data_Size);
    Nßsledujφcφ k≤d ilustruje volßnφ tΘto funkce v C++.
    double d[] = { 3.1, 4.4, 5.6 };
    // explicitnφ specifikace poslednφho indexu
    long double x = Mean(d, 2);
    // lepÜφ: pou₧itφ sizeof pro v²poΦet poΦtu prvk∙ pole
    long double y = Mean(d, (sizeof(d) / sizeof(d[0])) - 1);
    // pou₧itφ makra z sysdefs.h
    long double z = Mean(d, ARRAYSIZE(d) - 1);
    Kdy₧ pou₧φvßme sizeof, makro ARRAYSIZE nebo makro EXISTINGARRAY k v²poΦtu poΦtu prvk∙ v poli, pak nesmφme pou₧φt ukazatel na prvnφ prvek pole, ale je nutno p°edat jmΘno pole:
    double d[] = { 3.1, 4.4, 5.6 };
    ARRAYSIZE(d) == 3;
    double *pd = d;
    ARRAYSIZE(pd) == 0; // Chyba!
    sizeof pole neodpovφdß sizeof ukazatele. Nap°. p°i deklaraci:
    double d[3];
    double *p = d;
    zφskßme velikost pole v²razem:
    sizeof(d)/sizeof d[0]
    ale pou₧itφ nßsledujφcφho v²razu je chybnΘ
    sizeof(p)/sizeof(p[0])
    Object Pascal umo₧≥uje funkcφm p°edßvat nepojmenovanß doΦasnß otev°enß pole. V C++ pro toto neexistuje syntaxe (je mo₧no to vy°eÜit n∞kolika p°φkazy a pojmenovßnφm pole). Zßpis v Object Pascalu
    Result := Mean([3.1, 4.4, 5.6]);
    lze v C++ zapsat jako
    double d[] = { 3.1, 4.4, 5.6 };
    return Mean(d, ARRAYSIZE(d) - 1);
    K omezenφ rozsahu pojmenovanΘho doΦasnΘho pole je mo₧no pou₧φt zßpis:
    long double x;
    {
     double d[] = { 4.4, 333.1, 0.0 };
     x = Mean(d, ARRAYSIZE(d) - 1);
    }
    Object Pascal podporuje jazykovou koncepci nazvanou array of const. Tento typ parametru je stejn² jako p°edßvßnφ otev°enΘho pole TVarRec hodnotou. Nap°. segment k≤du Object Pascalu:
    function Format(const Format: string; Args: array of const): string;
    V C++ se jednß o nßsledujφcφ prototyp:
    AnsiString __fastcall Format(const AnsiString Format;
               TVarRec const *Args, const int Args_Size);
    Funkce je volßna jako libovolnß jinß funkce, kterß p°ebφrß otev°enΘ pole:
    void show_error(int error_code, AnsiString const &error_msg)
    {
      TVarRec v[] = { error_code, error_msg };
      ShowMessage(Format("%d: %s", v, ARRAYSIZE(v) - 1));
    }
    Makro OPENARRAY definovanΘ v sysdefs.h m∙₧e b²t pou₧ito jako alternativa k pou₧itφ pojmenovanΘ prom∞nnΘ pro p°edßnφ doΦasnΘho otev°enΘho pole funkci, kterß p°ebφrß otev°enΘ pole hodnotou. Pou₧itφ makra vypadß takto:
    OPENARRAY(T, (value1, value2, value3)) // a₧ 19 hodnot
    kde T je typ vytvß°enΘho otev°enΘho pole. Nap°.
    void show_error(int error_code, AnsiString const &error_msg)
    {
     ShowMessage(Format("%d: %s",OPENARRAY(TVarRec,error_code,error_msg)));
    }
    Makro OPENARRAY m∙₧e b²t pou₧ito pro p°edßnφ a₧ 19 hodnot. Pro v∞tÜφ pole musφ b²t definovßna explicitnφ prom∞nnß.
    Makro EXISTINGARRAY definovanΘ v sysdefs.h m∙₧e b²t pou₧ito k p°edßnφ existujφcφho pole, kdy₧ je oΦekßvßno otev°enΘ pole. Pou₧itφ makra vypadß takto:
    long double Mean(const double *Data, const int Data_Size);
    double d[] = { 3.1, 3.14159, 2.17128 };
    Mean(EXISTINGARRAY (d));
  13. N∞kterΘ typy jsou v Object Pascalu a v C++ definovßny r∙zn∞. Jednß se o logickΘ datovΘ typy a typ char. S odchylkami v t∞chto typech se nynφ seznßmφme.

  14. True pro datovΘ typy ByteBool, WordBool a LongBool Object Pascalu je reprezentovßno hodnotou -1 a False hodnotou 0. Ostatnφ logickΘ datovΘ typy majφ pro True hodnotu 1 (False je v₧dy 0). C++ p°evßdφ tyto typy sprßvn∞, ale problΘmy mohou nastat p°i pou₧φvßnφ funkcφ Win API.
    Typ char je v C++ znamΘnkov² typ, zatφmco v Object Pascalu se jednß o bezznamΘnkov² typ. Tato odchylka m∙₧e n∞kdy zp∙sobit problΘmy p°i sdφlenφ k≤du.
  15. Jestli₧e mßme k≤d v jednotce Pascalu, kter² pou₧φvß zdroje °et∞zc∙, pak p°ekladaΦ Pascalu (DCC32) generuje globßlnφ prom∞nnou a odpovφdajφcφ makro preprocesoru pro ka₧d² zdroj °et∞zce p°i generovßnφ hlaviΦkovΘho souboru. Makra jsou pou₧φvßny k automatickΘmu zavßd∞nφ zdroj∙ °et∞zc∙ a jsou urΦena pro pou₧itφ v naÜem C++ k≤du na mφstech, kde se odkazujeme na zdroje °et∞zc∙. Nap°. sekce resourcestring v k≤du Object Pascalu m∙₧e obsahovat:

  16. unit borrowed;
    interfaceS
    resourcestring
     Warning = 'Be careful when accessing string resources.';
    implementation
    begin
    end.
    Odpovφdajφcφ k≤d generovan² p°ekladaΦem Pascalu pro C++ Builder bude:
    extern System::Resource ResourceString _Warning;
    #define Borrowed_Warning System::LoadResourceString(&Borrowed::_Warning)
    To umo₧≥uje pou₧φt exportovanΘ zdroje °et∞zc∙ Object Pascalu bez nutnosti explicitnφho volßnφ LoadResourceString.
  17. P°ekladaΦ Pascalu nynφ akceptuje implicitnφ parametry pro kompatibilitu s rozliÜovßnφm konstruktor∙ C++. Na rozdφl od C++, konstruktory Object Pascalu majφ stejn² poΦet a typy parametr∙. K rozliÜenφ konstruktor∙ v Object Pascalu jsou pou₧ity prßzdnΘ parametry (p°i generovßnφ hlaviΦkov²ch soubor∙ C++). Nap°. pro t°φdu TInCompatible, jsou v Object Pascalu konstruktory:

  18. constructor Create(AOwner: TComponent);
    constructor CreateNew(AOwner: TComponent);
    kterΘ jsou nesprßvn∞ (nelze je rozliÜit) p°elo₧eny na tyto konstruktory v C++:
    __fastcall TInCompatible(Classes::TComponent* Owner); // Create
    __fastcall TInCompatible(Classes::TComponent* Owner); // CreateNew
    P°i pou₧itφ implicitnφch parametr∙ v Object Pascalu lze nap°. vytvo°it konstruktory:
    constructor Create(AOwner: TComponent);
    constructor CreateNew(AOwner: TComponent; Dummy: Integer = 0);
    kterΘ je mo₧no ji₧ sprßvn∞ p°elo₧it na:
    __fastcall TCompatible(Classes::TComponent* Owner); // Create
    __fastcall TCompatible(Classes::TComponent* Owner, int Dummy); // CreateNew
  19. Object Pascal mß jazykovΘ konstrukce pracujφcφ s RTTI (identifikace typu za b∞hu). N∞co obdobnΘho je i v C++. Nap°. konstrukce Object Pascalu

  20. if Sender is TButton...
    je mapovßna v C++ na
    if (dynamic_cast <TButton*> (Sender))...
    Obdob∞
    b := Sender as TButton;
    je mapovßno na
    TButton& ref_b = dynamic_cast <TButton&> (*Sender);
    DalÜφ metody t²kajφcφ se RTTI jsou zaobaleny v TObject, nap°. metody ClassNameIs, ClassName, ClassParent apod.
  21. Star² 6-ti slabikov² formßt pohyblivΘ °ßdovΘ Φßrky Object Pascalu se nynφ naz²vß Real48. V C++ nenφ ekvivalent pro tento typ. Nelze tedy pou₧φt k≤d Object Pascalu s tφmto k≤dem v k≤du C++.

  22. V Object Pascalu funkce mohou vracet pole. Nap°. syntaxe pro funkci GetLine vracejφcφ 80 znak∙ je:
    type Line_Data = array[0..79] of char;
    function GetLine: Line_Data;
    V C++ funkce nemohou vracet pole. VCL nemß ₧ßdnou vlastnost, kterß je polem, i kdy₧ to Object Pascel umo₧≥uje. Proto₧e vlastnosti mohou pou₧φvat Φtecφ metody, kterΘ vracejφ hodnoty typu vlastnosti, nenφ v C++ Builderu vlastnost typu pole.
20. Pou₧φvßnφ DLL