华南理工大学 刘林 麦智晖 阎汉生
9 J; ~; W" p$ Y7 T' l$ \6 P9 n1 Y' s4 F+ o
- W) Z n& d8 F* {: ~
本文基于AutoCAD 2006新推出的.NET API为工具,介绍了在.NET平台下对AutoCAD进行二次开发的技术,并与目前常用的VBA、ObjectARX作了对比。同时讨论了如何弥补.NET API某些不足的功能。 2 w/ I T. p/ u; F7 Z
4 `' Z4 B- D8 V. c) X8 @2 i _7 B& x. J$ c4 t8 X( w7 o* }6 G& M
当前AutoCAD的二次开发工具主要有:VisualLisp、VBA和ObjectARX等。其中,VisualLisp与VBA较为简单,特别是VBA,使用方便且开发速度较快,但其功能相比ObjectARX有所不足,尤其是对面向对象的功能支持不好。而ObjectARX基于VC平台,在C++的支持下,其功能非常强大,可以很好地运用各种面向对象技术,但其缺点是发开速度比较慢,同时对开发人员的能力要求较高。
; I% e/ |' Z+ r/ P: t+ [* r; Y
; Z. v1 N8 ^$ z K- T.NET是微软新推出的开发平台,具有众多优点。基于.NET平台对AutoCAD进行二次开发,可充分利用.NET的各种优势,在保证功能强大的前提下大大提高开发速度。' k H3 J3 z; _ ?
* h6 k! p, M/ }4 S* e一、基于.NET的开发
/ A$ b y$ l# ]: c2 c( A! ^) a! m, O \2 S3 l
1..NET API简介2 L* u5 I% s8 W! [% F
! v" n/ B- ^/ V/ x% y0 R4 |在新推出的AutoCAD 2006中,Autodesk为其开发增加了.NET API。.NET API提供了一系列托管的外包类(Managed Wrapper Class),使开发人员可在.NET框架下,使用任何支持.NET的语言,如VB.NET、C# 和Managed C++等对AutoCAD进行二次开发。其优点是完全面向对象,在拥有与C++相匹配的强大功能的同时,具有方便易用的特点,是较理想的AutoCAD二次开发工具。
3 G' \9 K, \9 k3 Z" [, {5 Z0 q) v
2..NET API与传统ObjectARX的主要区别
8 }3 e% \0 w% \& _7 I. T
. l! m6 z9 D& R0 T9 P. a4 |.NET API与传统ObjectARX的区别主要源于在.NET环境下开发应用程序与在VC环境下开发应用程序的区别。首先,在VC环境下,程序员需要自己管理内存的申请和释放,而.NET采用了垃圾回收机制,由.NET框架自行判断内存回收的时机并实行回收,从而解决了令C++程序员头痛的内存泄漏问题。也正是由于这个特点,在.NET环境下不能象在C++环境下那样利用析构函数释放其他的资源,需要程序员在程序中显式地释放。在.NET API中,主要通过Dispose来函数进行资源的释放。
0 ]# J5 T6 b, L r4 D- @
/ N7 ?+ D u/ g6 y! a% c1 C& e其次,ObjectARX中的各种反应器(Reactor)在.NET API中由外包类映射为各种事件(Event),可通过定义这些事件的响应函数来响应AutoCAD的各种操作。同时对于错误信息的处理也从函数返回值改变为通常异常来处理,使其更好地兼容.NET。由于VB.NET、C#等语言都是完全面向对象的,没有全局函数的概念,所以.NET API将ObjectARX下的全局函数封装为.NET API下的某些对象或对象的属性,如ObjectARX下与用户交互的系列全局函数被封装为CommandLinePrompt类。
6 ~6 U+ R' ?" v4 ~; ~; V5 J2 @% V1 @) U2 I
3.使用.NET API
/ H& U% s2 d1 U
8 l5 z3 p% Y$ I2 K) j/ x下面以C#为例,在Microsoft Visual C# 2005 Express Edition Beta平台上,先新建一个Class Library项目,再将AutoCAD2005安装目录下的acdbmgb.dll与acmgb.dll作为引用添加进项目中。这两个文件包含了.NET API中所有的外包类。
/ C; o' J( e: s, A
' [- k9 c' k' t) I. |# T0 D然后在要使用.NET API的类中添加以下语句以引用.NET API的命名空间。需要添加的语句如下:
1 q+ s3 Q2 T p% U: [- ~/ U* p7 F' F& N$ I+ Y
using Autodesk.AutoCAD.ApplicationServices;2 w r" H7 h3 a! @" L: L
using Autodesk.AutoCAD.DatabaseServices;$ x3 Q2 p$ @! H# n
using Autodesk.AutoCAD.Runtime;9 Y1 a% J' ]9 R
using Autodesk.AutoCAD.Geometry;, Y7 @% ^# i7 v7 u, J
# V: C7 b7 ~ @/ t L0 s% B2 J这样就可以利用.NET API进行开发了。以下代码可在AutoCAD注册为一个命令"AddLine",该命令可在当前工作空间中添加一条起点为(0,0,0),终点(200,200,0)的直线。代码如下:0 i' M: Y( r& }+ ~7 M/ t8 o
. o! p; m: j5 t! g
[CommandMethod ("AddLine")]
8 @9 k% B8 Z% l/ |public static void AddLineCmd()
6 e/ R* h9 U- B% E4 A8 L{( T" [- `, f9 v( V& V
Database db = HostApplicationServices.WorkingDatabase;//获得当前工作空间的数据库
. `3 p+ [; u7 _/ a% E y* |9 eBlockTable bt = (BlockTable)db.BlockTableId.Open(OpenMode.ForRead); //获得块表
: \$ s' L% o n3 u5 TBlockTableRecord btr = (BlockTableRecord)bt[BlockTableRecord.ModelSpace].Open(OpenMode.ForWrite); //获得模型空间的块表记录 q1 Q! x s; H! O a3 h( Y$ E
Line line = new Line(new Point3d(0, 0, 0), new Point3d(200, 200, 0));//创建一条直线
# v# S7 N0 K8 i( O' M: ^- R. Rtry {
6 A( {& N: X% Nbtr.AppendEntity(line);//将直线添加到模型空间中
8 M$ Z7 @6 P& s0 D9 fline.Close();//关闭该直线* L; n$ m8 C1 }" z( p: }
}
/ h9 x7 g1 i. E8 Q: f, t& x) \finally {! Y8 M" D- r! A, n3 ~
btr.Close();//关闭块表记录 s9 F1 q) ?( Q! I( g' q
bt.Close();//关闭块表
$ {# X3 ` t& T. P6 a1 x; B" g}
* I* Y" ]* v1 e* h4 e/ r}
: n+ c0 h* d7 g( {( f2 n% b- U T$ f% N# w1 r, M1 P. n0 h9 n8 M
由此可见,上述语句与在VC下的开发非常类似,其过程都是先得到数据库,然后依次打开块表、块表记录,接着添加实体,最后关闭块表、块表记录。值得注意的是finally语句,无论try块中的语句是否发生异常,finally块中的语句都会被执行,从而确保关闭块表和块表记录的操作会被执行。
1 Q4 T0 Y; m1 {) [* I# e9 I, O9 C2 [% k" w; J1 S9 c1 f, H
写完代码后进行编译,编译完成将得到一个dll文件。在AutoCAD 2006中通过"netload"命令即可选择该dll文件进行加载,加载成功后即可以通过"AddLine"命令执行上述代码。遗憾的是目前的.NET API版本还不支持卸载,若要卸载只能关闭AutoCAD。
* p* d/ ]9 i# h8 b. ]$ i
/ m% y* o1 I# R/ O$ ^% c5 D& ]4..NET API的初始化与清除
0 E9 Y K& I/ B: S6 J- f" a: `) O/ e" Z' Y
在ObjectARX中,"acrxEntryPoint"函数是ARX程序的载入点,程序的初始化和清除均可在该函数中进行。而在.NET API中则首先需要将初始化代码封装在一个类中,同时该类需要压迫实现% e7 H9 A* F5 r. e
1 O7 g/ ^! _- GIExtensionApplication接口。该接口包含Initialize与Terminate两个函数。其中Initialize负责加载程序时的初始化操作,Terminate则负责进行卸载程序时的清除操作。代码如下:
: C* ^6 Q2 q0 D$ C4 a; }. ^
- B$ X |* Q4 N8 m5 y Xnamespace ARXExample {0 D1 w8 }* b. Y v: Q$ q
public class MyARX : IExtensionApplication {4 V+ h6 [, t7 N2 p4 c7 u( G
……: \* Q! }. v# |; W$ b) ^
public void Initialize() {& [* t8 {; Z/ w
//初始化操作
2 b, Y% l* ?- H! O# N}
! q) [! i# [( J. m/ qpublic void Terminate() {. @6 b, P: i/ g) H+ W9 `
//清除操作
4 w! W1 H% n8 J* p& p! _, q1 L \}/ C$ o( N$ i3 o2 @. R; q% b
……
7 B! I) { O8 D# b: F; y}: k# ]5 M# |4 a6 C( g) L/ d5 j
}
) E7 _& z, [/ J. v$ m7 B9 C. b2 R- ^, H2 \% E% k/ h7 w
同时,为加快加载速度,可在MyARX.cs的文件头加入以下语句:! ], h" @ |) C0 f8 W8 W# M5 g9 a
$ g$ K% ?4 K: Q1 u3 {[assembly: ExtensionApplication (typeof (ARXExample.MyARX) ) ]1 a7 ^5 x4 }! p: A! x1 A- N
[assembly: CommandClass (typeof (ARXExample.MyARX) ) ]
- k1 k* T g4 K2 w2 H7 L
0 E4 L* X0 o. b# M这样在加载程序时AutoCAD将直接通过MyARX中的Initialize语句进行初始化,同时注册MyARX中的命令。否则,AutoCAD将搜索dll中所有的类以找到实现IExtensionApplication接口的类进行初始化,如找不到则不进行初始化。同样,通过CommandClass属性,AutoCAD也会直接到MyARX类中搜索要注册的命令。当程序中包含的类数目较多时,通过ExtensionApplication和CommandClass这两个属性可显著地加快程序的加载速度。
. s: o# k/ x Z9 J' D
* C5 W9 K0 G( G+ Y1 |1 a5..NET API与COM交互操作# m& S# d: A( f" L
4 N6 O" d" l J) Q; G" A8 u* B3 G在目前的.NET API中,其功能与传统的ObjectARX相比有所不及,有相当的ObjectARX函数目前还没有封装到.NET API中,如GetPoint等。但可以通过COM方式使用ActiveX来弥补.NET API的不足。
( M2 H) f9 I# [4 W( V; u6 A+ O# X. U6 m( R O* ^
增加了COM引用后,程序就可以使用许多VBA中的功能了。以AutoCAD ActiveX中的事件为例,以下代码可以为当前工作空间中所有的图元添加Modified事件:
) S# `/ U3 M7 Q, ^# G1 g) ]; R5 G0 F. ?( r5 N( }
Database db = HostApplicationServices.WorkingDatabase;) c% S' V! ~. P3 c& I) F9 a' Y
BlockTable bt = (BlockTable)db.BlockTableId.Open(OpenMode.ForRead);* |$ a! c# `4 {( F8 P: u
BlockTableRecord btr = (BlockTableRecord)bt[BlockTableRecord.ModelSpace].Open(OpenMode.ForWrite);1 {/ ?: t. ~8 G
try {2 k4 |# B' j) G* a/ L/ B5 N: V
AcadObject obj;' ~# C# i; _/ K6 l0 `7 E
//遍历块表记录; W, d) x8 b# E+ w* v6 U
foreach (ObjectId objId in btr) {0 M* U" m# {, Z) D
//由ObjectId得到ActiveX中的AcadObject对象
% A9 q7 s# ]- ]5 r$ F; u! r7 L" U+ Vobj = (AcadObject)((AcadDatabase)db.AcadDatabase).ObjectIdToObject(objId.OldId);6 v) x+ M' |8 }8 g- x
//为obj添加响应Modified事件- E, @) K5 {2 d. ?' p
obj.Modified += new IAcadObjectEvents_ModifiedEventHandler(obj_Modified);
* f, s) U. z4 L- c}
6 N5 ^+ m" u' M# g; }8 n}
: b1 G4 L0 I* l2 }; _finally {+ k& `1 b: j" V( ~( d5 `
btr.Close();' X" w/ D# x8 Y2 T+ ~4 _
bt.Close();( `5 I o- G6 F6 G4 K! s. O
}
! p* v0 K& m. V+ X& Y
' f6 ^, A$ ~+ [7 q7 l2 e其中事件响应函数obj_Modified的表示如下所示:
0 T/ ]9 ~0 k& o* S
+ Q4 j; [$ v/ K- apublic static void obj_Modified(AcadObject obj) {
: l4 _8 D& o, K: c) lCommandLinePrompts.Message("object modified!" + obj.ObjectID + "\n");
8 ] o7 g) u7 q& T! d: y- V1 @}
: E9 H- w9 p# T. I# E5 ^9 ^( d5 J" K4 p/ \* ] d
二、结论
5 Q$ X+ v2 v' z; I& o3 v! Z5 f/ ?0 X& T2 f
本文以C#为例,对基于.NET API的AutoCAD二次开发作了较详细的介绍。.NET API在具有ObjectARX强大功能的同时具有VBA使用方便易用的优点,同时具有C++的强大功能,是较为理想的开发工具。但目前.NET API在某些方面还有些不足,但随着其版本的更新、完善,定会成为众多开发人员的首选工具。 |