华南理工大学 刘林 麦智晖 阎汉生3 E- ^" l' k! @: }2 U! B) Z) m: B
' b( R- }$ M$ h0 Z+ r
' d9 v2 C7 H5 X8 c本文基于AutoCAD 2006新推出的.NET API为工具,介绍了在.NET平台下对AutoCAD进行二次开发的技术,并与目前常用的VBA、ObjectARX作了对比。同时讨论了如何弥补.NET API某些不足的功能。
2 j" e8 e7 t* l. g/ W) n, }1 X5 W
* r5 c) c8 P" p, Y- |$ ?5 A$ N+ r; T
当前AutoCAD的二次开发工具主要有:VisualLisp、VBA和ObjectARX等。其中,VisualLisp与VBA较为简单,特别是VBA,使用方便且开发速度较快,但其功能相比ObjectARX有所不足,尤其是对面向对象的功能支持不好。而ObjectARX基于VC平台,在C++的支持下,其功能非常强大,可以很好地运用各种面向对象技术,但其缺点是发开速度比较慢,同时对开发人员的能力要求较高。, ~4 C) V( I; m' E
4 ?7 }) u9 }# f) x; p.NET是微软新推出的开发平台,具有众多优点。基于.NET平台对AutoCAD进行二次开发,可充分利用.NET的各种优势,在保证功能强大的前提下大大提高开发速度。
+ ^! ^+ n0 [! m& _# Y- X# v+ u5 a6 m
一、基于.NET的开发
2 y2 f' O! Q7 H6 @7 |1 c6 ~2 l- {% `4 ^. g" k l
1..NET API简介6 x2 J9 g" i1 N% {: R) O, q+ K" q
7 |2 U" u6 V( v. {3 \9 s3 I. |在新推出的AutoCAD 2006中,Autodesk为其开发增加了.NET API。.NET API提供了一系列托管的外包类(Managed Wrapper Class),使开发人员可在.NET框架下,使用任何支持.NET的语言,如VB.NET、C# 和Managed C++等对AutoCAD进行二次开发。其优点是完全面向对象,在拥有与C++相匹配的强大功能的同时,具有方便易用的特点,是较理想的AutoCAD二次开发工具。3 ` }. c$ |2 Z4 S
) a0 s9 J2 M6 y1 R( ^# K
2..NET API与传统ObjectARX的主要区别% L: T! B, `) L9 i5 W' T( a
2 r. R& Z/ F3 Y1 _' M4 B
.NET API与传统ObjectARX的区别主要源于在.NET环境下开发应用程序与在VC环境下开发应用程序的区别。首先,在VC环境下,程序员需要自己管理内存的申请和释放,而.NET采用了垃圾回收机制,由.NET框架自行判断内存回收的时机并实行回收,从而解决了令C++程序员头痛的内存泄漏问题。也正是由于这个特点,在.NET环境下不能象在C++环境下那样利用析构函数释放其他的资源,需要程序员在程序中显式地释放。在.NET API中,主要通过Dispose来函数进行资源的释放。
' i3 e T' e) l5 G% ]) }( N5 Q* r. e W0 T' l q: E' E
其次,ObjectARX中的各种反应器(Reactor)在.NET API中由外包类映射为各种事件(Event),可通过定义这些事件的响应函数来响应AutoCAD的各种操作。同时对于错误信息的处理也从函数返回值改变为通常异常来处理,使其更好地兼容.NET。由于VB.NET、C#等语言都是完全面向对象的,没有全局函数的概念,所以.NET API将ObjectARX下的全局函数封装为.NET API下的某些对象或对象的属性,如ObjectARX下与用户交互的系列全局函数被封装为CommandLinePrompt类。1 k7 N2 g! f3 Q. }! y, n
! h, M- Q" K: T& V; c4 G# ^3 X3.使用.NET API
4 d6 {! z* F/ f+ ]
% R& e9 E# a& l( a) Z下面以C#为例,在Microsoft Visual C# 2005 Express Edition Beta平台上,先新建一个Class Library项目,再将AutoCAD2005安装目录下的acdbmgb.dll与acmgb.dll作为引用添加进项目中。这两个文件包含了.NET API中所有的外包类。& J; t6 q- s' a; H W
/ _1 p" U7 G$ g5 \ x7 U然后在要使用.NET API的类中添加以下语句以引用.NET API的命名空间。需要添加的语句如下:
2 z+ c3 h# g- _+ O* V0 p3 \* x# t) P. Y! [, w+ u' v; Q& c
using Autodesk.AutoCAD.ApplicationServices;
# G1 M$ ~5 Z4 t( j0 b. B& Xusing Autodesk.AutoCAD.DatabaseServices;
) W9 Y# u# J- ]. s+ T6 ^using Autodesk.AutoCAD.Runtime;
9 _; f6 u# |9 }& n7 d2 n) ~% Musing Autodesk.AutoCAD.Geometry;9 ^( O( [7 |9 }
9 h4 q8 ~& ~, j+ o这样就可以利用.NET API进行开发了。以下代码可在AutoCAD注册为一个命令"AddLine",该命令可在当前工作空间中添加一条起点为(0,0,0),终点(200,200,0)的直线。代码如下:
9 n. _- j' i& D# ^, l7 Y4 b' D* d
( I8 s7 s* I9 u4 P. |: |4 x8 y[CommandMethod ("AddLine")]8 j3 b% Z. Y: N: A- i% J" \ r
public static void AddLineCmd(). Y7 [- ]5 o: e# J, N* d
{
- W6 [; z% Z% [Database db = HostApplicationServices.WorkingDatabase;//获得当前工作空间的数据库
$ U0 v0 m2 A6 A* a ^) H' ]1 d4 sBlockTable bt = (BlockTable)db.BlockTableId.Open(OpenMode.ForRead); //获得块表
; b' N! Y9 u) i5 O9 | |BlockTableRecord btr = (BlockTableRecord)bt[BlockTableRecord.ModelSpace].Open(OpenMode.ForWrite); //获得模型空间的块表记录
" B5 ~8 p' S0 I& H: V: p( s. F2 C7 QLine line = new Line(new Point3d(0, 0, 0), new Point3d(200, 200, 0));//创建一条直线: g* K$ l6 n& Q. r
try {
' A. ~+ s/ j n9 v3 m/ wbtr.AppendEntity(line);//将直线添加到模型空间中
) A- ~5 q6 U7 q& f* Bline.Close();//关闭该直线8 p( B* D# _9 H, z) }0 b& v
}
2 z0 D# V1 ]0 |+ }finally {9 X4 ~% P: G. D* i# X
btr.Close();//关闭块表记录& X ]+ N* B/ _ j/ o
bt.Close();//关闭块表
5 M; i) X% }/ i' L, y) F5 J}
; m5 C3 A/ l7 d& E}
$ {# T3 Y1 Y6 a2 ^# |0 T: U! s$ n+ a- N0 {
由此可见,上述语句与在VC下的开发非常类似,其过程都是先得到数据库,然后依次打开块表、块表记录,接着添加实体,最后关闭块表、块表记录。值得注意的是finally语句,无论try块中的语句是否发生异常,finally块中的语句都会被执行,从而确保关闭块表和块表记录的操作会被执行。
9 @6 N. S5 Y( k. F% u; J3 Y" O; b% R& d
写完代码后进行编译,编译完成将得到一个dll文件。在AutoCAD 2006中通过"netload"命令即可选择该dll文件进行加载,加载成功后即可以通过"AddLine"命令执行上述代码。遗憾的是目前的.NET API版本还不支持卸载,若要卸载只能关闭AutoCAD。
9 d/ j/ X; X9 E& |6 f4 ]: j8 B4 T. O! I; Y+ C
4..NET API的初始化与清除. o' Q& q' Y0 O) X" y- W* `
% m8 w( x. Z8 ^7 c. @" F
在ObjectARX中,"acrxEntryPoint"函数是ARX程序的载入点,程序的初始化和清除均可在该函数中进行。而在.NET API中则首先需要将初始化代码封装在一个类中,同时该类需要压迫实现$ X& J; H0 J" e o; D$ l' u
, \% U& Q4 a3 A) t+ E$ w! p- r
IExtensionApplication接口。该接口包含Initialize与Terminate两个函数。其中Initialize负责加载程序时的初始化操作,Terminate则负责进行卸载程序时的清除操作。代码如下:6 W3 u$ e* S$ l0 Q$ ~
- _ h: m% x, i( F
namespace ARXExample {
7 ]9 {3 d4 g8 Z& E9 c* cpublic class MyARX : IExtensionApplication {$ H- R9 ~3 `. P, H& i+ j8 v
……' }: Y2 O8 ^8 o4 s+ a0 w8 ^% l0 o, g# T* p
public void Initialize() {/ M8 O+ n+ k X" d0 C1 a) f3 H2 \
//初始化操作
, M+ U( s' t3 Z* q1 n}
1 ~2 n' g7 L, H4 P Xpublic void Terminate() {" e5 w9 r* d) _
//清除操作
0 y, F5 g3 Y# S% E: b; ^- i/ I}
4 h# `- c! r; m* X1 Y, B, Z……
# `4 H2 F0 o4 d& d: \}% U! G/ k* {$ t3 g+ u O6 n I5 a, }
}
; C" _# r b | t+ ^" P3 c; D1 ?/ H4 c8 H( D
同时,为加快加载速度,可在MyARX.cs的文件头加入以下语句:- f* N( a. a+ j; w9 b2 D q# g. N
' U. s1 Y9 k+ a5 o
[assembly: ExtensionApplication (typeof (ARXExample.MyARX) ) ]
, R. Y% U# b9 P; ^' w# M8 G. `$ b[assembly: CommandClass (typeof (ARXExample.MyARX) ) ]
% J" X0 G2 i0 l" [9 D$ r0 c) h; d A x1 l; W
这样在加载程序时AutoCAD将直接通过MyARX中的Initialize语句进行初始化,同时注册MyARX中的命令。否则,AutoCAD将搜索dll中所有的类以找到实现IExtensionApplication接口的类进行初始化,如找不到则不进行初始化。同样,通过CommandClass属性,AutoCAD也会直接到MyARX类中搜索要注册的命令。当程序中包含的类数目较多时,通过ExtensionApplication和CommandClass这两个属性可显著地加快程序的加载速度。
% D, ?0 d$ j% U7 B4 C% f7 ?+ M. n
3 n/ R* v3 q( S5..NET API与COM交互操作
8 ]& x0 L; S) i2 o
# j2 J% ]4 m2 }! K在目前的.NET API中,其功能与传统的ObjectARX相比有所不及,有相当的ObjectARX函数目前还没有封装到.NET API中,如GetPoint等。但可以通过COM方式使用ActiveX来弥补.NET API的不足。
7 y" J$ a4 V: h% J( c! y0 W5 ^6 P, y0 b% |! \8 t7 O9 M7 H
增加了COM引用后,程序就可以使用许多VBA中的功能了。以AutoCAD ActiveX中的事件为例,以下代码可以为当前工作空间中所有的图元添加Modified事件:
, k$ \* I1 M* s/ n
9 ^# @. n# [7 \( n$ k3 C' CDatabase db = HostApplicationServices.WorkingDatabase;
: \! G: b& u% [. H2 g" K; s; SBlockTable bt = (BlockTable)db.BlockTableId.Open(OpenMode.ForRead);& w# B" F! Y1 \( n2 n1 p" j5 B+ m
BlockTableRecord btr = (BlockTableRecord)bt[BlockTableRecord.ModelSpace].Open(OpenMode.ForWrite);) r5 q' }0 [. G( S/ m" f
try {
" f2 \4 O3 K# h w$ w" kAcadObject obj;7 o' f4 J5 Y7 H0 Z
//遍历块表记录
) i: T3 `: H1 v: Gforeach (ObjectId objId in btr) {0 R+ f/ I% a9 O- m/ ?& w* `
//由ObjectId得到ActiveX中的AcadObject对象# j# d8 n4 b1 s5 w4 \# Z
obj = (AcadObject)((AcadDatabase)db.AcadDatabase).ObjectIdToObject(objId.OldId);
7 }0 G3 z' P. N" Q//为obj添加响应Modified事件) ~( [4 e2 j& l6 |6 _
obj.Modified += new IAcadObjectEvents_ModifiedEventHandler(obj_Modified);
8 p+ F1 {/ ~% N2 r* M}
- y; N! w4 D% d, M}+ v0 ]/ O; c. b" e% n7 p* F
finally {
. G! s$ \4 o0 m+ c: jbtr.Close();
) Y) _+ T8 i& F$ \- g: }) N! {! ybt.Close();
3 v1 _" \" s6 C+ l; b0 x9 V}# j3 |# T+ b+ o, P0 n" G, W8 \
- j; Q5 D+ t/ z* p8 c L
其中事件响应函数obj_Modified的表示如下所示:
7 x6 k% v6 F7 n! r0 Z' `8 d/ K9 C1 `* `5 b2 ~- @0 O
public static void obj_Modified(AcadObject obj) {
Q* R ^6 f5 S1 O0 F$ zCommandLinePrompts.Message("object modified!" + obj.ObjectID + "\n");
' t" ]- a% @1 m8 o}
( L8 ^# q3 H( T: y: a) P, Z8 I. A3 I$ G b3 w7 h) q) `
二、结论
) b& U; v. g/ f! w& T
" }+ s0 j5 R3 \9 D8 \6 n本文以C#为例,对基于.NET API的AutoCAD二次开发作了较详细的介绍。.NET API在具有ObjectARX强大功能的同时具有VBA使用方便易用的优点,同时具有C++的强大功能,是较为理想的开发工具。但目前.NET API在某些方面还有些不足,但随着其版本的更新、完善,定会成为众多开发人员的首选工具。 |