
简介这份资源面向AutoCAD二次开发人员与.NET工程师聚焦不依赖AutoCAD环境直接读写DWG文件这一常见难题。Teigha原名OpenDwg、DWGdirect相关开发资料在网络上极为稀缺作者花费大量精力收集整理并给出VB.NET与C#两套可直接参考的源码示例帮助读者快速理解DWG文件解析与写入的核心流程。压缩包共165个文件约61.5MB包含62个dll类库、6个vb与6个cs源码文件、6个exe示例程序以及config、manifest、resx、sln、csproj、vbproj等工程配置与资源文件另附DWGdirect_NET_3_02、Teigha_Net_40010及配套帮助文档结构完整便于直接编译调试。目前已有3826人学习下载适合需要脱离AutoCAD批量处理图纸、做格式转换或搭建轻量级DWG读写工具的中高级开发者参考。1. Teigha4 读写 DWG为什么老工程师还在用它手上接过一个老厂区的竣工图甲方要求把 DWG 里的图层、块参照、扩展数据全部抽出来再按规则回写一批标注。试过在线转换、试过 ODA 官方新版本最后能稳定跑通、还能在 VB.NET 老项目里直接引用的还是 Teigha4 那一套。Teigha4 是 ODA 早期发布的一套 DWG/DXF 读写组件配套的 DWGdirect_NET_3_02 和 Teigha_Net_40010 是当年 .NET 平台接入 DWG 的主流方式C# 和 VB.NET 都能直接调。它解决的核心问题很具体不依赖 AutoCAD 客户端纯托管代码打开 DWG读实体、改属性、另存回 DWG。适合谁做土木、机械、GIS 数据转换的 .NET 开发者尤其是维护存量项目、需要离线批量处理图纸的人。这一章先把「它是什么、能干什么」讲清楚后面几章落到代码和参数。2. Teigha4 与 DWGdirect_NET 的选型账版本、依赖与运行环境2.1 三个组件到底谁管什么标题里出现了三个名字很多人第一次接触会混。Teigha4 是 ODA 的 SDK 代号底层是 C 的 DWG 读写引擎DWGdirect_NET_3_02 是这套引擎在 .NET 上的早期封装命名空间以Teigha.DatabaseServices为主Teigha_Net_40010 是后续的 .NET 程序集版本接口基本延续但部分类名和加载方式有调整。三者关系可以这样理解Teigha4 是引擎DWGdirect_NET 和 Teigha_Net 是不同时期给 .NET 用的「外壳」。选型时先确认一件事你的项目是 .NET Framework 还是 .NET Core。DWGdirect_NET_3_02 和 Teigha_Net_40010 都是面向 .NET Framework 的典型是 4.x。如果你的项目已经迁到 .NET 6/8直接引用会报BadImageFormatException或找不到依赖这时候要么把读写逻辑拆成一个独立的 Framework 控制台程序要么换 ODA 更新的 .NET 包。我一般建议存量项目继续用 DWGdirect_NET_3_02新项目如果非要用 Teigha 系优先试 Teigha_Net_40010接口更整齐。2.2 部署时必须带上的原生依赖纯托管程序集只是壳真正干活的是同目录下的原生 DLL。以 DWGdirect_NET_3_02 为例bin目录里除了TD_Mgd_3.02_10.dll这类托管程序集还会有TD_Db_3.02_10.dll、TD_Ge_3.02_10.dll、TD_Root_3.02_10.dll等原生库。少一个运行时就抛DllNotFoundException。部署清单可以按这个表核对文件类型示例作用缺失后果托管程序集TD_Mgd_3.02_10.dll.NET 调用入口编译期找不到类型原生数据库库TD_Db_3.02_10.dllDWG 实体读写打开图纸即崩原生几何库TD_Ge_3.02_10.dll点、向量、矩阵读取坐标报错根库TD_Root_3.02_10.dll内存与基础服务初始化失败提示这些 DLL 必须和你的 exe 在同一目录或者放在系统 PATH 能找到的目录。放到子目录再用SetDllDirectory也行但老项目里最常见的问题就是「编译过了一跑就崩」九成是原生库没拷全。2.3 初始化与许可别跳过的那几行Teigha 系组件在真正打开数据库前需要做一次宿主初始化。不同版本写法略有差异DWGdirect_NET_3_02 常见做法是调用Teigha.Runtime.Services的Initialize并传入宿主应用名。下面这段是 C# 的最小初始化骨架using Teigha.Runtime; using Teigha.DatabaseServices; // 宿主应用名随便给但不要为空 HostApplicationServices.HostVersion 1.0; // 初始化 Teigha 运行时只需一次 Services.Initialize(MyDwgApp); // 程序退出前记得反初始化 // Services.Uninitialize();逻辑说明Services.Initialize会加载原生库并建立内部服务表不调用它直接new Database()会抛异常。参数是宿主应用标识用于日志和许可校验不是路径。Uninitialize在进程退出前调用避免原生资源泄漏。VB.NET 写法完全对应Imports Teigha.Runtime Imports Teigha.DatabaseServices HostApplicationServices.HostVersion 1.0 Services.Initialize(MyDwgApp)参数说明HostVersion是字符串填你的应用版本即可不影响读写结果。初始化只需在程序启动时做一次不要每次打开图纸都调。3. 用 C# 和 VB.NET 读写 DWG 的最小可运行路径3.1 打开 DWG 并遍历模型空间实体读 DWG 的核心对象是Database模型空间通过BlockTable找到ModelSpace块表记录再遍历其中的实体。下面这段 C# 打开一个 DWG打印所有实体的类型和句柄using (Database db new Database(false, true)) { // 第二个参数 true 表示允许打开未保存的图纸 db.ReadDwgFile(D:\test.dwg, FileOpenMode.OpenForReadAndAllShare, false, null); using (Transaction tr db.TransactionManager.StartTransaction()) { BlockTable bt (BlockTable)tr.GetObject(db.BlockTableId, OpenMode.ForRead); BlockTableRecord ms (BlockTableRecord)tr.GetObject(bt[BlockTableRecord.ModelSpace], OpenMode.ForRead); foreach (ObjectId id in ms) { Entity ent tr.GetObject(id, OpenMode.ForRead) as Entity; if (ent null) continue; // Handle 是 DWG 内部唯一标识调试时很有用 Console.WriteLine(${ent.GetType().Name} handle{ent.Handle}); } tr.Commit(); } }逻辑说明ReadDwgFile把磁盘文件读进内存数据库StartTransaction开启事务所有读取都要在事务里做。BlockTableRecord.ModelSpace是模型空间固定入口布局空间要用PaperSpace。ent.Handle是十六进制字符串回写时用它定位实体最稳。参数说明ReadDwgFile第一个参数是路径第二个FileOpenMode常用OpenForReadAndAllShare允许多进程同时读第三个bool是「允许打开未保存」一般 false第四个是密码无密码传 null。VB.NET 对应写法Using db As New Database(False, True) db.ReadDwgFile(D:\test.dwg, FileOpenMode.OpenForReadAndAllShare, False, Nothing) Using tr As Transaction db.TransactionManager.StartTransaction() Dim bt As BlockTable tr.GetObject(db.BlockTableId, OpenMode.ForRead) Dim ms As BlockTableRecord tr.GetObject(bt(BlockTableRecord.ModelSpace), OpenMode.ForRead) For Each id As ObjectId In ms Dim ent As Entity TryCast(tr.GetObject(id, OpenMode.ForRead), Entity) If ent IsNot Nothing Then Console.WriteLine(ent.GetType().Name handle ent.Handle) End If Next tr.Commit() End Using End Using3.2 修改实体属性并另存回 DWG读只是第一步真正落地往往是改。比如把所有圆半径放大 1.1 倍再另存为新文件using (Database db new Database(false, true)) { db.ReadDwgFile(D:\in.dwg, FileOpenMode.OpenForReadAndAllShare, false, null); using (Transaction tr db.TransactionManager.StartTransaction()) { BlockTable bt (BlockTable)tr.GetObject(db.BlockTableId, OpenMode.ForRead); BlockTableRecord ms (BlockTableRecord)tr.GetObject(bt[BlockTableRecord.ModelSpace], OpenMode.ForRead); foreach (ObjectId id in ms) { Circle c tr.GetObject(id, OpenMode.ForWrite) as Circle; if (c null) continue; c.Radius * 1.1; // 半径放大 } tr.Commit(); } // 另存为不要覆盖原文件 db.SaveAs(D:\out.dwg, DwgVersion.Current); }逻辑说明写操作必须用OpenMode.ForWrite打开对象否则抛eNotOpenForWrite。SaveAs的第二个参数是 DWG 版本DwgVersion.Current表示当前库支持的版本也可以指定AC10152000、AC10182004等。参数说明SaveAs第一个参数是输出路径路径目录必须存在否则抛异常。注意不要对同一个Database既ReadDwgFile又SaveAs到原路径容易在部分版本上出现文件锁冲突。稳妥做法是读原文件、写到新文件再在业务层替换。3.3 块参照与扩展数据的读取DWG 里大量信息藏在块参照和 XData 里。读块参照要先拿BlockReference再通过BlockTableRecord找块定义XData 用GetXDataForApplication。下面这段读指定 AppName 的扩展数据using (Transaction tr db.TransactionManager.StartTransaction()) { BlockTable bt (BlockTable)tr.GetObject(db.BlockTableId, OpenMode.ForRead); BlockTableRecord ms (BlockTableRecord)tr.GetObject(bt[BlockTableRecord.ModelSpace], OpenMode.ForRead); foreach (ObjectId id in ms) { Entity ent tr.GetObject(id, OpenMode.ForRead) as Entity; if (ent null) continue; // 读取注册在 MYAPP 下的扩展数据 ResultBuffer rb ent.GetXDataForApplication(MYAPP); if (rb null) continue; foreach (TypedValue tv in rb) { Console.WriteLine($type{tv.TypeCode} value{tv.Value}); } } tr.Commit(); }逻辑说明GetXDataForApplication返回ResultBuffer里面是TypedValue列表TypeCode表示数据类型1000 字符串、1040 实数、1070 短整型等。参数说明AppName 必须和写入时注册的一致大小写敏感。VB.NET 里For Each tv As TypedValue In rb即可。4. 避坑与排查Teigha4 读写 DWG 最常见的 5 个翻车点4.1 现象一打开图纸就抛 DllNotFoundException原因原生 DLL 没拷到输出目录或者位数不匹配x64 程序引了 x86 库。解决确认托管程序集和原生库位数一致把TD_*.dll全部拷到 exe 同目录用dumpbin /headers看目标平台或在项目属性里锁定x64。4.2 现象读到的实体数量比 AutoCAD 里少原因只遍历了模型空间没处理布局空间和块定义内部实体或者图纸里有代理实体Proxy EntityTeigha 默认不展开。解决同时遍历BlockTableRecord.PaperSpace对块参照用BlockReference.BlockTableRecord递归进去代理实体需要额外处理读不到就跳过并记录。4.3 现象SaveAs 后文件打不开或提示损坏原因写操作没提交事务就 SaveAs或者对只读打开的对象做了修改。解决确保所有写操作在tr.Commit()之后再做SaveAs写对象必须OpenMode.ForWrite输出路径不要和输入相同。4.4 现象中文图层名或文字乱码原因DWG 内部字符串编码和 .NET 默认编码不一致老版本尤其明显。解决读取时用TypedValue的原始值不要过早ToString()写回时确认Database的DwgCodepage设置必要时显式指定代码页。4.5 现象批量处理几百张图后内存暴涨原因Database和Transaction没释放原生内存不归 GC 管。解决每个文件用using包住Database事务及时Commit或Abort处理完一批后手动GC.Collect()并WaitForPendingFinalizers()虽然不优雅但对付原生库有效。5. 进阶用 Handle 做增量回写与版本兼容验证真正在生产里跑最怕的是「全量重写」把别人改过的图覆盖掉。我后来固定用一个习惯读的时候把每个实体的Handle和关键属性记下来回写时只按Handle定位并改需要改的字段其余原样保留。下面这段演示按 Handle 找实体并改文字内容// handleStr 来自上一次读取时记录的 ent.Handle.ToString() Handle h new Handle(Convert.ToInt64(handleStr, 16)); ObjectId id db.GetObjectId(false, h, 0); using (Transaction tr db.TransactionManager.StartTransaction()) { DBText txt tr.GetObject(id, OpenMode.ForWrite) as DBText; if (txt ! null) { txt.TextString 已复核; // 只改内容不动位置和样式 } tr.Commit(); }逻辑说明Handle是 DWG 内部持久标识跨会话稳定GetObjectId用Handle反查ObjectId第三个参数 0 表示不限制。参数说明Convert.ToInt64(handleStr, 16)把十六进制字符串转长整型注意Handle可能超过int范围必须用long。版本兼容验证我一般做三步先用 Teigha_Net_40010 读一遍再用 DWGdirect_NET_3_02 读同一张图对比实体数量和关键属性然后写回一个副本用 AutoCAD 或 ODA 查看器打开确认无损坏最后把副本再读一次确认回写内容可被再次解析。这套流程跑下来基本能挡住「读得到、写坏了」这类最坑的问题。踩过最疼的一次是没做 Handle 校验就直接全量 SaveAs结果把同事加的一批标注冲掉了返工了一下午。从那以后凡是回写先备份、再按 Handle 增量改、最后做一次回读验证成了我的固定动作。希望帮到你。本文还有配套的精品资源点击获取