Author Topic: C++ or .NET  (Read 7859 times)

0 Members and 1 Guest are viewing this topic.

mcarson

  • Guest
C++ or .NET
« on: February 18, 2009, 04:27:37 PM »
I started developing in VBA, then VB.NET, slowly moving over to C#. Most of my posts and/or learning by
example :-) (thanks) have included references to the API 'bug' relating to the eWasErased errors.

The code posted by TonyT (http://www.caddzone.com/DBUtils.cs) has been very helpful. In fact, while
translating the various lisp routines I have used this to check for erased object ids.

Now, being a bit 'annoyed'  :| having to implement a new class to return a collection of objectids that
have NOT been marked for deletion for every symboltable iteration I need to make, should I stop and move
into development in C++, as the correct methods are available?

(?acdbSymbolTableGetAt@@YG?AW4ErrorStatus@Acad@@PBVAcDbSymbolTable@@PB_WAAVAcDbObjectId@@_N@Z):
Code: [Select]
Acad::ErrorStatus getAt (const ACHAR* entryName,
                             AcDbObjectId& recordId,
                             bool getErasedRecord = false) const;
(?getRecord@AcDbSymbolTableIterator@@QBE?AW4ErrorStatus@Acad@@AAPAVAcDbSymbolTableRecord@@W4OpenMode@AcDb@@_N@Z):
Code: [Select]
Acad::ErrorStatus newIterator(AcDbSymbolTableIterator*& pIterator,
                                  bool atBeginning = true,
                                  bool skipDeleted = true) const;
Being slightly oiled at the moment (with a bottle of 1990 Ardbeg  :evil:), I may have got the above wrong.

Getting to the point, should I stick with learning C# and ObjectArx (.NET) or learn C++?


On another thread, would any comments re: CAD Management be welcome; as I manage 289 AutoCAD users.



sinc

  • Guest
Re: C++ or .NET
« Reply #1 on: February 18, 2009, 04:30:02 PM »
If you switch to C++ you can expect to take at least twice as long to do anything, assuming you are equally proficient in C++ and C#.

mcarson

  • Guest
Re: C++ or .NET
« Reply #2 on: February 18, 2009, 04:39:22 PM »
I am proficient in neither C++ or C#. However, with the latter I am making some progress.

Glenn R

  • Guest
Re: C++ or .NET
« Reply #3 on: February 18, 2009, 04:42:57 PM »
Are you using the GetTableRecordId at the beginning of Tony's file or, the far superior AcDbSymbolTable.GetSymbolTableRecordId in the latter half of the file?

In answer to your question, if you want to spend several years becoming proficient with C++ and then another 1 - 2 years with the ObjectARX API, go for it :D

Spike Wilbury

  • Guest
Re: C++ or .NET
« Reply #4 on: February 18, 2009, 04:55:25 PM »
What AutoCAD version you are using?

Here in my A2007 - I have access to the SymbolTable Class and use IncludingErased

Or I am in another tv channel ?

mcarson

  • Guest
Re: C++ or .NET
« Reply #5 on: February 18, 2009, 05:06:07 PM »
Sample, still writing... (love the imports declarations: AcDb = Autodesk.AutoCAD.DatabaseServices)
Code: [Select]
/// <summary>
/// Checks if a block definition exists
/// </summary>
/// <param name="BlockName">Input for the block definition name</param>
/// <param name="UnErase">Unerase the block defintion if it's marked for deletion</param>
/// <returns>True if block definition exists</returns>
/// <remarks></remarks>
public bool DoesBlockExist(string BlockName, bool UnErase)
{
    AcDb.Database db = AcAp.Application.DocumentManager.MdiActiveDocument.Database;
    AcDb.TransactionManager tm = db.TransactionManager;
   
    //Get the linetype object id using the getAt P/Invoke function to prevent the returning of an invalid/deleted ObjectId
    AcDb.ObjectId blockdefid = GetSymbolTableRecordId(db.BlockTableId, BlockName); //Call to [i]the[/i] TonyT GetTableRecordId method
    //If the objectid is null, then the objectid with the blockname does not exist, deleted or not
    if (blockdefid.IsNull) {
        return false;
    }
    else {
        //Check if the block definition is marked for deletion
        //TODO: Modify for input flag (UnErase)
        if (blockdefid.IsErased) {
            //Unerase the block definition
            using (AcDb.Transaction tr = tm.StartTransaction) {
                AcDb.BlockTableRecord btr = (AcDb.BlockTableRecord)tr.GetObject(blockdefid, AcDb.OpenMode.ForWrite, true);
                btr.Erase(false);
                tr.Commit();
            }
        }
    }
    return true;
}


The implementation of the 'fix' by TonyT, perhaps, is confusing me. As follows:

If a block reference is deleted, and the drawing is purged in the current session. Do I need to 'unerase the blocktablerecord?

I would prefer NOT to learn C++. (I have got enough on my plate as it is).



mcarson

  • Guest
Re: C++ or .NET
« Reply #6 on: February 18, 2009, 05:07:34 PM »
What AutoCAD version you are using?

Here in my A2007 - I have access to the SymbolTable Class and use IncludingErased

Or I am in another tv channel ?

AutoCAD 2008 and verticals, attempting to write code for easy transfer to 2009 (and 2010)

SymbolTable use IncludingErased?! Maybe I should be watching the same!

Glenn R

  • Guest
Re: C++ or .NET
« Reply #7 on: February 18, 2009, 05:24:52 PM »
I think Luis is referring to a transaction and one of its arguments to include erased....or am I mistaken Luis?

Spike Wilbury

  • Guest
Re: C++ or .NET
« Reply #8 on: February 18, 2009, 05:32:48 PM »
I think Luis is referring to a transaction and one of its arguments to include erased....or am I mistaken Luis?

something like that... but without testing:

Quote
SymbolTable table = (SymbolTable)tm.GetObject(db.BlockTableId, OpenMode.ForRead, true); // no idea if this can be mark as false
table.IncludingErased;

the above returns an error

maybe:

Quote
SymbolTable table = ((SymbolTable)tm.GetObject(db.BlockTableId, OpenMode.ForRead, true)).IncludingErased;
« Last Edit: February 18, 2009, 05:36:27 PM by LE »

Glenn R

  • Guest
Re: C++ or .NET
« Reply #9 on: February 18, 2009, 05:33:36 PM »
Scratch that - there is an IncludingErased property on the SymbolTable class, however it's read-only.....

Spike Wilbury

  • Guest
Re: C++ or .NET
« Reply #10 on: February 18, 2009, 05:50:40 PM »
Can you test your function with this?... and see if works

Code: [Select]
SymbolTable table = ((SymbolTable)tm.GetObject(db.BlockTableId, OpenMode.ForRead, true)).IncludingErased;
if (!table.Has(BlockName)) return false;
ObjectId blockid = table[BlockName];
if (blockid.IsErased)
{
...

mcarson

  • Guest
Re: C++ or .NET
« Reply #11 on: February 19, 2009, 03:58:37 AM »
Now that I'm sober, :-)

LE: tested your snippet...
Code: [Select]
public bool DoesBlockExist(string BlockName)
{
    AcDb.Database db = AcAp.Application.DocumentManager.MdiActiveDocument.Database;
    AcDb.TransactionManager tm = db.TransactionManager;
   
    using (AcDb.Transaction tr = tm.StartTransaction) {
        AcDb.SymbolTable table = ((AcDb.SymbolTable)tm.GetObject(db.BlockTableId, AcDb.OpenMode.ForRead, true)).IncludingErased;
        if (!table.Has(BlockName)) {
            return false;
        }
        AcDb.ObjectId blockdefid = table(BlockName);
        if (blockdefid.IsErased) {
            //Unerase the block definition
            AcDb.BlockTableRecord btr = (AcDb.BlockTableRecord)tr.GetObject(blockdefid, AcDb.OpenMode.ForWrite, true);
            btr.Erase(false);
            tr.Commit();
        }
    }
    return true;
}

The results are identical.


mcarson

  • Guest
Re: C++ or .NET
« Reply #12 on: February 19, 2009, 04:25:20 AM »
The results are identical.

It appears so.

On this occasion, I am recreating a lisp routine in .NET to insert a tadpole (block that symbolises a slope)
The program should prompt for the bottom and top of slope. The block is inserted rotated and scaled between the two points.

Code: [Select]
public AcDb.ObjectId SmartInsert(string BlockFileName, string BlockName, string FirstPointMessage, string SecondPointMessage)
{
    AcAp.Document doc = AcAp.Application.DocumentManager.MdiActiveDocument;
    AcDb.Database db = doc.Database;
    AcEd.Editor ed = doc.Editor;
    AcDb.TransactionManager tm = db.TransactionManager;
   
    //Get first point
    AcGe.Point3d FirstPoint = GetPoint(FirstPointMessage);
    if ((FirstPoint == null)) {
        ed.WriteMessage(Constants.vbCrLf + "<Cancelled>");
        return null;
    }
    //Get second point
    AcGe.Point3d SecondPoint = GetPoint(SecondPointMessage);
    if ((SecondPoint == null)) {
        ed.WriteMessage(Constants.vbCrLf + "<Cancelled>");
        return null;
    }
    //Get distance and angle between first and second points
    //Create temporary line object
    AcDb.Line line = new AcDb.Line(FirstPoint, SecondPoint);
    double distance = line.Length;
    double angle = line.Angle;
   
    //Check if the block definition exists in the drawing
    if (!Blocks.DoesBlockExist(BlockName)) {
        //Insert the block defintion from a file
        Blocks.InsertBlock(BlockName, BlockFileName);
    }
    Blocks.InsertBlockReference("0", BlockName, FirstPoint, new AcGe.Scale3d(distance), angle);
}

public AcGe.Point3d GetPoint(string Message)
{
    AcGe.Point3d result = null;
   
    AcEd.Editor ed = AcAp.Application.DocumentManager.MdiActiveDocument.Editor;
    AcEd.PromptPointOptions pntopts = new AcEd.PromptPointOptions(Message);
   
    AcEd.PromptPointResult pnt = ed.GetPoint(pntopts);
    if (pnt.Status == Autodesk.AutoCAD.EditorInput.PromptStatus.OK) {
        result = pnt.Value;
    }
    else if (pnt.Status == Autodesk.AutoCAD.EditorInput.PromptStatus.Cancel) {
        Autodesk.AutoCAD.Internal.Utils.PostCommandPrompt(); //reference to acmgdinternal.dll required
        return null;
    }
    else {
        result = null;
    }
    return result;
}

public AcDb.ObjectId InsertBlockReference(string Layer, string BlockName, AcGe.Point3d Position, AcGe.Scale3d Scale, double RotateAngle, Hashtable AttNameValues)
{
   
    using (AcDb.Transaction tr = ThisTransactionManager.StartTransaction) {
        AcDb.BlockTable bt = (AcDb.BlockTable)tr.GetObject(ThisDatabase.BlockTableId, AcDb.OpenMode.ForWrite, true);
        //Check for eWasErased?
        if (!bt.Has(BlockName)) {
            return AcDb.ObjectId.Null;
        }
        AcDb.BlockTableRecord btr = (AcDb.BlockTableRecord)tr.GetObject(bt.Item(BlockName), AcDb.OpenMode.ForRead, true);
        AcDb.BlockReference ent = new AcDb.BlockReference(Position, bt.Item(BlockName));
        ent.ScaleFactors = Scale;
        ent.Rotation = RotateAngle;
        ent.Layer = Layer;
       
        ((AcDb.BlockTableRecord)tr.GetObject(ThisDatabase.CurrentSpaceId, AcDb.OpenMode.ForWrite)).AppendEntity(ent);
        tr.AddNewlyCreatedDBObject(ent, true);
       
        foreach (AcDb.ObjectId attoid in btr) {
            AcDb.AttributeDefinition attdef = tr.GetObject(attoid, AcDb.OpenMode.ForRead) as AcDb.AttributeDefinition;
            AcDb.AttributeReference attref = new AcDb.AttributeReference();
            if ((!(attdef == null))) {
                attref.SetAttributeFromBlock(attdef, ent.BlockTransform);
                attref.Position = (attdef.Position + ent.Position.GetAsVector);
                if (AttNameValues.ContainsKey(attdef.Tag.ToUpper)) {
                    attref.TextString = AttNameValues.Item(attdef.Tag.ToUpper).ToString;
                }
                ent.AttributeCollection.AppendAttribute(attref);
            }
        }
        tr.Commit();
        return ent.ObjectId;
    }
}

public bool InsertBlock(string BlockName, string SourceFile)
{
    using (AcDb.Database sourceDatabase = GetDatabaseFromFile(SourceFile)) {
        //Insert the blocks database into a new Block Table Record
        AcDb.HostApplicationServices.WorkingDatabase.Insert(BlockName, sourceDatabase, false);
    }
}

Sorry about the lengthly code post. The program works great. Until the drawing is purged.

Pick bottom of slope:
Pick top of slope: ** Undefined block #1

edit Start the program by calling SmartInsert()

« Last Edit: February 19, 2009, 04:36:29 AM by mcarson »

TonyT

  • Guest
Re: C++ or .NET
« Reply #13 on: February 19, 2009, 01:57:41 PM »

Now, being a bit 'annoyed'  :| having to implement a new class to return a collection of objectids that
have NOT been marked for deletion for every symboltable iteration I need to make, should I stop and move
into development in C++, as the correct methods are available?


The Enumerator returned by the SymbolTableClass (the items
you iterate through with foreach()) does not include erased
entries. If you want erased entries, you would use:

Code: [Select]

       BlockTable bt = //....

       foreach( ObjectId id in bt.IncludingErased )
       {
           // you will get both erased and non-erased ids here
       }





TonyT

  • Guest
Re: C++ or .NET
« Reply #14 on: February 19, 2009, 11:48:05 PM »
Sample, still writing... (love the imports declarations: AcDb = Autodesk.AutoCAD.DatabaseServices)
Code: [Select]
   
    //Get the linetype object id using the getAt P/Invoke function to prevent the returning of an invalid/deleted ObjectId
    AcDb.ObjectId blockdefid = GetSymbolTableRecordId(db.BlockTableId, BlockName); //Call to [i]the[/i] TonyT GetTableRecordId method
    //If the objectid is null, then the objectid with the blockname does not exist, deleted or not


The implementation of the 'fix' by TonyT, perhaps, is confusing me. As follows:
If a block reference is deleted, and the drawing is purged in the current session. Do I need to 'unerase the blocktablerecord?
I would prefer NOT to learn C++. (I have got enough on my plate as it is).


The GetSymbolTableRecordId() method from DBUtils.cs, might more
appropriately be called 'GetNonErasedSymbolTableRecordId()', because
it only returns a non-null SymbolTableRecord id if a non-erased
entry with the given name exists.

The code you show is redundant, because it goes on to test the
returned ObjectId to see if it is erased, which it will never be.

For what you're doing, you don't have to unerase an existing erased
block, you just proceed as you would if there was no block with the
given name, erased or not.

In other words, if GetSymbolTableRecordId() returns a null objectId,
then insert the block from a file (or create one in code).

mcarson

  • Guest
Re: C++ or .NET
« Reply #15 on: February 20, 2009, 06:30:02 AM »
TonyT,

Thanks very much for your reply. I think I'll rename the GetSymbolTableRecordId to your 'more appropriate' name.