Delphi - Как мне сломаться, когда ComponentCount формы уменьшается

Код ниже воспроизведен из Toolbar2000. Это часть подпрограммы, которая считывает позиции панели инструментов и состояния док-станции из INI-файла. Я вызываю эту процедуру во время инициализации. Этот код ниже перебирает все компоненты в главной форме (OwnerComponent) и загружает настройки всех найденных панелей инструментов.

for I := 0 to OwnerComponent.ComponentCount-1 do begin
  ToolWindow := OwnerComponent.Components[I];  //  <------------------------
....

Эта итерация занимает некоторое время (секунды - в форме 1500 с лишним компонентов), и я получаю ошибку диапазона в показанной точке. Я установил, что один или несколько элементов удаляются из компонентов основной формы во время выполнения этого цикла, поэтому в конечном итоге цикл пытается получить доступ к одному за концом массива, как только это произошло (предположительно, было бы лучше закодировать это как "вниз" для цикла, чтобы предотвратить это).

В любом случае, мне нужно выяснить, где основная форма теряет компонент. Может ли кто-нибудь дать мне какие-либо советы по отладке Delphi 2006 о том, как это сделать? Я бы не ожидал, что какие-либо основные компоненты формы будут освобождены на этом этапе моей программы.

ОБНОВЛЕНИЕ

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

Dock 
  Toolbar 1
    Control 1
    Control 2
    Toolbar 2
      Control 3
      Control 4

и исправление заключалось в том, чтобы расположить их следующим образом:

Dock 
  Toolbar 1
    Control 1
    Control 2
  Toolbar 2
    Control 3
    Control 4

Это по-прежнему указывает на ошибку в коде TB2k — можно было бы предположить, что он должен иметь возможность обрабатывать вложенные панели инструментов.


person rossmcm    schedule 28.04.2011    source источник
comment
Это не кажется нормальным поведением, если только время не выбрано неправильно (сам владелец все еще находится в процессе создания). Когда/где именно вы загружаете настройки для своих панелей инструментов?   -  person Andriy M    schedule 28.04.2011
comment
Используйте цикл for только в том случае, если количество элементов фиксировано до начала итерации. Используйте while или repeat, если вы не знаете, сколько итераций потребуется циклу. Repeat проверки условий после каждой итерации (а вы знаете, что это как минимум одна итерация) и while проверки условий перед каждой итерацией (это может быть вообще без итерации).   -  person Jørn E. Angeltveit    schedule 28.04.2011
comment
@Andy - вызов загрузки настроек файла INI происходит из процедуры инициализации, которая, в свою очередь, вызывается из DPR (основной программы) непосредственно перед Application.Run, после того, как все формы были созданы. Даже если основная форма еще не закончила создание, я бы не ожидал, что количество компонентов уменьшится   -  person rossmcm    schedule 28.04.2011
comment
@ Jørn - (код принадлежит Джордану Расселу). Я думаю, он ожидал, что список компонентов владельца будет постоянным по размеру на протяжении всего цикла. Если бы это было так, то цикл for...to был бы уместен. В общем случае, когда что-то может войти и удалить элемент из списка во время выполнения цикла (похоже, что это должно происходить здесь), я думаю, вы не можете не можете защитить доступ к списку компонентов, кроме как с помощью явная проверка перед каждой ссылкой.   -  person rossmcm    schedule 28.04.2011
comment
@Андрей, я ошибся в твоем имени...   -  person rossmcm    schedule 28.04.2011
comment
@rossmcm: Что ж, мне это кажется прекрасным местом, я бы тоже не ожидал, что количество изменится. Пока что это, кажется, за пределами моего уровня знаний, извините.   -  person Andriy M    schedule 28.04.2011


Ответы (2)


В дополнение к ответу Ливена вы также можете использовать отладку dcu и установить точку останова в TComponent.Destroy непосредственно перед входом в цикл.

В обоих случаях вам нужно будет изучить стек вызовов, чтобы увидеть, откуда исходит вызов/изменение счетчика.

Кэри Дженсен написал очень интересную статью о контрольных точках: http://caryjensen.blogspot.com/2010/08/breakpoints-with-side-effects.html

person Marjan Venema    schedule 28.04.2011
comment
Еще лучше: установите точку останова в TComponent.Notification; это отследит ошибку, если компонент не уничтожен, а только удален из списка компонентов основной формы. - person Ritsaert Hornstra; 28.04.2011
comment
и @Lieven. На основании ваших комментариев я поставил точку останова в начале TComponent.Remove непосредственно перед входом в цикл, и он сработал. Теперь осталось выяснить, почему код Джордана удаляет компоненты при чтении настроек. Я думаю, что это связано с док-станцией, но я пока не вижу... - person rossmcm; 28.04.2011
comment
@rossmcm - хорошо найдено, вы можете сообщить об этом Джордану Расселу из TB2K. - person Marjan Venema; 02.05.2011

Вам нужно будет добавить точку останова данных в @Self.FComponents.FCount, чтобы она прерывалась при каждом изменении счетчика. .

  • ComponentCount — это свойство, которое возвращает значение из GetComponentCount.
  • GetComponentCount возвращает FComponents.Count.
  • FComponents — это экземпляр TList, который имеет частную переменную FCount.
person Lieven Keersmaekers    schedule 28.04.2011