Отладка программ
Если Вы пытались отлаживать какую-либо программу в Turbo Vision, Вы наверняка убедились, что трассировка (пошаговое прослеживание логики работы) таких программ весьма неэффективна. Вызвано это двумя обстоятельствами. Во-первых, значительная часть библиотеки Turbo Vision скрыта от Вас: библиотека поставляется в TPU-файлах, прослеживание работы которых невозможно. Во-вторых, в Turbo Vision используется принцип отделения логики создания видимых элементов от логики обработки связанных с ними событий: как только видимый элемент активизируется вызовом Execute, начинает работать его метод HandleEvent, который может породить целую цепочку непрослеживаемых трассировкой действий программы.
Ключом к решению проблемы отладки программ в Turbo Vision является расстановка точек контроля в наследуемых методах HandleEvent. Если программа не хочет открывать диалоговое окно или не реагирует на нажимаемую кнопку, следует прежде всего убедиться в том, что Ваши действия действительно порождают нужное событие.
Может случиться, что установленная контрольная точка не будет реагировать вообще или, наоборот, будет активизироваться слишком часто. Если точка не активизируется, это означает, что Ваш обработчик событий просто "не видит" событие. В этом случае необходимо убедиться в том, что поле EventMask видимого объекта содержит маску, позволяющую ему реагировать на событие нужного вида. Другой причиной "исчезновения" события может быть его перехват (и обработка) другим видимым элементом.
Это может быть вызвано различными обстоятельствами. Например, Вы могли ошибочно связать две разные команды с одной константой или используете команду, которую использует также другой видимый элемент. Кроме того, обычно в наследуемых методах HandleEvent вызывается обработчик событий объекта-родителя, который может "украсть" событие у Вашего обработчика. В таких ситуациях бывает достаточно сделать вызов родительского метода после того, как событие будет обработано Вами.
Если контрольная точка активизируется слишком часто, значит Вы установили ее неправильно. Например, если Вы установили эту точку внутри метода TGroup.Execute, точка будет непрерывно активизироваться, т.к. значительная часть времени работы программы тратится на ожидание события. Если Вам все-таки требуется установить контрольную точку именно в этом месте, сделайте ее условной, чтобы она не реагировала на пустые или ненужные события.
Иногда запущенная программа "зависает", т.е. перестает реагировать на любые действия пользователя. Такие ошибки отлаживать труднее всего. Если программа "зависла", попытайтесь прежде всего локализовать то место, в котором это происходит. Для этого обычно используется расстановка контрольных точек в подозрительных местах программы. Следует помнить, что в Turbo Vision "зависания" связаны в основном с тремя видами ошибок:
- освобождается динамический объект, который входил в состав ранее освобожденной динамической группы;
- читаются данные из потока в ошибочно зарегистрированный объект (объект имеет неуникальный регистрационный номер);
- элемент коллекции ошибочно трактуется как элемент другого типа.
Ошибки первого вида встречаются наиболее часто.
Например, прогон следующего невинного на первый взгляд варианта программы приводит к зависанию:
Uses Objects,Views; var G1, G2: PGroup; R: TRect; begin R.Assign(10.5.70.20); Gl: = New(PGroup, Init(R)); R.Grow(-10, -3); G2: = New(PGroup, Init(R)); G1.Insert(G2); Dispose(G1, Done); Dispose(G2, Done) {Здесь программа "зависнет"!} end.
Заметим, что перестановка операторов Dispose местами приводит к корректному варианту, т.к. метод G1.Done умеет контролировать освобождение своего подэлемента G2 и не освобождает его вторично. Во всех случаях оператор Dispose (G2, Done) излишен: освобождение группы вызывает автоматическое освобождение всех ее подэлементов.
Поскольку динамическая память используется в Turbo Vision очень интенсивно, полезно предусмотреть в отладочном варианте программы визуализацию ее размера. Для этого можно использовать такой объект THeapView:
Unit HeapView; Interface Uses Dialogs,Objects; type PHeapView = THeapView; THeapView = object(TStaticText) Constructor Init(var R: TRect); Procedure Update; end; Implementation Constructor THeapView.Init; var S: String; begin Str(MemAvail,S); Inherited lnit(R,#3+S) end; Procedure THeapView.Update; var S: String; begin Str(MemAvail,S); DisposeStr(Text); Text: = NewStr(#3+S); Draw end; end.