Cannot read file < xyz> at offset

Jun 25, 2009 at 6:32 PM

Cheeso,

I'm re-running this update to the zip archive to see if it happens again, but a penny for your thoughts...?

Why couldn't it read archive.pst? File locked?

6/25/2009 1:19:01 PM

Cannot read file E-Mail/archive.pst, at offset 0x00000000 after 10 retries

   at Ionic.Zip.SharedUtilities.ReadWithRetry(Stream s, Byte[] buffer, Int32 offset, Int32 count, String FileName)

   at Ionic.Zip.ZipEntry._WriteFileData(Stream s)

   at Ionic.Zip.ZipEntry.Write(Stream s)

   at Ionic.Zip.ZipFile.Save()

   at SMG_Backup.MainForm.ZipUpdate(String OriginalFile)

Coordinator
Jun 25, 2009 at 7:00 PM

Ahhhh, yes.  A portion of the file is locked (via a byte range lock).  In this case I 'd assume it's locked by Outlook. 

Darn it, that error message should have said as much. 

The approach I took in addressing the original error (ERROR_LOCK_VIOLATION) was to retry the Read, repeatedly, with a backoff delay interval.  This will work if the application that holds the lock releases it, within a short period (let's say, one or two seconds).  But it will not succeed as a strategy for apps that hold range locks indefinitely, as apparently Outlook does.

I don't see a simple solution to this problem.  I could:

  • wait longer, say 30 seconds.
  • NOT allow DotNetZip to open those files in the first place.  This was a feature people had asked for - backing up open files.
  • allow the application some input into the decision, or some way to handle the exception within the call to Save().

I like the last option the best, but it is also the most complex.

What do you think?

 

Jun 25, 2009 at 7:11 PM

I'll briefly explain the scenario then offer up some ideas. 

In this particular case the update to the zip archive is scheduled to come on at 12p while the user is at lunch. He only had Outlook open, which he usually does, but today Outlook locked a hold of the pst for some reason. That said, I think it is an isolated incident. I'm running the update again, asked him not to touch anything and we'll see what transpires.

Without me thinking this through, you could wait longer - say 5-10 seconds, then if still locked have dotnetzip forcefully close the application or process that holds the lock, then wait 5-10 more seconds and retry. 

Now that probably threw you into code hell didn't it?

Jun 25, 2009 at 7:22 PM

Another option would be to wait 5-10 seconds and try again - repeat that about 3 times, then if still locked, skip the file but then have a way to list the files that were skipped. That way I could at least code the app to say, 'save successfull but the following files were skipped: blah blah blah' - or something to that end.

Jun 25, 2009 at 7:35 PM

This may or may not help you. The update was successfully updating the E-Mail\archive.pst because I was watching it progress in the progress bar. As soon as I left for a moment, of course, "do not touch" was forgotten, the network connection was disrupted and his backup failed since it backs up to his network directory. Don't get me started - ok moving on...

So the second update is tentatively successful. That leads me to Outlook behavior. Since all of these users have Outlook open 99% of the time, I think Outlook doesn't "lock" a pst file unless it is being used. "Used" is defined as a user transferring a file to the pst, reading an email from the pst, or if an email is highlighted\showing in the preview pane. Now that I'm typing this and re-reading, Outlooks behavior is probably irrelevant. A locked file is a locked file - it doesn't matter from what or how.

 

Coordinator
Jun 25, 2009 at 7:43 PM
Edited Jun 25, 2009 at 7:46 PM

Ok, the real scenario is very interesting.

Maybe DotNetZip just needs to wait longer.  I don't know enough about Outlook to know when or why it grabs a range lock, and how long it typically holds them.  I will research it, but of course that will give me an answer specific to Outlook, and there's no guarantee that other apps will behave similarly.  If apps typically hold rangelocks for 5 seconds, and I try to open the file 5 times in 2 seconds, then my "backoff" interval doesn't really work, does it?   But if apps typically hold range locks for 10 hours, then no reasonable wait-and-retry approach will succeed.

The approach taken today in DotNetZip is, when the range lock is experienced, delay 270 milliseconds, and then retry.  If it fails again, I add 20 milliseconds to the delay interval, wait again, and retry again, up to 10 times.  On the tenth retry, it will have waited 450 milliseconds since the failure.  The total aggregate time waited during the retry dance will be 3.6 seconds.

I could have DotNetZip wait longer.  10 seconds, even 30 seconds doesn't seem outrageous I guess.  But until we try it (or until I research outlook) we won't know if this backoff approach will be fruitful. 

I don't like the idea of forcibly closing any application.  There's nothing in the Windows API that allows me to nicely ask an app to shutdown, though I could kill an app.  that is inherently dangerous, though, and I won't change DotNetZip to do something like that.  Suppose outlook is in the middle of compacting a PST file (Which it does from time to time) .  Closing it unilaterally while that is happening will lead to data errors.  Bad stuff.

In my experience, Outlook can take 10-15 minutes doing housekeeping on very large PST files.

Having said all of this, there is one additional twist.  With a range lock, the app is telling other apps that no-one can read the range the app has locked. Now suppose I enlarge the backoff interval, to 30 seconds.  It is possible that the ZipFile.Save() proceeds after a delay, during which the locking app relinquishes the range lock.  But the data within that range will have changed during that 30 seconds (or I should say, "up to 30 seconds") delay.  The question is now, is the data previously read from that file still valid?  In other words, if we continue reading the file after the range lock is relinquished, that means that we've got a fuzzy image of the file data.  Each part of the data read by DotNetZip was valid at some point , but it is not true that the data read from the file corresponds to a version of the file that ever actually existed, if you know what I mean.

like this:  suppose there is a file with 10 records.  DotNetZip reads 5 records successfully, then hits a range lock, and waits.  The application updates records 6 and 7, and then takes another range lock, on records 3 and 4, updates them, and then relinquishes the locks.  DotNetZip continues reading the updated records 6 and 7, as well as 8-10.   Now DotNetZip has a picture of the file that never existed at any particular point in time.  It has records 1,2,3,4,5,6a,7a,8,9,10, while the file at one point may have had 1,2,3,4,5,6,7,8,9,10, and then at a later point had 1,2,3a,4a,5,6a,7a,8.9,10.  You see?

We can hope that Outlook locks only a contiguous range, and that the updates are confined to that range, and that any updates made in that range are independent of other ranges in the file.  IF all those things are true then a simple read-and-wait-and-retry approach will work for backup, for files that are open and potentially rangelocked.  But if NOT, then, a backup may get an inconsistent copy of the file. 

By the way, this problem is not limited to files that have range locks on them.  Any file that is Open at the time of backup may be exposed to the same risk, but silently.  See, it's possible for an app to update a file without taking a range lock on it.   The range lock is a sort of courtesy that some apps use.   When DotNetZip opens files, it says "it's ok to open them even if some other app has it open for writing."  If the other app has the file open for writing and is actively writing, then we could have the same kind of situation where we don't get an image of the file that ever existed at any particular time.   

I'm sorry I am not very familiar with the use of Range Locks in the Windows API - how they are done or what the recommended and followed practice is.  It may even be true that it is not possible to take a range lock on two distinct, non-overlapping ranges in a single file.  If that is the case the conscientious use of  Range Locks would avoid the problem I described.  But apps that did not use range locks could still be exposed to the problem.

so, it's pretty involved. 

I think the best approach is

  • make the backoff longer, and
  • think about how to allow the app (in your case, your backup app) to handle the "cannot read" problem.  For example your app could pop a message box that says "warning: cannot read file Foo.pst, it's locked!"  And then based on user input, the app could tell DotNetZip to Skip or Retry that file.

 

 

Coordinator
Jun 25, 2009 at 7:54 PM

I think it should be not too difficult to add an Error event to the ZipFile class to allow the app to intervene and take some action when an error occurs. 

The Logic would work something like this:

     If (an error occurs during read)
         If (no Event handler is registered) Then 
             take the default action.
             -- the default action is either Skip, or Throw Exception
         Else 
           Invoke the Error eventhandler
           check the response from the app:
           if (response = Retry) then retry
           if (response = Skip) then do so.
           otherwise throw an exception
         End If 
     End If
Coordinator
Jun 25, 2009 at 7:56 PM
This discussion has been copied to a work item. Click here to go to the work item and continue the discussion.
Jun 25, 2009 at 7:58 PM

Interesting. 

Would it not just be easier to retry the locked file, if unsuccessful after <condition\variable X>, then skip the file? That actually works best in my case. I mean realistically, users are backing up 100's, most likely thousands of files. I would much rather have some sort of log file or message that tells them the archive was created and saved (or updated and saved) but the following files were skipped - x,y,z, etc.

Just got your last 2 emails - great. If it can be coded to skip the file and provide a way for me to log or post what was skipped - problem solved.

Coordinator
Jun 25, 2009 at 8:16 PM

Yep, that's what I'll do.  This will take a little while to design, build, and test.

Coordinator
Aug 25, 2009 at 7:50 PM

Hey Jeff, sorry it took so long.

I think I have something reasonable for you to try.

I've posted a new update of DotNetZip, v1.8.4.22 .

There is now a new property on the ZipFile class, called ZipErrorAction.  Use it to specify what you want to do, in the event the DotNetZip library encounters an IO error (such as the one you saw, "The process cannot access the file 'C:\Documents and Settings\...\JK06.DBF' because it is being used by another process.").  You can specify:

  • Throw (this is the default, and this is what the library did previously)
  • Skip the file that incurred the error
  • Retry the file - try to zip it again
  • Invoke an Error Event handler, that you provide

You can use the evevnt handler to prompt the user, for example, to ask what they want to do.   You then set ZipErrorAction to one of the other 3 options (Skip,. Throw, Retry) and execution continues.

The new event handler is ZipFile.ZipError. 

At the very least thie new property and new Event handler should provide a simple way for you to skip files that cannot be opened or read.

I'd really like you to test this.  Let me know what you think.

Sep 1, 2009 at 2:15 PM

Hey Cheeso, I'm just now starting to work on this so give me a few days to get you some results...

Thanks!

Sep 1, 2009 at 2:30 PM

There is an obsolete method I'm using in the zipextract.extract line, but I can't seem to figure out the correct syntax.

 

zipExtract = Zip.ZipFile.Read(SourceFile)
                For Each checkedEntry As System.String In CheckedListBox1.CheckedItems
                    Dim checkedFile As String = CheckedListBox1.GetItemText(checkedEntry)
                    zipExtract.Extract(checkedFile, DestFolder, Zip.ExtractExistingFileAction.OverwriteSilently)
                Next
                zipExtract.Dispose()

Your wisdom please..

Brief history- It's a partial restore where a user can view all the files in the zip archive, check each one they want restored\extracted, then extract.

 

Sep 1, 2009 at 4:02 PM

Ok, after trying a couple things I'm lost on the usage. Here is my current zip save:

 

Try
            zipArchive.AddDirectory(SourceFolder)
            zipArchive.ZipErrorAction = Zip.ZipErrorAction.InvokeErrorEvent
            zipArchive.CompressionLevel = Zlib.CompressionLevel.BestCompression
            zipArchive.UseZip64WhenSaving = Ionic.Zip.Zip64Option.Always
            zipArchive.TempFileFolder = txtFolderSave.Text
            zipArchive.Save(DestFile)
            zipArchive.Dispose()
        Catch ex As Exception
            MsgBox("The attempt to create a backup file failed with the following error: " & Chr(13) & ex.Message & Chr(13) & "Please contact the helpdesk for assistance." & Chr(13) & Chr(13) & ex.StackTrace, MsgBoxStyle.Critical, "Backup File Creation Failed")
            FileIO.FileSystem.WriteAllText("C:\SMGBackupErrorLog.txt", vbCrLf & vbCrLf & Now() & vbCrLf & ex.Message & vbCrLf & ex.StackTrace & vbCrLf, True)
            zipArchive.Dispose()
            pbarTotal.Value = 0
            pbarCurrent.Value = 0
            lblCurrentFile.Text = String.Empty
            lblProcess.Text = String.Empty
            btnZip.Enabled = True
            Return
        End Try

My test event handler (I think):

 

    Private Sub zipArchive_ZipError(ByVal sender As Object, ByVal e As Ionic.Zip.ZipErrorEventArgs) Handles zipArchive.ZipError

        MsgBox("Zip Error Recorded-zipArchive")
        zipArchive.ZipErrorAction = Zip.ZipErrorAction.Skip

    End Sub

I'm missing something somewhere as it behaves normally as if ziperroraction was never there..

 

 

Coordinator
Sep 1, 2009 at 4:43 PM

> it behaves normally as if ziperroraction was never there..

Yeah, you need to add the event handler, in addition to setting the ZipErrorAction property.  You need this:

AddHandler zip.ZipError, AddressOf zipError_ZipError

Did you figure out the zip.Extract thing?   call the Extract method on the ZipEntry, not on the ZipFile.

 

Sep 1, 2009 at 6:35 PM

Excuse my rookieness (new word), although I'm looking at examples on the web, this AddHandler thing is just not clicking. Usually, I can play around, test code, and based on results, figure out what to try next until it makes sense.

Not so now since nothing happens. I set the ziperroraction property to invoke error event, then I add the handler with addressof (which I assume tells where the section of code is to use on error).

 

 

        Try
            zipArchive.AddDirectory(SourceFolder)
            zipArchive.CompressionLevel = Zlib.CompressionLevel.BestCompression
            zipArchive.UseZip64WhenSaving = Ionic.Zip.Zip64Option.Always
            zipArchive.TempFileFolder = txtFolderSave.Text
            zipArchive.ZipErrorAction = Zip.ZipErrorAction.InvokeErrorEvent
            AddHandler zipArchive.ZipError, AddressOf zipArchive_ZipError
            zipArchive.Save(DestFile)
            zipArchive.Dispose()
        Catch ex As Exception

 

So in my code for the zip error event, I basically say msgbox and skip (for now), yet all I get is could not find file error (which is currently supposed to happen on error).

 

 

    Private Sub zipArchive_ZipError(ByVal sender As Object, ByVal e As Ionic.Zip.ZipErrorEventArgs) Handles zipArchive.ZipError

        MsgBox("Zip Error Recorded-zipArchive")
        zipArchive.ZipErrorAction = Zip.ZipErrorAction.Skip

    End Sub

 

And I have not messed with the extract yet.

 

 

Coordinator
Sep 1, 2009 at 6:48 PM

> all I get is could not find file error

What does this mean - your error handler is not invoked?   Under what conditions does this happen?  What is the file that is not found?   Is there an exception?  Can you show me the exception?

 

Sep 1, 2009 at 7:05 PM

Here is the current code:

 

        Try
            zipArchive.AddDirectory(SourceFolder)
            zipArchive.CompressionLevel = Zlib.CompressionLevel.BestCompression
            zipArchive.UseZip64WhenSaving = Ionic.Zip.Zip64Option.Always
            zipArchive.TempFileFolder = txtFolderSave.Text
            zipArchive.ZipErrorAction = Zip.ZipErrorAction.InvokeErrorEvent
            AddHandler zipArchive.ZipError, AddressOf zipArchive_ZipError
            zipArchive.Save(DestFile)
            zipArchive.Dispose()
        Catch ex As Exception
            MsgBox("The attempt to create a backup file failed with the following error: " & Chr(13) & ex.Message & Chr(13) & "Please contact the helpdesk for assistance." & Chr(13) & Chr(13) & ex.StackTrace, MsgBoxStyle.Critical, "Backup File Creation Failed")
            FileIO.FileSystem.WriteAllText("C:\SMGBackupErrorLog.txt", vbCrLf & vbCrLf & Now() & vbCrLf & ex.Message & vbCrLf & ex.StackTrace & vbCrLf, True)
            zipArchive.Dispose()
            pbarTotal.Value = 0
            pbarCurrent.Value = 0
            lblCurrentFile.Text = String.Empty
            lblProcess.Text = String.Empty
            btnZip.Enabled = True
            Return
        End Try

So how I test is, I have a file in the directory to be archived. I start the archive process and once the cataloging of the directory (by dotnetzip) is completed, I delete a file it thinks should be there. When it gets to the file, I get the msgbox as expected within the Catch portion of the code.

What I was expecting to happen was the ziperroraction to  (do something) even if I had the code wrong. So when I say nothing happens, the program works like it always has, throwing an error on because it can't find the file I deleted.

Notice in the "Catch ex.." code I write to an error log. The error log shows this (which is normal behavior): (I have an "Acronis.iso" image file that takes a minute or so to archive, so when it archiving this file, I know I can delete the ACRZ.txt file (which is next) to test the ziperroraction).

 

9/1/2009 1:16:10 PM
Could not find file 'C:\Users\jeffy.SELLETHICS\Documents\ACRZ.txt'.
   at System.IO.__Error.WinIOError(Int32 errorCode, String maybeFullPath)
   at System.IO.FileInfo.get_Length()
   at Ionic.Zip.ZipEntry.FigureCompressionMethodForWriting(Int32 cycle)
   at Ionic.Zip.ZipEntry.WriteHeader(Stream s, Int32 cycle)
   at Ionic.Zip.ZipEntry.Write(Stream s)
   at Ionic.Zip.ZipFile.Save()
   at Ionic.Zip.ZipFile.Save(String fileName)
   at SMG_Backup.MainForm.ZipSave(String SourceFolder, String DestFile) in C:\Users\jeffy.SELLETHICS\Documents\Visual Studio 2005\Projects\SMG Backup\SMG Backup\MainForm.vb:line 121

 

Am I doing something wrong in the code, or should I see something that isn't working?

 

 

 

 

Coordinator
Sep 1, 2009 at 9:48 PM
Edited Sep 1, 2009 at 10:02 PM

Hmm, ok , I guess I need a better example in the code.  Looking at the doc right now I can see it's pretty lame.

I ran your example and got the same result as you. Then I looked at your code and realized that you don't set ZipErrorAction until after you call AddDirectory.   ZipErrorAction is one of those properties in DotNetZip, like Encryption or Password,  that applies to both the ZipFile and the ZipEntry.  If you set it on the ZipFile, it gets copied to all ZipEntry instances subsequently added to the ZipFile

Since you set it after you call AddDirectory, it applies to none of the entries.

A second problem in your code is in your error handler.  The exception you get there applies to a particular entry.  You have to set the ZipErrorAction on that entry.  This is available via ZipErrorEventArgs.CurrentEntry. 

I modified your code and this works for me: 

Private Sub zipArchive_ZipError(ByVal sender As Object, ByVal e As Ionic.Zip.ZipErrorEventArgs) 
    ' Could now ask the user for what he or she wants to do. 
    Console.WriteLine("Zip Error Recorded-zipArchive with entry {0}", e.CurrentEntry.FileName)
    ' Set the desired action on the CurrentEntry.
    e.CurrentEntry.ZipErrorAction = Zip.ZipErrorAction.Skip
End Sub


Public Sub Run()

    Dim SourceFolder As String = "fodder"
    Dim DestFile As String =  "eHandler.zip"

    Console.WriteLine("Zipping up {0} into {1}", SourceFolder, DestFile)
    If File.Exists(DestFile) Then
        Console.WriteLine("Removing file {0}", DestFile)
        File.Delete(DestFile)
    End If

    Using zipArchive As ZipFile = New ZipFile
        Try
            zipArchive.ZipErrorAction = Zip.ZipErrorAction.InvokeErrorEvent
            AddHandler zipArchive.ZipError, AddressOf zipArchive_ZipError
            zipArchive.AddDirectory(SourceFolder)
            zipArchive.CompressionLevel = Zlib.CompressionLevel.BestCompression
            zipArchive.UseZip64WhenSaving = Ionic.Zip.Zip64Option.Always
            ''zipArchive.TempFileFolder = txtFolderSave.Text
            ''this just gives me a chance to delete a file from the directory. 
            Console.Write("Ready to save...")
            Console.ReadLine
            Console.WriteLine
            zipArchive.Save(DestFile)
            ''zipArchive.Dispose  '' not necessary with a using clause
        Catch ex As Exception
            MsgBox("The attempt to create a backup file failed with the following error: " & _
                Chr(13) & ex.Message & Chr(13) & "Please contact the helpdesk for assistance." & _
                Chr(13) & Chr(13) & ex.StackTrace, MsgBoxStyle.Critical, _
                "Backup File Creation Failed")
            FileIO.FileSystem.WriteAllText("C:\SMGBackupErrorLog.txt", vbCrLf & _
                vbCrLf & Now() & vbCrLf & ex.Message & vbCrLf & ex.StackTrace & _
                vbCrLf, True)
            Return
        End Try
    End Using


End Sub


Sep 1, 2009 at 9:48 PM
Edited Sep 1, 2009 at 9:55 PM

And yes, struggling with this too...maybe fresh eyes tomorrow will help. 

 

            Try
                zipExtract = Zip.ZipFile.Read(SourceFile)
                For Each checkedEntry As System.String In CheckedListBox1.CheckedItems
                    Dim checkedFile As String = CheckedListBox1.GetItemText(checkedEntry)
                    zipExtract.Extract(checkedFile, DestFolder, Zip.ExtractExistingFileAction.OverwriteSilently)
                Next
                zipExtract.Dispose()
            Catch ex As Exception

 

**Just got your last post...I'll try in the morning..

Coordinator
Sep 1, 2009 at 10:04 PM

Try this:

Try
    zipExtract = Zip.ZipFile.Read(SourceFile)
    For Each checkedEntry As System.String In CheckedListBox1.CheckedItems
        Dim checkedFile As String = CheckedListBox1.GetItemText(checkedEntry)
        zipExtract(checkedFile).Extract(DestFolder, Zip.ExtractExistingFileAction.OverwriteSilently)
    Next
    zipExtract.Dispose()
Catch ex As Exception 


zipExtract(checkedFile) retrieves the ZipEntry with that given filename. 

zipExtract(checkedFile).Extract() calls the Extract() method on the ZipEntry.

Sep 2, 2009 at 2:50 PM

Ok, getting ready to start working on this and I'll let you know. But as for the listing order of parameters, in this example (or general rule), should AddDirectory (or Add<whatever>) be last (before save)? I'm thinking yes, that way there is no question as to whether or not all other settings are applied.

 

 

            
            zipArchive.CompressionLevel = Zlib.CompressionLevel.BestCompression
            zipArchive.UseZip64WhenSaving = Ionic.Zip.Zip64Option.Always
            zipArchive.TempFileFolder = txtFolderSave.Text
            zipArchive.ZipErrorAction = Zip.ZipErrorAction.InvokeErrorEvent
            AddHandler zipArchive.ZipError, AddressOf zipArchive_ZipError
           zipArchive.AddDirectory(SourceFolder)
zipArchive.Save(DestFile)

 

 

Sep 2, 2009 at 3:17 PM

Very early initial tests seem to work ok, although I'm still testing and playing.

One thing I did notice was it writes the skipped file(s) twice in the log file.

 

9/2/2009 10:08:44 AM ACRZ.txt
9/2/2009 10:08:46 AM ACRZ.txt
9/2/2009 10:08:46 AM Active Directory Time Sync.docx
9/2/2009 10:08:47 AM Active Directory Time Sync.docx

And the code is:

 

    Private Sub zipArchive_ZipError(ByVal sender As Object, ByVal e As Ionic.Zip.ZipErrorEventArgs) Handles zipArchive.ZipError

        FileIO.FileSystem.WriteAllText("C:\SMGBackupErrorTest.txt", vbCrLf & vbCrLf & Now() & e.CurrentEntry.FileName, True)
        e.CurrentEntry.ZipErrorAction = Zip.ZipErrorAction.Skip

    End Sub

I'm guessing there is actually 2 error events firing on 1 file...? Not a show stopper by any means. I'll keep testing and follow up later. 

As usual, you're a big help. Remind me to send you half my paycheck this week. :)

 

 

Coordinator
Sep 2, 2009 at 4:29 PM

I guess there are two separate events, but I don't know what they would be.   If you skip the entry (as you do), it should move on. 

You could learn more by including the exception into the log message for each event with e.Exception.Message:

FileIO.FileSystem.WriteAllText("C:\SMGBackupErrorTest.txt", vbCrLf & vbCrLf & Now() & _
    e.CurrentEntry.FileName & " : " & e.Exception.Message, True)

 

Sep 3, 2009 at 5:01 PM

Ok Cheeso, job well done once again. In my testing I can't break it, but I've updated the program which automatically updates end users. That said, the real test will come over the course of a few days but I think it will work just fine.

The only thing I noticed is, as mentioned above, something in your code (I assume) fires the error event twice. Which, in turn, writes 2 entries in my log file. This is no big deal to me (but it might bother you). What I do:

 

            zipArchive.ZipErrorAction = Zip.ZipErrorAction.InvokeErrorEvent
            AddHandler zipArchive.ZipError, AddressOf zipArchive_ZipError
            zipArchive.AddDirectory(SourceFolder)
            zipArchive.CompressionLevel = Zlib.CompressionLevel.BestCompression
            zipArchive.UseZip64WhenSaving = Ionic.Zip.Zip64Option.Always
            zipArchive.TempFileFolder = txtFolderSave.Text
            zipArchive.Save(DestFile)

    Private Sub zipArchive_ZipError(ByVal sender As Object, ByVal e As Ionic.Zip.ZipErrorEventArgs) Handles zipArchive.ZipError

        FileIO.FileSystem.WriteAllText("C:\SMGBackupSkippedFiles.txt", vbCrLf & Now() & "  FILE: " & e.CurrentEntry.FileName & vbCrLf & "REASON: " & e.Exception.Message, True)
        e.CurrentEntry.ZipErrorAction = Zip.ZipErrorAction.Skip
        fileerrorcheck = 1

    End Sub


9/3/2009 11:37:54 AM  FILE: Active Directory Time Sync.docx
                      REASON: Could not find file 'C:\Users\jeffy.SELLETHICS\Documents\Active Directory Time Sync.docx'.
9/3/2009 11:37:54 AM  FILE: Active Directory Time Sync.docx
                      REASON: Could not find file 'C:\Users\jeffy.SELLETHICS\Documents\Active Directory Time Sync.docx'.
9/3/2009 11:37:54 AM  FILE: ar_for_exchange_guide_beta2.pdf
                      REASON: Could not find file 'C:\Users\jeffy.SELLETHICS\Documents\ar_for_exchange_guide_beta2.pdf'.
9/3/2009 11:37:54 AM  FILE: ar_for_exchange_guide_beta2.pdf
                      REASON: Could not find file 'C:\Users\jeffy.SELLETHICS\Documents\ar_for_exchange_guide_beta2.pdf'.
9/3/2009 11:37:54 AM  FILE: ASA Add-Delete.pdf
                      REASON: Could not find file 'C:\Users\jeffy.SELLETHICS\Documents\ASA Add-Delete.pdf'.
9/3/2009 11:37:54 AM  FILE: ASA Add-Delete.pdf
                      REASON: Could not find file 'C:\Users\jeffy.SELLETHICS\Documents\ASA Add-Delete.pdf'.

 

Again, no big deal and all seems to be working quite well - so thank you.

(separate observation on when you update the archive file - After the update is completed, if I look at the zip archive thru 7zip, Windows, whomever the archive is alphabetical, if I open the archive with dotnetzip, it's alphabetical, but any updated files show at then end. This is not a big deal, but just curious as to why?)

 

Coordinator
Sep 3, 2009 at 5:49 PM

Thanks for testing it Jeff.  I'll see if I can understand how or why there are two updates.  Seems like that shouldn't happen.

On the ordering of entries- DotNetZip presents them to your application in the order in which they appear in the zipfile.  I guess if I were going to write an application (like 7zip or Windows Compressed Folders), I'd probably want a list of entries, sorted alphabetically. 

 

Coordinator
Sep 3, 2009 at 6:49 PM

On the double-firing ...

I think the cause is that the error handler is being registered twice.  It's just one error, but DotNetZip is invoking the handler twice. 

I'm not a VB expert , but I think the words "Handles zipArchive.ZipError" at the end of your error handler, successfully registers it as a handler for errors.  Then, I've also advised you to call

AddHandler zipArchive.ZipError, AddressOf zipArchive_ZipError

..which registeres the handler.  So you've got 2 registrations for the same handler.  Hence two messages.

Obvious fix is to register just once.  I don't know how your code is structured and as I said am no VB expert.  BUT, my preference would be to keep the AddHandler statement and dump the "Handles zipArchive.ZipError".  In v1.9.0.2, available now, you can also skip the statement like:  

zipArchive.ZipErrorAction = Zip.ZipErrorAction.InvokeErrorEvent

It also works to skip the AddHandler and keep the "Handles zipArchive.ZipError". (in v1.9.0.2)  

 

Sep 3, 2009 at 7:08 PM

As usual, you're right. I removed the AddHandler as a test and viola. Double registration was the issue.

 

In v1.9.0.2, skip statement..does ZipErrorAction automatically invoke the error event? So if there is an error event handler that code fires, if no handler, then nothing?

Coordinator
Sep 3, 2009 at 7:13 PM

The ZipFile.ZipErrorAction property is still there in v1.9.0.2.  And there is also the ZipError event.  Here's how it works: if you assign a handler (via "Handles ..." or via "AddHandler...") then the ZipErrorAction property implicitly gets set to ZipErrorAction.InvokeErrorEevnt.

My thinking is, if you assign an error handler, then you actually want to invoke it.  On the other hand, the ZipErrorAction property is still there because not everyone wants an event.  Some people want to just skip all files, or retry, or whatever.  So both properties are still there, but you need to set only one of them. 

If there is an error handler, the event fires.  If no error handler, then check the ZipErrorAction and see what the application wants in lieu of an error handler.

make sense?

 

Sep 3, 2009 at 7:29 PM

Makes sense and what I was thinking (roughly, just not in a sensei programming mind such as yourself.)

I'll give the v1.9.02 a go and see what happens! Thanks again.

Coordinator
Sep 3, 2009 at 8:38 PM

ha! we all have our talents. yours is running a business.

 

Sep 3, 2009 at 8:43 PM

I'm just a Network Engineer by title. I dabble in a little of a lot and try to do what I can to help the small business in which I'm employed.

I like what I do and enjoy life outside of work! Even if the Bucs are killing me with all this rebuilding....jeez.

I implemented v1.9.0.2 and all seems well. Kudos to your abilities, you're a smart dude and I appreciate all the help.

jeff

Coordinator
Sep 3, 2009 at 8:51 PM
Edited Sep 3, 2009 at 8:52 PM

it's my pleasure to help. It's a long time since you reported the problem and I hope what it does now, works for you.

What Bucs? 

oh... the 17-year-streak Bucs.  *Those* Bucs.  Give it 5 more years I guess.

Baseball season in Pittsburgh is the time beteen the end of the Stanley Cup playoffs and the beginning of Steeler training camp. And since we are well into the NFL pre-season, baseball season has been over for a while now.

 

 

Sep 3, 2009 at 8:54 PM

Tampa Bay Bucs.... that's right, the one's who fired their **new** OC today 10 days before the home opener vs the Cowgirls. The bright side is they have MUCH better talent the prognosticators are giving them credit for...they'll surprise a few people.

Coordinator
Sep 3, 2009 at 9:12 PM

oh that's right, we went over this.

How did I forget?