【问题标题】:Visual Studio does not show the original location of rethrown inner exception in C#Visual Studio 不显示 C# 中重新抛出的内部异常的原始位置
【发布时间】:2012-01-16 13:36:55
【问题描述】:

所以我按照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:line 19 中的 Example.ExampleController.NullTest() 在 C:\Server Storage\Projects\Redpoint\Pivot\Example\ExampleWorld.cs:line 29 中的 Example.ExampleWorld.t_Spawned(Object sender, EventArgs e) 在 C:\Server Storage\Projects\Redpoint\Pivot\Pivot.Core\Actor.cs:line 62 中的 Pivot.Core.Actor.OnSpawned__Distributed0() 处

在 [0] 处重新抛出异常: 在 Process4.Providers.ExceptionExtensions.Rethrow(异常前) 在 Process4.Providers.ExceptionExtensions.Rethrow(异常前) 在 Process4.Providers.DpmEntrypoint.Invoke(委托 d,对象 [] 参数) 在 Pivot.Core.Actor.OnSpawned() 在 C:\Server Storage\Projects\Redpoint\Pivot\Pivot.Core\GameInfo.cs:line 35 中的 Pivot.Core.GameInfo.set_World__Distributed0(WorldInfo 值)

1 处重新抛出异常: 在 Process4.Providers.ExceptionExtensions.Rethrow(异常前) 在 Process4.Providers.ExceptionExtensions.Rethrow(异常前) 在 Process4.Providers.DpmEntrypoint.SetProperty(Delegate d, Object[] args) 在 Pivot.Core.GameInfo.set_World(WorldInfo 值) 在 C:\Server Storage\Projects\Redpoint\Pivot\Example\ExampleGame.cs:line 25 中的 Example.ExampleGame.t_Spawned(Object sender, EventArgs e) 在 C:\Server Storage\Projects\Redpoint\Pivot\Pivot.Core\Actor.cs:line 62 中的 Pivot.Core.Actor.OnSpawned__Distributed0() 处

在 [2] 处重新抛出异常: 在 Process4.Providers.ExceptionExtensions.Rethrow(异常前) 在 Process4.Providers.ExceptionExtensions.Rethrow(异常前) 在 Process4.Providers.DpmEntrypoint.Invoke(委托 d,对象 [] 参数) 在 Pivot.Core.Actor.OnSpawned() 在 C:\Server Storage\Projects\Redpoint\Pivot\Pivot.Core\Engine.cs:line 47 中的 Pivot.Engine.set_Game(GameInfo 值) 在 C:\Server Storage\Projects\Redpoint\Pivot\Example\Program.cs:line 14 中的 Example.Program.Main(String[] args) 在 System.AppDomain._nExecuteAssembly(程序集程序集,字符串 [] 参数) 在 System.AppDomain.ExecuteAssembly(字符串 assemblyFile,证据 assemblySecurity,String [] args) 在 Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly() 在 System.Threading.ThreadHelper.ThreadStart_Context(对象状态) 在 System.Threading.ExecutionContext.Run(ExecutionContext executionContext,ContextCallback 回调,对象状态) 在 System.Threading.ThreadHelper.ThreadStart()

但我遇到的问题是,即使库在 Release 模式下编译,Visual Studio 仍然在 Rethrow 而不是 NullTest (引发 NullReferenceException 的地方)显示异常源。

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

如果无法按原样重新抛出内部异常,则在与分布式处理库一起使用时,它基本上会在 .NET 中创建整个异常系统,而开发人员不会意识到幕后发生的事情(这完全相反的目标图书馆)。

有谁知道让 Visual Studio 在最初抛出的位置显示它的方法?

编辑:

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

问题是这些函数不允许我“传输托管异常”,因此当我尝试在托管异常上使用它时,rethrow_exception 会引发 SEHException。如果有人知道解决此问题的方法,则可以使用 C++/CLI 解决(如果只有一种方法可以获取当前异常的确切指针,则可以使用 rethrow 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;
        }
    };
}

【问题讨论】:

  • 我认为解决此问题的唯一选择是自动替换重写方法中的 catch 处理程序以真正执行代码,如果它是具有匹配的 InnerException 的 TargetInvocationException。编辑:嗯,虽然 /still/ 不会让 Visual Studio 显示未处理异常的正确来源。
  • 我最近通过 C# 阅读了 Jeff Richter 的 CLR(虽然没有在工作中 - 所以我不能给你具体细节)但是他将异常数据的一部分累积到“数据”字段并不断重新抛出第一个异常。
  • 在 .NET 框架中有几个地方适合您尝试做的事情。例如 Control.Invoke() 重新抛出最里面的异常。但同样的问题,您会在堆栈跟踪上看到管道。隐藏框架与异常处理的设计目的完全相反。你不能让它工作。还要注意,重新抛出最里面的异常本身就是一个问题,被调用的方法本身可能会捕获并重新抛出,将原始异常作为内部异常传递。您将重新抛出错误的异常。也没有解决办法。

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


【解决方案1】:

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

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

【讨论】:

  • 这并不能解决用户必须捕获与实际抛出的异常类型不同的异常的问题。事实上,这与 TargetInvocationException 现在发生的情况相同。
【解决方案2】:

throw ex;unwind the stack

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

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

【讨论】:

  • 再次抛出 TargetInvocationException。它不会重新抛出内部异常。
【解决方案3】:

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

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

【讨论】:

    猜你喜欢
    • 2011-01-22
    • 2023-03-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多