Network Monitor for Windows 7

If you are using Windows 7 you may have noticed that Microsoft decided to remove the Network Activity icon from the tray area and instead, substituted it with a dull plain white icon.

According to this video at Channel 9, the design team for Windows 7 removed it because they wanted to make Windows quieter. Well, I’m all for “quieter” as long as it doesn’t compromise an important feature.

Since I find it very important to know how much activity (if any) is going on through my network adapter I decided to code a small utility that would mimic the behavior of the old Network Icon.

The application is very simple: launch it and then right click its icon to select the network adapter that you want to monitor… and that’s it.

Network Monitor

Network Monitor's Activity Icon

As a bonus, I added the ability to display a customizable led-type display of the network activity by double clicking the icon (click the image below to see it full size).

Network Monitor Led Bars

Network Monitor Led Bars

Note that the led bars can be moved and resized and all settings, including the selected Network Adapter are saved across sessions.

Simple but very useful.

Here you can download the binary version (which requires the Microsoft .NET Framework 4 Client Profile):
Network Monitor for Windows 7 (1523 downloads )

And here you can download its source code as a Visual Studio 2010 project:
Network Monitor Source Code for Windows 7 (1188 downloads )

How does Network Monitor work?

Network Monitor uses the Performance Counters provided through the Windows Management Instrumentation (WMI) API as exposed by the .NET through the System.Runtime.InteropServices namespace.

Although using WMI can facilitate the task of accessing the Performance Counters under Windows, there’s a major issue with localization. The counters are accessed by their name which varies under every language so an application that uses WMI needs to be able to determine the name of the counter as it is spelled under the language which the OS is running. Terrible… I know.

After doing some research (nice word for Googling) I was able to find that Windows keeps a list of the counter names in the Registry and each counter is given an ID so in order to make the Network Monitor program work under any language I coded a small routine that gathers the appropriate name for the counters based on their ID.
Here’s the code that does the magic:

Dim categoryName As String = ""
Dim counterNameReceived As String = ""
Dim counterNameSent As String = ""
Dim counterNameBandwidth As String = ""
Dim firstInstanceName As String = ""

Dim countersNames() As String = CType(My.Computer.Registry.LocalMachine.OpenSubKey("SOFTWARE\Microsoft\Windows NT\CurrentVersion\Perflib\CurrentLanguage").GetValue("Counter"), String())

For i As Integer = 0 To countersNames.Length - 2 Step 2
	If countersNames(i) = "510" Then categoryName = countersNames(i + 1)
	If countersNames(i) = "264" Then counterNameReceived = countersNames(i + 1)
	If countersNames(i) = "506" Then counterNameSent = countersNames(i + 1)
	If countersNames(i) = "520" Then counterNameBandwidth = countersNames(i + 1)
Next

Once you know the names of the counters is a matter of instantiating them:

pcBytesRecv = New PerformanceCounter(categoryName, counterNameReceived, True)
pcBytesSend = New PerformanceCounter(categoryName, counterNameSent, True)

Finally, Network Monitor starts a thread that keeps querying the values returned by the counters to determine when a send or receive operation is being performed:

Do
	Try
		curRecv = pcBytesRecv.NextValue()
		curSend = pcBytesSend.NextValue()

		If delay = 0 Then
			maxRecValue = Math.Max(maxRecValue, curRecv)
			maxSndValue = Math.Max(maxSndValue, curSend)
		Else
			delay -= 1
		End If

		If lastRecv <> curRecv OrElse lastSend <> curSend Then
			Me.Invalidate()
			DrawTryIcon()

			lastRecv = curRecv
			lastSend = curSend
		End If

		Thread.Sleep(250)
	Catch
	End Try
Loop Until cancelThreads

So every time a read or write state changes, the program updates the tray icon by calling the DrawTryIcon() sub, which looks like this:

Private Sub DrawTryIcon()
	Dim bmpIcon As Bitmap = Nothing

	If curRecv <> 0 OrElse curSend <> 0 Then
		bmpIcon = New Bitmap(16, 16)

		Dim b As SolidBrush
		Dim g As Graphics = Graphics.FromImage(bmpIcon)

		If curSend > minThreshold Then
			g.DrawImageUnscaled(My.Resources.ledon, 4, 0)
			b = New SolidBrush(Color.FromArgb(200, Color.Red))
			g.FillRectangle(b, 6, 2, 8, 6)
			b.Dispose()
		Else
			g.DrawImageUnscaled(My.Resources.ledoff, 4, 0)
		End If

		If curRecv > minThreshold Then
			g.DrawImageUnscaled(My.Resources.ledon, 0, 6)
			b = New SolidBrush(Color.FromArgb(200, Color.LimeGreen))
			g.FillRectangle(b, 2, 8, 8, 6)
			b.Dispose()
		Else
			g.DrawImageUnscaled(My.Resources.ledoff, 0, 6)
		End If

		g.Dispose()
	Else
		bmpIcon = My.Resources.neticon
	End If

	Dim oldIcon As Icon = niIcon.Icon

	Try
		niIcon.Icon = Drawing.Icon.FromHandle(bmpIcon.GetHicon)
	Catch
		bmpIcon = My.Resources.neticon
	End Try

	DestroyIcon(New HandleRef(Me, oldIcon.Handle))
	oldIcon.Dispose()
	bmpIcon.Dispose()
End Sub

Basically, the subroutine simply draws over the standard Windows Network Icon based on the status of the curSend and curRecv variables, so when their value is over a pre-defined threshold, a color is painted over the appropriate area in the icon to represent the send or receive states of the selected network adapter.

Finally, the led bars are painted right over the form using the following code:

If maxRecValue = 0 Then Return

Dim g As Graphics = e.Graphics
Dim rSend As Rectangle = Me.DisplayRectangle
Dim rRecv As Rectangle = Me.DisplayRectangle

rRecv.Height = rRecv.Height \ 2 + 2
rRecv.Inflate(-4, -4)
DrawBar(g, CInt(rRecv.Width * (gRecv / maxRecValue)), rRecv, Brushes.DarkGreen, Brushes.LightGreen)

rSend.Y += (rRecv.Height + 4)
rSend.Inflate(-4, -4)
rSend.Height = rRecv.Height
DrawBar(g, CInt(rSend.Width * (gSend / maxSndValue)), rSend, Brushes.DarkRed, Brushes.Red)

For x As Integer = rRecv.Left To rRecv.Right Step 2
	g.DrawLine(Pens.Black, x, rRecv.Top, x, rSend.Bottom)
Next

PrintText(g, String.Format("{0:F1} KB/s", (gSend / 1024)), rSend)
PrintText(g, String.Format("{0:F1} KB/s", (gRecv / 1024)), rRecv)

Notice that the code actually draws two solid bars and then a For…Next cycle is run to paint black lines at 1 pixels intervals to create the illusion that the bars are actually leds.

So, basically that is it. Feel free to grab the source code from the link above and play with it. As with all the source code distributed in this blog, you are free to use it for any purpose.