在异步方法中获取剪贴板文本

use*_*245 0 c# clipboard asynchronous winforms

我在通过异步方法获取剪贴板文本时遇到问题。因为它总是返回一个空值(虽然它不为空)。这是问题的简单演示:

    private async void button_Click(object sender, EventArgs e)
    {

        string result = await Task<string>.Run(() =>
        {
            System.Threading.Thread.Sleep(3000);

            return Clipboard.GetText(); //returns empty! (but clipboard is not empty)

        });

        MessageBox.Show(result);
    }
Run Code Online (Sandbox Code Playgroud)

我确信剪贴板不为空。解决办法是什么?

小智 5

它不起作用,因为剪贴板仅在 是 而您的公寓是COM threading model (apartment)时才起作用。您无法更改任务的单元,但可以使用线程代替。线程有一个方法。STATaskMTASetApartmentState

STA 和 MTA 的解释请参见此处

但我找到了创建 STA 任务的解决方案

技巧是使用任务运行 STA 线程:

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

所以现在你可以让它像这样工作:

private async void button1_Click(object sender, EventArgs e)
{
    var result = await StartSTATask(() =>
    {
        Thread.Sleep(3000);
        return Clipboard.GetText();
    });
    MessageBox.Show(result);
}
Run Code Online (Sandbox Code Playgroud)

  • @user11448245是的,“Task.Run”将提供的委托卸载到“ThreadPool”线程,这是一个可重用线程的小型池。因此,如果您连续“await Task.Run(() =&gt; Something())”1,000 次,则只会使用少数不同的线程(很可能是一两个)。相反,“StartSTATask”方法每次都会创建一个新线程,该线程在委托完成时被销毁。如果您只想执行几行代码,那么这是非常浪费的。另一个区别:“ThreadPool”线程是后台线程(“IsBackground = true”)。 (2认同)