Beware of the Vectored Exception Handler and managed code


A vectored exception handler (see kernel32!AddVectoredExceptionHandler) lets you add to a global list of filters that get executed by the OS when a native exception is thrown. More specifically, this list is executed by the OS before the filters in the FS:0 chain are executed. This means you can get your exception handler to run even if you don’t have any sort of try/catch block. Matt Pietrek has a great explanation and demo here.


Sounds cool? But with great power comes great responsibility. This is a very dangerous thing, especially in managed code.


My advice is to just play it safe and avoid vectored exception handlers in managed apps.


1. The vectored exception handler can interfere with CLR exception processing. For example, if the VEH swallows the exception.
2. The VEH can break Interop-debugging (mixed-mode) and cause it to hang. Interop-debugging does very evil things that don’t play well with a VEH. For example, if the VEH generates any debug events (such as an int3), that can cause great pain in the debugger. Of if the VEH swallows an exception, it will prevent CLR’s FS:0 handlers from getting it and doing critical work for interop-debugging.
3. Since the VEH occurs at any native exception, including ones thrown from the middle of the CLR, a vectored exception handler can cause reentrancy problems. For example, if  your VEH calls managed code, then you may be executing managed code at a point where the CLR is not prepared for it. I think we have a reentrancy MDA to help detect things like this.
(I’m sure there are other reasons I’m missing, don’t be surprised to see this list grow)


If you follow enough constraints, it’s possible to write a “safe” VEH – but imo, why tempt fate?


Exposing from managed code?
Brad details how we almost added managed support for this via an ExceptionThrown event on the appdomain. That’s very scary and would have caused lots of problems. For example, different customers would have different needs and it would be difficult to satisfy them all:
– what exceptions get notified? Native ones? Managed ones? 
– what about CLR-internal ones? Eg, if inside the CLR, an exception is thrown and caught, should it show up? (It will with the VEH).
– what about Hardware vs. Software ones?  Should an STATUS_BREAKPOINT show up?
– What about hardware exceptions (like Access Violations) that get wrapped by managed exceptions? At the native 1st-chance notification, you may not have a managed exception object. But at the managed notification, it may be too late.
– what about knowing when exceptions are caught?
I think the VEH is dangerous enough, that exposing something like it from managed code would have caused a lot of grief.


Alternatives?
If you absolutely need some sort of VEH functionality, consider:
1. One alternative is to have an out-of-process debugger harness sniff for exceptions. (see Mdbg exception harness for an example). That’s slower, but a lot safer. And you also get more information about the managed exception, such as what type it is and where it’s caught on the stack.
2. The profiler API also has exception notifications.
3. IL filters .

Comments (6)

  1. Jeff Stong says:

    Mike Stall points out several reasons why you shouldn’t make use of the AddVectoredExceptionHandler API…

  2. dimkaz says:

    Wow, I didn’t realize until now that ExceptionThrown has been removed!

    That’s a pity it was such a convinient place to put all exception reporting logic. It doesn’t have to have all the complexities and ability of VEH. It could be called just before the catch/filter handler, where CLR is already setup to handle the recursion, nested exceptions etc.

  3. Ayende Rahien says:

    Mike,

    The problem is that there is no way now to find out if an exception has been thrown.

    For instnace, transactions:

    using(new transaction())

    {

    }

    If transaction could register for the app domain exception, it could handle it all transperantely.

    There are many other reasons why I would want to know that an exception has been raised even if I can’t put a catch block there.

  4. Fduch says:

    Where can I find answers to why debugger in VS2005 is so much slower than in VS2003 or how to speed it up.

    (I’m talking about running application in Release mode via F5)

  5. Interop-debugging (mixed-mode) is managed + native debugging combined. Well, sort of. Native and managed