【问题标题】:How to handle AccessViolationException如何处理 AccessViolationException
【发布时间】:2020-07-24 03:03:49
【问题描述】:

我在我的 .net 应用程序中使用 COM 对象 (MODI)。我调用的方法会抛出一个System.AccessViolationException,它会被 Visual Studio 拦截。奇怪的是,我已经将我的调用包装在一个 try catch 中,它具有 AccessViolationExceptionCOMException 和其他所有内容的处理程序,但是当 Visual Studio (2010) 拦截 AccessViolationException 时,调试器会中断方法调用(doc.OCR),如果我单步执行,它将继续到下一行,而不是进入 catch 块。此外,如果我在 Visual Studio 之外运行它,我的应用程序会崩溃。如何处理 COM 对象中引发的这个异常?

MODI.Document doc = new MODI.Document();
try
{
    doc.Create(sFileName);
    try
    {
        doc.OCR(MODI.MiLANGUAGES.miLANG_ENGLISH, false, false);
        sText = doc.Images[0].Layout.Text;
    }
    catch (System.AccessViolationException ex)
    {
        //MODI seems to get access violations for some reason, but is still able to return the OCR text.
        sText = doc.Images[0].Layout.Text;
    }
    catch (System.Runtime.InteropServices.COMException ex)
    {
        //if no text exists, the engine throws an exception.
        sText = "";
    }
    catch
    {
        sText = "";
    }

    if (sText != null)
    {
        sText = sText.Trim();
    }
}
finally
{
    doc.Close(false);

    //Cleanup routine, this is how we are able to delete files used by MODI.
    System.Runtime.InteropServices.Marshal.FinalReleaseComObject(doc);
    doc = null;
    GC.WaitForPendingFinalizers();
    GC.Collect();
    GC.WaitForPendingFinalizers();

}

【问题讨论】:

  • 您是否尝试过(暂时!)放置一个Exception 处理程序来捕获所有异常并查看异常实际上是什么?
  • @ChrisF - 是的,看到最后一个 catch 处理程序了吗?这应该捕获所有内容,包括异常和异常的任何子类。同样,Visual Studio 报告异常是 System.AccessViolationException

标签: c# .net exception com process-state-exception


【解决方案1】:

在 .NET 4.0 中,运行时会将某些异常作为 Windows 结构化错误处理 (SEH) 错误作为损坏状态的指标进行处理。您的标准托管代码不允许捕获这些损坏状态异常 (CSE)。我不会在这里讨论为什么或如何。阅读这篇关于 .NET 4.0 框架中 CSE 的文章:

http://msdn.microsoft.com/en-us/magazine/dd419661.aspx#id0070035

但还是有希望的。有几种方法可以解决这个问题:

  1. 重新编译为 .NET 3.5 程序集并在 .NET 4.0 中运行。

  2. 在应用程序的配置文件的配置/运行时元素下添加一行: <legacyCorruptedStateExceptionsPolicy enabled="true|false"/>

  3. 使用HandleProcessCorruptedStateExceptions 属性装饰您想要捕获这些异常的方法。详情请见http://msdn.microsoft.com/en-us/magazine/dd419661.aspx#id0070035


编辑

之前,我参考了forum post 以了解更多详细信息。但由于 Microsoft Connect 已停用,如果您有兴趣,请查看以下详细信息:

来自 Microsoft CLR 团队的开发人员 Gaurav Khanna

由于 CLR 4.0 的一项称为损坏状态异常的功能,此行为是设计使然。简而言之,托管代码不应尝试捕获表明进程状态已损坏的异常,而 AV 就是其中之一。

然后他继续参考HandleProcessCorruptedStateExceptionsAttribute 和上述文章的文档。可以说,如果您正在考虑捕获这些类型的异常,那么绝对值得一读。

【讨论】:

  • HandleProcessCorruptedStateExceptions 在 .Net 4.5 中为我工作。
  • 感谢 villecoder,你是一颗宝石!我已经处理这个问题好几个星期了,试图解决根本问题,最后让自己只治标不治本。您的解决方案非常完美。
  • !请注意:强烈建议在 AccessViolationException 是损坏状态异常 (CSE) 之后结束进程。否则会导致更严重的错误。
  • 谢谢,这真的很有帮助,虽然起初我的印象是我需要执行所有 3 个步骤才能捕获这些异常,而它实际上是一个“合乎逻辑的OR" 的方法。 :)
  • @deerchao 我希望您已经阅读了答案中提供的第一个链接。处理 CSE 异常是个坏主意。
【解决方案2】:

在配置文件中添加以下内容,它将被try catch块捕获。 请注意...尽量避免这种情况,因为这意味着正在发生某种违规行为。

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

【讨论】:

  • 使用c++/cli作为dll的,代码应该加到最上面的.exe项目中。
【解决方案3】:

从上面的答案编译,为我工作,做了以下步骤来捕捉它。

步骤 #1 - 将以下 sn-p 添加到配置文件

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

步骤 #2

添加 -

[HandleProcessCorruptedStateExceptions]

[SecurityCritical]

在你绑定的函数的顶部捕获异常

来源:http://www.gisremotesensing.com/2017/03/catch-exception-attempted-to-read-or.html

【讨论】:

【解决方案4】:

Microsoft: “损坏的进程状态异常是表示进程状态已损坏的异常。我们不建议在此执行您的应用程序state.....如果您绝对确定要保持对这些异常的处理,则必须应用 HandleProcessCorruptedStateExceptionsAttribute 属性"

Microsoft: “使用应用程序域来隔离可能导致进程中断的任务。”

下面的程序将保护您的主应用程序/线程免受不可恢复的故障,而不会出现与使用 HandleProcessCorruptedStateExceptions&lt;legacyCorruptedStateExceptionsPolicy&gt; 相关的风险

public class BoundaryLessExecHelper : MarshalByRefObject
{
    public void DoSomething(MethodParams parms, Action action)
    {
        if (action != null)
            action();
        parms.BeenThere = true; // example of return value
    }
}

public struct MethodParams
{
    public bool BeenThere { get; set; }
}

class Program
{
    static void InvokeCse()
    {
        IntPtr ptr = new IntPtr(123);
        System.Runtime.InteropServices.Marshal.StructureToPtr(123, ptr, true);
    }

    private static void ExecInThisDomain()
    {
        try
        {
            var o = new BoundaryLessExecHelper();
            var p = new MethodParams() { BeenThere = false };
            Console.WriteLine("Before call");

            o.DoSomething(p, CausesAccessViolation);
            Console.WriteLine("After call. param been there? : " + p.BeenThere.ToString()); //never stops here
        }
        catch (Exception exc)
        {
            Console.WriteLine($"CSE: {exc.ToString()}");
        }
        Console.ReadLine();
    }


    private static void ExecInAnotherDomain()
    {
        AppDomain dom = null;

        try
        {
            dom = AppDomain.CreateDomain("newDomain");
            var p = new MethodParams() { BeenThere = false };
            var o = (BoundaryLessExecHelper)dom.CreateInstanceAndUnwrap(typeof(BoundaryLessExecHelper).Assembly.FullName, typeof(BoundaryLessExecHelper).FullName);         
            Console.WriteLine("Before call");

            o.DoSomething(p, CausesAccessViolation);
            Console.WriteLine("After call. param been there? : " + p.BeenThere.ToString()); // never gets to here
        }
        catch (Exception exc)
        {
            Console.WriteLine($"CSE: {exc.ToString()}");
        }
        finally
        {
            AppDomain.Unload(dom);
        }

        Console.ReadLine();
    }


    static void Main(string[] args)
    {
        ExecInAnotherDomain(); // this will not break app
        ExecInThisDomain();  // this will
    }
}

【讨论】:

    【解决方案5】:

    您可以尝试使用AppDomain.UnhandledException,看看是否能让您抓住它。

    **编辑*

    这里有一些可能有用的more information(读起来很长)。

    【讨论】:

    • 由于 .NET 框架的变化,这个答案不再完全准确。在 4.0 之前它是正确的。 msdn.microsoft.com/en-us/library/… 中的每个 AccessViolationException 和 try/catch 块的部分
    猜你喜欢
    • 2012-06-06
    • 1970-01-01
    • 1970-01-01
    • 2012-07-06
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多