web analytics

How to Use FileSystemWatcher to Monitor File Change and Creation in C#?

Options
@2016-01-15 18:54:46

Waiting for Changes to Occur

Sometimes you want to wait for a change to a file before continuing execution. For example, a program might want to wait for a particular file to be created before continuing. The FileSystemWatcher class has a method, WaitForChanged which blocks execution until a change occurs.

For example, the code below waits for the file "foo.txt" to be created in the monitored directory:

static void WaitForFoo(FileSystemWatcher watcher)
{
  Console.WriteLine("Waiting 10 seconds for foo.txt to be created");

  WaitForChangedResult rslt = watcher.WaitForChanged(WatcherChangeTypes.Created, 10000);

  if (rslt.TimedOut)
    Console.WriteLine("Request timed out.");
  else
    Console.WriteLine("ChangeType = {0}", rslt.ChangeType);
}

The optional second parameter to WaitForChanged method specifies the time in milliseconds to wait before the request times out.

Be careful using WaitForChanged method on the main thread of a console mode program or the UI thread of a Windows Forms application. Since this method blocks until the change occurs or the timeout value is reached, no other processing will take place on the thread that calls it. In a Windows Forms application, the user interface will be unresponsive if this call occurs on the UI thread rather than on a worker thread.

@2016-01-15 20:39:51

I am using FileSystemWatcher to watch file creation in a folder, when any xml file is added /created in that folder, its file path will be displayed in a TextBox.

namespace WpfApplication1
{
  /// <summary>
  /// Interaction logic for MainWindow.xaml
  /// </summary>
  public partial class MainWindow : Window
  {
    FileSystemWatcher w = new FileSystemWatcher();

    public MainWindow()
    {
        InitializeComponent();
    }
    private void _startButton_Click(object sender, RoutedEventArgs e)
    {
       w.Path = @"D:\Temp";
        w.NotifyFilter = NotifyFilters.LastAccess | NotifyFilters.LastWrite
       | NotifyFilters.FileName | NotifyFilters.DirectoryName;
        // Only watch text files.
        w.Filter = "*.xml";

        // Add event handlers.
        w.Created += new FileSystemEventHandler(OnChanged);
        w.EnableRaisingEvents = true;

    }
    // Define the event handlers.
    private void OnChanged(object source, FileSystemEventArgs e)
    {
        try
        {
            this.textBox1.Text = e.FullPath;
        }
        catch (Exception ex)
        {
            //
        }
    }

    private void _stopButton_Click(object sender, RoutedEventArgs e)
    {
        w.EnableRaisingEvents = false;
    }
  }
}

when I run the program and add a xml file to the watched folder, a InvalidOperationException exception is thrown with the following messag:

"The calling thread cannot access this object because a different thread owns it."

Solution

By default FileSystemWatcher calls its registered event handlers in arbitrary threads. WPF/Forms doesnt allow this because method and properties on WPF/Forms UI objects should only be called on the thread that the WPF/Forms UI object was created.

To fix the issue, use txtBox1.Dispatcher.BeginInvoke(priority, uiMethod, args) to get you UI code sent to the UI thread to be executed:

namespace WpfApplication1
{
  /// <summary>
  /// Interaction logic for MainWindow.xaml
  /// </summary>
  public partial class MainWindow : Window
  {
    FileSystemWatcher w = new FileSystemWatcher();

    public MainWindow()
    {
        InitializeComponent();
    }
    private void _startButton_Click(object sender, RoutedEventArgs e)
    {
       w.Path = @"D:\Temp";
        w.NotifyFilter = NotifyFilters.LastAccess | NotifyFilters.LastWrite
       | NotifyFilters.FileName | NotifyFilters.DirectoryName;
        // Only watch text files.
        w.Filter = "*.xml";

        // Add event handlers.
        w.Created += new FileSystemEventHandler(OnChanged);
        w.EnableRaisingEvents = true;

    }
    private void AddMessage(string msg)
    {
        this.textBox1.Text += msg + Environment.NewLine;
    }

    private delegate void AddMessageDelegate(string msg);
    // Define the event handlers.
    private void OnChanged(object source, FileSystemEventArgs e)
    {
        AddMessageDelegate addMessage = new AddMessageDelegate(AddMessage);
        this.textBox1.Dispatcher.BeginInvoke(addMessage, new object[] { e.FullPath });
    }

    private void _stopButton_Click(object sender, RoutedEventArgs e)
    {
        w.EnableRaisingEvents = false;
    }
  }
}

Comments

You must Sign In to comment on this topic.


© 2024 Digcode.com