サイトアイコン Embarcadero RAD Studio, Delphi, & C++Builder Blogs

VCLフォームのClientWidthとWindowsバージョンのPEフラグの奇妙なケース

Windows アプリケーションは、EXEファイルのPEヘッダで宣言された Windows バージョンによって動作が異なることを認識している開発者は少ないかもしれません。実際、Delphi 11ではWindowsバージョンのPEフラグが変更されておりますが、これに気がついている開発者は少なく、この変更によって以前のDelphiバージョンと比べて動作が異なるという報告をいただいております。

このブログでは、WindowsバージョンのPEフラグの変更によって影響するVCLのケースについて解説いたします。

Windowsアプリケーションは、PE形式のEXEファイルに格納されており、多くの領域でアプリケーションの動作を決定する多数のPEフラグが含まれています。これらのPEフラグの1つが「Windowsバージョン」です。

昨年、エンバカデロの開発チームは、WindowsのAPIの一部(特に非クライアント領域とHigh DPI関連)が、最新バージョンのWindows OSで正常に動作しないことを認識しています。

この主な原因は、Windows XPのような古いバージョンの公式サポートが終了しているにもかかわらず、DelphiアプリケーションがデフォルトでPEフラグにかなり古いバージョンのWindowsを使用していたことが理由です。このため、Delphi 11 Alexandriaでは、いくつかの純粋なAPIの問題に対処するために、PEフラグのWindowsバージョンを6.0に切り替えました。

この変更は多くの分野で有効なのですが、既存のVCLアプリケーションの動作に大きな変更をもたらす可能性があります。目に見える例としては、フォームのBorderStyleプロパティがbsDialogの時のウィンドウ境界線のサイズです。ボーダーのスタイルが大きくなるため、フォームの幅(Width)とクライアントの幅(Width)の差が変わり、高さ(Height)も同様です。

実際に簡単な例をお見せしましょう。この例では、フォーム上に1つのTButtonコンポーネント、2つのTLabelコンポーネントを配置した非常にシンプルなVCLアプリケーションを作成しています。またメインフォームでは、現象を再現させるために以下のプロパティ設定を行っています。

[crayon-672acb6c8e05d512338981/]

そしてButton1のOnClickイベントハンドラに以下のコードを実装します。

[crayon-672acb6c8e065553207211/]

このサンプルアプリケーションを実行すると、下図のDelphi 11(左)とDelphi 10.4(右)のように異なる結果になります。

フォームの幅(Width)が1000の場合、Delphi 11ではクライアントピクセルは974 になりますがが、古いバージョンでは994でした。この理由は、Delphi 11ではボーダーが太くなっているためです。 

ただし、これによって良い面もあります。タイトルバーの間隔が広くなり、ウィンドウのキャプションがボーダーに詰め込まれていないことです。(古いバージョンのDelphiでは、これに関する不具合が報告されていました)

つまりDelphi 11で作成された上図の左側のフォームの方が、非クライアント領域の見栄えが良くなっています。Widthを設定する境界線がない場合、どちらの場合もClientWidthは同じで、フォームの幅(Width)は異なります。 

したがって、コンポーネントの配置(絶対値の場合)には影響がありません。 フォームのClient Widthが以前のバージョンと異なる値に設定されるのは、手動でフォームのWidthを設定している場合だけです。

もし既存のアプリケーションに対して、古いバージョンとの互換性や動作を維持したい場合、Delphi 10.4に戻す必要はありません。PEフラグは、コンパイラとリンカで制御することができます。

実際、Delphi 11を使用していても、メインのプロジェクトファイルに以下の2つのコンパイラ指令を追加すると、上図の右の画像(旧バージョン)と同じ結果が得られます。

[crayon-672acb6c8e067939569696/]

上記のコードによって、エンバカデロがDelphi 11に適用したデフォルトの変更を元に戻すことができ、古いWindowsバージョンのPEフラグと古い動作を使用し続けることができますが、この設定は良くも悪くも働く場合があるため、最終的な判断は開発者次第となります。

モバイルバージョンを終了