Создание
класса СОМ-объекта
Подключите
к проекту новый файл 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-реестр, который открыт всем ветрам — как случайным, так
и преднамеренным воздействиям со стороны человека и программы. Однако следует
согласиться с тем, что уникальная идентификация снимает проблему случайного,
но весьма вероятного совпадения имен интерфейсов, разработанных в разных частях
света. То же относится и к именам классов, библиотек типов и т. д.