704 lines
16 KiB
C++
704 lines
16 KiB
C++
/*
|
|
* title: drvclass.cpp
|
|
*
|
|
* purpose: Implement C++ utilities
|
|
*
|
|
*
|
|
*/
|
|
|
|
extern "C"
|
|
{
|
|
|
|
#include <ntddk.h>
|
|
|
|
}
|
|
|
|
#define BOOL BOOLEAN
|
|
#define BYTE unsigned char
|
|
#define PBYTE unsigned char *
|
|
|
|
#include "drvclass.h"
|
|
|
|
#define HidBattTag 'HtaB'
|
|
|
|
extern "C" NTSTATUS DriverEntry (DRIVER_OBJECT *, UNICODE_STRING *);
|
|
|
|
void * __cdecl operator new(size_t nSize, POOL_TYPE iType, ULONG iPoolTag)
|
|
{
|
|
return ExAllocatePoolWithTag(iType,nSize,iPoolTag);
|
|
};
|
|
|
|
void __cdecl operator delete(void* p)
|
|
{
|
|
ExFreePool(p);
|
|
};
|
|
|
|
// CUString::CUString()
|
|
//
|
|
// Default constructor. Creates an empty string.
|
|
//
|
|
CUString :: CUString()
|
|
{
|
|
|
|
m_String.MaximumLength = 0;
|
|
m_String.Length = 0;
|
|
m_String.Buffer = NULL;
|
|
|
|
m_bType = TYPE_SYSTEM_ALLOCATED;
|
|
m_status = STATUS_SUCCESS;
|
|
|
|
}
|
|
|
|
//
|
|
// CUString::CUString( CUString& )
|
|
//
|
|
// Copy constructor
|
|
//
|
|
CUString :: CUString( CUString * pNewString )
|
|
{
|
|
|
|
/*
|
|
RtlInitUnicodeString( &m_String, NULL );
|
|
|
|
m_String.MaximumLength = NewString.m_String.MaximumLength;
|
|
m_String.Length = 0;
|
|
m_String.Buffer = ( unsigned short* )ExAllocatePoolWithTag( PagedPool, m_String.MaximumLength, HidBattTag );
|
|
|
|
if( !m_String.Buffer )
|
|
{
|
|
|
|
m_status = STATUS_INSUFFICIENT_RESOURCES;
|
|
return;
|
|
|
|
}
|
|
|
|
RtlZeroMemory( m_String.Buffer, m_String.MaximumLength );
|
|
RtlAppendUnicodeStringToString( &m_String, &NewString.m_String );
|
|
|
|
m_bType = TYPE_CLASS_ALLOCATED;
|
|
m_status = STATUS_SUCCESS;
|
|
m_String.Buffer[ m_String.Length ] = NULL;
|
|
*/
|
|
m_bType = TYPE_CLASS_ALLOCATED;
|
|
m_status = STATUS_SUCCESS;
|
|
m_String.MaximumLength = pNewString->m_String.MaximumLength;
|
|
m_String.Length = pNewString->m_String.Length;
|
|
m_String.Buffer = ( PWSTR )ExAllocatePoolWithTag( PagedPool, m_String.MaximumLength, HidBattTag );
|
|
if( !m_String.Buffer )
|
|
{
|
|
|
|
m_status = STATUS_INSUFFICIENT_RESOURCES;
|
|
return;
|
|
|
|
}
|
|
|
|
ZeroBuffer();
|
|
memcpy( m_String.Buffer, pNewString->m_String.Buffer, m_String.MaximumLength );
|
|
|
|
}
|
|
|
|
//
|
|
// CUString::CUString( UNICODE_STRING& )
|
|
//
|
|
// Copy constructor for UNICODE_STRING objects
|
|
//
|
|
CUString :: CUString( UNICODE_STRING * NewString )
|
|
{
|
|
|
|
/*
|
|
RtlInitUnicodeString( &m_String, NULL );
|
|
|
|
m_bType = TYPE_CLASS_ALLOCATED;
|
|
m_String.MaximumLength = NewString.MaximumLength + sizeof( WCHAR );
|
|
m_String.Length = 0;
|
|
m_String.Buffer = ( unsigned short* )ExAllocatePoolWithTag( PagedPool, m_String.MaximumLength, HidBattTag );
|
|
|
|
if( !m_String.Buffer )
|
|
{
|
|
|
|
m_status = STATUS_INSUFFICIENT_RESOURCES;
|
|
return;
|
|
|
|
}
|
|
|
|
RtlCopyUnicodeString( &m_String, &NewString );
|
|
|
|
m_status = STATUS_SUCCESS;
|
|
m_String.Buffer[ m_String.Length ] = NULL;
|
|
*/
|
|
|
|
m_bType = TYPE_CLASS_ALLOCATED;
|
|
m_status = STATUS_SUCCESS;
|
|
m_String.MaximumLength = NewString->Length + sizeof( WCHAR );
|
|
m_String.Length = NewString->Length;
|
|
m_String.Buffer = ( PWSTR )ExAllocatePoolWithTag( PagedPool, m_String.MaximumLength, HidBattTag );
|
|
if( !m_String.Buffer )
|
|
{
|
|
|
|
m_status = STATUS_INSUFFICIENT_RESOURCES;
|
|
return;
|
|
|
|
}
|
|
|
|
ZeroBuffer();
|
|
|
|
memcpy( m_String.Buffer, NewString->Buffer, m_String.Length );
|
|
|
|
}
|
|
|
|
//
|
|
// CUString::CUString( PWCHAR )
|
|
//
|
|
// Copy constructor for WCHAR pointer objects
|
|
//
|
|
CUString :: CUString( PWCHAR NewString )
|
|
{
|
|
|
|
|
|
m_bType = TYPE_CLASS_ALLOCATED;
|
|
m_status = STATUS_SUCCESS;
|
|
m_String.Length = ( unsigned short )( Length( NewString ) * sizeof( WCHAR ) );
|
|
m_String.MaximumLength = m_String.Length + sizeof( WCHAR );
|
|
m_String.Buffer = ( PWSTR )ExAllocatePoolWithTag( PagedPool, m_String.MaximumLength, HidBattTag );
|
|
if( !m_String.Buffer )
|
|
{
|
|
|
|
m_status = STATUS_INSUFFICIENT_RESOURCES;
|
|
return;
|
|
|
|
}
|
|
|
|
ZeroBuffer();
|
|
|
|
memcpy( m_String.Buffer, NewString, m_String.Length );
|
|
|
|
}
|
|
|
|
//
|
|
// CUString::CUString( int )
|
|
//
|
|
// Constructor which creates an empty string but
|
|
// allocates a string buffer of the given size of characters
|
|
//
|
|
CUString :: CUString( int nSize )
|
|
{
|
|
|
|
ASSERT( nSize >= 0 );
|
|
|
|
m_bType = TYPE_CLASS_ALLOCATED;
|
|
m_String.MaximumLength = 0;
|
|
m_String.Length = 0;
|
|
m_String.Buffer = NULL;
|
|
|
|
if( nSize > 0 )
|
|
{
|
|
|
|
m_String.MaximumLength = (USHORT)(( nSize + 1 ) * sizeof( WCHAR ));
|
|
|
|
if( nSize )
|
|
{
|
|
|
|
m_String.Buffer = (PWSTR)ExAllocatePoolWithTag( PagedPool, m_String.MaximumLength, HidBattTag );
|
|
if( !m_String.Buffer )
|
|
{
|
|
|
|
m_status = STATUS_INSUFFICIENT_RESOURCES;
|
|
return;
|
|
|
|
}
|
|
|
|
ZeroBuffer();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
m_status = STATUS_SUCCESS;
|
|
|
|
}
|
|
|
|
//
|
|
// CUString::CUString( UNICODE_STRING& )
|
|
//
|
|
// Constructor with creates a string that is a representation
|
|
// of the given integer and radix.
|
|
//
|
|
CUString :: CUString( int iVal, int iBase )
|
|
{
|
|
|
|
m_status = STATUS_INSUFFICIENT_RESOURCES;
|
|
m_bType = TYPE_CLASS_ALLOCATED;
|
|
m_String.Length = 0;
|
|
m_String.MaximumLength = 0;
|
|
m_String.Buffer = NULL;
|
|
|
|
int iSize = 1;
|
|
int iValCopy = ( !iVal ) ? 1 : iVal;
|
|
|
|
while( iValCopy >= 1 )
|
|
{
|
|
|
|
iValCopy /= iBase;
|
|
iSize++;
|
|
|
|
};
|
|
|
|
//
|
|
// iSize is the number of digits in number, max length of string
|
|
// is iSize plus the null terminator
|
|
//
|
|
m_String.MaximumLength = (USHORT)(( iSize + 1 ) * sizeof( WCHAR ));
|
|
|
|
m_String.Buffer = (PWSTR)ExAllocatePoolWithTag( PagedPool, m_String.MaximumLength, HidBattTag );
|
|
ASSERT( m_String.Buffer );
|
|
|
|
if( !m_String.Buffer )
|
|
{
|
|
|
|
m_status = STATUS_INSUFFICIENT_RESOURCES;
|
|
return;
|
|
|
|
}
|
|
|
|
ZeroBuffer();
|
|
|
|
m_status = RtlIntegerToUnicodeString(iVal, iBase, &m_String);
|
|
|
|
}
|
|
|
|
//
|
|
// CUString::~CUString()
|
|
//
|
|
// Destructor which frees the string buffer if:
|
|
// 1. It exists,
|
|
// and
|
|
// 2. It was allocated by the class
|
|
//
|
|
CUString :: ~CUString()
|
|
{
|
|
|
|
//
|
|
// If the buffer exists and was allocated by the class, free it.
|
|
//
|
|
if( ( m_bType == TYPE_CLASS_ALLOCATED ) && m_String.Buffer )
|
|
{
|
|
|
|
ExFreePool(m_String.Buffer);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
// CUString::Append( CUString& )
|
|
//
|
|
// Append the given string to the object
|
|
//
|
|
void CUString :: Append( CUString * Append )
|
|
{
|
|
|
|
UNICODE_STRING NewString;
|
|
|
|
//
|
|
// Determine the length of the new string ( including a null ) and allocate its memory
|
|
//
|
|
NewString.MaximumLength = m_String.Length + Append->m_String.Length + sizeof( WCHAR );
|
|
NewString.Length = 0;
|
|
NewString.Buffer = (PWSTR)ExAllocatePoolWithTag( PagedPool, NewString.MaximumLength, HidBattTag );
|
|
|
|
ASSERT( NewString.Buffer );
|
|
|
|
//
|
|
// Check for allocation failure.
|
|
//
|
|
if( !NewString.Buffer )
|
|
{
|
|
|
|
m_status = STATUS_INSUFFICIENT_RESOURCES;
|
|
return;
|
|
|
|
}
|
|
|
|
RtlZeroMemory( NewString.Buffer, NewString.MaximumLength );
|
|
|
|
//
|
|
// Copy the original string into the new string
|
|
//
|
|
RtlCopyUnicodeString( &NewString, &m_String );
|
|
|
|
//
|
|
// Append the 'append' string onto the new string
|
|
//
|
|
NTSTATUS Status = RtlAppendUnicodeStringToString( &NewString, &Append->m_String );
|
|
|
|
//
|
|
// If we allocated the original string, free it
|
|
//
|
|
if( m_bType == TYPE_CLASS_ALLOCATED && m_String.Buffer )
|
|
{
|
|
|
|
ExFreePool( m_String.Buffer );
|
|
|
|
}
|
|
|
|
//
|
|
// Copy the new string into the original strings place
|
|
//
|
|
m_String.MaximumLength = NewString.MaximumLength;
|
|
m_String.Length = NewString.Length;
|
|
m_String.Buffer = NewString.Buffer;
|
|
m_bType = TYPE_CLASS_ALLOCATED;
|
|
|
|
m_status = Status;
|
|
|
|
}
|
|
|
|
//
|
|
// CUString::Append( UNICODE_STRING* )
|
|
//
|
|
// Append the given string to the object
|
|
//
|
|
void CUString :: Append( UNICODE_STRING* pAppendString )
|
|
{
|
|
|
|
ASSERT( pAppendString );
|
|
|
|
if( !pAppendString )
|
|
return;
|
|
|
|
CUString AppendString( pAppendString );
|
|
|
|
Append( &AppendString );
|
|
|
|
}
|
|
/*
|
|
//
|
|
// operator + ( UNICODE_STRING&, ULONG& )
|
|
//
|
|
CUString operator + ( UNICODE_STRING * pUCS, ULONG dwValue )
|
|
{
|
|
|
|
// This routine is broken for now...don't use...
|
|
ASSERT( 0 );
|
|
|
|
CUString ReturnString( pUCS );
|
|
CUString ValueString( dwValue, 10 );
|
|
|
|
ReturnString.Append( ValueString );
|
|
|
|
return ReturnString;
|
|
|
|
}
|
|
|
|
//
|
|
// operator + ( CUString&, CUString& )
|
|
//
|
|
CUString operator + ( CUString& l, CUString& r )
|
|
{
|
|
|
|
CUString ReturnString( l );
|
|
ReturnString.Append( r );
|
|
|
|
return ReturnString;
|
|
|
|
}
|
|
|
|
//
|
|
// operator + ( UNICODE_STRING&, UNICODE_STRING& )
|
|
//
|
|
CUString operator + ( UNICODE_STRING& l, UNICODE_STRING& r )
|
|
{
|
|
|
|
CUString ReturnValue( l );
|
|
CUString Right( r );
|
|
|
|
ReturnValue.Append( Right );
|
|
|
|
return ReturnValue;
|
|
|
|
}
|
|
*/
|
|
|
|
//
|
|
// operator = ( CUString )
|
|
//
|
|
void CUString :: operator = ( CUString str )
|
|
{
|
|
|
|
m_String.Length = str.m_String.Length;
|
|
m_String.MaximumLength = str.m_String.MaximumLength;
|
|
m_String.Buffer = NULL;
|
|
|
|
//
|
|
// If the source string has a non-zero length buffer make a buffer of
|
|
// equal size in the destination.
|
|
//
|
|
if( str.m_String.MaximumLength > 0 )
|
|
{
|
|
|
|
m_String.Buffer = (PWSTR)ExAllocatePoolWithTag( PagedPool, str.m_String.MaximumLength, HidBattTag );
|
|
if( !m_String.Buffer )
|
|
{
|
|
|
|
m_status = STATUS_INSUFFICIENT_RESOURCES;
|
|
return;
|
|
|
|
}
|
|
|
|
ZeroBuffer();
|
|
|
|
//
|
|
// If the source string has a non-zero length, copy it into the dest string.
|
|
//
|
|
if( str.m_String.Length > 0 )
|
|
{
|
|
|
|
memcpy( m_String.Buffer, str.m_String.Buffer, str.m_String.Length );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
m_bType = TYPE_CLASS_ALLOCATED;
|
|
m_status = STATUS_SUCCESS;
|
|
|
|
}
|
|
|
|
NTSTATUS CUString :: ToCString( char** pString )
|
|
{
|
|
|
|
ULONG dwLength = m_String.Length >> 1;
|
|
|
|
*pString = ( char* )ExAllocatePoolWithTag( PagedPool, dwLength + 1, HidBattTag );
|
|
if( !*pString )
|
|
return STATUS_UNSUCCESSFUL;
|
|
|
|
char* pDst = *pString;
|
|
char* pSrc = ( char* )m_String.Buffer;
|
|
|
|
while( *pSrc )
|
|
{
|
|
|
|
*pDst++ = *pSrc;
|
|
|
|
pSrc += sizeof( WCHAR );
|
|
|
|
}
|
|
|
|
*pDst = 0x0;
|
|
|
|
return STATUS_SUCCESS;
|
|
|
|
}
|
|
|
|
void CUString :: Dump()
|
|
{
|
|
|
|
char* pString;
|
|
|
|
ToCString( &pString );
|
|
|
|
KdPrint( ( pString ) );
|
|
KdPrint( ( "\n" ) );
|
|
|
|
ExFreePool( pString );
|
|
|
|
}
|
|
|
|
ULONG CUString :: Length( PWCHAR String )
|
|
{
|
|
|
|
ULONG dwLength = 0;
|
|
|
|
while( *String++ )
|
|
{
|
|
|
|
dwLength++;
|
|
|
|
}
|
|
|
|
return dwLength;
|
|
|
|
}
|
|
|
|
// the registry access class
|
|
|
|
CRegistry::CRegistry(int iSize)
|
|
{ m_status = STATUS_INSUFFICIENT_RESOURCES;
|
|
m_pTable = (PRTL_QUERY_REGISTRY_TABLE)
|
|
ExAllocatePoolWithTag(NonPagedPool,sizeof(RTL_QUERY_REGISTRY_TABLE)*(iSize+1),HidBattTag);
|
|
if(m_pTable)
|
|
{
|
|
m_status = STATUS_SUCCESS;
|
|
RtlZeroMemory(m_pTable,sizeof(RTL_QUERY_REGISTRY_TABLE)*(iSize+1)); //this will terminate the table
|
|
}; // appropriately
|
|
|
|
};
|
|
CRegistry::~CRegistry()
|
|
{
|
|
if (m_pTable) ExFreePool(m_pTable);
|
|
};
|
|
|
|
|
|
BOOL CRegistry::QueryDirect(CUString *location,CUString *key, void **pReceiveBuffer, ULONG uType)
|
|
{
|
|
ULONG zero = 0;
|
|
|
|
m_pTable[0].Flags = RTL_QUERY_REGISTRY_DIRECT;
|
|
m_pTable[0].Name = key->m_String.Buffer;
|
|
m_pTable[0].EntryContext = *pReceiveBuffer;
|
|
m_pTable[0].DefaultType = uType;
|
|
m_pTable[0].DefaultData = &zero;
|
|
m_pTable[0].DefaultLength = sizeof(ULONG); // there must be something here, but we need to know what...
|
|
KdPrint( ( "RegClass QueryDirect: to retrieve Reg name...\n" ) );
|
|
location->Dump();
|
|
key->Dump();
|
|
|
|
if (STATUS_SUCCESS!=
|
|
RtlQueryRegistryValues(RTL_REGISTRY_ABSOLUTE,location->m_String.Buffer,m_pTable,NULL,NULL))
|
|
return FALSE;
|
|
return TRUE;
|
|
};
|
|
|
|
/*NTSTATUS CRegistry::QueryMustExist( CUString* pwzLocation, CUString* pwzKey, void **pReceiveBuffer )
|
|
{
|
|
m_pTable[0].Flags = RTL_QUERY_REGISTRY_DIRECT | RTL_QUERY_REGISTRY_NOEXPAND | RTL_QUERY_REGISTRY_REQUIRED;
|
|
m_pTable[0].Name = pwzKey->m_String.Buffer;
|
|
m_pTable[0].EntryContext = *pReceiveBuffer;
|
|
|
|
KdPrint( ( "RegClass QueryMustExist(): to retriee Reg name...\n" ) );
|
|
pwzLocation->Dump();
|
|
pwzKey->Dump();
|
|
|
|
return RtlQueryRegistryValues( RTL_REGISTRY_ABSOLUTE, pwzLocation->m_String.Buffer, m_pTable, NULL, NULL );
|
|
}
|
|
*/
|
|
|
|
BOOL CRegistry::QueryWithCallback(PRTL_QUERY_REGISTRY_ROUTINE callback,ULONG RelativeTo,PWSTR Path,PVOID Context, PVOID Environment)
|
|
{
|
|
m_pTable[0].QueryRoutine = callback;
|
|
m_pTable[0].Name = NULL;
|
|
m_status = RtlQueryRegistryValues(RelativeTo|RTL_REGISTRY_OPTIONAL,Path,m_pTable,Context,Environment);
|
|
return NT_SUCCESS(m_status);
|
|
};
|
|
|
|
BOOL CRegistry::WriteString(ULONG relativeTo, CUString *pBuffer, CUString *pPath, CUString *pKey)
|
|
{
|
|
return NT_SUCCESS(RtlWriteRegistryValue(relativeTo, pPath->GetString(), pKey->GetString(),REG_SZ,pBuffer->GetString(),pBuffer->GetLength()+sizeof(UNICODE_NULL)));
|
|
};
|
|
|
|
BOOL CRegistry::WriteDWord(ULONG relativeTo, void *pBuffer,CUString *pPath,CUString *pKey)
|
|
{
|
|
return NT_SUCCESS(RtlWriteRegistryValue(relativeTo, pPath->GetString(), pKey->GetString(),REG_DWORD,pBuffer,sizeof(REG_DWORD)));
|
|
};
|
|
|
|
NTSTATUS CRegistry::zwOpenKey(HANDLE * pKeyHandle,HANDLE hRoot,ACCESS_MASK DesiredAccess,CUString * pPath)
|
|
{
|
|
OBJECT_ATTRIBUTES ThisObject;
|
|
NTSTATUS status;
|
|
CUString ThisNull(NULL);
|
|
// setup target object for call
|
|
|
|
InitializeObjectAttributes( &ThisObject,
|
|
&(pPath->m_String),
|
|
OBJ_CASE_INSENSITIVE,
|
|
hRoot,
|
|
NULL);
|
|
|
|
KdPrint( ( "RESMAN: Opening registry key: " ) );
|
|
pPath->Dump();
|
|
|
|
status = ZwOpenKey( pKeyHandle,
|
|
DesiredAccess,
|
|
&ThisObject );
|
|
|
|
return status;
|
|
}
|
|
|
|
/*
|
|
NTSTATUS CRegistry::DeleteKey(HANDLE hTheKey)
|
|
{
|
|
return ZwDeleteKey(hTheKey);
|
|
}
|
|
*/
|
|
|
|
NTSTATUS CRegistry::zwCreateKey(HANDLE * pKeyHandle,HANDLE hRoot,ACCESS_MASK DesiredAccess,CUString * pPath,ULONG CreateOptions)
|
|
{
|
|
OBJECT_ATTRIBUTES ThisObject;
|
|
NTSTATUS status;
|
|
// setup target object for call
|
|
|
|
InitializeObjectAttributes( &ThisObject,
|
|
&(pPath->m_String),
|
|
OBJ_CASE_INSENSITIVE,
|
|
hRoot,
|
|
NULL);
|
|
|
|
KdPrint( ( "RESMAN: Creating registry key: " ) );
|
|
pPath->Dump();
|
|
|
|
status = ZwCreateKey(pKeyHandle,
|
|
DesiredAccess,
|
|
&ThisObject,
|
|
0,
|
|
NULL,
|
|
CreateOptions,
|
|
(ULONG*)&m_lDisposition);
|
|
return status;
|
|
}
|
|
|
|
BOOL CRegistry::zwCloseKey(HANDLE TheKey)
|
|
{
|
|
return NT_SUCCESS(ZwClose(TheKey));
|
|
}
|
|
|
|
NTSTATUS CRegistry::zwWriteValue(HANDLE hTheKey,CUString * ValueName,ULONG lType,PVOID pData,ULONG lSize)
|
|
{
|
|
NTSTATUS status;
|
|
status = ZwSetValueKey(hTheKey,
|
|
&ValueName->m_String,
|
|
0,
|
|
lType,
|
|
pData,
|
|
lSize);
|
|
return status;
|
|
}
|
|
|
|
NTSTATUS CRegistry::CheckKey(ULONG RelativeTo ,PUNICODE_STRING puRegKey)
|
|
{
|
|
|
|
// return (RtlCheckRegistryKey( RelativeTo,(PWSTR)puRegKey));
|
|
return FALSE;
|
|
}
|
|
|
|
// error logging methods
|
|
/*
|
|
CErrorLogEntry::CErrorLogEntry(PVOID pSource, ULONG errorCode, USHORT dumpDataSize, ULONG uniqueErrorValue,
|
|
NTSTATUS status, ULONG *dumpData, UCHAR FunctionCode)
|
|
{
|
|
m_pPacket = (PIO_ERROR_LOG_PACKET)
|
|
IoAllocateErrorLogEntry(pSource, (UCHAR) (sizeof(IO_ERROR_LOG_PACKET)+
|
|
(dumpDataSize * sizeof(ULONG))));
|
|
if (!m_pPacket) return;
|
|
int i;
|
|
m_pPacket->ErrorCode = errorCode;
|
|
m_pPacket->DumpDataSize = dumpDataSize * sizeof(ULONG);
|
|
m_pPacket->SequenceNumber = 0;
|
|
m_pPacket->MajorFunctionCode = FunctionCode;
|
|
m_pPacket->IoControlCode = 0;
|
|
m_pPacket->RetryCount = 0;
|
|
m_pPacket->UniqueErrorValue = uniqueErrorValue;
|
|
m_pPacket->FinalStatus = status;
|
|
for (i = 0; i < dumpDataSize; i++)
|
|
m_pPacket->DumpData[i] = dumpData[i];
|
|
IoWriteErrorLogEntry(m_pPacket);
|
|
};
|
|
|
|
|
|
CErrorLogEntry::~CErrorLogEntry()
|
|
{
|
|
};
|
|
|
|
*/
|
|
|