ウィンドウの一覧を取得した場合だけでなく、特定のウィンドウを見つけたい場合にも使えます。
既に WEB 上に似たような記事がありますが、自分が確認したものだと、実際にタスクバーに表示されるものと完全には一致しなかったため、改めて調べました。
0. 要点
-
EnumWindows()
関数で (可視でないものも含めて) ウィンドウの一覧を取得 - ウィンドウスタイルと拡張ウィンドウスタイルを確認し、タスクバーに表示されるウィンドウか確認
- オーナー付きウィンドウは原則タスクバー非表示
- ただし、オーナー付きウィンドウでもタスクバーに表示される条件が存在する
1. コード
※ここでは文字列をすべて Unicode で扱うことにします。
※ここでは WSL 上の MinGW-w64 でコンパイルすることを想定しています。
#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";
}
#!/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 以降の拡張ウィンドウスタイルのため、コンパイル時に WINVER
を 0x602
以降に設定します。
参考「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. 実行ファイル名を取得したい場合
別記事にしました。