ウィンドウの列挙


起動中のウィンドウを知る

プログラムは、現在デスクトップにどのようなウィンドウがあるのかを知り
そして、そのウィンドウを制御するためのハンドルを必要とするかもしれません

もちろん、必要な時以外には
異なるプログラムのウィンドウを制御するようなことは避けるべきですが
プログラムによっては、その必要に迫られる時もあることでしょう

Windows は、げんざい有効なウィンドウのハンドルを列挙する
EnumWindows() 関数を提供しています
これを用いれば、有効なメインウィンドウのハンドルを全て取得できます

BOOL EnumWindows(WNDENUMPROC lpEnumFunc , LPARAM lParam);

lpEnumFunc は、WNDENUMPROC 型の関数へのポインタを指定します
これは、アプリケーション定義のコールバック関数へのポインタです
lParam には、列挙時にコールバック関数に送る追加情報を指定します
関数が成功すれば 0 以外、失敗すれば 0 が返ります

WNDENUMPROC 型の関数は、ウィンドウの列挙時に
それぞれのウィンドウのハンドルを順に受けるコールバック関数です

BOOL CALLBACK EnumWindowsProc(HWND hwnd , LPARAM lParam);

hwnd には親を持たないウィンドウののハンドルが渡されます
lParam は EnumWindows の lParam 引数で指定した値が格納されています
列挙を続ける時は TRUE、中断する時は FALSE を返します
#include <windows.h>

BOOL CALLBACK EnumWindowsProc(HWND hwnd , LPARAM lp) {
	TCHAR strWindowText[1024];
	GetWindowText(hwnd , strWindowText , 1024);
	if (strWindowText[0] == 0) return TRUE;

	SendMessage((HWND)lp , LB_ADDSTRING , 0 , (long)strWindowText);
	return TRUE;
}

LRESULT CALLBACK WndProc(HWND hWnd , UINT msg , WPARAM wp , LPARAM lp) {
	HDC hdc;
	PAINTSTRUCT ps;
	static HWND hList;

	switch (msg) {
	case WM_DESTROY:
		PostQuitMessage(0);
		return 0;
	case WM_CREATE:
		hList = CreateWindow(
			TEXT("LISTBOX") , NULL , 
			WS_CHILD | WS_VISIBLE | LBS_STANDARD , 
			0 , 0 , 400 , 100 , hWnd , (HMENU)1 ,
			((LPCREATESTRUCT)(lp))->hInstance , NULL
		);
		EnumWindows(EnumWindowsProc , (LPARAM)hList);
	}
	return DefWindowProc(hWnd , msg , wp , lp);
}

int WINAPI WinMain(HINSTANCE hInstance , HINSTANCE hPrevInstance ,
			PSTR lpCmdLine , int nCmdShow) {
	HWND hWnd;
	MSG msg;
	WNDCLASS winc;

	winc.style 		= CS_HREDRAW | CS_VREDRAW;
	winc.lpfnWndProc	= WndProc;
	winc.cbClsExtra	= winc.cbWndExtra	= 0;
	winc.hInstance		= hInstance;
	winc.hIcon		= LoadIcon(NULL , IDI_APPLICATION);
	winc.hCursor		= LoadCursor(NULL , IDC_ARROW);
	winc.hbrBackground	= GetStockObject(WHITE_BRUSH);
	winc.lpszMenuName	= NULL;
	winc.lpszClassName	= TEXT("KITTY");

	if (!RegisterClass(&winc)) return 1;

	hWnd = CreateWindow(
		TEXT("KITTY") , TEXT("Kitty on your lap") ,
		WS_OVERLAPPEDWINDOW | WS_VISIBLE ,
		CW_USEDEFAULT , CW_USEDEFAULT ,
		CW_USEDEFAULT , CW_USEDEFAULT ,
		NULL , NULL , hInstance , NULL
	);
	if (hWnd == NULL) return 1;

	while (GetMessage(&msg , NULL , 0 , 0 )) {
		TranslateMessage(&msg);
		DispatchMessage(&msg);
	}

	return msg.wParam;
}


このプログラムは、親ウィンドウを持たないトップウィンドウを列挙関数で取得し
リストボックスにウィンドウテキストを表示します


子ウィンドウを列挙する

上で紹介したウィンドウの列挙はトップレベルウィンドウでした
しかし、場合によってはあるウィンドウの子ウィンドウを取得したいと思うかもしれません
とくに MDI ウィンドウの場合は必要になることが多いでしょう

例えば、MDI アプリケーションでいくつかの MDI 子ウィンドウが開かれている時
ユーザがおもむろにフレームウィンドウの閉じるボタンを押すかもしれません
MDI 子ウィンドウが処理していたデータが何の許可もなく消えるのは健全ではありません
この時は、各 MDI 子ウィンドウがダイアログを表示してユーザーに保存を促すべきです

これを実現するには EnumChildWindows() 関数がスマートです
この関数は、指定したウィンドウの子ウィンドウを列挙します
BOOL EnumChildWindows(
	HWND hWndParent
	WNDENUMPROC lpEnumFunc
	LPARAM lParam
);
hWndParent は子ウィンドウを持つ親ウィンドウを指定します
lpEnumfunc は列挙した子ウィンドウのハンドルを受け取る関数のポインタ
lParam は lpEnumfunc への追加情報を指定します
関数が成功すると 0 以外、失敗すると 0 が返ります

要領は先ほどのウィンドウの列挙と同じです
コールバック関数の型も EnumWindows() と同じです

BOOL CALLBACK EnumChildProc(HWND hwnd , LPARAM lParam);

関数の名前はプレースホルダーであり、関数名を変えてもかまわないので
型はまったく同じです
//resource.h
#define IDM_EXIT 0x1001
#define IDM_NEW 0x2001
#define IDM_ALLDEL
//リソーススクリプト
#include "resource.h"

KITTY MENU {
	POPUP "File(&F)" {
		MENUITEM "終了(&X)" , IDM_EXIT
	}
	POPUP "Window(&W)" {
		MENUITEM "新規作成(&N)" , IDM_NEW
		MENUITEM "全て閉じる(&A)" , IDM_ALLDEL
	}
}
#include <windows.h>
#include "resource.h"

#define TITLE TEXT("Kitty on your lap")
#define MDI_FRAME TEXT("FRAMEWINDOW")
#define MDI_CHILD TEXT("MDICHILD")

#define ID_CHILDWND 0x00010000

HINSTANCE hIns;
HWND hClient;

BOOL CALLBACK EnumChildProc(HWND hWnd , LPARAM lp) {
	SendMessage(hWnd , WM_CLOSE , 0 , 0);
	return TRUE;
}

LRESULT CALLBACK MDIProc(HWND hWnd , UINT msg , WPARAM wp , LPARAM lp) {
	switch (msg) {
	case WM_CLOSE:
		if (MessageBox(hWnd , TEXT("ウィンドウを閉じますか?") ,
				TEXT("選択してください") , MB_YESNO) == IDNO)
			return 0;
	}
	return DefMDIChildProc(hWnd , msg , wp , lp);
}

LRESULT CALLBACK FrameProc(HWND hWnd , UINT msg , WPARAM wp , LPARAM lp) {
	HDC hdc;
	PAINTSTRUCT ps;
	CLIENTCREATESTRUCT ccsClient;

	switch (msg) {
	case WM_DESTROY:
		PostQuitMessage(0);
		return 0;
	case WM_CREATE:
		ccsClient.hWindowMenu = GetSubMenu(GetMenu(hWnd) , 1);
		ccsClient.idFirstChild = ID_CHILDWND;

		hClient = CreateWindow(TEXT("MDICLIENT") , NULL ,
			WS_CHILD | WS_CLIPCHILDREN | WS_VISIBLE ,
			0 , 0 , 0 , 0 , hWnd , (HMENU)1 , hIns , &ccsClient
		);
		return 0;
	case WM_COMMAND:
		switch(LOWORD(wp)) {
		case IDM_EXIT:
			SendMessage(hWnd , WM_CLOSE , 0 , 0);
			return 0;
		case IDM_NEW:
			CreateMDIWindow(MDI_CHILD , TITLE , 0 ,
				CW_USEDEFAULT , CW_USEDEFAULT ,
				CW_USEDEFAULT , CW_USEDEFAULT ,
				hClient , hIns , 0
			);
			return 0;
		case IDM_ALLDEL:
			EnumChildWindows(hClient , EnumChildProc , 0);
			return 0;
		}
	}
	return DefFrameProc(hWnd , hClient , msg , wp , lp);
}

int WINAPI WinMain(HINSTANCE hInstance , HINSTANCE hPrevInstance ,
			PSTR lpCmdLine , int nCmdShow) {
	HWND hWnd;
	MSG msg;
	WNDCLASS winc;

	hIns = hInstance;

	winc.style 		= CS_HREDRAW | CS_VREDRAW;
	winc.lpfnWndProc	= FrameProc;
	winc.cbClsExtra	= winc.cbWndExtra	= 0;
	winc.hInstance		= hInstance;
	winc.hIcon		= LoadIcon(NULL , IDI_APPLICATION);
	winc.hCursor		= LoadCursor(NULL , IDC_ARROW);
	winc.hbrBackground	= (HBRUSH)(COLOR_APPWORKSPACE + 1);
	winc.lpszMenuName	= TEXT("KITTY");
	winc.lpszClassName	= MDI_FRAME;

	if (!RegisterClass(&winc)) return 1;

	winc.lpfnWndProc		= MDIProc;
	winc.hbrBackground	= (HBRUSH)GetStockObject(WHITE_BRUSH);
	winc.lpszClassName	= MDI_CHILD;

	if (!RegisterClass(&winc)) return 1;

	hWnd = CreateWindow(
			MDI_FRAME , TITLE ,
			WS_OVERLAPPEDWINDOW | WS_VISIBLE ,
			CW_USEDEFAULT , CW_USEDEFAULT ,
			CW_USEDEFAULT , CW_USEDEFAULT ,
			NULL , NULL , hInstance , NULL
	);

	if (hWnd == NULL) return 1;

	while (GetMessage(&msg , NULL , 0 , 0 )) {
		if (!TranslateMDISysAccel(hClient , &msg)) {
			TranslateMessage(&msg);
			DispatchMessage(&msg);
		}
	}

	return msg.wParam;
}
このプログラムのメニューには、「すべて閉じる」という項目があります
これをクリックすれば、表示されている MDI 子ウィンドウを全て閉じることができますが
全ての MDI 子ウィンドウは、閉じる前にダイアログで確認してきます

プログラムは「すべて閉じる」メニュー項目を選択すると
EnumChildWindows() 関数を呼び出して、MDI クライアントウィンドウの子を列挙します
コールバック関数は、渡されたウィンドウのハンドルに WM_CLOSE を送信する仕組みです


EnumWindows()

BOOL EnumWindows(WNDENUMPROC lpEnumFunc , LPARAM lParam);

トップレベルのウィンドウを列挙します

lpEnumFunc - ユーザー定義コールバック関数へのポインタを指定します
lParam - コールバック関数に渡す追加情報を指定します

戻り値 - 関数が成功すれば 0 以外、失敗すれば 0

EnumWindowsProc()

BOOL CALLBACK EnumWindowsProc(HWND hwnd , LPARAM lParam);

EnumWindows() で使用するアプリケーション定義のコールバック関数です

hwnd - ウィンドウのハンドルを指定します
lParam - 追加情報を指定します

戻り値 - 列挙を続ける時は TRUE、そうでなければ FALSE

EnumChildWindows()

BOOL EnumChildWindows(
	HWND hWndParent
	WNDENUMPROC lpEnumFunc
	LPARAM lParam
);
指定した親ウィンドウの子ウィンドウを列挙します

hWndParent - 子ウィンドウを持つ親ウィンドウを指定します
lpEnumFunc - ユーザー定義コールバック関数へのポインタを指定します
lParam - コールバック関数に渡す追加情報を指定します

戻り値 - 関数が成功すれば 0 以外、失敗すれば 0

EnumChildProc()

BOOL CALLBACK EnumWindowsProc(HWND hwnd , LPARAM lParam);

EnumChildWindows() で使用するアプリケーション定義のコールバック関数です

hwnd - 子ウィンドウのハンドルを指定します
lParam - 追加情報を指定します

戻り値 - 列挙を続ける時は TRUE、そうでなければ FALSE



前のページへ戻る次のページへ