Testing for and Responding to Network Connections in the .NET Compact Framework
Dan Fox, Jon Box
Microsoft® .NET Compact Framework® 1.0
Microsoft® Visual Studio .NET® 2003
Summary: Learn how to detect network connections from your Microsoft® .NET Compact Framework-based applications. (14 printed pages)
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.
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
| || || |
| || || |
| || || |
| || || |
| || || |
| || || |
| || || |
| || || |
| || || |
| || || |
In the remaining sections of this whitepaper we'll walk through the four requirements listed above and show how they can be implemented.
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.
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.
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:
If Atomic.CF.Network.Connected Then ' Connected so now synchronize End If
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
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.
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.
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
StartBackground method is where most of the logic is implemented. This method performs the following steps:
- Set the
_isRunningflag to True to indicate that the background thread is active
- Start the loop to poll while the
EnableRaisingEventsproperty is set to True
- If the current state is connected then check and see whether or not we are still connected by invoking the
IsConnectedproperty. If not then raise the event with a disconnected flag
- 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
ShowNotificationproperty is set to true (the
_showBubblefield as discussed in more detail in a later section of this paper), and raise an event with the connected flag
- Put the thread to sleep for the for the interval
- Cleanup any visual cues left over
- Once the loop exits because the
EnableRaisingEventsproperty has been set to False, set the
_isRunningflag to False to show that the polling thread is exiting and exit the method
The complete code for the
StartBackground method is shown here.
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
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
ShowNotification properties to False and then waiting until the polling thread exits by checking the
_isRunning flag as shown below.
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.
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
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.
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.
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.
' Setup the callback to update the user interface inv = New Invoker(Me) UICallback = AddressOf UpdateUI
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.
Private Sub ConnectedEvent(ByVal sender As Object, ByVal e As NetworkEventArgs) inv.Invoke(UICallback, e.IPAddress, e.EventReason) End Sub
UpdateUI method of the form can then unpack the array of objects and use the values to update the user interface.
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.
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.
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.
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.
_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 = 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
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
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
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.
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
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.