Форум по Delphi программированию

Delphi Sources



Вернуться   Форум по Delphi программированию > Все о Delphi > [ "Начинающим" ]
Ник
Пароль
Регистрация <<         Правила форума         >> FAQ Пользователи Календарь Поиск Сообщения за сегодня Все разделы прочитаны

Ответ
 
Опции темы Поиск в этой теме Опции просмотра
  #1  
Старый 30.04.2018, 12:19
Bosa Bosa вне форума
Прохожий
 
Регистрация: 30.04.2018
Сообщения: 5
Версия Delphi: Delphi 7
Репутация: 10
По умолчанию Ошибка EAccess Violation при удалении элементов

Здравствуйте!
Никак не могу понять почему вываливается ошибка доступа к памяти. Пользователь динамически создаёт список запрещённых частот, при удалении уже созданной из списка появляется ошибка. Частоты хранятся в динамическом массиве freq_array.
Помогите пожалуйста!
Код:
unit Unit2;

interface

uses
  Unit1, Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, ExtCtrls;

type
  TForm2 = class(TForm)
    Timer1: TTimer;
    GroupBox1: TGroupBox;
    GroupBox2: TGroupBox;
    Label1: TLabel;
    Edit1: TEdit;
    Label3: TLabel;
    GroupBox3: TGroupBox;
    Button3: TButton;
    Button1: TButton;
    Button4: TButton;

    procedure Edit1KeyPress(Sender: TObject; var Key: Char);
    procedure Timer1Timer(Sender: TObject);
    procedure ShowList;
    procedure ListButtonClick(Sender: TObject);
    procedure FormCreate(Sender: TObject);

  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form2: TForm2;
  lab_empty: TLabel;

implementation

{$R *.dfm}

procedure TForm2.ListButtonClick(Sender: TObject);
  var l_array, Index : integer;

begin
  Index:= StrToInt(TComponent(Sender).Name[Length(TComponent(Sender).Name)])-1;
  l_array:= Length(freq_array);
  {$HINTS OFF} Finalize(freq_array[Index]); {$HINTS ON}
  if (l_array = 1) then
  begin
    SetLength(freq_array, 0);
  end
  else
  begin
    freq_array[Index]:= freq_array[l_array-1];
    SetLength(freq_array, l_array-1);
  end;
  ShowList;
end;


procedure TForm2.Edit1KeyPress(Sender: TObject; var Key: Char);
  var
  a: Extended;
  i, k: Integer;
  freq_povtor: Boolean;
  s, h: String;

begin
  if not (Key in ['0'..'9', #8, #13, '.', ',']) then Key:=#0
  else
  if (Key = '.') then Key:=','
  else
  if (Key = #13) then
  begin
    h:= Edit1.Text;
    if (Length(h)>10) then
      begin
        Label1.Font.Size:=12;
        Label1.Font.Color:=clRed;
        Label1.Top:= 36;
        Label1.Height:= 24;
        Label1.Caption:= 'Некорректное значение!';
        Edit1.Clear;
        Timer1.Enabled:=True;
      end
    else
    begin
      s:= '';
      for i:=1 To Length(h) Do
      begin
        if (h[i]='.') then s:=s+','
        else s:=s+h[i];
      end;
      if TryStrToFloat(s,a) then
        begin
          if (a<149) Or (a>150) And (a<173) Or (a>174) then
            begin
              Label1.Font.Size:=12;
              Label1.Font.Color:=clRed;
              Label1.Top:= 36;
              Label1.Height:= 24;
              Label1.Caption:= 'Неправильный диапазон!';
              Edit1.Clear;
              Timer1.Enabled:=True;
            end
          else
            begin
              freq_povtor:=False;
              for k:=0 to length(freq_array)-1 do
              begin
                if (freq_array[k] = a) then freq_povtor:=True;
              end;
              if (freq_povtor=True) then
                begin
                  Label1.Font.Size:=12;
                  Label1.Font.Color:=clRed;
                  Label1.Top:= 36;
                  Label1.Height:= 24;
                  Label1.Caption:= 'Частота уже есть в списке!';
                  Edit1.Clear;
                  Timer1.Enabled:=True;
                end
              else
                begin
                  Label1.Font.Size:=12;
                  Label1.Font.Color:=clGreen;
                  Label1.Top:= 36;
                  Label1.Height:= 24;
                  Label1.Caption:= 'Ввод успешен!';
                  SetLength(freq_array,length(freq_array)+1);
                  freq_array[length(freq_array)-1]:= a;
                  ShowList;
                  Edit1.Clear;
                  Timer1.Enabled:=True;
                end
            end
        end
      else
      begin
        Label1.Font.Size:=12;
        Label1.Font.Color:=clRed;
        Label1.Top:= 36;
        Label1.Height:= 24;
        Label1.Caption:= 'Некорректное значение!';
        Edit1.Clear;
        Timer1.Enabled:=True;
      end;
    end;
  end;
end;


procedure TForm2.Timer1Timer(Sender: TObject);
begin
  Timer1.Enabled:=False;
  Label1.Font.Size:=10;
  Label1.Font.Color:=clBlack;
  Label1.Top:= 29;
  Label1.Height:= 36;
  Label1.Caption:= 'Введите частоту в МГц в диапазоне 149...150 МГц или 173...174 МГц и нажмите клавишу "Enter"';
end;

procedure TForm2.ShowList;
var
  m, t: integer;
  lab_empty, lab_freq, lab_ce, lab_msg: TLabel;
  edit_freq: TEdit;
  but_freq: TButton;

begin
  //Form1.Label3.Caption:= IntToStr(Length(list_array));
  for t:= Form2.ComponentCount-1 downto 0 do
  begin
    if (Form2.Components[t].GetParentComponent=GroupBox1) then
       Form2.Components[t].Free;
  end;

  if (Length(freq_array)=0) then
    begin
      lab_empty:= TLabel.Create(Form2);
      lab_empty.Parent:= Form2.GroupBox1;
      lab_empty.Top:= 72;
      lab_empty.Left:= 216;
      lab_empty.Width:= 85;
      lab_empty.Height:= 19;
      lab_empty.Font.Color:= clMaroon;
      lab_empty.Font.Size:= 12;
      lab_empty.Font.Name:= 'Calibri';
      lab_empty.Caption:= 'Список пуст';
      Form2.GroupBox1.Height:= 110;
    end
  else
  begin
    Form2.GroupBox1.Height:= 110 + 35*(length(freq_array)-1);
    for m:=0 to length(freq_array)-1 do
    begin
      lab_freq:= TLabel.Create(Form2);
      lab_freq.Parent:= Form2.GroupBox1;
      lab_freq.Top:= 73+m*35;
      lab_freq.Left:= 17;
      lab_freq.Width:= 36;
      lab_freq.Alignment:= taRightJustify;
      lab_freq.Font.Color:= clBlack;
      lab_freq.Caption:= 'f'+IntToStr(m+1)+' =';
      lab_freq.Name:='el_list1_'+ IntToStr(m+1);

      edit_freq:= TEdit.Create(Form2);
      edit_freq.Parent:= Form2.GroupBox1;
      edit_freq.Top:= 70+m*35;
      edit_freq.Left:= 59;
      edit_freq.Width:= 83;
      edit_freq.Font.Color:= clBlack;
      edit_freq.MaxLength:= 10;
      edit_freq.Text:= FloatToStr(freq_array[m]);
      edit_freq.Name:='el_list2_'+ IntToStr(m+1);

      lab_ce:= TLabel.Create(Form2);
      lab_ce.Parent:= Form2.GroupBox1;
      lab_ce.Top:= 73+m*35;
      lab_ce.Left:= 144;
      lab_ce.Font.Color:= clBlack;
      lab_ce.Caption:= 'МГц';
      lab_ce.Name:='el_list3_'+ IntToStr(m+1);

      but_freq:= TButton.Create(Form2);
      but_freq.Parent:= Form2.GroupBox1;
      but_freq.Top:= 70+m*35;
      but_freq.Left:= 190;
      but_freq.Width:= 135;
      but_freq.Font.Color:= clBlack;
      but_freq.Font.Size:= 8;
      but_freq.Font.Name:= 'MS Sans Serif';
      but_freq.Font.Style:= [fsBold];
      but_freq.Caption:= 'Удалить из списка';
      but_freq.Name:='el_list4_'+ IntToStr(m+1);
      but_freq.onClick:=ListButtonClick;

      lab_msg:= TLabel.Create(Form2);
      lab_msg.Parent:= Form2.GroupBox1;
      lab_msg.Top:= 75+m*35;
      lab_msg.Left:= 340;
      lab_msg.Alignment:= taLeftJustify;
      lab_msg.Font.Color:= clBlack;
      lab_msg.Font.Size:= 10;
      lab_msg.Font.Name:= 'Calibri';
      lab_msg.Name:='el_list5_'+ IntToStr(m+1);
      lab_msg.Caption:=IntToStr(m);
    end;
  end;
end;


procedure TForm2.FormCreate(Sender: TObject);
begin
  ShowList;
end;

end.
Ответить с цитированием
  #2  
Старый 30.04.2018, 13:26
Аватар для dr. F.I.N.
dr. F.I.N. dr. F.I.N. вне форума
I Like it!
 
Регистрация: 12.12.2009
Адрес: Россия, г. Новосибирск
Сообщения: 663
Версия Delphi: D6/D7
Репутация: 26643
По умолчанию

Очень тяжелый код. Упростить можно много чего, но речь не об этом.
1. С какой целью Вы используете Finalize(freq_array[Index])? Вы же не динамическую структуру уничтожаете, да и для этого есть Dispose().
2. Полагаю Вы не правильно понимаете действие процедуры Finalize. Она не удалит элемент массива. Вам нужно сдвинуть все элементы массива с индеском больше Index, на место удаляемого элемента (с индексом Index). Move(freq_array[Index+1], freq_array[Index], Length(freq_array) - Index + 1); Мог ошибиться в длине сдвигаемого участка массива, проверьте сами.
__________________
Грамотно поставленный вопрос содержит не менее 50% ответа.
Грамотно поставленная речь вызывает уважение, а у некоторых даже зависть.
Ответить с цитированием
  #3  
Старый 30.04.2018, 13:59
Bosa Bosa вне форума
Прохожий
 
Регистрация: 30.04.2018
Сообщения: 5
Версия Delphi: Delphi 7
Репутация: 10
По умолчанию

Цитата:
"Очень тяжелый код"
- знаю, я только учусь
С Finalize я экспериментировал (что с ней, что без - всё равно ошибка).
С массивом у меня так: на место удаляемого элемента вставляется последний, а потом уменьшается длина массива на 1.
Проблема возникает, когда нажимается динамически созданная кнопка с надписью "Удалить из списка", появляющаяся в списке вместе с добавленной частотой. Причём, если добавить две частоты и удалять вторую, происходит удаление частоты и кнопки из списка без ошибки (как надо). При этом, после каждого добавления или удаления частоты происходит перерисовка списка через процедуру ListShow. А вот если удалять первую частоту, третью и т.д. - всё грустно.

Последний раз редактировалось Bosa, 30.04.2018 в 14:03.
Ответить с цитированием
  #4  
Старый 30.04.2018, 14:02
Аватар для dr. F.I.N.
dr. F.I.N. dr. F.I.N. вне форума
I Like it!
 
Регистрация: 12.12.2009
Адрес: Россия, г. Новосибирск
Сообщения: 663
Версия Delphi: D6/D7
Репутация: 26643
По умолчанию

Мне лень перепечатывать код. Если не затруднит - выложите проект. Быстрее будет отладить, чем искать ошибку в браузере.
__________________
Грамотно поставленный вопрос содержит не менее 50% ответа.
Грамотно поставленная речь вызывает уважение, а у некоторых даже зависть.
Ответить с цитированием
  #5  
Старый 30.04.2018, 19:08
Bosa Bosa вне форума
Прохожий
 
Регистрация: 30.04.2018
Сообщения: 5
Версия Delphi: Delphi 7
Репутация: 10
По умолчанию

dr. F.I.N.
Ок, архив проекта во вложении
Вложения
Тип файла: rar Delphi_project.rar (181.5 Кбайт, 1 просмотров)
Ответить с цитированием
  #6  
Старый 01.05.2018, 06:04
lmikle lmikle вне форума
Модератор
 
Регистрация: 17.04.2008
Сообщения: 8,087
Версия Delphi: 7, XE3, 10.2
Репутация: 49089
По умолчанию

Да, а не проще просто взять TListBox и в него напихать частоты, а кнопки сделать общими. Кстати, сами частоты можно прямо там в итемах хранить (через .Objects[]). Если число целое, то и вообще можно просто так туда засунуть через приведение типа. И интерфейс будет более стандартный, как у большинства программ...
Ответить с цитированием
  #7  
Старый 01.05.2018, 21:49
Bosa Bosa вне форума
Прохожий
 
Регистрация: 30.04.2018
Сообщения: 5
Версия Delphi: Delphi 7
Репутация: 10
По умолчанию

Цитата:
Сообщение от lmikle
Да, а не проще просто взять TListBox
Тут как говорится "мы не ищем лёгких путей" - шучу, конечно. Придётся делать через него.
Но указанную проблему я так и не решил. Пробовал удаление элементов из группбокса списка индивидуально по имени.
Обнаружил странную вещь: делаю "добавил - удалил" с одной частотой, два раза срабатывает - на третий ошибка. Загадка
Хотя кажется на первый взгляд ничего сложного не должно быть.

Даже появился спортивный интерес найти причину такого поведения
В любом случае всем откликнувшимся - СПАСИБО, LIKE и т.д.
Ответить с цитированием
  #8  
Старый 02.05.2018, 07:47
Аватар для dr. F.I.N.
dr. F.I.N. dr. F.I.N. вне форума
I Like it!
 
Регистрация: 12.12.2009
Адрес: Россия, г. Новосибирск
Сообщения: 663
Версия Delphi: D6/D7
Репутация: 26643
По умолчанию

Ошибка была не очевидная, но таки понятная Связана она с самоуничтожением объекта.
Когда Вы нажимаете на динамически созданную кнопку, срабатывает метод ListButtonClick, который в свою очередь вызывает ShowList. Ну а в ShowList Вы удаляете все ранее созданные динамические объекты и создаете их по новой. По завершению метода ShowList программа возвращается в ListButtonClick и когда завершается он, то происходит внутреннее обращение к вызвавшему этот метод объекту. А он уже уничтожен. Вот Вам и ошибка доступа к памяти.

Почему она происходила не всегда? Да потому, что под массив Form2.Components[] память выделена и только перераспределяется по необходимости при добавлении или удалении элементов, а адресация при этом сохраняется. Если Вы удаляете элемент из середины списка, то ошибки не будет, т.к. весь массив элементов сдвигается на место удаленных и ссылка на объект не NULL. А когда удаляете последний, то его место уже никто не занимает и ссылка - NULL. Привет ошибка доступа к памяти.

Код для удаления объектом самого себя можно найти в интернете. Вариантов для этого уйма. Удачи!
__________________
Грамотно поставленный вопрос содержит не менее 50% ответа.
Грамотно поставленная речь вызывает уважение, а у некоторых даже зависть.

Последний раз редактировалось dr. F.I.N., 02.05.2018 в 07:55.
Ответить с цитированием
Этот пользователь сказал Спасибо dr. F.I.N. за это полезное сообщение:
Bosa (02.05.2018)
  #9  
Старый 02.05.2018, 17:16
Bosa Bosa вне форума
Прохожий
 
Регистрация: 30.04.2018
Сообщения: 5
Версия Delphi: Delphi 7
Репутация: 10
По умолчанию

dr. F.I.N.
Вот это ёпрст ... (непереводимый русский язык) хитроумная наука - понять в чём ошибка. Мне таки надо больше d'эльфийской грамоте учиться.
Ответить с цитированием
Ответ


Delphi Sources

Опции темы Поиск в этой теме
Поиск в этой теме:

Расширенный поиск
Опции просмотра

Ваши права в разделе
Вы не можете создавать темы
Вы не можете отвечать на сообщения
Вы не можете прикреплять файлы
Вы не можете редактировать сообщения

BB-коды Вкл.
Смайлы Вкл.
[IMG] код Вкл.
HTML код Выкл.
Быстрый переход


Часовой пояс GMT +3, время: 21:23.


 

Сайт

Форум

FAQ

Соглашения

Прочее

 

Copyright © Форум "Delphi Sources" by BrokenByte Software, 2004-2025