www.pudn.com > WDM_Driver_Dev_ExampleCode.zip > DebugPrint.c


////////////////////////////////////////////////////////////////////////////// 
//	Copyright © 1998,1999 PHD Computer Consultants Ltd 
// 
//	DebugPrint code to add to a test driver		http://www.phdcc.com/debugprint/ 
///////////////////////////////////////////////////////////////////////////// 
//	DebugPrint.cpp:			Debug printing in test driver 
///////////////////////////////////////////////////////////////////////////// 
//	DebugPrintInit			Initialise DebugPrint 
//	DebugPrintMsg			Print ANSI string 
//	DebugPrintClose			Close DebugPrint 
//	DebugPrint				Formatted print, allocating 100 byte print buffer 
//	DebugPrint2				Formatted print, specifying print buffer size 
//*	DebugPrintVA			Allocate DebugPrint buffer and call DebugSprintf 
//*	PrintXxx				Print various types to buffer 
//*	DebugSprintf			Do formatted print to buffer 
//*	ANSIstrlen				Get ANSI string length 
//*	DebugPrintSystemThread	System thread to print device events. 
//*	OpenDebugPrintDriver	Attempt to open DebugPrt driver for 5 mins 
//*	ClearEvents				Clear any remaining events 
///////////////////////////////////////////////////////////////////////////// 
//	DebugPrinting occurs automatically in checked build 
//	To force debug printing in free build, #define DEBUGPRINT 1 
///////////////////////////////////////////////////////////////////////////// 
//	Format specification characters 
//		%c	ANSI char 
//		%C	Wide char 
//		%d	Signed int in decimal 
//		%D	__int64 in decimal 
//		%i	Signed int in decimal 
//		%I	IRP 
//		%l	__int64 in hex 
//		%L	LARGE_INTEGER in hex 
//		%s	null-terminated ANSI char string 
//		%S	null-terminated Wide char string 
//		%T	PUNICODE_STRING 
//		%u	ULONG in decimal 
//		%x	ULONG in hex 
// 
//	Be very careful to provide appropriate parameters for the formatted string 
//	in your calls to DebugPrint. 
///////////////////////////////////////////////////////////////////////////// 
//	Version history 
//	7-Dec-98	1.0.0	CC	creation 
//	17-Dec-98	1.0.1	CC	DriverName allocated from NonPagedPool 
//	21-Dec-98	1.0.1	CC	sizeof char * used properly 
//	28-Dec-98	1.0.2	CC	%* allowed for %sSx 
//	22-Jan-99	1.0.3	CC	%D, %l, %L added.  width>'9' allowed 
//	16-Feb-99	1.0.4	CC	RtlInitUnicodeString used 
//	31-Mar-99	1.0.5	CC	Altered to make it work in NT4 
//	24-Apr-99	1.0.5	CC	DebugPrintClose checks DebugPrintStarted 
//	25-Apr-99	1.0.5	CC	System thread tries for 5 mins to open DebugPrt 
//	13-May-99	1.0.6	CC	IRP_MN_QUERY_LEGACY_BUS_INFORMATION added 
//	19-May-99	1.0.6	CC	ThreadExiting ensures that thread finished before unload 
///////////////////////////////////////////////////////////////////////////// 
 
#ifdef __cplusplus 
extern "C" 
{ 
#endif 
#include  
#ifdef __cplusplus 
} 
#endif 
 
#include "debugprint.h" 
#include 	// OK to use this for va_* macros 
 
#if DODEBUGPRINT 
 
////////////////////////////////////////////////////////////////////////////// 
//	Definitions copied from Wdm.h to make this compile in NT4 
//	Cross-fingers - lets hope these definitions do not change 
 
#define Dbp_IRP_MJ_POWER                    0x16 
#define Dbp_IRP_MJ_SYSTEM_CONTROL           0x17 
#define Dbp_IRP_MJ_PNP                      0x1b 
 
#define Dbp_IRP_MN_QUERY_DEVICE_RELATIONS       0x07 
 
#define Dbp_IRP_MN_SET_POWER                    0x02 
#define Dbp_IRP_MN_QUERY_POWER                  0x03 
 
typedef enum Dbp__DEVICE_RELATION_TYPE { 
    Dbp_BusRelations, 
    Dbp_EjectionRelations, 
    Dbp_PowerRelations, 
    Dbp_RemovalRelations, 
    Dbp_TargetDeviceRelation 
} Dbp_DEVICE_RELATION_TYPE, *Dbp_PDEVICE_RELATION_TYPE; 
 
typedef struct _Dbp_QueryDeviceRelations 
{ 
	Dbp_DEVICE_RELATION_TYPE Type; 
} Dbp_QueryDeviceRelations, *Dbp_PQueryDeviceRelations; 
 
typedef enum _Dbp_SYSTEM_POWER_STATE { 
    Dbp_PowerSystemUnspecified = 0, 
    Dbp_PowerSystemWorking, 
    Dbp_PowerSystemSleeping1, 
    Dbp_PowerSystemSleeping2, 
    Dbp_PowerSystemSleeping3, 
    Dbp_PowerSystemHibernate, 
    Dbp_PowerSystemShutdown, 
    Dbp_PowerSystemMaximum 
} Dbp_SYSTEM_POWER_STATE, *Dbp_PSYSTEM_POWER_STATE; 
 
typedef enum { 
    Dbp_PowerActionNone, 
    Dbp_PowerActionReserved, 
    Dbp_PowerActionSleep, 
    Dbp_PowerActionHibernate, 
    Dbp_PowerActionShutdown, 
    Dbp_PowerActionShutdownReset, 
    Dbp_PowerActionShutdownOff 
} Dbp_POWER_ACTION, *Dbp_PPOWER_ACTION; 
 
typedef enum _Dbp_DEVICE_POWER_STATE { 
    Dbp_PowerDeviceUnspecified = 0, 
    Dbp_PowerDeviceD0, 
    Dbp_PowerDeviceD1, 
    Dbp_PowerDeviceD2, 
    Dbp_PowerDeviceD3, 
    Dbp_PowerDeviceMaximum 
} Dbp_DEVICE_POWER_STATE, *Dbp_PDEVICE_POWER_STATE; 
 
typedef enum _Dbp_POWER_STATE_TYPE { 
    Dbp_SystemPowerState, 
    Dbp_DevicePowerState 
} Dbp_POWER_STATE_TYPE, *Dbp_PPOWER_STATE_TYPE; 
 
typedef union _Dbp_POWER_STATE { 
    Dbp_SYSTEM_POWER_STATE SystemState; 
    Dbp_DEVICE_POWER_STATE DeviceState; 
} Dbp_POWER_STATE, *Dbp_PPOWER_STATE; 
 
typedef struct _Dbp_Power { 
    ULONG SystemContext; 
    Dbp_POWER_STATE_TYPE Type; 
    Dbp_POWER_STATE State; 
    Dbp_POWER_ACTION ShutdownType; 
} Dbp_Power, *Dbp_PPower; 
 
////////////////////////////////////////////////////////////////////////////// 
//	DebugPrint globals 
 
static BOOLEAN DebugPrintStarted = FALSE; 
static char* DriverName = NULL; 
static USHORT DriverNameLen = 0; 
 
///////////////////////////////////////////////////////////////////////////// 
//	DebugPrint Event structure (put in doubly-linked EventList) 
 
typedef struct _DEBUGPRINT_EVENT 
{ 
	LIST_ENTRY ListEntry; 
	ULONG Len; 
	UCHAR EventData[1]; 
} DEBUGPRINT_EVENT, *PDEBUGPRINT_EVENT; 
 
////////////////////////////////////////////////////////////////////////////// 
//	Globals to communicate with our system thread 
 
PVOID ThreadObjectPointer=NULL;	// Thread pointer 
BOOLEAN ExitNow;				// Set to cause thread to exit 
KEVENT ThreadEvent;				// Set to make thread look at ExitNow. 
LIST_ENTRY EventList;			// Doubly-linked list of written Events 
KSPIN_LOCK EventListLock;		// Spin lock to guard access to EventList 
KEVENT ThreadExiting;			// Set when thread exiting 
 
void DebugPrintSystemThread( IN PVOID Context); 
NTSTATUS OpenDebugPrintDriver( HANDLE* pDebugPrintDeviceHandle); 
 
////////////////////////////////////////////////////////////////////////////// 
//	DebugPrint local functions 
 
HANDLE OpenDebugPrint(); 
void CloseDebugPrint( HANDLE h); 
void DebugSprintf( char* buffer, int max, const char* format, va_list marker); 
USHORT ANSIstrlen( char* str); 
void ClearEvents(); 
 
////////////////////////////////////////////////////////////////////////////// 
//	DebugPrintInit:		Initialise DebugPrint 
//						Connect to DebugPrint driver at \Device\PHDDebugPrint 
// 
//	IRQL PASSIVE_LEVEL 
 
void DebugPrintInit(char* _DriverName) 
{ 
	HANDLE threadHandle; 
	NTSTATUS status; 
 
	// Copy the driver's name out of INIT code segment 
	DriverNameLen = 1 + ANSIstrlen(_DriverName); 
	DriverName = (char*)ExAllocatePool(NonPagedPool,DriverNameLen); 
	if( DriverName==NULL) return; 
	RtlCopyMemory( DriverName, _DriverName, DriverNameLen); 
 
	///////////////////////////////////////////////////////////////////////// 
	// Prepare for thread start 
 
	ExitNow = FALSE; 
	KeInitializeEvent(&ThreadEvent, SynchronizationEvent, FALSE); 
	KeInitializeEvent(&ThreadExiting, SynchronizationEvent, FALSE); 
	// Initialise event list 
	KeInitializeSpinLock(&EventListLock); 
	InitializeListHead(&EventList); 
 
	///////////////////////////////////////////////////////////////////////// 
	// Start system thread to write events to DebugPrint driver 
 
	status = PsCreateSystemThread( &threadHandle, THREAD_ALL_ACCESS, NULL, NULL, NULL, 
									DebugPrintSystemThread, NULL); 
	if( !NT_SUCCESS(status)) 
		return; 
 
	///////////////////////////////////////////////////////////////////////// 
	// Save a pointer to thread and close handle. 
 
	status = ObReferenceObjectByHandle( threadHandle, THREAD_ALL_ACCESS, NULL, KernelMode, 
										&ThreadObjectPointer, NULL); 
 
	if( NT_SUCCESS(status)) 
		ZwClose(threadHandle); 
	else 
	{ 
		// Uh oh... force thread to exit 
		ExitNow = TRUE; 
		KeSetEvent( &ThreadEvent, 0, FALSE); 
		return; 
	} 
 
	DebugPrintStarted = TRUE; 
 
	// Send event that we've started logging 
	DebugPrintMsg("DebugPrint logging started"); 
} 
 
////////////////////////////////////////////////////////////////////////////// 
//	DebugPrintClose:	Close connection to DebugPrint 
// 
//	IRQL PASSIVE_LEVEL 
 
void DebugPrintClose() 
{ 
	if( !DebugPrintStarted) return; 
 
	DebugPrintMsg("DebugPrint logging ended"); 
	DebugPrintStarted = FALSE; 
	 
	// Tell thread to stop, and wait for it to stop 
	ExitNow = TRUE; 
	KeSetEvent( &ThreadEvent, 0, FALSE); 
	KeWaitForSingleObject( &ThreadExiting, Executive, KernelMode, FALSE, NULL); 
 
	// Dereference thread object 
	if( ThreadObjectPointer!=NULL) 
	{ 
		ObDereferenceObject(&ThreadObjectPointer); 
		ThreadObjectPointer = NULL; 
	} 
	 
	// Release our copy of DriverName 
	if( DriverName!=NULL) 
		ExFreePool(DriverName); 
//	ClearEvents(); 
} 
 
////////////////////////////////////////////////////////////////////////////// 
//	DebugPrintMsg:	Send message event to DebugPrint 
// 
//	IRQL <= DISPATCH_LEVEL 
 
void DebugPrintMsg(char* Msg) 
{ 
	LARGE_INTEGER Now; 
	TIME_FIELDS NowTF; 
	USHORT MsgLen; 
	ULONG EventDataLen, len; 
	PDEBUGPRINT_EVENT pEvent; 
 
	if( !DebugPrintStarted || DriverName==NULL) return; 
 
	// Get current time 
	KeQuerySystemTime(&Now); 
//	LARGE_INTEGER NowLocal; 
//	ExSystemTimeToLocalTime( &Now, &NowLocal);	// NT only 
//	RtlTimeToTimeFields( &NowLocal, &NowTF); 
	RtlTimeToTimeFields( &Now, &NowTF); 
 
	// Get size of Msg and complete event 
	MsgLen = ANSIstrlen(Msg)+1; 
	EventDataLen = sizeof(TIME_FIELDS) + DriverNameLen + MsgLen; 
	len = sizeof(LIST_ENTRY)+sizeof(ULONG)+EventDataLen; 
 
	// Allocate event buffer 
	pEvent = (PDEBUGPRINT_EVENT)ExAllocatePool(NonPagedPool,len); 
	if( pEvent!=NULL) 
	{ 
		PUCHAR buffer = (PUCHAR)pEvent->EventData; 
		// Copy event info to buffer 
		RtlCopyMemory( buffer, &NowTF, sizeof(TIME_FIELDS)); 
		buffer += sizeof(TIME_FIELDS); 
		RtlCopyMemory( buffer, DriverName, DriverNameLen); 
		buffer += DriverNameLen; 
		RtlCopyMemory( buffer, Msg, MsgLen); 
 
		// Insert event into event list for processing by system thread 
		pEvent->Len = EventDataLen; 
		ExInterlockedInsertTailList(&EventList,&pEvent->ListEntry,&EventListLock); 
	} 
} 
 
////////////////////////////////////////////////////////////////////////////// 
//*	DebugPrintVA:	Implement DebugPrint calls 
 
void DebugPrintVA(int max, const char* format, va_list marker) 
{ 
	char* Msg; 
	if( !DebugPrintStarted) return; 
	Msg = (char*)ExAllocatePool(NonPagedPool,max); 
	if( Msg==NULL) 
	{ 
		DebugPrintMsg("DebugPrint: Could not allocate buffer"); 
		return; 
	} 
	DebugSprintf(Msg,max,format,marker); 
	DebugPrintMsg(Msg); 
	ExFreePool(Msg); 
} 
#endif // DODEBUGPRINT 
 
////////////////////////////////////////////////////////////////////////////// 
//	DebugPrint:	Formatted print to DebugPrint, allocating own print buffer 
// 
//	IRQL <= DISPATCH_LEVEL 
 
void DebugPrint(const char* format, ... ) 
{ 
#if DODEBUGPRINT 
	va_list marker; 
	if( !DebugPrintStarted) return; 
	va_start(marker,format); 
	DebugPrintVA(100,format,marker); 
#endif // DODEBUGPRINT 
} 
 
////////////////////////////////////////////////////////////////////////////// 
//	DebugPrint:	Formatted print to DebugPrint, giving size of buffer to allocate 
// 
//	IRQL <= DISPATCH_LEVEL 
 
void DebugPrint2(int max, const char* format, ... ) 
{ 
#if DODEBUGPRINT 
	va_list marker; 
	if( !DebugPrintStarted) return; 
	va_start(marker,format); 
	DebugPrintVA(max,format,marker); 
#endif // DODEBUGPRINT 
} 
 
#if DODEBUGPRINT 
////////////////////////////////////////////////////////////////////////////// 
//	IRP Major and Minor function names 
 
static char* IrpMajorFunctionNames[] = 
{ 
	"IRP_MJ_CREATE", 
	"IRP_MJ_CREATE_NAMED_PIPE", 
	"IRP_MJ_CLOSE", 
	"IRP_MJ_READ", 
	"IRP_MJ_WRITE", 
	"IRP_MJ_QUERY_INFORMATION", 
	"IRP_MJ_SET_INFORMATION", 
	"IRP_MJ_QUERY_EA", 
	"IRP_MJ_SET_EA", 
	"IRP_MJ_FLUSH_BUFFERS", 
	"IRP_MJ_QUERY_VOLUME_INFORMATION", 
	"IRP_MJ_SET_VOLUME_INFORMATION", 
	"IRP_MJ_DIRECTORY_CONTROL", 
	"IRP_MJ_FILE_SYSTEM_CONTROL", 
	"IRP_MJ_DEVICE_CONTROL", 
	"IRP_MJ_INTERNAL_DEVICE_CONTROL", 
	"IRP_MJ_SHUTDOWN", 
	"IRP_MJ_LOCK_CONTROL", 
	"IRP_MJ_CLEANUP", 
	"IRP_MJ_CREATE_MAILSLOT", 
	"IRP_MJ_QUERY_SECURITY", 
	"IRP_MJ_SET_SECURITY", 
	"IRP_MJ_POWER", 
	"IRP_MJ_SYSTEM_CONTROL", 
	"IRP_MJ_DEVICE_CHANGE", 
	"IRP_MJ_QUERY_QUOTA", 
	"IRP_MJ_SET_QUOTA", 
	"IRP_MJ_PNP", 
}; 
static ULONG NUM_IrpMajorFunctionNames = sizeof(IrpMajorFunctionNames)/sizeof(char*); 
 
static char* PnPIrpMinorFunctionNames[] = 
{ 
	"IRP_MN_START_DEVICE", 
	"IRP_MN_QUERY_REMOVE_DEVICE", 
	"IRP_MN_REMOVE_DEVICE", 
	"IRP_MN_CANCEL_REMOVE_DEVICE", 
	"IRP_MN_STOP_DEVICE", 
	"IRP_MN_QUERY_STOP_DEVICE", 
	"IRP_MN_CANCEL_STOP_DEVICE", 
	"IRP_MN_QUERY_DEVICE_RELATIONS", 
	"IRP_MN_QUERY_INTERFACE", 
	"IRP_MN_QUERY_CAPABILITIES", 
	"IRP_MN_QUERY_RESOURCES", 
	"IRP_MN_QUERY_RESOURCE_REQUIREMENTS", 
	"IRP_MN_QUERY_DEVICE_TEXT", 
	"IRP_MN_FILTER_RESOURCE_REQUIREMENTS", 
	"Nowt", 
	"IRP_MN_READ_CONFIG", 
	"IRP_MN_WRITE_CONFIG", 
	"IRP_MN_EJECT", 
	"IRP_MN_SET_LOCK", 
	"IRP_MN_QUERY_ID", 
	"IRP_MN_QUERY_PNP_DEVICE_STATE", 
	"IRP_MN_QUERY_BUS_INFORMATION", 
	"IRP_MN_DEVICE_USAGE_NOTIFICATION", 
	"IRP_MN_SURPRISE_REMOVAL", 
	"IRP_MN_QUERY_LEGACY_BUS_INFORMATION", 
}; 
static ULONG NUM_PnPIrpMinorFunctionNames = sizeof(PnPIrpMinorFunctionNames)/sizeof(char*); 
 
static char* PowerIrpMinorFunctionNames[] = 
{ 
	"IRP_MN_WAIT_WAKE", 
	"IRP_MN_POWER_SEQUENCE", 
	"IRP_MN_SET_POWER", 
	"IRP_MN_QUERY_POWER", 
}; 
static ULONG NUM_PowerIrpMinorFunctionNames = sizeof(PowerIrpMinorFunctionNames)/sizeof(char*); 
 
static char* WMIIrpMinorFunctionNames[] = 
{ 
	"IRP_MN_QUERY_ALL_DATA", 
	"IRP_MN_QUERY_SINGLE_INSTANCE", 
	"IRP_MN_CHANGE_SINGLE_INSTANCE", 
	"IRP_MN_CHANGE_SINGLE_ITEM", 
	"IRP_MN_ENABLE_EVENTS", 
	"IRP_MN_DISABLE_EVENTS", 
	"IRP_MN_ENABLE_COLLECTION", 
	"IRP_MN_DISABLE_COLLECTION", 
	"IRP_MN_REGINFO", 
	"IRP_MN_EXECUTE_METHOD", 
}; 
static ULONG NUM_WMIIrpMinorFunctionNames = sizeof(WMIIrpMinorFunctionNames)/sizeof(char*); 
 
static char* PowerSystemStates[] = 
{ 
    "PowerSystemUnspecified", 
    "PowerSystemWorking", 
    "PowerSystemSleeping1", 
    "PowerSystemSleeping2", 
    "PowerSystemSleeping3", 
    "PowerSystemHibernate", 
    "PowerSystemShutdown", 
}; 
static int NUM_PowerSystemStates = sizeof(PowerSystemStates)/sizeof(char*); 
 
static char* PowerDeviceStates[] = 
{ 
    "PowerDeviceUnspecified", 
    "PowerDeviceD0", 
    "PowerDeviceD1", 
    "PowerDeviceD2", 
    "PowerDeviceD3", 
}; 
static int NUM_PowerDeviceStates = sizeof(PowerDeviceStates)/sizeof(char*); 
 
///////////////////////////////////////////////////////////////////////////// 
///////////////////////////////////////////////////////////////////////////// 
//*	All these PrintXxx routines return TRUE if the buffer is filled 
//*	Safe way to put a character in a buffer 
 
#define PHDput(ch) if( (*pbufpos)>=max) return TRUE; else buffer[(*pbufpos)++] = (ch) 
 
///////////////////////////////////////////////////////////////////////////// 
//	Digits for decimal and hex conversions 
 
static char hexdigits[] = "0123456789ABCDEF"; 
 
///////////////////////////////////////////////////////////////////////////// 
///////////////////////////////////////////////////////////////////////////// 
 
BOOLEAN PrintChar( char* buffer, int max, int* pbufpos, char ch) 
{ 
	PHDput(ch); 
	return FALSE; 
} 
 
///////////////////////////////////////////////////////////////////////////// 
 
BOOLEAN PrintULONG( char* buffer, int max, int* pbufpos, ULONG v) 
{ 
	int digits; 
	ULONG v2; 
	int digno; 
 
	if( v==0) 
	{ 
		PHDput('0'); 
		return FALSE; 
	} 
	// Get number of digits 
	digits = 0; 
	v2 = v; 
	while(v2!=0) 
	{ 
		v2 /= 10; 
		digits++; 
	} 
	// Write out 
	(*pbufpos) += digits; 
	for( digno=1;digno<=digits;digno++) 
	{ 
		char ch = hexdigits[v%10]; 
		if( (*pbufpos)-digno0 && FormatSize<8) 
	{ 
		mask >>= 4*(8-FormatSize); 
		shift -= 4*(8-FormatSize); 
	} 
	else 
		FormatSize = 8; 
	for( digno=0;digno>shift; 
		PHDput(hexdigits[digit]); 
		mask >>= 4; 
		shift -= 4; 
	} 
	return FALSE; 
} 
 
///////////////////////////////////////////////////////////////////////////// 
 
BOOLEAN PrintANSIString( char* buffer, int max, int* pbufpos, char* s, int FormatSize) 
{ 
	char ch; 
	BOOLEAN sized = (FormatSize>0); 
	while( ch=*s++) 
	{ 
		PHDput(ch); 
		if( sized && (--FormatSize==0)) 
			break; 
	} 
	return FALSE; 
} 
 
///////////////////////////////////////////////////////////////////////////// 
 
BOOLEAN PrintWideString( char* buffer, int max, int* pbufpos, wchar_t* ws, int FormatSize) 
{ 
	wchar_t ch; 
	BOOLEAN sized = (FormatSize>0); 
	while( ch=*ws++) 
	{ 
		PHDput((char)ch); 
		if( sized && (--FormatSize==0)) 
			break; 
	} 
	return FALSE; 
} 
 
///////////////////////////////////////////////////////////////////////////// 
 
BOOLEAN PrintUnicodeString( char* buffer, int max, int* pbufpos, PUNICODE_STRING pus) 
{ 
	int uslen = (pus->Length) >> 1; 
	wchar_t* ws = pus->Buffer; 
	int chno; 
	for( chno=0;chnoMajorFunction; 
	ULONG MinorFunction = IrpStack->MinorFunction; 
	if( MajorFunctionParameters.Read.Length; 
				switch( pQueryDeviceRelations->Type) 
				{ 
				case Dbp_BusRelations:			ExtraDetails1 = "BusRelations"; break; 
				case Dbp_EjectionRelations:		ExtraDetails1 = "EjectionRelations"; break; 
				case Dbp_PowerRelations:		ExtraDetails1 = "PowerRelations"; break; 
				case Dbp_RemovalRelations:		ExtraDetails1 = "RemovalRelations"; break; 
				case Dbp_TargetDeviceRelation:	ExtraDetails1 = "TargetDeviceRelation"; break; 
				} 
			} 
			break; 
		case Dbp_IRP_MJ_POWER: 
			if( MinorFunctionParameters.Read.Length; 
				Dbp_POWER_STATE PowerState = pPower->State; 
				ExtraDetails1 = (MinorFunction==Dbp_IRP_MN_SET_POWER ? "Set Power" : "Query Power"); 
				if( pPower->Type==Dbp_SystemPowerState) 
				{ 
					if( PowerState.SystemState0) 
				FormatSize = size; 
			if( (ch=*format++) == '\0') 
				break; 
		} 
		else if( ch>='1' && ch<='9') 
		{ 
			FormatSize = 0; 
			do 
			{ 
				FormatSize = (FormatSize*10) + ch-'0'; 
				if( (ch=*format++) == '\0') 
					break; 
			} 
			while( ch>='1' && ch<='9'); 
		} 
		// Switch on specification character 
		switch( ch) 
		{ 
		//	ANSI char 
		case 'c': 
		{ 
			PHD_va_arg(marker,char,ch); 
			if( PrintChar(buffer,max,&bufpos,ch)) goto done; 
			break; 
		} 
		//	Wide char 
		case 'C': 
		{ 
			PHD_va_arg(marker,wchar_t,ch); 
			if( PrintChar(buffer,max,&bufpos,(char)ch)) goto done; 
			break; 
		} 
		//	Integer 
		case 'd': 
		case 'i': 
		{ 
			PHD_va_arg(marker,int,i); 
			if( PrintInt(buffer,max,&bufpos,i)) goto done; 
			break; 
		} 
		//	__int64 in hex 
		case 'D': 
		{ 
			PHD_va_arg(marker,__int64,i64); 
			if( PrintInt64(buffer,max,&bufpos,i64)) goto done; 
			break; 
		} 
		//	__int64 in hex 
		case 'l': 
		{ 
			PHD_va_arg(marker,__int64,i64); 
			LARGE_INTEGER li; 
			li.QuadPart = i64; 
			if( FormatSize==0) FormatSize=16; 
			if( FormatSize>8) 
			{ 
				if( PrintULONGhex(buffer,max,&bufpos,li.HighPart,FormatSize-8)) goto done; 
			} 
			if( PrintULONGhex(buffer,max,&bufpos,li.LowPart,FormatSize)) goto done; 
			break; 
		} 
		//	LARGE_INTEGER in hex 
		case 'L': 
		{ 
			PHD_va_arg(marker,LARGE_INTEGER,li); 
			if( FormatSize==0) FormatSize=16; 
			if( FormatSize>8) 
			{ 
				if( PrintULONGhex(buffer,max,&bufpos,li.HighPart,FormatSize-8)) goto done; 
			} 
			if( PrintULONGhex(buffer,max,&bufpos,li.LowPart,FormatSize)) goto done; 
			break; 
		} 
		//	ULONG 
		case 'u': 
		{ 
			PHD_va_arg(marker,ULONG,uv); 
			if( PrintULONG(buffer,max,&bufpos,uv)) goto done; 
			break; 
		} 
		// ULONG as hex 
		case 'x': 
		{ 
			PHD_va_arg(marker,ULONG,uv); 
			if( PrintULONGhex(buffer,max,&bufpos,uv,FormatSize)) goto done; 
			break; 
		} 
		//	ANSI string 
		case 's': 
		{ 
			PHD_va_arg(marker,char*,s); 
			if( PrintANSIString(buffer,max,&bufpos,s,FormatSize)) goto done; 
			break; 
		} 
		//	Wide string 
		case 'S': 
		{ 
			PHD_va_arg(marker,wchar_t*,ws); 
			if( PrintWideString(buffer,max,&bufpos,ws,FormatSize)) goto done; 
			break; 
		} 
		//	PUNICODE_STRING 
		case 'T': 
		{ 
			PHD_va_arg(marker,PUNICODE_STRING,pus); 
			if( PrintUnicodeString(buffer,max,&bufpos,pus)) goto done; 
			break; 
		} 
		//	PIRP 
		case 'I': 
		{ 
			PHD_va_arg(marker,PIRP,Irp); 
			if( PrintIrp(buffer,max,&bufpos,Irp)) goto done; 
			break; 
		} 
		default: 
			if( PrintChar(buffer,max,&bufpos,ch)) goto done; 
		} 
	} 
 
	// NULL terminate string 
	PrintChar(buffer,max,&bufpos,'\0'); 
done: 
	// Ensure string terminated 
	buffer[max-1] = '\0'; 
} 
 
////////////////////////////////////////////////////////////////////////////// 
//*	ANSIstrlen:	Return length of null terminated ANSI string 
 
USHORT ANSIstrlen( char* str) 
{ 
	USHORT len = 0; 
	for(;*str++!='\0';) 
		len++; 
	return len; 
} 
 
///////////////////////////////////////////////////////////////////////////// 
//*	DebugPrintSystemThread: 
// 
// Description: 
//		System thread to print device events. 
//		Check for events every second, and wait for ThreadEvent ExitNow 
// 
// IRQL: 
//		<= APC_LEVEL_IRQL 
// 
// Arguments: 
//		Context has no meaning 
// 
// Return Value: 
//		(None) 
 
void DebugPrintSystemThread( IN PVOID Context) 
{ 
	HANDLE DebugPrintDeviceHandle = NULL; 
	NTSTATUS status; 
	LARGE_INTEGER OneSecondTimeout; 
	LARGE_INTEGER ByteOffset; 
 
	///////////////////////////////////////////////////////////////////////// 
	// Lower thread priority 
 
	KeSetPriorityThread( KeGetCurrentThread(), LOW_REALTIME_PRIORITY); 
 
	///////////////////////////////////////////////////////////////////////// 
	// Attempt to open DebugPrt driver for 5 mins 
 
	status = OpenDebugPrintDriver( &DebugPrintDeviceHandle); 
	if( !NT_SUCCESS(status) || DebugPrintDeviceHandle==NULL) 
		goto exit1; 
	 
	///////////////////////////////////////////////////////////////////////// 
	// Set up timeout and byte offset values 
	 
	OneSecondTimeout.QuadPart = -1i64 * 1000000i64 * 10i64; 
	ByteOffset.QuadPart = 0i64; 
 
	///////////////////////////////////////////////////////////////////////// 
	// Loop waiting for events or ExitNow to go TRUE 
 
	while(TRUE) 
	{ 
		///////////////////////////////////////////////////////////////////// 
		// Wait for a request from DebugPrintMsg or DebugPrintClose 
 
		KeWaitForSingleObject( &ThreadEvent, Executive, KernelMode, FALSE, &OneSecondTimeout); 
 
		///////////////////////////////////////////////////////////////////// 
		//	Remove any Events from EventList and send to DebugPrint driver 
 
		while(TRUE) 
		{ 
			IO_STATUS_BLOCK IoStatus; 
			PDEBUGPRINT_EVENT pEvent; 
			ULONG EventDataLen; 
 
			PLIST_ENTRY pListEntry = ExInterlockedRemoveHeadList( &EventList, &EventListLock); 
			if( pListEntry==NULL) 
				break; 
 
			// Get event as DEBUGPRINT_EVENT 
			pEvent = CONTAINING_RECORD( pListEntry, DEBUGPRINT_EVENT, ListEntry); 
 
			// Get length of event data 
			EventDataLen = pEvent->Len; 
 
			// Send event to DebugPrint 
			status = ZwWriteFile(	DebugPrintDeviceHandle, NULL, NULL, NULL, 
									&IoStatus, pEvent->EventData, EventDataLen, &ByteOffset, NULL); 
//			if( status!=STATUS_SUCCESS) 
//				return; 
//			if( IoStatus.Information != len) 
//				return; 
 
			// Free our event buffer 
			ExFreePool(pEvent); 
		} 
 
		///////////////////////////////////////////////////////////////////// 
		// DebugPrintClose called, so stop the thread now. 
 
		if( ExitNow) 
			break; 
	} 
 
	///////////////////////////////////////////////////////////////////////// 
	// Tidy up and terminate thread 
	ZwClose(DebugPrintDeviceHandle); 
exit1: 
	DebugPrintStarted = FALSE; 
	ClearEvents(); 
	KeSetEvent( &ThreadExiting, 0, FALSE); 
	PsTerminateSystemThread(STATUS_SUCCESS); 
} 
 
////////////////////////////////////////////////////////////////////////////// 
//*	OpenDebugPrintDriver:	Attempt to open DebugPrt driver for 5 mins 
//							Try opening every 20 seconds. 
//							This gives DebugPrt time to start at boot time 
 
NTSTATUS OpenDebugPrintDriver( HANDLE* pDebugPrintDeviceHandle) 
{ 
	NTSTATUS status; 
	UNICODE_STRING DebugPrintName; 
	OBJECT_ATTRIBUTES ObjectAttributes; 
	IO_STATUS_BLOCK IoStatus; 
	LARGE_INTEGER OneSecondTimeout; 
	int DelaySeconds = 0; 
 
	// Set up timeout 
	OneSecondTimeout.QuadPart = -1i64 * 1000000i64 * 10i64; 
 
	// Make DebugPrint device name as UNICODE_STRING 
	RtlInitUnicodeString( &DebugPrintName, L"\\Device\\PHDDebugPrint"); 
 
	// Make appropriate ObjectAttributes for ZwCreateFile 
	InitializeObjectAttributes( &ObjectAttributes, &DebugPrintName, OBJ_CASE_INSENSITIVE, NULL, NULL); 
 
	////////////////////////////////////////////////////////////////////////// 
	// Attempt to open DebugPrt handle for 5 minutes 
 
	while( DelaySeconds<5*60) 
	{ 
		int Delay; 
 
		////////////////////////////////////////////////////////////////////// 
		// Open handle to DebugPrint device 
 
		status = ZwCreateFile( pDebugPrintDeviceHandle, 
			GENERIC_READ | GENERIC_WRITE, 
			&ObjectAttributes, 
			&IoStatus, 
			0, // alloc size = none 
			FILE_ATTRIBUTE_NORMAL, 
			FILE_SHARE_READ|FILE_SHARE_WRITE, 
			FILE_OPEN, 
			0, 
			NULL,  // eabuffer 
			0 );   // ealength 
 
		if( NT_SUCCESS(status) && *pDebugPrintDeviceHandle!=NULL) 
			return status; 
 
		////////////////////////////////////////////////////////////////////// 
		// Wait for 20 seconds. 
 
		for( Delay=20; Delay>0; Delay--) 
		{ 
			KeWaitForSingleObject( &ThreadEvent, Executive, KernelMode, FALSE, &OneSecondTimeout); 
			if( ExitNow) 
				return status; 
			DelaySeconds++; 
		} 
	} 
	return status; 
} 
 
////////////////////////////////////////////////////////////////////////////// 
//*	ClearEvents:	Clear any remaining events 
 
void ClearEvents() 
{ 
	while(TRUE) 
	{ 
		PDEBUGPRINT_EVENT pEvent; 
		PLIST_ENTRY pListEntry = ExInterlockedRemoveHeadList( &EventList, &EventListLock); 
		if( pListEntry==NULL) 
			break; 
		pEvent = CONTAINING_RECORD( pListEntry, DEBUGPRINT_EVENT, ListEntry); 
		ExFreePool(pEvent); 
	} 
} 
////////////////////////////////////////////////////////////////////////////// 
 
#endif  // DODEBUGPRINT