Componenti COM

22
Componenti COM

description

Componenti COM. Componenti COM (COM server). Un Componente COM implementa ed esporta un insieme di Interfacce (oltre a IUnknown) L’implementazione delle interfacce è fornita da un insieme di CoClass Ogni CoClass è univocamente identificata da un CLSID - PowerPoint PPT Presentation

Transcript of Componenti COM

Page 1: Componenti COM

Componenti COM

Page 2: Componenti COM

Componenti COM (COM server)

• Un Componente COM implementa ed esporta un insieme di Interfacce (oltre a IUnknown)

• L’implementazione delle interfacce è fornita da un insieme di CoClass

• Ogni CoClass è univocamente identificata da un CLSID• Le CoClass di un componente sono contenute nel file

eseguibile del componente (per esempio Dll o Exe)• In Windows i CLSID delle CoClass dei componenti

disponibili sono registrate nel Registry di sistema

Page 3: Componenti COM

Creazione di un server COM locale (server in un file .exe)

• Passi necessari1. Definire le interfacce specifiche del componente

2. Definire i parametri delle CoClass del componente

3. Fornire l’implementazione delle interfacce esportate dal componente

4. Fornire una class factory per creare le istanze del componente quando richieste dai client

5. Implementare l’entry point (main) del server

6. Impostare il registry per l’attivazione del componente

Page 4: Componenti COM

Passo 1Interfaccia del componente

• IDL è un linguaggio che permette di definire interfacce e librerie di tipi (contenenti CoClass)

• Attributi IDL– Identificano le caratteristiche delle interfacce/CoClass– Attributo [uuid]: definisce gli IID o CLSID– Attributo [oleautomation]: definisce che l’interfaccia sarà usata per

comunicazione inter-processi – Attributo [version]: definisce il numero di versione– Attributo [helpstring]: descrizione testuale– (per altri attributi vedere

http://msdn2.microsoft.com/en-US/library/8tesw2eh(VS.80).aspx)• MIDL (Microsoft IDL compiler) permette di compilare l’interfaccia.

Genera 3 file– <interface>_h.h: Contiene le definizioni delle interfacce (da usare come

header file)– <interface>_i.c: Contiene gli IID e CLSID (usare in tutti I server e client)– <interface>_p.c: Contiene il codice di proxy/stub per client-server

distribuiti

Page 5: Componenti COM

Interfaccia IDL (esempio)

[uuid(5C6CD72C-8FDE-4117-98BE-B2BB2D4110CB), version(1.0), oleautomation, helpstring("Interfaccia IMyCounter")]

interface IMyCounter : IUnknown { HRESULT Reset( [in] int val); HRESULT Inc( ); HRESULT GetVal( [out,retval] int* val);};

Le specifiche sui parametri di input/outputsono necessarie per la corretta implementazionedella comunicazione fra i processi (eventualmentein rete)

Page 6: Componenti COM

Passo 2. Type Library

• Una TypeLibrary contiene informazioni sulle CoClass incluse nel componente e le interfacce da esse implementate

[uuid(2EBFC693-433E-41f5-B80B-98BDCDD8AD96), version(1.0), helpstring("TypeLib contenente il server locale (coclass) dei componenti MyCounter")]

library CounterLocalServerLib { importlib("stdole32.tlb");

[uuid(34898D9A-B306-42ee-9105-300945C2B151)] coclass MyCounter {

[default]interface IMyCounter; };};

Page 7: Componenti COM

Passo 3 (i/iii)Implementazione del componente

• Implementazione di tutte le funzionalità esportate dalle interfacce del componente

• Può essere realizzata in un qualsiasi linguaggio facendo riferimento agli IID e CLSID definiti in IDL

• L’implementazione può consistere di uno o più moduli indipendentemente dal numero delle interfacce implementate

• Va sempre implementata anche l’interfaccia IUnknown

Page 8: Componenti COM

Passo 3 (ii/iii): Esempio in C++class MyCounter : public IMyCounter {public:

MyCounter();virtual ~MyCounter();

// Metodi per implementare IUnknownSTDMETHODIMP QueryInterface(REFIID riid, void** pIFace);STDMETHODIMP_(DWORD)AddRef();STDMETHODIMP_(DWORD)Release();

// Metodi per implmentare IMyCounterSTDMETHODIMP Reset(int val);STDMETHODIMP Inc();STDMETHODIMP GetVal(int* val);

private:DWORD m_refCount;int val;

};

Page 9: Componenti COM

Passo 3 (iii/iii)Implementazione di QueryInterface

STDMETHODIMP MyCounter::QueryInterface(REFIID riid, void** pIFace){

if (riid == IID_IUnknown) *pIFace = this;else if (riid == IID_IMyCounter) *pIFace = this;else {

*pIFace = NULL;return E_NOINTERFACE;

}

( (IUnknown*) (*pIFace) )->AddRef();return S_OK;

}

Page 10: Componenti COM

Passo 4. Class Factory• La class factory è necessarie per poter creare più istanze dello stesso

componente per richieste provenienti da client diversi• Le class factory implementano l’interfaccia standard IClassFactory

class MyCounterClassFactory : public IClassFactory {public:

MyCounterClassFactory();virtual ~MyCounterClassFactory();// IUnknown……// IClassFactorySTDMETHODIMP LockServer(BOOL fLock);STDMETHODIMP CreateInstance(LPUNKNOWN pUnkOuter,REFIID riid,void** ppv);

private:ULONG m_refCount;

};

Page 11: Componenti COM

Passo 5. Server Entry Point

• Main che viene eseguito quando viene lanciato l’eseguibile del server– Registra tutti i tipi del componente (CLSID e IID) nel

registry di sistema (LoadTypeLibEx)– Crea gli oggetti ClassFactory per ogni componente

gestito dal server e li registra nel sistema (CoRegisterClassObject) in modo che possano essere trovato attraverso CoGetClassObject

– Rimane in attesa che il client smetta di usarlo client

Page 12: Componenti COM

Server object counting

• Il server viene attivato da COM quando un client fa richiesta

• Il server deve rimanere attivo fino a quando almeno un client sta usando almeno un oggetto residente sul server, poi terminare

• Per questo motivo, il server conta gli oggetti attivi (usati dai client), e quando il contatore va a 0 termina– La terminazione può per esempio avvenire attraverso

un messaggio che interrompe il ciclo di attesa (o attraverso altre primitive di sincronizzazione)

Page 13: Componenti COM

Object counting e reference counting

• L’object counting è di solito associato alla costruzione/distruzione degli oggetti– Per ogni nuovo oggetto creato, si incrementa il

contatore di 1– Per ogni oggetto distrutto lo si decrementa di 1

• A sua volta, la distruzione degli oggetti è regolata dal meccanismo di reference counting (AddRef-Release)– Quando viene rilasciato l’ultimo reference, l’oggetto

viene distrutto

Page 14: Componenti COM

Class Factory e Reference Counting

• Per le class factory l’incremento del contatote di reference (AddRef) effettuato in fase di registrazione (CoRegisterClassObject) non sarebbe mai controbilanciato, rendendo così impossibile terminare il server quando non è più usato da nessun client

• Soluzione– Non uso reference counting per le class factory (annullando così l’effetto

problematico di CoRegisterClassObject)– Quando una class factory viene richiesta per la prima volta con

CoGetClassObject, COM invoca IClassFactory::LockServer(TRUE)– Com gestisce il reference counting relativo al client, attraverso un

oggetto proxy, e quando viene rilasciato l’ultimo riferimento invoca ICLassFactory::LockServer(FALSE)

• Quindi– Non c’è bisogno di usare reference counting, perché viene gestito

automaticamente da COM– Il contatore degli oggetti sul server può essere gestito opportunamente

attraverso IClassFactory::LockServer

Page 15: Componenti COM

Passo 6. Impostazioni per l’attivazione del componente

• Nel Registry di sistema va registrata l’associazione fra CLSID principale del server e file eseguibile che va lanciato per attivare il server

• Bisogna creare opportune chiavi sotto HKEY_CLASSES_ROOT per registrare– L’associazione fra CLSID del server, e il per percorso

del file eseguibile– L’associazione fra LIBID della TypeLib e il percorso del

file contenente la typeLib– L’associazione fra gli IID delle interfacce e le proprietà

delle stesse

Page 16: Componenti COM

Meccanismi di riuso in COM

• In COM esistono due principali forme di riuso di componenti (e delle loro interfacce) per implementare nuovi componenti– Delega (delegation)– Aggregazione (aggregation)

Page 17: Componenti COM

Riuso attraverso “delega”

• Si “riusano” i servizi di un componente esistente per implementare alcune funzionalità di un nuovo componente

Page 18: Componenti COM

Delega: cenni all’implementazione e all’uso

• Il componente esterno (outer object) è client del componente interno (inner object)

• Generalmente, il componente interno non è visibile all’esterno• L’implementazione del componente esterno controlla il ciclo di vita

del componente interno– definisce un puntatore al componente interno– crea il componente interno quando opportuno (per esempio in fase di

creazione del componente esterno)– usa il componente interno attraverso le opportune interfacce– Rilascia il componente interno quando opportuno (per esempio quando

viene rilasciato il componente esterno) • In questo modo è inoltre possibile

– usare le interfacce del componente esistente come base per implementare le medesime interfacce nel nuovo componente

– aggiungere (se necessario) codice prima e/o dopo l’invocazione dei metodi del componente esistente

Page 19: Componenti COM

Riuso attraverso “aggregazione”

• Si espongono le interfacce di un componente esistente come parte di un nuovo componente senza modificarne il comportamento

Page 20: Componenti COM

Aggregazione: cenni all’implementazione e all’uso

• Il componente interno viene creato in modo da delegare le chiamate a IUnknown all’interfaccia IUnknown del componente esterno

• Il metodo QueryInterface del componente esterno restituisce (per le opportune interfacce) un puntatore al componente interno

• In questo modo è possibile– usare lo stesso esatto comportamento di un componente

esistente senza conoscerne i dettagli comportamentali – evitare il codice di delega dei servizi– aumentare le prestazioni evitando troppe chiamate di procedura

annidate

Page 21: Componenti COM

Aggregazione: creazione del componente interno (cenni)

• Attraverso i metodi di creazione della COM library (CoCreateInstance, CoGetClassObject, IClassFactory::CreateInstance)– Tali metodi ricevono un puntatore (pUnkOuter) all’interfaccia

IUnknown cui delegare le chiamate– L’idea è di usare tale parametro in modo che, se il puntatore

pUnkOuter è diverso da NULL, le chiamate vengano delegate all’oggetto da esso puntato

• L’implementazione dei componenti deve tenere opportunamente conto del puntatore pUnkOuter affinché il componente sia aggregabile

• Tenere conto dell’aggregabilità costa poche linee di codice (circa 10) quindi è generalmente consigliato nell’implementazione del componente