Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
3 семестр, WinAPI, MFC.pdf
Скачиваний:
370
Добавлен:
15.06.2014
Размер:
6.17 Mб
Скачать

17.Обязательно ли приложение, получившее сообщение WM_LBUTTONDOWN, получит и сообщений WM_LBUTTONUP? В каких случаях это будет обязательно, а в каких нет?

18.Что необходимо сделать, чтобы окно приложения получало сообщения о двойных щелчках мыши?

19.Когда приложение получает сообщения мыши, связанные с нерабочей областью окна?

20.В чем заключается процесс захвата мыши окном? Что при этом происходит?

21.Как приложение может установить таймер? Что происходит после его установки?

22.Каким способом можно заставить Windows посылать сообщения таймера обычной оконной процедуре приложения?

23.Что такое идентификатор таймера? Для чего при обработке сообщений от таймера следует проверять передаваемый вместе с сообщением идентификатор таймера?

Организация ввода

Разупорядоченный ввод

Используемый в 16-разрядной Windows упорядоченный ввод (serialized input),

означает, что система обрабатывает события от клавиатуры или мыши в том порядке, в каком они инициируются действиями пользователя. События извлекаются из системной

очереди по мере того, как их запрашивают приложения. Пусть на клавиатуре нажаты сочетания клавиш: ABC, Alt+Tab и XYZ. Тогда в системную очередь сообщений ставится 7 аппаратных событий. Приложение, в фокусе которого находится клавиатура, выбирает сообщения ABC из системной очереди и выводит в свое окно соответствующие символы. Теперь предположим, что в данной программе есть ошибка, из-за которой программа при каждом вводе буквы С попадает в бесконечный цикл. В этот момент зависает и вся система. Поэтому Alt+Tab и XYZ никогда не будут выбраны из системной очереди. А если пользователь попытается мышью активизировать другое приложение, то событие, связанное с мышью, будет добавлено в конец системной очереди — за последним событием от клавиатуры. Значит, и это событие никогда не будет выбрано из системной очереди. Остается одно — перезагрузить машину. Microsoft в свое время пыталась улучшить эту ситуацию. В 16разрядной Windows была введена поддержка комбинации клавиш Ctrl+Alt+Del, к которой пользователь мог прибегнуть, если приложение переставало реагировать на его команды. При нажатии этих клавиш система определяла, какое приложение в настоящий момент активно, и делала попытку убрать его из памяти.

Решение проблемы — разупорядоченный ввод (deserialized input). В этом случае

аппаратные события не обязательно обрабатываются в порядке поступления.

В Win32 ввод рассматривается на уровне потока, а не всей системы, как в 16-разряд- ной Windows. И поток принимает аппаратные события в том порядке, в каком пользователь их формирует. Вот так и разупорядочивается ввод.

Рассмотрим, как достигается разупорядочивание. При запуске система создает себе особый поток необработанного ввода (raw input thread, RIT). Когда на клавиатуре нажимают и отпускают клавишу или кнопку мыши или перемещают мышь, соответствующий драйвер устройства добавляет аппаратное событие к очереди RIT. В результате поток RIT

пробуждается, проверяет первое событие в очереди, транслирует его в соответствующее сообщение WM_KEY*, WM_?BUTTON* или WM_MOUSE-MOVE, а затем отправляет событие в виртуальную очередь ввода нужного потока.

У каждого потока есть своя очередь асинхронных сообщений и своя виртуальная очередь ввода. Когда поток создает окно, система помещает все предназначенные этому окну сообщения в очередь асинхронных сообщений потока, которому принадлежит окно.

При обработке аппаратного события поток RIT должен определить, в чью виртуальную очередь ввода его отправить. В случае сигнала от мыши RIT сначала определяет, поверх какого окна находится курсор мыши, а затем размещает событие (WM_?BUTTON* или WM_MOUSEMOVE) в виртуальной очереди ввода потока, создавшего данное окно. Обрабатывая сигнал от клавиатуры, RIT определяет, какой поток активен, т. е. с каким потоком пользователь работает в данный момент. Соответствующее клавиатурное сообщение помещается в виртуальную очередь ввода этого потока.

Чтобы система могла переключать потоки независимо от того, занят ли поток в данный момент обработкой ввода, RIT — прежде чем отправить аппаратное событие в виртуальную очередь ввода потока — проверяет его тип.

Разделение потоками виртуальных очередей ввода

Два и более потоков могут совместно использовать одну виртуальную очередь ввода и переменные локального состояния ввода (о них чуть позже) с помощью AttachThreadlnput

BOOL AttachThreadInput(DWORD idAttach, DWORD idAttachTo. BOOL fAttach);

Функция сообщает системе разрешить двум потокам использовать одну и ту же виртуальную очередь ввода (рис. 1). Первый параметр, idAttach, задает идентификатор потока, чья виртуальная очередь ввода Вам больше не нужна. Второй — idAttachedTo — это идентификатор потока, чью виртуальную очередь ввода должны разделять два потока. И, наконец, fAttach должен быть или TRUE, чтобы

инициировать совместное использование единой очереди, или FALSE — тогда каждый поток будет вновь использовать свою очередь. А чтобы одну виртуальную очередь ввода могли разделить более двух потоков, вызовите AttachThreadlnput соответствующее число раз.

Рис. 1

Потоки, присоединенные к одной виртуальной очереди ввода, сохраняют индивидуальные очереди сообщений. Чем больше потоков Вы подключаете к одной виртуальной очереди ввода, тем больше система становится похожей на 16-разрядную

Windows.

Однако снижается надежность Win32-системы, если заставите все потоки использовать одну очередь сообщений. Если какое-нибудь приложение зависнет при обработке нажатия клавиши, другие программы не получат ввода.

Система неявно соединяет виртуальные очереди ввода нескольких потоков, если приложение устанавливает ловушку-регистратор (journal record hook) или ловушку-

проигрыватель (journal playback hook). Когда ловушка снимается, система автоматически восстанавливает схему организации очереди ввода, существовавшую до установки ловушки.

Установкой ловушки-регистратора программа сообщает, что хочет получать уведомления о всех аппаратных событиях, вызываемых пользователем.

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

Локальное состояние ввода

Программы, исполняемые одновременно, должны делить между собой ограниченные системные ресурсы. Каждой приходится ограничивать поле своего вывода маленькой прямоугольной областью экрана, выделять память только при необходимости, передавать управление другим приложениям при нажатии некоторых клавиш, специально приостанавливать свое выполнение, чтобы и другие могли получить частицу процессорного времени.

Система устанавливает предельный размер памяти, которую приложение может «прибрать к рукам». Она следит за клавиатурой и дает пользователю возможность переключиться на другое приложение, хочет того активное приложение или нет. Она вытесняет текущее приложение и предоставляет время другой программе независимо от того, насколько текущее приложение стремится дорваться до процессора.

Такой стиль работы системы основан на концепции локального состояния ввода (local input state). Каждый поток обладает собственным состоянием ввода, сведения о котором хранятся в структуре THREADINFO. В информацию об этом состоянии включается данные о виртуальной очереди ввода потока и группа переменных. Последние содержат управляющую информацию о состоянии ввода:

Клавиатура:

какое окно находится в фокусе клавиатуры

какое окно активно;

какие нажатые клавиши хранятся в массиве синхронного состояния клавиш (synchronous key state array);

состояние курсора ввода.

Мышь:

каким окном она захвачена;

какова форма ее курсора;

видим ли этот курсор.

Ввод с клавиатуры и фокус

Сообщения, которые приложение получает от 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>. Они вызывают опции меню программы или

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