Московский инженерно-физический институт

Вид материалаПрактикум

Содержание


Создание и показ окна
HWND hwnd=CreateWindow(szClassName,//
Ws_thickframe | ws_sysmenu | ws_minimizebox
Ws_overlappedwindow & ~ws_maximizebox
Сообщения Windows и цикл их обработки
Рис. 1.2. Процедура создания и пересылки сообщения от мыши
Рис. 1.3. Прохождение программного сообщения WM_CREATE
Подобный материал:
1   2   3   4   5   6   7   8   9   ...   24

Создание и показ окна


Для создания окна, в частности главного окна приложения, используется функция Windows CreateWindow(), требующая при вызове указания 11 параметров, описание которых можно найти в интерактивном справочнике; впрочем, назначение большей части этих параметров очевидно:

HWND hwnd=CreateWindow(szClassName,//Класс окна

szTitle, WS_OVERLAPPEDWINDOW,//Заголовок, стиль окна

10,10,300,100, //Координаты, размеры

HWND_DESKTOP,NULL,//Родитель, меню

hInst,NULL);//Дескриптор приложения, параметры

Передача функции CreateWindow() через первый параметр имени класса окна позволяет использовать при создании окна те его общие характеристики, которые были определены в структуре WNDCLASS, например форму курсора и цвет фона. Вызывая функцию CreateWindow() многократно, можно создать много окон данного класса, различающихся, например, размерами и местоположением на экране. Однако пока мы создаем только главное окно приложения, которое, очевидно, должно быть в одном экземпляре.

Второй параметр определяет адрес строки с заголовком, появляющейся в верхней части окна.

Третий параметр определяет стиль окна. С помощью стиля задаются такие характеристики окна, как вид окружающей его рамки, наличие или отсутствие строки заголовка и целый ряд других. Стиль представляет собой комбинацию большого количества констант, каждая из которых отвечает за тот или иной элемент оформления или свойство окна. Так, константа WSTHICKFRAME придает окну толстую рамку, константа WSSYSMENU снабжает окно системным меню, константа WSVSCROLL отвечает за появление в правой части окна линейки вертикальной прокрутки и т. д. Операция побитового ИЛИ (знак | ) позволяет “набрать” требуемый комплект свойств.

Обычно главное окно описывается составной константой WSOVERLAPPEDWINDOW, в которую входят элементы стиля WSOVERLAPPED (перекрывающееся окно), WSCAPTION (строка заголовка), WSSYSMENU (системное меню), WSTHICKFRAME (толстая рамка), WSMINIMIZEBOX (кнопка минимизации, т. е. свертывания окна в пиктограмму) и WSMAXIMIZEBOX (кнопка максимизации, т. е. развертывания окна на весь экран). Если требуется создать главное окно с меньшим количеством элементов, весь необходимый набор можно указать явным образом. Так, для создания главного окна без кнопки максимизации константа стиля должна быть такой:

WS_THICKFRAME | WS_SYSMENU | WS_MINIMIZEBOX

(строка заголовка появляется в главном окне в любом случае, а константа WSOVERLAPPED равна нолю, и ее указание не влияет на стиль).

С другой стороны, воспользовавшись операторами побитовых преобразований И (&) и НЕ (~), можно не набирать заново всю комбинацию констант, а просто исключить ненужный элемент WSMAXIMIZEBOX из полного набора:

WS_OVERLAPPEDWINDOW & ~WS_MAXIMIZEBOX

Следующие четыре параметра определяют горизонтальную и вертикальную координаты левого верхнего угла окна относительно начала экрана, а также размеры окна по горизонтали и вертикали (в пикселах).

В качестве восьмого параметра указывается дескриптор родительского окна. Для главного окна, у которого нет родителя, используется константа HWNDDESKTOP, равная 0.

Следующий параметр позволяет задать меню окна. Если меню нет (как в нашем случае) или используется меню класса, заданное в структуре WNDCLASS, этот параметр должен быть равен NULL.

Десятый параметр идентифицирует экземпляр приложения. Значение дескриптора приложения было получено нами через аргумент hInst функции WinMain().

Последний параметр является адресом дополнительных данных, которые часто не требуются; соответственно мы указали “пустой” адрес (NULL).

Функция CreateWindow() возвращает (при успешном выполнении) дескриптор созданного окна. Этот дескриптор, поступающий в нашем примере в локальную переменную hwnd, передается затем в функцию ShowWindow(), которая организует вывод созданного окна на экран. В качестве второго параметра этой функции указывается режим показа окна. Использованная в примере константа SWSHOWNORMAL выводит окно с теми размерами, которые указаны в параметрах функции CreateWindow().

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

Сообщения Windows и цикл их обработки


Цикл обработки сообщений в простейшем виде состоит из одного предложения:

while(GetMessage(&msg,NULL,0,0))

DispatchMessage(&msg);

В этом бесконечном (если его не разорвать изнутри) цикле вызывается функция Windows GetMessage(), и, если она возвращает ненулевое значение, вызывается функция Dispatch­Mes­sage(). Назначение функции DispatchMessage() – вызов оконной функции приложения с передачей ей параметров, извлеченных из пришедшего в приложение сообщения (вспомним, что имя нашей оконной функции мы сообщили Windows в процессе регистрации класса окна).

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

Сообщения являются реакцией системы Windows на различные происходящие в системе события: движение мыши, нажатие клавиши, срабатывание таймера и т. д. Отличительным признаком сообщения является его код, который может принимать значения (для системных сообщений) от 1 до 0x3FF. Каждому коду соответствует своя символическая константа, имя которой достаточно ясно говорит об источнике сообщения. Так, при движении мыши возникают сообщения WMMOUSEMOVE (код 0x200), при нажатии на левую клавишу мыши – сообщение WMLBUTTONDOWN (код 0x201), при срабатывании таймера – WMTIMER (код 0x113).

Перечисленные события относятся к числу аппаратных; однако сообщения могут возникать и в результате программных действий системы или прикладной программы. Так, по ходу создания и вывода на экран главного окна Windows последовательно посылает в приложение целую группу сообщений, сигнализирующих об этапах этого процесса: WMGETMINMAXINFO – для уточнения размеров окна, WMERASEBKGND – при заполнении окна цветом фона, WMSIZE – при оценке размеров рабочей области окна, WMPAINT – для получения от программы информации о содержимом окна и многие другие. Некоторые из этих сообщений Windows обрабатывает сама; другие обязана обработать прикладная программа.

Может быть и обратная ситуация, когда сообщение создается в прикладной программе по воле программиста и посылается в Windows для того, чтобы система выполнила требуемые действия (например, заполнила конкретной информацией окно со списком или сообщила о состоянии некоторого элемента управления). Сообщения такого рода тоже стандартизованы и имеют определенные номера и соответствующие им символические обозначения.

Наконец, программист может использовать собственные сообщения и направлять их в различные окна приложения для оповещения о тех или иных ситуациях (и сам же их обрабатывать).

Рассмотрим процедуру пересылки и состав аппаратного сообщения на примере сообщения WMMOUSEMOVE о движении мыши (рис. 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 для каждого сообщения содержат специфические данные. Для сообщения WMMOUSEMOVE структурная переменная msg заполняется следующей информацией:
  • msg.hwnd – дескриптор окна под курсором мыши;
  • msg.message – код сообщения WMMOUSEMOVE=0x200;
  • msg.wParam – комбинация битовых флагов, индицирующих состояние клавиш мыши (нажаты/не нажаты), а также клавиш клавиатуры Ctrl и Shift;
  • msg.lParam – позиция курсора мыши относительно рабочей области окна на момент отправления сообщения;
  • msg.time – время отправления сообщения;
  • msg.pt – позиция курсора мыши относительно границ
    экрана.

Активизируя оконную функцию, функция Dispatch­Mes­sage() передает ей первые четыре из перечисленных выше шести параметров; если программе для организации правильной реакции на пришедшее сообщение требуются оставшиеся два параметра, их можно извлечь непосредственно из переменной msg.

Манипуляции с мышью могут порождать и другие сообщения. Так, нажатие левой клавиши мыши возбуждает сообщение WM­LBUT­TONDOWN (код 0x201), отпускание левой клавиши – сообщение WMLBUTTONUP (код 0x202), нажатие правой клавиши – сообщение WMRBUTTONDOWN (код 0x204). Сложнее обстоит дело с двойными щелчками клавиш. Двойной щелчок левой клавиши порождает целых четыре сообщения: WMLBUTTONDOWN, WM­LBUT­TON­UP, WMLBUTTONDBLCLK и снова WMLBUTTONUP. Программист может обрабатывать как все эти сообщения, так и только сообщения о двойном нажатии, не обращая внимания на остальные. Механизм образования всех этих сообщений в точности такой же, как и для сообщения WMMOUSEMOVE (аппаратное прерывание, формирование драйвером мыши пакета данных, установка сообщения в системную очередь, пересылка сообщения в очередь приложения, вызов оконной функции). Даже пакеты данных для этих сообщений не различаются.

Схожим образом формируются сообщения, например, от клавиатуры: WMKEYDOWN о нажатии любой “несистемной” клавиши (т. е. любой клавиши, не сопровождаемой нажатием клавиши Alt), WMKEYUP об отпускании несистемной клавиши, WMSYSKEYDOWN о нажатии “системной” клавиши (т. е. любой клавиши совместно с клавишей Alt) и др.

Рассмотренные сообщения относятся к сообщениям нижнего уровня – они оповещают об аппаратных событиях практически без всякой их обработки Windows. Некоторые аппаратные события предварительно обрабатываются Windows, и в приложение поступает уже результат этой обработки. Так, при щелчке левой клавишей мыши над строкой меню, помимо сообщения WMLBUT­TON­DOWN формируется сообщение WMCOMMAND, в число параметров которого входит идентификатор того пункта меню, над которым был курсор мыши в момент щелчка. Это избавляет нас от необходимости анализа положения курсора мыши и выделения всех положений курсора, входящих в прямоугольную область данного пункта меню.

Рассмотренный механизм прохождения сообщений справедлив, главным образом, для аппаратных сообщений. Большая часть программных сообщений, т. е. сообщений, прямо не связанных с аппаратными событиями, а возникающих по ходу протекания программных процессов в приложении или в самой Windows, обслуживаются системой иным образом. Рассмотрим, например, сообщение WMCREATE. Оно формируется системой в процессе создания окна, чтобы программист, перехватив это сообщение, мог выполнить необходимые инициализирующие действия: открыть файлы с данными, установить системные таймеры, создать дополнительные дочерние окна и т. д. Сообщение WMCREATE не поступает в очередь сообщений приложения. Вместо этого Windows непосредственно вызывает оконную функцию WndProc() с передачей ей необходимых параметров (рис. 1.3). С точки зрения программиста обычно не имеет особого значения, каким образом вызывается оконная функция – функцией DispatchMessage() или непосредственно программами Windows. Полезно, однако, иметь в виду, что при обработке, например, сообщения WMMOUSEMOVE все содержимое этого сообщения находится в структурной переменной msg, а при обработке WMCREATE мы имеем дело только с параметрами, переданными Windows в оконную функцию. В переменной msg в это время находится старое, уже обработанное сообщение, т. е. “мусор”.



Рис. 1.3. Прохождение программного сообщения WM_CREATE

Аналогично, т. е. помимо очереди сообщений приложения и структурной переменной msg, обрабатываются, например, сообщения WMINITDIALOG (инициализация диалога), WMSYSCOM­MAND (выбор пунктов системного меню), WMDESTROY (уничтожение окна) и многие другие.

Вернемся к обсуждению цикла обработки сообщений, действие которого схематически изображено на рис. 1.4.



Рис. 1.4. Действие цикла обработки сообщений

Как уже упоминалось, функция GetMessage() анализирует очередь сообщений приложения. Если в очереди обнаруживается сообщение, функция GetMessage() изымает его из очереди и передает в структуру msg, после чего завершается с возвратом значения TRUE. При отсутствии сообщений в очереди система переводит цикл обработки сообщений в спящее состояние. Приложение, находящееся в спящем состоянии, не потребляет процессорного времени и не тормозит работу системы. Усыпление данного приложения позволяет Windows эффективно обслуживать другие приложения. Появление в очереди какого-либо сообщения снова вызывает к жизни функцию GetMessage(), которая выполняет предназначенную ей работу – перенос сообщения из очереди сообщений в структурную переменную msg.

В любом случае функция GetMessage() завершится (с возвратом значения TRUE) лишь после того, как очередное сообщение попадет в переменную msg.

Особая ситуация возникает, если функция GetMessage() обнаруживает в очереди сообщение WMQUIT с кодом 0x12. В этом случае GetMessage() сразу же завершается с возвратом значения FALSE. Однако цикл while выполняется, лишь если GetMes­sage() возвращает TRUE. Возврат FALSE приводит к завершению цикла и переходу на предложение

return 0;

т. е. к завершению функции WinMain() и всего приложения. Таким образом, условием завершения приложения является появление сообщения WMQUIT. Как оно возникает, будет рассмотрено в следующем подразделе.