华南理工大学 刘林 麦智晖 阎汉生
) ]6 k0 P. _# B% N1 y+ J2 e- l6 z3 `2 D
" g5 D/ U3 W; N. G7 Z6 r$ B
本文基于AutoCAD 2006新推出的.NET API为工具,介绍了在.NET平台下对AutoCAD进行二次开发的技术,并与目前常用的VBA、ObjectARX作了对比。同时讨论了如何弥补.NET API某些不足的功能。 - I1 F% u7 B4 Z4 X6 S
0 R1 o1 t* x1 {% w
* w9 H* C7 @# z$ G' P6 z0 ^' i当前AutoCAD的二次开发工具主要有:VisualLisp、VBA和ObjectARX等。其中,VisualLisp与VBA较为简单,特别是VBA,使用方便且开发速度较快,但其功能相比ObjectARX有所不足,尤其是对面向对象的功能支持不好。而ObjectARX基于VC平台,在C++的支持下,其功能非常强大,可以很好地运用各种面向对象技术,但其缺点是发开速度比较慢,同时对开发人员的能力要求较高。+ B f: S( R! Z" y( t
2 v% _" y( q* d- f8 [
.NET是微软新推出的开发平台,具有众多优点。基于.NET平台对AutoCAD进行二次开发,可充分利用.NET的各种优势,在保证功能强大的前提下大大提高开发速度。
# s7 M/ H* {' N: l1 \
: v5 u) Z+ j+ H9 T; U1 W" P# \一、基于.NET的开发
m9 I, G% U( d# j/ w' @3 H7 a% a& }7 O: e" F$ t u2 l* l
1..NET API简介
' }! `1 c% Z H% _) M. W7 F0 s% |
. _7 f0 j$ C$ k1 b4 S在新推出的AutoCAD 2006中,Autodesk为其开发增加了.NET API。.NET API提供了一系列托管的外包类(Managed Wrapper Class),使开发人员可在.NET框架下,使用任何支持.NET的语言,如VB.NET、C# 和Managed C++等对AutoCAD进行二次开发。其优点是完全面向对象,在拥有与C++相匹配的强大功能的同时,具有方便易用的特点,是较理想的AutoCAD二次开发工具。
) d/ U2 I$ w8 N/ u7 P0 v, A- T* ]
* V, O9 l( v0 t- a9 A. J, d2..NET API与传统ObjectARX的主要区别
) a" m4 l4 a6 `0 }! x% a4 @0 g8 b3 ^; @4 ?5 e l+ z. g& F
.NET API与传统ObjectARX的区别主要源于在.NET环境下开发应用程序与在VC环境下开发应用程序的区别。首先,在VC环境下,程序员需要自己管理内存的申请和释放,而.NET采用了垃圾回收机制,由.NET框架自行判断内存回收的时机并实行回收,从而解决了令C++程序员头痛的内存泄漏问题。也正是由于这个特点,在.NET环境下不能象在C++环境下那样利用析构函数释放其他的资源,需要程序员在程序中显式地释放。在.NET API中,主要通过Dispose来函数进行资源的释放。" ^1 T+ n' B2 _6 `- Z
$ H/ M1 J6 R! L; b0 I$ C
其次,ObjectARX中的各种反应器(Reactor)在.NET API中由外包类映射为各种事件(Event),可通过定义这些事件的响应函数来响应AutoCAD的各种操作。同时对于错误信息的处理也从函数返回值改变为通常异常来处理,使其更好地兼容.NET。由于VB.NET、C#等语言都是完全面向对象的,没有全局函数的概念,所以.NET API将ObjectARX下的全局函数封装为.NET API下的某些对象或对象的属性,如ObjectARX下与用户交互的系列全局函数被封装为CommandLinePrompt类。
! R3 k) n/ D; y8 B8 Q* L0 Y" j% C
& [* M: i" r$ N3.使用.NET API: J7 v. K: h, U# ~8 j0 @7 m
$ i, o0 O* g% l, m! A! |# R1 C4 P下面以C#为例,在Microsoft Visual C# 2005 Express Edition Beta平台上,先新建一个Class Library项目,再将AutoCAD2005安装目录下的acdbmgb.dll与acmgb.dll作为引用添加进项目中。这两个文件包含了.NET API中所有的外包类。
: ~. U: S9 W, y4 J. i) L+ ]! ]% l0 R+ I/ W( Q
然后在要使用.NET API的类中添加以下语句以引用.NET API的命名空间。需要添加的语句如下:4 O$ a3 M7 Y+ x' I; W
) M5 k) R. h; ~5 g3 y8 ] Y: D
using Autodesk.AutoCAD.ApplicationServices;6 f, @2 L6 {8 B, v* o7 @2 n
using Autodesk.AutoCAD.DatabaseServices;3 V# x* Q+ p! z. i# l! D2 H
using Autodesk.AutoCAD.Runtime;) F* R7 g' U1 Z
using Autodesk.AutoCAD.Geometry;
7 q+ m1 u/ C% E$ R
9 A4 ^: F$ H& p3 S1 f- L2 C这样就可以利用.NET API进行开发了。以下代码可在AutoCAD注册为一个命令"AddLine",该命令可在当前工作空间中添加一条起点为(0,0,0),终点(200,200,0)的直线。代码如下:& p) Z- j+ d0 T- N/ W) U
T& G$ x9 R+ a8 i% s7 g% [! t[CommandMethod ("AddLine")]3 O5 A' d# o/ V5 A [1 J1 }
public static void AddLineCmd()) T( H, j, k6 e* M6 W( A% ~# H
{' l2 ~4 Z1 C. N
Database db = HostApplicationServices.WorkingDatabase;//获得当前工作空间的数据库
' p. O7 l' Z" D+ r Z) t8 gBlockTable bt = (BlockTable)db.BlockTableId.Open(OpenMode.ForRead); //获得块表. u. K( m" m. i6 o/ q
BlockTableRecord btr = (BlockTableRecord)bt[BlockTableRecord.ModelSpace].Open(OpenMode.ForWrite); //获得模型空间的块表记录! h5 h8 [1 n4 p2 R; J
Line line = new Line(new Point3d(0, 0, 0), new Point3d(200, 200, 0));//创建一条直线
6 v3 | f5 Z/ @6 G$ ltry {
" x8 Z; ]$ O+ x d+ i" |" E8 R4 |# [6 qbtr.AppendEntity(line);//将直线添加到模型空间中8 R- s9 M$ \( j. N4 _
line.Close();//关闭该直线
, K3 e, F; W/ r$ t' o}
) ^5 T7 y1 \1 o# u$ [/ L- e# \finally {
. @. s$ x8 Y o j. Z/ ?btr.Close();//关闭块表记录5 s! O6 ]6 t, j& ^: J* D5 c
bt.Close();//关闭块表6 ?. G1 y U; m' k
}
0 x, K2 y# v- {$ g9 k: f}
6 Y: M# P; f, I, j$ j `
+ {0 d1 j" ^9 V- I* u由此可见,上述语句与在VC下的开发非常类似,其过程都是先得到数据库,然后依次打开块表、块表记录,接着添加实体,最后关闭块表、块表记录。值得注意的是finally语句,无论try块中的语句是否发生异常,finally块中的语句都会被执行,从而确保关闭块表和块表记录的操作会被执行。
) z+ @% {8 Q/ E1 l- k7 L" e9 k
7 w2 `9 V9 S O2 H* f4 z1 r- H' |; i; E" A写完代码后进行编译,编译完成将得到一个dll文件。在AutoCAD 2006中通过"netload"命令即可选择该dll文件进行加载,加载成功后即可以通过"AddLine"命令执行上述代码。遗憾的是目前的.NET API版本还不支持卸载,若要卸载只能关闭AutoCAD。) O6 s& M, h5 w1 H5 z
/ @& V8 }# ]1 E" X& E3 x4..NET API的初始化与清除" P, K% r8 q, h; B( x. Z
( k3 t* u9 D$ a在ObjectARX中,"acrxEntryPoint"函数是ARX程序的载入点,程序的初始化和清除均可在该函数中进行。而在.NET API中则首先需要将初始化代码封装在一个类中,同时该类需要压迫实现
% y5 ?* ]( H& X1 ]6 `0 R, C
- i, @/ W! z6 ^; V1 J* D. KIExtensionApplication接口。该接口包含Initialize与Terminate两个函数。其中Initialize负责加载程序时的初始化操作,Terminate则负责进行卸载程序时的清除操作。代码如下:
, D/ E3 ^' D I
& `0 [# }' ^7 O, fnamespace ARXExample {: o5 g# B. j$ c9 L: C8 Y" o
public class MyARX : IExtensionApplication {3 \7 u! p$ x" Z; O/ g: L
……
' {' l* A2 A. t+ Apublic void Initialize() {
. j, I8 b, ^% ]* \7 `8 E% ~* }//初始化操作- V5 K5 ~3 A2 v& R7 n& U( J9 @" O
}; d- l6 v" w+ R
public void Terminate() {
$ s V& m: B0 k- `! c9 z) [7 U9 ~//清除操作8 g$ r5 S U: t( j: Y
}
& J Q( O, U) {9 p' D0 r& F( x$ d……) g) h# z0 u1 e
}) ?* d6 y" P q% F% f! }' F3 @
}
9 `/ p$ p/ @7 l0 t1 C: x! _8 z4 }4 i E* o" d6 r: j, x
同时,为加快加载速度,可在MyARX.cs的文件头加入以下语句:: H1 W2 M9 R6 e0 w; a3 s- {
9 A1 Z1 c& G* E; }- b& x& o+ a
[assembly: ExtensionApplication (typeof (ARXExample.MyARX) ) ]
% g6 A$ ~" S9 j' v2 A. D- g; X[assembly: CommandClass (typeof (ARXExample.MyARX) ) ]; a4 |2 W0 g" w+ X4 h6 W1 C, O$ v
( ]; o% m+ v! Z+ r( k6 u: v O这样在加载程序时AutoCAD将直接通过MyARX中的Initialize语句进行初始化,同时注册MyARX中的命令。否则,AutoCAD将搜索dll中所有的类以找到实现IExtensionApplication接口的类进行初始化,如找不到则不进行初始化。同样,通过CommandClass属性,AutoCAD也会直接到MyARX类中搜索要注册的命令。当程序中包含的类数目较多时,通过ExtensionApplication和CommandClass这两个属性可显著地加快程序的加载速度。. k( [2 q+ T8 u
0 y4 Y* {; M/ d9 h/ Y( O5 h0 K* V
5..NET API与COM交互操作
/ E2 @# s1 f! J$ e
" }$ Q8 F5 r$ ?3 |( K1 y3 X9 f" W在目前的.NET API中,其功能与传统的ObjectARX相比有所不及,有相当的ObjectARX函数目前还没有封装到.NET API中,如GetPoint等。但可以通过COM方式使用ActiveX来弥补.NET API的不足。
8 |9 G- H2 p( m% X2 k4 E% B, X \- z7 r3 [, q+ p5 d. ]
增加了COM引用后,程序就可以使用许多VBA中的功能了。以AutoCAD ActiveX中的事件为例,以下代码可以为当前工作空间中所有的图元添加Modified事件:; h, E8 s0 m& p9 c2 s
7 C) V. \+ x! k+ nDatabase db = HostApplicationServices.WorkingDatabase;5 [; Q- y' H$ H& ^& E( b* Y
BlockTable bt = (BlockTable)db.BlockTableId.Open(OpenMode.ForRead);& K4 H+ b3 T) e
BlockTableRecord btr = (BlockTableRecord)bt[BlockTableRecord.ModelSpace].Open(OpenMode.ForWrite);
- p/ m h2 n3 D9 j/ a! Jtry {& ~5 z5 U6 Y' }8 [5 w2 X" m- \
AcadObject obj;- v9 j% U1 x+ m" E0 {% n
//遍历块表记录
( j- g$ h$ N9 w+ [ nforeach (ObjectId objId in btr) {
' M0 \/ W0 A. E& p- ^: x//由ObjectId得到ActiveX中的AcadObject对象 ^9 B0 V! P9 [% j( B
obj = (AcadObject)((AcadDatabase)db.AcadDatabase).ObjectIdToObject(objId.OldId);) ?! n3 p: s( n" D/ v/ ~+ _8 w
//为obj添加响应Modified事件$ s" c4 ]- v- K
obj.Modified += new IAcadObjectEvents_ModifiedEventHandler(obj_Modified);+ ?: |# J: r6 D
}( L0 i8 t( |* `, \+ ~
}; K/ [! `* I- p; ~& C4 [
finally {% Q1 D3 ?* t# B: O: A0 q& A
btr.Close();0 p0 A- ^$ m+ b9 ~3 z5 B2 h$ d
bt.Close();
& p1 U B9 W2 A g3 `/ }0 q1 q- O}5 y' F# n7 @" S# x! e+ x1 C0 }* x
8 r- h2 U4 i& Z/ p! D3 O9 v其中事件响应函数obj_Modified的表示如下所示:% g& g8 r- p$ Y) ?6 W/ o
) I3 C" P" w! Y7 m! t1 I1 Opublic static void obj_Modified(AcadObject obj) {5 g0 R( ~0 F5 Q
CommandLinePrompts.Message("object modified!" + obj.ObjectID + "\n");3 t& P% S) u2 R. _4 S, n& G- k! k
}
/ _/ t5 i# A4 Q% \6 g- a
! |" n8 B# E* w二、结论" e. ? k( F6 f3 t `
7 @8 N4 m" a- ^. y/ m本文以C#为例,对基于.NET API的AutoCAD二次开发作了较详细的介绍。.NET API在具有ObjectARX强大功能的同时具有VBA使用方便易用的优点,同时具有C++的强大功能,是较为理想的开发工具。但目前.NET API在某些方面还有些不足,但随着其版本的更新、完善,定会成为众多开发人员的首选工具。 |