In a C# console app (.net framework 4.7), I am able to copy text to the clipboad like so:
using System.Windows.Forms;
class Program {
[STAThread]
public static void Main(string[] args] {
Clipboard.SetText("this works");
}
}
But my logic requires Main
to be async
because I will be calling other async methods from main. Once I change it to async, I keep getting an exception everytime I try to access the clipboard.
using System.Windows.Forms;
class Program {
[STAThread]
public static async Task Main(string[] args] {
string text = await GetTextAsync();
// this throws System.Threading.ThreadStateException
Clipboard.SetText(text);
await Task.Run(()=>Clipboard.SetText("this throws too"));
await Task.Run([STAThread]()=>Clipboard.SetText("this doesn't compile"));
}
[STAThread] // this attribute doesn't change anything
public static async Task<string> GetTextAsync() {
// fetching data from database that takes too much time
// for simplicity, assume that the string value is ready
// after one second
await Task.Delay(1000);
return "value fetched from database";
}
}
How can I put text in the clipboard from within an Async method?
I already tried the suggested answer from comments: https://stackoverflow.com/a/56737049/14171304
However, the code won't compile because of [NotNull]
attribute not being recognized. When I remove it, There is another problem preventing compilation:
The type arguments for method STATask.Run(Func) cannot be inferred from the usage. Try specifying the type arguments explicitly.
I got several suggestions from the comments to my question (by @dr.null and @Dai), and I finally managed to have a working solution to my problem.
The answer in set clipboard in async method contained 2 solutions. Only the second one was working for me. I was about to close or delete the question, but decided to post this answer to remove any confusion caused by the answer in that question.
public static class STATask {
public static Task Run(Action action) {
var tcs = new TaskCompletionSource<ojbect>();
var thread = new Thread(() => {
try {
action();
tcs.SetResult(null);
} catch (Exception e) {
tcs.SetException(e);
}
thread.SetApartmentState(ApartmentState.STA);
thread.Start();
return tcs.Task;
});
}
}
Now from my Main method, I simply utilize the STATask by:
public static async Task Main() {
string text = await GetTextAsync();
await STATask.Run(() => Clipboard.SetText(text));
}
The solution above doesn't even require to use the attribute [STAThread]
anywhere.