Technical Article

Fix Delphi E2010 tagLOGBRUSH and tagLOGBRUSH32 incompatible types

· Delphi Programming

Delphi XE2 and later moved a large part of the Windows API declarations into the scoped Winapi.Windows unit and tightened several record declarations. Code that compiled in older Delphi versions can therefore fail when an Enhanced Metafile record exposes a tagLOGBRUSH32 value but the GDI API expects a tagLOGBRUSH or TLogBrush.

The symptom usually appears while replaying EMF records or rebuilding GDI brush objects from PEMRCreateBrushIndirect. A direct call such as the following looks reasonable, but the two record types are not assignment-compatible in newer Delphi compilers.

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;

The compiler reports E2010 Incompatible types: 'tagLOGBRUSH' and 'tagLOGBRUSH32'. Avoid forcing the record through an unsafe cast. The safer fix is to create a local TLogBrush, copy the compatible fields explicitly, and pass that structure to 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;

Why the explicit copy is safer

tagLOGBRUSH32 is used in the EMF record format so the serialized metafile data has stable 32-bit field layout. CreateBrushIndirect, however, accepts the normal Windows brush structure used by the current API declaration. Copying lbStyle, lbColor, and lbHatch documents the conversion and avoids depending on compiler-specific record compatibility.

This pattern is also easier to review when porting legacy graphics code to Unicode Delphi or 64-bit targets. If a later Windows SDK declaration changes type aliases again, the field-by-field conversion makes the intended data boundary clear.

Porting checklist

  • Keep Winapi.Windows in the uses list for modern Delphi projects.
  • Copy EMF record fields into the API structure expected by the GDI call.
  • Do not replace this with a blind pointer cast unless you have verified the binary layout and lifetime.
  • After the brush is created, keep the existing object ownership rules for your GDIObjects table.

Why this appears during Delphi upgrades

Legacy EMF playback code often treats Windows records as if the API structures and the serialized metafile structures were interchangeable. Older units and looser aliases sometimes made that assumption compile. Modern Delphi declarations are stricter, so the compiler exposes the boundary that was already present in the Windows API.

The error is therefore not only a syntax problem. It is a useful warning that the code is crossing from a file-format structure into a live GDI call. Keeping the conversion explicit makes the code safer when it is later reviewed for 64-bit builds, Unicode migration, or updated Windows SDK declarations.

What not to do

A pointer cast may appear to remove the compiler error, but it hides the actual conversion and can make later maintenance harder. It also gives reviewers no clue which fields are intentionally used. The field copy is short, deterministic, and self-documenting, so it is the better default for application code and component samples.

If your EMF parser handles many similar records, wrap this conversion in a small helper function and test it with several brush styles. That keeps the replay loop readable while still keeping the Windows API boundary explicit.