AutoCAD_ads详解 - _银子

时间:2024-03-03 13:20:40

3.1   ADS的历史
当Autodesk发布基于OS/2操作系统的AutoCAD R10时,推出了一种全新的称为AutoCAD开发系统(ADS)的编程环境。ADS是一个带有Autodesk提供的库文件和头文件的C语言编程环境。随着AutoCAD R11的发布,AutoCAD的DOS版本用户也可以使用ADS了。在AutoCAD R11之前,大多数的应用程序
都是用AutoLISP开发的,当出售开发的第三方应用程序时,无法避免用户读取你的源代码。有一些应用程序可用来加密LISP代码—其思想由Kelvinator提出。然而,随着开发者队伍的日益强大,又有了一些可以解密源代码的应用程序。我个人从未使用过Kelvinator程序,总之,我认为如
果能领会我编写的代码,你就有足够能力编写AutoLISP应用程序。
所以后来出现了ADS,也就有了一种编译应用程序的手段。AutoCAD ADS应用程序呈现两种模式:实模式和保护模式。实模式的应用程序工作在DOS的640K常规内存范围内,而保护模式的应用程序能借助DOS的扩展内存打破640K的限制。开发ADS应用程序可以使用多种编译器。在实模式环境,
可以使用Borland的Turbo C或Microsoft C 5.1;而在保护模式环境,可以使用MetaWare High C、Zortech C++或Watcom C。那时保护模式的编译器比较昂贵。我研究后决定使用Watcom的产品。
既然现在有了ObjectARX,为什么还必须了解ADSRX呢?因为AutoCAD提供的ObjectARX中包含了ADS函数库,并称之为ADSRX。令人高兴的是我们不必精通ADSRX的各个方面。ADSRX主要用于下列三个方面:
ナ获取用户输入。
ナAutoCAD的选择集。
ナDCL类型的对话框,详见第6章。
利用ADSRX把已有的ADS应用程序转换成ObjectARX应用程序是比较容易的。如果你是AutoCAD编程的初学者,就直接学习ObjectARX的方法来进行程序开发。随着AutoCAD版本的提高和ObjectARX的升级,ADS最终将被淘汰。在AutoCAD/ObjectARX 2000中,形如ads_XXX的函数名大部分已被新的
函数名替换。在函数名发生变化的地方,我将以如下形式表示新老函数名:
新的ObjectARX 2000函数名[老的ads_XXX函数名]
我知道许多读者已经积累了大量的程序,很高兴告诉你ObjectARX 2000提供了一个名为migrtion.h的文件。如果你在工程文件中包含了该文件,这种新老函数名的转换将会自动进行,所以不必为此担忧。请注意在AutoCAD的下一个版本中,将不再提供migrtion.h文件。这仅是协助将ADS代
码转换为ObjectARX 2000的一个临时措施。
3.2   ADS中定义的变量、类型和常数值
有四个ADS使用的头文件,其文件名分别为adslib.h、ads.h、adscodes.h和adsdlg.h。头文件adsdlg.h用于DCL (对话框控制语言) 类型的对话框,这是第6章的主题。ObjectARX 2000提供了大量的数据类型(这些数据类型也适用于AutoCAD R14开发的ObjectARX 2.02)。然而,仍然使用着一
些老的ADS(ADSRX)数据类型。在应用程序中,当使用#include 时,应用程序将自动为你包含ads.h和adscodes.h文件。在ads.h文件中,许多类型定义说明如下。
1. 实数
在AutoCAD中,实数是双精度浮点数值,在ads.h文件中定义如下:
当在AutoCAD中使用双精度数时,可使用ads_real。
2. 点
在AutoCAD中,点就是X、Y和Z的位置,在ads.h文件中定义为数组类型,如下所示:
一个ads_point点就是一个具有三个ads_real数值的数组。在处理点的数值和点的计算时,在ads.h文件中很容易找到如下定义:
在下面的代码段中,我们定义了两个点变量pt1和pt2。表明了怎样初始化一个点,怎样使一个点与另一个点相等。
为了方便,AutoCAD在ads.h文件中提供了一个名为acdbPointSet()的复制宏。为了在应用程序中使用acdbPointSet()宏,必须在应用程序中包含string.h文件。
下面的代码段使点to与点from相等:
3. 转换矩阵类型
在这里我只想谈论一下转换矩阵类型的定义—在讨论选择集时再详细论述转换矩阵。一个转换矩阵就是一个具有ads_real实数值的4×4阶数组,在ads.h文件中定义如下:

 

4. 有用的常数值
在adslib.h文件中,可以找到如下有用的定义为你提供方便。
另一个有用的定义是PAUSE符,这是一个包含单个反斜线符号的字符串,用于acedCommand()和acedCmd()函数(后面讨论)。在定制菜单宏时,反斜线符号代表一个停顿点,允许用户用命令序列输入信息。
5. 结果缓冲区和类型码
虽然结果缓冲区(resbuf)在ObjectARX中仍然使用,但其使用本意已发生了改变。下面首先说明什么是结果缓冲区,然后说明怎样在ADS中使用结果缓冲区,最后说明怎样在ObjectARX中使用结果缓冲区。结果缓冲区结构resbuf在ads.h文件中定义,其中包含的联合体ads_u_val考虑了在Auto
CAD和ADS中使用的各种数据类型。
联合体ads_u_val如下:
结构resbuf如下:
利用此结构的rbnext字段可以把多个结果缓冲区链接成一单链表。当rbnext等于NULL时,表示到达了链表的结尾。通常定义二个 resbuf 指针,一个指向链表的开头,另一个用来遍历链表。restype字段用来表示在联合体的 resval 字段中存储的数据类型。restype字段中包含有在adscode
s.h文件中定义的许多预定义“结果类型码”中的一个,如表3-1所示。
表3-1   结果类型码
结果类型码 说      明
RTNONE 无结果值
RTREAL 实数(浮点数)值
RTPOINT 二维点(X和Y;Z==0.0)
RTSHORT 短整数(16位)
RTANG 角度
RTSTR 字符串
RTENAME 实体名
RTPICKS 选择集名
(续)
结果类型码 说      明
RTORINT 方位
RT3DPOINT 三维点(X、Y和Z)
RTLONG 长整数(32位)
RTVOID 无值(空)符号
RTLB 表开头(用于嵌套表)
RTLE 表结尾(用于嵌套表)
RTDOTE 点(用于点对)
RTT AutoLISP真值(true)
RTNIL AutoLISP无值(nil)
RTDXF0 DXF组码零(仅用于acutBuildList()函数)
现在我们来讨论怎样在ADS中使用结果缓冲区表。在ADS中访问实体时,我们先使用acedEntSel()函数来选择实体,然后再使用acdbEntGet()函数来获得实体的数据。后面我们将讨论acedEntSel()函数。ADS函数acdbEntGet()返回一个结果缓冲区链表。随后我们遍历该结果缓冲区链表,以确
定我们要处理什么类型的实体,该实体具有什么样的特性。
图3-1   结果缓冲区链表
在ADS中(在AutoLISP中也一样)要访问实体的必要条件之一就是要知道并理解DXF组码。如图3-1所示的结果缓冲区链表表示在“0”层上有一个“圆(Circle)”实体,圆心为(5,5,0),半径为2.24086,平放在WCS的XY平面。假定我们有两个指向结果缓冲区的指针rb1和tb1如下:
在经过acedEntSel()和acdbEntGet()操作后,*rb1将指向一结果缓冲区。
以下面的方式遍历链表,往下移动指针*tb1。
假定我们要指定一个ads_real类型的变量rad存放图3-1所示的圆半径,我们就要检查结果缓冲区的restype字段以确认其值为40(圆半径的DXF组码为40)。
稍后我们将编写一个应用程序来画出这个圆,并对其进行修改。这样,你将获得在ADS中使用结果缓冲区的感受。ObjectARX也使用结果缓冲区,但与ADS是完全不同的。在ADS中结果缓冲区用于访问实体、符号表、浏览器,也用于acedCommand()、acedCmd()和acutBuildList()函数,所有这
些将在后面讨论。我不想在ObjectARX不再适用的结果缓冲区方面钻得太深,因为ObjectARX有比这好得多的机制来处理实体的访问、修改与创建以及对符号表的访问。只不过要知道在ObjectARX中仍将处理结果缓冲区。在ObjectARX中,所有的实体都是类,每一个类有查询和编辑函数,用
这些函数可以对实体进行操作。对于符号表的访问,ObjectARX有符号表浏览器类,它允许在不使用结果缓冲区的情况下遍历整个符号表,这也是为什么我不想在ADS的这一方面深究的部分原因。
6. ADSRX函数的返回类型
在开始讨论acedCommand()、acedCmd()和acutBuildList()函数之前,必须谈论一下ADSRX库函数的返回类型。ADS使用表3-2所示的返回类型码表示调用ADS库函数的成功、失败或特殊情况(如用户取消),这些返回类型码在adscodes.h文件中定义。
表3-2   库函数返回类型码
返回类型码 说      明
RTNORM 用户输入值有效,正常
RTERROR 函数调用失败
RTCAN 用户按了Esc键
RTREJ AutoCAD拒绝无效请求
RTFAIL AutoLISP通信失败
RTKWORD 用户输入键盘或任意文字
并非所有的ADSRX库函数都返回这些状态码,某些库函数直接返回数值。我们最感兴趣的返回类型码是RTNORM、RTERROR、RTCAN和RTKWORD。
3.3   ADSRX中的AutoCAD命令
1. acedCommand()[ads_command()]
现在让我们把注意力转向acedCommand()函数。其定义如下:
acedCommand()函数的参数表是可变长的。acedCommand()函数的参数是成对处理的。每对参数中的第一个表示参数的类型,第二个表示其实际的数据。参数表中的最后一个必须是单个参数,其值为0或RTNONE。每个表示参数类型的参数值必须为表3-1列出的在adscodes.h文件中定义的结果
类型码中的一个(例如RTPOINT)。acedCommand()函数可执行任一AutoCAD命令,就像在键盘输入命令一样。为画出图3-1所示结果缓冲区链表列举的圆,可执行acedCommand()函数,如下所示:
使用单个0也可以结束参数表,但使用RTNONE更好些。圆心是一个点,半径是一个实数,但为什么使用RTSTR类型呢?如果考虑输入的值,它们都用双引号,就像在命令提示下输入命令一样,是字符型。也可按如下方式调用该函数:
在这段代码中,注意我们是借助返回码符号RTNORM来判断ADSRX库函数acedCommand()是调用成功还是调用失败。而acedCmd()和acutBuildList()函数是用在哪里呢?
2. acedCmd()[ads_cmd()]
acedCmd()函数的定义是:
注意此函数有一个resbuf指针类型的参数。该 resbuf 指针从何而来?它来自acutBuildList()函数。利用acedCommand()函数,实质上是为要执行的命令构造一个 resbuf 结构,只是在编译时就知道参数的值。利用acedCmd()函数,我们可以构造一个运行时的 resbuf 结构,在运行时把参
数传递到acedCmd()函数。下面我们来关注一下acutBuildList()函数。
3. acutBuildList()[ads_buildlist()]
acutBuildList()函数的定义如下:
acutBuildList()函数创建一个结果缓冲区链表。注意acutBuildList()函数返回一个指向resbuf 结构的指针,如果调用失败则返回NULL。上面提到并非所有的ADSRX函数都返回表3-2列出的类型码,这是其中之一。该函数像acedCommand()函数一样,其参数表是可变长的。下面我们来看一
看用acedCmd()函数创建一直线实体。
4. acutRelRb()[ads_relrb()]
观察上面代码段的最后部分,我们使用了一个新的函数,其名为acutRelRb()。该函数用来释放结果缓冲区,其定义如下:
acutRelRb()函数释放分配给结果缓冲区或结果缓冲区链表的内存。ADS函数acutBuildList()为结果缓冲区链表分配内存,直到我们释放该内存。如果acutRelRb()函数调用成功,则返回RTNORM(尽管我未做过测试)。有时只需要单个结果缓冲区,而非结果缓冲区链表。ADS函数acutNewRb()
可方便地用于这种情况,稍后我们将看到其应用的例子。
5. acutNewRb()[ads_newrb()]
acutNewRb()函数的定义为:
acutNewRb()函数分配一新的结果缓冲区,并设置restype字段为v。acutNewRb()函数返回一个指向新分配的结果缓冲区的指针。参数v应为在adscodes.h文件中定义的结果类型码中的一个(例如RTPOINT)。别忘记调用acutRelRb()函数释放用acutNewRb()函数分配的内存。
3.4   向用户发送信息
向用户发送信息由下列三个ADS函数来实现:acutPrompt()、acutPrintf()和acedAlert()。
1. acutPrompt()[ads_prompt]
acutPrompt()函数在命令行显示一段信息,其定义如下:
如果acutPrompt()函数调用成功返回RTNORM,否则返回一错误码。函数的参数是一指向字符串常量的指针,函数不能改变字符串的内容。
执行上述语句,在命令行将提示:
2. acutPrintf()[ads_printf()]
acutPrintf()函数的定义如下:
acutPrintf()函数等同于标准C库函数printf(),只是acutPrintf()函数在文本屏幕显示输出。acutPrintf()函数在向AutoCAD发送字符串之前,把要输出的字符串保存在一内部缓冲区中。该缓冲区含有133个字节,所以,调用acutPrintf()函数显示的信息不能超过132个字符。acutPrintf(
)函数使用与C语言中的printf()函数完全相同的扫描码,如下例所示:
执行的结果是在文本屏幕或命令行输出如下提示:
如果acutPrintf()函数调用成功返回RTNORM,否则返回一错误码。
3. acedAlert()[ads_alert()]
acedAlert()函数显示一个带有错误或警告信息的警告框,其定义如下:
该函数显示一个带有错误或警告信息的警告框,其错误或警告信息由参数str传递。警告框就是具有一个OK按钮的对话框。如果要显示多行信息,可以在字符串中包含换行字符(用‘ ’表示)。字符串最多可含132个字符(第133个字符保留用作字符串结束符号)。
如果acedAlert()函数调用成功返回RTNORM,否则返回RTERROR。
3.5   应用程序实例CH3_1
本章的所有应用程序实例均由ObjectARX 2000向导(Wizard)建立。ObjectARX 2000向导把所有的用户定义的命令放在一个名为Commands.cpp的文件中。在该应用程序实例中,示范了各种ADSRX数据类型和下列ADS函数的应用:

在实例中,还示范了怎样遍历一个结果缓冲区链表。在本应用程序实例中,有许多函数还未谈及,但在本章稍后我们将会进行讨论。这些函数如下所示:
我们首先列出应用程序,然后讨论其要点。Ch3_1.cpp文件的内容如下:
用户定义的命令RBCIRC放在Ch3_1Commands.cpp文件中,其内容如下:
3.6   应用程序实例CH3_1要点分析
本应用程序有一个必需的入口点函数acrxEntryPoint()。在AutoCAD启动过程中(如果acad.rx文件中有此应用程序文件名)或当用户使用ARX命令加载此应用程序时,入口点函数acrxEntryPoint()就调用用户定义的initApplication()函数。上述任何一种情况应用程序均将接收到AcRx::kInit
AppMsg消息。initApplication()函数调用用户定义的AddCommand()函数,后者负责acedRegCmds->addCommand()函数的调用。这样就把我们要定义的命令RBCIRC(Result Buffer CIRCle)添加到了命令栈。当终止AutoCAD的运行或者用户使用ARX命令卸载此应用程序时,应用程序会接收到一
条AcRx::kUnloadAppMsg消息,此消息促使应用程序调用unloadApplication()函数。unloadApplication()函数通过对acedRegCmds->removeGroup()函数的调用从命令栈中移去我们定义的命令组。如果你对这部分内容还有点模糊,请参阅第1章,在那里我们详细讨论了开始和运行ARX应用程
序的机制。
我们增加了两个用户定义的函数:rbcirc()和printEntInfo()。rbcirc()函数负责画出图3-1所示结果缓冲区描述的圆。printEntInfo()函数负责遍历结果缓冲区并显示所画圆的信息。在rbcirc()函数中,圆是通过调用acedCommand()函数创建的,如下所示:
注意,ads_point类型的变量cp声明和初始化是一次进行的:
也可以这样来声明和初始化cp:
下一个函数是acdbEntLast,稍后我们将做详细讨论。它捕捉由AutoCAD画出的最近一个实体,并把其ads_name名保存在circEnt中。我们在调用遍历结果缓冲区链表的printEntInfo()函数时使用circEnt。在rbcirc()函数的while循环中,会询问用户是否要再画一个圆实体。许多用户输入函
数我们还未谈及,后面将会介绍。请注意调用acutBuildList()函数构造结果缓冲区链表的地方,我们依次把它传给acedCmd()函数以画出圆实体。