I am using a COM object (MODI) from within my .net application. The method I am calling throws a System.AccessViolationException, which is intercepted by Visual Studio. The odd thing is that I have wrapped my call in a try catch, which has handlers for AccessViolationException, COMException and everything else, but when Visual Studio (2010) intercepts the AccessViolationException, the debugger breaks on the method call (doc.OCR), and if I step through, it continues to the next line instead of entering the catch block. Additionally, if I run this outside of the visual studio my application crashes. How can I handle this exception that is thrown within the COM object?

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();

}

您可以尝试使用AppDomain。UnhandledException,看看这是否能让你捕获它。

* *编辑*

这里有一些可能有用的信息(这是一个很长的阅读)。


编辑(3/17/2021)

免责声明:这个答案写于2011年,引用了最初的。net Framework 4.0实现,而不是。net的开源实现。


在。net 4.0中,运行时处理某些异常,作为Windows结构化错误处理(SEH)错误作为损坏状态的指示器。标准托管代码不允许捕获这些损坏状态异常(CSE)。我就不讲原因了。阅读这篇关于。net 4.0框架中的CSE的文章:

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

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

重新编译为. net 3.5程序集并在. net 4.0中运行。 在应用程序的配置文件的configuration/runtime元素下添加一行: < legacyCorruptedStateExceptionsPolicy启用= "真|假" / > 使用HandleProcessCorruptedStateExceptions属性装饰您想要捕获这些异常的方法。详情见http://msdn.microsoft.com/en-us/magazine/dd419661.aspx#id0070035。


EDIT

之前,我参考了一个论坛帖子来获得更多的细节。但由于Microsoft Connect已经退役,下面是额外的细节,以防你感兴趣:

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

这种行为是由于CLR 4.0的一个被称为损坏状态异常的特性而设计的。简单地说,托管代码不应该试图捕获指示已损坏进程状态的异常,AV就是其中之一。

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


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

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

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

步骤#1 -添加以下片段配置文件

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

步骤# 2

添加-

[HandleProcessCorruptedStateExceptions]

[SecurityCritical]

在函数的顶部,你正在绑定catch异常

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


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

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

下面的程序将保护您的主应用程序/线程免受不可恢复的失败,而没有使用HandleProcessCorruptedStateExceptions和<legacyCorruptedStateExceptionsPolicy>相关的风险

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
    }
}