AutoCAD has supported the eXtended Data for long time, back to the ADS era. In the AutoCAD .NET world, those functionalities regarding the XData manipulations and even those extended DXF codes are wrapped nicely into some classes, structs, methods and enums.
However, because of the different syntaxes in the new AutoCAD .NET and the old ADS, even for people coming from the ADS time and using those old ResultBuffer a lot, they may still be confused about some classes, methods, and enum values as far as XData and ResultBuffer are concerned.
From this post on, let’s look at some aspects about using XData in AutoCAD .NET. Particularly, in this post, let’s add various XData types to a single picked AutoCAD entity. Here is the code and command.
[CommandMethod("AddXData")]
public static void AddXData_Method()
{
const string TestAppName = "XDataAppName1";
Database db = HostApplicationServices.WorkingDatabase;
Editor ed = MgdAcApplication.DocumentManager.MdiActiveDocument.Editor;
try
{
PromptEntityResult prEntRes = ed.GetEntity("Select an Entity to attach XDATA");
if (prEntRes.Status == PromptStatus.OK)
{
using (Transaction tr = db.TransactionManager.StartTransaction())
{
RegAppTable regAppTable = (RegAppTable)tr.GetObject(db.RegAppTableId, OpenMode.ForRead);
if (!regAppTable.Has(TestAppName))
{
using (RegAppTableRecord regAppRecord = new RegAppTableRecord())
{
regAppRecord.Name = TestAppName;
regAppTable.UpgradeOpen();
regAppTable.Add(regAppRecord);
regAppTable.DowngradeOpen();
tr.AddNewlyCreatedDBObject(regAppRecord, true);
}
}
Entity ent = (Entity)tr.GetObject(prEntRes.ObjectId, OpenMode.ForWrite);
ent.XData = new ResultBuffer
(
new TypedValue((int)DxfCode.ExtendedDataRegAppName, TestAppName), //1001
new TypedValue((int)DxfCode.ExtendedDataAsciiString, "String1"), //1000
new TypedValue((int)DxfCode.ExtendedDataLayerName, "0"), //1003
new TypedValue((int)DxfCode.ExtendedDataBinaryChunk, Encoding.ASCII.GetBytes("BinaryChunk")), //1004
new TypedValue((int)DxfCode.ExtendedDataHandle, ent.Handle), //1005
new TypedValue((int)DxfCode.ExtendedDataXCoordinate, new Point3d(1.1, 2.1, 3.1)), //1010
//new TypedValue((int)DxfCode.ExtendedDataYCoordinate, 2.1), //1020
//new TypedValue((int)DxfCode.ExtendedDataZCoordinate, 3.1), //1030
new TypedValue((int)DxfCode.ExtendedDataWorldXCoordinate, new Point3d(1.2, 2.2, 3.2)), //1011
//new TypedValue((int)DxfCode.ExtendedDataWorldYCoordinate, 2.2), //1021
//new TypedValue((int)DxfCode.ExtendedDataWorldZCoordinate, 3.2), //1031
new TypedValue((int)DxfCode.ExtendedDataWorldXDisp, new Point3d(1.3, 2.3, 3.3)), //1012
//new TypedValue((int)DxfCode.ExtendedDataWorldYDisp, 2.3), //1022
//new TypedValue((int)DxfCode.ExtendedDataWorldZDisp, 3.3), //1032
new TypedValue((int)DxfCode.ExtendedDataWorldXDir, new Point3d(1.4, 2.4, 3.4)), //1013
//new TypedValue((int)DxfCode.ExtendedDataWorldYDir, 2.4), //1023
//new TypedValue((int)DxfCode.ExtendedDataWorldZDir, 3.4), //1033
new TypedValue((int)DxfCode.ExtendedDataControlString, "{"), //1002
new TypedValue((int)DxfCode.ExtendedDataReal, 12345.6789), //1040
new TypedValue((int)DxfCode.ExtendedDataDist, 25.25), //1041
new TypedValue((int)DxfCode.ExtendedDataScale, 0.2), //1042
new TypedValue((int)DxfCode.ExtendedDataControlString, "}"), //1002
new TypedValue((int)DxfCode.ExtendedDataInteger16, 16), //1070
new TypedValue((int)DxfCode.ExtendedDataInteger32, 32) //1071
);
tr.Commit();
}
}
}
catch (System.Exception ex)
{
ed.WriteMessage(ex.ToString());
}
}
After the command is run and an entity is selected in AutoCAD, the various XData will be added to the entity successfully. If the ArxDbg tool has been loaded, we can use it to check the XData on the entity.
Here is what the ArxDbgSnoopEnts will tell us about the XData of the entity.
The code seems simple and straightforward, but there are quite some tips and tricks there.
• The XData has to be started with the 1001 (DxfCode.ExtendedDataRegAppName) code.
• The XData Application Name has to be registered beforehand.
• The XData Application Name can be registered through creating a RegAppTableRecord and adding it to the RegAppTable (another kind of AutoCAD symbol table).
• The RegAppTable had better be opened for READ first to check the existence of the RegAppTableRecord.
• If the RegAppTableRecord does not exist in the RegAppTable, it can be upgraded to WRITE through the UpgradeOpen() call.
• The UpgradeOpen() had better be balanced with the DowngradeOpen() call though not necessary if the object/entity is closed right after the modification.
• The Entity.XData property can be used to retrieve all the XData associated with the entity if any or to set some XData to it.
• The XData property is inherited from the DBObject actually indicating that all AutoCAD database resident objects having graphics or not such as layers and line types all can have XData.
• The ResultBuffer is a list of TypedValue which is a pair of type and value.
• The first element of the ResultBuffer has to be the registered XData application name with the integer 1001 or the enum value DxfCode.ExtendedDataRegAppName.
• The DxfCode.ExtendedDataLayerName enum value (with integer 1003) specifies that the value will be a layer name. Whether the layer has to present or not in the current database may need further exploration.
• The DxfCode.ExtendedDataBinaryChunk enum value (with integer 1004) specifies that the XData value will be some binary chunk. We will address it further in the future.
• The DxfCode.ExtendedDataHandle enum value (with integer 1005) specifies that the XData value is supposed to be a valid AutoCAD object handle. If it is not a good object handle or the object it references to has been deleted, the Audio command will report an error and the Fix option will set it as NULL or empty.
• The DxfCode.ExtendedDataXCoordinate enum value (with integer 1010) and the DxfCode.ExtendedDataWorldXCoordinate (with integer 1011) specify that the XData value will be a 3D point.
• When the two enum values (1010 or 1011) are specified as the XData type, their value objects should be of type Point3d.
• The DxfCode.ExtendedDataYCoordinate (1020) and the DxfCode.ExtendedDataZCoordinate (1030) enum values are there for other purposes. They should not be used in the TypedValue constructors. Otherwise, exceptions would just occur.
• The same apply to the ExtendedDataWorldYCoordinate (1021) and the ExtendedDataWorldYCoordinate (1031) enum values.
• Though the DxfCode.ExtendedDataWorldXDisp (1012) and the DxfCode.ExtendedDataWorldXDir (1013) indicate that the XData value should be a displacement, direction, or a vector, the TypedValue constructor does not accept a Vector3d object! They still only accept the type of Point3d!
• Once again, the DxfCode.ExtendedDataWorldYDisp(1022)/DxfCode.ExtendedDataWorldZDisp(1032) and DxfCode.ExtendedDataWorldYDir(1023)/DxfCode.ExtendedDataWorldZDir(1033) are there for other purposes. They should not be used in the TypedValue constructors. Otherwise, exceptions would just occur.
• The DxfCode.ExtendedDataControlString indicates that the XData value can be either "{" or “}” and the two symbols must be balanced in the whole XData group. They can be nested.
• The DxfCode.ExtendedDataReal (1040), DxfCode.ExtendedDataDist(1041), DxfCode.ExtendedDataScale, (1042), DxfCode.ExtendedDataInteger16 (1070) and DxfCode.ExtendedDataInteger32 (1071) are straightforward as their names suggest.
The leading edge AutoCAD .NET Addin Wizard (AcadNetAddinWizard) provides project wizards in C#, VB.NET and CLI/Managed C++, and various item wizards such as Event Handlers, Command/LispFunction Definers, and Entity/Draw Jiggers in both C# and VB.NET, to help program AutoCAD addins.
Be careful depending on XDATA in blocks with attributes - ATTSYNC removes all XDATA from blocks. See http://docs.autodesk.com/ACD/2010/ENU/AutoCAD%202010%20User%20Documentation/index.html?url=WS1a9193826455f5ffa23ce210c4a30acaf-512c.htm,topicNumber=d0e199454
Posted by: CAD bloke | 11/14/2012 at 10:43 PM
Ewen, thx for the info. It's not good that the AttSync would erase all XData associated with block references. We should be really careful about it!
Posted by: Spiderinnet1 | 11/14/2012 at 11:43 PM
Why you initialized the regAppRecord in the "using" block? This object will be added to database, but in your code will be called Dispose for this object. You shan't cause the Dispose method if the object is added in a database (as far as I remember).
Posted by: Андрей Бушман | 05/15/2014 at 10:04 AM
Андрей Бушман,
It might be true in the ARX/C++ world, but not for .NET. In fact, wrapping the RegAppTableRecord creation in a using block prevents AutoCAD from crashing if the object is not able to be successfully added to the database due to some reason. If successful, it won't hurt to dispose of the newly created RegAppTableRecord instance even if not quite necessary in this case.
Posted by: Spiderinnet1 | 05/15/2014 at 05:29 PM
Thank you for answer. How a using block can prevent AutoCAD from crashing? It is not the try\catch block. I don't philosophize, but simply I try to understand your response. Will it correctly if I not use the "using" block in this case, but use the try\catch block?
Regards.
Posted by: Андрей Бушман | 05/16/2014 at 05:05 AM
I see here ( http://through-the-interface.typepad.com/through_the_interface/2007/04/adding_xdata_to.html ) what Kean Walmsley not use the "using" block for the RegAppTableRecord, and not call RegAppTableRecord.Dispose.
Posted by: Андрей Бушман | 05/16/2014 at 05:21 AM
Андрей Бушман, the 'using' is kind of try/finally + Dispose(). Not using 'using' with RegAppTableRecord or any other DBObject/Entity creations might not get AutoCAD into trouble soon, but using 'using' properly and timely could avoid so later.
Posted by: Spiderinnet1 | 05/16/2014 at 01:12 PM
Hi Spiderinnet1!
Be careful! If DBObject-derived object call Dispose() method:
a) if object is database resident then is call Close()
b) if object is not database resident then is delete unmanaged object
So if you code run without errors then regAppRecord is closed twice: first after 'using' block end, and second after tr.Commit() which also call Close() for all transaction resident object. It is potentially may crash AutoCAD.
Posted by: Alexander Rivilis | 05/16/2014 at 05:41 PM
Alexander Rivilis, thanks for the tip regarding possibly closing the object twice. Not sure doing so would crash AutoCAD though. It may be worth of some investigations and/or another post.
By the way, it was really found that if the DBObject were not disposed of using 'using' or try/catch/finally AutoCAD would behave abnormally later in case its addition to the Database failed due to some reason, as the recent post demonstrated.
Posted by: Spiderinnet1 | 05/16/2014 at 07:54 PM