NullReferenceException after 2nd Save

Apr 6, 2010 at 9:52 PM
Edited Apr 6, 2010 at 9:58 PM

Dino,

I'm experiencing an issue when performing two independent operations. The snippet at below contains two blocks, each including an invocation of the Save method. Both are totally unrelated:

  • The first one adds a directory "XX" to the ZIP file
  • The second one updates paths of all entries within the "Download" folder, including the folder itself.

Now, if I comment out either the first part or the second, the code runs through without any errors and the ZIP file is properly updated. However, the snippet as it is below, fails with a NullReferenceException on the second save invocation:

System.NullReferenceException : Object reference not set to an instance of an object.
    at Ionic.Zip.SharedUtilities.ReadWithRetry(Stream s, Byte[] buffer, Int32 offset, Int32 count, String FileName)
    at Ionic.Zip.ZipEntry._WriteEntryData(Stream s)
    at Ionic.Zip.ZipEntry.Write(Stream s)
    at Ionic.Zip.ZipFile.Save()

Now, if I switch part 1 and 2 (first do the renaming and save, then add the directory and save), this again does not cause an exception. And the code also works if I only save once at the end of both blocks (add directory, rename, save). Any ideas what's happening here?

Thanks for your advice

Philipp

 

 

    [Test]
    public void DummyTest()
    {
      var zip = Provider.NodeRepository.ZipFile;
      var entries = zip.Entries.Where(e => e.FileName.Contains("Download")).ToArray();

      //PART1 - Add directory and save
      zip.AddDirectoryByName("XX");
      zip.Save();
      
      //PART2 - Rename paths (not related to XX directory from above) and save
      foreach (var zipEntry in entries)
      {
        zipEntry.FileName = zipEntry.FileName.Replace("Download", "Download2");
      }
      zip.Save();
 }

 

Apr 7, 2010 at 11:22 AM
Edited Apr 7, 2010 at 11:27 AM

I did some more debugging, and it might have to do that the _Source property is None (sorry, mistakenly wrote null first) on the second save, which prevents the entry to be saved without restreaming:

 

if (_Source == ZipEntrySource.ZipFile && !_restreamRequiredOnSave)
{
  CopyThroughOneEntry(s);
  return;
}

 

If I only save once, the _Source property of the renamed file entry is set, and the the CopyThroughOneEntry method is invoked accordingly.


 

Apr 15, 2010 at 7:44 PM

First of all sorry for bumping the thread, but I'm a bit lost on this one, as it is a severe show stopper (recreating the ZipFile after every safe is not an option). For now, I got my code working by maintaining a custom build of your code, where I modified the ZipEntry class like this:

 

if ( (_Source == ZipEntrySource.ZipFile || source == ZipEntrySource.None) && !_restreamRequiredOnSave)
{
CopyThroughOneEntry(s);
return;
}


...but I don't have any ideas about potential side effects (and also don't know whether a simple fix will be possible in the mid future). So any advice on how to handle this is highly appreaciated.

Cheers,

Philipp

Apr 19, 2010 at 12:05 PM
Edited Apr 19, 2010 at 12:08 PM

Another side effect when trying to open an item once it has been created in the InternalOpenReader method, which caused an exception because once again, the source was None:

if (this._Source != ZipEntrySource.ZipFile)
throw new BadStateException("You must call ZipFile.Save before calling OpenReader.");

Given the the Save itself caused the issue (setting the _Source to _None), the error message itself is problematic, too. I assume it's this method that causes the issue:

        internal void NotifySaveComplete()
        {
            _Encryption_FromZipFile = _Encryption;
            _CompressionMethod_FromZipFile = _CompressionMethod;
            _restreamRequiredOnSave = false;
            _metadataChanged = false;
            _Source = ZipEntrySource.None;
        }