Search code examples
c#winui-3winuiwindows-app-sdk

'The application called an interface that was marshalled for a different thread


I try to create a thread and manipulate UI controls inside it, then I recived an exception System.Runtime.InteropServices.COMException: 'The application called an interface that was marshalled for a different thread.
Through searching, I found this is a very common problem, here is one way I have found

Windows.ApplicationModel.Core.CoreApplication.MainView.CoreWindow.Dispatcher.RunAsync(CoreDispatcherPriority.Normal,
() =>
    {
        // Your UI update code goes here!
    }
);

But this cause another exception
System.InvalidOperationException: 'A method was called at an unexpected time.

I don't know the correct way

private void MessageReceived(string message_content)
{
    InvertedListView.Items.Add(new Message(message_content, DateTime.Now,HorizontalAlignment.Left));
}

static bool flag = true;

Thread thread = new Thread(() =>
{
    FileSystemWatcher watcher = new FileSystemWatcher();
    watcher.Path = "C:\\Users";
    watcher.Filter = "text.txt";
    watcher.NotifyFilter = NotifyFilters.LastWrite;
    watcher.Changed += OnChanged;
    Debug.WriteLine("START!");
    watcher.EnableRaisingEvents = true;

    void OnChanged(object sender, FileSystemEventArgs e)
    {
        if (flag)
        {
            Debug.WriteLine("OnChanged");
            Thread.Sleep(10);
            var lastLine = File.ReadLines(watcher.Path + watcher.Filter, Encoding.UTF8).Last();
            MessageReceived(lastline); // The exception happened here
            Debug.WriteLine(lastLine);
            flag = false;
        }
        else
        {
            flag = true;
        }
    }
});

Solution

  • To create/change an UI control, you need to do it on the UI thread. Let me give you a simple example:

    private DispatcherQueue _dispatcherQueue;
    
    public MainPage()
    {
        this.InitializeComponent();
        this.Loaded += MainPage_Loaded;
        _dispatcherQueue = DispatcherQueue.GetForCurrentThread();
    }
    
    private async void MainPage_Loaded(object sender, Microsoft.UI.Xaml.RoutedEventArgs e)
    {
        // This works because this event handler is called on the UI thread.
        Button button = new();
    
        // This throws an exception because the button is not created on the UI thread.
        await Task.Run(() =>
        {
            Button button = new();
        });
    
        // This works because the DispatcherQueue is used to create the button on the UI thread.
        this.DispatcherQueue.TryEnqueue(() =>
        {
            Button button = new();
        });
    
        // This works because the DispatcherQueue is used to create the button on the UI thread.
        _dispatcherQueue.TryEnqueue(() =>
        {
            Button button = new();
        });
    }