Использование механизма сообщений для обработки событий в ОС Windows
Продолжительность работы – 4 часа.
Цель работы:Знакомство с обработкой событий в ОС Windows и использование механизма сообщений для обработки событий.
Используемое оборудование: IBM – совместимый компьютер на базе процессора Pentium 166 и выше.
Используемое программное обеспечение: Операционная система Windows 95, 98 NT, и среда программирования Delphi 4 и выше.
Подготовка к работе
Изучить по конспекту лекций и рекомендованной литературе:
- организацию механизма сообщений в ОС Windows;
- виды сообщений, передаваемых ОС окнам приложения;
- что представляет собой сообщение и из каких частей оно состоит.
Программа работы
1. Определить обработчик стандартного сообщения Windows для формы из табл. 11 в соответствии с вариантом, заданным преподавателем.
табл. 11 Варианты индивидуальных заданий
№ вар. | Стандартное сообщение Windows | Действия, выполняемые обработчиком сообщения |
WM_LBUTTONDOWN | Вывести в окне диалога сообщение с координатами указателя мыши | |
WM_LBUTTONDBLCLK | Переместить окно в позицию, определяемую координатами указателя мыши | |
WM_RBUTTONUP | Изменить тип заголовка окна свойство (BorderStyle) по выбору студента | |
WM_MOUSEMOVE | Изменить случайным образом цвет окна | |
WM_RBUTTONDBLCLK | Закрыть окно | |
WM_KEYDOWN | Если нажата клавиша “С”, то изменить цвет окна случайным образом | |
WM_KEYUP | Вывести окно сообщения с указанием нажатой клавиши |
2. Реализовать обработку этого же сообщения на уровне приложения, причём сначала разрешить обработку этого сообщения всем элементам приложения, а затем изменить обработчик сообщения таким образом, чтобы данное сообщение обрабатывала только одна из форм приложения по указанию преподавателя.
3. Определить своё собственное сообщение в соответствии с вариантом из табл. 12, создать для него обработчик, который выполняет заданные вариантом действия при возникновении этого сообщения.
табл. 12
№ вар. | Событие, которое инициирует сообщение | Действия, выполняемые обработчиком сообщения |
При нажатии левой кнопки мыши на форме “Новое окно” | Изменить цвет элемента TLabel в окне “Обработка сообщений” | |
При закрытии окна “Новое окно” | Переместить окно “Обработка сообщений в новую позицию на экране” | |
При закрытии окна “Новое окно” | Сделать видимым компонент TLabel окна “Обработка сообщений” | |
При щелчке левой кнопкой мыши на кнопке “Закрыть” окна “Новое окно” | Изменить цвет формы “Обработка сообщений’ и вид заголовка окна | |
При нажатии клавиши на форме “Новое окно’ | Отразить соответствующий нажатой клавише символ в компоненте TLabel окна “Обработка сообщений” | |
При отжатии клавиши на форме “Новое окно’ | Отразить соответствующий нажатой клавише символ в названии окна “Обработка сообщений” | |
При двойном щелчке мышью на форме “Новое окно” | Спрятать окно “Обработка сообщний’ |
Методические указания
К п.1. Одной из особенностей традиционного программирования под Windows заключается обработке сообщений, посылаемых Windows приложениям. Операционная система обрабатывает все действия пользователя, выполняемые в клиентской области активного окна, а также за его пределами (нажатия клавиш мыши, перемещения указателя мыши, нажатие клавиш клавиатуры и т.д.) и передаёт информацию о происходящих событиях всем приложениям Windows.
Сообщение Windows представляет собой запись, которая состоит из четырех полей:
- первое поле Handle это дескриптор окна, получающего сообщение;
- второе поле Msg (16 бит) это идентификатор (номер) сообщения, который определяет какое из событий произошло;
- третье и четвёртое поля lParam и wParam (16 и 32 бита соответственно) содержат дополнительную информацию о сообщении.
Например, если была нажата левая кнопка мыши, то поле Msg содержит значение WM_LBUTTONDOWN, поле wParam определяет какая кнопка мыши была нажата, а поле lParam содержит координаты указателя мыши (координата X в младших 16-ти битах, координата Y в старших 16-ти битах).
При нажатии клавиши, поле Msg содержит значение WM_KEYDOWN, в поле wParam хранится ASCII код нажатой клавиши, а поле lParam содержит скан-код клавиши и другую информацию.
Использование системы обозначения параметров типа lParam и wParam не удобно, поскольку необходимо постоянно помнить каким образов следует извлекать из них нужную Вам информацию. Гораздо удобнее использовать типы сообщений, предоставляемые Delphi, которые описаны в модуле Message. Так например, для всех событий мыши объявлен тип TWMMouse, описание которого напоминает следующее:
Type TWMMouse = record
Msg: Cardinal; {это wParam}
Keys: Longint; {два способа определения wParam}
Case Integer of
0: XPos: SmalInt; {либо как координаты X и Y}
YPos: SmalInt;
1: Pos: TSmallPoint; {или как одна точка]
Result: LongInt; {поле результата}
end;
Для событий клавиатуры в модуле Message объявлен тип TWMKey
TWMKey = record
Msg: Cardinal; {идентификатор сообщения}
CharCode: Word; {ASCII код нажатой клавиши}
Unused: Word; {не используется}
KeyData: Longint; {дополнительная информация}
Result: Longint; {обработано ли нажатие}
end;
Общий синтаксис определения стандартного обработчика сообщений Windows имеет вид:
procedure Handle_Name (var Msg: TMessageType); message WM_XXX;
где HandlerName – имя метода; Msg – идентификатор передаваемого сообщения, TMessageType - тип записи, подходящий для данного сообщения, директива message указывает, что данный метод является обработчиком сообщения, WM_XXX - константа или выражение, которое определяет номер обрабатываемого сообщения Windows.
Рассмотрим обработку сообщений на следующем примере: необходимо, чтобы при нажатии правой кнопки мыши на форме появлялось диалоговое окно с сообщением.
Type
TForm1 = class (TForm)
…
private
procedure WMRButtuonDown (var Msg: TWMMouse);
message WM_RbuttonDown;
….
end; {TForm1}
Далее в разделе implementation модуля формы следует написать код обработчика данного сообщения.
procedure TForm1.WmrButtonDown(var Msg: TWMMouse);
begin
MessageDlg(‘Нажата правая кнопка мыши’,mtInformation,[mbOk],0);
end;
Откройте в Delphi проект, разработанный в предыдущей лабораторной работе, добавьте в основное меню приложения пункт ”Обработка сообщений”, в обработчике выбора этого пункта меню создайте новую форму проекта. Задайте название и геометрические размеры формы и определите обработчик для указанного вариантом сообщения. Запустите проект на выполнение и проверьте работоспособность обработчика сообщения, выполнив соответствующее действие.
К п.2. Delphi предоставляет также способ обработки всех сообщений, которые получает приложение. Для этого используется свойство OnMessage объекта Application, который автоматически создаётся при запуске программы. Если определён обработчик события OnMessage,то он получает управление при любом событии, направленном в программу.
Следующий пример приводит к появлению окна сообщения при двойном щелчке мыши на любом объекте в приложении. Обработчик события OnMessage лучше всего разместить в модуле главного окна приложения (MainF).
type
TForm1 = class (TForm)
…
private
procedure ProceedMessage (var Msg:TMsg; var Handled: Boolean);
…
published
procedure FormCreate (Sender : TObject);
…
end; {Form1}
…..
implementation
procedure TForm1.FormCreate (Sender: TObject);
begin
Application.OnMessage:=ProceedMessage;
end;
procedure TForm1.ProceedMessage (var Msg:TMsg; var Handled: Boolean);
begin
Handled:=False;
If Msg.Message = WM_LBUTTONDBLCLK then
begin
MessageDlg(‘Двойной щелчок мыши’,mtInformation,[mbOk],0);
Handled:=True;
end;
end;
В обработчике сообщения нельзя выполнять операции, требующие больших затрат времени, поскольку это приведет к замедлению выполнения всего приложения.
При создании обработчика обратите внимание, что в качестве типа – сообщения используется тип TMsg, описанный в модуле Windows:
tagMSG = packed record
hwnd: HWND;
message: UINT;
wParam: WPARAM;
lParam: LPARAM;
time: DWORD;
pt: TPoint;
end;
TMsg = tagMSG;
где поле hwnd определяет дескриптор окна, wParam и lParam несут информацию о подробностях события.
Так, если обрабатываются сообщения мыши, то информацию о положении указателя можно извлечь следующим образом:
with Msg do
begin
x:=lo(Msg.lParam); {извлечь координату X}
lParam:= lParam shr 16; {сдинуть длинное слово на 16 бит вправо}
{если объявить предварительно тип TCoord, то Y можно извлечь при помощи операции приведения типа]
//y:=lo(TCoord(lParam).ypos);
y:=lo(lParam); {извлечь координату Y}
MessageDlg('Координаты '+IntTostr(x)+’:’IntToStr(y)+' ',mtInformation,[mbOk],0);
end;
Тип TCoord представляет собой следующее объявление:
type tCoord = record
xpos:word;
ypos:word;
end;
Если требуется, чтобы только какое-то определенное окно приложения обрабатывало данное сообщение, следует организовать проверку на равенство дескриптора сообщения и дескриптора окна или элемента управления окна.
Например:
if Msg.hwnd = Form3.Handle then
begin
if Msg.Message = WM_KeyDown then
….
К п.3. Хотя Delphi предоставляет обработчики для большинства стандартных сообщений Windows, вам иногда может потребоваться создать своё собственное сообщение и определить для него соответствующий обработчик. Таким образом, чтобы обеспечить обработку собственных сообщений необходимо:
- определить ваше собственное сообщение;
- назначить новый обработчик этого сообщения.
Для определения своего собственного сообщения следует задать идентификатор сообщения, который представляет собой целочисленную константу. Windows резервирует сообщения с номерами меньшими чем 1024 для своего использования. Константа WM_APP, определенная в модуле Messages, представляет собой начальный номер для всех определяемых пользователем сообщений.
Пример определения сообщения пользователя:
WM_MYMESSAGE = WM_APP+400;
Если вы хотите дать информативные названия, вам следует объявить тип – запись для этого сообщения. Если вы не используете параметры сообщения или используете систему обозначения параметров (wParam, lParam и т.д.) вы можете использовать заданный по умолчанию тип сообщения TMessage.
Идентификатор сообщения и его тип (если его необходимо создать) лучше всего определить в отдельном модуле проекта, в котором описаны все константы, используемые в проекте. Чтобы добавить в состав проекта такой модуль, выберите в основном меню Delphi File½New… и в открывшемся окне выбрать Unit, после этого в Редакторе кода появится заготовка модуля. В разделе interface модуля следует выполнить необходимые объявления, а также подключить модуль Messages.
Затем в модуле формы, которая обрабатывает сообщения следует сначала подключить к нему модуль в котором было объявлено сообщение и его тип, а затем определить обработчик для этого сообщения. Чтобы определить обработчик сообщения необходимо выполнить следующее:
- объявить метод в части protected раздела объявления класса;
- метод должен быть процедурой с соответствующим названием без пробелов;
- передать обработчику один var параметр типа сообщения – записи;
- написать код, реализующий обработку сообщения в разделе implementation модуля;
Пример объявления обработчика сообщения для определяемого пользователем сообщения CM_CHANGECOLOR.
type
TMyForm = class (TForm)
…
protected
procedure CMChangeColor (var Msg : TMessage); message CM_CHANGECOLOR;
…..
end; {TMyForm}
…
implementation
…
procedure TMyForm.CMChangeColor(var Msg: TMessage);
begin
Color:=Msg.lParam;
Inherited;
end;
После определения обработчика сообщения следует в модуле формы, которая будет посылать сообщение в случае возникновения события, заданного вариантом, следует определить обработчик заданного события, который будет передавать сообщение форме “Обработка сообщений”, и подключить модуль в котором описан идентификатор сообщения. Для этого необходимо в Инспекторе объектов на стране Events выбрать необходимый вам обработчик события элемента, передающего сообщение о событии, щёлкнуть на нем левой кнопкой мыши и в появившейся заготовке обработчика события передать сообщение форме “Обработка сообщений”.
Передать сообщение другой форме или элементу управления формы можно используя функции Windows API SendMessage и PostMessage.
Функции SendMessage и PostMessage обеспечивают передачу сообщения в окно или окна, разница между ними заключается в том, что функция SendMessage вызывает оконную процедуру указанного окна и не возвращается, до тех пор пока сообщение не будет обработано, а функция PostMessage размещает сообщение в очередь сообщений, связанную с потоком указанного окна и возвращается без ожидания обработки сообщения.
Функции SendMessage и PostMessage имеют одинаковый синтаксис, приведённый ниже:
BOOL PostMessage(
HWND hWnd, // дескриптор окна, которому передаётся сообщение
UINT Msg, // передаваемое сообщение
WPARAM wParam, // первый параметр сообщения
LPARAM lParam // второй параметр сообщения
);
LRESULT SendMessage(
HWND hWnd, // дескриптор окна, которому передаётся сообщение
UINT Msg, // передавамое сообщение
WPARAM wParam, // первый параметр сообщения
LPARAM lParam // второй параметр сообщения
);
Пример: При щелчке левой кнопкой мыши на форме, форма передаёт сообщение другой форме, что ей необходимо изменить цвет и в lParam передаёт цвет, который следует установить.
procedure TForm2.FormClick(Sender: TObject);
var color:integer;
begin
color:=Random(10000);
PostMessage(Form3.Handle,WM_MYMESSAGE,0,color);
end;
Запустите программу на выполнение, в меню откройте сначала окно, которое будет обрабатывать сообщения, затем окно, которое передаёт сообщения, выполните действия, приводящие к передаче сообщения и убедитесь, в правильности обработки сообщения.
Контрольные вопросы
1. В каких случаях Windows передаёт сообщения окнам?
2. Что представляет собой сообщение и из каких частей состоит сообщение ?
3. Kaкая информация передаётся в параметрах сообщения ?
4. На каких уровнях можно обрабатывать сообщения в приложении ?
5. Как определить собственное сообщение ?
6. Как определить обработчик сообщения ?
7. В чем разница между функциями SendMessage и PostMessage ?
Лабораторная работа № 5