The uncatchable exception, pt 2
Asked Answered
M

2

22

Update: I've filed a bug report on Microsoft Connect: https://connect.microsoft.com/VisualStudio/feedback/details/568271/debugger-halting-on-exception-thrown-inside-methodinfo-invoke#details

If you can reproduce this problem on your machine, please upvote the bug so it can be fixed!


Ok I've done some testing and I've reduced the problem to something very simple:

i. Create a method in a new class that throws an exception:

public class Class1 {
    public void CallMe() {
        string blah = null;
        blah.ToLower();
    }
}

ii. Create a MethodInfo that points to this method somewhere else:

Type class1 = typeof( Class1 );
Class1 obj = new Class1();
MethodInfo method = class1.GetMethod( "CallMe" );

iii. Wrap a call to Invoke() in a try/catch block:

try {
    method.Invoke( obj, null ); // exception is not being caught!
} catch {
}

iv. Run the program without the debugger (works fine).

v. Now run the program with the debugger. The debugger will halt the program when the exception occurs, even though it's wrapped in a catch handler that tries to ignore it. (Even if you put a breakpoint in the catch block it will halt before it reaches it!)

In fact, the exception is happening when you run it without the debugger too. In a simple test project it's getting ignored at some other level, but if your app has any kind of global exception handling, it will get triggered there as well. [see comments]

This is causing me a real headache because it keeps triggering my app's crash-handler, not to mention the pain it is to attempt to debug.

Masakomasan answered 16/6, 2010 at 23:6 Comment(9)
+1 cause you took the time to reduce this into a sane example.Equalizer
See here: #2725203Footlocker
Have you enabled the exception as 'stop when throw' in Visual studio? to Stop this behaviour, go to Debug| Exceptions and uncheck stop on throw.Sloth
AFAIK, debugger catches everything and doesn't care if it is handled or not. Try to make a short verification code to show that the exception is not caught, which would not involve using a debugger.Institutionalize
I did some more experimenting and it's only occurring with .NET 4.0. Should I report this as a bug?Masakomasan
@Dan, I saw that, but it doesn't help in my case. @Institutionalize I will work on that. @Preet: stop when throw is not enabled. I checked.Masakomasan
No repro, works fine on .NET 4.0 RTM on my machine. As it should.Pacificas
Also works fine with my .NET 4.0 RTM, w/ and w/o debugger.Moriahmoriarty
@John I deleted my comment, the problem was internal! somehow, the application's startup page was changed on the combo @ project settings, it should be set blank and that's the problem, that it always attempts to set it with the first value, which was a window subclass that has only parameterized public constructors, so it thew the exception you see here, but justified.Plage
W
5

I can reproduce this on my .NET 4 box, and you're right -- it only happens on .NET 4.0.

This smells very much like a bug to me, and should go on MS Connect. Major bummer if this is tripping your crash handler. Sounds like a non-pleasing way to work around this is to wrap the invoked method inside its own handler. :-(

One thing I can not reproduce, though, is tripping the crash handler. Here's my program:

namespace trash {
    public class Class1 {
        public void CallMe() {
            string blah = null;
            blah.ToLower();
        }
    }

    class Program {
        static void Main(string[] args) {
            AppDomain.CurrentDomain.UnhandledException += new UnhandledExceptionEventHandler(CurrentDomain_UnhandledException);           
            var class1 = typeof(Class1);
            var method = class1.GetMethod("CallMe");

            try {
                var obj = new Class1();
                method.Invoke(obj, null); // exception is not being caught!
            }
            catch (System.Reflection.TargetInvocationException) {
                Console.Write("what you would expect");
            }

        }

        static void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e) {
            Console.Write("it would be horrible if this got tripped but it doesn't!");
        }
    }
}
Warsle answered 16/6, 2010 at 23:35 Comment(4)
I haven't yet been able to reproduce the crash handler tripping in a test app either, however I'm working on it. For some reason my real app is dying pretty hard and the exception (which I print to a log file) looks very similar.Masakomasan
I did however discover something else interesting: if you bind to a property in WPF that throws an exception, the debugger will halt there too when you run it--you don't even need to call MethodInfo.Invoke at all ! The crash handler tripping when not in debug mode seems to be false, as it turns out it was a different problem causing that--meaning, this could be a VS2010 bug.Masakomasan
This answer is the same result I get. Basically there is a catch-all somewhere inside Invoke, something I think they seriously regret now, but it causes no end of headaches. NB. I believe exactly when the UnhandledException handler will execute depends on the version of Windows! So you may get different results on XP/Vista/7. My testing is on 7 and the UnhandledException event never fires, but it does halt in the debugger.Higgledypiggledy
This works for me as expected: only the "What you would expect" is printed. .NET 4.0, 32-bit. Perhaps they fixed it in one of the security updates they release from time to time...Galloon
P
-1

You can't catch all exceptions. There's a few assumptions in your example. You are, for instance, assuming the exception was raised on the calling thread. Catching unhandled exceptions on other threads depends on which runtimes you're using (console, winforms, WPF, ASP.Net, etc).

Additionally, calls to System.Environment.FailFast() do not generate any handlable condition - the process is effectively terminated with no chance for intervention.

Portecochere answered 16/6, 2010 at 23:34 Comment(5)
He's calling Invoke(), not BeginInvoke(), so the call happens on the current thread. There is some external code that gets in between the call when the invocation is done, though.Warsle
@Dave, but that does not guarantee that the invoked method in question does not cause another thread to be created. My assertion above holds.Portecochere
@Dave, by that, I mean, the method called by Invoke, could in turn create a thread (directly, or indirectly), I don't mean that Invoke, itself, will arbitrarily create another thread.Portecochere
It could also torrent the entire Lady Gaga anthology in the background on each call, but I don't see a reason why it would do that either.Warsle
@Dave, but there is zero guarantee that it doesn't. I personally live in a world where clients invoking my libs cause me to spawn external threads, and while I do my damnedest to handle errors properly on that thread, there are occasions when I cannot. And I'm faced with 2 possiblilites: ignoring the error and providing bad data, or failing and potentially allowing the client to respond. Regardless, I cannot and do not fail silently.Portecochere

© 2022 - 2024 — McMap. All rights reserved.