- Любовные романы
- Фантастика и фэнтези
- Ненаучная фантастика
- Ироническое фэнтези
- Научная Фантастика
- Фэнтези
- Ужасы и Мистика
- Боевая фантастика
- Альтернативная история
- Космическая фантастика
- Попаданцы
- Юмористическая фантастика
- Героическая фантастика
- Детективная фантастика
- Социально-психологическая
- Боевое фэнтези
- Русское фэнтези
- Киберпанк
- Романтическая фантастика
- Городская фантастика
- Технофэнтези
- Мистика
- Разная фантастика
- Иностранное фэнтези
- Историческое фэнтези
- LitRPG
- Эпическая фантастика
- Зарубежная фантастика
- Городское фентези
- Космоопера
- Разное фэнтези
- Книги магов
- Любовное фэнтези
- Постапокалипсис
- Бизнес
- Историческая фантастика
- Социально-философская фантастика
- Сказочная фантастика
- Стимпанк
- Романтическое фэнтези
- Ироническая фантастика
- Детективы и Триллеры
- Проза
- Юмор
- Феерия
- Новелла
- Русская классическая проза
- Современная проза
- Повести
- Контркультура
- Русская современная проза
- Историческая проза
- Проза
- Классическая проза
- Советская классическая проза
- О войне
- Зарубежная современная проза
- Рассказы
- Зарубежная классика
- Очерки
- Антисоветская литература
- Магический реализм
- Разное
- Сентиментальная проза
- Афоризмы
- Эссе
- Эпистолярная проза
- Семейный роман/Семейная сага
- Поэзия, Драматургия
- Приключения
- Детская литература
- Загадки
- Книга-игра
- Детская проза
- Детские приключения
- Сказка
- Прочая детская литература
- Детская фантастика
- Детские стихи
- Детская образовательная литература
- Детские остросюжетные
- Учебная литература
- Зарубежные детские книги
- Детский фольклор
- Буквари
- Книги для подростков
- Школьные учебники
- Внеклассное чтение
- Книги для дошкольников
- Детская познавательная и развивающая литература
- Детские детективы
- Домоводство, Дом и семья
- Юмор
- Документальные книги
- Бизнес
- Работа с клиентами
- Тайм-менеджмент
- Кадровый менеджмент
- Экономика
- Менеджмент и кадры
- Управление, подбор персонала
- О бизнесе популярно
- Интернет-бизнес
- Личные финансы
- Делопроизводство, офис
- Маркетинг, PR, реклама
- Поиск работы
- Бизнес
- Банковское дело
- Малый бизнес
- Ценные бумаги и инвестиции
- Краткое содержание
- Бухучет и аудит
- Ораторское искусство / риторика
- Корпоративная культура, бизнес
- Финансы
- Государственное и муниципальное управление
- Менеджмент
- Зарубежная деловая литература
- Продажи
- Переговоры
- Личная эффективность
- Торговля
- Научные и научно-популярные книги
- Биофизика
- География
- Экология
- Биохимия
- Рефераты
- Культурология
- Техническая литература
- История
- Психология
- Медицина
- Прочая научная литература
- Юриспруденция
- Биология
- Политика
- Литературоведение
- Религиоведение
- Научпоп
- Психология, личное
- Математика
- Психотерапия
- Социология
- Воспитание детей, педагогика
- Языкознание
- Беременность, ожидание детей
- Транспорт, военная техника
- Детская психология
- Науки: разное
- Педагогика
- Зарубежная психология
- Иностранные языки
- Филология
- Радиотехника
- Деловая литература
- Физика
- Альтернативная медицина
- Химия
- Государство и право
- Обществознание
- Образовательная литература
- Учебники
- Зоология
- Архитектура
- Науки о космосе
- Ботаника
- Астрология
- Ветеринария
- История Европы
- География
- Зарубежная публицистика
- О животных
- Шпаргалки
- Разная литература
- Зарубежная литература о культуре и искусстве
- Пословицы, поговорки
- Боевые искусства
- Прочее
- Периодические издания
- Фанфик
- Военное
- Цитаты из афоризмов
- Гиды, путеводители
- Литература 19 века
- Зарубежная образовательная литература
- Военная история
- Кино
- Современная литература
- Военная техника, оружие
- Культура и искусство
- Музыка, музыканты
- Газеты и журналы
- Современная зарубежная литература
- Визуальные искусства
- Отраслевые издания
- Шахматы
- Недвижимость
- Великолепные истории
- Музыка, танцы
- Авто и ПДД
- Изобразительное искусство, фотография
- Истории из жизни
- Готические новеллы
- Начинающие авторы
- Спецслужбы
- Подростковая литература
- Зарубежная прикладная литература
- Религия и духовность
- Старинная литература
- Справочная литература
- Компьютеры и Интернет
- Блог
О чём не пишут в книгах по Delphi - А. Григорьев
Шрифт:
Интервал:
Закладка:
procedure TForm1.Button1Click(Sender: TObject);
var
A1, A2: Integer;
begin
X := 2;
A1 := X;
Inc(A1, GetValueAndModifyX);
X := 2;
A2 := GetValueAndModifyX;
Inc(A2, X);
Label1.Caption := IntToStr(A1);
Label2.Caption := IntToStr(A2);
end;
Такой код, несмотря на побочные эффекты функции GetValueAndModifyX, даст ожидаемые значения при любом порядке вычисления операндов, т.к. здесь вычисление операндов разнесено по разным операторам, а порядок выполнения операторов четко определен.
ПримечаниеДругие компиляторы могут использовать иной порядок вычисления операндов. Так, FreePascal вычисляет их в том порядке, в каком они встречаются в выражении, т.е. в первом примере А1 получит значение 4, А2 — 3.
3.4.2. Зацикливание обработчика TUpDown.OnClick при открытии диалогового окна в обработчике
Для демонстрации этого "подводного камня" нам потребуется проект, на форме которого находится компонент TUpDown со следующим обработчиком события OnClick (листинг 3.51, пример UpDownDlg на компакт-диске).
Листинг 3.51. Обработчик события OnClick компонента UpDown1procedure TForm1.UpDown1Click(Sender: TObject; Button: TUDBtnType);
begin
Application.MessageBox('Text', 'Caption', MB_OK);
end;
Теперь, если запустить программу и нажать на верхнюю кнопку UpDown1, откроется окно с сообщением (при нажатии на нижнюю кнопку окно не будет открываться потому, что по умолчанию у компонент TUpDown свойства Position и Min равны нулю, поэтому нажатие на нижнюю кнопку не приводит к изменению значения Position, и событие OnClick не возникает; если изменить значение свойства Min или Position, то тот же эффект будет наблюдаться и при нажатии на нижнюю кнопку). Если закрыть это окно, то щелчок мышью в любом месте формы снова приведет к срабатыванию события OnClick и открытию окна, и так до бесконечности: любой щелчок по форме в любом ее месте будет снова и снова приводить к появлению сообщения. Эффект наблюдается и в том случае, когда вместо стандартного сообщения в обработчике показывается любая другая модальная форма. Кроме того, тот же эффект будет, и если использовать события OnChanging или OnChangingEx вместо OnClick, но мы далее для определенности будем говорить только об OnClick.
Если этот код пройти по шагам в отладчике, то никакого зацикливания не возникает: OnClick вызывается один раз, любое последующее нажатие кнопки мыши на форме не приводит ни к каким необычным результатам.
Причина этой проблемы в том, как VCL обрабатывает сообщения, которые система помещает в очередь. При нажатии на кнопку компонента TUpDown в очередь сообщений помещаются два сообщения: WM_LBUTTONDOWN и WM_NOTIFY. Компонент TUpDown по умолчанию имеет стиль csCaptureMouse — это означает, что при обработке WM_LBUTTONDOWN VCL захватывает мышь в монопольное пользование для данного компонента.
ПримечаниеМонопольное использование мыши означает, что любые сообщения, связанные с мышью, будут поступать захватившему мышь окну даже если ее курсор в это время находится за пределами данного компонента. Примером захвата мыши может служить любая кнопка: щелкните мышью над любой кнопкой на экране и, не отпуская клавиши мыши, начните перемещать курсор. Когда курсор будет выходить за пределы кнопки, она будет отжиматься, находить на нее — снова нажиматься. Теперь отведите курсор за пределы кнопки, отпустите клавишу мыши и снова подведите его к кнопке. Кнопка не нажмется. Это происходит потому, что пока клавиша мыши удерживается нажатой, мышь захвачена кнопкой, и сообщение об отпускании клавиши мыши передаётся кнопке, независимо от того, над каким окном находится курсор. Это позволяет кнопке правильно реагировать на отпускание пользователем мыши, в том числе и за ее пределами.
Затем начинает обрабатываться событие 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. Код класса TForm2type
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. Проблема будет в строке, отмеченной {*}. При этом любые другие манипуляции с окном никаких исключений вызывать не будут.

