TheSwamp

Code Red => .NET => Topic started by: lCine7ic on March 05, 2020, 10:06:14 AM

Title: AutoCAD 2020 and Random System.AccessViolationExceptions
Post by: lCine7ic on March 05, 2020, 10:06:14 AM
Hi,

I've been working on a .Net Addin since AutoCAD 2009 and have been adapting/extending it over the years to work with the various versions of AutoCAD I've used (AutoCAD 2013, 2016, 2018 and now 2020).

It has run as expected in all versions until 2020.  Granted, there have been Exceptions thrown over the time but always due to errors in my code and acted predictably.  By predictably, I mean if there was an exception thrown, then unless I resolved it, it would continue to be thrown at the same line of code.

With AutoCAD 2020, I'm getting System.AccessViolationException thrown at different points of execution with really nothing else changing.  I have Visual Studio set to Debug the addin by running AutoCAD 2020 and opening a specific drawing file.  After that, I type a simple command to load the .Net Addin which goes through a set of initialization procedures for the drawing (loading blocks, initializing layouts, etc), BUT will ultimately terminate with a System.AccessViolationException at some point.  I'm not saving the file or anything along the way so I'd expect two consecutive Debug Runs to terminate at the same place, but it doesn't.  Heck, I've seen it throw the Exception when simply trying to Create a new XmlSerializer based on a custom Serialized class that I KNOW works.  It doesn't make ANY sense!

Funny thing is, the addin still works fine when ran in AutoCAD 2018.

Has anyone had similar issues?

Any recommendations?!  Any feedback you can provide is TRULY appreciated.

Best Regards,
Chris


Title: Re: AutoCAD 2020 and Random System.AccessViolationExceptions
Post by: It's Alive! on March 06, 2020, 08:53:33 AM
Do you mean while the debugger is attached?
Title: Re: AutoCAD 2020 and Random System.AccessViolationExceptions
Post by: lCine7ic on March 06, 2020, 11:47:21 AM
Yes -
Do you mean while the debugger is attached?
Yes -- the System.AccessViolationException exceptions occur seemingly randomly with the Debugger attached.  Oddly enough, running the Addin without the Debugger attached allows the addin to startup / initialize completely, and then, without failing throws the System.AccessViolationException exception seemingly as soon as the program becomes quiescent.
Title: Re: AutoCAD 2020 and Random System.AccessViolationExceptions
Post by: Bryco on March 06, 2020, 07:11:03 PM
Is there any document lock in there?  As I have seen that work then not work
Title: Re: AutoCAD 2020 and Random System.AccessViolationExceptions
Post by: It's Alive! on March 06, 2020, 08:04:51 PM
Managed debugger has issues, but if your getting exceptions without the debugger attached, then its more than likely your code. I would start by adding a logger in your try..catch, (if you can catch it) output the message and the stack, this should help narrow things down.
Title: Re: AutoCAD 2020 and Random System.AccessViolationExceptions
Post by: nobody on March 08, 2020, 03:07:53 AM
Nothing to do with this but curious what framework you in?
Title: Re: AutoCAD 2020 and Random System.AccessViolationExceptions
Post by: lCine7ic on March 09, 2020, 09:33:05 AM
Nothing to do with this but curious what framework you in?

It is targeting 4.7.1.

I notice when building, the Build log includes:
Build started 3/16/2020 6:22:42 PM.
5>CoreResGen:
5>  "C:\Program Files (x86)\Microsoft SDKs\Windows\v10.0A\bin\NETFX 4.6.1 Tools\resgen.exe" /useSourcePath...

Could it be that it is using the wrong verison of resgen.exe (the NETFX 4.6.1 as opposed to the 4.7.1)?

Title: Re: AutoCAD 2020 and Random System.AccessViolationExceptions
Post by: lCine7ic on March 09, 2020, 09:49:33 AM
Managed debugger has issues, but if your getting exceptions without the debugger attached, then its more than likely your code. I would start by adding a logger in your try..catch, (if you can catch it) output the message and the stack, this should help narrow things down.
Indeed, there is a Try Catch block attempting to catch a System.Exception wrapping the code in the CommandMethod Method.
Title: Re: AutoCAD 2020 and Random System.AccessViolationExceptions
Post by: It's Alive! on March 09, 2020, 07:49:19 PM
Have you looked into the [HandleProcessCorruptedStateExceptions] attribute?
Title: Re: AutoCAD 2020 and Random System.AccessViolationExceptions
Post by: lCine7ic on March 10, 2020, 01:15:03 PM
Have you looked into the [HandleProcessCorruptedStateExceptions] attribute?

No -- I will look into this - thank you for the suggestion!
Title: Re: AutoCAD 2020 and Random System.AccessViolationExceptions
Post by: lCine7ic on March 16, 2020, 06:20:06 PM
Have you looked into the [HandleProcessCorruptedStateExceptions] attribute?

No -- I will look into this - thank you for the suggestion!

I added the [HandleProcessCorruptedStateExceptions] and the [SecurityCritical] Attributes to my Command (which the contents are entirely wrapped in a try / catch [System.Exception] block) but that does not seem to help.
Title: Re: AutoCAD 2020 and Random System.AccessViolationExceptions
Post by: It's Alive! on March 30, 2020, 12:37:38 AM
If you can find a way to post a sample solution that reproduces the issue, someone might be able to spot the problem.
Title: Re: AutoCAD 2020 and Random System.AccessViolationExceptions
Post by: lCine7ic on April 03, 2020, 09:30:15 AM
If you can find a way to post a sample solution that reproduces the issue, someone might be able to spot the problem.

It is WAYYY to large to generalize ;)

So I uninstalled / re-installed AutoCAD 2020 and discovered something weird.

When I run my addin w/o the debugger, I now get an 'AutoCAD Error Aborting' window with a 'Fatal Error: Unhandled Access Violation' popup.  However, as long as I do NOT click 'OK' to close the pop-up, I can still use AutoCAD.  My ToolTip handlers work, my custom commands work, I can switch between various paperspace tabs...

This REALLY leads me to believe that the problem is w/ perhaps one of the new AutoCAD 2020 features --  maybe related to cloud storage which I am not using but is perhaps putting some sort of exclusive document lock.

It didn't behave this way before the re-install; before it would immediately go straight to the AutoDesk Error reporting app.
Title: Re: AutoCAD 2020 and Random System.AccessViolationExceptions
Post by: lamarn on April 03, 2020, 12:44:58 PM
"..features --  maybe related to cloud storage which I am not using but is perhaps putting some sort of exclusive document lock."

Sound exactly like the new feature that was released for testing. Got that request by several emails.
Title: Re: AutoCAD 2020 and Random System.AccessViolationExceptions
Post by: lCine7ic on April 03, 2020, 12:55:53 PM
"..features --  maybe related to cloud storage which I am not using but is perhaps putting some sort of exclusive document lock."

Sound exactly like the new feature that was released for testing. Got that request by several emails.

Wow... if there was only some way to check -- or at least disable those features.
Title: Re: AutoCAD 2020 and Random System.AccessViolationExceptions
Post by: lamarn on April 03, 2020, 02:55:25 PM
This was a message

"
BIM 360 Plugin Survey:

If you do not want to take the very short survey, and have access to BIM 360, here is the download location of the plugin.

AutoCAD File Locking Plugin for BIM 360"

A possibility to provide feedback is available. Please check


Title: Re: AutoCAD 2020 and Random System.AccessViolationExceptions
Post by: lCine7ic on October 19, 2020, 04:33:58 PM
All,

I know this is an old topic, but I've recently had the opportunity to do a bit more digging and have some more leads.  If you have the time, please take a look and let me know if there is a solution.

I enabled the extended debugger features and noticed that the failure always occured in thread other than the 'MAIN' thread.  The methods being called are:
Acdbmgd.dll!Autodesk.AutoCAD.DatabaseServices.DynamicBlockReferencePropertyCollection.DeleteUnmanagedObject()
Acdbmgd.dll!<Module>.AcArray<AcDbDynBlockReferenceProperty,AcArrayObjectCopyReallocator<AcDbDynBlockReferenceProperty> >.setPhysicalLength(AcArray<AcDbDynBlockReferenceProperty,AcArrayObjectCopyReallocator<AcDbDynBlockReferenceProperty> >*, int)  - < crash occurs in this method;

This made me think it was a Dynamic Block Issue. 
I removed the vast majority of the dynamic blocks that were imported into the drawing at startup, leaving only 1.  CRASH.
I created a static version of the remaining Dynamic block and ran the plugin.  No CRASH.
I replaced the static version back with the Dynamic block and ran the plugin.  CRASH.
I swapped out this dynamic block with another dynamic block (still only importing 1).  CRASH.
Before running the addin, I inserted the Dynamic Block and so that it had 1 active (non-purged) Block Reference and ran the Addin.  NO CRASH
Before running the addin, I inserted the Dynamic Block and so that it had 1 active (non-purged) Block Reference.  I then deleted the Block Reference but did not purge.  I then ran the Addin.  CRASH.

If you have any ideas on how to resolve this, please let me know.

Best Regards,
Chris

Title: Re: AutoCAD 2020 and Random System.AccessViolationExceptions
Post by: nobody on October 19, 2020, 06:21:45 PM
I don't know if it will help you but you could try the assembly binding log viewer tool

https://docs.microsoft.com/en-us/previous-versions/dotnet/netframework-1.1/e74a18c4(v=vs.71)?redirectedfrom=MSDN
Title: Re: AutoCAD 2020 and Random System.AccessViolationExceptions
Post by: lCine7ic on October 29, 2020, 11:36:41 AM
Thank you for the suggestion.

I figured out where the problem lies, and a crude (not ideal) work around.


Code - Visual Basic: [Select]
  1. Dim TargetDocument As Document = Application.DocumentManager.MDIActiveDocument
  2. Dim TargetDocumentDB As Database = TargetDocument.Database
  3. Using dockLock As DocumentLock = TargetDocument.LockDocument()
  4.      Using transOuterMost As Transaction = TargetDocumentDB.TransactionManager.StartTransaction()
  5.           ...
  6.          
  7.                 ' INSIDE BlockDef Class
  8.                '**The purpose of the BlockDef class is so that other classes that work with BlockReferences based on each block can do quick lookups against the relevant BlockDef's dictionaries to access/modify the AttributeReferences or DynamicBlockReferenceProperties w/o enumerating each time.
  9.                ' At this Point, the block has been imported into the TargetDocument.
  10.                'newBTR: BlockTableRecord of Block Recently Added to TargetDocument
  11.                'idBlockTableRecord: ObjectId of BlockTableRecord recently added to BlockTable of TargetDocument's Database
  12.                'AttIdsByTag: Dictionary with Non-Constant Attribute Reference Tag as key, AttributeDefinition ObjectId as value
  13.                'AttIdxByTag: Dictionary with Non-Constant Attribute Reference Tag as key, AttributeDefinition 0-based Index (not counting constant) as value
  14.                'DynamicPropertyIndexByName: Dictionary with DynamicBlockReferenceProperty PropertyName as Key (skipping "ORIGIN"), and 0-based Index as value)
  15.                Using oTrans As Transaction = idBlockTableRecord.Database.TransactionManager.StartTransaction()
  16.                     Dim eBRE As IEnumerator = newBTR.GetEnumerator
  17.                     Dim iIndex As Integer = 0
  18.                     Dim oEnt As Entity
  19.                     Dim oAD As AttributeDefinition
  20.                     While eBRE.MoveNext
  21.                         oEnt = DirectCast(oTrans.GetObject(eBRE.Current, OpenMode.ForRead), Entity)
  22.                         If TypeOf oEnt Is AttributeDefinition Then
  23.                             oAD = DirectCast(oEnt, AttributeDefinition)
  24.                             If Not oAD.Constant Then
  25.                                 doc.WriteLine("Adding NonConstant Attribute:{Tag:'" + oAD.Tag + "', Index:" + iIndex.ToString + ", ObjectId:" + HexIdVal(oAD.ObjectId) + "} to Dictionaries")
  26.                                 AttIdsByTag.Add(oAD.Tag, oAD.ObjectId)
  27.                                 AttIdxByTag.Add(oAD.Tag, iIndex )
  28.                                 iIndex += 1
  29.                             End If
  30.                         End If
  31.                     End While
  32.                     If newBTR.IsDynamicBlock Then
  33.                         iIndex = 0
  34.                         'Cause of Failures since AutoCAD 2020.  If This Section (lines 35 through 47) is Commented out, no issues.
  35.                        Using tempBR As New BlockReference(Point3d.Origin, idBlockTableRecord)
  36.                                 For Each cDBRP As DynamicBlockReferenceProperty In tempBR.DynamicBlockReferencePropertyCollection
  37.                                     iIndex += 1
  38.                                     If cDBRP.PropertyName.ToUpper = "ORIGIN" Then
  39.                                         Debug.WriteLine("Skipping Index " + iIndex.ToString + ", <ORIGIN>")   ' < Updated per n.yaun's post immediately below
  40.                                        Continue For
  41.                                     End If
  42.                                     DynamicPropertyIndexByName.Add(cDBRP.PropertyName.ToUpper, iIndex)   ' < Updated per n.yaun's post immediately below
  43.                                    doc.WriteLine("Adding DynamicBlockReferenceProperty:{PropertyName.ToUpper():'" + cDBRP.PropertyName.ToUpper + "', Index:" + iIndex.ToString + "} to Dictionaries")
  44.                                 Next
  45.                             tempBR.Dispose()
  46.                         End Using
  47.                         'End Cause of Failures.  
  48.                    End If
  49.                 End Using
  50.         ...
  51.         Try
  52.             transOuterMost.Commit()
  53.         Catch ex As System.Exception
  54.  
  55.         End Try
  56.     End Using
  57. End Using
  58.  
  59.  
  60.  

My crude workaround is to statically define the Indices of the Dynamic Block Reference Properties I need in the classes that uses them... I will regret this later I know  :straight:



MODIFICATION: Corrected simplified code insert based on n.yuan's post below
Title: Re: AutoCAD 2020 and Random System.AccessViolationExceptions
Post by: n.yuan on October 30, 2020, 10:11:07 AM
Code: [Select]
Thank you for the suggestion.

I figured out where the problem lies, and a crude (not ideal) work around.

...

                    If newBTR.IsDynamicBlock Then
                        iIndex = 0
                        ...
                                Dim iTemp As Integer = 0
                                For Each cDBRP As DynamicBlockReferenceProperty In tempBR.DynamicBlockReferencePropertyCollection
                                    iIndex += 1
                                    If cDBRP.PropertyName.ToUpper = "ORIGIN" Then
                                        Debug.WriteLine("Skipping Index " + iTemp.ToString + ", <ORIGIN>")
                                        Continue For
                                    End If
                                    DynamicPropertyIndexByName.Add(cDBRP.PropertyName.ToUpper, iTemp)
                                    doc.WriteLine("Adding DynamicBlockReferenceProperty:{PropertyName.ToUpper():'" + cDBRP.PropertyName.ToUpper + "', Index:" + iIndex.ToString + "} to Dictionaries")
                                Next
                        ...
 
                    End If


My crude workaround is to statically define the Indices of the Dynamic Block Reference Properties I need in the classes that uses them... I will regret this later I know  :straight:

I assume DynamicPropertyIndexByName is a Dictionary<string, int>, where string key is dynamic property name, and int value is what you are to use later (looking up as you mentioned). However, I do not see why you need the Dictionary for looking up, because the value of every item in the dictionary is the same: 0, according to your code in the foreach(){...} loop, where you set iTemp=0 before the loop, and never change its value during the loop where it is added into the Dictionary. I am not sure if it is intentional, or it is code mistake (of forgetting to change/increment its value).

Could this be the reason of the crash? Imagine that if the dynamic blocks you worked on only have one dynamic property (or 2, but one of it is named "ORIGIN", thus does not count here), the dictionary would only have 1 item, therefore there would be no problem for later lookup. But if there are more dynamic property name collected in the dictionary but all have its value equal 0, then the later lookup might cause trouble (have no idea what the later process doing here), hence the crash. More often than not, even it appears as random crash, 99.9% would come from our own code mistake.

Title: Re: AutoCAD 2020 and Random System.AccessViolationExceptions
Post by: lCine7ic on October 30, 2020, 10:33:49 AM
Code: [Select]
Thank you for the suggestion.

I figured out where the problem lies, and a crude (not ideal) work around.

...

                    If newBTR.IsDynamicBlock Then
                        iIndex = 0
                        ...
                                Dim iTemp As Integer = 0
                                For Each cDBRP As DynamicBlockReferenceProperty In tempBR.DynamicBlockReferencePropertyCollection
                                    iIndex += 1
                                    If cDBRP.PropertyName.ToUpper = "ORIGIN" Then
                                        Debug.WriteLine("Skipping Index " + iTemp.ToString + ", <ORIGIN>")
                                        Continue For
                                    End If
                                    DynamicPropertyIndexByName.Add(cDBRP.PropertyName.ToUpper, iTemp)
                                    doc.WriteLine("Adding DynamicBlockReferenceProperty:{PropertyName.ToUpper():'" + cDBRP.PropertyName.ToUpper + "', Index:" + iIndex.ToString + "} to Dictionaries")
                                Next
                        ...
 
                    End If


My crude workaround is to statically define the Indices of the Dynamic Block Reference Properties I need in the classes that uses them... I will regret this later I know  :straight:

I assume DynamicPropertyIndexByName is a Dictionary<string, int>, where string key is dynamic property name, and int value is what you are to use later (looking up as you mentioned). However, I do not see why you need the Dictionary for looking up, because the value of every item in the dictionary is the same: 0, according to your code in the foreach(){...} loop, where you set iTemp=0 before the loop, and never change its value during the loop where it is added into the Dictionary. I am not sure if it is intentional, or it is code mistake (of forgetting to change/increment its value).

Could this be the reason of the crash? Imagine that if the dynamic blocks you worked on only have one dynamic property (or 2, but one of it is named "ORIGIN", thus does not count here), the dictionary would only have 1 item, therefore there would be no problem for later lookup. But if there are more dynamic property name collected in the dictionary but all have its value equal 0, then the later lookup might cause trouble (have no idea what the later process doing here), hence the crash. More often than not, even it appears as random crash, 99.9% would come from our own code mistake.

n.yuan: You're absolutely correct about the indices all being 0 -- truth is, what I showed is not my actual code.  I tried to simplify my actual code and clearly failed to do a sanity check.  The "real" code actually does assign the correct index.  I'll update the other post to reflect the correction. 

The issue is not with the dictionary; in fact, the dictionary is only used when there is a block reference added to the drawing that is actually used, and the failure occurs even when there isn't one.

When it fails, the thread containing the failure shows the following callstack.
Acdbmgd.dll!<Module>.AcArray<AcDbDynBlockReferenceProperty,AcArrayObjectCopyReallocator<AcDbDynBlockReferenceProperty> >.setPhysicalLength(AcArray<AcDbDynBlockReferenceProperty,AcArrayObjectCopyReallocator<AcDbDynBlockReferenceProperty> >*, int) < Failure occurs here
Acdbmgd.dll!Autodesk.AutoCAD.DatabaseServices.DynamicBlockReferencePropertyCollection.DeleteUnmanagedObject()
Acdbmgd.dll!Autodesk.AutoCAD.DatabaseServices.BlockTableRecordEnumerator.DeleteUnmanagedObject()
Acdbmgd.dll!Autodesk.AutoCAD.Runtime.DisposableWrapper.~DisposableWrapper()

Again, this code has worked in previous versions of AutoCAD (AutoCAD 2013, 2016, 2018); it only started failing in AutoCAD 2020.
I'm wondering if the problem has to do changes in the native code that help implement the updated 'INSERT' and 'PURGE' commands.
Title: Re: AutoCAD 2020 and Random System.AccessViolationExceptions
Post by: lCine7ic on November 16, 2021, 12:07:36 PM
Just an update.  I was able to reuse the code below still in 2020 -- the issue seemed to be with the 'Continue For'.  Not sure why it's causing problems but be warned.