Export (0) Print
Expand All

Testing for and Responding to Network Connections in the .NET Compact Framework

.NET Compact Framework 1.0
 

Dan Fox, Jon Box
Quilogy

June 2004

Applies to:
   Microsoft® .NET Compact Framework® 1.0
   Microsoft® Visual Studio .NET® 2003

Download sample.

Summary: Learn how to detect network connections from your Microsoft® .NET Compact Framework-based applications. (14 printed pages)

Contents
Introduction
Designing the Component
Checking for Connectivity
Polling for Connectivity
Notifying the Application
Notifying the User
Taking a Test Drive
Summary

Introduction

Although we live in an increasingly inter-networked society Wi-Fi connections are by no means ubiquitous. The affect of users moving into and out of coverage areas whether corporate campus networks or networks available in public spaces such as airports impacts the way in which we developers design our software. In short, this means designing for the occasionally connected application scenario as described in our book Building Solutions for the Microsoft .NET Compact Framework.

A key component of designing for this scenario is the ability of the application to be network aware and respond appropriately to moving into and out of a connected state. For example, a .NET Compact Framework application running on a "ruggadized" Pocket PC might be used to record inspection information as trailers are returned to a rental lot. Although the rental station has installed a wireless access point (WAP) the coverage is not uniform across the entire area of the lot and becomes intermittent as inspectors enter or move in close proximity to the trailers. Of course, even when temporarily disconnected the application still needs to allow the inspector to record information. However, when the connection again is present the application needs to sense the reconnection and then submit any completed work and download new work using web services.

In this whitepaper we'll detail how to create a fully managed .NET Compact Framework networking component packaged in its own assembly that can be used in just such a scenario.

Designing the Component

The above scenario dictates that the networking component we design will need to fulfill the following four requirements.

  • It must periodically check for the presence of a network connection
  • In order to not disrupt the application the component should poll for the network connection on a background thread
  • The component should track what state it is in (connected or disconnected) and raise an event when that state changes
  • If the application requires user interaction in order to synchronize its data the component should also be able to provide a visual cue to the user that the connection has been reestablished

In order to build a networking component that fulfills these simple requirements we can design a public interface like that shown in Table 1 where each member is listed along with how it will be used.

Table 1: The Networking Component Interface

Member Scope Description
EnableRaisingEvents Instance property Boolean used to turn on and off the raising of connection events by the component
Icon Instance property A pointer to the icon used in the notification balloon window
Interval Instance property Time in seconds used to poll for a connection on a background thread
IPAddress Read-only Instance property Returns the current IP address of the smart device
Message Instance property Message displayed in the notification balloon
ShowNotification Instance property Boolean used to turn on and off the balloon notification in response to connection events
Dispose Instance method Method that shuts down the current instance and blocks until the background thread exits. Implemented as part of IDisposable
IsConnected Instance method Boolean that returns the connected state of the smart device
NetworkConnected Instance event Event raised when the smart device is connected or disconnected from the network
Connected Read-only Static property Boolean that returns the connection status

In the remaining sections of this whitepaper we'll walk through the four requirements listed above and show how they can be implemented.

Checking for Connectivity

The first requirement of the networking component is to detect the presence of a network connection. Although this can be done with calls to unmanaged APIs the simplest technique is to use managed code and the classes in the System.Net namespace to determine if the smart device has been assigned an IP address. In the component both the IsConnected instance method and Connected static property use this technique to return True if the device is connected. For example, the static Connected property looks as follows.

[VB]

Public Shared ReadOnly Property Connected() As Boolean
    Get
        Dim ret As Boolean
        Try
            ' Returns the Device Name
            Dim HostName As String = Dns.GetHostName()
            Dim thisHost As IPHostEntry = Dns.GetHostByName(HostName)
            Dim thisIpAddr As String = thisHost.AddressList(0).ToString

            ret = thisIpAddr <> _
              Net.IPAddress.Parse("127.0.0.1").ToString()

        Catch ex As Exception
            Return False
        End Try

        Return ret
    End Get
End Property

Here you can see that the static GetHostName method of the Dns class is used to retrieve the device name which is then passed to the GetHostByName method in order to retrieve the IPHostEntry object for the device. The AddressList property then returns the array of IP Addresses that resolve to the host name. By comparing the first address in the array with the 127.0.0.1, the unassigned address, we can determine whether or not the device has been assigned an address and therefore whether or not the device is connected to a network. Note that if an exception occurs the method simply returns False.

In the IsConnected instance property the same code is used although the return value from the comparison is saved to a private field that tracks the connection state of the component.

Note that the inclusion of both a static and instance property to check for a connection dynamically means that an application can simply invoke these properties without having to use the features discussed in the remainder of this article. For example, by using the static property an application can check for itself whether a connection has been established like so:

[VB]

If Atomic.CF.Network.Connected Then
    ' Connected so now synchronize
End If

Polling for Connectivity

The second requirement is that the component poll for connectivity on a background thread. Because the .NET Compact Framework does not support asynchronous delegates or all of the various methods and properties of the System.Threading.Thread class (notably the IsBackground and IsAlive properties and the Abort method), this is slightly more difficult than it would be when writing the same component in the desktop framework.

The basic technique implemented by the component will be to start a polling thread which loops at a configurable interval, checking for the network connection and testing the returned state against the last known connection state. If the state has changed an event may be fired and the user notified visually. It is important to note that this happens only for instances of the component and not statically.

Note The technique used here of creating an explicit thread to perform the background task is one of several possible techniques as outlined in Jim Wilson's excellent article "Microsoft .NET Compact Framework Background Processing Techniques”.

To setup the polling our component will use the private fields as shown below.

Private _t As Thread  ' Thread to perform the polling
Private _notify As Boolean = False ' Boolean flag to determine when to stop
Private _isRunning As Boolean = False  ' Set when the polling is taking place
Private _interval As Integer = 10 ' Interval to poll at

The thread (_t) will be used to execute the code to do the polling while the _notify flag will be set and returned from EnableRaisingEvents property so that the polling code will know when to stop. The _isRunning flag is used internally to track when the polling thread is active (because the IsAlive property of the Thread object is not supported in the .NET Compact Framework) while the _interval (exposed by the Interval property) determines how often the polling thread will check the network connection.

When the EnableRaisingEvents property is set to True, the private SetupNotification method shown below is called. This method is responsible for checking to see if the background thread is already running. If not, the thread is started at the address of the StartBackground private method.

[VB]

Private Sub SetUpNotification()
    ' If not already checking then start a new thread to check
    If Not _isRunning Then
        _t = New Thread(AddressOf StartBackground)
        _t.Start()
    End If
End Sub

The StartBackground method is where most of the logic is implemented. This method performs the following steps:

  1. Set the _isRunning flag to True to indicate that the background thread is active
  2. Start the loop to poll while the EnableRaisingEvents property is set to True
  3. If the current state is connected then check and see whether or not we are still connected by invoking the IsConnected property. If not then raise the event with a disconnected flag
  4. If the current state is disconnected then check to see whether a connection has been made. If so show a visual cue to the user if the ShowNotification property is set to true (the _showBubble field as discussed in more detail in a later section of this paper), and raise an event with the connected flag
  5. Put the thread to sleep for the for the interval
  6. Cleanup any visual cues left over
  7. Once the loop exits because the EnableRaisingEvents property has been set to False, set the _isRunning flag to False to show that the polling thread is exiting and exit the method

The complete code for the StartBackground method is shown here.

[VB]

Private Sub StartBackground()
    ' Polling thread is executing
     _isRunning = True

    ' While the EnableRaisingEvents property is set continue to poll
    Do While _notify
        If _isConnected Then
            If Not Me.IsConnected Then
                ' Raise the event if we've just been disconnected
                 OnNetworkConnected(_ipAddress, NetworkEventType.Disconnected)
            End If
        Else
            If Me.IsConnected Then
                If _showBubble Then
                    ' Show the notification
                     _notification.Add(_notificationHTML, TITLE, _
                      Convert.ToUInt32(DURATION), _hIcon)
                 End If
                ' Raise the event to show we're connected
                OnNetworkConnected(_ipAddress, NetworkEventType.Connected)
            End If
        End If

        If _notify Then
             ' Put the thread to sleep for the interval
            Thread.Sleep(_interval * 1000)
        End If

        ' Now remove the notification if it hasn't been dismissed already
        _notification.RemoveLast()
    Loop

    _isRunning = False
End Sub

Because the Thread class in the .NET Compact Framework does not support the IsBackground property the polling thread created in the SetupNotification method will be treated like any other thread in the application. Therefore the thread will not automatically be destroyed when the main thread of the application terminates and the application will continue to run until the thread exits. And to compound the problem the Thread class does not support the Abort method which would make it simple to destroy the polling thread programmatically.

As a result, the component needs to make sure the polling thread gets cleaned up in its Dispose method implemented as a part of the IDisposable interface. This method shuts down the current instance by setting the EnableRaisingEvents and ShowNotification properties to False and then waiting until the polling thread exits by checking the _isRunning flag as shown below.

[VB]

Public Sub Dispose() Implements IDisposable.Dispose
    ' Shuts down the current instance and waits for the background thread to die
    ' Will block until the thread exits so the longer the interval, the longer
    ' the app will have to wait to close
    Me.EnableRaisingEvents = False
    Me.ShowNotification = False
    Do While _isRunning
        ' Wait until the thread has updated the isRunning flag
        Thread.CurrentThread.Sleep(250)
    Loop
End Sub

This method will then block the thread on which it is called until the polling thread exits. As a result, an application that uses the component, in order to appear more responsive on shutdown, would need to hide its forms before exiting as in the following example where a menu item is used to exit the application.

[VB]

Private Sub MenuItem1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) _
  Handles MenuItem1.Click
    Me.Hide() ' Would also have to hide other forms
    ' Critical to call the Dispose method to wait for the background thread to stop
    net.Dispose()
    Application.Exit()
End Sub

Notifying the Application

When the polling thread detects a state change in the connection it raises an event. The component defines a public event called NetworkConnected that follows the event pattern document in the .NET Framework SDK. The code to raise the event is encapsulated in the private OnNetworkConnected method implemented as follows.

[VB]

Private Sub OnNetworkConnected(ByVal ipAddress As Net.IPAddress, _
 ByVal reason As NetworkEventType)
   Dim e As New NetworkEventArgs(_ipAddress, reason)
   RaiseEvent NetworkConnected(Me, e)
End Sub

This simple method instantiates a NetworkEventArgs object inherited from EventArgs and passes to its' constructor the IPAddress object that represents the current IP address along with an enumerated type that identifies the reason for the event. Both the NetworkEventArgs class and the NetworkEventType enumerated type are shown here.

[VB]

Public Class NetworkEventArgs : Inherits EventArgs
    Public Sub New()
        EventReason = NetworkEventType.None
    End Sub

    Public Sub New(ByVal ipAdd As Net.IPAddress, ByVal reason As NetworkEventType)
        IPAddress = ipAdd
        EventReason = reason
    End Sub

    Public IPAddress As Net.IPAddress
    Public EventReason As NetworkEventType
End Class

Public Enum NetworkEventType
    None
    Connected
    Disconnected
End Enum

Obviously, the raising of the event is fairly trivial. However, when the event is raised to the application the event handler, which will be executed on the polling thread, must not manipulate the user interface directly. This is the case since the controls on the form are created on the main thread of the application and these controls (which are simply wrappers for the Windows CE controls) have Thread Affinity. Changing the properties of the controls or calling methods on the controls from a thread other than the one they were created on leads to instability and ultimately an application that goes down in flames.

Typically in a .NET Framework application this unpleasantness would be avoided by calling the Invoke method of the Form class from the event handler which would invoke a method running on the main thread that can safely update the user interface. Unfortunately, the Invoke method of the Form class in the .NET Compact Framework does not support the overloaded signature that allows for passing arguments that could be used to refresh the user interface. However, a component such as the Invoker component we outline in chapter 3 (Listing 3-3) of our book Building Solutions with the Microsoft .NET Compact Framework can do the trick.

Using this component the form in the application can instantiate the Invoker object and pass it the address of a method. In the example below the UpdateUI method will be used to update the user interface.

[VB]

' Setup the callback to update the user interface
inv = New Invoker(Me)
UICallback = AddressOf UpdateUI

The Invoke method of the Invoker component then accepts a parameter array of objects that can be used to pass arguments such as the IP address and the reason code from the NetworkEventType enumerated type.

[VB]

Private Sub ConnectedEvent(ByVal sender As Object, ByVal e As NetworkEventArgs)
    inv.Invoke(UICallback, e.IPAddress, e.EventReason)
End Sub

The UpdateUI method of the form can then unpack the array of objects and use the values to update the user interface.

[VB]

Private Sub UpdateUI(ByVal args() As Object)
    ' Show the IP Address and current state
    Label1.Text = args(0).ToString
    Label3.Text = args(1).ToString
End Sub

By handling the NetworkConnected event an application will then be notified when the connection state changes (within the latency set using the Interval property) and take action such as synchronizing pending changes.

Note Since by design applications running on Pocket PCs are not typically closed but simply move to the background when the X is tapped in the upper right-hand corner of the form, you may want your application to detect this fact and stop the polling and notification. Although originally designed with security in mind, the Inactivity class we discuss in chapter 9 of our book (Listing 9-2) can be used for just such a purpose.

Notifying the User

The final requirement for this component is that it provides a visual cue to the user when a connection is reestablished. Although the component could provide the notification simply when the connection state changes, we elected to design it to only show a notification when the connection state changes from disconnected to connected. This is because we don't want the component to clutter the user interface with multiple messages and because we assume that moving from disconnected to connected is more interesting to users since that state change may be used to prompt the user to action, for example to perform a manual synchronization of data collected while offline.

On a Pocket PC device the standard technique for providing these visual cues is to use the notification balloons that are displayed on the title bar as documented on MSDN. Since the .NET Compact Framework does not provide a managed interface to this functionality (yet) we would have needed to write a significant amount of P/Invoke code to call the unmanaged APIs using the techniques outlined in our two previous whitepapers "An Introduction to P/Invoke and Marshalling in the Microsoft .NET" and "Advanced P/Invoke in the Microsoft .NET Compact Framework". Fortunately, this is a task that has been done many times before and so instead of reinvent the wheel we relied on an existing component originally written by Seth Dempsey and Gokhan Altinoren.

Note For those interested a library also exists on the OpenNETCF shared source web site (www.opennetcf.org) that encapsulates this functionality nicely. We did not use the OpenNETCF library because it has several issues that need to be resolved when using it from Visual Basic and because it does not support loading a custom icon when the library is referenced indirectly through a second class library assembly. In total, the assemblies you can download on OpenNETCF are referred to as the Smart Device Framework v1.0. Many of the classes provided here can simplify your life as a .NET Compact Framework developer and so should be consulted before attempting to roll your own.

Our networking component then simply needs to reference the assembly containing the Notification class found in the PocketPC namespace in order to use the functionality.

The networking component then declares the following private fields and constants to interface with the notification system.

[VB]

Private _showBubble As Boolean = False
Private _notification As PocketPC.Notification
Private _message As String
Private _notificationHTML As String
Private _hIcon As IntPtr ' Executable icon
Private Const TITLE As String = "Network Connection Established"
Private Const DURATION As Integer = 3

As mentioned previously, the _showBubble field is exposed by the ShowNotification property while the _notification field references the notification object. The _message property allows the application to customize the message shown to the user in the balloon while the _notificationHTML is the message couched in HTML created when the Message property is populated.

[VB]

Public Property Message() As String
    Get
        Return _message
    End Get
    Set(ByVal Value As String)
        _message = Value
        _notificationHTML = "<html><body>" & _message & _
          "<p><input type=button value=Dismiss name=cmd:202></body><html>"
    End Set
End Property

Notice that the HTML includes a button to close the notification balloon and can even include hyperlinks.

The _hIcon field is a pointer to the icon to display in the notification balloon accepted through the constructor and the Icon instance property, and the two constants are used for the title of the balloon and the duration for which it will be displayed.

When an instance of our networking component is created its initialization code simply instantiates the Notification object

[VB]

_notification = New PocketPC.Notification

As shown previously, when the StartBackground method detects a state change from disconnected to connected the _showBubble field is consulted and if True the notification is added by calling the Add method of the Notification object.

[VB]

If _showBubble Then
    ' Show the notification
    _notification.Add(_notificationHTML, TITLE, _
      Convert.ToUInt32(DURATION), _hIcon)
End If

You'll also notice that the Notification object is later removed by calling the RemoveLast method of the Notification class. When an application sets the EnableRaisingEvents and ShowNotification properties to True the balloon will be shown when a connection is established as shown in Figure 1.

Figure 1. Showing a Visual Notification

Taking a Test Drive

The sample application NetworkConnTest included with the downloadable source code and shown in Figure 2 simply exercises the networking component. The Load event of the Form extracts the icon from the executable using the ExtractIconEx Windows CE API, instantiates the Network component, and sets up the event handler as shown here.

[VB]

Private net As Network

Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) _
 Handles MyBase.Load

    ' Get the icon from the executable
    Dim hIcon As IntPtr
    Dim szPath As String = _
     [Assembly].GetExecutingAssembly().GetModules()(0).FullyQualifiedName
    ExtractIconEx(szPath, 0, 0, hIcon, 1)

    ' Setup the callback to update the user interface as shown above

    ' Initialize the Network component
    net = New Atomic.CF.Network(True, 10, True, _
     "Synchronizing changes for NetworkConnTest", hIcon)
    Label1.Text = net.IPAddress.ToString
    AddHandler net.NetworkConnected, AddressOf ConnectedEvent

    CheckBox1.Checked = net.ShowNotification
    CheckBox2.Checked = net.EnableRaisingEvents
End Sub

You'll notice that the overloaded constructor of the Network component used in this example accepts the EnableRasingEvents flag, the interval at which to poll, the ShowNotification flag, the message to display, and the pointer to the icon.

Figure 2. Testing the Network Component

Summary

Being able to detect the presence or absence of a network connection using a .NET Compact Framework component is valuable in occasionally connected scenarios. Using a managed component such as the one discussed in this whitepaper allows your application to respond with or without user interaction when a connected is established using a polling thread, events, and visual user notifications.

Show:
© 2014 Microsoft