Search code examples
error-handlingbackgroundworkerglobalfilesystemwatcher

Global Error Handler for FileSystemWatcher and BackgroundWorker


I have written a FileProcessor class which wraps the FileSystemWatcher (fsw), and also has a BackgroundWorker (bgw) thread to process items in a Queue;

The FileProcessor class gets consumed from a WPF App; thus it is the WPF UI which starts both the fsw threads and bgw threads;

I don't need to tell the WPF UI thread that errors have occurred, what i need is to ensure that errors in the fsw and bgw threads don't bring down (crash) the WPF UI, which is my current problem.

I handled the errors which i know about, and that stopped the crashing, but what i really need are global catch-All error handlers for both the fsw and bgw objects to (silently) ignore any unexpected errors. Is there such a thing?

public class FileProcessor
{
     private FileSystemWatcher _fsw;
     private BackgroundWorker _bgw;


     //constructor
    public FileProcessor()
        {
        //initialize
        _bgThread = new BackgroundWorker();
        _fsw = new FileSystemWatcher();

        _fsw.Created += new FileSystemEventHandler(fsw_OnCreated);
        _fsw.Error += new ErrorEventHandler(fsw_OnError);
        //... etc.

        }


        public void StartAsync()
        {
        _fsw.EnableRaisingEvents = true; //start fsw on its own thread     
        _bgThread.RunWorkerAsync(); //start bgw on its own thread

        //... etc

        }

        private void fsw_OnCreated(object sender, FileSystemEventArgs e)
        {

              //local error handler;
              try
              {
                 DoStuff();
              }
              catch (Exception ex)
              {
                 MessageBox.Show(ex.Message);
              }
        }


    void fsw_OnError(object sender, ErrorEventArgs e)
     {

          //THIS EVENT NEVER FIRED OnError from DoStuff() when i didn't have try/catch around DoStuff() ! 
          //so it seems it's not meant to handle all Global errors from fsw.
          MessageBox.Show(e.GetException().Message);
     }


     //What i want are Global Error Handlers so that _fsw does not bring down the UI!; 
     //Ditto for _bgw


} //end FileProcessor class

... and some method in the WPF UI which consumes above FileProcessor class.

FileProcessor _processor = new FileProcessor() //form level.
private void btnButtonStart_Click(object sender, RoutedEventArgs e)
{
   _processor.StartAsync();
}

EDIT: If it is relevant, currently the FileProcessor class and the WPF UI are in the same Project (of type Windows Application), but i intend to move FileProcessor into its own Class Library project.


Solution

  • BackgroundWorker only runs one event in its background thread: DoWork. If DoWork raises an exception, then it is caught and passed as part of the argument into RunWorkerCompleted. So it is not possible for a BGW to take down a process due to an unhandled exception in DoWork.

    FileSystemWatcher is at a slightly lower level of abstraction. It normally raises its events on the ThreadPool. I'm of the opinion that the event handlers simply should not throw exceptions (but neither should they try to prevent all exceptions): catch and handle vexing and exogenous exceptions, and allow boneheaded and fatal exceptions to crash the process. IMO, it is very dangerous to ignore unexpected exceptions.

    If you do wish to continue on the "ignore unexpected exceptions" path, then there are a few approaches you can take. First, you can assign FileSystemWatcher.SynchronizingObject to marshal its events into a WPF Dispatcher. WPF does not include an appropriate object for this, but you can use the GenericSynchronizingObject from the Nito.Async library. Once the FSW has its events marshaled to the Dispatcher, you can use Dispatcher.UnhandledException to handle any exceptions coming from the Dispatcher context. Note that this may include some exceptions that are not from the FSW; in particular, it would include any events that run in the UI context, such as BackgroundWorker.RunWorkerCompleted.

    Alternatively, you can set an "ignore all ThreadPool exceptions" flag in your app.config:

    <legacyUnhandledExceptionPolicy enabled="1"/>
    

    I really recommend you don't do that, though. It will catch, write to the console, and then ignore all exceptions on ThreadPool threads (including FSW event handlers if you leave SynchronizingObject set to null). This is very dangerous, since you don't know what the exceptions are. This flag was introduced in .NET 2.0 for people to more easily port their .NET 1.0 and .NET 1.1 code; .NET 4.0 still has the flag, but it's a backwards compatibility hack that may disappear in a future version.