View Categories

The two instances of intermittent AccessViolationException at AutoCAD object Dispose()

4 min read

Motivation #

I’ve encountered intermittent AccessViolationException errors at the AutoCAD object Dispose() method when running Civil 3D test cases via NUnit 2.6. I’ve asked on the forum, but so far the responses have been disappointing. I hope that readers of this blog who know something can shed some light on this.

Intermittent Crash 1 #

This occurs on the main thread, when I finish my operations and dispose of the database.

protected void RunTestAction(Action<Database> action)
{
    var doc = Application.DocumentManager.MdiActiveDocument;
    
    using (doc.LockDocument())
    {
        using (var db = new Database(false, NoDocument))
        {
            var templatePath = HostApplicationServices.Current.GetEnvironmentVariable(EnvironmentVariable.QnewTemplate);
            db.ReadDwgFile(templatePath, FileOpenMode.OpenForReadAndAllShare, false, null);
            
            using (new WorkingDatabaseSwitcher(db))
            {
                action(db);
            }
        }  // dispose will be called at the end of using, and generate an indeterminate AccessViolationException
    }
}

The Stack Trace is

System.AccessViolationException: Attempted to read or write protected memory. This is often an indication that other memory is corrupt.The stack trace is 

[Managed to Native Transition]
acdbmgd.dll!Autodesk.AutoCAD.Runtime.RXObject.DeleteUnmanagedObject()
acdbmgd.dll!Autodesk.AutoCAD.Runtime.DisposableWrapper.DisposableWrapper()
acdbmgd.dll!Autodesk.AutoCAD.Runtime.DisposableWrapper.Dispose(bool A_0)
acdbmgd.dll!Autodesk.AutoCAD.Runtime.DisposableWrapper.Dispose()
MES.Civil3DIntegrator.TestCase.dll!MES.Civil3DIntegrator.Test.BaseTests.RunTestAction(System.Action<Autodesk.AutoCAD.DatabaseServices.Database> action) Line 63
MES.Civil3DIntegrator.TestCase.dll!MES.Civil3DIntegrator.Test.PlatformSyncTest.PlatformBreakLineCutFillTest_WithSlopeString projectName) Line 130
[Native to Managed Transition]
[Managed to Native Transition]
System.Private.CoreLib.dll!System.Reflection.MethodBaseInvoker.InvokeDirectByRefWithFewArgs(object obj, System.Span<object> copyOfArgs, System.Reflection.BindingFlags invokeAttr)
System.Private.CoreLib.dll!System.Reflection.MethodBaseInvoker.InvokeWithOneArg(object obj, System.Reflection.BindingFlags invokeAttr, System.Reflection.Binder binder, object[] parameters, System.Globalization.CultureInfo culture) Line 424
nunit.core.dll!NUnit.Core.Reflect.InvokeMethod(System.Reflection.MethodInfo method, object fixture, object[] args) Line 424
nunit.core.dll!NUnit.Core.TestMethod.RunTestMethod() Line 495
nunit.core.dll!NUnit.Core.TestMethod.RunTestCase(NUnit.Core.TestResult testResult) Line 459
nunit.core.dll!NUnit.Core.TestMethod.RunTest() Line 333
nunit.core.dll!NUnit.Core.TestMethod.RunTest() Line 31
nunit.core.dll!NUnit.Core.TestMethod.RunRepeatedTest() Line 298
nunit.core.dll!NUnit.Core.TestMethod.RunTestInContext() Line 264
nunit.core.dll!NUnit.Core.TestMethod.Run(NUnit.Core.EventListener listener, NUnit.Core.ITestFilter filter) Line 178

The AccessViolationException is thrown from the RXObject.DeleteUnmanagedObject() method.

According to some posters, I need to manually call Dispose on objects that are not added to the database. I’m not entirely sure how accurate this is, since I haven’t been able to find any first-hand references.

The tricky part is that you’re also supposed to manually call Close on every AutoCAD object within a StartTransaction. Otherwise… the consequences are unclear. Close and Dispose are independent of each other, so apparently you need to call both? I’m also uncertain about this point.

In any case, I would have thought the worst consequence of failing to call Dispose would be a memory leak—not a crash. So the reason for the crash remains unknown. The exception occurs on the main thread, so it likely has nothing to do with accessing AutoCAD objects from a side thread, unlike the case described below.

Intermittent Crash 2 #

Sometimes, the same exception occurs in a side thread, as shown below:

System.AccessViolationException: Attempted to read or write protected memory. This is often an indication that other memory is corrupt.

Call Stack:
[Managed to Native Transition]
acdbmgd.dll!Autodesk.AutoCAD.Runtime.RXObject.DeleteUnmanagedObject() Line 130
acdbmgd.dll!Autodesk.AutoCAD.Runtime.DisposableWrapper.Dispose(bool A_0) Line 173

It’s not immediately clear whether this crash is related to the first crash at all. They could be, or not. I just don’t know at this point; my hope is that if I can solve this crash, then I can solve the first crash as well.

However, this exception is a lot easier to reason about. The theory is that I don’t take the manual step to Dispose AutoCAD objects (God knows which), and have to rely on the .NET Garbage Collector (GC) to do it. But AutoCAD objects can only operate on the main thread, and the Garbage Collector works on a background thread — this will lead to a crash. Also see here for more information.

It is said that:

Temporary objects with the potential to be database-resident but which never actually get added to a Database need to be manually disposed.

Failing to do so can lead to the GC taking matters into its own hands, which causes the exception. But I’ve looked through my code many times and I can’t really find such a candidate. Maybe I haven’t looked hard enough, but that’s highly unlikely.

But is there a way to quickly identify such objects? That will make my debugging job easier.

Powered by BetterDocs