Иллюстрированный самоучитель по Visual Studio.Net

         

Создание элемента типа ATL Control


Создаваемый модуль DLL будет содержать в себе элемент управления, который внедряется в окно клиентского приложения, поэтому в проект следует добавить заготовку нового СОМ-класса, обладающего функциональностью элемента типа ATL Control. В следующем уроке мы внесем в него функциональность окна OpenGL, поэтому мы назовем класс OpenGL, хотя в этом уроке элемент не будет иметь дело с библиотекой Silicon Graphics. Он будет элементом ActiveX, созданным на основе заготовки ATL. Создать вручную элемент ActiveX достаточно сложно, поэтому воспользуемся услугами еще одного мастера Studio.Net. При включении нового мастера (wizard) важно, где установлен фокус. Отметьте, что сейчас в рабочем пространстве существуют два проекта: один (ATLGL) — это DLL-сервер, а другой (ATLGLPS) — это коды заглушек proxy/stub.

  • Установите фокус на элемент ATLGL в дереве Solution Explorer и в меню Project выберите команду Add Class (при этом важно, чтобы фокус стоял на имени проекта ATLGL).

  • В окне диалога Add Class выберите категорию ATL, Templates ATL Control и нажмите кнопку Open.

  • В окне мастера ATL Control Wizard выберите вкладку Names и в поле Short Name введите OpenGL.

  • Перейдите на вкладку Attributes и установите следующие значения переключателей и флажков: Control Type: Standard Control, Aggregation: Yes, Threading Model: Apartment, Interface: Dual, Support: Connection Points.

  • Просмотрите и оставьте по умолчанию установки на вкладке Interfaces. Они сообщают о том, что создаваемый класс будет поддерживать шесть интерфейсов: IDataObject, IPersistStorage, IProvideClassInfoZ, IQuickActivate, ISpedfyPropertyPages и ISupportErrorlnfo.

  • На вкладке Miscellaneous поднимите флажок Insertable.

  • На вкладке Stock Properties найдите и добавьте свойство Fill Color, нажав кнопку Add.

  • Нажмите кнопку Finish.

    Просмотрите результаты работы мастера. Самым крупным его произведением является файл OpenGLh, который содержит объявление и одновременно коды класса COpenGL. Для ATL-проектов характерно то, что создаваемые ко-классы наследуют данные и методы от многих родителей, в число которых входят как СОМ-классы, так и интерфейсы.
    Другой характерной чертой является сосредоточение значительной части функциональности в h-файле. Напрашивается вывод, что некоторые принципы и идеи, отстаиваемые Microsoft в MFC, были инвертированы в ATL. Сколько полемического задора было растрачено в критике множественного наследования (намек на Borland OWL) на страницах документации по MFC, и вот теперь мы видим вновь созданный класс (COpenGL), который имеет 18 родителей, среди которых 5 классов и 13 интерфейсов.

    Здесь у вас опять должна закружиться голова, но не сдавайтесь. Важно не выпускать главную нить логики приложения. Резон таков: мастера настрочили уйму кода, который пока непонятен, возможно, и всегда будет таким, но этот код уже работает и нам нужно где-то встроиться в него, чтобы почувствовать возможность управлять общей логикой внедряемого элемента ActiveX. Имея под рукой Wizards Studio.Net, это можно сделать, даже оставаясь в некотором неведении относительно деталей работы интерфейсов СОМ. Вам не придется вручную реализовывать ни одного интерфейса. Вы можете сосредоточиться только на алгоритме работы самого элемента, то есть на том, что вы должны продемонстрировать пользователю вашего объекта.

    Запустите приложение, но на этот раз не закрывайте тестовый контейнер, который должен запуститься автоматически, без вашего участия. В окне тестового контейнера вы не увидите признаков нашего элемента, так как он еще не загружен. Дайте команду Edit > IhsertNew Control. После некоторой паузы, в течение которой контейнер собирает информацию из реестра обо всех элементах OLE Controls, вы увидите диалоговое окно с длинным списком элементов, о которых есть информация в реестре.

    Примечание



    Это совсем не означает, что все элементы живы и здоровы. На мой взгляд, ситуация уже вырастает в серьезную проблему. В систему следует ввести эффективные средства корректировки реестра, потому что совсем неинтересно проводить часы драгоценного времени, копаясь в реестре или инструменте типа OLE/COM Object Viewer (Просмотр объектов OLE/COM) и выясняя, жив элемент или его давно нет.


    Может быть, как говорят политики, я не владею информацией, но все программки типа CleanRegistry либо опасны, либо мало полезны и неэффективны.

    При открытом окне диалога Insert Control вы можете просто ввести букву о — начальную букву нашего элемента OpenGL. Теперь, используя клавиши навигации по списку (стрелки), быстро найдете в нем строку OpenGL Class. Выберите ее и нажмите ОК. Вы должны увидеть окно внедренного элемента, которое выглядит так, как показано на рис. 8.2.



    Рис. 8.2. Стартовая заготовка элемента ActiveX в окне тестового контейнера

    Загляните в файл ATLGLJ.c и увидите три новых макроса типа MIDL_DEFINE_GUID, которые уже выполнили свою работу и поместили в реестр множество новых записей по адресам:

    HKEY_CLASSES_ROOT\ATLGL.OpenGL\

    HKEY_CLASSES_ROOT\ATLGL.OpenGL.1\

    HKEY_CLASSES_ROOT\CLSID\

    HKEY_CLASSES_ROOT\ Interface\

    Когда клиент СОМ-объекта пользуется услугами локального или удаленного сервера, то есть когда данные передаются через границы различных процессов или между узлами сети, требуется поддержка маршалинга (marshaling). Так называется процесс упаковки и посылки параметров, передаваемых методам интерфейсов через границы потоков или процессов, который мы слегка затронули ранее. Вы помните, что MIDL генерирует код на языке С для двух компонентов: Proxy (представитель СОМ-объекта на стороне клиента) и stub (заглушка на стороне СОМ-сервера). Эти компоненты общаются между собой и решают проблемы Вавилонской башни, то есть преодолевают сложности обмена данными, возникающими из-за того, что клиент и сервер используют различные типы данных — разговаривают на разных языках. Чтобы увидеть проблему, надо ее создать. Интересно то, что при объяснении необходимости этого чудовищного сооружения:

  • idl-файл;

  • новый класс CProxy_iOpenGLEvents в вашем проекте;

  • новый проект ATLGLPS (proxy-stub) в вашем рабочем пространстве;

  • новый тип структур VARIANT, который надо использовать или просто иметь в виду,

    приводится соображение о том, что программы на разных языках программирования смогут общаться, то есть обмениваться данными.


    Как мы уже обсуждали, разработчики имеют в виду четыре языка, два из которых реально используются (Visual C++ и Visual Basic), а два других (VBScript и Visual J++) едва подают признаки жизни. Правда здесь надо учесть бурное развитие нового языка с#, который, очевидно, тоже участвует в движении СОМ.

    Откройте файл ATLGLidl и постарайтесь вникнуть в смысл новых записей, не отвлекаясь на изучение языка IDL, который потребует от вас заметных усилий и временных затрат. Прежде всего отметьте, что в библиотеке типов (library ATLGLLib), сопровождающей наш СОМ-объект, появилось описание СОМ-класса

    coclass OpenGL

    {

    [default] interface IQpenGL;

    [default, source] dispinterface _IOpenGLEvents;

    };

    который предоставляет своим пользователям два интерфейса. Я не привожу здесь предшествующий классу OpenGL блок описаний в квадратных скобках, который носит вспомогательный характер. Элементы ActiveX используют события (events) для того, чтобы уведомить приложение-контейнер об изменениях в состоянии объекта в результате действий пользователя — манипуляции посредством мыши и клавиатуры в окне объекта. Найдите описание одного из объявленных интерфейсов:

    dispinterface _IOpenGLEvents

    {

    properties:

    methods:

    };

    Пока пустые секции properties (свойства): и methods (методы): намекают на то, что мы должны приложить усилия и ввести, с помощью инструментов Studio.Net в разрабатываемый СОМ-объект способность изменять свои свойства и экспортировать методы. Информация о втором интерфейсе расположена вне блока, описывающего библиотеку типов:

    interface IQpenGL : IDispatch

    {

    [propput, bindable, requestedit, id(DISPID_FILLCOLOR)]

    HRESULT FillColor([in]OLE_COLOR clr);

    [propget, bindable, requestedit, id(DISPID_FILLCOLOR)]

    HRESULT FillColor([out, retval]OLE_COLOR* pclr);

    };




    Содержание раздела