Managed vs. Native debugging APIs

FxCop has a great rule (UseManagedEquivalentsOfWin32Api) to tell you about managed APIs that exist instead of trying to pinvoke out. 

I’m writing a native debugger in managed code (more on this later), and FxCop was telling me to use the managed debugging APIs instead of pinvoke out to the native ones. This came up in the fxcop forums. This raises an interesting point about the difference between some of the managed vs. native debugging APIs. Managed and Native debugging are (as of .NET 2.0) different services and so have different APIs with subtle distinctions.

Now, odds are that if you’re running managed code (which you must be to do a pinvoke in the first place), then you care about managed-debugging and actually want to be calling the managed versions. In that case, Debugger.IsAttached(), Debugger.Break(), Debugger.Log() are really the one you care about. However, these APIs are still significantly different from their native counterparts. So if you are writing a native-debugger in managed code and really intend to pinvoke out to the native debugging APIs, you can’t just replace them with the managed counterparts.

DebugActiveProcess != System.Diagnostics.Debugger.Launch
These ones are totally different.
System.Diagnostics.Debugger.Launch will (i) launch a (ii) managed debugger (using registry settings to find it), with the intent of attaching it to the (iii) current process.
kernel32!DebugActiveProcess will tell the current process to start (i) attach as a (ii) native debugger to the (iii) specified process (not the current process).

DebugBreak != System.Diagnostics.Debugger.Break
DebugBreak injects a (i) native breakpoint exception (eg, int3 on x86). If no native debugger is attached, it will use (ii) normal SEH processing, which may trigger an unhandled native exception and native jit-attach.
Debugger.Break injects a (ii) managed stop (UserBreak) and if no managed debugger is attached, it will (ii) trigger a jit-attach for a managed debugger.

IsDebuggerPresent != System.Diagnostics.Debugger.IsAttached
See here for more details. In short, kernel32!IsDebuggerPresent tells you if a (i) native debugger is attached (which includes interop-debugging), whereas Debugger.IsAttached tells you if a (i) managed debugger is attached. As of .Net 2.0, this is a significant distinction. This may change in the future (eg, if we built managed debugging on top of native debugging).

So for your standard C# debugging experience, IsDebuggerPresent() will return false while Debugger.IsAttached() will return true.

However, both of these APIs are evil because debuggers aren’t supposed to change behavior, so please don’t call them unless you really know what you’re doing.

OutputDebugString =? System.Diagnostics.Debugger.Log
These are the closest in relationship. See here for more details. If you’re writing managed code, you probably want to call Debugger.Log() instead of pinvoke out to OutputDebugString.


1. The manage and native debugging APIs are functionally very different.
2. However, if you’re writing in managed code, you probably intend to use the managed methods on Debugger (especially Debugger.Log and Debugger.Break)
3. If you are indeed writing a native debugger in managed code, then the differences between the method sets are important.


Comments (6)

  1. barrkel says:

    On the topic of debuggers not changing behaviour, is there any chance of a change on the WinForms debugging front any time soon?

    Currently, debugging a managed app using WinForms causes gunk to be inserted into the message handling call stack, which changes how exceptions get propagated from event handlers. Caused me no end of grief. I had to rearchitect the UI exception handling in order to make the app sanely debuggable.

  2. Sameer V. says:

    Hi Mike,

    wow, you are working on native debugger on managed code. I hope you will be sharing your experiences with us!!

    I’ve been trying to do this for sometime. but as I’m not a C++ guy. my PInvoke signatures were never right.

    Just out of curiocity r u using Kernel32 API or Dbgeng.dll

    How about 64bit?

    Good luck


  3. jmstall says:

    Barrkel – that problem with winforms is notorious and a text book example of the "No changing behavior" rule. I’d like to see it become an MDA instead. I don’t know when it will be fixed.

  4. I’m writing some managed wrappers for the native-debugging API (I expect they’ll eventually become part…

  5. I received some questions in the mailbag about what Debugger.Launch actually does. Debugger.Launch the…

  6. System.Diagnostics.Debugger.Break() is a BCL method that causes a program to issue a User Breakpoint