From the mail bag:

Is there a way to determine if a managed debugger is already attached to a given process id?

It looks like ICorDebug.CanLaunchOrAttach might be the right API for this (wouldn't it return FALSE if another debugger is already attached?). However, it seems like this API always returns TRUE. I searched your blog at http://blogs.msdn.com/jmstall/archive/category/7367.aspx but didn't find something specific to this. Any insight would be appreciated.

I've had this a few times, and so I wanted to blog the answer.

In short: No. Process X can't tell if a managed-only debugger is attached to process Y. Here are related APIs:

API: What it does
kernel32!IsDebuggerPresent Tells you if an native debugger is attached to the current process. This will also return true when interop-debugging.
kernel32!CheckRemoteDebuggerPresent Tells you if an native debugger is attached to an arbitrary process. This will also return true when interop-debugging.
mscorlib!System.Diagnostics.Debugger.IsAttached Tells you if a managed debugger is attached to the current process.

What about ICorDebug::CanLaunchOrAttach?
Note that ICorDebug::CanLaunchOrAttach() is distinct from the above APIs. It gives a prediction about whether the ICorDebug instance it's called on can handle a future managed launch / attached. This prediction is not necessarily accurate. (I personally think that API is useless and we should never have shipped it.). This prediction originally existed because we build the managed eventing pipeline on top of WaitForMultipleObjects, where each process we're debugging gets a object in the wait set (an event handle to listen for debug events from the debuggee). The OS limits the wait set to MAXIMUM_WAIT_OBJECTS (64) objects. Thus a single ICorDebug instance can't handle more than 64 debug connections. That hard number is entirely implementation dependent. For example, if each debuggee requires two objects in the wait set, the number drops to 32.And there is also a fixed set of overhead events in the wait set. This is just a very silly thing to bake into our API.
It could also serve as a canary to sniff out if a kernel debugger is attached.

Recall that managed-only debugging is technically not native win32 debugging. This is sort of an implementation detail but certainly has implications that can be discoverable at the public interface level. We reserve the right to change this in future versions of the CLR.