home *** CD-ROM | disk | FTP | other *** search
/ Night Owl 25 / nopv25.iso / 040A / DELZIP10.ZIP / VCL.ZIP / ZIPMSTR.PAS < prev   
Encoding:
Pascal/Delphi Source File  |  1997-04-07  |  43.3 KB  |  1,157 lines

  1. Unit ZipMstr;
  2. (* TZipMaster VCL by Eric W. Engler.   v1.00    April 3, 1997
  3.    A Delphi v2 wrapper for my freeware ZIP and UNZIP DLLs.  At run
  4.    time, the DLL's: ZIPDLL.DLL and UNZDLL.DLL must be present on the
  5.    hard disk - preferrably in C:\WINDOWS\SYSTEM (or else in your
  6.    application directory, or a directory in the PATH).
  7.  
  8.    The DLLs are based on the InfoZip Official Freeware Zip/Unzip code:
  9.                http://www.cdrom.com/pub/infozip/
  10.    I have customized the DLL interface especially for use with Delphi.
  11.    These customizations are fully compatible with the C/C++ language, also.
  12.  
  13.    VB users: unless version 5 of VB fixes the DLL interface issues, my
  14.    DLLs won't work for you.  Custom "helper" DLL functions would allow
  15.    the use of my DLLs with VB, but this is not my area of expertise.
  16.  
  17.    The five methods that can be invoked are:
  18.        add      - add one or more files to a ZIP archive
  19.        delete   - delete one or more files from ZIP archive
  20.        extract  - expand one or more files from a ZIP archive
  21.        list     - transfer "table of contents" of ZIP archive
  22.                   to a StringList
  23.        copyfile - copies a file
  24.  
  25.    Various properties exist to control the actions of the methods.
  26.  
  27.    Filespecs are specified in the FSpecArgs TStringList property, so you
  28.    can easily combine many different filespecs into one Add, Delete, or
  29.    Extract operation. For example:
  30.  
  31.       1. Add entries directly to the FSpecArgs property:
  32.        ZipMaster1.FSpecArgs.Add('C:\AUTOEXEC.BAT');
  33.        ZipMaster1.FSpecArgs.Add('C:\DELPHI\BIN\DELPHI.EXE');
  34.        ZipMaster1.FSpecArgs.Add('C:\WINDOWS\*.INI');
  35.  
  36.       2. Take the filespecs from a StringList, just assign them all over
  37.          to ZipMaster1.
  38.        ZipMaster1.FSpecArgs.Assign(StringList1);
  39.  
  40.       3. Take the filespecs from a ListBox, just assign them all over
  41.          to ZipMaster1.
  42.        ZipMaster1.FSpecArgs.Assign(ListBox1.Items);
  43.  
  44.    You can specify either the MS-DOS backslash path symbol, or the one
  45.    normally used by PKZIP (the Unix path separator: /).  They are treated
  46.    exactly the same.
  47.  
  48.    All of your FSpecArgs accept MS-DOS wildcards.
  49.  
  50.    Add, Delete, and Extract are the only methods that use FSpecArgs.
  51.    The List method doesn't - it just lists all files.
  52.  
  53.  
  54.    Following is a list of all TZipMaster properties, events and methods:
  55.  
  56.    Properties
  57.    ==========
  58.      Verbose      Boolean     If True, ask for the maximum amount of "possibly
  59.                               important" information from the DLLs.  The
  60.                               informational messages are delivered to your
  61.                               program via the OnMessage event, and the ErrCode
  62.                               and Message properties. This is primarily used
  63.                               to determine how much info you want to show your
  64.                               "end-users" - developers can use the Trace
  65.                               property to get additional infomation.
  66.  
  67.      Trace        Boolean     Similar to Verbose, except that this one is
  68.                               aimed at developers.  It lets you trace the
  69.                               execution of the C code in the DLLs.  Helps
  70.                               you locate possible bugs in the DLLs, and
  71.                               helps you understand why something is happening
  72.                               a certain way.
  73.  
  74.      ErrCode      Integer     Holds a copy of the last error code sent to
  75.                               your program by from DLL. 0=no error.
  76.                               See the OnMessage event.  Most messages from
  77.                               the DLLs will have an ErrCode of 0.
  78.  
  79.      Message      String      Holds a copy of the last message sent to your
  80.                               program by the DLL.  See the OnMessage event.
  81.  
  82.      ZipContents  TList       Read-only TList that contains the directory
  83.                               of the archive specified in the ZipFileName
  84.                               property. Every entry in the list points to
  85.                               a ZipDirEntry record.  This is automatically
  86.                               filled with data whenever an assignment is
  87.                               made to ZipFileName, and can be manually
  88.                               filled by calling the List method.
  89.                                  For your convenience, this VCL hides the
  90.                               TList memory allocation issues from you.
  91.                                  Automatic updates to this list occur
  92.                               whenever this VCL changes the ZIP file.
  93.                               Event OnDirUpdate is triggered for you
  94.                               each time this list is updated - that is
  95.                               your queue to refresh your directory display.
  96.  
  97.      ExtrBaseDir  String      This base directory applies only to "Extract"
  98.                               operations.  The UNZIP DLL will "CD" to this
  99.                               directory before extracting any files. If you
  100.                               don't specify a value for this property, then the
  101.                               directory of the ZipFile itself will be the
  102.                               base directory for extractions.
  103.  
  104.      Cancel       Boolean     If you set this to True, it will abort any
  105.                               Add or Extract processing now underway.  There
  106.                               may be a slight delay before the abort will
  107.                               take place.  Note that a ZIP file can be
  108.                               corrupted if an Add operation is aborted.
  109.  
  110.      ZipBusy      Boolean     If True, a ZIP operation in underway - you
  111.                               must delay your next Add/Delete operation
  112.                               until this is False.  You won't need to be
  113.                               concerned about this in most applications.
  114.  
  115.      UnzBusy      Boolean     If True, an UNZIP operation in underway -
  116.                               you must delay your next Extract operation
  117.                               until this is False.  You won't need to be
  118.                               concerned about this in most applications.
  119.  
  120.      AddOptions   Set         This property is used to modify the default
  121.                               action of the Add method.  This is a set of
  122.                               options.  If you want an option to be True,
  123.                               you need to add it to the set.  This is
  124.                               consistant with the way Delphi deals with
  125.                               "options" properties in general.
  126.  
  127.         AddDirNames           If True, saves the pathname with each fname.
  128.                               Names of empty directories in any fspec are
  129.                               also stored inside the archive.  Drive IDs
  130.                               are never stored in ZIP file directories.
  131.                               NOTE: the root directory name is never
  132.                               stored in a pathname; in other words, the
  133.                               first character of a pathname stored in the
  134.                               zip file's directory will never be a slash.
  135.  
  136.         RecurseDirs           If True, subdirectories below EACH given fspec
  137.                               will be included in the fspec. Defaults to False.
  138.                               This is potentially dangerous if the user does
  139.                               this from the root directory (his hard drive
  140.                               may fill up with a huge zip file)!
  141.  
  142.         Move                  If True, after adding to archive, delete orig
  143.                               file.  Potentially dangerous.  Use with caution!
  144.  
  145.         NOTE: You can not have more than one of the following three options
  146.               set to "True".  If all three are False, then you get a standard
  147.               "add": all files in the fspecs will be added to the archive
  148.               regardless of their date/time stamp.  This is also the default.
  149.  
  150.         AddFreshen            If True, add newer files to archive (only for
  151.                               files that are already in the archive).
  152.  
  153.         AddUpdate             If True, add newer files to archive (but, any
  154.                               file in an fspec that isn't already in the
  155.                               archive will also be added).
  156.  
  157.  
  158.      ExtrOptions  set         This property is used to modify the default
  159.                               action of the Extract method.  This is a set
  160.                               of options.  If you want an option to be
  161.                               True, you need to add it to the set.
  162.  
  163.         ExtrDirNames          If True, extracts and recreates the relative
  164.                               pathname that may have been stored with each file.
  165.                               Empty dirs stored in the archive (if any) will
  166.                               also be recreated.
  167.  
  168.         OverWrite             If True, overwrite any pre-existing files during
  169.                               Extraction.
  170.  
  171.         ExtrFreshen           If True, add newer files to archive (only for
  172.                               files that are already in the archive).
  173.  
  174.         ExtrUpdate            If True, add newer files to archive (but, any
  175.                               file in an fspec that isn't already in the
  176.                               archive will also be added).
  177.  
  178.      FSpecArgs    TStrings    Stringlist containing all the filespecs used
  179.                               as arguments for Add, Delete, or Extract
  180.                               methods. Every entry can contain MS-DOS wildcards.
  181.                               If you give filenames without pathnames, or if
  182.                               you use relative pathnames with filenames, then
  183.                               the base drive/directory is assumed to be that
  184.                               of the Zipfile.
  185.  
  186.      ZipFileName  String      Pathname of a ZIP archive file.  If the file
  187.                               doesn't already exist, you will only be able to
  188.                               use the Add method.  I recommend using a fully
  189.                               qualified pathname in this property, unless
  190.                               your program can always ensure that a known
  191.                               directory will be the "current" directory.
  192.  
  193.      Count        Integer     Number of files now in the Zip file.  Updated
  194.                               automatically, or manually via the List method.
  195.  
  196.      SuccessCnt   Integer     Number of files that were successfully
  197.                               operated on (within the current ZIP file).
  198.                               You can read this after every Add, Delete, and
  199.                               Extract operation.
  200.  
  201.      MajZipVers   ShortInt     The major version number of the ZIPDLL.DLL.
  202.  
  203.      MinZipVers   ShortInt     The minor version number of the ZIPDLL.DLL.
  204.  
  205.      MajUnzVers   ShortInt     The major version number of the UNZDLL.DLL.
  206.  
  207.      MinUnzVers   ShortInt     The minor version number of the UNZDLL.DLL.
  208.  
  209.    Events
  210.    ======
  211.      OnDirUpdate              Occurs immed. after this VCL refreshes it's
  212.                               TZipContents TList.  This is your queue to
  213.                               update the screen with the new contents.
  214.  
  215.      OnProgress               Occurs during compression and decompression.
  216.                               Intended for "status bar" or "progress bar"
  217.                               updates.  Criteria for this event:
  218.                                 - starting to process a new file (gives you
  219.                                     the filename and total uncompressed
  220.                                     filesize)
  221.                                 - every 32K bytes while processing
  222.                                 - completed processing on a batch of files
  223.  
  224.      OnMessage                Occurs when the DLL sends your program a message.
  225.                               The Message argument passed by this event will
  226.                               contain the message. If an error code
  227.                               accompanies the message, it will be in the
  228.                               ErrCode argument.
  229.                                  The Verbose and Trace properties have a
  230.                               direct influence on how many OnMessage events
  231.                               you'll get.
  232.                                  See Also: Message and ErrCode properties.
  233.    Methods
  234.    =======
  235.      Add                      Adds all files specified in the FSpecArgs
  236.                               property into the archive specified by the
  237.                               ZipFileName property. 
  238.                                 Files that are already compressed will not be
  239.                               compressed again, but will be stored "as is" in
  240.                               the archive. This applies to .GIF, .ZIP, .LZH,
  241.                               etc. files. Note that .JPG files WILL be
  242.                               compressed, since they can still be squeezed
  243.                               down in size by a notable margin.
  244.  
  245.      Extract                  Extracts all files specified in the FSpecArgs
  246.                               property from the archive specified by the
  247.                               ZipFilename property. If you don't specify
  248.                               any FSpecArgs, then all files will be extracted.
  249.  
  250.      Delete                   Deletes all files specified in the FSpecArgs
  251.                               property from the archive specified by the
  252.                               ZipFilename property.
  253.  
  254.      List                     Refreshes the contents of the archive into 
  255.                               the ZipContents TList property.  This is
  256.                               a manual "refresh" of the "Table of Contents".
  257.  
  258.      CopyFile                 This copies any file to any other file.
  259.                               Useful in many application programs, so 
  260.                               it was included here as a method.  This returns
  261.                               0 on success, or else one of these errors:
  262.                                     -1   error in open of outfile
  263.                                     -2   read or write error during copy
  264.                                     -3   error in open of infile
  265.                                     -4   error setting date/time of outfile
  266.                               Can be used to make a backup copy of the 
  267.                               ZipFile before an Add operation.
  268.                               Sample Usage:
  269.                                 with ZipMaster1 do
  270.                                 begin
  271.                                    ret=CopyFile(ZipFileName, 'C:\$$$$$.ZIP');
  272.                                    if ret < 0 then
  273.                                       ShowMessage('Error making backup');
  274.                                 end;
  275.  
  276.    Example of how to copy a file:
  277.      Showmessage('result of copyfile: ' + IntToStr(
  278.         ZipMaster1.CopyFile('C:\borlandc\bin\bcc.exe','c:\bcc.exe')));
  279.  
  280.      DLL usage for each method:
  281.        Add        ZIPDLL.DLL
  282.        Delete     ZIPDLL.DLL
  283.        Extract    UNZDLL.DLL
  284.        List         none
  285.        CopyFile     none
  286. *)
  287. interface
  288.  
  289. uses
  290.   WinTypes, WinProcs, SysUtils, Classes, Messages, Dialogs, Controls, FileCtrl,
  291.   ZipDLL, UnzDLL, ZCallBck;
  292.  
  293. type
  294.   EInvalidOperation = class(exception);
  295.  
  296. type ZipDirEntry = packed Record
  297.   Version                     : WORD;
  298.   Flag                        : WORD;
  299.   CompressionMethod           : WORD;
  300.   DateTime                    : Longint; { Time: Word; Date: Word; }
  301.   CRC32                       : Longint;
  302.   CompressedSize              : Longint;
  303.   UncompressedSize            : Longint;
  304.   FileNameLength              : WORD;
  305.   ExtraFieldLength            : WORD;
  306.   FileName                    : String;
  307. end;
  308.  
  309. type
  310.   PZipDirEntry = ^ZipDirEntry;
  311.  
  312. const
  313.   LocalFileHeaderSig   = $04034b50; { 'PK03' }
  314.   CentralFileHeaderSig = $02014b50; { 'PK12' }
  315.   EndCentralDirSig     = $06054b50; { 'PK56' }
  316.  
  317. type
  318.   ProgressType = ( NewFile, ProgressUpdate, EndOfBatch );
  319.  
  320.   AddOptsEnum = ( AddDirNames, RecurseDirs, Move, AddFreshen, AddUpdate );
  321.   AddOpts = set of AddOptsEnum;
  322.  
  323.   ExtrOptsEnum = ( ExtrDirNames, OverWrite, ExtrFreshen, ExtrUpdate );
  324.   ExtrOpts = set of ExtrOptsEnum;
  325.  
  326.   TProgressEvent = procedure(Sender : TObject;
  327.           ProgrType: ProgressType;
  328.           FileName: String;
  329.           FileSize: Longint) of object;
  330.  
  331.   TMessageEvent = procedure(Sender : TObject;
  332.           ErrCode: Integer;
  333.           Message : String) of object;
  334.  
  335.   TZipMaster = class(TWinControl) { We need a window handle for DLL }
  336.   private
  337.     { Private versions of property variables }
  338.     FVerbose:      Boolean;
  339.     FTrace:        Boolean;
  340.     FErrCode:      Integer;
  341.     FMessage:      String;
  342.     FZipContents:  TList;
  343.     FExtrBaseDir:  String;
  344.     FCancel:       Boolean;
  345.     FZipBusy:      Boolean;
  346.     FUnzBusy:      Boolean;
  347.     FAddOptions:   AddOpts;
  348.     FExtrOptions:  ExtrOpts;
  349.     FFSpecArgs:    TStrings;
  350.     FZipFileName:  String;
  351.     FSuccessCnt:   Integer;
  352.  
  353.     { misc private vars }
  354.     ZipParms1: ZipParms;     { declare an instance of ZipParms }
  355.     UnZipParms1: UnZipParms; { declare an instance of UnZipParms }
  356.  
  357.     { Event variables }
  358.     FOnDirUpdate    : TNotifyEvent;
  359.     FOnProgress     : TProgressEvent;
  360.     FOnMessage      : TMessageEvent;
  361.  
  362.     { Property get/set functions }
  363.     function  GetCount: Integer;
  364.     function  GetMajZipVers: ShortInt;
  365.     function  GetMinZipVers: ShortInt;
  366.     function  GetMajUnzVers: ShortInt;
  367.     function  GetMinUnzVers: ShortInt;
  368.     procedure SetFSpecArgs(Value : TStrings);
  369.     procedure SetFileName(Value: String);
  370.  
  371.     { Private "helper" functions }
  372.     function  AppendSlash(const sDir : String): String;
  373.     procedure FreeZipDirEntryRecords;
  374.     procedure SetZipSwitches;
  375.     procedure SetUnZipSwitches;
  376.  
  377.   public
  378.     constructor Create(AOwner : TComponent); override;
  379.     destructor Destroy; override;
  380.     procedure WMPaint (var Message : TMessage); message WM_PAINT;
  381.  
  382.     { Public Properties (run-time only) }
  383.     property ErrCode:      Integer   read FErrCode;
  384.     property Message:      String    read FMessage;
  385.     property ZipContents:  TList     read FZipContents;
  386.     property Cancel:       Boolean   read FCancel
  387.                                      write FCancel;
  388.     property ZipBusy:      Boolean   read FZipBusy;
  389.     property UnzBusy:      Boolean   read FUnzBusy;
  390.  
  391.     property Count:        Integer   read GetCount;
  392.     property SuccessCnt:   Integer   read FSuccessCnt;
  393.  
  394.     property MajZipVers:   ShortInt  read GetMajZipVers;
  395.     property MinZipVers:   ShortInt  read GetMinZipVers;
  396.     property MajUnzVers:   ShortInt  read GetMajUnzVers;
  397.     property MinUnzVers:   ShortInt  read GetMinUnzVers;
  398.  
  399.     { Public Methods }
  400.     procedure Add;
  401.     procedure Delete;
  402.     procedure Extract;
  403.     procedure List;
  404.     function CopyFile(const src, dest: String):Integer;
  405.  
  406.   published
  407.     { Public properties that also show on Object Inspector }
  408.     property Verbose:      Boolean  read FVerbose
  409.                                     write FVerbose;
  410.     property Trace:        Boolean  read FTrace
  411.                                     write FTrace;
  412.     property ExtrBaseDir:  String   read FExtrBaseDir
  413.                                     write FExtrBaseDir;
  414.     property AddOptions:   AddOpts  read FAddOptions
  415.                                     write FAddOptions;
  416.     property ExtrOptions:  ExtrOpts  read FExtrOptions
  417.                                      write FExtrOptions;
  418.     property FSpecArgs:    TStrings  read FFSpecArgs
  419.                                      write SetFSpecArgs;
  420.     { At runtime: every time the filename is assigned a value, the
  421.       ZipDir will be read.  You don't need to call ReadZipDir yourself,
  422.       unless you just want to refresh your list. }
  423.     property ZipFileName: String  read FZipFileName
  424.                                   write SetFileName;
  425.  
  426.     { Events }
  427.     property OnDirUpdate         : TNotifyEvent   read FOnDirUpdate
  428.                                                   write FOnDirUpdate;
  429.     property OnProgress          : TProgressEvent read FOnProgress
  430.                                                   write FOnProgress;
  431.     property OnMessage           : TMessageEvent  read FOnMessage
  432.                                                   write FOnMessage;
  433.   end;
  434.  
  435. procedure Register;
  436.  
  437. { The callback function must NOT be a member of a class }
  438. { We use the same callback function for ZIP and UNZIP }
  439. function ZCallback(ZCallBackRec: PZCallBackStruct): LongBool; stdcall; export;
  440. function StripJunkFromString(s: String): String;
  441.  
  442. implementation
  443.  
  444. const
  445.   LocalDirEntrySize = 26;   { size of zip dir entry in local zip directory }
  446.  
  447. { Dennis Passmore (Compuserve: 71640,2464) contributed the idea of passing an
  448.   instance handle to the DLL, and, in turn, getting it back from the callback.
  449.   This lets us referance variables in the TZipMaster class from within the
  450.   callback function.  Way to go Dennis! }
  451. function ZCallback(ZCallBackRec: PZCallBackStruct): LongBool; stdcall; export;
  452. var
  453.   Msg: String;
  454. begin
  455.    with ZCallBackRec^, (TObject(Caller) as TZipMaster) do
  456.    begin
  457.       if ActionCode = 1 then
  458.          { progress type 1 = starting any ZIP operation on a new file }
  459.          if assigned(FOnProgress) then
  460.             FOnProgress(Caller, NewFile, StrPas(FileNameOrMsg), FileSize);
  461.  
  462.       if ActionCode = 2 then
  463.          { progress type 2 = increment bar }
  464.          if assigned(FOnProgress) then
  465.             FOnProgress(Caller, ProgressUpdate, ' ', 0);
  466.  
  467.       if ActionCode = 3 Then
  468.          { end of a batch of 1 or more files }
  469.          if assigned(FOnProgress) then
  470.             FOnProgress(Caller, EndOfBatch, ' ', 0);
  471.  
  472.       if ActionCode = 4 Then
  473.          { show a routine status message }
  474.          if assigned(FOnMessage) then
  475.          begin
  476.             Msg:=StripJunkFromString(StrPas(FileNameOrMsg));
  477.             FOnMessage(Caller, ErrorCode, Msg);
  478.          end;
  479.  
  480.       { If you return TRUE, then the DLL will abort it's current
  481.         batch job as soon as it can. }
  482.       if fCancel then
  483.          result:=True
  484.       else
  485.          result:=False;
  486.     end; { end with }
  487. end;
  488.  
  489. function StripJunkFromString(s: String): String;
  490. var
  491.    EndPos: Integer;
  492. begin
  493.    { Remove possible trailing CR or LF }
  494.    EndPos:=Length(s);
  495.    if ((s[EndPos] = #13)
  496.     or (s[EndPos] = #10)) then
  497.        s[EndPos] := #0;
  498.    if EndPos > 1 then
  499.    begin
  500.       if ((s[EndPos-1] = #13)
  501.        or (s[EndPos-1] = #10)) then
  502.           s[EndPos-1] := #0;
  503.    end;
  504.    result:=s;
  505. end;
  506.  
  507. constructor TZipMaster.Create(AOwner : TComponent);
  508. begin
  509.   inherited Create(AOwner);
  510.   FZipContents:=TList.Create;
  511.   FFSpecArgs := TStringList.Create;
  512.   FZipFileName := '';
  513.   fSuccessCnt:=0;
  514.   Height:=28;  { bitmap size of pseudo-icon on form at design time }
  515.   Width:=28;
  516. end;
  517.  
  518. destructor TZipMaster.Destroy;
  519. begin
  520.   FreeZipDirEntryRecords;
  521.   FZipContents.Free;
  522.   FFSpecArgs.Free;
  523.   inherited Destroy;
  524. end;
  525.  
  526. { Paint the 24x24 bitmap onto form at design time. I call this a pseudo-icon.
  527.   We'll use the same bitmap used to paint the pseudo-icon in the VCL palette.
  528.   This code is needed bec. we're descending from TWinControl, which is pretty
  529.   high up the inheritance ladder.  We're descending from TWinControl to minimize
  530.   the VCL overhead at runtime, while still allowing us to have a window handle. }
  531. procedure TZipMaster.WMPaint (var Message : TMessage);
  532. var
  533.   PS : TPaintStruct;
  534.   MyBrush : HBrush;
  535.   MyPen : HPen;
  536.   MyBitmap : HBitmap;
  537.   bmpDC : HDC;
  538. begin
  539.   { Size of VCL "pseudo-icon" set in Create to have width and height of 28. }
  540.   { This gives us a 2 pixel 3-D border around a 24x24 bitmap. }
  541.   { This paint procedure makes the "pseudo-icon" look exactly the same as
  542.     those made automatically by Delphi 2 "heavier weight" classes. }
  543.  
  544.   { buttonhighlight = white }
  545.   { buttonface      = light gray }
  546.   { buttonshadow    = dark gray }
  547.  
  548.   BeginPaint (Handle, PS);
  549.   if csDesigning in ComponentState then
  550.   begin
  551.     { draw a black border, and fill the inside with light gray }
  552.     MyPen   := SelectObject (PS.HDC,
  553.                    GetStockObject (BLACK_PEN));
  554.     MyBrush := SelectObject (PS.HDC,
  555.                    CreateSolidBrush (GetSysColor (COLOR_BTNFACE)));
  556.     with ClientRect do
  557.        Rectangle (PS.HDC, Left, Top, Right, Bottom);
  558.  
  559.     { write a white line on left and top sides of the black border }
  560.     SelectObject(PS.HDC, CreatePen(PS_SOLID, 1,
  561.                                 GetSysColor(COLOR_BTNHIGHLIGHT)));
  562.     MoveToEx (PS.HDC, 0, ClientRect.Bottom - 2, NIL);
  563.     LineTo (PS.HDC, 0, 0);
  564.     LineTo (PS.HDC, ClientRect.Right - 1, 0 );
  565.  
  566.     { write a dark gray shadow just inside the bottom and right sides }
  567.     DeleteObject(SelectObject (PS.HDC,
  568.                       CreatePen(PS_SOLID, 1, GetSysColor(COLOR_BTNSHADOW))));
  569.     MoveToEX (PS.HDC, ClientRect.Right - 2, 1, NIL);
  570.     LineTo (PS.HDC, ClientRect.Right - 2, ClientRect.Bottom - 2);
  571.     LineTo (PS.HDC, 0, ClientRect.Bottom - 2);
  572.  
  573.     { border is done, now we'll get the bitmap inside it }
  574.     bmpDC := CreateCompatibleDC (PS.HDC);
  575.     { The name of the bitmap below must match the name used for the bitmap
  576.       in the VCL palette.  This has to be the uppercase full classname.  It
  577.       is contained in a file named ZIPMSTR.DCR (same name as this file, but
  578.       a different extension), and is automatically included by Delphi during
  579.       compilation/linking. }
  580.     MyBitmap := SelectObject (bmpDC, LoadBitmap (HInstance, 'TZIPMASTER'));
  581.     BitBlt (PS.HDC, 2, 2, 24, 24, bmpDC, 0, 0, SRCCOPY);
  582.     DeleteObject (SelectObject (bmpDC, MyBitmap));
  583.     DeleteObject (bmpDC);
  584.  
  585.     { cleanup these drawing objects }
  586.     DeleteObject (SelectObject (PS.HDC, MyBrush));
  587.     DeleteObject (SelectObject (PS.HDC, MyPen));
  588.   end;
  589.   EndPaint (Handle, PS);
  590. end;
  591.  
  592. function TZipMaster.GetMajZipVers: ShortInt;
  593. var
  594.    FMajZipVers: Word;
  595.    FMinZipVers: Word;
  596. begin
  597.    try
  598.       GetDLLVersion(@FMajZipVers, @FMinZipVers);
  599.    except
  600.       ShowMessage('Error talking to ZIPDLL.DLL');
  601.    end;
  602.    result:= ShortInt(FMajZipVers);
  603. end;
  604.  
  605. function TZipMaster.GetMinZipVers: ShortInt;
  606. var
  607.    FMajZipVers: Word;
  608.    FMinZipVers: Word;
  609. begin
  610.    try
  611.       GetDLLVersion(@FMajZipVers, @FMinZipVers);
  612.    except
  613.       ShowMessage('Error talking to ZIPDLL.DLL');
  614.    end;
  615.    result:= ShortInt(FMinZipVers);
  616. end;
  617.  
  618. function TZipMaster.GetMajUnzVers: ShortInt;
  619. var
  620.    FMajUnzVers: Word;
  621.    FMinUnzVers: Word;
  622. begin
  623.    try
  624.       { notice the trailing U on function name below }
  625.       GetDLLVersionU(@FMajUnzVers, @FMinUnzVers);
  626.    except
  627.       ShowMessage('Error talking to UNZDLL.DLL');
  628.    end;
  629.    result := ShortInt(FMajUnzVers);
  630. end;
  631.  
  632. function TZipMaster.GetMinUnzVers: ShortInt;
  633. var
  634.    FMajUnzVers: Word;
  635.    FMinUnzVers: Word;
  636. begin
  637.    try
  638.       { notice the trailing U on function name below }
  639.       GetDLLVersionU(@FMajUnzVers, @FMinUnzVers);
  640.    except
  641.       ShowMessage('Error talking to UNZDLL.DLL');
  642.    end;
  643.    result := ShortInt(FMinUnzVers);
  644. end;
  645.  
  646. { We'll normally have a TStringList value, since TStrings itself is an
  647.   abstract class. }
  648. procedure TZipMaster.SetFSpecArgs(Value : TStrings);
  649. begin
  650.    FFSpecArgs.Assign(Value);
  651. end;
  652.  
  653. procedure TZipMaster.SetFileName(Value : String);
  654. begin
  655.    FZipFileName := Value;
  656.    if not (csDesigning in ComponentState) then
  657.       List; { automatically build a new TLIST of contents in "ZipContents" }
  658. end;
  659.  
  660. function TZipMaster.GetCount:Integer;
  661. begin
  662.    if FZipFileName <> '' then
  663.       Result:=FZipContents.Count
  664.    else
  665.       Result:=0;
  666. end;
  667.  
  668. { Empty FZipContents and free the storage used for dir entries }
  669. procedure TZipMaster.FreeZipDirEntryRecords;
  670. var
  671.    i: integer;
  672. begin
  673.    if FZipContents.Count = 0 then
  674.       Exit;
  675.    for i:=FZipContents.Count-1 downto 0 do
  676.    begin
  677.       if Assigned(FZipContents[i]) then
  678.          // dispose of the memory pointed-to by this entry
  679.          Dispose(PZipDirEntry(FZipContents[i]));
  680.       FZipContents.Delete(i); // delete the TList pointer itself
  681.    end; { end for }
  682.    // The caller will free the FZipContents TList itself, if needed
  683. end;
  684.  
  685. { The Delphi code used in the List method is based on the TZReader VCL by
  686.   Dennis Passmore (Compuserve: 71640,2464).  This "list" code is also used
  687.   in the ZIPDIR VCL used by Demo3. TZReader was inspired by Pier Carlo Chiodi
  688.   pc.chiodi@mbox.thunder.it
  689. }
  690. { The List method reads thru all entries in the local Zip directory.
  691.   This is triggered by an assignment to the ZipFileName, or by calling
  692.   this method directly. }
  693. procedure TZipMaster.List;  { all work is local - no DLL calls }
  694. var
  695.   Sig: Longint;
  696.   ZipStream: TFileStream;
  697.   Res: Longint;
  698.   ZipDirEntry: PZipDirEntry;
  699.   Name: array [0..255] of char;
  700. begin
  701.   if (csDesigning in ComponentState) then
  702.      Exit;  { can't do LIST at design time }
  703.  
  704.   { zero out any previous entries }
  705.   FreeZipDirEntryRecords;
  706.  
  707.   if not FileExists(FZipFileName) then
  708.      Exit; { don't complain - this may intentionally be a new zip file }
  709.  
  710.   ZipStream := TFileStream.Create(FZipFileName,fmOpenRead);
  711.   try
  712.      while TRUE do
  713.      begin
  714.         Res := ZipStream.Read(Sig, SizeOf(Sig));
  715.         if (Res = HFILE_ERROR) or (Res <> SizeOf(Sig)) then
  716.            raise EStreamError.create('Error 1 reading Zip File');
  717.  
  718.         if Sig = LocalFileHeaderSig then
  719.         begin
  720.            {===============================================================}
  721.            { This is what we want.  We'll read the local file header info. }
  722.  
  723.            { Create a new ZipDirEntry record, and zero fill it }
  724.            new(ZipDirEntry);
  725.            fillchar(ZipDirEntry^, sizeof(ZipDirEntry^), 0);
  726.  
  727.            { fill the ZipDirEntry struct with local header info for one entry. }
  728.            { Note: In the "if" statement's first clause we're reading the info
  729.              for a whole Zip dir entry, not just the version info. }
  730.            with ZipDirEntry^ do
  731.            if (ZipStream.Read(Version, LocalDirEntrySize) = LocalDirEntrySize)
  732.            and (ZipStream.Read(Name, FileNameLength)=FileNameLength) then
  733.               FileName := Copy(Name, 0, FileNameLength)
  734.            else
  735.            begin
  736.               dispose(ZipDirEntry);  { bad entry - free up memory for it }
  737.               raise EStreamError.create('Error 2 reading Zip file');
  738.            end;
  739.            FZipContents.Add(pointer(ZipDirEntry));
  740.  
  741.            if (ZipStream.Position + ZipDirEntry^.ExtraFieldLength +
  742.             ZipDirEntry^.CompressedSize) > (ZipStream.Size - 22) then
  743.            begin
  744.               { should never happen due to presence of central dir }
  745.               raise EStreamError.create('Error 3 reading Zip file');
  746.               break;
  747.            end;
  748.  
  749.            with ZipDirEntry^ do
  750.            begin
  751.               if ExtraFieldLength > 0 then
  752.               begin
  753.                  { skip over the extra fields }
  754.                  res := (ZipStream.Position + ExtraFieldLength);
  755.                  if ZipStream.Seek(ExtraFieldLength, soFromCurrent) <> res then
  756.                     raise EStreamError.create('Error 4 reading Zip file');
  757.               end;
  758.  
  759.               { skip over the compressed data for the file entry just parsed }
  760.               res := (ZipStream.Position + CompressedSize);
  761.               if ZipStream.Seek(CompressedSize, soFromCurrent) <> res then
  762.                  raise EStreamError.create('Error 5 reading Zip file');
  763.            end;
  764.            {===============================================================}
  765.         end  { end of local stuff }
  766.  
  767.         else
  768.            { we're not going to read the Central or End directories }
  769.            if (Sig = CentralFileHeaderSig) or (Sig = EndCentralDirSig) then
  770.               break;   { found end of local stuff - we're done }
  771.      end;  { end of loop }
  772.  
  773.   finally
  774.      ZipStream.Free;
  775.   end;  { end of try...finally }
  776.  
  777.   { let user's program know we just refreshed the zip dir contents }
  778.   if assigned (FOnDirUpdate) then
  779.      FOnDirUpdate(self);
  780. end;
  781.  
  782. procedure TZipMaster.SetZipSwitches;
  783. begin
  784.    with ZipParms1 do
  785.    begin
  786.       Version:=100;    // version we expect the DLL to be
  787.       Caller := Self;  // point to our instance
  788.       ZipParms1.Handle:=Parent.Handle;
  789.       ZCallbackFunc:=ZCallback; // pass addr of function to be called from DLL
  790.  
  791.       fEncryptVerify:=False; { not supported }
  792.       fEncrypt:=False;       { not supported }
  793.  
  794.       fQuiet:=True;   { we'll report errors upon notification in our callback }
  795.       fJunkSFX:=False;      { if True, convert input .EXE file to .ZIP }
  796.       fLatestTime:=False;   { if True, make zipfile's timestamp same as newest file }
  797.       fComprSpecial:=False; { if True, try to compr already compressed files }
  798.       fSystem:=False;    { if True, include system and hidden files }
  799.       fVolume:=False;    { if True, include volume label from root dir }
  800.       fExtra:=False;     { if True, include extended file attributes }
  801.  
  802.       { fDate and Date are not yet supported }
  803.       fDate:=False;      { if True, exclude files earlier than specified date }
  804.       { Date:= '100592'; } { Date to include files after; only used if fDate=TRUE }
  805.  
  806.       fLevel:=9;       { Compression level (0 - 9, 0=none and 9=best) }
  807.       fCRLF_LF:=False; { if True, translate text file CRLF to LF (if dest is Unix) }
  808.       fForce:=False;  { if True, convert all filenames to 8x3 format }
  809.       fGrow := True;  { if True, Allow appending to a zip file (-g)}
  810.  
  811.       seven:=7;       { used to QC the data structure passed to DLL }
  812.       fDeleteEntries:=False; { distinguish bet. Add and Delete }
  813.  
  814.       if fTrace then
  815.          fTraceEnabled:=True
  816.       else
  817.          fTraceEnabled:=False;
  818.       if fVerbose then
  819.          fVerboseEnabled:=True
  820.       else
  821.          fVerboseEnabled:=False;
  822.       if (fTraceEnabled and not fVerbose) then
  823.          fVerboseEnabled:=True;  { if tracing, we want verbose also }
  824.  
  825.       if Move in fAddOptions then
  826.          fMove:=True      { dangerous, beware! }
  827.       else
  828.          fMove:=False;
  829.  
  830.       if AddFreshen in fAddOptions then
  831.          fFreshen:=True
  832.       else
  833.          fFreshen:=False;
  834.       if AddUpdate in fAddOptions then
  835.          fUpdate:=True
  836.       else
  837.          fUpdate:=False;
  838.       if fFreshen and fUpdate then
  839.          fFreshen:=False;  { Update has precedence over freshen }
  840.  
  841.       { NOTE: if user wants recursion, then he probably also wants
  842.         AddDirNames, but we won't demand it. }
  843.       if RecurseDirs in fAddOptions then
  844.          fRecurse:=True
  845.       else
  846.          fRecurse:=False;
  847.  
  848.       if AddDirNames in fAddOptions then
  849.       begin
  850.          fNoDirEntries:=False;  { we want dirnames by themselves }
  851.          fJunkDir:=False;       { we also want dirnames with filenames }
  852.       end
  853.       else
  854.       begin
  855.          fNoDirEntries:=True;  { don't store dirnames by themselves }
  856.          fJunkDir:=True;       { don't store dirnames with filenames }
  857.       end;
  858.    end; { end with }
  859. end;
  860.  
  861. procedure TZipMaster.SetUnZipSwitches;
  862. begin
  863.    with UnZipParms1 do
  864.    begin
  865.       Version:=100;    // version we expect the DLL to be
  866.       Caller := Self;  // set our instance
  867.       ZipParms1.Handle:=Parent.Handle; // pass our parent form's window handle
  868.       ZCallbackFunc:=ZCallback; // pass addr of function to be called from DLL
  869.  
  870.       if fTrace then
  871.          fTraceEnabled:=True
  872.       else
  873.          fTraceEnabled:=False;
  874.       if fVerbose then
  875.          fVerboseEnabled:=True
  876.       else
  877.          fVerboseEnabled:=False;
  878.       if (fTraceEnabled and not fVerboseEnabled) then
  879.          fVerboseEnabled:=True;  { if tracing, we want verbose also }
  880.  
  881.       fQuiet:=True;     { no DLL error reporting }
  882.       fDecrypt:=False;  { decryption - not supported }
  883.       fComments:=False; { zipfile comments - not supported }
  884.       fConvert:=False;  { ascii/EBCDIC conversion - not supported }
  885.       fTest:=False;     { test zipfile - not supported }
  886.       seven:=7;         { used to QC the data structure passed to DLL }
  887.  
  888.       if ExtrDirNames in ExtrOptions then
  889.          fDirectories:=True
  890.       else
  891.          fDirectories:=False;
  892.       if OverWrite in fExtrOptions then
  893.          fOverwrite:=True
  894.       else
  895.          fOverwrite:=False;
  896.  
  897.       if ExtrFreshen in fExtrOptions then
  898.          fFreshen:=True
  899.       else
  900.          fFreshen:=False;
  901.       if ExtrUpdate in fExtrOptions then
  902.          fUpdate:=True
  903.       else
  904.          fUpdate:=False;
  905.       if fFreshen and fUpdate then
  906.          fFreshen:=False;  { Update has precedence over freshen }
  907.    end; { end with }
  908. end;
  909.  
  910. procedure TZipMaster.Add;
  911. var
  912.   i: Integer;
  913. begin
  914.   if fFSpecArgs.Count = 0 then
  915.   begin
  916.      ShowMessage('Error - no files to zip');
  917.      Exit;
  918.   end;
  919.   if FZipBusy then
  920.      Exit;
  921.   { We must allow a zipfile to be specified that doesn't already exist,
  922.     so don't check here for existance. }
  923.   if FZipFileName = '' then   { make sure we have a zip filename }
  924.   begin
  925.      ShowMessage('Error - no zip file specified');
  926.      Exit;
  927.   end;
  928.  
  929.   { Make sure we can't get back in here while work is going on }
  930.   FZipBusy := True;
  931.   FCancel := False;
  932.  
  933.   SetZipSwitches;
  934.   with ZipParms1 do
  935.   begin
  936.       PZipFN := StrAlloc(256);  { allocate room for null terminated string }
  937.       StrPCopy(PZipFN, fZipFileName);   { name of zip file }
  938.       argc:=0;  { init to zero }
  939.  
  940.       { Copy filenames from the Stringlist to new var's we will alloc
  941.         storage for.  This lets us append the null needed by the DLL. }
  942.       for i := 0 to fFSpecArgs.Count - 1 do
  943.       begin
  944.          PFileNames[argc]:=StrAlloc(256);  { alloc room for the filespec }
  945.          StrPCopy(PFileNames[argc], fFSpecArgs[i]);  { file to add to archive }
  946.          argc:=argc+1;
  947.       end;
  948.       { argc is now the no. of filespecs we want added/deleted }
  949.    end;  { end with }
  950.  
  951.    Cursor:=crHourGlass;
  952.    try
  953.       { pass in a ptr to parms }
  954.       fSuccessCnt:=Integer(DllZipUpFiles(@ZipParms1));
  955.    except
  956.       ShowMessage('Fatal DLL Error: abort exception');
  957.    end;
  958.    Cursor:=crDefault;
  959.  
  960.    fFSpecArgs.Clear;
  961.    { Free the memory for the zipfilename and parameters }
  962.    with ZipParms1 do
  963.    begin
  964.       { we know we had a filename, so we'll dispose it's space }
  965.       StrDispose(PZipFN);
  966.       { loop thru each parameter filename and dispose it's space }
  967.       for i := 0 to argc - 1 do
  968.          StrDispose(PFileNames[i]);
  969.    end;
  970.    FCancel := False;
  971.    FZipBusy := False;
  972.    if fSuccessCnt > 0 then
  973.       List;  { Update the Zip Directory by calling List method }
  974. end;
  975.  
  976. procedure TZipMaster.Delete;
  977. var
  978.   i: Integer;
  979. begin
  980.   if fFSpecArgs.Count = 0 then
  981.   begin
  982.      ShowMessage('Error - no files selected for deletion');
  983.      Exit;
  984.   end;
  985.   if not FileExists(FZipFileName) then
  986.   begin
  987.      ShowMessage('Error - no zip file specified');
  988.      Exit;
  989.   end;
  990.   if FZipBusy then
  991.      Exit;
  992.   FZipBusy:= True;  { delete uses the ZIPDLL, so it shares the ZipBusy flag }
  993.  
  994.   SetZipSwitches;
  995.   { override "add" behavior assumed by SetZipSwitches }
  996.   ZipParms1.fDeleteEntries:=True;
  997.   ZipParms1.fGrow:=False;
  998.   ZipParms1.fNoDirEntries:=False;
  999.   ZipParms1.fJunkDir:=False;
  1000.  
  1001.   with ZipParms1 do
  1002.   begin
  1003.       PZipFN := StrAlloc(256);  { allocate room for null terminated string }
  1004.       StrPCopy(PZipFN, fZipFileName);  { name of zip file }
  1005.       argc:=0;
  1006.  
  1007.       { Copy filenames from the Stringlist to new var's we will alloc
  1008.         storage for.  This lets us append the null needed by the DLL. }
  1009.       for i := 0 to fFSpecArgs.Count - 1 do
  1010.       begin
  1011.          PFileNames[argc]:=StrAlloc(256);  { alloc room for the filespec }
  1012.          { ShowMessage(fFSpecArgs[i]); } { for debugging }
  1013.          StrPCopy(PFileNames[argc], fFSpecArgs[i]); { file to del from archive }
  1014.          argc:=argc+1;
  1015.       end;
  1016.       { argc is now the no. of filespecs we want deleted }
  1017.    end;  { end with }
  1018.  
  1019.    Cursor:=crHourGlass;
  1020.    try
  1021.       { pass in a ptr to parms }
  1022.       fSuccessCnt:=Integer(DllZipUpFiles(@ZipParms1));
  1023.    except
  1024.       ShowMessage('Fatal DLL Error: abort exception');
  1025.    end;
  1026.    Cursor:=crDefault;
  1027.  
  1028.    fFSpecArgs.Clear;
  1029.    { Free the memory }
  1030.    with ZipParms1 do
  1031.    begin
  1032.       StrDispose(PZipFN);
  1033.       for i := 0 to argc - 1 do
  1034.          StrDispose(PFileNames[i]);
  1035.    end;
  1036.    FZipBusy:=False;
  1037.    if fSuccessCnt > 0 then
  1038.       List;  { Update the Zip Directory by calling List method }
  1039. end;
  1040.  
  1041. procedure TZipMaster.Extract;
  1042. var
  1043.   i: Integer;
  1044. begin
  1045.   if FUnzBusy then
  1046.      Exit;
  1047.   { Make sure we can't get back in here while work is going on }
  1048.   FUnzBusy := True;
  1049.   FCancel := False;
  1050.  
  1051.   { Select the extract directory }
  1052.   if DirectoryExists(fExtrBaseDir) then
  1053.      SetCurrentDir(fExtrBaseDir);
  1054.  
  1055.   SetUnzipSwitches;
  1056.  
  1057.   with UnzipParms1 do
  1058.   begin
  1059.       PZipFN := StrAlloc(256);  { allocate room for null terminated string }
  1060.       StrPCopy(PZipFN, fZipFileName);   { name of zip file }
  1061.       argc:=0;
  1062.  
  1063.       { Copy filenames from the Stringlist to new var's we will alloc
  1064.         storage for.  This lets us append the null needed by the DLL. }
  1065.       for i := 0 to fFSpecArgs.Count - 1 do
  1066.       begin
  1067.          PFileNames[argc]:=StrAlloc(256);  { alloc room for the filespec }
  1068.          { ShowMessage(fFSpecArgs[i]); } { for debugging }
  1069.          StrPCopy(PFileNames[argc], fFSpecArgs[i]); { file to extr from archive }
  1070.          argc:=argc+1;
  1071.       end;
  1072.       { argc is now the no. of filespecs we want extracted }
  1073.    end;  { end with }
  1074.  
  1075.    Cursor:=crHourGlass;
  1076.    try
  1077.       { pass in a ptr to parms }
  1078.       fSuccessCnt:=Integer(DLLProcessZipFiles(@UnZipParms1));
  1079.    except
  1080.       ShowMessage('Fatal DLL Error: abort exception');
  1081.    end;
  1082.    Cursor:=crDefault;
  1083.  
  1084.    fFSpecArgs.Clear;
  1085.    { Free the memory }
  1086.    with UnZipParms1 do
  1087.    begin
  1088.       StrDispose(PZipFN);
  1089.       for i := 0 to argc - 1 do
  1090.          StrDispose(PFileNames[i]);
  1091.    end;
  1092.    fFSpecArgs.Clear;
  1093.    FCancel := False;
  1094.    FUnzBusy := False;
  1095.    { no need to call the List method; contents unchanged }
  1096. end;
  1097.  
  1098. function TZipMaster.AppendSlash(const sDir : String): String;
  1099. begin
  1100.   Result := sDir;
  1101.   if (Length(sDir)>0) and (sDir[Length(sDir)]<>'\') then
  1102.      Result := Result+'\';
  1103. end;
  1104.  
  1105. { returns 0 if good copy, or a negative error code }
  1106. function TZipMaster.CopyFile(const src, dest: String): Integer;
  1107. Const
  1108.    SE_CreateError   = -1;  { error in open of outfile }
  1109.    SE_CopyError     = -2;  { read or write error during copy }
  1110.    SE_OpenReadError = -3;  { error in open of infile }
  1111.    SE_SetDateError  = -4;  { error setting date/time of outfile }
  1112. Var
  1113.    S,T: TFileStream;
  1114. Begin
  1115.    Result := 0;
  1116.    try
  1117.       S := TFileStream.Create( src, fmOpenRead );
  1118.    except
  1119.       Result:=SE_OpenReadError;
  1120.       exit;
  1121.    end;
  1122.  
  1123.    try
  1124.       T := TFileStream.Create( dest, fmOpenWrite or fmCreate );
  1125.    except
  1126.       Result := SE_CreateError;
  1127.       S.Free;  { S was already made - free it }
  1128.       exit;
  1129.    end;
  1130.  
  1131.    try
  1132.       T.CopyFrom(S, S.Size ) ;
  1133.    except
  1134.       Result := SE_CopyError;
  1135.       S.Free;
  1136.       T.Free;
  1137.       exit;
  1138.    end;
  1139.  
  1140.    try 
  1141.       FileSetDate(T.Handle, FileGetDate( S.Handle ));
  1142.    except
  1143.       Result := SE_SetDateError;
  1144.    end;
  1145.  
  1146.    S.Free;
  1147.    T.Free;
  1148. End;
  1149.  
  1150. procedure Register;
  1151. begin
  1152.   RegisterComponents('Samples', [TZipMaster]);
  1153. end;
  1154.  
  1155. end.
  1156.  
  1157.