Windows NT 4.0 source code leak
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.
 
 
 
 
 
 

903 lines
24 KiB

/*++ BUILD Version: 0002 // Increment this if a change has global effects
Copyright (c) 1992 Microsoft Corporation
Module Name:
perfnbt.c
Abstract:
This file implements the Extensible Objects for
the LAN object types
Created:
Revision History:
--*/
//
// include files
//
#include <nt.h>
#include <ntrtl.h>
#include <nturtl.h>
#include <ntstapi.h>
#include <ntstatus.h>
#include <windows.h>
#include <sys\snet\nbt_stat.h>
#include <fcntl.h>
#include <sys\stropts.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <winperf.h>
#include "perfctr.h" // error message definition
#include "perfmsg.h"
#include "perfutil.h"
#include "perfnbt.h"
#include "datanbt.h"
// New header file for getting nbt data
#include <tdi.h>
#include <nbtioctl.h>
enum eSTATE
{
NBT_RECONNECTING, // waiting for the worker thread to run NbtConnect
NBT_IDLE, // not Transport connection
NBT_ASSOCIATED, // associated with an address element
NBT_CONNECTING, // establishing Transport connection
NBT_SESSION_INBOUND, // waiting for a session request after tcp connection setup inbound
NBT_SESSION_WAITACCEPT, // waiting for accept after a listen has been satisfied
NBT_SESSION_OUTBOUND, // waiting for a session response after tcp connection setup
NBT_SESSION_UP, // got positive response
NBT_DISCONNECTING, // sent a disconnect down to Tcp, but it hasn't completed yet
NBT_DISCONNECTED // a session has been disconnected but not closed with TCP yet
};
//
// References to constants which initialize the Object type definitions
//
extern NBT_DATA_DEFINITION NbtDataDefinition;
#define NBT_CONNECTION_NAME_LENGTH 17
#define NETBIOS_NAME_SIZE NBT_CONNECTION_NAME_LENGTH-1
//
// Nbt data structures
//
typedef struct _NBT_DEVICE_DATA {
HANDLE hFileHandle;
UNICODE_STRING DeviceName;
} NBT_DEVICE_DATA, *PNBT_DEVICE_DATA;
PNBT_DEVICE_DATA pNbtDeviceData;
int MaxNbtDeviceName;
int NumberOfNbtDevices;
// initial count - will update to last
PVOID pNbtDataBuffer = NULL;
int NbtDataBufferSize;
// HANDLE NbtHandle = INVALID_HANDLE_VALUE; // Handle of Nbt Device
#define NBT_CONTROLLING_STREAM "CSB" // Ignore Controlling Stream XEB
#define NBT_LISTEN_CONNECTION 3 // All NBT connections with type <= 3,
// are just listening for clients
// The error value returned by the perfctrs.dll when an error occurs while we
// are getting the data for the NBT connections.
// The error codes we get from the socket calls (OpenStream(), s_ioctl(),
// getmsg()) are Unix errors, not Dos or Windows errors. Hopefully, somebody
// will implement the conversion from these errors to Windows errors.
// The error value is not used within the Collect data routine because this
// routine shouldn't return an error in case it fails to collect Nbt data from
// connections. In this case, it just returns the buffer it was supposed to
// place the data into, unchanged.
#define ERROR_NBT_NET_RESPONSE \
(RtlNtStatusToDosError(STATUS_INVALID_NETWORK_RESPONSE))
#define BUFF_SIZE 650
PM_OPEN_PROC OpenNbtPerformanceData;
PM_COLLECT_PROC CollectNbtPerformanceData;
PM_CLOSE_PROC CloseNbtPerformanceData;
//------------------------------------------------------------------------
NTSTATUS
OpenNbt(
IN char *path,
OUT PHANDLE pHandle,
OUT UNICODE_STRING *uc_name_string
)
/*++
Routine Description:
This function opens a stream.
Arguments:
path - path to the STREAMS driver
oflag - currently ignored. In the future, O_NONBLOCK will be
relevant.
ignored - not used
Return Value:
An NT handle for the stream, or INVALID_HANDLE_VALUE if unsuccessful.
--*/
{
HANDLE StreamHandle;
OBJECT_ATTRIBUTES ObjectAttributes;
IO_STATUS_BLOCK IoStatusBlock;
STRING name_string;
// UNICODE_STRING uc_name_string;
NTSTATUS status;
RtlInitString(&name_string, path);
RtlAnsiStringToUnicodeString(uc_name_string, &name_string, TRUE);
InitializeObjectAttributes(
&ObjectAttributes,
uc_name_string,
OBJ_CASE_INSENSITIVE,
(HANDLE) NULL,
(PSECURITY_DESCRIPTOR) NULL
);
status =
NtCreateFile(
&StreamHandle,
SYNCHRONIZE | FILE_READ_DATA ,
// SYNCHRONIZE | FILE_READ_DATA | FILE_WRITE_DATA,
&ObjectAttributes,
&IoStatusBlock,
NULL,
FILE_ATTRIBUTE_NORMAL,
FILE_SHARE_READ | FILE_SHARE_WRITE,
FILE_OPEN_IF,
0,
NULL,
0);
// RtlFreeUnicodeString(&uc_name_string);
*pHandle = StreamHandle;
return(status);
} // Open_nbt
NTSTATUS
DeviceIoCtrl(
IN HANDLE fd,
IN PVOID ReturnBuffer,
IN ULONG BufferSize,
IN ULONG Ioctl
)
/*++
Routine Description:
This procedure performs an ioctl(I_STR) on a stream.
Arguments:
fd - NT file handle
iocp - pointer to a strioctl structure
Return Value:
0 if successful, -1 otherwise.
--*/
{
NTSTATUS status;
TDI_REQUEST_QUERY_INFORMATION QueryInfo;
IO_STATUS_BLOCK iosb;
PVOID pInput;
ULONG SizeInput;
if (Ioctl == IOCTL_TDI_QUERY_INFORMATION)
{
pInput = &QueryInfo;
QueryInfo.QueryType = TDI_QUERY_ADAPTER_STATUS; // node status or whatever
SizeInput = sizeof(TDI_REQUEST_QUERY_INFORMATION);
}
else
{
pInput = NULL;
SizeInput = 0;
}
status = NtDeviceIoControlFile(
fd, // Handle
NULL, // Event
NULL, // ApcRoutine
NULL, // ApcContext
&iosb, // IoStatusBlock
Ioctl, // IoControlCode
pInput, // InputBuffer
SizeInput, // InputBufferSize
(PVOID) ReturnBuffer, // OutputBuffer
BufferSize); // OutputBufferSize
if (status == STATUS_PENDING)
{
status = NtWaitForSingleObject(
fd, // Handle
TRUE, // Alertable
NULL); // Timeout
}
return(status);
} // DeviceIoCtrl
PCHAR
printable(
IN PCHAR string,
IN PCHAR StrOut
)
/*++
Routine Description:
This procedure converts non prinatble characaters to periods ('.')
Arguments:
string - the string to convert
StrOut - ptr to a string to put the converted string into
Return Value:
a ptr to the string that was converted (Strout)
--*/
{
unsigned char *Out;
unsigned char *cp;
LONG i;
Out = StrOut;
for (cp = string, i= 0; i < NETBIOS_NAME_SIZE; cp++,i++) {
if (isprint(*cp)) {
*Out++ = *cp;
continue;
}
if (*cp >= 128) { /* extended characters are ok */
*Out++ = *cp;
continue;
}
*Out++ = '.';
}
return(StrOut);
} // printable
DWORD
OpenNbtPerformanceData (
IN LPWSTR dwVoid // not used by this routine
)
/*++
Routine Description:
This routine will open the Nbt device and remember the handle returned
by the device.
Arguments:
None.
Return Value:
ERROR_NBT_NET_RESPONSE if unable to open NBT stream device
ERROR_SUCCESS if open was successful
--*/
{
PUCHAR SubKeyLinkage="system\\currentcontrolset\\services\\netbt\\linkage";
PUCHAR Linkage="Export";
CHAR pBuffer[BUFF_SIZE];
CHAR *lpLocalDeviceNames = pBuffer;
LONG status, status2, Type;
ULONG size;
HKEY Key;
HANDLE hFileHandle;
UNICODE_STRING fileString;
NTSTATUS ntstatus;
PNBT_DEVICE_DATA pTemp;
MonOpenEventLog();
REPORT_INFORMATION (NBT_OPEN_ENTERED, LOG_VERBOSE);
size = BUFF_SIZE;
status = RegOpenKeyEx(HKEY_LOCAL_MACHINE,
SubKeyLinkage,
0,
KEY_READ,
&Key);
if (status == ERROR_SUCCESS)
{
// now read the linkage values
status = RegQueryValueEx(Key,
Linkage,
NULL,
&Type,
pBuffer,
&size);
if (status == ERROR_SUCCESS)
{
status2 = RegCloseKey(Key);
}
else
{
return ERROR_SUCCESS;
}
}
else
{
return ERROR_SUCCESS;
}
while (TRUE)
{
if (*lpLocalDeviceNames == '\0')
{
break;
}
ntstatus = OpenNbt (lpLocalDeviceNames,
&hFileHandle,
&fileString);
if (ntstatus == ERROR_SUCCESS)
{
if (NumberOfNbtDevices == 0)
{
// allocate memory to hold the device data
pNbtDeviceData = RtlAllocateHeap(RtlProcessHeap(),
HEAP_ZERO_MEMORY,
sizeof(NBT_DEVICE_DATA));
if (pNbtDeviceData == NULL)
{
RtlFreeUnicodeString(&fileString);
return ERROR_OUTOFMEMORY;
}
}
else
{
// resize to hold multiple devices
pTemp = RtlReAllocateHeap(RtlProcessHeap(), 0,
pNbtDeviceData,
sizeof(NBT_DEVICE_DATA) * (NumberOfNbtDevices + 1));
if (pTemp == NULL)
{
NtClose(hFileHandle);
RtlFreeUnicodeString(&fileString);
REPORT_ERROR (TDI_PROVIDER_STATS_MEMORY, LOG_USER);
break;
}
else
{
pNbtDeviceData = pTemp;
}
}
// build the Data structure for this device instance
pNbtDeviceData[NumberOfNbtDevices].hFileHandle
= hFileHandle;
pNbtDeviceData[NumberOfNbtDevices].DeviceName.MaximumLength =
fileString.MaximumLength;
pNbtDeviceData[NumberOfNbtDevices].DeviceName.Length =
fileString.Length;
pNbtDeviceData[NumberOfNbtDevices].DeviceName.Buffer =
fileString.Buffer;
NumberOfNbtDevices++;
if (fileString.MaximumLength > MaxNbtDeviceName)
{
MaxNbtDeviceName = fileString.MaximumLength;
}
} // ntstatus OK
else
{
RtlFreeUnicodeString(&fileString);
}
// increment to the next device string
// lpLocalDeviceNames += strlen(lpLocalDeviceNames) + 1;
// we only support one device at this point since we cannot
// tell which Connection goes with which device
break;
} // while TRUE
REPORT_SUCCESS (NBT_OPEN_PERFORMANCE_DATA, LOG_DEBUG);
return ERROR_SUCCESS;
}
DWORD
CollectNbtPerformanceData(
IN LPWSTR lpValueName,
IN OUT LPVOID *lppData,
IN OUT LPDWORD lpcbTotalBytes,
IN OUT LPDWORD lpNumObjectTypes
)
/*++
Routine Description:
This routine will return the data for the Nbt counters.
IN LPWSTR lpValueName
pointer to a wide character null-terminated string passed by the
registry.
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 lpNumObjectTypes
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
Return Value:
ERROR_MORE_DATA if buffer passed is too small to hold data
any error conditions encountered are reported to the event log if
event logging is enabled.
ERROR_SUCCESS if success or any other error. Errors, however are
also reported to the event log.
--*/
{
// Variables for reformatting the Nbt data
LARGE_INTEGER UNALIGNED *pliCounter;
NBT_DATA_DEFINITION *pNbtDataDefinition;
PPERF_OBJECT_TYPE pNbtObject;
ULONG SpaceNeeded;
UNICODE_STRING ConnectionName;
ANSI_STRING AnsiConnectionName;
WCHAR ConnectionNameBuffer[NBT_CONNECTION_NAME_LENGTH + 20];
WCHAR DeviceNameBuffer[NBT_CONNECTION_NAME_LENGTH + 1 + 128];
CHAR AnsiConnectionNameBuffer[NBT_CONNECTION_NAME_LENGTH + 1 + 20];
WCHAR TotalName[] = L"Total";
PERF_INSTANCE_DEFINITION *pPerfInstanceDefinition;
PERF_COUNTER_BLOCK *pPerfCounterBlock;
CHAR NameOut[NETBIOS_NAME_SIZE +4];
int ConnectionCounter = 0;
LARGE_INTEGER TotalReceived, TotalSent;
DWORD dwDataReturn[2];
NTSTATUS status;
tCONNECTION_LIST *pConList;
tCONNECTIONS *pConns;
LONG Count;
int i;
int NumberOfConnections = 5; // assume 5 to start
if (lpValueName == NULL) {
REPORT_INFORMATION (NBT_COLLECT_ENTERED, LOG_VERBOSE);
} else {
REPORT_INFORMATION_DATA (NBT_COLLECT_ENTERED, LOG_VERBOSE,
lpValueName, (lstrlenW(lpValueName) * sizeof(WCHAR)));
}
//
// define pointer for Object Data structure (NBT object def.)
//
pNbtDataDefinition = (NBT_DATA_DEFINITION *) *lppData;
pNbtObject = (PPERF_OBJECT_TYPE) pNbtDataDefinition;
if (!pNbtDeviceData || NumberOfNbtDevices == 0)
{
//
// Error getting NBT info, so return 0 bytes, 0 objects and
// log error
//
REPORT_ERROR (NBT_IOCTL_INFO_ERROR, LOG_USER);
*lpcbTotalBytes = (DWORD) 0;
*lpNumObjectTypes = (DWORD) 0;
return ERROR_SUCCESS;
}
if (!pNbtDataBuffer)
{
NbtDataBufferSize = 1024L;
pNbtDataBuffer = RtlAllocateHeap(RtlProcessHeap(),
HEAP_ZERO_MEMORY,
NbtDataBufferSize
);
if (!pNbtDataBuffer)
{
*lpcbTotalBytes = (DWORD) 0;
*lpNumObjectTypes = (DWORD) 0;
return ERROR_SUCCESS;
}
}
REPORT_SUCCESS (NBT_IOCTL_INFO_SUCCESS, LOG_VERBOSE);
// Compute space needed to hold NBT data
SpaceNeeded = sizeof(NBT_DATA_DEFINITION) +
(NumberOfConnections *
NumberOfNbtDevices *
(sizeof(PERF_INSTANCE_DEFINITION) +
DWORD_MULTIPLE((NBT_CONNECTION_NAME_LENGTH * sizeof(WCHAR))
+ sizeof(UNICODE_NULL)
+ MaxNbtDeviceName ) +
SIZE_OF_NBT_DATA));
if ( *lpcbTotalBytes < SpaceNeeded ) {
dwDataReturn[0] = *lpcbTotalBytes;
dwDataReturn[1] = SpaceNeeded;
REPORT_WARNING_DATA (NBT_DATA_BUFFER_SIZE, LOG_DEBUG,
&dwDataReturn, sizeof (dwDataReturn));
return ERROR_MORE_DATA;
}
AnsiConnectionName.Length =
AnsiConnectionName.MaximumLength = sizeof(AnsiConnectionNameBuffer);
AnsiConnectionName.Buffer = AnsiConnectionNameBuffer;
//
// If here, then there's a object to display so initialize
// the Object data structure in the buffer passed to us.
//
RtlMoveMemory(pNbtDataDefinition, &NbtDataDefinition, sizeof(NBT_DATA_DEFINITION));
//
// point to where the first instance of this will be (if we find one.
//
pPerfInstanceDefinition = (PERF_INSTANCE_DEFINITION *)
(pNbtDataDefinition + 1);
TotalReceived.LowPart = 0; // initialize counters
TotalSent.LowPart = 0;
TotalReceived.HighPart = 0; // initialize counters
TotalSent.HighPart = 0;
// NOTE:- we only support NumberOfNbtDevices == 1 since
// DeviceIoCtrl can't tell which connection is for which NBT device
for (i=0; i < NumberOfNbtDevices; i++)
{
if (pNbtDeviceData[i].hFileHandle == 0 ||
pNbtDeviceData[i].hFileHandle == INVALID_HANDLE_VALUE)
{
continue;
}
status = STATUS_BUFFER_OVERFLOW;
while (status == STATUS_BUFFER_OVERFLOW)
{
status = DeviceIoCtrl (
pNbtDeviceData[i].hFileHandle,
pNbtDataBuffer,
NbtDataBufferSize,
IOCTL_NETBT_GET_CONNECTIONS);
if (status == STATUS_BUFFER_OVERFLOW)
{
// resize to hold multiple devices
NbtDataBufferSize += 1024L;
pNbtDataBuffer = RtlReAllocateHeap(RtlProcessHeap(), 0,
pNbtDataBuffer,
NbtDataBufferSize);
if (pNbtDataBuffer == NULL || NbtDataBufferSize == 0x0FFFFL)
{
*lpcbTotalBytes = (DWORD) 0;
*lpNumObjectTypes = (DWORD) 0;
return ERROR_SUCCESS;
}
}
} // while Buffer overflow
pConList = (tCONNECTION_LIST *) pNbtDataBuffer;
Count = pConList->ConnectionCount;
pConns = pConList->ConnList;
if (Count == 0)
{
continue;
}
if (NumberOfConnections < Count)
{
NumberOfConnections = Count;
// Better check space needed to hold NBT data again
// this is because the Count could be hugh
SpaceNeeded = sizeof(NBT_DATA_DEFINITION) +
(NumberOfConnections *
NumberOfNbtDevices *
(sizeof(PERF_INSTANCE_DEFINITION) +
DWORD_MULTIPLE((NBT_CONNECTION_NAME_LENGTH * sizeof(WCHAR))
+ sizeof(UNICODE_NULL)
+ MaxNbtDeviceName )
+ SIZE_OF_NBT_DATA));
if ( *lpcbTotalBytes < SpaceNeeded ) {
dwDataReturn[0] = *lpcbTotalBytes;
dwDataReturn[1] = SpaceNeeded;
REPORT_WARNING_DATA (NBT_DATA_BUFFER_SIZE, LOG_DEBUG,
&dwDataReturn, sizeof (dwDataReturn));
return ERROR_MORE_DATA;
}
}
while ( Count-- )
{
if (pConns->State == NBT_SESSION_UP)
{
// only care about UP connection
if (pConns->RemoteName[0])
{
AnsiConnectionName.Length = sprintf (
AnsiConnectionNameBuffer,
"%16.16s",
printable(pConns->RemoteName, NameOut));
}
else if (pConns->LocalName[0])
{
if (pConns->LocalName[NETBIOS_NAME_SIZE-1] < ' ')
{
AnsiConnectionName.Length = sprintf (
AnsiConnectionNameBuffer,
"%15.15s%02.2X",
printable(pConns->LocalName, NameOut),
pConns->LocalName[NETBIOS_NAME_SIZE-1]);
}
else
{
AnsiConnectionName.Length = sprintf (
AnsiConnectionNameBuffer,
"%16.16s",
printable(pConns->LocalName, NameOut));
}
}
else
{
AnsiConnectionNameBuffer[0] = ' ';
AnsiConnectionName.Length = 1;
}
ConnectionName.Length =
ConnectionName.MaximumLength =
sizeof(ConnectionNameBuffer);
ConnectionName.Buffer = ConnectionNameBuffer;
RtlAnsiStringToUnicodeString (&ConnectionName,
&AnsiConnectionName,
FALSE);
// no need to put in device name since we can
// only support one device
#if 0
lstrcpyW (DeviceNameBuffer, pNbtDeviceData[i].DeviceName.Buffer);
lstrcatW (DeviceNameBuffer, L" ");
lstrcatW (DeviceNameBuffer, ConnectionNameBuffer);
ConnectionName.Length =
lstrlenW (DeviceNameBuffer) * sizeof(WCHAR);
ConnectionName.MaximumLength =
sizeof(DeviceNameBuffer);
ConnectionName.Buffer = DeviceNameBuffer;
#endif
//
// load instance data into buffer
//
MonBuildInstanceDefinition (pPerfInstanceDefinition,
(PVOID *) &pPerfCounterBlock,
0,
0,
(DWORD)PERF_NO_UNIQUE_ID, // no unique ID, Use the name instead
// ConnectionCounter++,
&ConnectionName);
//
// adjust object size values to include new instance
//
pNbtObject->NumInstances++;
//
// initialize this instance's counter block
pPerfCounterBlock->ByteLength = SIZE_OF_NBT_DATA;
pliCounter = (LARGE_INTEGER UNALIGNED * ) (pPerfCounterBlock + 1);
*(pliCounter++) = pConns->BytesRcvd;
TotalReceived.QuadPart = TotalReceived.QuadPart +
pConns->BytesRcvd.QuadPart;
*pliCounter++ = pConns->BytesSent;
TotalSent.QuadPart = TotalSent.QuadPart +
pConns->BytesSent.QuadPart;
pliCounter->QuadPart = pConns->BytesRcvd.QuadPart +
pConns->BytesSent.QuadPart;
//
// update pointer for next instance
//
pPerfInstanceDefinition = (PERF_INSTANCE_DEFINITION *)
(((PBYTE) pPerfCounterBlock) + SIZE_OF_NBT_DATA);
} // pConns->State == NBT_SESSION_UP
pConns++;
} // while ( Count-- )
} // for i < NumberOfNbtDevices
// The last instance definition contains the total data from all the
// displayed connections
RtlInitUnicodeString (&ConnectionName, TotalName);
MonBuildInstanceDefinition (pPerfInstanceDefinition,
(PVOID *) &pPerfCounterBlock,
0,
0,
// ConnectionCounter++,
(DWORD)PERF_NO_UNIQUE_ID, // no unique ID, Use the name instead
&ConnectionName);
//
// adjust object size values to include new instance
//
pNbtObject->NumInstances++;
pNbtObject->TotalByteLength += sizeof (PERF_INSTANCE_DEFINITION)
+ SIZE_OF_NBT_DATA;
// initialize counter block for this instance
pPerfCounterBlock->ByteLength = SIZE_OF_NBT_DATA;
// load counters
pliCounter = (LARGE_INTEGER UNALIGNED * ) (pPerfCounterBlock + 1);
(*(pliCounter++)) = TotalReceived;
(*(pliCounter++)) = TotalSent;
pliCounter->QuadPart = TotalReceived.QuadPart + TotalSent.QuadPart;
pliCounter++;
// Set returned values
*lppData = (LPVOID)pliCounter;
*lpNumObjectTypes = NBT_NUM_PERF_OBJECT_TYPES;
*lpcbTotalBytes = (DWORD)((LPBYTE)pliCounter-(LPBYTE)pNbtObject);
pNbtDataDefinition->NbtObjectType.TotalByteLength = *lpcbTotalBytes;
REPORT_INFORMATION (NBT_COLLECT_DATA, LOG_DEBUG);
return ERROR_SUCCESS;
}
DWORD
CloseNbtPerformanceData(
)
/*++
Routine Description:
This routine closes the open handles to Nbt devices.
Arguments:
None.
Return Value:
ERROR_SUCCESS
--*/
{
int i;
REPORT_INFORMATION (NBT_CLOSE, LOG_VERBOSE);
if (pNbtDeviceData)
{
for (i=0; i < NumberOfNbtDevices; i++)
{
if (pNbtDeviceData[i].DeviceName.Buffer)
{
RtlFreeUnicodeString(&(pNbtDeviceData[i].DeviceName));
}
if (pNbtDeviceData[i].hFileHandle)
{
NtClose (pNbtDeviceData[i].hFileHandle);
}
}
RtlFreeHeap(
RtlProcessHeap(),
0,
pNbtDeviceData);
pNbtDeviceData = NULL;
NumberOfNbtDevices = 0;
}
if (pNbtDataBuffer)
{
RtlFreeHeap(
RtlProcessHeap(),
0,
pNbtDataBuffer);
pNbtDataBuffer = NULL;
NbtDataBufferSize = 0;
}
MonCloseEventLog();
return ERROR_SUCCESS;
}