Zip it, Zip it Good Doug Hennig

Similar documents
pdating an Application over the Internet, Part II Doug Hennig

May I See Your License? Doug Hennig

Manage Your Applications Doug Hennig

The Mother of All TreeViews, Part 2 Doug Hennig

Splitting Up is Hard to Do Doug Hennig

uilding Your Own Builders with BuilderB Doug Hennig and Yuanitta Morhart

A File Open Dialog Doug Hennig

Taking Control Doug Hennig

Data-Drive Your Applications Doug Hennig

A New Beginning Doug Hennig

Extending the VFP 9 IDE Doug Hennig

Role-Based Security, Part III Doug Hennig

IntelliSense at Runtime Doug Hennig

The Best of Both Worlds

CATCH Me if You Can Doug Hennig

Christmas Stocking Stuffers Doug Hennig

Report Objects Doug Hennig

Data Handling Issues, Part I Doug Hennig

The Happy Project Hooker Doug Hennig

Putting Parameters in Perspective Doug Hennig

Base Classes Revisited Doug Hennig

I Got Rendered Where? Part II Doug Hennig

A Generic Import Utility, Part 2

More Flexible Reporting With XFRX Doug Hennig

anguage Enhancements in VFP 7, Part V Doug Hennig

Web Page Components Doug Hennig

Data Handling Issues, Part II Doug Hennig

Advanced Uses for Dynamic Form

FoxTalk. GOING through the classes provided in the FoxPro. More Gems in the FFC. Doug Hennig 6.0

Much ADO About Something Doug Hennig

Cool Tools by Craig Boyd, Part II Doug Hennig

Custom UI Controls: Splitter

A File Open Dialog Box Doug Hennig

Advisor Answers. Clean up a Project. May, Visual FoxPro 9/8/7

anguage Enhancements in VFP 7, Part I Doug Hennig

VFP: Ideal for Tools, Part 3

A New IDE Add-on: FoxTabs Doug Hennig

Extending the VFP 9 Reporting System, Part I: Design-Time

Access and Assign Methods in VFP

Making the Most of the Toolbox

Make Thor Your Own. Tamar E. Granor Tomorrow's Solutions, LLC Voice:

## Version: FoxPro 7.0 ## Figures: ## File for Subscriber Downloads: Publishing Your First Web Service Whil Hentzen

Introducing Project Explorer

Zipping and Unzipping for Clients, Part 2 Richard David Hodder

Windows Event Binding Made Easy Doug Hennig

Creating Explorer Interfaces in Visual FoxPro

Speed in Object Creation and. Destruction. March 2016 Number 49. Tamar E. Granor, Ph.D.

Drilling Down Into Your Data Doug Hennig

Multi-User and Data Buffering Issues


Give Thor Tools Options

How To Upload Your Newsletter

Working with the Registry. The Registry class makes it easy. The Registry Structure. January, By Tamar E. Granor

Handling crosstabs and other wide data in VFP reports

Session V-STON Stonefield Query: The Next Generation of Reporting

CHAPTER 1 COPYRIGHTED MATERIAL. Finding Your Way in the Inventor Interface

Access Intermediate

Knowledgebase Article. Queue Member Report. BMC Remedyforce

Google Drive: Access and organize your files

Chapter 18 Outputting Data

QuickBooks 2008 Software Installation Guide

Excel Basics: Working with Spreadsheets

Xchange for Samsung MAC User Guide. Version 2.4

4Sight for Mac User Guide. Version 2.4

Excel Basics Rice Digital Media Commons Guide Written for Microsoft Excel 2010 Windows Edition by Eric Miller

Give users a control that makes entering dates as easy as it is in Intuit Quicken.

A Deep Dive into the VFPX ThemedControls

Try Thor s Terrific Tools, Part 2

Script.byu.edu SharePoint Instructions

XP: Backup Your Important Files for Safety

1 Introduction Move Scale Squash or skew Rotate Transparency Colour transform

When. you.) This. not available. organization. If it s. might

GUARD1 PLUS Manual Version 2.8

[Compatibility Mode] Confusion in Office 2007

For Volunteers An Elvanto Guide

In this chapter, I m going to show you how to create a working

Keep Track of Your Passwords Easily

Instructions for Using the Databases

2013 edition (version 1.1)

How To Get Your Word Document. Ready For Your Editor

Social Sharing. Facebook

Download Free Pictures & Wallpaper from the Internet

Keeping Sane - Managing your

IBM Atlas Suite Users Guide: Data Source Maintenance with IIM. for IBM Atlas Suite v6.0

Custom Fields in QuickBooks

Training Tracker 6. PC Edition. Copyright Computer Directions, LLC

NTP Software Storage Investigator TM User Guide

NetBackup 7.6 Replication Director A Hands On Experience

Oracle SQL. murach s. and PL/SQL TRAINING & REFERENCE. (Chapter 2)

MAPLOGIC CORPORATION. GIS Software Solutions. Getting Started. With MapLogic Layout Manager

What is version control? (discuss) Who has used version control? Favorite VCS? Uses of version control (read)

HIGH-IMPACT SEO DIY IN 5 MINUTES SEO OR LESS. Digital Marketer Increase Engagement Series

Installing Dolphin on Your PC

Setting up ODBC, Part 2 Robert Abram

seminar learning system Seminar Author and Learning System are products of Information Transfer LLP.

Style Report Enterprise Edition

Quick Start Guide - Contents. Opening Word Locating Big Lottery Fund Templates The Word 2013 Screen... 3

Multiple Variable Drag and Drop Demonstration (v1) Steve Gannon, Principal Consultant GanTek Multimedia

Template Tidbits. Q How do I get the places I can enter copy to show up? (Highlight Fields Bar)

Custom SharePoint Workflows

Transcription:

Zip it, Zip it Good Doug Hennig This month s article presents a class to zip and pack the files in a project, and a class to interface VFP with a zipping utility like WinZip. Combining these classes with a ProjectHook and a project toolbar helps improve your productivity. In my column in the September 1998 issue of FoxTalk ( The Happy Project Hooker, a article for which I received way more comments about the title than the article itself <g>), I discussed the ProjectHook class, a welcome addition to VFP 6 that makes it easier to build project-related tools for developers. I also presented several ProjectHook classes that provided functionality like build buttons in a toolbar, text searching, and restoring project-specific settings like Intellidrop classes and form templates. I mused that other features would be useful, like packing the table-based source files (SCX, VCX, FRX, and MNX) in a project or creating a zip file for archive or transportation purposes. Since I ve actually built these functions, it s time to present these as reusable developer tools. As a refresher, SFPROJ.VCX contains several ProjectHook classes. SFProjectHook is the base ProjectHook class; it contains core functionality like chaining to hooked ProjectHook (or other) objects and implementing a toolbar if desired. SFProjectHookFind is a subclass that implements text search capabilities; it collaborates with SFFindText, a form that displays where the specified text was found. SFProjectHookRegistry saves and restores project-specific settings, making it easy to switch between projects. SFProjectToolbar, SFProjectToolbarTimer, and several SFToolbarButton classes provide a toolbar that works with a ProjectHook object and automatically releases itself when the project is closed. SFProjectUtils Rather than creating another ProjectHook subclass for project packing and zipping, I decided to create a project utilities class (SFProjectUtils) based on Custom instead. Although this class is in SFPROJ.VCX like the ProjectHook classes, I wanted this class to be used for projects that aren t necessarily open in the Project Manager, so I didn t need the functionality of a ProjectHook. The reason I based it on Custom rather than SFCustom (our Custom base class in SFCTRLS.VCX) is that having SFCTRLS.VCX open (which happens when you instantiate a subclass of one of its classes, even when that subclass is in a different VCX) when trying to pack or zip a project that includes this file causes problems. Just because it isn t based on ProjectHook doesn t mean it can t be used from within a ProjectHook; that s in fact what we ll do later in this article. SFProjectUtils has two main methods, and several supporting methods. The main methods are ZipProject, which creates a zip file of the files in a project, and PackProject, which packs the table-based source files in a project. The supporting methods, which we won t look at due to space limitations, are GetFilesFromProject, which fills an array with the files in a project, GetDateTime, which combines the date and time values from an array filled with ADIR() into a DateTime value, and GetAssociatedFile, which returns the name of a file associated with the specified file (for example, passing TEST.SCX to GetAssociatedFile will return TEST.SCT). SFProjectUtils has several properties, the two important ones being cproject, which contains the name of the project file we re working with, and ozip, which contains a reference to an object that ll actually perform the zipping work for us. Let s look at PackProject first, since it s the simpler of the two main methods. I like to use this method before I build an EXE or archive a project because, as you probably know, the memo file of table-based source code files (such as VCTs) can get huge with discarded data as you make changes to it. Although the Project Manager will compact these files when you build with the Recompile All Files option chosen, I don t use that option often. As a result, the EXE or archive can be bloated with garbage. PackProject provides a fast way to trim the files down to size. The major code in this method gets a list of files in the project, opens those that are table-based (the file type is K [screen], V [VCX], R [report], B [label], or M [menu]) exclusively, and packs them. lnfiles = This.GetFilesFromProject(@laFiles, lcproject) for lni = 1 to lnfiles if lafiles[lni, 2] $ 'KVRBM'

lcname = lafiles[lni, 1] if file(lcname) wait window 'Processing ' + lcname + '...' nowait use (lcname) exclusive pack use endif file(lcname) endif lafiles[lni, 2] $ 'KVRBM' next lni wait clear PACKPROJ.PRG is a simple utility program you can call to pack the files in a project. Pass it the name of the project to pack and it ll let SFProjectUtils take care of the work. The ZipProject method zips the files in a project. I use it both to archive the project once it s completed and for transportation purposes (taking a copy of the files home or giving them to another developer). ZipProject accepts two parameters: the name of the zip file to create and, because you may not want to zip all the files in a project, an expression to filter the files to zip. We ll look at how the filter expression is used in a moment. We won t examine all the code in this method, just the important stuff. First, we get a list of files in the project, the directory the project is in, and the name of the APP or EXE file created from the project (if it exists). with This lcproject =.cproject if empty(lcproject) return.f. endif empty(lcproject) * Get a list of files in the project. lnfiles =.GetFilesFromProject(@laFiles, lcproject) * Get the directory and name of the APP file. lcdirectory = addbs(justpath(lcproject)) lcappfile = forceext(lcproject, 'APP') if not file(lcappfile) lcappfile = forceext(lcproject, 'EXE') endif not file(lcappfile) Next, we put information about the project file into an object and pass that object to the ShouldFileBeZipped method, which returns.t. if the filter expression is either empty (meaning there is no filter) or evaluates to.t. If it does, the AddFile method of the ozip object adds the PJX and PJT files to the list of files to zip. If the APP or EXE file created from the project exists, it s treated the same way. adir(ladir, lcproject) lofile.cfilename = lcproject lofile.ctype = 'p' lofile.nfilesize = ladir[2] lofile.tmodified =.GetDateTime(laDir[3], ladir[4]) lofile.cattributes = ladir[5] if.shouldfilebezipped(lofile, tcfilterexpr).ozip.addfile(lcproject).ozip.addfile(forceext(lcproject, 'PJT')) endif.shouldfilebezipped(lcproject,... if file(lcappfile) adir(ladir, lcappfile) lofile.cfilename = lcappfile lofile.ctype = '' lofile.nfilesize = ladir[2] lofile.tmodified =.GetDateTime(laDir[3], ladir[4]) lofile.cattributes = ladir[5] if.shouldfilebezipped(lofile, tcfilterexpr).ozip.addfile(lcappfile) endif.shouldfilebezipped(lofile, tcfilterexpr) endif file(lcappfile)

Each of the files in the project is also added to the list of files to zip if it passes the filter criteria. Because a file may have one or two associated files (for example, DCX and DCT files accompany a DBC file), the GetAssociatedFile method is called to ensure all necessary files are included. for lni = 1 to lnfiles lcfile = lafiles[lni, 1] lctype = lafiles[lni, 2] lofile.cfilename = lcfile lofile.ctype = lctype lofile.nfilesize = lafiles[lni, 3] lofile.tmodified = lafiles[lni, 4] lofile.cattributes = lafiles[lni, 5] if.shouldfilebezipped(lofile, tcfilterexpr).ozip.addfile(lcfile) for lnj = 1 to 2 lcfile =.GetAssociatedFile(lcFile, lctype, lnj) if not empty(lcfile).ozip.addfile(lcfile) endif not empty(lcfile) next lnj endif.shouldfilebezipped(lcfile,... next lni Finally, if any of the files passed the filter criteria, we close the project file if necessary (we can t zip it if it s open in the Project Manager), have the ozip object do the zipping work, and reopen the project if we closed it. if.ozip.nfiles > 0 loproject = iif(type('_vfp.projects[lcproject].' + ; 'Name') = 'C', _vfp.projects[lcproject],.null.) llopen = vartype(loproject) = 'O' if llopen loproject.close() endif llopen llreturn =.ozip.zipfiles(tczipfile) if llopen modify project (lcproject) nowait endif llopen endif.ozip.nfiles > 0 endwith return llreturn Before we look at the zipping object, let s take a look at an example of how ZipProject might be called. I like to create two zip files for each project. One, called SOURCE, contains the project file, APP or EXE, and all the project-specific source code. The other, called LIBRARY, contains the library (or non-projectspecific) source code used in the project. The reason for zipping LIBRARY is that sometimes you may need to restore an old project to do maintenance work on it but you ve since updated the library files it uses to a later version. Rather than trying to figure out what changes in the library files will cause havoc in your old project, it s easier to just use the snapshot of the library files as they were when the project was archived. ZIPPROJ.PRG (included with this month s Subscriber downloads) accomplishes this goal. Pass it the name of the project to zip and it ll create SOURCE.ZIP and LIBRARY.ZIP. The interesting part of this program is the following code (loproject is a reference to the SFProjectUtil object and lcproject contains the name of project to process): pcdirectory = addbs(justpath(lcproject)) loproject.zipproject(pcdirectory + 'source.zip', ; 'ZipSourceFile(toFile)') loproject.zipproject(pcdirectory + 'library.zip', ; 'ZipLibraryFile(toFile)') Notice that for the filter expressions, this code passes function names. These functions are located in ZIPPROJ.PRG, which means that the SFProjectUtil object in effect calls back to ZIPPROJ to figure out which files should be zipped. The ZipSourceFile function will allow the project file (tofile.ctype = p ), application file (tofile.ctype is blank), or any file in the project directory or a subdirectory of it to be

zipped, while ZipLibraryFile will only allow files that aren t in the project directory or a subdirectory of it to be zipped: function ZipSourceFile lparameters tofile return tofile.ctype = 'p' or empty(tofile.ctype) or ; addbs(justpath(tofile.cfilename)) = pcdirectory function ZipLibraryFile lparameters tofile return addbs(justpath(tofile.cfilename)) <> pcdirectory What if you don t want this functionality? Easy: change the filter conditions. Mike Yearwood (who I owe thanks to, both for the following code and for helping make the classes in this article better) wanted a single zip file containing only files changed since the last time the project was zipped so he could send just those files to someone else. MIKEZIPPER.PRG uses a table that contains the date and time the project was last zipped (which is placed into the ptlasttimezipwasmade variable), and uses this as the filter function: function ZipSourceFile lparameters tofile return tofile.tmodified > ptlasttimezipwasmade SFZipUtils and SFZipUtilsWinZip Now let s look at the classes that handle the zipping process for us. Because the exact process of how to zip files depends on what tool you use (several tools are available, including WinZip, PKZip, DynaZip, and several free or shareware utilities), I created an abstract class called SFZipUtils (based on Custom rather than SFCustom for the same reason I outlined earlier) that has the framework for a zipping class and leaves the implementation to a subclass. SFZipUtils has an AddFile method that adds a filename to the protected afiles array. Call this method, passing it the name and path of the file, for each file to be zipped. The nfiles property contains the number of files to be zipped; it has an assign method that makes it read-only and an access method that calculates the value for the property. The Clear method simply clears the afiles array; call this method after zipping some files when you want to zip a different set of files. The czipfile property contains the name of the zip file to create; it has an assign method to ensure a valid filename is entered. The luserelativepaths property indicates whether relative paths should be used in the zip file; set it to.f. to use absolute paths. The ZipFiles method uses a Template design pattern to create the zip file. It doesn t do a lot of work itself; rather, it directs other methods to do the work. The following is a stripped down version of the code for space reasons (see the source code for the complete version): lparameters tczipfile with This * Hook method before the list of files to zip has been * created..beforeaddfilestozip() * Add each file to the zip file. lnfiles =.nfiles for lni = 1 to lnfiles lcfile =.afiles[lni] if.luserelativepaths lcfile = sys(2014, lcfile, lczipfile) endif.luserelativepaths.addfiletozip(lcfile) next lni * Hook method after the list of files to zip has been * created..afteraddfilestozip()

* Hook method before the zip file has been created..beforecreatezipfile() * Ensure we're in the directory where the zip file * should be created (it works better if we're using * relative file paths) and zip the files. lccurdir = sys(5) + curdir() cd (justpath(lczipfile)) llreturn =.CreateZipFile(lcZipFile) cd (lccurdir) * Hook method after the zip file has been created..aftercreatezipfile() endwith return llreturn The BeforeAddFilesToZip, AfterAddFilesToZip, BeforeCreateZipFile, and AfterCreateZipFile methods called from ZipFiles are hook methods; they don t have any code in this class, but can be used to place additional code at the appropriate place in the process in a subclass. AddFileToZip and CreateZipFile are also empty in this class, but they re not hook methods; they re abstract methods that must be coded in a subclass to provide the behavior for the zipping mechanism used. Since I have WinZip (from Nico Mak Computing Inc, www.winzip.com), I created a subclass of SFZipUtils called SFZipUtilsWinZip that uses WinZip to create the zip file. This subclass creates a file containing the list of files to be zipped (the name of this file is passed to WinZip as we ll see later), so the BeforeAddFilesToZip and AfterAddFilesToZip methods were overridden to create and close a textmerge file, respectively. AddFileToZip was overridden to output the name of the specified file to the textmerge file: lparameters tcfile \\<<tcfile + chr(13) + chr(10)>> The CreateZipFile method, which actually creates the zip file, calls WINZIP.EXE (the actual name and path to the program are stored in the czipprogram property) to do the work. WinZip is passed the following parameters: -min to run minimized, -a to add files, -p to save directory information (rather than just the filename itself), the name of the zip file, and @<filename>, where <filename> is the name of the textmerge file (stored in the cziplist property) containing the list of files to zip. Initially, I had this code call WINZIP.EXE using the VFP RUN command. However, the problem with this is that RUN returns immediately even though WinZip hasn t finished executing. I wanted to wait until it was done to be sure the zip file was created, so I used a great public domain class called Process (included in this month s Subscriber downloads), written by Ed Rauh of esolutions Services, LLC. Set the iccommandline property of this class to the command line to execute and the icwindowmode property to the mode for the application ( HID means hidden; see the Process class for other choices), then call the LaunchAppAndWait method to execute the application and wait until it terminates before continuing. The Init method of SFZipUtilsWinZip instantiates Process into the oprocess property. OK, now that this description took way more space than the actual code for CreateZipFile <g>, here s the code: lparameters tczipfile with This.oProcess.icCommandLine =.czipprogram + ; ' -min -a -p ' + tczipfile + ' @' +.cziplist.oprocess.icwindowmode = 'HID'.oProcess.LaunchAppAndWait() endwith Before using SFZipUtilsWinZip, don t forget to set the czipprogram property to the path to WINZIP.EXE on your system. Adding to a ProjectHook

To make it easier to access the methods in SFProjectUtils, I created a subclass of SFProjectHookFind (I used this class because I also want text search capability) called MyProjectHook (in SFPROJECT.VCX) that instantiates SFProjectUtils into its outils property and has PackProject and ZipFiles methods that call outils.packproject and outils.zipproject, respectively. It also has ZipSourceFile and ZipLibraryFile methods with the same code as the functions in ZIPPROJ.PRG so file filtering creates the desired zip files. I created a subclass of SFProjectToolbar called MyProjectToolbar (also in SFPROJECT.VCX) that has buttons for building an APP or EXE, checkboxes for various build options, and buttons to perform a text search or zip or pack the project (see Figure 1). These buttons call the appropriate methods in MyProjectHook, which instantiates MyProjectToolbar because its ctoolbarclass property contains MyProjectToolbar. Figure 1. MyProjectToolbar provides fast access to project functions. Conclusion Project utilities like those I presented in my September 1998 article and this article make working with project files a lot easier. I use these utilities all the time, and hope you find them as useful as I do. Doug Hennig is a partner with Stonefield Systems Group Inc. in Regina, Saskatchewan, Canada. He is the author or co-author of Stonefield s add-on tools for FoxPro developers, including Stonefield Database Toolkit, Stonefield Query, and Stonefield Reports. He is also the author of The Visual FoxPro Data Dictionary in Pinnacle Publishing s The Pros Talk Visual FoxPro series. Doug has spoken at the 1997, 1998, and 1999 Microsoft FoxPro Developers Conferences (DevCon) as well as user groups and regional conferences all over North America. He is a Microsoft Most Valuable Professional (MVP) and Microsoft Certified Professional (MCP). He can be reached at dhennig@stonefield.com.