2

I have a class called DirectoryCopier which expose progress event in which my windows form object will subscribe to this event then call CopyFolderAsync to start copying & notify event handlers on different thread.

class DirectoryCopier
{
    public event EventHandler<CopyProgressChangedEventArgs> CopyProgressChanged;
    public void CopyFolderAsync()
    {
        Task.Run(() => CopyFolder(Source, Destination));
    }
    void CopyFolder(string src, string des)
    {
        string srcName = Path.GetFileName(src);
        string desDir = Path.Combine(des, srcName);
        Directory.CreateDirectory(desDir);
        var files = Directory.EnumerateFiles(src);
        foreach (string file in files)
        {
            string fileName = Path.GetFileName(file);
            string desFile = Path.Combine(desDir, fileName);
            CopyFile(file, desFile);
        }
        var dirs = Directory.EnumerateDirectories(src);
        foreach (string dir in dirs)
            CopyFolder(dir, desDir);
    }
    void CopyFile(string src, string dest)
    {
        using (FileStream input = new FileStream(src, FileMode.Open, FileAccess.Read, FileShare.None, buffer.Length))
        using (FileStream output = new FileStream(dest, FileMode.Create, FileAccess.Write, FileShare.None, buffer.Length))
        {
            byte[] buffer = new byte[1024 * 1024];
            int bytesRead;
            do
            {
                bytesRead = input.Read(buffer, 0, buffer.Length);
                output.Write(buffer, 0, bytesRead);
                if (CopyProgressChanged != null)
                    CopyProgressChanged.BeginInvoke(this, new CopyProgressChangedEventArgs(,,,),null,null);
            }
            while (bytesRead > 0);
        }
    }
}

In my form's event handler, i do update a progressbar with the received event args, this works fine when i run.

        progressBar1.Value = e.ProgressPercentage;

When i debug my app the progressbar value is updated normally but is not advanced in my form and many cross-thread exceptions have been caught in IntelliTrace pane. I can use Form.Invoke() to update the progressBar but is this the responsibility of the event handler? is there any way to invoke every subcriber on his own/creation thread?

Ramy Yousef
  • 2,982
  • 2
  • 25
  • 24

1 Answers1

1

The problem isn't invoking every subscriber on his creation thread, its specifically the UI control which bound to the UI thread.

I've found a nice extension method here which attempts to extract the SynchronizationContext for each delegate. If one exists, it will invoke the delegate on it using BeginInvoke:

public static object Raise(this MulticastDelegate 

multicastDelegate, object sender, EventArgs e) { object retVal = null;

    MulticastDelegate threadSafeMulticastDelegate = multicastDelegate;
    if (threadSafeMulticastDelegate != null)
    {
        foreach (Delegate d in threadSafeMulticastDelegate.GetInvocationList())
        {
            var synchronizeInvoke = d.Target as ISynchronizeInvoke;
            if ((synchronizeInvoke != null) && synchronizeInvoke.InvokeRequired)
            {
                retVal = synchronizeInvoke.EndInvoke(synchronizeInvoke.BeginInvoke(d, new[] { sender, e }));
            }
            else
            {
                retVal = d.DynamicInvoke(new[] { sender, e });
            }
        }
    }

    return retVal;
}

}

And you can use it on your event like this:

MyEvent.Raise(sender, eventArgs);
Community
  • 1
  • 1
Yuval Itzchakov
  • 146,575
  • 32
  • 257
  • 321