Getting AutoCAD block attributes from a .NET application
It's been a hectic week, between one thing and another, and I don’t expect it to get better anytime soon: apart from anything else, my wife is due to give birth any day to our second child. We're both excited - and admittedly a little anxious - about it, and our 2 year-old seems to be feeding of that, which means none of us have ended getting much sleep of late. All good preparation, I suppose. :-)
So I decided the path of least resistance for getting a blog entry out today was to look through some of the responses DevTech have sent out to ADN members recently, to find something that might be of interest to a wider audience. A lot of what we do ends up being very specific to individual situations, but we also get requests for code samples that prove to be of general interest (these latter topics are generally turned into DevNotes (technical solutions) and get posted to the ADN website). Over the busy months ahead I'm sure I'll end up feeding many of these through this blog.
One response that caught my eye was this helpful little code sample, written by Varadan (a.k.a. Krishnan Varadarajan), a member of the DevTech team in India.
It asks the user to select block references, and then goes through them, looking for any contained attributes, which it then dumps to the AutoCAD console:
using Autodesk.AutoCAD;
using Autodesk.AutoCAD.Runtime;
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.EditorInput;
namespace MyApplication
{
public class DumpAttributes
{
[CommandMethod("LISTATT")]
public void ListAttributes()
{
Editor ed =
Application.DocumentManager.MdiActiveDocument.Editor;
Database db =
HostApplicationServices.WorkingDatabase;
Transaction tr =
db.TransactionManager.StartTransaction();
// Start the transaction
try
{
// Build a filter list so that only
// block references are selected
TypedValue[] filList = new TypedValue[1] {
new TypedValue((int)DxfCode.Start, "INSERT")
};
SelectionFilter filter =
new SelectionFilter(filList);
PromptSelectionOptions opts =
new PromptSelectionOptions();
opts.MessageForAdding = "Select block references: ";
PromptSelectionResult res =
ed.GetSelection(opts, filter);
// Do nothing if selection is unsuccessful
if (res.Status != PromptStatus.OK)
return;
SelectionSet selSet = res.Value;
ObjectId[] idArray = selSet.GetObjectIds();
foreach (ObjectId blkId in idArray)
{
BlockReference blkRef =
(BlockReference)tr.GetObject(blkId,
OpenMode.ForRead);
BlockTableRecord btr =
(BlockTableRecord)tr.GetObject(
blkRef.BlockTableRecord,
OpenMode.ForRead
);
ed.WriteMessage(
"\nBlock: " + btr.Name
);
btr.Dispose();
AttributeCollection attCol =
blkRef.AttributeCollection;
foreach (ObjectId attId in attCol)
{
AttributeReference attRef =
(AttributeReference)tr.GetObject(attId,
OpenMode.ForRead);
string str =
("\n Attribute Tag: "
+ attRef.Tag
+ "\n Attribute String: "
+ attRef.TextString
);
ed.WriteMessage(str);
}
}
tr.Commit();
}
catch (Autodesk.AutoCAD.Runtime.Exception ex)
{
ed.WriteMessage(("Exception: " + ex.Message));
}
finally
{
tr.Dispose();
}
}
}
}
Here's what happened when I ran it against the "Blocks and Tables - Imperial.dwg" sample drawing that ships with AutoCAD. On running the LISTATT command and selecting the title block on the right of the page, here's what was dumped out:
Command: LISTATT
Select block references: 1 found
Select block references:
Block: ARCHBDR-D
Attribute Tag: NAME
Attribute String: CORY B.
Attribute Tag: NAME
Attribute String: BOB M.
Attribute Tag: DATE
Attribute String:
Attribute Tag: X"=X'-X"
Attribute String: 1/4" =1'
Attribute Tag: 0
Attribute String: 1
Attribute Tag: 0
Attribute String: 1
Attribute Tag: PROJECT
Attribute String: ADDA
Attribute Tag: TITLE
Attribute String: FLOOR PLANS
Attribute Tag: X
Attribute String:
Attribute Tag: X
Attribute String:
Attribute Tag: X
Attribute String:
Attribute Tag: X/XX/XX
Attribute String:
Attribute Tag: X/XX/XX
Attribute String:
Attribute Tag: X/XX/XX
Attribute String:
Attribute Tag: COMMENT
Attribute String:
Attribute Tag: COMMENT
Attribute String:
Attribute Tag: COMMENT
Attribute String:
Command:

Subscribe via RSS
The try/finally in this example could be replaced with using (Transaction tr = db.TransactionManager.StartTransaction()). No difference in the generated IL code but more elegant and readable code.
Posted by: Albert | September 20, 2006 at 05:56 PM
Hi Kean
Thanks for your code, do you have a vbnet simmilar example?. Does it list raster block references attribuites?
Thanks again
Posted by: Raul | September 27, 2006 at 08:11 PM
Hi Raul,
Sorry, no (and have my hands full on paternity leave right now).
I generally use an automatic conversion tool such as the one mentioned in this previous entry:
http://through-the-interface.typepad.com/through_the_interface/2006/08/some_cool_copyp.html
This tool simply uses a website convertor, such as at:
http://carlosag.net/Tools/CodeTranslator/Default.aspx
This automatic conversion should get you a good deal of the way there...
Regards,
Kean
Posted by: Kean | September 29, 2006 at 07:58 PM
BTW - I don't know what you mean by "raster block references"...
Regards,
Kean
Posted by: Kean | September 29, 2006 at 07:59 PM
Thanks Kean for your tip and congrats for your newborn, I am a 2 little girls father (1 and 4 year old), so no hurry about technical stuff answers.
I meant by "raster block references" the external references of the *.jpg or *.tif type. I was able to get the block id of files (.dwg ones) attached as external references, and then its attribuites like the file path.
Well, hope you get some decent sleep and thanks again for your usefull blog, I have found some good code and history.
Regards
Posted by: Raul | October 12, 2006 at 01:44 AM
Thanks for the code. Can you help me on the code for doing the oposite ie edit block attributes given a known objectid for the block. I have seach for the code seem to be struggling especially with the open.mode.write. Also can with resources can i learn autocad vb.net.
Posted by: edmore | October 12, 2006 at 09:39 PM
For Raul:
Thanks! :-)
Although conceptually similar to blocks, rasters are actually separate objects. You would need to change the above code to handle raster image and raster image definition objects:
Autodesk.AutoCAD.DatabaseServices.RasterImage
Autodesk.AutoCAD.DatabaseServices.RasterImageDef
For Ed:
Check out the resources listed on the AutoCAD Developer Center (http://www.autodesk.com/developautocad). I'd suggest posting your problematic code to the AutoCAD .NET Discussion Group (http://discussion.autodesk.com/forum.jspa?forumID=152).
Regards,
Kean
Posted by: Kean | October 13, 2006 at 01:24 PM
Hi Kean;
Thanks for the hint, I found a sample to get raster image attributes, but unfortunately I have not found any sample to set raster image attributes like the filename.
Well, I hope you find the time, maybe one of those sleepless nights ;-) to show us the way in this blog.
Thanks again
Raul
Posted by: Raul | October 18, 2006 at 03:19 AM
Hi Raul,
Oh, you need to repath images? I haven't tried it, but you might try getting the RasterImageDef and setting the ActiveFilePath property.
Regards,
Kean
Posted by: Kean | October 18, 2006 at 05:05 PM
Hi Kean,
Thanks for your fast reply. I coud not find the RasterImageDef.ActiveFilePath property although I tried to set rasterImageDef.SourceFileName and rasterImageDef.ActiveFileName, both throw an exception.
Thanks
Raul
Posted by: Raul | October 19, 2006 at 01:46 AM
Hi Raul,
You probably don't have the object open for write... I'd suggest posting the code to the .NET Customization Discussion Group (http://discussion.autodesk.com/forum.jspa?forumID=152).
Regards,
Kean
Posted by: Kean | October 19, 2006 at 12:10 PM
Hi Kean;
I posted my code on the group you mentioned some days ago, see http://discussion.autodesk.com/thread.jspa?threadID=513346
no answer yet, I changed all the read occurences of the object for OpenMode.ForWrite and still I do not see the ActiveFilePath property.
Any help I will appreciate.
Thanks
Raul
Posted by: Raul | October 20, 2006 at 09:45 AM
Hi Kean
I replied to you at http://discussion.autodesk.com/thread.jspa?messageID=5368660
I will appreciate if you review it.
Thanks
Raul
Posted by: Raul | October 26, 2006 at 10:52 PM
Reply posted...
Kean
Posted by: Kean | October 27, 2006 at 10:44 AM
Hi Kean
I did not have the opportunity to thanks for your last reply on http://discussion.autodesk.com/thread.jspa?threadID=513346
It works fine now. I had to add a line imageAttributeDef.load since it did not load the raster reference by default. Also this repath operation works for the database of files that are not opened, I will investigate how to do it on open files.
I noticed that AutoCAD 2007 adds a DWF external reference feature, I will also investigate how to repath those files.
Again, thanks for your altruistic effort.
Best wishes
Raul
Posted by: Raul | November 08, 2006 at 02:55 PM
Hi Raul,
Great - I'm glad you got it working.
Regards,
Kean
Posted by: Kean | November 08, 2006 at 03:30 PM
Hi Kean, hows parenthood suiting you?
I have a question regarding 3rd party supplied blocks.
Is there a way to modify the layers on an 3rd party issued block so it "works" in my drawings.
I have hundreds to go through from dozens of suppliers and it just seems too much to open each and every block and send each part to the relevant layers in my drawings.
Do you have any clues or ideas?
Any help would be great.
Thanks
Posted by: Adie Porter | November 10, 2006 at 08:02 AM
Hi Adie,
Parenthood's treating me well, thanks (having 2 boys under 3 years old is a challenge, but luckily the youngest is still fairly easy to handle).
I'd suggest checking out the CAD Standards implementation... aside from the STANDARDS command, take a look at LAYTRANS and also the Batch Standards Checker (a separate executable that can check across sets of files).
You could always implement some custom code to go in and do this, but it's always worth seeing what standard functionality already exists.
Regards,
Kean
Posted by: Kean | November 10, 2006 at 02:30 PM
Hi Kean,
Sorry about the delay getting back to you,
Thanks for the info, i will be trawling through looking at these tips.
Thanks
Adie
Posted by: Adie Porter | November 29, 2006 at 06:29 AM
Hi Kean
I´m using Autocad 2006 and .Net and I want to know how can i get the Id of one document (dwg) throw .net, and if i make a copy of the dwg file (document) when i open the copy it has the same Id of the original....?
Posted by: Cynthia | April 02, 2007 at 06:52 AM
Hi Cynthia,
Which ID are you referring to? Do you mean the FingerprintGuid, or something else? A new FingerprintGuid does get assigned when you SaveAs, WBlock a new DWG, etc., but there's no way to control someone copying the file through Explorer etc.
But then it sounds as though you want to maintain the same ID as the original (which FingerprintGuid mostly will not), so you may need to find another way to do this. Bear in mind that with this logic, nearly every DWG would end up referring to the template it was created from...
Regards,
Kean
Posted by: Kean | April 02, 2007 at 08:45 AM
Hi Kean, sorry, in my las comment i wasn´t clear.
I´m going to explain what i want to do.
I start Autocad 2006, and creat a new document or dwg. I set a name like Test.dwg and save it. Then i draw some lines, I know each line has an unique identifier assigned (id) in the draw or document, so i get throw .net each id of each line and save it in a external database with name and others properties i add.
I close Autocad, i start again and open Test.dwg, y choose each line i draw before, and the unique identifier assigned (id) of the lines maintance the same value, but i can get the unique identifier assigned (id) of the document. I belive that autocad must assign an id for each document ( when i said document i mean dwg file), so i need to get unique identifier assigned (id) to the document and then relate it with the lines drawed. This way I could save and relate a document wich all objets drawed in it throw each id´s and save it in an external database like Acces.
I want to know too if i make a file copy of the Test.dwg, what happend with the unique identifier assigned (id) of the document or dwg. And if there's no way to control someone copying the file through Explorer etc. What happend with the Id´s of the lines and other objects drawed in it.
Thanks so much, and i´m happy to have the oportunity to ask you.
Posted by: Cynthia | April 15, 2007 at 10:29 PM
You can use the FingerprintGuid property the Database object, but this will remain the same across DWGs file copied via Explorer (for instance). AutoCAD is simply not involved in the copying of files at the OS-level.
Most developers working on this type of problem would rely on the uniqueness of the file location on the system (using its path/URL/etc.).
I assume you're using handles for object identifiers: these are maintained (and kept unique) for the drawing's lifetime, but are not guaranteed to be unique across DWGs.
Posted by: Kean | April 16, 2007 at 09:45 AM
Hi Kean,
I am back again, hopping you are doing fine with your kids.
After working doing programmatic .net repath work with different external references objects using AutoCAD 2008 I got few comments you may find useful.
DWG external reference object:
It accepts any kind of path convention, local i.e. c:\myxdwgref.dwg and network \\myserver\myxdwgref.dwg, relative local and url i.e. .\..\ and ./../ types, absolute url i.e. http://myserver/myxdwgref.dwg
Raster image external reference (jpg, bmp, etc):
It behaves as above but it changes any relative url path reference by its local counterpart i.e.
RasterImageDef.SourceFileName = "./myxdwgref.dwg"
It accepts the value but it changes to ".\myxdwgref.dwg"
DWF and DGN underlay references:
Works as DWG references, but it do not process absolute url references, i.e. when using the following,
DwfDefinition.SourFileName = "http://myserver/myDwfXref.dwf" it takes the value but it does not load the myDwfXref.dwf as rasters and dwg objects do. It seems that dwg and raster external references are downloaded automatically.
Well I just wanted to expose what I think there are some inconsistencies on several external references objects. I hope Autodesk will come with some fix in future releases.
I will appreciate any comments you may have
Best wishes
Raul
Posted by: Raul | May 09, 2007 at 04:04 PM
Hi Raul,
Thanks - we're all doing very well. :-)
Some of the differences you've found are probably historical (the objects having been implemented at different times) and some are probably logical (the objects do behave differently).
If there are product changes you would like to see happen, then I suggest submitting through ADN (if you're a member), or otherwise you can submit them via this form on the Autodesk website:
http://usa.autodesk.com/adsk/servlet/index?siteID=123112&id=1109794
Regards,
Kean
Posted by: Kean | May 10, 2007 at 03:13 PM