Размещение управляющих элементов при помощи CWnd
Одесса 2004
Методические указания к лабораторным работам по курсу «Программное обеспечение сетей ЭВМ. Часть 4. Версия 2. Развитие схемы «клиент-сервер» в COM и CORBA» для студентов специальностей 7.080403 и 7.091501. / Сост. А.Н. Мартынюк. – Одесса: ОНПУ, 2002. - 105 с.
Составитель: А.Н. Мартынюк,
ст. преподаватель кафедры «Компьютерных интеллектуальных систем и сетей»
ВВЕДЕНИЕ
Методические указания к лабораторным работам подготовлены в соответствии с программой курса «Программное обеспечение сетей ЭВМ», изучаемого студентами специальностей 7.080403 и 7.091501.
Целью данных методических указаний является закрепление и дополнение лекционного материала, а также выработка у студентов навыков решения новых задач программирования с использованием технологий COM и CORBA, развивающих традиционные схемы «клиент-сервер».
Для задач лабораторных работ приводятся основные теоретические и справочные положения, а также общие и индивидуальные задания для самостоятельной работы студентов.
Число индивидуальных заданий определяется числом студентов в группе, числом рабочих станций в сетевых учебных лабораториях кафедры «Компьютерных интеллектуальных систем и сетей». Индивидуальный вариант выбирается на основе номера зачетной книжки или номера в деканатской ведомости.
В ходе самостоятельной работы, предшествующей лабораторным работам, производится изучение лекционного и дополнительного материала, подготовка и анализ решений задач лабораторных работ.
В начале каждой лабораторной работы производится индивидуальный контроль теоретической подготовленности, результатом которого является допуск к работе. В ходе работы каждый студент выполняет задание в установленные сроки и оформляет протокол с результатами работы. Контроль знаний, защита работающей программы и защита протокола производятся для каждого студента индивидуально.
Лабораторная работа № 1. Интеграция COM-компонентов в Visual C++
В лабораторной работе рассматривается быстрое применение готовые компоненты с использованием стандартных возможностей MFC, компилятора Visual C++ и директивы #import.
Необходимость использования компонент
Причин использования компонент несколько. Применение компонент сторонних производителей расширяет возможности программы без затрат на разработку кода.
Например, нужно, чтобы приложение обрабатывало изображения. Все, что необходимо – это приобрести компонент, реализующий нужные возможности, затем при помощи описанных методов внедрить его в программу. Далее, возможно просто вызывать нужные методы компонента. Возможно в итоге не придется писать и строчки кода для обработки графической информации.
Может появиться потребность воспользоваться некоторыми новыми компонентами расширений операционной системы, таких как DirectX Media или ActiveX Data Objects, или уже ранее разработан компонент, который нужно интегрировать в приложения для выполнения необходимых операций. Поставленной цели можно добиться несколькими способами. Один из наиболее быстрых способов показан ниже в тестовом приложении.
Методы встраивания компонентов
Чтобы понять целесообразность использования MFC и директивы #import для внедрения компонентов, необходимо знать возможные пути решения этой задачи.
Например, можно написать код, использующий только возможности C++ и COM, который создаст компонент и будет его использовать; в этом случае следует разобраться с концепцией COM-компонентов и написать фрагмент повторяющегося и подверженного ошибкам кода. Возможно использовать и классы ATL, но помимо знания COM необходимо знать и ATL. Можно использовать и возможность работы с контейнерами ActiveX, заложенную в «мастер» создания MFC-приложений, но если не хватит возможностей, предлагаемых меню Insert Object, придется изучать код поддержки OLE в MFC. Более простой подход - использовать классы-оболочки компонентов, генерируемые библиотекой компонентов и элементов управления (Components and Controls Library). Но эти классы целиком зависят от поддержки автоматизации, которая есть не во всех компонентах. Интерфейсы автоматизации могут также понизить и производительность.
В лабораторной работе рассмотрен подход, требующий написания минимального кода и минимального знания COM. В нем используется комбинация возможностей MFC и директивы #import. Рассмотрено использование данного подхода для создания компонентов, программного взаимодействия с ними и организации взаимодействия одних компонентов с другими.
Основы COM
Для эффективного использования COM-компонентов следует разобраться в механизме работы и программирования таких компонентов.
Ниже рассмотрены базовые положения. Возможности компонентов COM реализуются по схеме клиент-сервер. Приложение (или другой компонент) получают доступ к компоненту и используют его возможности в роли клиента, а сам компонент выступает в роли сервера. COM определяет правила взаимодействия клиента и сервера на уровне исполняемого кода, что и позволяет клиентам, написанным на одном языке, взаимодействовать с серверами, написанными на других языках. Клиент взаимодействует с компонентом только посредством интерфейса. Компонент может иметь любое количество интерфейсов, и каждый интерфейс может иметь любое количество определенных в нем методов. Возможности взаимодействия компонента полностью определяются методами его интерфейсов.
Все интерфейсы COM-компонента унаследованы от интерфейса IUnknown. Различие между интерфейсами заключается в том, поддерживают ли они автоматизацию или нет. Интерфейсы с поддержкой автоматизации унаследованы от интерфейса IDispatch, который, в свою очередь, унаследован от IUnknown. Такое наследование влияет на метод доступа к интерфейсу из C++ и других языков, а также определяет, доступен ли интерфейс из таких языков, как JScript или VBScript, которые не могут напрямую манипулировать указателями. Основное следствие наличия автоматизации у интерфейса при использовании C++ - механизм определения возможностей компонента (то есть, какие методы реализованы в данном интерфейсе) во время выполнения. Такая гибкость приводит к проигрышу в производительности, однако, она сильно расширяет совместимость компонента с приложениями, написанными на других языках.
Надо понимать, каким образом компонент делает доступными свои свойства. Доступ к свойствам компонента позволяет контролировать поведение компонента. Обычно доступ к свойствам осуществляется при помощи методов интерфейса, и эти свойства не обязательно в точности соответствуют внутренним структурам данных компонента, хотя может создаться впечатление, что получен доступ к публичным членам класса.
Типы компонентов
Одна из причин отсутствия простого описания COM - наличие большого количества "стандартных" интерфейсов. IUnknown и IDispatch - наиболее распространенные, однако, есть и другие. Одни определяют взаимодействие компонента с контейнером, при размещении компонента в клиентской области окна приложения, аналогично визуальным элементам управления. Другие - работают аналогично элементам управления, размещаемым на Web-страницах, которые взаимодействуют с браузером. В дополнение к этому существует еще и путаница в терминах: COM, ActiveX, OLE, элементы управления и компоненты: Чтобы не путаться в возможных вариантах, следует ввести следующую классификацию компонентов: невизуальные компоненты, визуальные компоненты (которые будут называться элементами управления) и компоненты, уведомляющие клиента о наступлении событий. Между этими упрощенными группами возможны пересечения, и существует много других вариантов разбиения компонентов на группы в зависимости от их интерфейсов.
Под невизуальными компонентами подразумевают COM-компоненты, которые не имеют пользовательского интерфейса, что упрощает их интеграцию и использование. Обращение к функциональности компонента осуществляется через бинарный интерфейс, удовлетворяющий требованиям COM, что позволяет совмещать приложения и компоненты, написанные на разных языках. Более того, взаимодействие программы и компонента будет выполняться даже в случае, если пользователь установил более новую версию того же компонента, нежели та, которая использовалась при разработке программы. Последнее будет выполняться только в том случае, если разработчик компонента строго следовал описанным им соглашениям по бинарному интерфейсу.
Невизуальные компоненты чаще всего предоставляют некоторые вспомогательные функции, например, возможность распознавания речи для голосового управления или вычисление определенных показателей в финансовом приложении. Независимо от того, что делают невизуальные компоненты, процесс их интеграции и использования одинаков. Создается экземпляр компонента, получается интерфейс, содержащий необходимые методы, и вызываются эти методы для выполнения требуемых функций.
Работа с визуальными компонентами, или элементами управления, сложнее, поскольку нужно обеспечить контейнер, содержащий компонент, некоторой информацией о компоненте, так как компонент становится дочерним окном контейнера. Нужно также передавать некоторую информацию и от контейнера обратно компоненту, поскольку последнему нужно знать, как отрисовывать себя и как взаимодействовать с содержащим его контейнером. В приводимом ниже примере используются некоторые возможности класса CWnd библиотеки MFC, автоматизирующие большинство необходимых операций.
Визуальные COM-компоненты чаще называют управляющим элементами ActiveX или OLE (здесь используется этот термин только по отношению к компонентам, реагирующим на ввод пользователя). Примерами визуальных компонентов могут служить некоторые специфические элементы управления, например, циферблат, шкала, или компоненты, содержащие некоторые графические элементы, например индикатор радара.
Третий тип компонентов, о которых здесь пойдет речь, - это компоненты, уведомляющие клиента через точки взаимодействия (connection points) о наступлении событий (например, о нажатии кнопки мыши или о выполнении определенной вычислительной задачи), либо вызывающие определенные методы клиента для завершения выполнения каких-либо задач (в этом случае клиент обычно тоже является компонентом). В приводимом ниже примере видно, как использовать компоненты в обоих случаях.
Служба COM+ Event Service в Windows 2000 вводит абсолютно новые возможности маршрутизации и обработки сообщений от компонентов. По сравнению с традиционным механизмом, использующим для этих целей точки взаимодействия, новая модель более приспособлена к использованию в распределенных вычислениях и позволяет отделить компоненты от их клиентов при обработке событий. Подробности этого механизма - это отдельная тема.
Размещение управляющих элементов при помощи CWnd
При размещении элемента управления ActiveX класс CWnd из MFC выполняет огромный кусок работы самостоятельно. Написав одну строку кода, можно попросить CWnd создать элемент управления и выполнить все необходимые действия для обеспечения его взаимодействия с содержащим его контейнером. Еще проще этого добиться при помощи редактора ресурсов, вставив элемент управления в нужное диалоговое окно. Но возможно понадобится использовать элементы управления или компоненты в произвольном месте приложения, поэтому будет рассмотрен вариант размещения элемента управления в произвольном окне MFC.
Для добавления управляющего элемента ActiveX в окно приложения в качестве дочернего окна следует создать в классе родительского окна член типа CWnd и использовать для создания самого элемента функцию CWnd::CreateControl() там, где это необходимо.
Возможно создавать компоненты на различных стадиях создания приложения, в зависимости от типа компонента. Простые COM-компоненты (без пользовательского интерфейса) можно создавать сразу после инициализации библиотеки COM при помощи вызова AfxEnableControlContainer(). Мастер создания приложения помещает этот вызов в функцию InitInstance() класса, наследованного от CWinApp. Если же компонент имеет пользовательский интерфейс или использует некоторые более сложные возможности ActiveX/OLE, нужно подождать, пока не будут проинициализированы еще несколько объектов окружения MFC. Классы MFC, наследованные от CView, имеют виртуальную функцию OnInitialUpdate(), которую можно переопределить для выполнения необходимых действий непосредственно перед отображением пользовательского интерфейса. Это наилучшее место для инициализации ваших визуальных компонентов, гарантирующее, что MFC уже завершила создание среды OLE.
CWnd::CreateControl() имеет набор параметров, полностью определяющих создаваемый компонент и несущих информацию о клиентском окне, с тем, чтобы Windows могла правильно инициализировать содержащее управляющий элемент окно. Первый параметр - это CLSID или ProgID, идентифицирующий управляющий элемент, который необходимо создать. CLSID и ProgID - это идентификаторы, которые компоненты помещают в реестр Windows при своей инсталляции. CLSID - это GUID (128-битное значение), представляющее класс компонента, а ProgID - текстовое имя, например "DomeWorks.DemoController", которое связано с CLSID в реестре. Функция CreateControl() имеет также другие параметры, включающие название окна, флаги типа окна, координаты прямоугольника в системе координат клиента, в котором должно быть создано окно, родительское окно и идентификатор управляющего элемента. Несколько перегруженных вариантов этой функции видоизменяют эти параметры и имеют значения по умолчанию для дополнительных параметров. Как правило, можно воспользоваться значениями по умолчанию.