Delphi

Tracking Down Memory Leaks

Memory leaks can be a serious problem or just a minor annoyance. I have seen a Delphi program that leaked so much that it was unusable. I have seen others that suffer from small leaks that hardly matter. Even the Delphi VCL contains memory leaks! Whether a memory leak is serious depends on the individual application and the expected time between failures (ETBF). Generally, a memory leaks occurs because the programmer is unfamiliar with the language constructs but once found is generally easy to fix.

Every programmer should use a memory and resource checker to identify leaks - if you do not use one your program could contain serious leaks that may only show up when the product is out in the field in the hands of users.

Anyone, I use MemProof and not just because it is free - it is also easy to use and appears to work.

Memory Leaks in Delphi

Copying Text to the Clipboard

I discovered this "bug" in a work program.

uses Clipbrd;

   ClipBoard.AsText := 'Some Text';

The above code use a function defined within the VCL to copy the text to the Windows clipboard. The object Clipboard is a global object that the VCL allocates the first time that this function is called. However, MEMPROOF reports a memory leak in the Clipbrd.pas unit for both Delphi 3 and Delphi 4. Upon investigation, the leak is located in the SetBuffer procedure and seems apparent and easy to fix (as shown in red).

    Data := GlobalAlloc(GMEM_MOVEABLE+GMEM_DDESHARE, Size);

    try
      DataPtr := GlobalLock(Data);
      try
        Move(Buffer, DataPtr^, Size);
        Adding;
        SetClipboardData(Format, Data);
      finally
        GlobalUnlock(Data);
      end;
    Except
      GlobalFree(Data);
      raise;
    end;

I could not locate any references to this bug and wonder whether there is something in Windows that I have missed that somehow makes this valid  I finally found a reference to the use of Clipboard on www.Delphi3000.com in an article by Bernhard Angerer.

IMPORTANT: Do not delete the buffer you GlobalAlloc().  Once you put it on the clipboard, it's up to the clipboard to dispose of  it. When retrieving it, again, do not delete the buffer you retrieve -- just make a copy of the contents.
So this "bug" is not really a bug - more a limitation within MemProof.

Using TStringList with Objects

This bug was reported in the PCUG Delphi SIG mailing list by M Phillips and I have edited his comments below.

TStringList allows for a sorted list with or without duplicates. TStringList allows allows the program to store an object with the string by using the AddObject method.

To use the AddObject routine you first create your object then do a call to AddObject(aString, anObject). The way AddObject works is it does a call to Add (to add the string to the list) then calls PutObject which sets the TStringList.Object[ndx] to the object we passed into AddObject.All this goes fine until you use the "TStringList.Duplicates := dupIgnore" option.

The code below is from the CLASSES.PAS unit.

function TStringList.Add(const S: string): integer; begin   if not Sorted then
    Result := FCount
  else
    if Find(S, Result) then
      case Duplicates of
        dupIgnore: Exit;
        dupError: Error(SDuplicateString, 0);
      end;
  InsertItem(Result, S);
end;

A book called "Delphi 3 Example Book" says that the AddObject routine will return a "-1" result when "Duplicates = dupIgnore" so that you know that the Add failed so you can dispose of the object you created. Fair enough but AddObject isn't returning "-1" when dupIgnore is set so the calling program doesn't know that it should dispose of the object.The Delphi 3 help on the AddObject routine it makes no mention that a dupIgnore should cause a -1 result from AddObject. (Michael suggests that the code could be fixed as shown below:)

function TStringList.Add(const S: string): integer;
begin
  if not Sorted then
    Result := FCount
  else
    if Find(S, Result) then
      case Duplicates of
        dupIgnore:
          begin
           Result := -1;
           Exit;
          end;
        dupError: Error(SDuplicateString, 0);
      end;
  InsertItem(Result, S);
end;

However, I suggest that the AddObject function should also be changed to identify a result of -1 so as not to add the object at all. If it does so, then the program has lost the pointer to the object that may have been stored there (which is replaced by AObject). Therefore, the code has not ignored the dupIgnore situation (which contradicts the help file description of the Duplicates property). Something chills my bones when I think of changing some core VCL like the above. The above code also raises the question: Should the routine simply return a -1 or raise an Exception that the calling function / program should catch?

function TStrings.AddObject(const S: string; AObject: TObject): Integer;
begin
  Result := Add(S);
  if Result <> -1 then
    PutObject(Result, AObject);
end;

Last Revised: 21 June 2000
Copyright &copy; 2000

gra...@pcug.org.au