How to handle {STAThread] in C# 9 Using Top-Level Program.cs

Viewed 1041

I am just getting into C# 9 and am trying to implement top-level statements, in particular in the ubiquitous Program.cs. I did this successfully in one case, but in the second case the application gets a ThreadStateException in OpenFileDialog().

I replaced the generated Program.cs

using System;
using System.Windows.Forms;

namespace MapLines {
    static class Program {
        /// <summary>
        ///  The main entry point for the application.
        /// </summary>
        [STAThread]
        static void Main() {
            Application.SetHighDpiMode(HighDpiMode.SystemAware);
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);
            Application.Run(new MainForm());
        }
    }
}

with

using System.Windows.Forms;
using MapLines;

Application.SetHighDpiMode(HighDpiMode.SystemAware);
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new MainForm());

This is the exception in OpenFileDialog

System.Threading.ThreadStateException
  HResult=0x80131520
  Message=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. This exception is only raised if a debugger is attached to the process.
  Source=System.Windows.Forms
  StackTrace:
   at System.Windows.Forms.FileDialog.RunDialog(IntPtr hWndOwner)
   at System.Windows.Forms.CommonDialog.ShowDialog(IWin32Window owner)
   at System.Windows.Forms.CommonDialog.ShowDialog()
   at MapLines.MainForm.OnOpenImageClick(Object sender, EventArgs e) in C:\Users\evans\Documents\Visual Studio 2019\Projects\Map Lines\Map Lines\MainForm.cs:line 664
   at System.Windows.Forms.ToolStripItem.RaiseEvent(Object key, EventArgs e)
   at System.Windows.Forms.ToolStripMenuItem.OnClick(EventArgs e)
   at System.Windows.Forms.ToolStripItem.HandleClick(EventArgs e)
   at System.Windows.Forms.ToolStripItem.HandleMouseUp(MouseEventArgs e)
   at System.Windows.Forms.ToolStrip.OnMouseUp(MouseEventArgs mea)
   at System.Windows.Forms.ToolStripDropDown.OnMouseUp(MouseEventArgs mea)
   at System.Windows.Forms.Control.WmMouseUp(Message& m, MouseButtons button, Int32 clicks)
   at System.Windows.Forms.Control.WndProc(Message& m)
   at System.Windows.Forms.ScrollableControl.WndProc(Message& m)
   at System.Windows.Forms.ToolStrip.WndProc(Message& m)
   at System.Windows.Forms.ToolStripDropDown.WndProc(Message& m)
   at System.Windows.Forms.Control.ControlNativeWindow.WndProc(Message& m)
   at System.Windows.Forms.NativeWindow.Callback(IntPtr hWnd, WM msg, IntPtr wparam, IntPtr lparam)

Going back to the original Program.cs, which has [STAThread], fixes it.

None of the articles on the new features of C# 9 that I have found mention this. It seems important since there must be a lot of applications that use OpenFileDialog() and presumably others. It is my understanding Winforms needs Single Threaded Apartment (STA) threading. Is there a way to use top-level statements when STA threading is needed?

2 Answers

It's possible, this is the minimal example that seems to run well:

using System.Threading;
using System.Windows.Forms;

// It is unfortunate but we have to set it to Unknown first.
Thread.CurrentThread.SetApartmentState(ApartmentState.Unknown);
Thread.CurrentThread.SetApartmentState(ApartmentState.STA);

Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(defaultValue: false);
Application.SetHighDpiMode(HighDpiMode.PerMonitorV2);

Application.Run(new Form());

Source: this ticket from dotnet/winforms Github: https://github.com/dotnet/winforms/issues/5071

using System.Threading;
using System.Windows.Forms;
using MapLines;

var thread = new Thread(() =>
{
    Application.SetHighDpiMode(HighDpiMode.SystemAware);
    Application.EnableVisualStyles();
    Application.SetCompatibleTextRenderingDefault(false);
    Application.Run(new MainForm());
});
thread.SetApartmentState(ApartmentState.STA);
thread.Start();
Related