只要连接发生变化,您就可以使用INetworkListManager 请求INetworkConnectionEvents。要将这些放在一起,您必须执行以下操作:
- 实现
INetworkConnectionEvents 类型的事件接收器。
- 实例化一个
INetworkListManager 对象。
- 连接事件接收器。
如果您不了解连接点,最后一步可能会造成混淆。 Raymond Chen 已就该主题发表了一篇颇具教育意义的博文,如果您需要赶上进度:An introduction to COM connection points。
以下大致概述了如何实现这一点。它使用的是微软的_com_ptr_t,所以前面有相当多的样板代码:
#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));
这确保链接到适当的库,拉入所需的头文件,并声明一些基于_com_ptr_t 的智能指针类型。接下来是INetworkConnectionEvents 实现:
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; }
};
这实现了IUnknown 接口以及两个INetworkConnectionEvents 成员。 IUnknown 的实现对于我们的用例来说还不够;生产质量的实现需要通过AddRef 和Release 提供正确的引用计数。
接下来的代码会实例化一个INetworkListManager 对象并将事件接收器连接起来以接收通知:
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.
}