Unwound Stack Puzzle on Dynamic Call

This is a new attempt to present a version of the question asked less successfully this morning.

Consider the following program, which we will run once inside Visual Studio 2010 and again by double clicking on the executable itself

namespace ConsoleApplication3
{
    delegate void myFoo(int i, string s);

    class Program
    {
        static void Main(string[] args)
        {
            Foo(1, "hello");
            Delegate Food = (myFoo)Foo;
            Food.DynamicInvoke(new object[] { 2, null });
        }

        static void Foo(int i, string s)
        {
            Console.WriteLine("If the next line triggers an exception, the stack will be unwound up to the .Invoke");
            Console.WriteLine("i=" + i + ", s.Length = " + s.Length);
        }
    }
}

      

When an exception in Foo is thrown when VS starts up, the debugger shows the stack correctly and shows that the problem occurred in the second WriteLine in Foo.

But when an exception is thrown when running the executable directly, a small popup appears in the CLR indicating that the program has thrown an unhandled exception. Click Debug and select VS Debugger. In this case, the stack is unwound to the most recent .DynamicInvoke, and when you attach to the debugger, the stack context that existed at the time of the exception was partially lost.

It exists in limited form within the "inner exception" part of the exception event. You click to expand the related information and find the line number where the problem occurred. But obviously local variables and other context will disappear.

If you're trying to do the same, but without .DynamicInvoke (for example, call Foo (1, null) on line 1 of Main) while still double-clicking the .exe file, we'll get the correct line number when the debugger attaches. Likewise, if the application is started by clicking on the .exe, but then the debugger is connected before the exception is thrown.

Does anyone know how an application using dynamic reflection / invocation could avoid this problem? In my intended use case on a system whose name I will not mention here, I cannot predict the type signature of the object to be used in .DynamicInvoke, or even the number of arguments to be used, hence static typing or even generics are not a way out this.

My question is, does anyone know why we get this behavior when run directly from the debugger versus app to program after an exception?

+1


source to share


2 answers


As per the comments, whether you see NullReferenceException

how unprocessed depends on whether it is being processed. There are several ways to call Foo

, the first three will leave the exception unhandled, the last two will be handled NullReferenceException

by wrapping it and throwing a new exception.



using System;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;

namespace ConsoleApplication3
{
    delegate void myFoo(int i, string s);

    internal class Program
    {
        private static void Main(string[] args)
        {
            Foo(1, "hello");

            // From a delegate
            try
            {
                Delegate Food = (myFoo)Foo;
                ((dynamic)Food).Invoke(2, null);
            }
            catch (NullReferenceException ex)
            { Console.WriteLine("Caught NullReferenceException at " + ex.StackTrace); }

            MethodInfo Foom = typeof(Program).GetMethod("Foo", BindingFlags.Static | BindingFlags.NonPublic);

            // From a MethodInfo, obtaining a delegate from it
            try
            {
                Delegate Food = Delegate.CreateDelegate(typeof(Action<,>).MakeGenericType(Foom.GetParameters().Select(p => p.ParameterType).ToArray()), Foom);
                ((dynamic)Food).Invoke(2, null);
            }
            catch (NullReferenceException ex)
            { Console.WriteLine("Caught NullReferenceException at " + ex.StackTrace); }

            // From a MethodInfo, creating a plain Action
            try
            {
                Expression.Lambda<Action>(
                    Expression.Call(
                        Foom,
                        Expression.Constant(2),
                        Expression.Constant(null, typeof(string)))).Compile()();
            }
            catch (NullReferenceException ex)
            { Console.WriteLine("Caught NullReferenceException at " + ex.StackTrace); }

            // MethodBase.Invoke, exception gets wrapped
            try
            {
                Foom.Invoke(null, new object[] { 2, null });
            }
            catch (NullReferenceException)
            { Console.WriteLine("Won't catch NullReferenceException"); }
            catch (TargetInvocationException)
            { Console.WriteLine("Bad!"); }

            // DynamicInvoke, exception gets wrapped
            try
            {
                Delegate Food = (myFoo)Foo;
                Food.DynamicInvoke(2, null);
            }
            catch (NullReferenceException)
            { Console.WriteLine("Won't catch NullReferenceException"); }
            catch (TargetInvocationException)
            { Console.WriteLine("Bad!"); }
        }

        private static void Foo(int i, string s)
        {
            Console.WriteLine("i=" + i + ", s.Length = " + s.Length);
        }
    }
}

      

+2


source


Actually @hvd answered:

((dynamic)Food).Invoke(2, null);

      



solves my problem in one line of code. Thank!

+1


source







All Articles