Модель программирования Component Object Model. Разработка COM-сервера, страница 5

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

return S_OK;

}

HRESULT __stdcall CoSay::SetWord(BSTR word)

{

SysReAllocString (&m_word, word); // Повторное выделение памяти

return S_OK;

}

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

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

{

if (rclsid != CLSID_CoSay)  // Если идентификатор класса задан неправильно,

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

CoSay *pSay = new CoSay; // Создаем объект ко-класса

HRESULT hr = pSay->QueryInterface (riid, ppv); // Запрошиваем интерфейс (его адрес)

if (FAILED(hr))

delete pSay;

return hr;

}

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

extern "C" HRESULT __stdcall

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

Файл описания DLL

Для успешной работы DLL следует добавить к проекту файл ее описания (DEF-файл). Этот способ является альтернативным и, возможно, более простым, чем использование описателей __declspec(dllexport) для экспортируемых функций. DEF-файл сопровождает DLL и содержит список функций, экспортируемых ею. Создайте новый файл MyCom.def и введите в него такие строки:

LIBRARY    "MYCOM.dll"

EXPORTS

DllGetClassObject PRIVATE

Заметим, что теперь нет необходимости нумеровать экспортируемые функции (как делалось ранее).

DllGetClassObject @1 PRIVATE

При наличии DEF-файла компоновщик создает (кроме основного файла библиотеки MyCom.dll) еще два необходимых файла: MyCom.lib (заголовов экспортируемых функций) и MyCom.exp. При отсутствии последних двух файлов система не сможет обратиться к функции DllGetClassObject, а следовательно и к нашему COM-объекту CoSay. Для того, чтобы DEF-файл участвовал в процессе сборки DLL, в рамках 6-й студии его достаточно было лишь подключить к проекту. Этого шага, однако, недостаточно в рамках 7-й студии. Надо сделать еще одну установку.

1.  Установите фокус на строке MyCom в окне Solution Explorer и дайте команду View4Propertiy Pages;

2.  Раскойте узел Linker4Input в дереве левого окна диалога MyCom Property Pages и введите имя MyCom.def в строку Module Definition File списка свойств.

3.  Нажмите кнопку OK.

Следующим шагом вы должны зарегистрировать сервер, то есть внести в реестр Windows записи, которые регистрируют факт существования и местоположение DLL. При работе с ATL это действие будет автоматизировано, но сейчас создайте и подключите к проекту еще один файл MyCom.reg, формат которого соответствует командам регистрации, воспринимаемых редактором реестра RegEdit.exe.

В версии студии, с которой я имею дело, в списке типов добавляемых файлов отсутствует тип REG. Поэтому создайте текстовый файл MyCom.txt и запишите его как MyCom.reg.