Visual Studio不会在C#中显示重新抛出的内部异常的原始位置

时间:2011-12-09 07:42:56

标签: .net debugging exception c++-cli throw

所以我按照How to rethrow the inner exception of a TargetInvocationException without losing the stack trace中显示的推荐答案进行操作,最后得到的代码如下:

// Inside DpmEntrypoint static class.
public static object Invoke(Delegate d, object[] args)
{
    try
    {
        // Invoke directly if not networked.
        if (LocalNode.Singleton == null)
            return d.DynamicInvoke(args);

        // Get the network name of the object and the name of the method.
        string objectName = (d.Target as ITransparent).NetworkName;
        string methodName = d.Method.Name;

        // Get our local node and invoke the method.
        return LocalNode.Singleton.Invoke(objectName, methodName, args);
   }
   catch (Exception ex)
   {
        ex.Rethrow();
        return null;
    }
}

// Inside ExceptionExtensions static class.
public static void Rethrow(this Exception ex)
{
    if (ex is TargetInvocationException)
        ex.InnerException.Rethrow();
    else
    {
        typeof(Exception).GetMethod("PrepForRemoting",
            BindingFlags.NonPublic | BindingFlags.Instance)
            .Invoke(ex, new object[0]);
        throw ex;
    }
}

在这种情况下,后处理器在编译时运行在程序集上,指定的方法包装在委托中并使用上面的方法调用。为了然后调用委托,我必须在行的某个地方使用Invoke(即使它在Singleton.Invoke中使用的反射中)。

上面的Rethrow方法正确地保留了堆栈跟踪,如下所示:

  

服务器堆栈跟踪:      在C:\ Server Storage \ Projects \ Redpoint \ Pivot \ Example \ ExampleController.cs中的Example.ExampleController.NullTest():第19行      at Example.ExampleWorld.t_Spawned(Object sender,EventArgs e)在C:\ Server Storage \ Projects \ Redpoint \ Pivot \ Example \ ExampleWorld.cs:第29行      在Pivot.Core.Actor.OnSpawned__Distributed0()在C:\ Server Storage \ Projects \ Redpoint \ Pivot \ Pivot.Core \ Actor.cs:第62行

     

在[0]处重新抛出异常:      在Process4.Providers.ExceptionExtensions.Rethrow(Exception ex)      在Process4.Providers.ExceptionExtensions.Rethrow(Exception ex)      在Process4.Providers.DpmEntrypoint.Invoke(Delegate d,Object [] args)      在Pivot.Core.Actor.OnSpawned()      在Pivot.Core.GameInfo.set_World__Distributed0(WorldInfo值)C:\ Server Storage \ Projects \ Redpoint \ Pivot \ Pivot.Core \ GameInfo.cs:第35行

     

1重新招募异常:      在Process4.Providers.ExceptionExtensions.Rethrow(Exception ex)      在Process4.Providers.ExceptionExtensions.Rethrow(Exception ex)      在Process4.Providers.DpmEntrypoint.SetProperty(Delegate d,Object [] args)      在Pivot.Core.GameInfo.set_World(WorldInfo值)      at Example.ExampleGame.t_Spawned(Object sender,EventArgs e)在C:\ Server Storage \ Projects \ Redpoint \ Pivot \ Example \ ExampleGame.cs:第25行      在Pivot.Core.Actor.OnSpawned__Distributed0()在C:\ Server Storage \ Projects \ Redpoint \ Pivot \ Pivot.Core \ Actor.cs:第62行

     

在[2]重新抛出异常:      在Process4.Providers.ExceptionExtensions.Rethrow(Exception ex)      在Process4.Providers.ExceptionExtensions.Rethrow(Exception ex)      在Process4.Providers.DpmEntrypoint.Invoke(Delegate d,Object [] args)      在Pivot.Core.Actor.OnSpawned()      在C:\ Server Storage \ Projects \ Redpoint \ Pivot \ Pivot.Core \ Engine.cs中的Pivot.Engine.set_Game(GameInfo值):第47行      在C:\ Server Storage \ Projects \ Redpoint \ Pivot \ Example \ Program.cs中的Example.Program.Main(String [] args):第14行      在System.AppDomain._nExecuteAssembly(Assembly assembly,String [] args)      在System.AppDomain.ExecuteAssembly(String assemblyFile,Evidence assemblySecurity,String [] args)      在Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly()      在System.Threading.ThreadHelper.ThreadStart_Context(对象状态)      在System.Threading.ExecutionContext.Run(ExecutionContext executionContext,ContextCallback回调,对象状态)      在System.Threading.ThreadHelper.ThreadStart()

但我遇到的问题是,即使在发布模式下编译库,Visual Studio仍会在Rethrow而不是NullTest(抛出NullReferenceException)中显示异常源。

由于应用程序中的所有方法都将被挂钩,因此抛出TargetInvocationException对开发人员来说是无用的;他们对代码中发生原始异常的地方更感兴趣。

如果不能重新抛出内部异常,它基本上会在.NET中使用整个异常系统时与分布式处理库一起使用,而开发人员不知道幕后发生了什么(正好相反的目标)图书馆。)

有没有人知道如何让Visual Studio在最初抛出的位置显示它?

修改

我正在考虑使用C ++ / CLI解决问题,因为这样可以访问一些特殊功能,如MSDN文章Transporting Exceptions Between Threads中所述。

问题是这些函数不允许我“传输托管异常”,因此当我尝试在托管异常上使用它时,rethrow_exception会抛出SEHException。如果有人知道解决这个问题的方法,那么可以使用C ++ / CLI解决它(如果只有一种方法可以获得当前异常的确切指针,那么可以使用重新抛出IL指令!)。

namespace Rethrow {
    [System::Runtime::CompilerServices::Extension]
    public ref class ExceptionExtensions abstract sealed
    {
    public: 
        [System::Runtime::CompilerServices::Extension]
        static void Rethrow(System::Exception^ s)
        {
            std::rethrow_exception(std::copy_exception<System::Exception^>(s));
            throw;
        }
    };
}

3 个答案:

答案 0 :(得分:0)

嗯...尝试用这种结构替换“ throw ex ”:

Exception HighelLevelException = new Exception("An inner error occured!", ex);
throw HighelLevelException;

答案 1 :(得分:0)

throw ex;unwind the stack

catch (Exception ex)
{
    throw;
    return null;
}

就够了。但请记住这是C#语法。

答案 2 :(得分:0)

我设法通过修改后处理器来直接通过匹配的委托来调用方法来解决这个问题(因为后处理器在运行时知道方法签名并且可以生成适当的匹配委托)。在委托上使用Invoke()时,它不会使用TargetInvocationException包装任何异常;只有在动态调用方法时才会发生。

不幸的是,这个解决方案实际上并不解决动态调用方法和传递内部异常;它只是利用了这个特定库的构建过程中已经存在的后处理器(即,这个解决方案无法解决任何人构建不进行后处理的库的问题。)