Getting access to an entity from its handle in AutoCAD .NET

This comment came in overnight from Brian:

I was hoping to work out how to get the Entity from the handle and then erase it, from your posts, I've learned a lot but not found the answer to this one yet, I still have a mountain to climb. (I'm using VB.NET, I've done it in VB6 +COM, even in Lisp but .NET ....)

As it's such a good question, I'm making a post out of it...

The trick is to go from a hexadecimal string to a 64-bit integer (using System.Convert.ToInt64(str,16); which specifies the string as being "base-16" or hexadecimal).

From there you simply use the long integer to create a Handle, which you pass into Database.GetObjectId(): you should specify false for the first argument, as we don't want to create a new ObjectId if one does not exist, and pass 0 into the third argument (which is reserved for future use).

From there you should have a valid ObjectId that can be opened and erased. If the handle entered doesn't exist, then GetObjectId() will throw an exception - I'll leave it to the user to catch this exception more elegantly (i.e. with a nicer error message).

Here's the C# code:

using Autodesk.AutoCAD.ApplicationServices;

using Autodesk.AutoCAD.DatabaseServices;

using Autodesk.AutoCAD.EditorInput;

using Autodesk.AutoCAD.Runtime;

using System;

namespace HandleTest

{

  public class HandleCommands

  {

    [CommandMethod("EH")]

    public void EraseObjectFromHandle()

    {

      Document doc =

        Application.DocumentManager.MdiActiveDocument;

      Editor ed = doc.Editor;

      Database db = doc.Database;

      try

      {

        // Ask for a string representing the handle

        PromptResult pr =

          ed.GetString("\nEnter handle of object to erase: ");

        if (pr.Status == PromptStatus.OK)

        {

          // Convert hexadecimal string to 64-bit integer

          long ln = Convert.ToInt64(pr.StringResult, 16);

          // Not create a Handle from the long integer

          Handle hn = new Handle(ln);

          // And attempt to get an ObjectId for the Handle

          ObjectId id = db.GetObjectId(false,hn,0);

          // Finally let's open the object and erase it

          Transaction tr =

            doc.TransactionManager.StartTransaction();

          using (tr)

          {

            DBObject obj =

              tr.GetObject(id, OpenMode.ForWrite);

            obj.Erase();

            tr.Commit();

          }

        }

      }

      catch (System.Exception ex)

      {

        ed.WriteMessage(

          "Exception: " + ex

        );

      }

    }

  }

}

And as Brian specifed VB.NET, here's that, too:

Imports Autodesk.AutoCAD.ApplicationServices

Imports Autodesk.AutoCAD.DatabaseServices

Imports Autodesk.AutoCAD.EditorInput

Imports Autodesk.AutoCAD.Runtime

Imports System

Namespace HandleTest

  Public Class HandleCommands

    <CommandMethod("EH")> _

    Public Sub EraseObjectFromHandle()

      Dim doc As Document = _

        Application.DocumentManager.MdiActiveDocument

      Dim ed As Editor = doc.Editor

      Dim db As Database = doc.Database

      Try

        ' Ask for a string representing the handle

        Dim pr As PromptResult = _

          ed.GetString( _

            vbLf + _

            "Enter handle of object to erase: ")

        If (pr.Status = PromptStatus.OK) Then

          ' Convert hexadecimal string to 64-bit integer

          Dim ln As Long = _

            Convert.ToInt64(pr.StringResult, 16)

          ' Not create a Handle from the long integer

          Dim hn As Handle = New Handle(ln)

          ' And attempt to get an ObjectId for the Handle

          Dim id As ObjectId = _

            db.GetObjectId(False, hn, 0)

          ' Finally let's open the object and erase it

          Dim tr As Transaction = _

            doc.TransactionManager.StartTransaction

          Dim obj As DBObject = _

            tr.GetObject(id, OpenMode.ForWrite)

          obj.Erase()

          tr.Commit()

          tr.Dispose()

        End If

      Catch ex As System.Exception

        ed.WriteMessage("Exception: " + ex.ToString)

      End Try

    End Sub

  End Class

End Namespace

18 responses to “Getting access to an entity from its handle in AutoCAD .NET”

  1. Of course this means that you can't tell if a handle exists in a database without catching an exception. You could always PInvoke to acdbHandEnt (which I do), but that only works on the current drawing, not any other drawing/database. This is really bad if your doing this in a loop (say your auditing a SQL-database full of handles against a drawing). Especially if you have a debugger attached.

    It was fine in ObjectARX/C++ when you just checked the return value for the error, there wasn't a significant overhead to doing that. There is with catching an exception. This is a perfect example of bad API design.

  2. I'll pass on your concern to our Engineering team.

    Yes, P/Invoking acdbHandEnt() needs you to set the current document if working on different files (you don't have to make the document active, as I'm sure you're aware).

    Are you seeing a performance impact when compared with calling AcDbDatabase::getAcDbObjectId() on a specific database? (Aside from the exception handling question, that is.)

  3. No real performance problems either way assuming the handle is there. I haven't measured the other case.

    Most CAD-related performance problems went away for me when I switch from the old COM API to the .NET API.

  4. Hi Wyatt,

    Yes, this is indeed suboptimal. We should have an TryGetObjectId method that returns a boolean.

    Thanks for pointing this out,
    Albert

  5. Thank you very much for your
    amazing work
    That will be help to many programmers
    Just for info, I used your example
    to help to one guy on this russian forum:

    autocad.ru/cgi-b...

    Sorry for my pooe English,

    Regards

    Oleg

  6. Hi Kean,

    I did't understand if ObjectId or Handle are unique identifier for an object in the database in a persistent way or only session limited.

    If I create objects on a DWG file and then save and close this file, when I try to re-open that... ObjectId and Handle remains the same?

    Cheers,

    Flea

  7. Hi Flea,

    ObjectIds are per session, but get remapped on DWG load from the values stored in the file.

    Handles are persistent across sessions.

    Regards,

    Kean

  8. Thanks Kean, I needed this badly, been looking in to this for some time now, but couldn't figure this out. I'll try this out as soon as I get too work. Hopefully it will help me save data linked to objects over sessions... 🙂

  9. Luigi Ballotta Avatar

    Hi to all.
    I think that Wyatt's point of view is performance-prone, that is good, but we also keep in mind that custom application related objects in a drawing should be managed through our application. If so when we delete an object from model space, our application is responsible to do update our SQL db accordingly. The Try-Catch-End try statement is just to preserve the crash of our application.

    In other words is our application that should manage the SQL db, in order to mantain the db up to date with the drawing.

  10. Hi Luigi,

    Thanks for adding your input on this!

    Regards,

    Kean

  11. Is there a way to create a new entity base from the handle? assuming i was able to save the handle value in an xml file and eventually i want to retrieve it and create an object from it.

    1. What do you mean by entity base? I think this post shows pretty much exactly what you want, but I may be misunderstanding your query.

      Kean

  12. Mehdi Houshmand Avatar

    Hi every body and thank you dear Kean for your very nice post.
    my question is: how is it possible to get and object as implied selection by its handle, for example we generated a list of handles which we have in drawing (as database) and now we want to select or filter one cad element by its handle not by get selection from model space. (VB.net or C# please)

    1. Hi Mehdi,

      This isn't a support forum: please post your question on the AutoCAD .NET Discussion Group.

      Regards,

      Kean

      1. Mehdi Houshmand Avatar

        Thank you for your response i post the question in this address.
        forums.autodesk.com/

  13. This blog post requires the user to know the handle, in order to access the object. But what if you don't know the handle? This was the very problem that I had. I thought someone reading this might have that very question so i thought to share my solution. Presumably you would get the object by selecting, and would then obtain it's object ID. And once you have the object ID, you can use this to get the handle:

    (Untested -- disclaimer -- it's all on you if it cocks up.)

    public static Handle GetHandle(ObjectId blockID)

    {

    Document doc = Application.DocumentManager.MdiActiveDocument;

    Editor ed = doc.Editor;

    Database db = doc.Database;

    Handle h = new Handle();

    using (Transaction tr = doc.TransactionManager.StartTransaction())

    {

    DBObject block = tr.GetObject(blockID, OpenMode.ForRead) as DBObject;

    if (block != null)

    {

    h = block.Handle;

    }

    }

    return h;

    }

  14. Hi to all
    i have a problem with lisp and c#.
    I have a lisp code below

    (setq criterio '((-4 . "<and") (0="" .="" "text")="" (-4="" .="" "and="">"))
    )

    (if (ssget "X" criterio )

    ;;;; do something
    )
    )

    when i run this code "ssget "X" criterio", result is false, then i have translate the lisp code above in c# like this : (i think is correct!!!)

    TypedValue[] criterio= new TypedValue[3]
    {
    new TypedValue(-4,"<and"), new="" typedvalue(0,"text"),="" new="" typedvalue(-4,"and="">")
    };

    Editor ed = Application.DocumentManager.MdiActiveDocument.Editor;
    SelectionFilter sf = new SelectionFilter(criterio);
    PromptSelectionResult ret = ed.SelectAll(sf);

    if (ret.Status == PromptStatus.OK)
    {
    // do something
    }

    but the ret.status is "OK" instead "NO" or false like lisp result.
    I don't know why.
    Is there someone who can help me?

    Thanks.

    1. Hi Marco,

      Please post your support questions to the relevant online forum: forums.autodesk.com

      Thank you,

      Kean

Leave a Reply to Albert Cancel reply

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