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