我正在从.net应用程序中使用COM对象(MODI)。我正在调用的方法将引发System.AccessViolationException,它会被Visual Studio拦截。奇怪的是,我将调用包装在一个try捕获中,该捕获具有用于AccessViolationException,COMException和其他所有内容的处理程序,但是当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-是的,看到最后一个捕获处理程序了吗?那应该捕获所有东西,包括Exception和Exception的任何子类。同样,Visual Studio报告该异常是System.AccessViolationException

#1 楼

在.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中运行。
在应用程序的配置文件下添加一行配置/运行时元素:
<legacyCorruptedStateExceptionsPolicy enabled="true|false"/>
使用HandleProcessCorruptedStateExceptions属性装饰您要捕获这些异常的方法。有关详细信息,请参见http://msdn.microsoft.com/zh-cn/magazine/dd419661.aspx#id0070035。


编辑

以前,我引用过有关更多详细信息的论坛帖子。但是,由于Microsoft Connect已停用,因此,如果您有兴趣,这里还有其他详细信息:

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



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


然后他继续引用HandleProcessCorruptedStateExceptionsAttribute及其以上的文档。文章。可以说,如果您考虑捕获这些类型的异常,绝对值得一读。

评论


.Net 4.5中的HandleProcessCorruptedStateExceptions对我有用。

– Deerchao
13年5月26日在8:56

谢谢villecoder,您是一颗宝石!我已经解决这个问题好几个星期了,试图解决根本问题,最后辞职去治疗症状。您的解决方案是完美的。

– gadildafissh
13年7月30日在18:45

!请注意:强烈建议在AccessViolationException之后将其终止,该过程是Corrupted State Exception(CSE)。否则会导致更严重的错误。

–克里斯·W
2014年12月8日14:56



谢谢,这确实有帮助,尽管起初我觉得我需要完成所有三个步骤才能捕获这些异常,而实际上这是执行这些方法的“逻辑或”。 :)

–楼
16-10-10在9:58



@deerchao我希望您已阅读答案中提供的第一个链接。处理CSE异常不是一个好主意。

–像素
17年2月20日在22:33

#2 楼

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

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


评论


对于使用c ++ / cli作为dll的用户,应将代码添加到顶部的.exe项目中。

–费利克斯
17年6月20日在2:34

#3 楼

从上面的答案中编译出来,为我工作,做了以下步骤来对其进行捕获。

步骤1-将以下代码段添加到配置文件中

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


步骤#2

添加-

[HandleProcessCorruptedStateExceptions]

[SecurityCritical]


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

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

评论


根据msdn.microsoft.com/zh-cn/library/…,SecurityCriticalAttribute等效于对完全信任的链接需求。我认为所描述的问题并不需要完全信任。

–杰里米
17 Mar 23 '17 at 18:59

#4 楼

Microsoft:“损坏的进程状态异常是表明进程状态已损坏的异常。我们不建议在此状态下执行您的应用程序.....如果您绝对确定要保持对这些状态的处理,例外,您必须应用HandleProcessCorruptedStateExceptionsAttribute属性“
Microsoft:”使用应用程序域来隔离可能导致进程崩溃的任务。“
下面的程序将保护您的主应用程序/线程免于不可恢复的故障,而不会带来风险使用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
    }
}
 


#5 楼

您可以尝试使用AppDomain.UnhandledException,看看是否可以捕获它。

** EDIT *

这里有一些更多有用的信息(阅读了很长时间) )。

评论


由于.NET框架的变化,此答案不再完全准确。在4.0之前是正确的。 msVn.microsoft.com/zh-cn/library/…中的AccessViolationException和try / catch块的每个部分

–特福德
17年8月11日在17:52