1 Обработка сообщений Windows в Delphi Приложения Windows состоят из множества объектов, которые взаимодействуют друг с другом, обмениваясь сообщениями (messages). Источниками этих сообщений могут быть: пользователь, оперирующий с клавиатурой и мышью, операционная система Windows, посылающая сообщения приложениям, другие приложения, обменивающиеся информацией с вашим приложением и, ваше приложение. Большинство сообщений, которые могут потребоваться, Delphi обрабатывает сама, так что достаточно использовать обработчики стандартных событий компонентов. Но иногда может потребоваться самому обрабатывать сообщения Windows. Такая необходимость возникает, если нужное сообщение Delphi не обрабатывается, или если вы желаете определить свое собственное сообщение. В Delphi определено около 100 стандартных типов сообщений. Сообщение Windows представляет собой структуру, содержащую несколько полей. Первое поле обычно называется Msg, оно является типом Cardinal (32 бита, беззнаковый). В этом поле содержится информация, идентифицирующая сообщение. Для удобного указания сообщения существует набор констант определяющих вид сообщения, например WM_KEYDOWN, WM_CLOSE и т.д. Далее следуют поля, содержащие передаваемые значения. Последние поля обычно используются для записи результата обработки сообщения. Например, основной тип сообщений, используемых в Delphi, определяется следующим образом: TMessage = packed record Msg: Cardinal; case Integer of 0: ( WParam: Longint; LParam: Longint; Result: Longint); 1: ( WParamLo: Word; WParamHi: Word; LParamLo: Word; LParamHi: Word; ResultLo: Word; ResultHi: Word); end; или для работы с сообщениями обработки нажатия клавиш используется следующая структура: TWMKey = packed record Msg: Cardinal; CharCode: Word; Unused: Word; KeyData: Longint; Result: Longint; end; В модуле Messages.pas в Delphi содержатся типы и константы, позволяющие работать с сообщениями. Обработка сообщений Для обработки сообщения необходимо в классе объявить специальный метод: procedure <имя метода>(vаr <переменная>: <тип сообщения>); message <номер сообщения или константа>; 2 Имя метода может быть любым, но принято перед именем указывать «WM». Если метод обработки сообщения переопределяет уже существующий в классе-родителе, то обычно в нем описывают дополнительные действия, а затем вызывается наследуемый метод без указания имени метода, например: procedure <имя класса>.<имя метода>; begin <дополнительные действия> inherited; {вызов наследуемого метода обработки сообщения} end; Задача. В обработчике события onMouseDown для класса TForm1 необходимо определять координаты нажатой клавиши и вид кнопки (левая, средняя, правая). procedure TForm1.FormMouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer); begin Label1.Caption:='X='+IntToStr(X); Label2.Caption:='Y='+IntToStr(Y); case Button of mbLeft : Label3.Caption:='левая кнопка'; mbMiddle: Label3.Caption:='средняя кнопка'; mbRight : Label3.Caption:='правая кнопка'; end; end; Задача. Необходимо создать класс TEdit1 порожденный от класса TEdit, в котором нужно объявить метод обработки сообщения WM_KEYDOWN. В созданном методе будем определять, относится ли введенный символ в поле Edit1 к цифре или к букве. type TEdit1=class(TEdit) procedure WMKEYDOWN(var Msg: TWMKeyDown); message WM_KEYDOWN; end; … procedure TEdit1.WMKEYDOWN; begin if ((Msg.CharCode>=48)AND(Msg.CharCode<=57)) then Form1.Label1.Caption:='число' else Form1.Label1.Caption:='символ'; end; //////////////////////////////////////////////////////// procedure TForm1.FormCreate(Sender: TObject); begin Edit1:=TEdit1.Create(self); Edit1.Parent:=self; Edit1.Left:=20; Edit1.Top:=100; end; //////////////////////////////////////////////////////// procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction); begin Edit1.Destroy; end; 3 Создание и обработка собственных сообщений Передача сообщений объектам Delphi может осуществляться с помощью функций SendMessage, PostMessage и процедуры Perform. 1. Для передачи сообщения элементу окна через очередь сообщений с ожиданием завершения его обработки используется функция: Function SendMessage(hWnd: HWND; Msg: Cardinal; wParam, lParam: LongInt): LongInt; Функция возвращает результат обработки сообщения. Параметр hWnd называется дескриптором окна, представляет собой значение целого типа, идентифицирующее окно в Windows (каждое окно при создании получает от ОС номер). Для каждого оконного элемента этот номер хранится в свойстве Handle, например Form1.Handle. Параметры wParam и lParam применяются для передачи информационной части сообщения. 2. Для передачи сообщения элементу окна через очередь сообщений без ожидания завершения его обработки используется функция: Function PostMessage(hWnd: HWND; Msg: Cardinal; wParam, lParam: LongInt): LongBool; Данная функция ставит сообщение в очередь и возвращает управление, не ожидая завершения его обработки. Функция возвращает True, если сообщение поставлено в очередь, и False – в противном случае. 3. Для передачи сообщения, минуя очередь, используется метод Perfom, который определен в классе TControl. От метода TControl порождены такие классы как TForm, TLabel, TEdit, TButton и т.д. Procedure Perform(Mes: Cardinal; wParam, lParam: LongInt); Данный метод передает сообщение элементу управления напрямую, поэтому дескриптор окна не указывается. Для создания собственных сообщений используются сообщения с номерами выше $3FF ($ - число в шестнадцатеричной СС). Первый свободный номер обозначен константой WM_USER=$400. Пример: создадим два приложения, в одном будем генерировать сообщение (SendMessage.dpr), а в другом принимать (GetMessage.dpr). В первом проекте мы будем в поле Edit1 вводить цифру и нажимать кнопку «Переслать», а в другом проекте принимать цифровое значение и помещать в поле Memo1. В общем подходе обработки сообщений, можно с помощью оператора case создать обработчик сообщений. /////////////// SendMessage.dpr//////////////////// const WM_MyMess = WM_USER; procedure TForm1.Button1Click(Sender: TObject); var h: HWND; begin h:=FindWindow('TForm1', 'Прием сообщений'); SendMessageW(h, WM_MyMess, 0, StrToInt(Edit1.Text)); end; /////////////// GetMessage.dpr//////////////////// const WM_MyMess=WM_USER; type TForm1 = class(TForm) Memo1: TMemo; private procedure WMMyMessage(var Msg: TMessage); message WM_MyMess; public end; 4 var Form1: TForm1; implementation procedure TForm1.WMMyMessage; begin Memo1.Lines.Add(IntToStr(Msg.LParam)); end; Запустив два приложения (файлы *.exe) мы можем наблюдать работу пересылки сообщения от одного приложения к другому.