以前、閉じたときのフォームの位置を記憶する 本当に鳩でも分かるC#講座という記事を書きました。今回はそのアプリケーションのフォームではなく別のアプリケーションのウィンドウの位置を記憶させたり、記憶させていた場所に移動させる方法を考えます。ここでいう「別のアプリケーション」とはC#で書かれているとは限りません。Windowsアプリケーション全般です。

他のアプリケーションの位置とサイズを知る

まず実行されているアプリケーションのウィンドウの位置を把握しなければなりません。ウィンドウハンドルがわかれば、位置の移動やサイズの変更、さらにはタイトルバーの書き換えなども可能になります。そこで最初にデスクトップに表示されているウィンドウハンドルを取得するところからはじめます。

表示されているすべての親を持たないウィンドウハンドルを取得する

取得の対象は親ウィンドウをもたないウィンドウであり、表示されているウィンドウだけに限定します。表示されることのないウィンドウの位置とかサイズを変更してもあまり意味はないので・・・。

実行ファイルのパスからアプリケーションが実行されているかを知る

ウィンドウハンドルを知ることができてもアプリケーションを終了して再起動すればウィンドウハンドルも変更されてしまいます。しかし実行ファイルがあるパスは変わりません。そこで実行ファイルのパスから現在、そのアプリケーションが実行されていないかを調べる方法を考えます。

System.Management.dllを参照設定に追加すれば、実行されているすべてのプロセスのプロセスID、プロセス名、ファイル名が取得できます。ただしウィンドウをもたないプロセスは必要ないのでHaveWindowHandleで除外しています。

GetWindowThreadProcessId関数を使えばウィンドウハンドルからプロセスIDを調べることができます。また上記のやり方でプロセスID、プロセス名、ファイル名がセットで取得できるので、実行ファイルのパスからウィンドウハンドルを求めることができるのです。もちろんそのときにそのアプリが実行されてたらという条件つきですが・・・。

アプリの作成

では早速はじめましょう。まず仕様については以下のとおりです。

作成するアプリの仕様について

まずリストビューをふたつ作成します。ひとつは現在実行されているプロセスを表示するためのものです。ここにはプロセス名、プロセスID、実行ファイルのパスが表示されます。

そして[アプリケーションを監視する]ボタンをおすとそのアプリケーションウィンドウの位置とサイズが0.5おきに表示されます。途中で監視していたアプリケーションを終了すると最後に取得されたウィンドウの位置とサイズが表示されたままになります。

[登録する]ボタンをおすと監視しているアプリケーションのウィンドウの位置とサイズが実行ファイルのパスとともに登録されます。この時点ではファイルに保存されるわけではありません。

[保存する]ボタンをおすと登録された内容がファイルとして登録されます。また[読み込み]ボタンを押すとファイルとして保存されていた内容が読み込まれます。

最後に[反映させる]ボタンがおされると、もしそのアプリケーションが実行されていた場合、ウィンドウの位置とサイズが登録されていた状態に変更されます。

でざいな

リストビューの初期化

アプリケーションが開始されたらふたつのリストビューを初期化します。

実行されているプロセスを取得する

次に[実行されているプロセスを取得する]ボタンが押されたときの処理を示します。現在実行されているプロセスのなかでウィンドウが表示されているものだけ集めて、リストビューに表示させています。

アプリケーションを監視する

次に[アプリケーションを監視する]ボタンが押されたときの処理を示します。

まずリストビューでどのアイテムが選択されているか調べます。そして選択されているアイテムからプロセスのIDと実行ファイルのパスがわかるので、そこから探したいウィンドウハンドルを探します。

ウィンドウハンドルを取得することができたら自作メソッドのShowWindowInfoでウィンドウの位置とサイズを表示します。

ウィンドウの位置とサイズをリアルタイムで表示する

ShowWindowInfoメソッドでおこなわれる処理を示します。API関数のGetWindowRect関数からウィンドウの位置とサイズを求めてこれを表示します。

途中でアプリケーションが終了したときの対策

またタイマーが起動しているときは監視しているアプリケーションの情報を表示しつづければよいのですが、ユーザーによって終了されるかもしれないので、その対策もしておきます。IsWindow関数は渡されたウィンドウハンドルが無効である場合は0を返します。そこでIsWindow(WindowHandle) == 0のときはタイマーを停止させて最後に取得されたウィンドウの状態を表示します。

ウィンドウの位置とサイズの登録

[登録する]ボタンが押されたときの処理を示します。AppInfoオブジェクトを生成してそのなかにデータを保存します。そしてリストのなかに格納します。それと同時にふたつめのリストビューに登録したデータ(実行ファイルのパス、ウィンドウの位置とサイズ)を表示させます。

ファイルとして保存する

[保存する]ボタンが押されたときの処理を示します。Docオブジェクトを作成して、これをXMLファイルとして保存します。

ファイルを読み出す

[読み込み]ボタンが押されたときの処理を示します。ファイルからデータを読み出して、ここからふたつめのリストビューにデータを表示させます。

情報を反映させる

[反映させる]ボタンを押したときの処理を示すまえに、ウィンドウを移動させるAPI関数を使った移動の処理を示します。自作メソッドのSetWindowPosから同名のAPI関数SetWindowPosを呼び出しています。

では[反映させる]ボタンを押したときの処理を示します。ふたつめのリストビューで選択されている位置とAppInfosのインデックスから実行ファイルのパスを取得します。この実行ファイルのパスをもつアプリケーションが実行されているときはそのウィンドウの位置とサイズを登録されている位置に変更します。