17

I have a simple try-catch-finally code block that works as expected in .NET3.5, but the same code behaves completely different on a project created with .NET4.5.1. Basically, in .NET4.5.1 the "finally" block doesn't get hit if an exception occurs which is not the behavior I expected from the try-catch-finally block. I tried in different machines, and had 2 other colleagues of mine also trying and we all got the same result. This is a concern for me, because I use the finally block to close DataReaders, certain connections, and whatnot.

.NET4.5.1 does not hit the "finally" block if an exception is thrown in RELEASE mode without debugger or when running the RELEASE compiled EXE file. In debug mode both .NET versions hit the "finally" block.

Again, the code below behaves as expected in .NET3.5 RELEASE mode without debugger but not in .NET4.5.1. Am I missing something? Can someone help?

class Program
{
    static void Main(string[] args)
    {
        try
        {
            string a = null;
            var x = a.Length;
            Console.WriteLine(x);
        }
        catch (Exception ex)
        {
            throw;
        }
        finally
        {
            Console.WriteLine("This is the finally block.");
        }
        Console.WriteLine("You should not be here if an exception occured!");
    }
}
MarkJ
  • 353
  • 1
  • 9
  • @GrantWinney the OP is obviously trying to fix the underlying issue. A `Console.WriteLine` was added to use the variable that was not used in the "duplicate". – Mike Zboray Jun 16 '15 at 22:54
  • Maybe edit the title of the question to be more descriptive (e.g. “Finally block not executed in .NET 4.5.1”) – Arturo Torres Sánchez Jun 16 '15 at 22:55
  • @GrantWinney Yea but that change now invalidates the highly upvoted answer over there. – Mike Zboray Jun 16 '15 at 22:55
  • 2
    @GrantWinney. He had two questions in that question. The first was resolved the second wasn't. He decided to create a separate question like he should of done at first. I think this question is valid if he edits this part from the original question – Mark Hall Jun 16 '15 at 22:57
  • 1
    Thanks. My original post had 2 questions. Only one question got replied to. I thought it would be easier to go ahead give the credit to the best answer, edit the original post mentioning that the second question would be moved into a new thread. My mistake for asking 2 questions on the same thread. – MarkJ Jun 16 '15 at 22:58
  • I actually see the finally block run in both. The thing that I notice that is different is that on 4.5.1 I can see the unhandled exception in the console before the "application has stopped working" dialog. After closing the dialog, the finally block runs. In 3.5 they both come after closing the dialog. – Mike Zboray Jun 16 '15 at 23:01
  • @mikez Looks like it's a difference when the .NET Runtime is executing the finally – cost Jun 16 '15 at 23:06
  • @mike z, yes in debug mode they both hit the finally block (although differently), but in RELEASE mode without debugger, or the compiled Release EXE file is where the big discrepancy between the 2 versions of .NET is at (sorry I need to edit my post as I didn't specify that at all. – MarkJ Jun 16 '15 at 23:12
  • 1
    @MarkJ I did not see a difference between release/debug w/o debugger attached. The finally block always ran for me. – Mike Zboray Jun 16 '15 at 23:14
  • @Markj Are you using Visual Studio 2013 or Visual Studio 2015 to build your program? – Scott Chamberlain Jun 16 '15 at 23:28
  • @mike z it still doesn't do it for me. I even replaced the "Console.WriteLine("This is the finally block"); with File.Create(@"c:\Temp\temp.txt"); to see if the file gets created and no file gets created when it runs. I tried the .Net3.5 again and it shows the message and it creates the file. this is very strange.... don't think security settings can have anything to do with it... I would get the same issue with 3.5. any ideas? – MarkJ Jun 16 '15 at 23:30
  • @ScottChamberlain i'm using Visual Studio 2013. – MarkJ Jun 16 '15 at 23:32
  • I can confirm MarkJ's behavior. [This is the output I get](https://gist.github.com/anonymous/8591e774735d37616f4d), no text written. – Scott Chamberlain Jun 16 '15 at 23:34
  • @ScottChamberlain What are you doing differently? Mine works fine. – DavidG Jun 16 '15 at 23:35
  • [Here you can see](http://imgur.com/p1Ed5C2) it working for me and that I was using the release version without debugger. Also, the project is set to use .Net 4.5.1. – DavidG Jun 16 '15 at 23:37
  • @DavidG I get the same as you, but it's strange the the exception prints before the finally text.. – Blorgbeard Jun 16 '15 at 23:38
  • @DavidG Ok, super weird. I changed a few project settings and now the build I had in `D:\bin` is behaving differently and is showing the text. I did not replace the compiled exe. Some sort of cahced behavior is affecting the .NET framework – Scott Chamberlain Jun 16 '15 at 23:39
  • @ScottChamberlain that's exactly what I get. any code in the finally block is completely bypassed. in .NET3.5 I get all that is expected on the same machine using the same Visual studio 2013. only difference is the target .NET version. – MarkJ Jun 16 '15 at 23:39
  • @DavidG your screen shot is what I would expect and I would like to see, but I don't get that. I get just the exception followed by the "Press any key...". but on .net3.5 I get the "finally" comment. – MarkJ Jun 16 '15 at 23:45
  • @MarkJ Why is it asking you to press any key? The code you posted doesn't have anything like that in it. – DavidG Jun 16 '15 at 23:47
  • @DavidG he probably runs with Ctrl-F5 from visual studio – Yishai Galatzer Jun 17 '15 at 00:01
  • @DavidG when I run in RELEASE mode without debugging I get the console showing the unhandled exception message, and then a message box saying ConsoleApplication has stopped working. the message box has 2 buttons "Debug" and "Close Program". When I hit the "close program" the press any key appears which will exit the console. Exactly the same as if I would run the ConsoleApplication.exe file. No different than when I run the .net3.5 version, except that in .net3.5 the console shows the finally block message above the press any key. – MarkJ Jun 17 '15 at 00:01

3 Answers3

20

the code below behaves as expected in .NET3.5 RELEASE mode without debugger but not in .NET4.5.1. Am I missing something?

NOTE: I had overstated the level of undefined-ness of this behaviour; thanks to commenter Voo for pointing that out. I should have gone back to the spec in the first place.

Yes. The CLR is required by the CLI specification to end the program when there is an unhandled exception. It is only required to run finally blocks if the exception is handled. The spec is vague on the question of whether the CLR is required, permitted, or disallowed to execute finally blocks when there is an unhandled exception; the safe assumption is then to say that this is behaviour that is undefined by the specification, and that is up to a particular implementation.

The CLR can choose to run finally blocks for unhandled exceptions, or not, at its whim. Many people believe that the CLR uses this algorithm: upon exception, walk up the call stack, executing finally blocks as you go, looking for handlers; if no handler is found, terminate the process. The CLR is not required to conform to this algorithm in a program with an unhandled exception. In particular, the CLR is permitted to determine by black magic that there is no exception handler, and never run any finally blocks. Whether it chooses to do so or not in some versions of the CLR in some circumstances, I don't know. In no case can you rely on that behavior for the correctness of your program because a program that has an unhandled exception is not correct.

The specification also notes that the CLR can choose to offer to start debuggers or not, at its whim. The CLR is not required to do the same thing in debug or release, and it is not required to do the same thing from version to version.

The problem here is that you formed an expectation based on past experience, but there is no documentation which says that past experience is a basis for a prediction of the future. Rather, just the opposite; the CLR is permitted to change its behavior on the basis of the phase of the moon if it likes, in a program that has an unhandled exception.

If you want your program to behave predictably then do not throw unhandled exceptions.

So if I understand you correctly, as long as there is another catch somewhere upstream, the finally block will execute?

No, I didn't say that. Let's break it down.

If there is an uncaught exception in the program then the program's behavior is implementation-defined. Whatever behavior you get, that's the behavior you got, and the CLR is within its rights to produce that behavior. That includes both running finally blocks and not running finally blocks.

Suppose there is not an uncaught exception, and an exception is thrown, and there is a finally block along the way to the catch. Is it guaranteed that the finally block will execute? No. There are many things that could prevent that finally block from executing in a legal program. For example, another finally block or exception filter along the way could go into an infinite loop or fast fail, either of which would prevent the finally block from executing. If you ABSOLUTELY POSITIVELY must have some cleanup code run then you need to be researching Constrained Execution Regions. (I don't know how they work; I've never had need to learn. I hear they are tricky.).

What is guaranteed is that if control leaves a finally-protected block then the finally code will run. Code run during exception filters does not count as leaving the block, and failing fast does not cause program control to exit a block, it causes program control to end abruptly. Obviously infinite loops cause control to never exit a block.

I suppose in the case of a truly unhandled exception, the program should terminate anyways so an orphaned DB connection/transaction shouldn't be an issue?

Whether it is an issue or not, I cannot say. Ask the author of your database.

It is very likely that the program will terminate, though again I note that the CLR is not required to have that behavior. Suppose for example there is some thread that keeps on running while the CLR is trying to figure out whether you have a debugger installed or not. The CLR is within its rights to take arbitrarily long to figure that out, and therefore within its rights to keep that thread running. Whether it does or not, I don't know. What I do know is that I would not want to rely on either behavior.

Also, does using the 'AppDomain.CurrentDomain.UnhandledException event count as 'handling'

Nope. If that thing runs then there was an unhandled exception, and the behavior of the program is implementation-defined. That event handler should be used only to do things like log the fact that the program has a bug.

Eric Lippert
  • 647,829
  • 179
  • 1,238
  • 2,067
  • So if I understand you correctly, as long as there is another catch somewhere upstream, the finally block will execute? I suppose in the case of a truly unhandled exception, the program should terminate anyways so an orphaned DB connection/transaction shouldn't be an issue? Also, does using the `'AppDomain.CurrentDomain.UnhandledException` event count as 'handling'.? – Kratz Jun 17 '15 at 13:51
  • @Kratz: Good questions. I'll post some follow-ups in my answer. – Eric Lippert Jun 17 '15 at 16:43
  • The OP's related question is here http://stackoverflow.com/a/30878412/141172. One of the comments in my answer questions why the compiler potentially throwing an exception is not considered a side effect. I wonder if you could add anything beyond the basic reasoning I provided in response. – Eric J. Jun 17 '15 at 18:43
  • 1
    You’re right, I did form an expectation based on my past experience with .NET but also based on the fact that Microsoft claims that a finally block is always executed regardless of whether an exception is thrown, although they also claim the exact opposite in other occasions. Contradiction at its best! What I don’t understand is why even bother adding the finally block feature in the .NET framework in the first place if there is no guarantee that it’ll work? Anyways, to me a solid programming language shouldn't leave anything up to interpretation. – MarkJ Jun 17 '15 at 21:47
  • 3
    So an unhandled exception really results in undefined behavior and not implementation defined? I know that you know the difference so I'm sure you picked your words carefully, but I find this very, very surprising considering the language's stance on trying to avoid undefined (and implementation defined to a lesser degree) behavior. Now I'd think that contrary to say C++ optimizing compilers the runtime will not try to exploit this detail for some minor performance gains, but just to have more options on how to best show an error to the user, but still. – Voo Jun 18 '15 at 14:24
  • @Voo: That is a very good point which I had not considered. The CLI spec actually says *"If a match is found, the CLI walks the stack back to the point just located, but this time calling the finally and fault handlers."* and *"If no match is found the CLI will dump a stack trace and abort the program. Note: A debugger can intervene and treat this situation like a breakpoint..."* so it does not come right out and say that this is undefined behaviour; rather, it implies that the program must end, and is vague at best on the subject of whether finally blocks run. – Eric Lippert Jun 18 '15 at 16:29
  • @EricLippert So I take it that if the CLI specification does not explicitly denote some behavior as undefined it is implementation defined contrary to say C++ where it'd be undefined? Makes sense for the CLI considering the different goals. Still I'm wondering - in your opinion was this just an oversight (hey it happens!) or was it a conscious decision to not require finally blocks to run in the case of uncaught exceptions? Seems to go against "least surprise for programmers" and I don't really see many advantages for it (some SEH details possibly?) – Voo Jun 18 '15 at 17:36
  • @Voo: I don't know; that's an interesting question. The people to ask that question would be Chris Brumme, who designed the thing, and Jim Miller, who wrote the specification. Next time I see either of them, I'll ask. – Eric Lippert Jun 18 '15 at 19:32
4

On top of what Lipper wrote, note that it is written in MSDN... Under the try...finally:

However, if the exception is unhandled, execution of the finally block is dependent on how the exception unwind operation is triggered. That, in turn, is dependent on how your computer is set up.

and

Usually, when an unhandled exception ends an application, whether or not the finally block is run is not important.

and then it goes on to explain that if you put a try... catch at a "high" level then the inner try... finally will be executed.

xanatos
  • 109,618
  • 12
  • 197
  • 280
  • `try { FlyToLondon() } finally { LandTheAirplaneAtNearestAirport() }` is an example of a finally block that better run. The whole *your program better catch exceptions if you need finally blocks to run* is an important concept, especially because it will not matter 99% of the time making that 1% all the more of an issue. – Eric J. Jun 17 '15 at 18:51
3

Prior to Framework 4.0 unhandled exceptions launched 'Microsoft .NET Error Reporting Shim', which shows the dialog offering to 'Debug' or 'Close program'. The shim allows .NET applications to close "cleanly".

Starting with Framework 4.0 (as far as I can tell) unhandled exceptions result in Windows launching Windows Error Reporting (WER) which shows-up as Windows Problem Reporting in Task Manager. This application shows a similar dialog to the shim but takes a more hardline approach to killing the application, probably calling TerminateProcess or TerminateThread which would not allow any further code to execute in the misbehaving process.

Chris Taylor
  • 52,623
  • 10
  • 78
  • 89
  • interesting.... but what does it mean for me? I'm re-writing an application that I originally developed in .net3.5 many years ago. Should I be concerned about the "finally" block not being hit? Is this issue only happening in a console application? In my application i'll be dealing with multiple libraries WCF calls, and Windows Service calls. I'm a bit concerned if I can't completely rely on the "finally" block when I coded it. – MarkJ Jun 17 '15 at 00:16
  • One line of thought is that once the application has experienced an unhandled exception things have gone wrong and it is probably safer to not run anymore code that might do more damage. You could install a global exception handler 'AppDomain.CurrentDomain.UnhandledException += new UnhandledExceptionEventHandler( (s, e) => { Console.WriteLine("Oops"); });' that can do some clean-up etc. other resources like DB connections, network connection will be reclaimed when the process dies. – Chris Taylor Jun 17 '15 at 00:23