Get Zip Functionality for Free

Looking for a way to integrate zip and unzip functionality into your application on the cheap? Here's the plan-and Fernando Vicaria has even wrapped it all up into a neat little component for you!




This article originally appeared in Delphi Developer



Copyright Pinnacle Publishing, Inc. All rights reserved.







You need a chunk of functionality. Do you buy a component or roll up your sleeves and start in on the design yourself? Very often you can find a freeware component that does the job. Unfortunately, it's also common that those components don't come with source or are less than perfect. And if you're like me, you hate to add anything to your application that doesn't come with source code or hasn't been been fully tested.



If one extra feature you need is the capability to compress or decompress files using the industry standard Zip format, then you're in luck!



WinZip 7.0 (and later versions) now has its own command line support. This incredibly useful add-on is available for download from Nico Mak Computing Inc. at its Web site: http://www.winzip.com.



What's so great about a command line? Think about it. Now you can build a component to call the command line program, and all your problems will be solved!



Here's one way to do just that.



Getting WinZip

First of all, you need to install WinZip 7.0. If you al-ready have any other version of WinZip, upgrades can be downloaded for free from the URL I just mentioned.



Once you have the latest version, the next step is to download and install the command line add-on. Once you have them both installed, make sure they've been added to your path.



Now if you could build a component to call the command line program and pass the right instructions to execute WinZip, that would make compressing and decompressing files in your application simple!



An enumerated type and the declaration

This is a typical example of what OOP (and, more specifically, Delphi) is all about. The first thing you should do is create an enumerated type where you can specify which direction you want to go (compress or decompress):



TZipState = (zsCompress, zsDecompress);



This type will let you switch between the two possible actions of the component. Listing 1 shows the component declaration.



Listing 1. The compression/decompression component declaration.



TFVWinZip = class(TComponent)

  private

    { Private declarations }

    FFileName: string;

    FUseFileList: boolean;

    FFileList: TStringList;

    FZipFileName: string;

    FZipState: TZipState;

    FOnExecute: TNotifyEvent;

    function GetFileList: TStringList;

    procedure SetFileList(value: TStringList);

    procedure CreateCompressBat;

  published

    { Published declarations }

    property FileName: string

      read FFileName write FFileName;

    property UseFileList: boolean

      read FUseFileList write FUseFileList

      default False;

    property FileList: TStringList

      read GetFileList write SetFileList;

    property ZipState: TZipState

      read FZipState write FZipState;

    property ZipFileName: string

      read FZipFileName write FZipFileName;

    property OnExecute: TNotifyEvent

      read FOnExecute write FOnExecute;

  public

    { Public declarations }

    constructor Create(AOwner: TComponent); override;

    destructor Destroy; override;

    procedure Execute;

  end;



Most of its properties and methods should be self-explanatory. You can find the full implementation in the file FVWinZip.pas.



Implementation declarations

If you look at the beginning of the implementation section of the code, you'll see the following code:



{$R FVWinZip.res}

const

  BatchFile = 'tfvzip.bat';

  Zip = 'wzzip';

  UnZip = 'wzunzip';

  ListName = 'zipit.lst';



The first line should be very familiar for those used to creating their own components. It specifies the resource file that contains the icon for our component. You can create and design your own icons using any resource editor (in Delphi, the default is Imagedit.exe, which can be found in the bin directory).



The constants declared here will help you deal with the various files used in the process of creating or extracting our zip files.



BatchFile is the name of the batch file that will be created by our component. This file has the responsibility of calling one of the zip files (Zip for compress or Unzip for decompressing). The last one is ListName, which is the name of a text file that contains a list of the files to be added to the Zip file as well as commands for the command line program (much like you'd do in a batch file and DOS). ListName is only used when the property UseFileList of our Zip component is set to true and a valid list of files is entered in the FileList property.



As I mentioned earlier, this is only one of the possible implementations for this component-there's definitely room for improvement. Have a look at the online Help file that comes with the WinZip Command Line Support Add-on for more information.



The implementation code

There are two important methods in the implementation section I'd like to describe for you.



The first one is the procedure CreateCompressBat (see Listing 2). In this procedure, you create a batch file based on the user's requirements. It specifies the action to take (compress/decompress), the name of the file to compress, the name of the zip file, and whether you're using a list file or not.



Listing 2. The CreateCompressBat routine.

procedure TFVWinZip.CreateCompressBat;

var

  F: TextFile;

  Command: string;

begin

  try

    if FzipState = zsCompress then

      Command:= Zip

    else

      Command:= UnZip;

    { Create compress batch file }

    AssignFile(F, BatchFile);

    Rewrite(F);

    Writeln(F, '@ECHO OFF');


    Writeln(F, 'REM *** TFVWinZip by F Vicaria ***');

    if UseFileList then

      Writeln(F, Command+' "'+ZipFileName+

        '" "'+'@zipit.lst'+'"')

    else

      Writeln(F, Command+' "'+ZipFileName+

        '" "'+FileName+'"');

    Writeln(F, 'cd\');

    Writeln(F, 'cls');

  finally

    CloseFile(F);

  end;

end;



The second one is the heart of our component. The Execute method (see Listing 3) will initiate a separate process that will run the batch file, which will, in turn, call WinZip's command line. This will avoid any sort of dependency on the speed of the machine running the program. To do this, I use two API functions-CreateProcess and WaitForSingleObject (check the Windows API Help file for more details on how to use these functions).



Listing 3. The Execute routine.

procedure TFVWinZip.Execute;

var

  ProcessInfo: TProcessInformation;

  StartupInfo: TStartupInfo;

  Closed: boolean;

  OldTime: TDateTime;

begin

  if Assigned(FOnExecute) then FOnExecute(Self);

  try

    { Chech UseFileList is set or if

      FileName or is a valid file }

    if UseFileList then

    begin

      if FileList.Count= 0 then

      begin

        MessageDlg('No file to compress!',

          mtError, [mbok], 0);

        Exit;

      end

      else

        FileList.SaveToFile(ListName);

    end

    else

    begin

      if not FileExists(FileName) then

      begin

        MessageDlg(

          'Program could not find file to compress!',

          mtError, [mbok], 0);

        Exit;

      end;

    end;

    { If ZipFileName is blank use FileName

      as the default name }

    if ZipFileName='' then

    begin

      if FileName<>'' then

        ZipFileName:= ChangeFileExt(FileName,'.zip')

      else

        ZipFileName:='Untitled.zip';

    end;

    { Create batch file }

    CreateCompressBat;

    { Prepare window settings for the process }

    FillChar(StartupInfo, SizeOf(TStartupInfo), 0);

    with StartupInfo do

    begin

      cb:= SizeOf(TStartupInfo);

      dwFlags:= STARTF_USESHOWWINDOW;

      wShowWindow:= SW_MINIMIZE;

    end;

    { Execute batch file as separated process }

    CreateProcess(PChar(BatchFile), nil, nil, nil,

      False, NORMAL_PRIORITY_CLASS,

      nil, nil, StartupInfo, ProcessInfo);

    { Wait for that process to finish or

      cancel it after 10 seconds }

    Closed:= False;

    OldTime:= Now;

    repeat

      case WaitForSingleObject(

        ProcessInfo.hProcess, 100) of

          WAIT_OBJECT_0 : Closed:= True;

          WAIT_FAILED : RaiseLastWin32Error;

      end;

      Application.ProcessMessages;

    until (Closed) or (Now>OldTime+10000);

    { Delete batch file }

    DeleteFile(BatchFile);

    if UseFileList then

      DeleteFile(ListName);

  except

    MessageDlg(

      'Program could not compress file!',

      mtError, [mbok],0);

  end;

end;



After executing the batch file and, consequently, WinZip, the Execute method will get rid of all of the internally created files for you. Once the method returns, all you're left with is the Zip file you created or the files you extracted.



Finally

Using your object couldn't be easier: Just call the Execute method after creating the object and set some of its properties with the appropriate values (see the example application included).



To run this component, just call the Execute method after having filled the component's properties with the appropriate values.



The simplest option is to supply the name of the file you wish to compress (FileName) and the name of the Zip file to create(ZipFileName).



Note: If you want to compress more than one file in the same Zip file, use the FileList string list.



The Help file that comes with the command line add-on for WinZip will give you more ideas on how to improve this component and how to take full advantage of WinZip in your applications.



A couple of warnings...

You'll need to make sure that the two files that come with the WinZip Command Line Add-on (WZZIP.EXE and WZUNZIP.EXE) are in your path. If they aren't, you'll need to move them to your system directory.



Another thing to remember is that these files are free only for those who have bought WinZip and upgraded to version 7 or later. In my opinion, this can barely be considered a drawback nowadays. WinZip has become the industry standard archive utility for the Windows environment, and it's very difficult to find anyone who doesn't use it.



If you have any questions or comments, please send them to me at fvicaria@venon.com.



Files associated with this article: vicaria.zip

 

Share this article!

Follow us!

Find more helpful articles: