.NET
cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

Saving changes to a drawing. (LISP related maybe).

7 REPLIES 7
Reply
Message 1 of 8
Millerni456
1259 Views, 7 Replies

Saving changes to a drawing. (LISP related maybe).

Hey all,

 

Despite what I see in the .NET documentation, I always run into issues when it's time to save my drawings.

 

So, here is my objective:

I need to open a drawing, run a LISP command that modifies that drawing, use C# to modify the drawing, then save.

I will be doing this for a list of drawings as well.

 

My goal is to change hundreds of drawings in an efficient and automatic way.

 

 

I want to start fresh, but I will post the little code that I am using.  I have been trying a lot of different things to save these drawings, but I don't want to descredit methods that seemed to not work.

 

Anyways.... here is what I got:

//First, I open the drawing from a file on my computer.  Note, that it is not for read only access.
AcAp.Document acadDoc = AcAp.Application.DocumentManager.Open(myDrawing, false); Database database = acadDoc.Database;
//I attempted locking the document for modification (currently is disabled).
//I don't know if the document should be locked since I am modifying with AutoLISP right now. //using (AcAp.DocumentLock docLock = acadDoc.LockDocument()) { //I also attempted to use a transaction. Modifications are done to the database, yet I am offloading that job to an AutoLISP script.
//I do not know if I need to create and commit a transaction. using (Transaction tr = database.TransactionManager.StartTransaction()) {
//Below are two commands to load a lisp and then run it. Assume these work; I've watched them make changes. //Note, the first bool argument must be false because the document cannot be activated until the GUI is closed. acadDoc.SendStringToExecute(String.Format("(load \"{0}\") ", MY_LISP_FILE), false, false, true); acadDoc.SendStringToExecute("RUNLISP ", false, false, true);

//I've attempt to use database.SaveAs to save changes. This results in an eFileSharingViolation exception that doesn't tell me anything. //database.SaveAs(bomDWG, true, DwgVersion.Current, database.SecurityParameters); tr.Commit(); } }//Unlock

//I've also attempt to just Close and Save, this doesn't give me an exception, but nothing saves. acadDoc.CloseAndSave(bomDWG);

 

So, what do I need to do?

- Lock Document (Y/N)?

- Commit a Transaction (Y/N)?

- Database.SaveAs() (Y/N)?  (If so, which parameters)

- Document.CloseAndSave() (Y/N)?

 

Also, here is some technical information you may need to understand my problem:

- The file I open should have the same name as the file I save (the file overwrites itself)

- I do not want a .bak file to be created.

 

 

If anyone could explain thorough what is required and WHY, it would be greatly appreciated.

 

 

 

7 REPLIES 7
Message 2 of 8
Millerni456
in reply to: Millerni456

UPDATE:

I see that I was using the Transactions incorrectly in the above example.  They are not needed since I never use it.  (AutoLisp doesn't use it after).

 

Also, after conducting several more tests I found that all of the AutoLisp commands execute after the .NET code finishes.  Here is my modified code which shows this behavior:

 

AcAp.Document acadDoc = AcAp.Application.DocumentManager.Open(MYDRAWING, false);
                Database database = acadDoc.Database;
                using (AcAp.DocumentLock docLock = acadDoc.LockDocument())
                {
                    using (Transaction tr = database.TransactionManager.StartTransaction())
                    {
//I created this utility to return a collection of ObjectId's for all block references of a given name. ObjectIdCollection blockRefIds = Tools.GetBlockReferences(database, "MYBLOCKNAME"); if (blockRefIds.Count > 0) { ObjectId blockRefId = blockRefIds[0]; BlockReference blockRef = (BlockReference)tr.GetObject(blockRefId, OpenMode.ForRead); //<--This is how a transaction is used. foreach (ObjectId attributeId in blockRef.AttributeCollection) { AttributeReference attribute = (AttributeReference)tr.GetObject(attributeId, OpenMode.ForWrite); //<--Again with the transaction. if (attribute.Tag.Equals("ATT1", StringComparison.OrdinalIgnoreCase) || attribute.Tag.Equals("ATT2", StringComparison.OrdinalIgnoreCase)) { attribute.TextString = "Hello"; } else if (attribute.Tag.Equals("ATT3", StringComparison.OrdinalIgnoreCase)) { attribute.TextString = "Hello Again"; } } } tr.Commit(); } //Note, the first bool argument must be false because the document cannot be activated until the GUI is closed. acadDoc.SendStringToExecute(String.Format("(load \"{0}\") ", MY_LISP_FILE), false, false, true); acadDoc.SendStringToExecute("RUNLISP ", false, false, true); acadDoc.SendStringToExecute("QSAVE ", false, false, true); //<--LISP method to save drawing.

}//Unlock acadDoc.CloseAndSave(MYDRAWING); //.NET method to save drawing.

 

After running the code above, I notice that all changes except for the LISP functions are made and saved.  If I remove the last line which saves the document using .NET, I notice that the LISP functions execute and save.

 

 

So, I suppose a better question to ask is this.   How do I run LISP commands immediately, instead of waiting for them to execute after the .NET code returns?

Message 3 of 8
Millerni456
in reply to: Millerni456

Although not the solution I was hoping to do, because it is kind of hacky,  I am able to close the drawing through AutoLISP.

 

In order, this is what happens.

.NET code schedules AutoLISP code by using stringToExecute

.NET code executes to modify the drawing.

AutoLISP code follows after.

AutoLISP closes drawing (and saves).

 

I am still looking for a proper way to do this.

Message 4 of 8
_gile
in reply to: Millerni456

Hi,

 

You can synchronously invoke LISP function from .NET using the Application.Invoke() method.

To be able to invoke the LISP function from the .NET application, you have to be prefix it with: c: or use the vl-acad-defun function.

 

Example 1, a LISP function with none argument prefixed with c:

Lisp function:

(defun c:foo () (alert "foo") (princ))

 C# code:

AcAp.Invoke(new ResultBuffer(new TypedValue((int)LispDataType.Text, "c:foo")));

 

Example 2, a LISP function with one argument using vl-acad-defun

Lisp function:

(defun foo (msg) (alert msg) (princ))
(vl-acad-defun "foo")

 C# code:

ResultBuffer args = new ResultBuffer(
    new TypedValue((int)LispDataType.Text, "foo"),
    new TypedValue((int)LispDataType.Text, "Message"));
AcAp.Invoke(args);

 



Gilles Chanteau
Programmation AutoCAD LISP/.NET
GileCAD
GitHub

Message 5 of 8
Millerni456
in reply to: _gile

This is great.

 

How can I use Application.Invoke to load a new lisp and run it?

I've been looking at threads about creating ResultBuffer's and I cannot seem to create any that do not result in an eInvalidInput exception.

(Sometimes, I don't get the exception, and AutoCAD just crashes.)

 

In general, how do I construct meaningful ResultBuffer's for LISP scripts.

Is there documentation on this?

 

 

Threads about AutoLISP and Application.Invoke:

http://www.theswamp.org/index.php?topic=40540.0

http://adndevblog.typepad.com/autocad/2012/05/returning-nested-list-from-net-to-lisp.html

 

 

I would like to do something like this:

ResultBuffer args = new ResultBuffer();
args.Add(new TypedValue((int)LispDataType.ListBegin));
args.Add(new TypedValue((int)LispDataType.Text, "load"));
args.Add(new TypedValue((int)LispDataType.Text, "\"C:\\\\users\\\\...\\\\DO_SOMETHING.LSP\""));
args.Add(new TypedValue((int)LispDataType.ListEnd));

AcAp.Application.Invoke(buffer);
Message 6 of 8

I think .NET code must be running in the document execution context in order to use Invoke() to run LISP. (e.g., if the code you show is from a CommandMethod, the CommandFlags.Session flag must NOT included).

Message 7 of 8
_gile
in reply to: Millerni456

Hi,

 

First, Application.Invoke() method can only run defun LISP functions (the first item in the ResultBuffer argument have to be the LISP function name).

 

Second, I think you're going to a wrong route calling LISP function from .NET. It would be simpler to have a single .NET command (including the converted the LISP code into C#) or write the attribute stuff in LISP and call the LISP function(s) from a script (search for ScriptPro for example).



Gilles Chanteau
Programmation AutoCAD LISP/.NET
GileCAD
GitHub

Message 8 of 8
Millerni456
in reply to: _gile

Hi _gile,

 

I take it that built-in commands must be defun LISP functions in order to work as well.  I tried using c:Appload with no success, so I believe it is not a defun LISP function:

 

args.Add(new TypedValue((int)LispDataType.Text, "c:APPLOAD"));
args.Add(new TypedValue((int)LispDataType.Text, PATH_TO_LISP_FILE));

 

Ideally, yes, it would be great to have all of our code in .NET.  Unfortunately, we already have a lot of LISP functions defined, and it makes sense to use them at least in the short term.  On the other hand, it also makes sense to use .NET because we process a lot of files and we need graphical user interfaces.  Our LISP commands would need to be loaded for each drawing to make modifications.

 

By the way, I've been using SendStringToExecute to load and run LISP command with success.  However, do you know when exactly these strings are executed?  I am sending strings to finish up work to be done in LISP, then I use LISP to save and close.

 

Essentially, I am doing all of the .NET work first, then finish up in LISP..

 

 

It would be great to have the best of both .NET and AutoLISP. 🙂

 

UPDATE:

I found this thread about SendStringToExecute, it appears that the synchronization can achieved by creating a callback from LISP to .NET using the LispEnded event.

http://www.theswamp.org/index.php?topic=39319.0

Can't find what you're looking for? Ask the community or share your knowledge.

Post to forums  

Autodesk DevCon in Munich May 28-29th


Autodesk Design & Make Report

”Boost