Warsztat.GDCompo!ProjektyMediaArtykułyQ&AForumOferty pracyPobieranie

Opisz napotkaną sytuację, a redakcja niezwłocznie znajdzie rozwiązanie!

wyślij anuluj

Potwór zwany COM

Uwaga! Tekst posiada 2 niepotwierdzonych zmian!

Tekst został importowany z Warsztatowych artykułów. Jego oryginalnym autorem jest Artur Poznański (artpoz). Jeżeli został importowany poprawnie, usuń ten szablon!

Wprowadzenie do komponentów

Wstęp

Początkujący programista DirectX spotyka się z nowymi pojęciami takimi jak komponenty, obiekty COM, interfejsy czy metody. Zrozumienie tych pojęć może być trudne, zwłaszcza jeśli nie ma się wiedzy na temat programowania obiektowego. Tym artykułem chciałbym nieco przybliżyć pewne idee i pomysły jakie zrodziły się w głowach informatyków pod koniec lat osiemdziesiątych a zaowocowały w latach dziewięćdziesiątych modelem komponentów obiektowych zwanym w skrócie COM (Component Object Model).

Podejście obiektowe

W latach sześćdziesiątych i siedemdziesiątych powstawały pierwsze obiektowe języki programowania. Ich celem było lepsze odzwierciedlenie rzeczywistego świata i relacji zachodzących między jego różnymi elementami. Wprowadzały one nowe pojęcia, takie jak klasa, obiekt, metoda, dziedziczenie, hermetyzacja i polimorfizm. Klasa to pewien zbiór atrybutów i operacji na tych atrybutach (np. forma na ciastko o odpowiednim kształcie, rys. 1). Obiekt to z kolei egzemplarz klasy (konkretne ciastko o odpowiednim kształcie stworzone na podstawie tej formy). Metoda to opis tego co dany obiekt może zrobić (np. ciastko może się przypalić, jakoś smakować). Hermetyzacja ukrywa szczegóły (budowy ciastka), dziedziczenie pozwala wykorzystać jedne klasy do tworzenia innych wyspecjalizowanych (forma na eklerki, forma na pączki), dzięki polimorfizmowi możemy umieszczania różne zachowania obiektu na tą samą sytuację w zależności od rodzaju obiektu (skoro już ciągnę tą analogię z ciastkami to każde ciastko wie jak smakuje, a każdy rodzaj smakuje oczywiście inaczej ).

rys1.png

Problemy

Wkrótce w branży informatycznej pojawiły się problemy z tworzeniem oprogramowania. Było ono zbyt związane z konkretnym środowiskiem. Posiadało zbyt wiele statycznych cech, których nie dało się modyfikować lub było to trudne i ryzykowne dla stabilności programu. Rozwiązaniem okazał się komponent – niezależny binarny moduł dodawany do systemu. Ważną też jego zaletą stała się niezależność od języka programowania.

Podejście komponentowe

Programowanie komponentowe miało polegać na budowie aplikacji z komponentów, jak z klocków LEGO. Każdy komponent jest obiektem COM z wbudowanymi interfejsami. Posłużmy się kolejną analogią (Rys.2). Mamy magnetowid, który będzie reprezentacją obiektu COM. Interfejs to rodzaj pilota do tego magnetowidu z zestawem gotowych przycisków. Każdy z tych przycisków włącza inną funkcję magnetowidu. Jest to magnetowid zabezpieczony przed dziećmi (hermetyzacja) i nie można tych funkcji uruchamiać z panelu magnetowidu, a jedynie za pomocą pilota. Musimy też sobie wyobrazić, że nasz magnetowid (obiekt COM) obsługiwać można za pomocą wielu pilotów (wiele interfejsów). W pewnym sensie interfejs można traktować jako klasę z samymi metodami zaś obiekt COM jako zbiór takich właśnie interfejsów.

rys2.png

Model COM stanowi podstawę dla innych technologii komponentowych takich jak DCOM, COM+ czy ActiveX. Jest to narzucone rozwiązanie, które nas w pewnym stopniu ogranicza, ale niewątpliwą zaletą staje się szybkość tworzenia samych programów wykorzystujących gotowe komponenty.

Interfejs IUnknown

Komponenty są zazwyczaj przechowywane w plikach DLL i łączone dynamicznie w trakcie pracy programu. Przypominam, że obiekt COM może posiadać wiele interfesów, które mogą mieć wiele metod. Jest jednak jeden interfejs obowiązkowy o nazwie IUnknown, który posiada trzy metody (funkcje):

  • QueryInterface
  • AddRef
  • Release

rys3.gif

Rysunek 3 przedstawia przykładowy obiekt COM. Interfejs IUnknown ma dwa zadania. Pierwsze z nich to „dostanie się” do pozostałych interfejsów, drugie to regulowanie czasu życia obiektu. Aby mieć dostęp do jakiegoś innego niż IUnknown interfejsu musimy mieć do niego wskaźnik. Wskaźnik ten da nam funkcja QueryInterface, jeśli podamy jej tzw. identyfikator interfejsu (IID). Jest to 128-bitowa liczba, która nam jednoznacznie określa dany interfejs. W ten sposób mówimy funkcji jaki interfejs nas interesuje. Czas życia obiektu regulują dwie funkcje: AddRef i Release. Ta pierwsza zwiększa licznik odwołań, ta druga go zmniejsza. Licznik odwołań jest to liczba typu ULONG (unsigned long), która określa ile obiektów zostało utworzonych. Jeśli wartość ta spadnie do zera, obiekt jest niszczony (zwalniana jest pamięć).

Uwzględniamy dziedziczenie

Aby uprościć rozważania pominąłem bardzo istotny szczegół, który teraz trzeba jeszcze wziąć pod uwagę, a mianowicie dziedziczenie. IUnknown można traktować jako klasę abstrakcyjną z samymi czysto wirtualnymi funkcjami. W tłumaczeniu z języka IDL, w którym pisze się komponenty, na (znany wam doskonale ;) C++ wygląda to tak:

class IUnknown
{
public:
virtual HRESULT QueryInterface(const IID& idd,void** ppVObj) = 0;
virtual ULONG AddRef( void ) = 0;
virtual ULONG Release( void ) = 0;
}

Każdy kolejny interfejs musi być odziedziczony po IUnknown i posiadać własne implementacje funkcji QueryInterface, AddRef i Release. Zatem nasz prawidłowy schemat obiektu powinien wyglądać raczej tak jak na rysunku 4.

rys4.gif

QueryInterface

Funkcja QueryInterface niejako „przepytuje” (query) obiekt o dodatkowe interfejsy i zwraca jako rezultat 32-bitową liczbę typu HRESULT (long). Jeśli to sprawdzanie się powiedzie, funkcja zwraca wartość dodatnią lub zero (jako S_OK), jeśli nie to wartość ujemną (E_NOINTERFACE) i ustawia wskaźnik na NULL. Istnieją też dwa makra zdefiniowane w pliku WinError.h, które ułatwiają sprawdzanie czy wartość HRESULT jest dodatnia lub równa zero (operacja udała się) czy ujemna (błąd).

#define SUCCEEDED(Status) ((HRESULT) (Status) >= 0)
#define FAILED(Status) ((HRESULT) (Status) < 0)

Jak to wygląda w praktyce ? Otóż jeżeli funkcja QueryInterface zwróci wskaźnik do interfejsu to już sama wywołuje funkcję AddRef, a ta ustawia licznik odwołań na 1. Jednak zmniejszenie tego licznika o 1 funkcją Release i tym samym zwolnienie pamięci należy już do samej aplikacji.

Tekst dodał:
Adam Sawicki
18.02.2006 10:55

Ostatnia edycja:
Adam Sawicki
18.02.2006 10:55
(+ 2 niepotwierdzonych zmian)

Kategorie:

Aby edytować tekst, musisz się zalogować.

# Edytuj Porównaj Czas Autor Rozmiar
#3 edytuj (poprz.) 03.01.2014 02:14 Wojciech 6.92 KB (+36)
#2 edytuj (poprz.) (bież.) 03.01.2014 02:11 Wojciech 6.88 KB (+83)
#1 edytuj (bież.) 18.02.2006 10:55 Adam Sawicki 6.8 KB
Zwykły
Do sprawdzenia
Do akceptacji
  • ~RedHot 22 czerwca 2007 12:29
    Już razm czytałem o COM, ale teraz mam lepszy obraz , Thx .Szkoda tylko ,że błędy ortograficzne wynikające z niewiedzy (patrz : "wziąŚć" ,a powinno być wziąć).No trudno ;/
  • Artur Poznański (@artpoz) 15 lipca 2007 11:41
    RedHot: Thx za uwagi, orty właśnie poprawiłem
  • ~worm 31 sierpnia 2007 23:41
    rozumiem że to tylko wprowadzenie..więc artpoz masz +.przydałby się art jak wykorzystać,jak napisać com'a..np.w c++.pozdrawiam
  • ~programmer 03 czerwca 2008 10:13
    Serio, przydał by się kod
  • ~andymen 14 października 2008 00:28
    Jestem pod wrażeniem pomysłowości i przykladów. Wielkie dzięki
  • Zegar / Reeven (@Zegar) 01 września 2011 00:31
    Nie chcę się czepiać, ale Autor byl uprzejmy napisać:

    "Czas życia obiektu regulują dwie funkcje: AddRef i Release. Ta pierwsza zwiększa licznik odwołań, ta druga go zmniejsza. Licznik odwołań jest to liczba typu ULONG (unsigned long), która określa ile obiektów zostało utworzonych. Jeśli wartość ta spadnie do zera, obiekt jest niszczony (zwalniana jest pamięć)."

    Co jest delikatnym przejęzyczeniem. Chodzi o ilosc WSKAZNIKÓW NA OBIEKT, które zostaly uwtorzone. IUnknown zlicza ile zmiennych wskazuje na obiekt, a nastepnie w momencie w ktorym nie ma juz zadnych wskaznikow obiekt SAM SIE KASUJE.

    To tak gwoli scislosci.
  • Napisz komentarz:
    Aby dodać swój komentarz, musisz się zalogować.
Licencja Creative Commons

Warsztat używa plików cookies. | Copyright © 2006-2017 Warsztat · Kontakt · Regulamin i polityka prywatności
build #ff080b4740 (Tue Mar 25 11:39:28 CET 2014)