Programozás | C / C++ » Marossy Kálmán - COM komponensek létrehozása és használata

Alapadatok

Év, oldalszám:2002, 7 oldal

Nyelv:magyar

Letöltések száma:1429

Feltöltve:2004. július 03.

Méret:114 KB

Intézmény:
-

Megjegyzés:

Csatolmány:-

Letöltés PDF-ben:Kérlek jelentkezz be!



Értékelések

Nincs még értékelés. Legyél Te az első!


Tartalmi kivonat

COM komponensek létrehozása és használata COM alapfogalmak Komponens, coclass, interfész A COM komponensek olyan önálló egységek, melyek bináris formában jelennek meg, rendszerint egy dll vagy egy exe fájl formájában. Egy komponens tulajdonképpen több coclass (COM osztály) összefogása. Egy coclass több interfészt is megvalósíthat. Ha egy coclass egy interfészt megvalósít, az a coclass valamilyen képességét jelenti. Egy coclass nagyon hasonló egy objektum-orientált szemléletmódban megszokott osztályhoz. Az interfészeknek metódusokat definiálhatunk, amivel valamilyen működés váltható majd ki. Az interfész csak egy definíció, amihez az összes őt megvalósító osztálynak ragaszkodnia kell. Az interfész leírja, hogy minek kell történnie, míg hogy az ténylegesen hogyan megy majd végbe, azért az implementáció a felelős. Az interfészek, a coclassok azonosítására egy 128 bites szám szolgál, mely egy teljesen egyedi azonosító,

vagy GUID (Globally Unique IDentifyer). Egy ilyen GUID generálásakor figyelembe vett dolgok következtében gyakorlatilag kizárt, hogy két különböző objektum azonosítója megegyezzen. Metódusok visszatérési értéke A COM interfészek metódusai rendszerint egy HRESULT értékkel térnek vissza. A HRESULT egy 32-bites szám, mely a metódus futásának sikerességéről ad információt. Három részből áll. Az első bit a severity kód, amely a hívás sikerességéről ad tájékoztatást A facility kód (16-28. bit) megadja, hogy a hibát melyik alrendszer (például RPC) érzékelte A status kód (első 16 bit) pedig a hiba fajtáját jelzi. HRESULT értékek feldolgozására és konstruálására számos makró lehet segítségünkre. IDL és TLB A COM interfészeket valamilyen programozási nyelvtől független módon kell definiálni, hogy a komponensek készítésének és használatának programozási nyelve független tudjon lenni. Erre az egyik

lehetőség az IDL (Interface Definition Language) használata. Az interfészünk IDL nyelvű megfogalmazása után egy IDL fordítóval tudjuk azt a többi programnyelv által használható formába konvertálni. Egy másik lehetőség, ha valamilyen szabványos bináris formában tároljuk az interfésszel kapcsolatos információkat. Ez COM-ban a Type Library, vagy típuskönyvtár, és vagy egy különálló tlb fájl tartalmazza, vagy maga a komponens. Az IDL nyelvű leírásból a MIDL fordíró mind C és C++ nyelvű leírást, mind egy tlb fájlt generál. IUnknown Az IUnknown interfész olyan fontos alapszolgáltatásokat definiál, amit minden COM komponensnek biztosítani kell tudni. A QueryInterface függvény segítségével egy komponenstől megkérdezhetjük, hogy egy adott interfészt támogat-e. Ha a komponens támogatja az adott interfészt, akkor arra egy mutatón keresztül visszaad egy mutatót a kért interfészére Egy COM komponens saját maga tartja nyilván,

hogy hányan használják, vagyis, hogy hány használatban lévő interfészpointer van még, ami a komponens valamely interfészére mutat. Ha már egy sincs, a komponens kitöltődhet a memóriából. Éppen ezért a kliensnek minden interfészpointerre legelőször az AddRef metódust kell meghívnia, majd amikor nem használ egy adott interfészpointert, meg kell hívnia a Release metódust. A QueryInterface-szel kapott interfészpointerre már egyszer meg van hívva az AddRef, úgy kapjuk meg. Ha viszont egy interfészpointert másolunk, vagy függvénynek paraméterként adjuk át, akkor nekünk kell gondoskodni a meghívásáról. A Release metódus megfelelő számú hívásáról is gondoskodni kell. Komponens aktiválása Egy in-process komponens tulajdonképpen egy Windows dll. Felvetődhet bennünk az a kérdés, hogy bár a QueryInterface-től kaphatunk egy pointert valamilyen interfészre, honnan kapunk pointert az IUnknown interfészre. A kérdés megoldásához minden

coclassnak szüksége van egy úgynevezett classobjectre, ami az osztály létrehozásáért felelős. A classobjectet kérhetjük meg tehát, hogy hozza létre a coclass egy példányát. Mivel egy komponens több coclasst is tartalmazhat, ezért kell még egy szabályos út, hogy létre tudjunk hozni egy adott coclass classobjectjét. Tehát a komponenst tartalmazó dll betöltése után meg kell hívnunk egy olyan függvényt, ami létrehozza egy adott komponens classobjectjét. Ennek a függvénynek a neve DllGetClassObject, és minden COM dll-nek exportálnia kell. A függvényhívás eredményeként egy kért interfészpointert kaphatunk vissza az adott coclass classobjectjére. A classobject valamely létrehozó metódusával keletkezik tehát a kért coclassból egy példány, és innen kapunk egy interfészpointert vissza – legtöbbször ez a sokat keresett IUnknown interfészpointerének forrása. Persze nem szabad majd elfelejteni a kapott interfészpointerekre

meghívni a Release() metódust. Van egy standard út egy classobject létrehozására. Mégpedig az, hogy olyan komponenst hozunk létre, amely implementálja az IClassFactory interfészt. Így a kliensnek leegyszerűsödik a dolga egy coclass példányának létrehozásához. A COM ugyanis biztosít egy segítő függvényt, a CoCreateInstance(Ex)-t, mely az IClassFactory létezését veszi alapul. Saját aktivációs interfész megvalósítására csak ritkán van szükség. IDispatch, duális interfészek Az interfész lehetővé teszi az egyszerűbb programokból a komponens bizonyos funkcióinak elérését. Régebben volt olyan elgondolás, hogy ha egy függvényünket elérhetővé tesszük az IDispatch interfészen keresztül, akkor már nincs is szükség külön interfészben elérhetővé tenni. Ez azt eredményezte, hogy az összes metódus ebben az interfészben foglalt helyet, valamint hogy a C/C++ kliensek nem tudták kihasználni a gyors hívás lehetőségét. Éppen

ezért manapság az az elfogadott, hogy definiáljunk minden külön interfészünket ténylegesen külön interfészben, készítsünk hozzájuk type library-t, de ha lehet, a script nyelvek támogatására implementáljuk az IDispatch interfészt is. Ebben nem is szükséges az összes funkciónak szerepelnie, ami a komponensből elérhető, a lényeg, hogy a fő funkciók szélesebb körből elérhetők legyenek. Ezek után a C/C++ kliensek használhatják a midl header fájljait, a VB/Java kliensek a tlb-t, a script nyelvek pedig az IDispatch interfészen keresztül érhetik el komponensünket. Komponens létrehozása Visual C++-ban ATL segítségével A rövid bevezető után készítsünk el egy komponenst Visual C++-ban. Ehhez el kéne készíteni a komponens idl nyelvű leírását, ebből az idl segítségével generálni a szükséges fájlokat. Létre kellene hozni egy dll-t a szükséges függvényekkel. Implementálni az IUnknown-t, IDispatch-et, és persze a saját

interfészeinket is. Ebben egy csomó jól automatizálható tevékenység van, amit nem szeretnénk minden egyes COM komponensünk létrehozásakor elvégezni (pl. QueryInterface megvalósítása) Ezeket a feladatokat bátran bízhatjuk néhány előre elkészített template-re és makróra. Ekkor lehet segítségünkre az ATL (Active Template Library). Project létrehozása, AppWizard Az előzőek tükrében könnyen készíthetünk COM komponenst, még C++-ban is. Visual C++-ban hozzunk létre új projectet, az „ATL COM AppWizard” segítségével! Válasszuk a DLL-ben való megvalósítás opcióját. Így keletkezik egy project, ami egy olyan dll-t hoz létre, ami megvalósítja és exportálja mindazokat a függvényeket, amik egy COM komponenshez szükségesek. Új coclass létrehozása Csináljunk ebbe egy új coclasst, egy új „default” interfésszel. Ezt legkönnyebben az ATL Object Wizard segítségével tehetjük meg, abból is válasszuk most a Simple

Objectet. Itt a Names részben beállíthatjuk a különböző neveket: az interfész neve, a coclass neve, a coclassnak megfelelő C++ osztály neve, a ProgID neve. A ProgID egy olyan azonosító, amellyel később röviden hivatkozhatunk a komponensünkre. Alapértelmezés szerint a komponens neve, egy „.”, majd a coclass neve Az Attributes részben különböző szálkezeléssel kapcsolatos dolgokat, a duális interfészek támogatását, az aggregáció (egy bizonyos fajta COM komponens újrahasznosítás mód) támogatását állíthatjuk többek között, kezdetben hagyjuk meg az alapértelmezés szerinti beállításokat. Végeredményül kapunk egy C++ osztályt, amely más, az ATL által előre definiált osztályokból örököl. Bizonyos makrók is el vannak helyezve a generált kódban, amik a megvalósított interfészekre vonatkoznak például. Az idl fájlban pedig létrejön a coclass, az interfész definíciója. Láthatjuk, COM osztályunk default interfészét

is. Új interfészek hozzáadása Egy coclass azonban több interfészt támogathat. Adjunk tehát az idl fájlhoz még egy interfészt, mely az IUnknown-ból származik, példának használhatjuk az ATL Object Wizard által generáltat. Adjuk hozzá a coclass definíciójához ezt az interfészt is A project lefordítása után a COM osztályunkra kiválaszthatjuk az Implement Interface menüpontot, ahol kiválaszthatjuk az újonnan létrehozott interfészt. Látható, eggyel több osztályból örökölünk, és eggyel több COM INTERFACE ENTRY bejegyzés is került a korábban említett makrók közé. Metódusok hozzáadása Már nincs más hátra, mint a megfelelő interfészekhez a metódusok hozzáadása, azok implementálása, a project lefordítása, majd a COM komponensünket bárki használhatja egy regisztráció után. A Class View-ban kinyitva a coclass megvalósító osztályt, láthatjuk is a két interfészt. Bármelyikre kiválaszthatjuk az Add Method opciót. A

név, és a paraméterek beírása után az hozzáadódik mind az idl fájlhoz, mind a C++ fejléc fájlhoz, valamint létrejön egy üres implementáció is, amelyet már csak ki kell tölteni. Ezt hagyjuk későbbre, mert a példa kedvéért egy COM osztályt fogunk felhasználni az implementációban. Komponens használata Visual C++-ban Mit is kell csinálni, hogy egy komponenst használni tudjunk C++-ból? Először is hozzá kell adni a projectünkhöz egy idl fordító által generált fejlécfájlt, mely az interfész definícióját tartalmazza. Kódunkban inicializálni kell a COM alrendszert a CoInitialize függvény meghívásával, majd (egyszerűbb esetben) létre kell hozni a komponensből egy példányt a CoCreateInstance segítségével. Ekkor kapunk egy pointert a kért interfészre Ha további interfészeket szeretnénk használni, meg kell hívnunk a QueryInterface-t, ha más helyen is használjuk az interfészpointert (például átadjuk egy függvénynek

paraméterül), akkor meg kell hívnunk az AddRef, Release függvényeket a megfelelő helyen. Ez elég bonyolult, és nehézkessé tenné a COM komponensek használatát, lássuk, hogyan lehet könnyíteni ezeken a feladatokon. Import direktíva Az idl és az idl fordító használatát helyettesítheti, ha a típus könyvtárakra támaszkodunk az interfészek leírásához. A Visual C++ fordító támogat egy ún #import direktívát, amely a megadott típuskönyvtárból (vagy ha a komponenshez hozzá van fűzve a típuskönyvtár, akkor közvetlenül a komponensből) készítsen az adott komponens egyszerű használatához szükséges definíciókat. SmartPointerek Az interfészpointerek használatát jelentősen egyszerűsítheti, ha nem közvetlenül használjuk őket, hanem egy csomagoló osztályon keresztül. Ez az osztály például konstruktorában meghívhatná a CoCreateInstance-t, castoláskor meghívhatná a QueryInterface-t, másoló konstruktorában az AddRef-et,

destruktorban a Release-t. Egy ilyen osztályra bízhatnánk a nem kívánatos, részeit a COM-nak. Az import direktíva használatakor létrejönnek ilyen osztályok, SmartPointerek, de mi is készíthetünk magunknak a COM SMARTPTR TYPEDEF makró segítségével akár. Az import egyéb tevékenységei Az import a megadott tlb-hez kapcsolódó típusokhoz elkészíti a definíciót, és hozzáadja egy namespace-hez, melyet át is nevezhetünk. Ezen kívül a metódusoknak két változatát készíti el, az egyik a „igazi” visszatérési értéket ad vissza, és hiba esetén kivételt dob, a másik HRESULT-ot ad vissza. Szabályozhatjuk, hogy melyik készüljön el, és mi legyen a neve, alapbeállításként a HRESULT-ot visszadó változat neve egy „raw ” előtagot kap. Komponens használata Visual Basicben Visual Basic-ben még a C++ import direktívájának használatánál is egyszerűbb dolgunk van COM komponens használatának esetén. Több lehetőségünk is

van, nézzük sorra őket As Object Az első lehetőség, ha létrehozunk egy változót, melynek a típusa „object”, majd beállítjuk egy CreateObject hívás segítségével, melynek paramétere a ProgID. Ezután használhatjuk a komponensünk default interfészének metódusait. As Coclass Valamivel egyszerűbb a használat, ha az Add References menüpont kiválasztása után bepipáljuk a regisztrált komponensünk típuskönyvtárának megfelelő sort. Ekkor nincs szükség CreateObject-re, egyszerűen csak a coclass nevét adjuk a változó típusának. Hátránya, hogy csak lokális komponenseket használhatunk így, míg az előbbi esetben remote komponenseket is. As Interface Eddig csak azt láttuk, hogy egy komponensnek Visual Basic-ből csak egy, mégpedig a „default” interfészét tudjuk használni. Hogy tudjuk elérni a többi interfészt, hogy hívjuk meg a QueryInterface-t? Mi igazából csak közvetett módon tudjuk meghívni azt, a VB runtime gondoskodik az

IUnknown megfelelő használatáról. Közvetett módon pedig úgy tudjuk elérni, hogy létrehozunk az Add References után egy változót, az interfész típusának megfelelően. Ezt a változót utána egyenlővé tesszük a korábban (például New-val) létrehozott objektummal, és ha a kért típus nem a default interfész, akkor a QueryInterface „meghívódik”. Komponens használata szkript nyelvekben A VBScript illetve a JScript nyelvekben minden változó object. Ezért a CreateObject-ettel kell létrehozni a használandó COM komponenst, és csak a default interfészének IDispatch-en keresztüli részét lehet elérni. Feladat A laboron feladat lesz egy COM komponens elkészítése, mely ADO-n keresztül ér el egy megadott adatbázist. Az elkészített COM komponenshez egy Visual Basic kliens megírása is feladat. Kiegészítő irodalom Aki mélyebb részletességgel szeretné megismerni a COM-ot, annak álljék itt egy alapvető mű: Eddon, G.-Eddon, H: Inside

COM+ Base Services, Microsoft Press, 1999 A mérési segédletet összeállította: Marossy Kálmán (coloman@avalon.autbmehu)