首页 > 解决方案 > 在异步方法中设置剪贴板

问题描述

[STAThread]
static void Main(string[] args)
{
    DoThing().Wait();
}

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

[STAThread]首先添加了 bc 我得到了这个错误

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

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

剪贴板来自 System.Windows.Forms。

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

标签: c#.netole

解决方案


问题是异步线程是从线程池运行的,它们都是 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(),但您必须自己处理应用程序上下文。


推荐阅读