1. 程式人生 > >mfc vc++自動精確判斷網路連線變化

mfc vc++自動精確判斷網路連線變化

微軟在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
002	{
003	public:
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	
052	private:
053	
054	    LONG m_ref;
055	};
056	
057	int _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++

01	void 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	
14	void 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	}