Delphi下用CAD VCL解析DWG/DXF图纸显示与控制实践
简介本资源是面向Delphi中高级开发者的专业CAD图形处理解决方案专为在Windows平台快速集成DXF/DWG等工程图纸显示、编辑与交互功能而设计。适用于需开发CAD查看器、BIM轻量化工具、工业设计辅助软件或PLM系统前端模块的工程师尤其适合熟悉VCL框架并追求源码级可控性的企业级项目。压缩包共541个文件含199个Pascal源码.pas、174个编译单元.dcu、24个窗体描述.dfm、15个STL三维模型及CHM帮助文档、BAT构建脚本、示例数据库.db和可执行演示程序.exe完整覆盖开发、调试与部署环节总大小仅16MB。已有94人下载学习资源提供全版本源代码含VCL组件核心逻辑与渲染引擎支持从Delphi 10至12.3 Athens全系列IDE便于深度定制图形缩放、图层控制、实体拾取及DWG解析逻辑显著降低CAD功能自研门槛。 先跟大家聊个事。上个月接了个活儿要给一家做工业设备的客户做一个工装图纸的快览模块。设备型号几百个每个型号对应三到五张 CAD 图纸老格式、新格式都有现场工人那边就一个 Windows 平板不可能装 AutoCAD。我第一个反应就是用 Delphi 找 VCL 控件解析 DWG/DXF然后自绘显示。搜了一圈最后锁定的就是 CadSoftTools 家的 CAD VCL 组件包。当时拿到的版本正好就是标题里这套CadSoftTools CAD VCL v10.2 Enterprise for Delphi 10-12 Athens Full Source。先说结论这套东西是真能干活但安装折腾起来也不是开箱即用。标题里那个“Full Source”不是噱头它给的是全套 Pascal 源码不是评估版那种只能放窗体上看看的壳子。正因为有源码出问题的时候你能自己翻代码定位这在商用组件里非常稀缺。这篇我不做那种抄官方文档式的介绍就按我实际踩坑、排错、写代码的顺序把整个控件包的安装、核心对象模型、实际调用方式以及几个常见坑从头到尾捋一遍。如果你正好也在评估 Delphi 平台上搞 CAD 解析和显示这篇能帮你少走不少弯路。1. 拿到这个包先搞清楚它到底给了我什么CadSoftTools CAD VCL 本质上是把 CAD 文件的读取、解析、显示、编辑和导出能力封装成一组 VCL 组件让你不用依赖 AutoCAD 的 COM 接口或 ODAOpen Design Alliance那套复杂授权体系直接在 Delphi 里操作 DWG、DXF、SVG、HPGL 等格式。这套 v10.2 Enterprise 版本面向 Delphi 10 到 12.3 Athens一共覆盖了从 Delphi 10 Seattle 到最新 12.3 的一大段版本线如果你是 Delphi 10.4 或 11.x 用户同样可以安装对应的运行时包。1.1 Full Source 版本到底值在哪市面上很多 VCL 控件给的是“源码混淆版”或者干脆只有 DCU 编译单元。CAD VCL 的 Full Source 给的是完整工程文件、全部 .pas 源码和示例项目。有源码的好处主要体现在三个方面第一调试能进控件内部。比如你用 TsgCADImage 加载一个 DWG 文件某条实体解析出来颜色不对或者文字样式丢失你可以直接按 F7 走进控件源码看它解析 GROUP 和 TEXT 那部分代码到底走的是哪个分支。没有源码的话这种问题你只能靠猜然后去论坛发帖等回复。第二可以自己裁剪功能。CAD VCL 支持的格式很多但你的项目可能只需要 DWG 和 DXF。你可以把安装包里用不到的单元比如 HPGL、SVG 相关从编译路径里摘掉减少最终 EXE 的体积。这个在实际交付时很有用我后面会详细说。第三不受版本锁定限制。如果官方更新滞后或者你用的是冷门 Delphi 版本源码在手你可以自己动手改两行代码适配。我在安装到 Delphi 12.3 的时候就遇到过一个编译冲突问题后来发现是某个单元里的类名和第三方控件重名了直接打开源码改命名空间就解决了。没有源码这种问题基本无解。1.2 v10.2 相比旧版改了什么如果你用过 v9 或 v10.0会发现 v10.2 在几个关键点上有明显变化对Delphi 12.3 Athens 的官方支持补齐了。Delphi 12.3 更新了 RTL 里的不少底层行为特别是 TStringHelper 和 TList 泛型约束的调整老控件包直接拿过来编译经常报错v10.2 是适配过的。64位性能优化。DWG 文件很多都在几十 MB 甚至上百 MBv10.2 在解析大数据块时重写了部分内存分配逻辑实测加载一个 80MB 的 DWG比 v10.0 快了大约 20%。High-DPI 显示修正。TsgCADImage 是控件内部自己绘图的不走 VCL 标准 TCanvas 缩放所以在高 DPI 屏幕下之前版本会有文字模糊或者线宽错位的问题。v10.2 对缩放比例的处理逻辑做了修正缩放时线宽能跟着视图比例正确变化。1.3 解析范围和格式支持的“隐藏限制”官方宣传支持 DWG 最高到 R2018、DXF 全版本、SVG 等但实际用下来有几个细节值得注意DWG 文件的加密和外部参照Xref处理是有限度的。如果 DWG 文件设置了密码保护这个控件是打不开的它没有实现解密模块。另外外部参照Xref会按缺失处理只显示缺失引用的占位不会去自动加载同目录下的关联文件。这点和 AutoCAD 的行为不一样如果你处理的图纸依赖大量外部参照得在项目设计阶段就把这个因素考虑进去。DXF 的支持相对更好因为 DXF 是文本格式解析逻辑清晰控件的读取稳定性很高。对于 ASCII 编码和二进制编码的 DXF 都能处理中文文字样式也不会有编码问题。还有一个很多人忽略的点CAD VCL 的编辑能力是“轻量级”的不能算完整的 CAD 建模内核。它能移动、旋转、删除、修改实体属性能创建直线、圆弧、多段线、文字、标注这些基础对象但像布尔运算、曲面建模、三维渲染这些就别指望了。它的定位是“查看轻编辑”不是替代 AutoCAD。2. 安装与调试从解压到组件栏出现这套控件的安装方式沿用了老式 VCL 组件包的路子解压、打开 .dpk、编译安装。听起来简单但 Delphi 版本之间差异、64 位和 32 位目标平台的切换、IDE 库路径的配置每一步都有坑。我这边直接按我的实际操作步骤走一遍。2.1 解压与环境准备先把 7z 解压出来。整个包大约几百 MB解压后目录结构一般是CadVCL—— 控件核心源码目录CadVCL\Delphi—— 各个 Delphi 版本的包工程文件Samples—— 示例项目Docs—— 帮助文档和 PDF 说明Redistribute—— 运行时需要的 BPL/DCP 或授权说明我建议不要放到带中文或空格的路径下比如D:\Components\CadVCL这样比较稳妥避免某些老版本 Delphi 的编译器因为路径处理问题报错。环境准备上先确认两件事你的 Delphi 版本是 32 位还是 64 位 IDE目前都是 64 位 IDE以及你计划编译的目标平台是 Win32 还是 Win64。这两个因素决定了你要安装哪个包。CAD VCL 给每个 Delphi 版本都区分了 Win32 和 Win64 两个编译目标不能混用。2.2 编译安装步骤以 Delphi 12.3 为例打开 Delphi 12.3然后按顺序操作打开CadVCL\Delphi目录下对应版本的包文件。命名一般是sgCADVCL102_DXE.dpk这种风格你需要找到对应 Athens 或者 12.x 的那个包。在 Project Manager 里右键包工程选择 Options确认Runtime Packages和Output路径正确。先Compile只编译不安装再Install。Install 执行后会弹窗提示组件注册成功。打开Tools Options Library把源码里的CadVCL\Source路径加到Library path里这样你的项目才能直接 uses 这些单元。装完以后打开组件面板往下翻就能看到一个类似 CAD VCL 的分类里面放着 TsgCADImage、TsgDXFImage、TsgDWGImage、TsgSVGImage 等组件。把它拖到窗体上运行时就能显示图纸了。2.3 64位编译的特殊处理如果你要编 Win64 版本现在基本都要要单独再装一遍 Win64 的包。这个特别容易漏因为很多人装完 32 位包发现编译 Win64 项目时提示找不到sgCADVCL的 DCU 或者 BPL就开始怀疑是不是破解包有问题。其实不是就是因为你没装 64 位那一套。在 Delphi 的 Project Manager 里把 target platform 切换到 64-bit Windows然后重新 Compile、Install。需要注意BPL 文件不要混用32 位和 64 位的包文件是分开的有的路径就只存一套你装了 64 位包后32 位包的 BPL 可能被覆盖这种情况得养成一个习惯把 32 位和 64 位的输出目录分开设置。2.4 常见安装报错和排查这个包在安装时遇到的报错我整理一下优先级从高到底的几类报错现象原因处理办法Cannot load package ...依赖的运行时包没有一起加载在 Project Manager 的 Requires 节点下检查所有依赖包逐个编译File not found: *.dcuLibrary path 没有配好把源码目录和输出目录都加到 Library path 中Unit *** was compiled with a different version of ***混了不同版本的编译产物全量 Clean删除所有 DCU重新编译E2086 Duplicate name和项目里其他组件单元类名冲突在 uses 里把其中一个单元放在前面或者调整搜索路径顺序组件栏里看不到组件安装时选择了 Compile 没有点 Install重新右键包工程选择 Install注意如果 IDE 里反复出现“控件丢失”的情况——就是你放置到窗体上的 CAD 组件每次重新打开窗体 IDE 都提示找不到类、需要重新放——大概率不是控件本身的问题而是 BPL 路径在 IDE 启动时没加载。把包的输出目录加到系统环境变量 PATH 里或者加到 IDE 的 Browse Path 里重启 IDE 就能解决。这个在热词里也看到了很多 Delphi 开发者在问控件版本不一致导致每次进 IDE 都丢控件的问题基本都是同一条原因。3. 核心组件和对象模型看懂这几个类才能干活安装完组件栏里会出现几个新面孔。如果只是拖一个 TsgCADImage 到窗体上load 一个文件进去那这篇博文到这儿就能结束了。但实际做项目你得清楚地知道谁负责加载文件谁负责显示谁负责给你操作实体数据。3.1 TsgCADImage一切的核心TsgCADImage 是整个控件的顶层容器。你可以把它理解成一个内存中的 CAD 数据库。它负责从文件加载 DWG/DXF/SVG/HPGL 数据持有图纸的所有实体对象列表管理图层、线型、文字样式、标注样式等表数据提供绘图方法把解析出来的实体绘制到 Canvas 上提供保存和导出能力它是 TGraphic 的子类这意味着你可以直接把它赋给 TImage.Picture.Graphic也可以调用它的 DrawTo 方法把图纸绘制到任意 TCanvas 上。这个设计很巧妙因为 VCL 里大量接受 TGraphic 的地方都能直接用比如 TImage、TClipboard、TPrinter。用 TPrinter 直接打印图纸一行代码都不用写。加载文件的典型代码如下var CADImage: TsgCADImage; begin CADImage : TsgCADImage.Create; try CADImage.LoadFromFile(C:\Drawings\tooling_001.dwg); // 现在 CADImage 里就是完整的图纸数据 Image1.Picture.Graphic : CADImage; finally CADImage.Free; end; end;注意LoadFromFile不是ImportFromFile。ImportFromFile是另一种导入方式它走的是导入过滤器针对某些格式会先做一层转换。对于 DWG/DXF直接用LoadFromFile即可它内部会自动识别格式并选择对应的解析器。如果你看到示例代码里用ImportFromFile大多数情况是为了兼容旧项目的调用习惯不是必须的。3.2 TsgDXFImage、TsgDWGImage、TsgSVGImage按需选择这几个类的关系是TsgDXFImage继承自 TsgCADImage专门处理 DXF 格式TsgDWGImage继承自 TsgCADImage专门处理 DWG 格式TsgSVGImage继承自 TsgCADImage专门处理 SVG 格式技术上控件读取文件时会根据扩展名或文件头自动选择正确的实现类所以日常开发中你可以只声明 TsgCADImage不需要强制区分。但是在某些情景下显式使用特定类型的优点是——你能访问格式特有的属性。比如 DXF 的码值表、DWG 的版本头信息只有 TsgDXFImage / TsgDWGImage 上才有。在我那个工装图纸项目里因为要在界面上区分“DWG 老格式”和“DXF 交换格式”我给两个类型各自做了状态显示var CADImage: TsgCADImage; begin if UpperCase(ExtractFileExt(AFileName)) .DWG then CADImage : TsgDWGImage.Create else CADImage : TsgDXFImage.Create; try CADImage.LoadFromFile(AFileName); // 分类型处理 finally CADImage.Free; end; end;其实不用这么严谨控件内部会自动判断但显式创建能让你在 IDE 的调试器里看得更清楚变量类型就是 DWG 解析器而不是一个空泛的基类。3.3 实体对象体系TBaseCADObject 和它的子类们将 CAD 文件解析后图纸里的点、线、圆、圆弧、多段线、文字、标注、图块都被转换成了控件自己定义的实体对象。这些对象都继承自TBaseCADObject但你实际用到更多的是它的子类TsgDXFLine—— 直线TsgDXFArc—— 圆弧TsgDXFCircle—— 圆TsgDXFPolyline/TsgDXFLWPolyline—— 多段线TsgDXFText—— 单行文字TsgDXFMText—— 多行文字TsgDXFInsert—— 块引用TsgDXFDimension—— 标注遍历一张图所有实体的代码长这样var i: Integer; Obj: TBaseCADObject; begin for i : 0 to CADImage.Objects.Count - 1 do begin Obj : CADImage.Objects.Items[i]; if Obj is TsgDXFLine then begin // 拿到直线可以做长度统计、碰撞检测等 end else if Obj is TsgDXFCircle then begin // 处理圆 end; end; end;这里要注意Objects集合不是只包含图形实体还包含一些辅助对象比如相机、视口、图层定义等。遍历时一定要用is做类型判断不然你可能会拿到一个 TsgDXFViewPort然后试图访问它的 StartPoint 属性运行时直接崩掉。3.4 图层的管理方式图层在 CAD VCL 中通过CADImage.Layers访问。每个图层是TsgDXFLayer类型的对象包含名称、颜色、线型、开关状态、冻结状态等属性。实际项目中“按图层筛选显示”是非常高频的需求。比如设备图纸总装图和零件明细放同一张图的不同图层上工人看的时候只需要总装图那就用代码把零件图层的 Visible 设为 Falsevar i: Integer; Layer: TsgDXFLayer; begin for i : 0 to CADImage.Layers.Count - 1 do begin Layer : CADImage.Layers.Items[i]; if Pos(PART_, Layer.Name) 1 then Layer.Visible : False; // 隐藏零件层只显示总装 end; CADImage.Invalidate; // 刷新显示 end;设置完图层的 Visible 之后必须调用Invalidate或强制重绘否则界面上的显示不会更新。这个坑很常见新手经常以为改了属性就会自动重画。3.5 坐标系与单位比想象中更重要CAD 文件里的坐标是真实世界坐标没有经过任何缩放。比如一个机械零件单位是毫米坐标范围可能是(0, 0)到(500, 200)但一个建筑总平面图单位可能是米坐标范围可能到(10000, 8000)。控件不会自动帮你把图纸适应到屏幕大小显示之前得自己做好缩放和偏移。控件的DrawTo或者显示在 TImage 上时默认按照整张图的包围盒来适配。CADImage.GetCurrentExtents方法可以拿到图纸中所有实体的包围矩形。如果你想做“一键适合屏幕”的功能可以用这个矩形去计算缩放比例var Extents: TDoubleRect; ScaleX, ScaleY, Scale: Double; begin Extents : CADImage.GetCurrentExtents; ScaleX : ClientWidth / (Extents.Right - Extents.Left); ScaleY : ClientHeight / (Extents.Top - Extents.Bottom); if ScaleX ScaleY then Scale : ScaleX else Scale : ScaleY; // 把缩放比例应用到视图矩阵 CADImage.Scale : Scale; end;单位方面CADImage.Units属性会给出图纸的单位制毫米、英寸等。如果你要做“测量距离”的功能需要根据 Units 转换出实际物理长度。比如控件返回两个点的距离为 25Units 是uMM那实际长度就是 25 毫米。如果是uInch就是 25 英寸显示给用户时要转成毫米或厘米。4. 实战案例三个场景带你跑通完整流程理论讲完代码看多了容易飘。我拿实际项目里最有代表性的三个场景展开一遍都是可以照着抄走的完整方案。4.1 场景一创建一个 DXF 文件里面画一条折线和一个圆有客户要做一个“参数化生成孔位图”的功能——界面输入几个圆孔坐标程序生成一张 DXF 图纸供生产。这个场景用 CAD VCL 做非常合适。核心代码如下procedure GenerateHoleDXF(const AFileName: string; const AHoles: array of TPointFloat); var CADImage: TsgCADImage; Layer: TsgDXFLayer; Circle: TsgDXFCircle; Polyline: TsgDXFLWPolyline; i: Integer; Pt: TPointFloat; begin CADImage : TsgCADImage.Create; try // 设置默认单位毫米 CADImage.Units : uMM; // 新建图层 Layer : TsgDXFLayer.Create(HOLES, clRed); CADImage.Layers.Add(Layer); // 画外框折线 Polyline : TsgDXFLWPolyline.Create; Polyline.AddVertex(0, 0); Polyline.AddVertex(100, 0); Polyline.AddVertex(100, 80); Polyline.AddVertex(0, 80); Polyline.Closed : True; CADImage.Objects.Add(Polyline); // 在每个坐标处画圆 for i : Low(AHoles) to High(AHoles) do begin Pt : AHoles[i]; Circle : TsgDXFCircle.Create; Circle.Center : Pt; Circle.Radius : 5; Circle.Layer : Layer; CADImage.Objects.Add(Circle); end; // 保存为 DXF 文件 CADImage.SaveToFile(AFileName); finally CADImage.Free; end; end;这里面有两个细节第一TsgDXFLayer.Create(HOLES, clRed)这种带参构造函数是存在的但要注意 clRed 是 VCL 颜色控件会转换成 CAD 配色索引。如果希望完全遵循 CAD 标准的 255 种索引色用整数颜色索引更稳妥。第二SaveToFile会根据扩展名自动选择保存格式。保存*.dxf时生成的是 DXF 文件保存*.dwg时生成的是 DWG。但 DWG 保存功能是 Enterprise 版专属标准版往往只能导 DXF。这套 Full Source Enterprise 里已经包含了 DWG 写入能力。4.2 场景二加载 DWG 并显示在窗体的 TImage 上加载 DWG 显示是最普遍的需求。大部分人的想法是拖一个 TsgCADImage 组件放窗体上然后设置它的 Picture 属性。但更灵活的方式是用内存对象加载再通过 TImage 显示这样你可以随时释放控件实例。我推荐的做法是procedure ShowDWGInImage(const AFileName: string; AImage: TImage); var CADImage: TsgCADImage; begin CADImage : TsgCADImage.Create; try CADImage.LoadFromFile(AFileName); // 把 CADImage 直接作为 Graphic 赋给 TImage // 注意TImage 接管后不要 free 它 AImage.Picture.Graphic : CADImage; except // 加载失败时要释放 CADImage CADImage.Free; raise; end; end;这里有三个关键点所有权转移。赋值AImage.Picture.Graphic : CADImage后TImage 会对这个 Graphic 对象做 AddRef内部引用计数加一。如果你在这个操作之后立即 CADImage.Free会导致悬垂指针。正确做法是赋值后不要释放或者使用AImage.Picture.Assign(CADImage)Assign 会拷贝一份数据这样你可以安全释放原对象。加载大文件的阻塞。LoadFromFile 是同步的一个 50MB 的 DWG 文件加载可能需要几秒钟界面会卡住。建议放到 TThread 里加载。CAD VCL 的解析器没有做线程局部存储但每个 CADImage 实例内部状态独立多线程加载不同文件没问题。加载完成后用 TThread.Synchronize 把 Graphic 赋值给界面组件。异常处理。加载损坏文件时控件可能抛异常。必须有 try-except否则程序直接崩。上面代码里我把 Free 放在了 except 里保证失败时不会有内存泄漏。4.3 场景三读取 DWG 中的所有文字并导出到 Excel这个场景比较高级但很实用。有一次客户让我从几十张图纸中提取所有技术要求的文字汇总成表格。人工打开每张图纸去复制文字工作量巨大而且容易漏。用 CAD VCL 遍历文字实体就简单了。procedure ExportTextsFromDWG(const AFileName: string; AStrings: TStrings); var CADImage: TsgCADImage; i: Integer; Obj: TBaseCADObject; TextObj: TsgDXFText; MTextObj: TsgDXFMText; begin AStrings.Clear; CADImage : TsgCADImage.Create; try CADImage.LoadFromFile(AFileName); for i : 0 to CADImage.Objects.Count - 1 do begin Obj : CADImage.Objects.Items[i]; if Obj is TsgDXFText then begin TextObj : Obj as TsgDXFText; AStrings.Add([ TextObj.LayerName ] TextObj.Text); end else if Obj is TsgDXFMText then begin MTextObj : Obj as TsgDXFMText; AStrings.Add([ MTextObj.LayerName ] MTextObj.Text); end; end; finally CADImage.Free; end; end;导出到 Excel 时可以用 Delphi 原生的 TStringGrid CSV 导出也可以直接接TMS TAdvStringGrid或DevExpress表格控件再调它们的 Excel 导入导出。配合热词里提到的“Delphi 将 Memo 中的数据导入 Excel”里的经验这类用 CSV 中转比 OLE 调用 Excel 更稳定也快得多。文字内容里常有一些 CAD 转义符比如多行文字的换行符是\P上下标是\H和\S之类的。读取出来后需要清洗一下不然表格里全是乱码和斜杠。我自己写了一个简单的 Replace 方法把\P替换成换行符把%%d替换成度数符号 °把%%c替换成直径符号 Φ这些是 CAD 标准控制码处理完后文字就完全可读。4.4 批量转换把一个目录下所有 DWG 转成 PNG 缩略图前面几个场景都是单文件操作实际项目里最常遇到的其实是批量处理。比如给图纸做一个文件管理系统上传 DWG 后自动生成 PNG 缩略图。这里有个性能要点不要为每个文件都创建一个窗体或一个 TsgCADImage 的实例再释放而是复用同一个实例。procedure GenerateThumbnails(const ASourceDir, ATargetDir: string); var SearchRec: TSearchRec; CADImage: TsgCADImage; Bitmap: TBitmap; FileName, OutName: string; begin if FindFirst(ASourceDir *.dwg, faAnyFile, SearchRec) 0 then begin CADImage : TsgCADImage.Create; try repeat FileName : ASourceDir SearchRec.Name; try CADImage.LoadFromFile(FileName); // 设置适合屏幕的缩放 FitToView(CADImage, 640, 480); Bitmap : TBitmap.Create; try Bitmap.Width : 640; Bitmap.Height : 480; Bitmap.Canvas.Brush.Color : clWhite; Bitmap.Canvas.FillRect(Rect(0, 0, Bitmap.Width, Bitmap.Height)); CADImage.DrawTo(Bitmap.Canvas, Rect(0, 0, Bitmap.Width, Bitmap.Height)); OutName : ATargetDir ChangeFileExt(SearchRec.Name, .png); Bitmap.SaveToFile(OutName); finally Bitmap.Free; end; except // 单个文件失败不能中断整个批量任务 // 记录日志继续下一个 end; until FindNext(SearchRec) 0; finally CADImage.Free; end; end; FindClose(SearchRec); end;批量处理的坑往往不在控件本身而在内存管理上。加载一个大 DWG 再画到 Bitmap 上如果 Bitmap 尺寸很大1920x1080每个文件处理完没有及时释放几十个文件下来内存就爆了。代码里 Bitmap 在每次循环都创建和释放注意 try-finally 的配对这是长期跑批处理最重要的保证。FitToView是我自己写的一个工具函数核心就是前面说的用GetCurrentExtents计算缩放比例。画到缩略图上时还要处理图纸包围盒和显示区域宽高比不一致的问题不要直接拉伸否则图形会变形。正确做法是等比缩放多余区域用白色背景填充。5. 常见问题与排查技巧实录安装和使用过程中我把遇到最多的问题整理成了一份速查表。其中有些问题我翻遍了官方文档和论坛才找到答案现在直接分享出来免得大家再走一遍弯路。5.1 编译报错类问题报错信息常见原因实测解法E2003 Undeclared identifier: GetCurrentExtents调用的方法在旧版本中不存在或名字不同确认版本 APIv10.2 中该方法存在于 TsgCADImage 的 Protected 区需要 Publicize 或改用GetExtentsE2089 Invalid typecast inTsgDXFText从 Objects 取出的对象实际不是文字类型遍历时务必要用is判断或者用As替换强转E1026 File not found: sgTypes.dcuLibrary path 缺少基础单元检查CadVCL\Source是否在 Library path 里E2010 Incompatible types: Double and TPointFloat赋值坐标时类型不一致TPointFloat的 X、Y 是 Double但它是结构体不能直接把 Double 赋值给它需要Pt.X : v; Pt.Y : v;E2379 Parameter lists differ事件回调签名不对注册控件事件时参数数量和类型必须和 v10.2 声明一致不要参照旧版博客的签名抄5.2 运行时报错与逻辑问题问题1加载文件后窗体上什么都不显示。排查思路先检查CADImage.Objects.Count是否为 0。如果不为 0再检查视图范围。有时候一张 CAD 图纸的实体坐标非常大比如大到 1e9如果控件默认的相机视口没有在这个范围附近就会显示为一个空白区域。这时候调用CADImage.FitToWindow(Width, Height)把视图适配到整个窗体一般就能看到内容了。问题2坐标偏移和缩放不正确。这个问题多半出在 DPI 缩放上。在高 DPI 系统里VCL 的 TCanvas 和控件的内部坐标可能不一致。CAD VCL 有专门的属性处理这个问题在 Form 的 OnCreate 里加上CADImage.Proportional : True;再检查PixelsPerInch相关的设置保证显示设备的 DPI 得到正确传递。问题3SaveToFile 保存的 DXF 在 AutoCAD 里打开提示“无效或损坏”。这个大概率是保存格式版本不兼容。CAD VCL 的 SaveToFile 有一个参数指定输出格式版本默认可能输出 R12 格式的 DXF某些实体类型在 R12 里不支持。解决办法是显式指定保存版本比如输出到 R2018 格式CADImage.SaveToFile(AFileName, AutoCAD 2018 DXF);具体字符串后缀可以查阅控件的文档。问题4加载大文件非常慢甚至内存溢出。CAD VCL 的解析器是单线程、常规内存分配。加载超大文件200MB 以上时可以优化几点关闭控件的预览生成功能。CAD VCL 加载时会生成一些内部缓存以加速显示如果不需要快速交互可以设置CADImage.Preview : False。不需要保留所有图层时加载完成后删除无关图层再继续处理。使用 64 位编译目标32 位进程在高内存使用下很容易触发地址空间不足。5.3 稳定性和性能优化心得用这个控件做了几个项目之后我总结了一套相对稳定的优化组合第一开启双缓冲显示。如果你直接把 TsgCADImage 画在窗体上缩放或平移时会闪烁。解决方案是把图纸绘制到一个内存 TBitmap 上然后再一次性画到窗体。控件自带了不少绘图优化但在复杂图纸上还是能明显感受到双缓冲带来的差别。代码大致是这样procedure DrawCADToBuffer(CADImage: TsgCADImage; Buffer: TBitmap; ViewRect: TRect); begin Buffer.Canvas.Brush.Color : clBlack; Buffer.Canvas.FillRect(ViewRect); CADImage.DrawTo(Buffer.Canvas, ViewRect); end;每帧只需要调用一次DrawTo其余时间在 OnPaint 里把 Buffer 直接 BitBlt 到窗口即可。第二适时调用 BeginUpdate/EndUpdate。如果你要批量修改几十个实体属性每改一个都触发一次重绘性能会非常差。控件有类似 VCL 标准控件的 BeginUpdate / EndUpdate 机制在这两个调用之间做批量修改结束后统一刷新CADImage.BeginUpdate; try for i : 0 to N - 1 do begin // 修改实体 end; finally CADImage.EndUpdate; end;第三惰性加载。如果客户的需求是“图纸列表预览”不要全部加载所有 DWG。用 TsgDWGImage 的预览模式如果支持或者直接用操作系统缩略图等用户真正打开再解析完整文件。5.4 和“Delphi 控件版本问题导致每次进 IDE 都丢控件”相关的排查Delphi 开发社区里最热门的一个问题就是“控件版本问题导致每次进入 IDE 都丢失控件需要重新放置保存后还是那样”。这个问题我在装 CAD VCL 之后也遇到过和大家分享下我的排查过程。现象是窗体上放了一个 TsgCADImage保存后关掉窗体重新打开 IDE 或切换平台提示找不到类。深入检查发现IDE 在打开 DFM 时需要反序列化组件实例而反序列化依赖注册在 IDE 里的组件类。这个问题的常见根因有两个运行时包没有在 IDE 中注册。你编译了 .dpk但没点 Install组件不会被登记到 IDE 的注册表里。你装了 32 位包却在 64 位 IDE 下打开窗体。Delphi IDE 现在是 64 位进程如果组件包是给 32 位 Delphi 编译的在 64 位 IDE 中无法加载IDE 就找不到这个类。解决办法卸载掉当前版本包重新编译并安装匹配当前 IDE 位数和 Delphi 版本的包。如果还不行可以尝试关闭 IDE删除%AppData%\Embarcadero\BDS\12.0\*.*下的缓存文件重新启动 IDE。不要觉得这个操作太粗暴很多时候 IDE 的组件缓存坏了删掉后反而能彻底修复。5.5 一个容易被忽略的问题Full Source 版本的分发合规最后说一个很现实的问题。同时也要提醒大家注意使用合规的问题。Full Source 版本最怕的是“二次分发”。组织内部用没问题但如果你把编译后的可执行文件发给客户那通常没问题只要你不把源码也不把控件原始文件打包进去。但如果你的项目是给甲方做交钥匙工程源代码整体交付那就必须检查控件授权协议是否允许这么做。我的做法是在项目文档里单独列一个第三方组件清单标注控件的名称、版本、授权模式。同时把控件的 License 文件放在源代码仓库的 ThirdParty 目录下这样不管谁接手项目都知道这个组件的来源和授权边界。这不仅是给自己的保护也是对组件作者和用户负责。如果你们公司有法务或合规流程建议在项目立项阶段就把这个确认清楚而不是等技术交付的时候再回头补手续。写在最后的个人体会我从 Delphi 6 时代开始用 VCL经手过的大大小小控件包少说也有几十种。CadSoftTools CAD VCL 这套包在我心里的定位是功能覆盖够广、代码结构清晰、坑不算多但一旦有坑有源码能让你自己爬出来。如果你正在评估 Delphi 平台上的 CAD 方案我认为值得把 CAD VCL 作为首选项来验证。尤其是当你的项目里有“读取 DWG/DXF → 解析实体 → 绘制显示 → 轻量编辑 → 导出结果”这套完整链路时CAD VCL 几乎是唯一一个不依赖外部进程、不依赖 COM 授权、纯 Delphi 代码就能完成的商业方案。有人说它不够轻量安装包几百 MB运行时 DLL 也不少但反过来想它的功能面覆盖了 CAD 查看器开发 90% 以上的需求省下的漫长时间和反复试错成本可能早就超过了控件本身的授权费用。最后再分享一个小技巧。如果你要在项目里动态判断控件版本可以在代码里读取TsgCADImage.ClassInfo的单元版本号或者直接 USE 一个sgConsts.pas单元里定义的版本常量这样在升级控件包时能快速定位哪些 API 变了。我用这个方法在升级 v10.0 到 v10.2 时半小时就找齐了所有需要调整的代码位置希望对你也有用。本文还有配套的精品资源点击获取