华南理工大学 刘林 麦智晖 阎汉生: r$ {7 ^( m) e" W7 p
5 ^; o5 l: j! w0 h- P0 n3 ]8 R! {: y( c
本文基于AutoCAD 2006新推出的.NET API为工具,介绍了在.NET平台下对AutoCAD进行二次开发的技术,并与目前常用的VBA、ObjectARX作了对比。同时讨论了如何弥补.NET API某些不足的功能。
, ~. X9 U* z, w9 N$ m
/ Z! u; c$ _+ P2 W$ j1 [! c# f
3 z2 C# q$ ^- R- u% j' w当前AutoCAD的二次开发工具主要有:VisualLisp、VBA和ObjectARX等。其中,VisualLisp与VBA较为简单,特别是VBA,使用方便且开发速度较快,但其功能相比ObjectARX有所不足,尤其是对面向对象的功能支持不好。而ObjectARX基于VC平台,在C++的支持下,其功能非常强大,可以很好地运用各种面向对象技术,但其缺点是发开速度比较慢,同时对开发人员的能力要求较高。7 K9 X, R+ |6 ~% f- o
2 w7 y) v* B5 J, A$ H2 y. n.NET是微软新推出的开发平台,具有众多优点。基于.NET平台对AutoCAD进行二次开发,可充分利用.NET的各种优势,在保证功能强大的前提下大大提高开发速度。2 L0 P' @. i. O( U" E% F
9 T1 y5 |! m4 N0 |一、基于.NET的开发
7 J) d8 w- v% y- q0 W: x* q9 ^' {8 I/ j# X7 l- M
1..NET API简介
& T1 R3 ]; Q$ f! P: W7 t1 N v$ F* a' a% E7 X7 X' C
在新推出的AutoCAD 2006中,Autodesk为其开发增加了.NET API。.NET API提供了一系列托管的外包类(Managed Wrapper Class),使开发人员可在.NET框架下,使用任何支持.NET的语言,如VB.NET、C# 和Managed C++等对AutoCAD进行二次开发。其优点是完全面向对象,在拥有与C++相匹配的强大功能的同时,具有方便易用的特点,是较理想的AutoCAD二次开发工具。
5 W5 z1 x5 J' U0 |" O
) |" J3 _: Z2 ~) E& I% _9 z% \2..NET API与传统ObjectARX的主要区别
+ S8 E# O9 o. ~' ^2 a! u: [
8 K/ S6 k) u( ]0 V3 t.NET API与传统ObjectARX的区别主要源于在.NET环境下开发应用程序与在VC环境下开发应用程序的区别。首先,在VC环境下,程序员需要自己管理内存的申请和释放,而.NET采用了垃圾回收机制,由.NET框架自行判断内存回收的时机并实行回收,从而解决了令C++程序员头痛的内存泄漏问题。也正是由于这个特点,在.NET环境下不能象在C++环境下那样利用析构函数释放其他的资源,需要程序员在程序中显式地释放。在.NET API中,主要通过Dispose来函数进行资源的释放。
! I4 J" Y! l y" ]: X9 {3 V- @
4 v7 ?: Q6 v3 l3 N6 M8 \% f* F其次,ObjectARX中的各种反应器(Reactor)在.NET API中由外包类映射为各种事件(Event),可通过定义这些事件的响应函数来响应AutoCAD的各种操作。同时对于错误信息的处理也从函数返回值改变为通常异常来处理,使其更好地兼容.NET。由于VB.NET、C#等语言都是完全面向对象的,没有全局函数的概念,所以.NET API将ObjectARX下的全局函数封装为.NET API下的某些对象或对象的属性,如ObjectARX下与用户交互的系列全局函数被封装为CommandLinePrompt类。# n3 S% T1 h5 l F9 ^% o
5 O: k5 W# }2 @, y9 t8 d3.使用.NET API& t ?0 @! C6 C# u! u: ?
" `0 V" z/ t+ x- ~* J, f下面以C#为例,在Microsoft Visual C# 2005 Express Edition Beta平台上,先新建一个Class Library项目,再将AutoCAD2005安装目录下的acdbmgb.dll与acmgb.dll作为引用添加进项目中。这两个文件包含了.NET API中所有的外包类。
( R9 b: l, X% _6 B5 S" X" w* a
5 J4 h6 f7 N! K+ H6 Q6 B) C- X然后在要使用.NET API的类中添加以下语句以引用.NET API的命名空间。需要添加的语句如下:) x1 B. @+ d: N k4 [) D
4 X+ ?# V# X! `/ U& N( Gusing Autodesk.AutoCAD.ApplicationServices;
3 |, m1 r6 P" s, qusing Autodesk.AutoCAD.DatabaseServices;
0 x8 p( k/ }6 d* W6 Xusing Autodesk.AutoCAD.Runtime;
- a# v# j8 \' \3 a) M4 |; ]+ |: Musing Autodesk.AutoCAD.Geometry;
) C0 E/ M. m% k/ a. a! g B. e* f, X
3 L8 p" v" P5 U7 l" |. q/ s这样就可以利用.NET API进行开发了。以下代码可在AutoCAD注册为一个命令"AddLine",该命令可在当前工作空间中添加一条起点为(0,0,0),终点(200,200,0)的直线。代码如下:! |6 ?- _5 l" d9 z, H
1 Z# q" }' _4 S, _- M2 F
[CommandMethod ("AddLine")]
: h' Y6 x6 v: |public static void AddLineCmd()
: h3 F# o+ F7 B: X5 V, e{
' B( r9 Q r6 eDatabase db = HostApplicationServices.WorkingDatabase;//获得当前工作空间的数据库
: `2 B1 |* |2 DBlockTable bt = (BlockTable)db.BlockTableId.Open(OpenMode.ForRead); //获得块表3 g w5 B- Q4 T G2 t! D; J2 R; J
BlockTableRecord btr = (BlockTableRecord)bt[BlockTableRecord.ModelSpace].Open(OpenMode.ForWrite); //获得模型空间的块表记录" X- h' g- {% x0 d' x
Line line = new Line(new Point3d(0, 0, 0), new Point3d(200, 200, 0));//创建一条直线2 [0 a* Q% _: o) y! H8 a( n1 S
try {
/ z# t+ _ n* H3 Z/ M7 r% y- o) ~btr.AppendEntity(line);//将直线添加到模型空间中
) Z$ M& ^5 h- a& I0 v2 j$ aline.Close();//关闭该直线
/ j9 l) I( `$ q/ Z; |' }! o}
; c4 E! N; a4 N3 P: x! f% J; qfinally {3 M& f7 A0 \( R
btr.Close();//关闭块表记录
: G* q5 o; x+ z5 k; Z5 Z9 a- ` X$ Dbt.Close();//关闭块表. D: ^8 f; R: e8 X' }% X v4 {! m
}& j- u& i$ y' w L7 C$ x
}; i9 O& a) w% e
. q [6 i$ f# r: e+ t% \
由此可见,上述语句与在VC下的开发非常类似,其过程都是先得到数据库,然后依次打开块表、块表记录,接着添加实体,最后关闭块表、块表记录。值得注意的是finally语句,无论try块中的语句是否发生异常,finally块中的语句都会被执行,从而确保关闭块表和块表记录的操作会被执行。
7 o) g4 V& W) A. y, z
; P$ F9 {' a. M" [7 K3 i写完代码后进行编译,编译完成将得到一个dll文件。在AutoCAD 2006中通过"netload"命令即可选择该dll文件进行加载,加载成功后即可以通过"AddLine"命令执行上述代码。遗憾的是目前的.NET API版本还不支持卸载,若要卸载只能关闭AutoCAD。# l0 J r0 A! t1 q8 @1 C @1 @. f
1 I; h6 r; u& [, Q" ~" {1 x
4..NET API的初始化与清除
% l- V7 t' m/ C& W0 O ~5 G! R1 I- ?/ R4 s& F( M
在ObjectARX中,"acrxEntryPoint"函数是ARX程序的载入点,程序的初始化和清除均可在该函数中进行。而在.NET API中则首先需要将初始化代码封装在一个类中,同时该类需要压迫实现
# @! ]) a' A4 |+ ~! I) A& a
5 x( a2 }4 K9 K* o( \IExtensionApplication接口。该接口包含Initialize与Terminate两个函数。其中Initialize负责加载程序时的初始化操作,Terminate则负责进行卸载程序时的清除操作。代码如下:' u- |1 m4 S7 j4 L4 N' I
a3 F# H- r% f: G b: z# |
namespace ARXExample {- M$ O/ v+ X* v
public class MyARX : IExtensionApplication {7 D! L2 Y. l' l6 k/ V2 Z1 E% M
……( t, W% l% _; r9 Z4 W0 D3 S) ?. ^
public void Initialize() {6 c/ e! B4 F2 j3 i
//初始化操作
+ t! H1 O) I! [! B6 l4 t* W( B& c}# k E0 ^: `% C! o
public void Terminate() {
! K% N! W ~/ B p//清除操作. s- `) P' T% I$ g
}
3 e, j5 w8 B3 U) n7 ~3 M. O( U, c……- p. S' [4 H7 z) ]& j) o
}
. O1 @0 k) `" s2 q( T+ p, ?4 V0 i' _5 W}
" n2 {. f# [! u6 E4 p- j5 i) J) m K, D; \6 i0 H/ E+ |" l
同时,为加快加载速度,可在MyARX.cs的文件头加入以下语句:4 Z$ A7 s/ ]9 K* v9 {$ k) A
2 b, L8 w# t7 ?! w% ?0 m3 W[assembly: ExtensionApplication (typeof (ARXExample.MyARX) ) ]- v& D& |& V, N. K: e1 r- t
[assembly: CommandClass (typeof (ARXExample.MyARX) ) ]
; [: Y: N) k# f; v( R+ p; \' C/ E0 }) n
这样在加载程序时AutoCAD将直接通过MyARX中的Initialize语句进行初始化,同时注册MyARX中的命令。否则,AutoCAD将搜索dll中所有的类以找到实现IExtensionApplication接口的类进行初始化,如找不到则不进行初始化。同样,通过CommandClass属性,AutoCAD也会直接到MyARX类中搜索要注册的命令。当程序中包含的类数目较多时,通过ExtensionApplication和CommandClass这两个属性可显著地加快程序的加载速度。
0 s, t: S. X i1 y: ?6 N
0 ?% b: j# L" i5..NET API与COM交互操作& {! Q3 N' ^9 g. g7 V7 p' a+ p% k9 I o
0 I) p8 Z3 D% I$ R; J4 `9 {
在目前的.NET API中,其功能与传统的ObjectARX相比有所不及,有相当的ObjectARX函数目前还没有封装到.NET API中,如GetPoint等。但可以通过COM方式使用ActiveX来弥补.NET API的不足。" I, s/ y1 e& T P4 o* S
% q3 K/ P5 p o G; Y6 m
增加了COM引用后,程序就可以使用许多VBA中的功能了。以AutoCAD ActiveX中的事件为例,以下代码可以为当前工作空间中所有的图元添加Modified事件:
! K2 Z$ ~9 n6 [6 [* {* y8 S& g/ [* w/ D% `8 N j
Database db = HostApplicationServices.WorkingDatabase;
! S1 o, ]" H+ N3 S; B9 ~BlockTable bt = (BlockTable)db.BlockTableId.Open(OpenMode.ForRead);
5 Q+ K) @' N0 KBlockTableRecord btr = (BlockTableRecord)bt[BlockTableRecord.ModelSpace].Open(OpenMode.ForWrite); M4 K8 f7 `* e! x% r
try {/ H4 Z) e% k. E* m9 h
AcadObject obj;: C" q7 T" M" U
//遍历块表记录
9 _) ^5 E/ T* Dforeach (ObjectId objId in btr) {7 w) ^0 O" p+ [2 {8 P) c/ y
//由ObjectId得到ActiveX中的AcadObject对象
+ v; {4 j. t" _( aobj = (AcadObject)((AcadDatabase)db.AcadDatabase).ObjectIdToObject(objId.OldId);' U: ]% K" w& E6 u/ C% a4 q1 m" Y2 f
//为obj添加响应Modified事件* b! k( Y5 A! t4 @
obj.Modified += new IAcadObjectEvents_ModifiedEventHandler(obj_Modified);
' m4 o K- k+ a0 s' U}
6 C5 g8 _' B7 X E}
6 i' S) E! u! n6 v Nfinally {4 D. W5 ]% A: a
btr.Close();
+ d3 K* [2 v) t8 q/ Bbt.Close();6 D( S! f' t' A, `& Q4 f: q( u
}1 g; \2 [/ P3 |+ _# s& J' x
& h& e/ x3 D' O, b其中事件响应函数obj_Modified的表示如下所示:- H8 ~( g0 l* e0 Z
( Y8 H2 i/ i9 Q( n) G1 [7 k' gpublic static void obj_Modified(AcadObject obj) {
$ w: U5 T( Z3 y' `! k# _CommandLinePrompts.Message("object modified!" + obj.ObjectID + "\n");3 x2 o$ u$ r/ }; d" L
}# M. }5 X( p9 _/ {: V7 i) T
1 G( a, e$ P' R% d' A6 u% E1 x8 j8 y
二、结论; ^8 ^. w/ L, D! M
# Q, b L8 h( y本文以C#为例,对基于.NET API的AutoCAD二次开发作了较详细的介绍。.NET API在具有ObjectARX强大功能的同时具有VBA使用方便易用的优点,同时具有C++的强大功能,是较为理想的开发工具。但目前.NET API在某些方面还有些不足,但随着其版本的更新、完善,定会成为众多开发人员的首选工具。 |