mfc vc++自動精確判斷網路連線變化
阿新 • • 發佈:2019-01-04
微軟在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 }