627 lines
20 KiB
C
627 lines
20 KiB
C
/*++
|
||
|
||
Copyright (c) 1996 Microsoft Corporation
|
||
|
||
Module Name:
|
||
|
||
perfsrv.c
|
||
|
||
Abstract:
|
||
|
||
This file implements a Performance Object that presents
|
||
Server Performance object data
|
||
|
||
Created:
|
||
|
||
Bob Watson 22-Oct-1996
|
||
|
||
Revision History
|
||
|
||
|
||
--*/
|
||
//
|
||
// Include Files
|
||
//
|
||
#include <nt.h>
|
||
#include <ntrtl.h>
|
||
#include <nturtl.h>
|
||
#include <ntddnfs.h>
|
||
#include <windows.h>
|
||
#include <assert.h>
|
||
#include <lmerr.h>
|
||
#include <lmapibuf.h>
|
||
#include <lmwksta.h>
|
||
#include <srvfsctl.h>
|
||
#include <winperf.h>
|
||
#include <ntprfctr.h>
|
||
#include <assert.h>
|
||
#include <perfutil.h>
|
||
#include "perfnet.h"
|
||
#include "netsvcmc.h"
|
||
#include "datasrv.h"
|
||
#include "datasrvq.h"
|
||
|
||
#define MAX_SRVQ_NAME_LENGTH 16
|
||
|
||
static HANDLE hSrv = NULL;
|
||
|
||
static SRV_QUEUE_STATISTICS *pSrvQueueStatistics = NULL;
|
||
static DWORD dwDataBufferLength = 0L;
|
||
static SYSTEM_BASIC_INFORMATION BasicInfo;
|
||
|
||
static BOOL bSrvQOk = TRUE;
|
||
|
||
DWORD APIENTRY
|
||
OpenServerObject (
|
||
IN LPWSTR lpValueName
|
||
)
|
||
{
|
||
STRING DeviceName;
|
||
UNICODE_STRING DeviceNameU;
|
||
OBJECT_ATTRIBUTES ObjectAttributes;
|
||
IO_STATUS_BLOCK IoStatusBlock;
|
||
NTSTATUS status;
|
||
|
||
UNREFERENCED_PARAMETER (lpValueName);
|
||
|
||
// open the handle to the server for data collection
|
||
//
|
||
// Get access to the Server for it's data
|
||
//
|
||
|
||
RtlInitString(&DeviceName, SERVER_DEVICE_NAME);
|
||
DeviceNameU.Buffer = NULL;
|
||
status = RtlAnsiStringToUnicodeString(&DeviceNameU, &DeviceName, TRUE);
|
||
if (NT_SUCCESS(status)) {
|
||
InitializeObjectAttributes(&ObjectAttributes,
|
||
&DeviceNameU,
|
||
OBJ_CASE_INSENSITIVE,
|
||
NULL,
|
||
NULL
|
||
);
|
||
|
||
status = NtOpenFile(&hSrv,
|
||
SYNCHRONIZE,
|
||
&ObjectAttributes,
|
||
&IoStatusBlock,
|
||
0,
|
||
FILE_SYNCHRONOUS_IO_NONALERT
|
||
);
|
||
}
|
||
|
||
if (!NT_SUCCESS(status)) {
|
||
hSrv = NULL;
|
||
bSrvQOk = FALSE;
|
||
ReportEvent (hEventLog,
|
||
EVENTLOG_ERROR_TYPE,
|
||
0,
|
||
PERFNET_UNABLE_OPEN_SERVER,
|
||
NULL,
|
||
0,
|
||
sizeof(DWORD),
|
||
NULL,
|
||
(LPVOID)&status);
|
||
}
|
||
|
||
if (DeviceNameU.Buffer) {
|
||
RtlFreeUnicodeString(&DeviceNameU);
|
||
}
|
||
|
||
return (DWORD)RtlNtStatusToDosError(status);
|
||
|
||
}
|
||
|
||
DWORD APIENTRY
|
||
OpenServerQueueObject (
|
||
IN LPWSTR szValueName
|
||
)
|
||
{
|
||
NTSTATUS status;
|
||
|
||
UNREFERENCED_PARAMETER (szValueName);
|
||
//
|
||
// collect basic and static processor data
|
||
//
|
||
|
||
status = NtQuerySystemInformation(
|
||
SystemBasicInformation,
|
||
&BasicInfo,
|
||
sizeof(SYSTEM_BASIC_INFORMATION),
|
||
NULL
|
||
);
|
||
|
||
assert (NT_SUCCESS(status));
|
||
if (!NT_SUCCESS(status)) {
|
||
// all we really want is the number of processors so
|
||
// if we can't get that from the system, then we'll
|
||
// substitute 32 for the number
|
||
BasicInfo.NumberOfProcessors = 32;
|
||
status = ERROR_SUCCESS;
|
||
}
|
||
// compute the various buffer sizes required
|
||
|
||
dwDataBufferLength = sizeof(SRV_QUEUE_STATISTICS) *
|
||
(BasicInfo.NumberOfProcessors + 1);
|
||
|
||
pSrvQueueStatistics = (SRV_QUEUE_STATISTICS *)ALLOCMEM (
|
||
hLibHeap, HEAP_ZERO_MEMORY, dwDataBufferLength);
|
||
|
||
// if memory allocation failed, then no server queue stats will
|
||
// be returned.
|
||
|
||
assert (pSrvQueueStatistics != NULL);
|
||
|
||
if (pSrvQueueStatistics == NULL) {
|
||
bSrvQOk = FALSE;
|
||
}
|
||
|
||
return ERROR_SUCCESS;
|
||
|
||
}
|
||
|
||
DWORD APIENTRY
|
||
CollectServerObjectData(
|
||
IN OUT LPVOID *lppData,
|
||
IN OUT LPDWORD lpcbTotalBytes,
|
||
IN OUT LPDWORD lpNumObjectTypes
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine will return the data for the Physical Disk object
|
||
|
||
Arguments:
|
||
|
||
IN OUT LPVOID *lppData
|
||
IN: pointer to the address of the buffer to receive the completed
|
||
PerfDataBlock and subordinate structures. This routine will
|
||
append its data to the buffer starting at the point referenced
|
||
by *lppData.
|
||
OUT: points to the first byte after the data structure added by this
|
||
routine. This routine updated the value at lppdata after appending
|
||
its data.
|
||
|
||
IN OUT LPDWORD lpcbTotalBytes
|
||
IN: the address of the DWORD that tells the size in bytes of the
|
||
buffer referenced by the lppData argument
|
||
OUT: the number of bytes added by this routine is writted to the
|
||
DWORD pointed to by this argument
|
||
|
||
IN OUT LPDWORD NumObjectTypes
|
||
IN: the address of the DWORD to receive the number of objects added
|
||
by this routine
|
||
OUT: the number of objects added by this routine is writted to the
|
||
DWORD pointed to by this argument
|
||
|
||
Returns:
|
||
|
||
0 if successful, else Win 32 error code of failure
|
||
|
||
--*/
|
||
{
|
||
DWORD TotalLen; // Length of the total return block
|
||
NTSTATUS Status = ERROR_SUCCESS;
|
||
IO_STATUS_BLOCK IoStatusBlock;
|
||
|
||
SRV_DATA_DEFINITION *pSrvDataDefinition;
|
||
SRV_COUNTER_DATA *pSCD;
|
||
|
||
SRV_STATISTICS SrvStatistics;
|
||
|
||
if (hSrv == NULL) {
|
||
// bail out if the server didn't get opened.
|
||
*lpcbTotalBytes = (DWORD) 0;
|
||
*lpNumObjectTypes = (DWORD) 0;
|
||
return ERROR_SUCCESS;
|
||
}
|
||
|
||
//
|
||
// Check for sufficient space for server data
|
||
//
|
||
|
||
TotalLen = sizeof(SRV_DATA_DEFINITION) +
|
||
sizeof(SRV_COUNTER_DATA);
|
||
|
||
if ( *lpcbTotalBytes < TotalLen ) {
|
||
// bail out if the data won't fit in the caller's buffer
|
||
// or the server didn't get opened.
|
||
*lpcbTotalBytes = (DWORD) 0;
|
||
*lpNumObjectTypes = (DWORD) 0;
|
||
return ERROR_MORE_DATA;
|
||
}
|
||
|
||
//
|
||
// Define objects data block
|
||
//
|
||
|
||
pSrvDataDefinition = (SRV_DATA_DEFINITION *) *lppData;
|
||
|
||
memcpy (pSrvDataDefinition,
|
||
&SrvDataDefinition,
|
||
sizeof(SRV_DATA_DEFINITION));
|
||
|
||
//
|
||
// Format and collect server data
|
||
//
|
||
|
||
pSCD = (PSRV_COUNTER_DATA)&pSrvDataDefinition[1];
|
||
|
||
// test for quadword alignment of the structure
|
||
assert (((DWORD)(pSCD) & 0x00000007) == 0);
|
||
|
||
pSCD->CounterBlock.ByteLength = sizeof(SRV_COUNTER_DATA);
|
||
|
||
Status = NtFsControlFile(hSrv,
|
||
NULL,
|
||
NULL,
|
||
NULL,
|
||
&IoStatusBlock,
|
||
FSCTL_SRV_GET_STATISTICS,
|
||
NULL,
|
||
0,
|
||
&SrvStatistics,
|
||
sizeof(SrvStatistics)
|
||
);
|
||
|
||
if ( NT_SUCCESS(Status) ) {
|
||
pSCD->TotalBytes = SrvStatistics.TotalBytesSent.QuadPart +
|
||
SrvStatistics.TotalBytesReceived.QuadPart;
|
||
|
||
pSCD->TotalBytesReceived = SrvStatistics.TotalBytesReceived.QuadPart;
|
||
pSCD->TotalBytesSent = SrvStatistics.TotalBytesSent.QuadPart;
|
||
pSCD->SessionsTimedOut = SrvStatistics.SessionsTimedOut;
|
||
pSCD->SessionsErroredOut = SrvStatistics.SessionsErroredOut;
|
||
pSCD->SessionsLoggedOff = SrvStatistics.SessionsLoggedOff;
|
||
pSCD->SessionsForcedLogOff = SrvStatistics.SessionsForcedLogOff;
|
||
pSCD->LogonErrors = SrvStatistics.LogonErrors;
|
||
pSCD->AccessPermissionErrors = SrvStatistics.AccessPermissionErrors;
|
||
pSCD->GrantedAccessErrors = SrvStatistics.GrantedAccessErrors;
|
||
pSCD->SystemErrors = SrvStatistics.SystemErrors;
|
||
pSCD->BlockingSmbsRejected = SrvStatistics.BlockingSmbsRejected;
|
||
pSCD->WorkItemShortages = SrvStatistics.WorkItemShortages;
|
||
pSCD->TotalFilesOpened = SrvStatistics.TotalFilesOpened;
|
||
pSCD->CurrentOpenFiles = SrvStatistics.CurrentNumberOfOpenFiles;
|
||
pSCD->CurrentSessions = SrvStatistics.CurrentNumberOfSessions;
|
||
pSCD->CurrentOpenSearches = SrvStatistics.CurrentNumberOfOpenSearches;
|
||
pSCD->CurrentNonPagedPoolUsage = SrvStatistics.CurrentNonPagedPoolUsage;
|
||
pSCD->NonPagedPoolFailures = SrvStatistics.NonPagedPoolFailures;
|
||
pSCD->PeakNonPagedPoolUsage = SrvStatistics.PeakNonPagedPoolUsage;
|
||
pSCD->CurrentPagedPoolUsage = SrvStatistics.CurrentPagedPoolUsage;
|
||
pSCD->PagedPoolFailures = SrvStatistics.PagedPoolFailures;
|
||
pSCD->PeakPagedPoolUsage = SrvStatistics.PeakPagedPoolUsage;
|
||
pSCD->ContextBlockQueueRate = SrvStatistics.TotalWorkContextBlocksQueued.Count;
|
||
pSCD->NetLogon =
|
||
pSCD->NetLogonTotal = SrvStatistics.SessionLogonAttempts;
|
||
|
||
} else {
|
||
|
||
// log an event describing the error
|
||
DWORD dwData[4];
|
||
DWORD dwDataIndex = 0;
|
||
|
||
dwData[dwDataIndex++] = Status;
|
||
dwData[dwDataIndex++] = IoStatusBlock.Status;
|
||
dwData[dwDataIndex++] = (DWORD)IoStatusBlock.Information;
|
||
|
||
ReportEvent (hEventLog,
|
||
EVENTLOG_ERROR_TYPE, // error type
|
||
0, // category (not used)
|
||
PERFNET_UNABLE_READ_SERVER, // error code
|
||
NULL, // SID (not used),
|
||
0, // number of strings
|
||
dwDataIndex * sizeof(DWORD), // sizeof raw data
|
||
NULL, // message text array
|
||
(LPVOID)&dwData[0]); // raw data
|
||
//
|
||
// Failure to access Server: clear counters to 0
|
||
//
|
||
|
||
memset(pSCD, 0, sizeof(SRV_COUNTER_DATA));
|
||
pSCD->CounterBlock.ByteLength = sizeof(SRV_COUNTER_DATA);
|
||
}
|
||
|
||
*lppData = (LPVOID)&pSCD[1];
|
||
*lpcbTotalBytes = (DWORD)((LPBYTE)&pSCD[1] - (LPBYTE)pSrvDataDefinition);
|
||
*lpNumObjectTypes = 1;
|
||
return ERROR_SUCCESS;
|
||
}
|
||
|
||
DWORD APIENTRY
|
||
CollectServerQueueObjectData(
|
||
IN OUT LPVOID *lppData,
|
||
IN OUT LPDWORD lpcbTotalBytes,
|
||
IN OUT LPDWORD lpNumObjectTypes
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine will return the data for the Physical Disk object
|
||
|
||
Arguments:
|
||
|
||
IN OUT LPVOID *lppData
|
||
IN: pointer to the address of the buffer to receive the completed
|
||
PerfDataBlock and subordinate structures. This routine will
|
||
append its data to the buffer starting at the point referenced
|
||
by *lppData.
|
||
OUT: points to the first byte after the data structure added by this
|
||
routine. This routine updated the value at lppdata after appending
|
||
its data.
|
||
|
||
IN OUT LPDWORD lpcbTotalBytes
|
||
IN: the address of the DWORD that tells the size in bytes of the
|
||
buffer referenced by the lppData argument
|
||
OUT: the number of bytes added by this routine is writted to the
|
||
DWORD pointed to by this argument
|
||
|
||
IN OUT LPDWORD NumObjectTypes
|
||
IN: the address of the DWORD to receive the number of objects added
|
||
by this routine
|
||
OUT: the number of objects added by this routine is writted to the
|
||
DWORD pointed to by this argument
|
||
|
||
Returns:
|
||
|
||
0 if successful, else Win 32 error code of failure
|
||
|
||
--*/
|
||
{
|
||
DWORD TotalLen; // Length of the total return block
|
||
LONG nQueue;
|
||
|
||
NTSTATUS Status = ERROR_SUCCESS;
|
||
IO_STATUS_BLOCK IoStatusBlock;
|
||
|
||
SRVQ_DATA_DEFINITION *pSrvQDataDefinition;
|
||
PERF_INSTANCE_DEFINITION *pPerfInstanceDefinition;
|
||
SRVQ_COUNTER_DATA *pSQCD;
|
||
|
||
|
||
SRV_QUEUE_STATISTICS *pThisQueueStatistics;
|
||
|
||
UNICODE_STRING QueueName;
|
||
WCHAR QueueNameBuffer[MAX_SRVQ_NAME_LENGTH];
|
||
|
||
if (!bSrvQOk) {
|
||
*lpcbTotalBytes = (DWORD) 0;
|
||
*lpNumObjectTypes = (DWORD) 0;
|
||
return ERROR_SUCCESS;
|
||
}
|
||
|
||
//
|
||
// Check for sufficient space for server data
|
||
//
|
||
|
||
TotalLen = sizeof(SRVQ_DATA_DEFINITION) +
|
||
sizeof(PERF_INSTANCE_DEFINITION) +
|
||
sizeof(SRVQ_COUNTER_DATA);
|
||
|
||
if ( *lpcbTotalBytes < TotalLen ) {
|
||
*lpcbTotalBytes = (DWORD) 0;
|
||
*lpNumObjectTypes = (DWORD) 0;
|
||
return ERROR_MORE_DATA;
|
||
}
|
||
|
||
// assign local pointer to current position in buffer
|
||
pSrvQDataDefinition = (SRVQ_DATA_DEFINITION *) *lppData;
|
||
|
||
//
|
||
// Define perf object data block
|
||
//
|
||
|
||
memcpy (pSrvQDataDefinition,
|
||
&SrvQDataDefinition,
|
||
sizeof(SRVQ_DATA_DEFINITION));
|
||
|
||
//
|
||
// Format and collect server Queue data
|
||
//
|
||
|
||
QueueName.Length = 0;
|
||
QueueName.MaximumLength = sizeof(QueueNameBuffer);
|
||
QueueName.Buffer = QueueNameBuffer;
|
||
|
||
Status = NtFsControlFile(hSrv,
|
||
NULL,
|
||
NULL,
|
||
NULL,
|
||
&IoStatusBlock,
|
||
FSCTL_SRV_GET_QUEUE_STATISTICS,
|
||
NULL,
|
||
0,
|
||
pSrvQueueStatistics,
|
||
dwDataBufferLength
|
||
);
|
||
|
||
if (NT_SUCCESS(Status)) {
|
||
// server data was collected successfully so...
|
||
// process each processor queue instance.
|
||
|
||
nQueue = 0;
|
||
pPerfInstanceDefinition = (PERF_INSTANCE_DEFINITION *)
|
||
&pSrvQDataDefinition[1];
|
||
|
||
TotalLen = sizeof(SRVQ_DATA_DEFINITION);
|
||
|
||
for (nQueue = 0; nQueue < BasicInfo.NumberOfProcessors; nQueue++) {
|
||
// see if this instance will fit
|
||
TotalLen += sizeof(PERF_INSTANCE_DEFINITION) +
|
||
8 + // size of 3 (unicode) digit queuelength name
|
||
sizeof(SRVQ_COUNTER_DATA);
|
||
|
||
if ( *lpcbTotalBytes < TotalLen ) {
|
||
*lpcbTotalBytes = (DWORD) 0;
|
||
*lpNumObjectTypes = (DWORD) 0;
|
||
return ERROR_MORE_DATA;
|
||
}
|
||
|
||
RtlIntegerToUnicodeString(nQueue,
|
||
10,
|
||
&QueueName);
|
||
|
||
// there should be enough room for this instance so initialize it
|
||
|
||
MonBuildInstanceDefinition(pPerfInstanceDefinition,
|
||
(PVOID *) &pSQCD,
|
||
0,
|
||
0,
|
||
(DWORD)-1,
|
||
QueueName.Buffer);
|
||
|
||
pSQCD->CounterBlock.ByteLength = sizeof (SRVQ_COUNTER_DATA);
|
||
|
||
// initialize pointers for this instance
|
||
pThisQueueStatistics = &pSrvQueueStatistics[nQueue];
|
||
|
||
pSQCD->QueueLength = pThisQueueStatistics->QueueLength;
|
||
pSQCD->ActiveThreads = pThisQueueStatistics->ActiveThreads;
|
||
pSQCD->AvailableThreads = pThisQueueStatistics->AvailableThreads;
|
||
pSQCD->AvailableWorkItems = pThisQueueStatistics->FreeWorkItems;
|
||
pSQCD->BorrowedWorkItems = pThisQueueStatistics->StolenWorkItems;
|
||
pSQCD->WorkItemShortages = pThisQueueStatistics->NeedWorkItem;
|
||
pSQCD->CurrentClients = pThisQueueStatistics->CurrentClients;
|
||
pSQCD->TotalBytesTransfered =
|
||
pSQCD->BytesReceived = pThisQueueStatistics->BytesReceived.QuadPart;
|
||
pSQCD->TotalBytesTransfered +=
|
||
pSQCD->BytesSent = pThisQueueStatistics->BytesSent.QuadPart;
|
||
pSQCD->TotalOperations =
|
||
pSQCD->ReadOperations = pThisQueueStatistics->ReadOperations.QuadPart;
|
||
pSQCD->TotalBytes =
|
||
pSQCD->BytesRead = pThisQueueStatistics->BytesRead.QuadPart;
|
||
pSQCD->TotalOperations +=
|
||
pSQCD->WriteOperations = pThisQueueStatistics->WriteOperations.QuadPart;
|
||
pSQCD->TotalBytes +=
|
||
pSQCD->BytesWritten = pThisQueueStatistics->BytesWritten.QuadPart;
|
||
pSQCD->TotalContextBlocksQueued = pThisQueueStatistics->TotalWorkContextBlocksQueued.Count;
|
||
|
||
// update the current pointer
|
||
pPerfInstanceDefinition = (PERF_INSTANCE_DEFINITION *)&pSQCD[1];
|
||
}
|
||
|
||
RtlInitUnicodeString (&QueueName, (LPCWSTR)L"Blocking Queue");
|
||
|
||
// now load the "blocking" queue data
|
||
// see if this instance will fit
|
||
TotalLen += sizeof(PERF_INSTANCE_DEFINITION) +
|
||
QWORD_MULTIPLE(QueueName.Length + sizeof(WCHAR)) +
|
||
sizeof (SRVQ_COUNTER_DATA);
|
||
|
||
if ( *lpcbTotalBytes < TotalLen ) {
|
||
// this instance won't fit so bail out
|
||
*lpcbTotalBytes = (DWORD) 0;
|
||
*lpNumObjectTypes = (DWORD) 0;
|
||
return ERROR_MORE_DATA;
|
||
}
|
||
|
||
// there should be enough room for this instance so initialize it
|
||
|
||
MonBuildInstanceDefinition(pPerfInstanceDefinition,
|
||
(PVOID *) &pSQCD,
|
||
0,
|
||
0,
|
||
(DWORD)-1,
|
||
QueueName.Buffer);
|
||
|
||
pSQCD->CounterBlock.ByteLength = sizeof(SRVQ_COUNTER_DATA);
|
||
|
||
// initialize pointers for this instance
|
||
pThisQueueStatistics = &pSrvQueueStatistics[nQueue];
|
||
|
||
pSQCD->QueueLength = pThisQueueStatistics->QueueLength;
|
||
pSQCD->ActiveThreads = pThisQueueStatistics->ActiveThreads;
|
||
pSQCD->AvailableThreads = pThisQueueStatistics->AvailableThreads;
|
||
pSQCD->AvailableWorkItems = 0;
|
||
pSQCD->BorrowedWorkItems = 0;
|
||
pSQCD->WorkItemShortages = 0;
|
||
pSQCD->CurrentClients = 0;
|
||
pSQCD->TotalBytesTransfered =
|
||
pSQCD->BytesReceived = pThisQueueStatistics->BytesReceived.QuadPart;
|
||
pSQCD->TotalBytesTransfered +=
|
||
pSQCD->BytesSent = pThisQueueStatistics->BytesSent.QuadPart;
|
||
pSQCD->ReadOperations = 0;
|
||
pSQCD->TotalBytes =
|
||
pSQCD->BytesRead = pThisQueueStatistics->BytesRead.QuadPart;
|
||
pSQCD->WriteOperations = 0;
|
||
pSQCD->TotalBytes +=
|
||
pSQCD->BytesWritten = pThisQueueStatistics->BytesWritten.QuadPart;
|
||
pSQCD->TotalOperations = 0;
|
||
pSQCD->TotalContextBlocksQueued = pThisQueueStatistics->TotalWorkContextBlocksQueued.Count;
|
||
|
||
nQueue++; // to include the Blocking Queue statistics entry
|
||
|
||
// update the current pointer
|
||
pPerfInstanceDefinition = (PERF_INSTANCE_DEFINITION *)&pSQCD[1];
|
||
|
||
// update queue (instance) count in object data block
|
||
pSrvQDataDefinition->SrvQueueObjectType.NumInstances = nQueue;
|
||
|
||
// update available length
|
||
*lpcbTotalBytes =
|
||
pSrvQDataDefinition->SrvQueueObjectType.TotalByteLength =
|
||
(DWORD)((PCHAR) pPerfInstanceDefinition -
|
||
(PCHAR) pSrvQDataDefinition);
|
||
|
||
#if DBG
|
||
if (*lpcbTotalBytes > TotalLen ) {
|
||
DbgPrint ("\nPERFNET: Server Queue Perf Ctr. Instance Size Underestimated:");
|
||
DbgPrint ("\nPERFNET: Estimated size: %d, Actual Size: %d", TotalLen, *lpcbTotalBytes);
|
||
}
|
||
#endif
|
||
|
||
*lppData = (LPVOID)pPerfInstanceDefinition;
|
||
|
||
*lpNumObjectTypes = 1;
|
||
return ERROR_SUCCESS;
|
||
} else {
|
||
// unable to read server queue data for some reason so don't return this
|
||
// object
|
||
|
||
// log an event describing the error
|
||
DWORD dwData[4];
|
||
DWORD dwDataIndex = 0;
|
||
|
||
dwData[dwDataIndex++] = Status;
|
||
dwData[dwDataIndex++] = IoStatusBlock.Status;
|
||
dwData[dwDataIndex++] = (DWORD)IoStatusBlock.Information;
|
||
|
||
ReportEvent (hEventLog,
|
||
EVENTLOG_ERROR_TYPE, // error type
|
||
0, // category (not used)
|
||
PERFNET_UNABLE_READ_SERVER_QUEUE, // error code
|
||
NULL, // SID (not used),
|
||
0, // number of strings
|
||
dwDataIndex * sizeof(DWORD), // sizeof raw data
|
||
NULL, // message text array
|
||
(LPVOID)&dwData[0]); // raw data
|
||
|
||
*lpcbTotalBytes = (DWORD) 0;
|
||
*lpNumObjectTypes = (DWORD) 0;
|
||
return ERROR_SUCCESS;
|
||
}
|
||
}
|
||
|
||
DWORD APIENTRY
|
||
CloseServerObject ()
|
||
{
|
||
if (hSrv != NULL) {
|
||
NtClose(hSrv);
|
||
hSrv = NULL;
|
||
}
|
||
|
||
return ERROR_SUCCESS;
|
||
}
|
||
DWORD APIENTRY
|
||
CloseServerQueueObject ()
|
||
{
|
||
if (hLibHeap != NULL) {
|
||
if (pSrvQueueStatistics != NULL) {
|
||
FREEMEM (hLibHeap, 0, pSrvQueueStatistics);
|
||
pSrvQueueStatistics = NULL;
|
||
}
|
||
}
|
||
return ERROR_SUCCESS;
|
||
}
|
||
|