How do I monitor and cancel progress of Abbrevia compression?

450 Views Asked by At

Delphi Xe4. There is a set of components for data compression : ABBREVIA (http://tpabbrevia.sourceforge.net) It implements LZMA compression, and a module AbLZMA.pas (Lzma compression / decompression routines).

Use it :

...
Uses ablzma;
...
procedure TForm1.Button1Click(Sender: TObject);
var f1,f2:tfilestream;
begin
  f1:=tfilestream.Create('d:\1.test',fmOpenRead);
  f2:=tfilestream.Create('d:\1.lzma',fmCreate);
  LzmaEncodeStream(f1,f2,f1.Size);
  f2.Free;
  f1.Free;
end;
...

Everything is working fine.

Questions:

  1. how to add code to display percent complete operation?
  2. how to add code to the abort of the compression process?

In module AbLZMA.pas (also tried to use AbLZMAStream.pas) is the main procedure LzmaEnc_Encode, who works at a call LzmaEncodeStream:

function LzmaEnc_Encode(p: CLzmaEncHandle; outStream: PISeqOutStream;
  inStream: PISeqInStream; Progress: PICompressProgress;
  Alloc, allocBig: PISzAlloc): SRes; cdecl; external;

It has a parameter "Progress: PICompressProgress;", where

ICompressProgress = packed record
 Progress: function(p: Pointer; inSize, outSize: Int64): SRes; cdecl;
end;
PICompressProgress = ^ICompressProgress;

I tried to add a procedure in the module AbLZMA.pas:

function MyProgress(p: Pointer; inSize, outSize: Int64): SRes;cdecl;
begin
// what is "p"?
// form1.caption:=result //? 
end;

...

procedure LzmaEncodeStream(ASourceStream, ATargetStream: TStream; ASourceSize: Int64);
var
  ...
  PMyProgress:PICompressProgress;
begin
  ...
  PMyProgress.Progress:=MyProgress;
  ...
  LzmaCheck(LzmaEnc_Encode(LEncHandle, @LOutStreamRec.Intf, @LInStreamRec.Intf,
    {nil}PMyProgress // this
    ,@DelphiMMInterface, @DelphiMMInterface));
  ...
end;

In this case (even if the body of the procedure blank), getting Error AV. How to get the data from the current percentage of completion?

1

There are 1 best solutions below

3
On BEST ANSWER

You have to allocate an ICompressProgress variable. You've declared a pointer to one, but then never pointed it at anything.

Do it like this:

procedure LzmaEncodeStream(ASourceStream, ATargetStream: TStream;
  ASourceSize: Int64);
var
  MyProgress: ICompressProgress;
begin
  ...
  MyProgress.Progress:=MyProgress;
  ...
  LzmaCheck(LzmaEnc_Encode(LEncHandle, @LOutStreamRec.Intf, @LInStreamRec.Intf,
    @MyProgress, @DelphiMMInterface, @DelphiMMInterface));
  ...
end;

You are calling LzmaEnc_Encode which is the raw LZMA C interface. The progress callback documentation should exist in the LZMA SDK, but I cannot as yet find any good documentation. I suspect that you'll need to read the C implementation of LZMA to get to the bottom of this.

OK, here's the code that calls your progress callback:

for (;;)
{
  res = LzmaEnc_CodeOneBlock(p, False, 0, 0);
  if (res != SZ_OK || p->finished != 0)
    break;
  if (progress != 0)
  {
    res = progress->Progress(progress, p->nowPos64, RangeEnc_GetProcessed(&p->rc));
    if (res != SZ_OK)
    {
      res = SZ_ERROR_PROGRESS;
      break;
    }
  }
}

The progress callback passes the PICompressProgress in the first parameter. This allows you to declare your ICompressProgress record with extra fields and so allow your callback function to receive state information. The inSize parameter is the position in the input stream. You can make a progress value by dividing inSize by the size of the input stream. And the outSize parameter is, presumably, the number of bytes written so far in the output file.

If you return any value of than SZ_OK then the operation will be cancelled.