华南理工大学 刘林 麦智晖 阎汉生
# q* @2 O, T, R/ a3 D2 T0 f5 B$ ?+ r& C
/ \- [' B" A% r' j. h本文基于AutoCAD 2006新推出的.NET API为工具,介绍了在.NET平台下对AutoCAD进行二次开发的技术,并与目前常用的VBA、ObjectARX作了对比。同时讨论了如何弥补.NET API某些不足的功能。 # W2 g7 |5 T) a% p/ K+ \8 r+ n
: ^% H' d. @3 n' I" ]
. r7 N8 n/ I" h) ?/ x$ N+ ~' M当前AutoCAD的二次开发工具主要有:VisualLisp、VBA和ObjectARX等。其中,VisualLisp与VBA较为简单,特别是VBA,使用方便且开发速度较快,但其功能相比ObjectARX有所不足,尤其是对面向对象的功能支持不好。而ObjectARX基于VC平台,在C++的支持下,其功能非常强大,可以很好地运用各种面向对象技术,但其缺点是发开速度比较慢,同时对开发人员的能力要求较高。' U _+ Y" S& s" n3 @
$ g: s) U8 u7 [4 Q, _. S.NET是微软新推出的开发平台,具有众多优点。基于.NET平台对AutoCAD进行二次开发,可充分利用.NET的各种优势,在保证功能强大的前提下大大提高开发速度。
1 N9 m) _- t' H3 u' d8 d z+ P7 @' C+ J' b. C$ y0 N1 ~
一、基于.NET的开发0 a9 ?+ ^1 D, g( V& `
% a6 V6 }2 ]1 {9 P5 t( s2 K1..NET API简介
& w. z1 D1 v" u6 C0 w' c8 M: F, l. m* v
在新推出的AutoCAD 2006中,Autodesk为其开发增加了.NET API。.NET API提供了一系列托管的外包类(Managed Wrapper Class),使开发人员可在.NET框架下,使用任何支持.NET的语言,如VB.NET、C# 和Managed C++等对AutoCAD进行二次开发。其优点是完全面向对象,在拥有与C++相匹配的强大功能的同时,具有方便易用的特点,是较理想的AutoCAD二次开发工具。( J! s% \" p: h2 d; o7 G9 \" M
# A e. N( f! k$ k1 L: B# U9 g2..NET API与传统ObjectARX的主要区别
, m$ Y2 G' \5 S A. ~ [2 O* b& Q: D. M; [2 D* l5 k, y
.NET API与传统ObjectARX的区别主要源于在.NET环境下开发应用程序与在VC环境下开发应用程序的区别。首先,在VC环境下,程序员需要自己管理内存的申请和释放,而.NET采用了垃圾回收机制,由.NET框架自行判断内存回收的时机并实行回收,从而解决了令C++程序员头痛的内存泄漏问题。也正是由于这个特点,在.NET环境下不能象在C++环境下那样利用析构函数释放其他的资源,需要程序员在程序中显式地释放。在.NET API中,主要通过Dispose来函数进行资源的释放。# A! u. ^& v) e0 l0 f
# G( A2 p( `* S2 d其次,ObjectARX中的各种反应器(Reactor)在.NET API中由外包类映射为各种事件(Event),可通过定义这些事件的响应函数来响应AutoCAD的各种操作。同时对于错误信息的处理也从函数返回值改变为通常异常来处理,使其更好地兼容.NET。由于VB.NET、C#等语言都是完全面向对象的,没有全局函数的概念,所以.NET API将ObjectARX下的全局函数封装为.NET API下的某些对象或对象的属性,如ObjectARX下与用户交互的系列全局函数被封装为CommandLinePrompt类。' f- O/ Z' l2 m$ R. ^
0 F+ x6 j; F6 J% A3.使用.NET API. l% [& ?/ h' ~. W
8 D3 a3 d* {% @# P5 X下面以C#为例,在Microsoft Visual C# 2005 Express Edition Beta平台上,先新建一个Class Library项目,再将AutoCAD2005安装目录下的acdbmgb.dll与acmgb.dll作为引用添加进项目中。这两个文件包含了.NET API中所有的外包类。% T0 P o3 |* V7 g( {/ Q+ m
% U. r- F& \, V k4 y: @然后在要使用.NET API的类中添加以下语句以引用.NET API的命名空间。需要添加的语句如下:
0 l# B( U6 p' c6 }* y7 y$ O. V1 D/ D5 f6 F5 W3 f4 x7 y E7 ~
using Autodesk.AutoCAD.ApplicationServices;
; h4 ^, g& v/ R, M6 Pusing Autodesk.AutoCAD.DatabaseServices;
6 A3 V& F7 N, x8 pusing Autodesk.AutoCAD.Runtime;
3 \, Z3 M) t1 `8 s" Dusing Autodesk.AutoCAD.Geometry;
) P* b b$ ^- l9 j4 i7 N: X% l. [7 V5 `- c2 a2 T, }. g* W1 G) a, `4 A
这样就可以利用.NET API进行开发了。以下代码可在AutoCAD注册为一个命令"AddLine",该命令可在当前工作空间中添加一条起点为(0,0,0),终点(200,200,0)的直线。代码如下: i4 ]$ c; u; I/ @9 f2 W1 N, d. `
$ k5 S6 a: Q2 g
[CommandMethod ("AddLine")]4 }/ g4 \: D, t+ Q- n; K
public static void AddLineCmd()
2 \* v, h- N. f" E4 a2 | u{
6 J9 u8 L6 j2 f3 g9 RDatabase db = HostApplicationServices.WorkingDatabase;//获得当前工作空间的数据库
$ x$ P0 P3 b) V0 e- h1 y3 OBlockTable bt = (BlockTable)db.BlockTableId.Open(OpenMode.ForRead); //获得块表8 y3 s7 e6 l% E- f7 D0 u
BlockTableRecord btr = (BlockTableRecord)bt[BlockTableRecord.ModelSpace].Open(OpenMode.ForWrite); //获得模型空间的块表记录
R, P* _8 [2 ]- g; xLine line = new Line(new Point3d(0, 0, 0), new Point3d(200, 200, 0));//创建一条直线
' `$ ]9 [) X& T- [6 r3 S( Xtry {: Z; z- u# E5 [% J
btr.AppendEntity(line);//将直线添加到模型空间中$ Q9 X8 K2 q3 R3 n
line.Close();//关闭该直线8 d- H& M, n/ b" i
}
# O1 I% C) N$ [2 E5 rfinally {! @! m% O' e7 q+ Q
btr.Close();//关闭块表记录
9 q+ `2 ~5 \4 }9 g7 ^" d* Obt.Close();//关闭块表; F' B/ m" B s5 V/ \3 ?" v
}- Q7 r( D& Q' i* _3 y+ ~) A
}, ?5 ^ w4 E4 e! ^/ P, N
3 P8 W6 N7 A+ s0 B由此可见,上述语句与在VC下的开发非常类似,其过程都是先得到数据库,然后依次打开块表、块表记录,接着添加实体,最后关闭块表、块表记录。值得注意的是finally语句,无论try块中的语句是否发生异常,finally块中的语句都会被执行,从而确保关闭块表和块表记录的操作会被执行。
! n" X4 p2 [. E
8 m: Y7 s' A' l/ D) Y3 b0 t写完代码后进行编译,编译完成将得到一个dll文件。在AutoCAD 2006中通过"netload"命令即可选择该dll文件进行加载,加载成功后即可以通过"AddLine"命令执行上述代码。遗憾的是目前的.NET API版本还不支持卸载,若要卸载只能关闭AutoCAD。, S& o7 S I8 p; [
( g v/ D0 H% R8 t4 q7 Q1 m* }4..NET API的初始化与清除 v. c" r) X; }5 J& U1 X+ m% E7 b
' ~" g4 Y; h3 n1 j. x在ObjectARX中,"acrxEntryPoint"函数是ARX程序的载入点,程序的初始化和清除均可在该函数中进行。而在.NET API中则首先需要将初始化代码封装在一个类中,同时该类需要压迫实现
5 \9 }' A* c# p2 n+ }4 M$ g9 W2 M
# N' }# M; {" z% m. i4 {IExtensionApplication接口。该接口包含Initialize与Terminate两个函数。其中Initialize负责加载程序时的初始化操作,Terminate则负责进行卸载程序时的清除操作。代码如下:$ F/ E# q/ n& s5 n: V) P9 [
+ ], k. _8 ~% _3 v0 p# X4 M$ {& B
namespace ARXExample {
4 [% t6 B$ x& l F4 Cpublic class MyARX : IExtensionApplication {! [/ l" |" |; q1 @7 O
……
# \0 ~& j+ }4 l( q' p0 ^3 |- l _$ cpublic void Initialize() {
$ X9 O/ A( M# v. H" |! v+ K) \//初始化操作
$ { l7 z) c3 ^3 w3 t9 r( R ?% X}
) O$ X0 _$ | m6 ]public void Terminate() {" b3 b& _+ y W% [$ e" F3 `
//清除操作 H5 J9 H6 J/ _ _% x
}
2 f- f" m) e, N……$ I" a( V+ }# m6 j4 p
}7 J% R: ?2 v* S; U8 T2 K5 y
}
. R* d8 U: [& M: f' W8 B
* m/ _1 k! t2 Z4 i, f# b( ?同时,为加快加载速度,可在MyARX.cs的文件头加入以下语句:5 S4 X5 c! L7 W0 ]' l6 l5 B+ t3 X
# k0 _2 K$ L# s& h9 U% ~: V: |
[assembly: ExtensionApplication (typeof (ARXExample.MyARX) ) ]$ n0 t8 V- N p4 x4 k' O/ d0 E' Y% _
[assembly: CommandClass (typeof (ARXExample.MyARX) ) ]4 h& w7 d. |' {' j: W4 p
8 a$ S' h; l! B$ C这样在加载程序时AutoCAD将直接通过MyARX中的Initialize语句进行初始化,同时注册MyARX中的命令。否则,AutoCAD将搜索dll中所有的类以找到实现IExtensionApplication接口的类进行初始化,如找不到则不进行初始化。同样,通过CommandClass属性,AutoCAD也会直接到MyARX类中搜索要注册的命令。当程序中包含的类数目较多时,通过ExtensionApplication和CommandClass这两个属性可显著地加快程序的加载速度。3 X5 ^3 o7 U3 @0 `
8 [1 q" K- `: P
5..NET API与COM交互操作& X u! u7 |* @0 `3 Z" Y8 r2 @
8 b; Z4 ` `" |2 ]+ ~7 P
在目前的.NET API中,其功能与传统的ObjectARX相比有所不及,有相当的ObjectARX函数目前还没有封装到.NET API中,如GetPoint等。但可以通过COM方式使用ActiveX来弥补.NET API的不足。
! [* J- z( ?4 ?! V& k
1 ?# Q3 a" J B' L% {增加了COM引用后,程序就可以使用许多VBA中的功能了。以AutoCAD ActiveX中的事件为例,以下代码可以为当前工作空间中所有的图元添加Modified事件:
( t* W8 t; L- f/ Z- R( r) i! b* k+ `2 Q N! W
Database db = HostApplicationServices.WorkingDatabase;' L! I+ \8 ~3 O: E
BlockTable bt = (BlockTable)db.BlockTableId.Open(OpenMode.ForRead);
- X+ U2 z2 D- v3 kBlockTableRecord btr = (BlockTableRecord)bt[BlockTableRecord.ModelSpace].Open(OpenMode.ForWrite);9 F$ R K% \6 s' @; s. [6 m
try {! G% K) ~) X4 R. U* H% O
AcadObject obj;
6 }( b/ a$ t, W) s1 n$ R: W6 R" |//遍历块表记录
* N) }! B% g9 bforeach (ObjectId objId in btr) {
& y1 }$ i& Z# d0 B' ~, u//由ObjectId得到ActiveX中的AcadObject对象2 S8 A9 c5 d x9 Z
obj = (AcadObject)((AcadDatabase)db.AcadDatabase).ObjectIdToObject(objId.OldId);
7 A9 u' I6 d4 x' v8 ~% I: I9 M//为obj添加响应Modified事件
! u6 Z" ?' n% k3 iobj.Modified += new IAcadObjectEvents_ModifiedEventHandler(obj_Modified);
; H* V; G3 `2 C6 ]}
! K$ {( j2 @) P5 U! [& i}
2 _! r6 p9 I5 Z! ?5 [: k8 ifinally {
. Q: O7 e) I5 \btr.Close();6 J; ]. Z5 o2 }8 ^2 C
bt.Close();
$ p$ g' X1 l( d}+ i( D# I# z; h3 }1 A) ~# b) g
7 _9 v C5 V/ L
其中事件响应函数obj_Modified的表示如下所示:
6 G; X0 U7 f2 U; l. ^. B; u! K- q6 G$ U8 A' E1 h
public static void obj_Modified(AcadObject obj) {7 f( x; o8 X" f5 ?1 U% S
CommandLinePrompts.Message("object modified!" + obj.ObjectID + "\n");! k3 H( s3 s9 _/ }/ x
}
( u, [" X6 T* H4 H6 O' C. T& U' v0 V* o& A
二、结论
N2 ~! d2 ]: q! W3 t. F- ?* S, a4 i" x# i u- J: F6 k, H
本文以C#为例,对基于.NET API的AutoCAD二次开发作了较详细的介绍。.NET API在具有ObjectARX强大功能的同时具有VBA使用方便易用的优点,同时具有C++的强大功能,是较为理想的开发工具。但目前.NET API在某些方面还有些不足,但随着其版本的更新、完善,定会成为众多开发人员的首选工具。 |