华南理工大学 刘林 麦智晖 阎汉生9 B/ h7 ~* A* A m- z# D% j$ d% b# `
L, h' ]2 S% Q
# f/ W% F) ]" H: E+ n/ K本文基于AutoCAD 2006新推出的.NET API为工具,介绍了在.NET平台下对AutoCAD进行二次开发的技术,并与目前常用的VBA、ObjectARX作了对比。同时讨论了如何弥补.NET API某些不足的功能。 ) M% g( g1 M/ B, C
' C# Z) l2 j: N% s& s4 l1 j+ c1 H' P
当前AutoCAD的二次开发工具主要有:VisualLisp、VBA和ObjectARX等。其中,VisualLisp与VBA较为简单,特别是VBA,使用方便且开发速度较快,但其功能相比ObjectARX有所不足,尤其是对面向对象的功能支持不好。而ObjectARX基于VC平台,在C++的支持下,其功能非常强大,可以很好地运用各种面向对象技术,但其缺点是发开速度比较慢,同时对开发人员的能力要求较高。
+ |, H7 U6 x' n) u; x
' u; H- {) t: S( R.NET是微软新推出的开发平台,具有众多优点。基于.NET平台对AutoCAD进行二次开发,可充分利用.NET的各种优势,在保证功能强大的前提下大大提高开发速度。- ]+ R5 R5 U* ?
, w* ^3 c8 O& T) w- |) @" y一、基于.NET的开发
$ k/ L/ k- ?! r- h
+ B6 s: T& v- a, P( e0 Q1..NET API简介
) r/ g& W$ b V, p+ q: ]: m3 D! I0 [
在新推出的AutoCAD 2006中,Autodesk为其开发增加了.NET API。.NET API提供了一系列托管的外包类(Managed Wrapper Class),使开发人员可在.NET框架下,使用任何支持.NET的语言,如VB.NET、C# 和Managed C++等对AutoCAD进行二次开发。其优点是完全面向对象,在拥有与C++相匹配的强大功能的同时,具有方便易用的特点,是较理想的AutoCAD二次开发工具。
' Q- J# ]$ _7 _# A# q" D4 a# R6 ?' L3 {0 c7 G1 C' l) w6 y
2..NET API与传统ObjectARX的主要区别, w5 f) a, U1 ]: E
) v) o8 ]5 n# A% n, @.NET API与传统ObjectARX的区别主要源于在.NET环境下开发应用程序与在VC环境下开发应用程序的区别。首先,在VC环境下,程序员需要自己管理内存的申请和释放,而.NET采用了垃圾回收机制,由.NET框架自行判断内存回收的时机并实行回收,从而解决了令C++程序员头痛的内存泄漏问题。也正是由于这个特点,在.NET环境下不能象在C++环境下那样利用析构函数释放其他的资源,需要程序员在程序中显式地释放。在.NET API中,主要通过Dispose来函数进行资源的释放。
2 i% x! d& z# [; v. b/ q2 {
, l" M, _# \9 T% @其次,ObjectARX中的各种反应器(Reactor)在.NET API中由外包类映射为各种事件(Event),可通过定义这些事件的响应函数来响应AutoCAD的各种操作。同时对于错误信息的处理也从函数返回值改变为通常异常来处理,使其更好地兼容.NET。由于VB.NET、C#等语言都是完全面向对象的,没有全局函数的概念,所以.NET API将ObjectARX下的全局函数封装为.NET API下的某些对象或对象的属性,如ObjectARX下与用户交互的系列全局函数被封装为CommandLinePrompt类。5 s% i* U% r0 k: ` g" c4 `
( ^7 t0 B) `# C p: J
3.使用.NET API6 i0 t" l, R3 s+ l
7 q9 ^! q5 K! D' s, t5 R" _+ g1 j7 ~
下面以C#为例,在Microsoft Visual C# 2005 Express Edition Beta平台上,先新建一个Class Library项目,再将AutoCAD2005安装目录下的acdbmgb.dll与acmgb.dll作为引用添加进项目中。这两个文件包含了.NET API中所有的外包类。
7 m, i6 Z @% j+ w* z4 u. e( c
+ X. s6 \2 D) s( ^然后在要使用.NET API的类中添加以下语句以引用.NET API的命名空间。需要添加的语句如下:
( e+ n2 p1 f8 ^& j$ a4 [6 _) r2 X$ U( L7 X3 o+ m2 x
using Autodesk.AutoCAD.ApplicationServices;
8 k8 e. l$ |) h0 busing Autodesk.AutoCAD.DatabaseServices;. X8 I/ D& @2 G$ Y( x# Q4 j, {
using Autodesk.AutoCAD.Runtime;: i$ s- [6 h+ Z; m9 G0 P
using Autodesk.AutoCAD.Geometry;4 h0 ?) r* q3 H* J" q" c
u9 O- a! ?# j8 J3 P, P4 a这样就可以利用.NET API进行开发了。以下代码可在AutoCAD注册为一个命令"AddLine",该命令可在当前工作空间中添加一条起点为(0,0,0),终点(200,200,0)的直线。代码如下:
& S" j5 c2 X' s3 o, t1 _0 J/ K, E* U/ U3 P
[CommandMethod ("AddLine")]/ K' e5 g4 B [5 @; a5 C' H9 K; F3 y
public static void AddLineCmd()
, q3 g& h$ W1 L9 M! E{
' D* `+ F* r9 J. U7 |2 WDatabase db = HostApplicationServices.WorkingDatabase;//获得当前工作空间的数据库3 V4 I- i' z* X: `( M) G3 j
BlockTable bt = (BlockTable)db.BlockTableId.Open(OpenMode.ForRead); //获得块表. Z6 H: ?* _9 V
BlockTableRecord btr = (BlockTableRecord)bt[BlockTableRecord.ModelSpace].Open(OpenMode.ForWrite); //获得模型空间的块表记录& H- s) |+ o+ o6 f5 k9 m$ O
Line line = new Line(new Point3d(0, 0, 0), new Point3d(200, 200, 0));//创建一条直线2 r4 m7 G# T! x7 r- {
try {$ Y7 B K' w# ?
btr.AppendEntity(line);//将直线添加到模型空间中
9 r- z" S8 u' f; `" c4 c* uline.Close();//关闭该直线( h- @# z1 a" |$ H) a" \* ]6 \6 ?
}
# W2 q1 q q. q4 ?0 Tfinally {
6 [ r" z/ ^) B6 F3 r! M5 G( Ybtr.Close();//关闭块表记录2 h/ e. b$ M: s7 }
bt.Close();//关闭块表
& A! w% D7 d) ~/ a* D+ Z1 _4 Y. H}
5 N& t6 h5 V" ? }}
2 O* o4 m& |% N8 h6 f3 W- z, j+ J+ J9 W' f6 \: @$ } ?+ U% M8 K1 S
由此可见,上述语句与在VC下的开发非常类似,其过程都是先得到数据库,然后依次打开块表、块表记录,接着添加实体,最后关闭块表、块表记录。值得注意的是finally语句,无论try块中的语句是否发生异常,finally块中的语句都会被执行,从而确保关闭块表和块表记录的操作会被执行。
% h8 a3 b9 O. T9 i3 n) A9 [5 I0 L1 I4 [* s; U- J
写完代码后进行编译,编译完成将得到一个dll文件。在AutoCAD 2006中通过"netload"命令即可选择该dll文件进行加载,加载成功后即可以通过"AddLine"命令执行上述代码。遗憾的是目前的.NET API版本还不支持卸载,若要卸载只能关闭AutoCAD。: J( j4 \( f: ]/ l
8 ` @; j2 s- ^+ ?9 o4..NET API的初始化与清除
3 m( _: f; @, k0 B' y) d
4 Q! v5 X1 x, L! A3 W在ObjectARX中,"acrxEntryPoint"函数是ARX程序的载入点,程序的初始化和清除均可在该函数中进行。而在.NET API中则首先需要将初始化代码封装在一个类中,同时该类需要压迫实现& k5 R) B) w% V7 I+ m
! k- e* s- |3 ^. x) M* d; oIExtensionApplication接口。该接口包含Initialize与Terminate两个函数。其中Initialize负责加载程序时的初始化操作,Terminate则负责进行卸载程序时的清除操作。代码如下:
U( l; s8 C$ D! N+ O: I6 ]- @9 F5 y7 {& j1 s+ k# E
namespace ARXExample {( f# y, `; L' l) m
public class MyARX : IExtensionApplication {7 f! Z& [) F2 w
……
, c- S- j: d. |0 upublic void Initialize() {& ]. A4 U4 c+ E) X6 h
//初始化操作6 j) h+ {% m6 t1 h( m; r
}
5 W! B# J4 y! h# x- r6 `public void Terminate() {
$ d% c% Z; @1 j- i" Y8 m//清除操作" z5 W" Q6 q) h! F1 T4 z8 I
}2 M6 e0 I! w1 T4 k- a# |* B
……# e. h. {" o1 H. F
}
% \! [' c& P& K; ^4 ]0 G}
0 Q% M1 E% [) d
% g2 a; G- X; f2 O6 g" \0 R3 ]; M同时,为加快加载速度,可在MyARX.cs的文件头加入以下语句:
& M4 u# P* L: F# Z. Y8 A# ^: }/ a/ I6 Z! R$ g7 I" U
[assembly: ExtensionApplication (typeof (ARXExample.MyARX) ) ]: a6 o: r9 w/ z2 u6 k4 J- ?6 w
[assembly: CommandClass (typeof (ARXExample.MyARX) ) ]( f3 D m G. u$ W
; n( L$ A5 T1 V) w- \6 U( ]2 ]
这样在加载程序时AutoCAD将直接通过MyARX中的Initialize语句进行初始化,同时注册MyARX中的命令。否则,AutoCAD将搜索dll中所有的类以找到实现IExtensionApplication接口的类进行初始化,如找不到则不进行初始化。同样,通过CommandClass属性,AutoCAD也会直接到MyARX类中搜索要注册的命令。当程序中包含的类数目较多时,通过ExtensionApplication和CommandClass这两个属性可显著地加快程序的加载速度。: J6 L% b7 x5 D1 k
2 R0 x2 c9 k" t" [# f( x' j B* ^
5..NET API与COM交互操作
0 _3 o B; [: T+ n! a# T- e; Z
/ }7 v" B# w$ `, x" {$ I9 r在目前的.NET API中,其功能与传统的ObjectARX相比有所不及,有相当的ObjectARX函数目前还没有封装到.NET API中,如GetPoint等。但可以通过COM方式使用ActiveX来弥补.NET API的不足。* |$ R, h0 I6 T0 U* G7 d# x
% U( u# n! |$ U
增加了COM引用后,程序就可以使用许多VBA中的功能了。以AutoCAD ActiveX中的事件为例,以下代码可以为当前工作空间中所有的图元添加Modified事件:" B: g- ^. _( m# j$ w; M
9 n# T& X7 r6 k% c
Database db = HostApplicationServices.WorkingDatabase;
2 x8 }+ j' Z6 \& a# v& q' J8 O+ YBlockTable bt = (BlockTable)db.BlockTableId.Open(OpenMode.ForRead);0 A, U' `4 Q$ x4 T+ q+ i9 F9 w
BlockTableRecord btr = (BlockTableRecord)bt[BlockTableRecord.ModelSpace].Open(OpenMode.ForWrite);. n b: w% M: m& p! ~; e W1 f
try {
, @# C) \. h; L: GAcadObject obj;; G. x: O" O! W) y; O% D- i
//遍历块表记录
6 l8 y+ [- k: ?foreach (ObjectId objId in btr) {7 p( r* t: w+ n% `: H8 k5 ^9 O: Z
//由ObjectId得到ActiveX中的AcadObject对象' }4 A$ Y. T: L& X9 q0 l1 L$ H+ d
obj = (AcadObject)((AcadDatabase)db.AcadDatabase).ObjectIdToObject(objId.OldId);' W2 ~4 G- S+ m! d/ N
//为obj添加响应Modified事件
) A+ h6 U; d4 z9 a1 lobj.Modified += new IAcadObjectEvents_ModifiedEventHandler(obj_Modified);
" B! ^9 b; N Q! l1 ~}
% d- ?% g5 c% y" ^& Q! O9 c$ \/ h}
6 u% X' C% n" }! m' [, Xfinally {, K, ~8 r' _6 `: _; r
btr.Close();
3 r$ G2 e& h, M Kbt.Close();9 s' M$ t; Q! E8 y% M! K8 N
}/ r- R7 t; p, C" `, h, B* R
! T' B ^4 {9 e0 f其中事件响应函数obj_Modified的表示如下所示:
' P2 R3 x* ^7 Z
) F5 k" Z, }5 L4 e& zpublic static void obj_Modified(AcadObject obj) {
% G/ @$ Z0 o8 s- ]* sCommandLinePrompts.Message("object modified!" + obj.ObjectID + "\n");" P: K- d8 u0 S0 h% E7 R1 w
}" ~' I2 z& g" @# f( E) {7 Q# p0 ]8 ^
9 F3 v, k, r, E5 t* [' i二、结论
; o* Z- V1 v* X- G9 y% g, {. ]. B& U; K" W# H! ?
本文以C#为例,对基于.NET API的AutoCAD二次开发作了较详细的介绍。.NET API在具有ObjectARX强大功能的同时具有VBA使用方便易用的优点,同时具有C++的强大功能,是较为理想的开发工具。但目前.NET API在某些方面还有些不足,但随着其版本的更新、完善,定会成为众多开发人员的首选工具。 |