Структурата LOGBRUSH има два отделни живота в Windows API. Единият е дескрипторът на живия GDI обект, който CreateBrushIndirect приема. Другият е неговият сериализиран еквивалент в запис на Enhanced Metafile, където EMF спецификацията фиксира ширината на полетата на 32 бита за платформено независимо съхранение. Тези две неща винаги са били структурно различни; Delphi просто ви позволяваше да се преструвате на обратното
До Delphi XE (и грубо, всичко преди преименуването на модулите с обхват/scoped unit rename), преводите на Windows хедърите бяха достатъчно свободни, че присвояването на tagLOGBRUSH32 към TLogBrush се компилираше без оплаквания. Delphi XE2 премести декларациите на Windows API в Winapi.Windows и затегна дефинициите на записите (records). В този момент компилаторът спря да приема директно подаване на Data^.lb към CreateBrushIndirect, защото Data^.lb е от тип tagLOGBRUSH32, а функцията очаква TLogBrush и тези два типа записи вече не са съвместими за присвояване
Съобщението за грешка е E2010 Incompatible types: 'tagLOGBRUSH' and 'tagLOGBRUSH32'. Кодът, който го предизвиква, изглежда така:
procedure VEMRCREATEBRUSHINDIRECT(Data: PEMRCreateBrushIndirect);
begin
GDIObjects[Data^.ihBrush] := CreateBrushIndirect(Data^.lb);
end;
Директното подаване на Data^.lb работеше преди XE2. То вече не работи и преобразуването на указател (pointer cast) е грешният отговор. Двоичните оформления са достатъчно сходни, че преобразуването може да не се срине веднага, но то погребва конверсията в мълчание и оставя проверяващите без индикация, че изобщо се случва някаква конверсия. Когато бъдеща актуализация на SDK промени псевдоним на поле или добави padding, преобразуването ще подаде грешните байтове без никакво предупреждение по време на компилация
Правилната поправка е локален TLogBrush с изрично копиране на полета:
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 форматът използва за сериализация. Нейното поле lbHatch е широко 32 бита, което е стабилният размер, който метафайл форматът гарантира през платформи и граници на битовостта. TLogBrush, от друга страна, е структурата, която CreateBrushIndirect предава директно в ядрото. При 64-битов Windows билд (build), lbHatch в TLogBrush е ULONG_PTR с размер на указател, така че се разширява до 64 бита. Това не е детайл, който компилаторът може безшумно да преодолее вместо вас
Индивидуалното копиране на трите полета дава на компилатора пълната картина на това, което възнамерявате. lbStyle носи типа на четката (solid, hatched, pattern, hollow и така нататък). lbColor носи COLORREF. lbHatch носи или индекс на щрих (hatch index), или манипулатор на растерно изображение (bitmap handle) в зависимост от lbStyle. И трите са директно четими в дебъгера и и трите са видими за ревюиращия кода, без да се налага да преследва из хедърните файлове, за да разбере какво покрива преобразуването на указателя
Защо грешката изниква при надграждане на Delphi
Старият EMF playback код често беше писан срещу стария Windows.pas на Delphi, където псевдонимите на типове бяха по-малко строги и присвояването между структурно сходни записи беше разрешено в повече случаи. Когато надградите до XE2 или по-нова версия и компилаторът започне да вижда декларации на Winapi.Windows, той чертае по-твърда линия около идентичността на записите (record identity). Грешката E2010 не е ново поведение в някакъв изненадващ смисъл; по-строгата проверка просто прави вече съществуващата граница на API видима. EMF структурата и GDI структурата на извикване никога не са били едно и също нещо, дори когато Delphi ви позволяваше да ги третирате като такива
Следователно пътят за надграждане е механичен: намерете всяко място в цикъла за възпроизвеждане (replay loop) на EMF, където поле tagLOGBRUSH32 се влива в GDI извикване, и вмъкнете локален TLogBrush с копирането на трите полета. Ако вашият парсер (parser) обработва много типове записи с параметри за четка, малък помощник за конвертиране (conversion helper) държи модела ограничен:
function LogBrushFrom32(const B: tagLOGBRUSH32): TLogBrush;
begin
Result.lbStyle := B.lbStyle;
Result.lbColor := B.lbColor;
Result.lbHatch := B.lbHatch;
end;
Извикайте LogBrushFrom32(Data^.lb) навсякъде, където старият код е подавал Data^.lb директно. Цикълът за възпроизвеждане остава четим и границата на API остава изрична. Няма нищо друго, което компилаторът да изисква от вас тук, и нищо изненадващо не ви чака при 64-битовите таргети (targets), след като ширината на полетата е обработена правилно