dotnet / runtime

.NET is a cross-platform runtime for cloud, mobile, desktop, and IoT apps.
https://docs.microsoft.com/dotnet/core/
MIT License
14.95k stars 4.65k forks source link

`[STAThread]` attribute is ignored when `Main` is asynchronous #73099

Open lostmsu opened 2 years ago

lostmsu commented 2 years ago

Description

When I tried to switch to async Task Main from void Main in a WPF app, I got

System.InvalidOperationException: 'The calling thread must be STA, because many UI components require this.'

My async Task Main method has [STAThread] attribute, but I can see in the debugger, that ApartmentState is MTA, which causes the above exception.

Reproduction Steps

[STAThread]
static async Task<int> Main() {
  Console.WriteLine(Thread.CurrentThread.ApartmentState);
}

Expected behavior

"STA" should be printed

Actual behavior

"MTA" is printed

Regression?

No response

Known Workarounds

I needed STA for WPF, and got it by creating a secondary thread to run WPF App Main function:

var thread = new Thread(SaveApp.Main) {
    Name = "UI",
};
thread.SetApartmentState(ApartmentState.STA);
thread.Start();
thread.Join();

Configuration

.NET 6

Other information

No response

ghost commented 2 years ago

Tagging subscribers to this area: @mangod9 See info in area-owners.md if you want to be subscribed.

Issue Details
### Description When I tried to switch to `async Task Main` from `void Main` in a WPF app, I got > System.InvalidOperationException: 'The calling thread must be STA, because many UI components require this.' My `async Task Main` method has `[STAThread]` attribute, but I can see in the debugger, that `ApartmentState` is MTA, which causes the above exception. ### Reproduction Steps ```csharp [STAThread] static async Task Main() { Console.WriteLine(Thread.CurrentThread.ApartmentState); } ``` ### Expected behavior "STA" should be printed ### Actual behavior "MTA" is printed ### Regression? _No response_ ### Known Workarounds I needed STA for WPF, and got it by creating a secondary thread to run WPF App `Main` function: ```csharp var thread = new Thread(SaveApp.Main) { Name = "UI", }; thread.SetApartmentState(ApartmentState.STA); thread.Start(); thread.Join(); ``` ### Configuration .NET 6 ### Other information _No response_
Author: lostmsu
Assignees: -
Labels: `area-System.Threading`
Milestone: -
jkotas commented 2 years ago

As discussed in https://github.com/dotnet/csharplang/issues/97, this is expected behavior.

Async Main does not really work well for STAThread anyway. You are guaranteed to be on the STAThread only until the first await. So it is best to not use async Main for UI apps that need to run on STAThread.

Known Workarounds I needed STA for WPF, and got it by creating a secondary thread to run WPF App Main function:

It is cheaper to reset the apartment state on the current thread.

Thread.CurrentThread.SetApartmentState(ApartmentState.Unknown); Thread.CurrentThread.SetApartmentState(ApartmentState.STA);

lostmsu commented 2 years ago

Async Main does not really work well for STAThread anyway. You are guaranteed to be on the STAThread only until the first await.

I understood that. My exact scenario is a GUI app, that can be called with command line arguments to perform utility actions (call self with elevated privileges to perform admin tasks). With no arguments app goes straight to WpfApp.Main so the startup thread is used, but with arguments WPF (which requires STA) is never touched, instead it performs commands that have async implementations.

It is cheaper to reset the apartment state on the current thread.

That may be, but the official documentation for SetApartmentState says

Sets the apartment state of a thread before it is started.