Jump to content

DCL / LSP development - Constant Autocad 2018 crash


MrRokDok

Recommended Posts

A few questions:

1) DCL errors locks up autocad while running - need to kill Autocad using task manager

2) Autocad crashes when building a program that I forgot to save-all changes - even though it prompts me that i can save them.

3) Debug logging being cached, therefore, log messages being writting to a file with the application are incomplete.

 

Some details:

I have been using Autocad for many years, and wrote numerous programs in lsp, but newer at DCL. During development, of the DCL, actions, events, etc. I find that (due to my own coding errors) autocad locks up while testing the project that I am making. I have searched many hours over the past month, but have yet to find a solution where if an error occurs while starting the DCL or interacting with it, the interface stops responding and requires a task manager, kill autocad to start again.

 

I have setup custom error traps, use VL-BT and a drawing log file (which actually dumps the error before I kill autocad. While I wish I can get the code right on the first time (maybe in a few decades), is there a better way to exit the dcl without killing autocad?

 

One more item that I encounter is when in the Visual Lisp IDE, if I forget to "save-all" before building, I get a command prompt where it says I can save; however, it crashes autocad every time. is this normal? this behavior was in 2016 and 2018.

 

Thanks for your help and suggestions.

Link to comment
Share on other sites

I have a visual lisp project with a number of lsp files. When I compile the project, Visual Lisp IDE shows a message that some files need to be saved before building. I click save all, and then AutoCAD crashes!

 

The work around for me was to save all before building, but sometimes I forget. Does anyone else have this problem and is there a known solution?

 

I have searched for the past month at different times, but have not found anything that resolves the issue.

 

Any suggestions would be appreciated.

Link to comment
Share on other sites

Hate to be the one to kick in an open door (not!) but train yourself in saving your work before testing or compiling , just look at it as digital diaper training... use the preview function for dialogs in the editor. Make a template function with ok_only or ok_cancel button. When creating a new function start with the template, saveas and then add buttons as you go.

Link to comment
Share on other sites

Thx for the comments. I typically save before compiling, but sometimes forget. I just was wondering why it would crash every time I forget to save. Autocad knows you didn't save it, it warns you, then it asks if you want to save, then it crashes.

 

Is this the way it works for others? I am much better at saving and its not an issue, just an annoyance occasionally.

 

Understand your comments on DCL, i use the preview and build the DCL adding features. While the DCL seems to work, its the supporting LSP that causes problems as I am writing code. I make some coding mistakes and when setting action tiles, mode or values, the autocad stops responding if there is an error.

 

Just wondering if there is a way to avoid killing autocad each time as part of troubleshooting. The other option is to stop making coding mistakes so it doesn't crash... maybe someday.

Link to comment
Share on other sites

There are just a couple of things you must make sure are correct. Never reference a tile that's not there. So rather copy and paste the names to avoid typo's. And most tiles demand strings as input so check your data before you use it. If you have like say a popup list with predefined strings like left , right , bottom etc. you can even put them in the dcl definition itself. But when you pass a list , first check if all items are strings. When testing , put a watch before every tile in order to make sure you see the correct data is going to it. Lisp is really forgiving when it comes to mix data types but dcl isn't. And if you're having trouble with a code , just post and there will always be somebody willing to help you.

 

 

Just remembered Grrr had a nice check for dcl input : http://www.cadtutor.net/forum/showthread.php?102696-AutoCAD-Lisp-Routine-to-change-all-attributes-text-to-a-style/page3&highlight=%28defun+void

 

 

Ah, I see... altho I liked that subfoo, so I use it /a rewritten version/ in some of my DCL routines (to check for a valid edit_box input).

Still solvable without vl-* :


; _$ (mapcar 'valid-p (list nil "" " " "  " 123 "str" "                            ")) >> (nil nil nil nil 123 T nil)

(defun valid-p ( s / c )   
 (cond ( (not (eq 'STR (type s))) s)  ( (= s "") nil)  ( (/= (setq c (substr s 1 1)) " ") t) ( (valid-p (substr s 2)) )   ) ); defun valid-p

(although above function tests for a value rather than if it's a string)

 

 

and just a couple more :

; test (setq a 1 b "" c nil) -> 1 "" nil -> (nonil '(a b c)) -> 1 "" ""
(defun nonil (%vl) (mapcar '(lambda (x) (if (null (vl-symbol-value x))(set (read (vl-symbol-name x)) ""))) %vl))
; same but for 1 value
(defun !nil (%v) (if (null %v) "" %v))

Edited by rlx
Link to comment
Share on other sites

You can load just a dcl and just look at it as a 1st check to see if dcl at least works, then choose what ever, close the dcl and check the lisp answers for the dcl reponse, eg (action_tile "key1" "(setq val1 $value)") on the command line type !val1 should reveal a text string if you get nil that's ok maybe if its what your code expects check your other variables. !val2 !val3

 

Post your dcl we can have a look at it.

Link to comment
Share on other sites

 

Thanks, although I don't remember why I did used that 'c' variable -

Maybe my first intention was to map to the whole string and return the results for each chr, instead of vl-some'ing and return a bool. :thinking:

 

 

and just a couple more :

 

Nice, now you got me playing with quoted-lists of symbols (aka unevaluated lists).

 

I think that it would be faster just to reassign the values, than checking for nulls:

; _$ (mapcar 'set '(a b c d) '("" nil 23 nil)) >> ("" nil 23 nil)
; _$ (nil->dquote '(a b c d))                  >> ("" "" 23 "")
(defun nil->dquote ( nil->dquote:L )
 (foreach nil->dquote:x nil->dquote:L
   (set nil->dquote:x (cond ((vl-symbol-value nil->dquote:x)) ("")))
 ); foreach
 (mapcar 'eval nil->dquote:L) ; if you want-ed the function to return something
)

 

You could switch "" to nil, vice-versa:

; _$ (mapcar 'set '(a b c d e x) '(nil "" nil nil 123 "string")) >> (nil "" nil nil 123 "string")
; _$ (nil<->dquote '(a b c d e x))                               >> ("" nil "" "" 123 "string")
; _$ (nil<->dquote '(a b c d e x))                               >> (nil "" nil nil 123 "string")
(defun nil<->dquote ( nil<->dquote:L )
 (foreach nil<->dquote:x nil<->dquote:L
   (set nil<->dquote:x ((lambda (x) (cond ( (not x) "") ( (= x "") nil) (x))) (vl-symbol-value nil<->dquote:x)))
 ); foreach  
 (mapcar 'eval nil<->dquote:L) ; if you want-ed the function to return something
); defun

 

Note, in my codes:

'->' means 'to'

'' means 'switch'

 

I use such type of subfunctions (to reassign symbol's values) very rare, but most oftenly this one (to reset the vars in my code to nil, when debugging manually) :

; (SymsSetToNull '(a b c d e f g h i x a1 a2 a3 a4 b1 b2 b3 b4 aa bb cc))
(defun SymsSetToNull ( SymsSetToNull:L )
 (foreach SymsSetToNull:x SymsSetToNull:L
   (set SymsSetToNull:x nil)
 )
 (mapcar 'eval SymsSetToNull:L)
)

Link to comment
Share on other sites

Nice, now you got me playing with quoted-lists of symbols (aka unevaluated lists).

 

I think that it would be faster just to reassign the values, than checking for nulls:

; _$ (mapcar 'set '(a b c d) '("" nil 23 nil)) >> ("" nil 23 nil)
; _$ (nil->dquote '(a b c d))                  >> ("" "" 23 "")
(defun nil->dquote ( nil->dquote:L )
  (foreach nil->dquote:x nil->dquote:L
    (set nil->dquote:x (cond ((vl-symbol-value nil->dquote:x)) ("")))
  ); foreach
  (mapcar 'eval nil->dquote:L) ; if you want-ed the function to return something
)

 

 

Nice to get you playing again Grrr :D

 

 

I mostly focus on easy to read code than speed unless I need it. Had to search couple of thousand of drawings for a string and decided to give master Lee's bfind a go but it was to slow , only because I needed to scan for simple text's and attributes and it does so much more so quickly made a lighter version with some digital ductape haha. And also found that wcmatch is faster than vl-string-search so don't think I'm gonna rewrite my little text appie to vl- commands.

 

 

But the main issue for OP is to first check the data before passing it to set_tile

 

 

It's getting to hot for heavy coding anyway :P

 

 

its more time for :beer:

 

 

8)

Link to comment
Share on other sites

I mostly focus on easy to read code than speed unless I need it.

 

I tend to do a breakdown my overall code into subfunctions, where each one ha a clear description of required input and what it does return/or do.

If an error occured into the subfoo, then I blame on it and try to fix it.. (but usually try to forecast all the possible errors in the first place).

 

 

Had to search couple of thousand of drawings for a string and decided to give master Lee's bfind a go but it was to slow , only because I needed to scan for simple text's and attributes and it does so much more so quickly made a lighter version with some digital ductape haha. And also found that wcmatch is faster than vl-string-search so don't think I'm gonna rewrite my little text appie to vl- commands.

 

I see that he uses the RegExp object in order to cover the processing of all/most of the text-formatted strings, so yea it should be relatively slow but necessary -

else one might mess up his drawing(s) and blame on him.

 

 

It's getting to hot for heavy coding anyway :P

its more time for :beer:

 

Indeed! :beer:

 

 

Ontopic,

I usually put/define the problematic .lsp code (that uses DCL) into VLIDE's console, to debug it

After running it, and getting the error -

From VLIDE's window -> debug -> last break source to determine where and what error occured.

And to force closing the locked dialog, just use Ctrl+R, or in VLIDE's window Debug->Reset to top level.

 

But IMO the best way is just to use your own error-free DCL code template and just use it.

And I don't see any other way to kill ACAD with task manager, because of the locked up dialog and VLIDE not being opened.

Link to comment
Share on other sites

just something to play with , educational purposes only

(defun c:test ( / fn fp id msg drv)
 ; make temp dialog
 ; here I use mapcar but in this case (a tiny dialog) this probably could fit on one line (write line "test : dialog ....etc" fp)
 ; you can use mapcar to write a list of strings but many roads lead to Rome , this is just one of them
 ; because we write dialog directly to file you need \" even though you mean "
 (if (and (setq fn (vl-filename-mktemp ".dcl")) (setq fp (open fn "w")))
   (mapcar '(lambda (x)(write-line x fp))
    '("test : dialog {label = \"Just a little test\";"
      ": text_part { key = \"txt_part\";}"
      ":button { key = \"bye\"; label = \"bye\";}"
      ":button { key = \"zoom\"; label = \"Zoom\";}"
      "spacer; ok_cancel;}"
     )
   )
 )
 ; close the file pointer
 (if fp (close fp))
 ; start the dialog
 (if (and (findfile fn) (setq id (load_dialog fn)) (new_dialog "test" id))
   (progn
     ; these should allways be the very first on your list, without them you can't exit your dialog
     ; if you don't have a 'retirement button' your only way out is ctrl-alt-del
     ; you can make your own retirement button to exit the dialog by giving it allow_accept = true; in dcl
     (action_tile "cancel" "(done_dialog 0)")
     (action_tile "accept" "(done_dialog 1)")
     ; note that in : \"bye\" an escape character is used \" = " so the key name for action_tile is "bye"
     ; also, this won't work , key is case sensitive "Bye" is not equal to "bye"
     (action_tile "Bye" "(done_dialog 2)")
     ; msg should be a string and if its not an error message is displayed
     (setq msg nil)
     (if (= (type msg) 'STR) (set_tile "txt_part" msg) (set_tile "txt_part" "* msg error *"))
     ; get value from non existing tile only gives nil , wont crash autocad
     (setq err (get_tile "missing"))
     
     ; will cause error and stop lisp but wont crash autocad :
     ; error: bad argument type: stringp nil
     ; (set_tile "txt_part" nil)
     
     ; this will give an error, trying to set non existing tile to a non existing value :
     ; error: bad argument type: stringp nil
     ;(set_tile "missing" nil)


     ; set a non existing tile (misspelled name) with a good value just does nothing
     (set_tile "missing" "")

     ; if you really want to mess things up, uncommend the command line if you dare! , well do ya punk? ;-)
     ; this will crash autocad. Absofreakinglutely no commands calls while dialog is active else : ctrl-alt-del...
     ; So allways save your work before you test.
     ;(command "zoom" "extents")


     ; if you want to do a command call , first end dialog
     (action_tile "zoom" "(done_dialog 3)")
     
     (setq drv (start_dialog))
     (unload_dialog id)
     (cond
((= drv  0)(alert "Exit with cancel"))
((= drv  1)(alert "Exit with ok"))
((= drv  2)(alert "Exit with bye button"))
; first close the dialog and now you can safely perform command calls
((= drv  3)(alert "I will now zoom extents...")(command "zoom" "extents"))
     )
   )
 )
 ; if you didn't catch the error , this line will never be executed and temp dialog will be lonely forever
 ; so define error function to clean up your mess (defun *error* (s)(if (and fn .... (vl-file-delete fn))) 
 ; clean up temp dialog file
 (if (and fn (findfile fn))(vl-file-delete (findfile fn)))
 (princ)
)

error 500 is really irritating!

Edited by rlx
Link to comment
Share on other sites

Appreciate the feedback posted. While over the past month, I came to many of the same conclusions / solutions posted in this thread, I was hoping for something I was just stupid and overlooking something that would make my coding life easier in lisp/dcl.

 

The DCL I have been working on has about 40 controls of different types and serves as the UI to control analysis that I wrote in 2001, 17 years ago.

 

After this experience, my next step is to learn how to do this in Visual Studio / C#.

 

Some specific steps implemented:

1) created a dictionary of all DCL control names to validate against - avoid typo's

2) wrote wrappers functions for "get_tile", "set_tile" "tile_mode" etc. to ensure proper key and values are being used.

3) used the dictionary of control names to store the values, and "enabled" or "disabled" (since I have not found a way to get the current "tile_mode" for a key).

4) wrote some debugging functions to log status and errors throughout the code, in addition to a push-stack (at the start of a function) and pop-stack (at the end of a function) that tracks the code paths and logs it to a file.

5) tried to abstract the functions as much as possible for small tasks that are reusable.

6) Writing *ERROR* handlers that includes the VL-BT command to "dump" the call stack.

7) Turn on the autocad log file so if AutoCAD does crash, I at least have an indication of where to look to correct the issue (VL-BT is written to the log file)

6) For my DCL front end, setting defaults, control validations, control event logic, store / restore settings, etc. I used 80+ functions with 1600+ lines of code. What I though would be a relatively easy update turned into complete rewrite a few times as I found better ways to implement.

 

General issues remaining:

1) writing log information to a file during execution - how to force writing to the file instead of caching the data and writing later.

2) A way to write the output of VL-BT to a log file (instead of the main autocad log file)

 

I hope this might be useful to others.

Link to comment
Share on other sites

@Grrr & totally oftopic

Nice, now you got me playing with quoted-lists of symbols (aka unevaluated lists).

I think that it would be faster just to reassign the values, than checking for nulls:

(defun tst ( / start a b c d e f g h i j k l m)
 
 (defun nonil (%vl) (mapcar '(lambda (x) (if (null (vl-symbol-value x))(set (read (vl-symbol-name x)) ""))) %vl))
 
 (defun nil->dquote ( nil->dquote:L )
   (foreach nil->dquote:x nil->dquote:L (set nil->dquote:x (cond ((vl-symbol-value nil->dquote:x)) (""))))
   (mapcar 'eval nil->dquote:L))
 
 (setq start (car (_vl-times)))
 (repeat 10000
   (mapcar 'set '(a b c d e f g h i j k l m) '(1 2 3 4 5 6 7 8 9 0 nil nil nil))
   (nonil '(a b c d e f g h i j k l m)))
 (princ (strcat "\n\nProcessed nonil in " (rtos (/ (- (car (_vl-times)) start) 1000.) 2 4) " secs."))
 (princ "\n\n\n")
 (setq start (car (_vl-times)))
 (repeat 10000
   (mapcar 'set '(a b c d e f g h i j k l m) '(1 2 3 4 5 6 7 8 9 0 nil nil nil))
   (nil->dquote '(a b c d e f g h i j k l m)))
 (princ (strcat "\n\nProcessed nil->dquote in " (rtos (/ (- (car (_vl-times)) start) 1000.) 2 4) " secs."))
 (princ)
)
(tst)

yes , it is (you are) faster :beer:

Well , back to work , seems suddenly (here at work) everybody wants a piece of me this week , well my attention anyway

:D

Link to comment
Share on other sites

@Grrr & totally oftopic

 

Nice quick benchmark - Learned something new 2day! :thumbsup:

 

BTW more accurate comparsion would be if they are defined like this:

(defun nonil (%vl) (foreach x %vl (if (null (vl-symbol-value x))(set (read (vl-symbol-name x)) ""))))

 

(defun nil->dquote ( nil->dquote:L )
 (foreach nil->dquote:x nil->dquote:L (set nil->dquote:x (cond ((vl-symbol-value nil->dquote:x)) (""))))
)

 

I used foreach function instead of mapcar for your subfoo (because mapcar is mapping a function, and its slower than foreach).

In mine removed the (mapcar 'eval L) part, since for this case returns are not required.

But the results are the same - when compared again.

 

 

 

Well , back to work , seems suddenly (here at work) everybody wants a piece of me this week , well my attention anyway

 

Its written somewhere on the web: ITs are required only when problem occurs or theres something to develop, for the rest of the time they are unnoticeable.

Cough *Internal Server Error* cough *500*.

 

Happy coding! :)

Link to comment
Share on other sites

how about just

 (defun Grrrill (%vl) (foreach v %vl (set v (cond ((vl-symbol-value v)) (""))))) 

, for short :D

 

 

nah, first some (power distribution) lists were in autocad , then oh no! , not good! , must be excel , and now (years later) , oh no! not good! , must be autocad. Fine with me , but no money (order number) no deal...

 

 

almost weekend :beer:

Link to comment
Share on other sites

Appreciate the feedback posted. While over the past month, I came to many of the same conclusions / solutions posted in this thread, I was hoping for something I was just stupid and overlooking something that would make my coding life easier in lisp/dcl.

 

The DCL I have been working on has about 40 controls of different types and serves as the UI to control analysis that I wrote in 2001, 17 years ago.

 

After this experience, my next step is to learn how to do this in Visual Studio / C#.

 

Some specific steps implemented:

1) created a dictionary of all DCL control names to validate against - avoid typo's

2) wrote wrappers functions for "get_tile", "set_tile" "tile_mode" etc. to ensure proper key and values are being used.

3) used the dictionary of control names to store the values, and "enabled" or "disabled" (since I have not found a way to get the current "tile_mode" for a key).

4) wrote some debugging functions to log status and errors throughout the code, in addition to a push-stack (at the start of a function) and pop-stack (at the end of a function) that tracks the code paths and logs it to a file.

5) tried to abstract the functions as much as possible for small tasks that are reusable.

6) Writing *ERROR* handlers that includes the VL-BT command to "dump" the call stack.

7) Turn on the autocad log file so if AutoCAD does crash, I at least have an indication of where to look to correct the issue (VL-BT is written to the log file)

6) For my DCL front end, setting defaults, control validations, control event logic, store / restore settings, etc. I used 80+ functions with 1600+ lines of code. What I though would be a relatively easy update turned into complete rewrite a few times as I found better ways to implement.

 

General issues remaining:

1) writing log information to a file during execution - how to force writing to the file instead of caching the data and writing later.

2) A way to write the output of VL-BT to a log file (instead of the main autocad log file)

 

I hope this might be useful to others.

 

 

Didn't see your post before but it looks like we're the ones asking you for advise from now on :D

 

 

I assume C# has the same dcl abilities as VBA (a graphic DCL editor) and more. Only things that really matter are verify your data before starting dialog , make sure you have a working exit button and no command calls during active dialog... rest should be peanuts for you!

 

 

:beer:

Edited by rlx
Link to comment
Share on other sites

I was hoping for something I was just stupid and overlooking something that would make my coding life easier in lisp/dcl.

 

The DCL I have been working on has about 40 controls of different types and serves as the UI to control analysis that I wrote in 2001, 17 years ago.

 

After this experience, my next step is to learn how to do this in Visual Studio / C#.

 

I'd say coding with LISP is alot easier and flexible, than other languages.

DCL is alot different (and limited), than designing User/Windows Forms, because you do it thru pure coding -

Cycle of:

1.Write/Modify the DCL code

2.Display the dialog in ACAD to check its design

 

On the other hand, you can quickly get used to working with C#'s WindowsForm projects on Visual studio but the main problem would be looking for techniques to do "this" and "that" in ACAD.

Another alternative for easier design of dialogs and being not-so limited at accessing objects'n'classes would be through VBA.

BTW the Form's controls have more events than DCL's tiles - for DCL you can check if something's being clicked/double-clicked,

while when using forms you can check if the cursor is hovered inside/outside of the control and if some KBD key has been pressed.

 

If I was you I would list exactly(be alot more specific) what I want - i.e. "upon what *events* that are possible in this language, to perform what exact algorithm/steps that are logical for the code".

Say:

1) writing log information to a file during execution - how to force writing to the file instead of caching the data and writing later.

 

During execution of what? Pressing an 'execute' button? Running the dialog itself?, the command-call, Autocad?

About the force writing you could try something with the logfilename system variable.

 

 

IMO unless you don't have a certain idea to develop, that could not be done with the language you are using - the switch to a higher-level language is useless.

 

BTW Heres a C# demo, that uses WindowsForm to gather few inputs and write them to a .txt file -

But you could do the same with LISP&DCL

 

AcadDialogInputGather.gif

Link to comment
Share on other sites

before I flush my work computer and begin my weekend : (gc) or garbage collection , forces all buffers to clear so usually I combine (close fp) with (gc)

 

 

well , enough for this week , here @ work that is , have a good weekend

 

 

nice demo Grrr!

 

 

gr. Rlx

Link to comment
Share on other sites

@rlx - there is much I don't know, but I am still learning. This forum has helped me greatly as many of my questions had answers / solutions posted.

 

@grrr - i agree, once you get the hang of LISP, it is quite easy and flexible. I have done some development in C#, and creating a UI with events, validation, etc. is sure is a lot easier compared to DCL where there is complexity.

 

Regarding the log file - I am using the AutoCAD system variable logfilename, and it does write the results of VL-BT to the file (even when I cause ACAD to crash). While executing my lisp code, and writing to a separate log file, when it crashes, not all information is written to the log file. What I would like to understand is how to force writing while my program is executing, so if it does crash for some reason, I have all the logging information to correct the issue. VL-BT has helped me out a lot but I cannot figure out a way to write that output to my log file (not the logfilename system variable file - if this makes sense.

 

Is it possible to capture the output from VL-BT and redirect it to the a log file?

 

As suggested by rlx, I will try (gc) to see if this will help force writing to a file. I will try putting a (gc) in my write file function. Thinking about it now, I could just close/reopen the file (which would add overhead and slow down the code)...

 

I will also zip up my project and share it with the group... maybe I am making this more complicated than it needs to be!

Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Unfortunately, your content contains terms that we do not allow. Please edit your content to remove the highlighted words below.
Reply to this topic...

×   Pasted as rich text.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...