А. Григорьев - О чём не пишут в книгах по Delphi

Тут можно читать онлайн А. Григорьев - О чём не пишут в книгах по Delphi - бесплатно ознакомительный отрывок. Жанр: comp-programming, издательство БХВ-Петербург, год 2008. Здесь Вы можете читать ознакомительный отрывок из книги онлайн без регистрации и SMS на сайте лучшей интернет библиотеки ЛибКинг или прочесть краткое содержание (суть), предисловие и аннотацию. Так же сможете купить и скачать торрент в электронном формате fb2, найти и слушать аудиокнигу на русском языке или узнать сколько частей в серии и всего страниц в публикации. Читателям доступно смотреть обложку, картинки, описание и отзывы (комментарии) о произведении.
  • Название:
    О чём не пишут в книгах по Delphi
  • Автор:
  • Жанр:
  • Издательство:
    БХВ-Петербург
  • Год:
    2008
  • Город:
    СПб
  • ISBN:
    978-5-9775-019003
  • Рейтинг:
    4.25/5. Голосов: 81
  • Избранное:
    Добавить в избранное
  • Отзывы:
  • Ваша оценка:
    • 80
    • 1
    • 2
    • 3
    • 4
    • 5

А. Григорьев - О чём не пишут в книгах по Delphi краткое содержание

О чём не пишут в книгах по Delphi - описание и краткое содержание, автор А. Григорьев, читайте бесплатно онлайн на сайте электронной библиотеки LibKing.Ru

Рассмотрены малоосвещённые вопросы программирования в Delphi. Описаны методы интеграции VCL и API. Показаны внутренние механизмы VCL и приведены примеры вмешательства в эти механизмы. Рассмотрено использование сокетов в Delphi: различные механизмы их работы, особенности для протоколов TCP и UDP и др. Большое внимание уделено разбору ситуаций возникновения ошибок и получения неверных результатов в "простом и правильном" коде. Отдельно рассмотрены особенности работы с целыми, вещественными и строковыми типами данных, а также приведены примеры неверных результатов, связанных с ошибками компилятора, VCL и др. Для каждой из таких ситуаций предложены методы решения проблемы. Подробно рассмотрен синтаксический анализ в Delphi на примере арифметических выражений. Многочисленные примеры составлены с учётом различных версий: от Delphi 3 до Delphi 2007. Прилагаемый компакт-диск содержит примеры из книги.

Для программистов

О чём не пишут в книгах по Delphi - читать онлайн бесплатно ознакомительный отрывок

О чём не пишут в книгах по Delphi - читать книгу онлайн бесплатно (ознакомительный отрывок), автор А. Григорьев
Тёмная тема
Сбросить

Интервал:

Закладка:

Сделать

Посмотрим. что будет, если строку S[1] := 'x'заменить на S := IntToStr(Msg.Msg). Как мы уже выяснили, после уничтожения объекта в той области памяти, где хранилось значение S, будет nil. Указатель на вновь созданную строку будет помещен в эту область памяти. Но к ней уже не будет применяться финализация, т.к. менеджер памяти будет считать эту область памяти финализированной. Произойдет утечка памяти.

Отметим, что для вновь созданной строки память может быть выделена таким образом, что она наложится на те ячейки, в которых хранились значения полей уничтоженной формы, в том числе значение S. В этом случае попытка обратиться к такому полю приведет к непредсказуемым результатам.

Аналогичная проблема может появляться не только при перекрытии WndProc, а вообще при любом способе внедрения своего кода в цепочку обработки так, чтобы он выполнялся после CMRelease.

Совершенно непонятно, почему разработчики VCL реализовали такой заведомо некорректный механизм работы Release. Чтобы избежать всех описанных проблем, достаточно было бы просто посылать CM_RELEASEне самой форме, а окну, создаваемому объектом Application, а указатель на освобождаемую форму передавать через параметры этого сообщения. Тогда деструктор формы вызывался бы из метода объекта Application, и никаких проблем не было бы.

Эта проблема обнаружена во всех версиях Delphi с 3-й по 2007-ю (в других версиях не проверялась). Самый простой способ ее преодоления — отмена опасных действий, если получено сообщение CM_RELEASE. Например, в описанном случае безопасным будет следующий код (листинг 3.53).

Листинг 3.53. Безопасный вариант метода WndProc

procedure TForm2.WndProc(var Message: TMessage);

begin

inherited;

if Msg.Msg <> CM_RELEASE then s[2] := 'x';

end;

Другой способ заключается в том. чтобы перенести обработку CM_RELEASEв объект Applicationс помощью его события OnMessage. Проблема заключается лишь в том, что адрес удаляемой формы будет неизвестен, но его легко найти по дескриптору окна. Например, в данном случае можно положить на первую форму TApplicationEventsи в его обработчике OnMessageнаписать следующий код (листинг 3.54; в примере CloseAV этот код закомментирован).

Листинг 3.54. Обработка сообщения CM_RELEASEобъектом Application

procedure TForm1.ApplicationEvents1Message(var Msg: tagMSG; var Handled: Boolean);

var

I: Integer;

begin

if Msg.Message = CM_RELEASE then

for I := 0 to Screen.FormCount - 1 do

if Screen. Forms[I].Handle = Msg.hwnd then

begin

Screen.Forms[I].Free;

Handled := True;

Exit;

end;

end;

Событие OnMessageпозволяет перехватить сообщения до того, как они будут диспетчеризованы окну-адресату, соответственно, форма будет уничтожена раньше, чем начнет обрабатывать CM_RELEASE.

3.4.4. Подмена имени оконного класса, возвращаемого функцией GetClassInfo

Создадим новый проект в Delphi, поместим на форму кнопку и метку и создадим следующий обработчик нажатия кнопки (листинг 3.55, пример ClassName на компакт-диске).

Листинг 3.55. Подмена имени оконного класса

procedure TForm1.Button1Click(Sender: TObject);

var

CI: TWndClass;

S: string;

procedure DoGetClassInfo;

begin

GetClassInfo(hInstance, PChar('TForm' + IntToStr(1)), CI);

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менеджер памяти выделит другую область.

Читать дальше
Тёмная тема
Сбросить

Интервал:

Закладка:

Сделать


А. Григорьев читать все книги автора по порядку

А. Григорьев - все книги автора в одном месте читать по порядку полные версии на сайте онлайн библиотеки LibKing.




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


Отзывы читателей о книге О чём не пишут в книгах по Delphi, автор: А. Григорьев. Читайте комментарии и мнения людей о произведении.


Понравилась книга? Поделитесь впечатлениями - оставьте Ваш отзыв или расскажите друзьям

Напишите свой комментарий
x