Модераторы: Poseidon, Snowy, bems, MetalFan
  

Поиск:

Ответ в темуСоздание новой темы Создание опроса
> Access Violation с TObjectList<T> 
V
    Опции темы
Beltar
Дата 1.4.2013, 15:53 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 627
Регистрация: 11.1.2006

Репутация: 3
Всего: 7



Delphi XE3

Есть такой класс:

Код

  TFormator=class
    private
      FOwner:TFormList;
      function Q:TADOQuery;
    public
      Id:Integer;
      Id_Type:Integer;
      Id_Div:Integer;
      Row:Integer;
      Num:Integer;
      IP:String;
      Status:Integer;
      DeletedDate:TDate;
      DblField1:Double;
      DblField2:Double;
      DblField3:Double;
      IntFields:array[1..12] of Integer;
      constructor Create(AOwner:TFormList);
      destructor Destroy;override;
      //Разные методы до кучи
  end;


В общем ничего особенного не содержит кроме ссылки на владельца.

И есть контейнер.

Код

  TFormList=class (TObjectList<TFormator>)
    private
    public
      Q:TADOQuery;
      procedure Fill(Q:TADOQuery; Deleted:Boolean);
      constructor Create(AQuery:TADOQuery=nil);
      destructor Destroy;override;
      //Разного рода методы
  end;



Контейнеры являются глобальными (их 2 шт) и создаются всего 1 раз.

Код

FVS:=TFormList.Create(Q);
FVS.OwnsObjects:=True;


Для заполнения контейнеров есть ф-ия. Больше нигде добавление, или исключение объектов не производится.

Код

procedure TFormList.Fill(Q: TADOQuery; Deleted: Boolean);
var i:Integer;
    Del:Boolean;
    F:TFormator;
begin
Clear;
Q.First;
for i:=0 to Q.RecordCount-1 do
  begin
  Del:=Q.Fields[6].Value<>NULL;
  if Del=Deleted then
    begin
    F:=TFormator.Create(Self);
    //Просто заполнили поля
    F.Id:=Q.Fields[0].AsInteger;
    F.Id_Div:=Q.Fields[1].AsInteger;
    F.Id_Type:=Q.Fields[2].AsInteger;
    F.Row:=Q.Fields[3].AsInteger;
    F.Num:=Q.Fields[4].AsInteger;
    F.IP:=Q.Fields[5].AsString;
    if Del then F.DeletedDate:=Q.Fields[6].AsDateTime
      else F.DeletedDate:=0;
    //Засунули в контейнер, больше ничего интересного не делаем
    Add(F);
    end;
  Q.Next;
  end;
end;



Если контейнер не пустой, то на вызове Clear происходит AV по нулевому адресу. Значение OwnsObjects, или предварительное уничтожение объектов в контейнере роли не играют, до их деструктора просто не доходит. Аналогичная проблема происходит при попытке уничтожения контейнера. Если включить debug dcu's, то все падает в

Код

procedure TList<T>.DeleteRange(AIndex, ACount: Integer);
var
  oldItems: array of T;
  tailCount, I: Integer;
begin
  if (AIndex < 0) or (ACount < 0) or (AIndex + ACount > Count)
    or (AIndex + ACount < 0) then
    raise EArgumentOutOfRangeException.CreateRes(@SArgumentOutOfRange);
  if ACount = 0 then
    Exit;
  
  SetLength(oldItems, ACount);
  FArrayManager.Move(FItems, oldItems, AIndex, 0, ACount);{На вот этой строке}

  tailCount := Count - (AIndex + ACount);
  if tailCount > 0 then
  begin
    FArrayManager.Move(FItems, AIndex + ACount, AIndex, tailCount);
    FArrayManager.Finalize(FItems, Count - ACount, ACount);
  end else
    FArrayManager.Finalize(FItems, AIndex, ACount);

  Dec(FCount, ACount);

  for I := 0 to Length(oldItems) - 1 do
    Notify(oldItems[I], cnRemoved);
end;


Проблему можно обойти, если заменить вызов Clear на
Код

for i:=Count-1 downto 0 do Delete(i);


Однако это не отменяет вопроса, что я делаю не так. Вопрос, как подойти к проблеме, судя по нулевому адресу похоже на обращение к несуществующему объекту, но как этот вызов отловить? Просмотр ассемблерного кода в FArrayManager.Move не много дал, хотя скажем прямо с асмом мне работать не приходилось.


--------------------
Опытный программист на C++ легко решает любые не существующие в Паскале проблемы. smile(с) я, хотя может и нет
Пищущий на C++ мужик. Даже если это мужик сидит в написанном на Delphi и жрущем паскалевскую библиотеку билдере.
PM MAIL   Вверх
Akella
Дата 1.4.2013, 21:58 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Творец
****


Профиль
Группа: Модератор
Сообщений: 18485
Регистрация: 14.5.2003
Где: Корусант

Репутация: 36
Всего: 329



может заменить
Код

begin
Clear;
Q.First;


на

Цитата

begin
Q.Clear;
Q.First;

PM MAIL   Вверх
Beltar
Дата 1.4.2013, 22:43 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 627
Регистрация: 11.1.2006

Репутация: 3
Всего: 7



А что это даст, если вообще скомпилируется (У TADOQuery вроде Clear нету)? Все, что я делаю, очищаю список и заново заполняю его данными из набора данных Q.


--------------------
Опытный программист на C++ легко решает любые не существующие в Паскале проблемы. smile(с) я, хотя может и нет
Пищущий на C++ мужик. Даже если это мужик сидит в написанном на Delphi и жрущем паскалевскую библиотеку билдере.
PM MAIL   Вверх
Akella
Дата 1.4.2013, 23:19 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Творец
****


Профиль
Группа: Модератор
Сообщений: 18485
Регистрация: 14.5.2003
Где: Корусант

Репутация: 36
Всего: 329



Вот я и не понял, к чему относится  Clear.
PM MAIL   Вверх
БелАмор
Дата 1.4.2013, 23:48 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Бывалый
*


Профиль
Группа: Участник
Сообщений: 209
Регистрация: 10.6.2010
Где: Россия

Репутация: 5
Всего: 17



FArrayManager = nil ?

PM   Вверх
Beltar
Дата 2.4.2013, 12:43 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 627
Регистрация: 11.1.2006

Репутация: 3
Всего: 7



Да, nil. И какой из этого вывод?

Ради интереса попробовал юнит с этими классами подключить к чистому проекту, где ничего нет кроме

Код

procedure TForm1.Button1Click(Sender: TObject);
begin
FVS.Clear;
end;

procedure TForm1.FormCreate(Sender: TObject);
var i:Integer;
    F:TFormator;
begin
FVS:=TFormList.Create(nil);
for i:=0 to 5 do
  begin
  F:=TFormator.Create(FVS);
  FVS.Add(F);
  end;
end;


На FVS.Clear точно такое же AV.

Это сообщение отредактировал(а) Beltar - 2.4.2013, 13:17


--------------------
Опытный программист на C++ легко решает любые не существующие в Паскале проблемы. smile(с) я, хотя может и нет
Пищущий на C++ мужик. Даже если это мужик сидит в написанном на Delphi и жрущем паскалевскую библиотеку билдере.
PM MAIL   Вверх
БелАмор
Дата 2.4.2013, 15:39 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Бывалый
*


Профиль
Группа: Участник
Сообщений: 209
Регистрация: 10.6.2010
Где: Россия

Репутация: 5
Всего: 17



Цитата(Beltar @  2.4.2013,  13:43 Найти цитируемый пост)
Да, nil. И какой из этого вывод?


Хм... Странно...
Я думал, намёка будет достаточно...
Тогда отвечу развёрнуто.

Я не работал с XE3 и не знаю, что это за класс TArrayManager, хотя и могу догадываться.
Однако, судя по всему, FArrayManager является переменной именного этого класса, а судя по префиксу F - это не переменнная, а поле вашего класса. FArrayManager - указатель, который должен указывать на реально существующий объект. Либо вы присваиваете ссылку на уже существующий объект, либо должны создать его сами, например, в собственном конструкторе. Если вы этого не сделали, то в FArrayManager имеется nil, что является ошибкой и при попытке обращения к объекту в большинстве случаев вы получите AV. Что вы и имеете в наличии.
PM   Вверх
Beltar
Дата 2.4.2013, 19:04 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 627
Регистрация: 11.1.2006

Репутация: 3
Всего: 7



Нет, это поле класса TList<T>, т. е. кишки, которые я, как пользователь VCL, не трогаю. И это самое странное, если бы я сам где-то раньше времени свой объект убил, или не создал, и ошибка была в моем коде, то 99% проблема бы не прожила и 15 минут после обнаружения.
Есть некоторое подозрение на баг в VCL, тем более что в Delphi XE метод TList<T>.DeleteRange выглядит по другому:

Код

procedure TList<T>.DeleteRange(AIndex, ACount: Integer);
var
  oldItems: array of T;
  tailCount, i: Integer;
begin
  if (AIndex < 0) or (ACount < 0) or (AIndex + ACount > Count)
    or (AIndex + ACount < 0) then
    raise EArgumentOutOfRangeException.CreateRes(@SArgumentOutOfRange);
  if ACount = 0 then
    Exit;
  
  SetLength(oldItems, ACount);
  System.Move(FItems[AIndex], oldItems[0], ACount * SizeOf(T));
  
  tailCount := Count - (AIndex + ACount);
  if tailCount > 0 then
  begin
    System.Move(FItems[AIndex + ACount], FItems[AIndex], tailCount * SizeOf(T));
    FillChar(FItems[Count - ACount], ACount * SizeOf(T), 0);
  end
  else
  begin
    FillChar(FItems[AIndex], ACount * SizeOf(T), 0);
  end;
  Dec(FCount, ACount);
  
  for i := 0 to Length(oldItems) - 1 do
    Notify(oldItems[i], cnRemoved);
end;


Но это, крайне маловероятно. Все-таки ошибка в TList затронет 95%+ программ.
Проект изначально был начат именно в XE, но я не помню, покрывался ли при тестировании сбойный участок. Сам по себе этот участок вызывается один раз при запуске программы, где контейнер пустой и все проходит нормально и в дальнейшем может быть вызван лишь при изменении настроек, что происходит не часто.

Пока буду экспериментировать.


--------------------
Опытный программист на C++ легко решает любые не существующие в Паскале проблемы. smile(с) я, хотя может и нет
Пищущий на C++ мужик. Даже если это мужик сидит в написанном на Delphi и жрущем паскалевскую библиотеку билдере.
PM MAIL   Вверх
Beltar
Дата 2.4.2013, 19:41 (ссылка) |    (голосов:1) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 627
Регистрация: 11.1.2006

Репутация: 3
Всего: 7



Вопрос решен, как обычно, виной всему невнимательность.

Код

constructor TFormList.Create(AQuery: TADOQuery);
begin
inherited Create;//Было пропущено, логично, что внутренние поля не создавались.
Q:=AQuery;
end;


Всем спс за участие.


--------------------
Опытный программист на C++ легко решает любые не существующие в Паскале проблемы. smile(с) я, хотя может и нет
Пищущий на C++ мужик. Даже если это мужик сидит в написанном на Delphi и жрущем паскалевскую библиотеку билдере.
PM MAIL   Вверх
  
Ответ в темуСоздание новой темы Создание опроса
Правила форума "Delphi: Общие вопросы"
SnowyMetalFan
bemsPoseidon
Rrader

Запрещается!

1. Публиковать ссылки на вскрытые компоненты

2. Обсуждать взлом компонентов и делиться вскрытыми компонентами

  • Литературу по Дельфи обсуждаем здесь
  • Действия модераторов можно обсудить здесь
  • С просьбами о написании курсовой, реферата и т.п. обращаться сюда
  • Вопросы по реализации алгоритмов рассматриваются здесь
  • 90% ответов на свои вопросы можно найти в DRKB (Delphi Russian Knowledge Base) - крупнейшем в рунете сборнике материалов по Дельфи


Если Вам понравилась атмосфера форума, заходите к нам чаще! С уважением, Snowy, MetalFan, bems, Poseidon, Rrader.

 
1 Пользователей читают эту тему (1 Гостей и 0 Скрытых Пользователей)
0 Пользователей:
« Предыдущая тема | Delphi: Общие вопросы | Следующая тема »


 




[ Время генерации скрипта: 0.0997 ]   [ Использовано запросов: 21 ]   [ GZIP включён ]


Реклама на сайте     Информационное спонсорство

 
По вопросам размещения рекламы пишите на vladimir(sobaka)vingrad.ru
Отказ от ответственности     Powered by Invision Power Board(R) 1.3 © 2003  IPS, Inc.