33

I'm making an application where I interact with each running application. Right now, I need a way of getting the window's z-order. For instance, if Firefox and notepad are running, I need to know which one is in front.

Any ideas? Besides doing this for each application's main window I also need to do it for its child and sister windows (windows belonging to the same process).

Steven Jeuris
  • 18,274
  • 9
  • 70
  • 161
user361526
  • 3,333
  • 5
  • 25
  • 36

5 Answers5

15

You can use the GetTopWindow function to search all child windows of a parent window and return a handle to the child window that is highest in z-order. The GetNextWindow function retrieves a handle to the next or previous window in z-order.

GetTopWindow: http://msdn.microsoft.com/en-us/library/ms633514(VS.85).aspx
GetNextWindow: http://msdn.microsoft.com/en-us/library/ms633509(VS.85).aspx

NineBerry
  • 26,306
  • 3
  • 62
  • 93
  • 4
    And the "desktop" should able to be used as the parent window by specify null for the parent. Hence you can easily get the top level window on the desktop. – Lawrence Dol May 05 '09 at 16:24
  • 2
    This is not reliable. `GetNextWindow` just calls `GetWindow`. From the [`GetWindow` reference](https://msdn.microsoft.com/en-us/library/ms633515(v=vs.85).aspx): "_An application that calls GetWindow to perform this task risks being caught in an infinite loop or referencing a handle to a window that has been destroyed._" – zett42 Dec 16 '17 at 00:41
14

Nice and terse:

int GetZOrder(IntPtr hWnd)
{
    var z = 0;
    for (var h = hWnd; h != IntPtr.Zero; h = GetWindow(h, GW.HWNDPREV)) z++;
    return z;
}

If you need more reliability:

/// <summary>
/// Gets the z-order for one or more windows atomically with respect to each other. In Windows, smaller z-order is higher. If the window is not top level, the z order is returned as -1. 
/// </summary>
int[] GetZOrder(params IntPtr[] hWnds)
{
    var z = new int[hWnds.Length];
    for (var i = 0; i < hWnds.Length; i++) z[i] = -1;

    var index = 0;
    var numRemaining = hWnds.Length;
    EnumWindows((wnd, param) =>
    {
        var searchIndex = Array.IndexOf(hWnds, wnd);
        if (searchIndex != -1)
        {
            z[searchIndex] = index;
            numRemaining--;
            if (numRemaining == 0) return false;
        }
        index++;
        return true;
    }, IntPtr.Zero);

    return z;
}

(According to the Remarks section on GetWindow, EnumChildWindows is safer than calling GetWindow in a loop because your GetWindow loop is not atomic to outside changes. According to the Parameters section for EnumChildWindows, calling with a null parent is equivalent to EnumWindows.)

Then instead of a separate call to EnumWindows for each window, which would also be not be atomic and safe from concurrent changes, you send each window you want to compare in a params array so their z-orders can all be retrieved at the same time.

jnm2
  • 7,960
  • 5
  • 61
  • 99
  • 1
    Does not work, when comparing two overlapped windows like a form and the taskbar for example. When the window is over the taskbar, the taskbar z-order is higher than the window that suggests that the taskbar is on top which is not. – Codebeat Aug 30 '15 at 00:33
  • Actually, I changed my mind. @Erwinus, [according to Microsoft](https://msdn.microsoft.com/en-us/library/windows/desktop/ms632599%28v=vs.85%29.aspx#zorder) top level windows appear in z-order *before* other windows, implying that a smaller z-order number means the window is actually higher. The way `EnumWindows` and `GW_HWNDPREV` and `GW_HWNDNEXT` work bears this out. The other answers here agree. I'm loathe to make this counter to the Windows API. – jnm2 Aug 30 '15 at 02:14
  • Yes, got some doubts on this method. Use your first code (with use of getWindow(h, GW_HWNDPREV )) anyway but is slightly different. When a window it is on top of another window you can test if the lower window is in the 'list'. If true, you have to check the bounds of the window to check if it really overlaps. – Codebeat Aug 30 '15 at 15:48
3

Here is my C# solution: The function returns the zIndex among the siblings of the given HWND, starting at 0 for the lowest zOrder.

using System;
using System.Runtime.InteropServices;

namespace Win32
{
    public static class HwndHelper
    {
        [DllImport("user32.dll")]
        private static extern IntPtr GetWindow(IntPtr hWnd, uint uCmd);

        public static bool GetWindowZOrder(IntPtr hwnd, out int zOrder)
        {
            const uint GW_HWNDPREV = 3;
            const uint GW_HWNDLAST = 1;

            var lowestHwnd = GetWindow(hwnd, GW_HWNDLAST);

            var z = 0;
            var hwndTmp = lowestHwnd;
            while (hwndTmp != IntPtr.Zero)
            {
                if (hwnd == hwndTmp)
                {
                    zOrder = z;
                    return true;
                }

                hwndTmp = GetWindow(hwndTmp, GW_HWNDPREV);
                z++;
            }

            zOrder = int.MinValue;
            return false;
        }
    }
}
Kino101
  • 765
  • 8
  • 18
2
            // Find z-order for window.
            Process[] procs = Process.GetProcessesByName("notepad");
            Process top = null;
            int topz = int.MaxValue;
            foreach (Process p in procs)
            {
                IntPtr handle = p.MainWindowHandle;
                int z = 0;
                do
                {
                    z++;
                    handle = GetWindow(handle, 3);
                } while(handle != IntPtr.Zero);

                if (z < topz)
                {
                    top = p;
                    topz = z;
                }
            }

            if(top != null)
                Debug.WriteLine(top.MainWindowTitle);
MightyZot
  • 29
  • 1
1

For get Z-Order implement this function (by using GetWindow Windows API function)

[DllImport("user32.dll", SetLastError = true)]
static extern IntPtr GetWindow(IntPtr hWnd, uint uCmd);

static int GetWindowZOrder(IntPtr hWnd)
{
    var zOrder = -1;
    while ((hWnd = GetWindow(hWnd, 2 /* GW_HWNDNEXT */)) != IntPtr.Zero) zOrder++;
    return zOrder;
}

The return value is a zero-based index. Returning -1 indicates an invalid hWnd.

The approach is to go up through the windows to the top. The total number of climbs is the value of the Z-Order.

Sergio Cabral
  • 6,490
  • 2
  • 35
  • 37