问题
[STAThread]
static void Main(string[] args)
{
DoThing().Wait();
}
static async Task DoThing()
{
Clipboard.SetText("hi");
}
I added [STAThread]
in the first place bc I got this error
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.
Clipboard is from System.Windows.Forms.
How do I set the clipboard from that async method?
回答1:
The issue is that async threads are run from the threadpool, and they are all MTA threads. Task.Run()
also creates MTA threads.
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;
}
}
You could then implement DoThing()
like this:
static async Task DoThing()
{
await STATask.Run(() => Clipboard.SetText("hi"));
}
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.
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.
来源:https://stackoverflow.com/questions/56736803/set-clipboard-in-async-method