益智涉及解开堆栈动态调用 [英] Puzzle involving unwound stacks on dynamic invoke

查看:130
本文介绍了益智涉及解开堆栈动态调用的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

这是一个新的尝试,以造成一个版本的要求不太成功今早的问题。

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

考虑下面的程序,我们将在Visual Studio 2010中的内部运行一次,再次双击直接可执行

Consider the following program, which we'll run once inside Visual Studio 2010 and once more by double-clicking the executable directly

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);
        }
    }
}

当在运行VS除了在富触发,调试器显示堆栈正确,表明问题发生在美孚的第二的WriteLine。

When the exception in Foo triggers while running VS, the debugger shows the stack correctly and shows that the problem occured on the second WriteLine in Foo.

但是,当在直接运行可执行程序出现异常,一名来自CLR表明程序抛出了一个未处理的异常会有点弹出窗口。点击调试,并选择与调试。在这种情况下,退绕堆叠到最近.DynamicInvoke的点和当附加与调试器,即在异常时存在堆栈上下文已经部分消失。

But when the exception occurs while running the executable directly, one gets a little popup window from the CLR indicating that the program threw an unhandled exception. Click debug and select the VS debugger. In this case, the stack unwinds up to the point of the most recent .DynamicInvoke and when you attach with the debugger, the stack context that existed at the time of the exception has been partially lost.

它确实存在,在有限的形式,在异常事件的内部异常部分。单击以展开相关的信息,并找到行号发生问题。但很明显,局部变量和其他方面将不复存在。

It does exist, in a limited form, within the "inner exception" portion of the exception event. You click to expand the associated information and find the line number where the problem occured. But obviously local variables and other context will be gone.

如果有人试图同样的事情,但没有.DynamicInvoke(例如,调用foo(1,NULL)在主要的1号线),还是通过双击该.exe文件,我们得到的时候正确的行号调试器附加。同样,如果应用程序是通过点击该.exe启动,但随后的调试器附加到的异常被抛出前。

If one tries the same thing but without the .DynamicInvoke (for example, call Foo(1, null) on line 1 of Main), still by double-clicking the .exe file, we DO get the correct line number when the debugger attaches. Similarly if the application is launched by clicking on the .exe, but then the debugger is attached before the exception gets thrown.

有谁知道如何使用动态反射/调用的应用程序可能会避免这个问题?在我的预期使用情况下,在一个系统中的名字,而我也不会在这里提到,我不能predict对象的类型签名,将在.DynamicInvoke使用,或参数连号,这将是就业,因此静态类型,甚至仿制药都没有办法摆脱这一点。

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

我的问题是:有没有人知道为什么直接从调试器附加到程序时与运行异常被抛出之后,当我们得到这样的不同的行为?

My question is this: does anyone know why we get such different behaviors when running directly from the debugger versus when attaching to the program after the exception is thrown?

推荐答案

根据该意见,你是否看到的NullReferenceException 作为未处理取决于它的处理。这里有一些方法来调用,前三将离开例外,因为未处理的,最后两个将处理的NullReferenceException 通过包装它,并抛出一个新的异常。

As per the comments, whether you see the NullReferenceException as unhandled depends on whether it's handled. Here are some ways to call Foo, the first three will leave the exception as unhandled, the last two will handle the 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);
        }
    }
}

这篇关于益智涉及解开堆栈动态调用的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆