Search code examples
vb.netprogress-barfile-copyingmy.resources

Progress bar for copying file from 'My.resources' VB.net


I am trying to copy a file from 'My.resources' with a progress bar, i have search through many other file copy progress bar scripts but none of them support copying from internal; files. here is my code:

Private Sub Install_Click(sender As Object, e As System.EventArgs) Handles Install.Click
    InstallDialog.ShowNewFolderButton = True
    InstallDialog.ShowDialog()
    If Not (IO.Directory.Exists(IO.Path.GetTempPath & "extract")) Then
        IO.Directory.CreateDirectory(IO.Path.GetTempPath & "extract")
    End If
    If Not (InstallDialog.SelectedPath = "") Then
        Dim exepath As String = IO.Path.GetTempPath & "extract\7zG.exe"
        Dim archpath As String = IO.Path.GetTempPath & "extract\arch.7z"
        If File.Exists(archpath) = False Then
            Dim b As Byte() = My.Resources.arch
            Dim TempFile As IO.FileStream = IO.File.Create(archpath)
            TempFile.Write(b, 0, b.Length)'Copies the archive to disk
            TempFile.Flush()
            TempFile.Close()
        End If
        If File.Exists(exepath) = False Then
            Dim c As Byte() = My.Resources.zipexe
            Dim TempFile As IO.FileStream = IO.File.Create(exepath)
            TempFile.Write(c, 0, c.Length)'Copies the 7-zip executable to disk
            TempFile.Flush()
            TempFile.Close()
        End If
            Dim p As New ProcessStartInfo
            p.WindowStyle = ProcessWindowStyle.Normal
            p.FileName = exepath
            p.Arguments = " x " & archpath & " -mmt -aoa -o" & InstallDialog.SelectedPath
            Dim Extract As Process = Process.Start(p)'runs 7-zip to extract the archive
            Dim i As Boolean = True
            Do While i = True
                If Extract.HasExited = True Then
                    i = False
                Else
                    Threading.Thread.Sleep(2000)
                End If
            Loop
            Msgbox("Installation Complete")            
    End If
End Sub

my progress bar is called 'copyprog', 'arch.7z' is about 150Mb and the UI hangs while it's copying, my users are using very slow machines and want to have some response other than the app freezing on button click.


Solution

  • This is one approach (of many) to achieve what you want. This approach leaves the UI alone so it will update fine. I wrote support for two progress-bars, one for total progress (number of files) and one for current progress. The code support cancellation.

    Use and modify as you wish, it's provided as-is.

    The code assumes as mentioned, two progress bars on the form as well as two buttons (install and cancel) - see snapshot below.

    snapshot of demo

    Usage

    You just add to a task-list all the resources you want to copy, then start the cue.

    AddCopyTask(resourceToCopy1, pathToWhereToCopy1)
    AddCopyTask(resourceToCopy2, pathToWhereToCopy2)
    ...
    StartCopyCue()
    

    Complete code:

    Imports System.ComponentModel
    Imports System.IO
    
    Public Class Form1
    
        'If you use small file sizes, lower this value to have progress-bar going
        Private Const BufferSize As Integer = 4096
    
        Private WithEvents bgCopier As New BackgroundWorker
    
        Private Class WorkerPacket
            Public DataRef As Byte()
            Public Filename As String
        End Class
    
        Private taskList As New List(Of WorkerPacket)
        Private _cancel As Boolean = False
    
        '
        '-- Setup worker
        '
        Private Sub Form1_Load(sender As System.Object, e As System.EventArgs) Handles MyBase.Load
    
            bgCopier.WorkerReportsProgress = True
            bgCopier.WorkerSupportsCancellation = True
    
        End Sub
        Private Sub Form1_FormClosing(sender As Object, e As System.Windows.Forms.FormClosingEventArgs) Handles Me.FormClosing
    
            If bgCopier IsNot Nothing Then
                bgCopier.Dispose()
            End If
    
        End Sub
        '
        '-- UI
        '
        Private Sub Install_Click(sender As System.Object, e As System.EventArgs) Handles Install.Click
    
            '
            '-- This is where you initilize the paths and data you want to copy.
            '-- For this example I use the same data
            '
            AddCopyTask(My.Resources.TestData, "c:\test1.dat")
            AddCopyTask(My.Resources.TestData, "c:\test2.dat")
            AddCopyTask(My.Resources.TestData, "c:\test3.dat")
    
            StartCopyCue()
    
        End Sub
        Private Sub CancelCopy_Click(sender As System.Object, e As System.EventArgs) Handles CancelCopy.Click
    
            _cancel = True
    
            If bgCopier.IsBusy Then
                bgCopier.CancelAsync()
            End If
    
        End Sub
        '
        '-- The methods to build and perform task-list
        '
        Private Sub AddCopyTask(data As Byte(), filename As String)
            '
            '-- Create argument packet for worker
            '
            Dim wp As New WorkerPacket
            wp.DataRef = data
            wp.Filename = filename
    
            taskList.Add(wp)
    
        End Sub
        Private Sub StartCopyCue()
            '
            '-- Init total progressbar
            '
            ProgressBarTotal.Value = 0
            ProgressBarTotal.Maximum = taskList.Count
    
            _cancel = False
            '
            '-- Update UI
            '
            Install.Enabled = False
            CancelCopy.Enabled = True
            '
            '-- Go
            '
            CopyBytesToFileMT()
    
        End Sub
        Private Sub CopyBytesToFileMT()
            '
            '-- Get work packet
            '
            Dim wp As WorkerPacket = taskList(0)
            '
            '-- Init progress bars
            '
            ProgressBarCurrent.Value = 0
            ProgressBarTotal.Value += 1
            '
            '-- Start worker
            '
            If Not _cancel Then
                Label1.Text = String.Format("Copying {0}...", Path.GetFileName(wp.Filename))
                bgCopier.RunWorkerAsync(wp)
            End If
    
        End Sub
    
        Private Sub DoWork(s As Object, e As DoWorkEventArgs) Handles bgCopier.DoWork
    
            Dim wp As WorkerPacket = CType(e.Argument, WorkerPacket)
            '
            '-- Calculate segments
            '
            '   note: byte().length returns integer which means we're limited to 2 Gb files
            '
            Dim length As Integer = wp.DataRef.Length
            Dim segments As Integer = CInt(Math.Floor(length / BufferSize))
            Dim leftOver As Integer = length - segments * BufferSize
    
            Dim bf As Integer = BufferSize
            If bf > length Then bf = length
    
            Dim fs As New FileStream(wp.Filename,
                                     FileMode.Create,
                                     FileAccess.Write,
                                     FileShare.None)
            '
            '-- Copy blocks
            '
            For i As Integer = 0 To segments - 1
                '
                '-- Cancelled?
                '
                If e.Cancel Then
                    leftOver = 0
                    Exit For
                End If
                '
                '-- Write a segment to file
                '
                Dim pos As Integer = i * BufferSize
                fs.Write(wp.DataRef, pos, bf)
                '
                '-- Report current progress
                '
                bgCopier.ReportProgress(CInt(pos / length * 100))
    
            Next
            '
            '-- Copy any remainer
            '
            If leftOver > 0 Then
                fs.Write(wp.DataRef, segments * BufferSize, leftOver)
                bgCopier.ReportProgress(100)
            End If
            '
            '-- Done
            '
            fs.Flush()
            fs.Dispose()
    
        End Sub
        Private Sub CopyProgress(s As Object, e As ProgressChangedEventArgs) Handles bgCopier.ProgressChanged
    
            ProgressBarCurrent.Value = e.ProgressPercentage
    
        End Sub
        Private Sub CopyCompleted(s As Object, e As RunWorkerCompletedEventArgs) Handles bgCopier.RunWorkerCompleted
            '
            '-- Remove task just finished
            '
            taskList.RemoveAt(0)
            '
            '-- Do we have another task?
            '
            If taskList.Count > 0 AndAlso Not _cancel Then
                CopyBytesToFileMT()
            Else
                If _cancel Then
                    Label1.Text = "Cancelled by user."
                Else
                    Label1.Text = "Completed!"
                    '
                    '-- Execute other tasks here...
                    '
                End If
                '
                '-- Update UI
                '
                CancelCopy.Enabled = False
                Install.Enabled = True
            End If
    
        End Sub
    
    End Class
    

    Update: The following modification runs a process after the copy tasks has completed. Modify as needed.

    Imports System.ComponentModel
    Imports System.IO
    Imports System.Security
    
    Public Class Form1
    
        Private Event AllCompleted()
    
        Private Const BufferSize As Integer = 4096
    
        Private WithEvents bgCopier As New BackgroundWorker
        Private WithEvents procUnzipper As New Process
    
        Private Class WorkerPacket
            Public DataRef As Byte()
            Public Filename As String
        End Class
    
        Private taskList As New List(Of WorkerPacket)
        Private _cancel As Boolean = False
    
        '
        '-- Setup worker
        '
        Private Sub Form1_Load(sender As System.Object, e As System.EventArgs) Handles MyBase.Load
    
            bgCopier.WorkerReportsProgress = True
            bgCopier.WorkerSupportsCancellation = True
    
            procUnzipper.EnableRaisingEvents = True
            procUnzipper.SynchronizingObject = Me
    
        End Sub
        Private Sub Form1_FormClosing(sender As Object, e As System.Windows.Forms.FormClosingEventArgs) Handles Me.FormClosing
    
            bgCopier.Dispose()
            procUnzipper.Dispose()
    
        End Sub
        '
        '-- UI
        '
        Private Sub Install_Click(sender As System.Object, e As System.EventArgs) Handles Install.Click
            '
            '-- This is where you initilize the paths and data you want to copy.
            '-- For this example I use the same data
            '
            AddCopyTask(My.Resources.TestData, "c:\test1.dat")
            AddCopyTask(My.Resources.TestData, "c:\test2.dat")
            AddCopyTask(My.Resources.TestData, "c:\test3.dat")
    
            StartCopyQue()
    
        End Sub
        Private Sub CancelCopy_Click(sender As System.Object, e As System.EventArgs) Handles CancelCopy.Click
    
            _cancel = True
    
            If bgCopier.IsBusy Then
                bgCopier.CancelAsync()
            End If
    
        End Sub
        '
        '-- The methods to build and perform task-list
        '
        Private Sub AddCopyTask(data As Byte(), filename As String)
            '
            '-- Create argument packet for worker
            '
            Dim wp As New WorkerPacket
            wp.DataRef = data
            wp.Filename = filename
    
            taskList.Add(wp)
    
        End Sub
        Private Sub StartCopyQue()
            '
            '-- Init total progressbar
            '
            ProgressBarTotal.Value = 0
            ProgressBarTotal.Maximum = taskList.Count
    
            _cancel = False
    
            '
            '-- Update UI
            '
            Install.Enabled = False
            CancelCopy.Enabled = True
            '
            '-- Go
            '
            CopyBytesToFileMT()
    
        End Sub
        Private Sub CopyBytesToFileMT()
            '
            '-- Get work packet
            '
            Dim wp As WorkerPacket = taskList(0)
            '
            '-- Init progress bars
            '
            ProgressBarCurrent.Value = 0
            ProgressBarTotal.Value += 1
            '
            '-- Start worker
            '
            If Not _cancel Then
                Label1.Text = String.Format("Copying {0}...", Path.GetFileName(wp.Filename))
                bgCopier.RunWorkerAsync(wp)
            End If
    
        End Sub
    
        Private Sub DoWork(s As Object, e As DoWorkEventArgs) Handles bgCopier.DoWork
    
            Dim wp As WorkerPacket = CType(e.Argument, WorkerPacket)
            '
            '-- Calculate segments
            '
            '   note: byte().length returns integer which means we're limited to 2 Gb files
            '
            Dim length As Integer = wp.DataRef.Length
            Dim segments As Integer = CInt(Math.Floor(length / BufferSize))
            Dim leftOver As Integer = length - segments * BufferSize
    
            Dim bf As Integer = BufferSize
            If bf > length Then bf = length
    
            Dim fs As New FileStream(wp.Filename,
                                     FileMode.Create,
                                     FileAccess.Write,
                                     FileShare.None)
            '
            '-- Copy blocks
            '
            For i As Integer = 0 To segments - 1
                '
                '-- Cancelled?
                '
                If e.Cancel Then
                    leftOver = 0
                    Exit For
                End If
                '
                '-- Write a segment to file
                '
                Dim pos As Integer = i * BufferSize
                fs.Write(wp.DataRef, pos, bf)
                '
                '-- Report current progress
                '
                bgCopier.ReportProgress(CInt(pos / length * 100))
    
            Next
            '
            '-- Copy any remainer
            '
            If leftOver > 0 Then
                fs.Write(wp.DataRef, segments * BufferSize, leftOver)
                bgCopier.ReportProgress(100)
            End If
            '
            '-- Done
            '
            fs.Flush()
            fs.Dispose()
    
        End Sub
        Private Sub CopyProgress(s As Object, e As ProgressChangedEventArgs) Handles bgCopier.ProgressChanged
    
            ProgressBarCurrent.Value = e.ProgressPercentage
    
        End Sub
        Private Sub CopyCompleted(s As Object, e As RunWorkerCompletedEventArgs) Handles bgCopier.RunWorkerCompleted
            '
            '-- Remove task just finished
            '
            taskList.RemoveAt(0)
            '
            '-- Do we have another task?
            '
            If taskList.Count > 0 AndAlso Not _cancel Then
                CopyBytesToFileMT()
            Else
                If _cancel Then
                    Label1.Text = "Cancelled by user."
                Else
                    Label1.Text = "Unzipping..."
                    '
                    '-- Start process
                    '
                    ProgressBarTotal.Style = ProgressBarStyle.Marquee
    
                    Dim arg As String = String.Format(" x ""{0}"" -mmt -aoa -o ""{1}""",
                                                      "theZipFile.7z",
                                                      "installpath\")
    
                    procUnzipper.StartInfo.FileName = "...\7z.exe"
                    procUnzipper.StartInfo.Arguments = arg
                    procUnzipper.Start()
    
                End If
    
            End If
    
        End Sub
        Private Sub UnzipCompleted(s As Object, e As EventArgs) Handles procUnzipper.Exited
    
            'just for example
            'this following require syncronizationobject set, see form_load
            RaiseEvent AllCompleted()
    
        End Sub
        Private Sub Done() Handles Me.AllCompleted
            '
            '-- Update UI
            '
            Label1.Text = "Completed!"
    
            ProgressBarTotal.Style = ProgressBarStyle.Blocks
            CancelCopy.Enabled = False
            Install.Enabled = True
    
        End Sub
    
    End Class