Delphi XE2 i nowsze przeniosły dużą część deklaracji Windows API do zakresowej jednostki Winapi.Windows oraz zaostrzyły kilka deklaracji rekordów. Kod, który kompilował się w starszych wersjach Delphi, może więc przestać się kompilować, gdy rekord Enhanced Metafile udostępnia wartość tagLOGBRUSH32, a GDI API oczekuje tagLOGBRUSH albo TLogBrush.
Objaw zwykle pojawia się podczas odtwarzania rekordów EMF lub odbudowywania obiektów pędzla GDI z PEMRCreateBrushIndirect. Bezpośrednie wywołanie takie jak poniżej wygląda rozsądnie, ale te dwa typy rekordów nie są zgodne przy przypisaniu w nowszych kompilatorach 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; |
Kompilator zgłasza E2010 Incompatible types: 'tagLOGBRUSH' and 'tagLOGBRUSH32'. Nie przepychaj rekordu przez niebezpieczne rzutowanie. Bezpieczniejsza poprawka polega na utworzeniu lokalnego TLogBrush, jawnym skopiowaniu zgodnych pól i przekazaniu tej struktury do 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; |
Dlaczego jawne kopiowanie jest bezpieczniejsze
tagLOGBRUSH32 jest używany w formacie rekordu EMF, aby serializowane dane metafile miały stabilny 32-bitowy układ pól. CreateBrushIndirect przyjmuje jednak zwykłą strukturę pędzla Windows używaną przez bieżącą deklarację API. Kopiowanie lbStyle, lbColor i lbHatch dokumentuje konwersję i unika zależności od specyficznej dla kompilatora zgodności rekordów.
Ten wzorzec jest też łatwiejszy do sprawdzenia podczas przenoszenia starszego kodu graficznego do Unicode Delphi lub celów 64-bitowych. Jeśli późniejsza deklaracja Windows SDK znów zmieni aliasy typów, konwersja pole po polu jasno pokaże zamierzoną granicę danych.
Lista kontrolna portowania
- Zostaw
Winapi.Windowsna liście uses w nowoczesnych projektach Delphi. - Kopiuj pola rekordu EMF do struktury API oczekiwanej przez wywołanie GDI.
- Nie zastępuj tego ślepym rzutowaniem wskaźnika, chyba że zweryfikowano układ binarny i czas życia danych.
- Po utworzeniu pędzla zachowaj istniejące reguły własności obiektów w tabeli
GDIObjects.
Dlaczego pojawia się to przy aktualizacjach Delphi
Starszy kod odtwarzania EMF często traktuje rekordy Windows tak, jakby struktury API i serializowane struktury metafile były wymienne. Starsze jednostki i luźniejsze aliasy czasem pozwalały takiemu założeniu się kompilować. Nowoczesne deklaracje Delphi są ściślejsze, więc kompilator ujawnia granicę, która już istniała w Windows API.
Błąd nie jest więc tylko problemem składni. To przydatne ostrzeżenie, że kod przechodzi ze struktury formatu pliku do aktywnego wywołania GDI. Jawna konwersja zwiększa bezpieczeństwo kodu, gdy później jest sprawdzany pod kątem kompilacji 64-bitowych, migracji Unicode lub zaktualizowanych deklaracji Windows SDK.
Czego nie robić
Rzutowanie wskaźnika może wyglądać jak usunięcie błędu kompilatora, ale ukrywa rzeczywistą konwersję i może utrudnić późniejsze utrzymanie. Nie daje też recenzentom wskazówki, które pola są używane celowo. Kopia pól jest krótka, deterministyczna i samodokumentująca, więc jest lepszym domyślnym wyborem dla kodu aplikacji i przykładów komponentów.
Jeśli parser EMF obsługuje wiele podobnych rekordów, umieść tę konwersję w małej funkcji pomocniczej i przetestuj ją z kilkoma stylami pędzla. Dzięki temu pętla odtwarzania pozostaje czytelna, a granica Windows API nadal jest jawna.