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