System.Reflection.TargetInvocationException没有被捕获

5

解决方案后添加的说明:反射调用的方法中出现了AccessViolationException异常,这是TargetInvocationException无法被捕获的原因。

注意:这是在IDE之外。所引用的问题与此不同。

简短概述:

  1. 获取不到堆栈跟踪
  2. 无法获取内部异常
  3. 不能使用调试器(第三方库的版权保护机制会干扰)
  4. 对代码进行任何更改都会阻止异常的发生 - 这意味着我无法添加记录以查找异常发生的位置

如何捕获异常或以其他方式获取所需信息?

详细描述:

我有一个问题,涉及到通过反射调用的方法中发生的异常。实际上,异常是在被调用的方法中发生的,但由于该方法是通过反射调用的,因此真正的异常被包装在了“System.Reflection.TargetInvocationException”中。没有问题,只需捕获它并获取内部异常 - 但“System.Reflection.TargetInvocationException”没有被捕获。我的程序崩溃了,并且我得到了转储以及Windows事件日志中的条目。
Windows事件日志不包含内部异常,转储也不包含。我无法将调试器附加到程序中,因为这样会导致一个外部库(需要访问反射调用)无法运行 - 这是版权保护,你知道的。如果我在有问题的方法中放置try/catch,则异常不会发生 - 这很糟糕。原因没有解决,只是不再发生了。如果我在有问题的方法中放置记录日志,则同样会发生这种情况 - 异常不会再次发生。
我无法使用日志记录,无法使用调试器,在唯一可以捕获异常并记录其的地方,异常却没有被捕获。我正在使用Visual Studio 2010和dotnet 4.0。
让我来解释一下:当程序在Visual Studio之外运行时,try/catch就无法起作用,而我也不能在调试器中运行它,因为程序无法到达异常发生的点。这不是在IDE中。

消除反射不是一个选择(我已经尝试了只针对一个情况,异常就消失了)。

被调用的方法做了很多事情,但将其分解成小方法并没有帮助——异常仍然存在。

异常并不总是发生,只有在我执行某个特定步骤时才会发生——并且当它发生时,总是在整个序列第二次执行时发生。

在我使用的序列中,该方法几乎同时由两个线程调用——输入了一堆特定数据,导致报告和另一个文档的副本在两台独立的打印机上打印——每台打印机各有一个报告和文档。由于生成报告和打印文档可能需要一些时间,因此它们在后台的线程中完成,以便用户可以继续工作。

我怀疑这些线程互相干扰(进行了大量文件和数据库操作),但如果不知道实际发生了什么,我就无法修复它。

下面的代码显示了通过反射调用的简化版本。

有人有关于导致 System.Reflection.TargetInvocationException 无法被捕获的建议吗?或者有没有其他捕获内部异常的替代方法?

Try
    Dim methode As System.Reflection.MethodInfo
    methode = GetType(AParticularClass).GetMethod("OneOfManyMethods", Reflection.BindingFlags.NonPublic Or Reflection.BindingFlags.Static)
    Dim resultobject As Object = methode.Invoke(Nothing, Reflection.BindingFlags.InvokeMethod Or Reflection.BindingFlags.NonPublic Or Reflection.BindingFlags.Static, Nothing, New Object() {SomeBooleanVariable, SomeStringVariable},  Nothing)
    result = DirectCast(resultobject, DataSet)
Catch ex As Exception
    'Log the error here.
End Try

可能是为什么IDE将TargetInvocationException视为未捕获异常?的重复问题。 - Bradley Uffner
1
不是重复的,因为这发生在IDE之外。 - JRE
1
这是一大段文字,几乎没有任何信息可以让任何人猜测原因。没有堆栈跟踪。没有实际涉及你的问题的代码。你提到了线程运行的事情,但没有展示如何运行。请包含相关信息。 - Alex
3
@Alex:这正是问题所在。我无法获得堆栈跟踪,因为它只显示了System.Reflection.TargetInvocationException。我不是在寻求修复代码的帮助,而是在请求获取信息以便可以自行修复它。 - JRE
我在Catch块内部使用System.Reflection.MethodInfo.GetCurrentMethod.ToString,而不是在外部使用它,它完全正常运行...你为什么要“调用”,它根本没有做任何事情... - Trevor
显示剩余3条评论
2个回答

8

我找到了无法捕获异常的原因:

实际异常是AccessViolationException,在dotnet 4.0中需要采取特殊步骤(如何处理AccessViolationException。)才能捕获它

更有趣的是,当AccessViolationException在反射调用的方法中抛出时,只有TargetInvocationException在Windows事件日志中被记录,而且只有TargetInvocationException在转储中可用。

一旦我成功获取了真正的异常,我发现问题是由非GUI线程中的Application.DoEvents()方法引起的访问冲突。 在GUI线程上调用DoEvents()已经足够有趣了(使用Application.DoEvents()),更不用说在后台线程中调用它。

解决这个问题之后,我发现我们的第三方库(具有复制保护功能)不喜欢同时在不同的实例中被调用。 解决这个问题只需要在正确的位置加入同步锁即可。

导致所有这些问题的代码曾经全部在GUI线程中,并且最初是在dotnet 1.1的年代编写的 – 这可以解释为什么调用了DoEvents。 代码已被逐步转换为在后台线程中并行运行,在多个不同的阶段进行了修改,没有任何一个开发人员有完整的过程概述。


你是怎么调试出来的?我们在Windows事件日志中只看到了TIE出现了相同的问题。 - ChaseMedallion
如果您还没有,请添加Try/catch,然后按照这里的说明(https://dev59.com/KHA75IYBdhLWcg3wGlIa#4759831)启用捕获“损坏状态”异常。 - JRE
1
@Lousy:我终于成功地通过遵循我在这个答案中链接的答案和我在这个答案的第一个评论中提到的指示来捕获异常。有一个指令可以放在你的app.config文件中,告诉.Net让你捕获所有异常。然后,你可以捕获并记录异常,包括堆栈跟踪。 - JRE
@JRE:谢谢!那么,TargetInvocationException是一种误导吗?实际的异常与之无关?因为如果我理解正确的话,我需要在包含实际try/catch的方法上使用[HandleProcessCorruptedStateExceptions]属性进行修饰,就是发生异常的地方,但如果我不知道实际的方法,我不确定该如何做。或者说app.config设置就足够了吗? - Lou
1
app.config设置就是你所需要的。是的,TargetInvocationException只是一个误导。它被记录在Windows事件日志中,但真正的问题完全不同。TargetInvocationException只告诉你在调用的方法中发生了错误,但它本身并不是错误。 - JRE
显示剩余2条评论

-1

我不确定我能够复制您的问题,但这是我们得到它并且它运行得非常好的方式...

 Try
   'YOUR CODE'
 Catch ex As Exception
    'This is where we grab it from... It needs to be in this block to work...
    System.Reflection.MethodInfo.GetCurrentMethod.ToString
 End Try

请告诉我它对你有什么作用?


就像我说的那样,catch语句没有捕获到任何异常,程序直接崩溃了。我只能在Windows事件日志中看到一个程序崩溃的记录,并告诉我是什么类型的异常。 - JRE
所以这是一种不同类型的异常,这就是为什么它没有被捕获...展示该块中的代码... - Trevor
无法做到,因为它有数百行,并包括对其他方法的调用。这是我没有犯过但不得不忍受的古老罪恶。最肯定的是 System.Reflection.TargetInvocationException,但它是由反射方法内部的某些原因引起的。无论如何,类型并不重要 - Catch ex as Exception 应该能捕获每种异常。 - JRE

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接