
/******************************************************************************\
*       This is a part of the Microsoft Source Code Samples. 
*       Copyright (C) 1993 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( _MIPS_ == 1 || _ALPHA_ == 1 )
 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( _MIPS_ == 1 || _ALPHA_ == 1 )
  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( _MIPS_ == 1 || _ALPHA_ == 1 )
// ************************************************************************
// 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 );
  Context.Fir += 4L;  // Fir is the PC (program counter)
                      // BREAK (breakpoint instruction) occupies 4 bytes

  // -----------------------------------------------------------------------
  //  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 );
}
