ホーム >プログラム >Delphi 6 ローテクTips

クラスメモ・TScreen

TScreenの問題点。

TScreen.Monitorsが画面のプロパティの変更を感知しない

TScreenのMonitorsプロパティはマルチモニター環境での各モニターの位置やサイズを取得するために必要となります。
ところが画面のプロパティでモニターの数を変更してもTScreen.Monitorsはそれを感知しません。
TScreenはウィンドウハンドルを持たないのでモニター情報が変わったというメッセージを受け取ることができないからです。
モニター情報が変わったというメッセージはトップレベルウィンドウにしか通知されないのでこの問題はTScreenの問題というよりもTFormあるいはTApplicationの問題といった方が良いかもしれません。
テストしてみるとモニターの数が変わるとそれまでのモニターのハンドルはすべて無効になり、モニターのハンドルは新しいものになるようです。
けれどもTScreenのMonitorsプロパティは古いモニターハンドルを保持したままなのでその古いモニターハンドルを元にモニターの情報を得ようとしてもうまくいきません。
TScreenのMonitorsプロパティでモニターの位置やサイズを取得できないということです。
それだけであればEnumDisplayMonitors APIを呼び出すなりして自前でやればよいだけなのですが、困ったことにツールボタンのDropDownプロパティでエラーが起きてしまいます。

プログラムを起動した後にモニターの数を変えると、それ以降ツールバーのツールボタンのドロップダウンメニューを表示させようとするとアクセス違反のエラーが起きてドロップダウンメニューが表示されなくなります。
ドロップダウンメニューを表示する時に無効となった古いモニターハンドルを基にモニターの高さを取得しようとするために起きるエラーです。

このエラーを回避するためにはフォームにWM_DISPLAYCANGEメッセージを補足するためのイベントハンドラを追加してその中でTFormのMonitorプロパティを呼び出します。
TFormのMonitorプロパティはTScreenのMonitorsプロパティの値が無効であった場合Monitorsプロパティのリセットと再取得を行う実装になっているのでそれを利用するためです。

  private
  { Private 宣言 }
  ...

    procedure WMDisplayChange(var Msg: TMessage); message WM_DISPLAYCHANGE;
  ...

procedure TForm1.WMDisplayChange(var Msg: TMessage);
begin
  //TScreenのMonitorsプロパティがディスプレイの変更に追随しない不具合の回避のため
  Self.Monitor;
end;

これだけでプログラムの起動中にモニターの数が変わってもツールボタンのドロップダウンのエラーは回避されますしTScreen.Monitorsプロパティを利用してモニターの位置やサイズを取得することも可能になります。


2011-05-11: