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

筅森魡賤 提交于 2019-12-22 18:17:41

问题


private void LogInButton_Click(object sender, RoutedEventArgs e)
{
    var api = new RestAPI("http://localhost:2624/", UsernameTextBox.Text, PasswordTextBox.Password);

    api.AutenticarUsuarioFinalizado += (o, args) =>
    {
        ProgressBar.IsIndeterminate = false;
        ProgressBar.Visibility = Visibility.Collapsed;
        LogInButton.IsEnabled = true;

        if (args.Error) return;

        if (args.Resultado.Autenticado)
        {
        }
    };

    api.AutenticarUsuario();
    ProgressBar.Visibility = Visibility.Visible;
    ProgressBar.IsIndeterminate = true;
    LogInButton.IsEnabled = false;
}

api.AutenticarUsuario(); calls a rest API asynchronously, when it's done it calls the event handler api.AutenticarUsuarioFinalizado and got this error in line ProgressBar.IsIndeterminate = false; because the call open a new thread, how can I fix it? the error is:

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


回答1:


The problem is that your event handler doesn't execute on the UI thread. I think the best way to fix that is to convert your EAP (Event-based Asynchronous Pattern) method to TAP (Task-based Asynchronous Pattern) using TaskCompletionSource:

public static Task<Resultado> AutenticarUsuarioAsync(this RestAPI api)
{
    var tcs = new TaskCompletionSource<Resultado>();

    api.AutenticarUsuarioFinalizado += (sender, args) =>
    {
        if (args.Error)
            tcs.TrySetException(new SomeAppropriateException());
        else
            tcs.TrySetResult(args.Resultado);
    };

    api.AutenticarUsuario();

    return tcs.Task;
}

…

private async void LogInButton_Click(object sender, RoutedEventArgs e)
{
    var api = new RestAPI(
        "http://localhost:2624/", UsernameTextBox.Text,
        PasswordTextBox.Password);

    ProgressBar.Visibility = Visibility.Visible;
    ProgressBar.IsIndeterminate = true;
    LogInButton.IsEnabled = false;

    try
    {
        var resultado = await api.AutenticarUsuarioAsync();
        if (resultado.Autenticado)
        {
            // whatever
        }
    }
    catch (SomeAppropriateException ex)
    {
        // handle the exception here
    }
    finally
    {
        ProgressBar.IsIndeterminate = false;
        ProgressBar.Visibility = Visibility.Collapsed;
        LogInButton.IsEnabled = true;
    }
}

Because awaiting a Task will always resume on the original context, you're not going to get an exception this way. As an additional advantage, you don't have to write your code “inside-out”, like you do with EAP.

You should also consider using bindings, instead of setting the properties of your UI controls manually.




回答2:


Your AutenticarUsuarioFinalizado event handler is probably being executed in a thread different from the UI thread. You should only modify/create UI objects from the UI thread. Use a dispatcher in your event handler, check here: Run code on UI thread in WinRT

var dispatcher = Windows.UI.Core.CoreWindow.GetForCurrentThread().Dispatcher;
api.AutenticarUsuarioFinalizado += (o, args) =>
{
   dispatcher.RunAsync(DispatcherPriority.Normal, () => 
       ProgressBar.IsIndeterminate = false;
       ProgressBar.Visibility = Visibility.Collapsed;
   [...]


来源:https://stackoverflow.com/questions/17245512/exception-the-application-called-an-interface-that-was-marshalled-for-a-differe

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!