技術記事

Delphi の非互換型エラー: tagLOGBRUSH と tagLOGBRUSH32

Delphi XE2 以降では、Windows API 宣言の大部分がスコープ付きの Winapi.Windows ユニットへ移され、いくつかのレコード宣言もより厳密になりました。そのため、古い Delphi ではコンパイルできていたコードでも、Enhanced Metafile レコードが tagLOGBRUSH32 値を公開し、GDI API が tagLOGBRUSH または TLogBrushを要求する場面で失敗することがあります。

この症状は通常、EMF レコードを再生するとき、または PEMRCreateBrushIndirectから GDI ブラシ オブジェクトを再構築するときに現れます。次のような直接呼び出しは妥当に見えますが、新しい Delphi コンパイラでは 2 つのレコード型に代入互換性がありません。

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は現在の API 宣言で使われる通常の Windows ブラシ構造体を受け取ります。 lbStylelbColorlbHatch をコピーすると、変換の意図が明確になり、コンパイラ固有のレコード互換性に依存しなくて済みます。

このパターンは、レガシーのグラフィックス コードを Unicode Delphi や 64 ビット ターゲットへ移植するときにもレビューしやすくなります。来 Windows SDK の宣言で型エイリアスが再び変わっても、フィールド単位の変換により意図したデータ境界が明確です。

移植チェックリスト

  • 最新の Delphi プロジェクトでは Winapi.Windows を uses リストに残します。
  • EMF レコードのフィールドを、GDI 呼び出しが期待する API 構造体へコピーします。
  • バイナリ レイアウトと寿命を確認していない限り、これを盲目的なポインタ キャストに置き換えないでください。
  • ブラシ作成後は、既存の GDIObjects テーブルのオブジェクト所有ルールを維持します。

Delphi アップグレード時にこれが現れる理由

レガシーの EMF 再生コードでは、Windows レコードを API 構造体とシリアル化されたメタファイル構造体が交換可能であるかのように扱うことがよくあります。古いユニットや緩いエイリアスでは、その前提のままコンパイルできる場合がありました。最新の Delphi 宣言はより厳密なので、Windows API に元から存在していた境界をコンパイラが表面化させます。

したがって、このエラーは単なる構文上の問題ではありません。コードがファイル形式の構造体から実際の GDI 呼び出しへ渡っていることを示す有用な警告です。変換を明示しておくと、後で 64 ビット ビルド、Unicode 移行、更新された Windows SDK 宣言をレビューするときにも安全です。

してはいけないこと

ポインタ キャストはコンパイラ エラーを消すように見えますが、実際の変換を隠し、後の保守を難しくします。また、どのフィールドを意図して使っているのかをレビュー担当者が判断できません。フィールド コピーは短く、決定的で、意図を表すため、アプリケーション コードやコンポーネント サンプルではより良い既定の方法です。

EMF パーサーが同様のレコードを多数扱う場合は、この変換を小さなヘルパー関数にまとめ、複数のブラシ スタイルでテストしてください。そうすれば再生ループを読みやすく保ちながら、Windows API との境界も明示できます。