- •История Windows
- •Windows 1.0
- •Windows 1.3
- •Windows 2.0
- •Windows 3.0
- •Windows 3.1
- •Windows for Workgroups версии 3.11
- •Windows NT
- •Windows 95
- •Windows NT Workstation 4.0 и Windows NT Server 4.0
- •Windows NT 4.0 Server Enterprise Edition
- •Windows 98
- •Windows 98 Second Edition (SE)
- •Windows 2000
- •Windows 98 Millenium Edition (ME)
- •Windows XP
- •Общая архитектура WINDOWS приложения.
- •Каркас приложения
- •Регистрация класса окна
- •Создание окна.
- •Отображение окна.
- •Цикл обработки очереди сообщений.
- •WndProc
- •Обработка сообщений типового приложения.
- •Контрольные вопросы
- •Сообщения
- •Типы сообщений
- •Обработка клавиатурных сообщений
- •Обработка сообщений от мыши
- •Сообщения таймера
- •Контрольные вопросы
- •Организация ввода
- •Разупорядоченный ввод
- •Разделение потоками виртуальных очередей ввода
- •Локальное состояние ввода
- •Ввод с клавиатуры и фокус
- •Управление курсором мыши
- •Контрольные вопросы
- •Окна
- •Окна Windows
- •Иерархия окон
- •Оконная процедура, стандартные оконные процедуры
- •Стиль класса окна
- •Стили окон, окна основных стилей
- •Перекрывающиеся окна
- •Временные окна
- •Дочерние окна
- •Сообщения управления окнами
- •Окна с полосами прокрутки
- •Системные метрики
- •Определение размера окна
- •Определение расположения окна
- •Контрольные вопросы
- •Графический интерфейс устройств(GDI).
- •Создание (получение) контекста устройств.
- •Сохранение контекста устройства.
- •Прямые (отрезки) и кривые
- •Закрашенные области
- •Битовые шаблоны (растровые шаблоны, растровые образы)
- •Текст и шрифты
- •Режимы масштабирования и преобразования
- •Метафайл
- •Регионы
- •Путь
- •Палитры
- •Контрольные вопросы
- •Объекты ядра
- •Что такое объект ядра
- •Учет пользователей объектов ядра
- •Защита
- •Таблица описателей объектов ядра
- •Создание объекта ядра
- •Закрытие объекта ядра
- •Синхронизация объектов
- •Именованные объекты
- •Дублирование описателей объектов
- •Изменение флагов описателя
- •Синхронизация потоков
- •Критическая секция
- •Мьютексы
- •Семафоры
- •События
- •Ожидаемые таймеры
- •Контрольные вопросы
- •Ресурсы Windows приложения
- •Файлы ресурсов приложения
- •Значки (пиктограммы)
- •Курсоры
- •Битовые образы
- •Символьные строки
- •Ресурсы, определяемые пользователем
- •Таблица акселераторов
- •Меню
- •Диалоговые окна
- •Контрольные вопросы
- •Элементы управления
- •Дочерние окна управления
- •Создание дочерних окон
- •Сообщения дочерних окон родительскому окну
- •Сообщения родительского окна дочерним окнам
- •Дочерние окна и фокус ввода
- •Дочерние окна управления и цвет
- •Кнопки различных стилей (класс BUTTON)
- •Нажимаемые кнопки
- •Флажки-переключатели
- •Переключатели
- •Окна групп
- •Кнопки, определяемые пользователем
- •Статические поля (класс static)
- •Текстовые редакторы (класс edit)
- •Списки разных стилей (класс listbox)
- •Комбинированные списки (класс combobox)
- •Полосы прокрутки (класс scrollbar)
- •Контрольные вопросы
- •Расширенные элементы управления
- •Основы общих элементов управления
- •Инициализация библиотеки элементов общего пользования
- •Создание элементов управления общего пользования
- •Посылка сообщений общим элементам управления
- •Уведомляющие сообщения от общих элементов управления
- •Контрольные вопросы
- •Стандартные диалоговые окна.
- •Окна сообщений
- •Диалоговые окна общего пользования
- •Контрольные вопросы
- •Динамически подключаемые библиотеки
- •Создание DLL
- •Проецирование DLL на адресное пространство процесса
- •Функция входа/выхода
- •Функция DllMain и стандартная библиотека С
- •Функция LibEntry
- •Функция LibMain
- •Функция WEP
- •Экспорт функций и переменных из DLL
- •Импорт функций и переменных из DLL
- •Заголовочный файл DLL
- •Разделы в ЕХЕ- и DLL-файлах
- •Контрольные вопросы
- •Файлы, проецируемые в память
- •Контрольные вопросы
- •Классы, определяющие архитектуру приложения
- •Классы приложения и поддержки потоков
- •Классы приложения и потоков
- •Классы фреймов окон
- •Получение информации о приложении
- •Соглашения об именах MFC
- •Включаемые файлы
- •Функция WinMain
- •Класс CWinApp
- •Класс CWnd
- •Класс CFrameWnd
- •Создание главного окна SDI-приложения
- •Примечание
- •Создание главного окна SDI-приложения
- •Создание дочерних окон
- •Ограничение размеров окна
- •Ограничение доступа к окну
- •Создание многодокументных приложений
- •Класс CMDIChildWnd
- •MDI — пример приложения
- •Категории сообщений
- •Карта сообщений
- •Компоненты карты сообщений
- •Класс CCmdTarget
- •Стандартный маршрут команды
- •Команды обновления и класс CCmdlll
- •Функции для работы с сообщениями
- •Основные положения
- •Класс CDocTemplate
- •Класс CSingleDocTemplate
- •Роль фреймов в архитектуре "документ/представление"
- •Документ и его представления
- •Документы
- •Класс CDocument
- •Класс CArchive
- •Представления
- •Класс CView
- •Панели элементов управления
- •Класс CStatusBar
- •Класс CStatusBarCtrl
- •Класс CDialogBar
- •ControlBars — пример приложения
- •НЕКОТОРЫЕ КЛАССЫ MFC
- •Класс CObject - основной класс MFC
- •Конструкторы класса
- •Оператор присваивания
- •Диагностика
- •Проверка целостности объектов класса
- •Получение дампа объекта класса
- •Сохранение и восстановление состояния объекта
- •Метод IsSerializable
- •Виртуальный метод Serialize
- •Информация о классе
- •Виртуальный метод GetRuntimeClass
- •Метод IsKindOf
- •Класс CPoint - точка на плоскости
- •Класс CSize - относительные координаты
- •Класс CString - текстовые строки
- •Конструктор класса
- •Коллекции
- •Массивы - шаблон CArray
- •Списки - шаблон CList
- •Словари - шаблон CMap
- •Класс CTime - дата и время
- •Файловая система - класс CFile
- •Открытие и создание файлов
- •Идентификатор открытого файла
- •Закрытие файлов
- •Чтение и запись файлов
- •Метод Flush
- •Операции с файлами
- •Блокировка
- •Позиционирование
- •Характеристики открытого файла
- •Файловая система - классы CMemFile и CStdioFile
- •Модификация класса CMemFile
- •Файловая система - класс CStdioFile
- •Запись и восстановление объектов
- •Запись в архивный файл
- •Чтение из архивного файла
- •Исключения - класс CException
- •Класс CException
- •Класс CMemoryException
- •Класс CFileException
- •Приложение Except
- •Класс CArchiveException
- •Класс CNotSupportedException
- •Класс CResourceException
- •Класс CUserException
ON_NOTIFY_EX_RANGE (wNotifyCode, idFirst, idLast, memberFn)
Команды, определяемые пользователем, включаются в карту сообщений при помощи макроса
ON_MESSAGE (WM_NAMEMSG, OnNameMsg)
В качестве параметров этот макрос использует номер (WM_NAMEMSG) и имя (OnNameMsg) обработчика сообщения. Очевидно, что необходимо определить как номер сообщения, так и его функцию обработки:
#define WM_NAMEiMSG (WM_USER + 100) afx _ msg LRESULT OnNameMsg (
WPARAM |
wParam, |
LPARAM |
1 Pa ram |
Ясно, что в этом случае всю ответственность по использованию параметров wParam и IParam берет на себя программист.
Сообщения, определяемые пользователем, включаются в карту при помощи макроса
ON_REGISTERED_MESSAGE(nMessageVariable, memberFn)
В качестве параметров этот макрос использует идентификатор зарегистрированного сообщения nMessageVariable и имя обработчика memberFn. Обработчик должен иметь следующий прототип:
afx_msg LRESULT memberFn( WPARAM wParam, LPARAM 1Param);
Для получения уникального идентификатора сообщения необходимо вызвать функцию Windows RegisterWindowMessage, описанную ниже в этой главе.
Теперь, ознакомившись с общей структурой карты сообщений и ее компонентами, можно переходить к вопросу о том, в какой последовательности и кем осуществляется поиск обработчика сообщения или команды. Если внимательно посмотреть на иерархию классов библиотеки MFC, приведенную в приложении 1, то можно заметить, что все классы, которые могут получать сообщения, являются производными от класса CCmdTarget.
Класс CCmdTarget
Подобно тому как класс CObject является базовым для практически всех классов библиотеки MFC, класс CCmdTarget выступает в качестве базового для всех классов, которые могут работать с сообщениями.
Начнем с рассмотрения функций, входящих в состав класса CCmdTarget, и определенных в файле
AFXWIN.H:1
void CCmdTarget: :BeginWaitCursor() —
изображает курсор в виде песочных часов. Обычно вызывается, чтобы показать пользователю, что выполняется длительная операция, такая как загрузка файла или сохранение документа в файле на диске.
void CCmdTarget::EndWaitCursor() -
восстанавливает предыдущее изображение курсора, которое было изменено на "песочные часы" при вызове функции BeginWaitCursor.
void CCmdTarget: :RestoreWaltCursor() —
восстанавливает соответствующий курсор в виде песочных часов, после того как он был заменен на системный. virtual BOOL CCmdTarget::OnCmdMsg(
UINT nID, int nCode, void* pExtra,
AFX_CMDHANDLERINFO *pHandlerlnfo) -
устанавливаетмаршрутираспределяеткомандные сообщения. Еслирассмотретьпоставляемые исходные тексты, то видно, что это единственная функция (не считая оконной процедуры по умолчанию), которая вызывается в оконной процедуре. Этоозначает, чтоименнооназанимаетсяпоискомсоответствующихобработчиковсообщений.
В качестве параметров функция OnCmdMsq принимает: идентификатор команды — nfD; код извещения
— nCode; значение параметра pExtra определяется значением параметра nCode; pHandlerlnfo — обычно этот параметр равен NULL, в противном случае функция сама заполняет элементы pTarget и pmf
структурыpHandlerlnfo.
Если функция нашла своего адресата, который обработает сообщение, то она возвращает значение TRUE, в противном случае FALSE.
Таким образом, основным назначением функции OnCmdMsg является распределение команд по другим объектам. В принципе, можно переопределить эту функцию. Но при этом не следует забывать, что на вас перекладывается задача указания следующего получателя команды, чтобы не прервалась цепочка передачи сообщений.
К практическим аспектам обработки сообщений Windows и извещений элементов управления мы приступим, когда ознакомимся с имеющимися в библиотеке MFC классами, отвечающими за работу с окнами, а пока займемся рассмотрением достаточно важного вопроса — каким образом осуществляется поиск обработчика команды.
Стандартный маршрут команды
Когда Run — функция класса CWinThread — получает сообщение, она направляет его в соответствующее окно-получатель сообщения, где оно будет обрабатываться. Несколько иначе все выглядит для команд. Поскольку они формируются в результате взаимодействия пользователя с приложением, то, обычно, команда начинает свой путь к получателю от главного окна приложения. Напомним, что каждый объект, способный получать сообщения, располагает своей картой сообщений. Что же происходит, когда какой-либо объект получает сообщение? А все очень просто. Имея свою карту сообщений получатель ищет в ней поступившую команду (или сообщение) и в случае успеха запускает соответствующий обработчик. Сразу же возникает вопрос: а если не найдет? Ну что ж, в этом случае поиск переносится в карту сообщений базовых классов, а если и там нет ассоциированного обработчика, то главное окно передает эту команду следующему кандидату на обработку. Для его определения в библиотеке MFC определен стандартный маршрут, по которому передается команда. Этот процесс продолжается до тех пор, пока команда не будет выполнена или отправлена оконной процедуре, действующей по умолчанию.
Порядок, в котором адресаты передают команды, "зашит" в библиотеке классов MFC и представляет собой следующую последовательность шагов:
1. Команду получает фрейм MDI (объект класса CMDIFrameWnd или производного от него). Порядок получателей команды:
•активное окно класса CMDIChildWnd,
•сам объект класса CMDIFrameWnd,
•приложение-объект класса CWinApp или производного от него.
2. Команду получает фрейм документа (объект класса CFrameWnd, CMDIChildWnd или производного от одного из них). Порядок получате лей команды:
•активное представление (view),
•текущий фрейм документа,
•приложение-объект класса CWinApp или производного от него.
3.Команду получает представление. Порядок получателей команды:
• текущее представление,
• документ, присоединенный к представлению.
4.Команду получает документ. Порядок получателей команды:
•текущий документ,
•шаблон документа, присоединенный к нему.
5.Команду получает блок диалога. Порядок получателей команды:
«текущий блок диалога,
•окно, которое владеет этим блоком диалога,
•приложение-объект класса CWinApp или производного от него.
Очевидно, что представленные здесь переходы взаимозависимы. Например, если команду получает документ, присоединенный к текущему представлению, то следующим объектом, которому будет передана команда, является текущий документ, а затем его шаблон.
Чтобы сказанное стало более понятным, рассмотрим небольшой пример. Пользователь работает с MDIприложением и хочет выделить весь текущий документ (для реализации этого действия в объекте класса документа приложения предусмотрен специальный обработчик). Он выбирает пункт меню
Edit\Select All (Правка\Выделить все), с которым ассоциирована команда ID EDIT_SELECT ALL. Первым эту команду получает главное окно приложения, которое дает возможность обработать его текущему активному дочернему окну MDI. До того как дочернее окно просмотрит свою карту сообщений, оно дает возможность обработать команду своему
представлению. Представление проверяет свою карту сообщений и, поскольку не находит там обработчика, направляет команду в ассоциированный с ним документ. Теперь уже документ ищет обработчик в своей карте сообщений, находит его, и начинает обрабатывать команду. Если бы мы не реализовали обработчик в нашем объекте-документе, то команда была бы направлена в шаблон, и в случае неудачи поиск продолжается в объекте класса CMDIFrameWnd, а затем в объекте класса приложения. Наглядно описанная последовательность действий представлена на рис. 17. Для команды, совершающей этот или любой другой маршрут, вызывается метод OnCmdMsg следующего объекта.
Выборобъектаинтерфейса |
Элементменю |
|
Edit\Select All |
||
пользователя |
||
|
||
Формирование |
ID EDIT SELECT LL |
|
команды |
|
|
Поискполучателя |
Объект-документ |
|
командыON_COMMAND |
||
|
||
в карте сообщений |
|
|
Вызовобработчика |
OnEditSelectAIIO |
|
команды |
|
Выделениеданныхвсегодокумента
Рис. 17. Пример обработки действия пользователя
Стандартные сообщения Windows, в отличие от команд, не используют какой-либо маршрут для поиска обработчика, т. к. они посылаются непосредственно в оконную процедуру того объекта, который должен соответствующим образом отреагировать на них. Но и в этом случае каждый оконный класс, образованный непосредственно или косвенно от класса CWnd, имеет собственную карту сообщений, которая устанавливает связи между сообщениями и соответствующими обработчиками. Если соответствующий обработчик не найден в карте сообщений некоторого класса, то он ищется в карте сообщений базового для него класса. При этом более эффективным является использование механизма виртуальных функций, которые определены в классе CWnd для стандартных сообщений Windows.
Команды обновления и класс CCmdlll
Когда пользователь, например, раскрывает меню, каждый его элемент "должен знать" каким он будет отображаться — доступным, отмеченным или нет. Получатель команды предоставляет ему эту информацию, решшзуя обработчик сообщения ON_UPDATE_COMMAND_UI, внутри которого используется специальный класс CCmdUI, определяющий или элемент меню, или кнопку панели инструментов, или любой другой объект интерфейса пользователя, способный формировать команды. Этот обработчик вызывается до вывода элементов интерфейса пользователя на экран (или их
перерисовки), что |
позволяет задать их внешний вид. Маршрут, по которому ищется обработчик, |
|
совпадает с тем, который действует для "обычных" командных сообщений (рис. 18) |
||
Проверкасостоянияэлемента |
Циклфоновойобработки |
|
менюEdiftSeiect All |
|
|
|
|
|
интерфейса пользователя |
|
|
Т |
|
|
Формирование |
ID EDIT SELECT ALL |
|
команды |
|
|
Т
Поискполучателякоманды |
|
ON_UPDATЈ_COMMAND |
Объект-документ |
_UI в карте |
|
сообщений |
|
Т
Вызовобработчика |
OnUpdateEditSelectAIIO |
команды |
|
Блокирование/разблокированиепунктаменю
Рис. 18. Пример обновления элемента меню
Рассмотрим те возможности, которые заложены в классе CCmdUI для обновления элементов интерфейса пользователя. Начнем с членов класса:
UINT CCmdUI::m_nlD — идентификатор объекта интерфейса пользователя. DINT
CCmdlll::m_nlndex — индекс объекта интерфейса пользователя.
CMenu CCmdUI::m_pMenu —
указатель на меню, представляемое объектом CCmdUI. Если представляется не меню, то этот параметр равен NULL.
CMenu CCmdUI::m_pSubMenu -
указатель на имеющееся подменю, представляемое объектом CCmdUI. Если представляется не меню, то этот параметр равен NULL. Если подменю является раскрывающимся, то параметр m_nlD содержит идентификатор его первого элемента.
CWnd* CCmdUI::m_pOther -
указательнаобъектокна, которыйпослализвещение, например, панельэлементовуправленияилистрокасостояния. Если извещение пришло от меню или не от оконного объекта, то этот параметр равен NULL.