Click here to Skip to main content
13,596,324 members
Click here to Skip to main content
Add your own
alternative version


82 bookmarked
Posted 5 Sep 2006
Licenced CPOL

SolutionZipper: VS 2005 Add-in Cleans and Zips a Solution in One Step

, 3 Aug 2007
Rate this:
Please Sign up or sign in to vote.
A convenient tool for quick solution back-ups.


Updated to v1.3 on 31-July-2007: See updated source, notes in article and Revision history for details. There are already a couple of good Visual Studio solution and project zipping tools available:

  • ProjectZip 1.6: Zip up the source code for your latest CodeProject article
  • ZipStudio: A versatile Visual Studio add-in to zip up Visual Studio solutions and projects

The code described in this article provides a simplified version of these add-ins. Specifically, SolutionZipper has a single-click-does-everything interface, which is what automation is all about: saving time. There are no dialogs that present file lists, file names to be chosen or user options to select. SolutionZipper does the following in a single step:

  1. Saves all unsaved project files
  2. Cleans all available solution configurations (Debug, Release, etc.)
  3. Cleans all Deployment Projects
  4. Zips all files in the entire solution:
    • The ZIP file is uniquely named
    • The path for all files are solution directory relative
    • The ZIP file is placed at the same level as the solution directory

When these operations are completed, a confirmation dialog is displayed:

Screenshot - SolutionZipper-confirm.JPG

The ZIP file can be used to share projects and is in the proper form for CodeProject submissions (of course). If you are not using a source code control system, SolutionZipper is useful for quickly backing up a snapshot of a solution at major development milestones.

Add-in implementation

The standard VS Add-in Wizard was used to create the project. See How to: Create an Add-in for details. In order to get a custom icon on the menu item,

Screenshot - SolutionZipper-menu.JPG

follow the instructions in How to: Display a Custom Icon on the Add-in Button carefully. Also note that the icon must be exactly 16x16 pixels to be displayed. ICSharpCode.SharpZipLib.dll is used for the ZIP implementation and can be downloaded here.


The automation actions are implemented in the IDTExtensibility2.Exec function.

// Updated: v1.3
public void Exec(string commandName, 
    vsCommandExecOption executeOption, 
    ref object varIn, ref object varOut, ref bool handled)
    handled = false;
    if(executeOption == 
        if(commandName == 
            const string ADDIN_NAME = "SolutionZipper";
            _Solution sol = _applicationObject.Solution;
            handled = true;
            if (string.IsNullOrEmpty(sol.FullName)) return; 
            // no solution loaded
            string solpath = Path.GetDirectoryName(sol.FullName); 
            // solution path
            // If there are external projects, they will be copied here to
            // be included in the zip file and then deleted.
            string extDir = null;
            // Status bar
            EnvDTE.StatusBar statusbar = _applicationObject.StatusBar;

            // Do a Save All
            statusbar.Text = ADDIN_NAME + ": Save All Files...";
            _applicationObject.ExecuteCommand("File.SaveAll", "");

            // Clean all configurations
            statusbar.Text = ADDIN_NAME + ": Cleaning all configurations...";
            SolutionBuild2 sb = (SolutionBuild2)sol.SolutionBuild;
            // Remember current configuration
            SolutionConfiguration curr_conf = sb.ActiveConfiguration;
            foreach (SolutionConfiguration s in sb.SolutionConfigurations)
                sb.Clean(true); // Clean the solution.
            // Restore previously active config
            // Clean deployment projects.
            statusbar.Text = ADDIN_NAME + ": Handle deployment projects...";
            foreach (Project p in sol.Projects)
                    if (p.UniqueName.IndexOf(".vdproj", 
                        0, StringComparison.CurrentCultureIgnoreCase) != -1)
                        string vddir = Path.GetDirectoryName(p.FullName);
                        // Remove contents of the Release and 
                        // Debug directories
                        DeleteDirContents(Path.Combine(vddir, "Release"));
                        DeleteDirContents(Path.Combine(vddir, "Debug"));
                catch (NullReferenceException)
                    // A Web Deployment project will throw this 
                    // exception when UniqueName is accessed.
                    // Name is the only valid property: See if the 
                    // project is in the solution dir.
                    // Delete product if it is.
                    string wdprojfile = 
                        Path.Combine(Path.Combine(solpath, p.Name), 
                        p.Name + ".wdproj");
                    if (File.Exists(wdprojfile))
                        string wddir = Path.Combine(solpath, p.Name);
                        // Remove contents of the Release and 
                        // Debug directories
                        DeleteDirContents(Path.Combine(wddir, "Release"));
                        DeleteDirContents(Path.Combine(wddir, "Debug"));
                catch (Exception ex)
                    // Just in case some other type of exception occurs!!
                        "An Error has occurred:\n{0}\n\nZip " + 
                        "file not created.",
                        ex.Message), ADDIN_NAME, 
                        MessageBoxButtons.OK, MessageBoxIcon.Error);

            // Copy external projects to solution dir
            ExtProjectMap fmap = null;
            statusbar.Text = ADDIN_NAME + ": Handle external projects...";
            foreach (Project p in sol.Projects)
                // Ignore Web deployment projects!
                    string uname = p.UniqueName; 
                catch (NullReferenceException) 

                if (!string.IsNullOrEmpty(p.FileName) 
                    && Path.IsPathRooted(p.FileName) && 
                    p.FileName.IndexOf(solpath) == -1)
                    // This project is not in the solution directory
                    if (extDir == null) 
                        // Make the external projects dir
                        extDir = Path.Combine(solpath, "szExternalProjects");
                        if (!Directory.Exists(extDir))
                            catch (Exception ex)
                                    "Failed to create temporary " + 
                                    "directory:\n{0}\nError: " + 
                                    "{1}\n\nZip file not created.",
                                    extDir, ex.Message), ADDIN_NAME, 
                        fmap = new ExtProjectMap(extDir);
                    // Copy this project to solution temporary directory
                    string srcPath = Path.GetDirectoryName(p.FullName);
                    string dstPath = 
                        Path.Combine(extDir, Path.GetFileName(srcPath));
                        xDirectory.Copy(srcPath, dstPath);
                    catch (Exception ex)
                            "Failed to copy:\n\nSource: " + 
                            "{0}\nDestination: {1}\n\nError: {2}\n\nZip " + 
                            "file not created.",
                            srcPath, dstPath, ex.Message), 
                            ADDIN_NAME, MessageBoxButtons.OK, 
                    fmap.Add(Path.GetFileName(srcPath) + 
                         "\\" + Path.GetFileName(p.UniqueName),srcPath);
            // Create zip file paths and name
            statusbar.Text = ADDIN_NAME + ": Creating Zip file...";
            DateTime dt = DateTime.Now;
            string sname = Path.GetFileName(sol.FileName);
            string zipname = 
                sname.Substring(0, sname.Length - 4), 
                dt.Year, dt.Month, dt.Day, dt.Hour, dt.Minute, dt.Second);
            // Save zip file in the parent directory, not 
            // in the solution directory
            string zippath = 
            ZipDirectoryHierarchy zs = new ZipDirectoryHierarchy();
            zs.ZipIt(solpath, zippath);

            // Clean up for projects that were copied to the temporary dir
            if (extDir != null)
                statusbar.Text = ADDIN_NAME + ": Project clean-up...";
                "Successfully created:\n\n{0}\n\n{1}",
                ADDIN_NAME, MessageBoxButtons.OK, 

This code is pretty self-explanatory.


  • In addition to its Text property, EnvDTE.StatusBar also has a Progress method that allows control of the VS progress bar
  • The _DTE.ExecuteCommand method allows you to execute any VS command or macro. The command name can be found in the Tools>Options>Environment>Keyboard dialog, which has a nice search feature:

    Screenshot - SolutionZipper-options-kb.JPG

  • All available solution configurations are cleaned. Unfortunately, VS does not provide a way to clean Deployment Projects. This is handled by searching through all projects and finding those that have ".vdproj" in the UniqueName property. The contents of the Release and Debug directories are then deleted.
  • v1.1 update notes:
    • Projects that do not reside in the solution directory are cleaned and then copied to the current solution in a temporary directory, szExternalProjects. After the ZIP file is created, the temporary directory is removed. See Revision history for more details.
    • Note that the DeleteDirContents method now removes the entire directory tree recursively, including the directory itself.
  • v1.2 update notes:
    • Added improved error handling. See Revision history for more details. </a />
    • Added support for Web Deployment projects. See Revision history for more details. Other than cleaning these projects when they exist in the solution directory, the changes mostly protect SolutionZipper from crashing due to the incomplete Project instance they create. </a />
    • The ExtProjectMap class creates a ExternalProjectsMap.txt file and adds an entry for each external project added to the temporary directory. This documentation allows for easier reconstruction of the solution from the extracted ZIP. Here is an example:
      SolutionZipper: External projects in this directory.
      Project                     : Original Path
      ----------------------      : -------------
      extProject\extProject.csproj: C:\Projects\SolutionZipper\
      extSetup\extSetup.vdproj    : C:\Projects\SolutionZipper\
    • The IDTExtensibility2.QueryStatus function was modified so that the SolutionZipper Tools menu item would only be available when a saved solution is loaded into the IDE.
      public void QueryStatus(string commandName, 
          vsCommandStatusTextWanted neededText, 
          ref vsCommandStatus status, ref object commandText)
          if(neededText == 
              if(commandName == "SolutionZipper.Connect.SolutionZipper")
                  status = (vsCommandStatus
                  if (!string.IsNullOrEmpty(
                      status |= vsCommandStatus.vsCommandStatusEnabled;


Since there are usually other non-project files somewhere in the solution, it is preferable to simply zip up the entire solution directory hierarchy. The ZipDirectoryHierarchy class uses the SharpZipLib to do this. The path entries in the ZIP file will be relative, i.e. not begin with \, and will start at the solution directory.

internal class ZipDirectoryHierarchy
    // Keep track of dir/file/byte counts
    private int DirCount = 0;
    private int FileCount = 0;
    private long ByteCount = 0;

    // Add a single file to the zip output
    private void ZipOne(ZipOutputStream s, 
        Crc32 crc, string basedir, string file)
        if (Path.GetExtension(file) == ".ncb") 
            return; // ignore VC++ Intellisense database files
        FileInfo fi = new FileInfo(file);
        if ((fi.Attributes & FileAttributes.Hidden) != 0) 
            return; // ignore hidden files
        FileStream fs = File.OpenRead(file);
        byte[] buffer = new byte[fs.Length];
        fs.Read(buffer, 0, buffer.Length);
        String relpath = file.Substring(basedir.Length + 1); 
            // make path relative 
        ZipEntry entry = new ZipEntry(relpath);
        entry.DateTime = DateTime.Now;
        entry.Size = fs.Length;
        this.ByteCount += entry.Size;
        entry.Crc = crc.Value;
        s.Write(buffer, 0, buffer.Length);
    // Recursive directory zipping
    private void ZipDirectory(ZipOutputStream s, 
        Crc32 crc, string basedir, string dirpath)
        // All of the files
        string[] filenames = Directory.GetFiles(dirpath);
        if (filenames.Length > 0)
            // only count directories with files in them
            foreach (string file in filenames)
                ZipOne(s, crc, basedir, file);
        // All of the directories
        string[] dirs = Directory.GetDirectories(dirpath);
        foreach (string dir in dirs)
            DirectoryInfo di = new DirectoryInfo(dir);
            if ((di.Attributes & FileAttributes.Hidden) == 0) 
                // ignore hidden directories
                ZipDirectory(s, crc, basedir, dir);
    // Public methods

    // Zip method
    public void ZipIt(string dirpath, string zipname)
        Crc32 crc = new Crc32();
        ZipOutputStream s = new ZipOutputStream(File.Create(zipname));
        s.SetLevel(6); // 0 - store only to 9 - means best compression
        // Get the base path, which is the parent of the directory being
        // zipped (dirpath).
        // All zip file paths will be relative to this.
        string basepath = Path.GetDirectoryName(dirpath);
        ZipDirectory(s, crc, basepath , dirpath);

    // Get the status string
    public string StatusString
            return string.Format(
                "{0,-6}\tKB " + 
                ByteCount / 1024, FileCount, DirCount);


I have only been able to test SolutionZipper on a limited number of environments and project types.

  • VS 2005: C# and Deployment projects are OK. It has not been tested on any other project types -- but it should work.
  • VS 2003: In addition to the Add-in installation differences between VS2003 and VS2005, SolutionZipper.dll is a .NET 2.0 assembly, so it will not work with older IDEs. It shouldn't be difficult to port Connect.cs back to VS 2003 though.
  • VS C# Express Edition: Does not work. Express Editions do not support Add-ins.


Please let me know if you have problems with SolutionZipper. I will provide bug fix updates as necessary and will consider including suggested enhancements in the future. Enjoy!

Revision history

  • v1.3: 31-July-2007
    • Fixed a bug that was causing SZ to fail during the "Handle external projects" phase.
    • Ignore VC++ Intellisense Database files, i.e. *.ncb.
    • Ignore hidden files and folders.
  • v1.2: 17-Sep-2006
    • Fixed a bug in DeleteDirContents that was causing SolutionZipper to fail if the directory did not exist.
    • Added error messages for when failures occur. One typical situation is when a project contains a file that is currently in use by another process. The inability to access that file will cause an error. For example, the following error will occur when trying to zip a web project when the ASP.NET development server is running:

      Screenshot - SolutionZipper-error.JPG

      This is solved by stopping the development server and running SolutionZipper again.
    • Added an external project map file, ExternalProjectsMap.txt, to the szExternalProjects directory. This file shows the original full directory path for each project in that directory. See details here.
    • Added limited support for web deployment projects. "Limited" means that the web deployment project must reside within the solution directory. This is because these project types only provide the Name property in the Project instance, so it is impossible to know the actual project directory. What's interesting is that other Project properties, like UniqueName, contain instances of an Exception. I've used this to keep SolutionZipper from failing when this project type is encountered.
  • v1.1: 09-Sep-2006
    • SolutionZipper v1.0 did not take into account that Visual Studio allows solutions to contain projects that reside outside of the solution directory. There were two possible solutions for this:
      1. Include the external projects in the ZIP file with "project" level relative directories at the same level as the solution directory.
      2. Copy the external projects into a temporary solution directory and remove the temporary directory after the ZIP file is created. All projects are maintained under the single solution directory.
      I decided to take the second approach because the external projects in #1 would likely be extracted to the wrong location. In either case then, the external projects would have to be manually copied back to their original location. However, solution #2 avoids possible confusion. External C# and deployment projects were added to the SolutionZipper solution source for testing.
    • The DeleteDirContents method now removes the entire directory tree recursively, including the directory itself.
    • Recursive directory copy code is from xDirectory.Copy() - Copy Folder, SubFolders and Files (unmodified).
  • v1.0: 05-Sep-2006
    • Initial release.


This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


About the Author

Robert Nadler
Technical Lead
United States United States
Bob is a Biomedical Engineer and has been involved in the development of software for medical devices for many (many) years. He lives and works in beautiful San Diego, CA.

You may also be interested in...


Comments and Discussions

GeneralMy vote of 5 Pin
hjgode28-Jul-11 17:50
memberhjgode28-Jul-11 17:50 
GeneralVS2008 Pin
DanMayer23-Jan-08 12:06
memberDanMayer23-Jan-08 12:06 
AnswerRe: VS2008 Pin
Robert Nadler29-Jan-08 9:31
memberRobert Nadler29-Jan-08 9:31 
GeneralRe: VS2008 Pin
DanMayer15-Feb-08 8:02
memberDanMayer15-Feb-08 8:02 
GeneralRe: VS2008 Pin
Robert Nadler18-Feb-08 13:40
memberRobert Nadler18-Feb-08 13:40 
GeneralVS2003 alternative Pin
nicorac727-Aug-07 21:16
membernicorac727-Aug-07 21:16 
Generaldoes work too long ;-) Pin
Valery A. Boronin30-Apr-07 13:37
memberValery A. Boronin30-Apr-07 13:37 
Generaldoesn't work with makefiles Pin
Valery A. Boronin30-Apr-07 13:18
memberValery A. Boronin30-Apr-07 13:18 
GeneralExclude option? .SVN folders Pin
Sharp-Developer.NET22-Feb-07 0:48
memberSharp-Developer.NET22-Feb-07 0:48 
GeneralAnother strange bug... Pin
Michal Altair Valasek5-Oct-06 15:43
memberMichal Altair Valasek5-Oct-06 15:43 
AnswerRe: Another strange bug... Pin
Robert Nadler5-Oct-06 17:40
memberRobert Nadler5-Oct-06 17:40 
GeneralSolution with solution folders Pin
bgiot5-Oct-06 0:04
memberbgiot5-Oct-06 0:04 
QuestionRe: Solution with solution folders Pin
Robert Nadler5-Oct-06 5:47
memberRobert Nadler5-Oct-06 5:47 
AnswerRe: Solution with solution folders Pin
bgiot5-Oct-06 21:35
memberbgiot5-Oct-06 21:35 
GeneralRe: Solution with solution folders Pin
ElectroDiet7-Feb-07 2:00
memberElectroDiet7-Feb-07 2:00 
GeneralProblem with non_English filenames Pin
Aziz Rahip3-Oct-06 13:43
memberAziz Rahip3-Oct-06 13:43 
AnswerRe: Problem with non_English filenames Pin
Robert Nadler4-Oct-06 12:21
memberRobert Nadler4-Oct-06 12:21 
GeneralRe: Problem with non_English filenames Pin
Aziz Rahip4-Oct-06 12:33
memberAziz Rahip4-Oct-06 12:33 
GeneralRe: Problem with non_English filenames Pin
Aziz Rahip6-Dec-06 22:05
memberAziz Rahip6-Dec-06 22:05 
GeneralRe: Problem with non_English filenames Pin
Aziz Rahip19-Mar-08 13:30
memberAziz Rahip19-Mar-08 13:30 
GeneralDoes not work for web deployment projects Pin
Michal Altair Valasek15-Sep-06 9:13
memberMichal Altair Valasek15-Sep-06 9:13 
GeneralRe: Does not work for web deployment projects Pin
Robert Nadler17-Sep-06 17:27
memberRobert Nadler17-Sep-06 17:27 
GeneralRe: Does not work for web deployment projects Pin
Michal Altair Valasek19-Sep-06 1:05
memberMichal Altair Valasek19-Sep-06 1:05 
GeneralDon't work on my computer Pin
Gonzalo Brusella5-Sep-06 9:52
memberGonzalo Brusella5-Sep-06 9:52 
GeneralRe: Don't work on my computer Pin
Robert Nadler5-Sep-06 10:31
memberRobert Nadler5-Sep-06 10:31 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

Permalink | Advertise | Privacy | Cookies | Terms of Use | Mobile
Web02 | 2.8.180621.3 | Last Updated 3 Aug 2007
Article Copyright 2006 by Robert Nadler
Everything else Copyright © CodeProject, 1999-2018
Layout: fixed | fluid