14

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

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

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

有时,GetAssemblyName会引发 a 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 上的远程服务器。)

4

1 回答 1

16

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

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

您可以选择处理这些异常(通过属性或应用程序配置文件中的策略更改),但不建议这样做,因为您的程序现在可能正在处理无效数据:

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

编写 catch (Exception e) 是一个常见的编程错误,因为未处理的异常会产生严重后果。但是您可能会争辩说,如果您不知道函数会引发什么错误,那么您应该在程序调用该函数时防止所有可能的错误。在您考虑当您的进程可能处于损坏状态时继续执行意味着什么之前,这似乎是一个合理的行动方案。有时中止并重试是最好的选择:没有人喜欢看到 Watson 对话框,但最好重新启动程序而不是损坏数据。

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

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

此引用来自(在 MSDN 杂志中)的文章更详细。

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

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


此外,您可能应该将此作为错误报告给 Microsoft,因为GetAssemblyName不应该抛出这种异常。

于 2013-05-08T09:23:33.647 回答