Technisch artikel

Delphi E2010 tagLOGBRUSH en tagLOGBRUSH32 incompatibele typen oplossen

Delphi XE2 en later hebben een groot deel van de Windows API-declaraties verplaatst naar de scoped Winapi.Windows-unit en verschillende recorddeclaraties aangescherpt. Code die in oudere Delphi-versies compileerde, kan daardoor mislukken wanneer een Enhanced Metafile-record een tagLOGBRUSH32-waarde bevat terwijl de GDI API een tagLOGBRUSH of TLogBrush verwacht.

Het symptoom verschijnt meestal bij het afspelen van EMF-records of het opnieuw opbouwen van GDI-borstelobjecten uit PEMRCreateBrushIndirect. Een directe aanroep zoals hieronder lijkt logisch, maar de twee recordtypen zijn in nieuwere Delphi-compilers niet assignment-compatible.

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;

De compiler meldt E2010 Incompatible types: 'tagLOGBRUSH' and 'tagLOGBRUSH32'. Forceer het record niet via een onveilige cast. De veiligere oplossing is een lokale TLogBrush te maken, de compatibele velden expliciet te kopiëren en die structuur aan CreateBrushIndirect door te geven.

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;

Waarom expliciet kopiëren veiliger is

tagLOGBRUSH32 wordt gebruikt in de EMF-recordindeling zodat de geserialiseerde metafilegegevens een stabiele 32-bit veldindeling hebben. CreateBrushIndirect accepteert echter de normale Windows-borstelstructuur uit de huidige API-declaratie. Het kopiëren van lbStyle, lbColor en lbHatch documenteert de conversie en voorkomt afhankelijkheid van compiler-specifieke recordcompatibiliteit.

Dit patroon is ook eenvoudiger te reviewen wanneer legacy grafische code naar Unicode Delphi of 64-bit targets wordt geport. Als een latere Windows SDK-declaratie opnieuw type-aliases wijzigt, maakt de veld-voor-veldconversie de bedoelde gegevensgrens duidelijk.

Porting-checklist

  • Laat Winapi.Windows in de uses-lijst staan voor moderne Delphi-projecten.
  • Kopieer EMF-recordvelden naar de API-structuur die de GDI-aanroep verwacht.
  • Vervang dit niet door een blinde pointercast tenzij u de binaire indeling en levensduur hebt gecontroleerd.
  • Nadat de borstel is gemaakt, behoudt u de bestaande eigendomsregels voor uw GDIObjects-tabel.

Waarom dit bij Delphi-upgrades verschijnt

Legacy EMF-afspeelcode behandelt Windows-records vaak alsof API-structuren en geserialiseerde metafile-structuren uitwisselbaar zijn. Oudere units en lossere aliases lieten die aanname soms compileren. Moderne Delphi-declaraties zijn strikter, waardoor de compiler de grens zichtbaar maakt die al in de Windows API aanwezig was.

De fout is daarom niet alleen een syntaxisprobleem. Het is een nuttige waarschuwing dat de code van een bestandsformaatstructuur naar een live GDI-aanroep gaat. Door de conversie expliciet te houden, blijft de code veiliger wanneer deze later wordt beoordeeld voor 64-bit builds, Unicode-migratie of bijgewerkte Windows SDK-declaraties.

Wat u niet moet doen

Een pointercast lijkt de compilerfout misschien te verwijderen, maar verbergt de werkelijke conversie en maakt later onderhoud moeilijker. Reviewers zien dan ook niet welke velden bewust worden gebruikt. De veldkopie is kort, deterministisch en zelfdocumenterend, en is daarom de betere standaard voor applicatiecode en componentsamples.

Als uw EMF-parser veel vergelijkbare records verwerkt, plaats deze conversie dan in een kleine helperfunctie en test die met meerdere borstelsstijlen. Zo blijft de replay-loop leesbaar terwijl de Windows API-grens expliciet blijft.