Search code examples
delphimultidimensional-arraydynamic-arraysdelphi-xe3

Keep getting AV when closing my application after imputing some data to dynamic multidimensional array


I'm expriencing some weird problems in one of my projects. What is the most strange is that this only happens in this project and I can't recreate it in another.

Luckilly this is a tiny project (intended to provide an answer to one of the questions here on SO - still not finished) so I managed to figure out that it has to do something with destroying of TImage component I have placed on my form at design time and set a BMP image to it also at design time.

The AV I get is: Project Project1.exe raised exception class $C0000005 with message 'acces violation at 0x00407430: read of adress 0xfffffffc'.

Last three Call stacks are:
Vcl.Graphics.TBitmapCanvas.Destroy
Vcl.Graphics.TCanvasDestroy
System.TObject.Free

Also Delphi puts me in System.pas unit in TObbject.Free method on line "Destroy".

procedure TObject.Free;
begin
  if Self <> nil then
{$IFDEF AUTOREFCOUNT}
    __ObjRelease;
{$ELSE}
    Destroy;
{$ENDIF}
end;

Value of Self at this time is shown only as ().

But why this AV only happens if I store some data into my multidimensional array.
I can create and set the size of the multidimensional array at runtime and everyting is fine. But as soon as I change data of some items in this multidimensional array I get AV when closing my application.

Here is my full source code:

unit Unit2;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, Vcl.ExtCtrls;

type
  TSubImage = record
    LeftBound: Integer;
    RightBound: Integer;
    TopBound: Integer;
    BottomBound: Integer;
  end;

  TForm2 = class(TForm)
    Button1: TButton;
    ListBox1: TListBox;
    Image1: TImage;
    procedure Button1Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form2: TForm2;
  SubImages: Array of TSubImage;

implementation

{$R *.dfm}

procedure TForm2.Button1Click(Sender: TObject);
var X,Y,I: Integer;
    RegionMask: Array of Array of Integer;
begin
  SetLength(RegionMask,Image1.Width+1,Image1.Height+1);
  for Y := 0 to Image1.Height do
  begin
    for X := 0 to Image1.Width do
    begin
      if Image1.Canvas.Pixels[X,Y] <> clFuchsia then
      begin
        //Check left pixel
        if X > 0 then
        begin
          if RegionMask[X-1,Y] <> 0 then
          begin
            RegionMask[X,Y] := RegionMask[X-1,Y];
            //Check to se if pixel X position is leftwards to subimages left bound
            if Subimages[RegionMask[X,Y]].LeftBound > X then
              //Move subimage left bound to match pixel X position
              Subimages[RegionMask[X,Y]].LeftBound := X;
            //Check to se if pixel X position is rightwards to subimages right bound
            if Subimages[RegionMask[X,Y]].RightBound < X then
              //Move subimage right bound to match pixel X position
              Subimages[RegionMask[X,Y]].RightBound := X;
            //Check to se if pixel Y position is upwards to subimages top bound
            if Subimages[RegionMask[X,Y]].TopBound > Y then
              //Move subimage top bound to match pixel Y position
              Subimages[RegionMask[X,Y]].TopBound := Y;
            //Check to se if pixel Y position is downwards to subimages bottom bound
            if Subimages[RegionMask[X,Y]].BottomBound < Y then
              //Move subimage bottom bound to match pixel Y position
              Subimages[RegionMask[X,Y]].BottomBound := Y;
          end;
        end;
        //Check top pixel
        if Y > 0 then
        begin
          if RegionMask[X,Y-1] <> 0 then
          begin
            RegionMask[X,Y] := RegionMask[X,Y-1];
            //Check to se if pixel X position is leftwards to subimages left bound
            if Subimages[RegionMask[X,Y]].LeftBound > X then
              //Move subimage left bound to match pixel X position
              Subimages[RegionMask[X,Y]].LeftBound := X;
            //Check to se if pixel X position is rightwards to subimages right bound
            if Subimages[RegionMask[X,Y]].RightBound < X then
              //Move subimage right bound to match pixel X position
              Subimages[RegionMask[X,Y]].RightBound := X;
            //Check to se if pixel Y position is upwards to subimages top bound
            if Subimages[RegionMask[X,Y]].TopBound > Y then
              //Move subimage top bound to match pixel Y position
              Subimages[RegionMask[X,Y]].TopBound := Y;
            //Check to se if pixel Y position is downwards to subimages bottom bound
            if Subimages[RegionMask[X,Y]].BottomBound < Y then
              //Move subimage bottom bound to match pixel Y position
              Subimages[RegionMask[X,Y]].BottomBound := Y;
          end;
        end;
        //Create new region
        if RegionMask[X,Y] = 0 then
        begin
          SetLength(SubImages,Length(SubImages)+1);

          //If I comment out this line no exception is raised on closing the from
          RegionMask[X,Y] := Length(SubImages);

          //Set subimage initial bounds which are coordinates of one pixel
          //since we created new region for this pixel
          SubImages[RegionMask[X,Y]].LeftBound := X;
          SubImages[RegionMask[X,Y]].RightBound := X;
          SubImages[RegionMask[X,Y]].TopBound := Y;
          SubImages[RegionMask[X,Y]].BottomBound := Y;
        end;
      end;
    end;
  end;
  Form2.Caption := IntToStr(Length(SubImages)-1);
  for I := 0 to Length(Subimages)-1 do
  begin
    ListBox1.Items.Add(IntToStr(SubImages[I].LeftBound)+','+
                       IntToStr(SubImages[I].RightBound)+','+
                       IntToStr(SubImages[I].TopBound)+','+
                       IntToStr(SubImages[I].BottomBound));
  end;
  SetLength(RegionMask,0,0);
  RegionMask := nil;
end;

end.

Solution

  • You've got a one-off error in these lines. Consider the first time you add an element to SubImages. The length of SubImages is 1, but the only element in the array is SubImages[0]. You set RegionMask[X,Y] to 1, and then use that value to index the array. So you are trying to access one item beyond the end of the array.

       SetLength(SubImages,Length(SubImages)+1);
    
          RegionMask[X,Y] := Length(SubImages);
    
          SubImages[RegionMask[X,Y]].LeftBound := X;