在异步方法中设置剪贴板
set clipboard in async method
[STAThread]
static void Main(string[] args)
{
DoThing().Wait();
}
static async Task DoThing()
{
Clipboard.SetText("hi");
}
我在第一位添加了 [STAThread]
,因为我得到了这个错误
ThreadStateException: Current thread must be set to single thread apartment (STA) mode before OLE calls can be made
但我仍然遇到同样的错误。
剪贴板来自 System.Windows.Forms。
如何从该异步方法设置剪贴板?
问题是异步线程 运行 来自线程池,它们都是 MTA 线程。 Task.Run()
还会创建 MTA 线程。
您必须显式启动一个 STA 线程以 运行 代码。这是一个示例助手 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()
:
static async Task DoThing()
{
await STATask.Run(() => Clipboard.SetText("hi"));
}
请注意,正如 Stephen Cleary 所指出的,通常您需要一个用于 STA 线程的消息泵。如果您只是设置剪贴板文本,您似乎能够摆脱这个问题,但对于任何更复杂的事情,您可能必须 运行 线程中的消息泵。
运行 消息泵的最简单方法是通过调用 Application.Run()
,但您必须自己处理应用程序上下文。
[STAThread]
static void Main(string[] args)
{
DoThing().Wait();
}
static async Task DoThing()
{
Clipboard.SetText("hi");
}
我在第一位添加了 [STAThread]
,因为我得到了这个错误
ThreadStateException: Current thread must be set to single thread apartment (STA) mode before OLE calls can be made
但我仍然遇到同样的错误。
剪贴板来自 System.Windows.Forms。
如何从该异步方法设置剪贴板?
问题是异步线程 运行 来自线程池,它们都是 MTA 线程。 Task.Run()
还会创建 MTA 线程。
您必须显式启动一个 STA 线程以 运行 代码。这是一个示例助手 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()
:
static async Task DoThing()
{
await STATask.Run(() => Clipboard.SetText("hi"));
}
请注意,正如 Stephen Cleary 所指出的,通常您需要一个用于 STA 线程的消息泵。如果您只是设置剪贴板文本,您似乎能够摆脱这个问题,但对于任何更复杂的事情,您可能必须 运行 线程中的消息泵。
运行 消息泵的最简单方法是通过调用 Application.Run()
,但您必须自己处理应用程序上下文。