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.

752 lines
22 KiB

  1. /*++
  2. Copyright (c) 1996 Microsoft Corporation
  3. Module Name:
  4. faxperf.cpp
  5. Abstract:
  6. This module contains the fax perfom dll code.
  7. Author:
  8. Wesley Witt (wesw) 22-Aug-1996
  9. --*/
  10. #include <windows.h>
  11. #include <winperf.h>
  12. #include "fxscount.h"
  13. #include "faxperf.h"
  14. #include "faxreg.h"
  15. #include "faxutil.h"
  16. #define FAX_NUM_PERF_OBJECT_TYPES 1
  17. #define COUNTER_SIZE sizeof(DWORD)
  18. #define INBOUND_BYTES_OFFSET (COUNTER_SIZE ) // 1
  19. #define INBOUND_FAXES_OFFSET (INBOUND_BYTES_OFFSET + COUNTER_SIZE) // 2
  20. #define INBOUND_PAGES_OFFSET (INBOUND_FAXES_OFFSET + COUNTER_SIZE) // 3
  21. #define INBOUND_MINUTES_OFFSET (INBOUND_PAGES_OFFSET + COUNTER_SIZE) // 4
  22. #define INBOUND_FAILED_RECEIVE_OFFSET (INBOUND_MINUTES_OFFSET + COUNTER_SIZE) // 5
  23. #define OUTBOUND_BYTES_OFFSET (INBOUND_FAILED_RECEIVE_OFFSET + COUNTER_SIZE) // 6
  24. #define OUTBOUND_FAXES_OFFSET (OUTBOUND_BYTES_OFFSET + COUNTER_SIZE) // 7
  25. #define OUTBOUND_PAGES_OFFSET (OUTBOUND_FAXES_OFFSET + COUNTER_SIZE) // 8
  26. #define OUTBOUND_MINUTES_OFFSET (OUTBOUND_PAGES_OFFSET + COUNTER_SIZE) // 9
  27. #define OUTBOUND_FAILED_CONNECTIONS_OFFSET (OUTBOUND_MINUTES_OFFSET + COUNTER_SIZE) // 10
  28. #define OUTBOUND_FAILED_XMIT_OFFSET (OUTBOUND_FAILED_CONNECTIONS_OFFSET + COUNTER_SIZE) // 11
  29. #define TOTAL_BYTES_OFFSET (OUTBOUND_FAILED_XMIT_OFFSET + COUNTER_SIZE) // 12
  30. #define TOTAL_FAXES_OFFSET (TOTAL_BYTES_OFFSET + COUNTER_SIZE) // 13
  31. #define TOTAL_PAGES_OFFSET (TOTAL_FAXES_OFFSET + COUNTER_SIZE) // 14
  32. #define TOTAL_MINUTES_OFFSET (TOTAL_PAGES_OFFSET + COUNTER_SIZE) // 15
  33. #define LAST_COUNTER_OFFSET (TOTAL_MINUTES_OFFSET + COUNTER_SIZE) //
  34. #define SIZE_OF_FAX_PERFORMANCE_DATA LAST_COUNTER_OFFSET
  35. #define PERF_COUNTER_DEFINITION(nm,ty) \
  36. { \
  37. sizeof(PERF_COUNTER_DEFINITION), \
  38. nm, \
  39. 0, \
  40. nm, \
  41. 0, \
  42. 0, \
  43. PERF_DETAIL_NOVICE, \
  44. ty, \
  45. COUNTER_SIZE, \
  46. nm##_OFFSET \
  47. }
  48. #define PERF_COUNTER_INC(nm) \
  49. gs_FaxDataDefinition.nm##.CounterNameTitleIndex += dwFirstCounter; \
  50. gs_FaxDataDefinition.nm##.CounterHelpTitleIndex += dwFirstHelp
  51. #pragma pack (4)
  52. typedef struct _FAX_DATA_DEFINITION {
  53. PERF_OBJECT_TYPE FaxObjectType;
  54. PERF_COUNTER_DEFINITION InboundBytes;
  55. PERF_COUNTER_DEFINITION InboundFaxes;
  56. PERF_COUNTER_DEFINITION InboundPages;
  57. PERF_COUNTER_DEFINITION InboundMinutes;
  58. PERF_COUNTER_DEFINITION InboundFailedReceive;
  59. PERF_COUNTER_DEFINITION OutboundBytes;
  60. PERF_COUNTER_DEFINITION OutboundFaxes;
  61. PERF_COUNTER_DEFINITION OutboundPages;
  62. PERF_COUNTER_DEFINITION OutboundMinutes;
  63. PERF_COUNTER_DEFINITION OutboundFailedConnections;
  64. PERF_COUNTER_DEFINITION OutboundFailedXmit;
  65. PERF_COUNTER_DEFINITION TotalBytes;
  66. PERF_COUNTER_DEFINITION TotalFaxes;
  67. PERF_COUNTER_DEFINITION TotalPages;
  68. PERF_COUNTER_DEFINITION TotalMinutes;
  69. } FAX_DATA_DEFINITION, *PFAX_DATA_DEFINITION;
  70. #pragma pack ()
  71. static FAX_DATA_DEFINITION gs_FaxDataDefinition = {
  72. {
  73. sizeof(FAX_DATA_DEFINITION) + SIZE_OF_FAX_PERFORMANCE_DATA,
  74. sizeof(FAX_DATA_DEFINITION),
  75. sizeof(PERF_OBJECT_TYPE),
  76. FAXOBJ,
  77. 0,
  78. FAXOBJ,
  79. 0,
  80. PERF_DETAIL_NOVICE,
  81. (sizeof(FAX_DATA_DEFINITION)-sizeof(PERF_OBJECT_TYPE))/sizeof(PERF_COUNTER_DEFINITION),
  82. 0,
  83. PERF_NO_INSTANCES,
  84. 0,
  85. },
  86. PERF_COUNTER_DEFINITION( INBOUND_BYTES, PERF_COUNTER_RAWCOUNT ),
  87. PERF_COUNTER_DEFINITION( INBOUND_FAXES, PERF_COUNTER_RAWCOUNT ),
  88. PERF_COUNTER_DEFINITION( INBOUND_PAGES, PERF_COUNTER_RAWCOUNT ),
  89. PERF_COUNTER_DEFINITION( INBOUND_MINUTES, PERF_COUNTER_RAWCOUNT ),
  90. PERF_COUNTER_DEFINITION( INBOUND_FAILED_RECEIVE, PERF_COUNTER_RAWCOUNT ),
  91. PERF_COUNTER_DEFINITION( OUTBOUND_BYTES, PERF_COUNTER_RAWCOUNT ),
  92. PERF_COUNTER_DEFINITION( OUTBOUND_FAXES, PERF_COUNTER_RAWCOUNT ),
  93. PERF_COUNTER_DEFINITION( OUTBOUND_PAGES, PERF_COUNTER_RAWCOUNT ),
  94. PERF_COUNTER_DEFINITION( OUTBOUND_MINUTES, PERF_COUNTER_RAWCOUNT ),
  95. PERF_COUNTER_DEFINITION( OUTBOUND_FAILED_CONNECTIONS, PERF_COUNTER_RAWCOUNT ),
  96. PERF_COUNTER_DEFINITION( OUTBOUND_FAILED_XMIT, PERF_COUNTER_RAWCOUNT ),
  97. PERF_COUNTER_DEFINITION( TOTAL_BYTES, PERF_COUNTER_RAWCOUNT ),
  98. PERF_COUNTER_DEFINITION( TOTAL_FAXES, PERF_COUNTER_RAWCOUNT ),
  99. PERF_COUNTER_DEFINITION( TOTAL_PAGES, PERF_COUNTER_RAWCOUNT ),
  100. PERF_COUNTER_DEFINITION( TOTAL_MINUTES, PERF_COUNTER_RAWCOUNT )
  101. };
  102. #define QUERY_GLOBAL 1
  103. #define QUERY_ITEMS 2
  104. #define QUERY_FOREIGN 3
  105. #define QUERY_COSTLY 4
  106. WCHAR GLOBAL_STRING[] = L"Global";
  107. WCHAR FOREIGN_STRING[] = L"Foreign";
  108. WCHAR COSTLY_STRING[] = L"Costly";
  109. WCHAR NULL_STRING[] = L"\0";
  110. // test for delimiter, end of line and non-digit characters
  111. // used by IsNumberInUnicodeList routine
  112. //
  113. #define DIGIT 1
  114. #define DELIMITER 2
  115. #define INVALID 3
  116. #define EvalThisChar(c,d) ( \
  117. (c == d) ? DELIMITER : \
  118. (c == 0) ? DELIMITER : \
  119. (c < (WCHAR)'0') ? INVALID : \
  120. (c > (WCHAR)'9') ? INVALID : \
  121. DIGIT)
  122. static DWORD gs_dwOpenCount = 0;
  123. static BOOL gs_bInitOK = FALSE;
  124. static HANDLE gs_hMap = NULL;
  125. static PFAX_PERF_COUNTERS gs_pPerfCounters = NULL;
  126. DWORD
  127. GetQueryType (
  128. IN LPWSTR lpValue
  129. )
  130. /*++
  131. GetQueryType
  132. returns the type of query described in the lpValue string so that
  133. the appropriate processing method may be used
  134. Arguments
  135. IN lpValue
  136. string passed to PerfRegQuery Value for processing
  137. Return Value
  138. QUERY_GLOBAL
  139. if lpValue == 0 (null pointer)
  140. lpValue == pointer to Null string
  141. lpValue == pointer to "Global" string
  142. QUERY_FOREIGN
  143. if lpValue == pointer to "Foriegn" string
  144. QUERY_COSTLY
  145. if lpValue == pointer to "Costly" string
  146. otherwise:
  147. QUERY_ITEMS
  148. --*/
  149. {
  150. WCHAR *pwcArgChar, *pwcTypeChar;
  151. BOOL bFound;
  152. if (lpValue == 0)
  153. {
  154. return QUERY_GLOBAL;
  155. }
  156. else if (*lpValue == 0)
  157. {
  158. return QUERY_GLOBAL;
  159. }
  160. // check for "Global" request
  161. pwcArgChar = lpValue;
  162. pwcTypeChar = GLOBAL_STRING;
  163. bFound = TRUE; // assume found until contradicted
  164. // check to the length of the shortest string
  165. while ((*pwcArgChar != 0) && (*pwcTypeChar != 0))
  166. {
  167. if (*pwcArgChar++ != *pwcTypeChar++)
  168. {
  169. bFound = FALSE; // no match
  170. break; // bail out now
  171. }
  172. }
  173. if (bFound) return QUERY_GLOBAL;
  174. // check for "Foreign" request
  175. pwcArgChar = lpValue;
  176. pwcTypeChar = FOREIGN_STRING;
  177. bFound = TRUE; // assume found until contradicted
  178. // check to the length of the shortest string
  179. while ((*pwcArgChar != 0) && (*pwcTypeChar != 0))
  180. {
  181. if (*pwcArgChar++ != *pwcTypeChar++)
  182. {
  183. bFound = FALSE; // no match
  184. break; // bail out now
  185. }
  186. }
  187. if (bFound) return QUERY_FOREIGN;
  188. // check for "Costly" request
  189. pwcArgChar = lpValue;
  190. pwcTypeChar = COSTLY_STRING;
  191. bFound = TRUE; // assume found until contradicted
  192. // check to the length of the shortest string
  193. while ((*pwcArgChar != 0) && (*pwcTypeChar != 0))
  194. {
  195. if (*pwcArgChar++ != *pwcTypeChar++)
  196. {
  197. bFound = FALSE; // no match
  198. break; // bail out now
  199. }
  200. }
  201. if (bFound) return QUERY_COSTLY;
  202. // if not Global and not Foreign and not Costly,
  203. // then it must be an item list
  204. return QUERY_ITEMS;
  205. }
  206. BOOL
  207. IsNumberInUnicodeList (
  208. IN DWORD dwNumber,
  209. IN LPWSTR lpwszUnicodeList
  210. )
  211. /*++
  212. IsNumberInUnicodeList
  213. Arguments:
  214. IN dwNumber
  215. DWORD number to find in list
  216. IN lpwszUnicodeList
  217. Null terminated, Space delimited list of decimal numbers
  218. Return Value:
  219. TRUE:
  220. dwNumber was found in the list of unicode number strings
  221. FALSE:
  222. dwNumber was not found in the list.
  223. --*/
  224. {
  225. DWORD dwThisNumber;
  226. WCHAR *pwcThisChar;
  227. BOOL bValidNumber;
  228. BOOL bNewItem;
  229. WCHAR wcDelimiter; // could be an argument to be more flexible
  230. if (lpwszUnicodeList == 0) return FALSE; // null pointer, # not founde
  231. pwcThisChar = lpwszUnicodeList;
  232. dwThisNumber = 0;
  233. wcDelimiter = (WCHAR)' ';
  234. bValidNumber = FALSE;
  235. bNewItem = TRUE;
  236. while (TRUE)
  237. {
  238. switch (EvalThisChar (*pwcThisChar, wcDelimiter))
  239. {
  240. case DIGIT:
  241. // if this is the first digit after a delimiter, then
  242. // set flags to start computing the new number
  243. if (bNewItem)
  244. {
  245. bNewItem = FALSE;
  246. bValidNumber = TRUE;
  247. }
  248. if (bValidNumber)
  249. {
  250. dwThisNumber *= 10;
  251. dwThisNumber += (*pwcThisChar - (WCHAR)'0');
  252. }
  253. break;
  254. case DELIMITER:
  255. // a delimter is either the delimiter character or the
  256. // end of the string ('\0') if when the delimiter has been
  257. // reached a valid number was found, then compare it to the
  258. // number from the argument list. if this is the end of the
  259. // string and no match was found, then return.
  260. //
  261. if (bValidNumber)
  262. {
  263. if (dwThisNumber == dwNumber) return TRUE;
  264. bValidNumber = FALSE;
  265. }
  266. if (*pwcThisChar == 0)
  267. {
  268. return FALSE;
  269. }
  270. else
  271. {
  272. bNewItem = TRUE;
  273. dwThisNumber = 0;
  274. }
  275. break;
  276. case INVALID:
  277. // if an invalid character was encountered, ignore all
  278. // characters up to the next delimiter and then start fresh.
  279. // the invalid number is not compared.
  280. bValidNumber = FALSE;
  281. break;
  282. default:
  283. break;
  284. }
  285. pwcThisChar++;
  286. }
  287. } // IsNumberInUnicodeList
  288. DWORD APIENTRY
  289. OpenFaxPerformanceData(
  290. LPWSTR lpDeviceNames
  291. )
  292. /*++
  293. Routine Description:
  294. This routine will open and map the memory used by the Fax Service to
  295. pass performance data in. This routine also initializes the data
  296. structures used to pass data back to the registry
  297. Arguments:
  298. Pointer to object ID of each device to be opened
  299. Return Value:
  300. None.
  301. --*/
  302. {
  303. LONG status;
  304. HKEY hKeyDriverPerf = NULL;
  305. DWORD size;
  306. DWORD type;
  307. DWORD dwFirstCounter;
  308. DWORD dwFirstHelp;
  309. DEBUG_FUNCTION_NAME(TEXT("OpenFaxPerformanceData"));
  310. //
  311. // Since SCREG is multi-threaded and will call this routine in
  312. // order to service remote performance queries, this library
  313. // must keep track of how many times it has been opened (i.e.
  314. // how many threads have accessed it). the registry routines will
  315. // limit access to the initialization routine to only one thread
  316. // at a time so synchronization (i.e. reentrancy) should not be
  317. // a problem
  318. //
  319. if (!gs_dwOpenCount)
  320. {
  321. gs_hMap = OpenFileMapping(
  322. FILE_MAP_READ,
  323. FALSE,
  324. FAXPERF_SHARED_MEMORY
  325. );
  326. if (NULL == gs_hMap)
  327. {
  328. //
  329. // Maybe the service is down and the memeory mapped file does not exist.
  330. // Try to create it
  331. //
  332. SECURITY_ATTRIBUTES *pSA;
  333. DWORD Rval;
  334. pSA = CreateSecurityAttributesWithThreadAsOwner (FILE_MAP_READ, FILE_MAP_READ, FILE_MAP_WRITE);
  335. if (!pSA)
  336. {
  337. Rval = GetLastError ();
  338. DebugPrintEx(
  339. DEBUG_ERR,
  340. TEXT("CreateSecurityAttributesWithThreadAsOwner() failed. (ec: %ld)"),
  341. Rval);
  342. }
  343. else
  344. {
  345. gs_hMap = CreateFileMapping(
  346. INVALID_HANDLE_VALUE,
  347. pSA,
  348. PAGE_READWRITE | SEC_COMMIT,
  349. 0,
  350. sizeof(FAX_PERF_COUNTERS),
  351. FAXPERF_SHARED_MEMORY
  352. );
  353. DestroySecurityAttributes (pSA);
  354. }
  355. if (NULL == gs_hMap)
  356. {
  357. goto OpenExitPoint;
  358. }
  359. }
  360. gs_pPerfCounters = (PFAX_PERF_COUNTERS) MapViewOfFile(
  361. gs_hMap,
  362. FILE_MAP_READ,
  363. 0,
  364. 0,
  365. 0
  366. );
  367. if (!gs_pPerfCounters)
  368. {
  369. goto OpenExitPoint;
  370. }
  371. // get counter and help index base values from registry
  372. // Open key to registry entry
  373. // read First Counter and First Help values
  374. // update static data strucutures by adding base to
  375. // offset value in structure.
  376. status = RegOpenKeyEx(
  377. HKEY_LOCAL_MACHINE,
  378. REGKEY_FAXPERF,
  379. 0L,
  380. KEY_QUERY_VALUE,
  381. &hKeyDriverPerf
  382. );
  383. if (status != ERROR_SUCCESS)
  384. {
  385. goto OpenExitPoint;
  386. }
  387. size = sizeof (DWORD);
  388. status = RegQueryValueEx(
  389. hKeyDriverPerf,
  390. "First Counter",
  391. 0L,
  392. &type,
  393. (LPBYTE)&dwFirstCounter,
  394. &size
  395. );
  396. if (status != ERROR_SUCCESS)
  397. {
  398. // this is fatal, if we can't get the base values of the
  399. // counter or help names, then the names won't be available
  400. // to the requesting application so there's not much
  401. // point in continuing.
  402. goto OpenExitPoint;
  403. }
  404. size = sizeof (DWORD);
  405. status = RegQueryValueEx(
  406. hKeyDriverPerf,
  407. "First Help",
  408. 0L,
  409. &type,
  410. (LPBYTE)&dwFirstHelp,
  411. &size
  412. );
  413. if (status != ERROR_SUCCESS)
  414. {
  415. // this is fatal, if we can't get the base values of the
  416. // counter or help names, then the names won't be available
  417. // to the requesting application so there's not much
  418. // point in continuing.
  419. goto OpenExitPoint;
  420. }
  421. gs_FaxDataDefinition.FaxObjectType.ObjectNameTitleIndex += dwFirstCounter;
  422. gs_FaxDataDefinition.FaxObjectType.ObjectHelpTitleIndex += dwFirstHelp;
  423. PERF_COUNTER_INC( InboundBytes );
  424. PERF_COUNTER_INC( InboundFaxes );
  425. PERF_COUNTER_INC( InboundPages );
  426. PERF_COUNTER_INC( InboundMinutes );
  427. PERF_COUNTER_INC( InboundFailedReceive );
  428. PERF_COUNTER_INC( OutboundBytes );
  429. PERF_COUNTER_INC( OutboundFaxes );
  430. PERF_COUNTER_INC( OutboundPages );
  431. PERF_COUNTER_INC( OutboundMinutes );
  432. PERF_COUNTER_INC( OutboundFailedConnections );
  433. PERF_COUNTER_INC( OutboundFailedXmit );
  434. PERF_COUNTER_INC( TotalBytes );
  435. PERF_COUNTER_INC( TotalFaxes );
  436. PERF_COUNTER_INC( TotalPages );
  437. PERF_COUNTER_INC( TotalMinutes );
  438. gs_bInitOK = TRUE; // ok to use this function
  439. }
  440. InterlockedIncrement( (PLONG)&gs_dwOpenCount); // increment OPEN counter
  441. status = ERROR_SUCCESS; // for successful exit
  442. OpenExitPoint:
  443. if (hKeyDriverPerf)
  444. {
  445. RegCloseKey (hKeyDriverPerf);
  446. }
  447. if (!gs_bInitOK)
  448. {
  449. if (gs_pPerfCounters)
  450. {
  451. UnmapViewOfFile(gs_pPerfCounters);
  452. }
  453. if (gs_hMap)
  454. {
  455. CloseHandle( gs_hMap );
  456. }
  457. }
  458. //
  459. // the performance APIs log an error in eventvwr if you fail this call
  460. // so we always return ERROR_SUCCESS so we don't clutter the logs
  461. //
  462. //return status;
  463. return ERROR_SUCCESS;
  464. }
  465. DWORD APIENTRY
  466. CollectFaxPerformanceData(
  467. IN LPWSTR lpValueName,
  468. IN OUT LPVOID *lppData,
  469. IN OUT LPDWORD lpcbTotalBytes,
  470. IN OUT LPDWORD lpNumObjectTypes
  471. )
  472. /*++
  473. Routine Description:
  474. This routine will return the data for the Fax counters.
  475. Arguments:
  476. IN LPWSTR lpValueName
  477. pointer to a wide character string passed by registry.
  478. IN OUT LPVOID *lppData
  479. IN: pointer to the address of the buffer to receive the completed
  480. PerfDataBlock and subordinate structures. This routine will
  481. append its data to the buffer starting at the point referenced
  482. by *lppData.
  483. OUT: points to the first byte after the data structure added by this
  484. routine. This routine updated the value at lppdata after appending
  485. its data.
  486. IN OUT LPDWORD lpcbTotalBytes
  487. IN: the address of the DWORD that tells the size in bytes of the
  488. buffer referenced by the lppData argument
  489. OUT: the number of bytes added by this routine is writted to the
  490. DWORD pointed to by this argument
  491. IN OUT LPDWORD NumObjectTypes
  492. IN: the address of the DWORD to receive the number of objects added
  493. by this routine
  494. OUT: the number of objects added by this routine is writted to the
  495. DWORD pointed to by this argument
  496. Return Value:
  497. ERROR_MORE_DATA if buffer passed is too small to hold data
  498. any error conditions encountered are reported to the event log if
  499. event logging is enabled.
  500. ERROR_SUCCESS if success or any other error. Errors, however are
  501. also reported to the event log.
  502. --*/
  503. {
  504. LPDWORD pData;
  505. ULONG SpaceNeeded;
  506. DWORD dwQueryType;
  507. //
  508. // before doing anything else, see if Open went OK
  509. //
  510. if (!gs_bInitOK)
  511. {
  512. // unable to continue because open failed.
  513. *lpcbTotalBytes = 0;
  514. *lpNumObjectTypes = 0;
  515. return ERROR_SUCCESS;
  516. }
  517. // see if this is a foreign (i.e. non-NT) computer data request
  518. //
  519. dwQueryType = GetQueryType (lpValueName);
  520. if (dwQueryType == QUERY_FOREIGN)
  521. {
  522. // this routine does not service requests for data from
  523. // Non-NT computers
  524. *lpcbTotalBytes = 0;
  525. *lpNumObjectTypes = 0;
  526. return ERROR_SUCCESS;
  527. }
  528. if (dwQueryType == QUERY_ITEMS)
  529. {
  530. if (!(IsNumberInUnicodeList (gs_FaxDataDefinition.FaxObjectType.ObjectNameTitleIndex, lpValueName)))
  531. {
  532. // request received for data object not provided by this routine
  533. *lpcbTotalBytes = 0;
  534. *lpNumObjectTypes = 0;
  535. return ERROR_SUCCESS;
  536. }
  537. }
  538. SpaceNeeded = sizeof(FAX_DATA_DEFINITION) + SIZE_OF_FAX_PERFORMANCE_DATA;
  539. if ( *lpcbTotalBytes < SpaceNeeded )
  540. {
  541. *lpcbTotalBytes = 0;
  542. *lpNumObjectTypes = 0;
  543. return ERROR_MORE_DATA;
  544. }
  545. pData = (LPDWORD) *lppData;
  546. //
  547. // Copy the (constant, initialized) Object Type and counter definitions
  548. // to the caller's data buffer
  549. //
  550. CopyMemory(
  551. pData,
  552. &gs_FaxDataDefinition,
  553. sizeof(FAX_DATA_DEFINITION)
  554. );
  555. pData = (LPDWORD)((LPBYTE)pData + sizeof(FAX_DATA_DEFINITION));
  556. //
  557. // Format and collect Fax data from the service
  558. //
  559. *pData = SIZE_OF_FAX_PERFORMANCE_DATA;
  560. pData += 1;
  561. CopyMemory( pData, gs_pPerfCounters, sizeof(FAX_PERF_COUNTERS) );
  562. pData = (LPDWORD)((LPBYTE)pData + sizeof(FAX_PERF_COUNTERS));
  563. *lpNumObjectTypes = FAX_NUM_PERF_OBJECT_TYPES;
  564. *lpcbTotalBytes = (DWORD)((LPBYTE)pData - (LPBYTE)*lppData);
  565. *lppData = (PVOID) pData;
  566. return ERROR_SUCCESS;
  567. }
  568. DWORD APIENTRY
  569. CloseFaxPerformanceData(
  570. )
  571. /*++
  572. Routine Description:
  573. This routine closes the open handles to Fax performance counters
  574. Arguments:
  575. None.
  576. Return Value:
  577. ERROR_SUCCESS
  578. --*/
  579. {
  580. InterlockedDecrement( (PLONG)&gs_dwOpenCount );
  581. if ((gs_dwOpenCount == 0) && gs_bInitOK)
  582. {
  583. if (gs_pPerfCounters)
  584. {
  585. UnmapViewOfFile(gs_pPerfCounters);
  586. gs_pPerfCounters = NULL;
  587. }
  588. if (gs_hMap)
  589. {
  590. CloseHandle( gs_hMap );
  591. gs_hMap = NULL;
  592. }
  593. gs_bInitOK = FALSE;
  594. }
  595. return ERROR_SUCCESS;
  596. }