Search code examples
c#multithreadingxamldelegatesfilesystemwatcher

The calling thread cannot access this object because a different thread owns it (in v4.5)


I am aware that there are plenty questions have been already asked on this. But the solutions provided about delegates is not what I am getting to work with my code because I am working on 4.5 version and looks like the Dispatcher methods have slightly changed. Added to that, I am touching dotnet nearly after a decade and c# for the first time.

What I am actually tying to do in my WPF project is to append text of the textbox inside the main window to display the status/log. This is inside the singleton class called Logger The main static function of this class is log() which accepts a string which needs to be added to the text box with one simple line

txt.AppendText(msg);

After doing some research I changed it to

       Dispatcher.CurrentDispatcher.Invoke(() =>
        {
                txt.AppendText(msg);
        });

But I still get the same error. What is going wrong here? Is there anything else to do? Because I am calling this Logger.log() from FileSystemEventHandler insider another singleton class.

private void somethingChanged(object sender, FileSystemEventArgs e)
{

    Logger.log("File: " + e.FullPath + " " + e.ChangeType);

}

Below is my Logger class

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Controls;
using System.Windows.Threading;
using System.Threading;

namespace httpclientui.comps
{
    class Logger
    {

        private static TextBox txt;
        private Logger() { }

        public static void setup(TextBox t){

            txt = t;

        }
        public static void clear()
        {
            if (txt == null)
            {

                return;

            }
            txt.Clear();
        }
        public static void log(String msg, bool addNewLineChar = true)
        {
            if (txt == null)
            {
                return;
            }
            Dispatcher.CurrentDispatcher.Invoke(() => {
                txt.AppendText(msg);
            });
        }
    }
}

This is my main window with only one textbox control called txtMessage.

    public partial class MainWindow : Window
    {

        private String folderToWatch;
        public MainWindow()
        {
            InitializeComponent();
            initialize();

        }
        private void initialize()
        {
            Logger.setup(txtMessage);
            Logger.clear();
            Logger.log("Launched");

            folderToWatch = "E:\\folder\\subfolder";
            Watcher.Instance.setup(folderToWatch);
            Watcher.Instance.start();

        }

    }
}

Below is my Watcher class which has FileSystemWatcher object to keep watching one of the folder and whenever something happens inside that folder like adding/deleting files and renaming the files will needs to be logged inside that textbox.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.IO;

namespace httpclientui.comps
{
    class Watcher
    {
        private String path;
        //https://msdn.microsoft.com/en-us/library/system.io.filesystemwatcher(v=vs.110).aspx
        private FileSystemWatcher dog;
        private static Watcher instance;
        private Watcher()
        {
            //dog = new FileSystemWatcher()
        }
        public static Watcher Instance
        {
            get
            {
                if (instance == null)
                {
                    instance = new Watcher();
                }
                return instance;
            }
        }
        public void setup(String folderPath)
        {
            path = folderPath;
            dog = new FileSystemWatcher(folderPath);
            Logger.log("setting up to watch folder: " + folderPath);
            dog.Changed += new FileSystemEventHandler(somethingChanged);
            dog.Created += new FileSystemEventHandler(somethingChanged);
            dog.Deleted += new FileSystemEventHandler(somethingChanged);
            dog.Renamed += new RenamedEventHandler(somethingRenamed);

        }
        public void start()
        {
            Logger.log("starting to watch folder");
            dog.EnableRaisingEvents = true;
        }
        public void pause()
        {
            dog.EnableRaisingEvents = false;
        }
        public void stop()
        {
            dog.EnableRaisingEvents = false;
        }
        private void somethingChanged(object sender, FileSystemEventArgs e)
        {

            Logger.log("File: " + e.FullPath + " " + e.ChangeType);

        }

        private void somethingRenamed(object sender, RenamedEventArgs e)
        {

            Logger.log("File: " + e.OldFullPath + " renamed to " + e.FullPath);

        }

    }
}

Solution

  • At first glance, it looks like you are not marshalling the Clear() method call to the Dispatcher. That's what is likely throwing the exception- all access to DispatcherObjects (which includes all Controls) must be done by the Dispatcher thread.

    Also, you want to use Application.Current.Dispatcher, not Dispatcher.CurrentDispatcher. The latter just spins up a new Dispatcher on the current (background) thread, which is not what you want.