ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

AutoCAD二次开发:VBA与VB.NET选型与工程实践

AutoCAD二次开发:VBA与VB.NET选型与工程实践 简介面向AutoCAD二次开发初学者与进阶者的完整示例源码包聚焦VBA与VB.NET两种主流开发方式覆盖图层管理、实体操作、数据库交互、用户界面设计等典型场景。压缩包共1777个文件约35MB以C#源码cs和可运行程序集dll为主同时包含调试符号pdb、工程配置csproj/sln、资源文件resources/resx以及少量ARX扩展和示例DWG图纸便于直接打开工程、对照学习与二次修改。已有995人学习下载说明该资料在AutoCAD开发圈子中有一定参考价值。通过逐个运行和调试这些实例读者可以掌握调用AutoCAD .NET API创建与修改几何对象、处理图层和块表记录、响应用户界面事件以及利用.NET框架进行数据文件处理的完整思路是衔接基础语法与真实项目开发的实用素材。1. 从一张两万行的图纸开始认识 AutoCAD 二次开发接手设计院的老图纸时最让人头疼的不是复杂的实体关系而是那些重复到麻木的操作批量改图层、统一文字样式、按规则重排块属性。手工做两个小时VBA 宏可能只需要两分钟。AutoCAD VBA 与 VB.NET 是两种最常见的自动化方案前者内嵌在 CAD 进程中启动快、入门门槛低后者依托 .NET Framework能写独立插件、做复杂交互界面。严格说VBA 和 VB.NET 不是替代关系而是按任务复杂度选择的两条路线轻量脚本用 VBA正式工具用 VB.NET。这篇博文把这两条路线的环境搭建、核心 API、参数设置和源码组织方式拆开讲清楚读完后你就能把手上的重复性 CAD 操作改写成能落地的代码工程。源码的组织方式、错误处理和性能边界是这次文章里信息量最密集的部分。2. 开发环境里的两个关键选择VBA 还是 VB.NET2.1 内嵌还是外挂先搞清楚两种运行模型很多新手上来就纠结语法其实 VBA 和 VB.NET 的差异不在语法而在运行模型。VBA 是 AutoCAD 内嵌的脚本宿主随 acad.exe 启动所有代码都跑在 CAD 进程内部。优点是无需额外运行时打开图纸就能用适合处理当前文档缺点是受限于进程内的资源约束崩溃可能导致整个 CAD 退出且无法轻易做出多窗口的复杂界面。VB.NET 走的是 ObjectARX 托管封装路线编译成 DLL 后通过 NETLOAD 命令加载。它不在 CAD 进程内直接执行源码而是由 .NET 运行时承载所以可以调用 WinForms 和 WPF能访问数据库和外部服务错误隔离也更干净。代价是每次修改代码都要重新编译、卸载程序集调试周期比 VBA 长。如果你的任务是给当前图纸改几个属性选 VBA如果是做成一个给同事反复使用的生产工具选 VB.NET。2.2 用最小命令验证两边环境是否可用VBA 侧在 AutoCAD 命令行输入VBAIDE如果弹出 Visual Basic 编辑器说明内嵌 VBA 已启用。许多精简版或绿色版 CAD 没有这个组件命令行输入VBAIDE会提示未安装 VBA 支持库需在安装包中选择编辑器和 VBA 支持组件。安装后在 VBA 编辑器的工具-引用中确认已勾选 AutoCAD 2024 Type Library这是访问 CAD API 的必经之路。VB.NET 侧先确认当前 CAD 版本对应的 .NET Framework 版本。以 AutoCAD 2024 为例它支持 .NET Framework 4.8用 Visual Studio 2022 创建类库项目时目标框架选 .NET Framework 4.8而不是 .NET 6/7/8。目标框架选错会导致加载失败或找不到程序集。VB.NET 加载需要先编译 Class1.vb - 一个最基础的命令行命令 Imports Autodesk.AutoCAD.ApplicationServices Imports Autodesk.AutoCAD.Runtime Public Class NetloadTest CommandMethod(HELLOCAD) Public Sub HelloCad() DocumentManager.MdiActiveDocument.Editor.WriteMessage(vbLf 加载成功VB.NET 环境 OK。) End Sub End Class编译成功后在 AutoCAD 命令行输入NETLOAD选中生成的 DLL 文件再输入HELLOCAD若命令行输出文本则验证通过。这段代码里CommandMethod(HELLOCAD)是入口特性声明这个方法是 CAD 命令DocumentManager.MdiActiveDocument拿到当前活动文档对象Editor.WriteMessage向命令行写内容。2.3 引用与程序集关系为什么网上源码到你电脑就报错打开带源码的示例工程最常见的错误是引用失效。VBA 工程里工具-引用列表中如果某一项显示丢失说明参考的 AutoCAD 版本对象库与当前安装版本不一致。VBA 代码中若出现ThisDrawing.Application.ActiveDocument这依赖当前打开的文档若出现Application.Documents.Open则是在应用级操作两者引用层级不同混用会报未找到成员。VB.NET 工程中常见的错误是缺少以下引用引用程序集命名空间作用Acdbmgd.dllAutodesk.AutoCAD.DatabaseServices操作图形数据库、图元、事务Acmgd.dllAutodesk.AutoCAD.ApplicationServices应用级对象、文档管理AcMgd.dllAutodesk.AutoCAD.EditorInput命令行交互、选择集操作AcDbMgd.dllAutodesk.AutoCAD.Geometry数学对象如点、向量、矩阵这些 DLL 位于 CAD 安装目录下右键项目添加引用-浏览定位到对应目录。缺哪个引用就报哪种类型的错误记不住不要紧编译时看报错信息里的命名空间按表反查即可。3. 写第一个能放到实际图纸里的批量处理代码3.1 VBA 侧遍历模型空间并修改图元属性以一个典型的图纸里所有文字统一图层为例。VBA 里用ThisDrawing.ModelSpace访问模型空间通过Entity.ObjectName判断图元类型。之所以用ObjectName而不是TypeName是因为 CAD 图元对象在 VBA 中都以AcDbXxx命名例如AcDbText表示单行文字、AcDbMText表示多行文字两者的属性和方法不一样统一改为图层时需逐个处理。Public Sub UniformLayerToText() Dim ent As AcadEntity Dim textObj As AcadText Dim mtextObj As AcadMText For Each ent In ThisDrawing.ModelSpace If ent.ObjectName AcDbText Then Set textObj ent textObj.Layer TEXT_LAYER ElseIf ent.ObjectName AcDbMText Then Set mtextObj ent mtextObj.Layer TEXT_LAYER End If Next ent ThisDrawing.Regen acActiveViewport End Sub外层For Each遍历模型空间集合ObjectName判断图元类别再分别赋值Layer属性。末尾的Regen是强制重生成模型否则某些图层显示状态不会立即刷新。这段代码在生产中要先双击执行测试图形避免直接选中全部图元时误改了非文字对象。3.2 VB.NET 侧事务机制才是正确姿势同一个功能在 VB.NET 中必须用事务Transaction这是托管 API 与 VBA 最大的不同。VBA 直接改对象属性CAD 内部自动管理数据一致性VB.NET 里手动开事务是为了保证一组修改要么全部提交要么全部回滚避免半途出错造成数据库不一致。Imports Autodesk.AutoCAD.DatabaseServices Imports Autodesk.AutoCAD.ApplicationServices Imports Autodesk.AutoCAD.Runtime Imports Autodesk.AutoCAD.EditorInput Public Class BatchLayerTool CommandMethod(SETTEXTLAYER) Public Sub SetTextLayer() Dim doc As Document DocumentManager.MdiActiveDocument Dim db As Database doc.Database Using trans As Transaction db.TransactionManager.StartTransaction() Try Dim bt As BlockTable trans.GetObject(db.BlockTableId, OpenMode.ForRead) Dim btr As BlockTableRecord trans.GetObject(bt(BlockTableRecord.ModelSpace), OpenMode.ForRead) For Each id As ObjectId In btr Dim ent As Entity trans.GetObject(id, OpenMode.ForRead) If ent.ObjectName AcDbText OrElse ent.ObjectName AcDbMText Then Dim upgraded As Entity trans.GetObject(id, OpenMode.ForWrite) upgraded.Layer TEXT_LAYER End If Next trans.Commit() Catch ex As Exception ed.WriteMessage(vbLf 处理失败: ex.Message) trans.Abort() End Try End Using End Sub End Class注意第 12 行先以ForRead打开图元读取ObjectName判断类型后再以ForWrite重新打开同个ObjectId并修改属性。不能直接以ForWrite打开全部图元因为若图元位于锁定图层或外部参照中写入打开会抛异常。事务内Commit确认变更Abort撤销所有未提交操作这种模式是后续所有批量工具的基础模板。3.3 学会用选择集替代全图遍历全图遍历在图纸超过五万图元时会明显卡顿更优的做法是先让用户框选范围只处理选中的图元。用户交互类放在EditorInput命名空间用PromptSelectionResult接收选择集。Dim ed As Editor doc.Editor Dim pOpt As New PromptSelectionOptions() pOpt.MessageForAdding 选择要修改的文字对象: Dim selRes As PromptSelectionResult ed.GetSelection(pOpt) If selRes.Status PromptStatus.OK Then Return End If Dim ids As ObjectId() selRes.Value.GetObjectIds()GetSelection是阻塞式方法用户按回车后才会继续执行返回的PromptSelectionResult.Status若为PromptStatus.OK才代表选择了有效对象。拿到ObjectId数组后配合事务打开修改。这种方式的性能优势在于 CAD 内部检索只遍历被选中图元不触碰其余数据。4. 源码工程里的 DBTrans 封装、错误边界与图纸安全4.1 把事务模板封装成可复用代码单元示例源码中你可能见过DBTrans类它是一个把事务生命周期封装起来的工具类不必重复编写StartTransaction、Commit、Abort的样板代码。常见做法是用一个方法接收委托Delegate方法内部负责事务创建与异常处理业务代码只写核心修改逻辑。Public Shared Function RunInTransaction(doc As Document, action As Action(Of Transaction, Document)) As Boolean Dim db As Database doc.Database Using trans As Transaction db.TransactionManager.StartTransaction() Try action(trans, doc) trans.Commit() Return True Catch ex As Autodesk.AutoCAD.Runtime.Exception trans.Abort() Return False Catch ex As Exception trans.Abort() Return False End Try End Using End Function调用时业务逻辑以Sub(trans, doc)形式传入。这种封装的另一个好处是允许统一处理异常日志比如在Catch块里写Debug.WriteLine(ex.ToString())这样调试时能看到完整堆栈而不是 CAD 命令行的截断信息。注意这里必须区分Autodesk.AutoCAD.Runtime.Exception和普通异常前者的ErrorStatus属性对应 CAD 内部错误码能直接映射到系统变量处理起来更精准。4.2 锁定图层、冻结图层与块内图元的处理批量修改时最隐蔽的坑是锁定图层。图层被锁定时图元不可写入。许多源码直接用ForWrite打开对象遇到锁定图层就抛eLockedLayer异常。正确处理方式是先打开图层的LayerTableRecord判断IsLocked或者直接在上面的封装中先跳过锁定图层图元并记录数量最后向用户汇报跳过 12 个锁定图层对象。块内图元则需要先进入块表记录再遍历。BlockTableRecord的Name属性指明这是模型空间、图纸空间还是某个块定义块定义里的图元也是ObjectId集合。常见的错误是只改模型空间忘记处理块引用内部嵌套的对象导致明明改了看到还是旧值。正确方式 获取块表记录后, 遍历其内部所有 ObjectId For Each id As ObjectId In btr Dim obj As DBObject trans.GetObject(id, OpenMode.ForRead) If TypeOf obj Is BlockTableRecord Then 继续向下递归处理嵌套块 End If Next递归遍历块嵌套结构时要设置最大深度限制比如 10 层防止环形块引用导致的死循环。4.3 崩溃排查从哪里看错误信息与写日志代码运行时报eNotApplicable指的是当前状态下该操作不可用最常见于没有文档对象但代码调用了DocumentManager.MdiActiveDocument或处于图纸空间却访问模型空间相关数据。建议在 VB.NET 工程中统一配置一个日志文件写入类每次异常都记录时间、命令名、异常类型和堆栈。日志不要写进 CAD 安装目录放到%APPDATA%\YourTool\下因为普通用户没有安装目录写权限。VBA 侧则可以用ThisDrawing.Application.DocumentManager.MdiActiveDocument.SendCommand写临时文件但更轻量的方式是直接用MsgBox配合错误号和描述定位适合脚本级排查。提示若命令执行后无任何响应先检查命令行是否设置了FILEDIA为 0这会导致文件对话框不出现常见于二次开发脚本改过系统变量后忘了恢复。4.4 源码组织结构多个命令如何避免互相污染一个完整的工程通常包含三类文件命令入口类、业务逻辑类、数据模型类。命令入口类只做参数接收和状态检查业务逻辑类只处理图元筛选与修改数据模型类承载扩展数据和设置参数。VBA 里对应的是标准模块放入口宏、类模块放业务逻辑、用户窗体放交互界面。如果全部堆在一个模块里后期维护时要反复翻代码定位这是示例源码中最常见的坏味道。5. 用时间测量和数据对比验证优化效果让代码真正可生产VBA 和 VB.NET 的性能差距在批量任务上很明显。验证方式不是看感觉而是记录操作前后的耗时。VBA 里用Timer函数VB.NET 用Stopwatch类Dim sw As New System.Diagnostics.Stopwatch() sw.Start() 执行批量修改逻辑 sw.Stop() ed.WriteMessage(vbLf $总耗时: {sw.ElapsedMilliseconds} ms)系统化改完后的正确验证步骤是先对同一份测试图纸分别用 VBA 宏和 VB.NET 命令执行相同操作对比耗时再全选修改范围通过LIST命令抽查图元属性是否一致最后用AUDIT命令检查数据库完整性。若耗时差异超过 5 倍优先检查是否误用了全图遍历而非选择集其次检查是否在循环中重复打开了同一对象。还有一个容易被忽略的细节在 VB.NET 批量更新前用doc.Database.CloseInput(True)关闭输入处理可让 CAD 在批量处理期间不处理外部输入减少不必要的消息循环开销处理完毕后再用CloseInput(False)恢复。VBA 没有对应概念但可以通过Application.Update和ThisDrawing.Regen的调用时机控制刷新频率。把这些验证步骤写成一个独立的命令如BenchmarkTool后续每次改动代码直接跑它通过数字而非手感判断是否变快这比任何代码审查都可靠。本文还有配套的精品资源点击获取
返回列表