ARTICLE DETAIL

资讯详情

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

AutoCAD .NET API插件开发全攻略:从环境搭建到踩坑实战

AutoCAD .NET API插件开发全攻略:从环境搭建到踩坑实战 简介AutoCAD Code Pack 是为使用 AutoCAD .NET API 的 C# 开发者准备的插件开发库面向需要定制 CAD 功能、实现自动化工作流的工程与设计领域开发者。该库封装了图形处理、图层管理、DWG 读写等常用操作可减少重复编码帮助开发者在创建几何形状、分析模型、定制工具栏等场景中快速落地。压缩包共 61 个文件约 5.36MB。其中 20 个 cs 源代码文件为核心涵盖命令注册、布局管理、数据存储等模块13 个 dll 为编译好的引用库可直接集成xaml 文件用于界面定制csproj 与 sln 构成完整工程便于打开和调试。另有少量 XML 配置、说明文档及 license 文件方便了解用法与授权。已有 254 人浏览学习这份资料适合个人学习与中小型插件开发场景。开发者既可将其作为工具库直接调用也可作为源码范例学习 AutoCAD .NET API 插件架构与常见开发模式省去从零搭建基础代码的繁琐过程。1. 为什么说 AutoCAD .NET API 是插件开发里的“强力工具”第一次听到“AutoCAD插件开发库使用AutoCAD.NET API进行开发的强力工具”这个说法很多人第一反应是找个现成源码包直接抄。实际上这个“插件开发库”并不是某个封装好的第三方库而是 Autodesk 官方提供的托管接口——AutoCAD .NET API。它把 Lisp 难以处理的批量绘图、复杂 UI、与外部系统比如 DCS、上位机的数据交换变成了 C# 开发者熟悉的类库调用。在工程制图和自动化集成场景里这是一条比 VBA 和 ObjectARX C 更稳的落地路径尤其适合已经有 C# 基础、不想啃 C 的工程师。下面按我自己的落地习惯把版本选型、环境搭建、第一个能跑的插件、必踩的坑和进阶技巧一次讲透。2. 把环境搭对版本对应、要引用的 DLL 和让 AutoCAD 认出插件2.1 版本不对一切都是玄学ObjectARX 和 .NET Framework 的硬对应AutoCAD .NET API 不是独立安装的它随 ObjectARX SDK 一起提供每个 AutoCAD 版本只保证接受对应版本的托管程序集。你需要先到 Autodesk 官网的开发者页面下载与你的 AutoCAD 版本一致的 ObjectARX SDK。这一步不能省用别人博客里拷贝出来的老 DLL几乎都会在 NETLOAD 时报“无法加载程序集”或直接闪退。版本对应关系大致是AutoCAD 20162017 的目标框架多为 .NET Framework 4.520182020 一般是 4.7/4.7.22021 之后基本是 4.8再新的版本要看安装包里的 Readme。这不是拍脑袋而是因为 AutoCAD 进程本身是 .NET Framework 宿主如果你的 C# 项目目标框架高于宿主加载时会直接报“此程序集由更高版本的 .NET Framework 构建”低于宿主则可能遇到一些莫名其妙的类型行为。AutoCAD 版本常见目标框架说明2016-2017.NET Framework 4.5较老需兼容旧代码2018-2020.NET Framework 4.7/4.7.2大多数公开例子默认值2021 及以后.NET Framework 4.8以官方 Readme 为准Visual Studio 里新建类库后记得把“目标框架”按上表选好再把“平台目标”设为 AnyCPU 或 X64。AutoCAD 2020 之后基本只有 64 位版本千万不能选 x86也不要勾选“Prefer 32-bit”。这些设置看起来不起眼但几乎每个新手都会在这里栽一次。2.2 引用哪几个 DLLAcMgd、AcDbMgd、AcCoreMgd 各管什么解压 ObjectARX SDK 后找到inc目录里面有三个核心托管程序集AcMgd.dll负责应用生命周期、文档集合、编辑器交互和命令行输出。AcDbMgd.dll负责图形数据库、块表、实体读写是开发时用得最多的程序集。AcCoreMgd.dll提供核心运行时和部分新的 API较新版本必须引用。在 Visual Studio 里右键“引用 → 添加引用 → 浏览”定位到这三个 DLL全部选上。有的老代码还会引Autodesk.AutoCAD.Interop那是给 COM 兼容场景用的新项目可以先不碰。添加完引用后在“解决方案资源管理器”里找到这三个引用把属性中的“复制本地”Copy Local设为 False。如果不改编译时会把 DLL 拷到你的输出目录插件加载时 AutoCAD 会优先使用输出目录里的程序集一旦和你当前 CAD 版本内置的版本不一致就会报“无法加载文件或程序集”或触发奇怪的权限异常。我一般还会在项目里建一个References文件夹把三个 DLL 放进去方便多个项目共用一份避免每次装新 SDK 都要改引用路径。这个习惯能省很多换版本时的麻烦。2.3 用 IExtensionApplication 给插件一个“入口”插件类库和普通类库的区别是要有一个主类实现IExtensionApplication。这个接口有两个方法Initialize()在插件被加载后调用Terminate()在卸载时调用。很多人以为必须在这里注册命令其实[CommandMethod]标注的方法只要 DLL 被加载就能被 AutoCAD 识别入口类主要用来注册事件、创建 UI、预加载资源。using Autodesk.AutoCAD.ApplicationServices; using Autodesk.AutoCAD.Runtime; namespace MyCadTool { public class PluginMain : IExtensionApplication { public void Initialize() { // 初始化里不要操作图形数据库此时文档还没完全就绪 Application.DocumentManager.MDocumentActivated OnDocActivated; } public void Terminate() { Application.DocumentManager.MDocumentActivated - OnDocActivated; } private void OnDocActivated(object sender, DocumentCollectionEventArgs e) { // 文档激活后可以在这里创建或更新 Ribbon、菜单 Application.MainWindow.Title 我的CAD工具; } } }逻辑说明Initialize适合做一次性订阅事件Terminate里做对称的退订。MDocumentActivated事件在文档新建、打开或切换时都会触发是安全地访问当前文档和数据库的时机。参数说明DocumentCollectionEventArgs中携带当前文档对象但不要直接缓存它因为文档可以被关闭。2.4 三种加载方式手动 NETLOAD、启动组和 Bundle 自动加载调试阶段最常用的是手动加载。在 AutoCAD 命令行输入NETLOAD弹窗选择编译出来的 DLL命令行如果没有任何红色错误说明加载成功。这种方式的好处是随时可以重新加载新版本坏处是每次重开 AutoCAD 都要手动操作不适合交付给最终用户。第二种是把 DLL 加入启动组。输入APPLOAD打开“加载/卸载应用程序”对话框点击“内容”把你的 DLL 添加进去。下次 AutoCAD 启动时会自动加载但这种方式会锁住 DLL 文件重新编译前必须先关闭 AutoCAD否则 Visual Studio 会提示“无法复制文件另一个程序正在使用”。第三种适合分发就是做 Bundle 自动加载。在插件目录里放一个PackageContents.xml写明插件程序集和目标版本用户把整个文件夹放到 CAD 的“ApplicationPlugins”目录下即可。这个方式安装体验最好但需要额外打包逻辑适合后期把工具交给非开发人员的场景。前两种足以覆盖日常开发和内部工具的使用。3. 写出第一个能跑的 C# 插件从画直线到批量导线段坐标3.1 事务是 AutoCAD 插件的安全区为什么所有写操作都要在 Transaction 里AutoCAD 的图形数据库是带事务机制的几乎所有对实体和表的读写都要包在一个事务里。很多 C# 新手会问为什么不 new 一个 Line 然后直接加到数据库因为数据库可能同时被其他命令访问事务可以保证要么全部生效要么全部回滚。最常见的做法是用using包裹Transaction代码简洁且不会忘记提交。using Autodesk.AutoCAD.ApplicationServices; using Autodesk.AutoCAD.DatabaseServices; using Autodesk.AutoCAD.Geometry; using Autodesk.AutoCAD.Runtime; namespace MyCadTool { public class DrawCommands { [CommandMethod(DrawLine)] public void DrawLine() { Document doc Application.DocumentManager.MdiActiveDocument; Database db doc.Database; using (Transaction tr db.TransactionManager.StartTransaction()) { BlockTable bt (BlockTable)tr.GetObject(db.BlockTableId, OpenMode.ForRead); BlockTableRecord btr (BlockTableRecord)tr.GetObject( bt[BlockTableRecord.ModelSpace], OpenMode.ForWrite); using (Line line new Line(new Point3d(0, 0, 0), new Point3d(100, 100, 0))) { btr.AppendEntity(line); tr.AddNewlyCreatedDBObject(line, true); } tr.Commit(); } } } }逻辑说明先打开块表再打开模型空间表记录然后创建直线实体并追加到表记录里。AddNewlyCreatedDBObject(line, true)告诉事务这个对象是新建的必须在提交前登记。参数说明BlockTableRecord.ModelSpace是模型空间在块表中的固定 ObjectId不要自己搜表查找OpenMode.ForWrite表示当前块表记录可写否则追加会报错。3.2 从 DWG 取线段pick 一条直线并把坐标打到命令行这个需求对应热词里常出现的“autocad dwg 获取线段 c#”。工业现场经常要把图纸里的线段数据取出来发给测量工具或上游系统。先实现最基础的单条拾取后续可以扩展成批量。using Autodesk.AutoCAD.EditorInput; [CommandMethod(GetLineData)] public void GetLineData() { Document doc Application.DocumentManager.MdiActiveDocument; Editor ed doc.Editor; PromptEntityResult res ed.GetEntity(\n选择一条直线: ); if (res.Status ! PromptStatus.OK) { return; } using (Transaction tr doc.Database.TransactionManager.StartTransaction()) { Line line tr.GetObject(res.ObjectId, OpenMode.ForRead) as Line; if (line null) { ed.WriteMessage(\n选中的不是直线。); } else { ed.WriteMessage($\n起点: {line.StartPoint}, 终点: {line.EndPoint}, 长度: {line.Length}); } tr.Commit(); } }逻辑说明GetEntity让用户在屏幕上拾取一个对象返回结果里带有 ObjectId。tr.GetObject根据 ObjectId 拿到实体对象这里用as Line做类型判断避免用户选中多段线或圆导致强转异常。参数说明PromptEntityResult.Status是枚举值只有PromptStatus.OK才能继续Editor.WriteMessage输出到命令行比 MessageBox 更适合 CAD 环境。3.3 批量把模型空间线段导出 CSV上位机和 DCS 联动的前置动作在自动化产线里CAD 图纸里的线段坐标经常要交给上位机、DCS 或者视觉软件比如 VisionMaster去做路径规划。最常见的方式是把当前模型空间的所有 Line 批量导出成 CSV。这个思路能和“c# 上位机通用框架”无缝衔接上位机只需要轮询文件或读数据库CAD 这边做成一个命令按钮即可。[CommandMethod(ExportLines)] public void ExportLines() { Document doc Application.DocumentManager.MdiActiveDocument; Database db doc.Database; var lines new Liststring(); string fileName C:\Temp\lines.csv; using (Transaction tr db.TransactionManager.StartTransaction()) { BlockTable bt (BlockTable)tr.GetObject(db.BlockTableId, OpenMode.ForRead); BlockTableRecord btr (BlockTableRecord)tr.GetObject( bt[BlockTableRecord.ModelSpace], OpenMode.ForRead); foreach (ObjectId id in btr) { DBObject obj tr.GetObject(id, OpenMode.ForRead); if (obj is Line line) { lines.Add(${line.StartPoint.X},{line.StartPoint.Y},{line.EndPoint.X},{line.EndPoint.Y}); } } tr.Commit(); } System.IO.Directory.CreateDirectory(System.IO.Path.GetDirectoryName(fileName)); System.IO.File.WriteAllLines(fileName, lines, System.Text.Encoding.UTF8); doc.Editor.WriteMessage($\n共导出 {lines.Count} 条直线。); }逻辑说明遍历块表记录里的所有 ObjectId用tr.GetObject逐个打开并判断类型。这里只处理模型空间不处理块内嵌套实体如果需要块内线段要递归进入BlockTableRecord。参数说明Encoding.UTF8很重要否则 Excel 打开 CSV 时中文表头或注释会乱码文件路径建议用可配置参数不要硬编码在生产工具里。3.4 为什么不能缓存 DBObject从 access violation c0000005 到对象生命周期热词里有“c# 调用 c 出现 access violation c0000005”这在 AutoCAD .NET 开发里很常见。AutoCAD .NET API 本质是托管包装器底层是 C 对象。比如你在事务里拿到一个Line对象事务提交后这个包装器可能已经失效如果把它存到静态字段里下一次命令再调用属性就会访问已释放的 C 对象抛出 access violation c0000005整个 AutoCAD 可能直接崩溃。正确做法是只缓存ObjectId每次需要实体时再通过tr.GetObject(id, OpenMode.ForRead)获取。ObjectId是稳定的句柄而DBObject实例不是。这个原则也解释了为什么不能把Document或Database缓存后跨文档使用。记住在 AutoCAD 插件里缓存对象实例等于埋雷缓 ID、不缓存对象。4. 避坑与排查1603、未知命令、DLL 被占用4.1 AutoCAD 2020 安装失败错误代码 1603现象安装 AutoCAD 2020 中途中断日志或弹窗显示“安装失败错误代码 1603”。有些人重装系统或卸载不干净后也会遇到。原因1603 是 Windows Installer 的通用失败代码绝大多数情况是 .NET Framework 组件损坏、杀毒软件拦截或者 Visual C 运行库残留。AutoCAD 安装程序依赖这些基础组件任何一环出问题都会报 1603。解决先彻底卸载 AutoCAD用 Autodesk 官网的卸载工具清理注册表和安装目录再修复 .NET Framework去“控制面板 → 程序和功能 → 启用或关闭 Windows 功能”里重新勾选或者用官方修复工具最后临时关闭杀毒软件右键安装包“以管理员身份运行”。如果还不行看安装日志里的具体 MSI 错误码而不是只搜 1603。4.2 NETLOAD 能加载 DLL但输命令提示“未知命令”现象命令行输入 NETLOAD 后显示加载成功没有红色错误但执行你的命令名时提示“未知命令”。原因DLL 里没有定义[CommandMethod]方法或者方法不是 public或者类不是 public。还有可能是命令名拼写不一致比如你定义的是DrawLine却输入drawline应该没问题但如果你定义了DrawLine而输入DRAWLINEAutoCAD 对命令不区分大小写所以问题多半还是出在方法签名上。解决检查命令类和方法都声明为public命令方法加上[CommandMethod(DrawLine)]重新编译后先卸载再加载。也可以用NETLOAD加载后在命令行输入(vl-load-all)并用(command DrawLine)测试观察错误输出。如果加载时命令行出现异常堆栈优先看第一条异常往往就是初始化逻辑的问题。4.3 编译时提示 DLL 被占用或者“AutoCAD 卸载不干净无法重装”现象Visual Studio 生成时提示“无法复制文件另一个程序正在使用”重装 AutoCAD 时又提示已经有旧版本残留。原因AutoCAD 进程没完全退出加载的插件 DLL 被进程锁定另外 acwebtray、acad 等后台进程会驻留卸载程序不一定能全部清理。解决编译前先保存所有图纸再关闭 AutoCAD。如果进程还残留用任务管理器结束acad.exe和acwebtray.exe。调试项目时可以在 VS 生成事件里加一句强制结束命令但会丢未保存图纸慎用taskkill /F /IM acad.exe /T这条命令会强行关闭所有 AutoCAD 实例只适合本地开发机。重装前还要清掉C:\Program Files\Autodesk残留目录和注册表HKEY_LOCAL_MACHINE\SOFTWARE\Autodesk下的对应项再用官方卸载工具扫一遍。4.4 许可管理器不起作用或“现在将关闭 AutoCAD”现象NETLOAD 加载插件后AutoCAD 弹出“许可管理器不起作用或未正确安装现在将关闭 AutoCAD”然后直接退出。原因插件在初始化阶段就触发了许可相关 API而当前许可还没准备好或者引用的托管程序集版本和当前 CAD 版本不一致导致托管层调用 C 层时进入错误分支。解决不要在IExtensionApplication.Initialize里操作图形数据库也不要在此处创建 Ribbon把 UI 和业务初始化推迟到文档激活事件或第一次执行命令时。检查引用的AcMgd.dll/AcDbMgd.dll/AcCoreMgd.dll是否都来自当前版本 ObjectARX SDK千万别混用。如果改过系统日期或注册表先用/r参数重置 AutoCAD 配置再试。4.5 新增实体后提交崩溃AddNewlyCreatedDBObject 忘了调用现象代码里写了btr.AppendEntity(line)提交事务或者命令结束时 AutoCAD 崩溃。原因AppendEntity只是把实体放进块表记录但事务并不知道这是一个“新创建且需要被持有”的对象。没有登记事务提交时会尝试按既有对象处理触发内存访问错误。解决在AppendEntity后立刻调用tr.AddNewlyCreatedDBObject(line, true);让事务接管新对象btr.AppendEntity(line); tr.AddNewlyCreatedDBObject(line, true);第二个参数传true表示这是刚由托管 API 创建的数据库对象。这个坑很隐蔽代码越写越长越容易漏掉建议把“创建实体”封装成公共方法统一处理登记逻辑。5. 把插件做成“像官方功能”Ribbon 按钮、日志留后路和回归验证5.1 用 Ribbon API 加一个带自定义图标的大按钮热词里提到“net autocad中的ribbonbutton的自定义image”这是很多工具从命令行升级到图形界面的第一步。AutoCAD 的 Ribbon 接口在Autodesk.Windows命名空间下需要引用AcWindows.dll。下面是最小示例using Autodesk.Windows; public static void AddRibbonTab() { RibbonControl ribbon ComponentManager.Ribbon; RibbonTab tab ribbon.FindTab(MyTools); if (tab null) { tab new RibbonTab { Id MyTools, Title 我的工具 }; ribbon.Tabs.Add(tab); } RibbonPanelSource panel new RibbonPanelSource { Title 绘制与导出 }; RibbonButton btn new RibbonButton { Text 画线, Size RibbonItemSize.Large, Image LoadImageFromResource(MyCadTool.Resources.drawline.png), CommandParameter DrawLine }; panel.Items.Add(btn); tab.Panels.Add(panel); }逻辑说明FindTab防止重复创建Image属性接收System.Drawing.Image建议把图标作为嵌入资源加载不要用外部图片路径否则部署后容易丢图。参数说明RibbonItemSize.Large表示大按钮CommandParameter只是携带命令名字符串真正的点击绑定需要根据你使用的 AutoCAD 版本在按钮事件里调用SendStringToExecute或命令处理器。不同版本 Ribbon API 有差异升级版本时要回归测试。5.2 给插件加异常日志和回归验证不留黑匣子插件交付给现场后最怕的是用户只说“刚才报错了”你却不知道错在哪。我习惯在每个命令入口统一包一层 try/catch并订阅AppDomain.CurrentDomain.UnhandledException把异常时间、命令名和堆栈写到本地日志。这样至少不会变成黑匣子。AppDomain.CurrentDomain.UnhandledException (s, e) { System.IO.File.AppendAllText(C:\Temp\cadPlugin.log, ${System.DateTime.Now}: {(e.ExceptionObject as System.Exception)?.Message}\n); };代码说明这个订阅写在Initialize里所有未捕获异常都会落到日志文件。参数说明日志路径要保证可写正式工具建议放在用户文档目录不要放系统盘根目录。升级版本前我会跑一遍“遍历当前图纸所有对象并逐个用GetObject打开”的回归命令看是否有类型转换触发 access violation。以前我也习惯吞异常直到现场崩溃过一次才长教训。现在宁可先慢一步也要把日志和回归验证留足这比多写几个命令更值。希望帮到你。本文还有配套的精品资源点击获取
返回列表