Back to site
Since 2004, our University project has become the Internet's most widespread web hosting directory. Here we like to talk a lot about web development, networking and server security. It is, after all, our expertise. To make things better we've launched this science section with the free access to educational resources and important scientific material translated to different languages.

Interfejs kod skripting klijenta

 

Predsatavljanje višebrojnih interfejsa kod

skripting klijenata

Ova stranica predstavlja pokušaj da se razume kolektivna svest o COM zajednici. Naravno, svaka povratna informacija je vise nego dobrodosla (ko sam ja da budem glas predstavnik celokupne COM zajednice?). Jednom kada se sve ovo sredi, nadam se da nikada vise neću morati da uklapam IDispatch nanovo (imajte COM+ sada, i ja cu vam pokazati kako!). Pokušaćemo da vam odgovorimo na sledeće pitanja: "Na koji način izlazem višebrojni interfejs kodiranim klijentima?" Zašto se o tome uopšte raspravlja? Jer je IDispatch limitiran? Ne. Problem je jer trenutni skriptovani klijenti ne podrzavaju QI.

Pokušaćemo da vam odgovorimo na sledeće pitanja:

"Na koji način izlazem višebrojni interfejs kodiranim klijentima?"

Zašto se o tome uopšte raspravlja? Jer je IDispatch limitiran? Ne. Problem je jer trenutni skriptovani klijenti ne podrzavaju QI. Na osnovu mojih opservacija sa ove liste i mog celokupnog razgovaranja sa članovima COM zajednice, vidim nekoliko rešenja tog problema:

1. Ne pokušavajte da prikažete funkcionalnost višebrojnog interfejsa klijentima.

Ovo je moja oiginalna tehnika koju mi je predložio Keith Brown. On je predložio korišćenje IDispatch koji je modelirao celokupnu funkcionalnost obkjekata na višem nivou nego što je to individualni interfejs, npr:

interface IRect : IUnknown
{
    HRESULT GetCoords([out] long* pleft, [out] long* ptop,
                      [out] long* pright, [out] long* pbottom);

    HRESULT SetCoords([in] long left, [in] long top,
                      [in] long right, [in] long bottom);
}

interface I2DObject : IUnknown
{
    HRESULT Inflate([in] long cx, [in] long cy);
    HRESULT Translate([in] long cy, [in] long cy);
}

// For scripting clients only
[ hidden, dual ]
interface _IRectangle : IDispatch
{
    [propput] HRESULT Left([in] long left);
    [propget] HRESULT Left([out, retval] long* pleft);
    ...
    HRESULT Inflate([in] long cx, [in] long cy);
    HRESULT Translate([in] long cy, [in] long cy);
}

An implementation would use the dual strictly for IDispatch purposes, i.e. vtbl-binding clients wouldn't use _IRectangle:

class CRectangle : ...,
    public IRect,
    public I2DObject,
    public IDispatchImpl<_IRectangle, &IID__IRectangle,
                         &LIBID_SHAPESLib>
{
public:
BEGIN_COM_MAP(CRectangle)
    COM_INTERFACE_ENTRY(IRect)
    COM_INTERFACE_ENTRY(I2DObject)
    COM_INTERFACE_ENTRY(IDispatch)
    // No entry for _IRectangle
END_COM_MAP()

    // IRect methods
    ...

    // I2DObject methods
    ...

    // _IRectangle methods
    ...

};

Pošto više ne koritimo programiranje bazirano na interfejsu, ova tehnika nam omogućava potpunu kontrolu kod implementiranja jdnog interfejsa kod pisanog klijenta da bi se videlo da w/o više ne mora da brine o mapiranju svake metode interfejsa. Ako striktno koristimo dual kao način implementiranja IDispatcha, a ne njegovog predstavljanja, vaša IDispatch implementacija može da se razvija kao i vaša funkcionalnost, npr. korišćenje nekog drugog dual w/ IID. Iako je još uvek moguće da savvy developer zgrabi dual i pokuša da se implementira ili da se izvede i svega, jedan udarac je dovoljan da obeshrabi takvo ponašanje.

Nažalost, niko ne voli ovu tehniku jer ih ona tera da obezbede posebnu implementaciju za klijente (koja, između ostalog, mora svakako da se odradi...).

2. Koristite C++ MI trik.

Ako imate n interfejs koji je već [oleautomation] kompatibilan, možete definisati dual interfejs kod IDL što jeste povezivanje svih metoda svih interfejsa koje vi želite da predstavite metodama i da dozvolite C++ kompajleru da se poklopi sa vtbls za vas.

[ oleautomation ]
interface IRect : IUnknown
{
    HRESULT GetCoords([out] long* pleft, [out] long* ptop,
                      [out] long* pright, [out] long* pbottom);

    HRESULT SetCoords([in] long left, [in] long top,
                      [in] long right, [in] long bottom);
}

[ oleautomation ]
interface I2DObject : IUnknown
{
    HRESULT Inflate([in] long cx, [in] long cy);
    HRESULT Translate([in] long cy, [in] long cy);
}

[ hidden, dual ]
interface _IRectangle
{

    // Copied from IRect
    HRESULT GetCoords([out] long* pleft, [out] long* ptop,
                      [out] long* pright, [out] long* pbottom);

    HRESULT SetCoords([in] long left, [in] long top,
                      [in] long right, [in] long bottom);

    // Copied from I2DObject
    HRESULT Inflate([in] long cx, [in] long cy);
    HRESULT Translate([in] long cy, [in] long cy);

}

Implementacija koja je izvedena iz svih interfejsa i implementacija zajednice svih metoda. C++ kompajler će popuniti vtbl unose za dual interfejs korišćenjem metoda koje su već implementirane za vaš ne dual interfejs, npr.

class CRectangle :
    ...,
    public IRect,
    public I2DObject,
    public IDispatchImpl<_IRectangle, &IID__IRectangle,
                         &LIBID_SHAPESLib>

{
public:

BEGIN_COM_MAP(CRectangle)
    COM_INTERFACE_ENTRY(IRect)
    COM_INTERFACE_ENTRY(I2DObject)
    COM_INTERFACE_ENTRY(IDispatch)
    // No entry for _IRectangle
END_COM_MAP()

    // IRect methods
    ...

    // I2DObject methods
    ...

    // _IRectangle methods already implemented!

};

Ova metoda dozvoljava vašim klijentima da imaju pristup union metodama svih interfejsa koje ste vi kopiralii prebacili na vaš dual interfejs. Ipak, sada ste ostavljeni sa kopijom 'n' paste dance kod IDL i nema načina da rešite imenovani konflikt, npr. IRect i I2DObject oba imaju metodu Foo() koja je naznačena za različite stvari. Konačno, ta metoda takođe predstavlja dual kod TypeLib za savvy razvoj (pogledajte pomenuta forehead-slap rešenja).

3. Koristite typeinfo-driven implementaciju IDispatch koja obezbedjuje zajednicu metoda pisanog interfejsa.

Daniel Sinclair obezbeđuje drugu implementaciju ove tehnike nazvanu MultiDual opisanu u ovom online ReadMe.
Herbert Carroll pruža ,sličnu implementciju,koja nije bazirana na typeinfo.
Kjell Tangen Kjell Tangen obezbeđuje drugo rešenje za implementaciju proširene verzije CComTypeInfoHolder.

Imagine the following IDL:

[ oleautomation ]
interface ICowboy : IUnknown
{
    HRESULT Draw([in] long nSpeed); // Conflicts with IArtist::Draw
    HRESULT Shoot();                 // Conflicts with ICurse::Shoot
}

[ oleautomation ]
interface IArtist : IUnknown
{
    HRESULT Draw([in] long nStrokes); // Conflicts with ICowboy::Draw
    HRESULT Paint();
}

[ oleautomation ]
interface ICurse : IUnknown
{
    HRESULT Shoot(); // Conflicts with ICowboy::Shoot
    HRESULT Darn();
}

library TURNOFTHECENTURYLib
{
    coclass AcePowell
    {
        interface ICowboy;
        [default] interface IArtist;
        interface ICurse;
    }
}

The client would like to write code like this:

<script language=vbscript>

    ace.paint    ' unambiguously IArtist::Paint
    ace.draw 100 ' resolved to IArtist::Draw because
                 'IArtist is [default]

    ace.darn    ' unambiguously ICurse::Darn
    ace.shoot   ' resolved to ICowboy::Shoot because ICowboy
                ' comes before ICurse in the coclass statement

    ace.icurse_shoot    ' resolved to ICurse::Shoot because of prefix
    ace.icurse_darn     ' prefix unnecessary but still works

</script>

Ta typelib-based table-driven implementacija koristi coclass izjavu da bi se poklopila sa pozivima GetIDsOfNames i Invoke odgovarajućoj interfejs definiciji, radeći malo pre procesuiranja koje dozvoljava obezbeđivanje non-colliding DISPID za nedefinisane interfejs metoda i rukovanje prefiksima koji imaju potpun performans date rezolucije.

Zato što je implementacija u potpunosti table-deriven bazirana na coclass izjavi, aggregatable implementacija IDispatch koja daje ovo amalgam ponašanje može biti implementirana, a to zavisi od objekta koji je izložen IProvideClassInfo. Zapravo, članovi sa ove liste su obezbedili implementaciju slične tehnike w/o, ako ne grešim, imenovana rezolucija jeste šema koju sam ja malopre, gore predložio. Ako preuzmemo takvu implementaciju IDispatch, objektna implementacija će izgledati ovako:

class CAcePowell :
    ...,
    public IProvideClassInfo
{

BEGIN_COM_MAP(CAcePowell)
    // Appropriate entries for nested composition of
    // ICowboy, IArtist and ICurse

    COM_INTERFACE_ENTRY_AUTOAGGREGATE(IID_IDispatch, m_spunkDisp.p,
                                      CLSID_StdAmalgamDispatch)

    COM_INTERACE_ENTRY(IProvideClassInfo)
END_COM_MAP()

    ...

private:
    CComPtr<IUnknown> m_spunkDisp;
};

Imajte na umu: Ako pretpostavimo da je implementacija izložila isti DISPID za metode i vlasništva datih interfejsa, rani klijenti koji su se prijavili biće podjednako zadovoljni kao i oni koji su tu došli kasnije. I, pošto, ni jedan dual interfejs nije tu da definiše implementaciju IDispatch, nema razloga za brigu oko nalaženja direktne definicije dual interfejsa (iako je greška dozvoljena onima koji pokušavaju).

4. Upotreba typeinfo-driven implementacije IDispatch koja pruža kolekciju objekata koji implementiraju pisani interfejs.

Don i ja smo implmentirali tehniku Timova kuća. Dostupna je ovde.Takođe, ažurirao je Serge Sandler za VC6 i za kompatibilni Win9x.

Ako pretpostvimo da je dole prikazani AcePowell IDL, ono što klijenti žele da napišu, onda je to dati kod.

<script language=vbscript>

    ace.paint    ' top level object has all methods of
                 ' [default] interface

    dim artist
    set artist = ace.iartist ' Can "QI" for specific interface
    artist.draw 100

    dim curse
    set curse = artist.icurse ' Simulated QI
    curse.darn

    dim cowboy
    set cowboy = curse.icowboy ' Simulated QI
    cowboy.shoot

    curse.shoot
    curse.darn

</script>

Kao što možete da vidite mi predstavljamo kolekciju objekata gde najbolji objekti imaju metode i vlasništva [datog] interfejsa, takođe i vlasništvo interfejs pokazivača pod-objekta kod oleautomation interfejsa. Pod-objekti su odvojeni pomoću COM identifikatora čija implementacija kod IDispatch jednostavno prosleđuje glavnu identifikaciju putem posebnih interfejsa koji su u pitanju. Svaki od pod-objekata ima jedno vlasništvo po interfejsu da bi se došlo do drugih interfejsa, i na taj način se simuliralo pravilo COM identifikatora kao što je prikazano kod QI. To nam omogućava da gradimo veoma jednostavan model VB kada je QI dodeljen i jeste referentni tip klase označavajući referencu [datog] interfejsa.

Delovi implementacije šeme su takođe dati kod w/o, i ako se ne varam, to je potpuno simuliran QI, kao što sam ja gore pretstavio. Data implementacija IDispatch takođe može u potpunosti biti typelib-drived bazirana na objektnoj implementaciji IProvideClassInfo i aggregated, npr. korišćenjem clsid kao CLSID_StdNestedDispatch.

Jedna od alternativnih sintaksa koje možda malo više fleksibilna jeste sledeća:

<script language=vbscript>

    ace.paint    ' top level object has all methods of
                 ' [default] interface

    dim artist
    set artist = ace.interface("iartist")

    if not artist is nothing then artist.draw 100

    dim curse
    set curse = artist.interface("icurse")
    if not curse is nothing then curse.darn

    dim cowboy
    set cowboy = curse.interface("icowboy")
    if not cowboy is nothing then cowboy.shoot

   if not curse is nothing then
        curse.shoot
        curse.darn

    end if

</script>

Ova sintaksa pojednostavljuje činjenicu da mi radimo sa COM, i da je samo potrebno dodatno vlasništvo (interfejs) i da je onda lakše rukovati sa neuspelim slučajevima Qi ubuduće.

5. Koritite odvojeni objekat da bi ste prikazali QI.

Ova metoda je veoma realna kod implementacije Dave Rogers,koja je dostupna na http://www.combatcom.com/adminstore/component-warehouse/thedispadapter.htm i Valery Pryamikov, dostupna na http://home.sol.no/~valery Umesto da napravite pseudo-QI kod samog objekta, Valery koristi drugi objekat da bi se izveo QI. Ovo vam omogućava da kod klijenta izgleda ovako:

<script language=vbscript>

    ' Requires object to implement IProvideClassInfo or IPersist
    set dispenum.ObjectClass = ace

    ' Doesn't require IProvideClassInfo or IPersist
    ' Note: Could use CLSID instead of ProgID
    dispenum.SetCLSIDObjectClass(ace, "Ace.AcePowell.1")

    Dim artist
    Set artist = dispenum("iartist") ' Use disenum to perform QI
    If not artist is nothing then artist.draw 100

    Dim curse
    Set curse = dispenum("icurse")
    If not curse is nothing then curse.darn

    Dim cowboy
    Set cowboy = dispenum("icowboy")
    If not cowboy is nothing then cowboy.shoot

   If not curse is nothing then
        Curse.shoot
        Curse.darn
    End if

</script>

Ova tehnika zahteva da još jedan objekat izvede QI, što izgleda malo čudno, ali dobra stvar je da sve funkcioniše i sa postojećim objektima. Nije potrebno rekompajliranje!

Šta sada?

Imajući u obzir tehnike implementacije IDispatch pri prikazivanju funkcionalnosti višebrojnog interfejsa, prva dva ne zahtevaju nikakvu dodatnu pomoć. Možete ih danas koristiti. Dva poslednja, amalgam dispatch i nested dispatch, predstavljaju dva modela funkcionalnosti višebrojnog interfejsa za skripting klijente. Svaki model može biti odgovarajući, ali je svaki konceptualno poseban, npr. objektni implementator će izabrati jedan ili drugi.

Nakon što napišete kod za klijenta, shvatite da je ipak tehnika br. 4 najbolja. To izgleda kao COM za mene. Ipak, vidim da neki klijenti više vole tehnike 3 ili 5.

Dakle, šta sada da radimo? Da li da čekamo da MS pošalje COM+? Nećemo videti meta-informaciju date pisane metode dok ne dođe do preokreta millennia. Da li da čekamo da MS implementira CLSID_StdAmalgamDispatch i CLSID_StdNestedDispatch? Ti momci su previše zauzeti sa COM+, tako da to ne bih očekivao. Da li smo sigurni u povraćaj i korišćenje amalgam dispatch i nested dispatch? Ja jesam, ali ne totalno, u potpunosti. Ako smatrate da nisam ni malo u pravu (ili čak možda samo malo) javite mi.

Sa druge strane, ako ima dovoljno ljudi koji su uzbuđeni oko typelib-driven implementacije IDispatch, to možemo srditi. Posvetio sam ovu veb stranicu sakupljanju i distribuiranju ovog dela. Ako imate na neki način da doprinesete, onda ću to iskoristit da napravim dve implementacije. Ako imate potpunu implementaciju, takođe sam srećan i to da objavim. Molim vas, hajde da prestanemo da implamantiramo IDispatch, tako da ja mogu da prestanem da razmišljam o bespredmetnosti duala. Hvala.

Između ostalog, želite li da znate pravi razlog zašto imamo duale? Štede vptr. Doh!





Published (Last edited): 19-02-2013 , source: http://sellsbrothers.com/Posts/Details/12657