华南理工大学 刘林 麦智晖 阎汉生
* ^' k& F- [8 n6 @" S
- v$ T. p% E4 r" q* f! ~2 d0 o: J! i- w4 e: D
本文基于AutoCAD 2006新推出的.NET API为工具,介绍了在.NET平台下对AutoCAD进行二次开发的技术,并与目前常用的VBA、ObjectARX作了对比。同时讨论了如何弥补.NET API某些不足的功能。 ( p" F% Y0 v/ ]
' i; \ y: o# v5 t) t$ G# O+ e- F
+ f i' i) D4 s" d当前AutoCAD的二次开发工具主要有:VisualLisp、VBA和ObjectARX等。其中,VisualLisp与VBA较为简单,特别是VBA,使用方便且开发速度较快,但其功能相比ObjectARX有所不足,尤其是对面向对象的功能支持不好。而ObjectARX基于VC平台,在C++的支持下,其功能非常强大,可以很好地运用各种面向对象技术,但其缺点是发开速度比较慢,同时对开发人员的能力要求较高。$ l" B9 V) S( J- P% _& ~; t5 |
+ `7 L( X# h* w- t D
.NET是微软新推出的开发平台,具有众多优点。基于.NET平台对AutoCAD进行二次开发,可充分利用.NET的各种优势,在保证功能强大的前提下大大提高开发速度。" y% ^, K" _, s+ D+ S5 [
0 K! v, c {. U$ M2 @一、基于.NET的开发
8 Q+ e% r$ K+ g8 ]
2 U) j, K/ F- K1 F2 [; D9 `7 M0 Z1..NET API简介$ |+ r, L4 X7 ~' `/ z6 t E
6 ?/ X* E% C" @2 M0 e7 R在新推出的AutoCAD 2006中,Autodesk为其开发增加了.NET API。.NET API提供了一系列托管的外包类(Managed Wrapper Class),使开发人员可在.NET框架下,使用任何支持.NET的语言,如VB.NET、C# 和Managed C++等对AutoCAD进行二次开发。其优点是完全面向对象,在拥有与C++相匹配的强大功能的同时,具有方便易用的特点,是较理想的AutoCAD二次开发工具。
! `6 u0 f( N$ W4 `2 ]3 G3 i$ L& C1 h% a
2..NET API与传统ObjectARX的主要区别
n) M6 x$ g% r: n. B7 P
% U Z" b3 c7 D* G! }2 |1 Z1 M4 u& M( N.NET API与传统ObjectARX的区别主要源于在.NET环境下开发应用程序与在VC环境下开发应用程序的区别。首先,在VC环境下,程序员需要自己管理内存的申请和释放,而.NET采用了垃圾回收机制,由.NET框架自行判断内存回收的时机并实行回收,从而解决了令C++程序员头痛的内存泄漏问题。也正是由于这个特点,在.NET环境下不能象在C++环境下那样利用析构函数释放其他的资源,需要程序员在程序中显式地释放。在.NET API中,主要通过Dispose来函数进行资源的释放。( L9 l B% l c: A1 @3 F/ p( b
/ g. n: h& i, S6 j. n1 U" }其次,ObjectARX中的各种反应器(Reactor)在.NET API中由外包类映射为各种事件(Event),可通过定义这些事件的响应函数来响应AutoCAD的各种操作。同时对于错误信息的处理也从函数返回值改变为通常异常来处理,使其更好地兼容.NET。由于VB.NET、C#等语言都是完全面向对象的,没有全局函数的概念,所以.NET API将ObjectARX下的全局函数封装为.NET API下的某些对象或对象的属性,如ObjectARX下与用户交互的系列全局函数被封装为CommandLinePrompt类。
- K5 ^) c; W5 m1 {; b2 e& Z, N$ ^
9 D8 T4 A$ @& R2 Y3.使用.NET API- j+ w* [9 r9 s5 Z- C3 g
0 |# |$ ]' _* ^+ F6 ~* z下面以C#为例,在Microsoft Visual C# 2005 Express Edition Beta平台上,先新建一个Class Library项目,再将AutoCAD2005安装目录下的acdbmgb.dll与acmgb.dll作为引用添加进项目中。这两个文件包含了.NET API中所有的外包类。+ `# y7 D# r6 |% j0 r
0 _) Z6 N6 u/ y& u, x- m
然后在要使用.NET API的类中添加以下语句以引用.NET API的命名空间。需要添加的语句如下:% Q+ I2 T$ k5 m' h( M6 b
# c3 R T0 } D' Gusing Autodesk.AutoCAD.ApplicationServices;' E$ y0 `1 L% R
using Autodesk.AutoCAD.DatabaseServices;
7 Y9 w! A/ H+ u& a; l$ Gusing Autodesk.AutoCAD.Runtime;$ x. h! y7 G e+ J5 l
using Autodesk.AutoCAD.Geometry;
$ I) p) A) c: [
5 {0 o0 L: h D* A$ {' R# j这样就可以利用.NET API进行开发了。以下代码可在AutoCAD注册为一个命令"AddLine",该命令可在当前工作空间中添加一条起点为(0,0,0),终点(200,200,0)的直线。代码如下:4 \ [, }( Q" ^, T4 f1 @ p( N
; _8 c- `% J8 |( L( t7 t' ~
[CommandMethod ("AddLine")]0 k; P; y2 u4 M. S* x
public static void AddLineCmd()
' c% u* |0 }, F) b& ~{0 q: V+ {! W N. j8 H' |+ A
Database db = HostApplicationServices.WorkingDatabase;//获得当前工作空间的数据库
* ~, s9 V6 {- uBlockTable bt = (BlockTable)db.BlockTableId.Open(OpenMode.ForRead); //获得块表$ [' K- ?2 m) ]9 j
BlockTableRecord btr = (BlockTableRecord)bt[BlockTableRecord.ModelSpace].Open(OpenMode.ForWrite); //获得模型空间的块表记录 v; n" o% u1 S8 h- f, v
Line line = new Line(new Point3d(0, 0, 0), new Point3d(200, 200, 0));//创建一条直线1 k* R1 q) A$ d) z
try {
% V1 v/ g: q- R+ Y% N4 S) ~, Rbtr.AppendEntity(line);//将直线添加到模型空间中
& I8 B4 U0 W% v: f" wline.Close();//关闭该直线$ p3 g4 r- V' T9 T: J
}
. i0 e2 U+ _9 X1 h: F" Bfinally {
5 T3 r2 w2 K$ k$ [1 P, wbtr.Close();//关闭块表记录+ ^- o" p6 [$ p
bt.Close();//关闭块表0 g8 J. J1 K' ~8 X! m% C
}
6 N5 v$ D5 s x' s}
. l" A8 f. ^" x& S+ F% i4 h# O9 `! g3 X; f t/ C5 |
由此可见,上述语句与在VC下的开发非常类似,其过程都是先得到数据库,然后依次打开块表、块表记录,接着添加实体,最后关闭块表、块表记录。值得注意的是finally语句,无论try块中的语句是否发生异常,finally块中的语句都会被执行,从而确保关闭块表和块表记录的操作会被执行。
P0 B" H# s) [. V' C5 s; D0 W6 W4 ~! ^/ r( F+ ?
写完代码后进行编译,编译完成将得到一个dll文件。在AutoCAD 2006中通过"netload"命令即可选择该dll文件进行加载,加载成功后即可以通过"AddLine"命令执行上述代码。遗憾的是目前的.NET API版本还不支持卸载,若要卸载只能关闭AutoCAD。
- J; N* U0 p: F- H8 Z& ~) t8 {4 B+ `5 s# m) M; T( I
4..NET API的初始化与清除
8 }5 L4 F. {9 r+ P
% \8 K( G8 S! y) e8 Z/ J. R在ObjectARX中,"acrxEntryPoint"函数是ARX程序的载入点,程序的初始化和清除均可在该函数中进行。而在.NET API中则首先需要将初始化代码封装在一个类中,同时该类需要压迫实现
- c- l: d4 [; p% `6 l4 f
' Y' D- T2 f8 G6 g! y5 Y. cIExtensionApplication接口。该接口包含Initialize与Terminate两个函数。其中Initialize负责加载程序时的初始化操作,Terminate则负责进行卸载程序时的清除操作。代码如下:& i% O: b5 @( n) M4 f
' U4 T4 d5 ^$ I, Z! l0 `namespace ARXExample {, i3 |! n3 `% v1 W, y
public class MyARX : IExtensionApplication {3 M' R9 z# C4 C) X+ X! m6 N
……3 c/ C/ q' L% a4 x- \3 Q6 Z
public void Initialize() {7 f2 O1 I( F5 \# X# \/ ]( D
//初始化操作( v, Q5 q* _% w8 @( h7 N1 A
}
! p9 Q7 {; e9 P& n0 `public void Terminate() {/ d* C% n5 l* z& Z v2 D
//清除操作
i& |6 Z2 G/ |$ L8 T' {( X; g}
/ a5 b- H4 F/ p6 O! a……
C8 ?4 D! t. L7 B' ?# R6 ?}# B: H" h- _. M- r: w2 V
}
/ ]' N8 z2 j5 @+ e0 b2 I' x. r! u, h5 f9 }$ f+ e/ w! v
同时,为加快加载速度,可在MyARX.cs的文件头加入以下语句: n: \- S' ^* \6 {' B
7 X% y( x# }, E, x4 Q; W8 V2 }[assembly: ExtensionApplication (typeof (ARXExample.MyARX) ) ]: g2 P* ~! l+ @. D2 ^. D
[assembly: CommandClass (typeof (ARXExample.MyARX) ) ]
6 a8 Q5 \7 Z- G# B% Q% z% M& z7 K k( }, v% F# ]( u7 \
这样在加载程序时AutoCAD将直接通过MyARX中的Initialize语句进行初始化,同时注册MyARX中的命令。否则,AutoCAD将搜索dll中所有的类以找到实现IExtensionApplication接口的类进行初始化,如找不到则不进行初始化。同样,通过CommandClass属性,AutoCAD也会直接到MyARX类中搜索要注册的命令。当程序中包含的类数目较多时,通过ExtensionApplication和CommandClass这两个属性可显著地加快程序的加载速度。8 D9 u- j8 J9 I( j w) W
C T0 M* }; J3 }- u
5..NET API与COM交互操作8 q' c" f( V% D5 L6 t! f/ O+ [
. @; [ m7 J$ ?- D) \
在目前的.NET API中,其功能与传统的ObjectARX相比有所不及,有相当的ObjectARX函数目前还没有封装到.NET API中,如GetPoint等。但可以通过COM方式使用ActiveX来弥补.NET API的不足。/ S3 r, f: R& N# A9 V+ L! e. x! W
9 H! F7 `4 }" Q- U6 S增加了COM引用后,程序就可以使用许多VBA中的功能了。以AutoCAD ActiveX中的事件为例,以下代码可以为当前工作空间中所有的图元添加Modified事件:) v l+ M% w" L
3 \ ]# R9 c% c9 p7 fDatabase db = HostApplicationServices.WorkingDatabase;
3 @& a5 y. a! {6 r+ E, L' ]4 R! OBlockTable bt = (BlockTable)db.BlockTableId.Open(OpenMode.ForRead);0 S) {0 @* X8 E! S n3 I
BlockTableRecord btr = (BlockTableRecord)bt[BlockTableRecord.ModelSpace].Open(OpenMode.ForWrite);0 Y- `% h5 w+ E% S M
try {
- G# ^4 p3 d8 r, G( W# h; S; XAcadObject obj;2 I2 s* c' `* w: F0 u
//遍历块表记录: D$ W5 U8 P) T" V* P
foreach (ObjectId objId in btr) {
1 \# [& H! u X b* ^( P7 h, e! C//由ObjectId得到ActiveX中的AcadObject对象* A- |- e; H0 }8 Z% y. [
obj = (AcadObject)((AcadDatabase)db.AcadDatabase).ObjectIdToObject(objId.OldId);
+ }- U% a' ?, F! x//为obj添加响应Modified事件* ^* R$ {, m2 W5 V* E- X$ V+ |
obj.Modified += new IAcadObjectEvents_ModifiedEventHandler(obj_Modified);
1 z* F5 C2 ~4 D* ?5 r}
4 j: I7 U, z e- b$ I2 k# c% m+ }}' E' G7 o2 I; D! L+ k, W
finally {6 M6 c) A0 O( N: Z
btr.Close();+ z ]0 q# W; w- z
bt.Close();2 Q/ E; y4 ]9 A B4 v: X
}2 l/ Q* A0 V" Z* T, i( N. [: c
" Y2 J/ B& y# Y L% `: w其中事件响应函数obj_Modified的表示如下所示:% ~, p+ o$ a, q5 w
f8 ^. y+ J8 Y# S' h$ V
public static void obj_Modified(AcadObject obj) {- D8 r/ d r7 \9 J+ e
CommandLinePrompts.Message("object modified!" + obj.ObjectID + "\n");0 L: X f7 _' f- q" K
}
3 s }3 |2 I. `! R* L- b! |3 \+ C4 K2 A4 i9 k" l: e. s9 }
二、结论
# f# I5 s" O6 J2 X, F0 f3 E% U1 Y: \6 ?1 }, e2 `
本文以C#为例,对基于.NET API的AutoCAD二次开发作了较详细的介绍。.NET API在具有ObjectARX强大功能的同时具有VBA使用方便易用的优点,同时具有C++的强大功能,是较为理想的开发工具。但目前.NET API在某些方面还有些不足,但随着其版本的更新、完善,定会成为众多开发人员的首选工具。 |