华南理工大学 刘林 麦智晖 阎汉生
& |3 N; W' s0 S: W/ G
2 k j# p% i' D" L- P$ I9 \5 G; O9 e" D0 S2 t3 ~
本文基于AutoCAD 2006新推出的.NET API为工具,介绍了在.NET平台下对AutoCAD进行二次开发的技术,并与目前常用的VBA、ObjectARX作了对比。同时讨论了如何弥补.NET API某些不足的功能。
' b4 u8 `0 P2 `+ n, N4 {2 _$ V3 B' }0 @) K1 N& s
3 R" L) n% h8 A# b+ G
当前AutoCAD的二次开发工具主要有:VisualLisp、VBA和ObjectARX等。其中,VisualLisp与VBA较为简单,特别是VBA,使用方便且开发速度较快,但其功能相比ObjectARX有所不足,尤其是对面向对象的功能支持不好。而ObjectARX基于VC平台,在C++的支持下,其功能非常强大,可以很好地运用各种面向对象技术,但其缺点是发开速度比较慢,同时对开发人员的能力要求较高。
9 A) X1 f0 D6 ~5 d$ v) [; }. q# Q+ _" Y* J
.NET是微软新推出的开发平台,具有众多优点。基于.NET平台对AutoCAD进行二次开发,可充分利用.NET的各种优势,在保证功能强大的前提下大大提高开发速度。1 s( V! J. S8 A0 ]/ P
; z( T1 G. I! _8 E) J
一、基于.NET的开发
/ L. Q! s9 @7 b% x; ^4 Z% a" t
- o# M( T# l k8 g1..NET API简介
: d6 ~0 v8 [0 C# g
E/ g0 H# H* ?5 O* }, ?在新推出的AutoCAD 2006中,Autodesk为其开发增加了.NET API。.NET API提供了一系列托管的外包类(Managed Wrapper Class),使开发人员可在.NET框架下,使用任何支持.NET的语言,如VB.NET、C# 和Managed C++等对AutoCAD进行二次开发。其优点是完全面向对象,在拥有与C++相匹配的强大功能的同时,具有方便易用的特点,是较理想的AutoCAD二次开发工具。
! \. I9 x4 h& O/ k
% E }. O8 h4 p$ r, ?& z: p2..NET API与传统ObjectARX的主要区别, P$ N" K) i* X9 L1 V; x
# b) a$ C9 I! N# J/ u. a: t.NET API与传统ObjectARX的区别主要源于在.NET环境下开发应用程序与在VC环境下开发应用程序的区别。首先,在VC环境下,程序员需要自己管理内存的申请和释放,而.NET采用了垃圾回收机制,由.NET框架自行判断内存回收的时机并实行回收,从而解决了令C++程序员头痛的内存泄漏问题。也正是由于这个特点,在.NET环境下不能象在C++环境下那样利用析构函数释放其他的资源,需要程序员在程序中显式地释放。在.NET API中,主要通过Dispose来函数进行资源的释放。9 A- }5 n$ a5 [% i
# o) a7 e6 H4 Z% Q9 D9 @: Y
其次,ObjectARX中的各种反应器(Reactor)在.NET API中由外包类映射为各种事件(Event),可通过定义这些事件的响应函数来响应AutoCAD的各种操作。同时对于错误信息的处理也从函数返回值改变为通常异常来处理,使其更好地兼容.NET。由于VB.NET、C#等语言都是完全面向对象的,没有全局函数的概念,所以.NET API将ObjectARX下的全局函数封装为.NET API下的某些对象或对象的属性,如ObjectARX下与用户交互的系列全局函数被封装为CommandLinePrompt类。
6 d/ I: \. i4 Y1 ~, E
! X. ~# x% d9 n- ~9 M5 c5 D3.使用.NET API
8 v/ `8 Z; O, N% d4 y6 q! O8 a
) }/ T! Z o; g0 h' t$ n9 R下面以C#为例,在Microsoft Visual C# 2005 Express Edition Beta平台上,先新建一个Class Library项目,再将AutoCAD2005安装目录下的acdbmgb.dll与acmgb.dll作为引用添加进项目中。这两个文件包含了.NET API中所有的外包类。
7 F- V' v* {: M! Q5 m; F3 m* e% x
然后在要使用.NET API的类中添加以下语句以引用.NET API的命名空间。需要添加的语句如下:- ]1 f. U8 h$ U
1 `! @- w0 s m+ }# z( v2 cusing Autodesk.AutoCAD.ApplicationServices;* Q2 ]6 g/ r/ E/ ^* B/ R
using Autodesk.AutoCAD.DatabaseServices;
! ?5 {) \" `6 }using Autodesk.AutoCAD.Runtime;
7 q2 h( N7 P( Xusing Autodesk.AutoCAD.Geometry;
6 }8 f+ i) A. p4 h. P1 F Y+ u9 _2 @! V( ?
这样就可以利用.NET API进行开发了。以下代码可在AutoCAD注册为一个命令"AddLine",该命令可在当前工作空间中添加一条起点为(0,0,0),终点(200,200,0)的直线。代码如下:
% A* s0 m; O% J, C6 B- w0 W9 x
8 u8 G6 ?6 ~! p% w3 C- f3 |& D4 l1 q[CommandMethod ("AddLine")]2 I! x' r1 W2 K0 {" H
public static void AddLineCmd()4 ?' J, [$ l& U( |
{1 y, P1 z! U; V5 U7 ^; N( e* \
Database db = HostApplicationServices.WorkingDatabase;//获得当前工作空间的数据库/ G' A0 \ u# |. v+ V
BlockTable bt = (BlockTable)db.BlockTableId.Open(OpenMode.ForRead); //获得块表( V9 w; ~! ~3 N8 I, ?5 S
BlockTableRecord btr = (BlockTableRecord)bt[BlockTableRecord.ModelSpace].Open(OpenMode.ForWrite); //获得模型空间的块表记录
5 ]' [7 }8 m, ^9 x% Y) TLine line = new Line(new Point3d(0, 0, 0), new Point3d(200, 200, 0));//创建一条直线
; W9 l8 F/ Y+ i) X9 q. K' ~try {
' _* k3 h, N9 a, wbtr.AppendEntity(line);//将直线添加到模型空间中
) V4 C2 d( [, g7 v1 z0 N" h6 eline.Close();//关闭该直线
7 d5 q6 i2 E: y' C3 F6 Y}
' ~. K" o/ a, t/ C6 J# L2 d" p8 Rfinally {& O( A* T. ]2 ^8 u' I: I# D
btr.Close();//关闭块表记录
( y5 x5 m* w& G7 q# e0 Ibt.Close();//关闭块表
& A5 ~8 {; h, H# I3 e" F9 o}2 J+ r& d1 n' g0 k6 Q9 z
}% ]7 J* @; [) ~0 g, v. c0 z9 e9 [
+ z8 B @$ N! t2 J3 X, p" a
由此可见,上述语句与在VC下的开发非常类似,其过程都是先得到数据库,然后依次打开块表、块表记录,接着添加实体,最后关闭块表、块表记录。值得注意的是finally语句,无论try块中的语句是否发生异常,finally块中的语句都会被执行,从而确保关闭块表和块表记录的操作会被执行。8 ~( X: L* G+ U8 P$ o5 T5 {! g' p
6 g: ~) h- ~! z2 E8 a! W! H写完代码后进行编译,编译完成将得到一个dll文件。在AutoCAD 2006中通过"netload"命令即可选择该dll文件进行加载,加载成功后即可以通过"AddLine"命令执行上述代码。遗憾的是目前的.NET API版本还不支持卸载,若要卸载只能关闭AutoCAD。& D- [2 S/ F2 k- @' g% e# T3 o& p
3 w3 ?' N+ F6 Q& u9 I+ q
4..NET API的初始化与清除7 n" E6 k3 Y; c+ Q" y: g! I X) @4 p
/ B& [6 s, h1 Z+ G) N0 W在ObjectARX中,"acrxEntryPoint"函数是ARX程序的载入点,程序的初始化和清除均可在该函数中进行。而在.NET API中则首先需要将初始化代码封装在一个类中,同时该类需要压迫实现
$ f9 F, l3 s5 }7 b. k. I# ?. A6 M/ `8 D8 Q9 y$ {0 i$ b6 [
IExtensionApplication接口。该接口包含Initialize与Terminate两个函数。其中Initialize负责加载程序时的初始化操作,Terminate则负责进行卸载程序时的清除操作。代码如下:
1 _! ?& {2 p; T/ P g' c: m3 P; s8 K; A+ u% U
namespace ARXExample {8 {& R6 g( i" S L6 X% k' E% m
public class MyARX : IExtensionApplication {4 A; g, ^( e5 Y3 H) {% t: x7 E
……
1 K0 Y. B# i4 g" S3 I- L, `, Ypublic void Initialize() {
& e5 G9 _/ Z. l//初始化操作
+ y/ P. W7 ~4 b8 G1 E Z( x2 N}9 y3 ?0 @3 [8 A
public void Terminate() {9 o% |3 q- c( O; _& y$ Q! }& I1 }
//清除操作
; | Z( R( h0 s) m; N& c}2 r- y; m/ ~( L
……
' w* t% ?% V7 {6 B' V) M}
1 H6 e, B; t( T% M. P% S* H} m% ` Y: u( g
6 b& h! w. i& f9 P3 U
同时,为加快加载速度,可在MyARX.cs的文件头加入以下语句:7 u7 C2 b6 ]. V
) h0 i$ ^ c# M! ]* x* l
[assembly: ExtensionApplication (typeof (ARXExample.MyARX) ) ]
& j/ U* }/ b$ w: O[assembly: CommandClass (typeof (ARXExample.MyARX) ) ]' \5 G3 d% `: p S3 T$ c/ N
( K! p0 U! f5 ^2 K7 Y: C: [1 g这样在加载程序时AutoCAD将直接通过MyARX中的Initialize语句进行初始化,同时注册MyARX中的命令。否则,AutoCAD将搜索dll中所有的类以找到实现IExtensionApplication接口的类进行初始化,如找不到则不进行初始化。同样,通过CommandClass属性,AutoCAD也会直接到MyARX类中搜索要注册的命令。当程序中包含的类数目较多时,通过ExtensionApplication和CommandClass这两个属性可显著地加快程序的加载速度。
+ K& p) [5 c1 u/ w+ f
( D L& W8 {1 j0 c2 D- `& P5..NET API与COM交互操作0 v4 b- p! D+ v8 H) W
1 {( n k5 S' w# c0 |4 _- c
在目前的.NET API中,其功能与传统的ObjectARX相比有所不及,有相当的ObjectARX函数目前还没有封装到.NET API中,如GetPoint等。但可以通过COM方式使用ActiveX来弥补.NET API的不足。! C& |+ w1 w* T0 c6 g+ i
: b$ w" w* D# j( j增加了COM引用后,程序就可以使用许多VBA中的功能了。以AutoCAD ActiveX中的事件为例,以下代码可以为当前工作空间中所有的图元添加Modified事件:
7 Z( j( B& X: H' j4 s+ `- ~
4 L, \& T3 l# Y d/ L3 N4 z6 pDatabase db = HostApplicationServices.WorkingDatabase;4 @" D6 g! p3 C8 {0 E2 h( r! E
BlockTable bt = (BlockTable)db.BlockTableId.Open(OpenMode.ForRead);
" D5 |7 {1 A9 G, O5 ]$ NBlockTableRecord btr = (BlockTableRecord)bt[BlockTableRecord.ModelSpace].Open(OpenMode.ForWrite);4 h$ H1 h$ [9 J3 s
try {5 ?* B% }* b6 y8 {
AcadObject obj;
- `1 D# E, s" {4 M# }5 |//遍历块表记录
1 ~! u) l6 r. F7 Y/ H% r( @# N. yforeach (ObjectId objId in btr) {
% @# l$ I9 A/ \1 ^. T( T, C//由ObjectId得到ActiveX中的AcadObject对象( V2 Q& p- I1 F" Z" D
obj = (AcadObject)((AcadDatabase)db.AcadDatabase).ObjectIdToObject(objId.OldId);* E" G/ o/ `2 x' k) [/ x! w
//为obj添加响应Modified事件
7 i# A8 }6 c( \obj.Modified += new IAcadObjectEvents_ModifiedEventHandler(obj_Modified);
3 o! y) |& a5 q2 \3 _% ]}
% B4 @' Z; _) `$ v9 ]}2 i8 M& c& J8 }, p
finally {' \- q4 ^/ i7 l n. i+ ?8 Y
btr.Close();
. X5 X/ b& N; A8 qbt.Close();; J$ ~. Q. l% a( r# C% K
}
+ w& b* _$ ~( p# j# H3 X. p
8 H: t6 G. a: R其中事件响应函数obj_Modified的表示如下所示:
Q+ s" S8 M b0 K; n# b; P* e7 \+ w5 [( F* q* a2 Y2 U
public static void obj_Modified(AcadObject obj) {% c$ V7 l9 g/ M3 T- ]/ M
CommandLinePrompts.Message("object modified!" + obj.ObjectID + "\n");. I6 G% M: F% c& B: w" e2 k# z4 o3 f! z
}0 D" l' S) @3 k: h
) L- |4 |/ _6 o' l, K二、结论
8 H" u# b: |1 ^+ B/ q; G0 j+ g/ s5 V* f( T# P1 Z
本文以C#为例,对基于.NET API的AutoCAD二次开发作了较详细的介绍。.NET API在具有ObjectARX强大功能的同时具有VBA使用方便易用的优点,同时具有C++的强大功能,是较为理想的开发工具。但目前.NET API在某些方面还有些不足,但随着其版本的更新、完善,定会成为众多开发人员的首选工具。 |