Delphi XE2 及更新版本將大量 Windows API 宣告移入帶作用域的 Winapi.Windows 單元,並收緊了一些記錄型別宣告。因此,舊版 Delphi 可以編譯的程式碼,在新版編譯器中可能會失敗:EMF 記錄中提供的是 tagLOGBRUSH32,而 GDI API 需要的是 tagLOGBRUSH 或 TLogBrush。
這個問題通常出現在回放增強型圖元檔案或從 PEMRCreateBrushIndirect 重建 GDI 畫刷物件時。下面這種直接呼叫看起來合理,但在新編譯器中兩個記錄型別並不賦值相容。
|
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 宣告再次調整類型別名,逐欄位轉換仍能說明程式碼的真實意圖。
升級 Delphi 時為什麼會暴露
舊的 EMF 回放程式碼經常把 Windows 記錄當作 API 結構直接使用。舊單元和寬鬆別名有時會讓這種假設通過編譯。現代 Delphi 宣告更嚴格,所以編譯器把原本就存在的檔案格式結構與 GDI 呼叫邊界暴露出來。
因此,這不僅是語法問題。它提醒你程式碼正在從檔案格式結構跨入即時 GDI API。保持轉換顯式,有助於後續做 64 位遷移、Unicode 遷移或更新 Windows SDK。
不要這樣修復
指標強制轉換也許能消除編譯錯誤,但它隱藏了真正的轉換,並增加後續維護難度。欄位複製短小、確定、可讀,通常是應用程式碼和元件示例中的更好預設選擇。
- 現代 Delphi 專案中保持使用
Winapi.Windows。 - 把 EMF 記錄欄位複製到 GDI 呼叫需要的 API 結構中。
- 除非驗證過二進位制佈局和生命週期,否則不要使用盲目的指標轉換。
- 畫刷建立後,繼續遵守
GDIObjects表中原有的物件所有權規則。