
简介面向C#开发者的AutoCAD .NET插件开发辅助库以模块化封装方式降低原生API调用门槛适合需要实现参数化绘图、图层批量管理、几何数据交互及自定义界面等二次开发场景。压缩包共61个文件以C#源码、DLL动态库、XAML界面文件及项目工程文件为主整体仅6.06MB结构紧凑便于快速部署。库内预置几何元素生成、图层控制、DWG文件操作等模块并附带演示案例、接口文档与质量验证套件可帮助开发者建立自动化图形生成流程、构建CAD与外部数据源的传输通道、开发模型几何属性分析工具及定制专业化工具栏命令。已有101人学习下载对于从事AutoCAD二次开发的C#工程师这份资源提供了经过工程验证的代码框架能有效提高代码复用效率将精力集中于业务逻辑与创新实现。1. AutoCAD .NET API开发库不是黑匣子C#插件让图形自动化从手工修图变成批量生成每天面对几百张图纸重复改图层、加标注、导明细表这些在键盘上反复按快捷键的瞬间就是 AutoCAD .NET API 开发库最典型的用武之地。用 C# 写一个插件挂进 AutoCAD把重复操作变成一条命令这就是“图形自动化”最务实的落地方式。它适合已经在用 CAD、又有 C# 基础、想摆脱手工重复劳动的工程师。接下来要讲的内容不需要你懂计算机图形学也不要求你精通 CAD 底层只要会 C# 的基本语法能跟着步骤建一个类库工程就能动手做出第一个属于自己的 CAD 工具。2. 搭建AutoCAD .NET API开发环境从空类库到加载一个命令的完整闭环2.1 开发前提让 AutoCAD 和 Visual Studio“对上话”的版本组合用 C# 做 AutoCAD 插件本质是把一个 .NET 类库编译成 DLL再由 AutoCAD 进程加载并执行。这需要你提前把三件事对齐Visual Studio 版本、目标框架、AutoCAD 位数。我推荐 Visual Studio 2019 或 2022目标框架选 .NET Framework 4.7.2 或 4.8AutoCAD 版本在 2016 到 2026 之间都可以。注意这里不要用“类库(.NET Core)”或“类库(.NET Standard)”模板AutoCAD 的托管 API 是挂在 .NET Framework 上的项目模板选成 Core 会导致引用程序集时遇到一堆依赖问题。我最近在 AutoCAD 2026 上做验证接口和之前的 2020 版本没有本质区别习惯了一版就能跨版本用。另一个容易被忽略的是目标平台。AutoCAD 2026 是 64 位进程你的 DLL 必须编译成 x64或 AnyCPU 但去掉“首选 32 位”。否则加载时会直接抛 System.BadImageFormatException。这一点在 NETLOAD 阶段会让你一头雾水明明代码没错就是加载不进去。2.2 创建类库工程并引用两个核心程序集打开 Visual Studio新建项目选择“Visual C# - 类库(.NET Framework)”。项目名称建议用英文避免路径含中文导致 AutoCAD 加载异常。然后添加引用在解决方案管理器里右键“引用”-“添加引用”-“浏览”定位到 AutoCAD 安装目录比如 C:\Program Files\Autodesk\AutoCAD 2026\找到两个核心 DLLAcDbMgd.dll负责图形数据库所有实体、图层、线型、块表的读写都靠它。AcMgd.dll负责应用程序框架包含 Document、Editor、DocumentManager 等。引用后务必将这两个程序集的“复制本地”属性设为 False。否则生成的 DLL 会把 AutoCAD 程序集复制到输出目录加载时因版本冲突引发“程序集未能加载”的玄学问题。这一步我每次写教程都要强调因为默认引用本地 DLL 的坑太常见了。下面写一个最小的可用插件。在项目里新建一个类代码如下using Autodesk.AutoCAD.ApplicationServices; using Autodesk.AutoCAD.EditorInput; using Autodesk.AutoCAD.Runtime; namespace CadDemo { public class Hello { [CommandMethod(HELLOCAD)] public static void Run() { // 获取当前活动文档 Document doc Application.DocumentManager.MdiActiveDocument; // 获取编辑器对象用于与命令行交互 Editor ed doc.Editor; ed.WriteMessage(\nHello from C#!); } } }这段代码的逻辑分三步先通过 Application.DocumentManager.MdiActiveDocument 拿到当前摆在最前面的图纸再通过 Document.Editor 拿到命令行编辑器最后用 WriteMessage 把字符串输出到 CAD 命令行。注意 CommandMethod 特性里的命令名“HELLOCAD”建议全大写AutoCAD 命令名不区分大小写但全大写能在冲突提示里看得更清楚。命令名不能带空格否则 AutoCAD 无法识别。2.3 用 NETLOAD 加载 DLL 并运行命令编译通过后打开 AutoCAD在命令行输入 NETLOAD 并回车弹出的文件选择框里选中 bin\Debug 目录下刚生成的 CadDemo.dll。加载成功后命令行输入 HELLOCAD 并回车命令行会输出“Hello from C#!”。如果运行命令后没看到任何输出优先检查以下两点一是你的命令名是否拼错二是项目里是否出现两个同名命令。调试阶段我建议在 WriteMessage 前加一个断点然后用附加进程的方式确认代码是否真正执行到了这比盲目改代码快得多。2.4 附加到 AutoCAD 进程断点调试的配置方法在 Visual Studio 里点“调试”-“附加到进程”在进程列表里找到 acad.exe选中后点“附加”。然后回到 AutoCAD确保当前文档是刚才加载插件的那个文档再执行 HELLOCAD 命令。这里有一个高频问题断点不命中。现象是代码明明执行了但 Visual Studio 没有任何反应。常见原因是调试器类型不对新的 VS 默认启用“仅我的代码”和托管兼容模式。解决办法是打开“调试”-“选项”-“调试”-“常规”把“使用托管兼容模式”勾上并把“启用仅我的代码”取消勾选。这个配置改完后断点基本都能命中。2.5 让 DLL 随 AutoCAD 自动加载免去每次手敲 NETLOAD每次手动 NETLOAD 只适合开发调试。给别人用的时候更专业的方式是把 DLL 放到 AutoCAD 支持文件搜索路径下再用 APPLOAD 命令打开“启动组”把 DLL 加进去。这样 AutoCAD 每次启动都会自动加载。启动组的配置路径是命令行输入 APPLOAD - 右侧“启动组”-“内容”- 添加 DLL。注意添加后要勾选“添加到历史记录列表”。这种方式对单机分发够用但要批量部署还是会用注册表或安装包脚本去做启动加载这里不展开。3. 认识AutoCAD .NET API的三个核心对象Application、Document与Database3.1 从顶层进程到当前图纸为什么一定要先理清这三者第一次写 AutoCAD 插件的人很容易被 Application、Document、Database 三个对象绕晕。我用一个比喻解释Application 是 AutoCAD 这个应用程序本身一个进程只有一个DocumentManager 是它的文档管理器负责管所有已打开的图纸MdiActiveDocument 是当前显示在最前面的那张图而 Database 是这张图背后的全部图形数据——模型空间、图层表、线型表、文字样式表全在 Database 里。这三个对象的分工对应到写插件时的思考路径就是Application 管全局状态Document 管当前上下文Database 管你要读写的图形数据。大多数插件的标准开头都是这三行代码// 获取当前文档 Document doc Application.DocumentManager.MdiActiveDocument; // 获取编辑器 Editor ed doc.Editor; // 获取当前文档的图形数据库 Database db doc.Database;Document 还提供了很多实用的能力比如保存、关闭、获取当前视口等。Database 则是所有实体对象的“根”。理解这三层的层级关系后面学习事务和实体操作会顺利得多。3.2 Transaction事务为什么所有读改写操作都必须在事务里进行AutoCAD 的数据库不允许你随意拿一个对象直接修改。所有读改写操作必须包在 Transaction 里。原因在于 AutoCAD 要保证图形数据一致性事务提交时才把内存里的修改写回真正的数据库中途可以回滚Rollback。理解这一点你就能解释很多奇怪现象代码跑完图没变化——多半是事务没提交代码报“对象已被释放”——多半是事务结束后还在用实体对象。标准事务模板如下using (Transaction tr doc.Database.TransactionManager.StartTransaction()) { // 1. 打开块表块表是数据库图形对象的根目录 BlockTable bt tr.GetObject(doc.Database.BlockTableId, OpenMode.ForRead) as BlockTable; // 2. 打开模型空间块表记录 BlockTableRecord btr tr.GetObject(bt[BlockTableRecord.ModelSpace], OpenMode.ForWrite) as BlockTableRecord; // 3. 在这里创建或修改实体 tr.Commit(); // 必须提交否则改动全部丢弃 }StartTransaction 是事务开始using 块保证无论代码是否抛异常事务对象都会被正确释放。Commit 是把修改写回数据库的唯一时机去掉 Commit 不会报错但你的所有改动都不会出现在图纸上这是我见过最多人踩的坑没有之一。这里有两个核心参数要理解。一是 GetObject 的第一个参数是 ObjectIdAutoCAD 用它在数据库里定位对象第二个参数是打开模式OpenMode.ForRead 表示只读OpenMode.ForWrite 表示需要改写它。如果只读打开却调用实体的写方法会抛出“不能以只读模式写入对象”的异常这个提示会直接告诉你代码哪里写错了。3.3 ObjectId 与 DBObject图形对象的“身份证”与“本体”在 AutoCAD .NET API 中每一个图形对象圆、直线、文字、图块引用在数据库里都有一个唯一标识 ObjectId。它是进入对象的钥匙事务里通过 GetObject 把 ObjectId 转换成真实的 DBObject 实体比如 Circle、Line、DBText。这里有一个关键区别ObjectId 不是句柄Handle。ObjectId 只在当前图形会话内有效关闭图纸再打开同一个圆会得到一个新的 ObjectId。而句柄是字符串形式在同一张 DWG 文件的生命周期内保持稳定。所以如果你要把对象标识存到外部数据库应该存句柄而不是 ObjectId。很多项目在做图纸校审数据回写时翻车就是因为把 ObjectId 存进了数据库下次打开图纸就找不到了。3.4 从命令行命令到事件通知扩展插件触发时机的另一种路径除了用 CommandMethod 注册命令AutoCAD .NET API 还支持事件机制。例如想在“新文档创建后”自动执行初始化逻辑可以订阅 DocumentManager.DocumentCreatedApplication.DocumentManager.DocumentCreated (s, e) { Document doc e.Document; doc.Editor.WriteMessage(\n新文档已打开自动执行初始化); };事件回调有一个重要限制它运行在 AutoCAD 的内部上下文中不能直接弹窗或做长时间阻塞操作。我一般只在事件里记录日志或做轻量初始化把重逻辑延后到命令行命令或 Idle 事件中。另外事件注册要放在静态构造函数或插件初始化入口里不能放在命令内部。否则每次执行命令都会重复注册一次回调事件触发时你的逻辑会被叠加执行多次排查起来很迷惑。4. 图形自动化操作的实战拆解遍历、批量创建、批量修改与合并DWG4.1 遍历模型空间的所有实体并按类型过滤图形自动化的第一步往往是“先弄清楚图里有什么”。遍历模型空间并统计不同类型实体数量是写批量处理工具的地基。下面这段代码实现了对当前图纸模型空间中圆和文字的统计using (Transaction tr doc.Database.TransactionManager.StartTransaction()) { // 打开块表 BlockTable bt tr.GetObject(doc.Database.BlockTableId, OpenMode.ForRead) as BlockTable; // 打开模型空间 BlockTableRecord btr tr.GetObject(bt[BlockTableRecord.ModelSpace], OpenMode.ForRead) as BlockTableRecord; int circleCount 0; int textCount 0; // 遍历模型空间里的每个对象 foreach (ObjectId id in btr) { DBObject obj tr.GetObject(id, OpenMode.ForRead); if (obj is Circle) circleCount; else if (obj is DBText || obj is MText) textCount; } doc.Editor.WriteMessage($\n圆:{circleCount}, 文字:{textCount}); tr.Commit(); }这段代码的核心在 foreach 这一行BlockTableRecord 本身就是一个对象容器遍历它就能拿到模型空间里所有实体的 ObjectId。这里用 OpenMode.ForRead 打开每个对象是做统计的最优选择因为不需要修改任何实体只读模式不会触发写锁遍历大图纸时性能更好。注意 MText 是多行文字DBText 是单行文字两者是不同的类统计文字时要把它们都算上。4.2 批量创建实体从循环画圆到自动生成一组标注批量创建是自动化里最常见的需求。比如要在一张图上生成一圈阵列孔手工画既慢又容易出错。下面这段代码创建一个中心圆并围绕它生成 6 个小圆using (Transaction tr doc.Database.TransactionManager.StartTransaction()) { BlockTable bt tr.GetObject(doc.Database.BlockTableId, OpenMode.ForRead) as BlockTable; BlockTableRecord btr tr.GetObject(bt[BlockTableRecord.ModelSpace], OpenMode.ForWrite) as BlockTableRecord; // 创建中心圆圆心(0,0,0)法向量(0,0,1)半径10 Circle center new Circle(new Point3d(0, 0, 0), new Vector3d(0, 0, 1), 10); btr.AppendEntity(center); tr.AddNewlyCreatedDBObject(center, true); // 生成6个小圆均匀分布在半径20的圆周上 for (int i 0; i 6; i) { double angle i * Math.PI / 3; Point3d pt new Point3d(20 * Math.Cos(angle), 20 * Math.Sin(angle), 0); Circle c new Circle(pt, new Vector3d(0, 0, 1), 2); btr.AppendEntity(c); tr.AddNewlyCreatedDBObject(c, true); } tr.Commit(); }Circle 构造函数的三个参数分别是圆心、法向量、半径。法向量表示圆所在平面的朝向(0,0,1) 代表 XY 平面这是画平面圆最常用的值。AppendEntity 把新实体放到模型空间里AddNewlyCreatedDBObject 把实体注册到当前事务中。这两步缺一不可只调 AppendEntity 会出现“写入无法跟踪”的问题。如果要创建文字用 DBText设置它的 Position、Height、TextString 即可配合遍历找到的点坐标就能自动生成标注。4.3 批量修改实体属性把指定图层的所有线型改成虚线真实项目里经常要批量清理图纸规范比如把“中心线”图层的所有直线改成虚线线型。这里最容易踩一个“先读后写”的坑我用下面这段代码展示正确写法using (Transaction tr doc.Database.TransactionManager.StartTransaction()) { BlockTable bt tr.GetObject(doc.Database.BlockTableId, OpenMode.ForRead) as BlockTable; BlockTableRecord btr tr.GetObject(bt[BlockTableRecord.ModelSpace], OpenMode.ForRead) as BlockTableRecord; foreach (ObjectId id in btr) { DBObject obj tr.GetObject(id, OpenMode.ForRead); if (obj is Line line line.Layer 中心线) { // 先读后写第二次用 ForWrite 打开同一个 id 升级权限 tr.GetObject(id, OpenMode.ForWrite); line.LineType CENTER; } } tr.Commit(); }这里的关键点在于先用 ForRead 打开每个对象并判断是不是目标图层确定需要改写后再用同一个 ObjectId 调一次 ForWrite 升级权限。这是遍历修改的标准套路。如果一开始就用 ForWrite 打开所有对象处理大图纸时写锁争用会拖慢速度而且可能引发事务冲突。还要注意一个前提图形数据库中必须存在名为 CENTER 的线型否则给 LineType 赋值会抛异常。修改前应该用 LinetypeTable 查询线型是否已加载没有就先创建或从线型文件加载。很多批量工具在别人机器上跑一半崩掉就是因为目标图里没加载 CENTER 线型。4.4 批量合并 DWG 与导出报表把自动化扩展到图纸之外有同事让我做“c# dwg合并”工具把十几个文件里的实体汇总到一张图上。这种需求听起来复杂其实思路就是两个数据库之间的实体搬运。把源 DWG 用 Database.ReadDwgFile 读进内存遍历它的模型空间再把实体复制到目标数据库。示意如下// 用内存数据库读取源 DWG 文件 Database srcDb new Database(false, true); srcDb.ReadDwgFile(D:\drawing\part01.dwg, FileShare.Read, true, ); using (Transaction tr destDb.TransactionManager.StartTransaction()) { // 遍历 srcDb 的模型空间复制实体到 destDb // 需要处理图层、线型、文字样式等依赖对象 } srcDb.Dispose();这个操作中最容易翻车的是“依赖对象”问题。从源图复制的某些实体可能引用了源图独有的图层、线型或文字样式目标数据库中没有这些定义导致实体复制过去后样式全乱或直接报错。严谨的做法是先用 IdMapping 把依赖的表记录一并复制过去。初次实现时建议先做小样试验复制一两个实体确认样式无异常后再扩展到全量。统计完图元数量后自然想把结果导出成 Word 或 Excel。常见方案是用 OpenXML 或 NPOI在插件里直接引用第三方 DLL 就能生成报表。这里不展开代码但方向是行得通的。5. AutoCAD插件开发避坑指南5个反复出现的真实问题这 5 个坑按出现频次排列前三个几乎每个项目都会遇到后两个在工程部署时格外明显。每条按“现象 - 原因 - 解决”写方便直接对照。5.1 改了没效果但也不报错事务 Commit 不是可选项现象代码完整执行完命令行无任何异常回到图纸一看图形一个没变。原因最常见的是代码分支里漏了 tr.Commit()。比如你的逻辑里有一段 if 判断满足条件时修改实体不满足时什么都不做。如果 Commit 写在 if 内部条件为假时事务自然没有提交。另一个常见原因是把 StartTransaction 写在了 using 块外面异常时事务被 Dispose 但没 CommitAutoCAD 默认回滚。解决让事务体始终包在 using 块里所有修改逻辑结束之后再调用 Commit。调试时在 Commit 处打断点看执行顺序是否如预期。一个更稳妥的习惯是只要代码里出现“修改实体”操作写完立刻检查同一作用域内有没有 Commit 调用没有就补上。这个习惯能帮你省掉大量排查时间。5.2 实体对象被释放把 DBObject 存进 List 的后果现象第一次遍历图纸时把找到的 Circle 都放进了 ListCircle第二次循环想修改它们程序抛出 ObjectDisposedException。原因事务结束并释放后事务内打开的所有 DBObject 临时对象都会被释放。你虽然把引用存进了 List但对象本体已经失效。这是 AutoCAD .NET API 最典型的“黑匣子”——看起来对象还在其实已经被数据库回收了。解决长期保存的应该是 ObjectId不是实体对象。需要操作时再新开一个事务用 GetObject 重新获取。参考写法ListObjectId ids new ListObjectId(); using (Transaction tr ...) { // 把需要的 ObjectId 加入 ids } // 后续操作 using (Transaction tr2 doc.Database.TransactionManager.StartTransaction()) { foreach (ObjectId id in ids) { DBObject obj tr2.GetObject(id, OpenMode.ForWrite); // 修改 } tr2.Commit(); }这个坑常出现在“先收集后处理”的批处理工具中。新手容易把实体对象存在字段或静态集合里第一次运行时没问题第二次重新打开图纸就抛异常。请记住DBObject 是事务的临时视图ObjectId 才是数据库的稳定索引。5.3 串了会话就找不到对象ObjectId 与句柄的区别现象把 ObjectId.ToString() 存到了数据库程序重启后按这个字符串去获取对象要么抛异常要么返回 null。原因ObjectId 只在当前图形会话内有效。关闭 DWG 再打开每个实体的 ObjectId 都会变化。跨会话保存对象标识应该用句柄Handle它是一串稳定的字符串。解决保存实体时用实体.Handle.ToString()下次打开文档后在事务里遍历块表记录找到 Handle 匹配的实体。如果只做小范围查找可以用 Handle.TryGetObject 先转成 ObjectId再打开实体。注意句柄在同一张 DWG 文件生命周期内稳定但如果你把实体复制到另一张图句柄会重新分配不能跨文件依赖它。5.4 锁文档导致整个命令卡死现象插件运行到一半AutoCAD 假死光标一直转圈。原因在事件回调或命令中手动调用了 Document.LockDocument()并且没有释放锁。AutoCAD 在命令行命令执行期间本身就会对当前文档加锁你再手动加一次相当于试图获取一个自己已经持有的锁结果就是死锁。解决如果确实需要显式锁定文档推荐用 using 语句包裹using (DocumentLock docLock doc.LockDocument()) { // 在此安全地操作当前文档 }DocumentLock 对象释放时自动解锁。另一个经验是命令内部通常不需要手动 LockDocument因为 AutoCAD 在执行命令时已经帮你锁好了。手动加锁的场景多见于事件回调或后台线程但这个方向很容易引发并发冲突我一般会尽量避免在事件回调里操作文档改为记录日志并延后到 Idle 事件处理。5.5 加载 DLL 时报“尝试加载格式不正确的程序集”现象在 64 位 AutoCAD 2026 上用 NETLOAD 加载 DLL弹出 System.BadImageFormatException。原因项目目标平台被设置为 x86或勾选了“首选 32 位”。AutoCAD 2026 是 64 位进程只能加载 x64 程序集。解决右键项目 - 属性 - 生成 - 平台目标选 x64同时取消勾选“首选 32 位”。此外确认项目中所有引用的第三方 DLL 也都是 x64 版本。这里给一个简单的对照表AutoCAD 位数插件平台目标32 位AutoCAD 2013 及更早多数为 32 位x8664 位AutoCAD 2014 及以上常见为 64 位x64如果想快速确认当前 AutoCAD 位数可以在命令行输入 ABOUT查看弹出的“关于”对话框里的版本描述。别小看这个坑我见过好几个团队做的工具在测试机器上正常一搬到新机器就报这个错最后发现是安装的 AutoCAD 位数不同。6. 进阶玩法PaletteSet交互面板、acCoreConsole批处理与性能验证6.1 用 PaletteSet 把命令行拆进侧边栏命令行命令适合自己用给同事用还是面板更友好。PaletteSet 是 AutoCAD 的侧边栏容器可以承载自定义 WinForms 控件。我写过一个小工具面板代码如下[CommandMethod(SHOWMYPANEL)] public static void ShowPanel() { PaletteSet ps new PaletteSet(我的批量工具); ps.Size new Size(320, 480); ps.Add(批量工具, new MyUserControl()); ps.Visible true; }这里需要额外引用 AcWindows.dllPaletteSet 就在这个程序集里。Add 方法的第一个参数是选项卡标题第二个参数是自定义 UserControl。如果你平时做“c#上位机”开发接触过 WinForms 或 WPF会发现这套面板模型几乎零学习成本事件绑定、控件布局都能直接复用。这也是 C# 做 AutoCAD 二次开发比 C ObjectARX 更亲民的重要原因。6.2 用无界面批处理应对几百张图纸AutoCAD 安装目录下有一个 acCoreConsole.exe可以不启动完整界面直接执行 NETLOAD 和命令。批量处理图纸时这是最佳选择不用一台台打开 CAD 界面。常见用法是acCoreConsole.exe /i D:\batch\a.dwg /s D:\batch\run.scr /out D:\batch\out.log其中 /i 是输入文件/s 是脚本文件脚本里写要加载的 DLL 和要运行的命令/out 是日志输出路径。我第一次用这个模式处理 150 张图纸时原来逐个打开人工操作要一下午脚本化后约 8 分钟跑完。这正是“高效实现”在批量场景下最直观的体现。6.3 量化耗时用 Stopwatch 验证你的优化是否有效不要靠感觉说“程序变快了”。我在调试批量删除工具时最初用了大量 WriteMessage 输出进度25 万实体整整跑了 5 秒多去掉输出后直接降到 0.8 秒。这里用 Stopwatch 量化var sw System.Diagnostics.Stopwatch.StartNew(); // 批量操作 sw.Stop(); doc.Editor.WriteMessage($\n耗时: {sw.ElapsedMilliseconds} ms);如果耗时异常优先检查循环里是否有反复获取文档锁、频繁 WriteMessage、反复打开同一个对象的情况这些都会指数级放大耗时。另外AutoCAD .NET API 的事务不是线程安全的不要试图用多线程并行处理同一张图纸里的实体那会让问题变成真玄学。合理做法是减少事务数量、减少控制台输出、用批量接口替代逐个处理。如果要从这些经验里挑一条最该养成的习惯我会说永远保存 ObjectId 而不是实体对象永远用 using 包住事务永远在发布前用批处理模式跑一遍完整流程。这三个习惯帮我少加了很多个班也让我在做图形自动化时不再被各种“灵异现象”牵着走。希望帮到你。本文还有配套的精品资源点击获取