- •Лабораторный практикум «Основы разработки приложений Windows» Книга 1
- •Часть 1. Теоретические сведения4
- •Часть 2. Лабораторный практикум95
- •Часть 1 Теоретические сведения
- •1. Структура приложения Windows Простейшая программа с главным окном
- •Структура программы
- •Главная функция WinMain()
- •Регистрация класса окна
- •Создание и показ окна
- •Сообщения Windows и цикл их обработки
- •Оконная функция
- •Макрос handle_msg и структурирование программы
- •2. Интерфейс графических устройств
- •Простая программа, использующая средства gdi
- •Обработка сообщений wmpaint
- •Контекст устройства
- •Использование графических инструментов
- •3. Ресурсы: меню Простая программа с меню
- •Файл ресурсов
- •Описание меню в файле ресурсов
- •Сообщение wmcommand
- •Программное создание меню
- •Плавающее меню
- •Инструментальная панель
- •Всплывающие подсказки
- •0,"Первая строка"
- •1,"Вторая строка"
- •2,"Третья строка"
- •4. Ресурсы: диалоговые окна
- •Простая программа с меню и диалогом
- •Описание диалога в файле ресурсов
- •Обслуживание модального диалога
- •Модальный диалог как главное окно приложения
- •Немодальный диалог
- •Список в диалоговом окне
- •Окно редактирования и статический элемент управления
- •Графика в диалоговом окне
- •Перекрашивание диалогового окна и его элементов
- •Часть 2 Лабораторный практикум Работы лабораторного практикума Работа 1. Вызов функций Windows
- •Работа 2. Главное окно приложения (пример 1-1 из настоящего пособия)
- •Работа 3. Вывод в главное окно приложения текста и фигур (пример 2-1 из настоящего пособия)
- •Работа 4.Вывод в главное окно приложения текста и фигур (индивидуальное задание а)
- •Работа 5. Меню и модальный диалог (пример 4-1 из настоящего пособия)
- •Работа 6.Меню (индивидуальное задание в)
- •Работа 7.Модальный диалог в качестве главного окна приложения (индивидуальное задание с)
- •Работа 8.Вывод графика в главное окно приложения (индивидуальное заданиеD)
- •Работа 9. Немодальный диалог
- •Работа 10. Диалог с окном редактирования
- •Работа 11. Программное создание меню
- •Работа 12. Плавающее меню
- •Работа 13. График в диалоговом окне
- •Работа 14. Инструментальная панель (tool bar)
- •Работа 15. Инструментальная панель со всплывающими подсказками (tool tips)
- •Работа 16. Пользовательские пиктограммы и курсоры
- •Работа 17. Локализация программных продуктов
- •Индивидуальные задания лабораторного практикума
Сообщения Windows и цикл их обработки
Цикл обработки сообщений в простейшем виде состоит из одного предложения:
while(GetMessage(&msg,NULL,0,0))
DispatchMessage(&msg);
В этом бесконечном (если его не разорвать изнутри) цикле вызывается функция Windows GetMessage(), и, если она возвращает ненулевое значение, вызывается функцияDispatchMessage(). Назначение функцииDispatchMessage()– вызов оконной функции приложения с передачей ей параметров, извлеченных из пришедшего в приложение сообщения (вспомним, что имя нашей оконной функции мы сообщили Windows в процессе регистрации класса окна).
Для того, чтобы разобраться в деталях этого очень важного механизма, нам надо познакомиться поближе с самим понятием сообщений.
Сообщения являются реакцией системы Windows на различные происходящие в системе события: движение мыши, нажатие клавиши, срабатывание таймера и т. д. Отличительным признаком сообщения является его код, который может принимать значения (для системных сообщений) от 1 до 0x3FF. Каждому коду соответствует своя символическая константа, имя которой достаточно ясно говорит об источнике сообщения. Так, при движении мыши возникают сообщения WMMOUSEMOVE(код 0x200), при нажатии на левую клавишу мыши – сообщениеWMLBUTTONDOWN(код 0x201), при срабатывании таймера –WMTIMER(код 0x113).
Перечисленные события относятся к числу аппаратных; однако сообщения могут возникать и в результатепрограммныхдействий системы или прикладной программы. Так, по ходу создания и вывода на экран главного окна Windows последовательнопосылает в приложение целую группу сообщений, сигнализирующих об этапах этого процесса: WMGETMINMAXINFO – для уточнения размеров окна, WMERASEBKGND– при заполнении окна цветом фона,WMSIZE– при оценке размеров рабочей области окна,WMPAINT– для получения от программы информации о содержимом окна и многие другие. Некоторые из этих сообщений Windows обрабатывает сама; другие обязана обработать прикладная программа.
Может быть и обратная ситуация, когда сообщение создается в прикладной программе по воле программиста и посылается в Windows для того, чтобы система выполнила требуемые действия (например, заполнила конкретной информацией окно со списком или сообщила о состоянии некоторого элемента управления). Сообщения такого рода тоже стандартизованы и имеют определенные номера и соответствующие им символические обозначения.
Наконец, программист может использовать собственные сообщения и направлять их в различные окна приложения для оповещения о тех или иных ситуациях (и сам же их обрабатывать).
Рассмотрим процедуру пересылки и состав аппаратного сообщения на примере сообщения WMMOUSEMOVEо движении мыши (рис.1.2). Это сообщение возникает всякий раз, когда в результате движения мыши по столу зубец зубчатого колесика, связанного с катящимся по столу резиновым шариком, пересекает луч света от светодиода.
Пересечение луча света в механизме мыши возбуждает сигнал аппаратного прерывания, который, поступив в компьютер, активизирует драйвер мыши, входящий в состав Windows. Драйвер мыши формирует пакет данных и пересылает его в форме сообщения в системную очередь сообщений Windows. Далее Windows пересылает сообщение из своей очереди в очередь сообщений того приложения, которому оно адресовано.
Рис. 1.2. Процедура создания и пересылки сообщения от мыши
Рассмотрим, из чего состоит каждое сообщение. В начале главной функции приложения WinMain объявлена структурная переменная msg. Это важнейшая переменная, с помощью которой в программу передается содержимое сообщений Windows. Каждое сообщение представляет собой пакет из шести данных, описанных в файлеWINUSER.Hс помощью структуры типаMSG:
typedef struct tagMSG {
HWND hwnd;//Дескриптор окна, которому адресовано сообщение
UINT message;//Код данного сообщения
WPARAM wParam;//Первая группа параметров сообщения
LPARAM lParam;//Вторая группа параметров сообщения
DWORD time;//Время отправления сообщения
POINT pt;//Позиция курсора мыши
} MSG;//Новое имя для типа tagMSG
Часть этих параметров (hwnd, message, time иpt) имеет одинаковый смысл для всех сообщений; параметры жеwParam иlParam для каждого сообщения содержат специфические данные. Для сообщенияWMMOUSEMOVEструктурная переменнаяmsg заполняется следующей информацией:
msg.hwnd– дескриптор окна под курсором мыши;
msg.message– код сообщенияWMMOUSEMOVE=0x200;
msg.wParam– комбинация битовых флагов, индицирующих состояние клавиш мыши (нажаты/не нажаты), а также клавиш клавиатуры Ctrl и Shift;
msg.lParam– позиция курсора мыши относительно рабочей области окна на момент отправления сообщения;
msg.time– время отправления сообщения;
msg.pt– позиция курсора мыши относительно границ экрана.
Активизируя оконную функцию, функция DispatchMessage()передает ей первые четыре из перечисленных выше шести параметров; если программе для организации правильной реакции на пришедшее сообщение требуются оставшиеся два параметра, их можно извлечь непосредственно из переменнойmsg.
Манипуляции с мышью могут порождать и другие сообщения. Так, нажатие левой клавиши мыши возбуждает сообщение WMLBUTTONDOWN(код0x201), отпускание левой клавиши – сообщениеWMLBUTTONUP(код0x202), нажатие правой клавиши – сообщениеWMRBUTTONDOWN(код 0x204). Сложнее обстоит дело с двойными щелчками клавиш. Двойной щелчок левой клавиши порождает целых четыре сообщения:WMLBUTTONDOWN,WMLBUTTONUP,WMLBUTTONDBLCLKи сноваWMLBUTTONUP. Программист может обрабатывать как все эти сообщения, так и только сообщения о двойном нажатии, не обращая внимания на остальные. Механизм образования всех этих сообщений в точности такой же, как и для сообщенияWMMOUSEMOVE(аппаратное прерывание, формирование драйвером мыши пакета данных, установка сообщения в системную очередь, пересылка сообщения в очередь приложения, вызов оконной функции). Даже пакеты данных для этих сообщений не различаются.
Схожим образом формируются сообщения, например, от клавиатуры: WMKEYDOWN о нажатии любой “несистемной” клавиши (т. е. любой клавиши, не сопровождаемой нажатием клавишиAlt),WMKEYUP об отпускании несистемной клавиши,WMSYSKEYDOWN о нажатии “системной” клавиши (т. е. любой клавиши совместно с клавишейAlt)и др.
Рассмотренные сообщения относятся к сообщениям нижнего уровня – они оповещают об аппаратных событиях практически без всякой их обработки Windows. Некоторые аппаратные события предварительно обрабатываются Windows, и в приложение поступает уже результат этой обработки. Так, при щелчке левой клавишей мыши над строкой меню, помимо сообщения WMLBUTTONDOWNформируется сообщениеWMCOMMAND, в число параметров которого входит идентификатор того пункта меню, над которым был курсор мыши в момент щелчка. Это избавляет нас от необходимости анализа положения курсора мыши и выделения всех положений курсора, входящих в прямоугольную область данного пункта меню.
Рассмотренный механизм прохождения сообщений справедлив, главным образом, для аппаратных сообщений. Большая часть программных сообщений, т. е. сообщений, прямо не связанных с аппаратными событиями, а возникающих по ходу протекания программных процессов в приложении или в самой Windows, обслуживаются системой иным образом. Рассмотрим, например, сообщение WMCREATE. Оно формируется системой в процессе создания окна, чтобы программист, перехватив это сообщение, мог выполнить необходимые инициализирующие действия: открыть файлы с данными, установить системные таймеры, создать дополнительные дочерние окна и т. д. СообщениеWMCREATE не поступает в очередь сообщений приложения. Вместо этого Windows непосредственно вызывает оконную функциюWndProc()с передачей ей необходимых параметров (рис. 1.3). С точки зрения программиста обычно не имеет особого значения, каким образом вызывается оконная функция – функциейDispatchMessage() или непосредственно программами Windows. Полезно, однако, иметь в виду, что при обработке, например, сообщенияWMMOUSEMOVEвсе содержимое этого сообщения находится в структурной переменнойmsg, а при обработкеWMCREATEмы имеем дело только с параметрами, переданными Windows в оконную функцию. В переменнойmsgв это время находится старое, уже обработанное сообщение, т. е. “мусор”.
Рис. 1.3. Прохождение программного сообщения WM_CREATE
Аналогично, т. е. помимо очереди сообщений приложения и структурной переменной msg, обрабатываются, например, сообщенияWMINITDIALOG(инициализация диалога),WMSYSCOMMAND(выбор пунктов системного меню),WMDESTROY(уничтожение окна) и многие другие.
Вернемся к обсуждению цикла обработки сообщений, действие которого схематически изображено на рис. 1.4.
Рис. 1.4. Действие цикла обработки сообщений
Как уже упоминалось, функция GetMessage()анализирует очередь сообщений приложения. Если в очереди обнаруживается сообщение, функцияGetMessage()изымает его из очереди и передает в структуруmsg, после чего завершается с возвратом значенияTRUE. При отсутствии сообщений в очереди система переводит цикл обработки сообщений в спящее состояние. Приложение, находящееся в спящем состоянии, не потребляет процессорного времени и не тормозит работу системы. Усыпление данного приложения позволяет Windows эффективно обслуживать другие приложения. Появление в очереди какого-либо сообщения снова вызывает к жизни функциюGetMessage(), которая выполняет предназначенную ей работу – перенос сообщения из очереди сообщений в структурную переменнуюmsg.
В любом случае функция GetMessage()завершится (с возвратом значенияTRUE) лишь после того, как очередное сообщение попадет в переменнуюmsg.
Особая ситуация возникает, если функция GetMessage()обнаруживает в очереди сообщениеWMQUITс кодом 0x12. В этом случаеGetMessage()сразу же завершается с возвратом значенияFALSE. Однако циклwhileвыполняется, лишь еслиGetMessage()возвращаетTRUE. ВозвратFALSEприводит к завершению цикла и переходу на предложение
return 0;
т. е. к завершению функции WinMain()и всего приложения. Таким образом, условием завершения приложения является появление сообщенияWMQUIT. Как оно возникает, будет рассмотрено в следующем подразделе.