/// <summary>
        ///   Add a ZipEntry for which content is written directly by the application.
        /// </summary>
        ///
        /// <remarks>
        /// <para>
        ///   When the application needs to write the zip entry data, use this
        ///   method to add the ZipEntry. For example, in the case that the
        ///   application wishes to write the XML representation of a DataSet into
        ///   a ZipEntry, the application can use this method to do so.
        /// </para>
        ///
        /// <para>
        ///   For <c>ZipFile</c> properties including <see cref="Encryption"/>, <see
        ///   cref="Password"/>, <see cref="SetCompression"/>, <see
        ///   cref="ProvisionalAlternateEncoding"/>, <see cref="ExtractExistingFile"/>,
        ///   <see cref="ZipErrorAction"/>, and <see cref="CompressionLevel"/>, their
        ///   respective values at the time of this call will be applied to the
        ///   <c>ZipEntry</c> added.
        /// </para>
        ///
        /// <para>
        ///   About progress events: When using the WriteDelegate, DotNetZip does
        ///   not issue any SaveProgress events with <c>EventType</c> = <see
        ///   cref="ZipProgressEventType.Saving_EntryBytesRead">
        ///   Saving_EntryBytesRead</see>. (This is because it is the
        ///   application's code that runs in WriteDelegate - there's no way for
        ///   DotNetZip to know when to issue a EntryBytesRead event.)
        ///   Applications that want to update a progress bar or similar status
        ///   indicator should do so from within the WriteDelegate
        ///   itself. DotNetZip will issue the other SaveProgress events,
        ///   including <see cref="ZipProgressEventType.Saving_Started">
        ///   Saving_Started</see>,
        ///   <see cref="ZipProgressEventType.Saving_BeforeWriteEntry">
        ///   Saving_BeforeWriteEntry</see>, and <see
        ///   cref="ZipProgressEventType.Saving_AfterWriteEntry">
        ///   Saving_AfterWriteEntry</see>.
        /// </para>
        ///
        /// <para>
        ///   Note: When you use PKZip encryption, it's normally necessary to
        ///   compute the CRC of the content to be encrypted, before compressing or
        ///   encrypting it. Therefore, when using PKZip encryption with a
        ///   WriteDelegate, the WriteDelegate CAN BE called twice: once to compute
        ///   the CRC, and the second time to potentially compress and
        ///   encrypt. Surprising, but true. This is because PKWARE specified that
        ///   the encryption initialization data depends on the CRC.
        ///   If this happens, for each call of the delegate, your
        ///   application must stream the same entry data in its entirety. If your
        ///   application writes different data during the second call, it will
        ///   result in a corrupt zip file.
        /// </para>
        ///
        /// <para>
        ///   The double-read behavior happens with all types of entries, not only
        ///   those that use WriteDelegate. It happens if you add an entry from a
        ///   filesystem file, or using a string, or a stream, or an opener/closer
        ///   pair. But in those cases, DotNetZip takes care of reading twice; in
        ///   the case of the WriteDelegate, the application code gets invoked
        ///   twice. Be aware.
        /// </para>
        ///
        /// <para>
        ///   As you can imagine, this can cause performance problems for large
        ///   streams, and it can lead to correctness problems when you use a
        ///   <c>WriteDelegate</c>. This is a pretty big pitfall.  There are two
        ///   ways to avoid it.  First, and most preferred: don't use PKZIP
        ///   encryption.  If you use the WinZip AES encryption, this problem
        ///   doesn't occur, because the encryption protocol doesn't require the CRC
        ///   up front. Second: if you do choose to use PKZIP encryption, write out
        ///   to a non-seekable stream (like standard output, or the
        ///   Response.OutputStream in an ASP.NET application).  In this case,
        ///   DotNetZip will use an alternative encryption protocol that does not
        ///   rely on the CRC of the content.  This also implies setting bit 3 in
        ///   the zip entry, which still presents problems for some zip tools.
        /// </para>
        ///
        /// <para>
        ///   In the future I may modify DotNetZip to *always* use bit 3 when PKZIP
        ///   encryption is in use.  This seems like a win overall, but there will
        ///   be some work involved.  If you feel strongly about it, visit the
        ///   DotNetZip forums and vote up <see
        ///   href="http://dotnetzip.codeplex.com/workitem/13686">the Workitem
        ///   tracking this issue</see>.
        /// </para>
        ///
        /// </remarks>
        ///
        /// <param name="entryName">the name of the entry to add</param>
        /// <param name="writer">the delegate which will write the entry content</param>
        /// <returns>the ZipEntry added</returns>
        ///
        /// <example>
        ///
        ///   This example shows an application filling a DataSet, then saving the
        ///   contents of that DataSet as XML, into a ZipEntry in a ZipFile, using an
        ///   anonymous delegate in C#. The DataSet XML is never saved to a disk file.
        ///
        /// <code lang="C#">
        /// var c1= new System.Data.SqlClient.SqlConnection(connstring1);
        /// var da = new System.Data.SqlClient.SqlDataAdapter()
        ///     {
        ///         SelectCommand=  new System.Data.SqlClient.SqlCommand(strSelect, c1)
        ///     };
        ///
        /// DataSet ds1 = new DataSet();
        /// da.Fill(ds1, "Invoices");
        ///
        /// using(Ionic.Zip.ZipFile zip = new Ionic.Zip.ZipFile())
        /// {
        ///     zip.AddEntry(zipEntryName, (name,stream) => ds1.WriteXml(stream) );
        ///     zip.Save(zipFileName);
        /// }
        /// </code>
        /// </example>
        ///
        /// <example>
        ///
        /// This example uses an anonymous method in C# as the WriteDelegate to provide
        /// the data for the ZipEntry. The example is a bit contrived - the
        /// <c>AddFile()</c> method is a simpler way to insert the contents of a file
        /// into an entry in a zip file. On the other hand, if there is some sort of
        /// processing or transformation of the file contents required before writing,
        /// the application could use the <c>WriteDelegate</c> to do it, in this way.
        ///
        /// <code lang="C#">
        /// using (var input = File.Open(filename, FileMode.Open, FileAccess.Read, FileShare.ReadWrite ))
        /// {
        ///     using(Ionic.Zip.ZipFile zip = new Ionic.Zip.ZipFile())
        ///     {
        ///         zip.AddEntry(zipEntryName, (name,output) =>
        ///             {
        ///                 byte[] buffer = new byte[BufferSize];
        ///                 int n;
        ///                 while ((n = input.Read(buffer, 0, buffer.Length)) != 0)
        ///                 {
        ///                     // could transform the data here...
        ///                     output.Write(buffer, 0, n);
        ///                     // could update a progress bar here
        ///                 }
        ///             });
        ///
        ///         zip.Save(zipFileName);
        ///     }
        /// }
        /// </code>
        /// </example>
        ///
        /// <example>
        ///
        /// This example uses a named delegate in VB to write data for the given
        /// ZipEntry (VB9 does not have anonymous delegates). The example here is a bit
        /// contrived - a simpler way to add the contents of a file to a ZipEntry is to
        /// simply use the appropriate <c>AddFile()</c> method.  The key scenario for
        /// which the <c>WriteDelegate</c> makes sense is saving a DataSet, in XML
        /// format, to the zip file. The DataSet can write XML to a stream, and the
        /// WriteDelegate is the perfect place to write into the zip file.  There may be
        /// other data structures that can write to a stream, but cannot be read as a
        /// stream.  The <c>WriteDelegate</c> would be appropriate for those cases as
        /// well.
        ///
        /// <code lang="VB">
        /// Private Sub WriteEntry (ByVal name As String, ByVal output As Stream)
        ///     Using input As FileStream = File.Open(filename, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)
        ///         Dim n As Integer = -1
        ///         Dim buffer As Byte() = New Byte(BufferSize){}
        ///         Do While n &lt;&gt; 0
        ///             n = input.Read(buffer, 0, buffer.Length)
        ///             output.Write(buffer, 0, n)
        ///         Loop
        ///     End Using
        /// End Sub
        ///
        /// Public Sub Run()
        ///     Using zip = New ZipFile
        ///         zip.AddEntry(zipEntryName, New WriteDelegate(AddressOf WriteEntry))
        ///         zip.Save(zipFileName)
        ///     End Using
        /// End Sub
        /// </code>
        /// </example>
        public ZipEntry AddEntry(string entryName, WriteDelegate writer)
        {
            ZipEntry ze = ZipEntry.CreateForWriter(entryName, writer);

            if (Verbose)
            {
                StatusMessageTextWriter.WriteLine("adding {0}...", entryName);
            }
            return(_InternalAddEntry(ze));
        }
        public ZipEntry AddFile(string fileName, String directoryPathInArchive)
        {
            string   nameInArchive = ZipEntry.NameInArchive(fileName, directoryPathInArchive);
            ZipEntry ze            = ZipEntry.CreateFromFile(fileName, nameInArchive);

            if (Verbose)
            {
                StatusMessageTextWriter.WriteLine("adding {0}...", fileName);
            }
            return(_InternalAddEntry(ze));
        }
        public ZipEntry AddEntry(string entryName, Stream stream)
        {
            ZipEntry ze = ZipEntry.CreateForStream(entryName, stream);

            ze.SetEntryTimes(DateTime.Now, DateTime.Now, DateTime.Now);
            if (Verbose)
            {
                StatusMessageTextWriter.WriteLine("adding {0}...", entryName);
            }
            return(_InternalAddEntry(ze));
        }
        /// <summary>
        ///   Add an entry, for which the application will provide a stream
        ///   containing the entry data, on a just-in-time basis.
        /// </summary>
        ///
        /// <remarks>
        /// <para>
        ///   In cases where the application wishes to open the stream that
        ///   holds the content for the ZipEntry, on a just-in-time basis, the
        ///   application can use this method.  The application provides an
        ///   opener delegate that will be called by the DotNetZip library to
        ///   obtain a readable stream that can be read to get the bytes for
        ///   the given entry.  Typically, this delegate opens a stream.
        ///   Optionally, the application can provide a closer delegate as
        ///   well, which will be called by DotNetZip when all bytes have been
        ///   read from the entry.
        /// </para>
        ///
        /// <para>
        ///   These delegates are called from within the scope of the call to
        ///   ZipFile.Save().
        /// </para>
        ///
        /// <para>
        ///   For <c>ZipFile</c> properties including <see cref="Encryption"/>, <see
        ///   cref="Password"/>, <see cref="SetCompression"/>, <see
        ///   cref="ProvisionalAlternateEncoding"/>, <see cref="ExtractExistingFile"/>,
        ///   <see cref="ZipErrorAction"/>, and <see cref="CompressionLevel"/>, their
        ///   respective values at the time of this call will be applied to the
        ///   <c>ZipEntry</c> added.
        /// </para>
        ///
        /// </remarks>
        ///
        /// <example>
        ///
        ///   This example uses anonymous methods in C# to open and close the
        ///   source stream for the content for a zip entry.
        ///
        /// <code lang="C#">
        /// using(Ionic.Zip.ZipFile zip = new Ionic.Zip.ZipFile())
        /// {
        ///     zip.AddEntry(zipEntryName,
        ///                  (name) =>  File.Open(filename, FileMode.Open, FileAccess.Read, FileShare.ReadWrite ),
        ///                  (name, stream) =>  stream.Close()
        ///                  );
        ///
        ///     zip.Save(zipFileName);
        /// }
        /// </code>
        ///
        /// </example>
        ///
        /// <example>
        ///
        ///   This example uses delegates in VB.NET to open and close the
        ///   the source stream for the content for a zip entry.  VB 9.0 lacks
        ///   support for "Sub" lambda expressions, and so the CloseDelegate must
        ///   be an actual, named Sub.
        ///
        /// <code lang="VB">
        ///
        /// Function MyStreamOpener(ByVal entryName As String) As Stream
        ///     '' This simply opens a file.  You probably want to do somethinig
        ///     '' more involved here: open a stream to read from a database,
        ///     '' open a stream on an HTTP connection, and so on.
        ///     Return File.OpenRead(entryName)
        /// End Function
        ///
        /// Sub MyStreamCloser(entryName As String, stream As Stream)
        ///     stream.Close()
        /// End Sub
        ///
        /// Public Sub Run()
        ///     Dim dirToZip As String = "fodder"
        ///     Dim zipFileToCreate As String = "Archive.zip"
        ///     Dim opener As OpenDelegate = AddressOf MyStreamOpener
        ///     Dim closer As CloseDelegate = AddressOf MyStreamCloser
        ///     Dim numFilestoAdd As Int32 = 4
        ///     Using zip As ZipFile = New ZipFile
        ///         Dim i As Integer
        ///         For i = 0 To numFilesToAdd - 1
        ///             zip.AddEntry(String.Format("content-{0:000}.txt"), opener, closer)
        ///         Next i
        ///         zip.Save(zipFileToCreate)
        ///     End Using
        /// End Sub
        ///
        /// </code>
        /// </example>
        ///
        /// <param name="entryName">the name of the entry to add</param>
        /// <param name="opener">
        ///  the delegate that will be invoked by ZipFile.Save() to get the
        ///  readable stream for the given entry. ZipFile.Save() will call
        ///  read on this stream to obtain the data for the entry. This data
        ///  will then be compressed and written to the newly created zip
        ///  file.
        /// </param>
        /// <param name="closer">
        ///  the delegate that will be invoked to close the stream. This may
        ///  be null (Nothing in VB), in which case no call is makde to close
        ///  the stream.
        /// </param>
        /// <returns>the ZipEntry added</returns>
        ///
        public ZipEntry AddEntry(string entryName, OpenDelegate opener, CloseDelegate closer)
        {
            ZipEntry ze = ZipEntry.CreateForJitStreamProvider(entryName, opener, closer);

            ze.SetEntryTimes(DateTime.Now, DateTime.Now, DateTime.Now);
            if (Verbose)
            {
                StatusMessageTextWriter.WriteLine("adding {0}...", entryName);
            }
            return(_InternalAddEntry(ze));
        }
        private string SfxSaveTemporary()
        {
            var    tempFileName = System.IO.Path.Combine(TempFileFolder, System.IO.Path.GetRandomFileName() + ".zip");
            Stream outstream    = null;

            try
            {
                bool save_contentsChanged = _contentsChanged;
                outstream = new System.IO.FileStream(tempFileName, System.IO.FileMode.CreateNew);
                if (outstream == null)
                {
                    throw new BadStateException(String.Format("Cannot open the temporary file ({0}) for writing.", tempFileName));
                }
                if (Verbose)
                {
                    StatusMessageTextWriter.WriteLine("Saving temp zip file....");
                }
                // write an entry in the zip for each file
                int n = 0;
                foreach (ZipEntry e in _entries)
                {
                    OnSaveEntry(n, e, true);
                    e.Write(outstream);
                    n++;
                    OnSaveEntry(n, e, false);
                    if (_saveOperationCanceled)
                    {
                        break;
                    }
                }

                if (!_saveOperationCanceled)
                {
                    WriteCentralDirectoryStructure(outstream);
                    outstream.Close();
                    outstream = null;
                }
                _contentsChanged = save_contentsChanged;
            }

            finally
            {
                if (outstream != null)
                {
                    try { outstream.Close(); }
                    catch { }
                    try { outstream.Dispose(); }
                    catch { }
                }
            }
            return(tempFileName);
        }
예제 #6
0
 private void RemoveTempFile()
 {
     try
     {
         if (File.Exists(_temporaryFileName))
         {
             File.Delete(_temporaryFileName);
         }
     }
     catch (Exception ex1)
     {
         if (Verbose)
             StatusMessageTextWriter.WriteLine("ZipFile::Save: could not delete temp file: {0}.", ex1.Message);
     }
 }
        /// <summary>
        /// Saves the ZipFile instance to a self-extracting zip archive.
        /// </summary>
        ///
        /// <remarks>
        ///
        /// <para>
        /// The generated exe image will execute on any machine that has the .NET Framework 2.0
        /// installed on it.
        /// </para>
        ///
        /// <para>
        /// There are two "flavors" of self-extracting archive.  The <c>WinFormsApplication</c>
        /// version will pop up a GUI and allow the user to select a target directory into which
        /// to extract. There's also a checkbox allowing the user to specify to overwrite
        /// existing files, and another checkbox to allow the user to request that Explorer be
        /// opened to see the extracted files after extraction.  The other flavor is
        /// <c>ConsoleApplication</c>.  A self-extractor generated with that flavor setting will
        /// run from the command line. It accepts command-line options to set the overwrite
        /// behavior, and to specify the target extraction directory.
        /// </para>
        ///
        /// <para>
        /// There are a few temporary files created during the saving to a self-extracting zip.
        /// These files are created in the directory pointed to by
        /// <see cref="ZipFile.TempFileFolder"/>, which defaults to  <see cref="System.IO.Path.GetTempPath"/>.
        /// These temporary files are removed upon successful completion of this method.
        /// </para>
        ///
        /// <para>
        /// When a user runs the SFX, the user's personal directory
        /// (<see cref="Environment.SpecialFolder.Personal"/>)
        /// will be used as the default extract location.
        /// The user who runs the SFX will have the opportunity to change the extract
        /// directory before extracting.
        /// </para>
        ///
        /// <para>
        /// NB: This method is not available in the version of DotNetZip
        /// build for the .NET Compact Framework, nor in the "Reduced" DotNEtZip library.
        /// </para>
        ///
        /// </remarks>
        ///
        /// <example>
        /// <code>
        /// string DirectoryPath = "c:\\Documents\\Project7";
        /// using (ZipFile zip = new ZipFile())
        /// {
        ///     zip.AddDirectory(DirectoryPath, System.IO.Path.GetFileName(DirectoryPath));
        ///     zip.Comment = "This will be embedded into a self-extracting console-based exe";
        ///     zip.SaveSelfExtractor("archive.exe", SelfExtractorFlavor.ConsoleApplication);
        /// }
        /// </code>
        /// <code lang="VB">
        /// Dim DirectoryPath As String = "c:\Documents\Project7"
        /// Using zip As New ZipFile()
        ///     zip.AddDirectory(DirectoryPath, System.IO.Path.GetFileName(DirectoryPath))
        ///     zip.Comment = "This will be embedded into a self-extracting console-based exe"
        ///     zip.SaveSelfExtractor("archive.exe", SelfExtractorFlavor.ConsoleApplication)
        /// End Using
        /// </code>
        /// </example>
        ///
        /// <param name="exeToGenerate">a pathname, possibly fully qualified, to be created. Typically it will end in an .exe extension.</param>
        /// <param name="flavor">Indicates whether a Winforms or Console self-extractor is desired.</param>
        public void SaveSelfExtractor(string exeToGenerate, SelfExtractorFlavor flavor)
        {
            if (File.Exists(exeToGenerate))
            {
                if (Verbose)
                {
                    StatusMessageTextWriter.WriteLine("The existing file ({0}) will be overwritten.", exeToGenerate);
                }
            }
            if (!exeToGenerate.EndsWith(".exe"))
            {
                if (Verbose)
                {
                    StatusMessageTextWriter.WriteLine("Warning: The generated self-extracting file will not have an .exe extension.");
                }
            }

            string TempZipFile = SfxSaveTemporary();

            OnSaveEvent(ZipProgressEventType.Saving_AfterSaveTempArchive);

            if (TempZipFile == null)
            {
                return; // cancelled
            }
            // look for myself (ZipFile will be present in the Ionic.Utils.Zip assembly)
            Assembly a1 = typeof(ZipFile).Assembly;

            //Console.WriteLine("DotNetZip assembly loc: {0}", a1.Location);

            Microsoft.CSharp.CSharpCodeProvider csharp = new Microsoft.CSharp.CSharpCodeProvider();

            // Perfect opportunity for a linq query, but I cannot use it.
            // The DotNetZip library can compile into 2.0, but needs to run on .NET 2.0.
            // Using LINQ would break that. Here's what it would look like:
            //
            //  var settings = (from x in SettingsList
            //          where x.Flavor == flavor
            //          select x).First();

            ExtractorSettings settings = null;

            foreach (var x in SettingsList)
            {
                if (x.Flavor == flavor)
                {
                    settings = x;
                    break;
                }
            }

            if (settings == null)
            {
                throw new BadStateException(String.Format("While saving a Self-Extracting Zip, Cannot find that flavor ({0})?", flavor));
            }

            // This is the list of referenced assemblies.  Ionic.Utils.Zip is needed here.
            // Also if it is the winforms (gui) extractor, we need other referenced assemblies.
            System.CodeDom.Compiler.CompilerParameters cp = new System.CodeDom.Compiler.CompilerParameters();
            cp.ReferencedAssemblies.Add(a1.Location);
            if (settings.ReferencedAssemblies != null)
            {
                foreach (string ra in settings.ReferencedAssemblies)
                {
                    cp.ReferencedAssemblies.Add(ra);
                }
            }

            cp.GenerateInMemory        = false;
            cp.GenerateExecutable      = true;
            cp.IncludeDebugInformation = false;
            cp.OutputAssembly          = exeToGenerate;

            Assembly a2 = Assembly.GetExecutingAssembly();

            string TempDir = GenerateUniquePathname("tmp", null);

            if ((settings.CopyThroughResources != null) && (settings.CopyThroughResources.Count != 0))
            {
                System.IO.Directory.CreateDirectory(TempDir);
                int    n     = 0;
                byte[] bytes = new byte[1024];
                foreach (string re in settings.CopyThroughResources)
                {
                    string filename = Path.Combine(TempDir, re);
                    using (Stream instream = a2.GetManifestResourceStream(re))
                    {
                        using (FileStream outstream = File.OpenWrite(filename))
                        {
                            do
                            {
                                n = instream.Read(bytes, 0, bytes.Length);
                                outstream.Write(bytes, 0, n);
                            } while (n > 0);
                        }
                    }
                    // add the embedded resource in our own assembly into the target assembly as an embedded resource
                    cp.EmbeddedResources.Add(filename);
                }
            }

            // add the zip file as an embedded resource
            cp.EmbeddedResources.Add(TempZipFile);

            // add the Ionic.Utils.Zip DLL as an embedded resource
            cp.EmbeddedResources.Add(a1.Location);

            //Console.WriteLine("Resources in this assembly:");
            //foreach (string rsrc in a2.GetManifestResourceNames())
            //{
            //    Console.WriteLine(rsrc);
            //}
            //Console.WriteLine();

            //Console.WriteLine("reading source code resources:");


            // concatenate all the source code resources into a single module
            var sb = new System.Text.StringBuilder();

            // set the default extract location if it is available
            bool wantCodeReplace = (flavor == SelfExtractorFlavor.WinFormsApplication && _defaultExtractLocation != null);

            if (wantCodeReplace)
            {
                _defaultExtractLocation = _defaultExtractLocation.Replace("\"", "");
            }

            foreach (string rc in settings.ResourcesToCompile)
            {
                //Console.WriteLine("  trying to read stream: ({0})", rc);
                Stream s = a2.GetManifestResourceStream(rc);
                using (StreamReader sr = new StreamReader(s))
                {
                    while (sr.Peek() >= 0)
                    {
                        string line = sr.ReadLine();
                        if (wantCodeReplace)
                        {
                            line = line.Replace("@@VALUE", _defaultExtractLocation);
                        }
                        sb.Append(line).Append("\n");
                    }
                }
                sb.Append("\n\n");
            }

            string LiteralSource = sb.ToString();

            System.CodeDom.Compiler.CompilerResults cr = csharp.CompileAssemblyFromSource(cp, LiteralSource);
            if (cr == null)
            {
                throw new SfxGenerationException("Cannot compile the extraction logic!");
            }

            if (Verbose)
            {
                foreach (string output in cr.Output)
                {
                    StatusMessageTextWriter.WriteLine(output);
                }
            }

            if (cr.Errors.Count != 0)
            {
                throw new SfxGenerationException("Errors compiling the extraction logic!");
            }

            OnSaveEvent(ZipProgressEventType.Saving_AfterCompileSelfExtractor);

            try
            {
                if (Directory.Exists(TempDir))
                {
                    try { Directory.Delete(TempDir, true); }
                    catch { }
                }

                if (File.Exists(TempZipFile))
                {
                    try { File.Delete(TempZipFile); }
                    catch { }
                }
            }
            catch { }

            OnSaveCompleted();

            if (Verbose)
            {
                StatusMessageTextWriter.WriteLine("Created self-extracting zip file {0}.", cr.PathToAssembly);
            }
            return;


            //       catch (Exception e1)
            //       {
            //  StatusMessageTextWriter.WriteLine("****Exception: " + e1);
            //  throw;
            //       }
            //       return;
        }
예제 #8
0
        /// <summary>
        /// Saves the Zip archive to a file, specified by the Name property of the <c>ZipFile</c>.
        /// </summary>
        ///
        /// <remarks>
        /// <para>
        /// The <c>ZipFile</c> instance is written to storage, typically a zip file in a
        /// filesystem, only when the caller calls <c>Save</c>.  The Save operation writes
        /// the zip content to a temporary file, and then renames the temporary file
        /// to the desired name. If necessary, this method will delete a pre-existing file
        /// before the rename.
        /// </para>
        ///
        /// <para> The <see cref="ZipFile.Name"/> property is specified either
        /// explicitly, or implicitly using one of the parameterized ZipFile
        /// constructors.  For COM Automation clients, the <c>Name</c> property must be
        /// set explicitly, because COM Automation clients cannot call parameterized
        /// constructors.  </para>
        ///
        /// <para>
        /// When using a filesystem file for the Zip output, it is possible to call
        /// <c>Save</c> multiple times on the <c>ZipFile</c> instance. With each call the zip
        /// content is re-written to the same output file.
        /// </para>
        ///
        /// <para>
        /// Data for entries that have been added to the <c>ZipFile</c> instance is written
        /// to the output when the <c>Save</c> method is called. This means that the input
        /// streams for those entries must be available at the time the application calls
        /// <c>Save</c>.  If, for example, the application adds entries with <c>AddEntry</c>
        /// using a dynamically-allocated <c>MemoryStream</c>, the memory stream must not
        /// have been disposed before the call to <c>Save</c>. See the <see
        /// cref="ZipEntry.InputStream"/> property for more discussion of the availability
        /// requirements of the input stream for an entry, and an approach for providing
        /// just-in-time stream lifecycle management.
        /// </para>
        ///
        /// </remarks>
        ///
        /// <seealso cref="Ionic.Zip.ZipFile.AddEntry(String, System.IO.Stream)"/>
        ///
        /// <exception cref="Ionic.Zip.BadStateException">
        /// Thrown if you haven't specified a location or stream for saving the zip,
        /// either in the constructor or by setting the Name property, or if you try to
        /// save a regular zip archive to a filename with a .exe extension.
        /// </exception>
        ///
        public void Save()
        {
            try
            {
                bool thisSaveUsedZip64 = false;
                _saveOperationCanceled             = false;
                _numberOfSegmentsForMostRecentSave = 0;
                OnSaveStarted();

                if (WriteStream == null)
                {
                    throw new BadStateException("You haven't specified where to save the zip.");
                }

                if (_name != null && _name.EndsWith(".exe") && !_SavingSfx)
                {
                    throw new BadStateException("You specified an EXE for a plain zip file.");
                }

                // check if modified, before saving.
                if (!_contentsChanged)
                {
                    OnSaveCompleted();
                    if (Verbose)
                    {
                        StatusMessageTextWriter.WriteLine("No save is necessary....");
                    }
                    return;
                }

                Reset();

                if (Verbose)
                {
                    StatusMessageTextWriter.WriteLine("saving....");
                }

                // validate the number of entries
                if (_entries.Count >= 0xFFFF && _zip64 == Zip64Option.Never)
                {
                    throw new ZipException("The number of entries is 65535 or greater. Consider setting the UseZip64WhenSaving property on the ZipFile instance.");
                }


                // write an entry in the zip for each file
                int n = 0;
                // workitem 9831
                ICollection <ZipEntry> c = (SortEntriesBeforeSaving) ? EntriesSorted : Entries;
                foreach (ZipEntry e in c)     // _entries.Values
                {
                    OnSaveEntry(n, e, true);
                    e.Write(WriteStream);
                    if (_saveOperationCanceled)
                    {
                        break;
                    }

                    n++;
                    OnSaveEntry(n, e, false);
                    if (_saveOperationCanceled)
                    {
                        break;
                    }

                    // Some entries can be skipped during the save.
                    if (e.IncludedInMostRecentSave)
                    {
                        thisSaveUsedZip64 |= e.OutputUsedZip64.Value;
                    }
                }



                if (_saveOperationCanceled)
                {
                    return;
                }

                var zss = WriteStream as ZipSegmentedStream;

                _numberOfSegmentsForMostRecentSave = (zss != null)
                    ? zss.CurrentSegment
                    : 1;

                bool directoryNeededZip64 = ZipOutput.WriteCentralDirectoryStructure(WriteStream,
                                                                                     c,
                                                                                     _numberOfSegmentsForMostRecentSave,
                                                                                     _zip64,
                                                                                     Comment,
                                                                                     ProvisionalAlternateEncoding);

                OnSaveEvent(ZipProgressEventType.Saving_AfterSaveTempArchive);

                _hasBeenSaved    = true;
                _contentsChanged = false;

                thisSaveUsedZip64 |= directoryNeededZip64;
                _OutputUsesZip64   = new Nullable <bool>(thisSaveUsedZip64);


                // do the rename as necessary
                if (_name != null &&
                    (_temporaryFileName != null || zss != null))
                {
                    // _temporaryFileName may remain null if we are writing to a stream.
                    // only close the stream if there is a file behind it.
                    WriteStream.Close();
#if !NETCF
                    WriteStream.Dispose();
#endif
                    if (_saveOperationCanceled)
                    {
                        return;
                    }

                    if ((_fileAlreadyExists) && (this._readstream != null))
                    {
                        // This means we opened and read a zip file.
                        // If we are now saving to the same file, we need to close the
                        // orig file, first.
                        this._readstream.Close();
                        this._readstream = null;
                        // the archiveStream for each entry needs to be null
                        foreach (var e in c)
                        {
                            e._archiveStream = null;
                        }
                    }

                    if (_fileAlreadyExists)
                    {
                        // We do not just call File.Replace() here because
                        // there is a possibility that the TEMP volume is different
                        // that the volume for the final file (c:\ vs d:\).
                        // So we need to do a Delete+Move pair.
                        //
                        // Ideally this would be transactional.
                        //
                        // It's possible that the delete succeeds and the move fails.
                        // in that case, we're hosed, and we'll throw.
                        //
                        // Could make this more complicated by moving (renaming) the first file, then
                        // moving the second, then deleting the first file. But the
                        // error handling and unwrap logic just gets more complicated.
                        //
                        // Better to just keep it simple.
                        File.Delete(_name);
                    }

                    OnSaveEvent(ZipProgressEventType.Saving_BeforeRenameTempArchive);
                    File.Move((zss != null) ? zss.CurrentName : _temporaryFileName, _name);
                    OnSaveEvent(ZipProgressEventType.Saving_AfterRenameTempArchive);

                    _fileAlreadyExists = true;
                }

                NotifyEntriesSaveComplete(c);
                OnSaveCompleted();
                _JustSaved = true;
            }

            // workitem 5043
            finally
            {
                CleanupAfterSaveOperation();
            }

            return;
        }
        public void Save()
        {
            try
            {
                bool thisSaveUsedZip64 = false;
                _saveOperationCanceled             = false;
                _numberOfSegmentsForMostRecentSave = 0;
                OnSaveStarted();

                if (WriteStream == null)
                {
                    throw new BadStateException("You haven't specified where to save the zip.");
                }

                if (_name != null && _name.EndsWith(".exe") && !_SavingSfx)
                {
                    throw new BadStateException("You specified an EXE for a plain zip file.");
                }

                // check if modified, before saving.
                if (!_contentsChanged)
                {
                    OnSaveCompleted();
                    if (Verbose)
                    {
                        StatusMessageTextWriter.WriteLine("No save is necessary....");
                    }
                    return;
                }

                Reset(true);

                if (Verbose)
                {
                    StatusMessageTextWriter.WriteLine("saving....");
                }

                // validate the number of entries
                if (_entries.Count >= 0xFFFF && _zip64 == Zip64Option.Never)
                {
                    throw new ZipException("The number of entries is 65535 or greater. Consider setting the UseZip64WhenSaving property on the ZipFile instance.");
                }


                // write an entry in the zip for each file
                int n = 0;
                // workitem 9831
                ICollection <ZipEntry> c = (SortEntriesBeforeSaving) ? EntriesSorted : Entries;
                foreach (ZipEntry e in c) // _entries.Values
                {
                    OnSaveEntry(n, e, true);
                    e.Write(WriteStream);
                    if (_saveOperationCanceled)
                    {
                        break;
                    }

                    n++;
                    OnSaveEntry(n, e, false);
                    if (_saveOperationCanceled)
                    {
                        break;
                    }

                    // Some entries can be skipped during the save.
                    if (e.IncludedInMostRecentSave)
                    {
                        thisSaveUsedZip64 |= e.OutputUsedZip64.Value;
                    }
                }



                if (_saveOperationCanceled)
                {
                    return;
                }

                var zss = WriteStream as ZipSegmentedStream;

                _numberOfSegmentsForMostRecentSave = (zss != null)
                    ? zss.CurrentSegment
                    : 1;

                bool directoryNeededZip64 =
                    ZipOutput.WriteCentralDirectoryStructure
                        (WriteStream,
                        c,
                        _numberOfSegmentsForMostRecentSave,
                        _zip64,
                        Comment,
                        new ZipContainer(this));

                OnSaveEvent(ZipProgressEventType.Saving_AfterSaveTempArchive);

                _hasBeenSaved    = true;
                _contentsChanged = false;

                thisSaveUsedZip64 |= directoryNeededZip64;
                _OutputUsesZip64   = new Nullable <bool>(thisSaveUsedZip64);

                if (_fileAlreadyExists && this._readstream != null)
                {
                    // This means we opened and read a zip file.
                    // If we are now saving, we need to close the orig file, first.
                    this._readstream.Close();
                    this._readstream = null;
                }
                // the archiveStream for each entry needs to be null
                foreach (var e in c)
                {
                    var zss1 = e._archiveStream as ZipSegmentedStream;
                    if (zss1 != null)
                    {
                        zss1.Dispose();
                    }
                    e._archiveStream = null;
                }

                // do the rename as necessary
                if (_name != null &&
                    (_temporaryFileName != null || zss != null))
                {
                    // _temporaryFileName may remain null if we are writing to a stream.
                    // only close the stream if there is a file behind it.
                    WriteStream.Dispose();

                    if (_saveOperationCanceled)
                    {
                        return;
                    }

                    string tmpName = null;
                    if (File.Exists(_name))
                    {
                        tmpName = _name + "." + Path.GetRandomFileName();
                        if (File.Exists(tmpName))
                        {
                            DeleteFileWithRetry(tmpName);
                        }
                        File.Move(_name, tmpName);
                    }

                    OnSaveEvent(ZipProgressEventType.Saving_BeforeRenameTempArchive);
                    File.Move((zss != null) ? zss.CurrentTempName : _temporaryFileName,
                              _name);

                    OnSaveEvent(ZipProgressEventType.Saving_AfterRenameTempArchive);

                    if (tmpName != null)
                    {
                        try
                        {
                            // not critical
                            if (File.Exists(tmpName))
                            {
                                File.Delete(tmpName);
                            }
                        }
                        catch
                        {
                            // don't care about exceptions here.
                        }
                    }
                    _fileAlreadyExists = true;
                }
                _readName = _name;

                NotifyEntriesSaveComplete(c);
                OnSaveCompleted();
                _JustSaved = true;
            }

            // workitem 5043
            finally
            {
                CleanupAfterSaveOperation();
            }

            return;
        }
예제 #10
0
        private void _InternalExtractAll(string path, bool overrideExtractExistingProperty)
        {
            bool header = Verbose;

            _inExtractAll = true;
            try
            {
                OnExtractAllStarted(path);

                int n = 0;
                foreach (ZipEntry e in _entries.Values)
                {
                    if (header)
                    {
                        StatusMessageTextWriter.WriteLine("\n{1,-22} {2,-8} {3,4}   {4,-8}  {0}",
                                                          "Name", "Modified", "Size", "Ratio", "Packed");
                        StatusMessageTextWriter.WriteLine(new System.String('-', 72));
                        header = false;
                    }
                    if (Verbose)
                    {
                        StatusMessageTextWriter.WriteLine("{1,-22} {2,-8} {3,4:F0}%   {4,-8} {0}",
                                                          e.FileName,
                                                          e.LastModified.ToString("yyyy-MM-dd HH:mm:ss"),
                                                          e.UncompressedSize,
                                                          e.CompressionRatio,
                                                          e.CompressedSize);
                        if (!String.IsNullOrEmpty(e.Comment))
                        {
                            StatusMessageTextWriter.WriteLine("  Comment: {0}", e.Comment);
                        }
                    }
                    e.Password = _Password;  // this may be null
                    OnExtractEntry(n, true, e, path);
                    if (overrideExtractExistingProperty)
                    {
                        e.ExtractExistingFile = this.ExtractExistingFile;
                    }
                    e.Extract(path);
                    n++;
                    OnExtractEntry(n, false, e, path);
                    if (_extractOperationCanceled)
                    {
                        break;
                    }
                }

                // workitem 8264:
                // now, set times on directory entries, again.
                // The problem is, extracting a file changes the times on the parent
                // directory.  So after all files have been extracted, we have to
                // run through the directories again.
                foreach (ZipEntry e in _entries.Values)
                {
                    // check if it is a directory
                    if ((e.IsDirectory) || (e.FileName.EndsWith("/")))
                    {
                        string outputFile = (e.FileName.StartsWith("/"))
                            ? Path.Combine(path, e.FileName.Substring(1))
                            : Path.Combine(path, e.FileName);

                        e._SetTimes(outputFile, false);
                    }
                }

                OnExtractAllCompleted(path);
            }
            finally
            {
                _inExtractAll = false;
            }
        }
예제 #11
0
        /// <summary>
        ///   Saves the Zip archive to a file, specified by the Name property of the
        ///   <c>ZipFile</c>.
        /// </summary>
        ///
        /// <remarks>
        /// <para>
        ///   The <c>ZipFile</c> instance is written to storage, typically a zip file
        ///   in a filesystem, only when the caller calls <c>Save</c>.  In the typical
        ///   case, the Save operation writes the zip content to a temporary file, and
        ///   then renames the temporary file to the desired name. If necessary, this
        ///   method will delete a pre-existing file before the rename.
        /// </para>
        ///
        /// <para>
        ///   The <see cref="ZipFile.Name"/> property is specified either explicitly,
        ///   or implicitly using one of the parameterized ZipFile constructors.  For
        ///   COM Automation clients, the <c>Name</c> property must be set explicitly,
        ///   because COM Automation clients cannot call parameterized constructors.
        /// </para>
        ///
        /// <para>
        ///   When using a filesystem file for the Zip output, it is possible to call
        ///   <c>Save</c> multiple times on the <c>ZipFile</c> instance. With each
        ///   call the zip content is re-written to the same output file.
        /// </para>
        ///
        /// <para>
        ///   Data for entries that have been added to the <c>ZipFile</c> instance is
        ///   written to the output when the <c>Save</c> method is called. This means
        ///   that the input streams for those entries must be available at the time
        ///   the application calls <c>Save</c>.  If, for example, the application
        ///   adds entries with <c>AddEntry</c> using a dynamically-allocated
        ///   <c>MemoryStream</c>, the memory stream must not have been disposed
        ///   before the call to <c>Save</c>. See the <see
        ///   cref="ZipEntry.InputStream"/> property for more discussion of the
        ///   availability requirements of the input stream for an entry, and an
        ///   approach for providing just-in-time stream lifecycle management.
        /// </para>
        ///
        /// </remarks>
        ///
        /// <seealso cref="Ionic.Zip.ZipFile.AddEntry(String, System.IO.Stream)"/>
        ///
        /// <exception cref="Ionic.Zip.BadStateException">
        ///   Thrown if you haven't specified a location or stream for saving the zip,
        ///   either in the constructor or by setting the Name property, or if you try
        ///   to save a regular zip archive to a filename with a .exe extension.
        /// </exception>
        ///
        /// <exception cref="System.OverflowException">
        ///   Thrown if <see cref="MaxOutputSegmentSize"/> is non-zero, and the number
        ///   of segments that would be generated for the spanned zip file during the
        ///   save operation exceeds 99.  If this happens, you need to increase the
        ///   segment size.
        /// </exception>
        ///
        internal void Save()
        {
            try
            {
                bool thisSaveUsedZip64 = false;
                _saveOperationCanceled             = false;
                _numberOfSegmentsForMostRecentSave = 0;
                OnSaveStarted();

                if (WriteStream == null)
                {
                    throw new BadStateException("You haven't specified where to save the zip.");
                }


                // check if modified, before saving.
                if (!_contentsChanged)
                {
                    OnSaveCompleted();
                    if (Verbose)
                    {
                        StatusMessageTextWriter.WriteLine("No save is necessary....");
                    }
                    return;
                }

                Reset(true);

                if (Verbose)
                {
                    StatusMessageTextWriter.WriteLine("saving....");
                }

                // validate the number of entries
                if (_entries.Count >= 0xFFFF && _zip64 == Zip64Option.Never)
                {
                    throw new ZipException("The number of entries is 65535 or greater. Consider setting the UseZip64WhenSaving property on the ZipFile instance.");
                }


                // write an entry in the zip for each file
                int n = 0;
                // workitem 9831
                ICollection <ZipEntry> c = (SortEntriesBeforeSaving) ? EntriesSorted : Entries;
                foreach (ZipEntry e in c) // _entries.Values
                {
                    OnSaveEntry(n, e, true);
                    e.Write(WriteStream);
                    if (_saveOperationCanceled)
                    {
                        break;
                    }

                    n++;
                    OnSaveEntry(n, e, false);
                    if (_saveOperationCanceled)
                    {
                        break;
                    }

                    // Some entries can be skipped during the save.
                    if (e.IncludedInMostRecentSave)
                    {
                        thisSaveUsedZip64 |= e.OutputUsedZip64.Value;
                    }
                }



                if (_saveOperationCanceled)
                {
                    return;
                }

                var zss = WriteStream as ZipSegmentedStream;

                _numberOfSegmentsForMostRecentSave = (zss != null)
                    ? zss.CurrentSegment
                    : 0;

                bool directoryNeededZip64 =
                    ZipOutput.WriteCentralDirectoryStructure
                        (WriteStream,
                        c,
                        (zss != null) ? zss.CurrentSegment : 1,
                        _zip64,
                        Comment,
                        new ZipContainer(this));

                OnSaveEvent(ZipProgressEventType.Saving_AfterSaveTempArchive);

                _hasBeenSaved    = true;
                _contentsChanged = false;

                thisSaveUsedZip64 |= directoryNeededZip64;
                _OutputUsesZip64   = new Nullable <bool>(thisSaveUsedZip64);



                NotifyEntriesSaveComplete(c);
                OnSaveCompleted();
                _JustSaved = true;
            }

            // workitem 5043
            finally
            {
                CleanupAfterSaveOperation();
            }

            return;
        }
        private ZipEntry AddOrUpdateDirectoryImpl(string directoryName,
                                                  string rootDirectoryPathInArchive,
                                                  AddOrUpdateAction action,
                                                  bool recurse,
                                                  int level)
        {
            if (Verbose)
            {
                StatusMessageTextWriter.WriteLine("{0} {1}...",
                                                  (action == AddOrUpdateAction.AddOnly) ? "adding" : "Adding or updating",
                                                  directoryName);
            }

            if (level == 0)
            {
                _addOperationCanceled = false;
                OnAddStarted();
            }

            // workitem 13371
            if (_addOperationCanceled)
            {
                return(null);
            }

            string   dirForEntries = rootDirectoryPathInArchive;
            ZipEntry baseDir       = null;

            if (level > 0)
            {
                int f = directoryName.Length;
                for (int i = level; i > 0; i--)
                {
                    f = directoryName.LastIndexOfAny("/\\".ToCharArray(), f - 1, f - 1);
                }

                dirForEntries = directoryName.Substring(f + 1);
                dirForEntries = Path.Combine(rootDirectoryPathInArchive, dirForEntries);
            }

            // if not top level, or if the root is non-empty, then explicitly add the directory
            if (level > 0 || rootDirectoryPathInArchive != "")
            {
                baseDir                        = ZipEntry.CreateFromFile(directoryName, dirForEntries);
                baseDir._container             = new ZipContainer(this);
                baseDir.AlternateEncoding      = this.AlternateEncoding; // workitem 6410
                baseDir.AlternateEncodingUsage = this.AlternateEncodingUsage;
                baseDir.MarkAsDirectory();
                baseDir.EmitTimesInWindowsFormatWhenSaving = _emitNtfsTimes;
                baseDir.EmitTimesInUnixFormatWhenSaving    = _emitUnixTimes;

                // add the directory only if it does not exist.
                // It's not an error if it already exists.
                if (!_entries.ContainsKey(baseDir.FileName))
                {
                    InternalAddEntry(baseDir.FileName, baseDir);
                    AfterAddEntry(baseDir);
                }
                dirForEntries = baseDir.FileName;
            }

            if (!_addOperationCanceled)
            {
                String[] filenames = Directory.GetFiles(directoryName);

                if (recurse)
                {
                    // add the files:
                    foreach (String filename in filenames)
                    {
                        if (_addOperationCanceled)
                        {
                            break;
                        }
                        if (action == AddOrUpdateAction.AddOnly)
                        {
                            AddFile(filename, dirForEntries);
                        }
                        else
                        {
                            UpdateFile(filename, dirForEntries);
                        }
                    }

                    if (!_addOperationCanceled)
                    {
                        // add the subdirectories:
                        String[] dirnames = Directory.GetDirectories(directoryName);
                        foreach (String dir in dirnames)
                        {
                            // workitem 8617: Optionally traverse reparse points
                            FileAttributes fileAttrs = System.IO.File.GetAttributes(dir);
                            if (this.AddDirectoryWillTraverseReparsePoints ||
                                ((fileAttrs & FileAttributes.ReparsePoint) == 0)
                                )
                            {
                                AddOrUpdateDirectoryImpl(dir, rootDirectoryPathInArchive, action, recurse, level + 1);
                            }
                        }
                    }
                }
            }

            if (level == 0)
            {
                OnAddCompleted();
            }

            return(baseDir);
        }
예제 #13
0
        private void _SaveSfxStub(string exeToGenerate, SelfExtractorFlavor flavor, string defaultExtractLocation, string postExtractCmdLine, string nameOfIconFile)
        {
            bool   removeIconFile = false;
            string StubExe        = null;
            string TempDir        = null;

            try
            {
                if (File.Exists(exeToGenerate))
                {
                    if (Verbose)
                    {
                        StatusMessageTextWriter.WriteLine("The existing file ({0}) will be overwritten.", exeToGenerate);
                    }
                }
                if (!exeToGenerate.EndsWith(".exe"))
                {
                    if (Verbose)
                    {
                        StatusMessageTextWriter.WriteLine("Warning: The generated self-extracting file will not have an .exe extension.");
                    }
                }

                StubExe = GenerateTempPathname("exe", null);

                // get the Ionic.Zip assembly
                Assembly a1 = typeof(ZipFile).Assembly;

                Microsoft.CSharp.CSharpCodeProvider csharp = new Microsoft.CSharp.CSharpCodeProvider();

                // Perfect opportunity for a linq query, but I cannot use it.
                // The DotNetZip library can compile into 2.0, but needs to run on .NET 2.0.
                // Using LINQ would break that. Here's what it would look like:
                //
                //      var settings = (from x in SettingsList
                //                      where x.Flavor == flavor
                //                      select x).First();

                ExtractorSettings settings = null;
                foreach (var x in SettingsList)
                {
                    if (x.Flavor == flavor)
                    {
                        settings = x;
                        break;
                    }
                }

                if (settings == null)
                {
                    throw new BadStateException(String.Format("While saving a Self-Extracting Zip, Cannot find that flavor ({0})?", flavor));
                }

                // This is the list of referenced assemblies.  Ionic.Zip is needed here.
                // Also if it is the winforms (gui) extractor, we need other referenced assemblies,
                // like System.Windows.Forms.dll, etc.
                System.CodeDom.Compiler.CompilerParameters cp = new System.CodeDom.Compiler.CompilerParameters();
                cp.ReferencedAssemblies.Add(a1.Location);
                if (settings.ReferencedAssemblies != null)
                {
                    foreach (string ra in settings.ReferencedAssemblies)
                    {
                        cp.ReferencedAssemblies.Add(ra);
                    }
                }

                cp.GenerateInMemory        = false;
                cp.GenerateExecutable      = true;
                cp.IncludeDebugInformation = false;
                cp.CompilerOptions         = "";

                Assembly a2 = Assembly.GetExecutingAssembly();

                if (nameOfIconFile == null)
                {
                    removeIconFile = true;
                    nameOfIconFile = GenerateTempPathname("ico", null);
                    ExtractResourceToFile(a2, "Ionic.Zip.Resources.zippedFile.ico", nameOfIconFile);
                    cp.CompilerOptions += String.Format("/win32icon:\"{0}\"", nameOfIconFile);
                }
                else if (nameOfIconFile != "")
                {
                    cp.CompilerOptions += String.Format("/win32icon:\"{0}\"", nameOfIconFile);
                }

                //cp.IncludeDebugInformation = true;
                cp.OutputAssembly = StubExe;
                if (flavor == SelfExtractorFlavor.WinFormsApplication)
                {
                    cp.CompilerOptions += " /target:winexe";
                }

                if (cp.CompilerOptions == "")
                {
                    cp.CompilerOptions = null;
                }

                TempDir = GenerateTempPathname("tmp", null);
                if ((settings.CopyThroughResources != null) && (settings.CopyThroughResources.Count != 0))
                {
                    System.IO.Directory.CreateDirectory(TempDir);
                    foreach (string re in settings.CopyThroughResources)
                    {
                        string filename = Path.Combine(TempDir, re);

                        ExtractResourceToFile(a2, re, filename);
                        // add the file into the target assembly as an embedded resource
                        cp.EmbeddedResources.Add(filename);
                    }
                }

                // add the Ionic.Utils.Zip DLL as an embedded resource
                cp.EmbeddedResources.Add(a1.Location);

                //Console.WriteLine("Resources in this assembly:");
                //foreach (string rsrc in a2.GetManifestResourceNames())
                //{
                //    Console.WriteLine(rsrc);
                //}
                //Console.WriteLine();

                //Console.WriteLine("reading source code resources:");


                // concatenate all the source code resources into a single module
                var sb = new System.Text.StringBuilder();

                // assembly attributes
                sb.Append("[assembly: System.Reflection.AssemblyTitle(\"DotNetZip SFX Archive\")]\n");
                sb.Append("[assembly: System.Reflection.AssemblyProduct(\"ZipLibrary\")]\n");
                sb.Append("[assembly: System.Reflection.AssemblyCopyright(\"Copyright © Dino Chiesa 2008, 2009\")]\n");
                sb.Append(String.Format("[assembly: System.Reflection.AssemblyVersion(\"{0}\")]\n\n", ZipFile.LibraryVersion.ToString()));


                // Set the default extract location if it is available, and if supported.

                bool haveLocation = (defaultExtractLocation != null);
                if (haveLocation)
                {
                    defaultExtractLocation = defaultExtractLocation.Replace("\"", "").Replace("\\", "\\\\");
                }

                foreach (string rc in settings.ResourcesToCompile)
                {
                    //Console.WriteLine("  trying to read stream: ({0})", rc);
                    Stream s = a2.GetManifestResourceStream(rc);
                    if (s == null)
                    {
                        throw new ZipException(String.Format("missing resource '{0}'", rc));
                    }
                    using (StreamReader sr = new StreamReader(s))
                    {
                        while (sr.Peek() >= 0)
                        {
                            string line = sr.ReadLine();
                            if (haveLocation)
                            {
                                line = line.Replace("@@EXTRACTLOCATION", defaultExtractLocation);
                            }

                            if (postExtractCmdLine != null)
                            {
                                line = line.Replace("@@POST_UNPACK_CMD_LINE", postExtractCmdLine.Replace("\\", "\\\\"));
                            }

                            sb.Append(line).Append("\n");
                        }
                    }
                    sb.Append("\n\n");
                }

                string LiteralSource = sb.ToString();

                #if DEBUGSFX
                // for debugging only
                string sourceModule = GenerateTempPathname("cs", null);
                using (StreamWriter sw = File.CreateText(sourceModule))
                {
                    sw.Write(LiteralSource);
                }
                Console.WriteLine("source: {0}", sourceModule);
                #endif

                System.CodeDom.Compiler.CompilerResults cr = csharp.CompileAssemblyFromSource(cp, LiteralSource);


                if (cr == null)
                {
                    throw new SfxGenerationException("Cannot compile the extraction logic!");
                }

                if (Verbose)
                {
                    foreach (string output in cr.Output)
                    {
                        StatusMessageTextWriter.WriteLine(output);
                    }
                }

                if (cr.Errors.Count != 0)
                {
                    //Console.ReadLine();
                    string sourcefile = GenerateTempPathname("cs", null);
                    using (TextWriter tw = new StreamWriter(sourcefile))
                    {
                        tw.Write(LiteralSource);
                    }
                    throw new SfxGenerationException(String.Format("Errors compiling the extraction logic!  {0}", sourcefile));
                }

                OnSaveEvent(ZipProgressEventType.Saving_AfterCompileSelfExtractor);

                // Now, copy the resulting EXE image to the _writestream.
                // Because this stub exe is being saved first, the effect will be to
                // concatenate the exe and the zip data together.
                using (System.IO.Stream input = System.IO.File.OpenRead(StubExe))
                {
                    byte[] buffer = new byte[4000];
                    int    n      = 1;
                    while (n != 0)
                    {
                        n = input.Read(buffer, 0, buffer.Length);
                        if (n != 0)
                        {
                            WriteStream.Write(buffer, 0, n);
                        }
                    }
                }

                OnSaveEvent(ZipProgressEventType.Saving_AfterSaveTempArchive);
            }
            finally
            {
                try
                {
                    if (Directory.Exists(TempDir))
                    {
                        try { Directory.Delete(TempDir, true); }
                        catch { }
                    }
                    if (File.Exists(StubExe))
                    {
                        try { File.Delete(StubExe); }
                        catch { }
                    }
                    if (removeIconFile && File.Exists(nameOfIconFile))
                    {
                        try { File.Delete(nameOfIconFile); }
                        catch { }
                    }
                }
                catch { }
            }

            return;
        }
예제 #14
0
        private void _SaveSfxStub(string exeToGenerate, SelfExtractorSaveOptions options)
        {
            string nameOfIconFile      = null;
            string stubExe             = null;
            string unpackedResourceDir = null;
            string tmpDir = null;

            try
            {
                if (File.Exists(exeToGenerate))
                {
                    if (Verbose)
                    {
                        StatusMessageTextWriter.WriteLine("The existing file ({0}) will be overwritten.", exeToGenerate);
                    }
                }
                if (!exeToGenerate.EndsWith(".exe"))
                {
                    if (Verbose)
                    {
                        StatusMessageTextWriter.WriteLine("Warning: The generated self-extracting file will not have an .exe extension.");
                    }
                }

                // workitem 10553
                tmpDir  = TempFileFolder ?? Path.GetDirectoryName(exeToGenerate);
                stubExe = GenerateTempPathname(tmpDir, "exe");

                // get the Ionic.Zip assembly
                Assembly a1 = typeof(ZipFile).Assembly;

                using (var csharp = new Microsoft.CSharp.CSharpCodeProvider
                                        (new Dictionary <string, string>()
                {
                    { "CompilerVersion", "v2.0" }
                })) {
                    // The following is a perfect opportunity for a linq query, but
                    // I cannot use it.  DotNetZip needs to run on .NET 2.0,
                    // and using LINQ would break that. Here's what it would look
                    // like:
                    //
                    //   var settings = (from x in SettingsList
                    //                   where x.Flavor == flavor
                    //                   select x).First();

                    ExtractorSettings settings = null;
                    foreach (var x in SettingsList)
                    {
                        if (x.Flavor == options.Flavor)
                        {
                            settings = x;
                            break;
                        }
                    }

                    // sanity check; should never happen
                    if (settings == null)
                    {
                        throw new BadStateException(String.Format("While saving a Self-Extracting Zip, Cannot find that flavor ({0})?", options.Flavor));
                    }

                    // This is the list of referenced assemblies.  Ionic.Zip is
                    // needed here.  Also if it is the winforms (gui) extractor, we
                    // need other referenced assemblies, like
                    // System.Windows.Forms.dll, etc.
                    var cp = new System.CodeDom.Compiler.CompilerParameters();
                    cp.ReferencedAssemblies.Add(a1.Location);
                    if (settings.ReferencedAssemblies != null)
                    {
                        foreach (string ra in settings.ReferencedAssemblies)
                        {
                            cp.ReferencedAssemblies.Add(ra);
                        }
                    }

                    cp.GenerateInMemory        = false;
                    cp.GenerateExecutable      = true;
                    cp.IncludeDebugInformation = false;
                    cp.CompilerOptions         = "";

                    Assembly a2 = Assembly.GetExecutingAssembly();

                    // Use this to concatenate all the source code resources into a
                    // single module.
                    var sb = new System.Text.StringBuilder();

                    // In case there are compiler errors later, we allocate a source
                    // file name now. If errors are detected, we'll spool the source
                    // code as well as the errors (in comments) into that filename,
                    // and throw an exception with the filename.  Makes it easier to
                    // diagnose.  This should be rare; most errors happen only
                    // during devlpmt of DotNetZip itself, but there are rare
                    // occasions when they occur in other cases.
                    string sourceFile = GenerateTempPathname(tmpDir, "cs");


                    // // debugging: enumerate the resources in this assembly
                    // Console.WriteLine("Resources in this assembly:");
                    // foreach (string rsrc in a2.GetManifestResourceNames())
                    //   {
                    //     Console.WriteLine(rsrc);
                    //   }
                    // Console.WriteLine();


                    // all the source code is embedded in the DLL as a zip file.
                    using (ZipFile zip = ZipFile.Read(a2.GetManifestResourceStream("Ionic.Zip.Resources.ZippedResources.zip")))
                    {
                        // // debugging: enumerate the files in the embedded zip
                        // Console.WriteLine("Entries in the embbedded zip:");
                        // foreach (ZipEntry entry in zip)
                        //   {
                        //     Console.WriteLine(entry.FileName);
                        //   }
                        // Console.WriteLine();

                        unpackedResourceDir = GenerateTempPathname(tmpDir, "tmp");

                        if (String.IsNullOrEmpty(options.IconFile))
                        {
                            // Use the ico file that is embedded into the Ionic.Zip
                            // DLL itself.  To do this we must unpack the icon to
                            // the filesystem, in order to specify it on the cmdline
                            // of csc.exe.  This method will remove the unpacked
                            // file later.
                            System.IO.Directory.CreateDirectory(unpackedResourceDir);
                            ZipEntry e = zip["zippedFile.ico"];
                            // Must not extract a readonly file - it will be impossible to
                            // delete later.
                            if ((e.Attributes & FileAttributes.ReadOnly) == FileAttributes.ReadOnly)
                            {
                                e.Attributes ^= FileAttributes.ReadOnly;
                            }
                            e.Extract(unpackedResourceDir);
                            nameOfIconFile      = Path.Combine(unpackedResourceDir, "zippedFile.ico");
                            cp.CompilerOptions += String.Format("/win32icon:\"{0}\"", nameOfIconFile);
                        }
                        else
                        {
                            cp.CompilerOptions += String.Format("/win32icon:\"{0}\"", options.IconFile);
                        }

                        cp.OutputAssembly = stubExe;

                        if (options.Flavor == SelfExtractorFlavor.WinFormsApplication)
                        {
                            cp.CompilerOptions += " /target:winexe";
                        }

                        if (!String.IsNullOrEmpty(options.AdditionalCompilerSwitches))
                        {
                            cp.CompilerOptions += " " + options.AdditionalCompilerSwitches;
                        }

                        if (String.IsNullOrEmpty(cp.CompilerOptions))
                        {
                            cp.CompilerOptions = null;
                        }

                        if ((settings.CopyThroughResources != null) && (settings.CopyThroughResources.Count != 0))
                        {
                            if (!Directory.Exists(unpackedResourceDir))
                            {
                                System.IO.Directory.CreateDirectory(unpackedResourceDir);
                            }
                            foreach (string re in settings.CopyThroughResources)
                            {
                                string filename = Path.Combine(unpackedResourceDir, re);

                                ExtractResourceToFile(a2, re, filename);
                                // add the file into the target assembly as an embedded resource
                                cp.EmbeddedResources.Add(filename);
                            }
                        }

                        // add the Ionic.Utils.Zip DLL as an embedded resource
                        cp.EmbeddedResources.Add(a1.Location);

                        // file header
                        sb.Append("// " + Path.GetFileName(sourceFile) + "\n")
                        .Append("// --------------------------------------------\n//\n")
                        .Append("// This SFX source file was generated by DotNetZip ")
                        .Append(ZipFile.LibraryVersion.ToString())
                        .Append("\n//         at ")
                        .Append(System.DateTime.Now.ToString("yyyy MMMM dd  HH:mm:ss"))
                        .Append("\n//\n// --------------------------------------------\n\n\n");

                        // assembly attributes
                        if (!String.IsNullOrEmpty(options.Description))
                        {
                            sb.Append("[assembly: System.Reflection.AssemblyTitle(\""
                                      + options.Description.Replace("\"", "")
                                      + "\")]\n");
                        }
                        else
                        {
                            sb.Append("[assembly: System.Reflection.AssemblyTitle(\"DotNetZip SFX Archive\")]\n");
                        }

                        if (!String.IsNullOrEmpty(options.ProductVersion))
                        {
                            sb.Append("[assembly: System.Reflection.AssemblyInformationalVersion(\""
                                      + options.ProductVersion.Replace("\"", "")
                                      + "\")]\n");
                        }

                        // workitem
                        string copyright =
                            (String.IsNullOrEmpty(options.Copyright))
                            ? "Extractor: Copyright � Dino Chiesa 2008-2011"
                            : options.Copyright.Replace("\"", "");

                        if (!String.IsNullOrEmpty(options.ProductName))
                        {
                            sb.Append("[assembly: System.Reflection.AssemblyProduct(\"")
                            .Append(options.ProductName.Replace("\"", ""))
                            .Append("\")]\n");
                        }
                        else
                        {
                            sb.Append("[assembly: System.Reflection.AssemblyProduct(\"DotNetZip\")]\n");
                        }


                        sb.Append("[assembly: System.Reflection.AssemblyCopyright(\"" + copyright + "\")]\n")
                        .Append(String.Format("[assembly: System.Reflection.AssemblyVersion(\"{0}\")]\n", ZipFile.LibraryVersion.ToString()));
                        if (options.FileVersion != null)
                        {
                            sb.Append(String.Format("[assembly: System.Reflection.AssemblyFileVersion(\"{0}\")]\n",
                                                    options.FileVersion.ToString()));
                        }

                        sb.Append("\n\n\n");

                        // Set the default extract location if it is available
                        string extractLoc = options.DefaultExtractDirectory;
                        if (extractLoc != null)
                        {
                            // remove double-quotes and replace slash with double-slash.
                            // This, because the value is going to be embedded into a
                            // cs file as a quoted string, and it needs to be escaped.
                            extractLoc = extractLoc.Replace("\"", "").Replace("\\", "\\\\");
                        }

                        string postExCmdLine = options.PostExtractCommandLine;
                        if (postExCmdLine != null)
                        {
                            postExCmdLine = postExCmdLine.Replace("\\", "\\\\");
                            postExCmdLine = postExCmdLine.Replace("\"", "\\\"");
                        }


                        foreach (string rc in settings.ResourcesToCompile)
                        {
                            using (Stream s = zip[rc].OpenReader())
                            {
                                if (s == null)
                                {
                                    throw new ZipException(String.Format("missing resource '{0}'", rc));
                                }
                                using (StreamReader sr = new StreamReader(s))
                                {
                                    while (sr.Peek() >= 0)
                                    {
                                        string line = sr.ReadLine();
                                        if (extractLoc != null)
                                        {
                                            line = line.Replace("@@EXTRACTLOCATION", extractLoc);
                                        }

                                        line = line.Replace("@@REMOVE_AFTER_EXECUTE", options.RemoveUnpackedFilesAfterExecute.ToString());
                                        line = line.Replace("@@QUIET", options.Quiet.ToString());
                                        if (!String.IsNullOrEmpty(options.SfxExeWindowTitle))
                                        {
                                            line = line.Replace("@@SFX_EXE_WINDOW_TITLE", options.SfxExeWindowTitle);
                                        }

                                        line = line.Replace("@@EXTRACT_EXISTING_FILE", ((int)options.ExtractExistingFile).ToString());

                                        if (postExCmdLine != null)
                                        {
                                            line = line.Replace("@@POST_UNPACK_CMD_LINE", postExCmdLine);
                                        }

                                        sb.Append(line).Append("\n");
                                    }
                                }
                                sb.Append("\n\n");
                            }
                        }
                    }

                    string LiteralSource = sb.ToString();

#if DEBUGSFX
                    // for debugging only
                    string sourceModule = GenerateTempPathname(tmpDir, "cs");
                    using (StreamWriter sw = File.CreateText(sourceModule))
                    {
                        sw.Write(LiteralSource);
                    }
                    Console.WriteLine("source: {0}", sourceModule);
#endif

                    var cr = csharp.CompileAssemblyFromSource(cp, LiteralSource);


                    if (cr == null)
                    {
                        throw new SfxGenerationException("Cannot compile the extraction logic!");
                    }

                    if (Verbose)
                    {
                        foreach (string output in cr.Output)
                        {
                            StatusMessageTextWriter.WriteLine(output);
                        }
                    }

                    if (cr.Errors.Count != 0)
                    {
                        using (TextWriter tw = new StreamWriter(sourceFile))
                        {
                            // first, the source we compiled
                            tw.Write(LiteralSource);

                            // now, append the compile errors
                            tw.Write("\n\n\n// ------------------------------------------------------------------\n");
                            tw.Write("// Errors during compilation: \n//\n");
                            string p = Path.GetFileName(sourceFile);

                            foreach (System.CodeDom.Compiler.CompilerError error in cr.Errors)
                            {
                                tw.Write(String.Format("//   {0}({1},{2}): {3} {4}: {5}\n//\n",
                                                       p,                                     // 0
                                                       error.Line,                            // 1
                                                       error.Column,                          // 2
                                                       error.IsWarning ? "Warning" : "error", // 3
                                                       error.ErrorNumber,                     // 4
                                                       error.ErrorText));                     // 5
                            }
                        }
                        throw new SfxGenerationException(String.Format("Errors compiling the extraction logic!  {0}", sourceFile));
                    }

                    OnSaveEvent(ZipProgressEventType.Saving_AfterCompileSelfExtractor);

                    // Now, copy the resulting EXE image to the _writestream.
                    // Because this stub exe is being saved first, the effect will be to
                    // concatenate the exe and the zip data together.
                    using (System.IO.Stream input = System.IO.File.OpenRead(stubExe))
                    {
                        byte[] buffer = new byte[4000];
                        int    n      = 1;
                        while (n != 0)
                        {
                            n = input.Read(buffer, 0, buffer.Length);
                            if (n != 0)
                            {
                                WriteStream.Write(buffer, 0, n);
                            }
                        }
                    }
                }

                OnSaveEvent(ZipProgressEventType.Saving_AfterSaveTempArchive);
            }
            finally
            {
                try
                {
                    if (Directory.Exists(unpackedResourceDir))
                    {
                        try { Directory.Delete(unpackedResourceDir, true); }
                        catch (System.IO.IOException exc1)
                        {
                            StatusMessageTextWriter.WriteLine("Warning: Exception: {0}", exc1);
                        }
                    }
                    if (File.Exists(stubExe))
                    {
                        try { File.Delete(stubExe); }
                        catch (System.IO.IOException exc1)
                        {
                            StatusMessageTextWriter.WriteLine("Warning: Exception: {0}", exc1);
                        }
                    }
                }
                catch (System.IO.IOException) { }
            }

            return;
        }
예제 #15
0
        /// <summary>
        ///   Saves the Zip archive to a file, specified by the Name property of the
        ///   <c>ZipFile</c>.
        /// </summary>
        ///
        /// <remarks>
        /// <para>
        ///   The <c>ZipFile</c> instance is written to storage, typically a zip file
        ///   in a filesystem, only when the caller calls <c>Save</c>.  In the typical
        ///   case, the Save operation writes the zip content to a temporary file, and
        ///   then renames the temporary file to the desired name. If necessary, this
        ///   method will delete a pre-existing file before the rename.
        /// </para>
        ///
        /// <para>
        ///   The <see cref="ZipFile.Name"/> property is specified either explicitly,
        ///   or implicitly using one of the parameterized ZipFile constructors.  For
        ///   COM Automation clients, the <c>Name</c> property must be set explicitly,
        ///   because COM Automation clients cannot call parameterized constructors.
        /// </para>
        ///
        /// <para>
        ///   When using a filesystem file for the Zip output, it is possible to call
        ///   <c>Save</c> multiple times on the <c>ZipFile</c> instance. With each
        ///   call the zip content is re-written to the same output file.
        /// </para>
        ///
        /// <para>
        ///   Data for entries that have been added to the <c>ZipFile</c> instance is
        ///   written to the output when the <c>Save</c> method is called. This means
        ///   that the input streams for those entries must be available at the time
        ///   the application calls <c>Save</c>.  If, for example, the application
        ///   adds entries with <c>AddEntry</c> using a dynamically-allocated
        ///   <c>MemoryStream</c>, the memory stream must not have been disposed
        ///   before the call to <c>Save</c>. See the <see
        ///   cref="ZipEntry.InputStream"/> property for more discussion of the
        ///   availability requirements of the input stream for an entry, and an
        ///   approach for providing just-in-time stream lifecycle management.
        /// </para>
        ///
        /// </remarks>
        ///
        /// <seealso cref="Ionic.Zip.ZipFile.AddEntry(String, System.IO.Stream)"/>
        ///
        /// <exception cref="Ionic.Zip.BadStateException">
        ///   Thrown if you haven't specified a location or stream for saving the zip,
        ///   either in the constructor or by setting the Name property, or if you try
        ///   to save a regular zip archive to a filename with a .exe extension.
        /// </exception>
        ///
        /// <exception cref="System.OverflowException">
        ///   Thrown if <see cref="MaxOutputSegmentSize"/> is non-zero, and the number
        ///   of segments that would be generated for the spanned zip file during the
        ///   save operation exceeds 99.  If this happens, you need to increase the
        ///   segment size.
        /// </exception>
        ///
        public void Save()
        {
            try
            {
                bool thisSaveUsedZip64 = false;
                _saveOperationCanceled             = false;
                _numberOfSegmentsForMostRecentSave = 0;
                OnSaveStarted();

                if (WriteStream == null)
                {
                    throw new BadStateException("You haven't specified where to save the zip.");
                }

                if (_name != null && _name.EndsWith(".exe") && !_SavingSfx)
                {
                    throw new BadStateException("You specified an EXE for a plain zip file.");
                }

                // check if modified, before saving.
                if (!_contentsChanged)
                {
                    OnSaveCompleted();
                    if (Verbose)
                    {
                        StatusMessageTextWriter.WriteLine("No save is necessary....");
                    }
                    return;
                }

                Reset(true);

                if (Verbose)
                {
                    StatusMessageTextWriter.WriteLine("saving....");
                }

                // validate the number of entries
                if (_entries.Count >= 0xFFFF && _zip64 == Zip64Option.Never)
                {
                    throw new ZipException("The number of entries is 65535 or greater. Consider setting the UseZip64WhenSaving property on the ZipFile instance.");
                }


                // write an entry in the zip for each file
                int n = 0;
                // workitem 9831
                ICollection <ZipEntry> c = (SortEntriesBeforeSaving) ? EntriesSorted : Entries;
                foreach (ZipEntry e in c) // _entries.Values
                {
                    OnSaveEntry(n, e, true);
                    e.Write(WriteStream);
                    if (_saveOperationCanceled)
                    {
                        break;
                    }

                    n++;
                    OnSaveEntry(n, e, false);
                    if (_saveOperationCanceled)
                    {
                        break;
                    }

                    // Some entries can be skipped during the save.
                    if (e.IncludedInMostRecentSave)
                    {
                        thisSaveUsedZip64 |= e.OutputUsedZip64.Value;
                    }
                }



                if (_saveOperationCanceled)
                {
                    return;
                }

                var zss = WriteStream as ZipSegmentedStream;

                _numberOfSegmentsForMostRecentSave = (zss != null)
                    ? zss.CurrentSegment
                    : 1;

                bool directoryNeededZip64 =
                    ZipOutput.WriteCentralDirectoryStructure
                        (WriteStream,
                        c,
                        _numberOfSegmentsForMostRecentSave,
                        _zip64,
                        Comment,
                        new ZipContainer(this));

                OnSaveEvent(ZipProgressEventType.Saving_AfterSaveTempArchive);

                _hasBeenSaved    = true;
                _contentsChanged = false;

                thisSaveUsedZip64 |= directoryNeededZip64;
                _OutputUsesZip64   = new Nullable <bool>(thisSaveUsedZip64);


                // do the rename as necessary
                if (_name != null &&
                    (_temporaryFileName != null || zss != null))
                {
                    // _temporaryFileName may remain null if we are writing to a stream.
                    // only close the stream if there is a file behind it.
#if NETCF
                    WriteStream.Close();
#else
                    WriteStream.Dispose();
#endif
                    if (_saveOperationCanceled)
                    {
                        return;
                    }

                    if (_fileAlreadyExists && this._readstream != null)
                    {
                        // This means we opened and read a zip file.
                        // If we are now saving to the same file, we need to close the
                        // orig file, first.
                        this._readstream.Close();
                        this._readstream.Dispose();
                        this._readstream = null;
                        // the archiveStream for each entry needs to be null
                        foreach (var e in c)
                        {
                            var zss1 = e._archiveStream as ZipSegmentedStream;
                            if (zss1 != null)
#if NETCF
                            { zss1.Close(); }
#else
                            { zss1.Dispose(); }
#endif
                            e._archiveStream = null;
                        }
                    }

                    if (_fileAlreadyExists && this._writestream != null)
                    {
                        this.WriteStream.Close();
                    }

                    string tmpName = null;
                    if (File.Exists(_name))
                    {
                        // the steps:
                        //
                        // 1. Delete tmpName
                        // 2. move existing zip to tmpName
                        // 3. rename (File.Move) working file to name of existing zip
                        // 4. delete tmpName
                        //
                        // This series of steps avoids the exception,
                        // System.IO.IOException:
                        //   "Cannot create a file when that file already exists."
                        //
                        // Cannot just call File.Replace() here because
                        // there is a possibility that the TEMP volume is different
                        // that the volume for the final file (c:\ vs d:\).
                        // So we need to do a Delete+Move pair.
                        //
                        // But, when doing the delete, Windows allows a process to
                        // delete the file, even though it is held open by, say, a
                        // virus scanner. It gets internally marked as "delete
                        // pending". The file does not actually get removed from the
                        // file system, it is still there after the File.Delete
                        // call.
                        //
                        // Therefore, we need to move the existing zip, which may be
                        // held open, to some other name. Then rename our working
                        // file to the desired name, then delete (possibly delete
                        // pending) the "other name".
                        //
                        // Ideally this would be transactional. It's possible that the
                        // delete succeeds and the move fails. Lacking transactions, if
                        // this kind of failure happens, we're hosed, and this logic will
                        // throw on the next File.Move().
                        //
                        //File.Delete(_name);
                        // workitem 10447
#if NETCF || SILVERLIGHT
                        tmpName = _name + "." + SharedUtilities.GenerateRandomStringImpl(8, 0) + ".tmp";
#else
                        tmpName = _name + "." + Path.GetRandomFileName();
#endif
                        if (File.Exists(tmpName))
                        {
                            DeleteFileWithRetry(tmpName);
                        }

                        try {
                            File.Move(_name, tmpName);
                        }
                        catch {
                            if (System.Diagnostics.Debugger.IsAttached)
                            {
                                System.Diagnostics.Debugger.Break(); //Look at the comment below. Added this to force a break here instead of your catch so you don't miss the cause and waste your time like I did
                            }
                            throw;
                        }

                        //Oh boy. You've just hit an exception on that File.Move line right above this stating that the template file can't be renamed because it's open
                        //in another process, right?

                        //Congratulation! You've probably run into the same gibberish I ran into!

                        //Check to make sure your file streams are closed, and ITpipes/Template Editor/Winrar/7-Zip don't have the template file open. I'm betting you won't find anything.

                        //If you try closing Visual Studio and running the binary directly I'm betting you won't have this crash occur. Go ahead--try it.

                        //What the hell, right?

                        //The XAML designer process (XDesProc.exe) is configured, by default, to populate its ViewModel in the designer UI. This creates a copy of the model--which creates a second
                        //Ionic.Zip.ZipFile object using the same template file. So when *this* ZipFile object attempts to move the existing template to replace it with an updated file
                        //the exception is thrown.

                        //To fix this, disable the following option in the Visual Studio toolbar above:
                        // Tools -> Options -> XAML Designer -> "Run project code in XAML Designer (if supported)"

                        //Once that's disabled, restart Visual Studio. Problem solved.

                        //Hopefully these comments will prevent someone else from wasting their time obsessing over their streams and AntiVirus. - Vincent Nary
                    }

                    OnSaveEvent(ZipProgressEventType.Saving_BeforeRenameTempArchive);
                    File.Move((zss != null) ? zss.CurrentTempName : _temporaryFileName,
                              _name);

                    OnSaveEvent(ZipProgressEventType.Saving_AfterRenameTempArchive);

                    if (tmpName != null)
                    {
                        try
                        {
                            // not critical
                            if (File.Exists(tmpName))
                            {
                                File.Delete(tmpName);
                            }
                        }
                        catch
                        {
                            // don't care about exceptions here.
                        }
                    }
                    _fileAlreadyExists = true;
                }

                NotifyEntriesSaveComplete(c);
                OnSaveCompleted();
                _JustSaved = true;
            }

            // workitem 5043
            finally
            {
                CleanupAfterSaveOperation();
            }

            return;
        }
예제 #16
0
        /// <summary>
        ///   Saves the Zip archive to a file, specified by the Name property of the
        ///   <c>ZipFile</c>.
        /// </summary>
        ///
        /// <remarks>
        /// <para>
        ///   The <c>ZipFile</c> instance is written to storage, typically a zip file
        ///   in a filesystem, only when the caller calls <c>Save</c>.  In the typical
        ///   case, the Save operation writes the zip content to a temporary file, and
        ///   then renames the temporary file to the desired name. If necessary, this
        ///   method will delete a pre-existing file before the rename.
        /// </para>
        ///
        /// <para>
        ///   The <see cref="ZipFile.Name"/> property is specified either explicitly,
        ///   or implicitly using one of the parameterized ZipFile constructors.  For
        ///   COM Automation clients, the <c>Name</c> property must be set explicitly,
        ///   because COM Automation clients cannot call parameterized constructors.
        /// </para>
        ///
        /// <para>
        ///   When using a filesystem file for the Zip output, it is possible to call
        ///   <c>Save</c> multiple times on the <c>ZipFile</c> instance. With each
        ///   call the zip content is re-written to the same output file.
        /// </para>
        ///
        /// <para>
        ///   Data for entries that have been added to the <c>ZipFile</c> instance is
        ///   written to the output when the <c>Save</c> method is called. This means
        ///   that the input streams for those entries must be available at the time
        ///   the application calls <c>Save</c>.  If, for example, the application
        ///   adds entries with <c>AddEntry</c> using a dynamically-allocated
        ///   <c>MemoryStream</c>, the memory stream must not have been disposed
        ///   before the call to <c>Save</c>. See the <see
        ///   cref="ZipEntry.InputStream"/> property for more discussion of the
        ///   availability requirements of the input stream for an entry, and an
        ///   approach for providing just-in-time stream lifecycle management.
        /// </para>
        ///
        /// </remarks>
        ///
        /// <seealso cref="Ionic.Zip.ZipFile.AddEntry(String, System.IO.Stream)"/>
        ///
        /// <exception cref="Ionic.Zip.BadStateException">
        ///   Thrown if you haven't specified a location or stream for saving the zip,
        ///   either in the constructor or by setting the Name property, or if you try
        ///   to save a regular zip archive to a filename with a .exe extension.
        /// </exception>
        ///
        /// <exception cref="System.OverflowException">
        ///   Thrown if <see cref="MaxOutputSegmentSize"/> is non-zero, and the number
        ///   of segments that would be generated for the spanned zip file during the
        ///   save operation exceeds 99.  If this happens, you need to increase the
        ///   segment size.
        /// </exception>
        ///
        public void Save()
        {
            try
            {
                bool thisSaveUsedZip64 = false;
                _saveOperationCanceled             = false;
                _numberOfSegmentsForMostRecentSave = 0;
                OnSaveStarted();

                if (WriteStream == null)
                {
                    throw new BadStateException("You haven't specified where to save the zip.");
                }

                if (_name != null && _name.EndsWith(".exe") && !_SavingSfx)
                {
                    throw new BadStateException("You specified an EXE for a plain zip file.");
                }

                // check if modified, before saving.
                if (!_contentsChanged)
                {
                    OnSaveCompleted();
                    if (Verbose)
                    {
                        StatusMessageTextWriter.WriteLine("No save is necessary....");
                    }
                    return;
                }

                Reset(true);

                if (Verbose)
                {
                    StatusMessageTextWriter.WriteLine("saving....");
                }

                // validate the number of entries
                if (_entries.Count >= 0xFFFF && _zip64 == Zip64Option.Never)
                {
                    throw new ZipException("The number of entries is 65535 or greater. Consider setting the UseZip64WhenSaving property on the ZipFile instance.");
                }


                // write an entry in the zip for each file
                int n = 0;
                // workitem 9831
                ICollection <ZipEntry> c = (SortEntriesBeforeSaving) ? EntriesSorted : Entries;
                foreach (ZipEntry e in c) // _entries.Values
                {
                    OnSaveEntry(n, e, true);
                    e.Write(WriteStream);
                    if (_saveOperationCanceled)
                    {
                        break;
                    }

                    n++;
                    OnSaveEntry(n, e, false);
                    if (_saveOperationCanceled)
                    {
                        break;
                    }

                    // Some entries can be skipped during the save.
                    if (e.IncludedInMostRecentSave)
                    {
                        thisSaveUsedZip64 |= e.OutputUsedZip64.Value;
                    }
                }



                if (_saveOperationCanceled)
                {
                    return;
                }

                var zss = WriteStream as ZipSegmentedStream;

                _numberOfSegmentsForMostRecentSave = (zss != null)
                    ? zss.CurrentSegment
                    : 1;

                bool directoryNeededZip64 =
                    ZipOutput.WriteCentralDirectoryStructure
                        (WriteStream,
                        c,
                        _numberOfSegmentsForMostRecentSave,
                        _zip64,
                        Comment,
                        new ZipContainer(this));

                OnSaveEvent(ZipProgressEventType.Saving_AfterSaveTempArchive);

                _hasBeenSaved    = true;
                _contentsChanged = false;

                thisSaveUsedZip64 |= directoryNeededZip64;
                _OutputUsesZip64   = new Nullable <bool>(thisSaveUsedZip64);

                if (_fileAlreadyExists && this._readstream != null)
                {
                    // This means we opened and read a zip file.
                    // If we are now saving, we need to close the orig file, first.
                    this._readstream.Close();
                    this._readstream = null;
                }
                // the archiveStream for each entry needs to be null
                foreach (var e in c)
                {
                    var zss1 = e._archiveStream as ZipSegmentedStream;
                    if (zss1 != null)
#if NETCF
                    { zss1.Close(); }
#else
                    { zss1.Dispose(); }
#endif
                    e._archiveStream = null;
                }

                // do the rename as necessary
                if (_name != null &&
                    (_temporaryFileName != null || zss != null))
                {
                    // _temporaryFileName may remain null if we are writing to a stream.
                    // only close the stream if there is a file behind it.
#if NETCF
                    WriteStream.Close();
#else
                    WriteStream.Dispose();
#endif

                    if (_saveOperationCanceled)
                    {
                        return;
                    }

                    string tmpName = null;
                    if (File.Exists(_name))
                    {
                        // the steps:
                        //
                        // 1. Delete tmpName
                        // 2. move existing zip to tmpName
                        // 3. rename (File.Move) working file to name of existing zip
                        // 4. delete tmpName
                        //
                        // This series of steps avoids the exception,
                        // System.IO.IOException:
                        //   "Cannot create a file when that file already exists."
                        //
                        // Cannot just call File.Replace() here because
                        // there is a possibility that the TEMP volume is different
                        // that the volume for the final file (c:\ vs d:\).
                        // So we need to do a Delete+Move pair.
                        //
                        // But, when doing the delete, Windows allows a process to
                        // delete the file, even though it is held open by, say, a
                        // virus scanner. It gets internally marked as "delete
                        // pending". The file does not actually get removed from the
                        // file system, it is still there after the File.Delete
                        // call.
                        //
                        // Therefore, we need to move the existing zip, which may be
                        // held open, to some other name. Then rename our working
                        // file to the desired name, then delete (possibly delete
                        // pending) the "other name".
                        //
                        // Ideally this would be transactional. It's possible that the
                        // delete succeeds and the move fails. Lacking transactions, if
                        // this kind of failure happens, we're hosed, and this logic will
                        // throw on the next File.Move().
                        //
                        //File.Delete(_name);
                        // workitem 10447
#if NETCF || SILVERLIGHT
                        tmpName = _name + "." + SharedUtilities.GenerateRandomStringImpl(8, 0) + ".tmp";
#else
                        tmpName = _name + "." + Path.GetRandomFileName();
#endif
                        if (File.Exists(tmpName))
                        {
                            DeleteFileWithRetry(tmpName);
                        }
                        File.Move(_name, tmpName);
                    }

                    OnSaveEvent(ZipProgressEventType.Saving_BeforeRenameTempArchive);
                    File.Move((zss != null) ? zss.CurrentTempName : _temporaryFileName,
                              _name);

                    OnSaveEvent(ZipProgressEventType.Saving_AfterRenameTempArchive);

                    if (tmpName != null)
                    {
                        try
                        {
                            // not critical
                            if (File.Exists(tmpName))
                            {
                                File.Delete(tmpName);
                            }
                        }
                        catch
                        {
                            // don't care about exceptions here.
                        }
                    }
                    _fileAlreadyExists = true;
                }
                _readName = _name;

                NotifyEntriesSaveComplete(c);
                OnSaveCompleted();
                _JustSaved = true;
            }

            // workitem 5043
            finally
            {
                CleanupAfterSaveOperation();
            }

            return;
        }
예제 #17
0
        private void _AddOrUpdateSelectedFiles(String selectionCriteria,
                                               String directoryOnDisk,
                                               String directoryPathInArchive,
                                               bool recurseDirectories,
                                               bool wantUpdate)
        {
            if (directoryOnDisk == null && (Directory.Exists(selectionCriteria)))
            {
                directoryOnDisk   = selectionCriteria;
                selectionCriteria = "*.*";
            }
            else if (String.IsNullOrEmpty(directoryOnDisk))
            {
                directoryOnDisk = ".";
            }

            // workitem 9176
            while (directoryOnDisk.EndsWith("\\"))
            {
                directoryOnDisk = directoryOnDisk.Substring(0, directoryOnDisk.Length - 1);
            }
            if (Verbose)
            {
                StatusMessageTextWriter.WriteLine("adding selection '{0}' from dir '{1}'...",
                                                  selectionCriteria, directoryOnDisk);
            }
            Ionic.FileSelector ff = new Ionic.FileSelector(selectionCriteria,
                                                           AddDirectoryWillTraverseReparsePoints);
            var itemsToAdd = ff.SelectFiles(directoryOnDisk, recurseDirectories);

            if (Verbose)
            {
                StatusMessageTextWriter.WriteLine("found {0} files...", itemsToAdd.Count);
            }

            OnAddStarted();

            AddOrUpdateAction action = (wantUpdate) ? AddOrUpdateAction.AddOrUpdate : AddOrUpdateAction.AddOnly;

            foreach (var item in itemsToAdd)
            {
                // workitem 10153
                string dirInArchive = (directoryPathInArchive == null)
                    ? null
                                      // workitem 12260
                    : ReplaceLeadingDirectory(Path.GetDirectoryName(item),
                                              directoryOnDisk,
                                              directoryPathInArchive);

                if (File.Exists(item))
                {
                    if (wantUpdate)
                    {
                        this.UpdateFile(item, dirInArchive);
                    }
                    else
                    {
                        this.AddFile(item, dirInArchive);
                    }
                }
                else
                {
                    // this adds "just" the directory, without recursing to the contained files
                    AddOrUpdateDirectoryImpl(item, dirInArchive, action, false, 0);
                }
            }

            OnAddCompleted();
        }