技术文章

修复 Delphi E2010 tagLOGBRUSH 与 tagLOGBRUSH32 不兼容类型

· Delphi 编程

Delphi XE2 及更新版本将大量 Windows API 声明移入带作用域的 Winapi.Windows 单元,并收紧了一些记录类型声明。因此,旧版 Delphi 可以编译的代码,在新版编译器中可能会失败:EMF 记录中提供的是 tagLOGBRUSH32,而 GDI API 需要的是 tagLOGBRUSHTLogBrush

这个问题通常出现在回放增强型图元文件或从 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 声明使用的普通画刷结构。逐字段复制 lbStylelbColorlbHatch 能清楚表达转换边界,也避免依赖编译器特定的记录兼容性。

把旧图形代码迁移到 Unicode Delphi 或 64 位目标时,这种写法也更容易审查。如果后续 Windows SDK 声明再次调整类型别名,逐字段转换仍能说明代码的真实意图。

升级 Delphi 时为什么会暴露

旧的 EMF 回放代码经常把 Windows 记录当作 API 结构直接使用。旧单元和宽松别名有时会让这种假设通过编译。现代 Delphi 声明更严格,所以编译器把原本就存在的文件格式结构与 GDI 调用边界暴露出来。

因此,这不仅是语法问题。它提醒你代码正在从文件格式结构跨入实时 GDI API。保持转换显式,有助于后续做 64 位迁移、Unicode 迁移或更新 Windows SDK。

不要这样修复

指针强制转换也许能消除编译错误,但它隐藏了真正的转换,并增加后续维护难度。字段复制短小、确定、可读,通常是应用代码和组件示例中的更好默认选择。

  • 现代 Delphi 项目中保持使用 Winapi.Windows
  • 把 EMF 记录字段复制到 GDI 调用需要的 API 结构中。
  • 除非验证过二进制布局和生命周期,否则不要使用盲目的指针转换。
  • 画刷创建后,继续遵守 GDIObjects 表中原有的对象所有权规则。