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

class ATL_NO_VTABLE CSay :

public CComObjectRootEx<CComSingleThreadModel>,

public CComCoClass<CSay, &CLSID_Say>,

public ISay

{

public:

CSay() { }

DECLARE_REGISTRY_RESOURCEID(IDR_SAY)

DECLARE_NOT_AGGREGATABLE(CSay)

BEGIN_COM_MAP(CSay)

COM_INTERFACE_ENTRY(ISay)

END_COM_MAP()

DECLARE_PROTECT_FINAL_CONSTRUCT()

HRESULT FinalConstruct()

{

return S_OK;

}

void FinalRelease(){  }

public:

};

Первые два класса предоставил мастер библиотеки ATL. Шаблон классов CComObjectRootEx кроит родительский класс по образцу и подобию класса CComSingleThreadModel. От него CSay автоматически наследует реализацию методов IUnknown. Второй класс (CComCoClass) реализует функциональность IClassFactory. Кроме того, по умолчанию он объявляет всему миру, что в класс CSay может быть вложен (агрегирован) какой-то другой ко-класс. Но наличие макроса DECLARE_NOT_AGGREGATABLE опровергает эту установку. Он говорит о том, что в наш объект нельзя агрегировать другие ко-классы.

Далее следует пустое тело конструктора. Здесь уместно наполнить его кодом инициализации строки текста, которую выводит наш тестовый сервер:

CoSay() { m_word = "Hi, there. This is ATL test speaking"; }

Для того, чтобы присвоение имело смысл, введите в состав CoSay новый элемент private-данных:

private: CComBSTR m_word;

Новый для вас класс CComBSTR, хоть и не такой мощный как CString в MFC, но все же здорово упрощает жизнь в рамках ATL. Возвращаясь к анализу класса CSay, отметим, что макросы, которые вы видите после конструктора, работают примерно по тому же принципу, что и в MFC-классах. Например, COM-карта содержит список всех интерфейсов, поддерживаемых классом. Мы экспонируем лишь один. Именно здесь происходит связывание интерфейса ISay с его предком IUnknown. Если из списка:

BEGIN_COM_MAP(CSay)

COM_INTERFACE_ENTRY(ISay)

END_COM_MAP()

убрать COM_INTERFACE_ENTRY(ISay), то клиент не сможет получить адрес интерфейса ISay с помощью метода QueryInterface. Чтобы понять смысл макроса DECLARE_REGISTRY_RESOURCEID (IDR_SAY), откройте дерево ресурсов и рассмотреть новый узел "REGISTRY", а также его элемент IDR_SAY. Это — сценарий или, как теперь принято говорить, скрипт процедуры регистрации. В нем содержится та же логика, что и в файле Say.rgs. Но, в отличие от файла Say.rgs, ресурс IDR_SAY не предназначен для ручного редактирования.

Еще один макрос DECLARE_PROTECT_FINAL_CONSTRUCT содержит код, который защищает COM-объект от уничтожения в случае, когда вложенный (агрегированный) объект обнуляет счетчик числа пользователей внешнего объекта.

Вы можете построить DLL, дав команду Build4Build ATest. Затем вы можете даже запустить приложение (Ctrl+F5). В этот момент студия запросит имя exe-файла, то есть модуля или процесса, в пространство которого должна быть загружена созданная компоновщиком DLL. Воспользуйтесь выпадающим списком для выбора стандартного контейнера для отладки элементов ActiveX (tstcon32.exe), поставляемого вместе со студией по адресу: ..\Microsoft Visual Studio\ Common\ Tools.

В рамках тестового контейнера можно отлаживать работу элементов ActiveX, OLE-controls и других COM-объектов. Дайте команду Edit4Insert New Control. После некоторой паузы, в течение которой контейнер собирает информацию из реестра обо всех элементах OLE Controls, вы увидите диалог с длинным списком элементов, о которых есть информация. Однако, в окне тестового контейнера вы не увидите признаков нашего элемента, так как он не элемент управления, а всего лишь "Simple COM-Object".

Добавление метода

Процедура вставки нового метода в существующий интерфейс в рамках проекта ATL тоже автоматизирована. Поставьте фокус на интерфейс ISay в дереве классов и вызовите контекстное меню. Затем дайте команду Add Method.  В поле Method Name введите имя метода Say. Если хотите, то нажмите кнопку Attributes и подправьте helpstring, заменив ее, например, на "Launches Message Box". После этого нажмите Finish.