Teknisk artikel

Løsning af Delphi E2010 tagLOGBRUSH og tagLOGBRUSH32 uoverensstemmelse

LOGBRUSH-strukturen har to distinkte liv i Windows API. Det ene er den aktive GDI-objektdeskriptor, som CreateBrushIndirect accepterer. Det andet er dens serialiserede modstykke inde i en Enhanced Metafile-post, hvor EMF-specifikationen fastsætter feltbredder til 32 bit for platformsneutral lagring. Disse to ting har altid været strukturelt forskellige; Delphi lod dig blot lade som om det modsatte

Op til og med Delphi XE (og groft sagt alt før de ændrede navnerum) var Windows-headeroversattelserne løse nok til, at tildeling af tagLOGBRUSH32 til TLogBrush kompilerede uden klager. Delphi XE2 flyttede Windows API-deklarationerne til Winapi.Windows og strammede postdefinitionerne. Fra det tidspunkt afviste compileren direkte at sende Data^.lb til CreateBrushIndirect, fordi Data^.lb er typet som tagLOGBRUSH32, og funktionen forventer TLogBrush — og disse to posttyper er ikke længere tildelingskompatible

Fejlmeddelelsen er E2010 Incompatible types: 'tagLOGBRUSH' and 'tagLOGBRUSH32'. Den kode, der udløser den, ser sådan ud:

procedure VEMRCREATEBRUSHINDIRECT(Data: PEMRCreateBrushIndirect);
begin
  GDIObjects[Data^.ihBrush] := CreateBrushIndirect(Data^.lb);
end;

At sende Data^.lb direkte virkede før XE2. Det gør det ikke længere, og en pointerkast er det forkerte svar. Det binære layout er tilstrækkeligt ens til, at et kast måske ikke bryder umiddelbart, men det skjuler konverteringen i stilhed og giver anmeldere ingen indikation af, at nogen konvertering overhovedet finder sted. Når en fremtidig SDK-opdatering ændrer et feltalias eller tilføjer polstring, vil et kast sende forkerte bytes uden nogen kompileringsfejl

tagLOGBRUSH32 gemmer en fast 32-bit lbHatch i EMF-posten, mens TLogBrush bruger en pointerstørrelses ULONG_PTR; kopiering af de tre felter til en lokal TLogBrush løser Delphi-fejlen E2010
En direkte overførsel udløser E2010, og en pointer-cast skjuler blot problemet; den eksplicitte tre-felts-kopiering giver compileren det fulde billede på tværs af 32-bit- og 64-bit-mål

Den korrekte rettelse er en lokal TLogBrush med en eksplicit feltkopi:

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;

Hvad feltkopien faktisk gør

tagLOGBRUSH32 er den form, EMF-formatet bruger til serialisering. Dens lbHatch-felt er 32 bit bredt, hvilket er den stabile størrelse, metafilformatet garanterer på tværs af platforme og bitgrænser. TLogBrush er derimod den struktur, som CreateBrushIndirect sender direkte til kernen. I et 64-bit Windows-build er lbHatch i TLogBrush en pointerstørret ULONG_PTR, så den udvides til 64 bit. Det er en detalje, compileren ikke stiltiende kan bygge bro over

At kopiere de tre felter individuelt giver compileren det fulde billede af, hvad du har til hensigt. lbStyle indeholder penseltypen (solid, skraveret, mønster, hul og så videre). lbColor indeholder COLORREF. lbHatch indeholder enten et skraveringsindeks eller et bitmap-handle afhængigt af lbStyle. Alle tre er direkte læsbare i debuggeren, og alle tre er synlige for en kodegennemganger uden at skulle spore igennem headerfiler for at forstå, hvad et pointerkast skjulte

Hvorfor fejlen dukker op under Delphi-opgraderinger

Arvet EMF-afspilningskode var ofte skrevet mod Delphis gamle Windows.pas, hvor typealiaser var mindre strenge, og tildeling mellem strukturelt lignende poster var tilladt i flere tilfælde. Når du opgraderer til XE2 eller nyere, og compileren begynder at se Winapi.Windows-deklarationer, trækker den en hårdere grænse omkring postidentitet. E2010-fejlen er ikke ny adfærd i nogen overraskende forstand; den strengere kontrol gør blot den allerede eksisterende API-grænse synlig. EMF-strukturen og GDI-kaldsstrukturen var aldrig det samme, selv når Delphi lod dig behandle dem som sådan

Opgraderingsstien er derfor mekanisk: find hvert sted i EMF-afspilningsløkken, hvor et tagLOGBRUSH32-felt flyder ind i et GDI-kald, og indsæt en TLogBrush-lokal med trefeltskopien. Hvis din parser håndterer mange posttyper med penselparametre, holder en lille konverteringshjælper mønstret samlet:

function LogBrushFrom32(const B: tagLOGBRUSH32): TLogBrush;
begin
  Result.lbStyle := B.lbStyle;
  Result.lbColor := B.lbColor;
  Result.lbHatch := B.lbHatch;
end;

Kald LogBrushFrom32(Data^.lb) overalt, hvor den gamle kode sendte Data^.lb direkte. Afspilningsløkken forbliver læselig, og API-grænsen forbliver eksplicit. Der er intet andet, compileren har brug for fra dig her, og intet overraskende, der venter på 64-bit-mål, når feltbredderne håndteres korrekt