Delphi XE2 и более новые версии перенесли большую часть объявлений Windows API в область видимости модуля Winapi.Windows и ужесточили несколько объявлений записей. Поэтому код, который собирался в старых версиях Delphi, может перестать компилироваться, когда запись Enhanced Metafile предоставляет значение tagLOGBRUSH32 а GDI API ожидает tagLOGBRUSH или TLogBrush.
Обычно это проявляется при воспроизведении записей EMF или восстановлении объектов кисти GDI из PEMRCreateBrushIndirect. Прямой вызов вроде следующего выглядит разумно, но в новых компиляторах Delphi эти два типа записей не совместимы для присваивания.
|
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. Копирование lbStyleи lbColorа также lbHatch документирует преобразование и убирает зависимость от совместимости записей в конкретном компиляторе.
Такой прием также проще проверять при переносе старого графического кода на Unicode Delphi или 64-битные цели. Если в будущих объявлениях Windows SDK псевдонимы типов снова изменятся, поэлементное преобразование ясно покажет предполагаемую границу данных.
Контрольный список переноса
- Оставьте
Winapi.Windowsв списке uses для современных проектов Delphi. - Копируйте поля записи EMF в структуру API, которую ожидает вызов GDI.
- Не заменяйте это слепым приведением указателя, пока не проверены двоичная раскладка и время жизни данных.
- После создания кисти сохраните существующие правила владения объектами для таблицы
GDIObjects.
Почему это появляется при обновлении Delphi
Старый код воспроизведения EMF часто обращается с записями Windows так, будто структуры API и сериализованные структуры метафайла взаимозаменяемы. Старые модули и более свободные псевдонимы иногда позволяли такой предпосылке компилироваться. Современные объявления Delphi строже, поэтому компилятор показывает границу, которая уже существовала в Windows API.
Следовательно, эта ошибка не только синтаксическая. Это полезное предупреждение о том, что код переходит от структуры файлового формата к живому вызову GDI. Явное преобразование делает код безопаснее при последующей проверке для 64-битных сборок, миграции Unicode или обновленных объявлений Windows SDK.
Чего не делать
Приведение указателя может показаться способом убрать ошибку компилятора, но оно скрывает реальное преобразование и усложняет дальнейшее сопровождение. Оно также не показывает проверяющим, какие поля используются намеренно. Копирование полей короткое, детерминированное и самодокументируемое, поэтому это лучший вариант по умолчанию для кода приложений и примеров компонентов.
Если ваш парсер EMF обрабатывает много похожих записей, вынесите это преобразование в небольшую вспомогательную функцию и протестируйте ее с несколькими стилями кистей. Так цикл воспроизведения останется читаемым, а граница Windows API будет явной.