![]() |
| ||
|
|
Новости |
Поиск |
FAQ |
Статьи |
Книги |
Новости VCL
| | Форумы ЧАТ |
Орешник |
|
| |||
| |||
« предыдущая ветвь | форум | следующая ветвь »
Динамические объекты [D6, Win2k]
Turalyon © (2002-01-15 09:44:00)Привет, Всем.
Есть такая проблемма при работе с динамическими объектами. Есть некая форма на которой в зависимости от действий пользователя создаются несколько кнопочек (TSpeedButton для удобства). Прежде чем их создавать я уничтожаю уже созданные ранее делаю все примерно так.
NumButton - количество созданых кнопок.
NewNumButton - колчество кнопок, которые надо сделать.
Var
BA : array [1..50] of TSpeedButton;
...
for i := 1 to NumButton do (BA[i] as TSpeedButton).Free;
...
for i := 1 to NewNumButton do
begin
BA[i] := TSpeedButton.Create(nil)
...
BA[i].Parent := Form1;
end;
Так вот проблемма в том что прога без конца вылетает с "Access violation at adress ...". Причем без какой либо видимой закономерности... то все делает нормально, то глючит. (Хотя небольшая есть - если новое количество кнопок меньше старого то вероятность больше). Если убрать сточку где удаляются старые кнопки - то все работает. Помогите плз...
Turalyon (2002-01-16 11:26:00) [1]Спасибо всем откликнувшимся за помощь, особенно Alx2 ;)
Digitman (2002-01-15 15:31:00) [2]>Turalyon
"На выходе" - это надо понимать - при выполнениии в процессе трассировки оперетора END, завершающего процедуру ? Вопрос -какой процедуры ? В этом случае практически 100%-но можно утверждать, что процедура портит либо стек либо область памяти, ей не принадлежащей по логике.
Воспользуйся советом от <Владислав>, это - основополагающий элемент умения пользоваться отладчиком для поиска простых лог.ошибок
Alx2 (2002-01-15 14:48:00) [3]> Turalyon ©
Так мы, кажется, и не двинулись с мертвой точки...
Намыль мне unit. Покажу на подозрительные места, если что...
Владислав (2002-01-15 14:39:00) [4]Определяй где ошибка. Меню Search->Find Error...
panov (2002-01-15 14:38:00) [5]У тебя в коде написано ButtonArray[i] := TSpeedButton.Create(Self);
Это так и нужно?
После этой строки владельцем кнопки будет форма.
А если они у тебя динамически создаются и уничтожаются, то, мне кажется, лучше все-таки TSpeedButton.Create(nil);
Turalyon (2002-01-15 14:33:00) [6]>Digitman
Я обчно пользуюсь Watch-ем Trace, ... и т.д.
Но вся проблемма что Trace проходит все нормаьлно и ошибки выдает уже не на выходе....
Как определить ошбку по адресу? Подскажите пожалуйста.
Я переделал все немного, и начал делать так, как написал GorA (без массива), но прога все равно вываливается. А удалял кнопки методом GroupBox2.DestroyComponents , т.к. кроме временных кнопок там больше ничего нет.
Alx2 (2002-01-15 12:42:00) [7]>Digitman
Конечно, локализуется. Но в том то и петрушка, чтобы вычислить причину AV "на расстоянии", по методу "вопрос-ответ", как в этом случае.
Digitman (2002-01-15 12:32:00) [8]>Alx2
Это из той же оперы - тот же AV. Локализуется точно так же.
Alx2 (2002-01-15 12:29:00) [9]>Digitman
происходят именно в момент выполнения ButtonArray[i].Free в момент, когда ButtonArray[i] <> nil и указывает на несуществующий уже (или - еще) объект.
Ага. Но такое же может случиться при записи в ButtonArray[51], например, при отключенном Range Checking.
Digitman (2002-01-15 12:23:00) [10]>Turalyon (15.01.02 12:02)
Как бы там ни было, но исключения AV, которые ты получаешь, происходят именно в момент выполнения ButtonArray[i].Free в момент, когда ButtonArray[i] <> nil и указывает на несуществующий уже (или - еще) объект. И это выясняется очень просто установкой breakpoint"а на эту строчку (либо использовав адрес, который сопровождает возникшее исключение, для локализации в тексте проблемной строчки). То, что ты до сих пор мучаешься в поисках источника такой простейшей проблемы, лишний раз должно заставить тебя изучить и задействовать всю мощь интегрированного отладчика Делфи. И неважно при этом, какие там методы распределения памяти используешь - динамические или статические : дебаггер одинаково хорошо позволяет локализовать подобные источники ошибок в том и другом случаях.
Turalyon (2002-01-15 12:04:00) [11]2 GorA
Сейчас попробую...
Turalyon (2002-01-15 12:02:00) [12]Дело в том что при первом запуске DM.EAnswerQuery.State стабильно равна dsInactive и первый раз цикл выполнятся не должон.
Alx2 (2002-01-15 11:58:00) [13]Согласен с GorA.
Возвращаесь к конкретной задаче:
По поводу ButtonArray:
1. Сначала надо занулить его (см Digitman)
2. Где гарантия, что 0<AnswNum<51?
GorA (2002-01-15 11:54:00) [14]попробуй создавать кнопки так:
while not DM.EAnswerQuery.Eof do
begin
with TSpeedButton.Create(GroupBox2) do
begin
Caption := ...;
......
Paren := GroupBox2;
end;
DM.EAnswerQuery.Next
end;
а удалять вот так:
for i = GroupBox2.ComponentCount-1 downto 0 do
if GroupBox2.Components[i] is TSpeedButton then
GroupBox2.Components[i].Free;
и не нужны тебе никакие массивы
Владислав (2002-01-15 11:53:00) [15]> Turalyon © (15.01.02 10:58)
Подробнее см.:
Владислав © (15.01.02 10:24)
Владислав © (15.01.02 10:27)
Задавай все свойства в этом коде.
Там где ты создаешь компоненты (TSpeedButton), вставь код, который я назвал "создание". Там где уничтожаешь - "уничтожение".
Что не понятно? Конкретнее.
Turalyon (2002-01-15 11:53:00) [16]Var
ButtonArray : array[1..50] of TSpeedButton;
AnswNum - глобальная переменная, нигде кроме этой процедуры не используется, задается перед началом инициализации новых кнопочек.
Digitman (2002-01-15 11:50:00) [17]Где у тебя код инициализации глобального массива ButtonArray ?
Перед первым использованием все его элементы д.б. сброшены в nil.
AV возникает у тебя при выполнении строчки
if Assigned(ButtonArray[i]) then ButtonArray[i].Free;
потому как массив, скорее всего, у тебя распределен статически, а компилятор не отвечает за инициализацию стат.массивов.
Ты же , видимо, ошибочно предполагаешь, что при первом выполнении проверки Assigned(ButtonArray[i]) она обязательно даст False, поскольку кнопок на этот момент ты никаких еще не создавал (и, соответственно, Free не будет выполняться).
Но Free может в какой-то момент и выполнится, потому что содержимое явно неинициализированного стат.распр.массива попросту неопределено (нет никакой гарантии, что все его элементы - nil)
Правильней (и красивей, imho) надо бы "нарисовать" так :
код предв.инициализации массива (его можно разместить, к примеру, в разделе иниц-ции модуля формы) :
for i := Low(ButtonArray) to High(ButtonArray) do
ButtonArray[i]:= nil;
и тогда цикл гарантированного разрушения потенциально созданных объектов в массиве будет выглядеть так:
if DM.EAnswerQuery.State = dsBrowse then
for i := 1 to AnswNum do
// уничтожить обьъект, если он на этот момент существует,
// и "убрать за собой мусор" во избежание последующих AV,
FreeAndNil(ButtonArray[i]);
Alx2 (2002-01-15 11:42:00) [18]Описание/инициализация массива ButtonArray нужны.
На всякий случай: AnswNum всегда совпадает при создании/удалении кнопочек?
Turalyon (2002-01-15 11:22:00) [19]ОК.
Вот процедура в которй все варится
procedure TEkzamForm.UpdateAnswer;
var Answ :array [1..2] of array of integer;
CountP, k,i : integer;
SetCif : set of 1..255;
tmpStr : String;
begin
DM.EAnswerQuery.DisableControls;
if DM.EAnswerQuery.State = dsBrowse then
for i := 1 to AnswNum do
begin
if Assigned(ButtonArray[i]) then ButtonArray[i].Free;
ButtonArray[i] := nil;
// ButtonArray[i].Free;
end;
В общем вот весь процесс удаления кнопок, проверка стоит на случай первого токрытия формы, тогда массив еще пустой, ButtonArray - глобальный массив
все что идет дальше - динамичесое построение SQL запроса.
DM.EAnswerQuery.SQL.Clear;
DM.EAnswerQuery.SQL.Add("select * from Answer");
DM.EAnswerQuery.SQL.Add("where A_Quest = " + DM.EQuestQuery.FieldByName("Q_id").AsString);
if Options.CheckBox3.Checked then
begin
SetCif := [];
CountP := 0;
SetLength(Answ[1], DM.EAnswerQuery.RecordCount);
SetLength(Answ[2], DM.EAnswerQuery.RecordCount);
repeat
k := random( length(Answ[1]) + 1);
if(not (k in SetCif))and(k <> 0) then
begin
SetCif:=SetCif +[k];
Answ[2,CountP] := k;
inc(CountP);
end;
until CountP = length(Answ[1]);
for k := 0 to length(Answ[1])-1 do
begin
DM.EAnswerQuery.RecNo := Answ[2,k];
Answ[1,k] := DM.EAnswerQuery.FieldByName("A_id").AsInteger;
end;
DM.EAnswerQuery.Close;
DM.EAnswerQuery.SQL.Clear;
DM.EAnswerQuery.SQL.Add("select * from Answer");
DM.EAnswerQuery.SQL.Add("where A_Quest = " + DM.EQuestQuery.FieldByName("Q_id").AsString);
tmpStr := "and A_id in (";
i:=0;
repeat
tmpStr := tmpStr + inttostr(Answ[1,i])+",";
inc(i);
until i=length(Answ[1])-1;
tmpStr := tmpStr + inttostr(Answ[1,i])+")";
DM.EAnswerQuery.SQL.Add(tmpStr);
end;
DM.EAnswerQuery.Open;
А здесь начинается создание новых кнопочек
AnswNum := DM.EAnswerQuery.RecordCount;
for i := 1 to AnswNum do
begin
ButtonArray[i] := TSpeedButton.Create(Self);
ButtonArray[i].Caption := inttostr(i);
ButtonArray[i].Width := 40;
ButtonArray[i].Height := 25;
ButtonArray[i].Left := 1 + 40*i ;
ButtonArray[i].Top := 18;
ButtonArray[i].Parent := GroupBox2;
ButtonArray[i].OnClick := OnMyButtonClick;
if Options.ComboBox2.ItemIndex = 1 then
begin
Memo1.Lines.Add("");
Memo1.Lines.Add("
Alx2 (2002-01-15 11:12:00) [20]>Владислав ©
Конечно. Но все дело в специфике задачи. Поэтому надо код...
Иначе будем гадать по методу черного ящика. Remote debug, так сказать...