Since Visual LISP was introduced, developers have taken advantage of its ability to call COM Automation interfaces (whether AutoCAD's or other applications'). The addition of this functionality to the LISP platform created many new development possibilities - previously you were able to call through to ObjectARX applications defining LISP functions, but enabling Automation access from LISP suddenly allowed developers to access any other application adopting the COM standard its API, such as Microsoft Excel.
A quick note on error handling in LISP...
Traditionally LISP applications have defined their own (*error*) function to trap errors during execution. During this function they often report the value of ERRNO - used by AutoCAD to tell the LISP app what kind of error has occurred - which in turn can help the user or developer pin down the cause of the problem. This is fine, but this kind of global error-trap doesn't make it easy to resume execution after the error.
When using Automation interfaces things are different. Automation clients generally need to trap exceptions as they occur, rather than defining a global error-handling function. Visual LISP enables this with a very helpful function named (vl-catch-all-apply).
(vl-catch-all-apply) is like the (apply) function, in that it takes a symbol representing the function name to be called as the first argument, followed by the various arguments to be passed to that function in the form of a list. (vl-catch-all-apply) executes the function call, and does its best to trap any errors that occur during it. The main difference between the function signatures of (apply) and (vl-catch-all-apply) is with the return value, which will either be the return value of the function call, if all works well, or an error object that can then be queried for additional information.
Let's take a simple example that doesn't involve Automation, which I've basically stolen from the Visual LISP online help. The following code asks the user to enter two numbers using my favourite LISP function, (getreal) :-), and then tries to perform a division. We check result of the division with (vl-catch-all-error-p), to see whether it succeeded or not: if it didn't we then use (vl-catch-all-error-message) to get an error string telling us what happened.
(defun c:div (/ first second result)
(setq first (getreal "\nEnter the first number: ")
second (getreal "\nEnter the second number: ")
result (vl-catch-all-apply '/ (list first second))
)
(if (vl-catch-all-error-p result)
(princ (strcat "\nCaught an exception: "
(vl-catch-all-error-message result)
)
)
(princ (strcat "\nSuccess - the result is "
(rtos result)
)
)
)
(princ)
)
Here's what happens when you run the command:
Command: div
Enter the first number: 50
Enter the second number: 2
Success - the result is 25.0000
Command: div
Enter the first number: 50
Enter the second number: 0
Caught an exception: divide by zero
So how does this technique apply to Automation calls? Let's take a look at another piece of code, this time calling a function to check the interference between two solids. This code defines a CHECKINT command that asks for two solids to be selected. Assuming two solids are selected, it will call the CheckInterference Automation method, specifying that any resulting intersection should be created as its own solid.
A lot of work was done to enhance the solids capabilities in AutoCAD 2007, so although this function works find in 2007, in prior releases it would often return modelling errors. This code allows it to fail gracefully even in prior releases.
; Helper function to check whether an entity is a solid
(defun is-solid-p (ename)
(= (cdr (assoc 0 (entget ename))) "3DSOLID")
)
; The CHECKINT command
(defun c:checkint (/ first second e1 e2 result)
(vl-load-com)
(setq first (entsel "\nSelect the first solid: ")
second (entsel "\nSelect the second solid: ")
)
(if (and first
second
(is-solid-p (setq e1 (car first)))
(is-solid-p (setq e2 (car second)))
)
(progn
(setq result (vl-catch-all-apply
'vla-CheckInterference
(list (vlax-ename->vla-object e1)
(vlax-ename->vla-object e2)
:vlax-true
)
)
)
(if (vl-catch-all-error-p result)
(princ (strcat "\nCaught an exception: "
(vl-catch-all-error-message result)
)
)
(progn
(princ "\nSuccess!")
; Highlight the newly created intersection solid
(vla-Highlight result :vlax-true)
(vlax-release-object result)
)
)
)
(princ "\nMust select two solids.")
)
(princ)
)