These two disk spanning modes create volumes with compatible internal structure. It means that you can easily convert the volumes created in one mode to the other one by renaming the files (in TDSpan mode each volume's extension (apart from the last one) consists of a 'z' letter and a number). To convert the archive from TD to PKZIP compatible archive, copy each file to the removable media, giving them the extension ".zip". You should also label each disk with the appropriate label starting from "pkback# 001" (note the space between '#' and '0').
There is a limited functions set available while working with multi-disk archives. Only adding is allowed when creating the archive and only extracting and testing after opening an existing one. Deleting files from these archives is not allowed at all.
Class CZipArchive uses write buffer to make write operations as fast as possible. You can change its size with CZipArchive::SetAdvanced function (first argument). While creating a multi-disk archive, set the size of the buffer to the maximum size of the volume for the best performance.
Popular archivers such as PKZIP and WinZip should be able to open the archive created in both modes.
//Windows code int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { CZipArchive zip; // get the path of the executable TCHAR szBuff[_MAX_PATH]; if (!::GetModuleFileName(hInstance, szBuff, _MAX_PATH)) return -1; CZipString szDest; // ... // add some code here to get the destination directory from the user // for example: // CBrowseForFolder bf; // bf.strTitle = _T("Select directory to extract files"); // if (!bf.GetFolder(szDest)) // return -1; // // class CBrowseForFolder is included in the sample application project // remember about including the header! zip.Open(szBuff, CZipArchive::zipOpenReadOnly); // zipOpenReadOnly mode is necessary for self extract archives for (WORD i = 0; i < zip.GetCount(); i++) zip.ExtractFile(i, szDest); zip.Close(); return 0; // this code will not work for the encrypted archives since it is needed // to get the password from the user ( a small addition to the // existing code I suppose ) }
After compiling it and appending a zip archive to it (e.g. with the DOS command: copy /b SelfExtract.exe + ZipFile.zip FinalFile.exe ) you have a self extracting archive.
CException:
CMemoryException*
, CFileExeption*
and CZipException*
(be sure to delete the object when you done with it). Handling them may be done in the following way:
try { // ... // some operations on the ZipArchive library } catch (CException* e) { if (e->IsKindOf( RUNTIME_CLASS( CZipException ))) { CZipException* p = (CZipException*) e; //... and so on } else if (e->IsKindOf( RUNTIME_CLASS( CFileException ))) { CFileException* p = (CFileException*) e; //... and so on } else { // the only possibility is a memory exception I suppose //... and so on } e->Delete(); }
std::exception
. In this case you should catch std::exception
object (not a pointer to it).
CZipArchive zip; CZipMemFile mf; // create archive in memory zip.Open(mf, CZipArchive::zipCreate); // ... // add some files to archive here e.g. by calling CZipArchive::AddNewFile // ... zip.Close(); // write the archive to disk CZipFile f; if (f.Open("c:\\temp.zip", CZipFile::modeWrite|CZipFile::modeCreate, false) { int iLen = mf.GetLength(); BYTE* b = mf.Detach(); f.Write(b, iLen); f.Close(); // we must free the detached memory free(b); }
You can as well read the archive from disk and then extract files, e.g.:
CZipFile f; if (f.Open("c:\\temp.zip", CZipFile::modeRead, false) { int iLen = f.GetLength(); BYTE* b = (BYTE*)malloc((UINT)iLen); f.Read(b, iLen); f.Close(); CZipMemFile mf; mf.Attach(b, iLen); CZipArchive zip; zip.Open(mf); // ... // extract files here from the archive e.g. by calling CZipArchive::ExtractFile // ... zip.Close(); }
With functions CZipArchive::AddNewFile(CZipMemFile&, LPCTSTR, int, int, unsigned long)
and CZipArchive::ExtractFile(WORD, CZipMemFile&, DWORD)
you can add files to archive from memory and extract them to a memory file. Now a bit larger example:
// create the archive in memory with two files inside CZipMemFile mf; CZipArchive zip; zip.Open(mf, CZipArchive::zipCreate); zip.AddNewFile("c:\\testfile1.txt"); zip.AddNewFile("c:\\testfile2.txt"); zip.Close(); //create the archive on disk and add a previously zipped file from memory zip.Open("c:\\test.zip", CZipArchive::zipCreate); zip.AddNewFile(mf, "File1.zip"); zip.Close(); // we have now zip-in-zip file on the disk, // let's extract the embedded zip file back to memory zip.Open("c:\\test.zip"); // reset the contents of the CZipMemFile object mf.SetLength(0); zip.ExtractFile(0, mf); zip.Close(); // write the file from memory to disk CZipFile f; if (f.Open("c:\\File1.zip", CZipFile::modeWrite|CZipFile::modeCreate, false)) { int iLen = mf.GetLength(); BYTE* b = mf.Detach(); f.Write(b, iLen); f.Close(); // we must free the detached memory free(b); }
One important thing: when you operate on the archive in memory, you must ensure that CZipMemory object will not be destroyed before calling CZipArchive::Close. In some cases you'll need to construct the object using the new
operator, e.g.:
// ... CZipMemFile* pmf = new CZipMemFile; zip.Open(*pmf, CZipArchive::zipCreate); // ... zip.Close(); delete pmf;