华南理工大学 刘林 麦智晖 阎汉生5 l; Y( s2 Y3 o: ~5 i6 f6 `! ]
4 F. p/ m# Y7 U, H0 x3 N2 v0 L! d2 b
6 {7 y' s- ?2 N' H2 p本文基于AutoCAD 2006新推出的.NET API为工具,介绍了在.NET平台下对AutoCAD进行二次开发的技术,并与目前常用的VBA、ObjectARX作了对比。同时讨论了如何弥补.NET API某些不足的功能。
" `1 R8 R! V) G( f9 h6 l; s& f4 q, m+ ~9 B" R
4 H+ J g6 }; W4 B& n6 |
当前AutoCAD的二次开发工具主要有:VisualLisp、VBA和ObjectARX等。其中,VisualLisp与VBA较为简单,特别是VBA,使用方便且开发速度较快,但其功能相比ObjectARX有所不足,尤其是对面向对象的功能支持不好。而ObjectARX基于VC平台,在C++的支持下,其功能非常强大,可以很好地运用各种面向对象技术,但其缺点是发开速度比较慢,同时对开发人员的能力要求较高。
1 |2 d( ~5 e! _! Z# t* P2 v8 }# _: R L$ y+ ?8 F
.NET是微软新推出的开发平台,具有众多优点。基于.NET平台对AutoCAD进行二次开发,可充分利用.NET的各种优势,在保证功能强大的前提下大大提高开发速度。
/ z9 H* i1 r' {1 a3 D ?% H( H* F( ^4 Z/ K$ j, K
一、基于.NET的开发
/ k% {7 d9 T6 ], i( \8 c# |) T
" `6 u. w. }: z+ H! P" b. E5 c1..NET API简介9 M$ l9 y1 X z3 F, k& }
- v) v ] J7 z3 Q& v& j& A3 C在新推出的AutoCAD 2006中,Autodesk为其开发增加了.NET API。.NET API提供了一系列托管的外包类(Managed Wrapper Class),使开发人员可在.NET框架下,使用任何支持.NET的语言,如VB.NET、C# 和Managed C++等对AutoCAD进行二次开发。其优点是完全面向对象,在拥有与C++相匹配的强大功能的同时,具有方便易用的特点,是较理想的AutoCAD二次开发工具。
1 S: |* a* j$ t$ N5 P' r& [% |5 L2 M- s9 c' m3 s' \
2..NET API与传统ObjectARX的主要区别7 ~1 U; F9 z$ ~# E4 H
. e' q0 l7 P; R3 c# g
.NET API与传统ObjectARX的区别主要源于在.NET环境下开发应用程序与在VC环境下开发应用程序的区别。首先,在VC环境下,程序员需要自己管理内存的申请和释放,而.NET采用了垃圾回收机制,由.NET框架自行判断内存回收的时机并实行回收,从而解决了令C++程序员头痛的内存泄漏问题。也正是由于这个特点,在.NET环境下不能象在C++环境下那样利用析构函数释放其他的资源,需要程序员在程序中显式地释放。在.NET API中,主要通过Dispose来函数进行资源的释放。
' h( [7 C8 U! s) ]0 z. p F6 L, V) S5 \0 S4 d9 i
其次,ObjectARX中的各种反应器(Reactor)在.NET API中由外包类映射为各种事件(Event),可通过定义这些事件的响应函数来响应AutoCAD的各种操作。同时对于错误信息的处理也从函数返回值改变为通常异常来处理,使其更好地兼容.NET。由于VB.NET、C#等语言都是完全面向对象的,没有全局函数的概念,所以.NET API将ObjectARX下的全局函数封装为.NET API下的某些对象或对象的属性,如ObjectARX下与用户交互的系列全局函数被封装为CommandLinePrompt类。0 b+ r: z# ~) f e% o8 U# {/ {
1 t$ q" y* ^- _1 g
3.使用.NET API& O6 b# h6 u7 X/ y* d: z
3 D8 Y' t& R& n9 q7 o, ]9 A$ q
下面以C#为例,在Microsoft Visual C# 2005 Express Edition Beta平台上,先新建一个Class Library项目,再将AutoCAD2005安装目录下的acdbmgb.dll与acmgb.dll作为引用添加进项目中。这两个文件包含了.NET API中所有的外包类。
- w; L$ A# S9 E6 \2 ]! Z7 F
- X3 d! O2 G Z然后在要使用.NET API的类中添加以下语句以引用.NET API的命名空间。需要添加的语句如下:
# y4 p- _/ @; n
3 Y) g7 Y" S# V, M0 rusing Autodesk.AutoCAD.ApplicationServices;) \ ]7 @7 \( h4 W
using Autodesk.AutoCAD.DatabaseServices;
* `4 F9 I+ z9 v: F! Lusing Autodesk.AutoCAD.Runtime;
% ^7 v3 G+ _5 y4 ]6 r- ~using Autodesk.AutoCAD.Geometry;* \" R0 A3 `$ x. m) R1 g
+ _& j. K' j n* T这样就可以利用.NET API进行开发了。以下代码可在AutoCAD注册为一个命令"AddLine",该命令可在当前工作空间中添加一条起点为(0,0,0),终点(200,200,0)的直线。代码如下:# ]8 I+ P) e. P* I u; U" v# |
' Z0 ^/ a1 K. K2 P9 g& B5 [% H- s[CommandMethod ("AddLine")], @$ z4 X E5 ~- y6 t+ F
public static void AddLineCmd()
9 e9 W0 x: w9 H{9 N: f3 \" B" g, s2 }! A
Database db = HostApplicationServices.WorkingDatabase;//获得当前工作空间的数据库
, d$ z/ d2 {) C( ~BlockTable bt = (BlockTable)db.BlockTableId.Open(OpenMode.ForRead); //获得块表
* n# J% D+ H* HBlockTableRecord btr = (BlockTableRecord)bt[BlockTableRecord.ModelSpace].Open(OpenMode.ForWrite); //获得模型空间的块表记录; _* W; ?$ j7 I2 x* K$ x4 y
Line line = new Line(new Point3d(0, 0, 0), new Point3d(200, 200, 0));//创建一条直线+ [) |; G3 Q7 {0 v/ D& I* U
try {
4 {$ ] x! n' G1 g% x8 lbtr.AppendEntity(line);//将直线添加到模型空间中) O' c0 W9 |- Z: m/ o' [/ R
line.Close();//关闭该直线, ?* Z9 V9 ~2 T: D k, j% C
}
4 N* O9 Q3 H7 f- c1 ?4 ~* Pfinally {- R0 ?2 U0 g& C: Z# G2 B; G) R
btr.Close();//关闭块表记录
; N8 ~- w: K$ z- N% j1 `& [* Rbt.Close();//关闭块表
8 i8 }/ u$ B& s; ~1 m. d% Q) l}
) ~9 I' S1 Y$ d5 T) [1 y}
7 ]. [, Q% t6 A4 _& n7 `: `6 o6 R5 t% L
由此可见,上述语句与在VC下的开发非常类似,其过程都是先得到数据库,然后依次打开块表、块表记录,接着添加实体,最后关闭块表、块表记录。值得注意的是finally语句,无论try块中的语句是否发生异常,finally块中的语句都会被执行,从而确保关闭块表和块表记录的操作会被执行。
" V# e2 I$ W. ?" ?' |- Y# Z6 P7 j D/ y
写完代码后进行编译,编译完成将得到一个dll文件。在AutoCAD 2006中通过"netload"命令即可选择该dll文件进行加载,加载成功后即可以通过"AddLine"命令执行上述代码。遗憾的是目前的.NET API版本还不支持卸载,若要卸载只能关闭AutoCAD。
0 f$ @+ s& N& D0 o! ]9 n2 s
. j% H4 V; m' R- }' B1 B4..NET API的初始化与清除
( P* R0 V3 q, d+ }9 d* S$ B- i0 x K3 L% h
在ObjectARX中,"acrxEntryPoint"函数是ARX程序的载入点,程序的初始化和清除均可在该函数中进行。而在.NET API中则首先需要将初始化代码封装在一个类中,同时该类需要压迫实现
7 a; y! j& f# ]1 _9 ~) S8 e6 z0 d) E2 Z0 W
IExtensionApplication接口。该接口包含Initialize与Terminate两个函数。其中Initialize负责加载程序时的初始化操作,Terminate则负责进行卸载程序时的清除操作。代码如下:
7 ?. |/ ?* x# `; `* F
2 }' T6 m7 g7 y9 c+ fnamespace ARXExample {
4 c5 J0 e! D& ]. gpublic class MyARX : IExtensionApplication {
7 F, r+ e Q1 P6 L) H4 c……
6 o$ q; d. c" i tpublic void Initialize() {, J" G; _" i& O# h3 G' [9 `+ O
//初始化操作
' N- @+ C( V! |9 F}% n- G$ @" A, o/ ~+ C" h0 {; a
public void Terminate() {
! J. V! |+ o& X7 f//清除操作
& _$ t% k- u! v}
( @7 U% p+ L6 \……
6 j+ G3 e2 J) J+ j! [}# B) b0 H" R% A, R' C1 U% m
}
) Z0 j% y9 a8 _4 {& t/ ]
% d& t, P% M# p/ F同时,为加快加载速度,可在MyARX.cs的文件头加入以下语句:
1 O) O2 W- V/ Y
( M; _# e- y+ T+ O4 p" F) a[assembly: ExtensionApplication (typeof (ARXExample.MyARX) ) ]9 T/ y, p d$ n/ ?7 D b% d
[assembly: CommandClass (typeof (ARXExample.MyARX) ) ]3 V% k3 Y7 u( N' n B
- \4 b7 a7 {6 i3 j J
这样在加载程序时AutoCAD将直接通过MyARX中的Initialize语句进行初始化,同时注册MyARX中的命令。否则,AutoCAD将搜索dll中所有的类以找到实现IExtensionApplication接口的类进行初始化,如找不到则不进行初始化。同样,通过CommandClass属性,AutoCAD也会直接到MyARX类中搜索要注册的命令。当程序中包含的类数目较多时,通过ExtensionApplication和CommandClass这两个属性可显著地加快程序的加载速度。
( S/ q5 @3 T% X4 K# B: Z+ s' S$ p! G6 \8 y$ |
5..NET API与COM交互操作( s2 j$ E9 A. v6 P2 a
9 C6 Q! K4 V4 n% t$ p在目前的.NET API中,其功能与传统的ObjectARX相比有所不及,有相当的ObjectARX函数目前还没有封装到.NET API中,如GetPoint等。但可以通过COM方式使用ActiveX来弥补.NET API的不足。" o: |+ H3 @9 f! v2 E/ }: _1 D
& S3 L, Z& Y) A: e* y增加了COM引用后,程序就可以使用许多VBA中的功能了。以AutoCAD ActiveX中的事件为例,以下代码可以为当前工作空间中所有的图元添加Modified事件:
/ }. r* v" ^. E6 ]- n) n. B
0 M2 L2 }5 k$ S M7 fDatabase db = HostApplicationServices.WorkingDatabase;; X8 p7 ~ v/ ^9 M! O
BlockTable bt = (BlockTable)db.BlockTableId.Open(OpenMode.ForRead);
& {. f7 h7 j1 G* z }8 uBlockTableRecord btr = (BlockTableRecord)bt[BlockTableRecord.ModelSpace].Open(OpenMode.ForWrite);- D9 ]/ B% H+ |% @
try {1 a# F9 N+ b) a6 @
AcadObject obj;
: @% i. i7 N" G1 @3 @' I1 `//遍历块表记录' N) F, a4 e. Y3 n& t
foreach (ObjectId objId in btr) {; E" ~% y7 a7 }0 Z; g; r
//由ObjectId得到ActiveX中的AcadObject对象
0 U* t/ D% K7 W2 q6 [obj = (AcadObject)((AcadDatabase)db.AcadDatabase).ObjectIdToObject(objId.OldId);
/ {+ R, Y, j. u8 H/ p: K//为obj添加响应Modified事件' O1 A# r5 G& n' l6 C# _ Q. m
obj.Modified += new IAcadObjectEvents_ModifiedEventHandler(obj_Modified);& I1 s- S7 {( p4 Q
}" T/ ~3 E7 x4 [! A
}$ Z& p% t/ C6 n1 C% ]% v
finally {0 v( ~/ Q, f% M( m. `6 B2 h
btr.Close();
$ I4 L/ S, k; _7 Tbt.Close();! i0 x: [% s5 n( u1 q
}& Q8 v# y; b! s! C8 X
2 H1 ?9 A6 o, |& H4 g3 t
其中事件响应函数obj_Modified的表示如下所示:, Q4 j9 R+ T4 s0 R2 h
/ |* ~# V5 j# G6 Y! I( `
public static void obj_Modified(AcadObject obj) {
1 s2 V5 T8 R$ X$ A5 ACommandLinePrompts.Message("object modified!" + obj.ObjectID + "\n");& x0 t: G4 ~. ^
}
: [3 u7 S2 A7 g& P: C$ H H& i1 S" M$ U
二、结论: r+ j! |7 e; W
. c% S" _# i; b4 ^! l7 a4 q6 B本文以C#为例,对基于.NET API的AutoCAD二次开发作了较详细的介绍。.NET API在具有ObjectARX强大功能的同时具有VBA使用方便易用的优点,同时具有C++的强大功能,是较为理想的开发工具。但目前.NET API在某些方面还有些不足,但随着其版本的更新、完善,定会成为众多开发人员的首选工具。 |