220 likes | 395 Views
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
E N D
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
Creazione di un server COM locale (server in un file .exe) • Passi necessari • Definire le interfacce specifiche del componente • Definire i parametri delle CoClass del componente • Fornire l’implementazione delle interfacce esportate dal componente • Fornire una class factory per creare le istanze del componente quando richieste dai client • Implementare l’entry point (main) del server • Impostare il registry per l’attivazione del componente
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
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/output sono necessarie per la corretta implementazione della comunicazione fra i processi (eventualmente in rete)
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; }; };
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
Passo 3 (ii/iii): Esempio in C++ class MyCounter : public IMyCounter { public: MyCounter(); virtual ~MyCounter(); // Metodi per implementare IUnknown STDMETHODIMP QueryInterface(REFIID riid, void** pIFace); STDMETHODIMP_(DWORD)AddRef(); STDMETHODIMP_(DWORD)Release(); // Metodi per implmentare IMyCounter STDMETHODIMP Reset(int val); STDMETHODIMP Inc(); STDMETHODIMP GetVal(int* val); private: DWORDm_refCount; intval; };
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; }
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 …… // IClassFactory STDMETHODIMP LockServer(BOOL fLock); STDMETHODIMP CreateInstance(LPUNKNOWN pUnkOuter,REFIID riid,void** ppv); private: ULONG m_refCount; };
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
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)
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
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
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
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)
Riuso attraverso “delega” • Si “riusano” i servizi di un componente esistente per implementare alcune funzionalità di un nuovo componente
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
Riuso attraverso “aggregazione” • Si espongono le interfacce di un componente esistente come parte di un nuovo componente senza modificarne il comportamento
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
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
Alcuni riferimenti utili • Principali Interfacce COM • http://msdn.microsoft.com/library/default.asp?url=/library/en-us/com/html/aa9b11ae-7978-44ff-afe7-00c34bd235e3.asp