Windows Forms entry point is not marked STAThread

ID

vbnet.correctness.winforms_entry_point_sta_thread

Severity

critical

Remediation Complexity

trivial

Remediation Risk

low

Remediation Effort

low

Resource

Concurrency

Language

VB.NET

Description

Reports a Main entry point that starts a Windows Forms message loop with Application.Run - written fully qualified, or unqualified whether the namespace is imported in the file or, as is usual in VB.NET, at project level in the .vbproj - and is not marked <STAThread> or <STAThreadAttribute>. Both entry-point forms are covered, Sub Main and Function Main returning an exit code, with or without a command-line argument array. An entry point marked <MTAThread> is reported: it selects the apartment that Windows Forms cannot use.

Rationale

The apartment state of a thread is decided when the thread first needs one and cannot be changed afterwards - Thread.SetApartmentState on a thread that has already started raises ThreadStateException - so for the main thread the attribute on Main is the only place the decision can be made. There is no run-time recovery and no configuration switch. What makes the omission dangerous is that the application still starts: forms load, controls paint, and ordinary event handling works exactly as it should, because only the parts of Windows Forms that sit on single-threaded COM need the apartment. The clipboard, drag and drop, the common file, folder, colour and print dialogs, and any hosted OLE or ActiveX control are the parts that break, typically as a ThreadStateException or a dialog that never returns. Each of those is reached by a user action rather than by start-up, so a smoke test passes, the build ships, and the failure is first reported from a real workstation by whoever tried to print or attach a file.

The following code illustrates the pattern detected by this rule:

Module ShellProgram

    ' FLAGGED: Windows Forms entry point is not marked STAThread
    Sub Main()
        Application.EnableVisualStyles()
        Application.SetCompatibleTextRenderingDefault(False)
        Application.Run(New MainForm())
    End Sub

Remediation

Mark the entry point <STAThread>, and replace <MTAThread> with it where one is present. If the application needs background work in a multi-threaded apartment, do that on a thread the code creates and sets explicitly, leaving the UI thread as STA. Visual Basic projects that use the application framework get an STA entry point generated for them, so a hand-written Sub Main - the shape this rule reports - is where the attribute has to be applied by hand.

' Before: clipboard, drag and drop and the common dialogs fail at run time
Sub Main()
    Application.EnableVisualStyles()
    Application.Run(New MainForm())
End Sub

' After
<STAThread>
Sub Main()
    Application.EnableVisualStyles()
    Application.Run(New MainForm())
End Sub

Configuration

This detector does not need any configuration.