Network Awareness in Windows Vista and Windows 7

How to create network-aware applications that run on Windows Vista and Windows 7.

Note

To download the complete code samples discussed in this topic, see Microsoft Windows Network List Manager (NLM) Samples in Code Gallery.

 

Network Awareness

Computing environments are more interconnected than ever before. Computers can connect over a large variety of networking technologies such as Ethernet, WiFi, WWAN, VPN, and dial-up, to name just a few. Well-designed applications are prepared for connectivity issues, because these applications are used in many different scenarios:

  • One stable connected network. For example, a LAN connection to a desktop machine at a workplace.
  • One network that is intermittently connected and disconnected. For example, a laptop user connecting to a WiFi network and roaming around an office or a building or any wireless hotspot falls into this scenario.
  • Connections to different networks throughout the course of a day. For example, many PCs connect to a home network, then a work network, then a public network throughout the course of a single day. This type of connectivity not only spans multiple networks but may also include multiple types of connection media (WiFi at home, WWAN when in public) as well as situations where multiple connection media is used simultaneously, such as a public WWAN connection with a VPN connection.
  • Disconnected entirely. Ten years ago this was the common case. Today it's becoming increasingly rare, and often is only used when someone turns off their networking hardware in order to do their work without interruption or to save power.

Beyond the simple connected/disconnected state of a network, additional network properties are often relevant to an application. You can use the state of each of these properties at decision points in your application to enable or disable features or perform or prevent from performing certain functionality. Your application may need to know:

  • Is Internet connectivity available? Does your application pull data from the Internet?
  • Is Intranet (domain) connectivity present? Is this a managed network, often maintained by a workplace?
  • Is this a specific network (by name or id or category)? Maybe your application needs to present different UI or different features when it's on a specific network. Maybe the network name is a good way to determine if features should be enabled or disabled.

Windows Vista and Windows 7 provide interfaces that can be used to obtain detailed information about these network characteristics and more. With Windows INetworkListManager interface it is easy to enumerate all networks (INetwork) a machine has ever seen, or just the connected networks, or just the disconnected networks.

The INetwork interface is used to determine the properties of a network: name, description, network ID, category, managed/authenticated, connected/disconnected, and more. In Windows it is possible that a single network is connected to several interfaces, so through an INetwork you can also enumerate the INetworkConnection being used.

The INetworkConnection interface tells you the relevant properties of a specific connection: Connection ID, Adapter ID, Type (managed, authenticated), and connection properties (connected, disconnected, V4 Local, V4 Internet, V6 Local, V6 Internet). V4 Local means Internet Protocol version 4 (IPv4) local access. V4 Internet means IPv4 with internet access. V6 Local and V6 Internet mean IPv6.

Using the NLM API to make your applications network-aware

Understanding what type of decisions your application needs to make based on connectivity information will determine how you go about using the NLM API. Which of the following best describes your application needs?

  • My application needs to be quickly informed of changing networking conditions.
  • My application needs to perform a quick check of what the network conditions are just once.
  • My application needs to do both.

Network connections and their connection characteristics change constantly. If you use the notification mechanism in NLM, it solves this problem by providing connectivity status changes as they occur. For example, an email application may want to know when connectivity is present so that it can either check for new mail or buffer outgoing mail until the next time connectivity is present. If this describes your application then read more on this in the "making your application network aware in real-time" section.

Some applications need only to perform quick checks of what the current networking conditions are. For example, an anti-virus program may poll for updated AV signatures from a central repository once per day. In this case it is only necessary to understand whether the PC is connected at that moment. If this describes your application then read ahead to the section about "checking for current network conditions".

Sometimes applications need to do both. Particularly in the case where an application needs to be aware of connectivity changes as they occur it is typical for an application to first query the current network state and then register for any changes that occur from that point forward.

Making your application network-aware in real time

To keep track of network changes asynchronously, your application needs to register for a callback. When a network or network property change occurs your callback will be invoked.

By registering for a callback your application will be able to handle all network changes as silently as possible.

It is also important to note that not only do network connections change over time but network connection characteristics may also change over time. Some characteristic are learned gradually as the connection is maintained while other characteristics may change dynamically due to changes in the quality of the connection. An example of the former is the following: both Internet (get_IsConnectedToInternet) and domain (GetDomainType) determinations are examples of properties that are learned over time after the initial connection is established. An example of the latter is that Internet connectivity may change due to connectivity issues further upstream than the directly connected connection, say several network hops away. So something like Internet connectivity may be present, may go down, and then may return. The event mechanism provided by the NLA API provides constant updates to reflect any changes in status but it is important to keep these principles in mind so that your application does not assume that connection properties are static in what can be a very dynamic system.

How to register for NLM events

Place the following code in the initialization sequence for your application or where you decide that network events are important to your application.

    CComPtr<INetworkListManager> pNLM;
    hr = CoCreateInstance(CLSID_NetworkListManager, NULL, 
        CLSCTX_ALL, __uuidof(INetworkListManager), (LPVOID*)&amp;pNLM);
    if (SUCCEEDED(hr))
    {
        CComPtr<IConnectionPointContainer> pCpc;
        hr = pNLM->QueryInterface(IID_IConnectionPointContainer, (void**)&amp;pCpc);
        if (SUCCEEDED(hr))
        {
            hr = pCpc->FindConnectionPoint(IID_INetworkEvents, &amp;m_pConnectionPoint);
            if (SUCCEEDED(hr))
            {
                CComPtr<IUnknown> pSink;
                hr = this->QueryInterface(IID_IUnknown, (void**)&amp;pSink);
                if (SUCCEEDED(hr))
                {
                    hr = m_pConnectionPoint->Advise(pSink, &amp;m_dwCookie);
                  ...

After you have created the INetworkListManager object above you will receive INetworkEvents notifications from that point forward. pSink implements the INetworkEvent interface including those event processing methods such as NetworkAdded, NetworkDeleted, NetworkConnectivityChanged, and NetworkPropertyChanged.

Once you begin to receive these network events, your application is well on its way to being network-aware.

Events to register for

In the previous example, the application registered to receive INetworkEvents. When writing your application you need to decide which type of events really matter to your application. The NLM API notification mechanism is flexible and enables you to choose the types of events your application is interested in. You can register for events at three different levels:

Cleanup

At the end of your application's execution the application should stop listening to events by performing the following instruction for each event sink.

    if (m_pConnectionPoint != NULL)
    {
        hr = m_pConnectionPoint->Unadvise(m_dwCookie);
    }

Checking for current network conditions

Sometimes an application need not concern itself with the network conditions until a particular user event requires network connectivity. An example of this could be an application that needs to check for updated information such as current local traffic conditions or weather updates. Only when that information needs to be periodically updated is the network connection status meaningful to the application.

To answer such contingency, the NLM API provides a set of common networking connectivity data, such as:

  • Whether the computer is connected to the Internet through any networks.
  • A list of all available networks.
  • The capabilities of the network connected to.
  • A list of network properties.
  • Whether the computer is connected to the domain network.
  • The connections that make up a particular network.
  • The capabilities of the current connection.

The following code shows how to determine the characteristics of a network synchronously. It is important to keep in mind that you would not want to use this technique in a tight loop, but it is fine to use this to determine the current conditions. The reason for this is that after enumerating through the networks you can keep your application aware of changes to the set of networks without re-enumerating. Code that walks through all networks or all connected networks really only needs to occur a single time, such as on startup or initialization of the application. Subsequent network changes can be handled as shown in the earlier section, making your application network aware in real time.

You first need to obtain the INetwork you're interested in. To get this, enumerate the networks by using the INetworkListManager function. The following sample obtains and displays the list of connected networks and prints the name of each network.

     // Enumerate connected networks
                hr = pNLM->GetNetworks(NLM_ENUM_NETWORK_CONNECTED, &amp;pEnumNetworks);
                if (SUCCEEDED(hr))
                {
                    INetwork* pNetworks[NUM_NETWORK];
                    ULONG cFetched = 0;
                    BOOL  bDone = FALSE;
                    while (!bDone)
                    {             
                        hr = pEnumNetworks->Next(_countof(pNetworks), pNetworks, &amp;cFetched);
                        if (SUCCEEDED(hr) &amp;&amp; (cFetched > 0))
                        {
                            for (ULONG I = 0; I < cFetched; I++)
                            {
  CComBSTR szName;
  hr = pNetworks[i]->GetName(&amp;szName);
  if (SUCCEEDED(hr))
  {
     wprintf(L"Network Name                   : %s\n", szName);
  }
                                ShowInfoForNetwork(pNetworks[i]);
                                pNetworks[i]->Release();
                            }
                        }
                        else
                        {
                            bDone = TRUE;
                        }
                    }
                }

With the previous code, values for NLM_ENUM_NETWORK can be:

  • NLM_ENUM_NETWORK_CONNECTED: connected networks only
  • NLM_ENUM_NETWORK_DISCONNECTED: disconnected networks only
  • NLM_ENUM_NETWORK_ALL: connected and disconnected networks

Within each INetwork you obtain the INetworkConnections and the network adapter ID that each is associated with, as shown in the following code.

    INetworkConnection* pNetworkConnections[NUM_CONNECTION];
    ULONG cFetched = 0;
    BOOL  bDone = FALSE;
    while (!bDone)
    {
        hr = pEnum->Next(_countof(pNetworkConnections), pNetworkConnections, &amp;cFetched);
        if (SUCCEEDED(hr) &amp;&amp; (cFetched > 0))
        {
            for (ULONG I = 0; I < cFetched; I++)
            {
    GUID gdAdapterId;
    hr = pNetworkConnections[i]->GetAdapterId(&amp;gdAdapterId);
    if (SUCCEEDED(hr))
    {
            BSTR szAdapterIDName =  GetSzGuid(gdAdapterId);
            wprintf(L"    Adapter Id                 : %s\n", szAdapterIDName);
    }       
                ShowInfoForNetworkConnection(pNetworkConnections[i]);
                pNetworkConnections[i]->Release();
            }
        }
        else
        {
            bDone = TRUE;
        }
    }

Finally, you obtain the connectivity information of each network provided through the NLM API, as shown in the following code sample.

HRESULT PrintNetworkConnectivity(INetwork* pNetwork)
{
    HRESULT hr = S_OK;
    if(pNetwork != NULL)
    {
        NLM_CONNECTIVITY nlmConnectivity;
        hr = pNetwork->GetConnectivity(&amp;nlmConnectivity);
        CComBSTR szConnectivity = L"";
        if( hr == S_OK)
        {
            if (nlmConnectivity == NLM_CONNECTIVITY_DISCONNECTED)
            {
                szConnectivity = L"network disconnected";
            }

            if (nlmConnectivity & NLM_CONNECTIVITY_IPV4_NOTRAFFIC)
            {
                if (szConnectivity.Length() != 0)
                {
                    szConnectivity += L" | ";
                }
                szConnectivity += L"network no traffic";
            }

            if (nlmConnectivity & NLM_CONNECTIVITY_IPV4_LOCALNETWORK)
            {
                if (szConnectivity.Length() != 0)
                {
                    szConnectivity += L" | ";
                }
                szConnectivity += L"IPv4 local connectivity";
            }

            if (nlmConnectivity & NLM_CONNECTIVITY_IPV4_INTERNET)
            {
                if (szConnectivity.Length() != 0)
                {
                    szConnectivity += L" | ";
                }
                szConnectivity += L"IPv4 internet connectivity";
            }

            if (nlmConnectivity & NLM_CONNECTIVITY_IPV6_LOCALNETWORK)
            {
                if (szConnectivity.Length() != 0)
                {
                    szConnectivity += L" | ";
                }
                szConnectivity += L"IPv6 local connectivity";
            }

            if (nlmConnectivity & NLM_CONNECTIVITY_IPV6_INTERNET)
            {
                if (szConnectivity.Length() != 0)
                {
                    szConnectivity +=   L" | ";
                }
                szConnectivity += L"IPv6 internet connectivity";
            }
        }
        if (SUCCEEDED(hr))
        {
               WCHAR result[MAX_LENGTH];
               wcscpy_s(result, _countof(result), szConnectivity);
               wprintf(L"Network Connectivity bitmask           : 0x%08x %s\n", nlmConnectivity, result);
        }
        else
        {
            wprintf(L"failed to get connectivity from INetwork ");
        }
    }
    return hr;
}

The samples provided offer a glimpse into the rich set of information that is available through the NLM API. The API answers even more common networking connectivity questions, such as:

Sample Code

Code samples are available as examples of coding against the Network List Manager (NLM) APIs for applications running on Windows Vista or Windows 7. The samples describe how to use the API set to obtain network connectivity information as needed and how to register for events so that applications can react to a changing state of networking connectivity. Samples are written for both C++ and C# and can be found on the Downloads tab at Microsoft Windows Network List Manager (NLM) Samples in Code Gallery.

Conclusion

Keep these general recommendations in mind when creating your network aware application:

  • Cache data locally where possible to avoid disruptions to your users when network transitions occur.
  • Do not poll in tight loops by repetitively calling NLM API, for example - get_IsConnectedToInternet; sleep 1 second; get_IsConnectedToInternet; sleep 1 second; and so on. If your application needs to know whether network connectivity has changed please register to receive notifications from NLM. This optimizes the performance of the computer and ensures that your application does not miss a network change event.
  • When connectivity is resumed after being disconnected consider whether you need to perform a networking action immediately or whether your application can wait momentarily. On mobile PCs there may be numerous applications that utilize the network. If they were all to start utilizing the network immediately upon the resumption of connectivity they can consume the bandwidth available. Consider whether your app can wait briefly before utilizing the network.
  • It is also important to note that not only do network connections change over time but network connection characteristics may also change over time. Some characteristic are learned gradually as the connection is maintained while other characteristics may change dynamically due to changes in the quality of the connection. An example of the former is the following: both Internet (get_IsConnectedToInternet) and domain (GetDomainType) determinations are examples of properties that are learned over time after the initial connection is established. An example of the latter is that Internet connectivity may change due to connectivity issues further upstream than the directly connected connection, say several network hops away. So something like Internet connectivity may be present, may go down, and then may return. The event mechanism provided by the NLA API will provide constant updates to reflect any changes in status but it is important to keep these principles in mind so that your application does not assume that connection properties are static in what can be a very dynamic system.
  • If a network is disconnected, queue and defer data transfers until later, and determine whether user feedback is appropriate for your application.

Network transitions will happen frequently, especially if your application is running on a mobile PC. Consider the following when notifying your user of underlying connectivity transitions:

  • Does this event directly impact the user's current task, and therefore demand the user's immediate attention, or can it be handled transparently by the application?
  • There may be only certain events that are important to your application. For example, maybe you only care when a network with internet connectivity is connected or disconnected. Or perhaps your application will only run when connected to the PC's domain network. Connectivity to other types of networks may or may not be interesting to the user in some of these cases.