#include "precomp.h"
#pragma hdrstop

#ifdef WIN32S
extern BOOL fProcessingDebugEvent;
extern BOOL fWaitForDebugEvent;
extern DWORD tidExit;
extern BOOL  fExitProcessDebugEvent; // set true when the last debug event
#endif

HANDLE hEventContinue;
extern LPDM_MSG     LpDmMsg;
extern SYSTEM_INFO SystemInfo;
extern WT_STRUCT             WtStruct;          // ..  for wt
extern DEBUG_ACTIVE_STRUCT   DebugActiveStruct; // ... for DebugActiveProcess()

extern PKILLSTRUCT           KillQueue;
extern CRITICAL_SECTION      csKillQueue;

extern HTHDX        thdList;
extern HPRCX        prcList;
extern CRITICAL_SECTION csThreadProcList;

extern BOOL    fSmartRangeStep;
extern HANDLE hEventNoDebuggee;
extern HANDLE hEventRemoteQuit;
extern BOOL         fDisconnected;
extern BOOL         fUseRoot;
extern char       nameBuffer[];

#if defined(TARGET_MIPS)
MIPSCONTEXTSIZE MipsContextSize;
#endif



BOOL
DbgWriteMemory(
    HPRCX       hprc,
    LPVOID      lpOffset,
    LPBYTE      lpb,
    DWORD       cb,
    LPDWORD     pcbWritten
    )
/*++

Routine Description:

    Write to a flat address in a process.

Arguments:

    hprc - Supplies the handle to the process

    lpOffset - Supplies address of data in process

    lpb    - Supplies a pointer to the bytes to be written

    cb     - Supplies the count of bytes to be written

    pcbWritten - Returns the number of bytes actually written

Return Value:

    TRUE if successful and FALSE otherwise

--*/

{
    BOOL        fRet;

    assert(hprc->rwHand != (HANDLE)-1);

    if (hprc->rwHand == (HANDLE)-1) {
        return FALSE;
    }

    fRet = WriteProcessMemory(hprc->rwHand, lpOffset, lpb, cb, pcbWritten);

#if  defined(JLS_RWBP) && DBG
    {
        DWORD   cbT;
        LPBYTE  lpbT = malloc(cb);

        assert( fRet );
        assert( *pcbWritten == cb );
        fRet = ReadProcessMemory(hprc->rwHand, lpOffset, lpbT, cb, &cbT);
        assert(fRet);
        assert( cb == cbT);
        assert(memcmp(lpbT, lpb) == 0);
        free lpbT;
    }
#endif

    return fRet;
}


BOOL
DbgReadMemory(
    HPRCX   hprc,
    LPVOID  lpOffset,
    LPVOID  lpb,
    DWORD   cb,
    LPDWORD lpRead
    )
/*++

Routine Description:

    Read from a flat address in a process.

Arguments:

    hprc - Supplies the handle to the process

    lpOffset - Supplies address of data in process

    lpb    - Supplies a pointer to the local buffer

    cb     - Supplies the count of bytes to read

    lpRead - Returns the number of bytes actually read

Return Value:

    TRUE if successful and FALSE otherwise

--*/

{
#ifndef WIN32S
    DWORD cbr;
    if (CrashDump) {
        cbr = DmpReadMemory( (PVOID)lpOffset, lpb, cb );
        if (lpRead) {
            *lpRead = cbr;
        }
        return (cbr > 0) || (cbr == cb);
    }
#endif

    assert(hprc->rwHand != (HANDLE)-1);

    if (hprc->rwHand == (HANDLE)-1) {
        return FALSE;
    }

    if (ReadProcessMemory(hprc->rwHand, lpOffset, lpb, cb, lpRead)) {

        return TRUE;

    } else {

#if DBG
        int e = GetLastError();
#endif
        //
        //  Reads across page boundaries will not work if the
        //  second page is not accessible.
        //
#define PAGE_SIZE (SystemInfo.dwPageSize)
#define PAGE_MASK (~(PAGE_SIZE-1))

        DWORD firstsize;
        DWORD dwRead;

        firstsize = (((DWORD)lpOffset + PAGE_SIZE) & PAGE_MASK) - (DWORD)lpOffset;
        if (cb < firstsize) {
            firstsize = cb;
        }

        //
        // read from the first page.  If the first read fails,
        // fail the whole thing.
        //

        if (!ReadProcessMemory(hprc->rwHand, lpOffset, lpb, firstsize,
                                                                     lpRead)) {
            return FALSE;
        }

        //
        // read intermediate complete pages.
        // if any of these reads fail, succeed with a short read.
        //

        assert(*lpRead == firstsize);
        cb -= firstsize;
        lpb = (LPVOID)((LPBYTE)lpb + firstsize);
        lpOffset = (LPVOID)((LPBYTE)lpOffset + firstsize);

        while (cb >= PAGE_SIZE) {

            if (!ReadProcessMemory(hprc->rwHand, lpOffset, lpb, PAGE_SIZE,
                                                                 &dwRead)) {
                return TRUE;
            } else {
                assert(dwRead == PAGE_SIZE);
                lpb = (LPVOID)((LPBYTE)lpb + PAGE_SIZE);
                lpOffset = (LPVOID)((LPBYTE)lpOffset + PAGE_SIZE);
                *lpRead += dwRead;
                cb -= PAGE_SIZE;
            }
        }

        if (cb > 0) {
            if (ReadProcessMemory(hprc->rwHand, lpOffset, lpb, cb, &dwRead)) {
                assert(dwRead == cb);
                *lpRead += dwRead;
            }
        }

        return TRUE;
    }

}

BOOL
DbgGetThreadContext(
    HTHDX hthd,
    PCONTEXT lpcontext
    )
{
#ifndef WIN32S
    if (CrashDump) {
        return DmpGetContext(hthd->tid-1, lpcontext);
    } else if (hthd->fWowEvent) {
        return WOWGetThreadContext(hthd, lpcontext);
    } else
#endif
    {
#ifndef TARGET_MIPS
        return GetThreadContext(hthd->rwHand, lpcontext);
#else
        BOOL rc;
        DWORD Flags = lpcontext->ContextFlags;

        if (MipsContextSize == Ctx32Bit) {
            lpcontext->ContextFlags = ((lpcontext->ContextFlags & ~CONTEXT_EXTENDED_INTEGER) | CONTEXT_INTEGER);
        }
        rc = GetThreadContext(hthd->rwHand, lpcontext);
        if (rc) {
            if ((Flags & CONTEXT_EXTENDED_INTEGER) == CONTEXT_EXTENDED_INTEGER) {
                CoerceContext32To64(lpcontext);
            } else if ((Flags & CONTEXT_INTEGER) == CONTEXT_INTEGER) {
                CoerceContext64To32(lpcontext);
            }
        }
        return rc;
#endif
    }
}

BOOL
DbgSetThreadContext(
    HTHDX hthd,
    PCONTEXT lpcontext
    )
{
    assert(!CrashDump);

#ifndef WIN32S
    if (CrashDump) {
        return FALSE;
    } else if (hthd->fWowEvent) {
        return WOWSetThreadContext(hthd, lpcontext);
    } else
#endif
    {
#ifdef TARGET_MIPS
        CONTEXT ctx = *lpcontext;
        lpcontext = &ctx;
        if (MipsContextSize == Ctx32Bit) {
            CoerceContext64To32(lpcontext);
        } else {
            CoerceContext32To64(lpcontext);
        }
#endif
        return SetThreadContext(hthd->rwHand, lpcontext);
    }
}

BOOL
WriteBreakPoint(
    IN PBREAKPOINT Breakpoint
    )
{
    DWORD cb;
    BP_UNIT opcode = BP_OPCODE;
    BOOL r = AddrWriteMemory(Breakpoint->hprc,
                             Breakpoint->hthd,
                             &Breakpoint->addr,
                             &opcode,
                             BP_SIZE,
                             &cb);
    return (r && (cb == BP_SIZE));
}

BOOL
RestoreBreakPoint(
    IN PBREAKPOINT Breakpoint
    )
{
    DWORD cb;
    BOOL r;

    assert(Breakpoint->bpType == bptpExec);

    r = AddrWriteMemory(Breakpoint->hprc,
                        Breakpoint->hthd,
                        &Breakpoint->addr,
                        &Breakpoint->instr1,
                        BP_SIZE,
                        &cb);
    return (r && (cb == BP_SIZE));
}



/****************************************************************************/
/****************************************************************************/
#ifndef PROCESSOR_MIPS_R10000
#define PROCESSOR_MIPS_R10000  10000
#endif


VOID
GetMachineType(
    LPPROCESSOR p
    )
{
    // Look Ma, no ifdefs!!

    SYSTEM_INFO SystemInfo;

    GetSystemInfo(&SystemInfo);
    switch (SystemInfo.wProcessorArchitecture) {

        case PROCESSOR_ARCHITECTURE_INTEL:
            p->Level = SystemInfo.wProcessorLevel;
            p->Type = mptix86;
            p->Endian = endLittle;
            break;

        case PROCESSOR_ARCHITECTURE_MIPS:
            p->Level = SystemInfo.wProcessorLevel * 1000;
            p->Type = mptmips;
            p->Endian = endLittle;
            break;

        case PROCESSOR_ARCHITECTURE_ALPHA:
            p->Level = SystemInfo.wProcessorLevel;
            p->Type = mptdaxp;
            p->Endian = endLittle;
            p->Level = 21064;           // BUGBUG - why?
            break;

        case PROCESSOR_ARCHITECTURE_PPC:
            p->Level = SystemInfo.wProcessorLevel + 600;    // BUGBUG - 603+
            p->Type = mptmppc;
            p->Endian = endLittle;
            break;

        default:
            assert(!"Unknown target machine");
            break;
    }
}



HWND
HwndFromPid (
    PID pid
    )
{

    HWND    hwnd = GetForegroundWindow();
    HWND    hwndNext;

    DPRINT(4, ( "*HwndFromPid, pid = 0x%lx\n", pid ) );

    for (hwndNext = GetWindow ( hwnd, GW_HWNDFIRST );
         hwndNext;
         hwndNext = GetWindow ( hwndNext, GW_HWNDNEXT )) {

        // what we want is windows *without* an owner, hence !GetWindow...
        if ( !GetWindow ( hwndNext, GW_OWNER ) &&
                                             IsWindowVisible ( hwndNext ) ) {
            PID pidT;

            GetWindowThreadProcessId ( hwndNext, &pidT );
            DPRINT(4, ("\thwnd 0x%08lx owned by process 0x%lx, ",
                                                            hwndNext, pidT ) );
#if DBG
            {
                char    szWindowText[256];
                if ( GetWindowText(hwndNext, szWindowText,
                                                     sizeof(szWindowText)) ) {
                    DPRINT(4, ("title = \"%s\"\n", szWindowText) );
                } else {
                    DPRINT(4, ("title = \"<none>\"\n") );
                }
            }
#endif
            if ( pid == pidT ) {
                // found a match, return the hwnd
                break;
            }
        } // if ( !GetWindow...
        hwndNext = GetWindow ( hwndNext, GW_HWNDNEXT );
    } // while ( hwndNext )

    return hwndNext;
}

VOID
DmSetFocus (
    HPRCX phprc
    )
{
    PID     pidGer;         // debugger pid
    PID     pidCurFore;     // owner of foreground window
    HWND    hwndCurFore;    // current foreground window
    HWND    phprc_hwndProcess;
    HWND    hwndT;


    // decide if we are the foreground app currently
    pidGer = GetCurrentProcessId(); // debugger pid
    hwndCurFore = GetForegroundWindow();
    if ( hwndCurFore &&
        GetWindowThreadProcessId ( hwndCurFore, &pidCurFore ) ) {
        if ( pidCurFore != pidGer ) {
            // foreground is not debugger, bail out
            return;
        }
    }

    phprc_hwndProcess = HwndFromPid ( phprc->pid );
    if ( !phprc_hwndProcess ) {
        // no window attached to pid; bail out
        return;
    }

    // continuing with valid hwnd's and we have foreground window
    assert ( phprc_hwndProcess );

    // now, get the last active window in that group!
    hwndT = GetLastActivePopup ( phprc_hwndProcess );

    // NOTE: taskman has a check at this point for state disabled...
    //  don't know if I should do it either...
    SetForegroundWindow ( hwndT );
}



/****************************************************************************/
//
// ContinueDebugEvent() queue.
//  We can only have one debug event pending per process, but there may be
//  one event pending for each process we are debugging.
//
//  There are 200 entries in a static sized queue.  If there are ever more
//  than 200 debug events  pending, AND windbg actually handles them all in
//  less than 1/5 second, we will be in trouble.  Until then, sleep soundly.
//
/****************************************************************************/
typedef struct tagCQUEUE {
    struct tagCQUEUE *next;
    DWORD  pid;
    DWORD  tid;
    DWORD  dwContinueStatus;
} CQUEUE, *LPCQUEUE;

static LPCQUEUE lpcqFirst;
static LPCQUEUE lpcqLast;
static LPCQUEUE lpcqFree;
static CQUEUE cqueue[200];
static CRITICAL_SECTION csContinueQueue;

static BOOL DequeueContinueDebugEvents( void );


/***************************************************************************/
/***************************************************************************/



VOID
QueueContinueDebugEvent(
    DWORD   dwProcessId,
    DWORD   dwThreadId,
    DWORD   dwContinueStatus
    )

/*++

Routine Description:

    Queue a debug event continue for later execution.

Arguments:

    dwProcessId = pid to continue

    dwThreadId = tid to continue

    dwContinueStatus - Supplies the continue status code

Return Value:

    None.

--*/

{
#ifdef WIN32S

    HTHDX       hthd;

    if (dwContinueStatus != DBG_TERMINATE_PROCESS) {
        hthd = HTHDXFromPIDTID(dwProcessId, dwThreadId);
        if (hthd) {
            if (hthd->fContextDirty) {
                /*
                 * Set the child's context
                 */
                DPRINT(1, ("Context is dirty\n"));

                DbgSetThreadContext(hthd, &hthd->context);

                hthd->fContextDirty = FALSE;
            }
        }
    }

    DPRINT(1, ("ContinueDebugEvent(PID:%x, TID:%x, Stat:%u)\r\n",
      dwProcessId, dwThreadId, dwContinueStatus));

    ContinueDebugEvent(dwProcessId, dwThreadId, dwContinueStatus);
    fProcessingDebugEvent = FALSE;
    fWaitForDebugEvent = FALSE;

#else

    LPCQUEUE lpcq;

    EnterCriticalSection(&csContinueQueue);

    lpcq = lpcqFree;
    assert(lpcq);

    lpcqFree = lpcq->next;

    lpcq->next = NULL;
    if (lpcqLast) {
        lpcqLast->next = lpcq;
    }
    lpcqLast = lpcq;

    if (!lpcqFirst) {
        lpcqFirst = lpcq;
    }

    lpcq->pid = dwProcessId;
    lpcq->tid = dwThreadId;
    lpcq->dwContinueStatus = dwContinueStatus;

    LeaveCriticalSection(&csContinueQueue);

#endif

    return;
}                               /* QueueContinueDebugEvent() */


#ifndef WIN32S
BOOL
DequeueContinueDebugEvents(
    VOID
    )
/*++

Routine Description:

    Remove any pending continues from the queue and execute them.

Arguments:

    none

Return Value:

    TRUE if one or more events were continued.
    FALSE if none were continued.

--*/
{
    LPCQUEUE    lpcq;
    BOOL        fDid = FALSE;
    HTHDX       hthd;

    EnterCriticalSection(&csContinueQueue);

    while ( lpcq=lpcqFirst ) {

        hthd = HTHDXFromPIDTID(lpcq->pid, lpcq->tid);
        if (hthd) {
            if (hthd->fContextDirty) {
                /*
                 * Set the child's context
                 */

                DbgSetThreadContext(hthd, &hthd->context);

                hthd->fContextDirty = FALSE;
            }
            hthd->fWowEvent = FALSE;
        }

        ContinueDebugEvent(lpcq->pid, lpcq->tid, lpcq->dwContinueStatus);

        lpcqFirst = lpcq->next;
        if (lpcqFirst == NULL) {
            lpcqLast = NULL;
        }

        lpcq->next = lpcqFree;
        lpcqFree   = lpcq;

        fDid = TRUE;
    }

    LeaveCriticalSection(&csContinueQueue);
    return fDid;
}                               /* DequeueContinueDebugEvents() */
#endif  // !WIN32S


VOID
AddQueue(
    DWORD   dwType,
    DWORD   dwProcessId,
    DWORD   dwThreadId,
    DWORD   dwData,
    DWORD   dwLen
    )

/*++

Routine Description:



Arguments:

    dwType

    dwProcessId

    dwThreadId

    dwData

    dwLen

Return Value:

    none

--*/

{
    switch (dwType) {
        case QT_CONTINUE_DEBUG_EVENT:
        case QT_TRACE_DEBUG_EVENT:
            if (CrashDump) {
                break;
            }

            QueueContinueDebugEvent(dwProcessId, dwThreadId, dwData);
            break;


        case QT_RELOAD_MODULES:
        case QT_REBOOT:
        case QT_CRASH:
        case QT_RESYNC:
            assert(!"Unsupported usermode QType in AddQueue.");
            break;

        case QT_DEBUGSTRING:
            assert(!"Is this a bad idea?");
            DMPrintShellMsg( "%s", (LPSTR)dwData );
            free((LPSTR)dwData);
            break;

    }

    if (dwType == QT_CONTINUE_DEBUG_EVENT) {
        SetEvent( hEventContinue );
    }

    return;
}

BOOL
DequeueAllEvents(
    BOOL fForce,       // force a dequeue even if the dm isn't initialized
    BOOL fConsume      // delete all events from the queue with no action
    )
{
#ifdef WIN32S
    return TRUE;
#else
    return DequeueContinueDebugEvents();
#endif
}

VOID
InitEventQueue(
    VOID
    )
{
    int n;
    int i;

    InitializeCriticalSection(&csContinueQueue);

    n = sizeof(cqueue) / sizeof(CQUEUE);
    for (i = 0; i < n-1; i++) {
        cqueue[i].next = &cqueue[i+1];
    }
    cqueue[n-1].next = NULL;
    lpcqFree = &cqueue[0];
    lpcqFirst = NULL;
    lpcqLast = NULL;
}


VOID
ProcessQueryTlsBaseCmd(
    HPRCX    hprcx,
    HTHDX    hthdx,
    LPDBB    lpdbb
    )

/*++

Routine Description:

    This function is called in response to an EM request to get the base
    of the thread local storage for a given thread and DLL.

Arguments:

    hprcx       - Supplies a process handle
    hthdx       - Supplies a thread handle
    lpdbb       - Supplies the command information packet

Return Value:

    None.

--*/

{
    XOSD       xosd;
    OFFSET      offRgTls;
    DWORD       iTls;
    LPADDR      lpaddr = (LPADDR) LpDmMsg->rgb;
    OFFSET      offResult;
    DWORD       cb;
    int         iDll;
    OFFSET      offDll = * (OFFSET *) lpdbb->rgbVar;

    /*
     * Read number 1.  Get the pointer to the Thread Local Storage array.
     */


    if ((DbgReadMemory(hprcx, (char *) hthdx->offTeb+0x2c,
                           &offRgTls, sizeof(OFFSET), &cb) == 0) ||
        (cb != sizeof(OFFSET))) {
    err:
        xosd = xosdUnknown;
        Reply(0, &xosd, lpdbb->hpid);
        return;
    }

    /*
     *  Read number 2.  Get the TLS index for this dll
     */

    for (iDll=0; iDll<hprcx->cDllList; iDll+=1 ) {
        if (hprcx->rgDllList[iDll].fValidDll &&
            (hprcx->rgDllList[iDll].offBaseOfImage == offDll)) {
            break;
        }
    }

    if (iDll == hprcx->cDllList) {
        goto err;
    }

    if (!DbgReadMemory(hprcx,
                      (char *) hprcx->rgDllList[iDll].offTlsIndex,
                      &iTls,
                      sizeof(iTls),
                      &cb) ||
            (cb != sizeof(iTls))) {
        goto err;
    }


    /*
     * Read number 3.  Get the actual TLS base pointer
     */

    if ((DbgReadMemory(hprcx, (char *)offRgTls+iTls*sizeof(OFFSET),
                           &offResult, sizeof(OFFSET), &cb) == 0) ||
        (cb != sizeof(OFFSET))) {
        goto err;
    }

    memset(lpaddr, 0, sizeof(ADDR));

    lpaddr->addr.off = offResult;
#ifdef TARGET_i386
    lpaddr->addr.seg = (SEGMENT) hthdx->context.SegDs;
#else
    lpaddr->addr.seg = 0;
#endif
    ADDR_IS_FLAT(*lpaddr) = TRUE;

    LpDmMsg->xosdRet = xosdNone;
    Reply( sizeof(ADDR), LpDmMsg, lpdbb->hpid );
    return;
}                               /* ProcessQueryTlsBaseCmd() */


VOID
ProcessQuerySelectorCmd(
    HPRCX   hprcx,
    HTHDX   hthdx,
    LPDBB   lpdbb
    )
/*++

Routine Description:

    This command is sent from the EM to fill in an LDT_ENTRY structure
    for a given selector.

Arguments:

    hprcx  - Supplies the handle to the process

    hthdx  - Supplies the handle to the thread and is optional

    lpdbb  - Supplies the pointer to the full query packet

Return Value:

    None.

--*/

{
    XOSD               xosd;

#if defined( TARGET_i386 )
    SEGMENT             seg;

    seg = *((SEGMENT *) lpdbb->rgbVar);

    if (hthdx == hthdxNull) {
        hthdx = hprcx->hthdChild;
    }

    if ((hthdx != NULL) &&
        (GetThreadSelectorEntry(hthdx->rwHand, seg, (LDT_ENTRY *) LpDmMsg->rgb))) {
        LpDmMsg->xosdRet = xosdNone;
        Reply( sizeof(LDT_ENTRY), LpDmMsg, lpdbb->hpid);
        return;
    }
#endif

#ifdef OSDEBUG4
    xosd = xosdInvalidParameter;
#else
    xosd = xosdInvalidSelector;
#endif

    Reply( sizeof(xosd), &xosd, lpdbb->hpid);

    return;
}                            /* ProcessQuerySelectorCmd */


VOID
ProcessVirtualQueryCmd(
    HPRCX hprc,
    LPDBB lpdbb
    )
{
    XOSD xosd = xosdNone;
    ADDR addr;
    BOOL fRet;
    DWORD dwSize;

    if (!hprc->rwHand || hprc->rwHand == (HANDLE)(-1)) {
#ifdef OSDEBUG4
        xosd = xosdBadProcess;
#else
        xosd = xosdInvalidProc;
#endif
    }

    addr = *(LPADDR)(lpdbb->rgbVar);

    if (!ADDR_IS_FLAT(addr)) {
        fRet = TranslateAddress(hprc, 0, &addr, TRUE);
        assert(fRet);
        if (!fRet) {
            xosd = xosdBadAddress;
            goto reply;
        }
    }

    dwSize = VirtualQueryEx(hprc->rwHand,
                            (LPCVOID)addr.addr.off,
                            (PMEMORY_BASIC_INFORMATION)LpDmMsg->rgb,
                            sizeof(MEMORY_BASIC_INFORMATION));

    if (dwSize != sizeof(MEMORY_BASIC_INFORMATION)) {
        xosd = xosdUnknown;
        goto reply;
    }

  reply:

    LpDmMsg->xosdRet = xosd;
    Reply( sizeof(MEMORY_BASIC_INFORMATION), LpDmMsg, lpdbb->hpid );

    return;
}                                  /* ProcessVirtualQueryCmd */

VOID
ProcessGetDmInfoCmd(
    HPRCX hprc,
    LPDBB lpdbb,
    DWORD cb
    )
{
    LPDMINFO lpi = (LPDMINFO)LpDmMsg->rgb;

    LpDmMsg->xosdRet = xosdNone;

    lpi->fAsync = 1;
#ifdef WIN32S
    lpi->fHasThreads = 0;
#else
    lpi->fHasThreads = 1;
#endif
    lpi->fReturnStep = 0;
    //lpi->fRemote = ???
    lpi->fAsyncStop  = 1;
    lpi->fAlwaysFlat = 0;
    lpi->fHasReload  = 0;

    lpi->cbSpecialRegs = 0;
    lpi->MajorVersion = 0;
    lpi->MinorVersion = 0;

    lpi->Breakpoints = bptsExec |
                       bptsDataC |
                       bptsDataW |
                       bptsDataR |
                       bptsDataExec;

    GetMachineType(&lpi->Processor);

    //
    // hack so that TL can call tlfGetVersion before
    // reply buffer is initialized.
    //
    if ( cb >= (sizeof(DBB) + sizeof(DMINFO)) ) {
        memcpy(lpdbb->rgbVar, lpi, sizeof(DMINFO));
    }

    Reply( sizeof(DMINFO), LpDmMsg, lpdbb->hpid );
}                                        /* ProcessGetDMInfoCmd */



ActionResumeThread(
    DEBUG_EVENT * pde,
    HTHDX hthd,
    DWORD unused,
    PSUSPENDSTRUCT pss
    )
{
    //
    // This thread just hit a breakpoint after falling out of
    // SuspendThread.  Clear the BP, put the original context
    // back and continue.
    //

    RemoveBP( pss->pbp );

    hthd->context = pss->context;
    hthd->fContextDirty = TRUE;
    hthd->pss = NULL;

    free(pss);

    AddQueue( QT_CONTINUE_DEBUG_EVENT,
              hthd->hprc->pid,
              hthd->tid,
              DBG_CONTINUE,
              0);
    return 0;
}


BOOL
MakeThreadSuspendItself(
    HTHDX   hthd
    )
/*++

Routine Description:

    Set up the thread to call SuspendThread.  This relies on kernel32
    being present in the debuggee, and the current implementation gives
    up if the thread is in a 16 bit context.

    The cpu dependent part of this is MakeThreadSuspendItselfHelper,
    in mach.c.

Arguments:

    hthd    - Supplies thread

Return Value:

    TRUE if the thread will be suspended, FALSE if not.

--*/
{
    PSUSPENDSTRUCT  pss;
    ADDR            addr;
    HANDLE          hdll;
    FARPROC         lpSuspendThread;

    //
    // the only time this should fail is when the debuggee
    // does not use kernel32, which is rare.
    //

    if (!hthd->hprc->dwKernel32Base) {
        DPRINT(1, ("can't suspend thread %x: Kernel32 not loaded\n",
                                                                 (DWORD)hthd));
        DMPrintShellMsg("*** Unable to suspend thread.\n");
        return 0;
    }

    //
    // Oh, yeah... don't try to do this with a 16 bit thread, either.
    // maybe someday...
    //

    if (hthd->fWowEvent) {
        DMPrintShellMsg("*** Can't leave 16 bit thread suspended.\n");
        return 0;
    }

    //
    // find the address of SuspendThread
    //

    hdll = GetModuleHandle("KERNEL32");
    assert(hdll || !"kernel32 not found in DM!!!");
    if (!hdll) {
        return 0;
    }

    lpSuspendThread = GetProcAddress(hdll, "SuspendThread");
    assert(lpSuspendThread || !"SuspendThread not found in kernel32!!!");
    if (!lpSuspendThread) {
        return 0;
    }

    //
    // this is probably unneccessary, because I think kernel32
    // may not be relocated.
    //
    lpSuspendThread = (FARPROC)((DWORD)lpSuspendThread - (DWORD)hdll
                                                 + hthd->hprc->dwKernel32Base);

    pss = malloc(sizeof(*pss));
    assert(pss || !"malloc failed in MakeThreadSuspendItself");
    if (!pss) {
        return 0;
    }

    //
    // Remember the current context
    //
    hthd->pss = pss;
    pss->context = hthd->context;

    //
    // set a BP on the current PC, and register a persistent
    // expected event to catch it later.
    //

    AddrInit(&addr, 0, 0, (DWORD) PC(hthd), TRUE, TRUE, FALSE, FALSE);
    pss->pbp = SetBP( hthd->hprc, hthd, bptpExec, bpnsStop, &addr, (HPID) INVALID);

    //
    // don't try to step off of BP.
    //
    pss->atBP = hthd->atBP;
    hthd->atBP = NULL;

    RegisterExpectedEvent(
            hthd->hprc,
            hthd,
            BREAKPOINT_DEBUG_EVENT,
            (DWORD)pss->pbp,
            NULL,
            (ACVECTOR)ActionResumeThread,
            TRUE,
            pss);

    //
    // do machine dependent part
    //
    MakeThreadSuspendItselfHelper(hthd, lpSuspendThread);

    return TRUE;
}



VOID
ProcessIoctlGenericCmd(
    HPRCX   hprc,
    HTHDX   hthd,
    LPDBB   lpdbb
    )
{
    LPIOL              lpiol  = (LPIOL)lpdbb->rgbVar;
    PIOCTLGENERIC      pig    = (PIOCTLGENERIC)lpiol->rgbVar;
    DWORD              len;
    ADDR               addr;


    switch( pig->ioctlSubType ) {
        case IG_TRANSLATE_ADDRESS:
            memcpy( &addr, pig->data, sizeof(addr) );
            if (TranslateAddress( hprc, hthd, &addr, TRUE )) {
                memcpy( pig->data, &addr, sizeof(addr) );
                len = sizeof(IOCTLGENERIC) + pig->length;
                memcpy( LpDmMsg->rgb, pig, len );
                LpDmMsg->xosdRet = xosdNone;
                Reply( sizeof(IOCTLGENERIC)+pig->length, LpDmMsg, lpdbb->hpid );
            } else {
                LpDmMsg->xosdRet = xosdUnknown;
                Reply( 0, LpDmMsg, lpdbb->hpid );
            }
            break;

        case IG_WATCH_TIME:
            WtRangeStep( hthd );
            LpDmMsg->xosdRet = xosdNone;
            Reply( 0, LpDmMsg, lpdbb->hpid );
            break;

        case IG_WATCH_TIME_STOP:
            WtStruct.fWt = TRUE;
            WtStruct.dwType = pig->ioctlSubType;
            WtStruct.hthd = hthd;
            LpDmMsg->xosdRet = xosdNone;
            Reply( 0, LpDmMsg, lpdbb->hpid );
            break;

        case IG_WATCH_TIME_RECALL:
            WtStruct.fWt = TRUE;
            WtStruct.dwType = pig->ioctlSubType;
            WtStruct.hthd = hthd;
            LpDmMsg->xosdRet = xosdNone;
            Reply( 0, LpDmMsg, lpdbb->hpid );
            break;

        case IG_WATCH_TIME_PROCS:
            WtStruct.fWt = TRUE;
            WtStruct.dwType = pig->ioctlSubType;
            WtStruct.hthd = hthd;
            LpDmMsg->xosdRet = xosdNone;
            Reply( 0, LpDmMsg, lpdbb->hpid );
            break;

        case IG_THREAD_INFO:
#ifdef WIN32S
            LpDmMsg->xosdRet = xosdUnknown;
            Reply( 0, LpDmMsg, lpdbb->hpid );
#else
            {
            typedef NTSTATUS (* QTHREAD)(HANDLE,THREADINFOCLASS,PVOID,ULONG,PULONG);

            NTSTATUS                   Status;
            THREAD_BASIC_INFORMATION   ThreadBasicInfo;
            QTHREAD                    Qthread;

            Qthread = (QTHREAD)GetProcAddress( GetModuleHandle( "ntdll.dll" ), "NtQueryInformationThread" );
            if (!Qthread) {
                LpDmMsg->xosdRet = xosdUnknown;
                Reply( 0, LpDmMsg, lpdbb->hpid );
                break;
            }

            Status = Qthread( hthd->rwHand,
                             ThreadBasicInformation,
                             &ThreadBasicInfo,
                             sizeof(ThreadBasicInfo),
                             NULL
                            );
            if (!NT_SUCCESS(Status)) {
                LpDmMsg->xosdRet = xosdUnknown;
                Reply( 0, LpDmMsg, lpdbb->hpid );
            }

            *(LPDWORD)pig->data = (DWORD)ThreadBasicInfo.TebBaseAddress;

            len = sizeof(IOCTLGENERIC) + pig->length;
            memcpy( LpDmMsg->rgb, pig, len );

            LpDmMsg->xosdRet = xosdNone;
            Reply( len, LpDmMsg, lpdbb->hpid );
            }
#endif // WIN32S
            break;

        case IG_TASK_LIST:
#ifdef WIN32S
            LpDmMsg->xosdRet = xosdUnknown;
            Reply( 0, LpDmMsg, lpdbb->hpid );
#else
            {
            PTASK_LIST pTaskList = (PTASK_LIST)pig->data;
            GetTaskList( pTaskList, pTaskList->dwProcessId );
            len = sizeof(IOCTLGENERIC) + pig->length;
            memcpy( LpDmMsg->rgb, pig, len );
            LpDmMsg->xosdRet = xosdNone;
            Reply( sizeof(IOCTLGENERIC)+pig->length, LpDmMsg, lpdbb->hpid );
            }
#endif
            break;

        default:
            LpDmMsg->xosdRet = xosdUnknown;
            Reply( 0, LpDmMsg, lpdbb->hpid );
            break;
    }

    return;
}


VOID
ProcessIoctlCustomCmd(
    HPRCX   hprc,
    HTHDX   hthd,
    LPDBB   lpdbb
    )
{
    LPIOL   lpiol  = (LPIOL)lpdbb->rgbVar;
    LPSTR   p      = lpiol->rgbVar;


    LpDmMsg->xosdRet = xosdUnsupported;

    //
    // parse the command
    //
    while (*p && !isspace(*p++));
    if (*p) {
        *(p-1) = '\0';
    }

    //
    // we don't have any custom dot command here yet
    // when we do this is what the code should look like:
    //
    // at this point the 'p' variable points to any arguments
    // to the dot command
    //
    //      if (_stricmp( lpiol->rgbVar, "dot-command" ) == 0) {
    //          -----> do your thing <------
    //          LpDmMsg->xosdRet = xosdNone;
    //      }
    //
#if 0
    if ( !_stricmp(lpiol->rgbVar, "FastStep") ) {
        fSmartRangeStep = TRUE;
        LpDmMsg->xosdRet = xosdNone;
    } else if ( !_stricmp(lpiol->rgbVar, "SlowStep") ) {
        fSmartRangeStep = FALSE;
        LpDmMsg->xosdRet = xosdNone;
    }
#else
        LpDmMsg->xosdRet = xosdNone;
#endif

    //
    // send back our response
    //
    Reply(0, LpDmMsg, lpdbb->hpid);
}                             /* ProcessIoctlCustomCmd() */


#ifndef WIN32S

DWORD WINAPI
DoTerminate(
    LPVOID lpv
    )
{
    HPRCX hprcx = (HPRCX)lpv;

    if (CrashDump) {
        ProcessUnloadCmd(hprcx, NULL, NULL);
        return 0;
    }
    TerminateProcess(hprcx->rwHand, 1);

    //
    // now that TerminateThread has completed, put priority
    // back before calling out of DM
    //

    SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_NORMAL);

    WaitForSingleObject(hprcx->hExitEvent, INFINITE);

    ProcessUnloadCmd(hprcx, NULL, NULL);

    return  0;
}

VOID
CompleteTerminateProcessCmd(
    VOID
    )
{
    DEBUG_EVENT devent, *de=&devent;
    HANDLE      hThread;
    DWORD       dwTid;
    BREAKPOINT *pbpT;
    BREAKPOINT *pbp;
    PKILLSTRUCT pk;
    HPRCX       hprc;
    HTHDX       hthd;

    DEBUG_PRINT("CompleteTerminateProcessCmd");

    EnterCriticalSection(&csKillQueue);

    pk = KillQueue;
    if (pk) {
        KillQueue = pk->next;
    }

    LeaveCriticalSection(&csKillQueue);

    assert(pk);
    if (!pk) {
        return;
    }

    hprc = pk->hprc;
    free(pk);

    ConsumeAllProcessEvents(hprc, TRUE);

    /*
     * see if process is already dead
     */

    if ((hprc->pstate & ps_dead) || (hprc->rwHand == (HANDLE)INVALID)) {

        //
        // Queue a continue if any thread is stopped
        //

        for (hthd = hprc->hthdChild; hthd; hthd = hthd->nextSibling) {
            if (hthd->tstate & ts_stopped) {
                AddQueue( QT_CONTINUE_DEBUG_EVENT,
                          hthd->hprc->pid,
                          hthd->tid,
                          DBG_CONTINUE,
                          0);
                hthd->tstate &= ~ts_stopped;
                hthd->tstate |= ts_running;
            }
        }

        ProcessUnloadCmd(hprc, NULL, NULL);

    } else {

        for (pbp = BPNextHprcPbp(hprc, NULL); pbp; pbp = pbpT) {
            pbpT = BPNextHprcPbp(hprc, pbp);
            RemoveBP(pbp);
        }

        //
        // Start another thread to kill the thing.  This thread needs to
        // continue any threads which are stopped.  The new thread will then
        // wait until this one (the poll thread) has handled all of the
        // events, and then send destruction notifications to the shell.
        //

        hThread = CreateThread(NULL,
                               4096,
                               DoTerminate,
                               (LPVOID)hprc,
                               0,
                               &dwTid);
        assert(hThread);
        if ( !hThread ) {
            return;
        }

        //
        //  Yield so DoTerminate can do its thing before we start posting
        //  ContinueDebugEvents, so we minimize the time that the app
        //  runs before it is terminated.
        //

        hprc->pstate |= ps_killed;
        SetThreadPriority(hThread, THREAD_PRIORITY_TIME_CRITICAL);
        Sleep(0);

        CloseHandle(hThread);

        //
        // Queue a continue if any thread is stopped
        //

        for (hthd = hprc->hthdChild; hthd; hthd = hthd->nextSibling) {
            if (hthd->tstate & ts_stopped) {
                AddQueue( QT_CONTINUE_DEBUG_EVENT,
                          hthd->hprc->pid,
                          hthd->tid,
                          DBG_CONTINUE,
                          0);
                hthd->tstate &= ~ts_stopped;
                hthd->tstate |= ts_running;
            }
        }

    }

}


DWORD
ProcessTerminateProcessCmd(
    HPRCX hprc,
    HTHDX hthd,
    LPDBB lpdbb
    )
{
    PKILLSTRUCT pk;

    if (!hprc) {
        return FALSE;
    }

    Unreferenced( lpdbb );

    pk = (PKILLSTRUCT)malloc(sizeof(KILLSTRUCT));
    pk->hprc = hprc;

    EnterCriticalSection(&csKillQueue);

    pk->next = KillQueue;
    KillQueue = pk;

    LeaveCriticalSection(&csKillQueue);

    return TRUE;
}
#endif

#ifdef WIN32S

DWORD
ProcessTerminateProcessCmd(
    HPRCX hprc,
    HTHDX hthd,
    LPDBB lpdbb
    )
{
    DWORD rval;

    if (!hprc) {
        return FALSE;
    }

    Unreferenced( lpdbb );

    DEBUG_PRINT_2("ProcessTerminateProcessCmd called hprc=0x%x, hthd=0x%x.\n\r",
      hprc, hthd);

    // Win32s doesn't support TerminateProcess(), but does give us a special
    // ContinueDebugEvent flag.  If we are stopped at a debug event, we can
    // Continue with this flag to terminate the child app.

    DEBUG_PRINT("ConsumeAllProcessEvents\r\n");

    ConsumeAllProcessEvents(hprc, TRUE);

    DEBUG_PRINT("Check process state\r\n");

    if ((hprc->pstate & ps_dead) || hprc->rwHand == (HANDLE)INVALID) {
        DEBUG_PRINT("Process already dead\r\n");
        if (fExitProcessDebugEvent) {
            // we saved tidExit when we got the EXIT_PROCESS_DEBUG_EVENT
            AddQueue( QT_CONTINUE_DEBUG_EVENT,
                      hprc->pid,
                      tidExit,
                      DBG_CONTINUE,
                      0);
        }
        rval = FALSE;   // already dead
    }

    if (fProcessingDebugEvent) {
        DEBUG_PRINT_1("Continue with %s\r\n",
          (fExitProcessDebugEvent ? "DBG_CONTINUE" : "DBG_TERMINATE_PROCESS"));

        for (hthd = hprc->hthdChild; hthd; hthd = hthd->nextSibling) {
            if (hthd->tstate & ts_stopped) {
                AddQueue( QT_CONTINUE_DEBUG_EVENT,
                          hthd->hprc->pid,
                          hthd->tid,
                          fExitProcessDebugEvent?
                                 DBG_CONTINUE : DBG_TERMINATE_PROCESS,
                          0);
                hthd->tstate &= ~ts_stopped;
                hthd->tstate |= ts_running;
            }
        }

        // mark this process as killed
        DEBUG_PRINT("Mark process as killed\r\n");
        hprc->pstate |= ps_killed;
        rval = TRUE;   // killed it.
    } else {
        DEBUG_PRINT("Can't terminate process right now\r\n");
        // can't continue debug event right now, so can't terminate.
        rval = FALSE;
    }

    return rval;
}
#endif


VOID
ProcessAllProgFreeCmd(
    HPRCX hprcXX,
    HTHDX hthd,
    LPDBB lpdbb
    )
{
    HPRCX hprc;

    Unreferenced(hprcXX);
    Unreferenced(hthd);

    for (;;) {

        EnterCriticalSection(&csThreadProcList);
        for (hprc = prcList; hprc; hprc = hprc->next) {
            if (hprc->pstate != (ps_root | ps_destroyed)) {
                break;
            }
        }
        LeaveCriticalSection(&csThreadProcList);

        if (hprc) {
            ProcessTerminateProcessCmd(hprc, hthd, lpdbb);
            ProcessUnloadCmd(hprc, hthd, lpdbb);
        } else {
            break;
        }

    }

    WaitForSingleObject(hEventNoDebuggee, INFINITE);
}


DWORD
ProcessAsyncGoCmd(
    HPRCX hprc,
    HTHDX hthd,
    LPDBB lpdbb
    )
{
    XOSD       xosd = xosdNone;
#ifndef WIN32S
    DEBUG_EVENT de;
#endif

    DEBUG_PRINT("ProcessAsyncGoCmd called.\n\r");

#ifdef WIN32S
    xosd = xosdUnsupported;    // can't resume thread in win32s
#else

#ifdef WIN32
    if ((hthd->tstate & ts_frozen)) {
        if (hthd->tstate & ts_stopped) {
            //
            // if at a debug event, it won't really be suspended,
            // so just clear the flag.
            //
            hthd->tstate &= ~ts_frozen;

        } else if (ResumeThread(hthd->rwHand) == -1L ) {

#ifdef OSDEBUG4
            xosd = xosdBadThread;
#else
            xosd = xosdInvalidThread;
#endif

        } else {

            hthd->tstate &= ~ts_frozen;

            /*
             * deal with dead, frozen, continued thread:
             */
            if ((hthd->tstate & ts_dead) && !(hthd->tstate & ts_stopped)) {

                de.dwDebugEventCode = DESTROY_THREAD_DEBUG_EVENT;
                de.dwProcessId = hprc->pid;
                de.dwThreadId = hthd->tid;
                NotifyEM(&de, hthd, 0, NULL);
                FreeHthdx(hthd);

                hprc->pstate &= ~ps_deadThread;
                for (hthd = hprc->hthdChild; hthd; hthd = hthd->nextSibling) {
                    if (hthd->tstate & ts_dead) {
                        hprc->pstate |= ps_deadThread;
                    }
                }

            }
        }
    }
#endif  // WIN32
#endif  // !WIN32S

    Reply(0, &xosd, lpdbb->hpid);
    return(xosd);
}


void
ActionAsyncStop(
    DEBUG_EVENT *   pde,
    HTHDX           hthd,
    DWORD           unused,
    BREAKPOINT *    pbp
    )
/*++

Routine Description:

    This routine is called if a breakpoint is hit which is part of a
    Async Stop request.  When hit is needs to do the following:  clean
    out any expected events on the current thread, clean out all breakpoints
    which are setup for doing the current async stop.

Arguments:

    pde - Supplies a pointer to the debug event which just occured

    hthd - Supplies a pointer to the thread for the debug event

    pbp  - Supplies a pointer to the breakpoint for the ASYNC stop

Return Value:

    None.

--*/

{
    union {
        RTP rtp;
        char rgb[sizeof(RTP) + sizeof(BPR)];
    } rtpbuf;
    RTP *       prtp = &rtpbuf.rtp;
    BPR *       pbpr = (BPR *) prtp->rgbVar;
    HPRCX       hprc = hthd->hprc;
    BREAKPOINT * pbpT;

    /*
     *  We no longer need to have this breakpoint set.
     */

    RemoveBP( pbp );

    /*
     *  Remove any other breakpoints in this process which are for
     *  async stop commands
     */

    for (pbp = BPNextHprcPbp(hprc, NULL); pbp != NULL; pbp = pbpT) {

        pbpT = BPNextHprcPbp(hprc, pbp);

        if (pbp->id == (HPID)ASYNC_STOP_BP) {
            RemoveBP( pbp );
        }
    }

    /*
     * Setup a return packet which says we hit an async stop breakpoint
     */

    prtp->hpid = hprc->hpid;
    prtp->htid = hthd->htid;
    prtp->dbc = dbcAsyncStop;
    prtp->cb = sizeof(BPR);

#ifdef TARGET_i386
    pbpr->segCS  = (SEGMENT) hthd->context.SegCs;
    pbpr->segSS  = (SEGMENT) hthd->context.SegSs;
    pbpr->offEBP = (UOFFSET) hthd->context.Ebp;
#endif

    pbpr->offEIP = (DWORD) PC(hthd);

    DmTlFunc(tlfDebugPacket, prtp->hpid, sizeof(rtpbuf), (LONG)&rtpbuf);

    return;
}                               /* ActionAsyncStop() */



VOID
ProcessAsyncStopCmd(
    HPRCX       hprc,
    HTHDX       hthd,
    LPDBB       lpdbb
    )

/*++

Routine Description:

    This function is called in response to a asynchronous stop request.
    In order to do this we will set breakpoints the current PC for
    every thread in the system and wait for the fireworks to start.

Arguments:

    hprc        - Supplies a process handle

    hthd        - Supplies a thread handle

    lpdbb       - Supplies the command information packet

Return Value:

    None.

--*/

{
#ifdef WIN32S

    /*
     * Win32s doesn't support async stop this way.  The user should
     * press the debugger hot key at the debuggee console to generate
     * an async stop.  This may change if BoazF gives us a private API
     * to generate the async stop exception.
     */
    DEBUG_PRINT("\r\nProcessAsyncStopCmd\r\n");

    LpDmMsg->xosdRet = xosdUnsupported;
    Reply(0, LpDmMsg, lpdbb->hpid);
    return;

#else
    CONTEXT     regs;
    BREAKPOINT * pbp;
    ADDR        addr;
    BOOL        fSetFocus = * ( BOOL *) lpdbb->rgbVar;

    regs.ContextFlags = CONTEXT_CONTROL;


    /*
     *  Step 1.  Enumerate through the threads and freeze them all.
     */

    for (hthd = hprc->hthdChild; hthd != NULL; hthd = hthd->nextSibling) {
        if (SuspendThread(hthd->rwHand) == -1L) {
            ; // Internal error;
        }
    }

    /*
     *  Step 2.  Place a breakpoint on every PC address
     */

    for (hthd = hprc->hthdChild; hthd != NULL; hthd = hthd->nextSibling) {
#ifndef WIN32S
        if (CrashDump) {
            DmpGetContext( hthd->tid-1, &regs );
        } else
#endif
        {
            GetThreadContext( hthd->rwHand, &regs );
        }

        AddrInit(&addr, 0, 0, (DWORD)cPC(&regs), TRUE, TRUE, FALSE, FALSE);
        pbp = SetBP(hprc, hthd, bptpExec, bpnsStop, &addr, (HPID) ASYNC_STOP_BP);

        RegisterExpectedEvent(hthd->hprc,
                              hthd,
                              BREAKPOINT_DEBUG_EVENT,
                              (DWORD)pbp,
                              DONT_NOTIFY,
                              (ACVECTOR)ActionAsyncStop,
                              FALSE,
                              pbp);
    }

    /*
     *  Step 3.  Unfreeze all threads
     */

    if (fSetFocus) {
        DmSetFocus(hprc);
    }
    for (hthd = hprc->hthdChild; hthd != NULL; hthd = hthd->nextSibling) {
        if (ResumeThread(hthd->rwHand) == -1) {
            ; // Internal error
        }
    }

    LpDmMsg->xosdRet = xosdNone;
    Reply(0, LpDmMsg, lpdbb->hpid);
    return;
#endif
}                            /* ProcessAsyncStopCmd() */


VOID
ProcessDebugActiveCmd(
    HPRCX hprc,
    HTHDX hthd,
    LPDBB lpdbb
    )
{
#ifdef WIN32S

    Unreferenced(hprc);
    Unreferenced(hthd);

    LpDmMsg->xosdRet = xosdUnsupported; // can't attatch in win32s
    *((DWORD *)LpDmMsg->rgb) = ERROR_NOT_SUPPORTED;
    Reply(sizeof(DWORD), LpDmMsg, lpdbb->hpid);

#else

#ifdef OSDEBUG4

    LPDAP lpdap = ((LPDAP)(lpdbb->rgbVar));

    Unreferenced(hprc);
    Unreferenced(hthd);

    if (fDisconnected) {

        SetEvent( hEventRemoteQuit );

    } else if (!StartDmPollThread()) {

        //
        // CreateThread() failed; fail and send a dbcError.
        //
        LpDmMsg->xosdRet = xosdUnknown;
        Reply(0, LpDmMsg, lpdbb->hpid);

    } else if (WaitForSingleObject(DebugActiveStruct.hEventReady, INFINITE)
                                                                        != 0) {
        //
        // the wait failed.  why?  are there cases where we
        // should restart the wait?
        //
        LpDmMsg->xosdRet = xosdUnknown;
        Reply(0, LpDmMsg, lpdbb->hpid);

    } else {

        ResetEvent(DebugActiveStruct.hEventReady);
        ResetEvent(DebugActiveStruct.hEventApiDone);

        DebugActiveStruct.dwProcessId = lpdap->dwProcessId;
        DebugActiveStruct.hEventGo    = lpdap->hEventGo;
        DebugActiveStruct.fAttach     = TRUE;

        *nameBuffer = 0;

        // wait for it...

        if (WaitForSingleObject(DebugActiveStruct.hEventApiDone, INFINITE) == 0
           && DebugActiveStruct.fReturn != 0) {

            LpDmMsg->xosdRet = xosdNone;
            //
            // the poll thread will reply when creating the "root" process.
            //
            if (!fUseRoot) {
                Reply(0, LpDmMsg, lpdbb->hpid);
            }

        } else {
            LpDmMsg->xosdRet = xosdUnknown;
            Reply(0, LpDmMsg, lpdbb->hpid);
        }

    }


#else // OSDEBUG4

    LPDBG_ACTIVE_STRUCT lpdba = ((LPDBG_ACTIVE_STRUCT)(lpdbb->rgbVar));

    Unreferenced(hprc);
    Unreferenced(hthd);

    if (fDisconnected) {
        SetEvent( hEventRemoteQuit );
    } else if (!StartDmPollThread()) {

        LpDmMsg->xosdRet = xosdUnknown;
        // Last error is from CreateThread();
        *((DWORD *)LpDmMsg->rgb) = GetLastError();
        Reply(0, LpDmMsg, lpdbb->hpid);

    // wait for attach struct to be available
    } else if (WaitForSingleObject(DebugActiveStruct.hEventReady, INFINITE)
                                                                        != 0) {
        LpDmMsg->xosdRet = xosdUnknown;
        *((DWORD *)LpDmMsg->rgb) = GetLastError();
        Reply(0, LpDmMsg, lpdbb->hpid);

    } else {

        ResetEvent(DebugActiveStruct.hEventReady);
        ResetEvent(DebugActiveStruct.hEventApiDone);

        DebugActiveStruct.dwProcessId = lpdba->dwProcessId;
        DebugActiveStruct.hEventGo    = lpdba->hEventGo;
        DebugActiveStruct.fAttach     = TRUE;

        *nameBuffer = 0;

        // wait for it...

        if (WaitForSingleObject(DebugActiveStruct.hEventApiDone, INFINITE) == 0
           && DebugActiveStruct.fReturn != 0) {

    LpDmMsg->xosdRet = xosdNone;
            //
            // the poll thread will reply when creating the "root" process.
            //
            if (!fUseRoot) {
    Reply(0, LpDmMsg, lpdbb->hpid);
            }

        } else {

            DebugActiveStruct.dwProcessId = 0;
            DebugActiveStruct.hEventGo    = NULL;
            LpDmMsg->xosdRet = xosdUnknown;
            *((DWORD *)LpDmMsg->rgb) = DebugActiveStruct.dwError;
            Reply(0, LpDmMsg, lpdbb->hpid);
        }
    }

#endif // OSDEBUG4

    SetEvent(DebugActiveStruct.hEventReady);

#endif // WIN32S
}


VOID
ProcessRemoteQuit(
    VOID
    )
{
    HPRCX      hprc;
    BREAKPOINT *pbp;
    BREAKPOINT *pbpT;


    EnterCriticalSection(&csThreadProcList);

    for(hprc=prcList->next; hprc; hprc=hprc->next) {
        for (pbp = BPNextHprcPbp(hprc, NULL); pbp; pbp = pbpT) {
            pbpT = BPNextHprcPbp(hprc, pbp);
            RemoveBP(pbp);
        }
    }

    LeaveCriticalSection(&csThreadProcList);

    fDisconnected = TRUE;
    ResetEvent( hEventRemoteQuit );
}