【发布时间】: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