.NET

.NET

Reply
Valued Contributor
Millerni456
Posts: 55
Registered: ‎01-23-2013
Message 1 of 8 (717 Views)

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

717 Views, 7 Replies
01-10-2014 12:25 PM

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.

 

 

 

Valued Contributor
Millerni456
Posts: 55
Registered: ‎01-23-2013
Message 2 of 8 (705 Views)

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

01-10-2014 01:36 PM 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?

Valued Contributor
Millerni456
Posts: 55
Registered: ‎01-23-2013
Message 3 of 8 (697 Views)

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

01-10-2014 01:56 PM 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.

*Expert Elite*
_gile
Posts: 2,133
Registered: ‎04-29-2006
Message 4 of 8 (688 Views)

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

01-10-2014 03:04 PM 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
Valued Contributor
Millerni456
Posts: 55
Registered: ‎01-23-2013
Message 5 of 8 (639 Views)

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

01-13-2014 08:18 AM 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);
Valued Mentor
DiningPhilosopher
Posts: 370
Registered: ‎05-06-2012
Message 6 of 8 (607 Views)

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

01-13-2014 11:58 AM in reply to: Millerni456
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).

*Expert Elite*
_gile
Posts: 2,133
Registered: ‎04-29-2006
Message 7 of 8 (591 Views)

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

01-13-2014 10:27 PM 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
Valued Contributor
Millerni456
Posts: 55
Registered: ‎01-23-2013
Message 8 of 8 (577 Views)

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

01-14-2014 05:44 AM 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. :smileyhappy:

 

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

Post to the Community

Have questions about Autodesk products? Ask the community.

New Post
Announcements
Do you have 60 seconds to spare? The Autodesk Community Team is revamping our site ranking system and we want your feedback! Please click here to launch the 5 question survey. As always your input is greatly appreciated.