Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

698 lines
22 KiB

  1. //+---------------------------------------------------------------------------
  2. //
  3. // Microsoft Windows
  4. // Copyright (C) Microsoft Corporation, 1997.
  5. //
  6. // File: S M L A N . C P P
  7. //
  8. // Contents: The LAN engine that provides statistics to the status monitor
  9. //
  10. // Notes:
  11. //
  12. // Author: CWill 12/02/1997
  13. //
  14. //----------------------------------------------------------------------------
  15. #include "pch.h"
  16. #pragma hdrstop
  17. #include "sminc.h"
  18. #include "smpsh.h"
  19. #include "ndispnp.h"
  20. #include "ntddndis.h"
  21. #include "ncnetcfg.h"
  22. #include "..\\folder\\cfutils.h"
  23. #include "..\\folder\\shutil.h"
  24. #include "naming.h"
  25. //
  26. // External data
  27. //
  28. extern const WCHAR c_szDevice[];
  29. extern const WCHAR c_szSpace[];
  30. extern SM_TOOL_FLAGS g_asmtfMap[];
  31. extern WCHAR c_szCmdLineFlagPrefix[];
  32. const ULONG c_aulConStateMap[] =
  33. {
  34. NCS_DISCONNECTED,
  35. NCS_CONNECTED
  36. };
  37. //+---------------------------------------------------------------------------
  38. //
  39. // Member: CLanStatEngine::CLanStatEngine
  40. //
  41. // Purpose: Creator
  42. //
  43. // Arguments: None
  44. //
  45. // Returns: Nil
  46. //
  47. CLanStatEngine::CLanStatEngine(VOID)
  48. {
  49. m_ncmType = NCM_LAN;
  50. m_ncsmType = NCSM_LAN;
  51. m_dwCharacter = 0;
  52. return;
  53. }
  54. //+---------------------------------------------------------------------------
  55. //
  56. // Member: CLanStatEngine::HrUpdateData
  57. //
  58. // Purpose: Get new statistics from the devices. This data is used to be
  59. // displayed in the UI.
  60. //
  61. // Arguments: pdwChangeFlags - Where to modify the changed flags. This
  62. // param may be NULL.
  63. //
  64. // Returns: Error code
  65. //
  66. HRESULT
  67. CLanStatEngine::HrUpdateData (
  68. DWORD* pdwChangeFlags,
  69. BOOL* pfNoLongerConnected)
  70. {
  71. HRESULT hr = S_OK;
  72. // Initialize the output parameter.
  73. //
  74. *pfNoLongerConnected = FALSE;
  75. UINT uiRet = 0;
  76. NIC_STATISTICS nsNewLanStats = { 0 };
  77. // Prime the structure
  78. //
  79. nsNewLanStats.Size = sizeof(NIC_STATISTICS);
  80. // Retrieve the statistics
  81. //
  82. uiRet = ::NdisQueryStatistics(&m_ustrDevice, &nsNewLanStats);
  83. EnterCriticalSection(&g_csStatmonData);
  84. // Make sure we have a statistics structure
  85. //
  86. if (!m_psmEngineData)
  87. {
  88. m_psmEngineData = new STATMON_ENGINEDATA;
  89. if (m_psmEngineData)
  90. {
  91. ZeroMemory(m_psmEngineData, sizeof(STATMON_ENGINEDATA));
  92. }
  93. }
  94. if (m_psmEngineData)
  95. {
  96. if (uiRet && (MEDIA_STATE_DISCONNECTED != nsNewLanStats.MediaState))
  97. {
  98. AssertSz((c_aulConStateMap[MEDIA_STATE_DISCONNECTED] == NCS_DISCONNECTED)
  99. && (c_aulConStateMap[MEDIA_STATE_CONNECTED] == NCS_CONNECTED),
  100. "Someone is messing around with NETCON_STATUS values");
  101. // Update the change flags if asked for
  102. //
  103. if (pdwChangeFlags)
  104. {
  105. *pdwChangeFlags = SMDCF_NULL;
  106. // Bytes Transmitting
  107. //
  108. if (m_psmEngineData->SMED_PACKETSTRANSMITTING
  109. != nsNewLanStats.PacketsSent)
  110. {
  111. *pdwChangeFlags |= SMDCF_TRANSMITTING;
  112. }
  113. // Bytes Received
  114. //
  115. if (m_psmEngineData->SMED_PACKETSRECEIVING
  116. != nsNewLanStats.DirectedPacketsReceived)
  117. {
  118. *pdwChangeFlags |= SMDCF_RECEIVING;
  119. }
  120. }
  121. // No Compression on LAN devices
  122. //
  123. m_psmEngineData->SMED_COMPRESSIONTRANSMITTING = 0;
  124. m_psmEngineData->SMED_COMPRESSIONRECEIVING = 0;
  125. //
  126. // Update the LAN statistics
  127. //
  128. // LinkSpeed is in 100 bps
  129. //
  130. m_psmEngineData->SMED_SPEEDTRANSMITTING = static_cast<UINT64>(nsNewLanStats.LinkSpeed) * 100;
  131. m_psmEngineData->SMED_SPEEDRECEIVING = static_cast<UINT64>(nsNewLanStats.LinkSpeed) * 100;
  132. Assert((nsNewLanStats.MediaState == MEDIA_STATE_CONNECTED) ||
  133. (nsNewLanStats.MediaState == MEDIA_STATE_DISCONNECTED));
  134. m_psmEngineData->SMED_CONNECTIONSTATUS = c_aulConStateMap[nsNewLanStats.MediaState];
  135. m_psmEngineData->SMED_DURATION = nsNewLanStats.ConnectTime;
  136. m_psmEngineData->SMED_BYTESTRANSMITTING = nsNewLanStats.BytesSent;
  137. m_psmEngineData->SMED_BYTESRECEIVING = nsNewLanStats.DirectedBytesReceived;
  138. m_psmEngineData->SMED_ERRORSTRANSMITTING = nsNewLanStats.PacketsSendErrors;
  139. m_psmEngineData->SMED_ERRORSRECEIVING = nsNewLanStats.PacketsReceiveErrors;
  140. m_psmEngineData->SMED_PACKETSTRANSMITTING = nsNewLanStats.PacketsSent;
  141. m_psmEngineData->SMED_PACKETSRECEIVING = nsNewLanStats.DirectedPacketsReceived;
  142. m_psmEngineData->SMED_INFRASTRUCTURE_MODE = IM_NOT_SUPPORTED;
  143. if (NCSM_WIRELESS == m_ncsmType)
  144. {
  145. DWORD dwInfraStructureMode;
  146. DWORD dwInfraStructureModeSize = sizeof(DWORD);
  147. HRESULT hrT = HrQueryNDISAdapterOID(m_guidId,
  148. OID_802_11_INFRASTRUCTURE_MODE,
  149. &dwInfraStructureModeSize,
  150. &dwInfraStructureMode);
  151. if (SUCCEEDED(hrT))
  152. {
  153. switch (dwInfraStructureMode)
  154. {
  155. case Ndis802_11IBSS:
  156. m_psmEngineData->SMED_INFRASTRUCTURE_MODE = IM_NDIS802_11IBSS;
  157. break;
  158. case Ndis802_11Infrastructure:
  159. m_psmEngineData->SMED_INFRASTRUCTURE_MODE = IM_NDIS802_11INFRASTRUCTURE;
  160. break;
  161. case Ndis802_11AutoUnknown:
  162. m_psmEngineData->SMED_INFRASTRUCTURE_MODE = IM_NDIS802_11AUTOUNKNOWN;
  163. break;
  164. default:
  165. m_psmEngineData->SMED_INFRASTRUCTURE_MODE = IM_NOT_SUPPORTED;
  166. }
  167. }
  168. NDIS_802_11_SSID ndisSSID;
  169. DWORD dwndisSSIDSize = sizeof(NDIS_802_11_SSID);
  170. hrT = HrQueryNDISAdapterOID(m_guidId,
  171. OID_802_11_SSID,
  172. &dwndisSSIDSize,
  173. &ndisSSID);
  174. if (SUCCEEDED(hrT))
  175. {
  176. if (ndisSSID.SsidLength > 1)
  177. {
  178. DWORD dwLen = ndisSSID.SsidLength;
  179. if (dwLen > sizeof(ndisSSID.Ssid))
  180. {
  181. dwLen = sizeof(ndisSSID.Ssid);
  182. AssertSz(FALSE, "Unexpected SSID encountered");
  183. }
  184. ndisSSID.Ssid[dwLen] = 0;
  185. mbstowcs(m_psmEngineData->SMED_802_11_SSID, reinterpret_cast<LPSTR>(ndisSSID.Ssid), celems(m_psmEngineData->SMED_802_11_SSID));
  186. }
  187. }
  188. DWORD dwWepStatus;
  189. DWORD dwWepStatusSize = sizeof(DWORD);
  190. hrT = HrQueryNDISAdapterOID(m_guidId,
  191. OID_802_11_WEP_STATUS,
  192. &dwWepStatusSize,
  193. &dwWepStatus);
  194. if (SUCCEEDED(hrT))
  195. {
  196. if (Ndis802_11WEPEnabled == dwWepStatus)
  197. {
  198. m_psmEngineData->SMED_802_11_ENCRYPTION_ENABLED = TRUE;
  199. }
  200. }
  201. LONG lSignalStrength;
  202. DWORD dwSignalStrengthSize = sizeof(DWORD);
  203. hrT = HrQueryNDISAdapterOID(m_guidId,
  204. OID_802_11_RSSI,
  205. &dwSignalStrengthSize,
  206. &lSignalStrength);
  207. if (SUCCEEDED(hrT))
  208. {
  209. m_psmEngineData->SMED_802_11_SIGNAL_STRENGTH = lSignalStrength;
  210. }
  211. }
  212. }
  213. else
  214. {
  215. *pfNoLongerConnected = TRUE;
  216. // set the connection status to "disconnected" so we can close the UI
  217. m_psmEngineData->SMED_CONNECTIONSTATUS = c_aulConStateMap[NCS_DISCONNECTED];
  218. if (!uiRet)
  219. {
  220. TraceTag(ttidStatMon,
  221. "NdisQueryStatistics failed on %S. err=%u. "
  222. "Treating as disconnected.",
  223. m_strDevice.c_str(),
  224. GetLastError ());
  225. }
  226. else
  227. {
  228. TraceTag(ttidStatMon,
  229. "NdisQueryStatistics returned MediaState = MEDIA_STATE_DISCONNECTED on %S.",
  230. m_strDevice.c_str());
  231. }
  232. hr = S_OK;
  233. }
  234. }
  235. else
  236. {
  237. hr = E_OUTOFMEMORY;
  238. }
  239. LeaveCriticalSection(&g_csStatmonData);
  240. TraceError("CLanStatEngine::HrUpdateData", hr);
  241. return hr;
  242. }
  243. //+---------------------------------------------------------------------------
  244. //
  245. // Member: CLanStatEngine::put_Device
  246. //
  247. // Purpose: Sets the device that is associated with this device
  248. //
  249. // Arguments: pstrDevice - The name of the device
  250. //
  251. // Returns: Error code.
  252. //
  253. HRESULT CLanStatEngine::put_Device(tstring* pstrDevice)
  254. {
  255. HRESULT hr = S_OK;
  256. // Set the new device name
  257. if (pstrDevice)
  258. {
  259. CExceptionSafeComObjectLock EsLock(this);
  260. // Remember the name
  261. m_strDevice = *pstrDevice;
  262. // Make sure we have a nice UNICODE string as well
  263. ::RtlInitUnicodeString(&m_ustrDevice, m_strDevice.c_str());
  264. }
  265. else
  266. {
  267. hr = E_POINTER;
  268. }
  269. TraceError("CLanStatEngine::put_Device", hr);
  270. return hr;
  271. }
  272. //+---------------------------------------------------------------------------
  273. //
  274. // Member: CLanStatEngine::put_MediaType
  275. //
  276. // Purpose: Pass media type of LAN connection type to the LAN engine
  277. //
  278. // Arguments: ncmType - NETCON_MEDIATYPE being set
  279. // ncsmType - NETCON_SUBMEDIATYPE being set
  280. //
  281. // Returns:
  282. //
  283. VOID CLanStatEngine::put_MediaType(NETCON_MEDIATYPE ncmType, NETCON_SUBMEDIATYPE ncsmType)
  284. {
  285. m_ncmType = ncmType;
  286. m_ncsmType = ncsmType;
  287. }
  288. //////////////////////////////////////////////////////////////////////////////
  289. // //
  290. // CPspLanGen //
  291. // //
  292. //////////////////////////////////////////////////////////////////////////////
  293. CPspLanGen::CPspLanGen(VOID)
  294. {
  295. m_ncmType = NCM_LAN;
  296. m_ncsmType = NCSM_LAN;
  297. m_dwCharacter =0;
  298. m_adwHelpIDs = NULL;
  299. return;
  300. }
  301. //////////////////////////////////////////////////////////////////////////////
  302. // //
  303. // CPspLanGen //
  304. // //
  305. //////////////////////////////////////////////////////////////////////////////
  306. VOID CPspLanGen::put_MediaType(NETCON_MEDIATYPE ncmType, NETCON_SUBMEDIATYPE ncsmType)
  307. {
  308. m_ncmType = ncmType;
  309. m_ncsmType = ncsmType;
  310. return;
  311. }
  312. //////////////////////////////////////////////////////////////////////////////
  313. // //
  314. // CPspLanTool //
  315. // //
  316. //////////////////////////////////////////////////////////////////////////////
  317. CPspLanTool::CPspLanTool(VOID)
  318. {
  319. m_ncmType = NCM_LAN;
  320. m_ncsmType = NCSM_LAN;
  321. m_dwCharacter = 0;
  322. return;
  323. }
  324. //+---------------------------------------------------------------------------
  325. //
  326. // Member: CPspLanTool::HrInitToolPageType
  327. //
  328. // Purpose: Gets from the connection any information that is relevant to
  329. // this particular connection type.
  330. //
  331. // Arguments: pncInit - The connection assocatied with this dialog
  332. //
  333. // Returns: Error code
  334. //
  335. HRESULT CPspLanTool::HrInitToolPageType(INetConnection* pncInit)
  336. {
  337. HRESULT hr = S_OK;
  338. INetLanConnection* pnlcInit = NULL;
  339. // Get some LAN specific info
  340. //
  341. hr = HrQIAndSetProxyBlanket(pncInit, &pnlcInit);
  342. if (SUCCEEDED(hr))
  343. {
  344. GUID guidDevice = { 0 };
  345. // Find the component's GUID
  346. //
  347. hr = pnlcInit->GetDeviceGuid(&guidDevice);
  348. if (SUCCEEDED(hr))
  349. {
  350. WCHAR achGuid[c_cchGuidWithTerm];
  351. // Make the device name
  352. //
  353. ::StringFromGUID2(guidDevice, achGuid,
  354. c_cchGuidWithTerm);
  355. m_strDeviceName = c_szDevice;
  356. m_strDeviceName.append(achGuid);
  357. }
  358. ::ReleaseObj(pnlcInit);
  359. }
  360. TraceError("CPspLanTool::HrInitToolPageType", hr);
  361. return hr;
  362. }
  363. //+---------------------------------------------------------------------------
  364. //
  365. // Member: CPspLanTool::HrAddCommandLineFlags
  366. //
  367. // Purpose: Adds the flags for this selection to the command line for the
  368. // tool being launched.
  369. //
  370. // Arguments: pstrFlags - The command line that the flags have to be
  371. // appended to
  372. // psmteSel - The tool entry associated with this selection
  373. //
  374. // Returns: Error code
  375. //
  376. HRESULT CPspLanTool::HrAddCommandLineFlags(tstring* pstrFlags,
  377. CStatMonToolEntry* psmteSel)
  378. {
  379. HRESULT hr = S_OK;
  380. DWORD dwFlags = 0x0;
  381. // Same some indirections
  382. //
  383. dwFlags = psmteSel->dwFlags;
  384. //
  385. // Check what flags are asked for and provide them if we can
  386. //
  387. if (SCLF_ADAPTER & dwFlags)
  388. {
  389. pstrFlags->append(c_szCmdLineFlagPrefix);
  390. pstrFlags->append(g_asmtfMap[STFI_ADAPTER].pszFlag);
  391. pstrFlags->append(c_szSpace);
  392. pstrFlags->append(m_strDeviceName);
  393. }
  394. TraceError("CPspStatusMonitorTool::HrAddCommandLineFlags", hr);
  395. return hr;
  396. }
  397. HRESULT CPspLanTool::HrGetDeviceType(INetConnection* pncInit)
  398. {
  399. TraceFileFunc(ttidStatMon);
  400. UINT uiRet = 0;
  401. NIC_STATISTICS nsLanStats = { 0 };
  402. // Set the default type
  403. m_strDeviceType = L"Ethernet";
  404. // Prime the structure
  405. //
  406. nsLanStats.Size = sizeof(NIC_STATISTICS);
  407. // Retrieve the statistics
  408. //
  409. WCHAR szDeviceGuid[c_cchGuidWithTerm];
  410. ::StringFromGUID2(m_guidId, szDeviceGuid, c_cchGuidWithTerm);
  411. tstring strDeviceName = c_szDevice;
  412. strDeviceName.append(szDeviceGuid);
  413. UNICODE_STRING ustrDevice;
  414. ::RtlInitUnicodeString(&ustrDevice, strDeviceName.c_str());
  415. uiRet = ::NdisQueryStatistics(&ustrDevice, &nsLanStats);
  416. if (uiRet)
  417. {
  418. switch(nsLanStats.MediaType)
  419. {
  420. case NdisMedium802_3:
  421. TraceTag(ttidStatMon, "Medium type: NdisMedium802_3 - Ethernet");
  422. m_strDeviceType = L"Ethernet";
  423. break;
  424. case NdisMedium802_5:
  425. TraceTag(ttidStatMon, "Medium type: NdisMedium802_5 - Token Ring");
  426. m_strDeviceType = L"Token ring";
  427. break;
  428. case NdisMediumFddi:
  429. TraceTag(ttidStatMon, "Medium type: NdisMediumFddi - FDDI");
  430. m_strDeviceType = L"FDDI";
  431. break;
  432. case NdisMediumLocalTalk:
  433. TraceTag(ttidStatMon, "Medium type: NdisMediumLocalTalk - Local Talk");
  434. m_strDeviceType = L"Local Talk";
  435. break;
  436. case NdisMediumAtm:
  437. TraceTag(ttidStatMon, "Medium type: NdisMediumAtm - Atm");
  438. m_strDeviceType = L"Atm";
  439. break;
  440. case NdisMediumIrda:
  441. TraceTag(ttidStatMon, "Medium type: NdisMediumIrda - IRDA");
  442. m_strDeviceType = L"IRDA";
  443. break;
  444. case NdisMediumBpc:
  445. TraceTag(ttidStatMon, "Medium type: NdisMediumBpc - BPC");
  446. m_strDeviceType = L"BPC";
  447. break;
  448. case NdisMediumArcnetRaw:
  449. TraceTag(ttidStatMon, "Medium type: NdisMediumArcnetRaw - ArcnetRaw");
  450. m_strDeviceType = L"ArcnetRaw";
  451. break;
  452. case NdisMediumArcnet878_2:
  453. TraceTag(ttidStatMon, "Medium type: NdisMediumArcnet878_2 - MediumArcnet878_2");
  454. m_strDeviceType = L"MediumArcnet878_2";
  455. break;
  456. case NdisMediumWirelessWan:
  457. TraceTag(ttidStatMon, "Medium type: NdisMediumWirelessWan - WirelessWan");
  458. m_strDeviceType = L"WirelessWan";
  459. break;
  460. case NdisMediumWan:
  461. TraceTag(ttidStatMon, "Medium type: NdisMediumWan - Wan");
  462. m_strDeviceType = L"Wan";
  463. break;
  464. case NdisMediumCoWan:
  465. TraceTag(ttidStatMon, "Medium type: NdisMediumCoWan - CoWan");
  466. m_strDeviceType = L"CoWan";
  467. break;
  468. case NdisMediumMax:
  469. TraceTag(ttidStatMon, "Not real medium type ??? NdisMediumMax");
  470. break;
  471. case NdisMediumDix:
  472. TraceTag(ttidStatMon, "Not real medium type ??? NdisMediumDix");
  473. break;
  474. default:
  475. TraceTag(ttidStatMon, "Unknown medium type ??? %d", nsLanStats.MediaType);
  476. break;
  477. }
  478. }
  479. return S_OK;
  480. }
  481. HRESULT CPspLanTool::HrGetComponentList(INetConnection* pncInit)
  482. {
  483. // Get a readonly INetCfg, enumerate components bound to this adapter
  484. HRESULT hr = S_OK;
  485. INetCfg * pNetCfg = NULL;
  486. PWSTR pszClientDesc = NULL;
  487. BOOL fInitCom = TRUE;
  488. BOOL fWriteLock = FALSE;
  489. // Get a read-only INetCfg
  490. hr = HrCreateAndInitializeINetCfg(&fInitCom, &pNetCfg, fWriteLock, 0,
  491. SzLoadIds(IDS_STATMON_CAPTION),
  492. &pszClientDesc);
  493. if (SUCCEEDED(hr))
  494. {
  495. Assert(pNetCfg);
  496. if (pNetCfg)
  497. {
  498. // Get the INetCfgComponent for the adapter in this connection
  499. // ?? Has the GUID been set already ?
  500. INetCfgComponent * pnccAdapter = NULL;
  501. BOOL fFound = FALSE;
  502. CIterNetCfgComponent nccIter(pNetCfg, &GUID_DEVCLASS_NET);
  503. INetCfgComponent* pnccAdapterTemp = NULL;
  504. while (!fFound && SUCCEEDED(hr) &&
  505. (S_OK == (hr = nccIter.HrNext(&pnccAdapterTemp))))
  506. {
  507. GUID guidDev;
  508. hr = pnccAdapterTemp->GetInstanceGuid(&guidDev);
  509. if (S_OK == hr)
  510. {
  511. if (m_guidId == guidDev)
  512. {
  513. fFound = TRUE;
  514. pnccAdapter = pnccAdapterTemp;
  515. AddRefObj(pnccAdapter);
  516. }
  517. }
  518. ReleaseObj (pnccAdapterTemp);
  519. }
  520. // Enumerate the binding paths from the adapter
  521. // and fill in the components list m_lstpstrCompIds
  522. if (pnccAdapter)
  523. {
  524. HRESULT hrTmp;
  525. PWSTR pszCompId;
  526. // Add the adapter to our list
  527. hrTmp = pnccAdapter->GetId(&pszCompId);
  528. if (SUCCEEDED(hrTmp))
  529. {
  530. if (!FIsStringInList(&m_lstpstrCompIds, pszCompId))
  531. {
  532. m_lstpstrCompIds.push_back(new tstring(pszCompId));
  533. }
  534. CoTaskMemFree(pszCompId);
  535. }
  536. // Add other components to our list
  537. CIterNetCfgUpperBindingPath ncbpIter(pnccAdapter);
  538. INetCfgBindingPath * pncbp;
  539. while(SUCCEEDED(hr) && (hr = ncbpIter.HrNext(&pncbp)) == S_OK)
  540. {
  541. // Note: (tongl 9/17/98): should we only consider enabled paths ?
  542. // If we do, how does the tool list refresh when component bindings change ?
  543. // Also, what about component getting added\removed ? Does the tool list
  544. // need to refresh ??
  545. // Enumerate components on the path and add to our list
  546. CIterNetCfgBindingInterface ncbiIter(pncbp);
  547. INetCfgBindingInterface* pncbi;
  548. while(SUCCEEDED(hr) && (hr = ncbiIter.HrNext(&pncbi)) == S_OK)
  549. {
  550. INetCfgComponent * pnccUpper = NULL;
  551. hrTmp = pncbi->GetUpperComponent(&pnccUpper);
  552. if (SUCCEEDED(hrTmp))
  553. {
  554. PWSTR pszCompId;
  555. hrTmp = pnccUpper->GetId(&pszCompId);
  556. if(SUCCEEDED(hrTmp))
  557. {
  558. if (!FIsStringInList(&m_lstpstrCompIds, pszCompId))
  559. {
  560. m_lstpstrCompIds.push_back(new tstring(pszCompId));
  561. }
  562. CoTaskMemFree(pszCompId);
  563. }
  564. ReleaseObj(pnccUpper);
  565. }
  566. ReleaseObj (pncbi);
  567. }
  568. if (hr == S_FALSE) // We just got to the end of the loop
  569. hr = S_OK;
  570. ReleaseObj(pncbp);
  571. }
  572. if (hr == S_FALSE) // We just got to the end of the loop
  573. hr = S_OK;
  574. }
  575. }
  576. // Release the INetCfg
  577. (VOID) HrUninitializeAndReleaseINetCfg(fInitCom, pNetCfg, fWriteLock);
  578. CoTaskMemFree(pszClientDesc);
  579. }
  580. return hr;
  581. }