华南理工大学 刘林 麦智晖 阎汉生8 ]7 k8 y+ _8 h& V8 p# P7 s
. j* a) A& K7 ?+ w8 q3 C7 y" G
# g5 x6 B7 _( o% c2 J本文基于AutoCAD 2006新推出的.NET API为工具,介绍了在.NET平台下对AutoCAD进行二次开发的技术,并与目前常用的VBA、ObjectARX作了对比。同时讨论了如何弥补.NET API某些不足的功能。
7 b" ] s n" z K( r2 m" Q+ E, Z# U" t! A, W! ~. W
6 U0 ^5 i! ~, f! f3 W当前AutoCAD的二次开发工具主要有:VisualLisp、VBA和ObjectARX等。其中,VisualLisp与VBA较为简单,特别是VBA,使用方便且开发速度较快,但其功能相比ObjectARX有所不足,尤其是对面向对象的功能支持不好。而ObjectARX基于VC平台,在C++的支持下,其功能非常强大,可以很好地运用各种面向对象技术,但其缺点是发开速度比较慢,同时对开发人员的能力要求较高。 B" b1 [" J1 I) P. ]
2 t% h0 x# c. K" t2 `% I* C5 z.NET是微软新推出的开发平台,具有众多优点。基于.NET平台对AutoCAD进行二次开发,可充分利用.NET的各种优势,在保证功能强大的前提下大大提高开发速度。/ m# w& K% p# t
! Y* s: Z/ a7 ]" j% J; z* W t6 |
一、基于.NET的开发: J* a. w1 W% Z+ t; N
+ ?; H7 U8 a5 s& S
1..NET API简介2 X4 q2 a' U! C$ O) n; {
3 c6 v3 P3 i, y0 b/ F6 D$ P6 D, u3 x3 e在新推出的AutoCAD 2006中,Autodesk为其开发增加了.NET API。.NET API提供了一系列托管的外包类(Managed Wrapper Class),使开发人员可在.NET框架下,使用任何支持.NET的语言,如VB.NET、C# 和Managed C++等对AutoCAD进行二次开发。其优点是完全面向对象,在拥有与C++相匹配的强大功能的同时,具有方便易用的特点,是较理想的AutoCAD二次开发工具。2 j. D9 W; C& A; Y, _
. a0 K: G( F& M- e5 R7 K4 b: f! g
2..NET API与传统ObjectARX的主要区别
% _; ?' D: T+ D6 q0 u9 t+ {8 P4 v+ o" o; C$ ?4 }. m4 C
.NET API与传统ObjectARX的区别主要源于在.NET环境下开发应用程序与在VC环境下开发应用程序的区别。首先,在VC环境下,程序员需要自己管理内存的申请和释放,而.NET采用了垃圾回收机制,由.NET框架自行判断内存回收的时机并实行回收,从而解决了令C++程序员头痛的内存泄漏问题。也正是由于这个特点,在.NET环境下不能象在C++环境下那样利用析构函数释放其他的资源,需要程序员在程序中显式地释放。在.NET API中,主要通过Dispose来函数进行资源的释放。
1 q/ a( S. R3 o' o4 a
7 ^/ G8 G' i/ C0 U6 w) t- l; r其次,ObjectARX中的各种反应器(Reactor)在.NET API中由外包类映射为各种事件(Event),可通过定义这些事件的响应函数来响应AutoCAD的各种操作。同时对于错误信息的处理也从函数返回值改变为通常异常来处理,使其更好地兼容.NET。由于VB.NET、C#等语言都是完全面向对象的,没有全局函数的概念,所以.NET API将ObjectARX下的全局函数封装为.NET API下的某些对象或对象的属性,如ObjectARX下与用户交互的系列全局函数被封装为CommandLinePrompt类。
, Y8 K$ }4 d' D7 B" K
/ M n' ~- `: @6 _3 X v N3.使用.NET API
% {! [( A! }% D9 }9 M9 L$ R0 h) n; G% i+ y" r, c& s1 ~. F0 L
下面以C#为例,在Microsoft Visual C# 2005 Express Edition Beta平台上,先新建一个Class Library项目,再将AutoCAD2005安装目录下的acdbmgb.dll与acmgb.dll作为引用添加进项目中。这两个文件包含了.NET API中所有的外包类。
6 i9 h0 E* s) u1 e8 I, p; V/ [7 j! _) A Y' k2 c. c
然后在要使用.NET API的类中添加以下语句以引用.NET API的命名空间。需要添加的语句如下:0 }) F% m# ~. z. X
9 b. R- I. a& h
using Autodesk.AutoCAD.ApplicationServices;
) k8 J2 Z) r: s4 r R1 Y; d# h. ]using Autodesk.AutoCAD.DatabaseServices;6 n) t: f& {7 Q9 D, h6 w' R) b$ k
using Autodesk.AutoCAD.Runtime;8 [ _. F9 S7 V! e
using Autodesk.AutoCAD.Geometry;4 d/ z- F1 @" t1 m
( R* c; D; `* n0 ]这样就可以利用.NET API进行开发了。以下代码可在AutoCAD注册为一个命令"AddLine",该命令可在当前工作空间中添加一条起点为(0,0,0),终点(200,200,0)的直线。代码如下:( e3 ?) c& D8 O+ _& f
; ~8 q6 T# p" B4 E6 S. V& W[CommandMethod ("AddLine")]" H, ?2 z* Q) I8 y {
public static void AddLineCmd()
2 j* D9 R" S/ Y G4 l5 d, d{
( b- g$ Y" ]5 p& y% K/ oDatabase db = HostApplicationServices.WorkingDatabase;//获得当前工作空间的数据库
* [+ {, b& s7 ~3 m, R% yBlockTable bt = (BlockTable)db.BlockTableId.Open(OpenMode.ForRead); //获得块表
! h2 N: H6 T! o7 ~) NBlockTableRecord btr = (BlockTableRecord)bt[BlockTableRecord.ModelSpace].Open(OpenMode.ForWrite); //获得模型空间的块表记录/ h" v5 {+ z' U. W* ^! C' I- C' G
Line line = new Line(new Point3d(0, 0, 0), new Point3d(200, 200, 0));//创建一条直线0 K& b+ j$ b u( ~8 b
try {
1 p% r3 ~% x% d6 p; Lbtr.AppendEntity(line);//将直线添加到模型空间中
9 z6 D6 }6 R# g7 W2 {' ^- q. Pline.Close();//关闭该直线
8 v& T* \0 H6 ?}
6 c# ^0 G- M* S# ffinally {
0 w- y8 J% K7 @% }: ~3 p) c' Q" Jbtr.Close();//关闭块表记录, I* e! j) F6 ?! Q6 O$ N
bt.Close();//关闭块表) p# x) H( ~$ a5 L1 ~& x* }" Y9 j
}
8 N% i' Y7 w8 Q9 p7 j/ a}% e+ t a- ?; ~! [
2 {" x* O/ L4 p) H; P/ k由此可见,上述语句与在VC下的开发非常类似,其过程都是先得到数据库,然后依次打开块表、块表记录,接着添加实体,最后关闭块表、块表记录。值得注意的是finally语句,无论try块中的语句是否发生异常,finally块中的语句都会被执行,从而确保关闭块表和块表记录的操作会被执行。# B6 v/ z. q( y: _
& A4 z5 G; j9 | o写完代码后进行编译,编译完成将得到一个dll文件。在AutoCAD 2006中通过"netload"命令即可选择该dll文件进行加载,加载成功后即可以通过"AddLine"命令执行上述代码。遗憾的是目前的.NET API版本还不支持卸载,若要卸载只能关闭AutoCAD。
- e: }8 o! ?3 V' V' I* w J" m3 D U, d* v/ v6 N
4..NET API的初始化与清除
: [* B7 i8 U1 T( F6 @4 S, f/ s3 x$ K6 b( P. n# j9 i$ Z
在ObjectARX中,"acrxEntryPoint"函数是ARX程序的载入点,程序的初始化和清除均可在该函数中进行。而在.NET API中则首先需要将初始化代码封装在一个类中,同时该类需要压迫实现2 M- |. f) W4 T: R/ W
) D& _8 n, N$ [9 w: q( C0 Y% r$ h$ }
IExtensionApplication接口。该接口包含Initialize与Terminate两个函数。其中Initialize负责加载程序时的初始化操作,Terminate则负责进行卸载程序时的清除操作。代码如下:
6 F9 P; O( M7 ]: J o5 ]! i4 k% l' R P" X
namespace ARXExample {
F( Q% B& R) o' ppublic class MyARX : IExtensionApplication {
+ g6 ^9 y& F6 T$ ]$ y' q, h+ e- b……6 s) Z. W! V7 p
public void Initialize() {
" R0 G9 u0 I3 j2 T/ j//初始化操作
9 k1 R1 N$ X% U/ k" ^1 V}
8 w: N6 f, P3 ~4 K& a7 vpublic void Terminate() { m1 `6 L. h0 t/ P
//清除操作
0 Z8 N9 M7 \! T. P$ H. _}5 g1 W% Z# b9 W
……
- O* c# E* y e. j! C# g! ~/ v}( Y2 O; t4 g, r# e* N3 [4 F
}: m% ^9 D& t0 D8 n) j3 ^( ?
% P# J5 }' O/ M" W% C8 ~1 S4 f' \同时,为加快加载速度,可在MyARX.cs的文件头加入以下语句:
+ K% @6 o1 A+ L( G8 E* ]: \& y+ f' ]+ T$ Y
[assembly: ExtensionApplication (typeof (ARXExample.MyARX) ) ]' W: V7 y2 Y5 `$ X! o
[assembly: CommandClass (typeof (ARXExample.MyARX) ) ], O# `4 u: K# l8 a+ |5 g6 X
, ^" {7 E' c) X1 k7 c" Y& l' Q! ]& ?这样在加载程序时AutoCAD将直接通过MyARX中的Initialize语句进行初始化,同时注册MyARX中的命令。否则,AutoCAD将搜索dll中所有的类以找到实现IExtensionApplication接口的类进行初始化,如找不到则不进行初始化。同样,通过CommandClass属性,AutoCAD也会直接到MyARX类中搜索要注册的命令。当程序中包含的类数目较多时,通过ExtensionApplication和CommandClass这两个属性可显著地加快程序的加载速度。
7 P+ d: |. O# E
I& B8 ]6 S" k1 U8 s/ B f j5..NET API与COM交互操作
3 b! `! d0 z( L0 H/ D7 X* h5 V {* I
在目前的.NET API中,其功能与传统的ObjectARX相比有所不及,有相当的ObjectARX函数目前还没有封装到.NET API中,如GetPoint等。但可以通过COM方式使用ActiveX来弥补.NET API的不足。
) O- D4 Y/ L0 A+ Y, c4 D4 m& [; U
0 j5 Z$ g2 l- ^- g" C增加了COM引用后,程序就可以使用许多VBA中的功能了。以AutoCAD ActiveX中的事件为例,以下代码可以为当前工作空间中所有的图元添加Modified事件:4 E9 W" \4 O7 O. r3 L6 L
. { m K; A& m2 }8 R" x: d4 L
Database db = HostApplicationServices.WorkingDatabase;3 \$ d) ?: Q9 B, ?+ M
BlockTable bt = (BlockTable)db.BlockTableId.Open(OpenMode.ForRead);
4 H: v; ?6 t9 H# BBlockTableRecord btr = (BlockTableRecord)bt[BlockTableRecord.ModelSpace].Open(OpenMode.ForWrite);
+ n7 C; R( U @: l; A2 H a" otry {
0 C4 ?$ ~& s4 R# e; J8 j8 i- CAcadObject obj;. X5 \( y5 t. E. A6 d8 T! u
//遍历块表记录& C2 v) ?! I' _7 \% n
foreach (ObjectId objId in btr) {$ w+ H$ j3 R4 Y; }/ A3 I1 n: ?" ^
//由ObjectId得到ActiveX中的AcadObject对象% u5 g! y N3 x
obj = (AcadObject)((AcadDatabase)db.AcadDatabase).ObjectIdToObject(objId.OldId);+ ~ [4 [, d6 } t
//为obj添加响应Modified事件
C0 ~; V7 O" xobj.Modified += new IAcadObjectEvents_ModifiedEventHandler(obj_Modified);( ~! Q. m/ I1 C9 P
}
3 C2 l w# ~- V) i+ N}# B) D. P( Y. N, F" ?0 ^
finally {% a6 O/ X. H% T/ n/ K; ~5 H. b
btr.Close();0 K, u! d c$ o- F0 ^
bt.Close();
+ C' |9 k9 P0 p; O* S* [) a}
" t( w0 Q6 D/ u; `/ f
$ {5 T* @: B4 A" n3 {9 K其中事件响应函数obj_Modified的表示如下所示:/ i4 ]9 i. {8 o O- I* ]+ S" [# z
! \: c8 _. d5 i k3 e0 d
public static void obj_Modified(AcadObject obj) {
. s6 u3 c0 E* J8 QCommandLinePrompts.Message("object modified!" + obj.ObjectID + "\n");
4 C. A. T% v$ P6 e/ j t}5 s6 r: I3 W; D# {7 O, J7 _
6 I* }# T( l% p7 F
二、结论
" I3 d0 Z. K. ^ R. O/ y1 ~
7 U- y. J9 K! e2 H) c2 S/ R! W本文以C#为例,对基于.NET API的AutoCAD二次开发作了较详细的介绍。.NET API在具有ObjectARX强大功能的同时具有VBA使用方便易用的优点,同时具有C++的强大功能,是较为理想的开发工具。但目前.NET API在某些方面还有些不足,但随着其版本的更新、完善,定会成为众多开发人员的首选工具。 |