问题 SEHException未被Try / Catch捕获


在后台线程中,我的应用程序会定期检查网络文件夹(UNC路径)以获取应用程序更新。它会读出文件的汇编版本,如下所示:

Try
    newVers = System.Reflection.AssemblyName.GetAssemblyName("\\server\app.exe").Version
Catch ex As Exception
    ' ignore
End Try

这段代码经常被执行,到目前为止我总共在多个客户站点上猜测超过100.000次没有问题。

有时, GetAssemblyName 提出一个 FileNotFoundException例如,如果网络文件夹不可访问(可能发生并且必须处理)。这个例外是由 Catch 阻止在下面,一切正常。

然而,在三个报告的案例中, GetAssemblyName 电话提出了一个 SEHException。奇怪的是,这个例外没有被抓住 Catch 阻止在下面,但由我的全局未处理的异常处理程序(System.AppDomain.CurrentDomain.UnhandledException)。结果,应用程序崩溃。

这是一个例外细节(不幸的是, ErrorCode 和 CanResume 我的错误处理例程不记录异常的字段:

Caught exception: System.Runtime.InteropServices.SEHException
   Message: External component has thrown an exception.
   Source: mscorlib
   TargetSite: System.Reflection.AssemblyName nGetFileInformation(System.String)
   StackTrace: 
      at System.Reflection.AssemblyName.nGetFileInformation(String s)
      at System.Reflection.AssemblyName.GetAssemblyName(String assemblyFile)
      at SyncThread.Run() 
      at System.Threading.ThreadHelper.ThreadStart_Context(Object state)
      at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean ignoreSyncCtx)
      at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
      at System.Threading.ThreadHelper.ThreadStart()

为什么异常没有被捕获 Catch 挡在下面?

(也许这是相关的:这只发生在客户站点上,其中UNC路径指向的服务器不是本地网络的一部分,而是VPN上的远程服务器。)


10271
2018-05-08 09:04


起源

在我们的例子中,我们使用的是Telerik RadGridView,并且在添加行时偶尔会获得其中一个。我有同样的问题,为什么它不会被抓住。感谢Andreas的问题,以及@porges的答案。 - ebol2000


答案:


从.NET 4开始,有些 SEHExceptions表示损坏的进程状态,被称为“损坏的状态异常”。这些是诸如段错误/访问冲突之类的事情,其中​​在检测到内存损坏后抛出异常。

虽然这些错误仍然映射回托管.NET SEHExceptions,默认情况下它们不可捕获,所以 try { ... } catch (Exception ex) { ... } 不会处理它们。

您可以选择加入处理这些例外(或者 通过应用配置文件中的属性或策略更改),但不推荐,因为您的程序现在可以处理无效数据:

CLR始终使用与程序本身引发的异常相同的机制向托管代码提供SEH异常。只要代码不尝试处理无法合理处理的异常条件,这就不是问题。在访问冲突后,大多数程序无法安全地继续执行。不幸的是,CLR的异常处理模型总是鼓励用户通过允许程序捕获System.Exception层次结构顶部的任何异常来捕获这些严重错误。但这很少是正确的事情。

写入捕获(异常e)是一种常见的编程错误,因为未处理的异常会产生严重后果。但是你可能会争辩说,如果你不知道函数会引发什么错误,你应该在程序调用该函数时防止所有可能的错误。这似乎是一个合理的行动过程,直到您考虑在您的流程可能处于损坏状态时继续执行意味着什么。有时中止和再次尝试是最好的选择:没有人喜欢看Watson对话框,但重启程序比让数据损坏更好。

程序捕获由他们不理解的上下文引起的异常是一个严重的问题。但是,您无法通过使用异常规范或其他合同机制来解决问题。由于CLR是多种应用程序和主机的平台,因此托管程序能够接收SEH异常通知非常重要。某些主机(如SQL Server)需要完全控制其应用程序的进程。与本机代码互操作的托管代码有时必须处理本机C ++异常或SEH异常。

但是大多数编写catch的程序员(Exception e)并不真正想要捕获访问冲突。他们宁愿在发生灾难性错误时停止执行程序,而不是让程序在未知状态下跛行。对于托管托管加载项(如Visual Studio或Microsoft Office)的程序尤其如此。如果加载项导致访问冲突然后吞下异常,则主机可能会损坏其自身的状态(或用户文件),而不会意识到出现问题。

这句话的文章来自(在MSDN杂志中) 进一步详细说明。

如果您决定处理这些异常,则需要考虑很多 - 正如文章所述:“编写处理CSE的正确代码并继续安全地运行流程非常困难。”

特别是,任何 finally 异常传递的块有  已被执行(因此,例如,任何文件句柄都悬空直到收集),甚至 约束执行区域 可能会被跳过!


此外,您应该将此报告为Microsoft的错误,如 GetAssemblyName 不应该抛出这种例外。


13
2018-05-08 09:23



那么System.Reflection.AssemblyName.GetAssemblyName如何破坏内存呢? - Andreas
@Andreas:我在最后添加了一个注释,但你太快了!这看起来像一个bug,它永远不应该这样做。 - porges