630 likes | 836 Views
Komponentowe systemy rozproszone. COM i inni. Komunikacja/komponenty w WINDOWS w ujęciu historycznym. Schowek DDE 16-bitowe OLE 1.0 16-bitowe OLE 2.0 OLE 2.0 = COM = ActiveX 32-bitowe OLE Dcom COM+ = COM + MTS + MSMQ. Component Object Model. Binarny standard komunikacji
E N D
Komponentowe systemy rozproszone COM i inni
Komunikacja/komponenty w WINDOWS w ujęciu historycznym • Schowek • DDE • 16-bitowe OLE 1.0 • 16-bitowe OLE 2.0 • OLE 2.0 = COM = ActiveX • 32-bitowe OLE • Dcom • COM+ = COM + MTS + MSMQ
Component Object Model • Binarny standard komunikacji • Nie jest językiem • Niewymagakonketnegojęzyka • Niezależnyodjęzyka • Zarządzaniezasobami • Interfejsy – standard komunikacji • Identyfikacjaobiektówiusług • (Pół)Automatycznemetodymarszalingudanych • Wspiera: enkapsulację, dziedziczenieinterfejsuiimplementacji, polimorfizm
Rodzaje serwerów COM • serwer w procesie = bibliotekadynamicznaInprocServer / InprocServer32 • serwerlokalny = oddzielny proces wykonywalnylokalnieLocalServer / LocalServer32 • serwerzdalny = proces wykonywalnyzdalnieRemoteServer/ RemoteServer32
COM – zarządzanie obiektem • Zarządzaniepamięcią • czasżycia - zliczaniereferencji • zarządzaniekopiamidanychprzekazywanymimiędzyobiektami • Zunifikowanysposóbtworzenia / likwidowania
COM – identyfikacja klas • GUID - globalnieunikalnyidentyfikator:128-bitowa liczbanp.: B77D61E0-47D8-11d0-B53F-444553540000 • CLSID - GUID identyfikującyklasęobiektu • IID - GUID identyfikującyinterfejs (niezbędnygdymamy do czynienia z marszalingiemdanych) Generowanie GUID: • przezużytkownika - GUIDGEN.EXE, UUIDGEN.EXE • Przezśrodowiskonp .NET, VB, VC++ • przez API - funkcjaCoCreateGuid
Tworzenie obiektów • Indywidualna funkcja tworzaca obiekty • CoCreateObject() – wymaga dostarczenia IClassFactory
"Ręczne "tworzenie COM-a– InProcess DllGetClassObject(CLSID_Test, IID_IClassFactory, (void**)pClassFactory); pClassFactory->CreateInstance( NULL, IID_Test, (void**)pTest); pClassFactory->Release();
class ComClientClass { ... virtual DoSmthg(int); } * pInterface; Klient COM if (! FAILED(CoInitialize(NULL))) { err = (CoCreateInstance(CLSID_TESTCLASS, NULL,CLSCTX_INPROC_SERVER, IID_TESTINTERFACE, (void **)&pInterface) ); //użycie COM-a pInterface->DoSmthg(5); //zwolnienie COM-a pInterface->Release(); CoUninitialise(); } CLSCTX_ALL CLSCTX_INPROC_SERVER CLSCTX_LOCAL_SERVER
Powiązanie obiektu z serwerem • systemowabazadanych (rejestrsystemu) - REGEDIT.EXE:HKEY_CLASSES_ROOT CLSID{GUIDklasyobiektu} = OpisklasyobiektuProgID = Producent.Serwer.WersjaVersionIndependentProgID = Producent.SerwerRodzajSerwera = Pełnanazwa (ześcieżką) InsertableProducent.Serwer.Wersja = OpisklasyobiektuCLSID = {GUIDklasyobiektu} Producent.Program = Opisklasyobiektu (bezwersji)CurVer = Producent.Serwer.Wersjafunkcje: ProgIDFromCLSIDCLSIDFromProgID
RejestracjaCOM-a • ”ręczne” uruchomienie pliku typu .REG • automatyczny import pliku .REG • bezpośrednia manipulacja rejestrem • obsługa /REGISTER i /UNREGISTER
COM - interfejsy • Koncepcja interfejsu: • kontrakt na realizację pakietu usług • niezależny od języków programowania sposób opisu: • IDL • Biblioteki typów TLB • odpowiednik tablicy wskaźników do funkcji: • C, C++, Pascal • Java • VB
Realizacja interfejsu w C++ class CBeeper: CUnknown { public: // wysokośćdźwięku long m_lSound ; // wydaniedźwięku long GetSound (); void SetSound (long lSound); long Beep () ; } ptr = CoCreateInstance(....); (CBeeper)ptr->Beep(); interface IBeeper : IUnknown{ long GetSound (); void SetSound (long lSound); long Beep () ;};
Interfejsy HKEY_CLASSES_ROOTCLSID{a123x3y1-4ce6-4ce6-4ce6-2a212123223} = My Prog Proxy = Ole2.dll InprocServer32 = c:\\coms\\myserver.dllLocalServer32 = c:\\coms\\myserver.exe{x111111-4ce6-4ce6-4ce6-2a212123223}InprocServer32 = c:\\coms\\mystub.dllInterface{a123x3y1-4ce6-4ce6-4ce6-2a212123223} NumMethodsBaseInterfaceProxyStubClsId = {x111111-4ce6-4ce6-4ce6-2a212123223}
SkryptIDL • Językopisuinterfejsów [uuid(108dbc1b-1ad2-4bda-b45a-e6b0f014c3a1),object] interface IMKInterface : IUnknown { import "unknwn.idl"; HRESULT TransformTxt([in,out,string]char *file); HRESULT GetMaxLen([out]WORD *dw); HRESULT SetMaxLen([in] WORD dw); } • MIDL.EXE – kompilacja IDL do: • bibliotekitypów • zrodla w c dla stub-a (dll) szeregującego
Biblioteka typów • Możebyćlinkowanajakozasóblubdostępnaoddzielnie: HKEY_CLASSES_ROOTCLSID{a123x3y1-4ce6-4ce6-4ce6-2a212123223} = My ProgProgID= Producent.Serwer.WersjaTypeLib= {a123x3y1-4ce6-4ce6-4ce6-a2121223} TypeLib{a123x3y1-4ce6-4ce6-4ce6-a2121223} = My Type LibDir = c:\tmpHelpDir= c:\tmp\Help1.0 0 = Any.TLB9 = English.TLB
IUnknown - podstawowy interfejs obiektu • każdy obiekt odostępnia interfejs IUnknown • każdy interfejs obejmuje (dziedziczy) IUnknown
Właściwości Interfejsów • Statyczny (niezmienny w czasie) zestawinterfejsów • JednoznacznyiunikatowyIUnknowndlaróżnychobiektów • Zadazwrotności: IA -> IA • Zasadasymetrii: IA -> IB -> IA • Zasadaprzechodniości: IA -> IB -> IC i IA -> IC
IUnknown - funkcjonalność interface IUnknown { // zwiększalicznikodniesień ULONG AddRef () ; // zmniejszalicznikodniesień, // gdylicznik == 0 to zwalniaobiekt ULONG Release () ; // udostępniainterfejsy do obiektu HRESULT QueryInterface ( REFIID riid,// identyfikatorinterfejsuvoid** ppvObj) ;// wskaźnik do zwracanegointerf. } ;
IUnknown – realizacja w C++ class IUnknown { public: virtual HRESULT QueryInterface (REFIID, void**) ; virtual ULONG AddRef () ; virtual ULONG Release () ; } ; class IUnknown { public: STDMETHODIMP QueryInterface (REFIID, void**) ; STDMETHODIMP_(ULONG) AddRef () ; STDMETHODIMP_(ULONG) Release () ; } ; Makra MFC
Zliczanie odniesień • Tworzeniekopiiodniesienia do obiektu – AddRef • Unieważnienieodniesienia do obiektu – Release W praktyceAddRef / Release • Zwrotreferencjiprzezfunkcję • Przekazanieodniesienia do oddzielnegowątku
Zarządzanie obiektami przez zliczanie odniesień class CXxxObject : public IUnknown { public: CXxxObject (…): m_cRef (0) {…} ; virtual HRESULT QueryInterface (REFIID, void**) ; virtual ULONG AddRef () ; virtual ULONG Release () ; … private: ULONG m_cRef ; // licznikodniesień … } ;
IUnknown::AddRef / Release ULONG CXxxObject::AddRef () { return ++m_cRef; // licznikodniesień } ULONG CXxxObject::Release () {if (0 != --m_cRef) // licznikodniesień return m_cRef ; delete this ; // usunięcieobiektu return 0 ; }
IUnknown::QueryInterface HRESULT CXxxObject::QueryInterface ( REFIID riid, void** ppvObj) {*ppvObj = NULL ; if ( IsEqualIID(riid, IID_IUnknown) || IsEqualIID(riid, IID_IMyInterface) ) // if (riid == IID_IUnknown || riid == IID_IMyInterface) *ppvObj = this ; if (NULL != *ppvObj) { ((IUnknown)*ppvObj)->AddRef(); //licznikodniesień return NOERROR ;} else return E_NOINTERFACE; }
IClassFactory - tworzenie obiektów interface IClassFactory : public IUnknown { // stworzenieobiektu HRESULT CreateInstance(IUnknown* pUnkOuter,// wskaźnik do obiektunadrzęd.REFIID riid,// podstawowyinterfejsobiektu void** ppvObj) ;// wskaźnik do zwracanegointerf. // zablokowanieserwera w pamięci HRESULT LockServer(BOOL fLock); } ;
Realizacja wielu interfejsów? Techniczny problem w przypadkurealizacjiinterfejsówjakooddzielnychklas pot. problem z powolywaniem do zyciaizarzadzaniewieloma POWIĄZANYMI obiektami możliwerozwiązanie: klasyzagnieżdzone MFC – BEGIN/END_INTERFACE_PART metody z IUnknownsądelegowne do klasygłównej
Serwer w procesie (in-process) • bibliotekadynamicznaudostępniającafunkcje: STDAPI DllGetClassObject ( REFCLSID rclsid,// GUID klasyudostępnianychobiektów REFIID riid,// GUID łączatworzącegoobiekty// (zwykleIID_ClassFactory) void** ppv) ;// wskaźnik do zwracanegołącza STDAPI DllCanUnloadNow () ;
Funkcje serwera typu InProcess STDAPI DllGetClassObject(REFCLSID rclsid,REFIIDriid,void**ppv) {if (rclsid != CLSID_XXX) return ResultFromScode (E_FAIL) ; CXxxClassFactory* pObj = new CXxxClassFactory;if (pObj == NULL) return ResultFromScode(E_OUTOFMEMORY); HRESULT hr = pObj->QueryInterface (riid, ppv) ;if (FAILED (hr)) delete pObj ;return hr; } STDAPI DllCanUnloadNow () {SCODE sc = (/*czysąobiekty? */…) ? S_FALSE : S_OK ;return ResultFromScode (sc); }
Klient COM Server COM CoCreateInstance CLSID {123434-23213-2323-4234} InprocServer32 = “test.dll” CoGetClassObject ReadRegistry CoLoadLibrary(“test.dll”,TRUE) Hmodule = LoadLiibrary(test.dll) DllMain GetProcAdress(hModule, ”DllGetClassObject”) DllGetClassObject DllGetClassObject pFactory->QueryInterface pFactory->AddRef pFactory->CreateInstance pFactory-> CreateInstance pObject->QueryInterface pObject->AddRef pFactory->Release pFactory->Release
Serwer lokalny • program wykonywalnyrejestrującyudostępnianeobiekty: • zagadnienia • utrzymanieprogramu w pamięcidopókiistniejąobiekty • pot. różnywygląd w zależnościodtypupracy (/embeeded) STDAPI CoRegisterClassObject( REFCLSID rclsid,// GUID klasyobiektów IUnknown * pUnk, // InterfejsIUnknown// udostępnianegoobiektu DWORD dwClsCtxt,// Rodzajserwera DWORD flags,// Sposóbnawiązywaniapołączenia DWORD** pdwReg// Wskaźnik do zarejestrowanej klasy );
Inicjalizacjaizakończenieserweralokalnego // inicjalizacja if (FAILED (CoInitializeEx (NULL))) …// błądinicjalizacji CXxxClassFactory* pClassFact = new CXxxClassFactory ; pClassFact->AddRef () ; DWORD dwReg; if (FAILED (CoRegisterClassObject(CLSID_XXX, pClassFact,CLSCTX_LOCAL_SERVER,REGCLS_MULTIPLEUSE, &dwReg))) … // błądinicjalizacji // zakończenie CoRevokeClassObject (dwReg) ; pClassFact->Release () ; CoUninitializeEx() ;
COM – marszalingdanych między procesami Wymagane jest zarejestrowanie interfejsów
Interfejsy HKEY_CLASSES_ROOTCLSID{a123x3y1-4ce6-4ce6-4ce6-2a212123223} = My Prog Proxy = Ole2.dll InprocServer32 = c:\\coms\\myserver.dllLocalServer32 = c:\\coms\\myserver.exe{x111111-4ce6-4ce6-4ce6-2a212123223}InprocServer32 = c:\\coms\\mystub.dllInterface{a123x3y1-4ce6-4ce6-4ce6-2a212123223} NumMethodsBaseInterfaceProxyStubClsId = {x111111-4ce6-4ce6-4ce6-2a212123223}
Wielokrotne wykorzystywanie (reusability) kodu • C++ • dziedziczenie • wspólne pola danych dla klasy podstawowej i pochodnej • wymaga bardzo dokładnego dokumentowania, zwykle przez udostępnianie kodu źródłowego • COM • zawieranie obiektów • agregacja obiektów
Zawieranie obiektów • modyfikacjametodobiektu • obiektzewnętrznynieudostępniałączaIUnknownobiektuwewnętrznego • obiektzewnętrznyudostępniapośredniowybranełączaobiektuwewnętrznego
Zawieranie obiektów - zewnętrzny obiekt class CExtObject: IUnknown, IInterfaceW, IInterfaceZ { public:CExtObject (…): m_pInObj (NULL){ ::CoCreateInstance (CLSID_ExtObject, NULL, CLSCTX_ALL, IID_IUnknown,&m_pInObj) ;} ; ~CExtObject () { Release();delete m_pInObj; } ; … // metodyłączIInterfaceZiIInterfaceW private:IUnknown* m_pInObj ; // obiektwewnętrzny… } ;
Agregacja obiektów • udostępnienieniezmienionychmetodobiektu • obiektzewnętrznyudostępniapośredniołączeIUnknownobiektuwewnętrznego • obiektzewnętrznyudostępniabezpośredniopozostałełączaobiektuwewnętrznego
Agregacja - wewnętrzny obiekt class CInObject : IUnknown, IInterfaceW { public:CInObject (IUnknown* pUnkOuter, …) ; virtual HRESULT QueryInterface (REFIID, void**) ; virtual ULONG AddRef () ; virtual ULONG Release () ; … private:ULONG m_cRef ;// licznikodniesień IUnknown *m_pUnkOuter ; // zewnętrzneIUnknown … } ;
Agregacja – zliczanie odniesień CInObject:: CInObject(IUnknown* pUnkOuter, …)m_cRef (0), m_pUnkOuter (pUnkOuter) { … } ULONG CInObject::AddRef (){ if (m_pUnkOuter) return m_pUnkOuter->AddRef () ; return ++m_cRef ; } ULONG CInObject::Release (){ if (m_pUnkOuter) return m_pUnkOuter->Release () ; if (0 != --m_cRef) return m_cRef ; delete this ; return 0 ; }
Agregacja - QueryInterface HRESULT CInObject::QueryInterface (REFIID riid, void** ppvObj) {*ppvObj = NULL ; if (m_pUnkOuter) return m_pUnkOuter->QueryInterface(riid, ppvObj); if (IsEqualIID (riid, IID_IUnknown) ||IsEqualIID (riid, IID_IInterface1)) *ppvObj = this ; if (NULL != *ppvObj) { ((LPUNKNOWN)*ppvObj)->AddRef () ; return NOERROR ;}return ResultFromScode (E_NOINTERFACE) ; }
Agregacja – CreateInstance HRESULT CInClassFactory::CreateInstance ( IUnknown* pUnkOuter, REFIID riid, void** ppvObj) {*ppvObj = NULL ; if (pUnkOuter && riid != IID_IUnknown) return ResultFromScode (E_NOINTERFACE); CInObjectpObj = new CInObject (pUnkOuter, …) ; HRESULT hr = pObj->QueryInterface (riid, ppvObj) ; if (FAILED (hr)) delete pObj ; else *ppvObj = pObj ; return hr ; }
ActiveX Kontrolki, Serwery • Kontrolki ActiveX: • standard do rozszerzaniazestawupóldialogowychdostępnych w systemie Windows • poprzedniestandardy:Custom Controls, Visual Basic Controls • OCX = ActiveX (OLE controls = ActiveX controls) • gotowekomponentyfunkcjonalne do budowaniaprogramów • Server ActiveX: program udostępniającyIDispatch
RealizacjainterfejsuIDispatch w C++ interface IDispatch: IUnknown {HRESULT GetTypeInfoCount (…) ; HRESULT GetTypeInfo (…) ; HRESULT GetIDsOfNames (…) ; HRESULT Invoke (DISPID dispID, REFIID riid, LCID lcid, unsigned short wFlags, DISPPARAMS* pDispParams, VARIANT* pVarResult, EXCEPINFO* pExcepInfo, unsigned int* puArgErr) ; } ;
Implementacja klienta automatyzacji - Visual Basic Dim ThisExcel As Excel.ApplicationDim ThisChart As Excel.Chart Private Sub Command1_Click()Timer1.Enabled = Not Timer1.EnabledIf Timer1.Enabled Then Command1.Caption = "&Pause Chart Turning"Else Command1.Caption = "&Resume Chart Turning"End If End Sub Private Sub Timer1_Timer()Static iRotateWith ThisChart .Rotation = iRotateEnd WithiRotate = (iRotate + 5) Mod 360 End Sub
Private SubForm_Load() Dim TitleArray As VariantDim DataArray As VariantTitleArray = Array("Dogs", "Cats", "Horses")DataArray = Array(34, 53, 12) Set ThisExcel = CreateObject("Excel.application")With ThisExcel .Workbooks.Add .Range("A1:C1").Value = TitleArray .Range("A2:C2").Value = DataArray .Range("A1:C2").Select Set ThisChart = .Charts.Add() .Visible = TrueEnd WithWith ThisChart .Type = Excel.Constants.xl3DColumn .HasLegend = FalseEnd WithCommand1_Click End Sub
Implementacjaklientaautomatyzacji– C++ CExcelApplication * app = new CExcelApplication(.....); //app-> Attach(....); App->Workbooks.Add();
Dedykowanyklientautomatyzacji MFC/.Netpotrafiąwygenerowaćklasęudostępniającąfunkcjonalnośćobiektu ActiveX napostawie: • biblioteki TLB (równieżnp. wkompilowanej w dll) • zawartościrejestru (ID.klasy) • klasapośredniczy w wołaniumetodserwera • dostępie do póludostepnianychprzezserwer (Get/Set) CMyComClassDriver c; c.CreateDispatch(ComObjectCLSID); x = c.Calculate(15); c.ReleaseDispatch();
Klientautomatyzacji - VC++ CLSID clsid; IDispatch* pIDispatch ; ::CLSIDFromProgID(T2OLE(_T("Excel.application")),&clsid); HRESULT hr = ::CoCreateInstance (clsid, NULL, CLSCTX_SERVER,IID_IDispatch, (void**) &pIDispatch); CApplication m_pThisExcel = new CApplication (pIDispatch) ; CChart*m_pThisChart =new CChart(CCharts(m_pThisExcel->Charts ()).Add ()); CRange( m_pThisExcel->Range("A1", "C1")).SetValue(TitleArray); CRange (m_pThisExcel->Range ("A1", "C2")).Select () ; m_pThisExcel->SetVisible (TRUE) ; m_pThisChart->SetRotation (iRotate) ;