华南理工大学 刘林 麦智晖 阎汉生/ g, M& w4 r; k* n4 h0 w
+ @4 d" x& k2 i# k7 e
/ s! K, O- {% U& i1 [
本文基于AutoCAD 2006新推出的.NET API为工具,介绍了在.NET平台下对AutoCAD进行二次开发的技术,并与目前常用的VBA、ObjectARX作了对比。同时讨论了如何弥补.NET API某些不足的功能。
7 N2 G# `4 d8 R4 G! g, F8 i& `
; }# q& D& d7 T5 d- z: s! o
" P" j8 M8 [4 F, K当前AutoCAD的二次开发工具主要有:VisualLisp、VBA和ObjectARX等。其中,VisualLisp与VBA较为简单,特别是VBA,使用方便且开发速度较快,但其功能相比ObjectARX有所不足,尤其是对面向对象的功能支持不好。而ObjectARX基于VC平台,在C++的支持下,其功能非常强大,可以很好地运用各种面向对象技术,但其缺点是发开速度比较慢,同时对开发人员的能力要求较高。% ^- e1 z: X8 K$ A0 Z; s. f1 ]
/ I* `& p( N G# f$ O9 b* b.NET是微软新推出的开发平台,具有众多优点。基于.NET平台对AutoCAD进行二次开发,可充分利用.NET的各种优势,在保证功能强大的前提下大大提高开发速度。
5 f. o0 F; P: {2 _8 I, v; Q
5 |& B4 i' l0 p一、基于.NET的开发( P5 q$ d( c& g7 f6 K8 f! k
+ X. y& f8 j% ^; Y) _. b
1..NET API简介
1 a* e0 M8 D* g/ t" s& H: c
2 X5 ^; z* ^1 T- e5 ~& k3 e9 r2 }在新推出的AutoCAD 2006中,Autodesk为其开发增加了.NET API。.NET API提供了一系列托管的外包类(Managed Wrapper Class),使开发人员可在.NET框架下,使用任何支持.NET的语言,如VB.NET、C# 和Managed C++等对AutoCAD进行二次开发。其优点是完全面向对象,在拥有与C++相匹配的强大功能的同时,具有方便易用的特点,是较理想的AutoCAD二次开发工具。
; z; R" ^1 G) }( n p3 ]( b+ @/ s
& h- y2 v+ r# Z2 P) D7 W2..NET API与传统ObjectARX的主要区别
2 V8 T8 p$ j0 k# p
( P O. h. I2 I* Q" o. M5 `$ b.NET API与传统ObjectARX的区别主要源于在.NET环境下开发应用程序与在VC环境下开发应用程序的区别。首先,在VC环境下,程序员需要自己管理内存的申请和释放,而.NET采用了垃圾回收机制,由.NET框架自行判断内存回收的时机并实行回收,从而解决了令C++程序员头痛的内存泄漏问题。也正是由于这个特点,在.NET环境下不能象在C++环境下那样利用析构函数释放其他的资源,需要程序员在程序中显式地释放。在.NET API中,主要通过Dispose来函数进行资源的释放。2 P( M& A- ~1 F( `
+ M; F+ H* b( R其次,ObjectARX中的各种反应器(Reactor)在.NET API中由外包类映射为各种事件(Event),可通过定义这些事件的响应函数来响应AutoCAD的各种操作。同时对于错误信息的处理也从函数返回值改变为通常异常来处理,使其更好地兼容.NET。由于VB.NET、C#等语言都是完全面向对象的,没有全局函数的概念,所以.NET API将ObjectARX下的全局函数封装为.NET API下的某些对象或对象的属性,如ObjectARX下与用户交互的系列全局函数被封装为CommandLinePrompt类。
- i+ X" K$ O% q$ `7 M2 P7 m
" W; i- Q+ y8 Z! j) U. _3.使用.NET API
& z ?+ f+ _6 w {
8 h" G4 i. `/ l: P \0 ~* }1 x下面以C#为例,在Microsoft Visual C# 2005 Express Edition Beta平台上,先新建一个Class Library项目,再将AutoCAD2005安装目录下的acdbmgb.dll与acmgb.dll作为引用添加进项目中。这两个文件包含了.NET API中所有的外包类。1 H/ e" K2 p6 J6 o1 w
5 b8 F; \8 b% b% G4 K8 `0 L; d
然后在要使用.NET API的类中添加以下语句以引用.NET API的命名空间。需要添加的语句如下:% k( O# ?2 \( a5 F5 Q6 S2 R
* U7 A& C+ g, R8 u8 Z" T- M8 uusing Autodesk.AutoCAD.ApplicationServices;; P7 z3 j A: T4 U( u. Y9 _0 ]$ ]
using Autodesk.AutoCAD.DatabaseServices;
/ t0 B6 e8 ?* |" a5 k/ cusing Autodesk.AutoCAD.Runtime;
, q& }4 T, `! l1 t) yusing Autodesk.AutoCAD.Geometry;6 R7 `8 Q5 e" {* c, I/ y7 \
3 m" }3 B- z ~9 I# z
这样就可以利用.NET API进行开发了。以下代码可在AutoCAD注册为一个命令"AddLine",该命令可在当前工作空间中添加一条起点为(0,0,0),终点(200,200,0)的直线。代码如下:% ]/ j1 f! l+ g p
. C* B. @* ?" _[CommandMethod ("AddLine")]
/ K! [0 f* [! V4 W, x& Spublic static void AddLineCmd()" G) V% e3 d5 F7 b1 D# o( d/ j- P
{( q% O) X( }6 ^0 x3 \9 t
Database db = HostApplicationServices.WorkingDatabase;//获得当前工作空间的数据库0 H* V4 b* L) @$ g& M
BlockTable bt = (BlockTable)db.BlockTableId.Open(OpenMode.ForRead); //获得块表+ _2 z; p( Y5 ]- `6 ^7 ]6 Z
BlockTableRecord btr = (BlockTableRecord)bt[BlockTableRecord.ModelSpace].Open(OpenMode.ForWrite); //获得模型空间的块表记录2 w4 r+ T8 I& L3 l( M
Line line = new Line(new Point3d(0, 0, 0), new Point3d(200, 200, 0));//创建一条直线- R9 R$ O0 l3 Y5 a2 _: A
try {! N: N- d' m3 {) \/ T) j
btr.AppendEntity(line);//将直线添加到模型空间中
4 p; m- v+ z; b8 Q( ^+ ^ eline.Close();//关闭该直线6 g2 v8 O3 j% |7 r( R* \
}
' a3 B6 g) g. Y1 W" Ufinally {( g/ G+ N& {9 _) a5 K
btr.Close();//关闭块表记录
/ ]- S: ^. F: ?* F6 Y) s" xbt.Close();//关闭块表) O" ^) a. x- s G7 ], I" @
}% ]% p. ?4 E, I7 E6 U9 J
}& j+ p2 M: W6 i" q9 e2 X8 k
$ v) d! l; y9 ]
由此可见,上述语句与在VC下的开发非常类似,其过程都是先得到数据库,然后依次打开块表、块表记录,接着添加实体,最后关闭块表、块表记录。值得注意的是finally语句,无论try块中的语句是否发生异常,finally块中的语句都会被执行,从而确保关闭块表和块表记录的操作会被执行。
; x4 ~& J+ G: U
2 P( _" ?) i/ U/ M% O9 R% k; F2 n( q写完代码后进行编译,编译完成将得到一个dll文件。在AutoCAD 2006中通过"netload"命令即可选择该dll文件进行加载,加载成功后即可以通过"AddLine"命令执行上述代码。遗憾的是目前的.NET API版本还不支持卸载,若要卸载只能关闭AutoCAD。) v, r8 A# X4 R$ R1 S/ t1 d/ z
2 @2 ]' ?! B/ y( I4..NET API的初始化与清除4 x8 [5 S+ x# D' i: y( b& C
- Z. y" O9 J2 J0 q9 S+ i8 q
在ObjectARX中,"acrxEntryPoint"函数是ARX程序的载入点,程序的初始化和清除均可在该函数中进行。而在.NET API中则首先需要将初始化代码封装在一个类中,同时该类需要压迫实现7 K6 I2 u' \! \! Z3 t0 g6 I
* v% ?: F I8 m0 G: l, W' s4 ]7 L
IExtensionApplication接口。该接口包含Initialize与Terminate两个函数。其中Initialize负责加载程序时的初始化操作,Terminate则负责进行卸载程序时的清除操作。代码如下:, \# M) ^/ X0 k
9 c3 K$ R5 q2 s' [8 l! O
namespace ARXExample {
, Z4 ~8 h3 Y, Fpublic class MyARX : IExtensionApplication {
2 s) w- i) S# G3 g! X5 a…… h& K9 s" p3 A+ \+ X8 ]1 V( H
public void Initialize() {! g0 A$ T( ^1 P1 F) }( ?
//初始化操作& H# B, o) w7 c' C! ?) J# s
}& [ u. M% S! C7 r* h9 K6 Y, o
public void Terminate() {
) G- N# }* T/ T8 a6 q1 E2 u |1 s//清除操作
. h2 m! L: `. ]4 H7 Z4 r& G0 c}: D: Y. c, g) B6 D) I
……# S' u; ]8 b. e2 o3 ]
}
4 p- k' @( l) q" q: m" Z. H; E- a, H}. G" H' J1 w, N7 g
6 Q1 S; f/ P* k同时,为加快加载速度,可在MyARX.cs的文件头加入以下语句:3 F0 `& P( F( Y3 a8 t3 I7 H
|) z* U' E5 O9 V! s[assembly: ExtensionApplication (typeof (ARXExample.MyARX) ) ]
0 h# E6 F* R+ d) [- q" Y[assembly: CommandClass (typeof (ARXExample.MyARX) ) ]4 P( p$ c2 q9 B
6 l' U9 H) [% [6 K" Y; {5 a5 f
这样在加载程序时AutoCAD将直接通过MyARX中的Initialize语句进行初始化,同时注册MyARX中的命令。否则,AutoCAD将搜索dll中所有的类以找到实现IExtensionApplication接口的类进行初始化,如找不到则不进行初始化。同样,通过CommandClass属性,AutoCAD也会直接到MyARX类中搜索要注册的命令。当程序中包含的类数目较多时,通过ExtensionApplication和CommandClass这两个属性可显著地加快程序的加载速度。
- K |* s- n! @. H7 q% m) O8 m+ J" n, o3 D N$ A- R5 A, ^
5..NET API与COM交互操作
& k6 y+ r1 X z0 v' p, ]- [% _% E! L
在目前的.NET API中,其功能与传统的ObjectARX相比有所不及,有相当的ObjectARX函数目前还没有封装到.NET API中,如GetPoint等。但可以通过COM方式使用ActiveX来弥补.NET API的不足。. ?# |# H! H9 z. q$ B
6 f i; L+ f8 A: { |
增加了COM引用后,程序就可以使用许多VBA中的功能了。以AutoCAD ActiveX中的事件为例,以下代码可以为当前工作空间中所有的图元添加Modified事件:: N/ k( G* p% a/ |
& @- V3 I! @1 j \6 T
Database db = HostApplicationServices.WorkingDatabase;# _) q( X3 `5 L$ W# q" N
BlockTable bt = (BlockTable)db.BlockTableId.Open(OpenMode.ForRead);5 Q7 _& E* [' `8 ?# s6 J
BlockTableRecord btr = (BlockTableRecord)bt[BlockTableRecord.ModelSpace].Open(OpenMode.ForWrite);
- r1 y2 q# k. btry {. o k9 S* [) a9 X. b! B* C6 A j
AcadObject obj;
' O3 h& E/ i0 M% B. ~# o% b//遍历块表记录
% Z, Q: C, K9 M, Y4 Nforeach (ObjectId objId in btr) {- Q) @; P: P! n0 q C8 D5 K. B) a9 T6 { M
//由ObjectId得到ActiveX中的AcadObject对象
# G/ c1 n( J+ ` {obj = (AcadObject)((AcadDatabase)db.AcadDatabase).ObjectIdToObject(objId.OldId);& `# ~$ \8 N7 \3 y! Q7 H
//为obj添加响应Modified事件. k3 }3 w; w0 `! [8 \& B
obj.Modified += new IAcadObjectEvents_ModifiedEventHandler(obj_Modified);
$ L( f+ ]" T3 U9 v1 Q}, a/ u/ W. h* q9 s/ @- ]
}# ~8 o* }2 p0 I" F3 D% w) I0 D0 p
finally {
1 ]6 g/ X/ f0 u& kbtr.Close();
0 n# g2 y! F& C, ^" D4 J3 |bt.Close();% h) C' z9 |8 |" Q
}- P( i0 r) |# L, ~5 P+ N4 P) J5 p
& e/ R$ x: _+ R7 u; T其中事件响应函数obj_Modified的表示如下所示:
! @ z7 o. ~1 b4 J O
0 c7 k& T/ V7 d& F) }% K; I+ Z spublic static void obj_Modified(AcadObject obj) {9 d7 E5 A1 G! `* A5 A. z
CommandLinePrompts.Message("object modified!" + obj.ObjectID + "\n");
+ G# {- N0 M' U" q8 O8 N, d* @ s}
5 h L: Y& F9 E: n6 X" @9 \) y& Z$ V
二、结论
8 `6 x- [+ @+ c+ P4 t4 i/ t
( M8 ?2 R, n, [ o9 _ F本文以C#为例,对基于.NET API的AutoCAD二次开发作了较详细的介绍。.NET API在具有ObjectARX强大功能的同时具有VBA使用方便易用的优点,同时具有C++的强大功能,是较为理想的开发工具。但目前.NET API在某些方面还有些不足,但随着其版本的更新、完善,定会成为众多开发人员的首选工具。 |