SDKでウィンドウ位置を正しく保存・復元する方法。†Windowsプログラミングにも慣れてきた初心者と中級者の間くらいのプログラマが往々にして実装したがる機能があります。それが今回取り扱うウィンドウ位置・状態の保存・復元機能です。アプリケーションの終了時にその時点でのウィンドウの位置と状態(最大化されているか否か)を設定ファイル等に保存し、次回起動時に同じ位置と状態で起動するという、プログラマの自己満足度をかなり補充することのできる機能です。この機能、簡単なようで意外と奥深い面があります。見た目はうまく実装できているようでも、いざ配布してみると思いもよらないバグが報告されたりもします。マルチモニタ環境で使うユーザに捕まったりした日には、まず手元にマルチモニタ環境が無いと現象の再現すらできません。そこでこのコンテンツでは、誰でも実装できるようで誰でも間違っている可能性のあるこの機能を正しく実装してみたいと思います。今回は、以下の2関数の実装を目的とします。セクションに保存する。対象となるウィンドウハンドル。INIファイルのパス。メッセージ内で呼び出すこと。セクションからウィンドウ状態を復元する。対象となるウィンドウハンドル。INIファイルのパス。メッセージ内で呼び出すこと。iniFilePath);今回はINIファイルへ保存するようにしましたが、別に保存先はレジストリでも構いません。作成するアプリケーションにとって適切な場所へ保存して下さい。マルチユーザ環境の考慮も忘れずに。構造体の内容を保存、復元する以下の関数を定義します。セクションに保存する。対象となるRECT構造体。INIファイルのパス。セクションから復元する。対象となるRECT構造体。INIファイルのパス。保存されていない場合は元データを書き換えない。iniFilePath);}以下の説明において、ウィンドウ位置の保存と復元にはこれらの関数を用います。次の実装を見てみて下さい。関数の説明コメントは省略してあります(以下同様)。}}初心者が現在の知識を総動員して実装したウィンドウ状態保存・復元はこんな感じになるんじゃないでしょうか。実際、このコードの過ちに気付かない方もいるのでは?このコードを実装したアプリケーションの起動と終了を繰り返すと、確かにウィンドウの位置と最大化状態が保存されていることがわかります。ここで満足してしまうと、ユーザの使い方一つで恐ろしいことになってしまいます。最大化した状態で終了し、再び起動した後、最大化を解いて(通常状態にして)みて下さい。やってみればわかりますが、最大化した時のウィンドウサイズが保存されてしまっており、通常状態にしてもウィンドウが小さくなりません。最小化した状態でタスクバーの右クリックメニューから終了させてみて下さい。環境にもよりますが、INIファイルになかなか恐ろしい値が書き込まれたのではないでしょうか。いう値が書き込まれました。次の起動時には、タスクバーに姿はあれどウィンドウは見えなくなってしまいました。関数は現在の状態におけるウィンドウの位置を返します。最大化されていれば最大化された位置とサイズを返しますし、最小化されていればとてつもなく小さい値を返します。これでは正しい位置保存とはいえません。次のコードを見て下さい。構造体に値を設定します。具体的にどんな値が設定されるかについてはMSDNライブラリを参照して下さい。メンバには、現在の状態に関係なく常に通常状態でのウィンドウ位置が格納されます。これにより、どんな状態で終了したとしても正しいウィンドウ位置を書き出すことができます。実は先に述べたコードではまだ問題があります。それは、アプリケーション終了後に解像度が変更された場合です。1280x1024の環境でウィンドウを左端へ持っていき終了した後、解像度を1024x768などに下げると、これまたウィンドウは画面の外へ行ってしまいます。そこで、ウィンドウ位置の復元時に現在の画面領域内に収まっていなかったら位置をずらす必要があります。それを実装してみたのが次のコードです。関数は内容が変わらないため省略します。関数で画面のサイズを取得し、ウィンドウが画面からはみ出ていた場合は画面内に収まるように位置を補正します。ウィンドウの方が画面よりも大きい場合はウィンドウを左端及び上端を画面の端に合わせます。これで完璧に見えるでしょう。実際、こういった感じで紹介されているサイトも探せばあると思います。これでは間違い、というか不十分なのです。画面が一つの環境では気付かないかもしれません。このコードはマルチモニタ環境に対応していないのです。関数で取得できる画面のサイズは、プライマリモニタの画面サイズです。邪魔をしてしまいます。上のコードでは、セカンダリモニタ上は画面外と見なされ、プライマリモニタ上へ位置補正されてしまうのです。これを考慮せずにリリースされているソフトウェアは多く、市販のソフトウェアですら考慮されていないこともあります。関数を使ってみたいと思います。この関数は、指定した長方形領域との交差部分が最も広いモニタのハンドルを返します。この関数を用いた実装方法を次に示します。}}対象となるモニタの範囲内に収まるように位置を補正しています。マルチモニタ環境の方は実際に動作確認をしてみると良いでしょう。このコードでは、ウィンドウが複数のモニタにまたがっていた場合、より広く交差しているモニタの方へ移動されます。複数のモニタをまたぐことを許可する場合は処理を考え直す必要があります。0)としないことです。0)はあくまでプライマリモニタの左上端座標です。プライマリモニタの左側にセカンダリモニタを置いている環境の場合、全体での左端のX座標はマイナスになります。このことを考慮しているプログラムはフリーソフトレベルでは滅多に無いですが、完璧を目指すならば是非考慮してみて下さい。