Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Лабы Мартын 1(ComCorbaLab2004).doc
Скачиваний:
34
Добавлен:
10.02.2016
Размер:
2 Мб
Скачать
☆

1.5. Размещение управляющих элементов при помощи 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() имеет также другие параметры, включающие название окна, флаги типа окна, координаты прямоугольника в системе координат клиента, в котором должно быть создано окно, родительское окно и идентификатор управляющего элемента. Несколько перегруженных вариантов этой функции видоизменяют эти параметры и имеют значения по умолчанию для дополнительных параметров. Как правило, можно воспользоваться значениями по умолчанию.

1.6. Использование директивы #import

Директива #import, впервые появившаяся в Visual C++ 5.0,- мощный инструмент для работы с компонентами и их интерфейсами. Ее использование интуитивно более понятно и требует написания гораздо меньшего объема кода, нежели при использовании традиционных возможностей C++/COM. Директива читает библиотеку типов компонента или группы компонентов и создает исходный код классов-оболочек с указателями на интерфейсы (smart pointers), реализованные и экспортируемые из этих компонентов.

Эти классы являются классами-шаблонами, основанными на шаблоне _com_ptr_t<>, который создает класс-оболочку для COM-интерфейса. Они освобождают от необходимости учета ссылок на интерфейс, обязательного при обычных условиях. Они также создают операторы для работы с интерфейсом, использование которых интуитивно более понятно, и позволяют методам интерфейса возвращать значения. В приводимом примере можноувидеть все это в действии.

Вызовы методов COM всегда возвращают результат типа HRESULT, который является набором флагов, содержащих признаки успешности выполнения метода и другую информацию, которую разработчик компонента пожелал закодировать в разрядах HRESULT. Оболочка интерфейса перехватывает значение, возвращаемое методом, и, если оно сигнализирует об ошибке, порождает исключение типа _com_error со значением, содержавшемся в результате метода.

При таком подходе метод-оболочка может вернуть значение параметра из метода интерфейса в качестве своего результата. Возможноиспользовать этот результат как параметр в вызове другой функции непосредственно, без необходимости сохранения результата в промежуточной переменной. А исключения автоматически освобождают соответствующие ресурсы, поэтому класс-оболочка сам освободит интерфейсы компонента в случае возникновения исключения. Это освобождает от написания значительной части кода обработки ошибок. Опять же, это делает код легко читаемым.

Есть и некоторые недостатки. Использование исключений делает исполняемый код более громоздким и ухудшает производительность.

Класс-оболочка позволяет получать доступ к свойствам интерфейса так, как если бы они были членами самого класса-оболочки. При традиционном подходе, применяемом при программировании COM на C++, для получения значения свойства нужно вызвать метод get_XXX(), где XXX - нужное свойство. Аналогично, для изменения значения свойства нужно вызвать метод put_XXX(). Интеллектуальные указатели, используемые в классе-оболочке, позволяют писать код так, как если бы выполнялясьссылка или изменение значения свойства напрямую; заботу о вызовах get_ и put_ берет на себя класс-оболочка.

Например, если без использования класса-оболочки используется примерно такой код:

HRESULT hr;

int i1, i2, result;

hr = m_pInterface->get_IntMember(&i1);

if (FAILED(hr))

// сделать то, что нужно

// освободить интерфейсы

// прервать выполнение

hr = m_pInterface->CalcOtherInt(&i2);

if (FAILED(hr))

// сделать то, что нужно

// освободить интерфейсы

// прервать выполнение

hr = m_pInterface->AddInt(i1, i2, &result);

if (FAILED(hr))

// сделать то, что нужно

// освободить интерфейсы

// прервать выполнение

то при помощи классов-оболочек то же самое можно выполнить вот так:

int i = 5;

try

{

int result = m_pSmartPtr->AddInt(m_pSmartPtr->GetInt(),i);

}

catch (_com_error &e)

{

// сделать то, что нужно

}

На деле задача может быть не такой простой, как в этом примере, и тогда выигрыш от сохраненного объема кода, предотвращения ошибок и понятности кода будет заметнее. То, что интеллектуальные указатели берут на себя все заботы по учету указателей, является основной причиной их использования. Теоретически же их использование может привести к появлению ошибок при работе с указателями, что является основной причиной нестабильности COM-приложений.