惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

GbyAI
GbyAI
Blog — PlanetScale
Blog — PlanetScale
The GitHub Blog
The GitHub Blog
Microsoft Security Blog
Microsoft Security Blog
I
InfoQ
A
About on SuperTechFans
T
The Blog of Author Tim Ferriss
D
DataBreaches.Net
L
LangChain Blog
F
Fortinet All Blogs
C
Check Point Blog
Google DeepMind News
Google DeepMind News
云风的 BLOG
云风的 BLOG
Engineering at Meta
Engineering at Meta
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
H
Help Net Security
J
Java Code Geeks
月光博客
月光博客
H
Hackread – Cybersecurity News, Data Breaches, AI and More
IT之家
IT之家
aimingoo的专栏
aimingoo的专栏
小众软件
小众软件
宝玉的分享
宝玉的分享
Jina AI
Jina AI

博客园 - suiyingjie

vs2010 将.mc编译为.rc文件 列出系统支持的区域设置 MySQL判断索引是否存在的存储过程 如何用windbg分析64位机上32位程序的dump文件 libcurl发送邮件C++类 (转) libcurl教程(转) VC 宏与预处理使用方法总结(转) 解决VS2010升级SP1后出现的warning C4005问题 nafxcw.lib与LIBCMT.lib在vs2008,VS2010中链接冲突解决方案 使用JAVASCRIPT去360DOC复制限制 用户中心 - 博客园 使用Symchk.exe 下载符号 A Crash Course on the Depths of Win32 Structured Exception Handling [转载] 从文件句柄获得全路径 Ip安全策略批处理脚本 c++资源之不完全导引 (转) 突破局域网中对用户上网的限制(转) [转载]HTML七种加密解密 - suiyingjie - 博客园 挂马代码大全 - suiyingjie - 博客园
如何自动获取网络变化通知(转)
suiyingjie · 2012-07-05 · via 博客园 - suiyingjie

当我们把网线插到计算机上时,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;
030         return Result;
033     ULONG STDMETHODCALLTYPE AddRef()
035         return (ULONG)InterlockedIncrement(&m_ref);
038     ULONG STDMETHODCALLTYPE Release()
040         LONG Result = InterlockedDecrement(&m_ref);
041         if (Result == 0)
042             delete this;
043         return (ULONG)Result;
046     virtual HRESULT STDMETHODCALLTYPE ConnectivityChanged(
047         /* [in] */ NLM_CONNECTIVITY newConnectivity)
057 int _tmain(int argc, TCHAR** argv, TCHAR** Env)
059     CoInitialize(NULL);   
062     //  通过NLA接口获取网络状态
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))
088                     DWORD Cookie = NULL;
089                     CNetworkListManagerEvent *NetEvent = new CNetworkListManagerEvent;
090                     Result = pConnectPoint->Advise((IUnknown *)NetEvent, &Cookie);
091                     if (SUCCEEDED(Result))
092                     {
093                         printf("Loop Message\n");
094                         MSG msg;
095                         while(GetMessage(&msg, NULL, 0, 0))
096                         {
097                             TranslateMessage(&msg);
098                             DispatchMessage(&msg);
100                             if (msg.message == WM_QUIT)
101                             {
102                                 break;
103                             }
104                         }
106                         pConnectPoint->Unadvise(Cookie);
108                         pConnectPoint->Release();
109                     }
112                 pCPContainer->Release();
115             pNetworkListManager->Release();
118         pUnknown->Release();
121     CoUninitialize();

因为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);
16     WSAData data = {0};
17     WSAStartup(MAKEWORD(2, 0), &data);
21         printf("BeginWait %d\n", i++);
22         WaitForNetworkChnages();
23         printf("EndWait\n");