ОГЛАВЛЕНИЕ
ВЕДЕНИЕ
Комплект поставки ObjectWindows
Что представляет собой руководства по ObjectWindows
Требования по программному и аппаратному обеспечению
Установка ObjectWindows
Использование INSTALL
Файл README для ObjectWindows
Файлы помощи ObjectWindows
Помощь в Windows
Помощь в ДОС
Соглашения, используемые в данных книгах
Соглашения по именам в ObjectWindows
Как связаться с фирмой Borland
Возможности поставляемого пакета
Возможности фирмы Borland
ЧАСТЬ I. ЗНАКОМСТВО С ObjectWindows
Глава 1. Наследовать окно
Что такое прикладная программа для Windows
Достоинства Windows
Требования
Объектно-ориентированное программирование в Windows
Лучший интерфейс с Windows
Формирование оконной информации
Абстрагирование функций Windows
Автоматический ответ на сообщения
Структура Windows программы
Структура Windows
Взаимодействие с Windows и с ДОС
"Hello, Windows" ("Здравствуй Windows")
Характеристики запуска прикладной программы
Характеристики главного окна
Процесс создания прикладной программы
Глава 2. Шагаем по Windows
Создание прикладной программы ObjectWindows: предварительные рассуждения
Библиотека контейнерных классов
Каталоги
Указание необходимой библиотеки
Программы ObjectWindows, использующие DLL
Создания файла ресурсов
Создание прикладной программы ObjectWindows: специфика
Использование ИСР для создания приложения ObjectWindows
Использование утилит командной строки для создания прикладных программ
ObjectWindows
Шаг 1: Простая Windows программа
Требования прикладной программы
Определение класса прикладная программа
Шаг 2: Класс главного окна
Что такое оконный объект ?
Дескрипторы
Родительские и дочерние окна
Создание объекта главное окно.
Ответы на сообщения
Завершение работы прикладной программы
Глава 3. Вывод информации в окно
Что такое контекст вывода ?
Шаг 3 : Вывод в окно текстовой информации
Структура сообщения
Очистка экрана
Шаг 4 : Рисование линий в окне
Модель перемещения
Реакция на сообщения перемещения
Шаг 5 : Изменение толщины линий
Выбор нового карандаша
Замена карандаша
Реализация диалога ввода
Шаг 6 : Вывод графической информации
Модель рисования
Хранение графики как объекта
Перерисовка сохраненной графической информации
Глава 4. Дабавление меню
Ресурсы меню
Шаг 7 : Меню для главного окна
Перехват сообщений меню
Ответ на сообщение меню
Глава 5. Поддержка диалога
Шаг 8 : Добавление всплывающего окна
Создание всплывающего окна
Функция MakeWindow
Добавление блока диалога
Добавление компоненты данных
Инициализация блока диалога
Шаг 9 : Сохранение графической информации в файле
Отслеживание статуса
Открытие и сохранение файлов
Глава 6. Всплывающие окна
Шаг 10 : Вывод всплывающего окна помощи
Использование модулей с ObjectWindows
Модификация главной программы
Создание модуля
Добавление управляющих элементов к окну
Что такое управляющие элементы
Создание управляющих элементов окна
Упраляющие объекты как компоненты данных
Организация работы управляющих элементов
Реакция на события управляющих элементов
ЧАСТЬ 2. ИСПОЛЬЗОВАНИЕ ObjectWindows
Глава 7. Обзор ObjectWindows
Соглашения ObjectWindows
Иерархия ObjectWindows
Object
TModule
TApplication
Интерфейсные объекты
TWindowsObject
Оконные объекты
TWindow
TEditWindow
TFileWindow
Диалоговые объекты
TDialog
TFileDialog
TInputDialog
Управляющие объекты
TControl
TButton
TCheckBox
TRadioButton
TListBox
TComboBox
TGroupBox
TStatic
TEdit
TScrollBar
Объекты MDI
TMDIFrame
TMDIClient
Прокручивающиеся объекты
Функции Windows API.
ObjectWindows вызывает функции Windows
Доступ к функциям Windows
Комбинирование стилевых констант
Типы функций Windows
Интерфейсные функции управления окнами
Функции интерфейса с графическими устройствами (GDI)
Функции интерфейса с системными средствами
Функции с обратным вызовом
Функции подсчета
Использование интеллектуальных обратных вызовов
Сообщения Windows
Параметры сообщений Windows
Типы сообщений Windows
Сообщения управления окнами
Инициализационные сообщения
Сообщения ввода
Системные сообщения
Сообщения буфера информационного обмена (Clipboard)
Сообщения о системной информации
Сообщения манипулирования управляющими элементами
Сообщения уведомления управляющих элементов
Уведомляющие сообщения линеек прокрутки
Сообщения не из области пользователя
Сообщения мультидокументального интерфейса
Обработка сообщений по умолчанию
Посылка сообщений
Диапазоны сообщений
Сообщения, определяемые пользователем
Глава 8. Объекты прикладная программа и модуль
Поток прикладной программы
Инициализация приложений
Инициализация главного окна
Инициализация каждого экземпляра прикладной программы
Инициализация первого экземпляра прикладной программы
Запуск прикладных программ
Закрытие прикладных программ
Глава 9. Интерфейсные объекты
TWindowsObject
Почему интерфейсные объекты ?
Отношение родители-дети между окнами
Списки дочерних окон
Итерация по дочерним окнам
Обработка сообщений
Ответы на сообщения
Командные сообщения и сообщения дочерних окон
Обработка командных сообщений
Обработка сообщений дочерних окон
Обработка сообщений по умолчанию
Глава 10. Оконные объекты
Класс ТWindows
Инициализация и создания оконных объектов
Инициализация оконных объектов
Создание оконных элементов
Обобщение информации о создании и инициализации
Регистрация оконного класса
Атрибуты регистрации
Компонента стиля класса
Компонента иконки
Компонента курсора
Компонента цвета фона
Компонента меню по умолчанию
Прокручиваемые окна
Атрибуты прокрутки
Организации прокрутки в окне
Пример с прокруткой
Авто-прокрутка и отслеживание местоположения
Модификация единиц прокрутки и диапазона
Модификация положения прокрутки
Установка размера страницы
Оптимизация функций-компонент Paint при организации прокрутки
Окна редактирования и окна работы с файлами
Окна редактирования
Окна работы с файлами
Глава 11. Диалоговые объекты
Использование ресурсов диалога
Использование дочернего диалогового объекта
Конструирование и инициализация дочерних диалоговых объектов
Создание и выполнение диалогов
Модальные и немодальные диалоги
Закрытие дочернего диалога
Использование диалога как главного окна
Определение класса Windows для вашего немодального диалога
Работа с управляющими элементами и обработка сообщений
Работа с управляющими элементами диалога
Ответы на сообщения уведомления управляющих элементов
Пример связи диалог/управляющий элемент
Усложненный пример использования диалогов
Диалоги ввода
Файловые диалоги
Глава 12 Объекты управления
Использование объектов управления
Конструирование и создание объектов управления
Уничтожение и удаление управляющих элементов
Управляющие элементы и обработка сообщений
Манипулирование управляющими элементами окон
Реакция на предупреждающие сообщения управляющих элементов
Фокус управления и клавиатура
Управляющие элементы блоков списков
Конструирование и создание блоков списков
Модификация блоков списков
Запросы к блокам списков
Получение выборов из блока списка
Статичные управляющие элементы
Конструирование статичных управляющих элементов
Запросы к статичным управляющим элементам
Модификация статичных управляющих элементов
Пример: Приложение StatTest
Управляющие элементы кнопок нажатия
Реакция на сообщения кнопок
Блоки проверки и селективные кнопки
Конструирование блоков проверки и селективных кнопок
Запрос состояния селективного блока
Модификация состояния селективного блока
Реакция на сообщения блоков проверки и селективных кнопок
Блоки группы
Конструирование блока группы
Реакция на сообщения блока группы
Пример программы: BtnTest
Линейки прокрутки
Конструирование объектов линеек прокрутки
Запросы линейкам прокрутки
Модификация линеек прокрутки
Реакция на события линейки прокрутки
Пример: SBarTest
Редактируемые управляющие элементы
Конструирование редактируемых управляющих элементов
Буфер информационного обмена и операции редактирования
Запросы к редактируемым управляющим элементам
Модификация редактируемых управляющих элементов
Удаление текста
Вставка текста
Принудительный выбор и прокрутка текста
Пример программы: EditTest
Комбинированные блоки
Три разновидности комбинированных блоков
Простые комбинированные блоки
Выпадающие комбинированные блоки
Выпадающие списковые комбинированные блоки
Выбор типов комбинированных блоков
Конструирование комбинированных блоков
Модификация комбинированных блоков
Пример приложения: CBoxTest
Передача данных управляющих элементов
Определение буфера передачи
Конструирование управляющих элементов и разрешение передачи
Передача данных
Модификация передачи для управляющих элементов
Примеры передачи
Глава 13. Адаптированные объекты управления
Модификация предопределенных управляющих элементов
Модификация стилей создания
Как сделать предопределенный управляющий элемент рисуемым
Модификация предопределенных реакций на сообщения
Задание дополнительной обработки для предопределенного управляющего
элемента
Обход реакции предопределенного управляющего элемента
Использование адаптированного управляющего элемента
Разработка адаптированного управляющего элемента
Описание адаптированного управляющего элемента
Глава 14. Объекты MDI
Компоненты приложений MDI
Каждое окно MDI является объектом
Конструирование окон MDI
Конструирование окон рамки MDI
Конструирование дочерних окон MDI
Обработка сообщений в приложениях MDI
Управление дочерними окнами MDI
Активизация дочерних окон
Меню дочерних окон
Пример приложения MDI
Глава 15. Объекты, которые возможно использовать в потоках
Библиотека iostream
Перегруженные операторы << и >>
Классы, имеющие возможность участвовать в потоках, и TStreamable
Менеджер потоков
Конструкторы классов, имеющих возможность участвовать в потоках
Имена классов, имеющих возможность участвовать в потоках
Использование менеджера потоков
Установление связей в программе менеджера потоков
Создание потокового объекта
Использование потокового объекта
Совокупности в потоках
Придание массивам возможности участвовать в потоках
Построительная функция класса, имеющего возможность участвовать в
потоках
Функция-компонента streamableName
Функция-читатель, имеющая возможность работы с потоками
ЧАСТЬ 3. ПРОГРАММИРОВАНИЕ ДЛЯ WINDOWS
Глава 16. Управление памятью
Использование менеджера памяти
Как Windows управляет памятью
Дескрипторы и адреса
Особенности реального режима
Особенности защищенного режима
Локальная и глобальная память
Использование локальной области динамически распределяемой памяти
Размещение и доступ к локальной памяти
Освобождение и отбрасывание блоков локальной памяти
Переразмещение и модификация блоков локальной памяти
Запросы к блокам локальной памяти
Замечания по программированию
Использование глобальной области динамически распределяемой памяти
Размещение и доступ к глобальной памяти
Другие способы блокировки блоков глобальной памяти
Освобождение и отбрасывание блоков глобальной памяти
Переразмещение и модификация блоков глобальной памяти
Запросы к блокам глобальной памяти
Изменение глобального отбрасывания
Получение предупреждений о недостаточной памяти
Замечания по программированию
Глава 17. Взаимодействие процессов
Буфер информационного обмена Windows
Форматы Буфера информационного обмена
Помещение данных в Буфер информационного обмена
Получение данных из Буфера информационного обмена
Задержка преобразования
Обмен сообщениями между процессами
Динамический обмен данными
Термины
Установка диалога
Прекращение диалога
Функции-компоненты обмена данными
Запрос одного элемента данных
Событийное преобразование данных
Запрос к серверу на изменение значения данных
Выполнение команд макросов в сервере
Системная тема
Глава 18. Введение в GDI
Контекст представления
Управление контекстом представления
Что находится в контексте представления?
Побитовая графика
Цвет
Режимы отображения
Области усечения
Средства рисования
Средства рисования
Имеющиеся средства
Логические средства
Логические карандаши
Логические кисти
Логические шрифты
Представление графики в окнах
Рисование в окнах
Управление контекстом представления
Вызов оконных графических функций
Отрисовка окон
Стратегии графики
Использование средств рисования
Функции рисования GDI
Рисование текста
Рисование линий
Move To и LineTo
Polyline
Arc
Рисование форм
Rectangle
RoundRect
Ellipse
Pie и Chord
Polygon
Использование палитр
Установка палитры
Рисование с помощью палитр
Запросы к палитрам
Модификация палитры
Реакция на изменение палитры
Глава 19. Ресурсы в подробностях
Создание ресурсов
Добавление ресурсов к выполняемому файлу
Использование Компилятора ресурсов
Файлы описания Компилятора ресурсов
Советы по работе с Компилятором ресурсов
Загрузка ресурсов в приложении
Загрузка меню
Загрузка таблиц акселераторов
Загрузка блоков диалога
Загрузка курсоров и пиктограмм
Загрузка ресурсов строк
Загрузка побитовых отображений
Использование побитового отображения для создания кисти
Представление побитовых отображений в меню
Глава 20. Стандартные руководящие принципы разработки приложений
Принципы разработки
Предоставление реакции, ожидаемой пользователем
Возможность пользователя управлять ходом событий
Стандартные внешний вид и режим работы
Общее представление
Взаимодействие с помощью мыши и клавиатуры
Меню
Блоки диалога
Другие соглашения разработки
Написание надежных программ
Элементарные операции
Пул безопасности
Другие ошибки при создании окон
Проверка потребителей памяти
ТАБЛИЦЫ
Таблица 2.1 Каталоги INCLUDE по умолчанию
Таблица 2.2 Библиотеки для каждой модели памяти
Таблица 2.3 DLL и библиотеки импорта
Таблица 2.4 Классы ObjectWindows, которые требуют файлов ресурсов
Таблица 3.1 Наиболее часто получаемые сообщения "мыши".
Таблица 3.2 Сообщения, используемые в Шаге 4.
Таблица 7.1 Диапазоны сообщений Windows
Таблица 10.1 Компоненты данных TWindowAttr.
Таблица 10.2 Атрибуты регистрации окна
Таблица 10.3 Типичные установки компонент данных окна редактирования
Таблица 10.4 Функции-компоненты окна работы с файлами и идентификаторы
меню
Таблица 12.1 Управляющие элементы Windows, поддерживаемые ObjectWindows
Таблица 12.2 Предупреждающие сообщения блоков списков
Таблица 12.3 Общие стили редактируемых управляющих элементов
Таблица 12.4 Идентификаторы меню и вызываемые ими функции-компоненты
Таблица 12.5 Функции-компоненты, определяемые TScrollBar
Таблица 12.6 Компоненты данных TListBoxData
Таблица 12.7 Компоненты данных TComboBoxData
Таблица 14.1 Стандартные действия, команды и функции-компоненты MDI
Таблица 17.1 Форматы буфера информационного обмена
Таблица 18.1 Имеющиеся средства рисования
Таблица 18.2 Простые RGB-значения цветов
Таблица 18.3 Константы шага и семейства шрифта
Таблица 19.1 Параметры командной строки Компилятора ресурсов
Таблица 20.1 Значения ошибок создания окон
РИСУНКИ
Рисунок 1.1 Экранные компоненты приложения Windows
Рисунок 1.2 Как приложение Windows взаимодействует с Windows и ДОС
Рисунок 2.1 Полное приложение ObjectWindows.
Рисунок 2.2 Реализация первого Шага вашего приложения ObjectWindows
Рисунок 2.3 Наша прикладная программа отвечает на событие
Рисунок 2.4 Шаг приложения с переопределенной реакцией на завершение
работы
Рисунок 3.1 Вывод координат точки нахождения указателя "мыши" в момент
нажатия левой клавиши.
Рисунок 3.2 Изменение толщины линии.
Рисунок 4.1 Прикладная программа с ресурсом меню.
Рисунок 4.2 Прикладная программа с системой помощи
Рисунок 5.1 Группа связанных родительских и дочерних окон
Рисунок 5.2 Прикладная программа с блоком файлового диалога.
Рисунок 6.1 Система помощи созданной нами прикладной программы
Рисунок 7.1 Иерархия классов ObjectWindows
Рисунок 8.1 Функция-компонента осуществляет вызовы, организуя поток
прикладной программы
Рисунок 8.2 Главное окно
Рисунок 8.3 Последовательная инициализация экземпляров приложения
Рисунок 10.1 Атрибуты окна
Рисунок 10.2 Программа, использующая класс IBeamWindow.
Рисунок 10.3 Окно редактирования
Рисунок 11.1 Прикладная программа с диалогом
Рисунок 11.2 Диалог, проверяющий допустимость ввода пользователя
Рисунок 11.3 Диалог ввода
Рисунок 11.4 Файловый диалог
Рисунок 12.1 Некоторые простые управляющие элементы
Рисунок 12.2 Заполненный блок списка
Рисунок 12.3 Реакция на выбор пользователем элемента блока списка
Рисунок 12.4 Три типа комбинированных блоков и блок списка
Рисунок 12.5 Выпадающий списковый комбинированный блок
Рисунок 12.6 Статичные управляющие элементы
Рисунок 12.7 Окно с редактируемыми управляющими элементами
Рисунок 12.8 Редактируемый управляющий элемент создается без рамки
Рисунок 12.9 Окно с различными кнопками
Рисунок 12.10 Управляющий элемент линейки прокрутки
Рисунок 12.11 Окно с различными линейками прокрутки
Рисунок 14.1 Компоненты примера приложения MDI
Рисунок 15.1 Иерархия классов, имеющих возможность участвовать в потоках и
использующихся в ObjectWindows
Рисунок 16.1 Пример уплотнения
Рисунок 18.1 Стили линий для средств карандаша
Рисунок 18.2 Стили штриховки для средств кисти
Рисунок 18.3 Результат работы функции TextOut
Рисунок 18.4 Результат работы функции LineTo
Рисунок 18.5 Результат работы функции Polyline
Рисунок 18.6 Результат работы функции Arc
Рисунок 18.7 Результат работы функции Rectangle
Рисунок 18.8 Результат работы функции RoundRect
Рисунок 18.9 Результат работы функции Ellipse
Рисунок 18.10 Результат работы функции Pie
Рисунок 18.11 Результат работы функции Chord
Рисунок 18.12 Результат работы функции Polygon
Рисунок 19.1 Шаблон с полосками, заполняющий область дисплея
Рисунок 19.2 Ресурс побитового отображения для создания кисти для шаблона,
показанного на Рис. 19.1
Рисунок 19.3 Меню, использующее побитовое отображение в качестве одного из
своих пунктов
Рисунок 20.1 Стандартная линейка меню CUA
Рисунок 20.2 Пункты меню Файл, рекомендованные CUA
Рисунок 20.3 Пункты меню Редактирование, рекомендованные CUA
Рисунок 20.4 Пункты меню Просмотр, рекомендованные CUA
Рисунок 20.5 Пункты меню Опции, рекомендованные CUA
Рисунок 20.6 Пункты меню Подсказка, рекомендованные CUA
ВЕДЕНИЕ
ObjectWindows обеспечивает совершенно новый великолепный способ
создания прикладных программ для Microsoft Windows. До последнего времени
программирование для среды Windows требовало компилятора Microsoft C и
большого числа отдельных, и достаточно сложных, прикладных утилит. В
результате чего, создание программ под Windows являлось относительно
медленным, сложным и кропотливым делом. С появлением прикладного
программного продукта ObjectWindows программирование для Windows стало
намного более приятным занятием.
Программирование в Windows требует от вас знания многих новых
тонкостей, о которых, возможно, вы и не должны были думать до этого.
Например, манипулирование с текстом и графикой в изменяющихся по размеру
окнах, взаимодействие с другими программами в многозадачной среде и
манипулирование более чем 600 функциями в Интерфейсе Программирования
Приложения Windows (Windows Application Programming Interface API).
Возможно, что большая часть всего этого может только прояснить, какие
основные вещи ваша программа должна делать для того, чтобы функционировать
как прикладная программа Windows и затем убедиться, что вы реализовали все
из них.
ObjectWindows позволяет все это выполнить. Это объектно ориентированная
библиотека классов, которая формирует механизм (на уровне прикладных
программ и на уровне окон), реализуемый обычными прикладными программами
Windows. ObjectWindows упрощает создание программ под Windows из-за
- согласующегося, интуитивного и упрощенного интерфейса с Windows;
- обеспечения механизма управления окнами и обработки сообщений;
- базового набора оконных элементов для структурирования прикладной
программы под Windows.
Вы автоматически получаете эти базовые функциональные возможности,
которые позволяют вам сосредоточить основные усилия на специфических
требованиях вашей прикладной программы.
ObjectWindows не предоставляет классы для исчерпывающего формирования
всех логических элементов прикладной программы для Windows. Но, посредством
модификации имеющихся в ObjectWindows классов, вы существенно сократите
время создания вашей прикладной программы и обеспечите получение гораздо
более компактного кода.
Комплект поставки ObjectWindows
Комплект поставки ObjectWindows состоит из набора дискет и двух
руководств:
- ObjectWindows для С++. Руководство программиста (Данное руководство).
- ObjectWindows для С++. Справочное руководство.
На дисках имеются все необходимые программы ObjectWindows для начала
написания приложений для Windows с использованием объектно-ориентированного
подхода ObjectWindows. На них содержаться и все примеры программ, на
которые имеются ссылки в книге ObjectWindows для С++ Руководство
программиста, а также другие полезные программы, которые вы можете изучить
и использовать в дальнейшем. На дисках содержаться два файла Помощи (Help):
- OWLWHELP.HLP для системы помощи Windows Help.
- OWLTHELP.OWH для ДОСовского THELP.
Что представляет собой руководства по ObjectWindows
Так как ObjectWindows использует некоторые новые технические методы, то
данное Руководство программиста включает в себя большое количество
пояснительного материала. Оно состоит из трех частей:
- Часть 1, Знакомство с ObjectWindows, описывает принципы написания
прикладных программ для Windows. Она представляет собой учебник,
прослеживающий весь процесс написания и расширения возможностей приложения
ObjectWindows.
- Часть 2, Использование ObjectWindows, посвящена в основном
детальному описанию элементов самого ObjectWindows. Она включает в себя
обзор иерархии классов и объясняет как взаимодействовать со средой Windows.
- Часть 3, Программирование в Windows, дает описание аспектов
программирования в Windows, которые ObjectWindows не поддерживает, такие
как, графика, связь между процессами, использование ресурсов, управление
памятью, разделение кодов и данных и управляющие строки в Windows.
В "ObjectWindows для С++ Справочное руководство" вы найдете справочные
материалы по классам ObjectWindows, типам ObjectWindows, константам
ObjectWindows, функциям Windows, типам Windowsи константам Windows. Все
материалы приводятся в алфавитном порядке и для удобства пользования имеют
закладки.
Требования по программному и аппаратному обеспечению
Так как ObjectWindows является графическим инструментальным средством
для создания прикладных программ под Windows, то основные требования по
аппаратному обеспечению для прикладных программ ObjectWindows совпадают с
аналогичными требованиями для Windows:
- жесткий диск;
- 2 Мб и более оперативной памяти;
- совместимый с Windows графический дисплей;
- Windows 3.0 и выше в стандартном или расширенном режиме процессора
386.
Вы можете компилировать приложения ObjectWindows (и исходные коды
самого ObjectWindows) с помощью любого компилятора Borland C++, который
создает программы для Windows.
Установка ObjectWindows
ObjectWindows поставляется с программой автоматической установки
INSTALL. Так как для хранения файлов на дистрибутивных дискетах мы
используем технологии их сжатия, то вы обязательно должны использовать эту
программу; вы не можете просто скопировать файлы ObjectWindows с дискет на
ваш жесткий диск. Программа INSTALL автоматически копирует и производит
декомпрессию файлов ObjectWindows. На установочной дискете в файле
FILELIST.DOC для справки приводится списки всех дистрибутивных файлов.
До начала инсталляции сделайте полные рабочие копии всех ваших
дистрибутивных дисков, и положите оригинальные диски в безопасное место.
Найдите время для изучения соглашения, включаемого в пакет поставки
ObjectWindows, и вышлите нам заполненную регистрационную карточку; это
гарантирует вам получение информации об изменениях и новых версиях
ObjectWindows.
Использование INSTALL
Программа INSTALL создает каталоги для хранения файлов ObjectWindows и
перемещает эти файлы с дистрибутивных дисков на ваш жесткий диск. Все что
вам необходимо знать приводится ниже.
Для установки ObjectWindows:
1. Установите инсталляционный диск (диск 1) в дисковод А. Наберите
следующую команду и нажмите Ввод (Enter):
A:INSTALL
2. Нажмите Ввод (Enter) при появлении установочного экрана.
3. Следуйте выдаваемым приглашениям.
Файл README для ObjectWindows
Файл README содержит последнюю информацию, которая может быть и не
включена в прилагаемые руководства.
Для получения доступа к файлу README сделайте следующее:
1. Установите инсталляционный диск ObjectWindows в дисковод А. Если вы
уже установили ObjectWindows, то перейдите к пункту 3.
2. Наберите А: и нажмите Ввод (Enter).
3. Наберите README и нажмите Ввод (Enter). Вы окажитесь в программе
просмотра файла, используйте ключи управления курсором Стрелка Вверх и
Стрелка Вниз для перемещения по файлу.
4. Нажмите Esc для выхода.
Файлы помощи ObjectWindows
Мы поставляет два файла помощи для ObjectWindows: OWLWHELP.HLP и
OWLTHELP.OWH. OWLWHELP.HLP обеспечивает помощь в Windows, а OWLTHELP.OWH
предоставляет помощь в среде ДОС. Информация в этих файлах является
полностью идентичной, за исключением формата хранения.
Помощь в Windows
OWLWHELP.HLP является файлом помощи для прикладной программы помощи в
Windows WINHELP.EXE. Может оказаться полезным создать иконку в Program
Manager со следующими описателями:
- Description: ObjectWindows Help
- Command line: WINHELP.EXE C:\BORLANDC\OWL\OWLWHELP.HLP
Двойное нажатие кнопки "мыши" на этой иконке автоматически загружает
файл OWLWHELP.HLP под управлением WINHELP.EXE. Если вы установили
ObjectWindows не в каталоги по умолчанию, то вам необходимо изменить имя
каталога, в котором находится файл OWLWHELP.HLP.
Помощь в ДОС
Программа THELP представляет собой резидентную утилиту, обеспечивающую
доступ к помощи по ObjectWindows во время выполнения прикладных программ в
ДОС. Например, когда вы пишите свою прикладную программу ObjectWindows в
интегрированной среде Borland C++ вы можете загрузить THELP и в любой
момент работы с прикладной программой вам будет доступна помощь по
ObjectWindows.
Загрузка THELP осуществляется из командной строки ДОС путем набора
команды:
THELP /FC:\BORLANDCOWL\OWLHELP.OWH
Эта команда загружает THELP и задает файл OWLTHELP.OWH, как файл для
использования в Turbo Help. Для вызова THELP после ее загрузки, нажмите
клавишу 5 на блоке цифровой клавиатуры. THELP активизируется и пытается
найти в индексе помощи слово, на которое (или около которого) установлен
курсор. Если она находит слово, то высвечивается экран помощи,
соответствующий этому топику; в противном случае, появляется индекс помощи
для выбора нужного вам топика.
!!! Вы можете изменять "горячие" клавиши, задаваемые по умолчанию; смотри
файл помощи THELP.DOC.
Ниже приводится краткая справки по возможностям THELP:
-------------------------------------------------------------------
Клавиша Действие
-------------------------------------------------------------------
Tab перемещение курсора к следующему ключевому слову
Shift+Tab перемещение курсора к предшествующему ключевому слову
Home перейти к началу строки
End перейти к концу строки
Enter выбрать элемент Помощи для подсвеченного ключевого слова
на текущем экране Помощи
Esc выйти из THELP
F1 вывести Индекс Помощи
Alt+F1 вывести в обратном порядке последние 20 экранов Помощи,
которые вы уже просматривали
Ctrl+F1 выдать экран помощи по "горячим" клавишам THELP
Ctrl+P поместить текст примера в прикладную программу
-------------------------------------------------------------------
Вы можете изменять опции THELP путем задания различных параметров в
командной строке. Более подробно эта процедура описана в файле THELP.DOC.
Соглашения, используемые в данных книгах
Все шрифты и иконки, используемые в данном руководстве, были созданы с
помощью Borland's Sprint: The Professional Word Processor на лазерном
принтере PostScript.
Тонкий шрифт - этот шрифт используется для представления текста,
который появляется на экран или в программе. Он также используется для
указания на то, что вы должны набрать на клавиатуре.
ВСЕ ЗАГЛАВНЫЕ - все заглавные буквы используются для наименований
констант (например, WM_MOUSEMOVE).
МАЛЕНЬКИЕ ЗАГЛАВНЫЕ - маленькие заглавные буквы используются для имен
файлов и каталогов (например, STEP1.CPP).
[] - квадратные скобки в тексте и командных строках ДОС указывают на
необязательные элементы, которые зависят от вашей системы. Текст такого
типа не должен набираться дословно.
Жирный (Bold) - зарезервированные слова С++, имена функций, классов и
структур появляются в тексте (но не в примерах программ) выделенные жирным
шрифтом. Этот шрифт используется и для опций утилит командной строки (такой
как -W).
Италик (Italic) - шрифт италик указывает на идентиикаторы данных,
которые появляются в тексте. Идентификаторы данных включают в себя имена
переменных и компонент данных класса. Этот тип используется и для выделения
определенных слов, например новых терминов.
Выделенный италик (Keycaps) - этот шрифт указывает на клавиши вашей
клавиатуры. Он обычно выделяет определенную клавишу или набор клавиш,
которые вы должны нажать; например, "Нажмите Shift+Ins для вставки данных
из буфера Clipboard".
Иконка с указывающим пальцем - эта иконка используется для выделения
важной информации , на которую вы обязательно должны обратить внимание.
Примечание переводчика !!!
Вместо этой иконки мы будем использовать слово "Внимание !!!".
Примечание переводчика !!!
Мы будем выделять тремя восклицательными знаками (!!!) замечания,
вынесенные на поля книги.
Соглашения по именам в ObjectWindows
Многие классы и структуры в иерархии ObjectWindows имеют имена,
начинающиеся с буквы Т (например TWindow). Для всех всех классов с Т
ObjectWindows так же определяет следующие связанные типы:
- Pclass, тип указатель на класс class. (Например, PTWindow
определяется как указатель на ТWindow).
- Rclass, тип ссылки на класс class. (Например, RTWindow определяется
как ссылка на TWindow).
- RРclass, ссылка на указатель на класс class. (Например, RРTWindow
определяется как ссылка на указатель на TWindow).
- РСclass, тип указатель на константу const классa class. (Например,
PCTWindow определяется как указатель на const TWindow).
- RClass, тип ссылки на константу const классa class. (Например,
RCTWindow определяется как ссылка на const TWindow).
Мы включили эти типы для облегчения некоторых сложных процедур
программирования в Windows. Например, в противном случае динамически
связываемые библиотеки в Windows (DLL) требовали бы объявлений, подобных
TWindow _FAR * !!! Это эквивалентно РТWindow.
, где требовался бы указатель на ТWindow, или
TWindow _FAR * _FAR & !!! Это эквивалентно RРТWindow.
для ссылки на указатель на ТWindow.
Использование типов P, R, RP, PC и RC облегчает преобразование вашей
прикладной программы, если позднее вы решите сделать ее частью DLL. Мы
широко используем эти типы в примерах программ в ObjectWindows.
Как связаться с фирмой Borland
Фирма Borland предоставляет собой широкий набор сервисных услуг по
ответу на ваши вопросы, связанные с данным продуктом. Убедитеся, что вы
послали нам вашу регистрационную карточку; зарегистрированные пользователи
имеют право на техническую поддержку и могут получать информацию об
изменениях данного продукта и других сопутствующих программных продуктах.
Возможности поставляемого пакета
Данный продукт содержит широкие возможности для помощи:
- руководства предоставляют исчерпывающую информацию по всем аспектам
программ. Используйте их как главный источник информации;
- вы можете использовать файлы интерактивной помощи.
Возможности фирмы Borland
Отдел технической поддержки фирмы Borland публикует информационные
беллютени по различным темам и доступен для ответа на любые ваши вопросы.
!!! 800-822-4269 (звуковой) TechFax
TechFax является круглосуточной автоматической справочной службой,
которая посылает техническую информацию на ваш факс бесплатно. Вы можете
запросить по телефону запрос на три документа за один звонок.
!!! 408-439-9096 (модем) File Download BBS
Электронная почта фирмы Borland File Download BBS имеет простые файлы,
прикладные программы и техническую информацию, к которой вы можете получить
доступ с помощью вашего модема. При этом не требуется никаких специальных
настроек.
Абоненты информационных служб CompuServe, GEnie или BIX могут получать
техническую поддержку посредством модема. Для контактов с фирмой Borland с
получением доступа к информационному серверу используйте команды из
следующей таблицы.
-------------------------
Сервис Команда
-------------------------
CompuServe GO BORLAND
BIX JOIN BORLAND
GEnie BORLAND
-------------------------
Адресуйте электронное сообщение к SYSOP или All. Не включайте ваш
серийный номер; сообщения не являются закрытыми, если не посылаются
электронной почтой. Включайте как можно больше информации по интересующему
вас вопросу; отдел технической поддержки ответит на ваше сообщение в
течении одного рабочего дня.
!!! 408-438-5300 (звуковой) Техническая Поддержка (Technical Support)
До звонка в Техническую Поддержку (Technical Support), просмотрите
внимательно ваш файл README; возможно это поможет разрешить проблему. В нем
содержится достаточно много информации по различным возникающим проблемам.
Если вы все же решили позвонить, то вы можете связаться с отделом
Технической Поддержки фирмы Borland с 6.00 до 17.00. Пожалуйста, звоните
недалеко от вашего компьютера. На руках вам следует иметь следующую
информацию:
- имя продукта, серийный номер и номер версии;
- типы и модели всех аппаратных частей вашей системы;
- операционную систему и ее версию (используйте команду ДОС VER для
установления номера версии);
- содержимое ваших файлов AUTOEXEC.BAT и CONFIG.SYS (расположенных в
корневом каталоге (\) вашего загрузочного диска);
- содержимое ваших файлов WIN.INI и SYSTEM.INI (расположенных в
каталоге для Windows);
- номер телефона, по которому с вами можно связаться в дневное время;
- если звонок касается некоторой проблемы, постарайтесь воспроизвести
ее по шагам.
ЧАСТЬ I. ЗНАКОМСТВО С ObjectWindows
-----------------------------------
Глава 1. Наследовать окно
В этой части описывается программирования для Microsoft Windows с
упором на объектоно-ориентированное программирование. Вы узнаете что
является составляющими частями Windows программы и как она работает. Вы
изучите требуемые свойства прикладной программы для Windows и как
объектно-ориентированное программирование с помощью ObjectWindows
автоматизирует решение многих задач и облегчает выполнение других.
Что такое прикладная программа для Windows
На Рисунке 1.1 показаны основные компонента приложения Windows. Для
успешного понимания и использования ObjectWindows вы должны быть знакомы с
этими компонентами.
Рисунок 1.1 Экранные компоненты приложения Windows
Приложение Windows является специальным типом программы, которая должна
- хранится в специальном формате исполняемого (.ЕХЕ) файла;
- выполнятся только под Windows;
- обычно выполнятся в прямоугольном окне на экране;
- удовлетворять строчному управляющему интерфейсу пользователя для
вывода и выполнения стандартным способом;
- быть способной выполнятся одновременно с другими Windows и не-Windows
программами, включая другие экземпляры самой себя;
- быть способной связывать и разделять данные с другими
Windows-программами.
Достоинства Windows
Windows предоставляет многие преимущества и для пользователей и для
создателей программ. Преимущества для пользователя включают в себя:
- стандартные и предопределенные операции. Если вы знаете как
использовать одну прикладную программу для Windows, то вы знаете как
использовать их все.
- нет необходимости устанавливать устройства и драйверы для каждой
программы. Windows предоставляет драйверы для поддержки переферийных
устройств.
- взаимодействие и связь между процессами.
- многозадачность: возможность выполнять несколько программ сразу.
- доступ к большей памяти. Windows поддерживает защищенный режим на
процессорах 80286, 80386 и 80486; она поддерживает виртуальную память на
процессорах 80386 и 80486.
Достоинства для разработчиков включают в себя следующие:
- независимую от устройств графику, поэтому графические прикладные
программы могут работать со всеми стандартными дисплейными адаптерами.
- встроенную поддержку широкого круга принтеров, мониторов и
указательных устройств, таких как "мышь" и т.п.
- богатую библиотеку графических подпрограмм;
- больший объем памяти для больших программ;
- поддержку меню, иконок, побитовых изображений и т.д.
Требования
Список достоинств, предоставляемых Windows, требует достаточно сложного
аппаратного обеспечения: Windows обычно требует для выполнения программ,
сравнимых по производительности с ДОС программами, лучшее графическое
аппаратное обеспечение, больше памяти и более быстрый процессор. Если у
вас машина с 80286 процессором или лучше с по крайней мере с 2Мб памяти, то
Windows будет работать превосходно.
Объектно-ориентированное программирование в Windows
Программирование для среды Windows требует знакомства с многими новыми
понятиями. Создание даже простой программы для Windows может оказаться
достаточно сложной задачей. ObjectWindows облегчает этот процесс, позволяя
сосредоточится на функционировании вашей прикладной программы а не на ее
форме.
Структура ObjectWindows позволяет вам использовать объекты для
представления довольно сложных элементов Windows программ. Оконные объекты
ObjectWindows формируют данные, которые удовлетворяют всем требованиям
окон, обеспечивают все основные операции с окномами и отвечают на основные
сообщения и события Windows. Классы приложений и окон ObjectWindows
полностью обеспечивают обработку сообщений, которая обычно занимает
подавляющую часть Windows программы.
Лучший интерфейс с Windows
ObjectWindows использует объектно-ориентирование возможности Borland
C++ для формирования частей API Windows, изолируя вас от деталей
программирования в Windows. В результате, вы можете писать Windows
программы с гораздо меньшими затратами времени и энергии. Проще говоря,
ObjectWindows обеспечивает три полезные возможности:
- формирование оконной информации;
- абстракцию многих функцийAPI Windows;
- автоматический ответ на сообщения.
Формирование оконной информации
ObjectWindows содержит объекты, которые определяют свойства и хранение
данных для окон, диалоговых блоков и управляющих средств прикладных
программ под Windows. В прикладной программе ObjectWindows интерфейсный
объект выступает как представитель явного интерфейсного элемента Windows.
Хотя интерфейсный объект и интерфейсный элемент работают в тесной
взаимосвязи, важно понимать различие между ними.
Интерфейсный объект связывается с новым интерфейсным объектом
посредством вызова (неявно) его функции-компоненты Create. Create вызывает
соответствующую функцию Windows для создания нового интерфейсного элемента.
Компонента данных интерфейсного объекта, передаваемые вызову Windows,
определяют физические атрибуты создаваемого интерфейсного элемента. Windows
возвращает дескриптор, или идентификатор, созданного интерфейсного объекта.
Интерфейсный объект хранит этот дескриптор как его компоненту данных
HWindow.
Функции Windows, которые оперируют с окном, требуют передачи его
дескриптора; хранение дескриптора как компоненты данных делает его легко
доступным. Аналогично, компоненты данных могут использоваться для хранения
инструментальных средств рисования или информации о состоянии для
определенного окна.
Абстрагирование функций Windows
Прикладные программы в Windows формируют свой внешний вид и поведение
путем вызова набора из более 600 функций , составляющих Windows API. Каждая
функция может иметь различное число параметров многих типов. Хотя вы можете
вызывать любую функцию Windows непосредственно из Borland C++,
ObjectWindows облегчает эту задачу, путем предоставления функций-компонент
объектов, которые абстрагируют многие вызовы функций.
Как замечалось выше, многие параметры для функций Windows уже хранятся
в компонентах данных интерфейсных объектов. Поэтому, функции-компоненты
могут использовать эти данные для передачи их функциям Windows в качестве
параметров. В дополнение, ObjectWindows группирует связанные вызовы функций
в одни функции-компоненты, которые обслуживают задачи более высокого
уровня. Результатом этого является направленный, легкий в использовании
интерфейс с Windows.
Хотя этот подход достаточно сильно снижает вашу зависимость от сотен
функций API Windows, он не исключает возможности их прямого вызова из API.
ObjectWindows содержит лучшее из двух миров: высокоуровневое,
объектно-ориентированное написание программ плюс максимальный контроль за
графической средой.
Автоматический ответ на сообщения
Большему числу Windows программ в дополнение к тому, что они должны
указывать среде Windows что ей делать, необходимо уметь реагировать на
сотни сообщений Windows, которые являются результатом действий пользователя
(например, нажатие кнопки "мыши"), других программ или других источников.
Помним, что Windows посылает вашей программе сообщение каждый раз, когда
пользователь нажимает кнопку "мыши" или передвигает ее указатель. Ваша
прикладная программа возможно будет получать сотни сообщений в течении
всего нескольких минут. Обработка и корректный ответ на сообщения является
важным фактором правильного функционирования вашей программы.
Объекты, с их способностью наследовать и переопределять поведение
(функции-компоненты), являются наиболее удобными для решения задачи ответа
на входящие стимуляторы (сообщения Windows). ObjectWindows берет сообщения
Windows и превращает их в вызовы функций-компонент Borland C++. Поэтому,
используя ObjectWindows, вы просто определяете функцию-компоненту для
ответа на каждое сообщение, на которое ваша программа должна реагировать.
Например, когда пользователь нажимает левую кнопку "мыши", то Windows
генерирует сообщение WM_LBUTTONDOWN. Если вы хотите, чтобы окно или
управляющий блок в вашей программе ответил на это нажатие, вы просто
определяете функцию-компоненту WMLButtonDown, являющеюся ключевой для
сообщения WM_LBUTTONDOWN. Затем, при посылке Windows этого сообщения, ваш
объект автоматически вызовет функцию-компоненту, определенную ранее.
Компоненты-функции такого тип называются функциями ответа на сообщения.
Вы коммутируете определенное сообщение Windows с функцией ответа на
сообщение использованием возможности компиляторов Borland C++, называемой
виртуальными таблицами динамического диспетчирования (DDVT). При объявлении
динамически диспетчируемой функции-компоненты вы также задаете и
целочисленный индекс диспетчирования. Например, в
virtual void Test() = [100];
Test определяется как динамически диспетчируемая функция-компонента класса
с индексом диспетчирования 100.
Если вы просмотрите файл WINDOWS.H, то вы увидите, что сообщениям
Windows даются описательные имена типа WM_LBUTTONDOWN, но в
действительности используются только целочисленные значения, поэтому вы
можете использовать DDVT с сообщениями Windows.
ObjectWindows направляет сообщение Windows той функции, чей индекс
диспетчирезации равен значению сообщения. Например,
virtual void WMLButtonUp(RTMessage Msg) = [WM_FIRST + WM_LBUTTONUP];
!!! Сумма констант WM_FIRST и WM_LBUTTONUP является целочисленной,
поэтому Windows использует его для идентификации сообщения.
объявляет функцию void, которая отвечает на сообщение Windows WM_FIRST +
WM_LBUTTONUP. Вам больше нечего делать - ObjectWindows знает когда вы
добавили новую функцию ответа на сообщение и автоматически начинает
отслеживание сообщений для нее. ObjectWindows ожидает, что функции ответа
на сообщения имеют тип параметра RTMessage. Помним, что RTMessage является
ссылкой на TMessage. Объекты TMessage содержат всю информацию, которую
Windows передает вместе с сообщением.
Внимание !!!
Существует несколько ограничений на использование динамически
диспетчируемых функций-компонент:
- При переопределении в производном классе динамически диспетчируемой
функции базового класса должен обязательно использоваться тот же индекс
диспетчирования. Например, следующее определение ошибочно:
class TestBase
{
virtual void sample() = [100];
};
class TestDerived : public TestBase
{
virtual void sample() = [299];
};
!!! Это определение ошибочно, так как индексы диспетчирования различны.
- Каждая динамически диспетчируемая функция в иерархии классов должна
иметь уникальный индекс диспетчирования. Например, следующее определение
ошибочно:
class TestBase
{
virtual void sample1() = [100];
virtual void sample2() = [200];
virtual void sample3() = [100];
};
!!! Это определение ошибочно, так как тут пытаются присвоить двум
различным функциям одинаковые индексы диспетчирования.
- "Обычная" (не динамически диспетчируемая) виртуальная функция в
базовом классе не может быть переопределена в производном классе
динамически диспетчируемой функцией. Например, следующее определение
ошибочно:
class TestBase
{
virtual void sample();
};
class TestDerived : public TestBase
{
virtual void sample() = [100];
};
!!! Это определение ошибочно, так как тут пытаются переопределить
обычную функцию динамически диспетчируемой.
- Динамически диспетчируемая функция в базовом классе не может быть
переопределена в производном классе "обычной" виртуальной функцией.
Например, следующее определение ошибочно:
class TestBase
{
virtual void sample() = [100];
};
class TestDerived : public TestBase
{
virtual void sample();
};
!!! Это определение ошибочно, так как тут пытаются переопределить
динамически диспетчируемую функцию обычной.
- Когда класс является производным из более чем одного базового класса,
то говорят, что этот производный класс имеет множественное наследование.
Только первый базовый класс для производного класса со множественным
наследованием может иметь динамически диспетчируемые функции. Например,
следующее определение класса является допустимым:
class A
{
virtual void AA() = [100];
};
class B
{
};
class C : public A, public B
{
};
!!! Здесь динамически диспетчируемые функции находятся в первом базовом
класс и все нормально.
Но это определение является недопустимым:
class A
{
};
class B
{
virtual void ВВ() = [100];
};
class C : public A, public B
{
};
!!! Это определение ошибочно, так как второй базовый класс, класс В,
имеет динамически диспетчируемые функции-компоненты.
- Когда объект класса указывается посредством указателя и не
используется конкретного переопределения контекста, вы не можете вызывать
динамически диспетчируемые функции. Например, следующий пример является
ошибочным:
class Base
{
virtual void sample() = [100];
};
int main(void)
{
Base b;
Base *BasePtr = &b;
BasePtr->sample();
}
!!! Это выражение является ошибочным, так как вы не можете вызывать
динамически диспетчируемые функции-компоненты непосредственно через
указатель.
- Виртуальный базовый класс не может иметь динамически диспетчируемых
функций-компонент. Например, следующий пример является ошибочным:
class Base
{
virtual void sample() = [100];
};
class derived : virtual public Base
{
};
!!! Это определение является ошибочным, так как виртуальный базовый
класс не может иметь динамически диспетчируемых функций-компонент.
Альтернативным методом ответа на сообщения Windows, которого
ObjectWindows избегает (но при программировании на Microsoft C требуется
обязательно), является использование длинных операторов switch с
ветвью case для каждого сообщения Windows, требующего обработки вашей
программой. Следующий пример приложения Windows взят из программы на С; как
вы можете видеть он имеет операторы switch внутри операторов switch.
switch (message)
{
case WMU_DDE_CLOSE:
CloseMsgWnd();
break;
case WM_COMMAND:
switch (wParam)
{
case IDM_QUIT:
// Пользователь выбрал QUIT из меню
PostMessage(hWnd, WM_CLOSE, 0, 0L);
break;
case IDM_HOME:
// Нажата клавиша Home
SendMessage(hWnd, WM_HSCROLL, SB_TOP, 0L);
SendMessage(hWnd, WM_VSCROLL, SB_TOP, 0L);
break;
case IDM_ABOUT:
// Пользователь выбрал ABOUT из меню
lpproc = MakeProcInstance(About, hInst);
DialogBox(hInst, MAKEINTRESOURCE(ABOUT), hWnd, lpproc);
FreeProcInstance(lpproc);
break;
...
!!! В этом примере показан не-объектно-ориентированный подход к
реализации ответов на сообщения Windows.
Очевидно, что с 200 различными сообщениями Windows эти операторы switch
станут быстро практически неуправляемыми. Используемый ObjectWindows DDVT
является более простым.
Структура Windows программы
Так как много элементов программного обеспечения, такого как ДОС,
Windows, ваша прикладная программа и другие прикладные программы,
одновременно взаимодействуют друг с другом, будет полезным знать, какая
часть вашей Windows программы взаимодействует с окружающей ее средой. В
этой части рассматривается структура Windows программы и приводится пример
типичной Windows программы, написанный на Botland C++ с помощью
ObjectWindows.
Структура Windows
Ко времени исполнения выполняемые функции Windows и его API находятся в
трех внешних библиотечных модулях, которые вызываются выполняемым в
настоящий момент приложением:
- KERNEL.EXE - отвечает за управление памятью и ресурсами, планирование
и взаимодействие с ДОС.
- GDI.EXE - отвечает за вывод графики на экран и принтер.
- USER.EXE - отвечает за управление окнами, ввод пользователя и
взаимодействия.
Эти модули являются частью поставляемой версии Windows. Мы
предполагаем, что ваши пользователи имеют Windows и данные модули уже
загружены на их компьютеры. Вы будете поставлять программы, которые
используют эти библиотечные модули, но не содержат их в себе.
Взаимодействие с Windows и с ДОС
Из-за ограниченной области действия операционной системы ДОС легко
упустить из виду тот вклад, который она вносит в успешное функционирование
ее прикладных программ. Тем не менее, ДОС программа работает благодаря
взаимодействию между вашей прикладной программой и элементами операционной
системы. То же самое относится и Windows программам. Так как Windows
предоставляет гораздо больше функций операционной системы, то гораздо
сложнее упустить взаимосвязь между Windows и прикладной программой. Ваша
программа должна постоянно взаимодействовать с операционной системой (ДОС
плюс Windows).
____________________________________________________________%
_ Прикладная программа Windows ^ _
_ ________________________________________________"
_ _ V _
_ ^ _ Модули Windows: GDI, USER и KERNEL _ _
____________<____________% ^ _
_ ^ V ДОС ___________________________________"
_________________________" V _
_ VДрайверы устройсв ДОС _ ^ Драйверы устройств Windows ^ _
_________________________<__________________________________"
_ V V V _
_ Компьютер и перефирийное оборудование _
____________________________________________________________T
"Hello, Windows" ("Здравствуй Windows")
Традиционным способом презентации нового языка или среды является
написание на этом языке или для этой среды программы "Hello, World". Эта
программа содержит только несколько команд, необходимых для вывода на
экран строки "Hello, World".
Конечно в Windows вам придется сделать достаточно много операций, чтобы
достигнуть результата. Вы должны высветить окно, написать в нем, и затем
задать порядок взаимодействия окна с окружающей его средой, по крайней мере
пока, достаточно просто закрыть его и удалить. Если вы будете это делать в
лоб, то решение этой простой задачи приведет к созданию достаточно длинной
программы.
Это происходит из за того, что Windows имеет ряд требований, которым
должна удовлетворять прикладная программа прежде чем она может быть
запущена. Даже простейшая программа реализуется достаточно большим исходным
кодом. К счастью, программы, написанные с помощью ObjectWindows,
автоматически удовлетворяют большему числу этих требований, включая
создание и вывод главного окна. Поэтому программа HelloWorld упрощается до
приведенной ниже:
!!! Это программа HELLOAPP.CPP
#include
// Определяем класс, являющийся производным от TApplication
class THelloApp :public TApplication
{
public:
THelloApp(LPSTR AName, HANDLE hInstance, HANDLE hPrevInstance,
LPSTR lpCmdLine, int nCmdShow) : TApplication(AName,
hInstance, hPrevInstance, lpCmdLine, NCmdShow) {};
virtual void InitMainWindow();
};
// Конструктор компоненты данных MainWindow класса THelloApp
void THelloApp::InitMainWindow()
{
MainWindow = new TWindow(NULL, "Hello World!");
}
int PASCAL WinMain(HANDLE hInstance, HANDLE hPrevInstance,
LPSTR lpCmdLine, int nCmdShow)
{
THelloApp HelloApp ("HelloApp", hInstance, hPrevInstance,
lpCmdLine, nCmdShow);
HelloApp.Run();
return Hello.App.Status;
}
Характеристики запуска прикладной программы
В начале исполнения прикладной программы, Windows передает ей четыре
параметра. В программе ObjectWindows эти параметры передаются конструктору
объекта прикладная программа, производного от класса TApplication. Эти
параметры также заносятся в компоненты данных объекта прикладная программа,
для обеспечения удобного доступа к ним из кода программы.
Компоненты данных приложения имеют следующее назначение:
- hInstance хранит дескриптор экземпляра приложения.
- hPrevInstance хранит дескриптор предыдущего экземпляра этого же
приложения.
- lpCmdLine хранит LPSTR на строку, соответсвующее командной строке
запуска приложения, включающую имена файлов и опции. Например, "CALC.EXE
/M" или "WORDPROC.EXE LETTER1.DOC".
- nCmdShow хранит целое число, представляющее начальный режим дисплея
для главного окна. Оно используется при вызове функции-компоненты Show.
Первым делом прикладная программа ObjectWindows должна сконструировать
объект прикладная программа из базового класса TApplication. В нашем
примере мы используем THelloApp.
Для первого экземпляра прикладной программы ObjectWindows, Run вызывает
InitApplication для проведения необходимых инициализаций для всего
приложения. Run инициализирует все экземпляры (включая первый) вызовом
InitInstance. Затем InitInstance вызывает функцию-компоненту InitMainWindow
для конструирования и инициализации объекта главное окно.
В заключении, Run запускает цикл главного сообщения вашей прикладной
программы ObjectWindows. Возврат Run происходит только при завершении
программы. Код возврата приложения заносится в компоненту данных Status,
которую вы должны возвратить в Windows, аналогично тому, как мы делали в
показанной ранее функции WinMain.
Характеристики главного окна
Главное окно прикладной программы является окном, которое при ее
запуске появляется первым. Оно ответственно за выдачу пользователю списка
доступных команд (меню). Во время сеанса работы с приложением главное окно
управляет его интерфейсом и во многих случаях, выступает в качестве
единственной рабочей области программы. В других, более сложных
приложениях, несколько окон могут выступать в качестве рабочих областей. И
наконец, при закрытии пользователем главного окна, оно инициализирует
процесс завершения приложения.
Процесс создания прикладной программы
Поскольку существуют определенный набор требований для любой Windows
программы (например, инициализация главного окна), то проще всего начинать
создание вашей прикладной программы с переделки для ваших целей уже
существующих Windows программ. ObjectWindows предоставляет широкий набор
простых программ; выберете из них наиболее подходящую для вас. Затем вам
придется пройти следующие этапы:
1. Создание нового программного кода.
2. Создание ресурсов для меню, блоков диалога и так далее.
3. Компиляция программы.
4. Отладка программы.
Глава 2. Шагаем по Windows
Теперь, когда вы познакомились с библиотекой ObjectWindows, вы готовы
к написанию первых простых программ ObjectWindows. В следующих нескольких
частях вы узнаете как создавать графические, интерактивные Windows
программы, поддерживающие меню, загрузку и сохранение файлов, вывод графики
и текста и , даже, имеющие собственные простейшие системы помощи. Вместе с
этим, мы расскажем вам об основных принципах создания приложений под
Windows, например, обработка сообщений, управление связями между
родительскими и "дочерними" окнами и автоматическая перерисовка графики.
Наше путешествие по Windows будет состоять из 10 шагов, описываемых в
частях со 2 по 6:
- шаг 1: простейшая Windows программа;
- шаг 2: класс главного окна;
- шаг 3: вывод текста в окно;
- шаг 4: рисование линий в окне;
- шаг 5: изменение толщины линий;
- шаг 6: вывод графики;
- шаг 7: меню для главного окна;
- шаг 8: добавление всплывающего окна;
- шаг 9: занесение изображения в файл;
- шаг 10: всплывающее окно помощи.
Если вы не поменяли имена каталогов, задаваемые по умолчанию, то
исходные тексты программ будут находится в подкаталоге EXAMPLES\STEPS
главного каталога OWL. Они имеют названия STEP1.CPP, STEP2.CPP и так далее,
соответствую номеру описываемого в данной книге шага.
На рисунке 2.1 показан внешний вид приложения, получаемого в конце 10
шага нашего путешествия по Windows.
Рисунок 2.1 Полное приложение ObjectWindows.
Создание прикладной программы ObjectWindows: предварительные
рассуждения
До того как мы начнем, мы обязаны сделать несколько общих замечаний по
программированию с использованием ObjectWindows. Первое, вы должны точно
задавать имена каталогов, с тем, чтобы компилятор и Компилятор Ресурсов
могли найти все необходимые файлы. Второе, вы должны точно задавать все
каталоги библиотек, иначе, TLINK не сможет найти все необходимые вашему
приложения библиотеки. И последнее, вы должны создать файл ресурсов,
содержащий соответствующие ресурсы ObjectWindows, и затем привязать этот
файл ресурсов к вашей прикладной программе.
Библиотека контейнерных классов
Библиотека классов ObjectWindows зависит от библиотеки контейнерных
классов в подкаталоге CLASSLIB. Все объекты ObjectWindows совместно
используют класс Object (определяемый в библиотеке контейнерных классов)
как свой базовый класс. В классах ObjectWindows также используются
некоторые другие классы из этой библиотеки. В ваших программах
ObjectWindows вы можете использовать любые из контейнерных классов.
Каталоги
Помимо стандартных для С++ файлов заголовка , как STDIO.H, вашей
прикладной программе возможно потребуются дополнительные файлы заголовков
ObjectWindows, например OWL.H. В этом случае для вспомогательных утилит вы
будете обязаны указывать их местонахождение. Для написания программ с
использованием библиотеки ObjectWindows вы обязательно должны включать, по
крайней мере, файл OWL.H. Более сложные приложения требуют включения и
других файлов заголовков. Например, программа, использующая класс TButton,
должна включать BUTTON.Н. Прикладные программы ObjectWindows и сама
ObjectWindows основываются на библиотеке контейнерных классов, поставляемой
с ObjectWindows, поэтому вы обязательно должны включать путь поиска
включаемой библиотеки контейнерных классов ObjectWindows.
Всем прикладным программам необходим доступ к стандартным библиотекам
Borland C++, плюс, к созданным вами самими и полученным от третьих лиц.
Кроме этого, приложения ObjectWindows требуют доступа к поставляемым
библиотекам контейнерных классов ObjectWindows и стандартным библиотекам
ObjectWindows.
В следующей таблице приведены создаваемые установочными утилитами по
умолчанию имена каталогов ObjectWindows или Borland C++. Если в
установочной утилите вы зададите другие имена, то вам нужно будет внести
соответствующие изменения в MAKE файлы и файлы проектов.
Таблица 2.1 Каталоги INCLUDE по умолчанию
------------------------------------------------------------------------
Тип Каталог INCLUDE Каталог библиотек
по умолчанию по умолчанию
------------------------------------------------------------------------
ObjectWindows \BORLANDC\OWL\INCLUDE \BORLANDC\OWL\LIB
Библиотека \BORLANDC\CLASSLIB\INCLUDE \BORLANDC\CLASSLIB\LIB
контейнерных классов
Borlan C++ \BORLANDC\INCLUDE \BORLANDC\LIB
------------------------------------------------------------------------
Указание необходимой библиотеки
Существует три различных библиотеки ObjectWindows и библиотеки
контейнерных классов - одна для каждой из моделей памяти: small (малой),
medium (средней) и large (большой). Эти библиотеки располагаются в каталогах
библиотек и приведены в следующей таблице:
Таблица 2.2 Библиотеки для каждой модели памяти
------------------------------------------------------
Модель памяти Библиотека Библиотека контейнерных
ObjectWindows классов
------------------------------------------------------
Small OWLWS.LIB TCLASSWS.LIB
Medium OWLWM.LIB TCLASSWM.LIB
Large OWLWL.LIB TCALSSWL.LIB
------------------------------------------------------
Программы ObjectWindows, использующие DLL
Динамически связываемые библиотеки (DLL), библиотеки импорта для
ObjectWindows и библиотека времени выполнения Borland C++ доступны как
показано в таблице 2.3. Если вы хотите, чтобы ваша прикладная программа
использовала одну из этих DLL, то вы должны связать ее с соответствующей
библиотекой импорта.
Таблица 2.3 DLL и библиотеки импорта
---------------------------------------------
Тип DLL Библиотека импорта
---------------------------------------------
ObjectWindows OWL.DLL OWL.LIB
Borland C++ BCRTL.DLL CRTLL.LIB
---------------------------------------------
Внимание !!!
Вы можете использовать одну, обе или ни одну из этих DLL; однако, если
вы используете по крайней мере одну DLL, то вы обязательно должны
компилировать вашу программу в модели памяти large (-ml) и с опцией
интеллектуальных обратных вызовов (-WS). Вы также должны связать вашу
прикладную программу с библиотекой контейнерных классов TCLASSDL.LIB. Не
существует DLL для библиотеки контейнерных классов.
Создания файла ресурсов
Хотя обычно для создания файлов ресурсов используют редактор ресурсов,
вы можете использовать и компилятор ресурсов командной строки RC. Файл
ресурсов ObjectWindows аналогичен любому другому файлу ресурсов в Windows,
за исключением возможного присутствия в нем включаемых файлов, специфичных
для ObjectWindows. Некоторые файлы ресурсов *с расширениями .DLG или .RC)
поставляются вместе с ObjectWindows. Четыре класса в ObjectWindows
ссылаются ни эти файлы для получения ресурсов, используемых в этих классах.
Таблица 2.4 Классы ObjectWindows, которые требуют файлов ресурсов
----------------------------------------------------
Классы Необходимый файл(ы) ресурсов
----------------------------------------------------
TInputDialog INPUTDIA.DLG
TFileDialog FILEDIAL.DLG
TEditWindow STDWNDS.DLG, EDITACC.RC, EDITMENU.RC
TFileWindow STDWNDS.DLG, FILEACC.RC, FILEMENU.RC
----------------------------------------------------
Если приложение ObjectWindows использует один из этих классов, то
убедитесь, что файл ресурсов для этого приложения включает в себя
соответствующие файлы ресурсов ObjectWindows. Простейший файл ресурсов для
прикладной программы, использующей классы TInputDialog и TFileDialog, может
быть следующим:
#include
#include
rcinclude inputdia.dlg
rcinclude filedial.dlg
...
Другие необходимые вашему приложению ресурсы будут следовать за
привиденными выше директивами. Файлы ресурсов ObjectWindows и файл OWLRC.H
располагаются в каталоге включаемых файлов ObjectWindows; этот каталог
должен обязательно задаваться в RC как опция командной строки. Поскольку
файл WINDOWS.H находится в каталоге включаемых файлов Borland C++, то этот
каталог также обязательно задается для RC как опция командной строки. Для
задания включаемых каталогов используйте опцию командной строки RC -i.
Несколько включаемых каталогов задается повторением опции -i для каждого из
них.
Создание прикладной программы ObjectWindows: специфика
Обсудив предварительные замечания к созданию Windows программ с помощью
библиотеки ObjectWindows (мы будем называть их ObjectWindows программы или
приложения), мы можем перейти к специфическим моментам этого создания.
Существует три способа создания прикладных программ ObjectWindows.
Первый (наиболее простой) - это изменить для своих целей один из
поставляемых MAKE-файлов. Второй - использовать менеджер проектов
Интегрированной Среды Разработки (ИСР) Borland C++. Третий, и последний, -
напрямую использовать утилиты командной строки. Здесь мы обсудим только
второй и третий способы, так как использование MAKE-файла почти совпадает с
прямым использованием утилит командной строки.
В этой документации мы предполагаем, что вы знакомы с утилитами,
входящими в Borland C++ (ИСР и утилиты командной строки). Вы также должны
быть знакомы с созданием Windows программ в общем. Более подробная
информация по этим вопросам приводится в документации по Borland C++.
Использование ИСР для создания приложения ObjectWindows
Для создания проложения ObjectWindows в ИСР Borland C++, вы должны
использовать файл проекта. Подробная информация о файлах проекта приводится
в документации по ИСР. Общие шаги построения проекта приложения
ObjectWindows включают в себя следующие:
- Для повышения скорости компиляции (и очень часто достаточно большого)
многих требуемых файлов заголовков вы должны выбрать опцию
предкомпилированных заголовков.
- Открыть файл проекта.
- Выбрать тип приложения ЕХЕ для Windows.
- Если вместе с вашей прикладной программой вы используете DLL,
DLL библиотеки времени выполнения Borland C++ или пользовательскую DLL, то
вы должны выбрать опцию интеллектуального обратного вызова и модель памяти
large.
- Вы рать любые другие требуемые опции компилятора и компоновщика.
- Задать включаемые каталоги. По умолчанию берутся следующие имена:
C:\BORLANDC\OWL\INCLUDE,
C:\BORLANDC\CLASSLIB\INCLUDE и
C:\BORLANDC\INCLUDE
- Задать каталоги библиотек. По умолчанию берутся следующие имена:
С:\BORLANDC\OWL\LIB
С:\BORLANDC\CLASSLIB\LIB и
С:\BORLANDC\LIB
- Добавить в проект имена исходных файлов вашего приложения (с
расширениями .CPP, .C, .ASM и т.д.).
!!! Предупреждение ! Не добавляйте здесь файлы заголовков, имеющие
расширение .Н !
- Добавьте в проект файл ресурсов .RC вашего приложения. Смотри более
подробную информацию о файлах проектов выше. В приводимом примере вы должны
добавить файл ресурсов STEPS.RC.
!!! Предупреждение ! Не добавляйте здесь файлы ресурсов диалога .DLG !
- Если вы хотите использовать ObjectWindows статически связанную с
вашим приложением, то добавьте библиотеку ObjectWindows так, чтобы она
соответствовала используемой модели памяти. Например, добавьте OWLWS.LIB
для модели памяти приложения small. В противном случае, если вы хотите
использовать динамически связываемую библиотеку (DLL) ObjectWindows
(OWL.DLL), то добавьте библиотеку импорта DLL ObjectWindows, OWL.DLL.
- Добавьте допустимую для модели памяти вашего приложения библиотеку
контейнерных классов ObjectWindows. Например, для приложения с моделью
памяти medium добавьте TCALSSWM.LIB. Допустимой библиотекой контейнерных
классов для приложения, которое использует DLL ObjectWindows, DLL
библиотеки времени выполнения Borland C++ или пользовательскую DLL
ObjectWindows, является TCLASSDL.LIB.
- Вы можете захотеть динамически связать библиотеку времени выполнения
Borland C++ с вашим приложением. Это уменьшит размер вашего .ЕХЕ файла. Для
этого просто добавьте в проект библиотеку импорта BCRTL.LIB.
- Создайте вашу прикладную программу. Менеджер проектов ИСР проверит
ваш проект на наличие подлежащих компиляции исходных файлов вашего
приложения и файла ресурсов .RC; затем компоновщик скомпонует промежуточные
объектные файлы .OBJ и библиотеки; и в конце, в файл ЕХЕ или DLL будут
добавлены соответсвующие ресурсы.
Использование утилит командной строки для создания прикладных программ
ObjectWindows
Если для компиляции и связывания вашего приложения ObjectWindows вы
хотите использовать компилятор командной строки Borland C++, то возможно
самым простым будет отредактировать ваш файл конфигурации (или создать его,
если еще не создали) для задания нужных каталогов включаемых файлов и
библиотек. Для задания требуемых каталогов в файле конфигурации используйте
директивы -i или -I.
Компиляция и связывание вашего приложения производится следующей строкой
команд и опций:
BCCX -WE myprog.cpp owlws.lib tclassws.lib
Этой командой вначале компилируется программа MYPROG.CPP и затем
происходит связывание ее с OWLWS.LIB и TCLASSWS.LIB.
Внимание !!!
Если вы используете вместе с вашим приложением DLL ObjectWindows, DLL
библиотеки времени выполнения Borland C++ или пользовательскую DLL
ObjectWindows, то вы должны обязательно производить компиляцию с опцией
командной строки -WS. Заметим, что для использования DLL ObjectWindows вы
должны создавать ваше приложение в модели памяти large. Например,
BCCX -WS -ml myprog.cpp owl.lib tclassdl.lib bcrtl.lib
компилирует MYPROG.CPP и затем связывает ее с библиотеками импорта OWL.LIB
и BCRTL.LIB и библиотекой статического связывания TCLASSDL.LIB.
Вы должны обязательно откомпилировать файлы ресурсов приложения.
Командная строка для этого будет иметь следующий вид:
RC -i c:\borlandc\owl\include -i c:\borlandc\include myprog.rc
myprog.exe
Заметим, что вы должны, или явно задать имена каталогов включаемых
файлов в командной строке RC, или создать переменную среды с именем INCLUDE
и задать в ней имена соответствующих каталогов. Например, вы можете
заменить предыдущую команду следующими:
SET INCLUDE=c:\borlandc\owl\include;c:\borlandc\include
RC myprog.rc myprog.exe
Оператор SET может быть размещен в вашем файле AUTOEXEC.BAT.
В обеих случаях RC компилирует файл ресурсов .RC в двоичный файл
ресурсов .RES и затем добавляет его в ЕХЕ файл прикладной программы.
Результатом всей работы является создание исполняемого ЕХЕ файла прикладной
программы.
Шаг 1: Простая Windows программа
Вы начнете создание вашей прикладной программы с написания простейшей
программки ObjectWindows, но она будет являться обязательным шагом в
создании всей нашей демонстрационной программы. Эта программа, она
находится в файле STEP1.CPP, может сохранятся как отправная точка в
создании всех программ в ObjectWindows. Первым шагом наше приложение
создает экземпляр и главное окно.
Требования прикладной программы
Все Windows имеют главное окно, которое появляется при старте
программы. Пользователь завершает работу прикладной программы закрытием
главного окна. В приложении ObjectWindows главное окна обычно является
оконным объектом. Его владельцем является объект прикладная программа,
который создает и выводит главное окно, обрабатывает сообщения Windows и
завершает работу прикладной программы. Объект прикладная программа
действует как объектоно-ориентированный заменитель самой прикладной
программы. Аналогично этому, ObjectWindows предоставляет классы окон,
диалогов и т.п. для ухода от деталей программирования в Windows.
Каждая программа ObjectWindows должна определить новый класс прикладная
программа, который происходит от поставляемого класса TApplication.
Экземпляр этого производного класса (объект прикладная программа)
конструируется в WinMain (точка входа в Windows программу). По соглашению,
типы (классы и экземпляры классов) обычно начинаются с буквы Т, а указатели
на типы - с букв РТ. В создаваемом нами приложении этот класс называется
TMyApp.
Ниже приводится главная функция нашего приложения:
int PASCAL WinMain(HANDLE hInstance, HANDLE hPrevInstance,
LPSTR lpCmdLine, int nCmdShow)
{
TMyApp MyApp("Simple ObjectWindows Program", hInstance,
hPrevInstance, lpCmdLine, nCmdShow);
MyApp.Run();
return MyApp.Status;
}
!!! Это фрагмент из файла STEP1.CPP.
В первом операторе WinMain конструирует объект прикладная программа
создаваемого нами приложения. Строка "Simple ObjectWindows Programm"
("Простая программа ObjectWindows") передается конструктору для занесения в
качестве значения его компоненты данных Name. Другие параметры,
передаваемые WinMain, также задаются конструктору и сохраняются как
компоненты данных объекта прикладная программа. MyApp.Run вызывается для
запуска приложения ObjectWindows. В последнем операторе возвращается
итоговый статус (компонента данных Status объекта прикладная программа).
Определение класса прикладная программа
Ваше приложение должно из стандартного класса ObjectWindows
TApplication породить новый класс (или несколько классов порожденных от
TApplacation). Этот новый класс должен переопределять по крайней мере одну
функцию-компоненту InitMainWindow. InitMainWindow конструирует объект
главное окно во время инициализации приложения ObjectWindows. Приведем
определение класса TMyApp:
class TMyApp : public TApplication
{
public:
TMyApp(LPSTR AName, HANDLE hInstance, HANDLE hPrevInstance, LPSTR
lpCmdLine, int nCmdShow) : TApplication(AName, hInstance,
hPrevInstance, lpCmdLine, nCmdShow) {};
virtual void InitMainWindow();
};
!!! Это фрагмент из файла STEP1.CPP.
Этот объект главное окно заносится в компоненту данных MainWindow
объекта прикладная программа. Говорят, что объект прикладная программа
владеет объектом главное окно, но эти два объекта не связаны иерархически.
Эта взаимоотношение владения называется экземплярное связывание.
Определение
TMyApp::InitMainWindow следующее:
void TMyApp::InitMainWindow()
{
MainWindow = new TWindow(NULL, Name);
}
!!! Это фрагмент из файла STEP1.CPP.
Объект главное окно класса TMyApp конструируется выше как экземпляр
класса TWindow. Передаваемый параметр NULL указывает, что окно должно быть
главным окном вашего приложения; второй параметр определяет титул окна. Мы
передаем компоненту данных Name объекта прикладная программа, которая к
этому времени установлена в значение "Simple ObjectWindows Programm". Ваш
объект главное окно будет сконструирован как экземпляр классов, производных
от классов ObjectWindows. В Шаге 2 мы заменим сконструированное здесь окно
на более интересное окно производного класса. На Рисунке 2.2 показан
внешний вид полученного окна.
Рисунок 2.2 Реализация первого Шага вашего приложения ObjectWindows
На этом шаге приложение просто выводит пустое окно, которое можно
перемещать, изменять размеры, максимизировать, минимизировать и закрывать.
Ниже приводится полный листинг программы в Шаге 1:
#include
class TMyApp : public TApplication
{
public:
TMyApp(LPSTR AName, HANDLE hInstance, HANDLE hPrevInstance, LPSTR
lpCmdLine, int nCmdShow) : TApplication(AName, hInstance,
hPrevInstance, lpCmdLine, nCmdShow) {};
virtual void InitMainWindow();
};
void TMyApp::InitMainWindow()
{
MainWindow = new TWindow(NULL, Name);
}
int PASCAL WinMain(HANDLE hInstance, HANDLE hPrevInstance,
LPSTR lpCmdLine, int nCmdShow)
{
TMyApp MyApp("Simple ObjectWindows Program", hInstance,
hPrevInstance, lpCmdLine, nCmdShow);
MyApp.Run();
return MyApp.Status;
}
Шаг 2: Класс главного окна
Программа, созданная на Шаге 1, состоит из двух объектов: объекта
прикладная программа и объекта окно. Объект прикладная программа, MyApp,
является экземпляром класса TMyApp, производного от TApplication. Указатель
на объект окно, содержащийся в компоненте данных MainWindow объекта
прикладная программа, указывает на экземпляр TWindow, групповое окно
ObjectWindows. Обычно вы определяете для главного окна ваш собственный
класс окно, который отражает специфичные для вашей прикладной программы
характеристики. В этом разделе вы создадите объект главное окно, путем
определения специального оконного класса, производного от TWindow.
Что такое оконный объект ?
В Шаге 1 мы видели, что объект прикладная программа инкапсулирует
стандартные характеристики Windows программы, включая конструкцию главного
окна. Класс TApplication реализует основные характеристики вашей прикладной
программы ObjectWindows.
Аналогично, оконный объект инкапсулирует необходимые характеристики
окна, включая способность к
- выводу и изменению размера;
- реакции на события;
- управлению дочерними окнами;
- закрытие в упорядоченном режиме.
Оконные классы ObjectWindows поддерживают все эти характеристики.
Для обеспечения большей гибкости и полезности вашей программы, вы
должны будете создавать новые оконные классы, производные от поставляемых в
ObjectWindows. Новые классы будут наследовать функции-компоненты и
компоненты данных имеющихся классов, а также добавлять некоторые новые.
Объектно-ориентированный подход оградит вас от постоянного "выдумывания
окон".
Дескрипторы
Все оконные объекты имеют, по крайней мере, три компоненты данных:
HWindow, Parent и список его "дочерних" окон. HWindow содержит дескриптор
окна. Дескриптор связывает интерфейсный объект, такой как окно, блок
диалога, или управляющий объект с с соответствующим ему интерфейсным
элементом. Он является в некотором смысле аналогом номерка к вашему пальто,
при оставлении его в гардеробе. Как вы предъявляете ваш номерок для
идентификации вашего пальто, так вы задаете дескриптор для идентификации
окна.
В работе с оконными объектами вы не обязательно должны манипулировать
непосредственно с дескриптором окна. Однако, это необходимо при
непосредственном вызове функций Windows. Например, для посылки блока
сообщения вы вызываете функцию API Windows MessageBox. Функция MessageBox
требует задания дескриптора родительского окна блока сообщения. Ниже
приводится вызов MessageBox как это возможно в функции-компоненте
производного оконного класса. Информация о функциях Windows API содержится
в Главе 4 "Функции Windows" руководства "ObjectWindows для С++. Справочное
руководство".
MessageBox(HWindow, "Do you want to save?", "Drawing has changed",
MB_YESNO | MB_ICONQUESTION);
Родительские и дочерние окна
Большинство окон не существует независимо от других: она связаны вместе
и действуют взаимосвязанно. Например, когда вы завершаете работу вашей
прикладной программы, то все управляемые ей окна должны быть уничтожены.
Windows устанавливает взаимоотношения между окнами как отношения между
"родителем" и "дочерьми". Родительское окно ответственно за свои дочерние
окна. ObjectWindows предоставляет компоненты данных для каждого оконного
объекта для сохранения связи со своим родительским окном и своими дочерними
окнами.
Компонента данных Parent содержит оконный объект родительского окна.
Это не родитель, в прямом смысле этого слова, а скорее владелец окна.
Третьей компонентой данных окна является список его дочерних окон,
который содержит список указателей на дочерние окна данного окна, если они
есть. Мы будем добавлять дочерние окна на Шаге 8.
Создание объекта главное окно.
Теперь, когда вы уже имеете представление о том, что содержат оконные
объекты, вы можете определить новый оконный класс, производный от класса
ТWindow, выступающий в качестве главного окна создаваемой нами прикладной
программы. Первое, для задания нового класса TMyWindow изменим описание
класса.
_CLASSDEF(TMyWindow)
class TMyWindow : public TWindow
{
public:
TMyWindow(PTWindowsObject AParent, LPSTR ATitle) :
TWindow(AParent, ATitle) {};
};
!!! Это фрагметн программы из файла STEP2.CPP.
Заметим, что конструктор для нового производного класса не определяет
новых характеристик, а просто вызывает конструктор базового класса
(TWindow).
Второе, изменяем TMyApp::InitMainWindow так, чтобы он конструировал
TMyWindow , а не TWindow, как главное окно прикладной программы.
void TMyApp::InitMainWindow()
{
MainWindow = new TMyWindow(NULL, Name);
}
!!! Это фрагметн программы из файла STEP2.CPP.
Объявление нового класса и реализация его в InitMainWindow является
тем, что необходимо для определения нового класса для главного окна TMyApp.
Объект прикладная программа вызывает компоненты-функции оконного объекта
для создания оконного интерфейсного элемента и для вывода его на экран. Вы
не должны об этом беспокоится.
Однако, TMyWindow не определяет новых свойств помимо тех, которые
наследуются из TWindow и TWindowsObject. Другими словами этот прием не
делает нашу прикладную программу более интересной. В следующем разделе вы
добавите несколько интересных свойств.
Ответы на сообщения
Наиболее быстрым способом извлечь пользу из оконного объекта - это
научить его реагировать на сообщения Windows. Например, при нажатии
пользователем в главном окне нашей прикладной программы на левую клавишу
"мыши" соответствующий оконный объект получает от Windows сообщений
WM_LBUTTONDOWN. Оно сообщает оконному объекту, что в нем пользователь нажал
на соответствующую клавишу "мыши". В этой ситуации также передается
координата точки, где нажата кнопка. (Эта информация используется в Шаге 6
данного руководства.) Аналогично, когда пользователь нажимает правую
клавишу "мыши", объект главное окно получает от Windows сообщение
WM_RBUTTONDOWN.
Следующим шагом мы должны научить главное окна, являющееся экземпляром
TMyWindow, как отвечать на эти сообщения и делать что-либо полезное. Для
перехвата и реагирования на сообщения Windows вы должны определить
функцию-компоненту вашего оконного класса для каждого типа получаемого
сообщения на которое необходимо ответить. Эти функции-компоненты называются
функциями-компонентами ответа на сообщения. Вы определяете
функцию-компоненту ответа на сообщение для каждого сообщения на которое вы
хотите прореагировать. (Если вы не определите ответ на определенное
сообщение, то ObjectWindows будет обрабатывать его процедурой, принятой по
умолчанию). Для указания на то, что данная функция-компонента является
функцией-компонентой ответа на сообщение, добавьте к ее объявлению индекс
диспетчирования как обсуждалось выше в данном руководстве. Заметим, что имя
функции не играет никакого значения, важен только индекс диспетчирования.
Для имен функций ответа на сообщения мы берем наименования соответствующих
сообщений Windows и выбрасываем из них символы подчеркивания.
class TMyWindow : public TWindow {
public:
...
virtual void WMLButtonDown(RTMessage Msg) =
[WM_FIRST + WM_LBUTTONDOWN];
...
};
!!! Это фрагметн программы из файла STEP2.CPP.
Msg является ссылкой на структуру TMessage (укороченная до типа
RTMessage ObjectWindows), содержащей информацию о происшедшем событии. (Об
этой структуре более подробно вы узнаете в Шаге 3). Всем
функциям-компонентам ответа на сообщения передается RTMessage и они имеют
тип возврата void.
Для начала, определим функции-компоненты ответа на сообщения только
осуществляющие вывод блока сообщения, говорящего о нажатии соответствующей
клавиши "мыши". Позднее, вы добавите более полезные ответы.
Приведем определение функции-компоненты ответа на нажатие левой клавиши:
void TMyWindow::WMLButtonDown(RTMessage)
{
MessageBox(HWindow, "You have pressed the left mouse button",
"Message Dispatched", MB_OK);
}
!!! Это фрагметн программы из файла STEP2.CPP.
Рисунок 2.3 Наша прикладная программа отвечает на событие
!!! Полный исходный код программы этого Шага приведен в конце данной
главы.
Завершение работы прикладной программы
Созданная нами программа прекращает работу при двойном нажатии кнопки
"мыши" на блоке управляющего меню главного окна, это маленький квадрат в
его верхнем левом углу. Окно и прикладная программа закрываются немедленно
после выполнения этого действия. Такое поведение является удобным лишь для
простых программ, но для более сложных является примитивным.
Например, вы когда-нибудь завершаете работу прикладной программы без
сохранения результатов вашей работы. Хорошая прикладная программа просто
обязана предложить пользователю сохранить результаты его работы, если они
не были еще сохранены. Вы легко добавите эту возможность в вашу программу
ObjectWindows. Просто добавте действия программы на двойное нажатие кнопки
"мыши" для выхода.
При попытке закрыть ваше ObjectWindows приложение, вызывается
виртуальная функция-компонента CanClose вашего класса прикладная программа.
Функция CanClose является функцией BOOL, которая указывает на согласие
(TRUE) пользователя закрыть прикладную программу. Как определено по
умолчанию, наследуемая из TApplication функция-компонента CanClose вызывает
функцию-компоненту объекта главное окно CanClose.
В большинстве случаев объект главное окно решает, закрывать ли
приложение.
Ваш класс главное окно, TMyWindow, наследует функцию-компоненту
CanClose из TWindowsObject. В случае наличия у окна дочерних окон, она
вызывает функцию-компоненту CanClose каждого из них. Если дочерние окна
отсутствуют (как в нашем случае), то она просто возвращает TRUE. Для
модификации поведения закрытия нашей прикладной программы вы должны
переопределить функцию-компоненту CanClose для класса главное окно:
BOOL TMyWindow::CanClose()
{
return MessageBox(HWindow, "Do you want to save?",
"Drawing has changed", MB_YESNO | MB_ICONQUESTION) == IDNO;
}
!!! Это фрагметн программы из файла STEP2.CPP.
Теперь при попытке закрыть вашу прикладную программу она выдаст вам
блок сообщения "Do you want to save?" ("Хотите ли вы сохранится?"). Нажатие
кнопки Yes возвращает FALSE и предотвращает закрытие окна и прикладной
программы. Нажатие кнопки No возвращает TRUE и завершает работу программы.
На Рисунке 2.4 показан результат работы модифицированной программы.
Рисунок 2.4 Шаг приложения с переопределенной реакцией на завершение
работы
Ниже приводится полный листинг нашей прикладной программы для Шага 2:
#include
class TMyApp : public TApplication
{
public:
TMyApp(LPSTR AName, HANDLE hInstance, HANDLE hPrevInstance, LPSTR
lpCmdLine, int nCmdShow) : TApplication(AName, hInstance,
hPrevInstance, lpCmdLine, nCmdShow) {};
virtual void InitMainWindow();
};
_CLASSDEF(TMyWindow)
class TMyWindow : public TWindow
{
public:
TMyWindow(PTWindowsObject AParent, LPSTR ATitle) :
TWindow(AParent, ATitle) {};
virtual BOOL CanClose();
virtual void WMLButtonDown(RTMessage Msg)
= [WM_FIRST + WM_LBUTTONDOWN];
virtual void WMRButtonDown(RTMessage Msg)
= [WM_FIRST + WM_RBUTTONDOWN];
};
BOOL TMyWindow::CanClose()
{
return MessageBox(HWindow, "Do you want to save?",
"Drawing has changed", MB_YESNO | MB_ICONQUESTION) == IDNO;
}
void TMyWindow::WMLButtonDown(RTMessage)
{
MessageBox(HWindow, "You have pressed the left mouse button",
"Message Dispatched", MB_OK);
}
void TMyWindow::WMRButtonDown(RTMessage)
{
MessageBox(HWindow, "You have pressed the right mouse button",
"Message Dispatched", MB_OK);
}
void TMyApp::InitMainWindow()
{
MainWindow = new TMyWindow(NULL, Name);
}
int PASCAL WinMain(HANDLE hInstance, HANDLE hPrevInstance,
LPSTR lpCmdLine, int nCmdShow)
{
TMyApp MyApp("Simple ObjectWindows Program", hInstance,
hPrevInstance, lpCmdLine, nCmdShow);
MyApp.Run();
return MyApp.Status;
}
!!! Это STEP2.CPP
Глава 3. Вывод информации в окно
В этой главе мы продолжим усовершенствование нашей прикладной
программы, которая после реализации Шага 3 является конечно слишком
примитивной с точки зрения полезности. Первое что мы сделаем, это выведем
текст в главное окно нашей программы. Затем, мы преобразуем нашу программу
в небольшое графическое приложение, способное осуществлять вывод линий в
главное окно. После этого мы заставим ее перерисовать выведенную
графическую информацию, изменяя при этом толщину линий, и наконец, научим
вас сохранять графику в файле для последующей загрузки.
Что такое контекст вывода ?
Вы можете понимать контекст вывода как представление поверхности окна,
предназначенной для вывода информации. Контекст вывода необходим Windows
для отрисовки в определенном окне графической и текстовой информации.
Дескриптор контекста вывода, его идентификатор, должен обязательно
передаваться функции "отрисовки" Интерфейса Графических Устройств Windows
(GDI).
Перед выводом в окно, вы должны получить контекст вывода. До начала
процесса вывода на экран, вызовете в одной из функций-компонент оконного
объекта функцию Windows GetDC:
TheDC = GetDC(HWindow);
GetDC возвращает дескриптор контекста вывода (определяемого в Windows
типа HDC), который может теперь передаваться функциям Windows GDI. Приведем
несколько примеров вызовов GDI:
LineTo(TheDC, 30, 45);
Rectangle(TheDC, 100, 100, 200, 200);
TextOut(TheDC, 20, 20, "Sample text", 11);
После завершения вывода текста или графики вы обязаны освободить
контекст вывода. Обеспечение освобождения контекста вывода полностью
ложится на вас. Как только вы закончили вывод, освобождайте полученный
контекст.
ReleaseDC(HWindow, TheDC);
Внимание !!!
Если вы не освободите все полученные контексты вывода, то вскоре вы
исчерпаете их (вся среда Windows имеет лимит в пять контекстов), и ваша
программа закончится аварийно, часто с зависанием компьютера. При аварийном
завершении работы вашей прикладной программы в течении 4-5 раз подряд при
осуществлении вывода на экран, самой вероятной причиной является
неосвобожденные контексты вывода.
Контекст вывода выполняет несколько важных функций. Первое, он
гарантирует, что вы осуществляете вывод текста или графики в пределах
границ заданного окна. Текст и графика выводимая функциями GDI урезается до
границ окна для которого получен контекст вывода.
Контекст вывода также поддерживает набор инструментальных средств
вывода (карандаш, кисточка, шрифт и палитра), используемых при вызове
функций GDI. В предыдущих примерах, выбранный в контексте вывода карандаш
будет использоваться для рисования линии при вызове функции LineTo;
нарисованная линия будет иметь характеристики выбранного карандаша (включая
цвет и ширину). Аналогично, выбранная в контексте вывода кисточка будет
использоваться функцией Rectangle для закраски заданной прямоугольной
области дисплея при . Выбранный шрифт будет использоваться функцией TextOut
для вывода строки "Sample Text".
Вы можете выбрать в контексте вывода новые инструментальные средства
для изменения внешнего вида выводимой информации. Например, вы можете
захотеть использовать расраску по шаблону, карандаш другого цвета или
различные стили шрифта. Выбор нового карандаша демонстрируется на Шаге 5.
Шаг 3 : Вывод в окно текстовой информации
Вывод текста в главное окно нашей прикладной программы мы будем
осуществлять согласно описанному выше плану: Вначале получим контекст
вывода, затем произведем собственно операции вывода, и, наконец, освободим
контекст вывода. С целью сделать наше программу более интересной, будем
выводить текст в ответ на нажатие левой клавиши "мыши". Вместо посылки
блока сообщения (что мы делали в Шаге 2), вы будете выводить координаты
точки на которой находился указатель "мыши" при нажатии левой клавиши.
Этот пример поможет вам разобраться и в системе координат Windows.
Например, "(20,30)" выводится в случае, когда клавиша "мыши" нажата в
точке, расположенной в 20 пикселах справа и 30 пикселах вниз от верхнего
левого угла области вывода окна (это область пользователя). Текст выводится
в точке нахождения указателя "мыши" в момент нажатия.
Рисунок 3.1 Вывод координат точки нахождения указателя "мыши" в момент
нажатия левой клавиши.
Помним, что событие нажатия левой клавиши приводит к генерации
сообщения WM_LBUTTONDOWN, которое обрабатывается функцией-компонентой
ответа на сообщение WMLButtonDown.
Структура сообщения
В Шаге 2 вы узнали, что аргумент Msg, передающаяся функции-компоненте
ответа на сообщение ссылка на структуру TMessage (укороченная до типа
RTMessage), содержит информацию о происшедшем событии. Эти данных хранятся
в компонентах структуры LParam и WParam (типа LONG и WORD соответственно).
Msg.LParam часто содержит две куска информации, первый - в старшем,
второй - в младшем слове. LParam объявляется как компонента объединения с
собой структуры LP, которая объявляет компоненты Hi и Lo типа WORD. Поэтому
вы можете использовать Msg.LP.Hi и Msg.LP.Lo для ссылки на старшие и
младшие слова Msg.LParam. Аналогично, WParam объявляется как компонента
объединения с собой cтруктуры WP. Однако Windows редко использует
компоненты Hi и Lo структуры WP.
В случае WMLButtonDown Msg.LP.Lo содержит координату х точки нажатия, а
Msg.LP.Hi - координату у. Чтобы заставить WMLButtonDown выводить координаты
точки нажатия вы должны преобразовать Msg.LP.Lo и Msg.LP.Hi в текст и
добавить знаки пунктуации. В этом примере для построения выводимой строки
используется функция sprintf.
Как только вы создали выводимую строку вы можете выдать ее на экран в
точку, где была нажата кнопка. Для этого вы вызываете Windows функцию
TextOut с передачей ей контекста вывода, координат точки (Msg.LP.Lo и
Msg.LP.Hi), строки S и длины строки strlen(S). Не забудем также получить и
освободить контекст вывода.
void TMyWindow::WMLButtonDown(RTMessage Msg)
{
HDC DC;
char S[10];
sprintf(S, "(%d,%d)", Msg.LP.Lo, Msg.LP.Hi);
DC = GetDC(HWindow);
TextOut(DC, Msg.LP.Lo, Msg.LP.Hi, S, strlen(S));
ReleaseDC(HWindow, DC);
}
!!! Это фрагмент программы из файла STEP3.CPP.
Очистка экрана
Вы можете добавить еще одну функцию в вашу прикладную программу вывода
текста - функцию очистки экрана. Заметим, что если вы однажды изменили
размер окна или накладываете и закрываете его, то выведенный текст
удаляется. Однако, вы можете захотеть принудительно очистить экран в ответ
на выбор пункта меню или некоторых действий пользователя, например нажатие
клавиши "мыши".
Мы будем очищать окно в ответ на нажатие провой клавиши "мыши". Для
реализации этого мы переопределим функцию-компоненту WMRButtonDown так,
чтобы она вызывала Windows функцию InvalidateRect чьи аргументами задают
закраску всего окна. Поскольку ваше окно еще не умеет закрашивать само
себя, то оно только очищает область пользователя:
void TMyWindow::WMLButtonDown(RTMessage)
{
InvalidateRect(HWindow, NULL, TRUE);
}
!!! Это фрагмент программы из файла STEP3.CPP.
Шаг 4 : Рисование линий в окне
Теперь когда вы уже знаете последовательность действий при выводе
информации (получение контекста вывода, собственно вывод и освобождение
контекста вывода), вы можете создать более сложную, интерактивную
графическую прикладную программу. В этой секции мы создадим простую
программу рисования в главном окне.
Реализуем следующие шаги:
1. Ответ на нажатие левой клавиши "мыши" вместе с перемещением
указателя в виде показа пунктирной линии по пути перемещения и последующего
рисования по ней сплошной линии.
2. Ответ на нажатие правой клавиши "мыши" выводом диалога ввода,
позволяющего пользователю изменить толщину линии.
3. Перерисовка окном своего содержимого, путем сохранения точек и вывода
их заново в ответ на сообщение Paint.
Для успешной реализации этих шагов необходимо знание модели перемещения
в Windows.
Модель перемещения
Будет полезным напомнить модель реакции Windows на события "мыши". Вы
уже видели Главе 2, что благодаря использованию поддержки DDVT, нажатие
лесой клавиши "мыши" приводит к выдаче сообщения WM_LBUTTONDOWN и вызову
функции-компоненты WMLButtonDoown. Ранее в этом руководстве мы реализовали
ответ на это сообщение в виде выдачи блока сообщения или выводе на экран
текстовой информации. Вы также видели, что нажатие на правую клавишу "мыши"
приводит к выдаче сообщения WM_RBUTTONDOWN и вызову функции-компоненты
WMRButtonDoown. Вы отвечали на это нажатие очисткой экрана. Но эти ответы
реагируют только на начальные нажатия клавиш "мыши". Но многие
интерактивные прикладные Windows программы требуют, чтобы вы, нажав на
клавишу "мыши", перемещали ее указатель по экрану для рисования линий или
прямоугольников, или размещения в определенном месте графических объектов.
В создаваемой нами графической программе мы должны перехватить события
перемещения и ответить на них рисованием линий.
Это реализуется ответом на некоторые новые для вас сообщения. Когда
пользователь перемещает "мышь" в новую точку окна, то принимается сообщение
WM_MOUSEMOVE. Когда пользователь отпускает левую клавишу "мыши", то
происходит прием сообщения WM_LBUTTONUP. Обычно окно получает одно
сообщение WM_LBUTTONDOWN, затем серию сообщений WM_MOUSEMOVE и за ними одно
сообщение WM_LBUTTONUP. Типичная графическая Windows программа отвечает на
сообщение WM_LBUTTONDOWN инициализацией процесса вывода (включая получение
контекста вывода). На сообщение WM_MOUSEMOVE она реагирует выводом или
перемещением графической информации, и, наконец, на сообщение WM_LBUTTONUP
она реагирует завершением процесса вывода (освобождение контекста вывода).
В следующей таблице приводятся наиболее часто получаемые сообщения о
событиях "мыши".
Таблица 3.1 Наиболее часто получаемые сообщения "мыши".
-------------------------------------------------------------------
Сообщение Событие
-------------------------------------------------------------------
WM_LBUTTONDOWN Пользователь нажал левую клавишу "мыши".
WM_RBUTTONDOWN Пользователь нажал правую клавишу "мыши".
WM_MOUSEMOVE Пользователь переместил "мышь".
WM_LBUTTONUP Пользователь отпустил левую клавишу "мыши".
WM_RBUTTONUP Пользователь отпустил правую клавишу "мыши".
WM_LBUTTONDBLCLK Пользователь дважды нажал левую клавишу "мыши".
WM_RBUTTONDBLCLK Пользователь дважды нажал правую клавишу "мыши".
-------------------------------------------------------------------
Реакция на сообщения перемещения
В следующей таблице приводятся ответы на сообщения перемещения в
создаваемой нами программе рисования линий:
Таблица 3.2 Сообщения, используемые в Шаге 4.
-------------------------------------------------------------------------
Сообщение Реакция
-------------------------------------------------------------------------
WM_LBUTTONDOWN Очистить экран. Затем получить контекст вывода и занести
его в DragDC. Позиционировать карандаш рисования в точку,
на которой нажата клавиша.
WM_MOUSEMOVE Нарисовать линию от предыдущей точки до текущей точки,
если клавиша "мыши" все еще нажата.
WM_LBUTTONUP Освободить DragDC.
-------------------------------------------------------------------------
Как говорилось ранее, за сообщением WM_LBUTTONDOWN всегда следует
сообщение WM_LBUTTONUP, с или без сообщений WM_MOUSEMOVE между ними. Когда
вы получаете сообщение WM_LBUTTONDOWN вы должны получить контекст вывода и
освободить его по сообщению WM_LBUTTONUP. Вы будете использовать один
контекст вывода для всех операций вывода пока пользователь нажимает левую
клавишу "мыши".
Сочетание получения контекста вывода с его освобождением является
обязательным условием правильного функционирования вашей программы. Однако,
вы можете добавить еще одну меру безопасности. Для TMuWindow, класса
главное окно, определите новую компоненту данных BOOL с именем ButtonDown.
WMLButtonDown установит ее в TRUE, а WMLButtonUp в FALSE. После этого вы
можете проверять значение ButtonValue до получения и освобождения контекста
вывода.
Ниже приводится три функции-компоненты ответа на события "мыши":
void TMyWindow::WMLButtonDown(RTMessage Msg)
{
InvalidateRect(HWindow, NULL, TRUE);
if ( !ButtonDown )
{
ButtonDown = TRUE;
SetCapture(HWindow);
DragDC = GetDC(HWindow);
MoveTo(DragDC, Msg.LP.Lo, Msg.LP.Hi);
}
}
void TMyWindow::WMMouseMove(RTMessage Msg)
{
if ( ButtonDown )
LineTo(DragDC, Msg.LP.Lo, Msg.LP.Hi);
}
void TMyWindow::WMLButtonUp(RTMessage)
{
if ( ButtonDown )
{
ButtonDown = FALSE;
ReleaseCapture();
ReleaseDC(HWindow, DragDC);
}
}
!!! Это фрагмент программы из файла STEP4.CPP.
Внимание !!!
MoveTo и LineTo являются графическими функциями Windows API, которые
перемещают текущую позицию вывода и рисуют линию к текущей позиции,
соответственно. Они требуют передачи указателя на контекст вывода, поэтому
TMyWindow сохраняет его в своей компоненте данных DragDC. (Помним, что мы
осуществляем вывод не непосредственно в окно, а его контекст вывода).
Если указатель "мыши" перемещается за пределы TMyWindow, сообщение
WM_MOUSEMOVE посылается ему и в этом случае, даже кода указатель
позиционируется на соседнее окно. Это происходит из-за того, что TMyWindow
захватывает входные данные "мыши" через обращение к SetCapture. (Вводные
данные "мыши" освобождаются через вызов ReleaseCapture).
Убедитесь, что в определении объекта для TMyWindow измены заголовки
функций-компонет WMMouseMove и WMLButtonUp:
virtual void WMLButtonDown(RTMessage Msg)
= [WM_FIRST + WM_LBUTTONDOWN];
virtual void WMLButtonUp(RTMessage Msg)
= [WM_FIRST + WM_LBUTTONUP];
!!! Это фрагмент программы из файла STEP4.CPP.
Шаг 5 : Изменение толщины линий
К настоящему моменту вы можете рисовать только тонкие линии. Но
традиционно, графические программы позволяют изменять толщину рисуемых
линий. Реально, вы изменяете не саму толщину линии, а только карандаш,
которым производится рисование. Карандаши, как и кисточки, шрифты и палитры
являются инструментальными средствами рисования, предоставляемыми
контекстом вывода. На этом шаге вы научитесь выбирать новые
инструментальное средства в контексте вывода и научите создаваемую вами
прикладную программу устанавливать заданную толщину рисуемой линии.
Вы также научитесь использовать диалог ввода (класса TInputDialog) для
обеспечения механизма изменения пользователем толщины линии. На Рисунке 3.2
показан активизированный диалог ввода.
Рисунок 3.2 Изменение толщины линии.
Выбор нового карандаша
Для реализации изменения толщины рисуемой линии вам необходимо, в
начале, узнать немного нового о графике Windows и контекстах вывода в
частности.
Инструментальными средствами рисования, используемыми для отображения
текстовой и графической информации, являются карандаши, кисточки и шрифты.
Описание этих элементов рисования хранится в памяти Windows как
интерфейсные элементы. Поэтому оба типа элементов идентифицируются с
помощью дескрипторов. Однако, элементы рисования не представлены объектами
ObjectWindows. Следовательно, только на вашей программе лежит
ответственность за создание и удаление используемых инструментальных
средств рисования.
Внимание !!!
Оставлении инструментальных средств ведет к значительному
непроизводительному расходу памяти Windows.
Представляйте инструментальные средства рисования как кисти художника,
а контекст вывода как холст. Как только рисующий имеет все инструментальные
средства (кисточки) и контекст вывода (холст), то он выбирает нужное
средство и начинает рисовать. Аналогично, Windows программа должна выбрать
инструментальное средство рисования в контексте вывода. Как же иначе вы
можете рисовать в окне текст и линии без выбора инструментального средства
рисования ? Все контекста вывода имеют в своем составе набор
инструментальных средств, принятый по умолчанию: тонкий черный карандаш,
толстую черную кисть и системный шрифт. В нашем приложении мы выберем для
рисования линий в окне более тонкий карандаш.
Первым шагом реализуем в нашем приложении поддержку диалога ввода.
Поддержка диалога ввода определяется в файле заголовка INPUTDIA.H. Добавим
его в наш исходный файл .СРР. Диалоги ввода предполагают, что вы определили
ресурс диалогов в файле .RC. Ресурс диалога ввода определяется в файле
INPUTDIA.DLG. Включим этот файл в наш файл .RC. Файл STERS.RC содержит все
определения ресурсов, используемых в создаваемой нами прикладной программе.
На этом шаге файл ресурсов .RC выглядит так
#include "windows.h"
#include "owlrc.h"
rcinclude inputdia.dlg
Следующим шагом добавим к TMyWindow компоненту данных для хранения
дескриптора инструментального средства карандаш, который мы будем
использовать для вывода графики. В нашей программе ограничимся только
рисованием и отображением линий, имеющих только одно значение толщины в
каждый текущий момент времени. Карандаш, соответствующий этой толщине,
будет хранится в новой компоненте данных TMyWindow, называемой ThePen. Вы
также создадите функцию-компоненту SetPenSize, отвечающую за создание
нового карандаша и удаление старого. Объявление класса TMyWindow теперь
будет выглядеть так:
_CLASSDEF(TMyWindow)
class TMyWindow : public TWindow
{
public:
HDC DragDC;
BOOL ButtonDown;
HPEN ThePen;
int PenSize;
TMyWindow(PTWindowsObject AParent, LPSTR ATitle);
~TMyWindow();
virtual BOOL CanClose();
void SetPenSize(int NewSize);
virtual void WMLButtonDown(RTMessage Msg)
= [WM_FIRST + WM_LBUTTONDOWN];
virtual void WMLButtonUp(RTMessage Msg)
= [WM_FIRST + WM_LBUTTONUP];
virtual void WMMouseMove(RTMessage Msg)
= [WM_FIRST + WM_MOUSEMOVE];
virtual void WMRButtonDown(RTMessage Msg)
= [WM_FIRST + WM_RBUTTONDOWN];
};
!!! Это фрагмент программы из файла STEP5.CPP.
Для инициализации этих новых компонент данных модифицируйте конструктор
с точки зрения установки карандаша, и определите деструктор для удаления
карандаша.
TMyWindow(PTWindowsObject AParent, LPSTR ATitle)
: TWindow(Aparent, ATitle)
{
ButtonDown = FALSE;
PenSize = 1;
ThePen = CreatePen(PS_SOLID, PenSize, 0);
}
TMyWindow::~TMyWindow()
{
DeleteObject(ThePen);
}
!!! Это фрагмент программы из файла STEP5.CPP.
Теперь изменим функцию-компоненту WMLButtonDown так, чтобы она выбирала
текущий карандаш (ThePen) в последнем полученном контексте вывода.
void TMyWindow::WMLButtonDown(RTMEssage Msg)
{
InvalidateRect(HWindow, NULL, TRUE);
if ( !ButtonDown)
{
ButtonDown = TRUE;
SetCapture(HWindow);
DragDC = GetDC(HWindow);
SelectObject(DragDC, ThePen);
MoveTo(DragDC, Msg.LP.Lo, Msg.LP.Hi);
}
}
!!! Это фрагмент программы из файла STEP5.CPP.
!!! Подобно MoveTo и MessageBox, SelectObject является функцией Windows
API.
Замена карандаша
Описанная выше функция-компонента выбирает в контексте вывода уже ранее
созданный карандаш. Для создания нового карандаша вам необходимо определить
следующую функцию-компоненту SetPenSize:
void TMyWindow::SetPenSize(int NewSize)
{
DeleteObject(ThePen);
ThePen = CreatePen(PS_SOLID, NewSize, 0);
PenSize = NewSize;
}
!!! Это фрагмент программы из файла STEP5.CPP.
Внимание !!!
Одним из способов создания карандаша с заданной толщиной является вызов
Windows функции CreatePen: Вы храните дескриптор карандаша в ThePen.
Удаление предыдущего карандаша в этом случае является очень важным шагом:
Если вы не удалите его, то это приведет к непродуктивному расходу памяти
Windows и замедлению работы.
Реализация диалога ввода
Нажатие правой клавиши "мыши" является удобным способом активизации
средства изменения толщины линии. Давайте переопределим функцию-компоненту
WMRButtonDown активизирующую блок диалога ввода, одного из диалоговых
средств ObjectWindows. Блок диалога ввода выступает как просто блок
диалога, получающий одну текстовую строку от пользователя.
Как только объект диалога ввода сконструирован, вы можете запускать его
вызовом TModule::ExecDialog как модельный диалог. ExecDialog, используемая
для диалоговых объектов, аналогична MakeWindow, которая используется для
оконных и диалоговых объектов представляющих немодельные блоки диалога.
Обе функции создают интерфейсный элемент, только если интерфейсный объект
может быть проверен. ExecDialog, однако, возвращает значение только после
того, как пользователь закрыл диалог нажатием ОК или Cancel. Если
пользователь нажал ОК, то InputText заполняется введенной пользователем
информацией. Поскольку вы запрашиваете числовую толщину линии, то вы должны
преобразовать возвращенный текст в число, и передать его вызову SetPenSize.
Каждый раз, когда пользователь выбирает новую толщину линии, удаляется
старый карандаш и создается новый.
void TMyWindow::WMRButtonDown(RTMessage)
{
char InputText[6];
int NewPenSize;
sprintf(InputText, "%d", PenSize);
if ( GetApplication()->ExecDialog(new TInputDialog(this, "Line
Thickness", "Input a new
thickness:", InputText, sizeof
InputText)) == IDOK )
{
NewPenSize = atoi(InputText);
if ( NewPenSize < 0 )
NewPenSize = 1;
SetPenSize(NewPenSize);
}
Шаг 6 : Вывод графической информации
Вы возможно будете удивлены, когда узнаете, что выведенная в окно с
помощью функций Windows подобных TextOut и LineTo графика и текст изчазнут
при изменении размеров или раскрытии окна. Куда они делись ?
Правильнее будет переформулировать вопрос "Где они были в начале ?". Вы
никогда не сохраняете текст или линии ни в одном типе переменных. Как
только графические данные были посланы на экран с помощью функций Windows,
вы уже не можете вернуть их назад для перерисовки. Для реализации
перерисовки графической информации в окне, вы должны сохранять графику в
некоторых других типах структур; объект как более никто подходит для этой
задачи. Объект может хранить простую или сложную графическую информацию, и
может легко обрабатываться как компонента данных главного окна.
Модель рисования
Когда изображение одного из ваших окон требует обновления, или
осуществления рисования, Windows посылает вашей прикладной программе
сообщение WM_PAINT. Например, сообщение WM_PAINT посылается когда одно из
ваших окон по требованию пользователя изменяет свой размер или
раскрывается. ObjectWindows перехватывает сообщение WM_PAINT и вызывает
функцию-компоненту вашего окна WMPaint.
ТObjectWindows определяет функцию-компоненту WMPaint, которая вызывает
виртуальную функцию Paint, осуществляющую необходимые оконные установки и
очистки до и после вызова. Переопределите Paint в классах, производных от
TWindows, с целью рисования вашего оконного изображения.
Существует одно существенное различие между выводом графикм в
функции-компоненте Paint и в других случаях, это ответ на действия "мыши".
Используемый для рисования контекст вывода передается в параметре PaintDC,
поэтому вашей программе не надо получать и освобождать его. Однако, вам
надо будет выбрать заново в PaintDC инструментальное средство рисования.
Для отрисовки содержимого вашего окна, повторите действия
осуществляемые при оригинальном рисовании на DragDC, но с использованием
PaintDC. Видимый эффект будет как и при рисовании в первый раз, которое
осуществ |