华南理工大学 刘林 麦智晖 阎汉生0 c4 p; {7 k' a1 d
, o1 P7 \0 ^: T6 V, I d
' f7 _) d+ e0 e+ ]2 _
本文基于AutoCAD 2006新推出的.NET API为工具,介绍了在.NET平台下对AutoCAD进行二次开发的技术,并与目前常用的VBA、ObjectARX作了对比。同时讨论了如何弥补.NET API某些不足的功能。 : u5 M4 ^' V4 j! T5 @
1 ]9 U7 Z5 @) b, e' o9 ]5 i2 k
6 ~7 Y6 }" z6 c- z% |& V当前AutoCAD的二次开发工具主要有:VisualLisp、VBA和ObjectARX等。其中,VisualLisp与VBA较为简单,特别是VBA,使用方便且开发速度较快,但其功能相比ObjectARX有所不足,尤其是对面向对象的功能支持不好。而ObjectARX基于VC平台,在C++的支持下,其功能非常强大,可以很好地运用各种面向对象技术,但其缺点是发开速度比较慢,同时对开发人员的能力要求较高。5 i/ x7 ~/ D0 x( J9 W
8 h: B5 J) ~. s i* \, r
.NET是微软新推出的开发平台,具有众多优点。基于.NET平台对AutoCAD进行二次开发,可充分利用.NET的各种优势,在保证功能强大的前提下大大提高开发速度。
. `1 M. r8 C5 n* z7 m n; s
! f( [4 ~2 q& U6 ^一、基于.NET的开发& q T2 b2 R* h# |7 g9 l2 w
i- R7 u8 u- ^$ B
1..NET API简介! a4 Z" V. U% o# X/ ^
- J) F, S4 M9 o" I0 Q在新推出的AutoCAD 2006中,Autodesk为其开发增加了.NET API。.NET API提供了一系列托管的外包类(Managed Wrapper Class),使开发人员可在.NET框架下,使用任何支持.NET的语言,如VB.NET、C# 和Managed C++等对AutoCAD进行二次开发。其优点是完全面向对象,在拥有与C++相匹配的强大功能的同时,具有方便易用的特点,是较理想的AutoCAD二次开发工具。; w& \* L% N6 ], c
- y& A |: t3 E" W* [% _
2..NET API与传统ObjectARX的主要区别
. O) d+ B; k `2 l% H2 h. W( Q+ _, m' h3 H" Y# x
.NET API与传统ObjectARX的区别主要源于在.NET环境下开发应用程序与在VC环境下开发应用程序的区别。首先,在VC环境下,程序员需要自己管理内存的申请和释放,而.NET采用了垃圾回收机制,由.NET框架自行判断内存回收的时机并实行回收,从而解决了令C++程序员头痛的内存泄漏问题。也正是由于这个特点,在.NET环境下不能象在C++环境下那样利用析构函数释放其他的资源,需要程序员在程序中显式地释放。在.NET API中,主要通过Dispose来函数进行资源的释放。/ w0 O1 s- l! q# ?$ L
2 E0 ?) Z4 ^+ J$ q其次,ObjectARX中的各种反应器(Reactor)在.NET API中由外包类映射为各种事件(Event),可通过定义这些事件的响应函数来响应AutoCAD的各种操作。同时对于错误信息的处理也从函数返回值改变为通常异常来处理,使其更好地兼容.NET。由于VB.NET、C#等语言都是完全面向对象的,没有全局函数的概念,所以.NET API将ObjectARX下的全局函数封装为.NET API下的某些对象或对象的属性,如ObjectARX下与用户交互的系列全局函数被封装为CommandLinePrompt类。
& h' g0 Q4 {( V+ e; T9 u3 v2 }- x b$ y* W/ Q
3.使用.NET API
: ~/ ~1 m$ f3 r& b
0 @: u) n/ T0 c2 R8 ?4 z( x下面以C#为例,在Microsoft Visual C# 2005 Express Edition Beta平台上,先新建一个Class Library项目,再将AutoCAD2005安装目录下的acdbmgb.dll与acmgb.dll作为引用添加进项目中。这两个文件包含了.NET API中所有的外包类。
- Z& H; \+ r) f- `/ T `+ ~. n' x2 s
然后在要使用.NET API的类中添加以下语句以引用.NET API的命名空间。需要添加的语句如下:( X" d1 w4 A0 _1 \3 F& l$ B
4 r9 C v0 A0 Z' n9 n- o* lusing Autodesk.AutoCAD.ApplicationServices;' f* ` ]2 I9 z- I3 L" O% [8 b
using Autodesk.AutoCAD.DatabaseServices;
; r5 \- K9 U9 H1 n$ g: k- Dusing Autodesk.AutoCAD.Runtime;# ~9 a/ V, M% S$ u0 R
using Autodesk.AutoCAD.Geometry;- ^8 i) z$ [& r6 o! C( D/ W8 ? h
9 o& c& [4 B" ], q0 p这样就可以利用.NET API进行开发了。以下代码可在AutoCAD注册为一个命令"AddLine",该命令可在当前工作空间中添加一条起点为(0,0,0),终点(200,200,0)的直线。代码如下:
+ h+ |7 }3 _0 ?0 O2 E0 n% b9 ` r
1 h/ i' Z! @+ {1 g% F/ k[CommandMethod ("AddLine")]; d* p! Y- i8 u. G# |& r
public static void AddLineCmd(): V+ l0 x) T! v2 z8 G* N
{
% g$ H/ ^& o" J+ z" ?) V- X' ~Database db = HostApplicationServices.WorkingDatabase;//获得当前工作空间的数据库# W0 E) L) C0 W8 ]( a
BlockTable bt = (BlockTable)db.BlockTableId.Open(OpenMode.ForRead); //获得块表5 H' Z) _* o# V4 X8 w
BlockTableRecord btr = (BlockTableRecord)bt[BlockTableRecord.ModelSpace].Open(OpenMode.ForWrite); //获得模型空间的块表记录
/ t P+ w6 Y8 g; NLine line = new Line(new Point3d(0, 0, 0), new Point3d(200, 200, 0));//创建一条直线1 O0 N) q+ I" H6 z% y P7 n! l
try {
$ V& n7 H; B- E8 N" ^6 \) dbtr.AppendEntity(line);//将直线添加到模型空间中7 c( z) M$ D6 h, |
line.Close();//关闭该直线
8 @* {6 R3 b1 A2 r" P$ |7 a}: [0 D7 f& W; ~. B# P- A! M; k# s2 d
finally {
V! i6 t* V. S" rbtr.Close();//关闭块表记录& M# q( o. n4 ]0 x1 X- o2 z* m9 P
bt.Close();//关闭块表& r2 C1 r f6 M# G, ? f8 a
}2 h& K9 Z$ x2 D; i
}
' v7 O6 U7 ~' i( ~2 |, u
O& m( m: g/ s由此可见,上述语句与在VC下的开发非常类似,其过程都是先得到数据库,然后依次打开块表、块表记录,接着添加实体,最后关闭块表、块表记录。值得注意的是finally语句,无论try块中的语句是否发生异常,finally块中的语句都会被执行,从而确保关闭块表和块表记录的操作会被执行。
8 z# K& _$ D- }9 C7 ]0 [
$ y: @4 ~8 D- I) h写完代码后进行编译,编译完成将得到一个dll文件。在AutoCAD 2006中通过"netload"命令即可选择该dll文件进行加载,加载成功后即可以通过"AddLine"命令执行上述代码。遗憾的是目前的.NET API版本还不支持卸载,若要卸载只能关闭AutoCAD。 \% J% s, d \+ k. o. D8 W
% }3 h: @" W% |# r- e* e' }4..NET API的初始化与清除* b) `6 p7 b4 ^, }& q: T! @
- M, n% c; \& W ?$ e' S8 {: p6 h在ObjectARX中,"acrxEntryPoint"函数是ARX程序的载入点,程序的初始化和清除均可在该函数中进行。而在.NET API中则首先需要将初始化代码封装在一个类中,同时该类需要压迫实现4 M; r4 m7 w1 g9 a" f* [
8 B$ G0 |! d" D6 \8 l0 M6 R p! hIExtensionApplication接口。该接口包含Initialize与Terminate两个函数。其中Initialize负责加载程序时的初始化操作,Terminate则负责进行卸载程序时的清除操作。代码如下:
+ s, U' Y' j/ o9 }% | [) L) b- I! E* \" T2 Q- R: R1 Q
namespace ARXExample {' Y! P8 }( ~8 [
public class MyARX : IExtensionApplication {3 U. \! l! Z6 Q! x, x1 Q
……
D4 `# w3 J1 G9 m) Hpublic void Initialize() { x6 B3 X/ r# g+ O
//初始化操作
" o5 s5 _9 {& R9 [7 B/ O}! }& X- z9 T/ a" q T" x2 y
public void Terminate() {, m' }9 z/ U5 L# f" a& ~; i
//清除操作
) I; l( b% O! ]5 J2 }( L}
$ y0 w, I y' R( _2 H! E……
0 A; n" ~+ A' ^" F}" ?8 b, I# H& Q3 k3 ^9 e: y5 a
}& y* l0 `9 i) h5 c0 b9 }7 L6 s
- H0 `$ Z6 [. V6 f8 v$ h+ v8 Q同时,为加快加载速度,可在MyARX.cs的文件头加入以下语句:
) X& y% n3 Y" K# G) }
+ z { X: G8 B- M2 z[assembly: ExtensionApplication (typeof (ARXExample.MyARX) ) ]: m, ` L2 U T0 e
[assembly: CommandClass (typeof (ARXExample.MyARX) ) ]- b! m; \ a+ {3 \1 z
% o3 ~" c3 q3 W8 [2 P4 j" _
这样在加载程序时AutoCAD将直接通过MyARX中的Initialize语句进行初始化,同时注册MyARX中的命令。否则,AutoCAD将搜索dll中所有的类以找到实现IExtensionApplication接口的类进行初始化,如找不到则不进行初始化。同样,通过CommandClass属性,AutoCAD也会直接到MyARX类中搜索要注册的命令。当程序中包含的类数目较多时,通过ExtensionApplication和CommandClass这两个属性可显著地加快程序的加载速度。
! @. A* ]0 N% G' l0 u$ k. h2 Z' N& q2 P N; y1 F
5..NET API与COM交互操作
4 @! f' k/ V6 Q' l. N, d; |, _' l
( z" G* p& g4 y, ^' ]+ R在目前的.NET API中,其功能与传统的ObjectARX相比有所不及,有相当的ObjectARX函数目前还没有封装到.NET API中,如GetPoint等。但可以通过COM方式使用ActiveX来弥补.NET API的不足。* }$ G2 n/ Y- R! ]6 \! B
" S7 v7 u( X3 Q# k% L
增加了COM引用后,程序就可以使用许多VBA中的功能了。以AutoCAD ActiveX中的事件为例,以下代码可以为当前工作空间中所有的图元添加Modified事件:
3 `3 g7 L3 c( a1 D
( E# U+ l% s. S- I, x5 ZDatabase db = HostApplicationServices.WorkingDatabase;
5 I) F3 ?2 r0 gBlockTable bt = (BlockTable)db.BlockTableId.Open(OpenMode.ForRead);
: h8 E, b; T, oBlockTableRecord btr = (BlockTableRecord)bt[BlockTableRecord.ModelSpace].Open(OpenMode.ForWrite);; x9 b, ~" L2 P5 E. B; Y
try {. q9 V; t2 ]" r. L' _) P$ O
AcadObject obj;
" d4 N7 G! U" @ U- v6 N E//遍历块表记录( i1 Q W' z \
foreach (ObjectId objId in btr) {6 _0 L& H- Y' H- P0 l
//由ObjectId得到ActiveX中的AcadObject对象
9 y3 r1 k& v! d) w2 G( [! z0 h# _obj = (AcadObject)((AcadDatabase)db.AcadDatabase).ObjectIdToObject(objId.OldId);
) i% f j6 w. E//为obj添加响应Modified事件
, q9 s. j6 Z; V4 K$ e7 n! |obj.Modified += new IAcadObjectEvents_ModifiedEventHandler(obj_Modified);
9 ], X5 R5 U5 J7 J: O% s8 N}5 r. s) p1 C; V, M8 F; |
}
) i& H5 R6 ^6 Y. L1 v! H' ]9 Afinally {7 `$ m/ @4 F! y& b& u% `- x9 y; I
btr.Close();& c& } S( g( N5 T
bt.Close();
, G7 w+ q x6 t& a( h}
4 d$ ~$ {$ `( _& z) T. V! S7 n' p2 c$ n0 h2 a
其中事件响应函数obj_Modified的表示如下所示:
2 z$ r( C) s- c+ `) i0 E/ g$ o
- T# X% s( o6 D& epublic static void obj_Modified(AcadObject obj) {1 t$ C8 B7 w# q" {$ B' ^) f
CommandLinePrompts.Message("object modified!" + obj.ObjectID + "\n");, F$ U; a1 ?' D5 m* u: f, z) Q
}: k& f8 Z( r+ T1 |) q
% |) R! `4 B Z& r' W, u/ @$ y; `+ S7 b
二、结论# k/ r5 t- S H3 K, C
% t: h( Z; e6 p2 q1 B X
本文以C#为例,对基于.NET API的AutoCAD二次开发作了较详细的介绍。.NET API在具有ObjectARX强大功能的同时具有VBA使用方便易用的优点,同时具有C++的强大功能,是较为理想的开发工具。但目前.NET API在某些方面还有些不足,但随着其版本的更新、完善,定会成为众多开发人员的首选工具。 |