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();
}
To handle such an AccessViolationException exception, apply the HandleProcessCorruptedStateExceptionsAttribute attribute to the method in which the exception is thrown. This change does not affect AccessViolationException exceptions thrown by user code, which can continue to be caught by a catch statement.
A System. AccessViolationException occurs when unmanaged/unsafe code attempts to use memory that has not been allocated, or to memory that it doesn't have access to.
EDIT (3/17/2021)
Disclaimer: This answer was written in 2011 and references the original .NET Framework 4.0 implementation, NOT the open-source implementation of .NET.
In .NET 4.0, the runtime handles certain exceptions raised as Windows Structured Error Handling (SEH) errors as indicators of Corrupted State. These Corrupted State Exceptions (CSE) are not allowed to be caught by your standard managed code. I won't get into the why's or how's here. Read this article about CSE's in the .NET 4.0 Framework:
http://msdn.microsoft.com/en-us/magazine/dd419661.aspx#id0070035
But there is hope. There are a few ways to get around this:
Recompile as a .NET 3.5 assembly and run it in .NET 4.0.
Add a line to your application's config file under the configuration/runtime element:
<legacyCorruptedStateExceptionsPolicy enabled="true|false"/>
Decorate the methods you want to catch these exceptions in with the HandleProcessCorruptedStateExceptions
attribute. See http://msdn.microsoft.com/en-us/magazine/dd419661.aspx#id0070035 for details.
EDIT
Previously, I referenced a forum post for additional details. But since Microsoft Connect has been retired, here are the additional details in case you're interested:
From Gaurav Khanna, a developer from the Microsoft CLR Team
This behaviour is by design due to a feature of CLR 4.0 called Corrupted State Exceptions. Simply put, managed code shouldnt make an attempt to catch exceptions that indicate corrupted process state and AV is one of them.
He then goes on to reference the documentation on the HandleProcessCorruptedStateExceptionsAttribute and the above article. Suffice to say, it's definitely worth a read if you're considering catching these types of exceptions.
Add the following in the config file, and it will be caught in try catch block. Word of caution... try to avoid this situation, as this means some kind of violation is happening.
<configuration>
<runtime>
<legacyCorruptedStateExceptionsPolicy enabled="true" />
</runtime>
</configuration>
Compiled from above answers, worked for me, did following steps to catch it.
Step #1 - Add following snippet to config file
<configuration>
<runtime>
<legacyCorruptedStateExceptionsPolicy enabled="true" />
</runtime>
</configuration>
Step #2
Add -
[HandleProcessCorruptedStateExceptions]
[SecurityCritical]
on the top of function you are tying catch the exception
source: http://www.gisremotesensing.com/2017/03/catch-exception-attempted-to-read-or.html
Microsoft: "Corrupted process state exceptions are exceptions that indicate that the state of a process has been corrupted. We do not recommend executing your application in this state.....If you are absolutely sure that you want to maintain your handling of these exceptions, you must apply the HandleProcessCorruptedStateExceptionsAttribute
attribute"
Microsoft: "Use application domains to isolate tasks that might bring down a process."
The program below will protect your main application/thread from unrecoverable failures without risks associated with use of HandleProcessCorruptedStateExceptions
and <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
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With