如何自动获取网络变化通知

如何自动获取网络变化通知
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来搞定。实现方式如下:

001class CNetworkListManagerEvent : public INetworkListManagerEvents
002{
003public:
004    CNetworkListManagerEvent() : m_ref(1)
005    {
006  
007    }
008  
009    ~CNetworkListManagerEvent()
010    {
011  
012    }
013  
014    HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void **ppvObject)
015    {
016        HRESULT Result = S_OK;
017        if (IsEqualIID(riid, IID_IUnknown))
018        {
019            *ppvObject = (IUnknown *)this;
020        }
021        else if (IsEqualIID(riid ,IID_INetworkListManagerEvents))
022        {
023            *ppvObject = (INetworkListManagerEvents *)this;
024        }
025        else
026        {
027            Result = E_NOINTERFACE;
028        }
029  
030        return Result;
031    }
032  
033    ULONG STDMETHODCALLTYPE AddRef()
034    {
035        return (ULONG)InterlockedIncrement(&m_ref);
036    }
037  
038    ULONG STDMETHODCALLTYPE Release()
039    {
040        LONG Result = InterlockedDecrement(&m_ref);
041        if (Result == 0)
042            delete this;
043        return (ULONG)Result;
044    }
045  
046    virtual HRESULT STDMETHODCALLTYPE ConnectivityChanged(
047        /* [in] */ NLM_CONNECTIVITY newConnectivity)
048    {
049        return S_OK;
050    }
051  
052private:
053  
054    LONG m_ref;
055};
056  
057int _tmain(int argc, TCHAR** argv, TCHAR** Env)
058{
059    CoInitialize(NULL);    
060  
061    //
062    //  通过NLA接口获取网络状态
063    //
064    IUnknown *pUnknown = NULL;
065  
066    HRESULT Result = CoCreateInstance(CLSID_NetworkListManager, NULL, CLSCTX_ALL, IID_IUnknown, (void **)&pUnknown);
067    if (SUCCEEDED(Result))
068    {
069        INetworkListManager *pNetworkListManager = NULL;
070        Result = pUnknown->QueryInterface(IID_INetworkListManager, (void **)&pNetworkListManager);
071        if (SUCCEEDED(Result))
072        {
073            VARIANT_BOOL IsConnect = VARIANT_FALSE;
074            Result = pNetworkListManager->get_IsConnectedToInternet(&IsConnect);
075            if (SUCCEEDED(Result))
076            {
077                printf("IsConnect Result %s\n", IsConnect == VARIANT_TRUE ? "TRUE" : "FALSE");
078            }
079  
080            IConnectionPointContainer *pCPContainer = NULL;
081            Result = pNetworkListManager->QueryInterface(IID_IConnectionPointContainer, (void **)&pCPContainer);
082            if (SUCCEEDED(Result))
083            {
084                IConnectionPoint *pConnectPoint = NULL;
085                Result = pCPContainer->FindConnectionPoint(IID_INetworkListManagerEvents, &pConnectPoint);
086                if(SUCCEEDED(Result))
087                {
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);
099  
100                            if (msg.message == WM_QUIT)
101                            {
102                                break;
103                            }
104                        }
105  
106                        pConnectPoint->Unadvise(Cookie);
107  
108                        pConnectPoint->Release();
109                    }
110                }
111  
112                pCPContainer->Release();
113            }
114  
115            pNetworkListManager->Release();
116        }
117  
118        pUnknown->Release();
119    }
120  
121    CoUninitialize();
122    return 1;
123}

因为NLA API是WINDOWS VISTA之后才有的,对于WINDOWS XP是不兼容的,但是WINDOWS XP下有一个方法可以达到同样的效果,可以参考文章Network Awareness in Windows XP,这个文章里的代码是C#的,其中关键的代码转换成C++

01void WaitForNetworkChnages()
02{
03    WSAQUERYSET querySet = {0};
04    querySet.dwSize = sizeof(WSAQUERYSET);
05    querySet.dwNameSpace = NS_NLA;
06  
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);
12}
13  
14void Test()
15{
16    WSAData data = {0};
17    WSAStartup(MAKEWORD(2, 0), &data);
18    int i = 0;
19    while(1)
20    {
21        printf("BeginWait %d\n", i++);
22        WaitForNetworkChnages();
23        printf("EndWait\n");
24    }
25  
26    WSACleanup();
27}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值