Edited at

[WindowsAPI]画面外にはみ出たウィンドウを強制的に画面内に移動する

More than 1 year has passed since last update.

帰省中です。実家で書いてます。


はじめに

画面外にはみ出たウィンドウがアプリケーションのメインウィンドウ(タスクバーに表示されるウィンドウ)の場合は、以下の手段でプログラムを書かずとも、手動で戻すことができます

例えば次のサイトをご覧ください。Windowsで画面外に移動してしまったウィンドウを表示領域内に戻す:Tech TIPS - @IT


どんな状況だったのか

私が使っているあるアプリケーションは、ツールボックスが別ウィンドウで出てくるようになっている。またツールボックスの位置はアプリケーション終了時に保存されるようになっている。これを自宅だけで使っている(※ノートパソコンを大きなディスプレイに繋いで使っている)ときはよかったのだが、実家に持っていってノートパソコンのディスプレイだけで使うときに問題が起きた。

自宅で使っていたときに、ツールボックスのウィンドウを「ノートパソコンのディスプレイとしてはその外になる場所」で終了させていたため、実家に着いてからそのツールボックスが使えなくなってしまったのである。

20180102-ウィンドウがディスプレイの外.png

ここで、画面外にはみ出したウィンドウがアプリケーションのメインウィンドウなのであれば、Windowsで画面外に移動してしまったウィンドウを表示領域内に戻す:Tech TIPS - @ITにあるような方法で手動で動かせるのだがそれができない。

まず考えたのは、そのアプリケーションの設定を保存しているファイルないしレジストリを見つけて設定の値を書き換えるということだったのだが、調べてもわからなくて断念。

その後、Windows APIには「ウィンドウを列挙する」関数があることを思い出し、試行錯誤の結果



  • EnumWindows関数で、現在立ち上がっているウィンドウ(※フォームに限らず、テキストボックスなど各種構成要素も含む)を手当たり次第列挙

  • ウィンドウの名称を調べ(移動したかったツールボックスのタイトルバーには固定の名前が付いている)、それが所望のものであれば、SetWindowPos関数で移動する

という方法で解決したのであった。


具体的なコード

Visual Studio Community 2015で動かしました。C++を使っています。

EnumWindows関数の第一引数は、C++11で規格化されたラムダ式を用いています。

#include <windows.h>

#include <cstring>
#include <set>

const char * QUERY_WINDOW_NAME = "見つけたいウィンドウのタイトル";

std::set<HWND> FoundWindows;
// グローバルにしないとならなかった。
// 「ローカル変数として定義して、EnumWindowsの第一引数を[]でなく[&FoundWindows]にする」
// という方法では、EnumWindowsの部分でコンパイルエラーとなった。

int main(void) {
for (;;) {
BOOL enumresult = EnumWindows([](HWND hwnd, LPARAM lParam) {
// 同じウィンドウが二度現れていた場合は終了
if (FoundWindows.count(hwnd) > 0) {
return FALSE;
}
FoundWindows.insert(hwnd);

CHAR buf[100];
GetWindowTextA(hwnd, buf, 100);
if (std::strstr(buf, QUERY_WINDOW_NAME)) {
// ウィンドウサイズは変更せず(SWP_NOSIZE)、位置を(0, 0)に移動する
SetWindowPos(hwnd, HWND_TOP, 0, 0, 0, 0, SWP_NOSIZE);
return FALSE;
}
return TRUE;
}, 0);
if (!enumresult) break;
}
}