Search code examples
delphidelphi-10.2-tokyo

Delphi 10.2 how to use the ZipProgress


An earlier StackOverflow topic provides some sample code for using the progress event while using TZipFile:

Using Delphi Toyko creating a Zip file onprocess example

I am able to make the example work if I use it as a MainForm. But I am completely stymied in trying to convert the code to work in a separate unit.

Every time I try to compile the code as a unit, I get an error message:

E2009 Incompatible types: 'method pointer and regular procedure'

The error is triggered on the line:

TZipFile.ZipDirectoryContents('C:\temp\Test.zip','c:\temp\zipTest',zcDeflate,OnZipProgressEvent);

I've researched in every way I could think of, and tried all manner of alternatives, but no luck.

Can anyone explain how to make the OnZipProgressEvent code work in a separate unit?

This is the unit code that I have so far:

unit ZipDelphiNative; 

interface 

uses 
  Windows,SysUtils, StrUtils, Variants, MaskUtils, Dialogs,StdCtrls, 
  Graphics, Menus,Classes, ClipBrd,  System.Zip, Winapi.Messages, 
  System.Generics.Collections, system.ioutils,  system.types, 
  Forms,ComCtrls, DateUtils; 

Type 
  TZipProgressEvent = procedure(Sender: TObject; FileName: string; Header: TZipHeader; Position: Int64) of object; 
  // TZipProgressEvent = procedure(Sender: TObject; FileName: string; Header: TZipHeader; Position: Int64); 

function jrCreateZipFromFolder(inZipName : string; inSourcePath : string; inTargetPath : string) : boolean; 
procedure OnZipProgressEvent (Sender: TObject; FileName: string; Header: TZipHeader; Position: Int64); 

var 
  PreviousFilename : string; 

implementation 

uses
  Unit1; 

function jrCreateZipFromFolder(inZipName : string; inSourcePath : string; inTargetPath : string) : boolean; 
  //inTargetPath is already confirmed to be valid 
  //Zips all files in inSourcePath & subfolders 
var 
  Zip : TZipFile; 
  //  ZipCallBack: TZipProgressEvent; 
  sZipName, sSourceName : string; 
begin 
  result := true; 
  //  ZipCallBack :=  OnZipProgressEvent; 
  sZipName := IncludeTrailingBackslash(inTargetPath) + inZipName; 
  sSourceName := IncludeTrailingBackslash(inSourcePath) ; 
  Zip := TZipFile.Create; 
  try 
    if FileExists(sZipName) then 
      DeleteFile(sZipName); 

    //    zip.ZipDirectoryContents(sZipName, sSourceName, zcDeflate, ZipCallBack);  //do not store folder too 

    zip.ZipDirectoryContents(sZipName, sSourceName, zcDeflate, OnZipProgressEvent);  //do not store folder too 

    zip.Close; 

  except 
    result := false; 
  end; 
  FreeAndNil(Zip); 
end; 

procedure OnZipProgressEvent(Sender: TObject; FileName: string; 
  Header: TZipHeader; Position: Int64); 
begin 
  if PreviousFilename <> FileName then 
  begin 
    PreviousFilename := FileName; 
    unit1.Form1.ProgressBar1.Max := Header.UncompressedSize; 
    unit1.Form1.ProgressBar1.Position := Position; 
  end 
  else 
    //    Unit1.Form1.ProgressBar1.Position := (Position * 100) div Header.UncompressedSize ; 
    Unit1.Form1.ProgressBar1.Position := Position; 
  Application.ProcessMessages; 
end; 

end. 

I have tried defining TZipProgressEvent with and without of object. I have two options in this code for using the TZipProgressEvent, either like the original example, or the commented out version of defining ZipCallBack. Both work under different circumstances when used in a Form. Neither work when used in this unit.

So, I am baffled as to how to make this work. It would sure be nice if Embarcadero offered useful sample code!


Solution

  • TZipProgressEvent is declared as being of object, which means it must be a method of an object (class). So in order to implement it, you need to declare a class that implements that method with that signature (the signature in the TZip class, not the one you made up).

    This should get you started. Add your own code to the implementation of jrCreateZipFromFolder and TMyZipProgress.ShowZipProgress, as I've said in the code comments below. I've made it a class procedure so that you don't have to create an instance of TMyZipProgress to use it.

    unit ZipDelphiNative; 
    
    interface
    
    function jrCreateZipFromFolder(inZipName, inSourcePath, inTargetPath: string): Boolean;
    
    implementation
    
    { TMyZip }
    
    uses
      System.Zip;
    
    type
      TMyZipProgress = class(TObject)
      private
        class procedure ShowZipProgress(Sender: TObject; FileName: string;
          Header: TZipHeader; Position: Int64);
      end;
    
    
    function jrCreateZipFromFolder(inZipName, inSourcePath,
      inTargetPath: string): boolean;
    var
      FZip: TZipFile;
    begin
      Result := False;
      FZip := TZipFile.Create;
      try
        // Rest of your zip file code here, then call
        FZip.ZipDirectoryContents(sZipName, sSourceName, zcDeflate, TMyZipProgress.ShowZipProgess);
        Result := True;
      finally
        FZip.Free;
      end;
    end;
    
    class procedure TMyZipProgress.ShowZipProgress(Sender: TObject; FileName: string;
      Header: TZipHeader; Position: Int64);
    begin
      // Show your progress here, whatever it needs to do
    end;
    
    end.