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