Introduction
I was disappointed to discover nothing in the .NET Framework that generated events when network interfaces became active or inactive.
If you are writing a program that requires an active network connection, it can be very useful to be able to respond to changes in the available networks.
Background
The .NET Framework does give some useful information about the network interfaces connected to the system in the System.Net.NetworkInformation
namespace.
Most notably, the NetworkInterface
class. This class contains a static method GetAllNetworkInterfaces()
, that returns an array of NetworkInterface
objects,
populated with information about the available network adapters on your machine.
Note that repeated calls to the GetAlllNetworkInterfaces()
method always returns references to the same classes.
Thus if you keep a reference to a network interface, then call GetAllNetworkInterfaces()
and find the same adapter, and compare the operational-status properties
of the two; even if the network adapter connected or disconnected between the two calls, the operational status properties will be identical, because you are comparing the same object.
So to be able to test the difference between two calls to the GetAllNetworkInterfaces()
method, another object will need to be populated with the values
of the properties from the first call. This object can then be compared to the current values and a difference can be detected.
To be able to raise events when a network interface changes, we need a task that is repeatedly recording the results of GetAllNetworkInterfaces()
,
waiting a defined time, then comparing the current results of GetAllNetworkInterfaces()
, and raising the appropriate events.
Using the code
The attached project defines two important classes: NetworkStatus
and NetworkStatusMonitor
.
NetworkStatus
This serves as a record of the network-interface status(es) for the local machine at the time it was constructed; it keeps its own copies of the NetworkInterface
class' properties.
It defines an inner-class that is the actual record of a network interface status, and keeps a collection of these classes.
public class NiStatusRecord
{
public NiStatusRecord(NetworkInterface ni)
{
Interface = ni;
OperationalStatus = ni.OperationalStatus;
Type = ni.NetworkInterfaceType;
Speed = ni.Speed;
}
public NetworkInterface Interface { get; set; }
public OperationalStatus OperationalStatus { get; set; }
public NetworkInterfaceType Type { get; set; }
public long Speed { get; set; }
}
The NetworkStatus
class also implements a number of methods to compare itself against another instance of the NetworkStatus
class.
These return IEnumerable
s that the network status monitor class uses to figure out which interfaces have changed:
public IEnumerable<NetworkInterface> Connected(NetworkStatus lastStatus)
{
foreach (var pair in _status)
{
if (lastStatus._status.ContainsKey(pair.Key))
{
if (lastStatus._status[pair.Key].OperationalStatus != OperationalStatus.Up &&
pair.Value.OperationalStatus == OperationalStatus.Up)
yield return pair.Value.Interface;
}
else
{
if (pair.Value.OperationalStatus == OperationalStatus.Up)
yield return pair.Value.Interface;
}
}
}
The NetworkStatusMonitor
class runs a looping method (in its own thread so it doesn't block the rest of the application) that stores a new instance
of the NetworkStatus
class, waits a predefined number of milliseconds, then generates a new instance of the NetworkStatus
class,
and runs the comparison methods to identify any adapters that have connected, disconnected etc.
This is the monitor-task method:
private void MonitorTask()
{
while (_run)
{
try
{
if (_last == null)
{
_last = new NetworkStatus();
Thread.Sleep(_waitInterval);
continue;
}
else
{
NetworkStatus current = new NetworkStatus();
if (NetworkInterfaceConnected != null && _monitorNewConnections)
{
foreach (var ni in current.Connected(_last))
{
OperationalStatus lastStatus = OperationalStatus.NotPresent;
if (_last.Contains(ni.Id))
lastStatus = _last[ni.Id].OperationalStatus;
NetworkInterfaceConnected(this, new StatusMonitorEventArgs()
{
EventType = StatusMonitorEventType.Connected,
Interface = ni,
LastOperationalStatus = lastStatus
});
}
}
if (NetworkInterfaceDisconnected != null && _monitorDisconnections)
{
foreach (var ni in current.Disconnected(_last))
{
NetworkInterfaceDisconnected(this, new StatusMonitorEventArgs()
{
EventType = StatusMonitorEventType.Disconnected,
Interface = ni,
LastOperationalStatus = OperationalStatus.Up
});
}
}
if (NetworkInterfaceChanged != null && _monitorAnyStatusChange)
{
foreach (var ni in current.Changed(_last))
{
OperationalStatus lastStatus = OperationalStatus.NotPresent;
if (_last.Contains(ni.Id))
lastStatus = _last[ni.Id].OperationalStatus;
NetworkInterfaceChanged(this, new StatusMonitorEventArgs()
{
EventType = StatusMonitorEventType.Changed,
Interface = ni,
LastOperationalStatus = lastStatus
});
}
}
_last = current;
if (_run)
Thread.Sleep(_waitInterval);
}
lock (_pulse)
Monitor.PulseAll(_pulse);
}
catch (Exception exception)
{
Console.WriteLine(exception.ToString());
Interlocked.Increment(ref _exceptionCount);
}
}
}
The main method of the program class shows how this is put together and started up as a console application that writes messages each time a network interface connects or disconnects.
History
V1.0.