1 / 33

Connection Points i COM

Connection Points i COM. Bakgrunn Begrunnelse Bruk. Oversikt over foredrag. Observer pattern Bruk Begrunnelse Variasjoner Løsninger i COM Advise sinks Connection Points Begrunnelse + oversikt Bruk. Noen forutsetninger. Relativt god kjennskap til COM IUnknown IDispatch

sef
Download Presentation

Connection Points i COM

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. Connection Points i COM Bakgrunn Begrunnelse Bruk

  2. Oversikt over foredrag • Observer pattern • Bruk • Begrunnelse • Variasjoner • Løsninger i COM • Advise sinks • Connection Points • Begrunnelse + oversikt • Bruk

  3. Noen forutsetninger • Relativt god kjennskap til COM • IUnknown • IDispatch • Formål og bruk (løs kobling mellom klient og tjener, transpartent distribuering) • IEnumXXX interfaces

  4. Bakgrunn - Observer • Observer er et av “Patterns” som er beskrevet i Gamma, et al “Design Patterns” [GOF] • Fra Design Patterns: • “Intent: Define a one-to-many dependency between objects so that when one object changes state, all its dependents are notified and updated automatically.” • Det vil si: Vi ønsker et design der noen objekter (Observers) ønsker å bli informert når “ting skjer” med et annet object (Subject)

  5. Observer - 2 (Arkitektur) • Observer har følgende design oversikt i “Design Patterns”:

  6. Observer - 3 (Bruk) • Sekvensdiagram:

  7. Observer - 4 (Eksempler) • Forhold mellom brukergrensesnitt og “modell” • Gir mulighet for bruk flere klienter mot samme state uten unødvendig kommunikasjonsoverhead • Brukes i Microsoft’s “Document-View” arkitektur, og i den eldre (og bedre) “Controller-Model-View” arkitekturen. • Kan med fordel brukes i distribuerte systemer .

  8. Observer - 5 Varianter • Flere Subjects, Observers, og Events. • Kan modifiseres med et “Subscription” object • Kan “flates ut” for bruk i f.eks. C. Dette minner endel om Windows’ “WindowProc” • Asynkron oppdatering • Egen(e) tråd(er) for å la Subject oppdatere Observers • Informasjon om state kan sendes med Update • Flere varianter til “Update” funksjonen

  9. COM og Observer • Observer patternet egner seg godt for distribuert og/eller modulbasert bruk • Mulighet for asynkron oppdatering • Løs kobling (“coupling”) mellom klient og tjener

  10. COM og Observers - Problemer • Hvordan kan en scripting klient/dynamisk klient finne ut Run Time hvilke “Observer” interfaces en “Subject” støtter • Hvordan vil COM’s identitetregler fungere med Detach? (Jeg vet ikke)

  11. COM Observer 1: Advise Sink • Den enkleste måten å lage en form for toveis kommunikasjon mellom klient og tjener • Eksempel: [ uuid(…), object, pointer_default(unique) ] interface IAdviseSink : IUnknown { HRESULT StateIsChanged(IAdviseSource* pSource); }; [ uuid(…), object, pointer_default(unique) ] interface IAdviseSource : IUnknown { HRESULT Attach(IAdviceSink* pSink); HRESULT Detach(IAdviceSink* pSink); // … + metoder for å endre tilstanden til objektet }; [ uuid(…) ] coclass AdviseSource { [default] interface IAdviseSource; };

  12. Advise Sink - Bruk • Lag en klasse som implementerer IAdviseSink • Kall Subject’sIAdviseSource::Subscribe • IAdviseSink::StateIsChanged vil bli kalt fra subject når nødvendig • Før klientens Advise Sink slettes, kall IAdviseSource::Unsubscribe class MyAdviseSink : public IAdviseSink { public: MyAdviseSink(IAdviseSource* pSource) : m_pSource(pSource) { m_pSource->Attach(this); } ~MyAdviseSink() { m_pSource->Detach(this); } STDMETHOD(StateIsChanged)(IAdviseSink*) { ::MessageBox(NULL, ”Server state has changed”, ””, MB_OK); } private: CComPtr<IAdviseSource> m_pSource; }; JMB: Det kan hevdes at det ikke er så lurt å gjøre Attach og Detach i Constructoren og destructoren, ettersom disse funksjonene ikke er gode til å håntere feil situasjoner.

  13. COM Observer 2: Connection Points (endelig!) • Formålet med å bruke Connection Points over Advise Sinks er hovedsaklig å gi støtte for Script klienter, og for å gi en dynamisk (run time) mulighet for å finne ut hvilke “observers” et objekt støtter. • Problemer som bør nevnes: • Mer komplisert enn Advise Sinks. • “Unødvendige” kall gir performance hit med distribuert klient/tjener.

  14. Connection Points - Interfaces • IConnectionPointContainer • IEnumConnectionPoints • IConnectionPoint • IEnumConnections • I tilfellet med Advise Sinks er det vi som implementerer denne funksjonaliteten.

  15. Connection points - Arkitektur

  16. Connection Points - Arkitektur 2 • En server kan ha flere connection points. • En klient kan implementere flere Sources.

  17. IConnectionPointContainer [ object, uuid(…), pointer_default(unique) ] interface IConnectionPointContainer : IUnknown { HRESULT EnumConnectionPoints ( [out] IEnumConnectionPoints ** ppEnum ); HRESULT FindConnectionPoint ( [in] REFIID riid, [out] IConnectionPoint ** ppCP ); };

  18. IEnumConnectionPoints [ object, uuid(...), pointer_default(unique) ] interface IEnumConnectionPoints : IUnknown { [local] HRESULT Next( [in] ULONG cConnections, [out, size_is(cConnections), length_is(*pcFetched)] IConnectionPoint** ppCP, [out] ULONG * pcFetched ); [call_as(Next)] HRESULT RemoteNext( [in] ULONG cConnections, [out, size_is(cConnections), length_is(*pcFetched)] IConnectionPoint** ppCP, [out] ULONG * pcFetched ); HRESULT Skip( [in] ULONG cConnections ); HRESULT Reset(); HRESULT Clone( [out] IEnumConnectionPoints ** ppEnum ); };

  19. IConnectionPoint [ object, uuid(...), pointer_default(unique) ] interface IConnectionPoint : IUnknown { HRESULT GetConnectionInterface( [out] IID * pIID ); HRESULT GetConnectionPointContainer( [out] IConnectionPointContainer ** ppCPC ); HRESULT Advise( [in] IUnknown * pUnkSink, [out] DWORD * pdwCookie ); HRESULT Unadvise( [in] DWORD dwCookie ); HRESULT EnumConnections( [out] IEnumConnections ** ppEnum ); };

  20. IEnumConnections [ object, uuid(…), pointer_default(unique) ] interface IEnumConnections : IUnknown { typedef struct tagCONNECTDATA { IUnknown * pUnk; DWORD dwCookie; } CONNECTDATA; [local]HRESULT Next( [in] ULONG cConnections, [out, size_is(cConnections), length_is(*pcFetched)] CONNECTDATA* rgcd, [out] ULONG * pcFetched ); [call_as(Next)] HRESULT RemoteNext( [in] ULONG cConnections, [out, size_is(cConnections), length_is(*pcFetched)] LPCONNECTDATA rgcd, [out] ULONG * pcFetched ); HRESULT Skip([in] ULONG cConnections); HRESULT Reset(); HRESULT Clone([out] IEnumConnections ** ppEnum); };

  21. Connection Points - Eksempel [ uuid(…), object, dual, pointer_default(unique) ] interface IAdviseSink : IDispatch { HRESULT StateIsChanged(IAdviceSource* pSource); }; [ uuid(…), object, dual, pointer_default(unique) ] interface IAdviseSource : IDispatch { … }; [ uuid(…) ] coclass AdviseSource { [default] interface IAdviseSource; [default, source] interface IAdviseSink; interface IConnectionPointContainer; };

  22. Connection points og Advise Sinks - Forskjeller • Både klient og tjener interface er Dispatch • IAdviseSource’s metoder for registrering og avregistrering er fjernet (Implementert i IConnectionPointContainer med venner)

  23. Connection Points - C++ klient • Klient har implementert et objekt med IAdviseSink, og har en peker til en AdviseSource (coclass) DWORD Connect(IAdviseSource* pSource, IAdviseSink *pSink) { CComPtr<IConnectionPointContainer> pCPC; pSource->QueryInterface(__uuidof(IConnectionPointContainer), &pCPC); CComPtr<IConnectionPoint> pCP; pCPC->FindConnectionPoint(__uuidof(IAdviseSink), &pCP); DWORD dwCookie; pCP->Advice(pSink, &dwCookie); return dwCookie; }

  24. Connection points - Vurderinger 1 • Endel lenger enn AdviseSink tilfellet (ville være en linje: pSource->Subscribe(pSink);) • QueryInterface, FindConnectionPoint og Advice fører alle til kall over nettverket. • I implementasjonen av Advice (eller hver gang IAdviseSource::StateIsChanged kalles), må serveren kalle QueryInterface for å få riktig interface (Advice tar en IUnknown* som argument)

  25. Connection points - Vurderinger 2 • IConnectionPointContainer og IConnectionPoint gir mulighet til å finne ut mer informasjon dynamisk, f.eks. hvilke Source interfaces som støttes (gjennom IConnectionPointContainer::EnumConnectionPoints og IConnectionPoint::EnumConnections)

  26. Connection Points Implementasjon - Server • Implementasjon av Connection Points på en server er relativt smertefritt med bruk av ATL. (mk:@MSITStore:<MSDNDIR>\VCMFC.CHM::/html/_atl_connection_points.htm) • ATL Wizard har støtte for connection points.

  27. Connection Points Implementasjon - ATL class CoAdviseSource : public CComObjectRootEx<CComObjectThreadModel>, public CComCoClass<CoAdviceSource, &CLSID_AdviseSource>, public IConnectionPointContainerImpl<CoAdviseSource>, public IConnectionPointImpl<CoAdviseSource,&IID_IAdviseSink> { public: ... BEGIN_COM_MAP(CoAdviseSource) COM_INTERFACE_ENTRY_IMPL(IConnectionPointContainer) END_COM_MAP() BEGIN_CONNECTION_POINT_MAP(CoAdviseSource) CONNECTION_POINT_ENTRY(IID_IPropertyNotifySink) END_CONNECTION_POINT_MAP() ... }; JMB: De uthevede linjene gjentas for hvert connection point som skal implementeres

  28. Advise Sinks - Implementasjon class CoAdviseSource : public CComObjectRootEx<CComObjectThreadModel>, public CComCoClass<CoAdviceSource, &CLSID_AdviseSource> { public: ... BEGIN_COM_MAP(CoAdviseSource) COM_INTERFACE_ENTRY(IAdviseSource) END_COM_MAP() class CSourceImpl : public IAdviseSink { public: STDMETHOD(StateIsChanged)(IAdviseSource* pSource) { std::list<IAdviceSink*>::iterator pSink = m_pSinkList.begin(); while ( pSink != m_pSinkList.end() ) (*pSink++)->StateIsChanged(pSource); return S_OK; } // … Implementasjon av Attach og Detach private: std::list<IAdviceSink*> m_pSinkList; } m_SourceImpl; STDMETHOD(Attach)(IAdviceSink* pSink); STDMETHOD(Detach)(IAdviceSink* pSink); }; JMB: Kaller inn til tilsvarende metode i CSourceImpl

  29. Advise Sink Implementasjon - sende event • IAdviseSink: { // … m_SourceImpl->StateIsChanged(this); }

  30. Connection Points ATL Implementasjon - Sende event CComQIPtr<IConnectionPointContainer, &IID_IConnectionPointContainer> pCPC(pUnk); if (!pCPC) return S_OK; CComPtr<IConnectionPoint> pCP; pCPC->FindConnectionPoint(IID_IAdviseSink, &pCP); if (!pCP) return S_OK; CComPtr<IEnumConnections> pEnum; if (FAILED(pCP->EnumConnections(&pEnum))) return S_OK; CONNECTDATA cd; while (pEnum->Next(1, &cd, NULL) == S_OK) { if (cd.pUnk) { HRESULT hr = S_OK; CComQIPtr<IPropertyNotifySink, &IID_IAdviseSink> pSink(cd.pUnk); if (pSink) hr = pSink->StateIsChanged(dispID); cd.pUnk->Release(); if (hr == S_FALSE) return S_FALSE; } } return S_OK; JMB: Koden er komplisert, ettersom den ønsker at serveren skal bruke klientens rammeverk. Det er mulig at det allerede finnes enklere måter å gjøre dette på. JMB: Denne koden er hentet fra CFirePropNotifyEvent::FireOnRequestEditog redigert til vårt formål

  31. Betraktninger • ATL gir endel kode gratis, men gir også endel kompleksitet (tidliger diskusjon) • Det er imidlertid mulig å ha både en ConnectionPoint og en AdviseSink implementasjon av Observer, nemlig: JMB: Signaturen til Attach er modifisert med en Cookie for å ha en enklere mapping til connection points. STDMETHODIMP(CoAdviseSource::Attach)(IAdviseSink* pSink, DWORD* pdwCookie) { typedef IConnectionPointImpl<CoAdviseSource,&IID_IAdviseSink> IAdviseSource; return ((IAdviseSource*)this)->Advise(pSink, pdwCookie); }

  32. Noen merknader • Connection points vil antageligvis gjennomgå en større endring til COM+. For det meste gjennom endringer i C++ språket i VC++ (versjon 7?) • Advise Sinks går ann å bruke fra VB, men det krever mer arbeid enn Connection points. • Connection points er eneste muligheten fra script klienter. • Connection points og Advise Sinks er kompatible, i det minste dersom man lager sin egen implementasjon.

  33. Oppsummering • Formålet med connection points er å åpne for to-veis kommunikasjon mellom klient og tjener i COM. • Den som implementerer tjeneren bestemmer Connection Point (Observer, Advise Sink) interfacet. • Connection points er en noe klønete og ineffektiv implementasjon av Observer Patternet i [GOF]. • Dersom man skal støtte script-klienter må man bruke Connection Points, eller er man fri til å bruke en egen løsning, som Advise Sinks. • ATL har en standard implementasjon av Connection Points. Den er imidlertid ikke helt perfekt.

More Related