尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

AutoCAD二次开发:VBA与VB.NET从环境搭建到批量处理实战

AutoCAD二次开发:VBA与VB.NET从环境搭建到批量处理实战 简介面向 AutoCAD 二次开发初学者与进阶开发者这份示例源码包系统汇集了 VBA 与 VB.NET 的基础编程知识和实例工程。内容覆盖图层管理、实体操作、图形数据库交互、自定义 Ribbon 界面与事件驱动工具等典型场景包含从基础对象操作到交互工具开发的多个层次并配有 ARX 插件项目与可运行的 DLL/EXE 示例帮助读者搭建环境、理解对象模型再到通过 .NET API 自动化和扩展设计绘图流程。资源共 1777 个文件压缩包约 35MB以 C# 源码.cs、Visual Studio 解决方案.sln/.csproj和编译库.dll/.pdb为主另含 DWG 测试图形、LSP 脚本、CHM/PDF 帮助文档及多种资源与配置文件目录和工程结构清晰便于按章节检索与对照调试。已有 995 人学习下载适合课程实训、项目参考或自学入门。1. 拿到这套 AutoCAD VBA / VB.NET 开发源码包先别急着解压很多工程师第一次接触 AutoCAD 二次开发都是从网上下载一个标注着「基础与实例教程示例全部源码」的压缩包开始的。解压后里面通常是几十个.dvb、.bas、.cs、.vbproj文件随手双击一个.dvbAutoCAD 提示「未安装 VBA 支持库」换成 Visual Studio 打开工程又缺一堆 Autodesk 命名空间的引用。这个标题覆盖的其实是两条完整的技术路线老牌的 VBA 宏和现代 .NET 环境下的 VB.NET 插件。它解决的是两类问题——一是设计院、制造企业里大量重复的图纸处理工作比如批量改图框、导明细表二是把 AutoCAD 能力嵌入到企业自己的业务系统里。适合刚转岗 CAD 二次开发的人也适合被图纸批量操作折磨了几年、想系统化写工具却不知从哪里下手的工程师。看完这篇你能搞清楚这两条路分别怎么搭环境、怎么写第一行能跑的代码、以及遇到「无法运行文档中的宏」「unhandled access violation writing」这类报错时去查哪里。2. 开发环境搭建把 VBA 和 VB.NET 的最小可运行代码跑起来2.1 为什么 VBA 在 64 位 AutoCAD 里越来越难用AutoCAD VBA 的历史比很多人想象的更长。从 R14 时代开始VBA 就内置在安装包里用户直接打开 VBA 管理器就能写宏。但现在的问题在于Autodesk 从 2010 版开始逐步把 VBA 从默认安装中移除64 位版本尤其明显。打开 AutoCAD 后命令行输入VBAIDE如果弹出「未安装 VBA 支持库」说明你的机器上没有装 VBA Enabler。这不是给 AutoCAD 打补丁而是 Autodesk 单独发布的一个运行库安装包需要去官网按对应版本下载。另一个现实约束是VBA 本质上是 COM 自动化它通过AutoCAD.Application这个 ProgID 与主进程通信。在 64 位 Windows 上AutoCAD 主程序是 64 位进程而 VBA 7.1 运行库本身是 32 位组件两者通过 COM 互操作层进行桥接。这种架构在单机脚本场景下问题不大但一旦涉及大量图形实体遍历性能会明显劣于 .NET API 直调。所以我的建议是如果你是今天才决定入坑直接学 VB.NET把 VBA 当作「读旧代码、改小脚本」的辅助技能。如果手上就有一批老.dvb文件要维护可以两条腿走路。2.2 VBA 环境检查与最小宏三行代码验证 COM 链路先确认环境。打开 AutoCAD命令行输入VBAIDE能打开 VBA 编辑器说明运行库正常。如果提示未安装需要先装对应版本的 VBA Enabler。装好后在 VBA 编辑器的 Immediate 窗口按 CtrlG 调出里输入以下代码并回车ThisDrawing.Utility.Prompt VBA-OK vbCrLf能输出VBA-OK说明 VBA 与当前图纸的 Application 对象通信正常。这段代码不涉及任何文件操作只用来验证最基本的 COM 链路是否通畅。ThisDrawing是 VBA 对当前激活文档的入口Utility.Prompt是向命令行输出文本的标准方法vbCrLf是换行符避免输出粘在下一行命令前。如果是第一次接触 VBA可以用宏录制先建立感性认识。工具菜单里的「宏」→「录制」画一条线再停止录制VBA 会自动生成类似AddLine的代码。录制的代码虽然啰嗦但它能告诉你对象模型的调用顺序——从ThisDrawing.ModelSpace拿到空间再调用AddLine方法传入两个坐标。这个顺序就是后面所有代码的基础。2.3 VB.NET 侧的环境准备Visual Studio AutoCAD .NET APIVB.NET 走的是另一条路。AutoCAD 从 2006 版开始提供基于 .NET 的托管 API命名空间是Autodesk.AutoCAD.*底层是对 ObjectARXC API的托管封装。和 VBA 的 COM 桥接不同.NET API 直接在进程内运行性能好很多也支持事件、事务、自定义实体等高级功能。推荐开发环境如下表组件推荐版本说明AutoCAD2020 或更高版本越高.NET 目标框架要求越高Visual Studio2019 或 2022Community 版足够.NET Framework4.7.2 或 4.8AutoCAD 2020 对应 4.72023 以上要求 4.8托管 API 引用Autodesk.AutoCAD.Interop需在添加引用时手动指向安装目录创建一个 VB.NET 类库项目后右键「添加」→「引用」从 AutoCAD 安装目录通常是C:\Program Files\Autodesk\AutoCAD 2020找到如下 DLLAcDbMgd.dll AcMgd.dll AccoreMgd.dll这三个引用缺一不可。AcMgd.dll包含 Application 和 Document 相关的核心类型AcDbMgd.dll是数据库对象模型也就是操作实体、图层、块表的基础AccoreMgd.dll是命令行和编辑器相关功能。很多新手的错误是只引了AcMgd.dll一写Transaction就报类型不明确。2.4 第一个 VB.NET 插件从 Netload 到命令行输出下面这个例子是插件的最简骨架功能是在命令行输出一段文字类似 VBA 里Prompt的效果Imports Autodesk.AutoCAD.ApplicationServices Imports Autodesk.AutoCAD.EditorInput Imports Autodesk.AutoCAD.Runtime Public Class FirstPlugin CommandMethod(HELLO) Public Sub HelloWorld() Dim doc As Document Application.DocumentManager.MdiActiveDocument Dim ed As Editor doc.Editor ed.WriteMessage(vbLf HELLO from VB.NET vbLf) End Sub End Class代码说明CommandMethod(HELLO)特性声明了这是一个 AutoCAD 命令命令名是HELLO在命令行输入它就会触发下面的方法。Application.DocumentManager.MdiActiveDocument拿到当前激活的文档对象Editor是面向用户的命令行输入输出接口WriteMessage把字符串写到命令行。整个类不需要实例化AutoCAD 通过反射找到带CommandMethod特性的方法。编译项目得到ClassLibrary1.dll然后回到 AutoCAD命令行输入NETLOAD在弹出的文件选择框中选中这个 DLL。命令行输入HELLO能看到输出就说明加载成功。这里有个冷知识NETLOAD命令会常驻内存多次加载同一个 DLL 时不会覆盖旧版本调试代码时需要先UNLOAD或者重启 AutoCAD。可以在命令行执行NETLOAD后按 F2查看加载日志确认是否有程序集冲突。3. 理解 AutoCAD 对象模型从 ThisDrawing 到 Transaction 的演进3.1 对象层级和入口选择VBA 的代码入口非常直观ThisDrawing就是一个全局变量背后是 COM 的AcadDocument对象。顺着ThisDrawing可以拿到ModelSpace、Layers、SelectionSets。它的代码风格简洁适合小工具但问题也很明显COM 对象的属性和方法名是固定的想要扩展必须靠 COM 的ExecuteMso或者反射不方便做复杂业务封装。VB.NET 的入口则要显式得多。每次操作数据库前都要建立一个Transaction对象然后通过Transaction.GetObject获取块表、注册表或实体。一个典型的操作流程如下Using tr As Transaction doc.Database.TransactionManager.StartTransaction() Dim bt As BlockTable tr.GetObject(doc.Database.BlockTableId, OpenMode.ForRead) Dim btr As BlockTableRecord tr.GetObject(bt(BlockTableRecord.ModelSpace), OpenMode.ForWrite) 在这里创建或修改实体 tr.Commit() End Using代码说明StartTransaction()是事务的起点BlockTable是块表存储了图纸里所有块定义。bt(BlockTableRecord.ModelSpace)取出模型空间对应的记录。OpenMode.ForRead表示只读打开如果后续要添加新实体需要对模型空间OpenMode.ForWrite。所有修改操作完成后必须Commit()否则事务回滚图纸不会生效。这个模式对读操作也适用因为 AutoCAD 所有对象访问都被纳入事务模型跳过事务直接操作对象会导致运行时异常。为什么这么设计因为 AUTOCAD 的图形数据库支持多文档环境用户可以在一个进程里打开多个图纸每个文档对应一个独立的Database实例。事务机制保证了对数据库的并发访问是安全的——读取和写入不会互相污染。VBA 时代的 COM 对象其实底层也走事务只是被封装掉了你感觉不到。但一旦你的 VBA 宏触发了Undo就会发现部分操作回退不了原因就是没有显式事务边界。3.2 SelectionSet过滤和选择是高频操作几乎每个生产工具都要让用户选择实体然后批量处理。VBA 里用SelectionSet.SelectOnScreen接受用户框选VB.NET 用Editor.SelectAll或SelectWindow。下面这段是 VB.NET 里选择所有圆并把它们改成红色圆环的完整代码Imports Autodesk.AutoCAD.DatabaseServices Imports Autodesk.AutoCAD.EditorInput Imports Autodesk.AutoCAD.Runtime Imports Autodesk.AutoCAD.ApplicationServices Public Class CircleTool CommandMethod(REDCIRCLE) Public Sub RedCircle() Dim doc As Document Application.DocumentManager.MdiActiveDocument Dim db As Database doc.Database Dim ed As Editor doc.Editor Dim filter As New SelectionFilter(New TypedValue() { New TypedValue(0, CIRCLE) }) Dim res As PromptSelectionResult ed.SelectAll(filter) If res.Status PromptStatus.OK Then Return Using tr As Transaction db.TransactionManager.StartTransaction() For Each id As ObjectId In res.Value.GetObjectIds() Dim ent As Entity tr.GetObject(id, OpenMode.ForWrite) Dim circle As Circle TryCast(ent, Circle) If circle IsNot Nothing Then circle.ColorIndex 1 End If Next tr.Commit() End Using ed.WriteMessage(vbLf 已处理 res.Value.Count 个圆 vbLf) End Sub End Class代码说明TypedValue是过滤器的最小单元(0, CIRCLE)表示按实体类型过滤DXF 组码 0 对应实体类型。如果还想过滤图层可以再加一维比如New TypedValue(8, 标注层)表示只选该图层上的实体。SelectAll不带参数会全图选择配合过滤器就是「全图中满足条件的对象」。GetObject(id, OpenMode.ForWrite)是事务内获取对象的唯一方式ColorIndex 1对应红色这是 AutoCAD 标准调色板的索引值。这里有一个很多人容易踩的坑For Each循环里对同一个对象调用GetObject两次第二次必须用ForWrite。如果第一个GetObject用了ForRead后面又用ForWrite再次打开同一个实体AutoCAD 会抛出eWasOpenForWrite异常。处理方式是把第一次打开的实体先Dispose或者在循环外用TransactionManager.GetObject一次性拿取。3.3 字典与扩展数据VBA 字典和 AutoCAD 自带的 Dictionaries热词里经常出现的「vba 字典」和 AutoCAD 没有直接关系——VBA 里Scripting.Dictionary是 Windows 脚本宿主提供的键值对集合常用来做去重、缓存和计数和普通Collection的区别在于它提供Exists方法查询接近 O(1)。我在写批量导出工具时常用它做「图层名 → 数量」的统计。下面是一个典型用法统计当前图纸里每种图层的实体数量Sub CountByLayer() Dim dict As Object Set dict CreateObject(Scripting.Dictionary) Dim ent As AcadEntity For Each ent In ThisDrawing.ModelSpace If dict.Exists(ent.Layer) Then dict(ent.Layer) dict(ent.Layer) 1 Else dict.Add ent.Layer, 1 End If Next Dim key As Variant For Each key In dict.Keys Debug.Print key : dict(key) Next End Sub代码说明CreateObject(Scripting.Dictionary)是创建字典对象的标准方式它的键可以是字符串也可以是数字。For Each ent In ThisDrawing.ModelSpace遍历模型空间全部实体ent.Layer取出该实体所在图层名。dict(ent.Layer)的写法在字典已包含该键时直接更新值比Item属性更简洁。需要注意字典的键区分大小写如果你要统计的图层名存在「标注」和「标注 」这种带尾随空格的情况键会被当成不同项建议对ent.Layer先做Trim。AutoCAD 自带的Dictionaries则是图形数据库的一部分藏在Database.NamedObjectsDictionary里。它和 VBA 字典完全是两回事——前者是把自定义数据持久化在 DWG 文件内后者是内存中的临时集合。如果你要在图面上挂业务属性比如给每根管道附上「材质、压力等级」正确做法是XRecord写到 NamedObjectsDictionary而不是指望某个宏结束后内存里的字典还活着。4. 批量处理实战两个拿来就能改的实例源码4.1 批量替换文字前缀从选择集到逐实体修改生产现场最常见的需求是把一批文字对象的编号前缀统一改掉比如把「OLD-」改成「NEW-」。VBA 实现如下Sub ReplacePrefix() Dim sset As AcadSelectionSet Set sset ThisDrawing.SelectionSets.Add(TMP_SSET) Dim fType(0) As Integer Dim fData(0) As Variant fType(0) 0 fData(0) TEXT sset.SelectOnScreen fType, fData Dim ent As AcadEntity Dim txt As AcadText Dim count As Integer For Each ent In sset If TypeOf ent Is AcadText Then Set txt ent If Left(txt.TextString, 4) OLD- Then txt.TextString NEW- Mid(txt.TextString, 5) count count 1 End If End If Next ThisDrawing.SelectionSets.Item(TMP_SSET).Delete MsgBox 已替换 count 个文字 End Sub代码说明SelectionSets.Add创建新的选择集名称要唯一所以用了带时间戳的想法也可以用Add(TMP_ Timer)。SelectOnScreen让用户在屏幕上框选过滤条件只有一个——实体类型是TEXT。TypeOf ent Is AcadText判断实体是否为文字对象避免把多行文字MTEXT也算进去。Left和Mid是 VBA 字符串函数分别取左侧指定长度和从第 5 位开始取子串。最后删除选择集是个好习惯否则下次运行时SelectionSets.Add会报「集已存在」。.NET 版本思路一致但要用Transaction包住修改过程。注意编辑文字内容这个动作在 VBA 里是直接改TextString属性在 .NET 里要先UpgradeOpen()或初始就以ForWrite打开否则读打开状态下写入属性会抛异常。4.2 导出块属性到 CSVVBA 字典辅助去重导出明细表是设计院高频需求。块的属性数据存在GetAttributes集合中每个属性有TagString和TextString两个关键字段。下面的 VBA 代码把所有块参照的「名称、属性、数量」导出为 CSV 文件中间用字典做名称去重和数量累加Sub ExportBlockAtts() Dim dict As Object Set dict CreateObject(Scripting.Dictionary) Dim blk As AcadBlockReference Dim att As AcadAttributeReference Dim key As String For Each blk In ThisDrawing.ModelSpace If TypeOf blk Is AcadBlockReference Then key blk.EffectiveName If dict.Exists(key) Then dict(key) dict(key) 1 Else dict.Add key, 1 End If End If Next Dim fso As Object Dim ts As Object Set fso CreateObject(Scripting.FileSystemObject) Set ts fso.CreateTextFile D:\block_export.csv, True ts.WriteLine 块名,数量 Dim k As Variant For Each k In dict.Keys ts.WriteLine k , dict(k) Next ts.Close MsgBox 导出完成共 dict.Count 种块 End Sub代码说明blk.EffectiveName取得块参照的真实名称如果是动态块Name属性可能是匿名块名EffectiveName才是用户定义的名称这一行就避开了动态块统计混乱的坑。CreateObject(Scripting.FileSystemObject)创建文件系统对象CreateTextFile的第二个参数True表示覆盖同名文件。CSV 文件的换行符在 VBA 里用vbCrLf但 Excel 打开时建议用vbLf更兼容不过大部分情况WriteLine已经追加了换行不需要手动加。.NET 版本的导出通常用File.AppendAllText或StreamWriter并且注意System.IO.File和 AutoCAD 的文件锁会冲突——如果你用 AutoCAD 的SaveAs方法保存了同路径文件NET 的FileStream可能会抛 IO 异常正确做法是先File.Close释放句柄。4.3 崩溃点unhandled access violation writing 的常见来源热词「autocad unhandled access violation writing」对应的是一类系统级错误本质是 AutoCAD 进程内存访问违例。做过 .NET 开发的都知道AccessViolationException最常见的触发方式是托管代码持有非托管对象并在对象被垃圾回收后继续访问它。在 AutoCAD 二次开发里典型场景包括Transaction已Dispose但你保存了其中的ObjectId稍后用或者对Document对象在另一个线程执行了修改。VBA 里也有类似的坑——AcadBlockReference对象没有显式释放循环里反复创建和丢弃COM 引用计数错乱会触发内存问题。避免方法一是所有ObjectId只做短生命周期使用跨事务必须重新GetObject二是 .NET 代码里绝不要自己GC.Collect()让运行时自己管理三是 VBA 宏结束后主动Set obj Nothing释放 COM 对象。调试时打开 Windows 事件查看器如果错误记录里显示故障模块是acdb22.dll基本可以判定是数据库对象访问时序问题优先检查事务边界。5. 源码包的正确打开方式阅读顺序、引用缺失和排错清单5.1 这类教程包的典型目录结构网上流传的「基础与实例教程示例全部源码」压缩包解压后通常按阶段分目录命名一般是01_Basic、02_Commands、03_Entities、04_DataExchange这类。里面既有.dvb也有.sln意味着教程同时覆盖 VBA 和 VB.NET。不要从第一个文件按顺序看——很多初学者的错误是打开01_HelloWorld.dvb运行一下觉得简单直接跳到最后一个复杂工程结果被事务、事件、命令覆盖搞得一头雾水。推荐的阅读顺序是先把所有.sln后缀的 VB.NET 工程单独拎出来按工程名排序找到名字最短的那一两个。它们通常对应第 2 章里那个「第一个插件」类文件只有一个包含一个CommandMethod。打开后检查引用路径如果显示黄色感叹号需要手动重新指定 AutoCAD 安装目录下的AcMgd.dll。把所有.dvb文件用压缩工具解压到独立文件夹。.dvb本质上是复合文档里面嵌入了 VBA 工程可以用 7-Zip 直接打开查看内部结构。这个技巧比启动 AutoCAD 逐个加载要快得多。具体做法右键.dvb文件用 7-Zip 的「打开压缩包」能看到_VBA_PROJECT目录里面就是 VBA 的源码模块。找到名字里带Util、Common、Helper的模块这部分是跨示例复用的基础函数比如坐标转换、字符串处理。先读懂这些后面的实例里大量调用它们不先看会卡在「这个函数哪来的」这个问题上。5.2 加载时最常见的三个报错及处理对策报错信息出现原因处理对策未安装 VBA 支持库AutoCAD 组件缺失下载对应版本 VBA Enabler 并安装注意区分 32/64 位无法运行文档中的宏宏被安全级别拦截命令行输入OPTIONS→「系统」→「安全选项」把宏安全级别调为「启用所有宏」或对文件右键「解除锁定」加载 DLL 时找不到指定模块引用的托管 DLL 路径错误在 Visual Studio 中引用属性里把「复制本地」设为 False并把 AutoCAD 的托管 DLL 放到系统能找到的路径第三类报错在 64 位系统上偶尔还会伴随「未能加载文件或程序集 Autodesk.AutoCAD.Interop」。原因是引用了 Interop 程序集但没有启用 Embed Interop Types嵌入互操作类型。处理方式选中引用按 F4 打开属性把「嵌入互操作类型」改为 False。这个问题在 .NET Framework 4.7 以上特别常见因为默认是 True。如果遇到NETLOAD之后命令不响应检查你的类有没有写CommandMethod特性以及类本身是否声明为Public。VB.NET 中类默认是Public但如果你复制代码时把它改成了FriendAutoCAD 反射可能看不到方法。从排错效率看先看输出面板有没有「加载成功」字样有则说明程序集加载没问题问题在命令注册环节。5.3 WPS 环境下的 VBA 兼容性问题很多教程源码里附带了 Excel 导出相关的.bas模块源码直接用Excel.Application。如果你本机装的是 WPS且没有安装「WPS VBA 宏插件」运行到CreateObject(Excel.Application)时会报「自动化错误」。处理方式有两种其一安装 WPS 官方提供的 VBA 宏插件下载包但要注意它与 Office VBA 的运行库存在细微差异比如ActiveSheet的属性加载顺序不一样其二把导出目标从 Excel 换成 CSV 文件用Scripting.FileSystemObject直接写文本这样完全不依赖 Excel 组件。从集成角度看后者比前者更稳因为两台电脑的 Office/WPS 环境几乎不可能完全一致。6. 进阶技巧日期版本化、性能定位和功能边界确认做批处理工具时给输出文件加时间戳是基本操作。VBA 里Format(Now, yyyymmdd_hhmm)是主流写法但注意Now返回的是本地时间如果你的脚本分发给不同时区的同事使用导出的文件名可能排序错乱。这时用两个 API 组合获得 UTC 时间戳更可靠Dim utc As Date utc DateAdd(h, -TimeZone.CurrentTimeZone.GetUtcOffset(Now).Hours, Now) Dim stamp As String stamp Format(utc, yyyymmdd_hhmmss)DateAdd的参数说明第一个参数h表示按小时偏移第二个参数是时区差的小时数第三个参数是本地时间。VBA 没有内置的 UTC 获取函数这是最标准的模拟方式。「vba 日期比较大小」这类搜索意图也和此相关——直接用、比较两个Date类型即可但要注意 VBA 的日期类型是浮点数整数部分是天数小数部分是时间直接相减得到的是天数差乘以 24 才是小时差。性能定位方面写好的工具命令如果感觉慢别急着优化对象模型。先在代码前后加计时Dim sw As New System.Diagnostics.Stopwatch() sw.Start() 你的处理逻辑 sw.Stop() ed.WriteMessage(vbLf 耗时: sw.ElapsedMilliseconds ms vbLf)Stopwatch是 .NET 的计时器第一次调用会校验频率所以在循环体外创建不要在每次循环里重置。如果耗时集中在遍历实体的For Each考虑改用手工过滤比如用Editor.SelectAll加过滤条件而不是全图遍历再逐个判断类型。一个判断依据是10 万条实体的图纸全图遍历在 2 秒内完成是正常水平超过 5 秒说明你每访问一个实体都触发了 I/O 或字符串拼接等重操作需要把这类操作移到事务外。最后确认你的代码到底运行在哪个对象之上。命令行输入SYSVARMONITOR如果能看到 ACM 相关的变量说明当前进程既有 COM 也有 .NET 的宿主环境。调试时注意不要把Application类型混用——Application在 VBA 里是全局单例在 VB.NET 里是ApplicationServices.Application它的DocumentManager属性只在 .NET 侧存在。把 VBA 的ThisDrawing.Application.ActiveDocument直接翻译成 VB.NET 会报错正确对照是Application.DocumentManager.MdiActiveDocument。这是从示例源码迁移时最容易出错的一行代码。本文还有配套的精品资源点击获取
返回列表