Set ApartmentState on a Task

前端 未结 6 741
轮回少年
轮回少年 2020-11-27 03:02

I am trying to set the apartment state on a task but see no option in doing this. Is there a way to do this using a Task?

for (int i = 0; i < zom.Count; i         


        
相关标签:
6条回答
  • 2020-11-27 03:04

    You could for example make a new task as follows:

           try
            {
                Task reportTask = Task.Factory.StartNew(
                    () =>
                    {
                        Report report = new Report(this._manager);
                        report.ExporterPDF();
                    }
                    , CancellationToken.None
                    , TaskCreationOptions.None
                    , TaskScheduler.FromCurrentSynchronizationContext()
                    );
    
                reportTask.Wait();
            }
            catch (AggregateException ex)
            {
                foreach(var exception in ex.InnerExceptions)
                {
                    throw ex.InnerException;
                }
            }
    
    0 讨论(0)
  • 2020-11-27 03:05

    This is what I'm using with Action since I don't need to return anything:

    public static class TaskUtil
    {
        public static Task StartSTATask(Action action)
        {
            var tcs = new TaskCompletionSource<object>();
            var thread = new Thread(() =>
            {
                try
                {
                    action();
                    tcs.SetResult(new object());
                }
                catch (Exception e)
                {
                    tcs.SetException(e);
                }
            });
            thread.SetApartmentState(ApartmentState.STA);
            thread.Start();
            return tcs.Task;
        }
    }
    

    Where I call it like this:

    TaskUtil.StartSTATask(async () => await RefreshRecords());
    

    For details, please see https://github.com/xunit/xunit/issues/103 and Func vs. Action vs. Predicate

    FYI, this is the exception I was getting where I needed to set the apartment state:

    System.InvalidOperationException occurred HResult=-2146233079
    Message=The calling thread must be STA, because many UI components require this. Source=PresentationCore StackTrace: at System.Windows.Input.InputManager..ctor() at System.Windows.Input.InputManager.GetCurrentInputManagerImpl() at System.Windows.Input.Keyboard.ClearFocus()

    0 讨论(0)
  • 2020-11-27 03:06

    If Task needs to be provided with a CancellationToken, STA thread can be started within a task action and joined as shown below:

    var task = Task.Factory.StartNew(() =>
    {
        var tcs = new TaskCompletionSource<object>();
        var thread = new Thread(() =>
        {
            try
            {
                action();
                tcs.SetResult(new object());
            }
            catch (Exception e)
            {
                tcs.SetException(e);
            }
        });
        thread.SetApartmentState(ApartmentState.STA);
        thread.Start();
        thread.Join();
    }, cancellationToken);
    
    0 讨论(0)
  • 2020-11-27 03:08

    When StartNew fails you just do it yourself:

    public static Task<T> StartSTATask<T>(Func<T> func)
    {
        var tcs = new TaskCompletionSource<T>();
        Thread thread = new Thread(() =>
        {
            try
            {
                tcs.SetResult(func());
            }
            catch (Exception e)
            {
                tcs.SetException(e);
            }
        });
        thread.SetApartmentState(ApartmentState.STA);
        thread.Start();
        return tcs.Task;
    }
    

    (You can create one for Task that will look almost identical, or add overloads for some of the various options that StartNew has.)

    0 讨论(0)
  • 2020-11-27 03:14

    This is a good use case for the Task constructor, and the RunSynchronously method.

    public static Task<T> RunSTATask<T>(Func<T> function)
    {
        var task = new Task<T>(function, TaskCreationOptions.DenyChildAttach);
        var thread = new Thread(task.RunSynchronously);
        thread.IsBackground = true;
        thread.SetApartmentState(ApartmentState.STA);
        thread.Start();
        return task;
    }
    

    The purpose of TaskCreationOptions.DenyChildAttach is to make the resulting task behave identically to the Servy's solution (attaching a child task to a parent TaskCompletionSource.Task is not possible). Denying children to be attached is also the behavior of the Task.Run method.

    0 讨论(0)
  • 2020-11-27 03:19

    An overload of Servy's answer to start a void Task

    public static Task StartSTATask(Action func)
    {
        var tcs = new TaskCompletionSource<object>();
        var thread = new Thread(() =>
        {
            try
            {
                func();
                tcs.SetResult(null);
            }
            catch (Exception e)
            {
                tcs.SetException(e);
            }
        });
        thread.SetApartmentState(ApartmentState.STA);
        thread.Start();
        return tcs.Task;
    }
    
    0 讨论(0)
提交回复
热议问题