Отладка программ

Если Вы пытались отлаживать какую-либо программу в 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 вставляется в начало строки сообщения в том случае, когда требуется центрировать эту строку (расположить ее симметрично относительно границ окна сообщения).