Author Topic: Fatal errors when processing entities that are part of a group.  (Read 1983 times)

0 Members and 1 Guest are viewing this topic.

Atook

  • Swamp Rat
  • Posts: 1029
  • AKA Tim
Fatal errors when processing entities that are part of a group.
« on: October 09, 2020, 02:58:02 AM »
I'm seeing a fatal error when processing objects. It happens intermittently when I access certain objects, which I'm suspecting are always part of a group. In the attached drawing, I've pointed out the object I can replicate on.

This code crashes when processing the polyline in the attached drawing
Code - C#: [Select]
  1. public static Handle GetHandle(ObjectId id)
  2. {
  3.   Handle answer = new Handle();
  4.   if (id == ObjectId.Null) return answer;
  5.   Document doc = Autodesk.AutoCAD.ApplicationServices.Core.Application.DocumentManager.MdiActiveDocument;
  6.   Database db = doc.Database;
  7.   using (Transaction acTr = db.TransactionManager.StartTransaction())
  8.   {
  9.     var obj = acTr.GetObject(id, OpenMode.ForRead);
  10.     answer = obj.Handle;
  11.     Debug.WriteLine($"Handle: {answer.ToString()}");
  12.     acTr.Commit();//<-sometimes if the object is part of a group, I get a Fatal Error here and CAD crashes.
  13.                   // An unhandled exception of type 'System.AccessViolationException' occurred in accoremgd.dll
  14.                   // Additional information: Attempted to read or write protected memory.
  15.                   // This is often an indication that other memory is corrupt.
  16.   }
  17.  
  18.   return answer;
  19. }

I'm starting to suspect that the way I create the groups is suspect.
The way I create the group is:
Code - C#: [Select]
  1. /// <summary>
  2. /// Creates a group with the name as prefix followed by a number
  3. /// </summary>
  4. /// <param name="ents"></param>
  5. /// <param name="prefix"></param>
  6. public static void CreateAnonymousGroup(ObjectIdCollection groupEntIDs, string prefix)
  7. {
  8.   if (!groupEntIDs.Contains(ObjectId.Null))
  9.   {
  10.     Document doc = Autodesk.AutoCAD.ApplicationServices.Core.Application.DocumentManager.MdiActiveDocument;
  11.     Database db = doc.Database;
  12.     using (Transaction tr = db.TransactionManager.StartTransaction())
  13.     {
  14.       DBDictionary groupDictionary = (DBDictionary)tr.GetObject(db.GroupDictionaryId, OpenMode.ForWrite, true);
  15.       int number = groupDictionary.Count;
  16.       string groupName = prefix + number;
  17.       if (IsValidSymbolName(groupName))
  18.       {
  19.         while (groupDictionary.Contains(groupName))// Don't allow duplicate groupName
  20.         {
  21.           number++;
  22.           groupName = prefix + number;
  23.         }
  24.         // presumably we can add it now.
  25.         Group grp = new Group(groupName, true);
  26.         groupDictionary.SetAt(groupName, grp);
  27.         tr.AddNewlyCreatedDBObject(grp, true);
  28.         grp.InsertAt(0, groupEntIDs);
  29.       }
  30.       tr.Commit();
  31.     }
  32.   }
  33. }

Any ideas where my mistake lies?
« Last Edit: October 09, 2020, 03:11:45 AM by Atook »

MickD

  • King Gator
  • Posts: 3636
  • (x-in)->[process]->(y-out) ... simples!
Re: Fatal errors when processing entities that are part of a group.
« Reply #1 on: October 09, 2020, 05:42:21 AM »
All I can think of is to try using a for loop and append entities rather than use insert, at least as a test. I've used groups for years with no trouble like that.

The help on Group.InsertAt is a little cryptic in that it says:
Quote
Inserts the objects whose objectIds are in the ids collection into the group just after the entry at index. The indices are zero based. Hence the first element in the group will have an index of 0.

That sounds to me like you need to find the end of the group entries and add after that index and if the '0' index is null well...

I also set groups as 'Selectable' as well but I wouldn't think that would make much difference when querying the db directly.
hth
"Programming is really just the mundane aspect of expressing a solution to a problem."
- John Carmack

"Short cuts make long delays,' argued Pippin.”
- J.R.R. Tolkien

n.yuan

  • Bull Frog
  • Posts: 348
Re: Fatal errors when processing entities that are part of a group.
« Reply #2 on: October 09, 2020, 11:42:43 AM »
I ran your GetHandle() method against the polyline with handle 2D44 with following code:

Code: [Select]
public static void TestCrash()
        {
            var dwg = Application.DocumentManager.MdiActiveDocument;
            var ed = dwg.Editor;
            var res = ed.SelectAll(
                new SelectionFilter(
                    new Autodesk.AutoCAD.DatabaseServices.TypedValue[]
                    {
                        new Autodesk.AutoCAD.DatabaseServices.TypedValue((int)DxfCode.LayoutName, "MODEL")
                    }));
            if (res.Status== PromptStatus.OK)
            {
                foreach (ObjectId id in res.Value.GetObjectIds())
                {
                    if (id.Handle.ToString().ToUpper()=="2D44")
                    {
                        var h = GetHandle(id); //This is your code
                    }
                }
            }
        }

I did not get crash. Since you say it is intermittently, I guess you have not figured out a consistent way to reproduce it. However I do have a couple of issues here:

1. What is the purpose of the method GetHandle(ObjectId)? Maybe it is only here for testing to prove that particular ObjectId of Polyline in Group is the source of crash when the Transaction is to be commited? Because the Method itself does not make any business sense: if you have an ObjectId in memry, then you already know its Handle. So, I think you just make it up to prove the crashing and no really use it in any real work. But I ask anyway.

2. When I see code like GetHandle(), where an ObjectId is passed as parameter, and yet inside the method (which is supposed to be a black box to outside caller) the code still picks up MdiActiveDocument for the operation database, I feel quite uncomfortable: the ObjectId already provides a path to the database where the ObjectId is valid, why do you need to tie the method to MdiActiveDocument's database? This would effectively limited this method can only be used with MdiActiveDocument, not in general of any opening database, such as a side database you code make run against. That is, you could make the method useful in either MdiActiveDocument, or side database, if you do it like this:

Public static void DoSomething AgainstEntity(ObjectId entId)
{
    using (var tran = entId.Database.TransactionManager.StartTransaction())
    {
        ... ...
        tran.Commit();
    }
}

This also makes me wonderring: are you, maybe, get the erro when calling the GetHandle() method from side database processing by any chance?
« Last Edit: October 09, 2020, 11:46:00 AM by n.yuan »

Atook

  • Swamp Rat
  • Posts: 1029
  • AKA Tim
Re: Fatal errors when processing entities that are part of a group.
« Reply #3 on: October 14, 2020, 01:02:18 AM »
MickD: thanks for the input on group creation, I think appending is more intuitive than inserting at 0, I think I'll change to that method.

Norman: I use handles to link drawing objects together. For instance, a pipe (polyline) and a size label(mleader). If the size of the pipe changes, I can grab the handle of the Mleader associated with it (it's written on the pipe polyline) the mleader to show the new size.

As far as grabbing the TransactionManager from the active database.. I use it because it's what I found in a bunch of samples, and I use it everywhere.  :oops:

I appreciate you pointing out the obvious method of using the database attached to that ObjectID. I haven't seen this before, why is this not used more? Seems like the perfect solution for a function that acts on a single ObjectID. I think I'll transition most of my functions that act on an ObjectID to use this transaction manager.

The crash in question isn't being called from a side database, I struggled with side databases for a bit, and try not to use them. That might change with your insight. :)


n.yuan

  • Bull Frog
  • Posts: 348
Re: Fatal errors when processing entities that are part of a group.
« Reply #4 on: October 14, 2020, 09:28:12 AM »
To reiterate my points:

1. The said entity (handle 2D44) does not cause crash with the code as you showed in GetHandle() with my AutoCAD2021, while it is in the group created by your code;

2. If the GetHandle() method is meant as you said, then you do not need it: you have already had the ObjectId, hence had the access to Handle by ObjectId.Handle property, why do you need to open the entity in transaction by the ObjectId and then get its Handle?

3. Yes, I also saw lot of code samples of getting hold of MdiActiveDocument in beginning of each method, regardless what is passed in as parameter. In AutoCAD programming, while in majority of cases, the code is dealing with MdiActiveDocument, but it does not mean that introduce unnecessary dependency to a method, which is supposed to be a black box to its caller, is good practice.

Example 1:
public void DoSomethingWithEntity(ObjectId entId)
{
    var db=entId.Database;
    // do whatever with the database, the entity
}

Example 2:
public void DoSomethingWithEntity(ObjectId entId)
{
    var db=Application.DocumentManager.MdiActiveDocument.Database;
    // do whatever with the database, the entity
}

Obviously the method of example 1 can be used against different drawing: mdiActiveDocument, side database, or drawing opened, but not MdiActive ones (opened drawing with command without session flag); while the method in second example can only be used with MdiActiveDocument. So, the first method is more generic, a good programming practice.

Atook

  • Swamp Rat
  • Posts: 1029
  • AKA Tim
Re: Fatal errors when processing entities that are part of a group.
« Reply #5 on: October 14, 2020, 11:16:48 AM »
Thanks for the followup Norman.

Looks like I was mistaken what's causing the crash. When I duplicate the error in debug mode, that's where the debugger breaks out, on committing the transaction. I must have some errors in my architecture that are causing issues in downstream functions.  I've got some other issues that might be related. I'm not sure how to begin chasing the errors down. I appreciate you taking the time to run the function in isolation (from the stacktrace in my app) I will try to do that before posting here next time.

Thanks for the pointer on reducing the dependency on the active document, as well as creating a more generic function. That certainly looks like better code to me.