2

この記事は最終更新日から1年以上が経過しています。

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

投稿日

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

既に 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

新規登録して、もっと便利にQiitaを使ってみよう

  1. あなたにマッチした記事をお届けします
  2. 便利な情報をあとで効率的に読み返せます
ログインすると使える機能について

コメント

この記事にコメントはありません。
あなたもコメントしてみませんか :)
新規登録
すでにアカウントを持っている方はログイン
2

Qiitaにログインしてダークテーマを使ってみませんか?🌙

ログインするとOSの設定にあわせたテーマカラーを使用できます!