tabs ↹ over ␣ ␣ ␣ spaces

by Jiří {x2} Činčura

STAThread and async Main gotcha

15 Nov 2022 2 mins .NET, C#, WinForms

This took me quite a while to debug, because I was constantly wrongly assuming my threading and synchronization context handling was wrong. At the end of the day it was very simple, I just couldn’t see the forest for the trees.

I have a gutted out WinForms application, no forms, just manually handling bunch of stuff via ApplicationContext. For example, something like this.

[STAThread]
static void Main()
{
	ApplicationConfiguration.Initialize();
	Application.Run(new MyApplicationContext());
}

Which is pretty standard. But at one point I had to do some “asynchronous initialization” and switched to this (again, an example).

[STAThread]
static async Task Main()
{
	ApplicationConfiguration.Initialize();
	await foo.InitializeAsync();
	Application.Run(new MyApplicationContext());
}

And suddenly everything started to fell apart. I.e: System.Threading.ThreadStateException: 'Current thread must be set to single thread apartment (STA) mode before OLE calls can be made. Ensure that your Main function has STAThreadAttribute marked on it.'.

As I mentioned at the beginning, I immediately jumped into conclusion, that I’m doing something wrong regarding my threading and synchronization context. But as I was digging into it, everything seemed correct. But I kept digging. Oh, boy. Luckily, I realized what the issue was.

The async Main is no magic. In fact I blogged about it roughly 5 years ago. The end code is more or less this.

static void Main() => OldMain().GetAwaiter().GetResult();

[STAThread]
static async Task OldMain()
{
	ApplicationConfiguration.Initialize();
	await Foo.InitializeAsync();
	Application.Run(new MyApplicationContext());
}

Now the STAThread attribute is no longer on real Main, but on some “random” method.

With that, I simply manually created Main method that would be otherwise created by compiler and put STAThread attribute there.

Job done. But what a journey!

Profile Picture Jiří Činčura is .NET, C# and Firebird expert. He's focused on data and business layers, language constructs, parallelism, databases and performance. Speaker and Microsoft Most Valuable Professional. You can read his articles, guides and tips and tricks at www.tabsoverspaces.com.