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

Get clipboard text in async method

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

    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);
    }

我确定剪贴​​板不是空的。有什么解决办法?

这应该有效:

private static int counter;

private async void button1_Click(object sender, EventArgs e)
{
    counter++;
    button1.Text = counter.ToString();

    // Solution 1 (preferred)
    await LongOperation();
    Debug.WriteLine("Solution 1 completed for counter " + counter);

    // Solution 2 (use if LongOperation is CPU-bound)
    var t = Task.Run(LongOperation);
    await t;
    Debug.WriteLine("Solution 2 completed for counter " + counter);

    Debug.WriteLine(Clipboard.GetText());
}

private async Task LongOperation()
{
    await Task.Delay(10000);
}

连续点击 button1 3 次。结果:

// Click 3 times in a row on the button. Result:

// After 10 seconds:
Solution 1 completed for counter 3
Solution 1 completed for counter 3
Solution 1 completed for counter 3

// After 10 more seconds:
Solution 2 completed for counter 3
<clipboard content>
Solution 2 completed for counter 3
<clipboard content>
Solution 2 completed for counter 3
<clipboard content>

它不起作用,因为剪贴板仅在 COM threading model (apartment)STA 而您的 Task 公寓为 MTA 时才有效。 您不能更改任务的单元,但可以改用线程。线程有一个 SetApartmentState 方法。

STA and MTA explained here

但我找到了 a solution to create an STA Task !

诀窍是 运行 使用任务的 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;
}

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

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