Search code examples
delphidelphi-10.2-tokyo

Memory leak reading TZipFile


The following code creates a .zip file containing a text file named HelloWord.txt. Later, it reads the file correctly, but a memory leak occurs using procedure Zipfile.Read (0, LStream, ZHeader) and releasing LStream.

I am using ReportMemoryLeaksOnShutdown := DebugHook <> 0; to see memory leaks.

// Uses  System.zip, System.IOUtils;
    
procedure Probezip;
var
  zipfile           : TZipFile;
  PathDoc           : string;
  LStream           : TStream;
  ZHeader           : TZipHeader;
  MyList            : TStringList;
begin
  // (Path documents windows)
  PathDoc := TPath.GetDocumentsPath;

  zipfile := TZipFile.Create;
  MyList  := TStringList.Create;


try
 // Write test TZipfile
  MyList.Add ('Hello Word');
  MyList.SaveToFile (PathDoc + '\' + 'helloword.txt');

  zipfile.Open (PathDoc + '\' + 'test.zip', zmWrite);
  ZipFile.Add (PathDoc + '\' + 'helloword.txt');
  ZipFile.Close;
  MyList.Clear;

  // Read test Tzipfile
  zipfile.Open (PathDoc + '\' + 'test.zip', zmRead);
  LStream := TStream.Create; //This line should be removed to solve the
                               // problem as Andreas Rejbrand has pointed out.
                               // I leave it here as a didactic value.
   try
     zipfile.Read (0, LStream, ZHeader);
      MyList.LoadFromStream (LStream);
      Showmessage (MyList.Text); // Hello Word
    finally
      LStream.Free;
   end;
  finally
    zipfile.Close;
    zipfile.Free;
    MyList.Free;
  end;
end;

Solution

  • The second parameter of the TZipFile.Read overload you are using is of type TStream, but it is an out parameter.

    This means that the TZipFile.Read method creates a stream object and makes LStream point to it. Hence, you leak the stream you created manually on the line before. Remove that line (LStream := TStream.Create;) and move down the try protecting the stream:

    zipfile.Read(0, LStream, ZHeader); // will CREATE a stream object
                                       // and save its address in LStream
    try
      MyList.LoadFromStream(LStream);
      Showmessage(MyList.Text); // Hello Word
    finally
      LStream.Free;
    end;