Use of *Error* to make undo begin/end behave

JamesMaeding
Advisor
Advisor

Use of *Error* to make undo begin/end behave

JamesMaeding
Advisor
Advisor

For years I have thrown in the *error* functions to just exit somewhat cleanly, like:

(defun c:somecommand ()

  (DEFUN *error* (msg)
    (PRINC "\nFunction Cancelled\n")
    (PRINC)
  )

<code>

)

 

but I never cared too much about it. Sometimes I would throw in some variable restore statements.

But now some users of mine are getting hit with accidental undo's that go too far back.

 

I believe it is because of (vla-startundomark (VLA-GET-ACTIVEDOCUMENT (VLAX-GET-ACAD-OBJECT))) that is not closed out with

(vla-endundomark (VLA-GET-ACTIVEDOCUMENT (VLAX-GET-ACAD-OBJECT))).

 

It is hard to prevent that happening, as even if I am perfect in my code, the may have routines from wherever that did not close out the undo-begins.

I am wondering how to close out any undo-begins others may have started.

Let's say some other tool did this 5 times:

(vla-startundomark (VLA-GET-ACTIVEDOCUMENT (VLAX-GET-ACAD-OBJECT)))

 

How should I handle that?

It may be that I do not have my head on straight, and my guess at the problem is wrong.

If so, suggestions please - alternate career options included and always welcome.

 

If I am on the right track, is one (vla-endundomark... enough to end them all?

If not, how do I detect "non-closed" undomarks?

 

This can't be a new issue, url's also welcome.

 


internal protected virtual unsafe Human() : mostlyHarmless
I'm just here for the Shelties

0 Likes
Reply
1,231 Views
11 Replies
Replies (11)

Kent1Cooper
Consultant
Consultant

I use my little DUH command:

 

;; DumpUndoHistory.lsp
(defun C:DUH ()
  (setvar 'cmdecho 0)
  (command "_.undo" "_control" "_none" "_.undo" "")
  (setvar 'cmdecho 1)
  (prompt "\nDumped Undo History.")
  (princ)
)

 

But that kills everything -- it doesn't just End hanging Undo-Begins, so you wouldn't then be able to Undo stuff back prior to the initiation of those Undo-Begins.

Kent Cooper, AIA

JamesMaeding
Advisor
Advisor

further testing showed me it will not go back beyond an "unclosed" undo-begin.

You get:

Start of Group encountered.
Must enter UNDO END to go back further.

 

Also, just one (vla-endundomark... is enough to close out all previous (vla-startundomark, as if they were closed out properly.

 

So what can happen is some routine does a (vla-startundomark, and errors out.

Then 50 commands later, my routine does both a (vla-startundomark and (vla-endundomark.

The user does two U commands, and the second one jumps way back to the first (vla-startundomark.

 

Its like (vla-startundomark is really dangerous if not closed out perfectly along the way.

Can I detect or delete previous undo marks from the database somehow to clear them out?

Detecting them is the first thing.


internal protected virtual unsafe Human() : mostlyHarmless
I'm just here for the Shelties

0 Likes

john.uhden
Mentor
Mentor

Hi, James.

 

This is my style, which works well for me...

 

(defun C:TEST ( / *error* cmd Doc)
  (gc)
  (vl-load-com)
  ;;
  ;; This section initializes environmental and program variables:
  ;;
  (or *acad* (setq *acad* (vlax-get-acad-object)))
  (setq cmd (getvar "cmdecho")
           Doc (vla-get-ActiveDocument *acad*)
  )
  (defun *error* (error)
    (if (= (type cmd) 'INT)(setvar "cmdecho" cmd))
    (vla-endundomark Doc)
    (cond
      ((not error))
      ((wcmatch (strcase error) "*QUIT*,*CANCEL*"))
      (1 (princ (strcat "\nERROR: " error)))
    )
    (princ)
  )
  
  (vla-endundomark Doc)
  (vla-startundomark Doc)
  (setvar "cmdecho" 0)
  (command "_.expert" (getvar "expert")) ;; dummy command

  ;; DO YOUR THING HERE

  (*error* nil)
)

John F. Uhden

JamesMaeding
Advisor
Advisor

I think we are doing simul-posts... you read my mind.

Trust me, users that have been bit by this have no problem dumping undo history.

 

The problem is they don't realize the undo went too far back, work for 15 minutes so autosave kicks in and saves a "bad state", then realize the error and are stuck.

I still wonder how to detect the marks, time to study more on that...


internal protected virtual unsafe Human() : mostlyHarmless
I'm just here for the Shelties

0 Likes

JamesMaeding
Advisor
Advisor

Excellent John, thanks for such fast responses!


internal protected virtual unsafe Human() : mostlyHarmless
I'm just here for the Shelties

0 Likes

Ranjit_Singh2
Advisor
Advisor

@Anonymous wrote:

further testing showed me it will not go back beyond an "unclosed" undo-begin.

You get:

Start of Group encountered.
Must enter UNDO END to go back further.

 

Also, just one (vla-endundomark... is enough to close out all previous (vla-startundomark, as if they were closed out properly.

 

So what can happen is some routine does a (vla-startundomark, and errors out.

Then 50 commands later, my routine does both a (vla-startundomark and (vla-endundomark.

The user does two U commands, and the second one jumps way back to the first (vla-startundomark.

 

Its like (vla-startundomark is really dangerous if not closed out perfectly along the way.

Can I detect or delete previous undo marks from the database somehow to clear them out?

Detecting them is the first thing.



This should tell you if a group is currently active.

(= 8 (logand (getvar 'undoctl) 8))

 

However, it will not tell you how many more marks exist in the history. I honestly don't think there is a way to do that. The only way I could think is to have a reactor set for counting any time there is a vla-startundomark and vla-endundomark called and increment a variable by 1 for start and decrement by 1 for end. Checking value of this variable anytime during your drawing session will tell you how many open undomarks stay in the drawing at that instance. @Kent1Cooper's suggestion should work to clear all undo marks. 

JamesMaeding
Advisor
Advisor

again, very helpful Ranjit. I wonder if undo history is the realm of arx only. I have not looked at that in .net API or pinvoke yet.


internal protected virtual unsafe Human() : mostlyHarmless
I'm just here for the Shelties

0 Likes

JamesMaeding
Advisor
Advisor

John,

Why the (command "_.expert" (getvar "expert")) ;; dummy command?

was that just filler example code, or actually needed?

 

I also throw in dummy commands sometimes for particular reasons so just wondering.


internal protected virtual unsafe Human() : mostlyHarmless
I'm just here for the Shelties

0 Likes

JamesMaeding
Advisor
Advisor

I added the code recommended to my current "WTIT" routine and attached.

For those not familiar, that routine has been around for years and is better than copy/paste IMO.

It will not work in the block editor though, use copy/paste there.

 

One cool command combination in it is WTN and ITN.

One user types WTN, a name, and picks stuff.

Another user type ITN, types the name, and it pastes at same cords.

Like network copy paste.

 

Other variations too, but edit the block paths at the top to your environment.

Add (vl-load-com) at top if you want, I do that in acaddoc.lsp so not in after routines...

Comments welcome.

 


internal protected virtual unsafe Human() : mostlyHarmless
I'm just here for the Shelties

0 Likes

john.uhden
Mentor
Mentor

Yeah, well, I was only hanging here all day waiting for your question to show up.  :]

John F. Uhden

0 Likes

john.uhden
Mentor
Mentor

Years ago I experienced situations where lisp programs that didn't call (command ...) would terminate with excess blabber on the text screen.  Plus, I think that AutoCAD's undo works only with commands.  So the dummy command serves both purposes.  Technically, you probably need not use it in C: functions that call (command ,,,), but it doesn't hurt to add it anyway as it doesn't change anything.

John F. Uhden

0 Likes