NightFuryLxD
NightFuryLxD

Reputation: 829

Detect internet connectivity change event programmatically C++ Windows

I need to be notified when the Windows system is connected to/disconnected from the internet.

I have seen answers in C#, but I need to do this in C++ and without using WMI.

There are some answers in C++, but its only checking the internet connectivity at one point in time. But this needs to be detected as and when it happens. Repeating this every few mins or secs is not the best idea. An event needs to be communicated to my app. Also, pinging a well known host may be dangerous as that host may be unreachable sometime and that would lead to a false conclusion.

Is it possible to get such an event? If yes, how to do this?

Upvotes: 0

Views: 1731

Answers (1)

IInspectable
IInspectable

Reputation: 51345

You can use the INetworkListManager to request INetworkConnectionEvents whenever there are changes in connectivity. To put this together you'll have to do the following:

  • Implement an event sink of type INetworkConnectionEvents.
  • Instantiate an INetworkListManager object.
  • Connect the event sink.

The final step is what can be confusing, if you don't understand connection points. Raymond Chen has published a highly educational blog post on the topic if you need to catch up: An introduction to COM connection points.

The following roughly outlines how to implement this. It's using Microsoft's _com_ptr_t so there's a fair amount of boilerplate code up front:

#if defined(_DEBUG)
#    pragma comment(lib, "comsuppwd.lib")
#else
#    pragma comment(lib, "comsuppw.lib")
#endif

#include <comdef.h>
#include <comip.h>
#include <netlistmgr.h>

_COM_SMARTPTR_TYPEDEF(INetworkListManager, __uuidof(INetworkListManager));
_COM_SMARTPTR_TYPEDEF(IConnectionPointContainer, __uuidof(IConnectionPointContainer));
_COM_SMARTPTR_TYPEDEF(IConnectionPoint, __uuidof(IConnectionPoint));
_COM_SMARTPTR_TYPEDEF(IUnknown, __uuidof(IUnknown));

This makes sure to link against the appropriate libraries, pulls in the required header files, and declares a handful of _com_ptr_t-based smart pointer types. Next up is the INetworkConnectionEvents implementation:

struct EventSink : INetworkListManagerEvents
{
    HRESULT QueryInterface(REFIID riid, LPVOID* ppvObj)
    {
        if (!ppvObj)
        {
            return E_POINTER;
        }
        *ppvObj = nullptr;
        if (riid == IID_IUnknown || riid == IID_INetworkListManagerEvents)
        {
            AddRef();
            *ppvObj = reinterpret_cast<void*>(this);
            return S_OK;
        }
        return E_NOINTERFACE;
    }

    ULONG AddRef() { return 1; }
    ULONG Release() { return 1; }

    HRESULT ConnectivityChanged(NLM_CONNECTIVITY newConnectivity)
    {
        if ((newConnectivity & (NLM_CONNECTIVITY_IPV4_INTERNET | NLM_CONNECTIVITY_IPV6_INTERNET)) != 0)
        {
            printf("Internet connection available\n");
        }
        else
        {
            printf("Internet connection not available\n");
        }
        return S_OK;
    }
    HRESULT NetworkConnectionPropertyChanged(GUID connectionId, NLM_CONNECTION_PROPERTY_CHANGE) { return S_OK; }
};

This implements both the IUnknown interface as well as the two INetworkConnectionEvents members. The IUnknown implementation is barely sufficient for our use-case; a production-quality implementation would need to provide proper reference counting through AddRef and Release.

The remaining code then instantiates an INetworkListManager object and wires the event sink up to receive notifications:

int main()
{
    // Initialize COM
    auto hr { ::CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED) };

    // Instantiate INetworkListManager object
    INetworkListManagerPtr spManager { nullptr };
    if SUCCEEDED (hr)
    {
        hr = spManager.CreateInstance(CLSID_NetworkListManager, nullptr, CLSCTX_ALL);
    }

    // Query for connection point container
    IConnectionPointContainerPtr spConnectionPoints { nullptr };
    if SUCCEEDED (hr)
    {
        hr = spManager.QueryInterface(IID_PPV_ARGS(&spConnectionPoints));
    }

    // Find connection point for the interesting event
    IConnectionPointPtr spConnectionPoint { __nullptr };
    if SUCCEEDED (hr)
    {
        hr = spConnectionPoints->FindConnectionPoint(IID_INetworkListManagerEvents, &spConnectionPoint);
    }

    // Construct event sink
    EventSink sink {};
    IUnknownPtr spSink { nullptr };
    if (SUCCEEDED(hr))
    {
        hr = sink.QueryInterface(IID_IUnknown, reinterpret_cast<void**>(&spSink));
    }

    // And wire it up to the connection point
    DWORD cookie { 0 };
    if SUCCEEDED (hr)
    {
        hr = spConnectionPoint->Advise(spSink, &cookie);
    }

    // At this point everything is set up to receive notifications
    MSG msg {};
    while (::GetMessageW(&msg, nullptr, 0, 0) > 0)
    {
        ::DispatchMessageW(&msg);
    }

    // Cleanup
    if (SUCCEEDED(hr))
    {
        hr = spConnectionPoint->Unadvise(cookie);
    }

    // Don't uninitialize COM since we have smart pointers that
    // get cleaned up only after leaving this scope.
}

Upvotes: 2

Related Questions