views:

332

answers:

3

(by all mean do re-tag with the relevant technology: I don't know which ones they are :)

I'll probably come later with more detailed questions, about specific details but for now I'm trying to grasp the "big picture": I'm looking for a way to enumerate "real visible windows" on Windows. By "real visible window" I mean just that: what a user would call a "window". I need a way to get a list of all these visible windows, in Z-order.

Note that I do really need to do that. I've already done it on OS X (where it is a real headache to do, especially if you want to support OS X 10.4, because OS X doesn't have convenient windows API) and now I need to do it under Windows.

Here's an example, suppose there are three visible windows on the screen, like this:

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

Then I need to get back a list like this:

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

Then if the user (or the OS) brings the window A to the front, it becomes:

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

And I get (ideally) a callback giving me this:

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

Doing this under OS X requires the use of amazingly weird hacks (like mandating the user to turn on "Enable Access for assistive device"!) but I've done it under OS X and it works (under OS X, I didn't manage to get a callback everytime some window changes occurs, so I'm polling, but I got it to work).

Now I want to do this under Windows (I really do, no question about it) and I've got a few questions:

  • can this be done?

  • are there well documented Windows APIs (and working as per their specs) allowing to do that?

  • is it easy to register a callback everytime a window changes? (if it is resized, moved, brought to back/front or if a new window pops-up, etc.)

  • what would the gotchas be?

I know this question is not specific, which is why I've tried to describe my problem as clearly as possible (including nice ASCII art for which you can upvote this): for now I'm looking at the "big picture". I want to know what doing such a thing involves under Windows.

Bonus question: imagine you'd need to write a tiny .exe writing the windows names/position/size to a temporary file everytime there's a window change on screen, how long would such a program be approximately in your language of choice and how long would you need to write it?

(once again, I'm trying to get the "big picture" to understand what is at work here)

+6  A: 

can this be done?

Yes, though you'd have to register a hook in order to get what you want with regards to a callback. You would probably need to use a CBTProc Callback Hook, which is called whenever:

activating, creating, destroying, minimizing, maximizing, moving, or sizing a window; before completing a system command; before removing a mouse or keyboard event from the system message queue; before setting the keyboard focus; or before synchronizing with the system message queue

Note however that I don't believe such hooks work on Console windows because they are the domain of the kernel, not Win32.

are there well documented Windows APIs (and working as per their specs) allowing to do that?

Yes. You can use the GetTopWindow and GetNextWindow functions to get all the window handles on the desktop in correct Z order.

is it easy to register a callback everytime a window changes? (if it is resized, moved, brought to back/front or if a new window pops-up, etc.)

See first answer :)

what would the gotchas be?

See first answer :)

Bonus question: imagine you'd need to write a tiny .exe writing the windows names/position/size to a temporary file everytime there's a window change on screen, how long would such a program be approximately in your language of choice and how long would you need to write it?

A few hundred lines of C, and a couple hours. Though I'd have to use some form of polling -- I've never done hooks before myself. If I'd need the hooks it'd take somewhat longer.

Billy ONeal
@Billy ONeal: thanks a lot for this answer... So if I understand you correctly either you'd be polling (that's what I do under OS X) and then no hooks and no callback **OR** you'd be using hooks, allowing to have a callback? (once again, in OS X AFAICT there's no easy way to do this using a callback, which is why I'm polling).
NoozNooz42
@Nooz: A hook *is* a callback.
Billy ONeal
@Nooz: Put another way, yes. You can either poll or you can use the hook as a callback. The hook is better but I was just estimating the time it'd take as I've never used hooks before.
Billy ONeal
Hook is the only mechanism to being notified without pooling (see my question at http://stackoverflow.com/questions/3122215/system-wide-windows-cbt-hook-not-working-properly). It actually need some effort to get it work, but once you did it you can being notified about windows events.
Luca
+1  A: 

I remember back in 2006 there was a utility WinObj as part of sysinternals that possibly did what you want. Part of these utilities were provided with source code by author (Mark Russinovich).

Since that time, his company was bought by Microsoft so I do not know whether the source would be still available.

Also the following may be worth checking:

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

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

Miro A.
+3  A: 

To enumerate the top-level windows, you should use EnumWindows rather than GetTopWindow/GetNextWindow, since EnumWindows returns a consistent view of the window state. You risk getting inconsistent information (such as reporting on deleted windows) or infinite loops using GetTopWindow/GetNextWindow, when windows change z-order during iteration.

The EnumWindows uses a callback. On each call of the callback you get a window handle. The screen co-ordinates of the window can be fetched by passing that handle to GetWindowRect. Your callback builds a list of the window positions in z-order.

You can use polling, and build the window list repeatedly. Or, you set up a CBTHook to receive notifications of window changes. Not all CBT notifications will result in changes to order,position or visibility of top level windows, so it's wise to rerun EnmWindows to build a new list of window positions in z-order and compare this to the previous list before processing the list further, so that futher processing is done only when a real change has occurred.

Note that with hooking, you cannot mix 32- and 64-bit. If you are running a 32-bit app, then you will get notifications from 32-bit processes. Similarly for 64-bit. Thus, if you want to monitor the entire system on a 64-bit machine, it would seem that it's necessary to run two apps. My reasoning comes from reading this:

SetWindowsHookEx can be used to inject a DLL into another process. A 32-bit DLL cannot be injected into a 64-bit process, and a 64-bit DLL cannot be injected into a 32-bit process. If an application requires the use of hooks in other processes, it is required that a 32-bit application call SetWindowsHookEx to inject a 32-bit DLL into 32-bit processes, and a 64-bit application call SetWindowsHookEx to inject a 64-bit DLL into 64-bit processes. The 32-bit and 64-bit DLLs must have different names. (From the SetWindowsHookEx api page.)

As you're implementing this in Java, you might want to look at JNA - it makes writing access to native libraries much simpler (calling code in java) and removes the need for your own native JNI DLL.

EDIT: You asked how much code it is and how long to write. Here's the code in 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) {     // 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);
        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);
        final int GW_HWNDNEXT = 2;
    }

    public static class RECT extends Structure {
        public int left,top,right,bottom;
    }
    public static class WindowInfo {
        int hwnd;
        RECT rect;
        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);
        }
    }
}

I've made most of the related classes and interfaces inner classes to keep the example compact and pasteable for immediate compilation. In a real implementation, they would be regular top-level classes. The command line app prints out the visible windows and their position. I ran it on both 32-bit jvm and 64-bit, and got the same results for each.

EDIT2: Updated code to include z-order. It does use GetNextWindow. In a production application, you should probably call GetNextWindow twice for the next and previous values and check they are consistent and are valid window handles.

mdma
@mdma: just to be sure, if I simply poll then I can just use EnumWindows and there's no 32-/64-bit issue? JNA was the plan (they had an example enumerating the windows on Windows btw, I just need to find it back) but first I wanted to know what was involved "under the hood". I still don't know if I'll have the skills to do this myself (I've never done any Windows programming) but all these answers are greatly helping me understand what is involved!
NoozNooz42
@NoozNooz42 - That's correct, polling avoids the use of hooks and the 32/64 bit issue. Here's an article that calls EnumWindows from JNA. http://javajeff.mb.ca/cgi-bin/mp.cgi?/java/javase/articles/mjnae
mdma
@mdna: The problem with EnumWindows is that it does not return the windows in Z order. (Or at least does not according to it's documentation)
Billy ONeal
@mdma: Thanks a lot for the code... I gave +1 to everyone, and I give you the bounty for the code example and the time you took to help me! This is great. I'll probably run into issue later on with JNA and come with more detailed answers, but at least I've got stuff to get me started! Thousands of thanks!
NoozNooz42
Hi, I hope the code is helpful. It's not complete, but it should give you a good start. It looks like one of the other answers got the bounty.
mdma