connection points i com

Post on 11-Jan-2016

26 Views

Category:

Documents

0 Downloads

Preview:

Click to see full reader

DESCRIPTION

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 - PowerPoint PPT Presentation

TRANSCRIPT

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– Formål og bruk (løs kobling mellom klient og

tjener, transpartent distribuering)– IEnumXXX interfaces

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)

Observer - 2 (Arkitektur)

Observer har følgende design oversikt i “Design Patterns” (UML Static structure):

Observer - 3 (Bruk)

Sekvensdiagram (UML):

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.

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 .

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

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

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)

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

Advise Sink - Bruk

Lag en klasse som implementerer IAdviseSink Kall Subject’s IAdviseSource::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.

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.

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.

Connection Points - Interfaces

IConnectionPointContainer IEnumConnectionPoints IConnectionPoint IEnumConnections

I tilfellet med Advise Sinks er det vi som implementerer denne funksjonaliteten.

Connection points - Arkitektur

Connection Points - Arkitektur 2

En server kan ha flere connection points. En klient kan implementere flere Sources.

IConnectionPointContainer

[ object, uuid(…), pointer_default(unique) ]interface IConnectionPointContainer : IUnknown{ HRESULT EnumConnectionPoints ( [out] IEnumConnectionPoints ** ppEnum );

HRESULT FindConnectionPoint ( [in] REFIID riid, [out] IConnectionPoint ** ppCP );};

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

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

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

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

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)

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 feilskjekkingDWORD 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;}

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)

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.

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.

Connection Points Implementasjon - ATLclass 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

JMB:

De uthevede linjene gjentas for hvert connection point som skal implementeres

Advise Sinks - Implementasjonclass 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);};

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);

}

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:

Denne koden er hentet fra CFirePropNotifyEvent::FireOnRequestEdit og redigert til vårt formål

JMB:

Denne koden er hentet fra CFirePropNotifyEvent::FireOnRequestEdit og redigert til vårt formål

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:

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

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:

STDMETHODIMP(CoAdviseSource::Attach)(IAdviseSink* pSink, DWORD* pdwCookie){ typedef IConnectionPointImpl<CoAdviseSource,&IID_IAdviseSink> IAdviseSource; return ((IAdviseSource*)this)->Advise(pSink, pdwCookie);}

JMB:

Signaturen til Attach er modifisert med en Cookie for å ha en enklere mapping til connection points.

JMB:

Signaturen til Attach er modifisert med en Cookie for å ha en enklere mapping til connection points.

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.

Sammenligning

Observer Synkronisitet Avstand Scriptklienter

AdviseSink

Primærtsynkron

Nær tilmiddels

Nei

ConnectionPoints

Primærtsynkron

Nær Ja

MSQM Asynkron Middels tilfjern

?

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.

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.

top related