DCOM

Создание локального или удаленного объекта

Технология (Microsoft Distributed Component Object) позволяет взаимодействовать компонентам, расположенным на разных компьютерах.

 

Взаимодействие между клиентом и компонентом, расположенных не разных компьютерах представлено на следующей схеме.

 

 

 

Для создания удаленного компонента можно использовать функцию CoCreateInstanceEx, которая имеет следующее формальное описание:

HRESULT CoCreateInstanceEx(

            REFCLSID rclsid,

            IUnknown* punkOuter,

            DWORD dwClsCtx,

            COSERVERINFO* pServerInfo,

             ULONG cmq,

            MULTI_QI* pResults           // Структура MULTI_QI имеет три члена:

                                                           //идентификатор интерфейса – pIID,

                                                           // указатель на возвращаемый интерфейс – pItf

                                                           // и значение, возвращаемое при вызове

                                                           //метода QueryInterface – hr

);

Структура MULTI_QI определяется как:

typedef struct tagMULTI_QI

    {

            const IID *pIID;

            IUnknown *pItf;

            HRESULT hr;

    } MULTI_QI;

 

Например:

MULTI_QI mqi[]={     {IID_I1, NULL , hr1},

                                    {IID_I2, NULL, hr2} };

// Выполним соединение с сервером "MyeServer.ru"

COSERVERINFO info = {0, L"MyServer.ru", NULL, 0};

// Создание объекта и запрос двух интерфейсов

HRESULT hr=CoCreateInstanceEx(

            CLSID_My1,                // Запрос экземпляра класса CLSID_My1.

            NULL,                            // Без агрегирования

            CLSCTX_SERVER,       //Подходит любой найденный сервер.

            &info,                        // Содержит имя удаленного сервера

            sizeof(mqi)/sizeof(mqi[0]),    // Количество запрашиваемых интерфейсов (2)

            &mqi);                 // Структура определяющая IID  и указатели на интерфейс

if (SUCCEEDED(hr))

{ if (SUCCEEDED(mqi[0].hr))

    {   I1* p1=mqi[0].pItf;                  // Получаем первый указатель на интерфейс

        hr=p1->Fx1();                            // Используем указатель на интерфейс

        p1->Release();                            // Удаляем указатель на интерфейс

    }

    if (SUCCEEDED(mqi[1].hr))

    {  LPWSTR pParam=NULL;

        I2* p2=mqi[1].pItf;    // Получаем второй указатель на интерфейс

        hr=p2->Fy1(&pParam);    // Используем указатель на интерфейс

        p2->Release();                    // Удаляем указатель на интерфейс

    }

}

 Включение и агрегирование внутренних компонентов

Включение и агрегирование компонентов позволяет настраивать и подстраивать уже существующие компоненты.

При включении:

§  внешний компонент содержит указатели на интерфейсы внутреннего компонента;

§  внешний компонент является клиентом внутреннего компонента – используя интерфейсы последнего он реализует свои собственные интерфейсы;

§  внешний компонент может заново реализовать интерфейс, поддерживаемый внутренним компонентом, передавая последнему вызовы этого интерфейса;

§  внешний компонент может расширить этот интерфейс, добавляя свой код перед вызовом внутреннего компонента и после него.

На следующей схеме показано включение внутреннего компонента, предоставляющее доступ к его интерфейсу.

                                                Внешний компонент

 

 

 

 

Интерфейс IX

 

 

 

 

 

Интерфейс IY

 

 

 

 

 

Внутренний компонент

 

 

 

 

 

Интерфейс IZ

 

 

 

 

 

 

 

 

 

Агрегирование – это особый вид включения. При агрегировании:

§  внешний компонент:  
  – не реализует заново интерфейс внутреннего компонента,      
  – не передает внутреннему компоненту вызовы этого интерфейса явно;

§  внешний компонент передает указатель на интерфейс внутреннего компонента непосредственно клиенту;

§  клиент далее напрямую вызывает методы интерфейса внутреннего компонента.

Это освобождает внешний компонент от повторной реализации функций интерфейса и передачи вызовов внутреннему компоненту.

Недостаток агрегирования в том, что нельзя специализировать функции интерфейса.

Клиент должен думать, что работает с одним компонентом. Это реализуется через QueryInterface.

Реализация включения выполняется только для внешнего компонента – остальные ничего не знают.

Для реализации включения можно выполнить следующее:

1.    Создать внутренний компонент в коде внешнего, используя CoCreateInstance – инициализировать  внешний компонент
  iY* m_pIY;    // Указатель на интерфейс включаемого компонента
HRESULT __stdcall CFactory::CreateInstance(IUnknown* pOuter,
                                                       const IID& iid, 
                                                       void** ppv) {
       if (pOuter != NULL ) return CLASS_E_NOAGRERATION;
        ClassA* pA=new ClassA;
        HRESULT hr=pA-> CoCreateIstance(
                                           CLSID_Comp2, //  для CLSID внутреннего компонента
                                           NULL,          
                                           CLSCTX_INPROC_SERVER,      
                                            IID_IY, (void**)&m_pIY)
                                                       
        hr=pA->QueryInterface(iid,ppv);
        pA->Release;
        return hr;  }

2.    Сохранить в переменной указатель на интерфейс IY внутреннего компонента

3.    Когда клиент запрашивает у внешнего компонента интерфейс IY, то тот возвращает свой интерфейс

4.    Когда клиент вызывает метод интерфейса IY, то внешний компонент передает вызов внутреннему компоненту 
virtual void Fy1() {m_pIY->Fy1();}

5.    Когда внешний компонент завершает работу, то его деструктор вызывает
m_pIY->Release() для внутреннего компонента.

 

Расширение интерфейса позволяет добавить к интерфейсу свой код, но это очень трудоемко для "больших" интерфейсов.

Например:

// Интерфейс  – реализован в компоненте MyClassA

interface ICA : IUnknown

{ void F1();  void F2();  void F3();  } ;

// Интерфейс

Interface ICB : ICA

{ void Fb1();  void Fb2();  void Fb3();  } ;

Внешний компонент может включить MyClassA и использовать его интерфейс ICA для реализации членов ICA, наследующих этот интерфейс

            void MyClassB::F1()     { m_pICA->F1();}

 

Агрегирование можно рассматривать как динамический вид наследования. Внешний компонент не может напрямую передавать указатель ни интерфейс внутреннего компонента. Решением может служить то, что компонент, который будет агрегироваться, может иметь два интерфейса  IUnknown: обычный и используемый для передачи вызовов внешнему компоненту.

Так, для реализации агрегирования внутренний компонент реализует два интерфейса IUnknown

§  делегирующий – передает вызовы функций членов IUnknown либо внешнему IUnknown либо неделегирующему IUnknown.

§  неделегирующий – обычный IUnknown.

 

Объявление внешнего компонента, который реализует интерфейс IX, а IY внутреннего компонента предоставляет через агрегирование:

Основные действия внешнего

компонента происходят внутри его функции

QueryInterface, которая возвращает

указатель на интерфейс внутреннего объекта

class CA: public IX

            { public

            // IUnknown

            virtual HRESULT __stdcall QueryInterface(const IID& iid, void** ppv);

            virtual ULONG __stdcall AddRef();

            virtual ULONG __stdcall Release();

            // Интерфейс IX

            virtual void __stdcall Fx() {cout<< "   "<<endl;}

            // Конструктор и деструктор

            CA();

~CA();

 // Инициализация – создание включаемого компонента

      HRESULT Init();  // Произвольная функция, создающая внутренний компонент

            private:

            // Счетчик ссылок

            long m_cRef;

            // Указатель на IUnknown внутреннего компонента

            IUnknown*  m_pInnerUnknown

// Функция QueryInterface внешнего компонента возвращает указатель на

// интерфейс внутреннего объекта

HRESULT __stdcall CA::QueryInterface(const IID& iid, void** ppv)

            { // …

    if (iid == IID_IX) {*ppv = (IX*)this; }

    else if (iid == IID_IY) {return

                                         m_pInnerUnknown-> QueryInterface(iid,ppv) ; }

 

Интерфейсы внутреннего компонента должны использовать интерфейс IUnknown, реализованный внешним компонентом.

Интерфейс IUnknown внутреннего компонента должен быть скрыт от клиента. Для этого:

§  внутренний компонент должен знать, что он агрегируется и иметь указатель на внешний IUnknown;

§  передавать внутреннему компоненту вызовы своего IUnknown внешнему компоненту.

 

Внешний IUnknown:

            HRESULT __stdcall CoCreateInstance(

                                               const CLSID& clsid,

                                               IUnkown* pIUnknownOuter,   // Внешний компонент

                                                         // передает указатель внутреннему компоненту

                                               DWORD dwClsContext, // Контекст сервера

                                               const IID& iid,

                                               void** ppv);

                HRESULT __stdcall CreateInstance(    .      // IClassFactoty::CreateInstance

                                               IUnkown* pIUnknownOuter,   // Внешний компонент

                                               const IID& iid,

                                               void** ppv);

Внешний компонент передает указатель на свой интерфейс IUnknown внутреннему компоненту. Если этот указатель не равен NULL, то компонент агрегируется. (Внутренний компонент определяет агрегируется ли он по этому указателю). Если внутренний компонент агрегируется, то делегирующий IUnknown передает вызовы внешнему IUnknown, реализованному внешним компонентом.

Клиенты агрегата (внешний+внутрренний) вызывают делегирующий IUnknown,

а внешний компонент работает с внутренним через неделегируемый интерфейс. При этом:

§  Неделегирующий IUnknown передает вызовы обычным образом;

§  Делегирующий IUnknown внутреннего компонента передает вызовы:

o   внешнему IUnknown,

o   неделегирующему IUnknown.

Это проиллюстрировано на следующей схеме:

Внешний компонент

 

 

 

 

 

 

 

QueryInterface

AddRef

Release

Fx

 

  ®

Реализация внешнего IUnknown

 

 

 

 

 

                     Код управления 

              внутренним компонентом

 

 

 

 

 

 

 

 

 

Внутренний компонент

 

 

 

 

 

QueryInterface

 

 ­  (если есть агрегирование)

 

 

 

AddRef

  ®

Реализация делегирующего IUnknown

 

 

 

Release

 

?(нет агрегирования)

 

 

 

Fx

 

Реализация неделегирующего IUnknown

 

 

 

 

 

 

 

 

Реализация неделегирующего IUnknown:

§  Внутренний компонент использует реализацию IUnknown внешнего компонента;

§  Внешний компонент вызывает неделегирующий IUnknown для управления временем жихни внутреннего компонента.

Т.к. имя интерфейса не имеет значение, а важна только структура памяти, то назовем неделегирующий IUnknown – INondelUnknown.

Объявим

            struct INondelUnknown

            { virtual HRESLT __stdcall NondelQueryInterface(…)=0;

             virtual ULONG __stdcall NondelAddRef()=0;

            virtual ULONG __stdcall NondelReleace()=0;

}

Изменения будут только в реализации новой NondelQueryInterface:

HRESLT __stdcall CB::NondelQueryInterface (const IID& iid, void** ppv)

{ if (iid == IID_IUnknown)

{*ppv = (INondelUnknown *)this; }      // Приведение типа

            // гарантирует, что будет вызван неделегирующий IUnknown

            else if (iid == IID_IY)

{*ppv = (IY*)this; }

            // …

Неделигирующий IUnknown ( INondelUnknown) всегда возвращает указатель на себя.

Указатель на неделегирующий. IUnknown передается только внешнему компоненту.

При реализации делегирующего IUnknown вызовы передаются либо:

§  внешнему IUnknown   (если компонент агрегируется);

§  неделегирующему IUnknown.

 

Например:

  // m_pOuterUnknown – указатель на внешний интерфейс

class CB: public IY, public INondelUnknown

{

 public:

            // Делегирующий IUnknown

            . virtual HRESLT __stdcall QueryInterface(…)   {

             return m_pOuterUnknown->QueryInterface();}  // Если есть делегирование

            { return m_pOuterUnknown-> QueryInterface(iid,ppv);}

             virtual ULONG __stdcall AddRef()  { return m_pOuterUnknown-> AddRef(); }

            virtual ULONG __stdcall Releace() { return m_pOuterUnknown-> Release();}

}

// Нелегирующий IUnknown

            . virtual HRESLT __stdcall NondelQueryInterface(…);

             virtual ULONG __stdcall NondelAddRef()  ;

            virtual ULONG __stdcall NondelReleace() ;

// Интерфейс IY

            virtual void __stdcall Fy() { … ;}

// Конструктори деструктор

CB  (IUnknown* m_pOuterUnknown);  // Указатель на внешний интерфейс,

                                                                 // используемый при создании компонента

~CB();

private:

            long m_cRef;

            IUnknown* m_pOuterUnknown

Обобщим этапы реализации компонента, использующего агрегирование:

1.    Реализуем внутренний компонент с двумя интерфейсами IUnknown
Для делегирующего интерфейся выполняем переадресацию на  внешний интерфейс (m_pOuterUnknown)    (во внешнем компоненте)
В клиенте создаем внутренний компонент, передавая ему одновременно указатель на внешний IUnknown. Используем для этого функцию CoCreateInstance       
      HRESULT CA::Init()
      { IUnknow* pOuterUnknown = this;
      HRESULT hr= CoCreateInstance(CLSID_Comp2, pOuterUnknown,
                               CLSCTX_INPROC_SERVER, IDD_IUnknown,
                               (void**)& m_pInnerUnknown);   //  Когда компонент
                                                      // создается как внутр. и агрег-ый он
                                                     // может возвращать только IUnknown
… return S_OK; }

2.    Реализация IClassFactory::CreateInstance внешнего компонента вызывает метод, создающий внутренний компонент.

3.    Фабрика класса внутреннего компонента изменена:
– IClassFactory::CreateInstance использует INondelUnknown вместо IUnknown
– функция вызывает не QueryInterface, а NondelQueryInterface
– ФК возвращает указатель на неделегирующий QueryInterface
          HRESULT hr=pB= NondelQueryInterface(iid,ppv);
           pB->NondelRelease(); return hr;

4.    Указатель на внешний IUnknown передается конструктору внутреннего компонента. Конструктор инициализирует m_pOuterUnknown. Затем эта переменная используется делегирующим IUnknown для передачи вызовов (либо неделегирующему IUnknown либо внешнему IUnknown).
Если компонент не агрегируется, то конструктор помещает в m_pOuterUnknown указатель на неделегрующий IUnknown. (т.е. равно или (IUnknown*)this или параметру).

5.    Т.к. внешний компонент при агрегировании может запрашивать только интерфейс IUnknown, то для запроса интерфейса внутреннего компонента используется QueryInterface.

Вы можете следить за любыми ответами на эту запись через RSS 2.0 ленту. Вы можете оставить ответ, или trackback с вашего собственного сайта.

Оставьте отзыв

XHTML: Вы можете использовать следующие теги: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

 
Rambler's Top100