Trying to create a temporary database to hold data for block we want to import

Trying to create a temporary database to hold data for block we want to import

Anonymous
Not applicable
1,349 Views
9 Replies
Message 1 of 10

Trying to create a temporary database to hold data for block we want to import

Anonymous
Not applicable

I’m actually getting an error

System.InvalidProgramException occurred

  HResult=   0x8013153A Common Language Runtime detected an invalid program.

  Source=<Cannot evaluate the exception source>

  StackTrace: at Autodesk.AutoCAD.DatabaseServices.Database.get_TransactionManager()

 

While running this below piece of code

 

                    Database sourceDb = new Database(false, true); //Temporary database to hold data for block we want to import

                    try

                    {

                        sourceDb.ReadDwgFile(dwgPath, System.IO.FileShare.Read, true, ""); //Read the DWG into a side database

                        ObjectIdCollection blockIds = new ObjectIdCollection(); // Create a variable to store the list of block identifiers

 

                        Autodesk.AutoCAD.DatabaseServices.TransactionManager tm = sourceDb.TransactionManager; using (Transaction myT = tm.StartTransaction())

                        {

                            // Open the block table

                            BlockTable bt = (BlockTable)tm.GetObject(sourceDb.BlockTableId, OpenMode.ForRead, false);

 

                            // Check each block in the block table

                            foreach (ObjectId btrId in bt)

                            {

                                BlockTableRecord btr = (BlockTableRecord)tm.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();

                            }

                        }

                        // Copy blocks from source to destination database

                        //IdMapping mapping = new IdMapping(); sourceDb.WblockCloneObjects(blockIds, _database.BlockTableId, mapping, DuplicateRecordCloning.Replace, false); _editor.WriteMessage("\nCopied " + blockIds.Count.ToString() + " block definitions from " + blockToImport + " to the current drawing.");

  }

 
 
 
 
 
0 Likes
Accepted solutions (1)
1,350 Views
9 Replies
Replies (9)
Message 2 of 10

_gile
Consultant
Consultant

Hi,

 

Try like this:

            var doc = Application.DocumentManager.MdiActiveDocument;
            var db = doc.Database;
            using (var sourceDb = new Database(false, true))
            {
                sourceDb.ReadDwgFile(dwgPath, FileOpenMode.OpenForReadAndAllShare, true, null);
                using (var tr = sourceDb.TransactionManager.StartTransaction())
                {
                    var bt = (BlockTable)tr.GetObject(sourceDb.BlockTableId, OpenMode.ForRead);
                    var ids = new ObjectIdCollection();
                    foreach (ObjectId id in bt)
                    {
                        var btr = (BlockTableRecord)tr.GetObject(id, OpenMode.ForRead);
                        if (!btr.IsLayout && !btr.IsAnonymous)
                        {
                            ids.Add(id);
                        }
                    }
                    var mapping = new IdMapping();
                    sourceDb.WblockCloneObjects(ids, db.BlockTableId, mapping, DuplicateRecordCloning.Replace, false);
                    tr.Commit();
                }
            }


Gilles Chanteau
Programmation AutoCAD LISP/.NET
GileCAD
GitHub

0 Likes
Message 3 of 10

Anonymous
Not applicable

Hi Gilles,
Thanks for your response.
What I'm trying to do is, to insert a block in a drawing without opening the Autocad application. I’m ok to run the Autocad application as a background process. I think, the code snippet that you shared will work only when we load the program in autocad using NETLOAD command and then running the [CommandMethod]. Please correct me if my understanding is correct.

0 Likes
Message 4 of 10

_gile
Consultant
Consultant

The AutoCAD .NET API is designed to run in-process only (i.e., compiled as DLL and netloaded in AutoCAD).

Read this topic about out-of-process vs in-process applications.



Gilles Chanteau
Programmation AutoCAD LISP/.NET
GileCAD
GitHub

0 Likes
Message 5 of 10

Anonymous
Not applicable

I'm actually opening the autocad dwg file using "Autodesk.AutoCAD.Interop.Common.AxDbDocument" and able to read the data.  In this case, I'm not using NETLOAD to open the dwg and read data. So, I'm looking on the same lines to insert a block or text in the drawing.

Below is the code that I'm using for opening the dwg file

 

                                Type acType = Type.GetTypeFromProgID(ProgID);
                                acadApp = (AcadApplication)Activator.CreateInstance(acType, true);
                                acadApp.Visible = false;
                                acadApp.Width = 1;
                                acadApp.Height = 1;
                                axDbDocument = acadApp.GetInterfaceObject("ObjectDBX.AxDbDocument." + version);
                                sourceAxDb = axDbDocument as Autodesk.AutoCAD.Interop.Common.AxDbDocument;
                                sourceAxDb.Open(txtFileName.Text);
0 Likes
Message 6 of 10

Anonymous
Not applicable

Below is the error that i get when i'm executing to insert a block or text

 

image001.png

0 Likes
Message 7 of 10

_gile
Consultant
Consultant

One more time, you cannot use the AutoCAD .NET API from a standalone executable application except if you netload some .NET DLL and launch some custom command from this DLL (or expose the .NET assembly to COM which is much less trivial).

 

If you absoluteley want/need to use a standalone application, you can use the COM API to get or start the AutoCAD Application and still using CAM use AxDbDocument to import the block definitions from the source file.

 

Here's a little example, replace AutoCAD major version and the source file path to suit your needs.

        private void button1_Click(object sender, EventArgs e)
        {
            AcadApplication acadApp = null;
            const string progId = "AutoCAD.Application.19";
            try
            {
                acadApp = (AcadApplication)Marshal.GetActiveObject(progId);
            }
            catch
            {
                try
                {
                    acadApp = (AcadApplication)Activator.CreateInstance(Type.GetTypeFromProgID(progId), true);
                }
                catch
                {
                    MessageBox.Show("Instance of 'AutoCAD.Application' could not be created.");
                    return;
                }
            }
            while (true)
            {
                try
                {
                    if (acadApp.GetAcadState().IsQuiescent)
                        break;
                }
                catch { Thread.Sleep(100); }
            }
            acadApp.Visible = false;

            AxDbDocument sourceDoc = null;
            try
            {
                sourceDoc = acadApp.GetInterfaceObject("ObjectDBX.AxDbDocument.19");
                sourceDoc.Open(@"C:\Temp\Blocks.dwg");
                dynamic blocks = sourceDoc.Blocks
                    .Cast<AcadBlock>()
                    .Where(b => !b.IsLayout)
                    .ToArray();
                sourceDoc.CopyObjects(blocks, acadApp.ActiveDocument.Blocks);
                MessageBox.Show($"Copied {blocks.Length} blocks");
            }
            catch(Exception ex)
            {
                MessageBox.Show(ex.Message + "\n" + ex.StackTrace);
            }
            finally
            {
                if (sourceDoc != null)
                    Marshal.ReleaseComObject(sourceDoc);
            }
            acadApp.Visible = true;
        }


Gilles Chanteau
Programmation AutoCAD LISP/.NET
GileCAD
GitHub

0 Likes
Message 8 of 10

Anonymous
Not applicable

Thank you for ur quick response will try this & will let u know for any further queries

0 Likes
Message 9 of 10

ActivistInvestor
Mentor
Mentor

The code that you are showing that is causing the exception can only be used from a DLL that is loaded into AutoCAD using the NETLOAD command.

 

 


@Anonymous wrote:

Below is the error that i get when i'm executing to insert a block or text

 

image001.png


 

0 Likes
Message 10 of 10

_gile
Consultant
Consultant
Accepted solution

Another way (as suggested by @Virupaksha_aithal  in this reply) would be using the Core Console to run a .NET custom command.

 

Here's an example of command (to be called from the Core Console it cannot use the acmgd.dll library, only the accoremgd.dll and acdbmgd.dll).

using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.Runtime;

namespace ImportBlocks
{
    public class Commands
    {
        [CommandMethod("IMPORTBLOCKS")]
        public static void ImportBlocks()
        {
            var db = HostApplicationServices.WorkingDatabase;
            using (var sourceDb = new Database(false, true))
            {
                sourceDb.ReadDwgFile(@"C:\Temp\Blocks.dwg", FileOpenMode.OpenForReadAndAllShare, true, null);
                var ids = new ObjectIdCollection();
                using (var tr = new OpenCloseTransaction())
                {
                    var bt = (BlockTable)tr.GetObject(sourceDb.BlockTableId, OpenMode.ForRead);
                    foreach (ObjectId id in bt)
                    {
                        var btr = (BlockTableRecord)tr.GetObject(id, OpenMode.ForRead);
                        if (!btr.IsLayout && !btr.IsAnonymous)
                            ids.Add(id);
                    }
                    tr.Commit();
                }
                var mapping = new IdMapping();
                sourceDb.WblockCloneObjects(ids, db.BlockTableId, mapping, DuplicateRecordCloning.Replace, false);
            }
        }
    }
}

From a standalone executable (e.g., a Windows Form application), you can launch the Core Console, pass it a script file which NETLOAD and run the upper command.

In the following example, the script file is created 'on the fly' and deleted after used.

Change the paths and AutoCAD version to suit your needs (in this example, the ImportBlocks.dll file is supposed to be in the same directory as the executable).

        private void button1_Click(object sender, EventArgs e)
        {
                var scriptPath = @"C:\Temp\ImportBlocks.scr";
            try
            {
                // create the script file
                using (var writer = new StreamWriter(scriptPath))
                {
                    writer.WriteLine("(command \"NETLOAD\" \"ImportBlocks.dll\")");
                    writer.WriteLine("IMPORTBLOCKS");
                    writer.WriteLine("_SAVE \"C:\\Temp\\TestImportBlocks.dwg\"");
                    writer.WriteLine();
                }
                // run the accore console
                string accorePath = @"C:\Program Files\Autodesk\AutoCAD 2013\accoreconsole.exe";
                string args = $"/s {scriptPath}";
                var startInfo = new ProcessStartInfo(accorePath, args);
                startInfo.WindowStyle = ProcessWindowStyle.Hidden;
                var process = Process.Start(startInfo);
                process.WaitForExit();
                MessageBox.Show("Done");
            }
            catch(Exception ex)
            {
                MessageBox.Show($"{ex.Message}\n{ex.StackTrace}");
            }
            finally
            {
                // delete the script file
                File.Delete(scriptPath);
            }
        }


Gilles Chanteau
Programmation AutoCAD LISP/.NET
GileCAD
GitHub

0 Likes