.NET
cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

How to set an entity's XData

9 REPLIES 9
Reply
Message 1 of 10
Anonymous
2660 Views, 9 Replies

How to set an entity's XData

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?
9 REPLIES 9
Message 2 of 10
smcclure
in reply to: Anonymous

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.
Message 3 of 10
Anonymous
in reply to: Anonymous

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.
Message 4 of 10
smcclure
in reply to: Anonymous

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.
Message 5 of 10
smcclure
in reply to: Anonymous

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.
Message 6 of 10
Anonymous
in reply to: Anonymous

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.
Message 7 of 10
Anonymous
in reply to: Anonymous

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.
Message 8 of 10
smcclure
in reply to: Anonymous

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?
Message 9 of 10
Anonymous
in reply to: Anonymous

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?
Message 10 of 10
juanjo44
in reply to: Anonymous

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?

Can't find what you're looking for? Ask the community or share your knowledge.

Post to forums  

Autodesk DevCon in Munich May 28-29th


Autodesk Design & Make Report

”Boost