I’m writing this on my birthday (July 26). Never mind exactly how old I am–I don’t like to think about it.
When I was younger, I looked forward to getting older. But one thing I have noticed is that nature plays several cruel jokes on you as you get older.
Take a little time to decompress, with some help from the InfoZip free decompression libraries for C++.
The one that bothers me the most: Time goes faster as you get older.
Think back to when you were a kid. The time between when you went into second grade and the following summer was an eternity.
Even the summers seemed to take forever (thank goodness). When you get older, the years whiz by at an alarming rate. Remember how long four years seemed like when you entered college? Now four years is just a blip.
I’m not sure what this really means. While I read Stephen Hawking’s A Brief History of Time, I wondered if time was like a funnel and we swirl along going faster and faster until we fall through the hole at the bottom.
I’ve read that there is a simpler explanation. When you are 10 years old, a year is 10% of your life. At 40, it is only 2.5% of your life. But I prefer to visualize it as my life just going down the proverbial drain.
This is one of the few times in life that less is definitely not more. Usually, though, you want less. People want to weigh less, work less, and spend less.
With computing there is an impetus to write programs that take less time to run, use less memory, and cost less. Of course, today’s computers have lots of memory and disk space, so this isn’t as big a problem as it used to be, right?
For the desktop PC, you aren’t as constrained as you used to be, when it comes to memory and disk space.
However, as more users become sophisticated enough to run many programs at once, you should consider economizing as much as possible.
As PCs shrink (like the Windows CE palm top I got for my birthday), they may have a reduced amount of memory too.
One thing that smart developers have used for some time to make leaner programs is compression. Sometimes compression occurs at a level that is hidden to programmers.
For example, some network protocols and modems automatically compress data so that it transports more quickly. Products like DriveSpace compress hard drives on the fly to make them appear bigger.
The problem with transparent compression is that some data compresses better than others. Text, for example, is highly compressible.
Files that are already compressed (JPEG or ZIP files, for instance) are not very compressible. When you compress everything, you waste some time trying to compress things that are better left uncompressed.
Table 1 | ||
Arguments for Wiz_SingleEntryUnzip. | ||
Argument | Type | Description |
ifnc | int | Number of file names to extract (0 for all files). |
ifnv | char** | Pointer to array of file names to extract (NULL for all files). |
xfnc | int | Number of file names to exclude (0 to exclude no files). |
xfnv | char** | Pointer to array of file names to exclude (NULL if no exclusions). |
lpDCL | DCL* | Pointer to DCL structure; specifies zip file name and options (see Table 2). |
lpUserFunc | USERFUNCTIONS* | Pointer to user functions (see Table 3). |
In this installment of “Windows Commando,” I’ll show you how a project known as Info-Zip can help you add ZIP-style compression to your own programs.
Info-Zip is an open-source group (similar to GNU or Linux) that creates programs and libraries to handle ZIP files.
These libraries are free (with certain limitations) and have been used in products as diverse as OS/2, HotJava, and PGP (Pretty Good Privacy). Info-Zip’s work runs on platforms ranging from Windows and Windows CE to TOPS20, VMS, MVS, and the Acorn.
Table 2 | ||
The DCL structure. | ||
Field | Type | Description |
ExtractOnlyNewer | int | Set to 1 to only extract newer files. |
SpaceToUnderscore | int | Set to 1 to convert spaces to underscores in names. |
PromptToOverwrite | int | Set to 1 to prompt before overwriting. |
fQuiet | int | 0=print all messages; 1=print few messages; 2=print no messages (still prints warnings) |
ncflag | int | Set to 1 to write to STDOUT (using helper function). |
ntflag | int | Set to 1 to test zip file. |
nvflag | int | Set to 1 for verbose listing. |
nUflag | int | Set to 1 for update mode. |
nzflag | int | Set to 1 if code should display zip comment. |
ndflag | int | Set to 1 to treat all arguments as file names. |
noflag | int | Set to 1 to always overwrite existing files. |
naflag | int | Set to 1 to translate end of line between DOS/Unix standards. |
nZIflag | int | If 1, get zip info. |
C_flag | int | If 1, work without case sensitivity. |
fPrivilege | int | Set to 1 to restore ACLs. |
lpszZipFN | LPSTR | Zip file name. |
lpszExtractDir | LPSTR | Directory to receive files (NULL for current directory). |
For Windows, you can even have the code in a DLL and use it without having to include the source in your own program.
The source code is available, if you need to understand what the code is doing or want to modify it for your own purposes. You can find Info-Zip on the Web at www.cdrom.com/ pub/infozip.
Using Compression
The Info-Zip tools make it a little difficult to get started. With so much source code, it often isn’t clear exactly what you want to get.
In addition to the DLL code (separate for zipping and unzipping), there are several end-user programs that use the libraries or DLLs.
Besides that, Info-Zip makes updates, so referring you to a specific file name here wouldn’t work; it would be out of date before you could read it.
Suffice it to say that you will have to read a bit on the Web site and download a few files to find all the correct pieces.
The documentation for the DLLs seems somewhat sparse, but it is there, along with a nice short example program that uses the DLLs.
In addition, you have all the source code, so you can usually look through it and find out the answer to any particular question.
To see how this all works, consider the unzipping DLL, which is the only one I’ll use in this article. It has several different ways you can use it.
One way is to call Wiz_SingleEntryUnzip. This call takes several parameters. (See Tables 1, 2, and 3.) However, it does the entire unzip operation for you.
You can ask it to do a listing, extract certain files, and specify all the options you might need.
Table 3 | |
The USERFUNCTIONS structure. | |
Field | Description |
Function that prints all output. | |
sound | Function that makes sounds (optional). |
replace | Prompts user for replacement. |
password | Handles password archives. |
SendApplicationMessage | Points to function used during listing. |
ServCallBk | Allows application to handle messages. |
In addition to specifying options, you can supply helper functions for the DLL. For example, the DLL never prints anything to the screen– after all, in Windows, there may not be a console for the program.
Instead, it calls a user-supplied function to do the printing for it. (See Table 3.)
The helper functions allow you to completely customize the operation of the DLL. However, you can also make finer-grained calls into the DLL to further control what happens.
For example, you can call Wiz_Init followed by Wiz_Unzip and Wiz_UnzipToMemory, if you want to unzip a file to a memory buffer instead of to a disk file.
A Sample Program
Just to experiment with the Info-Zip DLL, I decided to try writing a simple program that displays pages of text, like a multi-page readme file. I had a only a few design goals in mind:
I wanted multiple pages contained in a single file.
Since text is very compressible, I also wanted to compress that file so it was as small as possible.
Figure 1: Displaying a page of compressed text.
The result is shown in Figure 1 and in Listings 1 and 2. Since ZIP archives can contain multiple files, the architecture of the program is quite simple.
You prepare the file by simply using a standard ZIP utility to create the archive. Each file within is named for its page number; for example, 1.TXT, 2.TXT, 3.TXT, etc. Once the ZIP archive is ready, you rename it to use the PGD extension, and you are ready to go.
You can run the program with the name of the archive on the command line, or you can use the File | Open command. Either way, you’ll see the first page and have the option to page forwards and backwards.
From a Windows programming standpoint, the program was not hard to write. A simple session with MFC’s App Wizard created a CFormView SDI program–nothing special about that. However, integrating it with the Info-Zip DLL did have some tricky spots that gave me a little trouble.
Starting with Info-Zip
After I downloaded all the files that I thought I would need to get the unzip DLL, it took a while to navigate through the many files that showed up on my hard drive.
I was never able to find LIB files (or even a single header file) that corresponded to the two DLLs. I did find the documentation files and all the pieces required to build the DLLs.
I downloaded the Windows binaries for WIZ, the end-user DLLs. However, they were linked with Borland C++ and I decided to go ahead and rebuild them.
The problem, of course, is that Info-Zip’s make files were made by Visual Studio and had many hard-coded path names in them.
Naturally, none of these paths matched the paths on my machine. Twenty minutes later, I got a clean compile.
Armed with DLLs and a LIB file, I was off and running. I noticed that all the examples from Info-Zip dynamically load the DLL and don’t use the LIB file. I decided I’d do the same, just for practice.
I decided that I wouldn’t worry about passwords, progress indicators, and other user interface items, so my helper functions are mostly stubs. That’s a good thing too.
The helper functions are only loosely documented unless you dig into the source, where the comments are helpful. The only helper I was really implementing was the print helper.
My theory was simple: Write my own print handler and tell the library to extract to standard output. In addition, I’d use the fQuiet flag to turn off messages.
My print routine would then route all the text to the edit control on the main form. Sounds simple, right?
The only problem is that fQuiet doesn’t prevent the library from issuing warnings, just informational messages.
My program worked until I tried to move past the last page. Then I saw the library error message instead of the last page’s text.
The answer turned out to be simple. I altered the program so that the print routine stored the outbound text in a CString.
Then I checked the return value from the library to see if an error occurred. If there was an error, I discarded the string. If there was no error, I placed the string in the edit control.
All the real work occurs in the document object’s showpage routine. Here is where the app uses the unzip library to extract the correct file.
It would be easy enough to statically link to the import library for the DLL instead of dynamically loading it, but instead, the program dynamically links the DLL in the document constructor.
It is tempting to look for other improvements in efficiency. For example, at first glance it appears that it’s wasteful to keep finding the edit control window during each call to showpage.
Why not find it once and cache the resulting CEdit pointer for later use? That sounds good– but there is a problem.
When you call GetDlgItem, the pointer it returns is not a permanent MFC object. Instead, it is a temporary object that lives during the current message processing. So if you store this pointer away, later it will not be valid.
Of course, another alternative would be to use Class Wizard to map the edit control to a CEdit object directly. This creates a permanent binding and would also work in this case.
Other Points of Interest
There are several things in this program that are interesting, even though they don’t really relate to using Info-Zip. For example, consider the File | Open menu function.
In an ordinary MFC program, MFC does a tremendous amount of work so that it is easy to write File | Open and File | Save.
With this program, we don’t want that much help. The Info-Zip DLL wants a file name and that’s all.
When MFC notices a File | Open command, it does a few things:
- Shows a file open dialog.
- Matches the file name with a document template.
- Creates the correct type of document and view (and possibly a frame in an MDI program).
Opens the file and binds it to a CArchive object. - Passes the CArchive object to the document.
- Closes the file.
If the Info-Zip DLL understood the CArchive object, that would be great. However, that would be unreasonable to expect; CArchive is purely an MFC construct. Info-Zip works on lots of different platforms and operating systems.
With some libraries, you might have to assume all the responsibility of opening the file yourself, if you didn’t want the default handling.
Luckily, MFC makes good use of virtual functions to let you do as much work as you want to do and leave the rest to the library.
For this program, you still want steps 1 to 3 to occur. You just don’t want all the parts relating to CArchive.
Luckily, each step takes place at a different location in the class library– locations you can override to bend things to your own will.
The function in question for step 4 is CDocument:OnOpenDocument. By overriding this function (see Listing 2), it is a simple matter to store the file name in an instance variable. Later, the code will pass this variable to the Info-Zip DLL.
Another interesting portion of the code deals with resizing. A common problem with form-based programs is that you want the elements in the form to reposition themselves in response to the user resizing the window. Unfortunately, I haven’t found a clever solution for this– just brute force.
When designing this type of code you have to think about which elements are fixed in size and which elements resize to match the window size.
In this case, the text area resizes with the window, but the buttons do not change size. However, you do want the button positions to change as the window grows and shrinks.
The code in the OnSize method is far from elegant. It simply repositions the buttons (retaining their current size) and then adjusts the text field so that it fills the window except for the area with the buttons.
Free Software?
Is free (or open source) software too good to be true? Apparently not. Many well-respected programs including Linux, Perl, Apache, PHP, and the GNU tools are available more or less for free.
The catch? You need to understand exactly what restrictions the license places on you. For example, if you use the GNU C++ compiler, you can redistribute the binaries you create with very few, if any, restrictions.
However, until recently, if you used the GNU version of YACC (known as BISON), the resulting program also had to be free software.
This has changed and you can now redistribute programs you create with BISON under whatever terms you like.
So if you are thinking about incorporating free software into your business, you’d better get your lawyer to read the license agreement and tell you that it is appropriate for you to use the software. A nice note to the author probably doesn’t hurt either.
Another problem with free software (at least from our point of view) is that much of it is written for Unix. This is slowly changing as more and more programmers move to Windows either by choice or by force.
Here are some links to get you started with free software:
- www.gnu.org– Richard Stallman’s group started it all.
- www.spi-inc.org– The people who own the trademark on Open Source (kind of ironic, isn’t it?)
- www.opensource.org– Papers on the open source concept.
- www.itribe.net/virtunix– Technically a site for Unix programs under Windows, you can find a great deal of open source material here.
- http://sourceware.cygnus.com/cygwin– A major source of GNU utilities ported to Windows.
- www.apache.org– This popular open source Web server now has a Win32 version.
- www.gimp.org/~tml/gimp/win32– Open source image manipulation.
Of course, there is more, but this will get you started.
If you like free software, perhaps you should consider writing some. While not rewarding financially, it can give you a good feeling.
I recently wrote a small program for my own use as a ham radio operator and released it on the Internet.
There are now literally several thousand users of the software. I’ve really enjoyed being able to give something back to the community. Like they say, it’s better to give than to receive!