Unzip with progress using Delegate to Invoke

Jul 8, 2012 at 8:09 PM
Edited Jul 10, 2012 at 2:23 PM

Hi Can someone please help with this. Its working but freezing up the UI so after googling for a while, It was brought to my attention that you need to invoke via delegate. I have no clue how to do that But tried my best.  ANY help will greately be appreciated.

Also, I have one large File that is over 3GB and it will not update the progressbar. Its it even possible to update the Progressbar with one large file?

Thanks in advance.

 

 

    Delegate Sub SetLabelTextInvokerZip(ByVal lblInfo As Label, ByVal Text As String, ByVal ProgBarZip As ProgressBar)

    Sub SetLabelTextZip(ByVal lblInfo As Label, ByVal Text As String, ByVal ProgBarZip As ProgressBar)
        If lblInfo.InvokeRequired = True Then
            lblInfo.BeginInvoke(New SetLabelTextInvokerZip(AddressOf SetLabelTextZip), lblInfo, Text, ProgBarZip)
        Else
            lblInfo.Text = Text
            ProgBarZip.Value = 0
        End If
    End Sub

 

    Sub Extract_Zip_Files()

        Dim ZipToUnpack As String = "C:\temp\1.zip"
        Dim DirectoryToExstractTo As String = "C:\Temp\Extract"
        Dim ProgBarZip As Integer
        SetLabelTextZip(lblInfo, "start")

        Try
            Using zip As ZipFile = ZipFile.Read(ZipToUnpack)
                ProgressBar2.Maximum = zip.Entries.Count
                Dim entry As ZipEntry
                For Each entry In zip
                    lblInfo.Text = entry.FileName
                    entry.Extract(DirectoryToExstractTo, ExtractExistingFileAction.OverwriteSilently)
                    ProgressBar2.Value = ProgressBar2.Value + 1
                    ' sleep because it's too fast otherwise.
                    System.Threading.Thread.Sleep(50)
                Next
                ProgressBar1.Value = 0
                SetLabelTextZip(lblInfo, "Done")
            End Using
        Catch ex As Exception
            MsgBox("Exception: " & ex.ToString())
        End Try
    End Sub

Jul 10, 2012 at 2:14 PM
Edited Jul 10, 2012 at 2:33 PM

Hi All,

I was able to get this to work after googling for a few days.  The only challenge is that the progress bar does not get update. Can anyone help with it. 

 I tried the following in the invoke section to no avail. ANY help will be greatly appreciated.

 ' Me.ProgressBar1.Value = 100 * e.BytesTransferred / e.TotalBytesToTransfer

' Me.ProgressBar1.Value = 100 * TranferTotal / TotalSize

' lblResults.Text = CStr(100 * (e.BytesTransferred / e.TotalBytesToTransfer).ToString("#,##0.00")) & "% Complete"

'ProgressBar1.Value = Convert.ToInt32(100 * e.BytesTransferred / e.TotalBytesToTransfer)

 

ImportsIonic.Zip

 Imports System.Threading

 

Public Class Form1

   Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click

       BackgroundWorker1.RunWorkerAsync()

   End Sub

 

   Private Sub BackgroundWorker1_DoWork(sender As System.Object, e As System.ComponentModel.DoWorkEventArgs) Handles BackgroundWorker1.DoWork

       lblResults.Text = Nothing

       DotNetZip_Extract_With_ProgressBar()

 

   End Sub

 

   Private Delegate Sub ZipProgress(ByVal e As ExtractProgressEventArgs)

   Private Delegate Sub ExtractEntryProgress(ByVal e As ExtractProgressEventArgs)

 

   Private Sub zip_ExtractProgress(ByVal sender As Object, ByVal e As ExtractProgressEventArgs)

       StepEntryProgress(e)

   End Sub

 

   Private Sub StepEntryProgress(ByVal e As ExtractProgressEventArgs)

       'Dim TranferTotal As Integer

       '   TranferTotal = TranferTotal + backup.UncompressedSize

       ' TranferTotal = e.BytesTransferred + TranferTotal

 

       If Me.ProgressBar1.InvokeRequired Then

           ProgressBar1.Invoke(New ExtractEntryProgress(AddressOf Me.StepEntryProgress), New Object() {e})

       Else

           If ProgressBar1.Maximum = 100 Then

 

               ProgressBar1.Value = Convert.ToInt32(100 * e.BytesTransferred / e.TotalBytesToTransfer)

       ' Me.ProgressBar1.Value = 100 * e.BytesTransferred / e.TotalBytesToTransfer

       ' lblResults.Text = CStr(100 * (e.BytesTransferred / e.TotalBytesToTransfer).ToString("#,##0.00")) & "% Complete"

       ' Me.ProgressBar1.Value = 100 * TranferTotal / TotalSize

             Else

               lblResults.Text = "Done"

           End If

       End If

 

   End Sub

 

   Sub DotNetZip_Extract_With_ProgressBar()

       Dim TotalSize = 0

       Dim TranferTotal As Integer = 0

       Dim ZipToUnpack As String = "C:\Temp\1.zip"

       Dim extractDir As String = "C:\Temp\Extract"

 

       Using zip As ZipFile = ZipFile.Read(ZipToUnpack)

           AddHandler (zip.ExtractProgress), New EventHandler(Of ExtractProgressEventArgs)(AddressOf Me.zip_ExtractProgress)

           For Each backup In zip

               TotalSize = backup.UncompressedSize + TotalSize

           Next

           For Each backup In zip

               backup.Extract(extractDir, Ionic.Zip.ExtractExistingFileAction.OverwriteSilently)

           Next

       End Using

    End Sub

End Class

 

 

 

 

Jul 10, 2012 at 5:11 PM

Anyone?

Jul 10, 2012 at 9:29 PM
Edited Jul 10, 2012 at 9:32 PM

I think you're going about it the wrong way. Put the required handlers in your BackgroundWorker_DoWork. It looks like you've done this step.

 

AddHandler Zip.SaveProgress, New EventHandler(Of SaveProgressEventArgs)(AddressOf Zip_SaveProgress)
AddHandler Zip.AddProgress, New EventHandler(Of AddProgressEventArgs)(AddressOf Zip_AddProgress)
AddHandler Zip.ExtractProgress, New EventHandler(Of ExtractProgressEventArgs)(AddressOf Zip_ExtractProgress)

Then create the appropriate subs (Zip_SaveProgress, Zip_AddProgress, Zip_ExtractProgress). It looks like you didn't do this step. here are some examples:
    Private Sub Zip_AddProgress(ByVal sender As Object, ByVal e As AddProgressEventArgs)
        If bwWorker.CancellationPending Then e.Cancel = True 'If we press the stop button, invoke Cancel. RunWorkerCompleted will be called.

        Select Case e.EventType
            Case ZipProgressEventType.Adding_AfterAddEntry
                SetStatusText(lang_Gathering & " " & e.CurrentEntry.FileName.Substring(e.CurrentEntry.FileName.LastIndexOf("/") + 1, e.CurrentEntry.FileName.Length - e.CurrentEntry.FileName.LastIndexOf("/") - 1))
        End Select

    End Sub
    Private Sub Zip_SaveProgress(ByVal sender As Object, ByVal e As SaveProgressEventArgs)
        If bwWorker.CancellationPending Then e.Cancel = True 'If we press the stop button, invoke Cancel. RunWorkerCompleted will be called.

        Select Case e.EventType
            Case ZipProgressEventType.Saving_AfterWriteEntry
                bwBackup.ReportProgress(CInt(Math.Truncate(100 * (e.EntriesSaved / e.EntriesTotal))))
                SetStatusText(lang_Compressing & " " & CInt(Math.Truncate(100 * (e.EntriesSaved / e.EntriesTotal))) & lang_PercentComplete)
        End Select

    End Sub
    Private Sub Zip_ExtractProgress(ByVal sender As Object, ByVal e As ExtractProgressEventArgs)
        If bwWorker.CancellationPending Then e.Cancel = True 'If we press the stop button, reset the variables we used for extracting, then invoke Cancel. RunWorkerCompleted will be called.

        Select Case e.EventType
            Case ZipProgressEventType.Extracting_BeforeExtractEntry
                CurrentCount += 1
                bwWorker.ReportProgress(CInt(Math.Truncate(100 * (CurrentCount / TotalCount))))
                SetStatusText(lang_Extracting & " " & CInt(Math.Truncate(100 * (CurrentCount / TotalCount))) & lang_PercentComplete)
        End Select

    End Sub

 

 

Enable the background worker's Report Progress. Then,

 

    Private Sub bwWorker_ProgressChanged(sender As Object, e As System.ComponentModel.ProgressChangedEventArgs) Handles bwbwWorker.ProgressChanged
        ProgressBar.Value = e.ProgressPercentage
    End Sub

 

You don't need to invoke controls if you report progress this way., Through the Zip_Add/Save/ExtractProgress subs, I am sending information to the background worker report sub via bwWorker.Report = Value, which then the progress bar uses (never invoked).

 

The only invoke I use is SetStatusText, and honestly, I could probably get around that with the ReportProgress also.

Do not sleep the thread because the progress bar doesn't keep up. This is a Windows theme (Vista+) issue, and not a code problem.

All of this information is available in the documentation. However, I remember having issues with this also.

Jul 11, 2012 at 4:06 AM
Edited Jul 11, 2012 at 4:10 AM

Hi Aerowinder,

Thanks for the suggestion. I am very new to vb.net so this a bit of a challenge for me. I've done the below as best to my knowledge and the files are extracting but no progress bar. If you create a window form and

1) add a button, label, background worker and progress bar and add the below code you'll see what I'm talking about.  The rem lines are having issues. Any help will  greatly be appreciated.

 

 

 

Imports Ionic.Zip
Imports System.Threading

Public Class Form1

    Private Property CurrentCount As Integer
    Private Property TotalCount As Integer

    Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
        BackgroundWorker1.RunWorkerAsync()
    End Sub

    Private Sub BackgroundWorker1_ProgressChanged(sender As Object, e As System.ComponentModel.ProgressChangedEventArgs) Handles BackgroundWorker1.ProgressChanged
        Label1.Text = ""
        ProgressBar1.Value = e.ProgressPercentage
    End Sub
    Private Sub BackgroundWorker1_DoWork(sender As System.Object, e As System.ComponentModel.DoWorkEventArgs) Handles BackgroundWorker1.DoWork
        Dim TotalSize = 0
        Dim TranferTotal As Integer = 0
        Dim ZipToUnpack As String = "C:\temp\1.zip"
        Dim extractDir As String = "C:\temp\extract"

        Using zip As ZipFile = ZipFile.Read(ZipToUnpack)
            AddHandler (zip.ExtractProgress), New EventHandler(Of ExtractProgressEventArgs)(AddressOf Zip_ExtractProgress)
            AddHandler zip.SaveProgress, New EventHandler(Of SaveProgressEventArgs)(AddressOf Zip_SaveProgress)
            AddHandler zip.AddProgress, New EventHandler(Of AddProgressEventArgs)(AddressOf Zip_AddProgress)
            For Each backup In zip
                TotalSize = backup.UncompressedSize + TotalSize
            Next
            For Each backup In zip
                backup.Extract(extractDir, Ionic.Zip.ExtractExistingFileAction.OverwriteSilently)
            Next
        End Using
    End Sub

    Private Sub Zip_AddProgress(ByVal sender As Object, ByVal e As AddProgressEventArgs)
        If BackgroundWorker1.CancellationPending Then e.Cancel = True 'If we press the stop button, invoke Cancel. RunWorkerCompleted will be called.

        Select Case e.EventType
            Case ZipProgressEventType.Adding_AfterAddEntry
                '  Label1(lang_Gathering & " " & e.CurrentEntry.FileName.Substring(e.CurrentEntry.FileName.LastIndexOf("/") + 1, e.CurrentEntry.FileName.Length - e.CurrentEntry.FileName.LastIndexOf("/") - 1))
        End Select

    End Sub

    Private Sub Zip_SaveProgress(ByVal sender As Object, ByVal e As SaveProgressEventArgs)
        If BackgroundWorker1.CancellationPending Then e.Cancel = True 'If we press the stop button, invoke Cancel. RunWorkerCompleted will be called.

        Select Case e.EventType
            Case ZipProgressEventType.Saving_AfterWriteEntry
                BackgroundWorker1.ReportProgress(CInt(Math.Truncate(100 * (e.EntriesSaved / e.EntriesTotal))))
                '    Label1(lang_Compressing & " " & CInt(Math.Truncate(100 * (e.EntriesSaved / e.EntriesTotal))) & lang_PercentComplete)
        End Select

    End Sub

    Private Sub Zip_ExtractProgress(ByVal sender As Object, ByVal e As ExtractProgressEventArgs)
        If BackgroundWorker1.CancellationPending Then e.Cancel = True 'If we press the stop button, reset the variables we used for extracting, then invoke Cancel. RunWorkerCompleted will be called.

        Select Case e.EventType
            Case ZipProgressEventType.Extracting_BeforeExtractEntry
                CurrentCount += 1
                '    BackgroundWorker1.ReportProgress(CInt(Math.Truncate(100 * (CurrentCount / TotalCount))))
                '   Label1(lang_Extracting & " " & CInt(Math.Truncate(100 * (CurrentCount / TotalCount))) & lang_PercentComplete)
        End Select
    End Sub
End Class

Jul 11, 2012 at 9:09 PM

First of all, if you are not using Zip.Add/SaveProgress, remove them and their AddHandler line.





Then, in Visual Studio, right click the solution, and go to properties. Click the compile tab. Option explicit: on. Option strict: on. This requires you to declare every variable with a type. These options should be on by default. A couple examples:

Dim TotalSize = 0

This method of coding forms bad habits. It should read:
Dim TotalSize As Long


It will be zero automatically.


And this:

For Each backup In zip
TotalSize = backup.UncompressedSize + TotalSize
Next

Should be more like this

For Each entry As ZipEntry In zip.Entries
TotalSize += backup.UncompressedSize
Next

The math probably doesn't check out.






The commented text Label1(.....) will not work, because this needs to be delegated in order to be used that way. The proper way to do that, is like this:

 

Private Delegate Sub SetStatusTextInvoker(ByVal Text As String)
Private Sub SetStatusText(ByVal Text As String)
  If Me.InvokeRequired Then
    Me.Invoke(New SetStatusTextInvoker(AddressOf SetStatusText), Text)
  Else
    StatusTS.Text = Text
  End If
End Sub

The label in this case, on the form, is named StatusTS. If you want to update it through another thread (like you are trying to do), you need to address it as SetStatusText(text here). And this will update the label from your worker thread.




Don't clear the label in the BackgroundWorker1_ProgressChanged. Either update it here directly, or use the delegate and update it in the Zip_ExtractProgress.

Jul 11, 2012 at 10:28 PM

Thanks again Aerowinder,  I tried to clean it up as suggested.  There is still an issue with the rem line. Not sure why the progressbar is not updating.  If you feel like, I would greatly appreciate it.  Thanks again.

 

 

Imports Ionic.Zip
Imports System.Threading

Public Class Form1

    Private Property CurrentCount As Integer
    Private Property TotalCount As Integer

    Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
        BackgroundWorker1.RunWorkerAsync()
    End Sub

    Private Sub BackgroundWorker1_ProgressChanged(sender As Object, e As System.ComponentModel.ProgressChangedEventArgs) Handles BackgroundWorker1.ProgressChanged
        ProgressBar1.Value = e.ProgressPercentage
    End Sub

    Private Delegate Sub SetStatusTextInvoker(ByVal Text As String)
    Private Sub SetStatusText(ByVal Text As String)
        If Me.InvokeRequired Then
            Me.Invoke(New SetStatusTextInvoker(AddressOf SetStatusText), Text)
        Else
            Label1.Text = Text
        End If
    End Sub

    Private Sub BackgroundWorker1_DoWork(sender As System.Object, e As System.ComponentModel.DoWorkEventArgs) Handles BackgroundWorker1.DoWork
        Dim TotalSize As Long
        Dim ZipToUnpack As String = "C:\temp\1.zip"
        Dim extractDir As String = "C:\temp\extract"


        Using zip As ZipFile = ZipFile.Read(ZipToUnpack)
            AddHandler (zip.ExtractProgress), New EventHandler(Of ExtractProgressEventArgs)(AddressOf Zip_ExtractProgress)
            For Each Entry As ZipEntry In zip.Entries
                TotalSize += Entry.UncompressedSize
            Next
            For Each Entry As ZipEntry In zip.Entries
                Entry.Extract(extractDir, Ionic.Zip.ExtractExistingFileAction.OverwriteSilently)
            Next
        End Using
    End Sub

    Private Sub Zip_ExtractProgress(ByVal sender As Object, ByVal e As ExtractProgressEventArgs)
        If BackgroundWorker1.CancellationPending Then
            e.Cancel = True 'If we press the stop button, reset the variables we used for extracting, then invoke Cancel. RunWorkerCompleted will be called.
        End If

        Select Case e.EventType
            Case ZipProgressEventType.Extracting_BeforeExtractEntry
                Dim lang_Extracting As Integer
                Dim lang_PercentComplete As Integer

                CurrentCount += 1
                BackgroundWorker1.ReportProgress(CInt(Math.Truncate(100 * (CurrentCount / TotalCount))))
                '   Label1(lang_Extracting & " " & CInt(Math.Truncate(100 * (CurrentCount / TotalCount))) & lang_PercentComplete)
        End Select
    End Sub

End Class


Jul 11, 2012 at 10:41 PM

When you have an error message, you need to tell me what it is, so I don't have to guess. The variables I gave you earlier came from code from one of my programs. It was not made for your program, and as such, you need to adapt it to fit your own code.

 

Let me tell you what I see:

Dim lang_Extracting As Integer
Dim lang_PercentComplete As Integer
Get rid of these. They aren't necessary for your program.

 

CurrentCount is not doing what you want. Here is what it is doing (why is it a Property, by the way?). Before each file is extracted, CurrentCount is incremented by 1. This is not what you want.

TotalCount obtains total uncompressed size for all entries. Looks good.

 

You are attempting to divide CurrentCount / TotalCount as your progress bar value. Think about the math behind this, and you will understand why it's wrong. Entry count / uncompressed entry size. Use ZipProgressEventType.Extracting_EntryBytesWritten instead.

 

Label1(lang_Extracting & " " & CInt(Math.Truncate(100 * (CurrentCount / TotalCount))) & lang_PercentComplete)
I told you how to fix this already.
Jul 12, 2012 at 11:13 AM

Thanks again Aerowinder for your time.  This code I got via google, being this is my first project I'm lost as to what to do here. It is working but not progressbar update.


Imports Ionic.Zip
Imports System.Threading

Public Class Form1

    Private Property CurrentCount As Integer
    Private Property TotalCount As Integer

    Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
        BackgroundWorker1.RunWorkerAsync()
    End Sub

    Private Sub BackgroundWorker1_ProgressChanged(sender As Object, e As System.ComponentModel.ProgressChangedEventArgs) Handles BackgroundWorker1.ProgressChanged
        ProgressBar1.Value = e.ProgressPercentage
    End Sub

    Private Delegate Sub SetStatusTextInvoker(ByVal Text As String)
    Private Sub SetStatusText(ByVal Text As String)
        If Me.InvokeRequired Then
            Me.Invoke(New SetStatusTextInvoker(AddressOf SetStatusText), Text)
        Else
            Label1.Text = Text
        End If
    End Sub

    Private Sub BackgroundWorker1_DoWork(sender As System.Object, e As System.ComponentModel.DoWorkEventArgs) Handles BackgroundWorker1.DoWork
        Dim TotalSize As Long
        Dim ZipToUnpack As String = "C:\temp\1.zip"
        Dim extractDir As String = "C:\temp\extract"

        Try
            Using zip As ZipFile = ZipFile.Read(ZipToUnpack)
                AddHandler (zip.ExtractProgress), New EventHandler(Of ExtractProgressEventArgs)(AddressOf Zip_ExtractProgress)

                For Each Entry As ZipEntry In zip.Entries
                    TotalSize += Entry.UncompressedSize
                Next
                For Each Entry As ZipEntry In zip.Entries
                    Entry.Extract(extractDir, Ionic.Zip.ExtractExistingFileAction.OverwriteSilently)
                Next
            End Using
        Catch EX As Exception
            MessageBox.Show(EX.Message)
        End Try
    End Sub

    Private Sub Zip_ExtractProgress(ByVal sender As Object, ByVal e As ExtractProgressEventArgs)
        If BackgroundWorker1.CancellationPending Then
            e.Cancel = True 'If we press the stop button, reset the variables we used for extracting, then invoke Cancel. RunWorkerCompleted will be called.
        End If

        Select Case e.EventType
            Case ZipProgressEventType.Extracting_EntryBytesWritten

                Dim Entry As Integer
                Dim CurrentCount As Integer = Entry
    
                BackgroundWorker1.ReportProgress(CInt(Int((100 * (CurrentCount / TotalCount)))))
        End Select
    End Sub
End Class




Jul 12, 2012 at 9:20 PM

Anyone?

Jul 12, 2012 at 9:21 PM
Edited Jul 12, 2012 at 9:26 PM

I can't use this forum for posting any more code. It is utterly terrible. I can speak to you on VoIP (Ventrilo/Mumble) if you are interested (email me through the board).

 

But the problem is with this code:

                Dim Entry As Integer
                Dim CurrentCount As Integer = Entry
    
                BackgroundWorker1.ReportProgress(CInt(Int((100 * (CurrentCount / TotalCount)))))

What do you expect it to do? I need you to walk me through how you think this should work (VoIP is best).