【问题标题】:set clipboard in async method在异步方法中设置剪贴板
【发布时间】:2019-06-24 12:39:05
【问题描述】:
[STAThread]
static void Main(string[] args)
{
    DoThing().Wait();
}

static async Task DoThing()
{
    Clipboard.SetText("hi");
}

我首先添加了[STAThread],因为我收到了这个错误

ThreadStateException: 当前线程必须设置为单线程单元 (STA) 模式才能进行 OLE 调用

但我仍然遇到同样的错误。

剪贴板来自 System.Windows.Forms。

如何通过该异步方法设置剪贴板?

【问题讨论】:

    标签: c# .net ole


    【解决方案1】:

    问题是异步线程是从线程池运行的,它们都是 MTA 线程。 Task.Run() 还创建 MTA 线程。

    您必须显式启动 STA 线程才能运行代码。这是一个示例帮助器类:

    public static class STATask
    {
        /// <summary>
        /// Similar to Task.Run(), except this creates a task that runs on a thread
        /// in an STA apartment rather than Task's MTA apartment.
        /// </summary>
        /// <typeparam name="TResult">The return type of the task.</typeparam>
        /// <param name="function">The work to execute asynchronously.</param>
        /// <returns>A task object that represents the work queued to execute on an STA thread.</returns>
    
        public static Task<TResult> Run<TResult>([NotNull] Func<TResult> function)
        {
            var tcs = new TaskCompletionSource<TResult>();
    
            var thread = new Thread(() =>
            {
                try
                {
                    // Most usages will require a message pump, which can be
                    // started by calling Application.Run() at an appropriate point.
    
                    tcs.SetResult(function());
                }
    
                catch (Exception e)
                {
                    tcs.SetException(e);
                }
            });
    
            thread.SetApartmentState(ApartmentState.STA);
            thread.Start();
    
            return tcs.Task;
        }
    
        /// <summary>
        /// Similar to Task.Run(), except this creates a task that runs on a thread
        /// in an STA apartment rather than Task's MTA apartment.
        /// </summary>
        /// <param name="action">The work to execute asynchronously.</param>
        /// <returns>A task object that represents the work queued to execute on an STA thread.</returns>
    
        public static Task Run([NotNull] Action action)
        {
            var tcs = new TaskCompletionSource<object>(); // Return type is irrelevant for an Action.
    
            var thread = new Thread(() =>
            {
                try
                {
                    action();
                    tcs.SetResult(null); // Irrelevant.
                }
    
                catch (Exception e)
                {
                    tcs.SetException(e);
                }
            });
    
            thread.SetApartmentState(ApartmentState.STA);
            thread.Start();
    
            return tcs.Task;
        }
    }
    

    然后你可以像这样实现DoThing()

    static async Task DoThing()
    {
        await STATask.Run(() => Clipboard.SetText("hi"));
    }
    

    请注意,正如 Stephen Cleary 所指出的,通常您需要一个用于 STA 线程的消息泵。如果您只是设置剪贴板文本,您似乎可以摆脱这种情况,但对于更复杂的事情,您可能必须在线程中运行消息泵。

    运行消息泵最简单的方法是调用Application.Run(),但您必须自己处理应用程序上下文。

    【讨论】:

    • 有没有办法在不重写 DoThing 的情况下做到这一点?我可以改变我的称呼吗?
    • 嗯,你的 DoThing() 有问题,因为你没有等待任何东西(这意味着它同步运行),但你可以这样称呼它:await STATask.Run(() =&gt; DoThing().Wait()); 或只是 await STATask.Run(DoThing);跨度>
    • 是的,我刚刚创建了一个小代码示例,在我的实际程序中有awaits。
    • 我猜可能是这样。
    • 认为这对于Clipboard 来说可以,但是in the general case STA threads must pump messages
    猜你喜欢
    • 2021-08-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-05-15
    • 1970-01-01
    相关资源
    最近更新 更多