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

GetWindowRect関数

GetWindowRect関数はウィンドウの位置とサイズを取得します。

BOOL GetWindowRect(
 HWND hWnd,
 LPRECT lpRect
);
ウィンドウhWndのスクリーン座標を取得しlpRectに格納する。
成功した場合は0以外を、失敗した場合は0を返す。

スクリーン座標とはデスクトップ(ディスプレイ)上の位置のことで、画面の左上が基準(0, 0)になります。

第二引数lpRectRECT構造体へのポインタです。
子ウィンドウやコントロールに使用した場合でも取得される値はスクリーン座標です。

得られる値はウィンドウの左上と右下の座標ですので、ウィンドウの幅や高さが欲しい場合は「right - left」や「bottom - top」といった計算を行います。

Windows VISTA以降では視覚効果としてウィンドウの影が描画されますが、GetWindowRect関数で取得されるウィンドウ位置にはこの影の領域も含まれています。

GetClientRect関数

ウィンドウ内のクライアント領域のサイズを取得するにはGetClientRect関数を使用します。
詳しくはリンク先を参照してください。

ScreenToClient関数/ClientToScreen関数

スクリーン座標をクライアント座標に変換するにはScreenToClient関数を使用します。
その逆はClientToScreen関数を使用します。
詳しくはリンク先を参照してください。

MoveWindow関数

MoveWindow関数はウィンドウの位置を設定します。

BOOL MoveWindow(
 HWND hWnd,
 int X,
 int Y,
 int nWidth,
 int nHeight,
 BOOL bRepaint
);
ウィンドウhWndのスクリーン座標とウィンドウサイズを設定する。
bRepaintがTRUEの場合はウィンドウを再描画する。
成功した場合は0以外を、失敗した場合は0を返す。

X/Yはスクリーン座標、nWidth/nHeightは幅と高さです。

bRepaintTRUEを設定すると、移動後にウィンドウおよび親ウィンドウ(ある場合)を再描画します。
コントロールに対してFALSEを指定して移動すると親ウィンドウが再描画されないので、実際には移動されているのに見た目には変化がないという状態になります。
(見た目通りの位置にコントロールが存在しないかもしれない)
正常に表示するには無効領域を作成するなどして自分で再描画をする必要があります。

SetWindowPos関数

ウィンドウの移動にはSetWindowPos関数を使用することもできます。

BOOL SetWindowPos(
 HWND hWnd,
 HWND hWndInsertAfter,
 int X,
 int Y,
 int cx,
 int cy,
 UINT uFlags
);
ウィンドウhWndのスクリーン座標とZオーダーを設定する。

XYはスクリーン座標、cxcyは幅と高さです。

hWndInsertAfterZオーダーの指定です。
Zオーダーとはウィンドウの重なり順を決定するものです。
以下の定数のいずれかを指定します。

定数 説明
HWND_TOP 最上位に配置する。
HWND_BOTTOM 最下位に配置する。
HWND_TOPMOST 常に手前に配置する。
HWND_NOTOPMOST 常に手前状態を解除し、常に手前でないウィンドウの中で最上位に配置する。

Zオーダーは「通常の状態」と「常に手前の状態(TOPMOST)」とが存在します。
常に手前に配置されるウィンドウは、非アクティブ状態であっても他のウィンドウの後ろに隠れることはありません。

常に手前に配置されるウィンドウは複数作ることができ、「常に手前グループ」の中でも重なり順が存在します。
「通常の状態グループ」のウィンドウは全ての「常に手前グループ」のウィンドウの後ろに描画されます。

最後のuFlagsは以下の定数の組み合わせを指定します。

定数 説明
SWP_NOSIZE 現在のサイズを保持する。
(cxcyの指定を無視する)
SWP_NOMOVE 現在の位置を保持する。
(XYの指定を無視する)
SWP_NOZORDER 現在のZオーダーを保持する。
(hWndInsertAfterの指定を無視する)
SWP_NOREDRAW ウィンドウ(および親ウィンドウ)を再描画しない。
SWP_NOACTIVATE ウィンドウをアクティブにしない。
SWP_FRAMECHANGED
SWP_DRAWFRAME
WM_NCCALCSIZEメッセージを送る。
(WM_NCCALCSIZEメッセージは通常、ウィンドウサイズを変更した場合に送られる)
SetWindowLongPtr関数でウィンドウスタイルを変更した場合に、変更を適用する。
SWP_SHOWWINDOW ウィンドウを表示する。
SWP_HIDEWINDOW ウィンドウを非表示にする。
SWP_NOCOPYBITS クライアント領域の内容を破棄する。
このフラグをセットしない場合、クライアント領域の有効な内容が保存され、移動後にクライアント領域にコピーされる。
SWP_NOOWNERZORDER
SWP_NOREPOSITION
オーナーウィンドウのZオーダーを変更しない。
SWP_NOSENDCHANGING ウィンドウがWM_WINDOWPOSCHANGINGメッセージを受信しないようにする。
SWP_DEFERERASE WM_SYNCPAINTメッセージを生成しない。
SWP_ASYNCWINDOWPOS 関数呼び出し元スレッドとウィンドウを所有するスレッドが異なる場合に、ウィンドウを所有するスレッドにリクエストを送信する。
これにより、スレッドでリクエストの処理が終了するまで関数呼び出し元スレッドが待機(ブロッキング)することを防ぐことができる。

SetForegroundWindow関数

ウィンドウを前面に出すだけの操作を行う場合はSetForegroundWindow関数が使用できます。

BOOL SetForegroundWindow(
 HWND hWnd
);
ウィンドウhWndをフォアグラウンドウィンドウに設定する。
成功した場合は0以外を、失敗した場合は0を返す。

デスクトップのサイズの取得

GetDesktopWindow関数

デスクトップ自体のサイズを取得する方法はいくつかあります。
GetDesktopWindow関数でデスクトップのハンドルを取得できるので、これを使用してGetWindowRect関数でそのサイズを取得できます。

HWND GetDesktopWindow();
デスクトップウィンドウのハンドルを取得する。

以下のように使用します。


RECT rtScreen;
GetWindowRect(GetDesktopWindow(), &rtScreen);

GetSystemMetrics関数

もうひとつ、GetSystemMetrics関数を使用する方法もあります。

int GetSystemMetrics(
 int nIndex
);
nIndexで指定したシステムメトリクスまたはシステム構成設定を取得する。

システムメトリクスとはシステムで使用されているサイズ情報のことです。
nIndexには定数を指定しますが、この関数は非常に多くの定数が用意されています。
ディスプレイのサイズの取得にはSM_CXSCREEN(横幅)、SM_CYSCREEN(縦幅)を指定します。


int width, height;
width = GetSystemMetrics(SM_CXSCREEN);
height = GetSystemMetrics(SM_CYSCREEN);

ウィンドウを画面中央に配置する

ウィンドウを画面上(または親ウィンドウ上)の中央に配置するような関数は用意されていないので、上述の関数を使用して自前で計算して配置します。
以下はそのサンプルです。


#include <windows.h>

//ウィンドウの生成等は省略

#define IDC_BUTTON_TOPLEFT		100
#define IDC_BUTTON_TOPCENTER	101
#define IDC_BUTTON_TOPRIGHT		102
#define IDC_BUTTON_MIDDLELEFT	103
#define IDC_BUTTON_MIDDLECENTER	104
#define IDC_BUTTON_MIDDLERIGHT	105
#define IDC_BUTTON_BOTTOMLEFT	106
#define IDC_BUTTON_BOTTOMCENTER	107
#define IDC_BUTTON_BOTTOMRIGHT	108

LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	static RECT rtScreen;
	RECT rt;
	int x, y, w, h;

	switch (message)
	{
	case WM_CREATE: //ウィンドウ作成
		//ボタンの作成
		CreateWindow(
			L"BUTTON", L"┌",
			WS_CHILD | WS_VISIBLE,
			0, 0, 25, 25,
			hWnd, (HMENU)IDC_BUTTON_TOPLEFT, hInst, NULL);

		CreateWindow(
			L"BUTTON", L"↑",
			WS_CHILD | WS_VISIBLE,
			25, 0, 25, 25,
			hWnd, (HMENU)IDC_BUTTON_TOPCENTER, hInst, NULL);

		CreateWindow(
			L"BUTTON", L"┐",
			WS_CHILD | WS_VISIBLE,
			50, 0, 25, 25,
			hWnd, (HMENU)IDC_BUTTON_TOPRIGHT, hInst, NULL);

		//

		CreateWindow(
			L"BUTTON", L"←",
			WS_CHILD | WS_VISIBLE,
			0, 25, 25, 25,
			hWnd, (HMENU)IDC_BUTTON_MIDDLELEFT, hInst, NULL);

		CreateWindow(
			L"BUTTON", L"┼",
			WS_CHILD | WS_VISIBLE,
			25, 25, 25, 25,
			hWnd, (HMENU)IDC_BUTTON_MIDDLECENTER, hInst, NULL);

		CreateWindow(
			L"BUTTON", L"→",
			WS_CHILD | WS_VISIBLE,
			50, 25, 25, 25,
			hWnd, (HMENU)IDC_BUTTON_MIDDLERIGHT, hInst, NULL);

		//

		CreateWindow(
			L"BUTTON", L"└",
			WS_CHILD | WS_VISIBLE,
			0, 50, 25, 25,
			hWnd, (HMENU)IDC_BUTTON_BOTTOMLEFT, hInst, NULL);

		CreateWindow(
			L"BUTTON", L"↓",
			WS_CHILD | WS_VISIBLE,
			25, 50, 25, 25,
			hWnd, (HMENU)IDC_BUTTON_BOTTOMCENTER, hInst, NULL);

		CreateWindow(
			L"BUTTON", L"┘",
			WS_CHILD | WS_VISIBLE,
			50, 50, 25, 25,
			hWnd, (HMENU)IDC_BUTTON_BOTTOMRIGHT, hInst, NULL);

		//デスクトップサイズを取得
		GetWindowRect(GetDesktopWindow(), &rtScreen);
		break;

	case WM_COMMAND: //コントロールの操作
		switch (LOWORD(wParam))
		{
		case IDC_BUTTON_TOPLEFT: //左上
			SetWindowPos(hWnd, 0, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOZORDER);
			break;

		case IDC_BUTTON_TOPCENTER: //中上
			GetWindowRect(hWnd, &rt);
			w = rt.right - rt.left;
			x = rtScreen.right / 2 - w / 2;
			y = 0;
			SetWindowPos(hWnd, 0, x, y, 0, 0, SWP_NOSIZE | SWP_NOZORDER);
			break;

		case IDC_BUTTON_TOPRIGHT: //右上
			GetWindowRect(hWnd, &rt);
			w = rt.right - rt.left;
			x = rtScreen.right - w;
			y = 0;
			SetWindowPos(hWnd, 0, x, y, 0, 0, SWP_NOSIZE | SWP_NOZORDER);
			break;

		case IDC_BUTTON_MIDDLELEFT: //左中
			GetWindowRect(hWnd, &rt);
			h = rt.bottom - rt.top;
			x = 0;
			y = rtScreen.bottom / 2 - h / 2;
			SetWindowPos(hWnd, 0, x, y, 0, 0, SWP_NOSIZE | SWP_NOZORDER);
			break;

		case IDC_BUTTON_MIDDLECENTER: //中中
			GetWindowRect(hWnd, &rt);
			w = rt.right - rt.left;
			h = rt.bottom - rt.top;
			x = rtScreen.right / 2 - w / 2;
			y = rtScreen.bottom / 2 - h / 2;
			SetWindowPos(hWnd, 0, x, y, 0, 0, SWP_NOSIZE | SWP_NOZORDER);
			break;

		case IDC_BUTTON_MIDDLERIGHT: //右中
			GetWindowRect(hWnd, &rt);
			w = rt.right - rt.left;
			h = rt.bottom - rt.top;
			x = rtScreen.right - w;
			y = rtScreen.bottom / 2 - h / 2;
			SetWindowPos(hWnd, 0, x, y, 0, 0, SWP_NOSIZE | SWP_NOZORDER);
			break;

		case IDC_BUTTON_BOTTOMLEFT: //左下
			GetWindowRect(hWnd, &rt);
			h = rt.bottom - rt.top;
			x = 0;
			y = rtScreen.bottom - h;
			SetWindowPos(hWnd, 0, x, y, 0, 0, SWP_NOSIZE | SWP_NOZORDER);
			break;

		case IDC_BUTTON_BOTTOMCENTER: //中下
			GetWindowRect(hWnd, &rt);
			w = rt.right - rt.left;
			h = rt.bottom - rt.top;
			x = rtScreen.right / 2 - w / 2;
			y = rtScreen.bottom - h;
			SetWindowPos(hWnd, 0, x, y, 0, 0, SWP_NOSIZE | SWP_NOZORDER);
			break;

		case IDC_BUTTON_BOTTOMRIGHT: //右下
			GetWindowRect(hWnd, &rt);
			w = rt.right - rt.left;
			h = rt.bottom - rt.top;
			x = rtScreen.right - w;
			y = rtScreen.bottom - h;
			SetWindowPos(hWnd, 0, x, y, 0, 0, SWP_NOSIZE | SWP_NOZORDER);
			break;
		}
		break;

	case WM_DESTROY: //ウィンドウの破棄
		PostQuitMessage(0);
		break;

	default:
		return DefWindowProc(hWnd, message, wParam, lParam);
	}
	return 0;
}

ウィンドウの位置変更のサンプル

やたらと長いコードですが、似たような処理をボタン9個分書いているだけです。
左上のボタンを押下すればウィンドウはデスクトップの左上に配置されます。
中央のボタンを押下すれば画面中央に配置されます。

ウィンドウを中央に配置するには、まず配置の基準となるウィンドウ(今回はデスクトップ)の位置とサイズを取得します。
次に自身のウィンドウの位置とサイズを取得します。

ウィンドウの左位置(X座標)は「(親ウィンドウの横幅 / 2) - (自ウィンドウの横幅 / 2)」で求めることができます。
上位置(Y座標)は「(親ウィンドウの縦幅 / 2) - (自ウィンドウの縦幅 / 2)」です。
以下の図を見るとわかりやすいかもしれません。
ウィンドウを中央に配置する時の計算方法

今回はデスクトップが基準なので座標は(0, 0)ですが、別のウィンドウを基準とする場合は最後に基準ウィンドウの座標をプラスする必要があります。