Pro Dev
Pro Dev

Reputation: 684

Why copying large files using system.io File.Copy becomes unresponsive/not responding in windows forms.

I'm creating a program that has auto-backup function, when i tried to copy large or many files in a single folder my application becomes unresponsive/not responding.

I'm using System.IO.File.Copy(source, destination) to copy files.

Below is my code for copying files. I'm also using a progressbar to indicate the file copy process.

Imports System.IO
Public class Form1
    Private Sub btnDestPath_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnBrowseDestPath.Click
        'get destination path from FolderBrowserDialog
        Dim destination As String

        If (FolderBrowserDialog1.ShowDialog() = Windows.Forms.DialogResult.OK) Then
            destination = FolderBrowserDialog1.SelectedPath
            txtDestination.Text = destination
        End If
    End Sub

     Private Sub btnCopy_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnCopy.Click
        'set folder to copy files
        Dim sourceFilePath As String = "C:\copyfiles\" 

        'get destinationpath from txtDestination.Text label
        Dim destinationPath As String = txtDestination.Text.Replace(vbCrLf, "")

        'add back slash(\) to destinationPath
        Dim filepath As String = destinationPath & "\"

        'count number of files to copy 
        Dim filecount As String
        filecount = Directory.GetFiles(sourceFilePath).Count

        ProgressBar1.Maximum = 1
        ProgressBar1.Maximum = filecount 'represent the # of files
        ProgressBar1.Step = 1 'step property value of 1 to represent each file being copied

        For Each filename As String In Directory.GetFiles(sourceFilePath)
            'check if files exist or not
            If File.Exists(filename) Then
                Dim dFile As String = String.Empty
                Dim dFilePath As String = String.Empty

                dFile = Path.GetFileName(filename)  'source file filenames
                dFilePath = filepath & dFile  'concatenate filepath and filenames

                File.Copy(filename, dFilePath, True) 'copy files from "c:\copyfiles" folder to destinationPath
                ProgressBar1.PerformStep()
            End If

        Next
        MsgBox("Copy Successful", vbOKOnly, "Message")
    End Sub
End Class

Is there anything I can do to improve the code. I hear about using BackgroundWorker would solve the problem, but i don't know where to start. I'm just new to .NET language.

Any help would be great.

Upvotes: 1

Views: 1689

Answers (2)

Scott Hannen
Scott Hannen

Reputation: 29222

You're on the right track with BackgroundWorker. You're currently executing your work on the same thread used to respond to your UI. That means that until your file copy finishes, the application is 'too busy' to pay attention to the user clicking on a button or doing anything else.

A BackgroundWorker executes that method on a different thread, meaning that your UI thread is still available to respond to the user. You might enable them to perform other actions, or you might just have a "cancel" button that allows them to stop the operation.

The documentation and example for BackgroundWorder are ok. In the example a long-running process is simulated by just having the background thread sleep for a moment. If that happened on the UI thread then the application would freeze, but the application doesn't freeze because it's the BackgroundWorker thread that's sleeping. The steps are

  • Add one to your form (drag from the toolbox)
  • Execute your file copy method from the BackgroundWorker's DoWork event. Think of this as the same as putting code in a button's Click event.
  • When the user wants to begin copying files, call myFileCopyBackgroundWorkder.RunWorkerAsync();. That starts the work happening on a background thread.
  • When the BackgroundWorker has progress to report call ReportProgress(). An event handler in your form can then show the progress to the user.
    This is important because you don't want something running on a different thread trying to update the UI. You want it to raise an event that the UI thread listens for, and the UI thread updates the UI.
  • If the user wants to cancel, call CancelAsync. As you're looping through the files, check myFileCopyBackgroundWorkder.CancellationPending to see if the user has requested a cancellation from the main thread. If they have then you can stop.
  • When the background processes completes (finishes, cancels, fails) it raises a Completed event so you can indicate that to the user. Or perhaps you disabled certain controls while the process was running (like disabling the "Start" button.) When the process is completed you can re-enable it.

Upvotes: 4

Jalal Mostafa
Jalal Mostafa

Reputation: 1024

Actually it's not a problem.

You've to know that each single process has one worker (the main thread) which is responsible for user interface and user interaction with the program. When you start copying the large file using a synchronous operation like File.Copy(), this worker (the main thread) is busy copying the file and can't handle events in the UI or do any other operation unless the synchronous operation of copying the file ends, therefore the application UI becomes unresponsive. If you wait the copying process to end (i.e. the synchronous operation ends), the UI will return to a responsive state.

If you want to solve this, you have to use asynchronous operations (which will not block as synchronous operations read more here) or you have to run the synchronous operation on a new single thread or a Task as suggested here. I recommend using an asynchronous operation.

Upvotes: 1

Related Questions