几何尺寸与公差论坛------致力于产品几何量公差标准GD&T (GDT:ASME)|New GPS(ISO)研究/CAD设计/CAM加工/CMM测量

几何尺寸与公差论坛------致力于产品几何量公差标准GD&T (GDT:ASME)|New GPS(ISO)研究/CAD设计/CAM加工/CMM测量 (http://www.dimcax.com/hust/index.php)
-   ObjectARX(AutoLISP) (http://www.dimcax.com/hust/forumdisplay.php?f=178)
-   -   出错处理函数的浅析 (http://www.dimcax.com/hust/showthread.php?t=11657)

yang686526 2009-04-26 05:35 PM

出错处理函数的浅析
 
出错处理函数的浅析
www.dimcax.com
出错处理函数的浅析
出错处理函数的浅析
■■■■ 1 浅析定义出错处理函数的常犯的错误
■■ 1.1 概念及其编程应用的意义

我们知道 *error* 函数是用来执行程序出错时进行相关错误处理
的特殊函数操作。就调用方式而言,大家都很熟悉。所以我们要讨论的
不是技术问题,而是它对我们编程应用的意义。
一个完整的程序应该具备一个周全的出错处理。因此,出错处理函
数我们就会频繁的使用。怎样让我们自定义的出错处理能够应对各种各
样的出错情况呢?
通常我们的出错处理函数会分为三部分来定义:
a. 定义初始化设置,如保存 *error* 的当前值,将自定义的出错函数
赋名为 *error*,放置 undo 的开始标记,保存系统变量信息并设置新
值等等。★这部分通常被放在程序的开始。
b. 定义自己的 *error*,在出错时自动激活该函数。执行必要的 undo
恢复,系统变量恢复,初始化设置中的全局变量恢复以及其他的图形操
作等。
c. 定义正常退出的恢复设置。★这部分通常被放在程序的结束。
基本的格式如下:
;;------------------------------------------------------------
;;例1 这是大多数人的写法
(defun xxx-begin (varlst / cmd)
(setq cmd (getvar "cmdecho"))
(setvar "cmdecho" 0)
(command "_.undo" "_begin");放置undo开始标记
(setvar "cmdecho" cmd)
(setq $savvarlst (mapcar '(lambda (x)
(list x (getvar x))
)
varlst
) ;系统变量保存及设置
$olderr *error* ;保存 *error* 的当前值
*error* xxx-error ;自定义的出错函数赋名为 *error*
)
;...
)
(defun xxx-error (msg)
(princ msg) ;打印出错信息
(xxx-end) ;调用自定义函数 xxx-end
;... ;其他出错处理
)
(defun xxx-end (/ cmd)
(if $savvarlst
(mapcar '(lambda (x)
(apply 'setvar x)
)
$savvarlst
) ;系统变量恢复到初始设置
)
(setq *error* $olderr ;*error* 恢复到初始设置
$savvarlst nil ;全局变量设置为 nil
$olderr nil
)
(setq cmd (setvar "cmdecho"))
(setvar "cmdecho" 0)
(command "_undo" "_end") ;放置 undo 的结束标志
(setvar "cmdecho" cmd)
(princ)
)
;;------------------------------------------------------------
看上去似乎可以了,但在一些特定的情况下还会出错,它仍然不具备通
用性。
我们在下面具体分析。
■■ 1.2 通用性及出错可能性分析
我们在出错处理函数要完成的基本任务,通常包括以下几个要素,
大家可以检验一下自己写的出错函数能否胜任以下要求。
a. 系统变量设置
◇当程序被用户取消或出现错误而中止能否恢复。
◇中途设置的(非在初始时设置的)的系统变量,当程序被用户取消
或出现错误而中止能否恢复。
用上面(例1)的函数举例如下:
;例2---------------------------------------------------
(defun c:test (/ os)
(xxx-begin '("cmdecho" "skpoly" "cecolor"))
(setvar "cmdecho" 0)
(setvar "skpoly" 1)
(setvar "cecolor" "1")
;...
(setq os (getvar "osmode"))
(setvar "osmode" 3333)
;...
;;出错点,在此按 esc
;...
(setvar "osmode" os)
(xxx-end)
)
;-------------------------------------------------------
在运行以上程序时用户按下 "esc" ,相信 "cmdecho" "skpoly"
"cecolor" 能够恢复原值,但 "osmode" 就不行了。
--------------------------------------------------------
问:为什么不在初试时的函数中设置?
答:假如在复杂程序中可能会套嵌调用初始化函数,然 *error*
设置只能有一个。
(defun c:test (/ ttt)
(xxx-begin '("cmdecho" "skpoly" "cecolor"))
...
(defun ttt ()
(xxx-begin '("osmode"))
...
)
...
)
问:为什么不在 xxx-error 中补充定义?
答:补充定义的 xxx-error 只能适用当前的程序。我们要求的是
能够在任意的程序中调用,这就是我讲的“通用性”。
---------------------------------------------------------
其实我们只要把例2中的
(setq os (getvar "osmode"))
(setvar "osmode" 3333)
...
***出错点
...
(setvar "osmode" os)
改成:
(setq $savvarlst
(cons (list "osmode" (getvar "osmode"))
$savvarlst
)
)
(setvar "osmode" 3333)
;...
;;出错点,在此按 esc
;...
就行了,这我们在后面讨论。
b. undo 设置
谈论 undo 设置之前我们可能有必要来了解一下系统变量 undoctl
的相关设置。下面是 autocad 的帮助:
---------------------------------------------------------------
(只读)
类型 整数
保存位置 尚未保存
初始值 21
指示 undo 命令的“自动”、“控制”和“编组”选项的状态。 系统将
使用下列位码值之和将该设置存储为一个位码:
0 undo 关闭
1 undo 打开
2 只能放弃一条命令
4 打开“自动”
8 一个编组处于当前活动状态
16 将缩放和平移操作编组为单个操作
---------------------------------------------------------------
看完上面的帮助,我们来用(例1)中的函数来作测试:
;例3-----------------------------------------------------------
;我们先作如下操作:
(command "_.undo" "_control" "_none")
;然后我们来测试
(xxx-begin '())
;结果返回命令行操作错误信息:"无效的选项关键字。"
;--------------------------------------------------------------
为什么呢?其实错误在于 xxx-begin 函数中的
(command "_.undo" "_begin")
因为我们执行了
(command "_.undo" "_control" "_none")
操作之后 undo 命令的关键字发生了变化,如下:
“输入 undo 控制选项 [全部(a)/无(n)/一个(o)/合并(c)] <全部>:”
所以我们需要根据系统变量 undoctl 的值,而对症下药。然而由于该变量
为只读属性,setvar 对它无效。所以我们必须通过命令行操作来完成相关
设置。
c. 程序出错后的操作
同样以上面的例1的函数为例:
;例4-------------------------------------------------------------
(defun c:test (/ p1 p2 e p3)
(xxx-begin '("osmode"))
(initget 1)
(setq p1 (getpoint "\n指定点1: "))
(initget 1)
(setq p2 (getpoint p1 "\n指定点2: "))
(command "_.line" p1 p2 "")
(setq e (entlast))
(redraw e 3)
(initget 1)
(setq p3 (getpoint p2 "\n指定点3 (或在此按 esc): ")) ;;出错点
(command "_.line" p2 p3 "")
(redraw e 4)
(xxx-end)
)
;---------------------------------------------------------------
正常运行例4的程序第一个对象被高亮显示后被恢复,而中途出错的则无法
恢复。
d. 先选后执行的功能
我们知道当 (= 1 (logand 1 (getvar "pickfirst"))) 返回 t 的情
况下我们可以先选择对象,而后执行命令操作。如命令 move、copy 等。
但是例1的函数在此则无法实现。为什么呢?
原来我们先选后,然后执行 xxx-begin 中的
(command "_.undo" "_begin")
后先选的对象被取消了。
activex 的方法不失为一种好方法:
;---------------------------------------------------------------
;初始时
(setq $acaddoc (vla-get-activedocument (vlax-get-acad-object)))
(vla-startundomark $acaddoc)
;结束时
(if $acaddoc
(progn (vla-endundomark $acaddoc) (setq $acaddoc nil))
)
;---------------------------------------------------------------
上面的方法优劣参半:
优点--是既能够实现先选后执行的功能,又避免了系统变量 cmdecho
的设置。
缺点--它在理论上等价于
(command "_undo" "_begin") 和
(command "_undo" "_end")
也存在我们在例 3 中的问题。
于是我们只能在调用 xxx-begin 开始加上以下代码:
(if (= 1 (logand 1 (getvar "pickfirst")))
(setq ss (ssgetfirst))
)
而在调用 xxx-begin 结束加上以下代码:
(if (apply 'or ss)
(apply 'sssetfirst ss)
)

确实是高手,很难懂,还得好好学习


所有的时间均为北京时间。 现在的时间是 05:40 AM.