Исключение при первой возможности в $ 7C81EB33

#delphi #exception

#delphi #исключение

Вопрос:

У меня есть приложение, которое при запуске дома работает нормально, однако при запуске на школьных компьютерах (Windows XP) я получаю следующее сообщение. (Это перекомпиляция, а не просто запуск .exe)- В Delphi 2005

Исключение первой случайности в $ 7C81EB33. EAccessViolation класса исключений с сообщением «Нарушение доступа по адресу 0045E5E2 в модуле»Project2.exe ‘. Считывание адреса 00000198’. Процесс Project2.exe (440)

Код: игнорирование ненужного материала.

         Image1: TImage; // Image(all the way to 72) 
        Timer1: TTimer; Timer2: TTimer;   
        procedure Button1Click(Sender: TObject);
        procedure FormCreate(Sender: TObject);
        procedure SomeOtherProcedure(Sender: TImage);
        procedure Timer1Timer(Sender: TObject);
        procedure Timer2Timer(Sender: TObject);
          private
        { private declarations }
      public
        { public declarations }
      end;
    var
      Form1: TForm1;
      left : integer;
      top  : integer;
      gap  : integer;
      type
        coordinates = record
          row : integer ;
          col : integer;
        end;

      var
      picarray : array[0..5,0..5] of timage;
      thiscover, midcover, lastcover : timage;
      imageindex : array[0..5,0..5] of integer;
      picloc: array[0..3] of coordinates;
      clickcount, pairsfound, attemptcount : integer;
implementation
{$R *.lfm}
procedure initialise();
var
i, j, whichcol, whichrow : integer;
begin
        for i := 0 to 5 do
        for j := 0 to 5 do
        imageindex[i,j] := -1; // not used
        randomize;
        for i := 0 to 11 do
        for j := 1 to 3 do
        begin
        repeat
          begin
          whichcol := random(6) ;
          whichrow := random(6)  ;
          end;
        until imageindex[whichcol, whichrow] = -1;
        picarray[whichcol, whichrow].Picture.LoadFromFile('C:UsersHaydenPictures'  inttostr(I 1)  '.jpg');
        imageindex[whichcol, whichrow] := I  ;
        end;
        clickcount := 0  ;            //
        pairsfound := 0    ;
        attemptcount := 0  ;
        end;

    procedure TForm1.FormCreate(Sender: TObject);
var
cpic : tcomponent;
whichcol: integer;
whichrow : integer;
begin
gap := image2.left - image1.left;
top := image1.Top;
left := image1.left;
for cpic in form1 do
begin
     if (cpic.ClassType = timage) and (cpic.Tag = 10) then
     begin
     whichcol := (timage(cpic).left - left) div gap;
     whichrow := (timage(cpic).Top - top) div gap;
     picarray[whichcol, whichrow] := timage(cpic)   ;
end;
end;
initialise;
end;
  

Строка >>> picarray[whichcol, whichrow].Рисунок.Загрузка из файла(‘C:UsersHaydenPictures ‘ inttostr(I 1) ‘.jpg’);
похоже, это вызывает ошибку. И если это ошибка кодирования, каков правильный способ сделать это?

Ответ №1:

Сначала я собираюсь немного почистить ваш код, потому что в его нынешнем виде очень сложно понять, что происходит. Я настоятельно рекомендую вам выработать привычку уделять несколько минут тому, чтобы ваш код был четко отформатирован — это сэкономит вам часы отладки.

Я применил только следующие простые изменения: отступ, пустые строки и широкое использование begin .. end;

 var
  picarray : array[0..5,0..5] of timage;
  thiscover, midcover, lastcover : timage;
  imageindex : array[0..5,0..5] of integer;
  picloc: array[0..3] of coordinates;
  clickcount, pairsfound, attemptcount : integer;

implementation

{$R *.lfm}
procedure initialise();
var
  i, j, whichcol, whichrow : integer;
begin
  for i := 0 to 5 do
  begin
    for j := 0 to 5 do
    begin
      //It's clear you're initialising the 36 entries of imageindex to -1
      imageindex[i,j] := -1; // not used
    end;
  end;

  randomize;
  for i := 0 to 11 do
  begin
    for j := 1 to 3 do
    begin
      //This loop also runs 36 times, so it fills the whole of imageindex with new values
      //It also loads all 36 entries of picarray with an image specfied by the current value of i
      //The approach is dangerous because it depends on the 'loop sizes' matching,
      //there are much safer ways of doing this, but it works
      repeat
        begin //This being one of the only 2 begin..end's you provided inside this is routine is pointless because repeat..until implies it.
          whichcol := random(6) ;
          whichrow := random(6)  ;
        end;
      until imageindex[whichcol, whichrow] = -1;

      //This line itself will throw an access violation if picarray[whichcol, whichrow] doesn't 
      //contain a valid TImage instance... we have to check other code to confirm that possibility
      picarray[whichcol, whichrow].Picture.LoadFromFile('C:UsersHaydenPictures'   inttostr(I 1)   '.jpg');
      imageindex[whichcol, whichrow] := I  ;
    end;
  end;

  clickcount := 0  ;            //
  pairsfound := 0    ;
  attemptcount := 0  ;
end;
  

Переходим к следующему фрагменту кода:

 procedure TForm1.FormCreate(Sender: TObject);
var
  cpic : tcomponent;
  whichcol: integer;
  whichrow : integer;
begin
  gap := image2.left - image1.left;
  top := image1.Top;
  left := image1.left;
  for cpic in form1 do
  begin
    //This loop attempts to assign existing TImage instances to picarray
    //However, the way you're going about it is extremely dangerous and unreliable.
    //You're trying to use the position of a component on the form to determine its
    //position in the array.
    //There are many things that could go wrong here, but since this seems to be a
    //homework excercise, I'll just point you in the right direction - you need
    //to debug this code.
    if (cpic.ClassType = timage) and (cpic.Tag = 10) then
    begin
      whichcol := (timage(cpic).left - left) div gap;
      whichrow := (timage(cpic).Top - top) div gap;
      picarray[whichcol, whichrow] := timage(cpic)   ;
    end;
  end;

  //Here you call initialise, which as I said before, will
  //cause an Access Violation if picarray is not correctly 'set up'
  //The previous code in this method certainly has a bug which is 
  //preventing one or more picarray entries from being assigned a 
  //valid TImage instance.
  //You could write a simple for I := 0 to 5, for J := 0 to 5 loop
  //here to check each of picarray entries and pinpoint which is 
  //incorrect to aid your debugging of the pevious loop.
  initialise;
end;
  

Комментарии:

1. Вау, это было очень проницательно. Я скажу, что у меня не очень большой опыт работы с Delphi, и мне это не очень нравится. Но я хочу попробовать и продолжать в том же духе, пока не пойму все правильно. Спасибо, вы действительно приложили некоторые усилия.

2. @Skeela87 — просто из любопытства (больше ничего): почему вам не нравится Delphi? можете ли вы привести несколько наглядных примеров? возможно, вы привыкли к управляемым языкам?

Ответ №2:

Критическим разделом является инициализация picarray . Вы не можете быть уверены, что каждому элементу массива присвоен компонент TImage. Если хотя бы одно изображение имеет неправильное left или top у вас двойное присвоение одному элементу, а другому оставлено значение nil. Это приведет к нарушению доступа при первом использовании, например, в picarray[whichcol, whichrow].Picture.LoadFromFile .

Я бы рекомендовал переработать инициализацию picarray с помощью циклов for для каждого измерения. Чтобы получить правильное время, я бы назвал их как ‘Image_2_3’ и получил экземпляры в цикле по имени.

Ответ №3:

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

 try
  if FileExists('C:UsersHaydenPictures'  inttostr(I 1)  '.jpg') then
    picarray[whichcol, whichrow].Picture.LoadFromFile('C:UsersHaydenPictures'  inttostr(I 1)  '.jpg');
  else 
    ShowMessage("File not found");
except
  on E : Exception do
    ShowMessage(E.ClassName ' error raised, with message : ' E.Message);
end;
  

Комментарии:

1. Спасибо за ответ, но файл действительно существовал, обычно это все равно приводит к ошибке «файл не найден».

2. это правда, но try-catch предназначен для всех ошибок, а не только для ‘файл не найден’ .. E. Сообщение может быть полезным, помогало мне много раз.

3. Хорошо, я должен буду проверить завтра. Я вернусь к отчету здесь. Спасибо

4. Джордж, перехват исключения и отображение сообщения полезны только в том случае, если вы уже выпустили программное обеспечение для пользователей и не можете воспроизвести проблему локально (и даже тогда это не так хорошо, как использование реальной библиотеки исключений, такой как EurekaLog или MadExcept). В этом случае мы уже знаем, какая строка вызвала исключение, какой класс исключений был выдан и каково было его сообщение. Предложенный вами код не добавит никакой информации к расследованию.

5. На самом деле, такой стиль отлова исключения очень, очень плох ! 1) Ничего не делая (предполагая, что обработчик исключений приложения по умолчанию), в любом случае появляется сообщение. 2) Разница в том, что вы больше не находитесь в состоянии исключения, поэтому теперь, сообщив пользователю о проблеме, вы больше не сообщаете об этом остальной части программы. Что приводит к крайне неприятному потенциалу повторных ошибок в дальнейшем.