Skip to main content
Email Password   helpLost your password?
Screenshot - zipstrip2.png

Introduction

When dealing with zip files you have a few choices: use native APIs from third party Dlls, java APIs or .Net APIs.
If you rush to use APIs from System.IO.Compress .net namespace you will be very disappointed.
For reasons only Microsoft knows, the support is limited to streams only and lacks completely
for multi-files archives. This was probably a reason why third party .net libraries like SharpZipLib cropped up.
If you don't trust the free software, you might be surprised to find out that you can find .net support
for multi-file archives in .net buried in J# assemblies that offers parity with Java APIs.
To make a useful application that uses it I started with an existing code project application that is very handy
when backing up source code. I replaced the SharpZipLib references and used the Microsoft's J# APIs instead.
When porting the application I noticed that SharpZipLib API's were looking very similar with J# APIs and that
made my work so much easier.
To make this utility more enticing to use I've added quite a few features that I will detail below.

Background

In order to use Microsoft's API for multi-file zips and Java streams, you have to add vjslib.dll and vjslibcw.dll
.net assemblies as project references. They are part of J# distribution pack.
The Java like types will show up in the java.util.zip namespace. Since Microsoft's documentation on this
topic is quite Spartan ,I often had to rely on intellisense to figure it out.
For simplicity's sake, some nonessential UI code is omitted bellow and can be found only in the source code provided.

Listing the content of a zip file

Below you could see a snippet of code edited for simplicity that enumerates the files in the archive:

public static List<string > GetZipFileNames(string zipFile)
{
    ZipFile zf = null;
    List<string > list = new List<string >();
    try
    {
        zf = new ZipFile(zipFile);
        java.util.Enumeration enu = zf.entries();
        while (enu.hasMoreElements())
        {
            ZipEntry zen = enu.nextElement() as ZipEntry;
            if (zen.isDirectory())
                continue;//ignore directories
            list.Add(zen.getName());
        }

    }
    catch(Exception ex) 
    {
        throw new ApplicationException("Please drag/drop only valid zip files\nthat are not password protected.",ex);
    }
    finally
    {
        if (zf != null)
            zf.close();               
    }
    return list;
}

As you probably noticed ZipEntry and ZipFile are easy to use for this goal.

Zipping files from a folder

Below you could see a helper method used to zip the files from a folder:

private static void _CreateZipFromFolder(string Folder, IsFileStrippableDelegate IsStrip)
{
    System.IO.DirectoryInfo dirInfo =
        new System.IO.DirectoryInfo(Folder);
    System.IO.FileInfo[] files = dirInfo.GetFiles("*");//all files
    foreach (FileInfo file in files)
    {
        if (IsStrip != null && IsStrip(file.FullName))
            continue;//skip, don't zip it
        java.io.FileInputStream instream = new java.io.FileInputStream(file.FullName);
        int bytes = 0;
        string strEntry = file.FullName.Substring(m_trimIndex);
        _zos.putNextEntry(new ZipEntry(strEntry));
        while ((bytes = instream.read(_buffer, 0, _buffer.Length)) > 0)
        {
            _zos.write(_buffer, 0, bytes);
        }
        _zos.closeEntry();
        instream.close();
    }

    System.IO.DirectoryInfo[] folders = null;
    folders = dirInfo.GetDirectories("*");
    if (folders != null)
    {
        foreach (System.IO.DirectoryInfo folder in folders)
        {
            _CreateZipFromFolder(folder.FullName, IsStrip);
        }
    }
}

The IsStrip delegate acts as a filter that trashes the unwanted files.

Unzipping files from a zip file

Below you could see an edited for brevity piece of code used to unzip the files from a zip:

ZipInputStream zis = null;
zis = new ZipInputStream(new java.io.FileInputStream(file));
ZipEntry ze = null;
while ((ze = zis.getNextEntry()) != null)
{
    if (ze.isDirectory())
        continue;//ignore directories
    string fname = ze.getName();
    bool bstrip = IsStrip != null && IsStrip(fname);
    if (!bstrip)
    {
        //unzip entry
        int bytes = 0;
        FileStream filestream = null;
        BinaryWriter w = null;
        string filePath = Folder + @"\" + fname;
        if(!Directory.Exists(Path.GetDirectoryName(filePath)))
            Directory.CreateDirectory(Path.GetDirectoryName(filePath));
        filestream = new FileStream(filePath, FileMode.Create);
        w = new BinaryWriter(filestream);
        while ((bytes = zis.read(_buffer, 0, _buffer.Length)) > 0)
        {
            for (int i = 0; i < bytes; i++)
            {
                unchecked
                {
                    w.Write((byte)_buffer[i]);
                }
            }
        }
    }
    zis.closeEntry();
    w.Close();
    filestream.Close();

    }
    if (zis != null)
        zis.close();
}

Again the IsStrip delegate acts as a filter that trashes the unwanted files.
Also I had to mix the java.io with System.IO namespaces because of the sbyte[] array.

Changing the zip file

You can not directly modify a zip file. However you can create another zip and copy only select files in it.
When the transfer is complete we can rename the new file as the original and it would look like as we
changed the zip. The edited for brevity method below receives a list of string with the unwanted files:

public static void StripZip(string zipFile, List<string > trashFiles)
{
    ZipOutputStream zos = null;
    ZipInputStream zis = null;
    //remove 'zip' extension
    bool bsuccess = true;
    string strNewFile = zipFile.Remove(zipFile.Length - 3, 3) + "tmp";
    zos = new ZipOutputStream(new java.io.FileOutputStream(strNewFile));
    zis = new ZipInputStream(new java.io.FileInputStream(zipFile));
    ZipEntry ze = null;
    while ((ze = zis.getNextEntry()) != null)
    {
        if (ze.isDirectory())
            continue;//ignore directories
        string fname = ze.getName();
        bool bstrip = trashFiles.Contains(fname);
        if (!bstrip)
        {
            //copy the entry from zis to zos
            int bytes = 0;
            //deal with password protected files
            zos.putNextEntry(new ZipEntry(fname));
            while ((bytes = zis.read(_buffer, 0, _buffer.Length)) > 0)
            {
                zos.write(_buffer, 0, bytes);
            }
            zis.closeEntry();
            zos.closeEntry();
        }
    }
    if (zis != null)
        zis.close();
    if (zos != null)
        zos.close();
    if (bsuccess)
    {
        System.IO.File.Delete(zipFile + ".old");
        System.IO.File.Move(zipFile, zipFile + ".old");
        System.IO.File.Move(strNewFile, zipFile);
    }
    else
        System.IO.File.Delete(strNewFile);
}

Improvements over the version 1.0

To make this tool more attractive I've added some improvements of my own: The first one to notice
is the usage of a checked list box that allows doing manual changes on the fly.
My favorite is the ability to edit the list of filter extensions that are bound to the CPZipStripper.exe.xml
file through a DataTable. Here is an edited snapshot of this file.

<configuration>
  <maskRow maskField="*.plg" / >
  <maskRow maskField=".opt" / >
  <maskRow maskField=".ncb" / >
  <maskRow maskField=".suo" / >
  <maskRow maskField="*.pdb" / >
......
</configuration>

Reading data from the config file

Notice that in the application configuration file we keep not only the appSetttings node,
but also the files, paths, and most importantly the DataTable content.
Loading the data from this xml file in the respective lists and dataSet is easy:

XmlDocument xd = new XmlDocument();
xd.Load(cfgxmlpath);
//use plain xml xpath for the rest
m_paths.Clear();
XmlNode xnpath = xd["configuration"]["paths"];
if(xnpath!=null)
{
    foreach(XmlNode xn in xnpath.ChildNodes)
    {
        m_paths.Add(xn.InnerXml);                        
    }    
}
XmlNode xnfile = xd["configuration"]["files"];
if(xnfile!=null)
{
    foreach(XmlNode xn in xnfile.ChildNodes)
    {
        m_files.Add(xn.InnerXml);                        
    }    
}
//use the data set
m_extensions.Clear();
_dataSet = new DataSet("configuration");
DataTable mytable = new DataTable("maskRow");
DataColumn exColumn = new DataColumn("maskField", 
    Type.GetType("System.String"), null, MappingType.Attribute);
mytable.Columns.Add(exColumn);
_dataSet.Tables.Add(mytable);
_dataSet.Tables[0].ReadXml(MainForm.cfgxmlpath);
for (int i = 0; i < _dataSet.Tables[0].Rows.Count; i++)
{
    DataRow row = _dataSet.Tables[0].Rows[i];
    string val = row[0].ToString().ToLower();
    if (val.Length > 0)//no empty mask
    {
    //.....code eliminated for brevity
    }
    else
    {   //don't show empty rows
        row.Delete();
    }
}
_dataSet.Tables[0].AcceptChanges();

Writing data into the config file

Using WriteXml from the dataSet will eliminate the data that does not belong to the table.
For this reason we have to save it before calling WriteXml and restore it afterwards:

XmlDocument xd = new XmlDocument();
//get the original
xd.Load(MainForm.cfgxmlpath);
//save nodes not part of the dataset
XmlNode xnpath = xd["configuration"]["paths"];
XmlNode xnfile = xd["configuration"]["files"];
XmlNode lastFolderpath = xd["configuration"]["LastUsedFolder"];     

//write the masks
_dataSet.WriteXml(MainForm.cfgxmlpath);

//restore the old saved nodes
xd.Load(MainForm.cfgxmlpath);
if(xnpath != null)
    xd.DocumentElement.AppendChild(xnpath);
if (xnfile != null)
     xd.DocumentElement.AppendChild(xnfile);
if (lastFolderpath != null)
    xd.DocumentElement.AppendChild(lastFolderpath);
xd.Save(MainForm.cfgxmlpath);

Using the wild chars for the filter

I'm not going to get into details on this one, but as you've already noticed in the xml snippet,
you can use * and ? chars.
It's a good ideea that the first thing you do when you open this application is setting the configuration.

Screenshot - config.png

Explorer folder context menu

I've added some new functionality in regards to using the context menu from Explorer.
You should start the exe only once before you can right click on the folder and zip it.

Screenshot - Menu.png

History

As a utility I consider version 2.x to be an improvement over the old one.
You can use it to some extent as a Winzip replacement, but it lacks features like encryption.
.Net 2.0 and J# package have to be installed on your machine to run it. If you have problems running
the exe alone,it might be because you are missing the J# distribution package or the .Net 2.0 runtime.
In that that's the case, I recommend you try to install this msi install file I've created or download
vjredist_32bit.zip and install it from locally.

Version 2.3

I've added email capability to the application using the Outlook wrapper and made other small enhancements.
I also included the deployment project that created the msi file.

You must Sign In to use this message board.
 
 
Per page   
 FirstPrevNext
GeneralError while deleting archived files manually Pin
GISDevil
13:11 21 Apr '08  
GeneralRe: Error while deleting archived files manually Pin
dmihailescu
14:02 24 Apr '08  
GeneralRe: Error while deleting archived files manually Pin
GISDevil
6:27 28 Apr '08  
GeneralRe: Error while deleting archived files manually Pin
dmihailescu
13:59 28 Apr '08  
GeneralRe: Error while deleting archived files manually Pin
GISDevil
9:17 29 Apr '08  
GeneralRe: Error while deleting archived files manually Pin
dmihailescu
16:05 29 Apr '08  
QuestionException in ZipFile Pin
HASEENAA
1:51 14 Feb '08  
GeneralRe: Exception in ZipFile Pin
dmihailescu
11:36 15 Feb '08  
Questionget folder Pin
manhnhien209
17:16 11 May '07  
GeneralGetfolder Pin
manhnhien209
17:09 11 May '07  
GeneralMicrosoft.VisualStudio.Zip Pin
David Hay
16:59 7 Mar '07  
GeneralRe: Microsoft.VisualStudio.Zip Pin
dmihailescu
12:02 28 Mar '07  
GeneralRe: Microsoft.VisualStudio.Zip Pin
Adam Tibi
3:54 17 Apr '07  
GeneralAbout Documentation Pin
Hawk777
18:00 27 Feb '07  
GeneralArchive Size Pin
Jamsey12001
4:07 27 Feb '07  
GeneralRe: Archive Size Pin
dmihailescu
5:00 28 Feb '07  
GeneralRe: Archive Size Pin
Jamsey12001
5:04 28 Feb '07  
GeneralRe: Archive Size Pin
Aleksei Krassovskikh
10:25 17 May '08  
GeneralRe: Archive Size Pin
dmihailescu
6:55 18 May '08  
GeneralRe: Archive Size Pin
Aleksei Krassovskikh
6:44 20 May '08  
GeneralHow to unzip password protected zip files? Pin
sakthivenkatesh
14:06 22 Feb '07  
GeneralRe: How to unzip password protected zip files? Pin
dmihailescu
4:44 23 Feb '07  
GeneralRe: How to unzip password protected zip files? Pin
sakthivenkatesh
6:58 23 Feb '07  
GeneralGood new bicycle :) Pin
Valeri
10:19 21 Feb '07  
GeneralMissing images Pin
Mark Nischalke
6:18 21 Feb '07  


Last Updated 16 May 2008 | Advertise | Privacy | Terms of Use | Copyright © CodeProject, 1999-2009