DEBDEBUG.C


/******************************************************************************\
* This is a part of the Microsoft Source Code Samples.
* Copyright 1993 - 1998 Microsoft Corporation.
* All rights reserved.
* This source code is only intended as a supplement to
* Microsoft Development Tools and/or WinHelp documentation.
* See these sources for detailed information regarding the
* Microsoft samples programs.
\******************************************************************************/

// ************************************************************************
// MODULE : DEBDebug.C
// PURPOSE : Debug support functions for the Debug Event Browser
// FUNCTIONS :
// DebugEventThread() - debug event processing thread
// COMMENTS :
//
// ************************************************************************
#define STRICT // enable strict typing
#include <Windows.H> // required for all Windows applications
#include <StdDef.H> // offsetof()

#include "LinkList.H" // include the linked list functions
#include "DEBMisc.H" // include the misc. support functions
#include "DEBDebug.H" // include the DEB debugging functions

// internal global data
// ------------------------------------------------------------------------
HANDLE hHeap; // local heap
PLIST pProcessList; // pointer to the process list
BOOL fFinished = FALSE; // set to TRUE if the DebugEventThread
// is no longer needed (such as the
// debuggee failed to load or the number
// of debuggee threads goes to zero
// indicating debug session termination

static TCHAR szSourceFileName[] = TEXT(__FILE__);

// internal function prototypes
// ------------------------------------------------------------------------

//-- debug event handling functions
BOOL HandleExceptionEvent( LPDEBUG_EVENT );
BOOL HandleBreakPointException( LPDEBUG_EVENT );
BOOL HandleCreateThreadEvent( LPDEBUG_EVENT );
BOOL HandleCreateProcessEvent( LPDEBUG_EVENT );
BOOL HandleExitThreadEvent( LPDEBUG_EVENT );
BOOL HandleExitProcessEvent( LPDEBUG_EVENT );
BOOL HandleLoadDllEvent( LPDEBUG_EVENT );
BOOL HandleUnloadDllEvent( LPDEBUG_EVENT );
BOOL HandleOutputDebugStringEvent( LPDEBUG_EVENT );
BOOL HandleRipEvent( LPDEBUG_EVENT );
BOOL HandleUnknownEvent( LPDEBUG_EVENT );

//-- misc debug event helper functions
BOOL DebugNewProcess( LPTSTR, LPTSTR );
BOOL GetDllFileName( LPDEBUG_EVENT, LPTSTR, DWORD );
BOOL GetDllFileNameFromList( LPDEBUG_EVENT, LPTSTR, DWORD );
BOOL GetOutputDebugString( LPDEBUG_EVENT, LPTSTR, DWORD );
DWORD GetModuleFileNameFromHeader( HANDLE, HANDLE, DWORD, LPTSTR, DWORD );
#if defined(_MIPS_) || defined(_ALPHA_) || defined(_PPC_)
BOOL SkipBreakPoint( HANDLE );
#endif

//-- linked list wrapper functions

//-- process list and node specific linked list wrapper functions
int ProcessOrderFunction( PNODE, PNODE );
BOOL CreateProcessList( PLIST* );
BOOL DestroyProcessList( PLIST );
BOOL AllocProcessNode( PNODE*, PDEB_PROCESS_NODE_INFO* );
BOOL InitProcessNodeInfo( PDEB_PROCESS_NODE_INFO*, LPDEBUG_EVENT );
BOOL InsertProcessNode( PLIST, PNODE );
BOOL SetCurrentProcessNode( PLIST, PNODE );
BOOL DeleteProcessNode( PLIST, PNODE );
BOOL FreeProcessNodeInfo( PNODE );
BOOL DestroyProcessNode( PNODE );
BOOL DeleteCurrentProcessNode( PLIST );

//-- thread list and node specific linked list wrapper functions
int ThreadOrderFunction( PNODE, PNODE );
BOOL CreateThreadList( PLIST* );
BOOL DestroyThreadList( PLIST );
BOOL AllocThreadNode( PNODE*, PDEB_THREAD_NODE_INFO* );
BOOL InitThreadNodeInfo( PDEB_THREAD_NODE_INFO*, LPDEBUG_EVENT );
BOOL InsertThreadNode( PLIST, PNODE );
BOOL SetCurrentThreadNode( PLIST, PNODE );
BOOL DeleteThreadNode( PLIST, PNODE );
BOOL FreeThreadNodeInfo( PNODE );
BOOL DestroyThreadNode( PNODE );
BOOL DeleteCurrentThreadNode( PLIST );

//-- dll list and node specific linked list wrapper functions
int DllOrderFunction( PNODE, PNODE );
BOOL CreateDllList( PLIST* );
BOOL DestroyDllList( PLIST );
BOOL AllocDllNode( PNODE*, PDEB_DLL_NODE_INFO* );
BOOL InitDllNodeInfo( PDEB_DLL_NODE_INFO*, LPDEBUG_EVENT );
BOOL InsertDllNode( PLIST, PNODE );
BOOL SetCurrentDllNode( PLIST, PNODE );
BOOL DeleteDllNode( PLIST, PNODE );
BOOL FreeDllNodeInfo( PNODE );
BOOL DestroyDllNode( PNODE );
BOOL DeleteCurrentDllNode( PLIST );


// ************************************************************************
// FUNCTION : DebugEventThread( PDEB_STARTUP_INFO )
// PURPOSE : Main debug event processing loop
// COMMENTS :
// A new debug event thread is created for each Debuggee process.
// Return TRUE (non 0) if success, else FALSE (0)
// ************************************************************************
DWORD WINAPI
DebugEventThread( PDEB_STARTUP_INFO pDebStartupInfo )
{
#define BUFFER_SIZE 256

static BOOL fFirstTime = TRUE;
static TCHAR szDebuggeeTitle[128];

DEBUG_EVENT DebugEvent;

LPTSTR lpszDebugEventBuffer;
LPTSTR lpszTempBuffer;

//-- set the minimum error level for debugging events
SetDebugErrorLevel( Profile.DebugErrorLevel );

if( fFirstTime ) {
if( !LoadString( Global.hInstance, IDS_OFN_DEBUGGEE_TITLE, szDebuggeeTitle,
sizeof(szDebuggeeTitle)/sizeof(TCHAR) ) )
ErrorMessageBox( TEXT("LoadString()"),
Global.szApiFailed, szSourceFileName, __LINE__ );
}

//-- determine if 'attach to' or 'open new' debuggee
if( pDebStartupInfo->fActive ) {
if( !DebugActiveProcess( pDebStartupInfo->dwProcessId ) )
ErrorMessageBox( TEXT("DebugActiveProcess()"),
Global.szApiFailed, szSourceFileName, __LINE__ );
}
else {
if( !DebugNewProcess( pDebStartupInfo->lpstrPathName, szDebuggeeTitle ) )
ExitThread( FALSE );
}

//-- increment active process count
Global.dwActiveDebuggees++;

//-- create a local heap
{
SYSTEM_INFO SysInfo;

GetSystemInfo( &SysInfo ); // get the system memory page size
hHeap = HeapCreate( (DWORD) NULL, SysInfo.dwPageSize, 1000*SysInfo.dwPageSize );
}

//-- create and initialize the process list
CreateProcessList( &pProcessList );

//-- alloc temporary (life of thread) string buffers
lpszDebugEventBuffer = (LPTSTR) HeapAlloc( hHeap, (DWORD) NULL, BUFFER_SIZE );
lpszTempBuffer = (LPTSTR) HeapAlloc( hHeap, (DWORD) NULL, BUFFER_SIZE );

// ----------------------------------------------------------------------
// begin debug event processing loop
// ----------------------------------------------------------------------
for(;;) {

//-- wait for debug events
if( !WaitForDebugEvent( &DebugEvent, INFINITE ) ) {
ListBoxPrintF( pDebStartupInfo->hWndListBox, TEXT( "%s" ), TEXT( "Failed to attach to Debuggee..." ) );
fFinished = TRUE;
break;
}

// --------------------------------------------------------------------
// display each debug event as it occurs and handle minimal debug
// event processing
// --------------------------------------------------------------------
MakeCommonDebugEventString( lpszDebugEventBuffer, &DebugEvent );

switch( DebugEvent.dwDebugEventCode ) {

// ------------------------------------------------------------------
// exception occured
// ------------------------------------------------------------------
case EXCEPTION_DEBUG_EVENT:

//-- figure out which type of exception
switch( DebugEvent.u.Exception.ExceptionRecord.ExceptionCode ) {

//--standard exceptions
case EXCEPTION_ACCESS_VIOLATION:
StringAppendF( lpszDebugEventBuffer, TEXT( "%s%s" ),
TEXT( "Exception: " ), TEXT( "Access Violation" ) );
break;

case EXCEPTION_DATATYPE_MISALIGNMENT:
StringAppendF( lpszDebugEventBuffer, TEXT( "%s%s" ),
TEXT( "Exception: " ), TEXT( "Datatype Misalignment" ) );
break;

case EXCEPTION_BREAKPOINT:
StringAppendF( lpszDebugEventBuffer, TEXT( "%s%s" ),
TEXT( "Exception: " ), TEXT( "Breakpoint" ) );
break;

case EXCEPTION_SINGLE_STEP:
StringAppendF( lpszDebugEventBuffer, TEXT( "%s%s" ),
TEXT( "Exception: " ), TEXT( "Single Step" ) );
break;

case EXCEPTION_ARRAY_BOUNDS_EXCEEDED:
StringAppendF( lpszDebugEventBuffer, TEXT( "%s%s" ),
TEXT( "Exception: " ), TEXT( "Array Bound Exceeded" ) );
break;

case EXCEPTION_FLT_DENORMAL_OPERAND:
StringAppendF( lpszDebugEventBuffer, TEXT( "%s%s %s" ),
TEXT( "Exception: " ), TEXT( "Floating Point" ),
TEXT( "Denormal Operand" ) );
break;

case EXCEPTION_FLT_DIVIDE_BY_ZERO:
StringAppendF( lpszDebugEventBuffer, TEXT( "%s%s %s" ),
TEXT( "Exception: " ), TEXT( "Floating Point" ),
TEXT( "Divide By Zero" ) );
break;

case EXCEPTION_FLT_INEXACT_RESULT:
StringAppendF( lpszDebugEventBuffer, TEXT( "%s%s %s" ),
TEXT( "Exception: " ), TEXT( "Floating Point" ),
TEXT( "Inexact Result" ) );
break;

case EXCEPTION_FLT_INVALID_OPERATION:
StringAppendF( lpszDebugEventBuffer, TEXT( "%s%s %s" ),
TEXT( "Exception: " ), TEXT( "Floating Point" ),
TEXT( "Invalid Operation" ) );
break;

case EXCEPTION_FLT_OVERFLOW:
StringAppendF( lpszDebugEventBuffer, TEXT( "%s%s %s" ),
TEXT( "Exception: " ), TEXT( "Floating Point" ),
TEXT( "Overflow" ) );
break;

case EXCEPTION_FLT_STACK_CHECK:
StringAppendF( lpszDebugEventBuffer, TEXT( "%s%s %s" ),
TEXT( "Exception: " ), TEXT( "Floating Point" ),
TEXT( "Stack Check" ) );
break;

case EXCEPTION_FLT_UNDERFLOW:
StringAppendF( lpszDebugEventBuffer, TEXT( "%s%s %s" ),
TEXT( "Exception: " ), TEXT( "Floating Point" ),
TEXT( "Underflow" ) );
break;

case EXCEPTION_INT_DIVIDE_BY_ZERO:
StringAppendF( lpszDebugEventBuffer, TEXT( "%s%s %s" ),
TEXT( "Exception: " ), TEXT( "Integer" ),
TEXT( "Divide By Zero" ) );
break;

case EXCEPTION_INT_OVERFLOW:
StringAppendF( lpszDebugEventBuffer, TEXT( "%s%s %s" ),
TEXT( "Exception: " ), TEXT( "Integer" ),
TEXT( "Overflow" ) );
break;

case EXCEPTION_PRIV_INSTRUCTION:
StringAppendF( lpszDebugEventBuffer, TEXT( "%s%s" ),
TEXT( "Exception: " ), TEXT( "Privileged Instruction" ) );
break;

case EXCEPTION_IN_PAGE_ERROR:
StringAppendF( lpszDebugEventBuffer, TEXT( "%s%s" ),
TEXT( "Exception: " ), TEXT( "In Page Error" ) );
break;

//-- Debug exceptions
case DBG_TERMINATE_THREAD:
StringAppendF( lpszDebugEventBuffer, TEXT( "%s%s" ),
TEXT( "Debug Exception: " ), TEXT( "Terminate Thread" ) );
break;

case DBG_TERMINATE_PROCESS:
StringAppendF( lpszDebugEventBuffer, TEXT( "%s%s" ),
TEXT( "Debug Exception: " ), TEXT( "Terminate Process" ) );
break;

case DBG_CONTROL_C:
StringAppendF( lpszDebugEventBuffer, TEXT( "%s%s" ),
TEXT( "Debug Exception: " ), TEXT( "Control+C" ) );
break;

case DBG_CONTROL_BREAK:
StringAppendF( lpszDebugEventBuffer, TEXT( "%s%s" ),
TEXT( "Debug Exception: " ), TEXT( "Control+Break" ) );
break;

//-- RPC exceptions (some)
case RPC_S_UNKNOWN_IF:
StringAppendF( lpszDebugEventBuffer, TEXT( "%s%s" ),
TEXT( "RPC Exception: " ), TEXT( "Unknown Interface" ) );
break;

case RPC_S_SERVER_UNAVAILABLE:
StringAppendF( lpszDebugEventBuffer, TEXT( "%s%s" ),
TEXT( "RPC Exception: " ), TEXT( "Server Unavailable" ) );
break;

//-- VDM exceptions (minimal information)
case EXCEPTION_VDM_EVENT: // see DEBDebug.H for definition
StringAppendF( lpszDebugEventBuffer, TEXT( "%s" ),
TEXT( "VDM Exception: " ) );

default:
StringAppendF( lpszDebugEventBuffer, TEXT( "%s%s%X%s" ),
TEXT( "Exception: " ), TEXT( "Unknown [0x" ),
DebugEvent.u.Exception.ExceptionRecord.ExceptionCode,
TEXT( "]" ) );
break;

}
if( Profile.fVerbose ) {
StringAppendF( lpszDebugEventBuffer, TEXT( "\n + %s%d" ),
TEXT( "dwFirstChance: " ), DebugEvent.u.Exception.dwFirstChance );
}
else {
if( DebugEvent.u.Exception.dwFirstChance != 0 )
StringAppendF( lpszDebugEventBuffer, TEXT( "%s" ),
TEXT( " - First Chance" ) );
else
StringAppendF( lpszDebugEventBuffer, TEXT( "%s" ),
TEXT( " - Second Chance" ) );
}
HandleExceptionEvent( &DebugEvent );
break;

// ------------------------------------------------------------------
// new thread started
// ------------------------------------------------------------------
case CREATE_THREAD_DEBUG_EVENT:
StringAppendF( lpszDebugEventBuffer, TEXT( "%s" ),
TEXT( "Create Thread: " ) );
if( Profile.fVerbose ) {
StringAppendF( lpszDebugEventBuffer, TEXT( "\n + %s%d\n + %s%X\n + %s%d" ),
TEXT( "hThread:0x" ), DebugEvent.u.CreateThread.hThread,
TEXT( "lpThreadLocalBase:0x" ), DebugEvent.u.CreateThread.lpThreadLocalBase,
TEXT( "lpStartAddress:0x" ), DebugEvent.u.CreateThread.lpStartAddress );
}
HandleCreateThreadEvent( &DebugEvent );
break;

// ------------------------------------------------------------------
// new process started
// ------------------------------------------------------------------
case CREATE_PROCESS_DEBUG_EVENT:
StringAppendF( lpszDebugEventBuffer, TEXT( "%s" ),
TEXT( "Create Process: " ) );
if( Profile.fVerbose ) {
StringAppendF( lpszDebugEventBuffer,
TEXT( "\n + %s%X\n + %s%X\n + %s%X\n + %s%X\n + %s%d" )
TEXT( "\n + %s%d\n + %s%X\n + %s%X\n + %s%X\n + %s%d" ),
TEXT( "hFile:0x" ), DebugEvent.u.CreateProcessInfo.hFile,
TEXT( "hProcess:0x" ), DebugEvent.u.CreateProcessInfo.hProcess,
TEXT( "hThread:0x" ), DebugEvent.u.CreateProcessInfo.hThread,
TEXT( "lpBaseOfImage:0x" ), DebugEvent.u.CreateProcessInfo.lpBaseOfImage,
TEXT( "dwDebugInfoFileOffset: " ), DebugEvent.u.CreateProcessInfo.dwDebugInfoFileOffset,
TEXT( "nDebugInfoSize: " ), DebugEvent.u.CreateProcessInfo.nDebugInfoSize,
TEXT( "lpThreadLocalBase:0x" ), DebugEvent.u.CreateProcessInfo.lpThreadLocalBase,
TEXT( "lpStartAddress:0x" ), DebugEvent.u.CreateProcessInfo.lpStartAddress,
TEXT( "lpImageName:0x" ), DebugEvent.u.CreateProcessInfo.lpImageName,
TEXT( "fUnicode: " ), DebugEvent.u.CreateProcessInfo.fUnicode );
}
HandleCreateProcessEvent( &DebugEvent );
break;

// ------------------------------------------------------------------
// existing thread terminated
// ------------------------------------------------------------------
case EXIT_THREAD_DEBUG_EVENT:
StringAppendF( lpszDebugEventBuffer, TEXT( "%s" ),
TEXT( "Exit Thread: " ) );
if( Profile.fVerbose ) {
StringAppendF( lpszDebugEventBuffer, TEXT( "\n + %s%d" ),
TEXT( "dwExitCode: " ), DebugEvent.u.ExitThread.dwExitCode );
}
else {
StringAppendF( lpszDebugEventBuffer, TEXT( "%s%d" ),
TEXT( "Returned " ), DebugEvent.u.ExitThread.dwExitCode );
}
HandleExitThreadEvent( &DebugEvent );
break;

// ------------------------------------------------------------------
// existing process terminated
// ------------------------------------------------------------------
case EXIT_PROCESS_DEBUG_EVENT:
StringAppendF( lpszDebugEventBuffer, TEXT( "%s" ),
TEXT( "Exit Process: " ) );
if( Profile.fVerbose ) {
StringAppendF( lpszDebugEventBuffer, TEXT( "\n + %s%d" ),
TEXT( "dwExitCode: " ), DebugEvent.u.ExitProcess.dwExitCode );
}
else {
StringAppendF( lpszDebugEventBuffer, TEXT( "%s%d" ),
TEXT( "Returned " ), DebugEvent.u.ExitProcess.dwExitCode );
}
HandleExitProcessEvent( &DebugEvent );
break;

// ------------------------------------------------------------------
// new DLL loaded
// ------------------------------------------------------------------
case LOAD_DLL_DEBUG_EVENT:
StringAppendF( lpszDebugEventBuffer, TEXT( "%s" ),
TEXT( "Load DLL: " ) );
lstrcpy( lpszTempBuffer, TEXT("Empty!") );
GetDllFileName( &DebugEvent, lpszTempBuffer, BUFFER_SIZE );
StringAppendF( lpszDebugEventBuffer, TEXT( "%s" ), lpszTempBuffer );
if( Profile.fVerbose ) {
StringAppendF( lpszDebugEventBuffer,
TEXT( "\n + %s%X\n + %s%X\n + %s%d\n + %s%d\n + %s%X\n + %s%d" ),
TEXT( "hFile:0x" ), DebugEvent.u.LoadDll.hFile,
TEXT( "lpBaseOfDll:0x" ), DebugEvent.u.LoadDll.lpBaseOfDll,
TEXT( "dwDebugInfoFileOffset: " ), DebugEvent.u.LoadDll.dwDebugInfoFileOffset,
TEXT( "nDebugInfoSize: " ), DebugEvent.u.LoadDll.nDebugInfoSize,
TEXT( "lpImageName:0x" ), DebugEvent.u.LoadDll.lpImageName,
TEXT( "fUnicode: " ), DebugEvent.u.LoadDll.fUnicode );
}
HandleLoadDllEvent( &DebugEvent );
break;

// ------------------------------------------------------------------
// existing DLL explicitly unloaded
// ------------------------------------------------------------------
case UNLOAD_DLL_DEBUG_EVENT:
StringAppendF( lpszDebugEventBuffer, TEXT( "%s" ), TEXT( "Unload DLL: " ) );
GetDllFileNameFromList( &DebugEvent, lpszTempBuffer, BUFFER_SIZE );
StringAppendF( lpszDebugEventBuffer, TEXT( "%s" ), lpszTempBuffer );
if( Profile.fVerbose ) {
StringAppendF( lpszDebugEventBuffer, TEXT( "\n + %s%X" ),
TEXT( "lpBaseOfDLL:0x" ), DebugEvent.u.UnloadDll.lpBaseOfDll );
}
HandleUnloadDllEvent( &DebugEvent );
break;

// ------------------------------------------------------------------
// OutputDebugString() occured
// ------------------------------------------------------------------
case OUTPUT_DEBUG_STRING_EVENT:
StringAppendF( lpszDebugEventBuffer, TEXT( "%s" ),
TEXT( "Output Debug String: " ) );
GetOutputDebugString( &DebugEvent, lpszTempBuffer, BUFFER_SIZE );
StringAppendF( lpszDebugEventBuffer, TEXT( "%s" ), lpszTempBuffer );
if( Profile.fVerbose ) {
StringAppendF( lpszDebugEventBuffer, TEXT( "\n + %s%X\n + %s%d\n + %s%d" ),
TEXT( "lpDebugStringData:0x" ), DebugEvent.u.DebugString.lpDebugStringData,
TEXT( "fUnicode: " ), DebugEvent.u.DebugString.fUnicode,
TEXT( "nDebugStringLength: " ), DebugEvent.u.DebugString.nDebugStringLength );
}
HandleOutputDebugStringEvent( &DebugEvent );
break;

// ------------------------------------------------------------------
// RIP occured
// ------------------------------------------------------------------
case RIP_EVENT:
StringAppendF( lpszDebugEventBuffer, TEXT( "%s" ),
TEXT( "RIP" ) );
if( Profile.fVerbose ) {
StringAppendF( lpszDebugEventBuffer, TEXT( "\n + %s%d\n + %s%d" ),
TEXT( "dwError: " ), DebugEvent.u.RipInfo.dwError,
TEXT( "dwType: " ), DebugEvent.u.RipInfo.dwType );
}
HandleRipEvent( &DebugEvent );
break;

// ------------------------------------------------------------------
// unknown debug event occured
// ------------------------------------------------------------------
default:
StringAppendF( lpszDebugEventBuffer, TEXT( "%s%X%s" ),
TEXT( "Debug Event:Unknown [0x" ),
DebugEvent.dwDebugEventCode, lpszTempBuffer,
TEXT( "]" ) );
HandleUnknownEvent( &DebugEvent );
break;
}

//-- insert the debug event string into the listbox
ListBoxPrintF( pDebStartupInfo->hWndListBox, TEXT( "%s" ), lpszDebugEventBuffer );

//-- default action, just continue
if( fFinished ) {
fFinished = FALSE; // reset the value
break;
}
else
ContinueDebugEvent( DebugEvent.dwProcessId, DebugEvent.dwThreadId,
DBG_CONTINUE );
}

//-- free temporary (life of thread) string buffers
HeapFree( hHeap, (DWORD) NULL, (PVOID) lpszDebugEventBuffer );
HeapFree( hHeap, (DWORD) NULL, (PVOID) lpszTempBuffer );

//-- free list
DestroyProcessList( pProcessList );

//-- free the heap
HeapDestroy( hHeap );

//-- decrement active process count
Global.dwActiveDebuggees--;
ExitThread( TRUE );

return( TRUE ); // avoid the "no return value" warning
}


// ========================================================================
// debug event handling functions
// ========================================================================


// ************************************************************************
// FUNCTION : HandleExceptionEvent( LPDEBUG_EVENT lpDebugEvent )
// PURPOSE : handle EXCEPTION_DEBUG_EVENT
// COMMENTS : except for the BreakPoint event, continue and let the
// application or system exception handlers to the work
// ************************************************************************
BOOL
HandleExceptionEvent( LPDEBUG_EVENT lpDebugEvent )
{
switch( lpDebugEvent->u.Exception.ExceptionRecord.ExceptionCode ) {

case EXCEPTION_BREAKPOINT:
HandleBreakPointException( lpDebugEvent );
break;

default:
ContinueDebugEvent( lpDebugEvent->dwProcessId, lpDebugEvent->dwThreadId,
DBG_EXCEPTION_NOT_HANDLED );

}

return( TRUE );
}


// ************************************************************************
// FUNCTION : HandleBreakPointException( LPDEBUG_EVENT lpDebugEvent )
// PURPOSE : handle the BREAKPOINT exception
// COMMENTS : search process list, search thread list, skip over breakpoint
// ************************************************************************
BOOL
HandleBreakPointException( LPDEBUG_EVENT lpDebugEvent )
{
#if defined(_MIPS_) || defined(_ALPHA_) || defined(_PPC_)
PNODE pProcessNode, pSearchProcessNode;
PNODE pThreadNode, pSearchThreadNode;
PDEB_PROCESS_NODE_INFO pProcessNodeInfo, pSearchProcessNodeInfo;
PDEB_THREAD_NODE_INFO pThreadNodeInfo, pSearchThreadNodeInfo;
PDEB_THREAD_LIST_INFO pThreadListInfo;

AllocProcessNode( &pSearchProcessNode, &pSearchProcessNodeInfo );
pSearchProcessNodeInfo->dwProcessId = lpDebugEvent->dwProcessId;
SetCurrentProcessNode( pProcessList, pSearchProcessNode );
GetCurrentNode( pProcessList, &pProcessNode );
pProcessNodeInfo = (PDEB_PROCESS_NODE_INFO) pProcessNode->pNodeData;
pThreadListInfo = (PDEB_THREAD_LIST_INFO) pProcessNodeInfo->pThreadList->pListData;
//-- if no thread nodes then hThread is stored in the process node
if( !pThreadListInfo->dwActiveThreads ) {
SkipBreakPoint( (pProcessNodeInfo->ProcessDebugInfo).hThread );
}
else {
AllocThreadNode( &pSearchThreadNode, &pSearchThreadNodeInfo );
pSearchThreadNodeInfo->dwThreadId = lpDebugEvent->dwThreadId;
SetCurrentThreadNode( pProcessNodeInfo->pThreadList, pSearchThreadNode );
GetCurrentNode( pProcessNodeInfo->pThreadList, &pThreadNode );
pThreadNodeInfo = (PDEB_THREAD_NODE_INFO) pThreadNode->pNodeData;
SkipBreakPoint( (pThreadNodeInfo->ThreadDebugInfo).hThread );
DestroyThreadNode( pSearchThreadNode );
}
DestroyProcessNode( pSearchProcessNode );
#else
ContinueDebugEvent( lpDebugEvent->dwProcessId, lpDebugEvent->dwThreadId,
DBG_CONTINUE );
#endif

return( TRUE );
}


// ************************************************************************
// FUNCTION : HandleCreateThreadEvent( LPDEBUG_EVENT )
// PURPOSE : handle CREATE_THREAD_DEBUG_EVENT
// COMMENTS : search process list, insert new thread node
// ************************************************************************
BOOL
HandleCreateThreadEvent( LPDEBUG_EVENT lpDebugEvent )
{
PNODE pProcessNode, pSearchProcessNode;
PNODE pThreadNode;
PDEB_PROCESS_NODE_INFO pProcessNodeInfo, pSearchProcessNodeInfo;
PDEB_THREAD_NODE_INFO pThreadNodeInfo;

AllocProcessNode( &pSearchProcessNode, &pSearchProcessNodeInfo );
pSearchProcessNodeInfo->dwProcessId = lpDebugEvent->dwProcessId;
SetCurrentProcessNode( pProcessList, pSearchProcessNode );
GetCurrentNode( pProcessList, &pProcessNode );
pProcessNodeInfo = (PDEB_PROCESS_NODE_INFO) pProcessNode->pNodeData;
AllocThreadNode( &pThreadNode, &pThreadNodeInfo );
InitThreadNodeInfo( &pThreadNodeInfo, lpDebugEvent );
InsertThreadNode( pProcessNodeInfo->pThreadList, pThreadNode );
DestroyProcessNode( pSearchProcessNode );

return( TRUE );
}


// ************************************************************************
// FUNCTION : HandleCreateProcessEvent( LPDEBUG_EVENT )
// PURPOSE : handle CREATE_PROCESS_DEBUG_EVENT
// COMMENTS : insert new process node
// ************************************************************************
BOOL
HandleCreateProcessEvent( LPDEBUG_EVENT lpDebugEvent )
{
PNODE pProcessNode;
PDEB_PROCESS_NODE_INFO pProcessNodeInfo;

AllocProcessNode( &pProcessNode, &pProcessNodeInfo );
InitProcessNodeInfo( &pProcessNodeInfo, lpDebugEvent );
InsertProcessNode( pProcessList, pProcessNode );

return( TRUE );
}


// ************************************************************************
// FUNCTION : HandleExitThreadEvent( LPDEBUG_EVENT )
// PURPOSE : handle EXIT_THREAD_DEBUG_EVENT
// COMMENTS : search process list, search thread list, delete existing
// thread node
// ************************************************************************
BOOL
HandleExitThreadEvent( LPDEBUG_EVENT lpDebugEvent )
{
PNODE pProcessNode, pSearchProcessNode;
PNODE pSearchThreadNode;
PDEB_PROCESS_NODE_INFO pProcessNodeInfo, pSearchProcessNodeInfo;
PDEB_THREAD_NODE_INFO pSearchThreadNodeInfo;

AllocProcessNode( &pSearchProcessNode, &pSearchProcessNodeInfo );
pSearchProcessNodeInfo->dwProcessId = lpDebugEvent->dwProcessId;
SetCurrentProcessNode( pProcessList, pSearchProcessNode );
GetCurrentNode( pProcessList, &pProcessNode );
pProcessNodeInfo = (PDEB_PROCESS_NODE_INFO) pProcessNode->pNodeData;
AllocThreadNode( &pSearchThreadNode, &pSearchThreadNodeInfo );
pSearchThreadNodeInfo->dwThreadId = lpDebugEvent->dwThreadId;
DeleteThreadNode( pProcessNodeInfo->pThreadList, pSearchThreadNode );
DestroyThreadNode( pSearchThreadNode );
DestroyProcessNode( pSearchProcessNode );

return( TRUE );
}


// ************************************************************************
// FUNCTION : HandleExitProcessEvent( LPDEBUG_EVENT )
// PURPOSE : handle EXIT_PROCESS_DEBUG_EVENT
// COMMENTS : search process list, delete existing process node,
// ************************************************************************
BOOL
HandleExitProcessEvent( LPDEBUG_EVENT lpDebugEvent )
{
PNODE pSearchProcessNode;
PDEB_PROCESS_NODE_INFO pSearchProcessNodeInfo;
PDEB_PROCESS_LIST_INFO pProcessListInfo;

AllocProcessNode( &pSearchProcessNode, &pSearchProcessNodeInfo );
pSearchProcessNodeInfo->dwProcessId = lpDebugEvent->dwProcessId;
DeleteProcessNode( pProcessList, pSearchProcessNode );
//-- if last process? free all temporary memory, exit thread
pProcessListInfo = (PDEB_PROCESS_LIST_INFO) pProcessList->pListData;
if( !pProcessListInfo->dwActiveProcesses )
fFinished = TRUE;
DestroyProcessNode( pSearchProcessNode );

return( TRUE );
}


// ************************************************************************
// FUNCTION : HandleLoadDllEvent( LPDEBUG_EVENT )
// PURPOSE : handle LOAD_DLL_DEBUG_EVENT
// COMMENTS : search process list, insert new DLL node
// ************************************************************************
BOOL
HandleLoadDllEvent( LPDEBUG_EVENT lpDebugEvent )
{
PNODE pProcessNode, pSearchProcessNode;

PNODE                  pDllNode; 
PDEB_PROCESS_NODE_INFO pProcessNodeInfo, pSearchProcessNodeInfo;
PDEB_DLL_NODE_INFO pDllNodeInfo;

AllocProcessNode( &pSearchProcessNode, &pSearchProcessNodeInfo );
pSearchProcessNodeInfo->dwProcessId = lpDebugEvent->dwProcessId;
SetCurrentProcessNode( pProcessList, pSearchProcessNode );
GetCurrentNode( pProcessList, &pProcessNode );
pProcessNodeInfo = (PDEB_PROCESS_NODE_INFO) pProcessNode->pNodeData;
AllocDllNode( &pDllNode, &pDllNodeInfo );
InitDllNodeInfo( &pDllNodeInfo, lpDebugEvent );
InsertDllNode( pProcessNodeInfo->pDllList, pDllNode );
DestroyProcessNode( pSearchProcessNode );

return( TRUE );
}


// ************************************************************************
// FUNCTION : HandleUnloadDllEvent( LPDEBUG_EVENT )
// PURPOSE : handle UNLOAD_DLL_DEBUG_EVENT
// COMMENTS : search process list, search DLL list, delete existing DLL
// node
// ************************************************************************
BOOL
HandleUnloadDllEvent( LPDEBUG_EVENT lpDebugEvent )
{
PNODE pProcessNode, pSearchProcessNode;
PNODE pSearchDllNode;
PDEB_PROCESS_NODE_INFO pProcessNodeInfo, pSearchProcessNodeInfo;
PDEB_DLL_NODE_INFO pSearchDllNodeInfo;

AllocProcessNode( &pSearchProcessNode, &pSearchProcessNodeInfo );
pSearchProcessNodeInfo->dwProcessId = lpDebugEvent->dwProcessId;
SetCurrentProcessNode( pProcessList, pSearchProcessNode );
GetCurrentNode( pProcessList, &pProcessNode );
pProcessNodeInfo = (PDEB_PROCESS_NODE_INFO) pProcessNode->pNodeData;
AllocDllNode( &pSearchDllNode, &pSearchDllNodeInfo );
pSearchDllNodeInfo->DllDebugInfo.lpBaseOfDll = lpDebugEvent->u.UnloadDll.lpBaseOfDll;
DeleteDllNode( pProcessNodeInfo->pDllList, pSearchDllNode );
DestroyDllNode( pSearchDllNode );
DestroyProcessNode( pSearchProcessNode );

return( TRUE );
}


// ************************************************************************
// FUNCTION : HandleOutputDebugStringEvent( LPDEBUG_EVENT )
// PURPOSE : handle OUTPUT_DEBUG_STRING_EVENT
// COMMENTS : do nothing
// ************************************************************************
BOOL
HandleOutputDebugStringEvent( LPDEBUG_EVENT lpDebugEvent )
{
return( TRUE );
}


// ************************************************************************
// FUNCTION : HandleRipEvent( LPDEBUG_EVENT )
// PURPOSE : handle RIP_EVENT
// COMMENTS : do nothing
// ************************************************************************
BOOL
HandleRipEvent( LPDEBUG_EVENT lpDebugEvent )
{
return( TRUE );
}


// ************************************************************************
// FUNCTION : HandleUnknownEvent( LPDEBUG_EVENT )
// PURPOSE : handle all unknown debug events
// COMMENTS : do nothing
// ************************************************************************
BOOL
HandleUnknownEvent( LPDEBUG_EVENT lpDebugEvent )
{
return( TRUE );
}


// ========================================================================
// misc debug event helper functions
// ========================================================================


// ************************************************************************
// FUNCTION : DebugNewProcess( LPTSTR, LPTSTR )
// PURPOSE : starts a new process as a debuggee
// COMMENTS :
// ************************************************************************
BOOL
DebugNewProcess( LPTSTR lpszFileName, LPTSTR lpszTitle )
{
static STARTUPINFO StartupInfo;
static LPSTARTUPINFO lpStartupInfo = &StartupInfo;
static PROCESS_INFORMATION ProcessInfo;
static LPPROCESS_INFORMATION lpProcessInfo = &ProcessInfo;

lpStartupInfo->cb = sizeof( STARTUPINFO );
lpStartupInfo->lpDesktop = NULL;
lpStartupInfo->lpTitle = lpszTitle;
lpStartupInfo->dwX = 0;
lpStartupInfo->dwY = 0;
lpStartupInfo->dwXSize = 0;
lpStartupInfo->dwYSize = 0;
lpStartupInfo->dwFlags = (DWORD) NULL;
lpStartupInfo->wShowWindow = SW_SHOWDEFAULT;

lpProcessInfo->hProcess = NULL;

//-- create the Debuggee process instead
if( !CreateProcess(
NULL,
lpszFileName,
(LPSECURITY_ATTRIBUTES) NULL,
(LPSECURITY_ATTRIBUTES) NULL,
TRUE,
Profile.DebugMode | Profile.DebuggeePriority | CREATE_NEW_CONSOLE,
(LPVOID) NULL,
(LPTSTR) NULL,
lpStartupInfo, lpProcessInfo ) ) {

switch( GetLastError() ) {

case ERROR_FILE_NOT_FOUND:
MessageBox( GetDesktopWindow(), TEXT( "This file does not exist." ),
TEXT( "Open File Error" ), MB_OK | MB_APPLMODAL | MB_SETFOREGROUND );
break;
case ERROR_ACCESS_DENIED:
MessageBox( GetDesktopWindow(), TEXT( "Access denied." ),
TEXT( "Open File Error" ), MB_OK | MB_APPLMODAL | MB_SETFOREGROUND );
break;
case ERROR_FILE_INVALID:
MessageBox( GetDesktopWindow(), TEXT( "Invalid file." ),
TEXT( "Open File Error" ), MB_OK | MB_APPLMODAL | MB_SETFOREGROUND );
break;
case ERROR_FILE_CORRUPT:
MessageBox( GetDesktopWindow(), TEXT( "The file is corrupt." ),
TEXT( "Open File Error" ), MB_OK | MB_APPLMODAL | MB_SETFOREGROUND );
break;
case ERROR_BAD_EXE_FORMAT:
MessageBox( GetDesktopWindow(), TEXT( "The file has a bad format." ),
TEXT( "Open File Error" ), MB_OK | MB_APPLMODAL | MB_SETFOREGROUND );
break;
default:
ErrorMessageBox( TEXT( "CreateProcess()" ),
Global.szApiFailed, szSourceFileName, __LINE__ );
break;

}
return( FALSE );

}
else {
CloseHandle( ProcessInfo.hProcess );
CloseHandle( ProcessInfo.hThread );
}

return( TRUE
);
}


// ************************************************************************
// FUNCTION : GetDllFileName( LPDEBUG_EVENT, LPTSTR, DWORD )
// PURPOSE : get DLL filename when LOAD_DLL_DEBUG_EVENT occurs
// COMMENTS : search process list, get DLL name from header
// ************************************************************************
BOOL
GetDllFileName( LPDEBUG_EVENT lpDebugEvent, LPTSTR lpszBuffer,
DWORD cchBuffer )
{
PNODE pProcessNode, pSearchProcessNode;
PDEB_PROCESS_NODE_INFO pProcessNodeInfo, pSearchProcessNodeInfo;

AllocProcessNode( &pSearchProcessNode, &pSearchProcessNodeInfo );
pSearchProcessNodeInfo->dwProcessId = lpDebugEvent->dwProcessId;
SetCurrentProcessNode( pProcessList, pSearchProcessNode );
GetCurrentNode( pProcessList, &pProcessNode );
pProcessNodeInfo = (PDEB_PROCESS_NODE_INFO) pProcessNode->pNodeData;
GetModuleFileNameFromHeader(
pProcessNodeInfo->ProcessDebugInfo.hProcess,
lpDebugEvent->u.LoadDll.hFile,
(DWORD) lpDebugEvent->u.LoadDll.lpBaseOfDll,
lpszBuffer, cchBuffer);
DestroyProcessNode( pSearchProcessNode );

return( TRUE );
}


// ************************************************************************
// FUNCTION : GetDllFileNameFromList( LPDEBUG_EVENT, LPTSTR, DWORD )
// PURPOSE : get DLL filename when UNLOAD_DLL_DEBUG_EVENT occurs
// COMMENTS : search process list, search DLL list, get DLL name
// ************************************************************************
BOOL
GetDllFileNameFromList( LPDEBUG_EVENT lpDebugEvent, LPTSTR lpszBuffer,
DWORD cchBuffer )
{
PNODE pProcessNode, pSearchProcessNode;
PNODE pDllNode, pSearchDllNode;
PDEB_PROCESS_NODE_INFO pProcessNodeInfo, pSearchProcessNodeInfo;
PDEB_DLL_NODE_INFO pDllNodeInfo, pSearchDllNodeInfo;

UNREFERENCED_PARAMETER( cchBuffer );

AllocProcessNode( &pSearchProcessNode, &pSearchProcessNodeInfo );
pSearchProcessNodeInfo->dwProcessId = lpDebugEvent->dwProcessId;
SetCurrentProcessNode( pProcessList, pSearchProcessNode );
GetCurrentNode( pProcessList, &pProcessNode );
pProcessNodeInfo = (PDEB_PROCESS_NODE_INFO) pProcessNode->pNodeData;
AllocDllNode( &pSearchDllNode, &pSearchDllNodeInfo );
pSearchDllNodeInfo->DllDebugInfo.lpBaseOfDll = lpDebugEvent->u.UnloadDll.lpBaseOfDll;
SetCurrentDllNode( pProcessNodeInfo->pDllList, pSearchDllNode );
GetCurrentNode( pProcessNodeInfo->pDllList, &pDllNode );
pDllNodeInfo = (PDEB_DLL_NODE_INFO) pDllNode->pNodeData;
lstrcpy( lpszBuffer, pDllNodeInfo->lpstrFileName );

return( TRUE );
}


// ************************************************************************
// FUNCTION : GetOutputDebugString( LPDEBUG_EVENT, LPTSTR, DWORD )
// PURPOSE : get the output debug string from the debuggee when
// OUTPUT_DEBUG_STRING_EVENT occurs
// COMMENTS : search process list, read the string from the debuggee
// ************************************************************************
BOOL
GetOutputDebugString( LPDEBUG_EVENT lpDebugEvent, LPTSTR lpszBuffer,
DWORD cchBuffer )
{
PNODE pProcessNode, pSearchProcessNode;
PDEB_PROCESS_NODE_INFO pProcessNodeInfo, pSearchProcessNodeInfo;
DWORD dwNumberOfBytesRead;

UNREFERENCED_PARAMETER( cchBuffer );

AllocProcessNode( &pSearchProcessNode, &pSearchProcessNodeInfo );
pSearchProcessNodeInfo->dwProcessId = lpDebugEvent->dwProcessId;
SetCurrentProcessNode( pProcessList, pSearchProcessNode );
GetCurrentNode( pProcessList, &pProcessNode );
pProcessNode = (PNODE) pProcessList->pCurrentNode;
pProcessNodeInfo = (PDEB_PROCESS_NODE_INFO) pProcessNode->pNodeData;
ReadProcessMemory(
pProcessNodeInfo->ProcessDebugInfo.hProcess,
lpDebugEvent->u.DebugString.lpDebugStringData,
lpszBuffer, lpDebugEvent->u.DebugString.nDebugStringLength,
&dwNumberOfBytesRead );
DestroyProcessNode( pSearchProcessNode );

return( TRUE );
}


// ************************************************************************
// FUNCTION : GetModuleFileNameFromHeader( HANDLE, HANDLE, DWORD, LPTSTR, DWORD )
// PURPOSE : Retrieves the DLL module name for a given file handle of a
// the module. Reads the module name from the EXE header.
// COMMENTS :
// Retrieves only the module name and not the pathname. Returns the
// number of characters copies to the buffer, else returns 0.
// ************************************************************************
DWORD
GetModuleFileNameFromHeader( HANDLE hProcess, HANDLE hFile, DWORD BaseOfDll,
LPTSTR lpszPath, DWORD cchPath )
{
#define IMAGE_SECOND_HEADER_OFFSET (15 * sizeof(ULONG)) // relative to file beginning
#define IMAGE_BASE_OFFSET (13 * sizeof(DWORD)) // relative to PE header base
#define IMAGE_EXPORT_TABLE_RVA_OFFSET (30 * sizeof(DWORD)) // relative to PE header base
#define IMAGE_NAME_RVA_OFFSET offsetof(IMAGE_EXPORT_DIRECTORY, Name)

WORD DosSignature;
DWORD NtSignature;
DWORD dwNumberOfBytesRead = 0;
DWORD PeHeader, ImageBase, ExportTableRVA, NameRVA;

//-- verify that the handle is not NULL
if( !hFile ) {
lstrcpy( lpszPath, TEXT("Invalid File Handle") );
return( 0 );
}

//-- verify that the handle is for a disk file
if( GetFileType(hFile) != FILE_TYPE_DISK ) {
lstrcpy( lpszPath, TEXT("Invalid File Type") );
return( 0 );
}

//-- Extract the filename from the EXE header
SetFilePointer( hFile, 0L, NULL, FILE_BEGIN );
ReadFile( hFile, &DosSignature, sizeof(DosSignature), &dwNumberOfBytesRead,
(LPOVERLAPPED) NULL);

//-- verify DOS signature found
if( DosSignature != IMAGE_DOS_SIGNATURE ) {
wsprintf( lpszPath, TEXT( "Bad MZ Signature: 0x%x" ), DosSignature );
return( 0 );
}

SetFilePointer( hFile, IMAGE_SECOND_HEADER_OFFSET, (LPLONG) NULL,
FILE_BEGIN );
ReadFile( hFile, &PeHeader, sizeof(PeHeader), &dwNumberOfBytesRead,
(LPOVERLAPPED) NULL );
SetFilePointer( hFile, PeHeader, (LPLONG) NULL, FILE_BEGIN );
ReadFile( hFile, &NtSignature, sizeof(NtSignature), &dwNumberOfBytesRead,
(LPOVERLAPPED) NULL);

//-- verify Windows NT (PE) signature found
if( NtSignature != IMAGE_NT_SIGNATURE ) {
wsprintf( lpszPath, TEXT( "Bad PE Signature: 0x%x" ), DosSignature );
return( 0 );
}

SetFilePointer( hFile, PeHeader + IMAGE_BASE_OFFSET, (LPLONG) NULL,
FILE_BEGIN );
ReadFile( hFile, &ImageBase, sizeof(ImageBase), &dwNumberOfBytesRead,
(LPOVERLAPPED) NULL);
SetFilePointer( hFile, PeHeader + IMAGE_EXPORT_TABLE_RVA_OFFSET,
(LPLONG) NULL, FILE_BEGIN );
ReadFile( hFile, &ExportTableRVA, sizeof(ExportTableRVA),
&dwNumberOfBytesRead, (LPOVERLAPPED) NULL);

//-- now read from the virtual address space in the process
ReadProcessMemory( hProcess,
(LPVOID) (BaseOfDll + ExportTableRVA + IMAGE_NAME_RVA_OFFSET),
&NameRVA, sizeof(NameRVA), &dwNumberOfBytesRead );
lstrcpy( lpszPath, TEXT("Empty!") );
if( !ReadProcessMemory( hProcess,
(LPVOID) (BaseOfDll + NameRVA),
lpszPath, cchPath, &dwNumberOfBytesRead ) )
lstrcpy( lpszPath, TEXT("Access Denied!") );

return( dwNumberOfBytesRead );
}


#if defined(_MIPS_) || defined(_ALPHA_) || defined(_PPC_)
// ************************************************************************
// FUNCTION : SkipThreadBreakPoint( HANDLE );
// PURPOSE : Skip over the break point instruction belonging to
// hThread.
// COMMENTS :
// Only the MIPS R4x00 and DEC Alpha AXP require this.
// ************************************************************************
BOOL
SkipBreakPoint( HANDLE hThread )
{
static CONTEXT Context;

Context.ContextFlags = CONTEXT_CONTROL;
if( !GetThreadContext( hThread, &Context ) )
return( FALSE );
#if defined(_PPC_)
Context.Iar += 4L; // Iar is the PC (program counter)
#else
Context.Fir += 4L; // Fir is the PC (program counter)
// BREAK (breakpoint instruction) occupies 4 bytes
#endif

// -----------------------------------------------------------------------
// Below would be equivalent for the Intel 80x86 (if it were necessary)
// however the Intel x86 automatically increments ip past the 'int 3':
// Context.Eip += 1L; // Eip is the PC (program counter)
// // int 3 (breakpoint instruction) occupies 1 byte
// -----------------------------------------------------------------------

if( !SetThreadContext( hThread, &Context ) )
return( FALSE );

return( TRUE );
}
#endif



// ========================================================================
// wrapper functions to the linked list services
// ========================================================================

// ========================================================================
// Debug Event Browser Data Structure Overview
// -------------------------------------------
//
// The Debug Event Browser (DEB) maintains a rather involved data structure
// to store various debug event and debuggee process information. It
// attempts to encapsulate the intricacies of what makes a process based on
// the occuring events. Much of this stored information is never utilized
// by the Debug Event Browser but it is included to demonstrate what types
// of debug event may be useful to a full blown debugger application.
//
// This data structure uses the generalized, sorted, double-linked list
// package provided with the sample. Each list can store list-specific
// instance data, list-specific node data, and maintain various pointers
// to these nodes. The list is sorted via the insertion sort method where
// the programmer defines the list-specific sort function whose purpose is
// to compare two given nodes and return their relative sort location. The
// list package in generalized in the sense that the list and node-specific
// data type is not known to this package at compile time or at runtime.
// The application programmer is merely responsible for defining the list
// and node-specific data structures and the sorting and optional searching
// functions and the list package keeps track of these nodes and provides
// easy access to them.
//
// DEB uses this list package to create three unique list types: process,
// thread and DLL lists.
//
// The backbone of the data structure is the process list. The nodes of
// the process list are the individual debuggee processes. DEB allows
// debugging (or should I say debug event browsing) of other processes that
// are spawned by the initial debuggee. Thus each debug session may have
// multiple debuggees and thus the process becomes the logical node unit.
//
// A visual diagram of the process list is as follows:
//
// (ProcessList)
// |
// |
// v
// +-----------------+ +----------------------+
// | -ProcessList- | | -ListData- |
// | | | |
// | ListData--------+---------->| ActiveProcessCount=N |
// +-----+-FirstNode | +----------------------+
// | +-+ CurrentNode |
// | | | LastNode--------+-----------------------+
// | | | OrderFunction=& | |
// | | | ListError=0 | |
// | | +-----------------+ |
// | | |
// | +-------------------+ |
// | | |
// v v v
// +------------+ +------------+ +------------+
// NULL <- |ProcessNode1| <=> |ProcessNode2| <=> ... <=> |ProcessNodeN| -> NULL
// +------------+ +------------+ +------------+
//
// Each process node also contains two lists: the thread list and the DLL list.
// This node also stores some of the relevent debug event information
// particular to the create process event. Visually it is as follows:
//
// +------------------+ +--------------+
// | -ProcessNode- | +------>| -ThreadList- |
// | | | +--------------+
// | ProcessID=0 | |
// | ThreadID=0 | | +-----------+
// | FileName="" | | +-->| -DllList- |
// | PathName="" | | | +-----------+
// | ThreadList-------+-+ |
// | DllList----------+-----+ +-------------------+
// | ProcessDebugInfo-+-------->|-ProcessDebugInfo- |
// +------------------+ +-------------------+
//
// Much like the process list, the visual diagram of the thread list is as
// follows:
//
// +-----------------+ +---------------------+
// | -ThreadList- | | -ListData- |
// | | | |
// | ListData--------+---------->| ActiveThreadCount=N |
// +-----+-FirstNode | +---------------------+
// | +-+ CurrentNode |
// | | | LastNode--------+-----------------------+
// | | | OrderFunction=& | |
// | | | ListError=0 | |
// | | +-----------------+ |
// | | |
// | +-------------------+ |
// | | |
// v v v
// +-----------+ +-----------+ +-----------+
// NULL <- |ThreadNode1| <=> |ThreadNode2| <=> ... <=> |ThreadNodeN| -> NULL
// +-----------+ +-----------+ +-----------+
//
// The thread nodes store some of the relevent debug event information
// particular to the create thread event. Visually it is as follows:
//
// +--------------------+
// | -ThreadNode- |
// | |
// | ProcessID=0 |
// | ThreadID=0 |
// | ThreadDebugInfo={} |
// +--------------------+
//
// Much like the process and thread lists, the visual diagram of the Dll list
// is as follows:
//
// +-----------------+ +------------------+
// | -DllList- | | -ListData- |
// | | | |
// | ListData--------+---------->| ActiveDllCount=N |
// +-----+-FirstNode | +------------------+
// | +-+ CurrentNode |
// | | | LastNode--------+-----------------+
// | | | OrderFunction=& | |
// | | | ListError=0 | |
// | | +-----------------+ |
// | | |
// | +-------------+ |
// | | |
// v v v
// +--------+ +--------+ +--------+
// NULL <- |DllNode1| <=> |DllNode2| <=> ... <=> |DllNodeN| -> NULL
// +--------+ +--------+ +--------+
//
// The Dll nodes store some of the relevent debug event information particular
// to the Dll load event. Visually it is as follows:
//
// +----------------+
// | -DllNode- |
// | |
// | FileName="" |
// | PathName="" |
// | DllDebugInfo={}|
// +----------------+
//
// ========================================================================

// ------------------------------------------------------------------------
// Process list and node specific linked list wrapper functions
// ------------------------------------------------------------------------


// ************************************************************************
// FUNCTION : ProcessOrderFunction( PNODE, PNODE );
// PURPOSE : Provides the sorting/search logic for the double linked
// list package.
// COMMENTS :
// Sorted by process ID value
// ************************************************************************
int
ProcessOrderFunction( PNODE pNode1, PNODE pNode2 )
{
PDEB_PROCESS_NODE_INFO pProcessNodeInfo1 = pNode1->pNodeData;
PDEB_PROCESS_NODE_INFO pProcessNodeInfo2 = pNode2->pNodeData;

if( pProcessNodeInfo1->dwProcessId < pProcessNodeInfo2->dwProcessId )
return( LIST_LEFT_OF );

if( pProcessNodeInfo1->dwProcessId > pProcessNodeInfo2->dwProcessId )
return( LIST_RIGHT_OF );

return( LIST_MATCH );
}


// ************************************************************************
// FUNCTION : CreateProcessList( PLIST* )
// PURPOSE :
// COMMENTS :
//
// ************************************************************************
BOOL
CreateProcessList( PLIST* ppProcessList )
{
PDEB_PROCESS_LIST_INFO pProcessListInfo;

//-- create list
CreateList( ppProcessList, ProcessOrderFunction );

//-- alloc info data
pProcessListInfo = (PDEB_PROCESS_LIST_INFO) HeapAlloc( hHeap, (DWORD) NULL,
sizeof( DEB_PROCESS_LIST_INFO ) );
(*ppProcessList)->pListData = pProcessListInfo;

//-- init info data
pProcessListInfo->dwActiveProcesses = 0;

return( TRUE );
}


// ************************************************************************
// FUNCTION : DestroyProcessList( PLIST )
// PURPOSE :
// COMMENTS :
//
// ************************************************************************
BOOL
DestroyProcessList( PLIST pProcessList )
{
PDEB_PROCESS_LIST_INFO pProcessListInfo = pProcessList->pListData;
PNODE pDeleteNode;

//-- make sure all nodes are removed first
while( pProcessListInfo->dwActiveProcesses ) {
GetCurrentNode( pProcessList, &pDeleteNode );
DeleteCurrentProcessNode( pProcessList );
}

//-- free info data
HeapFree( hHeap, (DWORD) NULL, (PVOID) pProcessListInfo );

//-- destroy list
DestroyList( pProcessList );

return( TRUE );
}


// ************************************************************************
// FUNCTION : AllocProcessNode( PNODE*, PDEB_PROCESS_NODE_INFO* )
// PURPOSE :
// COMMENTS :
//
// ************************************************************************
BOOL
AllocProcessNode( PNODE* ppProcessNode, PDEB_PROCESS_NODE_INFO* ppProcessNodeInfo )
{
//-- create node
CreateNode( ppProcessNode );

//-- alloc info data
*ppProcessNodeInfo = (PDEB_PROCESS_NODE_INFO) HeapAlloc( hHeap, (DWORD) NULL,
sizeof( DEB_PROCESS_NODE_INFO ) );
(*ppProcessNode)->pNodeData = *(ppProcessNodeInfo);

(*ppProcessNodeInfo)->lpstrFileName = (LPTSTR) HeapAlloc( hHeap, (DWORD) NULL,
(DWORD) MAX_PATH );
(*ppProcessNodeInfo)->lpstrPathName = (LPTSTR) HeapAlloc( hHeap, (DWORD) NULL,
(DWORD) MAX_PATH );
CreateThreadList( &((*ppProcessNodeInfo)->pThreadList) );
CreateDllList( &((*ppProcessNodeInfo)->pDllList) );

return( TRUE );
}


// ************************************************************************
// FUNCTION : InitProcessNodeInfo( PDEB_PROCESS_NODE_INFO*, LPDEBUG_EVENT )
// PURPOSE :
// COMMENTS :
//
// ************************************************************************
BOOL
InitProcessNodeInfo( PDEB_PROCESS_NODE_INFO* ppProcessNodeInfo, LPDEBUG_EVENT lpDebugEvent )
{
//-- init info data
(*ppProcessNodeInfo)->dwProcessId = lpDebugEvent->dwProcessId;
(*ppProcessNodeInfo)->dwThreadId = lpDebugEvent->dwThreadId;
// Note:pThreadList initialized via previous CreateThreadList() call
// Note:pDllList initialized via previous CreateDllList() call
(*ppProcessNodeInfo)->ProcessDebugInfo = lpDebugEvent->u.CreateProcessInfo;

return( TRUE );
}


// ************************************************************************
// FUNCTION : InsertProcessNode( PLIST, PNODE )
// PURPOSE :
// COMMENTS :
//
// ************************************************************************
BOOL
InsertProcessNode( PLIST pProcessList, PNODE pProcessNode )
{
PDEB_PROCESS_LIST_INFO pProcessListInfo = pProcessList->pListData;

//-- insert the node
InsertNode( pProcessList, pProcessNode );

//-- increment dwActiveProcesss
pProcessListInfo->dwActiveProcesses++;

return( TRUE );
}


// ************************************************************************
// FUNCTION : SetCurrentProcessNode( PLIST, PNODE )
// PURPOSE :
// COMMENTS :
//
// ************************************************************************
BOOL
SetCurrentProcessNode( PLIST pProcessList, PNODE pProcessNode )
{
SetCurrentNode( pProcessList, pProcessNode, pProcessList->OrderFunction );

return( TRUE );
}


// ************************************************************************
// FUNCTION : DeleteProcessNode( PLIST, PNODE )
// PURPOSE :
// COMMENTS :
//
// ************************************************************************
BOOL
DeleteProcessNode( PLIST pProcessList, PNODE pProcessNode )
{
PNODE pDeleteNode;

SetCurrentNode( pProcessList, pProcessNode, pProcessList->OrderFunction );
GetCurrentNode( pProcessList, &pDeleteNode );
DeleteCurrentProcessNode( pProcessList );

return( TRUE );
}


// ************************************************************************
// FUNCTION : FreeProcessNodeInfo( PNODE )
// PURPOSE :
// COMMENTS :
//
// ************************************************************************
BOOL
FreeProcessNodeInfo( PNODE pProcessNode )
{
PDEB_PROCESS_NODE_INFO pProcessNodeInfo = (PDEB_PROCESS_NODE_INFO) pProcessNode->pNodeData;

//-- free info data
DestroyDllList( pProcessNodeInfo->pDllList );
DestroyThreadList( pProcessNodeInfo->pThreadList );
HeapFree( hHeap, (DWORD) NULL, (PVOID) pProcessNodeInfo->lpstrPathName );
HeapFree( hHeap, (DWORD) NULL, (PVOID) pProcessNodeInfo->lpstrFileName );
HeapFree( hHeap, (DWORD) NULL, (PVOID) pProcessNodeInfo );

return( TRUE );
}


// ************************************************************************
// FUNCTION : DestroyProcessNode( PNODE )
// PURPOSE :
// COMMENTS : Frees all memory associated with the node.
// ************************************************************************
BOOL
DestroyProcessNode( PNODE pProcessNode )
{
//-- free info data
FreeProcessNodeInfo( pProcessNode );

//-- destroy node
DestroyNode( pProcessNode );

return( TRUE );
}


// ************************************************************************
// FUNCTION : DeleteCurrentProcessNode( PLIST )
// PURPOSE :
// COMMENTS :
//
// ************************************************************************
BOOL
DeleteCurrentProcessNode( PLIST pProcessList )
{
PDEB_PROCESS_LIST_INFO pProcessListInfo = pProcessList->pListData;
PNODE pProcessNode = (PNODE) pProcessList->pCurrentNode;

//-- free info data
FreeProcessNodeInfo( pProcessNode );


//-- delete and destroy node
DeleteCurrentNode( pProcessList );

//-- decrement dwActiveProcesss
pProcessListInfo->dwActiveProcesses--;

return( TRUE );
}


// ------------------------------------------------------------------------
// Thread list and node specific linked list wrapper functions
// ------------------------------------------------------------------------


// ************************************************************************
// FUNCTION : ThreadOrderFunction( PNODE, PNODE );
// PURPOSE : Provides the sorting/search logic for the double linked
// list package.
// COMMENTS :
// Sorted by thread ID value
// ************************************************************************
int
ThreadOrderFunction( PNODE pNode1, PNODE pNode2 )
{
PDEB_THREAD_NODE_INFO pThreadNodeInfo1 = pNode1->pNodeData;
PDEB_THREAD_NODE_INFO pThreadNodeInfo2 = pNode2->pNodeData;

if( pThreadNodeInfo1->dwThreadId < pThreadNodeInfo2->dwThreadId )
return( LIST_LEFT_OF );

if( pThreadNodeInfo1->dwThreadId > pThreadNodeInfo2->dwThreadId )
return( LIST_RIGHT_OF );

return( LIST_MATCH );
}


// ************************************************************************
// FUNCTION : CreateThreadList( PLIST* )
// PURPOSE :
// COMMENTS :
//
// ************************************************************************
BOOL
CreateThreadList( PLIST* ppThreadList )
{
PDEB_THREAD_LIST_INFO pThreadListInfo;

//-- create list
CreateList( ppThreadList, ThreadOrderFunction );

//-- alloc info data
pThreadListInfo = (PDEB_THREAD_LIST_INFO) HeapAlloc( hHeap, (DWORD) NULL,
sizeof( DEB_THREAD_LIST_INFO ) );
(*ppThreadList)->pListData = pThreadListInfo;

//-- init info data
pThreadListInfo->dwActiveThreads = 0;

return( TRUE );
}


// ************************************************************************
// FUNCTION : DestroyThreadList( PLIST )
// PURPOSE :
// COMMENTS :
//
// ************************************************************************
BOOL
DestroyThreadList( PLIST pThreadList )
{
PDEB_THREAD_LIST_INFO pThreadListInfo = pThreadList->pListData;
PNODE pDeleteNode;

//-- make sure all nodes are removed first
while( pThreadListInfo->dwActiveThreads ) {
GetCurrentNode( pThreadList, &pDeleteNode );
DeleteCurrentThreadNode( pThreadList );
}

//-- free info data
HeapFree( hHeap, (DWORD) NULL, (PVOID) pThreadListInfo );

//-- destroy list
DestroyList( pThreadList );

return( TRUE );
}


// ************************************************************************
// FUNCTION : AllocThreadNode( PNODE*, PDEB_THREAD_NODE_INFO* )
// PURPOSE :
// COMMENTS :
//
// ************************************************************************
BOOL
AllocThreadNode( PNODE* ppThreadNode, PDEB_THREAD_NODE_INFO* ppThreadNodeInfo )
{
//-- create node
CreateNode( ppThreadNode );

//-- alloc info data
*ppThreadNodeInfo = (PDEB_THREAD_NODE_INFO) HeapAlloc( hHeap, (DWORD) NULL,
(DWORD) sizeof( DEB_THREAD_NODE_INFO ) );
(*ppThreadNode)->pNodeData = *(ppThreadNodeInfo);

return( TRUE );
}


// ************************************************************************
// FUNCTION : InitThreadNodeInfo( PDEB_THREAD_NODE_INFO*, LPDEBUG_EVENT )
// PURPOSE :
// COMMENTS :
//
// ************************************************************************
BOOL
InitThreadNodeInfo( PDEB_THREAD_NODE_INFO* ppThreadNodeInfo,
LPDEBUG_EVENT lpDebugEvent )
{
//-- init info data
(*ppThreadNodeInfo)->dwProcessId = lpDebugEvent->dwProcessId;
(*ppThreadNodeInfo)->dwThreadId = lpDebugEvent->dwThreadId;
(*ppThreadNodeInfo)->ThreadDebugInfo = lpDebugEvent->u.CreateThread;

return( TRUE );
}


// ************************************************************************
// FUNCTION : InsertThreadNode( PLIST, PNODE )
// PURPOSE :
// COMMENTS :
//
// ************************************************************************
BOOL
InsertThreadNode( PLIST pThreadList, PNODE pThreadNode )
{
PDEB_THREAD_LIST_INFO pThreadListInfo = (PDEB_THREAD_LIST_INFO) pThreadList->pListData;

//-- insert the thread node
InsertNode( pThreadList, pThreadNode );

//-- increment dwActiveThreads
pThreadListInfo->dwActiveThreads++;

return( TRUE );
}


// ************************************************************************
// FUNCTION : SetCurrentThreadNode( PLIST, PNODE )
// PURPOSE :
// COMMENTS :
//
// ************************************************************************
BOOL
SetCurrentThreadNode( PLIST pThreadList, PNODE pThreadNode )
{
SetCurrentNode( pThreadList, pThreadNode, pThreadList->OrderFunction );

return( TRUE );
}


// ************************************************************************
// FUNCTION : DeleteThreadNode( PLIST, PNODE )
// PURPOSE :
// COMMENTS :
//
// ************************************************************************
BOOL
DeleteThreadNode( PLIST pThreadList, PNODE pThreadNode )
{
PNODE pDeleteNode;

SetCurrentNode( pThreadList, pThreadNode, pThreadList->OrderFunction );
GetCurrentNode( pThreadList, &pDeleteNode );
DeleteCurrentThreadNode( pThreadList );

return( TRUE );
}


// ************************************************************************
// FUNCTION : FreeThreadNodeInfo( PNODE )
// PURPOSE :
// COMMENTS :
//
// ************************************************************************
BOOL
FreeThreadNodeInfo( PNODE pThreadNode )
{
PDEB_THREAD_NODE_INFO pThreadNodeInfo = (PDEB_THREAD_NODE_INFO) pThreadNode->pNodeData;

//-- free info data
HeapFree( hHeap, (DWORD) NULL, (PVOID) pThreadNodeInfo );

return( TRUE );
}


// ************************************************************************
// FUNCTION : DestroyThreadNode( PNODE )
// PURPOSE :
// COMMENTS : Frees all memory associated with the node.
// ************************************************************************
BOOL
DestroyThreadNode( PNODE pThreadNode )
{
//-- free info data
FreeThreadNodeInfo( pThreadNode );

//-- destroy node
DestroyNode( pThreadNode );

return( TRUE );
}


// ************************************************************************
// FUNCTION : DeleteCurrentThreadNode( PLIST )
// PURPOSE :
// COMMENTS :
//
// ************************************************************************
BOOL
DeleteCurrentThreadNode( PLIST pThreadList )
{
PDEB_THREAD_LIST_INFO pThreadListInfo = pThreadList->pListData;
PNODE pThreadNode = (PNODE) pThreadList->pCurrentNode;

//-- free info data
FreeThreadNodeInfo( pThreadNode );

//-- delete and destroy node
DeleteCurrentNode( pThreadList );

//-- decrement dwActiveThreads
pThreadListInfo->dwActiveThreads--;

return( TRUE );
}


// ------------------------------------------------------------------------
// DLL list and node specific linked list wrapper functions
// ------------------------------------------------------------------------


// ************************************************************************
// FUNCTION : DllOrderFunction( PNODE, PNODE );
// PURPOSE : Provides the sorting/search logic for the double linked
// list package.
// COMMENTS :
// Sorted by base address of the DLL
// ************************************************************************
int
DllOrderFunction( PNODE pNode1, PNODE pNode2 )
{
PDEB_DLL_NODE_INFO pDllNodeInfo1 = pNode1->pNodeData;
PDEB_DLL_NODE_INFO pDllNodeInfo2 = pNode2->pNodeData;

if( pDllNodeInfo1->DllDebugInfo.lpBaseOfDll < pDllNodeInfo2->DllDebugInfo.lpBaseOfDll )
return( LIST_LEFT_OF );

if( pDllNodeInfo1->DllDebugInfo.lpBaseOfDll > pDllNodeInfo2->DllDebugInfo.lpBaseOfDll )
return( LIST_RIGHT_OF );

return( LIST_MATCH );
}


// ************************************************************************
// FUNCTION : CreateDllList( PLIST* )
// PURPOSE :
// COMMENTS :
//
// ************************************************************************
BOOL
CreateDllList( PLIST* ppDllList )
{
PDEB_DLL_LIST_INFO pDllListInfo;

//-- create list
CreateList( ppDllList, DllOrderFunction );

//-- alloc info data
pDllListInfo = (PDEB_DLL_LIST_INFO) HeapAlloc( hHeap, (DWORD) NULL,
sizeof( DEB_DLL_LIST_INFO ) );
(*ppDllList)->pListData = pDllListInfo;

//-- init info data
pDllListInfo->dwActiveDlls = 0;

return( TRUE );
}


// ************************************************************************
// FUNCTION : DestroyDllList( PLIST )
// PURPOSE :
// COMMENTS :
//
// ************************************************************************
BOOL
DestroyDllList( PLIST pDllList )
{
PDEB_DLL_LIST_INFO pDllListInfo = pDllList->pListData;
PNODE pDeleteNode;

//-- make sure all nodes are removed first
while( pDllListInfo->dwActiveDlls ) {
GetCurrentNode( pDllList, &pDeleteNode );
DeleteCurrentDllNode( pDllList );
}

//-- free list data and destroy the list
HeapFree( hHeap, (DWORD) NULL, (PVOID) pDllListInfo );
DestroyList( pDllList );

return( TRUE );
}


// ************************************************************************
// FUNCTION : AllocDllNode( PNODE*, PDEB_DLL_NODE_INFO* )
// PURPOSE :
// COMMENTS :
//
// ************************************************************************
BOOL
AllocDllNode( PNODE* ppDllNode, PDEB_DLL_NODE_INFO* ppDllNodeInfo )
{
//-- create node
CreateNode( ppDllNode );

//-- alloc info data
*ppDllNodeInfo = (PDEB_DLL_NODE_INFO) HeapAlloc( hHeap, (DWORD) NULL,
sizeof( DEB_DLL_NODE_INFO ) );
(*ppDllNode)->pNodeData = *(ppDllNodeInfo);
(*ppDllNodeInfo)->lpstrFileName = (LPTSTR) HeapAlloc( hHeap, (DWORD) NULL, (DWORD) MAX_PATH );

return( TRUE );
}


// ************************************************************************
// FUNCTION : InitDllNodeInfo( PDEB_DLL_NODE_INFO*, LPDEBUG_EVENT )
// PURPOSE :
// COMMENTS :
//
// ************************************************************************
BOOL
InitDllNodeInfo( PDEB_DLL_NODE_INFO* ppDllNodeInfo,
LPDEBUG_EVENT lpDebugEvent )
{
//-- init info data
GetDllFileName( lpDebugEvent, (*ppDllNodeInfo)->lpstrFileName, MAX_PATH );
(*ppDllNodeInfo)->DllDebugInfo = lpDebugEvent->u.LoadDll;

return( TRUE );
}


// ************************************************************************
// FUNCTION : InsertDllNode( PLIST, PNODE )
// PURPOSE :
// COMMENTS :
//
// ************************************************************************
BOOL
InsertDllNode( PLIST pDllList, PNODE pDllNode )
{
PDEB_DLL_LIST_INFO pDllListInfo = pDllList->pListData;

// insert the node
InsertNode( pDllList, pDllNode );

//-- increment dwActiveDlls
pDllListInfo->dwActiveDlls++;

return( TRUE );
}


// ************************************************************************
// FUNCTION : SetCurrentDllNode( PLIST, PNODE )
// PURPOSE :
// COMMENTS :
//
// ************************************************************************
BOOL
SetCurrentDllNode( PLIST pDllList, PNODE pDllNode )
{
SetCurrentNode( pDllList, pDllNode, pDllList->OrderFunction );

return( TRUE );
}


// ************************************************************************
// FUNCTION : DeleteDllNode( PLIST, PNODE )
// PURPOSE :
// COMMENTS :
//
// ************************************************************************
BOOL
DeleteDllNode( PLIST pDllList, PNODE pDllNode )
{
PNODE pDeleteNode;

SetCurrentNode( pDllList, pDllNode, pDllList->OrderFunction );
GetCurrentNode( pDllList, &pDeleteNode );
DeleteCurrentDllNode( pDllList );

return( TRUE );
}


// ************************************************************************
// FUNCTION : FreeDllNodeInfo( PNODE )
// PURPOSE :
// COMMENTS :
//
// ************************************************************************
BOOL
FreeDllNodeInfo( PNODE pDllNode )
{
PDEB_DLL_NODE_INFO pDllNodeInfo = (PDEB_DLL_NODE_INFO) pDllNode->pNodeData;

//-- free info data
HeapFree( hHeap, (DWORD) NULL, (PVOID) pDllNodeInfo->lpstrFileName );
HeapFree( hHeap, (DWORD) NULL, (PVOID) pDllNodeInfo );

return( TRUE );
}


// ************************************************************************
// FUNCTION : DestroyDllNode( PNODE )
// PURPOSE :
// COMMENTS : Frees all memory associated with the node.
// ************************************************************************
BOOL
DestroyDllNode( PNODE pDllNode )
{
//-- free info data
FreeDllNodeInfo( pDllNode );

//-- destroy node
DestroyNode( pDllNode );

return( TRUE );
}


// ************************************************************************
// FUNCTION : DeleteCurrentDllNode( PLIST )
// PURPOSE :
// COMMENTS : Deletes the current DLL node from the list and frees all
// memory associated with it.
// ************************************************************************
BOOL
DeleteCurrentDllNode( PLIST pDllList )
{
PDEB_DLL_LIST_INFO pDllListInfo = pDllList->pListData;
PNODE pDllNode = (PNODE) pDllList->pCurrentNode;

//-- free info data
FreeDllNodeInfo( pDllNode );

//-- delete and destroy node
DeleteCurrentNode( pDllList );

//-- decrement dwActiveDlls
pDllListInfo->dwActiveDlls--;

return( TRUE );
}