- •WINDOWS
- •Джеффри Рихтер
- •ЧАCTЬ I МАТЕРИАЛЫ ДЛЯ ОБЯЗАТЕЛЬНОГО ЧТЕНИЯ
- •ГЛАВА 1. Обработка ошибок
- •Вы тоже можете это сделать
- •Программа-пример ErrorShow
- •ГЛАВА 2 Unicode
- •Наборы символов
- •Одно- и двухбайтовые наборы символов
- •Unicode: набор широких символов
- •Почему Unicode?
- •Windows 2000 и Unicode
- •Windows 98 и Unicode
- •Windows CE и Unicode
- •В чью пользу счет?
- •Unicode и СОМ
- •Как писать программу с использованием Unicode
- •Unicode и библиотека С
- •Типы данных, определенные в Windows для Unicode
- •Unicode- и ANSI-функции в Windows
- •Строковые функции Windows
- •Ресурсы
- •Текстовые файлы
- •Перекодировка строк из Unicode в ANSI и обратно
- •ГЛАВА 3 Объекты ядра
- •Что такое объект ядра
- •Учет пользователей объектов ядра
- •Защита
- •Таблица описателей объектов ядра
- •Создание объекта ядра
- •Закрытие объекта ядра
- •Совместное использование объектов ядра несколькими процессами
- •Наследование описателя объекта
- •Изменение флагов описателя
- •Именованные объекты
- •Пространства имен Terminal Server
- •Дублирование описателей объектов
- •ЧАСТЬ II НАЧИНАЕМ РАБОТАТЬ
- •ГЛАВА 4 Процессы
- •Ваше первое Windows-приложение
- •Описатель экземпляра процесса
- •Описатель предыдущего экземпляра процесса
- •Командная строка процесса
- •Переменные окружения
- •Привязка к процессорам
- •Режим обработки ошибок
- •Текущие диск и каталог для процесса
- •Текущие каталоги для процесса
- •Определение версии системы
- •Функция CreateProcess
- •Параметры pszApplicationName и pszCommandLine
- •Параметры psaProcess, psaThread и blnheritHandles
- •Параметр fdwCreate
- •Параметр pvEnvironment
- •Параметр pszCurDir
- •Параметр psiStartlnfo
- •Параметр ppiProclnfo
- •Завершение процесса
- •Возврат управления входной функцией первичного потока
- •Функция ExitProcess
- •Функция TerminateProcess
- •Когда все потоки процесса уходят
- •Что происходит при завершении процесса
- •Дочерние процессы
- •Запуск обособленных дочерних процессов
- •Перечисление процессов, выполняемых в системе
- •Программа-пример Processlnfo
- •ГЛАВА 5 Задания
- •Определение ограничений, налагаемых на процессы в задании
- •Включение процесса в задание
- •Завершение всех процессов в задании
- •Получение статистической информации о задании
- •Уведомления заданий
- •Программа-пример JobLab
- •ГЛАВА 6 Базовые сведения о потоках
- •В каких случаях потоки создаются
- •И в каких случаях потоки не создаются
- •Ваша первая функция потока
- •Функция CreateThread
- •Параметр psa
- •Параметр cbStack
- •Параметры pfnStartAddr и pvParam
- •Параметр fdwCreate
- •Параметр pdwThreadlD
- •Завершение потока
- •Возврат управления функцией потока
- •Функция ExitThread
- •Функция TerminateThread
- •Если завершается процесс
- •Что происходит при завершении потока
- •Кое-что о внутреннем устройстве потока
- •Некоторые соображения по библиотеке С/С++
- •Ой, вместо _beginthreadex я по ошибке вызвал CreateThread
- •Библиотечные функции, которые лучше не вызывать
- •Как узнать о себе
- •Преобразование псевдоописателя в настоящий описатель
- •ГЛАВА 7 Планирование потоков, приоритет и привязка к процессорам
- •Приостановка и возобновление потоков
- •Приостановка и возобновление процессов
- •Функция Sleep
- •Переключение потоков
- •Определение периодов выполнения потока
- •Структура CONTEXT
- •Приоритеты потоков
- •Абстрагирование приоритетов
- •Программирование приоритетов
- •Динамическое изменение уровня приоритета потока
- •Подстройка планировщика для активного процесса
- •Программа-пример Scheduling Lab
- •Привязка потоков к процессорам
- •ГЛАВА 8 Синхронизация потоков в пользовательском режиме
- •Кэш-линии
- •Более сложные методы синхронизации потоков
- •Худшее, что можно сделать
- •Критические секции
- •Критические секции: важное дополнение
- •Критические секции и спин-блокировка
- •Критические секции и обработка ошибок
- •Несколько полезных приемов
- •Не занимайте критические секции надолго
- •ГЛАВА 9 Синхронизация потоков с использованием объектов ядра
- •Wait-функции
- •Побочные эффекты успешного ожидания
- •События
- •Программа-пример Handshake
- •Ожидаемые таймеры
- •Ожидаемые таймеры и АРС-очередь
- •И еще кое-что о таймерах
- •Семафоры
- •Мьютексы
- •Отказ от объекта-мьютекса
- •Мьютексы и критические секции
- •Программа-пример Queue
- •Сводная таблица объектов, используемых для синхронизации потоков
- •Другие функции, применяемые в синхронизации потоков
- •Асинхронный ввод-вывод на устройствах
- •Функция WaitForlnputldle
- •Функция MsgWaitForMultipleObjects(Ex)
- •Функция WaitForDebugEvent
- •Функция SignalObjectAndWait
- •ГЛАВА 10 Полезные средства для синхронизации потоков
- •Реализация критической секции: объект-оптекс
- •Программа-пример Optex
- •Создание инверсных семафоров и типов данных, безопасных в многопоточной среде
- •Программа-пример lnterlockedType
- •Синхронизация в сценарии "один писатель/группа читателей"
- •Программа-пример SWMRG
- •Реализация функции WaitForMultipleExpressions
- •Программа-пример WaitForMultExp
- •ГЛАВА 11 Пулы потоков
- •Сценарий 1: асинхронный вызов функций
- •Сценарий 2: вызов функций через определенные интервалы времени
- •Программа-пример TimedMsgBox
- •Сценарий 3: вызов функций при освобождении отдельных объектов ядра
- •Сценарий 4; вызов функций по завершении запросов на асинхронный ввод-вывод
- •ГЛАВА 12 Волокна
- •Работа с волокнами
- •Программа-пример Counter
- •ЧАСТЬ III УПРАВЛЕНИЕ ПАМЯТЬЮ
- •Виртуальное адресное пространство процесса
- •Как адресное пространство разбивается на разделы
- •Увеличение раздела для кода и данных пользовательского режима до 3 Гб на процессорах x86 (только Windows 2000)
- •Закрытый раздел размером 64 Кб (только Windows 2000)
- •Раздел для общих MMF (только Windows 98)
- •Регионы в адресном пространстве
- •Передача региону физической памяти
- •Физическая память и страничный файл
- •Физическая память в страничном файле не хранится
- •Атрибуты защиты
- •Защита типа «копирование при записи»
- •Специальные флаги атрибутов защиты
- •Подводя итоги
- •Блоки внутри регионов
- •Особенности адресного пространства в Windows 98
- •Выравнивание данных
- •ГЛАВА 14 Исследование виртуальной памяти
- •Системная информация
- •Программа-пример Syslnfo
- •Статус виртуальной памяти
- •Программа-пример VMStat
- •Определение состояния адресного пространства
- •Функция VMQuery
- •Программа-пример VMMap
- •ГЛАВА 15 Использование виртуальной памяти в приложениях
- •Резервирование региона в адресном пространстве
- •Передача памяти зарезервированному региону
- •Резервирование региона с одновременной передачей физической памяти
- •В какой момент региону передают физическую память
- •Возврат физической памяти и освобождение региона
- •В какой момент физическую память возвращают системе
- •Программа-пример VMAIloc
- •Изменение атрибутов защиты
- •Сброс содержимого физической памяти
- •Программа-пример MemReset
- •Механизм Address Windowing Extensions (только Windows 2000)
- •Программа-пример AWE
- •ГЛАВА 16 Стек потока
- •Стек потока в Windows 98
- •Функция из библиотеки С/С++ для контроля стека
- •Программа-пример Summation
- •ГЛАВА 17 Проецируемые в память файлы
- •Проецирование в память EXE- и DLL-файлов
- •Статические данные не разделяются несколькими экземплярами EXE или DLL
- •Программа-пример Applnst
- •Файлы данных, проецируемые в память
- •Метод 1: один файл, один буфер
- •Метод 2: два файла, один буфер
- •Метод 3: один файл, два буфера
- •Метод 4: один файл и никаких буферов
- •Использование проецируемых в память файлов
- •Этап1: создание или открытие объекта ядра «файл»
- •Этап 2: создание объекта ядра «проекция файла»
- •Этап 3: проецирование файловых данных на адресное пространство процесса
- •Этап 4: отключение файла данных от адресного пространства процесса
- •Этапы 5 и 6: закрытие объектов «проекция файла» и «файл»
- •Программа-пример FileRev
- •Обработка больших файлов
- •Проецируемые файлы и когерентность
- •Базовый адрес файла, проецируемого в память
- •Особенности проецирования файлов на разных платформах
- •Совместный доступ процессов к данным через механизм проецирования
- •Файлы, проецируемые на физическую память из страничного файла
- •Программа-пример MMFShare
- •Частичная передача физической памяти проецируемым файлам
- •Программа-пример MMFSparse
- •ГЛАВА 18 Динамически распределяемая память
- •Стандартная куча процесса
- •Дополнительные кучи в процессе
- •Защита компонентов
- •Более эффективное управление памятью
- •Локальный доступ
- •Исключение издержек, связанных с синхронизацией потоков
- •Быстрое освобождение всей памяти в куче
- •Создание дополнительной кучи
- •Выделение блока памяти из кучи
- •Изменение размера блока
- •Определение размера блока
- •Освобождение блока
- •Уничтожение кучи
- •Использование куч в программах на С++
- •Другие функции управления кучами
- •ЧАСТЬ IV ДИНАМИЧЕСКИ ПОДКЛЮЧАЕМЫЕ БИБЛИОТЕКИ
- •ГЛАВА 19 DLL: основы
- •DLL и адресное пространство процесса
- •Общая картина
- •Создание DLL-модуля
- •Что такое экспорт
- •Создание DLL для использования с другими средствами разработки (отличными от Visual C++)
- •Создание ЕХЕ-модуля
- •Что такое импорт
- •Выполнение ЕХЕ-модуля
- •ГЛАВА 20 DLL: более сложные методы программирования
- •Явная загрузка DLL и связывание идентификаторов
- •Явная загрузка DLL
- •Явная выгрузка DLL
- •Явное подключение экспортируемого идентификатора
- •Функция входа/выхода
- •Уведомление DLL_PROCESS_ATTACH
- •Уведомление DLL_PROCESS_DETACH
- •Уведомление DLL_THREAD_ATTACH
- •Уведомление DLL_THREAD_DETACH
- •Как система упорядочивает вызовы DIIMain
- •Функция DllMain и библиотека С/С++
- •Отложенная загрузка DLL
- •Программа-пример DelayLoadApp
- •Переадресация вызовов функций
- •Известные DLL
- •Перенаправление DLL
- •Модификация базовых адресов модулей
- •Связывание модулей
- •ГЛАВА 21 Локальная память потока
- •Динамическая локальная память потока
- •Использование динамической TLS
- •Статическая локальная память потока
- •Пример внедрения DLL
- •Внедрение DLL c использованием реестра
- •Внедрение DLL с помощью ловушек
- •Утилита для сохранения позиций элементов на рабочем столе
- •Внедрение DLL с помощью удаленных потоков
- •Программа-пример lnjLib
- •Библиотека lmgWalk.dll
- •Внедрение троянской DLL
- •Внедрение DLL как отладчика
- •Внедрение кода в среде Windows 98 через проецируемый в память файл
- •Внедрение кода через функцию CreateProcess
- •Перехват API-вызовов: пример
- •Перехват API-вызовов подменой кода
- •Перехват API-вызовов с использованием раздела импорта
- •Программа-пример LastMsgBoxlnfo
- •ЧАСТЬ V СТРУКТУРНАЯ ОБРАБОТКА ИСКЛЮЧЕНИЙ
- •ГЛАВА 23 Обработчики завершения
- •Примеры использования обработчиков завершения
- •Funcenstein1
- •Funcenstein2
- •Funcenstein3
- •Funcfurter1
- •Проверьте себя: FuncaDoodleDoo
- •Funcenstein4
- •Funcarama1
- •Funcarama2
- •Funcarama3
- •Funcarama4: последний рубеж
- •И еще о блоке finally
- •Funcfurter2
- •Программа-пример SEHTerm
- •ГЛАВА 24 Фильтры и обработчики исключений
- •Примеры использования фильтров и обработчиков исключений
- •Funcmeister1
- •Funcmeister2
- •EXCEPTION_EXECUTE_HANDLER
- •Некоторые полезные примеры
- •Глобальная раскрутка
- •Остановка глобальной раскрутки
- •EXCEPTION_CONTINUE_EXECUTION
- •Будьте осторожны с EXCEPTION_CONTINUE_EXECUTION
- •EXCEPTION_CONTINUE_SEARCH
- •Функция GetExceptionCode
- •Функция GetExceptionlnformation
- •Программные исключения
- •ГЛАВА 25 Необработанные исключения и исключения С++
- •Отладка по запросу
- •Отключение вывода сообщений об исключении
- •Принудительное завершение процесса
- •Создание оболочки вокруг функции потока
- •Создание оболочки вокруг всех функций потоков
- •Автоматический вызов отладчика
- •Явный вызов функции UnhandledExceptionFilter
- •Функция UnhandledExceptionFilter изнутри
- •Исключения и отладчик
- •Программа-пример Spreadsheet
- •Исключения С++ и структурные исключения
- •Перехват структурных исключений в С++
- •ЧАСТЬ VI ОПЕРАЦИИ С ОКНАМИ
- •ГЛАВА 26 Оконные сообщения
- •Очередь сообщений потока
- •Посылка асинхронных сообщений в очередь потока
- •Посылка синхронных сообщений окну
- •Пробуждение потока
- •Флаги состояния очереди
- •Алгоритм выборки сообщений из очереди потока
- •Пробуждение потока с использованием объектов ядра или флагов состояния очереди
- •Передача данных через сообщения
- •Программа-пример CopyData
- •ГЛАВА 27 Модель аппаратного ввода и локальное состояние ввода
- •Поток необработанного ввода
- •Локальное состояние ввода
- •Ввод с клавиатуры и фокус
- •Управление курсором мыши
- •Подключение к очередям виртуального ввода и переменным локального состояния ввода
- •Программа-пример LISLab
- •Программа-пример LISWatch
Для такого изменения переменные локального состояния ввода не нужны Но пе реведем курсор обратно в то окно, поток которого по-прежнсму занят обработкой. Системе "хочется" послать окну сообщения WM_SETCURSOR, но процедура этого окна не может выбрать их из очереди, так кяк его поток продолжает свою операцию. Тогда система определяет, какая форма была у курсора в прошлый раз (информация об этом содержится в переменных локального состояния ввода данного потока), и автомати чески восстанавливает ее (в нашем примере — "песочные часы"). Теперь пользовате лю четко видно, что в этом окне работа еще не закончена и придется подождать.
Подключение к очередям виртуального ввода и переменным локального состояния ввода
Как Вы уже убедились, отказоустойчивость модели ввода достигается благодаря тому, что у каждого потока имеются собственные переменные локального состояния вво да, а подключение потока к RIT и отключение от него происходит по мере необхо димости. Иногда нужно, чтобы два потока (или более) разделяли один набор пере менных локального состояния ввода или одну очередь виртуального ввода
Вы можете заставить два и более потока совместно использовать одну и ту же очередь виртуального ввода и переменные локального состояния ввода с помощью функции
AttachTbreadInput:
BOOL AttachThreadInput( DWORD idAttach, DWORD idAttachTo, BOOL fAttach);
Параметр idAttach задаст идентификатор потока, чьи переменные локального со стояния ввода и очередь виртуального ввода Вам больше не нужны, а паряметр idAtta chedTo — идентификатор потока, чьи переменные локального состояния ввода и вир туальная очередь ввода должны совместно использоваться потоками И, наконец, па раметр fAttach должен быть или TRUE, чтобы инициировать совместное использова ние одной очереди, или FALSE — тогда каждый поток будет вновь использовать свои переменные состояния ввода и очередь А чтобы одну очередь (и переменные состо яния ввода) разделяли более двух потоков, вызовите АttасЬТhrеаdInput соответствую щее число раз.
Вернемся к одному из предыдущих примеров и допустим, что поток А вызывает AttachThreadlnput, передавая в первом параметре свой идентификатор, во втором — идентификатор потока В и в последнем — TRUE:
AttachThreadInput(idThreadA, idThreadB, TRUE);
Теперь любое событие аппаратного ввода, адресованное окну A1, B1 или B2, будет добавлено в конец очереди виртуального ввода потока В. Аналогичная очередь пото ка А больше не получит новых событий, если только Вы не разъедините очереди, повторно вызвав AttachThreadInput с передачей FALSE в параметре fAttach
Рис. 27-3. Аппаратные сообщения для окон А1, В1 и B2 помещаются в очередь виртуального ввода потока В
Потоки, присоединенные к одной очереди виртуального ввода (VIQ), сохраняют индивидуальные очереди сообщений (синхронных, асинхронных и ответных), а так же флаги пробуждения. Однако Вы серьезно снизите надежность системы, если зас тавите все потоки использовать одну очередь сообщений. Если какой-нибудь поток зависнет при обработке нажатия клавиши, другие потоки не получат никакого ввода. Поэтому использования AttachThreadInput следует по возможности избегать.
Система неявно соединяет очереди виртуального ввода двух потоков, если какой то из них устанавливает ловушку регистрации (journal record hook) или ловушку вос произведения (journal playback hook). Когда ловушка снимается, система восстанав ливает схему организации очереди ввода, существовавшую до установки ловушки.
Установкой ловушки регистрации поток сообщает, что хочет получать уведомле ния о всех аппаратных событиях, вызываемых пользователем. Поток обычно сохра няет или регистрирует эту информацию в файле. Так как пользовательский ввод дол жен быть зарегистрирован в том порядке, в каком он происходил, все потоки в сис теме начинают разделять одну очередь виртуального ввода для синхронизации обра ботки ввода.
Есть еще один случай, когда система неявно вызывает AttachThreadlnput. Допустим, приложение создает два потока. Первый открывает на экране диалоговое окно Затем второй поток вызывает CreateWindow, указывая стиль WS_CHILD и передавая описа
тель этого диалогового окна, чтобы оно стало "родителем" дочернего окна. Тогда система сама вызывает АttасhТhrеаdIпрut, чтобы поток (которому принадлежит дочер нее окно)
использовал ту же очередь ввода, что и поток, создавший исходное диало говое окно Это приводит к синхронизации ввода во всех дочерних окнах исходного диалогового окна.
Программа-пример LISLab
Эта программа, «27 LISLab.exe» (см. листинг на рис 27-4), — своего рода лаборатория, в которой Вы сможете поэкспериментировать с локальным состоянием ввода. Файлы исходного кода и ресурсов этой программы находятся в каталоге 27-LISLab на ком пактдиске, прилагаемом к книге.
В качестве подопытных кроликов нам понадобятся два потока Один поток есть в нашей LISLab, a вторым будет Notepad Если на момент запуска LISLab программа Notepad не выполняется, LISLab сама запустит эту программу. После инициализации LISLab Вы увидите следующее диалоговое окно.
В левом верхнем углу окна — раздел Windows, его поля обновляются дважды в секунду, т. e дважды в секунду диалоговое окно получает сообщение WM_TIMER и в ответ вызывает функции GefFocus, GetActiveWtndow, GetForegroundWindow, GetCapture и GetClipCursor Первые четыре функции возвращают описатели окна (считываемые из переменных локального состояния ввода моего потока), через которые я могу определить класс и заголовок окна и вывести эту информацию на экран
Если я активизирую другое приложение (тот же Notepad), названия полей Focus и Activc
меняются на (No Window), ц поля Foreground — на [Notepad] Umitled - Notepad Обратите внимание, что активизация Notepad заставляет LISLab считать, что ни ак тивных, ни находящихся в фокусе окон нет
Теперь поэкспериментируем со сменой фокуса. Выберем SetFocus в списке Func tion — в правом верхнем углу диалогового окна. Затем в поле Delay введем время (в секундах), в течение которого LISLab будет ждать, прсжде чем вызвать SetFocus B дан ном случае, видимо, лучше установить пулевое время задержки Позже я объясню, как используется поле Delay.
Выберем окно (описатель которого мы хотим передать функции SetFocus) в спис ке Notepad Windows And Self, расположенном в левой части диалогового окна Для эксперимента укажем [Notepad] Untitled - Notepad. Теперь все готово к вызову SetFocus.
Щелкните кнопку Delay и понаблюдайте, что произойдет в разделе Windows Ничего. Система отказалась менять фокус.
Если Вы действительно хотите перевести фокус на Notepad, щелкните кнопку Attach To Notepad, что заставит LISLab вызвать:
AttachThreadinput(GetWindowThreadProcessId(g_hwnrtNotepad, NULL),
GetCurrentThreadId(), TRUE);
В результате этого вызова поток LISLab станет использовать ту же очередь вирту ального ввода, что и Notepad Кроме того, поток LISLab «разделит» переменные локаль ного состояния ввода Notepad
Если после щелчка кнопки Attach To Notepad Вы щелкнете окно Notepad, диало говое окно LISLab примет следующий вид.
Теперь, когда очереди ввода соединены друг с другом, LISLab способна отслежи вать изменения фокуса, происходящие в Notepad R приведенном выше диалоговом окне показано, что в данный момент фокус установлен на поле ввода А если мы от кроем в Notepad диалоговое окно File Opеn, то LISLab, продолжая следить за Notepad, покажет нам, какое окно Notepad получило фокус ввода, какое окно активно и т. д.
Теперь можно вернуться в LISLab, щелкнуть кнопку Delay и вновь попытаться зас тавить SetFocus перевести фокус на Notepad На этот раз всс пройдет успешно, пото му что очереди ввода соединены
Если хотите, поэкспериментируйте с SelActiweWindow, SetForegroundWtndow, Bring WindowToTop и SetWindowPos, выбирая нужную функцию в списке Function Попробуй те вызывать их и когда очереди соединены, и когда они разъединены; при этом обра щайте внимяние на различия в поведении функций
А сейчас я поясню, зачем предусмотрена задержка. Она заставляет LISLab вызывать указанную функцию только по истечении заданного числа секунд. Для иллюстрации возьмем такой пример. Но прежде отключите LISLab от Notcpad, щелкнув кнопку Detach From Notepad Затем в списке Notepad Windows And Self выберите —>This Dialog Box<—, a в списке Function — SerFocus и установите задержку на 10 секунд. На конец "нажмите" кнопку Delay и быстро щелкните окно Notepad, чтобы оно стало ак тивным Вы должны активизировать Notepad до того, как истекут заданные 10 секунд.
Пока идет отсчет времени задержки, справа от счетчика высвечивается слово Pending lIo истечении 10 секунд слово Pending меняется на Executed, и появляется
результат вызова функции Если Вы внимательно следите за работой программы, то увидите, что фокус теперь установлен на окно списка Function. Но ввод с клавиатуры попрежнему направляется в Notepad. Таким образом, и поток LISLab, и поток Note pad — оба считают, что в фокусе находится одно из их окон Но на самом деле RIT остается связанным с потоком Notepad.
И последнее замечание в этой связи: SetFocus и SetActiveWindow возвращают опи сатель окна, которое изначально находилось в фокусе или было активным Инфор мация об этом окне отображается в поле PrevWnd. Кроме того, непосредственно пе ред вызовом
SetForegroundWindow программа обращается к GetForegroundWindow, что бы получить описатель окна, которое располагалось "поверх" остальных окон. Эта информация также отображается в поле PrevWnd.
Далее поэкспериментируем с курсором мыши. Всякий раз, когда курсор проходит над диалоговым окном LISLab (но нс над каким-либо из его дочерних окон), он изоб ражается в виде вертикальной стрелки. По мере поступления диалоговому окну сооб щения от мыши добавляются в список Mouse Messages Received. Таким образом, Вы всегда в курсе того, когда диалоговое окно получает сообщения от мыши. Сдвинув курсор за пределы основного окна или поместив его на одно из дочерних окон, Вы увидите, что сообщения больше не вносятся в список.
Теперь переместите курсор в правую часть диалогового окна, установив его над текстом Click Right Mouse Button To Set Capture, а затем нажмите и удерживайте пра вую кнопку мыши. После этого LISLab вызовет функцию SctCapture, передав ей опи сатель своего диалогового окна. Заметьте: факт захвата мыши программой LISLab от разится в разделе
Windows
Не отпуская правую кнопку, проведите курсор над дочерними окнами LISLab и понаблюдайте за сообщениями от мыши, добавляемыми к списку. Если курсор выве ден за пределы диалогового окна LISLab, программа по-прсжнему получает сообще ния от мыши. Курсор сохраняет форму вертикальной стрелки независимо от того, над каким участком экрана Вы его перемещаете.
Теперь мы можем увидеть другой эффект. Отпустите правую кнопку и следите, что произойдет. Окно, в свое время захватившее мышь, показывает, что LISLab по-прежне му считает мышь захваченной. Но сдвиньте курсор за пределы диалогового окна LISLab, и он больше нс останется вертикальной стрелкой, а сообщения от мыши пере станут появляться в списке Mouse Messages Received. Установив же курсор на какое-либо из дочерних окон LISLab, Вы сразу увидите: захват по-прежнему действует, потому что все эти окна используют один набор переменных локального состояния ввода.
Закончив эксперименты, отключите режим захвата одним из двух способов:
двойным щелчком правой кнопки мыши в любом месте диалогового окна LISLab (чтобы программа вызвала функцию ReleaseCapture);
щелчком окна, созданного любым другим потоком (отличным от потока LISLab). В этом случае система автоматически передаст диалоговому окну LISLab сооб щения о нажатии и отпускании кнопки мыши.
Какой бы способ Вы ни выбрали, обратите внимание на поле Capture в разделе Win dows
— теперь оно отражает тот факт, что больше ни одно окно не захватывает мышь.
И еще два эксперимента, связанных с мышью в одном мы ограничим поле пере мещения курсора заданным прямоугольником, а в другом — изменим «видимость» курсора. Если Вы щелкнете кнопку «Top, Left», программа LISLab выполнит такой код:
RECT rc;
...
SetRect(&rc, 0, 0, GetSystemMetrics(SM_CXSCREEN) / 2,
GetSystemMetrics(SM_CYSCREEN) / 2);
Это ограничит поле перемещения курсора верхней левой четвертью экрана. Пе реключившись в окно другого приложения нажатием клавиш Alt+Tab Вы заметите, что ограничение по-прежнемудействует Но система автоматически снимет его в резуль тате одного из таких действий:
Windows 98 щелчка заголовка окна другого приложения и последующего пере мещения этого окна;
Windows 2000 щелчка заголовка окна другого приложения (последующего переме щения этого окна не требуется),
Windows 2000 активизации Task Manager нажатием клавиш Ctrl+Shifl+Еsc и после дующей его отмены.
Для снятия ограничения ня перемещение курсора можно также щелкнуть кнопку Remove в диалоговом окне LISLab (если эта кнопка находится в пределах текущего поля перемещения курсора).
Щелчок кнопки Hide или Show вызывает выполнение соответственно.
ShowCursor(FALSE);
или
ShowCursor(TRUE);
Когда курсор скрыт, его не видно при перемещении над диалоговым окном LISLab. Но как только курсор оказывается за пределами окна, он снова видим. Для нейтрали зации действия кнопки Hide используйте кнопку Show. Заметьте, что скрытие курсо ра носит кумулятивный характер пять раз щелкнув кнопку Hide, придется столько же раз щелкнуть кнопку Show, прежде чем курсор станет видимым.
И последний эксперимент — с кнопкой Infinite Loop При ее щелчке выполняется код:
SetCursor(LoadCursor(NULL, IDC_NO));
for (,,)
Первая строка меняет форму курсора на перечеркнутый круг, а вторая выполняет бесконечный цикл. После щелчка кнопки Infinite Loop программа перестает реагиро вать на какой-либо ввод. Перемещая курсор в пределах диалогового окна LISLab, Вы увидите, что курсор остается перечеркнутым кругом. Если же Вы сместите его в дру гое окно, он получит форму, заданную в текущем окне.
Если теперь вернуть курсор в диалоговое окно LlSLab, система обнаружит, что программа не отвечает, и автоматически восстановит прежнюю форму курсора — перечеркнутый круг. Так что вошедший в бесконечный цикл поток хоть и не вызывает положительных эмоций, но пользоваться другими окнами не мешает.
Заметьте: если Вы переместите на окно зависшей программы LISLab другое окно, а потом уберете его, система отправит LISLab сообщение WM_PAINT и обнаружит, что данный поток не отвечает. Из этой ситуации система выходит элементарно: перери совывает окно нереагирующего приложения. Конечно, перерисовать окно правиль но она нс в состоянии, так как ей не известно, что именно делало приложение, и поэтому она просто затирает окно цветом фона и перерисовывает рамку его окна.
Проблема теперь в том, что на экране есть ни на что не отвечающее окно. Как от него избавиться? Под управлением Windows 98 нужно сначала нажать клавиши Ctrl+Alt+Del, чтобы на экране появилось окно Close Program.
B Windows 2000 можно либо щелкнуть правой кнопкой мыши кнопку приложе ния на панели задач, либо открыть окно Task Manager
