3
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

[Win32 API] タスクバーに表示されるウィンドウの一覧取得 (完全版?)

Posted at

ウィンドウの一覧を取得した場合だけでなく、特定のウィンドウを見つけたい場合にも使えます。

既に WEB 上に似たような記事がありますが、自分が確認したものだと、実際にタスクバーに表示されるものと完全には一致しなかったため、改めて調べました。

0. 要点

  • EnumWindows() 関数で (可視でないものも含めて) ウィンドウの一覧を取得
  • ウィンドウスタイルと拡張ウィンドウスタイルを確認し、タスクバーに表示されるウィンドウか確認
  • オーナー付きウィンドウは原則タスクバー非表示
    • ただし、オーナー付きウィンドウでもタスクバーに表示される条件が存在する

1. コード

※ここでは文字列をすべて Unicode で扱うことにします。
※ここでは WSL 上の MinGW-w64 でコンパイルすることを想定しています。

main.cpp
#include <windows.h>
#include <iostream>
#include <fcntl.h>

// 
#define MAX_WINDOW_CLASS_NAME_LENGTH 256 // NULL 文字終端込み

BOOL CALLBACK enumWindowsProc(HWND hWnd, LPARAM lParam);
bool isWindowAppearingInTaskbar(HWND hWnd, DWORD dwStyle, DWORD dwExStyle);
void printTitle(HWND hWnd);

// 
int wmain() {

	// 出力の文字コード指定
	_setmode(fileno(stdout), _O_U8TEXT);
	_setmode(fileno(stderr), _O_U8TEXT);

	// 
	if ( ! EnumWindows(enumWindowsProc, 0) ) {
		std::wcerr << L"EnumWindows Error: " << GetLastError() << std::endl;
		return -1;
	}

	std::wcout << std::flush;

	return 0;

}

BOOL CALLBACK enumWindowsProc(HWND hWnd, LPARAM lParam) {

	// ウィンドウスタイル
	const DWORD dwStyle   = (DWORD) GetWindowLongW(hWnd, GWL_STYLE);
	const DWORD dwExStyle = (DWORD) GetWindowLongW(hWnd, GWL_EXSTYLE);

	// クラス名
	WCHAR lpszClassName[MAX_WINDOW_CLASS_NAME_LENGTH];

	if ( ! GetClassNameW(hWnd, lpszClassName, MAX_WINDOW_CLASS_NAME_LENGTH) ) {
		std::wcerr << L"GetClassNameW Error: " << GetLastError() << std::endl;
		return TRUE;
	}

	// タスクバーの一覧に表示されるウィンドウか確認する
	if ( ! isWindowAppearingInTaskbar(hWnd, dwStyle, dwExStyle) ) return TRUE;

	// タイトル
	printTitle(hWnd);

	// ウィンドウハンドル
	std::wcout << L"Window Handle: " << hWnd << L"\n";

	// クラス名
	std::wcout << L"Class Name   : " << lpszClassName << L"\n";

	std::wcout << L"\n";

	return TRUE;

}

/**
 * タスクバーの一覧に表示されるウィンドウか確認する
 */
bool isWindowAppearingInTaskbar(HWND hWnd, DWORD dwStyle, DWORD dwExStyle) {

	// 
	if ( ! (dwStyle & WS_VISIBLE) ) return false;

	if ( dwExStyle & WS_EX_TOOLWINDOW ) return false;

#if ( WINVER >= 0x0602 )
	if ( dwExStyle & WS_EX_NOREDIRECTIONBITMAP ) return false;
#endif

	// オーナー付きウィンドウの場合
	const HWND hOwnerWnd = GetWindow(hWnd, GW_OWNER);

	if ( NULL != hOwnerWnd && (! (dwExStyle & WS_EX_APPWINDOW) || IsIconic(hOwnerWnd)) ) return false;

	return true;

}

void printTitle(HWND hWnd) {

	const int length = GetWindowTextLengthW(hWnd) + 1; // NULL 文字終端込み

	WCHAR lpszTitle[length];

	GetWindowTextW(hWnd, lpszTitle, length);

	// 
	std::wcout << L"Title        : " << lpszTitle << L"\n";

}
build.sh
#!/bin/bash

# g++ ver.5.1 以降を想定
x86_64-w64-mingw32-g++ -Wall -std=c++14 \
	-finput-charset=UTF-8 -fexec-charset=CP932 \
	-municode \
	-static-libgcc -static-libstdc++ \
	-DWINVER=0x602 \
	-o main.exe \
	main.cpp

./build.sh でビルドできます。

※ここでは WINVER の設定が必要です。
※コンパイルオプションは一例です。
※ここでは簡単のため、シェルスクリプトでビルドすることにします。
※規模が大きいプロジェクトの場合は、何らかのビルドツールを利用した方が良いです。

2. タスクバーにウィンドウが表示される条件について

※「親ウィンドウと子ウィンドウ」は「ウィンドウとボタン」のような関係で、「オーナーウィンドウとオーナー付きウィンドウ」は「ウィンドウとダイアログ」のような関係です。

  • ウィンドウスタイルに WS_VISIBLE が含まれる、または IsWindowVisible(hWnd) が真である
  • 拡張ウィンドウスタイルに WS_EX_TOOLWINDOW が含まれない
  • 拡張ウィンドウスタイルに WS_EX_NOREDIRECTIONBITMAP が含まれない (Windows 8 以降)
  • オーナー付きウィンドウは原則非表示
    • ただし、オーナー付きウィンドウの拡張ウィンドウスタイルに WS_EX_APPWINDOW が含まれ、オーナーウィンドウが最小化 (IsIconic(hOwnerWnd) で確認可) されていない場合は表示

WS_EX_NOREDIRECTIONBITMAP は Windows 8 以降の拡張ウィンドウスタイルのため、コンパイル時に WINVER0x602 以降に設定します。

参考「Owned Windows - Window Features - Win32 apps | Microsoft Docs」(オーナー付きウィンドウ)
参考「Window Visibility - Window Features - Win32 apps | Microsoft Docs」(ウィンドウ表示)
参考「Extended Window Styles (Winuser.h) - Win32 apps | Microsoft Docs」(拡張ウィンドウスタイル WS_EX_TOOLWINDOW, WS_EX_APPWINDOW, WS_EX_NOREDIRECTIONBITMAP)
参考「What's with those blank taskbar buttons that go away when I click on them? | The Old New Thing」(タスクバーに表示されるウィンドウについて)
参考「Update WINVER and _WIN32_WINNT | Microsoft Docs」(WINVER)

3. その他注意点など

3.1. ウィンドウクラス名を取得する際のバッファサイズについて

ウィンドウクラスを登録する際の (NULL 文字終端を含めた) 長さの最大値は 256 と定められているため、256 に設定します。

参考「WNDCLASSW (winuser.h) - Win32 apps | Microsoft Docs」(lpszClassName)

3.2. ウィンドウタイトルを取得する際のバッファサイズについて

GetWindowTextLengthW() 関数でウィンドウタイトルの長さを取得できるため、そこからバッファサイズを決めるのが安全です。

3.3. 実行ファイル名を取得したい場合

別記事にしました。

参考「[Win32 API] ウィンドウハンドルから実行ファイル名取得と、その注意点 - Qiita

3
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?