
Introduction
This source code shows how to add zip/unzip functionality to your programs. Lots of people have written their own wrappers around Zip, and indeed there are several articles on CodeProject that are based on earlier versions of my own code. How is this version different?
- Clean packaging. There's one pair of files zip.cpp, zip.h to add to your project if you want Zip. Another pair unzip.cpp, unzip.h if you want unzip (or both if you want both!). There are no additional libraries or DLLs to worry about.
- Clean API. Most other APIs around zip/unzip are terrible. This one is the best. The API is short, clean, and in a familiar Win32 style. Most other APIs wrap things up in classes, which is ugly overkill for such a small problem and always turn out to be too inflexible. Mine doesn't. See the code snippets below.
- Flexibility. With this code, you can unzip from a zip that's in a disk file, memory-buffer, pipe. You can unzip into a disk file, memory-buffer or pipe. The same for creating Zip files. This means that at last you don't need to write out your files to a temporary directory before using them! One noteworthy feature is that you can unzip directly from an embedded resource into a memory buffer or onto a disk file, which is great for installers. Another is the ability to create your Zip in dynamically growable memory backed by the system page file. Despite all this power, the API remains clean and simple. The power didn't come from just writing wrappers around other people's code. It came from restructuring the internals of zlib and info-zip source code. My code is unique in what it does here.
- Encryption. This version supports password-based Zip encryption. Passwords are absent from many other Zip libraries, including gzip.
- Unicode. This version supports Unicode filenames.
- Windows CE. This version works as it is under Windows CE. No need to alter makefiles or
#defines, or worry about compatibility of any LIB/DLL.
- Bug fixes. This code is based on gzip 1.1.4, which fixes a security vulnerability in 1.1.3. (An earlier version of my code used 1.1.3, and has crept into other CodeProject articles...).
At its core, my code uses zlib and info-zip. See article end for acknowledgements.
Using the code
To add Zip functionality to your code, add the file zip.cpp to your project, and #include "zip.h" to your source code.
Similarly for unzipping, add the file unzip.cpp to the project and #include "unzip.h" to your source code. Zip and unzip can co-exist happily in a single application. Or you can omit one or the other if you're trying to save space.
The following code snippets show how to use zip/unzip. They are taken from one of the demo applications included in the download. It also has project files for Visual Studio .NET and Borland C++ Builder6 and Embedded Visual C++ 3. The code snippets here use ASCII. But the functions all take arguments of type TCHAR* rather than char*, so you can use it fine under Unicode.
Example 1 - create a Zip file from existing files
HZIP hz = CreateZip("simple1.zip",0);
ZipAdd(hz,"znsimple.bmp", "simple.bmp");
ZipAdd(hz,"znsimple.txt", "simple.txt");
CloseZip(hz);
Example 2 - unzip a Zip file using the names it has inside it
HZIP hz = OpenZip("\\simple1.zip",0);
ZIPENTRY ze; GetZipItem(hz,-1,&ze); int numitems=ze.index;
for (int zi=0; zi<numitems; zi++)
{ ZIPENTRY ze; GetZipItem(hz,zi,&ze);
UnzipItem(hz, zi, ze.name);
}
CloseZip(hz);
Example 3- unzip from resource directly into memory
This technique is useful for small games, where you want to keep all resources bundled up inside the executable, but restricting the size.
Suppose we used a .rc with 1 RCDATA "file.zip" to embed the Zip file as a resource.
HRSRC hrsrc = FindResource(hInstance,MAKEINTRESOURCE(1),RT_RCDATA);
HANDLE hglob = LoadResource(hInstance,hrsrc);
void *zipbuf = LockResource(hglob);
unsigned int ziplen = SizeofResource(hInstance,hrsrc);
hz = OpenZip(zipbuf, ziplen, 0);
ZIPENTRY ze; int i; FindZipItem(hz,"sample.jpg",true,&i,&ze);
char *ibuf = new char[ze.unc_size];
UnzipItem(hz,i, ibuf, ze.unc_size);
...
delete[] ibuf;
CloseZip(hz);
Example 4 - unzip chunk by chunk to a membuffer
Normally when you call UnzipItem(...), it gives the return-code ZR_OK. But if you gave it too small a buffer so that it couldn't fit it all in, then it returns ZR_MORE.
char buf[1024]; ZRESULT zr=ZR_MORE; unsigned long totsize=0;
while (zr==ZR_MORE)
{ zr = UnzipItem(hz,i, buf,1024);
unsigned long bufsize=1024; if (zr==ZR_OK) bufsize=ze.unc_size-totsize;
... maybe write the buffer to a disk file here
totsize+=bufsize;
}
Common Questions
STRICT? I think you should always compile with STRICT (in project-settings/preprocessor/defines), and full warnings turned on. Without STRICT, the HZIP handle becomes interchangeable with all other handles.
How to show a progress dialog? One of the included examples, "progress", shows how to do this.
How to add/remove files from an existing Zip file? The zip_utils currently only allows you to OpenZip() for unzipping, or CreateZip() for adding, but don't allow you to mix the two. To modify an existing Zip (e.g.: adding or removing a file), you need to create a new Zip and copy all the existing items from the old into the new. One of the included examples, "modify", shows how to do this. It defines two functions:
ZRESULT RemoveFileFromZip(const TCHAR *zip, const TCHAR *name);
ZRESULT AddFileToZip(const TCHAR *zip, const TCHAR *name, const TCHAR *fn);
"fatal error C1010: unexpected end of file while looking for precompiled header directive". To fix this, select zip.cpp and unzip.cpp and change Project > Settings > C++ > PrecompiledHeaders to NotUsingPrecompiledHeaders.
Discussion
Efrat says: "I think the design is very bad", and so objects when I say that my API is clean and others are not. (Actually, he says my documentation is the most conceited he's seen and my design is the worst that he's seen!) I've reproduced his comments here, with my responses, so you can make a more informed decision whether to use my library.
- [Efrat] Better instead to use the boost IOStream library.
[Response] I love the boost library. If people can figure out how to add it to their projects and zip/unzip with it, they should definitely use boost rather than my code. (I'm still trying to figure it out, though, and couldn't get it to compile under CE.)
- [Efrat] A compressed archive has internal state; it's a classic object; the author's criticisms of OOP are unjustified. "OOP doesn't mean placing your code in a CPP file."
[Response] I'm trying not to be OOP.
- you'll never inherit from an archive, nor invoke virtual methods from it: we only use encapsulation, not any of the other pillars of OOP. By using an opaque handle HZIP rather than a class, I indicate this clearly to the programmer. Also,
- C++ classes don't work cleanly across DLLs. Handles like HZIPs do.
- [Efrat] For instance, progress-notifications should be done by virtual functions in a derived class, not by callbacks.
[Response] To get progress, you invoke UnzipItem in a while loop, and each iteration unzips a little bit more of the file. This is clean, re-entrant, and has a simple API. I think this is an easier API than inheriting from a class. I think inheritance from library classes is bad, in general.
- [Efrat] Compression should go in a DLL.
[Response] I disagree. DLLs are always pain, for developers as well as users. Unzip only adds 40K in any case.
- [Efrat] The API doesn't use the type system to differentiate between an HZIP for zipping and an HZIP for unzipping.
[Response] This was intentional. The difference between zipping and unzipping is a current implementation drawback. I think an API should be clean, "inspirational", and you shouldn't encode current implementation limitations into the type system.
- [Efrat] The API uses error-codes, rather than exceptions, but anyone who has graduated Programming 101 knows exceptions are better.
[Response] I think exceptions are not welcomed anywhere nearly as widely as Efrat suggests. Also, they don't work cleanly across DLL boundaries, and they don't work on Pocket PC.
- [Efrat] The API is inflexible; it should be coded for change, not just coded for all the options that were conceived while designing (handles, files, memory). Most users will think of sources and targets which this design can't support.
[Response] The original Zip uses FILE*s, which are effectively the same as Windows pipes. I also provided memory-buffers which add an enormous amount of flexibility that's easy to use and requires no additional programming. For any user who needs sources and targets which can't be reached via a memory buffer, they shouldn't use these zip_utils.
- [Efrat] The is unnecessarily Windows-specific. The original zlib works great and is portable; zip_utils offers no advantages. Compression is memory-manipulation and IO and so should not be platform-specific.
[Response] In the olden days before STL, "cross-platform" code inevitably meant:
- peppered with so many
#ifdefs that you couldn't read it,
- didn't work straight away under Windows.
I started from an old code-base, and so Efrat's proposed bottom-up rewrite was not possible. The advantage this code offers over zlib is that it's just a single file to add to your project, it works first time under Windows, you can add it easily as a CPP module to your project (not just dll/lib), and the API is simpler.
In general, Efrat wants code to be a clean extensible framework. I don't; I want small compact code that works fine as it is. Furthermore, I think that "framework-isation" is the biggest source of bugs and code overruns in the industry.
Acknowledgements
This version of article was updated on 28th July 2005. Many thanks to the readers at CodeProject who found bugs and contributed fixes to an earlier version. There was one terrible bug where, after a large file had been unzipped, the next one might not work. Alvin77 spotted this bug.
My work is a repackaged form of extracts from the zlib code available at www.gzip.org by Jean-Loup Gailly and Mark Adler and others. Also from the info-zip source code at www.info-zip.org. Plus a bunch of my own changes. The original source code can be found at the two mentioned websites. Also the original copyright notices can be found there, and also inside the files zip.cpp and unzip.cpp of my code.
| You must Sign In to use this message board. |
|
|
 |
|
 |
when unzip to memory: char *ibuf = new char[ze.unc_size]; UnzipItem(hz,i, ibuf, ze.unc_size);
//allocate memory should like this: char *ibuf = new char[ze.unc_size+1];//add one byte to mark end memset(ibuf,0,ze.unc_size+1); UnzipItem(hz,i, ibuf, ze.unc_size);
I think the ze.unc_size is the file data size, in the memory ,it should end with 0; if you don't do this, how to deal with the end of the file?
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
Hi everyone! Could you please help me to solve my issue with zip password? I have zip file with password "Xj4". But if I use another password "CiT". It unzipped to file with zero size. I use the following code to unzip file:
#include "windows.h" #include "unzip.h" #include "conio.h" #include using namespace std;
int main() { char pass[]= "CiT"; HZIP hz = OpenZip("..\\Xj4.zip",pass); ZIPENTRY ze; GetZipItem(hz,-1,&ze); ZRESULT res; GetZipItem(hz,0,&ze); res = UnzipItem(hz,0,ze.name); if(res == ZR_OK) { cout << "Unzipped\n"; } else { cout << "NOT Unzipped\n"; } getch(); CloseZip(hz); return 0; }
Example of soluetion in VS2008 you can find here. If you run it you will get file "Screen_capture.pdb" which have size zero but password is wrong for this.
I tried to debug this issue and found that possible error in function:
int unzReadCurrentFile (unzFile file, voidp buf, unsigned len, bool *reached_eof) { ... unsigned int uDoEncHead = pfile_in_zip_read_info->encheadleft; if (uDoEncHead>pfile_in_zip_read_info->stream.avail_in) uDoEncHead=pfile_in_zip_read_info->stream.avail_in; if (uDoEncHead>0) { char bufcrc=pfile_in_zip_read_info->stream.next_in[uDoEncHead-1]; pfile_in_zip_read_info->stream.avail_in -= uDoEncHead; pfile_in_zip_read_info->stream.next_in += uDoEncHead; pfile_in_zip_read_info->encheadleft -= uDoEncHead; if (pfile_in_zip_read_info->encheadleft==0) { if (bufcrc!=pfile_in_zip_read_info->crcenctest) return UNZ_PASSWORD; } } ... } Why UnzipItem returns ZR_OK for wrong password? How to fix this? May be I do something wrong? Thanks.
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
 |
|
 |
Found the function. I didn't find it earlier because the code is a little messy, but it works like a charm for my needs! Thank you!
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
Can your zip utils be used to zip a whole directory tree structure Can it rebuild the tree when you unzip the archive Is the resultant zip compatible with Windows Extract?
Thanks.
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
This code works good. But I have one requirement, Suppose i have 6MB of file, after compressing it becomes 2MB but i don't want such 2MB of file, this compress file should be break into small chunks, and while decompressing zlib should collect all chunk and should make origional file of 6MB.
can it be possible with zlib current functions.
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
I saw many discussions dated by 2009 year, but sources in archive dated by 2005.
Where can I found new sources?
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
 | Thanks  Ross Peralta | 11:55 21 Sep '09 |
|
 |
This code is not so easy to read , but it is easy to use and works great .
With the exception of the password bug, which I had to research a bit (thanks to MetsoGuy 16 Mar 06), everything is working well for my applications.
Thanks for your work!
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
Hello, can someone explain me what means with this error: #define ZR_FLATE 0x05000000 // an internal error in the de/inflation code
I can add some files to my zipfile, but 2 times i become this error, and i don't know how to solve them
thanks for any help Arrin
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
Hi, I am getting a compiler error
integer constant is too large for "long" type at line 2152 where
return (lutime_t)((i-116444736000000000)/10000000);
I changed it to
return (lutime_t)((i-116444736000000000ULL)/10000000);
and it compiles however,
then I go into my main and add #include "zip.h" and I have all these errors
In file included from ..\src\ZipTest.cpp:11: ..\src\zip.h:13: error: expected constructor, destructor, or type conversion before '(' token ..\src\zip.h:17: error: `DWORD' does not name a type ..\src\zip.h:22: error: `HZIP' does not name a type ..\src\zip.h:23: error: `HZIP' does not name a type ..\src\zip.h:24: error: `HZIP' does not name a type ..\src\zip.h:56: error: `ZRESULT' does not name a type ..\src\zip.h:57: error: `ZRESULT' does not name a type ..\src\zip.h:58: error: `ZRESULT' does not name a type ..\src\zip.h:59: error: `ZRESULT' does not name a type ..\src\zip.h:60: error: `ZRESULT' does not name a type ..\src\zip.h:75: error: `ZRESULT' does not name a type ..\src\zip.h:81: error: `ZRESULT' does not name a type ..\src\zip.h:84: error: `ZRESULT' was not declared in this scope ..\src\zip.h:84: error: `TCHAR' was not declared in this scope ..\src\zip.h:84: error: `buf' was not declared in this scope ..\src\zip.h:84: error: expected primary-expression before "unsigned" ..\src\zip.h:84: error: initializer expression list treated as compound expression ..\src\zip.h:190: error: `ZRESULT' does not name a type ..\src\zip.h:191: error: `ZRESULT' was not declared in this scope ..\src\zip.h:191: error: expected primary-expression before "char" ..\src\zip.h:191: error: expected primary-expression before "unsigned" ..\src\zip.h:191: error: initializer expression list treated as compound expression ..\src\zip.h:192: error: `HZIP' was not declared in this scope Build error occurred, build is stopped
Can someone help me please?
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
Hi, if i try to compile this i recive many of this errors if i include zip.cpp, but no one if include zip.h, can someone help me out here?
regards Arrin
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
In the filetime2dosdatetime function, the *dostime |= (WORD)((st.wSecond*2)&0x1f); should be *dostime |= (WORD)((st.wSecond/2)&0x1f);
Minor, indeed, but i do want the right timestamp in there, including the seconds.
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
Problem: decrypted and decompressed files are sometimes 12 bytes short, eg. 1,075,599 bytes instead of 1,075,611.
Fix: delete a single line:
pfile_in_zip_read_info->rest_read_uncompressed-=uDoEncHead;
By the way: Lucian, in unzCloseCurrentFile you are doing a test for CRC error in decompression and set the return code accordingly. But then you don't test this return value, eg. in TUnzip::Unzip. You should fix those problems. It would have let you catch this bug during your own tests.
Thanks.
|
| Sign In·View Thread·PermaLink | 5.00/5 |
|
|
|
 |
|
|
 |
|
|
 |
|
 |
I place my own contributions to zip_utils in the public domain (hence "yes you can use it in comercial products").
But the vast bulk of the code comes from the zlib and gzip projects, so you have to check with their licenses first. Their licenses are embedded in the source code.
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
 |
|
 |
I've found that FindZipItem doesn't work correctly with unicode file names. Problem in unzlocal_GetCurrentFileInfoInternal it doesn't get correct file name from archive. 
Thank you for your work. 
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
 |
 | OOP  barneyman | 15:55 8 Jun '09 |
|
 |
have to say I agree with the OOP complaints ...
You assume that I want to read/write files, or memory ... and your impl precludes me doing anything else ...
I(Sequential)Stream anyone? (and PLEASE don't suggest i use LockBytes, or flush my stream to disk first, i'm on mobile where memory is at premium, and i already have the source object as a stream, and want to give the dest as a stream to MAPI)
Would have been much more flexible to virtualise the read/write and then impl a File class, memory class et al ...
Never EVER assume you have all the possible uses of your algo corralled ... or you will have 
i speak thru experience, i've impl'd a CAB read/write class with virtual methods, and have now used it in 3 or 4 cases i would never have expected to ...
That said, i'm grateful for the start point 
flinty
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
I don't buy into the idea that "creating an OO abstraction layer" is the right way to make this code compositional.
It already is compositional through streams. If your stream is already an OS stream -- a win32 HANDLE or a unix FILE* -- then you can pass that directly. If it's some other stream then you can turn it into one -- win32 CreatePipe or unix pipe(). One of the examples (simple.cpp / Example3) demonstrates how to do this.
I think that streams are a better way of making code compositional rather than an OO layer. That's because they're a standard way of being compositional across the platform, and they don't require the user to "buy into" or inherit from any of my own framework or classes.
Unfortunately for your case, CreatePipe isn't supported on CE. But I definitely didn't want to grow zip_utils code into a "platform abstraction layer"... there are already too many bad platform abstraction layers out there.
PS. oh, I made a version of zip_utils which lets you "#define ZIP_STD" to be a stdio/unix-native one that uses FILE*, instead of the win32-native one that uses HANDLE. I can't remember if this is the version I uploaded to codeproject or not.
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
"It already is compositional through streams. If your stream is already an OS stream" & "If it's some other stream then you can turn it into one"
hmmm, so it's not compositional thru streams if your stream is (say) another well supported MS type - an IStream - you're arguing my case, not yours ...
It depends what you were trying to provide/prove, if it was an academic exercise in providing a discrete ZIP code-block, you've succeeded; if it was an attempt to provide a ZIP toolbox for a mature, flexible dev team, then, 'close, but no cigar' 
And i'm not advocating *platform abstraction*, that is a pandora's box, i'm interested in implementation abstraction ... Open, Close, Read and Write would do Indeed, the MS CAB SDK uses callbacks specifically to allow exactly this form of method abstraction ...
I would like to understand your retiscence 
And for the record, i hate exceptions too - but that'll be my netware upbringing 
flinty
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
and i should stress, IMO your intention was to provide the former, and you have succeeded well, i'm just arguing 'real-world use case' and and intrigued on your thoughts
flinty
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
Thanks for the discussion, flinty.
Even with an IStream, you'd still be able to be compositional -- you'd just write a pipe-shim which takes in IStream data on one side and pumps out HANDLE/FILE* stream data on to the other.
The question is, what kind of shim is better? Is it better to ask users to inherit from my own abstract class and implement methods (or callbacks) for Open/Close/Read/Write in their shim? Or is it better to ask users to use CreatePipe/pipe() to make their own shim?
I think the latter is better for a lot of reasons, and I would expect a mature flexible dev team to use it... (1) the pipe shim would be re-usable in lots more ways because it's not coupled to the zip_utils library; (2) the pipe shim doesn't ask developers to buy into my Open/Close/Read/Write style; (3) the pipe shim would have better unit-testability; (4) the pipe shim means that zip_utils has a smaller API surface area.
Really, the existing pipe mechanism seem like the best abstraction for managing the flow of data between components in a program. Any abstraction I invent (e.g. Open/Close/Read/Write) would not be as clean.
Of course this argument kind of falls on its face in the absence of pipes on CE. But given their presence on Win32 and in the C RTL, it felt like working around them was compromising the cleanliness of the library just to support one platform. Why add a layer of implementation abstraction to the library's API when it's not needed?
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
i think, inadvertently, you've hit the nail on the head ... 
"Why add a layer of implementation abstraction to the library's API when it's not needed?" is missing the qualifier "with the state of technology at that time" - vis-a-vis the lack of pipes in CE ...
virtual methods help to ameliorate (required) changes in implementation that the original coder could not/would not be expected to predict; you're still allowed to write a File, Handle and Mem implementation, in fact, you only need allude to the fact that those v-methods are there
As for your points ...
1. A moot point, in my sphere of influence, these situations are handled with virtuals - there would be no other need for some gratuitous "turn a into a Pipe"
2. no, but you have forced them into the pipe/mem world - you're robbing peter to pay paul - in order to handle your pipe, i still have to implement a Read/Write/Open and Close set ... there's no less work for me, just extra complexity in the 'blender-code'
3. With virtuals, I can unit test the core, in a harness, and even unit test the client side with a null impl - i think that one's a draw tho 
4. With today's dev tools, unless we're talking orders of magnitude, i am no longer concerned about API size ...
Additionally, virtual methods lead to easier platform portability ... that said, i (personally) don't consider the win32 / WinMo a platform port, so i'm not using that as justification - tho' some others would 
There is no answer - tho discussing each side is stimulating ... I do see the 'purity' of your Pipe method, it just seems the wrong 'best approach' in this case
flinty
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|