Reputation: 829
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
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:
INetworkConnectionEvents
.INetworkListManager
object.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