The Leader entity drawn in C# does not follow the movement of the MText entity

It is translated from Japanese to English at DeepL.

Hi, all.

since V21, the Leader entity drawn in C# does not follow the movement of the MText entity. With the same code, there is no problem in V16 to V20.

  • OS: Windows 10 Pro 64bit 20H2
  • BricsCAD: V21.1.07
  • Development Environment: Microsoft Visual Studio Community 2019
  • Framework: .NET Framework 4.8
  1. run BricsCAD V21 in Debug mode of VS2019(Load the build CsBrxMgdTest21.dll with NETLOAD).
  2. execute the test command("Test2021DrawLeader") to draw the Leader and MText entities.
  3. Select the MText entity that you drew.
  4. Select the MText entity you drew and move it with the grip stretch.
  5. The position of the Leader entity is not automatically updated.

The following message is displayed in the output window after BricsCAD is closed.

Forgot to call Dispose? (Teigha.DatabaseServices.BlockTableRecord): DisposableWrapper
Forgot to call Dispose? (Teigha.DatabaseServices.BlockTable): DisposableWrapper
Forgot to call Dispose? (Teigha.DatabaseServices.Leader): DisposableWrapper
Forgot to call Dispose? (Teigha.DatabaseServices.MText): DisposableWrapper

This issue has been occurring since V21, but not in V16 to V20.

The state of the IsDisposed property after Transaction.

---- IsDisposed ----
-> BlockTable: False
-> BlockTableRecord: False
-> Leader: False
-> MText: False

In V20, IsDisposed is set to True.

---- IsDisposed ----
-> BlockTable: True
-> BlockTableRecord: True
-> Leader: True
-> MText: True

In V21, if I Dispose after Commit, the Leader entity position is automatically updated.
Is this a bug in "Transaction" class ? specification change?

---- Test2021DrawLeader ----

[CommandMethod("Test2021DrawLeader")]
public static void Test2021DrawLeader()
{
    _AcAp.Document doc = _AcAp.Application.DocumentManager.MdiActiveDocument;
    _AcDb.Database db = doc.Database;
    _AcEd.Editor edit = doc.Editor;

    try
    {
        using (_AcDb.Transaction trans = db.TransactionManager.StartTransaction())
        {
            BlockTable bt = trans.GetObject(db.BlockTableId, OpenMode.ForRead) as BlockTable;
            BlockTableRecord btr = trans.GetObject(bt[BlockTableRecord.ModelSpace], OpenMode.ForWrite) as BlockTableRecord;

            PromptPointOptions ppo1 = new PromptPointOptions("point1: ");
            PromptPointResult ppr1 = edit.GetPoint(ppo1);

            if (ppr1.Status != PromptStatus.OK)
            {
                return;
            }

            Point3d p1 = ppr1.Value;

            PromptPointOptions ppo2 = new PromptPointOptions("point2: ")
            {
                UseBasePoint = true,
                UseDashedLine = true,
                BasePoint = p1,
                AllowNone = true
            };

            PromptPointResult ppr2 = edit.GetPoint(ppo2);

            if (ppr2.Status != PromptStatus.OK)
            {
                return;
            }

            Point3d p2 = ppr2.Value;

            PromptResult pr = edit.GetString("\ntext: ");

            if (pr.Status != PromptStatus.OK)
            {
                return;
            }

            MText mtxt = new MText
            {
                Contents = string.IsNullOrEmpty(pr.StringResult) ? "ABC" : pr.StringResult,
                Location = p2
            };
            mtxt.SetDatabaseDefaults(db);
            ObjectId mtxtId = btr.AppendEntity(mtxt);
            trans.AddNewlyCreatedDBObject(mtxt, true);

            Leader ldr = new Leader();
            ldr.SetDatabaseDefaults(db);
            ldr.AppendVertex(p1);
            ldr.AppendVertex(p2);
            btr.AppendEntity(ldr);
            trans.AddNewlyCreatedDBObject(ldr, true);
            ldr.Annotation = mtxtId;
            ldr.EvaluateLeader();

            trans.Commit();

            edit.WriteMessage("---- IsDisposed ----\n");
            edit.WriteMessage("-> BlockTable: {0}\n", bt.IsDisposed);
            edit.WriteMessage("-> BlockTableRecord: {0}\n", btr.IsDisposed);
            edit.WriteMessage("-> Leader: {0}\n", ldr.IsDisposed);
            edit.WriteMessage("-> MText: {0}\n", mtxt.IsDisposed);
            edit.WriteMessage("\n");

            //mtxt.Dispose();
            //ldr.Dispose();
            //btr.Dispose();
            //bt.Dispose();
        }
    }
    finally
    {
        edit.Regen();
    }
}

Comments

  • Hello all,
    I have a problem very similar to Yamanoi and what Yamanoi describes is completely in line with my observations. For our application we use BricsCAD 18, 19, 20 and finally also the 21. I confirm that this problem only exists with BircsCAD 21. Yesterday I installed BricsCAD-V21.1.08-1-de_DE (x64) .msi and this problem persists.
    I became aware of the problem a few days ago when I saw that in the case of drawings with many elements, the Visual Studio output window filled with thousands of messages such as' Forgot to call Dispose? (Teigha.DatabaseServices.DBDictionary): DisposableWrapper '.
    When it comes to disposing of net objects I follow the advice of Daniel Marcotte to stick with the Transaction pattern, which I found here: https://forum.bricsys.com/discussion/28810/transactions-and-disposing-of-net-objects
    I was particularly astonished that these messages also come for objects that I only access for reading. None of these objects were created with the new keyword.

  • Starting in V21 there was a change in the implementation of GetObject(). Before V21, the objects returned by GetObject() had AutoDelete=true and were forcefully disposed when the transaction ended. This forced dispose created an incompatibility with the Acad platform that can be a nuisance for ported code. To address the incompatible behavior, V21 still returns the objects with AutoDelete=true but removed the forced dispose when a transaction ends. Unfortunately that results in many objects not deterministically disposed, and left for the garbage collector.

    I expect that V22 will address this new problem by returning objects from GetObject() as weak references with AutoDelete=false, fully compatible with Acad. In the meantime, V21.2 should improve the situation by performing more frequent garbage collection. This will not eliminate the "forgot to dispose?" warnings, but it will reduce the problems associated with leaving AutoDelete=true objects alive for an unknown time waiting on garbage collection.

  • Hello Owen Wengerd,
    Thanks for Your quick reply. I find it comforting that the cause of the "forgot to dispose?" warnings were not in my implementation.
    Your explanations regarding the causes also make sense in connection with my observations. The warnings in my application come with a delay which, as you say, is explained by the use of the garbage collector.
    Since I often have to deal with a large number of elements in a drawing, these messages come in thousands and this causes a noticeable slowdown in execution. During this time, Visual Studio is almost exclusively occupied with issuing these warnings. This sometimes takes several seconds until the whole thing calms down.
    The code for my application is used for BricsCAD 18, 19, 20 and 21 and also for AutoCAD 18, 19, 20 and 21, which is why I am not unfamiliar with the problems with the compatibility of AutoCAD to BricsCAD.
    These warnings suggest that I should take care of disposing these objects. From my point of view, however, I didn't create the objects, so disposing these objects shouldn't be my business either. When it comes to BricsCAD's compatibility with AutoCAD,
    In the same situation, I don't get such warnings in AutoCAD when it comes to disposing these objects.
    Since I don't know of any way to suppress these warnings in Visual Studio in order to get rid of this delay, I wonder whether it would not be possible to omit these warnings for objects that are generated by GetObject().
    That would be a fine thing and would make it much easier to use.

  • Skipping those warnings only for objects returned by GetObject() is not possible, and turning them off altogether is not good either. As the delays you mention are only affecting you while running under the debugger, I would rather focus on fixing the root cause.

  • Arti
    edited March 2021

    Hello Owen Wengerd,
    thank you for your valuable information.
    I followed your advice and managed to reduce the delay while debugging by 95 percent with just a few changes in the code. Therefore the delay is no longer noticeable. I'm looking forward to V22, where the changes you mentioned regarding returning objects from GetObject() as weak references should come.

  • Hello Owen Wengerd.
    thank you for the information.

    I have a lot of other things to investigate in V21, including performance issues, but the transaction thing was very helpful.

  • The GetObject() change was part of a major refactoring of transactions by ODA. After this refactoring there was discovered a performance issue with transactions containing many objects. That transaction performance issue was addressed in V21.1.08.