[NETAPI32]
[reactos.git] / reactos / dll / shellext / netshell / connectmanager.cpp
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