Filters + Finallys are not executed after unhandled exceptions when Native/Interop debugging!

I said earlier that Filters + Finallys are not executed after unhandled exceptions when Native / Interop debugging.  I wanted to elaborate on that statement here:


The symptoms:

First, I want to clarify exactly what that means.  Take the following trivial app that throws an unhandled exception:

using System;


class Class1


      static void Main(string[] args)





            throw new Exception("Blarg!"); // <-- this will be unhandled




            Console.WriteLine("*** Inside the finally ****!");                





Just compile it and run it from the command line, and it prints:



Unhandled Exception: System.Exception: Blarg!

   at Class1.Main(String[] args) in c:\dev\ev\finally\finally\class1.cs:line 10


*** Inside the finally ****!


Notice that the finally was executed, exactly as you'd expect. You’ll get the same output if you run it under a managed debugger.


Now run it under an native / interop debugger, and the output is just:



The unhandled exception was still reported in both cases, but the finally was not executed.  


This also affects managed filters. However, C# only has finallys (at least as of v2.0), not filters, so they become much less interesting. Filters are available in VB.Net (via “catch when”) and in ILasm. Thus I’ll keep referring to just finallys.


This is clearly a case of the debugger influencing program execution. We only compiled the test app once, and it was the exact same test app in all cases, and the only thing that changed was the debugger. Good thing the finally didn’t do anything important like release some external resource!


What’s happening?

The CLR implements its exception handling on top on Window’s native Structured-Exception-Handling (SEH) model. So the CLR filters / finallys are really built on top of the SEH filters. (You can learn all about about SEH in Matt Pietrek’s excellent article:  ) That means that if the SEH filters don’t run, then the CLR filters don’t run.


So why aren’t the SEH filters running when a native / interop debugger is attached?  


It turns out the answer is hidden in the inocent looking documentation of the OS’s native-debugging API under kernel32!ContinueDebugEvent.


If the DBG_EXCEPTION_NOT_HANDLED flag is specified for this parameter and the thread specified by dwThreadId previously reported an EXCEPTION_DEBUG_EVENT debugging event, the function continues exception processing. If this is a first-chance exception event, the search and dispatch logic of the structured exception handler is used; otherwise, the process is terminated.


I’ve hilighted the part in interest. I’d translate that as: “otherwise, when continuining from an unhandled exception event while under a native debugger, the debuggee is terminated.” And if the OS is determined to terminate the process, there’s little the CLR or ICorDebug can do to stop it.


So how can they get away with this?

This is a blatant example of the debugger changing the debuggee’s behavior. Finallys are everywhere in managed code. They’re even intergrated into languages. For example, C#’s “using” statement for IDisposable is built on finallys. Where’s the outrage?


There are several mitigating factors:

-         This only happens when native / interop debugging. It has no impact when not under a debugger, and Managed-only debugging is safe too (since the OS doesn’t recognize if an app is being managed-only debugged)

-         You don’t normally expect unhandled exceptions. In fact, many frameworks (such as winforms) have a top-level catcher that will avoid unhandled exceptions completely.

-         If you do happen to get an unhandled exception, most people call it quits and stop paying attention to anything after that.

-         A debugger can try to hide this at the application level. For example, Visual Studio tries to not let you continue from unhandled exceptions so that you don’t trip over this problem. However this causes other problems since the debugger is still impeding the natural execution of the debuggee.

-         It’s an unhandled exception so the process is exiting anyways.


That aside, this is still pretty lame. It means it’s near impossible to actually debug your unhandled exception filters (finallys). You’re basically reduced to having your finally pop up a dialog and then attach the debugger to it then.


It also causes us on the CLR Debugger team a lot of grief because it introduces a significant difference between interop-debugging vs. managed-only debugging.



Comments (6)

  1. Mark Pearce says:

    Managed to repro this – many thanks for the added detail. I also managed to repro this in a WinForms app providing I avoided throwing in the window message pump.

    But if you test this in, say, a button click event, it doesn’t repro – the finally block always executes regardless of the debugger type. I’m not quite sure why – will continue experimenting!


  2. Mark Pearce says:

    Okay – missed your point that WinForms framework has a top-level handler that kills unhandled exceptions.

    But the reason I missed it was that my code attempts to override the WinForms handler by registering for the event System.Threading.ThreadExceptionEventHandler.

    And that event wasn’t being raised, which made me think the WinForms handler wasn’t kicking-in.

    So there must be some circumstances in which the WinForms handler kicks in regardless of whether the aforementioned event is triggered. Maybe this is a different mechanism altogether.

    What I can say is that the CLR’s unhandled exception story is rather a mess!


  3. Hi Mike, great article! Would like to point out my own article on Rotor/SEH at this also has a link to Chris Brummies article on SEH that is also well worth a read.

  4. Mike Stall says:

    Andrew – thanks for the links.

    I’d very highly recommend any article by Chris Brumme. His SEH article is here:

Skip to main content