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 focuses on data and business layers, language constructs, parallelism, databases and performance. For almost two decades he contributes to open-source, i.e. FirebirdClient. He works as a senior software engineer for Microsoft. Frequent speaker and blogger at www.tabsoverspaces.com.