1. Computing

The Form as an Application

What a Windows Form Really Is: Threading

By

Updated January 02, 2012

Visual Studio makes us lazy. All you have to do to create a Windows Form application is select the template and Visual Studio does all the technical work of making it run. But there's a lot going on behind the scenes. This article explores just what that Windows Form application really is.

-------

If you reached this page using a search for threading, you might want to check out all of the articles here on that topic. A menu of all articles is available in the top of the first article of the series:

Introduction to Threading and VB.NET

-------

None of this is very easy to see today. When .NET first replaced VB6, now a full decade ago ("Time flies when you're having fun!"), it was easier to see the code in your project because it was in a #REGION section that you could display. This relative accessibility worked but it was brittle. As Dino Espositio wrote in a DevX article, "Any changes made to the hidden code can potentially break the application or component. Any crash or anomaly in Visual Studio .NET potentially leaves the code in an inconsistent state." When Partial Classes were added in Framework 2.0, the #REGION section wasn't used anymore and the compiler creates the full class from at least one declarative class and an action class. The main reason Microsoft made this change was to allow WPF and ASP.NET to use different partial class creation technologies and provide more flexibility.

This makes a big difference when you start trying to build systems with multiple threads (See my introduction article on Threads here.), use different application contexts, or just understand how your system actually works.

To begin, lets look at an elementary default Windows Form application. You build your "action code" in a file called Form1.vb but Visual Studio constructs another partial class with the same name. These two are put together by the compiler to form the complete Form1 class that is the entry form for the application.

--------
Click Here to display the illustration
--------

A significant difference with C# is that C# has a "program.cs" file that actually shows the application being started but VB doesn't. Here's the C# code.


static void Main()
{
...
    Application.Run(new Form1());
}

Equivalent code is created in VB but you won't be able to find it in the code that Visual Studio shows to you. (Even though Microsoft pages will often refer to it! I hate it when they do that!)

The documentation for Application.Run states that it, "Begins running a standard application message loop on the current thread, with an ApplicationContext."

You probably know that Windows applications are event driven. That means that a Windows app waits for the system to pass input to it and then reacts accordingly. The input that is passed (which triggers events) is in the form of messages. A message consists of four parameters: a window handle, a message identifier, and two values called message parameters. Every window has a unique identification number called a handle and the window handle lets the window know it is the target of the message. A message identifier is a predefined constant value tells the window what to do with the message. For example, the message identifier WM_PAINT tells the window to repaint the window's client area. Message parameters can do different things, depending on the message. (You might also see the term, "message pump". It's the same thing as "message loop".)

A key concept for Windows Form applications is that they run in a thread model called apartment threading. Basically, this means that each thread is like an apartment. An object in a thread lives only in that thread and is unaware of objects in other threads. By default, the primary thread for an application created with VB.NET will initialize to an single threaded apartment thread. In fact, Windows Forms is not supported within a multithreaded or free threaded apartment. When you compile a Windows Forms application or a console application, the Visual Basic .NET compiler automatically adds the something called the STAThread attribute to the Main method that serves as the application's entry point. The syntax for the STAThread attribute is coded this way:


Class aWindowsApp
    <System.STAThread()> Shared Sub aSub()
        ' application code
    End Sub
End Class

You can create different kinds of threading models, but you should know what your doing. An example is shown below where an MTAThread apartment model is used.

You might think that you can create a new thread using the same Application.Run statement that the compiler uses. But if you try to use a statement like ...


Application.Run(new Form())

... in a VB Windows Form application, it compiles but it crashes during execution.

--------
Click Here to display the illustration
--------

The message is that, "Starting a second message loop on a single thread is not a valid operation." The Application object is in the System.Windows.Forms namespace and always starts a message loop. Only one can be active at a time. (If several threads were all trying to process the same messages, it would be a disaster.)

But the rules are completely different in a console application. You can execute this code multiple times (if you add a reference to System.Windows.Forms first) and you can create forms at will!


Imports System.Windows.Forms
Module aModule
    Sub aSub()
        Application.Run(New Form())
        Application.Run(New Form())
        Application.Run(New Form())
    End Sub
End Module

The (empty) Form objects appear only after the previous one is closed. Because a console app is single threaded too! You can run new Form objects because the console app doesn't start a message loop. This is important to know if you want your app to communicate with Windows. It means that you have to code the communication with Windows messages yourself in this type of app.

To execute all three, you have to use MTAThread. Here's an example showing how that would be coded:


Imports System.Windows.Forms
Module aModule
    <System.MTAThread()> Sub Main()
        Dim aThread As New Threading.Thread(
            AddressOf Work.aSub)
        aThread.Start()
        Dim bThread As New Threading.Thread(
            AddressOf Work.bSub)
        bThread.Start()
        Dim cThread As New Threading.Thread(
            AddressOf Work.cSub)
        cThread.Start()
    End Sub
    Public Class Work
        Shared Sub aSub()
            Application.Run(New Form())
        End Sub
        Shared Sub bSub()
            Application.Run(New Form())
        End Sub
        Shared Sub cSub()
            Application.Run(New Form())
        End Sub
    End Class
End Module

Now, when you run the console app, all three forms pop open together!

--------
Click Here to display the illustration
--------

Notice, however, that the Form objects are completely empty. Visual Studio isn't doing a thing for you now. You have to code every property and event yourself.

The next article in this series: Joining Threads continues the discussion of threading by showing how to use Join to return control to the Main thread.

  1. About.com
  2. Computing
  3. Visual Basic
  4. Using VB.NET
  5. The Form as an Application - The Message Pump and Apartment Threads

©2014 About.com. All rights reserved.