OLE это сокращение от Objects Linking and Embedding, т. е. объекты связанные и
внедренные. Наверняка вы знакомы с таким редактором как Word. Вспомните, что
картинка, которую вы переносите в текстовый редактор, действительно может быть
либо внедрённой, либо связанной. Внедрённая картинка будет храниться в файле
документа, часто значительно увеличивается его объём. Связанная картинка
хранится в отдельном файле, а в файле документа хранится лишь ссылка на эту
картинку. В последнем случае доступ к объекту могут иметь одновременно
несколько приложений, что позволяет, в частности, проводить групповую
разработку документов. Сказанное, разумеется, относится не только к
графическому объекту, но, к любому другому, поддерживаемому приложением.
Контейнеры и серверы
В механизме OLE присутствуют два приложения – приложения, содержащие объект, и
приложения, вызываемые для редактирования объекта. Приложение, содержащее
объект, называется контейнером. Приложение, используемое для редактирования
объекта, называется сервером. Особо отметим, что одна и та же программа может
одновременно выполнять и роль контейнера, и роль сервера.
Контролёры и серверы автоматизации
Сервер автоматизации предоставляет свои методы и свойства другим приложениям.
Приложение, которое пользуется возможностью управлять сервером, называется
контролёром автоматизации. Заметим в этой связи, что элементы ActiveX являются
ни чем иным, как сервером автоматизации.
Создание проекта
Создаём MFC-приложение с названием ole.
На вкладке Application Type устанавливаем флаги Single document (SDI) и Use MFC
in a static library.
На вкладке Compound Document Support устанавливаем флаг Container
Остальные настройки можно оставить на вашей совести.
На вкладке Generated Classes можно посмотреть список классов, от которых будут
образованы классы приложения. Вот эти классы ColeApp – класс-приложение,
ColeView – класс представления, ColeDoc - класс-документ, CmainFrame – класс
окна-рамки, ColeCntrItem – класс объекта сервера OLE.
Откомпилируйте наш проект и запустите его. В меню Edit мы увидим единственный
активный пункт Insert New Object. Этот пункт и позволяет нам поместить в наш
документ выбранный нами объект. Выберем этот пункт. В появившемся окне вы
можете выбрать, объект какого типа будет помещен в наш контейнер.
Выберем в списке пункт Документ Microsoft Word и нажмём OK. В окне нашего
приложения появится объект – документ Word. Обратите внимание на изменившееся
меню. Это меню программы Word. Исключение является только пункт File, который
соответствует нашему приложению. Кроме того, в окне появится и панели
инструментов MS Word.
При помощи мыши изменим, размеры нашего объекта и в нем наберём некоторый
текст.
Нажмём на клавишу Esc – объект исчезнет из нашего окна, хотя набранный в нем
текст останется. Обратим внимание на то, что наш объект не активизируется и не
де активизируется с помощью щелчков мыши, как это положено в стандарте OLE.
Устранение этого недостатка и будет одним из пунктов нашего дальнейшего
рассмотрения.
Чтобы снова активизировать наш проект (ранее де активизированный клавишей Esc),
нужно обратиться к пункту меню Edit/Объект/Изменить.
Вот что должно, на мой взгляд, заинтересовать вас в первую очередь. Как
сохранить и восстановить объект в контейнере. Обратимся к пункту меню File/Save
As и сохраним документ в файле Untitled. Просмотр сохранённого файла наводит на
мысль, что созданный нами объект действительно сохранился в этом файле. Для
того, чтобы удалить объект из контейнера можно воспользоваться пунктом меню
File/New. Удалим объект из нашего контейнера, и попытаемся восстановить его с
помощью пункта File/Open. Пункт срабатывает без каких-либо сообщений об ошибке,
но объект в нашем контейнере не появляется. Стало быть, первое, чем мы сейчас
займемся, это проблема сохранения и восстановления из архива.
Изучение модулей проекта приводит нас к странному, на первый взгляд, открытию. В
проекте имеются две функции.
void ColeDoc::Serialize(CArchive& ar)
{
if (ar.IsStoring())
{
// дополнительный код сохранения документа в архиве
}
else
{
// дополнительный код восстановления данный из архива
}
// вызов функции сериализации базового класса, вызывает
// сохранения объекта, находящегося в данный момент в контейнере.
COleDocument::Serialize(ar);
}
Особо надо обратить внимание на последний вызов:
COleDocument::Serialize(ar);
Это очень важный вызов. Маленькое исследование, которое я предлагаю провести
вам, дорогой читатель, показывает, что при этом вызывается функция сериализации
из модуля CntrItem.cpp. Вот эта функция:
void ColeCntrItem::Serialize(CArchive& ar)
{
ASSERT_VALID(this);
// сохранить (восстановить) объект, находящийся
// Since в данный момент в контейнере
COleClientItem::Serialize(ar);
if (ar.IsStoring())
{
// можно сохранить другие данные из класса поддержки
// объекта к контейнере
}
else
{
// восстановить другие данные из класса поддержки
// объекта в контейнере
}
}
Интуитивно понятно, что всё дело в этой функции. Примем за исходное, что
сохранение данных осуществляется правильно. Следовательно, дело в
восстановлении данных. Поскольку и восстанавливаются данные той же функцией,
то, очевидно, восстановление данных также производится правильно. Таким
образом, причину не появления объекта нужно искать в методах класса
ColeClientItem. По смыслу самым подходящим методом оказывается метод DoVerb.
Данный метод используется для произведения действия, которое определяется
значением первого параметра. Второй и третий параметры обычно полагают равным
NULL, но можно поместить туда указатель на объект-предоставление. Действие
«представить для редактирования объект» определяется значением первого
параметра -1 (или 0). Значение -2 определяет редактирование объекта в отдельном
окне. В результате функция сериализации, позволяющая загрузить в контейнер и
представить для редактирования объект, будет иметь следующий вид. Вместо
функции DoVerb можно использовать метод
Мы продемонстрировали вам примерный ход рассуждений, которому приходится часто
следовать, работая программистом и не имею под рукой полной документации.
Итак, мы можем сохранить и восстановить объект контейнера. Теперь продолжим
совершенствовать наш проект.
Поставим перед собой следующую задачу: объект можно перемещать и менять его
положение. Добавим в класс ColeCntrItem новый член m_rect типа CRect. В этой
переменной будет храниться информация о визуальном положении объекта.
Позаботимся теперь о том, чтобы данная переменная сохранялась при сериализации.
Для этого ещё раз перепишем функцию сериализации
В жёсткой фиксации объектов нет ничего хорошего. Чтобы сделать систему более
гибкой, давайте обратимся к функции ColeCntrItem::OnChangeItemPosition. Эта
функция вызывается каждый раз, когда меняется положение и размер объекта в
контейнере. Вот текст этой функции.
Параметром этой функции как раз и является новое место положение объекта.
Добавим в функцию следующие функции.
//запомнить новый размер
m_rect=rectPos;
//установить флаг изменения изображения
GetDocument()->SetModifiedFlag();
//обновить отображение
GetDocument()->UpdateAllViews(NULL);
В результате мы пришли к приложению, которое позволяет помещать в контейнер
объект, редактировать его, изменять его размеры и положение, сохранять объект и
его состояние в файле и восстанавливать его оттуда.