3

Using: Delphi XE7 Update 1, TAwImageGrid, Windows 10 Professional running on Intel Core i7-2820QM.

This code loads images into the grid from a database:

var
  s, w: String;
  r: Integer;
  ms: TMemoryStream;
  bmp: TBitmap;
begin
  r := uqProj_Search.RecordCount;

  // Load images

  for r := imgGrid.Count - 1 downto 0 do
    imgGrid.Items.Images[r].Free;

  imgGrid.Clear;

  ms := TMemoryStream.Create;
  try
    while not(uqProj_Search.Eof) do
    begin
      r := uqProj_Search.FieldByName('row_id').AsInteger;

      // :proj_id
      uqImg_S.ParamByName('proj_id').AsInteger := r;
      uqImg_S.Prepared := True;
      uqImg_S.Open;

      ms.Clear;
      uqImg_Simg.SaveToStream(ms);

      uqImg_S.Close;

      ms.Position := 0;
      bmp := TBitmap.Create;
      try
        bmp.LoadFromStream(ms);

        imgGrid.Items.Add(IntToStr(r));
        imgGrid.Items.Images[imgGrid.Count - 1] := TBitmap.Create;
        imgGrid.Items.Images[imgGrid.Count - 1].Assign(bmp);
      finally
        bmp.Free;
      end;

      uqProj_Search.Next;
    end;

  finally
    ms.Free;
  end;

end;

I have this code in the KeyDown event (called when the Del key is pressed):

procedure TfmSrchRec.imgGridKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
var
  x, p: Integer;
  AFormat: Word;
  AData: THandle;
  APalette: HPalette;
begin
  x := imgGrid.ItemIndex;
  p := StrToInt(imgGrid.Items.Strings[x]);

  if (x = -1) then
    Exit;

  if (UpCase(Char(Key)) = 'C') and (Shift = [ssCtrl]) then
  begin
    // Clipboard.Assign(imgGrid.Images[imgGrid.ItemIndex])
    TBitmap(imgGrid.Images[x]).Dormant;
    TBitmap(imgGrid.Images[x]).SaveToClipboardFormat(AFormat, AData, APalette);
    Clipboard.SetAsHandle(AFormat, AData);
  end
  else if (Key = VK_DELETE) then
  begin
    imgGrid.Items.Images[x].Free;
    imgGrid.Items.Delete(x);
  end;
end;

Freeing up memory in the form's OnClose event:

procedure TfmSrchRec.FormClose(Sender: TObject; var Action: TCloseAction);
var
  r: Integer;
begin
  for r := imgGrid.Count - 1 downto 0 do
    imgGrid.Items.Images[r].Free;
end;

Here's the problem:

After deleting an image from the grid, if that image was the last remaining image, then closing the program would produce this error message:

---------------------------
Unexpected Memory Leak
---------------------------
An unexpected memory leak has occurred. The unexpected small block leaks are:



61 - 68 bytes: Unknown x 1


---------------------------
OK   
---------------------------

The error does not occur if there was a remaining image in the grid before the application was closed. I have ReportMemoryLeaksOnShutDown := True at project startup (in the DPR file).

I'm guessing that this error has to do with the component's code more than the way I am using it. I'm hoping that the TAwImageGrid component author NGLN could have a look at this question and provide the answer, but other Delphi gurus are also welcome.

Links:

  1. TAwImageGrid component source official home page: https://github.com/NGLN/AwImageGrid

  2. StackOverflow Question that gives a good introduction to the component: Looking for a custom image grid

NGLN
  • 43,011
  • 8
  • 105
  • 200
Steve F
  • 1,527
  • 1
  • 29
  • 55

1 Answers1

2

I can reproduce your findings and consider it a bug.

When making the component, I copied the implementation of TStringList from D7, i.e. by using a pointer to a non-existing fix-sized array for the internal storage of the items. Strangely enough, I cannot find flaws in it, but D7's TStringList implementation does not produce this bug. I suppose it has something to do as explained here.

I see that the implementation of TStringList in XE2 is changed to the use of a dynamic array. When I change the component's code to that same design, the memory leak is gone. So I will change the open source code too, but for the time being you might do yourself.

NGLN
  • 43,011
  • 8
  • 105
  • 200