【问题标题】:Is it possible to catch an access violation exception in .NET?是否可以在 .NET 中捕获访问冲突异常?
【发布时间】:2011-03-19 17:27:03
【问题描述】:

我可以做些什么来捕捉AccessViolationException?它是由我无法控制的非托管 DLL 抛出的。

【问题讨论】:

    标签: .net exception-handling unmanaged


    【解决方案1】:

    您可以使用 try-catch 块包装对非托管 DLL 的调用。 AccessViolationExceptions 可以正常被抓到。执行以下代码会显示两条消息:

    try
    {
        throw new AccessViolationException();
    }
    catch (Exception e)
    {
        MessageBox.Show(e.Message + e.StackTrace, e.Message, MessageBoxButtons.OK, MessageBoxIcons.Error);
    }
    MessageBox.Show("Still running..");
    

    编辑: .NET 4 引入了change in behavior,它不再可能捕获损坏的状态异常,除非您专门在运行时 "ask" 这样做。

    【讨论】:

    • 问题是,这样做是个好主意吗?向用户显示友好且友好的消息并编写日志可能是明智的。但比这更进一步不是一个好主意......
    • 可能不会,至少如果你不是绝对肯定没有发生任何不好的事情的话。
    • 那行不通。我尝试同时捕获 Exception 和 AccessViolationException,但它忽略了我的 catch 块。我假设我需要一些 app.config 标志。
    • @Sean,通常我会说不,但这在日志记录例程中会死掉,我更愿意丢失日志条目而不是整个服务。
    • @Jonathan Allen:根据您提供的信息,假设您使用的是 .NET 4,settig 将是 legacyCorruptedState­­ExceptionsPolicy=true。查看我的更新。
    【解决方案2】:

    正如其他人指出的那样,您不应该“处理”这种情况,但在开发过程中,为了排除故障,可以方便地捕捉到这种情况。

    您可以使用System.Runtime.ExceptionServices.HandleProcessCorruptedStateExceptions 属性标记您的托管方法:

    [HandleProcessCorruptedStateExceptions]
    public void MyMethod()
    {
        try
        {
            NaughtyCall();
        }
        catch (AccessViolationException e)
        {
            // You should really terminate your application here
        }
    }
    

    【讨论】:

    • 在 AutoCAD 的应用程序插件的上下文中,我从来没有遇到过问题,但是,主机不会崩溃,这对我的用例来说已经足够了 :)(我正在调用主机应用程序 API 的方法发生错误的地方,我不知道有任何预先检查可以防止......)
    【解决方案3】:

    你不应该。访问冲突是一个严重的问题:它是对无效内存地址的写入(或读取)的意外尝试。正如 John 已经阐明的那样,在引发访问冲突之前,非托管 DLL 可能已经损坏了进程内存。这可能会对当前流程的任何部分产生意想不到的影响。

    最安全的做法是可能通知用户,然后立即退出。

    更多细节:访问冲突是操作系统异常(所谓的 SEH 或 结构化异常处理异常)。这是一种不同于System.Exception 中的托管CLR 异常的异常。您很少会在纯托管代码中看到 SEH 异常,但如果发生这种情况,例如在非托管代码中,CLR 会将其传递给托管代码,您也可以在其中捕获它1

    但是,捕获 SEH 异常通常不是一个好主意。更多详细信息在 MSDN 杂志中的文章 Handling Corrupted State Exceptions 中进行了解释,其中摘自以下文本:

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

    1在 .NET 3.5 之前都是如此。在 .NET 4 中,行为已更改。如果您仍然希望能够捕获此类异常,则必须将 legacyCorruptedState­­ExceptionsPolicy=true 添加到 app.config。上面链接的文章中的更多详细信息。

    【讨论】:

    • 澄清一下:您想尽快退出的原因是您不知道非托管 DLL 在遇到访问冲突之前覆盖了什么。它可能已经在足够多的地方写入垃圾,您的程序无法安全地继续运行。
    • 我可以想到一个很好的理由来抓住这个:如果你有一个无人值守的进程并且你没有这个,而不是退出,进程将挂起并显示没有人的访问冲突对话框特别是。捕捉到这一点可以让您在不弹出该对话框的情况下退出。
    • 即使通知用户或记录问题,然后退出,也需要捕获异常。
    • 让人们知道。 AccessViolationException 并不一定意味着“写入”。它也可以是“阅读”。
    • 按照 Noggins 的说法,从 C# 中捕获异常是有充分理由的,就像在 C 本身中一样,就像在 C# 中的 C# 中一样。有很多示例表明服务或实用程序 DLL 中的错误不应导致整个系统瘫痪。
    【解决方案4】:

    是的。

    在您的 App.confg 中,将以下代码放入 <configuration> 标记中:

    <runtime>
        <legacyCorruptedStateExceptionsPolicy enabled="true"/>
    </runtime>
    

    现在您应该能够像其他任何情况一样捕获损坏的状态异常 (CSE)。

    注意:如果您已经有一个运行时标签,那么只需将&lt;legacyCorruptedStateExceptionsPolicy enabled="true"/&gt; 添加到它

    以上适用于.Net 4.5

    【讨论】:

    • 为此。我读了几个关于这个问题的答案。所有人都说“将&lt;legacyCorruptedStateExceptionsPolicy enabled="true"/&gt; 添加到app.config”,但没有人说不应该在默认的&lt;startup&gt; 部分中。感谢&lt;runtime&gt; 部分的提示
    【解决方案5】:

    首先,我完全同意 0xA3。但是如果没有出路,您可以将脏的非托管 dll 包装在自己的进程中并通过 IPC(TCP/IP、namedpipes 等)传输数据。捕获所有异常并通知主机进程。所以你的主机进程主要是从内存损坏中拯救出来的。

    【讨论】:

      猜你喜欢
      • 2010-10-02
      • 2022-01-07
      • 2010-11-01
      • 2015-08-28
      • 2023-03-19
      • 2013-09-08
      • 1970-01-01
      相关资源
      最近更新 更多