О чём не пишут в книгах по Delphi
Шрифт:
// риск ошибки ввода в тех случаях, когда требуется целое
// неотрицательное число.
SetWindowLong(EditNumber.Handle, GWL_STYLE, GetWindowLong(EditNumber.Handle, GWL_STYLE) or ES_NUMBER);
end;
procedure TForm1.BtnBroadcastClick(Sender: TObject);
var
Num: Integer;
Recipients: DWORD;
begin
try
Num := StrToInt(EditNumber.Text);
// Для широковещательной рассылки сообщения служит
// функция BroadcastSystemMessage.
В литературе обычно
// советуют использовать более простую функцию
// PostMessage, указывая в качестве адресата
// HWND_BROADCAST. Однако PostMessage рассылает
// сообщения только окнам верхнего уровня, не имеющим
// владельца (в терминах системы). Но главная форма
// приложения имеет владельца - это невидимое окно
// приложения, реализуемое объектом TApplication.
// Поэтому такое широковещательное сообщение главная
// форма приложения не получит — его получит только
// невидимое окно приложения (это сообщение можно
// будет перехватить, назначив обработчик
// Application.OnMessage - вручную или с помощью
// компонента TApplicationEvents). Чтобы главная форма
// тоже попала в список окон, получающих
// широковещательное сообщение, используется функция
// BroadcastSystemMessage.
Recipients := BSM_APPLICATIONS;
BroadcastSystemMessage(BSF_POSTMESSAGE, @Recipients, FSendNumberMessage, Num, 0);
except
on EConvertError do
begin
Application.MessageBox(
'Введенное значение не является числом', 'Ошибка',
MB_OK or MB_ICONSTOP);
end;
end;
end;
procedure TForm1.WndProc(var Msg: TMessage);
begin
if Msg.Msg = FSendNumberMessage then
LabelNumber.Caption := IntToStr(Msg.WParam)
else inherited;
end;
end.
Как уже отмечалось ранее, для обработки глобального сообщения нельзя использовать методы с директивой
message
, т.к. номер сообщения на этапе компиляции еще не известен. Здесь для обработки глобального сообщения мы перекрываем метод WndProc
. Соответственно, все оконные сообщения, в том числе и те, которые окно получает при создании, будет обрабатывать перекрытый метод WndProc
. Это значит, что поле FSendNumberMessage
, которое задействовано в этом методе, должно быть правильно инициализировано раньше, чем окно получит первое сообщение. Поэтому вызов функции RegisterWindowMessage
выполнять, например, в обработчике события OnCreate
формы уже поздно. Его необходимо выполнить в конструкторе формы, причем до того, как будет вызван унаследованный конструктор. Примечание
Существует другой способ решения этой проблемы: метод
WndProc
должен проверять значение поля FSendNumberMessage
, и, если оно равно нулю, сразу переходить к вызову унаследованного метода. В этом случае инициализировать FSendNumberMessage
можно позже. Нажатие на кнопку
BtnBroadcast
приводит к широковещательной отправке сообщения. Отправить широковещательное сообщение можно двумя способами: функцией PostMessage
с адресатом HWND_BROADCAST
BroadcastSystemMessage
. Первый вариант позволяет отправить сообщения только окнам верхнего уровня, не имеющим владельца в терминах системы. Таким окном в VCL-приложении является только невидимое окно приложения, создаваемое объектом Application
. Главная форма имеет владельца в терминах системы — то самое невидимое окно приложения. Поэтому широковещательное сообщение, посланное с помощью PostMessage
, главная форма не получит, это сообщение пришлось бы ловить с помощью события Application.OnMessage
. Мы здесь применяем другой способ — отправляем сообщение с помощью функции BroadcastSystemMessage
, которая позволяет указывать тип окон, которым мы хотим отправить сообщения. В частности, здесь мы выбираем тип BSM_APPLICATION
, чтобы сообщение посылалось всем окнам верхнего уровня, в том числе и тем, которые имеют владельца. При таком способе отправки главная форма получит это широковещательное сообщение, поэтому его обработку можно реализовать в главной форме. 1.2.6. Пример ButtonDel
Программа ButtonDel демонстрирует, как можно удалить кнопку в обработчике нажатия этой кнопки. Очень распространенная ошибка — попытка написать код, один из примеров которого приведен в листинге 1.32.
Листинг 1.32. Неправильный вариант удаления кнопки в обработчике ее нажатия
procedure TForm1.Button1Click(Sender: TObject);
begin
Button1.Free;
end;
Рассмотрим, что произойдет в случае выполнения этого кода. Когда пользователь нажимает на кнопку, форма получает сообщение
WM_COMMAND
. При обработке форма выясняет, что источником сообщения является объект Button1
и передает этому объекту сообщение CN_COMMAND
. Button1
, получив его, вызывает метод Click
, который проверяет, назначен ли обработчик OnClick
, и, если назначен, вызывает его. Таким образом, после завершения Button1Click
управление снова вернется в метод Click
объекта Button1
, из него — в метод CNCommand
, из него — в Dispatch
, оттуда — в WndProc
, а оттуда — в MainWndProc
. А из MainWndProc
управление будет передано в оконную процедуру, сформированную компонентом с помощью MakeObjectInstance
. В деструкторе Button1
эта оконная процедура будет уже удалена. Таким образом, управление получат последовательно пять методов уже не существующего объекта и одна несуществующая процедура. Это может привести к самым разным неприятным эффектам, но, скорее всего, — к ошибке Access violation (обращение к памяти, которую программа не имеет права использовать). Поэтому приведенный в листинге 1.32 код будет неработоспособным. В классе TCustomForm
для безопасного удаления формы существует метод Release
, который откладывает уничтожение объекта до того момента, когда это будет безопасно, но остальные компоненты подобного метода не имеют. Примечание
Метод
TCustomForm.Release
на поверку тоже оказывается не совсем безопасным — подробнее об этом написано в разд. 3.4.3. Очевидно, что для безопасного удаления кнопки эту операцию следует отложить до того момента, когда все методы удаляемой кнопки уже закончат свою работу. Вставить требуемый код в обработчик
WM_COMMAND
формы достаточно сложно, поэтому мы будем использовать другой способ: пусть обработчик кнопки посылает форме сообщение, в обработчике которого она будет удалять кнопку. Здесь важно, что сообщение посылается, а не отправляется, т.е. ставится в очередь, из которой извлекается уже после того, как будет полностью обработано сообщение WM_COMMAND
. В этом случае методы удаляемой кнопки не будут вызваны, и удаление пройдет без неприятных последствий.
Поделиться:
Популярные книги
Черный маг императора 3
3. Черный маг императора
Фантастика:
попаданцы
аниме
5.00
рейтинг книги
Повелитель механического легиона. Том VIII
8. Повелитель механического легиона
Фантастика:
технофэнтези
аниме
фэнтези
5.00
рейтинг книги
Пипец Котенку! 3
3. РОС: Пипец Котенку!
Фантастика:
юмористическое фэнтези
попаданцы
аниме
5.00
рейтинг книги
Разбуди меня
7. Серьёзные мальчики в форме
Любовные романы:
современные любовные романы
остросюжетные любовные романы
5.00
рейтинг книги
Боги, пиво и дурак. Том 6
6. Боги, пиво и дурак
Фантастика:
фэнтези
попаданцы
5.00
рейтинг книги
Болотник 2
2. Болотник
Фантастика:
попаданцы
альтернативная история
6.25
рейтинг книги
Ты всё ещё моя
4. Под запретом
Любовные романы:
современные любовные романы
7.00
рейтинг книги
S-T-I-K-S. Пройти через туман
Вселенная S-T-I-K-S
Фантастика:
боевая фантастика
7.00
рейтинг книги
Имя нам Легион. Том 4
4. Меж двух миров
Фантастика:
боевая фантастика
рпг
аниме
5.00
рейтинг книги
Сводный гад
2. Самбисты
Любовные романы:
современные любовные романы
эро литература
5.00
рейтинг книги
Я князь. Книга XVIII
18. Дорогой барон!
Фантастика:
юмористическое фэнтези
попаданцы
аниме
5.00
рейтинг книги
Королевская Академия Магии. Неестественный Отбор
Любовные романы:
любовно-фантастические романы
8.22
рейтинг книги
Последняя Арена 6
6. Последняя Арена
Фантастика:
рпг
постапокалипсис
5.00
рейтинг книги
Жребий некроманта. Надежда рода
1. Жребий некроманта
Фантастика:
фэнтези
попаданцы
6.50