Can't close MDIdocument

Can't close MDIdocument

Anonymous
Not applicable
2,236 Views
12 Replies
Message 1 of 13

Can't close MDIdocument

Anonymous
Not applicable

Hi all,

I'm trying to run a batch script that modify a drawing a little, change xdata in polylines and change their layers.

The problem is every time the process ends I want  to save the changes and close the drawing and move to the next but I always get a locked document exception.

 

I was trying to use something like this

 

try
{
var obj = CadHelper.GetSystemVariable("DWGTITLED");
if (doc != null && System.Convert.ToInt16(obj) == 0)
{
// Save the active drawing
doc.Database.SaveAs(s, true, DwgVersion.Current,
doc.Database.SecurityParameters);
doc.CloseAndDiscard();
}
}

 

Not sure why is locked still since every time I use doc.lockDocument comand I use it within a using statement so it should dispose properly, but I still get the error.

0 Likes
Accepted solutions (1)
2,237 Views
12 Replies
Replies (12)
Message 2 of 13

norman.yuan
Mentor
Mentor

The code itself you post is not the source of the problem. It is the context where the code runs in: does the code run in application context, or in document context? If it is latter, you cannot close a document while code runs in its context.

 

In you case, what does "batch script" mean? Is it a *.scr file (AutoCAD script file) that list a bunch of commands (including you custom commands defined with "CommandMethod" attribute)?

 

If the showed code runs within "CommandMethod" decorated class method, you need to set CommandFlags.Session to indicate the code should be executed in application context, so that you can close document.

 

You might want to post more code that tells how the code is executed (i.e. the usual CommandClass and/or the IExtensionApplication class where the AutoCAD .NET API code gets run from).

Norman Yuan

Drive CAD With Code

EESignature

0 Likes
Message 3 of 13

Anonymous
Not applicable
Accepted solution

Hi Norman,

Thanks for the answer, that explains more.

Right now I'm using the Session flag on the command but the opening and closing the drawing part is being performed on a separate controller class.

Maybe I need to have that piece of code withing the Command method? Does that makes any sense?

This is the code withing the method

[CommandMethod("TestBatch", CommandFlags.Session)]
public void TestBatch()
{

MyController oController = new MyController(_AppLogger);
OpenFileDialog dialog = new OpenFileDialog {
DefaultExt = ".dwg",
Multiselect = true,
Filter = "dwg files|*.dwg",
};
Nullable<bool> result = dialog.ShowDialog();
if (result == true)
{
try
{
oController.Start(dialog.FileNames);
}
catch (System.Exception e)
{
Application.DocumentManager.MdiActiveDocument.Editor.WriteMessage(e.StackTrace);
}

}

}

 

this is the function in the Controller class

public void Start(string[] drawings)
{
Drawings.AddRange(drawings);
//Start processing the drawings without issues
foreach (string s in Drawings)
{
//Document doc = Application.DocumentManager.MdiActiveDocument;
DocumentCollection docCollection = Application.DocumentManager;
Document doc = null;
_RpuId = string.Empty;
_FlId = string.Empty;

if (IOFile.Exists(s))
{
if (!AddDrawing(s, Drawings.Count > 1).ResultValue)
continue;

//Open drawing or use current opened drawing
if (!Application.DocumentManager.MdiActiveDocument.Name.Equals(s))
{
doc = docCollection.Open(s, false);
Application.DocumentManager.MdiActiveDocument = doc;
}
else doc = Application.DocumentManager.MdiActiveDocument;
GlobalLogger.Info(string.Format("Start tagging drawing: {0}", s));
}
else
{
GlobalLogger.Info(string.Format("File {0} does not exist.", s));
return;
}
ProcessDrawing(s);
//Save document
try
{
var obj = CadHelper.GetSystemVariable("DWGTITLED");
if (doc != null && System.Convert.ToInt16(obj) == 0)
{
// Save the active drawing
doc.Database.SaveAs(s, true, DwgVersion.Current,
doc.Database.SecurityParameters);
doc.CloseAndDiscard();
}
}
catch (System.Exception e)
{
GlobalLogger.Error(string.Format("Couldn't save drawing {0}, {3}exception {1}{3}{2}",
s, e.Message, e.StackTrace, Environment.NewLine));
}
}

}

 

 

 

 

 

0 Likes
Message 4 of 13

norman.yuan
Mentor
Mentor

Something happend: I just posted a reply a few minutes ago (which I have seen it) is now gone. So here I re-post it:

 

Your code seems OK in regarding opening, saving and closing drawings with CommandFlags.Session set. Maybe there is other thing you did not mention/reveal.

 

Here is my much simplified code that opens multiple drawings, changes something in each drawing's DB, and then saves and closes:

 

using System.Collections.Generic;
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.Runtime;
using CadApp = Autodesk.AutoCAD.ApplicationServices.Application;

[assembly: CommandClass(typeof(BatchWorkTest.MyCommands))]

namespace BatchWorkTest
{
    public class MyCommands
    {
        [CommandMethod("BatchDwg", CommandFlags.Session )]
        public static void RunCommand()
        {
            var dwg = CadApp.DocumentManager.MdiActiveDocument;
            var ed = dwg.Editor;

            var fileNames = GetFileNames();
            if (fileNames==null)
            {
                ed.WriteMessage("\n*Cancel*");
                return;
            }

            ProcessDwgs(fileNames);
            
        }

        private static IEnumerable<string> GetFileNames()
        {
            IEnumerable<string> files = null;
            using (var dialog = new System.Windows.Forms.OpenFileDialog
            {
                DefaultExt = ".dwg",
                Multiselect = true,
                Filter = "dwg files|*.dwg",
            })
            {
                if (dialog.ShowDialog()== System.Windows.Forms.DialogResult.OK)
                {
                    files = dialog.FileNames;
                }
            }

            return files;
        }

        private static void ProcessDwgs(IEnumerable<string> dwgFiles)
        {
            var dwgCol = CadApp.DocumentManager;

            foreach (var dwgFile in dwgFiles)
            {
                // Since the CommnadFlags.Session flag is set
                // The newly opened drawing becomes MdiActiveDocument automatically
                var dwg = dwgCol.Open(dwgFile, false, null);

                using (var lck = dwg.LockDocument())
                {
                    DoSomethingInDwg(dwg);
                }

                // Save and close
                dwg.Database.SaveAs(dwg.Name, true, DwgVersion.Current, null);
                dwg.CloseAndDiscard();
            }
        }

        private static void DoSomethingInDwg(Document dwg)
        {
            //CadApp.ShowAlertDialog($"Processing drawing:\n{dwg.Name}");

            //change 2 user system variables
            dwg.Database.Useri1 = 1;
            dwg.Database.Useri2 = 2;
        }
    }
}

Here is screencast clip showing how the code work through:

 

 

 

HTH

 

Norman Yuan

Drive CAD With Code

EESignature

Message 5 of 13

ActivistInvestor
Mentor
Mentor

How you solve this problem depends on what's in your ProcessDrawings() method, which I don't see.

 

For example, from the application context it can't make calls to the Editor's Command() method. 

 

There are ways around this, and the code and example in >this post< shows one way.

 


@Anonymous wrote:

Hi Norman,

Thanks for the answer, that explains more.

Right now I'm using the Session flag on the command but the opening and closing the drawing part is being performed on a separate controller class.

Maybe I need to have that piece of code withing the Command method? Does that makes any sense?

This is the code withing the method

[CommandMethod("TestBatch", CommandFlags.Session)]
public void TestBatch()
{

MyController oController = new MyController(_AppLogger);
OpenFileDialog dialog = new OpenFileDialog {
DefaultExt = ".dwg",
Multiselect = true,
Filter = "dwg files|*.dwg",
};
Nullable<bool> result = dialog.ShowDialog();
if (result == true)
{
try
{
oController.Start(dialog.FileNames);
}
catch (System.Exception e)
{
Application.DocumentManager.MdiActiveDocument.Editor.WriteMessage(e.StackTrace);
}

}

}

 

this is the function in the Controller class

public void Start(string[] drawings)
{
Drawings.AddRange(drawings);
//Start processing the drawings without issues
foreach (string s in Drawings)
{
//Document doc = Application.DocumentManager.MdiActiveDocument;
DocumentCollection docCollection = Application.DocumentManager;
Document doc = null;
_RpuId = string.Empty;
_FlId = string.Empty;

if (IOFile.Exists(s))
{
if (!AddDrawing(s, Drawings.Count > 1).ResultValue)
continue;

//Open drawing or use current opened drawing
if (!Application.DocumentManager.MdiActiveDocument.Name.Equals(s))
{
doc = docCollection.Open(s, false);
Application.DocumentManager.MdiActiveDocument = doc;
}
else doc = Application.DocumentManager.MdiActiveDocument;
GlobalLogger.Info(string.Format("Start tagging drawing: {0}", s));
}
else
{
GlobalLogger.Info(string.Format("File {0} does not exist.", s));
return;
}
ProcessDrawing(s);
//Save document
try
{
var obj = CadHelper.GetSystemVariable("DWGTITLED");
if (doc != null && System.Convert.ToInt16(obj) == 0)
{
// Save the active drawing
doc.Database.SaveAs(s, true, DwgVersion.Current,
doc.Database.SecurityParameters);
doc.CloseAndDiscard();
}
}
catch (System.Exception e)
{
GlobalLogger.Error(string.Format("Couldn't save drawing {0}, {3}exception {1}{3}{2}",
s, e.Message, e.StackTrace, Environment.NewLine));
}
}

}

 

 

 

 

 


 

0 Likes
Message 6 of 13

Anonymous
Not applicable

So I believe my issue is not related to this then.

I'm using some third party plugin and I think that is where the issue starts.

I believe that plugin uses reactors and I don't really know is there are jobs running on the background that don't let me close drawings. But to me that seems to be the likely reason.

Now I don't see the original error but I get an Interop exception "Drawing is Busy" so I guess I'm done with the batch script idea.

 

Thanks for the input I really appreciate it.

0 Likes
Message 7 of 13

yoitsarun
Advocate
Advocate

Hi @norman.yuan ,

Sorry to ask in this post. I am using your above code exactly for my plugin.

I want to make it side open. Now it takes too much to time to open all the files in batch process.

 

0 Likes
Message 8 of 13

norman.yuan
Mentor
Mentor

@yoitsarun wrote:

Hi @norman.yuan ,

Sorry to ask in this post. I am using your above code exactly for my plugin.

I want to make it side open. Now it takes too much to time to open all the files in batch process.

 


By "...make it side open", I assume you mean open each drawing as side database (so, you do not have to open the drawing in AutoCAD editor visually, which takes longer time), right? if so, simply change my posted code a bit like this:

 

 

        private static void ProcessDwgs(IEnumerable<string> dwgFiles)
        {
            foreach (var dwgFile in dwgFiles)
            {
                using (var db=new Database(false, true)
                {
                    db.ReadDwgFile(dwgFile, FileOpenMode.OpenForReadAndAllShare, true, null)

                    DoSomethingInDwg(db);

                    // Save and close
                    db.SaveAs(dwg.Name, true, DwgVersion.Current, null);
                }
            }
        }

        private static void DoSomethingInDwg(Database db)
        {
            //CadApp.ShowAlertDialog($"Processing drawing:\n{db.Filename}");

            //change 2 user system variables
            db.Useri1 = 1;
            db.Useri2 = 2;
        }

 

Also, in the [CommandMethod(...)] attribute, there is no need to set CommandFlags.Session flag (but leave it would be harmless as the code is, in this case).

 

HTH

Norman Yuan

Drive CAD With Code

EESignature

Message 9 of 13

yoitsarun
Advocate
Advocate

Thanks @norman.yuan , but iam getting error invalid input . can you please check my code;

 

private static void ProcessDwgs(IEnumerable<string> dwgFiles, Document dwg)
        {            
            foreach (var dwgFile in dwgFiles)
            {
                using (var db = new Database(false, true))
                {
                    db.ReadDwgFile(dwgFile, FileOpenMode.OpenForReadAndAllShare, true, null);
                    DoSomethingInDwg(dwg, db);
                                   
                string prefix = "Acad-";
                string newFileName = Path.Combine(Path.GetDirectoryName(dwg.Name), prefix + Path.GetFileName(dwg.Name));
                dwg.Database.SaveAs(newFileName, true, DwgVersion.Current, default);
                dwg.CloseAndDiscard();
                }
            }
        }

        private static void DoSomethingInDwg(Document dwg, Database db)
        {
            db.Useri1 = 1;
            db.Useri2 = 2;
            Document doc = Autodesk.AutoCAD.ApplicationServices.Application.DocumentManager.MdiActiveDocument;
            // Dim ed As Editor = doc.Editor
            // Dim collection As ObjectIdCollection = New ObjectIdCollection()
            var collection = new ObjectIdCollection();
            using (db)
            {
                // db.ReadDwgFile("c:\Temp\Test.dwg", FileOpenMode.OpenForReadAndWriteNoShare, False, "")

                using (Transaction tr = db.TransactionManager.StartTransaction())
                {
                    BlockTable bt = tr.GetObject(db.BlockTableId, OpenMode.ForRead) as BlockTable;
                    foreach (ObjectId btrId in bt)
                    {
                        BlockTableRecord btr = tr.GetObject(btrId, OpenMode.ForRead) as BlockTableRecord;
                        if (btr.IsFromExternalReference & btr.IsUnloaded)
                        {
                            collection.Add(btrId);
                            btr.UpgradeOpen();
                            string oldPath = btr.PathName;                            
                            string newpath = @"C:\ProgramData\Autodesk\ApplicationPlugins\XrefBind.bundle\Contents\Resources\CadART.dwg";                            
                            btr.PathName = newpath;                            
                        }
                    }

                    if (collection.Count > 0)
                    {
                        db.ReloadXrefs(collection);
                    }

                    tr.Commit();
                }
            }
0 Likes
Message 10 of 13

norman.yuan
Mentor
Mentor

Did you step through the code in debugging, and which line of the code resulted in the error?

 

Some critiques to the code:

 

In DoSomething() method,

1. Why do you need to have Document argument, which is completely no used at all.

2. is my old sample code, the 2 lines

    db.Useri1=1;

    db.Useri2-2;

was just there to show that you can do something with a side database. You do not just copy them over to your code, unless your specific process happens to need them.

3. There is no need to declare a variable to point to MdiActiveDocument and not use it in the method.

4. You do not need to use

    using (db)

    {

        ....

    }

in this method, because the side database has already been wrapped by "using(...){...}" block in the calling code (ProcessDwgs() method)

 

In ProcessDwgs() method:

1. Again, I do not see why you need a Document argument (well, you might argue that you use "dwg" parameter to get new dwg file name, see 2);

2. the way you obtain a new dwg file name for saving changed side database produces the same file name for all side databases being processed., so you would end up only the last processed database being saved. You should use Database .FileName of each processed side database to generate new file name.

3. dwg.ClaseAndDiscard() is not needed. It was in my old sample code, because the original question was about opening multiple drawing in AutoCAD editor for processing. If you read the code I posted to your request, there was no this line, because now the code opens side database one by one, not drawing in AutoCAD editor visually.  As matter of fact, this line might be the offensive line that caused the error. You need to remove it.

Norman Yuan

Drive CAD With Code

EESignature

Message 11 of 13

yoitsarun
Advocate
Advocate

Thanks @norman.yuan . I started coding recently only. I will try what you said.

But please see my below code it works perfectly by opening each dwg one by one as a batch process.

Actually i want to make this as side database.

namespace BatchWorkTest
{
    public partial class MyCommands
    {
        [CommandMethod("BatchDwg", CommandFlags.Session)]
        public void RunCommand()
        {
            var dwg = Autodesk.AutoCAD.ApplicationServices.Application.DocumentManager.MdiActiveDocument;
            var ed = dwg.Editor;
            var fileNames = GetFileNames();
            if (fileNames is null)
            {
                ed.WriteMessage("\n*Cancel*");
                return;
            }
            ProcessDwgs(fileNames);
        }

        private IEnumerable<string> GetFileNames()
        {
            IEnumerable<string> files = null;
            using (var dialog = new OpenFileDialog()
            {
                DefaultExt = ".dwg",Multiselect = true,Filter = "dwg files|*.dwg"})
            {
                if (dialog.ShowDialog() == System.Windows.Forms.DialogResult.OK)
                {
                    files = dialog.FileNames;
                }
            }
            return files;
        }

        private void ProcessDwgs(IEnumerable<string> dwgFiles)
        {            
            var dwgCol = CadApp.DocumentManager;
            foreach (var dwgFile in dwgFiles)
            {                
                var dwg = dwgCol.Open(dwgFile, false, null);
                using (var lck = dwg.LockDocument())
                {
                    DoSomethingInDwg(dwg);
                }
                string prefix = "Acad-";
                string newFileName = Path.Combine(Path.GetDirectoryName(dwg.Name), prefix + Path.GetFileName(dwg.Name));
                dwg.Database.SaveAs(newFileName, true, DwgVersion.Current, default);
                dwg.CloseAndDiscard();
            }
        }
        private static void DoSomethingInDwg(Document dwg)
        {           
            Document doc = Autodesk.AutoCAD.ApplicationServices.Application.DocumentManager.MdiActiveDocument;            
            var collection = new ObjectIdCollection();
            using (Database db = doc.Database)
            {                
                using (Transaction tr = db.TransactionManager.StartTransaction())
                {
                    BlockTable bt = tr.GetObject(db.BlockTableId, OpenMode.ForRead) as BlockTable;
                    foreach (ObjectId btrId in bt)
                    {
                        BlockTableRecord btr = tr.GetObject(btrId, OpenMode.ForRead) as BlockTableRecord;
                        if (btr.IsFromExternalReference & btr.IsUnloaded)
                        {
                            collection.Add(btrId);
                            btr.UpgradeOpen();
                            string oldPath = btr.PathName;
                            string newpath = @"C:\ProgramData\Autodesk\ApplicationPlugins\XrefBind.bundle\Contents\Resources\CadART.dwg";                            
                            btr.PathName = newpath;
                        }
                    }
                    if (collection.Count > 0)
                    {
                        db.ReloadXrefs(collection);
                    }
                    tr.Commit();
                }
            }

            Autodesk.AutoCAD.DatabaseServices.TransactionManager bTransMan;
            Autodesk.AutoCAD.DatabaseServices.Transaction bTrans;
            Document bDwg;
            BlockTable bBT;
            SymbolTableRecord bBTR;
            SymbolTableEnumerator bBTE;
            BlockTableRecord bBTRr;            
            bDwg = Autodesk.AutoCAD.ApplicationServices.Application.DocumentManager.MdiActiveDocument;
            bTransMan = bDwg.TransactionManager;
            bTrans = bTransMan.StartTransaction();
            try
            {
                // Open the database for Read
                bBT = (BlockTable)bDwg.Database.BlockTableId.GetObject(OpenMode.ForRead);
                bBTE = bBT.GetEnumerator();
                var XrefIds = new ObjectIdCollection();
                while (bBTE.MoveNext ())
                {
                    bBTR = (SymbolTableRecord)bBTE.Current.GetObject(OpenMode.ForRead);
                    bBTRr = (BlockTableRecord)bBTR;
                    
                    if (bBTRr.IsFromExternalReference & bBTRr.IsResolved)
                    {
                        XrefIds.Add(bBTR.Id);
                    }
                }

                try
                {
                    bDwg.Database.BindXrefs(XrefIds, false);
                }
                catch (Exception ex)
                {
                    Application.ShowAlertDialog("this drawing don't have any xref or xrefs are unreferenced, Error: " + ex.Message);
                }                
                bTrans.Commit();
            }
            catch (Exception)
            {
                Application.ShowAlertDialog("Warning: this drawing don't have any xref or xrefs are unreferenced");
            }
            finally
            {
                bTrans.Dispose();
                bTransMan.Dispose();
            }
        }
    }
}

If you can please suggest me how to make it possible.

 

0 Likes
Message 12 of 13

norman.yuan
Mentor
Mentor

Here is the code I modified for you (not tested, with NotePad, so there may be typos):

namespace BatchWorkTest
{
    public partial class MyCommands
    {
        ...
        ...

        private void ProcessDwgs(IEnumerable<string> dwgFiles)
        {
            // Use Acad's progress bar, so that AutoCAD would not looks like
            // frozen during long processing
            using (var prog = new ProgressMeter())
            {
               prog.SetLimit(dwgFiles.Count());
               prog.Start("Processing DWG files...";

                foreach (var dwgFile in dwgFiles)
                {   
                    prog.ProgressMether();
             
                    using (var db=new Database(false, true))
                    {
                        db.ReadDwgFile(dwgFile, FileOpenMode.OpenForReadAndWriteNoShare, true, null);
                        DoSomethingInDwg(db);

                        // Save changed database
                        string prefix = "Acad-";
                        string newFileName = Path.Combine(Path.GetDirectoryName(db.Filename), prefix + Path.GetFileName(db.Filename));
                        db.SaveAs(newFileName, true, DwgVersion.Current, default);
                    }
                }

                prog.Stop();
            }
        }

        private static bool DoSomethingInDwg(Database db)
        {              
            using (Transaction tr = db.TransactionManager.StartTransaction())
            {
                var collection = new ObjectIdCollection();
                BlockTable bt = tr.GetObject(db.BlockTableId, OpenMode.ForRead) as BlockTable;
                foreach (ObjectId btrId in bt)
                {
                    BlockTableRecord btr = tr.GetObject(btrId, OpenMode.ForRead) as BlockTableRecord;
                    if (btr.IsFromExternalReference & btr.IsUnloaded)
                    {
                        collection.Add(btrId);
                        btr.UpgradeOpen();
                        string oldPath = btr.PathName;
                        string newpath = @"C:\ProgramData\Autodesk\ApplicationPlugins\XrefBind.bundle\Contents\Resources\CadART.dwg";                            
                        btr.PathName = newpath;
                    }
                }
                if (collection.Count > 0)
                {
                    db.ReloadXrefs(collection);
                }
                tr.Commit();
            }
            
            using (var bTran = db.TransactionManager.StartTransaction())
            {
                var bBT = (BlockTable)db.BlockTableId.GetObject(OpenMode.ForRead);
  
                var XrefIds = new ObjectIdCollection();
                foreach(ObjectId brId in bBT)
                {
                    var bBTRr = (BlockTableRecord)bTran.GetObject(brId, OpenMode.ForRead);
                    if (bBTRr.IsFromExternalReference & bBTRr.IsResolved)
                    {
                        XrefIds.Add(bBTR.Id);
                    }
                }

                if (XrefIds.Count>0)
                {
                    try
                    {
                        db.BindXrefs(XrefIds, false);
                    }
                    catch (Exception ex)
                    {
                        Application.ShowAlertDialog("this drawing don't have any xref or xrefs are unreferenced, Error: " + ex.Message);
                    }
                }               
                bTrans.Commit();
            }
        }
    
    }//end of class
}// end of namespace

    

            

 

Norman Yuan

Drive CAD With Code

EESignature

Message 13 of 13

yoitsarun
Advocate
Advocate

Thanks @norman.yuan ,

i tried above code , batch processing works as fine. but xref reload has some issue. it is not works when the xref is nested and unloaded.

And progress bar is not working.

i changed to 

prog.MeterProgress();

and  added true for boolean.

private static bool DoSomethingInDwg(Database db)

return true;

 

0 Likes