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.

767 lines
19 KiB

  1. /*++ BUILD Version: 0001 // Increment this if a change has global effects
  2. Copyright (c) 1992 Microsoft Corporation
  3. Module Name:
  4. port.c
  5. Abstract:
  6. Contains functions responsible for data collection from the RAS ports.
  7. Created:
  8. Patrick Y. Ng 12 Aug 93
  9. Revision History
  10. --*/
  11. //
  12. // Include Files
  13. //
  14. #include <nt.h>
  15. #include <ntrtl.h>
  16. #include <nturtl.h>
  17. #include <ntddser.h>
  18. #include <raserror.h>
  19. #include <malloc.h>
  20. #include <windows.h>
  21. #include <string.h>
  22. #include <wcstr.h>
  23. #include "rasctrs.h" // error message definition
  24. #include "perfmsg.h"
  25. #include "perfutil.h"
  26. #include "dataras.h"
  27. #include "globals.h"
  28. #include "port.h"
  29. #include <rasman.h>
  30. #include <serial.h>
  31. #include <isdn.h>
  32. HANDLE ghRasmanLib; // Handle of RASMAN.DLL
  33. #define RASMAN_DLL "rasman.dll"
  34. //
  35. // Function types for the functions in RASMAN.DLL
  36. //
  37. typedef DWORD ( WINAPI *FPRASPORTENUM ) ( HANDLE, LPBYTE, LPDWORD, LPDWORD );
  38. typedef DWORD ( WINAPI *FPRASGETINFO ) (HANDLE, HPORT, RASMAN_INFO* );
  39. typedef DWORD ( WINAPI *FPRASPORTGETSTATISTICS ) (HANDLE, HPORT, LPBYTE, LPDWORD );
  40. typedef DWORD ( WINAPI *FPRASINITIALIZE) ();
  41. typedef DWORD ( WINAPI *FPRASPORTGETBUNDLE) (HANDLE, HPORT, HBUNDLE*);
  42. FPRASPORTENUM lpRasPortEnum;
  43. FPRASGETINFO lpRasGetInfo;
  44. FPRASPORTGETSTATISTICS lpRasPortGetStatistics;
  45. FPRASINITIALIZE lpRasInitialize;
  46. FPRASPORTGETBUNDLE lpRasPortGetBundle;
  47. //
  48. // Pointer to the port table array.
  49. //
  50. PRAS_PORT_DATA gpPortDataArray;
  51. RAS_PORT_STAT gTotalStat;
  52. DWORD gcPorts;
  53. RASMAN_PORT *gpPorts = NULL;
  54. DWORD gPortEnumSize;
  55. DWORD gTotalConnections;
  56. //***
  57. //
  58. // Routine Description:
  59. //
  60. // It will load rasman.dll and call GetProcAddress to obtain all the
  61. // necessary RAS functions.
  62. //
  63. // Arguments:
  64. //
  65. // None.
  66. //
  67. // Return Value:
  68. //
  69. // ERROR_SUCCESS - Successful.
  70. // ERROR_CAN_NOT_COMPLETE - Otherwise.
  71. //
  72. //***
  73. LONG InitRasFunctions()
  74. {
  75. ghRasmanLib = LoadLibrary( RASMAN_DLL );
  76. // log error if unsuccessful
  77. if( !ghRasmanLib )
  78. {
  79. REPORT_ERROR (RASPERF_OPEN_FILE_DRIVER_ERROR, LOG_USER);
  80. // this is fatal, if we can't get data then there's no
  81. // point in continuing.
  82. return ERROR_CAN_NOT_COMPLETE;
  83. }
  84. lpRasInitialize =
  85. (FPRASPORTENUM) GetProcAddress( ghRasmanLib, "RasInitialize" );
  86. lpRasPortEnum =
  87. (FPRASPORTENUM) GetProcAddress( ghRasmanLib, "RasPortEnum" );
  88. lpRasGetInfo =
  89. (FPRASGETINFO) GetProcAddress( ghRasmanLib, "RasGetInfo" );
  90. lpRasPortGetStatistics =
  91. (FPRASPORTGETSTATISTICS) GetProcAddress( ghRasmanLib, "RasPortGetStatistics" );
  92. lpRasPortGetBundle =
  93. (FPRASPORTGETBUNDLE) GetProcAddress( ghRasmanLib, "RasPortGetBundle" );
  94. if( !lpRasInitialize || !lpRasPortEnum || !lpRasGetInfo
  95. || !lpRasPortGetStatistics || !lpRasPortGetBundle)
  96. // || lpRasInitialize() )
  97. {
  98. return ERROR_CAN_NOT_COMPLETE;
  99. }
  100. //
  101. // ANSHULD: BUG: 750860
  102. // This function returns success even if RASMAN service is not running.
  103. // It is the responsibility of the users of the RASMAN functions to make
  104. // sure that the service is running.
  105. //
  106. #if 0
  107. else
  108. {
  109. SC_HANDLE schandle = NULL;
  110. SC_HANDLE svchandle = NULL;
  111. DWORD dwErr = NO_ERROR;
  112. //
  113. // Check to see if rasman service is started.
  114. // fail if it isn't - we don't want ras perf
  115. // to start rasman service.
  116. //
  117. schandle = OpenSCManager(NULL, NULL, SC_MANAGER_CONNECT);
  118. if(NULL != schandle)
  119. {
  120. svchandle = OpenService(schandle,
  121. "RASMAN",
  122. SERVICE_QUERY_STATUS);
  123. if(NULL != svchandle)
  124. {
  125. SERVICE_STATUS status;
  126. if( (!QueryServiceStatus(svchandle, &status))
  127. || (status.dwCurrentState != SERVICE_RUNNING))
  128. {
  129. dwErr = ERROR_CAN_NOT_COMPLETE;
  130. }
  131. CloseServiceHandle(svchandle);
  132. }
  133. CloseServiceHandle(schandle);
  134. }
  135. return dwErr;
  136. }
  137. #endif
  138. return ERROR_SUCCESS;
  139. }
  140. //***
  141. //
  142. // Routine Description:
  143. //
  144. // This routine will call lpRasPortEnum() and generate an array of port
  145. // tables which contains all the information for all the ports such as
  146. // number of bytes transferred, and number of errors, etc.
  147. //
  148. // The remaining initialization work of gRasPortDataDefinition is also
  149. // finished here.
  150. //
  151. // Arguments:
  152. //
  153. // None.
  154. //
  155. // Return Value:
  156. //
  157. // ERROR_SUCCESS - Successful.
  158. // ERROR_CAN_NOT_COMPLETE - Otherwise.
  159. //
  160. //***
  161. LONG InitPortInfo()
  162. {
  163. DWORD Size;
  164. DWORD i;
  165. gPortEnumSize = 0;
  166. gcPorts = 0;
  167. //
  168. // Free the portinfo information we got earlier
  169. //
  170. ClosePortInfo();
  171. if( lpRasPortEnum(NULL, NULL, &gPortEnumSize, &gcPorts) != ERROR_BUFFER_TOO_SMALL )
  172. {
  173. return ERROR_CAN_NOT_COMPLETE;
  174. }
  175. gpPorts = (RASMAN_PORT *) malloc( gPortEnumSize );
  176. if (!gpPorts)
  177. {
  178. return ERROR_CAN_NOT_COMPLETE;
  179. }
  180. if (lpRasPortEnum(NULL, (LPBYTE) gpPorts, &gPortEnumSize, &gcPorts))
  181. {
  182. return ERROR_CAN_NOT_COMPLETE;
  183. }
  184. //
  185. // Generate the array of data tables for all the ports, and fill up the
  186. // name of each port.
  187. //
  188. Size = gcPorts * sizeof( RAS_PORT_DATA );
  189. if(gpPortDataArray)
  190. {
  191. free(gpPortDataArray);
  192. }
  193. gpPortDataArray = ( PRAS_PORT_DATA ) malloc( Size );
  194. if( gpPortDataArray == NULL )
  195. {
  196. return ERROR_CAN_NOT_COMPLETE;
  197. }
  198. memset( gpPortDataArray, 0, Size );
  199. //
  200. // Fill up the names.
  201. //
  202. for( i = 0; i < gcPorts; i++ )
  203. {
  204. //
  205. // Note that the names passed to perfmon are in Unicodes.
  206. //
  207. MultiByteToWideChar( CP_ACP, MB_PRECOMPOSED,
  208. gpPorts[i].P_PortName,
  209. MAX_PORT_NAME,
  210. gpPortDataArray[i].PortName,
  211. MAX_PORT_NAME);
  212. }
  213. //
  214. // Finish the initialization of gRasPortDataDefinition.
  215. //
  216. gRasPortDataDefinition.RasObjectType.TotalByteLength =
  217. sizeof( RAS_PORT_DATA_DEFINITION ) +
  218. gcPorts * ( sizeof( RAS_PORT_INSTANCE_DEFINITION ) +
  219. SIZE_OF_RAS_PORT_PERFORMANCE_DATA );
  220. gRasPortDataDefinition.RasObjectType.NumInstances = gcPorts;
  221. return ERROR_SUCCESS;
  222. }
  223. VOID ClosePortInfo()
  224. {
  225. if(NULL != gpPortDataArray)
  226. {
  227. free( gpPortDataArray );
  228. gpPortDataArray = NULL;
  229. }
  230. if(NULL != gpPorts)
  231. {
  232. free( gpPorts );
  233. gpPorts = NULL;
  234. }
  235. }
  236. DWORD GetNumOfPorts()
  237. {
  238. return gcPorts;
  239. }
  240. LPWSTR GetInstanceName( INT i )
  241. {
  242. return (LPWSTR) gpPortDataArray[i].PortName;
  243. }
  244. VOID GetInstanceData( INT Port, PVOID *lppData )
  245. {
  246. PPERF_COUNTER_BLOCK pPerfCounterBlock;
  247. PDWORD pdwCounter;
  248. PRAS_PORT_STAT pRasPortStat;
  249. pPerfCounterBlock = (PERF_COUNTER_BLOCK *) *lppData;
  250. pPerfCounterBlock->ByteLength = SIZE_OF_RAS_PORT_PERFORMANCE_DATA;
  251. pRasPortStat = &gpPortDataArray[Port].RasPortStat;
  252. //
  253. // Go to end of PerfCounterBlock to get of array of counters
  254. //
  255. pdwCounter = (PDWORD) (&pPerfCounterBlock[1]);
  256. {
  257. ULONG ulBxu = pRasPortStat->BytesTransmittedUncompressed;
  258. ULONG ulBxc = pRasPortStat->BytesTransmittedCompressed;
  259. ULONG ulBx = pRasPortStat->BytesTransmitted;
  260. ULONG ulBxGone = 0;
  261. ULONG ulBxResult = 0;
  262. ULONG ulBru = pRasPortStat->BytesReceivedUncompressed;
  263. ULONG ulBrc = pRasPortStat->BytesReceivedCompressed;
  264. ULONG ulBr = pRasPortStat->BytesReceived;
  265. ULONG ulBrGone = 0;
  266. ULONG ulBrResult = 0;
  267. if (ulBxc <ulBxu) {
  268. ulBxGone = ulBxu - ulBxc;
  269. }
  270. if (ulBrc <ulBru) {
  271. ulBrGone = ulBru - ulBrc;
  272. }
  273. *pdwCounter++ = pRasPortStat->BytesTransmitted + ulBxGone;
  274. *pdwCounter++ = pRasPortStat->BytesReceived + ulBrGone;
  275. *pdwCounter++ = pRasPortStat->FramesTransmitted;
  276. *pdwCounter++ = pRasPortStat->FramesReceived;
  277. if (ulBx + ulBxGone > 100) {
  278. ULONG ulDen = (ulBx + ulBxGone) / 100;
  279. ULONG ulNum = ulBxGone + (ulDen / 2);
  280. ulBxResult = ulNum / ulDen;
  281. }
  282. *pdwCounter++ = ulBxResult; // % bytes compress out
  283. if (ulBr + ulBrGone > 100) {
  284. ULONG ulDen = (ulBr + ulBrGone) / 100;
  285. ULONG ulNum = ulBrGone + (ulDen / 2);
  286. ulBrResult = ulNum / ulDen;
  287. }
  288. *pdwCounter++ = ulBrResult; // % bytes compress in
  289. *pdwCounter++ = pRasPortStat->CRCErrors;
  290. *pdwCounter++ = pRasPortStat->TimeoutErrors;
  291. *pdwCounter++ = pRasPortStat->SerialOverrunErrors;
  292. *pdwCounter++ = pRasPortStat->AlignmentErrors;
  293. *pdwCounter++ = pRasPortStat->BufferOverrunErrors;
  294. *pdwCounter++ = pRasPortStat->TotalErrors;
  295. *pdwCounter++ = pRasPortStat->BytesTransmitted + ulBxGone;
  296. *pdwCounter++ = pRasPortStat->BytesReceived + ulBrGone;
  297. *pdwCounter++ = pRasPortStat->FramesTransmitted;
  298. *pdwCounter++ = pRasPortStat->FramesReceived;
  299. *pdwCounter++ = pRasPortStat->TotalErrors;
  300. }
  301. //
  302. // Update *lppData to the next available byte.
  303. //
  304. *lppData = (PVOID) pdwCounter;
  305. }
  306. VOID GetTotalData( PVOID *lppData )
  307. {
  308. PPERF_COUNTER_BLOCK pPerfCounterBlock;
  309. PDWORD pdwCounter;
  310. pPerfCounterBlock = (PERF_COUNTER_BLOCK *) *lppData;
  311. //DbgPrint("RASCTRS: total bytelength before align = 0x%x\n",
  312. // SIZE_OF_RAS_TOTAL_PERFORMANCE_DATA);
  313. pPerfCounterBlock->ByteLength = ALIGN8(SIZE_OF_RAS_TOTAL_PERFORMANCE_DATA);
  314. //DbgPrint("RASCTRS: total bytelength after align = 0x%x\n",
  315. // pPerfCounterBlock->ByteLength);
  316. //
  317. // Go to end of PerfCounterBlock to get of array of counters
  318. //
  319. pdwCounter = (PDWORD) (&pPerfCounterBlock[1]);
  320. {
  321. ULONG ulBxu = gTotalStat.BytesTransmittedUncompressed;
  322. ULONG ulBxc = gTotalStat.BytesTransmittedCompressed;
  323. ULONG ulBx = gTotalStat.BytesTransmitted;
  324. ULONG ulBxGone = 0;
  325. ULONG ulBxResult = 0;
  326. ULONG ulBru = gTotalStat.BytesReceivedUncompressed;
  327. ULONG ulBrc = gTotalStat.BytesReceivedCompressed;
  328. ULONG ulBr = gTotalStat.BytesReceived;
  329. ULONG ulBrGone = 0;
  330. ULONG ulBrResult = 0;
  331. if (ulBxc <ulBxu) {
  332. ulBxGone = ulBxu - ulBxc;
  333. }
  334. if (ulBrc <ulBru) {
  335. ulBrGone = ulBru - ulBrc;
  336. }
  337. *pdwCounter++ = gTotalStat.BytesTransmitted + ulBxGone;
  338. *pdwCounter++ = gTotalStat.BytesReceived + ulBrGone;
  339. *pdwCounter++ = gTotalStat.FramesTransmitted;
  340. *pdwCounter++ = gTotalStat.FramesReceived;
  341. if (ulBx + ulBxGone > 100) {
  342. ULONG ulDen = (ulBx + ulBxGone) / 100;
  343. ULONG ulNum = ulBxGone + (ulDen / 2);
  344. ulBxResult = ulNum / ulDen;
  345. }
  346. *pdwCounter++ = ulBxResult; // % bytes compress out
  347. if (ulBr + ulBrGone > 100) {
  348. ULONG ulDen = (ulBr + ulBrGone) / 100;
  349. ULONG ulNum = ulBrGone + (ulDen / 2);
  350. ulBrResult = ulNum / ulDen;
  351. }
  352. *pdwCounter++ = ulBrResult; // % bytes compress in
  353. *pdwCounter++ = gTotalStat.CRCErrors;
  354. *pdwCounter++ = gTotalStat.TimeoutErrors;
  355. *pdwCounter++ = gTotalStat.SerialOverrunErrors;
  356. *pdwCounter++ = gTotalStat.AlignmentErrors;
  357. *pdwCounter++ = gTotalStat.BufferOverrunErrors;
  358. *pdwCounter++ = gTotalStat.TotalErrors;
  359. *pdwCounter++ = gTotalStat.BytesTransmitted + ulBxGone;
  360. *pdwCounter++ = gTotalStat.BytesReceived + ulBrGone;
  361. *pdwCounter++ = gTotalStat.FramesTransmitted;
  362. *pdwCounter++ = gTotalStat.FramesReceived;
  363. *pdwCounter++ = gTotalStat.TotalErrors;
  364. *pdwCounter++ = gTotalConnections;
  365. }
  366. //
  367. // Update *lppData to the next available byte.
  368. //
  369. *lppData = (PVOID) ((PBYTE) pPerfCounterBlock + pPerfCounterBlock->ByteLength);
  370. //DbgPrint("RASCTRS : totalcount *lppdata = 0x%x\n", *lppData);
  371. }
  372. //***
  373. //
  374. // Routine Description:
  375. //
  376. // This routine will return the number of gTotalStat.Bytes needed for all the
  377. // objects requested.
  378. //
  379. // Arguments:
  380. //
  381. // None.
  382. //
  383. // Return Value:
  384. //
  385. // The number of gTotalStat.Bytes.
  386. //
  387. //***
  388. ULONG GetSpaceNeeded( BOOL IsRasPortObject, BOOL IsRasTotalObject )
  389. {
  390. ULONG Space = 0;
  391. if( IsRasPortObject )
  392. {
  393. Space += gRasPortDataDefinition.RasObjectType.TotalByteLength;
  394. }
  395. if( IsRasTotalObject )
  396. {
  397. Space += gRasTotalDataDefinition.RasObjectType.TotalByteLength;
  398. }
  399. return Space;
  400. }
  401. //***
  402. //
  403. // Routine Description:
  404. //
  405. // This routine will return the number of bytes needed for all the
  406. // objects requested.
  407. //
  408. // Arguments:
  409. //
  410. // None.
  411. //
  412. // Return Value:
  413. //
  414. // The number of bytes.
  415. //
  416. //***
  417. NTSTATUS CollectRasStatistics()
  418. {
  419. NTSTATUS status;
  420. DWORD i;
  421. HBUNDLE *hBundleArray = NULL;
  422. gTotalConnections = 0;
  423. //
  424. // We also initialize the data structure for the total.
  425. //
  426. memset( &gTotalStat, 0, sizeof( gTotalStat ) );
  427. //
  428. // First we do a lpRasPortEnum to obtain the port connection info.
  429. //
  430. #if 0
  431. status = lpRasPortEnum(NULL, (LPBYTE) gpPorts, &gPortEnumSize, &gcPorts);
  432. if( status != ERROR_SUCCESS )
  433. {
  434. REPORT_ERROR_DATA (RASPERF_RASPORTENUM_FAILED, LOG_USER,
  435. &status, sizeof(status));
  436. return ERROR_CAN_NOT_COMPLETE;
  437. }
  438. #endif
  439. hBundleArray = (HBUNDLE*)malloc(gcPorts * sizeof(HBUNDLE));
  440. if(NULL == hBundleArray)
  441. {
  442. return ERROR_NOT_ENOUGH_MEMORY;
  443. }
  444. memset (hBundleArray, 0, gcPorts * sizeof(HBUNDLE)) ;
  445. if (hBundleArray == NULL) {
  446. return ERROR_CAN_NOT_COMPLETE;
  447. }
  448. for( i = 0; i < gcPorts; i++ )
  449. {
  450. RASMAN_INFO RasmanInfo;
  451. HPORT hPort;
  452. DWORD wSize;
  453. RAS_STATISTICS *pStats;
  454. PRAS_PORT_STAT pData;
  455. BOOLEAN AddTotal;
  456. DWORD n;
  457. HBUNDLE hBundle;
  458. //
  459. // First we want to know if the port is open.
  460. //
  461. if( gpPorts[i].P_Status != OPEN )
  462. {
  463. //
  464. // Reset the port data and continue with next port.
  465. //
  466. memset( &gpPortDataArray[i].RasPortStat,0, sizeof(RAS_PORT_STAT));
  467. continue;
  468. }
  469. hPort = gpPorts[i].P_Handle;
  470. //
  471. // Check if the port is connected.
  472. //
  473. lpRasGetInfo(NULL, hPort, &RasmanInfo );
  474. if( RasmanInfo.RI_ConnState != CONNECTED )
  475. {
  476. //
  477. // Reset the port data and continue with next port.
  478. //
  479. memset( &gpPortDataArray[i].RasPortStat,0, sizeof(RAS_PORT_STAT));
  480. continue;
  481. }
  482. gTotalConnections++;
  483. //
  484. //
  485. // Obtain the statistics for the port.
  486. //
  487. wSize = sizeof(RAS_STATISTICS) +
  488. (NUM_RAS_SERIAL_STATS * sizeof(ULONG));
  489. pStats = (RAS_STATISTICS* )malloc( wSize );
  490. if (!pStats)
  491. {
  492. //
  493. // If it fails then we should return error.
  494. //
  495. status = ERROR_NOT_ENOUGH_MEMORY;
  496. REPORT_ERROR_DATA (RASPERF_NOT_ENOUGH_MEMORY, LOG_USER,
  497. &status, sizeof(status));
  498. return status;
  499. }
  500. lpRasPortGetStatistics( NULL, hPort, (PVOID)pStats, &wSize );
  501. //
  502. // Now store the data in the data array.
  503. //
  504. pData = &(gpPortDataArray[i].RasPortStat);
  505. pData->BytesTransmitted = pStats->S_Statistics[ BYTES_XMITED ];
  506. pData->BytesReceived = pStats->S_Statistics[ BYTES_RCVED ];
  507. pData->FramesTransmitted = pStats->S_Statistics[ FRAMES_XMITED ];
  508. pData->FramesReceived = pStats->S_Statistics[ FRAMES_RCVED ];
  509. pData->CRCErrors = pStats->S_Statistics[ CRC_ERR ];
  510. pData->TimeoutErrors = pStats->S_Statistics[ TIMEOUT_ERR ];
  511. pData->SerialOverrunErrors = pStats->S_Statistics[ SERIAL_OVERRUN_ERR ];
  512. pData->AlignmentErrors = pStats->S_Statistics[ ALIGNMENT_ERR ];
  513. pData->BufferOverrunErrors = pStats->S_Statistics[ BUFFER_OVERRUN_ERR ];
  514. pData->TotalErrors = pStats->S_Statistics[ CRC_ERR ] +
  515. pStats->S_Statistics[ TIMEOUT_ERR ] +
  516. pStats->S_Statistics[ SERIAL_OVERRUN_ERR ] +
  517. pStats->S_Statistics[ ALIGNMENT_ERR ] +
  518. pStats->S_Statistics[ BUFFER_OVERRUN_ERR ];
  519. pData->BytesTransmittedUncompressed = pStats->S_Statistics[ BYTES_XMITED_UNCOMP ];
  520. pData->BytesReceivedUncompressed = pStats->S_Statistics[ BYTES_RCVED_UNCOMP ];
  521. pData->BytesTransmittedCompressed = pStats->S_Statistics[ BYTES_XMITED_COMP ];
  522. pData->BytesReceivedCompressed = pStats->S_Statistics[ BYTES_RCVED_COMP ];
  523. lpRasPortGetBundle( NULL, hPort, &hBundle);
  524. //
  525. // See if we have already added in this bundle's stats
  526. // to the total stats!
  527. //
  528. AddTotal = TRUE;
  529. for (n = 0; n < gcPorts; n++) {
  530. if (hBundle == hBundleArray[n]) {
  531. AddTotal = FALSE;
  532. break;
  533. }
  534. if (NULL == (PVOID)hBundleArray[n]) {
  535. break;
  536. }
  537. }
  538. if (AddTotal) {
  539. hBundleArray[n] = hBundle;
  540. //
  541. // Also update the total data structure
  542. //
  543. gTotalStat.BytesTransmitted += pData->BytesTransmitted;
  544. gTotalStat.BytesReceived += pData->BytesReceived;
  545. gTotalStat.FramesTransmitted += pData->FramesTransmitted;
  546. gTotalStat.FramesReceived += pData->FramesReceived;
  547. gTotalStat.CRCErrors += pData->CRCErrors;
  548. gTotalStat.TimeoutErrors += pData->TimeoutErrors;
  549. gTotalStat.SerialOverrunErrors += pData->SerialOverrunErrors;
  550. gTotalStat.AlignmentErrors += pData->AlignmentErrors;
  551. gTotalStat.BufferOverrunErrors += pData->BufferOverrunErrors;
  552. gTotalStat.BytesTransmittedUncompressed += pData->BytesTransmittedUncompressed;
  553. gTotalStat.BytesReceivedUncompressed += pData->BytesReceivedUncompressed;
  554. gTotalStat.BytesTransmittedCompressed += pData->BytesTransmittedCompressed;
  555. gTotalStat.BytesReceivedCompressed += pData->BytesReceivedCompressed;
  556. gTotalStat.TotalErrors += pData->TotalErrors;
  557. }
  558. free( pStats );
  559. }
  560. free (hBundleArray);
  561. return ERROR_SUCCESS;
  562. }