Чтение онлайн

на главную - закладки

Жанры

О чём не пишут в книгах по Delphi

Григорьев Антон Борисович

Шрифт:

Затем начинает обрабатываться событие

WM_NOTIFY
, которое уведомляет программу о том, что пользователь нажал на кнопку компонента
TUpDown
. Именно при обработке этого сообщения VCL вызывает событие
TUpDown.OnClick
, в котором открывается модальное окно. Всё это происходит очень быстро, поэтому кнопку мыши пользователь отпускает тогда, когда модальное окно уже оказалось на экране. В результате сообщение
WM_LBUTTONUP
либо попадает в очередь открывшегося диалогового окна, если мышь находилась над ним, либо вообще никуда не попадает, если мышь была вне модального окна. На время существования модального окна система "забывает" о том, что мышь захвачена для монопольного использования, но "вспоминает"
об этом, как только модальное окно закрывается. Монопольное использование мыши компонентом
TUpDown
должно отменяться при обработке сообщения
WM_LBUTTONUP
, но оно, как было сказано ранее, в очередь не попадает, поэтому после закрытия окна мышь остается захваченной данным компонентом. Поэтому любое нажатие кнопки мыши воспринимается системой как относящееся к
UpDown1
, и снова приводит к помещению в очередь сообщений
WM_LBUTTONDOWN
и
WM_NOTIFY
, которые обрабатываются описанным образом. Так получается порочный круг, из которого при нормальной работе программы нет выхода. Этот круг может быть разорван, например, отладчиком, который отменяет монопольное использование мыши компонентами программы, чтобы иметь возможность работать.

В этой проблеме виновата VCL, которая зачем-то назначает компоненту

TUpDown
стиль
csCaptureMouse
. Данный компонент реализуется не средствами VCL, — это стандартное окно системного класса
UPDOWN_CLASS
, а компонент
TUpDown
— это только оболочка для него. Поэтому все необходимые перехваты мыши выполняются самой системой. VCL нет нужды в это вмешиваться. Чтобы избавиться от проблемы, нужно убрать
csCaptureMouse
из списка стилей компонента. Делается это так:

UpDown1.ControlStyle := UpDown1.ControlStyle - [csCaptureMouse];

Этот код достаточно выполнить один раз (например, в обработчике события

OnCreate
формы), и проблемы с зацикливанием исчезнут (в примере UpDownDlg эта строка закомментирована).

Отметим, что в Windows предусмотрено специальное сообщение —

WM_CANCELMODE
, — посылаемое при открытии диалогового окна тому окну, которое захватило мышь, чтобы оно ее освободило. Один из способов решения проблемы — добавление в
UpDown1
обработчика этого сообщения (для этого можно написать наследника
TUpDown
или же воспользоваться свойством
WindowProc
см. разд. 1.1.8), который отменит захват мыши. Отсутствие этого обработчика — тоже явная ошибка VCL.

3.4.3. Access violation при закрытии формы с перекрытым методом WndProc

Чтобы увидеть этот "подводный камень", создадим проект, содержащий две формы: главную

Form1
и вспомогательную
Form2
. В
Form1
добавим код, который по нажатию кнопки открывает
Form2
.

Во второй форме напишем обработчик события

OnClose
таким образом, чтобы он устанавливал по закрытию действие
caFree
. Добавим поле строкового типа, перекроем конструктор и метод
WndProc
так, чтобы окончательный код выглядел следующим образом (листинг 3.52, пример CloseAV на компакт- диске).

Листинг 3.52. Код класса
TForm2

type

 TForm2 = class(TForm)

procedure FormClose(Sender: TObject; var Action: TCloseAction);

 private

S: string;

 protected

procedure WndProc(var Message: TMessage); override;

 public

constructor Create(AOwner: TComponent); override;

 end;

.... 

constructor TForm2.Create(AOwner: TComponent);

begin

 S := 'abc';

 inherited;

end;

procedure TForm2.WndProc(var Message: TMessage);

begin

 inherited;

 S[2] := 'x'; { * }

end;

procedure TForm2.FormClose(Sender: TObject; var Action: TCloseAction);

begin

 Action := caFree;

end;

Обратите

внимание, что в конструкторе сначала присваивается значение полю
S
, и лишь потом вызывается унаследованный конструктор. Это сделано потому, что по умолчанию
S
содержит пустую строку, т.е.
nil
, а уже при вызове унаследованного конструктора окно получит сообщения, для обработки которых будет вызван метод
WndProc
. Если в этот момент
S
будет по-прежнему
nil
, попытка обратиться ко второму символу строки вызовет Access violation. Поэтому еще до начала работы унаследованного конструктора поле
S
должно получить подходящее значение.

Запустим программу и попытаемся закрыть второе окно. Возникнет исключение Access Violation: Write of address 00000001. Проблема будет в строке, отмеченной

{*}
. При этом любые другие манипуляции с окном никаких исключений вызывать не будут.

При

Action = caFree
после завершения работы метода FormClose VCL вызывает метод
TCustomForm.Release
. Проблема именно в нем: если попытаться закрыть
Form2
с помощью
Release
, возникнет то же самое исключение. В справке
Release
позиционируется как безопасный способ удаления формы из ее собственного метода. К сожалению, в действительности это не так: реализация этого удаления оставляет желать лучшего и может приводить к попыткам работать с объектом тогда, когда его уже не существует.

При вызове

Release
в очередь помещается сообщение
CM_RELEASE
, адресатом которого является сама удаляемая форма. В очередном цикле петли сообщений
CM_RELEASE
извлекается из очереди и передается на обработку. Так как сообщение адресовано форме, она же его и обрабатывает. Рассмотрим более подробно, как это происходит. (Детально механизм обработки сообщений в VCL описан в разд. 1.1.8; мы здесь рассмотрим только ту часть, которая относится к обработке
CM_RELEASE
.)

Система передает управление оконной процедуре. Для каждого экземпляра визуального компонента VCL создает свою оконную процедуру с помощью

MakeObjectInstance
. Эта процедура вызывает метод объекта
MainWndProc
, передающий управление тому методу, на который указывает свойство
WindowProc
. По умолчанию это
WndProc
.
WndProc
не обрабатывает
CM_RELEASE
самостоятельно, а передает его методу
Dispatch
.
Dispatch
пытается найти для этого сообщения специальный обработчик (метод с директивой
message
) и, т.к. в
TCustomForm
такой обработчик описан (он называется
CMRelease
), передаёт управление ему.

Поделиться:
Популярные книги

Черный маг императора 3

Герда Александр
3. Черный маг императора
Фантастика:
попаданцы
аниме
5.00
рейтинг книги
Черный маг императора 3

Повелитель механического легиона. Том VIII

Лисицин Евгений
8. Повелитель механического легиона
Фантастика:
технофэнтези
аниме
фэнтези
5.00
рейтинг книги
Повелитель механического легиона. Том VIII

Пипец Котенку! 3

Майерс Александр
3. РОС: Пипец Котенку!
Фантастика:
юмористическое фэнтези
попаданцы
аниме
5.00
рейтинг книги
Пипец Котенку! 3

Разбуди меня

Рам Янка
7. Серьёзные мальчики в форме
Любовные романы:
современные любовные романы
остросюжетные любовные романы
5.00
рейтинг книги
Разбуди меня

Боги, пиво и дурак. Том 6

Горина Юлия Николаевна
6. Боги, пиво и дурак
Фантастика:
фэнтези
попаданцы
5.00
рейтинг книги
Боги, пиво и дурак. Том 6

Болотник 2

Панченко Андрей Алексеевич
2. Болотник
Фантастика:
попаданцы
альтернативная история
6.25
рейтинг книги
Болотник 2

Ты всё ещё моя

Тодорова Елена
4. Под запретом
Любовные романы:
современные любовные романы
7.00
рейтинг книги
Ты всё ещё моя

S-T-I-K-S. Пройти через туман

Елисеев Алексей Станиславович
Вселенная S-T-I-K-S
Фантастика:
боевая фантастика
7.00
рейтинг книги
S-T-I-K-S. Пройти через туман

Имя нам Легион. Том 4

Дорничев Дмитрий
4. Меж двух миров
Фантастика:
боевая фантастика
рпг
аниме
5.00
рейтинг книги
Имя нам Легион. Том 4

Сводный гад

Рам Янка
2. Самбисты
Любовные романы:
современные любовные романы
эро литература
5.00
рейтинг книги
Сводный гад

Я князь. Книга XVIII

Дрейк Сириус
18. Дорогой барон!
Фантастика:
юмористическое фэнтези
попаданцы
аниме
5.00
рейтинг книги
Я князь. Книга XVIII

Королевская Академия Магии. Неестественный Отбор

Самсонова Наталья
Любовные романы:
любовно-фантастические романы
8.22
рейтинг книги
Королевская Академия Магии. Неестественный Отбор

Последняя Арена 6

Греков Сергей
6. Последняя Арена
Фантастика:
рпг
постапокалипсис
5.00
рейтинг книги
Последняя Арена 6

Жребий некроманта. Надежда рода

Решетов Евгений Валерьевич
1. Жребий некроманта
Фантастика:
фэнтези
попаданцы
6.50
рейтинг книги
Жребий некроманта. Надежда рода