ARTICLE DETAIL

资讯详情

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

AutoCAD.NET API插件开发全攻略:环境搭建、事务处理与避坑指南

AutoCAD.NET API插件开发全攻略:环境搭建、事务处理与避坑指南 简介一套基于AutoCAD .NET API的C#插件开发库面向需要在AutoCAD中实现自定义功能、批量自动化或二次开发的.NET开发者。资源封装了图形创建、图层管理、DWG读写等高频操作并提供源码、示例项目、说明文档与测试代码可显著减少重复编码。压缩包共61个文件以cs源码、dll程序集、xaml界面文件及csproj工程文件为主体辅以xml配置与README说明总大小5.36MB结构紧凑、易于直接引用。已有254人学习下载适合具备一定C#基础、希望高效掌握AutoCAD插件开发并快速落地的工程师与进阶学习者。1. AutoCAD.NET API 是什么为什么插件开发库里它最值得先学做 AutoCAD 二次开发的人迟早会在 LISP、ObjectARX 和 AutoCAD.NET API 之间做选择。LISP 写小脚本很快但批量处理几千个对象时慢得让人想放弃ObjectARX 性能最强可 C 门槛把大多数工程师挡在门外。AutoCAD.NET API 正好卡在中间用 C# 写托管代码通过 acdbmgd.dll 等程序集操作图形数据库编译成 DLL 后敲 NETLOAD 就能加载。网上的批量标注坐标工具、线段提取工具、Ribbon 定制插件基本都是这个路子。这套“插件开发库”本质就是 AutoCAD 安装目录里的几个托管 DLL配一个 C# 类库工程不需要额外下载庞大 SDK。它适合两类人被重复图纸操作折磨的工程师和要做标准化工具又不想碰 C 的二次开发新手。下面按落地顺序讲环境怎么搭、事务怎么用、坐标工具怎么写、坑在哪。2. 搭建 AutoCAD.NET 开发环境版本对应、三个 DLL 与第一个 NETLOAD 命令2.1 版本选型装哪个 AutoCAD 就引哪一套 DLL很多人搜“下载”是想找个独立开发包实际上真正要引用的“开发库”就在你本机 AutoCAD 安装目录里核心是三个文件acmgd.dllApplicationServices、Editor 等高层接口、acdbmgd.dllDatabaseServices、图形数据库相关、acCoreMgd.dllAutoCAD 2010 之后的核心能力。需要额外下载的只有 ObjectARX SDK它主要提供文档、头文件和示例工程纯 C# 开发不引它也能跑。版本对应关系是第一个大坑。目标框架定得比当前 AutoCAD 自带运行时高加载时直接报“需要更高版本 .NET Framework”定得太低则享受不到新语法还要忍受老 C# 的啰嗦。我按公司主流 CAD 版本定基线低版本编译的 DLL 在高版本上多数能跑反过来几乎必崩。AutoCAD 版本建议目标框架DLL 所在目录示例2010-2012.NET Framework 3.5/4.0C:\Program Files\Autodesk\AutoCAD 2012\2013-2014.NET Framework 4.0/4.5C:\Program Files\Autodesk\AutoCAD 2014\2015-2016.NET Framework 4.5C:\Program Files\Autodesk\AutoCAD 2016\2017-2019.NET Framework 4.6/4.7C:\Program Files\Autodesk\AutoCAD 2019\2020 及以后.NET Framework 4.7/4.8C:\Program Files\Autodesk\AutoCAD 2020\提示AutoCAD 2013 之后官方只有 64 位版本工程平台目标直接选 x64别用 AnyCPU 去赌。只有还要兼容 2012 的 32 位时才需要按 x86 单独编一份。2.2 第一个工程引用 DLL 时 Copy Local 必须为 False新建一个 C# 类库工程目标框架选 .NET Framework不是 .NET Core / .NET 5AutoCAD 托管 API 至今跑在完整框架上。添加引用时手工编辑 csproj 最直观ItemGroup Reference IncludeAcMgd HintPathC:\Program Files\Autodesk\AutoCAD 2020\AcMgd.dll/HintPath PrivateFalse/Private /Reference Reference IncludeAcDbMgd HintPathC:\Program Files\Autodesk\AutoCAD 2020\AcDbMgd.dll/HintPath PrivateFalse/Private /Reference Reference IncludeAcCoreMgd HintPathC:\Program Files\Autodesk\AutoCAD 2020\AcCoreMgd.dll/HintPath PrivateFalse/Private /Reference /ItemGroupHintPath指向你本机 CAD 安装目录换成自己的版本路径即可。Private必须为 False意思是编译时不要把这几个 DLL 复制到输出目录运行时直接复用 CAD 进程里已加载的同名程序集。很多人 NETLOAD 翻车就是因为 Copy Local 为 True输出文件夹里多了一份 acmgd.dll与 CAD 自带的版本冲突出现双重加载和类型不匹配。然后写第一个命令using Autodesk.AutoCAD.ApplicationServices; using Autodesk.AutoCAD.Runtime; namespace AcadPluginDemo { public class Hello { [CommandMethod(HELLO)] public void HelloCmd() { var doc Application.DocumentManager.MdiActiveDocument; if (doc null) return; // 没有打开图纸时直接返回 doc.Editor.WriteMessage(\n插件加载成功AutoCAD.NET API 跑通了。); } } }[CommandMethod(HELLO)]把方法注册为 AutoCAD 命令方法名随意命令名才是用户在命令行敲的东西建议全大写。MdiActiveDocument是当前活动文档多文档状态下可能为 null先判空是职业习惯。Editor.WriteMessage往命令行写文本是最廉价的调试输出比弹窗体好一百倍。2.3 加载与调试NETLOAD、外部程序启动和附加进程编译出 Hello.dll打开 AutoCAD命令行输入NETLOAD 选择编译输出的 AcadPluginDemo.dll HELLO命令行出现“插件加载成功”第一个插件跑通。但每次改代码都手动 NETLOAD 太慢我一般让 Visual Studio 直接拉起 AutoCAD项目属性 → 调试 → 启动外部程序填C:\Program Files\Autodesk\AutoCAD 2020\acad.exe。F5 启动 CAD 后再 NETLOAD 一次编译输出断点就能命中。如果 CAD 已经开着了也可以走“调试 → 附加到进程”选 acad.exe 时注意把代码类型勾成“托管”否则断点不会停。调试期还要注意两件事一是 AutoCAD 单实例进程还挂在后台时编译会报“文件被占用”先彻底退出再编译二是别把调试用的 DLL 加进 APPLOAD 的启动组否则卸载时会留下注册表残留后面重装 CAD 报错多半就是这么来的。3. 用 Transaction 读写图纸从遍历 Line 到批量标注坐标3.1 事务模型为什么所有读写都要包进 TransactionAutoCAD 的图形数据库不是普通内存集合实体对象由事务统一管理。读实体要tr.GetObject(id, OpenMode.ForRead)写要ForWrite操作完要么Commit()要么让事务 Dispose 自动回滚。最常见的翻车代码是GetObject 后直接改属性既不 Commit 也不 Dispose关图时提示“事务未结束”或者 StartTransaction 之后忘了 using异常一路抛到 CAD 内核acad.exe 直接消失。我的原则是一个命令一个事务全部读改写完后 Commit 一次。中间任何一步出错using 块自动 Abort图纸不会留下半改状态相当于 C# 里的后悔药。批处理场景尤其重要——几千个实体写一半崩了能不能整体回滚决定了今晚要不要加班。3.2 遍历图纸里的所有 Line看懂 BlockTable 两级结构“autocad dwg 获取线段 c#”这类需求核心代码就是两层遍历using Autodesk.AutoCAD.DatabaseServices; using Autodesk.AutoCAD.ApplicationServices; [CommandMethod(LISTLINES)] public void ListLines() { var doc Application.DocumentManager.MdiActiveDocument; var db doc.Database; using (var tr db.TransactionManager.StartTransaction()) { var bt (BlockTable)tr.GetObject(db.BlockTableId, OpenMode.ForRead); int count 0; foreach (var btrId in bt) // 遍历块表 { var btr (BlockTableRecord)tr.GetObject(btrId, OpenMode.ForRead); foreach (var entId in btr) // 遍历块表记录里的实体 { var ent (Entity)tr.GetObject(entId, OpenMode.ForRead); if (ent is Line line) { count; doc.Editor.WriteMessage($\n线段: {line.StartPoint} - {line.EndPoint}); } } } doc.Editor.WriteMessage($\n共 {count} 条 Line。); tr.Commit(); } }数据库结构是两级BlockTable 是块表BlockTableRecord 是块表记录。模型空间、图纸空间、每个块定义各对应一条记录所以遍历 bt 拿到的 btrId 只是 ObjectId还要再GetObject打开成实际对象。ent is Line是 C# 模式匹配只匹配 Line 实体多段线是 Polyline圆弧是 Arc不要混。整段只读全部 ForRead 打开性能最好。3.3 批量标注坐标工具先收集、后写入一个事务搞定网上流传的“autocad 批量标注坐标工具”各版本原理都是遍历实体收集坐标点往模型空间写 DBText。一个容易翻车的细节是不要在遍历 BlockTableRecord 的同时往它里面 AppendEntity。遍历集合时修改集合轻则漏项重则直接抛 InvalidOperationException。正确姿势是先收集、再写入[CommandMethod(BATCHCOORD)] public void BatchCoord() { var doc Application.DocumentManager.MdiActiveDocument; var db doc.Database; using (var tr db.TransactionManager.StartTransaction()) { var bt (BlockTable)tr.GetObject(db.BlockTableId, OpenMode.ForRead); var msId bt[BlockTableRecord.ModelSpace]; // 模型空间记录 ID var ms (BlockTableRecord)tr.GetObject(msId, OpenMode.ForWrite); var pts new ListPoint3d(); foreach (var btrId in bt) { if (btrId msId) continue; // 跳过模型空间本身 var btr (BlockTableRecord)tr.GetObject(btrId, OpenMode.ForRead); foreach (var entId in btr) { var ent (Entity)tr.GetObject(entId, OpenMode.ForRead); if (ent is Line line) { pts.Add(line.StartPoint); pts.Add(line.EndPoint); } } } foreach (var pt in pts) { var txt new DBText { TextString ${pt.X:F3},{pt.Y:F3}, Height 2.5, Position pt }; ms.AppendEntity(txt); // 加入模型空间 tr.AddNewlyCreatedDBObject(txt, true); // 加入事务管理 } tr.Commit(); doc.Editor.WriteMessage($\n共标注 {pts.Count} 个点。); } }bt[BlockTableRecord.ModelSpace]直接取模型空间 ID是固定写法。Height 2.5按图纸单位设置地形图常用 1.5-3.0机械图可能只要 1.0具体看你出图比例。F3保留三位小数坐标精度够用要带高程就再拼上pt.Z。AddNewlyCreatedDBObject(txt, true)是必须的新对象需要加入事务才会在 Commit 时写进数据库第二个参数 true 表示由 CAD 自动分配句柄。最后只 Commit 一次中间任何异常都会整体回滚。3.4 坐标和图上量出来不一样UCS、图纸单位与隐藏的变换新手最容易懵的是标出来的坐标和 DIST 量出来的对不上。原因通常是当前 UCS 不是世界坐标系。Line.StartPoint返回的是 WCS 坐标屏幕显示的是当前 UCS 坐标。两种处理标注前用doc.Editor.Command(_.UCS, )把 UCS 切回 WCS简单粗暴但不该动用户环境或者读UcsTable的GetUcsMatrix()做坐标变换。做通用工具我倾向后者但先跑通功能的话直接在 WCS 下标注更直观前提是告诉使用者“本工具只在 WCS 下可靠”。另外还要盯一眼单位设置单位为米的图纸和单位为毫米的图纸Height 2.5的效果差出三个数量级。4. 命令与 Ribbon 定制CommandMethod 参数、自定义 Image 和文档事件4.1 CommandMethod 的完整形态命令组、本地名与 CommandFlags只写[CommandMethod(BATCHCOORD)]可以跑但要做得像原生功能三个参数值得全用上[CommandMethod(AcadTools, BATCHCOORD, 批量标注, CommandFlags.Modal)] public void BatchCoord() { // 命令实体 }第一个参数是命令组名CAD 会把同组命令归到一起第二个是全局命令名第三个是本地化命令名允许用户敲中文“批量标注”执行第四个是 CommandFlags。开发阶段用CommandFlags.Modal最安全表示这个命令不能在其它命令执行过程中被调用Transparent虽然能让命令嵌套但调试时会把错误场景变复杂不建议新手碰。还有CommandFlags.UsePickSet可以让命令自动读取用户预选对象做批量工具很实用。4.2 Ribbon 上放自定义按钮RibbonButton 的 Image 与点击绑定“net autocad 中的 ribbonbutton 的自定义 image”是高频问题。Ribbon 接口在Autodesk.Windows命名空间里和图形数据库的 API 完全是两套先建 Tab 和 Panel再放按钮using Autodesk.Windows; using System.Windows.Media; using System.Windows.Media.Imaging; using System.IO; public static class RibbonBuilder { public static void AttachRibbon() { var ribbon ComponentManager.Ribbon; if (ribbon null) return; var tab ribbon.FindTab(AcadToolsTab) ?? ribbon.CreateTab(AcadToolsTab); var panel new RibbonPanel { Source new RibbonPanelSource { Title 坐标工具 } }; var btn new RibbonButton { Text 批量标注, ShowText true, Size RibbonItemSize.Large, Image LoadImage(coord16.png), LargeImage LoadImage(coord32.png), CommandParameter BATCHCOORD }; btn.Command new SendCommand(); panel.Source.Items.Add(btn); tab.Panels.Add(panel); ribbon.ActiveTab tab; } private static ImageSource LoadImage(string fileName) { var path Path.Combine( Path.GetDirectoryName(typeof(RibbonBuilder).Assembly.Location), fileName); return new BitmapImage(new Uri(path)); } }Image是 16x16 的小图标LargeImage是 32x32 的大图标。很多人只设了 Image大按钮模式下图标要么模糊要么拉伸变形。图标建议用透明背景 PNG白色轮廓在深色主题里更耐看。CommandParameter只是携带命令名字符串真正触发要绑定Commandusing System.Windows.Input; using Autodesk.AutoCAD.ApplicationServices; public class SendCommand : ICommand { public event EventHandler CanExecuteChanged; public bool CanExecute(object parameter) true; public void Execute(object parameter) { var doc Application.DocumentManager.MdiActiveDocument; doc?.Editor.Command(BATCHCOORD); } }Editor.Command(BATCHCOORD)是同步执行按钮点一下跑一次如果改用doc.SendStringToExecute是异步入队用户连点时命令会排队重复执行批量工具里容易造成重复标注。图标文件放在 DLL 同目录即可用Assembly.Location取路径比写死绝对路径可靠图纸在不同机器上分发时不会因为目录变化而找不到图。4.3 文档事件与委托在 DocumentCreated 里挂自动任务如果想让插件在每次打开图纸后自动做点什么比如给指定图层的线段自动标注需要订阅文档事件。C# 的 后面跟的匿名函数本质就是一个委托实例这也是“c# 委托”在插件开发里的常见落点public static void Subscribe() { var dm Application.DocumentManager; dm.DocumentCreated (s, e) { var doc e.Document; // 一定用事件参数别取 MdiActiveDocument doc.Editor.WriteMessage(\n文档已打开准备自动任务。); // 工程上这里会判断图框、比例尺后再决定是否执行 }; }DocumentCreated在文档完全建好之后触发此时数据库可读。关键点是事件参数e.Document才是触发事件的文档Application.DocumentManager.MdiActiveDocument在多文档切换时可能已经指向别的窗口。事件订阅必须在插件卸载时退订否则第二次 NETLOAD 会重复订阅一个事件执行两次这是“插件行为越来越怪”的经典来源。退订写法就是把 换回 -所以订阅时最好把委托保存到静态字段。5. AutoCAD.NET 开发避坑加载失败、崩溃与重绘卡顿的 5 条排查记录这一章是血泪经验集。前四章的功能代码你可能两天就能写完但真正消耗精力的永远是加载、崩溃和性能这三类问题。以下五条按出现频率排每一条我都亲手踩过。5.1 现象NETLOAD 报“无法加载程序集或它的某一个依赖项”原因有两类一是引用 DLL 的版本和当前 CAD 不匹配比如拿 AutoCAD 2024 的 acdbmgd.dll 编出来的插件拉到 2020 里加载类型定义对不上二是插件依赖了第三方程序集Newtonsoft.Json 之类而那个 DLL 不在 AutoCAD 的搜索路径里。解决先确认引用来自当前 CAD 安装目录HintPath与加载环境一致第三方依赖全部复制到插件 DLL 同目录必要时在AppDomain.CurrentDomain.AssemblyResolve里写手动加载逻辑。排查顺序看命令行 F2 窗口里的完整异常堆栈它比弹窗信息有用得多。5.2 现象命令一执行acad.exe 直接消失原因托管代码抛了未捕获异常事务没有回滚就一路炸到 CAD 内核。常见细节包括GetObject 打开后不 Dispose在事务外使用事务内的实体对象触发 ObjectDisposedException以及 StartTransaction 嵌套两次都 Commit外层事务状态错乱。解决每个 CommandMethod 方法体包一层 try/catchcatch 里写doc.Editor.WriteMessage(ex.ToString())让异常先在命令行现形所有 StartTransaction 一律 using不同事务之间只传 ObjectId绝不传实体引用。如果你发现崩溃只发生在第二次运行同一命令先怀疑静态字段里缓存了实体对象。5.3 现象批量标注几千个点屏幕每写一个就闪一下越来越卡原因每个 AppendEntity 之后系统自动触发重绘几千次下来把 CPU 拖死。这对应“关闭 autocad 自动重绘”的需求。解决命令开头把系统变量 REGENAUTO 关掉结尾恢复var doc Application.DocumentManager.MdiActiveDocument; var editor doc.Editor; editor.SetSystemVariable(REGENAUTO, 0); // 关闭自动重生成 try { // 一个事务里写完所有实体 } finally { editor.SetSystemVariable(REGENAUTO, 1); editor.Command(_.REGEN); // 最后统一刷一次 }SetSystemVariable把 REGENAUTO 设为 0抑制命令过程中的自动重生成finally 里恢复为 1 并手动 REGEN 一次保证最终显示正确。如果还卡检查是不是把每个实体单独包了一个事务——循环里建事务的开销是灾难性的。5.4 现象改了代码重新编译NETLOAD 执行的一直是旧的原因CAD 进程没退干净旧 DLL 被 acad.exe 占用编译输出文件覆盖失败或者你把调试 DLL 放进了启动组CAD 启动时优先加载了旧路径。解决编译前确认任务管理器里没有 acad.exe 残留把编译输出固定到一个专门目录别放 bin\Debug 这种会频繁变动的路径开发期不要注册启动组。这个坑和“autocad 卸载不干净无法重装”是同一类病根——程序集残留和注册表残留都会导致新旧版本互相打架部署时用干净目录替换卸载时把事件退订和文件删除都写成脚本。5.5 现象同一根线被标注了两次或者标注落到了块定义里面原因遍历了全部 BlockTableRecord把块定义里的几何也算进去了。块定义是“模板”不是实际摆出来的实例往里面写坐标既没有意义还可能把块定义污染所有块实例跟着出错。解决只遍历bt[BlockTableRecord.ModelSpace]外部参照和匿名块默认不展开如果确实要处理块实例里的实体用BlockReference的EffectiveName判断块名再用TransformBy或Explode把坐标变换到世界坐标系。判断逻辑放在最内层循环外面不要在循环里频繁查块表。6. 从“能跑”到“像产品”版本兼容、ACCoreConsole 验证和发布习惯6.1 一套代码兼容多个 AutoCAD 版本公司里不会所有人都用一个版本。常见做法是用条件编译csproj 里按目标版本加 DefineConstantsDefineConstants Condition$(ACADVersion)2020ACAD2020/DefineConstants DefineConstants Condition$(ACADVersion)2024ACAD2024/DefineConstants#if ACAD2020 private const string RibbonTab AcadTools2020; #else private const string RibbonTab AcadTools2024; #endif但决定兼容性的不是条件编译而是你用了哪些 API。事务、DBText、块表遍历这些老接口二十年来变化很少Ribbon 相关 API 反而是变动大的区域。核心功能全用底层 DatabaseServices界面层做薄每个版本单独编界面层这是最省事的维护结构。6.2 用 ACCoreConsole 做无界面验证发布前我习惯先用 ACCoreConsole 跑一遍它是 AutoCAD 自带的控制台程序不开界面也能执行命令和脚本。写一个验证脚本NETLOAD C:\publish\AcadTools.dll BATCHCOORD QSAVE然后命令行执行C:\Program Files\Autodesk\AutoCAD 2024\accoreconsole.exe /i test.dwg /s verify.scr /l en-US跑完打开 test.dwg 检查坐标文字数量和位置。这套流程能直接接进 CI每次改完事务代码先无头跑一遍比在界面上点点点快得多。我的习惯是每个新命令先在一张只有 10 个实体的小图上跑通再丢进几十 MB 的大图验证性能大图上炸出来的问题十有八九是事务没关干净或者跨越事务用了对象。这个习惯帮我在正式交付前拦下过至少三次崩溃。希望帮到你。本文还有配套的精品资源点击获取
返回列表