Как в Win32 реализовано программирование на основе событий?
В приложении Win32 C++ мы запускаем цикл обработки сообщений, который извлекает сообщения из очереди, переводит их и затем отправляет. В конце концов, каждое сообщение достигает нашего WndProc, где может быть обработано соответствующее событие.
Я понимаю эту часть. Чего я не понимаю, так это в промежутках между событиями. В частности:
- Различные типы обработчиков прерываний ОС должны помещать сообщения в указанную "очередь сообщений", но где в адресном пространстве процесса находится эта очередь? Как это связано с кодом обработчика прерываний?
- Что значит "перевести" сообщение? Что означает звонок
TranslateMessage()
действительно? - После отправки
DispatchMessage()
, по каким местам проходит сообщение перед тем, как оно попадает в мой WndProc (то есть, что делает с ним ОС)?
Если кто-нибудь знает ответы на вышесказанное, любезно удовлетвори мое любопытство. Благодарю.
5 ответов
ОС поддерживает очередь сообщений, куда она помещает события (например, из прерываний или других источников). Затем он отправляет сообщения из этой очереди во все окна, в зависимости от сообщения (например, он не будет отправлять ключевые сообщения в окно, которое не имеет фокуса).
Приложения могут иметь собственную очередь для обработки сообщений. Эти очереди создаются по запросу (только при необходимости).
Перевод сообщения используется для создания сообщений, которые не являются "настоящими" событиями. Например, сообщение WM_CONTEXTMENU "переводится" либо щелчком правой кнопкой мыши, либо клавишей контекстного меню, либо shift-F10. WM_CHAR переводится из сообщений WM_KEYDOWN. И, конечно, многие другие сообщения "переводятся" таким образом.
Сообщение публикуется в каждом окне, которое должно его получить. ОС решает, в зависимости от типа сообщения, должно ли окно получать это сообщение или нет. Большинство сообщений ожидает система, т. Е. Сообщение не будет отправлено в другое окно, пока оно не будет обработано окном. Это оказывает большое влияние на широковещательные сообщения: если при обработке этого сообщения одно окно не возвращается, очередь блокируется, а другие окна больше не будут получать сообщение.
Это зависит от того, как ваше сообщение отправлено и как оно обрабатывается.
Когда вы вызываете SendMessage, если целевое окно принадлежит текущему потоку, вызов обходит очередь сообщений для окна, и оконный менеджер напрямую вызывает windowproc в целевом окне. Если целевое окно принадлежит другому потоку, диспетчер окон эффективно вызывает PostMessage и перекачивает оконные сообщения до тех пор, пока целевое окно не вернется из оконного процесса.
Когда вы вызываете PostMessage, оконный менеджер собирает параметры сообщения и вставляет соответствующий объект в очередь сообщений для целевого окна. При следующем вызове GetMessage сообщение удаляется из очереди сообщений.
Диспетчер окон также регистрирует необработанные события ввода от устройств ввода (клавиатура и / или мышь) и генерирует сообщения для этих событий ввода. Затем он вставляет эти сообщения в очередь соответствующим образом (обработка входных событий сложна, поскольку зависит от того, какие сообщения уже находятся в очереди сообщений для окна).
Как указал Стефан, TranslateMessage просто переводит клавиши ускорения - например, он преобразует последовательности клавиш в сообщения WM_COMMAND.
Различные типы обработчиков прерываний ОС должны помещать сообщения в указанную "очередь сообщений", но где в адресном пространстве процесса находится эта очередь? Как он подвергается воздействию кода обработчика прерываний?
Окна связаны с потоками. Каждый поток с окном имеет очередь потока в адресном пространстве процесса. Операционная система имеет внутреннюю очередь в собственном адресном пространстве для событий, генерируемых оборудованием. Используя подробную информацию о событии и другую информацию о состоянии (например, какое окно имеет фокус), ОС переводит аппаратные события в сообщения, которые затем помещаются в соответствующую очередь потоков.
Сообщения, которые публикуются, помещаются непосредственно в очередь потоков для целевого окна.
Сообщения, которые отправляются, обычно обрабатываются напрямую (в обход очереди).
Детали становятся волосатыми. Например, очереди потоков - это больше, чем списки сообщений - они также содержат некоторую информацию о состоянии. Некоторые сообщения (например, WM_PAINT) на самом деле не ставятся в очередь, а синтезируются из дополнительной информации о состоянии, когда вы запрашиваете очередь, и она пуста. Сообщения, отправленные окнам, принадлежащим другим потокам, на самом деле отправляются в очередь получателя, а не обрабатываются напрямую, но система делает его похожим на обычную блокирующую отправку с точки зрения вызывающей стороны. Веселость наступает, если это может вызвать тупик (из-за циклических пересылок обратно в исходный поток).
В книгах Джеффри Рихтера есть много (всех?) Кровавых деталей. Мое издание старое (Advanced Windows). Текущее издание, похоже, называется Windows через C / C++.
ОС выполняет МНОГО работы, чтобы поток сообщений выглядел рациональным (и относительно простым) для вызывающей стороны.
Что значит "перевести" сообщение? Что действительно делает вызов TranslateMessage()?
Он следит за сообщениями виртуальных клавиш и, когда он распознает комбинацию нажатие клавиши / нажатие клавиши, добавляет символьные сообщения. Если вы не вызовете TranslateMessage, вы не получите символьные сообщения, такие как WM_CHAR.
Я подозреваю, что он отправляет символьное сообщение непосредственно перед возвратом (в отличие от публикации их). Я никогда не проверял, но мне кажется, что я помню, что сообщения WM_CHAR поступают непосредственно перед WM_KEYUP.
После отправки DispatchMessage(), во всех местах, куда попадает сообщение, прежде чем оно достигнет моего WndProc (то есть, что операционная система делает с ним)?
DispatchMessage передает сообщение в WndProc для целевого окна. Попутно некоторые хуки могут получить возможность увидеть сообщение (и, возможно, помешать ему).
Чтобы решить последний подзапрос, отправленное сообщение отправится в WindowProc после того, как оно будет передано через все ловушки (WH_CALLWNDPROC)
Не совсем положительно об этом, но мое лучшее предположение говорит:
Очередь - это системный объект, к которому вы обращаетесь с помощью вызовов Win32 API. Это не в вашем адресном пространстве процесса вообще. Таким образом, обработчики прерываний могут получить к нему доступ (вероятно, через HAL (Уровень аппаратной абстракции) ядра).
В Win16 этот вызов взял различные части большого сообщения и объединил их в одно целое. Поэтому TranslateMessage добавит WM_KEYPRESS, когда найдет соответствующую последовательность WM_KEYDOWN WM_KEYUP. Это также превратило бы различные сообщения о нажатиях кнопок в сообщения с двойным щелчком на основе внутренних настроек и временных отметок сообщений. Делает ли это до сих пор в Win32, я не знаю.
DispatchMessage, вероятно, где обработчики сообщений окна обрабатываются. Поэтому, если в вашем окне есть зацепка, она вызывается здесь или когда вызывается GetMessage. Я не уверен. Помимо этого, DispatchMessage просто ищет адрес WndProc, связанный с окном, и вызывает его. Больше ничего не нужно делать.
Надеюсь, это поможет.