Работа с Visual Studio.Net

         

Создание класса СОМ-объекта


Подключите к проекту новый файл MyCom.h, в который надо поместить объявление класса CoSay. Как вы помните, он должен быть потомком экспортируемого интерфейса iSay и дать тела всем методам, унаследованным от всех своих абстрактных предков (isay, lUnknown). Введите в файл следующие коды:

#if !defined(MY_COSAY_HEADER)

#define MY_COSAY_HEADER

#pragma once


class CoSay : public ISay

{

//=====Класс, реализующий интерфейсы ISay, lUnknown

public:

CoSay () ;

virtual -CoSay();

// lUnknown

HRESULT _stdcall Querylnterface(REFIID riid, void** ppv);

ULONG _stdcall AddRefO;

ULONG _stdcall Release ();

// ISay

HRESULT _stdcall Say();

HRESULT _stdcall SetWord (BSTR word);

private:

//====== Счетчик числа пользователей классом

ULONG m_ref; , //====== Текст, выводимый в окно

BSTR m word;

};

#endif

Для реализации тел методов класса CoSay подключите к проекту новый файл МуСоm. срр, в который введите коды, приведенные ниже. Обратите внимание на то, как принято работать со строками текста типа BSTR:

#include "interfaces.h"

#include "MyCom.h"

//====== Произвольный ограничитель длины строк

#define MAX_LENGTH 128

CoSay::CoSay()

{

//=== Обнуляем счетчик числа пользователей класса,

//=== так как интерфейс пока не используется

m_ref = 0;

//=== Динамически создаем строку текста по умолчанию

m_word = SysAllocString (L"Hi, there."

"This is MyCom speaking");

}

CoSay::-CoSay()

{

//=== При завершении работы освобождаем память

if (m_word)

SysFreeString(m_word);

}

//====== Реализация методов lUnknown

HRESULT _stdcall CoSay::QueryInterface(REFIID riid, void** ppv)

{

//====== Стандартная логика работы с клиентом

//====== Поддерживаем только два интерфейса

*ppv = 0;

if (riid==IID_IUnknown)

*ppv = static_cast<IUnknown*>(this) ;

else if (riid==IID_ISay)

*ppv = static_cast<ISay*>(this) ;

else

return E_NOINTERFACE;

//====== Есть пользователи нашим объектом

AddRef();

return S_OK;

}

ULONG _stdcall CoSay:-.AddRef ()

{

return ++m_ref;

}

ULONG _stdcall CoSay::Release()

{

if (--m_ref==0) delete this;

return m_re f;

}

//====== Реализация методов ISay

HRESULT _stdcall CoSay::Say()

{

//=== Преобразование типов (из BSTR в char*), которое

//=== необходимо для использования MessageBox

char buff[MAX_LENGTH];

WideCharToMultiByte(CP_ACP, 0, m_word, -1, buff, MAX_LENGTH, 0, 0);

MessageBox (0, buff, "Interface ISay:", MB_OK);

return S_OK;

}

HRESULT _stdcall CoSay::SetWord(BSTR word)

{

//====== Повторное выделение памяти

SysReAllocString (&m_word, word);

freturn S_OK;

}

Класс, поддерживающий интерфейс, готов. Теперь следует сделать доступным для пользователей СОМ-объекта весь DLL-сервер, где живет ко-класс CoSay. Минимальным набором функций, которые должна экспортировать COM DLL, является реализация только одной функции DllGetClassObject. Обычно ее сопровождают еще три функции, но в данный момент мы рассматриваем лишь минимальный набор. DLL должна создать СОМ-объект и позволить работать с ним, получив, то есть записав по адресу ppv, адрес зарегистрированного интерфейса. Вы, конечно, заметили, что в предложении дважды использовано слово адрес. Именно поэтому параметр ppv имеет тип void** . Введите эту функцию в конец файла МуСот.срр:

STDAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, void** ppv)

{

//=== Если идентификатор класса задан неправильно,

if (rclsid != CLSID_CoSay)

// возвращаем код ошибки с указанием причины неудачи

return CLASS_E_CLASSNOTAVAILABLE;

//====== Создаем объект ко-класса

CoSay *pSay = new CoSay;

//=== Пытаемся получить адрес запрошенного интерфейса

HRESULT hr = pSay->Query!nterface (riid, ppv) ;

if (FAILED(hr))

delete pSay;

return hr;

}

Макроподстановка STDAPI при разворачивании превратится в

extern "С" HRESULT stdcall

Примечание

Работа по опознаванию объектов идет с идентификаторами класса (rclsid) и интерфейса (riid). Это является, как считают апологеты СОМ, одной из самых важных черт, которые вносят небывалый уровень надежности в функционирование СОМ-приложений. Весьма спорное утверждение, так как центром всей вселенной как разработчика, так и пользователя становится Windows-реестр, который открыт всем ветрам — как случайным, так и преднамеренным воздействиям со стороны человека и программы. Однако следует согласиться с тем, что уникальная идентификация снимает проблему случайного, но весьма вероятного совпадения имен интерфейсов, разработанных в разных частях света. То же относится и к именам классов, библиотек типов и т. д.

Содержание раздела