Часть 3. Продвинутое программирование с использование ObjectWindows
Глава 16. Сообщения Windows
Приложения Windows управляются событиями. То есть вместо не-
посредственной обработки оператор за оператором управление прог-
раммой определяется внешними событиями, такие как взаимодействия
с пользователем и системное оповещение. Приложения узнают о собы-
тиях, на которые они должны реагировать, получая от Windows сооб-
щения.
Данная глава охватывает ряд тем, связанных с передачей, по-
лучением и обработкой сообщений, включая следующие вопросы:
* что такое сообщение?
* как выполняется диспетчеризация сообщений?
* обработка сообщений Windows;
* определение ваших собственных сообщений;
* передача и адресация сообщений;
* диапазоны сообщений.
Что такое сообщение?
-----------------------------------------------------------------
Если вы не используете программирование, управляемое событи-
ями, Windows может выглядеть достаточно странной операционной
средой. Возможно, вам придется писать программы, которые основную
часть своего времени просто ждут ввода от пользователя (например,
в операторе Readln).
Программирование, управляемое событиями, обходит эту ситуа-
цию, возлагая обработку ввода от пользователя на центральную
подпрограмму, которую вам даже не нужно вызывать. В этом случае
Microsoft Windows сама взаимодействует с пользователем и опраши-
вает список взаимодействий для каждого работающего приложения.
Эти информационные пакеты называются сообщениями и представляют
собой просто структуры записей типа TMsg:
type
TMsg = record
hwnd: HWnd;
message: Word;
wParam: Word;
lParam: Longint;
time: Longint;
pt: TPoint;
end;
Поля записи сообщения дают приложению информацию о том, ка-
кой вид событий сгенерировал сообщение, где оно произошло и какое
окно должно на него реагировать.
Относительно сообщений следует иметь в виду, что ваше сооб-
щение в общем случае получает их после того, что произошло. Нап-
ример, если вы изменяете размер окна на экране, то объект окна
получает сообщение wm_Size, когда вы закончите изменение размера.
Некоторые сообщения запрашивают выполнение каких-то действий. Од-
нако в большинстве случаев это уведомления о том, что пользова-
тель или система выполнили некоторые действия, на которые следует
реагировать вашей программе.
Именующие сообщения
-----------------------------------------------------------------
Наиболее важным полем сообщений является поле message, кото-
рое содержит одну из констант сообщений Windows, начинающихся с
wm_. Каждое сообщение Windows уникальным образом идентифицируется
16-битовым числом с соответствующим мнемоническим идентификато-
ром. Например, сообщение, являющееся результатом нажатия клавиши,
содержит в поле сообщения wm_KeyDown ($0100). На сообщения обычно
ссылаются по их мнемоническим именам.
Откуда поступают сообщения
-----------------------------------------------------------------
Генерировать сообщения позволяют несколько различных событий:
* взаимодействия с пользователем, такие как нажатия клавиш,
щелчок кнопкой "мыши" или ее буксировка;
* вызовы функций Windows, которым нужно информировать об из-
менениях другие окна;
* ваша программа явно посылает сообщение;
* другое приложение посылает сообщение через DDE (динамичес-
кий обмен данными);
* сообщение генерируется самой Windows (например, сообщение
об останове системы).
Вашему приложению в общем случае не важно, как генерируются
сообщения. Основным в программировании, управляемом событиями,
является генерация сообщений и реакция на них. Поскольку Windows
и ObjectWindows берут на себя функции по доставке сообщений из
одного места в другое, вы можете сосредоточиться на генерации со-
общений с соответствующими параметрами и реакции на сообщения, а
не на механизме их доставки из одного места в другое.
Обычная диспетчеризация сообщений
-----------------------------------------------------------------
Обычные приложения Windows (то есть не использующие
ObjectWindows) имеют цикл сообщения, в котором выполняется выбор-
ка и диспетчеризация сообщения. По существу, в цикле сообщения
вызывается связанная с окном функция, заданная описателем окна в
поле hwnd записи сообщения.
Каждое окно имеет функцию окна, заданную при его создании.
Когда Windows находит сообщение для конкретного окна, она переда-
ет сообщение функции данного окна. Функция окна отсортировывает
сообщения на основе типа сообщения, а затем вызывает подпрограмму
реакции на конкретное сообщение.
Обычный способ обработки сообщений в приложении и его окнах
показывает программа GENERIC.PAS. Вы можете видеть, что оконная
функция каждого окна для сортировки сообщение содержит большой
оператор case. Все это может выглядеть не так плохо, пока вы не
осознаете тот факт, что в окне может потребоваться обрабатывать
более 100 различных сообщений Windows. После этого идея написания
и обслуживания такого оператора case будет выглядеть менее впе-
чатляющей.
Даже если у вас имеется несколько аналогичных окон, каждое
из них будет, очевидно, иметь свою оконную функцию с аналогичным
большим оператором case.
Способ, предлагаемый ObjectWindows
-----------------------------------------------------------------
ObjectWindows вносит в это обычный способ диспетчеризации
сообщений два основных улучшения. Первое состоит в том, что цикл
сообщений скрыт в ядре вашего объекта приложения. Все, что вам
нужно сделать - это привести свое приложение в действие, вызвав
метод Run объекта приложения. После этого оно будет получать со-
общения Windows.
Второе основное улучшение - это автоматическая диспетчериза-
ция сообщений. Вместо необходимости иметь для каждого окна окон-
ную функцию, вы просто определяете в оконных объектах методы, ко-
торые реагируют на конкретные сообщения. Эти методы называются
методами реакции на сообщения.
Динамические виртуальные методы
-----------------------------------------------------------------
Ключем к автоматической диспетчеризации сообщений является
расширение описаний в объектах виртуальных методов, называемых
динамическими виртуальными методами. По существу вы связываете с
методом целочисленный номер (такой как константа сообщения). Ваша
программа ObjectWindows (в данном случае цикл сообщения объекта
приложения) может затем вызвать этот метод на основе номера сооб-
щения.
Например, сообщение, генерируемое при нажатии в окне левой
кнопки "мыши", содержит в своем поле message wm_LButtonDown
($0201). Когда цикл сообщения ObjectWindows считывает для одного
из своих окон такое сообщение, то выполняется поиск в таблице
виртуальных методов данного оконного объекта и определяется дина-
мический метод, описанный для данного значения. Если такой метод
найден, то он вызывается, и ему в качестве параметра передается
распакованная запись сообщения типа TMessage. Если оконный объект
не описывает метод с данным индексом динамического метода, то
цикл сообщения вызывает используемую по умолчанию оконную проце-
дуру.
Написание методов реакции на сообщение
-----------------------------------------------------------------
Чтобы описать методы реакции на сообщение, нужно задать в
оконном объекте процедуру, названную по имени константы сообще-
ния. Например, чтобы ответить на сообщение wm_LButtonDown, вам
нужно описать методы следующим образом:
type
TMyWindow = object(TWindow)
.
.
.
procedure WMLButtonDown(var Msg: TMessage);
virtual wm_First + wm_LButtonDown;
.
.
.
end;
На самом деле метод может называться как угодно, но наимено-
вание методов по сообщениям, на которые они реагируют, делают
программу понятней. Единственным реальным ограничением является
то, что этот метод должен быть процедурой с единственным парамет-
ром типа TMessage.
Поскольку для методов реакции ObjectWindows использует нес-
колько диапазонов, а все индексы методы отсчитываются с 0, в ка-
честве смещения используется константа wm_First. Добавив для каж-
дого метода это смещение, вы создадите уникальный индекс динами-
ческого метода. Подробнее о диапазонах сообщений рассказывается в
разделе "Диапазоны сообщений" данной главы.
Что такое сообщение?
-----------------------------------------------------------------
Теперь, когда вы знаете, как написать метод реакции на сооб-
щение, можно рассмотреть, какая информация содержится в сообще-
нии. Запись TMessage, передаваемая методу реакции на сообщение,
выглядит следующим образом:
type
TMessage = record
Receiver: HWnd;
Message: Word;
case Integer of
0: (
WParam: Word;
LParam: Longint;
Result: Longint);
1: (
WParamLo: Byte;
WParamHi: Byte;
LParamLo: Word;
LParamHi: Word;
ResultLo: Word;
ResultHi: Word);
end;
Поля Receiver и Message для объектов ObjectWindows не осо-
бенно полезны, поскольку описатель Receiver обычно представляет
собой тоже самое, что и поле HWindow оконного объекта, а Message
уже отсортировано в цикле сообщения ObjectWindows.
Однако другие три поля очень важны. WParam и LParam - это
16- и 32-битовые параметры, передаваемые в сообщениях от Windows.
Result содержит код результата, который может потребоваться пере-
дать обратно. Заметим, что TMessage - вариантная запись, так что
вы можете обращаться к старшему и младшему байту слов параметров.
Поля параметров
-----------------------------------------------------------------
Поля параметров записи сообщения имеют для каждого сообщения
свой смысл. Однако, можно сделать некоторые обобщения.
WParam
Параметр WParam типа Word обычно содержит описатель, иден-
тификатор (например, идентификатор управляющего элемента) или бу-
левское значение. Например, параметр WParam сообщения
wm_SetCursor содержит описатель окна, в котором находится курсор.
Уведомляющие сообщения управляющего элемента, такие как
bn_Clicked, содержат в WParam идентификатор соответствующего уп-
равляющего элемента. wm_Enable использует WParam для булевского
значения, указывающего, разрешено или запрещено соответствующее
окно.
LParam
Параметр LParam типа Longint обычно содержит значение-указа-
тель двух переменных размером в слово, таких как координаты x и
y. Например, параметр LParam сообщения wm_SetText указывает на
строку с завершающим нулем, содержащую устанавливаемый текст. Со-
общения "мыши", такие как wm_LButtonDown, используют LParam для
записи координат события "мыши". Благодаря вариантным частям за-
писи сообщения, LParamLo содержит x-координату, а LParamHi -
y-координату.
Поле Result
-----------------------------------------------------------------
Поле Result сообщения TMessage управляет возвращаемым значе-
нием сообщения. Иногда программа, посылающая сообщение, ожидает
возврата конкретного значения, такого как булевское значение,
указывающее успешное или неуспешное выполнение или код ошибки. Вы
можете задать возвращаемое значение, присвоив значение полю
Result.
Например, когда пользователь пытается восстановить окно из
состояния пиктограммы, ему посылается сообщение wm_QueryOpen. По
умолчанию wm_QueryOpen возвращает булевское значение True (не
ноль). Если вы хотите иметь окно, которое всегда выводится в виде
пиктограммы, то вы можете ответить на сообщение wm_QueryOpen и
установить Result в 0. Это означает, что окно не может быть отк-
рыто:
procedure TIconWindow.WMQueryOpen(var Msg: TMessage);
begin
Msg.Result := 0;
end;
Объектно-ориентированная обработка сообщения
-----------------------------------------------------------------
Одним из огромных преимуществ обработки сообщения в
ObjectWindows (кроме того, что можно избавиться от больших опера-
торов case) является обработка сообщений объектно-ориентированным
способом. То есть, ваши оконные объекты наследуют возможность оп-
ределенной реакции на сообщения, и вам нужно только изменить ре-
акцию на конкретные сообщения, которые ваш объект обрабатывает
по-другому.
Отмена поведения по умолчанию
-----------------------------------------------------------------
Иногда, когда вы переопределяете используемую по умолчанию
реакцию на сообщение, это делается потому что данное поведение
просто нежелательно. Простейшим случаем является ситуация, когда
объект должен игнорировать сообщение, а не отвечать на него. Для
этого вы можете просто написать пустой метод реакции на сообще-
ние. Например, следующий метод сообщает управляющему элементу ре-
дактирования, что нужно игнорировать передаваемые в сообщении
wm_Char символы:
procedure TNonEdit.WMChar(var Msg: TMessage);
begin
end;
Замена поведения по умолчанию
-----------------------------------------------------------------
Более полезным подходом, чем простое игнорирование сообще-
ния, является замена поведения по умолчанию чем-то совершенно
другим. Например, следующий метод сообщает управляющему элементу
редактирования, что вместо вставки символа при нажатии любой кла-
виши нужно давать звуковой сигнал:
procedure TBeepEdit.WMChar(var Msg: TMessage);
begin
MessageBeep(0);
end;
Звуковой сигнал по нажатию клавиш сам по себе не особенно
полезен, но дает хорошую иллюстрацию. В общем случае вы можете
заменить используемое по умолчанию поведение некоторым другим.
Определяемая вами реакция будет единственной.
Дополнение поведения по умолчанию
-----------------------------------------------------------------
Иногда может возникнуть потребность в комбинировании некото-
рых действий с используемой по умолчанию реакцией на сообщение.
ObjectWindows предоставляет для этого объектно-ориентированный
способ. Обычно, когда вы хотите, чтобы объект выполнял некоторые
действия на основе используемого по умолчанию поведения, вы може-
те встроить в свой переопределенный метод наследуемый метод.
ObjectWindows позволяет вам делать это также для реакции на сооб-
щения.
Вызов наследуемых методов
-----------------------------------------------------------------
Предположим, например, что вы создали новый оконный объект и
хотите, чтобы он в дополнение к другим обычно выполняемым дейс-
твиям он давал звуковой сигнал при щелчке в окне левой кнопкой
"мыши". Все, что вам нужно сделать - это вызов в вашем новом ме-
тоде наследуемого метода TWindow.WMLButtonDown:
procedure TBeepWindow.WMLButtonDown(var Msg: TMessage);
begin
inherited WMLButtonDown(Msg);
MessageBeep(0);
end;
В данном случае неважно, размещаете ли вы вызов наследуемого
метода WMLButtonDown перед или после вызова MessageBeep. Вам нуж-
но решить, должен ли ваш метод вызывать наследуемые действия пе-
ред специальной обработкой или после нее (на основе того, нужны
ли вам параметры сообщения, или требуется их изменить).
Следует иметь в виду, что вы можете изменять параметры Msg
перед вызовом наследуемого метода. Однако делать это следует ак-
куратно, так как передача параметров вне диапазона может привес-
ти к тому, что ваша программа вызовет сбой Windows, особенно если
эти параметры содержат описатели или указатели. Если вы будете
аккуратны, изменение Msg может оказаться полезным, но нужно учи-
тывать, что это может быть также опасным.
Вызов процедур, используемых по умолчанию
-----------------------------------------------------------------
Как вы могли заметить, в данном разделе при вызове наследу-
емых методов используется не такой пример, как в предыдущих раз-
делах. Вы, возможно, ожидали, что новый управляющий элемент ре-
дактирования для обработки действия по умолчанию будет вызывать
свой наследуемый метод WMChar. Это имеет смысл, но не будет рабо-
тать, так как TEdit не имеет метода WMChar, поэтому производный
из TEdit объект не может вызывать WMChar в качестве наследуемого
метода.
К счастью, ваши объекты все равно могут наследовать реакцию
на сообщения. Когда ObjectWindows диспетчеризует сообщение объек-
ту, и этот объект не определяет конкретного метода реакции,
ObjectWindows передает запись TMessage методу с именем DefWndProc
- используемой по умолчанию оконной процедуре. DefWndProc знает,
как применять действия по умолчанию для всех сообщений. Таким об-
разом, если компилятор дает ошибку, когда вы пытаетесь наследо-
вать метод реакции на сообщения, поскольку для данного сообщения
нет наследуемого метода, вызовите вместо этого DefWndProc.
Например, чтобы в дополнение к вставке набираемых символов
добавить к управляющему элементу редактирования звуковой сигнал,
вам нужно вызвать MessageBeep и DefWndProc:
procedure TBeepEdit.WMChar(var Msg: TMessage);
begin
MessageBeep(0);
DefWndProc(Msg);
end;
Вызов DefWndProc вы можете рассматривать как используемый
вместо всех не определенных конкретно наследуемых методов реакции
на сообщения. Отметим, однако, что это применяется только к сооб-
щениям Windows. Командные сообщения, уведомляющие сообщения и уп-
равляющие сообщения имеют свои собственные используемые по умол-
чанию обработчики сообщений. Эти сообщения и их используемые по
умолчанию процедуры описываются в следующем разделе.
Командные, уведомляющие и управляющие идентификаторы
-----------------------------------------------------------------
Сообщение - это просто запись данных, идентифицируемая конк-
ретным значением в поле message, а ObjectWindows экономит вам
массу времени и усилий путем сортировки этих сообщений и диспет-
черизации их методам реакции на сообщения в ваших объектах. Одна-
ко, сообщение Windows wm_Command имеет много вариантов, которые
нужно отсортировывать с помощью большого оператора case.
ObjectWindows также выполняет это для вас.
Например, сообщение wm_Command посылается при выборе элемен-
та меню или нажатии оперативной клавиши. В обычном приложении
сообщение Windows передавалось бы вашей функции окна, где в боль-
шом операторе case нужно было бы отсортировать различные сообще-
ния, вызывая другие подпрограммы при обнаружении wm_Command. Эта
подпрограмма должна в свою очередь определить, какая была переда-
на команда (с помощью другого большого оператора case).
Командные сообщения
-----------------------------------------------------------------
ObjectWindows обрабатывает команды меню и оперативных клавиш
путем отдельной диспетчеризации командных сообщений в основном
аналогично другим сообщениям Windows. Реально обработка выполня-
ется внутри метода WMCommand ваших оконных объектов, наследуемого
из TWindowsObject. Но вместо обработки самих команд WMCommand вы-
полняет диспетчеризацию командных сообщений на основе генерируе-
мого командой идентификатора меню или оперативной клавиши.
Например, если вы определяете элемент меню с идентификатором
cm_DoSomething, в ваших объектах следует на основе этого иденти-
фикатора определить методы реакции:
type
TSomeWindow = object(TWindow)
.
.
.
procedure CMDoSomething(var Msg: TMessage);
virtual cm_First + cm_DoSomething;
end;
procedure TSomeWindow.CMDoSomething(var Msg: TMessage);
begin
{ реакция на команду }
end;
Аналогично wm_First, cm_First - это константа ObjectWindows,
определяющая начало диапазона сообщений. Ваши командные константы
должны лежать в диапазоне 024319.
Обработка команд по умолчанию
Чтобы вызвать используемую по умолчанию реакцию на команду,
для нее обычно вызывается наследуемый метод реакции. Если в объ-
екте-предке не определяется метод реакции на конкретную команду,
по умолчанию обработка выполняется с помощью DefCommandProc.
DefCommandProc работает во многом аналогично методу DefWndProc
для сообщений Windows, но обрабатывает команды.
Уведомляющие сообщения
-----------------------------------------------------------------
Команды не обязательно должны поступать от меню или команд-
ных клавиш. Управляющие элементы в окнах посылают сообщения своим
порождающим окнам, когда вы щелкаете на них "мышью" или что-либо
набираете. Эти сообщения называются уведомляющими сообщениями, и
ObjectWindows обрабатывает их двумя различными путями.
В основном уведомления могут передаваться объекту управляю-
щего элемента или его порождающему окну. Если управляющий элемент
имеет связанный с ним объект ObjectWindows, ObjectWindows дает
объекту возможность сначала ответить на команду. Это называется
уведомлением управляющего элемента. Если управляющий элемент не
имеет соответствующего объекта, или объект управляющего элемента
не определяет реакцию на команду, то ответить имеет возможность
порождающее окно. Это называется уведомлением порождающего объек-
та.
Уведомления управляющих элементов
-----------------------------------------------------------------
Обычно управляющим элементам не требуется в ответ на дейс-
твия пользователя делать ничего особенного; ожидаемым поведением
является поведение, используемое по умолчанию. Но если вы хотите,
чтобы управляющий элемент делал что-то дополнительно или что-то
другое, то уведомляющие сообщения позволяют вам осуществить это.
Предположим, например, что вы хотите, чтобы при каждом щелч-
ке кнопкой "мыши" раздавался звуковой сигнал. Вы можете просто
задать для объекта кнопки метод реакции на уведомление:
type
TBeepButton = object(TButton)
procedure BNClicked(var Msg: TMessage);
virtual nf_First + bn_Clicked;
end;
procedure TBeepButton.BNClicked(var Msg: TMessage);
begin
MessageBeep(0);
end;
ObjectWindows определяет в качестве смещения, задающего диа-
пазон используемых в уведомлениях управляющих элементов сообще-
ний, константу nf_First.
Уведомление порождающего объекта
-----------------------------------------------------------------
Если управляющий элемент не имеет связанного с ним интер-
фейсного объекта, или управляющий объект не определяет реакции на
конкретную команду, уведомляющие сообщения посылаются вместо объ-
екта управляющего элемента порождающему окну управляющего элемен-
та. Так как порождающее окно должно знать, какой из его управляю-
щих элементов вызвал уведомление, уведомляющее сообщение порожда-
ющему окну основывается на идентификаторе управляющего элемента.
Например, чтобы ответить на уведомление о взаимодействиях с
управляющим элементом с идентификатором id_MyControl, вы можете
определить метод следующим образом:
type
TMyWindow = object(TWindow)
.
.
.
procedure IDMyControl(var Msg: TMessage);
virtual id_First + id_MyControl;
end;
procedure TMyWindow.IDMyControl(var Msg: TMessage);
begin
{ реакция на сообщение }
end;
В качестве смещения в диапазоне сообщений, используемых для
реакции на уведомления управляющих элементов, ObjectWindows опре-
деляет константу id_First.
Windows редко определяет реакцию на конкретные управляющие
элементы, заданную по умолчанию. Однако, если вы хотите позабо-
титься о заданном по умолчанию поведении, то можете вызвать
DefChildProc. DefChildProc работает аналогично DefWndProc, но об-
рабатывает вместо сообщений Windows уведомляющее сообщение по-
рождающему объекту.
Уведомления управляющих
элементов и порождающих объектов
-----------------------------------------------------------------
Возможно, иногда вам потребуется, чтобы одновременно на не-
которое взаимодействие пользователя с управляющим элементом реа-
гировали и управляющий элемент, и порождающее окно. ObjectWindows
также обеспечивает способ реализовать это. Поскольку уведомление
порождающего объекта предусматривает реакцию по умолчанию, когда
объект управляющего элемента не определяет реакции на уведомле-
ние, все, что нужно сделать - это вызов реакции по умолчанию в
дополнение реакции управляющего элемента.
Например, с учетом приведенного выше объекта TBeepButton вы
можете также уведомлять порождающее окно с помощью добавления вы-
зова DefNotificationProc:
procedure TBeepButton.BNClicked(var Msg: TMessage);
begin
MessageBeep(0);
DefNotificationProc(Msg);
end;
Вызов DefNotificationProc обеспечивает получение уведомляю-
щего сообщения на основе идентификатора порождающего окна управ-
ляющего элемента, как если бы управляющий элемент вовсе не опре-
делял реакции.
Определение ваших собственных сообщений
-----------------------------------------------------------------
Windows резервирует для собственного использования 1024 со-
общения. В этот диапазон попадают все стандартные сообщения. На-
чало диапазона сообщений определяется константой wm_User. Чтобы
определить сообщение, используемое окнами вашей программы, опре-
делите идентификатор сообщения, попадающий в диапазон
wm_Userwm_User+31744.
Чтобы избежать конфликта со стандартными сообщениями, вам
следует определить идентификаторы своих сообщений как константы
на основе wn_User. Например, в приложении, где требуется три оп-
ределенных пользователем сообщения, вы можете описать их следую-
щим образом:
const
wm_MyFirstMessage = wm_User;
wm_MySecondMessage = wm_User + 1;
wm_MyThirdMessage = wm_User + 2;
Реакция на ваши сообщения аналогично реакции на любое другое
сообщение:
TCustomWindow = object(TWindow)
.
.
.
procedure WMMyFirstMessage(var Msg: TMessage);
virtual wm_First + wm_MyFirstMessage
Согласно общему правилу, определенные пользователем сообще-
ния следует использовать для внутреннего обмена сообщения. Если
вы посылаете определенное пользователем сообщение другому прило-
жению, нужно убедиться, что это другое приложение определяет со-
общение также, как это делается в коде вашего приложения. Внешние
коммуникации лучше обрабатываются с помощью динамического обмена
данными (DDE).
Передача сообщений
-----------------------------------------------------------------
До сих пор мы определяли реакцию на сообщения, но не говори-
ли пока о том, как генерировать сообщения. Большинство сообщений
Windows генерируются самой Windows в ответ на действия пользова-
теля или системные события. Но ваша программа можете генериро-
вать сообщения либо моделируя действия пользователя, либо манипу-
лирую элементами на экране.
ObjectWindows уже обеспечивает для вас способы передачи мно-
гих сообщений, которые в противном случае пришлось бы передавать
вручную. Например, общим случаем для генерации сообщений является
работа с управляющим элементами. Чтобы добавить строку в блок
списка, Windows определяет такие сообщения как lb_AddString, а
чтобы отменить выбор кнопки с зависимой фиксацией или выбрать ее
- bm_SetCheck. ObjectWindows определяет методы для объектов уп-
равляющих элементов (TListBox.AddString и TCheckBox.SetCheck),
посылающие для вас эти сообщения, так что вам даже не нужно ду-
мать об их использовании.
Возможно вы найдете, что для большинства функций (которые в
противном случае пришлось бы выполнять передачей сообщений) уже
существуют объектные методы.
Передача и отправление сообщений
-----------------------------------------------------------------
Тем не менее, иногда возникает необходимость передачи сооб-
щений в окна вашего приложения. Windows предусматривает две функ-
ции, позволяющие вам генерировать сообщения - SendMessage и
PostMessage. Основное отличие этих двух функций состоит в том,
что SendMessage ожидает обработки сообщения перед возвратом.
PostMessage просто добавляет сообщение в очередь сообщений и
возвращает управление.
Имейте в виду, что SendMessage, особенно при вызове в методе
реакции на сообщение, может вызвать бесконечный цикл или клинч,
приводящие к сбою программ. Вам следует не только избегать оче-
видных циклов, таких как методы реакции на сообщения, генерирую-
щих то же сообщение, которое его вызывает, но избегать также пе-
редачи сообщений с побочными эффектами. Например, метод Paint
объекта, который вызывается в ответ на сообщения wm_Paint, оче-
видно должен явным образом посылать самому себе другое сообщение
wm_Paint. Но он должен также избегать других действий, дающих в
результате другое сообщение wm_Paint, посылаемое, пока метод еще
активен, таких как изменение размеров окна, запрещение окна или
создание/уничтожение перекрывающихся окон.
Передача сообщения
-----------------------------------------------------------------
Для передачи сообщения требуется следующее: описатель ок-
на-получателя, номер сообщения и параметры Word и Longint. В при-
ложении ObjectWindows описателем получателя является обычно поле
HWindow интерфейсного объекта. Идентификатор сообщения - это
просто константа, идентифицирующая конкретное сообщение, которое
вы хотите передать (такая как wm_More или em_SetTabStops). Пара-
метры в зависимости от сообщения могут быть различными.
Значение, возвращаемое SendMessage - это значение поля
Result в записи сообщения при завершении обработки. Имейте в ви-
ду, что если вы вызываете наследуемый или используемый по умолча-
нию метод реакции на сообщение, ваше значение может быть переза-
писано.
Windows с помощью SendMessage обеспечивает ограниченное
средство циркулярной рассылки сообщений. Если вы в качестве опи-
сателя окна, в которое нужно передать сообщение, зададите $FFFF,
Windows посылает сообщения всем всплывающим и перекрывающимся ок-
нам в системе (не только в вашем приложении). Таким образом, вы
не должны использовать циркулярную рассылку сообщений, опреде-
ляемых пользователем, так как не может быть уверены, что другое
приложение не определило данное сообщение каким-то другим спосо-
бом.
Отправление сообщения
-----------------------------------------------------------------
Отправление сообщения полезно использовать, если вас не осо-
бенно волнует, когда должна последовать реакция на сообщение, или
вам нужно избежать циклов, которые может вызвать SendMessage. Ес-
ли все, что вам нужно сделать - это убедиться в передаче сообще-
ния, и вы можете продолжать работу, не ожидая реакции, то можете
вызывать функцию PostMessage. Ее параметры идентичны параметрам
функции SendMessage. PostMessage не следует использовать для пе-
редачи сообщений управляющим элементам.
Передача сообщения управляющему элементу
-----------------------------------------------------------------
Возможно, единственным случаем, когда вам потребуется пере-
дать сообщение элементу на экране, не имеющему соответствующего
объекта, является случай, когда вам нужно взаимодействовать с уп-
равляющим элементом в диалоговом окне. Проще всего для этого ис-
пользовать объекты (управляющего элемента, диалогового блока или
обоих).
Если управляющий элемент имеет связанный с ним объект, вы
можете просто использовать для получения идентификатора управляю-
щего элемента поле HWindow объекта. Если диалоговый блок имеет
связанный с ним объект, вы можете вызвать метод SendDlgItemMsg
объекта диалогового блока, для которого задаются идентификатор
управляющего элемента, идентификатор сообщения и два параметра
сообщения. В большинстве приложений ObjectWindows для вас досту-
пен любой их этих подходов.
Если по каким-то причинам у вас нет ни объекта диалогового
блока, ни доступного объекта управляющего элемента, вы можете
послать сообщение управляющему элементу в диалоговом окне с по-
мощью функции API Windows SendDlgItemMessage, которая воспринима-
ет в качестве параметров описатель диалогового блока, идентифика-
тор управляющего элемента, идентификатор сообщения и два парамет-
ра сообщения.
Диапазоны сообщений
-----------------------------------------------------------------
Сообщение определяется полем message записи сообщения
(16-битовое значение). Windows резервирует для своих собственных
стандартных сообщений 0$03FF, а остальные сообщения до $7FFF
резервируются для сообщений, определенных пользователем. Диапа-
зоны остальных сообщений в ObjectWindows подразделяются на диа-
пазоны команд и уведомлений (как показано в следующей таблице):
Диапазоны сообщений Таблица 16.1
+-------------------------------------+----------------+
| Диапазон | Значения |
+-------------------------------------+----------------|
| Сообщения, зарезервированные | $0000-$7FFF |
| для Windows. | |
+-------------------------------------+----------------|
| Сообщения, определяемые | $0400-$8FFF |
| пользователем. | |
+-------------------------------------+----------------|
| Уведомляющие сообщения | $8000-$8FFF |
| управляющих элементов. | |
+-------------------------------------+----------------|
| Зарезервированные в Windows | $8F00-$8FFF |
| уведомляющие сообщения | |
| управляющих элементов. | |
+-------------------------------------+----------------|
| Уведомляющие сообщения | $9000-$9FFF |
| порождающего объекта. | |
+-------------------------------------+----------------|
| Зарезервированные в Windows | $9F00-$9FFF |
| уведомляющие сообщения | |
| порождающего объекта. | |
+-------------------------------------+----------------|
| Командные сообщения. | $A000-$FFFF |
+-------------------------------------+----------------|
| Команды, зарезервированные | $FF00-$FFFF |
| в ObjectWindows. | |
+-------------------------------------+----------------+
+------------------------+
cm_Internal ($FF00) +------------------------|
| |
| |
| Команды |
| |
| |
cm_First ($A000) +------------------------|
nf_Internal ($9F00) +------------------------|
| |
| Уведомления |
| управляющих элементов |
| |
nf_First ($9000) +------------------------|
id_Internal ($8F00) +------------------------|
| |
| Уведомления |
| порождающих объектов |
| |
id_First ($8000) +------------------------| +
| | |
| | |
| Сообщения, | |
| определенные | + wm_Count
| пользователем | | ($8000)
| | |
| | |
wm_User ($A000) +------------------------| +
| Сообщения Windows |
wm_First($0000) +------------------------+
Рис. 16.1 Диапазоны сообщений и команд.
Назад | Содержание | Вперед