华南理工大学 刘林 麦智晖 阎汉生
1 {/ [. f& n2 q) s K* U* W5 m
8 U" K! x4 Z! c6 ~# F. `2 G& S) s
4 B h) ~! e$ ~本文基于AutoCAD 2006新推出的.NET API为工具,介绍了在.NET平台下对AutoCAD进行二次开发的技术,并与目前常用的VBA、ObjectARX作了对比。同时讨论了如何弥补.NET API某些不足的功能。
( I' d* X+ |: l- r$ j1 @1 J; {9 W; A+ `/ C
5 T" z# i! h0 u' w8 i% @当前AutoCAD的二次开发工具主要有:VisualLisp、VBA和ObjectARX等。其中,VisualLisp与VBA较为简单,特别是VBA,使用方便且开发速度较快,但其功能相比ObjectARX有所不足,尤其是对面向对象的功能支持不好。而ObjectARX基于VC平台,在C++的支持下,其功能非常强大,可以很好地运用各种面向对象技术,但其缺点是发开速度比较慢,同时对开发人员的能力要求较高。4 \6 H8 u' {1 W) ~/ ~" S" x: w
4 O" Z" K& w$ c6 i, l( l1 q4 E
.NET是微软新推出的开发平台,具有众多优点。基于.NET平台对AutoCAD进行二次开发,可充分利用.NET的各种优势,在保证功能强大的前提下大大提高开发速度。
& w, L* q, Z4 g) k, f2 ?, v
6 W# q$ J* b% {" {/ M一、基于.NET的开发2 P& B/ A0 T) R. y: _& L# d- U
8 _ @$ H4 [) b7 G% c7 V/ g K1..NET API简介( b; Z( O o# T: H
A; b) m; e" y! F
在新推出的AutoCAD 2006中,Autodesk为其开发增加了.NET API。.NET API提供了一系列托管的外包类(Managed Wrapper Class),使开发人员可在.NET框架下,使用任何支持.NET的语言,如VB.NET、C# 和Managed C++等对AutoCAD进行二次开发。其优点是完全面向对象,在拥有与C++相匹配的强大功能的同时,具有方便易用的特点,是较理想的AutoCAD二次开发工具。- I: f5 n. `0 ~3 C0 C- W5 h% E
- D& t4 C4 Y, e1 Y& h4 }6 ?+ P2..NET API与传统ObjectARX的主要区别 x# D* n+ z3 P# r$ K
6 ^5 I3 c7 L9 Q3 V8 u.NET API与传统ObjectARX的区别主要源于在.NET环境下开发应用程序与在VC环境下开发应用程序的区别。首先,在VC环境下,程序员需要自己管理内存的申请和释放,而.NET采用了垃圾回收机制,由.NET框架自行判断内存回收的时机并实行回收,从而解决了令C++程序员头痛的内存泄漏问题。也正是由于这个特点,在.NET环境下不能象在C++环境下那样利用析构函数释放其他的资源,需要程序员在程序中显式地释放。在.NET API中,主要通过Dispose来函数进行资源的释放。
6 e, L. p, q6 A! F6 r4 Y: H/ K7 M0 N5 K, ^. i& g+ Y: U
其次,ObjectARX中的各种反应器(Reactor)在.NET API中由外包类映射为各种事件(Event),可通过定义这些事件的响应函数来响应AutoCAD的各种操作。同时对于错误信息的处理也从函数返回值改变为通常异常来处理,使其更好地兼容.NET。由于VB.NET、C#等语言都是完全面向对象的,没有全局函数的概念,所以.NET API将ObjectARX下的全局函数封装为.NET API下的某些对象或对象的属性,如ObjectARX下与用户交互的系列全局函数被封装为CommandLinePrompt类。
' H8 v0 E) U2 [) h Z; b1 U7 T ?5 _; ~, U+ D
3.使用.NET API
4 ]2 L$ D0 H- y$ d+ V* k6 W7 x- [! R
下面以C#为例,在Microsoft Visual C# 2005 Express Edition Beta平台上,先新建一个Class Library项目,再将AutoCAD2005安装目录下的acdbmgb.dll与acmgb.dll作为引用添加进项目中。这两个文件包含了.NET API中所有的外包类。
- F* G# a5 d' X% r. t1 l' I4 `' t- c$ ?* \
然后在要使用.NET API的类中添加以下语句以引用.NET API的命名空间。需要添加的语句如下:& i. Y6 P. W& ?$ p) y3 H3 @
2 ~/ t. y" f4 Q; t% L! R
using Autodesk.AutoCAD.ApplicationServices;
: y% S1 d: s' B5 Uusing Autodesk.AutoCAD.DatabaseServices;
+ ^6 u: U' n# C' B: M0 M+ Y- a1 {$ jusing Autodesk.AutoCAD.Runtime;4 p1 v9 U3 P1 ?! a- u6 h
using Autodesk.AutoCAD.Geometry;' I! Y: z' D- U# [. g2 H" A, `6 v: T
! P5 ?/ o/ X3 J5 {" h9 y9 E# o
这样就可以利用.NET API进行开发了。以下代码可在AutoCAD注册为一个命令"AddLine",该命令可在当前工作空间中添加一条起点为(0,0,0),终点(200,200,0)的直线。代码如下:; n' l# H" X. E2 q
/ p, g T/ i1 u. O8 B) E2 s: K7 P; K[CommandMethod ("AddLine")]
8 c N) Q/ k \- d8 @" i/ _public static void AddLineCmd()/ w% T% b8 Z" ]$ p# `
{
* {, C) s4 C0 S5 k% ~Database db = HostApplicationServices.WorkingDatabase;//获得当前工作空间的数据库
; s0 Z& b! Y- h: h H8 @6 eBlockTable bt = (BlockTable)db.BlockTableId.Open(OpenMode.ForRead); //获得块表
; s( o/ k8 D+ L( H! Q% N" aBlockTableRecord btr = (BlockTableRecord)bt[BlockTableRecord.ModelSpace].Open(OpenMode.ForWrite); //获得模型空间的块表记录
1 j: w: U% j4 d+ l$ D; m5 ?Line line = new Line(new Point3d(0, 0, 0), new Point3d(200, 200, 0));//创建一条直线8 V8 o" _/ {# \7 }9 D+ [: @5 E
try {, d7 V7 a" c6 t" G$ j# O# M6 z( F
btr.AppendEntity(line);//将直线添加到模型空间中+ r3 H6 N( A4 H& r! L- F
line.Close();//关闭该直线
g2 k+ U% ]7 P( `6 `4 l! C0 D; {}# E7 t9 K, z# f& C
finally {4 Q8 Z" i9 C) B5 ~; z s
btr.Close();//关闭块表记录- ^8 m/ K1 {8 a6 T
bt.Close();//关闭块表+ A% F) D" K& u; W( x
}, _7 N5 h; e$ ~3 I7 d# l8 a- Q
}
0 z3 d! t+ O @$ C. x: F! M# J" q! S. u- a* v) L$ c; F$ H
由此可见,上述语句与在VC下的开发非常类似,其过程都是先得到数据库,然后依次打开块表、块表记录,接着添加实体,最后关闭块表、块表记录。值得注意的是finally语句,无论try块中的语句是否发生异常,finally块中的语句都会被执行,从而确保关闭块表和块表记录的操作会被执行。! X" A' Z q; C# p" s
% k$ F3 i0 \4 a( l% M9 @( Q$ ?1 t) r
写完代码后进行编译,编译完成将得到一个dll文件。在AutoCAD 2006中通过"netload"命令即可选择该dll文件进行加载,加载成功后即可以通过"AddLine"命令执行上述代码。遗憾的是目前的.NET API版本还不支持卸载,若要卸载只能关闭AutoCAD。9 L$ v! x) O7 \* ]5 j1 \3 h7 ~
( f A7 d" B, u( U& F9 J6 d6 Y
4..NET API的初始化与清除
9 d+ k" \5 Y; t: N, }- [
5 K' M2 K7 s7 n7 U: C. R在ObjectARX中,"acrxEntryPoint"函数是ARX程序的载入点,程序的初始化和清除均可在该函数中进行。而在.NET API中则首先需要将初始化代码封装在一个类中,同时该类需要压迫实现$ s0 W( z6 ^9 M$ @5 c
6 S2 s. l! @1 t) [1 {* ~
IExtensionApplication接口。该接口包含Initialize与Terminate两个函数。其中Initialize负责加载程序时的初始化操作,Terminate则负责进行卸载程序时的清除操作。代码如下:
0 u* b4 k i8 z" \* {. O; U( Q8 U7 {/ b4 j
namespace ARXExample {
8 M) A! ?. v( y% f/ T8 ?public class MyARX : IExtensionApplication {
& P- k, H. |$ j……
; H; y W4 R3 {2 k8 ypublic void Initialize() {* j! e1 x6 a3 M" |
//初始化操作2 B' f1 G l% f' Z$ A
}( j7 u3 S; A+ l
public void Terminate() {
) J% f' ^) i( j% c, z//清除操作
/ Z. r/ G2 y3 I# h' T0 \1 w7 b}
/ c( J" y1 `1 g6 |9 Q% p……
/ {6 c( E" |5 [/ a2 k} f8 N9 Q6 Q9 L! M" D2 I
}" B& |; C4 U, F! S( L0 S1 S9 A5 Y
# ^# v8 @! \; J% `+ y. c同时,为加快加载速度,可在MyARX.cs的文件头加入以下语句:
0 X9 }+ M# n; i J5 e I: p# W' c' w/ C: L6 d* l8 v9 K
[assembly: ExtensionApplication (typeof (ARXExample.MyARX) ) ]+ J' P& H! a8 G0 _0 \) F
[assembly: CommandClass (typeof (ARXExample.MyARX) ) ]+ a) ]3 d# Y) n# I6 y3 ^4 P
% e& z4 j0 J5 |这样在加载程序时AutoCAD将直接通过MyARX中的Initialize语句进行初始化,同时注册MyARX中的命令。否则,AutoCAD将搜索dll中所有的类以找到实现IExtensionApplication接口的类进行初始化,如找不到则不进行初始化。同样,通过CommandClass属性,AutoCAD也会直接到MyARX类中搜索要注册的命令。当程序中包含的类数目较多时,通过ExtensionApplication和CommandClass这两个属性可显著地加快程序的加载速度。! b1 P+ l5 {( N" _7 |
/ J8 o% N( K) [. K5..NET API与COM交互操作
c) c, |7 `8 Z; s% U6 N
+ M8 k8 J% k5 Y7 F2 I% L在目前的.NET API中,其功能与传统的ObjectARX相比有所不及,有相当的ObjectARX函数目前还没有封装到.NET API中,如GetPoint等。但可以通过COM方式使用ActiveX来弥补.NET API的不足。* \& K- Y+ H C N/ ]$ A# V; k
% I9 g. x- N( r1 t1 Z; s增加了COM引用后,程序就可以使用许多VBA中的功能了。以AutoCAD ActiveX中的事件为例,以下代码可以为当前工作空间中所有的图元添加Modified事件:3 b+ p1 N& T- m7 r3 L1 L# o, j
$ f8 j/ x4 h9 i7 y' z, b
Database db = HostApplicationServices.WorkingDatabase;
' N1 A+ S+ A% O& v$ j% s0 ]; |" ^& |: N3 HBlockTable bt = (BlockTable)db.BlockTableId.Open(OpenMode.ForRead);/ t8 k- ]4 y$ M" Z1 i7 \' S7 S
BlockTableRecord btr = (BlockTableRecord)bt[BlockTableRecord.ModelSpace].Open(OpenMode.ForWrite);
) \" P2 m* g" F- c9 V) F5 F( Otry {
! `6 s. u; O2 P% SAcadObject obj;3 J0 z8 [; `9 C9 @
//遍历块表记录. C3 l8 t: z- r1 T+ C
foreach (ObjectId objId in btr) {
& [ c. {1 o! K//由ObjectId得到ActiveX中的AcadObject对象2 r2 L# {. `3 B7 S5 J" q: u7 s
obj = (AcadObject)((AcadDatabase)db.AcadDatabase).ObjectIdToObject(objId.OldId);# G7 U2 U) g# k% j- B
//为obj添加响应Modified事件
$ N( Z6 a% r! N Mobj.Modified += new IAcadObjectEvents_ModifiedEventHandler(obj_Modified);- L, J, }& ^! G! N/ d
}7 u9 Q7 L- F9 P7 D0 Q
}* g. @ N2 m7 I8 z7 `
finally { h. \: t0 U! A
btr.Close();
7 h8 ^6 K# [' ~) \bt.Close();' }. x2 G# h. `$ A3 P7 J/ v/ G2 c. `
}2 v1 o8 g: x, f5 `5 Q0 z3 Y: R
% A i' Q6 |; f& i% m4 o6 q
其中事件响应函数obj_Modified的表示如下所示:" b* p! W; P4 V4 n8 R/ `. \
+ K" R' @' n; `7 h o3 S
public static void obj_Modified(AcadObject obj) {
) ]3 e. s6 {& N1 u) aCommandLinePrompts.Message("object modified!" + obj.ObjectID + "\n");: h/ q4 r1 G0 p3 A( R$ v8 B
}( B+ t ?9 p9 [2 h! ]
% }9 N& q$ D9 g; P
二、结论
8 y, g. g2 F' |" }) c$ R J/ r! Z4 D: y E$ B
本文以C#为例,对基于.NET API的AutoCAD二次开发作了较详细的介绍。.NET API在具有ObjectARX强大功能的同时具有VBA使用方便易用的优点,同时具有C++的强大功能,是较为理想的开发工具。但目前.NET API在某些方面还有些不足,但随着其版本的更新、完善,定会成为众多开发人员的首选工具。 |