![](/user_photo/1549_7W_y5.jpg)
- •История 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
![](/html/1549/349/html_zF3yk3EBC5.yf3l/htmlconvd-KH3qLD27x1.jpg)
17.Обязательно ли приложение, получившее сообщение WM_LBUTTONDOWN, получит и сообщений WM_LBUTTONUP? В каких случаях это будет обязательно, а в каких нет?
18.Что необходимо сделать, чтобы окно приложения получало сообщения о двойных щелчках мыши?
19.Когда приложение получает сообщения мыши, связанные с нерабочей областью окна?
20.В чем заключается процесс захвата мыши окном? Что при этом происходит?
21.Как приложение может установить таймер? Что происходит после его установки?
22.Каким способом можно заставить Windows посылать сообщения таймера обычной оконной процедуре приложения?
23.Что такое идентификатор таймера? Для чего при обработке сообщений от таймера следует проверять передаваемый вместе с сообщением идентификатор таймера?
Организация ввода
![](/html/1549/349/html_zF3yk3EBC5.yf3l/htmlconvd-KH3qLD27xi2.jpg)
![](/html/1549/349/html_zF3yk3EBC5.yf3l/htmlconvd-KH3qLD27xi3.jpg)
Разупорядоченный ввод
Используемый в 16-разрядной Windows упорядоченный ввод (serialized input),
означает, что система обрабатывает события от клавиатуры или мыши в том порядке, в каком они инициируются действиями пользователя. События извлекаются из системной
очереди по мере того, как их запрашивают приложения. Пусть на клавиатуре нажаты сочетания клавиш: ABC, Alt+Tab и XYZ. Тогда в системную очередь сообщений ставится 7 аппаратных событий. Приложение, в фокусе которого находится клавиатура, выбирает сообщения ABC из системной очереди и выводит в свое окно соответствующие символы. Теперь предположим, что в данной программе есть ошибка, из-за которой программа при каждом вводе буквы С попадает в бесконечный цикл. В этот момент зависает и вся система. Поэтому Alt+Tab и XYZ никогда не будут выбраны из системной очереди. А если пользователь попытается мышью активизировать другое приложение, то событие, связанное с мышью, будет добавлено в конец системной очереди — за последним событием от клавиатуры. Значит, и это событие никогда не будет выбрано из системной очереди. Остается одно — перезагрузить машину. Microsoft в свое время пыталась улучшить эту ситуацию. В 16разрядной Windows была введена поддержка комбинации клавиш Ctrl+Alt+Del, к которой пользователь мог прибегнуть, если приложение переставало реагировать на его команды. При нажатии этих клавиш система определяла, какое приложение в настоящий момент активно, и делала попытку убрать его из памяти.
Решение проблемы — разупорядоченный ввод (deserialized input). В этом случае
аппаратные события не обязательно обрабатываются в порядке поступления.
В Win32 ввод рассматривается на уровне потока, а не всей системы, как в 16-разряд- ной Windows. И поток принимает аппаратные события в том порядке, в каком пользователь их формирует. Вот так и разупорядочивается ввод.
![](/html/1549/349/html_zF3yk3EBC5.yf3l/htmlconvd-KH3qLD28x1.jpg)
Рассмотрим, как достигается разупорядочивание. При запуске система создает себе особый поток необработанного ввода (raw input thread, RIT). Когда на клавиатуре нажимают и отпускают клавишу или кнопку мыши или перемещают мышь, соответствующий драйвер устройства добавляет аппаратное событие к очереди RIT. В результате поток RIT
пробуждается, проверяет первое событие в очереди, транслирует его в соответствующее сообщение WM_KEY*, WM_?BUTTON* или WM_MOUSE-MOVE, а затем отправляет событие в виртуальную очередь ввода нужного потока.
•У каждого потока есть своя очередь асинхронных сообщений и своя виртуальная очередь ввода. Когда поток создает окно, система помещает все предназначенные этому окну сообщения в очередь асинхронных сообщений потока, которому принадлежит окно.
•При обработке аппаратного события поток RIT должен определить, в чью виртуальную очередь ввода его отправить. В случае сигнала от мыши RIT сначала определяет, поверх какого окна находится курсор мыши, а затем размещает событие (WM_?BUTTON* или WM_MOUSEMOVE) в виртуальной очереди ввода потока, создавшего данное окно. Обрабатывая сигнал от клавиатуры, RIT определяет, какой поток активен, т. е. с каким потоком пользователь работает в данный момент. Соответствующее клавиатурное сообщение помещается в виртуальную очередь ввода этого потока.
•Чтобы система могла переключать потоки независимо от того, занят ли поток в данный момент обработкой ввода, RIT — прежде чем отправить аппаратное событие в виртуальную очередь ввода потока — проверяет его тип.
![](/html/1549/349/html_zF3yk3EBC5.yf3l/htmlconvd-KH3qLD28xi3.jpg)
![](/html/1549/349/html_zF3yk3EBC5.yf3l/htmlconvd-KH3qLD28xi4.jpg)
Разделение потоками виртуальных очередей ввода
Два и более потоков могут совместно использовать одну виртуальную очередь ввода и переменные локального состояния ввода (о них чуть позже) с помощью AttachThreadlnput
BOOL AttachThreadInput(DWORD idAttach, DWORD idAttachTo. BOOL fAttach);
•Функция сообщает системе разрешить двум потокам использовать одну и ту же виртуальную очередь ввода (рис. 1). Первый параметр, idAttach, задает идентификатор потока, чья виртуальная очередь ввода Вам больше не нужна. Второй — idAttachedTo — это идентификатор потока, чью виртуальную очередь ввода должны разделять два потока. И, наконец, fAttach должен быть или TRUE, чтобы
инициировать совместное использование единой очереди, или FALSE — тогда каждый поток будет вновь использовать свою очередь. А чтобы одну виртуальную очередь ввода могли разделить более двух потоков, вызовите AttachThreadlnput соответствующее число раз.
![](/html/1549/349/html_zF3yk3EBC5.yf3l/htmlconvd-KH3qLD29x1.jpg)
Рис. 1
Потоки, присоединенные к одной виртуальной очереди ввода, сохраняют индивидуальные очереди сообщений. Чем больше потоков Вы подключаете к одной виртуальной очереди ввода, тем больше система становится похожей на 16-разрядную
Windows.
Однако снижается надежность Win32-системы, если заставите все потоки использовать одну очередь сообщений. Если какое-нибудь приложение зависнет при обработке нажатия клавиши, другие программы не получат ввода.
Система неявно соединяет виртуальные очереди ввода нескольких потоков, если приложение устанавливает ловушку-регистратор (journal record hook) или ловушку-
проигрыватель (journal playback hook). Когда ловушка снимается, система автоматически восстанавливает схему организации очереди ввода, существовавшую до установки ловушки.
•Установкой ловушки-регистратора программа сообщает, что хочет получать уведомления о всех аппаратных событиях, вызываемых пользователем.
Приложение обычно записывает или регистрирует эту информацию. А в следующем сеансе работы приложение устанавливает ловушку-проигрыватель, в результате чего система игнорирует пользовательский ввод и ожидает, когда установившее ловушку приложение начнет воспроизводить записанные ранее события.
![](/html/1549/349/html_zF3yk3EBC5.yf3l/htmlconvd-KH3qLD29xi5.jpg)
![](/html/1549/349/html_zF3yk3EBC5.yf3l/htmlconvd-KH3qLD29xi6.jpg)
Локальное состояние ввода
Программы, исполняемые одновременно, должны делить между собой ограниченные системные ресурсы. Каждой приходится ограничивать поле своего вывода маленькой прямоугольной областью экрана, выделять память только при необходимости, передавать управление другим приложениям при нажатии некоторых клавиш, специально приостанавливать свое выполнение, чтобы и другие могли получить частицу процессорного времени.
![](/html/1549/349/html_zF3yk3EBC5.yf3l/htmlconvd-KH3qLD30x1.jpg)
Система устанавливает предельный размер памяти, которую приложение может «прибрать к рукам». Она следит за клавиатурой и дает пользователю возможность переключиться на другое приложение, хочет того активное приложение или нет. Она вытесняет текущее приложение и предоставляет время другой программе независимо от того, насколько текущее приложение стремится дорваться до процессора.
Такой стиль работы системы основан на концепции локального состояния ввода (local input state). Каждый поток обладает собственным состоянием ввода, сведения о котором хранятся в структуре THREADINFO. В информацию об этом состоянии включается данные о виртуальной очереди ввода потока и группа переменных. Последние содержат управляющую информацию о состоянии ввода:
Клавиатура:
•какое окно находится в фокусе клавиатуры
•какое окно активно;
•какие нажатые клавиши хранятся в массиве синхронного состояния клавиш (synchronous key state array);
•состояние курсора ввода.
Мышь:
•каким окном она захвачена;
•какова форма ее курсора;
•видим ли этот курсор.
![](/html/1549/349/html_zF3yk3EBC5.yf3l/htmlconvd-KH3qLD30xi4.jpg)
![](/html/1549/349/html_zF3yk3EBC5.yf3l/htmlconvd-KH3qLD30xi5.jpg)
Ввод с клавиатуры и фокус
Сообщения, которые приложение получает от Windows о событиях, относящихся к клавиатуре, различаются на "аппаратные" (keystrokes) и "символьные" (characters).
Аппаратные сообщения
Когда вы нажимаете клавишу, Windows помещает либо сообщение WM_KEYDOWN, либо сообщение WM_SYSKEYDOWN в очередь сообщений окна, имеющего фокус ввода. Когда вы отпускаете клавишу, Windows помещает либо сообщение WM_KEYUP, либо сообщение WM_SYSKEYUP в очередь сообщений.
Обычно сообщения о "нажатии" (down) и "отпускании" (up) появляются парами. Однако, если вы оставите клавишу нажатой так, чтобы включился автоповтор, то Windows посылает оконной процедуре серию сообщений WM_KEYDOWN (или WM_SYSKEYDOWN) и одно сообщение WM_KEYUP (или WM_SYSKEYUP), когда в конце концов клавиша будет отпущена. Аппаратные сообщения клавиатуры также становятся в очередь. Вы можете с помощью функции GetMessageTime получить время нажатия и отпускания клавиши относительно старта системы.
•Префикс "SYS" в WM_SYSKEYDOWN и WM_SYSKEYUP означает "системное"
(system) и относится к аппаратным сообщениям клавиатуры, которые больше важны для Windows, чем для приложений Windows. Сообщения
WM_SYSKEYDOWN и WM_SYSKEYUP обычно вырабатываются при нажатии клавиш в сочетании с клавишей <Alt>. Они вызывают опции меню программы или
![](/html/1549/349/html_zF3yk3EBC5.yf3l/htmlconvd-KH3qLD31x1.jpg)
системного меню, или используются для системных функций, таких как смена активного окна (<Alt>+<Tab> или <Alt>+<Esc>), или как быстрые клавиши системного меню (<Alt> в сочетании с функциональной клавишей).
Символьные сообщения
(!!!) Существует четыре символьных сообщения:
|
Символы |
Немые символы |
Несистемные |
WM_CHAR |
WM_DEADCHAR |
символы: |
|
|
Системные |
WM_SYSCHAR |
WM_SYSDEADCH |
символы: |
|
AR |
|
|
|
Сообщения WM_CHAR и WM_DEADCHAR являются следствием сообщений
WM_KEYDOWN. Сообщения WM_SYSCHAR и WM_SYSDEADCHAR являются следствием сообщений WM_SYSKEYDOWN. В большинстве случаев ваши программы для Windows могут игнорировать все сообщения, за исключением WM_CHAR.
Символьные сообщения доставляются в вашу оконную процедуру в промежутке между аппаратными сообщениями клавиатуры. Например, если переключатель <CapsLock> не включен и вы нажимаете и отпускаете клавишу <А>, оконная процедура получит три следующих сообщения:
Сообщение |
Клавиша или код |
WM_KEYDOW |
Виртуальная клавиша А |
N |
|
WM_CHAR |
ASCII код а |
WM_KEYUP |
Виртуальная клавиша А |
|
|
В Win32 ввод с клавиатуры направляется потоком необработанного ввода (RIT) в виртуальную очередь ввода какого-либо потока, но только не в окно. RIT помещает клавиатурные события в виртуальную очередь потока безотносительно конкретному окну.
Когда поток вызывает GetMessage, клавиатурное событие извлекается из очереди и связывается с окном (созданным потоком), на котором в данный момент сосредоточен фокус ввода.
Чтобы направить клавиатурный ввод в другое окно, нужно указать, в очередь какого потока RIT должен помещать клавиатурные события, а также «сообщить» переменным состояния ввода потока, какое окно должно быть в фокусе. Эта задача не решается простым вызовом SetFocus.
Приложения могут иметь больше одного окна, и клавиатура должна разделяться между этими окнами в рамках одного и того же приложения. Когда на клавиатуре нажата клавиша, только одна оконная процедура может получить сообщение об этом. Окно, которое получает это сообщение клавиатуры, является окном, имеющим "фокус ввода". Окно, имеющее фокус ввода — это активное окно. Определить активное окно обычно достаточно просто. Обрабатывая сообщения WM_SETFOCUS и WM_KILLFOCUS, оконная
процедура может определить, когда окно имеет фокус ввода. WM_SETFOCUS показывает, что окно получило фокус ввода, а WM_KILLFOCUS, что окно потеряло его.
Когда фокус переводится с одного окна на другое обычным способом (например, щелчком окна), теряющее фокус окно получает сообщение WM_KILLFOCUS. Если окно, получающее фокус, принадлежит другому потоку, переменные локального состояния ввода потока, которому принадлежит окно, теряющее фокус, обновляются так, чтобы показать: окон в фокусе нет. И вызов GetFocus возвращает при этом NULL, заставляя поток «думать», что окон в фокусе нет.
Функция SetActiveWindow активизирует в системе окно верхнего уровня:
HWND SetActiveWindow(HWND hwnd);
•В Win32 функция SetActiveWindow ведет себя аналогично SetFocus. Иначе говоря, если поток вызывает ее с описателем окна, созданного другим потоком, система ничего не делает. Но если окно создано тем же потоком, система сменяет активное окно.
Функцию SetActiveWindow дополняет GetActiveWindow.
HWND GetActiveWindow(HWND hwnd);
•Она работает, как и GetFocus, но возвращает описатель активного окна, указанного в переменных локального состояния ввода вызывающего потока. Так что, если активное окно принадлежит другому потоку, функция возвращает NULL.
Приведенные далее функции не только переводят фокус между окнами, но и позволяют заставить RIT перенаправить ввод в другой поток. Одна из них —
SetForegroundWindow
BOOL SetForegroundWindow(HWND hwnd) ;
•Она активизирует окно, идентифицируемое параметром hwnd, и переводит на него фокус независимо от того, какой поток создал это окно.
Функцию SetForegroundWindow дополняет GetForegroundWindow:
BOOL GetForegroundWindow(HWND hwnd):
которая возвращает описатель активного в данный момент окна.
Рассмотрим функции, способные изменять Z-порядок окон, их статус и фокус:
BringWindowToTop и SetWindowPos.
BOOL BringWindowToTop(HWND hwnd);
•Если ее вызывает поток активного (foreground) процесса, Win32 активизирует указанное окно независимо от того, каким потоком оно было создано.