320 likes | 489 Views
Uniwersytet Łódzki Katedra Informatyki. W. Bartkiewicz. Systemy rozproszone. Wykład 5. Komunikacja międzyprocesowa z wykorzystaniem usług warstwy pośredniej – zdalne obiekty Część 2. Katedra Informatyki. Ewolucja COM. Katedra Informatyki. COM Interfejsy.
E N D
Uniwersytet Łódzki Katedra Informatyki W. Bartkiewicz Systemy rozproszone Wykład 5. Komunikacja międzyprocesowa z wykorzystaniem usług warstwy pośredniej – zdalne obiekty Część 2
Katedra Informatyki Ewolucja COM
Katedra Informatyki COMInterfejsy • COM korzysta z modelu obiektu zdalnego. Obiekty COM umieszczane mogą być: • w procesie klienta, • w procesie na tej samej maszynie co klient, • w procesie na maszynie zdalnej. • Podobnie jak CORBA, COM skupia się na realizacji interfejsów. Obiekt COM jest generalnie realizacją interfejsu. Jeden obiekt może realizować kilka interfejsów. • Do definiowania interfejsów wykorzystywany jest język opisu interfejsu Microsoft IDL (MIDL). • W przeciwieństwie do CORBY interfejsy COM mają charakter binarny. Są to tablice wskaźników do funkcji będących jego częściami.
Katedra Informatyki COMInterfejsy binarne i zależne od języka Interfejsy binarne Wskaźniki do funkcji Kompilator z IDL na interfejs Specyfikacja IDL Tablica funkcji Interfejsy zdefiniowane w języku Definicje klas Javy Definicje klas C++ Kompilator z IDL na język Kompilator konkretnego języka Prototypy C
Katedra Informatyki COMInterfejsy • W środowisku CORBA standaryzacja interfejsów odbywa się na poziomie źródłowym, tzn. standardowy charakter ma IDL oraz odwzorowanie jego specyfikacji na dany język programowania. • W COM standaryzacja odbywa się na poziomie binarnym. Kompilator interfejsów generuje standardową postać binarną interfejsu, która wykorzystana może być w dowolnym języku programowania. • Każdy interfejs COM ma jednoznaczny 128 bitowy identyfikator, zwany identyfikatorem interfejsu (IID – Interface Identifier). Każdy IID jest globalnie jednoznaczny, tzn. nie ma dwu interfejsów o tym samym IID. Generowany jest na podstawie adresu interfejsu sieciowego danego komputera, czasu oraz dużej liczby losowej. Prawdopodobieństwo wygenerowania dwu takich samych IID jest praktycznie zerowe. • Każda klasa obiektu COM również posiada jednoznaczny identyfikator klasy (CLSID – Class Identifier), tworzony na tej samej zasadzie co UUID.
Katedra Informatyki COMInterfejsy • W środowisku CORBA obiekty funkcjonują w permanentnie działających procesach. W środowisku COM, odmiennie – obiekty mają charakter tymczasowy. Tworzone są z chwilą gdy klient żąda dostępu do obiektu, gdy zabraknie klientów odwołujących się do obiektu, następuje jego likwidacja. • Czas życia obiektu COM zarządzany jest na zasadzie zliczania odwołań. Klienci powinni zwiększać licznik odwołań za każdym razem, gdy pobierają nowe odniesienie do obiektu. Gdy odniesienie przestaje być potrzebne, licznik odwołań powinien być przez klientów zmniejszany. • Wszystkie obiekty COM realizują ten sam interfejs standardowy IUnknown. • Interfejs ten stanowi początkowy uchwyt do obiektu po jego utworzeniu. • Zawiera funkcje AddRef i Release, zarządzające licznikiem odwołań. • Zawiera funkcję QueryInterface, która pozwala pobrać odniesienie do innego z interfejsów realizowanych przez obiekt COM.
Katedra Informatyki COM Przykładowy interfejs IDL import "unknwn.idl"; [ object, uuid (7223BBFD-8F42-43e7-92D9-5080991112C7) ] interface ISum : IUnknown { HRESULT Sum([in] int x, [in] int y, [out, retval] int* retvl); };
Katedra Informatyki COM Implementacja obiektu COM #include"component.h” //wygenerowany przez MIDL zawiera deklarację interfejsu ISum w C++ const CLSID CLSID_MathComp = {0xcec2b111, 0xa70f, 0x41ac, {0xab, 0xb9, 0xc, 0x2, 0x43, 0xbe, 0xae,0x49}}; class CMathComp : public ISum { public: ULONG __stdcall AddRef(); ULONG __stdcall Release(); HRESULT __stdcall QueryInterface(REFIID riid, void** ppv); HRESULT __stdcall Sum(int x, int y, int* retvl); CMathComp(): m_cRef(1) { g_cLocks++; } ~CMathComp() { g_cLocks--;} private: ULONG m_cRef; };
Katedra Informatyki COM Implementacja obiektu COM ULONG CMathComp::AddRef() { return ++m_cRef; } ULONG CMathComp::Release() { if ( --m_cRef != 0 ) return m_cRef; delete this; return 0; } HRESULT CMathComp::QueryInterface(REFIID riid, void** ppv) { if ( riid == IID_IUnknown ) *ppv = (IUnknown*) this; else if ( riid == IID_ISum ) *ppv = (ISum*)this; else {*ppv = NULL;return E_NOINTERFACE;} AddRef(); return S_OK; } HRESULT CMathComp::Sum(int x, int y, int* retvl) { *retvl = x + y; return S_OK; }
Katedra Informatyki COMInterfejsy
Katedra Informatyki COMFabryki klas • Dla tworzenia obiektów COM, powłoka komponentu powinna dostarczać tzw. fabryki klas, tj. specjalnego obiektu COM, który utworzy właściwy obiekt. • Fabryki klas implementują standardowy interfejs COM IClassFactory. Głównym elementem tego interfejsu jest funkcja CreateInstance, której zadaniem jest stworzenie obiektu.
Katedra Informatyki COM Implementacja fabryki klas class CFactory: public IClassFactory { public: ULONG __stdcall AddRef(); ULONG __stdcall Release(); HRESULT __stdcall QueryInterface(REFIID riid, void** ppv); HRESULT __stdcall CreateInstance(IUnknown* pUnknownOuter, REFIID riid, void** ppv); HRESULT __stdcall LockServer(BOOL bLock); CFactory(): m_cRef(1) { g_cLocks++; } ~CFactory() { g_cLocks--; } private: ULONG m_cRef; };
Katedra Informatyki COM Implementacja fabryki klas ULONG CFactory::AddRef() { return ++m_cRef; } ULONG CFactory::Release() { if ( --m_cRef != 0 ) return m_cRef; delete this; return 0; } HRESULT CFactory::QueryInterface(REFIID riid, void** ppv) { if ( riid == IID_IUnknown ) *ppv = (IUnknown*) this; else if ( riid == IID_IClassFactory ) *ppv = (IClassFactory*) this; else { *ppv = NULL; return E_NOINTERFACE; } AddRef(); return S_OK; }
Katedra Informatyki COM Implementacja fabryki klas HRESULT CFactory::CreateInstance( IUnknown* pUnknownOuter, REFIID riid, void** ppv) { if ( pUnknownOuter ) return CLASS_E_NOAGGREGATION; CMathComp* pMathComp = new CMathComp(); if ( !pMathComp ) return E_OUTOFMEMORY; HRESULT hr = pMathComp->QueryInterface(riid, ppv); pMathComp->Release(); return hr; } HRESULT CFactory::LockServer(BOOL bLock) { if ( bLock ) g_cLocks++; else g_cLocks--; return S_OK; }
Katedra Informatyki COMDziałanie klienta • Klient tworzy obiekty COM, wykorzystując funkcję CoCreateInstance. Funkcja ta lokalizuje powłokę komponentu (serwer), pobiera fabrykę klas komponentu i tworzy obiekt COM. • Klient oddziaływuje na obiektach COM a wykorzystaniem wskaźników do interfejsów implementowanych przez te obiekty, pobieranych z wykorzystaniem funkcji QueryInterface. • Komponent dba Każde wywołanie QueryInterface zwiększa licznik odwołań (dba o to sam obiekt COM). Jeśli klient samodzielnie powiela uchwyt di obiektu, powinien ręcznie wywołać AddRef. • Dla każdego wywołania QueryInterface lub AddRef, jeśli uchwyt do obiektu nie jest już potrzebny, klient powinien wywołać Release.
Katedra Informatyki COM Implementacja klienta #include "component.h" // Wygenerowany Przez MIDL const CLSID CLSID_MathComp = {0xcec2b111, 0xa70f, 0x41ac, {0xab, 0xb9, 0xc, 0x2, 0x43, 0xbe, 0xae, 0x49}}; void main() { IUnknown* pUnknown; ISum* pSum; HRESULT hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED); if ( FAILED(hr) ) { cout<<"Blad CoInitializeEx. "<<endl; return; } hr = CoCreateInstance(CLSID_MathComp, NULL, CLSCTX_INPROC_SERVER, IID_IUnknown, (void**) &pUnknown); if ( FAILED(hr) ) { cout<<"Blad CoCreateInstance. "<<endl; return; } hr = pUnknown->QueryInterface(IID_ISum, (void**) &pSum); if ( FAILED(hr) ) { cout<<"IID_ISum nie jest obslugiwany."<<endl; return;} pUnknown->Release(); int sum; hr = pSum->Sum(2,3, &sum); if ( SUCCEEDED(hr) ) cout<<"2+3="<<sum<<endl; pSum->Release(); CoUninitialize(); }
Katedra Informatyki COMRejestr i SCM • Aby naprawdę uaktywnić obiekt, tzn. zapewnić jego utworzenie i umieszczenie w procesie, skąd może akceptować wywołania metod, COM korzysta z rejestru Windows oraz specjalnego procesu nazywanego kontrolerem usług (SCM – Service Control Manager). • Każdy komponent COM musi zostać zarejestrowany w kluczu HKEY_CLASSES_ROOT\CLSID pod swoim CLSID. Opis w rejestrze obejmuje między innymi nazwę pliku (DLL lub EXE) zawierającego implementację klasy. • Funkcja CoCreateInstance znajduje CLSID komponentu w rejestrze, konkretyzuje serwer (powłokę) obiektu, tzn. ładuje odpowiedni plik DLL lub uruchamia plik EXE i udostępnia komponent do obsługi wywołań klienta. • Dla komponentów zdalnych środowisko COM (a właściwie w tym przypadku DCOM) przekazuje CLSID obiektu lokalnemu SCM, który w lokalnym rejestrze odnajduje maszynę serwera, oraz przekazuje CLSID kontrolerowi SCM na tej maszynie.
Katedra Informatyki COMOdwołania zdalne • Dla klienta DCOM kontakt z obiektem zewnątrzprocesowym (lokalnym lub zdalnym) wygląda tak samo jak podczas wykonywania obiektów COM wewnątrzprocesowych we własnej przestrzeni adresowej. • Po konkretyzacji obiektu zdalnego, środowisko DCOM przetacza jego interfejs na serwerze i zwraca klientowi, gdzie jest on przetaczany odwrotnie do pośrednika realizującego odwołania do obiektu. • Po udostępnieniu interfejsu klientowi, środowisko COM praktycznie nie wykonuje żadnych dodatkowych operacji. Odwołania do metod obiektu zdalnego realizowane są niemalże poprzez natywne wywołania Windows RPC. • Parametry i wartości powrotne dla wywołań obiektów zdalnych muszą być oczywiście przetaczane. Standardowy kod przetaczania generowany jest przez MIDL. Z plików generowanych przez MIDL należy wygenerować bibliotekę DLL i zarejestrować w opisie komponentu (tzw. szeregowanie standardowe). COM oferuje również możliwości definiowania przetaczania niestandardowego.
Katedra Informatyki COMOdwołania zdalne
Katedra Informatyki COMOdwołania zdalne
Katedra Informatyki COMBiblioteki typów • Biblioteka typów (type library) jest odpowiednikiem magazynu interfejsów ze standardu CORBA. Biblioteka typów jest najczęściej kojarzona z aplikacją lub komponentem składającym się z różnych obiektów klas. • Kompilowana jest przez MIDL ze specjalnych deklaracji w opisie interfejsu. Zawiera binarną wersję interfejsów klasy COM, a także definiuje ich metody, parametry i zwracane typy. • Może być ona przechowywana w komponencie (jak inne zasoby) lub w odrębnym pliku. Jeśli komponent ma korzystać z biblioteki typów, musi być ona zarejestrowana w jego opisie w rejestrze Windows. • Biblioteka typów używana jest przede wszystkim do ścisłego określenia sygnatury metody. • Narzędzia programowania korzystają z bibliotek typów również w celu pomocy w opracowaniu programu, na przykład wyświetlając w wygodnej postaci interfejsy na ekranie.
Katedra Informatyki COM Dodanie biblioteki typów do interfejsu import "unknwn.idl"; [ object, uuid(10000001-0000-0000-0000-000000000001) ] interface ISum : IUnknown { HRESULT Sum(int x, int y, [out, retval] int* retval); } [ uuid(10000003-0000-0000-0000-000000000001), helpstring("Inside COM+ Component Type Library"), version(1.0) ] library Component { importlib("stdole32.tlb"); interface ISum; [ uuid(10000002-0000-0000-0000-000000000001) ] coclass MathComp { interface ISum; } };
Katedra Informatyki COM Klient korzystający z biblioteki typów #define _WIN32_DCOM //#import "component.tlb" no_namespace //skompil. przez MIDL biblioteka typow #import "component.dll" no_namespace //jeśli biblioteka typow w komponencie #include <iostream.h> void main() { CoInitializeEx(NULL, COINIT_APARTMENTTHREADED); ISumPtr myRef(__uuidof(MathComp)); int result = myRef->Sum(5, 13); cout << "5 + 13 = " << result << endl; myRef = NULL; CoUninitialize(); }
Katedra Informatyki COMAutomatyzacja • COM umożliwia również dynamiczne wywołania metod obiektów. Obiekty, których zamówienia wywołań mają być budowane w fazie wykonania programu, muszą realizować interfejs standardowy IDispatch. Interfejs ten podobny jest do interfejsu dynamicznych wywołań DII w standardzie CORBA. • Obiekty COM realizujące interfejs IDispatch nazywamy obiektami automatyzacji lub automatyzmu (automation objects). Mogą one realizować czysty disp-interfejs, lub udostępniać również własne interfejsy niestandardowe. Mówimy wówczas o tzw. interfejsach dualnych. • W obiektach automatyzacji możliwe jest również definiowanie własności. • Metody interfejsu IDispatch korzystają dla parametrów ze standardowych typów danych (przede wszystkim typ VARIANT). Mogą więc być przetaczane przy pomocy standardowej biblioteki szeregującej i biblioteki typów, niemal bez żadnej dodatkowej pracy programisty (tzw. szeregowanie z biblioteką typów).
Katedra Informatyki COM Interfejs serwera automatyzacji [ object, uuid(10000001-0000-0000-0000-000000000001), dual ] interface ISum : IDispatch { [id(1)] HRESULT Sum([in] int x, [in] int y, [out, retval] int* retvl); } • [id(1)] jest deklaracją tzw. DISPID, identyfikatora metody, który wykorzystywany będzie do dynamicznego jej wywołania. DISPID mogą mieć również właściwości. • Musi mieć on charakter jednoznaczny (wszystkie metody i właściwości muszą mieć różne identyfikatory). • Obiekt musi odpowiednio implementować interfejs IDispatch, pozwalający na wywołanie metody poprzez podanie jej DISPID oraz zawierający operacje wspomagające wywołania dynamiczne.
Katedra Informatyki COM Interfejs IDispatch interface IDispatch : IUnknown { // Czy obsługujesz informacje o typie? HRESULT GetTypeInfoCount( [out] UINT* pctinfo); // Zwraca wskaźnik do informacji o typie twojego obiektu. HRESULT GetTypeInfo( [in] UINT iTInfo,[in] LCID lcid, [out] ITypeInfo** ppTInfo); // Zwraca DISPID metody. HRESULT GetIDsOfNames( [in] REFIID riid, [in, size_is(cNames)] LPOLESTR* rgszNames, [in] UINT cNames, [in] LCID lcid, [out, size_is(cNames)] DISPID* rgDispId); // Wywołuje metodę. HRESULT Invoke( [in] DISPID dispIdMember, [in] REFIID riid, [in] LCID lcid, [in] WORD wFlags, [in, out] DISPPARAMS* pDispParams, [out] VARIANT* pVarResult, [out] EXCEPINFO* pExcepInfo, [out] UINT* puArgErr); };
Katedra Informatyki COM Implementacja serwera automatyzacji HRESULT CInsideCOM::GetIDsOfNames(REFIID riid, LPOLESTR* rgszNames, UINT cNames, LCID lcid, DISPID* rgDispId){ if(riid != IID_NULL) return DISP_E_UNKNOWNINTERFACE; return DispGetIDsOfNames(m_pTypeInfo, rgszNames, cNames, rgDispId); } HRESULT CInsideCOM::Invoke(DISPID dispIdMember, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS* pDispParams, VARIANT* pVarResult, EXCEPINFO* pExcepInfo, UINT* puArgErr){ if(riid != IID_NULL) return DISP_E_UNKNOWNINTERFACE; return DispInvoke(this, m_pTypeInfo, dispIdMember, wFlags, pDispParams, pVarResult, pExcepInfo, puArgErr); } • W większości przypadków funkcje interfejsu IDispatch implementowane są z wykorzystaniem funkcji pomocniczych biblioteki COM, przeszukujących bibliotekę typów. Możliwa jest jednak implementacja niestandardowa.
Katedra Informatyki COM Dynamiczne wywołanie metody IDispatch* pDispatch; OLECHAR* name = L"Sum"; DISPID dispid; pDispatch->GetIDsOfNames(IID_NULL,&name,1,GetUserDefaultLCID(),&dispid); VARIANTARG SumArgsPos[2]; VariantInit(&SumArgsPos[0]); SumArgsPos[0].vt = VT_I4; SumArgsPos[0].lVal = 7; VariantInit(&SumArgsPos[1]); SumArgsPos[1].vt = VT_I4; SumArgsPos[1].lVal = 2; VARIANT result; VariantInit(&result); DISPPARAMS Params1 = { SumArgsPos, NULL, 2, 0 }; if( FAILED(pDispatch->Invoke(dispid, IID_NULL, GetUserDefaultLCID(), DISPATCH_METHOD, &Params1, &result, NULL, NULL)) ) cout << "pDispatch->Invoke() failed" << endl;
Katedra Informatyki COMPunkty połączenia • Podstawowy model komunikacyjny COM opiera się na standardowych wywołaniach metod obiektów zdalnych. W niektórych sytuacjach może być on jednak niewystarczający. • Jeśli sytuacja wymaga komunikacji dwustronnej, tzn. aby obiekt poinformował klienta o zajściu pewnego zdarzenia i zażądał jego obsłużenia, możliwe jest to dzięki realizacji mechanizmu tzw. punktów połączeń. • Schemat działania jest następujący: • Obiekt definiuje interfejs komunikacyjny, jakiego wymaga aby przesłać klientowi komunikat o zdarzeniu. Interfejs ten nazywamy źródłowym (source interface) lub wychodzącym (outgoing interface). • Klient, który chce otrzymywać zdarzenia implementuje wymagany przez obiekt interfejs źródłowy. Implementację taką nazywamy ujściem (sink). • Obiekt z kolei musi implementować standardowe interfejsy COM obsługujące punkty połączeń (najważniejszy to interfejs IConnectionPoint). Interfejs ten zawiera funkcje umożliwiające przekazanie obiektowi przez klienta uchwytu do ujścia (wskaźnika do realizacji interfejsu źródłowego). • Klient po utworzeniu obiektu przekazuje mu wskaźnik do swojego ujścia.
Katedra Informatyki Klient Obiekt dołączalny Interfejs źródłowy Ujście COMPunkty połączenia
Katedra Informatyki COMGrupowe rozsyłanie zdarzeń
Katedra Informatyki COMOdbieranie zdarzeń od wielu obiektów