Doing Detach with ICorDebug

Detaching a managed-debugger is somewhat complicated at the ICorDebug API level. In a perfectly-friendly API, you could just call "ICorDebugProcess::Detach" and be done with it. With managed-debugging, there are two main constraints and the hresults (in parenthesis) that you'll get for violating them:

  1. You can't be doing interop-debugging (CORDBG_E_INTEROP_NOT_SUPPORTED) or Edit-and-Continue (CORDBG_E_DETACH_FAILED_ON_ENC).
  2. The debuggee must be synchronized (CORDBG_E_PROCESS_NOT_SYNCHRONIZED). This means that you must be stopped at a managed callback, or have called ICorDebugProcess::Stop().
  3. The debugger has to "undo" any stuff it did, such as:
    • adding func-evals (CORDBG_E_DETACH_FAILED_OUTSTANDING_EVALS )
    • cancel outstanding  steppers (CORDBG_E_DETACH_FAILED_OUTSTANDING_STEPPERS).
    • remove outstanding breakpoints (CORDBG_E_DETACH_FAILED_OUTSTANDING_BREAKPOINTS).
  4. The event queue should be drained.

Why?
A lot of this complication is a hold-over from our "ICorDebug is a rocket science API" days. In Whidbey (V2), we tried to make the API friendlier. We actually added almost all of the hresults above for Whidbey.

Some of these restrictions are overly conservative. For example, ICorDebug could remove all steppers and breakpoints itself. Or it could even keep them in and just ignore them if no debugger was attached.
However, some have decent reasons. Dealing with outstanding func-evals is more complex. There's not a clear correct behavior if you try to detach while in a nested-break state. Also, we wanted the debugger still attached to be available to restore the thread at the func-eval completion. Detaching during EnC is also problematic because the debugger (not ICorDebug) has a lot of responsibility for keeping an EnCed program running (eg, by handling remaps). 

What if you rudely kill the debugger?
By default, if you rudely kill the debugger, all it's debuggees get killed too. We call the scenario of killing off the debugger in an uncooperative fashion "Rude-Detach". It was definitely not supported in V1.1. For V2, a CLR Host can respond to Rude-detach.