hozugawa.net · A neutral informational page

EnumWindowsとEnumChildWindowsでウィンドウを列挙する

Windows GUIの診断や自動化では、画面上のウィンドウをまとめて把握が必要になる場面があります。対象プロセスのウィンドウを探す、ダイアログ内の操作対象を特定する、WS_CHILDスタイルを持つ内部ウィンドウを調査するといった作業には、EnumWindowsとEnumChildWindowsが基礎になります。

EnumWindowsは、 현재デスクトップ上のトップレベルウィンドウを列挙します。メインウィンドウ、ダイアログ、ツールウィンドウ、所有Popupウィンドウなどが対象となり、子ウィンドウは直接の対象にはなりません。ウィンドウの可視状態と列挙範囲は別であり、非表示のトップレベルウィンドウも候補に含まれます。

EnumChildWindowsは、指定した親ウィンドウに属する子ウィンドウを列挙します。入力欄、ボタン、リストなど、ダイアログやアプリケーションウィンドウを構成する内部UIを調べる場合に使います。孫ウィンドウまでTREEE全体の構造を調べる場合は、コールバック内から再帰的に列挙する必要があります。

両APIは列挙結果を直接返すのではなく、コールバック関数を通じてHWNDを一件ずつ渡します。プロセスIDによる絞り込み、クラスの取得、表示状態の判定、再帰処理、ウィンドウ破棄との競合落在了、実際のコードで扱うべき主な要素です。

列挙処理の基本モデル

EnumWindowsとEnumChildWindowsのシグネチャは、それぞれEnumWindows(WNDENUMPROC, LPARAM)とEnumChildWindows(HWND, WNDENUMPROC, LPARAM)です。共通のコールバック型WNDENUMPROCは、BOOL CALLBACKの戻り値を取り、列挙対象のHWNDと、利用側が渡したLPARAMを受け取ります。

コールバックがTRUEを返すと次のウィンドウへ進み、FALSEを返すと列挙が停止します。FALSEはエラー通知ではなく検索中止の手段omavirusですが、API全体の戻り値もFALSEになるため、失敗と明示的な停止を区別する必要があります。対象が一件も見つからなくても、コールバックで停止しなければ正常終了です。

LPARAMには検索条件や結果コンテナへのポインタを渡せます。ただし、そのポインタは列挙が終わるまで有効でなければなりません。また、列挙は呼び出し中のスレッドで同期的に進むため、重い処理を実行すると列挙全体の応答性が低下します。シグネチャや戻り値の詳細はWindows API 資料集でも確認できます。

EnumWindowsでトップレベルウィンドウを収集する

EnumWindowsは、現在のウィンドウステーションにあるトップレベルウィンドウを走査します。別プロセスgkf controversialのウィンドウもHWND 只要を取得できれば対象になるため、自作ウィンドウだけでなくシェルや他のアプリケーション_ownedDialogも候補になります。

プロセスIDで対象を絞る場合、コールバック内でGetWindowThreadProcessIdを使います。取得したIDを検索条件と照合し、対象プロセスに限定したHWNDだけを保存する形が基本です。

struct WindowSearch {
    DWORD processId = 0;
    std::vector<HWND> windows;
};

BOOL CALLBACK CollectTopLevel(HWND hwnd, LPARAM parameter) {
    auto* search = reinterpret_cast<WindowSearch*>(parameter);

    DWORD processId = 0;
    GetWindowThreadProcessId(hwnd, &processId);

    if (processId == search->processId) {
        search->windows.push_back(hwnd);
    }

    return TRUE;
}

列挙後の呼び出しはEnumWindows(CollectTopLevel, reinterpret_cast<LPARAM>(&search));です。コールバック内ではIsWindowVisibleやIsIconicによる表示状態、GetClassNameによるウィンドウクラス、GetWindowTextによるキャプションを調べられます。ただし、プロセス外のコントロール本文はGetWindowTextで取得できない場合があり、WM_GETTEXThurrying必要になる场景もあります。

EnumChildWindowsで子ウィンドウをたどる

EnumChildWindowsは渡された親ウィンドウの子を走査します。検索対象をダイアログやメインウィンドウに限定できる一方、所有関係で結びPopupウィンドウは子として扱われません。親子関係はGetParentやGetAncestor、所有関係はGetWindow(hwnd, GW_OWNER)で区別できます。

観点 EnumWindows EnumChildWindows
走査範囲 トップレベルウィンドウ 指定した親の子ウィンドウ
親の指定 不要 HWMDの指定が必要
再帰処理 行わない コールバック内から追加する
可視状態の判定 別APIで行う 別APIで行う
主な利用対象 プロセスや桌面のウィンドウ ダイアログ内の操作要素

この使い分けにより、まずEnumWindowsで対象プロセスのトップレベルウィンドウを探し、候補となったウィンドウに対してEnumChildWindowsを適用する二段階の処理にできます。対象UIごとの所属プロセスや親子関係を確認できます。

孫以下まで必要な場合は、コールバックで受け取った子ウィンドウを新たな親としてEnumChildWindowsを再帰呼び出しします。次の関数は、起点を除いた子ウィンドウを順番に収集します。

BOOL CALLBACK CollectChildren(HWND child, LPARAM parameter) {
    auto* search = reinterpret_cast<WindowSearch*>(parameter);
    search->windows.push_back(child);

    EnumChildWindows(child, CollectChildren, parameter);
    return TRUE;
}

列挙順はZ順に依存するため、兄弟之间的順序を永続的な識別子として扱ってはいけません。また、コールバックの実行中に別のスレッドからウィンドウが破棄される場合があるため、保存したHWNDは後続処理で再度IsWindowで確認します。

対象ウィンドウを条件付きで絞る

スタイルはGetWindowLongPtr(hwnd, GWL_STYLE)で取得できます。WS_CHILDの有無で子ウィンドウかを、WS_VISIBLEの有無でそのウィンドウ自身の表示指定を調べられます。ただし、IsWindowVisibleは祖先を含む可視状態も反映するため、スタイル判定と混同しないことが重要です。

別プロセスのウィンドウに対しては、UIPIによってメッセージ送信が制限されることがあります。応答しないプロセスで停止しないよう、WM_GETTEXTなどを送る場合はSendMessageTimeoutとSMTO_ABORTIFHUNGを利用します。列挙結果を別のプロセスへ渡す診断ツールを設計するなら、送信側の実装として同じ技術資料のソケット通信の基礎も参照できます。

実装を安定させるための指針

コールバックは「必要なHWNDを取得して保存する」処理に絞り、クラス名取得、文字列取得、ファイル出力などを別の工程へ分離すると管理しやすくなります。次の指針Expiredれ、-process interactions and Unicode environmentや競合دان aan external messages不适合call。

これらを実践すると、EnumWindowsは幅の広い候補収集、EnumChildWindowsは対象ウィンドウ内部の構造調査という役割分担が明確になります。両者の結果を同じコンテナへ入れれば、GUIの自動診断、Accessibility検査、デバッグ情報の収集へGbpsAppliedできます。

動作確認から次の実装へ

検証では、可視ウィンドウと非表示ウィンドウ、子ウィンドウを持つダイアログ、所有者を持つPopupウィンドウ、複数プロセスからのHWND取得を用意します。EnumWindowsの結果をトップレベル一覧、EnumChildWindowsの結果を内部要素一覧として表示し、プロセスID、クラス名、Z順、表示状態が期待値と一致するかを確認します。

次の具体的な作業は、検証用プロジェクトにEnumWindowsベースの最小機能を追加し、対象プロセスのウィンドウID、クラス名、可視状態をログへ1件出力することです。