Fatal error when importing blocks from another drawing using WblockCloneObjects.

shricharana_bharadwaj
Enthusiast
Enthusiast

Fatal error when importing blocks from another drawing using WblockCloneObjects.

shricharana_bharadwaj
Enthusiast
Enthusiast

Hi, 

 

I'm using WblockCloneObjects to import blocks from another drawing without opening it. 

In debug mode, the code works fine no matter how many times I run the import method on a drawing. 

But when I use the release dll, it seems to crash AutoCAD very often (around once after 3-4 times on average).

Since these are random crashes, I'm not sure what is going wrong. 

The below is the error.

shricharana_bharadwaj_0-1722597988938.png

 

 

-----Last-0 'first chance' exception:
System.AccessViolationException: Attempted to read or write protected memory. This is often an indication that other memory is corrupt.
   at AcDbDatabase.wblockCloneObjects(AcDbDatabase* , AcArray<AcDbObjectId\,AcArrayMemCopyReallocator<AcDbObjectId> >* , AcDbObjectId* , AcDbIdMapping* , DuplicateRecordCloning , Boolean )
   at Autodesk.AutoCAD.DatabaseServices.Database.WblockCloneObjects(ObjectIdCollection identifiers, ObjectId id, IdMapping mapping, DuplicateRecordCloning cloning, Boolean deferTranslation)

 

 

Here is the code I'm using,

 

public static bool ImportAllBlocks(string sourceFilePath, string sourceFileName)
{

    string tempCopyPath = "C:\\Autodesk\\" + sourceFileName;
    if (Directory.Exists(sourceFilePath))
    {
        //Copy all the files
        File.Copy(sourceFilePath + sourceFileName, tempCopyPath);
    }
    //DocumentCollection dm = Application.DocumentManager;
    Editor ed = Active.Editor;
    Database destDb = Active.Database;
    Database sourceDb = new Database(false, true);
    bool ret = false;
    try
    {
        // Read the DWG into a side database
        sourceDb.ReadDwgFile(tempCopyPath, System.IO.FileShare.Read, true, ""); //System.IO.FileShare.Read
        sourceDb.CloseInput(true);
        File.Delete(tempCopyPath);
        // Create a variable to store the list of block identifiers
        ObjectIdCollection blockIds = new ObjectIdCollection();
        //sourceDb.Audit(true, false);

        //Autodesk.AutoCAD.DatabaseServices.TransactionManager tm = sourceDb.TransactionManager;

        using (Transaction myT = sourceDb.TransactionManager.StartTransaction())
        {
            // Open the block table
            BlockTable bt = (BlockTable)myT.GetObject(sourceDb.BlockTableId, OpenMode.ForRead, false);

            // Check each block in the block table
            foreach (ObjectId btrId in bt)
            {
                BlockTableRecord btr = (BlockTableRecord)myT.GetObject(btrId, OpenMode.ForRead, false);

                // Only add named & non-layout blocks to the copy list
                if (!btr.IsAnonymous && !btr.IsLayout) blockIds.Add(btrId);

                btr.Dispose();
            }
            myT.Commit();
        }

        // Copy blocks from source to destination database
        IdMapping mapping = new IdMapping();
        if (blockIds.Count == 0 || destDb == null) { throw new System.Exception("\neNull or Zero Block count"); }
        sourceDb.WblockCloneObjects(blockIds, destDb.BlockTableId, mapping, DuplicateRecordCloning.Ignore, false);
        ret = true;
    }
    catch (Autodesk.AutoCAD.Runtime.Exception ex)
    {
        //ed.WriteMessage("\nError during copy: " + ex.Message);
        throw new System.Exception(ex.Message);
    }
    sourceDb.Dispose();
    return ret;
}

 

 

Could any one please help me fix this issue? I'm scratching my brains out trying to solve this.

 

Any alternate ways for doing this would be very helpful too!

 

Thanks in advance!

 

Edit: I'm using AutoCAD 2024

0 Likes
Replies (25)

norman.yuan
Mentor
Mentor

I am not sure what causes the said fatal error. When the error happens, is the same source file used (as the one used for debugging, which never cause the error, as you said)? 

 

There are a few things in the code are worth looking into:

 

 1. Line 20. Why delete the source file right after the DWG file is read into source database. Not sure it would cause of the error, but it would be more natural after the source database does its work (WBlockCloneObjects() is called);

 

2. Is it possible the source drawing contains XReference/Overlay? if yes, at the line 38 you may want to also test IsFromExternalReference/IsFromOverlayReference;

 

3. You should place sourceDb.Dispose() in the "finally{...}" clause of the existing "try..." block, or use "using "var sourceDb=new Database(...)){...}" to guarantee its dispose. In your code, if no block definition is found, or the "catch..." clause catches exception, the sourceDb would not be disposed. 

 

Since it crashes AutoCAD every 3 or 4 time, have you tried to run it is debugging mode against the same source drawings/destination drawings as the DLL was used in normally that causes the error?

 

 

Norman Yuan

Drive CAD With Code

EESignature

0 Likes

shricharana_bharadwaj
Enthusiast
Enthusiast

Hi, Thank you for your response.

 

To answer your questions, 

1. Since the cloning might crash autocad, if the file is not deleted earlier, it would still be there after the crash.

2. Good point. I just added the check. But none of the source drawings I've used so far have xref in them.

3. Good point again. I hadn't noticed that. I updated it.

4. The same drawing is being used for in both cases. I've tried importing multiple drawings, importing only one drawing with all the blocks, it still crashes.

One thing I've noticed though, in the release version, If i run the command once, it works, then if i open new drawing then run it again it crashes.

0 Likes

ed57gmc
Mentor
Mentor

Below is my method. Hope it helps you.

 

        //  ImportBlocksFromFile
        /// <summary>
        /// Imports block definitions from a dwg file.
        /// </summary>
        /// <param name="sourceFilePath">A <see cref="System.String"/> representing the path of dwg file to import as a block.</param>
        /// <param name="BlockName"><i>Optional: </i>Name of block to import. 
        /// If the parameter is missing or equals string.Empty or null, then all blocks are imported.</param>
        public static void ImportBlocksFromFile(string sourceFilePath, [Optional] string BlockName)
        {
            DocumentCollection dm = acApp.DocumentManager;
            Database destDb = Active.Database;
            Database sourceDb = new Database(false, true);

            try
            {
                // Read the DWG into a side database
                sourceDb.ReadDwgFile(sourceFilePath,
                                    System.IO.FileShare.Read,
                                    true,
                                    "");
                ObjectIdCollection colObjID = new ObjectIdCollection();
                sourceDb.ForEach<BlockTableRecord>(sourceDb.BlockTableId,  btr =>
                   {
                       // if BlockName == "" copy all
                      if (!btr.IsAnonymous && !btr.IsLayout && BlockName == string.Empty && BlockName == null)
                      {
                         colObjID.Add(btr.Id);
                      }
                      else if (BlockName != "" && BlockName == btr.Name)
                      {
                         colObjID.Add(btr.Id);                          
                      }
                   });
                  if (colObjID.Count > 0)
                  {
                     destDb.UsingTransaction(trans =>
                     {
                        IdMapping idMap = new IdMapping();
                        sourceDb.WblockCloneObjects(colObjID,destDb.BlockTableId,idMap,DuplicateRecordCloning.Ignore,true);
                     });
                  }               
            }
            catch (Autodesk.AutoCAD.Runtime.Exception ex)
            {
               if (ex.Message == "eSelfReference")
               {
                  Active.WriteMessage("\nError during copy: " + BlockName + " references itself.");
               }
               else
               {
                  Active.WriteMessage("\nError during copy: " + ex.Message);
               }
            }
            sourceDb.Dispose();
        }

 

Ed


Did you find this post helpful? Feel free to Like this post.
Did your question get successfully answered? Then click on the ACCEPT SOLUTION button.
How to post your code.

EESignature

0 Likes

_gile
Mentor
Mentor

Hi,

Try to make things as simple (and robust) as possible, and, if it works, try adding complexity.

 

Corrected code (see above remarks)

 

public static void ImportAllBlock(string sourceFileName)
{
    var doc = Application.DocumentManager.MdiActiveDocument;
    var targetDb = doc.Database;
    var ed = doc.Editor;

    using (var sourceDb = new Database(false, true))
    {
        try
        {
            sourceDb.ReadDwgFile(sourceFileName, FileOpenMode.OpenForReadAndAllShare, true, null);
            var ids = new ObjectIdCollection();
            using (var tr = sourceDb.TransactionManager.StartTransaction())
            {
                var blockTable = (BlockTable)tr.GetObject(sourceDb.BlockTableId, OpenMode.ForRead);
                foreach (ObjectId id in blockTable)
                {
                    var btr = (BlockTableRecord)tr.GetObject(id, OpenMode.ForRead);
                    if (!btr.IsLayout && !btr.IsAnonymous && !btr.IsDependent && !btr.IsFromExternalReference && !btr.IsFromOverlayReference)
                        ids.Add(id);
                }
                tr.Commit();
            }
            var mapping = new IdMapping();
            sourceDb.WblockCloneObjects(ids, targetDb.BlockTableId, mapping, DuplicateRecordCloning.Ignore, false);
        }
        catch(System.Exception ex)
        {
            ed.WriteMessage(ex.Message);
        }
    }
}

 



Gilles Chanteau
Programmation AutoCAD LISP/.NET
GileCAD
GitHub

0 Likes

_gile
Mentor
Mentor

@ed57gmc

The code you posted uses a static class and extension methods that you have not shown.

You should either add those codes or provide a link to the used library.



Gilles Chanteau
Programmation AutoCAD LISP/.NET
GileCAD
GitHub

0 Likes

shricharana_bharadwaj
Enthusiast
Enthusiast

Hi all, thank you all for trying to help me. 

 

I tried both @ed57gmc and @_gile codes with minor modifications. 

In Ed's to code for the unknown extensions and in gile's to change the db while getting blocktable as it was giving "not from this database" error. 

In both the cases I'm experiencing the crash issue. I don't think the issue is from the code.

But I don't know what the issue is.

Here is screenshot of error while debugging the dmp file when using  @_gile 's code. 

shricharana_bharadwaj_0-1722839806373.png

System.AccessViolationException: Attempted to read or write protected memory. This is often an indication that other memory is corrupt.
   at AcDbDatabase.wblockCloneObjects(AcDbDatabase* , AcArray<AcDbObjectId\,AcArrayMemCopyReallocator<AcDbObjectId> >* , AcDbObjectId* , AcDbIdMapping* , DuplicateRecordCloning , Boolean )
   at Autodesk.AutoCAD.DatabaseServices.Database.WblockCloneObjects(ObjectIdCollection identifiers, ObjectId id, IdMapping mapping, DuplicateRecordCloning cloning, Boolean deferTranslation)

 

The source drawing is not corrupt, because I can open it in AutoCAD without an issue.

I'm calling import before doing anything else in the code (or making any modifications in the destination drawing), I'm calling the command on new drawing. Not existing one.  

 

 

0 Likes

ActivistInvestor
Advisor
Advisor

 

using(var tr = sourceDb.TransactionManager.StartTransaction())
{
   var blockTable = (BlockTable)tr.GetObject(targetDb.BlockTableId, OpenMode.ForRead);

 

It looks like the code is failing because it is opening the wrong BlockTable. 

 

Since the transaction is from the source database, the ObjectIds should be from the source database, not the target database. Try changing targetDb.BlockTableId to sourceDb.BlockTableId.

0 Likes

shricharana_bharadwaj
Enthusiast
Enthusiast

Hi @ActivistInvestor

Thanks for the help.

 

I've already fixed the error you mentioned. The error is occurring when WblockCloneObjects() is called. And it's not being caught by the try-catch block causing the AutoCAD to crash.

A screen shot of the error (loaded from the dump file created when AutoCAD crashes) is shown in my previous reply.

0 Likes

_gile
Mentor
Mentor

Sorry for my mistake (wrongly renaming in a code snippet).

Did you try to Audit the source file? It looks like something is corrupted in some block definition (BlockTabkleRecord).



Gilles Chanteau
Programmation AutoCAD LISP/.NET
GileCAD
GitHub

0 Likes

shricharana_bharadwaj
Enthusiast
Enthusiast

There doesn't seem to be any corruptions when I audit the drawing.

shricharana_bharadwaj_0-1722858430515.png

 

0 Likes

ActivistInvestor
Advisor
Advisor

The image you show suggests that the call to the native AcDbDatabase.wblockCloneObjects() is never even reached. The exception is happening on the line that calls GetImpObj(). 

 

Just before the call to WblockCloneObjects(), try getting the value of the Database's UnmanagedObject property and displaying it on the console.

0 Likes

ed57gmc
Mentor
Mentor

@_gile wrote:

@ed57gmc

The code you posted uses a static class and extension methods that you have not shown.

You should either add those codes or provide a link to the used library.


Oops. @shricharana_bharadwaj  The static class Active.cs has been around for a long time. Scott McFarlane made it available at AU a long time ago. Here it is again.

using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.EditorInput;
using Autodesk.AutoCAD.DatabaseServices;

namespace TID_.Utilities
{
		/// <summary>
		/// Provides easy access to several "active" objects in the AutoCAD
		/// runtime environment.
		/// </summary>
		public static class Active
		{
			/// <summary>
			/// Returns the active Editor object.
			/// </summary>
			public static Autodesk.AutoCAD.EditorInput.Editor Editor
			{
				get { return Document.Editor; }
			}

			/// <summary>
			/// Returns the active Document object.
			/// </summary>
            public static Autodesk.AutoCAD.ApplicationServices.Document Document
			{
				get { return Autodesk.AutoCAD.ApplicationServices.Application.DocumentManager.MdiActiveDocument; }
			}

			/// <summary>
			/// Returns the active Database object.
			/// </summary>
			public static Database Database
			{
				get { return Document.Database; }
			}

			/// <summary>
			/// Sends a string to the command line in the active Editor
			/// </summary>
			/// <param name="message">The message to send.</param>
			public static void WriteMessage(string message)
			{
				Editor.WriteMessage(message);
			}

			/// <summary>
			/// Sends a string to the command line in the active Editor using String.Format.
			/// </summary>
			/// <param name="message">The message containing format specifications.</param>
			/// <param name="parameter">The variables to substitute into the format string.</param>
			public static void WriteMessage(string message, params object[] parameter)
			{
				Editor.WriteMessage(message, parameter);
			}
		}
}

Ed


Did you find this post helpful? Feel free to Like this post.
Did your question get successfully answered? Then click on the ACCEPT SOLUTION button.
How to post your code.

EESignature

0 Likes

ActivistInvestor
Advisor
Advisor

@ed57gmc wrote:


Oops. @shricharana_bharadwaj  The static class Active.cs has been around for a long time. Scott McFarlane made it available at AU a long time ago. Here it is again.


There's a little more to it than that:

 

sourceDb.ForEach<BlockTableRecord>()
destDb.UsingTransaction()

 

0 Likes

ed57gmc
Mentor
Mentor

Thanks, I missed that one. @shricharana_bharadwaj Below is another class for DbExtentions. I think I got this from @_gile  It might already be included in his library here. I know he also has Active.cs in it.

 


    /// <summary>
    /// Contains extension methods that facilitate working with objects in the context of a transaction.
    /// </summary>
    public static class DbExtensions
    {

        /// <summary>
        /// Extension method that allows you to iterate through model space and perform an action
        /// on a specific type of object.
        /// </summary>
        /// <typeparam name="T">The type of object to search for.</typeparam>
        /// <param name="db">The database to use.</param>
        /// <param name="action">A delegate that is called for each object found of the specified type.</param>
        public static void ForEach<T>(this Database db, Action<T> action)
            where T : Entity
        {
            db.UsingModelSpace((tr, modelSpace) => modelSpace.ForEach(tr, action));
        }

        /// <summary>
        /// Extension method that allows you to iterate through model space and perform an action
        /// on a specific type of object.
        /// </summary>
        /// <typeparam name="T">The type of object to search for.</typeparam>
        /// <param name="db">The database to use.</param>
        /// <param name="predicate"></param>
        /// <param name="action">A delegate that is called for each object found of the specified type.</param>
        public static void ForEach<T>(this Database db, Func<T, bool> predicate, Action<T> action)
            where T : Entity
        {
            db.ForEach<T>(
                obj =>
                    {
                        if (predicate(obj))
                            action(obj);
                    });
        }

        /// <summary>
        /// Iterates through the specified symbol table, and performs an action on each symbol table record.
        /// </summary>
        /// <typeparam name="T">The type of symbol table record.</typeparam>
        /// <param name="db">The database.</param>
        /// <param name="tableId">The table id.</param>
        /// <param name="action">A delegate that is called for each record.</param>
        public static void ForEach<T>(this Database db, ObjectId tableId, Action<T> action) where T : SymbolTableRecord
        {
            db.UsingTransaction(tr => tableId.OpenAs<SymbolTable>(tr).Cast<ObjectId>().ForEach(tr, action));
        }

        /// <summary>
        /// Extension method that allows you to iterate through model space and perform an action
        /// on a specific type of object.
        /// </summary>
        /// <typeparam name="T">The type of object to search for.</typeparam>
        /// <param name="document">The document to use.</param>
        /// <param name="action">A delegate that is called for each object found of the specified type.</param>
        /// <remarks>This method locks the specified document.</remarks>
        public static void ForEach<T>(this Document document, Action<T> action)
            where T : Entity
        {
            using (document.LockDocument())
            {
                document.Database.ForEach(action);
            }
        }

        /// <summary>
        /// Extension method that allows you to iterate through the objects in a block table
        /// record and perform an action on a specific type of object.
        /// </summary>
        /// <typeparam name="T">The type of object to search for.</typeparam>
        /// <param name="btr">The block table record to iterate.</param>
        /// <param name="tr">The active transaction.</param>
        /// <param name="action">A delegate that is called for each object found of the specified type.</param>
        public static void ForEach<T>(this IEnumerable<ObjectId> btr, Transaction tr, Action<T> action)
            where T : DBObject
        {
            RXClass theClass = RXObject.GetClass(typeof(T));

            // Loop through the entities in model space
            foreach (ObjectId objectId in btr)
            {
                // Look for entities of the correct type
                if (objectId.ObjectClass.IsDerivedFrom(theClass))
                {
                    action(objectId.OpenAs<T>(tr));
                }
            }
        }

        /// <summary>
        /// Extension method that returns the LayerTableRecord's of the given database.
        /// </summary>
        /// <param name="database">The database.</param>
        /// <param name="tr">A <see>Autodesk.AutoCAD.DatabaseServices.Transaction</see> within which to read the LayerTable.</param>
        /// <returns>An enumerable list of <see>Autodesk.AutoCAD.DatabaseServices.LayerTableRecord</see> objects.</returns>
        public static IEnumerable<LayerTableRecord> Layers(this Database database, Transaction tr)
        {
            return database.LayerTableId
               .OpenAs<LayerTable>(tr)
               .Cast<ObjectId>()
               .OfType<LayerTableRecord>();
        }

        /// <summary>
        /// Opens a database-resident object as the specified type within the context of the specified transaction,
        /// using the specified open mode.
        /// </summary>
        /// <typeparam name="T">The type of object that the objectId represents.</typeparam>
        /// <param name="objectId">The object id.</param>
        /// <param name="tr">The transaction.</param>
        /// <param name="openMode">The open mode.</param>
        /// <returns>The database-resident object.</returns>
        public static T OpenAs<T>(this ObjectId objectId, Transaction tr, OpenMode openMode)
         where T : DBObject
        {
            return (T)tr.GetObject(objectId, openMode);
        }

        /// <summary>
        /// Locks the document, opens the specified object, and passes it to the specified delegate.
        /// </summary>
        /// <typeparam name="T">The type of object the objectId represents.</typeparam>
        /// <param name="document">The document.</param>
        /// <param name="objectId">The object id.</param>
        /// <param name="openMode">The open mode.</param>
        /// <param name="action">A delegate that takes the opened object as an argument.</param>
        public static void OpenAs<T>(this Document document, ObjectId objectId, OpenMode openMode, Action<T> action)
            where T : DBObject
        {
            document.UsingTransaction(tr => action(objectId.OpenAs<T>(tr, openMode)));
        }

        /// <summary>
        /// Opens a database-resident object as the specified type (for read) within the context of the specified transaction.
        /// </summary>
        /// <typeparam name="T">The type of object that the objectId represents.</typeparam>
        /// <param name="objectId">The object id.</param>
        /// <param name="tr">The transaction.</param>
        /// <returns>The database-resident object.</returns>
        public static T OpenAs<T>(this ObjectId objectId, Transaction tr)
           where T : DBObject
        {
            return (T)tr.GetObject(objectId, OpenMode.ForRead);
        }

        /// <summary>
        /// Opens a database-resident object as the specified type, using the specifed OpenMode within the context of the specified delegate.
        /// </summary>
        /// <typeparam name="T">The type of object that the objectId represents.</typeparam>
        /// <param name="objectId">The object id.</param>
        /// <param name="openMode">An <see>Autodesk.Autocad.DatabaseServices.OpenMode</see> constant.</param>
        /// <param name="action">A delegate that takes the transaction and the OpenMode as arguments.</param>
        public static void OpenAs<T>(this ObjectId objectId, OpenMode openMode, Action<T> action)
            where T : DBObject
        {
            objectId.Database.UsingTransaction(tr => action(objectId.OpenAs<T>(tr, openMode)));
        }

        /// <summary>
        /// Opens a database-resident object as the specified type (for write) within the context of the specified delegate.
        /// </summary>
        /// <typeparam name="T">The type of object that the objectId represents.</typeparam>
        /// <param name="objectId">The object id.</param>
        /// <param name="action">A delegate that takes a transaction as an argument.</param>
        public static void OpenForWriteAs<T>(this ObjectId objectId, Action<T> action)
            where T : DBObject
        {
            objectId.Database.UsingTransaction(tr => action(objectId.OpenAs<T>(tr, OpenMode.ForWrite)));
        }

        /// <summary>
        /// Used to get a single value from a database-resident object.
        /// </summary>
        /// <typeparam name="TObject">The type of the object.</typeparam>
        /// <typeparam name="TResult">The type of the result.</typeparam>
        /// <param name="objectId">The object id.</param>
        /// <param name="func">A delegate that takes the object as an argument and returns the value.</param>
        /// <returns>A value of the specified type.</returns>
        public static TResult GetValue<TObject, TResult>(this ObjectId objectId, Func<TObject, TResult> func)
           where TObject : DBObject
        {
            TResult result = default;

            objectId.Database.UsingTransaction(
               tr =>
               {
                   result = func(objectId.OpenAs<TObject>(tr));
               });

            return result;
        }


        /// <summary>
        /// Executes a delegate function in the context of a transaction, and passes it the specified block table record.
        /// </summary>
        /// <param name="database">The database.</param>
        /// <param name="blockName">Name of the block.</param>
        /// <param name="action">A delegate that takes the transaction and the BlockTableRecord as arguments.</param>
        public static void UsingBlockTable(this Database database, string blockName, Action<Transaction, BlockTableRecord> action)
        {
            database.UsingTransaction(
               tr =>
               {
                   // Get the block table
                   var blockTable = database.BlockTableId.OpenAs<BlockTable>(tr);

                   // Get the block table record
                   var tableRecord = blockTable[blockName].OpenAs<BlockTableRecord>(tr);

                   // Invoke the method
                   action(tr, tableRecord);
               });
        }

        /// <summary>
        /// Executes a delegate function in the context of a transaction, and passes it the collection
        /// of Entity objects for the specified block table record.
        /// </summary>
        /// <param name="database">The database.</param>
        /// <param name="blockName">Name of the block.</param>
        /// <param name="action">A delegate that takes the transaction and the Entity collection as arguments.</param>
        public static void UsingBlockTable(this Database database, string blockName, Action<IEnumerable<Entity>> action)
        {
            database.UsingBlockTable
               (blockName,
                (tr, blockTable) => action(from id in blockTable select id.OpenAs<Entity>(tr)));
        }

        /// <summary>
        /// Executes a delegate function in the context of a transaction, and passes it the collection
        /// of ObjectIds for the specified block table record.
        /// </summary>
        /// <param name="database">The database.</param>
        /// <param name="blockName">Name of the block.</param>
        /// <param name="action">A delegate that takes the transaction and the ObjectIds as arguments.</param>
        public static void UsingBlockTable(this Database database, string blockName, Action<Transaction, IEnumerable<ObjectId>> action)
        {
            database.UsingTransaction(
               tr =>
               {
                   // Get the block table
                   var blockTable = database.BlockTableId.OpenAs<BlockTable>(tr);

                   // Get the block table record
                   var tableRecord = blockTable[blockName].OpenAs<BlockTableRecord>(tr);

                   // Invoke the method
                   action(tr, tableRecord.Cast<ObjectId>());
               });
        }

        /// <summary>
        /// Executes a delegate function in the context of a transaction, 
        /// and passes it the block table record of the current space.
        /// </summary>
        /// <param name="database">The database.</param>
        /// <param name="blockName">Name of the block.</param>
        /// <param name="action">A delegate that takes the transaction and the BlockTableRecord as arguments.</param>
        public static void UsingCurrentSpace(this Database database, Action<Transaction, BlockTableRecord> action)
        {
            database.UsingTransaction(
               tr =>
               {
                   // Get the block table
                   var currentSpaceTableRec = database.CurrentSpaceId.OpenAs<BlockTableRecord>(tr, OpenMode.ForWrite);

                   // Invoke the method
                   action(tr, currentSpaceTableRec);
               });
        }

        /// <summary>
        /// Executes a delegate function in the context of a transaction, and passes it the collection
        /// of ObjectIds for the current space block table record.
        /// </summary>
        /// <param name="database">The database.</param>
        /// <param name="blockName">Name of the block.</param>
        /// <param name="action">A delegate that takes the transaction and the ObjectId's as arguments.</param>
        public static void UsingCurrentSpace(this Database database, Action<Transaction, IEnumerable<ObjectId>> action)
        {
            database.UsingTransaction(
               tr =>
               {
                   // Get the block table
                   var currentSpaceTableRec = database.CurrentSpaceId.OpenAs<BlockTableRecord>(tr, OpenMode.ForWrite);

                   // Invoke the method
                   action(tr, currentSpaceTableRec.Cast<ObjectId>());
               });
        }

        /// <summary>
        /// Executes a delegate function in the context of a transaction, and passes it the collection 
        /// of objects from the current space of the specified type.
        /// </summary>
        /// <param name="database">The database.</param>
        /// <param name="action">A delegate that takes the transaction and the Entity collection as arguments.</param>
        /// <typeparamref name="T">The type of object to retrieve.</typeparamref>
        public static void UsingCurrentSpace<T>(this Database database, Action<IEnumerable<T>> action) where T : Entity
        {
            database.UsingCurrentSpace(
               (tr, csOIDs) =>
               {
                   RXClass rxClass = RXObject.GetClass(typeof(T));

                   action(from id in csOIDs
                          where id.ObjectClass.IsDerivedFrom(rxClass)
                          select id.OpenAs<T>(tr));
               });
        }

        /// <summary>
        /// Executes a delegate function with the collection of layers in the specified database.
        /// </summary>
        /// <param name="database">The database.</param>
        /// <param name="action">A delegate that takes the collection of layers as an argument.</param>
        public static void UsingLayerTable(this Database database, Action<IEnumerable<LayerTableRecord>> action)
        {
            database.UsingTransaction(
               tr => action(from ObjectId id in database.LayerTableId.OpenAs<LayerTable>(tr)
                            select id.OpenAs<LayerTableRecord>(tr)));
        }

        /// <summary>
        /// Executes a delegate function in the context of a transaction, and passes it the collection
        /// of objects from model space of the specified type.
        /// </summary>
        /// <param name="database">The database.</param>
        /// <param name="action">A delegate that takes the transaction and the Entity collection as arguments.</param>
        /// <typeparamref name="T">The type of object to retrieve.</typeparamref>
        public static void UsingModelSpace<T>(this Database database, Action<IEnumerable<T>> action) where T : Entity
        {
            database.UsingModelSpace(
               (tr, ms) =>
               {
                   RXClass rxClass = RXObject.GetClass(typeof(T));

                   action(from id in ms
                          where id.ObjectClass.IsDerivedFrom(rxClass)
                          select id.OpenAs<T>(tr));
               });
        }

        /// <summary>
        /// Executes a delegate function in the context of a transaction, and passes it the model space block table record.
        /// </summary>
        /// <param name="database">The database.</param>
        /// <param name="action">A delegate that takes the transaction and the ObjectId's as arguments.</param>
        public static void UsingModelSpace(this Database database, Action<Transaction, BlockTableRecord> action)
        {
            database.UsingBlockTable(BlockTableRecord.ModelSpace, action);
        }

        /// <summary>
        /// Executes a delegate function in the context of a transaction, and passes it the collection
        /// of ObjectIds for the model space block table record.
        /// </summary>
        /// <param name="database">The database.</param>
        /// <param name="action">A delegate that takes the transaction and the ObjectIds as arguments.</param>
        public static void UsingModelSpace(this Database database, Action<Transaction, IEnumerable<ObjectId>> action)
        {
            database.UsingBlockTable(BlockTableRecord.ModelSpace, action);
        }

        /// <summary>
        /// Executes a delegate function in the context of a transaction, and passes it the collection
        /// of objects from paper space of the specified type.
        /// </summary>
        /// <param name="database">The database.</param>
        /// <param name="action">A delegate that takes the transaction and the Entity collection as arguments.</param>
        /// <typeparamref name="T">The type of object to retrieve.</typeparamref>
        public static void UsingPaperSpace<T>(this Database database, Action<IEnumerable<T>> action) where T : Entity
        {
            database.UsingPaperSpace(
               (tr, ps) =>
               {
                   RXClass rxClass = RXObject.GetClass(typeof(T));

                   action(from id in ps
                          where id.ObjectClass.IsDerivedFrom(rxClass)
                          select id.OpenAs<T>(tr));
               });
        }

        /// <summary>
        /// Executes a delegate function in the context of a transaction, and passes it the paper space block table record.
        /// </summary>
        /// <param name="database">The database.</param>
        /// <param name="action">A delegate that takes the transaction and the ObjectIds as arguments.</param>
        public static void UsingPaperSpace(this Database database, Action<Transaction, BlockTableRecord> action)
        {
            database.UsingBlockTable(BlockTableRecord.PaperSpace, action);
        }

        /// <summary>
        /// Executes a delegate function in the context of a transaction, and passes it the collection
        /// of ObjectIds for the paper space block table record.
        /// </summary>
        /// <param name="database">The database.</param>
        /// <param name="action">A delegate that takes the transaction and the ObjectIds as arguments.</param>
        public static void UsingPaperSpace(this Database database, Action<Transaction, IEnumerable<ObjectId>> action)
        {
            database.UsingBlockTable(BlockTableRecord.PaperSpace, action);
        }

        /// <summary>
        /// Executes a delegate function within the context of a transaction on the specified database.
        /// </summary>
        /// <param name="database">The database.</param>
        /// <param name="action">A delegate that takes the <b>Transaction</b> as an argument.</param>
        public static void UsingTransaction(this Database database, Action<Transaction> action)
        {
            using (var tr = database.TransactionManager.StartTransaction())
            {
                try
                {
                    action(tr);
                    tr.Commit();
                }
                catch (Exception)
                {
                    tr.Abort();
                    throw;
                }
            }
        }

        /// <summary>
        /// Executes a delegate function within the context of a transaction on the specified document.
        /// The document is locked before the transaction starts.
        /// </summary>
        /// <param name="document">The document.</param>
        /// <param name="action">A delegate that takes the <b>Transaction</b> as an argument.</param>
        public static void UsingTransaction(this Document document, Action<Transaction> action)
        {
            using (document.LockDocument())
            {
                document.Database.UsingTransaction(action);
            }
        }

    }

 

Ed


Did you find this post helpful? Feel free to Like this post.
Did your question get successfully answered? Then click on the ACCEPT SOLUTION button.
How to post your code.

EESignature

0 Likes

_gile
Mentor
Mentor

@ed57gmc  a écrit :

Thanks, I missed that one. @shricharana_bharadwaj Below is another class for DbExtentions. I think I got this from @_gile  It might already be included in his library here.


No it's not from me. I think it's also Scott McFarlane's.



Gilles Chanteau
Programmation AutoCAD LISP/.NET
GileCAD
GitHub

0 Likes

ed57gmc
Mentor
Mentor

Oh well, I just know that I got if from one of these forums, maybe even the swamp. Thanks to whoever posted it.

Ed


Did you find this post helpful? Feel free to Like this post.
Did your question get successfully answered? Then click on the ACCEPT SOLUTION button.
How to post your code.

EESignature

0 Likes

shricharana_bharadwaj
Enthusiast
Enthusiast

Hi @ActivistInvestor ,

 

I tried writing the unmanaged object to console. But since AutoCAD crashes, I couldn't read what it says. So I wrote it to a file instead, like below.

 

IntPtr pre = sourceDb.UnmanagedObject;
string createText = pre + Environment.NewLine;
File.WriteAllText("C:\\Autodesk\\Unmananged.txt", createText);

 

 Since this crash issue is happening when using release version of the dll which is referenced by another project, I tried with the release version with the other project in debug mode. Adding the above three lines seems to crash AutoCAD every time. And in the txt file being written, there is always a random number. This was the no. last time it crashed - "1713974747552" . I'm assuming that means that the unmanaged object is being referenced to properly.

without issues?

And the crash is happening at the same location as the screenshot I shared before.

 

@ed57gmc  Thank you for sharing all the resources. Much appreciated. I'll try to use them and see if I still get the crashes. As far as I can guess though, the issue doesn't seem to be in the code itself.

 

0 Likes

ActivistInvestor
Advisor
Advisor

Referencing the UnmanagedObject isn't going to throw an exception if the pointer to the underlying AcDbDatabase is bad or invalid. To find out, try replacing the call to WblockCloneObjects() with a call to another method of the Database (UpdateExt() for example). If that call fails in the same way, it means that the pointer to the underlying AcDbDatabase is bad.

 

You also haven't mentioned what execution context the code is running in (application/document context, and/or awaited/async). That could have something to do with it.

0 Likes

shricharana_bharadwaj
Enthusiast
Enthusiast

Hi @ActivistInvestor ,

 

The application is running in Application/Document context, on the main thread. Multi-threading is not being used.

0 Likes