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

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

Жанры

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

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

Шрифт:

 end;

begin

 DoGetClassInfo;

 S := 'abcde' + IntToStr(2);

 Label1.Caption := CI.lpszClassName;

end;

Что будет выведено на экран в результате выполнения этого кода? Так как класс называется "TForm1", логично предположить, что именно это и будет выведено. На самом деле мы увидим abcde2 — ту строку, которая присвоена переменной

S
.

Разберемся, как значение переменной

S
оказывается в поле
CI.lpszClassName
. Согласно MSDN
поле
lpszClassName
имеет тип
LPCTSTR(PChar)
, и в него функция
GetClassInfo
заносит указатель на строку, содержащую имя оконного класса. Но нигде не сказано, в какой области памяти должна располагаться эта строка.

Функция

GetClassInfo
поступает очень просто, но не совсем корректно: один из ее аргументов — указатель на строку с именем класса. Именно его функция и помещает в
lpszClassName
.

В приведенном примере в качестве аргумента

GetClassInfo
передаётся выражение типа
string
, приведенное к
PChar
, которое не может быть вычислено на этапе компиляции, поэтому компилятор генерирует код, вычисляющий данное выражение. Этот код размещает вычисленное выражение в динамической памяти, и в
GetClassInfo
передаётся указатель на эту строку.

Все строковые выражения, вычисленные подобным образом, должны удаляться из памяти, чтобы не было утечек. Компилятор помещает код, освобождающий эту память, в эпилог той функции, в которой встретилось выражение. В данном случае — в эпилог локальной процедуры

DoGetClassInfo
.

Освободившуюся память менеджер памяти не сразу возвращает системе придерживает, чтобы иметь возможность быстрее выделить память при следующем запросе. Таким образом, после завершения работы

DoGetClassInfo
память, в которой хранится вычисленное имя оконного класса (и на которую указывает
CI.lpszClassName
), по-прежнему принадлежит процессу, но менеджер памяти полагает ее свободной и считает себя вправе использовать ее по своему усмотрению.

Когда присваивается значение переменной

S
, для размещения новой строки менеджер памяти выделяет ту самую область, в которой ранее хранилось имя класса. Так как
CI.lpszClassName
по-прежнему содержит этот адрес, обращение к этому полю возвращает новую строку, которая присвоена переменной
S
.

Примечание

В Delphi до 7-й версии включительно описанный эффект наблюдается при любой длине строки, присваиваемой переменной

S
, в более новых версиях Delphi — только в том случае, если длина этой строки находится в пределах от 5 до 11 символов. Это связано с тем, что новый менеджер памяти, появившийся в этих версиях Delphi, с целью уменьшения фрагментации разбивает кучу на несколько областей, в каждой из которых выделяет блоки памяти, укладывающиеся в соответствующий данной области диапазон размеров блоков. Если строка, присваиваемая переменной
S
, слишком сильно отличается по размеру от
'TForm1'
для этой строки выделяется память в другой области, и подмены не происходит.

Если в данном примере не выносить вызов функции

GetClassInfo
в отдельную процедуру
DoGetClassInfo
, а вызывать ее напрямую из
Button1Click
, описанного эффекта не будет, потому что в этом случае освобождение памяти, занятой для вычисленного имени класса, будет производиться в эпилоге
Button1Click
, и на момент
присваивания значения переменной
S
эта память будет считаться занятой, поэтому для
S
менеджер памяти выделит другую область.

Принципиально и то, что в обоих случаях (в функции

GetClassInfo
и при присваивании значения переменной
S
) используются не строковые литералы, а выражения, вычисляемые только на этапе выполнения программы. Строковые литералы размещаются компилятором в сегменте кода, и указатели, переданные в
GetClassInfo
и присвоенные переменной
S
, будут указывать не на динамическую память, а на эти литералы, и подмены не произойдет.

Избежать проблемы можно двумя способами. Во-первых, не следует передавать значение поля

lpszClassName
за пределы той функции, в которой была вызвана
GetClassName
. Во-вторых, имя оконного класса должно быть известно программе до вызова
GetClassName
. Лучше использовать ту строку, в которой хранится это имя, чем поле
lpszClassName
.

3.4.5. Ошибка EReadError при использовании вещественных свойств

Если в секции

published
компонента имеются свойства вещественного типа (
Single
,
Double
или Extended), то попытка присвоить в режиме проектирования формы этим свойствам некоторые вполне корректные значения приводит к ошибке
EReadError
при чтении ресурсов формы (т.е. при создании формы). Для типов
Double
и
Extended
ошибка возникает, если значение свойства
X
лежит в одном из указанных диапазонов:

– 1e15 < х <= MinInt - 1

или

MaxInt + 1 <= X < 1e15

Не совсем понятно, при чем здесь значения

MaxInt
и
MinInt
, если речь идет о вещественных числах, но проблема существует. Типу
Single
не хватает точности, чтобы передавать значения
MaxInt
и
MinInt
без искажений. Тем не менее, с поправкой на уменьшение точности границ диапазонов, эта же ошибка возникает и для свойств типа
Single
. Ошибка возникает только в случае текстовой формы dfm-файла (все версии Delphi, начиная с пятой, по умолчанию используют эту форму). При бинарной форме dfm-файла ошибки не происходит.

Ошибка обнаружена в Delphi 5 и 6, причем в Delphi 5 попытка ввести значение из указанного диапазона также может привести к ошибке и в режиме проектирования, при переключении между текстом модуля и формой. В Delphi 6 были замечены ошибки только при запуске программы, в режиме проектирования они не возникали. В Delphi 7 эта проблема уже решена, указанные значения свойств не приводят к ошибкам. В более ранних версиях Delphi проблема, естественно, также отсутствует, потому что в них dfm-файл всегда представляется в бинарной форме.

Для решения проблемы могут быть рекомендованы два способа.

1. Обновить Delphi до седьмой (или более поздней) версии.

2. Выбрать бинарную форму dfm-файла. Для этого нужно щёлкнуть правой кнопкой мыши на форме и в открывшемся меню убрать галочки с пункта Text DFM

Можно также отказаться от присвоения проблемных значений свойствам в режиме проектирования и присваивать их во время выполнения программы.

3.4.6. Ошибка List index out of bounds при корректном значении индекса

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

Черный маг императора 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
рейтинг книги
Жребий некроманта. Надежда рода