how to open an AutoCAD file update and close with save using C#

how to open an AutoCAD file update and close with save using C#

kite15
Advocate Advocate
25,521 Views
26 Replies
Message 1 of 27

how to open an AutoCAD file update and close with save using C#

kite15
Advocate
Advocate

Hi,

I am trying to open some AutoCAD file from a Location. After that in the newly opened file added a circle as required. Finally, Close the drawing file with save the newly drawing files.

Actually an error message shown that "Drawing is busy (COMException was caught)".

I have search so many link where discussed about the same. Whereas I am confuse which one the concrete solution for this type of coding.

Here is my Code please help!!

        [CommandMethod("OPSV",CommandFlags.Session)]
        public static void OpenSaveDwgFiles()
        {
            Document doc = Autodesk.AutoCAD.ApplicationServices.Application.DocumentManager.MdiActiveDocument;
            Database db = doc.Database;
            try
            {
                var path = @"C:\TestBack\";
                string dwgFlpath = string.Empty;
                DirectoryInfo d = new DirectoryInfo(path);
                FileInfo[] Files = d.GetFiles("*.dwg");
                //string str = "";
                foreach (FileInfo file in Files)
                {
                    DocumentCollection docMgr = Autodesk.AutoCAD.ApplicationServices.Application.DocumentManager;
                    var fileName = Path.GetFileName(file.FullName);
                    dwgFlpath = path+fileName;
                    DocumentCollectionExtension.Open(docMgr, dwgFlpath,false);
                    using (DocumentLock LckDoc = doc.LockDocument())
                    {
                        using (Transaction tr = db.TransactionManager.StartTransaction())
                        {
                            BlockTable bt = tr.GetObject(db.BlockTableId, OpenMode.ForRead) as BlockTable;
                            BlockTableRecord btr = tr.GetObject(bt[BlockTableRecord.ModelSpace], OpenMode.ForWrite) as BlockTableRecord;
                            using (Circle crcl = new Circle())
                            {
                                crcl.Center = new Point3d(1, 1, 0);
                                crcl.Radius = 2;
                                btr.AppendEntity(crcl);
                                tr.AddNewlyCreatedDBObject(crcl, true);
                            }
                            tr.Commit();
                        } 
                    }
                    //doc.CloseAndDiscard();
                    doc.CloseAndSave(dwgFlpath.Substring(0,dwgFlpath.Length-4));
                }

            }
            catch (System.Exception ex)
            {
                Autodesk.AutoCAD.ApplicationServices.Application.DocumentManager.MdiActiveDocument.Editor.WriteMessage(ex.ToString());
            }
        }

Thanks

 

0 Likes
Accepted solutions (1)
25,522 Views
26 Replies
Replies (26)
Message 2 of 27

dennis
Advisor
Advisor

I open a little different, that you may try:

docMgr.Open(fullpathfilename, false);

foreach (Document ndoc in docMgr)

{

      if (ndoc.Name.Equals(fullpathfilename)

      {

             docMgr.MdiActiveDocument = ndoc;

             *do your stuff*

      }

}

0 Likes
Message 3 of 27

_gile
Consultant
Consultant
Accepted solution

Hi,

 

You do not need to open the files in the Editor, opening them "in memory" should be much more faster.

 

        [CommandMethod("OPSV")]
        public static void OpenSaveDwgFiles()
        {
            try
            {
                var path = @"C:\TestBack\";
                DirectoryInfo d = new DirectoryInfo(path);
                FileInfo[] Files = d.GetFiles("*.dwg");
                foreach (FileInfo file in Files)
                {
                    var fileName = Path.GetFileName(file.FullName);
                    string dwgFlpath = path + fileName;
                    using (Database db = new Database(false, true))
                    {
                        db.ReadDwgFile(dwgFlpath, FileOpenMode.OpenForReadAndAllShare, false, null);
                        using (Transaction tr = db.TransactionManager.StartTransaction())
                        {
                            BlockTable bt = tr.GetObject(db.BlockTableId, OpenMode.ForRead) as BlockTable;
                            BlockTableRecord btr = tr.GetObject(bt[BlockTableRecord.ModelSpace], OpenMode.ForWrite) as BlockTableRecord;
                            using (Circle crcl = new Circle())
                            {
                                crcl.Center = new Point3d(1, 1, 0);
                                crcl.Radius = 2;
                                btr.AppendEntity(crcl);
                                tr.AddNewlyCreatedDBObject(crcl, true);
                            }
                            tr.Commit();
                        }
                        db.SaveAs(dwgFlpath, DwgVersion.Current);
                    }
                }
                Application.ShowAlertDialog("All files processed");
            }
            catch (System.Exception ex)
            {
                Application.DocumentManager.MdiActiveDocument.Editor.WriteMessage(ex.ToString());
            }
        }


Gilles Chanteau
Programmation AutoCAD LISP/.NET
GileCAD
GitHub

0 Likes
Message 4 of 27

kite15
Advocate
Advocate

Hi,

Now its working fine,

While opening the prepared drawing files it's recommended for RECOVER.

 

I have modified one line 

 

db.SaveAs(dwgFlpath.Substring(0,dwgFlpath.Length-4), DwgVersion.Current);

Instead of:

 

 

db.SaveAs(dwgFlpath, DwgVersion.Current);

 

Finally, its working smoothly......Smiley Very Happy

Thank you very much

0 Likes
Message 5 of 27

kite15
Advocate
Advocate

Can you please suggest how to get the newly opened LayoutManager.

Actually, whenever i have tried to change the layouts over the newly opened layouts its not working.

The LayoutManager takes the current files layout information only.

 

I have tried....

 

 

 

                        using (Database db = new Database(true, false))
                        {
                            db.ReadDwgFile(dwgFlpath, FileOpenMode.OpenForReadAndAllShare, false, null);
                            using (Transaction tr = db.TransactionManager.StartTransaction())
                            {
                                DBDictionary lDic = (DBDictionary)tr.GetObject(db.LayoutDictionaryId, OpenMode.ForRead);
                                LayoutManager laymgr = LayoutManager.Current;
                                if (lDic.Contains(RnLayTo))
                                {
                                    laymgr.RenameLayout(RnLayTo, "Test_" + RnLayTo);
                                }
                                tr.Commit();
                            }
                            db.SaveAs(dwgFlpath.Substring(0, dwgFlpath.Length - 4), DwgVersion.Current);
                        }

Thanks.

0 Likes
Message 6 of 27

_gile
Consultant
Consultant

LayoutManager.Current return the LayoutManager of the working database.

You have to set the working database to the side database at each loop in the foreach and reset it to the current database.

                // store the current database
                Database currentDatabase = Application.DocumentManager.MdiActiveDocument.Database;
                foreach (FileInfo file in Files)
                {
                    var fileName = Path.GetFileName(file.FullName);
                    string dwgFlpath = path + fileName;
                    using (Database db = new Database(false, true))
                    {
                        db.ReadDwgFile(dwgFlpath, FileOpenMode.OpenForReadAndAllShare, false, null);
                        // set the working database to the 'side database'
                        HostApplicationServices.WorkingDatabase = db;
                        using (Transaction tr = db.TransactionManager.StartTransaction())
                        {
                            DBDictionary lDic = (DBDictionary)tr.GetObject(db.LayoutDictionaryId, OpenMode.ForRead);
                            LayoutManager laymgr = LayoutManager.Current;
                            if (lDic.Contains(RnLayTo))
                            {
                                laymgr.RenameLayout(RnLayTo, "Test_" + RnLayTo);
                            }
                            tr.Commit();
                        }
                        db.SaveAs(dwgFlpath, DwgVersion.Current);
                        // reset the current database as working database
                        HostApplicationServices.WorkingDatabase = currentDatabase;
                    }
                }


Gilles Chanteau
Programmation AutoCAD LISP/.NET
GileCAD
GitHub

0 Likes
Message 7 of 27

kite15
Advocate
Advocate

Getting FATALError.

It does not able to rename the layout. Although, new elements can be added to the block table record.

.....Smiley Mad

0 Likes
Message 8 of 27

_gile
Consultant
Consultant

@kite15 wrote:

Getting FATALError.

It does not able to rename the layout. Although, new elements can be added to the block table record.

.....Smiley Mad


This works fine for me:

        static string RnLayTo = "Layout1";

        [CommandMethod("OPSV")]
        public static void OpenSaveDwgFiles()
        {
                // store the current database
                Database currentDatabase = Application.DocumentManager.MdiActiveDocument.Database;
            try
            {
                var path = @"C:\TestBack\";
                DirectoryInfo d = new DirectoryInfo(path);
                FileInfo[] Files = d.GetFiles("*.dwg");
                foreach (FileInfo file in Files)
                {
                    var fileName = Path.GetFileName(file.FullName);
                    string dwgFlpath = path + fileName;
                    using (Database db = new Database(false, true))
                    {
                        db.ReadDwgFile(dwgFlpath, FileOpenMode.OpenForReadAndAllShare, false, null);
                        // set the working database to the 'side database'
                        HostApplicationServices.WorkingDatabase = db;
                        using (Transaction tr = db.TransactionManager.StartTransaction())
                        {
                            DBDictionary lDic = (DBDictionary)tr.GetObject(db.LayoutDictionaryId, OpenMode.ForRead);
                            LayoutManager laymgr = LayoutManager.Current;
                            if (lDic.Contains(RnLayTo))
                            {
                                laymgr.RenameLayout(RnLayTo, "Test_" + RnLayTo);
                            }
                            tr.Commit();
                        }
                        db.SaveAs(dwgFlpath, DwgVersion.Current);
                        // reset the current database as working database
                        HostApplicationServices.WorkingDatabase = currentDatabase;
                    }
                }
                Application.ShowAlertDialog("All files processed");
            }
            catch (System.Exception ex)
            {
                Application.DocumentManager.MdiActiveDocument.Editor.WriteMessage(ex.ToString());
            }
        }

 



Gilles Chanteau
Programmation AutoCAD LISP/.NET
GileCAD
GitHub

0 Likes
Message 9 of 27

Anonymous
Not applicable

use   CommandFlags.Session in command method. it works.

[CommandMethod("opd", CommandFlags.Session)]

 

 

0 Likes
Message 10 of 27

1161314
Enthusiast
Enthusiast

I'm trying to do the same with this code:

using System;
using System.Windows;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Autodesk.AutoCAD.DatabaseServices;
using System.Windows.Forms;


namespace Etransm
{
    public class Etransm
    {
        [Autodesk.AutoCAD.Runtime.CommandMethod("eTransm")]
        public static void AETransm(string desenho)
        {
            //string dwgFilePath = null;
            MessageBox.Show(desenho);
            desenho = @"C:\Users\jmc\Desktop\Nova pasta\4448-00-02-02-02-005-PE-01.dwg";
            System.Windows.Forms.MessageBox.Show("Entrou DLL");
            Autodesk.AutoCAD.DatabaseServices.Database myDB = new Autodesk.AutoCAD.DatabaseServices.Database(false, true);
            myDB.ReadDwgFile(desenho, FileOpenMode.OpenForReadAndAllShare, true, "");
            using (Transaction myTrans = myDB.TransactionManager.StartTransaction())
            {
                DBDictionary nod = myTrans.GetObject(myDB.NamedObjectsDictionaryId, OpenMode.ForRead) as DBDictionary;
                ObjectId imageDictId = nod.GetAt("ACAD_IMAGE_DICT");
                DBDictionary imageDict
                    = myTrans.GetObject(imageDictId, OpenMode.ForRead)
                        as DBDictionary;

                foreach (DBDictionaryEntry dbDictEntry in imageDict)
                {
                    RasterImageDef rasterImageDef = myTrans.GetObject(dbDictEntry.Value, OpenMode.ForWrite) as RasterImageDef;

                    try
                    {
                        if (System.IO.File.Exists(rasterImageDef.SourceFileName))
                        {
                            dynamic dwgPathUri = new Uri(desenho);
                            dynamic rasterImagePathUri = new Uri(rasterImageDef.SourceFileName);

                            // Make the raster image path  
                            // relative to the drawing path 
                            dynamic relativeRasterPathUri = dwgPathUri.MakeRelativeUri(rasterImagePathUri);

                            // Set the source path as relative 
                            rasterImageDef.SourceFileName = Uri.UnescapeDataString(relativeRasterPathUri.ToString());

                            // Reload for AutoCAD to  
                            // resolve active path 
                            rasterImageDef.Load();

                            // Check if we found it 


                            //RasterImage raster = new RasterImage();
                        }
                    }
                    catch (Exception ex)
                    {

                    }

                    myTrans.Commit();
                    myDB.SaveAs(desenho, DwgVersion.Current);
                    myDB.Dispose();
                }
            }
            return;
        }



    }
}

but keep getting system null reference in transactionmanager...

0 Likes
Message 11 of 27

_gile
Consultant
Consultant

Hi,

 

A method decored with the Commandmethod attribute must return void and cannot have arguments.



Gilles Chanteau
Programmation AutoCAD LISP/.NET
GileCAD
GitHub

0 Likes
Message 12 of 27

1161314
Enthusiast
Enthusiast

Thanks for the replay. So, should i remove it? Or replace it with other command?

0 Likes
Message 13 of 27

_gile
Consultant
Consultant

@1161314 wrote:

Thanks for the replay. So, should i remove it? Or replace it with other command?


If you want to define a command, you should prompt the user for the file name.



Gilles Chanteau
Programmation AutoCAD LISP/.NET
GileCAD
GitHub

0 Likes
Message 14 of 27

1161314
Enthusiast
Enthusiast

Sorry, i didn't understand your answer, i'm new in autocad api. I want to compile that code and then call it in a Revit addin. I've allready manage to pass the file from Revit to the autocad dll. I just keep getting the null reference on the transactionmanager.

0 Likes
Message 15 of 27

_gile
Consultant
Consultant

@1161314 wrote:

Sorry, i didn't understand your answer, i'm new in autocad api. I want to compile that code and then call it in a Revit addin. I've allready manage to pass the file from Revit to the autocad dll. I just keep getting the null reference on the transactionmanager.


Sorry, I do not understand what you want to do. The upper code is supposed to define an AutoCAD command which cannot be called from Revit.

To make your code work, you have to remove the argument of the method.

 

        [Autodesk.AutoCAD.Runtime.CommandMethod("eTransm")]
        public static void AETransm()
        {
            //string dwgFilePath = null;
            string desenho = @"C:\Users\jmc\Desktop\Nova pasta\4448-00-02-02-02-005-PE-01.dwg";            
MessageBox.Show(desenho); // ... }


Gilles Chanteau
Programmation AutoCAD LISP/.NET
GileCAD
GitHub

0 Likes
Message 16 of 27

1161314
Enthusiast
Enthusiast

That part of the code is confusing, you are right. My company has a software that manages all the drawings. In Revit, we have an addin that exports the drawings to a temporary folder, and then, thru sql, creates a definitive folder in our server. then the dwg exported from Revit are copied to that folder. All the images that are in the drawings have absolute path, when the temp folder is deleted, they loose the reference. This i can't change. 

So, what i want is:

  • Then Revit exports, i want to batch open the dwgs and change the path type of the images;
  • After that, the code copies the dwgs and the images to the server folder.

In the code that i posted, the string with the path was a test, i want to send it from the Revit solution, and already have done that.

0 Likes
Message 17 of 27

1161314
Enthusiast
Enthusiast

To better explain my self, i'm gonna post the code again. So, this is the line in my revit addin code that calls the autocad dll:

public static void Etrans(string desenho)
        {
            try
            {
                Etransm.Etransm transm = new Etransm.Etransm();
                MethodInfo mi = transm.GetType().GetMethod("AETransm");
                Object[] ob = { desenho };
                if(mi != null)
                {
                    Object returnValue = mi.Invoke(transm, ob);
                    //return returnValue;
                }
            }
            catch (Exception ex)
            {
                System.Windows.Forms.MessageBox.Show(ex.ToString());

This is the code of the external dll:

using System;
using System.Windows;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Autodesk.AutoCAD.DatabaseServices;
using System.Windows.Forms;

namespace Etransm
{
    public class Etransm
    {
        //[Autodesk.AutoCAD.Runtime.CommandMethod("eTransm")]
        public static void AETransm(string desenho)
        {
            MessageBox.Show(desenho); 
           Autodesk.AutoCAD.DatabaseServices.Database myDB = new Autodesk.AutoCAD.DatabaseServices.Database(false, true);
            myDB.ReadDwgFile(desenho, FileOpenMode.OpenForReadAndAllShare, true, "");
            using (Transaction myTrans = myDB.TransactionManager.StartTransaction())
            {
                DBDictionary nod = myTrans.GetObject(myDB.NamedObjectsDictionaryId, OpenMode.ForRead) as DBDictionary;
                ObjectId imageDictId = nod.GetAt("ACAD_IMAGE_DICT");
                DBDictionary imageDict
                    = myTrans.GetObject(imageDictId, OpenMode.ForRead)
                        as DBDictionary;

                foreach (DBDictionaryEntry dbDictEntry in imageDict)
                {
                    RasterImageDef rasterImageDef = myTrans.GetObject(dbDictEntry.Value, OpenMode.ForWrite) as RasterImageDef;

                    try
                    {
                        if (System.IO.File.Exists(rasterImageDef.SourceFileName))
                        {
                            dynamic dwgPathUri = new Uri(desenho);
                            dynamic rasterImagePathUri = new Uri(rasterImageDef.SourceFileName);

                            // Make the raster image path  
                            // relative to the drawing path 
                            dynamic relativeRasterPathUri = dwgPathUri.MakeRelativeUri(rasterImagePathUri);

                            // Set the source path as relative 
                            rasterImageDef.SourceFileName = Uri.UnescapeDataString(relativeRasterPathUri.ToString());

                            // Reload for AutoCAD to  
                            // resolve active path 
                            rasterImageDef.Load();
                            // Check if we found it 
                            //RasterImage raster = new RasterImage();
                        }
                    }
                    catch (Exception ex)
                    {
                    }
                    myTrans.Commit();
                    myDB.SaveAs(desenho, DwgVersion.Current);
                    myDB.Dispose();
                }
            }
            return;
        }
    }
}

The variable "desenho" is the dwg file that i want to past.

 

When i run the command in Revit i get this error:

image.png

0 Likes
Message 18 of 27

_gile
Consultant
Consultant

You cannot use the AutoCAD .NET API from outside AutoCAD. IOW, you can only use the AutoCAD .NET API when you NETLOAD the DLL from AutoCAD.



Gilles Chanteau
Programmation AutoCAD LISP/.NET
GileCAD
GitHub

0 Likes
Message 19 of 27

1161314
Enthusiast
Enthusiast

But can i force to inicialize autocad and then acess the image and change the path type?

0 Likes
Message 20 of 27

d.j.gray
Contributor
Contributor
Is there any way that ghis could be used to run a script file? I dont want to use scriptpro. 
0 Likes