Delphi XE2 及更新版本将大量 Windows API 声明移入带作用域的 Winapi.Windows 单元,并收紧了一些记录类型声明。因此,旧版 Delphi 可以编译的代码,在新版编译器中可能会失败:EMF 记录中提供的是 tagLOGBRUSH32,而 GDI API 需要的是 tagLOGBRUSH 或 TLogBrush。
这个问题通常出现在回放增强型图元文件或从 PEMRCreateBrushIndirect 重建 GDI 画刷对象时。下面这种直接调用看起来合理,但在新编译器中两个记录类型并不赋值兼容。
|
1 2 3 4 5 |
procedure VEMRCREATEBRUSHINDIRECT(Data: PEMRCreateBrushIndirect); begin GDIObjects[Data^.ihBrush] := CreateBrushIndirect(Data^.lb); //GDIObjects and CreateBrushIndirect are defined in the winapi.windows unit. end; |
编译器会报告 E2010 Incompatible types: 'tagLOGBRUSH' and 'tagLOGBRUSH32'。不要用不安全的强制类型转换掩盖问题。更稳妥的做法是创建一个局部 TLogBrush,显式复制兼容字段,再传给 CreateBrushIndirect。
|
1 2 3 4 5 6 7 8 9 |
procedure VEMRCREATEBRUSHINDIRECT(Data: PEMRCreateBrushIndirect); var LogBrush: TLogBrush; begin LogBrush.lbStyle := Data^.lb.lbStyle; LogBrush.lbColor := Data^.lb.lbColor; LogBrush.lbHatch := Data^.lb.lbHatch; GDIObjects[Data^.ihBrush] := CreateBrushIndirect(LogBrush); end; |
为什么显式复制更安全
tagLOGBRUSH32 用在 EMF 记录格式中,确保序列化的图元文件数据保持稳定的 32 位字段布局。CreateBrushIndirect 接受的则是当前 Windows API 声明使用的普通画刷结构。逐字段复制 lbStyle、lbColor 和 lbHatch 能清楚表达转换边界,也避免依赖编译器特定的记录兼容性。
把旧图形代码迁移到 Unicode Delphi 或 64 位目标时,这种写法也更容易审查。如果后续 Windows SDK 声明再次调整类型别名,逐字段转换仍能说明代码的真实意图。
升级 Delphi 时为什么会暴露
旧的 EMF 回放代码经常把 Windows 记录当作 API 结构直接使用。旧单元和宽松别名有时会让这种假设通过编译。现代 Delphi 声明更严格,所以编译器把原本就存在的文件格式结构与 GDI 调用边界暴露出来。
因此,这不仅是语法问题。它提醒你代码正在从文件格式结构跨入实时 GDI API。保持转换显式,有助于后续做 64 位迁移、Unicode 迁移或更新 Windows SDK。
不要这样修复
指针强制转换也许能消除编译错误,但它隐藏了真正的转换,并增加后续维护难度。字段复制短小、确定、可读,通常是应用代码和组件示例中的更好默认选择。
- 现代 Delphi 项目中保持使用
Winapi.Windows。 - 把 EMF 记录字段复制到 GDI 调用需要的 API 结构中。
- 除非验证过二进制布局和生命周期,否则不要使用盲目的指针转换。
- 画刷创建后,继续遵守
GDIObjects表中原有的对象所有权规则。