Создание хранилища компонентов с помощью ATL Com AppWizard
Мастер ATL COM AppWizard предлагает на выбор три конфигурации типа сервера, используемые для создания хранилища:
· Dynamic Link Library (DLL). Мастер ATL COM AppWizard генерирует хранилище для COM DLL-сервера (внутрипроцессного). Все экспортируемые функции DLL полностью реализуются и предоставляются через ассоциированный DEF-файл.
· Executable (EXE). Мастер ATL COM AppWizard генерирует хранилище для СОМ ЕХЕ-сервера. Предоставляется реализация WinMain().
· Service (EXE). Мастер ATL COM AppWizard генерирует код хранилища для ЕХЕ-сервера, который может запускаться как сервис NT, локальный сервер или удаленный сервер.
Вставка кода заглушки/прокси-объекта.
Важна роль заглушек и прокси-объектов в модели СОМ. Если ограничивать СОМ-серверы использованием только variant-совместимых типов, то можно пользоваться процедурой универсального (библиотека типов) маршалинга, и DLL заглушки/прокси не требуется. Если использовать интерфейсы с атрибутом [oleautomation] (как и дуальные интерфейсы), то также нет необходимости выбирать эту опцию. Если же планируется создавать интерфейсы, выходящие за рамки использования variant-совместимых типов, то установка данного флажка добавит необходимые точки захвата, чтобы разместить код заглушки/прокси внутри вашего DLL-сервера — как результат, на один файл меньше распространять. Конечно, не обязательно вставлять DLL заглушки/прокси во внутрипроцессный сервер (любой DLL-сервер будет работать и без выбора этой опции). Это просто одна из возможностей проектирования СОМ-сервера.
Если флажок все-таки выбран, то мастер ATL COM AppWizard сгенерирует для проекта два дополнительных файла: dlldatax.h и dlldatax.c, так как каждый DLL-сервер в модели СОМ должен экспортировать функции DllCanUnloadNow(), DllRegisterServer(), DllUnregisterServer() и DllGetClassObject(), чтобы взаимодействовать с процедурой COM Runtime.
При создании СОМ можно выделить основные задачи, решаемые ATL:
· Предоставляет для коклассов реализацию по умолчанию интерфейса IUnknown. Эта поддержка осуществляется с помощью ССomObjectRootBase, CComObjectRootEx<> и СОМ-карты кокласса.
· Предоставляет родовую фабрику класса для создания объектов, при этом шаблон CComСoСlass<> определяет родовую фабрику класса.
· Обеспечивает поддержку саморегистрации для каждого кокласса сервера. Это поддерживается с помощью реестрового скриптового файла сервера, класса CComModule и нескольких регистрационных макросов.
Простой кокласс явно производится от двух шаблонов ATL. Эти шаблоны задают потоковую модель для кокласса, уровень поддержки агрегирования, ассоциированную фабрику класса, а также поддержку IUnknown.
4.1.6 Класс CComObjectRootEx<>
Класс CComObjectRootEx<> произведен от другого класса ATL, CComObjectRootBase. Эти два класса предоставляют вспомогательные функции, вызываемые реализацией IUnknown кокласса. Кокласс не является самым последним произведенным классом, а передается как параметр шаблона в другой класс ATL (как правило, CComObject<>), который реализует три метода интерфейса IUnknown.
Классы CComObjectRootBase и CComObjectRootEx<> предоставляют функции, доступ к которым производится CComObject<> для того, чтобы завершить реализации Querylnterface(), AddRef() и Release(). CComObjectRootBase задает поведение Querylnterface(). CComObjectRootEx<> расширяет спецификацию IUnknown, добавляя в него вспомогательные функции, используемые в реализации AddRef() и Release().
Единственным параметром шаблона, передаваемым в CComObjectRootEx<>, является класс потоковой модели, определяемый в ATL. Используя этот параметр, реализация счетчика ссылок объекта демонстрирует безопасность потока. Параметр шаблона, принимаемый CComObjectRootEx<>, определяется выбором типа модели потока (Threading Model) на вкладке Attributes мастера ATL Object Wizard.
ATL COM-карта
Чтобы реализация IUnknown средствами ATL функционировала корректно, кокласс должен определить так называемую СОМ-карту (COM map). COM-карта задается с использованием макросов begin_com_map и end_com_map. COM-карта объекта содержит список всех интерфейсов, поддерживаемых коклассом. Если не внести интерфейс в СОМ-карту, то клиент не сможет получить доступ к нему через Querylnterface. IUnknown представляет здесь исключение, поскольку его указатель рассчитывается неявно с помощью первого входа в СОМ-карту, и он не обнаружится в списке.
ATL СОМ-карта может включать в себя семнадцать различных интерфейсных. Многие из них потребуются только в случае, если кокласс построен с использованием сложных техник СОМ, таких как агрегирование или "сбрасываемые" интерфейсы. Наиболее популярным является com_interface_entry, который возвращает vPtr для указанного интерфейса. Надо отметить, что макрос com_interface_entry принимает IDL-имя интерфейса, а не соответствующие интерфейсам IID.
4.1.8 Класс CComCoClass<>
Второй шаблон ATL, от которого производится кокласс, задает объект класса для класса. Определение CComCoClass<> задает макрос declare_ classfactory, который при развертывании специфицирует фабрику класса по умолчанию, реализующую IClassFactory. Кроме того, CComCoClass<> определяет поведение кокласса в случае, если другой объект просит его служить составным (aggregate).
Агрегация — это способ повторного использования объектов в СОМ, при котором внешний объект создает и хранит внутренний объект. Интерфейсы внутреннего объекта экспонируются внешним объектом, создавая иллюзию, что внешний объект состоит из большего числа интерфейсов, чем он имеет в реальности. CComCoClass<> определяет макрос DECLARE_AGGREGATABLE, который сообщает, что кокласс может функционировать как составной (aggregated) объект, если поступит соответствующий запрос.
Класс CComModule
Саморегистрирующийся СОМ-сервер знает, что делать, когда ему поступает команда зарегистрироваться или снять регистрацию. В ATL класс CComModule отвечает за внесение (удаление) информации в реестр для каждого кокласса, перечисленного в карте объектов. Выполняя процедуру регистрации, CComModule пишет макрос declare_registry_re source id в заголовочном файле кокласса. При раскрытии этот макрос предоставляет реализацию метода UpdateRegistry(), и вызывается классом CComModule во время регистрации и снятии регистрации ваших коклассов. Единственным параметром declare_registry_resourceid является ID ресурса серверного двоичного RGS-файла. Если посмотреть вкладку ResourceView, то обнаружится, что появился новый подкаталог ресурса REGISTRY.