web-dev-qa-db-ja.com

Windows:表示されているすべてのウィンドウのリストを取得する方法は?

(つまり、関連するテクノロジーでタグを付け直してください:それらがどれであるかわかりません:)

特定の詳細について、後でもっと詳細な質問をするでしょうが、ここでは「全体像」を把握しようとしています。Windowsで「実際に表示されるウィンドウ」を列挙する方法を探しています。 「実際に見えるウィンドウ」とは、ユーザーが「ウィンドウ」と呼ぶものを意味します。これらすべての可視ウィンドウのリストをZオーダーで取得する方法が必要です。

私がdoを実行する必要があることに注意してください。私はすでにOS Xでそれを行っており(特にOS X 10.4をサポートしたい場合、OS Xには便利なWindows APIがないため、本当に頭痛の種です)、Windowsで実行する必要があります。

次に例を示します。次のように、画面に3つのウィンドウが表示されているとします。

 +------------------------------------------+
 |                                          |
 |           +=============+                |
 |           |             |                |
 |           |    A   +--------------------------+
 |           |        |                          |
 |    C      |        |             B            |
 |           |        +--------------------------+
 |           |             |                |
 +-----------|             |----------------+
             |             |
             +-------------+

次に、次のようなリストを取得する必要があります。

 windows B is at (210,40)
 windows A is at (120,20)
 windows C is at (0,0)

次に、ユーザー(またはOS)がウィンドウAを前面に持ってくると、次のようになります。

 +------------------------------------------+
 |                                          |
 |           +=============+                |
 |           |             |                |
 |           |    A        |---------------------+
 |           |             |                     |
 |    C      |             |        B            |
 |           |             |---------------------+
 |           |             |                |
 +-----------|             |----------------+
             |             |
             +-------------+

そして(理想的には)これを与えるコールバックを受け取ります:

windows A is at (120,20)
windows B is at (210,40)
windows C is at (0,0)

OS Xでこれを行うには、驚くほど奇妙なハックを使用する必要があります(ユーザーにオンにするように強制する「支援デバイスのアクセスを有効にする」!)などですが、私はOS Xでそれを実行し、動作します(OS Xでは、ウィンドウの変更が発生するたびにコールバックを取得できなかったため、ポーリングしていますが、動作しました)。

今、私はこれをWindowsで実行したいと思っています(私は本当にそうです、それについての質問はありません)。

  • これはできますか?

  • それを可能にする十分に文書化されたWindows API(およびその仕様に従って動作)はありますか?

  • ウィンドウが変更されるたびにコールバックを登録するのは簡単ですか? (サイズ変更、移動、背面/前面に移動した場合、または新しいウィンドウがポップアップした場合など)

  • 落とし穴は何でしょうか?

私はこの質問が具体的ではないことを知っています。そのため、私の問題を可能な限り明確に説明しようとしました(ニースASCIIこれに賛成できるアートを含む):今のところ私は「全体像」を見るWindowsでそのようなことを行うとどうなるのか知りたい。

おまけの質問:小さな。exeを書き込んで、ウィンドウが変更されるたびに一時ファイルに一時ファイルに書き込む必要があると想像してください。画面では、そのようなプログラムはおおよそ選択した言語でどれくらいの期間続きますか。

(もう一度、私は「全体像」を取得して、ここで何が機能しているかを理解しようとしています)

43
NoozNooz42

トップレベルウィンドウを列挙するには、GetTopWindow/GetNextWindowではなく EnumWindows を使用する必要があります。これは、EnumWindowsがウィンドウの状態の一貫したビューを返すためです。ウィンドウが反復中にzオーダーを変更すると、一貫性のない情報(削除されたウィンドウに関するレポートなど)またはGetTopWindow/GetNextWindowを使用した無限ループが発生する危険があります。

EnumWindowsはコールバックを使用します。コールバックの各呼び出しで、ウィンドウハンドルを取得します。ウィンドウの画面座標は、そのハンドルを GetWindowRect に渡すことで取得できます。コールバックは、ウィンドウの位置のリストをzオーダーで作成します。

ポーリングを使用して、ウィンドウリストを繰り返し作成できます。または、ウィンドウの変更の通知を受け取るようにCBTHookを設定します。すべてのCBT通知がトップレベルウィンドウの順序、位置、または可視性を変更するわけではないため、EnmWindowsを再実行して、ウィンドウの位置の新しいリストをzオーダーで作成し、これを前のリストと比較してから、リストをさらに処理してください。実際の変更が発生した場合にのみ、以降の処理が行われるようにします。

フッキングでは、32ビットと64ビットを混在させることはできません。 32ビットアプリを実行している場合は、32ビットプロセスから通知を受け取ります。 64ビットの場合も同様です。したがって、64ビットマシンでシステム全体を監視する場合、2つのアプリを実行する必要があるように見えます。私の推論はこれを読んでいます:

SetWindowsHookExは、DLLを別のプロセスに注入するために使用できます。32ビットDLLは、64ビットプロセスに注入できず、64ビットDLL 32ビットプロセスに注入することはできません。アプリケーションが他のプロセスでフックを使用する必要がある場合、32ビットアプリケーションが32ビットを注入するためにSetWindowsHookExを呼び出す必要があります= DLL= 32ビットプロセスに、64ビットアプリケーションが64ビットプロセスにSetWindowsHookExを呼び出して64ビットプロセスにインジェクトするDLL 64ビットDLLには異なる名前を付ける必要があります(SetWindowsHookEx APIページから)。

これをJavaで実装しているので、 [〜#〜] jna [〜#〜] を確認することをお勧めします。これにより、ネイティブライブラリへの書き込みがはるかに簡単になります(Javaでコードを呼び出す)。独自のネイティブJNI DLLが不要になります。

編集:あなたはそれがどれくらいのコードであり、どれだけ長く書くかを尋ねました。これがJavaのコードです

import com.Sun.jna.Native;
import com.Sun.jna.Structure;
import com.Sun.jna.win32.StdCallLibrary;

import Java.util.ArrayList;
import Java.util.Collections;
import Java.util.Comparator;
import Java.util.List;

public class Main {

    public static void main(String[] args) {
        Main m = new Main();
        final List<WindowInfo> inflList = new ArrayList<WindowInfo>();
        final List<Integer> order = new ArrayList<Integer>();
        int top = User32.instance.GetTopWindow(0);
        while (top != 0) {
            order.add(top);
            top = User32.instance.GetWindow(top, User32.GW_HWNDNEXT);
        }

        User32.instance.EnumWindows(new WndEnumProc() {
            public boolean callback(int hWnd, int lParam) {
                if (User32.instance.IsWindowVisible(hWnd)) {
                    RECT r = new RECT();
                    User32.instance.GetWindowRect(hWnd, r);
                    if (r.left > -32000) {     // If it's not minimized
                        byte[] buffer = new byte[1024];
                        User32.instance.GetWindowTextA(hWnd, buffer, buffer.length);
                        String title = Native.toString(buffer);
                        inflList.add(new WindowInfo(hWnd, r, title));
                    }
                }
                return true;
            }
        }, 0);

        Collections.sort(inflList, new Comparator<WindowInfo>() {
            public int compare(WindowInfo o1, WindowInfo o2) {
                return order.indexOf(o1.hwnd)-order.indexOf(o2.hwnd);
            }
        });
        for (WindowInfo w : inflList) {
            System.out.println(w);
        }
    }

    public static interface WndEnumProc extends StdCallLibrary.StdCallCallback {
        boolean callback(int hWnd, int lParam);
    }

    public static interface User32 extends StdCallLibrary {
        final User32 instance = (User32) Native.loadLibrary ("user32", User32.class);
        final int GW_HWNDNEXT = 2;

        boolean EnumWindows(WndEnumProc wndenumproc, int lParam);
        boolean IsWindowVisible(int hWnd);
        int GetWindowRect(int hWnd, RECT r);
        void GetWindowTextA(int hWnd, byte[] buffer, int buflen);
        int GetTopWindow(int hWnd);
        int GetWindow(int hWnd, int flag);
    }

    public static class RECT extends Structure {
        public int left, top, right, bottom;
    }

    public static class WindowInfo {
        public final int hwnd;
        public final RECT rect;
        public final String title;
        public WindowInfo(int hwnd, RECT rect, String title) {
            this.hwnd = hwnd;
            this.rect = rect;
            this.title = title;
        }

        public String toString() {
            return String.format("(%d,%d)-(%d,%d) : \"%s\"",
                rect.left, rect.top,
                rect.right, rect.bottom,
                title);
        }
    }
}

サンプルをコンパクトに保ち、すぐにコンパイルできるように貼り付けられるように、関連するクラスとインターフェイスの内部クラスのほとんどを作成しました。実際の実装では、それらは通常のトップレベルのクラスになります。コマンドラインアプリは、表示されているウィンドウとその位置を出力します。 32ビットのjv​​mと64ビットの両方で実行し、それぞれ同じ結果を得ました。

EDIT2:zオーダーを含むようにコードを更新しました。 GetNextWindowを使用します。運用アプリケーションでは、おそらく次の値と前の値に対してGetNextWindowを2回呼び出し、それらが一貫していて有効なウィンドウハンドルであることを確認する必要があります。

27
mdma

これはできますか?

はい。ただし、コールバックに関して必要なものを取得するには、 フック を登録する必要があります。おそらく CBTProc Callback Hook を使用する必要があります。これはいつでも呼び出されます。

ウィンドウのアクティブ化、作成、破棄、最小化、最大化、移動、またはサイズ変更。システムコマンドを完了する前。システムメッセージキューからマウスまたはキーボードイベントを削除する前。キーボードフォーカスを設定する前。またはシステムメッセージキューと同期する前

ただし、これらのフックはWin32ではなくカーネルのドメインであるため、コンソールウィンドウで機能するとは思わないことに注意してください。

それを可能にする十分に文書化されたWindows API(およびその仕様に従って動作)はありますか?

はい。 GetTopWindow および GetNextWindow 関数を使用して、デスクトップ上のすべてのウィンドウハンドルを正しいZ順序で取得できます。

ウィンドウが変更されるたびにコールバックを登録するのは簡単ですか? (サイズ変更、移動、背面/前面に移動した場合、または新しいウィンドウがポップアップした場合など)

最初の答えを見てください:)

落とし穴は何でしょうか?

最初の答えを見てください:)

おまけの質問:画面にウィンドウが変更されるたびに、ウィンドウの名前/位置/サイズを一時ファイルに書き込む小さな.exeを書き込む必要があると想像してください。あなたはそれを書く必要がありますか?

Cの数百行、および数時間。なんらかの形式のポーリングを使用する必要がありますが、自分の前でフックを実行したことはありません。フックが必要な場合は、多少時間がかかります。

7
Billy ONeal

私が2006年に思い出したのは、sysinternalsの一部としてWinObjユーティリティがあり、おそらくあなたが望んだことをしたということです。これらのユーティリティの一部は、作者(Mark Russinovich)によってソースコードとともに提供されました。

それ以来、彼の会社はマイクロソフトに買収されたため、ソースがまだ利用可能かどうかはわかりません。

また、次の点も確認する価値があります。

http://msdn.Microsoft.com/en-us/library/aa264396(VS.60).aspx

http://www.codeproject.com/KB/dialog/windowfinder.aspx

1
Miro A.