Here is the exact code with a couple of additions I tried.


-Added a event handler for ObjectClosed.
-Added after first Transaction

ed.WriteMessage("\n" + ad.UnmanagedObject.ToString());
ed.WriteMessage("\n" + ad.IsDisposed.ToString());
ed.WriteMessage("\n" + ad.Tag);
//ad.LockPositionInBlock = true;///// Boom


Notice how checking IsDisposed works because just checking managed wrappers properties
Code - C#: [Select]
  1. public bool IsDisposed
  2. {
  3. [ return : MarshalAs ( UnmanagedType . U1 ) ]
  4. get
  5. {
  6. GC . KeepAlive ( this ) ;
  7. return ( this . m_imp == IntPtr . Zero ) ;
  8. }
  9. }


Checking its Tag property works.
AcDbAttribute::tag
Quote
This function returns a pointer to a copy of the tag string for the attribute. The tag string is the identifier you see if you explode the AcDbBlockReference that owns the attribute, so that the attribute reverts back to the AcDbAttributeDefinition that was part of the original reference's block definition.
The caller of this function is responsible for deallocating the memory used by the returned string. The acutDelString() global function should be used for this purpose.
The tag string is used for DXF group code 2.


Notice it is just using the Intptr for its address to marshal back a string.
Code - C#: [Select]
  1. get
  2. {
  3. char * chPtr = 0L ;
  4. try
  5. {
  6. chPtr = AcDbAttribute . tag ( ( AcDbAttribute modopt ( IsConst ) * modopt ( IsConst ) modopt ( IsConst ) ) this . GetImpObj ( ) ) ;
  7. return WcharToString ( ( char modopt ( IsConst ) * ) chPtr ) ;
  8. }
  9. finally
  10. {
  11. acutDelBuffer ( ( void ** modopt ( IsImplicitlyDereferenced ) ) & chPtr ) ;
  12. }
  13. return null ;
  14. }


Now ad.LockPositionInBlock will go boom since it tries to modify the object.


The line ad.Erase is where it goes booms but can be fixed with adding
tr.GetObject(ad.ObjectId, OpenMode.ForWrite); or ad.ObjectId.Open()


The word Dispose gets used and thrown around alot and usually is understood as releasing unmanaged memory, but I think it is a big NoNo to delete a object in ObjectARX and just call Close because they keep the objects in memory.
So it might better to think of Dispose and Transactions as just opening and closing objects instead of memory management.


So from what I can tell is Clone and other methods work because they just use the address(GetImpObj, DisposableWrapper.UnmanagedObject) to go get some information and marshal it back to you.


Erase is trying to modify an object and there is where I get a BOOM!


Also the ObjectClosed event fires for me during Commit as the code below shows some message boxes.


Code - C#: [Select]
  1. [ CommandMethod ( "TransactionTesting" ) ]
  2. public void TestTransaction ( )
  3. {
  4. try
  5. {
  6. Document doc = Application . DocumentManager . MdiActiveDocument ;
  7. Editor ed = doc . Editor ;
  8. Database db = doc . Database ;
  9. PromptEntityOptions peo = new PromptEntityOptions ( " \n Select attribute to add" ) ;
  10. peo . SetRejectMessage ( " \n Not an AttributeDefinition" ) ;
  11. peo . AddAllowedClass ( typeof ( AttributeDefinition ) , true ) ;
  12. PromptEntityResult per = ed . GetEntity ( peo ) ;
  13. if ( per . Status != PromptStatus . OK ) return ;
  14. ObjectId adId = per . ObjectId ;
  15. peo = new PromptEntityOptions ( " \n Select block to append attribute" ) ;
  16. peo . SetRejectMessage ( " \n Not a BlockReference" ) ;
  17. peo . AddAllowedClass ( typeof ( BlockReference ) , true ) ;
  18. per = ed . GetEntity ( peo ) ;
  19. if ( per . Status != PromptStatus . OK ) return ;
  20. ObjectId brId = per . ObjectId ;
  21. AttributeDefinition ad = null ;
  22. BlockReference br = null ;
  23. using ( Transaction tr = db . TransactionManager . StartTransaction ( ) )
  24. {
  25. try
  26. {
  27. AttributeDefinition ad1 = ( AttributeDefinition ) tr . GetObject ( adId, OpenMode . ForWrite ) ;
  28. ad1 . ObjectClosed += ad1_ObjectClosed ;
  29. BlockReference br1 = ( BlockReference ) tr . GetObject ( brId, OpenMode . ForRead ) ;
  30. Application . ShowAlertDialog ( "Before Commit" ) ;
  31. tr . Commit ( ) ;
  32. Application . ShowAlertDialog ( "After Commit" ) ;
  33. ad = ad1 ; //will fail if disposed at tr.commit()
  34. br = br1 ;
  35. }
  36. catch ( System . Exception e )
  37. {
  38. ed . WriteMessage ( " \n Error: {0} \n {1}" , e . Message , e . StackTrace ) ;
  39. }
  40. Application . ShowAlertDialog ( "Before Transaction Closed" ) ;
  41. }
  42. Application . ShowAlertDialog ( "After Transaction Closed" ) ;
  43. ed . WriteMessage ( " \n " + ad . UnmanagedObject . ToString ( ) ) ;
  44. ed . WriteMessage ( " \n " + ad . IsDisposed . ToString ( ) ) ;
  45. ed . WriteMessage ( " \n " + ad . Tag ) ;
  46. //ad.LockPositionInBlock = true;///// Boom
  47. using ( Transaction tr = db . TransactionManager . StartTransaction ( ) )
  48. {
  49. try
  50. {
  51. BlockTableRecord btr = ( BlockTableRecord ) tr . GetObject ( br . BlockTableRecord , OpenMode . ForWrite ) ;
  52. AttributeDefinition NEWad = ( AttributeDefinition ) ad . Clone ( ) ;
  53. NEWad . TransformBy ( br . BlockTransform . Inverse ( ) ) ; //to set the AD to the BTR coordinate system
  54. ObjectId NEWadId = btr . AppendEntity ( NEWad ) ;
  55. tr . AddNewlyCreatedDBObject ( NEWad, true ) ;
  56. ObjectIdCollection blockIds = btr . GetBlockReferenceIds ( true , true ) ;
  57. if ( ! NEWad . Constant ) //constant attributes are NOT added to the block reference
  58. foreach ( ObjectId id in blockIds ) //add attribute reference to all blocks
  59. {
  60. br = ( BlockReference ) tr . GetObject ( id, OpenMode . ForWrite ) ;
  61. AttributeReference ar = new AttributeReference ( ) ;
  62. ar . SetAttributeFromBlock ( NEWad, br . BlockTransform ) ;
  63. ar . TextString = ad . TextString ;
  64. br . AttributeCollection . AppendAttribute ( ar ) ;
  65. tr . AddNewlyCreatedDBObject ( ar, true ) ;
  66. }
  67. tr . GetObject ( ad . ObjectId , OpenMode . ForWrite ) ; //////Commit line out and Boom
  68. ad . Erase ( ) ; //////Will only work if above line is commented out
  69. tr . Commit ( ) ;
  70. ed . Regen ( ) ; //Regen needed for constant attribute to correctly display
  71. }
  72. catch ( Autodesk . AutoCAD . Runtime . Exception ex )
  73. {
  74. if ( ex . ErrorStatus != ErrorStatus . NotOpenForWrite )
  75. {
  76. ed . WriteMessage ( " \n Error: {0} \n {1}" , ex . GetBaseException ( ) , ex . StackTrace ) ;
  77. }
  78. throw ;
  79. }
  80. }
  81. BlockReference br2 = ( BlockReference ) br . Clone ( ) ; //cloning outside of a transaction works
  82. using ( Transaction tr = db . TransactionManager . StartTransaction ( ) )
  83. {
  84. AttributeDefinition ad1 = new AttributeDefinition ( ) ;
  85. ad1 . SetDatabaseDefaults ( ) ;
  86. ad1 . Tag = "tag" ;
  87. ad1 . TextString = "textString" ;
  88. ad1 . Constant = true ;
  89. Matrix3d mat = br2 . BlockTransform ;
  90. ad1 . TransformBy ( mat ) ;
  91. BlockTableRecord btr = ( BlockTableRecord ) tr . GetObject ( db . CurrentSpaceId , OpenMode . ForWrite ) ;
  92. btr . AppendEntity ( ad1 ) ;
  93. tr . AddNewlyCreatedDBObject ( ad1, true ) ;
  94. tr . Commit ( ) ;
  95. ed . Regen ( ) ;
  96. }
  97. }
  98. catch ( Autodesk . AutoCAD . Runtime . Exception ex )
  99. {
  100. ed . WriteMessage ( " \n Error: {0} \n {1}" , ex . GetBaseException ( ) , ex . StackTrace ) ;
  101. }
  102. }
  103. void ad1_ObjectClosed ( object sender, ObjectClosedEventArgs e )
  104. {
  105. Application . ShowAlertDialog ( "ObjectClosed" ) ;
  106. }
Thanks for that, I didn't know how to use the ObjectClosed event before this example...

I put your code into my compiler and didn't do anything aside from changing line 109 so it would compile, uncommenting line 47 where you lock the position in the block, and commenting out line and 70 where you reopen ad for write. Everything worked...  What version are you using?? Any add ons?

changed line 109 to:
Code - C#: [Select]
  1. Application . DocumentManager . MdiActiveDocument . Editor . WriteMessage ( " \n Error: {0} \n {1}" , ex . GetBaseException ( ) , ex . StackTrace ) ;

Editor output after running:
Command: TRANSACTIONTESTING

Select attribute to add:  (BTW I never thought to simply check ad.IsDisposed)
Nothing Selected.

Select attribute to add:
Select block to append attribute:
693813248
False
MYTAGRegenerating model.
Regenerating model.


I'm even more confused at this point, I've read through the ARX documentation and everything there seems to agree with what you're telling me but it's not what I'm seeing :lmao:

2013 Vanilla flavour but this machine Design Suite(MEP, Revit, and crap like SkecthBook Designer that f**ks up all your dynamic blocks with wipeouts being stretched--sotty for the rant)

I thought a ObjectId or using the UnmanagedObject property would be valid until its database is destroyed which it is not ,but how your getting around not closing it or modifying it without opening it, I have no idea.


Does the ObjectClosed Event fire?


Wonder if OpenedForModify event fires before you erase it.


I really do not know,
Speaking of dispose earlier and to add another point I have code that plays different fart sounds for dispose.
A  loud, hard, quick trumpet sounding fart for when I explicitly call dispose on it, and a long slow squeaker if called in Finalization method
Which makes complete sense to me so it goes to show they could be doing anything they want in Dispose call.







Hugo, Jeff,

What version are you using?  I'm trying to wrap my head around why things are working here if they object is apparently closed with the transaction :-o .  If this is something version dependent then my code will be failing on different versions of AutoCAD... I can see how it would make logical sense that as the transaction was disposed the objects opened for write would be downgraded to read only to prevent any modification later.  Can anybody explain what is going on here?

Cheers

Only the outer-most transaction actually closes an object (or cancels it if the transaction is aborted). Inner or nested transactions are only markers that tell the transactionmanager what objects belong to what transactions. If you open an object for write in a transaction, the object stays open for write until the outer-most transaction is comitted or aborted. You will find that objects that are opened for read may still be usable after the outer-most transaction they were opened in has ended, but that is undocumented behavior that should never be relied on.
So that said, me experiencing objects which are still writable after a transaction is an anomaly that shouldn't happen   :blank:
I'm going to try to figure out why this is happening... I have written several functions that use this methodology...

How does that translate to your GetObjects extension method or the standard ObjectId.GetObject method? Do I need to use those inside a transaction to increase the stability of my code on different platforms?
So that said, me experiencing objects which are still writable after a transaction is an anomaly that shouldn't happen   :blank:

I don't think you understood Tony's explanation. If you modify a database resident object, and AutoCAD does not close with a fatal error, then the object is open, period. Either it was opened directly, or it belongs to an open transaction. It sounds like the latter in your case. It doesn't have to be a transaction that your code started. You're probably using a vertical that starts a transaction every time a command executes. If you are, your code will fail in vanilla AutoCAD.
Thanks for explicitly pointing out some versions will create a transaction implicitly... I was going to try to add an event to top transaction being opened and closed and see if that happens at the start and end of every routine.

In our office we run mechanical 2012 and also CADWORX on regular autocad 2012
So that said, me experiencing objects which are still writable after a transaction is an anomaly that shouldn't happen   :blank:
I'm going to try to figure out why this is happening... I have written several functions that use this methodology...


I didn't say writeable, I said that objects that were opened for read can still be accessed after the outer-most transaction they were opened in has ended. As far as I know, if an object was obtained from a transaction using OpenMode.ForWrite, the transaction will close the object when it ends (the outer-most transaction, that is), and you shouldn't be able to modify any object after that happens.

Quote


How does that translate to your GetObjects extension method or the standard ObjectId.GetObject method? Do I need to use those inside a transaction to increase the stability of my code on different platforms?

Use those as opposed to ..... ?