1 #include "precomp.h"
2 
3 typedef struct tagINetConnectionItem
4 {
5     struct tagINetConnectionItem * Next;
6     DWORD dwAdapterIndex;
7     NETCON_PROPERTIES    Props;
8 } INetConnectionItem, *PINetConnectionItem;
9 
10 class CNetConnectionManager final :
11     public INetConnectionManager,
12     public IEnumNetConnection
13 {
14     public:
15         CNetConnectionManager();
16         BOOL EnumerateINetConnections();
17 
18         // IUnknown
19         virtual HRESULT WINAPI QueryInterface(REFIID riid, LPVOID *ppvOut);
20         virtual ULONG WINAPI AddRef();
21         virtual ULONG WINAPI Release();
22 
23         // INetConnectionManager
24         virtual HRESULT WINAPI EnumConnections(NETCONMGR_ENUM_FLAGS Flags, IEnumNetConnection **ppEnum);
25 
26         // IEnumNetConnection
27         virtual HRESULT WINAPI Next(ULONG celt, INetConnection **rgelt, ULONG *pceltFetched);
28         virtual HRESULT WINAPI Skip(ULONG celt);
29         virtual HRESULT WINAPI Reset();
30         virtual HRESULT WINAPI Clone(IEnumNetConnection **ppenum);
31 
32     private:
33         LONG m_ref;
34         PINetConnectionItem m_pHead;
35         PINetConnectionItem m_pCurrent;
36 };
37 
38 class CNetConnection final :
39     public INetConnection
40 {
41     public:
42         CNetConnection(PINetConnectionItem pItem);
43 
44         // IUnknown
45         virtual HRESULT WINAPI QueryInterface(REFIID riid, LPVOID *ppvOut);
46         virtual ULONG WINAPI AddRef();
47         virtual ULONG WINAPI Release();
48 
49         // INetConnection
50         HRESULT WINAPI Connect();
51         HRESULT WINAPI Disconnect();
52         HRESULT WINAPI Delete();
53         HRESULT WINAPI Duplicate(LPCWSTR pszwDuplicateName, INetConnection **ppCon);
54         HRESULT WINAPI GetProperties(NETCON_PROPERTIES **ppProps);
55         HRESULT WINAPI GetUiObjectClassId(CLSID *pclsid);
56         HRESULT WINAPI Rename(LPCWSTR pszwDuplicateName);
57 
58     private:
59         ~CNetConnection();
60 
61         LONG m_ref;
62         NETCON_PROPERTIES m_Props;
63         DWORD m_dwAdapterIndex;
64 };
65 
66 VOID NormalizeOperStatus(MIB_IFROW *IfEntry, NETCON_PROPERTIES * Props);
67 
68 CNetConnectionManager::CNetConnectionManager() :
69     m_ref(0),
70     m_pHead(NULL),
71     m_pCurrent(NULL)
72 {
73 }
74 
75 HRESULT
76 WINAPI
77 CNetConnectionManager::QueryInterface(
78     REFIID iid,
79     LPVOID *ppvObj)
80 {
81     *ppvObj = NULL;
82 
83     if (IsEqualIID(iid, IID_IUnknown) ||
84         IsEqualIID(iid, IID_INetConnectionManager))
85     {
86         *ppvObj = static_cast<INetConnectionManager*>(this);
87         AddRef();
88         return S_OK;
89     }
90 
91     return E_NOINTERFACE;
92 }
93 
94 ULONG
95 WINAPI
96 CNetConnectionManager::AddRef()
97 {
98     ULONG refCount = InterlockedIncrement(&m_ref);
99 
100     return refCount;
101 }
102 
103 ULONG
104 WINAPI
105 CNetConnectionManager::Release()
106 {
107     ULONG refCount = InterlockedDecrement(&m_ref);
108 
109     if (!refCount)
110         delete this;
111 
112     return refCount;
113 }
114 
115 HRESULT
116 WINAPI
117 CNetConnectionManager::EnumConnections(
118     NETCONMGR_ENUM_FLAGS Flags,
119     IEnumNetConnection **ppEnum)
120 {
121     TRACE("EnumConnections\n");
122 
123     if (!ppEnum)
124         return E_POINTER;
125 
126     if (Flags != NCME_DEFAULT)
127         return E_FAIL;
128 
129     *ppEnum = static_cast<IEnumNetConnection*>(this);
130     AddRef();
131     return S_OK;
132 }
133 
134 /***************************************************************
135  * INetConnection Interface
136  */
137 
138 CNetConnection::CNetConnection(PINetConnectionItem pItem) :
139     m_ref(0),
140     m_Props(pItem->Props),
141     m_dwAdapterIndex(pItem->dwAdapterIndex)
142 {
143     if (pItem->Props.pszwName)
144     {
145         m_Props.pszwName = static_cast<PWSTR>(CoTaskMemAlloc((wcslen(pItem->Props.pszwName)+1)*sizeof(WCHAR)));
146         if (m_Props.pszwName)
147             wcscpy(m_Props.pszwName, pItem->Props.pszwName);
148     }
149 
150     if (pItem->Props.pszwDeviceName)
151     {
152         m_Props.pszwDeviceName = static_cast<PWSTR>(CoTaskMemAlloc((wcslen(pItem->Props.pszwDeviceName)+1)*sizeof(WCHAR)));
153         if (m_Props.pszwDeviceName)
154             wcscpy(m_Props.pszwDeviceName, pItem->Props.pszwDeviceName);
155     }
156 }
157 
158 CNetConnection::~CNetConnection()
159 {
160     CoTaskMemFree(m_Props.pszwName);
161     CoTaskMemFree(m_Props.pszwDeviceName);
162 }
163 
164 HRESULT
165 WINAPI
166 CNetConnection::QueryInterface(
167     REFIID iid,
168     LPVOID * ppvObj)
169 {
170     *ppvObj = NULL;
171 
172     if (IsEqualIID(iid, IID_IUnknown) ||
173         IsEqualIID(iid, IID_INetConnection))
174     {
175         *ppvObj = this;
176         AddRef();
177         return S_OK;
178     }
179 
180     return E_NOINTERFACE;
181 }
182 
183 ULONG
184 WINAPI
185 CNetConnection::AddRef()
186 {
187     ULONG refCount = InterlockedIncrement(&m_ref);
188 
189     return refCount;
190 }
191 
192 ULONG
193 WINAPI
194 CNetConnection::Release()
195 {
196     ULONG refCount = InterlockedDecrement(&m_ref);
197 
198     if (!refCount)
199         delete this;
200 
201     return refCount;
202 }
203 
204 HRESULT
205 WINAPI
206 CNetConnection::Connect()
207 {
208     return E_NOTIMPL;
209 }
210 
211 HRESULT
212 WINAPI
213 CNetConnection::Disconnect()
214 {
215     return E_NOTIMPL;
216 }
217 
218 HRESULT
219 WINAPI
220 CNetConnection::Delete()
221 {
222     return E_NOTIMPL;
223 }
224 
225 HRESULT
226 WINAPI
227 CNetConnection::Duplicate(
228     LPCWSTR pszwDuplicateName,
229     INetConnection **ppCon)
230 {
231     return E_NOTIMPL;
232 }
233 
234 HRESULT
235 WINAPI
236 CNetConnection::GetProperties(NETCON_PROPERTIES **ppProps)
237 {
238     MIB_IFROW IfEntry;
239     HKEY hKey;
240     LPOLESTR pStr;
241     WCHAR szName[140];
242     DWORD dwShowIcon, dwType, dwSize;
243     NETCON_PROPERTIES * pProperties;
244     HRESULT hr;
245 
246     if (!ppProps)
247         return E_POINTER;
248 
249     pProperties = static_cast<NETCON_PROPERTIES*>(CoTaskMemAlloc(sizeof(NETCON_PROPERTIES)));
250     if (!pProperties)
251         return E_OUTOFMEMORY;
252 
253     CopyMemory(pProperties, &m_Props, sizeof(NETCON_PROPERTIES));
254     pProperties->pszwName = NULL;
255 
256     if (m_Props.pszwDeviceName)
257     {
258         pProperties->pszwDeviceName = static_cast<LPWSTR>(CoTaskMemAlloc((wcslen(m_Props.pszwDeviceName)+1)*sizeof(WCHAR)));
259         if (pProperties->pszwDeviceName)
260             wcscpy(pProperties->pszwDeviceName, m_Props.pszwDeviceName);
261     }
262 
263     *ppProps = pProperties;
264 
265     /* get updated adapter characteristics */
266     ZeroMemory(&IfEntry, sizeof(IfEntry));
267     IfEntry.dwIndex = m_dwAdapterIndex;
268     if (GetIfEntry(&IfEntry) != NO_ERROR)
269         return NOERROR;
270 
271     NormalizeOperStatus(&IfEntry, pProperties);
272 
273 
274     hr = StringFromCLSID((CLSID)m_Props.guidId, &pStr);
275     if (SUCCEEDED(hr))
276     {
277         wcscpy(szName, L"SYSTEM\\CurrentControlSet\\Control\\Network\\{4D36E972-E325-11CE-BFC1-08002BE10318}\\");
278         wcscat(szName, pStr);
279         wcscat(szName, L"\\Connection");
280 
281         if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, szName, 0, KEY_READ, &hKey) == ERROR_SUCCESS)
282         {
283             dwSize = sizeof(dwShowIcon);
284             if (RegQueryValueExW(hKey, L"ShowIcon", NULL, &dwType, (LPBYTE)&dwShowIcon, &dwSize) == ERROR_SUCCESS && dwType == REG_DWORD)
285             {
286                 if (dwShowIcon)
287                     pProperties->dwCharacter |= NCCF_SHOW_ICON;
288                 else
289                     pProperties->dwCharacter &= ~NCCF_SHOW_ICON;
290             }
291             dwSize = sizeof(szName);
292             if (RegQueryValueExW(hKey, L"Name", NULL, &dwType, (LPBYTE)szName, &dwSize) == ERROR_SUCCESS)
293             {
294                 /* use updated name */
295                 dwSize = wcslen(szName) + 1;
296                 pProperties->pszwName = static_cast<PWSTR>(CoTaskMemAlloc(dwSize * sizeof(WCHAR)));
297                 if (pProperties->pszwName)
298                     CopyMemory(pProperties->pszwName, szName, dwSize * sizeof(WCHAR));
299             }
300             else
301             {
302                 /* use cached name */
303                 if (m_Props.pszwName)
304                 {
305                     pProperties->pszwName = static_cast<PWSTR>(CoTaskMemAlloc((wcslen(m_Props.pszwName)+1)*sizeof(WCHAR)));
306                     if (pProperties->pszwName)
307                         wcscpy(pProperties->pszwName, m_Props.pszwName);
308                 }
309             }
310             RegCloseKey(hKey);
311         }
312         CoTaskMemFree(pStr);
313     }
314 
315     return S_OK;
316 }
317 
318 HRESULT
319 WINAPI
320 CNetConnection::GetUiObjectClassId(CLSID *pclsid)
321 {
322     if (m_Props.MediaType == NCM_LAN)
323     {
324         CopyMemory(pclsid, &CLSID_LanConnectionUi, sizeof(CLSID));
325         return S_OK;
326     }
327 
328     return E_NOTIMPL;
329 }
330 
331 HRESULT
332 WINAPI
333 CNetConnection::Rename(LPCWSTR pszwDuplicateName)
334 {
335     WCHAR szName[140];
336     LPOLESTR pStr;
337     DWORD dwSize;
338     HKEY hKey;
339     HRESULT hr;
340 
341     if (pszwDuplicateName == NULL || wcslen(pszwDuplicateName) == 0)
342         return S_OK;
343 
344     if (m_Props.pszwName)
345     {
346         CoTaskMemFree(m_Props.pszwName);
347         m_Props.pszwName = NULL;
348     }
349 
350     dwSize = (wcslen(pszwDuplicateName) + 1) * sizeof(WCHAR);
351     m_Props.pszwName = static_cast<PWSTR>(CoTaskMemAlloc(dwSize));
352     if (m_Props.pszwName == NULL)
353         return E_OUTOFMEMORY;
354 
355     wcscpy(m_Props.pszwName, pszwDuplicateName);
356 
357     hr = StringFromCLSID((CLSID)m_Props.guidId, &pStr);
358     if (SUCCEEDED(hr))
359     {
360         wcscpy(szName, L"SYSTEM\\CurrentControlSet\\Control\\Network\\{4D36E972-E325-11CE-BFC1-08002BE10318}\\");
361         wcscat(szName, pStr);
362         wcscat(szName, L"\\Connection");
363 
364         if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, szName, 0, KEY_WRITE, &hKey) == ERROR_SUCCESS)
365         {
366             RegSetValueExW(hKey, L"Name", NULL, REG_SZ, (LPBYTE)m_Props.pszwName, dwSize);
367             RegCloseKey(hKey);
368         }
369 
370         CoTaskMemFree(pStr);
371     }
372 
373     return hr;
374 }
375 
376 HRESULT WINAPI IConnection_Constructor(INetConnection **ppv, PINetConnectionItem pItem)
377 {
378     if (!ppv)
379         return E_POINTER;
380 
381     CNetConnection *pConnection = new CNetConnection(pItem);
382     if (!pConnection)
383         return E_OUTOFMEMORY;
384 
385     pConnection->AddRef();
386     *ppv = pConnection;
387 
388     return S_OK;
389 }
390 
391 
392 /***************************************************************
393  * IEnumNetConnection Interface
394  */
395 
396 HRESULT
397 WINAPI
398 CNetConnectionManager::Next(
399     ULONG celt,
400     INetConnection **rgelt,
401     ULONG *pceltFetched)
402 {
403     HRESULT hr;
404 
405     if (!pceltFetched || !rgelt)
406         return E_POINTER;
407 
408     if (celt != 1)
409         return E_FAIL;
410 
411     if (!m_pCurrent)
412         return S_FALSE;
413 
414     hr = IConnection_Constructor(rgelt, m_pCurrent);
415     m_pCurrent = m_pCurrent->Next;
416 
417     return hr;
418 }
419 
420 HRESULT
421 WINAPI
422 CNetConnectionManager::Skip(ULONG celt)
423 {
424     while (m_pCurrent && celt-- > 0)
425         m_pCurrent = m_pCurrent->Next;
426 
427     if (celt)
428        return S_FALSE;
429     else
430        return S_OK;
431 
432 }
433 
434 HRESULT
435 WINAPI
436 CNetConnectionManager::Reset()
437 {
438     m_pCurrent = m_pHead;
439     return S_OK;
440 }
441 
442 HRESULT
443 WINAPI
444 CNetConnectionManager::Clone(IEnumNetConnection **ppenum)
445 {
446     return E_NOTIMPL;
447 }
448 
449 BOOL
450 GetAdapterIndexFromNetCfgInstanceId(PIP_ADAPTER_INFO pAdapterInfo, LPWSTR szNetCfg, PDWORD pIndex)
451 {
452     WCHAR szBuffer[50];
453     IP_ADAPTER_INFO * pCurrentAdapter;
454 
455     pCurrentAdapter = pAdapterInfo;
456     while (pCurrentAdapter)
457     {
458         szBuffer[0] = L'\0';
459         if (MultiByteToWideChar(CP_ACP, 0, pCurrentAdapter->AdapterName, -1, szBuffer, sizeof(szBuffer)/sizeof(szBuffer[0])))
460         {
461             szBuffer[(sizeof(szBuffer)/sizeof(WCHAR))-1] = L'\0';
462         }
463         if (!_wcsicmp(szBuffer, szNetCfg))
464         {
465             *pIndex = pCurrentAdapter->Index;
466             return TRUE;
467         }
468         pCurrentAdapter = pCurrentAdapter->Next;
469     }
470     return FALSE;
471 }
472 
473 VOID
474 NormalizeOperStatus(
475     MIB_IFROW *IfEntry,
476     NETCON_PROPERTIES    * Props)
477 {
478     switch (IfEntry->dwOperStatus)
479     {
480         case MIB_IF_OPER_STATUS_NON_OPERATIONAL:
481             Props->Status = NCS_HARDWARE_DISABLED;
482             break;
483         case MIB_IF_OPER_STATUS_UNREACHABLE:
484             Props->Status = NCS_DISCONNECTED;
485             break;
486         case MIB_IF_OPER_STATUS_DISCONNECTED:
487             Props->Status = NCS_MEDIA_DISCONNECTED;
488             break;
489         case MIB_IF_OPER_STATUS_CONNECTING:
490             Props->Status = NCS_CONNECTING;
491             break;
492         case MIB_IF_OPER_STATUS_CONNECTED:
493             Props->Status = NCS_CONNECTED;
494             break;
495         case MIB_IF_OPER_STATUS_OPERATIONAL:
496             Props->Status = NCS_CONNECTED;
497             break;
498         default:
499             break;
500     }
501 }
502 
503 BOOL
504 CNetConnectionManager::EnumerateINetConnections()
505 {
506     DWORD dwSize, dwResult, dwIndex, dwAdapterIndex, dwShowIcon;
507     MIB_IFTABLE *pIfTable;
508     MIB_IFROW IfEntry;
509     IP_ADAPTER_INFO * pAdapterInfo;
510     HDEVINFO hInfo;
511     SP_DEVINFO_DATA DevInfo;
512     HKEY hSubKey;
513     WCHAR szNetCfg[50];
514     WCHAR szAdapterNetCfg[50];
515     WCHAR szDetail[200] = L"SYSTEM\\CurrentControlSet\\Control\\Class\\";
516     WCHAR szName[130] = L"SYSTEM\\CurrentControlSet\\Control\\Network\\{4D36E972-E325-11CE-BFC1-08002BE10318}\\";
517     PINetConnectionItem pCurrent = NULL;
518 
519     /* get the IfTable */
520     dwSize = 0;
521     if (GetIfTable(NULL, &dwSize, TRUE) != ERROR_INSUFFICIENT_BUFFER)
522         return FALSE;
523 
524     pIfTable = static_cast<PMIB_IFTABLE>(CoTaskMemAlloc(dwSize));
525     if (!pIfTable)
526         return FALSE;
527 
528     dwResult = GetIfTable(pIfTable, &dwSize, TRUE);
529     if (dwResult != NO_ERROR)
530     {
531         CoTaskMemFree(pIfTable);
532         return FALSE;
533     }
534 
535     dwSize = 0;
536     dwResult = GetAdaptersInfo(NULL, &dwSize);
537     if (dwResult!= ERROR_BUFFER_OVERFLOW)
538     {
539         CoTaskMemFree(pIfTable);
540         return FALSE;
541     }
542 
543     pAdapterInfo = static_cast<PIP_ADAPTER_INFO>(CoTaskMemAlloc(dwSize));
544     if (!pAdapterInfo)
545     {
546         CoTaskMemFree(pIfTable);
547         return FALSE;
548     }
549 
550     if (GetAdaptersInfo(pAdapterInfo, &dwSize) != NO_ERROR)
551     {
552         CoTaskMemFree(pIfTable);
553         CoTaskMemFree(pAdapterInfo);
554         return FALSE;
555     }
556 
557     hInfo = SetupDiGetClassDevs(&GUID_DEVCLASS_NET, NULL, NULL, DIGCF_PRESENT );
558     if (!hInfo)
559     {
560         CoTaskMemFree(pIfTable);
561         CoTaskMemFree(pAdapterInfo);
562         return FALSE;
563     }
564 
565     dwIndex = 0;
566     do
567     {
568         ZeroMemory(&DevInfo, sizeof(SP_DEVINFO_DATA));
569         DevInfo.cbSize = sizeof(DevInfo);
570 
571         /* get device info */
572         if (!SetupDiEnumDeviceInfo(hInfo, dwIndex++, &DevInfo))
573             break;
574 
575         /* get device software registry path */
576         if (!SetupDiGetDeviceRegistryPropertyW(hInfo, &DevInfo, SPDRP_DRIVER, NULL, (LPBYTE)&szDetail[39], sizeof(szDetail)/sizeof(WCHAR) - 40, &dwSize))
577             break;
578 
579         /* open device registry key */
580         if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, szDetail, 0, KEY_READ, &hSubKey) != ERROR_SUCCESS)
581             break;
582 
583         /* query NetCfgInstanceId for current device */
584         dwSize = sizeof(szNetCfg);
585         if (RegQueryValueExW(hSubKey, L"NetCfgInstanceId", NULL, NULL, (LPBYTE)szNetCfg, &dwSize) != ERROR_SUCCESS)
586         {
587             RegCloseKey(hSubKey);
588             break;
589         }
590         RegCloseKey(hSubKey);
591 
592         /* get the current adapter index from NetCfgInstanceId */
593         if (!GetAdapterIndexFromNetCfgInstanceId(pAdapterInfo, szNetCfg, &dwAdapterIndex))
594             continue;
595 
596         /* get detailed adapter info */
597         ZeroMemory(&IfEntry, sizeof(IfEntry));
598         IfEntry.dwIndex = dwAdapterIndex;
599         if (GetIfEntry(&IfEntry) != NO_ERROR)
600             break;
601 
602         /* allocate new INetConnectionItem */
603         PINetConnectionItem pNew = static_cast<PINetConnectionItem>(CoTaskMemAlloc(sizeof(INetConnectionItem)));
604         if (!pNew)
605             break;
606 
607         ZeroMemory(pNew, sizeof(INetConnectionItem));
608         pNew->dwAdapterIndex = dwAdapterIndex;
609         /* store NetCfgInstanceId */
610         CLSIDFromString(szNetCfg, &pNew->Props.guidId);
611         NormalizeOperStatus(&IfEntry, &pNew->Props);
612 
613         switch (IfEntry.dwType)
614         {
615             case IF_TYPE_ETHERNET_CSMACD:
616                 pNew->Props.MediaType = NCM_LAN;
617                 break;
618             case IF_TYPE_IEEE80211:
619                 pNew->Props.MediaType = NCM_SHAREDACCESSHOST_RAS;
620                 break;
621             default:
622                 break;
623         }
624         /*  open network connections details */
625         wcscpy(&szName[80], szNetCfg);
626         wcscpy(&szName[118], L"\\Connection");
627 
628         if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, szName, 0, KEY_READ, &hSubKey) == ERROR_SUCCESS)
629         {
630             /* retrieve name of connection */
631             dwSize = sizeof(szAdapterNetCfg);
632             if (RegQueryValueExW(hSubKey, L"Name", NULL, NULL, (LPBYTE)szAdapterNetCfg, &dwSize) == ERROR_SUCCESS)
633             {
634                 pNew->Props.pszwName = static_cast<PWSTR>(CoTaskMemAlloc((wcslen(szAdapterNetCfg)+1) * sizeof(WCHAR)));
635                 if (pNew->Props.pszwName)
636                     wcscpy(pNew->Props.pszwName, szAdapterNetCfg);
637             }
638             dwSize = sizeof(dwShowIcon);
639             if (RegQueryValueExW(hSubKey, L"ShowIcon", NULL, NULL, (LPBYTE)&dwShowIcon, &dwSize) == ERROR_SUCCESS)
640             {
641                 if (dwShowIcon)
642                     pNew->Props.dwCharacter |= NCCF_SHOW_ICON;
643             }
644             RegCloseKey(hSubKey);
645         }
646 
647         /* Get the adapter device description */
648         dwSize = 0;
649         SetupDiGetDeviceRegistryPropertyW(hInfo, &DevInfo, SPDRP_DEVICEDESC, NULL, NULL, 0, &dwSize);
650         if (dwSize != 0)
651         {
652             pNew->Props.pszwDeviceName = static_cast<PWSTR>(CoTaskMemAlloc(dwSize));
653             if (pNew->Props.pszwDeviceName)
654                 SetupDiGetDeviceRegistryPropertyW(hInfo, &DevInfo, SPDRP_DEVICEDESC, NULL, (PBYTE)pNew->Props.pszwDeviceName, dwSize, &dwSize);
655         }
656 
657         if (pCurrent)
658             pCurrent->Next = pNew;
659         else
660             m_pHead = pNew;
661 
662         pCurrent = pNew;
663     } while (TRUE);
664 
665     CoTaskMemFree(pIfTable);
666     CoTaskMemFree(pAdapterInfo);
667     SetupDiDestroyDeviceInfoList(hInfo);
668 
669     m_pCurrent = m_pHead;
670     return TRUE;
671 }
672 
673 HRESULT WINAPI INetConnectionManager_Constructor(IUnknown *pUnkOuter, REFIID riid, LPVOID * ppv)
674 {
675     TRACE("INetConnectionManager_Constructor\n");
676 
677     if (!ppv)
678         return E_POINTER;
679     if (pUnkOuter)
680         return CLASS_E_NOAGGREGATION;
681 
682     CNetConnectionManager *pConnectionMgr = new CNetConnectionManager;
683     if (!pConnectionMgr)
684         return E_OUTOFMEMORY;
685 
686     pConnectionMgr->AddRef();
687     HRESULT hr = pConnectionMgr->QueryInterface(riid, ppv);
688 
689     if (SUCCEEDED(hr))
690         pConnectionMgr->EnumerateINetConnections();
691 
692     pConnectionMgr->Release();
693 
694     return hr;
695 }
696 
697 
698