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:

63 responses to “Getting AutoCAD block attributes from a .NET application”

  1. 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.

  2. Hi Kean
    Thanks for your code, do you have a vbnet simmilar example?. Does it list raster block references attribuites?
    Thanks again

  3. 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:

    keanw.com/...

    This tool simply uses a website convertor, such as at:

    carlosag.net/Tools/C...

    This automatic conversion should get you a good deal of the way there...

    Regards,

    Kean

  4. BTW - I don't know what you mean by "raster block references"...

    Regards,

    Kean

  5. 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

  6. 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.

  7. 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 (autodesk.com/dev...). I'd suggest posting your problematic code to the AutoCAD .NET Discussion Group (discussion.autodesk....).

    Regards,

    Kean

  8. 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

  9. 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

  10. 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

  11. Hi Raul,

    You probably don't have the object open for write... I'd suggest posting the code to the .NET Customization Discussion Group (discussion.autodesk....).

    Regards,

    Kean

  12. Hi Kean;
    I posted my code on the group you mentioned some days ago, see discussion.autodesk....
    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

  13. Hi Kean
    I replied to you at discussion.autodesk....

    I will appreciate if you review it.

    Thanks

    Raul

  14. Reply posted...

    Kean

  15. Hi Kean
    I did not have the opportunity to thanks for your last reply on discussion.autodesk....
    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

  16. Hi Raul,

    Great - I'm glad you got it working.

    Regards,

    Kean

  17. 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

  18. 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

  19. 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

  20. 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....?

  21. 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

  22. 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.

  23. 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.

  24. 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

  25. Kean Walmsley Avatar

    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:

    usa.autodesk.com/ads...

    Regards,

    Kean

  26. Hi Kean.
    Hope you are doing fine with your family. I have am been doing some research on .net wrapped Arx Objects and I have not been able to find an example related to FileDependencyInfo and FileDependencyManager .net objects. I will appreciate if you can shows the implementation of these objects in .net

    Best regards

    Raul

  27. Hi Raul,

    I'll add it to my list.

    Regards,

    Kean

  28. Is there any way to get the actions placed inside the Dynamic Blocks via objectARX?

    Regards,
    Daniel Enoch. S

  29. There is an API exposed to Dynamics Blocks, although I haven't done much with it. I'd suggest asking the question via the ADN site or the ObjectARX Discussion Group.

    Regards,

    Kean

  30. I know we can create dll's in .Net and run them in Autocad. But can I make drawings using commands and save the file within c# ?
    what i mean is can i work as we do with a .txt file i.e. create , open, write etc in .Net.

  31. Kean Walmsley Avatar

    You can populate Database objects and save them as DWG (binary) or DXF (text) formats using C#. And read them back in, of course.

    The Database can be loaded in the Editor or in a side database.

    Kean

  32. version in VB:

    Public Sub ListAttributes()
    Dim ed As Editor = Application.DocumentManager.MdiActiveDocument.Editor
    Dim db As Database = HostApplicationServices.WorkingDatabase
    Dim tr As Transaction = db.TransactionManager.StartTransaction

    ' Start the transaction
    Try
    ' Build a filter list so that only
    ' block references are selected
    Dim filList As TypedValue() = New TypedValue() {New TypedValue(CType(DxfCode.Start, Integer), "INSERT")}
    Dim filter As SelectionFilter = New SelectionFilter(filList)
    Dim opts As PromptSelectionOptions = New PromptSelectionOptions

    opts.MessageForAdding = "Select block references: "

    Dim res As PromptSelectionResult = ed.GetSelection(opts, filter)

    ' Do nothing if selection is unsuccessful
    If (res.Status <> PromptStatus.OK) Then
    Return
    End If

    Dim selSet As SelectionSet = res.Value
    Dim idArray() As ObjectId = selSet.GetObjectIds

    For Each blkId As ObjectId In idArray
    Dim blkRef As BlockReference = CType(tr.GetObject(blkId, OpenMode.ForRead), BlockReference)
    Dim btr As BlockTableRecord = CType(tr.GetObject(blkRef.BlockTableRecord, OpenMode.ForRead), BlockTableRecord)

    ed.WriteMessage("" & vbCrLf & "Block: " & btr.Name)
    btr.Dispose()

    Dim attCol As AttributeCollection = blkRef.AttributeCollection

    For Each attId As ObjectId In attCol
    Dim attRef As AttributeReference = CType(tr.GetObject(attId, OpenMode.ForRead), AttributeReference)
    Dim str As String = ("" & vbCrLf & " Attribute Tag: " & (attRef.Tag + ("" & vbCrLf & " Attribute String: " & attRef.TextString)))
    ed.WriteMessage(str)
    Next

    Next

    tr.Commit()

    Catch ex As Autodesk.AutoCAD.Runtime.Exception
    ed.WriteMessage(("Exception: " + ex.Message))
    Finally
    tr.Dispose()
    End Try

    End Sub

  33. Hi Kean!
    I'm not an expert in Autocad or programming!
    My question is: how can I get the attributes for other types of entities. In my case, I need to query the AttributeCollection of a PolylineVertex3d object. If i try your piece of code, I receive a cast exception.

  34. Hi Peter,

    Attributes are something that are quite specific to blocks. What do you mean by the term attributes in the context of polyline vertices?

    Regards,

    Kean

  35. I have a Polyline3d and the polyline was draw using some points, which seem to be BlockReferences objects(which have an attribute which I need to use). Based on that attribute, I want to move the polyline in one specific layer.

    But the only points I can get are the PolylineVertex3d and not those BlockReferences. Can you give an advice?

  36. Hmm... is that a standard AutoCAD feature? I haven't used this capability myself, and can't find any documentation on it. I've found a few external apps/routines that people have used to do this - is it possible you've been using a routine to keep blocks placed at polyline vertex locations?

    If there's no specific connection with the polyline, you may need to do extra work to identify which block reference is at which vertex.

    Kean

  37. I found a relation between the vertexes and the BlockReferences. Thank you Kean for your time. 🙂

  38. This is all very helpfull..... Is it actually possible to select and attribute within a block using the:

    TypedValue[] filList = new TypedValue[1] {
    new TypedValue((int)DxfCode.Start, "INSERT")
    i.e. Place antoher typed Value here for AttTag
    };

  39. Kean Walmsley Avatar

    Hi Stephan,

    Probably not directly. You can use the approach shown here to specify the DxfCode.AttributeTag, but I doubt that will allow direct selection.

    Let us know how you get on...

    Regards,

    Kean

  40. No luck really, The main reson I ask is that you can do this in LISP and was confident you could archieve this with the Selection Filters in VB.NET.

    I tried:

    DXFCODE.OPERATOR "<and" dxfcode.blockname,="" "theblockname"="" dxfcode.start,="" "attrib"="" dxfcode.attibutetag,="" "thetagname"="" dxfcode.operator="" "and="">"

    But with no luck..... I will persist but.

  41. after doing some more research into the LISP end it seams the the attributes are the next level down to the block, just like you have shown here. Pitty

  42. i want to achieve the block reference..and its property ..
    but when i select selectiion set it get objects not the blocks ...so i need block selection...suggest me proper solution..........
    Thanks in advance

  43. Rakesh,

    As mentioned earlier today, this is not a support forum.

    Please contact ADN or post to the appropriate online discussion forum.

    Regards,

    Kean

  44. Hi Kean,
    I would like to select a block called "TITLEBLOCK" from a drawing file that varies name and location based on the project number. Then "GET" all of the attribute tags and values and either create or update custom sheet set properties based on the original selection set. Usage would be similar to:
    1. Select file which contains "TITLEBLOCK" - user input
    2. Extract attribute tags and values - programmatically similar to this post
    3. Select ".dst file" to modify - user input
    4. Update ".dst file" - programmatically
    Your help is greatly appreciated.

  45. I'm afraid I don't have time to provide support when not directly related to code posted on my blog.

    Please post your question via ADN or to the appropriate discussion forum.

    Kean

  46. I've watching this code and it's nearly what I need.
    I want to read the block attributes from several .dwg files and I want to do it with "ReadDwgFile" property from dataBase.

    I know the name of the block I want to found and it's the same to all files, buy I don't know how to implement this solution without clicking on the screen.

    I think I'll have to loop all the blocks but I don't know how to do it.

    Can you help me?

  47. Hi Kean,
    Thanks for you code.
    How can I get the dependencies "Autodesk.AutoCAD;" ?

    Best regards,
    Ihab

  48. Kean Walmsley Avatar
    Kean Walmsley

    Hi Ihab,

    The easiest way (for AutoCAD 2015 or above) is via NuGet:

    keanw.com/2014/12/autocad-2015-apis-available-via-nuget.html

    keanw.com/2015/03/nuget-packages-now-available-for-autocad-2016.html

    For older versions you should install the appropriate ObjectARX SDK (from autodesk.com/objectarx) and use the reference assemblies in the inc folders.

    Regards,

    Kean

  49. Thanks Keen

    1. Hi Kean,

      I have array of dwg file names and I want to loop to get block attributes, all I have is the dwg file name, what methods should I use to parse the files and get the block attributes.

      Best regards,
      Ihab

      1. Kean Walmsley Avatar
        Kean Walmsley

        It's all there in the link I provided you previously.

        Kean

  50. Hi Kean,

    I have list of dwg files and I need to loop over the list to get the dwg block attributes from the drawing, all I have is the file path & name, but I tried to run your sample gives me file not found exception.

    Best regards,
    Ihab

    1. Kean Walmsley Avatar

      Hi Ihab,

      I assume you're talking about a different post - this one works on a single file.

      But the type of the exception is a big clue. Make sure it's a valid path (System.IO.File.Exists() shoudl help, here).

      Regards,

      Kean

  51. Hi Kean,

    I have list of gwt file names I want to loop over it and get the attributes block for every file, all I have is the file name, how to apply it using the API's ?

    Best regards,
    Ihab

  52. Kean Walmsley Avatar
    Kean Walmsley

    Hi Ihab,

    This should be of some help:

    keanw.com/2007/07/updating-a-sp-1.html

    Regards,

    Kean

  53. Hi Kean and folks,

    In this blog post we are looking at the tags and their associated values through block references, not the block table record themselves. Which brings us to an interesting question: is it possible to get the tags (i.e. the tag names) of a particular block (not the tag values associated with the names) directly from a block record - if so any ideas how this is possible?

    rgds, Ben

    1. For those interested: here is how I solved it - probably not the prettiest solution out there, but hey it works 🙂 AttributeDefinition is the key that you are looking for!

      If you can improve upon it, please let everyone know!

  54. i use vb.net To dev winform app. I can take blockreference, block name"Tab1". Now i want Get this block To edit block attribute, but i don't know how to do that,i search on gg but have no result. can u help me, please !!

    uploads.disquscdn.c...

    1. Please post your request to the AutoCAD .NET forum.

      Kean

  55. Yousef Golnahali Avatar
    Yousef Golnahali

    hi kean, i changed this code as shown in image, what is wrong? i have block with this name but this code doesn't work
    1

    1. Kean Walmsley Avatar

      Hi Yousef,

      This post is 11 years old - unfortunately I'm working on other things. Can you please post to the AutoCAD .NET forum and post your modified code there?

      Thanks,

      Kean

      1. Yousef Golnahali Avatar
        Yousef Golnahali

        Ok and Thanks for your 11 years old best sample for Autocad .Net

  56. I have a block with the following:

    NUMBER:
    DESCRIPTION:
    SECTOR:

    I would like to insert into the routine of this post the possibility of changing (Update) the values of the information of the selected block.

    I'm waiting
    Thank you

Leave a Reply to Stephan Cancel reply

Your email address will not be published. Required fields are marked *