Saturday, December 12, 2015

A little app for monitoring connectivity

One of the things that Windows Vista did better than the later releases was to recover network connectivity immediately when coming out of hibernation -- really, as if nothing had happened. Later releases seem to go back to square one to restart a WiFi connection -- and what is worse, there's no visual feedback from the systray icon after the initial handshake to tell when the link is actually useful.

As this can take tens of seconds, it is the biggest pain point I have with Windows X. So, finally I got around to writing a monitoring app.

I started with a simple systray application, and some sample code for using the Network Link Manager API as guidance for an F# application. I also needed to use the CoClass for instantiating an INetworkListManager in line 27 as noted here.

Then it was just a matter of adding some icons, for which I used stock images for blocked, warning and OK from the Visual Studio image library to indicate no connection, not yet usable connection and internet available. Finally StackOverflow reminded me about how to add an application icon, to make a shortcut look nicer.

So, without further ado

open System
open System.Drawing
open System.Reflection
open System.Resources
open System.Runtime.InteropServices.ComTypes
open System.Windows.Forms
open NETWORKLIST
let OnExit (sender : obj) (e : EventArgs) = Application.Exit()
let FindIcon name = new Icon(Assembly.GetExecutingAssembly().GetManifestResourceStream(name))
let FindString name =
ResourceManager("Resource", System.Reflection.Assembly.GetExecutingAssembly()).GetString(name)
let internetMask = NLM_CONNECTIVITY.NLM_CONNECTIVITY_IPV4_INTERNET |||
NLM_CONNECTIVITY.NLM_CONNECTIVITY_IPV6_INTERNET
let localMask = NLM_CONNECTIVITY.NLM_CONNECTIVITY_IPV4_LOCALNETWORK |||
NLM_CONNECTIVITY.NLM_CONNECTIVITY_IPV6_LOCALNETWORK
let subnetMask = NLM_CONNECTIVITY.NLM_CONNECTIVITY_IPV4_SUBNET |||
NLM_CONNECTIVITY.NLM_CONNECTIVITY_IPV6_SUBNET
type Connectivity() =
inherit Form()
let menu = new ContextMenu()
let icon = new NotifyIcon()
let mutable cookie = 0
let mutable icp : IConnectionPoint = null
let nlm = NetworkListManagerClass()
let full = FindIcon "Full.ico"
let partial = FindIcon "Partial.ico"
let none = FindIcon "None.ico"
let connectivity = FindString "Connectivity"
let noneText = FindString "None"
let internet = FindString "Internet"
let local = FindString "Local"
let subnet = FindString "Subnet"
let noTraffic = FindString "NoTraffic"
do menu.MenuItems.Add(FindString "Exit", EventHandler(OnExit)) |> ignore
icon.ContextMenu <- menu;
icon.Visible <- true;
override this.Dispose (isDisposing:bool) =
if isDisposing then
if icp <> null then icp.Unadvise(cookie)
icon.Icon <- null
icon.Dispose()
menu.Dispose()
[full; partial; none]
|> Seq.iter (fun x -> x.Dispose())
base.Dispose(isDisposing)
override this.OnLoad(e:EventArgs) =
this.Visible <- false // Hide form window.
this.ShowInTaskbar <- false; // Remove from taskbar.
let (state, text) = if nlm.IsConnectedToInternet then (full, internet) else (none, noneText)
icon.Icon <- state
icon.Text <- connectivity + "\r\n" + text
let nlmGuid = typeof<INetworkListManagerEvents>.GUID
let icpc = (nlm :> obj) :?> IConnectionPointContainer
icpc.FindConnectionPoint(ref nlmGuid , &icp);
icp.Advise(this, &cookie);
base.OnLoad(e)
interface INetworkListManagerEvents with
member this.ConnectivityChanged(newConnectivity:NLM_CONNECTIVITY) =
let (state, text) = match newConnectivity with
| NLM_CONNECTIVITY.NLM_CONNECTIVITY_DISCONNECTED -> (none, noneText)
| x when (x &&& internetMask) <>
NLM_CONNECTIVITY.NLM_CONNECTIVITY_DISCONNECTED -> (full, internet)
| x when (x &&& localMask) <>
NLM_CONNECTIVITY.NLM_CONNECTIVITY_DISCONNECTED -> (partial, local)
| x when (x &&& subnetMask) <>
NLM_CONNECTIVITY.NLM_CONNECTIVITY_DISCONNECTED -> (partial, subnet)
| _ -> (partial, noTraffic)
this.Invoke(new MethodInvoker(fun () -> icon.Icon <- state
icon.Text <- connectivity + "\r\n" + text
)) |> ignore
[<EntryPoint>]
[<STAThread>]
let main argv =
Application.Run(new Connectivity());
0 // return an integer exit code
view raw Connectivity.fs hosted with ❤ by GitHub

Later: having slept on it, there's this simplification can be made to the OnLoad method

override this.OnLoad(e:EventArgs) =
this.Visible <- false // Hide form window.
this.ShowInTaskbar <- false; // Remove from taskbar.
(this :> INetworkListManagerEvents).ConnectivityChanged(nlm.GetConnectivity())
let nlmGuid = typeof<INetworkListManagerEvents>.GUID
let icpc = (nlm :> obj) :?> IConnectionPointContainer
icp <- icpc.FindConnectionPoint(ref nlmGuid);
cookie <- icp.Advise(this);
base.OnLoad(e)
view raw OnLoad.fs hosted with ❤ by GitHub

Later still: when I've hibernated a laptop with this program running, both times now it has come back with the WiFi connection still live, just like it used to under Vista. Even better.