在异步方法中设置剪贴板 [英] set clipboard in async method

查看:114
本文介绍了在异步方法中设置剪贴板的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

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

我首先在错误代码中添加了[STAThread]

I added [STAThread] in the first place bc I got this error

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

ThreadStateException: Current thread must be set to single thread apartment (STA) mode before OLE calls can be made

但是我仍然遇到相同的错误.

But I am still getting the same error.

剪贴板来自System.Windows.Forms.

Clipboard is from System.Windows.Forms.

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

How do I set the clipboard from that async method?

推荐答案

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

The issue is that async threads are run from the threadpool, and they are all MTA threads. Task.Run() also creates MTA threads.

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

You will have to explicitly start an STA thread to run the code. Here's a sample helper class:

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():

You could then implement DoThing() like this:

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

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

Note that, as pointed out by Stephen Cleary, usually you need a message pump for an STA thread. You seem to be able to get away with this if you're just setting the clipboard text, but for anything more complicated you're likely to have to run a message pump in the thread.

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

The easiest way to run the message pump is via a call to Application.Run(), but you will have to handle the application context yourself.

这篇关于在异步方法中设置剪贴板的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆