如何处理AccessViolationException

我在.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();


}
173846 次浏览

你可以尝试使用应用程序域中。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

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

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

  2. 在应用程序的配置文件的configuration/runtime元素下添加一行: <legacyCorruptedStateExceptionsPolicy enabled="true|false"/> < / p >
  3. HandleProcessCorruptedStateExceptions属性修饰你想捕获这些异常的方法。详见http://msdn.microsoft.com/en-us/magazine/dd419661.aspx#id0070035


编辑

之前,我引用了论坛帖子来获取更多细节。但由于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

微软: 已损坏的进程状态异常是指示进程状态已损坏的异常。我们不建议在此状态下执行您的应用程序.....如果你绝对确定你想要维持你对这些异常的处理,你必须应用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
}
}