4

I'm using JCL version 2.4.1.4571 with Delphi XE3 and have had no luck decompressing archives. I've downloaded the dll's from JEDI's site and also tried using the 7z.dll (32bit) from 7-zip.org but either way I get the same error when I try to "ExtractAll"

See function below:

function TForm1.Decompress(FileName, DestDir: String): Boolean;

var
  archiveclass: TJclDecompressArchiveClass;
  Myarchive: TJclDecompressArchive;

begin
  Decompress := False;
  // Filename = name.7z or name.zip (simple test zips nothing fancy)
  // DestDir = fully qualified path to an existing directory
  archiveclass := GetArchiveFormats.FindDecompressFormat(FileName); 
  Try
    if Assigned(archiveclass) then
      Begin
        Myarchive := archiveclass.Create(FileName);
        if (Myarchive is TJclSevenZipDecompressArchive) then
          Begin
            try
              Myarchive.ListFiles; { Fails without doing this first }
{ ExtractAll (AutocreateSubDir) must be set true if arc has directories or it will crash }
              Myarchive.ExtractAll(DestDir, True); 
              Decompress := True;
            except on E: EJclCompressionError do
              Begin
                ShowMessage(e.Message); 
              End;
            end;
          End
        Else
          ShowMessage('Not supported by 7z.dll'); 
      End;
  Finally
    MyArchive.Free;
  End;
end;

When I execute the MyArchive.ExtractAll line, I get an exception Sevenzip: Error result (00000001) Incorrect function. I based my code on code from others here on StackOverflow. Am I missing something I need to do first or is this a bug? I've replaced the extract line with MyArchive.ListFiles and get the same error (I saw that in an example here; however, I've yet to divine the purpose of ListFiles.
Compiling to 32bit target.

Edit: Created a series of different types of archives using 7-zip and tried to decompress each with my program. The first thing I discovered is that if the archive contains directories of files, ExtractAll will crash if you don't set the second parameter to True. I then tested archives with different compression methods.

.7z archive using LZMA2 Ultra compression gives the Hresult = 1 error

.zip archive using LZMA Ultra compression gives the Hresult = 1 error

.zip archives using flavors of Deflate or deflate64 all work fine.

It appears that the library doesn't handle LZMA compression at all. Since it makes no sense that the 7z.dll can't handle it, I'm guessing the problem is with the JEDI JCL code. I need to be able to compress/decompress .7z and .zip's using LZMA with this library or I could have just used the built in zip stuff to begin with. Any further suggestions would be appreciated.

Mike
  • 153
  • 3
  • 11

3 Answers3

2

I think that is a JCL implementation bug. 7z use COM interfaces, and returns HRESULT codes. JCL attemptes to translate them into error messages using SysErrorMessase(), but AFAIK it works only for Win32 error codes, not HRESULT. That way the return code S_FALSE gets mapped to ERROR_INVALID_FUNCTION (see http://issuetracker.delphi-jedi.org/view.php?id=6348). My guess is that a 7z call is returning S_FALSE for some reason, because it encounters some issue when decompressing - and the error is deceiving.

See also Error Handling in COM.

LDS
  • 336
  • 1
  • 4
  • 1
    FWIW, OleCheck is the way to convert HRESULTs to exceptions. – David Heffernan Nov 06 '14 at 11:56
  • @LDS thanks. I'm still investigating. I stepped through the code and see the HResult being sent as the exception. I still don't know why. – Mike Nov 06 '14 at 17:41
  • The problem appears to be related to archives using LZMA as the compression method. See my edits above. – Mike Nov 06 '14 at 19:22
  • Which is the function that returns the "error" value? IIRC some 7z functions calls a callback where the "real" error value is set and returned. Download and check 7z code, there should be a small command line client example. – LDS Nov 06 '14 at 19:23
  • @LDS, I'm not sure I understand what you are suggesting. Are you suggesting I download the 7Zip LZMA source code and review it? The function inside the JCLCompression unit is "OpenArchive" – Mike Nov 06 '14 at 19:44
  • @LDS, I figured it out (see answer below). Thanks for the suggestions. – Mike Nov 06 '14 at 20:21
  • No, I suggested to check how actually 7z COM interfaces are called and how error are returned and handled. IIRC the client example uses the COM interfaces to call into the library. Anyway glad you found the issue, still I believe the JCL implementation is broken and error handling deceiving. – LDS Nov 06 '14 at 20:37
  • I agree, the JCL implementation is not good and the lack of documentation is silly. I realize this is an open source project and the authors have jobs, but it might as well be closed source as no one other than the developers know what any of these libraries do. Even documentation as weak as "ListFiles does xyz" with no details on parameters or usage would be a huge improvement. Mind blowing. – Mike Nov 08 '14 at 03:47
2

Further Googling on this problem turned up http://sourceforge.net/p/jcl/mailman/message/21371812/

It appears "FindDecompressFormat doesn't find archive format if file name is not in lower case."

I tried changing the string I was passing to lowercase and I successfully decompressed an LZMA archive.

archiveclass := GetArchiveFormats.FindDecompressFormat(lowercase(FileName));

JEDI JCL would be a cool library if it had any documentation whatsoever - sad.

Mike
  • 153
  • 3
  • 11
0

If you call TJclZipDecompressArchive with a filename that does not exist you will get the same not very helpful error message on the ListFiles function.

Moral of the story check if the file exists yourself before calling the api.

Jan Derk
  • 2,552
  • 27
  • 22