Author Topic: DeepClone = FATAL ERROR  (Read 3721 times)

0 Members and 1 Guest are viewing this topic.

dugk

  • Guest
DeepClone = FATAL ERROR
« on: September 29, 2009, 03:48:33 PM »
I'm trying to write some block cloning code but I can't figure out how to get the acCurDb.DeepCloneObjects method to work.  I've read many posts and examples but I've not able to fix it.

Please help.

Thanks!

The code below should have the user select some entities, then if the entity selected is a BlockReference it should clone the BlockDefinition.


        [CommandMethod("clone", CommandFlags.UsePickSet |
            CommandFlags.Redraw |
            CommandFlags.Modal)]
        static public void CloneTest()
        {
            Document acDoc = Application.DocumentManager.MdiActiveDocument;
            Editor ed = acDoc.Editor;
            Database acCurDb = acDoc.Database;

            try
            {
                PromptSelectionResult selectionRes = ed.SelectImplied();

                // If there's no pickfirst set available...
                if (selectionRes.Status == PromptStatus.Error)
                {
                    // ... ask the user to select entities
                    PromptSelectionOptions selectionOpts = new PromptSelectionOptions();
                    selectionOpts.MessageForAdding = "\nSelect objects to list: ";
                    selectionRes = ed.GetSelection(selectionOpts);
                }
                else
                {
                    // If there was a pickfirst set, clear it
                    ed.SetImpliedSelection(new ObjectId[0]);
                }

                // If the user has not cancelled...
                if (selectionRes.Status == PromptStatus.OK)
                {
                    // ... take the selected objects one by one
                    Transaction tr = acDoc.TransactionManager.StartTransaction();
                    try
                    {
                        ObjectId[] objIds = selectionRes.Value.GetObjectIds();
                        foreach (ObjectId objId in objIds)
                        {
                            DBObject obj = tr.GetObject(objId, OpenMode.ForRead);
                            Entity ent = (Entity)obj;

                            if (ent is BlockReference)
                            {
                                BlockReference blkRef = obj as BlockReference;

                                // Open the Block table for read
                                BlockTable acBlkTbl;
                                acBlkTbl = tr.GetObject(acCurDb.BlockTableId,
                                                             OpenMode.ForRead) as BlockTable;
                                // Step through the Block table record
                                foreach (ObjectId btrId in acBlkTbl)
                                {
                                    BlockTableRecord btr =
                                      (BlockTableRecord)tr.GetObject(btrId,
                                                                    OpenMode.ForRead,
                                                                    false);
                                    if (!btr.IsLayout && blkRef.Name == btr.Name)
                                    {
                                        ObjectIdCollection entityCollection = new ObjectIdCollection();
                                        entityCollection.Add(ent.ObjectId);
                                        IdMapping idMap = new IdMapping();
                                        acCurDb.DeepCloneObjects(entityCollection, acCurDb.CurrentSpaceId, idMap, false);
                                        ed.Regen();
                                    }
                                    acCurDb.Dispose();
                                }
                                blkRef.Dispose();
                            }
                        }
                        tr.Commit();
                    }
                    catch (Autodesk.AutoCAD.Runtime.Exception ex)
                    {
                        ed.WriteMessage(ex.Message);
                        tr.Abort();
                    }
                }
            }
            catch (Autodesk.AutoCAD.Runtime.Exception ex)
            {
                ed.WriteMessage(ex.Message);
            }
        }

dugk

  • Guest
Re: DeepClone = FATAL ERROR
« Reply #1 on: September 30, 2009, 10:52:29 AM »
The code in my first post was derived from several posts here at theSwamp.org and the .NET Autodesk Discussion Group.

I've also tried to implement a version of code that I derived from the book I purchased VB.NET Programming for AutoCAD and it's Chapter 9 on cloning.  It uses the DeepClone method and not the DeepCloneObjects method.

I get the compile error using the DeepClone method on the following code:

Error   1   Cannot implicitly convert type 'Autodesk.AutoCAD.DatabaseServices.DBObject' to 'Autodesk.AutoCAD.DatabaseServices.BlockTableRecord'. An explicit conversion exists (are you missing a cast?)

        [CommandMethod("clone", CommandFlags.UsePickSet |
            CommandFlags.Redraw |
            CommandFlags.Modal)]
        static public void CloneTest()
        {
            Document acDoc = Application.DocumentManager.MdiActiveDocument;
            Editor ed = acDoc.Editor;
            Database acCurDb = acDoc.Database;

            try
            {
                PromptSelectionResult selectionRes = ed.SelectImplied();

                // If there's no pickfirst set available...
                if (selectionRes.Status == PromptStatus.Error)
                {
                    // ... ask the user to select entities
                    PromptSelectionOptions selectionOpts = new PromptSelectionOptions();
                    selectionOpts.MessageForAdding = "\nSelect objects to list: ";
                    selectionRes = ed.GetSelection(selectionOpts);
                }
                else
                {
                    // If there was a pickfirst set, clear it
                    ed.SetImpliedSelection(new ObjectId[0]);
                }

                // If the user has not cancelled...
                if (selectionRes.Status == PromptStatus.OK)
                {
                    // ... take the selected objects one by one
                    Transaction tr = acDoc.TransactionManager.StartTransaction();
                    try
                    {
                        ObjectId[] objIds = selectionRes.Value.GetObjectIds();
                        foreach (ObjectId objId in objIds)
                        {
                            DBObject obj = tr.GetObject(objId, OpenMode.ForRead);
                            Entity ent = (Entity)obj;

                            if (ent is BlockReference)
                            {
                                BlockReference blkRef = obj as BlockReference;

                                // Open the Block table for read
                                BlockTable acBlkTbl;
                                acBlkTbl = tr.GetObject(acCurDb.BlockTableId,
                                                             OpenMode.ForWrite) as BlockTable;
                                // Step through the Block table record
                                foreach (ObjectId btrId in acBlkTbl)
                                {
                                    BlockTableRecord btr =
                                      (BlockTableRecord)tr.GetObject(btrId,
                                                                    OpenMode.ForWrite,
                                                                    false);
                                    if (!btr.IsLayout && blkRef.Name == btr.Name)
                                    {
                                        BlockTableRecord btrClone = new BlockTableRecord();
                                        IdMapping idMap = new IdMapping();
                                        btrClone = btr.DeepClone(acBlkTbl, idMap, true);
                                        btrClone.Name = btr.Name + "-Clone";
                                        acBlkTbl.Add(btrClone);
                                        tr.AddNewlyCreatedDBObject(btrClone, true);

                                    }
                                    acCurDb.Dispose();
                                }
                                blkRef.Dispose();
                            }
                        }
                        tr.Commit();
                    }
                    catch (Autodesk.AutoCAD.Runtime.Exception ex)
                    {
                        ed.WriteMessage(ex.Message);
                        tr.Abort();
                    }
                }
            }
            catch (Autodesk.AutoCAD.Runtime.Exception ex)
            {
                ed.WriteMessage(ex.Message);
            }
        }

I am guessing my first post is closer to being correct.  But I have a couple of questions:

1.  My first post seems to be cloning the block reference and not the block definition like the code in highlighted in this post.  From my AutoLISP and AutoCAD experience it would seem that I want to clone the block definition like the code in this post.  Which is correct?

2.  If my code from my first post is more correct, how to I apply the Name of the new block Definition?

This post from the .NET Autodesk Discussion Group has been a big influence on the code in my first post:  http://discussion.autodesk.com/forums/thread.jspa?messageID=6074184&#6074184

Any and all help is appreciated!

BillZndl

  • Guest
Re: DeepClone = FATAL ERROR
« Reply #2 on: September 30, 2009, 03:12:07 PM »
dugk,

I had so many problems with your code when I tried to test it that I decided to simplify it down to where you simply pick a block reference and then create a new blocktablerecord from there.
I couldn't tell but assumed it was a blocktablerecord that you wanted to get as it didn't make sense to me to try to clone a block reference.

So here goes....

Remember, I have VS2005 and net 2.0 so the cloning is a little different on this release but you'll get the idea.
Oh, and I remmed out the try/catch as it was driving me nuts not knowing what the error was...

Code: [Select]
using System;
using System.Collections.Generic;
using System.Text;
using System.Windows.Forms;
using Autodesk.AutoCAD.Geometry;
using Autodesk.AutoCAD.EditorInput;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.Runtime;
using AcadApp = Autodesk.AutoCAD.ApplicationServices.Application;

namespace BlockCloneTest
{
    public class CloneBlockRef
    {
        [CommandMethod("clone")]
        static public void CloneTest()
        {
            Document acDoc = AcadApp.DocumentManager.MdiActiveDocument;
            Editor ed = acDoc.Editor;
            Database acCurDb = acDoc.Database;

            //try
            //{
                PromptEntityOptions options = new PromptEntityOptions("\nSelect block: ");
                options.SetRejectMessage("\nMust be an insert: ");
                options.AddAllowedClass(typeof(BlockReference), true);                       

                PromptEntityResult entity = ed.GetEntity(options);
                if (entity.Status == PromptStatus.OK)
                {               
                   
                   using(Transaction tr = acDoc.TransactionManager.StartTransaction())
                   {                   
                        DBObject obj = tr.GetObject(entity.ObjectId, OpenMode.ForRead);
                        BlockReference blr = obj as BlockReference;

                        if (blr != null)
                        {
                            BlockTable acBlkTbl = (BlockTable)tr.GetObject(acCurDb.BlockTableId, OpenMode.ForWrite);

                            //get name of block record.
                            BlockTableRecord ExistingBTR = (BlockTableRecord)tr.GetObject(blr.BlockTableRecord, OpenMode.ForRead);

                            //open new block table record.
                            BlockTableRecord NewBtr = new BlockTableRecord();

                            NewBtr.Name = string.Concat(ExistingBTR.Name, "-Clone");
                            NewBtr.Origin = new Point3d(0, 0, 0);

                            acBlkTbl.Add(NewBtr);
                            tr.AddNewlyCreatedDBObject(NewBtr, true);                                           

                            ObjectIdCollection entityIDCollection = new ObjectIdCollection();

                            foreach (ObjectId id in ExistingBTR)
                            {
                                entityIDCollection.Add(id);
                            }

                            IdMapping IdMap = new IdMapping();
                            IdMap = acCurDb.DeepCloneObjects(entityIDCollection, acCurDb.CurrentSpaceId, false);

                            ObjectIdCollection entIDCol = new ObjectIdCollection();

                            foreach (IdPair pair in IdMap)
                                {
                                    if (pair.IsPrimary)
                                    {
                                        Entity en = (Entity)tr.GetObject(pair.Value, OpenMode.ForWrite);
                                        if (en != null)
                                        {
                                            entIDCol.Add(en.ObjectId);
                                        }
                                    }
                                }
                                NewBtr.AssumeOwnershipOf(entIDCol);
                                tr.Commit();
                        }
                                                           
                    }                               
                }                   
            //}
            /*catch (System.Exception ex)
            {
                ed.WriteMessage(ex.Message);
            }*/
        }
    }
}

Hope that helps!  :)

Bill

BillZndl

  • Guest
Re: DeepClone = FATAL ERROR
« Reply #3 on: September 30, 2009, 03:18:08 PM »
Oh, and don't mine all the "usings"(there might be a few extras),
I copied this from some other code I had and it was a larger program.
Probably should be more like this.

using System;
using Autodesk.AutoCAD.Geometry;
using Autodesk.AutoCAD.EditorInput;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.Runtime;
using AcadApp = Autodesk.AutoCAD.ApplicationServices.Application;



BillZndl

  • Guest
Re: DeepClone = FATAL ERROR
« Reply #4 on: September 30, 2009, 03:22:21 PM »
This could be for read:

Entity en = (Entity)tr.GetObject(pair.Value, OpenMode.ForRead);

Bill


dugk

  • Guest
Re: DeepClone = FATAL ERROR
« Reply #5 on: September 30, 2009, 03:52:07 PM »
Thank you VERY MUCH!

It worked flawlessly for me in VS 1008 and .NET 3.5 once I changed the following:
IdMap = acCurDb.DeepCloneObjects(entityIDCollection, acCurDb.CurrentSpaceId, false);

to:
acCurDb.DeepCloneObjects(entityIDCollection, acCurDb.CurrentSpaceId, IdMap, false);

BillZndl

  • Guest
Re: DeepClone = FATAL ERROR
« Reply #6 on: September 30, 2009, 04:09:46 PM »
You're welcome.

Oh, and don't forget to lock you document:


using (acDoc.LockDocument())
                    {
                        using (Transaction tr = acDoc.TransactionManager.StartTransaction())
                        {

Bill