
简介这份资源面向AutoCAD二次开发人员与.NET开发者聚焦于不依赖AutoCAD环境直接读写DWG文件这一常见难题。Teigha原名OpenDWG、DWGdirect相关开发资料在网络上较为稀缺作者花费大量精力收集整理并给出VB.NET读取DWG的完整实现适合需要做图纸解析、批量处理或格式转换的工程师参考。压缩包共165个文件约61.5MB包含62个dll核心库、6个vb与6个cs源码文件、6个exe示例程序以及sln、csproj、vbproj等工程文件另附config、xml、txt等配置与说明文档并打包了DWGdirect_NET_3_02、Teigha_Net_40010及配套帮助文件。目前已有3825人学习下载。读者可从中获得可编译运行的VB.NET与C#示例源码、Teigha各版本类库与帮助文档以及工程配置与依赖引用方式便于快速搭建读写DWG的开发环境并理解接口调用思路。1. Teigha4 读写 DWG从托管代码到图纸数据库的落地路径如果你手上有一批 DWG 图纸需要在不装 AutoCAD 的服务器上批量读取图层、块表、实体坐标甚至回写修改那么 Teigha4现属 ODA 的 Drawings SDK几乎是绕不开的选择。它把 DWG 当成一个可编程的数据库来暴露而不是一个只能靠桌面软件打开的黑匣子。标题里提到的 DWGdirect_NET_3_02 和 Teigha_Net_40010本质是同一套 .NET 封装在不同版本阶段的产物前者是早期命名后者是 4.x 时代的托管程序集。配套的 vb.net、C# 原码解决的是「怎么在托管环境里把非托管图纸引擎调起来」这件事。这篇笔记面向的是需要把 DWG 读写嵌进自己系统的工程师尤其是做上位机、做图纸转换、做批量处理的场景。下面从环境搭建讲到实体遍历再到回写和排错尽量把每一步落到能复现的命令和参数上。2. Teigha4 托管环境搭建程序集、平台与第一个能跑通的读取程序2.1 为什么是 Teigha4 而不是直接调 AutoCAD先说选型理由。AutoCAD 的 COM 自动化依赖本机安装完整 CAD进程外调用慢批量处理时容易卡死而且授权成本高。Teigha4 提供的是进程内 SDK直接解析 DWG 二进制格式不依赖 CAD 进程读取一张中等复杂度的图纸通常在几十到几百毫秒级别。它支持 DWG 的多个版本R14 到较新的格式并且同时提供 C、.NET、Java 等绑定。对于 C# 和 vb.net 开发者Teigha_Net_40010 里的托管程序集就是入口。需要区分两个概念DWGdirect_NET_3_02 是较早的 .NET 封装版本命名空间和 API 与后来的 Teigha_Net 有差异Teigha_Net_40010 对应 4.x 系列程序集命名通常带 TD_ 前缀比如 TD_Mgd.dll、TD_DbMgd.dll。两者不要混用引用错了会出现类型找不到或方法签名不匹配。2.2 程序集引用与平台目标设置Teigha 的 .NET 封装底层是非托管 DLL所以平台目标必须明确。常见做法是把项目平台设为 x64 或 x86不要用 Any CPU否则运行时会报「无法加载 DLL」或「试图加载格式不正确的程序」。在 C# 项目里需要引用的核心程序集一般包括程序集作用TD_Mgd.dll托管入口服务、数据库、事务等TD_DbMgd.dll数据库对象、实体、符号表TD_GeMgd.dll几何计算相关TD_RootMgd.dll基础类型与运行时引用后把 Teigha 的非托管 DLL 目录加入 PATH或者复制到输出目录。下面是一个最小的 C# 读取程序打开 DWG 并输出模型空间里的实体数量。using System; using Teigha.Runtime; using Teigha.DatabaseServices; class Program { static void Main(string[] args) { // 初始化 Teigha 运行时必须在使用任何数据库对象前调用 using (new Services()) { string dwgPath D:\samples\demo.dwg; using (Database db new Database(false, true)) { // 第二个参数 true 表示以只读方式打开避免误写 db.ReadDwgFile(dwgPath, FileOpenMode.OpenForReadAndAllShare, true, null); using (Transaction tr db.TransactionManager.StartTransaction()) { BlockTable bt (BlockTable)tr.GetObject(db.BlockTableId, OpenMode.ForRead); BlockTableRecord modelSpace (BlockTableRecord)tr.GetObject( bt[BlockTableRecord.ModelSpace], OpenMode.ForRead); int count 0; foreach (ObjectId id in modelSpace) { Entity ent tr.GetObject(id, OpenMode.ForRead) as Entity; if (ent ! null) { count; Console.WriteLine(ent.GetType().Name handle ent.Handle); } } Console.WriteLine(实体总数: count); tr.Commit(); } } } } }逻辑说明Services是 Teigha 运行时的入口负责加载非托管引擎必须最先创建、最后释放。Database代表一个 DWG 文件ReadDwgFile的第三个参数控制是否允许写回只读场景传 true 更安全。Transaction是 Teigha 的对象访问机制所有数据库对象都要通过事务获取直接持有 ObjectId 而不开事务会抛异常。BlockTableRecord.ModelSpace是模型空间图纸里的可见实体基本都在这里。参数说明FileOpenMode.OpenForReadAndAllShare表示只读且允许其他进程同时打开适合服务器批量读取。如果后续要写回改成OpenForReadAndWrite但要注意文件锁。db.ReadDwgFile的最后一个参数是密码加密图纸才需要。vb.net 的写法结构一致只是语法不同Imports Teigha.Runtime Imports Teigha.DatabaseServices Module Module1 Sub Main() Using svc As New Services() Dim db As New Database(False, True) db.ReadDwgFile(D:\samples\demo.dwg, FileOpenMode.OpenForReadAndAllShare, True, Nothing) Using tr As Transaction db.TransactionManager.StartTransaction() Dim bt As BlockTable DirectCast(tr.GetObject(db.BlockTableId, OpenMode.ForRead), BlockTable) Dim ms As BlockTableRecord DirectCast(tr.GetObject(bt(BlockTableRecord.ModelSpace), OpenMode.ForRead), BlockTableRecord) 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) End If Next tr.Commit() End Using End Using End Sub End Module2.3 第一个程序跑不通时先查这三处第一平台目标。Any CPU 在 64 位系统上默认以 64 位运行但如果你引用的非托管 DLL 是 32 位就会报 BadImageFormatException。第二PATH 里是否包含 Teigha 的非托管 DLL 目录缺 DLL 时异常信息通常是「找不到指定的模块」。第三Services 是否在 Database 之前创建顺序反了会直接崩溃。这三点覆盖了新手八成以上的启动失败。3. 遍历 DWG 实体与符号表图层、块、文字和坐标怎么取3.1 实体遍历的通用模式与类型判断DWG 里的可见对象都继承自 Entity常见类型包括 Line、Circle、Arc、Polyline、Text、MText、Insert块引用。遍历时不要假设只有这几种用基类接收再判断具体类型更稳。下面这段代码演示如何按类型提取关键数据。foreach (ObjectId id in modelSpace) { Entity ent tr.GetObject(id, OpenMode.ForRead) as Entity; if (ent null) continue; switch (ent) { case Line line: Console.WriteLine($Line: {line.StartPoint} - {line.EndPoint}); break; case Circle circle: Console.WriteLine($Circle: center{circle.Center}, r{circle.Radius}); break; case Polyline pl: Console.WriteLine($Polyline: vertices{pl.NumberOfVertices}); for (int i 0; i pl.NumberOfVertices; i) { var pt pl.GetPoint2dAt(i); Console.WriteLine($ v{i}: {pt}); } break; case Text txt: Console.WriteLine($Text: {txt.TextString} at {txt.Position}); break; case Insert ins: Console.WriteLine($BlockRef: {ins.Name}, pos{ins.Position}); break; } }逻辑说明switch模式匹配是 C# 7 以后的写法vb.net 里用TypeOf ... Is判断。Polyline 的顶点用GetPoint2dAt取二维点如果是三维多段线要用Polyline3d并遍历Vertex3d。Insert 代表块引用它的Name是块名实际几何在块表记录里需要再开一次事务取 BlockTableRecord。参数说明OpenMode.ForRead表示只读打开写回时改ForWrite。GetPoint2dAt返回的是 OCS 坐标如果图纸有非默认的挤出方向需要转换到 WCS常见做法是用pl.Elevation和pl.Normal做变换。3.2 图层表与块表的读取图层信息在 LayerTable 里块定义在 BlockTable 里。读取图层可以拿到颜色、线型、是否冻结等属性批量出图或做图层过滤时很有用。LayerTable lt (LayerTable)tr.GetObject(db.LayerTableId, OpenMode.ForRead); foreach (ObjectId layerId in lt) { LayerTableRecord ltr (LayerTableRecord)tr.GetObject(layerId, OpenMode.ForRead); Console.WriteLine($Layer: {ltr.Name}, Color{ltr.Color}, Off{ltr.IsOff}, Frozen{ltr.IsFrozen}); }逻辑说明LayerTable 是符号表之一遍历得到每个 LayerTableRecord。Color是 Teigha 的颜色对象转成 RGB 需要调ltr.Color.ColorValue或ltr.Color.Red/Green/Blue。IsOff和IsFrozen分别对应关闭和冻结做可见性判断时两个都要看。块表遍历类似但块表里包含模型空间和图纸空间两个特殊记录过滤时用ltr.Name判断模型空间的名字通常是*Model_Space。3.3 坐标转换与单位问题DWG 本身不强制单位图纸可能以毫米、米或英寸绘制。Teigha 读出来的坐标是图纸单位不做换算。如果下游系统需要米得自己判断。常见做法是读db.Insunits它对应 INSUNITS 系统变量值 4 是毫米6 是米。但很多图纸这个值是 0无单位这时候只能靠图框尺寸或人工约定。血泪经验是不要假设单位先输出几个已知尺寸的实体坐标和图纸标注对比一下再决定换算系数。4. 写回 DWG新增实体、修改属性与保存的完整流程4.1 写操作的事务与提交Teigha 的写操作必须在事务里并且对象要以OpenMode.ForWrite打开。新增实体时先创建对象再AppendEntity到块表记录最后AddNewlyCreatedDBObject通知事务。顺序错了对象不会出现在文件里。using (Transaction tr db.TransactionManager.StartTransaction()) { BlockTable bt (BlockTable)tr.GetObject(db.BlockTableId, OpenMode.ForRead); BlockTableRecord ms (BlockTableRecord)tr.GetObject(bt[BlockTableRecord.ModelSpace], OpenMode.ForWrite); Line newLine new Line(new Point3d(0, 0, 0), new Point3d(100, 100, 0)); newLine.Layer 0; // 指定图层图层必须已存在 ms.AppendEntity(newLine); tr.AddNewlyCreatedDBObject(newLine, true); tr.Commit(); } db.SaveAs(D:\samples\out.dwg, DwgVersion.Current);逻辑说明AppendEntity把实体加入块表记录AddNewlyCreatedDBObject让事务跟踪这个新对象缺一不可。Commit提交事务但此时还没写文件要调db.SaveAs才落盘。DwgVersion.Current表示用当前 SDK 支持的较新版本如果要兼容旧 CAD改成DwgVersion.AC10152000 格式之类。参数说明newLine.Layer赋值的图层名必须已经在 LayerTable 里存在否则保存时会报错或实体丢失。需要新建图层时先往 LayerTable 里加 LayerTableRecord。4.2 修改已有实体属性修改和新增类似区别是对象从ForRead改成ForWrite改完属性后事务提交即可。Entity ent tr.GetObject(id, OpenMode.ForWrite) as Entity; if (ent is Circle c) { c.Radius c.Radius * 2; // 半径翻倍 c.ColorIndex 1; // 红色 }逻辑说明ColorIndex是 AutoCAD 颜色索引1 是红2 是黄3 是绿256 是随层。用真彩色则设c.Color。修改后不需要额外调用事务提交时自动写回。4.3 保存格式与版本兼容SaveAs的第二个参数决定输出 DWG 版本。常见值AC1015对应 2000AC1018对应 2004AC1021对应 2007AC1024对应 2010AC1027对应 2013。选太低会丢新特性选太高旧 CAD 打不开。如果下游是 LibreCAD 或某些国产 CAD建议存 2004 或 2007 格式兼容性更稳。注意SaveAs会覆盖目标文件批量处理时先检查文件是否存在必要时加时间戳后缀。5. 避坑与排查Teigha4 读写 DWG 最常见的五类翻车5.1 现象程序启动即崩溃无托管异常信息原因Services 未初始化或非托管 DLL 位数与进程不匹配。Teigha 的异常有时不抛到托管层直接进程退出。解决确认new Services()在最先执行检查项目平台目标与 DLL 位数一致用 Dependency Walker 或dumpbin /headers看非托管 DLL 是 32 位还是 64 位。5.2 现象读取时抛 eInvalidInput 或 eNotOpenForWrite原因对象以只读打开却试图修改或者 ObjectId 已失效。常见于跨事务持有 ObjectId前一个事务提交后再用。解决所有对象访问都在同一个事务内完成需要跨事务时重新GetObject。写操作确认OpenMode.ForWrite。5.3 现象中文图层名或文字乱码原因DWG 内部字符串编码与 .NET 默认编码不一致早期版本尤其明显。解决Teigha 4.x 的 .NET 封装一般返回 Unicode但如果图纸是旧版本且用了非标准代码页可能需要在读取后做编码转换。更稳的做法是升级到较新的 Teigha_Net 版本旧版 DWGdirect_NET_3_02 对中文支持较弱。5.4 现象保存后的文件 AutoCAD 提示「图形文件无效」原因保存时事务未正确提交或实体引用了不存在的图层/线型。解决确保tr.Commit()在SaveAs之前执行新增实体引用的符号表记录必须已存在。可以用db.Audit做一次自检看有没有修复信息。5.5 现象批量处理时内存持续增长直至崩溃原因Database 和 Services 未释放或者事务未 Dispose。Teigha 的非托管内存不会自动被 GC 回收。解决每个文件处理完显式db.Dispose()Services 在整个批次结束后释放。用using包裹 Database 和 Transaction避免遗漏。6. 进阶技巧用 Teigha4 做 DWG 到结构化数据的批量管道如果只是单张读取上面的代码够了。但实际项目往往是几百上千张图纸需要抽图层、抽块、抽文字最后落到数据库或 CSV。我一般会搭一个三段式管道扫描目录、逐文件解析、结果落库。关键技巧是复用 Services不要每个文件都新建Services 创建一次即可Database 每个文件独立。using (new Services()) { foreach (string file in Directory.EnumerateFiles(D:\dwg, *.dwg)) { try { using (Database db new Database(false, true)) { db.ReadDwgFile(file, FileOpenMode.OpenForReadAndAllShare, true, null); using (Transaction tr db.TransactionManager.StartTransaction()) { // 抽取逻辑结果写入 ListRecord tr.Commit(); } } } catch (Exception ex) { // 记录失败文件继续下一张不要整体中断 Console.WriteLine(file 失败: ex.Message); } } }逻辑说明Services只创建一次避免反复加载非托管引擎的开销。每个文件用独立 Database异常捕获后继续保证批量任务不因单张图纸损坏而中断。抽取结果可以攒够一批再写数据库减少 IO。参数说明Directory.EnumerateFiles比GetFiles更省内存适合大目录。如果图纸有密码ReadDwgFile最后一个参数传密码字符串但批量场景建议先过滤加密文件。验证方法抽几张图纸用 AutoCAD 或 ODA Viewer 打开对比实体数量和关键坐标。我习惯先跑一个只统计实体类型和数量的版本确认解析没漏再开属性抽取。这样出问题时容易定位是解析层还是业务层。最后说个习惯Teigha 的版本和程序集命名比较乱DWGdirect_NET_3_02 和 Teigha_Net_40010 的 API 有差异网上搜到的代码不一定能直接跑。我一般会先写一个最小读取程序确认 Services、Database、Transaction 这条链路通了再往上堆业务逻辑。这个习惯帮我省了很多返工。希望帮到你。本文还有配套的精品资源点击获取