如何自动获取网络变化通知
2012-04-23
当我们把网线插到计算机上时,WINDOWS任务栏的托盘图标都会更改相应的网络图标,拔掉也会有相应的处理。一直都对这个机制感兴趣,却不知道如何做,而且公司的某个产品也需要这么一个功能。昨天在家测试某个程序的时候,发现了其中一个线程的栈中有一个叫wininet!CheckForNetworkChange的函数,IDA分析了WINNET.DLL后,有了本文。
微软在WINDOWS VISTA之后提供了一个叫NLA(Network List Manager API)的接口,用于获取网络状态变化通知的一个接口。以COM技术实现。
主要导出的COM接口如下:
IEnumNetworkConnections
IEnumNetworks
INetwork
INetworkConnection
INetworkConnectionEvents
INetworkEvents
INetworkListManager
INetworkListManagerEvents
其中INetworkListManager是一个根对象,可以获取计算机是否连接到因特网(INetworkListManager->get_IsConnectedToInternet)。还可以查询有哪些可用的网络和连接.更关键的是INetworkListManagerEvents和INetworkEvents两个类。这两个类在MSDN文档里的描述如下:
is a message sink interface that a client implements to get overall machine state related events.
也就是说我们要自己实现这两个类。而回调的方式是通过COM技术中特有的机制IConnectionPoint来搞定。实现方式如下:
001 | class CNetworkListManagerEvent : public INetworkListManagerEvents |
004 | CNetworkListManagerEvent() : m_ref(1) |
009 | ~CNetworkListManagerEvent() |
014 | HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void **ppvObject) |
016 | HRESULT Result = S_OK; |
017 | if (IsEqualIID(riid, IID_IUnknown)) |
019 | *ppvObject = (IUnknown *) this ; |
021 | else if (IsEqualIID(riid ,IID_INetworkListManagerEvents)) |
023 | *ppvObject = (INetworkListManagerEvents *) this ; |
027 | Result = E_NOINTERFACE; |
033 | ULONG STDMETHODCALLTYPE AddRef() |
035 | return ( ULONG )InterlockedIncrement(&m_ref); |
038 | ULONG STDMETHODCALLTYPE Release() |
040 | LONG Result = InterlockedDecrement(&m_ref); |
043 | return ( ULONG )Result; |
046 | virtual HRESULT STDMETHODCALLTYPE ConnectivityChanged( |
047 | NLM_CONNECTIVITY newConnectivity) |
057 | int _tmain( int argc, TCHAR ** argv, TCHAR ** Env) |
064 | IUnknown *pUnknown = NULL; |
066 | HRESULT Result = CoCreateInstance(CLSID_NetworkListManager, NULL, CLSCTX_ALL, IID_IUnknown, ( void **)&pUnknown); |
067 | if (SUCCEEDED(Result)) |
069 | INetworkListManager *pNetworkListManager = NULL; |
070 | Result = pUnknown->QueryInterface(IID_INetworkListManager, ( void **)&pNetworkListManager); |
071 | if (SUCCEEDED(Result)) |
073 | VARIANT_BOOL IsConnect = VARIANT_FALSE; |
074 | Result = pNetworkListManager->get_IsConnectedToInternet(&IsConnect); |
075 | if (SUCCEEDED(Result)) |
077 | printf ( "IsConnect Result %s\n" , IsConnect == VARIANT_TRUE ? "TRUE" : "FALSE" ); |
080 | IConnectionPointContainer *pCPContainer = NULL; |
081 | Result = pNetworkListManager->QueryInterface(IID_IConnectionPointContainer, ( void **)&pCPContainer); |
082 | if (SUCCEEDED(Result)) |
084 | IConnectionPoint *pConnectPoint = NULL; |
085 | Result = pCPContainer->FindConnectionPoint(IID_INetworkListManagerEvents, &pConnectPoint); |
086 | if (SUCCEEDED(Result)) |
089 | CNetworkListManagerEvent *NetEvent = new CNetworkListManagerEvent; |
090 | Result = pConnectPoint->Advise((IUnknown *)NetEvent, &Cookie); |
091 | if (SUCCEEDED(Result)) |
093 | printf ( "Loop Message\n" ); |
095 | while (GetMessage(&msg, NULL, 0, 0)) |
097 | TranslateMessage(&msg); |
098 | DispatchMessage(&msg); |
100 | if (msg.message == WM_QUIT) |
106 | pConnectPoint->Unadvise(Cookie); |
108 | pConnectPoint->Release(); |
112 | pCPContainer->Release(); |
115 | pNetworkListManager->Release(); |
因为NLA API是WINDOWS VISTA之后才有的,对于WINDOWS XP是不兼容的,但是WINDOWS XP下有一个方法可以达到同样的效果,可以参考文章Network Awareness in Windows XP,这个文章里的代码是C#的,其中关键的代码转换成C++
01 | void WaitForNetworkChnages() |
03 | WSAQUERYSET querySet = {0}; |
04 | querySet.dwSize = sizeof (WSAQUERYSET); |
05 | querySet.dwNameSpace = NS_NLA; |
07 | HANDLE LookupHandle = NULL; |
08 | WSALookupServiceBegin(&querySet, LUP_RETURN_ALL, &LookupHandle); |
09 | DWORD BytesReturned = 0; |
10 | WSANSPIoctl(LookupHandle, SIO_NSP_NOTIFY_CHANGE, NULL, 0, NULL, 0, &BytesReturned, NULL); |
11 | WSALookupServiceEnd(LookupHandle); |
17 | WSAStartup(MAKEWORD(2, 0), &data); |
21 | printf ( "BeginWait %d\n" , i++); |
22 | WaitForNetworkChnages(); |
tags: Network Awareness, NLA, 网卡状态, 网络状态
posted in 程序开发 by Hydra | No Comments