我在 .net 应用程序中使用 COM 对象 (MODI)。我调用的方法抛出一个
System.AccessViolationException
,它被 Visual Studio 拦截。奇怪的是,我将调用包装在 try catch 中,其中包含 AccessViolationException
、COMException
和其他所有内容的处理程序,但是当 Visual Studio (2010) 拦截 AccessViolationException
时,调试器会在方法调用上中断(doc.OCR),如果我单步执行,它会继续到下一行,而不是进入 catch 块。此外,如果我在视觉工作室之外运行它,我的应用程序就会崩溃。如何处理 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();
}
编辑 (3/17/2021)
免责声明:此答案写于 2011 年,引用了原始 .NET Framework 4.0 实现,而不是 .NET 的开源实现。
在 .NET 4.0 中,运行时会处理作为 Windows 结构化错误处理 (SEH) 错误引发的某些异常,作为损坏状态的指示符。标准托管代码不允许捕获这些损坏状态异常 (CSE)。我不会在这里讨论为什么或如何发生。阅读这篇有关 .NET 4.0 Framework 中的 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/en-us/magazine/dd419661.aspx#id0070035。
编辑
之前,我参考了论坛帖子以了解更多详细信息。但由于 Microsoft Connect 已停用,如果您感兴趣,以下是其他详细信息:
来自 Microsoft 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]
在您要绑定的函数顶部捕获异常
来源:http://www.gisremotesensing.com/2017/03/catch-exception-attempted-to-read-or.html
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
}
}
** 这就是我处理 AccessViolationException 的方式:**
[HandleProcessCorruptedStateExceptions]
[SecurityCritical]
private static ImageSource ImageSourceFromBitmap(System.Drawing.Bitmap bmp)
{
if (handle != IntPtr.Zero)
{
GC.SuppressFinalize(handle);
}
try
{
handle = bmp.GetHbitmap();
BitmapSource bitmapSource = Imaging.CreateBitmapSourceFromHBitmap(handle, IntPtr.Zero, Int32Rect.Empty, BitmapSizeOptions.FromEmptyOptions());
bitmapSource.Freeze();
return bitmapSource;
}
catch (System.AccessViolationException exception)
{
Console.WriteLine($"{exception}Fail: CreateBitmapSourceFromHBitmap");
throw new Exception();
}
finally
{
GC.SuppressFinalize(handle);
DeleteObject(handle);
}
}