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.

652 lines
22 KiB

  1. /*++
  2. Copyright (c) 1996 Microsoft Corporation
  3. Module Name:
  4. perfsrv.c
  5. Abstract:
  6. This file implements a Performance Object that presents
  7. Server Performance object data
  8. Created:
  9. Bob Watson 22-Oct-1996
  10. Revision History
  11. --*/
  12. //
  13. // Include Files
  14. //
  15. #include <nt.h>
  16. #include <ntrtl.h>
  17. #include <nturtl.h>
  18. #include <ntddnfs.h>
  19. #include <windows.h>
  20. #include <assert.h>
  21. #include <lmerr.h>
  22. #include <lmapibuf.h>
  23. #include <lmwksta.h>
  24. #include <srvfsctl.h>
  25. #include <winperf.h>
  26. #include <ntprfctr.h>
  27. #include <assert.h>
  28. #define PERF_HEAP hLibHeap
  29. #include <perfutil.h>
  30. #include "perfnet.h"
  31. #include "netsvcmc.h"
  32. #include "datasrv.h"
  33. #include "datasrvq.h"
  34. #define MAX_SRVQ_NAME_LENGTH 16
  35. HANDLE hSrv = NULL;
  36. SRV_QUEUE_STATISTICS *pSrvQueueStatistics = NULL;
  37. DWORD dwDataBufferLength = 0L;
  38. SYSTEM_BASIC_INFORMATION BasicInfo;
  39. BOOL bFsCtlError = FALSE; // print error only once per process
  40. BOOL bSrvQOk = TRUE;
  41. DWORD APIENTRY
  42. OpenServerObject (
  43. IN LPWSTR lpValueName
  44. )
  45. {
  46. STRING DeviceName;
  47. UNICODE_STRING DeviceNameU;
  48. OBJECT_ATTRIBUTES ObjectAttributes;
  49. IO_STATUS_BLOCK IoStatusBlock;
  50. NTSTATUS status;
  51. HANDLE hLocalSrv = NULL;
  52. UNREFERENCED_PARAMETER (lpValueName);
  53. // open the handle to the server for data collection
  54. //
  55. // Get access to the Server for it's data
  56. //
  57. RtlInitString(&DeviceName, SERVER_DEVICE_NAME);
  58. DeviceNameU.Buffer = NULL;
  59. status = RtlAnsiStringToUnicodeString(&DeviceNameU, &DeviceName, TRUE);
  60. if (NT_SUCCESS(status)) {
  61. InitializeObjectAttributes(&ObjectAttributes,
  62. &DeviceNameU,
  63. OBJ_CASE_INSENSITIVE,
  64. NULL,
  65. NULL
  66. );
  67. status = NtOpenFile(&hLocalSrv,
  68. SYNCHRONIZE,
  69. &ObjectAttributes,
  70. &IoStatusBlock,
  71. 0,
  72. FILE_SYNCHRONOUS_IO_NONALERT
  73. );
  74. }
  75. if (!NT_SUCCESS(status)) {
  76. hSrv = NULL;
  77. bSrvQOk = FALSE;
  78. if (!bFsCtlError) {
  79. ReportEvent (hEventLog,
  80. EVENTLOG_ERROR_TYPE,
  81. 0,
  82. PERFNET_UNABLE_OPEN_SERVER,
  83. NULL,
  84. 0,
  85. sizeof(DWORD),
  86. NULL,
  87. (LPVOID)&status);
  88. bFsCtlError = TRUE;
  89. }
  90. }
  91. else {
  92. if (InterlockedCompareExchangePointer(
  93. &hSrv,
  94. hLocalSrv,
  95. NULL) != NULL) {
  96. NtClose(hLocalSrv); // Close duplicate handle
  97. }
  98. }
  99. if (DeviceNameU.Buffer) {
  100. RtlFreeUnicodeString(&DeviceNameU);
  101. }
  102. return (DWORD)RtlNtStatusToDosError(status);
  103. }
  104. DWORD APIENTRY
  105. OpenServerQueueObject (
  106. IN LPWSTR szValueName
  107. )
  108. {
  109. NTSTATUS status;
  110. UNREFERENCED_PARAMETER (szValueName);
  111. //
  112. // collect basic and static processor data
  113. //
  114. status = NtQuerySystemInformation(
  115. SystemBasicInformation,
  116. &BasicInfo,
  117. sizeof(SYSTEM_BASIC_INFORMATION),
  118. NULL
  119. );
  120. assert (NT_SUCCESS(status));
  121. if (!NT_SUCCESS(status)) {
  122. // all we really want is the number of processors so
  123. // if we can't get that from the system, then we'll
  124. // substitute 32 for the number
  125. BasicInfo.NumberOfProcessors = 32;
  126. status = ERROR_SUCCESS;
  127. }
  128. // compute the various buffer sizes required
  129. dwDataBufferLength = sizeof(SRV_QUEUE_STATISTICS) *
  130. (BasicInfo.NumberOfProcessors + 1);
  131. pSrvQueueStatistics = (SRV_QUEUE_STATISTICS *)ALLOCMEM (dwDataBufferLength);
  132. // if memory allocation failed, then no server queue stats will
  133. // be returned.
  134. assert (pSrvQueueStatistics != NULL);
  135. if (pSrvQueueStatistics == NULL) {
  136. bSrvQOk = FALSE;
  137. }
  138. return ERROR_SUCCESS;
  139. }
  140. DWORD APIENTRY
  141. CollectServerObjectData(
  142. IN OUT LPVOID *lppData,
  143. IN OUT LPDWORD lpcbTotalBytes,
  144. IN OUT LPDWORD lpNumObjectTypes
  145. )
  146. /*++
  147. Routine Description:
  148. This routine will return the data for the Physical Disk object
  149. Arguments:
  150. IN OUT LPVOID *lppData
  151. IN: pointer to the address of the buffer to receive the completed
  152. PerfDataBlock and subordinate structures. This routine will
  153. append its data to the buffer starting at the point referenced
  154. by *lppData.
  155. OUT: points to the first byte after the data structure added by this
  156. routine. This routine updated the value at lppdata after appending
  157. its data.
  158. IN OUT LPDWORD lpcbTotalBytes
  159. IN: the address of the DWORD that tells the size in bytes of the
  160. buffer referenced by the lppData argument
  161. OUT: the number of bytes added by this routine is writted to the
  162. DWORD pointed to by this argument
  163. IN OUT LPDWORD NumObjectTypes
  164. IN: the address of the DWORD to receive the number of objects added
  165. by this routine
  166. OUT: the number of objects added by this routine is writted to the
  167. DWORD pointed to by this argument
  168. Returns:
  169. 0 if successful, else Win 32 error code of failure
  170. --*/
  171. {
  172. DWORD TotalLen; // Length of the total return block
  173. NTSTATUS Status = ERROR_SUCCESS;
  174. IO_STATUS_BLOCK IoStatusBlock;
  175. SRV_DATA_DEFINITION *pSrvDataDefinition;
  176. SRV_COUNTER_DATA *pSCD;
  177. SRV_STATISTICS SrvStatistics;
  178. if (hSrv == NULL) {
  179. // bail out if the server didn't get opened.
  180. *lpcbTotalBytes = (DWORD) 0;
  181. *lpNumObjectTypes = (DWORD) 0;
  182. return ERROR_SUCCESS;
  183. }
  184. //
  185. // Check for sufficient space for server data
  186. //
  187. TotalLen = sizeof(SRV_DATA_DEFINITION) +
  188. sizeof(SRV_COUNTER_DATA);
  189. if ( *lpcbTotalBytes < TotalLen ) {
  190. // bail out if the data won't fit in the caller's buffer
  191. // or the server didn't get opened.
  192. *lpcbTotalBytes = (DWORD) 0;
  193. *lpNumObjectTypes = (DWORD) 0;
  194. return ERROR_MORE_DATA;
  195. }
  196. //
  197. // Define objects data block
  198. //
  199. pSrvDataDefinition = (SRV_DATA_DEFINITION *) *lppData;
  200. memcpy (pSrvDataDefinition,
  201. &SrvDataDefinition,
  202. sizeof(SRV_DATA_DEFINITION));
  203. //
  204. // Format and collect server data
  205. //
  206. pSCD = (PSRV_COUNTER_DATA)&pSrvDataDefinition[1];
  207. // test for quadword alignment of the structure
  208. assert (((DWORD)(pSCD) & 0x00000007) == 0);
  209. pSCD->CounterBlock.ByteLength = QWORD_MULTIPLE(sizeof(SRV_COUNTER_DATA));
  210. Status = NtFsControlFile(hSrv,
  211. NULL,
  212. NULL,
  213. NULL,
  214. &IoStatusBlock,
  215. FSCTL_SRV_GET_STATISTICS,
  216. NULL,
  217. 0,
  218. &SrvStatistics,
  219. sizeof(SrvStatistics)
  220. );
  221. if ( NT_SUCCESS(Status) ) {
  222. pSCD->TotalBytes = SrvStatistics.TotalBytesSent.QuadPart +
  223. SrvStatistics.TotalBytesReceived.QuadPart;
  224. pSCD->TotalBytesReceived = SrvStatistics.TotalBytesReceived.QuadPart;
  225. pSCD->TotalBytesSent = SrvStatistics.TotalBytesSent.QuadPart;
  226. pSCD->SessionsTimedOut = SrvStatistics.SessionsTimedOut;
  227. pSCD->SessionsErroredOut = SrvStatistics.SessionsErroredOut;
  228. pSCD->SessionsLoggedOff = SrvStatistics.SessionsLoggedOff;
  229. pSCD->SessionsForcedLogOff = SrvStatistics.SessionsForcedLogOff;
  230. pSCD->LogonErrors = SrvStatistics.LogonErrors;
  231. pSCD->AccessPermissionErrors = SrvStatistics.AccessPermissionErrors;
  232. pSCD->GrantedAccessErrors = SrvStatistics.GrantedAccessErrors;
  233. pSCD->SystemErrors = SrvStatistics.SystemErrors;
  234. pSCD->BlockingSmbsRejected = SrvStatistics.BlockingSmbsRejected;
  235. pSCD->WorkItemShortages = SrvStatistics.WorkItemShortages;
  236. pSCD->TotalFilesOpened = SrvStatistics.TotalFilesOpened;
  237. pSCD->CurrentOpenFiles = SrvStatistics.CurrentNumberOfOpenFiles;
  238. pSCD->CurrentSessions = SrvStatistics.CurrentNumberOfSessions;
  239. pSCD->CurrentOpenSearches = SrvStatistics.CurrentNumberOfOpenSearches;
  240. pSCD->CurrentNonPagedPoolUsage = SrvStatistics.CurrentNonPagedPoolUsage;
  241. pSCD->NonPagedPoolFailures = SrvStatistics.NonPagedPoolFailures;
  242. pSCD->PeakNonPagedPoolUsage = SrvStatistics.PeakNonPagedPoolUsage;
  243. pSCD->CurrentPagedPoolUsage = SrvStatistics.CurrentPagedPoolUsage;
  244. pSCD->PagedPoolFailures = SrvStatistics.PagedPoolFailures;
  245. pSCD->PeakPagedPoolUsage = SrvStatistics.PeakPagedPoolUsage;
  246. pSCD->ContextBlockQueueRate = SrvStatistics.TotalWorkContextBlocksQueued.Count;
  247. pSCD->NetLogon =
  248. pSCD->NetLogonTotal = SrvStatistics.SessionLogonAttempts;
  249. } else {
  250. if (!bFsCtlError) {
  251. // log an event describing the error
  252. DWORD dwData[4];
  253. DWORD dwDataIndex = 0;
  254. dwData[dwDataIndex++] = Status;
  255. dwData[dwDataIndex++] = IoStatusBlock.Status;
  256. dwData[dwDataIndex++] = (DWORD)IoStatusBlock.Information;
  257. ReportEvent (hEventLog,
  258. EVENTLOG_ERROR_TYPE, // error type
  259. 0, // category (not used)
  260. PERFNET_UNABLE_READ_SERVER, // error code
  261. NULL, // SID (not used),
  262. 0, // number of strings
  263. dwDataIndex * sizeof(DWORD), // sizeof raw data
  264. NULL, // message text array
  265. (LPVOID)&dwData[0]); // raw data
  266. bFsCtlError = TRUE;
  267. }
  268. //
  269. // Failure to access Server: clear counters to 0
  270. //
  271. memset(pSCD, 0, sizeof(SRV_COUNTER_DATA));
  272. pSCD->CounterBlock.ByteLength = QWORD_MULTIPLE(sizeof(SRV_COUNTER_DATA));
  273. }
  274. *lpcbTotalBytes = pSrvDataDefinition->SrvObjectType.TotalByteLength
  275. = (DWORD) QWORD_MULTIPLE(((LPBYTE) (& pSCD[1])) - (LPBYTE) pSrvDataDefinition);
  276. * lppData = (LPVOID) (((LPBYTE) pSrvDataDefinition) + * lpcbTotalBytes);
  277. *lpNumObjectTypes = 1;
  278. return ERROR_SUCCESS;
  279. }
  280. DWORD APIENTRY
  281. CollectServerQueueObjectData(
  282. IN OUT LPVOID *lppData,
  283. IN OUT LPDWORD lpcbTotalBytes,
  284. IN OUT LPDWORD lpNumObjectTypes
  285. )
  286. /*++
  287. Routine Description:
  288. This routine will return the data for the Physical Disk object
  289. Arguments:
  290. IN OUT LPVOID *lppData
  291. IN: pointer to the address of the buffer to receive the completed
  292. PerfDataBlock and subordinate structures. This routine will
  293. append its data to the buffer starting at the point referenced
  294. by *lppData.
  295. OUT: points to the first byte after the data structure added by this
  296. routine. This routine updated the value at lppdata after appending
  297. its data.
  298. IN OUT LPDWORD lpcbTotalBytes
  299. IN: the address of the DWORD that tells the size in bytes of the
  300. buffer referenced by the lppData argument
  301. OUT: the number of bytes added by this routine is writted to the
  302. DWORD pointed to by this argument
  303. IN OUT LPDWORD NumObjectTypes
  304. IN: the address of the DWORD to receive the number of objects added
  305. by this routine
  306. OUT: the number of objects added by this routine is writted to the
  307. DWORD pointed to by this argument
  308. Returns:
  309. 0 if successful, else Win 32 error code of failure
  310. --*/
  311. {
  312. DWORD TotalLen; // Length of the total return block
  313. LONG nQueue;
  314. NTSTATUS Status = ERROR_SUCCESS;
  315. IO_STATUS_BLOCK IoStatusBlock;
  316. SRVQ_DATA_DEFINITION *pSrvQDataDefinition;
  317. PERF_INSTANCE_DEFINITION *pPerfInstanceDefinition;
  318. SRVQ_COUNTER_DATA *pSQCD;
  319. SRV_QUEUE_STATISTICS *pThisQueueStatistics;
  320. UNICODE_STRING QueueName;
  321. WCHAR QueueNameBuffer[MAX_SRVQ_NAME_LENGTH];
  322. if (!bSrvQOk) {
  323. *lpcbTotalBytes = (DWORD) 0;
  324. *lpNumObjectTypes = (DWORD) 0;
  325. return ERROR_SUCCESS;
  326. }
  327. //
  328. // Check for sufficient space for server data
  329. //
  330. TotalLen = sizeof(SRVQ_DATA_DEFINITION) +
  331. sizeof(PERF_INSTANCE_DEFINITION) +
  332. sizeof(SRVQ_COUNTER_DATA);
  333. if ( *lpcbTotalBytes < TotalLen ) {
  334. *lpcbTotalBytes = (DWORD) 0;
  335. *lpNumObjectTypes = (DWORD) 0;
  336. return ERROR_MORE_DATA;
  337. }
  338. // assign local pointer to current position in buffer
  339. pSrvQDataDefinition = (SRVQ_DATA_DEFINITION *) *lppData;
  340. //
  341. // Define perf object data block
  342. //
  343. memcpy (pSrvQDataDefinition,
  344. &SrvQDataDefinition,
  345. sizeof(SRVQ_DATA_DEFINITION));
  346. //
  347. // Format and collect server Queue data
  348. //
  349. QueueName.Length = 0;
  350. QueueName.MaximumLength = sizeof(QueueNameBuffer);
  351. QueueName.Buffer = QueueNameBuffer;
  352. Status = NtFsControlFile(hSrv,
  353. NULL,
  354. NULL,
  355. NULL,
  356. &IoStatusBlock,
  357. FSCTL_SRV_GET_QUEUE_STATISTICS,
  358. NULL,
  359. 0,
  360. pSrvQueueStatistics,
  361. dwDataBufferLength
  362. );
  363. if (NT_SUCCESS(Status)) {
  364. // server data was collected successfully so...
  365. // process each processor queue instance.
  366. nQueue = 0;
  367. pPerfInstanceDefinition = (PERF_INSTANCE_DEFINITION *)
  368. &pSrvQDataDefinition[1];
  369. TotalLen = sizeof(SRVQ_DATA_DEFINITION);
  370. for (nQueue = 0; nQueue < BasicInfo.NumberOfProcessors; nQueue++) {
  371. // see if this instance will fit
  372. TotalLen += sizeof(PERF_INSTANCE_DEFINITION) +
  373. 8 + // size of 3 (unicode) digit queuelength name
  374. sizeof(SRVQ_COUNTER_DATA);
  375. if ( *lpcbTotalBytes < TotalLen ) {
  376. *lpcbTotalBytes = (DWORD) 0;
  377. *lpNumObjectTypes = (DWORD) 0;
  378. return ERROR_MORE_DATA;
  379. }
  380. RtlIntegerToUnicodeString(nQueue,
  381. 10,
  382. &QueueName);
  383. // there should be enough room for this instance so initialize it
  384. MonBuildInstanceDefinition(pPerfInstanceDefinition,
  385. (PVOID *) &pSQCD,
  386. 0,
  387. 0,
  388. (DWORD)-1,
  389. QueueName.Buffer);
  390. pSQCD->CounterBlock.ByteLength = QWORD_MULTIPLE(sizeof (SRVQ_COUNTER_DATA));
  391. // initialize pointers for this instance
  392. pThisQueueStatistics = &pSrvQueueStatistics[nQueue];
  393. pSQCD->QueueLength = pThisQueueStatistics->QueueLength;
  394. pSQCD->ActiveThreads = pThisQueueStatistics->ActiveThreads;
  395. pSQCD->AvailableThreads = pThisQueueStatistics->AvailableThreads;
  396. pSQCD->AvailableWorkItems = pThisQueueStatistics->FreeWorkItems;
  397. pSQCD->BorrowedWorkItems = pThisQueueStatistics->StolenWorkItems;
  398. pSQCD->WorkItemShortages = pThisQueueStatistics->NeedWorkItem;
  399. pSQCD->CurrentClients = pThisQueueStatistics->CurrentClients;
  400. pSQCD->TotalBytesTransfered =
  401. pSQCD->BytesReceived = pThisQueueStatistics->BytesReceived.QuadPart;
  402. pSQCD->TotalBytesTransfered +=
  403. pSQCD->BytesSent = pThisQueueStatistics->BytesSent.QuadPart;
  404. pSQCD->TotalOperations =
  405. pSQCD->ReadOperations = pThisQueueStatistics->ReadOperations.QuadPart;
  406. pSQCD->TotalBytes =
  407. pSQCD->BytesRead = pThisQueueStatistics->BytesRead.QuadPart;
  408. pSQCD->TotalOperations +=
  409. pSQCD->WriteOperations = pThisQueueStatistics->WriteOperations.QuadPart;
  410. pSQCD->TotalBytes +=
  411. pSQCD->BytesWritten = pThisQueueStatistics->BytesWritten.QuadPart;
  412. pSQCD->TotalContextBlocksQueued = pThisQueueStatistics->TotalWorkContextBlocksQueued.Count;
  413. // update the current pointer
  414. pPerfInstanceDefinition = (PERF_INSTANCE_DEFINITION *)&pSQCD[1];
  415. }
  416. RtlInitUnicodeString (&QueueName, (LPCWSTR)L"Blocking Queue");
  417. // now load the "blocking" queue data
  418. // see if this instance will fit
  419. TotalLen += sizeof(PERF_INSTANCE_DEFINITION) +
  420. QWORD_MULTIPLE(QueueName.Length + sizeof(WCHAR)) +
  421. sizeof (SRVQ_COUNTER_DATA);
  422. if ( *lpcbTotalBytes < TotalLen ) {
  423. // this instance won't fit so bail out
  424. *lpcbTotalBytes = (DWORD) 0;
  425. *lpNumObjectTypes = (DWORD) 0;
  426. return ERROR_MORE_DATA;
  427. }
  428. // there should be enough room for this instance so initialize it
  429. MonBuildInstanceDefinition(pPerfInstanceDefinition,
  430. (PVOID *) &pSQCD,
  431. 0,
  432. 0,
  433. (DWORD)-1,
  434. QueueName.Buffer);
  435. pSQCD->CounterBlock.ByteLength = QWORD_MULTIPLE(sizeof(SRVQ_COUNTER_DATA));
  436. // initialize pointers for this instance
  437. pThisQueueStatistics = &pSrvQueueStatistics[nQueue];
  438. pSQCD->QueueLength = pThisQueueStatistics->QueueLength;
  439. pSQCD->ActiveThreads = pThisQueueStatistics->ActiveThreads;
  440. pSQCD->AvailableThreads = pThisQueueStatistics->AvailableThreads;
  441. pSQCD->AvailableWorkItems = 0;
  442. pSQCD->BorrowedWorkItems = 0;
  443. pSQCD->WorkItemShortages = 0;
  444. pSQCD->CurrentClients = 0;
  445. pSQCD->TotalBytesTransfered =
  446. pSQCD->BytesReceived = pThisQueueStatistics->BytesReceived.QuadPart;
  447. pSQCD->TotalBytesTransfered +=
  448. pSQCD->BytesSent = pThisQueueStatistics->BytesSent.QuadPart;
  449. pSQCD->ReadOperations = 0;
  450. pSQCD->TotalBytes =
  451. pSQCD->BytesRead = pThisQueueStatistics->BytesRead.QuadPart;
  452. pSQCD->WriteOperations = 0;
  453. pSQCD->TotalBytes +=
  454. pSQCD->BytesWritten = pThisQueueStatistics->BytesWritten.QuadPart;
  455. pSQCD->TotalOperations = 0;
  456. pSQCD->TotalContextBlocksQueued = pThisQueueStatistics->TotalWorkContextBlocksQueued.Count;
  457. nQueue++; // to include the Blocking Queue statistics entry
  458. // update the current pointer
  459. pPerfInstanceDefinition = (PERF_INSTANCE_DEFINITION *)&pSQCD[1];
  460. // update queue (instance) count in object data block
  461. pSrvQDataDefinition->SrvQueueObjectType.NumInstances = nQueue;
  462. // update available length
  463. *lpcbTotalBytes =
  464. pSrvQDataDefinition->SrvQueueObjectType.TotalByteLength =
  465. QWORD_MULTIPLE(
  466. (DWORD)((PCHAR) pPerfInstanceDefinition -
  467. (PCHAR) pSrvQDataDefinition));
  468. #if DBG
  469. if (*lpcbTotalBytes > TotalLen ) {
  470. DbgPrint ("\nPERFNET: Server Queue Perf Ctr. Instance Size Underestimated:");
  471. DbgPrint ("\nPERFNET: Estimated size: %d, Actual Size: %d", TotalLen, *lpcbTotalBytes);
  472. }
  473. #endif
  474. *lppData = (LPVOID) ((PCHAR) pSrvQDataDefinition + *lpcbTotalBytes);
  475. *lpNumObjectTypes = 1;
  476. } else {
  477. if (!bFsCtlError) {
  478. // unable to read server queue data for some reason so don't return this
  479. // object
  480. // log an event describing the error
  481. DWORD dwData[4];
  482. DWORD dwDataIndex = 0;
  483. dwData[dwDataIndex++] = Status;
  484. dwData[dwDataIndex++] = IoStatusBlock.Status;
  485. dwData[dwDataIndex++] = (DWORD)IoStatusBlock.Information;
  486. ReportEvent (hEventLog,
  487. EVENTLOG_ERROR_TYPE, // error type
  488. 0, // category (not used)
  489. PERFNET_UNABLE_READ_SERVER_QUEUE, // error code
  490. NULL, // SID (not used),
  491. 0, // number of strings
  492. dwDataIndex * sizeof(DWORD), // sizeof raw data
  493. NULL, // message text array
  494. (LPVOID)&dwData[0]); // raw data
  495. bFsCtlError = TRUE;
  496. }
  497. *lpcbTotalBytes = (DWORD) 0;
  498. *lpNumObjectTypes = (DWORD) 0;
  499. }
  500. return ERROR_SUCCESS;
  501. }
  502. DWORD APIENTRY
  503. CloseServerObject ()
  504. {
  505. HANDLE hLocalSrv = hSrv;
  506. if (hLocalSrv != NULL) {
  507. if (InterlockedCompareExchangePointer(
  508. &hSrv,
  509. NULL,
  510. hLocalSrv) == hLocalSrv) {
  511. NtClose(hLocalSrv);
  512. }
  513. }
  514. return ERROR_SUCCESS;
  515. }
  516. DWORD APIENTRY
  517. CloseServerQueueObject ()
  518. {
  519. if (hLibHeap != NULL) {
  520. if (pSrvQueueStatistics != NULL) {
  521. FREEMEM (pSrvQueueStatistics);
  522. pSrvQueueStatistics = NULL;
  523. }
  524. }
  525. return ERROR_SUCCESS;
  526. }