技術文章

修復 Delphi E2010 tagLOGBRUSH 與 tagLOGBRUSH32 不相容型別

· Delphi 程式設計

Delphi XE2 及更新版本將大量 Windows API 宣告移入帶作用域的 Winapi.Windows 單元,並收緊了一些記錄型別宣告。因此,舊版 Delphi 可以編譯的程式碼,在新版編譯器中可能會失敗:EMF 記錄中提供的是 tagLOGBRUSH32,而 GDI API 需要的是 tagLOGBRUSHTLogBrush

這個問題通常出現在回放增強型圖元檔案或從 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 宣告使用的普通畫刷結構。逐欄位複製 lbStylelbColorlbHatch 能清楚表達轉換邊界,也避免依賴編譯器特定的記錄相容性。

把舊圖形程式碼遷移到 Unicode Delphi 或 64 位目標時,這種寫法也更容易審查。如果後續 Windows SDK 宣告再次調整類型別名,逐欄位轉換仍能說明程式碼的真實意圖。

升級 Delphi 時為什麼會暴露

舊的 EMF 回放程式碼經常把 Windows 記錄當作 API 結構直接使用。舊單元和寬鬆別名有時會讓這種假設通過編譯。現代 Delphi 宣告更嚴格,所以編譯器把原本就存在的檔案格式結構與 GDI 呼叫邊界暴露出來。

因此,這不僅是語法問題。它提醒你程式碼正在從檔案格式結構跨入即時 GDI API。保持轉換顯式,有助於後續做 64 位遷移、Unicode 遷移或更新 Windows SDK。

不要這樣修復

指標強制轉換也許能消除編譯錯誤,但它隱藏了真正的轉換,並增加後續維護難度。欄位複製短小、確定、可讀,通常是應用程式碼和元件示例中的更好預設選擇。

  • 現代 Delphi 專案中保持使用 Winapi.Windows
  • 把 EMF 記錄欄位複製到 GDI 呼叫需要的 API 結構中。
  • 除非驗證過二進位制佈局和生命週期,否則不要使用盲目的指標轉換。
  • 畫刷建立後,繼續遵守 GDIObjects 表中原有的物件所有權規則。