1 / 36

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

joshwa
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” (UML Static structure):

  6. Observer - 3 (Bruk) • Sekvensdiagram (UML):

  7. Observer 4 - faser • Oppkobling • Klienten registerer ønske om å motta informasjon om hendelser (av en type) fra server • Hendelser • Serveren informerer klienten om ting som har skjedd • Nedkobling • Klienten informerer serveren om at den ikke lenger er interessert i hendelser.

  8. Observer - 5 (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 .

  9. Observer - 6 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

  10. 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

  11. 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)

  12. 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; };

  13. 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 feilsituasjoner.

  14. 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.

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

  16. Connection points - Arkitektur

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

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

  19. 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 ); };

  20. 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 ); };

  21. 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); };

  22. 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; };

  23. 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)

  24. Connection Points - C++ klient • Klient har implementert et objekt med IAdviseSink, og har en peker til en AdviseSource (coclass) • Denne metoden tar ikke hensyn til feilskjekking 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; }

  25. 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)

  26. 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) • Connection points støtter flere former for informasjon (flere connection points). Dette krever mer arbeid i tilfellet med Advise Sinks.

  27. 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.

  28. 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

  29. 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: typedef std::list< CComPtr<IAdviceSink> > SinkList; STDMETHOD(StateIsChanged)(IAdviseSource* pSource); void Attach(IAdviseSink* pSink) { m_pSinkList.push_back(pSink); } HRESULT Detach(IAdviceSink* pSink) { SinkList::iterator it; it = std::find(m_pSinkList.begin(),m_pSinkList.end(), pSink); if ( it == m_pSinkList.end() ) return CONNECT_E_NOCONNECTION; m_pSinkList.erase(it); return S_OK; } private: SinkList m_pSinkList; } m_SourceImpl; STDMETHOD(Attach)(IAdviceSink* pSink); STDMETHOD(Detach)(IAdviceSink* pSink); };

  30. Advise Sink Implementasjon - sende event • IAdviseSink: STDMETHODIMP(CoAdviseSink::SourceImpl::StateIsChanged)(IAdviceSoure* pSource) { SinkList::iterator pSink = m_pSinkList.begin(); while ( pSink != m_pSinkList.end() ) (*pSink++)->StateIsChanged(pSource); return S_OK; } { // … m_SourceImpl->StateIsChanged(this); }

  31. 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

  32. 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); }

  33. MSMQ (Preliminary) • Oppkobling: • Klient og tjener åpner den samme køen, uavhengig av hverandre • Events, sende: • Lag et objekt av typen MSMQMessage • Sett label og body • Body kan bl.a. være et COM objekt som implementerer IDispatch og IPersistStream eller IPersistStorage. • Events, motta: • Synkron: Kall MSMQQueue::Peek og så MSMQQueue::Receive • Asynkron: Implementer MSMQEvent’s interface, og kall MSMQQueue::Receive når Arrived blir kalt • Nedkobling: • Klient og tjener kaller MSMQQueue::Close uavhengig av hverandre.

  34. Sammenligning

  35. 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. • Connection points er velegnet til ”nære” og ”synkrone” ting (f.eks. Controls) • MSMQ er velegnet til ”fjerne” og ”asynkrone” ting.

  36. 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