Отладка программ
Если Вы пытались отлаживать какую-либо программу в 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.
Например, в следующей программе показан способ включения контрольного окна, создаваемого в этом объекте, в верхний правый угол экрана:
Uses Objects,Views,App, HeapView;
var
H: PHeapView;{Окно для MemAvail}
W: PWindow;
G: PGroup;
R: TRect;
P: TApplication;{Стандартная программа}
begin
P.Init;
R.Assign(70,0,80,1);{Верхний правый угол}
New(H, Init(R));{Создаем окно контроля}
P.Insert(H);{Помещаем его на экран}
ReadLn; {Пауза - показываем начальный размер кучи}
R.Assign(10,5,70,20);
W := New(PWindow,Init(R,'',0)); {Создаем окно}
R.Assign(5,3,55,12);
G := New(PGroup, Init(R));
W.Insert(G); {Вставляем в окно группу}
DeskTop.Insert(W); {Выводим на экран}
Н.Update; {Обновляем окно контроля}
ReadLn; {Пауза - размер кучи перед освобождением}
Dispose(W, Done); {Освобождаем окно и группу}
НА.Update; {Обновляем окно контроля}
ReadLn; {Пауза - размер после освобождения}
Р.Done
end.
Для получения текущего значения общего размера кучи используется вызов метода THeapView.Update в нужных местах программы. Вы можете автоматизировать обновление окна контроля, если включите вызов Update в перекрываемый метод TProgramIdle. В следующем варианте показан способ отображения MemAvail в фоновом режиме. Кроме того, в программе иллюстрируется возможное использование функции MessageBox.
{$Х+} {Используется расширенный синтаксис вызова функции MessageBox}
Uses Objects,Views,App,HeapView,MsgBox;
type
MyApp = object (TApplication)
Procedure Idle; Virtual;
end;
var
H: PHeapView;
Procedure MyApp.Idle;
begin
H^.Update
end;
var
W: PWindow;
G: PGroup;
R: TRect;
P: MyApp;
begin
P.Init;
R.Assign(70,0,80,1);
New(H,Init(R));
P.Insert(H);
MessageBox(#3'Размер кучи до размещения',NIL,0);
R.Assign(10,5,70,20) ;
W := New(PWindow, Init(R,'',0));
R.Assign(5,3,55,12) ;
G := New(PGroup, Init(R));
WA.lnsert(G);
DeskTop.Insert(W);
MessageBox(#3'Размер кучи после размещения', NIL,0);
Dispose(W, Done);
MessageBox(#3'Размер кучи после освобождения', NIL,0);
Р.Done
end.
Константа #3 вставляется в начало строки сообщения в том случае, когда требуется центрировать эту строку (расположить ее симметрично относительно границ окна сообщения).