Search code examples
c#async-awaitstamta

Set ApartmentState for async void main


I have a Windows Forms app.

Now I want to use an async method.

Since C# 7.1 I can use an async Main method:
https://learn.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-7-1

However, now my STAThread attribute is ignored and my app runs in MTA. Is this by design or can I force my app to run in STA mode again?

/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
private static async Task Main(string[] args)
{
    // returns MTA
    Console.WriteLine("{0}", Thread.CurrentThread.ApartmentState);
}

Solution

  • I know this is old, but Damien's answer and the subsequent comment helped me with my issue. I have a console app in which I need to call async methods that at some point may need STA execution to use the OpenFileDialog.

    Here is my resulting code in case it helps others (or just my future self).

    1. Created Extension Method for running thread as STA

    public static class Extensions
    {
        public static void RunSTA(this Thread thread)
        {
            thread.SetApartmentState(ApartmentState.STA); // Configure for STA
            thread.Start(); // Start running STA thread for action
            thread.Join(); // Sync back to running thread
        }
    }
    

    2. Created async main method with await to application method (no [STAThread] attribute).

    class Program
    {
        static async Task Main(string[] args)
        {
            await App.Get().Run(args);
        }
    }
    

    3. Use extension method to wrap OpenFileDialog call with STA

    public string[] GetFilesFromDialog(string filter, bool? restoreDirectory = true, bool? allowMultiSelect = true)
    {
        var results = new string[] { };
        new Thread(() =>
        {
            using (var dialog = new OpenFileDialog())
            {
                dialog.Filter = filter;
                dialog.RestoreDirectory = restoreDirectory ?? true;
                dialog.Multiselect = allowMultiSelect ?? true;
                if (dialog.ShowDialog() != DialogResult.OK)
                    return; // Nothing selected
                results = dialog.FileNames;
            }
        }).RunSTA();
        return results;
    }