Send command to multiple drawings

Send command to multiple drawings

p.serek
Contributor Contributor
1,810 Views
2 Replies
Message 1 of 3

Send command to multiple drawings

p.serek
Contributor
Contributor

Hi,

 

I've got a pretty nasty problem with AutoCAD .NET API. I want to programmatically open every drawing in project, run some commands inside, like AUDIT and then save and close.

 

I've tried Editor.Command() and also Document.SendStringToExecute().

 

SendStringToExecute is asynchronous so I gave up on this for a while because I wasn't able to force it do work as intended. I would like to send commands, then wait for them to execute and then save and close drawing. I've tried Document.CommandEnded event but to be honest it was the only idea that came to my mind:

 

 

foreach (var file in files)
            {
                using (var doc = docMgr.Open(file, false, ""))
                {
                    var ready = false;
                    doc.CommandEnded += (sender, e) =>
                    {
                        (sender as Document).CloseAndDiscard();
                        ready = true;
                    };

                    doc.SendStringToExecute("DWGNAME",true, false, true);

                    while (!ready)
                        Thread.Sleep(100);
                }
            }

 

It just stops on a while loop and don't go any further.

 

The other approach was to use Editor.Command(). That's what I use:

 

foreach (var file in files)
            {
                using (var doc = docMgr.Open(file, false, ""))
                {
                    doc.Editor.Command("DWGNAME");
                    doc.CloseAndDiscard();
                }
            }

 

But now all it does is to run the command in one document, the document I run the command from. And it logs it's document name. I'm aware that Editor.Command() works only in document's context so I've tried to set the active document by

 

Application.DocumentManager.MdiActiveDocument = doc

 

But then I got an error "Invalid input".

 

What an I missing? How can I wait until SendStringToExecute stops working or how can I access Editor of a document I've just opened? And I know that now I don't save my document, these snippets are just for testing.

 

I don't use any flags btw, I've read that Editor doesn't work with CommandFlags.Session.

 

Or maybe I can do this without opening document with Database.ReadDwgFile()? But then I don't have access to Editor.

 

Thanks in advance!

 

Przemek

 

0 Likes
Accepted solutions (1)
1,811 Views
2 Replies
Replies (2)
Message 2 of 3

norman.yuan
Mentor
Mentor
Accepted solution

I'd avoid doing batch-processing work by opening document in editor whenever it is possible and using "side database" approach instead. If a constant batch process of large quantity of documents is required by business, I'd consider either using core console, or even Forge Services (now, Platform Services).

 

Anyway, if you do have to do it by opening each document in AutoCAD editor for some reason(s), you can do it in 2 ways:

 

1. In a NON-session CommandMethod

foreach var file in fileNames)

{

     var doc=Application.DocumentManager.OPen(file, [true/false]);

    DoSomething(doc);

    CloseDoc(doc); //save change or not...

}

 

Yes, with a command of DocumentContext, new document can be opened, but the newly opened document DOES not become MdiActiveDocument, so in the method DoSpmething(Document), you cannot use Editor object, nor you can call Editor.Command() or SendStringToExecute(). But the logic of the whole process is quite clean/simple;

 

2. You can using a CommandMethod with Session Flags. You can do the samething as above, as long as you do not call Editor.Command() or SendStringToExecute() (because the "foreach ..." loop does not wait for it to finish). If you do want to use SendStringToExecute() to execute available command (why not, in most cases), you then can can use CommandEnded event handler as the looping mechanism instead of "for..." loop. You were almost right except you mixed the "for..." loop and the CommandEnded event handling.

 

The code would be like (off my head, only meant for ideas):

[CommandMethod("BatchWork", CommandFlags.Session)]
private static List<string> _fileNames=null;
private static int _fileIndex=0;
public static void DoBatch()
{
  _fileNames=GetFileNames();
  _fileIndex=0;
  ProcessFile()
}

private static void ProcessFile()
{
  if (_fileIndex==_fileNames.Count) return;
  
  // The opened document will be MdiActiveDocument, because of the SessionFlag
  var doc=Application.DocumentManager.Open(_fileNames[_fileIndex], false);
  doc.CommandEnded += MyCommandEnded;
  doc.SendStringToExecute("MyCommand ", false, false, true);
}

private static void MyCommandEnded(object sender, CommandEventArgs e)
{
  if (e.GlobalCommandName.ToUpper()!="MYCOMMAND) return;

  // deal the the current document after all work is done
  var doc=Application.DocumentManager.MdiActiveDocument;
  doc.CommandEnded-=MyCommandEnded;
  doc.Database.SaveAs(....); // if you want to save changes
  doc.CloseAndDiscard();

  // start processing next drawing
  _fileIndex++;
  ProcessFile();
}

Depending on what/how thing is done in the "MyCommand", you may also want to handle CommandCancelled and/or CommandFailed event, if necessary.

 

Norman Yuan

Drive CAD With Code

EESignature

Message 3 of 3

p.serek
Contributor
Contributor

Thanks @norman.yuan!

 

Now everything works fine.

 

I wasn't able to do what I needed by using only database (even though I've tried because it would be much faster) and ended up with the second approach based on Document.CommandWillStart event.

 

Funny thing is that you have to be in document context or something to save database and close document. I had a really bad time with it and almost gave up. But finally my code looks something like this:

 

private void ProcessFile()
{
  Document doc;
  doc = Application.DocumentManager.Open(_fileNames[_fileIndex], false);
  doc.CommandWillStart += CommandWillStart;
  doc.SendStringToExecute("DWGNAME\r", true, false, true);
  doc.SendStringToExecute("QSAVE\r", false, false, true);
  doc.SendStringToExecute("CLOSE\r", false, true, false);
}

private void CommandWillStart(object sender, CommandEventArgs e)
{
  if (e.GlobalCommandName.ToUpper() != "CLOSE")
    return;
  var doc = (sender as Document);
  doc.CommandWillStart -= CommandWillStart;
  _fileIndex++;
  ProcessFile();
}

 

So basically I have to save and close documents by commands. That's quite wired for me but it works.

 

Przemek