- •107 Методические указания «Программное обеспечение сетей эвм. Часть 4. Версия 2. Развитие схемы «клиент-сервер» в com и corba»
- •Введение
- •1.1. Необходимость использования компонент
- •1.2. Методы встраивания компонентов
- •1.3. Основы com
- •1.4. Типы компонентов
- •1.5. Размещение управляющих элементов при помощи cWnd
- •1.6. Использование директивы #import
- •1.7.1 О компонентах
- •1.7. Компоновка тестовой программы
- •1.7.2 Регистрация компонентов
- •1.7.3 Импортирование библиотеки типов
- •1.7.4 Определение членов cDemoClientView
- •1.7.5 Создание компонентов
- •1.7.6 Создание точек взаимодействия
- •1.7.7 Синхронизация параметров
- •1.7.8 Обработка событий от компонентов
- •1.7.9 Очистка
- •1.7.10 Шаблонный код
- •1.9. Индивидуальные задания на работу
- •2.1. Проект. Основные принципы
- •2.2. Как создать компонент
- •Шаг 2: Создание компонента:
- •2.3. Значения по умолчанию
- •2.5. Индивидуальные задания на работу
- •3.1. Cоздание сервера com
- •3.2. Использование com-объектов
- •3.3. Индивидуальные задания на работу
- •Лабораторная работа № 4. Использование atl
- •4.1 Создание dcom-сервера с использованием atl
- •4.1.1 Введение в atl
- •4.1.2 Что такое atl?
- •4.1.3 Разделение труда
- •4.1.4 Создание хранилища компонентов с помощью atl Com AppWizard
- •4.1.5 Вставка кода заглушки/прокси-объекта.
- •4.1.7 Atl com-карта
- •4.1.9 Класс cComModule
- •4.1.10 Язык скриптов реестра в atl
- •4.1.11 Распределенная com (dcom)
- •4.1.12 Dcom и службы nt
- •4.1.13 Структура службы nt
- •4.1.14 Основанный на службах nt сервер сом
- •4.1.15 Создание проекта при помощи atl
- •4.1.16 Добавление функциональных средств
- •4.1.17 Функция CacheQuotes (dcomServiceXdcomService.Cpp)
- •4.1.18 Функция GetQuote (dcomServiceXdcomService.Cpp)
- •4.2 Создание dcom сервера
- •4.3 Создание dcom клиента
- •4.4 Индивидуальные задания на работу
- •Лабораторная работа № 5. Разработка corba приложений
- •5.1. Конфигурирование
- •5.2. Порядок действий
- •5.3. Объектно-ориентированный анализ и моделирование
- •5.4. Описание и трансляция объектов
- •5.5. Создание сервера
- •5.6. Создание клиента
- •5.7. Отладка объектов
- •5.8. Индивидуальные задания на работу
- •Лабораторная работа № 6. Адаптер роа
- •6.1. Архитектура poa
- •6.2. Политики poa
- •Политика обработки запросов
- •6.3. Создание серверов на основе poa
- •6.4. Индивидуальные задания на работу
- •Лабораторная работа № 7. Прикладная задача связи
- •7.1. Постановка задачи
- •7.1.1. Сотовая станция
- •7.1.2. Телефоны
- •7.1.3. Система
- •7.2. Функционирование системы
- •7.3. Индивидуальные задания на работу
- •Лабораторная работа № 8. Работа по умолчанию
- •8.1. Сервер с сервантом по умолчанию
- •8.2. Индивидуальные задания на работу
- •Лабораторная работа № 9. Создание менеджеров сервантов
- •9.1. Менеджеры сервантов
- •ServantActivator
- •ServantLocator
- •9.2. И снова практика
- •9.3. Индивидуальные задания на работу
- •Лабораторная работа № 10. Сервис именования
- •10.1. Сервис для именования (Naming Service)
- •10.2. Индивидуальные задания на работу
1.7.4 Определение членов cDemoClientView
Теперь к классу необходимо добавить несколько членов-переменных, которые будут хранить реализации компонентов и их интерфейсов. Это приведет также к тому, что они будут доступны все время существования представления и не придется создавать их заново каждый раз, когда нужно будет использовать их в другом месте кода (например, в функции обработки сообщения).
Сначала будут добавлены в класс CDemoClientView две переменные типа CWnd, которые будут содержать реализации двух визуальных компонентов: m_viewWnd и m_controllerWnd. Далее, будут добавлены по одной переменной для каждого класса-оболочки интерфейса, что позволит получать доступ к методам в ответ на сообщения о событиях во время выполнения программы. Также добавлены по одной такой переменной для интерфейсов IDemoModel, IDemoView и IDemoController. Не следует забывать добавлять окончание "Ptr" к названиям интерфейсов, чтобы получить названия классов, сгенерированных директивой #import. Остальные члены, которые необходимо добавить, относятся к механизму точек взаимодействия. Надо добавить еще две переменные: интеллектуальный указатель на интерфейс IConnectionPoint и ключ типа DWORD. Вот эти определения:
class CDemoClientView : public CView
{
public:
CWnd m_viewWnd; // контейнер для компонента представления
CWnd m_controllerWnd; // контейнер для компонента управления
IDemoModelPtr m_pModel; // интеллектуальный указатель на IDemoModel
IDemoViewPtr m_pView; // интеллектуальный указатель на IDemoView
IDemoControllerPtr m_pController; // интеллектуальный указатель
IConnectionPointPtr m_pCP; // интеллектуальный указатель на IConnectionPoint
DWORD m_dwCookie; // содержит ключик для идентификатора
// точки взаимодействия...
}
Далее, переопределяется виртуальная функция OnInitialUpdate() классаCDemoClientView. Это можно сделать, нажав правую кнопку мыши в окне ClassView и выбрав команду Add Virtual Function, а далее выбрав элемент OnInitialUpdate() из предложенного списка функций. После этого нажимаются кнопки Add и Edit для добавления нужного кода и выполняется перемещение в определение функции, чтобы внести необходимые дополнения. Здесь, после вызова функции OnInitialUpdate() базового класса, следует вставить код создания компонентов.
1.7.5 Создание компонентов
Теперь возможноприступить к созданию самих компонентов. Сначаланадо создатьвизуальные компоненты, воспользовавшись только что созданными членами типа CWnd. Для этогоможно применитьфункцию CWnd::CreateControl(). Для указания CLSID компонентов используютсярезультаты работы компилятора при выполнении директивы #import: ключевое слово __uuidof() с именем класса. Вот код создания визуальных компонентов:
// создание управляющих элементов ActiveX
m_viewWnd.CreateControl(__uuidof(DemoView),"View", WS_VISIBLE, CRect(0,0,300,200),this,101);
m_controllerWnd.CreateControl(__uuidof(DemoController),"Controller",
WS_VISIBLE, CRect(0,201,300,350), this, 102);
Если на этом этапе откомпилировать проект и запустить программу DemoClient, она будет выглядеть примерно так, как показано на рис. 1.2. Главное окно содержит клиентскую часть, в которой расположены компоненты представления и отображения. Можно даже воспользоваться элементами управления, но это ни к чему не приведет, поскольку еще не привязаны их изменения к чему-либо.

Рис. 1.2. Приложение DemoClient в работе
Следующий шаг - создание компонента-модели. В данном случае оно выполняется несколько проще, поскольку компонент не требует наличия окна, которое бы его содержало. Для создания компонента можно использовать функцию-член CreateInstance() сгенерированного класса-оболочки, передав ей CLSID компонента, который нужно создать. Эта операция пройдет успешно только в случае, если в указанном компоненте реализован интерфейс, класс-оболочка для которого используется. В противном случае интеллектуальный указатель получит значение NULL. Вот как создается этот компонент:
// создание компонента-модели при помощи класса-оболочки
m_pModel.CreateInstance(__uuidof(DemoModel));
Теперь, когда созданы все компоненты, необходимы указатели на их интерфейсы, чтобы можно было вызывать методы этих интерфейсов и выполнять нужные действия. Что касается компонента-модели, то уже есть то, что нужно: компонент был создан при помощи функции CreateInstance() над самим интеллектуальным указателем, так что указатель уже связан с компонентом через интерфейс, который он представляет (в данном случае IDemoModel). Остальные два компонента были созданы при помощи класса CWnd, поэтому их нужно связать с интеллектуальными указателями отдельно. Благодаря члену класса CWnd и оператору связывания класса оболочки эта операция тоже выполняется достаточно просто.
Чтобы это сделать, нужно результат выполнения функции CWnd::GetControlUnknown() передать оператору связывания класса-оболочки. Функция GetControlUnknown() возвращает указатель на интерфейс IUnknown управляющего элемента ActiveX, содержащегося в CWnd. Оператор связывания получает этот указатель и самостоятельно вызывает функцию QueryInterface(), запрашивая указатель на интерфейс того же типа, который представляет класс-оболочка. При успешном выполнении интеллектуальный указатель получит правильное значение указателя на интерфейс нужного типа. Начиная с этого момента можно использовать методы интерфейса, до тех пор, пока указатель не освободит интерфейс перед своим уничтожением. В случае неуспеха вызова QueryInterface() указатель получит значение NULL.
