Author Topic: How to get Entities in a Block.  (Read 18484 times)

0 Members and 1 Guest are viewing this topic.

tik

  • Guest
How to get Entities in a Block.
« on: January 11, 2012, 11:23:45 AM »
This should be very straight forward but I am having hard time to get it to work. I  wrote the following sample code but I am having issues, I am hoping there is a easier way to do than my way.

In my code I explode the block reference for the block, I can see all the entities in the collection but entities doesn't have id's in them or XData for each entity.

I really appreciate if someone can point me in the right direction.

Thanks in advance.

<Code>
public GetEntities( string ThisBlock)
{
           ObjectId blockId =  GetObjectId(ThisBlock);

           DBObject[] ThisBlockEntities = GetBlockEntities(blockId);

            DBObjectCollection entitycoll = new DBObjectCollection();

            BlockReference blkRef = ThisBlockEntities[0] as BlockReference;
            if (blkRef != null)
            {
                blkRef.Explode(entitycoll);
            }

            for (ListCount = 0; ListCount < entitycoll.Count; ListCount++)
            {
                 // entitycoll has all the entities but they dont have ids in them.             
            }
}

//This method return the objectid for the block based on the name.
public ObjectId GetObjectID(string name)
        {
            // Get the current document and database, and start a transaction
            Document acDoc = Application.DocumentManager.MdiActiveDocument;
            Database acCurDb = acDoc.Database;
            ObjectId id = new ObjectId();
            // Starts a new transaction with the Transaction Manager
            using (Transaction acTrans = acCurDb.TransactionManager.StartTransaction())
            {
                // Open the Block table record for read
                BlockTable acBlkTbl;
                BlockTableRecord acBlkTblRec;
                acBlkTbl = acTrans.GetObject(acCurDb.BlockTableId, OpenMode.ForRead, false) as BlockTable;
                if (acBlkTbl != null)
                {
                    if (acBlkTbl.Has(name))
                    {
                        acBlkTblRec = acTrans.GetObject(acBlkTbl[BlockTableRecord.ModelSpace], OpenMode.ForRead) as BlockTableRecord;
                        if (acBlkTblRec != null)
                        {
                            id = acBlkTblRec.Id;
                        }
                    }
                }
                acTrans.Commit();
            }
            return id;
        }

// This method is returning a block reference
public DBObject[] GetBlockEntities(ObjectId blockId)
        {
            // Get the current document and database, and start a transaction
            Document acDoc = Application.DocumentManager.MdiActiveDocument;
            Database acCurDb = acDoc.Database;
            DBObject[] dbObjectCollection = null;
            DBObjectCollection coll = new DBObjectCollection();
            using (Transaction acTrans = acCurDb.TransactionManager.StartTransaction())
            {
                // Open the Block table record Model space for read
                BlockTableRecord acBlkTblRec;
                acBlkTblRec = acTrans.GetObject(blockId, OpenMode.ForRead) as BlockTableRecord;
                int nCnt = 0;
                //acDoc.Editor.WriteMessage("\nModel space objects: ");
                // Step through each object in Model space and// display the type of object found
                foreach (ObjectId acObjId in acBlkTblRec)
                {
                    coll.Add(acTrans.GetObject(acObjId, OpenMode.ForRead) as DBObject);
                }
                dbObjectCollection = new DBObject[coll.Count];
                coll.CopyTo(dbObjectCollection, 0);
            }
            return dbObjectCollection;
        }
</Code>

kaefer

  • Guest
Re: How to get Entities in a Block.
« Reply #1 on: January 11, 2012, 12:38:39 PM »
In my code I explode the block reference for the block, I can see all the entities in the collection but entities doesn't have id's in them

They are clones of those contained in the BlockTableRecord and haven't been added to the database. That's why there's no valid ObjectId.

If you create new non-database-resident objects, you are responsible to Dispose() them. Alternatively, you could wrap the DBObjectCollection itself in a using statement, which would take care of disposing the objects contained when leaving its scope.

tik

  • Guest
Re: How to get Entities in a Block.
« Reply #2 on: January 11, 2012, 12:55:59 PM »
Kaefer,

Thanks for the response,I will add a using around my collection. So is there some way to get actual entities instead of the clones.

regards
tik.

Jeff H

  • Needs a day job
  • Posts: 6150
Re: How to get Entities in a Block.
« Reply #3 on: January 11, 2012, 01:20:10 PM »
Have you tried looking at the entites in the BlockTableRecord(Block Definition) of the BlockReference?

tik

  • Guest
Re: How to get Entities in a Block.
« Reply #4 on: January 11, 2012, 03:45:03 PM »
Jeff,
Thanks for the reply, how can I get the entities from the BlockTableRecord, do I need to explode BlockTableRecord to get the entities ?

tik.

Jeff H

  • Needs a day job
  • Posts: 6150
Re: How to get Entities in a Block.
« Reply #5 on: January 11, 2012, 04:41:19 PM »
You can use BlockReference.BlockTableRecord Property
For dynamics blocks you might want the dynamic block def  etc.........
 
Are we using the same terminology?
It might make it easier to never say the word block unless followed by 'Definition', 'Reference', 'TableRecord', etc....
 
Code: [Select]
[CommandMethod("BlockDefinition")]
        public void BlockDefinition()
        {
            Document doc = Application.DocumentManager.MdiActiveDocument;
            Database db = doc.Database;
            Editor ed = doc.Editor;

            using (Transaction trx = db.TransactionManager.StartTransaction())
            {
                PromptEntityOptions peo = new PromptEntityOptions("\nSelect Block");
                peo.SetRejectMessage("Not a Block");
                peo.AddAllowedClass(typeof(BlockReference), true);
                PromptEntityResult per = ed.GetEntity(peo);
                if (per.Status != PromptStatus.OK)
                {
                    return;
                }
                BlockReference bref = trx.GetObject(per.ObjectId, OpenMode.ForRead) as BlockReference;
                BlockTableRecord btr = (BlockTableRecord)trx.GetObject(bref.BlockTableRecord, OpenMode.ForRead);
                foreach (ObjectId id in btr)
                {
                    ed.WriteMessage("{0} - {1}\n", id.ObjectClass.DxfName, id.ToString());
               
                }

                trx.Commit();
            }
        }
 

Code prints for a simple BlockReference that references a BlockTableRecord with a Arc, circle, line
Quote
CIRCLE - (8796082561152)
LINE - (8796082561168)
ARC - (8796082561184)
« Last Edit: January 11, 2012, 04:45:53 PM by Jeff H »

TheMaster

  • Guest
Re: How to get Entities in a Block.
« Reply #6 on: January 11, 2012, 10:13:09 PM »

If you create new non-database-resident objects, you are responsible to Dispose() them. Alternatively, you could wrap the DBObjectCollection itself in a using statement, which would take care of disposing the objects contained when leaving its scope.


Unless it has changed recently, I don't believe DBObjectCollection
disposes its elements when it gets disposed.


kaefer

  • Guest
Re: How to get Entities in a Block.
« Reply #7 on: January 12, 2012, 02:56:03 AM »
Unless it has changed recently, I don't believe DBObjectCollection disposes its elements when it gets disposed.

We've been at it before, inconclusively. Even then, under DBObjectCollection Class, arxmgd.chm says:
Quote
When a DBObjectCollection is disposed, it looks at the set of unmanaged objects it holds, and the set of wrappers it has generated. For those objects with wrappers, it assumes the wrappers will manage the lifetime of the object, and it does nothing. For those objects without wrappers, it will delete them or close them: it takes responsibility for object lifetime.
...
it is important to be sure DBObjectCollections are properly (perhaps explicitly) disposed.

It's Alive!

  • Retired
  • Needs a day job
  • Posts: 8691
  • AKA Daniel
Re: How to get Entities in a Block.
« Reply #8 on: January 12, 2012, 03:16:59 AM »
Maybe as a test, you can copy the dbobjects to another container such as list, call dispose the DBObjectCollection,  then call  IsDisposed on one of the items... just saying

TheMaster

  • Guest
Re: How to get Entities in a Block.
« Reply #9 on: January 12, 2012, 07:11:03 AM »

We've been at it before, inconclusively. Even then, under DBObjectCollection Class, arxmgd.chm says:

When a DBObjectCollection is disposed, it looks at the set of unmanaged objects it holds, and the set of wrappers it has generated. For those objects with wrappers, it assumes the wrappers will manage the lifetime of the object, and it does nothing. For those objects without wrappers, it will delete them or close them: it takes responsibility for object lifetime.
...

Regarding:

Quote
"Alternatively, you could wrap the DBObjectCollection itself in a using statement, which would take care of disposing the objects contained when leaving its scope."

I took the word "Dispose" to imply managed wrappers rather than pointers to native AcDbObjects, and DBObjectCollection (as the docs imply) doesn't call Dispose() on managed wrappers or delete native AcDbObjects for which managed wrappers were created.

For the items in a DBObjectCollection for which managed wrappers were created (which happens the first time an item is accessed via the indexer or the enumerator), the managed wrappers for those items must be treated the same way that all managed wrappers for AcDbObjects that are not database-resident are treated (e.g., it is absolutely mandatory that their Dispose() method be called to prevent them from being collected by the GC).

Quote
it is important to be sure DBObjectCollections are properly (perhaps explicitly) disposed.

Yes, because if you don't call Dispose() on a DBObjectCollection, the destructors of the native AcDbObjects it contains that are not database-resident will be called on the thread on which the garbage collector runs, where AutoCAD API access is limited or dangerous at best, and that can easily lead to a fatal error.

All of this absurdity is largely due to the quirk that results from the fact that the .NET garbage collector runs on a non-AutoCAD thread where AutoCAD API access is not fully-supported. Because the destructors of some native objects can make calls to AutoCAD APIs that are not thread-safe, the managed wrappers for those objects must always be disposed (from an AutoCAD thread, where most managed user code runs), to ensure the destructors of the wrapped native objects execute on that same AutoCAD thread.


« Last Edit: January 12, 2012, 07:26:37 AM by TheMaster »

kaefer

  • Guest
Re: How to get Entities in a Block.
« Reply #10 on: January 12, 2012, 08:16:10 AM »
Maybe as a test, you can copy the dbobjects to another container such as list, call dispose the DBObjectCollection,  then call  IsDisposed on one of the items... just saying

Just tried:
Quote
Select Entity:
28 Objects in collection
28 Objects in list
  0: IsDisposed False
  1: IsDisposed False
  etc.

... Because the destructors of some native objects can make calls to AutoCAD APIs that are not thread-safe, the managed wrappers for those objects must always be disposed (from an AutoCAD thread, where most managed user code runs), to ensure the destructors of the wrapped native objects execute on that same AutoCAD thread.

Ok, I was wrong. We do have to dispose the objects (that aren't added to the Database) and also the DBObjectCollection.

It's Alive!

  • Retired
  • Needs a day job
  • Posts: 8691
  • AKA Daniel
Re: How to get Entities in a Block.
« Reply #11 on: January 12, 2012, 08:18:58 AM »
Threading has nothing to do with it. the issue is 'when' to destroy a native pointer within a wrapper, in a garbage collected framework that does not have scoping? A, you don't, you suppress the finalizer and hand it off to the developer then put words like " it's important to dispose the object (perhaps explicitly)" in your documentation.  but it is what it is, if you create it and don't hand it off to an owner, dispose it, Easy Peasy Lemon Squeezy

It's Alive!

  • Retired
  • Needs a day job
  • Posts: 8691
  • AKA Daniel
Re: How to get Entities in a Block.
« Reply #12 on: January 12, 2012, 08:21:30 AM »
...We do have to dispose the objects (that aren't added to the Database) and also the DBObjectCollection.

Awesome, good to know  8-)

TheMaster

  • Guest
Re: How to get Entities in a Block.
« Reply #13 on: January 12, 2012, 08:23:56 AM »
Maybe as a test, you can copy the dbobjects to another container such as list, call dispose the DBObjectCollection,  then call  IsDisposed on one of the items... just saying

Watching AutoCAD unceremoniously die on the next GC after my code failed to call Dispose() on the DBObjects that I didn't add to a database was a pretty good test for me :laugh:

And there was that bug in the DBObjectCollection that only made matters worse (it didn't create managed wrappers when you tried to get them from the indexer, so you had to first use foreach() to iterate over the entire collection before you could use the indexer).  :roll:

A few years back I made wrapper class for DBObjectCollection that made sure that any DBObjects that were returned were properly disposed when the wrapper and wrapped DBObjectCollection were disposed. 

I routinely use that wrapper nowadays to simplify things.



TheMaster

  • Guest
Re: How to get Entities in a Block.
« Reply #14 on: January 12, 2012, 08:36:15 AM »
Threading has nothing to do with it. the issue is 'when' to destroy a native pointer within a wrapper, in a garbage collected framework that does not have scoping? A, you don't, you suppress the finalizer and hand it off to the developer then put words like " it's important to dispose the object (perhaps explicitly)" in your documentation.  but it is what it is, if you create it and don't hand it off to an owner, dispose it, Easy Peasy Lemon Squeezy

Well, threading has everything to do with why you must determinstically call Dispose() on DBObjects that are not database-resident.

But, in a managed environment with garbage collection, it shouldn't be necessary to call Dispose() on anything, unless there is something special that must occur when the object is no longer used (an example would be a class that manages an open file, where calling Dispose() would cause the file to be closed, allowing others to access it).

Unfortunately, we're stuck with a cooperative fiber-mode application that has many APIs that aren't thread-safe, which means they can only be called on one of the application-managed threads/fibers, and that is the root cause of the utterly-ridiculous Dispose() requirement that vastly-complicates development of managed AutoCAD solutions.

It's Alive!

  • Retired
  • Needs a day job
  • Posts: 8691
  • AKA Daniel
Re: How to get Entities in a Block.
« Reply #15 on: January 12, 2012, 08:45:42 AM »
example of how the garbage collector works with DBObjects... when do you see the message  :-)

Code: [Select]
#region
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.Runtime;
#endregion

[assembly: CommandClass(typeof(ExecMethod.Commands))]
namespace ExecMethod
{
  public class leak : DBPoint
  {
    protected override void DeleteUnmanagedObject()
    {
      System.Windows.Forms.MessageBox.Show("HI : )");
      base.DeleteUnmanagedObject();
    }
  }
  public class Commands
  {
    [CommandMethod("leak")]
    public static void leak()
    {
      leak spring = new leak();

    }
  }
}

It's Alive!

  • Retired
  • Needs a day job
  • Posts: 8691
  • AKA Daniel
Re: How to get Entities in a Block.
« Reply #16 on: January 12, 2012, 08:51:49 AM »
Threading has nothing to do with it. the issue is 'when' to destroy a native pointer within a wrapper, in a garbage collected framework that does not have scoping? A, you don't, you suppress the finalizer and hand it off to the developer then put words like " it's important to dispose the object (perhaps explicitly)" in your documentation.  but it is what it is, if you create it and don't hand it off to an owner, dispose it, Easy Peasy Lemon Squeezy

Well, threading has everything to do with why you must determinstically call Dispose() on DBObjects that are not database-resident.

But, in a managed environment with garbage collection, it shouldn't be necessary to call Dispose() on anything, unless there is something special that must occur when the object is no longer used (an example would be a class that manages an open file, where calling Dispose() would cause the file to be closed, allowing others to access it).

Unfortunately, we're stuck with a cooperative fiber-mode application that has many APIs that aren't thread-safe, which means they can only be called on one of the application-managed threads/fibers, and that is the root cause of the utterly-ridiculous Dispose() requirement that vastly-complicates development of managed AutoCAD solutions.

The need to call dispose in acad is by design, nothing to do with what thread the GC runs on. Study the DisposableWrapper class  :-)

It's Alive!

  • Retired
  • Needs a day job
  • Posts: 8691
  • AKA Daniel
Re: How to get Entities in a Block.
« Reply #17 on: January 12, 2012, 09:03:58 AM »
Take the following class as an example. When should the GC call the finalaizer? How does the GC know that Acad does not need the pointer? it doesn't, either you hand the task to an owner,  such as the transaction manager,  or you, the developer must call dispose.. by design    :-)

Code - C++: [Select]
  1.        ref class LineWrapper
  2.         {
  3.             AcDbLine *pline;
  4.             LineWrapper()
  5.             {
  6.                 pline = new AcDbLine();
  7.             }
  8.             ~LineWrapper()
  9.             {
  10.                 GC::SuppressFinalize(this);
  11.                 this->!LineWrapper();
  12.             }
  13.             !LineWrapper()
  14.             {
  15.                 delete pline;
  16.             }
  17.         };
  18.  

It's Alive!

  • Retired
  • Needs a day job
  • Posts: 8691
  • AKA Daniel
Re: How to get Entities in a Block.
« Reply #18 on: January 12, 2012, 09:11:10 AM »
@ tik, sorry to hijack your thread..

Just an FYI BlockRecords own the geometry,  BlockReferences can own attributes.  :-)

TheMaster

  • Guest
Re: How to get Entities in a Block.
« Reply #19 on: January 12, 2012, 09:38:27 AM »

Take the following class as an example. When should the GC call the finalaizer?


When there are no longer any reachable references to the object? :-D

In managed code, when an object can no longer be reached through code (e.g., there are no longer any usable references to it) it becomes eligible for collection - by design.  That's how managed code was intended to work.

The "Dispose()" method is really just given you a way to tell an object that you are done using it, in spite of the fact that there may still be reachable references to it.

What an object does in response to being told that, is entirely up to the object.

You might note that many managed objects in the API are all based on DisposableWrapper, but in many cases, it was done that way largely out of convenience, and for many of those managed wrapper types it is entirely unnecessary to call Dispose() on them. If you don't, they get collected and their destructors get called. Calling a destructor from the GCs thread is not supposed to be a problem, and in many cases it is not.

So, we can't say that the need to call Dispose() is by-design, because in a managed environment, what calling Dispose() does will inevitably be done anyways, when an object is collected, and that is the whole intent of a 'managed' execution environment  - to remove the burden of managing object life from the programmer and making the runtime responsible for it.


Quote

How does the GC know that Acad does not need the pointer? it doesn't,


Sure it does :grin:.  The GC knows that because the programmer allowed the wrapper to become unreachable from any code. That is how the GC determines that a managed object is no longer needed and can be collected.

Further, it isn't Acad that needs the pointer, it is the programmer that has the managed wrapper for the pointed-to object.

Quote

either you hand the task to an owner,  such as the transaction manager,  or you, the developer must call dispose.. by design    :-)


No, sorry to disagree but this is not by design. In fact, I have talked about it at length with the architect of AutoCAD's managed API and he has lamented about the fact that it is necessary for the programmer to determinstically Dispose() managed  wrappers for AcDbObjects that aren't database-resident. It shouldn't be necessary and for that matter, it wouldn't be necessary if Microsoft had done what it said it was going to do (allow a CLR host application to create and manage the thread which the GC runs on).  That was one of the features that was supposed to go into .NET 2.0, but was cut at the last minute, mainly because Microsoft decided to abandon the cooperative fiber-mode threading model in SQL Server, making that CLR feature unnecessary for their own use therein.

http://www.theswamp.org/index.php?topic=16375.0

Had Microsoft added that feature to the CLR like they proposed doing, then today, the GC would be running on an AutoCAD thread, and none of this utter nonsense would be necessary.
« Last Edit: January 12, 2012, 09:51:04 AM by TheMaster »

It's Alive!

  • Retired
  • Needs a day job
  • Posts: 8691
  • AKA Daniel
Re: How to get Entities in a Block.
« Reply #20 on: January 12, 2012, 10:19:06 AM »
did you run this code? when does the collector fire?
www.theswamp.org/index.php?topic=40630.msg459240#msg459240


additionally, you can derive from disposable wrapper and examine the behavior, you just might be surprised  : )
« Last Edit: January 12, 2012, 10:22:47 AM by eBeerHandler »

TheMaster

  • Guest
Re: How to get Entities in a Block.
« Reply #21 on: January 12, 2012, 10:25:05 AM »
did you run this code? when does the collector fire?
www.theswamp.org/index.php?topic=40630.msg459240#msg459240


additionally, you can derive from disposable wrapper and examine the behavior, you just might be surprised  : )

No I didn't run it.  When do you see the message?


It's Alive!

  • Retired
  • Needs a day job
  • Posts: 8691
  • AKA Daniel
Re: How to get Entities in a Block.
« Reply #22 on: January 12, 2012, 10:26:29 AM »
you tell me  :lol:

tik

  • Guest
Re: How to get Entities in a Block.
« Reply #23 on: January 12, 2012, 10:28:53 AM »
Guys,

I am sitting here and reading through the posts and learning a lot. Thanks for sharing your knowledge with every one.

Jeff,

Thanks for the code sample it worked like a charm but I have one issue, when i get the entities from the BlockTableRecord there is no XData in those entities, does this goes back to that cloning issue again or am I doing something wrong.


TheMaster

  • Guest
Re: How to get Entities in a Block.
« Reply #24 on: January 12, 2012, 10:37:56 AM »
you tell me  :lol:

Can't right now, I'm on my netbook, far from my desktop :grin:

Just tell us :lol:

I shouldn't see any anything until the next GC.

And in case you didn't know this, even a simple call to acutPrintF() will fail in that same context (with no exeception or any kind of error message).

It's Alive!

  • Retired
  • Needs a day job
  • Posts: 8691
  • AKA Daniel
Re: How to get Entities in a Block.
« Reply #25 on: January 12, 2012, 10:41:08 AM »
 I will wait for you, and your explanation as to 'why' DisposableWrapper behaves the way it does.     

It's Alive!

  • Retired
  • Needs a day job
  • Posts: 8691
  • AKA Daniel
Re: How to get Entities in a Block.
« Reply #26 on: January 14, 2012, 06:19:27 AM »
you tell me  :lol:

Can't right now, I'm on my netbook, far from my desktop :grin:

Just tell us :lol:

I shouldn't see any anything until the next GC.

And in case you didn't know this, even a simple call to acutPrintF() will fail in that same context (with no exeception or any kind of error message).

Did you quit playing?

Jeff H

  • Needs a day job
  • Posts: 6150
Re: How to get Entities in a Block.
« Reply #27 on: February 04, 2012, 11:07:54 PM »
What about this?
 
A little change to your code Daniel.
Code: [Select]
public class leak : Line
    {
        protected override void DeleteUnmanagedObject()
        {
            Application.ShowAlertDialog("DeleteUnmanagedObject");
            HostApplicationServices.WorkingDatabase.Clayer = HostApplicationServices.WorkingDatabase.LayerZero;
            base.DeleteUnmanagedObject();
        }
        protected override void Dispose(bool A_1)
        {
            Application.ShowAlertDialog("Dispose");
            base.Dispose(A_1);
            HostApplicationServices.WorkingDatabase.Clayer = HostApplicationServices.WorkingDatabase.LayerZero;
           
        }
    }
    public class Commands
    {
        [CommandMethod("leak")]
        public static void leak()
        {
            leak spring = new leak();
            spring.LayerId = HostApplicationServices.WorkingDatabase.Clayer;
        }
And it goes Boom!!!
 
Now open up acad.exe.config and add <gcConcurrent> Element
Code: [Select]
   <runtime>       
 <generatePublisherEvidence enabled="false"/>
     <gcConcurrent enabled="false"/>
   </runtime>

Start Acad back up run command again and no boom
 
 
A little more info
How to: Disable Concurrent Garbage Collection
Also the
<gcServer> Element
 
 
 
 

TheMaster

  • Guest
Re: How to get Entities in a Block.
« Reply #28 on: February 08, 2012, 05:40:33 PM »

Did you quit playing?


No, I've been busy building a new system for Android and iOS development with Mono For Android (http://xamarin.com/monoforandroid) and haven't been playing with much else

Regarding this, I was just trying to save you the embarrassment resulting from finding out that trying to show messages using an API that is inherently not thread-safe (MessageBox.Show) doesn't always work when it is run on a high-priority, background thread with no message pump (the GC's thread), and there will be no exception or any other indication that it failed (other than AutoCAD crashing :-P). For that matter, we're not supposed to do any kind of blocking UI calls in the GC's thread.

Just guessing here, but I think the reason why MessagBox.Show(), ShowAlertDialog(), etc, may not work or even may crash AutoCAD, has to do with the hooks AutoCAD uses to disable/enable floating windows whenever a modal dialog is shown/dismissed. If I'm not mistaken, that involves accessing state and APIs that cannot be accessed asynchronously, from a non-AutoCAD thread.

Did you not see my comment regarding the fact that even acutPrintF() fails in that context?  :wink:

I lost count of how many classes I've made that are based on DisposableWrapper, and every one of their implementations of DeleteUnmanagedObject() is called on the next GC (in the thread of the GC) after they become unreachable when Dispose() was not called on them. I know that, because from my overrides of their Dispose(bool) methods, I usually throw an exception in debug builds, to tell me that I failed to deterministically dispose the instance. :wink:

So, you might be mistaking the failure of MessageBox.Show() when run on the GC thread, with the presumption that the finalizer is not running, or is not calling DeleteUnmanagedObject().  It certainly is and does, and you can confirm that by using a thread-safe way of showing the message (even the debugger may not hit a breakbpoint in that context, BTW). 

I use a homegrown trace utility that outputs trace messages synchronously to another process via SendMessage(WM_COPYDATA,...) that doesn't return until the message has actually been shown in the other process (the trace listener, which I wrote in Delphi about 10 years ago and can't find the source code for now  :oops:).

As Jeff's experiment shows,when you tell the runtime to not do garbage collection asynchronously, everything is fine, except that it will most-likely result in severe 'lag' in the UI, due to the fact that AutoCAD must stop and wait for a GC to complete.

« Last Edit: February 09, 2012, 06:36:08 AM by TheMaster »

It's Alive!

  • Retired
  • Needs a day job
  • Posts: 8691
  • AKA Daniel
Re: How to get Entities in a Block.
« Reply #29 on: February 10, 2012, 01:55:00 AM »
I think you got lost  :lol:

I get that items such as acutPrintf are not thread safe. it's not even close to what I am taking about. I am talking about the "lifetime" of an underlying unmanaged object, when wrapped in a class such as disposableWapper.  I don't care what thread does the cleanup, I only care the state of the object. I made a suggestion that .NET has no clue how or when to deal with unmanaged objects, hence "unmanaged", you disagreed,  I created a sample to prove a point, but you never did run it.  so you can't tell me the behavior  :-P

My goal here is not to argue with you, it's to pass on the little knowledge i have gained from writing literally thousands of wrappers for .NET : ) .  Some interesting classes relating to disposablewrapper, Autodesk.AutoCAD.Windows.Window (which implements disposablewrapper) and its subclass Autodesk.AutoCAD.Windows.NonFinalizableWindow  8-)


cheers .

It's Alive!

  • Retired
  • Needs a day job
  • Posts: 8691
  • AKA Daniel
Re: How to get Entities in a Block.
« Reply #30 on: February 10, 2012, 02:03:49 AM »
Master, are you TonyT?  cause if you are, you could make objects dispose just by thinking it. Only two people in the world could do this, TT and Chuck Norris   ^-^   

Kerry

  • Mesozoic relic
  • Seagull
  • Posts: 11654
  • class keyThumper<T>:ILazy<T>
Re: How to get Entities in a Block.
« Reply #31 on: February 10, 2012, 03:36:39 AM »

 ^-^
 :lmao:


kdub, kdub_nz in other timelines.
Perfection is not optional.
Everything will work just as you expect it to, unless your expectations are incorrect.
Discipline: None at all.

TheMaster

  • Guest
Re: How to get Entities in a Block.
« Reply #32 on: February 13, 2012, 04:08:53 PM »
I think you got lost  :lol:

I get that items such as acutPrintf are not thread safe. it's not even close to what I am taking about.


If you get that, why did you post an example that uses MessageBox.Show(),
which fails because that API and/or other AutoCAD code that gets called
as a result of calling that API can't run on the GC's thread ?

I think you messed-up :lol: because my trace API shows the message
when the finalizer runs at the next GC, but your sample shows nothing,
and from that, you are implying what?  That DeleteUnmanagedObject()
is not getting called?

Sorry, but you're mistaken.

Quote

I am talking about the "lifetime" of an underlying unmanaged object, when wrapped in a class such as disposableWapper. 


That's not what I'm talking about. I'm talking about your sample that
calls MessageBox.Show() and the point you were trying to make with
it :lol:

Quote

My goal here is not to argue with you, it's to pass on the little knowledge i have gained from writing literally thousands of wrappers for .NET : ) . 


You mean you're not playing games here?  If not, then why don't you
just state what happens when your sample runs, and what should or
should not happen?

Cheers

Jeff H

  • Needs a day job
  • Posts: 6150
Re: How to get Entities in a Block.
« Reply #33 on: February 13, 2012, 04:22:01 PM »
Guys,

I am sitting here and reading through the posts and learning a lot. Thanks for sharing your knowledge with every one.

Jeff,

Thanks for the code sample it worked like a charm but I have one issue, when i get the entities from the BlockTableRecord there is no XData in those entities, does this goes back to that cloning issue again or am I doing something wrong.
Are you after Xdata attached to the BlockReference?
eNotEnoughBeer & TheMaster please do not mind us and keep your disscusion  going.
Thanks.
 

Jeff H

  • Needs a day job
  • Posts: 6150
Re: How to get Entities in a Block.
« Reply #34 on: February 28, 2012, 04:24:36 PM »
Am I the only who sees the message box everytime?
 
Attached is a little something threw together quickly for messing around with being notified when an object's finalizer is called.
 
Sorry for the 3rd grade mentality but unless dispose explicitly called the messagebox shows up in the background being called from the GC's thread so I added a FartNotifier that plays a different fart sound for Finalize, Dispose, and DeleteUnmanaged Object. Was a little easier and more amusing for me.
 
 
And I guess since DisposableWrapper's DeleteUnmanagedObject is abstract it is hard or impossible to know what implementation will be used. DBPoint uses DBObjects implementation but would be nice from our point view if they all acted the same. 
 
 
 
 

Kerry

  • Mesozoic relic
  • Seagull
  • Posts: 11654
  • class keyThumper<T>:ILazy<T>
Re: How to get Entities in a Block.
« Reply #35 on: February 28, 2012, 04:35:17 PM »
< ... > so I added a FartNotifier that plays a different fart sound for Finalize, Dispose, and DeleteUnmanaged Object. Was a little easier and more amusing for me.
 < ... >

:) I really enjoy how unique some people are !!
kdub, kdub_nz in other timelines.
Perfection is not optional.
Everything will work just as you expect it to, unless your expectations are incorrect.
Discipline: None at all.

Jeff H

  • Needs a day job
  • Posts: 6150
Re: How to get Entities in a Block.
« Reply #36 on: February 28, 2012, 05:42:01 PM »
< ... > so I added a FartNotifier that plays a different fart sound for Finalize, Dispose, and DeleteUnmanaged Object. Was a little easier and more amusing for me.
 < ... >

 :) I really enjoy how unique some people are !!
Unique/Tard same difference :)
 
For fun I guess someone could use reflection to create a new instance of each object thet derives from DisposableWrapper and see which ones crap out. Too many things to think about and I do not know about how vaild it would be.
 
Suppose to be working on something else so bad example(Not like it would good if I thought it out at all) but something like
 
Code - C#: [Select]
  1.  
  2.     public interface ILogger
  3.     {
  4.         void WriteLine(string s);
  5.     }
  6.     public class Finalizer
  7.     {
  8.         DisposableWrapper ds;
  9.         ILogger logger;
  10.         ~Finalizer()
  11.         {
  12.             logger.WriteLine(ds.GetType().Name);
  13.             ds.Dispose();
  14.         }
  15.         public Finalizer(DisposableWrapper disPos, ILogger log)
  16.         {
  17.             logger = log;
  18.             ds = disPos;
  19.         }
  20.     }
  21.     public class LogFile : ILogger
  22.     {
  23.        
  24.         StringBuilder sb;
  25.         public LogFile()
  26.         {
  27.             sb = new StringBuilder();
  28.         }
  29.         public void WriteLine(string s)
  30.         {
  31.             sb.AppendLine(s);
  32.         }
  33.         public override string ToString()
  34.         {
  35.             return sb.ToString();
  36.         }
  37.     }
  38.  

Or use the IAcadObjectLifetimeNotify interface from IFart ;)
 
« Last Edit: February 28, 2012, 05:46:28 PM by Jeff H »

kaefer

  • Guest
Re: How to get Entities in a Block.
« Reply #37 on: February 28, 2012, 06:54:56 PM »
For fun I guess someone could use reflection to create a new instance of each object thet derives from DisposableWrapper

Off on a tangent: How easy do you think is that done in a reliable manner? You'll start with
Code - C#: [Select]
  1. IEnumerable<Type> types =
  2.             from a in AppDomain.CurrentDomain.GetAssemblies()
  3.             from t in a.GetTypes()
  4.             where typeof(DisposableWrapper).IsAssignableFrom(t)
  5.             etc.
or somesuch, only to discover that some of the assemblies holding the types you're interested in aren't even loaded yet. Unless I am missing something here, you're going to need an idea which containers you want to be looked at.
« Last Edit: February 28, 2012, 07:03:14 PM by kaefer »

Jeff H

  • Needs a day job
  • Posts: 6150
Re: How to get Entities in a Block.
« Reply #38 on: February 28, 2012, 08:57:41 PM »
For fun I guess someone could use reflection to create a new instance of each object thet derives from DisposableWrapper
Off on a tangent: How easy do you think is that done in a reliable manner?

I have no idea was just a suggestion & reason for 
Too many things to think about and I do not know about how vaild it would be.
 

I probably should not post this as I know it is not a good idea but for a general idea maybe something for listing
 
Code - C#: [Select]
  1. [CommandMethod("ListAcDbMgdDisposableTypes")]
  2.         public void ListAcDbMgdDisposableTypes()
  3.         {
  4.             Assembly ass = Assembly.Load("acdbmgd");
  5.             foreach (AssemblyName assName in ass.GetReferencedAssemblies())
  6.             {
  7.                 Assembly.Load(assName.FullName);
  8.             }
  9.             Type[] types = ass.GetTypes();
  10.             foreach (Type typ in types)
  11.             {
  12.                 if (typ.IsSubclassOf(typeof(DisposableWrapper)))
  13.                 {
  14.                     Application.DocumentManager.MdiActiveDocument.Editor.WriteMessage("\n"   typ.Name);
  15.                 }
  16.             }
  17.         }
  18.  
  19.  

and for creating an instance of
 
Code - C#: [Select]
  1.  
  2.   [CommandMethod("GetAcdbmgdTypes")]
  3.         public void GetAcdbmgdTypes()
  4.         {
  5.             Assembly ass = Assembly.Load("acdbmgd");
  6.             foreach (AssemblyName assName in ass.GetReferencedAssemblies())
  7.             {
  8.                 Assembly.Load(assName.FullName);
  9.             }
  10.             Type[] types = ass.GetTypes();
  11.             foreach (Type typ in types)
  12.             {
  13.                 if (typ.IsSubclassOf(typeof(DisposableWrapper)))
  14.                 {                    
  15.                     Application.DocumentManager.MdiActiveDocument.Editor.WriteMessage("\n"   typ.Name);
  16.                     if ((!typ.IsAbstract) && (typ.GetConstructor(Type.EmptyTypes) != null))
  17.                     {
  18.                         try
  19.                         {
  20.                             object o = Activator.CreateInstance(typ);
  21.                             DisposableWrapper d = (DisposableWrapper)o;
  22.                             Application.DocumentManager.MdiActiveDocument.Editor.WriteMessage("    ---Disposing"   typ.Name);
  23.                             d.Dispose();
  24.                         }
  25.                         catch { }
  26.  
  27.                     }
  28.  
  29.                 }    
  30.                                
  31.             }        
  32.         }
  33.  
  34.  

 

kaefer

  • Guest
Re: How to get Entities in a Block.
« Reply #39 on: February 29, 2012, 03:13:35 AM »
I probably should not post this as I know it is not a good idea but for a general idea maybe something for listing
 
Code - C#: [Select]
  1. [CommandMethod("ListAcDbMgdDisposableTypes")]
  2.         public void ListAcDbMgdDisposableTypes()
  3.         {
  4.             Assembly ass = Assembly.Load("acdbmgd");
  5.             foreach (AssemblyName assName in ass.GetReferencedAssemblies())
  6.             {
  7.                 Assembly.Load(assName.FullName);
  8.             }
  9.             Type[] types = ass.GetTypes();
  10.             foreach (Type typ in types)
  11.             {
  12.                 if (typ.IsSubclassOf(typeof(DisposableWrapper)))
  13.                 {
  14.                     Application.DocumentManager.MdiActiveDocument.Editor.WriteMessage("\n", typ.Name);
  15.                 }
  16.             }
  17.         }

Why, this looks like a good idea...

This approach should take care of lazily loaded assemblies, if there were any issues with acdbmgd at all. I've got bitten by the plethora of MEP assemblies: built-in commands using unmanaged code are happily working, only their managed wrappers hadn't made it yet.

JanetDavidson

  • Mosquito
  • Posts: 17
Re: How to get Entities in a Block.
« Reply #40 on: March 14, 2012, 07:17:18 PM »
Hello Daniel,
Thanks for waking me up . Honestly I  couldn't understand most of these discussions.(It is not English :cry:). I just ran your code , nce, when I started  autocad ,and it took about 10 seconds. Then I ran one of my commands which I am worry about,and then again ran your code  and took about 2 minutes.
I   disposed everything I could and nothing changed ,still 2 minutes. Am I doing something wrong? Or this is nature of autocad?
Nothing crahsed and the program works fine. You gentlemen  just made me worry. what is this GC class? Can somebody show me whether it is a must to use GC.Collect in a real project ? I feel I know nothing .
 Thanks for a good help here ?




example of how the garbage collector works with DBObjects... when do you see the message  :-)