.NET

Reply
*Norman Yuan
Message 1 of 10 (632 Views)

How to set an entity's XData

632 Views, 9 Replies
08-23-2006 09:13 AM
Due to very poor documentation of .NET API (or should I say, no
documentation), I am having trouoble to attach Xdata to acadentity.

Here are the code that I wrote to set XData to a user picked entity:

using System;
using Autodesk.AutoCAD.Runtime;
using Autodesk.AutoCAD.EditorInput;
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
using System.Windows.Forms;

namespace ClassLibraryTest
{
public class MyClass
{
public MyClass()
{

}

[CommandMethod("SetMyXData")]
public void SetTheXData()
{
Document dwg =
Autodesk.AutoCAD.ApplicationServices.Application.DocumentManager.MdiActiveDocument;
ObjectId id=ObjectId.Null;

//A method to let user to Pick an entity of given type, ObjectId
is returned via an output parameter
PromptStatus status = PickAcadEntity(dwg, "Please pick a
polyline: ", typeof(Polyline), out id);
if (id==ObjectId.Null)
{
MessageBox.Show("No valid entity is picked: must be a
POLYLINE.");
return;
}

if (status != PromptStatus.OK)
{
MessageBox.Show("Invalid pick: no object is picked or picked
object is not a Polyline.");
return;
}

//Check if "MyApp" is registered app for XData or not
using (Transaction tran =
dwg.TransactionManager.StartTransaction())
{
RegAppTable appTable =
(RegAppTable)tran.GetObject(dwg.Database.RegAppTableId, OpenMode.ForWrite);
bool exist = false;
foreach (ObjectId aID in appTable)
{
RegAppTableRecord app =
(RegAppTableRecord)tran.GetObject(aID, OpenMode.ForRead);
if (app.Name.ToUpper() == "MyApp")
{
exist = true;
break;
}
}

//Create RegAppTableRecord, if needed
if (!exist)
{
RegAppTableRecord tr = new RegAppTableRecord();
tr.Name = "MyApp";
appTable.Add(tr);
}

tran.Commit();
}

//Set XData to picked entity
using (Transaction tran =
dwg.Database.TransactionManager.StartTransaction())
{
try
{
Entity ent = (Entity)tran.GetObject(id,
OpenMode.ForWrite, false);

//Create XData
TypedValue[] xdata = new TypedValue[]
{
new
TypedValue(Convert.ToInt16(DxfCode.ExtendedDataRegAppName),"MyApp"),
new
TypedValue(Convert.ToInt16(DxfCode.ExtendedDataAsciiString),"MyData")
};

ResultBuffer res = new ResultBuffer(xdata);

ent.XData = res;

tran.Commit();

MessageBox.Show("XData attached!");
}
catch (Autodesk.AutoCAD.Runtime.Exception ex)
{
MessageBox.Show("Attaching XData failed: " +
ex.Message);
}
}
}
}
}


When I run above code, it seems OK (Mseeage box pops up, showing "XData
attached!"). However, When I run another command that trying to retrieve the
Xdata on that entity, I get nothing. In that command I use
Entity.GetXDataForApplication("MyApp").

I also used VBA code (AcadEntity.GetXData()) to see if there is XData
attached or not. I got nothing also.

More serious problem is, after running the "SetMyXData" command (above
code), if I do not close the drawing, the entity being processed by the code
seems OK. But if I save and close the drawing, then open is again, I got
"Drawing cannot be opened due to corrupted data" error and asked to recorver
the drawing. Once recovering is done, the enity is gone (being discarded
during recovering).

Clearing, the code above corrupted the entity, although running it seems OK.

Can anyone point out what I am doing wrong, or the correct way to set/get
XData to/from an entity in .NET API?
Valued Contributor
smcclure
Posts: 60
Registered: ‎06-08-2006
Message 2 of 10 (632 Views)

Re: How to set an entity's XData

08-23-2006 10:11 AM in reply to: *Norman Yuan
Try adding XData without adding your application to the registered application names. After a quick look, your XData routine seems almost exactly the same as I use, so my guess is that the error is in adding your application to the registered app table.... Either way, trying them seperately will give you a better idea of where exactly the problem is.
*Norman Yuan
Message 3 of 10 (632 Views)

Re: How to set an entity's XData

08-23-2006 11:24 AM in reply to: *Norman Yuan
I am not sure what do you mean here by "the error is in adding your
application to the registered app table".

If I did not check if an app name is registered for XData in the database,
and set the first item in ResultBuffer object as
DxfCode.ExtendedDataRegAppName, I got error "eRegappidNotFound";

if I did not check if an app name is registered for XData in database, and
set the first item in ResultBuffer object as DxfCode.ExtendedDataAsciiString
(or any thing other than ExtendedRegAppName, i.e without supply RegAppName),
I got error "eBadDxfcodeSequency", meaning Acad cannot find registered app
name for this XData.

So, I tried your suggestion and did the regstering app name for XData in
seperate command, that is, register app name first (if not registered), and
then attach XData for that app in another command later.

It gives me the same result: command runs OK until you close the drawing and
open it again: the entity with XData attached become corrupted, recovering
would drop this entity out (recovering say "Error 34 (eWrongObjectType).
Object discarded").

Could you post your code snappet on this that works?

wrote in message news:5304386@discussion.autodesk.com...
Try adding XData without adding your application to the registered
application names. After a quick look, your XData routine seems almost
exactly the same as I use, so my guess is that the error is in adding your
application to the registered app table.... Either way, trying them
seperately will give you a better idea of where exactly the problem is.
Valued Contributor
smcclure
Posts: 60
Registered: ‎06-08-2006
Message 4 of 10 (632 Views)

Re: How to set an entity's XData

08-23-2006 11:50 AM in reply to: *Norman Yuan
Heh.... I think this may be a difference in versions of AutoCAD. I have added XData without registering the app name before, but I am running 2005 before all the checks on XData consistency were added, it seems (there is no method to retrieve XData by app. name, for instance).

I have one more suggestion. If you are wondering about the internal state of AutoCAD after executing your command, you can use the ARXDBG utility. This is an old program that was originally a ObjectARX sample that I have compiled. It is attached.
Valued Contributor
smcclure
Posts: 60
Registered: ‎06-08-2006
Message 5 of 10 (632 Views)

Re: How to set an entity's XData

08-23-2006 11:54 AM in reply to: *Norman Yuan
Ooops.... I forgot to explain how to use it. Load the ARX and right click anywhere to get to the menu. To see XData on a block, view the entity information and click the "XData" button. Viewing the database information will allow you to see the registered apps.
*Norman Yuan
Message 6 of 10 (632 Views)

Re: How to set an entity's XData

08-23-2006 02:40 PM in reply to: *Norman Yuan
Thamks for your help. I finally found the problem (very tricky) and solved
it (costing entire morning and the first hour after lunch!).

Your first instinct was right, it has something to do with registering app
name for the XData.

Here is what causes the problem: if you create a new RegAppTableRecord and
add it into RegAppTable, you have to call Dispose().

Below is the part of code I posted previously, plus the little change I
made:

Note: I added Dispose() in 3 places(marked with "!!!===") and tested the
code by commented out one or more of them in different combination, in the
end, only one matters.

//Check if "MyApp" is registered app for XData or not
using (Transaction tran =
dwg.TransactionManager.StartTransaction())
{
RegAppTable appTable =
(RegAppTable)tran.GetObject(dwg.Database.RegAppTableId, OpenMode.ForWrite);
bool exist = false;
foreach (ObjectId aID in appTable)
{
RegAppTableRecord app =
(RegAppTableRecord)tran.GetObject(aID, OpenMode.ForRead);
if (app.Name.ToUpper() == "MyApp")
{
exist = true;
break;
}

//!!!===Add Dispose() here, but has not affect
//app.Dispose();
}

//Create RegAppTableRecord, if needed
if (!exist)
{
RegAppTableRecord ct = new RegAppTableRecord();
ct.Name = "MyApp";
appTable.Add(ct);

//!!!===Added Dispose() here. Only this is required, if
you want to add XData to this application. However
//!!!===if you do registering app name and attaching
XData in seperate command, AND you close the drawing
//!!!===after restering app name, and open the drawing
again to attach XData, then you do not need this Dispose().
ct.Dispose();
}

tran.Commit();

//!!!===Add Dispose() here, but has not affect
appTable.Dispose();
}


It is just so tricky to find out the cause, and even more confusing when or
if to call object.Dispose(). In this particular case, I just do not see a
reason to call RegAppTableRecord.Dispose() after adding it into RegAppTable
object: this variable pointing to the newly created RegAppTableRecord is
going out of scope, the the RegAppTableRecord itself is handed to its
container, a RegAppTable object, why dispose it?

But anyways, after more than 5 hours struggling, I solved it. Whether it is
a .NET API implementation bug, or it is just an undocumented trick, I do not
expect there would be an explanation in future ObjectARX .NET API
documentation (if there would be such thing).



wrote in message news:5304553@discussion.autodesk.com...
Heh.... I think this may be a difference in versions of AutoCAD. I have
added XData without registering the app name before, but I am running 2005
before all the checks on XData consistency were added, it seems (there is no
method to retrieve XData by app. name, for instance).

I have one more suggestion. If you are wondering about the internal state of
AutoCAD after executing your command, you can use the ARXDBG utility. This
is an old program that was originally a ObjectARX sample that
I have compiled. It is attached.
*Tony Tanzillo
Message 7 of 10 (632 Views)

Re: How to set an entity's XData

08-23-2006 04:49 PM in reply to: *Norman Yuan
The reason why your code worked when you added
the call to Dispose(), is because you failed to add
the new RegAppTableRecord to the transaction via
AddNewlyCreatedDBObject().

When you do that, the Transaction will do the same
thing that calling Dispose() on the RegAppTableRecord
does (e.g., call Close() or Cancel() depending on if
you Commit() or Abort() the transaction, respectively).

Regarding when you must call Dispose(), it depends
on two things:

1. Is the object database resident?

2. Is the object transaction-resident?

If the answer to both questions is no, you should
call Dispose(), but you don't really need to because
when the garbage collector finalizes the object, the
wrapper will release/free the unmanaged resource.

If the object is database resident, then whether
you need to call Dispose() depends on whether the
object is transaction resident (e.g., it was returned
by calling GetObject() on the transaction, or added
to the transaction via AddNewlyCreatedDBObject()).

If the object is transaction-resident, you don't need
to call Dispose() as the transaction is responsible
for calling (and will call) either Close() or Cancel() on
the DBObject, when the transaction ends (which will
happen when you Commit(), Abort() or Dispose() it).

If a DBObject is not transaction-resident (e.g., you
used ObjectId.Open() to get the DBObject), then you
_MUST_ call either Close(), Cancel(), or Dispose() on
the object, and if you don't, it will most likely cause
AutoCAD to crash.

--
http://www.caddzone.com

AcadXTabs: MDI Document Tabs for AutoCAD 2004/2005/2006/2007
http://www.acadxtabs.com

"Norman Yuan" wrote in message news:5305024@discussion.autodesk.com...
Thamks for your help. I finally found the problem (very tricky) and solved
it (costing entire morning and the first hour after lunch!).

Your first instinct was right, it has something to do with registering app
name for the XData.

Here is what causes the problem: if you create a new RegAppTableRecord and
add it into RegAppTable, you have to call Dispose().

Below is the part of code I posted previously, plus the little change I
made:

Note: I added Dispose() in 3 places(marked with "!!!===") and tested the
code by commented out one or more of them in different combination, in the
end, only one matters.

//Check if "MyApp" is registered app for XData or not
using (Transaction tran =
dwg.TransactionManager.StartTransaction())
{
RegAppTable appTable =
(RegAppTable)tran.GetObject(dwg.Database.RegAppTableId, OpenMode.ForWrite);
bool exist = false;
foreach (ObjectId aID in appTable)
{
RegAppTableRecord app =
(RegAppTableRecord)tran.GetObject(aID, OpenMode.ForRead);
if (app.Name.ToUpper() == "MyApp")
{
exist = true;
break;
}

//!!!===Add Dispose() here, but has not affect
//app.Dispose();
}

//Create RegAppTableRecord, if needed
if (!exist)
{
RegAppTableRecord ct = new RegAppTableRecord();
ct.Name = "MyApp";
appTable.Add(ct);

//!!!===Added Dispose() here. Only this is required, if
you want to add XData to this application. However
//!!!===if you do registering app name and attaching
XData in seperate command, AND you close the drawing
//!!!===after restering app name, and open the drawing
again to attach XData, then you do not need this Dispose().
ct.Dispose();
}

tran.Commit();

//!!!===Add Dispose() here, but has not affect
appTable.Dispose();
}


It is just so tricky to find out the cause, and even more confusing when or
if to call object.Dispose(). In this particular case, I just do not see a
reason to call RegAppTableRecord.Dispose() after adding it into RegAppTable
object: this variable pointing to the newly created RegAppTableRecord is
going out of scope, the the RegAppTableRecord itself is handed to its
container, a RegAppTable object, why dispose it?

But anyways, after more than 5 hours struggling, I solved it. Whether it is
a .NET API implementation bug, or it is just an undocumented trick, I do not
expect there would be an explanation in future ObjectARX .NET API
documentation (if there would be such thing).



wrote in message news:5304553@discussion.autodesk.com...
Heh.... I think this may be a difference in versions of AutoCAD. I have
added XData without registering the app name before, but I am running 2005
before all the checks on XData consistency were added, it seems (there is no
method to retrieve XData by app. name, for instance).

I have one more suggestion. If you are wondering about the internal state of
AutoCAD after executing your command, you can use the ARXDBG utility. This
is an old program that was originally a ObjectARX sample that
I have compiled. It is attached.
Valued Contributor
smcclure
Posts: 60
Registered: ‎06-08-2006
Message 8 of 10 (632 Views)

Re: How to set an entity's XData

08-24-2006 06:11 AM in reply to: *Norman Yuan
A good trick is to use the "using" command... Any variables created at the beginning of the "using" block will have disposed called at the end of the block - it is basically a try-finally block with a new name and syntax. I tend to wrap most of my DB objects in these blocks

Question for Tony (or anyone who knows this stuff really well...) while you dont _need_ to call disposed on DB objects created under a transaction, I frequently do (the call is after Transaction.Commit()) This has never given me an error, and I do it mostly out of habit - I tend to wrap all AutoCAD related code in a try-finally/using block. Is this a bad idea?
*Tony Tanzillo
Message 9 of 10 (632 Views)

Re: How to set an entity's XData

08-24-2006 07:46 AM in reply to: *Norman Yuan
Bad idea? No, but largely pointless.

Committing/Aborting the transaction does exactly what calling
Dispose() on a DBObject that's open in the transaction does,
which is to Close()/Cancel() the open DBObject, so calling
Dispose() after the transaction is ended, is largely pointless,
and does nothing.

That is because in the case of a DB-resident object, there is
no need to release/free unmanaged resources (e.g., the
'wrapped' native AcDbObject).

However (and I have not researched this), if you add a new
DBObject (e.g., created with the new() operator) to a transaction
via AddNewlyCreatedDBObject(); but don't add the object to the
database; and you abort the transaction, in that case, you may
need to call Dispose() to free the unmanaged resource.

--
http://www.caddzone.com

AcadXTabs: MDI Document Tabs for AutoCAD 2004/2005/2006/2007
http://www.acadxtabs.com

wrote in message news:5305828@discussion.autodesk.com...
A good trick is to use the "using" command... Any variables created at the beginning of the "using" block will have disposed called at the end of the block - it is basically a try-finally block with a new name and syntax. I tend to wrap most of my DB objects in these blocks

Question for Tony (or anyone who knows this stuff really well...) while you dont _need_ to call disposed on DB objects created under a transaction, I frequently do (the call is after Transaction.Commit()) This has never given me an error, and I do it mostly out of habit - I tend to wrap all AutoCAD related code in a try-finally/using block. Is this a bad idea?
New Member
juanjo44
Posts: 2
Registered: ‎10-13-2007
Message 10 of 10 (632 Views)

Re: How to set an entity's XData

10-13-2007 07:26 PM in reply to: *Norman Yuan
Hi, I'm new to .NET and I'm trying to use de ArxDbg tool to see the database of a drawing but couldn't compile the arxdbg with VC++ express and the arxdbgd.arx file doesn't work in AutoCAD 2007.
Can anyone help please?
Post to the Community

Have questions about Autodesk products? Ask the community.

New Post
Announcements
Do you have 60 seconds to spare? The Autodesk Community Team is revamping our site ranking system and we want your feedback! Please click here to launch the 5 question survey. As always your input is greatly appreciated.